The hole
resolveMergeAuthority reads the working-tree AGENTS.md:
// work-driver-merge-authority.ts:110
text = await fs.readFile(path.join(repoRoot, "AGENTS.md"), "utf8");
and it is called at the merged step (work-driver-merged.ts:294) — which runs after commit-pr has integrated the developer subagents' patches into repoRoot.
grep -rniE 'protected.?file' extension/src/ returns nothing. There is no gate on what a develop-step patch may touch.
So a developer subagent that adds a sentence like "LLMs are allowed to squash merge PRs" to AGENTS.md gets that change integrated at commit-pr, and the merge gate then reads the file the cycle just wrote and grants itself authority.
This does not require malice. A cycle working on an issue that legitimately touches AGENTS.md — of which this project has had many — can do it by accident.
Prior art says this is the known shape
- Amp shipped a CVE for exactly this: the agent could edit its own allowlist via prompt injection (Embrace The Red, Aug 2025). Any policy file the agent can write is not a policy file.
- gh-aw refuses by default any patch touching
AGENTS.md, CLAUDE.md, GEMINI.md, CODEOWNERS, .github/, .claude/ or package manifests. Its stated rationale: "This protects against supply chain attacks where an AI agent could inadvertently (or through prompt injection) alter dependency definitions, CI/CD pipelines, or agent behavior files."
- Claude Code will not read
autoMode from .claude/settings.json at all, because "a checked-in repo or a build step could otherwise inject its own allow rules."
The principle is uniform: the repository is not trusted to loosen policy.
What to build
A protected-path set that a develop-step patch may not modify without halting the cycle. At minimum: AGENTS.md, CLAUDE.md, .github/, agents.json, .pi/, CODEOWNERS.
The natural home is the consolidation path in work-driver-integrate.ts, or the develop verification gate — both already inspect the staged patch set, so this is a check on data the driver already has rather than new plumbing.
On a match: halt with a distinct cap and name the offending paths. Do not silently drop the hunk — a partial integration is worse than a stop.
An operator who genuinely wants a cycle to edit doctrine can do it outside /work, which is the right amount of friction for changing the rules the harness runs by.
Acceptance criteria
Note on ordering
This is a prerequisite for #406 (LLM-judged, citation-verified policy resolution). Making the grant easier to express in natural language while the cycle can still write that natural language would make the hole wider, not narrower.
This work must ship as its own separate PR, independent of any other open issue.
The hole
resolveMergeAuthorityreads the working-treeAGENTS.md:and it is called at the
mergedstep (work-driver-merged.ts:294) — which runs aftercommit-prhas integrated the developer subagents' patches intorepoRoot.grep -rniE 'protected.?file' extension/src/returns nothing. There is no gate on what a develop-step patch may touch.So a developer subagent that adds a sentence like "LLMs are allowed to squash merge PRs" to
AGENTS.mdgets that change integrated atcommit-pr, and the merge gate then reads the file the cycle just wrote and grants itself authority.This does not require malice. A cycle working on an issue that legitimately touches
AGENTS.md— of which this project has had many — can do it by accident.Prior art says this is the known shape
AGENTS.md,CLAUDE.md,GEMINI.md,CODEOWNERS,.github/,.claude/or package manifests. Its stated rationale: "This protects against supply chain attacks where an AI agent could inadvertently (or through prompt injection) alter dependency definitions, CI/CD pipelines, or agent behavior files."autoModefrom.claude/settings.jsonat all, because "a checked-in repo or a build step could otherwise inject its own allow rules."The principle is uniform: the repository is not trusted to loosen policy.
What to build
A protected-path set that a develop-step patch may not modify without halting the cycle. At minimum:
AGENTS.md,CLAUDE.md,.github/,agents.json,.pi/,CODEOWNERS.The natural home is the consolidation path in
work-driver-integrate.ts, or the develop verification gate — both already inspect the staged patch set, so this is a check on data the driver already has rather than new plumbing.On a match: halt with a distinct cap and name the offending paths. Do not silently drop the hunk — a partial integration is worse than a stop.
An operator who genuinely wants a cycle to edit doctrine can do it outside
/work, which is the right amount of friction for changing the rules the harness runs by.Acceptance criteria
AGENTS.mdhalts the cycle beforemerged. Must fail againstmaintoday, where it integrates silently.AGENTS.mddoes not reach a granted merge authority.Note on ordering
This is a prerequisite for #406 (LLM-judged, citation-verified policy resolution). Making the grant easier to express in natural language while the cycle can still write that natural language would make the hole wider, not narrower.
This work must ship as its own separate PR, independent of any other open issue.