hive-subagent-mcp: fold the task out of the system prompt on the no-role path too

mara's ruling on this PR: the principle that landed for the role path is
general, so the no-role path moves the task out of the system prompt as
well. `system_prompt_and_trigger`'s no-role branch still passed
`prompt_file` straight through as `--append-system-prompt-file` —
byte-identical to main's pre-PR behaviour, and the exact bug this PR
exists to fix. Since no agent ships roles yet, every real dispatch takes
this path, so the standing-system-prompt bug (a task re-asserting itself
as an instruction on turn 2/3/N under continue/goal) was still live for
100% of usage; only the unused role path had actually been fixed.

`compose_prompt` now always folds the task into `trigger`, role or not.
Without a role there is no role text to hold a system-prompt file open
for, so none exists at all — `--append-system-prompt-file` is omitted
from the spawn entirely, not pointed at anything task-shaped.

Replaces `without_a_role_the_task_file_is_what_claude_is_pointed_at`
(which pinned the bug as "unchanged") with a test asserting the task is
absent from what reaches `--append-system-prompt-file` and present in
the trigger. Role-path tests are untouched in behavior; only mechanical
fallout from `compose_prompt`'s `Option<PathBuf>` return and `&str`
trigger params (clippy needless_pass_by_value once both branches only
borrowed it).

Blast radius: this changes behaviour for every existing dispatch, since
prompt_file's content has always gone into the system-prompt file before
this fix.
This commit is contained in:
atlas 2026-09-21 16:49:02 +02:00 committed by mara
commit 85ac62968e
2 changed files with 86 additions and 69 deletions

View file

@ -86,17 +86,25 @@ directory and has the answer. Per-agent rather than hive-wide: the clauses
a role carries come out of that one agent's own prompt files, and the
daemon only ever spawns on that agent's behalf.
**The role is the system prompt; the task is the turn — never the other
way round.** The daemon writes the named role's text, alone, into a
per-session file under the harness directory and points
`--append-system-prompt-file` at it — nothing about the task reaches that
file. It reads the task instructions (`prompt_file`) and folds them ahead
of the turn's own prompt instead, the same channel that carries them to
the subagent without a role. A task baked into the system prompt would
re-assert itself as an instruction on every later turn of the session, not
**The task is the turn, never a standing system prompt — with or without a
role.** A task baked into the system prompt re-asserts itself as an
instruction on every later turn of the session (`continue`, `goal`), not
just the one the caller wrote it for — the system prompt is the
subagent's standing identity, not a one-shot channel. The caller's own
task file stays untouched either way.
subagent's standing identity, not a one-shot channel. So the daemon always
reads the task instructions (`prompt_file`) and folds them ahead of the
turn's own prompt instead, whether or not the session has a role. The
caller's own task file stays untouched either way.
A role additionally names a standing identity: its text, alone, becomes a
per-session file under the harness directory, and `--append-system-prompt-file`
points at that — nothing about the task ever reaches it. Without a role
there is no such identity to hold a file open for, so `--append-system-prompt-file`
is omitted altogether rather than pointed at anything task-shaped.
⚠️ This applies to every dispatch, not just role-bearing ones — no agent
ships roles yet, so today it's the only path in real use. Before this fix
the no-role path put the task straight into the system prompt, same as
every `start` before roles existed at all.
### A role with no file refuses the call