Parent: #1936
Problem
scripts/check-migrations.mjs (wired into CI via npm run db:migrations:check) validates migration-number contiguity/duplicates by reading migrations/ off disk in the checked-out branch. CI checks out github.event.pull_request.head.sha — the contributor's own branch tip — not a fresh merge against the current tip of main. Two PRs can each independently add a new, non-overlapping migration file (e.g. 0090_a.sql and 0090_b.sql); each individually passes check-migrations.mjs (no duplicate visible in its own branch), each gets mergeable_state: clean from GitHub (two distinct file additions, no textual conflict), and the gate's own live pre-merge recheck (refreshLiveMergeState/refreshLiveCiAggregate in runAgentMaintenancePlanAndExecute, src/queue/processors.ts) only re-fetches GitHub's mergeable_state and CI-check status — neither is content-aware, so neither catches this.
This is not hypothetical: it has now happened 4 times in this repo's history (0015/0017, 0074, 0075/0076, and most recently 0090 — PR #2479 and PR #2527 each independently claimed migration number 0090, merged cleanly, and required a reactive one-off patch, PR #2532, to grandfather the duplicate). No systemic fix exists — only the reactive patch.
Requirements
- Add a live, content-aware recheck step to the gate's pre-merge pass (not just the CI/review-gate pass) for any PR touching
migrations/**: re-run migration-collision-detection logic against a FRESH merge of the PR's head with the current live tip of main, immediately before the actual merge call.
- If a live collision is detected (that wasn't visible at CI time because it originated from another PR merged since), downgrade the disposition to a HELD state with a clear "rebase needed — a sibling migration landed on main since your last CI run" comment, never merge blind.
- Config-driven, off-by-default per the house pattern (e.g.
gate.premergeContentRecheck), gated by the same autonomy/dry-run/kill-switch stack every other agent-driven mutation already uses.
- Must not add meaningful latency to the common case (no
migrations/** changes in the PR ⇒ skip entirely).
Deliverables
Acceptance criteria
- A PR that would introduce a migration-number collision with the LIVE tip of
main (not just its own branch snapshot) is held with a clear rebase-needed message, never merged.
- A PR with no
migrations/** changes sees zero behavior change and zero added latency.
- The existing
scripts/check-migrations.mjs CI check is unaffected — this is additive, not a replacement.
Expected outcome
The exact bug class that hit this repo 4 times (most recently #2479/#2527 → #90 collision, patched reactively via #2532) can no longer reach main silently — it is caught live, at the moment it would actually happen, instead of requiring a maintainer to notice db:migrations:check failing on main itself after the fact.
Parent: #1936
Problem
scripts/check-migrations.mjs(wired into CI vianpm run db:migrations:check) validates migration-number contiguity/duplicates by readingmigrations/off disk in the checked-out branch. CI checks outgithub.event.pull_request.head.sha— the contributor's own branch tip — not a fresh merge against the current tip ofmain. Two PRs can each independently add a new, non-overlapping migration file (e.g.0090_a.sqland0090_b.sql); each individually passescheck-migrations.mjs(no duplicate visible in its own branch), each getsmergeable_state: cleanfrom GitHub (two distinct file additions, no textual conflict), and the gate's own live pre-merge recheck (refreshLiveMergeState/refreshLiveCiAggregateinrunAgentMaintenancePlanAndExecute,src/queue/processors.ts) only re-fetches GitHub's mergeable_state and CI-check status — neither is content-aware, so neither catches this.This is not hypothetical: it has now happened 4 times in this repo's history (0015/0017, 0074, 0075/0076, and most recently 0090 — PR #2479 and PR #2527 each independently claimed migration number 0090, merged cleanly, and required a reactive one-off patch, PR #2532, to grandfather the duplicate). No systemic fix exists — only the reactive patch.
Requirements
migrations/**: re-run migration-collision-detection logic against a FRESH merge of the PR's head with the current live tip ofmain, immediately before the actual merge call.gate.premergeContentRecheck), gated by the same autonomy/dry-run/kill-switch stack every other agent-driven mutation already uses.migrations/**changes in the PR ⇒ skip entirely).Deliverables
scripts/check-migrations.mjs's duplicate-detection logic, invoked fromrunAgentMaintenancePlanAndExecuteimmediately before the merge action executes.gate.premergeContentRecheck(or equivalent) setting wired through the full config-as-code chain: migration, Drizzle schema,RepositorySettingstype, DB round-trip,.gittensory.ymlparsing, OpenAPI schema, docs.Acceptance criteria
main(not just its own branch snapshot) is held with a clear rebase-needed message, never merged.migrations/**changes sees zero behavior change and zero added latency.scripts/check-migrations.mjsCI check is unaffected — this is additive, not a replacement.Expected outcome
The exact bug class that hit this repo 4 times (most recently #2479/#2527 → #90 collision, patched reactively via #2532) can no longer reach
mainsilently — it is caught live, at the moment it would actually happen, instead of requiring a maintainer to noticedb:migrations:checkfailing onmainitself after the fact.