@sabaiway/agent-workflow-kit 5.6.0 → 5.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.md +1 -1
  3. package/SKILL.md +1 -1
  4. package/capability.json +1 -1
  5. package/package.json +1 -1
  6. package/references/hooks/gate-approve.mjs +7 -1
  7. package/references/modes/doc-parity.md +1 -1
  8. package/references/modes/gates.md +16 -3
  9. package/references/modes/recommendations.md +3 -0
  10. package/references/modes/review-state.md +1 -1
  11. package/references/modes/setup.md +18 -2
  12. package/references/modes/upgrade.md +38 -18
  13. package/references/scripts/migrate-gates-branches.test.mjs +146 -1
  14. package/references/scripts/migrate-gates.mjs +295 -60
  15. package/references/scripts/migrate-gates.test.mjs +206 -14
  16. package/references/shared/deploy-tail.md +1 -1
  17. package/references/templates/gates.json +1 -1
  18. package/tools/ack-write.mjs +20 -11
  19. package/tools/atomic-write.mjs +71 -18
  20. package/tools/checker-claim.mjs +100 -0
  21. package/tools/coverage-producer.mjs +43 -6
  22. package/tools/direct-run.mjs +76 -0
  23. package/tools/doc-parity.mjs +34 -3
  24. package/tools/engine-source.mjs +12 -8
  25. package/tools/ensure-configs.mjs +141 -0
  26. package/tools/ensure-ops.mjs +284 -0
  27. package/tools/ensure-vocabulary.mjs +71 -0
  28. package/tools/gates-declaration.mjs +23 -10
  29. package/tools/gates-init.mjs +6 -3
  30. package/tools/hide-footprint.mjs +21 -3
  31. package/tools/lens-region.mjs +74 -23
  32. package/tools/orchestration-config.mjs +5 -3
  33. package/tools/orchestration-write.mjs +7 -0
  34. package/tools/recommendations.mjs +315 -66
  35. package/tools/refresh-parity.mjs +263 -0
  36. package/tools/run-gates.mjs +8 -5
  37. package/tools/setup-backends.mjs +88 -77
  38. package/tools/source-size-check.mjs +6 -16
  39. package/tools/source-size-core.mjs +7 -1
  40. package/tools/source-size-gate-cmd.mjs +18 -46
  41. package/tools/tracked-tree-census.mjs +102 -0
  42. package/tools/upgrade-runlist.mjs +92 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,66 @@ Semantically versioned ([semver](https://semver.org)), newest first. The `versio
4
4
  is the current release. `upgrade` mode reads a project's `docs/ai/.workflow-version` and applies
5
5
  every `migrations/<version>-<slug>.md` newer than it, in semver order.
6
6
 
7
+ ## 5.7.0 — what the kit emits is now something a test executed, a claim it proved, or a command you can run (AD-092 + AD-093 + AD-094; memory 4.3.0)
8
+
9
+ **Three feedback plans, one release.** A live upgrade session on a deployed TypeScript project
10
+ returned seven items of feedback, and every one reduced to the same shape: the kit EMITTED something
11
+ — a shell command, a status line, a prescribed operation, an optimality verdict — and nothing
12
+ downstream ever ran it, proved it, or performed it. This release closes that shape end to end, and
13
+ closes it for future releases too: a kit candidate can no longer be dispatched without proving
14
+ itself against the kit that is ALREADY published.
15
+
16
+ - **The canonical coverage destination refuses by name instead of writing to the filesystem root.**
17
+ The emitted cmd becomes `"${AW_GIT_DIR:?exported by run-gates}/agent-workflow-lcov.info"`:
18
+ byte-identical where the runner injects the variable, a loud named bash refusal where a human
19
+ pastes it into a bare shell — the old form expanded an unset variable to empty and sent the lcov
20
+ to `/`. Recognition is APPEND-ONLY (`KNOWN_COVERAGE_FLAG_SETS`): every form the kit ever emitted
21
+ stays recognized, so a deployed old-form gate is never reclassified as customized. `run-gates` now
22
+ reads `${VAR:?…}` as a producer reference and refuses before spawning uninjected. And the emitted
23
+ command is a FIXTURE the tests execute — asserted by running it, unset and injected both, never by
24
+ admiring the string.
25
+ - **"Flow optimal" became a claim the advisor must be able to SEE.** Binary probes gained NAMED third
26
+ outcomes: `gates-inert.producer-unrecognized` (a checker no recognized producer feeds, where the
27
+ tracked-tree census says most of the tree is outside the assessable domain — the remedy is "mark
28
+ the real producer or drop the checker", never a `node --test` prescription) and
29
+ `gates-inert.coverage-domain-narrow` (a live producer certifying an assessable minority — converges
30
+ only on an explicit acknowledgment, fingerprint-bound to the fact, re-firing when the fact
31
+ changes). The census is the predicate: no census, no optimality claim. A vendored copy of a core
32
+ check no longer stops the upgrade: the migration preserves it as a zero-diff `keep` row plus
33
+ separately rendered verify metadata, and only a genuine id collision on the canonical checker id
34
+ still hard-stops. On the advisor side, the source-size tool copy gets its own named outcomes —
35
+ `source-size.adopted-elsewhere` (optional) and `source-size.id-squatter` (attention, a
36
+ hand-apply recovery, never a stop).
37
+ - **The `lcovProducer` marker lets a declaration CLAIM a producer the closed cmd-world cannot name**
38
+ — vitest, pnpm wrappers, any runner that writes the lcov. Only the literal `true` counts, the
39
+ marker never widens what a RUN may certify (a marker-claimed producer that writes no lcov still
40
+ ends `skipped-no-lcov`), and it is FORWARD-ONLY by decision: the published 5.6.0 kit rejects a
41
+ marker-carrying `gates.json` loudly at validation (exit 5, naming the key) rather than degrading
42
+ to a false advisory — upgrade the kit before marking (Issue-016 states all three cross-version
43
+ axes).
44
+ - **Upgrade step 3 is a registry-owned run-list, not prose.** `tools/upgrade-runlist.mjs` owns the
45
+ seven operation identities (`pointers` · `footprint` · `configs` · `gates-migration` · `bridges` ·
46
+ `lens` · `bridge-settings`); the checklist in `references/modes/upgrade.md` renders from it, a
47
+ structure test holds doc ↔ registry in both directions, and the four config "ensures" the doc used
48
+ to prescribe as prose are ONE runnable command — `ensure-configs.mjs --reconcile [--dry-run]`,
49
+ fixed order, create-only seeds, a CLOSED outcome vocabulary that throws on any token outside it.
50
+ A library module invoked as a command now says so and exits 2 instead of silently exiting 0.
51
+ - **A tool-composed line a user reads is user-grade language.** Machine tokens and tool self-labels
52
+ ride machine lines (`[tool] key=value`); alarm words render only under a DETECTED abnormal
53
+ condition; the read-only refresh degrade states only what its re-scan PROVED (one scanner, closed
54
+ `clean-parity`/`drifted`/`unverifiable` vocabulary) instead of an unconditional "may be PARTIALLY
55
+ updated". The guard test enumerates every composer's outcome variants from its own closed branch
56
+ set — a new line joins the contract by construction. The hide-footprint report now states its
57
+ delta against the current managed block: `+N added` / `−N removed`, sets listed, `+0/−0` explicit.
58
+ - **A kit-carrying dispatch needs TWO receipts, dry-run included.** Beside the candidate smoke
59
+ (packs the candidate, installs it into a foreign fixture, asserts the advisor sees what this
60
+ repo's own suite cannot show), `scripts/release/cross-version-gate.mjs` installs the PUBLISHED kit
61
+ `@latest` and asserts the three Issue-016 axes BY NAME — `schema-accept` / `execution` /
62
+ `producer-recognition` — with the conditional arms decided by comparing the probed published
63
+ version against `MARKER_AWARE_SINCE` (fixed forever at `5.7.0`, the first marker-aware kit), never
64
+ inferred from the accept itself. Both receipts are HEAD-bound and field-validated; an unreachable
65
+ registry refuses loudly with no receipt.
66
+
7
67
  ## 5.6.0 — a source-size practice your project declares, and a record that is debt rather than permission (AD-091; engine 2.1.0)
8
68
 
9
69
  **A big module is expensive long before anyone calls it a problem, and nothing in the kit ever said
package/README.md CHANGED
@@ -229,7 +229,7 @@ file), or run the guarded `/agent-workflow-kit uninstall`.
229
229
  | `/agent-workflow-kit upgrade` | existing deployment | reads `docs/ai/.workflow-version`, shows the changelog diff, preserves your authored memory, applies migrations, re-stamps — then prints a **read-only** one-line backend-status line (what's set up vs missing); refreshes the already-placed bridges from the kit's bundled copies (never installs a new one — set one up with `/agent-workflow-kit setup`) |
230
230
  | `/agent-workflow-kit help` | any time | **read-only command index** — every command, grouped (Inspect / Configure / Orchestrate / Lifecycle) and tagged read-only / writer / guarded. The discoverable entry point, and where any unrecognized invocation lands (always read-only — a garbage invocation never writes). Never writes, never commits, never runs a subscription CLI. |
231
231
  | `/agent-workflow-kit backends` | any time | **read-only** check of the optional execution-backends (the `codex` / `agy` bridges): what's set up vs missing and the next step. Never writes, never commits, never runs a subscription CLI (credentials = marker-file presence, not a live login). |
232
- | `/agent-workflow-kit gates` | any time | **project gate runner** — runs the verification commands **your project itself declares** in `docs/ai/gates.json` (seeded at deploy; hand-editable `{ id, title, cmd }` entries, each `cmd` one bash line) as one batch: a per-gate **PASS/FAIL table** + one machine-readable summary line, exit 0 iff all green; a failing gate's own output is shown verbatim; `--only <id>` re-runs one. Honest distinct outcomes for a missing / empty / malformed declaration — never a silent green. The runner writes nothing **by default** and never commits — opt-in `--final` runs the FULL declared matrix and mints ONE final-run receipt into the git-dir core-evidence store (status green/red, pre/post tree fingerprints, the declaration, the consumed lcov's sha; the receipt the commit-guard binds); it executes only your own declared commands (a batching convenience, not a sandbox). The velocity tier auto-approves only the exact no-`--final` form — the recording run stays explicit. |
232
+ | `/agent-workflow-kit gates` | any time | **project gate runner** — runs the verification commands **your project itself declares** in `docs/ai/gates.json` (seeded at deploy; hand-editable `{ id, title, cmd }` entries plus the optional boolean `lcovProducer`, each `cmd` one bash line) as one batch: a per-gate **PASS/FAIL table** + one machine-readable summary line, exit 0 iff all green; a failing gate's own output is shown verbatim; `--only <id>` re-runs one. Honest distinct outcomes for a missing / empty / malformed declaration — never a silent green. The runner writes nothing **by default** and never commits — opt-in `--final` runs the FULL declared matrix and mints ONE final-run receipt into the git-dir core-evidence store (status green/red, pre/post tree fingerprints, the declaration, the consumed lcov's sha; the receipt the commit-guard binds); it executes only your own declared commands (a batching convenience, not a sandbox). The velocity tier auto-approves only the exact no-`--final` form — the recording run stays explicit. |
233
233
  | `/agent-workflow-kit setup [backend]` | opt-in, any time | **link-only** auto-setup of a bridge: places the bundled bridge skill (only into an absent / empty / managed dir — never overwrites an unmanaged one) + links its wrappers onto `PATH` via managed symlinks (idempotent; refuses to clobber a non-symlink; try `--dry-run` to preview). The binary install + the one-time subscription login stay **manual**: it prints the exact **login** command and points the binary install at each bridge's `setup/README.md`. POSIX wrappers — on Windows use WSL. Never commits, never runs a subscription CLI. |
234
234
  | `/agent-workflow-kit status` | any time | **read-only** single view of **versions + deployment + settings + bridges**: which members (kit / memory / engine / the two bridges) are installed and at what version (with an honest "installed on this machine" note when one is behind) and — in a project — what's deployed (`docs/ai`, the version stamps, and the **visibility**: visible / hidden / unclear), plus your settings (orchestration recipes, attribution, velocity) and the bridges' readiness. The two version axes (package number vs deployment-structure head) stay decoupled. Never writes, never commits, never runs a subscription CLI. |
235
235
  | `/agent-workflow-kit recipes` | any time | **read-only** orchestration advisor: presents four named recipes for composing the bridges into plan → execute → review — **Solo / Reviewed / Council / Delegated** — plans + recommends one for your environment (degrading with a stated reason when a backend isn't ready), and offers the choice. The orchestrator runs it via the bridge skills and **always commits**; the kit never executes a recipe, never runs a subscription CLI, never commits. |
package/SKILL.md CHANGED
@@ -3,7 +3,7 @@ name: agent-workflow-kit
3
3
  description: Deploy or upgrade a portable AI-agent memory-and-workflow system in any project. Use when the user wants to bootstrap `docs/ai/` + an entry-point `AGENTS.md` (+ `CLAUDE.md` alias) + cap/archive/index enforcement in a new or existing repo, set up the Memory Map and session protocols, install the docs-rotation pre-commit hook, or run `/agent-workflow-kit` / `/agent-workflow-kit upgrade`. Triggers on phrases like "set up the memory system", "deploy the AI workflow here", "bootstrap docs/ai", "upgrade the workflow".
4
4
  disable-model-invocation: true
5
5
  metadata:
6
- version: '5.6.0'
6
+ version: '5.7.0'
7
7
  ---
8
8
 
9
9
  # agent-workflow-kit
package/capability.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "schema": 1,
4
4
  "name": "agent-workflow-kit",
5
5
  "kind": "composition-root",
6
- "version": "5.6.0",
6
+ "version": "5.7.0",
7
7
  "provides": [],
8
8
  "roles": {},
9
9
  "detect": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sabaiway/agent-workflow-kit",
3
- "version": "5.6.0",
3
+ "version": "5.7.0",
4
4
  "description": "Portable, cross-agent memory & workflow for AI coding agents — Claude Code, Codex, Cursor, Devin Desktop. One command deploys an AGENTS.md entry point + docs/ai context with cap/archive/index enforcement into any repo.",
5
5
  "keywords": [
6
6
  "ai-agents",
@@ -175,6 +175,11 @@ const WHITESPACE_PATTERN = /\s+/u;
175
175
  const GATE_ID_PATTERN = /^[a-z0-9]+(-[a-z0-9]+)*$/;
176
176
  const GATE_CMD_NEWLINE_PATTERN = /[\r\n]/u;
177
177
  const GATE_KEYS = Object.freeze(['id', 'title', 'cmd']);
178
+ // The ONE optional gate key (the coverage-producer marker), accepted here for the same reason every
179
+ // other shape rule is restated: a declaration the RUNNER accepts must never be rejected here, or a
180
+ // marker-carrying project would silently lose auto-approval on gates the runner runs happily.
181
+ const LCOV_PRODUCER_KEY = 'lcovProducer';
182
+ const ALLOWED_GATE_KEYS = Object.freeze([...GATE_KEYS, LCOV_PRODUCER_KEY]);
178
183
  const README_KEY = '_README';
179
184
  const GATES_KEY = 'gates';
180
185
 
@@ -196,11 +201,12 @@ export const validateDeclarationShape = (parsed) => {
196
201
  for (const gate of parsed[GATES_KEY]) {
197
202
  if (gate === null || typeof gate !== 'object' || Array.isArray(gate)) return invalid;
198
203
  for (const key of Object.keys(gate)) {
199
- if (!GATE_KEYS.includes(key)) return invalid;
204
+ if (!ALLOWED_GATE_KEYS.includes(key)) return invalid;
200
205
  }
201
206
  for (const key of GATE_KEYS) {
202
207
  if (typeof gate[key] !== 'string' || gate[key].trim() === '') return invalid;
203
208
  }
209
+ if (gate[LCOV_PRODUCER_KEY] !== undefined && typeof gate[LCOV_PRODUCER_KEY] !== 'boolean') return invalid;
204
210
  if (GATE_CMD_NEWLINE_PATTERN.test(gate.cmd)) return invalid;
205
211
  if (!GATE_ID_PATTERN.test(gate.id)) return invalid;
206
212
  if (seenIds.has(gate.id)) return invalid;
@@ -13,7 +13,7 @@ The DOC-PARITY lint (BUGFREE-3 / AD-049, session-economics item (b)) — the det
13
13
  - `references/modes/autonomy-doctor.md` carries the doctor's frozen D7 contract (AD-044 Plan 2): every live EXIT-table phrase (`` `0` ready `` … `` `6` unsupported / untrusted ``), every status token (sourced from the exported `STATUS`), and the trusted-dir allowlist (`TRUSTED_DIRS`);
14
14
  - `references/modes/recommendations.md` + `references/modes/upgrade.md` carry the frozen upgrade-Recommendations presentation contract (the section header, the exact empty-state line, the verdict templates);
15
15
  - `references/modes/recommendations.md` + `references/modes/velocity.md` carry the family-owned ack-store path (`docs/ai/acks.json`);
16
- - `references/modes/setup.md` + `references/modes/upgrade.md` carry the `skipped-readonly` refresh degrade token.
16
+ - `references/modes/setup.md` + `references/modes/upgrade.md` carry the `skipped-readonly` refresh degrade token **and all three parity verdicts that outcome reports** — `clean-parity`, `drifted`, `unverifiable` (one binding per value, backticked so a bare word in prose cannot pass for the token).
17
17
 
18
18
  **Why the modes/*.md docs and NOT the tool HELP strings.** Every tool's HELP INTERPOLATES the same constant, so it can never drift from the code — there is nothing to check there. The hand-authored contract prose in `references/modes/*.md` is the surface that DOES drift, so that is exactly what this lint pins. Change a constant and the current-value token stops appearing in the lagging doc → the gate fails, forcing the doc update **in the same edit as the code** (the §2.6 "contract docs change in the same edit as code" rule, mechanized).
19
19
 
@@ -8,7 +8,7 @@ The **generic project gate runner** — it batches the project's OWN declared ve
8
8
 
9
9
  Run `node ${CLAUDE_SKILL_DIR}/tools/run-gates.mjs [--cwd <project>] [--only <id>]… [--final]`:
10
10
 
11
- 1. **Reads `docs/ai/gates.json`** (strict JSON, hand-editable; seeded from `references/templates/gates.json`). Each gate is `{ id, title, cmd }` — `id` a unique kebab handle, `cmd` **ONE bash command line** (brace/glob expansion works; a host without bash gets a loud preflight error, exit 6 — never a silent reinterpretation under another shell). The declaration names **WHAT to check, never who executes it** — the schema has no lane/model/routing fields and rejects unknown keys loudly.
11
+ 1. **Reads `docs/ai/gates.json`** (strict JSON, hand-editable; seeded from `references/templates/gates.json`). Each gate is `{ id, title, cmd }` plus the optional boolean **`lcovProducer`** (the producer paragraph below) — `id` a unique kebab handle, `cmd` **ONE bash command line** (brace/glob expansion works; a host without bash gets a loud preflight error, exit 6 — never a silent reinterpretation under another shell). The declaration names **WHAT to check, never who executes it** — the schema has no lane/model/routing fields and rejects unknown keys loudly.
12
12
  2. **Runs each gate from the project root** and prints a per-gate **PASS/FAIL table** plus **one machine-readable summary line** as the last line (`[run-gates] status=… gates=… passed=… failed=… failed_ids=… coverage=…`). A failing gate's own output is preserved **verbatim** (triage without re-running); a green gate's output is not echoed; gates after a failure still run. **Exit 0 iff all selected gates are green.**
13
13
  3. **Honest outcomes, each distinct — never a silent green:** a **missing** declaration (exit 3 — the report names the recovery: create `docs/ai/gates.json` from the template; `upgrade` re-seeds a missing one), an **empty** `gates` list (exit 4), a **malformed/invalid** declaration (exit 5, loud `path: reason`). Repeatable **`--only <id>`** re-runs a subset; an unknown id is a loud usage error (exit 2). **`--pre-review`** runs the DERIVED mechanical subset (#66): the full matrix minus every gate whose cmd is a canonical kit checker invocation — derivation **matches canonical checker paths in the cmd strings** (realpath-resolved `--check` forms of review-state / commit-guard / coverage-check / flow-check, never a project-authored id), so a project abstracting a checker behind its own script declares it in `flow.pregateExclude` (an unknown id refuses loudly, exit 5). A failing subset gate gets the review-dependent diagnosis, naming the mechanical reset (a declared exclude changes the `subsetDigest`). **Under an ARMED flow (exactly one open adopted chain owned by this worktree) every subset run is RECORDED** as a `subset-attempt` via the flow store's locked append factory — the context keys `{planId, cycle, stepId, foldBatch, subsetDigest}`; index + hard-stop state are computed under the lock against the pre-run identity. **Hard stop (Decision 7/8):** the SECOND red records and exits red; past two reds every attempt needs `--diagnosis "<non-empty, byte-distinct from the prior>"` (recorded, self-servable); the THIRD red EXHAUSTS the context — further solo runs refuse, and only a recorded fresh-eyes consult verdict (a grounded bridge consult-attestation at this round context) reopens ONE further attempt. Armed-but-unrecordable (zero/several open chains, broken store) refuses loudly; a spawn failure records NO attempt; unarmed repos stay byte-unchanged. Mutually exclusive with `--only`/`--final` (exit 2); plain and `--final` runs never load the config.
14
14
  4. **`--final`** — the D3(a) final verification run: it REFUSES `--only` (a subset never attests) and a declaration lacking the canonical core checks (ONE plain invocation each of the kit's OWN `review-state.mjs --check` and `coverage-check.mjs --check`, the checker declared LAST — a masked form, a compound, or a lookalike path never counts); deletes the stale git-dir lcov before the suite; exports `AW_GIT_DIR` + `AW_LCOV_FILE` to every gate cmd; records EVERY attempt (start + completed green/red) in the core-evidence store via its sole writer; and binds the receipt to { fingerprint before/after · the full declaration · per-gate results · the canonical red-proof + degrade evidence hashes · the sha of the lcov the checker actually read (exactly ONE `lcov-sha256` machine line, end-re-hashed) · **`evidenceHashes.flow`** when a flow store exists (D10: the sha of the OWNER-SCOPED flow projection — foreign worktrees never move it, except same-fingerprint planId-less globals, which share this tree's decision context; absent store → absent field; a broken store refuses up front) }. An artifact moving UNDER the run — the flow projection included — is a named `integrityFailure`; the receipt lands red. Stated residual: the movement arm is best-effort — an append racing the receipt write is refused at commit by the guard. A receipt that cannot be written is its own distinct outcome (exit 8): green gates never read as success without it. `${CLAUDE_SKILL_DIR}/references/modes/commit-guard.md` consumes the receipt at commit time (the guard re-hashes the live projection against it — a post-final append, or the store vanishing, refuses the commit).
@@ -21,11 +21,24 @@ Declared gates can also be **auto-approved** (no permission prompt on a byte-exa
21
21
 
22
22
  **Candidate line — the review-receipt gate (opt-in, never auto-seeded; AD-021).** Projects that configure a reviewed/council `plan-execution.review` recipe can declare the review-state check as one more gate — the exact candidate `{ id, title, cmd }` line and its contract live under `${CLAUDE_SKILL_DIR}/references/modes/review-state.md` (step 3).
23
23
 
24
- **Candidate line — the source-size gate (opt-in, never auto-seeded).** A project declaring the source-size practice (`docs/ai/source-size.json`) can declare its checker as one more gate, offered **only over a MINTED practice** — an absent config has nothing to offer, an authored-but-unminted one is withheld by name rather than declaring a gate that would refuse. Adoption is ONE consented verb, `node ${CLAUDE_SKILL_DIR}/tools/source-size-check.mjs --adopt --reason "<text>" [--cwd <project>]`: it mints the record and declares this gate and NOTHING else, is idempotent once adopted, and with no config refuses with the exact file to author (the practice's one manual step). The advisor surfaces it as the `source-size` item.
24
+ **Candidate line — the source-size gate (opt-in, never auto-seeded).** A project declaring the source-size practice (`docs/ai/source-size.json`) can declare its checker as one more gate, offered **only over a MINTED practice** — an absent config has nothing to offer, an authored-but-unminted one is withheld by name rather than declaring a gate that would refuse. Adoption is ONE consented verb, `node ${CLAUDE_SKILL_DIR}/tools/source-size-check.mjs --adopt --reason "<text>" [--cwd <project>]`: it mints the record and declares this gate and NOTHING else, is idempotent once adopted, and with no config refuses with the exact file to author (the practice's one manual step). The advisor surfaces it as the `source-size` item, reading a declared cmd as one of THREE claims rather than a yes/no — **canonical** (this copy — answered), **tool-elsewhere** (the same shape resolving to a DIFFERENT real copy — a vendored deployment, converging on a `source-size-copy` acknowledgement, never on `--adopt`, which would collide on the declared id), **not-the-tool** (a gate merely CARRYING the id is a squatter: rename it, or repoint its cmd at this `--check` form) — in that precedence. An unminted record stays the same attention outcome for either copy, and no acknowledgement silences it.
25
25
 
26
26
  **Consent-gated filling — the init preview, not part of the runner (D9).** The template `gates.json` is seeded EMPTY; FILLING it is a consented preview at init (`node ${CLAUDE_SKILL_DIR}/tools/gates-init.mjs --cwd <project>`, dry-run by default — prints the derived entries and **writes NOTHING**; `--apply [--only <id>]…` writes exactly the consented entries on your explicit yes; add-only, id collisions refused). **Placement, not blind appending:** a consented entry lands at the END, except that a non-checker entry goes **BEFORE** a trailing canonical coverage checker — that checker must stay last, so appending after it would write a declaration the validator refuses, leaving a hand edit as the only lane on any final-capable declaration. Existing entries are never modified, removed or reordered. The offer derivation is **closed-world** (AD-052): only a terminating-class script NAME (test / lint / type-check / build — never dev/watch/serve, never a write-mode or release/publish/deploy variant) whose BODY is a member of the literal runner allowlist is offered — membership, never blocklist screening: the worst case is a legit command not offered, never a dangerous one offered. The offered cmd is the uniform hook-free **`COREPACK_ENABLE_NETWORK=0 <pm> exec -- <allowlisted-body>`** — `exec` runs a command, not a named script, so no pre/post hook can fire (npm/pnpm/yarn alike; never `<pm> run <name>`, which re-exposes hooks), and the Corepack env prefix blocks a hostile `packageManager` pin from fetching the PM binary before exec. npm is pinned `--offline --script-shell /bin/sh`; pnpm/yarn refuse an absent runner without network (a user-installed cache/global/PATH runner executing is user machine state — part of the disclosed residual); a family without a verified fail-closed exec contract is WITHHELD loudly. A screened-out body is always named — and when nothing but kit checkers remains, the preview says so in plain words: the offer carries **no project-verification gate at all**. **Disclose before the yes** (the preview prints it): gates.json is a PRIVILEGED file — the wired hook auto-approves byte-exact declared commands — and a script gate runs project-controlled tooling the preview does not sandbox (safe-by-construction = the OFFER DERIVATION). At upgrade the only gates.json writer is the consented legacy migration (`${CLAUDE_SKILL_DIR}/references/modes/upgrade.md`).
27
27
 
28
- **The coverage PRODUCER and the canonical checker are declared together or not at all.** `coverage-check` READS an lcov; something must WRITE it, and a checker with no producer PASSES (`skipped-no-lcov`) certifying nothing. A **producer** is a CLOSED set of full command forms, never a substring probe: the suite body `node --test --experimental-test-coverage --test-reporter=lcov --test-reporter-destination="$AW_GIT_DIR/agent-workflow-lcov.info" --test-reporter=spec --test-reporter-destination=stdout` (optionally + the project's own test paths), bare or behind ONE verified per-PM `exec` wrapper. A cmd that merely MENTIONS the destination (in an `echo`, as a bare substring) or carries a PARTIAL flag set is **not** one. The tail must be path-shaped — path/glob/quoting bytes only, nothing that could sequence, redirect or substitute a command, no plain leading `-`. **Residual:** the screen judges SOURCE bytes — brace *sequence* expansion can produce others (`{Y..a}`), though bash never re-scans an expansion result as syntax, so such a byte is literal argument data, not an operator; the leading-`-` rule is first-order only (`'--flag'`, `{path,--flag}` pass); neither proves the lcov SURVIVES — "producer" means *configured with the reporters*, and a run producing none is caught at runtime as `skipped-no-lcov`. The destination rides `AW_GIT_DIR`, exported to every gate child on plain and `--final` runs alike (`AW_LCOV_FILE` is `--final`-only), so one cmd survives the unmet-producer preflight in both modes. On BOTH declaration paths: the fill preview WIRES those reporters onto a `node --test` body — the one allowlist member producing lcov unaided, every other body is emitted unchanged — WITHHOLDS the `coverage-check` candidate, with a named note, when neither the offer nor the declaration carries a producer; and `--apply` REFUSES a checker with no producer, a checker that is not LAST (an ORDERING refusal about entries that are ALREADY declared — reorder by hand; the fill places new entries before a trailing checker but never reorders what it did not write), or a SECOND canonical checker. The legacy migration likewise never ADDS the checker into a producer-less declaration, and reports an already-declared one as INERT. A producer declared AFTER the checker leaves it just as inert (it reads nothing, or stale bytes) — ORDER is the rule. An already-declared inert pair, and a matrix of nothing but kit checkers, are surfaced by the advisor's `gates-inert` item (`${CLAUDE_SKILL_DIR}/references/modes/recommendations.md`); its cause-A remedy is the fill preview when the checker is LAST and the project's own scripts yield an offerable producer (the placement rule lands it before the checker), and HAND-APPLY otherwise.
28
+ **The coverage PRODUCER and the canonical checker are declared together or not at all.** `coverage-check` READS an lcov; something must WRITE it, and a checker with no producer PASSES (`skipped-no-lcov`) certifying nothing. A **producer** is a CLOSED set of full command forms, never a substring probe: the suite body `node --test --experimental-test-coverage --test-reporter=lcov --test-reporter-destination="${AW_GIT_DIR:?exported by run-gates}/agent-workflow-lcov.info" --test-reporter=spec --test-reporter-destination=stdout` (optionally + the project's own test paths), bare or behind ONE verified per-PM `exec` wrapper. A cmd that merely MENTIONS the destination (in an `echo`, as a bare substring) or carries a PARTIAL flag set is **not** one. The tail must be path-shaped — path/glob/quoting bytes only, nothing that could sequence, redirect or substitute a command, no plain leading `-`. **Residual:** the screen judges SOURCE bytes — brace *sequence* expansion can produce others (`{Y..a}`), though bash never re-scans an expansion result as syntax, so such a byte is literal argument data, not an operator; the leading-`-` rule is first-order only (`'--flag'`, `{path,--flag}` pass); neither proves the lcov SURVIVES — "producer" means *configured with the reporters*, and a run producing none is caught at runtime as `skipped-no-lcov`. The destination rides `AW_GIT_DIR`, exported to every gate child on plain and `--final` runs alike (`AW_LCOV_FILE` is `--final`-only), so one cmd survives the unmet-producer preflight in both modes; the `:?` is the required-parameter form, so a hand-run with `AW_GIT_DIR` unset or empty fails naming it instead of writing the lcov to the filesystem ROOT (a STALE exported value is not caught), and the preflight counts it as a reference (a `${VAR:-…}` fallback stays unmatched). Recognition is APPEND-ONLY — every destination form the kit has emitted stays a producer, so an earlier kit's declaration never re-reads as customized; emission uses the newest alone. Recognition never WIDENS: a suite the closed set cannot express declares itself with **`"lcovProducer": true`** — the ONE other way to be a producer. Only the literal `true` claims (a string is a loud refusal); the claim binds the DECLARATION, never the run (a marked gate producing no lcov still ends `skipped-no-lcov` / `attested=no`); and it is FORWARD-ONLY — an older kit rejects a marker-carrying declaration by name (exit 5) instead of reading a claim it cannot honor. On BOTH declaration paths: the fill preview WIRES those reporters onto a `node --test` body — the one allowlist member producing lcov unaided, every other body is emitted unchanged — WITHHOLDS the `coverage-check` candidate, with a named note, when neither the offer nor the declaration carries a producer; and `--apply` REFUSES a checker with no producer, a checker that is not LAST (an ORDERING refusal about entries that are ALREADY declared — reorder by hand; the fill places new entries before a trailing checker but never reorders what it did not write), or a SECOND canonical checker. The legacy migration likewise never ADDS the checker into a producer-less declaration, and reports an already-declared one as INERT. A producer declared AFTER the checker leaves it just as inert (it reads nothing, or stale bytes) — ORDER is the rule. An already-declared inert pair, and a matrix of nothing but kit checkers, are surfaced by the advisor's `gates-inert` item (`${CLAUDE_SKILL_DIR}/references/modes/recommendations.md`); its cause-A remedy is the fill preview when the checker is LAST and the project's own scripts yield an offerable producer (the placement rule lands it before the checker), and HAND-APPLY otherwise.
29
+
30
+ **The marker as it appears in a declaration** — the three required keys plus the boolean, nothing else:
31
+
32
+ ```json
33
+ { "gates": [
34
+ { "id": "suite", "title": "Test suite", "cmd": "vitest run --coverage", "lcovProducer": true },
35
+ { "id": "coverage-check", "title": "Coverage", "cmd": "node \"${CLAUDE_SKILL_DIR}/tools/coverage-check.mjs\" --check" }
36
+ ] }
37
+ ```
38
+
39
+ **A declared checker is read in THREE outcomes, not two.** `gates-inert.producer-unrecognized` — no producer anywhere in the declaration, over a tracked tree the changed-line domain cannot reach: the remedies are the marker above or dropping the checker, never a `node --test` prescription and never an acknowledgement (a dead pair is broken, not narrow). `gates-inert.coverage-domain-narrow` — the pair IS live and the tree is still dominated by what the domain excludes: certification covers the assessable minority, a FACT that converges on a `coverage-domain` acknowledgement. The census is the TRACKED tree (`git ls-files`) in this same closed vocabulary; dominance is STRICT (a tie, or a lone `.d.ts` beside real JS, never fires), and a tree it cannot read is a stated skip — no census, no optimality claim. A producer declared AFTER the checker is neither outcome: it keeps the ORDERING remedy above. Each outcome's convergence and posture note live in `${CLAUDE_SKILL_DIR}/references/modes/recommendations.md`.
40
+
41
+ **A marker-carrying declaration needs a CURRENT placed hook.** The gate-approval hook restates this schema in its own baked copy and goes dark on any key it does not know (`${CLAUDE_SKILL_DIR}/references/modes/hook.md`), so a declaration carrying the key under a hook that predates it silently switches auto-approval off — every gate prompting again, no error anywhere. The condition is the key's PRESENCE: an older hook rejects a key it does not know whatever the value, so `"lcovProducer": false` darkens it exactly as `true` does. `gate-hook --apply` places only an ABSENT target, so the recovery is remove-then-reseed by hand: `rm <project>/.claude/hooks/agent-workflow-gates.mjs`, then `node ${CLAUDE_SKILL_DIR}/tools/gate-hook.mjs --apply --cwd <project>`.
29
42
 
30
43
  **`coverage=` states what the run can honestly say about coverage.** A CLOSED four-value set, always present, DETAIL only — exit code, `status=`, the receipt status, `--final` acceptance and the commit-guard disposition are untouched. `coverage=certified` — the checker consumed an lcov and ISSUED a verdict, pass **or** fail (one listing uncovered lines still reads certified and still exits 1). `coverage=not-run` — the checker ran and issued NO verdict (no lcov bytes were read, or the run holds no attestation context). `coverage=none` — no canonical checker ran here (an `--only` subset, the `--pre-review` subset). `coverage=unknown` — the run ended before the gates produced a signal (missing / empty / malformed declaration, no bash, a pre-spend refusal), or that signal is unreadable: the checker could not spawn, or its two anchored lines are missing, duplicated, or CONTRADICTORY (`attested=yes` over `lcov-sha256=none` certifies nothing) — fail closed. The value is DERIVED by cross-reading both anchored machine lines, the bytes the `--final` receipt binds, so it can never disagree with what the checker printed; the checker's table row names a withheld verdict in the same words, and a `--final` receipt RECORDS the token (`none` never rides one — a final run always selects the checker).
31
44
 
@@ -29,6 +29,9 @@ Run `node ${CLAUDE_SKILL_DIR}/tools/recommendations.mjs --cwd <project-root> [--
29
29
 
30
30
  - `gates-inert` — the gate matrix is DECLARED but verifies nothing, and the two causes have different remedies, so the item renders a different apply for each. **Cause A** — a canonical `coverage-check` gate with no producer gate declared BEFORE it (none at all, or one declared after it, which writes the lcov too late): the run certifies no coverage of its own — `coverage=not-run` when nothing wrote an lcov, or a verdict over STALE bytes an earlier run left in the git dir, which is worse because it reads as `coverage=certified`. This item is what surfaces that state at upgrade instead of leaving it to be noticed. The remedy needs the producer to run BEFORE the checker — declared or MOVED there — and what the fill can do about that is exactly what the apply says. When the checker is the LAST declared gate and the project's own scripts yield an offerable producer, the fill PLACES that producer before it, so the apply is the ordinary `gates-init` dry-run preview. Otherwise — no offerable producer, or a checker that is not last — this arm stays **HAND-APPLY**: the maintainer edits `docs/ai/gates.json` (the exact producer form lives in `${CLAUDE_SKILL_DIR}/references/modes/gates.md`), never you, never the kit, because the fill never reorders entries it did not write. **Cause B** — every declared gate is one of the kit's own canonical checkers, so the matrix runs no project-verification command at all: the apply is the `gates-init` dry-run preview, the same consent-gated seeder the `gates-declaration` item renders, and after the SAME confirmation you run the `--apply` line it prints. Neither cause changes any gate result, exit code or receipt — the item adds no enforcement, only the offer. Risk profile: `docs/ai/gates.json` is a PRIVILEGED file (a declared gate is auto-approvable by the wired hook), so both causes land in the same place — where the fill can help, it declares a project-controlled command on your explicit yes; where it cannot, the edit stays maintainer-only.
31
31
 
32
+ - `source-size` — the practice's arms differ by what the declared gate actually IS. **`adopted-elsewhere`** — the gate runs a DIFFERENT real copy of this checker (a vendored deployment): the practice is running, the advisor simply cannot resolve it as its own sibling, and the realpath anchor deliberately does not widen (it is what stops a lookalike from certifying). So the convergence is the ack writer's preview (`--lane source-size-copy` → `sourceSizeCopyAck`), never `--adopt`, which would mint the record and then be refused by the fill on the id already declared. The fingerprint binds the declared claims AS AUTHORED, sorted — not their resolved paths, which are machine-specific and would churn a committed ack between machines — so a second external copy re-fires it. **`id-squatter`** — a gate carries the `source-size` id and is not this checker under any reading: the id claims the practice while nothing measures module size, so it is **HAND-APPLY** (rename the id, or repoint the cmd at this checker's `--check` form). **`unminted`** stays what it was for both copies: a gate certain to refuse on every run, attention, and NO acknowledgement silences it — only the rendered verb differs (with the gate already declared elsewhere the missing half is the record alone, so the line is `--write-baseline`). Risk profile: `docs/ai/gates.json` is a PRIVILEGED file, so every arm here either records an acknowledgement or hands you an edit — the kit declares nothing on its own.
33
+ - `gate-hook` — the base arm is the ordinary opt-in wiring offer. The **`marker-stale`** arm is not: the placed hook validates your declaration through its OWN baked copy and goes dark on any key it does not know, so a declaration carrying the `lcovProducer` key under a hook that predates it silently turns auto-approval OFF, and every gate prompts again with no error anywhere. The condition is the key's PRESENCE, not its value — an older hook rejects a key it does not know whatever that key says, so `"lcovProducer": false` darkens it exactly as `true` does. The arm is deliberately marker-scoped — a stale hook is otherwise harmless — and its recovery is the writer's own: `gate-hook --apply` places only an ABSENT target, so converging means deleting the placed hook and re-placing it. That makes it a destructive **HAND-APPLY** (`rm` + `--apply`, absolute path so it can only delete this project's hook), never something the consent flow runs for you. When the read-lane is also enabled, this arm and `read-lane.stale` would report the same file with the same recovery, so exactly one renders — **this one**, because its cause is the true one: a hook that postdates the read-lane and merely predates the marker key reads `lanes.json` perfectly well, and the read-lane arm's wording would be false over it. Risk profile: deleting one placed hook file, then re-placing it from the bundle; the declaration is never touched.
34
+ - ADDITIONAL `gates-inert` arms (the third outcomes) — two further arms, and they differ in whether anything is BROKEN. **`producer-unrecognized`** — a checker with no producer anywhere in the declaration, on a tracked tree the changed-line coverage domain cannot reach (`.ts`/`.tsx`/`.jsx`/`.mts`/`.cts` strictly outnumber `.mjs`/`.cjs`/`.js`): the dead pair is real, so this arm is **HAND-APPLY** and the two remedies are marking the real producer with `"lcovProducer": true` or dropping the checker. Never a `node --test` prescription over a project that has no such suite, never the fill preview, and never an acknowledgement — a dead pair is broken, not narrow, and removing a producer after an acknowledgement lands right back in this arm. **`coverage-domain-narrow`** — the producer/checker pair IS live and the tree is still dominated by what the domain excludes: nothing is broken, and the honest sentence is that certification covers the assessable minority. Its apply is the consent-gated **ack writer** preview (a NEUTRAL fingerprint into `docs/ai/acks.json` as `coverageDomainAck`, never a security key); after the SAME confirmation you run the `--apply` it prints. The fingerprint binds the FACT — the verdict plus the unsupported extensions present, never the file counts — so an acknowledged project stays quiet as it grows and re-fires when a new unsupported language arrives or the verdict flips. The census reads the TRACKED tree with a read-only `git ls-files`; a tree it cannot read (a non-git deployment) becomes a stated skip, so optimality is withheld rather than assumed. Risk profile: no enforcement change of any kind — one acknowledgement recorded in a family-owned file, and one hand edit that stays the maintainer's.
32
35
  - `adr-store-migration` — other items write project files too; what is unique here is that the crossing **overwrites and deletes files the project already has**: it replaces the deployed enforcement scripts in `scripts/` (the directional subset — only basenames the project already has; a locally-edited copy is snapshotted first, never silently clobbered) and, where a retired archive file exists, DELETES it once conservation has been proven. That is why it is **HAND-APPLY** and why the command shown in the apply slot is a **`--dry-run`** — it writes nothing and prints the whole plan. `--apply` is a SEPARATE step, run only after that plan has been shown and **fresh consent** obtained for it; the consent flow executes only the apply slot, so an item that needs consent AFTER its preview cannot use that lane at all. Every write is idempotent and the run is re-runnable to completion after any interruption, so a re-run repairs rather than double-applies. It never commits. Risk profile: overwrite + delete of existing project files, gated on a preview you have actually read.
33
36
 
34
37
  **Sandbox lanes (what to DO with the `sandbox-lane` recipe, per host class):**
@@ -8,7 +8,7 @@ Run `node ${CLAUDE_SKILL_DIR}/tools/review-state.mjs [--check] [--json]`:
8
8
 
9
9
  1. Plain run → the human report: resolved recipe + source, plan-in-flight, tree fingerprint, per-backend receipt state (current / stale / ungrounded / probe / rejected / missing) with verdict + grounding + timestamp.
10
10
  2. **`--check`** → the gate exit code. The **normative exit contract lives in the tool header** (the single home — do not re-enumerate it elsewhere): exit 0 for a CONFIGURED solo recipe (a computed readiness-degrade NEVER silently becomes solo — it needs the explicit degrade record below), no plan in flight (the `docs/plans` naming convention: `queue.md` and `EXECUTE-`/`FEEDBACK-`-prefixed or `PROMPT`/`prompt`/`handoff`-carrying names are scratch), a clean tree, a non-git cwd, or every recipe-named backend SATISFIED for the current tree. **A clean-tree PASS under a NON-SOLO review obligation is never silent about a latent arm:** when a `reviewed` or `council` recipe is configured, it NAMES every plan in flight and states that this gate arms as soon as the tree is dirty — the condition is discoverable BEFORE it blocks, instead of surfacing at the worst moment (a pending commit, or the landing of a feature worktree when main first turns dirty). Nothing is announced under a configured `solo` recipe or a non-git cwd, where the gate can never arm. **Satisfaction is ship-class-only on the LATEST NORMAL receipt (D3(b)):** per backend, the latest probe-free current-fingerprint receipt is selected FIRST and THEN verdict-checked — only the recognized ship-class vocabulary (`ship` / `ship with nits`) satisfies; a recognized NEGATIVE (`revise` / `rethink` / `rework`) is an authoritative VETO (an earlier ship never survives a later revise); an UNRECOGNIZED verdict (e.g. `unknown` from a dead run) fails CLOSED unconditionally — a later `unknown` never lets an earlier SHIP stand, and a fresh normal re-run supersedes it. **The ONLY escape is an explicit degrade RECORD** (`core-evidence.mjs degrade --backend <name> --reason "…"`, fingerprint-bound to the current tree) — and never all backends: ≥1 non-degraded ship-class receipt is required whenever ≥1 backend is configured; a malformed evidence store denies the escape fail-closed but never fails an independently-satisfied tree. Exit 1 otherwise — missing, **stale** (ANY edit after a review moves the fingerprint), ungrounded, vetoed, or unrecognized. Plan/diff receipts and continuations (`agy-review --continue`) are **informational-only**: after a fold, only a **fresh grounded re-run** (`codex-review code`; `agy-review code --facts @f`) restores green. **Probe receipts never attest either:** a `CODEX_PROBE=1` / `AGY_PROBE=1` review runs with the frontier-model/max-effort guard OFF, so the wrapper stamps `probe:true` and this checker excludes it — per receipt, so a real review at the same fingerprint still satisfies. **Silence is not a declaration:** a malformed *or* absent probe marker is rejected fail-closed and stated in the check line. Honest bound: receipts are **not authenticated** (a forger could write `probe:false` as easily as any other field) — like the rest of the receipt this is a self-discipline mechanism, not a security boundary.
11
- 3. **Wire it as a gate by hand OR via the explicit-consent init preview — never without consent (AD-021/D9).** The candidate line for your own `docs/ai/gates.json`: `{ "id": "review-state", "title": "Review receipts current for the uncommitted tree", "cmd": "node <path-to-this-skill>/tools/review-state.mjs --check" }` — with the path your project actually reaches the kit by, QUOTED so a path with spaces survives, executable from the project root. The consent-gated gates-init preview (`${CLAUDE_SKILL_DIR}/references/modes/gates.md`, consent-fill section) offers exactly this entry path resolved and quoted ONLY when your `docs/ai/orchestration.json` declares `reviewed`/`council` on `plan-execution.review` (the slot this checker enforces); it writes nothing without your explicit yes. Once declared, the opt-in `${CLAUDE_SKILL_DIR}/references/modes/hook.md` auto-approves it like any other declared gate — and `run-gates --final` requires this check among its canonical core gates.
11
+ 3. **Wire it as a gate by hand OR via the explicit-consent init preview — never without consent (AD-021/D9).** The candidate line for your own `docs/ai/gates.json`: `{ "id": "review-state", "title": "Review receipts current for the uncommitted tree", "cmd": "node <path-to-this-skill>/tools/review-state.mjs --check" }` — with the path your project actually reaches the kit by, QUOTED so a path with spaces survives, executable from the project root. The consent-gated gates-init preview — `node ${CLAUDE_SKILL_DIR}/tools/gates-init.mjs --cwd <project>` (dry-run: it prints the derived entries and writes NOTHING; `--apply --only review-state` writes exactly this one on your explicit yes) offers exactly this entry, path resolved and quoted, ONLY when your `docs/ai/orchestration.json` declares `reviewed`/`council` on `plan-execution.review` (the slot this checker enforces). The full consent-fill contract is in `${CLAUDE_SKILL_DIR}/references/modes/gates.md`. Once declared, the opt-in `${CLAUDE_SKILL_DIR}/references/modes/hook.md` auto-approves it like any other declared gate — and `run-gates --final` requires this check among its canonical core gates.
12
12
  4. **`--await [--timeout <s>]`** (BUGFREE-3 / AD-049) → BLOCK until every recipe-named backend is SATISFIED for the current tree (i.e. until `--check` would PASS), or the bounded timeout elapses (a loud exit 1; default 900s). Run it after dispatching the review bridges to WAIT for their receipts to land instead of hand-polling a pid: the durable completion signal is the **receipt**, never a process event (a harness "completed" notification fires early; a bridge's output late-flushes). It inherits the `--check` decision whole — a backend with a current-tree degrade RECORD stops being waited on (the shared decideCheck), so you never hand-`--await` around a known degrade. **An AUTHORITATIVE veto terminates the wait loudly BEFORE the deadline** (flow-orchestration Decision 4/#50): a landed recognized-negative verdict for the current tree is the dispatched review's *answer*, so `--await` exits 1 with `VETO — …` immediately instead of misclassifying it as a timeout — only a fresh review can move a landed negative, never waiting. Still read-only (it re-reads the receipts + the evidence store); solo / no-plan / clean-tree resolve instantly. For waiting on **one dispatch's arrival** (never obligation satisfaction) use the receipt-arrival deadline runner, `${CLAUDE_SKILL_DIR}/references/modes/receipt-deadline.md`.
13
13
 
14
14
  **Human residual (stated, accepted):** `git commit --no-verify` and receipt-file deletion/forgery remain possible — this is a self-discipline mechanism against silent process drift, not a security boundary.
@@ -16,8 +16,24 @@ Run `node ${CLAUDE_SKILL_DIR}/tools/setup-backends.mjs [<backend>] [--bindir <pa
16
16
  downgraded), and every outcome line is composed by the tool — paste verbatim. When the skills dir
17
17
  is **read-only this session** and the placed bridge is already at the bundled version, the
18
18
  equal-version re-sync it would run cannot write: that outcome is `skipped-readonly` — a **stated
19
- skip** (exit 0, not a failure) naming the current version, the skipped/incomplete re-sync, and the
20
- read-only cause; it never claims a re-sync ran, and any local drift persists until a writable rerun.
19
+ skip** (exit 0, not a failure) naming the current version and the read-only cause; it never claims
20
+ a re-sync ran. What it says about the tree is **never assumed** it runs a read-only **re-scan** of
21
+ the bundle-owned files **and** the wrapper links (the degrade returns before the re-link step, so
22
+ that axis is genuinely unreconciled) and reports exactly one **proven** verdict: `clean-parity` —
23
+ nothing the refresh manages differs — every file it would overwrite already matches, every node it
24
+ would add is present, every wrapper link and source mode is in place; `drifted` — something is
25
+ provably wrong, and it **names every item** with the recovery that actually applies to it. A
26
+ writable rerun **repairs** what it can bring to the required state (a differing file, an absent
27
+ bundled node, a wrapper it can re-link or chmod). It **REFUSES** anything else, by one rule: *a node
28
+ in the reconcile set that the rerun cannot be guaranteed to converge under the refresh's no-follow
29
+ and ownership policy* — for instance (a **non-exhaustive** illustration, never the contract) a
30
+ symlink standing where the refresh must write, a node of an incompatible kind, or a wrapper target
31
+ that is foreign or not a symlink at all. Refusals are named apart as resolve-by-hand-then-rerun; one
32
+ blanket "re-run to repair" would promise a repair that never happens. The **outcome line is the
33
+ authority on the exact cause** — each item carries its own; `unverifiable` — it **names every item
34
+ it could not compare** (a read or stat error, or a re-scan that could not run at all) and says the
35
+ remaining need for repair is unknown. Could-not-verify is never rendered as clean, and every verdict
36
+ keeps the exit code at 0.
21
37
  (A version-**behind** refresh blocked by the same read-only dir stays a loud `could not refresh`,
22
38
  its recovery pointing at a writable rerun.) Does not combine with `--dry-run`.
23
39
  - `--help`, `-h` — usage.
@@ -6,7 +6,17 @@ Requires: ${CLAUDE_SKILL_DIR}/references/shared/report-footer.md · ${CLAUDE_SKI
6
6
 
7
7
  1. Read `docs/ai/.workflow-version` (the project's stamped lineage). If missing, treat as a pre-versioned deployment and offer to re-bootstrap conservatively.
8
8
  2. **Never-downgrade gate — FIRST, before any write.** Compare the stamp to the **deployment-lineage head** (`3.0.0` — NOT this kit's package version). If the stamp is **greater than the head** or unparseable → **STOP and report**; do not touch a newer / unknown deployment at all (not even the methodology slot). This STOP is one of the few places the number is actionable (*Version disclosure* in `${CLAUDE_SKILL_DIR}/references/shared/report-footer.md`): show the user **the `docs/ai` structure version** their deployment carries versus the one this kit expects, plus the plain one-line two-axes note — naming it the structure version, **never** "lineage head".
9
- 3. **Reconcile the bounded pointers — stamp-independent, BEFORE the equal-head short-circuit.** Reached only when the stamp **≤ head**. Run `node ${CLAUDE_SKILL_DIR}/tools/inject-methodology.mjs reconcile <project>/AGENTS.md`. ONE call reconciles **three** pointers — the **workflow-methodology** pointer, the **orchestration-recipes** pointer (Solo / Reviewed / Council / Delegated, routing to `/agent-workflow-kit recipes`) right below it, and the **autonomy-policy** pointer (the `docs/ai/autonomy.json` read contract, routing to `/agent-workflow-kit set-autonomy`) below that — and is filled on **every** upgrade, idempotently (zero-diff when all are already present + filled), so even a legacy or current equal-head deployment gains them **without a lineage-head bump or a migration** (the `agent-workflow-memory` **package** template did get docs-only headroom trims for the chained pointers, but no deployed-`docs/ai` structure changed). Per slot it inserts an empty pair at its anchor if absent, preserves a customized pair verbatim, and STOPs (never edits) on a malformed pair, a missing / duplicate anchor, or **when a fill is needed but the installed `agent-workflow-engine` is absent/invalid** (the fragments are read live from it — see the distinct outcomes below).
9
+ 3. **The step-3 run-list — stamp-independent reconciles, BEFORE the equal-head short-circuit.** Reached only when the stamp **≤ head**. Run the **seven operations below in order, on every upgrade** (equal-head too) no lineage-head bump, no migration file. Each backticked id anchors its rationale block below the checklist; a consent marker is a CONDITIONAL gate an explicit yes is required exactly where the row's clause states one:
10
+
11
+ 1. `pointers` — `node ${CLAUDE_SKILL_DIR}/tools/inject-methodology.mjs reconcile <project>/AGENTS.md` → per pointer: added · already present · skipped (reported) · a hard STOP.
12
+ 2. `footprint` — `node ${CLAUDE_SKILL_DIR}/tools/hide-footprint.mjs --dir <project> --reconcile --dry-run` → visibility: visible · ambiguous · hidden — consent (conditional): ambiguous → ask which it is BEFORE anything; hidden → the conditional re-run without `--dry-run` (its surfaced paths ask per bootstrap step 9).
13
+ 3. `configs` — `node ${CLAUDE_SKILL_DIR}/tools/ensure-configs.mjs --reconcile --cwd <project>` → one line per ensure: `seeded` / `note-refreshed` / `already-current` / `customized-preserved` / `malformed-preserved` / `already-present` / `skipped-no-node` / `old-adr-layout-migration-instructed` / `failed`.
14
+ 4. `gates-migration` — `node ${CLAUDE_SKILL_DIR}/references/scripts/migrate-gates.mjs --kit-tools ${CLAUDE_SKILL_DIR}/tools --cwd <project>` → the preview plan · INERT checker · CUSTOMIZED entries, each named — consent: apply only on an explicit yes, re-run with `--apply`.
15
+ 5. `bridges` — `node ${CLAUDE_SKILL_DIR}/tools/setup-backends.mjs --refresh-placed` → per-bridge lines: refreshed · already current · skipped — with its stated reason (not placed / newer than the bundle / unsupported host) · `skipped-readonly` · could not refresh.
16
+ 6. `lens` — `node ${CLAUDE_SKILL_DIR}/tools/lens-region.mjs reconcile <project>/docs/ai/agent_rules.md` → per section: refreshed · already current · custom edit preserved · file absent / engine too old — skipped · over the line cap — refused · section absent — noted · a fully absent/invalid engine → hard STOP.
17
+ 7. `bridge-settings` — `node ${CLAUDE_SKILL_DIR}/tools/bridge-settings.mjs --reconcile` → outcome line(s), pasted verbatim: ok · absent · flagged · duplicates · unusable.
18
+
19
+ **`pointers` — the bounded pointers.** ONE call reconciles **three** pointers — the **workflow-methodology** pointer, the **orchestration-recipes** pointer (Solo / Reviewed / Council / Delegated, routing to `/agent-workflow-kit recipes`) right below it, and the **autonomy-policy** pointer (the `docs/ai/autonomy.json` read contract, routing to `/agent-workflow-kit set-autonomy`) below that — and is filled on **every** upgrade, idempotently (zero-diff when all are already present + filled), so even a legacy or current equal-head deployment gains them **without a lineage-head bump or a migration** (the `agent-workflow-memory` **package** template did get docs-only headroom trims for the chained pointers, but no deployed-`docs/ai` structure changed). Per slot it inserts an empty pair at its anchor if absent, preserves a customized pair verbatim, and STOPs (never edits) on a malformed pair, a missing / duplicate anchor, or **when a fill is needed but the installed `agent-workflow-engine` is absent/invalid** (the fragments are read live from it — see the distinct outcomes below).
10
20
 
11
21
  **Classify the exit — there are THREE non-zero exits + one soft in-band skip; handle each differently:**
12
22
 
@@ -18,19 +28,21 @@ Requires: ${CLAUDE_SKILL_DIR}/references/shared/report-footer.md · ${CLAUDE_SKI
18
28
 
19
29
  **No-Node project:** the fragments live only in the **installed `agent-workflow-engine`** (`references/methodology-slot.md` + `references/orchestration-slot.md` + `references/autonomy-slot.md`, under `~/.claude/skills/agent-workflow-engine` or `$AGENT_WORKFLOW_ENGINE_DIR`) — there is no bundled copy, and a No-Node host cannot run the `npx` engine install. Open `AGENTS.md` and classify **each** pointer by hand: a **filled / customized** pair → leave it verbatim (no engine needed); a **malformed** pair (not exactly one ordered `start → end`) → STOP, do not edit. A pair that needs filling — **absent markers OR a present-but-empty pair** — needs the engine's fragment, so: if the engine is **not installed**, that pointer **cannot be added** — report it plainly (the methodology is already in `docs/ai/agent_rules.md`; the recipes are available via `/agent-workflow-kit recipes`; the autonomy policy via `/agent-workflow-kit set-autonomy`; install the engine to add the pointers). If the engine **is** present, **count the lines first** — if adding/filling would take the file over 100 lines, **skip that pointer and report the skip** (methodology first, then orchestration, then autonomy; each chained pair sits right under the previous pair's end marker). Fill each empty pair from its engine fragment (`methodology-slot.md` / `orchestration-slot.md` / `autonomy-slot.md`) — never inline a copy (that would re-create the retired mirror).
20
30
 
21
- **Hidden-mode footprint reconcile stamp-independent, same gate, BEFORE the equal-head short-circuit (D9 / AD-014).** A deployment does not record whether it chose `hidden`, so first **infer visibility**: `node ${CLAUDE_SKILL_DIR}/tools/hide-footprint.mjs --dir <project> --reconcile --dry-run` (writes **zero bytes**). It reports one of — **visible** (the entry point is tracked) → nothing to do; **ambiguous** (untracked but not ignored — could be a fresh uncommitted repo, or a hide that broke) → **ASK** the user which it is, never guess; **hidden** → re-run without `--dry-run` to migrate any older **machine-global** hide to the **project-local** `.git/info/exclude` (one managed block; folds in the legacy `.claude/skills/` line), idempotently (a clean re-run is zero-diff). Handle its surfaced paths exactly as bootstrap step 9 (`${CLAUDE_SKILL_DIR}/references/modes/bootstrap.md`) (already-committed → show `git rm --cached`, ask before `--include`; generic-name present file → ask; **leftover machine-wide ignore block → ASK before `--remove-global`**, default keep + report). No Node on the agent host / Windows → as bootstrap step 9 (`${CLAUDE_SKILL_DIR}/references/modes/bootstrap.md`). This runs on **every** hidden upgrade, like the methodology slot — no lineage-head bump, no migration file.
31
+ **`footprint`hidden-mode footprint reconcile (D9 / AD-014).** A deployment does not record whether it chose `hidden`, so first **infer visibility** the dry-run writes **zero bytes** and reports one of — **visible** (the entry point is tracked) → nothing to do; **ambiguous** (untracked but not ignored — could be a fresh uncommitted repo, or a hide that broke) → **ASK** the user which it is, never guess; **hidden** → re-run without `--dry-run` to migrate any older **machine-global** hide to the **project-local** `.git/info/exclude` (one managed block; folds in the legacy `.claude/skills/` line), idempotently (a clean re-run is zero-diff). Handle its surfaced paths exactly as bootstrap step 9 (`${CLAUDE_SKILL_DIR}/references/modes/bootstrap.md`) (already-committed → show `git rm --cached`, ask before `--include`; generic-name present file → ask; **leftover machine-wide ignore block → ASK before `--remove-global`**, default keep + report). No Node on the agent host / Windows → as bootstrap step 9 (`${CLAUDE_SKILL_DIR}/references/modes/bootstrap.md`). This runs on **every** hidden upgrade, like the methodology slot — no lineage-head bump, no migration file.
22
32
 
23
- **Orchestration config ensure (seed-or-refresh)stamp-independent, same gate, BEFORE the equal-head short-circuit.** Ensure `docs/ai/orchestration.json` exists **and its onboarding note is current**: **create it from the template if missing**; **if it already exists, preserve every activity/slot the user set, and refresh ONLY the `_README` note when the existing one still matches a known prior canonical** the tested `refreshIfCanonical` / `refreshReadme` in `tools/orchestration-config.mjs` is the source of truth for that decision (normalize CRLF/whitespace before comparing; a *customized* `_README` is preserved verbatim; a *malformed* existing config is **preserved + a loud warning**, never clobbered or silently skipped). The current note points at `/agent-workflow-kit set-recipe` (the config is now agent-writable no more "never written for you"). **The refresh helper is kit-owned** in the **delegated** path memory only seeds/preserves the file (memory upgrade step 2) and the **kit** then applies the `_README` refresh; in the **fallback** path the kit seeds-or-refreshes directly from `${CLAUDE_SKILL_DIR}/references/templates/orchestration.json`. (Memory stays standalone it never depends on this helper.) Like the pointer slots + the footprint reconcile, this reaches an equal-head deployment **without a lineage-head bump or a migration file** (it is a `.json`, inherently outside the docs cap-validator). Report it in the step 4 / step 8 success report (config *seeded* / *note refreshed* / *already current* / *customized — preserved*).
33
+ **`configs` the project-configuration ensures, ONE run.** The ONE command performs **all four** ensures described below orchestration config · gate declaration · autonomy declaration · enforcement scripts — in a fixed order, and prints **one outcome line per ensure**: paste those lines into the step 4 / step 8 success report. Every seed is **create-only** (an existing file is preserved byte-for-byte), the ONE refresh is the orchestration onboarding note while it still matches a canonical this kit shipped, and one ensure failing **never** skips the others: each reports its own outcome and the run exits non-zero when any of them `failed`. The outcome tokens, by ensure: orchestration → `seeded` / `note-refreshed` / `already-current` / `customized-preserved` / `malformed-preserved`; gates and autonomy `seeded` / `already-present`; scripts `seeded` / `already-present` / `old-adr-layout-migration-instructed` / `skipped-no-node`; and any ensure may report `failed`, whose line OPENS with the cause (relay it with that cause never soften it into a skip; an op that copies file by file also states when it stopped partway). **A non-zero exit STOPs this upgrade** report the failed line and stop there, before the equal-head exit, the migrations and the re-stamp. Add `--dry-run` to preview without writing a byte. Like the pointer slots + the footprint reconcile, all four reach an equal-head deployment **without a lineage-head bump or a migration file** (they are `.json` / `scripts/`, inherently outside the docs cap-validator).
24
34
 
25
- **Gate-declaration ensure (seed-if-missing) stamp-independent, same gate, BEFORE the equal-head short-circuit.** Ensure `docs/ai/gates.json` exists: **create it from `${CLAUDE_SKILL_DIR}/references/templates/gates.json` if missing** the kit's OWN template twin, so this works even when the installed memory substrate predates the gates feature (a stale memory never silently loses it); **an existing file is preserved byte-for-byte** (a project's declared gate matrix is authored content never clobbered, never refreshed in place; unlike the orchestration `_README` there is no note-refresh here). Report it in the step 4 / step 8 success report (*seeded* / *already present*). Like the config ensure, this reaches an equal-head deployment without a lineage-head bump or a migration file (a `.json`, inherently outside the docs cap-validator).
35
+ **What the orchestration-config ensure does.** `docs/ai/orchestration.json` must exist **and its onboarding note must be current**: created from the canonical seed if missing; if it already exists, **every activity/slot the user set is preserved** and ONLY the `_README` note is refreshed, and only when the existing one still matches a known prior canonical the tested `refreshIfCanonical` / `refreshReadme` in `tools/orchestration-config.mjs` is the source of truth for that decision (it normalizes CRLF/whitespace before comparing; a *customized* `_README` is preserved verbatim → `customized-preserved`; a *malformed* existing config is **preserved untouched + LOUD** `malformed-preserved`, never clobbered and never silently skipped). The current note points at `/agent-workflow-kit set-recipe`. **Kit-owned:** in the **delegated** path memory only seeds/preserves the file (memory upgrade step 2) and this ensure applies the `_README` refresh; in the **fallback** path it does both. (Memory stays standalone.)
26
36
 
27
- **Legacy gates.json migration (consented preview — D8).** An EXISTING declaration may still carry the retired review-ledger / fold-completeness checks. Run the preview `node ${CLAUDE_SKILL_DIR}/references/scripts/migrate-gates.mjs --kit-tools ${CLAUDE_SKILL_DIR}/tools --cwd <project>` (dry-run — writes NOTHING), show the user the exact plan, and only on an explicit yes re-run it with `--apply`: canonical legacy entries (matched by their documented single-invocation cmd forms) are REMOVED, the canonical `unit-tests` cmd gains the built-in lcov reporters, and the coverage-check gate is ADDED last — atomic and COMPLETE, so the migrated declaration satisfies `run-gates --final`. **The checker rides a PRODUCER or is not declared at all** (`${CLAUDE_SKILL_DIR}/references/modes/gates.md`): with no gate producing the lcov it reads, the migration does NOT add it, an already-declared one is reported INERT, the result is not called final-run-capable, and the preview prints the paste-ready suite cmd to declare by hand — nothing is ever removed for you. CUSTOMIZED entries are NEVER auto-touched: the preview names each with a paste-ready recovery, and the commit guard must NOT be installed until they are resolved. This is the ONLY gates.json writer at upgrade (the consented FILL preview runs at init).
37
+ **What the gate-declaration ensure does.** `docs/ai/gates.json` must exist: created from `${CLAUDE_SKILL_DIR}/references/templates/gates.json` if missing the kit's OWN template twin, so a memory substrate predating the gates feature never silently loses it; **an existing file is preserved byte-for-byte** (a project's declared gate matrix is authored content; unlike the orchestration `_README` there is no note-refresh here).
28
38
 
29
- **Autonomy-declaration ensure (seed-if-missing) — stamp-independent, same gate, BEFORE the equal-head short-circuit.** Ensure `docs/ai/autonomy.json` exists: **create it from `${CLAUDE_SKILL_DIR}/references/templates/autonomy.json` if missing** (the kit's OWN template twin, mirrored from memory so a stale memory never silently loses the seed); **an existing file is preserved byte-for-byte** (a declared policy is authored content — never clobbered, never refreshed in place). The seed is SPARSE (the onboarding note only) and **defaults-equivalent** — deploying it never changes behavior (the computed defaults stay the policy until the user declares levels with `/agent-workflow-kit set-autonomy` or by hand). Report it in the step 4 / step 8 success report (*seeded* / *already present, preserved*). Like the other config ensures, no lineage-head bump or migration file (a `.json`, outside the docs cap-validator).
39
+ **What the autonomy-declaration ensure does.** `docs/ai/autonomy.json` must exist: created from `${CLAUDE_SKILL_DIR}/references/templates/autonomy.json` if missing (the kit's own template twin — a stale memory never silently loses the seed); **an existing file is preserved byte-for-byte** (a declared policy is authored content). The seed is SPARSE (the onboarding note only) and **defaults-equivalent** — deploying it never changes behavior (the computed defaults stay the policy until the user declares levels with `/agent-workflow-kit set-autonomy` or by hand).
30
40
 
31
- **Enforcement-script ensure (seed-if-missing) — stamp-independent, same gate, BEFORE the equal-head short-circuit.** A deployment older than the ADR-cascade feature has no `scripts/archive-decisions.mjs`, and an equal-head exit would otherwise never deliver it. Ensure the pairs exist in the project's `scripts/`: **copy `archive-decisions.mjs` + `archive-decisions.test.mjs` and `markdown-blocks.mjs` + `markdown-blocks.test.mjs` from `${CLAUDE_SKILL_DIR}/references/scripts/` if missing**; **an existing file is preserved, never overwritten** (drift repair belongs to a lineage migration). Seed nothing else — the other tokenizer-era tests red beside OLD archivers. **OLD ADR-store layout — DETECT FIRST, never auto-migrate (AD-051, Decision 13):** if `status` surfaces `adrLayout: "old"` (a `docs/ai/history/decisions-archive*.md` monolith) or `"old-unrotated"` (no monolith, but the deployed rotator predates the store), it is on the RETIRED 3-tier cascade **do NOT seed or refresh the now one-file-per-ADR rotator here** (the new `archive-decisions.mjs` beside un-migrated monoliths would red their ADR gate). Instead surface a LOUD instruct: run the opt-in **`/agent-workflow-kit migrate-adr-store`** (consent-gated; previews first, never commits). The seed-if-missing above applies ONLY to a clean layout (neither signal). The deployed pre-commit hook gains the `archive-decisions.mjs --check` line only when the hook itself is next refreshed (re-run `node scripts/install-git-hooks.mjs` after the ensure and it will refuse a non-marker hook as always); an OLD hook without the line stays consistent-safe — the decisions gate is simply not enforced yet, never a broken hook. Skip this ensure on a No-Node project (the scripts are Node enforcement). Report it in the step 4 / step 8 success report (*added* / *already present* / *old ADR layout — migration instructed*).
41
+ **What the enforcement-script ensure does.** A deployment older than the ADR-cascade feature has no `scripts/archive-decisions.mjs`, and an equal-head exit would otherwise never deliver it. The pairs must exist in the project's `scripts/`: `archive-decisions.mjs` + `archive-decisions.test.mjs` and `markdown-blocks.mjs` + `markdown-blocks.test.mjs`, copied from `${CLAUDE_SKILL_DIR}/references/scripts/` if missing; **an existing file is preserved, never overwritten** (drift repair belongs to a lineage migration). Nothing else is seeded — the other tokenizer-era tests red beside OLD archivers. **OLD ADR-store layout — DETECTED FIRST, never auto-migrated (AD-051, Decision 13):** on a `docs/ai/history/decisions-archive*.md` monolith (`old`), or no monolith but a deployed rotator predating the store (`old-unrotated`), the project is on the RETIRED 3-tier cascade, so the ensure writes NOTHING and reports `old-adr-layout-migration-instructed` (the new `archive-decisions.mjs` beside un-migrated monoliths would red their ADR gate). Relay it as the LOUD instruct it is: the fix is the opt-in **`/agent-workflow-kit migrate-adr-store`** (consent-gated; previews first, never commits), and the seed lands on the next upgrade. A layout the ensure cannot READ is `failed`, not a seed — it never writes on an unverifiable tree. The seed applies ONLY to a clean layout (neither signal), and a project with no `package.json` at its root reports `skipped-no-node` (these are Node enforcement scripts) while the three config ensures still run. The deployed pre-commit hook gains the `archive-decisions.mjs --check` line only when the hook itself is next refreshed (re-run `node scripts/install-git-hooks.mjs` after the ensure and it will refuse a non-marker hook as always); an OLD hook without the line stays consistent-safe — the decisions gate is simply not enforced yet, never a broken hook.
32
42
 
33
- **Placed-bridge refreshstamp-independent, same gate, BEFORE the equal-head short-circuit.** Run
43
+ **`gates-migration` legacy gates.json migration (consented preview D8).** An EXISTING declaration may still carry the retired review-ledger / fold-completeness checks. Run the preview (dry-run — writes NOTHING), show the user the exact plan, and only on an explicit yes re-run it with `--apply`: canonical legacy entries (matched by their documented single-invocation cmd forms) are REMOVED, the canonical `unit-tests` cmd gains the built-in lcov reporters, and the coverage-check gate is ADDED last — atomic and COMPLETE, so the migrated declaration satisfies `run-gates --final`. **The checker rides a PRODUCER or is not declared at all** (`${CLAUDE_SKILL_DIR}/references/modes/gates.md`): with no gate producing the lcov it reads, the migration does NOT add it, an already-declared one is reported INERT, the result is not called final-run-capable, and the preview prints the paste-ready suite cmd to declare by hand — nothing is ever removed for you. CUSTOMIZED entries are NEVER auto-touched: the preview names each with a paste-ready recovery, and the commit guard must NOT be installed until they are resolved. This is the ONLY gates.json writer at upgrade (the consented FILL preview runs at init).
44
+
45
+ **`bridges` — placed-bridge refresh — stamp-independent, same gate, BEFORE the equal-head short-circuit.** Run
34
46
  `node ${CLAUDE_SKILL_DIR}/tools/setup-backends.mjs --refresh-placed` and **paste its per-bridge
35
47
  output lines verbatim** — every outcome line is composed by the tool (*refreshed* / *already
36
48
  current* / *skipped — not placed* / `skipped-readonly` / *could not refresh* + its recovery). It
@@ -39,12 +51,21 @@ Requires: ${CLAUDE_SKILL_DIR}/references/shared/report-footer.md · ${CLAUDE_SKI
39
51
  (placement stays the opt-in `${CLAUDE_SKILL_DIR}/references/modes/setup.md` — AD-009/AD-011 honesty intact), a placed bridge
40
52
  **newer** than the bundle is a stated skip naming the kit update (**never a downgrade**), and
41
53
  `skipped-readonly` is an equal-version re-sync a **read-only** skills dir blocked this session (a
42
- stated skip, exit 0 — not a failure). Runs on **every** upgrade (equal-head too), no lineage-head
43
- bump; a *could not refresh* line is non-fatal relay it plainly with its recovery.
44
-
45
- **Agent-rules lens refresh stamp-independent, same gate, BEFORE the equal-head short-circuit.**
46
- Run `node ${CLAUDE_SKILL_DIR}/tools/lens-region.mjs reconcile <project>/docs/ai/agent_rules.md`
47
- and relay its outcome in plain language: the planning/review lens section was **refreshed** to
54
+ stated skip, exit 0 — not a failure). That line never guesses at the tree: it carries one **proven**
55
+ verdict from a read-only re-scan of the bundle-owned files **and** the wrapper links
56
+ `clean-parity` (nothing the refresh manages differs), `drifted` (every item **named** with the
57
+ recovery that applies: a rerun REPAIRS what it can converge, and REFUSES *what it cannot converge
58
+ under the refresh's no-follow and ownership policy* — **non-exhaustively**: a symlink where it must
59
+ write, an incompatible node kind, a foreign wrapper target; those are named apart as
60
+ resolve-by-hand-then-rerun), or `unverifiable` (every uncomparable item **named**, the remaining
61
+ need for repair unknown). The line carries the exact cause per item — relay it as worded, never
62
+ soften `unverifiable` into "already fine", never relay a resolve-by-hand item as rerun-fixable.
63
+ Runs on **every** upgrade
64
+ (equal-head too), no lineage-head bump; a *could not refresh* line is non-fatal — relay it plainly
65
+ with its recovery.
66
+
67
+ **`lens` — agent-rules lens refresh.** Relay the outcome in plain
68
+ language: the planning/review lens section was **refreshed** to
48
69
  the current canon · was **already current** · carries a **custom edit — preserved verbatim** (+
49
70
  its one-line note) · the **file is absent — skipped** · the **engine is too old — skipped**
50
71
  (refresh it with `npx @sabaiway/agent-workflow-engine@latest init`, then re-run) · **over the
@@ -57,13 +78,12 @@ Requires: ${CLAUDE_SKILL_DIR}/references/shared/report-footer.md · ${CLAUDE_SKI
57
78
  cap — refused*; an unreadable bundled template canon is its own loud STOP naming the kit
58
79
  reinstall command.
59
80
 
60
- **Bridge settings reconcile stamp-independent, same gate, BEFORE the equal-head short-circuit.**
61
- Run `node ${CLAUDE_SKILL_DIR}/tools/bridge-settings.mjs --reconcile` and **paste its outcome line
81
+ **`bridge-settings`bridge settings reconcile.** **Paste its outcome line(s)
62
82
  verbatim**: it validates the deployed host settings file's keys against the bundled manifests and
63
83
  **NEVER writes** it (the file lives outside every kit tree — D2), so an unknown/retired key is
64
84
  flagged + preserved, never edited. Runs on **every** upgrade; exit 0 covers every outcome.
65
85
  4. **Equal-head exit — a real successful-exit report, not a bare stop.** If the stamp **equals** the head, the lineage is up to date — but step 3 (the stamp-independent reconciles) ran first and may have changed things, so this is a proper exit report, not a no-op:
66
- - **Report step 3's outcome in plain language** — for **each** pointer (workflow-methodology, orchestration-recipes and autonomy-policy) whether it was *added*, was *already present* (nothing changed), or was *skipped* (the soft-skip from step 3, with its reason — over the line limit / engine too old / the autonomy pointer's anchor absent); whether the `docs/ai/orchestration.json` config was *seeded* (created from the template), had its onboarding note *refreshed*, was *already current*, or carried a *customized note that was preserved* (a user edit is never clobbered); whether the `docs/ai/gates.json` gate declaration was *seeded* or was *already present* (preserved byte-for-byte); whether the `docs/ai/autonomy.json` declaration was *seeded* (the sparse defaults-equivalent note) or was *already present* (preserved byte-for-byte); whether the enforcement-script ensure *added* the seed pairs to `scripts/`, found them *already present*, or found an *old ADR layout migration instructed*; the **placed-bridge refresh** outcome — paste the tool's per-bridge lines verbatim (they are already plain: *refreshed* / *already current* / *skipped — not placed* / `skipped-readonly` / *could not refresh* + recovery); the **agent-rules lens** outcome (*refreshed* / *already current* / *custom edit preserved + note* / *file absent* / *engine too old* / *over the line cap*) and the **Communication-section** outcome (its own set: refreshed / already current / custom preserved + note / section absent — noted / over the cap — refused); the **bridge-settings reconcile** outcome (paste the tool's line verbatim); and, for a hidden deployment, whether the hidden-mode footprint was *moved to project-local*, was *already project-local* (nothing changed), or needed a question (ambiguous visibility / a leftover machine-wide block). Plain wording only — never the reconcile/slot/anchor/marker terms (the never-leak-kit-internals Gotcha — `${CLAUDE_SKILL_DIR}/references/shared/deploy-tail.md`).
86
+ - **Report step 3's outcome in plain language** — for **each** `pointers` slot (workflow-methodology, orchestration-recipes and autonomy-policy) whether it was *added*, was *already present* (nothing changed), or was *skipped* (the soft-skip from step 3, with its reason — over the line limit / engine too old / the autonomy pointer's anchor absent); the **four project-configuration ensure** (`configs`) lines exactly as the one ensure run composed them (orchestration config, gate declaration, autonomy declaration, enforcement scripts their outcome tokens are enumerated in step 3), each rendered in plain language: what was created, what was left exactly as the user wrote it, and — for a `failed` line what stopped it; the **`gates-migration`** result *nothing to migrate*, the shown plan *applied* on your explicit yes, or the plan *left unapplied* (consent not given), with any INERT checker or CUSTOMIZED entries named; the **placed-bridge refresh** (`bridges`) outcome — paste the tool's per-bridge lines verbatim (they are already plain: *refreshed* / *already current* / *skipped — not placed* / `skipped-readonly` with its re-scan verdict / *could not refresh* + recovery); the **agent-rules lens** (`lens`) outcome (*refreshed* / *already current* / *custom edit preserved + note* / *file absent* / *engine too old* / *over the line cap*) and the **Communication-section** outcome (its own set: refreshed / already current / custom preserved + note / section absent — noted / over the cap — refused); the **bridge-settings reconcile** (`bridge-settings`) outcome (paste the tool's line(s) verbatim); and, for a hidden deployment, whether the hidden-mode footprint (`footprint`) was *moved to project-local*, was *already project-local* (nothing changed), or needed a question (ambiguous visibility / a leftover machine-wide block). Plain wording only — never the reconcile/slot/anchor/marker terms (the never-leak-kit-internals Gotcha — `${CLAUDE_SKILL_DIR}/references/shared/deploy-tail.md`).
67
87
  - **Never surface the structure number on this exit.** Whatever step 3 did, do **not** recite the `docs/ai` structure version, the internal versioning vocabulary, or the two-axes note here — the number is inert on an equal-head exit; it belongs to *Version disclosure* in `${CLAUDE_SKILL_DIR}/references/shared/report-footer.md` (shown at the never-downgrade STOP, the explicit status view, or on an explicit ask). Frame the success itself per the final bullet: if step 3 changed anything, say **what changed** in plain human terms; only a pure zero-diff no-op is *settings already current — no update needed*.
68
88
  - **Render the mandatory Recommendations section — on this exit too, BEFORE the footer.** Run `node ${CLAUDE_SKILL_DIR}/tools/recommendations.mjs --cwd <project-root>` and PRESENT its output — from the `## Recommendations (agent-workflow)` header — in the user's conversational language: every fact, count and item from the tool, nothing added or dropped; commands, paths, hosts and rule strings byte-exact; show the raw tool block on request. The section is present-even-when-empty (with everything optimal the body is exactly `no recommendations — flow optimal.`) and VERDICT-FIRST — the composed verdict line renders from the frozen templates `{K} item(s) need attention` / `nothing is broken` / `{N} optional recommendation(s), apply any you want` / `optimality NOT attested — {M} probe check(s) skipped`. Then OFFER the consent-gated applies: the user picks items in plain language; surface each picked item's posture note, get the explicit confirm, then run EXACTLY the rendered one-liners (a HAND-APPLY item is never run by you) — the full lane in `${CLAUDE_SKILL_DIR}/references/modes/recommendations.md`. Pinned order on this exit: Recommendations block → optional applies → report footer → the commit ask (the advisor/apply lane never lands after the commit ask).
69
89
  - **Live host/session facts are tool-composed only.** Any claim this report makes about the current host or session state — prompts fired, sandbox scope, whether a bypass was needed, network reachability, approval counts — must trace to **live tool output** from **this session** (the lines you just composed, or a probe you ran this run); a memory/handover snapshot is **context, never report facts**, and a claim with no live signal is **omitted or explicitly marked unverified** — never asserted from recollection. Full clause: *Live host/session facts* in `${CLAUDE_SKILL_DIR}/references/shared/report-footer.md`.
@@ -72,4 +92,4 @@ Requires: ${CLAUDE_SKILL_DIR}/references/shared/report-footer.md · ${CLAUDE_SKI
72
92
  5. Show the relevant `${CLAUDE_SKILL_DIR}/CHANGELOG.md` diff (entries newer than the project's stamp).
73
93
  6. **Collect the migration answers FIRST, then apply.** If `AGENTS.md` is missing BOTH the *Communication language* and *Attribution* blocks — i.e. both blocks are missing (a pre-1.1.0 deployment) — ask the two questions as ONE structured multi-question prompt; record each answer individually, write nothing until ALL are answered, and carry the answers into the migrations below: a migration whose answer was already collected never re-asks (its own "Ask the user" step is the standalone fallback); a single missing block keeps its single ask (step 7). Then apply `${CLAUDE_SKILL_DIR}/migrations/<version>-<slug>.md` in **semver order**, only those newer than the project's stamp. Migrations are **idempotent** — safe to re-run.
74
94
  7. Reconcile drift: add any kernel files/scripts the project is missing; never clobber project-authored content (their `decisions.md`, `known_issues.md`, page specs stay). Any user question a migration raises follows the same rule as bootstrap — **structured multiple-choice where supported** (`AskUserQuestion` in Claude Code), otherwise prose. If `AGENTS.md` has no *Communication language* block (pre-1.1.0 deployment), **ask the user their conversational language** and insert the block — see `migrations/1.1.0-communication-language.md`. If it has no *Attribution* block (pre-1.2.0 deployment), **ask whether the agent may attribute work to itself / AI** and insert the block (defaulting to `off`) — see `migrations/1.2.0-agent-attribution.md`. (An answer already collected by the step-6 batched prompt is carried in — never re-asked here.)
75
- 8. Re-stamp `docs/ai/.workflow-version` to the **deployment-lineage head** (`3.0.0`, not the package version — mechanics unchanged: the atomic write to the stamp file). In the report, **describe what the upgrade changed in plain human terms** — which parts of their `docs/ai` are now different (the migrations that ran), plus the step-3 **placed-bridge refresh** lines (pasted verbatim), the step-3 **agent-rules lens** + **Communication-section** outcomes (same outcome sets as step 4), the step-3 **bridge-settings reconcile** outcome, and the step-3 **autonomy-declaration ensure** outcome (*seeded* / *already present, preserved*) — rather than reciting a version number; **omit the raw structure number**, and do **not** print the two-axes note here (it belongs to *Version disclosure* in `${CLAUDE_SKILL_DIR}/references/shared/report-footer.md`, on demand only). Then **render the mandatory Recommendations section**: run `node ${CLAUDE_SKILL_DIR}/tools/recommendations.mjs --cwd <project-root>` and PRESENT its output — from the `## Recommendations (agent-workflow)` header — in the user's conversational language (every fact, count and item, nothing added or dropped; commands, paths, hosts and rule strings byte-exact; raw tool block on request; present-even-when-empty: `no recommendations — flow optimal.`), then OFFER the consent-gated applies (per picked item: posture note → explicit confirm → run EXACTLY the rendered one-liner; a HAND-APPLY item is never run by you — `${CLAUDE_SKILL_DIR}/references/modes/recommendations.md`). **Every current host/session claim in this report is tool-composed only** — prompts fired, sandbox scope, whether a bypass was needed, network reachability and approval counts must trace to **live tool output** from **this session**, a memory/handover snapshot is **context, never report facts**, and an unbacked claim is **omitted or explicitly marked unverified** (full clause: *Live host/session facts* in `${CLAUDE_SKILL_DIR}/references/shared/report-footer.md`). Then **print the report footer** in the canonical order (version block → one-line backend-status line → welcome mat — the shared contracts in `${CLAUDE_SKILL_DIR}/references/shared/report-footer.md`; rendered from the helpers, same host-can't-run skip-with-reason; the welcome mat closes on one caveat-aware next step). Then **ask before committing** — the pinned order on this exit is: Recommendations block → optional applies → report footer → the commit ask.
95
+ 8. Re-stamp `docs/ai/.workflow-version` to the **deployment-lineage head** (`3.0.0`, not the package version — mechanics unchanged: the atomic write to the stamp file). In the report, **describe what the upgrade changed in plain human terms** — which parts of their `docs/ai` are now different (the migrations that ran), plus the step-3 run-list outcomes, each as step 4 words it: the `pointers` slots, the `footprint` visibility verdict (and what a consented re-run migrated), the **four project-configuration ensure** (`configs`) lines, the **`gates-migration`** result, the **placed-bridge refresh** (`bridges`) lines (pasted verbatim), the **agent-rules lens** (`lens`) + **Communication-section** outcomes (same outcome sets as step 4), and the **bridge-settings reconcile** (`bridge-settings`) outcome (line(s) pasted verbatim) — rather than reciting a version number; **omit the raw structure number**, and do **not** print the two-axes note here (it belongs to *Version disclosure* in `${CLAUDE_SKILL_DIR}/references/shared/report-footer.md`, on demand only). Then **render the mandatory Recommendations section**: run `node ${CLAUDE_SKILL_DIR}/tools/recommendations.mjs --cwd <project-root>` and PRESENT its output — from the `## Recommendations (agent-workflow)` header — in the user's conversational language (every fact, count and item, nothing added or dropped; commands, paths, hosts and rule strings byte-exact; raw tool block on request; present-even-when-empty: `no recommendations — flow optimal.`), then OFFER the consent-gated applies (per picked item: posture note → explicit confirm → run EXACTLY the rendered one-liner; a HAND-APPLY item is never run by you — `${CLAUDE_SKILL_DIR}/references/modes/recommendations.md`). **Every current host/session claim in this report is tool-composed only** — prompts fired, sandbox scope, whether a bypass was needed, network reachability and approval counts must trace to **live tool output** from **this session**, a memory/handover snapshot is **context, never report facts**, and an unbacked claim is **omitted or explicitly marked unverified** (full clause: *Live host/session facts* in `${CLAUDE_SKILL_DIR}/references/shared/report-footer.md`). Then **print the report footer** in the canonical order (version block → one-line backend-status line → welcome mat — the shared contracts in `${CLAUDE_SKILL_DIR}/references/shared/report-footer.md`; rendered from the helpers, same host-can't-run skip-with-reason; the welcome mat closes on one caveat-aware next step). Then **ask before committing** — the pinned order on this exit is: Recommendations block → optional applies → report footer → the commit ask.