@sabaiway/agent-workflow-kit 3.14.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/README.md +3 -3
  3. package/SKILL.md +1 -1
  4. package/bridges/antigravity-cli-bridge/SKILL.md +12 -8
  5. package/bridges/antigravity-cli-bridge/bin/agy-review.sh +735 -55
  6. package/bridges/antigravity-cli-bridge/bin/agy-review.test.mjs +899 -51
  7. package/bridges/antigravity-cli-bridge/bin/agy.sh +4 -3
  8. package/bridges/antigravity-cli-bridge/bin/agy.test.mjs +23 -0
  9. package/bridges/antigravity-cli-bridge/capability.json +14 -4
  10. package/bridges/antigravity-cli-bridge/references/driving-agy.md +12 -4
  11. package/bridges/antigravity-cli-bridge/references/models-and-flags.md +4 -3
  12. package/bridges/antigravity-cli-bridge/references/review-prompt.md +65 -2
  13. package/bridges/codex-cli-bridge/SKILL.md +1 -1
  14. package/bridges/codex-cli-bridge/bin/codex-exec.sh +2 -1
  15. package/bridges/codex-cli-bridge/bin/codex-review.sh +63 -13
  16. package/bridges/codex-cli-bridge/bin/codex-review.test.mjs +38 -0
  17. package/bridges/codex-cli-bridge/capability.json +1 -1
  18. package/capability.json +1 -1
  19. package/package.json +1 -1
  20. package/references/agents/review-lens.md +39 -0
  21. package/references/hooks/gate-approve.mjs +15 -0
  22. package/references/modes/agents.md +11 -2
  23. package/references/modes/autonomy-doctor.md +2 -0
  24. package/references/modes/backends.md +2 -0
  25. package/references/modes/bootstrap.md +2 -0
  26. package/references/modes/bridge-settings.md +4 -1
  27. package/references/modes/commit-guard.md +2 -0
  28. package/references/modes/core-evidence.md +2 -0
  29. package/references/modes/coverage-check.md +2 -0
  30. package/references/modes/doc-parity.md +2 -0
  31. package/references/modes/gates.md +2 -0
  32. package/references/modes/grounding.md +2 -0
  33. package/references/modes/help.md +2 -0
  34. package/references/modes/hook.md +4 -1
  35. package/references/modes/migrate-adr-store.md +2 -0
  36. package/references/modes/procedures.md +2 -0
  37. package/references/modes/recipes.md +2 -0
  38. package/references/modes/recommendations.md +4 -3
  39. package/references/modes/review-state.md +2 -0
  40. package/references/modes/sandbox-masks.md +2 -0
  41. package/references/modes/set-autonomy.md +2 -0
  42. package/references/modes/set-recipe.md +3 -0
  43. package/references/modes/setup.md +2 -0
  44. package/references/modes/state-block-guard.md +9 -5
  45. package/references/modes/status.md +4 -1
  46. package/references/modes/uninstall.md +2 -0
  47. package/references/modes/upgrade.md +2 -0
  48. package/references/modes/velocity.md +7 -0
  49. package/references/modes/worktrees.md +2 -0
  50. package/tools/bridge-settings-read.mjs +40 -10
  51. package/tools/bridge-settings.mjs +22 -7
  52. package/tools/cheap-agents.mjs +15 -5
  53. package/tools/commands.mjs +2 -2
  54. package/tools/core-evidence.mjs +29 -2
  55. package/tools/detect-backends.mjs +1 -1
  56. package/tools/manifest/schema.md +7 -0
  57. package/tools/manifest/validate.mjs +8 -0
  58. package/tools/presentation.mjs +1 -1
  59. package/tools/procedures.mjs +9 -2
  60. package/tools/recipes.mjs +4 -1
  61. package/tools/recommendations.mjs +157 -59
  62. package/tools/renderers.mjs +10 -1
  63. package/tools/review-state.mjs +4 -0
  64. package/tools/view-model.mjs +3 -1
@@ -30,8 +30,8 @@
30
30
  # AGY_HARD_TIMEOUT=8m agy-run "..." # override the hard wall-clock cap (timeout(1))
31
31
  # AGY_MAX_PROMPT_BYTES=60000 agy-run @big.md # LOWER the single-argv byte ceiling (default 120000;
32
32
  # # the override only tightens it — it can never exceed the OS ~131072 limit)
33
- # agy-run "..." -- --add-dir . --dangerously-skip-permissions
34
- # # passthrough agy flags (future flows)
33
+ # agy-run "..." -- --add-dir . # passthrough agy flags (this wrapper stays
34
+ # # flow-agnostic; it never widens agy's own permissions)
35
35
  set -euo pipefail
36
36
 
37
37
  # --- --help / -h (pre-preflight: no agy, no login needed) ----------------------
@@ -94,7 +94,7 @@ aw_settings_file() {
94
94
  printf '%s/agent-workflow/bridge-settings.conf' "${XDG_CONFIG_HOME:-$HOME/.config}"
95
95
  }
96
96
  aw_settings_known() {
97
- case " CODEX_SERVICE_TIER CODEX_HARD_TIMEOUT CODEX_REVIEW_MAX_TOTAL_BYTES AGY_HARD_TIMEOUT AGY_REVIEW_ALLOW_ADDDIR " in
97
+ case " CODEX_SERVICE_TIER CODEX_HARD_TIMEOUT CODEX_REVIEW_MAX_TOTAL_BYTES AGY_HARD_TIMEOUT AGY_REVIEW_ALLOW_ADDDIR AGY_REVIEW_MAX_TOTAL_BYTES " in
98
98
  *" $1 "*) return 0 ;;
99
99
  *) return 1 ;;
100
100
  esac
@@ -117,6 +117,7 @@ aw_settings_valid() {
117
117
  CODEX_REVIEW_MAX_TOTAL_BYTES) [[ "$v" =~ $int_re ]] && aw_int_in_range "$v" 1 100000000 ;;
118
118
  AGY_HARD_TIMEOUT) [[ "$v" =~ $dur_re && ! "$v" =~ $zero_re ]] ;;
119
119
  AGY_REVIEW_ALLOW_ADDDIR) [[ "$v" == "0" || "$v" == "1" ]] ;;
120
+ AGY_REVIEW_MAX_TOTAL_BYTES) [[ "$v" =~ $int_re ]] && aw_int_in_range "$v" 1 100000000 ;;
120
121
  *) return 1 ;;
121
122
  esac
122
123
  }
@@ -83,6 +83,15 @@ const RECORDING_STUB = [
83
83
  '',
84
84
  ].join('\n');
85
85
 
86
+ // Records the full argv it was invoked with, one token per line.
87
+ const ARGV_STUB = [
88
+ '#!/usr/bin/env bash',
89
+ 'if [[ -n "${AGY_STUB_ARGV:-}" ]]; then { for a in "$@"; do printf "%s\\n" "$a"; done; } > "$AGY_STUB_ARGV"; fi',
90
+ 'echo "OK reply"',
91
+ 'exit 0',
92
+ '',
93
+ ].join('\n');
94
+
86
95
  // Run the wrapper with an explicit argv (so a `@file` / `-` prompt form can be passed)
