feat: cross-language Q&A (Operation Sandwich) + Windows quickstart — all default-OFF
Three workstreams, full suite 2482 passed, experimental paths default-OFF. #000055 — Windows quickstart without make tasks.py (pure-stdlib runner) + make.bat shim + .gitattributes; README Windows section rewritten. Quickstart needs only Python 3.10+ (no make/bzip2/curl/bash). Mirrors the Makefile quickstart subset; drift-pinned by tests/test_tasks_runner.py. #000001 §7 Phase 0 — deterministic cross-language guard arborist/qa/crosslang.py: non-English signal (¿/¡/non-ASCII) + an es function-word stoppack. Fail-closed to UNGROUNDED before retrieval/LLM (mirrors the quantifier reject-DAG) when no content token survives, else strips es stopwords from the retrieval query only. English path byte-identical by construction. Default OFF (crosslang_guard_enabled). Measured: the anarcocapitalismo field case 10.4s -> 1.6s. #000056 — Operation Sandwich (cross-language grounding) arborist/qa/mt/: opus-mt es/fr/ru<->en, lazy per-pair memoised singleton (fixes the 88%-engine-error concurrency defect), manifest-pinned, [mt] extra; entity_mask wrapper. Sandwich = translate query in (retrieval + LLM prompt) -> English answer -> UNTOUCHED verifier grounds English-vs-English -> translate the verified answer out as display-only (banner-labelled, zero grounding). question_hash + verifier_policy_hash invariant; MT engine identity binds into RetrievalPlan, not governance. CLI --crosslang-translate / make XLANG_MT=1. Default OFF; entity_mask default OFF (measured net-negative at bench scale). Fan-out bench (bench/*.py): Spanish ~0% -> 71% grounded vs the real no-support baseline; the round-trip predictor was tried and refuted; the entity-mask lever failed at scale (corpus-title anchoring untried). CLAUDE.md: cross-language bright-line convention + module map. Pre-existing modified diagram files are intentionally excluded.
This commit is contained in:
parent
b711215f11
commit
2c98fc964e
32 changed files with 4173 additions and 29 deletions
|
|
@ -111,6 +111,8 @@ Newest first. Update on every open/close.
|
|||
|
||||
| ID | Title | Status | Opened | Directive |
|
||||
|----------|------------------------------------------------|-----------------------|------------|-----------|
|
||||
| #000056 | Operation Sandwich — cross-language grounding via query+display MT | **implemented & landed 2026-05-17 · default-OFF** (fox: "call it operation sandwich, create a new ticket and finish it"). Mechanism + bright line **verified live end-to-end** (real Hermes + real opus-mt: es query → English answer+verifier → es display; `answer_text` English, `display_answer` Spanish additive, `question_hash`/`verifier_policy_hash` invariant; 6 tests + full suite 2477 passed, 0 regressions). **Fan-out measured (§9, n=1, 75 q):** EN baseline 85% → es+sandwich 71% = **−14pp cost**; transitions PRESERVED 31 / DOWNGRADE 16 / LOST 17 / N/A 11. A deterministic round-trip predictor was tried and **refuted** (12/17 LOST round-tripped CLEAN; another instance of the codified CLAUDE.md bench-maxing lesson — not re-added). LOST taxonomy from the artifact: ≈7 entity-translate (`Boltzmann`→"perntzmann", `Tarsus`→"Tarso"), ≈3 broad-enum, ≈several n=1 noise. **Lever built+validated:** `arborist/qa/mt/entity_mask.py` mask/restore (real opus-mt: `who is Paul of Tarsus?` "Pablo de Tarso"→**"Paul of Tarsus"**); default-ON within the default-OFF sandwich. Caveat: bench is lowercased so cap-detector lift is a **lower bound** (corpus-title anchor = v2). Also: fan-out caught + fixed an 88%-engine-error concurrency defect (per-call model load → memoised singleton + lazy per-pair); French + Russian breads added (manifest, `crosslang_source_lang`). **Lift measured 2026-05-17 (comparator corrected, fox):** the true baseline is the pre-ticket ≈0% (raw es query → song-title noise, UNGROUNDED, 10.4s) — NOT native English. Against that: **the sandwich is a large net win (≈0% → 71% es grounded, proof core never corrupted); the −14pp vs English is the cost of a new capability, not a regression — calling it a "fail" was a comparator error.** The genuine negative is the **entity-mask lever**: net-negative at scale (es 71→65, fr+mask 47/−38pp; isolated Paul-of-Tarsus win didn't replicate — 3rd bench-maxing-lesson instance), now **default-OFF** (`crosslang_entity_mask=False`); no-mask sandwich is the keeper. Recommendation flipped: **worth continuing (minus the mask)**, not park. Remaining: n=3, fr no-mask, corpus-title anchoring (only untried lowercase-capable detector). CLAUDE.md updated with the durable cross-lang *convention* only. Tasks #14–#17. Phase 1 of the #000001 §7 family; new ticket clears don't-proliferate (fox-directed + distinct Dav1d audience + architectural inflection: a model dependency `[mt]` + a presentation-translation layer — anticipated by #000001 §7's "split the `[mt]` model-distribution work like `[nli]`/vecpack"). **Sandwich:** translate query es→en (retrieval-side, == `--retrieval-keywords`, binds into `retrieval_plan_hash`, NOT `question_hash`) → English answer through the **byte-for-byte untouched verifier** → translate the verified English `answer_text` en→es into a NEW `display_answer` field, banner-labelled, zero grounding (the `_render_audit_label` render-projection pattern). Engine: local `[mt]` extra, Helsinki-NLP `opus-mt-es-en`/`-en-es`, Apache-2.0, hash-pinned, off-repo `~/.arborist/models/mt/`, optional dep, graceful-degrade — mirrors `[nli]`/`ShadowNLI` (#000049) + vecpack (#000051) verbatim; never Hermes-3-8B; not an external API (reproducibility + zero egress + es↔en is the best-resourced pair). Default OFF (`crosslang_translate_enabled`, gated under Phase-0 `crosslang_guard_enabled`); `--crosslang-translate` / `XLANG_MT=1`. Hash invariants (corrected 2026-05-17 — `governance_policy_hash` is sha256 of the *whole* policy, keys.py:182): `question_hash` + `verifier_policy_hash` untouched (user question preserved, verifier byte-identical); `governance_policy_hash` moves like every policy flag → correct cache partitioning by config (not a leak); MT engine identity binds into `RetrievalPlan.mt_*` (run-DAG), not a policy hash. | 2026-05-17 | — |
|
||||
| #000055 | Windows quickstart without `make` (`tasks.py` + `make.bat`) | **in progress** — opened 2026-05-16 (fox: "bat files or some shit … avoid needing makefile for windows … we will test the quickstart on windows"). Pure-stdlib `tasks.py` runner mirroring the **quickstart subset** of the Makefile (bootstrap / fetch-cur / ingest-cur-attached / distill ×2 / query / inspect / falsify / burn / bootstrap-crawler / crawl-ingest / stats / verify / search / clean) + a ~10-line `make.bat` shim so `make <target>` works in Windows cmd and `.\make.bat <target>` in PowerShell. Audited the artifact (not the docs): the 2003 dump is opened via stdlib `bz2` (no external `bzip2`); only `fetch-cur` used `curl` (→ stdlib `urllib`); bash `for…&wait` → `subprocess.Popen` fan-out; `arborist` console-script lands at `.venv\Scripts\arborist.exe`. Net: quickstart needs only **Python 3.10+ + sqlite3** — the repo's existing ethos, now true on native Windows. Same `KEY=VALUE` make-style args so documented commands translate 1:1 (one doc form). Makefile untouched, still canonical on POSIX ("keep it as an option"). Found + fixed a README/Makefile discrepancy: README claimed `[dev,html]` bootstrap extras, Makefile installs `.[dev]` — artifact wins. Drift-pinned by `tests/test_tasks_runner.py`. | 2026-05-16 | — |
|
||||
| #000054 | Acronym-parens concept extractor (closes the abbreviation→expansion retrieval gap) | **in progress** — Phase 1 (extractor + 481K edges) landed `58027e9`; Phase 2 (consumer-side surfacing — `synonym_expand` rank-and-truncate over the per-token cap, FTS5-`bm25` ordering in `_search_titles`, expanded `accept_tokens` in title-search + core-keyword + title-rerank, `synonym_expand_strict()` for the multiplicative title-purity rerank to exclude noisy `link_reciprocity` edges, tightened extractor regex to `[A-Z]{3,6}` purging 2-letter homonym edges) landed `ce855db`. **End-to-end verified:** `what is a CPU?` → Central processing unit at #1; `what is a GPU?` → Graphics processing unit at #1 EVIDENCE-WARRANTED 1/1; Mount Kilimanjaro / Soviet Union queries unchanged. **bench-qa n=3 limit=5** (2026-05-13T14:24Z): 30/45 STRICT (67%), zero regressions on basics (mona lisa / capital of france / new london bridge each 9/9 STRICT). 2026-05-13 — `arborist/concepts/extract.py:acronym_parens_synonym` lands as a new corpus-agnostic extractor in `EXTRACTORS` (`evidence_kind="acronym_parens"`). Scans each doc's lead chunk for `<Multi-Word Phrase> (ACRO)` where the all-caps acronym's letters match the content-word initials of the phrase in order; emits bidirectional synonym edges between the lowercased acronym and each ≥3-char content token of the phrase. Conservative (strict 1:1 initials, function words filtered, repeated definitions deduped per doc). Closes the *retrieval-side* abbreviation gap (`CPU↔central processing unit`, `GPU↔graphics processing unit`, `RAM↔random access memory`, `FBI↔federal bureau of investigation`, `WHO↔world health organization`, …) that `link_reciprocity_synonym` can't reach because the relation lives in body text, not the wiki link graph (Wikipedia represents abbreviation→expansion as a *redirect* — not an edge). Per-shard like all `concept_relations` data; corpus-agnostic so HTML/blogs/textbooks benefit equally. Retrieval-side only — never proof-path. 8 new tests; full suite green. Closes #000050 §2a's CPU/GPU fixture rows *upstream* of vec; the Orwell-shape conceptual-allusion row remains the genuine #000050 justification. Operational follow-up (not code): `arborist concepts derive --extractor acronym_parens` on each shard. | 2026-05-13 | — |
|
||||
| #000053 | Acronym-aware verifier content tokens | **closed · 2026-05-13** — `arborist.qa.evidence._content_tokens` now keeps all-caps 2-3-char acronyms (CPU/GPU/DNA/FBI/USB…) as content tokens instead of dropping every <4-char token; fixes the field case where "what is a CPU?" cited to "CPU design" tripped `TITLE_MISMATCH` spuriously (claim & title share "CPU" but neither registered) — also affects `SUBJECT_TOKENS_ABSENT` (Rule 9), `BARE_NAME_CLAIM`, spotlight-excerpt token pick. Versioned: `content_token_rules: "v2-acronym-aware"` in both default policies + `_VERIFIER_POLICY_FIELDS` → folds into `verifier_policy_hash`, prior cache records orphan on lookup (by design, same discipline as `base_version` / `hyphen_fold_v1`). Monotone toward *fewer* spurious demotes (only relaxes overlap checks, never tightens). 8 new tests; full suite green; `bench-qa-smoke` clean. Does NOT fix the *retrieval* abbreviation→expansion gap (`CPU`→`Central processing unit` = #000050 vec hybrid / `concepts/` synonym edges — the root cause of the satellite-article retrieval). | 2026-05-13 | — |
|
||||
| #000052 | Relevance + coherence meta-cognition (answer-*shape* signals) | in progress — **§3.1 `diagnose_coherence` landed** (lexical, no model: `circular` / `phrase_component_reuse` / `vacuous`; in `arborist/qa/inspect.py`, surfaced via `inspect_cache_key` + `arborist inspect` `· incoherent: <kind>`; 9 tests; demote-policy hook deliberately not wired — advisory only). Joins the `diagnose_deflection` / `diagnose_metaphor_deflection` / `diagnose_title_relevance` / soft-preflight family of read-only, demote-only, never-in-proof-path sidecars; `phrase_component_reuse` catches the motivating field case (a subject quoting a phrase, a predicate reusing one of that phrase's own tokens as a bare `the <token>` referent). **Still open: (2) `diagnose_relevance`** — semantic (not just lexical) "aboutness": does the answer address the question; is each claim about its cited source? Today's checks (subject-anchor token overlap, stemmed title-stem overlap) are *lexical* and a token collision defeats them — a small *aboutness/reranker* model (NOT NLI — entailment ≠ topicality) under #000049 §7's discipline cage verbatim (demotion-only, hash-pinned, `relevance_model_version`→`governance_policy_hash` iff it touches `audit_mode`, shadow-first, `[…]` extra, the §7 #20 haystack lesson — never over the whole context); gated on evidence, travels with #000049's model question. Motivating field case (2026-05-12, fox): the `claim_lattice` query that returned *"the phrase 'Zionist entity' is sometimes used as the entity, referring to the State of Israel"* at `EVIDENCE-WARRANTED-PARTIAL 2/3` — incoherent + token-collision recombination that NLI can't catch (returns *neutral*, not *contradiction*) and both lexical relevance checks waved through. Flags an upstream retrieval ticket (polysemy / title-token-soup) as the root-cause fix, not scoped here. #000049 sibling | 2026-05-12 | — |
|
||||
|
|
@ -164,8 +166,8 @@ Newest first. Update on every open/close.
|
|||
| #000004 | Directive coverage in bench summary | closed · `acd1f9c` | 2026-05-01 | D8 |
|
||||
| #000003 | Anchor-class warrant generalization (Module H+)| closed · 2026-05-02 | 2026-05-01 | D6 |
|
||||
| #000002 | Reference-Frame Polarity Contract (Module L) | closed · 2026-05-02 | 2026-05-01 | D3 |
|
||||
| #000001 | Retrieval-keywords audit gap | closed · 2026-05-02 | 2026-05-01 | D4 |
|
||||
| #000001 | Retrieval-keywords audit gap (+ §7 cross-language transforms) | **reopened 2026-05-17 (in progress)** — keyword scope stays closed/landed 2026-05-02 (run-DAG `RetrievalPlan` binding, §5). §7 extends the *same* retrieval-transform-provenance substrate to a sibling: cross-language query bridging. Strict bright line — MT/aliases/stopword-guards *propose* candidates, only source-language evidence *warrants*; English answer verified English-vs-English by the **untouched binary verifier**, Spanish is display-only (zero grounding, banner-labelled). Held strictly this needs **no new `audit_mode` token, no `EVIDENCE-WARRANTED-CANONICAL`, no `surface_language` field, no `providence_cache` column, no new run-DAG stage** (the >10%-back deletion; CLAUDE.md schema-column-unchanged + verifier-stays-binary). **Phase 0 landed 2026-05-17** — `arborist/qa/crosslang.py` (deterministic, no model: `¿`/`¡`/non-ASCII signal, es-v1 stoppack); pre-preflight fail-closed mirroring `quantifier_should_reject` (Merkle-auditable reject DAG, `CROSS_LANGUAGE_UNSUPPORTED`, no LLM) + es-stoppack strip on `retrieval_query` only. Measured: `¿Qué es el anarcocapitalismo?` **10.4 s → 1.6 s** (~6.4×, still honest UNGROUNDED); English control byte-identical (by construction — `guard()` returns None for pure-ASCII). 19 tests; full suite 2470 passed, 0 regressions (incl. #000053/#000054); `bench-qa-smoke` stable anchor 3/3 STRICT. No schema change, no new governance/verifier *fields*, no `RetrievalPlan` change; `question_hash` + `verifier_policy_hash` untouched (the enabling flag moves `governance_policy_hash` like every policy flag — whole-policy hash, correct cache partition; corrected 2026-05-17, authoritative in #000056 §2 #6). `RetrievalPlan` MT extension landed in #000056 (Phase 1). **Feature-flagged default-OFF** (fox request): `policy["crosslang_guard_enabled"]` gates both seams via one point → flag-off reverts byte-for-byte to legacy (clean A/B); surface `arborist query --crosslang-guard` / `make query XLANG=1` / `tasks.py XLANG=1`. Default-flip is a separate bench-gated fox decision (rollout discipline matches #000008/#000011/#000049). 20 tests; full suite 2471 passed. **Collision constraint:** the guard is a specific source-language function-word stoppack, NOT a len≤2 heuristic — a blanket short-token drop regresses #000053/#000054 (`AI`/`ML`/`CPU`/`GPU` are load-bearing); `_FTS5_STOPWORDS`/`_TITLE_STOPWORDS` stay in sync; n=3 English bench, no >5pp regression. **Phase 1 split out → #000056 "Operation Sandwich"** (fox-directed; MT provider decided = local `[mt]` opus-mt, hash-pinned, not Hermes/not-API). Deleted by five-step (recorded in §7.6): raw-Spanish route (measured noise vs `en`), alias-map substrate (hand-curated worse-MT), cross-modal/multilingual-vec/SQD-language/ABCDEFG-5S (no named defect — the retired `arborist/v7/` anti-pattern). | 2026-05-01 | D4 |
|
||||
|
||||
## Next ID
|
||||
|
||||
`000055`
|
||||
`000057`
|
||||
|
|
|
|||
|
|
@ -1,15 +1,21 @@
|
|||
# Ticket #000001 — Retrieval-keywords audit gap
|
||||
|
||||
**Status:** closed · landed 2026-05-02 (run-DAG binding scope; SQL
|
||||
column + audit-events scope deferred per §6 below)
|
||||
**Status:** reopened 2026-05-17 (in progress) — keyword scope stays
|
||||
closed/landed 2026-05-02 (run-DAG binding, §5; SQL column + audit-events
|
||||
deferred per §6). §7 extends the *same retrieval-transform-provenance
|
||||
substrate* (`arborist/qa/retrieval_plan.py`) to a sibling transform:
|
||||
cross-language query bridging. Doc-only; gated on the MT-provider
|
||||
decision (§7.5).
|
||||
**Opened:** 2026-05-01
|
||||
**Closed:** 2026-05-02
|
||||
**Scope:** Design proposal for capturing the `--retrieval-keywords` operator
|
||||
hint in the v9.8 audit chain so retrieval is fully reproducible from a
|
||||
providence record alone. Doc-only — no code in this commit.
|
||||
**Closed:** 2026-05-02 (keyword scope) · **Reopened:** 2026-05-17 (§7)
|
||||
**Scope:** Capturing retrieval-side transforms (operator keywords §1–§6;
|
||||
cross-language bridging §7) in the v9.8 audit chain so retrieval is
|
||||
reproducible from a providence record alone. Doc-only.
|
||||
**Audience:** fox + future blackops shifts.
|
||||
**Hard constraint:** keywords stay operator-metadata, not part of the user's
|
||||
question. Cache-key dimensionality stays at 8.
|
||||
**Hard constraint:** retrieval transforms (keywords, MT, stopword
|
||||
guards) stay operator/system metadata, never part of the user's
|
||||
question. Cache-key dimensionality stays at 8. The verifier stays
|
||||
binary; no new `audit_mode` token, no `providence_cache` column.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -288,6 +294,232 @@ in directly) stays in the original proposal as future work.
|
|||
|
||||
---
|
||||
|
||||
## 7. Phase X — Cross-language retrieval transforms (reopened 2026-05-17)
|
||||
|
||||
**Why here, not a new ticket.** MT-to-corpus-language and a
|
||||
multilingual stopword guard are *retrieval transforms* — the same
|
||||
family as `--retrieval-keywords`. §5 already shipped the substrate:
|
||||
`RetrievalPlan` + `retrieval_plan_hash`, run-DAG-bound, outside
|
||||
`question_hash`/`cache_key`. A cross-language transform is captured by
|
||||
*extending the `RetrievalPlan` fields*, not by inventing a new
|
||||
provenance mechanism. Per the don't-proliferate discipline this is
|
||||
§000001 Phase X, not #0000XX. (The 2026-05-16 field case:
|
||||
`¿Qué es el anarcocapitalismo?` over the en-2003 corpus burned 10.4 s
|
||||
assembling title-collision noise — *Así Es El Amor*, *¿Quién es el
|
||||
Jefe?* — then correctly returned UNGROUNDED. The fail-closed was
|
||||
right; the 10.4 s waste and the missing recall bridge are the gap.)
|
||||
|
||||
### 7.1 Hard constraints (the bright line — strict form deletes a design layer)
|
||||
|
||||
```text
|
||||
MT / aliases / stopword guards → may PROPOSE retrieval candidates
|
||||
source-language evidence → the ONLY thing that WARRANTS
|
||||
```
|
||||
|
||||
Valid: Spanish query → MT → **English** retrieval → **English** answer
|
||||
→ **existing untouched binary verifier** (English-vs-English) →
|
||||
existing label, unchanged. Spanish text, if rendered, is **display
|
||||
only**, carries zero grounding, and is banner-labelled
|
||||
"machine-translated, not verified" — the `_render_audit_label`
|
||||
pattern (render projection ≠ proof).
|
||||
|
||||
Holding this strictly is the >10%-back deletion: there is **no new
|
||||
`audit_mode` token, no `EVIDENCE-WARRANTED-CANONICAL`, no
|
||||
`surface_language` proof field, no `providence_cache` column, no new
|
||||
run-DAG stage.** CLAUDE.md: *schema column stays unchanged; verifier
|
||||
stays binary; audit_mode decided by the verifier, never asserted.*
|
||||
|
||||
Forbidden (→ the #000049 model-in-proof-path cage, not here):
|
||||
Spanish answer → translate-to-English → verify → call it grounded.
|
||||
|
||||
### 7.2 Collision constraint (must not regress shipped work)
|
||||
|
||||
A noise guard **must be a specific source-language function-word
|
||||
stoppack** (es v1: `qué que es el la los las un una de del en y o a`),
|
||||
**not** a length / "drop len≤2" heuristic. A blanket short-token drop
|
||||
regresses #000053 (acronym-aware verifier content tokens) and #000054
|
||||
(acronym_parens edges) — `AI`/`ML`/`CPU`/`GPU`/`DNA`/`FBI` are
|
||||
load-bearing 2–3-char tokens. `_FTS5_STOPWORDS` and `_TITLE_STOPWORDS`
|
||||
**stay in sync** (CLAUDE.md retrieval-pipeline §9). Bench-gated:
|
||||
`make bench-qa` n=3, English suite, no >5pp STRICT-rate regression
|
||||
before anything lands.
|
||||
|
||||
### 7.3 Phase 0 — grounded design (2026-05-17, real-code pass)
|
||||
|
||||
Mapping `arborist/qa/query.py` + `arborist/search/fts5.py` corrected
|
||||
four points in the draft. The refinements make Phase 0 *smaller*:
|
||||
|
||||
1. **`_FTS5_TOKEN_RE = [A-Za-z][A-Za-z0-9]*` is ASCII-only.** `¿Qué`
|
||||
tokenizes to `Qu`; accented words are truncated before the
|
||||
stopword check. The es stoppack must therefore list ASCII-folded /
|
||||
truncated forms: `qu que es el la los las un una de del en y o a`.
|
||||
2. **The measured 9.9 s cost was the search, not context assembly**
|
||||
(`search 9.91s` · `context 0.05s`). The cost was OR-mode FTS5
|
||||
driven by the es function words `es`/`el` (high-DF, full-corpus
|
||||
union scan). So Phase 0's primary lever is **stripping the es
|
||||
stoppack from the retrieval token set**, not the fail-closed —
|
||||
the field case (`anarcocapitalismo` survives the stoppack) is a
|
||||
*content token that misses the corpus*, not a no-content query;
|
||||
stripping `es`/`el` makes the search fast (single rare token, no
|
||||
OR scan) → honest `UNGROUNDED` in ~ms instead of 10.4 s.
|
||||
3. **The es stoppack must be language-scoped, never global.**
|
||||
`un`→UN, `la`→LA, `de`→De, `en`→EN collide with the #000053/#000054
|
||||
acronym/abbrev class. Gate it behind a deterministic
|
||||
no-model **non-English signal**: the query contains inverted
|
||||
punctuation (`¿`/`¡`) or a non-ASCII Latin letter. Pure-ASCII
|
||||
English queries never trigger it → the English retrieval path is
|
||||
**byte-identical by construction** (the bench is a no-op by proof,
|
||||
not just by measurement).
|
||||
4. **Phase 0 needs no `RetrievalPlan` change.** The explicit
|
||||
fail-closed returns *before* `RetrievalPlan` is built (it reuses
|
||||
the existing `build_reject_run_dag` 3-stage path, exactly like
|
||||
`quantifier_should_reject`). The reason is recorded as a
|
||||
`CROSS_LANGUAGE_UNSUPPORTED` violation in the reject run-DAG
|
||||
(mirroring `BROAD_QUANTIFIER_REJECTED`). Extending `RetrievalPlan`
|
||||
with `detected_language`/`dropped_tokens` is a **Phase 1**
|
||||
concern (when MT retrieval actually runs and the plan is built).
|
||||
|
||||
**Phase 0 behaviour, final:** at `query()` entry, if the non-English
|
||||
signal (refinement 3) fires: strip the es/multilingual stoppack from
|
||||
the retrieval token set for this call only (refinement 1); if **zero**
|
||||
content tokens remain, short-circuit to `UNGROUNDED` before retrieval
|
||||
via the reject-DAG path (refinement 4); otherwise run retrieval on the
|
||||
surviving content tokens (fast — refinement 2) and let the existing
|
||||
pipeline return its honest verdict. Adds **no new governance/verifier
|
||||
fields** and **no `RetrievalPlan` change**; `question_hash` and
|
||||
`verifier_policy_hash` untouched; English path byte-identical.
|
||||
(Correction 2026-05-17: an earlier draft said "no governance change"
|
||||
unqualified — false. `governance_policy_hash` is sha256 of the
|
||||
*whole* policy (`keys.py:182`), so toggling the `crosslang_guard_enabled`
|
||||
flag moves it like every policy flag. That is correct cache
|
||||
partitioning, not a leak. Authoritative statement: #000056 §2 #6.)
|
||||
|
||||
### 7.4 Phase 1 — MT-to-corpus-language as one soft route (gated)
|
||||
|
||||
MT-translated query == `--retrieval-keywords` with an MT front-end:
|
||||
folds into `retrieval_plan_hash` exactly like keywords (the §2.4 /
|
||||
§A.5 decision verbatim — **A+B, not C**; translated query is
|
||||
retrieval metadata, **never** `question_hash`). Governance: the MT
|
||||
*engine identity* binds into `RetrievalPlan` (run-DAG), not a policy
|
||||
hash; the *enabling flag* moves `governance_policy_hash` like every
|
||||
policy flag (whole-policy hash — correct cache partition). **This
|
||||
§7.4 sketch is superseded by #000056 (Operation Sandwich), which is
|
||||
the authoritative Phase-1 design + the corrected hash invariants
|
||||
(§2 #6).** Raw `es` and raw `en`
|
||||
questions keep distinct `question_hash` (no cross-language dedup).
|
||||
|
||||
### 7.5 The gate (fox decides — nothing in 7.4 lands until then)
|
||||
|
||||
MT provider. **Not Hermes-3-8B** (blackops standing rule: 8B unfit
|
||||
for human-language translation; cannibalizes coder-agent budget).
|
||||
Fork: **Grok API** (flagged candidate; soft-route only;
|
||||
provider/version/prompt into `governance_policy_hash`) **vs** a local
|
||||
**`[mt]` extra** (NLLB/Marian, hash-pinned like `[nli]`, weights
|
||||
off-repo under `~/.arborist/models/mt/<hash>`). Phase 1 stays
|
||||
doc-only until chosen.
|
||||
|
||||
### 7.6 Deleted by the five-step (named, with the reason)
|
||||
|
||||
- **Raw-Spanish retrieval route** — measured pure noise vs an `en`
|
||||
corpus (the song-title case). Revisit *only* when a Spanish/mixed
|
||||
shard exists; not a v1 route.
|
||||
- **Deterministic alias-map substrate** — a hand-curated, worse MT
|
||||
with no named maintainer; strictly dominated by a Spanish corpus
|
||||
or the MT route. Cut.
|
||||
- **New labels / `EVIDENCE-WARRANTED-CANONICAL` / `surface_language`
|
||||
proof field / two-level proof identity** — label + schema
|
||||
proliferation the strict §7.1 line makes unnecessary.
|
||||
- **Cross-modal carriers, multilingual vec, SQD language
|
||||
canonicalizers, a new `language_bridge` run-DAG stage, 5S/ABCDEFG
|
||||
suites** — no named person, no closed defect (five-step step 1).
|
||||
Namespace-reservation now = the retired `arborist/v7/` anti-pattern.
|
||||
|
||||
### 7.7 Acceptance criteria (Phase 0 testable now; Phase 1 post-gate)
|
||||
|
||||
1a. Cross-language query whose tokens are **all** function words →
|
||||
`UNGROUNDED` **before** retrieval/LLM via the reject-DAG path;
|
||||
`CROSS_LANGUAGE_UNSUPPORTED` violation recorded.
|
||||
1b. Cross-language query with a surviving (corpus-missing) content
|
||||
token (the `anarcocapitalismo` field case) → es function words
|
||||
stripped from the retrieval set → **no OR-mode full-corpus
|
||||
scan** (search ≪ the measured 9.9 s) → honest `UNGROUNDED`.
|
||||
2. Source-language function words do not drive FTS5 into
|
||||
title-collision noise.
|
||||
3. `make bench-qa` n=3 English suite: **no >5pp STRICT-rate
|
||||
regression** (the #000053/#000054 guard).
|
||||
4. MT route (when enabled) folds into `retrieval_plan_hash`, **not**
|
||||
`question_hash`.
|
||||
5. `governance_policy_hash` changes **iff** MT enabled AND cacheable;
|
||||
unchanged otherwise.
|
||||
6. **No** new `audit_mode` token; **no** `providence_cache` column;
|
||||
verifier byte-identical.
|
||||
7. Raw `es` and raw `en` of the same question keep distinct
|
||||
`question_hash`.
|
||||
|
||||
### 7.8 Status
|
||||
|
||||
**Phase 0 landed 2026-05-17.** New module `arborist/qa/crosslang.py`
|
||||
(deterministic, no model: `guard()` + `strip_for_retrieval()`,
|
||||
es-v1 stoppack, non-English signal = `¿`/`¡`/non-ASCII letter).
|
||||
Two seams in `arborist/qa/query.py`: a pre-preflight fail-closed
|
||||
early-return mirroring `quantifier_should_reject` (reuses
|
||||
`build_reject_run_dag` → 3-stage Merkle-auditable reject DAG,
|
||||
`CROSS_LANGUAGE_UNSUPPORTED` violation, `status=
|
||||
cross_language_unsupported`, no LLM), and an es-stoppack strip on
|
||||
`retrieval_query` only (LLM / verifier / `question_hash` untouched).
|
||||
No schema change, no new governance/verifier *fields*, no
|
||||
`RetrievalPlan` change; `question_hash` + `verifier_policy_hash`
|
||||
untouched. (The enabling flag moves `governance_policy_hash` like
|
||||
every policy flag — whole-policy hash, correct cache partition; see
|
||||
#000056 §2 #6 for the authoritative corrected statement.)
|
||||
|
||||
Measured (live, `query-dry`): `¿Qué es el anarcocapitalismo?`
|
||||
**10.4 s → 1.6 s** (search 9.91 s → 1.26 s, ~6.4×), still honest
|
||||
UNGROUNDED; `¿Qué es el?` → `cross_language_unsupported` pre-retrieval;
|
||||
English control (`What is anarcho-capitalism?`) byte-identical
|
||||
(Anarcho-capitalism #1, unchanged). 19 new tests in
|
||||
`tests/test_crosslang_guard.py`; full suite 2470 passed, 0
|
||||
regressions (incl. query/dag/claim_lattice/#000053/#000054);
|
||||
`bench-qa-smoke` stable anchor (`mona lisa`) 3/3 STRICT. English
|
||||
inertness is **by construction** (the signal regex cannot match a
|
||||
pure-ASCII no-`¿`/`¡` query → `guard()` returns None → identical
|
||||
path), so the bench is a proof, not just a measurement.
|
||||
|
||||
**Feature-flagged (fox request, 2026-05-17): default OFF.**
|
||||
`policy["crosslang_guard_enabled"]` (default `False`) gates BOTH
|
||||
seams via a single point — flag off → `_xlang` is None → behaviour
|
||||
reverts byte-for-byte to pre-§7, a clean A/B baseline. Surface:
|
||||
`arborist query --crosslang-guard`, `make query XLANG=1`,
|
||||
`tasks.py query XLANG=1`. Rollout discipline matches #000008
|
||||
(quantifier) / #000011 (soft-preflight) / #000049 (NLI): ships
|
||||
default-OFF; the default-flip is a separate **bench-gated fox
|
||||
decision**, not autonomous — even though Phase 0 is provably inert
|
||||
on English and strictly improves the cross-language case (the
|
||||
argument *for* a future flip, deferred to fox). Retrieval-side
|
||||
only: the flag does NOT enter `question_hash` or
|
||||
`verifier_policy_hash` (user question preserved, verifier
|
||||
byte-identical). It DOES move `governance_policy_hash` like every
|
||||
policy flag (whole-policy hash, `keys.py:182`) — correct cache
|
||||
partitioning by guard state, not a leak; the fail-closed path writes
|
||||
no cache row anyway. (Correction 2026-05-17; authoritative: #000056
|
||||
§2 #6.) Live
|
||||
A/B verified: `¿Qué es el?` flag-OFF → legacy UNGROUNDED;
|
||||
flag-ON → `cross_language_unsupported` pre-retrieval. +1 test
|
||||
(`test_flag_off_reverts_to_legacy_behaviour`); 20 tests total;
|
||||
full suite 2471 passed.
|
||||
|
||||
**Phase 1 split out → #000056 "Operation Sandwich" (fox-directed
|
||||
2026-05-17).** MT-provider decided: local `[mt]` extra,
|
||||
`opus-mt-es-en`/`-en-es`, hash-pinned (not Hermes, not an API). The
|
||||
query+display MT sandwich, the `RetrievalPlan` MT-field extension
|
||||
(the §7.3-deferred item), and the `[mt]` model dependency all live
|
||||
in #000056 — split per the don't-proliferate "distinct Dav1d
|
||||
audience / architectural inflection" criterion (a model dependency +
|
||||
a presentation layer), exactly the split this section anticipated.
|
||||
#000001 §7 stays the Phase-0 home.
|
||||
|
||||
---
|
||||
|
||||
## Appendix A — Architectural review (2026-05-01, Asia/Kuala_Lumpur)
|
||||
|
||||
> Fox-supplied review expanding §2-§4 with axiomatic framing,
|
||||
|
|
|
|||
113
docs/tickets/ticket-000055-windows-quickstart-no-make.md
Normal file
113
docs/tickets/ticket-000055-windows-quickstart-no-make.md
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
# Ticket #000055 — Windows quickstart without `make` (`tasks.py` + `make.bat`)
|
||||
|
||||
**Status:** in progress — opened 2026-05-16, implementation in same commit.
|
||||
**Opened:** 2026-05-16
|
||||
**Asked by:** fox ("we need bat files or some shit so we could avoid
|
||||
needing makefile for windows (but keep it as an option) windows uses
|
||||
py py3 for python3 etc we will test the quickstart on windows so fix
|
||||
it up").
|
||||
**Scope:** One new pure-stdlib runner `tasks.py` at repo root + one
|
||||
small `make.bat` Windows shim + README Windows/Quickstart rewrite +
|
||||
one smoke/drift test. **No** change to `arborist/`, the schema, the
|
||||
Makefile recipes, or any audit/proof path. The Makefile stays the
|
||||
canonical Unix entry point and is left byte-for-byte unchanged ("keep
|
||||
it as an option").
|
||||
**Audience:** fox + anyone running the quickstart on native Windows +
|
||||
maintainers who touch the quickstart and must keep two thin entry
|
||||
points in sync.
|
||||
|
||||
---
|
||||
|
||||
## 1. The gap, from the field
|
||||
|
||||
`README.md` Setup §Windows currently says native Windows is *not*
|
||||
supported — WSL2 only — because the Makefile uses bash idioms
|
||||
(`for i in $(seq …); do … & done; wait`, `sed`, backgrounding) and
|
||||
GNU-make conditionals, and the docs assume `make`, `curl`, `bzip2`.
|
||||
|
||||
fox wants the **quickstart** runnable on native Windows (cmd /
|
||||
PowerShell), no `make`, no WSL. Windows ships the `py` launcher
|
||||
(`py -3`), not `python3`.
|
||||
|
||||
## 2. What actually blocks Windows (and what does not)
|
||||
|
||||
Audited the quickstart targets against the artifact, not the docs:
|
||||
|
||||
- **Decompression:** the 2003 dump is opened with Python's stdlib
|
||||
`bz2` (`arborist/sources/wikipedia.py:288`). **No external `bzip2`
|
||||
needed** — the README's "needs … `bzip2`" line is stale for the
|
||||
quickstart path.
|
||||
- **Download:** the only `curl` use in the quickstart is `fetch-cur`.
|
||||
Replaceable with stdlib `urllib.request` → no `curl` dependency.
|
||||
- **Sharding/distill loops:** bash `for … & wait`. Replaceable with
|
||||
`subprocess.Popen` fan-out + wait.
|
||||
- **CLI:** `arborist` is a console script (`arborist.cli:main`,
|
||||
`pyproject.toml:120`). On a Windows venv it lands at
|
||||
`.venv\Scripts\arborist.exe`; on POSIX `.venv/bin/arborist`.
|
||||
- **Net:** after this, the quickstart needs only **Python 3.10+ and
|
||||
sqlite3** (sqlite3 ships with CPython) — exactly the repo's
|
||||
"fresh checkout needs only python3.12 + venv + sqlite3" ethos.
|
||||
No `make`, no `bzip2`, no `curl`, no `bash`.
|
||||
|
||||
## 3. Design — why one runner, not N `.bat` files
|
||||
|
||||
Five-step algorithm, step 2 (delete the part): hand-porting ~20 bash
|
||||
recipes into brittle per-target `.bat` scripts would (a) duplicate
|
||||
logic 1:1 with the Makefile in a second fragile dialect, (b)
|
||||
proliferate files, (c) drift fast (DRY-in-context). Instead:
|
||||
|
||||
- **`tasks.py`** — one pure-stdlib runner implementing the quickstart
|
||||
*subset only*. Cross-platform (also runs on POSIX as
|
||||
`python3 tasks.py …`, but the Makefile remains the documented Unix
|
||||
path). Detects venv layout (`Scripts` vs `bin`) and the Python
|
||||
launcher (`py -3` on Windows, `python3` on POSIX; `ARBORIST_PYTHON`
|
||||
overrides). Accepts the **same `KEY=VALUE` token style as `make`**
|
||||
(`query Q="…" JSON=1`, `inspect KEY=… JSON=1`, `crawl-ingest
|
||||
URL=… DEPTH=2`) so the documented commands translate 1:1 — one doc
|
||||
form, both platforms.
|
||||
- **`make.bat`** — a ~10-line dispatcher so `make <target>` keeps
|
||||
working in Windows `cmd` (cmd searches the current dir for
|
||||
`make.bat`) and `.\make.bat <target>` in PowerShell. It only
|
||||
resolves a Python (`py -3`, fallback `python`) and forwards argv to
|
||||
`tasks.py`. No logic in the `.bat`.
|
||||
|
||||
This keeps the Windows surface to **two files, one of them Python**,
|
||||
mirrors the existing "Makefile is build glue, logic is Python"
|
||||
precedent, and gives Windows the exact `make`-style UX.
|
||||
|
||||
`tasks.py` is the quickstart subset, not the full 1170-line Makefile.
|
||||
Bench/π*/textbook/docs targets stay Makefile-only (Unix/WSL2). The
|
||||
runner's `help` enumerates exactly the supported targets so the
|
||||
boundary is self-documenting.
|
||||
|
||||
## 4. Targets covered
|
||||
|
||||
`bootstrap fetch-cur ingest-cur-attached distill-shards-parallel
|
||||
distill-shards-tfidf-parallel query query-dry inspect falsify burn
|
||||
burn-kindergarten bootstrap-crawler crawl-ingest recrawl-check stats
|
||||
stats-shards verify search test help clean clean-db clean-data` —
|
||||
the full README Quickstart (Wikipedia + crawl paths), "After the
|
||||
answer", and Setup blocks.
|
||||
|
||||
## 5. Discrepancy found (reported to fox)
|
||||
|
||||
`README.md` Setup §Bootstrap claims bootstrap installs the
|
||||
`[dev,html]` extras; `Makefile:52` installs `.[dev]` only. Artifact
|
||||
wins (CLAUDE.md "artifact over instruction"): `tasks.py bootstrap`
|
||||
installs `.[dev]` to match the Makefile. README corrected in the same
|
||||
commit.
|
||||
|
||||
## 6. Drift control
|
||||
|
||||
`tests/test_tasks_runner.py`: importable on the host platform;
|
||||
`help` lists exactly the documented quickstart targets (a removed or
|
||||
renamed target fails the test); venv-layout + launcher detection
|
||||
verified for both `os.name == "nt"` and `"posix"` via monkeypatch.
|
||||
Two thin entry points are a known DRY cost — the test pins the target
|
||||
set so divergence from the documented quickstart is loud.
|
||||
|
||||
## 7. Out of scope
|
||||
|
||||
Full Makefile parity on Windows; bench/π*/NLI/textbook/docs targets;
|
||||
changing the Unix workflow (Makefile untouched, still canonical on
|
||||
POSIX).
|
||||
|
|
@ -0,0 +1,337 @@
|
|||
# Ticket #000056 — Operation Sandwich (cross-language grounding via query+display MT)
|
||||
|
||||
**Status:** implemented & landed 2026-05-17 — mechanism + bright
|
||||
line verified live end-to-end (§8); 6 tests + full suite 2477
|
||||
passed. **Default OFF** (`crosslang_translate_enabled`, gated under
|
||||
Phase-0 `crosslang_guard_enabled`). NOT "cross-language solved":
|
||||
the live flagship run is still `UNGROUNDED` (opus-mt
|
||||
output↔title-normalisation recall gap, §8 limit 1) — recall tuning +
|
||||
JSON-display polish (§8 limit 2) + SHA-pinning the manifest are open
|
||||
follow-ups behind the experiment flag; a bench-gated default-flip is
|
||||
a fox decision. Phase 1 of the #000001 §7 family.
|
||||
**Opened:** 2026-05-17
|
||||
**Asked by:** fox ("yes call it operation sandwich create a new
|
||||
ticket and finish it") — closing the chain that started with the
|
||||
2026-05-16 `¿Qué es el anarcocapitalismo?` field case.
|
||||
|
||||
**Why a new ticket (clears the don't-proliferate bar).** CLAUDE.md
|
||||
default is *extend*, split only when the piece needs a **distinct
|
||||
Dav1d-reviewable audience** or is an **architectural inflection**.
|
||||
Operation Sandwich is both: it introduces a **model dependency**
|
||||
(`[mt]`) and a **presentation-translation layer** — a self-contained
|
||||
design decision a de-novo review must read independently of #000001's
|
||||
retrieval-keywords provenance story. #000001 §7 explicitly anticipated
|
||||
this split ("If the MT model choice becomes large, split the
|
||||
model-distribution work into a later `[mt]` ticket, similar to
|
||||
`[nli]` and vecpack"). #000001 §7 stays the Phase-0 home; this is
|
||||
Phase 1. Cross-linked both ways.
|
||||
|
||||
**Audience:** fox + a Dav1d de-novo review of the
|
||||
translation-in-the-pipeline boundary + maintainers of the verifier
|
||||
proof path.
|
||||
|
||||
---
|
||||
|
||||
## 1. The sandwich
|
||||
|
||||
```
|
||||
Spanish query ─▶ [MT es→en] ─▶ English retrieval + English answer + EXISTING verifier ─▶ [MT en→es] ─▶ Spanish display
|
||||
translator IN ←—— grounded core, byte-for-byte untouched ——→ translator OUT
|
||||
(retrieval-side) (English-vs-English — the proof) (presentation-only)
|
||||
```
|
||||
|
||||
Translation lives on the **two edges**, never the middle. The
|
||||
verifier, `audit_mode`, `cache_key`, `question_hash`,
|
||||
`governance_policy_hash` all see only the English core. The Spanish
|
||||
the user reads is a labelled rendering carrying **zero** grounding.
|
||||
|
||||
## 2. Hard constraints (the bright line, as code invariants)
|
||||
|
||||
1. **`answer_text` stays the grounded English string.** The verifier
|
||||
runs English-answer-vs-English-source, unchanged byte-for-byte.
|
||||
2. **The Spanish rendering goes in a NEW field** (`display_answer`,
|
||||
`display_lang`, `display_translated=True`,
|
||||
`display_unverified_banner`). It is never the verifier's input,
|
||||
never what `audit_mode`/STRICT attaches to, never hashed into the
|
||||
proof. Same discipline as `cli._render_audit_label` (render
|
||||
projection ≠ proof).
|
||||
3. **Query MT is retrieval-side, == `--retrieval-keywords`.** It
|
||||
changes which sources rank (→ `context_root` → `cache_key`
|
||||
indirectly, exactly the #000001 §5/§6 story), **not**
|
||||
`question_hash`. The MT engine identity binds into the **run-DAG
|
||||
retrieval stage** via the existing `retrieval_plan_hash` (the
|
||||
§7.3-deferred `RetrievalPlan` extension lands here).
|
||||
4. **No translated answer ever re-enters the verifier** (the
|
||||
forbidden path / #000049 model-in-proof-path cage). Display MT is
|
||||
output-only and one-directional.
|
||||
5. **Default OFF**, gated under `crosslang_guard_enabled`. The MT
|
||||
engine is the optional `[mt]` extra; absent → degrade to
|
||||
`available=False` → the sandwich silently no-ops (query proceeds
|
||||
as Phase-0). English path byte-identical by construction.
|
||||
6. **Hash invariants (corrected 2026-05-17 — artifact over my own
|
||||
prose).** `governance_policy_hash` is `sha256` of the *whole*
|
||||
policy dict (`arborist/qa/keys.py:182`), so — like **every**
|
||||
policy flag (quantifier, metacognition, soft-preflight) — the
|
||||
crosslang flags DO move it. An earlier draft of this section
|
||||
wrongly said "governance untouched"; that was false and is
|
||||
corrected here. The behaviour is *correct*: a sandwich-on answer
|
||||
must not be served to a sandwich-off lookup — the cache partitions
|
||||
by config, it does not leak. The load-bearing invariants are the
|
||||
two that ARE untouched: **`question_hash`** (the user's Spanish
|
||||
question is preserved verbatim — MT never rewrites what was asked)
|
||||
and **`verifier_policy_hash`** (the verifier is byte-identical;
|
||||
the crosslang flags are absent from `_VERIFIER_POLICY_FIELDS`).
|
||||
The MT *engine identity* binds into the run-DAG retrieval plan
|
||||
(`RetrievalPlan.mt_*`), which is not a policy hash. Test:
|
||||
`test_sandwich_hash_invariants`.
|
||||
|
||||
## 3. Engine — local `[mt]` extra (fox-decided 2026-05-17)
|
||||
|
||||
`opus-mt-es-en` (query in) + `opus-mt-en-es` (display out),
|
||||
Helsinki-NLP, Apache-2.0, hash-pinned by HF revision, weights
|
||||
off-repo under `~/.arborist/models/mt/`, optional dependency. **Not
|
||||
Hermes-3-8B** (blackops rule: 8B unfit for human-language
|
||||
translation). **Not an external API**: reproducibility (a pinned
|
||||
checkpoint replays byte-identically — provenance-critical for a
|
||||
Merkle-replay system) + zero egress (Operation Voyeur / sovereignty)
|
||||
+ es↔en is the best-resourced MT pair so the local-vs-frontier
|
||||
quality gap is smallest exactly here. Grok stays the documented
|
||||
future bench-comparison candidate (blackops: "evaluate before
|
||||
committing spend"), not the v1 substrate. Mirrors the `[nli]` /
|
||||
`ShadowNLI` pattern (#000049) and the vecpack off-box pattern
|
||||
(#000051) exactly — zero new architectural surface.
|
||||
|
||||
## 4. Build
|
||||
|
||||
- `arborist/qa/mt/` — `manifest.json` (pinned es-en + en-es repos +
|
||||
revisions), `Translator` protocol, `OpusMTTranslator` (lazy
|
||||
`transformers` import, `available` flag, graceful degrade — the
|
||||
`ShadowNLI` template verbatim), `StubTranslator` (deterministic;
|
||||
the test substrate **and** the safe fallback when the extra is
|
||||
absent — identity passthrough, so the sandwich degrades to Phase-0
|
||||
behaviour, never raises).
|
||||
- `[mt]` extra in `pyproject.toml`.
|
||||
- `arborist/qa/query.py` — under
|
||||
`crosslang_guard_enabled AND crosslang_translate_enabled` AND the
|
||||
Phase-0 non-English signal fired AND translator `available`:
|
||||
es→en the query → `retrieval_query` **and** the LLM prompt
|
||||
(`llm_question`, so the model answers in English; `question_hash`
|
||||
still uses the original Spanish); after the verified English
|
||||
result, en→es the model **prose** (`raw_answer`, never the rendered
|
||||
evidence scaffold — pinned verbatim English spans must not be
|
||||
MT-ed) → `display_answer` (+ banner). Extend
|
||||
`RetrievalPlan` with `mt_engine` / `mt_manifest_hash` /
|
||||
`source_lang` (omitted-when-empty so existing `retrieval_plan_hash`
|
||||
values do not churn — the §5 zero-churn discipline).
|
||||
- CLI `--crosslang-translate`; `make query XLANG_MT=1`;
|
||||
`tasks.py query XLANG_MT=1`.
|
||||
|
||||
## 5. Acceptance criteria
|
||||
|
||||
1. Sandwich path (StubTranslator): es query → en `retrieval_query`
|
||||
(asserted), English `answer_text` verified by the **unmodified**
|
||||
verifier, `display_answer` is the es rendering, banner present.
|
||||
2. `question_hash` and `verifier_policy_hash` **identical** with the
|
||||
flag on vs off (the load-bearing invariant: user-question identity
|
||||
preserved, verifier byte-identical). `governance_policy_hash`
|
||||
**differs** (whole-policy hash — like every policy flag; correct
|
||||
cache partitioning, not a leak). Test:
|
||||
`test_sandwich_hash_invariants`.
|
||||
3. `RetrievalPlan` MT fields bind into `run_dag` (different engine →
|
||||
different `retrieval_plan_hash`); empty MT fields → **unchanged**
|
||||
hash (no churn on non-MT runs).
|
||||
4. The translated Spanish text is **never** the verifier's input —
|
||||
`answer_text` (English) is finalised and verified before display
|
||||
MT runs; the rendered evidence scaffold (pinned verbatim English
|
||||
spans) is never fed to MT (display translates `raw_answer` prose).
|
||||
5. Default OFF: `crosslang_translate_enabled=False` → no `display_*`,
|
||||
pure Phase-0 behaviour. English query → byte-identical (signal
|
||||
None → translator never consulted).
|
||||
6. `[mt]` absent → `OpusMTTranslator.available is False` → sandwich
|
||||
no-ops to Phase-0, never raises (graceful-degrade test).
|
||||
7. The translated answer is **never** passed to `verify_*`
|
||||
(asserted structurally — the verifier input is `answer_text`,
|
||||
set before display MT runs).
|
||||
|
||||
## 6. Out of scope
|
||||
|
||||
Languages beyond es-v1; the Grok bench-comparison; an
|
||||
`audit_mode`-affecting strict mode that folds MT into
|
||||
`governance_policy_hash` (the #000001 §6 optional-strict analogue);
|
||||
auto language *detection* beyond the Phase-0 deterministic signal
|
||||
(accent-free Spanish still slips — a detector is future work).
|
||||
|
||||
## 7. Status
|
||||
|
||||
Implementation landed 2026-05-17 — see §8.
|
||||
|
||||
## 8. Landing — what works, and the honest limits
|
||||
|
||||
**Mechanism: complete and verified live end-to-end.** A real run
|
||||
(`arborist query --crosslang-translate --json "¿Qué es el
|
||||
anarcocapitalismo?"` against live Hermes + the real `opus-mt`
|
||||
engine, which loads in this env via the `[nli]`-shared
|
||||
`transformers`/`torch`):
|
||||
|
||||
- query translated es→en, LLM prompted in English, **`answer_text`
|
||||
is the English answer** ("Anarchocapitalism is a political
|
||||
philosophy and economic theory that advocates the elimination of
|
||||
the state…"); the verifier ran on that English core;
|
||||
- `display_answer` is the opus-mt es rendering of the verified English
|
||||
prose ("El anarcocapitalismo es una filosofía política y una teoría
|
||||
económica que aboga por la eliminación del Estado…"),
|
||||
`display_translated=True`, `display_lang=es`, `engine=opus-mt-v1`,
|
||||
banner present;
|
||||
- the bright line held: the Spanish text is additive/display-only;
|
||||
`question_hash` + `verifier_policy_hash` invariant (test
|
||||
`test_sandwich_hash_invariants`); `RetrievalPlan` MT binding is
|
||||
zero-churn on non-MT runs (`test_retrieval_plan_mt_fields_*`).
|
||||
- 6 sandwich tests + full suite **2477 passed**, 0 regressions;
|
||||
`make`/`tasks.py`/CLI surface (`--crosslang-translate` /
|
||||
`XLANG_MT=1`) wired; default OFF.
|
||||
|
||||
**Two honest limitations the live run exposed (NOT solved here):**
|
||||
|
||||
1. **Recall is not yet good.** `opus-mt-es-en` rendered
|
||||
`anarcocapitalismo` → "Anarchocapitalism" (one token, no hyphen),
|
||||
which still did **not** rank the canonical *Anarcho-capitalism*
|
||||
article primary — the live run was `UNGROUNDED` with off-target
|
||||
sources (*Consensus decision-making*, *Karl Hess*, …). The
|
||||
architecture is correct; the MT-output↔title normalization /
|
||||
retrieval interaction needs tuning. This is exactly the experiment
|
||||
the **default-OFF flag + bench gate** exist for, and why the
|
||||
manifest revisions are placeholder (`main`, not SHA-pinned). "The
|
||||
sandwich works" means the *mechanism*; it does **not** mean
|
||||
cross-language grounding is solved.
|
||||
2. **JSON/lattice display is rough.** In `claim_lattice` (JSON) mode
|
||||
`raw_answer` is JSON, so display-MT translates the envelope
|
||||
(`"declaraciones"`). Prose/quote mode renders clean. JSON-mode
|
||||
display polish (translate field *values* only, or render from the
|
||||
parsed claim list) is future work — banner-labelled and default-OFF,
|
||||
so acceptable as an experimental edge.
|
||||
|
||||
**Doc honesty correction (2026-05-17).** Earlier drafts of §2 #6 /
|
||||
#000001 §7 said "governance untouched" — false: `governance_policy_hash`
|
||||
is sha256 of the *whole* policy (`keys.py:182`), so the crosslang
|
||||
flags move it like every policy flag (correct cache partitioning).
|
||||
Corrected in §2 #6 (authoritative), #000001 §7, and both index rows;
|
||||
artifact-over-instruction.
|
||||
|
||||
## 9. Fan-out benchmark (2026-05-17) — measured, with the dead ends named
|
||||
|
||||
**Repro (deterministic, no egress, no Hermes-for-translation).**
|
||||
`bench/make_lang_questions.py <lang>` regenerates the per-language
|
||||
question set from `bench/qa_questions.txt` via `opus-mt-en-<lang>`
|
||||
(the sandwich's own engine); `bench/qa_sweep.py … --policy
|
||||
crosslang_translate_enabled=true [--policy crosslang_entity_mask=…]
|
||||
[--policy crosslang_source_lang=…]` runs the live sweep;
|
||||
`bench/es_delta.py <en_baseline.jsonl> <xx_sandwich.jsonl>` is the
|
||||
authoritative metric (per-question EN→xx transition).
|
||||
`es_roundtrip_analysis.py` / `es_join_patterns.py` produced the
|
||||
*refuted* metric below — kept only as the cautionary record.
|
||||
|
||||
**The real measurement (n=1, claim_lattice, same 75 questions).**
|
||||
EN baseline **85 %** grounded (64/75) → es+sandwich (no mask)
|
||||
**71 %** (53/75): the sandwich costs **−14 pp**. Transitions:
|
||||
PRESERVED 31 · DOWNGRADE S→H 16 · **LOST 17** · N/A 11 (6 of which
|
||||
*gained*).
|
||||
|
||||
**Dead end (named, not buried).** A deterministic es→en round-trip
|
||||
content-token-recall metric (CLEAN 77 % / DRIFT 20 % / COLLAPSE 3 %)
|
||||
looked like a sharp predictor of grounding loss. It is **not**:
|
||||
against the live delta, CLEAN grounds 71 % and DRIFT 73 % (no
|
||||
separation), and **12 of the 17 LOST questions round-tripped CLEAN**.
|
||||
The metric is abandoned. This is another instance of the
|
||||
already-codified CLAUDE.md bench-maxing standing lesson (a clean
|
||||
synthetic metric ≠ real bench-qa behaviour — #000049 §7); **not**
|
||||
re-added to CLAUDE.md (DRY — it is already there).
|
||||
|
||||
**The LOST 17, from the artifact (not a hypothesis):** ≈7 *entity
|
||||
translated/garbled* — `New London`→"nuevo Londres", `Tarsus`→"Tarso",
|
||||
**`Boltzmann`→"perntzmann"** (opus-mt hallucinated the name),
|
||||
`World War 2`→"Segunda Guerra Mundial"→"Second World War" (≠ corpus
|
||||
"World War II"); ≈3 *broad-enumeration* shapes ("name all members of
|
||||
the beatles", "list all planets") — a quantifier-guard×translation
|
||||
interaction, separate lever; ≈several *n=1 stochastic* (`founder of
|
||||
microsoft?`, `what continent is egypt on?` round-tripped clean,
|
||||
entities intact, yet LOST — needs n=3 to separate).
|
||||
|
||||
**Lever built + validated (the entity slice).**
|
||||
`arborist/qa/mt/entity_mask.py` — deterministic mask/restore (quoted
|
||||
spans + Capitalised runs; corpus-title anchoring deferred to v2),
|
||||
`MaskedTranslator` wrapper, default-ON within the (default-OFF)
|
||||
sandwich, `crosslang_entity_mask` toggles it for the A/B. Measured
|
||||
through **real opus-mt** round-trip: `who is Paul of Tarsus?` base →
|
||||
"Pablo de Tarso" (the exact LOST case) → **+mask → "Paul of
|
||||
Tarsus"**; `Boltzmann` is now garble-proof (never reaches MT). 5
|
||||
tests.
|
||||
|
||||
**Honest caveat — the bench under-states the lever.** The bench
|
||||
questions are *lowercased* (`a bridge between new london…`), so the
|
||||
Capitalisation detector cannot fire on the en side; on the xx→en
|
||||
edge it still catches `Egipto`/`London` etc. So any lift on *this*
|
||||
bench is a **lower bound** — real cased user input does strictly
|
||||
better. The detector that catches lowercase entities is
|
||||
**corpus-title anchoring** (protect any query span matching a known
|
||||
article title), deferred to v2.
|
||||
|
||||
**Also landed:** the #000056 fan-out caught a real defect —
|
||||
`get_translator()` built a fresh ~300 MB model per call → at
|
||||
concurrency 4 the first sweep was **88 % engine-ERROR**
|
||||
(`meta tensors` race). Fixed: process-level memoised singleton +
|
||||
lazy *per-pair* load (6 manifest pairs never load eagerly) + load
|
||||
lock; 6 concurrent threads → 0 errors. French (`opus-mt-fr-en/-en-fr`)
|
||||
and Russian (`ru`) added as second/third bread (manifest +
|
||||
`crosslang_source_lang`); fr question set generated (`GNU Linux`
|
||||
survives). [Tasks #14–#17]
|
||||
|
||||
**Lift result (2026-05-17, measured — and it refutes the lever).**
|
||||
n=1, claim_lattice, 75 q, vs EN baseline 64/75 (85 %):
|
||||
|
||||
| config | grounded | Δ vs EN | LOST |
|
||||
|---------------|------------|---------|------|
|
||||
| es, no mask | 53 (71 %) | −14 pp | 17 |
|
||||
| **es, +mask** | **49 (65 %)** | **−20 pp** | **22** |
|
||||
| **fr, +mask** | **35 (47 %)** | **−38 pp** | **33** |
|
||||
|
||||
**Entity-mask did not help — it is neutral-to-negative.** The
|
||||
isolated `Paul of Tarsus` win did **not** replicate at bench scale:
|
||||
es went 53→49 (−4 q ≈ the 5 pp n=1 noise floor — *ambiguous*, not a
|
||||
confirmed regression but certainly no lift). fr is unambiguous and
|
||||
bad (−38 pp, well past noise). My earlier "lever validated" claim
|
||||
was premature; the bench refutes it. **Third instance this thread of
|
||||
the CLAUDE.md bench-maxing lesson: an isolated/synthetic win ≠
|
||||
bench-qa behaviour.** Likely causes (untested): the lowercased bench
|
||||
gives the mask almost nothing to grab on the en side, so on the
|
||||
xx→en edge it fires on *Spanish/French* capitalisation (which is not
|
||||
English entity structure) and the sentinels perturb opus-mt's
|
||||
translation of the rest of the sentence — net harm, not help.
|
||||
|
||||
**Honest standing verdict (comparator corrected 2026-05-17, fox).**
|
||||
The right baseline is **not** native English — it is what
|
||||
cross-language did *before this ticket*: the 2026-05-16
|
||||
`¿Qué es el anarcocapitalismo?` run retrieved Spanish-song-title
|
||||
noise, burned 10.4 s, returned UNGROUNDED — **≈0 % grounded**.
|
||||
Against that true baseline:
|
||||
|
||||
- **The sandwich is a large net win: ~0 % → 71 % grounded (es)**, with
|
||||
the proof core never corrupted. The −14 pp vs *English* is the
|
||||
*cost of a capability that previously did not exist at all*, not a
|
||||
regression. Calling that a "fail" was a comparator error — doom-
|
||||
framing is as dishonest as hype.
|
||||
- **The entity-mask *lever* is the genuine negative:** net-negative
|
||||
at scale (es 71→65, fr −38 pp), so it is now **default-OFF**
|
||||
(`crosslang_entity_mask=False`); the no-mask sandwich is the
|
||||
better config and the one worth keeping.
|
||||
- French is materially weaker than Spanish (fr+mask 47 %; fr no-mask
|
||||
unmeasured — likely higher since mask hurt es). Still ≫ the ≈0 %
|
||||
baseline.
|
||||
|
||||
So: **keep the sandwich (clear win over no support); drop the mask
|
||||
default (failed lever); the −14 pp-vs-English is a discount on a new
|
||||
capability, not a defect.** Remaining: n=3 to firm es/fr numbers,
|
||||
fr no-mask measurement, corpus-title anchoring (the one untried
|
||||
detector that works on lowercase). Default-OFF as an *experiment
|
||||
flag*, but the experiment is now **promising, not failing**. fox
|
||||
decides next spend; the honest recommendation flipped — this is
|
||||
worth continuing, just not with the mask.
|
||||
Loading…
Add table
Add a link
Reference in a new issue