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>
This commit is contained in:
Codeman maintainer committed 2026-10-18 11:10:14 +02:00
1 parent ca035dd41b
commit a2db03cda5
5 files changed
+162 -30

No files matched your search

+3 -1
View File
@@ -483,7 +483,8 @@ body.story-open {
display: none;
}
.story-splash-title {
margin: 0;
/* Room for the tilt: rotated about its bottom-left corner, its right end rises. */
margin: 0.4em 0 0;
font: 400 clamp(28px, 4.4cqi, 44px) / 0.92 var(--story-display);
color: var(--story-yellow);
-webkit-text-stroke: 2.5px var(--story-line);
@@ -791,6 +792,7 @@ body.story-open {
.story-df-path,
.story-out-cmd {
flex: 1 1 220px;
white-space: pre-wrap;
font: 500 12.5px/1.35 var(--story-mono);
font-variant-ligatures: none;
overflow-wrap: anywhere;
+29 -7
View File
@@ -1116,7 +1116,8 @@ const StoryArt = (() => {
}
// The cycle arrow between the page and the board.
const ax = 12 + pw + 17;
const spin = (f / 20) % TAU;
// It turns while the rounds are still going; on a finished page it rests.
const spin = p.panel.live || f < HERO ? (f / 20) % TAU : 0.4;
ctx.save();
ctx.translate(ax, 164);
ctx.rotate(spin);
@@ -1128,6 +1129,16 @@ const StoryArt = (() => {
ctx.strokeStyle = C.yellow;
ctx.lineWidth = 3;
ctx.stroke();
// The arrowhead at the arc's end.
const end = TAU - 0.6;
const hx = Math.cos(end) * 14;
const hy = Math.sin(end) * 14;
ctx.beginPath();
ctx.moveTo(hx + 7, hy - 3);
ctx.lineTo(hx - 1, hy + 7);
ctx.lineTo(hx - 6, hy - 6);
ctx.closePath();
inked(ctx, C.yellow, 2);
ctx.restore();
// Rounds and verdict stack under the top corner, which the balloon may take.
sfx(
@@ -1274,12 +1285,18 @@ const StoryArt = (() => {
halftone(ctx, W, H, C.yellow, W / 2, H / 2, { rmax: 3.4 });
},
draw(ctx, W, H, f, p, hit) {
// One cell per kind of step the montage folded, in this order, at most four.
const evs = p.panel.events.map((i) => p.page.events[i]).filter(Boolean);
const has = (...intents) => evs.some((e) => intents.includes(e.intent));
const cells = [
{ icon: 'look', label: 'reads' },
{ icon: 'run', label: 'runs' },
{ icon: 'edit', label: 'edits' },
{ icon: 'check', label: 'checks' },
];
has('read', 'search', 'look', 'web') && { icon: 'look' },
has('run', 'tool', 'message', 'helper', 'plan') && { icon: 'run' },
has('edit') && { icon: 'edit' },
has('test', 'build') && { icon: 'check' },
has('save') && { icon: 'save' },
]
.filter(Boolean)
.slice(0, 4);
const size = Math.min(70, (W - 120) / 2);
const x0 = 16;
const y0 = 92;
@@ -1298,7 +1315,12 @@ const StoryArt = (() => {
else if (c.icon === 'run') terminal(ctx, x + 8, y + 12, size - 16, size - 24, ['…'], HERO);
else if (c.icon === 'edit')
pencil(ctx, [cx + size * 0.3, cy - size * 0.3], [cx - size * 0.2, cy + size * 0.2]);
else star4(ctx, cx, cy, size * 0.3, C.go);
else if (c.icon === 'save') {
rr(ctx, cx - size * 0.3, cy - size * 0.12, size * 0.6, size * 0.38, 3);
inked(ctx, C.yellow, 2.5);
rr(ctx, cx - size * 0.34, cy - size * 0.26, size * 0.68, size * 0.14, 3);
inked(ctx, C.yellow, 2.5);
} else star4(ctx, cx, cy, size * 0.3, C.go);
ctx.restore();
});
hit('list', x0, y0, size * 2 + 10, size * 2 + 10, 'See every step');