v0.25.1
Tesserae v0.25.1 — the compile no longer wedges, and degraded docs are recoverable
한국어 · 中文 · 日本語 · Русский · Español · Français · Deutsch
Released 2026-07-25 · PyPI · GitHub release · pip install --upgrade tesserae==0.25.1
A bugfix release. No API changes — drop-in over v0.25.0, with one behaviour
default flipped (see Upgrading).
Every fix here came out of one investigation. A compile on a real project sat at
0% CPU for 5 hours 43 minutes, behind a codex exec child that had been idle
for 4 hours 6 minutes. It had already minted 32 COMMUNITY_SUMMARY nodes in
memory and never lived to persist them, so graph.json ended with zero. Pulling
that thread surfaced three more defects.
Fixes
-
The extraction wedge guard is armed by default. v0.25.0 shipped a per-file
extraction timeout, but opt-in — so it stayed switched off and the failure it
prevents kept happening._run_cli's process-groupkillpgwas correctly
wired the whole time; it simply never fired, becausecommunicate(timeout=None)
never raises. The bound is now 1800 s per attempt by default. Killing the
wedged child's process group let the parent compile resume within seconds —
it had been parked incommunicate()for four hours.A typo must not silently disarm a safety valve, so an unusable value
(10m,600s, negative,inf) now warns and keeps the default instead of
falling back to "no cutoff".TESSERAE_EXTRACT_TIMEOUT=0is the explicit
escape hatch. -
compile --retry-fallbacksrecovers docs that degraded to deterministic.
When an extraction fails, the doc is served by the deterministic baseline —
but its manifest entry was byte-identical to a clean extraction, so
--changed-onlyskipped it on every later compile and the degradation was
permanent until the file's own content changed. Recovering a handful of docs
meant hand-editingmanifest.json, with the standing risk of instead
deterministic-filling the whole corpus and destroying hours of typed work.
Such entries are now marked and re-attempted on demand, mirroring
distill --retry-fallbacks. -
live_member_countongraph_mapcards.sizeandleaf_member_count
both come from thehierarchy.jsonsidecar, which is written mid-compile. When
graph.jsonis rewritten afterwards the two diverge, and a card can advertise
hundreds of members that resolve to nothing — with no way for a client to tell
it from a healthy card without re-readinggraph.jsonitself. Graph-derived
cards (kind=community|node) now also report how many members the current
graph carries.0means the scope is dead: skip it rather than descend.On one real 7,133-node graph this immediately surfaced 2 dead root cards and
42 partially-resolved ones that nobody had noticed. -
The
_CLIENT_FACTORYtest seam honours the caller's timeout.
build_default_json_clientdropped it, handing the injected fake a 30 s default
regardless — so a test pinning a timeout silently proved nothing. Seam-only;
production composition was already correct.
Upgrading from v0.25.0
Drop-in, with one thing to know: extraction is now bounded by default
(1800 s per attempt). If you deliberately want unbounded runs, set
TESSERAE_EXTRACT_TIMEOUT=0.
The bound is per attempt, not per document — on timeout the client rotates to
the next CODEX_HOME / claude config dir, so a document's worst case is
timeout × configured profiles.
If a past compile left documents stuck on deterministic extraction, recover them
with:
tesserae compile --changed-only --retry-fallbacksOnly documents whose typed extraction actually failed are re-attempted; clean
ones stay skipped.