fix(compat): whitespace-insensitive schema lineage (#52) - #55
Merged
Conversation
… normalization Issue #52: databases migrated V1→V13 by published releases were rejected as unsupported_lineage because normalizeSQL()'s sensitivity to whitespace layout made ALTER TABLE ADD COLUMN output (inline columns) incompatible with hand-formatted fixtures. Changes: 1. Replace normalizeSQL's naive TrimSpace with full whitespace collapse outside SQL string literals. Preserves whitespace inside single-quoted literals and handles '' escape sequences correctly. This eliminates cosmetic layout differences as a source of signature mismatch. 2. Regenerate all 76 manifest signatures under the new normalization via RegenerateManifestJSON(). The regeneration is reproducible: load fixtures, compute signatures under current normalizeSQL, and write back manifest.json. See TestRegenerateManifestOnNormalizationChange with REGEN_MANIFEST=1. 3. Correct v13-legacy-alter-built.sql: replace hand-wrapped multi-line DDL with inline columns as ALTER TABLE ADD COLUMN actually produces. Fixtures for migrated lineages must be dumped from real migration runs, never edited. 4. Add regression test framework: TDD tests for normalizeSQL() behavior, plus TestInspectIndexRecognizesObservedDevelopmentV13Shape updated to reflect new post-normalization signature. This fix prevents false rejections when published-release-migrated databases encounter the catalog. The root cause (whitespace-sensitive identity) is eliminated; cosmetic DDL formatting will no longer silently fall a schema out of the catalog. Closes #52 Claude-Session: https://claude.ai/code/session_019LDXzStaKrArqJKvy4z3eF
…truth The hardcoded switch in internal/storage/recovery_records.go(36-64) contained 19 signature literals that were NOT regenerated after normalizeSQL became whitespace-insensitive, causing it to reject all known signatures with unsupported_lineage instead of proceeding to semantic diagnostics. Replace the hardcoded switch with a call to the canonical source: ask compat.Catalog.IsKnownSignature() instead of carrying a second, stale catalog in Go source. This is both a fix and a simplification: recovery_records.go no longer owns or maintains a separate lineage enumeration. Add IsKnownSignature() method to Catalog as the narrow interface recovery needs. Verify collision consistency in a new test: all fixtures that collapse to the same whitespace-normalized signature must agree on AppliedVersion and HasSourceMetadata, or recovery could plan wrong migration steps. The test passes, confirming the manifest is safe. Fix issue #52 where recovery diagnostics were masked by false positives. Claude-Session: https://claude.ai/code/session_019LDXzStaKrArqJKvy4z3eF
Record the design decision to centralize all lineage catalog logic in internal/compat and eliminate the hardcoded switch in recovery_records.go. Note the collision consistency guarantee and whitespace normalization drift prevention. Claude-Session: https://claude.ai/code/session_019LDXzStaKrArqJKvy4z3eF
Record the design decision for normalizeSQL() to collapse whitespace outside string literals, eliminating cosmetic DDL formatting as a source of signature mismatch. Note the regeneration of all 76 signatures and the false-rejection prevention for published-release-migrated databases. Claude-Session: https://claude.ai/code/session_019LDXzStaKrArqJKvy4z3eF
pablontiv
force-pushed
the
fix/issue-52-schema-lineage
branch
from
August 22, 2026 17:40
c78c089 to
123977e
Compare
…atalog Issue #52: databases migrated V1→V13 by published releases were rejected because normalizeSQL() was whitespace-sensitive, making ALTER TABLE ADD COLUMN output (inline) incompatible with hand-formatted fixtures. This regression test migrates a V1 fixture forward through SetupSchema() and asserts the resulting signature is recognized by the catalog. It would have caught the false rejection during development and prevents future cosmetic DDL changes from silently breaking lineage recognition. Relates to #52 fix. Claude-Session: https://claude.ai/code/session_019LDXzStaKrArqJKvy4z3eF
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.
Root Cause (First Issue — Corrected by d46cdf6)
Issue #52 reported that healthy databases migrated V1→V13 by published releases were rejected as
unsupported_lineage. The root cause:normalizeSQL()was naivelyTrimSpace(), making whitespace layout in stored DDL part of the schema signature. Altered tables with inline columns (asALTER TABLE ADD COLUMNleaves them) differed in signature from hand-formatted fixtures.Root Cause (Second Issue — This PR, Fix #52)
Even AFTER the normalizeSQL fix, CI tests still failed: runtime computed the NEW correct signature, but
internal/storage/recovery_records.gocontained a hardcoded switch with 19 OLD signature literals. The file was a second, stale lineage catalog that silently rejected all signatures withunsupported_lineagebefore semantic diagnostics could run.The Actual Problem
Two independent catalogs:
internal/compat/manifest.json(data, regenerable) — the single source of truthinternal/storage/recovery_records.go:36-64(hardcoded Go source literals) — stale copyNothing forced them to agree. When
normalizeSQL()changed, compat's catalog was regenerated, but the hardcoded switch was NOT. The switch rejected every known signature, masking the real diagnostics (recovery_conflict, uninterpretable_row).Solution
Replace the hardcoded switch with a call to the canonical source:
compat.Catalog.IsKnownSignature(signature)checks if the schema is recognizedinternal/storage/recovery_records.goqueries compat instead of owning a second catalogCollision Safety: When whitespace normalization collapsed 76 fixtures into 17 distinct signatures, a new test verifies all entries with the same signature agree on
AppliedVersionandHasSourceMetadata. Any mismatch would be a real bug (recovery could plan wrong steps). The test passes, confirming the manifest is safe.Verification
Local CI gate (build, scrubbed-HOME tests, aggregate coverage ≥85%):
Tests passing:
TestReadRecoveryInputSupportsCatalogReadableSignatures— all 20 catalog fixtures readableTestCollisionConsistencyGuardsAgainstSignatureAmbiguity— collision groups consistentTestValidateTextReportsSemanticRecoveryDiagnosticsReadOnly— correct semantic codesTestBlockingDiagnosticsHaveExecutableContinuations— all diagnostic paths unblockedCommits