diff --git a/.claude/agents/adr-conformance-reviewer.md b/.claude/agents/adr-conformance-reviewer.md index f05c59f..b6c05a0 100644 --- a/.claude/agents/adr-conformance-reviewer.md +++ b/.claude/agents/adr-conformance-reviewer.md @@ -27,7 +27,7 @@ If only a branch name is given, run `git diff main...HEAD` (or against the named - **A boundary impl** (e.g., a new backend, driver, or adapter) → the ADR that defines the behaviour/interface - **A new top-level module or service** → architectural ADRs about layering, hot-path vs cold-path placement, or extension seams -3. **Read each intersecting ADR — and follow its `Refines:` / `Supersedes:` chain.** Don't skim; the "Decision" and "Consequences" sections are the contract. If an intersecting ADR is refined or partially superseded by a later one (named in the index, or in a child's `Refines:` / `Supersedes:` field), **load the child too**. A *refiner* narrows a parent clause to a scoped reading *without* superseding it (the parent stays Accepted); a *partial supersession* replaces one named clause while the rest of the parent stands. A parent's literal clause is often narrower in practice than it reads — the scoped reading in the chain is authoritative. +3. **Read each intersecting ADR — and follow its `Refines:` / `Extends:` / `Supersedes:` chain.** Don't skim; the "Decision" and "Consequences" sections are the contract. If an intersecting ADR is refined or partially superseded by a later one (named in the index, or in a child's `Refines:` / `Supersedes:` field), **load the child too**. A *refiner* narrows a parent clause to a scoped reading *without* superseding it (the parent stays Accepted); a *partial supersession* replaces one named clause while the rest of the parent stands. An *extender* adds an obligation beside a parent clause that stays satisfied — the asymmetric case: the parent passes unchanged while the child can fail, so a diff clean against the parent is not clean until every extender is checked. A parent's literal clause is often narrower in practice than it reads — the scoped reading in the chain is authoritative. **Also read the project's reconciliation layer, if one exists.** Some projects keep a buildable reconciliation document that turns frozen upstream references + charter ADRs into the as-built spec; a scoped reading there is authoritative for the build. Read it before flagging an apparent ADR-literal violation. @@ -52,6 +52,7 @@ If only a branch name is given, run `git diff main...HEAD` (or against the named - **Be specific.** "Violates ADR-NNNN" is useless without "because the LLM is acting as a controller (line 42 calls `Servo.set_angle/2` directly) instead of emitting a tool call". - **Don't flag what isn't a violation.** ADRs don't govern every line; if a change is orthogonal, it's orthogonal. False positives erode trust in this reviewer. - **A literal ADR clause may be refined — confirm before flagging.** Before reporting "violates ADR-NNNN Dn", check that no `Refines:` child ADR or reconciliation-layer entry narrows that clause for the case at hand. A flag against a correctly-scoped clause is a false-positive, and false-positives erode trust faster than misses. +- **Consult `docs/adr/corrections.md` before flagging a claim.** A citation, figure, attribution or formula already recorded wrong there is cited by its `C-` number, never restated as a finding; a wrong claim not yet recorded is reported as one, with the register named as its destination. - **Don't propose new ADRs.** That's an `/adr-new` invocation, not a review finding. - **Don't review style, tests, or logging.** Other reviewers cover those. Stay scoped to ADR conformance. diff --git a/.claude/agents/cascade-rule-reviewer.md b/.claude/agents/cascade-rule-reviewer.md index feec02c..b70d1d6 100644 --- a/.claude/agents/cascade-rule-reviewer.md +++ b/.claude/agents/cascade-rule-reviewer.md @@ -15,7 +15,7 @@ The `adr-conformance-reviewer` covers ADRs in `docs/adr/`. The `logging-discipli The contracts I check for compliance: -Every file under `.claude/rules/` **except** `logging.md` (the logging reviewer's) and the `pr-review.md` pair (the rubric I apply, not a contract I check the diff against). That set is derived, not enumerated — a rule added, split or renamed is in scope the moment it lands. Today it is: `testing.md` (three-regime testing; the named tests trace to acceptance criteria), `cbk-conventions.md` and `cbk-conventions-reference.md` (branch naming, title prefixes, close markers, the `[skip ci]` rule, mutation discipline, workstream slug stability), `simplification.md`, `knowledge-backend.md` (writes HITL-gated and default-SKIP; the repo is canonical for code and cascade artifacts), `workflows.md`, `tooling.md`, `orchestration.md` and `orchestration-reference.md`. Read the reference half of a split rule when the contract's pointer names the section the diff touches. +Every file under `.claude/rules/` **except** `logging.md` (the logging reviewer's) and the `pr-review.md` pair (the rubric I apply, not a contract I check the diff against). That set is derived, not enumerated — a rule added, split or renamed is in scope the moment it lands. Today it is: `testing.md` (three-regime testing; the named tests trace to acceptance criteria), `cbk-conventions.md` and `cbk-conventions-reference.md` (branch naming including the issue-less form, title prefixes, close markers, the `[skip ci]` rule, mutation discipline, workstream slug stability, and the reference half's § Licensing, § Dependency settle-window and § .gitignore anchoring), `simplification.md`, `knowledge-backend.md` (writes HITL-gated and default-SKIP; the repo is canonical for code and cascade artifacts), `workflows.md`, `tooling.md`, `orchestration.md` and `orchestration-reference.md`. Read the reference half of a split rule when the contract's pointer names the section the diff touches. Out of scope (handled by other reviewers): - `.claude/rules/logging.md` → `logging-discipline-reviewer` diff --git a/.claude/commands/enrich.md b/.claude/commands/enrich.md index d349f4b..f1058ac 100644 --- a/.claude/commands/enrich.md +++ b/.claude/commands/enrich.md @@ -41,7 +41,7 @@ Synthesize into the resolved intent + the resolved forks. Don't move to Step 3 u A read-only investigation, like `/finish`'s plan-mode research — **do not write code here.** Ground the resolved forks in the actual code: the files / symbols the issue touches, the constraints (ADRs, `.claude/rules/*`, `docs/STANDARDS.md`), the dependencies, and the gotchas. -- `Grep` / `Glob` for the surface; find-references / symbol-info / call-site tooling for the blast radius; documentation-lookup tooling to confirm a library's actual behavior; read-only data/schema inspection when the issue is data-shaped. +- `Grep` / `Glob` for the surface; find-references / symbol info / call sites for the blast radius (which tool: `tooling.md` § Code intelligence); documentation-lookup tooling to confirm a library's actual behavior; read-only data/schema inspection when the issue is data-shaped. **Scale the investigation to the surface — when the tooling is available:** diff --git a/.claude/commands/finish-procedure.md b/.claude/commands/finish-procedure.md index c065a40..5aa5f5a 100644 --- a/.claude/commands/finish-procedure.md +++ b/.claude/commands/finish-procedure.md @@ -183,6 +183,8 @@ The principle for Steps 8–9: **the executor handles findings the codebase need - **Defer (N)** — ` — conflicts with `. - **Reject (N)** — one-line dismissal. + **The roadmap row, before `gh pr create`** — where the project keeps `docs/cbk/ROADMAP.md` and the spec names the flip: update the row and `## Now` on this branch as the last commit (`docs(cascade):`, no CI-skip marker — it is HEAD at the flip), with the PR number predicted from the shared issue-and-PR sequence (GitHub's REST API considers every pull request an issue and its issue endpoints return both — `https://docs.github.com/en/rest/issues/issues`, read 2026-09-06 — so the newest number across both is the last item of an issues listing) (`gh api 'repos/{owner}/{repo}/issues?state=all&sort=created&direction=desc&per_page=1'` plus one) and checked against the number `gh pr create` returns; one correcting commit on a mismatch; never a separate PR (the contract, item 8). + If `gh pr create --draft` fails (auth, missing scope, draft PRs disabled, rate limit, PR already exists for this branch), stop and surface the failure with per-step status. ## Step 11: Hand off @@ -199,8 +201,12 @@ If any finding was classified **Defer** because it would conflict with an ADR or **Roll-forward offer** (HITL). If the run surfaced deferred context that a *later* unit of work will need — TODOs, contract facts the next issue depends on, intentional omissions, or a follow-up worth its own issue — **offer** to post a roll-forward comment **wherever it's relevant**: the next issue, a sibling, a downstream milestone issue, a meta-issue, or a brand-new follow-up. Do **not** hardwire it to "the next issue." Default to posting **post-merge** unless the deferred context blocks another issue's start. If nothing was deferred, skip this silently. +**Backward sweep** (HITL offer, beside the roll-forward). What this run landed can make a sibling spec's *absence* claim false — "no adapter exists yet", "the table is not wired". The candidate set is closed: the open siblings under the same parent issue carrying `cascade-depth:roughed-in`, read in **one** call (`gh api repos/{owner}/{repo}/issues/{parent}/sub_issues` on github-issues, the parent's children on linear) and capped by the milestone's R-count — never the whole backlog. Check absence claims only, and verify each by **running the body's own command** (its grep, its test, its probe), not by recalling what this run did. Separate the two kinds: a claim this landing falsified (report it, with the commit) from a claim that was false when written (report it as a spec defect). **Default to not reporting** — a claim that still holds is not mentioned. The repair is rough-in's in-place body refresh, offered to the operator; this run never edits an issue body. An empty sweep is a one-line pass — `backward sweep: N siblings, no absence claim falsified` — never an omitted line. + **Optional learning-runbook write to Notion** (only when knowledge backend = `notion` is configured for this repo, per `.cascade/backends.toml`): if this execution surfaced a learning that would be a useful durable cross-project runbook entry (e.g., a non-obvious gotcha with the stack, a recipe for a recurring task spanning repos, a corrected understanding of an external system's behavior), prompt the operator: *"Want to promote this learning to a Notion runbook page under the Engineering Wiki? Defaults to SKIP."* Defaults to **SKIP**. Per `.claude/rules/knowledge-backend.md` § "When to write" — this is the rare opt-in path; HITL-gated; never default. If the operator opts in, announce the planned write (title, parent, body preview) before committing. This step is gated to genuinely cross-project learnings only — local-to-the-PR observations stay in the PR body and the hand-off summary, not in Notion. +**Post-merge checklist** (the hand-off ends with it; the operator runs it after the merge): `git switch main && git pull --ff-only && git fetch --prune`; post the drafted roll-forwards; close the meta-issues the close marker did not; append the frame's `## Rough-in events` row when this PR was the milestone's capstone; flip the roadmap row if Step 10 did not; the next runnable command, read off the roadmap. A research milestone's capstone is the verdict artifact — name it and its owner. + Do not call `gh pr ready` and do not merge — those remain the user's calls. The user's draft-review pass is where Surface findings get decided; they can ask you to apply any of them in the PR-feedback-loop turn, or wave them through. ## What `/finish` does NOT do @@ -209,7 +215,7 @@ Do not call `gh pr ready` and do not merge — those remain the user's calls. Th - **Does not handle re-rough-in.** Surface the need and return to chat for the rough-in skill. - **Does not auto-create dependent issues.** If the work reveals a missing sub-sub-issue, surface and return to chat for rough-in. - **Does not bypass dependencies.** Refuse to proceed when dependencies are unmet. -- **Does not modify cascade artifacts** (`docs/cbk/blueprint.md`, `docs/cbk/frame-NN.md`, etc.). Those are produced by chat-skill cascade phases. +- **Does not modify cascade artifacts** (`docs/cbk/blueprint.md`, `docs/cbk/frame-NN.md`, etc.). Those are produced by chat-skill cascade phases. *The status surfaces are carved out: `docs/cbk/ROADMAP.md`'s row and a frame's `## Rough-in events` table are not cascade events; Step 10 flips the row on this branch when the spec names it, the post-merge checklist otherwise (the contract wins where the two differ).* - **Does not modify ADRs.** ADRs are immutable once accepted; revisions happen via new ADRs that supersede the old (chat-skill territory, not `/finish`). - **Does not make workstream-level or framing-level decisions.** Those belong to upstream cascade phases (blueprint, framing, rough-in). - **Does not skip `/simplify` or `pr-review-toolkit:review-pr`.** The simplify and review passes both run before the PR opens; the simplify pass is non-negotiable per `docs/STANDARDS.md` § Step 4, and the review-toolkit findings are auto-triaged with the four-class rubric (Apply for correctness/validity/defensive; Surface for taste/style; Defer/Reject for conflicts and agent errors). The one exception is an explicit break-glass marker (Step 1), which waives exactly the `pr-review-toolkit:review-pr` half and is recorded on that skill's `## Review gate` line. The orchestrated sweep supplements the two skills and never substitutes for either. diff --git a/.claude/commands/finish.md b/.claude/commands/finish.md index b472b23..4ba83f7 100644 --- a/.claude/commands/finish.md +++ b/.claude/commands/finish.md @@ -32,8 +32,8 @@ This command is contract-first. On 2026-09-03 an A/B gave two workhorse-tier exe 5. **Atomic commits** in Conventional Commits form with the workstream's scope: the red tests, the implementation, each simplify Apply, each review Apply — one focused commit each, never amended across a logical boundary, never squashed on the branch. The squash happens at merge on the base branch. No code or test commit carries the CI-skip marker (`cbk-conventions.md` § `[skip ci]` rule). 6. **The gate, green.** The project's `check` task passes before simplify and review run, and again after every applied finding. Unrelated red is surfaced, not muted — never skipped, expected-failed or commented out to pass. 7. **The review floor, actually run, with the bounded sweep beside it, then triage — once.** First **`/simplify`**, as a skill invocation; triage its findings by the four-class rubric and land its Apply items one commit each with `check` re-run after each. Then, **concurrently**: **`pr-review-toolkit:review-pr`** with no arguments, as a skill invocation, and — where a multi-agent orchestration surface exists — **the review workflow** (`.claude/workflows/review-sweep.js`), sized to the diff and never to the session's effort setting, with the pre-filtered changed-path list passed as `files`. The project-local reviewers ride in it from the roster the workflow reads at runtime (`.claude/rules/pr-review.md` § Project-local agents); beyond them you name the finders this diff needs — none on a small single-surface change the two skills already cover, several on a cross-layer one — and the workflow logs its planned agent count before it finds anything and returns its own gate line. Without an orchestration surface, dispatch the project-local reviewers directly and record `skipped — no orchestration surface` on the sweep's line. The sweep supplements the two skills and never substitutes for either. Triage waits for both to return and stays with you (`pr-review.md` § Triage rubric; the reference half's calibration read at triage): Apply and Apply-with-care land as their own commits with `check` re-run; Surface, Defer and Reject are recorded, never applied. A skill that is uninstalled or errors is a stop-and-surface. A skill that runs without its agent fan-out because the Agent tool is unavailable in your context counts as invoked, and its dropped dimensions are recorded on its `## Review gate` line — never described as equivalent. A failed review agent is dropped coverage, not zero findings. **Describing a review is not running one**: a claimed invocation that did not happen fails this contract outright. The floor runs when the known work is complete and is not run again on a later delta — the reviewer round on the flip covers that, and `/pr-respond` answers it. -8. **A draft PR** on the pushed branch: title in Conventional Commits with the workstream's scope; body with the planning axis's close marker (`Closes #$1` on github-issues, `Closes $1` on linear — in the body, not only the title; `cbk-conventions.md` § Closes-keyword conventions); a summary of what changed and why, with the ADRs, frame milestone and rules it relies on; a **`## Review gate`** block, written before `## Triage`, in the shape `.claude/rules/pr-review.md` § The floor states — one line each for `/simplify`, `pr-review-toolkit:review-pr` and the sweep, each stating run-or-not with counts and dropped coverage, a waived skill on its line with the break-glass reason, the sweep's line transcribed from the record the workflow returns; a **`## Triage`** block listing every finding under its class — Apply and Apply-with-care as `SHA: fix`, Surface and Defer with the agent's verbatim rationale, Reject with a one-line dismissal; and any review block or label the project's conventions require when the diff touches a surface they guard. A docs commit that would be HEAD at the flip carries no CI-skip marker (`cbk-conventions.md` § Auto-review trap). The body's close markers name every issue the run resolves — a pre-flight meta-issue the work settles as well as `$1`. The PR is never flipped to ready and never merged by this run; both are the operator's. -9. **A hand-off** that stands on its own: the PR URL; the triage counts in one line; the three `## Review gate` lines verbatim — an invocation the transcript does not show was not made; the Surface and Defer entries verbatim; every operational criterion still open with what the operator must do; the one next action; an offer to post roll-forward context wherever it is relevant — the next issue, a sibling, a meta-issue, a new follow-up — post-merge unless it blocks another issue's start; an offer to sweep the open sibling issues for absence claims this run made false. Deferred conflicts with an ADR lead the hand-off. A learning that should outlive the PR is offered to the knowledge backend only where one is configured, HITL-gated and default-skip (`.claude/rules/knowledge-backend.md` § When to write); otherwise it goes into a rule file or an ADR. +8. **A draft PR** on the pushed branch: title in Conventional Commits with the workstream's scope; body with the planning axis's close marker (`Closes #$1` on github-issues, `Closes $1` on linear — in the body, not only the title; `cbk-conventions.md` § Closes-keyword conventions); a summary of what changed and why, with the ADRs, frame milestone and rules it relies on; a **`## Review gate`** block, written before `## Triage`, in the shape `.claude/rules/pr-review.md` § The floor states — one line each for `/simplify`, `pr-review-toolkit:review-pr` and the sweep, each stating run-or-not with counts and dropped coverage, a waived skill on its line with the break-glass reason, the sweep's line transcribed from the record the workflow returns; a **`## Triage`** block listing every finding under its class — Apply and Apply-with-care as `SHA: fix`, Surface and Defer with the agent's verbatim rationale, Reject with a one-line dismissal — **the body is the audit surface for the PR's whole life**: each later feedback round appends its own `## Triage — round N` block there through `/pr-respond`, never a comment alone, so a reader of the merged PR sees every finding and its disposition in one place; and any review block or label the project's conventions require when the diff touches a surface they guard. **The roadmap row rides this branch, as its last commit before the PR** — where the project keeps `docs/cbk/ROADMAP.md` and the spec names the flip: after the floor (item 7) and before `gh pr create`, the row for this step and the `## Now` paragraph are updated to cite this issue and this PR — *done* when this is the milestone's last R-issue, the progress note otherwise — as one `docs(cascade):` commit with no CI-skip marker (it is HEAD at the flip, and the marker would suppress the auto-review and the required checks — `cbk-conventions.md` § Auto-review trap). The PR number is the next in the repository's shared issue-and-PR sequence (GitHub's REST API considers every pull request an issue and its issue endpoints return both — `https://docs.github.com/en/rest/issues/issues`, read 2026-09-06 — so the newest number across both is the last item of an issues listing) (`gh api 'repos/{owner}/{repo}/issues?state=all&sort=created&direction=desc&per_page=1'` plus one) and is checked against the number `gh pr create` returns; a mismatch is one correcting commit. Never a separate PR: a one-row flip with its own PR costs a CI run for nothing, and a flip committed after `gh pr create` costs the same run on the open PR. The row reads *done* on this branch while the PR is open; it reaches the base branch only through this PR, which is what makes it true. Where the spec did not name the flip, the post-merge checklist (item 9) flips it. Any other docs commit that would be HEAD at the flip carries no CI-skip marker either. The body's close markers name every issue the run resolves — a pre-flight meta-issue the work settles as well as `$1`. The PR is never flipped to ready and never merged by this run; both are the operator's. +9. **A hand-off** that stands on its own: the PR URL; the triage counts in one line; the three `## Review gate` lines verbatim — an invocation the transcript does not show was not made; the Surface and Defer entries verbatim; every operational criterion still open with what the operator must do; the one next action; an offer to post roll-forward context wherever it is relevant — the next issue, a sibling, a meta-issue, a new follow-up — post-merge unless it blocks another issue's start; and the **backward sweep** beside it — the open siblings under the same parent carrying `cascade-depth:roughed-in`, read in one call and capped by the milestone's R-count; their *absence* claims only ("no X exists yet", "Y is not wired"), each verified by running the body's own command rather than by recollection; a claim this run's landing falsified is separated from one that was false when written; the default is to report nothing — a claim that still holds is not mentioned — and the repair is rough-in's in-place body refresh, never an edit from this run; an empty sweep is a one-line pass, not an omission. Deferred conflicts with an ADR lead the hand-off. The hand-off ends with the **post-merge checklist** the operator runs after the merge: sync and prune (`git switch main && git pull --ff-only && git fetch --prune`); post the roll-forwards the hand-off drafted; close the meta-issues the close marker did not; append the frame's `## Rough-in events` row when the PR is a milestone's capstone; flip the roadmap row if this run did not (item 8); and the next runnable command, read off the roadmap. A research milestone's capstone is the verdict artifact, owned by name in the checklist. A learning that should outlive the PR is offered to the knowledge backend only where one is configured, HITL-gated and default-skip (`.claude/rules/knowledge-backend.md` § When to write); otherwise it goes into a rule file or an ADR. ## The tests every finish must pass @@ -45,9 +45,11 @@ This command is contract-first. On 2026-09-03 an A/B gave two workhorse-tier exe - **Claims match code.** No comment, commit message or PR-body sentence asserts something the diff contradicts. - **Honesty of the gate.** The three `## Review gate` lines are literally true — an invocation the transcript does not show was not made — and every Apply commit named in `## Triage` exists on the branch with the stated SHA, one finding per commit. - **Green.** The project's `check` task passes at the branch head. +- **Backward sweep.** The hand-off carries the sweep's line — the sibling count read in one call, each absence claim checked by the body's own command, the falsified separated from the false-when-written, or the one-line pass — never an omitted line. +- **Measurement issues.** For an issue whose criteria are measurements, the **verdict rule**'s commit precedes the first measurement commit on the branch (`git log --reverse`), the capstone shows the findings table unchanged against the base branch, and every departure from the rule is weighed in the verdict rather than folded into it. - **No improvisation past the spec.** A spec defect — unverifiable criterion, ADR conflict, missing dependency, drifted doc — is surfaced and the run stops; it is not patched in flight. - **Partial failure surfaces.** A failed external call — `git`, `gh`, an MCP call, the check task, a skill — stops the run, and the hand-off states what reached the remote and what did not, with the choices and their consequences. No blind retry. ## What this command does not do -It does not modify the issue body, re-rough-in, create dependent issues, bypass dependencies, modify cascade artifacts or ADRs, make workstream- or framing-level decisions, skip either skill of the floor (the break-glass marker waives exactly the toolkit half, recorded), run the floor twice, flip the PR to ready, merge, or respond to auto-review comments (that is `/pr-respond`). It does not write to the knowledge backend as a side effect of anything above. +It does not modify the issue body, re-rough-in, create dependent issues, bypass dependencies, modify cascade artifacts or ADRs — the **status surfaces** are carved out of that ban: `docs/cbk/ROADMAP.md`'s row and a frame's `## Rough-in events` table are not cascade events, and item 8 says who flips them (this run, on its own branch, when the spec names the flip; the post-merge checklist otherwise) — make workstream- or framing-level decisions, skip either skill of the floor (the break-glass marker waives exactly the toolkit half, recorded), run the floor twice, flip the PR to ready, merge, or respond to auto-review comments (that is `/pr-respond`). It does not write to the knowledge backend as a side effect of anything above. diff --git a/.claude/commands/intake.md b/.claude/commands/intake.md index c0e0efd..2c5676c 100644 --- a/.claude/commands/intake.md +++ b/.claude/commands/intake.md @@ -30,7 +30,7 @@ If the reference can't be resolved (issue not found, ambiguous, resolves to a PR A read-only investigation, like plan mode — **do not write code here**. Trace the code paths implicated by the symptom: -- `Grep` / `Glob` for the symptom's surface; code-navigation tooling for find-references / symbol info / call sites; a library-docs source to confirm a dependency's actual behavior before blaming it; read-only query tooling to inspect the data store's shape when the bug is data-shaped. +- `Grep` / `Glob` for the symptom's surface; find-references / symbol info / call sites (which tool: `tooling.md` § Code intelligence); a library-docs source to confirm a dependency's actual behavior before blaming it; read-only query tooling to inspect the data store's shape when the bug is data-shaped. **Scale the investigation to the bug's surface — when the tooling is available:** diff --git a/.claude/commands/pr-respond.md b/.claude/commands/pr-respond.md index b516bc1..3339450 100644 --- a/.claude/commands/pr-respond.md +++ b/.claude/commands/pr-respond.md @@ -70,7 +70,7 @@ For each finding classified **Apply** or **Apply with care**: **One commit per Apply**, not bundled. The atomic-commit history is what the reviewer reads to verify each action; bundling defeats that audit surface. -**Tooling note.** If a comment asks for a local-symbol rename or a cross-file refactor, prefer a semantic code-navigation tool (find-references / safe-rename) if your setup provides one, over hand-editing call sites. Before making a defensive change the reviewer flagged against a library's behavior, verify that behavior against current documentation rather than from memory. +**Tooling note.** If a comment asks for a local-symbol rename or a cross-file refactor, prefer find-references / safe-rename tooling (which tool: `tooling.md` § Code intelligence) over hand-editing call sites. Before making a defensive change the reviewer flagged against a library's behavior, verify that behavior against current documentation rather than from memory. ## Step 5: Reply to every thread @@ -98,9 +98,11 @@ git push origin If the push fails (auth, network, branch protection that requires being up-to-date with base), stop and surface per § Partial failure handling. Local commits with no remote means the reviewer sees replies citing commit SHAs that aren't visible — confusing. -## Step 7: Top-level summary comment +## Step 7: Top-level summary comment — and the body's round block -Post one top-level comment on the PR (an issue comment, not a review) summarizing the response cycle: +**Append the round to the PR body first.** The PR body is the audit surface for the PR's whole life (`finish.md` item 8): append a `## Triage — round N` block — N the count of feedback rounds so far — listing every thread of this round under its class in the same `SHA: fix` / verbatim-rationale / one-line-dismissal shape as the original `## Triage`, via `gh pr edit $1 --body-file` on the fetched body (append, never rewrite; the original `## Review gate` and `## Triage` blocks stay as they were). A comment alone scrolls away; the body is what a reader of the merged PR opens. + +Then post one top-level comment on the PR (an issue comment, not a review) summarizing the response cycle: ```markdown ## /pr-respond summary diff --git a/.claude/hooks/protect-immutable-adrs.sh b/.claude/hooks/protect-immutable-adrs.sh index 59a094e..21985c8 100755 --- a/.claude/hooks/protect-immutable-adrs.sh +++ b/.claude/hooks/protect-immutable-adrs.sh @@ -9,6 +9,8 @@ # - creating a new ADR file (NNNN doesn't exist yet) # - editing docs/adr/template.md # - editing docs/adr/README.md (the index) +# - editing docs/adr/corrections.md (the append-only claim register — where a wrong +# citation, figure, attribution or formula in an accepted ADR is corrected) # Blocked: # - any Edit/Write/MultiEdit on docs/adr/NNNN-*.md when that file already exists # @@ -62,8 +64,9 @@ this one. Add 'Supersedes: ADR-NNNN' to the new ADR's frontmatter and update the old one's status only via that new ADR's existence (do not edit the old file's status field directly — the README index expresses supersession). -If you genuinely need to fix a typo, do it via a separate, explicit commit -that the user has reviewed in advance. +A wrong citation, figure, attribution or formula is not a decision revision: +record it as an entry in docs/adr/corrections.md (append-only) and leave this +file as written. EOF exit 2 fi @@ -72,7 +75,8 @@ EOF BLOCKED: ADRs are immutable (per ADR-0000). File: $rel -Write a new ADR that supersedes this one instead of editing it. +Write a new ADR that supersedes this one instead of editing it; a wrong claim +(citation, figure, attribution, formula) goes to docs/adr/corrections.md. EOF exit 2 fi diff --git a/.claude/hooks/protect-main-branch.sh b/.claude/hooks/protect-main-branch.sh index ce7f274..c66c798 100755 --- a/.claude/hooks/protect-main-branch.sh +++ b/.claude/hooks/protect-main-branch.sh @@ -70,7 +70,8 @@ BLOCKED: git commit on '$branch'. Branch-first is the rule (cbk-conventions.md § Branch naming; /finish creates the branch before any code lands). Create the feature branch, then re-run the commit: - git switch -c /-- + git switch -c /-- # cascade issue + git switch -c / # operator-directed maintenance, no issue If this block is genuinely wrong (rare), the operator can run the commit themselves outside Claude Code. diff --git a/.claude/hooks/require-repo-root-for-agents.sh b/.claude/hooks/require-repo-root-for-agents.sh index 8268c83..e309728 100755 --- a/.claude/hooks/require-repo-root-for-agents.sh +++ b/.claude/hooks/require-repo-root-for-agents.sh @@ -24,8 +24,13 @@ # rename degrades to a no-op rather than silence. # Timing: the guard reads the payload's `cwd` (a common field on every hook # event — https://code.claude.com/docs/en/hooks-guide § How hooks -# work) BEFORE the tool runs, so a `cd` inside the same call does not -# help: return to the root as its own command, then relaunch. +# work) BEFORE the tool runs. That field is the SESSION's working +# directory — where Claude Code was launched — not the Bash tool's +# persisted shell directory: a real dispatch made after `cd docs` in +# the shell was allowed, with the payload cwd still at the root (dated +# observation, 2026-09-07). The confirmed deny is a session launched +# from a subdirectory (the verification block's payload dry-run); the +# remedy there is to relaunch the session from the root. # Path: registered as ${CLAUDE_PROJECT_DIR}/.claude/hooks/… — handlers run # in the current directory (https://code.claude.com/docs/en/hooks), so # a bare relative path would not resolve from the very subdirectory diff --git a/.claude/rules/cbk-conventions-reference.md b/.claude/rules/cbk-conventions-reference.md index 67afad2..2079e4e 100644 --- a/.claude/rules/cbk-conventions-reference.md +++ b/.claude/rules/cbk-conventions-reference.md @@ -36,7 +36,7 @@ docs/cbk/ **Chronological tracking lives in two places**: -1. **`docs/cbk/README.md`** — across-cascade timeline. Status column tracks Active / Completed / Superseded by frame-NN / Abandoned. Mirrors the shape of `docs/adr/README.md`. +1. **`docs/cbk/README.md`** — across-cascade timeline. Status column tracks Active / Completed / Superseded by frame-NN / Abandoned. Mirrors the shape of `docs/adr/README.md`. Scaffold creates it from the scaffold skill's `references/templates/cascade-events-index-template.md`; blueprint, framing and rough-in append a row and a phase note each. 2. **`## Rough-in events` section inside each frame-NN.md** — per-frame timeline of rough-in events that built against that framing. Append-only table within the frame document. Useful for "which milestones from this framing have been roughed-in, on what date, with what capstone PR" lookups without leaving the frame. Both serve distinct jobs: README is the across-frames table of contents; in-frame events log is the per-frame log. Don't conflate. @@ -239,8 +239,38 @@ Two invariants keep the window honest; violate either and the policy inverts fro **CI workflow actions are dependencies too.** A tag reference (`uses: vendor/action@vN`) is mutable and open to tag-retag compromise: pin every `uses:` to a full commit SHA with a trailing version comment (`@ # vN.N.N`), resolve initial pins to the newest release in the current major that satisfies the settle window, and let the update bot's CI-actions ecosystem entry maintain the pins routinely (it carries the release-age floor like every other ecosystem). +**Age is necessary, not sufficient.** A release that has cleared the window can still be the wrong one to adopt: before a bump, check whether the fix the project needs is **merged upstream but not yet released** — adopting the latest release then buys nothing. Two paths exist for that case, and the second is gated: (a) a **git-revision pin** on the upstream commit, carrying in its manifest comment the date, the upstream PR, and a **retire trigger** ("replace with the registry release ≥ vX.Y that contains it"); (b) **promote-at-registry-version** — the pin is swapped for the registry release the moment it exists *and* has itself cleared the window; never a promotion the day it ships. A git-rev pin that outlives its trigger is a rail that outlived its evidence. + +**Toolchain and single-binary pins take the floor by hand.** No update bot covers the toolchain manager's pins (`mise.toml` `[tools]`, `.tool-versions`, `rust-toolchain.toml`, `.nvmrc`), a container base-image tag, or a single-binary tool fetched by URL. For each: the commit that sets the pin states the release's **settled age** in its body ("v3.2.1, released 2026-08-20, 17 days old at pin"), and the project names **one tracking mechanism** for the next bump — an open issue with a date, a scheduled check in the task runner, or a project automation — in the filled-in copy of this section. A pin with no named tracker is a pin nobody will bump. + +**Inactive ecosystem stubs carry the floor too.** A commented-out or scheduled-off ecosystem entry in the update-bot config ships *with* its cooldown block, so uncommenting it never produces a bare entry (the starter `dependabot.yml` is written this way). + +**Metadata-only lockfile diffs are discarded.** A lockfile change whose diff is only registry metadata (integrity re-hashes, resolved-URL churn, a tool's own version stamp) with no version change is not committed — regenerate it from the manifest and keep the tree still; a reviewer reading a lockfile diff should see only versions moving. + +**Bot PRs and the self-merge rule.** A dependency-bot PR is triaged by `pr-review.md`'s rubric like any other; on a solo project the operator may self-merge one **only** after CI is green *and* the lockfile diff has been read (which the counter-line below makes possible). A bot PR bundling a **behaviour delta into a security bump** is accommodated narrowly — the smallest code change that keeps the fix, with the delta named in the PR body — never by widening the window to dodge it (the first invariant above). + +### Keep the lockfile diff visible + +The git host classifies recognised lockfiles as *generated* — linguist's `lib/linguist/generated.rb` lists the predicates by file name (`cargo_lock?`, `npm_shrinkwrap_or_package_lock?`, `pnpm_lock?`, `poetry_lock?`, `uv_lock?`, `composer_lock?`, `go_lock?`, `mise_lock?` among them; `https://github.com/github-linguist/linguist/blob/main/lib/linguist/generated.rb`, read 2026-09-06) — and a generated file is *"excluded from stats, hidden in diffs"* (`docs/overrides.md`, same repository, same date). That collapses exactly the diff the settle-window review depends on. The counter-line is one `.gitattributes` entry per audited lockfile the host would collapse: + +``` + linguist-generated=false +``` + +`rust-lang/rust` carries `Cargo.lock linguist-generated=false` in its own `.gitattributes` for this reason (read 2026-09-06). The line is harmless where the file name is not on linguist's list; check the list rather than guess. **Pre-check before adding `* text=auto eol=lf` in the same file**: zero CRLF files in the tree, no prior `.gitattributes`, `core.autocrlf` and `core.eol` unset — renormalizing a tree that already holds CRLF content rewrites history-visible bytes; record the pre-check's result and date in the file's comment (the scaffold starter carries the shape). The lock-file entry in `pr-review.md` § Pre-filters presumes a human can still read the diff; this is what keeps that true. + Dependency-update-bot PRs are triaged by `pr-review.md`'s four-class rubric; this section states the adoption policy those PRs are gated by. +## .gitignore anchoring + +A `.gitignore` entry is **anchored by default** — `/build/`, `/.env`, `/target/` — so it matches one path at the repository root and nothing else. An unanchored `build/` also ignores `src/lib/build/` and `docs/build/`, and the silent miss shows up months later as a file that never landed. The rules: + +- **Any-depth entries are deliberate and marked.** An entry meant to match at every depth (`**/node_modules/`, `*.pyc`, `.DS_Store`) carries a one-line comment saying so; an unmarked unanchored entry is a defect. +- **Per-language sections are scoped to the language's directory home** once one is declared — a Python section under `/services/api/` writes `/services/api/__pycache__/`, not `__pycache__/`. Before a home is declared, the section says which. +- **A commit that changes `.gitignore` states its pin assertions in the body**: which path each new entry is meant to match, and one path it must *not* match. `git check-ignore -v ` is the test; the assertion is what makes a later reader able to re-run it. +- **Harness transients are ignored by anchored path** — the agent's scratch and memory-local trees (`/.claude/agent-memory-local/`, the session scratchpad if it is ever placed in-tree), never by a bare name that would also hide a real directory. +- **No shipped reviewer restates this.** The `cascade-rule-reviewer` names the section in scope; the rule lives here once. + ## Methodology — choice space Blueprint picks a methodology from the register based on team shape, appetite, and quality bar. Common choices: @@ -253,6 +283,15 @@ Blueprint picks a methodology from the register based on team shape, appetite, a Whichever methodology blueprint picks, this section in the project's filled-in copy of `cbk-conventions.md` should record: cycles on/off, pull-flow style, WIP discipline, appetite-tagging convention. Without this record, the methodology selection from blueprint is hard to operate against. +## Licensing + +The repository records a licence choice, and **"none yet — all rights reserved" is a valid, recorded choice** (the `LICENSE` file absent on purpose, the README § License saying so). Scaffold confirms the licence with the operator the way it confirms visibility — one question, at repository creation, with the solo default (MIT) offered and never a copyleft licence without explicit opt-in — and seeds the `LICENSE` file and the README section from the answer. Two constraints the choice carries: + +- **Relicensing needs every contributor's consent from the second contributor on.** A project that stays "none yet" through its first outside contribution has made a decision by default; decide before that PR merges. +- **Third-party asset licences bind independently** of the repository's — a font, an icon set, a dataset or a model weight ships under its own terms, recorded beside the asset (a `LICENSE-` or a `NOTICE` entry), and a licence the asset forbids for the repository's use is a blocker, not a footnote. + +The filled-in copy of this section records the SPDX identifier (or "none yet"), the date, and the asset licences the tree carries. + ## Verify-against-reality before a one-way door (optional practice) The portable framing skill trusts documentation. A project can add a heavier discipline if its stack is fast-moving or its data assumptions are load-bearing: **before committing a frame (or any one-way-door decision), verify the load-bearing assumptions against reality** rather than the docs. Two shapes, adopt if useful: @@ -325,11 +364,36 @@ The checklist runs auto-checkable; surfacing only failures. Per [GitHub Spec Kit Every phase-exit checklist the kit ships (scaffold, blueprint, framing) carries one standing item: **if this run exercised a call that a reference file flags as individually unexercised, restamp it in the same commit** — drop the flag, date the run generically ("a second real run, "), and update the file's § Exercise status. A flag with a re-check trigger nobody fires is a rail that outlives its evidence. +## ADR relation grains + +Moved from the contract's § Mutation discipline on 2026-09-07 (the section is consulted when a decision record is read or reviewed; this file loads on `docs/adr/**`). The contract keeps a one-paragraph pointer. + +**ADR supersession has more than one grain.** The `docs/adr/*` row above shows whole-ADR supersession; two finer-grained relationships sit alongside it, both preserving the parent's immutability (neither edits the parent file): + +- **Refine** — `Refines: ADR-NNNN (Dn, …)` in the child's header narrows or clause-level-clarifies a specific decision `Dn` in the parent **without invalidating it**. The parent stays **Accepted**; both parent and child are consulted for conformance. Use when implementation reveals an accepted clause was written too generally and needs a scoped reading, not a reversal. The parent gains **no back-pointer** (it is immutable) and **no status change** — discoverability comes from the child's `Refines:` field plus the child's ADR-index row. +- **Clause-scoped supersede** — `Supersedes: ADR-NNNN Dn` reverses only decision `Dn` of the parent while the parent's other clauses stand. The parent stays **Accepted** (it is not wholly superseded); the child's index row names the specific clause it replaces, and the parent's index row is annotated (`Accepted · Dn superseded by ADR-MMMM`) while the parent file stays untouched. +- **Extend** — `Extends: ADR-NNNN (Dn, …)` adds an obligation beside a parent clause that **stays satisfied as written**. The parent stays **Accepted** and is not narrowed; the child adds a check the parent alone would not raise. **The disambiguation test:** a child that *removes a permitted reading* of the parent clause is a Refine; one that *adds an obligation beside a clause that stays satisfied* is an Extend. Both are asymmetric — the parent gains no back-pointer; the child's header field and its index row carry the relation, and the status cell carries grain and parent inline (`Accepted · Extends ADR-0003 (D1)`). +- **Promote** — `Promotes: § ` records a decision lifted from a frozen pre-cascade corpus (the consultation skill's `references/frozen_corpus_ingestion.md`); the corpus is the provenance, the ADR the binding form. + +A wrong **claim** inside an accepted ADR — a citation, a figure, an attribution, a formula — is none of these grains: it goes to `docs/adr/corrections.md`, the append-only register, and the ADR stays as written. + +**Reviewers that check ADR conformance must follow the `Refines:` and `Extends:` chains.** When an ADR intersecting a diff names a refiner (or a clause-scoped superseder), load that child too and apply its scoped clauses — a parent read in isolation yields the pre-narrowing reading. An extender is the asymmetric case: the parent passes unchanged while the child can fail, so a diff clean against the parent is not clean until every extender is checked. A reviewer consults `docs/adr/corrections.md` before flagging a claim, and cites an entry rather than restating it. The kit's `adr-conformance-reviewer` agent (see `.claude/rules/pr-review.md` § Project-local agents to dispatch alongside) is where this chain-following lives. + +## Required-checks trap + +Moved from the contract's § `[skip ci]` rule on 2026-09-07 (consulted when a `.github/` file is authored or a check parks; this file loads on `.github/**`). The contract keeps the symptom and the one-line fix rule. + +**Required-checks-block-merge trap — one symptom, three causes.** Under strict branch protection or a ruleset that requires status-check contexts, a PR parks on "Expected — Waiting for status to be reported" and stays unmergeable indefinitely (a separate always-on workflow can still run, making the PR *look* green). GitHub names the family in its troubleshooting page for required checks — a required check "skipped by path filtering, branch filtering, or a commit message" never reports (`docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/troubleshooting-required-status-checks`, read 2026-09-06). The three causes, with the fix at each: + +1. **The CI-skip marker on the HEAD commit** suppresses the workflow, so its required contexts never report. For a docs-only PR bound for `main`, either drop the marker on the final commit so CI runs, or end the branch on a non-marker commit (`git commit --allow-empty -m "ci: run gates to satisfy required checks"`). The marker still earns its keep on intermediate commits that open no PR to `main`. +2. **A `paths:`-filtered workflow backing a required check** does not run on a diff that matches nothing, so its context never reports. A required-check workflow carries **no trigger filter**; any narrowing happens inside the job as a fast no-op exit — § Exclusion is not exemption, applied to trigger filters. The kit's own ADR-immutability lint is written this way for that reason. +3. **A promoted check-run's name changes.** Matching is by name: a job renamed after promotion, or a job with no explicit `name:` whose default shifts, orphans the required context under its old name (a dated field observation, 2026-09-06, from two exercised runs — not a documented platform claim; re-verify against the page above before relying on the exact wording). A job destined for promotion carries an explicit, stable `name:` set *before* promotion; after it, the name is immutable API — rename the workflow, never the job. + ## Hook authoring The shape a hook follows, stated once (the registry comment in `.claude/settings.json` and the hook headers cite this section; they do not restate it). The three hooks authored with this section carry every line; the earlier five carry `Blocked:`/`Allowed:` and `Tier:` (the line the verification block asserts) and approximate the rest — bring a hook up to the full shape when you next edit it. -- **Header.** Event and matcher on line 2; *why* in one paragraph with its dated source (a platform page or a dated real-run observation, never memory); `Blocked:` / `Allowed:` lines; a `Timing:` line when the guard reads state before the tool runs (a compound command is judged on the state at entry — create the branch and make the first commit in separate calls; return to the root as its own command before a dispatch); `Path:` naming the placeholder registration; `Tier:` on every hook. +- **Header.** Event and matcher on line 2; *why* in one paragraph with its dated source (a platform page or a dated real-run observation, never memory); `Blocked:` / `Allowed:` lines; a `Timing:` line when the guard reads state before the tool runs (a compound command is judged on the state at entry — create the branch and make the first commit in separate calls; and the payload `cwd` is the session's launch directory, which a shell `cd` does not move — a real dispatch, 2026-09-07 — so a launch-directory guard is answered by relaunching the session from the root); `Path:` naming the placeholder registration; `Tier:` on every hook. - **The stdin / exit contract.** JSON on stdin (`tool_name`, `tool_input`, and the common fields, `cwd` among them — `https://code.claude.com/docs/en/hooks-guide` § How hooks work). Exit 2 + stderr blocks (deny), exit 0 allows; an ask-gate prints `hookSpecificOutput.permissionDecision: "ask"` with a reason and exits 0 (`https://code.claude.com/docs/en/hooks`: allow / deny / ask / defer). Matching hooks in one group run in parallel — never rely on order between two hooks on the same event. `Stop` takes no matcher; `SubagentStop` matches agent types, and a finishing subagent has no hand-off — a repair-before-stop hook belongs on `Stop`. Verified 2026-09-06; re-verify after harness upgrades. - **Fail-open, with its backstop named.** On an environment defect (no `jq`, not a git checkout, an unset variable) a guard exits 0 with a stderr warning that names the surviving backstop — the CI lint, the Stop-tier hook, the verification block — never fails closed into a universal block. `set -uo pipefail`, deliberately not `set -e`; no bash-4-only builtins (`mapfile`), because a stock macOS bash is 3.2 and a "command not found" there is a fail-closed. - **Project-relative paths, in placeholder form.** The registry names `${CLAUDE_PROJECT_DIR}/.claude/hooks/.sh` — handlers run in the current directory (`https://code.claude.com/docs/en/hooks`), so a bare relative path is not found from a subdirectory, which is where a launch-directory guard must fire. The script derives its root from `CLAUDE_PROJECT_DIR` (falling back to `$PWD`) or, for a file-scoped hook, from the edited file's checkout (`git -C "$(dirname "$file")" rev-parse --show-toplevel`) — inside a worktree the project dir stays where the session started while the file lives in the worktree. @@ -447,8 +511,19 @@ absent grep -rn "initiative\.md" .claude/ README.md grep -q "^## Rough-in events" .claude/skills/framing/references/templates/frame-output-template.md grep -q "^## Pre-flight checks" .claude/skills/framing/references/templates/frame-output-template.md grep -q "^## Assumptions" .claude/skills/rough-in/references/templates/rough-in-spec-template.md -# Deferred pair (known red until #37 lands): adr-new cites `docs/ARCHITECTURE.md § Configurability summary` -# and `§ Open questions`, which the architecture template does not emit. Do not add the pin before #37. +# Resolved pair (#37): adr-new no longer names index surfaces of its own — the conventions' § ADR index sync is the +# one home for the sync targets — so the two sections the architecture template does not emit are not cited there. +absent grep -n "Configurability summar[y]\|§ Open question[s]" .claude/skills/adr-new/SKILL.md +# Decision records (P4): the Extends grain and the relation slots on every surface that reads them; the corrections +# register in both homes (the adr-starters diff below keeps them identical), named by the hook and the README; +# Multi-surface facts stated once; the frozen-corpus reference routed from consultation's SKILL.md. +grep -q '^## Multi-surface facts' .claude/rules/cbk-conventions.md || { echo "cbk-conventions.md lacks § Multi-surface facts"; exit 1; } +grep -q '^## ADR relation grains' .claude/rules/cbk-conventions-reference.md || { echo "the reference half lacks § ADR relation grains"; exit 1; } +grep -q 'ADR relation grains' .claude/rules/cbk-conventions.md || { echo "the contract does not point at § ADR relation grains"; exit 1; } +for f in .claude/rules/cbk-conventions-reference.md .claude/skills/adr-new/SKILL.md .claude/agents/adr-conformance-reviewer.md .claude/skills/scaffold/references/adr-starters/template.md; do grep -q 'Extends:' "$f" || { echo "$f does not name the Extends: grain"; exit 1; }; done +for f in .claude/hooks/protect-immutable-adrs.sh .claude/skills/scaffold/references/adr-starters/README.md; do grep -q 'corrections.md' "$f" || { echo "$f does not name docs/adr/corrections.md"; exit 1; }; done +[ -f .claude/skills/consultation/references/frozen_corpus_ingestion.md ] || { echo "consultation lacks references/frozen_corpus_ingestion.md"; exit 1; } +grep -q 'frozen_corpus_ingestion.md' .claude/skills/consultation/SKILL.md || { echo "consultation/SKILL.md does not route to frozen_corpus_ingestion.md"; exit 1; } # Bundled starters stay byte-identical to their originals (the kit's root docs/adr/ is the source of # truth) — kit tree only: a target project fills ADR-0000's header and adds ADRs, so its docs/adr @@ -544,6 +619,46 @@ for a in logging-discipline-reviewer cascade-rule-reviewer; do diff <(printf '%s # The commit-versus-local memory choice has a Surface inventory row for the bootstrap prompt to fill. { grep -q 'Reviewer agent-memory' .claude/rules/cbk-conventions.md && grep -q 'Reviewer agent-memory' .claude/skills/scaffold/references/bootstrap_checklist_template.md; } || { echo "the Reviewer agent-memory row or its bootstrap prompt is missing"; exit 1; } +# Conventions (P4): the Licensing section, .gitignore anchoring, the issue-less branch form on the contract and in the +# guard's remediation, and the lockfile counter-line rule with its citation. +grep -q '^## Licensing' .claude/rules/cbk-conventions-reference.md || { echo "the reference half lacks § Licensing"; exit 1; } +grep -q '^## Licensing' .claude/rules/cbk-conventions.md || { echo "the contract lacks the § Licensing pointer heading"; exit 1; } +grep -q '^## .gitignore anchoring' .claude/rules/cbk-conventions-reference.md || { echo "the reference half lacks § .gitignore anchoring"; exit 1; } +grep -q 'short-slug>` with' .claude/rules/cbk-conventions.md || { echo "§ Branch naming lacks the issue-less form and its PR-body statement"; exit 1; } +grep -q 'short-slug' .claude/hooks/protect-main-branch.sh || { echo "protect-main-branch.sh's remediation does not name both branch forms"; exit 1; } +grep -q 'linguist-generated=false' .claude/rules/cbk-conventions-reference.md || { echo "§ Dependency settle-window lacks the lockfile counter-line"; exit 1; } + +# Starters and the lint (P4, Tasks 1–2): the .github starter bodies exist and scaffold's profile cites them; the PR +# template carries both gate blocks; the ADR lint has no trigger filter and a pinned job name; manual_steps no longer +# scopes its list by detection state. The cascade-depth:rough label is created by scaffold on purpose (a recorded deviation). +[ -f .claude/skills/scaffold/references/github-starter-templates.md ] || { echo "scaffold lacks references/github-starter-templates.md"; exit 1; } +grep -q 'github-starter-templates.md' .claude/skills/scaffold/references/github_only_profile.md || { echo "github_only_profile.md does not cite the starter bodies"; exit 1; } +for h in '## Review gate' '## Triage'; do grep -q "^$h" .claude/skills/scaffold/references/github-starter-templates.md || { echo "the starter PR template lacks $h"; exit 1; }; done +absent grep -nE "^\s*paths(-ignore)?:" .github/workflows/adr-immutability-check.yml +grep -q 'name: ADR immutability' .github/workflows/adr-immutability-check.yml || { echo "the ADR lint's job has no pinned name"; exit 1; } +absent grep -n "regardless of detection stat[e]" .claude/skills/scaffold/references/manual_steps.md +# The roadmap and the executor (P4, Tasks 3 and 5): the template exists, the conventions carve the surface out, and the +# executor names the flip, the post-merge checklist, the backward sweep and the measurement issue's verdict rule. +[ -f .claude/skills/blueprint/references/templates/roadmap.md ] || { echo "blueprint lacks references/templates/roadmap.md"; exit 1; } +grep -q 'ROADMAP.md' .claude/rules/cbk-conventions.md || { echo "cbk-conventions.md does not carve out docs/cbk/ROADMAP.md"; exit 1; } +for w in 'ROADMAP.md' 'post-merge checklist' 'backward sweep' 'verdict rule'; do grep -qi "$w" .claude/commands/finish.md || { echo "commands/finish.md does not name: $w"; exit 1; }; done +grep -qi 'backward sweep' .claude/commands/finish-procedure.md || { echo "finish-procedure.md lacks the backward sweep"; exit 1; } +grep -q 'verdict rule' .claude/skills/rough-in/references/templates/rough-in-spec-template.md || { echo "the spec template lacks the measurement variant's verdict rule"; exit 1; } +# Review automation (P4, Task 4): both workflow templates exist with their stated constraints; scaffold's review +# question names its consequence; rough-in's gh shape carries the create-then-edit pass. +for t in claude-review.yml claude.yml; do [ -f .claude/skills/blueprint/references/templates/$t ] || { echo "blueprint lacks templates/$t"; exit 1; }; grep -q 'timeout-minutes' .claude/skills/blueprint/references/templates/$t || { echo "templates/$t has no job timeout (constraint 3)"; exit 1; }; done +for w in 'cannot review the PR that introduces it' -- '--disallowedTools Agent' 'continue-on-error: true' 'Assert the review posted'; do [ "$w" = -- ] && continue; grep -qF -- "$w" .claude/skills/blueprint/references/templates/claude-review.yml || { echo "templates/claude-review.yml lacks: $w"; exit 1; }; done +grep -q 'solo-merge with automated review' .claude/skills/scaffold/SKILL.md || { echo "scaffold's PR question does not name its consequence"; exit 1; } +grep -q 'create-then-edit' .claude/skills/rough-in/references/planning-backend-commit.md || { echo "rough-in's gh shape lacks the create-then-edit pass"; exit 1; } +# Phases (P4): the cascade-events index template exists and scaffold cites it; nothing in rough-in misnames the index; +# blueprint's template carries its append-only Amendments section; the tooling rule names the built-in LSP tool. +[ -f .claude/skills/scaffold/references/templates/cascade-events-index-template.md ] || { echo "scaffold lacks references/templates/cascade-events-index-template.md"; exit 1; } +grep -q 'cascade-events-index-template.md' .claude/skills/scaffold/SKILL.md || { echo "scaffold/SKILL.md does not cite the cascade-events index template"; exit 1; } +# The blueprint's append-only sections are named on both surfaces: the template emits them, the mutation row carves them out. +for sec in Amendments 'Retired justifications'; do grep -q "^## $sec" .claude/skills/blueprint/references/blueprint-output-template.md || { echo "blueprint-output-template.md does not emit ## $sec"; exit 1; }; grep -q "\`## $sec\`" .claude/rules/cbk-conventions.md || { echo "the blueprint mutation row does not carve out ## $sec"; exit 1; }; done +grep -q '`LSP` tool' .claude/rules/tooling.md || { echo "tooling.md § Code intelligence does not name the built-in LSP tool"; exit 1; } +absent grep -rn "framing\.md inde[x]\|framing\.md even[t]" .claude/skills/rough-in/ + # Context budget: every `.claude/rules/*.md` WITHOUT `paths:` frontmatter loads at launch, # every session, and every non-fork subagent loads the set again. Print the always-loaded # set and its size so the standing cost is a number, not a discovery. diff --git a/.claude/rules/cbk-conventions.md b/.claude/rules/cbk-conventions.md index d5595e8..66c95e9 100644 --- a/.claude/rules/cbk-conventions.md +++ b/.claude/rules/cbk-conventions.md @@ -18,9 +18,9 @@ A single glanceable manifest of where every surface for this project actually li - **Code + cascade artifacts (the constant):** `` - **Planning backend (``):** `` - **Knowledge backend (``):** `` -- **Upstream / pre-cascade docs:** `` +- **Upstream / pre-cascade docs:** `` — a designated corpus follows the consultation skill's `references/frozen_corpus_ingestion.md` (read in full, never edited; its `-errata.md` companion amends) - **Problem brief / scaffold output:** `docs/cbk/problem_brief.md` · `docs/cbk/scaffold.md` -- **Tooling conventions:** `` +- **Tooling conventions:** `` - **Reviewer agent-memory (``):** `<".claude/agent-memory/ — committed; the kit's .gitignore line deleted" | ".claude/agent-memory-local/ — never committed">` — settled at scaffold's rule-file disposition pass (`pr-review.md` § Reviewer precedent memory) ## Rule loading and the instruction budget @@ -70,6 +70,8 @@ Example shapes: `/finish` already creates branches in this shape; this convention codifies what was already happening. +**The issue-less branch.** Operator-directed maintenance that no cascade issue tracks — a dependency bump the bot did not open, a docs sweep, a hook fix — takes the form `/` with no issue segment, and its PR body carries the statement **"operator-directed maintenance; no cascade issue"** as its first line. The scope fence is § Contribution intake: anything that adds behaviour, fixes a reported bug, or touches a workstream's code is a cascade issue (`/intake`, `/enrich`, or framing), never an issue-less branch. Such a PR still carries the `## Review gate` block; what stands in for the floor is written as *not run* with the reason ("not run — docs-only sweep, no code changed"), never left blank and never described as run. + Create the branch and make the first commit in **separate tool calls**: the default-branch guard judges a compound command on the branch at entry, so `git switch -c … && git commit …` is blocked even though the commit would have been legal by the time it ran. ### Linear `{type}` placeholder — set the type label at issue creation (Linear only) @@ -84,6 +86,10 @@ Create the branch and make the first commit in **separate tool calls**: the defa The rough-in / `/intake` / `/enrich` flows set this at issue-creation time — see the rough-in skill's planning-backend matrix. +## Licensing + +→ *Moved to* `cbk-conventions-reference.md` § Licensing *(path-scoped; see § Rule loading and the instruction budget).* + ## Closes-keyword conventions PR body close markers depend on which planning backend the project picked at scaffold: @@ -123,13 +129,13 @@ How to avoid: - When `/finish` (or any branch-prep flow) ends with marker-carrying docs commits, **end the branch on a non-marker commit** before flipping to ready. An empty commit (`git commit --allow-empty -m "ci: trigger auto-review workflow"`) is the cleanest fix when no other change is queued. - Order commits so the last one is a code/test commit (which can't carry the marker per the rules above) — when feasible, it removes the foot-gun automatically. -**Deliverable trap — an unattended CI agent's deliverable is the posted artifact, not its exit code.** An auto-review workflow that runs green but posts no comment has failed silently: the run's success condition is its side-effect (the posted review), so the workflow should assert the artifact actually landed — and a green-run-no-comment symptom is investigated as a failure, not shrugged off. +**Deliverable trap — an unattended CI agent's deliverable is the posted artifact, not its exit code, in both directions.** An auto-review workflow that runs green but posts no comment has failed silently: the run's success condition is its side-effect (the posted review), so the workflow should assert the artifact actually landed — and a green-run-no-comment symptom is investigated as a failure, not shrugged off. The converse holds too: a run that ends red *after* posting its complete review has succeeded — an exercised run did exactly that. The artifact decides; the exit code is evidence, not the verdict. **Exclusion is not exemption — linter-excluded surfaces get their own CI gate.** When a surface is deliberately excluded from the generic linter (a dialect the linter can't parse, generated-but-checked files, a DSL), it still gets a dedicated CI check of its own; otherwise the exclusion quietly becomes a standing exemption from all verification. **Substring trap — quoting the literal marker token in a commit-message body re-triggers the matcher.** GitHub's match is a substring scan across the entire message, not anchored to the subject line or the end. A commit whose body explains *why* it's a fix for this trap, but quotes the literal token while explaining, is itself skipped. Use a paraphrase (e.g., "the CI-skip marker", "the conventional skip-tag") in prose; reserve the literal `[skip ci]` for the actual flag at the end of the subject line where you intend it to fire. -**Required-checks-block-merge trap — a skip-marked HEAD commit can't merge under strict branch protection.** When `main` requires status-check contexts with "require branches to be up to date" (strict), the CI-skip marker suppresses the CI workflow entirely, so those required contexts **never report** — the platform parks them as "Expected — Waiting for status to be reported" and the merge stays blocked indefinitely. (A separate always-on workflow can still run, making the PR *look* green while it stays unmergeable.) Net: `[skip ci]` saves nothing for a PR that has to merge through branch protection. For a docs-only PR bound for `main`, either (a) skip the marker on the final commit so CI runs and reports, or (b) keep the marker on the content commits but end the branch on a non-marker commit (`git commit --allow-empty -m "ci: run gates to satisfy required checks"`) before requesting merge. Same root cause and fix as the auto-review trap; the marker still earns its keep on intermediate WIP commits that don't open a PR to `main`. +**Required-checks-block-merge trap.** Under a ruleset or branch protection that requires status-check contexts, a PR parks on "Expected — Waiting for status to be reported" and stays unmergeable while an always-on workflow still runs green beside it. One symptom, three causes — the CI-skip marker on the HEAD commit, a `paths:`-filtered workflow backing a required check, a promoted check-run whose name changed — with their fixes and sources in `cbk-conventions-reference.md` § Required-checks trap. The rule in one line: a required-check workflow carries no trigger filter and an explicit, stable job `name:`, and a branch bound for the base ends on a non-marker commit. ## Dependency settle-window — supply-chain discipline @@ -143,6 +149,17 @@ How to avoid: → *Moved to* `cbk-conventions-reference.md` § Verify-against-reality before a one-way door (optional practice) *(path-scoped; see § Rule loading and the instruction budget).* +## Multi-surface facts + +Some facts are stated in more than one place by design — an ADR and its index rows, a rule and the reviewer that enforces it, a template and the executor that parses it, a repo artifact and its knowledge-backend companion. Stated once, the discipline is: + +- **Every restatement records where the fact's other statements live**, and an edit sweeps every recorded location in one commit — never "update the index later". +- **Back-pointers are asymmetric.** The immutable source never points forward — an ADR gains no link to its refiners, its extenders, its corrections or its companion page; the companion points back. Discoverability is the companion's job plus the index row. +- **A re-check trigger is reachable from the line that fires it** — a dated rail names what re-verifies it *at the rail*, not in a separate list. +- **Closed sets are machine-maintained where the tree can** — the verification block diffs the copies it can reach (the bundled executor templates, the reviewers' `## Writing memory`, the adr-starters, the price table); a set the tree cannot diff carries its other locations in prose, at each copy. + +**The companion vocabulary** — an *immutable source* plus an *append-only companion* — has three instances: `docs/adr/NNNN-*.md` + `docs/adr/corrections.md` (claims that proved wrong); a frozen pre-cascade corpus + its `-errata.md` (consultation's `references/frozen_corpus_ingestion.md`); a repo artifact + its knowledge-backend companion page (`knowledge-backend.md` § When to write). The four rules apply to each. § ADR index sync below is the first instance of this discipline, and a drift-guard test whose subject is the consistency itself is the fourth test shape (`testing.md` § Quick reference). + ## ADR index sync Every ADR addition (and every supersession) updates **multiple indexes** in lockstep: @@ -152,11 +169,11 @@ Every ADR addition (and every supersession) updates **multiple indexes** in lock 3. **`docs/ARCHITECTURE.md` § Configurability summary** (if the project uses a configurability-first principle) 4. **`docs/cbk/blueprint.md` § Stack decisions** — also updated for post-blueprint ADRs (since blueprint.md is itself a cascade artifact) -The `adr-new` skill (at `.claude/skills/adr-new/SKILL.md`) automates the cross-index sync. Manual ADR creation works but is error-prone (multiple indexes to keep in sync); use the skill. +This list is the one home for the sync targets: `adr-new` reads it and states no count of its own; on drift the README index row is canonical and the other surfaces are corrected to it. The blueprint's § Stack decisions table is append-only and gains a one-line bullet only when the ADR changes a *stack* decision — it is a cascade artifact, not an index. The `adr-new` skill (at `.claude/skills/adr-new/SKILL.md`) automates the cross-index sync. Manual ADR creation works but is error-prone (multiple indexes to keep in sync); use the skill. ADR immutability should be enforced two ways: - **A PreToolUse hook** at `.claude/hooks/protect-immutable-adrs.sh` blocks Claude Code edits to existing ADR files -- **A CI lint** at `.github/workflows/adr-immutability-check.yml` diffs `docs/adr/[0-9]{4}-*.md` files in PRs and fails on changes to existing ADRs (closes the raw-git-access gap that the hook can't catch) +- **A CI lint** at `.github/workflows/adr-immutability-check.yml` diffs `docs/adr/[0-9]{4}-*.md` files in PRs and fails on changes to existing ADRs (closes the raw-git-access gap that the hook can't catch). It carries no `paths:` filter and a pinned job name, so it can be a required check — `cbk-conventions-reference.md` § Required-checks trap, causes 2 and 3 Both belong in any project that takes ADRs seriously; the kit's `adr-new` skill assumes both exist. @@ -169,25 +186,23 @@ Both belong in any project that takes ADRs seriously; the kit's `adr-new` skill | Artifact | Mutation rule | Supersession pattern | Rationale | |---|---|---|---| | `docs/adr/[0-9]{4}-*.md` | **Immutable** | New ADR with `Supersedes: ADR-NNNN` field; old ADR's status changes to "Superseded by ADR-MMMM" | ADR-0000 immutability discipline + hook enforcement + CI lint | -| `docs/cbk/blueprint.md` | **Append-only for new ADRs** (the Stack decisions table); otherwise immutable to preserve cascade history | Re-blueprint creates new file | Blueprint is a cascade event; mutation breaks the audit trail | +| `docs/adr/corrections.md` | **Append-only**; dated entries never edited — a correction to a correction is a new entry; evidence annotated under the entry when its meaning goes stale | n/a | The claim companion to the immutable ADRs (§ Multi-surface facts); `protect-immutable-adrs.sh` leaves it editable by design | +| A designated frozen pre-cascade corpus + `-errata.md` | Corpus **frozen** — read in full, never edited by any phase; the errata companion **append-only** and dated ("amends; never edits") | n/a — a superseded reading is a new errata entry | The reference material the cascade inherits verbatim (consultation's `references/frozen_corpus_ingestion.md`); scaffold registers the hook, CI job, `.gitattributes` and editor entries that keep it byte-stable | +| `docs/cbk/blueprint.md` | **Append-only in three sections** — the Stack decisions table (new ADRs), `## Amendments`, `## Retired justifications`; otherwise immutable to preserve cascade history | Re-blueprint creates new file | Blueprint is a cascade event; mutation breaks the audit trail | | `docs/cbk/frame-NN.md` | **Append-only for `## Rough-in events` table**; otherwise immutable post-commit | Re-framing creates `frame-MM.md` with `Supersedes: frame-NN` field; old frame's status → "Superseded" | Frames are cascade events; rough-in events are the timeline log | | `docs/cbk/frame-MM.md` (additive increment) | **New file** (next sequential number); the prior frame is not mutated and stays `Active` | *No* supersession — an additive increment carries a `Builds on: frame-NN` header (not `Supersedes`); both frames stay `Active` and their open milestones coexist | Not every new framing replaces: an increment extends a workstream whose prior milestones are still valid and open, so the prior frame must not flip to `Superseded` (see the framing skill's `references/procedure.md` § Step 2 pattern D) | | `docs/cbk/frame-MM.md` (milestone-scoped re-frame) | **New file** (next sequential number); the prior frame is not mutated | Header states `Supersedes only milestone M of frame-NN`; the prior frame's index status is annotated `Active (M superseded by frame-MM)` via the permitted status-column mutation; the retired milestone's acceptance-criteria set is recorded as retired-un-executed in the new frame | One milestone's shape can fail while its siblings are built and Done; whole-frame supersession would falsify the siblings' history (see the framing skill's `references/procedure.md` § Step 2 pattern E) | -| `docs/cbk/README.md` | **Append-only for new entries**; status column updates allowed | Status updates are mutations to single column, not whole-file rewrites | Status changes (Active → Superseded → Completed) need to flow | +| `docs/cbk/ROADMAP.md` (github-issues and in-repo-markdown axes) | **Freely mutable** — a status surface, not a cascade event | n/a — a wrong row is fixed in place; a superseded row reads *retired* | Where we are and what is next; blueprint writes it, framing appends rows in the frame's commit, rough-in flips to *roughed-in*, `/finish` flips to *done* on the PR's own branch or the post-merge checklist does (`commands/finish.md` item 8). The audit trail is the index and git history, never this file | +| `docs/cbk/README.md` | **Append-only for new entries**; status column updates allowed. Scaffold creates it (from its index template); blueprint, framing and rough-in append a row and a phase note each | Status updates are mutations to single column, not whole-file rewrites | Status changes (Active → Superseded → Completed) need to flow | | `docs/STANDARDS.md`, `docs/ARCHITECTURE.md`, `CLAUDE.md` | **Freely mutable** | n/a — living docs | Project-context docs evolve with the project; git history is the version archive | | `.claude/rules/*.md` | **Freely mutable** | n/a | Operational rules; mutations are routine | | `.claude/skills/*` | **Freely mutable** within the local copy | n/a | Tooling content; mutations refine the cascade | | Code | **Freely mutable** | n/a | Standard code evolution | -Cascade events being append-only is structurally important: the cascade IS the audit trail of decisions. A new framing supersedes an old one with a new file; the old one stays in `docs/cbk/` for future readers to understand "we used to think X, now we think Y." +Cascade events being append-only is structurally important: the cascade IS the audit trail of decisions. **Two status surfaces are carved out of the executor's cascade-artifact ban** — the roadmap row and a frame's `## Rough-in events` table — because they record state, not decisions; the row above and `commands/finish.md` item 8 say who flips them and when. A new framing supersedes an old one with a new file; the old one stays in `docs/cbk/` for future readers to understand "we used to think X, now we think Y." **The table and the hook registry are two views of one list.** A row enforced by a hook names it: ADRs → `protect-immutable-adrs.sh` (plus the CI lint); lock files → `protect-lock-files.sh`. A hook that enforces a rule clause rather than a table row names the clause: `protect-main-branch.sh` → § Branch naming; `require-repo-root-for-agents.sh` and `detect-forked-agent-memory.sh` → `pr-review.md` § Reviewer precedent memory (one home for the memory tree); `guard-pr-state.sh` → the PR-state one-way door in `cbk-conventions-reference.md` § HITL gate load-bearing heuristics; `require-knowledge-backend-ok.sh` → `knowledge-backend.md` § HITL announcement discipline. Rows with no hook (the append-only cascade artifacts, the index's status column) are instruction-enforced and carry a deferred-hardening note per § HITL gate load-bearing heuristics. The registry in `.claude/settings.json` lists the same hooks under their tiers, and the verification block checks the registry against the files. The authoring shape lives in `cbk-conventions-reference.md` § Hook authoring. -**ADR supersession has more than one grain.** The `docs/adr/*` row above shows whole-ADR supersession; two finer-grained relationships sit alongside it, both preserving the parent's immutability (neither edits the parent file): - -- **Refine** — `Refines: ADR-NNNN (Dn, …)` in the child's header narrows or clause-level-clarifies a specific decision `Dn` in the parent **without invalidating it**. The parent stays **Accepted**; both parent and child are consulted for conformance. Use when implementation reveals an accepted clause was written too generally and needs a scoped reading, not a reversal. The parent gains **no back-pointer** (it is immutable) and **no status change** — discoverability comes from the child's `Refines:` field plus the child's ADR-index row. -- **Clause-scoped supersede** — `Supersedes: ADR-NNNN Dn` reverses only decision `Dn` of the parent while the parent's other clauses stand. The parent stays **Accepted** (it is not wholly superseded); the child's index row names the specific clause it replaces. - -**Reviewers that check ADR conformance must follow the `Refines:` chain.** When an ADR intersecting a diff names a refiner (or a clause-scoped superseder), load that child too and apply its scoped clauses — a parent read in isolation yields the pre-narrowing reading. The kit's `adr-conformance-reviewer` agent (see `.claude/rules/pr-review.md` § Project-local agents to dispatch alongside) is where this chain-following lives. +**ADR supersession has more than one grain** — Refine, clause-scoped supersede, Extend and Promote, with the claim register for what is not a decision at all. The definitions, the disambiguation test (a child that removes a permitted reading is a Refine; one that adds an obligation beside a clause that stays satisfied is an Extend) and the rule that reviewers follow the `Refines:` and `Extends:` chains and consult `docs/adr/corrections.md` before flagging a claim live in `cbk-conventions-reference.md` § ADR relation grains, which loads whenever a decision record is read. ## HITL gate load-bearing heuristics @@ -210,7 +225,8 @@ Cascade events being append-only is structurally important: the cascade IS the a | What you're doing | Where the convention lives | |---|---| | Naming a cascade event | Flat `docs/cbk/.md`, sequential numbering | -| Updating the cascade-events index | `docs/cbk/README.md` (status column) | +| Updating the cascade-events index | `docs/cbk/README.md` — see § Mutation discipline (its row names the creator and the appenders) | +| Flipping the roadmap row | `docs/cbk/ROADMAP.md` — see § Mutation discipline (its row names who flips it and when) | | Naming a planning-backend issue | `[:F<#>:R<#>] ` | | Naming a branch | `/--` | | Closing an issue from a PR | `Closes -N` (Linear) or `Closes #N` (GitHub) in PR body | diff --git a/.claude/rules/knowledge-backend.md b/.claude/rules/knowledge-backend.md index 80df511..2d2ecb4 100644 --- a/.claude/rules/knowledge-backend.md +++ b/.claude/rules/knowledge-backend.md @@ -141,7 +141,7 @@ Tiered by phase, default by HITL: Every write requires explicit HITL approval. No cascade phase writes to the knowledge backend as a side effect of its primary work. -When writing, write **companion** material that *adds to* the repo artifact, not a copy of it: +When writing, write **companion** material that *adds to* the repo artifact, not a copy of it — the companion vocabulary of `cbk-conventions.md` § Multi-surface facts: the repo artifact is the immutable source, the page its append-only companion, pointing back and never pointed at: - Repo holds the structured cascade artifact (immediate context) - Notion holds longer-form supporting material, decision threads, cross-project links, durable runbooks diff --git a/.claude/rules/pr-review-reference.md b/.claude/rules/pr-review-reference.md index 55adcd3..224109d 100644 --- a/.claude/rules/pr-review-reference.md +++ b/.claude/rules/pr-review-reference.md @@ -86,7 +86,7 @@ If the human reviewed the draft PR, decided to leave a Surface item alone, and t ### ❌ Running review-toolkit on a docs-only PR -If the diff is entirely under `docs/` or matches `*.md`, the toolkit's specialized agents have nothing to chew on. Skip the sweep; the simplify pass is sufficient. +If the diff is entirely under `docs/` or matches `*.md`, the toolkit's specialized agents have nothing to chew on. Skip the sweep; the simplify pass is sufficient. The exception — docs that are one-way doors — is stated once, in `pr-review.md` § What NOT to flag, and not restated here. ### ❌ Padding the hand-off summary with "looks good" prose diff --git a/.claude/rules/pr-review.md b/.claude/rules/pr-review.md index 94cf7b7..0bae9b4 100644 --- a/.claude/rules/pr-review.md +++ b/.claude/rules/pr-review.md @@ -36,7 +36,7 @@ A waived skill is recorded on its line with the break-glass reason, never omitte ### The orchestrated sweep supplements; it never substitutes -Where a multi-agent orchestration surface is available, `.claude/workflows/review-sweep.js` runs **beside** `pr-review-toolkit:review-pr`, after `/simplify`, and carries any focused or specific review the two skills do not cover, or do not cover enough, for the diff at hand — the project-local reviewers below always ride in it (the workflow reads § Project-local agents at dispatch time, never a copy kept in the script), and beyond them the caller names the finders this diff needs (a dimension the toolkit lacks; a targeted concern such as a schema change, a timing invariant, a boundary contract) — every finding through a refute-by-default **adversarial verification stage** before triage. Triage waits for the skill and the workflow both. The workflow widens coverage and pre-filters false positives. It is **never** an alternative to invoking the two skills, and a workflow that ran does not discharge either of them. +Where a multi-agent orchestration surface is available, `.claude/workflows/review-sweep.js` runs **beside** `pr-review-toolkit:review-pr`, after `/simplify`, and carries any focused or specific review the two skills do not cover, or do not cover enough, for the diff at hand — the project-local reviewers below always ride in it (the workflow reads § Project-local agents at dispatch time, never a copy kept in the script — `cbk-conventions.md` § Multi-surface facts), and beyond them the caller names the finders this diff needs (a dimension the toolkit lacks; a targeted concern such as a schema change, a timing invariant, a boundary contract) — every finding through a refute-by-default **adversarial verification stage** before triage. Triage waits for the skill and the workflow both. The workflow widens coverage and pre-filters false positives. It is **never** an alternative to invoking the two skills, and a workflow that ran does not discharge either of them. If the sweep is unavailable, its roster read fails, or its run fails, that is not a fallback event — the floor was always the requirement, and the sweep's absence costs only the extra coverage. Record that it was skipped, or what it dropped, on its gate line. @@ -80,7 +80,7 @@ If your project authors additional reviewer agents (e.g., for a dependency-injec Before any review work runs, exclude these from the diff. Cheaper than triaging them out post-hoc, and the agent's signal-to-noise improves as the input narrows. - **Generated files** — `*.generated.*`, codegen output directories, protobuf-emitted types, OpenAPI client stubs -- **Lock files** — `package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`, `Cargo.lock`, `mix.lock`, `uv.lock`, `Gemfile.lock`, `poetry.lock` +- **Lock files** — `package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`, `Cargo.lock`, `mix.lock`, `uv.lock`, `Gemfile.lock`, `poetry.lock` (excluded from the *agents'* read; the exclusion presumes a human can still see the diff, which `cbk-conventions-reference.md` § Dependency settle-window › Keep the lockfile diff visible keeps true against the host's collapse) - **Vendored dependencies** — `vendor/`, `third_party/`, `node_modules/` (shouldn't be tracked, but defensive) - **Build artifacts** — `dist/`, `build/`, `target/`, `_build/`, `.next/`, etc. - **Snapshot test fixtures** larger than ~200 lines unless the test itself is on the diff @@ -129,7 +129,7 @@ The highest-leverage tuning surface for AI code review (per Cloudflare's evidenc **Project may exclude additionally** (configure as the `pr-review-toolkit` configuration permits): - **Bot-author or dependabot PRs** — reviewing automated dependency bumps line-by-line is rarely worth the tokens. -- **Docs-only PRs** — if the diff is entirely under `docs/` or `*.md`, skip the heavy review sweep; the simplify pass is enough. +- **Docs-only PRs** — if the diff is entirely under `docs/` or `*.md`, skip the heavy review sweep; the simplify pass is enough. **Not automatically light where the docs are one-way doors** — a cascade artifact, an ADR, the conventions, a rule file: those are reviewed by a human and by the project-local reviewers, and the floor still runs. - **Project-specific noise patterns** that emerge from the first month of running the toolkit. Add them here as you find them. ## Path-conditional aggressiveness diff --git a/.claude/rules/testing.md b/.claude/rules/testing.md index 38fc45a..68d0bf0 100644 --- a/.claude/rules/testing.md +++ b/.claude/rules/testing.md @@ -236,6 +236,7 @@ A snapshot pins one specific output. The contract is usually a property ("the as | End-to-end integration | Tests-as-shape-of-done | Wire pipeline with mocked boundaries → assert telemetry | Mocked external services, assert turn/transaction telemetry | | Hardware-on-real-device rehearsal | Tests-as-shape-of-done | Tagged for target-only execution | Mock-backed in CI; real-hardware-backed manually | | High-frequency loop tick | Telemetry assertions only | n/a | Assert telemetry event shape; never per-tick state | +| A fact stated on more than one surface (an index and its source, a template and its parser, a bundled copy) | **Drift guard** — a fourth shape: the test's subject is the consistency itself | Written when the second copy lands | Diff the copies or parse both and compare; `cbk-conventions.md` § Multi-surface facts names the sets the kit already guards | ## Where the regimes meet diff --git a/.claude/rules/tooling.md b/.claude/rules/tooling.md index 02aa77b..019f730 100644 --- a/.claude/rules/tooling.md +++ b/.claude/rules/tooling.md @@ -15,9 +15,19 @@ | Find a literal string or regex | `Grep` (built-in) | Fast, ripgrep-backed, no server startup | | Find files by path / glob | `Glob` (built-in) | Same — pattern matching, no semantics | | Read a known file | `Read` (built-in) | Direct; line-numbered output | -| Find every reference to a symbol / rename cleanly / get signatures | `` | LSP-grade operations — handles renames, imports, scope; text search misses cross-file refs through aliases | +| Find every reference to a symbol / rename cleanly / get signatures | The built-in `LSP` tool — first-line | Enabled by the official per-language plugin plus the language server on `PATH` (`https://code.claude.com/docs/en/discover-plugins`, read 2026-09-06; re-verify after harness upgrades); handles renames, imports, scope. Cloud-session caveat: the server must be on the session's `PATH`, which a cloud sandbox may not provide | +| The same, for a language with no plugin | `` | Second-line only; wired by the phase that needs it | +| The same, when neither is available | `Grep` + manual cross-file work — last | Text search misses cross-file refs through aliases; say so in the hand-off | -**Decision rule**: if the operation cares about *symbols* (refs, renames, signatures), use the code-intelligence MCP (when wired). If it cares about *text* (strings, patterns), use built-ins. [If no code-intelligence MCP is wired, note the fallback: `Grep` + manual cross-file work.] +**Decision rule**: if the operation cares about *symbols* (refs, renames, signatures), the built-in `LSP` tool first; an MCP only for a language without a plugin; `Grep` last. If it cares about *text* (strings, patterns), use built-ins. + +**Deferred integrations** — a tool the project decided not to wire yet, with the trigger that wires it and the date the decision expires: + +| Integration | Deferred because | Wire when | Re-check by | +|---|---|---|---| +| [e.g. the language server for the secondary language] | [no code in that language yet] | [the first module lands] | [date] | + +**Read versus write per data integration**: every integration that touches a data store names its boundary here — which tools are read-only and which can mutate — and the mutating ones are ask-gated (`cbk-conventions-reference.md` § Hook authoring); a query tool that can also write is wired as two entries or not at all. ## Library / framework docs @@ -38,7 +48,8 @@ |---|---|---| | Read a cascade issue | `` | The planning backend is the source of truth for cascade sub-issues | | Create / update an issue or comment | `` | Same | -| Create / read / comment on a PR | `` or `Bash` with `gh` | The git host is the source of truth for PRs, code review, repo state | +| Create / read / comment on a PR | `Bash` with `gh` (default) or `` | The git host is the source of truth for PRs, code review, repo state | +| Create or link a cascade issue on the github-issues axis | `Bash` with `gh` — five commands as one script (the phase's `references/planning-backend-commit.md` § The `gh` shape) | `gh` is the default interface; a git-host MCP is one connection among several, and the phase reads and writes the working tree by default | **Decision rule**: cascade issues live on the planning backend; PRs and code review live on the git host. `/finish` reads the planning backend for the spec and writes a PR with the close marker from `cbk-conventions.md` § Closes-keyword conventions. @@ -62,10 +73,33 @@ **Decision rule**: convert any *relative* date in a prompt ("Thursday", "last week") to an *absolute* date before saving it to memory, a cascade artifact, or any persistent surface. Relative dates rot fast. +## MCP configuration + +| You want to … | Reach for | Why | +|---|---|---| +| Wire a server the project depends on | The committed `.mcp.json`, with credentials as **environment-variable references** (`${GITHUB_TOKEN}`), plus a committed `.env.example` naming every variable | The config is reviewable and shared; the secrets are not. The kit's `.mcp.json.example` is the starting shape | +| Wire a server only you use | The user-level MCP config, never the project file | A personal server in the committed file is a dependency for everyone | +| Add a server | The cascade phase that justifies it wires it — the planning MCP at scaffold, a docs MCP at blueprint, a domain MCP when a workstream needs it — and records it in `cbk-conventions.md` § Surface inventory | A server nobody's phase asked for is noise in every session's tool list | + +**Decision rule**: list-valued keys (`enabledPlugins`, `allow`, `deny`, hook arrays) are **never repeated** in the local settings file — the local file overrides by key, so a repeated list silently replaces the committed one instead of extending it; add to the committed list or not at all. A linter exclusion for an MCP or settings file is added only where nothing in it could be actioned (a generated file, a secrets template) — an exclusion is not an exemption (`cbk-conventions.md` § `[skip ci]` rule). + ## [Stack surfaces — add a section per wired MCP] [One section per stack surface the project wires — database client, cloud CLI, observability, domain services — each with the same table shape, a decision rule, and any **gotchas the build has actually proved out** (recorded here so they aren't re-discovered; date them per the conventions' dated-rails principle). Delete this placeholder once real sections exist.] +## Automated review on the git host + +*(Delete if scaffold's PR/review answer was self-reviewed or straight-to-main.)* + +| You want to … | Reach for | Why | +|---|---|---| +| A review on every PR flipped to ready | The auto-review workflow (`.github/workflows/claude-review.yml`, from blueprint's template) | Fires once per readiness cycle; re-trigger with `claude-review-again`; `skip-claude` opts a PR out | +| A deeper or cheaper review on one PR | The `claude-deep-review` / `claude-fast-review` labels | Label-driven proportionality — the model and effort are explicit per label, never inherited | +| Ask a question on a PR or issue | `@claude` in a comment (`.github/workflows/claude.yml`) | Interactive; least-privilege tool list | +| Answer the review's findings | `/pr-respond ` | The feedback loop; every finding a SHA and a reply | + +**Decision rule**: the posted artifact is the deliverable (`cbk-conventions.md` § Deliverable trap) — a green run with no comment is investigated, a red run whose review posted is a pass. The workflow cannot review the PR that introduces it; verify on the next one. Record here which labels the project actually created. + ## Plugins | Plugin / skill | When | Cite | diff --git a/.claude/rules/workflows.md b/.claude/rules/workflows.md index 6cdf1de..59abc93 100644 --- a/.claude/rules/workflows.md +++ b/.claude/rules/workflows.md @@ -135,7 +135,7 @@ Don't preview-and-confirm trivial things (reading a file, running a check comman ## Cost+scope-explicit *(optional section — keep if your project tracks paid resources)* -[If the project's viability depends on staying within a resource budget, record the discipline here. The observed shape: every cascade artifact touching paid resources or scope surfaces (1) the monthly cost — default free tier; if non-zero, name the tier and the amount — and (2) the required-vs-aspirational distinction (which deliverables are load-bearing vs nice-to-have). Default to the free tier; a paid resource is named explicitly for the operator's eyes-open approval. Per-milestone cost surfaces live in the framing docs; per-PR cost notes go in the PR body when a paid resource is introduced or changed.] +[If the project's viability depends on staying within a resource budget, record the discipline here. **Name money and agent-run quota separately** — a paid API tier is a monthly amount, an agent-run quota (subscription usage, a CI-minutes cap, a per-workflow agent ceiling) is a count with a reset period, and a milestone that is free in dollars can still exhaust the quota; each cascade artifact that touches either states which one it spends. The observed shape: every cascade artifact touching paid resources or scope surfaces (1) the monthly cost — default free tier; if non-zero, name the tier and the amount — and (2) the required-vs-aspirational distinction (which deliverables are load-bearing vs nice-to-have). Default to the free tier; a paid resource is named explicitly for the operator's eyes-open approval. Per-milestone cost surfaces live in the framing docs; per-PR cost notes go in the PR body when a paid resource is introduced or changed.] ## Spec-driven development (SDD) diff --git a/.claude/skills/adr-new/SKILL.md b/.claude/skills/adr-new/SKILL.md index 1507e60..19ef9ce 100644 --- a/.claude/skills/adr-new/SKILL.md +++ b/.claude/skills/adr-new/SKILL.md @@ -1,6 +1,6 @@ --- name: adr-new -description: Create a new immutable ADR in docs/adr/, allocating the next sequence number, rendering from template.md, and updating the three indexes (docs/adr/README.md, docs/ARCHITECTURE.md § Decisions log + Configurability summary, docs/cbk/blueprint.md § Stack decisions). Use when the user wants to record a new architecture decision or supersede an existing one. +description: Create a new immutable ADR in docs/adr/, allocating the next sequence number, rendering from template.md, and updating every index the project's conventions name (`.claude/rules/cbk-conventions.md` § ADR index sync). Use when the user wants to record a new architecture decision, or supersede, refine or extend an existing one. disable-model-invocation: true --- @@ -12,7 +12,7 @@ Create a new ADR. ADRs are **immutable** (ADR-0000) — superseding writes a new - The user has decided to record a new architecture decision. - The user wants to supersede an existing ADR (the new one references the old one's number). -- A previously-deferred Open Question (in `docs/ARCHITECTURE.md` § Open questions or `docs/cbk/blueprint.md` § Open questions) has been resolved. +- A previously-deferred open question (in `docs/cbk/blueprint.md` § Open Questions, or wherever the project's indexes keep their open-questions list) has been resolved. ## When NOT to use @@ -22,12 +22,15 @@ Create a new ADR. ADRs are **immutable** (ADR-0000) — superseding writes a new ## Inputs (interactive) -When invoked, ask the user (in this order, one question at a time — don't batch): +When invoked, **propose** every input below from what the operator already said and the tree (the conversation, the issue, the frame, the existing ADR index), present the filled set in one exchange, and ask only for what cannot be inferred — never one question at a time, and never a bare form. The operator corrects the proposal; the corrected set is the input. 1. **Slug** — kebab-case noun phrase, max 8 words. Used in the filename. Example: `vector-store-as-anubis-tool`. 2. **Title** — full title for the ADR header, sentence case. Example: `Vector store as an Anubis tool, not an MCP server`. -3. **Supersedes?** — if yes, the ADR number being superseded (e.g., `0019`). -4. **Configurable / Hot-swappable** — for the Configurability summary table. Format: `yes (per-Pilot) | no (config-time)`. Use `n/a` for non-component decisions. +3. **Supersedes?** — if yes, the ADR number being superseded (e.g., `0019`), or `ADR-NNNN Dn` for one clause. + **Refines?** — if yes, the parent and the clauses narrowed, in `ADR-NNNN (Dn, …)` form. + **Extends?** — if yes, the parent and the clauses an obligation is added beside, same form (§ Refines vs Supersedes has the disambiguation test). + **Promotes?** — if the decision is lifted from a frozen pre-cascade corpus, the corpus path and heading. +4. **Configurable / Hot-swappable** — for the configurability index row, where the project keeps one. Format: `yes (per-Pilot) | no (config-time)`. Use `n/a` for non-component decisions. 5. **One-line summary** — for the README index and the Decisions log table. ## Steps @@ -42,35 +45,30 @@ When invoked, ask the user (in this order, one question at a time — don't batc ```bash cp docs/adr/template.md "docs/adr/${NNNN}-${SLUG}.md" ``` - Then fill in: ADR number, title, status (`Accepted` for new decisions, `Proposed` if user wants HITL gate first), date (today, ISO format), deciders, related ADRs, supersedes (if any), context, options considered, decision, consequences. + Then fill in: ADR number, title, status (`Accepted` for new decisions, `Proposed` if user wants HITL gate first), date (today, ISO format), deciders, related ADRs, the relation slots the inputs filled (`Supersedes:` / `Refines:` / `Extends:` / `Promotes:` — delete the unused lines), context, options considered, decision, consequences. -3. **Update `docs/adr/README.md` index.** - - Add the row to the index table in number order. - - If superseding, mark the old ADR's status field in the index as `Superseded by ADR-${NNNN}` and add a backlink (don't edit the old ADR file itself; the index expresses supersession). +3. **Update `docs/adr/README.md` index** — the canonical surface. + - Add the row to the index table in number order; the status cell carries the grain and the parent inline (`Accepted · Refines ADR-0007 (D2)`, `Accepted · Extends ADR-0003 (D1)`). + - If wholly superseding, mark the old ADR's status field in the index as `Superseded by ADR-${NNNN}` (don't edit the old ADR file itself; the index expresses supersession). A clause-scoped supersession annotates the parent's row (`Accepted · Dn superseded by ADR-${NNNN}`); a refine or extend leaves the parent's row as it was. -4. **Update `docs/ARCHITECTURE.md`.** - - § Decisions log table: add the new row. - - § Configurability summary table: add the new row with the configurable/hot-swappable values from input 4. - - If the new ADR resolves an Open Question, remove the question from § Open questions. +4. **Update every other index the conventions name.** `.claude/rules/cbk-conventions.md` § ADR index sync is the one home for the target list — read it now and walk it; this skill states no count. The blueprint's § Stack decisions table is append-only and gets a one-line bullet **only when the ADR changes a stack decision**; a decision that is not about the stack does not touch it. Where a project keeps an open-questions list on an index surface, an ADR that resolves one removes it there. -5. **Update `docs/cbk/blueprint.md`.** - - § Stack decisions table: add the new row (matches the ARCHITECTURE.md and README.md indexes). - - If resolving an Open Question, remove from § Open questions there too. +5. **Verify cross-references match.** + - Every index the conventions name agrees on number, title, status and grain; on drift the README row wins and the others are corrected to it. + - Any ADR named in the new ADR's `Related:`, `Supersedes:`, `Refines:` or `Extends:` field exists; a `Promotes:` path resolves. -6. **Verify cross-references match.** - - All three indexes must agree on number, title, status, configurability. - - Any cross-ADR references in the new ADR's `Related:` field must exist. - -7. **Hand back to user.** Print the new file path and a one-line summary. Do not commit — the user runs `commit-commands:commit` separately. +6. **Hand back to user.** Print the new file path and a one-line summary. Do not commit — the user runs `commit-commands:commit` separately. ## Constraints - **Never edit an existing ADR file.** The PreToolUse hook (`.claude/hooks/protect-immutable-adrs.sh`) will block this. Supersession is index-level only. - **Date is always today** in `YYYY-MM-DD` format. Use the `time` MCP if uncertain rather than guessing. -- **Title in the file header must match the title in all three indexes** — drift here is the most common mistake. +- **Title in the file header must match the title in every index the conventions name** — drift here is the most common mistake; the README row is canonical when they disagree. - The skill produces files only; it does not commit, push, or open PRs. -## Refines vs Supersedes +## Refines vs Supersedes vs Extends + +**See also** `docs/adr/corrections.md` — a wrong *claim* in an accepted ADR (a citation, a figure, an attribution, a formula) is none of these grains; it is an append-only register entry, and the ADR stays as written. A new ADR connects to an existing one through one of two relationships. Both are recorded as header fields and both preserve the parent's immutability — neither ever edits the parent file. @@ -81,6 +79,8 @@ A new ADR connects to an existing one through one of two relationships. Both are **Header narrative.** A `Refines:` header carries more than the pointer: for each named parent clause, one or two sentences stating what is narrowed or additionally sanctioned and what stays binding, ending with an explicit "all parents stay Accepted and immutable" line. A bare `Refines: ADR-NNNN (D2)` forces every future reader to re-derive the delta; the clause-level narrative is what makes the chain readable at conformance-check speed. +**`Extends: ADR-NNNN (Dn, …)`** — the new ADR *adds an obligation beside* a parent clause that stays satisfied as written; the parent is not narrowed and stays `Accepted`. **The disambiguation test:** a child that removes a permitted reading of the parent clause is a Refine; one that adds an obligation beside a clause that stays satisfied is an Extend. The header narrative is the same as a refine's — per clause, what is added and what stays binding — and the asymmetry is the same: no back-pointer on the parent. + **Refines may target non-decision clauses.** The over-general text isn't always a `Dn` decision — a refine can scope a parent's `§ Consequences` (or another named section) when that's where the statement being narrowed lives: `Refines: ADR-NNNN (§ Consequences — )`. The same rules apply: parent untouched, both consulted. **Honest-disclosure refines.** When execution falsifies a rule an earlier ADR pre-committed to (a threshold, a protocol, an expected outcome), the deviation lands as a refining ADR whose body discloses all three parts — what was pre-committed, what reality showed, and what changes — never as a silent re-interpretation. The disclosure is the point: a pre-commitment only disciplines future decisions if deviations from it are visibly recorded. @@ -89,7 +89,7 @@ A new ADR connects to an existing one through one of two relationships. Both are - Add a **Refines?** input alongside the Supersedes? input (Inputs, above) — if yes, capture the parent number and the specific decision clauses in `ADR-NNNN (Dn, …)` form. - In Step 2, fill the `Refines:` field (or the clause-scoped `Supersedes:` field) in the new ADR's header. -- In Step 3, add the child's index row naming its `Refines:` (or clause-scoped supersession) target. **A refined parent gains no back-pointer and no status change** — it stays `Accepted`, and discoverability comes from the child's header field plus the child's index row. Only a *whole-ADR* supersession flips the parent's index status to `Superseded by ADR-NNNN`; a pure refine (or a clause-scoped supersede) leaves the parent `Accepted`. +- In Step 3, add the child's index row naming its `Refines:`, `Extends:` (or clause-scoped supersession) target. **A refined parent gains no back-pointer and no status change** — it stays `Accepted`, and discoverability comes from the child's header field plus the child's index row. Only a *whole-ADR* supersession flips the parent's index status to `Superseded by ADR-NNNN`; a pure refine (or a clause-scoped supersede) leaves the parent `Accepted`. -**Reviewers must follow the `Refines:` chain.** When an ADR-conformance check finds an ADR that intersects a diff, it also loads any ADR that names that ADR in a `Refines:` (or clause-scoped `Supersedes:`) field and applies the refiner's scoped clauses. A parent read in isolation — without its refiners — yields the pre-narrowing, too-general reading. +**Reviewers must follow the `Refines:` and `Extends:` chains.** When an ADR-conformance check finds an ADR that intersects a diff, it also loads any ADR that names that ADR in a `Refines:`, `Extends:` (or clause-scoped `Supersedes:`) field and applies the child's clauses — an extender can fail a diff the parent passes. A parent read in isolation — without its refiners — yields the pre-narrowing, too-general reading. diff --git a/.claude/skills/blueprint/SKILL.md b/.claude/skills/blueprint/SKILL.md index 48a6e24..b0a0ce2 100644 --- a/.claude/skills/blueprint/SKILL.md +++ b/.claude/skills/blueprint/SKILL.md @@ -185,14 +185,20 @@ Blueprint produces six prose foundation docs plus tooling configs. The order is | 4 | CONTRIBUTING.md | root | Branch/commit/PR norms — mostly inherits from scaffold.md, lighter lift | | 5 | README.md update | root | Updates the existing README with stack info; do not rewrite from scratch | | 6 | docs/cbk/blueprint.md | docs/cbk/ | The cascade artifact — written last so it can reference all the others | +| 7 | docs/cbk/ROADMAP.md | docs/cbk/ | On the `github-issues` and `in-repo-markdown` axes only: the freely mutable status surface (`references/templates/roadmap.md`) — where we are and what is next; seeded from § Core Projects with every row *planned* and the bootstrap row pointing at the handoff issue | **Plus tooling configs** (not prose docs, real config files): - CI workflow files in `.github/workflows/` +- The review workflows from `references/templates/claude-review.yml` and `references/templates/claude.yml`, **only when scaffold's PR/review answer allows review automation** (`scaffold.md` § Working conventions: *reviewed* or *solo-merge with automated review*); otherwise a one-line HITL notice — *"no review automation — scaffold said self-reviewed; say the word to add it"* — never a silent omission - Task runner config (mise.toml, Makefile, justfile, package.json scripts — depends on stack) - `.env.example` if applicable Each doc has its own template in `references/templates/`. Each template starts with a "what to inherit from scaffold.md and problem_brief.md" section so the doc-production process is grounded in the prior phases, not invented fresh. +**Canonical homes before drafting.** Every artifact more than one foundation doc will state — the data schema, a module interface, the check-task names, the version pins, the domain vocabulary — gets **one canonical home** named before any drafting starts: the schema lives in the data-model ADR, the interface in the seam ADR, the check names in `STANDARDS.md`, the pins in the tooling config, the terms in `ARCHITECTURE.md` § Glossary. Every other drafter *references or splices* the home; none restates it (`cbk-conventions.md` § Multi-surface facts). + +**The cross-document critic.** When the drafting fanned out — more than one context wrote the docs — a critic pass is **required** before the HITL review: one agent reads every produced doc and checks five axes across them — schema, interface, check names, versions, terminology — reporting each disagreement as `doc A says X · doc B says Y · home is Z`. When one context drafted every doc the critic is skipped and the skip is recorded in the phase note; the drift it hunts is a fan-out artefact. + **For each doc, the production pattern is**: 1. Read the template 2. Extract relevant inheritance from scaffold.md and problem_brief.md @@ -233,7 +239,7 @@ Detailed templates and production guidance: Blueprint's behavior differs along **two independent axes** set by scaffold: the planning backend (`github-issues` / `linear` / `in-repo-markdown`) and the knowledge backend (`notion` / `none`). The per-axis differences are documented in `references/planning-backend-matrix.md` (planning axis) and `.claude/rules/knowledge-backend.md` (knowledge axis). Short version: **Planning axis** (read `references/planning-backend-matrix.md` for full detail): -- `github-issues`: all foundation docs + `blueprint.md` commit to the GitHub repo via MCP; workstream parent Issues created via GitHub MCP; no Linear initiative entity. Methodology selection is informational only. +- `github-issues`: all foundation docs + `blueprint.md` + `ROADMAP.md` commit to the GitHub repo (`gh` or the git-host MCP — `references/planning-backend-commit.md`); workstream parent Issues created with their `workstream:` labels; no Linear initiative entity. Methodology selection is informational only. - `linear`: foundation docs + `blueprint.md` still commit to GitHub; `blueprint.md` also mirrors as a Linear initiative; Linear Projects created per workstream via Linear MCP. Methodology selection may inform Linear cycle length. - `in-repo-markdown`: only the markdown commits happen; no external planning entities are created. Setup steps that would be a GitHub handoff issue (or Linear initiative description) move into `blueprint.md` § Manual setup. @@ -283,6 +289,7 @@ When blueprint is complete, framing inherits: - **Six foundation docs** at known locations, all reviewed and committed - **Tooling configs** in `.github/workflows/` and the appropriate task runner location - **`docs/cbk/blueprint.md`** containing stack decisions, methodology selection, success criteria, workstreams, dependencies, not-in-scope, and open questions +- **`docs/cbk/ROADMAP.md`** on the `github-issues` and `in-repo-markdown` axes — framing reads it as an input and appends its rows in the same atomic commit as the frame - **All prior cascade artifacts** still readable: `problem_brief.md`, `scaffold.md` **What blueprint must not pass to framing**: actual project plans (that's framing), milestones (rough-in), issues (finish), or implementation code. Blueprint stops at "workstreams with one-sentence purpose each" — turning workstreams into project plans is framing's job. @@ -313,6 +320,7 @@ Detailed in `references/failure-modes.md`. Highlights: Auto-checkable, fires after the final gate and before blueprint declares itself complete. Not a gate; a safety surface — stop and surface if any item fails. Per `cbk-conventions.md` § Trip-wire / phase-exit checklist pattern. - [ ] The six docs exist at their paths (`CLAUDE.md`, `docs/ARCHITECTURE.md`, `docs/STANDARDS.md`, `CONTRIBUTING.md`, `README.md`, `docs/cbk/blueprint.md`), and `docs/cbk/blueprint.md` carries its Cascade metadata, Stack decisions, Methodology and Core Projects sections +- [ ] On the github-issues and in-repo-markdown axes `docs/cbk/ROADMAP.md` exists with its `## Now` paragraph; the review workflows are on disk when scaffold's PR answer allowed them, or the one-line skip notice was given; the cross-document critic ran when the drafting fanned out, or its skip is in the phase note; `docs/cbk/README.md` gained the blueprint row and note - [ ] `CLAUDE.md` mentions the other docs as backticked paths: `grep -n "^- @\|@docs/" CLAUDE.md` prints nothing - [ ] Every ADR blueprint wrote is indexed in `docs/adr/README.md` and in `docs/ARCHITECTURE.md` § Decisions Log, and `docs/cbk/blueprint.md` § Stack decisions lists it - [ ] The handoff issue (or its in-repo-markdown equivalent) exists, and the workstream parent entities blueprint.md names exist on the planning backend @@ -320,6 +328,8 @@ Auto-checkable, fires after the final gate and before blueprint declares itself ## Reference files +- `references/templates/roadmap.md` — the `docs/cbk/ROADMAP.md` template (github-issues and in-repo-markdown axes): the sequence table, the `## Now` paragraph, the status vocabulary, the tracker queries, and who flips what + - `references/planning-backend-commit.md` — the Milestone-per-workstream creation step, atomic transition pattern with the markdown commit, slug collision handling, profile-aware behavior (read this in tandem with `backends.md` from the cascade meta-doc set) - `references/inheritance.md` — how to read scaffold.md and problem_brief.md, what to extract, edge cases diff --git a/.claude/skills/blueprint/references/blueprint-output-template.md b/.claude/skills/blueprint/references/blueprint-output-template.md index 032ec4a..74df703 100644 --- a/.claude/skills/blueprint/references/blueprint-output-template.md +++ b/.claude/skills/blueprint/references/blueprint-output-template.md @@ -193,6 +193,73 @@ stays in this doc, one-time setup commands move to the issue tracker.] **Why this is in blueprint.md and not the handoff issue**: credential models don't go stale the way setup commands do. The user comes back to this section months later when they need to rotate the PAT or understand why a permission failed; it's a reference, not a checklist. The handoff issue is one-time work that closes; the credential model is forever. ``` +## Amendments *(append-only)* + +[Blueprint is a cascade event — immutable after commit except this section, § Retired +justifications and the Stack decisions table (`cbk-conventions.md` § Mutation discipline). A later phase that learns this blueprint was wrong in a way +that does not warrant a re-blueprint appends one dated row: what was stated, what +is true, which phase found it, where the correction landed (an ADR, a frame, a +rule). Never edit the section it amends; the row points at it.] + +| Date | Amends (§) | Was | Is | Found by | Landed in | +|---|---|---|---|---|---| + +## Retired justifications + +[Decisions in this blueprint whose original justification was later retired — a +constraint that lifted, a dependency that changed, a measurement that aged out. +The decision may still stand; the row records that its original reason no longer +does, so nobody re-derives the decision from a dead premise. Dated; append-only.] + +| Decision | Original justification | Retired on | Because | The decision now rests on | +|---|---|---|---|---| + +## PR lifecycle + +[Numbered, so every later phase and command cites a step number rather than a +paraphrase. The default; edit to what this project actually does:] + +1. Branch per `cbk-conventions.md` § Branch naming (the issue-less form for maintenance) +2. Red tests, then the implementation — atomic commits +3. The `check` task green +4. `/simplify`, then `pr-review-toolkit:review-pr` with the sweep beside it — the floor, once +5. Draft PR carrying `## Review gate` and `## Triage` +6. The operator flips to ready; automated review runs where it is wired +7. `/pr-respond` per feedback round; `## Triage — round N` appended to the body +8. Merge (squash); the post-merge checklist from the `/finish` hand-off + +## Newcomer traps *(gated on the operator's tool comfort)* + +[Include only when scaffold's discovery recorded the operator as new to a tool in +this stack; omit for an experienced operator. One bullet per trap actually +expected here — the command that looks right and is not, the setting that +silently defaults, the file that must never be hand-edited — each naming the +reference doc that explains it.] + +## Source precedence over non-cascade documents + +[When a non-cascade document — a vendor README, an inherited design doc, a wiki +page, the frozen corpus — disagrees with a cascade artifact, which wins, in +order. The default: ADRs › this blueprint › the active frame › the `docs/` +foundation docs › the frozen corpus (through its errata) › external documents. +A document not on this list is context, not authority.] + +## Operator runbooks *(the fifth foundation surface)* + +[`docs/runbooks/` — one file per recurring operator task the code does not +automate: a deploy, a key rotation, a data migration, the measurement procedure. +Blueprint creates the directory with an index and the first runbook the stack +already needs; later phases add files there, never sections here. Each runbook: +preconditions, the commands verbatim, the verification, the rollback.] + +## Domain label *(with its body contract)* + +[The one label that marks an issue or PR as touching this project's guarded +domain surface — a schema, a protocol, a safety boundary — as `domain:`, +and the block its PR body must carry: the heading, what it states (the invariant +touched, the check that proves it holds), and that a PR carrying the label +without the block is REQUEST CHANGES. The auto-review prompt reads this section.] + ## What used to be here and isn't anymore Earlier versions of this template had a "Manual setup runlist" section with the full first-build command sequence (run mise install, create the PAT with these scopes, run setup-claude.sh, install the GitHub App, etc). That content has moved to the handoff issue per the seventh-step pattern. Keeping it in blueprint.md was the failure mode of "doc section that goes stale the moment the user completes it" — the handoff issue closes naturally when the work is done, the doc would have to be edited. @@ -201,6 +268,8 @@ Earlier versions of this template had a "Manual setup runlist" section with the ## Planning-axis-aware behavior +On the `github-issues` and `in-repo-markdown` axes blueprint also writes `docs/cbk/ROADMAP.md` from `templates/roadmap.md`, seeded from § Core Projects: one *planned* row per project, the bootstrap row pointing at the handoff issue, `## Now` naming the first runnable command. It is a status surface, freely mutable, never the audit trail. + **`github-issues` planning**: this file is the *only* place the initiative content lives. There's no Linear initiative entity. Framing will read this file, decompose the workstreams into proper project specs, and create the GitHub workstream parent Issues + framing sub-issues from there. **`linear` planning**: this file still gets committed to `docs/cbk/blueprint.md`, AND a Linear initiative entity gets created via Linear MCP with the same Goal, Success Criteria, Not In Scope, and Dependencies content. The file is the source of truth; the Linear entity is a queryable mirror. Framing reads the file; Linear is for human navigation and roll-up reporting. diff --git a/.claude/skills/blueprint/references/handoff-issue.md b/.claude/skills/blueprint/references/handoff-issue.md index 35ccdef..54b7c9f 100644 --- a/.claude/skills/blueprint/references/handoff-issue.md +++ b/.claude/skills/blueprint/references/handoff-issue.md @@ -67,10 +67,15 @@ find what to check. ## 3. Manual setup steps The cascade cannot automate these. See `docs/cbk/blueprint.md` § "Credential -model" for background on why each is needed. +model" for background on why each is needed. The model extends to **credentials +not yet held**: a key the operator must apply for, a tier that must be paid for, +an access grant another person controls — each gets its own line here with who +grants it, the expected lead time, and what the build can do without it; a +milestone that needs one names it in its pre-flight row, never assumes it. - [ ] Run `/install-github-app` inside Claude Code (or equivalent for - whatever review automation was chosen) + whatever review automation was chosen) — this line only when + scaffold's PR/review answer allowed review automation; drop it otherwise - [ ] Create the local PAT and run `` to wire up MCP access - [ ] [Any other manual steps from blueprint's stack decisions] @@ -97,7 +102,7 @@ When generating section 2, blueprint walks every config file it just committed a Files to walk for the inventory: - The language-specific manifest (Cargo.toml workspace deps, package.json deps, pyproject.toml deps, etc.) -- Toolchain pinning (mise.toml, .tool-versions, .nvmrc, rust-toolchain.toml, etc.) +- Toolchain pinning (mise.toml, .tool-versions, .nvmrc, rust-toolchain.toml, etc.) — settle-window-covered but bot-uncovered: no dependabot ecosystem maintains these, so the auto-update bot's presence does not imply they are handled - CI workflow files for action refs (`.github/workflows/*.yml`, `.gitlab-ci.yml`, etc.) - Any other config file blueprint committed that contains version strings diff --git a/.claude/skills/blueprint/references/planning-backend-commit.md b/.claude/skills/blueprint/references/planning-backend-commit.md index bb5fb77..3a80fe5 100644 --- a/.claude/skills/blueprint/references/planning-backend-commit.md +++ b/.claude/skills/blueprint/references/planning-backend-commit.md @@ -46,6 +46,10 @@ Blueprint constructs each parent Issue's body from the **`cascade-workstream.md` ## Atomic transition pattern +### The `gh` shape (github-issues) + +`gh` is the default interface; the five-command shape and its two rough edges live in one home, the rough-in skill's `references/planning-backend-commit.md` § The `gh` shape. Blueprint's delta: the workstream parents have no link target (the link and verify lines are skipped), and the create-then-edit pass resolves cross-references between sibling parents. + The planning-backend commit and the markdown commit must be **atomic** — either both succeed or neither does. Order of operations: 1. **Capture** every operation as a planned transition diff --git a/.claude/skills/blueprint/references/planning-backend-matrix.md b/.claude/skills/blueprint/references/planning-backend-matrix.md index c66a5cb..e7b67c5 100644 --- a/.claude/skills/blueprint/references/planning-backend-matrix.md +++ b/.claude/skills/blueprint/references/planning-backend-matrix.md @@ -21,6 +21,8 @@ The planning backend is determined by `scaffold.md`'s Cascade metadata section. ## `github-issues` planning behavior +**Interface**: `gh` by default — the five-command shape lives in the rough-in skill's `references/planning-backend-commit.md` § The `gh` shape, this phase's delta in its own `references/planning-backend-commit.md`; a git-host MCP is one connection among several, and the phase reads and writes the working tree by default. + **The most common case.** If scaffold picked `github-issues`, blueprint operates entirely against the GitHub repo for planning. There is no Linear, no external project tool. The structural fact that shapes everything: **`github-issues` is three-level** (project board → sub-issue → sub-sub-issue, via GitHub's native sub-issue API), with no native concept of "initiative." This was surfaced and explicitly acknowledged in scaffold's backend selection. Blueprint's job under this constraint is to produce the initiative content **as a markdown document at `docs/cbk/blueprint.md`** rather than as a planning entity. Framing then reads that document as context for project decomposition. diff --git a/.claude/skills/blueprint/references/templates/claude-review.yml b/.claude/skills/blueprint/references/templates/claude-review.yml new file mode 100644 index 0000000..11801e1 --- /dev/null +++ b/.claude/skills/blueprint/references/templates/claude-review.yml @@ -0,0 +1,261 @@ +# Claude PR auto-review — blueprint template. Copy to .github/workflows/claude-review.yml +# and fill every [bracketed] value; the seven authoring constraints are stated below — +# 3, 5, 6 and 7 at the step they govern, 1, 2 and 4 as file-level properties in this +# preamble — and survive edits. Emitted only when scaffold's PR/review answer allows +# review automation (scaffold.md § Working conventions); otherwise blueprint surfaces the +# skip as a one-line notice and this file is not written. +# +# Fires ONCE per readiness cycle: opened (non-draft) · ready_for_review (the common +# trigger — /finish ends at draft, the author flips ready) · reopened · labeled (a manual +# re-trigger, gated to `claude-review-again`). NOT on `synchronize`: follow-up pushes do +# not re-fire a review (constraint 5 — label-driven proportionality); re-trigger with +# the label. Model and effort are explicit, never inherited (orchestration.md). +# +# Constraint 1 — the action cannot review the PR that introduces it: the action +# validates this file against the default branch and exits green without running on +# any PR that adds or changes it. Verify on the next PR, not this one. +# +# Constraint 4 — the posted artifact is the deliverable, in both directions: a green run +# with no summary comment is a failure to investigate; a red run whose review posted is a +# pass (cbk-conventions.md § Deliverable trap). The prompt makes the summary an explicit +# `gh pr comment` and names it first, and the last step asserts it landed: the action runs +# with continue-on-error so the job's verdict is the assertion's — red only when no summary +# comment from the review app appeared after the job started, with a one-line notice posted +# to the PR so the failure is visible there. Designed-unexercised as of 2026-09-06: verify on +# the first PR after this file lands and restamp this line. +# +# GOTCHA: the CI-skip marker on the HEAD commit at the draft→ready flip silently blocks +# this workflow — end the branch on a non-marker commit before flipping +# (cbk-conventions.md § [skip ci] rule, the auto-review trap). +# +# Needs the `CLAUDE_CODE_OAUTH_TOKEN` Actions secret (`/install-github-app` in Claude Code). +# +# Constraint 2 — SAFETY: no author-controlled input (title, body, commits, branch name, +# labels) is interpolated into a `run:` command. Labels arrive through `env:` as JSON and +# are read as quoted shell variables. The action is pinned to a full commit SHA with a +# version comment (cbk-conventions-reference.md § Dependency settle-window); dependabot's +# github-actions sweep maintains the pin under the floor. +# https://github.blog/security/vulnerability-research/how-to-catch-github-actions-workflow-injections-before-attackers-do/ + +name: Claude PR Auto-Review + +on: + pull_request: + types: [opened, ready_for_review, reopened, labeled] + # Docs-only and cascade-artifact PRs are not reviewed by the bot. This is NOT a + # required check, so a paths filter is safe here (unlike ci.yml — cbk-conventions-reference.md + # § Required-checks trap). A docs-only PR whose docs are one-way + # doors (ADRs, the conventions) is reviewed by a human, not skipped by everyone. + paths-ignore: + - 'docs/cbk/**' + - '**/*.md' + - '.gitignore' + - '.gitattributes' + - '.editorconfig' + - '.github/dependabot.yml' + # [add the project's other prose-only paths] + +concurrency: + group: claude-review-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + auto-review: + name: Auto-review (inline + summary) + if: | + (github.event.action != 'labeled' || github.event.label.name == 'claude-review-again') && + !github.event.pull_request.draft && + !contains(github.event.pull_request.labels.*.name, 'skip-claude') && + github.event.pull_request.user.login != 'dependabot[bot]' && + github.event.pull_request.user.login != 'renovate[bot]' && + github.event.pull_request.user.login != 'claude[bot]' + runs-on: ubuntu-latest + # Constraint 3 — a job-level timeout: the action has no timeout input of its own. + timeout-minutes: 30 + # Constraint 6 — permissions scoped to the job. `id-token: write` is required by the + # action even in OAuth-token mode; `checks: read` for the prompt's `gh pr checks`. + permissions: + contents: read + pull-requests: write + issues: read + actions: read + checks: read + id-token: write + steps: + - name: Record job start (the assertion below reads comments created after it) + id: start + run: echo "at=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_OUTPUT" + + - name: Remove claude-review-again label (so it can be re-added next time) + if: github.event.action == 'labeled' && github.event.label.name == 'claude-review-again' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} + REPO: ${{ github.repository }} + run: | + set -uo pipefail + # --repo because this step runs BEFORE checkout. Read the PR's labels first: the one + # benign case is the label already gone from this PR (a concurrent run took it); + # every other failure — including the label deleted from the repository's taxonomy, + # which would break re-triggering for everyone — is an error. + if gh pr view "$PR_NUMBER" --repo "$REPO" --json labels --jq '.labels[].name' | grep -qxF 'claude-review-again'; then + gh pr edit "$PR_NUMBER" --repo "$REPO" --remove-label claude-review-again \ + || { echo "::error::Failed to remove the claude-review-again label."; exit 1; } + else + echo "Label already removed from this PR (benign concurrent run)." + fi + + - name: Checkout PR HEAD + uses: actions/checkout@[full-commit-sha] # v[version] — [newest release clearing the settle window as of ] + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + + - name: Pick model + effort from labels + id: model + # Constraint 5 — label-driven proportionality. Precedence: claude-deep-review > + # claude-fast-review > default. Matches the QUOTED JSON token so a stray + # `claude-deep-review-wip` label cannot false-trigger the heavy path. + # Constraint 7 — no orphanable sub-dispatch: the default and fast paths keep the + # review in one session (`--disallowedTools Agent`); only the deep path fans out. + env: + LABELS_JSON: ${{ toJSON(github.event.pull_request.labels.*.name) }} + run: | + set -uo pipefail + labels="${LABELS_JSON:-[]}" + if printf '%s' "$labels" | grep -qF '"claude-deep-review"'; then + echo "model=[]" >> "$GITHUB_OUTPUT" + echo "effort=xhigh" >> "$GITHUB_OUTPUT" + # `ultracode` as exercised on the deep path (a dated observation, 2026-09-06); re-verify the setting name after harness upgrades. + echo "settings_arg=--settings '{\"ultracode\":true}'" >> "$GITHUB_OUTPUT" + echo "disallow_arg=" >> "$GITHUB_OUTPUT" + elif printf '%s' "$labels" | grep -qF '"claude-fast-review"'; then + echo "model=[]" >> "$GITHUB_OUTPUT" + echo "effort=high" >> "$GITHUB_OUTPUT" + echo "settings_arg=" >> "$GITHUB_OUTPUT" + echo "disallow_arg=--disallowedTools Agent" >> "$GITHUB_OUTPUT" + else + echo "model=[]" >> "$GITHUB_OUTPUT" + echo "effort=high" >> "$GITHUB_OUTPUT" + echo "settings_arg=" >> "$GITHUB_OUTPUT" + echo "disallow_arg=--disallowedTools Agent" >> "$GITHUB_OUTPUT" + fi + + - name: Auto-review + # continue-on-error: the assertion step below decides the job's verdict (constraint 4). + continue-on-error: true + uses: anthropics/claude-code-action@[full-commit-sha] # v[version] — [newest release clearing the settle window as of ] + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + # Full SDK output so an auth or stream failure is diagnosable from the run log. + show_full_output: true + # track_progress is unsupported on `labeled` events (the action fails). + track_progress: ${{ github.event.action != 'labeled' && 'true' || 'false' }} + additional_permissions: | + actions: read + prompt: | + You are reviewing a pull request against []. Read `CLAUDE.md` first + []. + + ## What to do + + Enumerate every changed file (`gh pr diff ${{ github.event.pull_request.number }} --name-only`) + and walk each one; read the surrounding file, not only the hunk, when context matters. + + Then post, **in this order**: + + 1. **One summary comment** at the top of the PR, with `gh pr comment` — the verdict and the + finding counts (format below). Post it FIRST; it is the durable output. + 2. **Inline comments** for specific code-level findings, via the inline-comment MCP tool + (`mcp__github_inline_comment__create_inline_comment`). + + **Your deliverable is the posted comments, not your reply.** A review that never ran + `gh pr comment` for its summary produced nothing. If you are low on turns, post the + summary before adding more inline comments. Do the review in this one session — do not + dispatch subagents and wait on them. + + ## Rubric (from `.claude/rules/pr-review.md`) + + Classify every finding into exactly one class: **Apply** (a behavior-preserving fix with + concrete evidence — a reproducible failing path, a factually wrong comment, dead code, a + low-risk change under about 15 lines), **Apply with care** (a cross-file refactor or a + non-obvious correctness fix), **Surface** (a genuine design or taste call — inline only + when tied to a file and line), **Defer** (conflicts with an ADR or is out of scope), + **Reject** (a misreading of the codebase or a finding that contradicts the project's + rules — one line in the summary, never inline). Tag inline comments with the class. + + ## Project-level rules to check against (read the ones the diff touches) + + - `.claude/rules/testing.md` — [] + - `.claude/rules/logging.md` — [] + - `.claude/rules/cbk-conventions.md` — branch naming, commit format, the label taxonomy, + the CI-skip-marker rule, mutation discipline (ADRs immutable; cascade artifacts append-only) + - `.claude/rules/simplification.md` — dead code, unnecessary indirection, over-clever expressions + - [] + + Architectural commitments live in the ADRs (`docs/adr/`, immutable). Read `docs/adr/README.md` + — the always-current index — and the ADRs the diff plausibly touches, following each + `Refines:` / `Extends:` chain; check the change against their Decision and Consequences. + Do not rely on a hard-coded ADR list in this prompt. Known citation defects live in + `docs/adr/corrections.md`; do not restate a claim already flagged wrong there. + + Project-local reviewer agents in `.claude/agents/` encode extra checks. Apply them + **inline yourself** (one session, no subagents): read the ones whose scope the diff + touches, per `.claude/rules/pr-review.md` § Project-local agents. + + ## How to format the summary comment + + One-line verdict: **APPROVE** / **APPROVE WITH NITS** / **REQUEST CHANGES** / **NEEDS DISCUSSION**. + Then a counts table (Apply / Apply with care / Surface / Defer / Reject). Then three to + seven lines on the most important findings and any cross-cutting concern. CI status is + usually NOT readable from your token (`gh pr checks` fails with "Resource not accessible + by integration"); if it is, reference a failed check, otherwise say nothing about CI — + never report the permission error as a finding. End with one line on what is NOT in + scope of this review. + + ## What to skip + + - Docs-only or cascade-artifact PRs (`docs/cbk/`, `*.md` only): post the one-line summary + `Docs-only PR — auto-review skipped per rule` and exit — unless the docs are one-way + doors (ADRs, the conventions), which a human reviews. + - Issues in unchanged code; speculative future-risk warnings with no concrete failing path; + restating what a function name already says; library or framework swaps (blueprint's job); + stylistic nits without a strong rationale (those are Reject, one line). + + ## Context + + `gh pr view ${{ github.event.pull_request.number }}` for metadata, `gh pr diff` for the diff, + and the inline-comment MCP tool for line-specific findings. + + Model in use: ${{ steps.model.outputs.model }} (effort: ${{ steps.model.outputs.effort }}) + claude_args: | + --model ${{ steps.model.outputs.model }} + --fallback-model [] + --effort ${{ steps.model.outputs.effort }} + ${{ steps.model.outputs.settings_arg }} + --max-turns 60 + --allowedTools "Read,Grep,Glob,Bash(gh pr view:*),Bash(gh pr diff:*),Bash(gh pr comment:*),Bash(gh pr checks:*),Bash(gh run view:*),Bash(gh run list:*),mcp__github_inline_comment__create_inline_comment" + ${{ steps.model.outputs.disallow_arg }} + + - name: Assert the review posted (constraint 4 — the artifact decides) + if: always() + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} + REPO: ${{ github.repository }} + STARTED_AT: ${{ steps.start.outputs.at }} + REVIEW_LOGIN: "[]" + RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + run: | + set -uo pipefail + n="$(gh api "repos/$REPO/issues/$PR_NUMBER/comments" --paginate \ + --jq "[.[] | select(.user.login == \"$REVIEW_LOGIN\") | select(.created_at >= \"$STARTED_AT\")] | length")" || n=0 + if [ "${n:-0}" -ge 1 ]; then + echo "Review posted ($n comment(s) since $STARTED_AT)." + else + gh pr comment "$PR_NUMBER" --repo "$REPO" --body "Auto-review did not post a summary — see $RUN_URL. Re-trigger with the \`claude-review-again\` label." || true + echo "::error::No review comment from $REVIEW_LOGIN since $STARTED_AT — the deliverable did not land (cbk-conventions.md § Deliverable trap)." + exit 1 + fi diff --git a/.claude/skills/blueprint/references/templates/claude.yml b/.claude/skills/blueprint/references/templates/claude.yml new file mode 100644 index 0000000..5334e33 --- /dev/null +++ b/.claude/skills/blueprint/references/templates/claude.yml @@ -0,0 +1,86 @@ +# Claude Code GitHub Action — the interactive @claude mention handler. Blueprint +# template: copy to .github/workflows/claude.yml and fill the [bracketed] values. +# Ships beside claude-review.yml under the same scaffold PR/review answer. +# +# Fires when @claude is mentioned in a PR or issue comment, a review, or an issue body. +# - concurrency + cancel-in-progress so a rapid back-and-forth does not queue +# - skips bots and the `skip-claude` label (avoids @claude loops; the opt-out) +# - explicit --allowedTools (least privilege at the tool level) +# - the workhorse model at its default effort: mentions are interactive and quota-sensitive +# +# Needs the `CLAUDE_CODE_OAUTH_TOKEN` Actions secret (`/install-github-app`). +# +# SAFETY (constraint 2): no untrusted input (issue title, PR body, comment body) is +# interpolated into `run:` — ACTOR and the label JSON arrive through `env:` and are read +# as quoted shell variables. The action is pinned to a full commit SHA (constraint: the +# settle window, cbk-conventions-reference.md). A job timeout (constraint 3) and job-scoped +# permissions (constraint 6) as in claude-review.yml. + +name: Claude (@claude mention) + +on: + issue_comment: + types: [created] + pull_request_review_comment: + types: [created] + pull_request_review: + types: [submitted] + issues: + types: [opened, assigned] + +concurrency: + group: claude-mention-${{ github.event.issue.number || github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + claude: + name: Claude responds to @mention + permissions: + contents: read + pull-requests: read + issues: read + actions: read + id-token: write + if: | + (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || + (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || + (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || + (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - name: Skip if bot author or skip-claude label + id: gate + env: + ACTOR: ${{ github.actor }} + ISSUE_LABELS_JSON: ${{ toJSON(github.event.issue.labels.*.name) }} + PR_LABELS_JSON: ${{ toJSON(github.event.pull_request.labels.*.name) }} + run: | + set -uo pipefail + ALL_LABELS="${ISSUE_LABELS_JSON:-[]} ${PR_LABELS_JSON:-[]}" + if [[ "$ACTOR" == "claude[bot]" || "$ACTOR" == "dependabot[bot]" || "$ACTOR" == "renovate[bot]" ]]; then + echo "skip=true" >> "$GITHUB_OUTPUT" + elif printf '%s' "$ALL_LABELS" | grep -qF '"skip-claude"'; then + echo "skip=true" >> "$GITHUB_OUTPUT" + else + echo "skip=false" >> "$GITHUB_OUTPUT" + fi + + - name: Checkout + if: steps.gate.outputs.skip == 'false' + uses: actions/checkout@[full-commit-sha] # v[version] + with: + fetch-depth: 1 + + - name: Run Claude Code + if: steps.gate.outputs.skip == 'false' + uses: anthropics/claude-code-action@[full-commit-sha] # v[version] + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + additional_permissions: | + actions: read + claude_args: | + --model [] + --fallback-model [] + --max-turns 60 + --allowedTools "Bash(gh issue view:*),Bash(gh issue comment:*),Bash(gh pr view:*),Bash(gh pr diff:*),Bash(gh pr comment:*),Bash(gh pr checks:*),Bash(gh run view:*),Bash(gh run list:*),mcp__github_inline_comment__create_inline_comment" diff --git a/.claude/skills/blueprint/references/templates/contributing.md b/.claude/skills/blueprint/references/templates/contributing.md index 53f0f71..8b2a77d 100644 --- a/.claude/skills/blueprint/references/templates/contributing.md +++ b/.claude/skills/blueprint/references/templates/contributing.md @@ -90,6 +90,8 @@ Examples: - `` - `` +Operator-directed maintenance with no cascade issue uses `/` and opens its PR with the line "operator-directed maintenance; no cascade issue" (`cbk-conventions.md` § Branch naming). + [Any additional notes about branch lifetimes, when to delete, etc.] ## Commits diff --git a/.claude/skills/blueprint/references/templates/roadmap.md b/.claude/skills/blueprint/references/templates/roadmap.md new file mode 100644 index 0000000..b2073b0 --- /dev/null +++ b/.claude/skills/blueprint/references/templates/roadmap.md @@ -0,0 +1,54 @@ +# Roadmap template — `docs/cbk/ROADMAP.md` + +Emitted by blueprint on the **github-issues** and **in-repo-markdown** planning axes (on `linear`, the planner's projects carry the sequence). It answers the question those axes otherwise cannot: *where are we, and what is next* — issues carry the detail, labels carry state, a saved filter shows a pool, but none of them shows the order the work happens in or which step is current. Framing reads it as an input and appends a row per milestone in the same atomic commit as the frame; rough-in flips a row to *roughed-in*; `/finish` flips a row to *done* on the PR's own branch (`.claude/commands/finish.md` item 8) or the post-merge checklist does. The kit's conventions record its mutation class (`cbk-conventions.md` § Mutation discipline). + +## The template + +```markdown +# Roadmap — where we are, where we are going + +**A living status surface, freely mutable** (unlike the cascade events index, which is append-only). Read it first when returning cold; the issue it points at has the detail. **Who keeps it current:** blueprint wrote it; **framing** adds a row per milestone it cuts and links the `F` issue, in the same commit as the frame; **rough-in** flips a row to *roughed-in* and links the `R` issues; **`/finish`** flips a row to *done* and links the PR — on the PR's own branch, as the last commit before the PR opens, with the PR number read ahead from the repository's shared issue-and-PR sequence (GitHub's REST API considers every pull request an issue and its issue endpoints return both — `https://docs.github.com/en/rest/issues/issues`, read 2026-09-06 — so the newest number across both is the last item of an issues listing) and checked at creation (`.claude/commands/finish.md` item 8), or the post-merge checklist flips it when the spec did not name the flip. An `R` issue short of the milestone's capstone updates the row's progress note and the `## Now` paragraph the same way. Anything else that changes the sequence — a milestone re-cut, a horizon item pulled forward — edits the table in the same PR. A row that is wrong is fixed in place; this is not an audit trail (that is `docs/cbk/README.md` and git history). + +## Now + +**Step <#> — , .** `, `rough-in `, `framing `); what is blocked and on what.> **▶ Next: .** + +## The sequence + +Ordered by dependency, not by preference. + +| # | Step | Workstream | What "done" is | Depends on | Status | Where | +|---|---|---|---|---|---|---| +| 0 | **Bootstrap** — | — | | blueprint merged | planned | | +| 1 | **** — | `` | | 0 | planned | | +| 2 | … | | | | | | + +**Status vocabulary**: *now* · *planned* (blueprint only) · *framed* (an `F` issue exists) · *roughed-in* (`R` issues exist, `/finish`-able) · *in progress* (a branch is open) · *done* (merged — or written by `/finish` on the merging PR's own branch, per the paragraph above) · *horizon* (tracked, no timeline) · *retired* (superseded; say by what). + +## Cascade position + +| Phase | State | +|---|---| +| consultation | done — `problem_brief.md` | +| scaffold | done — `scaffold.md` | +| blueprint | done — `blueprint.md`, the ADRs, the foundation docs, the tooling | +| framing | ` framed (`frame-NN.md`) · …> | +| rough-in | | +| `/finish` | | + +## Seeing the same thing from the tracker + +```bash +gh issue list --state open --label 'cascade-depth:framed' # milestones cut, not yet roughed-in +gh issue list --state open --label 'cascade-depth:roughed-in' # /finish-able now +gh issue list --state open --label enhancement # shaped by /intake, awaiting cascade work +gh issue list --search 'Blueprint handoff in:title' # step 0 +``` +``` + +## Authoring guidance + +- **One row per step the sequence needs to show**, not per issue: a workstream's milestones are rows once framing cuts them; before that the workstream is one *planned* row pointing at its blueprint entry. +- **`## Now` is a paragraph, not a table** — it carries the narrative a returning reader needs (what landed, what is next, what is blocked), with links; keep it current every time a row flips. +- **On `in-repo-markdown`** the `## Seeing the same thing from the tracker` section is dropped and the `Where` column points at the markdown issue records. +- **Never let the roadmap become the audit trail**: a superseded row reads *retired — superseded by *; the events index and git history hold what happened. diff --git a/.claude/skills/blueprint/references/templates/standards.md b/.claude/skills/blueprint/references/templates/standards.md index c6031fd..6b71d83 100644 --- a/.claude/skills/blueprint/references/templates/standards.md +++ b/.claude/skills/blueprint/references/templates/standards.md @@ -180,6 +180,8 @@ The exemption must be specific: name the gate, name the commits or sub-batch, an **When this section appears**: only when blueprint's stack decisions or scaffold's setup added a CI gate that didn't exist on day one of the repo. If the repo had its full CI from the first commit, no bootstrap exemption section is needed. +**A required check always reports**: a workflow promoted to a required check carries no `paths:` trigger filter and a stable, explicit job `name:` set before promotion — a skipped or renamed check parks every PR (`cbk-conventions-reference.md` § Required-checks trap); STANDARDS.md states this rule where it lists the gates. + **Surface this proactively**: if blueprint's stack decisions include adding a new gate during this phase (new CI workflow, new pre-commit hook, new required check), the skill should explicitly tell the user at the relevant HITL gate: *"This means the commit that adds can't itself be reviewed by — that's a known bootstrap exemption. I'll document it in STANDARDS.md so it doesn't surprise anyone later."* ## Quality bar calibration diff --git a/.claude/skills/blueprint/references/templates/tooling.md b/.claude/skills/blueprint/references/templates/tooling.md index 78218e5..eb93df7 100644 --- a/.claude/skills/blueprint/references/templates/tooling.md +++ b/.claude/skills/blueprint/references/templates/tooling.md @@ -40,7 +40,7 @@ The set depends on stack decisions. Default set: - Skip entirely if the project has no env config 4. **Optional**: review automation - - `claude-code-action` workflow if scaffold.md's PR/review process said to use it (e.g., a "CI must trigger Claude code review on PRs" preference) + - the review workflows from `templates/claude-review.yml` and `templates/claude.yml` if scaffold.md's PR/review process said to use them (*reviewed* or *solo-merge with automated review*); otherwise surface the skip as a one-line notice - Skip if scaffold didn't ask for it ## Rules @@ -89,6 +89,7 @@ The sanity pass asks eight questions. Each is **stack-agnostic**; the user bring 4. **License / advisory checker** — Will the license allowlist and advisory rules match what gets pulled transitively once dependencies are installed? Do you want to run the checker once now against a populated dependency tree and amend the allowlist, or ship defaults and fix at first CI run? 5. **Stack-decision contradictions** — Any tooling config rules we wrote that contradict stack decisions from earlier in this session? (Example: a ban on a system library that a storage decision requires.) This is a cross-check: read the bans / denies / allow-lists in the tooling configs and verify none contradict the stack decisions list. 6. **Automated review bot workflows** — **If we're committing an automated review bot as a merge gate** (claude-code-action, CodeRabbit, Copilot review, or equivalent): the out-of-the-box workflows these tools ship are usually wrong for any specific project. They over-review, under-review, comment on the wrong things, or apply generic rules that don't match your standards. Retuning them is a real authoring task — see the "Automated review bot prompt construction" section below for the full treatment. Sanity-pass-level decisions to make right now: (a) do you want the review bot to skip docs-only and template-only PRs to save tokens? (b) do you want concurrency cancellation so a burst of pushes only pays for the final review? (c) what wall-clock cap on the review job? (d) read-only tool permissions, or can the bot commit/push? (e) which PR authors should be skipped (drafts, dependabot, renovate, etc.)? **Also**: how will you verify the review bot actually ran and commented on your first post-blueprint PR? A bot gate that's silently broken is worse than no gate. +6b. **Toolchain pins the bot does not cover** — the toolchain manager's pins (`mise.toml` `[tools]`, `.tool-versions`, `rust-toolchain.toml`, `.nvmrc`) and container base-image tags are outside every dependabot ecosystem: the settle window applies by hand (`cbk-conventions-reference.md` § Dependency settle-window). Decide now how the project records the settled age at pin time and what tracks the next bump — an open question in the frame, or a project automation. 7. **Coupled dependency pairs** — Are there dependency pairs in your stack that are coupled upstream (library A caps library B's version, or they need to be bumped together)? If yes, your automated dependency bump config should group them so a single coordinated PR lands instead of two conflicting ones that can't both merge. 8. **Temporary workarounds and trip-wires** — Any workarounds we're shipping from questions 1–7 just to get CI green on empty scaffolding? For each, we'll capture it in a **Cleanup tracking** section in `blueprint.md` with an explicit trip-wire condition for removal. Example trip-wires: *"remove when the first real test binary lands"*, *"remove when each ignored dep gets actually wired up"*, *"remove when upstream library X drops dependency Y"*. @@ -210,6 +211,6 @@ If the user invoked light mode: - **Task runner config has the minimum set**: setup, check, test. Skip lint/typecheck/dev/fix/etc. unless they were explicitly chosen during stack decisions. - **CI workflow has 1–3 jobs** instead of 5+ - **Skip env template** unless the project clearly has env vars -- **Skip review automation** unless explicitly requested +- **Surface a skipped review automation as a one-line notice**, never a silent omission Even in light mode, the "every CLAUDE.md command = actual task" rule still applies. Non-negotiable. diff --git a/.claude/skills/blueprint/references/test_cases.md b/.claude/skills/blueprint/references/test_cases.md index a560044..e7cc094 100644 --- a/.claude/skills/blueprint/references/test_cases.md +++ b/.claude/skills/blueprint/references/test_cases.md @@ -19,6 +19,9 @@ For Claude.ai's qualitative test loop (no subagents, no benchmarking): a real hu - Working conventions from `scaffold.md` (branch naming, commit format, label taxonomy) appear in CONTRIBUTING.md (or equivalent) verbatim — no re-deriving - Tooling configs are produced: every command in CLAUDE.md maps to an actual task in the task runner config; every CI gate in STANDARDS.md maps to an actual job in the workflow file - A handoff issue gets created with the version-pin inventory, the toolchain bootstrap command, and the bootstrap-exemption-end statement +- `docs/cbk/ROADMAP.md` is emitted on this github-issues run (it would be on in-repo-markdown too, and not on linear); `docs/cbk/README.md` gets the blueprint row and phase note appended, never recreated +- The cross-document critic ran only if the drafting fanned out; with one context drafting every doc, the skip is stated in the phase note, not silent +- Review automation: the workflows are emitted only because scaffold's PR answer allowed them; on a self-reviewed scaffold the skip is a one-line notice - Standard mode runs four HITL gates total (post-stack, post-methodology, post-foundation-docs-batch, post-handoff-issue) - The phase exit checklist ran: `CLAUDE.md` mentions the other docs as backticked paths (no `@docs/` line), and every ADR written is indexed in both indexes. diff --git a/.claude/skills/consultation/SKILL.md b/.claude/skills/consultation/SKILL.md index 48ec62f..3484c6b 100644 --- a/.claude/skills/consultation/SKILL.md +++ b/.claude/skills/consultation/SKILL.md @@ -9,9 +9,9 @@ Phase 1 of the six-phase AI-assisted development cascade. Transforms a raw idea This skill is deliberately conversational and HITL-heavy. The human is the source of truth about what problem is worth solving; Claude's job is to interview well, reflect back honestly, and refuse to solutionize prematurely. -## Incoming context — Notion or other pre-cascade material (optional, runs first if applicable) +## Incoming context — Notion, a frozen corpus on disk, or other pre-cascade material (optional, runs first if applicable) -Before the four-step interview, check whether the operator is arriving with existing context worth ingesting as starting material — typically Notion pages they've curated, prior design docs, research from another tool. This is especially common for operators who picked the cascade to organize *existing* thinking, not to start from scratch. +Before the four-step interview, check whether the operator is arriving with existing context worth ingesting as starting material — typically Notion pages they've curated, prior design docs, research from another tool. **Two branches by where the material lives:** Notion (or pasted content) takes the four access modes below; a **directory of planning files on disk** takes `references/frozen_corpus_ingestion.md` — read in full, cited verbatim in `## Pre-cascade sources`, never edited, with a lazily created `-errata.md` companion for defects, and the brief's `## Handoff notes for later phases` telling scaffold what to land and register. This is especially common for operators who picked the cascade to organize *existing* thinking, not to start from scratch. **Opening question** (skip if the user's first message already provided rich context): @@ -175,6 +175,7 @@ The scaffold phase consumes the approved brief and extracts: project name (for n - `references/methodology_register_excerpt.md` — Shape Up, spikes, YAGNI entries plus pointers to the full register - `references/brownfield_addendum.md` — current-state-assessment step and brownfield-specific brief additions - `references/notion_ingestion.md` — operational reference for the Notion-as-input flow at § Incoming context: four access modes, MCP fetch dialogue, per-search HITL announcements, what to record in `## Pre-cascade sources` +- `references/frozen_corpus_ingestion.md` — the sibling flow for a directory of planning material on disk: the freeze discipline, the errata companion, `Promotes:`, the enforcement set scaffold registers, what to record in the brief - `references/test_cases.md` — realistic test prompts (greenfield, brownfield, partial-context) with success criteria for verifying the skill still works after revisions Kit-wide operational contracts (`.claude/rules/`): diff --git a/.claude/skills/consultation/references/frozen_corpus_ingestion.md b/.claude/skills/consultation/references/frozen_corpus_ingestion.md new file mode 100644 index 0000000..384afd3 --- /dev/null +++ b/.claude/skills/consultation/references/frozen_corpus_ingestion.md @@ -0,0 +1,39 @@ +# Frozen-corpus ingestion + +Operational reference for consultation's incoming-context flow when the operator points at a **directory of pre-existing planning material on disk** — design notes, a prior team's decision log, a research corpus, an exported wiki — rather than Notion pages (`references/notion_ingestion.md` covers that sibling flow). Runs before the four-step interview, like the Notion flow. + +## When to run this + +Trigger when the operator names a path ("everything under `planning/`", "the docs in `~/notes/`") or drops a directory of files as the starting material. A handful of pasted paragraphs is Mode D of the Notion flow, not this. + +## The freeze discipline + +The corpus is **frozen** the moment consultation reads it: a source of truth that is read in full and never edited by any cascade phase. + +- **Read in full.** Every file, not a sample; a corpus that is too large to read in full is split by the operator into the part that governs this cascade and the part that does not, and only the first is designated. +- **Cite verbatim.** The brief's `## Pre-cascade sources` names the corpus path and lists each file that informed a section, with the quoted passage where the brief's wording depends on it. Paraphrased inheritance is the cascade's most common failure mode. +- **Never edit the source.** A defect in the corpus — a wrong figure, a superseded claim, a broken link — is recorded in the errata companion, never fixed in place. + +## The errata companion + +`/-errata.md` is created **lazily**, at the first defect, never as setup. It is append-only and dated; each entry names the file and the passage, quotes it, states the correct reading and the evidence, and opens with the line *amends; never edits*. The companion points at the corpus; the corpus never points at the companion (`cbk-conventions.md` § Multi-surface facts). The shape is the ADR corrections register's (`docs/adr/corrections.md` in a scaffolded project), with the corpus file in place of the ADR. + +## `Promotes:` — a decision lifted from the corpus + +When a later phase turns a decision the corpus already made into an ADR, the ADR's header carries `Promotes: / § ` — the corpus is the provenance, the ADR the binding form. The ADR restates the decision in its own words and quotes the passage it promotes; the corpus file is not edited to point at the ADR. + +## The enforcement set scaffold registers + +Consultation records the designated path in the brief; **scaffold** lands the corpus in the repo (or records its external location) and registers the guards, in one commit, when the corpus lives in the tree: + +- **A hook and a CI job on the corpus pattern** — the immutability hook and the ADR-immutability CI job extended (or copied) to match the corpus path, with a block message that names the errata companion as the place a correction goes. +- **`.gitattributes`**: `/** linguist-documentation=false` so the corpus counts as content, not vendored prose, in the host's language stats and diffs. +- **Editor settings**: trailing-whitespace trim and final-newline insertion unset for the corpus path (a byte-preserving read is the point of a freeze). +- **A formatter skip entry** for the corpus path in whatever the project's docs formatter reads. + +Scaffold's brownfield audit and backend selection read the designated path from `## Pre-cascade sources`; the bootstrap checklist carries the four items as one row. + +## What to record in the brief + +- `## Pre-cascade sources`: the corpus path; one bullet per file consulted with its one-line contribution; the passages quoted where wording is inherited; the errata companion's path if one was created. +- `## Handoff notes for later phases`: what scaffold must land and register; what blueprint may promote (with the `Promotes:` form); what framing and rough-in should read before drafting. diff --git a/.claude/skills/consultation/references/notion_ingestion.md b/.claude/skills/consultation/references/notion_ingestion.md index 96e88aa..ae56c25 100644 --- a/.claude/skills/consultation/references/notion_ingestion.md +++ b/.claude/skills/consultation/references/notion_ingestion.md @@ -1,6 +1,6 @@ # Notion ingestion -Operational reference for consultation's "incoming context" flow when the operator arrives with existing material to ingest. Runs **before** the four-step interview. The cross-cutting operational contract (read patterns, HITL discipline, inheritance discipline) lives at `.claude/rules/knowledge-backend.md` — this file covers the consultation-specific behavior on top of that contract. +Operational reference for consultation's "incoming context" flow when the operator arrives with **Notion** material (or pasted content) to ingest; a directory of planning files on disk is the sibling flow in `references/frozen_corpus_ingestion.md`. Runs **before** the four-step interview. The cross-cutting operational contract (read patterns, HITL discipline, inheritance discipline) lives at `.claude/rules/knowledge-backend.md` — this file covers the consultation-specific behavior on top of that contract. ## When to run this diff --git a/.claude/skills/consultation/references/problem_brief_template.md b/.claude/skills/consultation/references/problem_brief_template.md index cf49ff9..de43823 100644 --- a/.claude/skills/consultation/references/problem_brief_template.md +++ b/.claude/skills/consultation/references/problem_brief_template.md @@ -24,6 +24,13 @@ re-asking: - **Notion scope** — `` (designated at consultation; scaffold may promote to project hub). +If consultation ingested a frozen corpus on disk (frozen_corpus_ingestion.md), +record the path and the citation convention so every later phase reads the +same source: + + - **Frozen corpus** — `` (read in full; never edited; cite as + `/ § `; defects go to `/-errata.md`). + Omit this section entirely if no pre-cascade content was ingested (greenfield-from-conversation).> @@ -77,6 +84,21 @@ constraint.> See brownfield_addendum.md for structure. Note if a codebase analysis is needed — flag it for blueprint, do not run it here.> +## 9. Handoff notes for later phases *(optional; one line per phase, addressed by name)* + + +- **blueprint:** +- **framing:** +- **rough-in:** > + --- *Approved: . Ready for scaffold.* diff --git a/.claude/skills/consultation/references/test_cases.md b/.claude/skills/consultation/references/test_cases.md index a0659a3..2f3fbea 100644 --- a/.claude/skills/consultation/references/test_cases.md +++ b/.claude/skills/consultation/references/test_cases.md @@ -59,6 +59,22 @@ For Claude.ai's qualitative test loop (no subagents, no benchmarking): a real hu - Skill skips problem discovery because the user "already knows what they want" - MVP scope is unbounded or matches the user's most ambitious description without trade-off discussion +## Test 4 — a frozen corpus on disk + +**Prompt** (the operator points at a directory of prior planning material): +> "Everything under `planning/` is what the last team wrote — decisions, research, a half-finished design. Use it as the starting material; I don't want it rewritten." + +**What success looks like:** +- The incoming-context step takes the directory branch (`references/frozen_corpus_ingestion.md`), not the Notion modes: every file is read in full, and the brief's `## Pre-cascade sources` names the corpus path with one bullet per file consulted and the passages quoted where wording is inherited +- Nothing under `planning/` is edited; a defect found while reading goes to a lazily created `planning/-errata.md` entry (dated, "amends; never edits"), and the brief cites the entry +- The brief's `## Handoff notes for later phases` addresses scaffold by name (land the corpus at the recorded path, register the enforcement set) and blueprint by name (which decisions may be promoted, with the `Promotes:` form) +- The four-step interview still runs, informed by the corpus, not replaced by it + +**What failure looks like:** +- The corpus is summarized from a sample rather than read in full, or its wording is paraphrased into the brief +- A file under `planning/` is corrected in place +- The handoff notes are omitted, leaving scaffold to rediscover the corpus + ## How to use these tests when revising the skill 1. Pick one or more tests to run diff --git a/.claude/skills/framing/SKILL.md b/.claude/skills/framing/SKILL.md index e2e968c..c261e69 100644 --- a/.claude/skills/framing/SKILL.md +++ b/.claude/skills/framing/SKILL.md @@ -59,7 +59,7 @@ If the project runs a contribution-intake lane, candidates filed under the works ## Producing the frame-NN.md file and committing -Write `docs/cbk/frame-NN.md` (path per the project's layout) from the approved draft and append a row to `docs/cbk/README.md` with the framing number, the workstream, the date and status `Active`. On a backend planning axis, create one F-sub-issue per milestone (and one meta-issue per pre-flight row) parented under the workstream's parent issue — the two-step create-then-link pattern, the atomic transition with the markdown commit, the rollback rules and the partial-failure protocol are in `references/planning-backend-commit.md`; the issue bodies come from the repo's `.github/ISSUE_TEMPLATE/cascade-framing.md` and `cascade-meta.md`, disk first. Where the project keeps a roadmap surface, add its rows in the same commit. If no commit path is available, hand the files to the operator as downloadable artifacts rather than leaving the frame in the conversation. +Write `docs/cbk/frame-NN.md` (path per the project's layout) from the approved draft and append to `docs/cbk/README.md` — which scaffold created — its row (`# | date | framing | frame-NN.md | Active`) and its phase note (the execution facts: issues created by number, departures from the default with the discipline that justified them, research provenance, the next runnable command) in the same commit. Create the file from the scaffold skill's `references/templates/cascade-events-index-template.md` only as a brownfield fallback, when the project predates scaffold's provisioning of it. On a backend planning axis, create one F-sub-issue per milestone (and one meta-issue per pre-flight row) parented under the workstream's parent issue — the two-step create-then-link pattern, the atomic transition with the markdown commit, the rollback rules and the partial-failure protocol are in `references/planning-backend-commit.md`; the issue bodies come from the repo's `.github/ISSUE_TEMPLATE/cascade-framing.md` and `cascade-meta.md`, disk first. Where the project keeps a roadmap surface, add its rows in the same commit. If no commit path is available, hand the files to the operator as downloadable artifacts rather than leaving the frame in the conversation. **HITL gate (final)**: present the file and the list of issues to be created; get explicit approval before any write. The gate wording is in `references/planning-backend-commit.md` § HITL gate update. @@ -72,7 +72,7 @@ Auto-checkable list that fires after the final gate, before declaring framing co - [ ] Workstream parent issue exists (github-issues and linear planning; n/a on in-repo-markdown) and matches the workstream slug - [ ] No prior F-issue exists for this milestone (idempotency) - [ ] Markdown commit and (on backend planning axes) F-issue creation atomic transition succeeded, or partial state surfaced cleanly -- [ ] `docs/cbk/README.md` updated with new entry + status `Active` +- [ ] `docs/cbk/README.md` appended with the frame's row (status `Active`) and its phase note, in the frame's commit - [ ] If the project runs a contribution-intake lane (cbk-conventions): no candidate it filed under this workstream remains un-reconciled — each was promoted to an F-issue or closed as superseded - [ ] The verification pass ran and its defects were fixed or consciously kept (recorded in the gate) diff --git a/.claude/skills/framing/references/inheritance.md b/.claude/skills/framing/references/inheritance.md index fc05027..c3673a9 100644 --- a/.claude/skills/framing/references/inheritance.md +++ b/.claude/skills/framing/references/inheritance.md @@ -14,7 +14,7 @@ Skipping any of these is the failure mode that minimum/light mode is most likely 1. docs/cbk/problem_brief.md (consultation output — required) 2. docs/cbk/scaffold.md (scaffold output — required) 3. docs/cbk/blueprint.md (blueprint output — required, especially § "Workstreams") -4. docs/cbk/README.md (the chronological index — required if it exists, otherwise skip) +4. docs/cbk/README.md (the chronological index — always present, scaffold creates it; its absence is a brownfield signal to create it from scaffold's template, never a reason to skip) 5. docs/cbk/frame-NN.md (every prior framing — required if any exist) 6. docs/ARCHITECTURE.md (foundation doc — read for the Decisions Log) 7. docs/STANDARDS.md (foundation doc — read for the testing philosophy, PR review checklist, AND § Unenforced invariants) diff --git a/.claude/skills/framing/references/planning-backend-commit.md b/.claude/skills/framing/references/planning-backend-commit.md index b056495..46a001d 100644 --- a/.claude/skills/framing/references/planning-backend-commit.md +++ b/.claude/skills/framing/references/planning-backend-commit.md @@ -41,8 +41,16 @@ Framing constructs each sub-issue's body from one of two templates depending on Section heading names from both templates are preserved verbatim — do not rename, reorder, or omit sections that the templates provide. Adding additional sections after the template-provided ones is permitted if the capability or meta-issue has content that doesn't fit the standard sections. +### The roadmap row (github-issues and in-repo-markdown axes) + +Where `docs/cbk/ROADMAP.md` exists, the markdown half of the transition also appends one row per milestone to § The sequence (status *framed*, `Where` linking the `F` issue) and updates `## Now` — in the **same commit** as `frame-NN.md` and the index row, never a separate one. The roadmap is a status surface (`cbk-conventions.md` § Mutation discipline); a row that is wrong is fixed in place. + ## Atomic transition pattern (two-step per sub-issue) +### The `gh` shape (github-issues) + +`gh` is the default interface; the five-command shape — create, read the id, link under the parent, verify once at the end, create-then-edit for placeholders — and its two rough edges live in one home, the rough-in skill's `references/planning-backend-commit.md` § The `gh` shape. Framing's delta: the link target is the workstream parent, and the create-then-edit pass resolves `F<#>` placeholders and meta-issue cross-references. + Framing's planning-backend commit uses GitHub's two-step sub-issue creation: 1. Create the sub-issue via `issue_write` (returns issue number + id) 2. Link it to the parent via `sub_issue_write` with method `add` and the parent's issue_number + the new sub-issue's id diff --git a/.claude/skills/framing/references/planning-backend-matrix.md b/.claude/skills/framing/references/planning-backend-matrix.md index 15951cf..b50d5e0 100644 --- a/.claude/skills/framing/references/planning-backend-matrix.md +++ b/.claude/skills/framing/references/planning-backend-matrix.md @@ -26,6 +26,8 @@ For project-specific overrides (workstream slugs, label schemes, Linear team nam ## `github-issues` planning behavior +**Interface**: `gh` by default — the five-command shape lives in the rough-in skill's `references/planning-backend-commit.md` § The `gh` shape, this phase's delta in its own `references/planning-backend-commit.md`; a git-host MCP is one connection among several, and the phase reads and writes the working tree by default. + The most common case. When scaffold picked `github-issues`, framing reads inputs from the GitHub repo via GitHub MCP, produces `frame-NN.md` + `docs/cbk/README.md` index update, and commits them back via GitHub MCP. No Linear, no cross-tool integration. The structural fact that shapes everything: **GitHub's three-level planning hierarchy (parent Issue → sub-issue → sub-sub-issue) maps onto framing's output without needing a fourth level**. Framing's output IS the project-level content — it lives as markdown in `docs/cbk/` plus framing sub-issues parented under the workstream's parent Issue (already created by blueprint). diff --git a/.claude/skills/framing/references/research-phase.md b/.claude/skills/framing/references/research-phase.md index 4e664d1..4912b69 100644 --- a/.claude/skills/framing/references/research-phase.md +++ b/.claude/skills/framing/references/research-phase.md @@ -74,6 +74,10 @@ At non-trivial depth, research is a parallel activity, not a single-threaded rea When research asserts a codebase fact into a framing artifact — "helper X already exists," "there is no Y, a milestone must create one" — the claim must be **verified with a search rooted at the repository root covering every source tree** (all packages, notebook/script directories, test trees, docs), never a single-package grep: helpers routinely live where their *consumers* live, not where the milestone's package does. The cost asymmetry drives the discipline: a stale positive anchor is cosmetic, but a wrong *absence* claim ships an instruction to duplicate machinery that already exists. When research or drafting is fanned out to parallel agents, embed this rule verbatim in each agent's prompt, have any verifier/harmonizer pass preferentially spot-check the **negative** claims, and have drafts **cite the run's existing grounding output** for facts it already covers instead of re-deriving them from fresh narrow greps. (The rough-in skill's `references/research-phase.md` carries the full three-rule statement; the discipline is identical at both phases.) +## The verdict-first committed corpus + +The rough-in skill's `references/research-phase.md` § The verdict-first committed corpus carries the full statement — three verdicts with unverifiable kept and labelled, usable-versus-raw counts, the gaps block, the attempted-URL log, reproduction over interface over prose; the discipline is identical at both phases and is not restated here. + ## What framing does NOT research Some things look like they belong in framing's research phase but actually belong elsewhere in the cascade. **Cross-reference `backends.md` for the interface contract between phases — framing operates at the project level, blueprint operates at the workspace level, and the boundary matters.** diff --git a/.claude/skills/framing/references/templates/frame-output-template.md b/.claude/skills/framing/references/templates/frame-output-template.md index cd0bfc5..6e6dfdc 100644 --- a/.claude/skills/framing/references/templates/frame-output-template.md +++ b/.claude/skills/framing/references/templates/frame-output-template.md @@ -45,6 +45,14 @@ spike solutions, YAGNI) when they applied to per-milestone shape decisions.] ## Resolved during this framing +[An `/intake` candidate absorbed into a milestone is closed as **completed** — its +work lives on in the F-issue — never as cancelled. Findings this framing's +research produced are marked here as **the grounding corpus** later phases cite +before re-deriving; a finding backed by a single probe is flagged `single-probe`. +A **prior frame's recorded verdict** (a spike's outcome, a retired milestone's +reason) binds this frame; re-opening one is an entry here with the new evidence, +never a silent departure.] + [Required section. Lists every gate, open question, or deferred decision identified at framing entry that was closed during this framing event itself (as opposed to being deferred to rough-in or a future framing). Common @@ -78,7 +86,13 @@ distinct from Open questions (deferred forward) and Deferred meta-issues - [Explicit list of what this project covers] ### Out of scope -- **** — [which sibling project owns this, or "deferred to future framing"] +- **** — [reason] — [which sibling project owns this, or the condition that reopens it] + +[**Hard edges** are surfaces this frame never touches; **soft edges** are +surfaces it consumes but does not own (a consumes-edge names the owning frame +under § Boundary agreements). Every out-of-scope entry carries its reason and +either an owner or a reopen condition — an entry with neither is a deferral +nobody will find.] ### Boundary agreements with prior framings - **frame-**: "frame-M provides . This project consumes it @@ -87,6 +101,11 @@ distinct from Open questions (deferred forward) and Deferred meta-issues ## Interface Commitments +[Numbered **continuously across the workstream's frames** — a second frame +continues at the next unused number, never restarts — and **amendable only by a +dated note** appended under the commitment (`Amended : …`), never by +editing its text.] + [The most valuable single output of framing. Enables future framings to know what they can build against. Make commitments explicit and milestone-dated. @@ -153,9 +172,12 @@ frames** (a second frame for this workstream starts at the next unused F); - [F1.AC3] [Demonstrable-capability criterion] **Rough issues** ( total, user-managed): -1. — [one-sentence intent] -2. — [one-sentence intent] -[Continue per issue] +1. — [one-sentence intent] — satisfies [F1.AC] +2. — [one-sentence intent] — satisfies [F1.AC] +[Continue per issue; every rough issue names the criterion it satisfies, so +coverage is visible before rough-in. **Re-sequencing is pre-authorised**: +rough-in may reorder the issues inside a milestone without a re-frame; reordering +milestones themselves is pre-authorised only where § Milestones says so.] **Issue notes**: [Anything rough-in needs to know about these issues — Claude-Code-implementable vs. user-managed flags, dependencies between issues, "done" signals at the milestone level.] @@ -187,6 +209,9 @@ After M1 you can [X]. After M2 the system can [Y]. By M, the project delivers - [Question 1] — revisit during rough-in *(or: "revisit at frame-")* - [Question 2] — revisit when [condition] +**Deliberate non-decisions** — decided *not* to decide, with the trigger that forces it: +- [Non-decision] — decide when [trigger] + ## Pre-flight checks [Required section. Always present, even if empty. @@ -205,9 +230,18 @@ sub-issues, but distinguished by the `meta` label). On in-repo-markdown planning no backend issue exists — the Issue # column holds "n/a" and this table row is itself the record.] -| Issue # | Subject | Depends on | Blocks | Type | -|---|---|---|---|---| -| # | | | | gate \| decision \| infrastructure | +| Issue # | Subject | Depends on | Blocks | Type | Status | +|---|---|---|---|---|---| +| # | | | | gate \| decision \| infrastructure | deferred meta \| checked at framing: | + +[The Status column separates a **deferred meta** (an issue rough-in must find +resolved) from a **framing-time check** (verified during this framing, result +recorded, no issue needed). A meta-issue's body names **what does not land in +it** — the adjacent work that stays with its milestone. **Charters have two blast +radii**: a workstream-wide charter (a convention every milestone obeys) stays a +pre-flight meta; a milestone-wide one becomes that milestone's first R-issue. +**Orphaned obligations** — work no milestone and no meta owns — are placed here +or in a milestone and the placement recorded; an obligation left in prose is lost.] [**Type values**: - **gate**: blocks a transition between milestones. Rough-in must verify it's diff --git a/.claude/skills/framing/references/templates/milestone-template.md b/.claude/skills/framing/references/templates/milestone-template.md index dccf4fe..3663d3c 100644 --- a/.claude/skills/framing/references/templates/milestone-template.md +++ b/.claude/skills/framing/references/templates/milestone-template.md @@ -52,6 +52,8 @@ A time-boxed research milestone aimed at reducing uncertainty before committing **Fails when**: overused as a crutch that delays delivery, not properly time-boxed, or when the question isn't clearly defined upfront. +**Four outcomes**, named in the frame before the spike runs: *confirmed* (the question answered as hoped), *refuted* (answered the other way — the frame's dependent milestones re-plan), *inconclusive, extended* (a bounded second time-box with a stated reason), and **retired as unsatisfiable** (the question cannot be answered with the means available — recorded as a verdict that binds later frames, never left open). A spike with no fourth outcome named is one that will be extended forever. + **Example**: M0 (or M1) of a project where the choice of vector database determines the rest of the architecture — "After this, we have benchmarks comparing pgvector, Qdrant, and Pinecone on our actual workload and a recommendation with rationale. No production code from this milestone — just a `docs/spikes/vector-db.md` and a decision." ### 5. Infrastructure (project-level) diff --git a/.claude/skills/framing/references/test_cases.md b/.claude/skills/framing/references/test_cases.md index 740b531..4ad151c 100644 --- a/.claude/skills/framing/references/test_cases.md +++ b/.claude/skills/framing/references/test_cases.md @@ -9,7 +9,7 @@ Realistic prompts for verifying the framing skill works correctly after any revi **What framing should do**: -1. **Step 1 — Inheritance**: read `docs/cbk/problem_brief.md`, `docs/cbk/scaffold.md`, `docs/cbk/blueprint.md` in full via GitHub MCP. Check `docs/cbk/` — empty. Check `docs/cbk/README.md` — doesn't exist yet (this is frame-01). Present verbatim inheritance summary quoting the brief's no-gos, scaffold's quality bar, blueprint's regex pack workstream row, and blueprint's methodology section. HITL gate: user confirms summary. +1. **Step 1 — Inheritance**: read `docs/cbk/problem_brief.md`, `docs/cbk/scaffold.md`, `docs/cbk/blueprint.md` in full via GitHub MCP. Check `docs/cbk/` — empty. Check `docs/cbk/README.md` — exists with the consultation, scaffold and blueprint rows (scaffold created it; this is frame-01). Present verbatim inheritance summary quoting the brief's no-gos, scaffold's quality bar, blueprint's regex pack workstream row, and blueprint's methodology section. HITL gate: user confirms summary. 2. **Step 2 — Project selection**: confirm "regex pack" against blueprint's workstreams table. Identify framing number as `frame-01`. HITL gate: user confirms. @@ -19,15 +19,15 @@ Realistic prompts for verifying the framing skill works correctly after any revi 5. **Step 5 — Milestones**: 3-5 milestones, default vertical slicing shape. M1 delivers one end-to-end regex lesson. Each milestone has capability verb, depends-on, rough issues, acceptance signal. HITL gate: user reviews narrative arc. -6. **Commit** `frame-01.md` + create `docs/cbk/README.md` with first index row via GitHub MCP. +6. **Commit** `frame-01.md` + append the frame-01 row and phase note to `docs/cbk/README.md` via GitHub MCP. **Success criteria**: - Inheritance summary quotes verbatim (not paraphrased) from at least problem_brief, scaffold, and blueprint - Research depth proposal happens explicitly as a user signal, not a silent default - Frame file is named `frame-01.md` (not `frame-regex.md` or `frame-001.md`) - Interface Commitments table is present even though the consumer doesn't exist yet +- `docs/cbk/README.md` was appended (row + phase note), not created — scaffold created it; and the roadmap rows for the new milestones landed in the frame's commit - Every milestone has a verb-phrase capability statement -- `README.md` index is created alongside `frame-01.md` in the same commit - Exactly three HITL gates (standard mode) - Full mode offered no MCP or plugin research track (framing does not research tooling); the frame's Pre-flight checks table carries the exact empty-state string `No deferred meta-issues from this framing` when empty. @@ -35,7 +35,7 @@ Realistic prompts for verifying the framing skill works correctly after any revi - Framing produces milestones without reading all three inheritance docs in full - Research happens without asking the user about depth - Interface Commitments section absent or written as prose instead of a table -- `README.md` index not created or created separately +- `README.md` index row or phase note missing, or appended in a separate commit - Framing all three workstreams instead of just the regex pack ## Test case 2 — Subsequent framing with "Builds on" inheritance diff --git a/.claude/skills/rough-in/references/contract.md b/.claude/skills/rough-in/references/contract.md index 6fe1630..2a25a22 100644 --- a/.claude/skills/rough-in/references/contract.md +++ b/.claude/skills/rough-in/references/contract.md @@ -75,7 +75,10 @@ open-questions list is a different list with its own numbering, cited by its own parsed value present) and flag the rest as operational, in an explicit list. - **Done signal**: one command or observation; an operational signal flagged as such. For a spike-shaped R-issue — one whose criteria are the operator's runs — the signal names the run-and-revise loop (the rows - filled from the runs, with the harness fixed on the same branch until they are), not the draft PR. + filled from the runs, with the harness fixed on the same branch until they are) **and** the record's completeness + check (the by-hand checker that confirms every planned row is present, run by hand and never in the CI gate), + not the draft PR. A measurement-shaped spec commits its verdict rule before any number exists; the + template's measurement variant is the shape. - **Dependencies**: identifiers only — `#N` for existing issues, `R<#>` placeholders for sibling R-issues, which the commit resolves — with one line on what each is *for*; or `None`. Never name an open issue in prose here unless it is a dependency: the executor refuses to proceed on any open issue it finds listed. diff --git a/.claude/skills/rough-in/references/finish-command.md b/.claude/skills/rough-in/references/finish-command.md index 5231dee..d3a4a5f 100644 --- a/.claude/skills/rough-in/references/finish-command.md +++ b/.claude/skills/rough-in/references/finish-command.md @@ -115,8 +115,8 @@ This command is contract-first. On 2026-09-03 an A/B gave two workhorse-tier exe 5. **Atomic commits** in Conventional Commits form with the workstream's scope: the red tests, the implementation, each simplify Apply, each review Apply — one focused commit each, never amended across a logical boundary, never squashed on the branch. The squash happens at merge on the base branch. No code or test commit carries the CI-skip marker (`cbk-conventions.md` § `[skip ci]` rule). 6. **The gate, green.** The project's `check` task passes before simplify and review run, and again after every applied finding. Unrelated red is surfaced, not muted — never skipped, expected-failed or commented out to pass. 7. **The review floor, actually run, with the bounded sweep beside it, then triage — once.** First **`/simplify`**, as a skill invocation; triage its findings by the four-class rubric and land its Apply items one commit each with `check` re-run after each. Then, **concurrently**: **`pr-review-toolkit:review-pr`** with no arguments, as a skill invocation, and — where a multi-agent orchestration surface exists — **the review workflow** (`.claude/workflows/review-sweep.js`), sized to the diff and never to the session's effort setting, with the pre-filtered changed-path list passed as `files`. The project-local reviewers ride in it from the roster the workflow reads at runtime (`.claude/rules/pr-review.md` § Project-local agents); beyond them you name the finders this diff needs — none on a small single-surface change the two skills already cover, several on a cross-layer one — and the workflow logs its planned agent count before it finds anything and returns its own gate line. Without an orchestration surface, dispatch the project-local reviewers directly and record `skipped — no orchestration surface` on the sweep's line. The sweep supplements the two skills and never substitutes for either. Triage waits for both to return and stays with you (`pr-review.md` § Triage rubric; the reference half's calibration read at triage): Apply and Apply-with-care land as their own commits with `check` re-run; Surface, Defer and Reject are recorded, never applied. A skill that is uninstalled or errors is a stop-and-surface. A skill that runs without its agent fan-out because the Agent tool is unavailable in your context counts as invoked, and its dropped dimensions are recorded on its `## Review gate` line — never described as equivalent. A failed review agent is dropped coverage, not zero findings. **Describing a review is not running one**: a claimed invocation that did not happen fails this contract outright. The floor runs when the known work is complete and is not run again on a later delta — the reviewer round on the flip covers that, and `/pr-respond` answers it. -8. **A draft PR** on the pushed branch: title in Conventional Commits with the workstream's scope; body with the planning axis's close marker (`Closes #$1` on github-issues, `Closes $1` on linear — in the body, not only the title; `cbk-conventions.md` § Closes-keyword conventions); a summary of what changed and why, with the ADRs, frame milestone and rules it relies on; a **`## Review gate`** block, written before `## Triage`, in the shape `.claude/rules/pr-review.md` § The floor states — one line each for `/simplify`, `pr-review-toolkit:review-pr` and the sweep, each stating run-or-not with counts and dropped coverage, a waived skill on its line with the break-glass reason, the sweep's line transcribed from the record the workflow returns; a **`## Triage`** block listing every finding under its class — Apply and Apply-with-care as `SHA: fix`, Surface and Defer with the agent's verbatim rationale, Reject with a one-line dismissal; and any review block or label the project's conventions require when the diff touches a surface they guard. A docs commit that would be HEAD at the flip carries no CI-skip marker (`cbk-conventions.md` § Auto-review trap). The body's close markers name every issue the run resolves — a pre-flight meta-issue the work settles as well as `$1`. The PR is never flipped to ready and never merged by this run; both are the operator's. -9. **A hand-off** that stands on its own: the PR URL; the triage counts in one line; the three `## Review gate` lines verbatim — an invocation the transcript does not show was not made; the Surface and Defer entries verbatim; every operational criterion still open with what the operator must do; the one next action; an offer to post roll-forward context wherever it is relevant — the next issue, a sibling, a meta-issue, a new follow-up — post-merge unless it blocks another issue's start; an offer to sweep the open sibling issues for absence claims this run made false. Deferred conflicts with an ADR lead the hand-off. A learning that should outlive the PR is offered to the knowledge backend only where one is configured, HITL-gated and default-skip (`.claude/rules/knowledge-backend.md` § When to write); otherwise it goes into a rule file or an ADR. +8. **A draft PR** on the pushed branch: title in Conventional Commits with the workstream's scope; body with the planning axis's close marker (`Closes #$1` on github-issues, `Closes $1` on linear — in the body, not only the title; `cbk-conventions.md` § Closes-keyword conventions); a summary of what changed and why, with the ADRs, frame milestone and rules it relies on; a **`## Review gate`** block, written before `## Triage`, in the shape `.claude/rules/pr-review.md` § The floor states — one line each for `/simplify`, `pr-review-toolkit:review-pr` and the sweep, each stating run-or-not with counts and dropped coverage, a waived skill on its line with the break-glass reason, the sweep's line transcribed from the record the workflow returns; a **`## Triage`** block listing every finding under its class — Apply and Apply-with-care as `SHA: fix`, Surface and Defer with the agent's verbatim rationale, Reject with a one-line dismissal — **the body is the audit surface for the PR's whole life**: each later feedback round appends its own `## Triage — round N` block there through `/pr-respond`, never a comment alone, so a reader of the merged PR sees every finding and its disposition in one place; and any review block or label the project's conventions require when the diff touches a surface they guard. **The roadmap row rides this branch, as its last commit before the PR** — where the project keeps `docs/cbk/ROADMAP.md` and the spec names the flip: after the floor (item 7) and before `gh pr create`, the row for this step and the `## Now` paragraph are updated to cite this issue and this PR — *done* when this is the milestone's last R-issue, the progress note otherwise — as one `docs(cascade):` commit with no CI-skip marker (it is HEAD at the flip, and the marker would suppress the auto-review and the required checks — `cbk-conventions.md` § Auto-review trap). The PR number is the next in the repository's shared issue-and-PR sequence (GitHub's REST API considers every pull request an issue and its issue endpoints return both — `https://docs.github.com/en/rest/issues/issues`, read 2026-09-06 — so the newest number across both is the last item of an issues listing) (`gh api 'repos/{owner}/{repo}/issues?state=all&sort=created&direction=desc&per_page=1'` plus one) and is checked against the number `gh pr create` returns; a mismatch is one correcting commit. Never a separate PR: a one-row flip with its own PR costs a CI run for nothing, and a flip committed after `gh pr create` costs the same run on the open PR. The row reads *done* on this branch while the PR is open; it reaches the base branch only through this PR, which is what makes it true. Where the spec did not name the flip, the post-merge checklist (item 9) flips it. Any other docs commit that would be HEAD at the flip carries no CI-skip marker either. The body's close markers name every issue the run resolves — a pre-flight meta-issue the work settles as well as `$1`. The PR is never flipped to ready and never merged by this run; both are the operator's. +9. **A hand-off** that stands on its own: the PR URL; the triage counts in one line; the three `## Review gate` lines verbatim — an invocation the transcript does not show was not made; the Surface and Defer entries verbatim; every operational criterion still open with what the operator must do; the one next action; an offer to post roll-forward context wherever it is relevant — the next issue, a sibling, a meta-issue, a new follow-up — post-merge unless it blocks another issue's start; and the **backward sweep** beside it — the open siblings under the same parent carrying `cascade-depth:roughed-in`, read in one call and capped by the milestone's R-count; their *absence* claims only ("no X exists yet", "Y is not wired"), each verified by running the body's own command rather than by recollection; a claim this run's landing falsified is separated from one that was false when written; the default is to report nothing — a claim that still holds is not mentioned — and the repair is rough-in's in-place body refresh, never an edit from this run; an empty sweep is a one-line pass, not an omission. Deferred conflicts with an ADR lead the hand-off. The hand-off ends with the **post-merge checklist** the operator runs after the merge: sync and prune (`git switch main && git pull --ff-only && git fetch --prune`); post the roll-forwards the hand-off drafted; close the meta-issues the close marker did not; append the frame's `## Rough-in events` row when the PR is a milestone's capstone; flip the roadmap row if this run did not (item 8); and the next runnable command, read off the roadmap. A research milestone's capstone is the verdict artifact, owned by name in the checklist. A learning that should outlive the PR is offered to the knowledge backend only where one is configured, HITL-gated and default-skip (`.claude/rules/knowledge-backend.md` § When to write); otherwise it goes into a rule file or an ADR. ## The tests every finish must pass @@ -128,9 +128,11 @@ This command is contract-first. On 2026-09-03 an A/B gave two workhorse-tier exe - **Claims match code.** No comment, commit message or PR-body sentence asserts something the diff contradicts. - **Honesty of the gate.** The three `## Review gate` lines are literally true — an invocation the transcript does not show was not made — and every Apply commit named in `## Triage` exists on the branch with the stated SHA, one finding per commit. - **Green.** The project's `check` task passes at the branch head. +- **Backward sweep.** The hand-off carries the sweep's line — the sibling count read in one call, each absence claim checked by the body's own command, the falsified separated from the false-when-written, or the one-line pass — never an omitted line. +- **Measurement issues.** For an issue whose criteria are measurements, the **verdict rule**'s commit precedes the first measurement commit on the branch (`git log --reverse`), the capstone shows the findings table unchanged against the base branch, and every departure from the rule is weighed in the verdict rather than folded into it. - **No improvisation past the spec.** A spec defect — unverifiable criterion, ADR conflict, missing dependency, drifted doc — is surfaced and the run stops; it is not patched in flight. - **Partial failure surfaces.** A failed external call — `git`, `gh`, an MCP call, the check task, a skill — stops the run, and the hand-off states what reached the remote and what did not, with the choices and their consequences. No blind retry. ## What this command does not do -It does not modify the issue body, re-rough-in, create dependent issues, bypass dependencies, modify cascade artifacts or ADRs, make workstream- or framing-level decisions, skip either skill of the floor (the break-glass marker waives exactly the toolkit half, recorded), run the floor twice, flip the PR to ready, merge, or respond to auto-review comments (that is `/pr-respond`). It does not write to the knowledge backend as a side effect of anything above. +It does not modify the issue body, re-rough-in, create dependent issues, bypass dependencies, modify cascade artifacts or ADRs — the **status surfaces** are carved out of that ban: `docs/cbk/ROADMAP.md`'s row and a frame's `## Rough-in events` table are not cascade events, and item 8 says who flips them (this run, on its own branch, when the spec names the flip; the post-merge checklist otherwise) — make workstream- or framing-level decisions, skip either skill of the floor (the break-glass marker waives exactly the toolkit half, recorded), run the floor twice, flip the PR to ready, merge, or respond to auto-review comments (that is `/pr-respond`). It does not write to the knowledge backend as a side effect of anything above. diff --git a/.claude/skills/rough-in/references/finish-procedure.md b/.claude/skills/rough-in/references/finish-procedure.md index 4b2d735..ce096d0 100644 --- a/.claude/skills/rough-in/references/finish-procedure.md +++ b/.claude/skills/rough-in/references/finish-procedure.md @@ -194,6 +194,8 @@ The principle for Steps 8–9: **the executor handles findings the codebase need - **Defer (N)** — ` — conflicts with `. - **Reject (N)** — one-line dismissal. + **The roadmap row, before `gh pr create`** — where the project keeps `docs/cbk/ROADMAP.md` and the spec names the flip: update the row and `## Now` on this branch as the last commit (`docs(cascade):`, no CI-skip marker — it is HEAD at the flip), with the PR number predicted from the shared issue-and-PR sequence (GitHub's REST API considers every pull request an issue and its issue endpoints return both — `https://docs.github.com/en/rest/issues/issues`, read 2026-09-06 — so the newest number across both is the last item of an issues listing) (`gh api 'repos/{owner}/{repo}/issues?state=all&sort=created&direction=desc&per_page=1'` plus one) and checked against the number `gh pr create` returns; one correcting commit on a mismatch; never a separate PR (the contract, item 8). + If `gh pr create --draft` fails (auth, missing scope, draft PRs disabled, rate limit, PR already exists for this branch), stop and surface the failure with per-step status. ## Step 11: Hand off @@ -210,8 +212,12 @@ If any finding was classified **Defer** because it would conflict with an ADR or **Roll-forward offer** (HITL). If the run surfaced deferred context that a *later* unit of work will need — TODOs, contract facts the next issue depends on, intentional omissions, or a follow-up worth its own issue — **offer** to post a roll-forward comment **wherever it's relevant**: the next issue, a sibling, a downstream milestone issue, a meta-issue, or a brand-new follow-up. Do **not** hardwire it to "the next issue." Default to posting **post-merge** unless the deferred context blocks another issue's start. If nothing was deferred, skip this silently. +**Backward sweep** (HITL offer, beside the roll-forward). What this run landed can make a sibling spec's *absence* claim false — "no adapter exists yet", "the table is not wired". The candidate set is closed: the open siblings under the same parent issue carrying `cascade-depth:roughed-in`, read in **one** call (`gh api repos/{owner}/{repo}/issues/{parent}/sub_issues` on github-issues, the parent's children on linear) and capped by the milestone's R-count — never the whole backlog. Check absence claims only, and verify each by **running the body's own command** (its grep, its test, its probe), not by recalling what this run did. Separate the two kinds: a claim this landing falsified (report it, with the commit) from a claim that was false when written (report it as a spec defect). **Default to not reporting** — a claim that still holds is not mentioned. The repair is rough-in's in-place body refresh, offered to the operator; this run never edits an issue body. An empty sweep is a one-line pass — `backward sweep: N siblings, no absence claim falsified` — never an omitted line. + **Optional learning-runbook write to Notion** (only when knowledge backend = `notion` is configured for this repo, per `.cascade/backends.toml`): if this execution surfaced a learning that would be a useful durable cross-project runbook entry (e.g., a non-obvious gotcha with the stack, a recipe for a recurring task spanning repos, a corrected understanding of an external system's behavior), prompt the operator: *"Want to promote this learning to a Notion runbook page under the Engineering Wiki? Defaults to SKIP."* Defaults to **SKIP**. Per `.claude/rules/knowledge-backend.md` § "When to write" — this is the rare opt-in path; HITL-gated; never default. If the operator opts in, announce the planned write (title, parent, body preview) before committing. This step is gated to genuinely cross-project learnings only — local-to-the-PR observations stay in the PR body and the hand-off summary, not in Notion. +**Post-merge checklist** (the hand-off ends with it; the operator runs it after the merge): `git switch main && git pull --ff-only && git fetch --prune`; post the drafted roll-forwards; close the meta-issues the close marker did not; append the frame's `## Rough-in events` row when this PR was the milestone's capstone; flip the roadmap row if Step 10 did not; the next runnable command, read off the roadmap. A research milestone's capstone is the verdict artifact — name it and its owner. + Do not call `gh pr ready` and do not merge — those remain the user's calls. The user's draft-review pass is where Surface findings get decided; they can ask you to apply any of them in the PR-feedback-loop turn, or wave them through. ## What `/finish` does NOT do @@ -220,7 +226,7 @@ Do not call `gh pr ready` and do not merge — those remain the user's calls. Th - **Does not handle re-rough-in.** Surface the need and return to chat for the rough-in skill. - **Does not auto-create dependent issues.** If the work reveals a missing sub-sub-issue, surface and return to chat for rough-in. - **Does not bypass dependencies.** Refuse to proceed when dependencies are unmet. -- **Does not modify cascade artifacts** (`docs/cbk/blueprint.md`, `docs/cbk/frame-NN.md`, etc.). Those are produced by chat-skill cascade phases. +- **Does not modify cascade artifacts** (`docs/cbk/blueprint.md`, `docs/cbk/frame-NN.md`, etc.). Those are produced by chat-skill cascade phases. *The status surfaces are carved out: `docs/cbk/ROADMAP.md`'s row and a frame's `## Rough-in events` table are not cascade events; Step 10 flips the row on this branch when the spec names it, the post-merge checklist otherwise (the contract wins where the two differ).* - **Does not modify ADRs.** ADRs are immutable once accepted; revisions happen via new ADRs that supersede the old (chat-skill territory, not `/finish`). - **Does not make workstream-level or framing-level decisions.** Those belong to upstream cascade phases (blueprint, framing, rough-in). - **Does not skip `/simplify` or `pr-review-toolkit:review-pr`.** The simplify and review passes both run before the PR opens; the simplify pass is non-negotiable per `docs/STANDARDS.md` § Step 4, and the review-toolkit findings are auto-triaged with the four-class rubric (Apply for correctness/validity/defensive; Surface for taste/style; Defer/Reject for conflicts and agent errors). The one exception is an explicit break-glass marker (Step 1), which waives exactly the `pr-review-toolkit:review-pr` half and is recorded on that skill's `## Review gate` line. The orchestrated sweep supplements the two skills and never substitutes for either. diff --git a/.claude/skills/rough-in/references/planning-backend-commit.md b/.claude/skills/rough-in/references/planning-backend-commit.md index 6b92b25..d2caf5d 100644 --- a/.claude/skills/rough-in/references/planning-backend-commit.md +++ b/.claude/skills/rough-in/references/planning-backend-commit.md @@ -39,6 +39,22 @@ Rough-in constructs each sub-sub-issue's body from the **`cascade-rough-in.md`** ## Atomic transition pattern (two-step per sub-sub-issue) +### The `gh` shape (github-issues) + +`gh` is the default interface for this axis; a git-host MCP is one connection among several, used when it is what the session has. Phase skills read and write the working tree by default and push through `gh` or `git`. This section is the one home for the shape; blueprint's and framing's `planning-backend-commit.md` state only their deltas and point here. The five commands, run as **one script** per transition so the placeholders resolve in the same pass — except the verify line, which runs **once after the last create of the run**, never per issue: + +```bash +set -euo pipefail # stop at the first failure (§ Partial failure recovery); never run on an empty value +url=$(gh issue create --title "$title" --body-file "$body" --label "$labels" --repo "$repo") # returns the URL; the number is its last path segment +n=${url##*/}; [ -n "$n" ] || { echo "issue create returned no URL" >&2; exit 1; } +id=$(gh api "repos/$repo/issues/$n" --jq .id); [ -n "$id" ] || { echo "no id for #$n" >&2; exit 1; } # the numeric id the link needs (not the number): `sub_issue_id` — "The id of the sub-issue" (`https://docs.github.com/en/rest/issues/sub-issues`, read 2026-09-06) +gh api -X POST "repos/$repo/issues/$parent/sub_issues" -F sub_issue_id="$id" >/dev/null # link under the parent +gh api "repos/$repo/issues/$parent/sub_issues" --jq '[.[].number]' # verify — once, after the last create +gh issue edit "$n" --body-file "$body_resolved" # create-then-edit: resolve the R<#> / F<#> placeholders once the numbers exist +``` + +Two rough edges recorded from an exercised run (a dated field observation, 2026-09-06; re-verify against the sub-issues API page above): the link call returns the **parent** issue object, so a status line built from its `.number` prints "#N under #N" — read the child's number from the create step, never from the link response; and a body carrying a literal placeholder cannot be final at creation, because an issue's own number is not known until it exists — the create-then-edit pass is the mechanism, not a workaround. + Rough-in's planning-backend commit uses GitHub's two-step sub-issue creation, same as framing's pattern: 1. Create the sub-sub-issue via `issue_write` (returns issue number + id) @@ -65,9 +81,9 @@ GitHub Issues cannot be deleted via the standard API — only closed. The cascad **Why the partial-supersede on re-rough-in**: if R1, R2, and R3 of the prior rough-in were already merged before R4 surfaced as needing rework, R1-R3 represent real work that's already in the codebase. Closing them as superseded would create a misleading historical record. Only R4 (the one that triggered the rework) and R5+ (the queued ones not yet started) get superseded; R1-R3 keep their `state_reason: completed` close. -### The framing.md event entry shape +### The README.md event entry shape -The entry appended to `docs/cbk/README.md` for each rough-in run records the event in the cascade's append-only log. The entry has a required shape: +The entry appended to `docs/cbk/README.md` for each rough-in run records the event in the cascade's append-only log, in the shape every appender shares (the scaffold skill's `references/templates/cascade-events-index-template.md`): **a row** in `## Index` — `# | date | rough-in | frame-NN.md § Rough-in events (M<#>) | Completed` — **plus a phase note** carrying the fields below; the compression rationale and the departures go in the note, never in the row. The frame's own `## Rough-in events` table gets its row in the same commit. The note's required fields: - **Date and milestone identifier**: *"2026-04-14 — regex-pack:F1:M1"* - **R-number range created**: *"R1-R2 (2 sub-sub-issues)"* diff --git a/.claude/skills/rough-in/references/planning-backend-matrix.md b/.claude/skills/rough-in/references/planning-backend-matrix.md index e6152bb..b0f65b9 100644 --- a/.claude/skills/rough-in/references/planning-backend-matrix.md +++ b/.claude/skills/rough-in/references/planning-backend-matrix.md @@ -6,6 +6,8 @@ The cascade ships three planning axes defined in scaffold and documented in `ref ## `github-issues` planning axis (default) +**Interface**: `gh` by default (`references/planning-backend-commit.md` § The `gh` shape — five commands as one script, the parent-object return, the create-then-edit placeholder pattern); a git-host MCP is one connection among several. The phase reads and writes the working tree by default. + This is the planning axis rough-in is built to run cleanly against, with full atomic transition, partial failure recovery, and inherit-from-disk discipline. **Hierarchy**: rough-in creates **sub-sub-issues** parented under the framing capability sub-issue via GitHub's native sub-issue API. The full Issue tree per workstream is three deep: @@ -100,7 +102,7 @@ The default heuristic (3-4 → append, 5+ → new file) is rough-in's proposal; | `create_or_update_file` | github MCP | Append rough-in spec content to `frame-NN.md` (option a) or create `frame-NN-M<#>-rough-in.md` (option b) | | `create_or_update_file` | github MCP | Append the rough-in event entry to `docs/cbk/README.md` index | -**No external planning-backend operations.** The atomic transition collapses to a single half (just the markdown commits). There is no rollback to perform on the planning side because no external planning ops ran. Partial failure recovery applies to the markdown commits only — if the spec file commit succeeds but the framing.md index update fails, surface and ask the operator to verify state before retry. +**No external planning-backend operations.** The atomic transition collapses to a single half (just the markdown commits). There is no rollback to perform on the planning side because no external planning ops ran. Partial failure recovery applies to the markdown commits only — if the spec file commit succeeds but the README.md index update fails, surface and ask the operator to verify state before retry. **Body source**: in `in-repo-markdown` planning, rough-in still reads the `cascade-rough-in.md` template from `.github/ISSUE_TEMPLATE/` (because the templates may exist on disk even in repos without external planning — scaffold commits them regardless of the planning axis, since they're workspace infrastructure that serves both human-created Issues and the cascade's automated runs). The template's section structure is used inside the markdown spec content too, so the spec headings are consistent across planning axes. If the disk template is missing, fall back to the bundled copy. diff --git a/.claude/skills/rough-in/references/procedure.md b/.claude/skills/rough-in/references/procedure.md index 830ee7e..e65fa74 100644 --- a/.claude/skills/rough-in/references/procedure.md +++ b/.claude/skills/rough-in/references/procedure.md @@ -243,7 +243,7 @@ The deferred meta-issue for the automation recommender pass will revise the **te ## Step 6 — Planning-backend commit (atomic transition) -After all specs are drafted and approved, commit the sub-sub-issues to the planning backend atomically with the markdown event log update. Same discipline as blueprint and framing: capture every operation, execute planning ops first, commit markdown second, rollback on failure, handle partial failure by stopping and surfacing state to the user. +After all specs are drafted and approved, commit the sub-sub-issues to the planning backend atomically with the markdown event log update — and, where the project keeps `docs/cbk/ROADMAP.md`, the milestone's row flipped to *roughed-in* with the `R` issues linked, in the same commit. Same discipline as blueprint and framing: capture every operation, execute planning ops first, commit markdown second, rollback on failure, handle partial failure by stopping and surfacing state to the user. Detailed commit step, slug inheritance, profile-aware behavior, atomic transition pattern, partial failure recovery, and failure modes live in `references/planning-backend-commit.md`. diff --git a/.claude/skills/rough-in/references/research-phase.md b/.claude/skills/rough-in/references/research-phase.md index f995f63..733df1e 100644 --- a/.claude/skills/rough-in/references/research-phase.md +++ b/.claude/skills/rough-in/references/research-phase.md @@ -109,6 +109,12 @@ Research findings and the specs drafted from them routinely assert codebase fact These rules apply to any phase that asserts codebase facts into a spec or an implementation plan (framing research, rough-in research, single-issue enrichment investigations, and the executor's own research fan-outs) — the sub-track structure above governs *what* to research; this section governs *how any claim about the codebase earns its way into a spec*. +## The verdict-first committed corpus + +Research that a phase inherits is committed **verdict-first**: each finding carries one of three verdicts — **verified** (reproduced or read at the primary source), **refuted** (with the consequence that refutes it, not a counter-claim), or **unverifiable** (kept and labelled, never dropped — an unverifiable claim silently removed reappears as a confident one). Verification targets the *specifics* a downstream decision rests on (a version, a limit, a signature), not the summary. The corpus states its counts as **usable versus raw** ("14 usable of 22 raw"), carries a **gaps block** (what was looked for and not found) and an **attempted-URL log** (what was fetched, what failed), and ranks its checks: **reproduction** is the strongest, then the **interface** (a signature, a schema, a test) over prose. An interface beats a paragraph; a paragraph beats memory. + +**The spec-verification stage** runs after drafting and before the one-way commit, on three axes: **format** (every section present, every tag keyed), **grounding** (every claim in the spec traces to the corpus or a live check), **coverage** (every `[F<#>.AC]` owned by exactly one R-issue). One research agent is dispatched **on the executor's own contract** — it reads `.claude/commands/finish.md` and reports what the spec asks `/finish` to do that the contract forbids or leaves undefined. A design doc the spec cites carries its **grounding line** (which corpus, which verdict) and an **anticipated-wrong-fix note** (the fix a hurried implementer would reach for and why it is wrong). `## Out of scope` is split into **deferred** (decided against, with the owner) and **unverified** (could not be checked, with what would check it); a **no-regression criterion names every reason** a regression could occur, not "no regressions"; and **telemetry is verified at its sink** — the dashboard, the log store — never at the emitting call. + ## What rough-in does NOT research - **Stack decisions** (which language, framework, runtime, libraries) — those are blueprint's layer diff --git a/.claude/skills/rough-in/references/templates/rough-in-spec-template.md b/.claude/skills/rough-in/references/templates/rough-in-spec-template.md index 46bd84a..145f80a 100644 --- a/.claude/skills/rough-in/references/templates/rough-in-spec-template.md +++ b/.claude/skills/rough-in/references/templates/rough-in-spec-template.md @@ -178,6 +178,23 @@ test, command, or observation that proves the criterion. - [ ] _Specific outcome 2_ - [ ] _Specific outcome 3_ + + ## Test plan + ## Dependencies + +Closes # + +## Review gate + + + +- `/simplify` — +- `pr-review-toolkit:review-pr` — +- `review-sweep` — + +## Triage + + + +- **Apply (0)** — +- **Apply with care (0)** — +- **Surface (0)** — +- **Defer (0)** — +- **Reject (0)** — + +## Notes + + +``` + +## `.github/CODEOWNERS` + +``` +* @ +``` + +Solo default; ask before assuming a team. CODEOWNERS is a **routing declaration** — it enforces nothing on its own. Enforcement is a ruleset (or branch protection) with "Require review from Code Owners" (`https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners`, read 2026-09-06; the ruleset itself: `manual_steps.md` § Repository administration). + +## `.github/workflows/ci.yml` + +A stub only. Real CI is blueprint's job (`blueprint/references/templates/tooling.md`). + +```yaml +name: CI +on: + pull_request: + push: + branches: [main] + +jobs: + check: + # `name:` is the check-run context a ruleset will require. Set it before the job + # is promoted and never rename it afterwards — matching is by name, and a rename + # orphans the required context (cbk-conventions-reference.md § Required-checks + # trap, cause 3). + name: check + runs-on: ubuntu-latest + steps: + - run: echo "CI stub — blueprint wires the real gate" +``` + +**Two traps to respect the moment this job is promoted to a required check** (`cbk-conventions.md` § `[skip ci]` rule): + +1. **A required-check workflow carries no `paths:` filter.** A filtered workflow does not run on a PR that touches nothing matching — the required context never reports, and the PR is blocked on a check that will never arrive. Required checks are always-run by construction; any narrowing happens inside the job as a fast no-op exit. +2. **The check-run name is the immutable API.** Rename the *workflow* if you must, never the job. + +## `.github/dependabot.yml` + +`cbk-conventions-reference.md` § Dependency settle-window is prose; this file is the mechanism. Three invariants are encoded here and must survive edits: + +- **`cooldown` gates version updates only — security advisories still patch immediately.** A platform guarantee, not a convention ("The cooldown option is only available for version updates, not security updates" — `docs.github.com/en/code-security/dependabot/working-with-dependabot/dependabot-options-reference`, read 2026-09-06). Never widen a window to delay a security fix; that is not what the knob does. +- **`groups` is the anti-spam lever; `schedule.interval` is the volume lever.** Raising a floor does not reduce update volume — an update fires on a new release, not on the floor value (the options reference — `https://docs.github.com/en/code-security/dependabot/working-with-dependabot/dependabot-options-reference` § groups, § schedule.interval, § cooldown, read 2026-09-06: cooldown "allow[s] updates to be delayed for a configurable number of days" and "is only available for version updates, not security updates"). +- **Every ecosystem entry carries the floor — including the inactive stubs.** An entry without `cooldown` is a policy violation, not an oversight; a stub that is later uncommented must not be able to drop the floor by omission. + +```yaml +version: 2 +updates: + # ── CI actions ──────────────────────────────────────────────────────────── + # Workflow actions are dependencies too, and a tag ref (`uses: vendor/x@v4`) is + # mutable — "a tag can be moved or deleted"; a full-length commit SHA "is currently the only + # way to use an action as an immutable release" (docs.github.com, Actions security hardening + # § Pin actions to a full-length commit SHA, read 2026-09-06). Every `uses:` is pinned to a full commit SHA with a trailing version + # comment; this entry maintains those pins under the same floor as everything else. + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + cooldown: + default-days: 7 # the settle window + groups: + ci-actions: + patterns: ["*"] + applies-to: "version-updates" + open-pull-requests-limit: 3 + commit-message: + prefix: "chore(deps)" + + # ── The project's lockfile-managed ecosystem(s) ─────────────────────────── + # One block per ecosystem with a lockfile in the repo. `directories` names every + # tree that carries one (a workspace with per-package lockfiles needs each path; + # a single "/" covers none of them). + - package-ecosystem: "" + directories: + - "/" + schedule: + interval: "monthly" + cooldown: + default-days: 7 + groups: + prod: + patterns: ["*"] + dependency-type: "production" + applies-to: "version-updates" + dev: + patterns: ["*"] + dependency-type: "development" + applies-to: "version-updates" + open-pull-requests-limit: 5 + commit-message: + prefix: "chore(deps)" + + # ── Inactive stubs carry the floor ──────────────────────────────────────── + # An ecosystem the repo does not use yet stays commented out WITH its cooldown, + # so enabling it later is an uncomment, never a re-derivation of the policy. + # - package-ecosystem: "docker" + # directory: "/" + # schedule: { interval: "monthly" } + # cooldown: { default-days: 7 } +``` + +**Not covered by any bot, and therefore not exempt:** toolchain and single-binary pins (`mise.toml` `[tools]`, `.tool-versions`, `rust-toolchain.toml`, a bare `.nvmrc`), container base-image tags, standalone binaries. They install the compilers that build everything else. Apply the same floor by hand, record the settle evidence in the commit (`" is the newest build clearing the 7-day window as of "`), and name the tracking mechanism (an open question in the frame, or a project automation) — silent exemption is how the highest-privilege dependency surface in the repo ends up unaudited (`cbk-conventions-reference.md` § Dependency settle-window). + +## `.gitattributes` + +Ships only when the repo has a lockfile the settle window audits. The git host marks recognised lockfiles as generated and collapses their diffs — hiding exactly the review the settle-window rule mandates. The rule and its sources are in `cbk-conventions-reference.md` § Dependency settle-window › Keep the lockfile diff visible. + +``` +# Only if the pre-check passes: zero CRLF files in the tree, no prior .gitattributes, +# core.autocrlf and core.eol unset — renormalizing a tree that already holds CRLF +# content rewrites history-visible bytes. Verified . +* text=auto eol=lf + +# Counter-line per audited lockfile the host would collapse (linguist's generated +# list is per-name; check it — the line is harmless where the name is not listed). + linguist-generated=false +``` diff --git a/.claude/skills/scaffold/references/github_only_profile.md b/.claude/skills/scaffold/references/github_only_profile.md index 05c672c..2a0e420 100644 --- a/.claude/skills/scaffold/references/github_only_profile.md +++ b/.claude/skills/scaffold/references/github_only_profile.md @@ -15,11 +15,11 @@ A single GitHub repository that holds everything: code, planning (via GitHub Pro MCP can create the repo and push files. Labels, milestones, and project boards **do not have MCP tools today** — those are manual operations regardless of PAT permissions. The flow: **Via MCP:** -1. **Create the repository.** Use the `create_repository` tool. Inputs: name (derived from problem brief, in kebab-case, confirm with user), visibility (ask: public or private — solo defaults to private, team to whatever the org standard is), initialize with README, add a sensible `.gitignore` for the rough stack the user mentioned in the brief (or skip if no signal), and a license (MIT default for solo, ask for team). -2. **Push starter `.github/` files.** Commit via the file-creation tool: an issue template for bug reports, an issue template for features, a PR template, a `CODEOWNERS` file (single owner = the user, for solo; ask for team), and one CI workflow stub (`.github/workflows/ci.yml`) that runs an empty job — real CI gets configured in blueprint. Templates live in `references/scaffold_output_template.md`. +1. **Create the repository.** Use the `create_repository` tool. Inputs: name (derived from problem brief, in kebab-case, confirm with user), visibility (ask: public or private — solo defaults to private, team to whatever the org standard is), initialize with README, add a `.gitignore` for the one stack hint the brief gives, **anchored** per `cbk-conventions-reference.md` § .gitignore anchoring (or skip if no signal), and **confirm the licence the way you confirm visibility** — one question: MIT offered as the solo default, the org standard for a team, never a copyleft licence without explicit opt-in, and "none yet — all rights reserved" a valid answer that is recorded, not skipped (`cbk-conventions.md` § Licensing). Seed `LICENSE` and the README § License from the answer. +2. **Push starter `.github/` files.** Commit via the file-creation tool: an issue template for bug reports, an issue template for features, a PR template, a `CODEOWNERS` file (single owner = the user, for solo; ask for team), and one CI workflow stub (`.github/workflows/ci.yml`) that runs an empty job — real CI gets configured in blueprint. The literal bodies live in `references/github-starter-templates.md` — the issue forms (provenance and holding labels applied at creation), `config.yml` (blank issues enabled), the PR template (the `## Review gate` and `## Triage` blocks, empty), `CODEOWNERS`, the CI stub with its promotion traps, `dependabot.yml` with the floor, and the `.gitattributes` counter-line when a lockfile exists. **Manual (generate instructions inline, user completes in browser tabs):** -3. **Create the label taxonomy.** Visit `/labels`. Delete or rename GitHub's auto-generated defaults (`bug`, `enhancement`, etc.) to avoid collisions with the cascade taxonomy, then create the standard set: `bug`, `feature`, `improvement`, `tech-debt`, `documentation`, plus area labels from the brief. Provide the full list with hex colors so the user can copy-paste. +3. **Create the label taxonomy — the full set, as an axis structure.** Visit `/labels` (or run `gh label create` per label when a token exists — the same list either way). Delete or rename GitHub's auto-generated defaults (`bug`, `enhancement`, `question`, …) first so they cannot collide, then create every axis: **cascade depth** — `cascade-depth:rough` (the workstream parent blueprint creates), `cascade-depth:framed`, `cascade-depth:roughed-in`; **awaiting cascade work** — `enhancement` (the transient holding marker `/intake` reads; see `cbk-conventions-reference.md` § Contribution intake); **provenance** — `source:github`, plus one `source:` per external channel the project takes reports from; **type** — `bug`, `feature`, `improvement`, `chore`, `documentation`, `tech-debt`; **workstream** — `workstream:` for every slug the brief already names (blueprint creates the rest when it creates the parent issues; framing creates one for a slug blueprint did not); **meta** — `meta`; **review control** — `skip-claude`, `claude-review-again`, `claude-fast-review`, `claude-deep-review` (read by the auto-review workflow blueprint may emit; harmless when it does not); and the optional **labour** axis — `labour:` only where the project distinguishes lanes, the default lane unlabelled. Every label a handoff doc or template names must be in this set; a label named nowhere is not created. 4. **Create the project board.** Visit `/projects` → "New project". **Template choice**: pick "Board" (basic kanban) unless the user has a reason for another layout — Board is the simplest and maps cleanly to the cascade's issue-state flow. Project name is goal-based and matches the repo name (e.g. "notes-cli"), not date-based. Link the project to the repo. 5. **Skip milestones and issues.** Milestones are planning artifacts that belong in rough-in (phase 5). Issues belong in finish (phase 6). Do not create either in scaffold, even as "starters." @@ -48,7 +48,7 @@ Same flow regardless of detection state. Build the scaffold output doc from `sca ## What this profile does *not* do -- **Does not configure branch protection.** That's an admin operation that requires UI clicks. Add it to the manual instructions section of the bootstrap checklist with a link and recommended settings. +- **Does not configure branch protection without a token.** With a `repo`-scoped `gh` token it creates the ruleset over `gh api` and discloses it (`manual_steps.md` § Repository administration — the check context read off a real run); without one, the bootstrap checklist carries the ruleset-page fallback with the recommended settings. - **Does not set up GitHub Actions beyond a stub workflow file.** Real CI configuration depends on stack decisions that happen in blueprint. - **Does not create the org-level project board.** Even when the user is in an org, scaffold creates a repo-scoped project. Org-level projects are an explicit user request, not a default. - **Does not create milestones, issues, or any planning content.** Milestones belong in rough-in (phase 5), issues in finish (phase 6). Scaffold provisions the *workspace*; planning artifacts come later. @@ -74,7 +74,7 @@ If any row fails, surface it and offer to retry (for MCP operations) or re-walk - **Visibility default**: solo defaults to private; team defaults to whatever the user states. Always confirm explicitly — getting this wrong is recoverable but annoying. - **License choice**: defaulting to MIT for solo is fine, but ask for teams. Never default to GPL or AGPL without explicit user opt-in — the implications are significant. -- **`.gitignore` overreach**: don't generate a 500-line `.gitignore` with every possible language. Pick one stack hint from the brief, generate that, note that the user can extend later. +- **`.gitignore` overreach**: don't generate a 500-line `.gitignore` with every possible language. Pick one stack hint from the brief, generate that anchored (`/target/`, not `target/` — `cbk-conventions-reference.md` § .gitignore anchoring), note that the user can extend later. - **Label cleanup on new repos**: GitHub auto-creates default labels (`bug`, `enhancement`, `question`, etc.) when initializing a repo. These will collide with the cascade taxonomy. When walking the user through manual label creation, tell them to delete or rename the auto-generated defaults first, then create the cascade set. One instruction, before the label list. - **Project board template choice**: GitHub Projects asks which template to start from (Board, Table, Roadmap, Team planning). Default to **Board** (basic kanban) — it's the simplest and maps cleanly to issue state flow. Only suggest another template if the user has a specific reason. - **Project-vs-project naming confusion**: a "project" in GitHub Projects is not the same as a "project" in the cascade. In GitHub-only mode, the cascade's framing-phase "project" maps to a GitHub project board. The project board name should match the repo name. Clarify once during stage 2. diff --git a/.claude/skills/scaffold/references/manual_steps.md b/.claude/skills/scaffold/references/manual_steps.md index b6abb21..5787bd8 100644 --- a/.claude/skills/scaffold/references/manual_steps.md +++ b/.claude/skills/scaffold/references/manual_steps.md @@ -1,6 +1,6 @@ # Manual steps reference -The canonical list of operations that scaffold **never automates**, regardless of detection state, MCP availability, or user preference. These are operations that either cannot be automated technically (UI-only, OAuth browser flows, billing) or that scaffold deliberately keeps in human hands for safety reasons (admin permissions, security tokens, irreversible workflow customization). +The canonical list of operations that scaffold does not automate. The list is scoped against **tool availability and the operator's preference** — never against detection state: where a tool the session has (a `repo`-scoped `gh` token, a connected MCP) can do the operation and the operator has not asked to keep it in hand, scaffold does it and discloses the result, and the entry below says what it tries first and what the fallback is. The rest are operations that cannot be automated technically (UI-only, OAuth browser flows, billing) or that scaffold deliberately keeps in human hands for safety (admin permissions, security tokens, irreversible workflow customization). When generating manual instructions in the bootstrap checklist's section 2, use this file as the source. Do not invent new manual steps. If the project genuinely needs something not listed here, surface it as an exception and flag the gap. @@ -22,7 +22,7 @@ When generating manual instructions in the bootstrap checklist's section 2, use ### Repository administration -- **Branch protection rules**: Visit `/settings/branches` → "Add branch protection rule" → pattern `main` → enable "Require a pull request before merging" and "Require status checks to pass before merging". For team profiles, also enable "Require approvals" with at least 1 reviewer. Expected outcome: direct pushes to main are rejected. +- **Branch protection (a ruleset)**: **try first** — when a `gh` token with `repo` scope exists, create a ruleset with `gh api repos/{owner}/{repo}/rulesets --method POST` (target `branch`, includes `refs/heads/main`; rules: `pull_request` with the approving reviews the team shape needs — 0 for solo — and `required_status_checks` naming the check-run context **read off a real run** (`gh run view --json jobs` on the stub CI's first run; never guessed from the workflow file), with `strict_required_status_checks_policy` set deliberately — `true` blocks a stale branch and interacts with the CI-skip marker per `cbk-conventions-reference.md` § Required-checks trap). Disclose the created ruleset (`gh api repos/{owner}/{repo}/rulesets`). **Fallback** — only when the call fails or no token exists: `/settings/rules` → "New branch ruleset" → target `main` → require a pull request and the status checks by their context names; for team profiles, at least one approving review. Expected outcome: direct pushes to `main` rejected; a PR whose required contexts have not reported cannot merge. - **Reviewer agent-memory**: decided in the bootstrap checklist § Rule-file disposition (`project` = committed, `local` = never committed) and recorded in `cbk-conventions.md` § Surface inventory; with `project`, delete the kit's `.claude/agent-memory/` line from `.gitignore`. - **Repository secrets**: Visit `/settings/secrets/actions` → "New repository secret" for any deployment tokens, API keys, or credentials. Scaffold never handles secret values directly. - **Repository visibility changes**: visibility set at create time. Changing later is at `/settings` → "Danger Zone". Scaffold never changes visibility after creation. @@ -33,7 +33,7 @@ When generating manual instructions in the bootstrap checklist's section 2, use - **Linear workflow state customization** (when planning = `linear`): Visit `https://linear.app//settings/teams//workflow` → customize states. Linear ships with sensible defaults (Backlog → Todo → In Progress → In Review → Done) which scaffold recommends keeping unchanged for the first few cycles. - **GitHub Projects custom fields**: Visit the project board → "..." → "Settings" → "Custom fields". Scaffold creates the board with default fields only; custom fields are user-added later if needed. -- **GitHub Actions workflows beyond the stub**: scaffold creates an empty CI workflow file as a structural placeholder. Real CI configuration depends on stack decisions that happen in blueprint, so it's deliberately deferred. +- **GitHub Actions workflows beyond the stub**: scaffold creates the CI stub as a structural placeholder and, with the operator's PR/review answer allowing it, blueprint emits the review workflows (`blueprint/references/templates/claude-review.yml`, `claude.yml`); the ADR-immutability lint ships with the ADR starters. Real CI gates depend on stack decisions that happen in blueprint, so they are deferred there — not to the operator. ### Enterprise-only operations diff --git a/.claude/skills/scaffold/references/scaffold_output_template.md b/.claude/skills/scaffold/references/scaffold_output_template.md index 94576ba..fb7b217 100644 --- a/.claude/skills/scaffold/references/scaffold_output_template.md +++ b/.claude/skills/scaffold/references/scaffold_output_template.md @@ -51,7 +51,9 @@ this and how do they coordinate?"> **Team identifier**: **Branch naming**: `/--` -(per `cbk-conventions.md` § Branch naming; `` is the Conventional Commits type) +(per `cbk-conventions.md` § Branch naming; `` is the Conventional Commits type; +issue-less maintenance uses `/` with the PR-body statement +"operator-directed maintenance; no cascade issue") Examples: - `` - `` diff --git a/.claude/skills/scaffold/references/templates/cascade-events-index-template.md b/.claude/skills/scaffold/references/templates/cascade-events-index-template.md new file mode 100644 index 0000000..949f00a --- /dev/null +++ b/.claude/skills/scaffold/references/templates/cascade-events-index-template.md @@ -0,0 +1,41 @@ +# Cascade-events index — template for `docs/cbk/README.md` + +Scaffold creates `docs/cbk/README.md` from this template **beside `problem_brief.md` and `scaffold.md`, in the same commit**, seeded with those two rows. Blueprint, framing and rough-in **append** — a row in `## Index` and a note in `## Phase notes`, in the commit that lands their artifact — and the only in-place edit anyone makes is a row's Status cell (`cbk-conventions.md` § Mutation discipline). Framing creates the file from this template only as a brownfield fallback, when the project predates scaffold's provisioning of it. + +--- BEGIN TEMPLATE --- +# Cascade events + +Chronological index of this project's cascade artifacts (`.claude/rules/cbk-conventions.md` § Cascade artifact layout). **Append-only**: new rows go at the bottom, newest last; the one in-place edit is a row's Status cell (Active → Completed · Superseded by frame-NN · Abandoned). Every phase that lands an artifact appends its row *and* its phase note in the same commit. + +## Index + +| # | Date | Phase | Artifact | Status | +|---|---|---|---|---| +| 1 | | consultation | [problem_brief.md](problem_brief.md) | Approved | +| 2 | | scaffold | [scaffold.md](scaffold.md) | Completed | + +## Phase notes + +One note per row, newest last. A note records the **execution facts** a later reader cannot recover from the artifact itself: what was created outside the repo (issue numbers, board, hub — by id or URL), what departed from the default and why (the discipline that justified it, never the skill version), the research provenance where there was any (agent counts, live probes, the grounding corpus), and **the next runnable command**. + +### 1 — consultation, + +- Sources ingested: +- Next: `scaffold` + +### 2 — scaffold, + +- Planning backend: — +- Knowledge backend: — +- Bootstrap checklist: — manual steps open +- Next: `blueprint` + +## Non-cascade surfaces + +Freely mutable status surfaces that live beside the cascade events and are **not** events (`cbk-conventions.md` § Mutation discipline carves them out): + +| Surface | Path | Who writes it | +|---|---|---| +| Roadmap | `ROADMAP.md` | blueprint seeds it; framing adds rows; rough-in and `/finish` flip status | +| Bootstrap checklist | | scaffold writes it; the operator ticks it | +| Issue records *(in-repo-markdown planning only)* | `issues/` | `/intake` and `/enrich` write them; the operator flips `status:` | diff --git a/.claude/skills/scaffold/references/test_cases.md b/.claude/skills/scaffold/references/test_cases.md index 6060393..44ac89b 100644 --- a/.claude/skills/scaffold/references/test_cases.md +++ b/.claude/skills/scaffold/references/test_cases.md @@ -15,7 +15,9 @@ For Claude.ai's qualitative test loop (no subagents, no benchmarking): a real hu - Discovery captures team shape (solo), quality bar, PR/review process (likely "self-merge with self-review"), tool comfort, and pace - Stage 1 confirms account state without lengthy interrogation - Stage 2 provisions repo via GitHub MCP, walks user through manual label creation, branch naming uses a short team identifier -- Stage 3 produces `docs/cbk/scaffold.md` with all five sections populated and commits both `problem_brief.md` and `scaffold.md` to `docs/cbk/` +- Stage 3 produces `docs/cbk/scaffold.md` with all five sections populated and commits `problem_brief.md`, `scaffold.md` and `README.md` — the cascade-events index from its template, two rows and two phase notes — to `docs/cbk/` +- Stage 2 confirmed the licence the way it confirmed visibility (MIT offered as the solo default; "none yet" accepted as an answer) and seeded `LICENSE` and README § License; the label set created is the full axis structure (`cascade-depth:*`, `source:*`, the type set, `workstream:` per workstream, `meta`, the review-control labels), not a subset +- The scaffold question about PRs named its consequence — the review workflows blueprint will or will not emit - Bootstrap checklist surfaces what's manual - The bootstrap checklist has four sections; section 4 lists every shipped template and path-scoped rule with a disposition, and prints the always-loaded set first. `docs/adr/` exists with ADR-0000's header filled (no `YYYY-MM-DD`). The committed `cascade-rough-in.md` carries eight headings including `## Assumptions`. - The bootstrap checklist's rule-file disposition pass settled the reviewer agent-memory choice, wrote it to the "Reviewer agent-memory" row of § Surface inventory, and — for `project` — deleted the kit's `.claude/agent-memory/` gitignore line. diff --git a/.github/dependabot.yml.example b/.github/dependabot.yml.example new file mode 100644 index 0000000..043a57e --- /dev/null +++ b/.github/dependabot.yml.example @@ -0,0 +1,61 @@ +# Dependency update policy — the kit's own example of the mechanism behind +# .claude/rules/cbk-conventions-reference.md § Dependency settle-window. Copy to +# .github/dependabot.yml in a target project and fill the ecosystem blocks; the +# scaffold starter (.claude/skills/scaffold/references/github-starter-templates.md) +# carries the github-actions entry below with its reasons, plus the stack-ecosystem +# stub a target project needs; the two files are not mirrors — this one is the kit's own. +# +# Three invariants: +# 1. `cooldown` gates VERSION updates only — never security updates (platform +# guarantee: docs.github.com … dependabot-options-reference, read 2026-09-06). +# 2. `groups` is the anti-spam lever; `schedule.interval` is the volume lever (sources: the +# scaffold starter's dependabot comment — the options reference, read 2026-09-06). +# 3. Every ecosystem entry — including an inactive stub — carries the floor. +# +# The kit itself has one ecosystem: the actions its workflows use. + +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + cooldown: + default-days: 7 + groups: + ci-actions: + patterns: ["*"] + applies-to: "version-updates" + open-pull-requests-limit: 3 + commit-message: + prefix: "chore(deps)" + + # Inactive stubs — uncomment the one the project uses; the floor is already there. + # - package-ecosystem: "npm" + # directory: "/" + # schedule: { interval: "monthly" } + # cooldown: { default-days: 7 } + # - package-ecosystem: "pip" + # directory: "/" + # schedule: { interval: "monthly" } + # cooldown: { default-days: 7 } + # - package-ecosystem: "cargo" + # directory: "/" + # schedule: { interval: "monthly" } + # cooldown: { default-days: 7 } + # - package-ecosystem: "pub" + # directory: "/" + # schedule: { interval: "monthly" } + # cooldown: { default-days: 7 } + # - package-ecosystem: "gomod" + # directory: "/" + # schedule: { interval: "monthly" } + # cooldown: { default-days: 7 } + # - package-ecosystem: "bundler" + # directory: "/" + # schedule: { interval: "monthly" } + # cooldown: { default-days: 7 } + # - package-ecosystem: "mix" + # directory: "/" + # schedule: { interval: "monthly" } + # cooldown: { default-days: 7 } diff --git a/.github/workflows/adr-immutability-check.yml b/.github/workflows/adr-immutability-check.yml index c1d92f5..f07825d 100644 --- a/.github/workflows/adr-immutability-check.yml +++ b/.github/workflows/adr-immutability-check.yml @@ -6,14 +6,19 @@ name: ADR immutability check # `docs/adr/[0-9]{4}-*.md` file has been modified (additions of new ADR # files are allowed; deletions and modifications are not). +# No `paths:` filter, on purpose: a required check that is skipped by a path +# filter never reports and parks the PR forever (cbk-conventions-reference.md +# § Required-checks trap, cause 2). The job exits 0 in seconds when no +# ADR changed, so always-run costs nothing. on: pull_request: branches: [main] - paths: - - 'docs/adr/**' jobs: immutability: + # The check-run context a ruleset requires. Pinned before promotion and never + # renamed afterwards — matching is by name (the trap's cause 3). + name: ADR immutability runs-on: ubuntu-latest steps: - name: Checkout PR branch diff --git a/CLAUDE.md b/CLAUDE.md index 3367298..74de04f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -42,9 +42,9 @@ The cascade is top-down, but a **bottom-up contribution lane** complements it fo ├── rules/ ← operational contracts (cbk-conventions, pr-review — each split into an always-loaded contract and a path-scoped `-reference.md` half — plus testing, logging, simplification, knowledge-backend) and rule templates (tooling; orchestration, itself also split); workflows.md is portable ├── workflows/ ← saved orchestrations (review-sweep: find-then-adversarially-verify review pass) and harness exemplars (finish-ab/ two-arm A/B, agent-cost.py) with their stub tests └── skills/ - ├── consultation/SKILL.md + references/ ← phase 1 - ├── scaffold/SKILL.md + references/ ← phase 2 - ├── blueprint/SKILL.md + references/ ← phase 3 + ├── consultation/SKILL.md + references/ (notion_ingestion, frozen_corpus_ingestion, …) ← phase 1 + ├── scaffold/SKILL.md + references/ (adr-starters/ incl. corrections.md, issue-templates/, github-starter-templates.md, templates/cascade-events-index-template.md, …) ← phase 2 + ├── blueprint/SKILL.md + references/ (templates/ incl. roadmap.md, claude-review.yml, claude.yml, …) ← phase 3 ├── framing/SKILL.md + references/ (contract.md, procedure.md, …) ← phase 4 ├── rough-in/SKILL.md + references/ (contract.md, procedure.md, …) ← phase 5 └── adr-new/SKILL.md ← ADR scaffolder (used by target projects) @@ -57,7 +57,7 @@ Each skill follows the same pattern: a top-level `SKILL.md` with frontmatter (`n These are load-bearing across the kit. Edits that violate them break the cascade in subtle ways that show up phases later. - **Skills stay portable; project specifics live in `.claude/rules/cbk-conventions.md` of the *target* project.** No project-specific identifiers (issue keys like `-NN`, project or repo names, specific framing-number pins) should leak into skill content. The skills describe *choice spaces*; the rules file in the target project records *the operational choice*. The `.claude/rules/cbk-conventions.md` in this kit is a template with bracketed placeholders — a target project copies it and fills in its own values. -- **Cascade events are append-only.** Re-framing produces `frame-02.md` that supersedes `frame-01.md` via a status field; it does not overwrite. ADRs are immutable; superseding writes a new ADR. The cascade IS the audit trail of decisions. +- **Cascade events are append-only.** Re-framing produces `frame-02.md` that supersedes `frame-01.md` via a status field; it does not overwrite. ADRs are immutable; superseding, refining or extending writes a new ADR, and a wrong *claim* inside one goes to `docs/adr/corrections.md`. The cascade IS the audit trail of decisions. The status surfaces beside it — `docs/cbk/ROADMAP.md`, a frame's `## Rough-in events` table, the events index's status column — are carved out as freely mutable; they are not events. - **Each phase has explicit HITL (human-in-the-loop) gates and explicit *rigor modes* (light / standard / full).** Don't collapse gates without considering the one-way-door property of the action they protect. A gate exists where a downstream commit (Linear write, GitHub Issue creation, branch/PR push, ADR commit) would be expensive to unwind. - **Inheritance is verbatim, not paraphrased.** Each phase reads prior-phase artifacts in full and quotes the relevant content into its inheritance summary. Paraphrasing is the most common cascade failure mode. - **`/finish` is the executor; planning happens upstream.** `/finish` does not modify issue bodies, does not handle re-rough-in, does not bypass dependencies, does not skip `/simplify` or `pr-review-toolkit:review-pr` (the floor — two skills actually invoked, once, recorded in the PR body's `## Review gate` block; the orchestrated sweep supplements and never substitutes). When `/finish` hits something the spec didn't anticipate, it surfaces and aborts rather than improvising — the gap is data for the next revision. @@ -69,6 +69,7 @@ These are load-bearing across the kit. Edits that violate them break the cascade - **Axis 1 — Planning backend**: GitHub Issues / Linear / in-repo markdown. Where live work-tracking with status, parent/child, queryable state happens. - **Axis 2 — Knowledge backend**: Notion / none. A durable longer-lived reference library — pre-cascade research, cross-project decisions, runbooks that span repos. Distinct from the constant; sits *alongside* rather than replacing. - The axes generate a 3 × 2 = 6 matrix. There are no named "preset profiles" — operators pick each axis independently. Skills are axis-aware along both; new features must consider both. **The canonical axis record is `docs/cbk/scaffold.md` § Cascade metadata** (`Planning backend` / `Knowledge backend` rows); `.cascade/backends.toml` is the machine-readable mirror consumers fall back to. `in-repo-markdown` planning is design-doc mode: no `/finish` executor (disclosed at scaffold's gate), specs executed manually against the markdown. +- **A fact stated on more than one surface is stated once, with its other locations recorded.** Back-pointers are asymmetric (the immutable source never points forward; the companion points back), a re-check trigger sits at the rail it fires, and closed sets are diffed by the verification block where the tree can. The companion vocabulary — immutable source plus append-only companion — has three instances: ADRs + `docs/adr/corrections.md`, a frozen pre-cascade corpus + its errata, a repo artifact + its knowledge-backend page. See `cbk-conventions.md` § Multi-surface facts. - **Knowledge backend is read-primary at lower phases; writes are tiered and HITL-gated.** Default behavior: rely on repo + inheritance. Reach to the knowledge backend on demand when richer detail is needed. Writes are routine at consultation and scaffold (companion-page, hub establishment); HITL-gated and default-SKIP at blueprint / framing / rough-in / `/finish`. The operational contract lives in `.claude/rules/knowledge-backend.md`. ## Working in this repo diff --git a/README.md b/README.md index 28ec6af..fedd574 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,9 @@ The kit is the operational surface of an AI-assisted development cascade: a sequ ↓ consultation → docs/cbk/problem_brief.md (chat-only, no repo yet) ↓ - scaffold → repo + docs/cbk/scaffold.md (planning surface, conventions) + scaffold → repo + docs/cbk/scaffold.md (planning surface, conventions, the events index, the .github starters) ↓ - blueprint → docs/cbk/blueprint.md + foundation docs (stack, ADRs, workstreams) + blueprint → docs/cbk/blueprint.md + foundation docs (stack, ADRs, workstreams, the roadmap, the review workflows) ↓ framing → docs/cbk/frame-NN.md (one workstream, sequenced milestones) ↓ @@ -292,8 +292,8 @@ The user then reviews the draft and flips it to ready when satisfied — that tr │ └── pr-respond.md ← the PR feedback-loop executor ├── skills/ │ ├── consultation/ ← Phase 1 -│ ├── scaffold/ ← Phase 2 (+ references/adr-starters/, references/issue-templates/) -│ ├── blueprint/ ← Phase 3 +│ ├── scaffold/ ← Phase 2 (+ references/adr-starters/ incl. corrections.md, references/issue-templates/, the .github starter bodies, the cascade-events index template) +│ ├── blueprint/ ← Phase 3 (+ references/templates/: the foundation docs, roadmap.md, the review workflows claude-review.yml + claude.yml) │ ├── framing/ ← Phase 4 (+ references/contract.md, references/procedure.md) │ ├── rough-in/ ← Phase 5 (+ references/contract.md, references/procedure.md; references/finish-command.md + finish-procedure.md, the bundled executor pair) │ └── adr-new/ ← ADR scaffolder (used by blueprint and onward) diff --git a/docs/adr/README.md b/docs/adr/README.md index f9aea4b..c5fd6b2 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -13,7 +13,7 @@ The `/adr-new` Claude Code skill (`.claude/skills/adr-new/SKILL.md`) automates t 3. Add cross-references in `Related` if the new ADR connects to existing ones. 4. Add a row to the Index in this README and to the Decisions log in `docs/ARCHITECTURE.md`. If the project tracks stack decisions in `docs/cbk/blueprint.md`, mirror there too. 5. Open a PR. ADR text changes through normal review. -6. After merge, an accepted ADR is **immutable** — its content is not edited again. Updates happen via new ADRs that supersede the old (Status: `Superseded by ADR-NNNN`). The PreToolUse hook in `.claude/hooks/protect-immutable-adrs.sh` enforces this at the Claude Code tool level; the `.github/workflows/adr-immutability-check.yml` CI workflow enforces it at the raw-git level. +6. After merge, an accepted ADR is **immutable** — its content is not edited again. Updates happen via new ADRs that supersede the old (Status: `Superseded by ADR-NNNN`). The PreToolUse hook in `.claude/hooks/protect-immutable-adrs.sh` enforces this at the Claude Code tool level; the `.github/workflows/adr-immutability-check.yml` CI workflow enforces it at the raw-git level. A **claim** inside an accepted ADR that proves wrong — a citation, a figure, an attribution, a formula — is not a decision revision: record it in [`corrections.md`](corrections.md) (append-only) and leave the ADR as written. ## Status legend @@ -24,6 +24,8 @@ The `/adr-new` Claude Code skill (`.claude/skills/adr-new/SKILL.md`) automates t | `Deprecated` | No longer recommended, but still in effect; new code should not assume it | | `Superseded by ADR-NNNN` | Replaced by a newer decision | +The status cell carries the relation grain and the parent inline — `Accepted · Refines ADR-0007 (D2)`, `Accepted · Extends ADR-0003 (D1)`, `Accepted · D3 superseded by ADR-0012` — so a clause-scoped supersession annotates the parent's row while the parent file stays untouched. + ## Numbering convention - Four-digit prefix, zero-padded: `0001`, `0002`, ..., `0099`, `0100` @@ -37,3 +39,7 @@ The `/adr-new` Claude Code skill (`.claude/skills/adr-new/SKILL.md`) automates t | # | Title | Status | |---|---|---| | [0000](0000-record-architecture-decisions.md) | Record architecture decisions | Accepted | + +## Corrections + +Claims that proved wrong after acceptance live in [`corrections.md`](corrections.md) — the append-only companion, four genres: wrong when written, right then stale, orphaned attribution, defective formula. A reviewer consults it before flagging; a decision that needs revising still takes a new ADR. diff --git a/docs/adr/corrections.md b/docs/adr/corrections.md new file mode 100644 index 0000000..4d0954e --- /dev/null +++ b/docs/adr/corrections.md @@ -0,0 +1,49 @@ +# Corrections register + +A register of **claims** inside accepted decision records that later proved wrong, stale, orphaned or miscomputed — and the correct reading. It exists because ADRs are immutable ([ADR-0000](0000-record-architecture-decisions.md)): a *decision* is revised by a superseding, refining or extending ADR, but a *claim* inside one — a cited figure, an attributed source, a formula, a fact about a dependency — is not a decision, and re-issuing a whole ADR to fix a citation would bury the decision history under errata. This file is the append-only companion to the immutable source; the source never points here, the entry points back (`cbk-conventions.md` § Multi-surface facts). + +## What it is not + +- **Not a place to revise a decision.** If the correct reading changes what the ADR decided, the consequence line says so and a refining or superseding ADR is filed; the entry here records only the claim. +- **Not a changelog of the ADR set** — the index in `README.md` is that. +- **Not a discussion surface.** An entry states the claim, the correct reading and the evidence; disagreement goes to a PR review or an ADR. + +## Mutation discipline + +Append-only. Entries are dated and never edited after they land — a correction to a correction is a new entry naming the old one. An entry's *evidence* may itself go stale; when its meaning changes, annotate under it (dated), never rewrite it. The immutability hook (`.claude/hooks/protect-immutable-adrs.sh`) leaves this file editable by design. + +## The four genres + +| Genre | What it is | Typical evidence | +|---|---|---| +| **Wrong when written** | The claim was false at the time the ADR was accepted — a misread source, a wrong figure | The primary source, quoted | +| **Right then stale** | The claim was true at acceptance and the world moved — a dependency's behaviour, a platform limit, a price | A dated re-check against the live source | +| **Orphaned attribution** | The claim is attributed to a source that does not say it, or no longer exists | The source as it reads today, or its absence | +| **Defective formula** | A calculation, threshold or derivation in the ADR does not produce what it claims | A reproduction with the inputs and the corrected result | + +## Entry shape + +```markdown +### C-NNN — · · + +- **Where:** ADR-NNNN §
— the sentence, quoted verbatim +- **Claim as written:** "…" +- **Correct reading:** … +- **Evidence:** — +- **Consequence:** none for the decision · the decision stands with a scoped reading · escalate — a refining or superseding ADR is warranted (name it once filed) +- **Annotations:** _(dated; appended only when the evidence's meaning goes stale)_ +``` + +## How to add an entry + +1. Confirm the claim is not a decision — if fixing it changes what was decided, file an ADR instead and record only the claim here. +2. Take the next `C-` number; numbers are append-only. +3. Quote the sentence verbatim, with its ADR and section, so the entry is findable from the ADR without the ADR pointing here. +4. Class the evidence and cite it — a corrected claim with no evidence is a new unsupported claim. +5. Decide the consequence line honestly; *escalate* is a valid outcome. +6. Land the entry in the PR that found the defect, not a separate one. +7. Reviewers consult this register before flagging: a claim already recorded here is cited, never restated as a finding. + +## Entries + +_(none yet)_ diff --git a/docs/adr/template.md b/docs/adr/template.md index 0199fe9..0866e65 100644 --- a/docs/adr/template.md +++ b/docs/adr/template.md @@ -4,6 +4,10 @@ - **Date:** YYYY-MM-DD - **Deciders:** - **Related:** ADR-NNNN, ADR-NNNN *(optional; remove the line if none)* +- **Supersedes:** ADR-NNNN *(replaces the parent's decision; `ADR-NNNN Dn` replaces one clause and the parent stays Accepted — remove if none)* +- **Refines:** ADR-NNNN (Dn, …) *(narrows a parent clause to a scoped reading — removes a permitted reading; the parent stays Accepted — remove if none)* +- **Extends:** ADR-NNNN (Dn, …) *(adds an obligation beside a parent clause that stays satisfied as written; the parent is not narrowed — remove if none)* +- **Promotes:** § *(a decision lifted from a frozen pre-cascade corpus; the corpus is the provenance — remove if none)* ## Context diff --git a/docs/superpowers/plans/2026-09-06-harvest-3-p4-axis-records-hygiene.md b/docs/superpowers/plans/2026-09-06-harvest-3-p4-axis-records-hygiene.md new file mode 100644 index 0000000..ea6feaa --- /dev/null +++ b/docs/superpowers/plans/2026-09-06-harvest-3-p4-axis-records-hygiene.md @@ -0,0 +1,116 @@ +# Harvest 3 — P4: the github-issues axis, after the draft, decision records, hygiene, what the phases emit — Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task, inline, with a checkpoint after each task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Land P4 of harvest 3 — clusters C5 (the github-issues axis), C6 (after the draft opens), C7 (decision records, corrections, multi-surface facts), C9 (supply chain, hygiene, the issue-less branch, the licence) and C10 (what the producing phases emit) — as one PR against `main` that closes #17 #18 #19 #20 #21 #22 #23 #24 #26 #27 #28 #29 #31 #32 #35 #36 #37 #38 #39 #46 #47 #50 #51 #52. + +**Architecture:** Every artifact the exercised runs settled on ships as a **template** (bracketed project block, dated platform claims, reasons at the site): the `.github/` starters, the roadmap, the two review workflows, the dependabot example, the corrections register, the frozen-corpus ingestion pattern, the cascade-events index. Every rule the runs settled on lands **once**, in the conventions template or its reference half, with the surfaces that restate it swept in the same commit (that is the discipline C7 states; this PR instantiates it). The three producing phases gain the emitted sections the runs found missing. Nothing here re-opens P3's contracts beyond the roadmap clause P3 deferred. + +**Register:** the P3 plan inlined every artifact; this plan names each edit and its check and authors the bodies at execution — the executor is this session, the sibling artifacts are read, and the review pass is the same floor. Every task still ends in a green suite and its own commit. + +**Tech Stack:** Markdown, YAML (workflow and issue-form bodies), bash (the verification block). No new scripts. + +**Spec:** `docs/superpowers/specs/2026-09-06-cascade-kit-harvest-3-design.md` § C5, § C6, § C7, § C9, § C10 (read in full before Task 1); § Governing constraints; decisions D22–D27. + +## Global Constraints + +Identical to P3's, restated for the executor: + +- **Sanitized, one-way.** No sibling repository, package, product, issue number or run id lands in kit content (the two public runs are named nowhere; the private instance never). Every lifted body is generalized to bracketed placeholders before it lands. Sibling repositories are read, never modified. +- **Portability greps are the hard gate.** The verification block (`cbk-conventions-reference.md § Verification`, kit sub-block) must be green after every task, extracted and run as in P3: + + ```bash + awk '/^## Verification/{p=1} p' .claude/rules/cbk-conventions-reference.md | awk '/^```bash/{c=1;next} /^```/{c=0} c' > /tmp/claude-1000/cbk-verify.sh && bash -e /tmp/claude-1000/cbk-verify.sh + ``` + + New checks are plain lines inside the single fence; must-be-absent checks use `absent` with a self-splitting literal; positive chains are wrapped `{ … } || { echo …; exit 1; }`. The adr-starters mirror is diffed byte-for-byte against `docs/adr/` — any starter change lands in both. +- **Never commit on `main`; branch and first commit in separate tool calls.** No `[skip ci]`. One commit per task, Conventional Commits, the `Co-Authored-By: Claude Fable 5.1 ` trailer. +- **No unsourced platform claims**: a GitHub or Claude Code behaviour lands with its page and date, or as a dated field observation. The three the issues flag: the required-check path-filter cause (GitHub's troubleshooting page), the check-run rename consequence (field-observed — dated as such), the linguist generated-file list (`generated.rb` and `docs/overrides.md`). +- **Bounded fan-outs.** Task 10's sweep stays at or under 25 planned agents; no research workflow is launched (the research is the issue bodies and the sibling artifacts, already read). +- **Every `SKILL.md` stays under 500 lines** (the block enforces it); every always-loaded rule edit is measured and recorded in the PR body. + +## Decisions taken while planning (for the PR body) + +- **D-P4-1 — the `.github/` starters ship in one reference file** (`scaffold/references/github-starter-templates.md`) with the literal bodies, plus a kit-root `.github/dependabot.yml.example` (the kit itself has only the actions ecosystem; the example carries the floor for it and inactive stubs for the common lockfile ecosystems). The kit does not adopt a PR template or issue forms for itself — the kit's own PRs are `gh pr create --body-file` runs. +- **D-P4-2 — the review workflows are blueprint templates**, not scaffold starters: they need the project block (rules to check, guarded surfaces, docs-only paths, model and effort) that only blueprint knows. Scaffold's PR/review answer gates whether blueprint emits them. +- **D-P4-3 — the roadmap is emitted by blueprint on the github-issues and in-repo-markdown axes** (not on linear, whose projects carry sequence), read by framing, appended by framing, flipped by rough-in and by `/finish` on the PR's own branch as the last commit (P3's deferred clause), with the post-merge checklist as the fallback flip when the spec did not name it. No `/land` command (D23). +- **D-P4-4 — corrections.md is a starter in both homes**: `docs/adr/corrections.md` (the kit's own register, empty) and `scaffold/references/adr-starters/corrections.md` (byte-identical; the block already diffs the two trees). +- **D-P4-5 — the frozen-corpus pattern ships as prose plus a pointer to the ADR hook as the enforcement exemplar**; no `protect-frozen-corpus.sh` ships, because the kit cannot know a corpus path. Scaffold registers the enforcement set when consultation designated a corpus. +- **D-P4-6 — one MCP-configuration section lives in `tooling.md`** (the template), covering committed config with env-var references, the example env file, phase-justified wiring and the list-valued-keys rule; `workflows.md`'s optional cost section names money and agent-run quota separately. +- **D-P4-7 — #47's spike-record method** ("draft → a script asserting the frozen table byte-identical → one adversarial pass → fold") lands as the measurement variant's completeness check, sized to one pass plus a delta, in the spec template, not as a new command. + +--- + +### Task 0: Branch and plan + +- [ ] `git switch main && git pull --ff-only` → `git switch -c feat/harvest-3-p4-axis-records-hygiene` (own tool call) → run the block (baseline: note `always-loaded total:`) → commit this plan: `docs(plan): harvest 3 P4 — the github-issues axis, after the draft, decision records, hygiene, what the phases emit (C5–C7, C9, C10)`. + +### Task 1: The `.github/` starters and the label taxonomy (C5, C9 half) — #19 #36, part of #22 + +**Files:** create `.claude/skills/scaffold/references/github-starter-templates.md`; create `.github/dependabot.yml.example`; modify `scaffold/references/github_only_profile.md` (line 19 citation; § label taxonomy; the `.gitignore` bullet at 77 and checklist clause at 18 — see Task 7), `scaffold/SKILL.md` § Reference files (add the new file and the missing `backends.md` row), `scaffold/references/bootstrap_checklist_template.md` (the "created with README, .gitignore, MIT license" line points at the conventions). + +- [ ] **The starter file**, generalized from the exercised body: a provenance note (the templates were promised and cited at a file that never held them); `bug_report.yml` and `feature_request.yml` as issue **forms** applying the provenance label (`source:github`) and the holding label (`enhancement` for requests; `triage` for bugs) at creation and telling the reporter the issue is not executable as filed; `config.yml` with blank issues **enabled** and the reason (cascade-native bodies); `pull_request_template.md` carrying `## Review gate` and `## Triage` empty with the not-a-`/finish`-run instruction ("mark the lines *not run* with the reason"); `CODEOWNERS` as a routing declaration that enforces nothing without a ruleset; `ci.yml` as a stub with the two promotion traps at its site (no `paths:` on a required check; the job name is the immutable API); `dependabot.yml` with `cooldown` per ecosystem, the three invariants as comments, `groups` as the anti-spam lever, inactive-ecosystem stubs commented in **with the floor** (Task 7's rule: an inactive stub carries the floor so enabling it cannot drop it), and the toolchain-pins-are-not-exempt paragraph. Bracket every project-specific value (ecosystem, directories, username). +- [ ] **The label taxonomy as an axis structure**, in `github_only_profile.md` § label taxonomy (and the one-line summary in `scaffold/SKILL.md`): cascade depth (`cascade-depth:framed`, `cascade-depth:roughed-in`), awaiting cascade work (`enhancement`), provenance (`source:github`, `source:`), type (`bug`, `feature`, `improvement`, `chore`, `documentation`, `tech-debt`), workstream (`workstream:` — created by blueprint when it creates the parent issues, and by framing for a slug blueprint did not), meta (`meta`), review control (`skip-claude`, `claude-review-again`, `claude-fast-review`, `claude-deep-review`), and the optional labour axis (`labour:`; the default lane unlabelled — C10). Scaffold creates the full set; the handoff docs stop naming labels scaffold does not create (`cascade-depth:rough` and `chore` — resolve by creating `chore` and dropping `cascade-depth:rough`). +- [ ] `.github/dependabot.yml.example`: the kit's own example — `github-actions` with the floor, plus commented stubs for `npm`, `pip`, `cargo`, `pub`, `gomod`, `bundler`, `mix`, each carrying `cooldown` so uncommenting cannot drop it; the header names the settle-window section and the three invariants. +- [ ] Checks: the block gains `[ -f .claude/skills/scaffold/references/github-starter-templates.md ]`, a grep that `github_only_profile.md` cites it and no longer cites `scaffold_output_template.md` for the templates, a grep that the starter file's PR template carries `## Review gate` and `## Triage`, and `absent grep -rn "cascade-depth:roug[h]\b" .claude/`. Commit: `feat(scaffold): the .github starters with their bodies — issue forms with provenance and holding labels, the PR template with the gate blocks, CODEOWNERS, the CI stub with its traps, dependabot with the floor; the label taxonomy as an axis structure`. + +### Task 2: The required-checks trap, the always-run lint, rulesets over `gh api` (C5) — #20 #21, #50 (d), #52's deliverable direction + +**Files:** modify `.claude/rules/cbk-conventions.md` § `[skip ci]` rule (the required-checks paragraph; the deliverable trap; § ADR index sync note), `.github/workflows/adr-immutability-check.yml`, `scaffold/references/manual_steps.md`, `scaffold/references/github_only_profile.md:51` (branch protection is not UI-only), `scaffold/references/bootstrap_checklist_template.md:44`, `blueprint/references/templates/standards.md:183`. + +- [ ] Rewrite the trap as **one symptom, three causes**: the CI-skip marker on HEAD (existing); a `paths:`-filtered workflow backing a required check never reports (cite GitHub's troubleshooting page on required checks skipped by path, branch or commit-message filtering; cross-link § Exclusion is not exemption); a promoted check-run's name changing orphans the context (dated field observation, not a documented claim). Rule: a required-check workflow carries no trigger filter and a stable explicit `name:` set before promotion. +- [ ] `adr-immutability-check.yml`: drop the `paths:` filter, name the job explicitly, the reasons as comments at the site; the job still exits 0 in seconds when no ADR changed. +- [ ] `manual_steps.md`: scope the list against **tool availability and preference, never detection state**; the branch-protection entry attempts a ruleset via `gh api repos/{owner}/{repo}/rulesets --method POST` when a `repo`-scoped token exists, wiring `required_status_checks` to the check-run context **read off a real run** (`gh run view`), `strict_required_status_checks_policy` set deliberately with the trap cross-linked, disclosing the result, falling back to the branch-settings UI only when the call fails or no token exists. Sweep `github_only_profile.md:51` and `bootstrap_checklist_template.md:44` in the same commit; `standards.md:183` inherits the always-reports rule. +- [ ] § Deliverable trap gains both directions: a red run whose review posted is a pass, a green run with no comment is a failure; the artifact decides. § ADR index sync notes the lint must be always-run to be a required check. +- [ ] Checks: `absent grep -n "paths:" .github/workflows/adr-immutability-check.yml`; `grep -q "name:" … ` on the job; `absent grep -n "regardless of detection stat[e]" .claude/skills/scaffold/references/manual_steps.md`. Commit: `fix(conventions): the required-checks trap as one symptom with three causes; the ADR lint always runs under a pinned job name; rulesets over gh api with the context read off a real run; the deliverable trap in both directions`. + +### Task 3: The roadmap (C5) and the post-merge checklist (C6) — #35, #50 (a)(b)(c), P3's deferred clause + +**Files:** create `.claude/skills/blueprint/references/templates/roadmap.md`; modify `blueprint/SKILL.md` (emit on github-issues and in-repo-markdown; the outputs list; § Handoff contract), `blueprint/references/blueprint-output-template.md` (the roadmap named), `framing/SKILL.md` § Producing … (the row in the same atomic commit) and `framing/references/planning-backend-commit.md`, `rough-in/references/procedure.md` § Step 6 (the flip), `.claude/commands/finish.md` items 8 and 9 and § What this command does not do, `.claude/commands/finish-procedure.md` Steps 10 and 11 and § What `/finish` does NOT do, both bundled templates (re-spliced byte-parallel), `cbk-conventions.md` § Mutation discipline (a `docs/cbk/ROADMAP.md` row: freely mutable status surface, not the audit trail; the status-surface carve-out sentence naming who flips what) and § Quick reference. + +- [ ] **The template**: line one states the mutation class; `## Now` paragraph; `## The sequence` table (`# | Step | Workstream | What "done" is | Depends on | Status | Where`) with the status vocabulary (*now · planned · framed · roughed-in · in progress · done · horizon · retired*); `## Cascade position`; `## Seeing the same thing from the tracker` with the `gh issue list` queries; who keeps it current, phase by phase, including the `/finish` rule below. +- [ ] **`/finish`**: item 8 gains the roadmap clause — where the project keeps a roadmap and the spec names the flip, the row and the `## Now` paragraph are updated on this branch as the last commit before `gh pr create`, as one `docs(cascade):` commit with no CI-skip marker; the PR number is predicted from the repository's shared issue-and-PR sequence (`gh api 'repos/{owner}/{repo}/issues?state=all&sort=created&direction=desc&per_page=1'` plus one), checked against the number `gh pr create` returns, one correcting commit on mismatch; never a separate PR. § does not do carves out the status surfaces (the roadmap row; a frame's `## Rough-in events` table) from the cascade-artifact ban and names who flips them: in-PR when the spec names it, the post-merge checklist otherwise. Item 9's hand-off ends with the **post-merge checklist**: sync and prune; post the drafted roll-forwards; close the metas the close marker did not; append the frame's rough-in-events row when the PR is a milestone's capstone; flip the roadmap row if it was not flipped in-PR; the next runnable command from the roadmap. The procedure's Steps 10–11 carry the same in procedure form; both bundled templates re-spliced; the block's two byte-parallel checks stay green. +- [ ] Checks: the block gains `[ -f .claude/skills/blueprint/references/templates/roadmap.md ]`, `grep -q 'ROADMAP.md' .claude/rules/cbk-conventions.md`, and a grep that `finish.md` names the roadmap flip and the post-merge checklist. Commit: `feat(cascade): the roadmap — a freely mutable status surface blueprint emits, framing appends, rough-in and /finish flip (on the PR's own branch, last commit, number predicted); the post-merge checklist in the hand-off`. + +### Task 4: Review automation and the `gh` shape (C5, C10 tooling) — #39 #46 #52, #36 (blueprint half) + +**Files:** create `blueprint/references/templates/claude-review.yml` and `claude.yml`; modify `scaffold/references/github_only_profile.md:104` and `scaffold/SKILL.md` (the PR/review question names its consequence and offers "solo-merge with automated review"), `blueprint/SKILL.md` § Foundation document production (tooling step: the skip surfaced as a one-line notice; the workflows emitted when the answer allows; `workstream:` labels created with the parent issues), `blueprint/references/handoff-issue.md` (the `/install-github-app` line conditional; toolchain pins named as bot-uncovered — #22), `blueprint/references/templates/tooling.md` (Q6/Q7 gain the toolchain-pin question — #22), `.claude/rules/tooling.md` § Planning backend (a `gh` row and the "one connection among several" rule) and a new `## Automated review on the git host` section shape pointing at the templates (C10), `blueprint/references/planning-backend-commit.md`, `framing/references/planning-backend-commit.md`, `rough-in/references/planning-backend-commit.md` and the three `planning-backend-matrix.md` § github-issues (a `gh` subsection each: the five commands, the parent-object return, the create-then-edit placeholder pattern as one script; `gh` is the default, the MCP one connection among several; phase skills read and write the working tree by default). + +- [ ] **The two workflow templates**, generalized from the exercised pair with the project block bracketed (the rules to check, the guarded surfaces and their review block, the docs-only paths, the model and effort per label) and the **seven authoring constraints at their sites** as comments: (1) the action cannot review the PR that introduces it — verify on the next PR; (2) no author-controlled input interpolated into `run:` — labels through `env:` as JSON, read as quoted variables; (3) a job-level timeout; (4) the posted artifact is the deliverable in both directions — the prompt makes the summary an explicit `gh pr comment` and names it first; (5) label-driven proportionality (`claude-fast-review`, `claude-deep-review`, `claude-review-again`) that does not re-fire on `synchronize`; (6) permissions scoped to the job; (7) no orphanable sub-dispatch (`--disallowedTools Agent` on the default and fast paths). Plus the two things the run learned: `gh pr checks` is usually unreadable from the action's token — say nothing about CI rather than report the permission error; `track_progress` is unsupported on `labeled` events. Actions pinned to a full SHA with a version comment (the settle-window rule), values bracketed where the project decides. +- [ ] Checks: both templates exist; `grep -c "cannot review the PR that introduces it\|--disallowedTools Agent\|timeout-minutes"` ≥ 1 each; `grep -q "solo-merge with automated review" github_only_profile.md`; `grep -q "create-then-edit" rough-in/references/planning-backend-commit.md`. Commit: `feat(blueprint): the review workflows as templates with their seven constraints at the site; scaffold's review question names its consequence; the gh shape at every planning-backend commit, gh the default and the MCP one connection among several`. + +### Task 5: After the draft opens (C6) — #47 items 1–2, #50 (a), #51 + +**Files:** modify `.claude/commands/finish.md` (item 8: the body is the audit surface for the PR's whole life, a `## Triage — round N` block appended by `/pr-respond`; item 9: the **backward sweep** beside the roll-forward; § tests: the measurement test — "for a measurement issue the verdict rule's commit precedes the first measurement commit, the capstone shows the table unchanged against the base branch, the departures are weighed in the verdict"), `.claude/commands/finish-procedure.md` Step 11 (the backward sweep in procedure form: the closed candidate set — open siblings under the parent carrying `cascade-depth:roughed-in`, one call, capped by the R-count; absence claims only, each verified by running the body's own command; falsified-by-landing separated from false-when-written; default to not reporting; the repair is rough-in's in-place body refresh; an empty sweep is a one-line pass), both bundled templates re-spliced, `.claude/commands/pr-respond.md` Step 7 (append the round's triage to the PR body, not only a comment), `rough-in/references/templates/rough-in-spec-template.md` (the **measurement variant** of § Acceptance criteria and § Done signal: the verdict rule committed before any number exists as the first criterion; the findings table frozen against the base branch; rows as value / method / date; a by-hand completeness checker that never runs in the CI gate; commit order as evidence; the row-verification pass sized to one pass plus a delta), `rough-in/references/contract.md` item 4's Done signal (names the loop **and** the record's completeness check), `.claude/skills/rough-in/references/test_cases.md` (one line in Test 9's criteria). + +- [ ] Checks: `grep -q "Backward sweep\|backward sweep" .claude/commands/finish.md .claude/commands/finish-procedure.md`; `grep -q "verdict rule" .claude/skills/rough-in/references/templates/rough-in-spec-template.md .claude/commands/finish.md`; byte-parallel checks green. Commit: `feat(finish): the PR body is the audit surface for the PR's life; the backward sweep beside the roll-forward; the measurement variant of a spec with the verdict rule committed first`. + +### Task 6: Decision records, corrections, facts stated more than once (C7) — #17 #18 #29 #37 #26 + +**Files:** modify `cbk-conventions.md` § Mutation discipline (the `Extends:` grain with the disambiguation test — removes a permitted reading → Refines; adds an obligation beside a satisfied clause → Extends; the reviewer sentence names Extends and its asymmetry; a `docs/adr/corrections.md` row; a reference-register row; the status cell carries grain and parent inline; a clause-scoped supersession annotates the parent's index row while the file stays untouched) and a new `## Multi-surface facts` section immediately before § ADR index sync (stated once: every restatement records where the fact's other statements live and an edit sweeps every recorded location in one commit; asymmetric back-pointers — the immutable source never points forward, the companion points back; a re-check trigger reachable from the line that fires it; closed sets machine-maintained where the tree can; § ADR index sync reframed as the first instance) with the **companion vocabulary** (immutable source plus append-only companion) and its three instances; create `docs/adr/corrections.md` and `scaffold/references/adr-starters/corrections.md` (identical; header: what it is, what it is not, mutation discipline, the four genres — wrong when written / right then stale / orphaned attribution / defective formula — the entry shape with evidence classed and annotated when its meaning goes stale, "how to add an entry"); modify `docs/adr/template.md` (relation slots: `Supersedes:` / `Refines:` / `Extends:` / `Promotes:`, one line each with the grain's meaning) and its starter mirror, `docs/adr/README.md` (step 6 pointer; a footer pointing at the register) and its mirror, `docs/adr/0000-…md` line 30 (decision revision vs claim correction) and its mirror, `.claude/hooks/protect-immutable-adrs.sh` (the block message names `docs/adr/corrections.md`; the `Allowed:` header lists it), `.claude/skills/adr-new/SKILL.md` (three relationships plus `Promotes:`; the `Extends?` and `Refines?` inputs; defers to the conventions for the sync targets, states no count, names one surface canonical on drift, treats the blueprint stack table as append-only — a one-line bullet only when a stack decision changes; a see-also to the register), `.claude/agents/adr-conformance-reviewer.md` (step 3 follows `Extends:`; the guard's asymmetric case; the register consulted so a flagged-wrong claim is not restated), `.claude/rules/testing.md` (a drift-guard test whose subject is the consistency itself, named as a fourth shape under § Three regimes' quick reference), `pr-review.md`'s sweep clause and `knowledge-backend.md`'s companion-material paragraph point at § Multi-surface facts (the sweep's mirror is already gone), `CLAUDE.md` § Architectural principles (a bullet), create `consultation/references/frozen_corpus_ingestion.md` (trigger: an operator points at a directory of planning material; the freeze discipline — read in full, cite verbatim in `## Pre-cascade sources`, never edit the source; the lazily created `-errata.md` companion — append-only, dated, "amends; never edits"; a `Promotes:` field on a decision lifted from the corpus; the enforcement set scaffold registers — a hook plus a CI job on the ADR pattern with a block message naming the errata, `.gitattributes` `linguist-documentation=false`, editor trim/newline unsets, a formatter skip entry), modify `consultation/SKILL.md` § Incoming context (the directory branch; the reference row), `consultation/references/notion_ingestion.md` (its own scope narrowed to Notion), `consultation/references/problem_brief_template.md` (§ Pre-cascade sources names the corpus path; a `## Handoff notes for later phases` section addressed to scaffold, blueprint, framing, rough-in by name), `scaffold/references/brownfield_audit.md` and `backend_selection.md` (inherit a designated corpus path; land the files and the errata companion; register the enforcement set), `cbk-conventions.md` § Surface inventory (the upstream-docs placeholder points at the pattern). + +- [ ] Checks: `diff -rq docs/adr .claude/skills/scaffold/references/adr-starters` (already in the block) stays green; the block gains `grep -q '^## Multi-surface facts' .claude/rules/cbk-conventions.md`, `grep -q 'Extends:' .claude/rules/cbk-conventions.md .claude/skills/adr-new/SKILL.md .claude/agents/adr-conformance-reviewer.md docs/adr/template.md` (each), `grep -q 'corrections.md' .claude/hooks/protect-immutable-adrs.sh docs/adr/README.md`, `[ -f .claude/skills/consultation/references/frozen_corpus_ingestion.md ]` with the SKILL.md routing to it. Commit: `feat(records): the Extends grain and the relation slots; the corrections register as a starter in both homes; multi-surface facts stated once with the companion vocabulary; the frozen-corpus ingestion pattern; adr-new defers to the conventions for its sync targets`. + +### Task 7: Supply chain, hygiene, the issue-less branch, the licence (C9) — #22 #23 #24 #31 #32 + +**Files:** modify `cbk-conventions-reference.md` § Dependency settle-window (age is necessary, not sufficient — the merged-but-unreleased-fix check, the git-rev pin with a retire trigger, the gated promote-at-registry-version path; toolchain and single-binary pins take the floor by hand with the settled age in the commit and a named tracking mechanism; inactive ecosystem stubs carry the floor; metadata-only lockfile diffs are discarded; a self-merge rule for bot PRs; behaviour deltas in security bumps accommodated narrowly; the `.gitattributes` counter-line rule with linguist's `generated.rb` and `docs/overrides.md` cited and dated, the `rust-lang/rust` precedent, the `text=auto` pre-check recorded inline) and a new `## .gitignore anchoring` section between it and § Methodology (anchor by default; deliberate any-depth entries marked in a comment; per-language sections scoped to the language's directory home once one is declared; a commit body for a `.gitignore` change states its pin assertions; harness transients ignored by anchored path; the note that no shipped reviewer restates this), `cbk-conventions.md` § Branch naming (the issue-less form `/` with the required PR-body statement "operator-directed maintenance; no cascade issue", the scope fence back to § Contribution intake, and the gate line rule: what stands in for the floor on such a PR is written as *not run* with the reason) and a new `## Licensing` section (a recorded choice including "none yet — all rights reserved"; the relicensing constraint at the second contributor; third-party asset licences bind independently; scaffold confirms the licence the way it confirms visibility), `.claude/hooks/protect-main-branch.sh` (the remediation names both branch forms), `blueprint/references/templates/contributing.md` § Branches and `scaffold/references/scaffold_output_template.md` § Working conventions (the issue-less form; a License row in Cascade metadata), `scaffold/references/github_only_profile.md` (Stage 2 licence confirmation; the `.gitignore` bullet at 77 and clause at 18), `scaffold/references/bootstrap_checklist_template.md` (line 20 points at the conventions; the licence row already there), `.claude/agents/cascade-rule-reviewer.md` (scope bullet gains Licensing and .gitignore anchoring), `.claude/rules/pr-review.md` § Pre-filters (the lock-file exclusion presumes the human can see the diff — cross-reference), `.claude/rules/tooling.md` (a `## MCP configuration` section: committed config with env-var references plus an example env file; servers wired by the phase that justifies them; list-valued keys never repeated in the local settings file; linter exclusions only where nothing could be actioned), `.claude/rules/workflows.md` § Cost+scope-explicit (money and agent-run quota named separately). + +- [ ] Checks: the block gains `grep -q '^## Licensing' .claude/rules/cbk-conventions.md`, `grep -q '^## .gitignore anchoring' .claude/rules/cbk-conventions-reference.md`, `grep -q 'short-slug>\` with' .claude/rules/cbk-conventions.md` (the issue-less form), `grep -q 'short-slug' .claude/hooks/protect-main-branch.sh`, `grep -q 'linguist-generated=false' .claude/rules/cbk-conventions-reference.md`. Commit: `feat(conventions): settle-window depth — age is necessary not sufficient, toolchain pins by hand, lockfile diffs kept visible; .gitignore anchoring; the issue-less branch with its PR statement; a licensing section scaffold confirms; MCP configuration in tooling`. + +### Task 8: What the producing phases emit (C10) — #27 #38 #28, the template halves of #26 #35 + +**Files:** create `scaffold/references/templates/cascade-events-index-template.md` (header with the append-only note; `## Index` table `# | Date | Phase | Artifact | Status` seeded with consultation and scaffold rows; `## Phase notes` with execution facts per note and the next runnable command; a `## Non-cascade surfaces` table); modify `scaffold/SKILL.md` § commit order (creates `docs/cbk/README.md` from the template beside `problem_brief.md` and `scaffold.md`) and § Reference files, `framing/SKILL.md` § Producing (check-if-exists, append the row and the note; create only as a brownfield fallback), `framing/references/test_cases.md` lines 12 and 22, `framing/references/inheritance.md` line 17 (the index is always present), the rough-in surfaces that misname the index `framing.md` (`planning-backend-commit.md:68,83`, `planning-backend-matrix.md:103`, `test_cases.md:26,34,80,88,152`) and the appenders' entry shapes reconciled (row plus note; rough-in's compression rationale and departures go in the note), `cbk-conventions.md`/`-reference.md` (§ Cascade artifact layout points at the template; § Mutation discipline's README row and § Quick reference name the creator and the appenders), `blueprint/SKILL.md` § Foundation document production (a canonical home per shared artifact — schema → the data-model ADR, interface → the seam ADR — before drafting, other drafters reference or splice; a cross-document critic on five axes — schema, interface, check names, versions, terminology — required when the drafting fanned out, skipped when one context drafted), `blueprint/references/blueprint-output-template.md` (an append-only `## Amendments` section; a retired-justifications table; a numbered PR lifecycle; a newcomer-traps section gated on the operator's tool comfort; a source-precedence order over non-cascade documents; an operator-runbook directory as a fifth foundation surface; a domain label with a body contract), `blueprint/references/handoff-issue.md` (the credential model extended to credentials not yet held), `framing/references/templates/frame-output-template.md` and `milestone-template.md` (a status column separating deferred metas from framing-time checks; absorbed-issue disposition completed, not cancelled; hard edges versus soft consumes-edges; prior verdicts bind; numbered rough issues naming their criterion; out-of-scope entries with reason and owner or reopen condition; re-sequencing pre-authorized; interface commitments numbered continuously and amendable with a dated note; findings marked as the grounding corpus with single-probe items flagged; deliberate non-decisions; a meta-issue names what does not land in it; orphaned obligations placed and recorded; charters at two blast radii — workstream-wide stays a pre-flight meta, milestone-wide becomes the first R-issue; the fourth spike outcome, retired as unsatisfiable), `framing/references/research-phase.md` and `rough-in/references/research-phase.md` (verdict-first committed corpus; three verdicts with unverifiable kept and labelled; verification targets the specifics; a gaps block; an attempted-URL log; refutations by consequence; usable-versus-raw counts; reproduction as the strongest check; interface over prose; a spec-verification stage — format, grounding, coverage — before the one-way commit; one research agent on the executor's own contract; a design doc's grounding line and anticipated-wrong-fix note; out-of-scope split into deferred and unverified; no-regression criteria name every reason; telemetry verified at its sink), `.claude/rules/tooling.md` § Code intelligence (the built-in LSP tool first-line, enabled by the official per-language plugin plus the language server on `PATH`, the cloud-session caveat; an MCP only for a language without a plugin; `Grep` last; the deferred-integrations table with trigger and expiry; the read-versus-write boundary per data integration) and `cbk-conventions.md` § Surface inventory's tooling line, the three commands' symbol-tooling sentences (`intake.md:33`, `pr-respond.md:73`, `enrich.md:44`), `pr-review.md` § What NOT to flag and `pr-review-reference.md` § anti-patterns (a docs-only PR is not automatically light where the docs are one-way doors — cascade artifacts, ADRs, the conventions). + +- [ ] Checks: the template exists and `scaffold/SKILL.md` cites it; `absent grep -rn "framing\.md index\|framing\.md event" .claude/skills/rough-in/`; `grep -q 'Amendments' blueprint-output-template.md`; `grep -q 'LSP' .claude/rules/tooling.md`; the framing template still carries `## Rough-in events` and `## Pre-flight checks` (existing pins). Commit: `feat(phases): the cascade-events index template scaffold creates and the appenders share; blueprint's canonical homes, critic, amendments and lifecycle sections; the framing template's emitted sections; the research phase's verdict-first corpus; LSP first-line in tooling`. + +### Task 9: Verification pins, indexes, test cases + +- [ ] Fold every check named in Tasks 1–8 into the block (kit sub-block; plain lines; `absent` with split literals). Sweep `CLAUDE.md` § Repo layout (the new templates and references; `docs/adr/corrections.md`) and § Architectural principles (multi-surface facts; the companion vocabulary), `README.md` (the tree; phase 2/3 outputs: the events index, the roadmap, the starters, the review workflows). Test cases: scaffold's `test_cases.md` (the index created; the licence confirmed; the label taxonomy full), blueprint's (the roadmap emitted on the right axes; the critic conditional), framing's (the roadmap row; the index appended not created), rough-in's Test 9 line (the completeness check). Run the block; record `always-loaded total:`. Commit: `chore(kit): verification pins for P4, index and README sweep, test cases`. + +### Task 10: Review pass and the PR + +- [ ] `/simplify` as a skill on `main...HEAD`; triage; Apply commits. Then `pr-review-toolkit:review-pr` (no args) concurrently with `review-sweep` (`base: main`, the changed-path list, no caller finder unless a dimension is missing; ≤ 25 planned) and one direct finder for **platform-claim sourcing** (every GitHub, linguist and Claude Code behaviour the diff asserts carries a page or a dated observation). Triage all; commit per surface. PR body in P3's shape: summary; `Closes` lines for the 24 issues; basis and declined asks (D-P4-1…7; #50's `/land` declined per D23; #19's stale caveat about the review gate, now moot); the review-gate block; the instruction budget; the triage block; the hand-off (the two audit flags from P3 remain; P2's real-dispatch row). `git push -u origin`, `gh pr create --draft --base main --title "feat: harvest 3 P4 — the github-issues axis, after the draft, decision records, hygiene, what the phases emit (C5–C7, C9, C10)"`. + +## Self-review against the spec + +C5 — starters (T1), traps and rulesets (T2), the roadmap (T3), the taxonomy (T1), the review question and notice (T4), the `gh` column (T4), the workflows (T4). C6 — the contract items (T5; three already landed in P3), the backward sweep (T5), the measurement variant (T5), the post-merge checklist and the capstone verdict (T3, T5). C7 — Extends, corrections, companion vocabulary, multi-surface facts, adr-new, the reference-register row and the handoff-notes companion (T6). C9 — settle-window, gitattributes, gitignore, the issue-less branch, licensing, MCP config and the cost line (T7). C10 — the index template, blueprint's sections, the framing template, the research phase, tooling, docs-only review (T8). Every task ends in the block and one commit; every artifact is generalized before it lands; every platform claim carries a source or a date.