fix(story-mode): name the check that failed, say whose plan a failure was, and when background work finished

From review round 9:
- "another check" was too vague to act on. A check other than the tests
  is named by its tool: a layout check (a formatter), a style check (a
  linter), a type check, the build. "Fix and test, 2 rounds: a test fails
  and Claude fixes it, then a layout check fails and Claude fixes that;
  everything passes."
- A failure the prompt asked for reads "The first run fails on purpose,
  as you asked; the second passes." ("The tests don't even load, as you
  asked."); one Claude planned itself keeps "Test first: … as planned".
- A test run sent to the background that reported in a later turn reads
  "Claude starts the tests in the background; later they pass.", not as
  if they passed in that turn.
- The test board says "1 test" and, when nothing loaded, "1 error".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer committed 2026-10-18 15:45:17 +02:00
1 parent 59d70caeb3
commit 555efef53d
9 files changed
+66 -35

No files matched your search

@@ -221,7 +221,7 @@
{
"kind": "check",
"ok": true,
"story": "The tests run in the background and pass.",
"story": "Claude starts the tests in the background; later they pass.",
"director": "python3 -m pytest -q → ok",
"balloon": null,
"tests": null,
@@ -73,7 +73,7 @@
{
"kind": "check",
"ok": false,
"story": "The tests don't even load, as planned.",
"story": "The tests don't even load, as you asked.",
"director": "python3 -m pytest -q → error · 0 passed, 1 error",
"balloon": "Now the red run, once, with calc_stats.py confirmed absent:",
"tests": {
@@ -51,7 +51,7 @@
{
"kind": "loop",
"ok": true,
"story": "Test first: the first run fails as planned, the second passes.",
"story": "The first run fails on purpose, as you asked; the second passes.",
"director": "2 checks · 3 edits · +35 −1 · last: ok",
"balloon": "All 4 pass.",
"tests": {
@@ -133,7 +133,7 @@
{
"kind": "loop",
"ok": true,
"story": "Test first: the first run fails as planned, the second passes.",
"story": "The first run fails on purpose, as you asked; the second passes.",
"director": "2 checks · 2 edits · +5 −1 · last: ok",
"balloon": "All 5 pass.",
"tests": {
@@ -73,7 +73,7 @@
{
"kind": "loop",
"ok": true,
"story": "Build and test, 4 rounds. The tests pass every time; another check fails once, then passes.",
"story": "Build and test, 4 rounds. The tests pass every time; a layout check fails once, then passes.",
"director": "5 checks · 10 edits · +694 · last: ok",
"balloon": "All 4 modules are done: 63 tests pass and ruff is clean.",
"tests": {
@@ -51,7 +51,7 @@
{
"kind": "loop",
"ok": true,
"story": "Fix and test, 2 rounds: a test run fails, then another check, then everything passes. Claude saves the work in 4 commits along the way.",
"story": "Fix and test, 2 rounds: a test fails and Claude fixes it, then a layout check fails and Claude fixes that; everything passes. Claude saves the work in 4 commits along the way.",
"director": "3 checks · 5 edits · +218 −1 · last: ok",
"balloon": "All 19 tests pass, ruff check is clean, and the output matches real wc column for column.",
"tests": {
+5 -3
View File
@@ -77,7 +77,7 @@ describe('Story Mode on real sessions', () => {
expect(panel('python-cli-fix-loop', 0, 'loop')).toMatchObject({
// The failing test was a real bug, the second failure only ruff: said apart, with the 4 commits made inside.
story:
'Fix and test, 2 rounds: a test run fails, then another check, then everything passes. Claude saves the work in 4 commits along the way.',
'Fix and test, 2 rounds: a test fails and Claude fixes it, then a layout check fails and Claude fixes that; everything passes. Claude saves the work in 4 commits along the way.',
roundsOk: [false, false, true],
});
});
@@ -131,7 +131,7 @@ describe('Story Mode on real sessions', () => {
it('tests that do not load, a commit then a push in its own command, a push with a failing step after it', () => {
const [first, second, third] = pagesOf('errors-push-append');
expect(first.panels.find((p) => p.kind === 'check')?.story).toBe("The tests don't even load, as planned.");
expect(first.panels.find((p) => p.kind === 'check')?.story).toBe("The tests don't even load, as you asked.");
expect(second.panels.find((p) => p.kind === 'loop')).toMatchObject({
story: 'Test first: the first run fails as planned, the second passes.',
roundsOk: [false, true],
@@ -155,7 +155,9 @@ describe('Story Mode on real sessions', () => {
expect(report.helpers).toBe(0);
expect(report.panels[0].story).toBe('A background job reports back.');
}
expect(pages[3].panels.map((p) => p.story)).toContain('The tests run in the background and pass.');
expect(pages[3].panels.map((p) => p.story)).toContain(
'Claude starts the tests in the background; later they pass.'
);
// A python script that rewrote README.md counts as a change, beside the two files written.
expect(pages[3].panels.map((p) => p.story)).toContain('Claude changes 3 files.');
});
+2 -2
View File
@@ -742,7 +742,7 @@ describe('buildStoryTurns', () => {
);
// Not tests that ran during that turn: they ran later, in the background.
expect(first.panels.find((p) => p.kind === 'check')!.caption.story).toBe(
'The tests run in the background and pass.'
'Claude starts the tests in the background; later they pass.'
);
const second = buildStoryPage(turns[1]);
expect(second.panels.find((p) => p.kind === 'check')).toMatchObject({
@@ -1194,7 +1194,7 @@ describe('buildStoryPage', () => {
const loop = page.panels.find((p) => p.kind === 'loop')!;
expect(loop.roundsOk).toEqual([true, false, true]);
expect(loop.caption.story).toBe(
'Build and test, 2 rounds. The tests pass every time; another check fails once, then passes.'
'Build and test, 2 rounds. The tests pass every time; a layout check fails once, then passes.'
);
});