Repository navigation
Landscape-sweep OmniRoute lane: exact context-mode read rules for the pinned skills; keep the gateway key out of model shells - #393
Conversation
…oots The OmniRoute smoke (wf_6576675c-0d6) passed every harness check except one. Both GPT-6 workers reported skills_used: []. Each tried to load its pinned skills (search-first, iterative-retrieval, verification-before-completion, supply-chain-risk-auditor, fp-check) with context-mode's ctx_execute_file. context-mode 1.0.169 refuses paths outside its project directory, here the runner's <work-dir>/empty, unless a Read(...) allow rule in <project>/.claude/settings.json names them (build/security.js evaluateProjectContainment and readToolPermissionPatterns, issue #852). The native lane's GPT-6 had reported them used. build_args.py now writes <work-dir>/empty/.claude/settings.json for the gateway lane. It allows Read on Codex's user skill roots only, $CODEX_HOME/skills and $HOME/.agents/skills (codex-rs/ext/skills/src/host_roots.rs at rust-v0.157.1), as absolute globs, since context-mode matches literally. staged.json records the roots symbolically. A native restage removes the file. Codex reads no .claude/ files, so no Codex project layer is added. The README records the rule and the 2026-09-27 web-search measurement: - /v1/alpha/search is 404 at a58000c7; - upstream PR #13788 answers from OmniRoute's search registry (duckduckgo-free when keyless), not OpenAI's hosted search. The stale note about the default worker profile goes, since #389 put it on main. The failing control: the new test errors against main's build_args.py (no settings file). Tests: tests.test_saturation_ledger + tests.test_landscape_sweep_harness, 167 run, OK (3 skipped). validate.py and build_ecosystem.py --check passed. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_012qwXNtFyUhY5x2dtrm7tG5
… model shells The GPT-6 review of ec9f071 returned needs_changes with one medium finding. context-mode 1.0.169 matches an allow rule against the raw path as well as the resolved one (security.js evaluateFilePath), so the <root>/** rules also admitted <root>/../elsewhere, sibling paths under the root and outward symlinks. The claim of "there and nowhere else" did not hold, and the test only checked serialization. - The lane now gets one exact Read(...) rule per file under $HOME/.agents/skills. That is where install_skills.py puts the pinned skills; the lane's $CODEX_HOME/skills holds only Codex's .system cache. Symlinked directories are followed, each real directory is listed once, and a path containing * or ? is left out. context-mode escapes and anchors every other character, so an exact rule matches only its path. - staged.json records the symbolic root and the file count. - New behavioral test: test_context_mode_reads_only_the_listed_skill_files runs context-mode's own readToolPermissionPatterns and evaluateProjectContainment on the staged file (set CONTEXT_MODE_SECURITY_JS to an installed build/security.js). Listed files pass; a sibling, an outside file and two .. traversals do not. Its control shows a <root>/** rule admitting the traversal. The first revision fails it (sibling and traversal allowed). Also, a residual the gateway owner found while reviewing their Codex templates: Codex 0.157.1 applies its default *KEY*/*SECRET*/*TOKEN* excludes only when ignore_default_excludes is false, and that defaults to true (codex-rs/config/src/shell_environment_policy.rs:136). A real gateway key would therefore reach model-run commands. The lane config now sets [shell_environment_policy.filters] OMNIROUTE_API_KEY = "exclude" (codex-rs/protocol/src/config_types.rs:222-230). Live probe on a lane staged from this revision, with a synthetic non-placeholder key through the keyless gateway: - GPT-6's shell reported the variable absent; - ctx_execute_file read fp-check/SKILL.md; - ctx_execute_file refused ~/.agents/skills/../../code/.../README.md. Tests: 168 run, OK (3 skipped), with CONTEXT_MODE_SECURITY_JS set. validate.py and build_ecosystem.py --check passed. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_012qwXNtFyUhY5x2dtrm7tG5
…_execute's reach The GPT-6 re-check of 45a9d77 confirmed the exact rules reject traversal, sibling and outside paths. It admits listed files, references and symlinked skill directories. The key filter removes the variable from model commands while env_key still reaches the provider. One case stays open, and it is upstream matcher behavior. context-mode 1.0.169 turns backslashes into slashes before matching (security.js evaluateFilePath) but its executor opens the literal Linux name. A planted symlink named <home>/.agents\skills\demo\SKILL.md therefore passed and read an outside file. No rule can exclude it, since deny matching normalizes the same way. Per the bounded review loop this is recorded as a residual, not fixed: - no such file exists; - creating one takes write access that ctx_execute already has. The README and comments also state the wider fact the review surfaced: the #852 boundary limits ctx_execute_file only, while ctx_execute (left enabled by the stack-worker profile) runs code with the user's own file access outside Codex's read-only sandbox. Tests: 168 run, OK (3 skipped), with CONTEXT_MODE_SECURITY_JS set. validate.py and build_ecosystem.py --check passed. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_012qwXNtFyUhY5x2dtrm7tG5
|
GPT-6 cross-family review, two rounds (
What 45a9d77 changes:
Why the r2 case is a residual and not a fix: no rule can exclude it, because deny matching normalizes the same way. No such file exists, and creating one needs write access that r2 confirmed the rest:
Live probe on a lane staged from 45a9d77, with a synthetic non-placeholder key through the keyless gateway:
Tests at 4f6cfd8: 168 run, OK (3 skipped), with Integration: smoke 2 ( 🤖 Generated with Claude Code |
|
Integration: smoke 2 passes.
Results:
Attempts are retained in the private work directory:
🤖 Generated with Claude Code |
…ll-roots-20260927
…instructions (AGENTS.md) The OmniRoute lane (build_args.py --gpt6-provider omniroute) runs Codex with CODEX_HOME set to a lane-local home holding only config.toml and the stack-worker profile. Codex reads $CODEX_HOME/AGENTS.md as global instructions, so the lane's GPT-6 got neither the repository's top rule nor RTK's instructions, which the native lane's workers get from ~/.codex/AGENTS.md. That is one cause of the low RTK prefix rate measured on GPT-6 lanes (M6c). - build_args.py: codex_user_instructions() imports the checkout's tools/adoption/apply_codex_lane.py and returns agents_block(), the managed block of adoption/templates/codex.AGENTS.template.md (the top rule, rtk-ai/rtk v0.50.0 hooks/rtk-awareness-full.md verbatim, the RTK exactness exceptions). stage_lane_home writes it as codex-home/AGENTS.md and records agents_sha256 in staged.json. No copy of the text. - tests: the staged AGENTS.md equals the template bytes and its sha256 is recorded; red before the change (no AGENTS.md). - README (harness) lists the third file. recipes/README.md's OmniRoute "Workers" bullet no longer says the lane has no key filter (#393 added [shell_environment_policy.filters]) and names AGENTS.md. Tests: 10 covering modules, 719 run, OK. validate.py passed. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…instructions (AGENTS.md) (#418) * Landscape sweep: the gateway lane home carries the host's Codex user instructions (AGENTS.md) The OmniRoute lane (build_args.py --gpt6-provider omniroute) runs Codex with CODEX_HOME set to a lane-local home holding only config.toml and the stack-worker profile. Codex reads $CODEX_HOME/AGENTS.md as global instructions, so the lane's GPT-6 got neither the repository's top rule nor RTK's instructions, which the native lane's workers get from ~/.codex/AGENTS.md. That is one cause of the low RTK prefix rate measured on GPT-6 lanes (M6c). - build_args.py: codex_user_instructions() imports the checkout's tools/adoption/apply_codex_lane.py and returns agents_block(), the managed block of adoption/templates/codex.AGENTS.template.md (the top rule, rtk-ai/rtk v0.50.0 hooks/rtk-awareness-full.md verbatim, the RTK exactness exceptions). stage_lane_home writes it as codex-home/AGENTS.md and records agents_sha256 in staged.json. No copy of the text. - tests: the staged AGENTS.md equals the template bytes and its sha256 is recorded; red before the change (no AGENTS.md). - README (harness) lists the third file. recipes/README.md's OmniRoute "Workers" bullet no longer says the lane has no key filter (#393 added [shell_environment_policy.filters]) and names AGENTS.md. Tests: 10 covering modules, 719 run, OK. validate.py passed. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> * Landscape sweep #418 repair: restore the whole import path around the installer import GPT-6 review of 2854514 (P3): importing tools/adoption/apply_codex_lane.py runs its line 69, which prepends the repository root to sys.path, so sys.path.pop(0) removed that root and left tools/adoption ahead of the harness. codex_user_instructions() now snapshots and restores the full sys.path. New test (installer unloaded first): red without the fix, green with it. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> --------- Co-authored-by: Scout <scout@local> Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
Resolves the independent review's findings on 68c4a8d and records the upstream tests that this host cannot run. - Line citation: at openai/codex rust-v0.157.1, core/src/session/session.rs calls install_initial_mcp_runtime(...) on L1860-1866 and awaits it with `.await?` on L1867. The template, two test comments and the decision record now cite L1860-1867. - Decision record, Problem: "Both exceed the grace" overstated the probe. The stdio probe times each server from its own launch, while the grace ends 1 s after Codex's first tool-list build (tool_catalog.rs L252-258). In the race test's failing-first runs the first request left 1.63 s and 1.69 s after codex started. Only serena's slower starts (up to 2.24 s) are shown past that point. Codebase-memory's absence on the gateway route rests on the relayed peer measurement. - Race test: the deferred-tool guidance proves only that some deferred tool was present. The fake endpoint now answers the first request with a code-mode exec call that lists ALL_TOOLS by name, as upstream's core/tests/suite/code_mode.rs run_code_mode_turn_with_builder (L264-293) and code_mode_exports_all_tools_metadata_for_namespaced_mcp_tools (L7133-7175) do. It reads the list from the second request's custom_tool_call_output. With the profile the first turn lists mcp__serena__fixture_serena and mcp__codebase_memory__fixture_codebase_memory. The relaxed control lists neither, and the two lists differ by exactly those names. - Failing-first attribution: the native apply test's before run (real codex, rehearsal failed on codebase-memory) is the behavioural evidence. The synthetic ApplyFlowTests/RequiredStartTests before run failed with AttributeError on the three helpers that did not exist yet. - Gateway profile template comment: since #393 the landscape sweep's lane home writes [shell_environment_policy.filters] OMNIROUTE_API_KEY = "exclude" (build_args.py L259-261 and L270-271). The template comment and the lane test's comment now say so, and the lane test checks that the staged filter equals the profile's. - Decision record: lists the relevant upstream tests at rust-v0.157.1 and records them as not run, because this host has no Rust toolchain. Evidence (codex-cli 0.157.1, TMPDIR=/var/tmp/claude-codex-startup): - strengthened race test: exit 0, Ran 1 test, OK. On a copy of the tree whose template lacks the two required lines: exit 1, FAILED (failures=3), first request at 1.69 s, and the two first turns' tool lists were identical. - tests.test_codex_worker_lane, tests.test_landscape_sweep_harness and tests.test_codex_lane together: exit 0, Ran 253 tests, OK (skipped=12). manifests/evidence.json is not re-registered here; the coordinator does that on replay. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
What
The landscape-sweep OmniRoute smoke (
wf_6576675c-0d6, harness at b9abcc5) passed every harness check. Both GPT-6 jobs wereok, the copy check matched 2/2, and nothing was lost or deviated. One gap remained: both GPT-6 workers reportedskills_used: [].Their events show why:
ctx_execute_file:search-first,iterative-retrieval;verification-before-completion,supply-chain-risk-auditor,fp-check.~/.agents/skills.<work-dir>/empty. ARead(...)allow rule in<project>/.claude/settings.jsonis the only way past it (build/security.jsevaluateProjectContainmentandreadToolPermissionPatterns, issue Pin vendor skills for CC lane messaging, evals and logs #852).What
build_args.py --gpt6-provider omniroutenow does:<work-dir>/empty/.claude/settings.jsonwith one exactRead(...)rule per file under$HOME/.agents/skills, whereinstall_skills.pyputs the pinned skills. Codex lists user skills from there and from$CODEX_HOME/skills(codex-rs/ext/skills/src/host_roots.rsat rust-v0.157.1); in the lane, the latter holds only Codex's.systemcache.*or?is left out.<root>/**rule would admit<root>/../elsewhereand siblings (GPT-6 review r1).staged.jsonrecords the symbolic root and the file count, and no host path..claude/files, so no Codex project layer is added.[shell_environment_policy.filters]excluding the gateway key variable. Codex 0.157.1 defaultsignore_default_excludesto true, so a real key would otherwise reach model-run commands.Residual, recorded in the README. context-mode normalizes backslashes before matching, while its executor opens the literal Linux name. A file whose name holds backslashes that normalize to a listed path would pass too (GPT-6 review r2). No rule can exclude it. The #852 boundary limits
ctx_execute_fileonly:ctx_execute, which the stack-worker profile enables, runs code with the user's own file access, outside Codex's read-only sandbox.The README also records the 2026-09-27 web-search measurement:
/v1/alpha/searchis 404 ata58000c7.duckduckgo-freewhen keyless), not OpenAI's hosted search.The stale note that the default worker profile was still to come is removed, since #389 merged it.
Verification
CONTEXT_MODE_SECURITY_JS=<installed build/security.js> python3 -m unittest tests.test_saturation_ledger tests.test_landscape_sweep_harnessgives 168 run, OK (3 skipped). The behavioral test runs context-mode's real matcher and skips without the variable.python3 scripts/validate.pyandpython3 scripts/build_ecosystem.py --check: passed.fp-check/SKILL.mdreads through its exact rule;..traversal is refused.wf_e4dee61f-e66) runs with the SubagentStart token-lanes hook active. The result is posted before merge.SOTA sources
build/security.js:evaluateProjectContainment,readToolPermissionPatterns,evaluateFilePath,fileGlobToRegex.codex-rs/ext/skills/src/host_roots.rs:96-113;codex-rs/config/src/shell_environment_policy.rs:136;codex-rs/protocol/src/config_types.rs:222-230;codex-rs/protocol/src/shell_environment.rs.🤖 Generated with Claude Code
https://claude.ai/code/session_012qwXNtFyUhY5x2dtrm7tG5