v0.50.7
A system earns trust not by asserting its own correctness, but by refusing to proceed without proof of it.
v0.50.7 — Identity, Proven; Authority, Bounded
Ninety-seven commits. No new surface. Every one of them narrows the gap between what Ouroboros claims about a run and what it can prove about it.
This is a patch release in version only. In substance it is a meditation on boundaries — the places where the system used to trust an inference, a string, a path, a verdict — and the deliberate, sometimes painstaking work of replacing each of those trusts with evidence.
The thinking behind this release
1. Identity must be proven, not inferred.
The largest single campaign in this release — some forty-five commits — anchors every session to a canonical project identity derived from what Git itself asserts: gitfile ownership, worktree-specific configuration, explicit owner precedence, bounded config grammar. Path heuristics are gone. When a worker's working directory cannot be resolved, the orchestrator now fails closed rather than guessing. Resume preserves identity across modes; publication re-validates workspace ownership before it writes. The question "which project is this session about?" now has exactly one answer, and it is Git's answer.
2. State belongs to the application, not the connection.
The MCP boundary was rebuilt on the official SDK v2 stateless protocol (#1807). Connection-scoped initialization is gone; explicit session_id, execution_id, and job_id handles can be resolved by any server instance. The modern MCP 2.0 process is fully isolated from the Claude Agent SDK's incompatible MCP 1.x world through split install profiles — two eras of a protocol, each sealed in its own environment (#1804, #1818).
3. Frugality is a kernel, not a preference.
Routing gained a deterministic Admission Kernel (#1735): a versioned, provider-neutral registry of route candidates where the cheapest eligible route wins — by construction, not by prompt. The Advisor may break equal-cost ties; it may never authorize, create, or make an expensive route beat a cheaper one. On top of it, cheapest-first bounded escalation (#1744) climbs the cost ladder only when durable observations justify it, and hands off with an explicit BLOCKED rather than a silent retry. Live decisions now flow through admission (#1739); Codex model routing state became canonical (#1765). The Final Gate remains the sole acceptance authority.
4. A fact is not a decision.
An interview answer prefixed [from-code] or [from-repo] is a fact the system observed, not a requirement the human chose. Yet every consumer used to append answers verbatim — so an adopted observation could quietly become an acceptance criterion nobody decided. Provenance is now settled once, at the single point where an answer enters, carried as a typed field, and withheld from requirement extraction (#1823). No Seed criterion may exist without a decider.
5. A write must survive its own failure.
Checkpoint writes are staged so a failed save keeps the old chain (#1836). Writes are fenced across shutdown (#1820). Interview artifacts persist atomically (#1720) and are owner-only from the moment they exist (#1757). The plugin URL cache refreshes in stages so a failed clone keeps the old cache (#1834). Config and credential YAML I/O is pinned to UTF-8 (#1841), and Windows consoles without UTF-8 no longer crash the run (#1819). The failure mode of every one of these paths is now the previous good state, never a torn one.
6. Evidence over verdicts.
Ralph's stop decision is gated on the QA result, not on a verdict string that a model happened to emit (#1828). Write evidence is bound to the claimed path (#1764); deliver-command evidence matching was aligned (#1745); Codex completion evidence became consumable by verifiers (#1740) and tool completions are journaled from lifecycle events (#1732). The verify gate no longer rejects a passing run because pytest touched __pycache__ (#1738). The TUI fails closed on reported tool failures instead of rendering them green (#1742). Where a verdict and the evidence disagree, the evidence now wins — everywhere we found the disagreement.
7. Discipline, turned into ratchets.
Module sizes are ratcheted in CI so god-modules can only shrink (#1806). Every pull request must reference an issue (#1778). Structured contracts continue to replace prose: brownfield list fields, exit conditions, evaluation principles, ontology fields, and context references are now JSON object arrays (#1730, #1762, #1767, #1792), with parsing that tolerates the fences and prose real models produce (#1734).
Architecture
┌──────────────────────────────────┐
│ Canonical Project Identity │
│ proven from Git topology — │
│ gitfile ownership, worktree │
│ config, explicit owner rules │
└────────────────┬─────────────────┘
│ anchors every session
┌────────────────────────┼────────────────────────┐
▼ ▼ ▼
┌───────────────────────┐ ┌───────────────────────┐ ┌───────────────────────┐
│ Stateless MCP v2 │ │ Admission Kernel │ │ Fenced Persistence │
│ boundary (mcp 2.0) │ │ (Routing A + B) │ │ staged checkpoints, │
│ explicit handles: │ │ cheapest eligible │ │ atomic artifacts, │
│ session / execution │ │ route wins; the │ │ shutdown fences — │
│ / job — resolvable │ │ Advisor advises, │ │ failure keeps the │
│ by any instance │ │ never authorizes │ │ previous good state │
└───────────┬───────────┘ └───────────┬───────────┘ └───────────┬───────────┘
│ │ bounded, cheapest-first │
│ ▼ escalation │
│ ┌───────────────────────┐ │
└─────────────▶│ Final Gate │◀─────────────┘
│ the only acceptance │
│ authority — verdicts │
│ follow evidence, │
│ never claims │
└───────────────────────┘
Identity anchors the session. The kernel bounds what may spend. Persistence guarantees the past. And one gate — only one — decides what is done.
The complete, auto-generated list of changes follows below.
What's Changed
- fix(orchestrator): scope dependency-analysis CLI paths to matching executables by @berkshirehathaways in #1725
- feat(seed): JSON-array contract for brownfield list fields (#1729, slice 1) by @sumin220 in #1730
- fix(pm): keep launch outcomes out of acceptance criteria by @berkshirehathaways in #1727
- fix(interview): atomically persist resumable artifacts by @berkshirehathaways in #1720
- fix(auto): preserve explicit Seed acceptance contracts (rebased #1679) by @Q00 in #1717
- fix(codex): journal correlated tool completions from item lifecycle events by @sumin220 in #1732
- fix(orchestrator): make Codex completion evidence consumable by verifiers (#1724, consumer slice) by @Q00 in #1740
- feat(orchestrator): add deterministic Routing B admission kernel by @Q00 in #1735
- fix(tui): fail closed on reported tool failures instead of rendering them green by @Q00 in #1742
- feat(routing): project live decisions through route admission by @Q00 in #1739
- fix(mcp): omit unset optional FastMCP arguments by @berkshirehathaways in #1726
- fix(mcp): expose evolve input guidance through FastMCP by @berkshirehathaways in #1718
- fix(orchestrator): align deliver command evidence matching by @Q00 in #1745
- fix(#1741): tool_calls_count double-counts every tool: results carry tool_name and increment the counter by @ouroboros-agent[bot] in #1743
- fix(release): sync bundled setup version marker by @berkshirehathaways in #1719
- feat(routing): execute cheapest-first bounded escalation by @Q00 in #1744
- feat(bigbang): extract EVALUATION_PRINCIPLES and EXIT_CONDITIONS as JSON object arrays (#1729 slice 2) by @sumin220 in #1762
- fix(release): harden version synchronization by @sergiobuilds in #1750
- docs: quote pip extras install examples by @nyxst4ck in #1752
- refactor(text): give the two real truncation duplicates one implementation by @Q00 in #1788
- fix(providers): share response_format handling so the schema guard reaches every adapter by @Q00 in #1786
- fix(config): parse .env with python-dotenv instead of Python string syntax by @Q00 in #1784
- refactor(evidence): back both command normalizers with one implementation by @Q00 in #1782
- fix(setup): parse pyproject.toml instead of substring-matching it by @Q00 in #1780
- fix(orchestrator): reject non-finite pause_seconds instead of raising by @MyoungSoo7 in #1776
- fix(decomposition): make verified MECE bounce-only by @Q00 in #1763
- chore(deps): bump the python-minor-patch group with 6 updates by @dependabot[bot] in #1761
- chore(deps): bump actions/checkout from 7.0.0 to 7.0.1 by @dependabot[bot] in #1760
- chore(deps): bump softprops/action-gh-release from 3.0.1 to 3.0.2 by @dependabot[bot] in #1759
- chore(deps): bump actions/setup-python from 6.3.0 to 7.0.0 by @dependabot[bot] in #1758
- fix(core): interview artifacts are owner-only from the moment they exist by @cohemm in #1757
- fix(hooks): honor Codex UserPromptSubmit JSON contract by @shaun0927 in #1753
- fix(cli): don't require LiteLLM for
ouroboros interview listby @rkfshakti in #1748 - fix(#1736): 0.50.6 verify gate rejects passing Claude runs when pytest mutates pycache by @ouroboros-agent[bot] in #1738
- fix(tui): truncate AC labels by display cell width by @Q00 in #1707
- ci: require every pull request to reference an issue by @Q00 in #1778
- feat(bigbang): extract ONTOLOGY_FIELDS as JSON object arrays (#1729 slice 3) by @sumin220 in #1767
- fix(orchestrator): cache /proc/stat btime so drift cannot fake process death (#1699) by @chojuyeon94 in #1790
- feat(bigbang): extract CONTEXT_REFERENCES as JSON object arrays (#1729 slice 4) by @sumin220 in #1792
- feat(project-map): anchor sessions to canonical projects by @Q00 in #1769
- fix(tests): make the watchdog and logging flakes deterministic by @Q00 in #1795
- feat(project-map): rebuild runs from EventStore by @Q00 in #1789
- feat(mcp): adopt stateless SDK v2 protocol boundary by @Q00 in #1807
- docs(mcp): align host guides with isolated launchers by @Q00 in #1818
- fix(bigbang): lenient non-finite tokens default to 1.0 instead of sign-saturating (#1766) by @sumin220 in #1800
- fix(auto): detect git worktrees as brownfield by @seiyeong in #1808
- fix(mcp): isolate PM and interview question generation from host MCP servers (#1768) by @sumin220 in #1804
- refactor(seed): weigh acceptance criteria by category, not by count by @cohemm in #1811
- fix(core): fail open on unreadable mechanical.toml and malformed Info.plist by @MyoungSoo7 in #1775
- perf(project-map): share the git capability probe per session start (#1796 L1) by @sumin220 in #1805
- Make Codex model routing state canonical by @hackertaco in #1765
- fix: harden persisted status and Auto repair boundaries by @seiyeong in #1817
- fix(persistence): fence writes across shutdown by @Q00 in #1820
- fix: resolve UnicodeEncodeError on Windows with non-UTF-8 consoles by @ce-dric in #1819
- feat(worktrees): default cleanup policy to prune-merged by @Q00 in #1803
- fix(observability): route cached loggers through the live sink by @sumin220 in #1801
- fix(orchestrator): bind pathlib write evidence to claimed path by @lee3Q in #1764
- fix(parsing): tolerate markdown fences and prose in LLM JSON responses by @MyoungSoo7 in #1734
- fix(auto): reject non-runnable acceptance contracts by @sergiobuilds in #1709
- feat(interview): withhold observation answers from requirement extraction by @cohemm in #1823
- ci(module-size): ratchet src module sizes so god-modules can only shrink by @Q00 in #1806
- fix(persistence): stage checkpoint writes so a failed save keeps the old chain by @sumin220 in #1836
- fix(config): pin UTF-8 on config and credentials YAML I/O by @sumin220 in #1841
- fix(providers): let the claude backend run where the SDK cannot (#1839) by @cohemm in #1840
- fix(tui): stop reporting pause/resume the monitor cannot perform by @handlecusion in #1842
- fix(orchestrator): gate Ralph's stop decision on the QA result, not the verdict string by @MyoungSoo7 in #1828
- fix(cli/plugin): stage URL cache refresh so failed clones keep the old cache by @sumin220 in #1834
New Contributors
- @nyxst4ck made their first contribution in #1752
- @rkfshakti made their first contribution in #1748
- @chojuyeon94 made their first contribution in #1790
- @seiyeong made their first contribution in #1808
- @ce-dric made their first contribution in #1819
- @handlecusion made their first contribution in #1842
Full Changelog: v0.50.6...v0.50.7