Skip to content

v0.7.0 — 2026-08-23

The question loop — a measured, self-driving improvement cycle for a corpus — plus authenticated operator access to the MCP toolset and corpus-quality tooling behind an autonomy rubric.

Added

  • The question loop (DEE-101 / DEE-103 / DEE-104). A repeatable cycle that takes a question your corpus answers badly, measures it against a weighted rubric, routes each failing criterion to the subsystem that owns it (coverage → extraction → linkage → retrieval), acts, and then decides whether to keep the change on evidence rather than impression. Every stage is a library you can call: app/eval/routing.py, actions.py, decision.py, suite.py, run.py. The full procedure, including when not to trust a measurement, is in Question Loop.
  • bb loop run — drive the loop from the command line. Point it at a fixture and a brain and it runs the cycle unattended, logging every score, routing decision and ingest at INFO: bb loop run --fixture dee-59 --brain-id calliope --runs 3. Options cover the corpus ref, the score target, budget ceilings (--max-fetches, --max-actions, --max-model-calls), and what to do with a mixed verdict. Worked examples, including cold-starting a fixture from a Linear ticket, are in Running the Loop (CLI).
  • A question base that must lift over its lifetime. Every iteration re-scores all fixtures, not just the one being worked on, and the run halts if the base degrades beyond what the runs' own spread explains — so a win on one question can no longer quietly cost you four others. The series is kept in app/eval/portfolio_history.jsonl, and each row records the answering model, because a score is only comparable to another taken on the same one.
  • A register of what has already been tried. Settled experiments are data (app/eval/register/*.yaml), so the loop refuses to re-run a dead end instead of rediscovering it — matched on provision keys rather than prose similarity.
  • Authenticated operator sessions for MCP (DEE-97). The operator toolset now requires a real key and says so in the audit trail. Three scopes: mcp:invoke (use the toolset at all — an advisor:read key is not enough), mcp:write (mutate a corpus), and mcp:any-brain (act on a brain other than the key's own). Mint one deliberately with bb apikey mint --operator --brain-id <id> --actor you@example.com (add --read-only to withhold mcp:write), then export the printed token as POLARIS_MCP_API_KEY for the stdio MCP server — the session then acts as --actor rather than as whatever string a caller passes. See MCP (Advisor).
  • An autonomy rubric on every MCP tool. Tools are classified A0–A4 — read, metered, reversible write, judgement write, irreversible — and gated accordingly: metered tools require a spend ceiling and always return what they spent; judgement writes require a named approved_by; A4 is never automated.
  • Corpus quality over MCP. Quality sweeps, groundedness reporting and escalation classification (app/service/quality_sweep.py, groundedness.py, escalation_policy.py) are exposed as operator tools behind that rubric.
  • The Librarian worker starts with the dev stack. ./scripts/dev.sh now launches it by default, so ingested documents actually get extracted without a second terminal. Opt out with --no-worker, or point it at another brain with --worker-brain <id>.
  • Linkage-loop guide. The procedure for raising a corpus to publishable linkage quality, written for an agent to drive: Linkage Loop.

Changed

  • The answer model is now Kimi K3 (REASONING_MODEL=together:moonshotai/Kimi-K3). Measured on the six-fixture question base at n=3, K3 stands at 0.646 against GLM-5.2's 0.602 — a lift inside the 0.104 noise floor, so this is a reliability choice, not a scored win: K3 produced zero degenerate outputs across 18 runs, the failure mode that had been costing GLM 10–17% of its runs. It is materially more expensive — 7k–20k output tokens per answer at $15.00/1M, so a full suite score costs about $2.93 and an hour, against $0.37 and ~25 minutes. env/local/app.env.example states the trade next to the setting; switch back to together:zai-org/GLM-5.2 if answer-path cost matters more than clean measurements.
  • Unpriced model calls are now priced when you read them, not by rewriting history. The CallLog stays append-only; the cost registry is applied at query time, so adding a rate fills in past rows without mutating the ledger. GLM-5.2 and Kimi K3 both carry published rates now.
  • Extraction budgets are per window, not per document, so a long document no longer exhausts its merge budget partway through and silently returns a thinner result.
  • The SPA dev server is pinned to 5174, which stops it colliding with other local Vite projects on 5173.

Fixed

  • A materially thinner re-extraction is refused at approval. A change set that yields substantially less than the same document produced before is rejected rather than merged, so a degraded extraction can no longer quietly replace a good one.
  • Budget exhaustion ends a run with a report instead of a traceback. The loop caps its spend, stops, and still tells you what it did — previously the exception destroyed the result of everything that had already succeeded.
  • A failed measurement no longer settles anything. An INCONCLUSIVE verdict triggers a re-measure rather than a revert, so a provider outage stops looking like a regression.
  • Supplement I commentary is labelled as commentary, keyed on where the body starts rather than where the heading sits — a boundary that was first rejected outright and then over-applied.
  • Citation ranges no longer key to their opening section, and a CFR citation carrying both a title and a section sign is parsed correctly.
  • The test suite no longer writes to the developer's real corpus.

Upgrade notes

  • No database migration — this release changes no schema.
  • Answer-model cost changes materially. If you copy env/local/app.env.example, you are opting into Kimi K3 at roughly 8× GLM-5.2's per-answer cost. Set REASONING_MODEL=together:zai-org/GLM-5.2 to keep the previous default.
  • ./scripts/dev.sh now starts a Librarian worker. If you already run one by hand, pass --no-worker to avoid two workers draining the same queue.
  • Pin PYTHONHASHSEED before comparing loop scores. Retrieval is deterministic within a process and varies across them, so PYTHONHASHSEED=0 bb loop run … is required for a before/after comparison to mean anything.