Repository navigation
Clean default for every host: carriers held out of the shared template and retired on re-apply, removed Codex flags pruned, jCodeMunch at user scope - #699
Conversation
31b1b94 to
a3045d6
Compare
23b1cd7 to
91118a3
Compare
f367c81 to
6ce2584
Compare
e2b355c to
1b28148
Compare
45445f6 to
ba3ee96
Compare
d48973c to
748cbe4
Compare
33b236b to
2f85d97
Compare
…Codex's own config writer `codex features list` marks a feature `removed` when the key does nothing: plugin_hooks is `Stage::Removed` (default false) at openai/codex rust-v0.160.0, codex-rs/features/src/lib.rs L1474-L1479, yet NativeStack's config.toml still carries `features.plugin_hooks = true`, and a template re-apply never removes a key. tools/adoption/codex_config_prune.py reads the removed names from the installed Codex (in an empty temporary HOME and CODEX_HOME), finds them in the base user layer's [features] table, and deletes exactly those keys with config/batchWrite (value null, mergeStrategy replace, expectedVersion from config/read), the writer Codex's own clients and apply_codex_lane.py use. Dry run by default; --apply refuses while a codex process runs, writes config.toml.bak.<UTC stamp> (0600, never overwriting) and reads the result back; idempotent, and a profile's or a plugin's table is never touched. 12 tests with a fake codex and a fake app-server (local integration, not upstream tests); the dry run on NativeStack's live config names plugin_hooks and nothing else. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> (cherry picked from commit 38f9533c205e082afed6d4a132a57a9021220f3b) (cherry picked from commit 227c5df) (cherry picked from commit 6c02453)
… shared template, retire them on re-apply, jCodeMunch at user scope
The user's clean-install directive of 2026-10-04 ("WE NEED THE CLEAN SOTA INSTALL ... YOUR ADAPTION AND TESTING METHODS MAYNOT BE
SOTA ALIGNED WITH UPSTREMA REPOS"), carried to NativeStack by the command center
(docs/decisions/2026-10-04-claude-template-holds-out-token-lane-carriers.md).
- adoption/templates/claude.settings.template.json registers neither carrier hook; install_claude_profile.py copies the nine carrier
files only when --hook names them (HELD_OUT_HOOKS); the files stay byte-pinned in adoption/hooks/claude/ with a README and
held-out-hook-entries.json for opting in.
- apply_claude_settings.py retires the hook objects that run the two carrier hook files from a host's live settings (the merge never
dropped a base hook, so a template change alone would have left NativeStack's SubagentStart carrier running), unless the template
carries the command or --keep-held-out-hooks is given: demonstrated on a copy of NativeStack's live settings (exactly one hook
removed, idempotent), 8 tests.
- The builder's carrier map entry goes with the pieces; counts 393 pieces, 354 wired, 24 not wired, 15 authorization.
- jCodeMunch at user scope in adoption/mcp/claude-user.json and the Codex user template (the user's directive of the same day,
docs/decisions/2026-10-04-new-wsl-jcodemunch-user-scope.md, cost evidence kept visible): the repository's tests make the two
user-scope sets mirror each other, so the 2604 additions give the entries up; the Codex AGENTS template and the scaffold's copy
of its top rule give jcodemunch serena's lane (one phrase shortened to stay under 8,192 bytes; applying the block to
~/.codex/AGENTS.md waits for the user's yes); bootstrap.md, the session handbook and the tests follow.
Local integration checks (2,190 tests in 37 modules), not upstream tests. Registry excluded from this commit.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
(cherry picked from commit 457ee6c4ba9f8724749896629c3e1380cfcebca0)
(cherry picked from commit 1c73227)
(cherry picked from commit 8946b20)
…mentions its path (read P2), and two record passages (read P3) Cross-family read of 91118a3 (668 tests, 0 failures) found one P2: runs_held_out_hook() matched any shell word that names a carrier file under .claude/hooks/, so merge_settings() deleted a host's own hook whose command merely mentions the path, for example `sha256sum /home/example/.claude/hooks/token-lanes-session-start.py` (reproduced against the committed template; the same hook survived with keep_held_out=True). The matcher now accepts a command only when the carrier file is the executable or the script operand of a python interpreter (the first word that is not an option, after any NAME=value assignments), whatever options, arguments and redirections follow, and nothing else runs but a trailing `|| true`, the shape of the shipped entries (shlex with punctuation_chars splits the control operators; a multi-line command is never matched). A hook that mentions the path or runs something else beside the carrier is left alone. Tests first: test_a_hook_that_merely_mentions_a_carrier_path_is_kept (nine commands, among them the read's reproduction, an argument of another script, an echo, a compound and a multi-line command) fails on the earlier matcher for eight of them and passes now; test_every_shape_that_runs_a_carrier_is_retired (eight shapes: $HOME, ${HOME}, an absolute path, ~, python, python3.12, an option, an environment assignment, the file as the executable) and the opt-in file's two entries pass on both as controls. tests.test_apply_claude_settings: 36 tests OK. Against a copy of this host's live settings the merge still removes exactly one hook, the SubagentStart carrier, a second merge changes nothing and keep_held_out keeps every hook. Two residual passages of the read are corrected: the jCodeMunch record said the counts moved to 404 pieces and 35 not wired, but the carrier entries this change removes from the map bring them to 393, 354, 24 and 15 (the builder's --check); and host step 3 named apply_codex_lane.py as a way to register jcodemunch for Codex, which it does not do (owned_edits: registering servers is not that lane's job). The step is now `codex mcp add jcodemunch --env ... -- <ECO_ROOT>/bin/jcodemunch-mcp` plus the template's three other keys through Codex's config writer, which a scratch Codex home on codex-cli 0.159.3 showed complete the entry (`codex mcp get jcodemunch` lists all of them). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> (cherry picked from commit e649c95) (cherry picked from commit 64fb0ad)
…g, fail closed (delta read P2 x2, an extra round) The cross-family delta read of 998420e found two more P2s in runs_held_out_hook(), both ways for a re-apply to delete a hook the host wrote for itself, and both from reading shell with a parser: - Python option operands: the first word that is not an option is not the script operand. `python3 -c 'print("host") # <path>'` and `python3 -X <path> -c ...` matched and were deleted though they never run the carrier; `python3 -X utf8 <path>` and `python3 -W ignore <path>` run it and survived. - Compound commands and substitutions: shlex glues `;(` and `&&(` into one token, which bypassed the exact control-operator check, so `python3 <path>;(printf host)` and `&&(printf host)` were retired, and so were commands whose arguments or redirections substitute a command (`$(...)`, `> >(tee ...)`). The matcher is now CARRIER_COMMAND, one anchored pattern over the whole command string with closed character classes: variable assignments, an optional python interpreter (bare or absolute) with the options that leave the script operand in place (the flags B E I O S b d q s u, -X and -W with a value, `--`; never -c or -m), the carrier file as the script operand or the executable (unquoted with $HOME, ${HOME}, ~ or an absolute path, double-quoted with $HOME or an absolute path, single-quoted with an absolute path), its arguments (plain words, or quoted words with no expansion), `2>/dev/null` and a trailing `|| true`, and nothing else. Anything it cannot match whole, a control operator, a substitution, a backslash, a comment, a newline, a wrapper such as bash -c, exec, nohup, timeout or env, is the host's and is kept; a carrier such a hook also runs keeps running until its owner edits it (recorded as the residual). Tests first: HeldOutHookTests.ADVERSARIAL holds the read's 25 command shapes with the expectation that executing them in a shell established; 11 of them and 13 of 19 syntax tails failed on the earlier matcher (24 failing subtests) and all pass now, with a timing test (worst of nine pathological inputs of up to 240,000 characters: 36 ms). tests.test_apply_claude_settings: 39 tests OK; with test_install_claude_profile and test_codex_config_prune the three modules pass. Against a copy of this host's live settings the merge still removes exactly one hook, the SubagentStart carrier, a second merge changes nothing and keep_held_out keeps every hook. The record (decision 3, and an addendum that counts this as the extra round past the bounded loop) and the opt-in README state the new rule. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> (cherry picked from commit bfcb0bb) (cherry picked from commit dbe1176)
…y shipped, no grammar (delta read, 3 x P2, a second extra round) The cross-family delta read of e2b355c found three more P2s in the anchored pattern that replaced the shlex matcher, each a way for a re-apply to delete a hook the host wrote: options after `--` still select a host script (`python3 -- -u <path>`), an unquoted -X or -W value is globbed by the shell (`-X dev* <path>` runs a host file), and an unquoted $HOME or ${HOME} word-splits the script operand when the home directory contains a space. Every fix to a grammar had opened the next edge, so this one recognises none. runs_held_out_hook() now returns true only when the command equals, after trimming outer whitespace, a carrier command this repository shipped: SHIPPED_CARRIER_COMMANDS holds the four strings the history shows (git log -S over the template and the opt-in entries file; python3 "${HOME}/.claude/hooks/token-lanes-{subagent,session}-start.py" 2>/dev/null || true, and the same with $HOME), each with where it first appeared, and carrier_commands(home) adds each as install_claude_profile.py renders it for the host (the placeholder replaced by the home directory; the live NativeStack entry is that form). The home is the one the settings file belongs to (host_home: the parent of its .claude directory, else the current user's); merge_settings and retire_held_out_hooks take it as an argument. Nothing is parsed or expanded; every other hook is the host's and stays, including a hand-edited carrier command (the fail-closed residual). The unused pattern code and the shlex-era tests are gone. Tests: HeldOutHookTests holds the history (four strings with their sources) and requires the allowlist to equal it and the shipped template and opt-in files to carry only allowlisted commands; each shipped string is retired as shipped and as rendered; outer whitespace is trimmed and nothing else; a mutation test (every single-character deletion, insertion and replacement of the six strings: 6,884 variants) retires none; the second read's 25 shapes and the third read's 30 are all kept (the pattern matcher retired 26 of those 55, the reader's 9 failing preservation subtests among them); another host's home does not retire; applying to a temporary home retires the carrier rendered for that home. tests.test_apply_claude_settings: 44 tests OK; with test_install_claude_profile, test_codex_config_prune, test_token_lanes_session_start and test_new_wsl_client_config 333 tests OK. Against a copy of this host's live settings the merge still removes exactly one hook, the SubagentStart carrier, a second merge changes nothing, keep_held_out keeps every hook. The record (decision 3 and an addendum counting this as the second extra round) and the opt-in README state the rule. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> (cherry picked from commit 8df0884) (cherry picked from commit 8b740fe)
… one-pass rendering (delta read 4: 2 x P2, P3; an extra round) The fourth cross-family read of 1b28148 found three ways the allowlist's inputs still guessed: - str.strip() also removes a no-break space, a next-line character and a carriage return, which the shell treats as part of a word, so a command it runs differently was retired. A command must now equal a shipped string exactly. - host_home took the home from the path string: a relative target gave ".", an external target fell back to the operator's own home, a symlinked alias was not resolved and a trailing slash rendered "//.claude". The home is now the resolved <home>/.claude/settings.json; any other path retires nothing; a trailing slash is normalised. - Two sequential replaces of the two placeholder spellings added aliases that render_config.py never writes. One string.Template pass renders them, as the renderer does. Tests were written first: 78 failures and one error on the previous code across the seven new behaviours, 50 of 50 after. The decision record and the hooks README carry the new rule, the fourth read, the corrected mutation domain (6,908 variants), render_config.py as the renderer, the live-copy check as a one-off that is not retained as a receipt, and the CLAUDE_CONFIG_DIR residual. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> (cherry picked from commit 3290554) (cherry picked from commit a2abfdd)
…path that names the home (delta read 5: 2 x P2; the fifth and final round) The fifth cross-family read of d48973c found two ways a carrier was kept, both from the renderer substituting HOME as written: - A carrier installed with HOME spelled with a trailing slash, a "." or ".." segment, a doubled slash or a doubled leading slash ran and was kept: retirement compared one normalised spelling, and the trailing-slash test expected the doubled form to survive. - With a symlinked home (alias -> real) the renderer and the installer write the alias, while the settings file's resolved home is real, so the alias-rendered carrier was kept. The four shipped shapes stay a finite allowlist; where the template says HOME the text may now be any absolute path with no control character, quote, dollar sign, backtick or backslash (the shell expands those inside the double quotes) whose realpath is the realpath of the settings file's home (names_home). A check on a path, not a grammar of shell syntax. Red first: nine failures on the previous code across the new tests (six spellings rendered by the real render_config.render_one, the alias rendered both ways and applied through both paths, the trailing-slash test); the keep-cases (another directory, relative, empty, tilde, other case and every spelling the shell would expand, including ".../$X/..") pass before and after. 53 of 53 after; the 6,908 single-edit variants are still all kept; the reviewer's own repro file for these two families passes 6 of 6 against this head. The record carries the fifth read and the case-insensitive and symlinked-.claude residuals. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> (cherry picked from commit 176ee5a) (cherry picked from commit 7edb91e)
…e (job-059), recorded without a code change A spelling that cannot reach the carrier (ENOTDIR or ENOENT through a file or a missing directory followed by "..") realpaths to the home and is retired although its command never ran the carrier (benign); a home alias with a Unicode space such as U+00A0 fails isprintable() and its working carrier is kept (errs toward keeping the hook). The loop is closed; this commit is carried by the rebase onto main and the registry commit is redone there. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> (cherry picked from commit d1698415b9f7fbc8a263b2a5225c975e242969d9) (cherry picked from commit 7b35a06)
…ation at the line this branch moved The jCodeMunch registration adds 22 lines to the Codex user template above its [otel] table, so metrics_exporter moved from line 269 to 291 and the disposition for codex:config_group:otel.metrics_exporter cited a comment line. The broad regression run over the modules that name a changed file (3,414 tests) found it: two tests of test_upstream_surface_watch failed, and 110 pass with the citation re-pointed. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> (cherry picked from commit be75911)
…the literal text (macos-15 failure of #699) The full suite on macos-15 failed one test of 10,900: test_a_settings_file_names_its_own_home asserted that host_home of /home/example/.claude/settings.json is "/home/example", but the home is the settings file's resolved path and macOS resolves /home to /System/Volumes/Data/home. The assertion now compares with resolve(); the code is unchanged. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…cord in manifests/evidence.json Hot-file protocol (docs/lanes.md): evidence.json as on main 2875145 (with PR 701), the 22 changed hashed files and the new decision record registered with host_receipts.register_file, then component_matrix.py --write and new_host_grand_list.py --write; validate.py and validate_convergence.py --all-recorded exit 0. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2f85d97 to
4230cb5
Compare
|
Claude session native-agent-stack-5f: landing at head Observed main Required checks at this head: 7 pass . Unresolved review threads: 0. |
|
Claude session native-agent-stack-5f: post-merge observation. Landed as |
…the wave-4 promptfoo owner default on the repository-quality rule Integrates the eight GPT fix-wave builders (bounded job 050, GPT-6.1 Sol at max), working from the verified-E2E fix list. Two review rounds followed: - A five-way Claude read. Its 4 P1 authority wordings were fixed by the coordinator, and its 19 P2 recipe defects by GPT repair job 058. - A Claude delta read. Its P2 (the gateway canary check's missing argument) and the leftover wording were fixed by the coordinator. Result: - 21 slots are repaired in the install plan, its acceptance and the client wiring. - 4 still need a native sign-in or a destination: alerting, grafana, promptfoo and ccusage. - OpenHands stays at v1.50.1, the manifest baseline. - Every revised destination command is UNRUN here; the co-op session applies them on NativeStack2604. wave4: an owner default on promptfoo, resting on the owner's 2026-10-04 repository-quality rule. Decide round 2 and its Opus verification named promptfoo for paired skill verification in both clients (docs/decisions/2026-10-04-repository-quality-rule.md; evidence/artifacts/new-wsl-fix-wave-20261004/). CI repair (the coordinator, after the first push): - The shared top-rule templates go back to main: codex.AGENTS.template.md and examples/claude-native/CLAUDE.md (the 8 KiB budget and the verbatim surfaces). - The host stack manifest and its freshness snapshot go back to main. A host pin move needs its saturation-audit row and a qualification receipt (tests/test_stack_lifecycle.py:21-37), so srt, Worktrunk, Collector Contrib, Grafana and Syft are left to the currency PRs. - The 2604 plan, profile and architecture keep the plan's pins, and build_ecosystem.py --check labels the drift. - Every architecture pin_source cites its component's version line in the current stack, including two that #637 had shifted on main. Rebuilt on main 5df0e0e (after #699, #700 and #637) as one net content commit. Generated outputs were regenerated with their own tools, and the evidence registry follows in the last commit. The earlier commits stay reachable as 1572f48, 89d7a2c and 8ca0d7a for the review record. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Skip validate-macos, bootstrap-macos and bootstrap-macos-brew on every pull_request; retain full runs on filtered main pushes and manual dispatch, and move the weekly bootstrap schedule to nightly 06:47 UTC. Match the committed ruleset and dated automation catalog to the coordinator's seven required contexts, including the merge guard's documented count. Keep the Linux PR detector working and label the retained macOS selector as historical. Record the user's 2026-10-05 advisory decision, #699's portability catch, the fix-forward path and overturn rules. Sources: GitHub Actions workflow-syntax#jobsjob_idif and events-that-trigger-workflows#schedule, github/docs at 2bd66de8cea336061c9ea060c9b37385136e6ab3; #677's retained measurements; #699 (landed as 5df0e0e). Validation: the final PR-event test fails the original workflow with 133 failing subtests across all three macOS jobs (exit 1), and passes the new workflow (exit 0); 388 selected unittest tests (36 skipped), including the merge-guard stale-count red/green control; git diff --check. actionlint is not on PATH. validate.py exits 1 for evidence registry drift only. Evidence registration remains with the coordinator. The count-only docs/lanes.md correction follows that file's lane:shared integration policy. Built by GPT bounded job 064 (GPT-6.1 Sol at max); committed by the coordinator, who re-pointed the #699 citation from its branch-local head to the landed main commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Skip validate-macos, bootstrap-macos and bootstrap-macos-brew on every pull_request; retain full runs on filtered main pushes and manual dispatch, and move the weekly bootstrap schedule to nightly 06:47 UTC. Match the committed ruleset and dated automation catalog to the coordinator's seven required contexts, including the merge guard's documented count. Keep the Linux PR detector working and label the retained macOS selector as historical. Record the user's 2026-10-05 advisory decision, #699's portability catch, the fix-forward path and overturn rules. Sources: GitHub Actions workflow-syntax#jobsjob_idif and events-that-trigger-workflows#schedule, github/docs at 2bd66de8cea336061c9ea060c9b37385136e6ab3; #677's retained measurements; Validation: the final PR-event test fails the original workflow with 133 failing subtests across all three macOS jobs (exit 1), and passes the new workflow (exit 0); 388 selected unittest tests (36 skipped), including the merge-guard stale-count red/green control; git diff --check. actionlint is not on PATH. validate.py exits 1 for evidence registry drift only. Evidence registration remains with the coordinator. The count-only docs/lanes.md correction follows that file's lane:shared integration policy. Built by GPT bounded job 064 (GPT-6.1 Sol at max); committed by the coordinator, who re-pointed the #699 citation from its branch-local head to the landed main commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…(second of two; the user's relayed pointer yes) (#705) ### Scope - Second of the two pull requests of `docs/decisions/2026-10-04-codex-rtk-hook-qualified.md` (the first, #701, landed as `287514581`; this branch was based on main `4c897418f`, after #699 and #704, and carries main `4af7417b7`, after #720, #712, #696 and #724, as a merge commit). The install plan's `command-output` row now installs RTK's Codex PreToolUse hook with upstream's own command and trusts it: 1. two commands after the existing four: `rtk init -g --codex`, then `python3 tools/adoption/codex_hook_trust.py --command "rtk hook codex" --apply`; 2. its post_install acceptance gains the two Global `[ok]` lines of `rtk init --show --codex` and `codex_hook_trust.py --check`; a new after_sign_in acceptance runs a fresh `codex exec` told to add no prefix and requires the first `rtk git status` command item to have `exit_code == 0` and `status == "completed"` (review thread 1; **UNRUN**: the model call needs the sign-in); 3. the trust tool's execution-rule review and `--check`. Codex rewrites a call to `rtk <command>` before it matches execution rules on the command words, so a `forbidden` or `prompt` rule on a command rtk can rewrite no longer matches it, and the rewrite space is not finite (env prefixes, global options such as `git -C`, absolute paths, wrappers, PHP tool spellings, user filters and Unicode word boundaries all change what rtk rewrites, and the rewrite changes words after `rtk` too: `cat f` becomes `rtk read f`). Three GPT reads each found a bypass in an analysing version of the review, so the tool now **parses nothing** (the design 5f recommended after 705f): it reads each regular `*.rules` file of `<codex home>/rules` as bytes and accepts it only if (1) its sha256 is on `tools/adoption/exec_rules_reviewed.json`, an exact-bytes review for one rtk version (`rtk --version` must match the entry, `rtk config` must show `transparent_prefixes = []`, `rtk trust --list` must say `No trusted filters.`, `RTK_TRUST_PROJECT_FILTERS` must be unset, the user TOML filters file beside rtk's config must hold no filter; today one entry, #713's `hcom-deny.rules` at head `7fbe586f3`, sha256 `ac6254ee…`, after its repair round 2 changed comments and justification strings only), or (2) it holds nothing but allow rules in Codex's own format byte for byte (the lines `amend.rs` writes, hcom's own `hcom.rules` with its one comment line, empty lines), none containing the letters `rtk` anywhere (the rewrite inserts that word, as a token or inside a shell-script token, so such a rule matches the rewritten command and not the original: codex-cli 0.159.3 `execpolicy check` allows `rtk git push origin main` under `["rtk"]`, and the script token `FOO=1 rtk git push origin main` under `["/bin/bash", "-lc", ...]`, and gives no decision for the originals; 705g P1-1). Anything else is an exposure: `--apply` refuses with exit 2 before the app-server starts (so before any write, even for an already trusted hook), `--check` exits 6 for a trusted hook beside one (so the post_install acceptance catches a rule file added or edited after the grant), `--allow-exec-rules` accepts it. The listing is Codex's `collect_policy_files` mirrored fail-closed (only opening the directory may report it missing; each entry's type comes from its own `lstat`, so an entry that vanished raises; a symlink, directory or named pipe is not loaded and never opened). `--check` exits 0 only while every named hook is trusted and no rule file is exposed, 5 when a hook is not trusted, 4 when none matches, 6 when it is trusted beside an exposure; mutually exclusive with `--apply`, no edit, no backup. The plan's trust and check commands pass `--rtk "$e/bin/rtk"`. 81 unit tests (local stand-ins for the app-server and for `rtk --version`, `rtk config` and `rtk trust --list`), 63 mutants each failing one; 4. text that follows the row: the plan README sentence, the token session handbook paragraph (Codex runs RTK through the hook, and the exclusions are what make it leave `git show REV:path`, `diff`, `jq` and `git branch` alone), a dated note on the RTK card (cards are dated editions), the F9 recipe sentence and its owner-list test (`command-output` joins the after-sign-in owners), the new-WSL profile (`adoption/new-wsl-profile.{json,md}`), the definitive manifest and the consensus record it copies, which no longer say that `rtk init --global --codex` is never run (review thread 2; the manifest's declared consensus digest follows), the regenerated new-WSL handbook and its receipt's digests (the `wsl-new-distro-image` convergence record needs no re-pin: #704 freezes copies of the recipe and its test); the F9 owner list is #704's, with `command-output` added; 5. the decision record's addendum, which quotes the user's answer **as relayed by the command center**: "yes for rtk pointer" (00:49:16Z in the command center's transcript, part of a longer message), and a standing rule, "for the practice like rtk, always proceed with sota convergence highest quality practice with upstream repos" (00:50:08Z, a queued message; the command center first relayed both as "about 00:55Z", an estimate, and corrected the times). The writing session heard neither; both rest on that relay, which the user may withdraw. The answer covers what `rtk init -g --codex` does beyond the hook (RTK.md and an inert `@<Codex home>/RTK.md` pointer line in the Codex `AGENTS.md`; there is no hook-only mode with `--codex`), and a second addendum answering review thread 3: Codex rewrites the call before approval and matches execution rules on the rewritten words, rtk rewrites 36 of 55 measured commands including mutating ones, and rtk has no allow-list, so the trust step is guarded instead of limited (sources at both Codex pins and rtk 0.51.0, alternatives and the untested boundary in the record). - Base commit: `4af7417b7` (main after #720, #712, #696 and #724; the branch's own base was `4c897418f`) - Lane: `lane:foundation` - Owned paths touched: `evidence/artifacts/new-wsl-install-plan-20261002/{install-plan.json,install.sh,accept.sh,README.md}`, `evidence/artifacts/token-stack-fresh-session-e2e-20261004/**` (README, the scratch-run script and its output, `fixtures/hcom-deny.rules`, the mutation check, the heads derivation, its binary check and the heads file `rtk-rewrite-heads.json`, now review evidence beside them), `tools/adoption/exec_rules_reviewed.json`, `evidence/artifacts/new-wsl-handbook-20261001/receipt.json`, `docs/{token-session-handbook.md,token-efficiency-stack.json,new-wsl-handbook.json,new-wsl-handbook.md}`, `adoption/new-wsl-profile.{json,md}`, `evidence/artifacts/new-wsl-definitive-defaults-20261001/definitive-manifest.json`, `evidence/artifacts/new-wsl-layer-consensus-20261002/consensus.json`, `docs/decisions/2026-10-04-codex-rtk-hook-qualified.md`, `adoption/platforms/linux-wsl2-new-distro.md`, `tools/adoption/codex_hook_trust.py`, the tests, and `manifests/evidence.json` (last commit, `registry:`). Nothing is installed or trusted on any host by this change. ### SOTA sources - RTK: rtk-ai/rtk v0.51.0 at `e001f773f80b22b7dc4c7a79521b30e35aaef026`, `README.md` L133 (`rtk init -g --codex`), the installed `rtk init --help` (`--codex`: "uses PreToolUse hook + AGENTS.md + RTK.md"; `--hook-only`, `--no-patch` and `--auto-patch` refused with it), `rtk init --show --codex` and `rtk-init-codex-probe.json`. - Codex hook trust: openai/codex rust-v0.159.3, commit `01fc69f4026735edfdf6789820549727a4867b11`, `codex-rs/tui/src/hooks_rpc.rs` L58-L91 (`write_hook_trusts`: the `/hooks` review persists trust with `config/batchWrite` of `hooks.state`) and `codex-rs/hooks/src/engine/discovery.rs` L775-L815 (hash and status), which `codex_hook_trust.py` reproduces. - Codex hook dispatch and approval (review thread 3), identical lines at rust-v0.159.3 `01fc69f40267` and rust-v0.160.0 `a956835d0207`: `codex-rs/core/src/tools/registry.rs` L603-L660 (PreToolUse hook, `invocation = updated_invocation` L632, handler L689), `codex-rs/core/src/exec_policy.rs` L327-L420 and L770-L850 (rules matched on the command words; unmatched fallback), `codex-rs/shell-command/src/command_safety/is_dangerous_command.rs` L133-L143 (forced `rm`, unwrapping `sudo` and `env`), `codex-rs/hooks/src/events/pre_tool_use.rs` L357 and L388-L405, and `codex-rs/exec/src/exec_events.rs` L151-L166 (`CommandExecutionItem`: `exit_code`, `status`); `codex execpolicy check`, Codex's own rule evaluator. - Execution-rule review, rust-v0.160.0 `a956835d0207`: `codex-rs/core/src/exec_policy.rs` L1121-L1170 (`collect_policy_files`: only opening the directory may report it missing, errors on the iteration and on an entry's file type propagate, every entry's type is asked before its extension, symlinks are not loaded), L645-L660 (`load_exec_policy_with_warning`: a parse error in any rules file makes Codex load no file rules), `codex-rs/execpolicy/src/amend.rs` (`blocking_append_allow_prefix_rule`: the format of the allow-only `default.rules`), `codex-rs/execpolicy/README.md` (`prefix_rule` and `host_executable` builtins, strictest decision wins). The 705f counterexamples rest on: `codex-rs/execpolicy/src/parser.rs` L64 and facebook/starlark-rust `starlark_syntax/src/lexer.rs` L415 at `0c8d107ab963` (a raw CR inside a string is dropped), `codex-rs/shell-command/src/bash.rs` L492, `codex-rs/core/src/exec_policy.rs` L876 and `codex-rs/core/src/shell.rs` L22 (assignment-prefixed scripts fall back to the whole shell argv); rtk v0.51.0 `e001f773f80b`: `src/discover/registry.rs` L393-L418 and L1811 (PHP tool normalization), `src/core/toml_filter.rs` L199-L227 and L456-L495 (filters load only while trusted; trusted user filters keep the complete pattern, alternative branches allowed), `src/hooks/trust.rs` L51-L166 and `run_trust` (the global trust store, `rtk trust --list`, and the `RTK_TRUST_PROJECT_FILTERS` + CI override), `src/discover/registry.rs` L1743-L1763 (`exclude_commands` is applied before the TOML-filter rewrite), `src/filters/gcc.toml` L3 and the rust-lang/regex word-class contract (Unicode `\b`), `src/discover/rules.rs` (94 `RULES` patterns, each `^`-anchored), `src/discover/registry.rs` L64 (`ENV_PREFIX`), L194-L215 (`classify_command` normalization), L438 and L568 (global options, absolute paths), L1386-L1498 (`PROCESS_WRAPPERS`, shell keywords, `uv run`), L1743-L1760 (the TOML filters are a second rewrite source), `src/filters/*.toml` (62 builtin filters), `src/core/config.rs` L119-L145 (`transparent_prefixes`), `src/main.rs` L3072-L3100 (`rtk hook check`: exit 0 with the rewrite, exit 1 with `No rewrite for:`) and `rtk config`'s output (`show_config`). hcom's own rule file (aannoo/hcom `7151660a3`): `src/hooks/codex.rs` `build_codex_rules` L1508-L1534 and `HCOM_TOOL_NAMES` L578-L585, `src/hooks/common.rs` `SAFE_HCOM_COMMANDS` L52-L72. - rtk v0.51.0 `e001f773f80b`: `hooks/codex/README.md` L14-L24 (the rewrite precedes Codex's approval and sandbox checks), `src/hooks/permissions.rs` L64-L72 (no Codex rule source), `src/core/config.rs` L119-L123 and `src/discover/registry.rs` L1548-L1582 (`exclude_commands` is exclusion-only), `rtk hook check --agent codex`. - Codex expands no `@` reference in `AGENTS.md`: `docs/token-efficiency-stack.md`, citing `codex-rs/codex-home/src/instructions/mod.rs` at rust-v0.157.1. - The plan's own conventions: `check_plan.py` (commands, acceptance and scripts agree), the hot-file protocol of `docs/lanes.md`, and #694's re-pin of the recipe-freezing convergence record. ### Evidence-class table | Claim | Evidence class | Command / receipt | | --- | --- | --- | | The row's steps, read from `install-plan.json` and run in scratch HOMEs with rtk 0.51.0 and codex app-server 0.159.3: `--check` exit 4 before the hook exists; `rtk init -g --codex` 0 (the hook lands at `pre_tool_use:1:0`, after a memory hook's group); `--check` 5 before the trust; the trust 0 ("read back from hooks/list by key and hash"); the post_install program 0; and nineteen homes for the rule review: #713's `hcom-deny.rules` by its exact bytes lets the trust proceed, and a `git push` rule added after the grant makes `--check` exit 6 and the trust refuse; the same bytes plus one comment line, a configured rtk transparent prefix, a user TOML filter beside the reviewed file, a trusted project filter and the CI override variable `RTK_TRUST_PROJECT_FILTERS` (the last two each make the real rtk rewrite `hcom kill luna`, recorded before and after, and with rtk exclusions for `hcom` and `uvx hcom` in its config the same filters leave both commands alone) are each refused; Codex's `default.rules` format and hcom's `hcom.rules` format let the trust proceed; a `git push` forbid rule is refused (exit 2, nothing written; `--check` 5), accepted with `--allow-exec-rules`, after which `--check` exits 6; the same rule with its `rtk git push` twin is still refused; the counterexamples of 705e (`git -C .`, broad `uv` and `npx`, the nested `host_executable` file, under which the real codex evaluator forbids `git push origin main`) and of 705f (a raw CR in a token, a `bash -lc` script rule, `phpunit.exe`, `g++` plus a combining mark, each of which the real rtk rewrites or the real evaluator reads differently), an allow rule that holds `rtk` (the evaluator allows `rtk git push origin main` under `["rtk"]` and the script token `FOO=1 rtk git push origin main` under a `bash -lc` rule, and gives no decision for the originals; the real hook produces the second from `FOO=1 git push origin main`) and an unreadable rules directory are refused (79 steps, all as expected) | `local_integration`: one run, one host | `plan_row_scratch_run.py`; `plan-row-scratch-run.json` (the trust command carries `--codex-process-name no-such-process-name` because other codex processes ran on the host; recorded in the file) | | The after_sign_in filter and test, taken from the plan's own text, over six streams in the shape of Codex's JSONL command item: the good one (`rtk git status`, exit 0, completed) ends 0, and five negative fixtures (exit 127, completed with exit 1, declined, no rtk prefix, no command item) end 1 | `synthetic` | same file, the six after_sign_in steps | | The after_sign_in check with a real fresh `codex exec` and a signed-in launcher | **not run** | untested boundary: the host step after the sign-in; the earlier arms R1 and R2 of `codex_hook_qual.py` showed the same prefix through the gateway route | | `--check` and the review, with local stand-ins for the app-server and for `rtk --version` and `rtk config` (`FakeRtk`): `--check` 0 when trusted and no rule file is exposed, 5 untrusted or modified, 4 none matches, 6 trusted beside an exposure; no edit and no backup; `--apply` refused with exit 2 before the app-server starts (no `AppServer` call, no backup, no write) for every counterexample above, a reviewed file with one changed byte, another rtk version, a failing rtk, config or trust-list command, a configured prefix, a trusted filter, the override variable or a user filter, a missing or malformed reviewed list, an unreadable file, and 29 near-misses of the allow-only grammar (a prompt line, CRLF, a tab, an escape, a non-ASCII byte, a justification, alternatives, a second statement, ...); accepted: the reviewed bytes under another name, Codex's and hcom's allow-only files (hcom's rebuilt from its installer's source lists), empty and comment-only files, tokens that merely contain the letters `rtk`; a listing in which an entry vanished, an iteration or an open raised, or a path that is a file is refused, while a symlink, directory or named pipe is not loaded (the pipe is never opened) | `synthetic` (fake app-server, fake rtk, fake `scandir`) | `tests/test_codex_hook_trust.py` (81 tests) | | The reviewed entry rests on the real rtk 0.51.0: the derived heads (`rtk-rewrite-heads.json`, 142 from the 94 RULES patterns and 62 builtin TOML filters, the wrappers and the env prefix) agree with the binary: a positive control for each of the 94 patterns, 13,041 scanned commands (1,449 names, 9 argument shapes) with 623 rewrites and none outside the heads, 67 wrapper prefixes in front of `git status` (24 rewritten, all led by a head), 8,884 spellings of the 101 literal heads with 480 rewrites and none outside the heads; and the entry's own first tokens `hcom` and `uvx` in 22 spellings and 8 shapes (352 commands) are never rewritten. The derivation had 101 gaps in the first scan (the `r#"..."#` sbt rule, the TOML filters, `\b` continuations such as `gcc-13`), now 0. Review evidence, not a gate: the tool reads neither file | `local_integration` (the scan can falsify the review, not prove it) | `derive_rtk_rewrite_heads.py`, `check_rtk_rewrite_heads.py`; `rtk-rewrite-heads-check.json` | | The tests discriminate: 63 mutants (listing: a `FileNotFoundError` on an entry or the iteration or an open error read as no rules, the extension before the file type, a followed symlink, a named pipe loaded, any name ending in `rules`, an unsorted listing; grammar: a prompt rule, a quote, a backslash, a tab or CR in a token or comment, trailing text, `any` for `all`, no empty lines, any comment, spacing, an empty pattern, an allow rule that holds `rtk` in four variants (a token after a wrapper, whole-token only, inside a shell-script token, only a leading token); the reviewed list: a malformed, uppercase or over-long hash, a malformed list read as empty or not blocking, the list read with no rule file; the decision: every file reviewed, every file allow-only, an unreadable file not blocking, rtk asked for allow-only files; rtk conditions: the version gate, exit statuses, the `Config:` line, transparent prefixes, the trust list (ignored, exit status ignored, matched loosely), the override variable (ignored, only the value 1), user filters, the schema line, comments, an unreadable or non-UTF-8 filters file, no rtk, no PATH lookup, telemetry, a terminal, no time limit; the flow: `--apply` not refusing, the flag accepting nothing or not accepting, `--check` ignoring an exposure or answering 6 before 5, `--rtk` not passed, no dry-run note) each fail at least one test; the tests were written after the code, so this is the check that they are not vacuous | `local_integration` | `rule_review_mutation_check.py`; `rule-review-mutation-check.txt` | | Codex applies the hook's rewrite before approval and matches execution rules on the rewritten words: with a rule forbidding `git push` and one prompting `git commit`, `git push` is `forbidden` and `git commit` is `prompt`, while `rtk git push` and `rtk git commit` get no decision (each answer needs exit 0 and a valid response) | `local_integration` (Codex's own evaluator, codex-cli 0.159.3) and `source_review` for the dispatch order at both pins | `plan-row-scratch-run.json`, `execpolicy_check`; the decision record's addendum | | rtk 0.51.0 rewrites mutating commands: 36 of 55 measured, among them `git push`, `git commit`, `gh pr merge`, `docker run`, `pip install`, `rsync --delete`; 19 are not (`git reset --hard`, `rm -rf`, `npm publish`, `terraform apply` and others); the five `exclude_commands` change none of these | `local_integration`: one run, one host | `rtk_behaviour_probe.py`; `rtk-behaviour-probe.json`, `codex_hook_check` | | A live Codex session with a forbidding execution rule and the hook active | **not run** | untested boundary: derived from the dispatch order and the evaluator above; project and managed rule layers are not visible to the tool; `RTK_TRUST_PROJECT_FILTERS` is checked in the tool's own environment, not in the one Codex runs the hook in, and a filter trusted after the review shows at the next `--check` (705g P1-2: a point-in-time check cannot establish what a global hook does in every working directory; the exclusions of alternative (j) are the enforced hardening and are not enforced here); the reviewed list covers rtk's default hook configuration of one version; hcom's installer itself was not run (its file is rebuilt from its source) | | `install-plan.json`, `install.sh` and `accept.sh` agree | `local_integration` | `check_plan.py`: 80 rows, 163 commands, 101 acceptance entries (with #704's rows) | | The generated handbook and its receipt follow the edited recipe, and the convergence record stays valid without a re-pin | `local_integration` | `build_new_wsl_handbook.py --check` 0; `test_new_wsl_handbook` OK; `validate_convergence.py --all-recorded` 0 | | The user's yes for the pointer and the standing rule | relayed, not heard | the decision record's addendum | ### Local commands run ``` $ python3 scripts/validate.py ; python3 scripts/validate_convergence.py --all-recorded exit 0 ; exit 0 $ python3 -B evidence/artifacts/new-wsl-install-plan-20261002/check_plan.py OK: 80 rows ... 163 commands and 101 acceptance entries agree with the scripts $ python3 -B scripts/build_new_wsl_handbook.py --check exit 0 $ python3 -B -m unittest <24 modules that name a changed file> Ran 1028 tests ... OK (skipped=2) (17 modules that name a changed file, on the merged head; 1522 tests in 24 modules before the merge) $ python3 evidence/artifacts/token-stack-fresh-session-e2e-20261004/plan_row_scratch_run.py <outfile> 79 steps, all as expected $ python3 evidence/artifacts/token-stack-fresh-session-e2e-20261004/rule_review_mutation_check.py 63 mutants, survivors: [] $ gitleaks detect --log-opts="4c897418f..HEAD" no leaks found ``` ### Decision record `docs/decisions/2026-10-04-codex-rtk-hook-qualified.md` gains the 2026-10-05 addendum (the relayed answer, its effect, and the one-command undo: `rtk init -g --codex --uninstall` removes the hook, `RTK.md` and the pointer together), its Residuals sentence now says the user approved the pointer edit, and a second addendum, "approval and execution rules": five sourced findings (item 5 corrected: approvals off do not switch execution rules off, and NativeStack2604 has no user-layer `rules` directory), the decision (the hash-list rule above, why three analysing versions failed, the reviewed list and its conditions, **the pin-move rule: a move of the rtk pin re-reviews the list**, tied to the RTK row of `docs/token-efficiency-stack.json`), the evidence, the limits and what would overturn it, the review rounds, and ten alternatives (an `exclude_commands` read-only limit, whose probe shows `git -C /tmp push` still rewritten; a never-rewrites-mutating proof; holding the hook out; shipping `rtk` rules; refusing on any rules file; the probe-and-sample review; a wrapper hook; derived heads with a parser; a purpose-written parser; (j) enforced `exclude_commands` entries for `hcom` and `uvx hcom`, measured and not enforced, with why). ### Review threads Three Codex-connector threads, each answered with its fix and source in the thread and resolved at head 4739ea8: after_sign_in exit status (`accept.sh`), the profile's "never runs `rtk init`" statement (`install.sh` L446), and approval classification against the rewrite (`install.sh` L448). Cross-family reads of the guard, four versions: 4739ea8 (refuse on any rules file) was read by 705d (Sol, through 5f: P1 a failed listing of the rules directory read as no rules, P2 the refusal came after the app-server started, P2 the scratch run accepted a failed evaluator) and job-071 (through the command center: P2 the same listing hole, P2 rules reported only for a hook still to be trusted, P3 the claim that approvals-off hosts consult no rules). `e6321c0b8` (a behavioural review with `rtk hook check` and `codex execpolicy check`) was read by 705e: three P1s that sampling cannot close (an entry-level `FileNotFoundError`, a `prefix_rule` nested in a `host_executable` argument, extensions of a prefix rtk leaves alone). `c771da9eb` (heads derived from rtk's source and a literal-only `ast` reader) was read by 705f: five P1s (a raw CR in a Starlark string, a rule on the whole shell argv, PHP tool suffix spellings, user filter regexes with a second branch, Unicode word boundaries) and a P2 (`DirEntry.is_file()` swallows a missing entry). `3a2bb97ba` (content `2d052a659`) replaced the analysis with the hash list on 5f's recommendation and added one exclusion found while checking the design against the real evaluator: an allow rule that names `rtk`. 705g (Sol, through 5f, at `3a2bb97ba`) found two P1s and no P2 in it: (P1-1) the exclusion refused only a whole token `rtk`, but Codex evaluates an assignment-prefixed script as one token of the shell argv and the hook inserts `rtk` inside it, so `["/bin/bash", "-lc", "FOO=1 rtk git push origin main"]` allowed the rewritten script and not the original; (P1-2) project filters, which rtk loads from the hook's working directory and which `rtk trust --yes` or `RTK_TRUST_PROJECT_FILTERS=1` with a CI variable activate, rewrote `hcom kill luna` out of the reviewed forbid rules while the reviewed hash still passed. This head's repair (`b7f758768`, registry `1b8d4c074`; merge of main `9080bc324`) repairs both in one bounded round: `rtk` is refused anywhere in an allow rule (regression cases, a whole-token mutant, a scratch home); the reviewed-file conditions also need an empty `rtk trust --list` and no `RTK_TRUST_PROJECT_FILTERS` (the reviewer's second option: classify the exposure so that it needs `--allow-exec-rules`), with rtk's exclusions measured as the enforced alternative and recorded, not enforced. It also follows #713's repair round 2 (the entry holds `ac6254ee…`, the 7fbe586 bytes; only comments and justification strings differ from 5f295f2). 705h (Sol, through 5f, at `14a8b119f`; verdict `verdict-705h.json`) **ACCEPTed**: no P1 and no P2. Both 705g P1s were verified fixed against the real binaries (a lowercase `rtk` anywhere in an allow line is refused, and the whole-token mutant dies with 36 test failures; the trust list and the override variable classify both project-filter activation routes, `--apply` exit 2 and `--check` exit 6, with the default and the XDG layouts, and `rtk trust --list` detected the filter from another directory), and no additional bypass was found in the pinned rewrite paths and inputs. Two boundaries stay not verified and are recorded as residuals: runtime coverage (codex-cli 0.159.3 and rtk 0.51.0 were probed, rust-v0.160.0 was read from source, no fresh model session) and the point-in-time and environment limits (the classification observes the current user-layer rule bytes and the tool process's RTK environment and trust store; no atomic snapshot across listing, loading and the grant, and no enforcement after a later change of rules, filters, HOME or XDG paths or the hook's environment; project and managed Codex policies are outside its scope). ### Merge of main `9080bc324` merges main `4af7417b7` (#720 re-pin to v2026.10.05.1, #712, #696, #724) into the branch. Conflicts were only in the generated and hot files (`docs/new-wsl-handbook.md`, the handbook receipt, `manifests/evidence.json`), resolved by the hot-file protocol of `docs/lanes.md`: main's copies, the branch's 44 registry entries re-registered from the pre-merge snapshot, the handbook regenerated with main's generator (#724), the receipt digests refreshed. Carry check: of every file this PR changes against its old base, only `docs/new-wsl-handbook.{json,md}`, the receipt and `manifests/evidence.json` differ between the pre-merge head `1b8d4c074` and the merge head (`git diff 1b8d4c0 <merge head> -- <the PR's files>`); the 705g repairs are `3a2bb97ba..1b8d4c0`. The merged tree failed two tests of main's new rules, fixed in `a7990f56a` (registry `14a8b119f`): the F9 unit says `tools/adoption/exec_rules_reviewed.json` was added after `v2026.10.05.1` (main's unreleased-path marker rule, #720), and the receipt's frozen profile digest is the branch's (the merge took main's receipt); the handbook and the receipt follow. ### Host evidence No files under `evidence/hosts/` change. The scratch run used a scratch HOME; the live `~/.codex` and rtk configuration were neither read nor written. ### Checklist - [x] No new or changed GitHub Actions or workflows. - [x] No secrets printed, logged or committed (gitleaks above). - [x] No new paid hosting, subscription or billing surface. - [x] Peer-owned untracked files and worktrees preserved. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…pushes, no PR runs; the user's 2026-10-05 decision) (#711) ### Scope - What this PR changes: it makes macOS CI advisory. `validate-macos`, `bootstrap-macos` and `bootstrap-macos-brew` no longer run on pull requests, and they leave the required checks. They still run nightly (06:47 UTC), on filtered main pushes and on manual dispatch, so a macOS regression is fixed forward. This follows the user's decision of 2026-10-05 (~01:50Z, an AskUserQuestion answer): "Advisory only". - Base commit: `5df0e0ede` - Lane: `lane:shared`. docs/lanes.md's required-context count goes from eight to seven, so the trading-custody owner must ACK. - Owned paths touched: - `.github/workflows/adoption-bootstrap.yml`; - `.github/main-ruleset.json` (the committed target); - `catalogs/foundation/automation.json`, `docs/github-automation.md`, `docs/lanes.md`, `adoption/platforms/macos-arm64.md`; - five test modules; - the decision record. The live ruleset 23739774 was already changed by the coordinator on 2026-10-05, with an API read-back: `validate-macos` is no longer required. This PR brings the committed target and the docs into line with it. ### SOTA sources - GitHub Actions workflow syntax, `jobs.<job_id>.if`: https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idif (github/docs at 2bd66de8cea336061c9ea060c9b37385136e6ab3) - Events that trigger workflows, `schedule`: https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#schedule (same pin) - Rulesets, required status checks: https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/available-rules-for-rulesets#require-status-checks-to-pass-before-merging - #677's retained macOS measurements, and #699's macOS catch (landed as 5df0e0e), which shows why the nightly runs stay. ### Evidence-class table | Claim | Evidence class | Command / receipt | | --- | --- | --- | | The new workflow skips all three macOS jobs on `pull_request`, and the old one runs them | local_integration | The PR-event test fails the original workflow (133 failing subtests, exit 1) and passes the new one (exit 0) | | The committed ruleset target equals the live seven-context ruleset | source_review | `.github/main-ruleset.json`; live read-back from `gh api repos/.../rulesets/23739774` on 2026-10-05 | | The workflow has no security findings | local_integration | `zizmor --offline .github/workflows/adoption-bootstrap.yml`: no findings (5 suppressed) | ### Local commands run ``` $ python3 -m unittest tests.test_workflow_hardening tests.test_adoption_bootstrap_macos tests.test_github_automation_practice tests.test_codex_broker_reaper tests.test_shell_parser_ci tests.test_merge_guard_doc 388 tests OK (36 skipped) [GPT builder job 064] $ zizmor --offline .github/workflows/adoption-bootstrap.yml No findings to report (5 suppressed) [coordinator] $ python3 scripts/validate.py status passed (10,044 hashed files) [coordinator, after the registry commit] $ python3 merge_tree_landing_check.py origin/main HEAD LANDABLE ``` actionlint was not on PATH in the builder's sandbox; zizmor ran instead. ### Decision record `docs/decisions/2026-10-05-macos-ci-advisory.md`: the user's decision, #699's portability catch, the fix-forward path and the overturn rules. Built by GPT bounded job 064 (GPT-6.1 Sol at max). Review chain: - Claude read: 4 P2 and 6 P3, all fixed by the GPT repair round (5f6eb31). - Claude delta read: all ten confirmed fixed; three new P3s, fixed by the coordinator (7e4d69f), along with a fresh ruleset read-back (enforcement active, strict false, merge methods squash only). - Registry last (331d008). This PR lands after the v2026.10.05 re-pin, because adoption/** is on the release window's hold list. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Scope
287514581(which holds the squash of New WSL builder: token layer wired by default on the owner's 2026-10-04 directive #684 and Codex rtk hook: a narrow trust tool, the qualification evidence and its decision record (first of two) #701). Restacked from the stack ona3045d629: the content commits cherry-pick without a conflict and the registry is redone last on each restack; main moves often, so the lander may find the hot file to redo once more. The command center decided that NativeStack follows NativeStack2604 on the user's clean-install directive (2026-10-04, quoted in the record). This PR does three things:tools/adoption/codex_config_prune.py, which deletes the[features]keys thatcodex features listreports asremoved(NativeStack'splugin_hooks = truetoday) through Codex's own config writer;287514581(main after Codex rtk hook: a narrow trust tool, the qualification evidence and its decision record (first of two) #701). Review history: five cross-family reads of the retirement matcher, closed by the command center's closing read (job-059) at748cbe418, whose content this head carries plus a docs-only commit that records the closing read's two residualslane:foundationadoption/templates/{claude.settings,codex.config,project.codex.config}.template.*,adoption/templates/codex.AGENTS.template.md,adoption/scaffold/AGENTS.md,adoption/mcp/claude-user.json,adoption/hooks/claude/{README.md,held-out-hook-entries.json},tools/adoption/{install_claude_profile,apply_claude_settings,codex_config_prune}.py,adoption/new-wsl/**(map, additions, generated Codex block),adoption/bootstrap.md,docs/token-session-handbook.md,docs/decisions/2026-10-04-*, the tests, andmanifests/evidence.json(last commit,registry:).SOTA sources
SessionStartandSubagentStart; read 2026-10-04). The carriers are our own adaptation, not an upstream feature.codex features listof the installed binary, and openai/codexrust-v0.160.0,codex-rs/features/src/lib.rsL1474-L1479 (plugin_hooks:Stage::Removed,default_enabled: false). Writer:config/batchWriteofcodex app-serverwithexpectedVersion, astools/adoption/apply_codex_lane.pyalready uses it (its rollback deletes keys withvalue: null).rust-v0.157.1,codex-rs/codex-mcp/src/mcp/mod.rsL89-L98 (anapprovemode auto-approves even underapproval_policy = "never").8f7b34abe16fb459e0bf1c04747d584216dfe32e:README.mdL119-L122 (claude mcp add -s user jcodemunch jcodemunch-mcp) and L225 (the anonymous savings counter and its default index path),CONFIGURATION.mdL229-L233 andSECURITY.mdL311 (JCODEMUNCH_SHARE_SAVINGS=0).init; the registration here does not, and was not measured.Evidence-class table
~/.claude/settings.jsonremoves exactly one hook, the SubagentStart carrier; a second merge changes nothing;keep_held_out=Truekeeps itlocal_integration(real input, in-memory merge on this host, with the host's resolved home)176ee5a6c(the same content as7edb91e3ehere), not retained as a receipt; stated indocs/decisions/2026-10-04-claude-template-holds-out-token-lane-carriers.md, decision 3codex_config_prune.pyon NativeStack's live config (dry run, realcodex features list) namesfeatures.plugin_hooksand nothing elselocal_integration(real input, no write)python3 tools/adoption/codex_config_prune.pyexit 0--applyagainst the realcodex app-server(codex-cli 0.159.3) on a scratch copy of NativeStack'sconfig.toml: exactly theplugin_hooks = trueline was removed, read back throughconfig/read, a 0600 backup was written, a second dry run found nothing, and the live file kept its hashlocal_integration(real upstream binary, scratch copy, no write to the live host)--codex-process-name) because five other codex processes were running, which the live apply must not do; recorded in the decision record, decision 5--applypath with its refusals (backup then delete through the writer and read back; a running codex refuses before any write; a failed write is reported with the backup; a key the writer still reports is a failure; two backups in one second do not collide)synthetictests/test_codex_config_prune.pywith a fake codex and a fake app-serverrender_config.pywrites there. The renderer substitutesHOMEas written, so that text may be any spelling of the settings file's home (a trailing slash, a.or..segment, a doubled or leading double slash, a symlink alias): it counts when it is an absolute path with no control character, quote,$, backtick or backslash whose realpath is the home's, and the home is the resolved<home>/.claude/settings.jsonor nothing is retired. Every other hook is kept (five cross-family reads found a defect in each earlier matcher or in what it was given: any shell word naming the path; the first non-option word and shlex tokens; an anchored pattern, a recognised grammar; trimming, a fallback home and two sequential replaces; and, in the fifth, two ways a carrier was kept because the renderer writes the home as given)local_integrationSHIPPED_CARRIER_COMMANDS, four fixed shapes with a hole whereHOMEis;HeldOutHookTests(53 tests; the fourth and fifth rounds' were written first: 78 failures and one error, then nine failures): the four shipped strings with their sources, every shipped string retired as shipped and as rendered, no whitespace around a command tolerated (nine paddings), a mutation test (6,908 single-character edits, none retired), the second read's 25 shapes and the third read's 30 all kept (the pattern matcher retired 26 of the 55), another host's home does not retire, the carriers rendered by the realrender_config.render_onewith seven spellings of one home retire, a carrier rendered for a symlink alias or for the real home retires through either path, spellings that name another directory or that the shell expands (.../$X/.., a substitution, a backtick, a backslash, a quote, a newline, a no-break space) are kept, a target that is not a home's.claude/settings.jsonretires nothing; the reviewer's own repro file for the fifth read's two families passes 6 of 6 against this head; the record counts the second to fifth repairs as extra rounds past the bounded loop, the fifth being the final onecodex:config_group:otel.metrics_exportercitedcodex.config.template.toml:269, which the jCodeMunch lines moved to 291 (fixed in66ba4c45e;test_upstream_surface_watchpasses, 110 tests). The other nine (8 intest_token_e2e_grader, 1 intest_windows_terminal_defaults) are not caused by this PRlocal_integration33b236ba1(the head before the citation fix); the six related modules (apply_claude_settings, install_claude_profile, codex_config_prune, new_wsl_client_config, ecosystem_manifest, adoption_docs_consistency) ran 447 tests, all passing (5 skipped), at every round and on the rebased headtest_a_settings_file_names_its_own_homecompared the literal/home/examplewith the settings file's resolved home, and macOS resolves/hometo/System/Volumes/Data/home; fixed inb6a152785by comparing withresolve()(the code is unchanged); the artifact held no other failurelocal_integrationvalidate-macosjob's uploaded logfull-suite-macos.log(1 failure, 1,412 skipped); the module passes (53 tests) after the fixtests.test_token_e2e_grader,tests.test_windows_terminal_defaultsandtests.test_upstream_surface_watchrun on clean main43005a9f8(a detached worktree): 776 tests, 9 failures, 6 skipped; the surface-watch module passes theresource_reviewRESULTS.md--applyon the live file,codex mcp add jcodemunch)Local commands run
Decision record
docs/decisions/2026-10-04-claude-template-holds-out-token-lane-carriers.md(decisions, the NativeStack host steps, alternatives, what would overturn each); addenda in2026-10-04-new-wsl-clean-default-holdouts.mdand2026-10-04-new-wsl-jcodemunch-user-scope.md.Host evidence
No files under
evidence/hosts/change. Applying the Codex AGENTS template's changed top-rule line (jcodemunchshares serena's lane) to~/.codex/AGENTS.mdis an instruction-file edit that the command center applies after this PR's cross-family read; it reported the user's yes at 20:40Z.Checklist
🤖 Generated with Claude Code