Commit Graph
3221 Commits
Author SHA1 Message Date
Codeman maintainer a80638308b fix(story-mode): a second opinion is a line in the next panel, not a panel of its own
From review round 9: "Claude asks a stronger AI model for a second
opinion" stood as a whole panel on almost every page, and the reader
never learns what it said (the answer is encrypted). An advisor call now
rides on the panel of the step after it (or the last one, when nothing
follows), counted in `secondOpinion`: a small line under the caption,
"after asking a stronger AI model for a second opinion" ("then asks…"
when it came last), and "· after 1 second opinion" in the Director's
cut. Pages lose a panel of noise and keep room for the work.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 15:49:45 +02:00
Codeman maintainer 555efef53d 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>
2026-10-18 15:45:17 +02:00
Codeman maintainer 59d70caeb3 fix(story-mode): Claude's last words keep the sentences aimed at you (review round 9)
Two fresh cold readers (the person who asked for the work, a newcomer to
the project) found the round-8 balloons still cut exactly where Claude
turns from describing to asking, offering or warning: "Want me to add
that?", "I can fix the number format whenever you want", the
http.server instruction, the fix that followed a reported bug.

Key sentences are now picked by kind, one of each, at most three, and
shown in the order Claude wrote them: a problem left (with the fix right
after it, when there is one, so it never reads as open), a question or
offer, how to use what was built, something not done or checked yet, a
step that failed, a word on what you asked for, a corrected warning. A
list intro is judged on its own words and brings its first items; a
pointer ("described below") is skipped; a short offer brings what
"that" was.

The final panel reads "Done, with a catch" for a problem left, "Done,
with a note" for something pending, and "Done, with a question" when
the next move is yours (`openNote` is now 'catch' | 'note'; `asks`).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 15:41:57 +02:00
Codeman maintainer 9c81e4c425 docs(story-mode): what may be drawn on a canvas, since Save image shares it
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 15:32:49 +02:00
Codeman maintainer e892278abc feat(story-mode): a run panel shows what its command printed
From review round 8: you asked to see a dry run, Claude's balloon said
"Here's the dry run you asked for", and the page showed nothing of it.
A run panel's terminal card now prints, dimmer under the commands, the
first lines its last command printed (the last ones when it failed:
that is where an error is).

The card is drawn on the canvas, so it goes into a saved image someone
may share: a line that looks like a secret (token, password, api key,
credential, inside identifiers too) is left out, addresses and long
token-like strings are masked, and Claude Code's own markers ("(Bash
completed with no output)", tool-error tags) are dropped.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 15:32:36 +02:00
Codeman maintainer b19317175e fix(story-mode): a stop says where it landed and what was saved before it
From review round 8: "STOPPED" with the balloon "Step 3: new tests under
test/." read like progress, and nothing said what the stop left. The
final panel now names the step Claude last announced and the commits made
before the stop: "You stop Claude here, during its step 3. Saved before
the stop: 2 commits."

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 15:27:30 +02:00
Codeman maintainer 88040c997d fix(story-mode): no celebration when the turn is not a clean finish
From review round 8: "Mission complete!" with confetti and cheering
helpers sat over balloons saying the page will not work as opened, a
helper's tests have not run, or a problem is left. The final panel is now
calm (no confetti, a neutral robot) whenever work was still out at the
turn's end, nothing worked, or Claude's last words name an open problem
(`page.openNote`: a key point about something wrong, not done, not
checked or not fixed); the last of these reads "Done, with a catch".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 15:23:19 +02:00
Codeman maintainer faca7571e2 docs(story-mode): keyPoints and finishedLater
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 15:18:07 +02:00
Codeman maintainer 6a7d8393dc fix(story-mode): what the cold readers believed that was not so (review round 8)
Two agents read only the comic of the eleven recorded sessions (one who
has never written code, one engineering manager skimming), wrote down
what they believed, then compared with the transcripts. Both were misled
in the same places, and the cause was the same: the final panel kept
Claude's first sentence or two, and what a reader must not lose sat
further into its last message.

