Skip to content

v3.0.0

Choose a tag to compare

@github-actions github-actions released this 25 Sep 22:31
· 121 commits to main since this release
3329c93

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): intent is now { action, topics }, and intent.hints, patterns and agentPrompt are gone. Read the ranked files and hotspots directly and let the model decide what the request means.
  • Impact (otito impact --json, change_impact): implementationPlan is gone. Rank, risk flags and suggested tests are unchanged.
  • PR review (otito pr --json, review_context): reviewPrompts and nextSteps are gone. Use risk.flags and reviewTargets, which carried the same information.
  • Convergence: untracked files are no longer scored by default. Pass --include-untracked / includeUntracked: true for the 1.x behaviour. Receipts issued by engine 0.1.0 do not recompute under 0.2.0; re-issue them.

Added

  • otito converge --head <ref> / convergence_score { head }: score exactly base..head. Convergence could only diff base against 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 untracked dump.rdb reported 29 changed files for a 25-file commit and listed all four extras as drift. head diffs 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 new git-commit subject (baseSha, headSha, treeSha) the way --staged binds to the index tree. --head and --staged cannot 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 a Commit snapshot check with scope: "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 into drivers.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 as index must sit beside their file. On the commit above, the new PersonDialog.tsx, SendMessageCard.tsx and three other components beside the PeopleTable.tsx owner, 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 untracked with a recommendation; --include-untracked / includeUntracked: true restores the old behaviour. change_impact still counts untracked files.
  • Receipts issued by convergence engine 0.1.0 do not recompute under 0.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 the patterns and agentPrompt fields, and their Markdown and terminal sections. intent is now { action, topics }.
    • impact: the implementationPlan field and its section.
    • pr / review_context: the reviewPrompts and nextSteps fields and their sections. They restated risk.flags and reviewTargets, which are unchanged.
    • Kept: classifyImpactRoles, because converge and model_route measure against its requiredOwners.
  • Measured against main on 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; impact output is 31% smaller as JSON. See docs/EVALS.md.

Fixed

  • Post-merge attestation attested whatever main was when the run started, not the commit it resolved. The workflow passed its target to the scripts as GITHUB_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 at b3f795d recorded fc0a7b9. The target is now passed as OTITO_TARGET_SHA, and GITHUB_SHA is 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 main since 2026-09-20, and CI had no way to recover. The durable ledger on audit-ledger holds 97 attestations whose commits are no longer in main's history. So every post-merge run stopped in reconcile-attestations.sh, correctly, and named a reset that only worked from a local shell. The workflow's manual trigger now takes reset_ledger, the only way an Actions run can set OTITO_ATTEST_RESET_LEDGER=1; a run started by CI never can. The persist step also keeps the archived chain (audit-pilot/ledger-orphaned-*.jsonl) on audit-ledger and in the uploaded evidence. Before this, a reset in CI would have written the archive on the runner and discarded it.