Skip to content

Director prompt hygiene: impossible tool refs, triple-stated report contract, dead no-ship branch, persona coupling - #582

Merged
TheGreatAxios merged 2 commits into
mainfrom
cl-6807-director-prompt-hygiene-impossible-tool-references-triple
Aug 24, 2026
Merged

Director prompt hygiene: impossible tool refs, triple-stated report contract, dead no-ship branch, persona coupling#582
TheGreatAxios merged 2 commits into
mainfrom
cl-6807-director-prompt-hygiene-impossible-tool-references-triple

Conversation

@TheGreatAxios

Copy link
Copy Markdown
Collaborator

Closes CL-6807.

This is a hygiene-only pass on director prompts: removes what is wrong or duplicated, never anything load-bearing-but-advisory. Does not touch the skywalker coordination prose (4-worker cap, lane disjointness, re-fix caps, anti-cascade block — that removal is CL-6953, explicitly last in the chain). Does not touch worker style/philosophy guidance (CL-6803, human-gated).

Diff is prompt text plus a small amount of structural plumbing to remove a persona-string check — please read the wording, not just the mechanics.

Defects and status

1. Impossible/stale tool references — all present, all fixed

  • tester/testsmith mount READ_TOOLS only (no write tools), but were told "Do not write_file/edit_file product code" / could write "optional notes under tmp/". Removed both — replaced with plain statements that no product-mutation tools exist.
    • tester, before: Do not write_file/edit_file product code. → after: you have no product-mutation tools, so there is no way to patch source to make green.
    • testsmith, before: Design in the report (and optional notes under tmp/ only if the brief allows). → after: Design in the report.
  • critique mounts REVIEW_TOOLS which always includes run_shell, but hedged if shell is allowed.
    • before: Prefer reading tests/callers; if shell is allowed, a tiny sync call that would hang on a Promise is evidence. → after: Prefer reading tests/callers; a tiny sync call via run_shell that would hang on a Promise is evidence.
  • emil/draper/gaasbot explained at length that write tools are denied, when the tool set already makes that true by omission.
    • emil, before: Verify — inspect code, run existing tests/linters when practical. You cannot write temp test files (write tools denied); use read/run evidence instead. → after: Verify — inspect code, run existing tests/linters when practical; use read/run evidence, not temp test files.
    • draper, before: Never write/edit/delete product files. Never spawn. Never commit. → after: Never commit.
    • gaasbot, before: Load philosophy when judgment trade-offs matter. Stay advice-only — no write_file/edit_file/delete_file. → after: Load philosophy when judgment trade-offs matter. Stay advice-only.
    • neckbeard, before: Do not apply fixes. Do not write, edit, or delete product files. Do not spawn agents. → after: Do not apply fixes. (spawn ban covered below)
  • plan mounts REVIEW_TOOLS (no ask_operator, ever) but hedged the tool as "available" sometimes.
    • before: Prefer interview skill when requirements are fuzzy (ask_operator / structured questions when available). → after: When requirements are fuzzy, note the open questions under Blockers instead of guessing — you cannot ask the operator mid-run.
  • Added src/agent/directors/registry.test.ts test "no director prompt references a tool its tool set does not mount" that scans every director's prompt text for known tool-name tokens and fails if a mentioned tool isn't in that director's mounted set (or the small always-mounted/spawn-conditional allowances). Skywalker is excluded — it's the primary session's role text, not a filtered sub-agent.

2. Triple-stated report contract — present, fixed
Every leaf's hand-written trailing line Report: Summary, Findings (...), Blockers, Paths. was a third restatement on top of (a) report.requiredSections (structural) and (b) the shared buildSubAgentReportContract() envelope appended to every sub-agent prompt. Removed the hand-written line from build, bruckheimer, explore, gaasbot, plan, neckbeard, greybeard, intern, shakespeare, tester, testsmith, critique, and emil — kept any domain-specific hint (e.g. "risk/sequencing advice") as a short standalone Findings: ... sentence where it added real information.

Also removed the redundant Never spawn. / Do not spawn agents. / Do not spawn sub-agents. lines from build, neckbeard, brand-reviewer, and draper — each already declares spawn: { maySpawn: false }, which structurally means the task tool is never mounted, and the shared sub-agent appendix already states the no-recursion rule in prose. Never commit. is kept where present — it isn't structurally enforced (these leaves keep run_shell), so it's a real instruction, not a duplicate.

