Public: anyone can read this; the crew can change it.
Deliberation: comments, questions and counter-proposals on a proposal #32
Check · Review · Land · Live done — Landed and live in production.
Deliberation lands on the proposal page: a `remark` table (migration 6) holds comments, questions and counters — one to 2000 characters, cascading with its proposal, with the option a counter wants in `option_label` and `accepted_at` once that option has been taken. Five actions carry every rule: `listRemarks` reads for anyone, `addRemark` refuses a guest, an unknown kind and a missing proposal bu…
shipped production is serving it deploy 2adbe7d
Sign in to join this conversation.
Deliberation lands on the proposal page: a
remarktable (migration 6) holds comments, questions and counters — one to 2000 characters, cascading with its proposal, with the option a counter wants inoption_labelandaccepted_atonce that option has been taken. Five actions carry every rule:listRemarksreads for anyone,addRemarkrefuses a guest, an unknown kind and a missing proposal but takes remarks on a withdrawn or decided one,deleteRemarkis its author or an admin, andacceptCounteris the proposal's author or an admin on an open proposal only — it appends the counter's label as one more option and stamps the remark, and nothing else about the proposal moves, so ballots already cast still mean what they meant.Every door calls the same functions: the thread and its composer on
/p/<id>,GET/POST /api/proposals/:id/remarks,DELETE /api/remarks/:id,POST /api/remarks/:id/accept, thelist_remarksandadd_remarkMCP tools, and the terminal, where an open proposal shows its thread andcwrites one. Worth looking at in the preview: open a proposal, counter it with an option label from a second account, and accept it — the option appears and the remark says earlier ballots stand. The thread is the last region on the page and grows at the bottom, live onremark.added,remark.removedandremark.accepted, so a remark arriving never moves the box someone is typing in; open two windows to see it.Two things to look at beyond the checklist.
src/plain.tsis new and has no dependencies: it turns every C0 and C1 control character in somebody else's text into a space, andsrc/tty.tsnow prints all foreign text through it, so a remark cannot drive a terminal's cursor (escapealready does this job for HTML). And/aboutmoves deliberation out of "not yet" into what works, which made its assertion insrc/server.test.tswrong — that test was right when it was written and I changed it because the feature landed, not because it was failing inconveniently; every other test change is new.What I did not do: a remark cannot be edited, there is no reply-to-a-reply, no thread is paginated (the record shows all of it), and nobody is told a question is waiting — that last one is now the "Answers" line in the not-yet list on
/about.bun testis green at 76 tests, and/p/<id>renders in well under a millisecond with a thread on it.🤖 Generated with Claude Code
Closes #6