87
96
  // and optional stdin. AGY_MODEL='' drops --model so the stub argv stays clean.
88
97
  const runArgs = (home, { args, env = {}, input } = {}) =>
@@ -217,6 +226,20 @@ describe('agy.sh — --help (pre-preflight, candidate C)', () => {
217
226
  }
218
227
  });
219
228
 
229
+ // D3b: what Phase 4 retires is the AUTOMATICALLY-CONSTRUCTED --add-dir inside `agy-review code`.
230
+ // The PUBLIC passthrough contract is deliberately preserved: agy-run is a thin, flow-agnostic
231
+ // wrapper and an operator who asks for --add-dir explicitly still gets it.
232
+ it('agy-run keeps the direct --add-dir passthrough (the retirement is scoped to the review offload)', () => {
233
+ const home = makeSandbox(ARGV_STUB);
234
+ const argvFile = join(home, 'argv');
235
+ const r = runArgs(home, { args: ['prompt', '--', '--add-dir', '.'], env: { AGY_STUB_ARGV: argvFile } });
236
+ const argv = existsSync(argvFile) ? readFileSync(argvFile, 'utf8') : '';
237
+ rmSync(home, { recursive: true, force: true });
238
+ assert.equal(r.status, 0, r.stderr);
239
+ assert.match(argv, /(^|\n)--add-dir(\n|$)/, 'an operator-supplied --add-dir still reaches agy');
240
+ assert.match(argv, /(^|\n)\.(\n|$)/, 'with its argument intact');
241
+ });
242
+
220
243
  it('--help after the -- separator is passthrough payload, never intercepted', () => {
221
244
  const home = makeSandbox(RECORDING_STUB);
222
245
  const sentinel = join(home, 'sentinel');
@@ -3,7 +3,7 @@
3
3
  "schema": 1,
4
4
  "name": "antigravity-cli-bridge",
5
5
  "kind": "execution-backend",
6
- "version": "4.1.0",
6
+ "version": "5.0.0",
7
7
  "provides": ["review", "probe"],
8
8
  "posture": { "model": "Gemini 3.1 Pro (High)" },
9
9
  "roles": {
@@ -30,7 +30,7 @@
30
30
  "agy-review --continue [--decided @f] [--focus \"…\"]",
31
31
  "agy-review --conversation <id> [--decided @f] [--focus \"…\"]"
32
32
  ],
33
- "receipt": "side effect — a successful review appends one JSON receipt line to <git dir>/agent-workflow-review-receipts.jsonl (AW_REVIEW_RECEIPTS overrides; plan/diff outside a git tree: warn + skip unless overridden): fingerprint = sha256 over the canonical uncommitted-state payload (staged diff + unstaged diff + untracked-not-ignored contents — the review-payload domain; never-committable untracked paths — character/block devices, FIFOs, sockets — are excluded from the domain entirely, untracked symlinks/directories ride as name-only notes) in code mode, the artifact-file sha256 in plan/diff mode; verdict recorded verbatim from the mandated '### Verdict' section (SHIP / SHIP WITH NITS / REWORK); grounded = whether a NON-EMPTY --facts payload was supplied (code mode refuses pre-spend without one — no run, no receipt — unless --ungrounded/AGY_PROBE=1; in plan/diff an empty payload records grounded:false — fail-closed, the state gate rejects it), factsHash = sha256 of the facts payload; a continuation receipt is fresh:false (informational-only — it cannot attest the folded tree); probe = whether the run relaxed the quality guards (AGY_PROBE=1), written on EVERY receipt so it self-declares — the kit's review-state gate rejects a probe-marked receipt (a probe review never attests) and equally rejects an unmarked one (silence is not a declaration); posture = the ACTUAL run posture {model} (agy has no tier), written on EVERY receipt (D5) — the gate rejects a receipt with an absent/invalid posture (a pre-D5 wrapper minted it; re-run the review), one stderr banner line states the same posture, an ATTESTING review with AGY_MODEL explicitly emptied refuses pre-spend, and a model string carrying control bytes refuses pre-spend in every mode; a run whose output carries NO recognized '### Verdict' section — empty output included — exits 4 with NO receipt (D4: a FAILED review to RE-RUN, never a fatal session error); a write failure warns, never fails the review",
33
+ "receipt": "side effect — a successful review appends one JSON receipt line to <git dir>/agent-workflow-review-receipts.jsonl (AW_REVIEW_RECEIPTS overrides; plan/diff outside a git tree: warn + skip unless overridden): fingerprint = sha256 over the canonical uncommitted-state payload (staged diff + unstaged diff + untracked-not-ignored contents — the review-payload domain; never-committable untracked paths — character/block devices, FIFOs, sockets — are excluded from the domain entirely, untracked symlinks/directories ride as name-only notes) in code mode, the artifact-file sha256 in plan/diff mode; verdict recorded verbatim from the mandated '### Verdict' section (SHIP / SHIP WITH NITS / REWORK); grounded = whether a NON-EMPTY --facts payload was supplied (code mode refuses pre-spend without one — no run, no receipt — unless --ungrounded/AGY_PROBE=1; in plan/diff an empty payload records grounded:false — fail-closed, the state gate rejects it), factsHash = sha256 of the facts payload; a continuation receipt is fresh:false (informational-only — it cannot attest the folded tree); probe = whether the run relaxed the quality guards (AGY_PROBE=1), written on EVERY receipt so it self-declares — the kit's review-state gate rejects a probe-marked receipt (a probe review never attests) and equally rejects an unmarked one (silence is not a declaration); posture = the ACTUAL run posture {model} (agy has no tier), written on EVERY receipt (D5) — the gate rejects a receipt with an absent/invalid posture (a pre-D5 wrapper minted it; re-run the review), one stderr banner line states the same posture, an ATTESTING review with AGY_MODEL explicitly emptied refuses pre-spend, and a model string carrying control bytes refuses pre-spend in every mode; delivery = how the change set REACHED the model, currently emitted as 'inline' (the whole set rode one prompt — proven by construction) or 'fed' (a chunked feed whose per-part echo proof verified); REQUIRED on every agy code receipt and its ABSENCE is what stops a pre-fed-lane receipt attesting, while the gate accepts any well-formed declaration rather than a particular value; absent by construction on plan/diff/continuation receipts, which carry no change set; a run whose output carries NO recognized '### Verdict' section — empty output included — exits 4 with NO receipt (D4: a FAILED review to RE-RUN, never a fatal session error); a write failure warns, never fails the review",
34
34
  "notes": [
35
35
  "pre-dispatch host-diff: before the FIRST dispatch of this bridge, diff its declared networkHosts against the live sandbox allow-list — a missing host is surfaced to the maintainer BEFORE dispatching, never fired into a known prompt",
36
36
  "the review posture banner appends a banner-only timeout=<duration|uncapped> field — exactly the duration agy-run hands to timeout(1), uncapped when no timeout/gtimeout binary caps the run; INFORMATIONAL only: it never enters the receipt posture or the D5 banner↔receipt parity",
@@ -66,7 +66,7 @@
66
66
  { "value": "an ungrounded review guesses — stale-model and partial-diff false positives", "enforcement": "advisory", "source": "bin/agy-review.sh" },
67
67
  { "value": "without a non-empty --facts payload the run refuses BEFORE the spend (exit 2) — the only escapes are --ungrounded and AGY_PROBE=1", "enforcement": "enforced", "source": "bin/agy-review.sh" },
68
68
  { "value": "an ungrounded run records grounded:false and the review-state gate rejects it", "enforcement": "enforced", "source": "capability.json roles.review.contract.receipt" },
69
- { "value": "an oversized prompt refuses rather than truncate", "enforcement": "enforced", "condition": "unless AGY_REVIEW_ALLOW_ADDDIR=1 offloads it to a private --add-dir staging dir", "source": "capability.json settings.AGY_REVIEW_ALLOW_ADDDIR" }
69
+ { "value": "an oversized prompt is never truncated", "enforcement": "enforced", "condition": "code mode DELIVERS it as a chunked feed whose per-part delivery proof must verify (a missing or wrong echo exits 4 with no receipt); plan/diff refuse over the cap", "source": "bin/agy-review.sh plan_fed_review + verify_delivery_proof" }
70
70
  ],
71
71
  "customHooks": ["AGY_PROBE"]
72
72
  },
@@ -213,7 +213,17 @@
213
213
  "kind": "boolean",
214
214
  "default": "0",
215
215
  "appliesTo": ["agy-review"],
216
- "effect": "1 an oversized code review offloads the change set to a private --add-dir staging dir (re-enables the Issue-001 stall risk; the hard timeout bounds it). Default 0: an oversized prompt refuses instead."
216
+ "retired": "the --add-dir offload is retired: headless agy AUTO-DENIES its own read_file tool, so an offloaded change set could return a confident fabrication with no way to tell. An oversized code review is now DELIVERED as a chunked feed with a per-part delivery proof. The key stays recognized so an existing settings line never warns as unknown, but it arms nothing; clear it with --unset.",
217
+ "effect": "RETIRED — recognized so an existing settings line never warns as unknown, but it arms NOTHING. A set value prints the retirement notice naming the chunked-feed lane that replaced it."
218
+ },
219
+ {
220
+ "key": "AGY_REVIEW_MAX_TOTAL_BYTES",
221
+ "kind": "integer",
222
+ "min": 1,
223
+ "max": 100000000,
224
+ "default": "240000",
225
+ "appliesTo": ["agy-review"],
226
+ "effect": "agy-review code: the ceiling on the SUM of all outgoing prompt bytes the chunked feed may send (every envelope, every body, the final turn). Checked BEFORE the first turn is spent; past it the fed review refuses. An economy guard, not the correctness guard — correctness is the per-part delivery echo."
217
227
  }
