Skip to content

test: static schema-drift gate against the v1.8.0 setup.sql (GIT-109 step 2) - #46

Merged
1 commit merged into
mainfrom
test/GIT-109-schema-drift
Sep 22, 2026
Merged

1 commit merged into
mainfrom
test/GIT-109-schema-drift

Conversation

@nTEG-dev

Copy link
Copy Markdown
Member

What

A static gate: nothing src/ reads or writes may be missing from the v1.8.0 setup.sql unless it's allowlisted against the ticket that owns it (GIT-109 step 2).

  • tests/helpers/schema-drift.ts does two things.

    • Schema: parses setup.sql into table → columns, covering CREATE TABLE, ALTER … ADD COLUMN and views.
    • Code: walks src/ with the TypeScript compiler API, collecting:
      • writes: table + payload keys reaching directUpsert, directPatch, upsertRecord, storage.upsert;
      • reads: table + select / filter / order keys reaching directQuery, directQueryAll, listRecords, getRecord.

    It follows getTableName("x"), const indirection, spreads and conditional spreads (...(ok && {col})), later row.col = … assignments, local functions' return {…}, and .map / .filter element shapes. Code under if (!hasSupabase()), the else of if (hasSupabase()), or a …Free function writes local JSON and is set aside. Anything it can't resolve is reported as unresolved, never assumed fine.

  • tests/fixtures/schema/setup-v1.8.0.sql: the floor, byte-identical to git show v1.8.0:schema/setup.sql. Findings are labelled "added after 1.8.0" or "in no setup.sql" by comparing with the current schema/setup.sql.

  • tests/unit/schema-drift.test.ts fails on:

    • a finding that isn't allowlisted;
    • an allowlist entry that no longer matches, so the list only shrinks;
    • an entry without a GIT-### ticket;
    • a new unresolved call site, so coverage can't quietly decay;
    • a SESSION_COLUMNS entry that v1.8.0 lacks and that isn't production-only. That covers session_close's payload, which is built dynamically.

    It also plants drift in a synthetic source tree every run, to prove the analyzer still catches each kind.

First run: everything it found

74 call sites, 362 column accesses; 7 free-tier-only sites set aside; 6 pass-through helpers (the table is a parameter: storage.ts ×3, store-columns.ts, supabase-client.ts ×2; their callers are checked).

11 findings, all in no setup.sql at all (none were added after 1.8.0):

Table / column Kind Where Why it's allowed
gitmem_learnings.archived_at write archive-learning.ts:132 GIT-109: probe-guarded, written only if supportedColumns() finds it
gitmem_sessions.claude_code_session_id write session-close.ts:1470 GIT-109: production-only, dropped by filterToStoreSessionColumns()
gitmem_sessions.task_observations write absorb-observations.ts:75 GIT-109: probe-guarded
variant_assignments (table) read variant-assignment.ts:100, 139 GIT-106: dev tier only (hasVariants())
variant_performance_metrics (table) write recall.ts:607 GIT-106
scar_enforcement_variants.active filter + write variant-assignment.ts:69, variant-generation.ts:322 GIT-106
scar_enforcement_variants.description, .enforcement_config, .variant_name, .variant_version write variant-generation.ts:322 GIT-106

Worth knowing: setup.sql creates scar_enforcement_variants, but with a different shape (variant_type, variant_config, metadata) from the one the code reads and writes. It's unreachable on pro and free (GIT-106), but if variants ever leave the dev tier, the table setup.sql provisions won't work with them.

No other drift. Every other table and column reached from src/ exists in v1.8.0.

Unresolved (5 sites; the test pins them, so a new one fails):

  • thread-supabase.ts:393, 433, 821: filters come from buildScopedThreadQuery() in another file (thread-scope tests pin them);
  • write-health.ts:72: loops over getTableName("learnings" | "decisions") and selects id only;
  • session-close.ts:1470: sessionData is narrowed to SESSION_COLUMNS, which the last check compares with v1.8.0.

All Release C branches scanned too (#37–#45): each has exactly the same 11 findings and no new unresolved sites. None of this release's fixes reaches a column a v1.8.0 store lacks.

How to test

npx vitest run tests/unit/schema-drift.test.ts: 9/9. Full suite 1283/1283 (+9).

Negative control on real code: adding drift_probe_column: 1 to create_decision's payload fails the gate with gitmem_decisions.drift_probe_column [write] (in no setup.sql) at src/tools/create-decision.ts:87. Reverted.

Venue: not applicable. This is a static test; nothing runs against a store.

Checklist

  • npm run test:unit passes (1283)
  • Planted-drift self-test and a real negative control
  • No schema change
  • Docs: n/a

Breaking changes

None. Test only.

🤖 Generated with Claude Code

…step 2)

Customers never run SQL after initial setup, so every table and column
gitmem touches must exist in the v1.8.0 schema or be deliberately guarded.

- tests/helpers/schema-drift.ts parses setup.sql into table -> columns
  (CREATE TABLE, ALTER ... ADD COLUMN, views) and walks src/ with the
  TypeScript compiler API. It collects table + payload keys reaching
  directUpsert / directPatch / upsertRecord / storage.upsert, and select /
  filter / order keys reaching directQuery / directQueryAll / listRecords /
  getRecord. It follows getTableName(), const indirection, spreads,
  conditional spreads, later x.prop = assignments, local functions' returns
  and .map/.filter element shapes. Free-tier-only code is set aside;
  anything it cannot resolve is reported, never assumed fine.
- tests/fixtures/schema/setup-v1.8.0.sql is the floor, byte-identical to
  `git show v1.8.0:schema/setup.sql`.
- schema-drift.test.ts fails on a finding not allowlisted by ticket, a stale
  allowlist entry, an entry without a GIT-### ticket, a new unresolved call
  site, or a SESSION_COLUMNS entry v1.8.0 lacks that is not production-only.
  A synthetic source tree with planted drift proves the analyzer still
  catches it.

First run: 74 call sites, 362 column accesses, 11 findings — 3 probe-guarded
production-only columns (GIT-109) and 8 variant tables/columns reachable only
on the dev tier (GIT-106). No other drift. +9 tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@nTEG-dev nTEG-dev closed this pull request by merging all changes into main in f93c3a3 Sep 22, 2026
@nTEG-dev
nTEG-dev deleted the test/GIT-109-schema-drift branch September 23, 2026 05:49
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.

1 participant