Use when building, extending, or reviewing tensor-grep's APPROVED local hybrid semantic search — BM25 + CPU dense embeddings fused with Reciprocal Rank Fusion (RRF), no API key, no GPU (roadmap item #1). Load before adding a dense/embedding leg, RRF fusion, a `tg index` command, or changing `tg search --rank` / `--bm25`. Covers the decision-gated build phases with exact commands + expected gate numbers, the ranked solution menu (Semble / ripvec / BM25-only) with derivation obligations, the retrieval-quality + editor-plane + token-economy promotion gates, the Backend Fail-Closed Contract for the dense leg, fenced-off wrong paths (no API-key embeddings, no GPU dependency, do not break `--format rg` / `--json` / `--ndjson` semantics), and routing promotion through change-control. STATUS as of 2026-07-08, v1.49.3: the dense leg + RRF fusion described here as the target architecture (Candidate 1 / the Semble pattern) SHIPPED as `tg search --semantic` (`retrieval_dense.py` + `retrieval_fusion.py`, default-OFF, gate
tensor-grep — Local Hybrid Semantic Search Campaign
A decision-gated runbook for building the APPROVED local hybrid semantic search
layer: BM25 (lexical) + a CPU dense-embedding leg, fused with Reciprocal Rank
Fusion (RRF), 100% local, no API key, no GPU. This is roadmap item #1
(grep -n "the #1 validated user ask" AGENTS.md; was :561-562 at v1.96.0, now :1738 at v1.101.27 — cite the grep, not the number) — the #1 validated user
ask and the biggest competitive gap.
This skill is the campaign map. It tells you what already exists, what you are
building, the exact commands + expected numbers at each gate, the wrong paths that
are fenced off, and how promotion routes through change-control. You do not ship
anything user-visible from this skill without beating the gate and doing a conscious
flag-flip (see Phase 5).
STATUS UPDATE (2026-07-08, v1.49.3): SHIPPED. The dense leg + RRF fusion this
skill specifies as the target architecture (Candidate 1, the Semble pattern) is now
live: src/tensor_grep/core/retrieval_dense.py (model2vec + potion-code-16M,
DenseUnavailableError/BackendExecutionError fail-closed contract exactly as §6
specifies) and src/tensor_grep/core/retrieval_fusion.py
(reciprocal_rank_fusion(rankings, k=DEFAULT_K=60) — matches §3/§5 exactly), wired
as tg search --semantic (grep -n '"--semantic"' src/tensor_grep/cli/main.py; was main.py:7069, now main.py:7331 typer option, default False; bootstrap
front doors at bootstrap.py:70 [_TG_ONLY_SEARCH_FLAGS, keeps it off the
rg-passthrough] and bootstrap.py:478 [_can_delegate_to_native_tg_search's
unsupported_flags, keeps it off native-Rust delegation]) gated on the optional
semantic extra (pyproject.toml:627, model2vec>=0.5+numpy>=1.26; all four
citations re-verified 2026-07-24 against v1.96.0). Sections 1-2 below still
describe the PRE-BUILD state and are now WRONG on the "does not exist yet" claims —
read them as historical design intent, not current fact. No tg index command
was added (the persisted-index building blocks in semantic_index.py remain
unwired, per the original §1 note). RESOLVED (2026-07-24, re-verified against
v1.96.0): --semantic has NOT graduated past default-OFF —
SearchConfig.semantic_rank: bool = False (config.py:188) and AGENTS.md's own
Roadmap Sequencing section still describes the shipped win as "default-OFF"
(grep -n "the #1 validated user ask" AGENTS.md; was :561-562, now :1738). Still not re-verified: whether Phase 4's promotion gate
(RRF-hybrid beats BM25-only on a real corpus + editor-plane latency) was actually
measured before the ORIGINAL ship — that is a historical-PR question this pass did
not chase down; if you need it, check the --semantic flag's introducing PR for
attached benchmark evidence. If you are extending this further (chunking, a
tg index command, a default-flip), Phases 4-5 below are still the right runbook.
Two further opt-in refinements SHIPPED since (both default-OFF, additive):TG_CHUNKER=structural (PR #443, 9015238, shipped v1.47.0) -- cAST AST-shaped
chunking (retrieval_chunker.py, CHUNKER_MODE_ENV_VAR) beside the fixed-window
chunker, fail-open, chunk-shape-identical contract, index-version-bumped
(semantic_index.py v2 folds the active chunker mode into its cache key). This
retires the old "cAST is a candidate deepener, not a requirement for v1" framing in
S3 Candidate 1 below -- it shipped as an explicit opt-in, not a requirement, but it is
no longer merely a future candidate. TG_RRF_CHANNELS=1 (PR #442, a402f81, shipped
v1.46.0) -- channelized RRF (reranker.py, _RRF_CHANNELS_ENV): weighted per-channel
fusion including a 1.5x path/filename channel, additive weights param, default-off
and byte-identical when unset.
(Superseded note, kept for history: as of 2026-07-05/v1.40.2 this was still unbuilt
and tensor-grep-large-repo-scale-campaign was the live campaign instead — that is
no longer the case for the dense/RRF leg specifically; re-check which campaign is
"live" at the time you read this.)
STATUS UPDATE 2 (2026-07-16, v1.77.0-v1.78.1, campaign #189): the architecture
GRADUATED into a standalone whole-repo command, tg find. Where --semantic
re-ranks an EXISTING regex match set, tg find walks and ranks the WHOLE repo (no
pattern pre-filter), reusing the same retrieval_dense.py/retrieval_fusion.py
core. It shipped its own golden harness, benchmarks/eval_late_rerank_quality.py
(a 40-query NL vocab-mismatch golden set + literal/identifier golden slices,
superseding this skill's toy eval_bm25_quality.py as the Phase-4-style
discriminating gate for tg find specifically) — gate-run result: rrf beats
bm25 by +0.195 ndcg@10 / +0.30 recall@10, bidirectional-oracle-validated
(internal; public numbers stay CEO-gated #72). Two related dispositions are:
(1) MaxSim late rerank (TG_LATE_RERANK)
is RETIRED (task F10): the post-role-aware-encoder measurement was decisively negative;
retrieval_late.py records the retirement. Do not re-run the same encoder expecting a
different verdict. (2) TG_FIND_DENSE_WEIGHT (see tensor-grep-config-and-flags) now
adaptively applies 5.0 to multi-word queries when unset, empty, malformed, or non-finite;
single-token queries remain 1.0, and explicit TG_FIND_DENSE_WEIGHT=1.0 opts out.
Receipt (real-corpus-dogfood-beats-fixture-green): the query
classifier that scopes TG_FIND_DENSE_WEIGHT to multi-word queries was originally a
split_terms() morpheme-count floor (> 2 morphemes = NL); it passed its synthetic
literal-golden fixture but a real-repo dogfood on tensor-grep's own src/ caught it
mis-boosting 5 of 6 literal identifier queries (_confine_mcp_path, getUserName,
reciprocal_rank_fusion all split into 3+ morphemes) — fixed by switching to a
whitespace word-count gate (len(query.split()) <= 1 stays literal), #191/#630.
See tensor-grep-run-and-operate §1/§7/§11c for the CLI/MCP command surface and
exit contract, and the dedicated operator skill tensor-grep-find-and-route for the
day-to-day tg find/tg route-test CUJ (this skill stays the BUILD/campaign history; that one is
the how-to-run doc).
STATUS UPDATE 3 (2026-07-21, research campaign #251) — cAST structural chunking REJECTED as
default; do not re-propose it.TG_CHUNKER=structural (shipped v1.47.0, mentioned above as an
opt-in refinement) was evaluated as a candidate for the DEFAULT chunker on a real-corpus retrieval
eval: the retrieval-quality delta was a net WASH, while cAST chunking ran 24.4x SLOWER and
produced ~38% LARGER chunks than the shipped line-window chunk_file. The opt-in code remains
shipped for experimentation; it is not promoted, and this is now a documented retirement in
docs/PAPER.md §3.10 — do not re-run this experiment expecting a different verdict without new
evidence. Use the right harness when re-measuring anything chunker-sensitive:benchmarks/eval_late_rerank_quality.py (live — imports and calls chunk_file/chunk_file_structural)
is the correct instrument; benchmarks/run_repo_retrieval_benchmarks.py is a static-fixture
REPLAY that never calls chunk_file at all — it cannot detect a chunker regression or improvement
and was mistakenly cited as the Phase-4 discriminating gate earlier in this skill (§0/Phase 4 below
now point at the correct script).
One-line caution (do not re-propose): dense-embedding compression (int8/binary/PCA) was
evaluated and DEFERRED — memory-only win (3.79x smaller on disk), but ~2x SLOWER in numpy (no
int8 SIMD on the CPU-only path this campaign targets); a real speed win needs a native kernel
(banked as a moat-investment option, not a quick follow-up here).
Sibling hardening, not a semantic-search change:#699/A7 hardened the SIBLING flat lexical
scorer (_score_symbol in repo_map.py — exact word-boundary bonus + test-file demotion), which
is a DIFFERENT scorer from this campaign's BM25/dense/RRF stack (see
code-search-and-retrieval-reference §3) — do not conflate the two when reading a "ranking fixed"
claim.
STATUS UPDATE 4 (2026-08-12 retention pass; verified against v1.110.14, base 568065a): the
TG_FIND_DENSE_WEIGHT claim in STATUS UPDATE 2 item (2) above — "default-OFF (1.0 =
byte-identical no-op)" with the flip "a separate, still-open CEO checkpoint" — is FALSE at this
SHA. The flip SHIPPED (#191/#634, commit c1d4ba4); the CEO checkpoint is closed by it.
Verified resolution rule (grep -n "_FIND_DENSE_WEIGHT_ADAPTIVE_DEFAULT" src/tensor_grep/cli/main.py — = 5.0 at main.py:4614; reader _find_dense_weight at
main.py:4627-4684): unset / empty / malformed / non-finite env → the adaptive
_FIND_DENSE_WEIGHT_ADAPTIVE_DEFAULT (5.0, the ledger-swept 1:5 bm25:dense ratio) for genuinely
multi-word NL queries; a single whitespace-free token stays pinned at _FIND_DENSE_WEIGHT_DEFAULT
(1.0) regardless of the env var's state; explicit TG_FIND_DENSE_WEIGHT=1.0 is the opt-out back
to the old equal-weight fusion (any other finite value, e.g. =3.0, is honored verbatim).
Evidence (grep -n "dense-weight flip" CHANGELOG.md): NL ndcg@10 0.3047->0.4466, zero
per-category regression. Version note (verified, do not propagate the mislabel): the
v1.93.4-era CHANGELOG #712 entry calls this "the v1.93.2 dense-weight flip", but
git tag --contains c1d4ba4 puts the flip commit's FIRST release at v1.79.0 (its own
CHANGELOG entry sits under ## v1.79.0 (2026-07-16)) — the v1.93.2 label in that later entry is
a mislabel. The old item-(2) text above is kept as dated history: it was accurate for the
#628/#630 flip-prep era and is closed by the flip.
SUPERSEDED (same 2026-08-12 pass): STATUS UPDATE 2 item (1)'s MaxSim / TG_LATE_RERANK
"re-run the tg find gate" instruction is RETIRED — do NOT re-run. The module docstring is the
authority: src/tensor_grep/core/retrieval_late.py:4-16 (note: core/, not cli/) records
"RETIRED 2026-08-05 (task F10)". The post-role-aware-encoder-fix re-measurement HAPPENED and was
decisively negative: ndcg@10 0.068 vs plain RRF 0.305 (docs/PAPER.md:469), root cause model
capacity (the 17M-param int8 LateOn-Code-edge model's raw MaxSim ranking is statistically
indistinguishable from random on in-repo code) — NOT the encoder wiring, so "re-flipping the same
encoder will not change the verdict." The module is kept in place, not deleted (the
core/reranker.py wiring is too load-bearing to touch for a demand-gated feature), reachable only
behind the undocumented TG_LATE_RERANK=1 env var with no tg-command install path. Reopen only
on BOTH a real tg-command install path AND a different encoder clearing the design doc's T8
golden-set thresholds — never off a partial win on either alone.
0. When to use this skill — and when to use a sibling instead
Use this skill when the task is the hybrid-retrieval build itself: adding a dense
leg, RRF fusion, a persisted hybrid index, a tg index command, or measurably
improving tg search --rank.
If you actually need to…
Use this sibling instead
Understand the front door / routing / registration sites / backend contract
tensor-grep-architecture-contract
Get merge/release/experimental-flag gates + the incidents behind them
Learn a settled battle (FFI reverts, dep caps, mock-vs-real) so you don't re-fight it
tensor-grep-failure-archaeology
Build/run the toolchain (uv, maturin, cargo)
tensor-grep-build-and-env
The local validation gate + QA
tensor-grep-validation-and-qa
Update README/AGENTS/docs after shipping
tensor-grep-docs-and-writing
Position the feature externally (never "faster grep")
tensor-grep-release-and-positioning
This skill never routes around change-control. Promotion is a change-control
decision (Phase 5); this skill only produces the evidence that decision needs.
1. What already exists (the BM25 lexical leg is SHIPPED)
2026-07-08 correction: this section (and §2 below) was written when only the BM25
leg existed. The dense leg (retrieval_dense.py) and RRF fusion
(retrieval_fusion.py) have SHIPPED since — see the STATUS box above. Treat every
"does not exist yet" statement below as describing the pre-2026-07-0x state, not
current fact; re-verify with the grep in the STATUS box before relying on it.
Read these before writing a line. Every path below is verified against the repo as
of v1.17.25.
File
What it does
Load-bearing facts
src/tensor_grep/core/retrieval_chunker.py
Splits files into line-window chunks
chunk_file(chunk_size=30, overlap=5); step = max(1, chunk_size-overlap) = 25; MAX_CHUNKS=100_000 loud guard (raises, never silent OOM). Per-chunk granularity is what the design council settled on (not per-line, not per-file).
src/tensor_grep/core/retrieval_lexical.py
Tokenizer + bare overlap counter
split_terms() is camelCase/underscore/hyphen aware, lowercased. This is the shared tokenizer — the dense leg MUST tokenize identically or scores diverge.
src/tensor_grep/core/retrieval_bm25.py
Okapi BM25 over chunks
Bm25Index, k1=1.5, b=0.75, IDF with +1 smoothing (non-negative weights). Dedupes query terms so a repeated token isn't double-counted. Returns [(chunk_index, score)], zero-score chunks excluded, ties break by chunk index (deterministic).
src/tensor_grep/core/reranker.py
The LIVE tg search --rank path
rerank_by_bm25(result, query, file_paths) re-orders matches by the best BM25 score of the chunk containing each match; stable sort (ties keep grep order); non-scoring matches sink. Builds the BM25 index in memory every call over just the matched files — no persisted index.
src/tensor_grep/core/semantic_index.py
Persisted chunk-BM25 index building blocks
build_and_save / load_or_warn under .tg_semantic_index/ (env TG_SEMANTIC_INDEX_DIR), SEPARATE from the Rust TGI v3 .tg_index (trigram). INDEX_VERSION=2 (re-verified 2026-07-24 against v1.96.0; bumped from 1 when the structural chunker folded its mode into the cache key, per the "Two further opt-in refinements" note above). Stale check = SHA-256 fingerprint over sorted paths + mtimes → on mismatch, warn to stderr + return None → in-memory fallback. NOT wired to the CLI — there is no tg index command yet (still confirmed absent).
tg install-dense (CLI command, v1.91.0)
One-shot dense-leg setup
Installs the semantic extra (model2vec+numpy, torch-free) via the same uv tool → uv pip → pip cascade tg upgrade uses, then fetches the checksum-pinned potion-code-16M model; fails closed (non-zero exit, no partial model directory) on any pip/network/checksum failure — never a silent half-installed state. Every dense-absent hint across the CLI (tg search --semantic, tg find's rank_fallback_reason) now leads with tg install-dense (v1.93.0/#705) instead of a bare "pip install the extra" instruction.
src/tensor_grep/core/retrieval_scoring.py
Metrics
recall_at_k, precision_at_k, mean_reciprocal_rank_at_k, ndcg_at_k, f1_score, RetrievalMetrics. These are the promotion yardsticks — use them, don't invent new ones.
How --rank is wired (verify before changing):
Flag: --rank (alias --bm25), default OFF. SearchConfig.rank_bm25 = False (config.py:183, re-verified 2026-07-24 against v1.96.0). The dense leg's own flag sits right below it: SearchConfig.semantic_rank = False (config.py:188).
It is a TG-only search flag: bootstrap.py::_TG_ONLY_SEARCH_FLAGS (--rank line 68, --bm25 line 69, --semantic line 70 — re-verified 2026-07-24) — the bootstrap front door intercepts it and does NOT forward it to ripgrep. This is one of the two flag front doors; see tensor-grep-config-and-flags.
Setting --rankleaves the ripgrep passthrough fast-path: the _can_passthrough_rg() condition includes and not config.rank_bm25 and and not config.semantic_rank (grep -n "not config.rank_bm25\|not config.semantic_rank" src/tensor_grep/cli/main.py; was main.py:5232-5250 at v1.96.0, now main.py:5438-5463 at v1.101.27 — this seam has already drifted twice inside two weeks, cite the grep, not the number), so the request runs the tg engine and results are re-ordered right after match aggregation — the elif config.rank_bm25 and all_results.matches: guard through the rerank_by_bm25(...) call (grep -n "elif config.rank_bm25 and all_results.matches\|rerank_by_bm25" src/tensor_grep/cli/main.py; was main.py:7965-8069; the follow-up :8411-8414 pin then landed INSIDE a --deadline option block deleted by the 2026-08-23 de-duplication, so it has no successor either -- the THIRD drift of this one anchor. Use the grep above and stop pinning it).
User docs: grep -n -- "--rank" README.md (feature bullet :39, example :172-173 as of 2026-08-14; were :38 and :147-148 at the v1.96.0 pass).
Bottom line: the lexical leg (BM25) and the persisted-index building blocks
already exist and ship default-OFF. The campaign adds the **dense leg + RRF fusion
(optionally) a wired persisted hybrid index.**
2. What you are building (the approved architecture)
chunk_file() ──► chunks ──► ┌─ BM25Index.query() ──► ranking A
query ──► split_terms() │
(same tokenizer) └─ dense encode+cosine ──► ranking B
│
RRF fuse(A, B, k=60) ──► final ranking
BM25 leg — exists (retrieval_bm25.py).
Dense leg — DOES NOT EXIST YET. A CPU static-embedding model produces a vector
per chunk and per query; rank chunks by cosine similarity. Static means a per-token
vector lookup (no transformer forward pass at query time) → fast on CPU, no GPU,
no API key, no network at query time.
RRF fusion — DOES NOT EXIST YET. Combine the two rankings without score
normalization: score(d) = Σ_r 1 / (k + rank_r(d)) over the rankers r ∈ {bm25, dense}, with k = 60 (the value the reference architecture uses). A document
absent from a ranker's list contributes 0 for that ranker. RRF is rank-based, so it
is robust to the fact that BM25 scores and cosine scores are on incomparable scales.
SUPERSEDED (was true through v1.40.2, 2026-07-05; false as of v1.49.3, 2026-07-08):no dense/embedding/RRF/Model2Vec/potion code exists in src/ today — this leg has
since shipped as retrieval_dense.py + retrieval_fusion.py; re-run
grep -rin "model2vec|potion|reciprocal_rank_fusion|StaticModel" src/ yourself and
expect real hits, not just comments.
The moat framing (do not lose it): this is not "faster grep." ripgrep is the
raw-text parity baseline. The value is agent-native retrieval quality on
vocabulary-mismatch queries (find authenticate when the user typed "verify login").
Keep the positioning honest per tensor-grep-release-and-positioning.
3. Solution menu (RANKED) with derivation obligations
Pick in this order. Each candidate carries a derivation obligation — a claim you
MUST verify (not assume) before you build on it. "Derive" = confirm against a
primary source (the model card, the license file, a local import test), then record
the finding. Route the research through tensor-grep-research-frontier +
tensor-grep-research-methodology; never trust a self-report (change-control gate B).
Candidate 1 (preferred): the Semble pattern
Tree-sitter chunking + potion-code-16M Model2Vec static embeddings + BM25 + RRF
(k=60). CPU-only, MIT. This is the reference architecture named in AGENTS.md
(grep -n "MinishLab .Semble." AGENTS.md; was :565 at v1.96.0, now :1741 at v1.101.27; the
"## Roadmap Sequencing" heading it sits under was :525, now :1701 — AGENTS.md keeps growing new
sections above these, cite the grep not the number).
Derivation obligations before you depend on it:
License — confirm potion-code-16M (and the model2vec runtime) are
MIT/Apache-compatible with tensor-grep's Apache-2.0 and add the required NOTICE
entries. Ideas are free; imported code/weights need their notices.
Truly offline — confirm the model loads from a bundled/cached file with no
network call and no API key at query time. If it phones home or needs a token,
it is DISQUALIFIED (see §4 fenced paths).
CPU + footprint — confirm it runs with no GPU, and record the on-disk model
size and the added dependency weight. The model must be an OPTIONAL extra, not
a hard install dependency (every-install must still work with BM25-only).
Chunking choice -- RESOLVED, both ship. Semble uses tree-sitter chunks;
tensor-grep already had line-window chunk_file as the default, and now also ships
TG_CHUNKER=structural (PR #443, v1.47.0) -- opt-in cAST AST-shaped chunking
(docs/PAPER.md/arXiv:2506.15655 is the reference paper), fail-open and
chunk-shape-identical to the fixed-window path when unset. This was a candidate
deepener as of the original design; it is now a shipped, default-off refinement --
do not describe it as unbuilt.
Candidate 2: ripvec (pure-Rust)
A pure-Rust vector path. Derivation obligations: confirm license, maturity, and
whether it fits the existing PyO3 bridge without reintroducing the FFI overhead that
was already measured too high and reverted (tensor-grep-failure-archaeology: FFI is
not the dir-scan speed path). Only choose this if Candidate 1 fails a gate AND you
have measured that the Rust path clears the same promotion bar.
Candidate 3: BM25-only (the honest null result)
Ship nothing new. This is a legitimate, non-embarrassing outcome if the dense leg
does not beat the BM25 baseline on both retrieval quality and editor-plane latency.
"No speed/quality claim without measured numbers vs the baseline" (change-control
gate C) cuts both ways: if the numbers aren't there, the correct move is to keep the
shipped --rank baseline and record the negative result. grep -n -- "demonstrably beats" README.md -- :237 as of 2026-08-14 (was :212 at the v1.96.0 pass)
states the rule explicitly: extend lexical
(BM25) re-ranking with AST-shaped chunking or semantic re-ranking only when it
demonstrably beats the shipped tg search --rank baseline on both retrieval quality
and editor-plane benchmarks.
4. Fenced-off wrong paths (do NOT do these)
Forbidden
Why
If you're tempted
API-key / hosted embeddings (OpenAI, Voyage, Cohere, any *_API_KEY)
Breaks "no API key, runs on every install, local-first." The whole point is $0, offline.
Static local model only. If a candidate needs a key or a network call at query time, it's disqualified.
GPU / CUDA dependency for the dense leg
GPU is EXPERIMENTAL, default-OFF, and currently slower than CPU with no promotion-ready path (Roadmap Sequencing Phase 1, "reversible flag-flip, not yet authorized" — no crossover proven, grep -n "reversible flag-flip, not yet authorized" AGENTS.md; was :539-541 at v1.96.0, now :1715 at v1.101.27). A GPU-gated ranking layer would not run on the common install.
CPU static embeddings. GPU may be an optional future accelerator, never a requirement.
Those output contracts are the raw-grep parity surface. --rank is a re-order overlay: same matches, different order. When --rank is NOT set, the ripgrep passthrough fast-path (main.py:5212, _can_passthrough_rg, re-verified 2026-07-24) must remain byte-for-byte.
Keep ranking strictly post-processing over an already-produced SearchResult. Never change match membership or the rg-shaped output when ranking is off.
A hard new install dependency
Every-install must keep working.
Make the dense model an optional extra; degrade to BM25-only when absent (see §6).
Default-OFF flag + benchmark + conscious flag-flip (Phase 5).
Eyeballing "it feels more relevant"
Ranking surfaces silently FLIP on corpus change; the blast radius is invisible to the call graph (known weak point — flat scorer, incident #302).
Measure recall@k / ndcg@k on a real corpus. Numbers or it didn't happen.
Routing around change-control
Non-negotiable.
Produce evidence here; let tensor-grep-change-control gate the flip.
5. The phased runbook (decision-gated)
Run phases in order. Each gate says the expected number and where to branch if
you see something else. All commands are copy-pasteable; PowerShell is the primary
shell on the dev box, but uv run is cross-platform.
uv run gotcha: a bare uv run ... can re-sync and drop the [dev]
tree-sitter/extras tree. For benchmark/import work use uv run --no-sync ... so the
installed dev tree (and any editable install of the dense model) is not wiped. See
tensor-grep-build-and-env.
Phase 0 — Establish the baseline (NEVER skip)
You cannot claim an improvement without the number you improved on.
uv run --no-sync python benchmarks/eval_bm25_quality.py --top-k 3
benchmarks/eval_bm25_quality.py defines V2_GATE_RECALL = 0.60 and states in its
own docstring: "the v2 dense+RRF leg must beat this before it ships user-visible."
GATE 0a: recall@k must be 1.000 here. If it is lower, the BM25 leg itself
regressed → STOP, do not build dense on a broken base → tensor-grep-debugging-playbook.
GATE 0b — read this or you'll waste weeks: this toy corpus is
keyword-discriminating, so BM25 already saturates it at recall 1.0. Passing the
0.60 gate proves nothing about whether dense helps — it is a floor / sanity
gate, not the discriminating gate. The real justification for a dense leg is
vocabulary-mismatch queries (synonyms/paraphrase) where lexical BM25 misses.
Your promotion evidence MUST come from a harder, realistic corpus (Phase 4), not
this file.
Phase 1 — Choose + derive the solution
Work the §3 menu top-down. Complete every derivation obligation for your chosen
candidate and write the findings down (license, offline-proof, CPU-proof,
footprint, optional-extra plan). Research via tensor-grep-research-frontier +
tensor-grep-research-methodology. Before writing code, verify the plan's seam
claims against the real files with file:line citations (verify-plan-against-code) —
an AI-drafted plan that says "add it in pipeline.py" is a hypothesis until you
confirm --rank is actually wired in main.py.
GATE 1: if the preferred candidate fails a derivation obligation (needs a key,
needs a GPU, incompatible license, cannot be an optional extra), do not "work around
it" — drop to the next candidate. If all dense candidates fail, Candidate 3
(BM25-only) is the correct answer; document the negative result and stop.
Phase 2 — Build the dense leg behind a default-OFF experimental flag
Add a new module (mirror the existing seam names, e.g.
core/retrieval_dense.py) — do not bolt onto retrieval_bm25.py.
Reuse split_terms() (or an explicitly justified tokenizer) so the two legs stay
comparable.
Wire it behind a separate default-OFF flag (the config.py:182 note already
anticipates "a separate gated flag" for the dense leg). Do NOT change the meaning of
--rank/--bm25 yet.
Honor the Backend Fail-Closed Contract (§6).
TDD: write the contract test first (tests/unit/test_retrieval_dense.py), then the
smallest implementation. See existing tests tests/unit/test_retrieval_bm25.py,
tests/unit/test_reranker.py, tests/unit/test_semantic_index.py for the pattern.
GATE 2: the dense leg imports and runs with the model absent (degrades to
BM25-only, visibly) AND with it present (produces a ranking). Prove the FFI/model
path against the REAL runtime, not a mock — mock-green while the real bridge is dead
is a documented trap (tensor-grep-failure-archaeology).
Phase 3 — Build RRF fusion
Implement reciprocal_rank_fusion(rankings, k=60) as a pure function taking each
leg's ordered list of chunk indices and returning the fused order. Keep k
configurable (default 60) and deterministic ties.
Fuse ranks, not raw scores (BM25 score vs cosine are incomparable scales — this
is the whole reason RRF is chosen).
GATE 3: unit-test that fusing two identical rankings is a no-op, and that a
document top-ranked by either leg surfaces near the top of the fused list.
Phase 4 — Measure (the real gate)
Two measurements, both required (grep -n -- "demonstrably beats" README.md -- :237 as of 2026-08-14, was :212):
Retrieval quality on a realistic corpus (not the toy). Use
benchmarks/eval_late_rerank_quality.py — the LIVE, chunker/ranking-sensitive harness (it actually
imports and calls chunk_file/rank_chunks, and computes RetrievalMetrics: recall/precision/
mrr/ndcg on a real repo + the 40-query NL golden set). Do not use
benchmarks/run_repo_retrieval_benchmarks.py for this — it is a static-fixture REPLAY that never
calls chunk_file and cannot detect a chunker or dense/RRF-weighting change at all (this was the
cAST-chunking evaluation's own harness-selection mistake before STATUS UPDATE 3 above corrected
it). Produce three rows: BM25-only, dense-only, RRF-hybrid, on the SAME corpus + queries.
uv run --no-sync python benchmarks/eval_late_rerank_quality.py --output artifacts/bench_find_quality.json
(Read its args first; see tensor-grep-benchmark-and-proof-toolkit §7 for the corpus-hardness and
paired win/loss/tie reporting rigor this gate specifically needs.)
Editor-plane latency — the ranking overlay must not blow the interactive
budget:
uv run --no-sync python benchmarks/run_editor_plane_benchmarks.py
Route interpretation through tensor-grep-benchmark-and-proof-toolkit (noise-floor
rule for sub-10ms rows, fair-baseline rule, launcher attribution). Also record the
token-economy delta if the surface feeds the agent capsule (AGENTS.md names
token economy as a focus).
Promotion gate (all must hold, measured vs the SAME baseline run):
Metric
Requirement
recall@k (real corpus)
RRF-hybrid > BM25-only by a margin beyond the noise floor
ndcg@k (real corpus)
RRF-hybrid ≥ BM25-only (no ranking-quality regression)
Editor-plane latency
within the interactive budget; no material regression vs --rank
Token economy (if capsule-facing)
no worse than BM25-only
GATE 4: if RRF-hybrid does not beat BM25-only on retrieval quality AND hold the
line on latency, do not ship it. Reject the regression even if the code is clean
(change-control gate C). The honest outcomes are: (a) improve the dense leg/chunking
and re-measure, or (b) record the negative result and keep BM25-only.
Phase 5 — Promote through change-control (never here)
This skill produces evidence; tensor-grep-change-control owns the flip. The
graduation path is fixed:
Ship experimental, default-OFF (already true after Phase 2).
Attach the Phase 4 evidence (three-row quality table + editor-plane numbers +
token economy) to the PR.
Dry-run on real data (dogfood the REAL binary via scripts/dogfood/; CliRunner
bypasses the bootstrap front door and will not exercise routing).
Conscious flag-flip — a deliberate, reviewed default change, never auto-merged,
never admin-merged. Autonomy is draft-PR-only.
Update docs (README.md, AGENTS.md, the usage skill) via
tensor-grep-docs-and-writing; observe one-merge-per-tick + the push-race rules
(tensor-grep-release-and-positioning).
If you add a tg index command (the natural home for a persisted hybrid index),
remember it needs the 4 command-registration sites (miss one → silent misroute)
and a new flag needs the 2 flag front doors — see tensor-grep-config-and-flags
and tensor-grep-architecture-contract.
6. Backend Fail-Closed Contract for the dense leg
The dense leg is a compute path; it is bound by backends/base.py
(BackendExecutionError) and the AGENTS.md §"Backend Fail-Closed Contract"
contract (grep -n "Backend Fail-Closed Contract" AGENTS.md; was :496 at v1.96.0, now :1672 at
v1.101.27). The recurring anti-pattern to
avoid: a bare except Exception: that silently returns empty or swaps engines.
Model missing / not installed → this is a legitimate degraded fallback to
BM25-only, but it MUST be VISIBLE: set a fallback_reason on the SearchResult
so JSON/CLI consumers can tell degraded output from full hybrid output. Never label
BM25-only output as "semantic."
Model load/encode raises at runtime (corrupt cache, OOM, version skew) → raise
BackendExecutionError; do not return a clean empty result that reads as "no
matches." A real failure reported as a no-match is the exact bug this contract
exists to prevent.
Contract flag the fallback cannot honor → fail closed (raise), do not swap. (For
a ranking overlay a graceful visible degrade to BM25 is the norm; only fail-closed
if a caller explicitly demanded semantic-only and you cannot deliver it.)
Validate the model's output shape (vector dimensionality, chunk count) before you
index, so a mismatch degrades gracefully instead of raising an IndexError that a
broad except then swallows.
See tensor-grep-architecture-contract for the full contract and the
planned SafeBackendMixin conformance gate.
7. Common failure modes → branch
Symptom
Likely cause
Branch to
BM25 baseline recall < 1.0 in Phase 0
BM25/tokenizer/chunker regression
tensor-grep-debugging-playbook
Dense leg "works" in tests but the real binary shows no effect
mock-green while the real path is dead; or --rank not actually re-routing
tensor-grep-failure-archaeology; verify against the real binary via scripts/dogfood/
uv run benchmark can't import the dense model
uv run re-synced away the extra
re-run with uv run --no-sync; tensor-grep-build-and-env
Phase 4 evidence: three-row quality table (BM25 / dense / RRF) on a REAL corpus + editor-plane latency + token economy — RRF beats BM25-only on quality without a latency regression, OR the negative result is documented and you stop.
--json / --ndjson / --format rg unchanged when ranking is off.
Local validation green: uv run ruff check . · uv run ruff format --check --preview . · uv run mypy src/tensor_grep · uv run pytest -q (CI runs ruff format --check --preview — you MUST pass --preview).
Real-binary dogfood, not just CliRunner (scripts/dogfood/).
Promotion routed through tensor-grep-change-control; draft PR only; conscious flag-flip, never auto-merge.
Provenance and maintenance
Everything below is verifiable from the repo. Re-run these when a claim may have
drifted; date-stamp any change.
Version / date: facts originally verified v1.17.25 (2026-07-02); re-verified
UNCHANGED against released v1.40.2 (origin/main 8829441) on 2026-07-05; spot-checked
again 2026-07-08 against v1.49.3 and found the dense/RRF leg now SHIPPED (see STATUS
UPDATE near the top); spot-checked again 2026-07-16 against v1.78.1 and found the
architecture graduated into tg find (see STATUS UPDATE 2 near the top); spot-checked again
2026-07-22 against v1.93.2 and recorded the cAST-chunking rejection + dense-int8 deferral +
install-dense row (see STATUS UPDATE 3 near the top — this was targeted at the research-campaign
#251 retirements and the harness-selection correction, not a full re-walk of Phases 0-8 below).
**Spot-checked again 2026-07-24 against v1.96.0 (origin/main 29cf59f): deleted the
Provenance bullet below that directly contradicted the "Dense leg + RRF now shipped" bullet
(its own grep no longer produces the "expect no hits" result it claimed — 5 real hits in
main.py/config.py/reranker.py/retrieval_dense.py/retrieval_fusion.py); resolved the
open "has --semantic graduated past default-OFF" question (NO — see the STATUS UPDATE box
near the top); re-verified and fixed drifted file:line citations throughout §1/§3/§4/§6/
Provenance (main.py/bootstrap.py/pyproject.toml/README.md/AGENTS.md line numbers had
each drifted anywhere from ~10 to ~1500 lines since the v1.17.25-era pins — main.py's
_can_passthrough_rg moved the most, 3883→5249); fixed INDEX_VERSION=1 → the
now-current INDEX_VERSION=2; and corrected a stale STATUS UPDATE 2 claim (the
retrieval_late.py doc-role-encoder harness gap it cited as blocking TG_LATE_RERANK was
fixed by #189 Item 1 since that note was written; the later F10 measurement retired MaxSim).
This was a targeted re-verification of THIS skill's own claims, not a full re-walk
of every sibling skill or every historical receipt (e.g. the 2026-07-16 +0.195 ndcg@10
gate-run number in STATUS UPDATE 2 is a dated point-in-time receipt, left as-is).
Skill-library drift audit, 2026-08-01, against v1.101.27: every main.py/AGENTS.mdfile:line citation re-grepped again and eve
…(truncated)
1---2name: tensor-grep-semantic-search-campaign3description: Use when building, extending, or reviewing tensor-grep's APPROVED local hybrid semantic search — BM25 + CPU dense embeddings fused with Reciprocal Rank Fusion (RRF), no API key, no GPU (roadmap item #1). Load before adding a dense/embedding leg, RRF fusion, a `tg index` command, or changing `tg search --rank` / `--bm25`. Covers the decision-gated build phases with exact commands + expected gate numbers, the ranked solution menu (Semble / ripvec / BM25-only) with derivation obligations, the retrieval-quality + editor-plane + token-economy promotion gates, the Backend Fail-Closed Contract for the dense leg, fenced-off wrong paths (no API-key embeddings, no GPU dependency, do not break `--format rg` / `--json` / `--ndjson` semantics), and routing promotion through change-control. STATUS as of 2026-07-08, v1.49.3: the dense leg + RRF fusion described here as the target architecture (Candidate 1 / the Semble pattern) SHIPPED as `tg search --semantic` (`retrieval_dense.py` + `retrieval_fusion.py`, default-OFF, gate4---56# tensor-grep — Local Hybrid Semantic Search Campaign78A decision-gated runbook for building the **APPROVED** local hybrid semantic search9layer: **BM25 (lexical) + a CPU dense-embedding leg, fused with Reciprocal Rank10Fusion (RRF), 100% local, no API key, no GPU.** This is roadmap item #111(`grep -n "the #1 validated user ask" AGENTS.md`; was `:561-562` at v1.96.0, now `:1738` at v1.101.27 — cite the grep, not the number) — the #1 validated user12ask and the biggest competitive gap.1314This skill is the campaign map. It tells you what already exists, what you are15building, the exact commands + expected numbers at each gate, the wrong paths that16are fenced off, and how promotion routes through change-control. **You do not ship17anything user-visible from this skill without beating the gate and doing a conscious18flag-flip** (see Phase 5).1920> **STATUS UPDATE (2026-07-08, v1.49.3): SHIPPED.** The dense leg + RRF fusion this21> skill specifies as the target architecture (Candidate 1, the Semble pattern) is now22> live: `src/tensor_grep/core/retrieval_dense.py` (model2vec + `potion-code-16M`,23> `DenseUnavailableError`/`BackendExecutionError` fail-closed contract exactly as §624> specifies) and `src/tensor_grep/core/retrieval_fusion.py`25> (`reciprocal_rank_fusion(rankings, k=DEFAULT_K=60)` — matches §3/§5 exactly), wired26> as `tg search --semantic` (`grep -n '"--semantic"' src/tensor_grep/cli/main.py`; was `main.py:7069`, now `main.py:7331` typer option, default `False`; bootstrap27> front doors at `bootstrap.py:70` [`_TG_ONLY_SEARCH_FLAGS`, keeps it off the28> rg-passthrough] and `bootstrap.py:478` [`_can_delegate_to_native_tg_search`'s29> `unsupported_flags`, keeps it off native-Rust delegation]) gated on the optional30> `semantic` extra (`pyproject.toml:627`, `model2vec>=0.5`+`numpy>=1.26`; all four31> citations re-verified 2026-07-24 against v1.96.0). **Sections 1-2 below still32> describe the PRE-BUILD state and are now WRONG on the "does not exist yet" claims —33> read them as historical design intent, not current fact.** No `tg index` command34> was added (the persisted-index building blocks in `semantic_index.py` remain35> unwired, per the original §1 note). **RESOLVED (2026-07-24, re-verified against36> v1.96.0): `--semantic` has NOT graduated past default-OFF** —37> `SearchConfig.semantic_rank: bool = False` (`config.py:188`) and AGENTS.md's own38> Roadmap Sequencing section still describes the shipped win as "default-OFF"39> (`grep -n "the #1 validated user ask" AGENTS.md`; was `:561-562`, now `:1738`). **Still not re-verified: whether Phase 4's promotion gate40> (RRF-hybrid beats BM25-only on a real corpus + editor-plane latency) was actually41> measured before the ORIGINAL ship** — that is a historical-PR question this pass did42> not chase down; if you need it, check the `--semantic` flag's introducing PR for43> attached benchmark evidence. If you are extending this further (chunking, a44> `tg index` command, a default-flip), Phases 4-5 below are still the right runbook.45>46> **Two further opt-in refinements SHIPPED since (both default-OFF, additive):**47> `TG_CHUNKER=structural` (PR #443, `9015238`, shipped v1.47.0) -- cAST AST-shaped48> chunking (`retrieval_chunker.py`, `CHUNKER_MODE_ENV_VAR`) beside the fixed-window49> chunker, fail-open, chunk-shape-identical contract, index-version-bumped50> (`semantic_index.py` v2 folds the active chunker mode into its cache key). This51> retires the old "cAST is a candidate deepener, not a requirement for v1" framing in52> S3 Candidate 1 below -- it shipped as an explicit opt-in, not a requirement, but it is53> no longer merely a future candidate. `TG_RRF_CHANNELS=1` (PR #442, `a402f81`, shipped54> v1.46.0) -- channelized RRF (`reranker.py`, `_RRF_CHANNELS_ENV`): weighted per-channel55> fusion including a 1.5x path/filename channel, additive `weights` param, default-off56> and byte-identical when unset.57>58> (Superseded note, kept for history: as of 2026-07-05/v1.40.2 this was still unbuilt59> and `tensor-grep-large-repo-scale-campaign` was the live campaign instead — that is60> no longer the case for the dense/RRF leg specifically; re-check which campaign is61> "live" at the time you read this.)6263> **STATUS UPDATE 2 (2026-07-16, v1.77.0-v1.78.1, campaign #189): the architecture64> GRADUATED into a standalone whole-repo command, `tg find`.** Where `--semantic`65> re-ranks an EXISTING regex match set, `tg find` walks and ranks the WHOLE repo (no66> pattern pre-filter), reusing the same `retrieval_dense.py`/`retrieval_fusion.py`67> core. It shipped its own golden harness, `benchmarks/eval_late_rerank_quality.py`68> (a 40-query NL vocab-mismatch golden set + literal/identifier golden slices,69> superseding this skill's toy `eval_bm25_quality.py` as the Phase-4-style70> discriminating gate for `tg find` specifically) — gate-run result: `rrf` beats71> `bm25` by **+0.195 ndcg@10 / +0.30 recall@10**, bidirectional-oracle-validated72> (internal; public numbers stay CEO-gated #72). Two related dispositions are:73> (1) **MaxSim late rerank** (`TG_LATE_RERANK`)74> is **RETIRED** (task F10): the post-role-aware-encoder measurement was decisively negative;75> `retrieval_late.py` records the retirement. Do not re-run the same encoder expecting a76> different verdict. (2) `TG_FIND_DENSE_WEIGHT` (see `tensor-grep-config-and-flags`) now77> adaptively applies `5.0` to multi-word queries when unset, empty, malformed, or non-finite;78> single-token queries remain `1.0`, and explicit `TG_FIND_DENSE_WEIGHT=1.0` opts out.79> **Receipt (real-corpus-dogfood-beats-fixture-green):** the query80> classifier that scopes `TG_FIND_DENSE_WEIGHT` to multi-word queries was originally a81> `split_terms()` morpheme-count floor (`> 2` morphemes = NL); it passed its synthetic82> literal-golden fixture but a real-repo dogfood on tensor-grep's own `src/` caught it83> mis-boosting 5 of 6 literal identifier queries (`_confine_mcp_path`, `getUserName`,84> `reciprocal_rank_fusion` all split into 3+ morphemes) — fixed by switching to a85> whitespace word-count gate (`len(query.split()) <= 1` stays literal), #191/#630.86> See `tensor-grep-run-and-operate` §1/§7/§11c for the CLI/MCP command surface and87> exit contract, and the dedicated operator skill **`tensor-grep-find-and-route`** for the88> day-to-day `tg find`/`tg route-test` CUJ (this skill stays the BUILD/campaign history; that one is89> the how-to-run doc).90>91> **STATUS UPDATE 3 (2026-07-21, research campaign #251) — cAST structural chunking REJECTED as92> default; do not re-propose it.** `TG_CHUNKER=structural` (shipped v1.47.0, mentioned above as an93> opt-in refinement) was evaluated as a candidate for the DEFAULT chunker on a real-corpus retrieval94> eval: the retrieval-quality delta was a net WASH, while cAST chunking ran **24.4x SLOWER** and95> produced **~38% LARGER** chunks than the shipped line-window `chunk_file`. The opt-in code remains96> shipped for experimentation; it is **not** promoted, and this is now a documented retirement in97> `docs/PAPER.md` §3.10 — do not re-run this experiment expecting a different verdict without new98> evidence. **Use the right harness when re-measuring anything chunker-sensitive:**99> `benchmarks/eval_late_rerank_quality.py` (live — imports and calls `chunk_file`/`chunk_file_structural`)100> is the correct instrument; `benchmarks/run_repo_retrieval_benchmarks.py` is a **static-fixture101> REPLAY that never calls `chunk_file` at all** — it cannot detect a chunker regression or improvement102> and was mistakenly cited as the Phase-4 discriminating gate earlier in this skill (§0/Phase 4 below103> now point at the correct script).104>105> **One-line caution (do not re-propose): dense-embedding compression (int8/binary/PCA) was106> evaluated and DEFERRED** — memory-only win (3.79x smaller on disk), but ~2x SLOWER in numpy (no107> int8 SIMD on the CPU-only path this campaign targets); a real speed win needs a native kernel108> (banked as a moat-investment option, not a quick follow-up here).109>110> **Sibling hardening, not a semantic-search change:** `#699`/A7 hardened the SIBLING flat lexical111> scorer (`_score_symbol` in `repo_map.py` — exact word-boundary bonus + test-file demotion), which112> is a DIFFERENT scorer from this campaign's BM25/dense/RRF stack (see113> `code-search-and-retrieval-reference` §3) — do not conflate the two when reading a "ranking fixed"114> claim.115>116> **STATUS UPDATE 4 (2026-08-12 retention pass; verified against v1.110.14, base `568065a`): the117> `TG_FIND_DENSE_WEIGHT` claim in STATUS UPDATE 2 item (2) above — "default-OFF (`1.0` =118> byte-identical no-op)" with the flip "a separate, still-open CEO checkpoint" — is FALSE at this119> SHA. The flip SHIPPED (#191/#634, commit `c1d4ba4`); the CEO checkpoint is closed by it.**120> Verified resolution rule (`grep -n "_FIND_DENSE_WEIGHT_ADAPTIVE_DEFAULT"121> src/tensor_grep/cli/main.py` — `= 5.0` at `main.py:4614`; reader `_find_dense_weight` at122> `main.py:4627-4684`): unset / empty / malformed / non-finite env → the adaptive123> `_FIND_DENSE_WEIGHT_ADAPTIVE_DEFAULT` (`5.0`, the ledger-swept 1:5 bm25:dense ratio) for genuinely124> multi-word NL queries; a single whitespace-free token stays pinned at `_FIND_DENSE_WEIGHT_DEFAULT`125> (`1.0`) regardless of the env var's state; explicit `TG_FIND_DENSE_WEIGHT=1.0` is the opt-out back126> to the old equal-weight fusion (any other finite value, e.g. `=3.0`, is honored verbatim).127> Evidence (`grep -n "dense-weight flip" CHANGELOG.md`): NL ndcg@10 0.3047->0.4466, zero128> per-category regression. **Version note (verified, do not propagate the mislabel):** the129> v1.93.4-era CHANGELOG #712 entry calls this "the v1.93.2 dense-weight flip", but130> `git tag --contains c1d4ba4` puts the flip commit's FIRST release at **v1.79.0** (its own131> CHANGELOG entry sits under `## v1.79.0 (2026-07-16)`) — the v1.93.2 label in that later entry is132> a mislabel. The old item-(2) text above is kept as dated history: it was accurate for the133> #628/#630 flip-prep era and is closed by the flip.134>135> **SUPERSEDED (same 2026-08-12 pass): STATUS UPDATE 2 item (1)'s MaxSim / `TG_LATE_RERANK`136> "re-run the `tg find` gate" instruction is RETIRED — do NOT re-run.** The module docstring is the137> authority: `src/tensor_grep/core/retrieval_late.py:4-16` (note: `core/`, not `cli/`) records138> "RETIRED 2026-08-05 (task F10)". The post-role-aware-encoder-fix re-measurement HAPPENED and was139> decisively negative: ndcg@10 0.068 vs plain RRF 0.305 (`docs/PAPER.md:469`), root cause model140> capacity (the 17M-param int8 `LateOn-Code-edge` model's raw MaxSim ranking is statistically141> indistinguishable from random on in-repo code) — NOT the encoder wiring, so "re-flipping the same142> encoder will not change the verdict." The module is kept in place, not deleted (the143> `core/reranker.py` wiring is too load-bearing to touch for a demand-gated feature), reachable only144> behind the undocumented `TG_LATE_RERANK=1` env var with no `tg`-command install path. Reopen only145> on BOTH a real `tg`-command install path AND a different encoder clearing the design doc's T8146> golden-set thresholds — never off a partial win on either alone.147148---149150## 0. When to use this skill — and when to use a sibling instead151152Use this skill when the task is **the hybrid-retrieval build itself**: adding a dense153leg, RRF fusion, a persisted hybrid index, a `tg index` command, or measurably154improving `tg search --rank`.155156| If you actually need to… | Use this sibling instead |157| --- | --- |158| Understand the front door / routing / registration sites / backend contract | `tensor-grep-architecture-contract` |159| Get merge/release/experimental-flag gates + the incidents behind them | `tensor-grep-change-control` |160| Register a new flag or command (the exact sites) | `tensor-grep-config-and-flags` (mechanics) + `tensor-grep-architecture-contract` |161| Run/read a benchmark so a number is claim-quality, not noise | `tensor-grep-benchmark-and-proof-toolkit` |162| BM25 / RRF / static-embedding / IDF theory reference | `code-search-and-retrieval-reference` |163| Research external prior art (Semble, ripvec, potion-code-16M) | `tensor-grep-research-frontier` + `tensor-grep-research-methodology` |164| Debug a broken build / test / import | `tensor-grep-debugging-playbook` |165| Learn a settled battle (FFI reverts, dep caps, mock-vs-real) so you don't re-fight it | `tensor-grep-failure-archaeology` |166| Build/run the toolchain (uv, maturin, cargo) | `tensor-grep-build-and-env` |167| The local validation gate + QA | `tensor-grep-validation-and-qa` |168| Update README/AGENTS/docs after shipping | `tensor-grep-docs-and-writing` |169| Position the feature externally (never "faster grep") | `tensor-grep-release-and-positioning` |170171**This skill never routes around change-control.** Promotion is a `change-control`172decision (Phase 5); this skill only produces the *evidence* that decision needs.173174---175176## 1. What already exists (the BM25 lexical leg is SHIPPED)177178> **2026-07-08 correction: this section (and §2 below) was written when only the BM25179> leg existed. The dense leg (`retrieval_dense.py`) and RRF fusion180> (`retrieval_fusion.py`) have SHIPPED since — see the STATUS box above. Treat every181> "does not exist yet" statement below as describing the pre-2026-07-0x state, not182> current fact; re-verify with the grep in the STATUS box before relying on it.**183184Read these before writing a line. Every path below is verified against the repo as185of v1.17.25.186187| File | What it does | Load-bearing facts |188| --- | --- | --- |189| `src/tensor_grep/core/retrieval_chunker.py` | Splits files into line-window chunks | `chunk_file(chunk_size=30, overlap=5)`; step = `max(1, chunk_size-overlap)` = 25; `MAX_CHUNKS=100_000` loud guard (raises, never silent OOM). Per-chunk granularity is what the design council settled on (not per-line, not per-file). |190| `src/tensor_grep/core/retrieval_lexical.py` | Tokenizer + bare overlap counter | `split_terms()` is camelCase/underscore/hyphen aware, lowercased. This is the shared tokenizer — the dense leg MUST tokenize identically or scores diverge. |191| `src/tensor_grep/core/retrieval_bm25.py` | Okapi BM25 over chunks | `Bm25Index`, `k1=1.5`, `b=0.75`, IDF with +1 smoothing (non-negative weights). Dedupes query terms so a repeated token isn't double-counted. Returns `[(chunk_index, score)]`, zero-score chunks excluded, ties break by chunk index (deterministic). |192| `src/tensor_grep/core/reranker.py` | The LIVE `tg search --rank` path | `rerank_by_bm25(result, query, file_paths)` re-orders matches by the best BM25 score of the chunk containing each match; stable sort (ties keep grep order); non-scoring matches sink. Builds the BM25 index **in memory every call** over just the matched files — no persisted index. |193| `src/tensor_grep/core/semantic_index.py` | Persisted chunk-BM25 index building blocks | `build_and_save` / `load_or_warn` under `.tg_semantic_index/` (env `TG_SEMANTIC_INDEX_DIR`), **SEPARATE** from the Rust TGI v3 `.tg_index` (trigram). `INDEX_VERSION=2` (re-verified 2026-07-24 against v1.96.0; bumped from 1 when the structural chunker folded its mode into the cache key, per the "Two further opt-in refinements" note above). Stale check = SHA-256 fingerprint over sorted paths + mtimes → on mismatch, warn to stderr + return `None` → in-memory fallback. **NOT wired to the CLI — there is no `tg index` command yet (still confirmed absent).** |194| `tg install-dense` (CLI command, v1.91.0) | One-shot dense-leg setup | Installs the `semantic` extra (`model2vec`+`numpy`, torch-free) via the same `uv tool → uv pip → pip` cascade `tg upgrade` uses, then fetches the checksum-pinned `potion-code-16M` model; fails closed (non-zero exit, no partial model directory) on any pip/network/checksum failure — never a silent half-installed state. Every dense-absent hint across the CLI (`tg search --semantic`, `tg find`'s `rank_fallback_reason`) now leads with `tg install-dense` (v1.93.0/#705) instead of a bare "pip install the extra" instruction. |195| `src/tensor_grep/core/retrieval_scoring.py` | Metrics | `recall_at_k`, `precision_at_k`, `mean_reciprocal_rank_at_k`, `ndcg_at_k`, `f1_score`, `RetrievalMetrics`. These are the promotion yardsticks — use them, don't invent new ones. |196197**How `--rank` is wired (verify before changing):**198- Flag: `--rank` (alias `--bm25`), default OFF. `SearchConfig.rank_bm25 = False` (`config.py:183`, re-verified 2026-07-24 against v1.96.0). The dense leg's own flag sits right below it: `SearchConfig.semantic_rank = False` (`config.py:188`).199- It is a **TG-only** search flag: `bootstrap.py::_TG_ONLY_SEARCH_FLAGS` (`--rank` line 68, `--bm25` line 69, `--semantic` line 70 — re-verified 2026-07-24) — the bootstrap front door intercepts it and does NOT forward it to ripgrep. This is one of the two flag front doors; see `tensor-grep-config-and-flags`.200- Setting `--rank` **leaves the ripgrep passthrough fast-path**: the `_can_passthrough_rg()` condition includes `and not config.rank_bm25` and `and not config.semantic_rank` (`grep -n "not config.rank_bm25\|not config.semantic_rank" src/tensor_grep/cli/main.py`; was `main.py:5232-5250` at v1.96.0, now `main.py:5438-5463` at v1.101.27 — this seam has already drifted twice inside two weeks, cite the grep, not the number), so the request runs the tg engine and results are re-ordered right after match aggregation — the `elif config.rank_bm25 and all_results.matches:` guard through the `rerank_by_bm25(...)` call (`grep -n "elif config.rank_bm25 and all_results.matches\|rerank_by_bm25" src/tensor_grep/cli/main.py`; was `main.py:7965-8069`; the follow-up `:8411-8414` pin then landed INSIDE a `--deadline` option block deleted by the 2026-08-23 de-duplication, so it has no successor either -- the THIRD drift of this one anchor. Use the grep above and stop pinning it).201- User docs: `grep -n -- "--rank" README.md` (feature bullet `:39`, example `:172-173` as of 2026-08-14; were `:38` and `:147-148` at the v1.96.0 pass).202203**Bottom line:** the **lexical leg (BM25) and the persisted-index building blocks204already exist and ship default-OFF.** The campaign adds the **dense leg + RRF fusion205+ (optionally) a wired persisted hybrid index.**206207---208209## 2. What you are building (the approved architecture)210211```212 chunk_file() ──► chunks ──► ┌─ BM25Index.query() ──► ranking A213 query ──► split_terms() │214 (same tokenizer) └─ dense encode+cosine ──► ranking B215 │216 RRF fuse(A, B, k=60) ──► final ranking217```218219- **BM25 leg** — exists (`retrieval_bm25.py`).220- **Dense leg** — DOES NOT EXIST YET. A CPU static-embedding model produces a vector221 per chunk and per query; rank chunks by cosine similarity. Static means a per-token222 vector *lookup* (no transformer forward pass at query time) → fast on CPU, no GPU,223 no API key, no network at query time.224- **RRF fusion** — DOES NOT EXIST YET. Combine the two rankings without score225 normalization: `score(d) = Σ_r 1 / (k + rank_r(d))` over the rankers `r ∈ {bm25,226 dense}`, with **k = 60** (the value the reference architecture uses). A document227 absent from a ranker's list contributes 0 for that ranker. RRF is rank-based, so it228 is robust to the fact that BM25 scores and cosine scores are on incomparable scales.229230**SUPERSEDED (was true through v1.40.2, 2026-07-05; false as of v1.49.3, 2026-07-08):**231~~no dense/embedding/RRF/Model2Vec/potion code exists in `src/` today~~ — this leg has232since shipped as `retrieval_dense.py` + `retrieval_fusion.py`; re-run233`grep -rin "model2vec|potion|reciprocal_rank_fusion|StaticModel" src/` yourself and234expect real hits, not just comments.235236**The moat framing (do not lose it):** this is **not** "faster grep." ripgrep is the237raw-text parity baseline. The value is agent-native retrieval quality on238vocabulary-mismatch queries (find `authenticate` when the user typed "verify login").239Keep the positioning honest per `tensor-grep-release-and-positioning`.240241---242243## 3. Solution menu (RANKED) with derivation obligations244245Pick in this order. **Each candidate carries a derivation obligation — a claim you246MUST verify (not assume) before you build on it.** "Derive" = confirm against a247primary source (the model card, the license file, a local import test), then record248the finding. Route the research through `tensor-grep-research-frontier` +249`tensor-grep-research-methodology`; never trust a self-report (change-control gate B).250251### Candidate 1 (preferred): the Semble pattern252Tree-sitter chunking + **`potion-code-16M`** Model2Vec static embeddings + BM25 + RRF253(k=60). CPU-only, MIT. This is the reference architecture named in AGENTS.md254(`grep -n "MinishLab .Semble." AGENTS.md`; was `:565` at v1.96.0, now `:1741` at v1.101.27; the255"## Roadmap Sequencing" heading it sits under was `:525`, now `:1701` — AGENTS.md keeps growing new256sections above these, cite the grep not the number).257258Derivation obligations before you depend on it:2591. **License** — confirm `potion-code-16M` (and the `model2vec` runtime) are260 MIT/Apache-compatible with tensor-grep's Apache-2.0 and add the required NOTICE261 entries. Ideas are free; imported code/weights need their notices.2622. **Truly offline** — confirm the model loads from a bundled/cached file with **no263 network call and no API key** at query time. If it phones home or needs a token,264 it is DISQUALIFIED (see §4 fenced paths).2653. **CPU + footprint** — confirm it runs with no GPU, and record the on-disk model266 size and the added dependency weight. The model **must be an OPTIONAL extra**, not267 a hard install dependency (every-install must still work with BM25-only).2684. **Chunking choice -- RESOLVED, both ship.** Semble uses tree-sitter chunks;269 tensor-grep already had line-window `chunk_file` as the default, and now also ships270 `TG_CHUNKER=structural` (PR #443, v1.47.0) -- opt-in cAST AST-shaped chunking271 (`docs/PAPER.md`/arXiv:2506.15655 is the reference paper), fail-open and272 chunk-shape-identical to the fixed-window path when unset. This was a *candidate273 deepener* as of the original design; it is now a shipped, default-off refinement --274 do not describe it as unbuilt.275276### Candidate 2: ripvec (pure-Rust)277A pure-Rust vector path. Derivation obligations: confirm license, maturity, and278whether it fits the existing PyO3 bridge without reintroducing the FFI overhead that279was already measured too high and reverted (`tensor-grep-failure-archaeology`: FFI is280not the dir-scan speed path). Only choose this if Candidate 1 fails a gate AND you281have measured that the Rust path clears the same promotion bar.282283### Candidate 3: BM25-only (the honest null result)284Ship nothing new. **This is a legitimate, non-embarrassing outcome** if the dense leg285does not beat the BM25 baseline on both retrieval quality and editor-plane latency.286"No speed/quality claim without measured numbers vs the baseline" (change-control287gate C) cuts both ways: if the numbers aren't there, the correct move is to keep the288shipped `--rank` baseline and record the negative result. `grep -n -- "demonstrably beats" README.md` -- `:237` as of 2026-08-14 (was `:212` at the v1.96.0 pass)289states the rule explicitly: extend lexical290(BM25) re-ranking with AST-shaped chunking or semantic re-ranking **only when it291demonstrably beats the shipped `tg search --rank` baseline on both retrieval quality292and editor-plane benchmarks.**293294---295296## 4. Fenced-off wrong paths (do NOT do these)297298| Forbidden | Why | If you're tempted |299| --- | --- | --- |300| **API-key / hosted embeddings** (OpenAI, Voyage, Cohere, any `*_API_KEY`) | Breaks "no API key, runs on every install, local-first." The whole point is $0, offline. | Static local model only. If a candidate needs a key or a network call at query time, it's disqualified. |301| **GPU / CUDA dependency for the dense leg** | GPU is EXPERIMENTAL, default-OFF, and currently *slower* than CPU with no promotion-ready path (Roadmap Sequencing Phase 1, "reversible flag-flip, not yet authorized" — no crossover proven, `grep -n "reversible flag-flip, not yet authorized" AGENTS.md`; was `:539-541` at v1.96.0, now `:1715` at v1.101.27). A GPU-gated ranking layer would not run on the common install. | CPU static embeddings. GPU may be an *optional* future accelerator, never a requirement. |302| **Breaking `--format rg` / `--json` / `--ndjson` semantics** | Those output contracts are the raw-grep parity surface. `--rank` is a **re-order overlay**: same matches, different order. When `--rank` is NOT set, the ripgrep passthrough fast-path (`main.py:5212`, `_can_passthrough_rg`, re-verified 2026-07-24) must remain byte-for-byte. | Keep ranking strictly post-processing over an already-produced `SearchResult`. Never change match membership or the rg-shaped output when ranking is off. |303| **A hard new install dependency** | Every-install must keep working. | Make the dense model an optional extra; degrade to BM25-only when absent (see §6). |304| **Shipping user-visible before the gate** | Violates experimental-until-proven (change-control gate D). | Default-OFF flag + benchmark + conscious flag-flip (Phase 5). |305| **Eyeballing "it feels more relevant"** | Ranking surfaces silently FLIP on corpus change; the blast radius is invisible to the call graph (known weak point — flat scorer, incident #302). | Measure `recall@k` / `ndcg@k` on a real corpus. Numbers or it didn't happen. |306| **Routing around change-control** | Non-negotiable. | Produce evidence here; let `tensor-grep-change-control` gate the flip. |307308---309310## 5. The phased runbook (decision-gated)311312Run phases in order. Each gate says the expected number and where to **branch** if313you see something else. All commands are copy-pasteable; PowerShell is the primary314shell on the dev box, but `uv run` is cross-platform.315316> **`uv run` gotcha:** a bare `uv run ...` can re-sync and drop the `[dev]`317> tree-sitter/extras tree. For benchmark/import work use `uv run --no-sync ...` so the318> installed dev tree (and any editable install of the dense model) is not wiped. See319> `tensor-grep-build-and-env`.320321### Phase 0 — Establish the baseline (NEVER skip)322323You cannot claim an improvement without the number you improved on.324325```powershell326uv run --no-sync python benchmarks/eval_bm25_quality.py --top-k 3327```328329**Expected (verified 2026-07-02):**330```331BM25 baseline (top_k=3, n=10 queries):332 recall@k = 1.000333 precision = 0.333334 mrr@k = 1.000335 ndcg@k = 1.000336v2 gate (recall@k >= 0.6): PASS337```338- `benchmarks/eval_bm25_quality.py` defines `V2_GATE_RECALL = 0.60` and states in its339 own docstring: "the v2 dense+RRF leg must beat this before it ships user-visible."340- **GATE 0a:** recall@k must be `1.000` here. If it is lower, the BM25 leg itself341 regressed → STOP, do not build dense on a broken base → `tensor-grep-debugging-playbook`.342- **GATE 0b — read this or you'll waste weeks:** this toy corpus is343 keyword-discriminating, so **BM25 already saturates it at recall 1.0.** Passing the344 0.60 gate proves *nothing* about whether dense helps — it is a **floor / sanity345 gate, not the discriminating gate.** The real justification for a dense leg is346 **vocabulary-mismatch** queries (synonyms/paraphrase) where lexical BM25 misses.347 Your promotion evidence MUST come from a harder, realistic corpus (Phase 4), not348 this file.349350### Phase 1 — Choose + derive the solution351352Work the §3 menu top-down. Complete every derivation obligation for your chosen353candidate and **write the findings down** (license, offline-proof, CPU-proof,354footprint, optional-extra plan). Research via `tensor-grep-research-frontier` +355`tensor-grep-research-methodology`. Before writing code, verify the plan's seam356claims against the real files with `file:line` citations (verify-plan-against-code) —357an AI-drafted plan that says "add it in pipeline.py" is a hypothesis until you358confirm `--rank` is actually wired in `main.py`.359360- **GATE 1:** if the preferred candidate fails a derivation obligation (needs a key,361 needs a GPU, incompatible license, cannot be an optional extra), do not "work around362 it" — drop to the next candidate. If all dense candidates fail, Candidate 3363 (BM25-only) is the correct answer; document the negative result and stop.364365### Phase 2 — Build the dense leg behind a default-OFF experimental flag366367- Add a **new** module (mirror the existing seam names, e.g.368 `core/retrieval_dense.py`) — do not bolt onto `retrieval_bm25.py`.369- Reuse `split_terms()` (or an explicitly justified tokenizer) so the two legs stay370 comparable.371- Wire it behind a **separate default-OFF flag** (the `config.py:182` note already372 anticipates "a separate gated flag" for the dense leg). Do NOT change the meaning of373 `--rank`/`--bm25` yet.374- Honor the **Backend Fail-Closed Contract** (§6).375- TDD: write the contract test first (`tests/unit/test_retrieval_dense.py`), then the376 smallest implementation. See existing tests `tests/unit/test_retrieval_bm25.py`,377 `tests/unit/test_reranker.py`, `tests/unit/test_semantic_index.py` for the pattern.378- **GATE 2:** the dense leg imports and runs with the model **absent** (degrades to379 BM25-only, visibly) AND with it present (produces a ranking). Prove the FFI/model380 path against the REAL runtime, not a mock — mock-green while the real bridge is dead381 is a documented trap (`tensor-grep-failure-archaeology`).382383### Phase 3 — Build RRF fusion384385- Implement `reciprocal_rank_fusion(rankings, k=60)` as a pure function taking each386 leg's ordered list of chunk indices and returning the fused order. Keep `k`387 configurable (default 60) and deterministic ties.388- Fuse **ranks**, not raw scores (BM25 score vs cosine are incomparable scales — this389 is the whole reason RRF is chosen).390- **GATE 3:** unit-test that fusing two identical rankings is a no-op, and that a391 document top-ranked by either leg surfaces near the top of the fused list.392393### Phase 4 — Measure (the real gate)394395Two measurements, both required (`grep -n -- "demonstrably beats" README.md` -- `:237` as of 2026-08-14, was `:212`):3963971. **Retrieval quality on a realistic corpus** (not the toy). Use398 `benchmarks/eval_late_rerank_quality.py` — the LIVE, chunker/ranking-sensitive harness (it actually399 imports and calls `chunk_file`/`rank_chunks`, and computes `RetrievalMetrics`: recall/precision/400 mrr/ndcg on a real repo + the 40-query NL golden set). **Do not use401 `benchmarks/run_repo_retrieval_benchmarks.py`** for this — it is a static-fixture REPLAY that never402 calls `chunk_file` and cannot detect a chunker or dense/RRF-weighting change at all (this was the403 cAST-chunking evaluation's own harness-selection mistake before STATUS UPDATE 3 above corrected404 it). Produce three rows: **BM25-only**, **dense-only**, **RRF-hybrid**, on the SAME corpus + queries.405406 ```powershell407 uv run --no-sync python benchmarks/eval_late_rerank_quality.py --output artifacts/bench_find_quality.json408 ```409 (Read its args first; see `tensor-grep-benchmark-and-proof-toolkit` §7 for the corpus-hardness and410 paired win/loss/tie reporting rigor this gate specifically needs.)4114122. **Editor-plane latency** — the ranking overlay must not blow the interactive413 budget:414 ```powershell415 uv run --no-sync python benchmarks/run_editor_plane_benchmarks.py416 ```417418Route interpretation through `tensor-grep-benchmark-and-proof-toolkit` (noise-floor419rule for sub-10ms rows, fair-baseline rule, launcher attribution). Also record the420**token-economy** delta if the surface feeds the agent capsule (`AGENTS.md` names421token economy as a focus).422423**Promotion gate (all must hold, measured vs the SAME baseline run):**424425| Metric | Requirement |426| --- | --- |427| `recall@k` (real corpus) | RRF-hybrid > BM25-only by a margin beyond the noise floor |428| `ndcg@k` (real corpus) | RRF-hybrid ≥ BM25-only (no ranking-quality regression) |429| Editor-plane latency | within the interactive budget; no material regression vs `--rank` |430| Token economy (if capsule-facing) | no worse than BM25-only |431432- **GATE 4:** if RRF-hybrid does not beat BM25-only on retrieval quality AND hold the433 line on latency, **do not ship it.** Reject the regression even if the code is clean434 (change-control gate C). The honest outcomes are: (a) improve the dense leg/chunking435 and re-measure, or (b) record the negative result and keep BM25-only.436437### Phase 5 — Promote through change-control (never here)438439This skill produces evidence; **`tensor-grep-change-control` owns the flip.** The440graduation path is fixed:4414421. Ship **experimental, default-OFF** (already true after Phase 2).4432. Attach the Phase 4 evidence (three-row quality table + editor-plane numbers +444 token economy) to the PR.4453. **Dry-run** on real data (dogfood the REAL binary via `scripts/dogfood/`; CliRunner446 bypasses the bootstrap front door and will not exercise routing).4474. **Conscious flag-flip** — a deliberate, reviewed default change, never auto-merged,448 never admin-merged. Autonomy is draft-PR-only.4495. Update docs (`README.md`, `AGENTS.md`, the usage skill) via450 `tensor-grep-docs-and-writing`; observe one-merge-per-tick + the push-race rules451 (`tensor-grep-release-and-positioning`).452453If you add a **`tg index`** command (the natural home for a persisted hybrid index),454remember it needs the **4 command-registration sites** (miss one → silent misroute)455and a new flag needs the **2 flag front doors** — see `tensor-grep-config-and-flags`456and `tensor-grep-architecture-contract`.457458---459460## 6. Backend Fail-Closed Contract for the dense leg461462The dense leg is a compute path; it is bound by `backends/base.py`463(`BackendExecutionError`) and the AGENTS.md §"Backend Fail-Closed Contract"464contract (`grep -n "Backend Fail-Closed Contract" AGENTS.md`; was `:496` at v1.96.0, now `:1672` at465v1.101.27). The recurring anti-pattern to466avoid: a bare `except Exception:` that silently returns empty or swaps engines.467468- **Model missing / not installed** → this is a **legitimate degraded fallback** to469 BM25-only, but it MUST be **VISIBLE**: set a `fallback_reason` on the `SearchResult`470 so JSON/CLI consumers can tell degraded output from full hybrid output. Never label471 BM25-only output as "semantic."472- **Model load/encode raises at runtime** (corrupt cache, OOM, version skew) → raise473 `BackendExecutionError`; do not return a clean empty result that reads as "no474 matches." A real failure reported as a no-match is the exact bug this contract475 exists to prevent.476- **Contract flag the fallback cannot honor** → fail closed (raise), do not swap. (For477 a *ranking* overlay a graceful visible degrade to BM25 is the norm; only fail-closed478 if a caller explicitly demanded semantic-only and you cannot deliver it.)479- Validate the model's output shape (vector dimensionality, chunk count) before you480 index, so a mismatch degrades gracefully instead of raising an `IndexError` that a481 broad `except` then swallows.482483See `tensor-grep-architecture-contract` for the full contract and the484planned `SafeBackendMixin` conformance gate.485486---487488## 7. Common failure modes → branch489490| Symptom | Likely cause | Branch to |491| --- | --- | --- |492| BM25 baseline recall < 1.0 in Phase 0 | BM25/tokenizer/chunker regression | `tensor-grep-debugging-playbook` |493| Dense leg "works" in tests but the real binary shows no effect | mock-green while the real path is dead; or `--rank` not actually re-routing | `tensor-grep-failure-archaeology`; verify against the real binary via `scripts/dogfood/` |494| `uv run` benchmark can't import the dense model | `uv run` re-synced away the extra | re-run with `uv run --no-sync`; `tensor-grep-build-and-env` |495| Numbers look great but flip on a different repo | ranking fragility / corpus-sensitive scorer (known weak point) | measure on multiple corpora; `tensor-grep-benchmark-and-proof-toolkit` |496| A speedup/quality claim disputed in review | no fair-baseline row, or sub-noise delta | `tensor-grep-benchmark-and-proof-toolkit` (noise-floor + fair-baseline rules) |497| `--json`/`--format rg` output changed shape | ranking leaked into match membership/output, not just order | revert to strictly post-processing; re-read §4 |498| Release didn't publish after the flip | push-race / one-merge-per-tick violation | `tensor-grep-release-and-positioning` |499500---501502## 8. Pre-flight checklist (before you open the PR)503504- [ ] Phase 0 baseline recorded (`eval_bm25_quality.py`) — with the note that it's a floor, not the discriminating gate.505- [ ] Chosen candidate's derivation obligations all verified + written down (license, offline, CPU, optional-extra).506- [ ] Dense leg is a NEW module, behind a NEW default-OFF flag; `--rank`/`--bm25` semantics unchanged.507- [ ] Fail-closed contract honored: missing model → visible `fallback_reason`; runtime error → `BackendExecutionError`.508- [ ] RRF is rank-based, `k=60` default, deterministic; unit-tested (identity no-op + top-surfacing).509- [ ] Phase 4 evidence: three-row quality table (BM25 / dense / RRF) on a REAL corpus + editor-plane latency + token economy — RRF beats BM25-only on quality without a latency regression, OR the negative result is documented and you stop.510- [ ] `--json` / `--ndjson` / `--format rg` unchanged when ranking is off.511- [ ] Local validation green: `uv run ruff check .` · `uv run ruff format --check --preview .` · `uv run mypy src/tensor_grep` · `uv run pytest -q` (CI runs `ruff format --check --preview` — you MUST pass `--preview`).512- [ ] Real-binary dogfood, not just CliRunner (`scripts/dogfood/`).513- [ ] Promotion routed through `tensor-grep-change-control`; draft PR only; conscious flag-flip, never auto-merge.514515---516517## Provenance and maintenance518519Everything below is verifiable from the repo. Re-run these when a claim may have520drifted; date-stamp any change.521522- **Version / date:** facts originally verified `v1.17.25` (2026-07-02); re-verified523 UNCHANGED against released `v1.40.2` (origin/main `8829441`) on 2026-07-05; spot-checked524 again 2026-07-08 against `v1.49.3` and found the dense/RRF leg now SHIPPED (see STATUS525 UPDATE near the top); spot-checked again 2026-07-16 against `v1.78.1` and found the526 architecture graduated into `tg find` (see STATUS UPDATE 2 near the top); spot-checked again527 2026-07-22 against `v1.93.2` and recorded the cAST-chunking rejection + dense-int8 deferral +528 install-dense row (see STATUS UPDATE 3 near the top — this was targeted at the research-campaign529 #251 retirements and the harness-selection correction, not a full re-walk of Phases 0-8 below).530 **Spot-checked again 2026-07-24 against `v1.96.0` (origin/main `29cf59f`): deleted the531 Provenance bullet below that directly contradicted the "Dense leg + RRF now shipped" bullet532 (its own grep no longer produces the "expect no hits" result it claimed — 5 real hits in533 `main.py`/`config.py`/`reranker.py`/`retrieval_dense.py`/`retrieval_fusion.py`); resolved the534 open "has `--semantic` graduated past default-OFF" question (NO — see the STATUS UPDATE box535 near the top); re-verified and fixed drifted `file:line` citations throughout §1/§3/§4/§6/536 Provenance (`main.py`/`bootstrap.py`/`pyproject.toml`/`README.md`/`AGENTS.md` line numbers had537 each drifted anywhere from ~10 to ~1500 lines since the v1.17.25-era pins — `main.py`'s538 `_can_passthrough_rg` moved the most, `3883`→`5249`); fixed `INDEX_VERSION=1` → the539 now-current `INDEX_VERSION=2`; and corrected a stale STATUS UPDATE 2 claim (the540 `retrieval_late.py` doc-role-encoder harness gap it cited as blocking `TG_LATE_RERANK` was541 fixed by #189 Item 1 since that note was written; the later F10 measurement retired MaxSim).542 This was a targeted re-verification of THIS skill's own claims, not a full re-walk543 of every sibling skill or every historical receipt (e.g. the 2026-07-16 `+0.195 ndcg@10`544 gate-run number in STATUS UPDATE 2 is a dated point-in-time receipt, left as-is).545 **Skill-library drift audit, 2026-08-01, against `v1.101.27`:** every `main.py`/`AGENTS.md`546 `file:line` citation re-grepped again and eve547548…(truncated)
Run npx skillmds@latest add oimiragieo/tensor-grep-semantic-search-campaign in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
Use when building, extending, or reviewing tensor-grep's APPROVED local hybrid semantic search — BM25 + CPU dense embeddings fused with Reciprocal Rank Fusion (RRF), no API key, no GPU (roadmap item #1). Load before adding a dense/embedding leg, RRF fusion, a `tg index` command, or changing `tg search --rank` / `--bm25`. Covers the decision-gated build phases with exact commands + expected gate numbers, the ranked solution menu (Semble / ripvec / BM25-only) with derivation obligations, the retrieval-quality + editor-plane + token-economy promotion gates, the Backend Fail-Closed Contract for the dense leg, fenced-off wrong paths (no API-key embeddings, no GPU dependency, do not break `--format rg` / `--json` / `--ndjson` semantics), and routing promotion through change-control. STATUS as of 2026-07-08, v1.49.3: the dense leg + RRF fusion described here as the target architecture (Candidate 1 / the Semble pattern) SHIPPED as `tg search --semantic` (`retrieval_dense.py` + `retrieval_fusion.py`, default-OFF, gate It is listed under Product & Planning on SkillMD.
This skill has not completed SkillMD's automated safety review yet. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
oimiragieo (@oimiragieo) published this skill. Their other Agent Skills are listed on their SkillMD profile.