docs(kb): record findings 31-36, retract the document_id false alarm, add two diagnostics - #899
Merged
DerrickF merged 2 commits intoSep 1, 2026
Conversation
… retract a false alarm Retracts the "known unknown" that an earlier revision listed as the top open risk. `document_id` and `relevance` were reported empty from the facade after promotion, with the inference that the status filter was either not running on the managed path or losing the join key. Both inferences were wrong. Measured against the live dev knowledge base: the raw Retrieve carries `location.customDocumentLocation.id`, `_to_chunk` produces it, and the facade exposes it at `metadata.document_id` with relevance as its exact negation in `distance`. The probe had read two top-level keys that have never existed — `git log -L` on the formatted_results block confirms the four-key shape back to the function's first commit — so the observation said nothing about either backend. The filter is live and did join: it matched DOC-ae5cc5434f2d and kept both chunks because that document reads `complete`. Adds defect 31, the missing `bedrock:StartIngestionJob` grant, fixed in fdf15d2, and generalizes it in §3: three times now a Bedrock grant listing exactly the API the code calls has deployed clean and failed on first real use, because AWS checks an adjacent action name. Also records the structural consequence for §2's local driver — an SSO identity is broader than every Lambda role, so that driver cannot find this class of defect at all. Adds two findings that came out of checking the false alarm, both still open: - 32. Routing exclusivity is enforced only on the consumer's side. The legacy handler has no engine gate in either copy and its S3 notification is still live, so a document added to a promoted knowledge base is indexed twice. Visible in real data on DOC-dc8b65658e29. Every exclusivity test covers the side that works. - 33. `_filter_vectors_by_document_status` opens with `if not doc_ids: return vectors` — one fail-open path in a function that fails closed everywhere else. Also corrects the stale claims that the worker image is undeployed and that the tree has uncommitted work, records what is verified working in dev today (including that a born-managed knowledge base is not implemented — `newDefault` has zero readers), refreshes the jest count to 634, and fixes a duplicate defect number 24 by renumbering the run to 25–33. All §5.x cross-references are internal to this file and were updated with it.
…nostics Records findings 32–36 and the tooling that found them. All but one are fixed in PR #900; §5.33 is the only one from 2026-08-31 still open. The four new entries share a root cause worth stating once: **the two engines were never made exclusive.** The ingestion consumer stands down for a legacy document; nothing made the legacy pipeline stand down for a managed one, and nothing propagated a deletion to the managed engine at all. Double-indexing (32), a status field with two owners (34), and an orphaned managed corpus (36) are three costumes on that one cause. Item 34 is the one to read. Two writers owned `status`, so "ready" was decided by whichever finished last. Observed both ways within an hour: a PDF marked `complete` 65 s before the managed KB could answer for it, and an image-only PDF marked `failed` while the managed KB served it correctly — the latter only ended up correct because the consumer happened to finish second. The generalisable lesson is that any field two components can write needs a stated owner, and nobody chose this race; it appeared because a writer was added beside an old one and the question was never asked. Item 35 is not a defect but a measured capability difference: Bedrock's image extraction works. A pure-diagram curriculum flowchart is permanently `failed` on legacy (`docling_processor.py` sets `do_ocr=False`) and retrievable in 94.5 s on managed, with the vision model's own description in the chunks. Also records that the old bundled `aws` CLI silently omits `mediaExtractionConfiguration` from `get-data-source`, so a field can look unset when it is not — do not conclude from CLI output alone. Item 36 documents the deliberate asymmetry between the two gates, which is the part most likely to be "cleaned up" by someone later: on ingest an unreadable KB record resolves to legacy, on delete it must fail. Same question, opposite answers, because the `DOC#` row is what the fail-closed status filter joins against. Corrects one claim in item 32: an earlier revision said the missing gate affected "both copies" of the handler including the bootstrap asset. The bootstrap copy is a 33-line no-op placeholder that indexes nothing and needs no gate. Adds two read-only diagnostics, documented in §2 with the numbers they produced: - `kb-doc-timings.py` — per-document ingestion timing and which engine did the work, derived from the record rather than guessed. Legacy 30 s vs managed 95 s on the same 132 KB PDF; `INDEXED → retrievable` 0.9 s, independently confirming the evaluation's 0.75–1.03 s. - `kb-compare-engines.py` — one query through both engines, exploiting the `retain` window where both indexes still hold the corpus. Marks each chunk against MAX_CONTEXT_CHARS, which is what makes the comparison honest: ~2,000 characters reach the model, so one or two chunks, so precision@1 is nearly everything. On `CS434` legacy's five chunks sit within 0.056 of each other and its top chunk does not contain the string; managed separates by 0.4988 with the literal match first. Notes that #900 ends the both-indexes trick, so a future A/B needs two assistants or documents predating promotion. Also adds an "engine visibility" item to the work queue: nothing logs which engine served a query, so "is the new one working?" is currently only answerable from the KB record — worth closing before a wide rollout given that this feature's risk profile is silent regressions.
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.
Documentation only —
HANDOFF.mdfor the managed-KB migration. No code.Retracts the top open risk
An earlier revision listed a "known unknown":
document_idandrelevancecoming back empty from the facade after promotion, with the inference that the status filter was either not running on the managed path or losing the join key. Both inferences were wrong. Measured against the live dev knowledge base:Retrievelocation.customDocumentLocation.id = "DOC-ae5cc5434f2d"_to_chunkdocument_id='DOC-ae5cc5434f2d',relevance=0.4226 / 0.1616metadata.document_idcorrect,distance = −0.4226 / −0.1616The probe read
result["document_id"]andresult["relevance"]at the top level. Those keys have never existed —git log -Lon theformatted_results.appendblock shows the same four keys (text,distance,metadata,key) back to the first commit of that function. Reading outside the contract returnsNoneon both backends, so the observation said nothing about the managed path. The filter is live and did join: it matched the document and kept both chunks because thatDOC#record readscomplete.Records defect 31 and generalizes it
The missing
bedrock:StartIngestionJobgrant, fixed in #898'sfdf15d21. Promoted to a §3 constraint because it is the third instance of one pattern: a Bedrock grant listing exactly the API the code calls, deploying clean, failing on first real use, because AWS checks an adjacent action name (TagResource,ListTagsForResource, nowStartIngestionJob).Also records why it survived a migration that had "completed end to end":
run-kb-migration.pyruns under an SSO identity broader than every Lambda role, so it is structurally unable to find this class of defect.Two new findings, both still open
DOC-dc8b65658e29(chunkCount: 8,vectorStoreId: assistants-index). Not a wrong-answer bug — retrieval reads managed only. Every exclusivity test covers the side that already works._filter_vectors_by_document_statusopens withif not doc_ids: return vectors— one fail-open path in a function that fails closed everywhere else.Housekeeping
newDefaulthas zero readers inbackend/src).24by renumbering the run to 25–33. All§5.xcross-references are internal to this file and were updated with it; each now resolves to exactly one item.Second commit: four more findings, and the tooling that found them
Records findings 32–36. All but one are fixed in #900; §5.33 is the only one from 2026-08-31 still open.
They share a root cause worth stating once: the two engines were never made exclusive. The consumer stands down for a legacy document; nothing made the legacy pipeline stand down for a managed one, and nothing propagated a deletion to the managed engine at all. Double-indexing (32), a
statusfield with two owners (34), and an orphaned managed corpus (36) are three costumes on one cause.Item 34 is the one to read. Two writers owned
status, so "ready" was decided by whichever finished last. Observed both ways within an hour — a PDF markedcomplete65 s before the managed KB could answer, and an image-only PDF markedfailedwhile managed served it correctly, which only came out right because the consumer finished second. The generalisable lesson: any field two components can write needs a stated owner. Nobody chose this race; a writer was added beside an old one and the question was never asked.Item 35 is not a defect but a measured capability difference: Bedrock's image extraction works. A pure-diagram curriculum flowchart is permanently
failedon legacy (do_ocr=False) and retrievable in 94.5 s on managed, with the vision model's own description in the chunks. Also records that the old bundledawsCLI omitsmediaExtractionConfigurationfromget-data-source— a field can look unset when it isn't.Item 36 documents the deliberate asymmetry between the two gates, which is the thing most likely to be "cleaned up" later: on ingest an unreadable KB record resolves to legacy; on delete it must fail. Same question, opposite answers, because the
DOC#row is what the fail-closed status filter joins against.Corrects one claim in item 32: an earlier revision said the gate was missing from "both copies" of the handler. The bootstrap copy is a 33-line no-op placeholder that indexes nothing.
Two read-only diagnostics, documented in §2
kb-doc-timings.pyINDEXED → retrievable0.9 s, independently confirming the evaluation's 0.75–1.03 skb-compare-engines.pyretainwindow where both indexes still hold the corpuskb-compare-engines.pymarks each chunk againstMAX_CONTEXT_CHARS, which is what makes the comparison honest: ~2,000 characters reach the model, so one or two chunks, so precision@1 is nearly the whole game. OnCS434, legacy's five chunks sit within 0.056 of each other and its top chunk does not contain the string; managed separates by 0.4988 with the literal match first.Noted in §2: #900 ends that both-indexes trick. Once the legacy pipeline stands down, new uploads land in the managed index only, so a future A/B needs two assistants or documents predating promotion.
Also adds an engine visibility item to the work queue — nothing logs which engine served a query, so "is the new one working?" is currently only answerable from the KB record. Worth closing before a wide rollout, given this feature's risk profile is silent regressions.