- Claude's last words now keep key sentences from anywhere in its final
  message (read in full, past the label's clip): an open problem or a
  quirk, something not yet done, checked or confirmed, work still out,
  what you asked for or expected, a correction of an earlier warning
  (with the sentence it refers to). A bare "I didn't" counts only when
  nothing stronger is there; table rows, list intros and questions never.
  "I built the pomodoro timer. Opening the page: it won't work if you
  double-click index.html…"; "…That note makes the current behaviour
  clear, but it doesn't fix it."; "As you expected, git describe --tags
  then failed because the repo has no tags."
- A turn that ended with helpers or a background command still out says
  so: "Done for now", and "Claude's part is done in 36 seconds. Its 2
  helpers finished later and reported back." (`finishedLater`).
- A failure you asked for, or that Claude said it expected, reads as the
  plan: "Test first: the first run fails as planned, the second
  passes.", "The tests don't even load, as planned." A loop that failed
  once on a test and once on another check says both, and the commits
  made inside a loop are part of its story; the final panel counts the
  turn's commits ("5 commits, uploaded").
- One or two files read are named ("Claude checks README.md." in a
  turn a report started); a tool lookup reads "Claude gets the tools it
  needs ready." again (it was overwritten) without its query; the
  advisor is "a stronger AI model", and one that answered with an error
  "gets no answer"; a commit message loses its `feat:` prefix in the
  plain caption.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 15:17:54 +02:00
Codeman maintainer 8daa158e21 docs(story-mode): the background event field
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 14:52:31 +02:00
Codeman maintainer 61e1ad6454 test(story-mode): two more real sessions in the corpus; background runs and script writes told right
Round 6's two sessions join the corpus (sanitized like the others, and
checked to draw the same pages as the originals): tests that do not load,
a fix-fail-fix-pass loop, a commit then a push in its own command, a push
with a failing step after it, an append; two helpers and a command in the
background whose reports come back as turns of their own.

Replaying them showed two captions to fix:
- A test command that ran in the background and reported back read "The
  tests pass.", as if they ran during that turn: "The tests run in the
  background and pass." (events keep `background`).
- A python script that rewrote README.md drew as "Claude works in the
  terminal." beside a separate change panel. A plain command that writes
  project files (a script fed by a here-document, or one Claude Code says
  changed files) is an edit: "Claude changes 3 files."

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 14:52:20 +02:00
Codeman maintainer a6a28d66a6 docs(story-mode): the changeset no longer promises an exact diff for every write
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 14:46:57 +02:00
Codeman maintainer d2d58a5239 docs(story-mode): the Bash share measured on every local transcript
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 14:45:59 +02:00
Codeman maintainer 42d1c75a19 fix(story-mode): the rest of the PR review: lookups, polling, frame budget, linear links
From review round 7 (PR review):

- With no hook path (normal after a server restart), the transcript was
  looked up by conversation id only; it now also tries the Codeman id and
  a recovered session's `restored-<fragment>` transcript, as
  last-response does, and keeps what it found briefly (a lookup reads
  every project folder; a miss is kept only 4 s).
- A session Story Mode cannot draw is not polled, and a page with nothing
  drawn yet is polled like a finished one (every 15 s), not every 4 s.
- Helper steps: one request per helper is drawn, the newest; a failed
  refetch never writes over steps already shown.
- The Director's cut toggle, a skin change and the fonts arriving redraw
  within the frame budget, not every panel in one task.
- A bare name under an `ls` header finds its folder in one pass over the
  lines; a search per link over the text before it was quadratic (about
  1.5 s for 700 links on agent output).
- A page of many questions still keeps to 12 panels: questions fold last.
- Opening Story Mode while it is open never starts a second runtime; the
  status announcer carries data-i18n-skip; the turn key in the ETag is
  clipped.
- The crafted-input timing test runs at sizes where the old code took 6
  to 29 s and the new one under 10 ms, with a 1.5 s bound.
- Docs: `older`, `origin` (`scheduled`), which route answers a 500;
  mobile.css's stale comment; an orphaned doc comment.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 14:44:37 +02:00
Codeman maintainer e7ce3b8e56 fix(story-mode): live helper steps behind an unchanged page, a clean reopen, no wedged view
From review round 7 (PR review):

- A helper's drawer froze while the helper worked: a background helper
  writes only to its own transcript, so the parent page answered 304 and
  the refresh returned before anything refreshed the drawer. An unchanged
  page now still refreshes an open drawer's working helpers (ETag'd).
- A helper messaged twice read as superseded ("The trail stops here…"):
  its one-page run took its ending from the first turn. It takes the last.
- More than 60 turns in a small file listed the newest 60 with no hint;
  `older` is now set whenever the list leaves turns out.
- One /story request that never settled wedged the view, even across a
  reopen. Requests carry a token and a 30 s timeout, and opening resets
  the request state, with what was announced and shown.
- The background canvases (megabytes each at 2x) are released when Story
  Mode closes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 14:38:13 +02:00
Codeman maintainer 91ee063fc2 test(story-mode): another user's session is a 404 on both Story routes
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 14:35:01 +02:00
Codeman maintainer 250a77d945 fix(story-mode): the reader cannot stall the server, and reads only real transcripts
From review round 7 (security):

- Eviction past the per-transcript caps dropped entries one shift() at
  a time after the whole tail was parsed: quadratic and never yielding
  (a 2 s event-loop stall on a 70 MB long turn, 51 s from 300k entries).
  It is one splice, done at every 1 MB yield, so the peak stays near the
  caps; and a window that saw a turn start which was then trimmed away no
  longer widens to a bigger tail that would be trimmed the same.
- A FIFO or a device in place of a transcript or a helper's .meta.json
  (stat said size 0, then open() waited for a writer) pinned a libuv
  worker per request; four of them hang every fs, dns and crypto call on
  the server. Files are now opened non-blocking, checked with the
  handle's own stat (a regular file only) and read through that handle.
- The cache held 6 transcripts for everyone, so a seventh open view made
  every poll a full re-read, and a 304 was decided only after reading. An
  unchanged file is now a 304 from a stat alone, and the cache holds up
  to 24 transcripts within 30 million characters.
- Story Mode is the first route to send a transcript's content to the
  browser, so the hook-reported path is used only when it really lies
  (symlinks resolved) in a Claude projects folder: the session's
  relocated CLAUDE_CONFIG_DIR (now also used for the lookup by id) or the
  server's. Anything else falls back to the lookup by conversation id.

Tests move their transcripts into the (temp) projects folder and cover a
path outside it, a symlink out of it, a FIFO transcript, a FIFO meta file
and a 100,000-entry tail.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 14:34:28 +02:00
Codeman maintainer 7aa7106cb4 docs(story-mode): this round's fields and the invariants behind the frontend fixes
API reference: `roundsOk` carries `null` for a run still going, a
background command's `output`, script writes in `edits`, what `helpers`
counts, and the page text budget behind `truncated`.

src/web/CLAUDE.md: panels kept by identity, the balloon text span the
clamp must target, the drawer's unchanged-refresh skip and held turn, the
drawer under the bar and the inert page behind a full-width one, and text
tokens versus accents.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 14:11:51 +02:00
Codeman maintainer 21de40309d fix(story-mode): no input shape blocks the server: linear tag and commit scans
Every string Story Mode matches is agent-written (and an agent can be
prompt-injected), and one regex call blocks the single-threaded server.
Timing each helper at its own input cap with crafted shapes found five
that were quadratic or worse:

- A lazy `<tag>[\s\S]*?</tag>` rescans to the end for every opener that
  never closes: system reminders, teammate messages and task-notification
  fields took 1.5 to 29 s on 0.5 to 1 MB of prompt text. They are found
  with indexOf now (withoutBlocks, blocks, blockText1).
- The commit message regex backtracked over many `git commit` tokens
  (290 ms at 4,000 characters, growing x8.7 per doubling) and ran on the
  whole command. It is a bounded line-by-line scan with the same rules.
- The "This command modified …" note was searched with a lazy match per
  repeat of the phrase (7 s on 1 MB of output). It is one anchored try
  at its last occurrence, where Claude Code appends it.
- firstSentence and planFromText now read only the start they need.

The worst shape is now under 10 ms at 1 MB. A test times five of them at
sizes where the old code took a second or more.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 14:11:01 +02:00
Codeman maintainer 67595ea980 perf(story-mode): a page budget that halves the longest live pages
A sweep of the Story core over every local transcript (1,311 files,
3,855 turns: no errors, never more than 12 panels, the slowest page 12
ms) found pages up to 551 KB (157 KB gzipped), refetched every few
seconds while a long turn runs. The page text budget (outputs, hunks,
helper texts; the oldest events give theirs up first) goes from 400,000
to 120,000 characters: 94% of turns never reach it, and the longest page
is now 74 KB gzipped.

An edit whose lines the budget dropped now says so ("left out to keep a
long turn fast"), instead of the wording for a file a script wrote.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 14:03:01 +02:00
Codeman maintainer 99c15777f5 test(story-mode): a too-tall balloon clamps its text, not the hidden speaker words
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 13:55:28 +02:00
Codeman maintainer b739e8e59a fix(story-mode): balloons clamp again, and the loop's stays above its board
The speaker words added for screen readers became the balloon's first
child, and the clamp took that element instead of the text: long balloons
were no longer cut to fit and ran over the art (the loop's covered the
test count). The clamp now finds the text by its class.

The loop has no Claude to keep clear of on the left, so its balloon may
run wide and is clamped down to one line rather than over the board it
points at; on a 400 px panel "All 19 tests pass…" now ends above "19
tests".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 13:55:01 +02:00
Codeman maintainer 6e45831909 fix(story-mode): read right by a screen reader, and the last of review round 6
Accessibility (review6-c):
- A panel is named "Panel 2 of 7" and its caption is read once, as its
  content (it was the name and the content both).
- Balloons say who speaks, visually hidden ("Claude says: ", "You say: ",
  "Claude asks: ", "Notice: "); Save image leaves those words out.
- The overlay is named "Story Mode: <session>", the drawer by its kind and
  title ("Diff 2 files"), the final panel's chips by what they open, and
  the wire's panel labels are headings.
- While a file preview opened from a panel is up, the overlay is not
  aria-modal (the preview sits beside it), and is again once focus returns.
- The focus ring on a click region gets a light band under its dashes; on
  the loop's magenta halftone it vanished.

From review6-b:
- "Mission complete!" no longer shows while a helper or a background
  command is still out ("To be continued…").
- A background command's drawer says "Started in the background…", then
  "Finished in the background, exit code 0…", instead of the launch note
  meant for Claude ("do not poll that file").
- `sed -n '5,25p' README.md` is a printed file, offered in the drawer.
- A report turn no longer counts the helper it answers as one it started.
- A helper's `cd /work/x && ls` keeps its folder as ".".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 13:51:11 +02:00
Codeman maintainer 89c222086a fix(story-mode): text contrast of at least 4.5:1 in both skins
From review round 6 (accessibility): in the paper skin the accents were
used as text colours, and yellow, light green and pink on white fell to
1.42 to 4.48:1 (the "running" tag, the pencils' "Claude is working…",
diff markers, ok/error tags, +N/−N counts, "You approved it", LIVE); in
the dark skin the panel's LIVE tag (white on pink, 3.09) and the note
balloon's "Notice" label (2.74) were under too.

Text now takes its own tokens (--story-go-text, -bad-text, -wait-text,
-pencil-text, the LIVE tag's pair, -note-ink): the accents themselves in
the dark skin, darker ones in the paper skin. Fills and borders keep the
accents. Every pair measured 4.87:1 or better, the tinted diff rows
included.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 13:46:21 +02:00
Codeman maintainer 3cdcfdd8be chore(story-mode): lint clean (control-character regexes, a case block)
Two control-character regexes (the NUL span markers, terminal escape
codes) get their eslint-disable directive on the line it covers, and the
montage case gets its own block for its declaration.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 13:43:20 +02:00
Codeman maintainer d4df9b18b2 fix(story-mode): a drawer stays with its turn, focus never falls to the page, live loop marks
From review round 6:

- An open drawer was not tied to its turn: when the newest turn moved on,
  a helper drawer went blank and a report drawer silently became the next
  turn's prompt. The drawer now remembers its turn; when the newest turn
  moves on, the page holds that turn (the picker lists the new one, and
  the status line says so) until the drawer closes, then follows the
  newest again. Picking a turn yourself wins.
- Previous on the second turn (or Next onto the last) disabled the
  focused button and dropped focus to the page body. Focus moves to the
  turn picker first.
- The drawer covered the right half of the bar at desktop widths; it now
  starts under the bar there too. Where it covers the whole page (a
  narrow window, a phone) the page behind it is inert, so Tab no longer
  walks the panels it hides.
- A live loop shows the runs that finished, and the one still going as an
  open circle (`roundsOk` carries `null` for it).
- The diff of a command that also printed something (`cat >> NOTES.md
  <<EOF ... cat calc_stats.py`) shows what it printed, with "Open
  calc_stats.py".
- "Red as expected: collection error." stays on its check, not the save
  after it. Parked on an older turn, a new turn is announced. A new panel
  is sized as it is inserted (no empty first frame). The turn picker is
  44 px on touch screens, and the diff drawer no longer says "Live diff"
  beside its LIVE tag.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 13:42:13 +02:00
Codeman maintainer 7f2304725b fix(story-mode): a live drawer that holds still, and panels that keep their place through a fold
From review round 6:

- An open drawer rebuilt its body on every refresh, unchanged or not (11
  rebuilds in 25 s, 7 identical), so a focused button inside it fell to
  the page body and a text selection was cleared. A refresh that changes
  nothing now keeps the nodes; a real change rebuilds and gives focus back
  to the control that does the same thing. Helper steps are filled after
  that decision, so a skipped rebuild costs no request and a helper still
  working still refreshes its steps.
- When a fix folded into a loop under an open drawer, Escape left focus on
  the body: the opener (`diff@fix:1`) was gone. Its replacement is found by
  view and panel position (`diff@loop:1`).
- Panels are matched by identity (kind and first event), not position: a
  fold into a montage near the start of a long live turn shifted every
  later index and rebuilt all eleven panels with their entrances. Only the
  montage is new now.
- An edit with no lines (a script, or a command Claude Code says changed
  the file) says so: "What changed is not in the transcript: a script or
  another program wrote this file."

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 13:37:31 +02:00
Codeman maintainer c5ee5aaf0a fix(story-mode): a push in its own command is not a commit
From review round 6: commit, then push as a second command (the most
common way to ship) drew as "Claude saves the work in 2 commits and
uploads it." for one commit, and the commit's message was dropped. A
command's `commits` was only recorded above one, and the save panel then
counted every save command as one. It is now recorded whenever a command
commits and is the only thing counted: "Claude saves the work and uploads
it: "feat: add mode()"." A push of earlier commits reads "Claude uploads
the work."

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 13:33:41 +02:00
Codeman maintainer c36700a9c0 fix(story-mode): a file a python or node script rewrites counts as changed
From review round 5: Claude rewrote textstats.py, the change the user
asked for, through `python3 - <<EOF ... open(p, "w").write(s)`, and
the page listed only the two files the same command wrote with `cat >`.
A script fed to python or node by a here-document is now read for the
files it opens for writing (`open(target, 'w'|'a'|...)`, `Path(...)
.write_text`, `writeFileSync`), with the target a string literal or a
name bound to one (`p = "textstats.py"`); anything computed is left
out, never guessed, and scratch targets are skipped. They are edits
without lines, like the files Claude Code says a command modified,
since what the script changed is not in the command.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 13:18:13 +02:00
Codeman maintainer b220d6d6d7 test(story-mode): the compact panel's drawer, and the poll while parked on an older turn
- The compact panel's click region opens what was packed up, with the
  compaction's token counts, not an empty output.
- Parked on an older turn, the poll asks for that turn, the picker learns
  of a third turn ("Turn 1 of 3"), the reader stays on turn 1 and nothing
  is announced.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 13:14:08 +02:00
Codeman maintainer d92e927184 fix(story-mode): focus returns to the chip that opened the drawer; document this round's fields
- The final panel's chips were rebuilt on every redraw, so the chip that
  opened a drawer was gone by the time it closed and focus fell to the
  page body. Unchanged chips are now kept, and a trigger replaced anyway
  hands focus to the control that opens the same thing.
- API reference: `older`, background Bash calls (`running`), `subject`,
  append hunks (`atEnd`), `files` resolved against the command's folder,
  test counts read before the output is cut, `roundsOk`, the `note`
  speaker and `noneWorked`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 12:57:58 +02:00
Codeman maintainer 6a1bac6ebe fix(story-mode): appends without invented line numbers, printed files you can open, whose words start a turn
The rest of review round 5 (a phone with touch):

- Text appended with `cat >> f <<EOF` or `echo x >> f` was drawn as lines
  1 to 8 of the file; where an append lands is not in the command, so the
  diff now says "Added at the end of the file (8 lines)" with no numbers.
- A command that prints a file and does something else (`cat textstats.py
  && pytest`) kept no record of the file; it is now on the event,
  resolved against the command's own folder, and the output drawer offers
  "Open textstats.py". (A file rewritten by a python here-document still
  cannot be diffed from the command alone; Claude Code's own "modified"
  note, when it comes, is used.)
- A background job's report that starts a turn was a balloon credited to
  "you" in the API (the page already drew it as a note); its speaker is
  now "note".
- The output drawer tagged a command still running in the background
  "ok"; it says "in the background".
- The Header buttons hint says that on a phone, Story Mode opens from
  Session Options.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 12:55:43 +02:00
Codeman maintainer 76d5c61eda fix(story-mode): a three-row bar on phones, buttons for panels out of view, loop words that match the marks
- The bar on a phone took four rows (216 px of a 664 px screen) with the
  close button alone on the last; it now takes three (170 px): title and
  close, the turn, the view and Save image. The drawer starts exactly
  under it and follows it when the phone turns (the bar is observed).
- A panel below the fold had no buttons until it was scrolled to (only
  drawn panels get click regions), so Tab skipped it. A quiet frame now
  draws one such panel, a band at a time; its entrance still plays when
  it comes into view.
- "Fix and test: the first run fails, the second passes." was chosen by
  rounds, so a loop with three runs (a failing test, then ruff, then a
  pass) said it under ✗ ✗ ✓. It now needs exactly two runs; that loop
  reads "Fix and test, 2 rounds: 2 runs fail, then it passes.", and the
  corpus pins every loop's marks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 12:50:40 +02:00
Codeman maintainer 3f1ecfe83a fix(story-mode): a phone you can use with a thumb, and saves that say what they saved
From review round 5 (a phone with touch):

- Touch screens: every control at least 44 px (prev/next, close, the
  Story/Director's cut toggle, Save image, the final panel's chips, drawer
  buttons and links), and the small type at 13 px.
- The drawer on a phone starts below the bar (it cut through it), closes by
  dragging its head down, and keeps its head small in landscape.
- The turn picker says where you are ("Turn 2 of 5"), learns about new
  turns while you look at an older one, and a turn you picked yourself is
  not announced as news.
- A check whose command also wrote files offers "See the 2 files it wrote".
- Test counts are read from the whole output: a later command's lines had
  pushed pytest's "5 passed" out of the kept tail.
- A save names its commit: "Claude saves the work: "feat: add wrap() for
  modular wrapping into a range"." (from -m, -am, --message or the
  here-document of git commit -F -; a trailer is never the subject).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 12:43:59 +02:00
Codeman maintainer cdb7b5d55f fix(story-mode): background commands, and Save image on a phone
From review round 5 (a phone with touch):

- Save image on a phone exported blank art for every panel not yet scrolled
  into view (the frame loop draws only what is on screen). It now draws
  every panel completely first, and the file name carries the turn number,
  so saves of different turns no longer overwrite each other.
- A test command sent to the background ("Command running in background
  with ID …") drew as "The tests pass." with POW before any test ran. It is
  now running: "Claude starts the tests in the background.", tagged "in the
  background" on the wire, and the final panel says a command is still
  running. Its task notification settles it from the exit code, and reading
  the job's output file that reports tests is a check ("The tests pass.",
  7 passed) instead of "Claude looks around".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 12:39:04 +02:00
Codeman maintainer 814bec739d fix(story-mode): what a non-technical reader would believe, from the cold-reader review
Review round 5 had an agent read only the comic of the nine real sessions
(20 turns), write down what it believed happened, and only then compare
with the transcripts. Three panels told it something false, one hid the
failure the user asked to see:

- A push that went through, followed by a check that failed in the same
  command, drew as a failed push; the output (`[new branch]`, `->`) now
  decides: "Claude saves the work in 2 commits and uploads it, but a step
  after that fails."
- A formatter's "would be reformatted" preview, printed before it fixed
  the file, made a clean run a failure: the last verdict wins.
- A blocked command ended in confetti and "Mission complete!": a run panel
  where everything failed says "Claude tries a command, and it is
  blocked.", and a turn where nothing worked ends "Turn over" / "Nothing
  Claude tried worked, so the job is not done." with a neutral robot.
- The loop stamped only the final verdict (POW ×2), so a first run that
  failed on purpose never showed. It now marks every test run red or green,
  and says it: "Fix and test: the first run fails, the second passes." /
  "Build and test, 4 rounds. A check fails once, then everything passes."

Also:
- Files written inside a check or a save reach the plain caption ("Claude
  writes 4 files; the tests pass.").
- Balloons keep the next sentence when the first reports a problem ("…left a
  stray file. I'll remove it…") or is very short ("Not done. The harness
  blocked sleep 65…"); Claude's last words keep a caveat that follows them
  ("Nothing is committed because this folder isn't a git repository.").
- The advisor never shares a panel with helpers that do work; ToolSearch
  reads "Claude gets the tools it needs ready."; Codeman's own case file is
  not drawn as a file Claude read; looking around shows what Claude meant
  (the call's description), not raw commands; a montage at the start of a
  turn reads "Claude gets started".
- Words: boards count tests ("19 tests"), "uploads it" replaces "sends it
  up", and the prompt balloon keeps 400 characters.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 12:32:40 +02:00
Codeman maintainer 0effbb9ce4 perf(story-mode): no long tasks while stepping through turns; the rest of review round 4
Stepping through 40 turns made 46 long tasks of 51 to 286 ms, enough for
the app's guard to drop the terminal's WebGL renderer for the rest of the
page's life. Drawing switched off made none: the cost was canvas raster at
paint time, which the per-frame JS budget never saw. Now:

- Panel backgrounds are shared between panels and turns (kind × size ×
  palette), and a new one is built in four bands, one per frame (a wide
  halftone at 2x was ~35 ms in one go; a band is ~8 ms).
- At most two panels are drawn per frame, the first one served rotating so
  none starves; clocks still run for all.
- Measured on 20 turns stepped 40 times at 2x: 1 long task (52 ms), against
  39; the settled overlay is still ~2% of a core.

Also from round 4:
- The turn picker says when older turns were not loaded (`older` in the
  response: the transcript is longer than the part that was read).
- A compaction after the last turn closes that turn; a local command's
  output is the final panel's balloon ("Compacted"), without its markdown
  ticks, and a turn under a second reads "Done.".
- Exact durations keep their seconds past an hour, so the two captions never
  disagree on the minute ("1 h 10 min." / "1 h 9 min 39 s").
- A turn that arrives already finished is announced as new too.
- Whole files written by here-documents read "Claude writes 3 files."; the
  advisor's caption reads "a second opinion" instead of repeating its name.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 12:11:54 +02:00
Codeman maintainer 846749a912 fix(story-mode): scheduled runs, work after a turn ended, and pages full of added prompts
From review round 4 (a 110 MB real session, about 23 turns, read through
all four tail steps):

- A scheduled task's run was drawn inside the /compact turn before it (96
  minutes and 237 tool calls credited to "You run /compact."): its prompt is
  a meta line, and work after an ended turn was appended to that turn. The
  scheduled prompt now starts a turn of origin `scheduled` ("A scheduled
  task starts this turn."), and work that comes more than 5 s after a turn
  ended starts a turn of its own ("Claude carries on"); a line Claude Code
  flushed a moment late still joins.
- A turn with seven prompts added mid-turn drew 14 panels (the cap is 12):
  missions were never folded. An added prompt may now fold into a montage,
  as the last resort after quiet panels and work panels, and the montage
  says so ("Claude keeps going (you add 2 things): ..."); the turn's own
  prompt and a question to you are never folded.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 12:00:51 +02:00
Codeman maintainer 50de4f16b3 docs(story-mode): restructure the API section
Eleven batches of findings had grown it by accretion. It now reads in
parts: the routes, where turns start and end, how background work joins
back, and what a page, an event and a panel carry.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 11:46:13 +02:00
Codeman maintainer f464548541 test(story-mode): real sessions as a regression corpus
Nine transcripts from the review rounds, each a real Claude Code worker
building a small program (a Python CLI with a fix loop, a web page with
node:test, a bash script with a background helper, a long turn with a
prompt added mid-turn, teammates then an interrupt, a question then tests
first then a push, a text plan with a scratch dry run, /compact then plan
mode, a test failed on purpose). Each is cut down to the lines and fields
the reducer reads, with every path, user name and email rewritten and the
file contents of Reads left out; replaying them before and after gives
the same pages.

The expected file beside each pins every page (panel kinds, captions,
balloons, counts), so a change that alters a story fails with a diff of
exactly what changed; STORY_CORPUS_UPDATE=1 regenerates them. The beats
the reviews were about are also stated outright.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 11:45:41 +02:00
Codeman maintainer f4898a626f fix(story-mode): a turn ended only by the next prompt says so
A turn with no end marker of its own (an abandoned or crashed turn) was
closed when the next prompt started and then drawn as "Mission complete!".
It now reads "To be continued…" / "The trail stops here: the next prompt
starts a new turn.", and the page carries `superseded`. The Director's cut
names what ended every turn: turn_duration, stop_hook_summary, a local
command, an interrupt, or the next prompt.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 11:42:26 +02:00
Codeman maintainer 873b899c52 fix(story-mode): the rest of review round 3
- A report's turn links to the helper it answers: its drawer shows what the
  helper did. Reports arrive HTML-escaped (`&lt;a@b.c&gt;`) and now read as
  text.
- Screen readers: a polite status region says when a new turn starts and
  when one finishes; the note is a status, the drawer a dialog named by its
  title; "Open file" buttons name their file.
- Save image draws the balloons' tails (from their CSS triangles) and is
  disabled with nothing to save.
- App toasts move to the bottom while Story Mode is open: at the top they
  covered its bar and turn picker on phones.
- The bar names the session as its tab does; a click on the mission balloon
  opens the prompt.
- A heading ends at its line (a plan's "# Plan: add wrap" no longer runs
  into its next paragraph) and a short one ("## Summary") is dropped; the
  plan you approved stamps "You approved it".
- "Claude's part is done in 3 minutes. A helper is still working."; the
  helper steps say "still working" while the helper runs; the advisor's
  labels call it a second opinion; a check run twice the same way reads once
  with a count; the session folder reads "." in commands only, never in
  prose; the loop's POW keeps off its board on a narrow panel; a shell
  session's note says it runs a shell or another CLI.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 11:38:52 +02:00
Codeman maintainer 757e14e991 fix(story-mode): keyboard focus with the file preview, and what round 3 found missing
From review round 3:

- Keyboard: a file opened from a panel or drawer takes focus on the
  preview's close button, Tab cycles inside the preview while it is open,
  and Escape gives focus back to what opened it. Before, Tab walked out of
  the overlay to the session tabs behind it. A panel rebuilt under focus (a
  new turn, a fold into a loop) hands focus to its replacement.
- A commit at the end of a test one-liner is no longer lost: commits and
  pushes are counted whatever intent wins, and the check says "The tests
  pass, and Claude saves the work." (plus "1 commit" in the Director's cut).
- Files written by `printf/echo ... > f`, and files Claude Code says a
  command changed ("This command modified 1 file you've previously read"),
  count as changed files.
- A compaction says what it did ("automatic · 46.9k → 3.6k tokens · 24 s");
  an automatic one reads "Claude's memory is full", and its panel opens.
- ToolSearch is looking around, not a terminal panel; EnterPlanMode reads
  "Claude switches to plan mode"; the plan drawer shows the plan you
  approved ("You approved the plan.").
- A finished newest page is checked every 15 s (a 304 when unchanged): a
  local command like /compact raises no hook.
- Boards draw the real number of dots (5 tests, 5 dots); captions leave
  here-document markers out.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 11:31:39 +02:00
Codeman maintainer dae6a52daa fix(story-mode): one turn per prompt written twice, and plan mode as planning
From review round 3 (compaction, then plan mode):

- A typed `/compact` and its <command-name> record share one promptId; they
  were two turns ("Done in 0 seconds", then a turn left running). A prompt
  carrying the open turn's promptId now joins it: one "You run /compact."
  turn of 24 s, the real compaction time. A command typed as such
  (`/model opus`) is a command too.
- Plan mode: writing the plan file under ~/.claude/plans/ is planning (a plan
  panel with its numbered steps), not a code change ("3 files changed" for
  a commit of 2). ExitPlanMode's input is empty in current builds; its
  result carries the plan and your answer: "Claude shows you its plan, and
  you approve it."
- A command's Director's caption names it ("Command · /compact").

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 11:26:34 +02:00
Codeman maintainer db9eb5cea3 fix(story-mode): slash commands, and where a compaction belongs
A local slash command (`/compact`, `/model`) gets no reply from Claude and no
turn_duration, so its turn stayed "Claude is working" until the next prompt.
It now ends at the command's own output (<local-command-stdout>, kept for
the mission drawer) and reads "You run /compact.". `/compact` writes its
compaction marker before the command line itself; a marker that lands
between turns now belongs to the turn after it (it was drawn at the end of
the previous, finished turn), and one mid-turn stays where it is.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 11:18:38 +02:00
Codeman maintainer ea104c8965 fix(story-mode): a remark about committing stays on its save
"Commits are in." landed on the check panel after the save it reports on;
the rule that keeps a result remark on its check now covers saves too.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 11:12:46 +02:00
Codeman maintainer a2db03cda5 fix(story-mode): commits in montages, lint verdicts, and a page that holds still
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>
2026-10-18 11:10:14 +02:00
Codeman maintainer ca035dd41b fix(story-mode): files written in Bash, the advisor, and honest checks, from review round 2
- 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>
2026-10-18 10:52:08 +02:00
Codeman maintainer 831599a6cf fix(story-mode): translate the Story Mode label for zh-CN
The shortcut registry gained a Story Mode entry; every registry label must
translate (test/i18n-exit-run-help.test.ts, caught by the full gate).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 10:44:44 +02:00
Codeman maintainer d7a6644c2f fix(story-mode): one verdict per call, failed saves, and loops that end at a check
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>
2026-10-18 10:43:04 +02:00