218
228
  ],
219
229
  "networkHosts": ["*.googleapis.com", "accounts.google.com", "antigravity-unleash.goog", "lh3.googleusercontent.com"],
@@ -82,10 +82,18 @@ What it does for you, and what YOU must supply:
82
82
  @round1-decisions.md --focus "only the still-open items"`. The continuation sends a small DELTA
83
83
  (restated posture + new focus + the output shape + the decided list) and never re-sends the artifact
84
84
  — `agy` holds it in the conversation.
85
- - **Oversized `code` review:** the byte ceiling (`AGY_MAX_PROMPT_BYTES`, default 120000) trips with
86
- trim/split guidance. `AGY_REVIEW_ALLOW_ADDDIR=1` offloads ONLY the change set to a private staging
87
- dir and passes it via `--add-dir` (the grounding stays inline) this re-enables the Issue-001 stall
88
- risk, bounded by the hard timeout; prefer splitting into focused per-area reviews.
85
+ - **Oversized `code` review a CHUNKED FEED, not a refusal.** Past the byte ceiling
86
+ (`AGY_MAX_PROMPT_BYTES`, default 120000) the assembled change set is cut into under-cap parts at
87
+ line boundaries, fed over continuation turns (each turn: reply `OK` only), and reviewed in a final
88
+ turn. **Delivery is proven, not assumed:** the wrapper picks an interior line from each part AFTER
89
+ assembly and asks for it BY ADDRESS in a `### Delivery proof` section that comes FIRST in the
90
+ output; a missing or non-matching echo is a FAILED review — `exit 4`, **no receipt**. The cost is
91
+ stated before it is spent (N parts = N+1 subscription turns) and `AGY_REVIEW_MAX_TOTAL_BYTES`
92
+ (default 240000) refuses an over-large feed BEFORE turn 1. `plan`/`diff` keep refusing over the cap
93
+ — their artifact is an operator-supplied file the operator can split.
94
+ `AGY_REVIEW_ALLOW_ADDDIR` is **RETIRED** (recognized, arms nothing): headless `agy` auto-denies its
95
+ own `read_file`, so the offload it armed could return a confident fabrication with no way to tell.
96
+ The kit never grants that permission — the feed exists so none is needed.
89
97
  - **Model:** frontier default `Gemini 3.1 Pro (High)`; any model is allowed (a sub-frontier one earns a
90
98
  silenceable `AGY_PROBE=1` advisory). The service can still **stall on large/substantive prompts**
91
99
  (Issue-001) — keep reviews **focused**; the hard timeout is the guard.
@@ -57,13 +57,14 @@ agy-review --continue | --conversation <id> [--decided @f] [--focus "…"] #
57
57
  |---|---|---|
58
58
  | `AGY_MODEL` | `Gemini 3.1 Pro (High)` | frontier default; **any** model is allowed — a sub-frontier one earns a silenceable advisory (quality-first, not a gate) |
59
59
  | `AGY_PROBE` | `0` | `1` silences the off-frontier model advisory AND lets `code` run without `--facts` (an ungrounded probe never attests — its receipt is probe-marked) |
60
- | `AGY_REVIEW_ALLOW_ADDDIR` | `0` | `1` lets an oversized `code` review offload ONLY the change set to a private staging dir via `--add-dir` (the grounding stays inline; re-enables the Issue-001 stall risk prefer splitting into focused reviews) |
60
+ | `AGY_REVIEW_MAX_TOTAL_BYTES` | `240000` | the ceiling on the SUM of all outgoing prompt bytes an oversized `code` review's chunked feed may send; checked BEFORE the first turn is spent |
61
+ | `AGY_REVIEW_ALLOW_ADDDIR` | `0` | **RETIRED** — recognized so an existing settings line never warns as unknown, but it arms nothing. An oversized `code` review is a chunked feed with a per-part delivery proof; the `--add-dir` offload it armed could not be verified (headless `agy` auto-denies `read_file`) |
61
62
  | `AGY_HARD_TIMEOUT` | `30m` | the review's hard cap (longer default than a probe — reviews are slower) |
62
- | `AGY_MAX_PROMPT_BYTES` | `120000` | the same single-argv byte ceiling; oversized trim/split (or the `--add-dir` escape above) |
63
+ | `AGY_MAX_PROMPT_BYTES` | `120000` | the same single-argv byte ceiling; oversized `code` is DELIVERED as a chunked feed (see above), oversized `plan`/`diff` refuses with trim/split guidance |
63
64
 
64
65
  `agy-review` is **read-only** and **advisory**: it never edits, commits, or passes a stray `--`
65
66
  passthrough (it owns the posture). The service can still **stall on large/substantive prompts**
66
- (Issue-001) regardless of `--add-dir`, so keep reviews **focused**; the hard timeout is the guard.
67
+ (Issue-001), so keep reviews **focused**; the hard timeout is the guard.
67
68
 
68
69
  ## Models
69
70
 
@@ -32,8 +32,8 @@ GUARD Do NOT comment on AI model names/versions or your own knowledge cutoff
32
32
  {{FOCUS}} # from --focus "…" + any trailing focus words, merged in parse order (optional)
33
33
 
34
34
  ## The change set / plan / diff under review
35
- {{ARTIFACT}} # code: the assembled, repo-complete working-tree change set (or, when oversized
36
- # with AGY_REVIEW_ALLOW_ADDDIR=1, a private --add-dir staging file)
35
+ {{ARTIFACT}} # code: the assembled, repo-complete working-tree change set (when oversized it
36
+ # is not inlined at all see the chunked feed below)
37
37
  # plan/diff: the supplied file, inlined
38
38
 
39
39
  ## Output — Markdown, this exact shape, nothing else
@@ -47,6 +47,69 @@ Numbered. Simplifications, reuse, naming, missing tests. Cite file:line. Empty?
47
47
  Anything ambiguous that would change your verdict if answered.
48
48
  ```
49
49
 
50
+ ## Over-cap `code`: the change set is DELIVERED, and delivery is PROVEN
51
+
52
+ `agy` takes its prompt as ONE argv value, and headless `agy` **auto-denies its own `read_file` tool**
53
+ (probed twice, including for a file inside the working tree). So an over-cap change set can never be
54
+ **fetched** by the model. Past `AGY_MAX_PROMPT_BYTES` a `code` review is therefore **delivered**:
55
+ the assembled change set is cut into under-cap parts, fed over continuation turns, and reviewed in a
56
+ final turn. `plan` / `diff` keep refusing over the cap — their artifact is an operator-supplied file
57
+ the operator can split.
58
+
59
+ - **Envelope and body are formally separate.** Each fed turn = an ENVELOPE the wrapper authors
60
+ (framing, part index, the acknowledge-only instruction) plus a **pristine BODY**, a verbatim slice
61
+ of the change set. Only BODIES concatenate, and they concatenate **byte-for-byte**: nothing the
62
+ wrapper adds ever enters the reviewed artifact, and the receipt's fingerprint domain is untouched.
63
+ - **Delivery is proven, never assumed.** After assembly the wrapper picks, per part, an interior line
64
+ the model cannot anticipate, and asks for it **by address only**. The mandated shape gains
65
+ `### Delivery proof` as its **FIRST** section, so output truncation cannot silently drop it:
66
+
67
+ ```text
68
+ ### Delivery proof
69
+ part <K> line <L>: <the text of line L of part K, VERBATIM>
70
+ Requested addresses, one per line:
71
+ part 1 line 743
72
+ part 2 line 512
73
+ ### Verdict
74
+
75
+ ```
76
+
77
+ The addresses ride **one per line, each shorter than the minimum length a proof candidate may have**.
78
+ That is not formatting: it makes a collision **constructively impossible**. A candidate is a single
79
+ line of at least that minimum, and a single line can never match across a newline — so the request
80
+ itself can never reveal the very text it asks for, and the wrapper needs no second selection pass.
81
+
82
+ - **Any missing or non-matching echo is a FAILED review** — `exit 4`, **NO receipt**, and a message
83
+ naming the cause. Never a downgraded verdict, never a warning beside a kept receipt.
84
+ - **Cost is stated before it is spent** (D5): N parts cost N+1 subscription turns, announced on
85
+ stderr before the first dispatch. `AGY_REVIEW_MAX_TOTAL_BYTES` (default 240000) bounds the SUM of
86
+ all outgoing prompt bytes and refuses **before** turn 1 — an economy guard, not the correctness
87
+ guard.
88
+ - **The receipt self-declares delivery**: `inline` (the whole change set rode one prompt — proven by
89
+ construction) or `fed` (proven by echo). The kit's review-state gate requires the field present and
90
+ well-formed, never a particular value, so a receipt minted before this lane existed no longer
91
+ attests. The recovery is stated: re-run the review.
92
+
93
+ ### Honest residuals (recorded, not engineered away)
94
+
95
+ - A model that genuinely received every part but **mis-transcribes** one echo produces a FALSE
96
+ refusal and its analysis is lost. Placing the proof FIRST bounds the truncation case, and the
97
+ comparison tolerates surrounding whitespace only — never content. The rest is the accepted price of
98
+ failing closed: **re-run the review**, do not distrust the lane.
99
+ - A change set whose parts carry **no unique interior line** in the 24..200-byte window (a single
100
+ huge minified line, for instance) cannot be proven delivered, so the wrapper **refuses** rather
101
+ than reviewing unprovably. Split the review, or exclude the blob.
102
+ - The conversation id is parsed from `agy`'s own run log, whose format is `agy`'s to change. An
103
+ unparseable log **degrades loudly** to `--continue`; correctness still rests on the echo proof,
104
+ which fails closed when the wrong conversation answers.
105
+
106
+ ## agy's own permission ask — surfaced, never applied
107
+
108
+ When `agy` denies `read_file` it names the permission rule it wants. The kit **never writes it**:
109
+ granting it would widen a boundary for ALL `agy` use on the machine, and it would buy nothing this
110
+ design needs — the fed lane delivers content inline and reads no file. `--dangerously-skip-permissions`
111
+ is strictly worse (it auto-approves writes during a read-only review) and is not offered.
112
+
50
113
  ## Why no "read the repo's AGENTS.md" instruction
51
114
 
52
115
  Earlier versions told `agy` to *read the repo's root `AGENTS.md` (your cwd)*. That was the documented
@@ -2,7 +2,7 @@
2
2
  name: codex-cli-bridge
3
3
  description: Delegate work to the OpenAI Codex CLI (`codex`) under a ChatGPT subscription — run plan/instruction EXECUTION in a sandboxed workspace, or get a read-only ADVISORY review of a plan or working-tree diff — as a second delegated-execution backend beside Antigravity. Use when the user wants to hand a bounded coding task or plan to `codex exec`, get a second-opinion review from codex, install or authenticate Codex CLI, understand its sandbox/network/approval policy, drive codex efficiently from the main agent (exec vs review, resume, the commit boundary), bridge project context (`AGENTS.md`) into codex, or troubleshoot codex flags, models, auth, or its no-TTY headless behaviour.
4
4
  metadata:
5
- version: '3.1.0'
5
+ version: '3.2.0'
6
6
  ---
7
7
 
8
8
  # codex-cli-bridge
@@ -121,7 +121,7 @@ aw_settings_file() {
121
121
  printf '%s/agent-workflow/bridge-settings.conf' "${XDG_CONFIG_HOME:-$HOME/.config}"
122
122
  }
123
123
  aw_settings_known() {
124
- case " CODEX_SERVICE_TIER CODEX_HARD_TIMEOUT CODEX_REVIEW_MAX_TOTAL_BYTES AGY_HARD_TIMEOUT AGY_REVIEW_ALLOW_ADDDIR " in
124
+ case " CODEX_SERVICE_TIER CODEX_HARD_TIMEOUT CODEX_REVIEW_MAX_TOTAL_BYTES AGY_HARD_TIMEOUT AGY_REVIEW_ALLOW_ADDDIR AGY_REVIEW_MAX_TOTAL_BYTES " in
125
125
  *" $1 "*) return 0 ;;
