@sabaiway/agent-workflow-kit 14.22.0 → 14.23.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 (28) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/SKILL.md +1 -1
  3. package/bridges/antigravity-cli-bridge/SKILL.md +16 -10
  4. package/bridges/antigravity-cli-bridge/bin/agy-jev.test.mjs +105 -0
  5. package/bridges/antigravity-cli-bridge/bin/agy-review.sh +20 -6
  6. package/bridges/antigravity-cli-bridge/bin/agy.sh +26 -11
  7. package/bridges/antigravity-cli-bridge/capability.json +3 -3
  8. package/bridges/antigravity-cli-bridge/references/models-and-flags.md +4 -3
  9. package/bridges/antigravity-cli-bridge/setup/README.md +2 -1
  10. package/bridges/codex-cli-bridge/SKILL.md +44 -24
  11. package/bridges/codex-cli-bridge/bin/codex-exec-jev.test.mjs +385 -0
  12. package/bridges/codex-cli-bridge/bin/codex-exec.sh +262 -42
  13. package/bridges/codex-cli-bridge/bin/codex-exec.test.mjs +16 -18
  14. package/bridges/codex-cli-bridge/bin/codex-model-posture.test.mjs +16 -7
  15. package/bridges/codex-cli-bridge/bin/codex-review-honesty.test.mjs +1 -0
  16. package/bridges/codex-cli-bridge/bin/codex-review-jev.test.mjs +301 -0
  17. package/bridges/codex-cli-bridge/bin/codex-review.sh +240 -26
  18. package/bridges/codex-cli-bridge/bin/codex-review.test.mjs +9 -9
  19. package/bridges/codex-cli-bridge/capability.json +10 -10
  20. package/bridges/codex-cli-bridge/references/driving-codex.md +18 -14
  21. package/bridges/codex-cli-bridge/references/sandbox-and-flags.md +61 -22
  22. package/bridges/codex-cli-bridge/setup/README.md +8 -5
  23. package/capability.json +1 -1
  24. package/package.json +1 -1
  25. package/references/modes/recommendations.md +1 -1
  26. package/references/modes/velocity.md +1 -1
  27. package/tools/detect-backends.mjs +3 -3
  28. package/tools/manifest/schema.md +2 -2
@@ -9,8 +9,9 @@ wrapper commands are `codex-exec` and `codex-review`, backed by `bin/codex-exec.
9
9
 
10
10
  | Level | Can write? | Network? | Wrapper that uses it |
11
11
  |---|---|---|---|
12
- | `read-only` | no | no | `codex-review` (codex only reads + emits findings) |
13
- | `workspace-write` | repo (cwd) only | OFF (we force it off) | `codex-exec` (codex edits the repo) |
12
+ | permissions profile `jev` | exec: the repository, `/tmp` and `$TMPDIR`; review: nothing | `api.typesafe.ai` and `docs.typesafe.ai` only | both wrappers on Linux with codex-cli 0.160.0 or newer (conditions below) |
13
+ | `read-only` | no | no | `codex-review`, the fallback (codex only reads + emits findings) |
14
+ | `workspace-write` | repository, `/tmp` and `$TMPDIR` | off (forced off) | `codex-exec`, the fallback (codex edits the repo) |
14
15
  | `danger-full-access` | anywhere | yes | never used by this skill |
15
16
 
16
17
  **A sandbox bounds WRITES, not READS.** Even `read-only` can read any file on disk (it is what kept a
@@ -20,7 +21,7 @@ a **prompt + env** concern, not a sandbox one — see the review read-fence belo
20
21
 
21
22
  ## Flags the wrappers always pass
22
23
 
23
- `codex-exec`:
24
+ `codex-exec`, on today's flags:
24
25
 
25
26
  ```bash
26
27
  codex exec --ignore-user-config \
@@ -32,10 +33,23 @@ codex exec --ignore-user-config \
32
33
  --color never -o "$out" --json -m "$CODEX_MODEL" -
