Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions .claude/agents/prose-orchestrator.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@ name: prose-orchestrator
description: Runs one prose-test case end to end — builds the world, dispatches the walker, computes the delta, dispatches the asserter, reruns a failure from a fresh world to confirm it, destroys the world — and returns just the verdict. Dispatched by the /prose-test skill, one per case.
tools: Bash, Read, Agent
model: sonnet
hooks:
PostToolUse:
- matcher: "Bash"
hooks:
- type: command
command: "node \"$CLAUDE_PROJECT_DIR/tests/prose/lib/record-action.cjs\""
---

# Prose Orchestrator
Expand Down
11 changes: 9 additions & 2 deletions tests/prose/lib/record-action.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

// The hook that records what prose-test agents actually do.
//
// Declared in the frontmatter of prose-walker, prose-orchestrator and
// prose-asserter, so it fires only while one of them is active. The
// Declared in the frontmatter of prose-walker and prose-asserter, so it
// fires only while one of them is active. The
// agents play no part in it: they cannot forget an entry, summarise one
// away, or write it late — which is the whole point. Walkers were
// repeatedly found doing a full walk and reporting only its tail, and
Expand Down Expand Up @@ -201,6 +201,13 @@ function main() {
// every other event is absent from it — resolve it from the transcript.
const stop = event === 'Stop' || event === 'SubagentStop';
const traced = stop ? fromTranscript(payload.agent_transcript_path) : null;
// The world log is the walker's record. The orchestrator shares this
// hook, and its own commands carry world paths in their payloads — the
// world it just built, the prompt it just emitted — so without this
// gate its rows land in the walker's log ahead of the walk itself,
// padding the record and handing the checks substrings no walk ran.
if (!agent.includes('walker')) return;

const found = JSON.stringify(payload).match(WORLD);
const world = found ? found[2].replace(/\\+/g, '') : traced && traced.world;
if (!world || !fs.existsSync(world)) return;
Expand Down
15 changes: 15 additions & 0 deletions tests/scripts/test-prose-record-action.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,21 @@ describe('prose recorder — tool events', () => {
assert.ok(row.includes('Usage: engine <command>'), 'and says what came back');
});

it('keeps the orchestrator out of the walker\'s record', () => {
// The orchestrator's own commands carry world paths — the world it
// just built, the prompt it just emitted — and its rows were landing
// in the walker's log ahead of the walk itself, padding the record
// and handing the checks substrings no walk ran.
fire({
hook_event_name: 'PostToolUse',
tool_name: 'Bash',
agent_type: 'prose-orchestrator',
tool_input: { command: 'node tests/prose/run.cjs world some-case' },
tool_response: { stdout: `{"world":"${world}","case":"some-case"}` },
});
assert.deepEqual(logLines(), [], 'no orchestrator row reaches the world log');
});

it('ignores anything happening outside a world', () => {
fire({
hook_event_name: 'PreToolUse',
Expand Down