Skip to content

Landscape sweep: fail closed when the worker profile overlays an MCP server the lane config lacks - #411

Merged
seathatflowsinourveins merged 1 commit into
mainfrom
claude/sweep-profile-mcp-guard-20260927
Sep 27, 2026
Merged

seathatflowsinourveins merged 1 commit into
mainfrom
claude/sweep-profile-mcp-guard-20260927

Conversation

@seathatflowsinourveins

@seathatflowsinourveins seathatflowsinourveins commented Sep 27, 2026 •

Copy link
Copy Markdown
Collaborator

What

The landscape sweep's GPT-6 lane stages a lane-local CODEX_HOME:

  • config.toml holds the [mcp_servers.*] tables of the rendered codex.config.template.toml;
  • stack-worker.config.toml is the worker profile, copied verbatim.

Codex merges the profile over config.toml. A merged server table with neither command nor url is rejected as "invalid transport" (codex-rs config/src/mcp_types.rs:454-510 at rust-v0.157.1). So when the profile has a partial table (startup_timeout_sec, enabled_tools, env) for a server the host's rendered template does not define, -p stack-worker fails to load, and only at run time.

  • The shipped profile already overlays ai-memory, socraticode, headroom and context-mode.
  • token-efficiency-evidence-cards' pending codex-agents unit adds serena and codebase-memory. It asked this lane to confirm its staging covers that.

Change: profile_servers_without_base() lists such tables. Staging refuses with the server names before it writes the lane home. A profile table carrying its own command or url is a whole server and stays accepted.

Verification

  • New tests in OmniRouteLaneBuildTests:
    • an overlay for a rendered server stages;
    • an overlay for an undefined server fails closed. Red without the guard: exit 0, not 2.
    • a whole server stages;
    • the shipped profile stages for the example host, which also covers the pending serena and codebase-memory overlays.
  • python3 -m unittest tests.test_landscape_sweep_harness tests.test_install_claude_profile tests.test_token_lanes_subagent_start: 180 run, OK.
  • python3 scripts/validate.py: passed.

SOTA sources

  • openai/codex rust-v0.157.1, codex-rs/config/src/mcp_types.rs:454-510: the transport is selected from command, else url, else Err("invalid transport").
  • The profile merge behaviour, as token-efficiency-evidence-cards observed it: stack-worker.config.toml:39:1: invalid transport in a Codex home whose config.toml lacked user-scope serena and codebase-memory.

🤖 Generated with Claude Code

https://claude.ai/code/session_012qwXNtFyUhY5x2dtrm7tG5

…server the lane config lacks

