A group that decides asynchronously only works if each person can see, in one glance, what the group is waiting on them for. Once ballots exist, give a signed-in member that list — and give an agent the same list over MCP, so a crew member can ask what it owes the group.
What done looks like
[ ] waitingFor(db, who) in src/actions.ts returns the open proposals that wait on this person, each with the reason it does: vote — they are on the roster and have cast no ballot on it; answer — they wrote it and it carries a question with no reply. A guest gets an empty list, never a failure; someone off the roster gets only their own answer rows.
[ ] A decided, withdrawn or expired proposal never waits on anyone, and a person who has voted (or changed their vote) is not asked again.
[ ] / shows a “Waiting for you” section above the list for a signed-in person who has something waiting: the count, each proposal linked, and the reason in three words. Nothing at all is drawn for a guest or for someone with an empty list — no empty box, no “you are all caught up” card.
[ ] GET /api/waiting answers the same list through the same action, and MCP gains waiting_for_me with one sentence saying it answers for whoever is calling.
[ ] The terminal shows it on a key that is free (w is withdraw) and says so on its status line.
[ ] It is a bounded number of queries, capped, indexed, and adds no more than a millisecond to / — a test seeds 200 open proposals and asserts the page still answers in well under 50 ms.
[ ] The section is live: it carries data-live for the events that could change it (proposal.opened, vote.cast, remark.added, decision.made) so a vote cast elsewhere removes the row.
[ ] Tests: a member with no ballot is waiting and stops when they vote; a non-member is not asked to vote but is asked to answer their own question; an author whose question has a reply is not listed; a guest's list is empty; the page draws nothing for an empty list.
Files I expect to change:src/actions.ts, src/actions.test.ts, src/pages/home.ts, src/routes.ts, src/mcp.ts, src/tty.ts, src/server.test.ts, README.md.
Risks: This reads the roster and the ballots, so it cannot be written before #5 lands — and “a question with no reply” is #38's shape, not a new column. Keep the whole thing one action: the page, the API, MCP and the terminal must not each decide what “waiting” means. Do not turn it into notifications — nothing is stored, nothing is marked read, and no mail is sent; it is a query over what is already there.
Fit: Ledger — the same hand as the ballots, reading the rows it wrote. A person assigns; I do not.
A group that decides asynchronously only works if each person can see, in one glance, what the group is waiting on them for. Once ballots exist, give a signed-in member that list — and give an agent the same list over MCP, so a crew member can ask what it owes the group.
What done looks like
waitingFor(db, who)insrc/actions.tsreturns the open proposals that wait on this person, each with the reason it does:vote— they are on the roster and have cast no ballot on it;answer— they wrote it and it carries a question with no reply. A guest gets an empty list, never a failure; someone off the roster gets only their ownanswerrows./shows a “Waiting for you” section above the list for a signed-in person who has something waiting: the count, each proposal linked, and the reason in three words. Nothing at all is drawn for a guest or for someone with an empty list — no empty box, no “you are all caught up” card.GET /api/waitinganswers the same list through the same action, and MCP gainswaiting_for_mewith one sentence saying it answers for whoever is calling.wis withdraw) and says so on its status line./— a test seeds 200 open proposals and asserts the page still answers in well under 50 ms.data-livefor the events that could change it (proposal.opened,vote.cast,remark.added,decision.made) so a vote cast elsewhere removes the row.Files I expect to change:
src/actions.ts,src/actions.test.ts,src/pages/home.ts,src/routes.ts,src/mcp.ts,src/tty.ts,src/server.test.ts,README.md.Risks: This reads the roster and the ballots, so it cannot be written before #5 lands — and “a question with no reply” is #38's shape, not a new column. Keep the whole thing one action: the page, the API, MCP and the terminal must not each decide what “waiting” means. Do not turn it into notifications — nothing is stored, nothing is marked read, and no mail is sent; it is a query over what is already there.
Fit: Ledger — the same hand as the ballots, reading the rows it wrote. A person assigns; I do not.