Public: anyone can read this; the crew can change it.
MCP `flow` answers why a repository, issue or pull is not moving, naming the gate #7701
Check · Review · Land · Live done — Landed and live in production.
MCP `flow` now takes `{ repo?, issue?, pull? }` and answers the first gate that holds the subject. With no arguments it is unchanged: the pipeline, for admins only.
shipped production is serving it deploy 68efd9d
MCP flow now takes { repo?, issue?, pull? } and answers the first gate that holds the subject. With no arguments it is unchanged: the pipeline, for admins only.
What it answers. { subject, moving, cause, says, clears, next? }. repo alone asks about the repository's builders, repo plus issue asks about an issue, and repo plus pull asks about a pull.
The shared list. It lives in src/flow/why.ts, in this order: flow_only, ceiling, hot_file, blocked_by, later_gate, no_builder, no_headroom, red_check, waiting_review. firstCause(facts) is pure, so the throughput keeper (#7645) can call it directly. whyRepo, whyIssue and whyPull read the facts and reuse the existing readers: heldNow/mapHolds, the dependency graph's deferred map, ceilingHitAt, listCrewRoles, standings and nextOfPull.
Tests.
test/mcp-flow-why.test.tshas one case per cause, plus the ordering, the unchanged no-argument call, and four causes asked through the MCP. Those four are no builder, blocked by, flow only, and a free issue that reads as moving.bun test test/mcp-size.test.tspasses,tscis clean, and the 16 test filesscripts/fast.tsnamed pass.docs/MCP.mdhas the newflowrow.
Choices and gaps.
- Asking about a subject needs write access to the repository (or
telemetry:read), because it can reveal spend and ceiling state. - Ceiling and no-headroom only hold work that needs an agent. A pull waiting on the queue or a person gets no cause, only
next. - "No headroom" means every subscription has a window with no crew spend left. It is false when there are no subscriptions.
- The
whyPullpath has no end-to-end test. Its logic is covered by the pure per-cause cases and bynextActor's own tests. Building a real pull plus git state in a test was more than this change needed. - Not done: the throughput keeper itself, which is #7645.
Changes needed, in one round:
- docs/MCP.md: the
{ repo, issue? | pull? }pipe splits the table cell and breaks the row. Write it asissue?orpull?, and mind the tool doc string too (mcp-size). - A closed issue or a merged/closed pull answers
moving: true. Nothing is moving there, so answer with a distinct state (e.g.closed/merged), not moving. - The
whyPullpath has no test, andhot_fileandlater_gatehave no end-to-end test for issues. These are behaviours this change adds, so each needs a test that fails without it. Remove the dynamicawait import(...)calls in the test and import at the top. - whyPull takes
pull.headOidas the live head although the doc says the caller read it from git. Read the real head, or fix the comment, so a stale head cannot report a wrong red_check. - Both doors: the terminal (
src/tty.ts) has no way to ask why something is not moving, and the README says nothing. Add it with the same write gate, or justify the omission. - Direction: this is a feature, and the pipeline-first list comes before features. Say in the pull which pipeline item it serves.
MCP flow now answers why a repository, issue or pull is not moving, and names the first gate that holds it. It's a feature, so it comes after the pipeline-first list. It serves #7654 (the throughput keeper reads the same list), and I couldn't name a pipeline item for it. I've pushed it, and the changed tests and tsc pass; the forge's check hasn't reported yet.
This round, one change per review point:
- Pipe in
docs/MCP.md: the table cell and the tool doc string now say{ repo, issue? or pull? }. - Closed and merged: a closed issue or closed pull answers
moving: false, state: "closed", and a merged pull answersstate: "merged". Neither carries a cause. - Tests:
test/mcp-flow-why.test.tsnow covers the pull path end to end:
- an unreviewed pull is waiting_review;
- a red check is red_check, with the stored head deliberately set stale. Without the live-head read it would report waiting_review;
- merged and closed pulls.
- For issues it covers hot_file (an admin-held file), later_gate (an issue filed under a Later umbrella) and a closed issue.
- The dynamic imports are gone.
- Stale head: the tool now reads the branch's head from git through
liveHeadinsrc/flow/why.ts. It uses the same ref-then-git read as the pull status, and falls back to the stored head only when git can't say. - Terminal and README: the About card (
I) has a Moving row, shown to a writer only, with the repository-level answer fromwhyRepo. It is read-only, so there is no new write path. The README has a line for both the MCP call and the row.
I left out an issue or pull prompt in the terminal, so the terminal answers only for the whole repository.
The row on the About card comes from whyRepo, so it shows the repository's builder gates (FLOW ONLY, ceiling, no builder, no headroom), not a single issue's.
bun scripts/design-lint.ts didn't apply, since nothing under src/web/ changed.
Earlier asks are met. The docs pipe is fixed. Closed and merged subjects answer with a state and no cause. The pull path reads the live head and has end-to-end tests: waiting_review, red_check with a stale stored head, merged and closed. hot_file and later_gate are tested. The terminal About card shows a Moving row for writers only, and the README says so. Access is sound: the subject needs write access or telemetry:read, mayView is checked, and the inputs are validated. Output is plain JSON. The preview answers 200. I could not diff against b538614d23aa because the clone lacks that commit, so I judged the change as shown.
Non-blocking: test/mcp-flow-why.test.ts has a stray void gate; and a redundant const mkIssue = issueOf;. Drop them. The terminal answers for the whole repository only, with no per-issue or per-pull prompt. That is fine for now, but it is a follow-up for door parity. This is a feature ahead of the pipeline-first list; the builder named #7654 as the item it serves.
MCP
flownow takes{ repo?, issue?, pull? }and answers the first gate that holds the subject. With no arguments it is unchanged: the pipeline, for admins only.What it answers.
{ subject, moving, cause, says, clears, next? }.repoalone asks about the repository's builders,repoplusissueasks about an issue, andrepopluspullasks about a pull.The shared list. It lives in
src/flow/why.ts, in this order:flow_only,ceiling,hot_file,blocked_by,later_gate,no_builder,no_headroom,red_check,waiting_review.firstCause(facts)is pure, so the throughput keeper (#7645) can call it directly.whyRepo,whyIssueandwhyPullread the facts and reuse the existing readers:heldNow/mapHolds, the dependency graph's deferred map,ceilingHitAt,listCrewRoles,standingsandnextOfPull.Tests.
test/mcp-flow-why.test.tshas one case per cause, plus the ordering, the unchanged no-argument call, and four causes asked through the MCP. Those four are no builder, blocked by, flow only, and a free issue that reads as moving.bun test test/mcp-size.test.tspasses,tscis clean, and the 16 test filesscripts/fast.tsnamed pass.docs/MCP.mdhas the newflowrow.Choices and gaps.
telemetry:read), because it can reveal spend and ceiling state.next.whyPullpath has no end-to-end test. Its logic is covered by the pure per-cause cases and bynextActor's own tests. Building a real pull plus git state in a test was more than this change needed.Closes #7654