v0.27.0.md
docs/release-notes/v0.27.0.md
Tesserae v0.27.0 — agents can write to the graph, and ask it what it licenses
<!-- translations:start -->
한국어 · 中文 · 日本語 · Русский · Español · Français · Deutsch
<!-- translations:end -->
Released 2026-07-26 · PyPI · GitHub release · pip install --upgrade tesserae==0.27.0
Until now an agent could only hand Tesserae prose and hope the extractor guessed the right types back out. This release opens the write path and the verification path: agents record findings as typed structure, and can ask the graph whether it actually licenses a claim.
Agents write typed findings
graph_write takes schema-validated typed nodes and edges with mandatory provenance. It refuses rather than coerces — untyped edges, types outside the controlled vocabulary, dangling endpoints and missing provenance are all rejected; duplicate writes are idempotent.
Agent-written nodes survive a full recompile, a deleted graph.json, --limit, and total corpus deletion. Under adversarial testing, 60 concurrent writes against a live compile came through with zero torn reads across 1,483 reads.
Agents verify against the graph
verify_claim answers whether the graph licenses a triple. The verdict is a pure function of graph bytes: no LLM, no embedding, no fuzzy matching anywhere on the decision path. A verifier that hallucinates is worse than no verifier.
It takes (subject, predicate, object) and has no natural-language parameter — deliberately. Seven verdicts, including PRESENT_UNEVIDENCED (the edge exists but nothing document-backed stands behind it) and ABSENT ("this graph does not assert that" — never a refutation).
Two guarantees worth stating plainly. supersedes never produces a verdict: it says a node was replaced, not that a triple is false. And an agent write can only ever weaken a provenance class, never upgrade one — nothing an agent asserts can present as document-grounded.
Also
- Semantic entity resolution. Embedding-clustered candidates now feed the existing review queue rather than auto-merging. Precision over recall: a wrong merge silently fuses two real entities and every downstream traversal inherits the error.
- Question-shape routing for
ask. Graphs win multi-hop, temporal and synthesis questions and lose simple fact lookup and cost, so paying graph prices for everything is a loss. The routing decision is in the envelope so a cheap answer is auditable;--routeoverrides. - Validity intervals on temporal facts, derived from source timestamps — never wall-clock.
--llm-limitholds under concurrent extraction. Since v0.26.0 made extraction parallel, the old check-then-increment let every worker read the counter before any of them wrote it, so--llm-limit 1bought one call per worker. It is a spend cap, so it now fails closed.- Compile coverage is an invariant, not a claim. Four silent-data-loss paths closed, then the machinery behind them removed entirely —
compile(loader=…)has no production caller, and defending that library-only API was the source of every serious defect across several review rounds. One of them was a regression v0.26.0 itself introduced. - New documentation for the write, verify and routing surfaces.
What did not ship, and why
A contrast pass to mine contradicts_claim / derived_from / criticizes edges was built, measured, and cut. It minted 0 edges from 80 judged pairs across two real corpora, and 60 of 60 judge rationales gave the same reason: the pairs were near-duplicates with nothing to contradict.
The cause was structural. Candidate generation blocked on shared rare tokens, which optimises for similarity — but contradiction and derivation hold between claims that differ. A redesign evaluated five new generators offline, at zero LLM cost: the high-volume one scored 0/10 on hand inspection (its "conflicting numbers" were arXiv IDs and publication dates), and the only precise one produced six candidate pairs on one corpus and none on the other.
A control run settled it — judging 15 reasoning edges that already exist returned recall 7/15. The judge was always working; the candidates were always wrong.
The REASONING_EDGE_RATIO lint rule stays, and pins the honest baseline: 7.5% of edges carry reasoning, with contradicts_claim, derived_from, attributes_improvement_to and criticizes at zero instances.
Upgrading from v0.26.0
Drop-in. If you set TESSERAE_CONTRAST_PASS, it no longer does anything — the pass is gone.