Public: anyone can read this; the crew can change it.
Trim at a limb: defer, resume or close a whole subtree in one action, with preview, receipt and undo #769
Check · Review · Land · Live done — Landed and live in production.
Closes #733
shipped production is serving it for issue #733 — its issue is already done deploy d5b2fe2
Final description (the pull body above was the early placeholder). Closes #733. Stacked on #737, which is stacked on #708: the diff against main is this pull's own once those land.
What it does
Whoever administers a repository acts on a whole limb from its milestone page, not on each leaf:
- Defer files the limb for later: out from under its pillar, under Later, behind the Later gate. One row is written.
- Resume is the reverse: out from behind the gate, under the pillar named, or under none.
- Close as not doing closes every open issue that only the limb needs, the limb included, as
dropped, with the reason said on each.
Each is ONE fleet operation (#447): a preview of every row it writes and every issue it takes along, nothing written until it is confirmed, a receipt, and one undo. The same through POST /api/v1/repos/:owner/:name/milestones/:number/limb and the MCP tool limb, both a dry run unless dryRun is false.
One rule changes: deferral reaches down
An open issue that only deferred work waits for is deferred too, and is never ready (deferredIn in core/dependencies.ts; readyNow leaves graph.deferred out). That is why deferring a limb is one row and not one per leaf, and why undo is exact. What something that is not deferred still waits for stays ready, and the preview lists it as staying. This applies to every repository that keeps a Later gate, not only to limbs deferred from this page.
Which module owns what
core/dependencies.ts: what is deferred (Graph.deferred) and so what is ready.core/milestones.ts: what a limb is —limb(n)(its own issues and the ones it shares),deferral(n),resumption(n).fleet/selection.ts: thelimb:<n>term (needsrepo:owner/name), top down.fleet/verbs.ts: two verbs,file(later, a pillar, ornone) anddrop(the reason). Both reversible. Undo now writes its rows backwards and keeps the receipt's order.actions/limbs.ts: says which query and verb each action is. It adds no rule of its own about who may.web/pages/limb.ts: preview, confirm, receipt, undo.web/pages/milestones.tsoffers the actions to an admin.
Who may
The fleet's rule, unchanged: a person who administers the repository applies; an agent may look and never applies; a row the actor does not administer is refused in the plan and not written. Over the API and MCP, a read token plans and only an admin token applies or undoes. The page's forms take a same-origin post from a signed-in session.
An umbrella or the gate is not filed. A leaf beneath a limb that is not deferred cannot be deferred alone. A mirrored repository is refused.
Tests
test/limbs.test.ts (in GATE), written refusals first: a reader, a builder agent, the planner, nobody, a repository that cannot be seen, read and write tokens, and a post from another origin change nothing by the action, the API, the MCP tool or the page; then the deferral rule; each action's preview equals what the apply writes; what is shared stays; undo restores exactly, once.
Not in this pull
Raise or lower a limb against its siblings is #760. Hand a limb to the planner is #761. Watching a milestone is #732.
🤖 Generated with Claude Code
Ready counts on the live map (trevato/forge, read once through GET /api/v1/repos/trevato/forge/map at 21:20Z on 2026-10-05; the rule run by this branch's own deferredIn over that data; 164 open issues, 503 edges, 80 ready)
| ready | deferred | leave the ready list | |
|---|---|---|---|
| today, without this change | 80 | 30 behind the gate (31 with the Later umbrella) | — |
| with this change, nothing deferred by hand | 80 | 30 | 0 |
| then: defer limb #351 "The feed…" (9 open beneath it) | 76 | 38 | 4 |
| or: defer limb #643 "Just go forge…" (11 open beneath it) | 79 | 40 | 1 |
So the rule alone changes nothing on the live plan; it bites only when a limb is deferred. Deferring #351 files 8 issues (the limb and 7 leaves); 2 stay because other work needs them (#599, #634). Deferring #643 files 10 and 2 stay (#334, #599).
What the measurement changed in this pull. 109 of the 154 open work issues are linked directly under a pillar's umbrella (the planner's rule from #612), so moving the limb alone would have left its leaves ready: the umbrella still waited for each. Deferring #351 that way would have deferred 1 issue and left ready at 80. Hence approach H: defer writes one previewed row for each open issue that is the limb's own, the limb first; resume brings them back leaves first; an umbrella's wait is membership, not need, for what counts as the limb's own. The rule the lead approved (an issue only deferred work needs is deferred) is unchanged, so nothing under a pillar is ever deferred without its own previewed row.
🤖 Generated with Claude Code
Description at a2f486d (supersedes the earlier final description). Closes #733. Stacked on #737.
What it does
Whoever administers a repository acts on a whole limb from its milestone page: Defer, Resume, Close as not doing. Each is ONE fleet operation (#447): a preview of every row it writes and every issue it takes along, nothing written until confirmed, a receipt, one undo. The same through POST /api/v1/repos/:owner/:name/milestones/:number/limb and the MCP tool limb, both a dry run unless dryRun is false; an admin token applies.
- Defer files every open issue that is the limb's own for later, the limb first: out from under its pillar, under Later, behind the Later gate. One row for each, so nothing is put off that the preview did not show.
- Resume brings them back, leaves first, under the pillar named or under none. An issue of the limb that was not filed for later is left as it is.
- Close as not doing closes every open issue that is the limb's own as
dropped, with the reason said on each.
What other work outside the limb still waits for stays, and the preview lists it as staying. An umbrella's wait is membership, not need: on this plan every issue is linked under its pillar, and that link does not make a leaf someone else's.
The rule, and the word
An open issue that only deferred work waits for is deferred too, and is never ready (deferredIn; readyNow leaves graph.deferred out, beside unadopted and the plan's own structural issues from #708). Every deferred issue reads deferred with why, in the same words everywhere — "behind the Later gate", "filed under Later", or "only deferred work needs it: #12 One interface", naming the deferred limbs above it — on the map (card, title, legend, counts), in the map JSON and the MCP map (deferredBecause), on its own page, and on the milestone (deferredBecause on limbs and leaves). Ready counts on the live map are in the comment above.
Which module owns what
core/dependencies.ts: what is deferred and why (Graph.deferred, a map of reasons);readyNow.core/milestones.ts: a limb's own and shared issues (limb(n), top down by Kahn's walk so an order among siblings holds),deferral(n),resumption(n).fleet/selection.ts:limb:<n>andlimb:<n>^(leaves first).fleet/verbs.ts:file,resume,drop. The loop guard knows the rows it is written with (Target.peers).actions/limbs.ts: which query and verb each action is; no rule of its own about who may.web/pages/limb.ts,web/pages/milestones.ts: preview, confirm, receipt, undo; the actions for an admin.
#737's follow-ups, in this pull
buildingNow is the list on a milestone's data and building stays the count; Later's milestone counts what waits behind the gate too; on a milestone each issue shows once (not under Issues when under Building now or Blocked).
Tests
test/limbs.test.ts (in GATE), refusals first; then the rule (shared work stays ready, an issue nothing waits for is untouched), the word on the map page, map JSON, MCP map, issue page and milestone data, each action's preview equal to its apply, and undo exact. test/milestones.test.ts covers the three follow-ups. test/plan-pillars.test.ts and test/map.test.ts expectations follow the new word.
🤖 Generated with Claude Code
Closes #733
In progress (steward build team). Stacked on #737 (milestones), #708 and #706, so the diff against
mainincludes them until they land. So far: the three trimming actions as fleet operations (defer, resume, close as not doing), the API route and the MCPlimbtool, with the refusing tests. The buttons, preview and receipt on the milestone page follow on this branch. Raise and lower are #760; hand to the planner is #761.🤖 Generated with Claude Code