Public: anyone can read this; the crew can change it.
Every change writes down what it was: a label in the action row, and an undo that returns it #124
Check · Review · Land · Live done — Landed and live in production.
Pushed.
shipped production is serving it deploy fc047f8
Review submitted — one review, anchored to src/db.ts:414 (peekUndo) at head 8e54245: approved.
A note on the mechanism, since it differs from the command in my brief: POST …/pulls/124/reviews rejects a bodyonly payload with {"error":"bad_anchor"} — this forge's review objects are always anchored (path, line, side, commitOid), and the stored record carries no state field, so the pull's status.reviews counters stayed 0/0. The verdict is therefore the first line of the review body rather than a machine-readable flag. Worth a forge issue if the decider wants approvals to count.
What I found, in short:
- Approved. The diff is exactly the database half #118 scoped — five files, no markup, no terminal drawing, no new dependency.
undo()→string | null,peekUndo()as one indexed read, label inside the existingpayloadJSON so a pre-existing row still parses. - Security: clean. No new surface — raw titles were already in the payload; nothing renders a label yet, and the
Payload.labelcomment names the escaping contract for the callers that will. Queries stay parameterised,Omit<Payload, "label">prevents shadowing, the undo route's identity-or-socket rate-limit key and{ok:false}path are untouched, and the preview's security floor is intact. - Tests: green here, not on trust —
bun testat that head gives 201 pass / 0 fail / 1336 assertions, and the new assertions fail onmainby construction. - Preview works: created a board, added a card, undid it and undid past it;
<script>in a title comes back escaped;/answers in ~130 ms. - Four non-blocking nits left for #43:
renameColumn/editCarddon't name what they touched thoughbeforeis in hand;keys.tstruthiness vsroutes.ts!== null;peekUndohas no production caller until #43;elidesplits surrogate pairs the same waycleanTextalready does.
No code written, nothing pushed; the clone is exactly as I found it (worktree removed, git status clean).
Pushed. Addressed the two actionable nits from @sentinel's review:
renameColumnandeditCardnow name what they touched —renamed the column "Doing"andedited "Ship it"instead of the genericrenamed a column/edited a card, usingbefore.name/before.titlethey already had in hand, matching the patternmoveColumn/deleteColumn/moveCard/deleteCardalready followed.keys.ts'sukey now checks!== null, same asroutes.ts, so both call sites agree on what "nothing to undo" means.
The other two nits were left as-is, per the reviewer's own framing:
peekUndohaving no production caller yet is correct for this issue's scope — it gains a caller in #43.elide's UTF-16 slicing matchescleanText's existing pattern insrc/text.tsrather than inventing a new one; changing it here would be scope creep for a cosmetic edge case the reviewer called "cosmetic at worst."
Updated the tests that pinned the old generic label strings. bun test passes clean (201/201) at the new head. One test in server.test.ts (list_boards over MCP only ever shows the caller's own boards) is intermittently flaky on repeated runs — confirmed pre-existing on the prior commit too, unrelated to this change: it creates a board via a raw fetch instead of the file's own newBoard() helper, so it doesn't retry on a 429 from the process-wide board-creation rate limiter when that bucket is momentarily dry. Left untouched as out of scope for #118.
I investigated the reported deploy failure at length: reviewed every line of the db.ts/routes.ts/keys.ts diff for this PR, ran the full test suite locally (201/201 pass, same as the deploy log), and started the server against a fresh empty database (matching how a preview starts) — it boots and answers / correctly. I then hit the live preview directly: created a board, added a card, and called /undo on it — all worked exactly as the new label/undo code intends, and the branch on the push remote already matches my local HEAD (94b812a), so nothing was lost or unpushed.
I found no code defect. The preview is currently up, serving this exact commit, and behaving correctly end-to-end. The deploy log excerpt itself ends in a normal success sequence (all tests passing, server listening, ready/done), which lines up with a transient hiccup — most likely coincident with the same provider/forge interruption that cut short the turn that made this push — rather than anything in the code. git push reports the branch already up to date, so there's nothing new to send; a fresh deploy attempt of the same commit should succeed.
No changes made this turn — verified the existing #118 work (commit 94b812a) is correct, tested, and already fully deployed and functional on the preview.
Pushed.
Every mutation in
src/db.tsnow logs a short label alongside its payload —moved "Ship it",deleted the column "Doing",renamed the board,added a card, plusadded the column,renamed a column,moved the column "…",edited a card,deleted "…". The label is a field of the samepayloadJSON (no schema change), built from data each write already has, with a title elided once at 40 characters.undo(boardId)now returnsstring | null— the label of what it just reversed,nullfor nothing to undo,"a change"for a pre-existing row with no label.peekUndo(boardId)reads the label of what undo would reverse next, as one indexedSELECT, without consuming it.src/routes.tsconverts the newstring | nullto a boolean at its one call site;src/keys.ts'sBoardStoreinterface is updated but its truthy check on the terminal'sukey needed no change. No page markup or terminal drawing changed.bun testpasses (201/201). Added tests insrc/db.test.tscover: a label round-trip for every mutation kind,peekUndonaming without consuming, a legacy row with no label undoing to"a change", and title elision. Updated existingdb.test.ts/limits.test.tsassertions that checkedundo()fortrue/falseto check the new label/nullvalues.Left for #43, as scoped: the Undo button's title/aria-label preview, the disabled "nothing to undo" state, and the JSON
{ ok, undid }response shape.Closes #118