126
126
  *) return 1 ;;
127
127
  esac
@@ -144,6 +144,7 @@ aw_settings_valid() {
144
144
  CODEX_REVIEW_MAX_TOTAL_BYTES) [[ "$v" =~ $int_re ]] && aw_int_in_range "$v" 1 100000000 ;;
145
145
  AGY_HARD_TIMEOUT) [[ "$v" =~ $dur_re && ! "$v" =~ $zero_re ]] ;;
146
146
  AGY_REVIEW_ALLOW_ADDDIR) [[ "$v" == "0" || "$v" == "1" ]] ;;
147
+ AGY_REVIEW_MAX_TOTAL_BYTES) [[ "$v" =~ $int_re ]] && aw_int_in_range "$v" 1 100000000 ;;
147
148
  *) return 1 ;;
148
149
  esac
149
150
  }
@@ -124,7 +124,7 @@ aw_settings_file() {
124
124
  printf '%s/agent-workflow/bridge-settings.conf' "${XDG_CONFIG_HOME:-$HOME/.config}"
125
125
  }
126
126
  aw_settings_known() {
127
- case " CODEX_SERVICE_TIER CODEX_HARD_TIMEOUT CODEX_REVIEW_MAX_TOTAL_BYTES AGY_HARD_TIMEOUT AGY_REVIEW_ALLOW_ADDDIR " in
127
+ case " CODEX_SERVICE_TIER CODEX_HARD_TIMEOUT CODEX_REVIEW_MAX_TOTAL_BYTES AGY_HARD_TIMEOUT AGY_REVIEW_ALLOW_ADDDIR AGY_REVIEW_MAX_TOTAL_BYTES " in
128
128
  *" $1 "*) return 0 ;;
129
129
  *) return 1 ;;
130
130
  esac
@@ -147,6 +147,7 @@ aw_settings_valid() {
147
147
  CODEX_REVIEW_MAX_TOTAL_BYTES) [[ "$v" =~ $int_re ]] && aw_int_in_range "$v" 1 100000000 ;;
148
148
  AGY_HARD_TIMEOUT) [[ "$v" =~ $dur_re && ! "$v" =~ $zero_re ]] ;;
149
149
  AGY_REVIEW_ALLOW_ADDDIR) [[ "$v" == "0" || "$v" == "1" ]] ;;
150
+ AGY_REVIEW_MAX_TOTAL_BYTES) [[ "$v" =~ $int_re ]] && aw_int_in_range "$v" 1 100000000 ;;
150
151
  *) return 1 ;;
151
152
  esac
152
153
  }
@@ -265,7 +266,7 @@ DEFAULT_CODEX_EFFORT="xhigh"
265
266
  # Review-receipt identity (AD-038). AW_BRIDGE_VERSION mirrors this bridge's SKILL.md/capability.json
266
267
  # version (drift-guarded by codex-review.test.mjs against capability.json).
267
268
  AW_RECEIPT_BACKEND="codex"
268
- AW_BRIDGE_VERSION="3.1.0"
269
+ AW_BRIDGE_VERSION="3.2.0"
269
270
  CODEX_MODEL="${CODEX_MODEL:-$DEFAULT_CODEX_MODEL}"
270
271
  CODEX_EFFORT="${CODEX_EFFORT:-$DEFAULT_CODEX_EFFORT}"
271
272
  # Generous hard cap for a slow xhigh review (subscription latency varies).
@@ -273,6 +274,12 @@ CODEX_HARD_TIMEOUT="${CODEX_HARD_TIMEOUT:-1800}"
273
274
  # Above this assembled-payload size (bytes), the diff goes via a git-dir-local temp
274
275
  # file instead of inline — never truncated.
275
276
  CODEX_REVIEW_MAX_TOTAL_BYTES="${CODEX_REVIEW_MAX_TOTAL_BYTES:-1500000}"
277
+ # The repo file map's share of the assembled payload (see emit_repo_file_map). A PINNED constant,
278
+ # not a knob: a plain assignment, so an inherited environment value can never move it. codex has no
279
+ # single-argv ceiling (the payload rides stdin, and past CODEX_REVIEW_MAX_TOTAL_BYTES a git-dir temp
280
+ # file), so the budget is pinned at the DEFAULT inline-payload cap — the map can never outgrow the
281
+ # whole payload, and every realistic repo's map assembles byte-unchanged.
282
+ AW_REVIEW_MAP_BUDGET_BYTES=1500000
276
283
  # Codex service tier (quality-neutral speed knob; live-probed 2026-07-05): default EMPTY ⇒ no
277
284
  # service_tier flag (standard tier) — enabling Fast is a consented per-host SPEND act, never a
278
285
  # silent default. The only server-catalog tier id on this subscription is 'priority' (catalog
@@ -544,7 +551,7 @@ posture_json() {
544
551
  # itself instead of inferring it from this wrapper's version (which bumps in a different release
545
552
  # phase). Silence is not a declaration — an unmarked receipt is untrustworthy and the gate rejects it.
546
553
  write_review_receipt() {
547
- local artifact="$1" fresh="$2" fingerprint="$3" verdict="$4" grounded="$5" facts_hash="$6" probe="${7:-false}"
554
+ local artifact="$1" fresh="$2" fingerprint="$3" verdict="$4" grounded="$5" facts_hash="$6" probe="${7:-false}" delivery="${8:-}"
548
555
  local receipts="${AW_REVIEW_RECEIPTS:-}"
549
556
  if [[ -z "$receipts" ]]; then
550
557
  local receipt_git_dir
@@ -554,27 +561,70 @@ write_review_receipt() {
554
561
  fi
555
562
  receipts="$receipt_git_dir/agent-workflow-review-receipts.jsonl"
556
563
  fi
557
- local line probe_field=',"probe":false'
564
+ local line probe_field=',"probe":false' delivery_field=""
558
565
  if [[ "$probe" == "true" ]]; then probe_field=',"probe":true'; fi
559
- line="$(printf '{"schema":1,"artifact":%s,"fresh":%s,"fingerprint":%s,"backend":"%s","verdict":"%s","grounded":%s,"factsHash":%s,"wrapperVersion":"%s","timestamp":"%s"%s,"posture":%s}' \
566
+ if [[ -n "$delivery" ]]; then delivery_field=",\"delivery\":\"$delivery\""; fi
567
+ line="$(printf '{"schema":1,"artifact":%s,"fresh":%s,"fingerprint":%s,"backend":"%s","verdict":"%s","grounded":%s,"factsHash":%s,"wrapperVersion":"%s","timestamp":"%s"%s,"posture":%s%s}' \
560
568
  "$(receipt_json_scalar "$artifact")" "$fresh" "$(receipt_json_scalar "$fingerprint")" \
561
569
  "$AW_RECEIPT_BACKEND" "$verdict" "$grounded" "$(receipt_json_scalar "$facts_hash")" \
562
- "$AW_BRIDGE_VERSION" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$probe_field" "$(posture_json)")"
570
+ "$AW_BRIDGE_VERSION" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$probe_field" "$(posture_json)" "$delivery_field")"
563
571
  if ! printf '%s\n' "$line" >>"$receipts" 2>/dev/null; then
564
572
  echo "warning: could not append the review receipt to $receipts — the review itself succeeded;" >&2
565
573
  echo " the review-state gate will read the current tree as un-receipted." >&2
566
574
  fi
567
575
  }
568
576
 
569
- # Emit the full review surface to stdout: repo map, status (never-committable untracked records
570
- # filtered), staged + unstaged diffs, and the CONTENTS of every untracked REGULAR file (NUL-safe
571
- # iteration over the SAME filtered walk as the fingerprint the payload is byte-identical with
572
- # and without a device mask). Symlinks are shown as their target (never followed — no out-of-repo
573
- # leak); directories/vanished paths are noted, never read (a `cat` on a FIFO would hang BEFORE the
574
- # hard timeout applies that class never reaches this loop).
577
+ # The repo file map is a FIXED cost that scales with REPO SIZE, not change size (measured 28,735
578
+ # bytes in the home repo 24% of agy's 120000-byte single-argv ceiling), so an unbounded map taxes
579
+ # the change budget of every review. AW_REVIEW_MAP_BUDGET_BYTES bounds it; the value is set by each
580
+ # wrapper so THIS BODY stays byte-identical across both (review-fingerprint-parity.test.mjs
581
+ # lockstep). Unset/0 = unbounded. Past the budget the map degrades to the CHANGED-path subset cut
582
+ # at the SAME budget, so a change touching very many long paths cannot re-breach the bound — plus a
583
+ # stated omitted count; a truncation-with-count, never a silent cut. The map was never part of the
584
+ # fingerprint domain (emit_fingerprint_payload does not contain it), so bounding it moves no receipt.
585
+ emit_repo_file_map() {
586
+ local budget="${AW_REVIEW_MAP_BUDGET_BYTES:-0}" total shown omitted subset tracked
587
+ # ONE deduplicated tracked-path SNAPSHOT, captured once and reused for all four uses — the
588
+ # byte-budget predicate, the printed map, `total`, and the index intersection. Re-running the query
589
+ # per use let a concurrent index change make the budget decision, the map and the counts describe
590
+ # DIFFERENT snapshots. Dedupe: an UNMERGED index lists a path once per stage, so a predicate
591
+ # counting duplicates against a map printing unique paths would push a map that fits into the
592
+ # truncated arm. `ls-files` output is sorted by path, so one path's stages are adjacent and `uniq`
593
+ # is exact — no git version floor needed.
594
+ tracked="$(git ls-files | LC_ALL=C uniq)"
595
+ if (( budget <= 0 )) || (( $(printf '%s\n' "$tracked" | wc -c) <= budget )); then
596
+ if [[ -n "$tracked" ]]; then printf '%s\n' "$tracked"; fi
597
+ return 0
598
+ fi
599
+ total=$(( $(printf '%s\n' "$tracked" | wc -l) ))
600
+ # The subset is INTERSECTED with the index before budgeting, so `shown` and `total` live in ONE
601
+ # domain and `shown + omitted == total` holds exactly: a STAGED DELETION is a changed path that is
602
+ # no longer in `git ls-files`, and counting it as shown would make the stated arithmetic a lie.
603
+ # (The NR==FNR reader is safe here: an empty index returns through the in-budget arm above.)
604
+ # awk never `exit`s early: a closed pipe would SIGPIPE `sort` and pipefail would abort the run.
605
+ subset="$(LC_ALL=C awk 'NR == FNR { known[$0] = 1; next } ($0 in known)' <(printf '%s\n' "$tracked") <(git diff --name-only --no-ext-diff; git diff --cached --name-only --no-ext-diff) |
606
+ LC_ALL=C sort -u |
607
+ LC_ALL=C awk -v cap="$budget" '{ n = length($0) + 1; if (!over && used + n <= cap) { used += n; print } else over = 1 }')"
608
+ shown=0
609
+ if [[ -n "$subset" ]]; then
610
+ printf '%s\n' "$subset"
611
+ shown=$(( $(printf '%s\n' "$subset" | wc -l) ))
612
+ fi
613
+ omitted=$(( total - shown ))
614
+ if (( omitted < 0 )); then omitted=0; fi
615
+ printf '=== repo file map TRUNCATED to the changed-path subset: %s of %s tracked paths shown, %s omitted (map budget %s bytes) ===\n' \
616
+ "$shown" "$total" "$omitted" "$budget"
617
+ }
618
+
619
+ # Emit the full review surface to stdout: repo map (bounded, see emit_repo_file_map), status
620
+ # (never-committable untracked records filtered), staged + unstaged diffs, and the CONTENTS of every
621
+ # untracked REGULAR file (NUL-safe iteration over the SAME filtered walk as the fingerprint — the
622
+ # payload is byte-identical with and without a device mask). Symlinks are shown as their target
623
+ # (never followed — no out-of-repo leak); directories/vanished paths are noted, never read (a `cat`
624
+ # on a FIFO would hang BEFORE the hard timeout applies — that class never reaches this loop).
575
625
  assemble_code_diff() {
576
626
  echo "=== repo file map (git ls-files) ==="
577
- git ls-files
627
+ emit_repo_file_map
578
628
  echo
579
629
  echo "=== git status (porcelain) ==="
580
630
  emit_status_porcelain_filtered
@@ -765,6 +765,44 @@ describe('codex-review.sh — source-level reverse guard (parser arms ⟷ manife
765
765
  });
766
766
  });
767
767
 
768
+ // ── the repo file map budget (Phase 2) ────────────────────────────────────────────
769
+ // The shared emit_repo_file_map bounds the map in BOTH wrappers (byte-identical, kit parity test).
770
+ // codex has no single-argv ceiling — its payload rides stdin, and past CODEX_REVIEW_MAX_TOTAL_BYTES
771
+ // a git-dir temp file — so its budget is set at the default inline cap: the map can never outgrow
772
+ // the whole payload, and every realistic repo's map assembles byte-UNCHANGED. Behaviour alone
773
+ // cannot tell "a huge budget" from "no budget at all", so the pinned value is asserted at source.
774
+ describe('codex-review.sh — repo file map budget (Phase 2)', () => {
775
+ const MAP_DIR = 'deeply/nested/fixture/directory/for/the/repo/file/map/budget';
776
+
777
+ it("codex-review's assembled payload is byte-unchanged by the map budget", () => {
778
+ const sb = makeSandbox();
779
+ const g = (...a) => spawnSync('git', a, { cwd: sb.repo, encoding: 'utf8' });
780
+ mkdirSync(join(sb.repo, MAP_DIR), { recursive: true });
781
+ for (let i = 0; i < 100; i += 1) {
782
+ writeFileSync(join(sb.repo, `${MAP_DIR}/aa-untouched-file-${String(i).padStart(3, '0')}.txt`), `untouched ${i}\n`);
783
+ writeFileSync(join(sb.repo, `${MAP_DIR}/zz-modified-file-${String(i).padStart(3, '0')}.txt`), `body ${i} v1\n`);
784
+ }
785
+ g('add', '-A');
786
+ g('commit', '-qm', 'map fixture');
787
+ for (let i = 0; i < 100; i += 1) writeFileSync(join(sb.repo, `${MAP_DIR}/zz-modified-file-${String(i).padStart(3, '0')}.txt`), `body ${i} v2 — changed\n`);
788
+ const r = run(sb, { args: ['code'] });
789
+ rmSync(sb.root, { recursive: true, force: true });
790
+ assert.equal(r.status, 0, r.stderr);
791
+ assert.doesNotMatch(r.capStdin, /TRUNCATED/, 'a 17 KB map is far inside the budget — no degradation');
792
+ assert.ok(r.capStdin.includes(`${MAP_DIR}/aa-untouched-file-000.txt`), 'the whole map is still listed');
793
+ assert.ok(r.capStdin.includes(`${MAP_DIR}/aa-untouched-file-099.txt`), 'including its last entry');
794
+ });
795
+
796
+ it('the wrapper pins a map budget no smaller than its default inline-payload cap', () => {
797
+ const source = readFileSync(WRAPPER, 'utf8');
798
+ const pinned = source.match(/^AW_REVIEW_MAP_BUDGET_BYTES=(\d+)$/m);
799
+ assert.ok(pinned, 'codex-review.sh must PIN AW_REVIEW_MAP_BUDGET_BYTES (a plain assignment, never an env knob)');
800
+ const inlineCap = source.match(/^CODEX_REVIEW_MAX_TOTAL_BYTES="\$\{CODEX_REVIEW_MAX_TOTAL_BYTES:-(\d+)\}"$/m);
801
+ assert.ok(inlineCap, 'the default inline-payload cap is readable at source');
802
+ assert.ok(Number(pinned[1]) >= Number(inlineCap[1]), `map budget ${pinned[1]} must not be smaller than the inline cap ${inlineCap[1]}`);
803
+ });
804
+ });
805
+
768
806
  // ── mode catalog ⟷ wrapper reality (BRIDGE-MODES-CATALOG) ─────────────────────────
769
807
  // The kit validator owns the catalog's INTERNAL shape; these arms pin what only the wrapper source
770
808
  // can settle — the catalog documents THIS wrapper's real modes and real escape hatches, and every
@@ -3,7 +3,7 @@
3
3
  "schema": 1,
4
4
  "name": "codex-cli-bridge",
5
5
  "kind": "execution-backend",
6
- "version": "3.1.0",
6
+ "version": "3.2.0",
7
7
  "posture": { "model": "gpt-5.6-sol", "effort": "xhigh", "tier": null },
8
8
  "provides": ["execute", "review"],
