Public: anyone can read this; the crew can change it.
A proposer fixes a typo: the title and the paragraph, edited in the open and stamped #60
Check · Review · Land · Live done — Landed and live in production.
`editProposal(db, who, {id, title, body})` lets a proposer correct the title and the paragraph of their own open proposal — the author only, so an admin who withdraws anybody's proposal still edits nobody's text — judged by the same two sentences `propose` uses, refused once the proposal is decided or withdrawn, and refusing the options out loud (`OPTIONS_LOCKED`) at every door rather than droppin…
shipped production is serving it deploy 2adbe7d
Sign in to join this conversation.
editProposal(db, who, {id, title, body})lets a proposer correct the title and the paragraph of their own open proposal — the author only, so an admin who withdraws anybody's proposal still edits nobody's text — judged by the same two sentencesproposeuses, refused once the proposal is decided or withdrawn, and refusing the options out loud (OPTIONS_LOCKED) at every door rather than dropping them.editRefusalholds the who/state rules once, so the form, the terminal and the action all say the same thing; the line where a ballot check belongs when #5 lands is marked in the action, since nothing casts one yet.The correction is shown rather than hidden, and this is the one place I deviated from the issue: the "edited" mark comes from a new
edited_atcolumn (migration 8, oneALTER TABLE ... ADD COLUMN) rather than fromupdated_atbeing later thancreated_at, because withdrawing a proposal and accepting a counter stampupdated_attoo — comparing the two would have made every withdrawn proposal claim an edit nobody made.updated_atis still set by an edit. Saving the same words succeeds, stamps nothing and emits nothing.Every door: an Edit link on
/p/:idfor the author,/p/:id/editas a page of its own (the proposal is a live region, and a form inside one can be swapped out from under someone's hands),POST /proposals/:id/edit,PATCHorPOST /api/proposals/:id, theedit_proposalMCP tool, andeat the terminal with both fields arriving already typed. An edit emitsproposal.edited, which the proposal region now listens for; the thread is untouched.To try it in the preview: open a proposal, follow Edit from
/p/<id>, change the title to something with a<in it, and watch the byline gain "edited" with the time; then open the same page in a second window before saving to see it update in place. Tests:bun testis green (129), covering a non-author and an admin refused, a decided and a withdrawn proposal refused, the over-length title refused with exactlypropose's sentence, a no-op edit leavingedited_atnull, the page showing "edited" only after a real change and never after a withdrawal, escaping on the way out, and the event reaching an open page from a second process (that last one lives inlive.test.tsbeside its sibling, becauseserver.test.tsstarts a second server and the live store follows whichever database it saw last).🤖 Generated with Claude Code
Closes #53