fix(bin): keep the firstmate home's supervisor contract out of nested ship and scout workers - #4
Merged
Merged
Conversation
…tructions A ship or scout worktree inside a firstmate home, as a second mate's in-project Treehouse pool places it, loaded the home's CLAUDE.md and @AGENTS.md supervisor contract on every Claude and Pi request. A nested Claude launch now carries claudeMdExcludes for the home's instruction files in its per-launch --settings, and a nested Pi launch's per-task extension removes the home's context-file blocks from the system prompt. Second mates and workers outside every home launch unchanged. Adds a portable spawn test, an opt-out live guard against the installed harnesses, and dated per-harness verification, including Cursor and Oh My Pi as documented unfixed leaks.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Lets try fixing this.
"This" is the finding from a token-usage study of the fleet: project workers supervised by a second mate have their worktrees inside that second mate's firstmate home (/projects//.treehouse///), so Claude Code loads every ancestor CLAUDE.md, including /CLAUDE.md -> @AGENTS.md, the full firstmate supervisor contract (about 87K chars, about 31K tokens), on every API call of every such worker and its subagents. The firstmate internal skills under /.claude/skills also appear in their skill listing (about 1.5K tokens). Measured: nested worker first-call context median 85-94K tokens versus 37K for a main-home worker whose pool sits outside the home; about 632M tokens over one week, about 351M tokens per day at the current rate. The same nesting triggered Claude Code's "external CLAUDE.md include" consent prompt repeatedly. Nested Pi workers show the same pattern (median first call 23.3K vs 7.4K tokens). The workers are told by their launch brief to ignore that contract anyway.
What Changed
bin/fm-spawn.shnow checks whether a ship or scout worktree sits inside the firstmate home that launches it, such as a second mate's in-project Treehouse pool at<home>/projects/<project>/.treehouse/.... When it does, a Claude launch gets aclaudeMdExcludeslist in its inline--settingsJSON. The list covers the home'sCLAUDE.md,CLAUDE.local.md,AGENTS.md,.claude/CLAUDE.md, and.claude/rules/**, so the@AGENTS.mdimport and the "external CLAUDE.md imports" prompt it caused both go away. A Pi launch's per-task extension adds abefore_agent_starthandler that removes those home files'<project_instructions>blocks from the system prompt on every turn. The project's own instruction files still load. Secondmates, and workers whose worktree is outside the home, launch unchanged. If the home path contains glob characters, the Claude exclusion is skipped and a warning is printed.docs/configuration.mdand the Claude harness-adapter reference now describe this behaviour.docs/verification/runtime-backends.mdgains a "Nested worktree instruction isolation" section with evidence for each harness: Claude and Pi fixed, Codex already stops at the git root, Cursor Agent CLI and Oh My Pi still leak and can't be fixed per path. It also records that the reported.claude/skillsleak did not reproduce on Claude Code 2.1.280.tests/fm-spawn-nested-home-context.test.sh, a spawn-level test, andtests/fm-nested-home-context-live-e2e.test.sh, a live check that captures each harness's real first request. Both are registered inbin/fm-test-run.sh.Risk Assessment
✅ Low: The change is well bounded. It only takes effect when a ship or scout worktree is strictly inside the launching FM_HOME, and every other launch gets an empty placeholder and stays byte-identical. Claude path quoting and escaping follow existing patterns in the script. The Pi filter removes only the exact rendered block of each file that sits in the home. A unit test drives the real spawn, and a live test compares captured model requests with and without the change.
Testing
Ran the change's own live guard, which points the installed claude 2.1.281 and pi 0.85.1 at a local server that records each request instead of sending it to a provider, plus the fm-spawn behavior test; both passed. Built a realistic measurement: a throwaway copy of this repo at HEAD served as the firstmate home (real 60KB AGENTS.md, CLAUDE.md, .claude/skills). Its own fm-spawn generated the launch for a nested worker, and the first request dropped the supervisor contract for both harnesses while keeping the project's rules. Firstmate skill names also no longer appear. Drove the interactive Claude screen in a private tmux server. The baseline launch shows the external-import consent dialog for the home's AGENTS.md; the fm-spawn launch reaches the input prompt with only the project's AGENTS.md loaded. Two edge-case home paths went through the real fm-spawn with a stub tmux pane only (no harness started), so those scenarios are recorded as untested live: the glob-character path printed a warning and left the settings unchanged, and the apostrophe path gave settings the shell parses as valid JSON. The Herdr lab could not be used because bin/fm-herdr-lab.sh requires a running default session for its fleet-state tripwire, and the default session was stopped; it was left untouched. Temporary directories were removed, the worktree is clean, and Herdr sessions are unchanged. Overall result: go.
Evidence: Live guard transcript (real claude + pi)
Source: Live guard transcript (real claude + pi)
Evidence: Realistic real-home measurement
Source: Realistic real-home measurement
claude nested, pre-change launch request_bytes= 174654 home_contract_hits=1 project_rules_hits=1 firstmate_skill_names_found=3/3 claude nested, fm-spawn launch request_bytes= 113090 home_contract_hits=0 project_rules_hits=1 firstmate_skill_names_found=0/3 pi nested, no filter request_bytes= 67908 home_contract_hits=1 project_rules_hits=1 firstmate_skill_names_found=3/3 pi nested, fm-spawn extension request_bytes= 7099 home_contract_hits=0 project_rules_hits=1 firstmate_skill_names_found=0/3Evidence: Measurement script
Source: Measurement script
Evidence: Claude screen, baseline launch: external-import consent dialog
Source: Claude screen, baseline launch: external-import consent dialog
Evidence: Claude screen, fm-spawn launch: straight to prompt, project AGENTS.md only
Source: Claude screen, fm-spawn launch: straight to prompt, project AGENTS.md only
Evidence: Edge-case home paths (glob char, apostrophe)
Source: Edge-case home paths (glob char, apostrophe)
Evidence: fm-spawn behavior test output
Source: fm-spawn behavior test output
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
bin/fm-spawn.sh:4287- This is a leftover gap, and it was already there before this change. The recorded Pi evidence (docs/verification/runtime-backends.md, Pi section) says Pi loads an instruction file from every directory between the cwd and the filesystem root. Claude, by contrast, skips the primary checkout's directories. So a nested Pi worker at <home>/projects/<project>/.treehouse/<pool>/<n>/<project> still loads the primary clone's <home>/projects/<project>/AGENTS.md as well as its worktree's own copy. That copy is the project's rules, not the firstmate supervisor contract the intent names, but it is a second copy and may come from another branch. The new filter only drops files sitting directly in the home, which matches the intent, so no change is needed here. It may be worth a follow-up if nested Pi first-call sizes still sit above the outside-home baseline.✅ **Test** - passed
✅ No issues found.
FM_NESTED_HOME_CONTEXT_LIVE=1 bash tests/fm-nested-home-context-live-e2e.test.sh(real claude 2.1.281 + pi 0.85.1 against a capture server)bash tests/fm-spawn-nested-home-context.test.shrealistic-nested-home.sh <worktree>: real-repo home copy, real fm-spawn launch, first-request size and contract/skill/project-rule presence for claude and pi, baseline vs fm-spawnInteractiveclaude --dangerously-skip-permissionsin the nested worktree inside a privatetmux -Lserver, with and without fm-spawn's--settings(isolated CLAUDE_CONFIG_DIR, worktree trusted, external imports not approved), screen captured after 12sadversarial-home-paths.sh <worktree>: real fm-spawn for a nested claude worker whose home path contains[1]and then an apostrophe; launch parsed by the shell and checked with jqAttemptedbin/fm-herdr-lab.sh prepare/provision fm-lab-nestedctx-*; refused because the default session was not running✅ **Document** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.