33
34
  ```
34
35
 
35
- `codex-review` is the same minus the write/network posture, plus `--sandbox read-only` and (optionally)
36
- `--output-schema`. In v0.142.3 `read-only` also grants **no network**, so `codex-review` relies on that
37
- and passes no separate network flag — the `sandbox_workspace_write.*` config (including
38
- `network_access`) applies **only** to `workspace-write`.
36
+ On Linux with codex-cli 0.160.0 or newer, from a repository outside every temp root and off a codex
37
+ version and profile digest recorded as refused, the first two
38
+ posture flags (`--sandbox workspace-write` and the `network_access=false` override) give way to the
39
+ permissions profile `jev` — four overrides, never beside `--sandbox` or `sandbox_mode` (codex refuses
40
+ both together):
41
+
42
+ ```bash
43
+ -c default_permissions=jev -c permissions.jev.network.enabled=true \
44
+ -c 'permissions.jev.network.domains={"api.typesafe.ai"="allow","docs.typesafe.ai"="allow"}' \
45
+ -c 'permissions.jev.filesystem={":root"="read",":tmpdir"="write",":slash_tmp"="write",":workspace_roots"={"."="write"}}'
46
+ ```
47
+
48
+ `codex-review` is the same minus the write/network posture, plus (optionally) `--output-schema`: on
49
+ today's flags `--sandbox read-only`, which in v0.142.3 also grants **no network** (the
50
+ `sandbox_workspace_write.*` config, `network_access` included, applies **only** to `workspace-write`);
51
+ under the profile `jev` the same four overrides with `permissions.jev.filesystem={":root"="read"}` —
52
+ no write entry at all, so a review still writes nothing. The banner's `jev=` field names the posture.
39
53
 
40
54
  ### Clean output capture
41
55
 
@@ -90,16 +104,30 @@ Note that `--skip-git-repo-check` (tier 2) only relaxes **codex's own** git-repo
90
104
  preflight still **requires a git work tree and a root `AGENTS.md`** and STOPs first if either is
91
105
  missing — passing it does not let `codex-exec` run outside a repo.
92
106
 
93
- ## Network-OFF invariant (exec)
94
-
95
- `codex-exec` keeps network access OFF on purpose: **new dependencies and any network step are installed
96
- by a human**, not by codex. If a task needs a new package, codex must STOP and report it; the
97
- orchestrator installs it, then re-dispatches.
107
+ ## Network: two hosts at most
108
+
109
+ A run reaches `api.typesafe.ai` and `docs.typesafe.ai` under the permissions profile `jev` and no other
110
+ host; on today's flags `codex-exec` keeps the network off. Either way **new dependencies and any other
111
+ network step are installed by a human**, not by codex: if a task needs a new package, codex must STOP
112
+ and report it; the orchestrator installs it, then re-dispatches. The profile runs on Linux with
113
+ codex-cli 0.160.0 or newer, off a codex version and profile digest recorded as refused and, for
114
+ `codex-exec`, from a repository whose root and git common dir sit outside `/tmp` and `$TMPDIR` (there
115
+ the profile would leave `.git` writable); every other run keeps today's flags and its banner says
116
+ `jev=unreachable (<reason>)`.
117
+
118
+ **The loud failure.** A run whose codex refuses the profile — a "Permissions profile `jev` …" or
119
+ "Configured filesystem path … is not recognized …" error item before the turn starts, or `bwrap: execvp`
120
+ in a command item whose failure is proven — exits **72** with the matched message and no success
121
+ receipt (a nonced exec run records `transport-failure`; a review writes no receipt). The error-item case
122
+ appends `<codex version> <sha256 of the profile overrides>` to
123
+ `${XDG_STATE_HOME:-~/.local/state}/agent-workflow/codex-jev-profile-refused`, so the next run on that
124
+ version and digest takes today's flags before any spend; if that file cannot be written the wrapper
125
+ says so on stderr and the next run tries the profile again.
98
126
 
99
127
  ## Escalation & approvals
100
128
 
101
129
  There is **no TTY** in `codex exec`, so `approval_policy=never`: codex never pauses for an interactive
102
- approval. Any action that would need escalation (network, writes outside the repo, an ambiguous
130
+ approval. Any action that would need escalation (network beyond the run's posture, writes outside the repo, an ambiguous
103
131
  decision) is **refused and reported**, and the orchestrator handles it by hand.
104
132
 
105
133
  ## Commit prohibition — enforced by a git-write shim
@@ -142,10 +170,13 @@ non-conforming text); a raw-text retry covers a rare validation/run failure. Def
142
170
  ### Review read-fence (best-effort env hygiene)
143
171
 
144
172
  `codex-review` runs under a throwaway `HOME` + `XDG_CONFIG_HOME`/`XDG_CACHE_HOME`/`XDG_DATA_HOME` with an
145
- **absolute `CODEX_HOME`** so auth + history still resolve while codex's default config/cache/skill-scan
146
- roots point at an empty dir (trims the roaming + skill-scan noise). This is **env hygiene, NOT a security
173
+ **absolute `CODEX_HOME`** so auth + history still resolve while codex's default config/cache roots point
174
+ at an empty dir (trims the roaming noise). When the user's real skill root `$HOME/.agents/skills`
175
+ exists, the throwaway home gets `.agents/skills` as a link to it (read-only on every posture), so a
176
+ review can load a skill, and the fence directives admit reading it. This is **env hygiene, NOT a security
147
177
  boundary** — absolute paths anywhere on disk remain readable under `read-only`; the real read-scoping is
148
- the prompt fence ("do not read outside the working tree, except the precomputed-diff temp file").
178
+ the prompt fence ("do not read outside the working tree, except the precomputed-diff temp file" and a
179
+ linked skill).
149
180
 
150
181
  ## `resume` — resets posture, restated via `-c`
151
182
 
@@ -157,7 +188,8 @@ the sandbox/approval/network posture, and — probed on codex-cli 0.147.0 — ac
157
188
  run starts, so the failure is loud and costs no quota; the wrapper's own test pins the accepted set
158
189
  (`RESUME_ACCEPTED_FLAGS`) precisely because the hermetic fake CLI accepts any argv and cannot answer
159
190
  this question. The `codex-exec --resume`/`--resume-last` entrypoint handles the reset: it restates
160
- the entire policy via `-c` (`sandbox_mode=workspace-write`, `approval_policy=never`,
191
+ the entire policy via `-c` (`approval_policy=never` and the posture it chooses afresh for this run —
192
+ the profile `jev`'s four overrides, or `sandbox_mode=workspace-write` with
161
193
  `sandbox_workspace_write.network_access=false`) plus the host posture's `-m`/effort and
162
194
  `--ignore-user-config`, reads the session id from the sidecar (or an argument), and applies the same
163
195
  EVIDENCE posture as a fresh run — `-o` for the final message, `--json` into the trace. Only a *raw*
@@ -175,8 +207,9 @@ no-op — while `codex-review` **refuses pre-spend** (the fail-closed hard-timeo
175
207
 
176
208
  Both wrappers, before invoking codex:
177
209
 
178
- - **unset** `OPENAI_API_KEY`, `CODEX_API_KEY`, `OPENAI_BASE_URL`, and every other `*_API_KEY`, so a
179
- stray key can't switch to paid api-key billing;
210
+ - **unset** `OPENAI_BASE_URL` and every `*_API_KEY` except `TYPESAFE_API_KEY` (`OPENAI_API_KEY` and
211
+ `CODEX_API_KEY` included), so a stray key can't switch to paid api-key billing; `TYPESAFE_API_KEY`
212
+ passes as given (empty stays empty, unset stays unset) and changes no flag;
180
213
  - pass **`--ignore-user-config`** so a personal `~/.codex/config.toml` cannot change behaviour. Auth
181
214
  still works: codex reads the cached login from `CODEX_HOME` (`~/.codex`) regardless of that flag;
182
215
  - preflight `codex login status` and refuse unless it contains `Logged in using ChatGPT`;
@@ -203,7 +236,8 @@ exec` over the precomputed diff instead, keeping every invariant intact.
203
236
  | `-c key=value` | override a config value (dotted, TOML-parsed) — how policy is set deterministically |
204
237
  | `--sandbox <mode>` | `read-only` \| `workspace-write` \| `danger-full-access` (this skill uses the first two) |
205
238
  | `-c approval_policy=never` | never pause for interactive approval (required: exec has no TTY) |
206
- | `-c sandbox_workspace_write.network_access=false` | network OFF under workspace-write (the exec invariant) |
239
+ | `-c sandbox_workspace_write.network_access=false` | network off under workspace-write (exec on today's flags) |
240
+ | `-c default_permissions=jev` + `-c permissions.jev.*` | the permissions profile `jev`: its filesystem part and a network limited to the listed domains (probed on 0.160.0; never beside `--sandbox`/`sandbox_mode`) |
207
241
  | `-c hide_agent_reasoning=true` / `-c model_reasoning_summary=none` | drop chain-of-thought from output (reasoning still runs) |
208
242
  | `-o, --output-last-message <f>` | write ONLY the final message (clean capture) |
209
243
  | `--json` | structured event stream (`thread.started` ⇒ session id) |
@@ -230,5 +264,10 @@ exec` over the precomputed diff instead, keeping every invariant intact.
230
264
  from the target project root.
231
265
  - **`exceeded the hard cap`** (exit 124/137): the run hit `CODEX_HARD_TIMEOUT` — raise it for a
232
266
  known-healthy slow run, or narrow the task, then re-dispatch.
233
- - **codex wants to install a dependency**: it can't (network OFF in exec) — install it by hand, then
234
- re-dispatch.
267
+ - **codex wants to install a dependency**: it can't (exec reaches at most `api.typesafe.ai` and
268
+ `docs.typesafe.ai`) — install it by hand, then re-dispatch.
269
+ - **exit 72**: codex refused the permissions profile `jev` (the message is on stderr). A refused profile
270
+ key, codex's error item before the first turn, is recorded for that wrapper's profile digest when the
271
+ state file can be written (otherwise the wrapper prints one warning naming the file), so that wrapper's
272
+ next run on that codex version takes today's flags — re-dispatch; a `bwrap: execvp` failure records
273
+ nothing and recurs on every run until its cause is fixed.
@@ -28,9 +28,10 @@ codex login status # expect: Logged in using ChatGPT
28
28
 
29
29
  This caches credentials under `CODEX_HOME` (`~/.codex`, e.g. `~/.codex/auth.json`). That directory is
30
30
  **personal** — never copy, commit, package, print, or share it. This skill needs **no API keys** and
31
- must not be configured with api-key billing; both wrappers unset every `*_API_KEY` (and
32
- `OPENAI_BASE_URL`) and pass `--ignore-user-config`, so billing can never silently fall back to
33
- pay-as-you-go and a personal `~/.codex/config.toml` can never change behaviour.
31
+ must not be configured with api-key billing; both wrappers unset `OPENAI_BASE_URL` and every
32
+ `*_API_KEY` except `TYPESAFE_API_KEY` (the one key a run passes, as given) and pass
33
+ `--ignore-user-config`, so billing can never silently fall back to pay-as-you-go and a personal
34
+ `~/.codex/config.toml` can never change behaviour.
34
35
 
35
36
  ## 3. Put the wrappers on `PATH`
36
37
 
@@ -69,8 +70,10 @@ if it reports a missing git work tree or root `AGENTS.md`, run it from a project
69
70
  ## Notes
70
71
 
71
72
  - The wrappers are **subscription-only** by design and will not use api-key billing.
72
- - `codex-exec` runs a **workspace-write** sandbox with **network OFF**; `codex-review` runs
73
- **read-only**. They also run on the host posture — `CODEX_MODEL` / `CODEX_EFFORT` from the
73
+ - `codex-exec` runs **workspace-write**; `codex-review` runs **read-only**. On Linux with codex-cli
74
+ 0.160.0 or newer, off a codex version and profile digest recorded as refused and, for `codex-exec`, from
75
+ a repository outside every temp root, both carry the permissions profile `jev`, whose network reaches `api.typesafe.ai`
76
+ and `docs.typesafe.ai` and no other host; elsewhere the network stays off. They also run on the host posture — `CODEX_MODEL` / `CODEX_EFFORT` from the
74
77
  bridge settings file, else `gpt-6.1-sol` / `high` — checked against the installed CLI's catalog,
75
78
  and refuse a one-off model or effort unless `CODEX_PROBE=1` (see `../SKILL.md`), enforce a hard
76
79
  timeout, capture only codex's final message, and block codex from writing git via a shim — see
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.22.0",
6
+ "version": "14.23.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": "14.22.0",
3
+ "version": "14.23.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",
@@ -44,7 +44,7 @@ Run `node ${CLAUDE_SKILL_DIR}/tools/recommendations.mjs --cwd <project-root> [--
44
44
 
45
45
  **Sandbox lanes (what to DO with the `sandbox-lane` recipe, per host class):**
46
46
 
47
- - **Settings-native sandbox** (the harness reads the `.claude/settings.json` sandbox keys): nothing beyond the `--bridge-tier` wiring — the tier's `excludedCommands` already routes the wrappers OUTSIDE the sandbox, so they never consult `allowedDomains`/`allowWrite`. Record the ack (the ack-write one-liner) once the tier is confirmed.
47
+ - **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), and the lane is the hand-apply `allowedDomains` shape in (2) below, filled from this recipe — the agent does not add it, the kit never writes these keys. Record the ack (the ack-write one-liner) once the lane is confirmed.
48
48
  - **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 never writes these keys) 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.
49
49
  - 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).
50
50
 
@@ -50,7 +50,7 @@ Honesty notes: tier entries get **NO PreToolUse-hook residual coverage — with
50
50
 
51
51
  **The boundary of that qualifier, stated so it is not guessed.** It covers claims about `sandbox.*` keys — the block whose honouring was observed to vary by host. It deliberately does NOT cover `permissions.*` semantics (an allow rule auto-approving a command, `local > project` precedence for a non-sandbox key): that is the harness's own documented permission model, the layer this whole mode rests on, and hedging it would say nothing true. **Stated residual:** which sentences count as settings-derived runtime claims is decided by review, not by a checker — deciding whether a sentence asserts a runtime effect is a question about prose meaning, and this family deliberately has no mechanism that judges that (the same limit that retired the byte-scanning deny rung). A new claim-bearing line therefore has to carry the qualifier by authorship; the acceptance criterion is that every line stating what a `sandbox.*` key DOES carries it, and the tests pin the surfaces that exist today. What does NOT change is the CLASSIFICATION: which red-line a key would weaken, and that a proven bridge-tier exclusion is a note rather than a weakening, are properties of the declaration and stay flat statements.
52
52
 
53
- **The `--bridge-tier` (own opt-in, AD-044).** Seeds what a promptless council review run needs — BOTH surfaces: `permissions.allow` prefix rules AND the wrapper names in `sandbox.excludedCommands` (where the host honors the settings sandbox keys the harness runs an excluded command OUTSIDE the sandbox — the wrappers need network — so a plain allowlisted invocation triggers no sandbox-bypass approval; where it does not, the wrapper starts sandboxed and simply prompts or fails loudly — fail-safe, never a silent widening). Both land in the **project** `.claude/settings.json` — the file this writer owns; an exclusion placed only in `settings.local.json` was live-observed NOT to route the command outside the sandbox (2026-07-11: the wrapper then starts sandboxed and dies on a read-only HOME + a network prompt), so hand-wiring the local file is not a working substitute for this tier. **Honesty note:** a session whose sandbox is imposed by the harness runtime itself (e.g. an IDE-managed session sandbox) may ignore settings-level exclusions entirely — there the wrappers need the session-level sandbox config (or a per-run consented bypass); the tier's seeded posture is correct for the settings-native sandbox and simply prompts again elsewhere (fail-safe, never a silent widening). Each bridge's observed egress hosts are declared in its `capability.json` `networkHosts` — the single documentation source (observed-minimal — a blocked host names itself at run time; read the manifests, this doc deliberately retypes no host list). The kit **never seeds** `sandbox.network.allowedDomains` or `sandbox.filesystem.allowWrite` (bridge council 2026-07-11, both backends concur): a network pre-allow widens egress for EVERY sandboxed command, and a write allowance on CLI state dirs (`~/.codex`, `~/.gemini/…`) would make credential dirs writable to every sandboxed command. Per-bridge picture under a harness-managed sandbox (live-observed 2026-07-11/12): an IDE-managed session sandbox ignores hand-applied `sandbox.network.allowedDomains` / `sandbox.filesystem.allowWrite` in BOTH settings scopes — its own per-host network consents govern egress; the durable zero-prompt lanes there are the session/host sandbox config (hosts from `networkHosts` + the CLI state-dir writes) or the per-run consented bypass. codex additionally needs a writable HOME (EROFS `~/.codex`); note the apex-vs-wildcard nuance — an apex domain is NOT covered by its `*.`-wildcard form, so the manifests carry both forms where observed (the blocked host names itself at run time). The upgrade Recommendations advisor surfaces exactly this recipe (hosts ∪ resolved `writableDirs`) as the `sandbox-lane` discoverability item — a consent-gated **ack writer** converging on a neutral fingerprint acknowledgement recorded into the family-owned `docs/ai/acks.json` (the kit still never seeds the security keys — those stay hand-apply), with the posture notes at the consent moment (`${CLAUDE_SKILL_DIR}/references/modes/recommendations.md`). Membership is the FROZEN review-wrapper constant, **never** the execution/probe wrappers (`codex-exec`, `agy-run` keep their human prompt — delegated execution is not covered by this consent; codex-exec's nested-sandbox recovery is the canon's observed-failure lane, not a preemptive tier seed), and only the **`code` review mode** — a `plan`/`diff` invocation takes a file argument that can point OUTSIDE the repo, so those modes keep their prompt; each wrapper entry derives ONLY when its bridge is **placed on PATH** (an absent bridge is a stated skip). The seeded byte-forms (this list IS the documented-invocation source for the bridge tier):
53
+ **The `--bridge-tier` (own opt-in, AD-044).** Seeds what a promptless council review run needs — BOTH surfaces: `permissions.allow` prefix rules AND the wrapper names in `sandbox.excludedCommands` (where the host honors the settings sandbox keys the harness runs an excluded command OUTSIDE the sandbox — the wrappers need network — so a plain allowlisted invocation triggers no sandbox-bypass approval; where it does not, the wrapper starts sandboxed and simply prompts or fails loudly — fail-safe, never a silent widening). Both land in the **project** `.claude/settings.json` — the file this writer owns; an exclusion placed only in `settings.local.json` was live-observed NOT to route the command outside the sandbox (2026-07-11: the wrapper then starts sandboxed and dies on a read-only HOME + a network prompt), so hand-wiring the local file is not a working substitute for this tier. **Honesty note:** a session whose sandbox is imposed by the harness runtime itself (e.g. an IDE-managed session sandbox) may ignore settings-level exclusions entirely — there the wrappers need the session-level sandbox config (or a per-run consented bypass); the tier's seeded posture is correct for a settings-native sandbox that honours the exclusion and simply prompts again elsewhere (fail-safe, never a silent widening). A harness that reads the settings sandbox keys may still drop this exclusion (observed 2026-10-06: Claude Code 2.1.291 ran an agent's plain `codex-review` inside its sandbox with the project entry present); its lane is the settings-native one in `${CLAUDE_SKILL_DIR}/references/modes/recommendations.md`. Each bridge's observed egress hosts are declared in its `capability.json` `networkHosts` — the single documentation source (observed-minimal — a blocked host names itself at run time; read the manifests, this doc deliberately retypes no host list). The kit **never seeds** `sandbox.network.allowedDomains` or `sandbox.filesystem.allowWrite` (bridge council 2026-07-11, both backends concur): a network pre-allow widens egress for EVERY sandboxed command, and a write allowance on CLI state dirs (`~/.codex`, `~/.gemini/…`) would make credential dirs writable to every sandboxed command. Per-bridge picture under a harness-managed sandbox (live-observed 2026-07-11/12): an IDE-managed session sandbox ignores hand-applied `sandbox.network.allowedDomains` / `sandbox.filesystem.allowWrite` in BOTH settings scopes — its own per-host network consents govern egress; the durable zero-prompt lanes there are the session/host sandbox config (hosts from `networkHosts` + the CLI state-dir writes) or the per-run consented bypass. codex additionally needs a writable HOME (EROFS `~/.codex`); note the apex-vs-wildcard nuance — an apex domain is NOT covered by its `*.`-wildcard form, so the manifests carry both forms where observed (the blocked host names itself at run time). The upgrade Recommendations advisor surfaces exactly this recipe (hosts ∪ resolved `writableDirs`) as the `sandbox-lane` discoverability item — a consent-gated **ack writer** converging on a neutral fingerprint acknowledgement recorded into the family-owned `docs/ai/acks.json` (the kit still never seeds the security keys — those stay hand-apply), with the posture notes at the consent moment (`${CLAUDE_SKILL_DIR}/references/modes/recommendations.md`). Membership is the FROZEN review-wrapper constant, **never** the execution/probe wrappers (`codex-exec`, `agy-run` keep their human prompt — delegated execution is not covered by this consent; codex-exec's nested-sandbox recovery is the canon's observed-failure lane, not a preemptive tier seed), and only the **`code` review mode** — a `plan`/`diff` invocation takes a file argument that can point OUTSIDE the repo, so those modes keep their prompt; each wrapper entry derives ONLY when its bridge is **placed on PATH** (an absent bridge is a stated skip). The seeded byte-forms (this list IS the documented-invocation source for the bridge tier):
54
54
 
