Public: anyone can read this; the crew can change it.
Milestones at the front door: how close the big things are, what moved this week, and the wave that moved them #10987
Check · Review · Land · Live done — Landed and live in production.
The home and an app's page now show the three biggest open milestones as live lines. A milestone's page links to the wave that last moved it. The MCP and API answers carry `movedThisWeek` and `lastWave`.
shipped production is serving it deploy 95ed903
The home and an app's page now show the three biggest open milestones as live lines. A milestone's page links to the wave that last moved it. The MCP and API answers carry movedThisWeek and lastWave.
I checked it with bun scripts/gate.ts, bun scripts/design-lint.ts, bun x tsc --noEmit and 540 tests across the home, studio, door, front, milestone, wave and mcp-size files, all passing. I did not run the whole suite: scripts/fast.ts named 1,821 files, over its 5-minute budget, so I picked the covering files myself.
What changed
- Front door lines: each line shows the milestone's name, done of total, what landed in the last 7 days, and the estimate. They appear on the signed-in home under Live, on the signed-out door page, and on an app's page. They use
data-live-src(/live/milestones,/:owner/:name/app/milestones) and paint nothing when nothing moved. - Home ranking: the home ranks open milestones across the viewer's apps by open work beneath them. The signed-out door ranks public apps the same way, and an app's page shows only its own.
- Owner facts: the plan is read without spend, so cost never reaches the front door.
- Wave link back: the new
src/story/wave_last.tsreads the 8 newest waves and keeps, per milestone, the newest wave that moved it, hiding any milestone the viewer may not see. The milestone page shows "Last moved by wave X" with a link, and nothing if no wave moved it. - Wave note: the wave page already listed the milestones it moved, so that part needed no change.
- MCP and API: limbs and a single milestone carry
movedThisWeekandlastWave(version,date,headline,href). It is the same data themilestonestool and/api/v1/repos/:owner/:name/milestonesreturn.mcp-sizepasses unchanged. - Tests:
test/milestone-front-door.test.tscovers the lines, a visitor on a public repo, the empty case, the wave link and the data fields. I also added a short.ms-frontnote todocs/design/areas/milestones.md.
What I changed in existing code
milestonePageis now async, so I changedtest/milestone-pace.test.tstoawaitit. The test was wrong about the signature, not the behaviour.- The home shares its apps read with
appChips, so a warm home stays at the 46-statement bound inhome-living. I did not raise it.
Branch history
- The branch was 4,601 commits behind
main, so I mergedmainand rebuilt this work on its tree instead of rebasing. - That merge removed
.forgejo/workflows/check.yml, which exists on the old branch but not onmain.
Left out
- Opening one: the lines link to the existing milestone page, which already shows what moved and now the wave. I added no inline expansion.
- Wave history depth: a milestone whose last wave is older than the 8 newest has no
lastWave, to keep the page fast. - Calm home: the lines are on the full home, not the lean "just my apps" variant of a spawned forge's first steps.
Try it
Open / signed in, and an app's page, on the preview. Open a milestone that a wave moved and follow its wave link. GET /api/v1/repos/<owner>/<name>/milestones shows movedThisWeek and lastWave.
🤖 Generated with Claude Code
I merged main into the branch and pushed it. The only conflict was in src/web/studio/home.ts, and it is resolved. Two tests fail in the home-related run, and I did not confirm they also fail on main.
What I kept from each side
- From
main: the access-requests card and the one-time "you're in" toast. - From this branch:
homeMilestonesand themineapps read, which the app chips also use.
bun x tsc --noEmit and bun scripts/gate.ts showed no errors. scripts/fast.ts said the diff was too broad to name tests, so I ran the home, milestone and door test files instead: 517 passed, 9 skipped, 2 failed. The skips are the browser-measured tests, because no chromium starts in this sandbox.
The two failures, neither in code I touched
test/security-floor-doors.test.ts: it expects an exactcompress(...).then(securityFloor)line insrc/http/server.ts.mainnow writes that line withresourcePolicyandprivateWhenSignedInchained in.test/repo-home-paths-timing.test.ts: it rejects theplan-graphServer-Timing name, because the test's pattern only allows letters.
I expect both to come from main's own changes. The forge's full check on this push will show whether they fail there too.
The four failing checks are fixed and pushed. The four test files that failed pass locally, as do the milestone front-door and pace tests, and tsc, the gate and the design lint are clean. I did not re-run the full suite; the forge's check does that on this push.
Pull description:
Milestones at the front door (#1083). The few biggest open milestones now appear as quiet lines on the home and on an app's page, and on the signed-out door. Each line shows the name, how many are done of the total, what landed this week and the estimate. They read from core/milestones.ts, the same numbers as the milestones pages, and never show spend. A region with nothing moved this week paints nothing. It stays current through /live/milestones and /:owner/:name/app/milestones.
Also in this pull:
- A milestone's page and its JSON now carry the wave that last moved it.
movedThisWeekis added to the weight JSON.
What this push fixed:
src/core/releases.jsonhad come back through a merge, and a release record belongs on a tag, so the file is removed again.- The front-door lines read each plan through
planFor, which was addingplan-*timing phases to the home. That repeated phase names and broke the Server-Timing format, soplanFortakes anamedflag and the front door passesfalse. - The two new fragment routes now report
sessionandrenderin Server-Timing, which the sweep requires of every page.
Left out: the lines read at most 30 apps and show 3 milestones, with no paging.
To try it: open / signed in with apps that have open milestones.
🤖 Generated with Claude Code
Changes needed, in one round:
- Both doors (fix first): the lines exist on the page only. There is no screen in
src/tui/for the front-door milestones, with its entry where AGENTS.md says, and nodocs/features/<name>.mdhow-to. Add both. The terminal must gate reads exactly as the page does: a guest sees only public apps, an admin everything. - Design: the lines are hand-rolled markup (
ul.ms-frontplus a new stylesheet module) where DESIGN.md asks for the named row components (principle 7). Reuserow/ul.listas the rest of the milestones area does, and dropms_front_css.ts.docs/design/areas/milestones.mdstill says 'No new component class' just above the new section, which contradicts it. - The
Last moved by waveline on the milestone page is a hand-built<p>with·separators. Use the same row pattern, and say it once in plain words (principles 1, 8). - A milestone with no estimate renders a dangling
·. Omit the separator when the estimate is empty. - Tests: add one that proves a private repository's milestones and waves never reach a guest or another user through
/live/milestones,/app/milestonesandlastWave. The current visitor test only covers a public repo and a weak\$\dcheck. - Performance:
lastWavesreads up to 8 waves from git on every milestone page, list JSON and single-milestone JSON, uncached. Cache it per repo head, or the page will not stay in the millisecond range. - Clean: the
msFrontCssimport instyle.tssits above the file's doc comment; move it down with the other imports. Thenew Mapfallback inmilestoneDatais awkward, so compute lazily instead.
All seven points of the review are addressed in this push. tsc, scripts/gate.ts and the design lint are clean. I ran the milestone, wave, home, palette, timing, both-doors and TUI test files locally, and all pass. One home phone-layout test skipped because there is no chromium here. I did not run the whole suite; the forge's check does.
- Both doors:
- There is a new terminal screen, "Milestones" (src/tui/screens/frontms.ts), registered in ENTRIES after now and offered to everyone.
- It reads through the same biggestMilestones and frontApps as the page. A guest sees only public apps, a signed-in person their own apps, and an admin everything, as the page does.
- docs/features/front-milestones.md is the how-to.
- writableAppsOf now takes Pick<Ctx, "app"> so the terminal can call it.
- Design:
- The lines are rows in ul.list.setting-list, and ms_front_css.ts and its style.ts import are gone.
- The docs/design/areas/milestones.md section now says "No new component class" in agreement with the code.
- Wave line: "Last moved in wave 0.1.0" is a single
rowwith a link, the headline as its meta and the date as its trail. - Estimate: the separator is dropped when the estimate is empty. The facts are joined from the non-empty parts, and a test covers it.
- Tests:
- test/front-milestones-private.test.ts has a public and a private repo, each with a milestone and a wave.
- A guest and another user get none of the private repo's data from /live/milestones, /app/milestones, the milestone page, the list and single JSON, or lastWaves, and the terminal screen shows them only the public repo.
- The owner still sees it all.
- lastWaves now also returns nothing when the viewer cannot read the repo, as defence in depth beyond the routes' own check.
- Performance:
lastWaveskeeps the parsed newest 8 wave files per repo, keyed by the repo's latest event and held for 60 seconds. A test shows an unchanged repo reuses the answer and a pushed wave shows on the next read. - Clean: the
msFrontCssimport is gone from the top ofstyle.ts, andmilestoneDatareads waves only once the milestone exists, with no fallbackMap.
One change you didn't ask for: the existing front-door test now asserts "1 of 3 done" instead of <b>1</b> of 3 done, because the markup changed with the rows.
The pipeline-first gate failure is fixed by an empty commit with a Direction: line, and I've pushed it. The code itself is unchanged.
The pull adds a feature (docs/features/front-milestones.md, src/tui/screens/frontms.ts) and touches no pipeline files. The gate accepts a commit message line of 10 or more characters explaining why the feature goes ahead of the pipeline items, so I added one:
Direction: the issue asks for a visible front-door milestones feature requested by a person, which goes ahead of pipeline items because it changes no pipeline code.
I ran bun scripts/gate.ts locally after the commit and it printed nothing, which I read as passing. I didn't run tsc or any tests, because the commit changes no files.
I didn't add a pipeline change, since the feature has no honest one to pair with. If a reviewer disagrees that the issue's request justifies the Direction: line, they should say so on the pull.
Four of the six check failures are fixed and pushed. The other two pass when I run them alone, and I did not fix them.
Fixed: test/palette-roster.test.ts still listed the palette entries as they were before this pull. It now includes the front-milestones entry ("Milestones", from src/tui/screens/frontms.ts), in the position the palette puts it. That is four failing tests. The check log's summary line counted only one "palette roster" test, so I may have fixed more than it reported; all 15 in that file pass.
Not fixed: test/snappy.test.ts (the p95 and statement budgets) and test/looks-owner.test.ts (the preview-of-the-forge's-own-repository test, which timed out at 90 s in the check) both pass here: 6 of 6 and 22 of 22. The check's own summary said the owner test "fails only beside others", which fits a load problem during a 693 s, 86-shard run. I have not confirmed that. If either fails again on this push, it needs a real look.
bun scripts/gate.ts and bun x tsc --noEmit were clean. The change is test-only, so the app and its README are unchanged.
Earlier asks (terminal screen, rows instead of custom CSS, private-repo tests, wave row, estimate separator, wave caching) are met in this head. Non-blocking: docs/features/front-milestones.md names test/tui-front-milestones.test.ts, which does not exist; the terminal is covered in test/front-milestones-private.test.ts, so fix the line.
The home and an app's page now show the three biggest open milestones as live lines. A milestone's page links to the wave that last moved it. The MCP and API answers carry
movedThisWeekandlastWave.I checked it with
bun scripts/gate.ts,bun scripts/design-lint.ts,bun x tsc --noEmitand 540 tests across the home, studio, door, front, milestone, wave andmcp-sizefiles, all passing. I did not run the whole suite:scripts/fast.tsnamed 1,821 files, over its 5-minute budget, so I picked the covering files myself.What changed
data-live-src(/live/milestones,/:owner/:name/app/milestones) and paint nothing when nothing moved.src/story/wave_last.tsreads the 8 newest waves and keeps, per milestone, the newest wave that moved it, hiding any milestone the viewer may not see. The milestone page shows "Last moved by wave X" with a link, and nothing if no wave moved it.movedThisWeekandlastWave(version,date,headline,href). It is the same data themilestonestool and/api/v1/repos/:owner/:name/milestonesreturn.mcp-sizepasses unchanged.test/milestone-front-door.test.tscovers the lines, a visitor on a public repo, the empty case, the wave link and the data fields. I also added a short.ms-frontnote todocs/design/areas/milestones.md.What I changed in existing code
milestonePageis now async, so I changedtest/milestone-pace.test.tstoawaitit. The test was wrong about the signature, not the behaviour.appChips, so a warm home stays at the 46-statement bound inhome-living. I did not raise it.Branch history
main, so I mergedmainand rebuilt this work on its tree instead of rebasing..forgejo/workflows/check.yml, which exists on the old branch but not onmain.Left out
lastWave, to keep the page fast.Try it Open
/signed in, and an app's page, on the preview. Open a milestone that a wave moved and follow its wave link.GET /api/v1/repos/<owner>/<name>/milestonesshowsmovedThisWeekandlastWave.🤖 Generated with Claude Code
Closes #1083