Public: anyone can read this; the crew can change it.

A studio page spawns one git process per row to decide the word "shipped" #319

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

Studio pages now ask git about production once per repository per page, not once per merged row. Before, `caughtUp` ran `git merge-base` for every merged change it worded; now it reads one `git rev-list <production commit>` and every row checks against that list.

shipped production is serving it deploy d5b2fe2

opened this

Studio pages now ask git about production once per repository per page, not once per merged row. Before, caughtUp ran git merge-base for every merged change it worded; now it reads one git rev-list <production commit> and every row checks against that list.

What moved where

  • src/git/merge.ts has a new ancestorsOf(dir, oid): the full ancestry of one commit in a single spawn, with the same safe-oid guard as mergeBase.
  • src/web/studio/words.ts: WordCtx gains a required history field, created by servingHistory(). It stores the pending lookup, keyed by git directory and production commit. caughtUp now takes the WordCtx and checks set membership. So shipped still means production is at or past the merge's commit (ancestry, not equality), and the equality shortcut stays. Because the pending lookup is stored before it finishes, rows worded at the same time through Promise.all share one spawn.
  • The memo is created fresh in each page that builds a WordCtx: app.ts, change.ts, lists.ts (×3), run.ts and tell.ts. It is dropped when the render ends, so a deploy that lands shows on the next page. The home makes one memo and passes it into shelfRows through a new optional ShelfReads.history, so the shelf and the home's list of open pulls share it. Keying by directory keeps separate repositories separate.
  • docs/STUDIO.md now says how the ancestry check is bounded.

Tests

  • A new test in test/studio.test.ts ("words come from facts") merges five pulls and words all five with production at the second merge. It expects shipped, shipped, waiting, waiting, waiting and exactly one git spawn, counted with spyOn(Bun, "spawn"). It then moves production to the last merge on a fresh page and again expects one spawn. With the old per-row mergeBase put back, it fails with 4 spawns instead of 1.
  • Two test fixtures each gained one line, history: servingHistory(), because the field is required: test/apps.test.ts:1932 and the word fixture in test/studio.test.ts. No assertion changed, and test/studio-words.test.ts was not touched.
  • bun x tsc --noEmit is clean, and the forge.json check list passes: 462 tests across 23 files.

Chose not to

  • No cache that lasts across requests.
  • No --first-parent or --max-count limit on rev-list: either could misjudge a commit that reached main as a second parent, or an old merge. The cost is that the output grows with the repository's history (about 41 bytes per commit), from one process per page.

To try it: open /changes or the home on an app with several merged pulls. The words match what they said before, and each repository costs one git process per page load.

🤖 Generated with Claude Code

Closes #285

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs