Carried over from the archived managoat/fountain-workbench#32 when the demo
suite was consolidated into this repository. The original repo has been deleted,
so this is the surviving copy.
- Originally opened by @jhgaylor on 2026-08-24 (PR
https://github.com/managoat/fountain-workbench/pull/32)
- Now lives at:
apps/fountain-workbench
- Status: unmerged feature work. The diff was 1,248 lines — too large for an
issue body — so it is preserved in the repository at
salvage/fountain-workbench-pr-32.diff.
git apply --directory=apps/fountain-workbench salvage/fountain-workbench-pr-32.diff
Splits the surfacing half out of item f17d065ff15f. The mechanism landed in #19; this is where it shows.
A proposal is the one state in the app that does nothing until a person answers it, and it rendered in exactly two places — the item rows on the project page and the WorkItem header, both reached by already knowing to go there. So a verdict sat unanswered until somebody happened to look at the item it was on.
The sidebar
An open item carrying a verdict gets a tiny needs you next to its title — the words the board's own column (src/lib/board.ts) already uses for exactly these items, so it is one name across four screens.
The item asked to decide the wording before committing, and the short form deliberately does not say the verdict: a pill reading "won't do" on an item that is still open is the exact confusion wont was separated from done to avoid. The sentence — "Coder says: won't do. Nothing has been retired — the item is still open." — moves into the title.
On whether it fits: I can't screenshot in this sandbox (no headless browser), but the sizing question has a concrete answer without one. The tree already carries a pill in that slot at that size — the one a closed item gets, won't do, eight characters in .pill.tiny. "needs you" is nine. And the two never appear together: the tiny proposal pill is only for an open item, which is the one case the status pill is absent. That reasoning is pinned as a test rather than left in the PR.
The count
Not a fourth number in 12 open · 3 done · 1 won't do. Those three partition the items and a proposal does not — an item waiting on you is still counted in open, which is the whole point of the field being separate from the status — so a fourth number in that line would read as a fourth state. The item itself flagged this ("adds a number to a row that already carries three").
Instead it rides the survey, beside the other things that are happening rather than filed. ItemCounts, ProjectDto and Db.itemCounts are untouched, and a test pins that proposing moves none of the three.
Activity gains proposals — the projects list renders it as a pill next to "N working".
ActivityDto gains proposals: ProposalEntry[] and proposalsDropped.
The bell
Since there is a lot of notification infrastructure now, and a proposal is the same shape of fact as "N agents are blocked waiting on you", it goes in the panel too — between the two things already there. The order is how long each will wait: a blocked agent is on a five-minute clock, a verdict waits forever but is a question somebody asked you, a finished conversation is news and news keeps.
A verdict row links to the work item, not a thread — the same argument the blocked rows made and won, and it lands harder here: Confirm is what retires the item's computers, and the notes saying why won't do are on the item. The top bar is outside every project so it holds no team and says "A teammate says: won't do"; the item page, which has the team, names them.
Two properties worth calling out:
- It answers without Fountain. A proposal is a column on the work item, so that half of the survey is a database read — a refused key or a Fountain timeout is a hole in the conversation sweep, and a question standing on an item is not something the browser should stop being told about because of it. Tested.
- The per-project number is counted before the list is capped, so it cannot say 50 while 55 are waiting.
- Confirm and Dismiss both drop the row immediately (
proposalAnswered, the same early-application feedRead already does), rather than leaving the bell saying "1 verdict waiting on you" for the rest of the minute.
Tests
New cases in server/app.test.ts: the entry's shape and that a member of a shared project is told; a proposal moves none of open/done/wont, and confirming it does; dismissing clears the row and leaves the item open; oldest-first ordering with the cap keeping the oldest while the per-project number stays exact; a proposal left on a closed item is waiting on nobody; and the survey answering with the owner's key refused. Plus src/lib/feed.test.ts and both component tests.
Gate
$ bun run typecheck && bun test && bun run build
387 pass
0 fail
1449 expect() calls
Ran 387 tests across 33 files. [5.16s]
✓ built in 1.55s
One caveat, reported rather than hidden: the cost suite's one conversation Fountain will not answer for is a hole it names test is flaky in a full run — roughly 1 in 8. It reproduces on origin/main unchanged and touches nothing here. Already filed as cf756055744a.
🤖 Generated with Claude Code
Splits the surfacing half out of item
f17d065ff15f. The mechanism landed in #19; this is where it shows.A proposal is the one state in the app that does nothing until a person answers it, and it rendered in exactly two places — the item rows on the project page and the
WorkItemheader, both reached by already knowing to go there. So a verdict sat unanswered until somebody happened to look at the item it was on.The sidebar
An open item carrying a verdict gets a tiny needs you next to its title — the words the board's own column (
src/lib/board.ts) already uses for exactly these items, so it is one name across four screens.The item asked to decide the wording before committing, and the short form deliberately does not say the verdict: a pill reading "won't do" on an item that is still open is the exact confusion
wontwas separated fromdoneto avoid. The sentence — "Coder says: won't do. Nothing has been retired — the item is still open." — moves into thetitle.On whether it fits: I can't screenshot in this sandbox (no headless browser), but the sizing question has a concrete answer without one. The tree already carries a pill in that slot at that size — the one a closed item gets,
won't do, eight characters in.pill.tiny. "needs you" is nine. And the two never appear together: the tiny proposal pill is only for an open item, which is the one case the status pill is absent. That reasoning is pinned as a test rather than left in the PR.The count
Not a fourth number in
12 open · 3 done · 1 won't do. Those three partition the items and a proposal does not — an item waiting on you is still counted inopen, which is the whole point of the field being separate from the status — so a fourth number in that line would read as a fourth state. The item itself flagged this ("adds a number to a row that already carries three").Instead it rides the survey, beside the other things that are happening rather than filed.
ItemCounts,ProjectDtoandDb.itemCountsare untouched, and a test pins that proposing moves none of the three.Activitygainsproposals— the projects list renders it as a pill next to "N working".ActivityDtogainsproposals: ProposalEntry[]andproposalsDropped.The bell
Since there is a lot of notification infrastructure now, and a proposal is the same shape of fact as "N agents are blocked waiting on you", it goes in the panel too — between the two things already there. The order is how long each will wait: a blocked agent is on a five-minute clock, a verdict waits forever but is a question somebody asked you, a finished conversation is news and news keeps.
A verdict row links to the work item, not a thread — the same argument the blocked rows made and won, and it lands harder here: Confirm is what retires the item's computers, and the notes saying why won't do are on the item. The top bar is outside every project so it holds no team and says "A teammate says: won't do"; the item page, which has the team, names them.
Two properties worth calling out:
proposalAnswered, the same early-applicationfeedReadalready does), rather than leaving the bell saying "1 verdict waiting on you" for the rest of the minute.Tests
New cases in
server/app.test.ts: the entry's shape and that a member of a shared project is told; a proposal moves none of open/done/wont, and confirming it does; dismissing clears the row and leaves the item open; oldest-first ordering with the cap keeping the oldest while the per-project number stays exact; a proposal left on a closed item is waiting on nobody; and the survey answering with the owner's key refused. Plussrc/lib/feed.test.tsand both component tests.Gate
One caveat, reported rather than hidden: the cost suite's
one conversation Fountain will not answer for is a hole it namestest is flaky in a full run — roughly 1 in 8. It reproduces onorigin/mainunchanged and touches nothing here. Already filed ascf756055744a.🤖 Generated with Claude Code