The retrieval contract was undocumented — `_filter_by_title_relevance` has three accept paths, each with its own scaling rules, and the recent breadth + stem refinements (2026-04-29 catches) lived only in commit messages. New "Retrieval pipeline" section between Live endpoints and Hot path / gotchas covers the eight stages in order: 1. Parallel FTS5 (body-BM25 + title-LIKE), unioned per shard 2. Body-coverage sqrt rerank — counter BM25 short-doc bias 3. Title-token boost 4. Three accept paths in _filter_by_title_relevance: title overlap (breadth-scaled: ≤2 require ALL, 3+ require N-1), TF-IDF core keyword match, body density (same breadth scaling plus min_mentions=3 depth) 5. Rivalry exclusion (Intel/AMD groups) 6. Stem-aware token matching: trailing-`s` strip on >4-char tokens (skip `ss`-enders) — 'supermans girlfriend' now collapses onto 'superman'/'girlfriend' instead of admitting `Girlfriends` TV show 7. Per-source context cap = max_context_chars / top_k — prevents 80 KB+ bibliographies from monopolizing the budget 8. Wikitext base-prose normalization before LLM (cross-ref to the Wikitext base prose convention) Each item includes the commit ref or the catch date so an operator can trace why the rule exists.
18 KiB
Agent Blackops — aborist
This repo is operated by agent blackops for fox/timehexon on the unsandbox / unturf / permacomputer platform.
Identity shard: ~/git/unsandbox.com/blackops/BLACKOPS.md.
What aborist is
A content-addressed, Merkle-committed document store. Implements the runtime spec from Merkle Providence Reverse RAG (April 2026 whitepaper) scaled up to the Merkle-AGI v9.8 admissibility ledger. Tends "trees and forests of cross-linked information" — the namesake.
Three layers stacked on one SQLite file:
- Surface — ingested documents (Wikipedia dumps, HTML pages, anything with a URI). Chunked, Merkle-rooted, FTS5-indexed.
- Core — distilled documents (haiku/keyword/equation-scale) Merkle-bound back to source surface(s) via per-chunk inclusion proofs in
derivations.proof_blob. Recursive: cores derive from cores. The "planet toward center" compression. - Providence cache — Q&A records keyed on the v9.8 8-dim invariant. Each record carries an
audit_modeset by the post-LLM faithfulness verifier (aborist/qa/verify.py): STRICT (every claim verbatim-grounded or token-coverage paraphrase-grounded), HYBRID (mixed source/emergent), UNGROUNDED (no recoverable grounding — purely emergent from training, or genuine model refusal).verifier_methodrecords which of four strategies — quote / span / entity / paraphrase — fired.
Source papers (read first if confused)
~/git/unfirehose-nextjs-logger/whitepaper/merkle-providence-reverse-rag-whitepaper.rst— canonical whitepaper source (rst, builds the PDF). Edit here, not the PDF.~/Downloads/merkle-providence-reverse-rag-whitepaper.pdf— built artifact; mirrors the rst above~/Downloads/merkle-agi-dag_v7.txt— formal substrate (TLV encoding A1, public quantization A2, collision-resistant hash A3, theorems T1–T5)~/git/proxy.unturf.com/pkg/verified/merkle.go— fox's existing Go merkle implementation. Aborist Python ports its conventions exactly.~/git/proxy.unturf.com/docs/merkle-tree.md— convention reference
Architecture
aborist/
├── merkle.py # Python port of proxy.unturf.com Go conventions
├── store.py # v9.8 SQLite schema, audit chain helpers
├── document.py # Document, Edge, Chunker (TokenChunker default)
├── source.py # Source ABC: iter_documents() -> Iterator[Document]
├── ingest.py # batched: normalize -> chunk -> merkle -> upsert
├── evict.py # hot->cold + rehydrate (v9.8 falsification on drift)
├── search/ # SearchBackend ABC + AuditMode + FTS5
├── sources/ # one file per corpus
│ ├── wikipedia.py # WikipediaSqlDump (cur + old tables)
│ └── html_page.py # HtmlPageSource (selectolax, robots-aware)
├── distill/ # surface->core distillation
│ ├── base.py # Distiller ABC + DistillationResult
│ ├── first_sentence.py # FirstSentenceDistiller (no-ML stub)
│ ├── tfidf.py # TfidfKeywordDistiller (pure-Python TF-IDF)
│ └── runner.py # batched: derive + per-contrib-chunk proofs
├── qa/ # Q&A: 8-dim cache_key + Merkle-bound answers
│ ├── client.py # ChatClient + StubClient + OpenAICompat
│ ├── keys.py # cache_key, question_hash, ... (pure functions)
│ ├── verify.py # layered verifier: quote → span → entity → paraphrase
│ ├── inspect.py # read-only sidecar: diagnose unverified spans
│ └── runner.py # ask(): cache -> infer -> verify -> classify -> write
├── wikitext.py # to_base(): wikitext → plain prose (BASE_VERSION-pinned)
└── cli.py # ingest / search / verify / stats / distill /
# evict / rehydrate / ask / providence / emergent /
# reclassify / inspect / analyze
Build, test, run
Every workflow is a make target. Bare python commands are not the user interface.
make bootstrap # venv + editable install with [dev] extras
make test # pytest -q (49 tests)
make all # bootstrap + fetch-cur + ingest-cur + verify + stats
make fetch # cur (82 MB) + old.1 (640 MiB) + old.2 (252 MiB) + concat
make ingest-cur # ingest snapshot articles
make ingest-old # ingest revision history (~hours)
make verify-shards # round-trip Merkle proofs on a random sample (cross-shard)
make analyze-shards # cross-shard compression spectrum + audit integrity
make chain-check-shards # audit-chain break count per shard (0 = intact)
Hygiene after any state-changing op (table rebuild, reclassify run, governance hash bump, mass falsify): make chain-check-shards for a one-second sanity (every shard should report 0), then make analyze-shards for the full spectrum + chain audit. Chain breaks are the loudest possible signal that something corrupted the audit log — catch them at the seam, not in production.
aborist/cli.py adds: analyze, distill --kind {surface,core}, evict, rehydrate, ask, providence, emergent (list UNGROUNDED/HYBRID records or --aggregate to rank unverified quotes — corpus-growth signal), reclassify (re-run the verifier against existing live providence records under the current entity policy; no LLM calls; --compare runs all four entity policies side-by-side, --dry-run reports without writing), inspect --cache-key X (read-only sidecar that pulls source chunks for one record & classifies each unverified span; never writes to providence_cache or audit_events). The query command human-renders by default; pass --json (or JSON=1 to make query) for the raw record. --batch-size defaults to 200 docs/transaction; lower it only to bound memory peaks.
Schema invariants (do not break)
- Aborist is a v9.8 store. Every Document carries
chunking_version,canonicalization_version,schema_version. Every providence record carries the full 8-dim cache_key:source_root | question_hash | model_profile_hash | conversation_hash | governance_policy_hash | schema_version | canonicalization_version | chunking_version. Bumping any one invalidates prior records on lookup. falsification_state ∈ {live, failed, stale, quarantined}. Cache lookups must filter onstate='live'. Drift detection (rehydrate vs source root mismatch) flips tostale.- Audit chain. Every state-changing op writes one row in
audit_eventswithevent_hash = sha256(prev_event_hash || canonical(body)). Chain integrity is verified inmake analyze-shards(full audit) ormake chain-check-shards(one-second per-shard break count,0= intact). Never insert intoaudit_eventsdirectly — useaborist.store.append_audit. - Cores never evict.
evict_to_coldonly toucheskind='surface'. - Idempotent re-ingest. Same content → same
document_root→ no-op insert. Same URI + different content → new doc +supersedesedge linking new → old (lossless history).
Conventions (do not silently change)
- Merkle: non-commutative
HashCombinewith prefix0x03. Leaves0x00. Odd-element rule = self-duplicate, NOT zero-pad.MerkleProof.siblingscarries explicitis_leftflag — never sort lexically. - Chunker default =
tok-512-v1. Changing the default bumpschunking_versionand stales every prior cache record. Add a new chunker as a newnameinstead. - Canonicalization =
norm-v1(NFC, collapsed whitespace). Same rule. - Schema =
v9.8.0. Same rule. question_hashis dedup-mode-aware.question_hash(q, mode=...)accepts"strict"(NFC + ws-collapse only — case-sensitive, punctuation-sensitive, article-sensitive; every variant gets its own hash) or"equivalence_class"(default — additionally lowercase + trailing-punct strip + standalone-article strip; collapses"who is X","who is X?","Who Is X.","who is the X","who is an X?"to one hash). Mode lives inpolicy["question_dedup"]so it folds intogovernance_policy_hash; agents under different modes write records under different cache_keys.- JIT lookup fidelity. Per-call
fidelityparameter onquery()/ask()decouples lookup tolerance from write policy."strict"checks only the primary cache_key (audit-grade — record reuse only on exact match)."equivalence_class"(default) tries primary first, then the alternate dedup mode's cache_key as a fallback so a fast-cache agent can reuse records written under either policy. Result includeslookup_path∈{"strict", "equivalence_class", "strict_fallback", "equivalence_class_fallback", "miss"}. Cross-silo fallback works because the helper rewritespolicy["question_dedup"]to the alternate mode when computing the fallback ckey, sogovernance_policy_hashmatches what an agent under that mode would have written. CLI:--question-dedup,--fidelityflags onquery(and theaskAPI params). audit_modeis decided by the verifier, never asserted unconditionally. Four layered strategies inaborist/qa/verify.py, tried in order; first to find evidence classifies the answer:- quote — model wrapped claims in double quotes per system prompt. Sequential pairing: 1st & 2nd
", 3rd & 4th, etc. (NOT regex pairing — that captures inter-pair prose as a phantom span when the model writes"title" prose "quote"). - span — bullet/sentence lines from the answer appear verbatim in context. Catches models that quote inline without
"..."marks. - entity — multi-word proper-noun phrases appear verbatim in context. Gated by
entity_policy ∈ {strict, hybrid, drop, proximity}. Defaultproximity: STRICT only when N=3 verified entities cluster within W=300 chars in source (cast list / infobox / roster). Otherwise HYBRID/UNGROUNDED. Distinguishes structural grounding from incidental mention. Lives inDEFAULT_QUERY_POLICY["entity_policy"]so any change bumpsgovernance_policy_hash. - paraphrase — soft fallback on prose-shaped spans when 1-3 fail. Token-coverage probe: ≥4-char content tokens minus an English stopword set (
from,with,would,which, etc.) checked for presence in normalized base context. Promotes when coverage ≥DEFAULT_PARAPHRASE_COVERAGE(0.85) with ≥DEFAULT_PARAPHRASE_MIN_TOKENS(4) content tokens. Fires only on prose-shaped spans (≥2 lowercase content tokens via_is_prose_span) so lists of proper nouns flow to the entity strategy instead. Quote strategy deliberately gets NO paraphrase fallback —"..."asserts verbatim citation; paraphrasing inside quotes is a model error to flag, not auto-promote. Records carryverifier_method='paraphrase'so an auditor can tell soft-verified evidence from lexical-verbatim. Trichotomy across all paths: STRICT = every evidence unit (≥1) verifies. HYBRID = some verify, some don't. UNGROUNDED = no evidence or none verifies. Persisted onprovidence_cache.audit_mode+verifier_method; cache-hits return the stored mode. Never overclaim — STRICT is a verifiable claim, not a default.
- quote — model wrapped claims in double quotes per system prompt. Sequential pairing: 1st & 2nd
- Trailing-citation strip.
_strip_trailing_citationpeels a single trailing parenthetical at end-of-span (gated on a citation-cue word —Source:,citing,see,ref,from— OR a URL) before substring testing. Keeps"...prose. (Source: https://...)"from flunking just because the model appended a citation tail. Refuses to strip genuine prose parentheticals (no cue word, no URL). - Verifier stays binary; falsifications carry soft signal. No per-quote diagnosis fields on hard verifier output.
verify_quotesreturns evidence units + classification; the falsify+reclassify loop owns "why didn't this ground" for the operator, andaborist inspect --cache-key Xis the read-only sidecar that classifies each unverified span (verbatim_in_base/verbatim_in_raw_only/trailing_artifact/paraphrase/partial_paraphrase/no_overlap) — sidecars never write toprovidence_cacheoraudit_events. Don't bolt confidence scores or partial-match indicators ontoverify.py. - Wikitext base prose.
aborist/wikitext.py:to_base(raw)converts MediaWiki wikitext → plain prose deterministically (mwparserfromhell-backed; pinned byBASE_VERSION = "wikitext-base-v1"). Applied before the LLM call inaborist/qa/runner.pyandaborist/qa/query.py(gated onpolicy["base_version"]), and again insideverify_quotesso the verifier compares like-against-like. Both sides — model and verifier — see prose; the model can quote source paragraphs verbatim instead of escaping[[wikilinks]], and Wikipedia chunks ship to Hermes with ~43% fewer tokens.policy["base_version"]lives inDEFAULT_POLICY/DEFAULT_QUERY_POLICYso it folds intogovernance_policy_hash; bumpingBASE_VERSIONinvalidates every prior cache record's 8-dim cache_key on next lookup. Optional dep — installs withoutmwparserfromhellkeep_wikitext_to_base = Noneandpolicy["base_version"] = None, leaving raw wikitext in both context and verifier (graceful fallback, no failure mode). - Soft hash vs hard hash. Hard = SHA-256 (commitments, proofs, cache_key). Soft = embeddings/TF-IDF/similarity (training, ranking, distillation candidate selection). Never mix — soft never enters proof path.
Live endpoints
- LLM:
https://hermes.ai.unturf.com/v1(Hermes-3 Llama-3.1-8B-FP8-Dynamic on vLLM, 82K ctx, no auth).uncloseai.comis marketing only — has no/v1. Override via--endpointorABORIST_LLM_ENDPOINT. - Wikipedia dumps:
https://dumps.wikimedia.org/archive/2003/2003-05-16/en/.robots.txtreturned 404 → no rules.
Retrieval pipeline (aborist/qa/query.py)
Multi-stage. Each stage exists because something earlier wasn't enough; revert at your peril.
- Two parallel FTS5 searches per shard, merged.
_search_corpusruns body-BM25 AND title-LIKE in parallel and unions hits. The title path closes a recall gap where list-pages with many URLs (e.g.List_of_HTTP_status_codes) outrank the actualHTTParticle on body BM25. Seed608c39 fix retrieval recall. - Body-coverage
sqrtrerank. BM25 systematically favors short docs with rare body tokens (Tell_(poker)outrankingBack_to_the_Futureon a film query). Counteract by reranking onsqrt(body_token_count)to recover long-doc hits.1983b79. - Title-token boost.
_rerank_by_titleaddsboost × (overlap)to hits whose title contains query tokens — strong topical signal that BM25 alone misses. _filter_by_title_relevance— three accept paths. A hit passes if any of: (a) title-token overlap meetstitle_breadth(≤2 tokens require ALL; 3+ require N-1); (b) it's incore_match_roots(TF-IDF core keywords contain a query token — closes the gap for neologisms like "permacomputer" that never appear in titles); (c) body density —_body_density_passesrequiresdistinct_present >= breadth_threshold AND total_mentions >= 3. Same breadth scaling as titles. Synonym fallback fires only for 1-token queries (otherwiseintel-titled doc bleeds into AMD queries via the rivalry group).- Rivalry exclusion (
rivalry_excludedinqa/concepts.py). Intel-titled docs drop from AMD queries; reverse holds. Applies on every accept path. - Stem-aware token matching.
_stem_token_for_match: trailing-sstrip on tokens >4 chars (skipss-enders). Possessive (superman's→supermans→superman) and plural (girlfriends→girlfriend) collapse onto bare-stem source mentions. Caught the 2026-04-29 "who is supermans girlfriend?" defect — pre-stem, the query admitted 7 unrelatedGirlfriends-titled articles. - Per-source context cap. Each top-K hit gets at most
max_context_chars / top_kchars before the globalchar_budgetis consumed. Fox's 2026-04-29 catch:List_of_Batman_comics(80 KB+ bibliography) was monopolizing the entire 60 KB budget at hit #1, dropping every other source. - Wikitext base prose runs on the assembled context BEFORE the LLM call (
policy["base_version"]gates it, see Wikitext base prose convention above).
Hot path / gotchas
- Parser is hand-rolled in
aborist/sources/wikipedia.py(char-position state machine, escape-aware). After the v9.8 commit it's 4× faster viastr.find+ slicing — easy to break by reverting to char-by-char loops. cProfile any change. PRAGMA synchronous=NORMALis set per-connection instore.connect(). Safe under WAL (the journal_mode is set inSCHEMA_SQL). Don't downgrade to FULL without a measured reason — costs ~5x throughput.- HTML source has optional deps:
pip install '.[html]'forselectolax. The CLI surfaces--source htmlonly if the import succeeds. - Background ingest/distill processes: stdout is buffered. Use
export PYTHONUNBUFFERED=1orpython -u. Per blackops top-level rule. - Disk pressure. Full cur ingest ~2 GB; full old ingest ~5–8 GB.
df -h /home/foxfirst.
Operational rules
- I propose, fox decides. Unsure = ask. Can't ask = stop.
- No autonomous destructive ops. No
clean-data,clean-db, force-push, or DB drops without explicit instruction. - Never add
Co-Authored-Byor "Generated with Claude" lines to commits. Professional commit messages only — code speaks for itself. - Always
export PYTHONUNBUFFERED=1for long-running processes. Buffered output disappears when processes die. - Fail-closed. Cleanup crew, not demolition.
- DRY in context — single source of truth, no sprawl.
- Never say "AI" — always say "machine learning."
- Prefer "defect" over "bug."
- Check robots.txt before any web fetch the user didn't authorize.
Orientation protocol
date -u
pwd
git log --oneline -5
git status
make test
make chain-check-shards # per-shard audit-chain integrity (0 = intact)
.venv/bin/aborist --shards-dir ~/.aborist/shards stats
.venv/bin/aborist --shards-dir ~/.aborist/shards analyze --gravity-top 5
Then ask fox what the mission is.