@sabaiway/agent-workflow-kit 14.24.0 → 14.25.1

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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,48 @@ 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
+ ## 14.25.1 — `init` reports what it did: a replaced MCP entry is labelled replaced, and a folder inside a kept folder prints no line of its own (AD-174)
8
+
9
+ - **The MCP apply report no longer shows the old entry as the current one.** After the `y` that replaces a differing
10
+ `agent-workflow` entry, `init` printed the old entry under `the entry standing in .mcp.json now:` while the file
11
+ already held the new one. It now prints `the entry this run replaced in .mcp.json:` and `the entry now in
12
+ .mcp.json`. The preview before the `y` is unchanged.
13
+ - **A folder inside a kept folder prints no line of its own.** A folder inside one of the kit's own folders
14
+ (`tools/`, `references/` and the like) that the package does not carry is still kept and reported as yours to
15
+ remove by hand, but the folders inside it no longer print a line each:
16
+ a home that kept an emptied `tools/manifest/fixtures/` printed 18 lines on every run and now prints one. A stray
17
+ file inside such a folder is still removed with its own line.
18
+
19
+ ## 14.25.0 — `init`, run in a console from a project's folder, applies that project's harness configuration on one "y" (AD-173)
20
+
21
+ **`npx @sabaiway/agent-workflow-kit@latest init` gains a project step.** Run in your own console from a project's
22
+ folder, it previews every pending Recommendations item whose writer it runs on the harness's own configuration
23
+ (`.claude/settings.json`, `.claude/agents/`, `.claude/hooks/agent-workflow-gates.mjs`, `.mcp.json`, the Jev agent
24
+ skill copies) and applies the previewed batch on one terminal `y`; a console item it has no writer for yet
25
+ (`commit-guard`, `enforcement`, `state-block`, `gate-hook.marker-stale`, `read-lane.stale`, the bare
26
+ `sandbox-provision`) is listed with its text, not applied. The route is now: `init` in a console, then
27
+ `/agent-workflow-kit upgrade` in a session, which applies the chat items on your chat "yes".
28
+
29
+ - **Every item names its lane.** The Recommendations screen and `--json` carry `lane: console` or `lane: chat` per
30
+ item, from one new leaf, `tools/write-lanes.mjs`. A console item `init` runs, and `family-freshness`, has the apply
31
+ slot `applies at your next npx @sabaiway/agent-workflow-kit@latest init, run from this project's folder`, an
32
+ env-dependent one with `; if init reports it not pending, restart the agent from that console` appended; the other
33
+ console items keep their apply text; a chat item's apply text is unchanged.
34
+ - **Where the step runs, and where it does not.** Without a terminal (a piped `init`) it prints `project step
35
+ skipped: no terminal — run init in a console from the project's folder`. Inside an agent session it writes nothing
36
+ and says `you are inside an agent; run this in your own terminal`. Outside any project it offers only the API key
37
+ and its agent skill. A project whose stamp is behind the kit lists no project item and says `run
38
+ /agent-workflow-kit upgrade, then init again`.
39
+ - **Consent covers what you saw.** The key line comes first (Enter skips). Then each preview prints, with its posture
40
+ note where its item has one, and one `y` applies them all (a sudo install asks once more). A file changed outside the batch after the
41
+ previews stops that item and every later one, named; a failed apply stops the batch and `init` exits non-zero. A
42
+ second pass asks once more only for items the batch unlocked.
43
+ - **Files a sandbox binds stay in place.** An existing `.mcp.json` or `.claude/settings.json` keeps its inode.
44
+ `/agent-workflow-kit mcp --replace` previews the standing `agent-workflow` entry and the one it would write, and
45
+ replaces it only with `--apply`; without `--replace` a differing entry still stops unwritten.
46
+ - **The agent never runs a console line.** The upgrade, bootstrap and recommendations mode docs say so; the agent
47
+ continues past every console item, and a failed chat step stays a stop. The tarball holds 362 files.
48
+
7
49
  ## 14.24.0 — the bridge tier also writes the placed bridges' hosts into the project sandbox, so where the host honours the settings sandbox keys a review the harness keeps sandboxed is not refused them (AD-172)
8
50
 