55
55
  - `Bash(codex-review code:*)` — the code-mode prefix, args wildcard; plus `codex-review` in `sandbox.excludedCommands`
56
56
  - `Bash(agy-review code:*)` — the code-mode prefix, args wildcard; plus `agy-review` in `sandbox.excludedCommands`
@@ -83,9 +83,9 @@ const RAW_BACKENDS = [
83
83
  },
84
84
  notes: [
85
85
  'nested-sandbox limit: codex-exec ships its OWN OS sandbox (bwrap workspace-write) and cannot run nested inside a harness sandbox (the FS turns read-only) — route it OUTSIDE the harness sandbox (excludedCommands / a per-run consented bypass) on the OBSERVED bwrap/EPERM failure, never a preemptive blanket',
86
- 'exec posture banner: ONE stderr line before dispatch states the ACTUAL run posture — exec posture: model=… effort=… tier=… sandbox=workspace-write session=fresh|resume:<id> source=… timeout=… — from RESOLVED post-validation values; the resume id is validated pre-spend, and control bytes in any banner field refuse pre-spend',
86
+ 'exec posture banner: ONE stderr line before dispatch states the ACTUAL run posture — exec posture: model=… effort=… tier=… sandbox=jev-profile|workspace-write session=fresh|resume:<id> jev=… source=… timeout=… — from RESOLVED post-validation values; the resume id is validated pre-spend, control bytes in a model, effort, tier or timeout value refuse pre-spend, and a jev= reason shows a control byte as ?',
87
87
  'threat model: the sidecar byte and grammar screens detect corrupted input under a trusted parent environment. A hostile parent environment — including exported shell functions or PATH substitution of core/backend commands — is outside the threat model and can substitute the backend itself. Targeted shadow-proof resolution protects banner/dispatch honesty from accidental shadowing; it is not an environment security boundary',
88
- 'the exec posture banner appends a banner-only source=model:<s>,effort:<s> field — where each value came from: default, setting or environment — then a banner-only timeout=<duration|uncapped> field — exactly the duration handed to timeout(1), uncapped when no timeout/gtimeout binary caps the run; INFORMATIONAL only: neither is persisted in a receipt or session sidecar',
88
+ "the exec posture banner states what the run reaches in a jev= field: jev=api.typesafe.ai,docs.typesafe.ai when the run carries the permissions profile `jev` (Linux, codex-cli 0.160.0 or newer, a repository outside every temp root, a version and profile digest not recorded as refused), else jev=unreachable (<reason>) on today's workspace-write flags with network off; a run whose codex refuses the profile exits 72 with no success receipt and, for a refused profile key, records its version and digest when the state file can be written, so the next run on them takes today's flags; then a banner-only source=model:<s>,effort:<s> field — where each value came from: default, setting or environment — then a banner-only timeout=<duration|uncapped> field — exactly the duration handed to timeout(1), uncapped when no timeout/gtimeout binary caps the run; INFORMATIONAL only: none of the three is persisted in a receipt or session sidecar",
89
89
  'quote the posture banner verbatim when labeling this dispatch — the banner is the machine-stated posture; a prose re-type drifts',
90
90
  'every-run nested-sandbox scan (DUAL policy, deliberately two different rules): the scan runs on EVERY completed run, not only a failed one, because a run that SURVIVES the nested-sandbox failure exits 0 with an ungrounded answer and nothing said so. Failed run (rc != 0): the existing loose whole-trace combination rule prints the recovery hint. Successful run (rc == 0): a warning fires ONLY on precise per-item evidence — both a sandbox-mechanism token AND a permission/read-only failure token inside the aggregated_output of ONE command_execution item whose failure is PROVEN (a nonzero exit_code, or the serialized status "failed"); a null exit_code is never failure by itself, tokens split across two items never fire, and a successful command\'s output never fires. The answer is printed FIRST on stdout, then the warning on stderr. HONEST RESIDUAL: the exit status does NOT change on that lane (a distinct nonzero exit would give a heuristic scan DENY polarity, refusing real work whenever the scan over-warns), so an orchestrator keying on exit status alone can still bank an ungrounded answer — the stderr warning is the signal',
91
91
  ],
@@ -99,7 +99,7 @@ const RAW_BACKENDS = [
99
99
  continue: [],
100
100
  receipt: 'side effect — a successful review appends one JSON receipt line to <git dir>/agent-workflow-review-receipts.jsonl (AW_REVIEW_RECEIPTS overrides): 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 mode; durationS = integer wall-clock seconds from CLI start through verdict parsing and the wrapper prints review duration: <n>s; blocking = the count of [blocker] and [major] lines (schema mode: findings with either severity; a payload whose findings cannot be counted fails the run, no receipt); artifactPath = the normalized realpath on plan receipts only (repo-relative inside the work tree, absolute otherwise), while a double quote, backslash or control byte refuses pre-spend because the receipt encoder cannot carry it; verdict parsed from the mandated literal verdict line (schema mode: the verdict field); always fresh:true (one-shot) + grounded:true (native AGENTS.md auto-merge, factsHash null); probe = whether the run relaxed the quality guards (CODEX_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, effort, tier} (tier null on the standard 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, and a posture value carrying control bytes refuses pre-spend in every mode; a run whose final message carries NO recognized \'Verdict: <ship|revise|rethink>\' line — empty or missing output included — exits 4 with NO receipt (D4: a FAILED review to RE-RUN, never a fatal session error); when the dispatch nonce seam is supplied — the AW_REVIEW_NONCE environment value or its plain-argument equivalent --nonce <n> (one seam: the flag assigns the same value; supplying both with different values refuses pre-spend) — under the safe grammar [A-Za-z0-9._-]{1,64} (anything else refuses pre-spend), the wrapper first mints the finding MANIFEST {schema, backend, nonce, fingerprint, findings} beside the receipts file (agent-workflow-finding-manifest-<backend>-<nonce>.json; atomic, no-clobber — a byte-identical rewrite is an idempotent no-op, different bytes refuse loudly) ORDERED before the receipt append — a failed manifest write EXCLUDES the receipt append, so a nonce-supplied dispatch can never land a receipt without its readable manifest; a nonce-less invocation adds NO nonce field and mints NO finding manifest (the existing wrapperVersion field still changes with each bridge release); a write failure warns, never fails the review',
101
101
  notes: [
102
- 'the review posture banner appends a banner-only source=model:<s>,effort:<s> field — where each value came from: default, setting or environment — then a banner-only timeout=<duration> field — exactly the duration handed to timeout(1); the hard-timeout preflight fails CLOSED when no timeout/gtimeout binary exists (the wrapper refuses by name before any CLI run, so an uncapped review run can no longer happen), and neither field enters the receipt posture or the D5 banner↔receipt parity',
102
+ 'the review posture banner states what the run reaches in a jev= field: jev=api.typesafe.ai,docs.typesafe.ai when the run carries the read-only permissions profile `jev` (Linux, codex-cli 0.160.0 or newer, a version and profile digest not recorded as refused), else jev=unreachable (<reason>) under --sandbox read-only; a run whose codex refuses the profile exits 72 with no receipt; then a banner-only source=model:<s>,effort:<s> field — where each value came from: default, setting or environment — then a banner-only timeout=<duration> field — exactly the duration handed to timeout(1); the hard-timeout preflight fails CLOSED when no timeout/gtimeout binary exists (the wrapper refuses by name before any CLI run, so an uncapped review run can no longer happen), and none of the three fields enters the receipt posture or the D5 banner↔receipt parity',
103
103
  'quote the posture banner verbatim when labeling this dispatch — the banner is the machine-stated posture; a prose re-type drifts',
104
104
  ],
105
105
  },
@@ -109,8 +109,8 @@ source** for a hand-applied sandbox/network allowlist (session sandbox config, o
109
109
  - **The kit never seeds these into settings** (bridge council 2026-07-11, both backends concur):
110
110
  a network pre-allow widens egress for **every** sandboxed command, so running the wrappers
111
111
  **outside** the sandbox (`sandbox.excludedCommands`, the `--bridge-tier` wiring) stays the
112
- primary lane; the hosts list exists for the **hand-apply** fallback under harness-managed
113
- sandboxes where settings-level exclusions are inert.
112
+ primary lane; the hosts list exists for the **hand-apply** fallback under a sandbox that does
113
+ not honour the exclusion (harness-managed, or a settings-native harness that drops it).
114
114
  - Observed-minimal, honestly incomplete: a blocked host names itself at run time — extend the
115
115
  hand-applied list by hand; the manifest list records what was actually observed.
116
116