fix(missions): repo-less capture was publishing the agent's own identity files

First live run of `capture_repo_less_phases`: 9 artifacts, of which 2 were the
user's research. The other 7 were AGENTS.md, HEARTBEAT.md, IDENTITY.md,
MEMORY.md, SOUL.md, TOOLS.md and USER.md — the agent runtime's identity
scaffolding, seeded into the workspace root because that root is pinned to the
repository root.

The codebase already knew about these files and already had the list. What it
did not have is a defence that works without a repo: `ignore_agent_scaffolding`
writes them to `.git/info/exclude`, and a mission with no repository has no
`.git`. So the exact files that once got committed into a user's repo and
pushed (the reason that list exists) came back through a new channel.

`AGENT_SCAFFOLDING` is now `pub(crate)` and `mission_outputs` filters on it
directly — one list, two consumers, so the next file the runtime starts seeding
is excluded from both at once rather than from whichever was remembered.

Negative control: replace the filter with `&& true` and
`research_documents_are_kept_and_scaffolding_is_not` fails.

Found by running it against a live mission, not by reading it. The unit tests
passed the whole time — they seeded a tree that did not contain the scaffolding,
because I did not know it would be there.
This commit is contained in:
Omar Sobh
2026-08-07 11:35:00 -07:00
parent ceab28b902
commit 89bc53b53d
2 changed files with 21 additions and 2 deletions
+7 -1
View File
@@ -560,7 +560,13 @@ fn strip_credentials(url: &str) -> String {
/// They are the agent's identity scaffolding, not the user's code — `SOUL.md`
/// opens "Who You Are / You're not a chatbot." Observed on mission 019fc058,
/// where all seven appeared as untracked files in a freshly cloned repo.
const AGENT_SCAFFOLDING: &[&str] = &[
///
/// `pub(crate)` because a repo-less mission needs the same list and cannot use
/// the same mechanism: `ignore_agent_scaffolding` writes `.git/info/exclude`,
/// and a mission with no repository has no `.git`. `mission_outputs` filters on
/// this list directly — one list, two consumers, so the next file the runtime
/// starts seeding is excluded from both at once.
pub(crate) const AGENT_SCAFFOLDING: &[&str] = &[
"AGENTS.md",
"HEARTBEAT.md",
"IDENTITY.md",