Documents the work shipped this session (Phase 1 foundation, 10
commits 9ba6317..20faae0) and the blocker that stops Phase 2: the
multi_route pipeline regresses on 2/5 smoke fixture questions
(Mercury Seven → Sam T. Beddingfield; dinosaurs extinct →
Paul Austin Kelly) because Phase 1 hasn't ported the 5 downstream
rerank stages legacy query() uses to suppress noisy phrase-route
hits — body-density check, body-coverage sqrt rerank, source-role
rerank, title-purity rerank, ordered-token-match rerank.
Path B chosen 2026-05-31: stop here, leave multi_route off by
default (which IS default — policy=None preserves body-only
behavior). Phase 1 foundation stays in tree as future-ready
infrastructure; user surface unchanged.
Path A (port the 5 stages, multi-day effort) reserved for a
future focused session. Until then, legacy query() keeps
producing the same answers it always has, INCLUDING the wrong
"Edwina" pick on dinosaur Q5. The slim-FTS5 cloud path already
fixes that bug for `arborist cloud query` / `arborist
corpus-query` callers (proven 5/5 source parity in d9fb6a9).
Bumps Next ID 000072 → 000073.
8.1 KiB
Ticket #000072 — Collapse legacy query() into the unified run_query
Status: open · Phase 1 foundation shipped 2026-05-31 · Phase 2 BLOCKED on
the 5 missing rerank stages. A multi_route smoke probe regressed on
2/5 questions — Phase 1 infrastructure is structurally complete but
not production-quality. Promoting policy.multi_route=True to default
today would degrade the user surface.
Opened: 2026-05-31 Owner: fox/blackops Directive: cleanup
Why this exists
arborist/qa/query.py is 4280 LOC; the query() function alone runs
lines 1975-4196 (2200 LOC, single function). It is the local-CLI
entry point for arborist query — every retrieval, rerank, cache
lookup, audit-chain append, run-DAG build, and verifier kwarg threads
through it. It also picks the WRONG primary source on the dinosaur-
extinction smoke fixture: Edwina, the Dinosaur Who Didn't Know She Was Extinct (a children's book) instead of Dinosaur (the encyclopedia
article). The cloud-via-slim-FTS5 path landed in d9fb6a9 already picks
Dinosaur correctly through the unified run_query orchestrator.
Collapsing legacy query() to delegate to the same orchestrator
would fix that retrieval bug as a byproduct and delete ~1500 LOC of
duplicated retrieval/rerank logic.
What shipped — Phase 1 foundation (2026-05-31)
Ten incremental commits, each behavior-additive, each with the byte-identity gate (step 5) staying green:
| commit | step | what landed |
|---|---|---|
9ba6317 |
1 | arborist/qa/source_roles.py — classify_source_role, weights, title-marker tuples extracted from query.py |
a83e47b |
2 | arborist/qa/_text_norm.py gains canonical stem_for_match; query.py + corpus.py both import |
056d785 |
3 | arborist/qa/retrieval_routes.py with filter_by_title_relevance (5-accept-path filter); query.py re-exports |
e322bbd |
4 | Corpus protocol gains core_keyword_match + doc_body; SqliteShard implements; SidecarBucket raises NotSupportedError |
1822741 |
5 | tests/test_run_query_byte_identity.py + tests/fixtures/byte_identity/claim_lattice.json — pins hash inputs as Phase 2 safety gate |
72d1117 |
6a | run_query gains policy= kwarg threading 12 verifier kwargs |
03f248c |
6b | role-classified + role-weighted budget when policy provided |
5fdd573 |
6c | multi-route retrieval (body+title+phrase+core_keyword merge) when policy.multi_route=True |
6c2ec1b |
6c-fil | wire filter_by_title_relevance into multi_route path |
b8bd9d6 |
6d | wikitext-strip when policy.base_version is set |
20faae0 |
P2/1 | arborist/qa/providence_query.py — cache-aware wrapper around run_query (skeleton; cache persist not yet done) |
Every commit ships with policy=None preserving pre-step byte-identity
behavior — existing arborist corpus-query and arborist cloud query
callers are unaffected. 264 tests in the
query/corpus/sidecar/wallet/bucket/claim_lattice/byte_identity gate
pass at HEAD.
What's broken — the multi_route smoke regression (2026-05-31)
Retrieval-only smoke against the 4-shard wikipedia corpus (no LLM,
StubClient), comparing policy=None (body-only) vs
policy={"multi_route": True} (body+title+phrase+core_keyword merged
through filter_by_title_relevance):
| Q | no-policy | multi_route | verdict |
|---|---|---|---|
| Soviet Union dissolve | Soviet Union | Soviet Union | preserved |
| Mount Kilimanjaro | Mount Kilimanjaro | Mount Kilimanjaro | preserved |
| Mona Lisa painter | Mona Lisa | Mona Lisa | preserved |
| Mercury Seven astronauts | Mercury Seven | Sam T. Beddingfield | REGRESSED |
| Dinosaurs extinct | Dinosaur | Paul Austin Kelly | REGRESSED |
Diagnosis: the phrase route (fts_phrase on 4-grams from the
question) surfaces oddly-titled stubs whose body contains the
literal question phrase. The 5-accept-path filter's path-4
(phrase_match_roots) lets them through with no body-density
gate, and apply_title_boost doesn't rerank them out because their
titles share zero tokens with the question.
Legacy query() suppresses these noisy phrase hits via downstream
stages that Phase 1 has NOT ported:
_body_density_passes— co-occurrence breadth check on full doc body (3+ token queries require N-1 of N tokens present at ≥3 mentions each)_rerank_by_body_coverage— sqrt-rerank that counters BM25 short-doc bias_rerank_by_source_role— role-weighted score multiplier (primary 2.0×, noisy 0.3×)_rerank_by_title_purity—(1+overlap)*(1+purity)boost_rerank_by_ordered_token_match— longest-ordered-subsequence match between question tokens and title tokens
Phase 2 — BLOCKED until the 5 stages above land
Phase 2 was meant to land:
providence_query.pycache wrapper (skeleton done, persist deferred)- Legacy
query()collapsed to a thin adapter (50-line shell that delegates toprovidence_query) - Bench parity proof on smoke + bench/qa_questions.txt themed subsets
But Phase 2 cannot ship without the 5 missing rerank stages — the new orchestrator currently produces WORSE primary picks than legacy on 2/5 smoke questions. Promoting it to default would degrade the user surface. Fox decision 2026-05-31: defer Phase 2; do NOT promote multi_route to default until A lands.
Path A — port the missing rerank stages (multi-day)
Estimated scope:
retrieval_routes.body_density_passes(corpus, hit, qtokens, *, min_mentions=3)— Corpus exposesdoc_body(document_root)since step 4 (e322bbd); fn already shaped for hits that carrydocument_root. ~30 LOC.retrieval_routes.rerank_by_body_coverage(hits, question, corpus)— usescorpus.doc_bodyper hit; sqrt-rerank multiplier. ~60 LOC.retrieval_routes.rerank_by_source_role(hits, question)— usesclassify_source_rolefrom source_roles.py + RANK_WEIGHTS. ~30 LOC.retrieval_routes.rerank_by_title_purity(hits, question, *, shards_dir=None)— lifts the (1+overlap)*(1+purity) shape with stem-aware matching; synonym_expand_strict gated on shards_dir. ~80 LOC.retrieval_routes.rerank_by_ordered_token_match(hits, question)— longest-common-subsequence over query tokens vs title tokens. ~70 LOC.run_querypolicy-gated chain that invokes them in order: filter → body_density → body_coverage → source_role → title_purity → ordered_token → apply_title_boost (existing tail). ~50 LOC.- Re-bench: smoke (must hit 5/5), bench/qa_questions.txt themed subsets (no >5pp drop vs legacy).
- Only after bench-parity: continue Phase 2 cache persist + legacy collapse.
Risk: byte-identity gate (step 5) catches cache_key drift, but
SEMANTIC drift (different primary source picks) requires the bench
gate. Legacy bench data in bench/three_way_results/*.jsonl is the
reference baseline.
Path B (chosen 2026-05-31)
Stop here. Document the regression. Leave policy.multi_route=False
as the default behavior (which it is — policy=None is default and
preserves body-only retrieval). Phase 1 infrastructure stays in tree
as the FOUNDATION for a future A-path effort; it is not the
production user surface today.
Practical implications:
arborist query(legacy) keeps producing the same answers it always has, INCLUDING the wrong "Edwina" pick on dinosaur Q5.arborist corpus-queryandarborist cloud querykeep producing the better answers via the unifiedrun_querypath (no-policy mode, which IS already shipped).providence_queryexists as a skeleton callers can opt into but is incomplete (cache persist missing).- No commits revert; the Phase 1 work is genuinely useful even without Phase 2.
When to reopen
When fox wants to either:
- Take a focused multi-day session to land Path A
- Investigate a specific bench regression that suggests the Phase 1 foundation is itself wrong (vs. just incomplete)
Until then this ticket sits open as the gap-tracker. Pattern from CLAUDE.md: "An open ticket whose code already shipped is a stale map" — but here the OPEN-ness is the point. Phase 1 shipped, Phase 2 hasn't. The ticket records the bridge that wasn't built yet.