You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Sites have no update path: patches age against a frozen trunk #94
A site is pinned forever to the trunk of the day it was created. The clone is shallow and single-branch (src/main.js:471-484) and no fetch, pull or reset operation exists anywhere in the app. Patches are diffed against that frozen local state, so a site created months ago produces patches against a months-old trunk — a real failure mode for a tool whose output is destined for Trac, where stale patches don't apply.
The patch path already fetches the remote trunk ref when it's missing (src/main.js:289-290) but never uses it as the diff base — design intent with no feature behind it yet.
Idea
An "update to latest trunk" action per site (fetch and reset, with a warning when local changes exist), and patches diffed against the remote trunk ref rather than the local snapshot. Even just surfacing the age of the checkout ("trunk as of March 3") would let a contributor know to recreate the site.
Updating is a chain, not a git operation
"Update to latest trunk" as a user action can't stop at the git layer. After trunk moves, the site may also need its dependencies and build refreshed — and a build against stale node_modules fails in confusing ways the target user can't diagnose. The full chain:
Fetch and reset to latest trunk (warning when local changes exist)
If package-lock.json changed, run the npm install step the app already has
Rebuild
Steps 2 and 3 exist in the app today; nothing triggers them when trunk moves. Implementing only step 1 turns this feature into a bug report.
The chain is cheaper than it sounds, measured on the real repo (July 1 → Aug 4, five weeks): the lockfile changed in only 3 commits, and even across the one bulk devDependency update, ~12% of the 2,604 packages changed — npm's cache means an update fetches only that delta, not the 1.5 GB tree. Over a typical few-day gap the install step is a no-op.
This is also what makes "prepare at home, refresh at the event" work (see #12): a site created days before a contributor day plus this update action equals a working environment on event-day wifi for kilobytes-to-MB, not gigabytes.
Mockups of what "update to latest trunk" could look like on today's site page. They follow the framing in the issue: updating is a chain, not a git call, and the UI has to show the whole chain or the feature reads as a bug.
1. The chain, as the UI tells it
Three steps, and the middle one is conditional. The UI names the skipped step ("Dependencies unchanged — skipping npm install") rather than hiding it, so "update" always means the same thing to the contributor.
2. Trunk age on the site page
"trunk as of 12 June 2026" sits next to the created date in every state — the issue's minimum ask, and shippable on its own. When the site falls behind, a notice appears above the action row with the consequence stated in patch terms ("patches you create now may not apply on Trac"), not git terms. A dot on the site in the sidebar surfaces staleness before you even open the site.
3. Running the chain
Left: the common case — lockfile untouched, so the install step is explicitly skipped. Right: the lockfile moved, and the install is framed as a delta against the npm cache, not a fresh 1.5 GB tree. Build output goes to the existing Terminal; no second log surface.
4. Local changes, and how it ends
The decision before step 1, when the tree is dirty. Three ways out: save the changes as a patch (a local .diff, nothing sent to Trac) and update clean; replay them on top of new trunk; or discard. Saving is the default — it's what the tool is for, and it's the only option that can't fail. On the right: success (naming the file it saved), and a failure mid-chain where trunk moved but the rebuild broke — worth its own state, because the code is new and the built assets are old.
5. Conflicts after replaying your changes
Only reachable from "keep them on top of new trunk". The app names the conflicted files and offers two honest paths: undo the whole update (back to old trunk, changes intact), or resolve the markers yourself — in an editor or with an AI assistant — and hit rebuild. Undo stays available the entire time.
6. The same box when a patch won't apply
Applying a saved patch, or one from a ticket (#11), fails for the same reason — changes written against a different trunk — so it should be one component. One difference: a patch that fails to apply leaves the tree untouched, so there's nothing to undo and the primary becomes "Leave it as it is". And when the mismatch is just age (patch newer than your checkout), the app should say so and offer to update first, instead of sending someone off to resolve conflicts that don't really exist.
Rename the files to match, or drop the images in first and paste GitHub's generated URLs over the placeholder paths.
With the update path implemented (#111), we measured what "update to latest trunk" actually downloads, against the real repo. Method: create a site whose git store contains only an old trunk snapshot, run the app's update, and diff the git store size before and after.
Scenario
Downloaded
Time
npm install
Fresh depth-1 clone (baseline)
~54 MB
~21 s
full
Update across a 1-week gap (Jul 28 → Aug 4)
~53.5 MB
~22 s
skipped — lockfile unchanged
Update across a 5-week gap (Jun 30 → Aug 4)
~53.5 MB
~23 s
runs, delta only
Two corrections to the issue body's framing:
The git layer costs ~54 MB flat, not "kilobytes-to-MB". Because sites are depth-1 shallow clones, the server can't compute a delta against history the client doesn't have, so every update fetch is a constant-size snapshot pack — the same ~54 MB whether the site is a week or five weeks behind. It is not the multi-GB working tree (rebuilt locally from the pack) and not the full history. A future optimization could deepen the clone enough for real delta negotiation, but ~54 MB on event wifi is already workable.
The npm layer is where the issue's argument fully holds. The install step is skipped outright when package-lock.json didn't change between the two tips — 3 lockfile commits in the whole Jul 1 → Aug 4 window, so a typical few-day gap skips it. When it does run, the "1.5 GB" figure is node_modules on disk, not network: npm's cache plus the existing tree means only changed packages download (~12% of 2,604 packages across the window's one bulk devDependency update).
Net for "prepare at home, refresh at the event" (#12): a site created days before the event costs ~54 MB + a local rebuild on event day, versus a full fresh setup. The rebuild is CPU, not bandwidth.
Activity
juanmaguitar commentedon Aug 5, 2026
Design exploration for this issue
Mockups of what "update to latest trunk" could look like on today's site page. They follow the framing in the issue: updating is a chain, not a git call, and the UI has to show the whole chain or the feature reads as a bug.
1. The chain, as the UI tells it
Three steps, and the middle one is conditional. The UI names the skipped step ("Dependencies unchanged — skipping npm install") rather than hiding it, so "update" always means the same thing to the contributor.
2. Trunk age on the site page
"trunk as of 12 June 2026" sits next to the created date in every state — the issue's minimum ask, and shippable on its own. When the site falls behind, a notice appears above the action row with the consequence stated in patch terms ("patches you create now may not apply on Trac"), not git terms. A dot on the site in the sidebar surfaces staleness before you even open the site.
3. Running the chain
Left: the common case — lockfile untouched, so the install step is explicitly skipped. Right: the lockfile moved, and the install is framed as a delta against the npm cache, not a fresh 1.5 GB tree. Build output goes to the existing Terminal; no second log surface.
4. Local changes, and how it ends
The decision before step 1, when the tree is dirty. Three ways out: save the changes as a patch (a local
.diff, nothing sent to Trac) and update clean; replay them on top of new trunk; or discard. Saving is the default — it's what the tool is for, and it's the only option that can't fail. On the right: success (naming the file it saved), and a failure mid-chain where trunk moved but the rebuild broke — worth its own state, because the code is new and the built assets are old.5. Conflicts after replaying your changes
Only reachable from "keep them on top of new trunk". The app names the conflicted files and offers two honest paths: undo the whole update (back to old trunk, changes intact), or resolve the markers yourself — in an editor or with an AI assistant — and hit rebuild. Undo stays available the entire time.
6. The same box when a patch won't apply
Applying a saved patch, or one from a ticket (#11), fails for the same reason — changes written against a different trunk — so it should be one component. One difference: a patch that fails to apply leaves the tree untouched, so there's nothing to undo and the primary becomes "Leave it as it is". And when the mismatch is just age (patch newer than your checkout), the app should say so and offer to update first, instead of sending someone off to resolve conflicts that don't really exist.
Rename the files to match, or drop the images in first and paste GitHub's generated URLs over the placeholder paths.
Align the update UI with the issue #94 mockups
juanmaguitar commentedon Aug 5, 2026
Measured network cost of the update chain
With the update path implemented (#111), we measured what "update to latest trunk" actually downloads, against the real repo. Method: create a site whose git store contains only an old trunk snapshot, run the app's update, and diff the git store size before and after.
Two corrections to the issue body's framing:
The git layer costs ~54 MB flat, not "kilobytes-to-MB". Because sites are depth-1 shallow clones, the server can't compute a delta against history the client doesn't have, so every update fetch is a constant-size snapshot pack — the same ~54 MB whether the site is a week or five weeks behind. It is not the multi-GB working tree (rebuilt locally from the pack) and not the full history. A future optimization could deepen the clone enough for real delta negotiation, but ~54 MB on event wifi is already workable.
The npm layer is where the issue's argument fully holds. The install step is skipped outright when package-lock.json didn't change between the two tips — 3 lockfile commits in the whole Jul 1 → Aug 4 window, so a typical few-day gap skips it. When it does run, the "1.5 GB" figure is node_modules on disk, not network: npm's cache plus the existing tree means only changed packages download (~12% of 2,604 packages across the window's one bulk devDependency update).
Net for "prepare at home, refresh at the event" (#12): a site created days before the event costs ~54 MB + a local rebuild on event day, versus a full fresh setup. The rebuild is CPU, not bandwidth.
🤖 Generated with Claude Code
Add an update path so sites can move to the latest trunk (#111)
Document the update-to-latest-trunk workflow in the README (#137)
Work a second ticket without rebuilding the world: the git layer (#108)…
Draw each site's server dot before its name in the sites list (#652)