Directors with real per-field report guidance (draper, brand-reviewer, emil's original "# Report" section) were left alone — that's domain content, not a restatement of the headings.

3. Dead no-ship branch — present, fixed
SubAgentDirector.decide in src/subagent/nudge-director.ts branched on stop === "no-ship", but evaluateSubAgentStop (src/subagent/stop-policy.ts) never returns that value — only evaluateThrashStop (which only returns "report-forced" or null) and the explicit never-acted/never-edited/incomplete-report/turn-budget/no-progress paths run. The branch was unreachable. Removed "no-ship" from SubAgentStopReason and from the nudge director's switch/checkpoint mapping. Left ForcedStopReason (a separate type used by forcedStopReport and brief-dispatch.ts's salvage classification, which genuinely does produce/consume "no-ship" reports) untouched — those tests still pass unchanged.

4. Persona-string coupling — present, fixed
shouldRequireEvidence in src/subagent/run.ts armed the empty-readCounts gate via systemPromptRole.includes("CritiqueDirector") — a rename of the persona or its identity-header text would silently change enforcement. Threaded the already-resolved closed-director id (resolvedDirectorId in task-tool.ts) through RunSubAgentParams.directorId and switched the gate to directorId === "critique".

Tests updated

  • src/agent/directors/registry.test.ts — new tool-reference cross-check test.
  • src/subagent/index.test.tsshouldRequireEvidence tests rewritten to assert on directorId instead of constructing/matching systemPromptRole text; added a case for "no directorId resolved".
  • No director package.test.ts needed changes — they already assert against report.requiredSections and tools.allow, not the removed prose.

Gate

bun run check (prettier --check, eslint, tsc --noEmit, build, full test suite) is green: 0 lint errors (1319 pre-existing warnings, untouched by this diff), typecheck clean, build succeeds, 5331 tests pass across 383 files.

…ract, drop dead no-ship branch, structured evidence gate

- Removed hand-written "Report: Summary, Findings, Blockers, Paths" duplicates
  from worker system prompts (build, bruckheimer, explore, gaasbot, plan,
  neckbeard, greybeard, intern, shakespeare, tester, testsmith, critique,
  emil) — the shared sub-agent report contract and each package's
  report.requiredSections already state it.
- Removed "Do not spawn agents"/"Never spawn" duplicates from prompts whose
  packages already declare spawn.maySpawn: false (build, neckbeard,
  brand-reviewer, draper) — spawn is enforced structurally by not mounting
  the task tool.
- Removed impossible tool references: tester/testsmith telling the model not
  to use write_file/edit_file or write notes under tmp/ when those tools are
  never mounted (READ_TOOLS); critique's "if shell is allowed" hedge when
  run_shell is always in its tool set; emil's and draper's write-tool denial
  explanations; gaasbot's write-tool disclaimer; plan's ask_operator mention
  (never mounted on workers).
- Removed the dead no-ship branch: evaluateSubAgentStop never returned
  "no-ship" (only evaluateThrashStop and the never-edited/never-acted paths
  fire), so the nudge director's stop === "no-ship" branch was unreachable.
  Dropped it from SubAgentStopReason and the nudge director's switch.
  ForcedStopReason (a separate type used by forcedStopReport/brief-dispatch
  salvage classification) is untouched.
- Replaced persona-string coupling: shouldRequireEvidence keyed off
  systemPromptRole.includes("CritiqueDirector"); now keys off a structured
  directorId threaded from task-tool's already-resolved director id.
- Added a registry test that cross-checks every director's prompt text
  against its mounted tool set for the known tool names, so a future prompt
  can't reference a tool it cannot call.
@linear-code

linear-code Bot commented Aug 23, 2026

Copy link
Copy Markdown

CL-6807

@TheGreatAxios

Copy link
Copy Markdown
Collaborator Author

Reviewed clean — holding the merge until immediately after the 0.2.106 tag, then this lands first in 0.2.107.

Reason is sequencing, not quality. This rewrites prompt text across 13 directors, which materially affects the inference loop. The new four-tier eval suite (#577) has no baseline yet — the 0.2.106 run establishes it. If this merges first, the baseline silently includes the prompt churn and we lose the ability to measure whether it helped or hurt. Landing it into 0.2.107 means it gets measured against a clean baseline, which is the whole point of resetting the suite.

Nothing to do on your side; I will merge it as soon as the tag is cut.

Resolved against the writePaths removal (#591) and requiredSections
removal (#588): kept this branch's directorId gate key, dropped every
writePaths reference including the registry guard test, since the field
no longer exists.
@TheGreatAxios
TheGreatAxios merged commit cc2dd0c into main Aug 24, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant