fix(knowledge): the guard did not apply on the way in, so the store never cleaned - #235
Merged
Merged
Conversation
…ever cleaned Deploying the guard was not enough, and measuring after the deploy is how that was found. A full re-index ran in production WITH the guard live — 9 981 files detected, 8 644 parsed, graph rebuilt to 25 491 symbols — and project_cache.knowledge_json, written at 01:00:16, still held 48 implausible names: CASCADE CURRENT_TIMESTAMP GET IF Our SI 0 200 1328 ases bs … The casing and the bare integers gave it away: the guard rejects all of those on write, so they were not produced. They were LOADED. `ProjectKnowledge.from_json` restored table_usage with no filter, and `_incremental_update` carried the cache forward the same way, so a name recorded before the guard existed came back every run, was re-saved, and outlived any number of clean extractions. Clearing it would have needed a manual purge nobody schedules. The guard applies on both boundaries now. One run cleans the store. Verified: loading 13 names — 10 noise, 3 real — yields exactly the three real ones, and a real name survives a round trip with its readers intact. Second change — the map is part of a repo index, not an ingestion automation: `auto_sync_after_index` defaults ON and leaves the ingestion-automation family. That family's rule is "nothing calls out on a schedule unasked" and the flag never qualified: code_db_sync_pipeline.py has zero references to adapter, connector, execute_query, introspect, httpx or aiohttp. The sync opens no connection to anything — its inputs are the stored DbIndex and the code knowledge, both local, and its output is the code↔DB map, which is what a repo index produces. The mis-grouping's consequence appeared the moment it was tested: unset on 2026-08-25 with eight genuine ingestion flags, it meant the re-index above rebuilt the graph while the map kept the previous night's updated_at. The flag stays, for the reason the old grouping never named: the sync runs an LLM (CodeDbSyncAnalyzer), so it costs tokens per run. One of the new tests fails if the sync ever grows an outward call, because that would make the classification wrong again. Production has the flag unset, so the new default applies without a config change and `make config-drift` stays clean. The config_drift fixture that listed it among the ten 2026-08-23 divergences now records WHY it left rather than being edited silently. Suite: 6922 passed, 4 skipped, 1 xfailed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XtEZboEDbEXLYrLf1owRXB
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.
Follow-up to #234, found by measuring production after the deploy rather than assuming the fix landed.
Deploying the guard was not enough
A full re-index ran with the guard live — 9 981 files detected, 8 644 parsed, graph rebuilt to 25 491 symbols — and
project_cache.knowledge_json, written at 01:00:16, still held 48 implausible names:The casing and the bare integers gave it away. The guard rejects every one of those on write, so they were not produced — they were loaded.
ProjectKnowledge.from_jsonrestoredtable_usagewith no filter, and_incremental_updatecarried the cache forward the same way. A name recorded before the guard existed came back on every run, got re-saved, and outlived any number of clean extractions. Clearing it would have needed a manual purge nobody schedules.The guard applies on both boundaries now, so one run cleans the store. Verified: loading 13 names — 10 noise, 3 real — yields exactly the three real ones, and a real name survives a round trip with its
readersintact.The map is part of a repo index, not an ingestion automation
auto_sync_after_indexdefaults ON and leaves the ingestion-automation family.That family's rule is nothing calls out on a schedule unasked, and the flag never qualified:
The sync opens no connection to anything. Its inputs are the stored
DbIndexand the code knowledge, both already local; its output is the code↔DB map, which is what a repo index produces.The mis-grouping's consequence appeared the moment it was tested: unset on 2026-08-25 alongside eight genuine ingestion flags, it meant the re-index above rebuilt the graph while the map kept the previous night's
updated_at. "The index ran and the map did not" is the wrong default for a product whose value is that map's freshness.The flag stays, for the reason the old grouping never named. The sync runs an LLM (
CodeDbSyncAnalyzer), so it costs tokens per run — turning it off now switches off a cost rather than a phantom outward call. One of the new tests fails if the sync ever grows an outward call, because that would make the classification wrong again.Production has the flag unset, so the new default applies with no config change and
make config-driftstays clean.The ratchet earned its keep again
Flipping the default turned
test_the_ten_that_were_found_are_reported_as_driftred — correctly, sinceAUTO_SYNC_AFTER_INDEX=trueis now agreement rather than divergence. The fixture records why the flag left the set, and asserts the default isTrue, so if it ever goes back toFalsethis test is the thing that says the flag is drift again.Suite:
6922 passed, 4 skipped, 1 xfailed.🤖 Generated with Claude Code
https://claude.ai/code/session_01XtEZboEDbEXLYrLf1owRXB