Commit Graph
1555 Commits
Author SHA1 Message Date
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 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 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
Codeman maintainer 848566828f fix(story-mode): read how current Claude Code builds deliver prompts and end turns
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>
2026-10-18 10:41:14 +02:00
Codeman maintainer 03cb3a5961 fix(story-mode): balloons over the speaker, honest boards, and drawers that keep up
From review round 1, checked against screenshots of the reviewers' real
transcripts at desktop and phone width:

- Claude's balloon is placed in the band above the head the scene drew, its
  tail pointing down at it, and clamped to stay inside the panel and off the
  head. Per-kind CSS offsets had covered the POW burst, the agent's head on a
  half-width fix panel and the first line of a terminal; a long prompt ran
  out of the mission panel. Save image clamps the same way.
- A board only shows real counts; a check that reported none shows one
  verdict instead of 21 invented dots, and the loop shows its last run.
- The mission panel opens the whole prompt, and on a task turn what the
  background job reported.
- A background helper says "in the background" and "still working" until it
  reports back, its drawer keeps polling while it works, and the final panel
  counts helpers still out.
- A fix made from the terminal opens what ran instead of an empty diff.
- The wire files Claude's words under the panel that draws them, and its
  last words under the final panel.
- A drawer follows its panel by the first event it showed when a refresh
  regroups panels (a check folding into a loop), not by kind.
- Click regions reused for a new page get the new label; a new turn draws
  all its panels fresh.
- Output links skip globs and paths a tool elided; prose links rooted paths
  only; paths inside the session folder read short (clicks keep the full
  path), and files Claude printed with cat open relative to that folder.
- File cards keep a name's separators (rename_by_ / date.sh), code drops
  ligatures (`!==` stays three characters), counts drop "−0", and the turn
  picker's text stops before its chevron.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 10:23:27 +02:00
Codeman maintainer 38585a806b fix(story-mode): draw what real sessions did, from review round 1
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>
2026-10-18 10:11:47 +02:00
Codeman maintainer 0c45682136 feat(story-mode): save as image, a helper's own steps, a way in on phones, and polish
- Save image: the page as one PNG to share (each panel canvas, then every
  caption, balloon and chip redrawn from its computed style and box).
- The helper drawer lists what the helper did, read live from its own
  transcript (GET .../story/helpers/:agentId), led by the task Claude gave it.
- Session Options has a Story button (same setting), the way in on phones.
- A turn a background task or another session started reads as a notice,
  not as your speech balloon.
- Wide panels keep their scene in a centered stage instead of spreading the
  art to both ends; the fix panel's agent stands next to the page.
- An unchanged page (same ETag) is neither parsed nor redrawn.
- Paths inside the session's folder are shown short; clicks keep full paths.
- Tab stays inside the overlay; a missing 2D context skips the art, not the page.
- A rebindable Story Mode shortcut (no default chord).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 09:51:48 +02:00
Codeman maintainer d313b868d4 test(story-mode): end to end in a real browser (browser suite)
A real server in test mode, real Chromium, a synthetic transcript handed over
through a real stop hook: off by default, the header button draws the running
turn, a grown transcript plus a hook redraws it live over SSE, the code page
opens the diff, Escape closes drawer then overlay, and a phone-width viewport
has no sideways scroll. Listed in BROWSER_TEST_GLOBS (nightly, not the gate).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 09:46:10 +02:00
Codeman maintainer fb71180632 feat(story-mode): a helper's own steps, from its transcript
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>
2026-10-18 09:41:39 +02:00
Codeman maintainer fc279f998c fix(story-mode): judge each part of a Bash one-liner, and keep underscores in balloons
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>
2026-10-18 09:38:24 +02:00
Codeman maintainer dfe023d49d test(story-mode): frontend overlay test in JSDOM, every scene drawn in every shape
Runs the real story-mode.js against pages the real server core builds from
invented transcripts: panels, captions, balloons, the turn picker, the
Director's cut wire, file and diff clicks with their line numbers, output
links through the terminal's own path matcher, a live refresh that keeps
existing panels' clocks, activity filtering, Escape one layer per press,
teardown, error notes and the setting switch. A permissive fake 2D context
lets every scene draw every panel kind across its whole animation, at four
sizes, in both reading levels, without throwing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 09:35:03 +02:00
Codeman maintainer 7c21973c54 perf(story-mode): 304 for an unchanged page, and a longer read for a turn bigger than the tail
GET /story now sends a weak ETag (server start, transcript size and mtime, turn)
with Cache-Control: no-cache, and answers 304 before building anything when the
transcript has not changed, so a live view's polls cost a stat.

A turn can be bigger than the 8 MB tail on its own (tool results carry whole
files, ~60 KB per result line on large local transcripts), which cut its first
panels. When the window holds no prompt, the reader re-reads a 24 MB, then a
64 MB tail.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 09:31:40 +02:00
Codeman maintainer 2653db10b5 feat(story-mode): draw a Claude session's turns as comic pages, live
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>
2026-10-18 09:22:06 +02:00
Codeman maintainer 5a297cd3e4 fix(multi-user): read a refused settings write as admin-only in the browser
Two gaps in how a regular user's browser handled the admin-only settings
writes:

- The role comes from GET /api/me, which answers after page load, and on
  a failed request admin-ui.js falls back to a single-user admin. A save
  made then was sent, came back 403, and still said "Settings saved",
  while the refused server-wide change stayed on the device. A 403
  FORBIDDEN on a settings write now ends that save like a known
  non-admin's: synced keys back to the server's value, the one admin-only
  notice, and the page is read-only from then on (_noteSettingsWriteRefusal).
  The display-key list moved into one method (_displaySettingKeySet), so
  that split no longer depends on GET /api/settings having loaded.
- A regular user's run mode and respawn presets were skipped for the
  server but overwritten by the server's on the next page load, while
  "Saved preset" said otherwise. They are now kept as that user's own on
  the device and put back over the server's after the settings load and
  on codeman:me (_rememberOwnSetting, _applyOwnSettings).

Admins and single-user mode are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 13:12:09 +02:00
Codeman maintainer 24a1c6c427 fix(multi-user): make the server-wide system operations admin-only
docs/multi-user-plan.md lists the system operations as admin-only, but
these routes had no role check, so any signed-in regular user could run
them in multi-user mode:

- POST /api/auth/revoke with an empty body cleared EVERY login session on
  the server, every admin's included. A non-admin now revokes only their
  own logins, and a named sessionToken only when it is one of theirs.
- POST /api/system/update (update and restart the server for everyone),
  POST /api/system/span-displays (a window on the server's desktop),
  POST /api/tunnel/qr/regenerate (clears every user's pending QR login
  code) and POST /api/cleanup-state (drops every user's stale session
  records) now answer 403 FORBIDDEN to a non-admin.
- GET /api/tunnel/info lists only a non-admin's own logins (it showed the
  address and browser of every user's sessions).

Admins and single-user mode are unchanged. The tunnel panel's Regenerate
QR button no longer reports success for a refused request.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 13:11:41 +02:00
Codeman maintainer 33f0bc423f fix(multi-user): keep the admin's Deepgram key out of a non-admin's settings read
GET /api/settings returned the whole settings.json to every signed-in user.
An admin's App Settings save stores the Deepgram key there
(voiceSettings.apiKey), so any regular user could read it, and their
browser copied it into its own voice config and dictated on the admin's
account. caseOrder and lastUsedCase also named the admin's cases.

In multi-user mode a non-admin now reads the file without those three
(settingsForNonAdmin). Admins and single-user mode get the file as it
is. Pinned by route tests and the live multi-user test.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 13:10:59 +02:00
Codeman maintainer cb928aefe2 test(multi-user): pin admin-only settings writes, server and browser
- test/routes/system-routes-settings-admin-only.test.ts (app.inject): a
  non-admin PUT /api/settings is 403 FORBIDDEN before the body is parsed, and
  nothing is written, no watcher toggled, no tunnel started, no history limit
  set; the same gate on PUT /api/execution/model-config, PUT /api/config and
  PUT /api/cases/order; admins and single-user mode write as before; GET stays
  readable.
- test/settings-admin-only-multiuser.test.ts (live server, port 0, real
  multi-user auth): settings.json is byte-identical after a non-admin's PUTs,
  and a non-admin's quick-start or Ralph loop no longer records lastUsedCase.
- test/settings-admin-only-ui.test.ts (real index.html in JSDOM): a non-admin
  App Settings save sends no PUT, keeps per-device keys, keeps synced keys at
  the server value, and toasts once only on a real synced change; the
  background writes are skipped; admins still PUT; zh-CN for both toasts.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 12:16:34 +02:00
Codeman maintainer 15a73dfa05 Merge pull request #618 from opticon454/feat/whats-new
feat: announce each release once with a toast generated from the changelog

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 02:39:49 +02:00
Codeman maintainer 5483811753 Merge pull request #625 from Ark0N/feat/git-worktrees
feat(worktrees): create, open and remove git worktrees of a case

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 02:00:32 +02:00
Codeman maintainer c76600315e Merge pull request #624 from Ark0N/feat/codex-blocked-detection
feat(codex): raise codex approval, question and trust dialogs as BLOCKED

Conflict resolved against #634 in src/session.ts _probePaneWorking():
#624's assignment (a dialog on screen is never work) is kept, and #634's
trust-dialog verdict block (log the hold once, remember the reading)
follows it, before _readWatching().

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 02:00:29 +02:00
Codeman maintainer 9aa3083280 Merge pull request #634 from JDProfresh/fix/idle-not-on-trust-dialog
fix(idle): hold idle while the workspace trust dialog is on screen

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:59:55 +02:00
Codeman maintainer eec2895dd7 Merge pull request #635 from JDProfresh/fix/trust-dialog-deferred-read
fix(trust-dialog): re-read a throttled trust-dialog scan instead of dropping it

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:59:55 +02:00
Codeman maintainer d080004436 Merge pull request #617 from opticon454/feat/custom-keybindings
feat(shortcuts): rebindable previous/next session, duplicate-chord refusal, rebinds stay out of the PTY

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:59:51 +02:00
Codeman maintainer 5eccaca3bc Merge pull request #616 from opticon454/fix/deepseek-install-path
fix(deepseek): find pnpm when installing the terminal profile, and install it with dsh

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:59:51 +02:00
Codeman maintainer 16bbd260e2 Merge pull request #642 from Ark0N/fix/skill-positional-args
fix(skill): keep the §0 bootstrap intact when Claude Code loads the skill with arguments

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:59:46 +02:00
Codeman maintainer 8084cae0fe Merge pull request #628 from Randalix/fix/codex-submit-paste
fix(tmux): submit text as a bracketed paste so Codex does not swallow Enter

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:59:46 +02:00
Codeman maintainer c5417abd27 Merge pull request #632 from ludovicfourrage/fix/capture-pane-sgr-reset-629
fix(tmux): reset captured SGR before live output resumes

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:59:46 +02:00
Codeman maintainer 6e4181a2e6 Merge pull request #622 from JDProfresh/fix/attach-video-containers
fix(uploads): refuse an unsupported video before uploading it

Conflict resolved against #621 in src/web/public/image-input.js (the
_promptAttachKind doc comment only): #621's comment is kept with #622's
clause that a video MIME counts only for the containers the server
admits; #622's video MIME check and #621's document branch both stay.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:59:42 +02:00
Codeman maintainer 0866090bc3 Merge pull request #621 from JDProfresh/feat/prompt-upload-documents
feat(uploads): prompt uploads take documents

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:59:23 +02:00
Codeman maintainer 4b73ec8417 fix(#624): read codex's trust prompt as BLOCKED when it ends in esc back
On codex-cli 0.162.0, with 0.163.0 released, every launch first shows the
"Update available" prompt, and choosing Skip leads to a variant of the
workspace trust prompt: "2. Back to Agent Command Center" over the key-hint
row "enter continue · esc back". The pattern only accepted cancel,
interrupt, quit, answer or all as the closing word, so a fresh git repo
launched from Codeman sat on that trust prompt reading idle, with no red
alert and no blocked signal (measured live on a throwaway instance).

Adding "back" is safe because the hint row must still START with one of
the anchored hints ("enter continue" here): the popups the user opens at
the keyboard (/model, /permissions, /review, Implement this plan?) end in
"esc back" too, but their row starts with "enter select", so they stay
unmatched, as every existing negative fixture confirms. The new fixture is
the live capture with the scratch path replaced.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:42:40 +02:00