Stop --dry-run creating the index tables, and report them in db stats - #358
Merged
Conversation
… db stats The knowledge base's three tables are built lazily against the `getDb()` connection instead of through `createStorage`, so neither dry-run guard reached them: no `ReadOnlyTabularStorage` wrapper in the path, and this module's one `isDryRun()` protected the `kb_index` row write several lines below the three `CREATE TABLE`s. `sec --dry-run ask` created all three on a database that had none — DDL from a command whose whole promise is to change nothing. A dry run against an index that already exists is the ordinary case and still reads it. One that would have to build the index now refuses and names `sec index`, rather than quietly building it. The same bypass had a second consequence the issue documents: `db stats` derives its rows from the storage registry, these three are not in it, and there is no repository token to count them through — so an operator had no way to see whether an index existed or how large it was. `getKbTableStats` counts them off the connection directly and the report appends them: `n/a` before the index is built, the same signal a registered-but-uncreated table reports, and omitted entirely where the index cannot exist (a non-SQLite backend, or before the SQLite location is bound — one unreadable appendix must not cost the operator every row count above it). Registry adoption, which would retire the exception outright, is left open on the issue: `defineStorage` cannot yet express a vector storage at a fixed width. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LWp6Z6wvAPDaDCjAFcTSj6
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.
Closes #352.
The problem
The knowledge base's three tables are built lazily against the
getDb()connection instead of throughcreateStorage, so neither dry-run guard reached them: noReadOnlyTabularStoragewrapper in the path, and this module's oneisDryRun()protected thekb_indexrow write several lines below the threeCREATE TABLEs.sec --dry-run askcreated all three on a database that had none — DDL from a command whose whole promise is to change nothing.Changes
The guard.
getSecKnowledgeBasenow checksisDryRun()beforesetupDatabase(). A dry run against an index that already exists is the ordinary case and still reads it; one that would have to build the index refuses and namessec index, rather than quietly building it.Existence is read off
sqlite_masterrather than by probing each storage — aSELECTagainst a missing table throws, and telling "no such table" from a real failure by its message is a guess this can simply avoid.db statsreports the index. The same bypass had a second consequence the issue documents: the report derives its rows from the storage registry, these three are not in it, and there is no repository token to count them through — so an operator had no way to see whether an index existed or how large it was.getKbTableStatscounts them off the connection and the report appends them.Three cases it handles deliberately:
rows: null, the samen/asignal a registered-but-uncreated table reports.getSecKnowledgeBaserefuses Postgres by name, so three permanentn/arows would read as a setup gap an operator could close, and there is nothing to close.DbStatsTasksuite, which went red:getDb()would otherwise open a database from an unset folder token, and one unreadable appendix must not cost the operator every row count above it.What is deliberately left open
Registry adoption (the issue's option 2) would retire the exception outright, but
defineStoragecannot yet express a vector storage at a fixed width. That stays on the issue — this closes the dry-run defect and the visibility gap, not the contract exception.Verification
Tests written first: the two "creates no table" assertions were red against the old code, reproducing the issue exactly, while the "already exists" and "not a dry run" cases passed — confirming only the missing-table path was broken.
format-check,lint,typecheckclean. 11/11 insecKnowledgeBase.test.ts, 4/4 in the newkbTableStats.test.ts, and 49/49 acrosssrc/web,src/task/db,src/kb,setupAllDatabasesandresetAllDatabases.Merge note
Conflicts with
claude/p1-sec-347-release-flooronCHANGELOG.mdonly — both append to the same### Changedlist. Resolution is to keep both bullet sets; no code overlap.🤖 Generated with Claude Code
https://claude.ai/code/session_01LWp6Z6wvAPDaDCjAFcTSj6
Generated by Claude Code