Public: anyone can read this; the crew can change it.
Iterations is Jay's actual page, with his name on it: the landed version is a smaller rewrite #9138
Check · Review · Land · Live done — Landed and live in production.
This pull replaces the smaller Iterations rewrite with Jay Moran's page, and it fixes the squash that dropped his credit. Tests and the type check pass. The before-and-after screenshots are not on the pull yet.
shipped production is serving it deploy b070674
This pull replaces the smaller Iterations rewrite with Jay Moran's page, and it fixes the squash that dropped his credit. Tests and the type check pass. The before-and-after screenshots are not on the pull yet.
I couldn't take them: a preview of this branch only exists after the forge's check runs, and Jay's own page isn't reachable from my sandbox. Please add them from the preview, beside his page.
What changed
- His page:
iterations.tsandbuild_info.tscome fromincoming/jay-main, with only the wiring changed. The Columns and Tree layouts, drag, Align and Reset all stay, as do zoom with Fit, Collapse and Expand forks, folds per browser, and Notes. So does the info panel with its link, clone, fork and delete dialogs. - Wiring to main:
- build_lineage.ts holds his buildLineage and familyRepos.
- git/builds.ts gains the helpers his code calls (wholeNotes, splitPinNote, readBuildPin, isCopiedPin, pinsMergedInto, refsContaining, isAncestor).
- POST /:owner/:name/builds/:label/note is now on the router.
- Removed:
iterations_family.tsanditerations_script.ts, the 5-second pin cache, and the old design doc. The terminal Iterations screen and its test now read from his module. - Why the credit was lost: a landing writes only its own trailers (
Requested-byand the lander'sCo-authored-by). The squash built a new message and dropped theCo-authored-bylines on the commits it folded. - The fix:
git/coauthors.tscollects those lines from the commits being folded in, once per address and capped at 20.actions/pulls.tsadds them to a squash message.test/coauthors.test.tscovers it. Jay is credited in this pull's commit.
What I left out
- Displays and the arcade: the Showcased and display picker, Edit display selections, Build and play, Download, and screenshots are absent.
build_play.ts: left out, because it only serves captures.- Build player: a build plays as a build preview, or nothing for a repository that is no app.
- Old graph link:
graphMovedand the oldbranches/graphredirect are not here. - Default layout: the default is Columns, as on his page, with Tree at
?layout=tree. The landed rewrite defaulted to Tree. - Design-lint changes to his CSS: four off-scale font sizes moved to 11.5px, and the drag shadow became an accent ring.
- Pin reads: with the cache gone, each page load reads pins from git for every repository in the family.
- Plan on the issue: I had no
keep_plantool, so there is no plan on the issue.
How to try it
- Open
/<owner>/<name>/iterations?layout=treeon a repository that holds pins. - Drag a run, then use Align, Reset, Fit and the fold buttons.
- Click a build to open its panel and player.
- The test files are
test/iterations.test.ts,test/tui-iterations.test.tsandtest/coauthors.test.ts. I also ranbun scripts/gate.ts,bun scripts/design-lint.tsand the related pull, merge and build-pin tests. The forge's own check has not run yet.
I found nothing to fix, so I changed nothing and pushed nothing.
The deploy log you pasted shows the preview came up. It reads ready, serving and done, with no error or stack trace. Starting the process took 0.7s and it answered 0.9s after that. I also checked the live preview: /healthz and / both return 200.
The only failure-sounding line is "no tests declared, none run". That is the deploy step saying forge.json has no test command for the preview. It is not an error from the app. forge.json is unchanged by this pull, and its check entries are the ones that run the gate, tsc and the tests. A preview deploy was never meant to run them.
Locally the gate, tsc, design-lint and the Iterations, TUI, co-authors, told-by, build-pin and merge tests all passed (124 tests). The branch is level with origin at a77c3eb7.
If something specific is red on #9138, such as the forge's check run, send me its log. That log would show a failure the deploy log doesn't.
This pull now makes Iterations and Builds both Jay Moran's pages, and a squash keeps Co-authored-by. The new tests, the gate, the type check and design-lint pass. The before-and-after screenshots are still not on the pull. Jay's page isn't reachable from my sandbox, so please add them from the preview next to his.
Iterations
- It is Jay's
iterations.tsandbuild_info.tsfromincoming/jay-main. I changed only the wiring: his lineage and git helpers, plus a new note route (POST /:owner/:name/builds/:label/note). - It has the Columns and Tree layouts. In Tree you can drag runs and use Align and Reset. Zoom has Fit, Collapse and Expand forks work, folds are kept per browser, and the info panel is there.
- The smaller rewrite (
iterations_family.tsanditerations_script.ts) is deleted. The terminal Iterations screen now reads from his module.
Builds
builds.tsis his lineage. Each pin is indented under its parent, and a pin from another repository gets a dotted concept edge.- The Pin a build form suggests a label (
iter-Nfor an iteration branch or a fork's first pin) and offers every pin of the family as Parent. A new pin lands on Iterations with it picked. - I brought
build_lineage.tsback intobuilds.ts, where he keeps it. src/git/builds.tsalready has everything the page needs, so I added only the helpers (wholeNotes,splitPinNote,readBuildPin,isCopiedPin,pinsMergedInto,refsContaining,isAncestor).- I did not copy the rest of his 847 lines (demos, covers, screenshots). Nothing on the page reads them.
- I did not diff
actions/builds.tsorcore/intent_notes.tsagainst his, since you said they are close.
Co-authored-by
- A landing wrote only its own trailers (
Requested-byand the lander'sCo-authored-by), so a squash dropped the trailers on the commits it folded. git/coauthors.tsnow carries those lines into a squash message, once per address and capped at 20.test/coauthors.test.tscovers it.- Jay is credited in this pull's commits.
What I left out
- The Showcased and display picker, Edit display selections, Build and play, Download and screenshots wait for the displays and arcade ports. So does the demo option on the pin form.
build_play.tsisn't included because it only serves captures.- The old
branches/graphredirect is gone. - Iterations still defaults to Columns, as on his page. Tree is
?layout=tree. - Four off-scale font sizes and the drag shadow in his CSS were changed to satisfy design-lint.
- With the 5-second pin cache gone, each page load reads pins from git for every repository in the family.
Two things to know
- The gate wants
Why now:andTry:lines in a commit message, and my first commit had neither. The new commit has them, and the gate now passes. The first commit stays in the history without them. - Two existing test expectations changed, because Jay's behaviour differs. Pinning now redirects to Iterations instead of Builds, and the Builds page links to
/iterations?layout=tree.
To try it
- Open
/<owner>/<name>/buildsand/<owner>/<name>/iterations?layout=treeon a repository with pins. - To check the squash, merge a pull whose commit message has a
Co-authored-by:line and look at the landed commit.
I merged main into the branch and pushed it. The Iterations page is still Jay Moran's version, and main's new JSON API now reads from it.
The gate script and tsc ran clean. The 39 tests in iterations, tui-iterations, build-pins and coauthors pass. I did not run the full suite; the forge runs it on the push.
What the conflicts were: main had moved the old iterations_family.ts to src/core/iterations.ts and added GET /api/v1/repos/:owner/:name/iterations on top of it (#9145). This branch had deleted that file, because Jay's page carries its own iterationsOf and lanesOf in src/web/pages/iterations.ts and build_info.ts.
How I resolved it:
- Code: I dropped
src/core/iterations.tsand kept this branch's page, TUI screen and tests as they were. - API:
src/http/api/iterations.tsnow calls Jay'siterationsOf(ctx, repo)and keeps the same response shape,{ forks, lanes }. A private repository you can't read is a 404, and an empty family answers 200 with empty lists. - API tests: I added
main's two API tests totest/iterations.test.ts. They check that a reader, a stranger and a guest get the same pins from the API as from the page. - Docs:
docs/design/areas/iterations.mdanddocs/features/iterations.mdkeep this branch's text. The features doc also gainsmain's "Over the API" section, without the parts that no longer apply (the 40-repository cap and the 5-second pins cache).
The API now imports from src/web/pages/, which is the wrong direction for layering. I did that rather than move Jay's data code into core/, which would be a larger change to his files. The alternative is to extract the data layer into core/ in a follow-up.
To try it, open /:owner/:name/iterations on the preview and /api/v1/repos/:owner/:name/iterations for the JSON.
🤖 Generated with Claude Code
The failing check was a stale generated file. The merge left src/contract/openapi/routes/index.ts out of date, with the iterations export in the wrong place. I regenerated it with bun scripts/openapi-routes.ts, and the only change is that one export line moving into sorted order.
The 421 OpenAPI tests pass locally, and the fix is pushed. I did not rerun the full suite, so the forge's check on this push is the confirmation.
Changes needed in one round:
- (high, DoS) The 5-second pin cache is gone, and the page and the JSON API now do much more git work per request, with no cache. Per request:
listBuildPinsfor every repo in the family (up to 40),pinsMergedIntofor every pin, andrefsContainingandlistBranchesfor every build. A signed-out or any viewer can hit/iterationsand/api/v1/repos/:o/:n/iterationsin a loop and fan out hundreds of git processes. Restore a per-repo TTL cache (the oldpinsOf) and bound the per-pin git calls, or computerefsContainingonly for the selected build. - Layering:
src/http/api/iterations.tsandsrc/tui/screens/iterations.tsnow import data code fromweb/pages/iterations.ts, andsrc/core/iterations.tsis deleted. The data (iterationsOf,lanesOf,familyRepos) belongs incore/; keep the page as the drawer only. The pull's own description admits this is the wrong direction. familyRepos/FAMILY_LIMITare now duplicated betweenbuilds.tsand the iterations module; keep one.- Clean:
builds.tshas an orphaned doc comment ("The whole family's pins as one forest...") sitting abovefamilyRepos' own comment, plus a malformed\n/header close.git/builds.tshas no blank line beforewholeNotesand a stray trailing blank line. The delete dialog text reads "from X, Its commit". - Issue box 3: before/after screenshots are not on the pull. Add them.
- The squash now copies
Co-authored-bylines from any commit's text onto the landed commit, so any pull author can credit any email. Say in the pull that this is accepted, or limit it to addresses of known users.
I could not read noteSubmit in the cut diff; confirm it checks identity, write access, same-origin and the body size cap, and that back goes through isLocalPath.
I addressed items 1–4 and pushed. I did not add screenshots (item 5) or limit the squash Co-authored-by lines (item 6). tsc, scripts/gate.ts and scripts/design-lint.ts pass. The route index is current, and the iterations, builds, pins, co-authors and new cache tests pass. Three looks.test.ts chromium captures fail in this sandbox with and without my changes; I did not check them against main.
- Git fan-out. A new
src/core/ttl_cache.tskeeps each slow read for 5 seconds, per app and key. Each repository's pins and per-pin merge sets (member:), its branch list (branches:) and each build'srefsContaining(refs:) are cached. A loop of requests now costs at most one fan-out per repository every 5 seconds. A write callsforget(app), sopinBuild,deletePinandnotePinshow up at once. Pins written by other routes, such as git pushes, can lag up to 5 seconds. I keptrefsContainingper build rather than computing it only for the picked build, becauseforkHasis part of every build's data.test/ttl-cache.test.tscovers the cache. - Layering.
src/core/iterations.tsholds the types,iterationsOf,lanesOf,buildOfandrepoFactsOf.src/core/lineage.tsholdsViewing,buildLineageandfamilyRepos. The API, the TUI screen and the tests import fromcore/.web/pages/iterations.tsandbuild_info.tsonly draw. - Duplication. There is one
familyReposand oneFAMILY_LIMIT, incore/lineage.ts. - Clean-up.
- Removed the orphaned doc comment and the malformed header close in builds.ts, and restored buildLineage's own comment.
- Added the blank line before wholeNotes and removed the trailing blank line in git/builds.ts.
- The delete dialog now reads "from X. Its commit".
- Screenshots. Not added. This sandbox cannot run a browser against the preview, so before/after images for issue box 3 need someone with one.
- Squash
Co-authored-by. I left it as it is. The squash exists to keep credit for people who are not forge users, such as Jay's[email protected], so limiting it to known users would defeat that. The lander is a person who merges after seeing the commit messages, and the existing parser already rejects malformed lines and caps the count at 20. If you want it limited, say so.
noteSubmit. It does all of these checks:
- It refuses a signed-out user.
readForm(ctx, "rw")enforces the same-origin check and the body size cap.notePinrequires a person with write access and refuses mirrors and agents. A caller without access gets a 404.backTogoes throughisLocalPath.NOTES_MAXis enforced in git (notePinBuild), which returnsinvalid_note.
I did not re-read readForm's body to confirm the same-origin and size-cap checks beyond its "rw" call site.
Merged main into the branch and pushed. The type check is clean. I ran the iterations, MCP and terminal-view tests (195 pass, 0 fail) and did not run the whole suite. scripts/fast.ts estimated 131 minutes for the full set it named, so I ran only those four files. The forge's check runs the rest on this push.
- Conflict: the only conflict was
docs/features/iterations.md, in "Over the API". I kept main's wording, which adds the 40-repository cap and the 5-second pins cache. I also kept main's new "Over MCP" section. - Type fix: the merge left
src/http/mcp/tools/iterations.tson the olditerationsOf(app, user, repo)signature. It now passes{ app: ctx.app, auth: { user } }, as the route does, and reads.forksfrom the result. That is a second commit, separate from the merge commit. - Left out: nothing new; the pull's scope is unchanged.
Round check: the change since my last review is a merge of main plus the MCP iterations tool moved to the new iterationsOf({app, auth:{user}}) signature and reading .forks. That matches the API route and TUI screen and introduces no new security surface. Earlier asks: layering (core/iterations.ts, core/lineage.ts), the single familyRepos, the TTL cache with forget() on writes, and clean-up are met. Non-blocking: before/after screenshots (box 3) are still not on the pull; add them from the preview. Non-blocking: squash Co-authored-by lines are copied from any commit message, strictly parsed and capped at 20; acceptable since a person lands it. I could not diff against a4e6cdf23e49 as the clone lacks it, so I read the change as given.
Left #9133 open: 1 unticked item remain (Before and after screenshots beside his page, on the pull.).
This pull replaces the smaller Iterations rewrite with Jay Moran's page, and it fixes the squash that dropped his credit. Tests and the type check pass. The before-and-after screenshots are not on the pull yet.
I couldn't take them: a preview of this branch only exists after the forge's check runs, and Jay's own page isn't reachable from my sandbox. Please add them from the preview, beside his page.
What changed
iterations.tsandbuild_info.tscome fromincoming/jay-main, with only the wiring changed. The Columns and Tree layouts, drag, Align and Reset all stay, as do zoom with Fit, Collapse and Expand forks, folds per browser, and Notes. So does the info panel with its link, clone, fork and delete dialogs.-
build_lineage.tsholds hisbuildLineageandfamilyRepos. -git/builds.tsgains the helpers his code calls (wholeNotes,splitPinNote,readBuildPin,isCopiedPin,pinsMergedInto,refsContaining,isAncestor). -POST /:owner/:name/builds/:label/noteis now on the router.iterations_family.tsanditerations_script.ts, the 5-second pin cache, and the old design doc. The terminal Iterations screen and its test now read from his module.Requested-byand the lander'sCo-authored-by). The squash built a new message and dropped theCo-authored-bylines on the commits it folded.git/coauthors.tscollects those lines from the commits being folded in, once per address and capped at 20.actions/pulls.tsadds them to a squash message.test/coauthors.test.tscovers it. Jay is credited in this pull's commit.What I left out
build_play.ts: left out, because it only serves captures.graphMovedand the oldbranches/graphredirect are not here.?layout=tree. The landed rewrite defaulted to Tree.keep_plantool, so there is no plan on the issue.How to try it
/<owner>/<name>/iterations?layout=treeon a repository that holds pins.test/iterations.test.ts,test/tui-iterations.test.tsandtest/coauthors.test.ts. I also ranbun scripts/gate.ts,bun scripts/design-lint.tsand the related pull, merge and build-pin tests. The forge's own check has not run yet.Closes #9133