Public: anyone can read this; the crew can change it.

A proposer picks the quorum rule, and the proposal says which one it is #21

shipped production is serving it opened this has it
opened this

src/quorum.ts has landed with three rules, but propose still writes the default majority for everyone and no door offers a choice — so the rule the group agreed to is invisible and unchangeable. This is the half of issue #5 that needs neither ballots nor a roster, and it can land now.

What done looks like

  • [ ] propose takes an optional rule string, validated by parseRule from src/quorum.ts — anything it refuses is invalid with a sentence naming the three forms (majority, all, named:alice,bob:2) — and stores the text it was given; no rule means majority, exactly as today.
  • [ ] getProposal and listProposals return the parsed rule alongside its stored text, so a door never parses it a second time.
  • [ ] The composer on / offers the rule as a <select> of the three kinds, with the names and count for named: as one small text field that only appears relevant for that kind (and works without JavaScript — a plain field with a label saying it is used only for "a named few").
  • [ ] /p/:id states the rule in words from RULES, above the options ("Decides by majority of the group", "Needs 2 of alice, bob, carol").
  • [ ] Every door carries it: rule in POST /api/proposals, in the MCP propose schema with the three forms described, and the terminal asking for the rule after the options (Enter for majority).
  • [ ] A proposal keeps the rule it was opened with; changing it later is not in this issue.
  • [ ] Tests: each of the three forms round-trips from POST /api/proposals to GET /api/proposals/:id; named: with a count above its name list is refused; named: naming nobody is refused; garbage ("unanimous", "majority:9", a 500-character string) is refused; no rule still yields majority; the proposal page prints the rule for each kind.
  • [ ] README.md shows the three forms in three lines. bun test is green.

Files I expect to change: src/actions.ts, src/actions.test.ts, src/routes.ts, src/pages/home.ts, src/pages/proposal.ts, src/mcp.ts, src/tty.ts, src/server.test.ts, README.md.

Risks: the rule is user text stored in a column and read back by parseRule — a row written before this change (or by hand) must still read, so treat an unparseable stored rule as a named failure at the read, never a throw that takes the page down. Do not teach a second module how to read the rule text: parseRule stays the only reader. Issue #5 (votes decide) assumes this is in place and no longer has to add it.

Fit: Ledger — a boundary that names what it refuses, and a value that means one thing everywhere. A person assigns; I do not.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs