From 52f340e95acb43fa6757d4ca10f1a005397fc828 Mon Sep 17 00:00:00 2001 From: Chris Pezza <153260989+chrispezza@users.noreply.github.com> Date: Mon, 12 Oct 2026 14:07:14 -0400 Subject: [PATCH] docs(claude-md): make CLAUDE.md the contributor guide (ADR 0019) The root CLAUDE.md was about 2,000 words, mostly restating the skills, the tier model, the plugin tree and the ADR list, and it told contributor sessions to behave as the product. Every restatement in it had drifted at least once, and plugin users never receive the file. - Rewrite CLAUDE.md as about 650 words: two modes, a table of pointers to each source of truth, and nine rules for changing the repo, including a legal move for requests that conflict with an Accepted ADR - Keep four runtime rules that never shipped in the plugin in a labelled section pending issue #62 - Add ADR 0019 and back-link amendments on ADRs 0006, 0016 and 0017 - Add the claude-md-scope check at warn: links resolve, 1,000-word budget - Guard: protect an ADR only once it exists in HEAD so drafts can be revised - Mark the two CLAUDE.md-era test documents as historical Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 3 + CLAUDE.md | 346 ++++-------------- CONTRIBUTING.md | 2 +- checks/enforcement.config.json | 3 +- checks/hooks/pretooluse_guard.py | 30 +- checks/run_checks.py | 25 ++ checks/tests/test_pretooluse_guard.py | 14 + ...rogressive-disclosure-tiered-engagement.md | 17 + .../adrs/0012-adr-enforcement-architecture.md | 11 + docs/adrs/0016-gstack-derived-patterns.md | 11 + ...017-gstack-derived-patterns-second-wave.md | 11 + docs/adrs/0019-claude-md-contributor-guide.md | 141 +++++++ docs/adrs/README.md | 9 +- scripts/test-chain.md | 2 + scripts/test-registry.md | 2 + 15 files changed, 350 insertions(+), 277 deletions(-) create mode 100644 docs/adrs/0019-claude-md-contributor-guide.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fc181c..67ca992 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,10 +12,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `checks/` — warn-only enforcement suite (14 checks: frontmatter, body structure, placeholders, dependency graph, word limits, manifest via `claude plugin validate --strict`, component census, legacy guards, self-containment, spec fixture, ADR status, filename convention); config `checks/enforcement.config.json`, CI job in `validate.yml` - Enforcement sections appended to all 11 prior ADRs: testable consequences, check mappings, honest not-machine-checkable lines, empty graduation logs - Two blocking hooks in checked-in `.claude/settings.json`: Stop-gate (suite BLOCKERs bounce the turn) and PreToolUse guard (existing ADRs append-only, Pattern Guide requires an ADR); kill-switches `PRODUCT_DEV_SKIP_STOP_GATE=1` / `PRODUCT_DEV_SKIP_ADR_GUARD=1` +- ADR 0019: CLAUDE.md is the contributor guide; runtime canon lives in the plugin. New warn-level check `claude-md-scope` (links resolve; 1,000-word budget) - ADR index, generated: `checks/adr_index.py` renders the index of records (status, enforcing checks, amended-by links) from the ADR files and the enforcement config. Two new warn-level checks (ADR 0012 TC-3, TC-4): `adr-index` fails when the committed index is stale, `adr-amendment-link` fails when an ADR says it amends another and the amended one never names it - `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 +- `CLAUDE.md` is now the contributor guide (ADR 0019): about 650 words of pointers and change rules, down from about 2,000 that restated the skills, the tier model, the plugin tree, and the ADR list. Four runtime rules that never shipped in the plugin stay in a labelled section pending issue #62 +- PreToolUse guard: an ADR is protected only once it exists in `HEAD`, so a draft can be revised before its first commit - README: the hand-kept ADR table is replaced by a pointer to the generated index - PreToolUse guard: the Bash rule for inline scripts now requires an existing protected file or iteration of the ADR folder, after it blocked two commands that only mentioned the folder in text - Enforcement suite: first graduation (ADR 0012 amendment, 2026-10-12). Twelve checks moved warn → block after 7+ clean days: `frontmatter-v2`, `name-uniqueness`, `body-structure`, `placeholder-syntax`, `placeholder-resolvability`, `dependency-graph`, `component-census`, `no-legacy`, `spec-fixture`, `adr-status`, `filename-convention`, `objective-parity`. Findings from these now fail CI and bounce the Stop-gate. `manifest`, `word-limit`, and `self-containment` stay at warn, with reasons in the amendment diff --git a/CLAUDE.md b/CLAUDE.md index 568480b..09cd029 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,267 +1,79 @@ -# Product Development Framework - -AI-assisted product development from vague idea to technical spec. Guides users through structured UX research, hypothesis formation, and prototype planning. - -## Repo Structure - -The framework prompts are bundled inside the plugin at `plugin/prompts/` so the -plugin is self-contained and distributable. Skills, commands, and the subagent -reference them via `${CLAUDE_PLUGIN_ROOT}/prompts/...` (ADR 0011). - -``` -plugin/prompts/ -├── 01_ux_research/ -│ ├── 00_fuzzy_front_end/ # Phase 00: Idea capture + exploration -│ ├── 01_define_problem/ # Phase 01: Problem statement + persona -│ ├── 02_objectives/ # Phase 02: Core objective + metrics -│ ├── 03_solution_hypothesis/# Phase 03: Solution concept + hypothesis -│ ├── 04_user_flow/ # Phase 04: User flow + screens -│ ├── 05_prototype/ # Phase 05: Prototype scoping + test design -│ └── 06_post_test_synthesis/# Phase 06: Test analysis + evaluation -├── 02_tech_requirements/ # Tech spec: data models, APIs, business rules, NFRs -├── 07_ux_optimization/ # Reverse pass: extract artifacts from an existing product (ADR 0013) -├── 03_tool_selection_setup/ # Environment and tooling (Tier 3, not in active workflow) -├── 04_bridge_to_architecture/ # Architecture transition prompts (Tier 3) -└── 05_implementation_docs/ # Implementation planning (Tier 3) -``` - -Non-framework reference material (build guides, IDE rules, portfolio prompts) -remains at repo root under `prompts/dev/` and is not part of the plugin bundle. - -**Frontmatter schema** (each prompt file): -```yaml -name: kebab-case-name -run: always | entry_point | context_gated -run_when: [plain-English condition, only for context_gated] -produces: artifact_name -requires: [dependency_artifact_1, dependency_artifact_2] -tier: 1 | 2 -``` - -**Prompt body structure:** `` sets your role, instructions define the output, `` set boundaries, `` shows quality. Template variables use `{{artifact_name}}` syntax. See ADR 0001 for full schema. - -## Workflow Paths - -Four skills (ADR 0004, amended by ADR 0013), each covering a contiguous set of phases: - -### product-ideation (Phases 00-03) - -Entry points: `/idea`, `/problem` - -| Prompt | Run | Produces | Requires | -|--------|-----|----------|----------| -| `00_fuzzy_front_end/01_capture_idea` | entry_point | `initial_concept` | — | -| `00_fuzzy_front_end/05_explore_problem` | entry_point | `initial_concept` | — | -| `01_define_problem/01_create_problem_statement` | always | `problem_statement` | `initial_concept` | -| `01_define_problem/02_create_proto_persona` | always | `proto_persona` | `problem_statement` | -| `02_objectives/01_identify_core_objective` | always | `core_objective` | `problem_statement` | -| `03_solution_hypothesis/01_generate_solution_concept` | always | `solution_concept` | `problem_statement`, `core_objective` | -| `03_solution_hypothesis/02_format_hypothesis_statement` | always | `hypothesis_statement` | `solution_concept` | - -### product-flow (Phases 04-06) - -| Prompt | Run | Produces | Requires | -|--------|-----|----------|----------| -| `04_user_flow/01_primary_user_flow` | always | `user_flow` | `solution_concept` | -| `04_user_flow/02_identify_screens_states` | context_gated (digital product with UI) | `screen_inventory` | `user_flow` | -| `05_prototype/01_scope_prototype` | always | `prototype_scope` | `user_flow`, `hypothesis_statement` | -| `05_prototype/04_test_questions` | always | `test_questions` | `hypothesis_statement` | -| `06_post_test_synthesis/01_test_patterns_insights` | context_gated (after user testing) | `test_insights` | — | -| `06_post_test_synthesis/02_check_hypothesis` | context_gated (test_insights exists) | `hypothesis_evaluation` | `hypothesis_statement`, `test_insights` | - -### tech-spec (Tech Requirements) - -Entry point: `/spec`. Spawns the Tech Spec Writer subagent (`plugin/agents/tech-spec-writer.md`). Requires design artifacts: `solution_concept`, `user_flow`. - -### ux-optimization (Reverse Pass, ADR 0013) - -Entry point: `/optimize`. Audits an **existing** product: extracts artifacts from its repository (via the read-only `ux-extractor` subagent), validates them with the owner, then gap-analyzes into a prioritized optimization spec. Extracted artifacts carry `mode`/`provenance`/`confidence`/`validation_status` frontmatter; `context.json` carries `"mode": "reverse"`. - -| Prompt | Run | Produces | Requires | -|--------|-----|----------|----------| -| `07_ux_optimization/01_product_archaeology` | entry_point | `initial_concept` | — | -| `07_ux_optimization/02_evidence_mining` | context_gated (research/marketing material exists) | `problem_statement` | `initial_concept` | -| `07_ux_optimization/03_journey_tracing` | always | `user_flow` | `initial_concept` | -| `07_ux_optimization/04_persona_extraction` | always | `proto_persona` | `initial_concept`, `user_flow` | -| `07_ux_optimization/05_value_prop_synthesis` | always | `value_prop_inventory` | `initial_concept` | -| `07_ux_optimization/06_validation_checkpoint` | always | `validation_log` | all extracted artifacts | -| `07_ux_optimization/07_objective_metrics` | always | `core_objective` | `user_flow`, `proto_persona` | -| `07_ux_optimization/08_gap_analysis` | always | `hypothesis_backlog` | `user_flow`, `proto_persona`, `value_prop_inventory`, `core_objective` | -| `07_ux_optimization/09_optimization_spec` | always | `optimization_spec` | `hypothesis_backlog` | -| `07_ux_optimization/10_live_site_audit` | context_gated (deployed URL + browser; Tier 2) | `live_site_audit` | `user_flow` | -| `07_ux_optimization/11_runtime_audit` | context_gated (locally runnable + toolchain; Tier 2) | `runtime_audit` | `user_flow` | - -After validation, the registry matches forward-pass Phases 00-04 and the standard downstream (product-flow, tech-spec, `/compile`) applies unchanged. Visual-layer defects are handed off to a design-system audit, not itemized in the spec. - -## Run Conditionality (ADR 0006) - -- **`always`**: Run unconditionally in sequence when reached. -- **`entry_point`**: Pick one based on user's starting context. `capture_idea` for a specific product idea, `explore_problem` for domain-level exploration. Both produce `initial_concept`. Never run both. -- **`context_gated`**: Check `run_when` condition before including. Skip with a note about why it was skipped and when to revisit. - -Context gates: -- `identify-screens-states`: "Digital product with UI" — skip for services, processes, physical products -- `synthesize-test-patterns`: "User has completed testing" — skip until real observations exist -- `evaluate-hypothesis`: "`test_insights` exists in context" — skip until post-test synthesis complete -- `define-api-endpoints`: "Software product with client-server architecture" — skip for hardware, processes - -When a gate is unclear, ask the user. - -## Tier Model (ADR 0006) - -Default to **Tier 1** (quick exploration, 5-10 min). Only the prompts listed above run at Tier 1. - -**Escalation signals** (switch to Tier 2): -- User provides detailed, multi-paragraph responses -- User asks to "go deeper", "analyze further", "be more thorough" -- User explicitly requests comprehensive analysis - -**De-escalation signals** (stay at / return to Tier 1): -- User says "that's enough", "move on", "keep it simple" -- Short, confirmatory responses - -When escalating: -1. Update `tier` to 2 in `context.json` -2. Check the current phase directory for Tier 2 prompts (files with `tier: 2` in frontmatter) -3. Present them as optional branches: "We can go deeper here. Available: [list with one-line descriptions]. Which would be most useful, or should we continue the main sequence?" -4. Let the user pick which Tier 2 prompts to run — do not run all automatically -5. After each selected Tier 2 prompt, return to the main Tier 1 sequence - -Never auto-escalate to Tier 3. Only run all prompts in a phase when explicitly requested. - ---- - -## Context Registry - -All project state persists in `.product-dev/` in the working directory (`context.json` registry + `artifacts/*.md` + `learnings.jsonl` + compiled `spec-package/`). - -**The canonical operations reference is bundled with the plugin: [`plugin/docs/registry-operations.md`](plugin/docs/registry-operations.md)** — directory layout, `context.json` schema (including `inputs` provenance), the five operations (`createProject`, `setArtifact`, `getArtifact`, `getStatus`, `checkGate`), process learnings, template variable resolution (including the `{{name?}}` optional-placeholder syntax), status display with staleness annotation, the Session Resume Algorithm, and re-entry rules. Skills reference it via `${CLAUDE_PLUGIN_ROOT}/docs/registry-operations.md` so it resolves in consumer projects (ADR 0011). Read that file before performing registry operations; do not work from memory of it. - -## Prompt Execution Flow - -When the user starts a conversation or continues an existing project: - -1. **Check for existing project**: Read `.product-dev/context.json`. If it exists, greet with a brief status summary and suggest next steps. If not, offer to start a new project. - -2. **Determine entry point**: Based on user's input: - - Vague idea for a specific product → `capture_idea` (entry_point) - - Domain interest, no specific product idea → `explore_problem` (entry_point) - - Has a problem but no statement → `create_problem_statement` (skip Phase 00) - - Request for specs → check prerequisites for tech-spec path - - Continuing from previous session → resume using the algorithm below - -### Session Resume Algorithm - -Canonical algorithm: `plugin/docs/registry-operations.md` § Session Resume Algorithm. Summary: read `context.json`, determine the active skill from `current_phase`, find the next unexecuted prompt whose `requires` are all satisfied (evaluating `run_when` gates), and resolve `{{variables}}` from disk — never from conversation history. - -3. **Execute prompt**: - - Read the prompt file from `${CLAUDE_PLUGIN_ROOT}/prompts/...` - - Resolve `{{variables}}` from the context registry - - Present the prompt's output to the user conversationally — not as a formatted dump - - Write the artifact via `setArtifact()` - -4. **Advance**: After each prompt, check `run` type of the next prompt in sequence: - - `always` → proceed automatically with brief transition - - `context_gated` → check `run_when` condition, skip if not met - - If crossing a workflow path boundary (e.g., ideation → flow), confirm with the user - -5. **Tier check**: After each user response, evaluate escalation/de-escalation signals. Adjust tier if warranted. - -### Checkpoints - -After every 2-3 prompts, offer navigation: -- "Does this capture what you're thinking?" (validation) -- "Should we go deeper on [X] or move forward?" (navigation) -- "I see [N] threads here. Which feels most promising?" (prioritization) - -### Re-entry and Iteration - -Canonical rules: `plugin/docs/registry-operations.md` § Re-entry and Staleness. Summary: revisions increment `version`; staleness is computed from `inputs` maps, not guessed; always ask before regenerating downstream artifacts. - ---- - -## Subagent Invocation - -### Tech Spec Writer - -**When:** User reaches the tech-spec path or explicitly asks for technical specifications. - -**Prerequisites:** Verify `solution_concept` and `user_flow` exist in the registry. - -**How:** Spawn `plugin/agents/tech-spec-writer.md` with the Agent tool. The subagent reads artifacts from `.product-dev/artifacts/` directly and writes specs back to the registry. - -**After return:** Present specs one area at a time (data models, API contracts, business rules, NFRs), rating each 0-10 and naming what a 10 would contain. Walk the Decision Log the subagent appended to `technical_spec` (Taste decisions as a batch, User-Challenges one at a time — the design artifacts are the default). Then run the adversarial review loop (5 dimensions, max 3 iterations, convergence guard) per the tech-spec skill. - ---- - -## Plugin - -The framework is packaged as a Claude Code plugin at `plugin/`. Structure follows ADR 0008. Install locally with `claude plugin install --plugin-dir ./plugin`. - -``` -plugin/ -├── .claude-plugin/plugin.json # Manifest: name, description, version, author -├── commands/ # 6 commands (/idea, /problem, /spec, /compile, /summary, /optimize) -│ ├── idea.md -│ ├── problem.md -│ ├── spec.md -│ ├── compile.md -│ ├── summary.md -│ └── optimize.md -├── skills/ # 5 skills (4 workflows + status) -│ ├── product-ideation/SKILL.md -│ ├── product-flow/SKILL.md -│ ├── tech-spec/SKILL.md -│ ├── ux-optimization/SKILL.md -│ └── status/SKILL.md -├── agents/ # 2 subagents -│ ├── tech-spec-writer.md -│ └── ux-extractor.md -├── prompts/ # Bundled prompt library (framework workflow) -│ ├── 01_ux_research/ -│ ├── 02_tech_requirements/ -│ ├── 03_tool_selection_setup/ -│ ├── 04_bridge_to_architecture/ -│ └── 05_implementation_docs/ -└── scripts/ # Bundled compile pipeline - ├── compile_spec.py - ├── validate_spec.py - ├── generate_handoff.py - └── requirements.txt -``` - -**Commands** are entry points — `/idea`, `/problem`, `/spec` dispatch to workflow skills. `/compile` assembles artifacts into a validated spec package. `/summary` assembles a consolidated project brief. -**Skills** own the conversational UX — prompt sequencing, tier escalation, registry operations, checkpoints. Also accessible as `/product-dev:product-ideation`, `/product-dev:product-flow`, `/product-dev:tech-spec`, `/product-dev:status`. -**Agents** are isolated workers — the tech-spec-writer takes design artifacts and produces structured specs. - -The plugin bundles its prompt library and compile scripts under `plugin/prompts/` and `plugin/scripts/`, referenced via `${CLAUDE_PLUGIN_ROOT}` so the bundle is self-contained and resolves regardless of the user's working directory (ADR 0011). The context registry (`.product-dev/`) lives in the user's project directory, not in the plugin. - -### Deliverables - -The workflow produces three deliverables: - -- **Project Brief** (`/summary`) — Assembles design artifacts (problem, persona, hypothesis, flows, prototype scope) into a single document. The "why and what." -- **Technical Spec** (`/spec`) — Data models, API contracts, business rules, NFRs. The "how." -- **Spec Package** (`/compile`) — Compiled output directory with three layers: context (prose), spec (YAML), governance (PRD + ADRs). Validated for cross-reference integrity. Agent-consumable. The "build from this." - -Working artifacts live in `.product-dev/artifacts/`. The compiled spec package lives in `.product-dev/spec-package/`. See `docs/spec-package-schema.md` for the full schema (ADR 0010). - ---- - -## Decisions & ADR References - -- Prompt frontmatter schema: ADR 0001 -- Context registry and state management: ADR 0003 -- Skill/subagent decomposition (3 skills, 1 subagent): ADR 0004 -- Progressive disclosure and tiered engagement: ADR 0006 -- Plugin architecture: ADR 0008 -- Prompt enhancement pattern: ADR 0009 -- Spec package as compilation target: ADR 0010 -- Plugin self-containment (bundled prompts/scripts/registry canon via `${CLAUDE_PLUGIN_ROOT}`): ADR 0011 -- ADR enforcement architecture (checks/run_checks.py, guard hooks): ADR 0012 -- UX optimization reverse pass (ux-optimization skill, /optimize): ADR 0013 -- Live-site audit mode + objective metrics prompt: ADR 0014 -- Runtime audit mode (locally runnable products): ADR 0015 -- gstack-derived patterns (interrogation gates, candidate directions, input provenance): ADR 0016 -- gstack-derived patterns, second wave (quality loops, decision classification, scope walk, learnings): ADR 0017 -- Ops dashboard audit signal (CI reports `audit.finding_count`): ADR 0018 +# product-dev — contributor guide + +This repo builds a Claude Code plugin: an AI-assisted product development +framework that takes a vague idea to a technical spec. This file governs work +**on the repo**. How the plugin behaves for its users is defined inside +`plugin/`, which ships without this file (ADR 0011, ADR 0019). + +Keep this file short and made of pointers. If something here restates a skill, +a prompt, or an ADR, delete the restatement and link to the source. + +## Two modes + +- **Working on the repo** (the default): editing prompts, skills, checks, ADRs, + docs. The rules below apply. Do not start the product workflow or report + `.product-dev/` status unless asked. +- **Using the plugin here** (someone runs `/idea`, `/spec`, `/optimize`, …): + the invoked skill is the instruction set. Follow it, not this file. + +## Where things are defined + +| Topic | Source of truth | +|-------|-----------------| +| Decisions, their status, what enforces them | [ADR index](docs/adrs/README.md), then the record itself | +| Workflow sequences, gates, checkpoints, tier escalation | each skill: [product-ideation](plugin/skills/product-ideation/SKILL.md), [product-flow](plugin/skills/product-flow/SKILL.md), [tech-spec](plugin/skills/tech-spec/SKILL.md), [ux-optimization](plugin/skills/ux-optimization/SKILL.md), [status](plugin/skills/status/SKILL.md) | +| Context registry, session resume, staleness, learnings | [registry-operations.md](plugin/docs/registry-operations.md) | +| What runs when (`run`, `run_when`, `tier`, `requires`) | each prompt's frontmatter under [plugin/prompts/](plugin/prompts) | +| Prompt authoring, plugin development, ADR process | [CONTRIBUTING.md](CONTRIBUTING.md) | +| Spec package format | [spec-package-schema.md](docs/spec-package-schema.md) | +| Checks, hooks, graduation | [checks/README.md](checks/README.md) | + +## Rules for changes + +1. **Find the decision first.** Open the ADR index, then read the record + including its `## Amendment` sections and anything listed under "Amended + by". Later text overrides earlier text. +2. **A request that conflicts with an Accepted ADR is not a dead end.** Say + which record it conflicts with, then either do the compliant version or + draft the change as an appended amendment or a new ADR with status + `Proposed`, and ask. Do not make the conflicting change and adjust the + record to match. +3. **ADRs are append-only.** Append a dated section or write a new numbered + ADR. A new ADR that changes an older one says "amends ADR NNNN", and the + older one gets an appended note naming it. Then run + `python checks/adr_index.py --write`. +4. **Never make a finding go away by editing the check, its config, or a + limit.** Fix the content. If the check is wrong, say so and propose the fix + as its own change. Demoting a check or raising a word limit is the owner's + decision and gets logged in the owning ADR. +5. **The plugin must stand alone.** Anything under `plugin/` references + bundled files through `${CLAUDE_PLUGIN_ROOT}` and never depends on this + file, on `docs/`, or on repo-root paths. +6. **The component set is decided.** Adding or removing a command, skill, or + subagent needs an amendment to ADR 0004 first. +7. **Before pushing:** `python checks/run_checks.py --ci` and + `python -m unittest discover -s checks/tests`. A `block` finding fails CI + and stops the session from finishing; the message names the ADR and the + remedy. +8. **Hook kill-switches are the owner's.** If a guard blocks you, take the + move its message offers or stop and ask. +9. Deferred work goes to GitHub issues, not into docs. Commits are + conventional (`type(scope): description`). User-visible changes get a + CHANGELOG line. + +## Runtime rules that exist only here + +These govern plugin behaviour but were never moved into `plugin/`, so plugin +users do not get them. Tracked in +[issue #62](https://github.com/clownware/product-dev/issues/62); until it +closes, apply them when running the workflow in this repo. + +- **Tier escalation is offered, not applied.** On an escalation signal, set + `tier` to 2 in `context.json`, list the current phase's `tier: 2` prompts + with one-line descriptions, and let the user pick. After each picked prompt, + return to the Tier 1 sequence. +- **Never auto-escalate to Tier 3.** Run every prompt in a phase only on + explicit request. +- **When a `context_gated` condition is unclear, ask the user.** +- **Entry points are exclusive.** `capture_idea` and `explore_problem` both + produce `initial_concept`; run one, never both. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 348eff7..3ca8fee 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ 1. Clone the repository 2. Review `docs/adrs/` for architectural decisions -3. Read `CLAUDE.md` for the full workflow specification +3. Read `CLAUDE.md` for the rules that apply to changes in this repo; the workflow itself is specified by the skills under `plugin/skills/` 4. Install the plugin locally: `claude --plugin-dir ./plugin` ## Prompt Authoring diff --git a/checks/enforcement.config.json b/checks/enforcement.config.json index 04956b8..60e921c 100644 --- a/checks/enforcement.config.json +++ b/checks/enforcement.config.json @@ -18,6 +18,7 @@ { "id": "filename-convention", "adr": "CONTRIBUTING", "tc": "CONTRIBUTING:naming-convention", "status": "block", "added": "2026-07-12", "graduated": "2026-10-12" }, { "id": "objective-parity", "adr": "0014", "tc": "0014:TC-parity", "status": "block", "added": "2026-07-25", "graduated": "2026-10-12" }, { "id": "adr-index", "adr": "0012", "tc": "0012:TC-3", "status": "warn", "added": "2026-10-12", "graduated": null }, - { "id": "adr-amendment-link", "adr": "0012", "tc": "0012:TC-4", "status": "warn", "added": "2026-10-12", "graduated": null } + { "id": "adr-amendment-link", "adr": "0012", "tc": "0012:TC-4", "status": "warn", "added": "2026-10-12", "graduated": null }, + { "id": "claude-md-scope", "adr": "0019", "tc": "0019:TC-1,TC-2", "status": "warn", "added": "2026-10-12", "graduated": null } ] } diff --git a/checks/hooks/pretooluse_guard.py b/checks/hooks/pretooluse_guard.py index f42a2f1..4a0520c 100644 --- a/checks/hooks/pretooluse_guard.py +++ b/checks/hooks/pretooluse_guard.py @@ -3,7 +3,8 @@ Protected (see ADR 0012 and its 2026-10-12 amendment): - docs/adrs/NNNN-*.md that already exist — ADRs are append-only history. - * Creating a NEW ADR file is always allowed (the supersession path). + * Creating a NEW ADR file is always allowed (the supersession path), and + an ADR not yet committed to HEAD is a draft that may be revised freely. * APPENDING to an existing ADR is allowed: an Edit/Write/MultiEdit whose result keeps every existing character and only adds text after it. That is how amendments, graduation-log entries, and supersession notes land. @@ -24,6 +25,7 @@ import json import os import re +import subprocess import sys from pathlib import Path @@ -58,6 +60,25 @@ def protected_kind(target, project_dir): return None +def is_committed(target, project_dir): + """True when the file exists in HEAD. An ADR that is new in the working + tree is still a draft and may be revised freely; it becomes append-only + once committed. Outside a git repository, or on any git error, assume + committed — the guard fails closed.""" + try: + rel = target.resolve().relative_to(project_dir).as_posix() + git = ["git", "-C", str(project_dir)] + head = subprocess.run(git + ["rev-parse", "--verify", "--quiet", "HEAD"], + capture_output=True, timeout=10) + if head.returncode != 0: + return True + found = subprocess.run(git + ["cat-file", "-e", f"HEAD:{rel}"], + capture_output=True, timeout=10) + return found.returncode == 0 + except (OSError, ValueError, subprocess.SubprocessError): + return True + + def is_pure_append(before, after): return after != before and after.startswith(before) @@ -98,8 +119,8 @@ def guard_file_tool(tool_name, tool_input, project_dir): "itself is not available to you. " + OWNER_ONLY ) - if not target.exists(): - return 0 # new ADR file: always allowed + if not target.exists() or not is_committed(target, project_dir): + return 0 # new ADR file, or a draft not yet committed: always allowed before = target.read_text() if tool_name == "Write": @@ -154,7 +175,8 @@ def existing_protected(token, bases, project_dir): candidate = token if os.path.isabs(token) else str(base / token) for match in glob.glob(candidate) or [candidate]: p = Path(match) - if p.is_file() and protected_kind(p, project_dir): + kind = protected_kind(p, project_dir) if p.is_file() else None + if kind == "guide" or (kind == "adr" and is_committed(p, project_dir)): hits.append(p.resolve().relative_to(project_dir)) return hits diff --git a/checks/run_checks.py b/checks/run_checks.py index 1537144..6d81c77 100644 --- a/checks/run_checks.py +++ b/checks/run_checks.py @@ -403,6 +403,30 @@ def check_adr_amendment_link(ctx): return out +CLAUDE_MD_WORD_BUDGET = 1000 + + +def check_claude_md_scope(ctx): + """ADR 0019: CLAUDE.md is a contributor guide made of pointers. Its + relative links must resolve (TC-1) and it stays within a word budget so + restated product specification does not creep back (TC-2).""" + out = [] + path = ROOT / "CLAUDE.md" + if not path.exists(): + out.append(finding("0019", "TC-1", "CLAUDE.md missing — the contributor guide", "CLAUDE.md")) + return out + text = path.read_text() + for target in re.findall(r"\]\(([^)\s]+)\)", text): + if re.match(r"^[a-z][a-z0-9+.-]*:", target) or target.startswith("#"): + continue # external URL or in-page anchor + if not (ROOT / target.split("#", 1)[0]).exists(): + out.append(finding("0019", "TC-1", f"link target {target!r} does not exist — fix the pointer or remove it", "CLAUDE.md")) + words = len(text.split()) + if words > CLAUDE_MD_WORD_BUDGET: + out.append(finding("0019", "TC-2", f"CLAUDE.md is {words} words — budget {CLAUDE_MD_WORD_BUDGET}. Replace restated content with a link to its source; raising the budget is an amendment to ADR 0019", "CLAUDE.md")) + return out + + def check_objective_parity(ctx): """ADR 0014: forward (02_objectives) and reverse (07_objective_metrics) passes both produce `core_objective`. Downstream consumers must be able to substitute @@ -464,6 +488,7 @@ def check_filename_convention(ctx): "objective-parity": check_objective_parity, "adr-index": check_adr_index, "adr-amendment-link": check_adr_amendment_link, + "claude-md-scope": check_claude_md_scope, } diff --git a/checks/tests/test_pretooluse_guard.py b/checks/tests/test_pretooluse_guard.py index ea8ec5f..9c0ce33 100644 --- a/checks/tests/test_pretooluse_guard.py +++ b/checks/tests/test_pretooluse_guard.py @@ -157,6 +157,20 @@ def test_inline_script_that_only_mentions_the_adr_folder_in_text_is_allowed(self "p.write_text(p.read_text() + 'Create docs/adrs/NNNN-short-title.md; see ls docs/adrs/ and docs/adrs/{name}')\nEOF") self.assertAllowed("Bash", {"command": command}) + def test_an_uncommitted_draft_adr_may_be_revised_but_a_committed_one_may_not(self): + def git(*args): + subprocess.run(["git", "-C", str(self.root), "-c", "user.name=t", "-c", "user.email=t@example.invalid", + "-c", "commit.gpgsign=false", *args], check=True, capture_output=True) + git("init", "-q") + git("add", "docs/adrs/0001-example.md") + git("commit", "-q", "-m", "init") + draft = self.root / "docs" / "adrs" / "0002-draft.md" + draft.write_text("# ADR 0002: Draft\n\n## Status\nProposed\n") + self.assertAllowed("Write", {"file_path": str(draft), "content": "# ADR 0002: Rewritten draft\n"}) + self.assertAllowed("Bash", {"command": "rm docs/adrs/0002-draft.md"}) + self.assertBlocked("Write", {"file_path": str(self.adr), "content": "# rewritten\n"}) + self.assertBlocked("Bash", {"command": "rm docs/adrs/0001-example.md"}) + def test_bash_may_create_a_new_adr_file(self): self.assertAllowed("Bash", {"command": "cat > docs/adrs/0002-new.md <<'EOF'\n# ADR 0002\nEOF"}) diff --git a/docs/adrs/0006-progressive-disclosure-tiered-engagement.md b/docs/adrs/0006-progressive-disclosure-tiered-engagement.md index 7ae4aa5..1d3cd61 100644 --- a/docs/adrs/0006-progressive-disclosure-tiered-engagement.md +++ b/docs/adrs/0006-progressive-disclosure-tiered-engagement.md @@ -157,3 +157,20 @@ consecutive clean days — zero findings in every CI run since 2026-07-27. `checks/enforcement.config.json` is authoritative for current status; the "warn" label in the Enforcement section above records the status when it was written. + +--- + +## Amendment (2026-10-12): Where the tier model is implemented (ADR 0019) + +The status note at the top says the tier model, escalation signals, and run +conditionality are "implemented in CLAUDE.md". ADR 0019 made CLAUDE.md the +contributor guide and removed its restatement of this model. The +implementation is the `tier`, `run`, and `run_when` frontmatter on each prompt +and the Tier sections of each workflow skill. + +One gap is recorded rather than hidden: the escalation procedure in this +ADR's "Skill Behavior" (offer deeper prompts and let the user choose) is +stated in full only in CLAUDE.md's "Runtime rules that exist only here" +section. `product-ideation/SKILL.md` says to insert the Tier 2 prompts, and +`product-flow` and `tech-spec` carry no escalation procedure. Issue #62 tracks +moving it into the plugin. diff --git a/docs/adrs/0012-adr-enforcement-architecture.md b/docs/adrs/0012-adr-enforcement-architecture.md index 5dd2c9b..f14a0e7 100644 --- a/docs/adrs/0012-adr-enforcement-architecture.md +++ b/docs/adrs/0012-adr-enforcement-architecture.md @@ -304,3 +304,14 @@ call. A regression test covers the false positive. **Not machine-checkable:** an amendment that changes another ADR without using one of the trigger phrases is invisible to TC-4; and the index says which record to open, not what it means. + +--- + +## Amendment (2026-10-12): A draft ADR is free until committed + +Writing ADR 0019 showed a third no-legal-move case in the guard: a new ADR +file became append-only the moment it was first saved, so a draft could not +be corrected before anyone had reviewed it. The guard now protects an ADR +only once it exists in `HEAD`. A file new in the working tree is a draft and +may be revised or removed. Outside a git repository, or on any git error, the +guard assumes the file is committed and stays closed. Covered by a test. diff --git a/docs/adrs/0016-gstack-derived-patterns.md b/docs/adrs/0016-gstack-derived-patterns.md index 8652f03..e8090dc 100644 --- a/docs/adrs/0016-gstack-derived-patterns.md +++ b/docs/adrs/0016-gstack-derived-patterns.md @@ -102,3 +102,14 @@ no-unsolicited-improvements rule). - Source review: garrytan/gstack `office-hours`, `plan-ceo-review`, `autoplan` skills and its per-branch review-log staleness mechanism (2026-07-24 session). + +--- + +## Amendment (2026-10-12): TC-3 is read against the bundled registry doc (ADR 0019) + +TC-3 expects CLAUDE.md's `context.json` schema to document `inputs`. The +schema moved to `plugin/docs/registry-operations.md` with the ADR 0011 +amendment of 2026-07-25, and ADR 0019 made CLAUDE.md a contributor guide that +points there. TC-3 now reads: `plugin/docs/registry-operations.md` documents +`inputs` on artifact entries, and `status/SKILL.md` documents the staleness +annotation. Still no dedicated check. diff --git a/docs/adrs/0017-gstack-derived-patterns-second-wave.md b/docs/adrs/0017-gstack-derived-patterns-second-wave.md index c020669..0980205 100644 --- a/docs/adrs/0017-gstack-derived-patterns-second-wave.md +++ b/docs/adrs/0017-gstack-derived-patterns-second-wave.md @@ -116,3 +116,14 @@ Adopt all four, adapted to this framework: (scope-decision tables), `/learn` (learnings store) — 2026-07-24 session. - Owner rule: deferred work is tracked as GitHub issues, never in repo docs (see issues #37–#43 precedent). + +--- + +## Amendment (2026-10-12): TC-4 is read against the bundled registry doc (ADR 0019) + +TC-4 expects CLAUDE.md to document `learnings.jsonl`. That documentation +lives in `plugin/docs/registry-operations.md` § Process Learnings since the +ADR 0011 amendment of 2026-07-25, and ADR 0019 made CLAUDE.md a contributor +guide that points there. TC-4 now reads: `plugin/docs/registry-operations.md` +documents `learnings.jsonl` (schema, dedup, recall rules), and all four +workflow skills reference reading it on start. Still no dedicated check. diff --git a/docs/adrs/0019-claude-md-contributor-guide.md b/docs/adrs/0019-claude-md-contributor-guide.md new file mode 100644 index 0000000..67412ba --- /dev/null +++ b/docs/adrs/0019-claude-md-contributor-guide.md @@ -0,0 +1,141 @@ +# ADR 0019: CLAUDE.md Is the Contributor Guide; Runtime Canon Lives in the Plugin + +## Status + +Accepted (2026-10-12) — owner-directed ("Proceed with all recommended enhancements") + +## Context + +The root `CLAUDE.md` had grown to about 2,000 words. Most of it restated +things defined elsewhere: the four workflow sequences (canonical in each +`SKILL.md`), the tier and run-conditionality model (ADR 0006 and prompt +frontmatter), the plugin directory tree and component counts (ADR 0004, +ADR 0008), the deliverables, and a list of ADRs. It addressed two readers in +one voice: an agent using the plugin, and an agent changing the repo. Its +opening instruction — read `.product-dev/context.json` and greet with project +status — is product behaviour, yet it loaded into every contributor session. + +Two things made this more than untidy: + +1. **Restatements drift.** A review on 2026-10-12 found the same failure in + every hand-kept copy: CLAUDE.md said "3 skills, 1 subagent" for ADR 0004 + after ADR 0013 changed it; the README's ADR table had two wrong rows; ADR + 0006's tier tables had needed a correction amendment. ADR 0011's amendment + had already drawn the conclusion for registry operations: "summary plus + pointer only — no inline copy, so there is nothing to drift". +2. **Plugin users never get this file.** Anything that exists only in + CLAUDE.md does not ship. The same review found four runtime rules in that + position, including the escalation procedure added as the fix for test T11. + +The owner also raised separation of concerns: one file mixing runtime +specification, repository layout, and contributor rules is hard to maintain. + +Evidence considered and its limits: the constitution-lift experiment +(chrispezza/portfolio#106, 240 runs on a different repo) found no measurable +behavioural difference between rules in one file and rules split across +layered files, and found in an earlier, confounded phase that trimming +*process rules* cost adherence on the smaller model. It did not test this +repo, and it did not test removing restated product documentation. This +decision therefore does not split rules by layer and does not remove +contributor rules; it removes duplicated product specification and adds +contributor rules that were missing. + +## Decision + +1. **CLAUDE.md is the contributor guide.** It says what the repo is, tells + the two modes apart (working on the repo; using the plugin here), points to + where each thing is defined, and states the rules for changing the repo. +2. **It is made of pointers.** It links to the source of truth instead of + restating it: the generated ADR index for decisions, each `SKILL.md` for + workflow behaviour, `plugin/docs/registry-operations.md` for the registry, + prompt frontmatter for run conditions, CONTRIBUTING for authoring. +3. **Runtime behaviour is canonical inside `plugin/`.** A rule about how the + plugin behaves belongs in a skill, command, agent, prompt, or + `plugin/docs/`. CLAUDE.md may carry such a rule only in a section that + names it as not yet shipped and links the issue tracking its move. At + acceptance that section holds four rules (issue #62). +4. **Contributor rules it now states**, which no document stated before: a + request that conflicts with an Accepted ADR has a legal move (do the + compliant version, or draft an amendment or a Proposed ADR and ask); a + finding is never cleared by editing the check, its config, or a limit; + hook kill-switches belong to the owner. +5. **It stays small.** A word budget of 1,000 keeps restatement from + creeping back. Raising it is an amendment to this ADR. + +This amends ADR 0006: its status note says the tier model and run +conditionality are "implemented in CLAUDE.md"; they are implemented in the +skills and prompt frontmatter, and CLAUDE.md no longer restates them. + +This amends ADR 0016: TC-3 expects CLAUDE.md to document `inputs` in the +`context.json` schema. That schema has lived in +`plugin/docs/registry-operations.md` since the ADR 0011 amendment of +2026-07-25; TC-3 is read against that file. + +This amends ADR 0017: TC-4 expects CLAUDE.md to document `learnings.jsonl`. +Same move, same reading. + +It extends the principle of ADR 0011's 2026-07-25 amendment from registry +operations to everything else the file restated. + +## Alternatives Considered + +- **Leave the file as it was.** Rejected: every restatement in it had drifted + at least once, and it told contributor sessions to act as the product. +- **Split the rules into layered files** (root file plus on-demand files by + topic). Rejected on current evidence: the only test available found no + behavioural gain from layering, and it adds routing that can fail. The + separation that matters here is between audiences, and the plugin directory + already provides it. +- **Move the four unshipped runtime rules into the plugin now.** Deferred to + issue #62: it changes shipped behaviour for `product-ideation`, which is a + product decision. +- **Delete the unshipped rules.** Rejected: one of them is a recorded test + fix, and removing the only copy would lose it silently. + +## Consequences + +**Positive:** + +- Nothing in CLAUDE.md can drift from a skill, a prompt, or an ADR, because + it no longer copies them. +- Contributor sessions load roughly a third of the previous text and are not + told to greet with product status. +- Dogfooding the plugin in this repo now exercises what ships, apart from the + four rules listed as unshipped. +- The conflict rule gives an agent a move other than halting or rewriting the + record. + +**Negative:** + +- An agent must follow a pointer to learn workflow detail it used to have + inline. For repo work it rarely needs that detail; for plugin use the skill + supplies it. +- `scripts/test-chain.md` and `scripts/test-registry.md` were written against + CLAUDE.md orchestration and now describe history; the tests should be run + with the plugin loaded. +- The word budget is a size limit, not a measure of whether the file + restates something. A short restatement passes it. + +## Enforcement + +- **Testable consequences:** + - TC-1: Every relative link in `CLAUDE.md` resolves to a file or directory + in the repository. + - TC-2: `CLAUDE.md` is at most 1,000 words. +- **Checks:** + - TC-1, TC-2 → `checks/run_checks.py :: claude-md-scope` (status: **warn**, + added 2026-10-12) +- **Not machine-checkable:** whether a given sentence restates a source or + points to it; whether a runtime rule has been placed in the plugin or left + here; whether agents follow the conflict rule. The last is the kind of + claim the constitution-lift experiment measures, and it has not been + measured on this repo. +- **Graduation log:** _(empty)_ + +## References + +- ADR 0011 (self-containment; its 2026-07-25 amendment set the pointer + principle), ADR 0012 (enforcement; generated ADR index), ADR 0004 and + ADR 0008 (component set), ADR 0006, ADR 0016, ADR 0017 (amended as above). +- Issue #62 (unshipped runtime rules). +- chrispezza/portfolio#106 (constitution-lift experiment record). diff --git a/docs/adrs/README.md b/docs/adrs/README.md index a762a1c..bc59416 100644 --- a/docs/adrs/README.md +++ b/docs/adrs/README.md @@ -15,19 +15,20 @@ listed under "Amended by" override its earlier text. | [0003](0003-context-registry-state-management.md) | Context Registry and State Management | Accepted | `frontmatter-v2` (block), `placeholder-syntax` (block), `placeholder-resolvability` (block), `dependency-graph` (block) | 0013, 0016 | 2, latest 2026-10-12 | | [0004](0004-skill-subagent-decomposition.md) | Skill and Subagent Decomposition | Accepted | `component-census` (block) | 0013 | 1, latest 2026-10-12 | | [0005](0005-prompt-enhancement-schema.md) | Prompt Enhancement Schema | Deferred | `no-legacy` (block) | — | — | -| [0006](0006-progressive-disclosure-tiered-engagement.md) | Progressive Disclosure and Tiered Engagement | Accepted | `frontmatter-v2` (block) | — | 1, latest 2026-07-25 | +| [0006](0006-progressive-disclosure-tiered-engagement.md) | Progressive Disclosure and Tiered Engagement | Accepted | `frontmatter-v2` (block) | 0019 | 2, latest 2026-10-12 | | [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` (warn), `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) | — | — | | [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) | — | 3, latest 2026-10-12 | +| [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 | | [0014](0014-live-site-audit-and-objective.md) | Live-Site Audit and Core Objective in the Reverse Pass | Accepted | `objective-parity` (block) | — | 1, latest 2026-07-25 | | [0015](0015-runtime-audit-mode.md) | Runtime Audit Mode for Locally Runnable Products | Accepted | no dedicated check | — | — | -| [0016](0016-gstack-derived-patterns.md) | gstack-Derived Patterns — Interrogation, Premise Challenge, Input Provenance | Accepted | no dedicated check | — | — | -| [0017](0017-gstack-derived-patterns-second-wave.md) | gstack-Derived Patterns, Second Wave — Quality Loops, Decision Classification, Scope Walk, Learnings | Accepted | no dedicated check | — | — | +| [0016](0016-gstack-derived-patterns.md) | gstack-Derived Patterns — Interrogation, Premise Challenge, Input Provenance | Accepted | no dedicated check | 0019 | 1, latest 2026-10-12 | +| [0017](0017-gstack-derived-patterns-second-wave.md) | gstack-Derived Patterns, Second Wave — Quality Loops, Decision Classification, Scope Walk, Learnings | Accepted | no dedicated check | 0019 | 1, latest 2026-10-12 | | [0018](0018-ops-dashboard-audit-signal.md) | Report the Enforcement Suite's Finding Count to the Ops Dashboard | Accepted | no dedicated check | — | — | +| [0019](0019-claude-md-contributor-guide.md) | CLAUDE.md Is the Contributor Guide; Runtime Canon Lives in the Plugin | Accepted | `claude-md-scope` (warn) | — | — | ## Reading the columns diff --git a/scripts/test-chain.md b/scripts/test-chain.md index 61591aa..5995dad 100644 --- a/scripts/test-chain.md +++ b/scripts/test-chain.md @@ -1,5 +1,7 @@ # Test Chain: Tier 1 Artifact Chaining Verification +> **Historical (2026-10-12):** this document was written when the root `CLAUDE.md` orchestrated the workflow. It is now a contributor guide (ADR 0019) and the skills under `plugin/skills/` are the orchestration. Run these tests with the plugin loaded (`claude --plugin-dir ./plugin`); references to CLAUDE.md instructions below describe the earlier setup. + Walks through the complete Tier 1 prompt chain using the tea tracker concept. Validates that CLAUDE.md orchestration works: prompts read from disk, artifacts chain via conversation memory, gates evaluate correctly, and the subagent spawns. ## Prerequisites diff --git a/scripts/test-registry.md b/scripts/test-registry.md index 9febf3f..48b7329 100644 --- a/scripts/test-registry.md +++ b/scripts/test-registry.md @@ -1,5 +1,7 @@ # Context Registry Validation Script +> **Historical (2026-10-12):** this document was written when the root `CLAUDE.md` orchestrated the workflow. It is now a contributor guide (ADR 0019) and the skills under `plugin/skills/` are the orchestration. Run these tests with the plugin loaded (`claude --plugin-dir ./plugin`); references to CLAUDE.md instructions below describe the earlier setup. + Manual test plan for Phase 3 context registry. Run in Claude Code from the repo root. ## Prerequisites