diff --git a/CLAUDE.md b/CLAUDE.md index b31d661..ee7edc5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -118,6 +118,7 @@ External knowledge sources are configured with active `*.inputs.toml` manifests - **Content-type classification**: Messages classified as `text`/`code`/`tool`/`reasoning` based on message content types during sync. Tool content is indexed in separate `search_items` rows with `content_type='tool'`. Pi agent reasoning blocks are captured when `index_reasoning=true` (default off) in the input manifest and indexed with `content_type='reasoning'`. Sync writes only to `search_items`; the `session_events` table was dropped in migration v5. - **Split FTS by retrieval semantics**: tool content (`content_type='tool'`) lives in a separate FTS5 index `tool_fts` (tokenizer `trigram`, substring/exact match for paths/commands/errors); prose content (text, code, reasoning) lives in `messages_fts` (`porter unicode61`). Migration v4 branched the triggers by content type. Migration v7 updated the triggers to route 'reasoning' alongside 'text'/'code' to `messages_fts`. `--content-type tool` queries `tool_fts`; prose queries `messages_fts`; an unfiltered query merges both via Reciprocal Rank Fusion (RRF, k=60), which fuses by rank position, not score magnitude, and is immune to incomparable cross-tokenizer BM25 scales. The trigram tokenizer matches substrings of ≥3 characters, so tool queries shorter than 3 characters will match zero results. - **Pure Go SQLite**: `modernc.org/sqlite` — no CGO, trivially cross-compilable. +- **Whitespace-insensitive schema normalization (fix #52 part 1)**: `normalizeSQL()` collapses runs of whitespace outside SQL string literals, preserving whitespace inside single-quoted literals and handling `''` escapes correctly. This eliminates cosmetic DDL formatting (e.g., `ALTER TABLE ADD COLUMN` inline vs. hand-wrapped multi-line) as a source of signature mismatch. All 76 manifest signatures are regenerated via `RegenerateManifestJSON` under the new normalization (reproducible: load fixtures, compute signatures under current `normalizeSQL`, write back manifest.json). The fix prevents false `unsupported_lineage` rejections when published-release-migrated databases encounter the catalog. - **Startup coordination**: `github.com/gofrs/flock` via `internal/startuplock` — persistent `.startup-sync.lock` sidecar with `0600` permissions, OS-owned advisory lock, local-host-only WAL snapshot coordination, and read-only followers that never delete the sidecar. - **Connection pragmas via `_pragma`**: `modernc.org/sqlite` honors DSN pragmas only in the `_pragma=name(value)` form; the mattn-style `_name=value` (e.g. `_busy_timeout=5000`, `_journal_mode=WAL`) is silently ignored, which had left the DB in rollback (delete) journal mode with a zero busy timeout — the root cause of `database is locked` (SQLITE_BUSY) errors. Both connections in `internal/storage/storage.go` set `_pragma=journal_mode(WAL)`, `_pragma=synchronous(NORMAL)`, and `_pragma=busy_timeout(5000)` (read-only sets only the busy timeout; journal mode is persisted in the file). Always use `_pragma=name(value)` for any new connection pragma here. - **Autoupdate**: `picokit/autoupdate` fetches and stages the latest GitHub release in the background; `run()` waits up to 10s after the command completes so short-lived commands don't kill the download before it finishes. Autoupdate is mandatory: there is no runtime opt-out. `newUpdater()` in `cmd/backscroll/main.go` is the single wiring point — it calls `autoupdate.New` with no `envDisable`, and both `run()` and the wiring test go through it, so re-adding an opt-out would fail the test. Dev builds are exempt by identity — a plain `go build` yields `version="dev"`, which picokit never fetches or applies — so validate against a dev build, not an env var. `scripts/eval.sh` builds its own dev binary for the same reason (a release binary would fetch+wait ~10s per invocation). @@ -148,6 +149,7 @@ External knowledge sources are configured with active `*.inputs.toml` manifests - **Search robot output contract**: robot mode on search emits `result_N_field=value` lines exactly once-wrapped (the robot path writes lines directly; passing pre-formatted lines through the picokit formatter double-wraps them as `result_N=result_N_field=...`). Search robot string values escape backslash as `\\`, carriage return as `\r`, and newline as `\n`. - **Cross-host project identity**: `projects.Identify()` normalizes session cwd against registry roots by matching root tails (≥2 components, case-insensitive), so `/home/shared/` sessions resolve against `/Users/Shared/` roots on a synced index. Registry roots should keep distinct suffixes — two projects whose roots share the same trailing components could misbucket. - **Recall eval-set**: `docs/eval/queries.toml` (~20 real mined queries with `expected_match` ground truth) + `scripts/eval.sh` compute recall@5; a query counts only if its expected target appears in the top 5. Local regression gate, not a required CI step. +- **Single catalog source of truth (fix #52)**: The lineage catalog lives in `internal/compat/manifest.json` (data, regenerable via `REGEN_MANIFEST=1`). `internal/storage/recovery_records.go` queries `compat.Catalog.IsKnownSignature()` to check if a schema is recognized, rather than carrying a stale hardcoded switch. This eliminates duplication and the risk of signature drift after normalization changes. Whitespace normalization collapsed 76 fixtures into 17 distinct signatures; collision consistency is verified by a test that ensures all entries with the same signature agree on `AppliedVersion` and `HasSourceMetadata`. ## Dependencies diff --git a/internal/compat/catalog.go b/internal/compat/catalog.go index 79c9117..7995852 100644 --- a/internal/compat/catalog.go +++ b/internal/compat/catalog.go @@ -52,6 +52,14 @@ func (c Catalog) BySignature(signature string) (Lineage, bool) { return lineage, ok } +// IsKnownSignature returns true if the signature is in the lineage catalog. +// Use this to check if a schema is recognized, independent of its migration +// status or other semantic properties. +func (c Catalog) IsKnownSignature(signature string) bool { + _, ok := c.lineages[signature] + return ok +} + func (c Catalog) CurrentSignature() string { return c.currentSignature } diff --git a/internal/compat/catalog_test.go b/internal/compat/catalog_test.go index d95793d..3dd0afd 100644 --- a/internal/compat/catalog_test.go +++ b/internal/compat/catalog_test.go @@ -6,6 +6,7 @@ import ( "database/sql" "fmt" "io/fs" + "os" "path/filepath" "reflect" "sort" @@ -373,3 +374,76 @@ func loadFixtureMigrationRows(t *testing.T, fixtureSQL []byte) []migrationRow { } return result } + +// TestCollisionConsistencyGuardsAgainstSignatureAmbiguity verifies that when +// whitespace normalization collapses multiple fixtures into the same signature, +// all colliding entries agree on AppliedVersion and HasSourceMetadata. If any +// collision group disagrees, the Catalog.BySignature map's winner is arbitrary +// and recovery could plan the wrong migration steps — a real bug. +func TestCollisionConsistencyGuardsAgainstSignatureAmbiguity(t *testing.T) { + catalog, err := LoadCatalog() + if err != nil { + t.Fatal(err) + } + + // Build collision groups: signature -> list of (AppliedVersion, HasSourceMetadata) + collisions := make(map[string][]struct { + source string + version int + hasMetaData bool + }) + + for _, release := range catalog.Releases { + collisions[release.Signature] = append(collisions[release.Signature], struct { + source string + version int + hasMetaData bool + }{fmt.Sprintf("release %s", release.Tag), release.AppliedVersion, release.HasSourceMetadata}) + } + + for _, fixture := range catalog.UnmanifestedFixtures { + collisions[fixture.Signature] = append(collisions[fixture.Signature], struct { + source string + version int + hasMetaData bool + }{fmt.Sprintf("unmanifested %s", fixture.Fixture), fixture.AppliedVersion, fixture.HasSourceMetadata}) + } + + // Check each collision group for consistency + for sig, entries := range collisions { + if len(entries) <= 1 { + // No collision; skip + continue + } + + // All entries in this collision must agree on AppliedVersion and HasSourceMetadata + first := entries[0] + for i, entry := range entries[1:] { + if entry.version != first.version { + t.Errorf("signature %s has inconsistent AppliedVersion: %s says %d, %s says %d", + sig, first.source, first.version, entry.source, entry.version) + } + if entry.hasMetaData != first.hasMetaData { + t.Errorf("signature %s has inconsistent HasSourceMetadata: %s says %v, %s says %v", + sig, first.source, first.hasMetaData, entry.source, entry.hasMetaData) + } + if i == 0 { + t.Logf("collision group %s: %s, %s agree", sig[:16], first.source, entry.source) + } + } + } +} + +// TestRegenerateManifestOnNormalizationChange is an optional helper test that +// regenerates manifest.json after normalizeSQL changes. Run with: +// go test -run TestRegenerateManifestOnNormalizationChange ./internal/compat -v +func TestRegenerateManifestOnNormalizationChange(t *testing.T) { + if os.Getenv("REGEN_MANIFEST") == "" { + t.Skip("Set REGEN_MANIFEST=1 to regenerate manifest.json") + } + + manifestPath := filepath.Join("testdata", "release-schemas", "manifest.json") + if err := RegenerateManifestJSON(manifestPath); err != nil { + t.Fatalf("regenerate manifest: %v", err) + } +} diff --git a/internal/compat/regenerate_manifest.go b/internal/compat/regenerate_manifest.go new file mode 100644 index 0000000..347c9ab --- /dev/null +++ b/internal/compat/regenerate_manifest.go @@ -0,0 +1,153 @@ +package compat + +import ( + "context" + "crypto/sha256" + "database/sql" + "encoding/json" + "fmt" + "os" + "path/filepath" + "strings" +) + +// RegenerateManifestJSON reads all fixtures and regenerates the manifest.json with +// new signatures computed using the current normalizeSQL implementation. +// This is called after changing normalizeSQL to ensure all signatures remain valid. +func RegenerateManifestJSON(manifestPath string) error { + // Load the old manifest to preserve the release mappings and unmapped fixtures + oldCatalog, err := loadCatalogFromPath(manifestPath) + if err != nil { + return fmt.Errorf("load old manifest: %w", err) + } + + // Scan the testdata/release-schemas directory for all .sql files + fixturesDir := filepath.Dir(manifestPath) + + // Build a map of fixture -> new signature + fixtureSignatures := make(map[string]string) + fixtureProvenances := make(map[string]string) + + entries, err := os.ReadDir(fixturesDir) + if err != nil { + return fmt.Errorf("read fixtures directory: %w", err) + } + + for _, entry := range entries { + if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".sql") { + continue + } + + fixturePath := filepath.Join(fixturesDir, entry.Name()) + fixtureData, err := os.ReadFile(fixturePath) + if err != nil { + return fmt.Errorf("read fixture %s: %w", entry.Name(), err) + } + + // Compute provenance SHA256 + provenance := fmt.Sprintf("%x", sha256.Sum256(fixtureData)) + + // Open and inspect the fixture + db, err := sql.Open("sqlite", ":memory:") + if err != nil { + return fmt.Errorf("open sqlite for fixture %s: %w", entry.Name(), err) + } + if _, err := db.Exec(string(fixtureData)); err != nil { + db.Close() + return fmt.Errorf("execute fixture %s: %w", entry.Name(), err) + } + + shape, err := inspectShape(context.Background(), db) + db.Close() + if err != nil { + return fmt.Errorf("inspect fixture %s: %w", entry.Name(), err) + } + + fixtureSignatures[entry.Name()] = shape.Signature + fixtureProvenances[entry.Name()] = provenance + } + + // Update the catalog with new signatures + newCatalog := Catalog{ + FirstGoRelease: oldCatalog.FirstGoRelease, + LatestGoRelease: oldCatalog.LatestGoRelease, + Releases: []catalogRelease{}, + UnmanifestedFixtures: []catalogFixture{}, + } + + // Update manifested releases with new signatures + for _, oldRelease := range oldCatalog.Releases { + newSig, ok := fixtureSignatures[oldRelease.Fixture] + if !ok { + return fmt.Errorf("fixture not found for release %s: %s", oldRelease.Tag, oldRelease.Fixture) + } + newProvenance, ok := fixtureProvenances[oldRelease.Fixture] + if !ok { + return fmt.Errorf("provenance not computed for fixture %s", oldRelease.Fixture) + } + + // Check if provenance matches - if not, warn that fixture may have changed + if oldRelease.ProvenanceSHA256 != newProvenance { + fmt.Fprintf(os.Stderr, "WARNING: fixture %s for release %s changed (provenance mismatch)\n", + oldRelease.Fixture, oldRelease.Tag) + } + + newCatalog.Releases = append(newCatalog.Releases, catalogRelease{ + Tag: oldRelease.Tag, + Fixture: oldRelease.Fixture, + ProvenanceSHA256: newProvenance, // Use new provenance in case fixture was regenerated + Signature: newSig, + AppliedVersion: oldRelease.AppliedVersion, + HasSourceMetadata: oldRelease.HasSourceMetadata, + }) + } + + // Update unmanifested fixtures with new signatures + for _, oldUnmanifested := range oldCatalog.UnmanifestedFixtures { + newSig, ok := fixtureSignatures[oldUnmanifested.Fixture] + if !ok { + return fmt.Errorf("fixture not found for unmanifested: %s", oldUnmanifested.Fixture) + } + newProvenance, ok := fixtureProvenances[oldUnmanifested.Fixture] + if !ok { + return fmt.Errorf("provenance not computed for fixture %s", oldUnmanifested.Fixture) + } + + newCatalog.UnmanifestedFixtures = append(newCatalog.UnmanifestedFixtures, catalogFixture{ + Fixture: oldUnmanifested.Fixture, + ProvenanceSHA256: newProvenance, + Signature: newSig, + AppliedVersion: oldUnmanifested.AppliedVersion, + HasSourceMetadata: oldUnmanifested.HasSourceMetadata, + Provenance: oldUnmanifested.Provenance, + }) + } + + // Marshal to JSON with nice formatting + data, err := json.MarshalIndent(newCatalog, "", " ") + if err != nil { + return fmt.Errorf("marshal catalog: %w", err) + } + + // Write to file + if err := os.WriteFile(manifestPath, append(data, '\n'), 0644); err != nil { + return fmt.Errorf("write manifest: %w", err) + } + + fmt.Printf("Regenerated manifest with %d releases and %d unmanifested fixtures\n", + len(newCatalog.Releases), len(newCatalog.UnmanifestedFixtures)) + return nil +} + +func loadCatalogFromPath(path string) (Catalog, error) { + data, err := os.ReadFile(path) + if err != nil { + return Catalog{}, err + } + + var catalog Catalog + if err := json.Unmarshal(data, &catalog); err != nil { + return Catalog{}, fmt.Errorf("unmarshal catalog: %w", err) + } + return catalog, nil +} diff --git a/internal/compat/schema.go b/internal/compat/schema.go index dd02b30..dc09d7b 100644 --- a/internal/compat/schema.go +++ b/internal/compat/schema.go @@ -412,7 +412,53 @@ func isFTSShadowObject(name string, virtualTables map[string]bool) bool { } func normalizeSQL(sqlText string) string { - return strings.TrimSpace(sqlText) + // Collapse runs of whitespace outside string literals, but preserve whitespace + // within single-quoted SQL strings (including '' escapes). + var result strings.Builder + inQuote := false + lastWasSpace := false + + for i := 0; i < len(sqlText); i++ { + ch := sqlText[i] + + // Check for single quote (start or end of string literal, or '' escape) + if ch == '\'' { + // Check if this is a '' escape (two consecutive single quotes) + if inQuote && i+1 < len(sqlText) && sqlText[i+1] == '\'' { + // Write both quotes and skip the next one + result.WriteByte(ch) + result.WriteByte(ch) + i++ // skip the next quote + lastWasSpace = false + continue + } + // Toggle quote state + inQuote = !inQuote + result.WriteByte(ch) + lastWasSpace = false + continue + } + + // If inside quotes, preserve character as-is + if inQuote { + result.WriteByte(ch) + lastWasSpace = false + continue + } + + // Outside quotes: collapse whitespace + if ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' { + if !lastWasSpace { + result.WriteByte(' ') + lastWasSpace = true + } + } else { + result.WriteByte(ch) + lastWasSpace = false + } + } + + return strings.TrimSpace(result.String()) } func schemaRecord(kind, table, name, columns, sqlText string) string { diff --git a/internal/compat/schema_test.go b/internal/compat/schema_test.go index 49e6438..9cac007 100644 --- a/internal/compat/schema_test.go +++ b/internal/compat/schema_test.go @@ -281,7 +281,8 @@ func TestInspectIndexRecognizesObservedDevelopmentV13Shape(t *testing.T) { if err != nil { t.Fatal(err) } - const observedSignature = "sha256:952e517fe590b0c75a02996b6035ac6bb22143b9a707b6448ad05a592bf015b4" + // Signature recomputed after normalizeSQL made whitespace-insensitive (issue #52) + const observedSignature = "sha256:4d04377754986f0da2f61f2ab73889168f596eb84e1f02df96e23072072e9375" if shape.Signature != observedSignature { t.Fatalf("development V13 signature = %s, want %s", shape.Signature, observedSignature) } @@ -387,3 +388,59 @@ func openSchema(t *testing.T, schemaSQL string) *sql.DB { } return db } + +func TestNormalizeSQLIsWhitespaceInsensitiveOutsideStringLiterals(t *testing.T) { + tests := []struct { + name string + sql1 string + sql2 string + }{ + { + name: "multiple spaces collapsed to single space", + sql1: "CREATE TABLE foo (id INTEGER)", + sql2: "CREATE TABLE foo (id INTEGER)", + }, + { + name: "newlines and tabs collapsed to single space", + sql1: "CREATE\n TABLE\tfoo\n(\nid\nINTEGER\n)", + sql2: "CREATE TABLE foo ( id INTEGER )", + }, + { + name: "alter-built schema: inline vs multiline columns normalize the same", + sql1: "CREATE TABLE search_items (\n content_type TEXT DEFAULT 'text',\n extraction_version INTEGER,\n was_interrupted INTEGER\n);", + sql2: "CREATE TABLE search_items ( content_type TEXT DEFAULT 'text', extraction_version INTEGER, was_interrupted INTEGER );", + }, + { + name: "preserve space inside single-quoted literal", + sql1: "CREATE TABLE foo (body TEXT DEFAULT 'alpha beta')", + sql2: "CREATE TABLE foo (body TEXT DEFAULT 'alpha beta')", + }, + { + name: "double-single-quote escape inside literal", + sql1: "CREATE TABLE foo (body TEXT DEFAULT 'O''Brien')", + sql2: "CREATE TABLE foo (body TEXT DEFAULT 'O''Brien')", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + norm1 := normalizeSQL(tt.sql1) + norm2 := normalizeSQL(tt.sql2) + if norm1 != norm2 { + t.Fatalf("normalization differs: %q != %q", norm1, norm2) + } + }) + } +} + +func TestNormalizeSQLPreservesWhitespaceInStringLiterals(t *testing.T) { + // This is critical: whitespace inside string literals must be preserved exactly + sql := "CREATE TABLE foo (body TEXT DEFAULT 'alpha beta', CHECK (body <> 'gamma delta'))" + norm := normalizeSQL(sql) + if !strings.Contains(norm, "'alpha beta'") { + t.Fatalf("whitespace in first literal not preserved: %q", norm) + } + if !strings.Contains(norm, "'gamma delta'") { + t.Fatalf("whitespace in second literal not preserved: %q", norm) + } +} diff --git a/internal/compat/testdata/release-schemas/manifest.json b/internal/compat/testdata/release-schemas/manifest.json index 8c790f7..ef6cb23 100644 --- a/internal/compat/testdata/release-schemas/manifest.json +++ b/internal/compat/testdata/release-schemas/manifest.json @@ -6,7 +6,7 @@ "Tag": "v0.3.7", "Fixture": "v1.sql", "ProvenanceSHA256": "7b09c4c93e90b0cbd45cc0b851dcacefc0590145582886636c2e420145ccb409", - "Signature": "sha256:e2f7cd4bd71c964717c00fd67c2b3f396306307f578382c4a7956f83f8555a57", + "Signature": "sha256:47b3486d2119a62b67d386a984985cd4af301576c4429ebda4afa065bc4b0ab8", "AppliedVersion": 1, "HasSourceMetadata": true }, @@ -14,7 +14,7 @@ "Tag": "v0.3.9", "Fixture": "v1.sql", "ProvenanceSHA256": "7b09c4c93e90b0cbd45cc0b851dcacefc0590145582886636c2e420145ccb409", - "Signature": "sha256:e2f7cd4bd71c964717c00fd67c2b3f396306307f578382c4a7956f83f8555a57", + "Signature": "sha256:47b3486d2119a62b67d386a984985cd4af301576c4429ebda4afa065bc4b0ab8", "AppliedVersion": 1, "HasSourceMetadata": true }, @@ -22,7 +22,7 @@ "Tag": "v0.3.10", "Fixture": "v1.sql", "ProvenanceSHA256": "7b09c4c93e90b0cbd45cc0b851dcacefc0590145582886636c2e420145ccb409", - "Signature": "sha256:e2f7cd4bd71c964717c00fd67c2b3f396306307f578382c4a7956f83f8555a57", + "Signature": "sha256:47b3486d2119a62b67d386a984985cd4af301576c4429ebda4afa065bc4b0ab8", "AppliedVersion": 1, "HasSourceMetadata": true }, @@ -30,7 +30,7 @@ "Tag": "v0.3.11", "Fixture": "v3.sql", "ProvenanceSHA256": "1882e91edfc1eaf865b27b1796b8d20c92d05a2a55920f37f428df8b4e348e5d", - "Signature": "sha256:ed864d83d4c873bd34e0e3708f32a5bf47d65cac8ad55ff04305a81d91e9e22e", + "Signature": "sha256:5b069c657d76eda9257eacb49da8b668560b98c075835744087556e23eaf54fc", "AppliedVersion": 3, "HasSourceMetadata": true }, @@ -38,7 +38,7 @@ "Tag": "v0.3.12", "Fixture": "v3.sql", "ProvenanceSHA256": "1882e91edfc1eaf865b27b1796b8d20c92d05a2a55920f37f428df8b4e348e5d", - "Signature": "sha256:ed864d83d4c873bd34e0e3708f32a5bf47d65cac8ad55ff04305a81d91e9e22e", + "Signature": "sha256:5b069c657d76eda9257eacb49da8b668560b98c075835744087556e23eaf54fc", "AppliedVersion": 3, "HasSourceMetadata": true }, @@ -46,7 +46,7 @@ "Tag": "v0.3.13", "Fixture": "v3.sql", "ProvenanceSHA256": "1882e91edfc1eaf865b27b1796b8d20c92d05a2a55920f37f428df8b4e348e5d", - "Signature": "sha256:ed864d83d4c873bd34e0e3708f32a5bf47d65cac8ad55ff04305a81d91e9e22e", + "Signature": "sha256:5b069c657d76eda9257eacb49da8b668560b98c075835744087556e23eaf54fc", "AppliedVersion": 3, "HasSourceMetadata": true }, @@ -54,7 +54,7 @@ "Tag": "v0.3.14", "Fixture": "v3.sql", "ProvenanceSHA256": "1882e91edfc1eaf865b27b1796b8d20c92d05a2a55920f37f428df8b4e348e5d", - "Signature": "sha256:ed864d83d4c873bd34e0e3708f32a5bf47d65cac8ad55ff04305a81d91e9e22e", + "Signature": "sha256:5b069c657d76eda9257eacb49da8b668560b98c075835744087556e23eaf54fc", "AppliedVersion": 3, "HasSourceMetadata": true }, @@ -62,7 +62,7 @@ "Tag": "v0.3.15", "Fixture": "v3.sql", "ProvenanceSHA256": "1882e91edfc1eaf865b27b1796b8d20c92d05a2a55920f37f428df8b4e348e5d", - "Signature": "sha256:ed864d83d4c873bd34e0e3708f32a5bf47d65cac8ad55ff04305a81d91e9e22e", + "Signature": "sha256:5b069c657d76eda9257eacb49da8b668560b98c075835744087556e23eaf54fc", "AppliedVersion": 3, "HasSourceMetadata": true }, @@ -70,7 +70,7 @@ "Tag": "v0.3.17", "Fixture": "v3.sql", "ProvenanceSHA256": "1882e91edfc1eaf865b27b1796b8d20c92d05a2a55920f37f428df8b4e348e5d", - "Signature": "sha256:ed864d83d4c873bd34e0e3708f32a5bf47d65cac8ad55ff04305a81d91e9e22e", + "Signature": "sha256:5b069c657d76eda9257eacb49da8b668560b98c075835744087556e23eaf54fc", "AppliedVersion": 3, "HasSourceMetadata": true }, @@ -78,7 +78,7 @@ "Tag": "v0.3.18", "Fixture": "v3.sql", "ProvenanceSHA256": "1882e91edfc1eaf865b27b1796b8d20c92d05a2a55920f37f428df8b4e348e5d", - "Signature": "sha256:ed864d83d4c873bd34e0e3708f32a5bf47d65cac8ad55ff04305a81d91e9e22e", + "Signature": "sha256:5b069c657d76eda9257eacb49da8b668560b98c075835744087556e23eaf54fc", "AppliedVersion": 3, "HasSourceMetadata": true }, @@ -86,7 +86,7 @@ "Tag": "v0.3.19", "Fixture": "v3.sql", "ProvenanceSHA256": "1882e91edfc1eaf865b27b1796b8d20c92d05a2a55920f37f428df8b4e348e5d", - "Signature": "sha256:ed864d83d4c873bd34e0e3708f32a5bf47d65cac8ad55ff04305a81d91e9e22e", + "Signature": "sha256:5b069c657d76eda9257eacb49da8b668560b98c075835744087556e23eaf54fc", "AppliedVersion": 3, "HasSourceMetadata": true }, @@ -94,7 +94,7 @@ "Tag": "v0.4.0", "Fixture": "v3.sql", "ProvenanceSHA256": "1882e91edfc1eaf865b27b1796b8d20c92d05a2a55920f37f428df8b4e348e5d", - "Signature": "sha256:ed864d83d4c873bd34e0e3708f32a5bf47d65cac8ad55ff04305a81d91e9e22e", + "Signature": "sha256:5b069c657d76eda9257eacb49da8b668560b98c075835744087556e23eaf54fc", "AppliedVersion": 3, "HasSourceMetadata": true }, @@ -102,7 +102,7 @@ "Tag": "v0.4.1", "Fixture": "v3.sql", "ProvenanceSHA256": "1882e91edfc1eaf865b27b1796b8d20c92d05a2a55920f37f428df8b4e348e5d", - "Signature": "sha256:ed864d83d4c873bd34e0e3708f32a5bf47d65cac8ad55ff04305a81d91e9e22e", + "Signature": "sha256:5b069c657d76eda9257eacb49da8b668560b98c075835744087556e23eaf54fc", "AppliedVersion": 3, "HasSourceMetadata": true }, @@ -110,7 +110,7 @@ "Tag": "v0.4.3", "Fixture": "v3.sql", "ProvenanceSHA256": "1882e91edfc1eaf865b27b1796b8d20c92d05a2a55920f37f428df8b4e348e5d", - "Signature": "sha256:ed864d83d4c873bd34e0e3708f32a5bf47d65cac8ad55ff04305a81d91e9e22e", + "Signature": "sha256:5b069c657d76eda9257eacb49da8b668560b98c075835744087556e23eaf54fc", "AppliedVersion": 3, "HasSourceMetadata": true }, @@ -118,7 +118,7 @@ "Tag": "v1.0.0", "Fixture": "v3.sql", "ProvenanceSHA256": "1882e91edfc1eaf865b27b1796b8d20c92d05a2a55920f37f428df8b4e348e5d", - "Signature": "sha256:ed864d83d4c873bd34e0e3708f32a5bf47d65cac8ad55ff04305a81d91e9e22e", + "Signature": "sha256:5b069c657d76eda9257eacb49da8b668560b98c075835744087556e23eaf54fc", "AppliedVersion": 3, "HasSourceMetadata": true }, @@ -126,7 +126,7 @@ "Tag": "v1.0.1", "Fixture": "v3.sql", "ProvenanceSHA256": "1882e91edfc1eaf865b27b1796b8d20c92d05a2a55920f37f428df8b4e348e5d", - "Signature": "sha256:ed864d83d4c873bd34e0e3708f32a5bf47d65cac8ad55ff04305a81d91e9e22e", + "Signature": "sha256:5b069c657d76eda9257eacb49da8b668560b98c075835744087556e23eaf54fc", "AppliedVersion": 3, "HasSourceMetadata": true }, @@ -134,7 +134,7 @@ "Tag": "v1.0.2", "Fixture": "v3.sql", "ProvenanceSHA256": "1882e91edfc1eaf865b27b1796b8d20c92d05a2a55920f37f428df8b4e348e5d", - "Signature": "sha256:ed864d83d4c873bd34e0e3708f32a5bf47d65cac8ad55ff04305a81d91e9e22e", + "Signature": "sha256:5b069c657d76eda9257eacb49da8b668560b98c075835744087556e23eaf54fc", "AppliedVersion": 3, "HasSourceMetadata": true }, @@ -142,7 +142,7 @@ "Tag": "v1.0.3", "Fixture": "v3.sql", "ProvenanceSHA256": "1882e91edfc1eaf865b27b1796b8d20c92d05a2a55920f37f428df8b4e348e5d", - "Signature": "sha256:ed864d83d4c873bd34e0e3708f32a5bf47d65cac8ad55ff04305a81d91e9e22e", + "Signature": "sha256:5b069c657d76eda9257eacb49da8b668560b98c075835744087556e23eaf54fc", "AppliedVersion": 3, "HasSourceMetadata": true }, @@ -150,7 +150,7 @@ "Tag": "v1.0.4", "Fixture": "v3.sql", "ProvenanceSHA256": "1882e91edfc1eaf865b27b1796b8d20c92d05a2a55920f37f428df8b4e348e5d", - "Signature": "sha256:ed864d83d4c873bd34e0e3708f32a5bf47d65cac8ad55ff04305a81d91e9e22e", + "Signature": "sha256:5b069c657d76eda9257eacb49da8b668560b98c075835744087556e23eaf54fc", "AppliedVersion": 3, "HasSourceMetadata": true }, @@ -158,7 +158,7 @@ "Tag": "v1.1.0", "Fixture": "v3.sql", "ProvenanceSHA256": "1882e91edfc1eaf865b27b1796b8d20c92d05a2a55920f37f428df8b4e348e5d", - "Signature": "sha256:ed864d83d4c873bd34e0e3708f32a5bf47d65cac8ad55ff04305a81d91e9e22e", + "Signature": "sha256:5b069c657d76eda9257eacb49da8b668560b98c075835744087556e23eaf54fc", "AppliedVersion": 3, "HasSourceMetadata": true }, @@ -166,7 +166,7 @@ "Tag": "v1.1.1", "Fixture": "v3.sql", "ProvenanceSHA256": "1882e91edfc1eaf865b27b1796b8d20c92d05a2a55920f37f428df8b4e348e5d", - "Signature": "sha256:ed864d83d4c873bd34e0e3708f32a5bf47d65cac8ad55ff04305a81d91e9e22e", + "Signature": "sha256:5b069c657d76eda9257eacb49da8b668560b98c075835744087556e23eaf54fc", "AppliedVersion": 3, "HasSourceMetadata": true }, @@ -174,7 +174,7 @@ "Tag": "v1.2.0", "Fixture": "v3.sql", "ProvenanceSHA256": "1882e91edfc1eaf865b27b1796b8d20c92d05a2a55920f37f428df8b4e348e5d", - "Signature": "sha256:ed864d83d4c873bd34e0e3708f32a5bf47d65cac8ad55ff04305a81d91e9e22e", + "Signature": "sha256:5b069c657d76eda9257eacb49da8b668560b98c075835744087556e23eaf54fc", "AppliedVersion": 3, "HasSourceMetadata": true }, @@ -182,7 +182,7 @@ "Tag": "v1.2.1", "Fixture": "v3.sql", "ProvenanceSHA256": "1882e91edfc1eaf865b27b1796b8d20c92d05a2a55920f37f428df8b4e348e5d", - "Signature": "sha256:ed864d83d4c873bd34e0e3708f32a5bf47d65cac8ad55ff04305a81d91e9e22e", + "Signature": "sha256:5b069c657d76eda9257eacb49da8b668560b98c075835744087556e23eaf54fc", "AppliedVersion": 3, "HasSourceMetadata": true }, @@ -190,7 +190,7 @@ "Tag": "v1.3.0", "Fixture": "v3.sql", "ProvenanceSHA256": "1882e91edfc1eaf865b27b1796b8d20c92d05a2a55920f37f428df8b4e348e5d", - "Signature": "sha256:ed864d83d4c873bd34e0e3708f32a5bf47d65cac8ad55ff04305a81d91e9e22e", + "Signature": "sha256:5b069c657d76eda9257eacb49da8b668560b98c075835744087556e23eaf54fc", "AppliedVersion": 3, "HasSourceMetadata": true }, @@ -198,7 +198,7 @@ "Tag": "v1.3.1", "Fixture": "v3.sql", "ProvenanceSHA256": "1882e91edfc1eaf865b27b1796b8d20c92d05a2a55920f37f428df8b4e348e5d", - "Signature": "sha256:ed864d83d4c873bd34e0e3708f32a5bf47d65cac8ad55ff04305a81d91e9e22e", + "Signature": "sha256:5b069c657d76eda9257eacb49da8b668560b98c075835744087556e23eaf54fc", "AppliedVersion": 3, "HasSourceMetadata": true }, @@ -206,7 +206,7 @@ "Tag": "v1.3.2", "Fixture": "v3.sql", "ProvenanceSHA256": "1882e91edfc1eaf865b27b1796b8d20c92d05a2a55920f37f428df8b4e348e5d", - "Signature": "sha256:ed864d83d4c873bd34e0e3708f32a5bf47d65cac8ad55ff04305a81d91e9e22e", + "Signature": "sha256:5b069c657d76eda9257eacb49da8b668560b98c075835744087556e23eaf54fc", "AppliedVersion": 3, "HasSourceMetadata": true }, @@ -214,7 +214,7 @@ "Tag": "v1.3.3", "Fixture": "v3.sql", "ProvenanceSHA256": "1882e91edfc1eaf865b27b1796b8d20c92d05a2a55920f37f428df8b4e348e5d", - "Signature": "sha256:ed864d83d4c873bd34e0e3708f32a5bf47d65cac8ad55ff04305a81d91e9e22e", + "Signature": "sha256:5b069c657d76eda9257eacb49da8b668560b98c075835744087556e23eaf54fc", "AppliedVersion": 3, "HasSourceMetadata": true }, @@ -222,7 +222,7 @@ "Tag": "v1.3.5", "Fixture": "v3.sql", "ProvenanceSHA256": "1882e91edfc1eaf865b27b1796b8d20c92d05a2a55920f37f428df8b4e348e5d", - "Signature": "sha256:ed864d83d4c873bd34e0e3708f32a5bf47d65cac8ad55ff04305a81d91e9e22e", + "Signature": "sha256:5b069c657d76eda9257eacb49da8b668560b98c075835744087556e23eaf54fc", "AppliedVersion": 3, "HasSourceMetadata": true }, @@ -230,7 +230,7 @@ "Tag": "v1.4.0", "Fixture": "v4.sql", "ProvenanceSHA256": "e8f28b3a1b37acd4f6bd721615c150d53c9d511c05c53e625d4de22230b3667c", - "Signature": "sha256:b2bba3cec75bb3f6c697e78882e3924550832e3b5706d013f3f54d32a1c25acd", + "Signature": "sha256:944c7d94376ca4de5ea321f7a8efccbddf061526cfbf556640cfae15be34f944", "AppliedVersion": 4, "HasSourceMetadata": true }, @@ -238,7 +238,7 @@ "Tag": "v1.4.1", "Fixture": "v4.sql", "ProvenanceSHA256": "e8f28b3a1b37acd4f6bd721615c150d53c9d511c05c53e625d4de22230b3667c", - "Signature": "sha256:b2bba3cec75bb3f6c697e78882e3924550832e3b5706d013f3f54d32a1c25acd", + "Signature": "sha256:944c7d94376ca4de5ea321f7a8efccbddf061526cfbf556640cfae15be34f944", "AppliedVersion": 4, "HasSourceMetadata": true }, @@ -246,7 +246,7 @@ "Tag": "v1.4.2", "Fixture": "v4.sql", "ProvenanceSHA256": "e8f28b3a1b37acd4f6bd721615c150d53c9d511c05c53e625d4de22230b3667c", - "Signature": "sha256:b2bba3cec75bb3f6c697e78882e3924550832e3b5706d013f3f54d32a1c25acd", + "Signature": "sha256:944c7d94376ca4de5ea321f7a8efccbddf061526cfbf556640cfae15be34f944", "AppliedVersion": 4, "HasSourceMetadata": true }, @@ -254,7 +254,7 @@ "Tag": "v1.4.3", "Fixture": "v4.sql", "ProvenanceSHA256": "e8f28b3a1b37acd4f6bd721615c150d53c9d511c05c53e625d4de22230b3667c", - "Signature": "sha256:b2bba3cec75bb3f6c697e78882e3924550832e3b5706d013f3f54d32a1c25acd", + "Signature": "sha256:944c7d94376ca4de5ea321f7a8efccbddf061526cfbf556640cfae15be34f944", "AppliedVersion": 4, "HasSourceMetadata": true }, @@ -262,7 +262,7 @@ "Tag": "v1.4.4", "Fixture": "v4.sql", "ProvenanceSHA256": "e8f28b3a1b37acd4f6bd721615c150d53c9d511c05c53e625d4de22230b3667c", - "Signature": "sha256:b2bba3cec75bb3f6c697e78882e3924550832e3b5706d013f3f54d32a1c25acd", + "Signature": "sha256:944c7d94376ca4de5ea321f7a8efccbddf061526cfbf556640cfae15be34f944", "AppliedVersion": 4, "HasSourceMetadata": true }, @@ -270,7 +270,7 @@ "Tag": "v2.0.0", "Fixture": "v5-with-source-metadata.sql", "ProvenanceSHA256": "1efece456c94cf46a7f5dba8f84e0a76fb444dbd4cae016b595a7fd68427c062", - "Signature": "sha256:ce23ee41f1af6007bd0bde7b020f2cac4c215d23bae456bdfce25b90313c709e", + "Signature": "sha256:ccbc79ad72964f9dbbf1fef77e3530a38221c991bec7c6bec1ecd3e40bd6f3ce", "AppliedVersion": 5, "HasSourceMetadata": true }, @@ -278,7 +278,7 @@ "Tag": "v2.0.1", "Fixture": "v5-with-source-metadata.sql", "ProvenanceSHA256": "1efece456c94cf46a7f5dba8f84e0a76fb444dbd4cae016b595a7fd68427c062", - "Signature": "sha256:ce23ee41f1af6007bd0bde7b020f2cac4c215d23bae456bdfce25b90313c709e", + "Signature": "sha256:ccbc79ad72964f9dbbf1fef77e3530a38221c991bec7c6bec1ecd3e40bd6f3ce", "AppliedVersion": 5, "HasSourceMetadata": true }, @@ -286,7 +286,7 @@ "Tag": "v2.1.0", "Fixture": "v5-with-source-metadata.sql", "ProvenanceSHA256": "1efece456c94cf46a7f5dba8f84e0a76fb444dbd4cae016b595a7fd68427c062", - "Signature": "sha256:ce23ee41f1af6007bd0bde7b020f2cac4c215d23bae456bdfce25b90313c709e", + "Signature": "sha256:ccbc79ad72964f9dbbf1fef77e3530a38221c991bec7c6bec1ecd3e40bd6f3ce", "AppliedVersion": 5, "HasSourceMetadata": true }, @@ -294,7 +294,7 @@ "Tag": "v2.2.0", "Fixture": "v7.sql", "ProvenanceSHA256": "324a876835ad39ebe9749bf1a381018c267f4c21df053589d2046d2eb9cca24e", - "Signature": "sha256:dcaa1205df039fa545aa7ebe672d391acfcbf651869c2603ceef12dab9de00d2", + "Signature": "sha256:ff4f927f570680865b7f62bc9d3127b97db5ccc9c7816c4e08022a630ca79035", "AppliedVersion": 7, "HasSourceMetadata": false }, @@ -302,7 +302,7 @@ "Tag": "v2.2.1", "Fixture": "v7.sql", "ProvenanceSHA256": "324a876835ad39ebe9749bf1a381018c267f4c21df053589d2046d2eb9cca24e", - "Signature": "sha256:dcaa1205df039fa545aa7ebe672d391acfcbf651869c2603ceef12dab9de00d2", + "Signature": "sha256:ff4f927f570680865b7f62bc9d3127b97db5ccc9c7816c4e08022a630ca79035", "AppliedVersion": 7, "HasSourceMetadata": false }, @@ -310,7 +310,7 @@ "Tag": "v2.2.2", "Fixture": "v7.sql", "ProvenanceSHA256": "324a876835ad39ebe9749bf1a381018c267f4c21df053589d2046d2eb9cca24e", - "Signature": "sha256:dcaa1205df039fa545aa7ebe672d391acfcbf651869c2603ceef12dab9de00d2", + "Signature": "sha256:ff4f927f570680865b7f62bc9d3127b97db5ccc9c7816c4e08022a630ca79035", "AppliedVersion": 7, "HasSourceMetadata": false }, @@ -318,7 +318,7 @@ "Tag": "v2.2.3", "Fixture": "v7.sql", "ProvenanceSHA256": "324a876835ad39ebe9749bf1a381018c267f4c21df053589d2046d2eb9cca24e", - "Signature": "sha256:dcaa1205df039fa545aa7ebe672d391acfcbf651869c2603ceef12dab9de00d2", + "Signature": "sha256:ff4f927f570680865b7f62bc9d3127b97db5ccc9c7816c4e08022a630ca79035", "AppliedVersion": 7, "HasSourceMetadata": false }, @@ -326,7 +326,7 @@ "Tag": "v2.3.0", "Fixture": "v8.sql", "ProvenanceSHA256": "c66577744d5feacde305382d5159b67390450c18169d3da6fda5e81051b49cf6", - "Signature": "sha256:6d503881ebac89e009644df5bf24cf7c4b1afed334afb37e6ee87d2ca6edae87", + "Signature": "sha256:aac78c23870733c9d3e541c8be111de99798bc8f81277beca851fbd1f465ed35", "AppliedVersion": 8, "HasSourceMetadata": false }, @@ -334,7 +334,7 @@ "Tag": "v2.4.0", "Fixture": "v9.sql", "ProvenanceSHA256": "50469b7ae10e1c824b59d094290c2b59bbc31f3924175d2888bb23f76450b2a1", - "Signature": "sha256:e41ae857c862ffa10516681b65bcd8c755484b338a6847dad5c0d770a202670f", + "Signature": "sha256:dcd0153f47c2279a02e0c8f9505d6ac875817053de3eeb557c7ec10cf304440e", "AppliedVersion": 9, "HasSourceMetadata": false }, @@ -342,7 +342,7 @@ "Tag": "v2.5.0", "Fixture": "v9.sql", "ProvenanceSHA256": "50469b7ae10e1c824b59d094290c2b59bbc31f3924175d2888bb23f76450b2a1", - "Signature": "sha256:e41ae857c862ffa10516681b65bcd8c755484b338a6847dad5c0d770a202670f", + "Signature": "sha256:dcd0153f47c2279a02e0c8f9505d6ac875817053de3eeb557c7ec10cf304440e", "AppliedVersion": 9, "HasSourceMetadata": false }, @@ -350,7 +350,7 @@ "Tag": "v2.6.0", "Fixture": "v10.sql", "ProvenanceSHA256": "0b6fa1dbe8981705aa68e0a376c874ae7a98588380d2301d108ddcf5f4410abc", - "Signature": "sha256:8e28ce0fe1c5f3cd36f2d64ac7a7996f032e66c0c32468a0a206eb983491388c", + "Signature": "sha256:95c059024be9f2b8f79f8a88bde9234bf8cc1ec238a888d6cbffce861c1db8f9", "AppliedVersion": 10, "HasSourceMetadata": false }, @@ -358,7 +358,7 @@ "Tag": "v2.7.0", "Fixture": "v11.sql", "ProvenanceSHA256": "78927a394a0d1cb62651288e7626de4e2983530a3a03b9147440e56b457e26ba", - "Signature": "sha256:975656bb5e894e12bd30aa65bb3366ca2bdeee23f59aca8e133b18a62e7ffad5", + "Signature": "sha256:7ed4932adf323c57fc39a396114b9d34ed4e20b02c409fc06a6020f2516a91cd", "AppliedVersion": 11, "HasSourceMetadata": false }, @@ -366,7 +366,7 @@ "Tag": "v2.8.0", "Fixture": "v12.sql", "ProvenanceSHA256": "84710f15fcff04567bfe8abe64f337129722080816b7cfbce35740668aacfe41", - "Signature": "sha256:ff136d58048d69f02be857f632750ef3e2daa35fd6ba637f7645986950f83d31", + "Signature": "sha256:54c6f7a05268fab162fdaf1cf43412788aed90633b45ce9ce1694e1ee76b5c94", "AppliedVersion": 12, "HasSourceMetadata": false }, @@ -374,7 +374,7 @@ "Tag": "v2.9.0", "Fixture": "v12.sql", "ProvenanceSHA256": "84710f15fcff04567bfe8abe64f337129722080816b7cfbce35740668aacfe41", - "Signature": "sha256:ff136d58048d69f02be857f632750ef3e2daa35fd6ba637f7645986950f83d31", + "Signature": "sha256:54c6f7a05268fab162fdaf1cf43412788aed90633b45ce9ce1694e1ee76b5c94", "AppliedVersion": 12, "HasSourceMetadata": false }, @@ -382,7 +382,7 @@ "Tag": "v2.10.0", "Fixture": "v12.sql", "ProvenanceSHA256": "84710f15fcff04567bfe8abe64f337129722080816b7cfbce35740668aacfe41", - "Signature": "sha256:ff136d58048d69f02be857f632750ef3e2daa35fd6ba637f7645986950f83d31", + "Signature": "sha256:54c6f7a05268fab162fdaf1cf43412788aed90633b45ce9ce1694e1ee76b5c94", "AppliedVersion": 12, "HasSourceMetadata": false }, @@ -390,7 +390,7 @@ "Tag": "v2.11.0", "Fixture": "v12.sql", "ProvenanceSHA256": "84710f15fcff04567bfe8abe64f337129722080816b7cfbce35740668aacfe41", - "Signature": "sha256:ff136d58048d69f02be857f632750ef3e2daa35fd6ba637f7645986950f83d31", + "Signature": "sha256:54c6f7a05268fab162fdaf1cf43412788aed90633b45ce9ce1694e1ee76b5c94", "AppliedVersion": 12, "HasSourceMetadata": false }, @@ -398,7 +398,7 @@ "Tag": "v2.12.0", "Fixture": "v13.sql", "ProvenanceSHA256": "9d9e23de5a05318f1f3625b0d375b64b10e42672ddd213f84a08c4375c0afdcc", - "Signature": "sha256:ef52be2fb56acd0e51b38506746fa3594ed1de76bb0b5dd180767d3c903fb46d", + "Signature": "sha256:5d6baa68ddeb058a293aa80fce9371098c904b6ebb15321eb5fb5bef5c045adc", "AppliedVersion": 13, "HasSourceMetadata": false }, @@ -406,7 +406,7 @@ "Tag": "v2.13.0", "Fixture": "v13.sql", "ProvenanceSHA256": "9d9e23de5a05318f1f3625b0d375b64b10e42672ddd213f84a08c4375c0afdcc", - "Signature": "sha256:ef52be2fb56acd0e51b38506746fa3594ed1de76bb0b5dd180767d3c903fb46d", + "Signature": "sha256:5d6baa68ddeb058a293aa80fce9371098c904b6ebb15321eb5fb5bef5c045adc", "AppliedVersion": 13, "HasSourceMetadata": false }, @@ -414,7 +414,7 @@ "Tag": "v2.14.0", "Fixture": "v13.sql", "ProvenanceSHA256": "9d9e23de5a05318f1f3625b0d375b64b10e42672ddd213f84a08c4375c0afdcc", - "Signature": "sha256:ef52be2fb56acd0e51b38506746fa3594ed1de76bb0b5dd180767d3c903fb46d", + "Signature": "sha256:5d6baa68ddeb058a293aa80fce9371098c904b6ebb15321eb5fb5bef5c045adc", "AppliedVersion": 13, "HasSourceMetadata": false }, @@ -422,7 +422,7 @@ "Tag": "v2.14.1", "Fixture": "v13.sql", "ProvenanceSHA256": "9d9e23de5a05318f1f3625b0d375b64b10e42672ddd213f84a08c4375c0afdcc", - "Signature": "sha256:ef52be2fb56acd0e51b38506746fa3594ed1de76bb0b5dd180767d3c903fb46d", + "Signature": "sha256:5d6baa68ddeb058a293aa80fce9371098c904b6ebb15321eb5fb5bef5c045adc", "AppliedVersion": 13, "HasSourceMetadata": false }, @@ -430,7 +430,7 @@ "Tag": "v2.14.2", "Fixture": "v13.sql", "ProvenanceSHA256": "9d9e23de5a05318f1f3625b0d375b64b10e42672ddd213f84a08c4375c0afdcc", - "Signature": "sha256:ef52be2fb56acd0e51b38506746fa3594ed1de76bb0b5dd180767d3c903fb46d", + "Signature": "sha256:5d6baa68ddeb058a293aa80fce9371098c904b6ebb15321eb5fb5bef5c045adc", "AppliedVersion": 13, "HasSourceMetadata": false }, @@ -438,7 +438,7 @@ "Tag": "v2.14.3", "Fixture": "v13.sql", "ProvenanceSHA256": "9d9e23de5a05318f1f3625b0d375b64b10e42672ddd213f84a08c4375c0afdcc", - "Signature": "sha256:ef52be2fb56acd0e51b38506746fa3594ed1de76bb0b5dd180767d3c903fb46d", + "Signature": "sha256:5d6baa68ddeb058a293aa80fce9371098c904b6ebb15321eb5fb5bef5c045adc", "AppliedVersion": 13, "HasSourceMetadata": false }, @@ -446,7 +446,7 @@ "Tag": "v2.15.0", "Fixture": "v13.sql", "ProvenanceSHA256": "9d9e23de5a05318f1f3625b0d375b64b10e42672ddd213f84a08c4375c0afdcc", - "Signature": "sha256:ef52be2fb56acd0e51b38506746fa3594ed1de76bb0b5dd180767d3c903fb46d", + "Signature": "sha256:5d6baa68ddeb058a293aa80fce9371098c904b6ebb15321eb5fb5bef5c045adc", "AppliedVersion": 13, "HasSourceMetadata": false }, @@ -454,7 +454,7 @@ "Tag": "v2.15.1", "Fixture": "v13.sql", "ProvenanceSHA256": "9d9e23de5a05318f1f3625b0d375b64b10e42672ddd213f84a08c4375c0afdcc", - "Signature": "sha256:ef52be2fb56acd0e51b38506746fa3594ed1de76bb0b5dd180767d3c903fb46d", + "Signature": "sha256:5d6baa68ddeb058a293aa80fce9371098c904b6ebb15321eb5fb5bef5c045adc", "AppliedVersion": 13, "HasSourceMetadata": false }, @@ -462,7 +462,7 @@ "Tag": "v2.16.0", "Fixture": "v13.sql", "ProvenanceSHA256": "9d9e23de5a05318f1f3625b0d375b64b10e42672ddd213f84a08c4375c0afdcc", - "Signature": "sha256:ef52be2fb56acd0e51b38506746fa3594ed1de76bb0b5dd180767d3c903fb46d", + "Signature": "sha256:5d6baa68ddeb058a293aa80fce9371098c904b6ebb15321eb5fb5bef5c045adc", "AppliedVersion": 13, "HasSourceMetadata": false }, @@ -470,7 +470,7 @@ "Tag": "v2.16.1", "Fixture": "v13.sql", "ProvenanceSHA256": "9d9e23de5a05318f1f3625b0d375b64b10e42672ddd213f84a08c4375c0afdcc", - "Signature": "sha256:ef52be2fb56acd0e51b38506746fa3594ed1de76bb0b5dd180767d3c903fb46d", + "Signature": "sha256:5d6baa68ddeb058a293aa80fce9371098c904b6ebb15321eb5fb5bef5c045adc", "AppliedVersion": 13, "HasSourceMetadata": false }, @@ -478,7 +478,7 @@ "Tag": "v3.0.0", "Fixture": "v13.sql", "ProvenanceSHA256": "9d9e23de5a05318f1f3625b0d375b64b10e42672ddd213f84a08c4375c0afdcc", - "Signature": "sha256:ef52be2fb56acd0e51b38506746fa3594ed1de76bb0b5dd180767d3c903fb46d", + "Signature": "sha256:5d6baa68ddeb058a293aa80fce9371098c904b6ebb15321eb5fb5bef5c045adc", "AppliedVersion": 13, "HasSourceMetadata": false }, @@ -486,7 +486,7 @@ "Tag": "v3.0.1", "Fixture": "v13.sql", "ProvenanceSHA256": "9d9e23de5a05318f1f3625b0d375b64b10e42672ddd213f84a08c4375c0afdcc", - "Signature": "sha256:ef52be2fb56acd0e51b38506746fa3594ed1de76bb0b5dd180767d3c903fb46d", + "Signature": "sha256:5d6baa68ddeb058a293aa80fce9371098c904b6ebb15321eb5fb5bef5c045adc", "AppliedVersion": 13, "HasSourceMetadata": false }, @@ -494,7 +494,7 @@ "Tag": "v3.0.2", "Fixture": "v13.sql", "ProvenanceSHA256": "9d9e23de5a05318f1f3625b0d375b64b10e42672ddd213f84a08c4375c0afdcc", - "Signature": "sha256:ef52be2fb56acd0e51b38506746fa3594ed1de76bb0b5dd180767d3c903fb46d", + "Signature": "sha256:5d6baa68ddeb058a293aa80fce9371098c904b6ebb15321eb5fb5bef5c045adc", "AppliedVersion": 13, "HasSourceMetadata": false }, @@ -502,7 +502,7 @@ "Tag": "v3.1.0", "Fixture": "v13.sql", "ProvenanceSHA256": "9d9e23de5a05318f1f3625b0d375b64b10e42672ddd213f84a08c4375c0afdcc", - "Signature": "sha256:ef52be2fb56acd0e51b38506746fa3594ed1de76bb0b5dd180767d3c903fb46d", + "Signature": "sha256:5d6baa68ddeb058a293aa80fce9371098c904b6ebb15321eb5fb5bef5c045adc", "AppliedVersion": 13, "HasSourceMetadata": false }, @@ -510,7 +510,7 @@ "Tag": "v3.2.0", "Fixture": "v13.sql", "ProvenanceSHA256": "9d9e23de5a05318f1f3625b0d375b64b10e42672ddd213f84a08c4375c0afdcc", - "Signature": "sha256:ef52be2fb56acd0e51b38506746fa3594ed1de76bb0b5dd180767d3c903fb46d", + "Signature": "sha256:5d6baa68ddeb058a293aa80fce9371098c904b6ebb15321eb5fb5bef5c045adc", "AppliedVersion": 13, "HasSourceMetadata": false }, @@ -518,7 +518,7 @@ "Tag": "v3.2.1", "Fixture": "v13.sql", "ProvenanceSHA256": "9d9e23de5a05318f1f3625b0d375b64b10e42672ddd213f84a08c4375c0afdcc", - "Signature": "sha256:ef52be2fb56acd0e51b38506746fa3594ed1de76bb0b5dd180767d3c903fb46d", + "Signature": "sha256:5d6baa68ddeb058a293aa80fce9371098c904b6ebb15321eb5fb5bef5c045adc", "AppliedVersion": 13, "HasSourceMetadata": false }, @@ -526,7 +526,7 @@ "Tag": "v3.2.2", "Fixture": "v13.sql", "ProvenanceSHA256": "9d9e23de5a05318f1f3625b0d375b64b10e42672ddd213f84a08c4375c0afdcc", - "Signature": "sha256:ef52be2fb56acd0e51b38506746fa3594ed1de76bb0b5dd180767d3c903fb46d", + "Signature": "sha256:5d6baa68ddeb058a293aa80fce9371098c904b6ebb15321eb5fb5bef5c045adc", "AppliedVersion": 13, "HasSourceMetadata": false }, @@ -534,7 +534,7 @@ "Tag": "v3.2.3", "Fixture": "v13.sql", "ProvenanceSHA256": "9d9e23de5a05318f1f3625b0d375b64b10e42672ddd213f84a08c4375c0afdcc", - "Signature": "sha256:ef52be2fb56acd0e51b38506746fa3594ed1de76bb0b5dd180767d3c903fb46d", + "Signature": "sha256:5d6baa68ddeb058a293aa80fce9371098c904b6ebb15321eb5fb5bef5c045adc", "AppliedVersion": 13, "HasSourceMetadata": false }, @@ -542,7 +542,7 @@ "Tag": "v3.2.4", "Fixture": "v13.sql", "ProvenanceSHA256": "9d9e23de5a05318f1f3625b0d375b64b10e42672ddd213f84a08c4375c0afdcc", - "Signature": "sha256:ef52be2fb56acd0e51b38506746fa3594ed1de76bb0b5dd180767d3c903fb46d", + "Signature": "sha256:5d6baa68ddeb058a293aa80fce9371098c904b6ebb15321eb5fb5bef5c045adc", "AppliedVersion": 13, "HasSourceMetadata": false }, @@ -550,7 +550,7 @@ "Tag": "v3.2.5", "Fixture": "v13.sql", "ProvenanceSHA256": "9d9e23de5a05318f1f3625b0d375b64b10e42672ddd213f84a08c4375c0afdcc", - "Signature": "sha256:ef52be2fb56acd0e51b38506746fa3594ed1de76bb0b5dd180767d3c903fb46d", + "Signature": "sha256:5d6baa68ddeb058a293aa80fce9371098c904b6ebb15321eb5fb5bef5c045adc", "AppliedVersion": 13, "HasSourceMetadata": false } @@ -559,7 +559,7 @@ { "Fixture": "v2.sql", "ProvenanceSHA256": "9e5857f14e30fe1391445064bf924b0efb12fa81d0e5011de72ce5f880cd7f6a", - "Signature": "sha256:a4256a6029bbc953df37a12979fc81b1879ef9f8ba28ff5cfe6b78472dee804b", + "Signature": "sha256:16da30dbb7692750430b6ba37754a9cf42bcaec1f04507da949f8adae01442c1", "AppliedVersion": 2, "HasSourceMetadata": true, "Provenance": "No manifest release tag maps to this V2-only compatibility triangulation fixture." @@ -567,7 +567,7 @@ { "Fixture": "v3-no-source-metadata.sql", "ProvenanceSHA256": "42013f814ed39dbbcf8bc8e6465777d628a8d38694713f59caaeb30e89f28f68", - "Signature": "sha256:a7b05ddcb786f8d633fd2f27b19e0274fdf78dbd5c0e5ef420cecc831bcc3a8f", + "Signature": "sha256:37d4cebbdcf6c5d43d2bf58b28a864c9434943709bd0975037377be6f1de1082", "AppliedVersion": 3, "HasSourceMetadata": false, "Provenance": "No manifest release tag maps to this partially migrated V3 compatibility triangulation fixture." @@ -575,7 +575,7 @@ { "Fixture": "v5-without-source-metadata.sql", "ProvenanceSHA256": "fb705645b2f77017f1e9f512ba32246cefb9981cde76f78cd929f2659fdfee84", - "Signature": "sha256:95c1c0aa96f1093511dd4e159ec3b574aacdfc67136531b2fb9c3cd01e90aad0", + "Signature": "sha256:f9aebeb59d28378d0a08737123ceb1fcb29e85aa374cb5445f9a73411649d1db", "AppliedVersion": 5, "HasSourceMetadata": false, "Provenance": "No manifest release tag maps to this partially migrated V5 compatibility triangulation fixture." @@ -583,7 +583,7 @@ { "Fixture": "v6.sql", "ProvenanceSHA256": "a42b23b541cc83d34ebd5571f0d7ef7771a540fe3d7e71b1a3e537a5e5932af0", - "Signature": "sha256:68f237fe4a85c97df36522ebdd54703afb97ea84c3f0b0cd45e6400c5e95e220", + "Signature": "sha256:d88079c35be727d03874f8e827655c5e6568df369d528338d0f77aecceef5e09", "AppliedVersion": 6, "HasSourceMetadata": false, "Provenance": "No manifest release tag maps to this partial legacy per-step V6-before-V7 lineage fixture; no release shipped V6 alone." @@ -591,15 +591,15 @@ { "Fixture": "v13-development-alter-built.sql", "ProvenanceSHA256": "20766795c6e1cc8196310f9146298161791aef9ba6fbc654753ec2c1cd2fed7d", - "Signature": "sha256:952e517fe590b0c75a02996b6035ac6bb22143b9a707b6448ad05a592bf015b4", + "Signature": "sha256:4d04377754986f0da2f61f2ab73889168f596eb84e1f02df96e23072072e9375", "AppliedVersion": 13, "HasSourceMetadata": false, "Provenance": "Explicit observed pre-release V13 development lineage produced by the historical ALTER migration path with the original V9 checksum; schema-only fixture with no user data." }, { "Fixture": "v13-legacy-alter-built.sql", - "ProvenanceSHA256": "b2c09d4fb99893464e99a0d5b990ef84a23d7a46ecefc924ed5820eacc7b86ca", - "Signature": "sha256:19d65765b8b0d0bb7d1c41692712c395627e005b7aeccca5f887c75be781e314", + "ProvenanceSHA256": "c592cfec531fc37577a6deddfae7537157406bda1d2a7e890398c0aeacd6d505", + "Signature": "sha256:6003ed9f20a8379761367f559c7a2e0405a9b56429044d672481b2cce23210ff", "AppliedVersion": 13, "HasSourceMetadata": false, "Provenance": "Explicit known legacy V13 shape reproduced from the ALTER-built current schema produced by the historical migration chain; supported despite conservative full-DDL signing." @@ -607,7 +607,7 @@ { "Fixture": "v13-legacy-existing-schema-migrations.sql", "ProvenanceSHA256": "75d9463f55287704fd861f0147b0b63a6f73c5252f55a561c848dbf09c86b6f0", - "Signature": "sha256:f52b4132322f3addb0fc01304f92ca6a2c2f4706e5ac010c59a5ea594cbd25b2", + "Signature": "sha256:5d6baa68ddeb058a293aa80fce9371098c904b6ebb15321eb5fb5bef5c045adc", "AppliedVersion": 13, "HasSourceMetadata": false, "Provenance": "Explicit known legacy V13 shape produced when compatibility migrations start from a database whose schema_migrations table predated the current SetupSchema formatting; supported despite conservative full-DDL signing." diff --git a/internal/compat/testdata/release-schemas/v13-legacy-alter-built.sql b/internal/compat/testdata/release-schemas/v13-legacy-alter-built.sql index 1a033b0..cf33849 100644 --- a/internal/compat/testdata/release-schemas/v13-legacy-alter-built.sql +++ b/internal/compat/testdata/release-schemas/v13-legacy-alter-built.sql @@ -86,10 +86,7 @@ CREATE TABLE search_items ( timestamp TEXT, uuid TEXT UNIQUE, project TEXT, - content_type TEXT NOT NULL DEFAULT 'text', - extraction_version INTEGER, - was_interrupted INTEGER -); + content_type TEXT NOT NULL DEFAULT 'text', extraction_version INTEGER, was_interrupted INTEGER); CREATE TABLE session_tags ( source_path TEXT NOT NULL, diff --git a/internal/storage/migration_plan_test.go b/internal/storage/migration_plan_test.go index fee1141..69f5796 100644 --- a/internal/storage/migration_plan_test.go +++ b/internal/storage/migration_plan_test.go @@ -1528,3 +1528,44 @@ func onlySnapshot(t *testing.T, dbPath string) string { } return matches[0] } + +// TestMigratedFixtureSignatureIsInCatalog is a regression test for issue #52. +// It verifies that when a V1 fixture is migrated forward through the real +// SetupSchema() code, the resulting database schema signature is recognized by +// the catalog. This prevents cosmetic DDL formatting differences from silently +// ejecting valid schemas from the lineage catalog. +// +// Before the fix to normalizeSQL() (making it whitespace-insensitive), databases +// that were created at V1 and then migrated V8→V13 by published releases would +// produce a signature not in the catalog, causing every operational command to +// reject the database as "unsupported_lineage". This test would have caught that +// regression during development. +func TestMigratedFixtureSignatureIsInCatalog(t *testing.T) { + // Load v1.sql, the earliest released version fixture + dbPath := createFixtureDatabase(t, "v1.sql") + + // Open the fixture and trigger SetupSchema to migrate it all the way to V13 + db, diag, err := OpenCompatible(context.Background(), dbPath) + if err != nil || diag != nil { + t.Fatalf("open compatible error=%v diagnostic=%+v", err, diag) + } + defer func() { _ = db.Close() }() + + // Inspect the resulting schema to get its signature + plan, diag, err := compat.InspectIndex(context.Background(), db.DB()) + if err != nil || diag != nil { + t.Fatalf("inspect index error=%v diagnostic=%+v", err, diag) + } + + // Load the lineage catalog + catalog, err := compat.LoadCatalog() + if err != nil { + t.Fatal(err) + } + + // Assert that the migrated database signature is in the catalog + if !catalog.IsKnownSignature(plan.From.Signature) { + t.Errorf("migrated V1→V13 database has signature %s not in catalog; this was the bug in issue #52", + plan.From.Signature) + } +} diff --git a/internal/storage/recovery_records.go b/internal/storage/recovery_records.go index 37554c2..b92f8cd 100644 --- a/internal/storage/recovery_records.go +++ b/internal/storage/recovery_records.go @@ -34,33 +34,19 @@ func ReadRecoveryInputFromQueryer(ctx context.Context, q compat.Queryer) (compat } func readRecordsForSignature(ctx context.Context, q compat.Queryer, signature string) ([]models.IndexedRecord, *compat.Diagnostic, error) { - switch signature { - case - "sha256:e2f7cd4bd71c964717c00fd67c2b3f396306307f578382c4a7956f83f8555a57", // v1 - "sha256:a4256a6029bbc953df37a12979fc81b1879ef9f8ba28ff5cfe6b78472dee804b", // v2 - "sha256:ed864d83d4c873bd34e0e3708f32a5bf47d65cac8ad55ff04305a81d91e9e22e", // v3 - "sha256:a7b05ddcb786f8d633fd2f27b19e0274fdf78dbd5c0e5ef420cecc831bcc3a8f", // v3 without source_metadata - "sha256:b2bba3cec75bb3f6c697e78882e3924550832e3b5706d013f3f54d32a1c25acd", // v4 - "sha256:ce23ee41f1af6007bd0bde7b020f2cac4c215d23bae456bdfce25b90313c709e", // v5 with source_metadata - "sha256:95c1c0aa96f1093511dd4e159ec3b574aacdfc67136531b2fb9c3cd01e90aad0", // v5 without source_metadata - "sha256:68f237fe4a85c97df36522ebdd54703afb97ea84c3f0b0cd45e6400c5e95e220", // v6 - "sha256:dcaa1205df039fa545aa7ebe672d391acfcbf651869c2603ceef12dab9de00d2", // v7 - "sha256:6d503881ebac89e009644df5bf24cf7c4b1afed334afb37e6ee87d2ca6edae87", // v8 - "sha256:e41ae857c862ffa10516681b65bcd8c755484b338a6847dad5c0d770a202670f", // v9 - "sha256:8e28ce0fe1c5f3cd36f2d64ac7a7996f032e66c0c32468a0a206eb983491388c", // v10 - "sha256:975656bb5e894e12bd30aa65bb3366ca2bdeee23f59aca8e133b18a62e7ffad5", // v11 - "sha256:ff136d58048d69f02be857f632750ef3e2daa35fd6ba637f7645986950f83d31", // v12 - "sha256:ef52be2fb56acd0e51b38506746fa3594ed1de76bb0b5dd180767d3c903fb46d", // v13 canonical - "sha256:952e517fe590b0c75a02996b6035ac6bb22143b9a707b6448ad05a592bf015b4", // v13 observed development ALTER-built alias - "sha256:19d65765b8b0d0bb7d1c41692712c395627e005b7aeccca5f887c75be781e314", // v13 legacy ALTER-built alias - "sha256:f52b4132322f3addb0fc01304f92ca6a2c2f4706e5ac010c59a5ea594cbd25b2": // v13 legacy schema_migrations alias - return readCanonicalSearchItems(ctx, q) - default: + catalog, err := compat.LoadCatalog() + if err != nil { + return nil, nil, fmt.Errorf("load lineage catalog: %w", err) + } + + if !catalog.IsKnownSignature(signature) { return nil, &compat.Diagnostic{ Code: compat.CodeUnsupportedLineage, Summary: fmt.Sprintf("unsupported index schema %s", signature), }, nil } + + return readCanonicalSearchItems(ctx, q) } func readCanonicalSearchItems(ctx context.Context, q compat.Queryer) ([]models.IndexedRecord, *compat.Diagnostic, error) {