From review round 2 (a long turn, and a prompt added mid-turn):
- Commits fold last and a montage names them ("saves the work in 3
commits"); a save counts its commits ("saves the work in 6 commits").
Before, all three commits of a 16-step turn vanished into montages that
read "runs 2 commands".
- Folding a full page starts at the oldest pair and grows a montage already
there, and never takes the newest panel while another choice exists: a
live page no longer reshuffles panels the reader is watching.
- A check whose output reports problems ("1 file would be reformatted",
"Found 3 errors", "✖ 5 problems", `error TS…`) failed, though a trailing
`; ls` hid the exit code: "The tests pass, but another check fails."
- Shell structure is not a command (`for x in`, `do`, `done`, `if`), so a
`--version` probe in a loop reads as looking; `command -v` and
`git config --get` too.
- The montage draws only the kinds of steps it folded, with a box for
saves; the loop's arrow has a head and rests on a finished page; the
"MISSION COMPLETE!" title has room for its tilt; drawer commands keep
their line breaks.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Files a Bash call writes through here-documents (`cat > f <<'EOF'`, `tee`)
are whole-file edits on the event: in the diff drawer, on the code page,
in "N files changed". Before, a worker that wrote seven files that way
drew a page with zero files changed. Labels leave the bodies out (the wire
row was a wall of file text), and a relative target resolves against the
transcript line's cwd unless the command changed directory first.
- A caption names the part of a one-liner that decided it (`node --test`,
the commit), not its first line.
- The advisor (a server tool, answer encrypted in the transcript) is drawn:
"Claude asks the advisor for a second opinion." It is not counted as a
helper. Server web search maps to the web panel.
- Checks that all pass from the start read "Build and test, N rounds. Every
check passes.", not as fixing.
- pytest's collection errors are counted apart: "The tests don't even load."
- A result remark replaces a step announcement on its check.
- A turn still open when the next prompt starts has ended; an interrupted
turn is timed by its span.
- The mission drawer keeps prompts up to 4000 characters.
- Drawer: diff headers, titles and commands read relative to the session
folder (the folder itself reads "."); a bare name under an `ls dir:`
header links into that folder; the chosen answer is marked, not struck
through; boards count errors.
- While a question waits, the pencils say "Waiting for your answer…";
balloons start below the LIVE tag; the loop's balloon points at its
verdict, above the board.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
From review round 2 (a question, failing tests first, and a push):
- A run that reported failed tests is now failed on the event itself, so
its drawer and wire tags agree with the panel (the TDD red run was a red
panel with a green "ok" tag; `; echo "exit=$?"` had hidden the exit code).
- A save whose command failed says so ("Claude tries to send the work up,
and the command fails."), stamps ERROR instead of SAVED, and counts the
error in the Director's cut. A save still running stamps nothing.
- A loop ends at its last check: README, .gitignore and the like written
after the tests passed are their own fix panel instead of disappearing
into the loop's diff.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Found by replaying round-2 review transcripts while the reviews ran, then
surveyed across 150 local transcripts:
- A prompt typed while Claude works writes no user line in current builds,
only a queued_command attachment where Claude took it in (602 of them
locally, 485 of those background-task reports). It now joins the running
turn; a report that lands mid-turn is a notice on the wire, not a new
mission panel, and a background helper's answer joins back from it.
- Some sessions never write turn_duration (13 of 135 locally): the Stop
hook's stop_hook_summary (never preventedContinuation in 815 seen) now
ends the turn, timed by its own span. Before, such a session's turns
never ended.
- A helper started as a teammate (teammate_spawned) is a background helper;
its <teammate-message> reports (text or a JSON idle notification) join it
by name, and the turn they start reads "readme-writer: README done".
- A <cross-session-message> names its sender; pasted text loses Claude
Code's <pasted_content> wrapper (it showed in balloons and the picker).
- The mission panel, its balloon and its drawer follow who sent each
prompt (you, a background job, another session), not who started the turn.
- A caption names a script by its first line that says something, not
`set -e`, a bare `cd` or `S=/tmp/...`.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Three review agents watched real Claude Code workers build small programs
and compared every panel with the transcript. Replaying their transcripts
through the core found, and now fixes:
- A Bash call was classified from its 400-character display clip, so a
commit or a test run late in a long one-liner vanished (a failing pytest
run drew as "saves the work"). The intent, the printed files and whether
a save ships are now read from the whole command.
- A test run that reports failures failed, even when `| tail` hid the exit
code. With that, the real fix-test-fix-test sequence folds into one loop:
"Fix and test, 2 rounds. It passes in the end."
- node:test (`# pass 7`), all-failing pytest (`3 failed in 0.1s`) and
pytest errors are counted.
- A helper started in the background was drawn as finished, with Claude
Code's internal launch notes as its answer. It now stays running until its
task notification arrives; the answer, tool count and duration are joined
back onto it, and the final panel says when helpers are still out.
- The notification turn reported Claude Code's own duration, counted from
the first prompt (2 min 38 s for a 21 s turn); a turn is capped at its span.
- Logging into scratch (`| tee "$S/dry.txt"`, `> /tmp/x`) counted as an edit;
redirect targets are read quote-aware, and a here-document's body is data.
- A probe (`pytest --version`) reads as looking around, not running a tool.
- A plan Claude writes as a numbered list (no plan tool, as in current
Claude Code builds) is drawn as a plan panel; files it cats are file cards.
- A balloon: code spans stay whole, an ellipsis or e.g. does not end the
sentence, a remark about the check that just ran stays on that check, and
a loop shows the last word of its rounds.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
GET /api/sessions/:id/story/helpers/:agentId draws one helper (an Agent call)
as a page from its own transcript, <conversation>/subagents/agent-<id>.jsonl
beside the session's, with the type, description and finished flag from its
.meta.json. The file is derived from the session's own transcript path and an
id checked against [A-Za-z0-9_-]{1,64}; a helper with no file yet is page
null, not an error. The reducer gains a sidechain mode (every helper line is a
sidechain line) and keeps the agentId of Agent results, so the main page links
each helper to its steps. Same gates and ETag/304 as /story, now shared.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Found by watching real workers: `node --test` was drawn as plain terminal work,
`git -c user.name=x commit` was not seen as saving, and a probe such as
`pytest --version` became a failing test. Commands are now split into their
simple commands (quote-aware, at && || ; | and newlines); each part is judged by
what it starts with after env assignments and wrappers like timeout and sudo,
git's own -c/-C options are skipped, a --version/--help probe is not a run, and
the strongest intent wins (test, save, build, edit, run, look). File
housekeeping (mkdir, touch, cp, mv, rm) and git init/add are plain commands.
Balloons dropped every _ and * as markdown (test_wcount.py read testwcount.py);
now only paired emphasis at word boundaries and line-start markers go.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Story Mode is an opt-in header button (showStoryButton, per device, default
off, kept off phones) that opens the active Claude Code session as comic pages:
one page per prompt, panels for reading code, planning, helpers, edits, tests
(POW/KRAK), questions and saving, each with a plain story caption and a
Director's cut caption naming the tools. Files open in the existing file
preview, the code page opens the exact diff, a test board opens the output,
and a running turn redraws as Claude works.
Server: GET /api/sessions/:id/story (experimental) reads a bounded tail of the
session's own transcript. A pure core (src/story-mode.ts) reduces lines,
joins tool calls with their results, splits turns at prompts and
turn_duration, and groups events into at most 12 panels; Bash calls are
sorted by what the command does. The reader (src/story-reader.ts) caches
reduced entries and reads only appended bytes while the file grows.
Frontend: story-mode.js (canvas frame engine, live refresh from transcript and
hook SSE events plus a poll while a turn runs, details drawer, the wire) and
story-mode.css, with two self-hosted OFL comic fonts.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>