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

The front page shows more than what is open: a state filter and each proposal's activity #37

stopped no changes pushed: API Error: Can't reach the API server — check your internet or DNS (EAI_AGAIN) (api_error) opened this
opened this

/ calls listProposals with state: "open" and nothing else, so a withdrawn proposal is unreachable from any page in the app — only from /api/proposals?state=withdrawn — and a row says how many options a proposal has but nothing about whether anyone has said a word. Give the list a filter and a pulse.

What done looks like

  • [ ] listProposals returns remark_count and last_activity_at (the later of the proposal's updated_at and its newest remark's created_at) on every row, from the one query it already runs — no second query per proposal.
  • [ ] listProposals takes an optional sort, one of newest (default, id DESC, what it does today) or active (last_activity_at DESC); anything else is invalid with a sentence. / asks for active; /api/proposals keeps newest unless the caller says otherwise, so no existing script changes shape.
  • [ ] / takes ?state=open|decided|withdrawn|all, default open. The filter is a row of plain links — no JavaScript — each naming its state and the number of proposals in it, with the current one marked aria-current="page". An unknown value renders the default list with 200 and no scolding.
  • [ ] A row shows the remark count and how long ago the last thing happened beside the option count. A proposal nobody has remarked on says nothing at all rather than “0 remarks”.
  • [ ] The counts per state come from one grouped query, not four.
  • [ ] The live region still works filtered: static/live.js refetches location.href, so the section keeps its id and a proposal that opens while ?state=withdrawn is on screen does not silently appear.
  • [ ] A guest sees the filter and the counts; nothing on this page needs an identity.
  • [ ] The terminal's list (src/tty.ts) and MCP's list_proposals carry the same two new facts, and list_proposals gains sort in its schema with one sentence.
  • [ ] Tests: a withdrawn proposal is absent from / and present under ?state=withdrawn; ?state=nonsense answers 200 with the open list; the state counts are right with proposals in all three states; remark_count and last_activity_at are right for a proposal with remarks and for one without; sort=active puts a freshly remarked older proposal first; a seeded 200 proposals with 1000 remarks still answers / in well under 50 ms.

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, static/app.css.

Risks: The state is a word from a query string — whitelist it against STATES plus all and never interpolate it into SQL; the existing STATES check is the pattern. last_activity_at as max(p.updated_at, coalesce(newest remark, p.updated_at)) in SQL keeps it one query; doing it in TypeScript means pulling every remark. This is not #24: decided proposals get their own page and feed there, and this filter must not grow a feed or a tally. Keep the empty state from #20 — an empty ?state=withdrawn needs its own one-line empty text, not the four-sentence first-visit block.

Fit: Ledger — the record as a product: every state reachable, every row saying when it last moved. A person assigns; I do not.

commented

API Error: Can't reach the API server — check your internet or DNS (EAI_AGAIN)

commented

Interrupted, not the work (outage; strike cleared 2026-10-02) — was: Parked by the crew: the run ended without a pull: no changes pushed: API Error: Can't reach the API server — check your internet or DNS (EAI_AGAIN) (api_error)

commented

API Error: Can't reach the API server — check your internet or DNS (EAI_AGAIN)

commented

Interrupted, not the work (outage; strike cleared 2026-10-02) — was: Parked by the crew: the run ended without a pull: no changes pushed: API Error: Can't reach the API server — check your internet or DNS (EAI_AGAIN) (api_error)

commented

Amendment from the planner. The issue stands: production's / shows only open proposals, and a withdrawn one is reachable from no page in the app.

Added boundary, now that #44 exists: the progress line on a row — the tally's reason — belongs to #44, not here. This issue gives / its state filter, its counts and each row's activity. Whichever of the two lands second keeps both; neither may re-derive the other's numbers in a template.

Narrowed, to keep it one run: drop sort from MCP's list_proposals and from /api/proposals. / sorts by activity, and that is the whole user-visible point; a sort parameter on two more doors is a second issue's worth of schema, sentences and tests for a thing nobody has asked for yet.

The park here was a DNS failure — nothing was attempted.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs