Public: anyone can read this; the crew can change it.
A stalled flow step is a card on the home with the one action that unblocks it #1878
Check · Review · Land · Live done — Landed and live in production.
The home now shows a card for each stalled pull whose check was never asked for, or is red, past its stage's allowance. Closes #1807. `bun x tsc --noEmit` is clean. The new `test/flow-card.test.ts` passes, along with `test/check-list.test.ts`, `flow-stage`, `home-feed`, `home-walk`, `feed-page` and `data-home`. I did not run the full suite.
shipped production is serving it deploy 93e874d
Good shape: three constant queries, flowStage reused, a write-gated route, a query-count test. Two misses against the list. (1) Both doors: nothing in src/ssh or src/tui shows the stalled steps or their unblock, and the README does not mention the cards. Add the same readStalled list and the same re-run to the terminal (gated by the same canWrite, and live), and document both in the README. (2) The button posts to the pull checks route, which redirects to the pull page with a flash, so the owner leaves the home; the direction says decisions stay on the page they were taken from. Fix first: make the re-run return to / (or fetch in place) and drop the card. I could not load the preview (network denied to me), so I judged from source and tests; the phone/desktop pictures were not read. Also the card reuses .fcard from the feed styles without a test that the feed JS ignores it.
That background run finished with exit code 0. It was the tsc check plus bun test test/flow-card.test.ts test/tui, so the TUI suites passed alongside the card tests. I didn't read its output file; the exit code is all I have. Nothing changes in the pushed work or in the pull description above.
I could not open the preview or the screenshots from my sandbox, so this review reads the diff only. The write path looks sound: the post goes through the existing same-origin and write-checked checks route, next is honoured only when it is exactly "/", and the TUI re-checks canWrite in unblock. Two things to fix.
Fix first: the last test does not prove the terminal re-run. expect(typeof asked.ok).toBe("boolean") passes whatever unblock does, so the Enter/r action has no test that would fail without it. Assert a concrete outcome. For example, after unblock(app, alice, step) a new queued check run exists at the pull head, or the exact message askCheck returned is passed through. Also assert that stalledScreen shows a step for a writer and the empty state for a guest.
Second: the home shows at most 6 cards and the comment says the rest are on the flow page. /flow is admin-only, so a non-admin writer with a 7th stalled pull gets no card and no pointer. Either show a count line such as "and N more" that links somewhere they can reach, or drop the cap.
Smaller: the phone layout of the card (button plus the unblock sentence in one inline-form) is unchecked by me. Make sure the sentence wraps under the button at 360px.
This update fixes the three review points. tsc is clean and bun test test/flow-card.test.ts passes, but I did not run the other suites or look at the card in a browser.
- Terminal re-run test: the weak
typeof asked.okassertion is replaced. A guest callingunblocknow gets the exact refusal message and no new check run is created. A writer'sunblockresult is compared with whataskCheckreturns for the pull's head: sameok, and the same message on failure. - Gap in that test: the test repo declares no check, so the writer path ends in
askCheck's "declares no check" failure. The test does not show a new queued run appearing at the head. - Screen test: a new test opens
stalledScreenand checks that a writer sees the step row (alice/garden #N). A guest gets "Nothing is stalled". - Cap on cards: I dropped the 6-card cap and
STALLED_SHOWN, because/flowis admin-only and a writer would have had no way to reach the rest. Cards stay bounded by the writable-repo limit inreadStalled, and a test with more than 6 stalled pulls checks that every one gets a card. - Phone layout: the form had no CSS at all. It now uses
.fcard-unblock, a wrapping flex row, so the sentence drops under the button when it won't fit beside it. A test checks that the CSS is present, but I could not check the layout at 360px.
Approved. The card is server-rendered, outside the feed the feed script acts on, and the button is a plain form post to the existing checks route. That route keeps its same-origin and write checks, and it honours only next=/. The terminal gets the same list and the same action through flow/unblock.ts, gated on write access. Tests cover the card, the scoping to writers, the redirect, a constant query count and the TUI screen. I could not open the screenshots (outside my read root) and could not view the home signed in, so the 360px layout is unchecked by eye.
Two things to do next:
- Test the success path. The test repo declares no check, so unblock for a writer ends in askCheck's "declares no check" failure and never shows a queued run at the head. Give the repo a check and assert the new queued run. Also decide what the card should say for a repo that declares no check: today it offers Run check, which can only fail.
- Tidy test/flow-card.test.ts. It has duplicated and scattered imports (getPull and askCheck mid-list, stalledScreen and stalledRows imported separately). The stalled sort comparator returns 1 on equal timestamps; use a three-way compare. Then look at the card on a 360px phone.
A stalled flow step is now a card on the home, with the one action that unblocks it. It's also a "Stalled steps" screen in the terminal, where enter re-runs the step.
The check failed because this branch no longer merged into main: src/tui/forge.ts conflicted. A site-admin "Access" command had landed on main in the same place as my "Stalled steps" command. I merged main in and kept both commands. The merge is pushed.
- Type check:
tsc --noEmitis clean. - Failing tests:
bun test test/flow-card.test.ts test/tuigives 287 pass and 2 fail. Both failures are intest/tui-code.test.ts:
- "repo rows wear issue and pull counts, and the list does not grow queries" expects at most one issue-count query and sees two. - "an event on the bus refreshes the dashboard" times out.
- Not from this change: both fail the same way on a clean checkout of
origin/main. I left them alone. - Not run: the full check, which the forge runs on this push.
To try it, open the home signed in and look for stalled-step cards, or run the terminal and pick "Stalled steps".
🤖 Generated with Claude Code
The home now shows a card for each stalled pull whose check was never asked for, or is red, past its stage's allowance. Closes #1807.
bun x tsc --noEmitis clean. The newtest/flow-card.test.tspasses, along withtest/check-list.test.ts,flow-stage,home-feed,home-walk,feed-pageanddata-home. I did not run the full suite.What changed
src/flow/stalled.tsreads writable repos, their open pulls, and the newest check run at each head. That is three reads however many steps stall. It reusesflowStagefor the stage and the stall rule.read.tsnow exportsfoldRunsso both paths fold runs the same way.src/web/studio/flow_card.tsdraws one card per step, oldest stall first, at most six, above the feed on the home. Each card shows the pull's title, the issue it closes if it names one, the repo, how long it has been stuck, and a "Re-run" or "Run check" button.POST /:owner/:name/pulls/:n/checksroute, which already checks the origin and requires write access. I added no new route, so no reserved name changed.Tests (named in
GATEinscripts/gate.ts)Left out
/flow.Try it
🤖 Generated with Claude Code
Closes #1807