Select the filings to index in the database, not by sifting headers - #361
Merged
Conversation
`limit` bounded the number of filings INDEXED, and an already-indexed one was skipped without counting toward it. So `--limit 5` against a corpus that is already indexed read every converted document and probed the knowledge base once per row before concluding there was nothing to do — and `ask` builds the index implicitly, so that ran on every question. `selectDocumentsToIndex` makes the set difference an anti-join, the shape `selectFilingsToConvert` already uses for the same question about conversion: `LEFT JOIN kb_document ... WHERE doc_id IS NULL ... LIMIT ?`, with the scope filters (`since` included, which used to be applied after the read) pushed into the query. It asks for one row more than the limit, so `truncated` is something the run observed rather than inferred from having filled the limit exactly. The knowledge-base tables are built lazily by the first command that opens the index, and this runs before that, so a database with converted filings and no index has no table to join against: the join is dropped and every document needs work, which is the answer rather than an error. Documents with no sections are excluded. They embed nothing, so they never enter the knowledge base and would be re-selected on every run — and under a selection-time limit they would spend it on work that cannot happen. `skipped` stays exact through one COUNT over the same join, replacing the unfiltered `count` this task took for its progress denominator. That denominator was wrong anyway: it measured a run indexing three filings out of three hundred candidates at one percent, where the honest denominator is the work selected. The repository fallback consults no knowledge base, because it is reached only where there cannot be one: a non-durable document repository is invisible to `getDb()`, so opening the index there would read a real database the caller never wrote to, and on Postgres the index does not exist at all. 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 #351.
limitbounded the number of filings indexed, and an already-indexed one was skipped without counting toward it:So
--limit 5against a corpus that is already indexed read every converted document and probed the knowledge base once per row before concluding it had nothing to do.askbuilds the index implicitly, so that ran on every question.Take the issue's preferred option, not its fallback
The issue offers an
examinedbudget as a fallback.selectFilingsToConvert.tsalready answers the same question about conversion as an anti-join, three directories away, so this copies that shape rather than inventing a second one.selectDocumentsToIndex:sincemoved into the query in the same change — filtered in the loop, it cost a full read of everything older than the cutoff just to discard it.Four things that are not just the anti-join
truncatedis observed, not inferred. The selector is asked forlimit + 1. Filling the limit exactly no longer reads as "there is more behind this".kb_documentand would be re-selected forever; under a selection-time limit they would also spend it on work that cannot happen.section_countis written in the same transaction as the section rows, so it is the same answer reading them gives.skippedstays exact, through one COUNT over the same join. It is whatsec indexprints and what decides whether it suggests asking a question or converting filings — reporting 0 on a fully indexed corpus would send the operator tosec update documents, which has nothing to do.The progress denominator was wrong, and this fixes it as a side effect
It used
documentRepo.count(scope)— every candidate, including the already-indexed ones — so a run indexing three filings out of three hundred candidates drew at one percent. The honest denominator is the work selected, which is now known before the loop starts. That COUNT is the oneskippedreplaces, so the query count is unchanged.The repository fallback consults no knowledge base
It is reached only where there cannot be one. A non-durable document repository is invisible to
getDb(), so opening the index there would read a real database this caller never wrote to and report the wrong documents as already indexed; on PostgresgetSecKnowledgeBaserefuses by name, so the index does not exist at all. Naming the candidates and letting the caller fail where the knowledge base itself refuses is the honest behaviour for both.Verified
selectDocumentsToIndex.sqlite.test.ts. The discriminating ones were watched failing first — removing thesection_count > 0guard turns that case red; thelimitcase fails against the old counting.getDocumentis never called per row, andskippedstill reports 3 on a fully indexed corpus.format-check,lint,tsc --noEmit, andsrc/task src/kb src/cli src/config(648 passed, 21 skipped) all clean.🤖 Generated with Claude Code
https://claude.ai/code/session_01LWp6Z6wvAPDaDCjAFcTSj6
Generated by Claude Code