diff --git a/CHANGELOG.md b/CHANGELOG.md index 839a3d7..c9a6372 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `skills-claude-ai/` — Claude.ai Agent Skill ports of product-ideation and product-flow (issue #12): bundled prompts with skill-relative paths, conversation-scoped artifact ledger with `product-dev-artifacts.md` export replacing the `.product-dev/` registry ### Changed +- Plugin 0.5.4: the `/compile` handoff tells the implementation agent to re-read the spec files after a recompile, because the builder may have applied a requested change differently and the package wins. In a closed-loop trial one run in six searched for its own field name, missed the builder's renamed field, and stopped (issue #71) - Plugin 0.5.3: the `/compile` handoff no longer tells the implementation agent to edit the spec package. `/compile` rebuilds the package from the source artifacts, so a hand edit was unvalidated and lost on the next compile; in a trial half of such edits failed validation. The agent now writes the agreed change to `spec-change-request.md`, names the source artifact for each spec file, asks the builder to recompile, and builds the feature from the recompiled package (issue #71) - Plugin 0.5.2: the `/compile` handoff (`CLAUDE.md` for the implementation agent) now says what to do when asked for something the spec does not cover: check the exclusion ADRs, otherwise say so, propose the spec change, and ask before building. It previously said only to stop and check the ADRs, which list exclusions and so resolved nothing for an unlisted request (issue #65) - `manifest` check: a missing `claude` CLI is now a note locally and a finding only under `--require-validator`, which CI passes. With that fixed the check graduates warn → block (issue #63); 13 of 18 checks now block diff --git a/checks/tests/test_handoff.py b/checks/tests/test_handoff.py index df18c24..da48342 100644 --- a/checks/tests/test_handoff.py +++ b/checks/tests/test_handoff.py @@ -20,7 +20,8 @@ def test_handoff_names_a_move_for_requests_outside_the_spec(self): self.assertIn("## When a Request Is Not in the Spec", self.generated) section = self.generated.split("## When a Request Is Not in the Spec", 1)[1].split("\n## ", 1)[0] for phrase in ("If an ADR excludes it", "propose the smallest spec change", "Ask before building", - "do **not** edit anything under", "spec-change-request.md", "re-run `/compile`"): + "do **not** edit anything under", "spec-change-request.md", "re-run `/compile`", + "re-read the spec files your request touched", "The package wins"): self.assertIn(phrase, section) def test_handoff_never_tells_the_agent_to_edit_the_compiled_package(self): diff --git a/docs/adrs/0010-spec-package-compilation-target.md b/docs/adrs/0010-spec-package-compilation-target.md index e25e543..0c28064 100644 --- a/docs/adrs/0010-spec-package-compilation-target.md +++ b/docs/adrs/0010-spec-package-compilation-target.md @@ -220,3 +220,35 @@ change is validated and survives. loop was not closed — no run was given a recompiled package to build from — and the request files were not themselves validated; at least one repeats the required-versus-optional mismatch, which the compile step exists to catch. + +--- + +## Amendment (2026-10-12): Closed-loop result; re-read after a recompile + +The loop left open above was closed the same day (record: issue #71). Six +isolated three-turn runs: after the agent wrote its request, the change was +applied to the fixture's source artifacts and compiled with `compile_spec.py` +(0 failed checks), deliberately naming the field `is_starred` where every +request said `is_favorite` and omitting the screen changes. The third turn +said only that the recompile was done. + +| | Runs | +|---|---| +| Built nothing for the field before the recompile | 6 of 6 | +| Edited any file under the package, any turn | 0 of 6 | +| Re-read the package and built `is_starred` | 5 of 6 | +| Any `is_favorite` left in code | 0 of 6 | +| Told the builder the name differed from the request | 5 of 6 | +| Built nothing and asked | 1 of 6 | + +The one miss searched the package for its own field name, found nothing, +concluded the recompile had not been applied, and stopped without re-reading +`entities.yaml`. Nothing wrong was built. + +**Decision (owner-directed):** the handoff gains a sixth step. When the +builder says the package was recompiled, re-read the spec files the request +touched; the builder may have applied the change differently; the package +wins; say where it differs. Plugin version 0.5.4. + +**Not measured:** this step itself. It answers one miss in six runs, and that +miss was induced by the deliberate rename. diff --git a/docs/adrs/README.md b/docs/adrs/README.md index 579df8f..c99f754 100644 --- a/docs/adrs/README.md +++ b/docs/adrs/README.md @@ -19,7 +19,7 @@ listed under "Amended by" override its earlier text. | [0007](0007-json-index-deprecation.md) | Deprecation of JSON Index Files and Stream Deck Document | Accepted | `no-legacy` (block) | — | — | | [0008](0008-plugin-architecture.md) | Plugin Architecture | Accepted | `manifest` (block), `component-census` (block) | 0011 | 1, latest 2026-10-12 | | [0009](0009-prompt-enhancement-pattern.md) | Prompt Enhancement Pattern v2 | Accepted | `frontmatter-v2` (block), `name-uniqueness` (block), `body-structure` (block), `placeholder-syntax` (block), `word-limit` (warn) | — | 1, latest 2026-07-25 | -| [0010](0010-spec-package-compilation-target.md) | Spec Package as Compilation Target | Accepted | `spec-fixture` (block) | — | 3, latest 2026-10-12 | +| [0010](0010-spec-package-compilation-target.md) | Spec Package as Compilation Target | Accepted | `spec-fixture` (block) | — | 4, latest 2026-10-12 | | [0011](0011-plugin-self-containment.md) | Plugin Self-Containment via Bundled Assets | Accepted | `self-containment` (warn) | — | 1, latest 2026-07-25 | | [0012](0012-adr-enforcement-architecture.md) | ADR Enforcement Architecture | Accepted | `adr-status` (block), `adr-index` (warn), `adr-amendment-link` (warn) | — | 4, latest 2026-10-12 | | [0013](0013-ux-optimization-reverse-pass.md) | UX Optimization Reverse Pass (ux-optimization skill) | Accepted | `component-census` (block) | — | 1, latest 2026-10-12 | diff --git a/examples/tea-tracker/CLAUDE.md b/examples/tea-tracker/CLAUDE.md index 12be54c..a5843c7 100644 --- a/examples/tea-tracker/CLAUDE.md +++ b/examples/tea-tracker/CLAUDE.md @@ -121,6 +121,7 @@ You will be asked for things this package does not cover. Neither refuse silentl 3. Ask before building. 4. Once the builder agrees, do **not** edit anything under `spec-package/`. It is compiled output: `/compile` rebuilds it from the builder's source artifacts, discards hand edits, and is the only step that validates it. Instead, write the agreed change to `spec-change-request.md` next to this file: for each spec file, the exact YAML to add or change, and the source artifact it belongs in (table below). Ask the builder to apply it to those artifacts and re-run `/compile`. 5. Build the new feature only after the recompiled package is in place, and build it from that package. Until then, keep building what the spec already covers. +6. When the builder says the package was recompiled, re-read the spec files your request touched before doing anything else. The builder may have applied the change differently from your request — another name, another type, fewer files. The package wins: build what it says, and tell the builder where it differs from what you asked for. Do not search for your own wording and conclude the change is missing. | Spec file | Source artifact the builder edits | |-----------|-----------------------------------| diff --git a/plugin/.claude-plugin/plugin.json b/plugin/.claude-plugin/plugin.json index 93b5b48..0857e15 100644 --- a/plugin/.claude-plugin/plugin.json +++ b/plugin/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "product-dev", "description": "AI-assisted product development framework. Guides from vague idea to technical spec through structured UX research, hypothesis formation, and prototype planning.", - "version": "0.5.3", + "version": "0.5.4", "author": { "name": "clownware" }, diff --git a/plugin/scripts/generate_handoff.py b/plugin/scripts/generate_handoff.py index 13a4acd..d6bd705 100644 --- a/plugin/scripts/generate_handoff.py +++ b/plugin/scripts/generate_handoff.py @@ -250,7 +250,8 @@ def generate_handoff(pkg_dir: Path, spec_package_rel: str = "spec-package") -> s # source artifacts, and only a compile re-runs validation. A hand edit here # is unvalidated and is lost on the next compile (issue #71). sections.append(f"4. Once the builder agrees, do **not** edit anything under `{spec_package_rel}/`. It is compiled output: `/compile` rebuilds it from the builder's source artifacts, discards hand edits, and is the only step that validates it. Instead, write the agreed change to `spec-change-request.md` next to this file: for each spec file, the exact YAML to add or change, and the source artifact it belongs in (table below). Ask the builder to apply it to those artifacts and re-run `/compile`.") - sections.append("5. Build the new feature only after the recompiled package is in place, and build it from that package. Until then, keep building what the spec already covers.\n") + sections.append("5. Build the new feature only after the recompiled package is in place, and build it from that package. Until then, keep building what the spec already covers.") + sections.append("6. When the builder says the package was recompiled, re-read the spec files your request touched before doing anything else. The builder may have applied the change differently from your request — another name, another type, fewer files. The package wins: build what it says, and tell the builder where it differs from what you asked for. Do not search for your own wording and conclude the change is missing.\n") sections.append("| Spec file | Source artifact the builder edits |") sections.append("|-----------|-----------------------------------|") sections.append("| `spec/entities.yaml` | `data_models` |")