Skip to content

Instantly share code, notes, and snippets.

@escherize
Created March 4, 2026 14:42
Show Gist options
  • Select an option

  • Save escherize/94683a9b2362daa9818a207c384c1ef5 to your computer and use it in GitHub Desktop.

Select an option

Save escherize/94683a9b2362daa9818a207c384c1ef5 to your computer and use it in GitHub Desktop.

Upgrade Audit Analysis — 7795a09d5f1

Date: 2026-03-04 Branch: source-replacement Commit: 7795a09d5f1 — "Skip non-field refs in upgrade-field-ref-to-name"


1. Executive Summary

Metric Value
Total cards audited 25,412
Compilable before upgrade 24,754 (97.4%)
Compilable after upgrade 24,754 (97.4%)
Pre-existing broken cards 658 (2.6%)
Regressions 0
Fixes 0
Upgrade errors 600 (2.4% of all cards, 2.42% of compilable cards)
Queries changed by upgrade 11,947 (47.0%)
Queries unchanged 13,465 (53.0%)

The critical safety invariant holds: zero regressions. No card that was compilable became non-compilable after upgrade. The 600 upgrade errors are non-fatal — they throw during the upgrade process but leave the query intact and still compilable (509 of 519 in the largest bucket remained compilable).


2. What Was Tested

The upgrade audit (dev.nocommit.upgrade-audit) runs field-refs/upgrade! on every card in the app-db. For each card it:

  1. Records whether the query compiles before upgrade
  2. Runs upgrade! (converts ID-based field refs → name-based)
  3. Records whether the query compiles after upgrade
  4. Tracks: query-changed?, regressed?, upgrade-error

The upgrade is Phase 1 of source replacement — normalizing field references so they survive source swaps. It converts [:field {} 42][:field {:base-type :type/Text} "COLUMN_NAME"].


3. Error Buckets

Overview

# Bucket Count % of Errors Compilable Before Compilable After Changed Query Root Cause
1 Aggregation ref in viz settings 519 86.5% 509 509 486 Bug in upgrade code
2 Legacy MBQL fails pMBQL conversion 70 11.7% 0 0 0 Pre-existing data quality
3 Nil table metadata during field resolution 9 1.5% 0 0 9 Pre-existing data quality
4 Expression ref dropped by upgrade 1 0.2% 1 1 1 Bug in upgrade code
5 FK target field ID is empty string 1 0.2% 0 0 1 Pre-existing data quality
Total 600 510 510 497

Bucket 1: Aggregation ref in viz settings (519 cards, 86.5%)

Error: Error converting :aggregation reference: no aggregation at index 0

Example: Card 4488 — upgrade-field-ref-to-name in field_refs.clj:45 tries to convert [:aggregation 0] (a legacy aggregation reference) found in visualization settings like pivot_table.column_split. It calls lib.convert/->pMBQL which expects aggregation context to resolve index 0 → UUID, but no aggregation list is provided.

Stacktrace path:

field_refs/upgrade-field-ref-to-name (field_refs.clj:45)
  → lib.convert/->pMBQL (convert.cljc:406)
    → fails: no aggregation at index 0

Impact: Non-fatal. 509 of 519 cards were compilable before AND after. The query itself was upgraded successfully (486 changed); only the viz settings upgrade failed. The error is caught and reported but doesn't prevent the query upgrade from proceeding.

Root cause: upgrade-field-ref-to-name doesn't guard against non-:field refs in viz settings locations. Viz settings like pivot_table.column_split.rows can contain :aggregation refs (e.g., ["ref", ["aggregation", 0]]) and :expression refs alongside :field refs.

Fix: The commit 7795a09d5f1 was intended to fix this by adding (when (= :field (first field-ref)) ...) guard. However, this audit was run at that commit and the error persists for 519 cards. This suggests either:

  • The guard was added but the ->pMBQL conversion happens before the guard check (the legacy format ["aggregation", 0] needs conversion before the :field check can work)
  • The guard is in place but ->pMBQL is called first and throws before reaching the guard

