v0.28.4.md
docs/release-notes/v0.28.4.md
Tesserae v0.28.4 — your other accounts were never being used
<!-- translations:start -->
한국어 · 中文 · 日本語 · Русский · Español · Français · Deutsch
<!-- translations:end -->
Released 2026-07-31 · PyPI · GitHub release · pip install --upgrade tesserae==0.28.4
Tesserae rotates over your logged-in CLI accounts so that a rate-limited account falls through to the next instead of losing the rest of the run to deterministic extraction. That loop was switched off, in the most common way Tesserae runs.
One environment variable disabled the whole mechanism
Setting CLAUDE_CONFIG_DIR or CODEX_HOME replaced the discovered account list with that single directory. Every process launched from a Claude Code session inherits CLAUDE_CONFIG_DIR — so a compile could only ever use the account of the session that started it, and when that account ran dry, rotation had nowhere to go.
Measured on this project's own graph, in one day:
- a compile exhausted one account's 5-hour limit and dropped 1,531 documents to deterministic extraction while two other logged-in accounts sat idle with full quota;
- a later run lost 1,590 more to one account's expired OAuth, with a healthy account beside it.
The graph had quietly shrunk from ~13,000 nodes to 5,155 before anyone noticed.
The environment variable now names the account to try first, not the only one that may be tried.
You choose which accounts get spent
llm_claude_config_dirs and the new llm_codex_homes (a list, in rotation order) are authoritative when set — nothing outside them is tried, and they beat the ambient environment variable, which is inherited rather than chosen.
{
"llm_codex_homes": ["/Users/you/.codex-work", "/Users/you/.codex-personal"]
}
Each provider keeps its own key because a Claude CLI config dir and a Codex home are different things on disk. Codex discovery now also requires auth.json, which on one real machine excluded 24 stray ~/.codex-*.log files — each of which had been costing a doomed codex exec per document.
An exhausted account no longer costs a subprocess per document
Quota is an account fact, not a document fact. Once every configured account reported its limit, the run still spawned a CLI child per account per remaining document to be told the same thing. That verdict is now remembered for the rest of the run.
It is deliberately hard to trip: every account tried must be a proven dead end, and at least one must have said "you've hit your … limit" outright. One timeout among them and the run keeps trying — transient is not exhausted. The verdict is per-client, so a long-lived daemon is not stuck after the limit resets.
A compile that loses its concept layer now says so
Compiled project wiki: processed=2492 skipped=0 fallbacks=1531 nodes=… edges=…
WARNING: 1531 of 2492 document(s) (61.4%) fell back to deterministic extraction —
their concept layer is missing from this graph.
fallbacks=N rides the machine-readable summary line so CI can gate on it; the human-facing line escalates from note: past 50%. Both name the recovery, which does not require recompiling the corpus:
tesserae compile --changed-only --retry-fallbacks
This is #92. The silence is what made every bug above invisible for a day. The exit code is unchanged — that is a contract change for anything scripting compile, and belongs in its own decision.
Also
The Claude extractor no longer passes --max-turns 1. That flag counts tool calls rather than replies, so on a machine with MCP servers configured the model's first tool call spent the only turn and the CLI exited before emitting JSON. --strict-mcp-config is what the one-shot intent actually requires.
@jokerized/tesserae now publishes from GitHub Actions via npm OIDC trusted publishing, so releases carry provenance attestations and no long-lived npm token exists.
Upgrading from v0.28.3
Drop-in. No API or schema change; llm_codex_homes is additive and the singular llm_codex_home still works.
If you run multiple accounts, recompile. Earlier versions may have degraded documents that a fresh run recovers, and cached responses mean only the affected files cost anything:
tesserae compile --changed-only --retry-fallbacks