docs(bugs): record #258 — migrate has no primary-key change kind - #261
Merged
Conversation
Adopting an existing Postgres database whose primary key differs from the metadata, the apply fails at the foreign keys with "there is no unique constraint matching given keys for referenced table". The table model reads a PK (TableDescriptor.primaryKey) but the Change union has no add-primary-key or drop-primary-key, so a key move degrades into an unrelated add-column plus drop-column and the constraint is simply lost. Only observable when adopting an existing database — a greenfield create-table carries its PK inline, which is why it had not surfaced before. Written up alongside the existing docs/bugs entries: root cause with the STAGE_ORDER excerpt, a suggested staging (drop-fk < drop-primary-key, add-column < add-primary-key < add-fk), the note that the SQLite emitter needs the same kinds, a synthetic two-table reproduction, and the alternative of detecting and refusing a PK change rather than emitting SQL that cannot apply. Found immediately behind #255, which 0.20.10 fixed — the apply now clears the column drops and fails at the FK stage instead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W6J7r8cEzA7ApfJD3CZt2L
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Commit a bug write-up produced this session, as part of getting to a clean slate. While adopting an existing Postgres database into the MetaObjects ledger for a downstream project, two defects in the TypeScript migrate engine were found and filed as GitHub issues. This commit records the second as docs/bugs/2026-08-02-no-primary-key-change-kind.md, matching the existing docs/bugs convention: issue-template frontmatter, kept afterwards as the written record, annotated with the filed issue link (#258).
The defect: meta migrate has no primary-key change kind. TableDescriptor.primaryKey exists so the engine can READ a table's PK, but the Change union has no add-primary-key or drop-primary-key, so when the metadata's identity disagrees with a live database's PRIMARY KEY the diff degrades into an unrelated add-column plus drop-column and the constraint is silently lost. The apply then fails at the foreign keys with 'there is no unique constraint matching given keys for referenced table'. Only observable when adopting an existing database via baseline --from-db, because a greenfield create-table carries its PK inline.
The document has the root cause with the STAGE_ORDER excerpt, suggested staging invariants (drop-fk < drop-primary-key, add-column < add-primary-key < add-fk), a note that src/emit/sqlite.ts needs the same kinds, a synthetic two-table reproduction, and an explicit alternative for the maintainer: detect and refuse a PK change rather than emit SQL that cannot apply. Found immediately behind issue #255, fixed in 0.20.10 this same session.
IMPORTANT — two prior attempts and their resolutions, so they are not re-litigated:
This is documentation-only: it touches no source, no metadata, no generated code.
What Changed
docs/bugs/2026-08-02-no-primary-key-change-kind.md, recording migrate-engine defect migrate: no primary-key change kind, so moving a table's PK leaves it with none and every referencing FK fails #258: theChangeunion exposes no primary-key change kind, so when the metadata'sidentitydisagrees with a live database'sPRIMARY KEYthe diff degrades into an unrelated add-column + drop-column and the constraint is silently lost (apply then fails at foreign keys withthere is no unique constraint matching given keys for referenced table). Only observable when adopting an existing DB viabaseline --from-db, since greenfieldCREATE TABLEcarries its PK inline.docs/bugs/convention — issue-template frontmatter, retained as the permanent record, annotated with the filed issue link (migrate: no primary-key change kind, so moving a table's PK leaves it with none and every referencing FK fails #258) — and includes the root-causeSTAGE_ORDERexcerpt, suggested staging invariants, a note that the SQLite emitter needs the same kinds, a synthetic two-table reproduction, and a detect-and-refuse alternative for the maintainer.Risk Assessment
✅ Low: Documentation-only addition of one bug write-up; all source citations verified accurate against migrate-ts (types.ts:35, the Change union, STAGE_ORDER's 18 keys, and the absence of any primaryKey comparison in diff/), it matches the existing docs/bugs convention, and the only potential public-hygiene concern (concrete table names) is explicitly owner-approved per the binding intent.
Testing
Validated the bug-#258 doc commit end-to-end: it is strictly documentation-only (one .md file, +126 lines, zero source/metadata/generated-code changes), follows the existing docs/bugs convention with valid issue-template frontmatter, and is annotated with the #258 issue link; crucially, every code claim the bug write-up makes (TableDescriptor.primaryKey at types.ts:35, the exhaustive STAGE_ORDER in postgres.ts, the absence of any add/drop/change-primary-key kind across both emitters, and sqlite.ts needing the same) was verified accurate against the live migrate-ts source. No defects found.
Evidence: Bug-#258 doc verification transcript
Doc-only commit (+126 lines, single .md); frontmatter parses as valid YAML matching the docs/bugs convention; annotated with issues/258 link. Code claims verified: TableDescriptor.primaryKey at types.ts:35 (exact), STAGE_ORDER in postgres.ts is an exhaustive Record<Change["kind"],number> whose 19 keys match the doc with no PK kind, grep confirms zero add/drop/change-primary-key kinds anywhere in migrate-ts, and sqlite.ts exists and also lacks PK kinds. Conclusion: intent satisfied, all claims accurate.Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
✅ **Review** - passed
✅ No issues found.
✅ **Test** - passed
✅ No issues found.
git show --stat 80015852 — confirms commit changes exactly one .md file (+126)git diff --stat c2dce37e 80015852 — confirms the branch-vs-base delta is doc-only (no source/metadata/generated code)compared new doc frontmatter + section structure against existing docs/bugs/2026-06-28-projection-aggregate-view-ddl.mdpython3 yaml.safe_load of the doc frontmatter — parses cleanly with issue-template shapegrep '^```' count (14, balanced) and '#258'/'issues/258' link presencesed/grep src/types.ts:35 — verifies TableDescriptor.primaryKey: string[] claimgrep + awk STAGE_ORDER block in src/emit/postgres.ts — verifies exhaustive Record<Change["kind"],number> and that its 19 keys match the doc, with no primary-key kindgrep -rn 'add-primary-key|drop-primary-key|change-primary-key|addPrimaryKey|dropPrimaryKey' over migrate-ts/src — none found (confirms the documented gap)grep Change union kinds in src/types.ts — confirms no PK change kind in the Change typels + grep src/emit/sqlite.ts — confirms the SQLite emitter exists and also has no PK kinds (consistent with the doc's note)✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.