The direction says guests read public decisions, but a decision will land and then live only inside the proposal that produced it. Give the record its own page and a feed, so a decision can be linked, cited and followed without visiting.
What done looks like
[ ] /decisions lists every decided proposal, newest first: the title, the option that won, the tally's reason at the moment it decided, who proposed it, when it decided, and a link to the proposal. A guest sees all of it; nothing on this page needs an identity.
[ ] Expired and withdrawn proposals are not decisions and do not appear here; the page says in one line where to find them (/).
[ ] /decisions.atom is the same list as a valid Atom feed: a stable id per entry (the proposal's permalink), updated from decided_at, the title, and a summary carrying the winning option and the reason — every value escaped for XML, which is not the HTML escaper.
[ ] The feed answers application/atom+xml, carries the floor, caps at the 50 most recent, and answers in milliseconds; HEAD answers like GET.
[ ] GET /api/decisions returns the same list as JSON through a listDecisions action, so the page, the feed, the API and MCP (list_decisions) all read one function.
[ ] The layout links /decisions once, and a decided proposal links back to it.
[ ] Tests: a decided proposal appears in all three shapes and an open one appears in none; the Atom document parses and its entry ids are absolute URLs on the public origin; a title containing <, & and ]]> is escaped correctly in XML and in HTML; the feed is capped at 50; the floor is present on both routes.
[ ] README.md gains two lines: the feed's address and what belongs in it.
Files I expect to change:src/actions.ts, src/actions.test.ts, src/feed.ts (new, the Atom rendering and its XML escaper), src/feed.test.ts (new), src/pages/decisions.ts (new), src/pages/layout.ts, src/routes.ts, src/mcp.ts, src/server.test.ts, README.md.
Risks: XML escaping is not HTML escaping — write a separate function and test it against <, &, ", ' and a CDATA-looking string, rather than reaching for escape from src/http.ts. Build absolute URLs from publicOrigin in src/env.ts, never from a request header a client controls. Keep the query indexed and capped so a long-lived group's feed stays fast.
Fit: Ledger — the record as the product, readable by anyone, with every entry traceable to the vote that made it. A person assigns; I do not.
The direction says guests read public decisions, but a decision will land and then live only inside the proposal that produced it. Give the record its own page and a feed, so a decision can be linked, cited and followed without visiting.
What done looks like
/decisionslists every decided proposal, newest first: the title, the option that won, the tally'sreasonat the moment it decided, who proposed it, when it decided, and a link to the proposal. A guest sees all of it; nothing on this page needs an identity./)./decisions.atomis the same list as a valid Atom feed: a stableidper entry (the proposal's permalink),updatedfromdecided_at, the title, and a summary carrying the winning option and the reason — every value escaped for XML, which is not the HTML escaper.application/atom+xml, carries the floor, caps at the 50 most recent, and answers in milliseconds;HEADanswers likeGET.GET /api/decisionsreturns the same list as JSON through alistDecisionsaction, so the page, the feed, the API and MCP (list_decisions) all read one function./decisionsonce, and a decided proposal links back to it.<,&and]]>is escaped correctly in XML and in HTML; the feed is capped at 50; the floor is present on both routes.README.mdgains two lines: the feed's address and what belongs in it.Files I expect to change:
src/actions.ts,src/actions.test.ts,src/feed.ts(new, the Atom rendering and its XML escaper),src/feed.test.ts(new),src/pages/decisions.ts(new),src/pages/layout.ts,src/routes.ts,src/mcp.ts,src/server.test.ts,README.md.Risks: XML escaping is not HTML escaping — write a separate function and test it against
<,&,",'and a CDATA-looking string, rather than reaching forescapefromsrc/http.ts. Build absolute URLs frompublicOrigininsrc/env.ts, never from a request header a client controls. Keep the query indexed and capped so a long-lived group's feed stays fast.Fit: Ledger — the record as the product, readable by anyone, with every entry traceable to the vote that made it. A person assigns; I do not.