Skip to content

fix(migrations): restore 0180's bytes and freeze released migrations against post-release edits (#9420) - #9776

Merged
JSONbored merged 2 commits into
mainfrom
fix/migration-content-drift-0180
Jul 29, 2026
Merged

fix(migrations): restore 0180's bytes and freeze released migrations against post-release edits (#9420)#9776
JSONbored merged 2 commits into
mainfrom
fix/migration-content-drift-0180

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

The bug

#9420 updated a doc comment inside migrations/0180_decision_ledger.sql. No DDL changed. It merged ~36 minutes before the orb-v3.5.0 stable tag and shipped in it.

runSelfHostMigrations (src/selfhost/migrate.ts) records a sha256 of each applied migration's full text and re-hashes it on every boot, throwing selfhost_migration_content_drift on a mismatch. Comments are part of that text.

So any deployment that had already applied 0180 would refuse to boot after upgrading — not degrade, not warn: fail to start, with the review pipeline down until a human restored the file by hand.

How it was caught

Cutting the next ORB beta. Before building, I compared the live server's _selfhost_migrations ledger against the repo:

server stored (beta.10):  0a95840ffb370a9c5e88357c9353bef525580b604d5e6676076535c2a5403528
orb-v3.5.0-beta.10:       0a95840ffb370a9c5e88357c9353bef525580b604d5e6676076535c2a5403528
main / orb-v3.5.0:        e1a90fb3703a03bf99069040faad543b503db5c3bf0caaf5a8e40d8783086de9

A sweep of all 199 ledger rows confirmed 0180 is the only drift. The stable orb-v3.5.0 image already carries it, so this is not hypothetical for anyone upgrading from <= beta.10.

The fix

Restore 0180 verbatim. Nothing is lost — #9420 wrote the same prose into src/review/decision-record.ts's header, which is where documentation about this table belongs and where it can change freely. That header now states the split and why the .sql must never be edited.

Add db:migrations:immutable:check. Once a migration appears in a released orb-v* tag its bytes are frozen — DDL, typos, comments, all of it. Deletion is refused too.

Two design points worth reviewing:

  • Baselined at each file's FIRST release, not every tag. When a released migration has been mutated, the released tags contradict each other (beta.5 has the original, 3.5.0 has the edit), so no content could satisfy all of them and the check could never go green again — not even after the correct repair.
  • The 35 files edited before this rule existed are re-frozen at current content, not exempted. Exempting would leave 35 permanent holes in a guard whose whole value is having none. Verified none of them drifts against the live fleet: every one was edited before the deployments that applied it, so no running ORB recorded the older hash. 0180 is deliberately not in that table — it was repaired properly, so it stays under the normal rule.

The checker reads the working tree, not ls-tree HEAD, so it can't go green locally on the exact change it exists to reject.

Verification

Guard mutation-tested in all four directions: passes with the fix; fails naming 0180 when #9420's edit is reintroduced; fails naming 0193 when any other released migration is touched; passes again when reverted. 10 unit tests including a regression for the real failure, the deletion case, and an invariant that the rebaseline table cannot grow.

tsc, db:migrations:check, db:migrations:immutable:check, dead-source-files:check, import-specifiers:check, validate:no-hand-written-js all green.

This should land before the next ORB image is cut.

…9420)

#9420 updated a doc COMMENT inside migrations/0180_decision_ledger.sql. No DDL
changed. It shipped in orb-v3.5.0.

runSelfHostMigrations records a sha256 of each applied migration's FULL text and
re-hashes on every boot, throwing selfhost_migration_content_drift on a mismatch.
Comments are part of that text. So every deployment that had already applied 0180
would REFUSE TO BOOT on upgrade -- not degrade, fail to start, review pipeline
down until a human restored the file.

Confirmed against the live ORB: its _selfhost_migrations row for 0180 stores
0a95840f..., byte-identical to orb-v3.5.0-beta.10 and different from main. A full
sweep of all 199 ledger rows found 0180 to be the only such drift.

Restore 0180 verbatim. Nothing is lost: #9420 wrote the same prose into
src/review/decision-record.ts's header, which is where it belongs and where it can
change freely -- that header now says so, and says why the .sql must not.

Add db:migrations:immutable:check: once a migration exists in a released orb-v tag
its bytes are frozen, baselined at its FIRST release so a later tag that disagrees
(as 3.5.0 does) cannot move the target and block the repair. The 35 files edited
before this rule existed are re-frozen at current content rather than exempted, so
the guard has no permanent holes.
@loopover-orb

loopover-orb Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Important

🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨

⏳ LoopOver is waiting…

LoopOver has seen this pull request and is waiting on CI checks to finish before reviewing it. This comment will update once the review runs.

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed · 🟨 Waiting

@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 29, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
loopover-ui c5135b2 Commit Preview URL

Branch Preview URL
Jul 29 2026, 08:10 AM

@JSONbored
JSONbored merged commit bf654cf into main Jul 29, 2026
5 checks passed
@JSONbored
JSONbored deleted the fix/migration-content-drift-0180 branch July 29, 2026 08:12
@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Bundle Report

Bundle size has no change ✅

@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.30%. Comparing base (983ea31) to head (c5135b2).
⚠️ Report is 6 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #9776   +/-   ##
=======================================
  Coverage   90.30%   90.30%           
=======================================
  Files         912      912           
  Lines      113547   113547           
  Branches    26951    26951           
=======================================
  Hits       102536   102536           
  Misses       9682     9682           
  Partials     1329     1329           
Flag Coverage Δ
backend 95.56% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/review/decision-record.ts 100.00% <ø> (ø)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant