v3.0.0
The first major release since the Òtítọ́ cutover. It follows 1.15.0 directly: the v2.x tags belong to the Repoctx releases, so the version skips them.
Migrating from 1.x
- Context pack (
otito context --json,context_pack):intentis now{ action, topics }, andintent.hints,patternsandagentPromptare gone. Read the ranked files and hotspots directly and let the model decide what the request means. - Impact (
otito impact --json,change_impact):implementationPlanis gone. Rank, risk flags and suggested tests are unchanged. - PR review (
otito pr --json,review_context):reviewPromptsandnextStepsare gone. Userisk.flagsandreviewTargets, which carried the same information. - Convergence: untracked files are no longer scored by default. Pass
--include-untracked/includeUntracked: truefor the 1.x behaviour. Receipts issued by engine0.1.0do not recompute under0.2.0; re-issue them.
Added
otito converge --head <ref>/convergence_score { head }: score exactlybase..head. Convergence could only diffbaseagainst the working tree, so any dirty or untracked file was scope drift. Scoring a one-commit feature (--base HEAD~1) in a checkout with three edited.claude/*files and an untrackeddump.rdbreported 29 changed files for a 25-file commit and listed all four extras as drift.headdiffs the two trees directly (no merge base), builds the scoring map from the head commit's raw blobs, and binds a v2 receipt to a newgit-commitsubject (baseSha,headSha,treeSha) the way--stagedbinds to the index tree.--headand--stagedcannot be combined.otito gate --head <ref>/review_gate { head }. The local gate's changed-path, risk, secret, and convergence checks read the head commit's tree, reported as aCommit snapshotcheck withscope: "commit". A JSON receipt whose subject names a different mode or head than the gate measured now fails with the mode to rerun in (--head <sha>,--staged,--pr <n>) instead of a bare hash mismatch.
Changed
- Convergence counts a confirmed owner's own fan-out as in scope (engine
0.2.0). A file added beside a confirmed required owner (owner-sibling) and a test named after a confirmed file or inferred sibling (owner-test) move out of drift intodrivers.inferredRelated, each with its rule and anchor. Siblings must be mapped, non-secret, and carry no risk flag the owner lacks; generic test stems such asindexmust sit beside their file. On the commit above, the newPersonDialog.tsx,SendMessageCard.tsxand three other components beside thePeopleTable.tsxowner, and five tests of confirmed files, stopped counting as drift: 55/100 (Scope 21, Risk alignment 15) became 84/100 (Scope 64, Risk alignment 85) with--head HEAD. - Working-tree convergence no longer scores untracked files by default. They are listed under
untrackedwith a recommendation;--include-untracked/includeUntracked: truerestores the old behaviour.change_impactstill counts untracked files. - Receipts issued by convergence engine
0.1.0do not recompute under0.2.0; re-issue them.
Removed
- BREAKING: otito stops interpreting the request; the model it serves does that better. Several rankers and output fields tried to understand what a request meant using keyword rules. The agent reading the pack does that job better, so otito now returns the evidence and leaves both interpretation and ordering to the model. Removed:
- Context pack:
intent.hints, and the ranking boosts built on them (the MCP/CLI/tool/API/test hints, the signup-verification boost of +220, the RSVP-privacy boost of +120, the Handlebars template boost, and the CLI-entrypoint and agent-tool related-file boosts). Also thepatternsandagentPromptfields, and their Markdown and terminal sections.intentis now{ action, topics }. impact: theimplementationPlanfield and its section.pr/review_context: thereviewPromptsandnextStepsfields and their sections. They restatedrisk.flagsandreviewTargets, which are unchanged.- Kept:
classifyImpactRoles, becauseconvergeandmodel_routemeasure against itsrequiredOwners.
- Context pack:
- Measured against
mainon the 21 labelled retrieval cases. p@5 stays at 0.867 and r@5 at 1.0, and 21/21 cases still pass. MRR drops from 1.0 to 0.975. Only the three cases the heuristics were written for changed rank, each by one place, and every expected file is still in the top three. Context packs are 18% smaller as JSON and 31% smaller as Markdown;impactoutput is 31% smaller as JSON. See docs/EVALS.md.
Fixed
- Post-merge attestation attested whatever
mainwas when the run started, not the commit it resolved. The workflow passed its target to the scripts asGITHUB_SHA, which GitHub does not let a step override, so the scripts saw the runner's own value, the default-branch head. A run could attest a commit before that commit's CI had passed, and the ledger commit could name one commit while recording another: the reset meant to restart the chain atb3f795drecordedfc0a7b9. The target is now passed asOTITO_TARGET_SHA, andGITHUB_SHAis still honoured when the scripts run on their own. - The context pack reported a working tree that no longer existed. Its uncommitted-changes warning, and
repos[].git, came from the cached code map, which records git state once, when the repository is indexed. The index is only rebuilt when a file's size or mtime changes, and a commit changes neither, so a repository indexed with work in progress kept warning about "4 uncommitted git change(s)" and told agents to inspect a tree that was already clean. The pack now reads git state live on every call, which costs about 50 ms. Other reports already read it live, and the catalog keeps its snapshot from index time on purpose. - Nothing had been attested on
mainsince 2026-09-20, and CI had no way to recover. The durable ledger onaudit-ledgerholds 97 attestations whose commits are no longer inmain's history. So every post-merge run stopped inreconcile-attestations.sh, correctly, and named a reset that only worked from a local shell. The workflow's manual trigger now takesreset_ledger, the only way an Actions run can setOTITO_ATTEST_RESET_LEDGER=1; a run started by CI never can. The persist step also keeps the archived chain (audit-pilot/ledger-orphaned-*.jsonl) onaudit-ledgerand in the uploaded evidence. Before this, a reset in CI would have written the archive on the runner and discarded it.