9
9
  "roles": {
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": "3.14.0",
6
+ "version": "4.0.0",
7
7
  "provides": [],
8
8
  "roles": {},
9
9
  "detect": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sabaiway/agent-workflow-kit",
3
- "version": "3.14.0",
3
+ "version": "4.0.0",
4
4
  "description": "Portable, cross-agent memory & workflow for AI coding agents — Claude Code, Codex, Cursor, Devin Desktop. One command deploys an AGENTS.md entry point + docs/ai context with cap/archive/index enforcement into any repo.",
5
5
  "keywords": [
6
6
  "ai-agents",
@@ -0,0 +1,39 @@
1
+ ---
2
+ name: review-lens
3
+ description: Read-only ADDITIONAL review lens — an extra independent opinion on code the orchestrator already has, when the configured review backends have run and you want another angle. Grants no shell, so it can never turn a review into a wave of approval prompts. Never for writing code, running gates, or replacing the configured review recipe.
4
+ model: sonnet
5
+ effort: high
6
+ tools: Read, Grep, Glob
7
+ ---
8
+
9
+ You are an ADDITIONAL, INDEPENDENT review lens. Something has already been reviewed by the project's
10
+ configured backends; your job is to find what they MISSED, not to restate what they found.
11
+
12
+ You have `Read`, `Grep` and `Glob` and **no `Bash`**. That is deliberate and it is the whole point of
13
+ this vehicle: a read-only fan-out that can reach for a shell turns one review into a wave of approval
14
+ prompts for the maintainer, so this lens structurally cannot.
15
+ If a harness omits `Grep`/`Glob`, fall back to the `Read` tool (whole-file reads) — never a
16
+ shelled-out command. Should a harness
17
+ nonetheless route your reads through `Bash`, keep each one a **plain single read-only command**
18
+ (`grep …`, `ls …`, `cat …`) — never a `;`/`&&`/`|` chain, never `node -e`; where the maintainer
19
+ enabled the opt-in **read-lane** (`docs/ai/lanes.json`), the gate hook keeps those seeded-read-only
20
+ Bash reads promptless (subagent Bash included, where the host fires hooks on subagent Bash).
21
+
22
+ How to review:
23
+
24
+ - **Read the code before judging it.** Every finding cites `file:line` and names the concrete input
25
+ or state that triggers it. A finding you cannot anchor in the code does not go in the output.
26
+ - **Say what BREAKS, not what could be nicer.** For each finding: the defect in one sentence, then
27
+ the failure scenario — the input, the resulting wrong behaviour. No style preferences, no
28
+ restatement of design intent back to the orchestrator.
29
+ - **Respect what is already decided.** The prompt will name findings already folded and decisions
30
+ already locked. Re-raising them is churn; checking whether the FIX is correct is real work.
31
+ - **Accuracy over volume.** A wrong finding costs the orchestrator more than a missed one — it
32
+ spends a verification cycle and erodes trust in the whole list. If an angle turns up nothing, say
33
+ so in one line and move on. Never pad.
34
+ - **You are advisory.** You never edit files, never run gates, never propose a commit. The
35
+ orchestrator verifies every finding and owns every change.
36
+
37
+ Output: a numbered list of findings, most severe first, each as
38
+ `[severity] — file:line — the defect — the failure scenario — the fix direction`.
39
+ Then one line: `no further findings` or the angles you deliberately did not cover.
@@ -42,6 +42,21 @@
42
42
  // (d) everything else → NO decision: exit 0, no output — the normal permission flow proceeds
43
43
  // unchanged. The hook NEVER emits `deny`.
44
44
  //
45
+ // WHY THERE IS NO DENY RUNG (kit 4.0.0, three council rounds, AD-078). One was built and REMOVED
46
+ // before release. It refused only a seeded read-only command that provably DISCARDS its output
47
+ // (`2>/dev/null`), on the argument that such a refusal cannot destroy anything the caller wanted.
48
+ // The argument was sound; the byte-level PROOF of "this command discards" was not, and could not be
49
+ // made so here. Five constructs defeated it in three rounds — `1<&2` (an fd dup routes stdout back
50
+ // out of /dev/null AFTER the approved `>`), a quoted literal `>/dev/null ` in an argument, a
51
+ // leading-token-only segment match (`… && npm test`), a bare `&` (backgrounds the read, runs the
52
+ // rest), and a `#` comment (bash never executes the redirect at all). Each was a FALSE REFUSAL.
53
+ // The lesson, which any future deny rung must start from: on an ASK rung an incomplete scan merely
54
+ // over-asks, which is safe; on a DENY rung the SAME incompleteness refuses real work. Deciding
55
+ // whether a `>` is an operator or text requires lexing the shell, which this dependency-free hook
56
+ // deliberately does not do. A deny rung therefore needs a justification that does not rest on
57
+ // parsing command bytes. Design record + all five counterexamples: docs/plans/queue.md,
58
+ // BARE-LANE-DENY-RUNG.
59
+ //
45
60
  // Fail-safe invariant, decoupled per function: a DECLARATION anomaly (missing / unreadable /
46
61
  // malformed / schema-invalid gates.json) disables ONLY exact-gate approval (a) — the residual
47
62
  // guard (b) needs no declaration and keeps running (a broken gates.json must not silently
@@ -1,6 +1,15 @@
1
1
  ### Mode: agents
2
2
 
3
- The opt-in **cheap-lane subagent writer** — the family's second `.claude/` writer, on the velocity discipline. It places the bundled cheap-lane subagent definitions (`references/agents/*.md`) into the project's `.claude/agents/` so mechanical work — extraction sweeps, changelog fact-skeletons, gate-failure triage — runs on a **cheap model** (`model: haiku`, `effort: low`, bounded read-only tools) instead of the frontier main lane. **Claude-Code-specific** (like velocity): other agent hosts ignore `.claude/agents/`. Judgment, review, real code, and user-facing copy never move to these vehicles — they are extraction/drafting only, and the orchestrator verifies their output.
3
+ <!-- opt-in-capability: agents -->
4
+
5
+ The opt-in **read-only subagent writer** — the family's second `.claude/` writer, on the velocity discipline. It places the bundled subagent definitions (`references/agents/*.md`) into the project's `.claude/agents/`. **Claude-Code-specific** (like velocity): other agent hosts ignore `.claude/agents/`.
6
+
7
+ **Every vehicle grants READ-ONLY tools and NO `Bash`** — that is the load-bearing property, not a detail. A read-only fan-out on a full-tool vehicle shells out for facts it could have read, and each shelled command is an approval prompt the maintainer never needed to see; a vehicle with no shell structurally cannot do that. Two lanes ride on it:
8
+
9
+ - **cheap lane** (`model: haiku`, `effort: low`) — `mechanical-sweep`, `changelog-skeleton`, `gate-triage`: extraction sweeps, changelog fact-skeletons, gate-failure triage. Extraction/drafting only; the orchestrator applies judgment and verifies the output.
10
+ - **review lens** (`review-lens`, review-capable model) — an ADDITIONAL independent read-only opinion on code the configured review backends have already seen. It exists because a third lens otherwise has **no vehicle at all**: the cheap vehicles are scoped away from judgment, and a review-capable full-tool subagent is the prompt-flood shape. It never replaces the configured review recipe, and it is advisory like every other review.
11
+
12
+ Writing code, running gates, and user-facing copy never move to these vehicles.
4
13
 
5
14
  Run `node ${CLAUDE_SKILL_DIR}/tools/cheap-agents.mjs [--dry-run | --apply] [--cwd <dir>]`:
6
15
 
@@ -8,4 +17,4 @@ Run `node ${CLAUDE_SKILL_DIR}/tools/cheap-agents.mjs [--dry-run | --apply] [--cw
8
17
  2. **Only on an explicit yes**, re-run with `--apply`. It writes **only** under `.claude/agents/` — never `settings.json` / `settings.local.json`, never a commit. `--apply` is deployment-gated (the stamp must be at the lineage head) and symlink-safe (a symlinked `.claude` / `.claude/agents` / target file is a STOP).
9
18
  3. **Hidden-mode deployments:** after apply, run the hide-footprint reconcile (`node ${CLAUDE_SKILL_DIR}/tools/hide-footprint.mjs --dir <project> --reconcile`) so the placed files stay invisible to `git status` — `/.claude/agents/` is in the known-footprint registry; the apply report reminds you.
10
19
 
11
- **Invariants:** writer (writes only `.claude/agents/`) · preview by default · a diverged existing file is reported and preserved, never clobbered · never touches settings · never commits · vehicles are pinned to `model: haiku` + `effort: low` + read-only tools (content-tested).
20
+ **Invariants:** writer (writes only `.claude/agents/`) · preview by default · a diverged existing file is reported and preserved, never clobbered · never touches settings · never commits · **no vehicle grants `Bash`** · the cheap-lane vehicles are pinned to `model: haiku` + `effort: low`, and the review lens is pinned OFF the cheap model (all content-tested).
@@ -1,5 +1,7 @@
1
1
  ### Mode: autonomy-doctor
2
2
 
3
+ <!-- opt-in-capability: sandbox-provision -->
4
+
3
5
  The **sandbox provisioner "doctor"** — the answer to *"can this machine run the Claude sandbox, and can you fix it?"* (AD-044: macOS Seatbelt built-in / Linux+WSL2 `bwrap`+`socat` / native Windows → WSL2). **Division of labor:** YOU narrate the diagnosis and relay the consent question; the KIT does the deterministic detect → consent-gated install → verify. It is **guarded**: the privileged lane runs ONLY with the per-run consent tuple, always faces the harness permission prompt (the mode sits outside every velocity auto-approve tier), **never auto-runs, never writes repo files, and never commits**.
4
6
 
5
7
  Run **`node ${CLAUDE_SKILL_DIR}/tools/autonomy-doctor.mjs [--verify | --apply <pm>:<pkg[,pkg...]>]`**:
@@ -1,5 +1,7 @@
1
1
  ### Mode: backends
2
2
 
3
+ <!-- opt-in-capability: none — an inspection surface that configures nothing -->
4
+
3
5
  Read-only. Answers *"which optional execution-backends are set up vs missing, and what's the next step?"* — for the family's subscription-CLI bridges (`codex-cli-bridge` → `codex`, `antigravity-cli-bridge` → `agy`). It **never writes, never commits, and never runs a subscription CLI**.
4
6
 
5
7
  1. Run `node ${CLAUDE_SKILL_DIR}/tools/detect-backends.mjs` and present its table verbatim. Each row reports two **decoupled** axes: `manifestState` (health of the bridge *skill* — `not-installed | unsupported-schema | invalid-manifest | foreign | stub | ok`) and the readiness signals `cli` / `credentials` / `wrappers`, probed independently — so a CLI that is installed and signed in but whose bridge *skill* is absent reads `needs-skill`, not "missing".
@@ -1,5 +1,7 @@
1
1
  ### Mode: bootstrap
2
2
 
3
+ <!-- opt-in-capability: none — the deployment action itself; the advisor only renders inside an existing deployment -->
4
+
3
5
  Requires: ${CLAUDE_SKILL_DIR}/references/shared/report-footer.md · ${CLAUDE_SKILL_DIR}/references/shared/composition-handoff.md · ${CLAUDE_SKILL_DIR}/references/shared/deploy-tail.md · ${CLAUDE_SKILL_DIR}/references/shared/command-shapes.md
4
6
 
5
7
  > Bundled sources below (templates, scripts) live in **this skill's own directory** — `${CLAUDE_SKILL_DIR}/` in Claude Code, or the folder containing this `SKILL.md` in Codex / other agents. Use that as the copy/read source; the working directory is the **target project**, not the skill.