This is the P0 fix target — resolving it eliminates 86.5% of all errors.


Bucket 2: Legacy MBQL fails pMBQL conversion (70 cards, 11.7%)

Error: Query with a :type or :lib/type key, got: {}

Example: Card 1424 — stored with legacy MBQL 4 format: {"type": "query", "database": 1, "query": {...}}. The normalize-query function in transforms.clj tries to convert legacy → pMBQL via lib.query/query-from-legacy-query, which fails because the converted query has invalid references (e.g., join-alias "Product" but no join defined).

Impact: None. All 70 cards are not compilable before the upgrade and remain not compilable after. These are pre-existing broken cards. Zero queries changed.

Root cause: Pre-existing data quality. These are very old cards with legacy MBQL that references joins or features that didn't exist in the format they're stored in. They can't even be loaded into pMBQL format. The upgrade correctly skips them (no query change).

No fix needed in upgrade code — these cards were already broken.


Bucket 3: Nil table metadata during field resolution (9 cards, 1.5%)

Error: Invalid output: ["Valid Table metadata, got: nil"]

Cards: 6594, 3027, 3456, 3673, 6593, 4455, 3322, 3413, 3414

Example: Card 6594 — during upgrade-field-refresolve-field-refreturned-columns, the metadata provider calls lib.metadata/table for a source table and gets nil. This happens when the card references a table that no longer exists in the metadata (deleted, or from a disconnected database).

Stacktrace path:

source_swap/upgrade-field-ref (source_swap.clj:139)
  → field.resolution/resolve-field-ref (resolution.cljc:688)
    → join/returned-columns → stage/visible-columns → lib.metadata/table
      → returns nil → Malli validation fails

Impact: All 9 cards are not compilable before the upgrade. They reference tables that don't exist. Despite the error, the query was changed in all 9 cards — partial upgrade succeeded before hitting the nil table.

Root cause: Pre-existing data quality. The cards reference deleted/inaccessible tables. The resolve-field-ref path doesn't gracefully handle nil table metadata.

Suggested fix: Add nil guard in resolve-field-ref — if table metadata is nil, skip that field ref's upgrade rather than throwing.


Bucket 4: Expression ref dropped by upgrade (1 card)

Error: Invalid :expression reference: no expression named "Original Time Copy"

Card: 14975

Details: This card has 4 expressions: "Big Endian Time", "Tax Rate", "Date Only Time", "Original Time Copy". After upgrade, only 2 expressions remain in the :expressions clause — "Big Endian Time" and "Tax Rate". "Date Only Time" and "Original Time Copy" were dropped, but :fields still references them as [:expression {} "Original Time Copy"].

The dropped expressions were likely deduplicated incorrectly by upgrade-field-refs-in-stage with {:distinct? true} for :expressions. Commit d1e416f5e6a changed this to {:distinct? false}, but this audit ran after that commit and the error persists. This card may have expressions that are structurally identical after stripping namespaced keys, triggering the remaining dedup logic.

Impact: The card was compilable before (true) and after (true) — so despite the error, the query survived. The stage validation catches the dangling expression reference.

Root cause: Bug in upgrade code — expression deduplication. The fix in d1e416f5e6a may not fully cover this case.


Bucket 5: FK target field ID is empty string (1 card)

Error: Invalid output: {:fk-target-field-id ["should be a positive int, got: \"\""]}

Card: 7169

Details: Card 7169 sources from card 7168, which has result_metadata containing :fk-target-field-id "" (empty string instead of a positive integer or nil). When ->card-metadata-column processes this metadata, Malli validation rejects the empty string.

Impact: Card was not compilable before, remains not compilable after.

Root cause: Pre-existing data quality — bad result_metadata in source card 7168. The empty string FK target was written by old code.

