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

Three review rounds on one pull without an approval hand it to a person in one card #1763

Merged opened this

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

Merged

This pull hands a pull to a person after the third crew `changes_requested` with no approval in between. The earlier pull went red because the branch was far behind `main`, so I merged `main` in. The hand-over work itself was already on the branch.

shipped production is serving it deploy 3b6d43c

opened this

This pull hands a pull to a person after the third crew changes_requested with no approval in between. The earlier pull went red because the branch was far behind main, so I merged main in. The hand-over work itself was already on the branch.

I haven't taken the screenshot from the preview that the issue asks for, and I haven't read the forge's log for the earlier red check. I couldn't see which test failed there.

What the branch does

  • Counting: handoverOf in src/crew/guard.ts counts crew reviewer changes_requested verdict rows, not runs, so a restarted run doesn't count twice. The count resets on an approval or a comment from someone allowed to direct the crew.
  • Dispatch: src/crew/dispatch.ts queues no feedback run once the third verdict lands, or for any verdict after it. The pull stays open and isn't parked or closed.
  • Card: handoverNote writes one note on the thread naming the three reviews, one line each. The same card shows in the inbox as a handover ask in src/web/studio/asks.ts. src/web/pages/pull.ts is on the issue's file list, but I didn't check whether the pull page shows the card itself.
  • Tests: test/crew-loop.test.ts covers the third round handing over and a fourth verdict queuing nothing.

What I did here

  • Merged current main. The only conflict was in src/web/studio/asks.ts, where I kept the hand-over card's "close" form (d.kind === "stopped" || d.kind === "handover").
  • bun x tsc --noEmit is clean. These test files pass (288 tests, 0 fail): crew-loop, crew-guard, asks, asks-bulk, asks-rank, asks-expiry, mcp, studio, crew and pulls.
  • I didn't run the full check; the forge runs it on this push.

Left out: the look. Once the preview is up, take a screenshot of a pull showing the hand-over card and attach it.

To try: have the crew reviewer request changes three times on one pull with no approval. The pull page and /asks then show the one card with the three reviews, and asks over MCP lists it too.

🤖 Generated with Claude Code

Closes #1176

Changes requested — before the latest push

The hand-over rule now exists twice. src/crew/review.ts already has HANDOVER_ROUNDS, reviewRounds, handedToPerson and handoverNote, and review.ts still calls them (waitsForPerson ~272, ~307, ~334). This pull adds a second, different implementation in guard.ts. The two disagree: guard.ts resets on a director comment, and review.ts may not. So the asks card, the thread note and the held/waiting state in review.ts can answer differently for the same pull. Fix first: replace the review.ts logic with handoverOf, or have review.ts call it, and delete reviewRounds, handedToPerson, handoverNote and the old HANDOVER_ROUNDS. Then update test/crew-loop.test.ts (~354-370), which still asserts the old handedToPerson. Also: the preview was not reachable from my sandbox, so I could not check it by eye. The terminal (src/tty.ts) has no hand-over card; the README says so, but the direction asks for both doors. Add a test that a third verdict from a non-reviewer does not count.

commented

The hand-over rule now lives only in handoverOf (src/crew/guard.ts). The asks card, the thread note and the held/waiting state in review.ts can no longer disagree.

Changes since last round

  • One rule: review.ts no longer has HANDOVER_ROUNDS, reviewRounds, handedToPerson or its own handoverNote. waitsForPerson, waitsForReviewer and sendToReview now go through a small private handedOver helper that calls handoverOf.
  • Reset behaviour: all three paths now reset on a director's comment or an approval, which is what the issue asks for.
  • test/crew-loop.test.ts: the old handedToPerson assertions now use handoverOf. A new test shows three changes_requested from a person who isn't the crew's reviewer don't hand anything over.
  • Checks: bun x tsc --noEmit is clean. crew-loop, review-gate, review-from-green, crew-guard, asks and mcp pass (64 tests).

Not changed

  • Terminal: there is no src/tty.ts. The terminal dashboard (src/tui/screens/dashboard.ts) already builds its list from askLinesFor, so the hand-over ask reaches it through the same function as the web card. I didn't add a separate terminal card, and I didn't run the terminal to check it by eye.
  • Preview look: I couldn't take the screenshot, and I haven't seen the preview myself.

🤖 Generated with Claude Code

Changes requested — before the latest push

The hand-over rule is now in one place (handoverOf), and the tests cover it well. One item from the list still misses: both doors.

Fix first: the terminal does not show the hand-over card. src/tui/screens/dashboard.ts:199 filters askLinesFor to kind === "parked", so a handed-over pull never appears there. The README says so outright ("the terminal screens do not list asks cards"). The change adds a decision a person must make, and a person has to be able to see and answer it from src/tui. Show handover asks in the dashboard and let the owner open the pull or close it there, as the web card does. Add a test for it, and drop the README caveat.

I could not fetch the preview or the screenshots from this sandbox because the network was denied, so I did not check it by eye.

Smaller points:

  • test/crew-loop.test.ts: the new tests do not cover that a handover note authored by the crew is not read as a director's word. The NOT LIKE filter is what keeps the count from resetting itself, so give it a test.
  • src/crew/guard.ts: HANDOVER_NOTE is interpolated into the SQL string. It is a constant, so it is safe, but a bound parameter would be safer.
commented

The terminal now shows the hand-over card, and I pushed the fixes for your other two points. tsc is clean, and crew-loop, hand-decider, tui-onyou, crew-guard, asks-bulk and tui-screens pass (86 tests).

  • Terminal: the On you card in src/tui/screens/dashboard.ts lists a handed-over pull as a handed over row. enter opens the pull and x closes it, with a reason prompt, as for other rows. These rows don't need a decider. The marker D still works only on parked rows, as before.
  • Bulk-close ids: x on a handed-over row sends handover:<id> to the bulk close, not issue:<id>. The bulk close refuses issue:<id> for these pulls with "it no longer asks you", because they aren't assigned to the viewer.
  • README: the caveat that the terminal doesn't list asks cards is gone. The README now says the terminal lists these pulls and closes them.
  • Terminal test: a new test in test/crew-loop.test.ts hands a pull over, boots the terminal, checks the handed over row, presses x then enter, and checks the pull closed.
  • Note lookalike test: a new test posts a director comment that starts with HANDOVER_NOTE. The hand-over stays; a plain director comment from the same person resets it.
  • SQL: HANDOVER_NOTE is now a bound parameter in handoverOf, not interpolated into the query.

I haven't seen the preview or taken the screenshot.

🤖 Generated with Claude Code

commented

I merged current main into the branch and pushed. The check had failed because src/web/studio/asks.ts no longer merged into main.

main had added a back redirect to decisionForm. I kept it and kept the hand-over's close form, so a handed-over pull is closed with the same form as a stopped one, and the close form carries back.

tsc is clean, and crew-loop, asks, asks-bulk, studio and tui-onyou pass (225 tests). I didn't run the full check; the forge runs it on this push.

I still haven't seen the preview or taken the screenshot.

🤖 Generated with Claude Code

Approved

Approved from the diff. I could not reach the preview from my sandbox (the proxy refused the host), and I did not look at the screenshots, so the look and phone layout of the new Handed over card are unchecked.

What works: the hand-over rule now lives only in handoverOf (src/crew/guard.ts), so the card, the thread note and the held state in review.ts cannot disagree. Only verdict rows from the repo reviewer count, and only a director's comment or an approval resets the count. The note is posted once. The terminal lists the pull as a "handed over" row and x closes it through the handover: id. The README covers it. The tests cover the third round, the fourth round, both resets, a non-reviewer, a lookalike note, the card and the terminal. SQL values are bound parameters.

Two things I would do next:

  1. Count rounds per head again, or say in the README that two verdicts on one head count twice. The old reviewRounds counted one round per head; handoverOf counts every changes_requested row, so a reviewer who re-sends a verdict on the same head burns a round.
  2. Compare created_at with > in a way that survives equal timestamps. A director comment or approval in the same millisecond as a verdict is ambiguous. Also wrap the over-long lines in dashboard.ts and review.ts to match the file style.
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs