Skip to content

fix: refuse shared projects before minting identity - #1748

Merged
bcdonadio merged 1 commit into
mainfrom
fix/1689-s22
Oct 25, 2026
Merged

bcdonadio merged 1 commit into
mainfrom
fix/1689-s22

Conversation

@bcdonadio

Copy link
Copy Markdown
Contributor

Why

Fixes #1689. On a second machine whose project-map entry already carries a remoteProjectId (a project shared across machines), lcm migration apply --confirm-generation <id> correctly refused with multi-machine-installation, but only after the fresh identity step had run. That step can persist the local map entry and resolve or create the PostgreSQL destination project, so the refusal reported applied: true and could leave a destination identity behind. The shared scope depends only on the existing local map entry, so the refusal can be decided before any write.

How

  • freshCutoverUnderBarrier now runs a read-only scope check right after the confirmed-candidate recheck and before the identity step. A shared entry refuses multi-machine-installation before the identity mint, the maintenance hold and capture, so the envelope reports applied: false and nothing is written.
  • readSqliteMigrationSourceScope (in maintenance.ts) reads one project-map snapshot under the retained publication token. It shares the entry-to-scope derivation with authenticateSqliteMigrationSource, which still reads the map itself after the mint so it sees the minted entry.
  • The post-mint scope check stays as a backstop. Held/recover paths, cli-preflight.ts and cli-apply.ts are unchanged. The cutover-set path already refused shared members before its identity step.

Testing

  • test/migration/apply.test.ts: a shared entry refuses before the identity resolver runs (resolver and destination resolution never called, project-map bytes unchanged, no maintenance journal). This fails at the base commit. Controls: a local project still reaches the identity step, and a project bound during the identity step is still refused by the retained post-mint check.
  • test/postgresql/cutover-rehearsal/scenarios.json: the H2 b-apply row now expects applied: false and its note describes the new order. The checked-in rehearsal gate record (docs/migration-rehearsal-gate.*) is a sealed record of an earlier tested commit and is not regenerated here.
  • Focused run of 11 related migration unit files: 436 passed; apply.ts and maintenance.ts at 100% statements, branches, functions and lines. pnpm run typecheck and eslint pass.

Reviewer guide

Start at freshCutoverUnderBarrier in src/migration/apply.ts (the new check before resolveIdentityUnderBarrier), then readSqliteMigrationSourceScope and sqliteMigrationSourceProjectIdentity in src/migration/maintenance.ts. Docs: the multi-machine-installation remedy in docs/migration-operations.md. Metadata: patch changeset and test/codecov-ownership/1689-shared-history-refusal-order.yml.

A fresh `lcm migration apply` ran the identity step (local project-map
entry plus PostgreSQL destination project) before checking whether the
project was already shared, so a second machine bound to a shared project
could be refused with multi-machine-installation after a destination
project had been created, and the refusal reported applied: true.

The scope depends only on whether the local project-map entry carries a
remoteProjectId, which the mint never writes. Apply now reads the map
under the retained barrier token, right after the confirmed-candidate
recheck, and refuses before the identity step, the hold or capture. The
read is a snapshot with no backfill or write. The entry-to-scope
derivation is shared with authenticateSqliteMigrationSource, which still
reads the map itself after the mint so it sees the minted entry. The
post-mint scope check stays as defence in depth.

The H2 rehearsal row now expects applied: false. The recorded gate files
(docs/migration-rehearsal-gate.*) are left untouched: they describe the
tested commit and are regenerated by a separate full rehearsal run.

Signed-off-by: Bernardo Donadio <bcdonadio@bcdonadio.com>
@bcdonadio bcdonadio self-assigned this Oct 25, 2026
@bcdonadio
bcdonadio enabled auto-merge October 25, 2026 04:10
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-25T04:11:18.227873Z 8d2619f PR opened
🔒 Security Review 🔄 Running since 2026-10-25T04:10:30.758003Z 8d2619f PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@bcdonadio
bcdonadio merged commit b62c9f9 into main Oct 25, 2026
34 of 36 checks passed
@bcdonadio
bcdonadio deleted the fix/1689-s22 branch October 25, 2026 04:19
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.

Apply writes the destination identity before the shared-history refusal

1 participant