Skip to content

fix(knowledge): matched was an opinion, and eleven tables that do not exist got it - #236

Merged
sshlg merged 2 commits into
mainfrom
fix/matched-requires-both-sides
Aug 27, 2026
Merged

fix(knowledge): matched was an opinion, and eleven tables that do not exist got it#236
sshlg merged 2 commits into
mainfrom
fix/matched-requires-both-sides

Conversation

@sshlg

@sshlg sshlg commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

sync_status is the word a customer reads on the code↔DB screen. matched says your code and your database agree about this table.

Measured in production, 2026-08-27:

db_index rows for  bs  ls  os  zes  active  unlimit  esim
                   clones  interfaces  price  contract      →  0

code_db_sync status for those same eleven                   →  matched, all of them

Eleven tables that are not in the database were reported as agreeing with it.

The mechanism, not a guess

_match_tables builds the code-only tail with _make_matched(nm, "", …)db_context empty, because there is no DB side. That struct goes to the LLM, which returns a sync_status.

The deterministic SYNC-L5 override fires only when both column sets are non-empty — which a table with no DB side can never satisfy. So effective_status = analysis.sync_status stood, and the model's answer became the customer's fact.

The order that replaces it

resolve_sync_status():

  1. Structure. No DB side → code_only. No code side → db_only. A missing side is a fact; no model can overturn it and none should be asked to.
  2. SYNC-L5. The column sets settle matched versus mismatch.
  3. The model — only where both sides exist with columns unknown on one of them. That is where its reading is the only reading available.

The fix takes judgement away from where it does not belong and leaves it where it does. Verified on the real names:

bs ls os zes active unlimit esim clones interfaces price contract
  LLM said matched  →  code_only

a genuine pair, code-only column present   →  mismatch
a genuine pair, columns agree              →  matched

Tests

23, with the eleven production names verbatim. It never raises: resolve_sync_status runs inside the sync pipeline, and losing a table's row to an exception is worse than keeping a status that is merely uncertain.

Suite: 6945 passed, 4 skipped, 1 xfailed.

This is the third defect in this feature found in one sitting — the other two being invented table names (#234) and a guard that did not apply on load (#235). All three were found by measuring production rather than reading the code.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XtEZboEDbEXLYrLf1owRXB

NGHTBOY and others added 2 commits August 27, 2026 10:52
…ot exist got it

`sync_status` is the word a customer reads on the code↔DB screen. `matched` says "your
code and your database agree about this table". Measured in production 2026-08-27:

  db_index rows for  bs ls os zes active unlimit esim clones
                     interfaces price contract               →  0
  code_db_sync status for those same eleven                  →  matched, all of them

Eleven tables that are not in the database were reported as agreeing with it.

The mechanism, not a guess. `_match_tables` builds the code-only tail with
`_make_matched(nm, "", …)` — db_context empty, because there is no DB side. That struct
goes to the LLM, which returns a sync_status. The deterministic SYNC-L5 override fires
only when BOTH column sets are non-empty, which a table with no DB side can never satisfy,
so `effective_status = analysis.sync_status` stood and the model's answer became the
customer's fact.

`resolve_sync_status()` decides in order:

1. structure — no DB side → code_only, no code side → db_only. A missing side is a fact;
   no model can overturn it and none should be asked to
2. SYNC-L5 — the column sets settle matched vs mismatch
3. the model — only where both sides exist with columns unknown on one of them, which is
   where its reading is the only reading available

Judgement is removed from where it does not belong and kept where it does: a genuine pair
with a code-only column still resolves to mismatch, a clean pair still to matched, both
without asking.

23 tests, the eleven names among them verbatim. It never raises — it runs inside the sync
pipeline, and losing a table's row to an exception is worse than an uncertain status.

Suite: 6945 passed, 4 skipped, 1 xfailed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XtEZboEDbEXLYrLf1owRXB
CI's Format check is a separate step from `ruff check`, and I ran only the latter before
pushing. The gate caught it in 54 seconds, which is the gate working.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XtEZboEDbEXLYrLf1owRXB
@sshlg
sshlg merged commit 6531013 into main Aug 27, 2026
2 checks passed
@sshlg
sshlg deleted the fix/matched-requires-both-sides branch August 27, 2026 09:09
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