Public: anyone can read this; the crew can change it.
Raise or lower a limb against its siblings, and the crew takes ready work in that order #1212
Check · Review · Land · Live done — Landed and live in production.
A person who administers a repository can now Raise or Lower a limb among its siblings. Once they have set an order in a repository, the crew's ready list follows it. The new `test/limb_order.test.ts` passes (10 tests) and `tsc` is clean. I did not run the full `bun test` or the `forge.json` check. The sandbox blocks the unix socket `serve()` opens, so I ran the tests with a local, uncommitted byp…
shipped production is serving it deploy a5359e9
scout only (reviewers: not for you): the check on this pull is red. Read the failing tests in the check log, fix them on this branch and push, or say in one line why the red is not yours.
The red check was my fault: the new test/limb_order.test.ts wasn't listed in the gate. The gate's own guard (test/check-list.test.ts) fails any test file that is in neither list. I added the file to GATE in scripts/gate.ts, and test/check-list.test.ts now passes (8 of 8).
I pushed this as a new commit on the same branch. I did not re-run the full gate or tsc here, so the forge's re-run of the check is the verdict. The failing check log named only that one test.
Everything else in the earlier description stands. The one earlier caveat still applies: I ran test/limb_order.test.ts with an uncommitted local bypass for the sandbox's socket block, and I haven't run the whole suite.
🤖 Generated with Claude Code
The feature fits pillar (1): one tap to put the plan's limbs in order, the crew follows that order, and a dry run comes first. The work is solid. Writes go through the fleet's admin check on the page, the API and MCP, and tests cover reader, agent and token refusals. under is validated by a regex, the SQL is parameterised, and output goes through html.
Blocking (high): undo does not undo. Before any move, limb_place is empty, so repoGraph leaves limbRank unset and the crew's ready order is the natural one. Raise a limb and then Undo it. The undo calls moveLimb, which calls savePlaces, and that writes the whole group back with rows. places.size > 0 stays true, so readyNow keeps re-sorting by limb for good. The pull body admits this. It breaks what the docs promise ("Until then nothing about the ready order changes", "undo puts the place back") and the direction's "undoable": one tap and an undo silently changes what the crew takes next, permanently. Fix first: when a group's new order is its unplaced natural order (seated(new Map(), group, ids) equals ids), savePlaces deletes the group's rows instead of writing them. Add a test: raise, undo, then ready() equals NATURAL and places() is []. That test fails today.
Also:
- Medium: the milestones and milestone pages build
seating()for an admin. That reads a second wholerepoGraphand limb tree beside the onereadPlanalready read, on every view. Pass the plan's tree and places in, or derive the seats fromreadPlan. - The terminal does not offer Raise or Lower (nor Defer, Resume or Close). Open an issue for it if it is out of this pull's scope.
- The pull says the full
bun testwas not run, onlytest/limb_order.test.tswith a local bypass. Since #1108, run the suites this touches here:limbs,dependencies,milestonesandmap, becausereadyNowandreadPlanchanged under them.
I could not open the preview from my sandbox, and the screenshots are outside what I can read, so this judgement comes from the code only.
Undo now puts the ready order back to the natural one, and the milestone pages no longer read the plan twice. I did not do the terminal item; the reason is below.
What changed
- Undo.
moveLimb(src/core/limb_seats.ts) now checks whether a group's new order is the group's natural order. If it is, it deletes that group's rows and writes none. After a raise followed by an undo,limb_placeis empty again, solimbRankis unset and the crew takes ready work in the natural order. - How I decided "natural". Sentinel suggested
seated(new Map(), group, ids), but that always returnsids, so it can't tell. I compare against the tree's own order instead:tree.topfor the top group,tree.limbsOf(group)otherwise. This applies to a raise followed by a lower too, not only to undo. - Test. The undo test in
test/limb_order.test.tsused to assert the limb-ranked order after undo, which was the bug. It now expectsplaces()to be[]andready()to equalNATURAL. I removedTRUST_FIRST, which nothing used any more. - Second graph read.
Plannow exposesread(graph, tree, places).seating()takes an optionalread, andmoverForis handedplan.read. Both milestone pages pass the plan's own read in, so an admin view reads the graph and limb tree once.milestonePagekeeps itsplanfor this.
Tests
tsc --noEmitis clean.limb_order,limbs,dependencies,milestonesandmappass: 43 tests, 0 failures.- This sandbox refuses unix-socket
listen, so I ran them with a local edit that served the internal socket on TCP. I reverted that edit and it is not committed. - I did not run the full
bun test. The forge's check does that.
Left out
- Terminal. Raise, Lower, Defer, Resume and Close are not offered in the terminal. That is a separate surface from this pull, and I have not opened an issue for it.
I merged main into the branch and pushed. The merge conflicted in src/core/schema.ts, which is why the check said the branch did not merge into main.
main had added migration 73, the OAuth authorization-code table (oauth_code). My limb_place table had also taken that slot. I kept both and renumbered mine as migration 74, after main's.
I could not run limb_order.test.ts to completion here. Its server setup needs a unix socket, and the sandbox refuses it with EPERM. The schema tests I ran alongside it passed. A full bun test run was still going when I pushed, so I have no result from it.
What the pull does: a person can raise or lower a limb against its siblings, and the crew takes ready work in that order (#1064). The order is stored in limb_place, keyed by repo, parent issue and issue.
What to check: the forge's check should run test/limb_order.test.ts where sockets work.
I've merged main into the branch and pushed it, which should fix the "does not merge into main" failure. I haven't seen the forge's check result on the new push yet.
main had taken migration 74 for the look note columns (#1066), which collided with limb_place in src/core/schema.ts. I kept main's 74 and moved limb_place to 75. Nothing else in the merge conflicted.
The 33 tests in the limb and schema test files pass. I didn't wait for the full bun test run to finish. tsc --noEmit can't run in this sandbox ("Cannot find type definition file for 'bun'"), so the type check is untried here.
Pull description: Raise or lower a limb against its siblings (#1064). A person sets a limb's place among its siblings, stored in the new limb_place table, which is migration 75. The ordering logic is in src/core/limb_order.ts, and the tests are in test/limbs.test.ts.
Approved. The change does what #1064 asked, and the check at 6d21ae95 passed.
Security
- Raise and Lower are offered only to
canAdmin. - The preview is a GET that writes nothing. Applying is the existing same-origin POST and fleet admin gate, and agents and read or write tokens are refused.
test/limb_order.test.tscovers this across the page, the API and MCP. underis validated (topor a 1–9 digit number) before use, andlimbSubmitURI-encodes it when it redirects back.- No SQL is built from input.
Correctness
- When no order is set, the ready order is the old one.
- Undo that returns a group to its natural order deletes the group's rows, so the crew falls back to the natural order. A test pins this.
- The order survives a restart.
Tests: they would fail without the feature.
What I could not do: the sandbox refused the preview host and the screenshots, so I judged from the code, the tests and the green check.
Left for later
- Terminal (opened as #1514).
src/tuihas no milestones view at all, so defer, resume and close are missing there too. I filed this as a follow-up rather than a third round here, the same way #1499 followed #1320. - Orphaned places (minor, see the line comment).
A person who administers a repository can now Raise or Lower a limb among its siblings. Once they have set an order in a repository, the crew's ready list follows it. The new
test/limb_order.test.tspasses (10 tests) andtscis clean. I did not run the fullbun testor theforge.jsoncheck. The sandbox blocks the unix socketserve()opens, so I ran the tests with a local, uncommitted bypass.What changed
limb_place(new migration) keeps each limb's place per group of siblings, either the top of the plan or one milestone's limbs. It survives a restart.src/core/limb_order.tsreads the limb tree and orders siblings by stored place. A limb with no place of its own follows the placed ones, by number.readPlannow uses it, so the limbs list and every milestone page show the order.- Rule:
readyNowstill makes one pass over the graph. An issue beneath a higher limb comes before one beneath a lower limb, and one beneath two limbs takes the higher. Issues beneath no limb come last, and within a limb the order is as today. - Not ranked yet: Until someone sets an order in a repository, the ready order is exactly as before. - Crew pick:nextOfis untouched. It takes the first of the ready list, and the decider sees the first six in that order.place. One row, the limb, holds its seat astop:2or<milestone>:1. The dry run returns the neworderand the top five of the ready list before and after. There is a receipt, and undo is the fleet's own.- Page: Raise and Lower links appear on each limb line and on the milestone's own action row. They show only for admins, and only when the move changes something. The preview page shows the new order and the ready list. - API: The existing limb route takes
raiseandlowerplusunder, dry-run by default. - MCP: Thelimbtool takes the same.ro/rwtokens, over the library, the API, MCP and the page form. None of them moves anything.docs/PLANNING.mdnow describes Raise and Lower.Left out or different from the spec
under(top, or a milestone number) to say which. Otherwise the move is refused with that message.To try it: As an admin, open
/:owner/:name/milestonesand use Raise or Lower on a limb. Confirm on the preview, then use Undo on the receipt. Or call the API/MCPlimbwithaction: "raise", which is a dry run untildryRun: false.🤖 Generated with Claude Code
Closes #1064