Show the last handful of changes to a board, so someone coming back to it can see what the others did without diffing it in their head.
What it is like today. A shared board tells you its present state and nothing about how it got there. Live events are only for tabs that were open at the time; a tab opened five minutes later sees a board that silently differs from the one its owner left. The only record is the action table — which already stores one row per change, per board, with a timestamp, capped at MAX_UNDO_DEPTH (200) — and it is used for exactly one thing: undo. #43 is about to give every one of those rows a human label (moved "Ship it", deleted the column "Doing"), which is the missing half of an activity list. After it lands, this is mostly rendering.
What done looks like
[ ] db.listActivity(boardId, limit) — one indexed read on the existing action_board_idx, newest first, returning each row's label and created_at, with a hard cap on limit.
[ ] The board page shows the most recent changes (ten is plenty) in a <details> beside the toolbar, collapsed by default so the first paint and its layout are unchanged, server-rendered so it needs no JavaScript.
[ ] Times read as a person says them (3 minutes ago, yesterday), computed server-side — no clock in the client, no layout shift.
[ ] Labels carry card and column titles, which are someone else's text: rendered through escape(), exactly like #43 requires for the Undo button.
[ ] The list says plainly what it is: the last changes still in the undo log, so an undone change leaves it, and a board past 200 changes has forgotten the old ones. Better a stated horizon than a history people trust wrongly.
[ ] A live undo or any other event refreshes it along with the rest of the board — it is part of the board's rendered state, not a separate poller.
[ ] Tests: a board with changes lists them newest first; an undone change drops out; the cap holds; a card titled <img src=x> renders as text.
[ ] bun test passes; the README gains a bullet saying what the activity list shows and what it deliberately does not (who did it — that is #18's by, and it is not on the wire today).
Files I expect to change:src/db.ts, src/pages/board.ts, src/db.test.ts, src/pages/board.test.ts, README.md.
Risks. This gives the undo log a second reader, so #43's labels become a rendered surface and their wording matters more; keep them short or the panel becomes a wall. Do not turn this into a durable audit log — that is a different table, a different retention story, and a decision someone should make deliberately rather than inherit from undo. Waits for #43, which writes the labels this reads.
Who fits:scout to build it, reviewed by juniper — the value is entirely in whether the sentences read like something a colleague would say.
Show the last handful of changes to a board, so someone coming back to it can see what the others did without diffing it in their head.
What it is like today. A shared board tells you its present state and nothing about how it got there. Live events are only for tabs that were open at the time; a tab opened five minutes later sees a board that silently differs from the one its owner left. The only record is the
actiontable — which already stores one row per change, per board, with a timestamp, capped atMAX_UNDO_DEPTH(200) — and it is used for exactly one thing: undo. #43 is about to give every one of those rows a human label (moved "Ship it",deleted the column "Doing"), which is the missing half of an activity list. After it lands, this is mostly rendering.What done looks like
db.listActivity(boardId, limit)— one indexed read on the existingaction_board_idx, newest first, returning each row's label andcreated_at, with a hard cap onlimit.<details>beside the toolbar, collapsed by default so the first paint and its layout are unchanged, server-rendered so it needs no JavaScript.3 minutes ago,yesterday), computed server-side — no clock in the client, no layout shift.escape(), exactly like #43 requires for the Undo button.undoor any other event refreshes it along with the rest of the board — it is part of the board's rendered state, not a separate poller.<img src=x>renders as text.bun testpasses; the README gains a bullet saying what the activity list shows and what it deliberately does not (who did it — that is #18'sby, and it is not on the wire today).Files I expect to change:
src/db.ts,src/pages/board.ts,src/db.test.ts,src/pages/board.test.ts,README.md.Risks. This gives the undo log a second reader, so #43's labels become a rendered surface and their wording matters more; keep them short or the panel becomes a wall. Do not turn this into a durable audit log — that is a different table, a different retention story, and a decision someone should make deliberately rather than inherit from undo. Waits for #43, which writes the labels this reads.
Who fits: scout to build it, reviewed by juniper — the value is entirely in whether the sentences read like something a colleague would say.