No fix needed in upgrade code.


4. Classification Summary

Category Buckets Cards Action
Bug in upgrade code 1, 4 520 Fix required
Pre-existing data quality 2, 3, 5 80 No action (or defensive guard)

By compilability impact

Compilable Before Not Compilable Before
Upgrade error 510 (Bucket 1 + 4) 90 (Buckets 2, 3, 5)
No error 24,244 568

The 510 compilable cards with upgrade errors are the ones that matter — they represent real cards whose viz settings upgrade fails. The 90 non-compilable cards with errors were already broken.


5. The 47% Changed Queries

11,947 of 25,412 cards had their query modified by the upgrade. This means nearly half of all cards had at least one ID-based field ref that was converted to name-based. This is expected — cards created before v56 predominantly use ID-based refs.

The 13,465 unchanged cards are likely:

  • Cards already using name-based refs (created in v56+)
  • Native SQL cards (no MBQL field refs to upgrade)
  • Cards with no field refs (pure aggregations, etc.)

6. Priority Fixes

P0: Fix aggregation ref handling in viz settings upgrade (519 cards)

The upgrade-field-ref-to-name function at field_refs.clj:43-53 needs to skip non-:field refs before attempting ->pMBQL conversion. The current guard added in 7795a09d5f1 may be checking after conversion rather than before.

The fix should:

  1. Check the raw legacy ref format (e.g., ["aggregation", 0]) for non-field types before calling ->pMBQL
  2. Return the ref unchanged for :aggregation and :expression refs

Fixing this one issue eliminates 86.5% of all upgrade errors, bringing the error rate from 2.4% to 0.3%.

P1: Guard nil table metadata in resolve-field-ref (9 cards)

Add a nil check in the field resolution path so that cards referencing deleted tables fail gracefully rather than throwing.

P2: Investigate expression deduplication for card 14975 (1 card)

Check why "Date Only Time" and "Original Time Copy" expressions are being dropped despite {:distinct? false} for expressions.


7. Comparison with Prior Findings

The swap-error-buckets.md and findings/ANALYSIS_7795a09d5f1.md in the source-replacement repo document swap operation findings (Phase 1 + Phase 2 together, tested on 99 cards). This audit is upgrade-only (Phase 1) tested on all 25,412 cards.

Source Scope Cards Phase Error Rate
This audit (upgrade_findings/) Full instance 25,412 Upgrade only 2.4%
findings/ANALYSIS_7795a09d5f1.md Sample 99 Upgrade + Swap 23.2% errored
swap-error-buckets.md Curated errors ~30 Upgrade + Swap N/A (error catalog)

The upgrade phase in isolation is much more reliable than upgrade+swap together. The swap phase introduces additional failure modes (join alias resolution, permission checks, source compatibility assertions) documented in the swap findings.


8. Raw Data

Error card IDs by bucket

Bucket 1 (519 cards): All cards listed in summary.edn :entities-with-upgrade-errors except those in buckets 2-5.

Bucket 2 (70 cards): 1424, 1394, 1426, 1398, 1410, 1414, 1412, 1392, 1404, 1406, 1434, 1438, 1400, 1374, 1364, 1336, 1418, 1416, 1350, 1396, 1436, 1340, 1376, 1342, 1372, 1358, 1386, 1380, 1338, 1344, 1334, 1366, 1368, 1378, 1370, 1348, 1352, 1382, 1384, 1354, 1388, 1362, 1356, 1360, 1346, 1390, 1408, 1402, 1432, 1420, 1430, 1428, 1422, 1238, 902, 705, 702, 646, 426, 418, 347, 1630, 1675, 1685, 1872, 1925, 2086, 2180, 2283, 2556

Bucket 3 (9 cards): 6594, 3027, 3456, 3673, 6593, 4455, 3322, 3413, 3414

Bucket 4 (1 card): 14975

Bucket 5 (1 card): 7169

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment