Date: 2026-07-11. Method: three read-only analysis passes over the F*
source and the committed perf docs (storage layer, query-planning layer,
and the VC track are separate reports; VC is in
2026-07-11-vc-canivc-eecc-plan.md).
Every claim here is either verified now (file:line read or grep run
this pass) or cited from a dated doc — the two are kept distinct.
This doc is the strengths / weaknesses / next-phase-goals assessment that
feeds the revised /goal.
Two strategic pressures shape everything below, and the storage and query layers share both:
VIOLATION-SEM/MIXED rows, every README/demo/talk carries the
"on-disk backend has unverified OCaml-side optimization layers being
migrated back to F*" caveat (CLAUDE.md rule #11, epic #200). Storage
and query are the two subsystems that still carry it./goal frames measured, verified performance as the payoff of
completeness. Storage + query is where "verified and fast" is won or
lost — so the perf numbers below are first-class, not footnotes.A third theme emerged from the analysis and ties the two layers together: Roaring and KaRaMeL-stratification are each a single change that unblocks multiple debts — see the unified roadmap (§4).
| Artifact | Magic | Indexes | Verified-F* vs OCaml-shim |
|---|---|---|---|
data.cottas (Parquet base) |
PAR1 |
the quad set | Reader Parquet.Footer.fst = verified Tot. Native writer RDF.CottasStore.BaseWriter.serialize_cottas_v2 (:1076, wired into factoidal import/compact --native-writer at factoidal_cli.ml:1300) = verified Tot, 0 assume val; general round-trip lemma admitted (base case only) |
.dict ×4, .presence ×4, .p.offsets, .po.presence |
COTD/COTP/COTO/COPO |
dictionary / presence / offsets / compound-PO | Byte-format spec in F* with round-trip lemmas; production writer is a hand-written OCaml "Option B" re-implementation (experimental_ocaml_glue/*.sh), checked only by SHA-256 hash-roundtrip on 4–5 fixtures each — the OCaml does not call the extracted F* serialize_* |
data.deltalog / data.compacted-epoch |
DLE1/DLB1/DLOG / CEP1 |
append-only UPDATE log + epoch guard | Framing + merge-on-read: verified Tot (DeltaLog.fst, DeltaMerge.fst), 5 I/O assume vals under #282, realised 4 ways (Unix/C/wasm-IndexedDB/in-mem) |
HDT container/dict/triples (--data-hdt) |
HDT | read-only container | Verified F* (Parser.BallyhooHDT.fst, landed 2026-07-06, shim deleted). Rank/select is naive O(n) (stage 3); indexed stage 5 not started |
Roaring (formal/roaring/) |
— | nothing in production | Pure F* Phases A–D (1,101-line Container.fst); not referenced by any formal/fstar/*.fst — 4 phases from load-bearing |
lemma_delta_entry_roundtrip +
lemma_merge_on_read_matches_apply_entries, plus 270/270 + 25/25 +
25/25 SIGKILL-point clean recoveries.disk-storage-format skill's stale "not wired in" note — §3.3 of the
storage report; an obsolescence-sweep fix)..p.offsets. Bound-S/O lookups prune to a row group then decode it
fully → 2.17s / 4.07s vs Jena TDB2's 1.16–3.88s on the same corpus.
The single clearest "missing index for the slow access pattern."
Partially closed 2026-07-13 for the S half: .s.offsets
(RDF.CottasStore.SubjectOffsetsWriter.fst /
RDF.Store.Columnar.SubjectOffsetIndex.fst) records each subject's
CONTIGUOUS global row range (rows are subject-primary sorted, so one
(start, end) pair per subject is exact — no per-row-group breakdown
needed, unlike .p.offsets). Wired into cottas_ondisk_search_tok's
candidate-rg intersection and cottas_ondisk_count_exact_tok's
bound-subject branch. Measured on the gene corpus (888,949 quads, 8
row groups): q3 subject point lookup 3.15s→2.35s median (old
committed binary/no sidecar vs new binary/with sidecar, 3 runs each,
byte-identical answers) — the win comes from skipping a second,
dict-page-unprunable (DELTA_LENGTH_BYTE_ARRAY-encoded) row group the
old dict-page-probe fallback always included; the row group that
DOES contain the subject is still fully decoded (no partial in-row-
group decode primitive exists — see the O assessment below and the
q6 indexed-decode refutation, docs/claude-rules/current-state.md).
The O half is NOT implemented: BaseWriter sorts (s, p, o, g), so
object values are contiguous only within a fixed (s, p) pair, not
globally — a dense per-object global-range table would be as
impractical as a naive .p.offsets-style matrix at object
cardinality (per this doc's own scaling note). .po.presence
already gives object-side row-group pruning when p is co-bound;
a genuine .o.offsets would need a different structure (e.g.
sorted (o) -> per-rg extents) and is left as a follow-up.BaseWriter.fst:32-38), so native output is 13.90 B/quad vs
pycottas zstd+RLE_DICTIONARY 1.14–1.17 (~12× gap). The reader handles
both codecs (the same commit added the UNCOMPRESSED branch), so native
files are readable by us and DuckDB — but pycottas/DuckDB stays the
import path when small files matter, until a zstd (or the in-flight
RLE_DICTIONARY v2) encoder lands in F*. "Used" and "emits compressed
bytes" are independent: it's the first, not yet the second.DictWriter/PresenceWriter round-trip lemmas are base-case-only
(ADMITTED general case; not a rule-#10 breach — no --lax, just tests
cover the inductive case).RDF.CottasInMem.fst scaffold.Two parallel evaluator stacks: Stack A in-memory
(SPARQL11.Algebra.fst, graph_store over RDF.Indexed) and Stack B
backend-neutral (SPARQL11.Store.fst over the store_caps capability
seam in RDF.Store.Capabilities.fst), the latter being the production
path for COTTAS/HDT. Join ordering (choose_best_tp(_backend)) is
cost-aware and adaptive — re-estimates live selectivity per output row
and peels the cheapest pattern — and joins use a real hash join (build
side by post-hoc length) since 2026-07-06.
factoidal-explain now calls the real
choose_best_tp_backend (divergent shadow deleted in ae1b912).SPARQL.Plan.* modules are unwired dead code.
Plan.Estimate/Pruning/AccessPath have zero production
callers; RDF.CottasStore.fst carries its own inline duplicate of
the identical cardinality formula (:2174 and :2274). The recovery
plan's "one reusable F* module per capability" half didn't happen even
though the OCaml-shadow half did — a single-source-of-truth debt,
distinct from (and smaller than) a rule-#11 violation.VIOLATION-SEM — cottas_ondisk_runtime.sh
(#118) + cottas_ondisk_z_lazy_open.sh (#254), ~1,384 lines of
unverified OCaml, kept alive only by three non-production consumers
(a unit test, the smoketest binary, factoidal_explain.ml's encode
side) still calling id-based entry points. The live query path already
bypasses them via the _tok entry points.SPARQL11.Algebra/SPARQL11.Store do not extract through KaRaMeL
— monomorphization stack-overflow; blocks 16 of 18 C/wasm-blocked
modules including the join executor itself, SPARQL.Plan.Explain/ Streamable, SPARQL.HTTP.RunQuery, SPARQL11.Parser. The query
execution engine is the one piece of "verified SPARQL" that can't
yet reach C/wasm.GP_Join gets no reordering at all (only the hash build side is
chosen, post-hoc)._tok. Neither is on the live
path; both block the qualifier drop (epic #200).SPARQL11.Algebra unblocks 16 modules across both
layers. Prioritize the shared-leverage items..s.offsets/.o.offsets,
mirroring .p.offsets — the OffsetsWriter already generalizes to any
column). Closes the clearest perf gap (2.17s/4.07s → toward Jena's
1.16–3.88s band) with an already-proven spec/writer/test pattern.
S half landed 2026-07-13 (§1.3 item 1 has the measurement + the
contiguity finding that made the S format simpler than .p.offsets).
O half assessed and NOT implemented — objects aren't globally
contiguous under the (s,p,o,g) sort, so the same dense-range
structure doesn't apply; see the same section for the follow-up
shape.
Storage, highest single perf item.SPARQL11.Algebra/SPARQL11.Store (split
evaluator from datatypes per the fstar-module-style roadmap).
Unblocks 16 modules incl. the join executor for C/wasm — the largest
single KaRaMeL blocker. Query, highest reach item._tok. Removes ~1,384 lines of unverified OCaml and the qualifier
blockers for these subsystems. Both.SPARQL.Plan.Estimate/ Pruning/AccessPath into RDF.CottasStore.fst (delete the inline
duplicate at :2174/:2274), then replace uniform-density with a
distinct-value-based estimate off the on-disk dictionaries + a
join-selectivity term. Query; also fixes the dead-code debt.DictWriter/PresenceWriter round-trip
lemmas; profile the 13× GROUP BY residual to a specific function
before optimizing it; HDT stage 5 after Roaring (#2) lands.Sequencing note: #2 (Roaring E) precedes #1's index if the offset sidecar is to share Roaring's popcount core, and precedes #7's HDT stage 5. #3 (stratification) is independent and can run in parallel. #4 is low-engineering-risk and directly advances the qualifier drop.