9
51
  **`velocity-profile --bridge-tier` now merges the declared hosts of every placed bridge (each bundled manifest's
package/README.md CHANGED
@@ -140,10 +140,18 @@ npx @sabaiway/agent-workflow-kit@latest init
140
140
  ```
141
141
 
142
142
  `init` installs/refreshes the skill at `~/.claude/skills/agent-workflow-kit/` and wires launchers for
143
- any Claude Code / Codex / Devin Desktop it finds. It **does not** deploy into a project, and **never
143
+ any Claude Code / Codex / Devin Desktop it finds. A fresh project is deployed in step 2. `init` **never
144
144
  places** the optional bridges — **once placed** (by `/agent-workflow-kit setup`) it **refreshes** them
145
145
  from its bundled copies (never a downgrade; skip with `--no-bridges`).
146
146
 
147
+ Run `npx @sabaiway/agent-workflow-kit@latest init` in your own console **from the project's folder** to also apply its console items —
148
+ the harness's own configuration (`.claude/settings.json`, `.claude/agents/`, `.claude/hooks/`, `.mcp.json`).
149
+ A pending API-key line comes first: Enter skips; its `y` runs the key command on that terminal.
150
+ Then `init` previews the pending console items it can apply and applies the previewed batch on one terminal `y` (sudo asks separately).
151
+ Outside any project it prints `no agent-workflow project here — the project items apply when init runs from that project's folder`
152
+ and offers only host-scoped items — the API key and the agent skill it pairs with. Piped without a terminal,
153
+ it prints `project step skipped: no terminal — run init in a console from the project's folder` and skips that step.
154
+
147
155
  ### 2. Deploy into a project — once per repo
148
156
 
149
157
  Invoke the installed skill **inside the target repository** — first time vs. already-deployed use
@@ -157,6 +165,8 @@ different sub-commands:
157
165
 
158
166
  <sub>`/agent-workflow-kit` bootstraps a fresh deployment (and asks your **visibility**, **conversational language**, and whether the agent may **attribute work to itself / AI** — default off); `/agent-workflow-kit upgrade` migrates an existing one to the kit's current version.</sub>
159
167
 
168
+ After `init`, run `/agent-workflow-kit upgrade` in an agent session: it applies the chat items on your chat "yes".
169
+
160
170
  > **Optional standalone memory substrate.** The memory layer is also published standalone as
161
171
  > [`@sabaiway/agent-workflow-memory`](https://www.npmjs.com/package/@sabaiway/agent-workflow-memory).
162
172
  > If a **healthy** copy is installed (the kit validates it with its own shipped validator), the kit
@@ -241,7 +251,7 @@ file), or run the guarded `/agent-workflow-kit uninstall`.
241
251
  | `/agent-workflow-kit set-recipe` | any time | **config writer** for `docs/ai/orchestration.json`: tell the agent your preference in plain language and it maps it to explicit `--set <activity>.<slot>=<value>` / `--unset` ops; the kit validates, **previews by default**, and writes only on `--write` (deployment-gated, atomic, symlink/TOCTOU-safe), resolving the effective recipe vs live readiness. Writes **only** that file — **never runs a backend or a subagent, never commits**; hand-editing stays fully supported. |
242
252
  | `/agent-workflow-kit review-state` | any time | **read-only review-receipt checker** — makes "reviewed ≠ shipped" detectable: the bridge review wrappers append a receipt per successful review (into a file inside the git dir — never committable); this checks that every backend your configured `plan-execution.review` recipe names holds a **fresh, grounded receipt for the current uncommitted tree** (any later edit the payload can see moves the fingerprint and stales the receipt; a review continuation never re-attests a folded tree). `--check` gives a gate exit code to declare in `docs/ai/gates.json` **by hand or via the consent-gated seeder** (preview → your explicit yes; never without consent). `--await [--timeout <s>]` (AD-049) BLOCKS until every recipe-named backend has receipted the current tree — the durable completion signal is the receipt, never a process event — so you wait for the bridges instead of hand-polling. Never writes, never commits, never runs a subscription CLI; it spawns read-only `git` queries to fingerprint the tree — and `git commit --no-verify` stays possible (discipline, not a sandbox). |
243
253
  | `/agent-workflow-kit sandbox-masks` | any time | **exclude lane for sandbox masks** — an OS sandbox (Claude Code) masks work-tree paths with a device or, on Linux, a foreign bind mount, as untracked `git status` noise; the review domain already ignores a **device mask** **by construction** (never-committable untracked classes — char/block devices, FIFOs, sockets — are excluded from the fingerprint, the assembled review payload, and the clean checks), while a **mount-only mask** rides the fingerprint and `git add -A` while git does not ignore it (load-bearing there). This mode hides them from `git status` too: flagless = read-only probe (derives the CURRENT mask set from the unfiltered walk + lstat + `/proc/self/mountinfo` — never a frozen list; off Linux the mount signal is not read and it says so, an unreadable or malformed table refuses — and revalidates fenced entries, loudly flagging one that became a real path); `--apply` = consent-gated FULL-BLOCK replace of its own fenced block in `git rev-parse --git-path info/exclude` (stale masks drop by construction; `--clear` always means REMOVE the block — it takes precedence over the derivation). Writes ONLY its fence — never `.gitignore`, never global config; symlinked/non-regular exclude paths and malformed fences fail closed. Watch note: a real file at an excluded path is silently skipped by bulk staging (`git add -A`/`git add .`) — delete the stale line first; the probe flags exactly this case. |
244
- | `/agent-workflow-kit mcp` | Claude Code · opt-in | **typed-channel registration** — the kit ships a read-only stdio **MCP server** exposing its two promptless readers as TYPED tools (`path_inventory`: exists / type / size / line count / listing / a small file's text, many paths in ONE call; `repo_search`: literal search, the pattern a JSON string). Their arguments are named JSON fields rather than a string handed to a shell, so a pipe, a redirect or a quote inside a pattern or a path stays DATA and is never interpreted — legal bytes to search for, with no shell to read them as operators. Shipping the server does nothing on its own — a client sees it only once the project declares it, and this mode is that declaration: the `agent-workflow` entry in **`.mcp.json`** (command `node`, args = the absolute path of the RUNNING kit's server) plus `enabledMcpjsonServers` and the two derived tool allow rules in **`.claude/settings.json`**. Preview by default and the **exact entry is printed before consent** (registering a server means your client will run that command); `--apply` writes `.mcp.json` FIRST, then settings, merge-don't-clobber with each file's EOL kept, and a re-apply adds nothing twice. An existing `agent-workflow` entry that **structurally differs** from what this kit copy would write is refused unwritten (the comparison ignores key order, so re-serialized identical bytes are the same registration) — silently changing what an MCP server launches is what consent must not slide past. Where an OS sandbox masks `.mcp.json` with a device node, it writes nothing, hands you both paste-ready texts and exits 0. The server is a read-only child of your client (no write, no exec API) running outside the Bash sandbox as the client does. Never writes `settings.local.json`; never commits. |
254
+ | `/agent-workflow-kit mcp` | Claude Code · opt-in | **typed-channel registration** — the kit ships a read-only stdio **MCP server** exposing its two promptless readers as TYPED tools (`path_inventory`: exists / type / size / line count / listing / a small file's text, many paths in ONE call; `repo_search`: literal search, the pattern a JSON string). Their arguments are named JSON fields rather than a string handed to a shell, so a pipe, a redirect or a quote inside a pattern or a path stays DATA and is never interpreted — legal bytes to search for, with no shell to read them as operators. Shipping the server does nothing on its own — a client sees it only once the project declares it, and this mode is that declaration: the `agent-workflow` entry in **`.mcp.json`** (command `node`, args = the absolute path of the RUNNING kit's server) plus `enabledMcpjsonServers` and the two derived tool allow rules in **`.claude/settings.json`**. Preview by default and the **exact entry is printed before consent** (registering a server means your client will run that command); `--apply` writes `.mcp.json` FIRST, then settings, merge-don't-clobber with each file's EOL kept, and a re-apply adds nothing twice. An existing `agent-workflow` entry that **structurally differs** from what this kit copy would write is refused unwritten unless `--replace` is given (the comparison ignores key order, so re-serialized identical bytes are the same registration) — silently changing what an MCP server launches is what consent must not slide past. `--replace` prints the entry standing in `.mcp.json` and the entry it would write, and replaces it only with `--apply`. An existing `.mcp.json` or `.claude/settings.json` is written in place: it keeps its inode, never removed or renamed over; an absent file is created. Where an OS sandbox masks `.mcp.json` with a device node, it writes nothing, hands you both paste-ready texts and exits 0. The server is a read-only child of your client (no write, no exec API) running outside the Bash sandbox as the client does. Never writes `settings.local.json`; never commits. |
245
255
  | `/agent-workflow-kit grounding` | any time | **grounded-review facts assembler** — mechanizes populating `agy-review --facts @f`: slices your entry-point's **Hard Constraints** section verbatim (exactly one match, else a loud stop) and/or a plan's three canon sections (`## Goal and boundary` + `## Module ledger` + `## Verification`, each required; duplicates stop), under the same byte budget the agy wrapper enforces (minus `--reserve-bytes` for the artifact share), with a loud tail-trim on overflow. `--autonomy` (AD-044) appends the COMPUTED effective autonomy policy from the git-top `docs/ai/autonomy.json` (every red-line + per-activity level, stated source line; absent file → the computed defaults ARE the policy, exit 0; a malformed policy fails CLOSED, exit 1). `--extra <text|@file>` (repeatable) appends your own facts **byte-verbatim** after the mechanical sections, so the merge is a tool input rather than a shell append — an `@file` must sit inside the proven git work tree (never the git dir) or the system temp surface, and is read through a no-follow descriptor (a FIFO cannot block it, a symlink leaf cannot substitute its target). Prints to stdout; `--out` writes **one scratch file only** — system-temp outside the repo ($TMPDIR / /tmp, rewritable) or a **fresh** gitignored in-repo path (create-only, exclusive write; an existing in-repo file, even gitignored, is refused — the `.env` clobber class); tracked, not-ignored-in-repo, other outside-repo, and symlink/non-regular destinations are all refused. Never commits, never runs a subscription CLI. |
246
256
  | `/agent-workflow-kit core-evidence` | any time | **the ONE loop-evidence writer** (strip-the-kit) — every core evidence record lands in a single append-only JSONL store inside the git dir (never committable; versioned schema, latest-per-key supersession, byte-identical duplicates refused, malformed lines fail every reader closed). `red-proof "<file>#<pattern>"` declares an observed-red **BEFORE a bugfix** (N/N red runs + content custody + base + the pre-fix fingerprint; green/mixed/timeout are DISTINGUISHED refusals — nothing written); `degrade --backend --reason` is the ONLY escape for an unavailable review backend (per-tree, never all backends); `summary` renders the whole loop state statelessly (gate result · per-backend verdicts · red-proofs · degrades) — no ledger, no rounds, nothing remembered. Honest residual: records are forgeable — self-discipline, not a security boundary. Never commits, never runs a subscription CLI. |
247
257
  | `/agent-workflow-kit coverage-check` | any time | **the final-run checker** (D3(c)+(d)) — **certifies coverage ONLY inside the `--final` run that owns the lcov** (ownership is exclusive by CONVENTION over the fixed path, not enforced — a concurrent writer to it is a stated residual, queued as LCOV-EXCLUSIVE-OWNERSHIP): an artifact on disk proves nothing about the tree it came from, so a standalone run prints its findings and states `attested=no` / `NO VERDICT` rather than a PASS (an lcov that predates an edit would otherwise certify a line the suite never executed). `attested=` claims a verdict was ISSUED, pass or fail — a run over uncovered lines still reads `attested=yes` and still exits 1, and a run whose coverage arm never executed (no lcov) reads `attested=no` even inside `--final`, because nothing was read and nothing is certified. The runner passes a nonce whose one-way commitment over `{nonce, fingerprint, base}` is the `final-start.attempt` it recorded; a context describing another tree, or matching no recorded attempt, is a REFUSAL, never a verdict. Findings are unchanged — reads the lcov the declared `unit-tests` gate produced at the FIXED git-dir path and fails on any uncovered CHANGED executable Node line (listed `file:line`; a changed file absent from the map is a file-level red; out-of-domain/unsupported files are LISTED — the claim narrowed honestly); VERIFIES every current-base red-proof declaration (bound test exists · custody hash unchanged · green N/N now · pre-fix fingerprint differs); prints `lcov-sha256=<hex|none>` of the exact bytes it consumed — the sha the `--final` receipt binds and re-hashes. An absent lcov is a LOUD `skipped-no-lcov`; a symlinked path is a refusal. `--check` is the gate exit code — declare it as the LAST gate (`run-gates --final` refuses otherwise). Read-only. |
@@ -263,6 +273,7 @@ It **never auto-commits** and **never overwrites** an existing `AGENTS.md` witho
263
273
  > **Two kinds of "upgrade":** `npx @sabaiway/agent-workflow-kit@latest init` updates the **kit's
264
274
  > own files** in `~/.claude/skills/`; `/agent-workflow-kit upgrade` then migrates a **project's**
265
275
  > `docs/ai/` deployment to that kit version.
276
+ > When a kit update moves the deployment stamp ahead of the project's, `init` lists no project item and says `run /agent-workflow-kit upgrade, then init again`.
266
277
 
267
278
  ---
268
279
 
@@ -280,8 +291,8 @@ The dependency-free **Node** enforcement scripts (`node --test`, no package mana
280
291
 
281
292
  ## 🧩 The composition root of the family
282
293
 
283
- The kit is the member you install — the family's **composition root**. `npx … init` only installs
284
- the kit globally; the composition happens when you **deploy it in a repo** (`/agent-workflow-kit`):
294
+ The kit is the member you install — the family's **composition root**. `npx … init` installs the kit
295
+ globally and, run in a console from a project's folder, previews that project's pending console items and applies the previewed batch on your y; composition starts when you **deploy it in a repo** (`/agent-workflow-kit`):
285
296
 
286
297
  ```
287
298
  agent-workflow-kit — the composition root (installed via npx … init)
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". New to the epic, story and task tier? Run `/agent-workflow-kit tier`. A typed decision with a confidence from Jev? Run `/agent-workflow-kit jev`.
4
4
  disable-model-invocation: true
5
5
  metadata:
6
- version: '14.24.0'
6
+ version: '14.25.1'
7
7
  ---
8
8
 
9
9
  # agent-workflow-kit
package/bin/install.mjs CHANGED
@@ -42,17 +42,9 @@ import { assertInstallableHome, prunePayload } from '../tools/payload-prune.mjs'
42
42
  // second drifting copy). The null-on-unparseable contract is load-bearing here: legacy installs
43
43
  // predate any version stamp, so an unparseable side means "no gate", never a false ordering.
44
44
  import { compareSemver } from '../tools/semver-lite.mjs';
45
- // The ONE registry of family members (npm packages, kinds). The init-refresh cascade derives its
46
- // membership from this table — no second source of "who gets refreshed" — so it can't drift from the
47
- // manifests (a drift-guard test pins the derivation). Imported from the DATA LEAF (family-members.mjs),
48
- // NOT family-registry.mjs. Leanness note: the bridge-refresh driver below does pull in the backend
49
- // detector + the manifest validator (setup-backends' imports), but still NOT the status/presenter
50
- // graph (family-registry, renderers, recipes) — the npx cold-start path stays presenter-free.
45
+ // Cascade membership derives from the data leaf; the npx cold-start path stays presenter-free.
51
46
  import { FAMILY_MEMBERS } from '../tools/family-members.mjs';
52
- // The refresh-only bridge driver (shared with `/agent-workflow-kit setup --refresh-placed` and the
53
- // upgrade reconcile): refreshes ONLY a bridge `setup` already placed — an absent bridge is a stated
54
- // skip (never a first placement, AD-009/AD-011) and a placed bridge newer than this kit's bundled
55
- // mirror is a stated skip too (never a downgrade). Every line it returns is tool-composed.
47
+ // Refresh only placed bridges; never place an absent bridge or downgrade a newer one (AD-009/AD-011).
56
48
  import { refreshPlacedBridges } from '../tools/setup-backends.mjs';
57
49
  import { reconcileSettings } from '../tools/bridge-settings.mjs';
58
50
 
@@ -535,17 +527,26 @@ const main = async () => {
535
527
  ` ${engineCmd} (install the engine — recommended)\n` +
536
528
  ` npx @sabaiway/agent-workflow-kit@latest init --no-engine (skip it deliberately)`,
537
529
  );
530
+ console.log('[agent-workflow-kit] project step skipped: an earlier step stopped init');
538
531
  process.exit(1);
539
532
  }
540
533
  console.log('[agent-workflow-kit] methodology engine installed.');
541
534
  }
542
535
 
543
536
  // The non-convergence line already took the verb's place.
544
- if (!prune.ok) process.exit(1);
537
+ if (!prune.ok) {
538
+ console.log('[agent-workflow-kit] project step skipped: an earlier step stopped init');
539
+ process.exit(1);
540
+ }
541
+
542
+ const project = spawnSync(process.execPath, [resolve(target, 'tools/init-project.mjs')], {
543
+ stdio: 'inherit',
544
+ cwd: process.cwd(),
545
+ });
546
+ const projectExited = Number.isInteger(project?.status) && project.status >= 0 && !project.error && !project.signal;
547
+ if (!projectExited) console.error(project?.error ?? project?.signal ?? project ?? 'empty answer');
548
+ process.exitCode = projectExited ? project.status : 1;
545
549
 
546
- // This command (de)installed the *kit* globally. Deploying it into a project is a
547
- // separate, in-agent step — and which sub-command depends on whether that project
548
- // already has the kit. Spell both out so it's unambiguous (see README "Use").
549
550
  printRestartHint(); // carrier fallback — a no-op when the verb path already printed it
550
551
  console.log(`
551
552
  Next — open your agent inside a project and run the skill:
@@ -555,7 +556,6 @@ Next — open your agent inside a project and run the skill:
555
556
  • per agent: Claude Code -> /agent-workflow-kit · Devin Local -> /agent-workflow-kit ·
556
557
  Codex -> its /skills menu -> agent-workflow-kit (Codex may also auto-trigger it)
557
558
 
558
- This command only installs/updates the kit itself (in ${tildify(target)}).
559
559
  To update the kit later, re-run: npx @sabaiway/agent-workflow-kit@latest init`);
560
560
  };
561
561
 
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": "14.24.0",
6
+ "version": "14.25.1",
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": "14.24.0",
3
+ "version": "14.25.1",
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",
@@ -43,13 +43,17 @@ Requires: ${CLAUDE_SKILL_DIR}/references/shared/report-footer.md · ${CLAUDE_SKI
43
43
  independent axes: a packaging-only release bumps the package but leaves the lineage head until a
44
44
  migration actually changes the deployed `docs/ai` structure. A stamp greater than the head →
45
45
  STOP (never downgrade).
46
- 11. **Report & ask.** Show `tree docs/ai/`, 2–3 lines on what was filled with real data vs left as TODO, 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 from the tool, nothing added or dropped; commands, paths, hosts and rule strings byte-exact; present-even-when-empty and verdict-first; then OFFER the consent-gated applies per `${CLAUDE_SKILL_DIR}/references/modes/recommendations.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 (a behind member first, else `setup` / `recipes` / `velocity` / `agents` / `hook`). **After the footer, present ONE compact optional-accelerators block** — a one-line why each; every writer previews first, and nothing runs without a yes:
46
+ 11. **Report & ask.** Show `tree docs/ai/`, 2–3 lines on what was filled with real data vs left as TODO, 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 from the tool, nothing added or dropped; commands, paths, hosts and rule strings byte-exact; present-even-when-empty and verdict-first; then OFFER the consent-gated applies per `${CLAUDE_SKILL_DIR}/references/modes/recommendations.md` for chat items only, applied by the agent on the chat yes.
47
+ When a console item's apply slot is the route line, only `init` applies it, run by the user in a console from the project's folder; the agent hands that line over and never runs it.
48
+ The agent never runs a console item's line, never retries a refused write outside the sandbox and continues past every console item; every failure of a chat step is a STOP.
49
+ 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 (a behind member first, else `setup` / `recipes` / `velocity` / `agents` / `hook`). **After the footer, present ONE compact optional-accelerators block** — a one-line why each; every writer previews first, and nothing runs without a yes:
47
50
  - `/agent-workflow-kit tier` — read-only: explains the epic, story and task tier and names the next step of the walk;
48
51
  - `/agent-workflow-kit velocity` — routine read-only commands stop prompting (incl. the `--kit-tools` tier for the kit's own read-only tools; `${CLAUDE_SKILL_DIR}/references/modes/velocity.md`);
49
52
  - `/agent-workflow-kit agents` — cheap-model subagents take the mechanical work (sweeps, changelog skeletons, gate triage; `${CLAUDE_SKILL_DIR}/references/modes/agents.md`);
50
53
  - **gates seeding + hook** — offer to seed `docs/ai/gates.json` from the commands recon already recorded (step 1), via the consent-gated preview in `${CLAUDE_SKILL_DIR}/references/modes/gates.md` — the Recommendations section's `gates-declaration` item renders that offer first; then, once gates are declared, `/agent-workflow-kit hook` auto-approves exactly those declared commands (`${CLAUDE_SKILL_DIR}/references/modes/hook.md`);
51
54
  - `/agent-workflow-kit set-recipe` — put a ready review backend to work on plans and diffs (`${CLAUDE_SKILL_DIR}/references/modes/set-recipe.md`).
52
55
  Then **ask before committing** — never auto-commit.
56
+ End the report with the **console-pending** list of console items still pending whose apply slot is the route line, and the exact line `run init from this folder, then restart`; a console item with its own text stays listed with that text.
53
57
 
54
58
  Fill strategy:
55
59
 
@@ -11,7 +11,7 @@ It writes exactly two files:
11
11
 
12
12
  **Trust posture (state it plainly when asking consent):** the registered server is a **read-only child of your MCP client** — it answers path facts and literal searches over the project root, and exposes no write and no exec API. It runs **outside the Bash sandbox**, exactly as the client itself does, so the sandbox is not what bounds it; the server's own root containment is. The two allow rules make its tool calls promptless — that is the whole velocity gain, and it applies to those two tools only. Registering a server means your client will RUN the named command, which is why the **exact entry is printed before consent is asked**, never described.
13
13
 
14
- Run `node ${CLAUDE_SKILL_DIR}/tools/mcp.mjs [--dry-run | --apply] [--cwd <dir>]`:
14
+ Run `node ${CLAUDE_SKILL_DIR}/tools/mcp.mjs [--dry-run | --apply] [--replace] [--cwd <dir>]`:
15
15
 
16
16
  1. **`--dry-run` first, always** (the default — changes nothing): prints what each file would gain and the **exact entry it would declare** — the same structured value, re-serialized for the preview rather than copied byte for byte — plus the apply one-liner. The preflight is **READ-ONLY**: an absent `.claude/` is a named state here and is created on `--apply` only — a preview that made a directory would not be a preview.
17
17
  2. **Ask consent** via **`AskUserQuestion` where supported**, the no-change option first: keep the shell lanes, or register the typed channel — presenting the trust posture above in plain language.
@@ -19,12 +19,17 @@ Run `node ${CLAUDE_SKILL_DIR}/tools/mcp.mjs [--dry-run | --apply] [--cwd <dir>]`
19
19
 
20
20
  4. **Hidden-mode deployments:** after apply, run the hide-footprint reconcile (`node ${CLAUDE_SKILL_DIR}/tools/hide-footprint.mjs --dir <project> --reconcile`) so the registration stays invisible to `git status` — `/.mcp.json` is in the known-footprint registry; every output arm reminds you.
21
21
 
22
- **The two arms that are not a write:**
22
+ An existing `.mcp.json` and `.claude/settings.json` are written in place: each keeps its inode, never removed, renamed over or linked onto, since a running sandbox can hold them as file-level bind mounts. An absent file is created.
23
23
 
24
- - **DIFFERING** — `.mcp.json` already carries an `"agent-workflow"` entry that **structurally differs** from ours (another kit copy, a hand-edited path, an added `env`, a `null`). The comparison is deliberately key-order-independent, so a re-serialized identical entry is the SAME registration, while any real difference in what would be launched is a difference. The run **STOPs unwritten, on both lanes**: silently changing what an MCP server launches is exactly what consent must not slide past. The recovery is named — review that entry, then remove or rename it and re-run. The kit never resolves this for you.
24
+ **The DIFFERING and MASKED arms:**
25
+
26
+ - **DIFFERING** — `.mcp.json` already carries an `"agent-workflow"` entry that **structurally differs** from ours (another kit copy, a hand-edited path, an added `env`, a `null`). The comparison is deliberately key-order-independent, so a re-serialized identical entry is the SAME registration, while any real difference in what would be launched is a difference. Without `--replace`, the run **STOPs unwritten, on both lanes**: silently changing what an MCP server launches is exactly what consent must not slide past.
27
+ With `--replace`, the preview prints the entry standing in `.mcp.json` now and the entry this registration declares, writes nothing and exits 0.
28
+ With `--replace --apply`, it replaces that entry, keeping every other server and key of `.mcp.json`, and prints the old entry as the one it replaced and ours as the one now in the file.
29
+ This is the console item `mcp-channel.differing`: `init` previews it in the user's console and applies it on the terminal "y".
25
30
  - **MASKED** — an OS sandbox injects a **character device** where `.mcp.json` would be (this is the normal condition of a sandboxed session, not a fault; see `${CLAUDE_SKILL_DIR}/references/modes/sandbox-masks.md`). Writing through a device node is refused by construction, so the mode **hands over what to merge, writes nothing, and exits 0**. The two halves of that handoff are worded differently, and the difference is load-bearing: `.claude/settings.json` was **observable — and read where present** — so its body is a real merge already carrying every foreign key it had, while the masked `.mcp.json` was not observable at all, so the mode hands over **the entry alone**, to be merged under `mcpServers` keeping every other server. A whole-file body would name only our server, and pasted as instructed it would delete every server the mask hid. Every other never-committable class (block device, FIFO, socket) takes the same arm. The handoff is scoped to `.mcp.json`: a masked `settings.json` is a **refusal**, because this mode can neither write it nor merge into what it cannot read.
26
31
 
27
- **Refusals (STOP, exit 1, zero writes) — decided BEFORE the handoff, so a mask can never swallow one:** a symlinked project root · a symlinked or **directory** `.mcp.json` / `settings.json` (the atomic publish renames over the path, which would replace the **link itself** — silently destroying a deliberate indirection, while the file the reader was actually looking at goes untouched). Note what is **not** in this list: a device node, FIFO or socket at `.mcp.json` takes the successful MASKED handoff above, not a refusal — only at `settings.json` is that class a refusal, since no handoff is sanctioned there · a symlinked or non-directory `.claude` · a masked `settings.json` · malformed or unreadable JSON in either target · a **managed key of the wrong type** — `mcpServers`, `enabledMcpjsonServers`, `permissions` or `permissions.allow` present but not the shape this mode writes, which read as empty containers would be merged over and silently destroyed (a foreign key of any shape is data and is never judged) · the DIFFERING entry above.
32
+ **Refusals (STOP, exit 1, zero writes) — decided BEFORE the handoff, so a mask can never swallow one:** a symlinked project root · a symlinked or **directory** `.mcp.json` / `settings.json` (the writer never writes through or over a link: following it would write a file outside the project's view, replacing it would silently destroy a deliberate indirection). Note what is **not** in this list: a device node, FIFO or socket at `.mcp.json` takes the successful MASKED handoff above, not a refusal — only at `settings.json` is that class a refusal, since no handoff is sanctioned there · a symlinked or non-directory `.claude` · a masked `settings.json` · malformed or unreadable JSON in either target · a **managed key of the wrong type** — `mcpServers`, `enabledMcpjsonServers`, `permissions` or `permissions.allow` present but not the shape this mode writes, which read as empty containers would be merged over and silently destroyed (a foreign key of any shape is data and is never judged) · the DIFFERING entry above without `--replace`.
28
33
 
29
34
  **Reading posture (the residual, stated).** The **container is classified before the file inside it is read**: path resolution follows an *intermediate* symlink (`O_NOFOLLOW` guards only the final component), so a symlinked `.claude` would otherwise deliver a settings file from outside the work tree — into memory, into a verdict, and into a rendered merge body — before anything could refuse it. A foreign container makes its settings target `unreadable` with that reason, and nothing inside it is read. Beyond that, every target is classified by `lstat` **before** any open, so a target decided **foreign** is never followed, never opened and never read: a symlink is refused by class, and a device node or FIFO is named rather than consumed. (A target decided **regular** is of course opened — that is the read.)
30
35
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  Requires: ${CLAUDE_SKILL_DIR}/references/shared/report-footer.md
6
6
 
7
- The **read-only deployment advisor** — the deterministic section every `upgrade` run ends with, also invocable on its own. It computes what in THIS deployment is configured sub-optimally (allowlist not seeded, autonomy render drifted, sandbox unavailable, gates undeclared, a declared gate matrix that verifies nothing, bridge friction, sandbox-mask clutter, an unacknowledged sandbox recipe, a configured `subagent` carrier whose executor vehicle cannot carry it, a difference from the reference profile the project has not declined, Jev not connected on this host, the Jev skill not installed for every agent) and renders **verdict-first**: one composed verdict line, then each item as **{severity · what is sub-optimal · the benefit in ONE plain line · an optional `recipe:` line (the `sandbox-lane` live recipe — egress hosts + resolved writable dirs — the `worktrees-dir` hand-apply-first grant advice, or the `agents` / `executor-vehicle` hidden-mode reconcile follow-up) · the exact consent-gated apply one-liner}**. The tool computes deterministic English DATA; **you PRESENT the section in the user's conversational language** — every fact, count and item from the tool, nothing added or dropped; commands, paths, hosts and rule strings stay **byte-exact**; show the raw tool block on request (the AD-032 report-contract lane — the tool cannot know the dialogue language, so the language rendering is your presentation layer).
7
+ The **read-only deployment advisor** — the deterministic section every `upgrade` run ends with, also invocable on its own. It computes what in THIS deployment is configured sub-optimally (allowlist not seeded, autonomy render drifted, sandbox unavailable, gates undeclared, a declared gate matrix that verifies nothing, bridge friction, sandbox-mask clutter, an unacknowledged sandbox recipe, a configured `subagent` carrier whose executor vehicle cannot carry it, a difference from the reference profile the project has not declined, Jev not connected on this host, the Jev skill not installed for every agent) and renders **verdict-first**: one composed verdict line, then each item as **{severity · what is sub-optimal · the benefit in ONE plain line · `lane: console` or `lane: chat` · an optional `recipe:` line (the `sandbox-lane` live recipe — egress hosts + resolved writable dirs — the `worktrees-dir` hand-apply-first grant advice, or the `agents` / `executor-vehicle` preconditions and hidden-mode reconcile follow-up) · the apply slot: a console route line or the exact consent-gated chat apply one-liner}**. The tool computes deterministic English DATA; **you PRESENT the section in the user's conversational language** — every fact, count and item from the tool, nothing added or dropped; commands, paths, hosts and rule strings stay **byte-exact**; show the raw tool block on request (the AD-032 report-contract lane — the tool cannot know the dialogue language, so the language rendering is your presentation layer).
8
8
 
9
9
  **Live host/session facts are tool-composed only.** Every fact this section states about the current
10
10
  host or session — prompts fired, sandbox scope, whether a bypass was needed, network reachability,
@@ -17,14 +17,18 @@ Run `node ${CLAUDE_SKILL_DIR}/tools/recommendations.mjs --cwd <project-root> [--
17
17
 
18
18
  1. **`--cwd` is REQUIRED** — the target project is explicit, never inferred from the shell's current directory (a subdirectory invocation still advises on the named root).
19
19
  2. **Verdict first (present-even-when-empty, opening at the `## Recommendations (agent-workflow)` header).** With everything optimal the body is exactly `no recommendations — flow optimal.` — the empty-state line ALONE is the verdict, zero added lines. Every other state opens the body with ONE verdict line composed from the frozen templates: `{K} item(s) need attention` leads when any item's severity is `attention` (a CONFIGURED declaration that is broken, drifted, degrading or invalid — the deployment needs review); `{N} optional recommendation(s), apply any you want` covers the `optional` class (offers to enable an unconfigured capability), led by `nothing is broken` ONLY when no item needs attention AND no probe check was skipped (a skipped probe could hide an attention-class problem — the claim never overreaches); `optimality NOT attested — {M} probe check(s) skipped` is appended last whenever probe checks were skipped. (The `(s)` invariant form IS the pinned pluralization — no singular/plural branching.) Items render attention-first, each tagged `needs attention:` / `optional:`. A failed probe renders as a stated `⚠ skipped item …` line — never a crash, never a fabricated item.
20
- 3. **The apply-through-agent lane — an explicit informed-consent checkpoint:** after presenting, OFFER to apply; the user selects items in plain language; for each selected item you **surface its posture note inline** where one exists (the per-item notes below cover exactly the risk-marked keys; for `sandbox-lane` the note INCLUDES the sandbox-lanes ladder — present the whole ladder inline at the consent moment, never as a bare pointer). The user **explicitly confirms**, and **only then** — **no command runs before confirmation** — do you run the rendered command. Do NOT infer safety from the presence or absence of an `--apply` flag: most items' rendered command IS the mutation and completes on that one run, **including a no-`--apply` mutation** such as `family-freshness`'s `npx … init`. Some items instead render a **dry-run preview** that changes nothing and prints an explicit follow-up `--apply`/mutating command to run NEXT (e.g. `sandbox-lane`'s ack-write — a NEUTRAL recipe fingerprint into the family-owned `docs/ai/acks.json`, never a security key — and the `gates-declaration` seeder): after the SAME confirmation you run that printed follow-up command — no second ask, no improvisation (each writer keeps its own consent semantics: previews, `--apply` flags, refusals). An item marked **HAND-APPLY** (e.g. the `read-lane` stale/missing arms) is **never run by you and never written by the kit on your behalf** (a kit command the user runs in a terminal of their own is the user's own write) — hand the user its rendered line together with its posture note; HAND-APPLY is **maintainer territory wherever it renders** — the apply slot or the `recipe:` line. For `worktrees-dir` the two classes COMPOSE in one item and the ORDER is part of the contract: its `recipe:` line is the HAND-APPLY grant advice, its apply one-liner is the dry-run ack preview, and you **wait for the maintainer to confirm the grant is applied (or the terminal fallback chosen)** before running that preview and the exact `--apply` command it prints — the ack RECORDS a choice that must already have been made, and against a trusted host NO the item renders no ack at all.
20
+ 3. **The apply-through-agent lane — an explicit informed-consent checkpoint:** this lane covers chat items only; a console item is not run on the chat yes — when its apply slot is the route line, name it as pending for `init`. The agent never runs a console item's line, never retries a refused write outside the sandbox and continues past every console item; every failure of a chat step is a STOP. After presenting, OFFER to apply; the user selects items in plain language; for each selected chat item you **surface its posture note inline** where one exists (the per-item notes below cover exactly the risk-marked keys; for `sandbox-lane` the note INCLUDES the sandbox-lanes ladder — present the whole ladder inline at the consent moment, never as a bare pointer). The user **explicitly confirms**, and **only then** — **no command runs before confirmation** — do you run the rendered command. Do NOT infer safety from the presence or absence of an `--apply` flag: most chat items' rendered command IS the mutation and completes on that one run, **including a no-`--apply` mutation** such as `spec-adoption`'s `ensure-configs --reconcile --only specs`. Some items instead render a **dry-run preview** that changes nothing and prints an explicit follow-up `--apply`/mutating command to run NEXT (e.g. `sandbox-lane`'s ack-write — a NEUTRAL recipe fingerprint into the family-owned `docs/ai/acks.json`, never a security key — and the `gates-declaration` seeder): after the SAME confirmation you run that printed follow-up command — no second ask, no improvisation (each writer keeps its own consent semantics: previews, `--apply` flags, refusals). An item marked **HAND-APPLY** (e.g. `enforcement` or `read-lane.stale`) is **never run by you and never written by the kit on your behalf** (a kit command the user runs in a terminal of their own is the user's own write) — hand the user its rendered line together with its posture note; HAND-APPLY is **maintainer territory wherever it renders** — the apply slot or the `recipe:` line. For `worktrees-dir` the two classes COMPOSE in one item and the ORDER is part of the contract: its `recipe:` line is the HAND-APPLY grant advice, its apply one-liner is the dry-run ack preview, and you **wait for the maintainer to confirm the grant is applied (or the terminal fallback chosen)** before running that preview and the exact `--apply` command it prints — the ack RECORDS a choice that must already have been made, and against a trusted host NO the item renders no ack at all.
21
21
  4. Registry strings (benefits + item texts) are frozen tool data, fact-true, ONE line under the pinned shape cap — posture/risk detail lives in the notes below at the consent moment, never inline in the overview. The dual velocity+security wording (`safer — blast radius bounded by the OS sandbox, not human attention`) rides ONLY the items with a real security delta (the autonomy render, the sandbox provisioning); the bridge-wrappers item claims **velocity only**.
22
22
 
23
- **The `executor-vehicle` item (the carrier the project already CONFIGURED — beside the `agents` offer, never the same item).** It fires on ONE pair of facts: a slot in `docs/ai/orchestration.json` names `subagent` while the executor vehicle (`.claude/agents/executor.md`) is `missing` or `unusable` — so every such slot silently degrades to solo. That is a configured declaration that is broken, hence **`attention`**, not an offer; its text names the state, its reason and how many configured slots are affected across the six activities (`plan-authoring`, `plan-execution`, `routine`, `feedback-triage`, `epic`, `task`), covering the subagent-capable slots `plan-authoring.author`, `plan-authoring.fold`, `plan-execution.execute`, `routine.carrier`, `epic.author`, `task.author` and `task.execute`. **`placed` and `customized` render NOTHING** (a working vehicle is not a recommendation), a config naming no `subagent` slot renders nothing, and a config the validated reader refuses is a stated **skip**, never an item computed over a shape nothing accepted. It is deliberately DISTINCT from its two neighbours, neither of which can observe this state: the `agents` offer converges the moment nothing is left to PLACE — which a present-but-unusable executor also satisfies — and the review-recipe probe skips a slot configured `subagent` by construction. Its apply is the agents writer's own `cheap-agents.mjs --apply` (the same writer the `agents` item previews; here the rendered line IS the mutation, so read the item's state before confirming), and its `recipe:` line is the hidden-mode reconcile follow-up (`hide-footprint --reconcile`), for a hidden deployment only. Honest limit on convergence: the apply places an ABSENT vehicle, so it clears `missing`; it STOPs on a symlink, so on `unusable` the item's apply line carries a `HAND-APPLY:` precondition naming each refusal the advisor observes; an executor whose frontmatter is invalid is re-derived by the apply itself. Risk profile: writes under `.claude/agents/` only — the executor's body re-derived from `docs/ai/vehicles.json`, every other vehicle create-only — never `settings.json`, never a commit.
23
+ The harness's own configuration — the project `.claude/settings.json`, `.claude/settings.local.json`, `.claude/agents/`, `.claude/hooks/`, `.claude/skills/`, `.mcp.json`, the git hooks dir, `.git/config` and every home path outside the project — is written ONLY by `init`, run by the user in a console from the project's folder. Console writers and `family-freshness` carry this whole apply slot: `applies at your next npx @sabaiway/agent-workflow-kit@latest init, run from this project's folder`. Env-dependent variants append `; if init reports it not pending, restart the agent from that console`; their notes name them below. The other console variants without a writer argv keep today's text beside their lane until S14.
24
+
25
+ **The `executor-vehicle` item (the carrier the project already CONFIGURED — beside the `agents` offer, never the same item).** It fires on ONE pair of facts: a slot in `docs/ai/orchestration.json` names `subagent` while the executor vehicle (`.claude/agents/executor.md`) is `missing` or `unusable` — so every such slot silently degrades to solo. That is a configured declaration that is broken, hence **`attention`**, not an offer; its text names the state, its reason and how many configured slots are affected across the six activities (`plan-authoring`, `plan-execution`, `routine`, `feedback-triage`, `epic`, `task`), covering the subagent-capable slots `plan-authoring.author`, `plan-authoring.fold`, `plan-execution.execute`, `routine.carrier`, `epic.author`, `task.author` and `task.execute`. **`placed` and `customized` render NOTHING** (a working vehicle is not a recommendation), a config naming no `subagent` slot renders nothing, and a config the validated reader refuses is a stated **skip**, never an item computed over a shape nothing accepted. It is deliberately DISTINCT from its two neighbours, neither of which can observe this state: the `agents` offer converges the moment nothing is left to PLACE — which a present-but-unusable executor also satisfies — and the review-recipe probe skips a slot configured `subagent` by construction. Its apply is the console route line above; `init` previews the agents writer's own `cheap-agents.mjs` and applies it with `--apply` on the terminal yes. Its `recipe:` line carries each precondition the advisor observes beside that route line, followed by the hidden-mode reconcile follow-up (`hide-footprint --reconcile`), for a hidden deployment only. Honest limit on convergence: the apply places an ABSENT vehicle, so it clears `missing`; it STOPs on a symlink, so on `unusable` the `recipe:` line names each refusal as a precondition; an executor whose frontmatter is invalid is re-derived by the apply itself. Risk profile: writes under `.claude/agents/` only — the executor's body re-derived from `docs/ai/vehicles.json`, every other vehicle create-only — never `settings.json`, never a commit.
24
26
 
25
27
  **Per-item posture notes (the consent moment — surface BEFORE running or handing over the apply):**
26
28
 
27
- - `bridge-tier` — the rendered line IS the mutation: `velocity-profile --apply --bridge-tier` writes the project `.claude/settings.json` on that one run. It seeds, for each placed bridge only, the review wrapper's `code`-mode allow rule (a wrapper then runs UNATTENDED: it reads any repo file it is pointed at and sends the assembled payload to its subscription backend), the quoted grounding pre-step rule when `agy-review` is placed, the wrapper names in `sandbox.excludedCommands`, and the declared hosts of the placed bridges (each bundled manifest's `networkHosts`) in `sandbox.network.allowedDomains`. Where the host honours the settings sandbox keys, every sandboxed command of the project can then reach those hosts, not only the wrappers, and the `--autonomy` preview reports them as a network weakening. The writable state dirs (`writableDirs`) stay hand-apply (the sandbox lanes below). The item fires while an allow rule, an exclusion or a declared host of a placed bridge is missing, so a project wired before the tier seeded hosts is offered the one re-run.
29
+ A variant's posture note, where its key has one, is printed by `init` from this installed mode doc (the one source); a variant without a note shows its preview alone.
30
+
31
+ - `bridge-tier` — the lane is console and env-dependent: `init` previews `velocity-profile --bridge-tier` in your console and applies it on the terminal y, writing the project `.claude/settings.json`. It seeds, for each placed bridge only, the review wrapper's `code`-mode allow rule (a wrapper then runs UNATTENDED: it reads any repo file it is pointed at and sends the assembled payload to its subscription backend), the quoted grounding pre-step rule when `agy-review` is placed, the wrapper names in `sandbox.excludedCommands`, and the declared hosts of the placed bridges (each bundled manifest's `networkHosts`) in `sandbox.network.allowedDomains`. Where the host honours the settings sandbox keys, every sandboxed command of the project can then reach those hosts, not only the wrappers, and the `--autonomy` preview reports them as a network weakening. The writable state dirs (`writableDirs`) stay hand-apply (the sandbox lanes below). The item fires while an allow rule, an exclusion or a declared host of a placed bridge is missing, so a project wired before the tier seeded hosts is offered the one re-run.
28
32
  - `sandbox-lane` — surface this note TOGETHER with the sandbox-lanes ladder below (the ladder IS the practical half of the note — inline, never a pointer). Pure DISCOVERABILITY: it surfaces the manifest-declared observed session-sandbox recipe (egress hosts ∪ resolved writable state dirs — `networkHosts` ∪ `writableDirs` of the wired bridges' `capability.json`, the single documentation source) and converges on a NEUTRAL fingerprint acknowledgement recorded by the consent-gated **ack writer** into the family-owned `docs/ai/acks.json` (`sandboxLaneAck`; a changed recipe re-fires the item). The store is family-owned so no host settings validator guards it (AD-055 relocated the ack off the Claude Code settings schema, which rejected the unknown key); the legacy `"agentWorkflow": { "sandboxLaneAck": … }` settings-scope key is still READ for one deprecation window (until the next kit MAJOR). It never claims the settings security keys take effect on any host class; the tier seeds `sandbox.network.allowedDomains` with the placed bridges' declared hosts only through the consented `--bridge-tier` (AD-172, which reverses the bridge council of 2026-07-11 for those hosts alone; the egress they widen for every sandboxed command is stated at that consent), and `sandbox.filesystem.allowWrite` stays unseeded (that council: a write allowance on CLI state dirs would expose credential dirs). Posture history: an IDE-managed session sandbox was live-observed (2026-07-11/12) ignoring hand-applied settings security keys in BOTH scopes, and codex needs a writable HOME (EROFS `~/.codex` in-sandbox); whether a session's sandbox honors the settings keys is runtime-unknowable from the advisor (a denial-only signal) — which is exactly why the item states only detectable facts and no zero-prompt promise on any host class.
29
33
 
30
34
  - `read-lane` — enabling the opt-in read-only compound lane auto-approves *compounds* (and singles) of the seeded read-only core that carry ZERO shell metaprogramming: an UNATTENDED trust extension, bounded by the audited read-only core (never a command outside it; prompt-bypass only, never a sandbox bypass) and applied regardless of which of those core commands you seeded as individual settings rules. It is a PROJECT-PERSISTENT declaration in `docs/ai/lanes.json` — every future session, subagents' Bash too where the host fires hooks on subagent Bash, and (committed) every checkout. The apply depends on state: when the lane is OFF, it is the `gate-hook --read-lane` preview (whose own currency check refuses a stale hook — a pre-1.48 hook never reads `lanes.json`); when the placed hook is STALE (an enabled lane over an old hook) or MISSING, the item instead surfaces a **delete-to-reseed** / re-place recovery (a destructive `rm` + `--apply`, an attention item — never the safe preview). Risk profile: a bounded read-only trust-posture extension — no write/exec exposure beyond the audited core.
@@ -33,22 +37,26 @@ Run `node ${CLAUDE_SKILL_DIR}/tools/recommendations.mjs --cwd <project-root> [--
33
37
  - `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.
34
38
 
35
39
  - `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.
36
- - `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.
40
+ - `gate-hook` — the base arm is the ordinary opt-in wiring offer on the console lane: `init` previews and applies it on the terminal yes. 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.
37
41
  - `enforcement` — copies the bundled `references/scripts/*.mjs` runnable scripts and their tests, then installs the pre-commit hook. It is HAND-APPLY: the agent never runs it; the offer renders only when `scripts/` is absent or a plain directory contains no bundled basename, so the copy never clobbers an existing file and never follows a link. The hook invokes `node` directly, so a committer without it on PATH gets a loud failure. The installer refuses to overwrite a pre-commit hook it did not write — on that refusal stop and merge the hook by hand (bootstrap step 9); the copied scripts stay in place. Risk profile: local tooling and a Git hook are added only when the maintainer applies the shown command.
38
- - `mcp-channel` — the offer is to REGISTER the kit's read-only stdio MCP server in this project, which is a different kind of consent from the other velocity items: a registration is a command your MCP client will RUN, so the apply here is the mode's own **flagless preview**, which prints the exact entry and writes nothing — the `--apply` that follows is a SEPARATE step you run after reading that entry, and the consent flow never carries it. What the registration buys: path questions and literal searches become typed tool calls whose arguments are named JSON fields instead of a string handed to a shell, so a pipe, a redirect or a quote inside a pattern or a path stays DATA and is never interpreted — they are perfectly legal bytes to search for, there is simply no shell to read them as operators. Posture: the server is a **read-only child of your client** (path/type/size/line facts and literal search over this project root; no write and no exec API), and like the client itself it runs **outside the Bash sandbox** — the sandbox is not what bounds it, the server's own root containment is. The two allow rules it adds make those two tools promptless and nothing else. Two arms are **HAND-APPLY** and never run for you: **`.differing`** — an `agent-workflow` entry already stands in `.mcp.json` and **structurally differs** from what this kit copy would write (another kit copy, a hand-edited path, an added `env`; the comparison ignores key order, so re-serialized identical bytes are the same registration), and silently changing what an MCP server launches is exactly what consent must not slide past, so the remedy is your edit; and the **masked** arm, where an OS sandbox hides `.mcp.json` behind a device node — the kit cannot write there, so it hands you the text to paste from outside the sandbox. When the file is masked but the settings half is already complete, the item **does not render at all**: what is unobservable becomes a stated SKIP, so optimality is withheld rather than a registration you already made being offered again. Stated limit: this item does **not** detect a `disabledMcpjsonServers` veto, so a converged `mcp-channel` means *what the mode writes is in place*, not *the client will load it* — see `${CLAUDE_SKILL_DIR}/references/modes/mcp.md` for why that check was subtracted rather than half-built. Risk profile: a new read-only channel your client will launch; no write or exec exposure, and no existing declaration is touched.
42
+ - `mcp-channel` — the offer is to REGISTER the kit's read-only stdio MCP server in this project on the console lane: a registration is a command your MCP client will RUN, so `init` previews the exact entry in your console and applies the registration on the terminal y. What the registration buys: path questions and literal searches become typed tool calls whose arguments are named JSON fields instead of a string handed to a shell, so a pipe, a redirect or a quote inside a pattern or a path stays DATA and is never interpreted — they are perfectly legal bytes to search for, there is simply no shell to read them as operators. Posture: the server is a **read-only child of your client** (path/type/size/line facts and literal search over this project root; no write and no exec API), and like the client itself it runs **outside the Bash sandbox** — the sandbox is not what bounds it, the server's own root containment is. The two allow rules it adds make those two tools promptless and nothing else. The **`.differing`** arm is also a console item: an `agent-workflow` entry already stands in `.mcp.json` and **structurally differs** from what this kit copy would write (another kit copy, a hand-edited path, an added `env`; the comparison ignores key order, so re-serialized identical bytes are the same registration). `init` previews `mcp --replace`: the entry standing in `.mcp.json` and the entry it would write, replaced only on that terminal y. Stated limit: this item does **not** detect a `disabledMcpjsonServers` veto, so a converged `mcp-channel` means *what the mode writes is in place*, not *the client will load it* — see `${CLAUDE_SKILL_DIR}/references/modes/mcp.md` for why that check was subtracted rather than half-built. Risk profile: a new read-only channel your client will launch; no write or exec exposure; a differing registration is replaced only on that terminal y and every other declaration is kept.
39
43
  - 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.
40
44
  - `spec-adoption` — the feature-spec layer's ADOPTION STATE, judged from `docs/ai/specs/` alone through the same census `spec-check --all` runs, so a store the checker would refuse to observe is never counted here either. Both arms are OFFERS (`optional`) — the layer is opt-in, and `attention` stays reserved for a configured declaration that is broken. **`not-adopted`** (no store): every plan then cites zero governing specs by default and nothing says adoption never started; its apply is the spec-layer ensure one-liner (`ensure-configs --reconcile --only specs`, the same seed `upgrade` runs — it writes the reader/checker pairs and the store root, create-only, and skips with a stated reason where no Node evidence exists), and its `recipe:` line carries a NAMED **HAND-APPLY alternative** — the DECLINE preview (`ack-write --lane spec-adoption`, a dry-run that prints the exact `--apply`), which records `specAdoptionAck` in the family-owned `docs/ai/acks.json`. **The two are exclusive, and the order is part of the contract:** ask which the maintainer wants BEFORE the confirmation; on "seed" run the apply; on "decline" run NOTHING — hand over the preview line and let them run it and its printed `--apply`; never run the seed after a decline was chosen, and never run the decline preview for them. **`adopting`** (a store with no live contract): an optional item naming the draft count; its apply is that same decline preview (a dry-run; after the SAME confirmation you run the `--apply` it prints), and the honest remedy is authoring a contract from the skill's `SPEC_TEMPLATE.md`. A recorded decline silences BOTH arms and shows on `status` as ` — declined`; one live contract makes the decline moot. An unreadable store is a stated skip, never `flow optimal`. Risk profile: the seed writes only create-only files under `scripts/` and `docs/ai/specs/`; the decline writes one key into the ack store; nothing here authors a spec or blocks a plan.
41
45
  - `profile-gap` — each item's apply is a PREVIEW that writes nothing, and consent takes two steps: the confirmed pick runs the preview and you relay every line it prints (the environment-resolved slot values, each checker `entry:` line with its trust-chain disclosure, a Communication region the insert plans in the same run); the same line with ` --apply` appended runs only on a SECOND yes over those lines, never carried by the first. Items whose apply lines are byte-equal are one writer and take one answer: the tier preview's `--apply` writes the absent slots, the store seed and the queue seed together; the checker verb writes only its own entries; the insert writes every region it plans, all or nothing, so a `communication-absent` skip beside a declined `story-sessions-section` gets its own ask after the insert's full preview. A no records nothing and the item stays on the screen. A DECLINE is asked apart from later, before anything runs, and runs `node ${CLAUDE_SKILL_DIR}/tools/tier-preview.mjs --decline --cwd <project-root>` only when the user refuses EVERY item the tier preview then reports offered: it records each of them at the shipped lineage, and a declined item leaves the screen until a lineage step changes the profile. An undecidable entry is a stated skip naming its reason, the remedy by reason: a prior → the upgrade's `lens` reconcile; an absent region → the insert; a customized region → a hand edit only (the lens preserves it), so that skip stands and withholds `flow optimal`. Risk profile: the tier preview's `--apply` rewrites `docs/ai/orchestration.json` in canonical form with the `_README` note and creates `docs/ai/epics/.gitkeep` and `docs/plans/queue.md` create-only; the insert adds regions to `docs/ai/agent_rules.md`; the checker verb adds entries to the PRIVILEGED `docs/ai/gates.json`; the decline writes `docs/ai/profile-declines.json`; nothing here commits.
42
- - `jev-connect` — the apply is HAND-APPLY: the user runs the connect command in a terminal of their own, never the agent (the command refuses without a terminal, and a terminal is not consent); when the recipe line opens with `run the apply in <place>`, that place is the terminal to name. The command reads the key with no echo, makes one network call to api.typesafe.ai with the vendor's sample text only on the user's own run, and writes the key in plain text — as managed export lines in the shell's startup files (bash: ~/.bashrc and the login file, created as ~/.bash_profile when none exists; zsh: ~/.zshrc), as the kit's own file ~/.config/fish/conf.d/typesafe-api-key.fish for fish, or on Windows as the user environment variable TYPESAFE_API_KEY, stored in the Windows user environment; a process already running at the save can keep its old environment and hand it to what it starts — the guide's key mark confirms the agent sees it (a file is readable by any process running as that user, and by other local accounts when one is world-readable in a traversable home); the vendor keeps data under its processing agreement and offers zero retention only to enterprise accounts. The item reads the environment the agent's commands run in: a host setting that filters it shows the item while the key is set in the user's shell. The decline is the HAND-APPLY alternative on the recipe line, asked apart BEFORE the confirmation (the `spec-adoption` order): on "connect" hand over the apply line, on "decline" run nothing and hand over the recipe line; the decline is recorded per project as `jevConnectAck` in `docs/ai/acks.json`. Risk profile: a plain-text key in the user's startup files or the Windows user environment, written by a command the user runs; one network call from that run.
43
- - `jev-skill` — the apply is HAND-APPLY: the user runs the skill command in a terminal of their own, never the agent (a terminal is not consent); when the recipe line opens with `run the apply in <place>`, that place is the terminal to name. The skill is third-party text — the vendor's, MIT — that the kit ships pinned and installs as a verified copy into three agent skill roots: `~/.agents/skills`, the Claude Code skills dir (`CLAUDE_CONFIG_DIR` or `~/.claude`) and `~/.gemini/config/skills`. It is never updated silently: a copy at an earlier pin is replaced only by that command, and a foreign copy is left untouched and named on the recipe line. A copy the vendor's `skills` CLI wrote with the same bytes reads current, and a later pin move replaces it like a kit copy; a vendor plugin install is invisible to the file check, so the Claude Code root may read absent while the plugin serves it. Only when the user asks to see the plan does the agent run the command without `--apply` (a read-only dry run). The decline is the HAND-APPLY alternative on the recipe line, asked apart BEFORE the confirmation: on "install" hand over the apply line, on "decline" run nothing and hand over the recipe line; the decline is recorded per project as `jevSkillAck` in `docs/ai/acks.json`. Risk profile: third-party text written into the user's agent skill roots by a command the user runs; no network.
46
+ - `jev-connect` — the lane is console and env-dependent: `init` runs the connect command with the terminal inherited on its own consent line, never the agent (the command refuses without a terminal, and a terminal is not consent); when the recipe line opens with `run the apply in <place>`, that place is the console to run `init` in. The command reads the key with no echo, makes one network call to api.typesafe.ai with the vendor's sample text only on the user's own run, and writes the key in plain text — as managed export lines in the shell's startup files (bash: ~/.bashrc and the login file, created as ~/.bash_profile when none exists; zsh: ~/.zshrc), as the kit's own file ~/.config/fish/conf.d/typesafe-api-key.fish for fish, or on Windows as the user environment variable TYPESAFE_API_KEY, stored in the Windows user environment; a process already running at the save can keep its old environment and hand it to what it starts — the guide's key mark confirms the agent sees it (a file is readable by any process running as that user, and by other local accounts when one is world-readable in a traversable home); the vendor keeps data under its processing agreement and offers zero retention only to enterprise accounts. The item reads the environment the agent's commands run in: a host setting that filters it shows the item while the key is set in the user's shell. The decline is the HAND-APPLY alternative on the recipe line, asked apart BEFORE the confirmation (the `spec-adoption` order): on "connect" hand over the apply line, on "decline" run nothing and hand over the recipe line; the decline is recorded per project as `jevConnectAck` in `docs/ai/acks.json`. Risk profile: a plain-text key in the user's startup files or the Windows user environment, written by a command the user runs; one network call from that run.
47
+ - `jev-skill` — the lane is console; both `jev-skill` and `jev-skill.earlier` are env-dependent: `init` applies the skill command on the batch terminal yes, never the agent; when the recipe line opens with `run the apply in <place>`, that place is the console to run `init` in. The skill is third-party text — the vendor's, MIT — that the kit ships pinned and installs as a verified copy into three agent skill roots: `~/.agents/skills`, the Claude Code skills dir (`CLAUDE_CONFIG_DIR` or `~/.claude`) and `~/.gemini/config/skills`. It is never updated silently: a copy at an earlier pin is replaced only by that command, and a foreign copy is left untouched and named on the recipe line. A copy the vendor's `skills` CLI wrote with the same bytes reads current, and a later pin move replaces it like a kit copy; a vendor plugin install is invisible to the file check, so the Claude Code root may read absent while the plugin serves it. Only when the user asks to see the plan does the agent run the command without `--apply` (a read-only dry run). The decline is the HAND-APPLY alternative on the recipe line, asked apart BEFORE the confirmation: on "install" hand over the apply line, on "decline" run nothing and hand over the recipe line; the decline is recorded per project as `jevSkillAck` in `docs/ai/acks.json`. Risk profile: third-party text written into the user's agent skill roots by a command the user runs; no network.
44
48
  - `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.
45
49
 
46
50
  **Sandbox lanes (what to DO with the `sandbox-lane` recipe, per host class):**
47
51
 
52
+ The sandbox items state: on native Windows Claude Code runs no sandbox: this item takes no effect there. `sandbox-provision.installable` is env-dependent and carries the console route line with the restart sentence; the bare `sandbox-provision` keeps its current text.
53
+ Only Claude Code on Linux/WSL2 is measured; Codex, Devin and agy as hosts and macOS are unmeasured.
54
+ A first session may meet ordinary prompts until the autonomy render lands; a chat apply is promptless only where the host auto-allows sandboxed commands.
55
+
48
56
  - **Settings-native sandbox** (the harness reads the `.claude/settings.json` sandbox keys): where the harness honours the `--bridge-tier` wiring's `excludedCommands`, the wrappers run OUTSIDE the sandbox and never consult `allowedDomains`/`allowWrite` — nothing beyond the tier. A harness may not honour that entry (observed 2026-10-06: Claude Code 2.1.291 ran an agent's plain `codex-review` inside its sandbox with the project entry present, and that run still reached `chatgpt.com`, which only `allowedDomains` admitted); there a recipe host outside `allowedDomains` is refused by the harness proxy or held for a network prompt (a refusal reads `Tunnel connection failed: 403` or `CONNECT tunnel failed, response 403` in the run). The lane for the hosts is the `--bridge-tier` re-run (the `bridge-tier` item offers it while a declared host of a placed bridge is missing): it seeds the placed bridges' declared hosts into `sandbox.network.allowedDomains`, and the agent never hand-adds them. For a wrapper the harness keeps sandboxed the hand-apply `allowWrite` step for the writable state dirs stays: the `filesystem.allowWrite` half of the shape in (2) below, filled from this recipe — the agent does not add it and the kit never writes it. Record the ack (the ack-write one-liner) once the lane is confirmed.
49
57
  - **Harness-managed sandbox** (an IDE/session-imposed sandbox that ignores the settings keys) — a NARROWEST-SCOPE ladder: (1) prefer wrapper/command-SCOPED sandbox rules where the harness offers them (only the review wrappers gain the egress + state-dir writes); (2) a SESSION-scoped allowance (the manifest hosts + the resolved state dirs for the whole session) is an INFORMED WIDENING — it carries the same blast-radius class the settings security keys were rejected for (every sandboxed command in the session gains that egress and those writable credential dirs), acceptable only as the maintainer's deliberate choice for a review-heavy session. Its **paste-ready hand-apply shape** (the kit writes `allowedDomains` only via `--bridge-tier`, and never `allowWrite`) is, in `.claude/settings.json`, `"sandbox": { "network": { "allowedDomains": ["<egress host>", …] }, "filesystem": { "allowWrite": ["<writable state dir>", …] } }` — fill `<egress host>` / `<writable state dir>` from THIS recipe (the wired bridges' `capability.json` `networkHosts` ∪ resolved `writableDirs`); one consent then lands durably in config. (3) Else the per-run consented bypass. Record the ack (the ack-write one-liner) once a lane is chosen — choosing the bypass, or consciously declining, also counts.
50
58
  - Either way the recipe is **observed-minimal, honestly incomplete** — a blocked host names itself at run time; extend by hand and re-ack (the fingerprint moves with the recipe, not with your extensions).
51
59
 
52
- **Invariants:** read-only (never writes, never commits, never runs a subscription CLI) · `--cwd` required · present-even-when-empty · verdict-first from frozen templates · probe failures degrade to stated skip lines · apply one-liners are cwd-independent (absolute tool paths + a pinned `--cwd`; the sandbox-provision item pins via a `cd <root> &&` prefix — the doctor reads its cwd; the ONE exception is the `set-autonomy` item, a conversational skill invocation explicitly labeled *run IN the target project*) · `sandbox.filesystem.allowWrite` stays hand-apply territory, and `sandbox.network.allowedDomains` is seeded only by the consented `velocity-profile --bridge-tier` · the sandbox-lane convergence is a neutral acknowledgement recorded into `docs/ai/acks.json` by the consent-gated ack writer (the legacy settings-scope key read for one deprecation window), never a security key · the `worktrees-dir` item converges on a declared `allowWrite` entry covering its probed dir, or on its own `worktreesDirAck` lane in that same store (recorded by its apply one-liner, never by the `recipe:` line).
60
+ **Invariants:** read-only (never writes, never commits, never runs a subscription CLI) · `--cwd` required · present-even-when-empty · verdict-first from frozen templates · probe failures degrade to stated skip lines · chat apply one-liners are cwd-independent (absolute tool paths + a pinned `--cwd`; the ONE exception is the `set-autonomy` item, a conversational skill invocation explicitly labeled *run IN the target project*) · the bare `sandbox-provision` item pins via a `cd <root> &&` prefix — the doctor reads its cwd · console writers and `family-freshness` carry the route line; the other console variants keep today's text until S14 · `sandbox.filesystem.allowWrite` stays hand-apply territory, and `sandbox.network.allowedDomains` is seeded only by the consented `velocity-profile --bridge-tier` · the sandbox-lane convergence is a neutral acknowledgement recorded into `docs/ai/acks.json` by the consent-gated ack writer (the legacy settings-scope key read for one deprecation window), never a security key · the `worktrees-dir` item converges on a declared `allowWrite` entry covering its probed dir, or on its own `worktreesDirAck` lane in that same store (recorded by its apply one-liner, never by the `recipe:` line).
53
61
 
54
62
  **Exit codes:** `0` report rendered (items or the empty state); `1` error (e.g. `--cwd` is not a directory); `2` usage.
@@ -113,11 +113,12 @@ Requires: ${CLAUDE_SKILL_DIR}/references/shared/report-footer.md · ${CLAUDE_SKI
113
113
  - **A skip line that contradicts the observed tree is a FINDING, never pasted as neutral.** Every ensure skip names the fact it proved (the probes that answered absent, the layout it read); if the tree you can see disproves that reason — a "no Node" skip beside deployed Node scripts, a "not deployed" skip beside a stamped `docs/ai` — do not relay the line as an outcome: raise it in the report as a defect of the tool, with the contradicting fact named, and stop short of any step that would build on the skip.
114
114
  - **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 **eight project-configuration ensure** (`configs`) lines exactly as the one ensure run composed them (the ensures and 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; for the navigator, relay the **late** `--only index` line, not the early one; 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) and the **story-sessions section** outcome (its own set: refreshed / already current / custom edit preserved + note / section absent — noted / over the file's line cap — refused); the **full-flow offer** (`tier`) lines; 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`).
115
115
  - **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*.
116
- - **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).
116
+ - **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 chat applies: the user picks items in plain language; surface each picked item's posture note, get the explicit confirm, then run EXACTLY the rendered chat 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).
117
+ - **Recommendations lanes — both exits.** The agent applies only chat items, on the chat yes. When a console item's apply slot is the route line, only `init` applies it, run by the user in a console from the project's folder. The agent never runs a console item's line, never retries a refused write outside the sandbox and continues past every console item; every failure of a chat step is a STOP.
117
118
  - **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`.
118
119
  - **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 (a behind member first, else `setup` / `recipes` / `velocity` / `agents` / `hook`).
119
120
  - **Then ask before committing — never auto-commit.** If step 3 added the slot (or anything else changed), report it and ask. If step 3 was a pure zero-diff no-op and nothing else changed, give the plain **settings already current — no update needed** message (the *Success state* contract in `${CLAUDE_SKILL_DIR}/references/shared/report-footer.md`) and still print the read-only version block (installed package versions) + backend line — but **no `docs/ai` structure version and no two-axes note** (nothing changed, so the number is inert here).
120
121
  5. Show the relevant `${CLAUDE_SKILL_DIR}/CHANGELOG.md` diff (entries newer than the project's stamp).
121
122
  6. **Collect the migration answers FIRST, then apply.** Run `node ${CLAUDE_SKILL_DIR}/tools/migration-notes.mjs --cwd <project>` and relay its stdout lines verbatim, in order; a non-zero exit relays its one named line and STOPs, except `stamp-absent`, which routes back to step 1. Then run the preview `node ${CLAUDE_SKILL_DIR}/tools/migration-blocks.mjs --cwd <project>`. If its lines say both blocks are missing (pre-1.1.0), 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; a single missing block keeps its single ask (step 7). Then apply exactly the notes the tool listed, in its order ("only those newer than the project's stamp" is why, never a second selection). Migrations are **idempotent** — safe to re-run.
122
123
  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. For a block the preview names as needing its answer, **ask the user their conversational language** (`--language`) or **whether the agent may attribute work to itself / AI** (`--attribution`, defaulting to `off`), never re-asking a step-6 answer; then re-run the preview with `--apply` and the answers, relaying its lines. Exit 1 `answer-missing`: ask exactly what its lines name and re-run `--apply` once; a second one STOPs. Any other exit 1 is a named refusal: relay it with its recovery (`cap`: trim `AGENTS.md`, re-run) and STOP before the finalizer and the stamp. **Then, before the stamp, re-run the LATE navigator finalizer:** the migrations of steps 6–7 may have changed `docs/ai` files, so run the one op again — `node ${CLAUDE_SKILL_DIR}/tools/ensure-configs.mjs --reconcile --only index --cwd <project>` — and relay THAT line in the step-8 report (idempotent: an untouched tree reports `already-current`; a failure STOPs this upgrade like any other ensure).
123
- 8. Re-stamp `docs/ai/.workflow-version` to the **deployment-lineage head** (`4.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 **eight project-configuration ensure** (`configs`) lines (enumerated in step 3) — the navigator one being the late `--only index` run at the end of step 7, the **`gates-migration`** result, the **placed-bridge refresh** (`bridges`) lines (pasted verbatim), the **agent-rules lens** (`lens`) + **Communication-section** + **story-sessions section** outcomes (same outcome sets as step 4), the **full-flow offer** (`tier`) outcome, 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.
124
+ 8. Re-stamp `docs/ai/.workflow-version` to the **deployment-lineage head** (`4.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 **eight project-configuration ensure** (`configs`) lines (enumerated in step 3) — the navigator one being the late `--only index` run at the end of step 7, the **`gates-migration`** result, the **placed-bridge refresh** (`bridges`) lines (pasted verbatim), the **agent-rules lens** (`lens`) + **Communication-section** + **story-sessions section** outcomes (same outcome sets as step 4), the **full-flow offer** (`tier`) outcome, 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 chat applies (per picked item: posture note → explicit confirm → run EXACTLY the rendered chat 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.