Skip to content
Open
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
29 changes: 29 additions & 0 deletions design/prose-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,19 @@ testing.
and the stubs, and returns a transcript. The asserter sees the
transcript, the expected path, and the world delta — never the
reverse.
- **P4b — the asserter is told which substitutions were armed.** The
recorded actions show a stub as the walker writing a file an agent
would have produced; without knowing the stub exists, an asserter
reads that as a missing dispatch and fails a walk that behaved
correctly. Measured: it did exactly that on the first round with
recording live. Armed stubs now reach the asserter with their
triggers.
- **P4c — assert what actions and state can prove.** A claim about
something *displayed* cannot be evidenced by the action log, because
emitting text is not a tool call — it rests on the walker's
narrative, the one piece of evidence it authors itself. Most display
claims have a consequence that can be asserted instead: the answer
consumed, the arm taken, the state produced. Prefer the consequence.
- **P4a — substitutions are declared and marked.** A stub is named
content; the case arming it owns the trigger, so one stub serves
many moments. The walker records `SUBSTITUTED:` for each, and the
Expand Down Expand Up @@ -196,6 +209,22 @@ testing.

## Log

- 2026-07-26 — The first round that found more prose than instrument.
Recording finally live (the blocker was `hasTrustDialogAccepted`, which
gates frontmatter hooks in project subagents and fails silently), five
cases run: one clean PASS, **two confirmed prose defects**, one case of
mine at fault, one framework gap. The defects: every handoff arm was
meant to say "invoke the skill" and four across three entry skills did
not — the walker found one, enumeration found the rest; and
`environment-check.md` opened with "information gathering only" then
told the entry to write and commit the setup document the processing
skill also writes, so whichever ran first won and the other silently
no-opped. Neither is visible to a unit test or the simulation, because
neither reads prose. The framework gap was mine and is closed (P4b):
the asserter saw a stub's Write as a missing agent dispatch. The
residual limit is P4c — display claims have no machine evidence, and
that is a property of the medium rather than a bug.

- 2026-07-25 — First real runs, and the nested-agent shape. The framework
executed end to end for the first time: a Sonnet walker followed
root-cause-validation.md against a live world — real engine calls, the
Expand Down
10 changes: 10 additions & 0 deletions tests/prose/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,16 @@ reason. Write "casing conventions load before the boot pipeline", not
"Step 0.1 before Step 0.2". Anchors in `case.json` are substring
fragments matched against heading text (`#Boot` matches "Step 0.2: Boot").

**Assert what actions and state can prove.** A claim about something
*displayed* — "offers the choice and waits", "emits the summary block" —
can never be evidenced by the action log, because emitting text is not a
tool call. Those claims rest on the walker's narrative, which is the one
piece of evidence it authors itself. Most have a consequence that can be
asserted instead: the answer that was consumed, the arm that was taken,
the state that resulted. Prefer the consequence; keep display claims for
where the display genuinely is the behaviour under test, and expect them
to be the weakest line in the case.

**Scope `files` tightly** — selection and the PR-end suggestion run off it.

## Stubs
Expand Down
4 changes: 0 additions & 4 deletions tests/prose/cases/smoke-boot-in-world/act.md

This file was deleted.

15 changes: 0 additions & 15 deletions tests/prose/cases/smoke-boot-in-world/assert.md

This file was deleted.

6 changes: 0 additions & 6 deletions tests/prose/cases/smoke-boot-in-world/case.json

This file was deleted.

11 changes: 0 additions & 11 deletions tests/prose/cases/smoke-boot-in-world/fixture-state.cjs

This file was deleted.

This file was deleted.

1 change: 0 additions & 1 deletion tests/prose/cases/smoke-boot-in-world/fixture/.recipe-hash

This file was deleted.

This file was deleted.

This file was deleted.

3 changes: 2 additions & 1 deletion tests/prose/lib/prompts.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,12 @@ function walkerPrompt({ worldDir, root, situation, task, scope, stubs, answers }
return `${parts.join('\n\n')}\n`;
}

function asserterPrompt({ expected, world, actions }) {
function asserterPrompt({ expected, world, actions, substitutions }) {
const t = loadTemplate('asserter');
const parts = [fill(t.main, { expected })];
if (world) parts.push(fill(t.world, { expecting: world.expecting, delta: world.delta }));
if (actions) parts.push(fill(t.actions, { actions }));
if (substitutions) parts.push(fill(t.substitutions, { substitutions }));
parts.push(t.transcript);
return `${parts.join('\n\n')}\n`;
}
Expand Down
10 changes: 10 additions & 0 deletions tests/prose/prompts/asserter.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ not have edited or omitted from it. This is what the walk *did*.

{{actions}}

=== substitutions ===

HARNESS SUBSTITUTIONS the case armed for this walk. Where the recorded
actions show the walker doing one of these itself — writing a report an
agent would have produced, for instance — that IS the substitution, and
it is expected. The absence of a real agent dispatch alongside it is not
a missing step.

{{substitutions}}

=== transcript ===

The walk's own account follows under `=== TRANSCRIPT ===`. It is the
Expand Down
8 changes: 7 additions & 1 deletion tests/prose/run.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,13 @@ function cmdAssert(argv) {
+ '(hasTrustDialogAccepted). Do not judge this run.');
}
}
process.stdout.write(prompts.asserterPrompt({ expected: c.assert, world, actions }));
const substitutions = c.stubs.length
? c.stubs.map((s) => {
const stub = cases.readStub(s.name);
return `- ${s.name} — fires ${s.trigger}\n ${stub.description.replace(/\n/g, '\n ')}`;
}).join('\n')
: null;
process.stdout.write(prompts.asserterPrompt({ expected: c.assert, world, actions, substitutions }));
}

// --- snap / verify --------------------------------------------------------
Expand Down