Different agents have different value functions on the same providence
records. Today aborist bakes one canonicalization policy into
governance_policy_hash and calls it universal. This adds two knobs that
let agents express their preference without breaking provenance.
- aborist/qa/keys.py: `canonical_question(q, mode=...)` and
`question_hash(q, mode=...)` accept "strict" (NFC + ws-collapse only;
every variant gets its own hash) or "equivalence_class" (default —
additionally lowercase + trailing-punct strip + article strip).
New constants: QUESTION_DEDUP_MODES, FIDELITY_MODES, defaults.
- aborist/qa/query.py + aborist/qa/runner.py: each call computes the
primary cache_key under policy["question_dedup"]. New `fidelity`
parameter:
"strict" only primary cache_key checked
"equivalence_class" primary first; if miss AND alternate mode
produces a different cache_key, try alternate
Cross-silo fallback works because _ckey_for_mode rewrites
policy["question_dedup"] to the alternate mode before computing
governance_policy_hash — so the fallback ckey matches what an agent
under that mode would have written. Lookups can find each other's
records when fidelity permits. Result dict gains `lookup_path` ∈
{"strict", "equivalence_class", "strict_fallback",
"equivalence_class_fallback", "miss"}.
- aborist/cli.py: `--question-dedup` and `--fidelity` flags on the
`query` subcommand. Human render annotates cache_hit lines that
came from a fallback ckey ("cached via equivalence_class_fallback").
- tests/test_query.py: three new regressions
* strict-policy + strict-fidelity: variants get distinct cache_keys
* cross-silo fallback: strict-policy reads eq-class-policy records
via fidelity=equivalence_class
* strict-fidelity refuses fallback (audit-grade): cache miss even
when the alternate silo has a hit
- CLAUDE.md: dedup-mode and fidelity convention bullets updated.
This is V1 + V2 of the substrate move sketched in conversation: write
policy determines which silo a record lives in; read fidelity determines
how loosely an agent walks across silos. Records stay exactly-keyed
(provenance hard); routing is per-agent (preference soft). Same
verifier_no_diagnostics discipline holds — no soft signal enters the
hard chain.
442 tests pass.
16 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.
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.