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

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

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

opened this

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.

What changed

  • Read: src/flow/stalled.ts reads writable repos, their open pulls, and the newest check run at each head. That is three reads however many steps stall. It reuses flowStage for the stage and the stall rule. read.ts now exports foldRuns so both paths fold runs the same way.
  • Card: src/web/studio/flow_card.ts draws 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.
  • Button: it is a plain form post to the existing POST /:owner/:name/pulls/:n/checks route, which already checks the origin and requires write access. I added no new route, so no reserved name changed.
  • Disappearing: nothing is stored. Once the check is queued or green, or the pull is merged or closed, the next render has no card.

Tests (named in GATE in scripts/gate.ts)

  • A red check shows a "Re-run" card, and queuing the re-run removes it.
  • A pull with no check shows "Run check", and a fresh pull shows no card.
  • A reader without write access sees no card.
  • A cross-origin post to the button's route gets 403.
  • The query count is the same with 6 more stalled steps.

Left out

  • Only the no-check and red-check cases get a button. A check that is still running has nothing to unblock, so it gets no card.
  • Review, land and issue-stage stalls get no card here. They have no single same-origin action today (merging needs a strategy and may be refused for an agent, for example) and stay on /flow.
  • A card links to the pull and shows "for #N" from its body, but it does not look up the issue's own title, to keep the query count constant.

Try it

  • Sign in, open the home, and look above the timeline.
  • To see a card, leave a pull open for more than an hour with no check, or with a red check for more than 30 minutes.

🤖 Generated with Claude Code

Closes #1807

Changes requested — before the latest push

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.

commented

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.

Changes requested — before the latest push

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.

commented

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.ok assertion is replaced. A guest calling unblock now gets the exact refusal message and no new check run is created. A writer's unblock result is compared with what askCheck returns for the pull's head: same ok, 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 stalledScreen and 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 /flow is admin-only and a writer would have had no way to reach the rest. Cards stay bounded by the writable-repo limit in readStalled, 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 — before the latest push

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:

  1. 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.
  2. 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.
commented

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 --noEmit is clean.
  • Failing tests: bun test test/flow-card.test.ts test/tui gives 287 pass and 2 fail. Both failures are in test/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

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs