Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion checks/tests/test_handoff.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
32 changes: 32 additions & 0 deletions docs/adrs/0010-spec-package-compilation-target.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
2 changes: 1 addition & 1 deletion docs/adrs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
1 change: 1 addition & 0 deletions examples/tea-tracker/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
|-----------|-----------------------------------|
Expand Down
2 changes: 1 addition & 1 deletion plugin/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -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"
},
Expand Down
3 changes: 2 additions & 1 deletion plugin/scripts/generate_handoff.py
Original file line number Diff line number Diff line change
Expand Up @@ -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` |")
Expand Down
Loading