- Task: 3.3
- Evaluated: 2026-09-05
- Bound model:
perplexity/pplx-embed-v1-0.6batperplexity/int8, protocolplain, 1024 dims - Full index: 57,008 rows × 1024 dims (222.7 MB), corpus
cc21e105c22d, chunker79148b760b1f - Member index: 0 rows — empty by construction, loader returns null
- Record digest:
0ca5c36ac6688e69151ca17942d94508a3ae646b7ac9a3a0c4bac5c3cbc52f8b
Why a file beside the corpus#
pgvector on this host cannot compute an L2 or cosine distance even over a three-dimensional literal, and exact cosine over the real corpus size in-process costs tens of milliseconds per query — a fraction of the model turn it feeds. The index therefore lives beside the lexical corpus with the same loader discipline and no runtime service.
pgvector 0.8.2: L1 works = true; L2 failure = stack-depth-limit-exceeded;
cosine failure = stack-depth-limit-exceeded. Observed by
docs/audits/eve-sota-environment-capabilities/2026-09-01.json
(vector.pgvector-operators) and a direct psql reproduction on 2026-09-05:
'[1,2,3]'::vector <-> '[1,2,4]'::vector and <=> both raise "stack depth limit
exceeded" at max_stack_depth 2MB; <+> returns 1.
Trigger for an approximate structure: exact search is O(rows × dims) per query; an order-of-magnitude larger corpus, or a query latency budget below the measured median, is the point at which an approximate structure earns its complexity (task 3.8 owns the operational half).
The exact lexical corpus boundary#
| Scope | Corpus chunks | Pages | Corpus sha256 | Chunker | Inventory hash | Rows | Vectors | Chunk rows |
|---|---|---|---|---|---|---|---|---|
| member | 0 | 0 | e3b0c44298fc |
79148b760b1f |
3d5f56907f27 |
0 | 0.0 MB e3b0c44298fc |
0 B e3b0c44298fc |
| full | 57,008 | 3,256 | cc21e105c22d |
79148b760b1f |
3d5f56907f27 |
57,008 | 222.7 MB 28481cc95f0a |
7,933,623 B 86d02e94ccb7 |
Row i of a scope's vector matrix is chunk i of that scope's corpus file. The chunker version is derived by the corpus builder from its own chunking source and constants, so a rechunk shows up as a version change rather than as rows that silently stopped lining up.
Disclosure before retrieval#
rows are exactly the chunks of this scope's lexical corpus, which the corpus builder filters by its positive member opt-in (MEMBER_AUDIENCE_PREFIXES); a member index is additionally refused if any href is not audience=member in docs-graph-inventory.json
- Member rows: 0; member violations: 0
- Member loader returns null: true
- Audience filtered before retrieval on every scope: true
- The loader never overlays one scope onto another: true (proved by the spec of that name)
Build, tombstones, freshness#
| Scope | Requests | Tokens | Billed | Duration | Batch | Pace | Retried attempts | Tombstones |
|---|---|---|---|---|---|---|---|---|
| member | 0 | 0 | $0 | 0 s | 100 | 300 ms | 0 | 0 |
| full | 571 | 8,836,447 | $0.03534579 | 527 s | 100 | 300 ms | 0 | not computed — no previous build of this scope is present in the output directory |
Re-check at generation#
- member: fresh; loader null
- full: fresh; loader loaded; self-query top-1 is its own row: true; exact search 42.52 ms median / 61.68 ms max over 7 queries
pnpm --dir apps/oshun/bff exec tsx scripts/build-docs-dense-index.ts --check
exits 0 fresh, 1 stale, 2 corrupt. Stale means the corpus or the registry
binding moved; corrupt means the files no longer match their manifest. The
loader refuses both; the lexical path is the degraded mode.
Persistence#
Manifests are committed; vectors and chunk rows are local-only under these ignore rules:
apps/oshun/bff/src/assistant/generated/docs-dense-index.*.vectors.f32apps/oshun/bff/src/assistant/generated/docs-dense-index.*.chunks.jsonl
Honest limits#
- The full-scope vectors and chunk rows are local-only (gitignored, ~223 MB). A checkout without them records the re-check as not performed, with the reason, instead of implying it passed; the committed manifest still binds the corpus hash, the file hashes and the embedding identity, so a rebuild elsewhere either reproduces the same files or fails --check.
- Nothing serves this index yet. search_docs remains lexical BM25F; task 3.4 decides hybrid fusion and the fail-loud modes for embedding, index, or provider loss. This record proves the index exists, binds what it claims, and searches — not that dense retrieval helps.
- Retrieval quality is unmeasured. The self-query proves a row retrieves itself; task 3.5 owns Recall@k and nDCG over the relevance set against the recorded shortlist, and task 3.7 owns promotion.
- Exact search is a documented approximation of a vector database, not a replacement for one: O(rows × dims) per query, all rows resident in memory, no incremental update. Tombstones are computed only when the previous build is present on the same machine, and the record says when they were not.
- The member index is empty by construction — no page has opted into the member audience — so the disclosure boundary is proved on the empty case and on the loader refusing to overlay scopes, not on a populated member index. If a page ever opts in, the member index must be rebuilt and the audience re-check re-run.
- The bound model's snapshot basis is a vendor version, not a dated slug or a published checkpoint. A re-pointed version invalidates every stored vector; --check catches a registry change but cannot see a silent re-point behind the same slug.
- Latency is the in-process scan on this host over unit-vector queries drawn from the index itself; it excludes the query embedding call, which is a network round trip the pricing record measured at ~200 ms median.