Original research
What survives thirty upstream releases
Measurements from one production site that customized a template heavily and then merged every release anyway. Every number here carries the command that reproduces it.
If you fork a template, customize it heavily, and then keep merging every upstream release for months — what breaks, and what survives?
It is asked constantly and answered almost entirely from intuition, because answering it properly means running a fork for months and writing down what happened. We did that without meaning to. These are the numbers, the method, and the parts that went badly.
Measurements verified 2026-08-02 against template v0.41.0. Re-run the commands below to reproduce them.
Results
The span these results cover. Not a single lucky upgrade — thirty-eight minor versions of continuous change.
git log --oneline | grep "Template v"Every template release published since this site was created has been merged into it. None was skipped, and none required abandoning the upgrade path.
git log --oneline --first-parent | grep -c "Merge remote-tracking branch 'template/main'"Files the template owns and content-hash locks. A site that edits one has left the upgrade path, and the checker says so by name.
node scripts/upgrade-check.mjsThe number that matters. This site is heavily customized and has not modified a single file the framework maintains — which is why thirty releases dropped in.
node scripts/upgrade-check.mjs # reports "0 edited, 0 missing"Site-owned files carrying the LANE marker: its own homepage, components, styling and pages. This is the customization the zero above survived.
grep -rl "@genesis-managed: LANE" app components lib | wc -lThings this site asserts a merge must not silently take away. Checked on every upgrade, because the dangerous merge is the CLEAN one that quietly takes the template's side.
node scripts/check-upgrade-survivals.mjs .All of them, through the most recent upgrade. A number below 19 would have blocked the release.
node scripts/check-upgrade-survivals.mjs .Method
The subject
One production marketing site (this one), forked from a shared template and customized to the point that it shares almost no visible design with its base — its own homepage, its own navigation, its own typography, fifty files of bespoke work.
The treatment
Every upstream release merged as it was published, without exception and without cherry-picking. Thirty releases spanning thirty-eight minor versions. No release was skipped because it looked risky.
The measurements
A content-hash lock over every framework-owned file reports how many were edited. A declaration manifest lists customizations that must survive a merge, and is checked on every upgrade. Both run as gates, so the numbers are produced by the same tooling that blocks a bad release rather than by somebody counting afterwards.
What this cannot tell you
n = 1. One site, one team, one codebase, one template. It shows that the outcome is achievable and what it costs; it cannot tell you the average outcome across many teams, and nothing here should be read as a rate.
Findings
Heavy customization and clean upgrades are not a trade-off — if the boundary is enforced by a machine
The common assumption is that you customize a template and then stop upgrading it. Fifty files of custom work and thirty clean merges says otherwise. The load-bearing part is not discipline, it is the content-hash lock: a human intending to stay in-lane still drifts, and a checker that names the drifted file the same day does not.
The dangerous merge is the one with no conflict
Conflicts are loud and get resolved. The regression that actually cost us was a CLEAN merge: git decided the template's copy of a file was simply newer, took it, and replaced this site's thirteen service cards with the template's generic six. No conflict, no error, green build, green gates. A human noticed on the live site. That is the failure mode worth designing against, and it is the one nobody warns you about.
Declared expectations catch what diffs cannot
The response to the above was to let a site declare, in a manifest, what a merge must not take away. Nineteen declarations, all still holding. This works because it inverts the question: instead of asking "what changed?" — which a clean merge answers with "nothing important" — it asks "is what I depend on still true?"
A release can break its destination while passing every check at the source
One release generated an upgrade-summary document that failed the release's own documentation gate in every clone that received it. It passed in the workshop, where the paths it referenced still existed. Anything a release GENERATES is subject to the gates that release ships, and the workshop is the one environment where a clone-shaped problem cannot occur.
Most upgrade failures are not the upgrade
Rolling one release to three sites produced four alarming failures. None was the release. A stale build cache failed typecheck naming a file the owner never wrote; a stale local clone was reported as a site months out of date; one stray untracked file aborted a fast-forward with no explanation. The tooling kept surfacing facts about a working copy and phrasing them as facts about the site.
What it cost
A results section with no failures is not a results section. These are the parts that argue against us.
- Every framework improvement has to be reachable through configuration, or sites edit the file and leave the path. That constraint shaped the whole design and it is not free.
- Custom typography and corner radius were reachable only by editing a locked file for most of this period, so a site had to choose between looking distinctive and staying upgradeable. Most chose generic. That is a real cost we paid in the appearance of every clone until it was fixed.
- The declaration manifest is manual. Nobody is reminded to write one, and a customization nobody declared is a customization nothing protects.
- This required tooling that took months to build. The result is reproducible; the path to it was not cheap.
Reuse and citation
These measurements are free to cite, quote and reproduce, with or without attribution. If you re-run the commands and get different numbers, the different numbers are the correct ones — tell us and we will publish the correction rather than the flattering version.
The site these numbers describe
You are on it. The case study covers a second site built the same way from a screenshot and a prompt, and the build page shows the boundary the whole result depends on.