DEV Community

Cover image for Ota v1.6.23 Now Available: Machine-Readable Execution Governance
Bobai Kato for Ota

Posted on • Originally published at ota.run

Ota v1.6.23 Now Available: Machine-Readable Execution Governance

Idea

v1.6.23 is a machine-readable governance release.

The pressure behind this version is straightforward: if Ota asks humans, CI, and AI agents to share one execution contract, then governance cannot stay partly implicit.

It has to be emitted as structured truth.

This release pushes exactly that boundary. Ota now publishes clearer governance posture before execution, clearer evidence after execution, and clearer drift ownership when contracts diverge from external repo sources.

At the same time, v1.6.23 widens first-class contract ownership for setup, orchestrator mediation, and deterministic Git materialization so more repos can stay out of shell glue.

Feature

v1.6.23 is broad, but the release is coherent. It strengthens six connected execution-governance surfaces.

1. Governance output is now significantly more machine-readable

This release adds and widens core machine output that CI systems and agent harnesses can consume directly:

  • additive governance summaries on ota run --dry-run --json and ota up --json
  • additive governance.merge_gate and governance.required_verification_lanes on ota doctor --json
  • additive metadata.governance.merge_check_id for stable merge-lane identity
  • additive capability_profile on ota tasks --json and ota workflows --json
  • additive artifact_routing[] across dry-run, receipt, proof, repo up, and workspace lanes
  • additive stage_family and phase signals across receipts, proof JSON, and workspace progress streams

The practical effect is important: external consumers can now read governance posture, execution lane identity, and follow-up artifact routing without scraping terminal prose or reverse-engineering semantics from mixed fields.

2. Agent safety moved from guidance toward enforced runtime behavior

v1.6.23 hardens agent-mode governance in two ways.

First, ota run --agent and ota up --agent now enforce execution-boundary safety at preflight time, with explicit refusal behavior and structured blocked receipts/results.

Second, governance evidence now carries clearer phase semantics (not_run_reason, crossing_record_state) so non-executed lanes are no longer ambiguous in machine output.

This is the right direction for serious agent operations: safety has to be enforceable and inspectable, not only documented.

3. Crossing and merge governance became explicit

This release introduces the first audited crossing-evidence slice for heavier lanes:

  • additive --reason <text> support on ota run and ota up
  • optional receipt.crossing on repo-target execution receipts
  • mirrored governance.crossing on ota up --json where applicable

In parallel, merge-oriented governance output gets stronger with projected-vs-drift-aware merge gates and required verification lane projection.

Together, these changes make Ota much better at answering two high-value questions in machine-readable form:

  • should this lane run under this governance posture?
  • what exact verification identity should merge consumers trust?

4. Source-governance and detect pressure widened substantially

v1.6.23 also expands V11.2 detector-source governance and drift review pressure across real repo-owned sources:

  • mise.toml, .devcontainer/devcontainer.json (including JSONC), devbox.json, and devenv.nix
  • structured external AGENTS.md / CLAUDE.md boundaries and bounded command-table lanes
  • Taskfile/justfile and GitHub Actions verification-lane recovery and drift warnings

The key product move is not “detect more things.”

It is that Ota now treats source provenance and source-class confidence as first-class governance signals, and warns when manual contract truth drifts from stronger external evidence.

That helps keep contracts honest as repos evolve.

5. First-class setup and action ownership widened again

This release also removes more setup-shell pressure by widening structural ownership:

  • prepare.kind: tool_bootstrap now includes prepare.tool: cypress_browsers
  • prepare.kind: dependency_hydration now supports source.kind: composer
  • action.kind: ensure_git_template added for template materialization with fresh init
  • action.kind: ensure_git_checkouts added for multi-checkout bootstrap
  • ensure_git_checkout/ensure_git_checkouts widened with remote wiring and workspace-relative targets
  • first-class effects.workspace_writes for explicit sibling/workspace mutation ownership
  • compose.build: true and compose.service_ports: true on compose.kind: run

This continues Ota's core posture: if the behavior is real and repeatable, it should be modeled structurally in contract truth.

6. Orchestrator ownership became broader and more usable

v1.6.23 widens orchestrator mediation beyond earlier slices:

  • first-class devbox and devenv orchestrator kinds
  • mediated command previews across task, exec, and subcommand
  • orchestrator-mediated prepare execution for command-backed hydration/bootstrap via execution.orchestrator.mode: exec
  • native activation ordering fix for command-acquired tools used by orchestrated lanes
  • ota doctor --fix widening to deterministic command-acquired tool activation

This closes a common real-world gap: repos can now keep orchestrator truth in contract-owned execution lanes without pretending host PATH ownership or dropping back to wrapper scripts.

Docs

If you want to adopt these capabilities directly, use the live docs:

  • Get Started: Install Ota and begin with ota doctor
  • Governance model: Governance for merge posture, safety posture, and machine-readable governance truth
  • Architecture loop: Execution Governance Loop for how contract, execution, proof, and governance fit together
  • Contract reference: Contract for tasks, workflows, setup, actions, and governance fields
  • Command reference: Commands for ota doctor, ota tasks, ota workflows, ota run, and ota up
  • JSON output: JSON output reference for governance, receipt, proof, and artifact-routing output
  • Workflow modeling: Workflows for verification lanes, workflow intent, and execution boundaries

Release

v1.6.23 is live here: https://ota.run/releases/v1.6.23

If you already use Ota, upgrade and verify:

ota upgrade
ota --version
ota validate
ota doctor --json
ota tasks --safe --use
ota run <task> --dry-run --json
Enter fullscreen mode Exit fullscreen mode

v1.6.23 keeps Ota on its core path: one declared contract, one governed execution model, and machine-readable evidence that lets humans and agents trust the same operational truth.


Original post here: https://ota.run/blog/ota-v1-6-23-release-essay

Top comments (14)

Collapse
 
vinimabreu profile image
Vinicius Pereira

governance has to be emitted as structured truth is the right target, but the hard half isn't emitting it, it's keeping the emitted thing honest after it ships. a JSON receipt that says approved: reason X can lie exactly as confidently as a human comment, and it's worse because it looks like data so nobody re-reads it. the failure i keep hitting: someone edits the enforcement branch, nobody touches the code that writes the governance field, and now the structured truth is structured and wrong. what i'd want next to these fields is a reconciliation guarantee, the receipt built on the same line where the decision is made rather than assembled afterward from a second read of state, plus a preflight check that what the receipt claims fired actually fired. move the reason into the branch that grants it and there's no second place to forget it.

the --reason flag on crossing/merge is the same theme one level down. a free-text reason a human types is self-attested, so the receipt captures what the operator claimed, not what got verified. still a real gain because the claim is now attributable, but it'd be worth making the schema say which fields are captured (the human asserted this) vs derived (the system checked this), since an agent consuming them downstream will otherwise treat both as equally true. good direction, evidence as a first-class output is what agent contracts have been missing.

Collapse
 
bobaikato profile image
Bobai Kato Ota

Strong point. Emitting governance as JSON is only half the job; the harder part is keeping it reconciled with the exact branch that made the decision.

The direction for Ota is the same one you’re describing: the authoritative record should be emitted at the decision site, not reconstructed afterward from a second read of state, and the schema should distinguish caller-asserted fields from runner-derived or boundary-attested ones.

That’s the next governance trust refinement I’m adding to Ota.

Where have you seen this drift most in practice: the runner decision itself, CI/merge wiring, or the emitted receipt/evidence layer?

Collapse
 
vinimabreu profile image
Vinicius Pereira

the receipt/evidence layer, and it's not close, but the reason is the useful part: drift tracks distance from the decision. the runner decision itself basically can't drift, it does whatever the branch says by definition, it IS the source of truth. CI/merge wiring drifts in one narrow way, a threshold duplicated between the gate and the runtime so the gate blesses something the runner would have scored differently, but you catch that the first time the two disagree loudly. the receipt is where it hurts because it's maximum distance: written later, usually in a different module, from a second read of state, and it's the exact layer everyone downstream trusts as THE record. so the drift is easiest precisely where the blast radius is biggest.

the concrete shape i hit most: a field recomputed at serialize-time. a row gets scored while spend reads 6000, then a later normalization stage rewrites spend into a bucket, and when the receipt re-reads spend to explain the score it cites the bucket, not the 6000 the decision actually used. nothing errored, the score is unchanged, the story next to it just quietly stopped matching the inputs it was built from. that's the whole reason i test that the points named in the reason sum to the score, it's the cheapest tripwire for exactly this.

on your taxonomy, caller-asserted / runner-derived / boundary-attested is the right cut and boundary-attested is the sneaky bucket. it FEELS derived because the system observed it, but it's captured from outside your trust boundary, so its ceiling is only as good as whatever attested it. if you don't tag it explicitly it silently collapses into runner-derived and gets trusted a notch above what it earned, which is the same class of bug as a narrated reason, just about provenance instead of logic.

Thread Thread
 
bobaikato profile image
Bobai Kato Ota

Very apt!

I agree on boundary-attested being the sneaky bucket. It arrives structured so people instinctively over-trust it, but provenance-wise it is still only as strong as the boundary that attested it.

This is directly shaping Ota for me too: authoritative governance needs to be recorded at the decision site, not recomputed later, and provenance class needs to stay explicit instead of collapsing into one generic “machine-generated” bucket.

When this goes wrong in practice, what usually ends up being the most useful tripwire first: field-level reconciliation like your points-to-score check, hook/branch identity checks, or provenance-class mismatches?

Thread Thread
 
vinimabreu profile image
Vinicius Pereira

field-level reconciliation, and it's not close either, for a reason that generalizes: it watches the outcome, not the mechanism. a reconciliation check fails only when the record is already lying, which means zero false alarms from benign refactors, and it catches the end symptom regardless of which upstream cause produced it, renarrated field, stale threshold, serialize-time recompute, all of them eventually break the arithmetic. one tripwire, many failure classes, and it costs an afternoon: parse your own output, assert the parts sum to the whole. it's also the only one of the three that's self-explanatory when it goes red, whoever sees the failure understands the crime without knowing your taxonomy.

provenance-class mismatch is the second layer, and the reason it can't be first is also the reason it's indispensable: it catches the lie reconciliation can't see. a receipt can reconcile perfectly while citing boundary-attested data as if it were derived, the arithmetic is clean and the trust label is wrong. the practical version isn't really a tripwire though, it's a schema decision: make the class a REQUIRED field on every value, and the silent collapse into "machine-generated" becomes a validation error instead of a judgment call. you get the check almost for free by refusing to let the field be optional.

hook/branch identity checks i'd build last or not at all. they watch the mechanism, and mechanisms churn legitimately, every refactor makes them cry wolf, and a check that cries wolf gets disabled, which is worse than not having it. the one place they earn their keep is when the decision output has no arithmetic to reconcile, pure allow/deny with no parts, then identity is the only handle left. so the ordering principle i'd hand ota: first the check that fails only when the harm is real, then the one that catches lies arithmetic can't see, and only then the ones that watch how instead of what.

Thread Thread
 
bobaikato profile image
Bobai Kato Ota

Watch the outcome first, not the mechanism, that’s a very strong rule.

Field-level reconciliation catches the real harm with almost no false alarms, and provenance-class checks catch the trust inflation that arithmetic cannot see.

So, when the decision is pure allow/deny with no arithmetic to reconcile, what has ended up being the least noisy tripwire in practice?

Thread Thread
 
vinimabreu profile image
Vinicius Pereira

two answers, because the first move is refusing the premise when you're allowed to. pure allow/deny is usually a schema smell, not a fact of life: require every verdict to carry its reason codes, deny must cite at least one blocker, allow must cite the gates it passed, and assert the implication both directions. a deny with no cited blocker is already a lie, an allow that coexists with a live blocker code is a worse one. that's reconciliation without arithmetic, the parts are codes instead of numbers and the whole is the verdict, same noise profile as the field-level check: it only fires when the record contradicts itself, refactors can't trip it. i'd spend real effort trying to un-pure the decision before accepting it as pure.

when the schema is genuinely frozen, the least noisy thing i've used is pinned replay. the receipt already names its input hashes and ruleset version, so a job re-derives the verdict from exactly the cited inputs and asserts equality. it fails on three crimes only: a hidden input (something influenced the decision but wasn't cited, so replay diverges), an unpinned rule change (logic moved, version claim didn't), or nondeterminism (the decision was never a function at all). all three are precisely the lies you care about, and none of them are triggered by refactors that keep the function honest, because replay doesn't care how the answer was produced, only that it's still a function of what it claims to depend on. it's the same watch-the-outcome rule wearing different clothes: the derivation is the arithmetic.

practice notes: run it on two feeds. a golden fixture set on every deploy catches rule drift before it ships, and a nightly sample of production receipts catches hidden inputs that fixtures never exercise. the first week is noisy in the honest way, every failure is a census entry for an input you didn't know you had, clock and feature flags being the usual suspects. promote those to cited inputs and it goes quiet. after that, silence actually means something: the receipt is still telling the truth about what decided.

Thread Thread
 
bobaikato profile image
Bobai Kato Ota

And pinned replay is a very strong second layer! I especially like the distinction there: if replay fails, it usually points to one of three real problems, hidden input, unpinned rule change, or nondeterminism. Those are exactly the lies worth catching.

From an Ota point of view, it gives a clean ordering: first reconcile the verdict against its declared blocker or gate codes, then use pinned replay where the decision really is function-like.

Question: when replay gets noisy in that first week, which hidden inputs have turned out to be the most common offenders in practice?

Thread Thread
 
vinimabreu profile image
Vinicius Pereira

in my pipelines the census always converges to the same ranking. number one by a distance: the clock, wearing costumes. it's almost never a literal now() inside the decision, it's the freshness comparison ("is this fact stale"), the ttl check, the day-of-week feature, the timezone conversion that makes a receipt replay differently at 23:50 than it derived at 23:49. the tell is replay failures that cluster around midnights and month boundaries. the fix is making evaluatedAt a cited input and having replay inject it, never read the wall. once the clock is a citation, half the first week's noise is gone.

number two: configuration that lives outside the pinned version. the ruleset hash was faithful, but a feature flag service answered live, or a threshold table got hot-reloaded, so tuesday's receipts became unreplayable by wednesday with zero code changes. anything that can change decision behavior belongs inside the version claim: snapshot the flags into the receipt, or fold their hash into the ruleset hash. there is no third option that survives contact with a flag flip.

number three, and in llm pipelines it fights the clock for first place: floating model aliases. an alias like flash-latest answers differently next month, embeddings get upgraded under you, and temperature 0 narrows nondeterminism without sealing it. the fix that actually works is not replaying through the model, it's demoting the model to a witness: record its reading as a cited input, replay the decision from the reading. the model run gets corroborated separately; the decision replay stays a pure function.

honorable mentions that appear once per codebase and never again after promotion: lookup tables that refresh quietly under a join (fx rates, geo, category maps: hash the row you used, not the table name), and tie-breaks that inherit iteration order, set or dict ordering deciding "first of equals", which is nondeterminism cosplaying as determinism. the pattern across all of them is the same: the offenders are never exotic. they're the dependencies too boring to declare. and the census works because every promotion is permanent, the schema grows a field, and that entire class of failure never comes back.

Thread Thread
 
bobaikato profile image
Bobai Kato Ota

The ranking is very clear, and it maps well to Ota: clock first, external config second, and floating model aliases as witness-not-decider.

From an Ota point of view, this sharpens the implementation order a lot:

  • time and evaluation timestamp need to be cited inputs, not ambient reads
  • mutable config and flags need to be folded into pinned decision identity
  • model outputs should be treated as witnessed inputs, not replayed as the decision function itself

So, when you promote one of these hidden inputs into the schema, what has made that promotion feel lightweight enough that teams actually keep doing it instead of treating it as audit overhead?

Thread Thread
 
vinimabreu profile image
Vinicius Pereira

the honest answer is that it stops feeling like overhead the moment you stop doing it proactively. nobody keeps up a discipline whose only payoff is a future audit, that is work you do for someone else later and it always loses to shipping. what survives is promotion triggered by a bug that already cost you the afternoon. a replay flakes, you spend an hour proving it was now() or a hot-reloaded flag, and citing that input is the same edit that closes the ticket, not extra work stacked on top of it. the audit trail is a side effect of fixing your own flake, so it costs nothing you were not already spending.

two things keep it lightweight after that. it has to be a one-liner at the point of writing: append a field to the receipt and have replay inject it, never a migration or a review ceremony, so the schema has to be additive and forgiving, old receipts stay valid when a new field shows up. and the payoff has to land the same session, replay goes green today, not "an auditor will thank you in q3." teams repeat what makes this afternoon easier and drop what helps a hypothetical reader.

but the real reason it does not rot is that it is finite and permanent, which audit overhead never is. the set of boring inputs is not infinite: the clock, the flags, the model alias, the lookup row, the iteration order, and each promotion retires its whole class forever. so the flaky-replay rate falls every week and the backlog visibly shrinks toward zero. that is the felt difference. audit overhead is sisyphean and speculative, you guess at what a future reader wants and the pile never goes down. promotion is a punch-list you are paying off, earned one bug at a time, and you can see it ending. people keep doing the thing that is clearly almost done.

Thread Thread
 
bobaikato profile image
Bobai Kato Ota

That’s very useful. Thanks!

The “same edit that closes the ticket” point is the part that really sticks. It means this only works if promotion feels like the smallest bug fix, not governance ceremony.

That’s a very good design constraint for Ota too.

A question I’d be curious about from your side: when teams do this well, what makes the shrinking backlog most visible in practice, just fewer replay flakes, or do you also track “hidden input classes retired” explicitly?

Thread Thread
 
vinimabreu profile image
Vinicius Pereira

Both, but they answer different questions. Fewer replay flakes is a treacherous headline signal on its own, it is a rate, and it can go quiet because you ran fewer replays or the flake moved somewhere you are not watching, not because the backlog shrank.

What actually makes it visible is a retired-class ledger where each retirement ships a guard, an assertion that fails if that class ever returns. That count only goes up, monotonic and permanent, which is the property the flake rate lacks. The honest catch: you can only count classes you have named, so a fresh flake is not a regression, it is a discovery that refills the list. Guards-installed going up is the backlog you can see ending, new flake classes appearing is the unknown set still leaking.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.