Skip to content

Fence the headless /skill-doctor run - #925

Merged
seathatflowsinourveins merged 7 commits into
mainfrom
claude/cc-native-practice-skill-doctor-fence-20261009
Oct 10, 2026
Merged

seathatflowsinourveins merged 7 commits into
mainfrom
claude/cc-native-practice-skill-doctor-fence-20261009

Conversation

@seathatflowsinourveins

@seathatflowsinourveins seathatflowsinourveins commented Oct 9, 2026 •

Copy link
Copy Markdown
Owner

Scope

  • What this PR changes, in one or two sentences: tools/skill-usage/skill_usage.py now runs the headless /skill-doctor call with the headless fences (--permission-mode dontAsk, --permission-prompts none, --tools "", --strict-mcp-config, --max-turns 1, --max-budget-usd 0.05) instead of inheriting the host's permission mode, MCP servers and tools; both CLI help entries and every documented copy of the command say the same, and a test ties each copy to the argv. On a host with a readable managed-mcp.json the run drops --strict-mcp-config, because the client refuses that flag there (skill_doctor_argv; a forward commit that answers the Codex review thread on enterprise-managed clients). A third forward commit (715229c72fcb5e4afe7e86969798c7b1e189b21a) tests the three documented locations through that default route, answering the co-op's GPT read of 46cc5dd1 (CHANGES_REQUESTED, P2).
  • Base commit: 4d345267866403d75edece400fde6cd35ec4d05c (one landing rebase onto it, 2026-10-10: four commits replayed with no conflict, all seven per-file patch-ids unchanged; first opened on 22e3ef6ffd53b09f3cb335c46f7c696093521274); three forward commits sit on top of the rebased head 940358882b906cb9fcc4df65c8a1c20052f1404d: d7c7ef24ad07a800b1cddd1780e7eb49e1a4d463 (the managed-config behaviour), 46cc5dd15ff56deb2c2283e739c1a91e4185737d (it removes a platform branch that CI's macOS drift guard flagged) and 715229c72fcb5e4afe7e86969798c7b1e189b21a (tests for each documented location through the default route; the source comment and README record what the pinned client checks)
  • Lane: lane:foundation
  • Owned paths touched (seven files): tools/skill-usage/skill_usage.py (the argv constant with its sourced comment, skill_doctor_argv and the three documented managed-config paths (all checked on every system, so the module has no platform branch), the call, both CLI help entries built from the constant), tests/test_skill_usage.py (the exact-argv test pins each fence with a literal; new tests tie every documented copy, each help entry, the comment's sources and the Windows decision's locator to the code, and five more cover the managed-config case; the run tests no longer depend on a managed file existing on the host; the new class ManagedMcpDefaultLocations tests each documented location through the default route in three tests, and the comment test also holds the pinned-client wording), tools/skill-usage/README.md, adoption/update.md and blueprints/native-skill-practice/README.md (the documented manual command, now the fenced form, with when to drop --strict-mcp-config), docs/decisions/2026-10-04-pwsh7-windows-guidance.md (one shifted line locator, now 2423-2429), manifests/evidence.json (re-registration). Record Codex native skill-event overturn condition #898 also changes the manifests/evidence.json entry for tools/skill-usage/README.md (its text does not overlap), so whichever lands second re-hashes it.

Closes the headless-sdk gap the Claude Code native practice record of 2026-10-09 lists ("two call sites lack fences (skill_usage.py)", #923).

SOTA sources

  • Claude Code 2.1.295 and 2.1.296 claude --help (the installed clients, read 2026-10-10): --tools ("Use "" to disable all tools"), --permission-mode, --permission-prompts, --strict-mcp-config, --max-budget-usd; --max-turns is accepted but hidden from --help and documented at https://code.claude.com/docs/en/cli-reference (read 2026-10-09).
  • Claude Code managed MCP, https://code.claude.com/docs/en/managed-mcp (read 2026-10-10T08:11Z): a deployed managed-mcp.json holds exclusive control of the MCP servers; "If a user passes it [--strict-mcp-config] while such a file is deployed, Claude Code exits at startup on a workstation and in a cloud session alike"; system paths /Library/Application Support/ClaudeCode/, /etc/claude-code/ and C:\Program Files\ClaudeCode\ (the configuration summary).
  • The installed Claude Code 2.1.296 binary (read 2026-10-10): the strings "You cannot use --strict-mcp-config when an enterprise MCP config is present" and "Ignored: an enterprise MCP config (managed-mcp.json) is present and has exclusive control over MCP servers".
  • The same binary, read first-hand at byte offsets (sha256 24972e3bc859fab2b46ed4c1e51f7d6130f06d3bd550811a114640de3370d0de, 2026-10-10; the co-op's GPT read reached the same offsets independently): the managed-config reader at 217796118, the refusal function at 217799919 (if(!D_()) continue, if(rTe()===null){if(n) refuse: only a present file with no read, JSON or schema error), the plugin eval init launcher at 247738504 (...D_()?[]:["--strict-mcp-config"]: presence alone), and My() at 209080002, which picks /Library/Application Support/ClaudeCode, C:\Program Files\ClaudeCode or /etc/claude-code by platform.
  • Repository rule PERM-03 (docs/harness-rules-convergence-20260922.md): deny anything not pre-approved without prompting, which --permission-mode dontAsk with --permission-prompts none does.
  • Permission modes, https://code.claude.com/docs/en/permission-modes (read 2026-10-09): dontAsk runs only reads and pre-approved tools and denies anything that would prompt (with --tools "" no tool is offered at all); a session with no --permission-mode starts in the settings' permissions.defaultMode, which is bypassPermissions in this host's user settings.
  • The headless default of the native practice record (Claude Code native practice, 2026-10: one evidenced default per role slot #923, slot headless-sdk): every fence on the command line for unattended runs.

Evidence-class table

Claim Evidence class Command / receipt
On each of 2.1.295 and 2.1.296 the earlier fences (b2189ba0) and these return the same table byte for byte (2.1.295: sha256 prefix b8cd3fe63acb; 2.1.296: 0d593d5aaa82; 6,086 characters each), all success, total_cost_usd 0, num_turns 0; the two clients' tables differ from each other local_integration paired receipt measured 2026-10-10T03:53:27Z, retained in the lane's research record (skill-doctor-paired-receipt-20261010.json, sha256 ee0c2ebd0d6e8652463a532a90de3c6e10d8487e93a3d374d59409161db347d4); an earlier unfenced-versus-fenced run on 2.1.295 gave the same table (2026-10-09)
The tool works end to end with the fences: Claude side measured: true, origin: run, cost 0, 0 turns local_integration python3 tools/skill-usage/skill_usage.py --run-skill-doctor --out <scratch>
The exact argv, with each fence, is pinned by an independent literal; an added --tools Read, an added --dangerously-skip-permissions and a second --max-turns each fail the test; each of the other copies (both help entries, the three documents, the module docstring) is tied to the argv by a test local_integration tests.test_skill_usage.RunSkillDoctor.test_runs_exact_argv_with_devnull_stdin_and_timeout, test_every_documented_copy_of_the_command_matches_the_argv, test_each_cli_help_entry_names_the_exact_command, test_the_fence_comment_names_its_sources; mutation runs in a scratch interpreter
On a host with a readable managed-mcp.json the argv drops only --strict-mcp-config and keeps every other fence; with none it is unchanged; the client refuses the flag where the file is deployed local_integration for the drop (a fake managed path), source_review for the client behaviour tests.test_skill_usage.RunSkillDoctor: test_a_deployed_managed_mcp_config_drops_only_the_strict_flag, test_run_skill_doctor_runs_the_managed_form_on_a_managed_host, test_without_a_readable_managed_file_every_fence_stays, test_the_default_paths_are_the_documented_system_paths, test_the_managed_mcp_comment_and_guide_cite_the_client_and_the_docs (all five error on 940358882); tests.test_workflow_hardening.MacosPatternsTests passes at the head; the 2.1.296 binary strings and the docs page above. Not run live: this host has no managed-mcp.json
Each documented location alone decides the form through the default route: absent, unreadable and a directory keep the flag, a parsed and an unparsable file drop it, every other fence keeps its place; each location is needed (removed from the tuple, its present cases keep the flag), and run_skill_doctor takes the same route local_integration (a fake filesystem for the documented strings, a real file behind each present state) tests.test_skill_usage.ManagedMcpDefaultLocations: test_each_documented_location_decides_the_form_alone, test_dropping_a_locations_detection_fails_its_cases, test_run_skill_doctor_takes_the_default_route (3 tests, 32 cases)
Thirteen in-memory mutations (each location ignored or misspelled, all default detection off, any to all, no readability check, no regular-file check, the strict flag never or always dropped, run_skill_doctor ignoring the default) each fail the new class; RunSkillDoctor alone fails three of them local_integration (mutation harness, nothing on disk changes) mutations925.py over the committed tests, table retained in the lane's research record (pr925/mutations-20261010.txt)
The pinned client refuses --strict-mcp-config only for a present file that loads without error, and its own launcher drops the flag on presence alone, so a readable file drops it here and an absent, unreadable or non-regular file keeps it source_review (the 2.1.296 binary, offsets above) test_the_managed_mcp_comment_and_guide_cite_the_client_and_the_docs holds the comment and the README to that wording
Caveat on the Windows cases: they model a host that reports a readable file at the documented Windows string. Whether that string is a location on a POSIX host is the separate P3 (a Windows-shaped filename in the working directory), which stays in its follow-up and narrows the Windows present cases to nt hosts source_review the co-op's GPT read of 46cc5dd1, finding 2
docs/decisions/2026-10-04-pwsh7-windows-guidance.md cites the ledger-path refusal at its new lines 2423-2429 of tools/skill-usage/skill_usage.py, counted by newline (str.splitlines() also breaks at the U+2028 and U+2029 on line 768 and is the wrong counter here) source_review tests.test_skill_usage test_the_windows_decision_cites_the_ledger_refusal_lines, which fails on an inserted line and on the earlier locator

Declared test contract change

tests.test_skill_usage.RunSkillDoctor.test_runs_exact_argv_with_devnull_stdin_and_timeout keeps its exact-argv pin; the pinned argv gains flags and loses none. At the merge base it asserts ["claude", "-p", "/skill-doctor", "--output-format", "json"], so that version fails against this head by design (the co-op's pre-cue read: 203 run, 1 failure). The added flags, in order after json:

  • --permission-mode dontAsk
  • --permission-prompts none
  • --tools ""
  • --strict-mcp-config
  • --max-turns 1
  • --max-budget-usd 0.05

The first five elements are unchanged, and the added flags are six. On a host with a readable managed-mcp.json the argv has five of them, without --strict-mcp-config (test_a_deployed_managed_mcp_config_drops_only_the_strict_flag pins that literal), because the client refuses the flag there. The test compares the captured argv with a complete literal list, independent of SKILL_DOCTOR_ARGV (the co-op's read showed that comparing with the constant pinned nothing); an added --tools Read, an added --dangerously-skip-permissions and a second --max-turns each fail it.

Local commands run

Head 715229c72fcb5e4afe7e86969798c7b1e189b21a (the rebased 940358882 plus three forward commits) on its base 4d3452678 (CPython 3.13.16 locally; main has since moved to fcdeae42c), TMPDIR outside /tmp and outside home:
$ python3 -E -s -B scripts/validate.py
{"components": 70, "hashed_files": 11337, "profiles": 4, "receipts": 239, "status": "passed"}   (rc 0)
$ python3 -E -s -B scripts/evidence_manifest.py --check
{"files": 11337, "status": "passed"}   (rc 0)
$ python3 -E -s -B scripts/validate_convergence.py --all-recorded
rc 0
$ python3 -E -s -B scripts/component_matrix.py --write && python3 -E -s -B scripts/new_host_grand_list.py --write
rc 0; no change to a generated report
$ python3 -E -s -B -m unittest tests.test_skill_usage tests.test_workflow_hardening tests.test_evidence_manifest tests.test_upstream_surface_watch
Ran 470 tests in 43.515s; OK (skipped=26)
$ /var/tmp/ccnp-ci-venv/bin/python -E -B -m unittest tests.test_skill_usage tests.test_freeze_snapshot tests.test_token_measurement tests.test_transcript_audit tests.test_workflow_hardening   # CPython 3.12.3 plus the CI requirements
Ran 635 tests in 214.135s; OK (skipped=66)
$ git diff origin/main --unified=0 | grep '^+'   # added lines, scanned for the host's private names
hits 0

The hosted CI at the first forward commit (d7c7ef24) failed one test, tests.test_workflow_hardening.MacosPatternsTests.test_drift_guard_every_flagged_file_is_listed_or_excluded, which flagged a platform branch in skill_usage.py; my targeted list had not included it, and the second forward commit removes the branch (the module now checks all three documented paths on every system). The hosted CI that ran before this rebase tested the merge of the earlier head onto the old main (30aa2fa7), where the evidence row for tests/test_local_pages_fleet_regressions.py was stale (fixed on main by #923); this head is rebuilt on the current main, so the hosted run at this head is the first measurement of the real merge.

The co-op's GPT read of 46cc5dd1 found that RunSkillDoctor empties the module's paths and injects one, so ignoring a location or all default detection still passed; the third forward commit answers it with the default-route class and the mutation table above. The local full-suite shards in a CI-like environment are noisy on this host (four of eight red at the CI-green 46cc5dd1 on tests no change touches), so the commands above are the targeted lists, and the hosted CI at the head is the full-suite measurement.

Decision record

No new record: this applies the headless-sdk default of the 2026-10-09 native practice record (#923). The slot's note is updated in the follow-up to that PR once both land.

Host evidence

Not applicable: no file under evidence/hosts/ changes.

Checklist

  • New/changed GitHub Actions are pinned to a full commit SHA with a version comment (no floating tags). (none changed)
  • New/changed workflows declare top-level permissions: {} and grant each job only what it needs. (none changed)
  • No secrets are printed, logged or committed; no new required secret was added without a documented owner.
  • No new paid hosting, subscription or billing surface was introduced.
  • Peer-owned untracked files and worktrees were preserved (not deleted, moved or overwritten).

🤖 Generated with Claude Code

@seathatflowsinourveins seathatflowsinourveins added the lane:foundation Foundation lane: Claude/Codex setup, hosts, memory, RAG, research, workers label Oct 9, 2026
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-09T08:18:28.179815Z 40d25be PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 40d25bebc9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tools/skill-usage/skill_usage.py Outdated
Comment thread tools/skill-usage/skill_usage.py Outdated
seathatflowsinourveins added a commit that referenced this pull request Oct 10, 2026
…al locator

The co-op's GPT read of #925 at 2c8fc61 asked for three forward fixes, each with a test that fails without it:
- The --claude-skill-doctor help still showed the bare command, and the drift test searched the whole --help output,
  so the correct --run-skill-doctor entry hid the stale one. Both entries now print shlex.join(SKILL_DOCTOR_ARGV),
  checked per option through build_parser() (main's parser, split out unchanged).
- The fence comment credited `claude --help` for every flag, but 2.1.295 and 2.1.296 hide --max-turns from --help.
  The comment now names the flags --help lists, cites the CLI reference for --max-turns and PERM-03 for the
  no-prompting rule; a test rejects the 2c8fc61 comment and any unlanded-record source.
- The Windows decision's locator: by newline count (editors, grep -n, GitHub) 2381-2386 did hold the def and the
  work-tree refusal at 2c8fc61; the read's probe used str.splitlines(), which also breaks at the U+2028 and U+2029
  this module holds in one line, two lines off. This commit's insertions move the function to 2389-2395, so the
  decision cites that, and a test (splitting on "\n") fails whenever the cited lines stop holding the refusal.

Measured on 2026-10-10 from this branch: /skill-doctor returns, per client, a sha256-identical table at $0 and 0 turns
with the b2189ba fences and with these (2.1.295 and 2.1.296).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
skill_usage.py ran `claude -p "/skill-doctor"` with no fences, so the run
inherited the host's default permission mode (bypassPermissions here), its
MCP servers and every tool. /skill-doctor is a local command (0 turns, $0),
so fences change nothing while the client recognises it; they keep an
unrecognised prompt from reaching a model with tools or spending:
--permission-mode dontAsk, --tools "", --strict-mcp-config, --max-turns 1
and --max-budget-usd 0.05. Measured on 2.1.295: the fenced and unfenced runs
return the same table byte for byte, at cost 0 and 0 turns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The test compared the captured argv with SKILL_DOCTOR_ARGV, the same list
the code passes, so a mutated constant still passed. It now compares with a
complete literal list; the co-op's three mutations (an added --tools Read,
--dangerously-skip-permissions, a second --max-turns) each fail. The
pwsh7 record's locator for ledger_path_issue moves from 2372-2377 to
2381-2386, the same six lines after the constant inserted above them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ommand to the argv

Blind judging of the review-gate run confirmed five defects that were still present at b2189ba:
- the argv lacked --permission-prompts none, which the adopted rule PERM-03 requires
  (docs/harness-rules-convergence-20260922.md);
- tools/skill-usage/README.md, adoption/update.md and blueprints/native-skill-practice/README.md still gave the
  bare, unfenced command, and no test tied those hand copies to SKILL_DOCTOR_ARGV;
- the --run-skill-doctor help named the Python constant instead of the command it runs;
- the fence comment cited an unlanded record with no repository, pin or path.

The argv gains --permission-prompts none; the docstring, both READMEs and the update guide carry the exact fenced
command; the help prints shlex.join(SKILL_DOCTOR_ARGV); a new test fails when any copy drifts from the argv; the
comment names the PERM-03 rule and the measured result. Measured on Claude Code 2.1.295: /skill-doctor returns the
same table (identical sha256) at $0 and 0 turns with and without the new flag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…al locator

The co-op's GPT read of #925 at 2c8fc61 asked for three forward fixes, each with a test that fails without it:
- The --claude-skill-doctor help still showed the bare command, and the drift test searched the whole --help output,
  so the correct --run-skill-doctor entry hid the stale one. Both entries now print shlex.join(SKILL_DOCTOR_ARGV),
  checked per option through build_parser() (main's parser, split out unchanged).
- The fence comment credited `claude --help` for every flag, but 2.1.295 and 2.1.296 hide --max-turns from --help.
  The comment now names the flags --help lists, cites the CLI reference for --max-turns and PERM-03 for the
  no-prompting rule; a test rejects the 2c8fc61 comment and any unlanded-record source.
- The Windows decision's locator: by newline count (editors, grep -n, GitHub) 2381-2386 did hold the def and the
  work-tree refusal at 2c8fc61; the read's probe used str.splitlines(), which also breaks at the U+2028 and U+2029
  this module holds in one line, two lines off. This commit's insertions move the function to 2389-2395, so the
  decision cites that, and a test (splitting on "\n") fails whenever the cited lines stop holding the refusal.

Measured on 2026-10-10 from this branch: /skill-doctor returns, per client, a sha256-identical table at $0 and 0 turns
with the b2189ba fences and with these (2.1.295 and 2.1.296).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@seathatflowsinourveins
seathatflowsinourveins force-pushed the claude/cc-native-practice-skill-doctor-fence-20261009 branch from c955581 to 9403588 Compare October 10, 2026 07:38
Claude Code refuses --strict-mcp-config while managed-mcp.json is deployed:
2.1.296 prints "You cannot use --strict-mcp-config when an enterprise MCP
config is present", and https://code.claude.com/docs/en/managed-mcp says the
client "exits at startup" when the flag is passed with that file deployed. So
--run-skill-doctor failed on enterprise-managed hosts (the Codex review thread
on #925, P2). skill_doctor_argv now drops only that flag when a readable
managed-mcp.json exists at the documented system path (macOS, Linux and WSL,
Windows) and keeps every other fence; the documented command is unchanged.

Five new tests, each failing on the previous code (the new function, the
keyword, the path table and the comment and guide citations did not exist);
the existing RunSkillDoctor tests no longer depend on a managed-mcp.json that
may exist on the host running them. The README, adoption/update.md and the
blueprint README say when to drop the flag, and the Windows decision's locator
follows the inserted lines (2389-2395 to 2421-2427, counted by newline).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…form

The macOS drift guard (tests.test_workflow_hardening.MacosPatternsTests)
flagged the platform branch that chose the managed-mcp.json path, a failure
the targeted run missed and CI's shard 0 found. Another system's documented
path cannot exist on this one, so skill_doctor_argv now checks all three
(/Library/Application Support/ClaudeCode/, /etc/claude-code/, and
C:\Program Files\ClaudeCode\ from the managed MCP page's configuration
summary) and the module has no platform branch. The run tests are hermetic by
patching the path tuple, the path test pins the three documented paths, and
the Windows decision's locator follows the removed lines (2421-2427 to
2417-2423, counted by newline).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… default route

The co-op's GPT read of 46cc5dd (CHANGES_REQUESTED, P2): RunSkillDoctor empties
MANAGED_MCP_CONFIG_PATHS and injects one temporary path, so ignoring the macOS, Linux or
Windows location, or all default detection, still passed 15/15.

ManagedMcpDefaultLocations answers it. For each documented location alone, a fake
filesystem (a real file backs each present state) reports the location absent, present and
parsed, present and unparsable, present but unreadable, or a directory, and the full
ordered argv is compared with independent literals: the strict flag kept or dropped, the
other fences unchanged. A sensitivity test removes each location from the tuple and shows
that its present cases then keep --strict-mcp-config while the other locations still decide
the form; run_skill_doctor takes the same default route. Thirteen in-memory mutations
(each location ignored or misspelled, all detection off, any->all, no readability check, no
regular-file check, strict never or always dropped, run_skill_doctor ignoring the default)
fail the new class; RunSkillDoctor alone fails three of them.

The Windows cases model a host that reports a readable file at the documented string. Whether
that string is a location on a POSIX host is the separate P3 (a Windows-shaped filename in
the working directory), which stays in its follow-up and narrows these cases to nt hosts.

The source comment and the README now record what the pinned client does (2.1.296 binary,
sha256 24972e3b..., read 2026-10-10): the refusal applies only to a present file that loads
without a read, JSON or schema error (reader at byte 217796118, refusal at 217799919), and
its own plugin eval init launcher drops the flag on presence alone (byte 247738504). A test
holds the comment and README to that wording. The six added comment lines move the
ledger-refusal locator in the pwsh7 decision from 2417-2423 to 2423-2429.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@seathatflowsinourveins
seathatflowsinourveins merged commit e1c88ff into main Oct 10, 2026
36 checks passed
@seathatflowsinourveins
seathatflowsinourveins deleted the claude/cc-native-practice-skill-doctor-fence-20261009 branch October 10, 2026 14:03
seathatflowsinourveins added a commit that referenced this pull request Oct 10, 2026
…967)

On POSIX the Windows path C:\Program Files\ClaudeCode\managed-mcp.json is a relative
filename, so a file of that literal name in the working directory made skill_doctor_argv
drop --strict-mcp-config while the client saw no managed config and would load user and
project MCP servers. The command center's micro of #925 at 46cc5dd found it and the
co-op's GPT reads of #925 at 46cc5dd and 715229c reproduced it (a readable file of that
name holding malformed JSON, the default helper omitting the flag); the native client's My()
picks the directory of the actual platform.

A candidate now counts only when it is absolute and already normalised under the host's own
path rules, with no platform branch and without resolving a relative candidate against the
working directory; the comment no longer claims another system's path cannot exist on this
one.

Tests: one plants both the Windows-named file and a plain managed-mcp.json in the working
directory and expects every fence to stay (it fails on the head of #925 and passes here);
the comment test holds the new wording. ManagedMcpDefaultLocations, added by #925, is
narrowed as that PR declared: a documented string is a location only where it is an absolute
path on the host (counts_here), so the Windows cases run on nt hosts and on POSIX the Windows
string is asserted inert in every state; the sensitivity test and the run test use the
host's own locations. Sixteen in-memory mutations (research record, p3-mutations-20261010):
the P3 revert, prepending the working directory and basename matching are caught, and
ignoring or misspelling the Windows location are equivalent mutants on POSIX, caught on nt
only. The Windows decision's ledger-refusal locator follows the added lines (2423-2429 to
2432-2438, counted by newline).

Co-authored-by: seathatflowsinourveins <234074349+seathatflowsinourveins@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Oct 11, 2026
The command center's note on landing #967: the most useful P3 is a CI-run test that patches the
module's os with ntpath, so the Windows side of the check is covered under Python 3.12, the CI
interpreter. os.path is posixpath on a POSIX host, so until now the Windows clauses of
_readable_system_file were argued from ntpath's functions and never executed.

ManagedMcpDefaultLocations.test_the_windows_path_rules_count_the_windows_string_and_not_the_posix_ones
replaces the module's os, for the test only, with a namespace whose isabs and abspath are ntpath's
and whose file checks answer yes, so the path rule alone decides: the Windows documented string
drops --strict-mcp-config, the macOS and Linux strings do not (under 3.12 ntpath.isabs accepts a
leading separator and only the equality rejects them; from 3.13 isabs rejects them first), and a
dot-dot, dot, forward-slash and doubled-separator spelling of the Windows string, a relative name
and a drive-relative name each keep every fence. One accepted candidate among rejected ones decides
the form, by parameter and by the default route.

Source-level mutants of _readable_system_file, run in the module's own globals so the mutated body
sees the swapped os (research record, pr925/p3-source-mutations-*): removing only the
normalisation equality fails the new test on 3.12 and 3.13 (the four non-normalised Windows
spellings, plus the macOS and Linux strings on 3.12) and the POSIX-side test added in the previous
commit; removing only isabs fails it through the drive-relative name, because ntpath's pure-Python
abspath on a POSIX host leaves "C:managed-mcp.json" equal to itself (the Windows abspath resolves
it, so there isabs is belt and braces); reverting to the presence-only check of #925 fails the
POSIX and Windows tests. The harness of the earlier mutation tables replaced the whole function and
would not see a swapped os, so these supersede it for this test.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Oct 11, 2026
…s under ntpath (#973)

* Test that an absolute but non-normalised managed-config spelling is not a managed config

The co-op's GPT read of #967 at 3de3dda (ACK, one non-blocking P3): the normalisation
equality in _readable_system_file (os.path.abspath(name) == name) could be removed with every
test green, because the relative-name test is already rejected by isabs and the default-location
matrix only passes normalised strings.

ManagedMcpDefaultLocations.test_an_absolute_path_that_is_not_normalised_is_not_a_managed_config
uses real files (no fake host, so it runs on every platform): an existing child directory and
the same managed-mcp.json reached through a dot-dot, a dot and a doubled-separator spelling.
Each exists through the filesystem and each keeps the complete fenced argv, by parameter and by
the default route; pathlib drops the dot and doubled-separator segments itself, so only the
dot-dot spelling is also asserted as a Path. The normalised spelling of the same file drops
only --strict-mcp-config, alone and next to a rejected candidate.

Mutations (research record, pr925/p3-mutations-20261010): removing only the normalisation
equality fails the new test (4 cases, none elsewhere), reverting the P3 fix still fails the
relative-name test and now the new one; removing only isabs stays green because
abspath(name) == name already implies an absolute name (an equivalent mutant, so isabs
remains for readability).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Cover the managed-config path rule under ntpath on every host

The command center's note on landing #967: the most useful P3 is a CI-run test that patches the
module's os with ntpath, so the Windows side of the check is covered under Python 3.12, the CI
interpreter. os.path is posixpath on a POSIX host, so until now the Windows clauses of
_readable_system_file were argued from ntpath's functions and never executed.

ManagedMcpDefaultLocations.test_the_windows_path_rules_count_the_windows_string_and_not_the_posix_ones
replaces the module's os, for the test only, with a namespace whose isabs and abspath are ntpath's
and whose file checks answer yes, so the path rule alone decides: the Windows documented string
drops --strict-mcp-config, the macOS and Linux strings do not (under 3.12 ntpath.isabs accepts a
leading separator and only the equality rejects them; from 3.13 isabs rejects them first), and a
dot-dot, dot, forward-slash and doubled-separator spelling of the Windows string, a relative name
and a drive-relative name each keep every fence. One accepted candidate among rejected ones decides
the form, by parameter and by the default route.

Source-level mutants of _readable_system_file, run in the module's own globals so the mutated body
sees the swapped os (research record, pr925/p3-source-mutations-*): removing only the
normalisation equality fails the new test on 3.12 and 3.13 (the four non-normalised Windows
spellings, plus the macOS and Linux strings on 3.12) and the POSIX-side test added in the previous
commit; removing only isabs fails it through the drive-relative name, because ntpath's pure-Python
abspath on a POSIX host leaves "C:managed-mcp.json" equal to itself (the Windows abspath resolves
it, so there isabs is belt and braces); reverting to the presence-only check of #925 fails the
POSIX and Windows tests. The harness of the earlier mutation tables replaced the whole function and
would not see a swapped os, so these supersede it for this test.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
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