Final embedding campaign report: 20260626T023243¶
Archive status — 2026-10-07¶
This is a historical report. Measurements and decisions describe the original
run and revision; recommendations and commands are not current operating
instructions. Path corrections below change navigation, not measurements.
The original ignored report remains unchanged at .artifacts/analysis/2026-06-27-final-embedding-campaign-20260626T023243-report.md
(SHA-256 3eabb4e4aa6b2a542ee6757057752fa8961a8773a1289a46794d8cdb3e347635).
Some original evidence is unavailable in this checkout. The missing paths are listed below; those observations have not been independently revalidated. Do not treat this archive as a fully reproducible current benchmark.
See the report archive index for coverage and remaining gaps.
| Artifact or input reference | Current availability |
|---|---|
.artifacts/benchmarks/backend/20260618T095148Z-bk-cpp-duckdb |
available |
.artifacts/benchmarks/backend/20260618T095148Z-bk-cpp-duckdb/*-hyperfine.json |
available pattern |
.artifacts/benchmarks/backend/20260618T095148Z-bk-cpp-duckdb/*-index-phases.json |
available pattern |
.artifacts/benchmarks/backend/20260618T095148Z-bk-cpp-duckdb/campaign-plan.json |
available |
.artifacts/benchmarks/backend/20260618T095148Z-bk-cpp-duckdb/failure-summary.json |
available |
.artifacts/benchmarks/backend/20260618T095148Z-bk-cpp-sqlite |
available |
.artifacts/benchmarks/backend/20260618T095148Z-bk-cpp-sqlite/*-hyperfine.json |
available pattern |
.artifacts/benchmarks/backend/20260618T095148Z-bk-cpp-sqlite/*-index-phases.json |
available pattern |
.artifacts/benchmarks/backend/20260618T095148Z-bk-cpp-sqlite/campaign-plan.json |
available |
.artifacts/final-embedding-model-campaign/20260626T023243 |
unavailable historical path |
.artifacts/final-embedding-model-campaign/20260626T023243/...current-minilm |
illustrative path |
.artifacts/final-embedding-model-campaign/20260626T023243/README.md |
unavailable historical path |
.artifacts/final-embedding-model-campaign/20260626T023243/campaigns/*/*-hyperfine.json |
unavailable historical pattern |
.artifacts/final-embedding-model-campaign/20260626T023243/campaigns/*/*-index-phases.json |
unavailable historical pattern |
.artifacts/final-embedding-model-campaign/20260626T023243/campaigns/*/failure-summary.json |
unavailable historical pattern |
.artifacts/final-embedding-model-campaign/20260626T023243/checkpoints/index.tsv |
unavailable historical path |
.artifacts/final-embedding-model-campaign/20260626T023243/checkpoints/labels.txt |
unavailable historical path |
.artifacts/final-embedding-model-campaign/20260626T023243/configs/*.toml |
unavailable historical pattern |
.artifacts/final-embedding-model-campaign/20260626T023243/logs/*.log |
unavailable historical pattern |
.artifacts/final-embedding-model-campaign/20260626T023243/metadata/environment.txt |
unavailable historical path |
benchmarks/embedding/model-candidates.json |
available |
benchmarks/embedding/uv-backed-repos.local.json |
available |
Resolved directory moves:
benchmarks/embedding-model-candidates.json→benchmarks/embedding/model-candidates.json..artifacts/20260618T095148Z-bk-cpp-duckdb→.artifacts/benchmarks/backend/20260618T095148Z-bk-cpp-duckdb..artifacts/20260618T095148Z-bk-cpp-sqlite→.artifacts/benchmarks/backend/20260618T095148Z-bk-cpp-sqlite.benchmarks/uv-backed-repos.local.json→benchmarks/embedding/uv-backed-repos.local.json.
Date: 2026-06-27
Campaign root:
.artifacts/final-embedding-model-campaign/20260626T023243
This report replaces the earlier incorrect analysis that used the incomplete
retry campaign 20260627T201446.
Executive Summary¶
Campaign 20260626T023243 is the completed DuckDB embedding-model campaign.
It ran all 6 configured embedding models across all 4 repositories, producing
24 successful checkpoints and 0 failure-summary entries.
Top-level result:
- Best average full-index Hyperfine time:
bge-small-en-v1.5-onnx, 263.277s. - Best average instrumented full-index phase wall time:
current-minilm-sentence-transformers, 584.137s. - Best average
ctx:bge-small-en-v1.5-onnx, 3.701s. - Best average
emb:bge-small-en-v1.5-onnx, 1.468s. - ONNX is clearly faster than SentenceTransformers for query-time
ctxandembin the paired BGE and Nomic comparisons. - Full-index time does not show the same clean ONNX advantage; for BGE the ONNX phase timings are slower than SentenceTransformers despite similar Hyperfine full-index means, while for Nomic ONNX is slightly better than SentenceTransformers in instrumented full-index phase time.
For real interactive use, bge-small-en-v1.5-onnx is the strongest result in
this campaign because it combines the fastest ctx, fastest emb, and best
average full-index Hyperfine time. For repeated full indexing only,
current-minilm-sentence-transformers remains attractive because its
instrumented full-index phase wall time and embedding diagnostic time are the
lowest.
Campaign Identity¶
Launch surface recorded by the campaign:
Repository manifest: benchmarks/embedding/uv-backed-repos.local.json
Model manifest: benchmarks/embedding/model-candidates.json
Backend mode: duckdb
Runs: 5
Warmup: 1
Environment:
BACKEND_MODE=duckdb
RUNS=5
WARMUP=1
STAMP=20260626T023243
PYTHON=.venv/bin/python
CODIRA=.venv/bin/codira
Artifact parity:
checkpoints/labels.txt: 24 rows
checkpoints/index.tsv: 24 data rows plus header
failure-summary.json files: 24
failure_count: 0 for every campaign subrun
backend: duckdb for every checkpoint
git_commit: 50ce7784cb636c540aa7af1a2e515d865eb56a0c for every subrun
codira_version: 1.44.6.post1.dev37 for every subrun
Relevant plugin versions captured in the artifacts:
codira-backend-duckdb: 1.49.1
codira-backend-sqlite: 1.45.0
codira-embedding-onnx: 1.0.1
codira-embedding-sentence-transformers: 1.0.1
codira-vector-store-duckdb: 1.0.1
codira-vector-store-sqlite: 1.0.1
Frozen Model Configs¶
All models used DuckDB for both structural backend and vector store.
| Model id | Engine | Dimension | Batch size | max_text_chars | ONNX max_tokens | ONNX threads |
|---|---|---|---|---|---|---|
| current-minilm-sentence-transformers | sentence-transformers | 384 | 32 | 0 | 512 | 0/0 |
| bge-small-en-v1.5-sentence-transformers | sentence-transformers | 384 | 32 | 0 | 512 | 0/0 |
| bge-small-en-v1.5-onnx | onnx | 384 | 8 | 0 | 512 | 0/0 |
| nomic-embed-text-v1.5-sentence-transformers | sentence-transformers | 768 | 1 | 2000 | 512 | 4/1 |
| nomic-embed-text-v1.5-onnx | onnx | 768 | 1 | 2000 | 512 | 4/1 |
| jina-embeddings-v2-base-code-onnx | onnx | 768 | 1 | 2000 | 512 | 4/1 |
The 768-dimensional models used the conservative memory-safe campaign settings:
batch_size = 1, max_text_chars = 2000, and ONNX intra_op_num_threads = 4,
inter_op_num_threads = 1.
Average Results by Model¶
Hyperfine means and instrumented phase timings, averaged over the 4 repositories. Seconds unless otherwise noted.
| Model | Full index | Warm index | ctx | emb | Phase total | Phase embeddings |
|---|---|---|---|---|---|---|
| bge-small-en-v1.5-onnx | 263.277 | 0.710 | 3.701 | 1.468 | 677.275 | 114.792 |
| current-minilm-sentence-transformers | 263.551 | 0.697 | 8.811 | 5.116 | 584.137 | 35.700 |
| bge-small-en-v1.5-sentence-transformers | 264.887 | 0.696 | 7.313 | 5.150 | 635.170 | 79.676 |
| nomic-embed-text-v1.5-sentence-transformers | 470.014 | 0.722 | 9.072 | 6.863 | 1311.704 | 284.108 |
| nomic-embed-text-v1.5-onnx | 470.427 | 0.729 | 4.687 | 2.347 | 1263.179 | 270.234 |
| jina-embeddings-v2-base-code-onnx | 474.944 | 0.729 | 4.504 | 2.489 | 1251.365 | 247.670 |
Interpretation:
- Warm structural indexing is effectively flat across models, around 0.70-0.73s, because it is mostly not embedding-bound.
- 384-dimensional models are far cheaper to fully index than 768-dimensional models.
- ONNX materially improves query-time embedding commands (
ctx,emb). - MiniLM has the lowest instrumented embedding diagnostic time, but its
ctxandemblatency are much worse than ONNX.
Per-Repository Top-Line Results¶
Hyperfine means by model and repository. Seconds.
codira¶
| Model | Full index | Warm index | ctx | emb |
|---|---|---|---|---|
| current-minilm-sentence-transformers | 423.276 | 0.706 | 13.043 | 5.459 |
| bge-small-en-v1.5-sentence-transformers | 423.009 | 0.707 | 6.965 | 5.477 |
| bge-small-en-v1.5-onnx | 422.188 | 0.722 | 3.366 | 1.802 |
| nomic-embed-text-v1.5-sentence-transformers | 752.492 | 0.715 | 8.631 | 7.129 |
| nomic-embed-text-v1.5-onnx | 757.945 | 0.743 | 4.335 | 2.682 |
| jina-embeddings-v2-base-code-onnx | 762.135 | 0.740 | 4.470 | 2.812 |
fontshow¶
| Model | Full index | Warm index | ctx | emb |
|---|---|---|---|---|
| current-minilm-sentence-transformers | 392.003 | 0.736 | 8.283 | 5.183 |
| bge-small-en-v1.5-sentence-transformers | 395.828 | 0.734 | 8.310 | 5.213 |
| bge-small-en-v1.5-onnx | 392.702 | 0.739 | 4.717 | 1.522 |
| nomic-embed-text-v1.5-sentence-transformers | 697.064 | 0.795 | 10.246 | 7.117 |
| nomic-embed-text-v1.5-onnx | 693.920 | 0.759 | 5.776 | 2.414 |
| jina-embeddings-v2-base-code-onnx | 709.729 | 0.770 | 6.065 | 2.558 |
chatops¶
| Model | Full index | Warm index | ctx | emb |
|---|---|---|---|---|
| current-minilm-sentence-transformers | 121.729 | 0.678 | 6.781 | 4.949 |
| bge-small-en-v1.5-sentence-transformers | 121.241 | 0.678 | 6.789 | 5.009 |
| bge-small-en-v1.5-onnx | 121.214 | 0.688 | 3.149 | 1.318 |
| nomic-embed-text-v1.5-sentence-transformers | 218.292 | 0.694 | 8.563 | 6.668 |
| nomic-embed-text-v1.5-onnx | 219.525 | 0.713 | 4.141 | 2.193 |
| jina-embeddings-v2-base-code-onnx | 217.455 | 0.704 | 3.901 | 2.336 |
sanikey¶
| Model | Full index | Warm index | ctx | emb |
|---|---|---|---|---|
| current-minilm-sentence-transformers | 117.197 | 0.669 | 7.138 | 4.872 |
| bge-small-en-v1.5-sentence-transformers | 119.471 | 0.666 | 7.188 | 4.900 |
| bge-small-en-v1.5-onnx | 117.006 | 0.693 | 3.572 | 1.229 |
| nomic-embed-text-v1.5-sentence-transformers | 212.208 | 0.683 | 8.849 | 6.540 |
| nomic-embed-text-v1.5-onnx | 210.317 | 0.702 | 4.495 | 2.100 |
| jina-embeddings-v2-base-code-onnx | 210.456 | 0.702 | 3.581 | 2.250 |
ONNX versus SentenceTransformers¶
BGE small, 384-dimensional¶
| Metric | SentenceTransformers | ONNX | ONNX / ST |
|---|---|---|---|
| Full index | 264.887 | 263.277 | 0.994 |
| ctx | 7.313 | 3.701 | 0.506 |
| emb | 5.150 | 1.468 | 0.285 |
| Phase total | 635.170 | 677.275 | 1.066 |
| Phase embeddings | 79.676 | 114.792 | 1.441 |
BGE ONNX is effectively tied for full-index Hyperfine mean, much faster for interactive query-time embedding commands, but worse in the instrumented embedding diagnostic phase. That split suggests ONNX startup/runtime behavior helps query commands, while full-index instrumentation and batching still need careful interpretation.
Nomic 768-dimensional¶
| Metric | SentenceTransformers | ONNX | ONNX / ST |
|---|---|---|---|
| Full index | 470.014 | 470.427 | 1.001 |
| ctx | 9.072 | 4.687 | 0.517 |
| emb | 6.863 | 2.347 | 0.342 |
| Phase total | 1311.704 | 1263.179 | 0.963 |
| Phase embeddings | 284.108 | 270.234 | 0.951 |
For Nomic, ONNX preserves full-index performance, slightly improves
instrumented full-index phase timings, and roughly halves ctx while cutting
emb latency by about two thirds.
Runtime Cost Shape¶
Average full-index Hyperfine time separates mainly by dimensionality:
| Family | Average full-index range |
|---|---|
| 384-dimensional MiniLM/BGE | 263-265s |
| 768-dimensional Nomic/Jina | 470-475s |
Average query-time embedding separates mainly by engine:
| Engine/model class | Average emb range |
|---|---|
| 384-dimensional ONNX BGE | 1.468s |
| 768-dimensional ONNX Nomic/Jina | 2.347-2.489s |
| 384-dimensional SentenceTransformers | 5.116-5.150s |
| 768-dimensional SentenceTransformers Nomic | 6.863s |
Average ctx shows the same pattern:
| Model | Average ctx |
|---|---|
| bge-small-en-v1.5-onnx | 3.701s |
| jina-embeddings-v2-base-code-onnx | 4.504s |
| nomic-embed-text-v1.5-onnx | 4.687s |
| bge-small-en-v1.5-sentence-transformers | 7.313s |
| current-minilm-sentence-transformers | 8.811s |
| nomic-embed-text-v1.5-sentence-transformers | 9.072s |
Operational Elapsed Time¶
The outer phase logs confirm the campaign was long but stable. Representative totals:
| Model | codira | fontshow | chatops | sanikey |
|---|---|---|---|---|
| current-minilm-sentence-transformers | 1h 52m 14s | 1h 38m 53s | 32m 22s | 31m 26s |
| bge-small-en-v1.5-sentence-transformers | 1h 49m 50s | 1h 41m 41s | 32m 45s | 32m 01s |
| bge-small-en-v1.5-onnx | 1h 50m 04s | 1h 42m 44s | 31m 46s | 30m 43s |
| nomic-embed-text-v1.5-sentence-transformers | 3h 29m 06s | 3h 10m 03s | 1h 00m 22s | 59m 11s |
| nomic-embed-text-v1.5-onnx | 3h 26m 15s | 3h 05m 34s | 57m 59s | 56m 20s |
| jina-embeddings-v2-base-code-onnx | 3h 26m 11s | 3h 05m 17s | 57m 21s | 56m 07s |
This agrees with the measured cost shape: 768-dimensional models roughly double the campaign cost versus the 384-dimensional group.
Profile Evidence and Caveats¶
Profile summaries exist for every subrun. The same broad pattern appears across the campaign:
- Full-index cost is dominated by embedding generation and vector persistence.
- Query-time
ctxincludes both embedding lookup work and recurring config / plugin/backend resolution costs. - The campaign is DuckDB-only, so no SQLite-vs-DuckDB conclusion can be drawn from this artifact alone.
- The campaign predates the later vector-store DuckDB
executemanyfix and the later--backend bothphase-visibility/config-generation fix. Do not use this run to validate those newer changes.
The phase-timing JSON exposes timings.total and timings.embeddings.
timings.total is wall-clock for the instrumented index pass.
timings.embeddings is a diagnostic hook total. These values must not be added
together.
Comparison with Preceding Comparable Campaigns¶
The directly comparable non-final campaign fragment is:
.artifacts/benchmarks/backend/20260618T095148Z-bk-cpp-duckdb
Manifest check:
20260618T095148Z-bk-cpp-duckdbcontains exactly the four repositories used by the final embedding campaign:codira,fontshow,chatops,sanikey.- Earlier broad
bk-cppcampaigns contain only partial overlap with the final campaign, usuallycodiraandfontshow, and were skipped for this comparison. - The sibling
.artifacts/benchmarks/backend/20260618T095148Z-bk-cpp-sqlitecampaign has the same four repositories and is useful as context, but the direct comparison below uses DuckDB-to-DuckDB.
Parity and drift:
| Field | 20260618 predecessor | 20260626 final MiniLM slice |
|---|---|---|
| Artifact | .artifacts/benchmarks/backend/20260618T095148Z-bk-cpp-duckdb |
.artifacts/final-embedding-model-campaign/20260626T023243/...current-minilm... |
| Backend | DuckDB | DuckDB |
| Repositories | codira, fontshow, chatops, sanikey | codira, fontshow, chatops, sanikey |
| Codira commit | 7df2d1925f46754fe6c64cd9421f220c0c1d96fb |
50ce7784cb636c540aa7af1a2e515d865eb56a0c |
| Codira version | 1.44.4.post1.dev1 |
1.44.6.post1.dev37 |
| DuckDB backend package | 1.48.0 |
1.49.1 |
| Embedding plugins | not separated plugin packages | codira-embedding-* 1.0.1 |
| Vector store plugins | absent | codira-vector-store-duckdb 1.0.1 |
| Batch size metadata | 128 | 32 |
Only the final MiniLM slice is used here because it is the closest model match to the predecessor run. The final campaign additionally measures BGE, Nomic, and Jina variants, but those have no direct predecessor-model counterpart.
MiniLM DuckDB Regression Table¶
Mean timings, seconds. Ratios are final / predecessor.
| Repo | Metric | 20260618 DuckDB | 20260626 MiniLM DuckDB | Ratio |
|---|---|---|---|---|
| codira | full index | 5.594 | 423.276 | 75.67x |
| codira | warm index | 0.458 | 0.706 | 1.54x |
| codira | ctx | 7.066 | 13.043 | 1.85x |
| codira | emb | 4.323 | 5.459 | 1.26x |
| codira | phase total | 69.524 | 944.494 | 13.59x |
| codira | phase embeddings | 64.793 | 67.954 | 1.05x |
| fontshow | full index | 5.054 | 392.003 | 77.57x |
| fontshow | warm index | 0.479 | 0.736 | 1.54x |
| fontshow | ctx | 5.815 | 8.283 | 1.42x |
| fontshow | emb | 4.321 | 5.183 | 1.20x |
| fontshow | phase total | 53.746 | 871.272 | 16.21x |
| fontshow | phase embeddings | 49.741 | 51.425 | 1.03x |
| chatops | full index | 2.577 | 121.729 | 47.23x |
| chatops | warm index | 0.419 | 0.678 | 1.62x |
| chatops | ctx | 5.201 | 6.781 | 1.30x |
| chatops | emb | 4.283 | 4.949 | 1.16x |
| chatops | phase total | 14.259 | 262.024 | 18.38x |
| chatops | phase embeddings | 11.739 | 11.903 | 1.01x |
| sanikey | full index | 2.079 | 117.197 | 56.36x |
| sanikey | warm index | 0.412 | 0.669 | 1.62x |
| sanikey | ctx | 5.255 | 7.138 | 1.36x |
| sanikey | emb | 4.330 | 4.872 | 1.12x |
| sanikey | phase total | 14.371 | 258.760 | 18.01x |
| sanikey | phase embeddings | 12.191 | 11.518 | 0.94x |
Average across the four common repositories:
| Metric | 20260618 DuckDB | 20260626 MiniLM DuckDB | Ratio |
|---|---|---|---|
| full index | 3.826 | 263.551 | 68.89x |
| warm index | 0.442 | 0.697 | 1.58x |
| ctx | 5.835 | 8.811 | 1.51x |
| emb | 4.314 | 5.116 | 1.19x |
| phase total | 37.975 | 584.137 | 15.38x |
| phase embeddings | 34.616 | 35.700 | 1.03x |
| embedding rows | 2,608.5 | 2,755.25 | 1.06x |
The important asymmetry is that embedding diagnostic time barely moved:
phase_embeddings increased only 3.1% on average. Hyperfine full-index time
increased 68.9x, and instrumented phase wall time increased 15.4x. That means
the regression is not explained by the MiniLM model or by the small increase in
embedded rows.
Likely Cause of the Regression¶
The most plausible code-path change between these campaigns is the separated embedding/vector-store architecture:
- The predecessor metadata has no first-party embedding or vector-store plugin packages.
- The final campaign metadata includes
codira-embedding-sentence-transformersandcodira-vector-store-duckdb. - The final frozen config writes vectors to
vector_store = "duckdb". - The final campaign predates the later DuckDB vector-store batching fix; it
used
codira-vector-store-duckdb 1.0.1.
The numbers match that hypothesis. Embedding computation time is almost flat,
but wall-clock full-index time explodes. In the instrumented final codira
MiniLM run, timings.total is 944.494s while timings.embeddings is only
67.954s. The unaccounted wall-clock cost is consistent with slow vector-store
materialization / persistence outside the embedding hook timing. This is the
same area later fixed by replacing row-wise DuckDB vector-store writes with a
batched Arrow-table path.
The predecessor SQLite sibling is also useful context. On the same four-repo
manifest, .artifacts/benchmarks/backend/20260618T095148Z-bk-cpp-sqlite averaged:
| Metric | 20260618 SQLite average |
|---|---|
| full index | 2.331s |
| warm index | 0.362s |
| ctx | 5.499s |
| emb | 4.223s |
| phase total | 36.705s |
| phase embeddings | 34.204s |
That context reinforces that the final campaign's multi-minute full-index times are not normal for this four-repo workload with MiniLM-like embeddings.
What the Predecessor Comparison Says About Software Evolution¶
Costs introduced:
- Full-index rebuilds became dramatically slower in the final campaign's code path.
- Warm structural operations also slowed, but much less severely: about 1.6x for warm index and 1.2-1.5x for embedding/query commands.
- The new plugin/vector-store architecture adds extra runtime surfaces and persistence work that the old campaign did not exercise.
Advantages introduced:
- The final campaign can compare multiple embedding engines and model families in a controlled matrix.
- It records explicit frozen model configs, embedding plugin versions, vector store plugin versions, and per-model backend configuration.
- ONNX query-time latency is much better than SentenceTransformers once the new plugin architecture is available.
Net conclusion:
The architecture evolution enabled the model/engine comparison and exposed a
clear ONNX advantage for interactive queries, but it also introduced a severe
DuckDB vector-store full-index performance regression in the measured
50ce7784... code path. The later DuckDB vector-store batching fix is therefore
not cosmetic; it targets the dominant regression surfaced by this predecessor
comparison. A new post-fix campaign is required before choosing final defaults.
Recommendations¶
- Use
bge-small-en-v1.5-onnxas the leading candidate when interactivectx/emblatency matters. - Keep
current-minilm-sentence-transformersas the conservative baseline for cheapest full-index phase behavior, but do not prefer it for interactive embedding query latency. - Treat 768-dimensional models as significantly more expensive to index. Nomic ONNX is better than Nomic SentenceTransformers for query latency, but its full-index cost is still in the expensive 768-dimensional class.
- Re-run a smaller confirmatory campaign after the DuckDB vector-store write fix if the goal is to choose the final production default. This campaign is complete and useful, but it predates that performance-sensitive code change.
- For a backend comparison, use a newer
--backend bothcampaign after the backend phase split fix; this run is DuckDB-only.
Source Artifacts¶
Primary files inspected:
.artifacts/final-embedding-model-campaign/20260626T023243/README.md
.artifacts/final-embedding-model-campaign/20260626T023243/metadata/environment.txt
.artifacts/final-embedding-model-campaign/20260626T023243/checkpoints/index.tsv
.artifacts/final-embedding-model-campaign/20260626T023243/checkpoints/labels.txt
.artifacts/final-embedding-model-campaign/20260626T023243/configs/*.toml
.artifacts/final-embedding-model-campaign/20260626T023243/campaigns/*/*-hyperfine.json
.artifacts/final-embedding-model-campaign/20260626T023243/campaigns/*/*-index-phases.json
.artifacts/final-embedding-model-campaign/20260626T023243/campaigns/*/failure-summary.json
.artifacts/final-embedding-model-campaign/20260626T023243/logs/*.log
.artifacts/benchmarks/backend/20260618T095148Z-bk-cpp-duckdb/campaign-plan.json
.artifacts/benchmarks/backend/20260618T095148Z-bk-cpp-duckdb/*-hyperfine.json
.artifacts/benchmarks/backend/20260618T095148Z-bk-cpp-duckdb/*-index-phases.json
.artifacts/benchmarks/backend/20260618T095148Z-bk-cpp-duckdb/failure-summary.json
.artifacts/benchmarks/backend/20260618T095148Z-bk-cpp-sqlite/campaign-plan.json
.artifacts/benchmarks/backend/20260618T095148Z-bk-cpp-sqlite/*-hyperfine.json
.artifacts/benchmarks/backend/20260618T095148Z-bk-cpp-sqlite/*-index-phases.json