stage_lane_home() copies the stack-worker profile verbatim beside a lane
config.toml that holds the [mcp_servers.*] tables of the rendered
codex.config.template.toml. Codex merges the profile over config.toml and
rejects any merged server table with neither command nor url ("invalid
transport", codex-rs/config/src/mcp_types.rs:454-510 at rust-v0.157.1), so a
partial profile table (startup_timeout_sec, enabled_tools, env) for a server
the host's rendered template does not define stops -p stack-worker from
loading at run time. The shipped profile already overlays ai-memory,
socraticode, headroom and context-mode, and a pending change adds serena and
codebase-memory (token-efficiency-evidence-cards' codex-agents unit).

profile_servers_without_base() finds such tables; staging now refuses with
the server names, before writing the lane home. A table that carries its own
command or url is a whole server and stays accepted.

Tests (test_landscape_sweep_harness.OmniRouteLaneBuildTests): an overlay for
a rendered server stages; an overlay for an undefined server fails closed
(red without the guard: exit 0, not 2); a whole server stages; the shipped
profile stages for the example host. 180 run across the harness, profile and
subagent-start modules, OK. validate.py passed.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_012qwXNtFyUhY5x2dtrm7tG5
@seathatflowsinourveins seathatflowsinourveins added the lane:foundation Foundation lane: Claude/Codex setup, hosts, memory, RAG, research, workers label Sep 27, 2026
@seathatflowsinourveins

Copy link
Copy Markdown
Collaborator Author

GPT-6 cross-family review of 07cc7b90: approve. (codex exec -s read-only -m gpt-6-astra, reasoning effort max)

No defects found.

Codex 0.157.1 loads the profile after the base, merges table keys recursively, and deserializes the merged configuration. Missing both command and url produces invalid transport. Per-file path resolution preserves partial tables when typed parsing fails, so the guard’s premise holds.

Verified nested env tables, quoted and escaped names, base-defined servers, and complete command/url definitions. The shipped profile stages and loads through the installed native client on the example host. Orphan staging exits 2 before creating lane-home or context settings files; the base-commit red control exits 0.

  • python3 -m unittest tests.test_landscape_sweep_harness: passed, 113 tests, 3 skipped.
  • python3 scripts/validate.py: passed.
  • Reviewed head matches 07cc7b90ce63ba1a342002c5ea01d768c408cee3; working tree remains clean.

🤖 Generated with Claude Code

@seathatflowsinourveins
seathatflowsinourveins merged commit acd366c into main Sep 27, 2026
27 of 28 checks passed
@seathatflowsinourveins
seathatflowsinourveins deleted the claude/sweep-profile-mcp-guard-20260927 branch September 27, 2026 16:13
seathatflowsinourveins pushed a commit that referenced this pull request Sep 29, 2026
…ks --required; move merge-guard mechanics to the decision record

The round-one verifier showed the documented statusCheckRollup filter prints superseded runs, so 'none failed' could not be judged with it (merged #389, #411 and #413 each show an earlier sota-sources failure and a later success on the final head). gh pr checks --required lists only the required checks with the latest run of each; measured exit 0 (all passed), 1 (one failed, none pending) and 8 (pending). The implementation-level citations move to the decision record, the stale-approval comparison is called an analogy, and 'today' leaves the dated record.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
seathatflowsinourveins added a commit that referenced this pull request Sep 29, 2026
…uire sota-sources (#430)

* docs/lanes.md: merge with the reviewed head pinned (--match-head-commit)

Strict up-to-date checks stay off and this User-owned repository cannot use
a merge queue (docs/decisions/2026-09-22-github-automation-closure.md), so
`gh pr merge <N> --squash --match-head-commit <SHA>` is the guard against
merging a head nobody reviewed (https://cli.github.com/manual/gh_pr_merge).
It was practised but not written down: grep for match-head-commit across
docs, recipes, adoption, examples and AGENTS.md returned nothing.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

* Decision record: close the sota-sources ruleset drift with dated GETs

The live ruleset 23739774 now requires the committed eight checks. Before-GET
21:00:04Z (seven, no sota-sources); PUT of .github/main-ruleset.json at main
e82e6be at 21:00:13Z; after-GET matches the committed file. Records the open
PRs affected at PUT time and the rollback.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

* docs/lanes.md merge guard: pin the checked and reviewed head read just before merge (B1, B2)

B1: <SHA> is the full 40-character headRefOid read with gh pr view <N> --json headRefOid,statusCheckRollup immediately before the merge, on which the review completed and every required check ran with none running or failed. The hot-file protocol's rebase or merge of main comes before that read; if main changed a shared hot file after <SHA>, merge main again, wait for the checks on the new head and pin it; the review carries over only if the reviewed files are byte-identical (GitHub's stale-approval rule, available-rules-for-rulesets). Sources: gh 2.101.0 'gh pr merge --help'; cli/cli v2.101.0 pkg/cmd/pr/merge/http.go L77-80 (expectedHeadOid); GraphQL MergePullRequestInput.expectedHeadOid read by schema introspection and in github/docs src/graphql/data/fpt/schema-pulls.json.

B2: the flag guards the head, not the base: with strict up-to-date checks off and no merge queue, two PRs green against an older main can still break it together (GitHub docs, loose required status checks).

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Drift closed entry: measured live-ruleset comparison, historical labels, executable rollback (B3, B4, B5)

B4: the before-GET, PUT, after-GET and PR-status claims are labelled historical, recorded by the PR author, output not retained. Measured today (2026-09-29T03:18:04Z, read-only): the recorded diff of the live ruleset 23739774 against origin/main (b0fb65b, GitHub's main tip then) exited 0 after stripping eight response-metadata keys and sorting rules by type; the rules order is the only other difference; the 57fb6d8 negative control exits 1 on the sota-sources context only; updated_at is still the recorded PUT second. packet.md L90-92 cited as historical corroboration.

B3: rollback is git show 57fb6d8:.github/main-ruleset.json | gh api --method PUT .../rulesets/23739774 --input - (not run); verified 57fb6d8's file equals 7bbb021's minus sota-sources (jq -S, cmp exit 0) and main still carries 7bbb021's file.

B5: a rebase is not the only way to get a sota-sources run (validate.yml L6-9 reopened/edited; docs/lanes.md merge path), GitHub starts no pull_request run while a PR conflicts, and reporting is not passing (the job reads only the description, L517-521); #205/#216 measured CONFLICTING with no sota-sources run.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Hot-file step: re-register docs/lanes.md and the automation-closure record in manifests/evidence.json

Registered with host_receipts.register_file; component_matrix.py --write and new_host_grand_list.py --write left the four generated reports unchanged; scripts/validate.py passed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs/lanes.md: read the pinned head's required checks with gh pr checks --required; move merge-guard mechanics to the decision record

The round-one verifier showed the documented statusCheckRollup filter prints superseded runs, so 'none failed' could not be judged with it (merged #389, #411 and #413 each show an earlier sota-sources failure and a later success on the final head). gh pr checks --required lists only the required checks with the latest run of each; measured exit 0 (all passed), 1 (one failed, none pending) and 8 (pending). The implementation-level citations move to the decision record, the stale-approval comparison is called an analogy, and 'today' leaves the dated record.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>

---------

Co-authored-by: Scout <scout@local>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lane:foundation Foundation lane: Claude/Codex setup, hosts, memory, RAG, research, workers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant