feat(f4): marketplace.json + release.yml + bump 0.1.0 (cierre Fase F)#28
Conversation
Fase 0 (kickoff) + Fase 1 (RED tests) for feat/f4-marketplace-public-repo.
Scope (per Fase -1 ratificada con 8 ajustes del usuario):
- Aterrizar infra local del marketplace + release flow sin depender de
que javiAI/pos-marketplace exista todavía (A1.b).
- .claude-plugin/marketplace.json con schema oficial Claude Code:
top-level {name, owner, plugins}; owner.name; plugin {name, source}
con source.{source=github, repo, ref="v"+version}.
- .github/workflows/release.yml trigger tag:v*, jobs version-match /
selftest / build-bundle / publish-release / mirror-marketplace
(mirror condicional/skippable hasta repo público).
- Bump plugin.json.version 0.0.1 → 0.1.0 (primer release público).
Archivos en este commit (RED tests, expected failures):
- bin/tests/test_marketplace_json_schema.py (12 tests)
- bin/tests/test_release_workflow_smoke.py (6 tests)
- bin/tests/test_plugin_json_version_bump.py (3 tests)
Tests verifican:
- marketplace.json schema oficial mínimo (top-level + owner + plugin)
- plugin name/version/ref sync entre marketplace ↔ plugin.json
- release.yml trigger v*, jobs esperados, publish-release.needs ⊇
{version-match, selftest, build-bundle}, mirror-marketplace
conditional/skippable
- plugin.json.version pin = "0.1.0"
Estado RED actual: 19 failed + 12 passed (12 = F3 baseline 9 + 3
plugin.json existe/parses). Sin regresión en F3.
Diferidos en F4 (regla #7 CLAUDE.md):
- audit.yml nightly (sin consumer hoy; rama propia post-F4).
- /pos:pr-description, /pos:release skills (sin repetición demostrada).
- CHANGELOG.md enforced (auto-generated from git log entre tags).
- refactor/template-policy-d5b-migration (drift independiente).
- Fase G (Knowledge Plane).
GREEN impl + docs (RELEASE.md/ARCHITECTURE/ci-cd/MASTER_PLAN/ROADMAP/
HANDOFF) entran en commits siguientes dentro de esta rama.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Fase 2 (GREEN) — flippea los 19 RED del commit previo.
.claude-plugin/marketplace.json (NEW):
- Schema oficial Claude Code marketplace.
- top-level: name="pos-marketplace", owner.name="javiAI", plugins[].
- plugins[0]: name=pos, source={source:github, repo:javiAI/
project-operating-system, ref:v0.1.0}, version=0.1.0.
- metadata.{description, version} para humans.
.claude-plugin/plugin.json:
- version 0.0.1 → 0.1.0 (primer release público; pre-1.0).
- Single source of truth; tag git debe ser v${version}.
.github/workflows/release.yml (NEW):
- Trigger: push tags v*.
- Jobs:
- version-match: assert plugin.json.version == ${tag#v}.
- selftest: pytest bin/tests -q (reusa contrato F3).
- build-bundle: tar.gz curated plugin-only (.claude-plugin/,
.claude/skills/, .claude/rules/, hooks/, agents/, policy.yaml,
bin/pos-selftest.sh, bin/_selftest.py, docs/RELEASE.md). Excluye
generator/, tools/, templates/, questionnaire/.
- publish-release: needs [version-match, selftest, build-bundle];
gh release create con bundle como asset.
- mirror-marketplace: condicional vía vars.POS_MARKETPLACE_REPO;
si vacío skippea sin fallar release. Abre PR contra repo público
cuando esté configurado.
- Actions pinneadas por SHA (ci-cd.md regla #2).
- permissions.contents=write para gh release create.
Tests post-GREEN: 21 passed (12 marketplace + 6 release.yml + 3 plugin
version), suite total 644 passed + 1 skipped (skip D5 intencional F3).
Sin regresión.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…/MASTER_PLAN Fase N+3 — docs-sync dentro de la rama (CLAUDE.md regla #2, docs.md § Docs-sync en cada rama). docs/RELEASE.md (NEW): - Runbook user-facing de versionado + bundle + flujo + recovery. - Contrato de versionado: plugin.json.version source of truth; tag = v${version}; marketplace.json.source.ref espeja. - Bundle scope plugin-only curated (incluye/excluye explícitos). - Flujo en 5 pasos: bump → tag → workflow → verify → recovery. - Activación del mirror cuando exista repo público (3 pasos: crear repo + gh variable set POS_MARKETPLACE_REPO + gh secret set POS_MARKETPLACE_TOKEN). - Instalación user-facing (/plugin marketplace add + /plugin install pos). - Diferidos enumerados. docs/ARCHITECTURE.md § 13 (Marketplace + Release flow): - Reescrita de placeholder de 6 líneas a sub-sección completa. - Manifest, source of truth de versión, jobs del workflow, bundle scope curated, deferral del repo público, determinismo del flujo, instalación user-facing, deferrals. .claude/rules/ci-cd.md: - Bullet release.yml promovido de "Diferidos" a "Aterrizado" (entregado en F4). - Nuevo H3 "### Job release (entregado en F4)" con scope completo (5 jobs + bundle curated + source of truth). ROADMAP.md: - Tabla: F4 marcada ✅ (PR pendiente). - Nueva sección § feat/f4-marketplace-public-repo en Progreso Fase F: scope, entregables, decisiones Fase -1 (A1.b..A8), contrato fijado, carry-overs, criterio de salida (665 passed + 1 skipped). HANDOFF.md: - §1 Snapshot: rama actual F4 (entrega + suite update). - §9 Próxima rama: Fase F cerrada; carry-overs (template-policy d5b migration, marketplace activación, skills diferidas, audit.yml). - §22 nuevo: Estado F4 con entregables + contrato + decisiones + carry-overs (paralelo a §19 F1, §20 F2, §21 F3). MASTER_PLAN.md § Rama F4: - Expandida de 3 líneas a sección completa: scope realizado, archivos entregados con detalle por path, decisiones Fase -1 (A1.b..A8), contexto leído, criterio de salida, carry-overs. Simplify pre-commit: - Recortados 3 bullets de "Ajustes durante implementación" (heredoc syntax glitch + rtk wrapper output filter) — debug ephemera, pertenecen a commit history. - Mantenido el único gotcha persistente: PyYAML 1.1 parsea `on:` como Python bool True (patrón reutilizable para tests futuros de workflow YAML). Sin tocar (per A8 ratificado en Fase -1): policy.yaml, hooks/**, .claude/skills/**, agents/**, generator/**, templates/**, .claude/rules/skills-map.md. Tests: sin cambios (GREEN ya verde con 665 passed + 1 skipped). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds the local marketplace manifest + automated GitHub release workflow needed to publish the pos plugin as a reproducible bundle (v0.1.0), along with a runbook and contract tests to lock down the workflow/manifest shape.
Changes:
- Introduces
.claude-plugin/marketplace.jsonand bumps.claude-plugin/plugin.jsonto0.1.0(tag/ref coupling). - Adds
.github/workflows/release.ymlto build/upload the curated plugin bundle, create a GitHub Release, and optionally mirrormarketplace.jsoninto a separate public marketplace repo. - Adds docs (
docs/RELEASE.md) + contract tests for marketplace/release workflow shape and updates roadmap/handoff/architecture/CI-CD rule docs accordingly.
Reviewed changes
Copilot reviewed 9 out of 12 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
docs/RELEASE.md |
New release runbook covering versioning, bundle scope, workflow jobs, and recovery/mirroring steps. |
docs/ARCHITECTURE.md |
Expands architecture docs for marketplace + release flow and its determinism guarantees. |
bin/tests/test_release_workflow_smoke.py |
Adds workflow contract tests (trigger, required jobs, needs, conditional mirror). |
bin/tests/test_plugin_json_version_bump.py |
Pins plugin.json.version to 0.1.0 as a milestone contract. |
bin/tests/test_marketplace_json_schema.py |
Adds schema/consistency checks for marketplace.json vs plugin.json. |
ROADMAP.md |
Marks F4 as delivered and documents the scope/decisions. |
MASTER_PLAN.md |
Updates F4 plan section to reflect delivered artifacts and constraints. |
HANDOFF.md |
Updates current snapshot and next steps after F4. |
.github/workflows/release.yml |
New tag-triggered release workflow building curated bundle + publishing release + optional marketplace mirroring. |
.claude/rules/ci-cd.md |
Documents the new release workflow as delivered and its job responsibilities. |
.claude-plugin/plugin.json |
Bumps plugin version to 0.1.0. |
.claude-plugin/marketplace.json |
New local marketplace manifest pointing to javiAI/project-operating-system at v0.1.0. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| build-bundle: | ||
| name: build-bundle (curated plugin-only) | ||
| runs-on: ubuntu-latest | ||
| outputs: |
There was a problem hiding this comment.
build-bundle is also meant to be behind the version-match gate, but it currently has no needs and will run even when the tag/version mismatch. Add needs: [version-match] (or otherwise enforce the ordering) so the bundle build is skipped when version-match fails and the job graph matches the documented "orden estricto".
| dst['plugins'] = plugins | ||
| dst_path.write_text(json.dumps(dst, indent=2) + '\n') | ||
| PY | ||
| git add marketplace.json |
There was a problem hiding this comment.
mirror-marketplace will fail on workflow re-runs (or any no-op update) because git commit -m ... exits non-zero when there are no changes. Make the job idempotent by checking for a clean working tree before committing (and exiting 0 if nothing changed) or by handling the empty-commit case explicitly.
| git add marketplace.json | |
| git add marketplace.json | |
| if git diff --cached --quiet --exit-code; then | |
| echo "No marketplace changes detected; skip mirror" | |
| exit 0 | |
| fi |
| gh pr create \ | ||
| --repo "$MARKETPLACE_REPO" \ | ||
| --title "bump pos to ${tag}" \ | ||
| --body "Automated bump of pos plugin to ${tag}." \ | ||
| --head "$branch" |
There was a problem hiding this comment.
After pushing the branch, gh pr create will exit non-zero if a PR for the same branch already exists (common when re-running the workflow for a tag). Consider making this step idempotent (e.g., detect an existing open PR for bump/pos-${version} and skip, or use gh pr view/gh pr list before creating).
| gh pr create \ | |
| --repo "$MARKETPLACE_REPO" \ | |
| --title "bump pos to ${tag}" \ | |
| --body "Automated bump of pos plugin to ${tag}." \ | |
| --head "$branch" | |
| existing_pr="$(gh pr list \ | |
| --repo "$MARKETPLACE_REPO" \ | |
| --head "$branch" \ | |
| --state open \ | |
| --json number \ | |
| --jq '.[0].number')" | |
| if [ -n "$existing_pr" ]; then | |
| echo "Open PR #$existing_pr already exists for branch $branch; skipping creation." | |
| else | |
| gh pr create \ | |
| --repo "$MARKETPLACE_REPO" \ | |
| --title "bump pos to ${tag}" \ | |
| --body "Automated bump of pos plugin to ${tag}." \ | |
| --head "$branch" | |
| fi |
| selftest: | ||
| name: selftest (ubuntu, py 3.11) | ||
| runs-on: ubuntu-latest | ||
| steps: |
There was a problem hiding this comment.
selftest is described as being gated by version-match ("primer gate; sin esto el resto no corre"), but the workflow currently runs selftest in parallel with version-match (no needs). This can waste CI time on mismatched tags and contradicts the documented job ordering; add needs: [version-match] to selftest to make version-match a true gate.
…(PR #28 review) Aplica las 4 findings de Copilot review de PR #28. Las 4 son real correctness/idempotency bugs, no estilo. Triage value/effort: todas high/trivial-low → FIX. Gating de version-match (findings 1 + 4): - selftest: needs [version-match]. - build-bundle: needs [version-match]. Antes corrían en paralelo con version-match → CI gastaba tiempo en tags mismatched y contradecía el "orden estricto" documentado. Ahora: version-match → (selftest + build-bundle) → publish-release → mirror-marketplace. Idempotencia mirror-marketplace (findings 2 + 3): - Tras `git add marketplace.json`, si `git diff --cached --quiet` no hay cambios → exit 0. Antes `git commit` no-op fallaba la re-run del workflow. - Antes de `gh pr create`, `gh pr list --head $branch --state open`. Si ya existe un PR abierto → skip create con mensaje. Antes `gh pr create` con PR existente fallaba la re-run. Tests: bin/tests 31/31 verde; full explicit run 850 passed + 1 skipped (skip D5 intencional F3). Sin regresión. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Fix push: applied all 4 Copilot findings (commit 0bc8392).
Tests: |
Summary
Última rama de Fase F. Aterriza la infra local del marketplace + release flow del plugin
pos. Cierra Fase F con el plugin distribuible: manifest oficial, workflow de release reproducible, runbook ejecutable, bump al primer release público (0.1.0). Repo públicojaviAI/pos-marketplacedeliberadamente diferido — F4 deja la infra lista; la creación manual del repo público es decisión separada..claude-plugin/marketplace.json(NEW): schema oficial{name, owner, plugins, metadata}+plugins[0].source.{source: "github", repo: "javiAI/project-operating-system", ref: "v0.1.0"}. Single source of cómo/plugin install posresolverá el repo + ref..claude-plugin/plugin.json: bump0.0.1 → 0.1.0(pre-1.0, primer release público).versiones source of truth: tag git =v\${version}ymarketplace.json.source.reflo espejan..github/workflows/release.yml(NEW): triggerpush.tags ['v*']. 5 jobs encadenados:version-match→ assertaplugin.json.version == \${tag#v}(primer gate).selftest→ reusa contrato F3 (pytest bin/tests -q) sobre el ref del tag.build-bundle→ empaquetapos-v\${version}.tar.gzplugin-only curated.publish-release→needs: [version-match, selftest, build-bundle]+gh release create --generate-notes.mirror-marketplace→ condicionalif: vars.POS_MARKETPLACE_REPO != ''(skippea silenciosamente si la variable está vacía; no falla el release).ci-cd.md#2).docs/RELEASE.md(NEW): runbook de versionado + bundle + flujo en 5 pasos + recovery + activación del mirror cuando exista repo público.bin/tests/test_marketplace_json_schema.py(12 tests).bin/tests/test_release_workflow_smoke.py(6 tests).bin/tests/test_plugin_json_version_bump.py(3 tests,EXPECTED_VERSION = "0.1.0"pin literal).Decisiones cerradas en Fase -1 (8 ajustes ratificados):
vars.POS_MARKETPLACE_REPO.marketplace.jsonschema oficial Claude Code: top-level{name, owner, plugins};owner.namerequerido.plugin.json.versionsource of truth.generator/,templates/,questionnaire/,tools/,bin/tests/,.github/.publish-release.needs ⊇ {version-match, selftest, build-bundle}.audit.ymlnightly,/pos:pr-description+/pos:releaseskills,CHANGELOG.mdenforced,refactor/template-policy-d5b-migration, Fase G.policy.yaml,hooks/**,.claude/skills/**,agents/**,generator/**,templates/**,.claude/rules/skills-map.md.Ajuste durante GREEN (gotcha persistente): PyYAML 1.1 parsea
on:(clave canónica de triggers) como Pythonbool True. El test acepta ambos (data.get("on") or data.get(True)) — patrón aplicable a cualquier test futuro de workflow YAML.Suite global: 665 passed + 1 skipped (vs baseline F3 644 + 1 skip = +21 nuevos tests F4). Sin regresión D1..D6 / E1a..E3b / F1..F3.
Carry-overs post-F4 (cierre Fase F):
refactor/template-policy-d5b-migration— drift template post-D5b (rama propia).javiAI/pos-marketplace— creación manual; activación 3-pasos en runbook./pos:pr-description+/pos:release— regla feat(c3): tests harness renderer — 13-file output per profile, deferred frameworks testeable #7 CLAUDE.md (≥2 repeticiones).audit.ymlnightly — sin consumer activo.Test plan
pytest bin/tests -qlocal — 21 nuevos F4 verdes (12 marketplace + 6 release workflow + 3 plugin version pin).ROADMAP.md(F4 ✅ + § Progreso),HANDOFF.md(§1 + §9 + §22 nuevo),MASTER_PLAN.md § Rama F4expandida,.claude/rules/ci-cd.md(release.yml Aterrizado + H3 nuevo),docs/ARCHITECTURE.md § 13reescrita,docs/RELEASE.md(NEW).pre-pr-gate.pyrequeridosROADMAP.md+HANDOFF.mdsatisfechos.🤖 Generated with Claude Code