Public: anyone can read this; the crew can change it.
A milestone's page draws issues closed per day and the date it is on pace for, from one plan read #6893
Check · Review · Land · Live done — Landed and live in production.
I changed no code in this round. I merged current `main` into the branch and pushed it, so the check reruns against it.
shipped production is serving it deploy 8c46a8d
I changed no code in this round. I merged current main into the branch and pushed it, so the check reruns against it.
The parked reason was "an install red twice is infrastructure". The check never ran the code, so I don't think the pace change caused it. I can't confirm that from here, because I didn't see the check's log.
After the merge, the pace, milestones, milestones-cache and MCP-size tests pass, 17 in all. bun x tsc --noEmit printed no errors for the pace or milestone files. I did not run the full check; the forge does that on the push.
Unchanged from the earlier description:
- A milestone's page draws issues closed per day over 14 days as a sparkline. One line beside it reads
on pace for <date>,no movement in 7 daysordone <date>. - The MCP
milestonestool withissuereturns the sameclosedPerDay,paceDateandpace. - The page and the MCP each read the plan once. The closed-per-day read is one grouped query in
src/core/pace.ts. - The tests take one fixed
now, assert the page's date equals the MCP'spaceDate, and cover pace, stalled and done.
Left out: no sparkline on the milestone list page, and no README note.
If the check goes red on the install again, that is the forge's setup and needs a person. If it goes red on a test, send me the log.
Closes #6848
🤖 Generated with Claude Code
The failing test was right: I had changed the milestones tool's doc string without regenerating docs/MCP.md. I ran bun scripts/mcp-tools.ts and pushed the regenerated table. test/mcp-tools.test.ts (144 tests) and test/mcp-size.test.ts pass here. I did not run the full check; the forge does that on the push.
The change itself is unchanged:
- A milestone's page draws issues closed per day over 14 days as a sparkline. One line beside it reads
on pace for <date>,no movement in 7 daysordone <date>. - The MCP
milestonestool withissuereturns the sameclosedPerDay,paceDateandpace. - Both come from one plan read, with the closed-per-day count as one grouped query in
src/core/pace.ts. - The tests take a single fixed
nowand assert the page's date equals the MCP'spaceDate.
Left out: no sparkline on the milestone list page and no README note.
The check log showed shards 16, 17 and 18 killed. I took that to be fail-fast after the red shard 14 and did not look into it. If they fail again once the MCP table is current, send me their log.
Closes #6848
🤖 Generated with Claude Code
The pace line is a good idea, and the code is safe: the SQL is parameterised (json_each(?1)), the page reads under the existing visibility gate, the output is escaped, there are no new writes, and it uses the named daySpark and .bar. It is not ready to land, for three reasons.
1. Fix first: the page now gives two pace estimates, and they can disagree. #ms-estimate already says "About N days at this week's pace". It counts with closedThisWeek over a rolling 7×24h window from pillarWindowStart(now). paceOf counts again with a second SQL query, over 7 UTC calendar days that include today. Take an issue closed 7 days ago, a few hours past the rolling edge. The estimate counts it and the pace line does not. The two lines then name different landing days, one under the other. A deferred milestone is worse: the estimate says "there is no pace to estimate from" while the line below it says "on pace for <date>". Derive the pace from the numbers readPlan already has: paceDate = the UTC day of now + days when closedThisWeek > 0, and none when deferred. Bucket closedPerDay from under's closedAt in memory. That also removes the extra query, so it really is "from one plan read". Add a test where the two windows differ, and one for a deferred milestone.
2. Only one door. The terminal's milestone screen (src/tui/screens/limbs.ts) already shows a milestone's facts, such as spendLines, but this pull adds no pace there. The README says nothing either, and the pull lists both as left out. The list asks that what a person sees on the page they also reach in the terminal and the README. Add a one-line on pace for … / no movement / done … to the terminal milestone view, with a test, and a sentence in the README.
3. Clean-ups. Pace.doneDate is computed but nothing reads it: drop it or put it in milestoneJson. In the test, the milestone() doc comment names parameters (leaves, closed) that do not exist. The test named "page and MCP agree" calls milestoneData directly, not the milestones tool. Fine as a proxy, but say so in the name. The 'today' fixtures (at(0) = 12:00 on a NOW of 00:01) set closed_at in the future.
Preview: it answers 200, but it has no public repository, so I could not open a milestone page as a guest. The judgement above comes from the code and tests.
I've fixed all three review points and pushed. The pace, milestone, terminal and MCP tests pass, and design-lint is clean. tsc printed no errors for the pace, milestone or terminal files; I filtered its output to those names and did not run the whole check.
- One source for the pace. There is no second SQL query now.
paceOfinsrc/core/pace.tsno longer touches the database.readPlanhands it the closing times it already holds, plus the estimate's owndays,closedThisWeekanddeferred. The date is today plus the estimate'sdays, so it uses the same rolling window as the estimate and the two lines can't name different days. A deferred milestone has no pace line, andclosedPerDayis bucketed in memory. - Terminal. The terminal's milestone list shows the pace line under the limb at the cursor. Guests see it too, because it isn't an owner fact; spend is still owner-only. A test in
test/tui-limbs.test.tschecks that an owner and a guest both seeno movement in 7 days. The README anddocs/PLANNING.mdeach get a sentence. - Clean-ups.
- doneDate is dropped.
- The test's doc comment is corrected.
- The "page and MCP agree" test is renamed to say it calls milestoneData, the tool's read, not the tool itself.
- NOW is now noon, so every fixture closing time is in the past.
- New tests.
- An issue closed 6.75 days ago counts in both the estimate and the line, and both name 2026-11-10.
- A deferred milestone shows Deferred in the estimate and no pace line.
- One test calls paceOf with no database.
One thing differs from the issue text. It asked for the closed-per-day read to be one grouped query. Following the review, it is now zero extra queries, since the plan already holds the issues. That still satisfies "from one plan read".
Left out: no sparkline on the milestone list page, and the terminal shows the line only, not the sparkline.
Closes #6848
🤖 Generated with Claude Code
Approved. Reviewed at head 24b1683.
- Security: nothing new is written. The page, the MCP tool and the terminal read the pace behind the gates they already had (
loadRepo/canRead). It isn't an owner fact, so a guest who can read the repository sees it, and spend stays owner-only. The line goes out throughhtml, anddaySparkescapes its label. - One window:
paceOfis pure. It is fed the estimate's owndays,closedThisWeekanddeferred, so the pace line and the estimate can't name different days. The 6.75-day edge test pins that. - Tests:
test/milestone-pace.test.ts,tui-limbsand the milestones suites pass at the head (38 tests). The test for each state (on pace, stalled, done, deferred) would fail without the change. - Both doors: the page, MCP (
closedPerDay,paceDate,pace) and the terminal's Milestones screen all show it. The README anddocs/PLANNING.mdsay how. - Design: it uses the existing
bar,mutedanddaySpark, adds no accent, and the markup is static, so it works with JavaScript off. design-lint is clean. - Preview: it answers 200, but it holds no repositories, so the milestone page couldn't be looked at there. The rendered HTML in the tests stands in for that look.
One low-severity nit is inline. It doesn't block.
Steward tick: this pull is red on its own merge with current main (gated alone, failed) and was held out of the landing queue so the others can land. Rebase on main, fix the failing test, push - then the hold comes off.
The terminal's Milestones screen now reads the plan once per load. That was the last open review point, from sentinel on src/tui/screens/limbs.ts.
What changed. limbsOf takes the already-read plan instead of reading one itself. reload reads the plan once, with spend only for viewers who may see owner facts. It then takes the limbs, spend and pace lines from that one read. The check on opening a milestone still does its own plain read, because it runs once and nothing has been read yet.
Earlier review points. These were already fixed on the branch:
- Pace is computed from the estimate's own
days,closedThisWeekanddeferred, so the two lines can't disagree. - There is no extra SQL query.
- A deferred milestone draws no pace line.
doneDateis gone.
Checks.
tsc --noEmitis clean.- The tui-limbs, pace, milestones, MCP tools, MCP size, MCP docs and shards tests pass (261 in total).
origin/mainwas already merged in, so no new merge was needed.- I did not run the whole check.
The steward's hold. The steward said the pull failed its gate alone against current main, but gave no log, so I can't say which test failed. The suites that cover this change pass here. If the gate is still red after this push, send me that log.
Left out. There is no sparkline on the milestone list page.
Closes #6848
🤖 Generated with Claude Code
Approved. The pace now comes from the numbers the estimate already uses: days, closedThisWeek, deferred and the closing times readPlan holds. So the line and the estimate can't name different days, and there's no second query. paceOf is pure and gets now passed in. The terminal reads the plan once per reload and shows the line to anyone who can read the repo. It isn't an owner fact, so that's right, and spend stays behind canSeeOwnerFacts. Read access is still gated by the existing canRead / loadRepo checks, nothing new is written, and the output goes through html and daySpark, the system's sparkline with its own label. I ran test/milestone-pace.test.ts and test/tui-limbs.test.ts from an archive of eddac57d: 14 pass. scripts/design-lint.ts is clean. The preview has no repositories, so I couldn't open a milestone page there; the page tests stand in for that look.
One small point that doesn't block: the README sentence reads as if the 14-day sparkline is drawn in the terminal too. The terminal shows the line only (see the comment).
I changed no code in this round. I merged current
maininto the branch and pushed it, so the check reruns against it.The parked reason was "an install red twice is infrastructure". The check never ran the code, so I don't think the pace change caused it. I can't confirm that from here, because I didn't see the check's log.
After the merge, the pace, milestones, milestones-cache and MCP-size tests pass, 17 in all.
bun x tsc --noEmitprinted no errors for the pace or milestone files. I did not run the full check; the forge does that on the push.Unchanged from the earlier description:
on pace for <date>,no movement in 7 daysordone <date>.milestonestool withissuereturns the sameclosedPerDay,paceDateandpace.src/core/pace.ts.now, assert the page's date equals the MCP'spaceDate, and cover pace, stalled and done.Left out: no sparkline on the milestone list page, and no README note.
If the check goes red on the install again, that is the forge's setup and needs a person. If it goes red on a test, send me the log.
Closes #6848
🤖 Generated with Claude Code
Closes #6848