Repository navigation
io-cli 0.24.0 - #62
Merged
Merged
Conversation
src/gates.rs holds what "done" means for a repository: the [app.io-cli.gates] section as written, the one criterion it resolves to, and the two refusals the file itself cannot express. The module is gates and not verify — src/verify.rs is already the wizard's live credential check, and two modules named for the same word would be a reader trap for the life of the product.
…mmand Settings::criterion resolves [app.io-cli.gates] to one Criterion or to a Refusal, and the refusals are the point: a rubric with no reviewer, or a reviewer that is the model doing the work, is Error::Config at run start in io-harness, so catching them at the file is the difference between a config edit and a session that will not start. The proposed command comes from io_harness::toolchain::detect layered with Config::toolchain. io-cli holds no marker table and no command table, and a test sweeps this module's comment-stripped source to keep it that way. File existence maps to Verification::None and is answered here through read_bytes. WorkspaceFileContains reads with unwrap_or_default, so an empty needle passes on a file nobody wrote; checked_here() exists so a caller reading verification() alone cannot report an ungated run.
The field, its catalogue keys, and a worked example of all three kinds. settings::render writes gates: None, following the containment precedent. A file arriving from the wizard with a gate already in it would hold back every turn of an operator who never said what done means here, and a review criterion would spend a second model's completion doing it. The example ships commented out, because the docs gate writes that file to disk and discovers it.
Drawn by fields() and by the hand-built footer alike, absent when no criterion is configured, and carrying the attempt number only from the second attempt. Cleared by start_run as well as forget_run. forget_run is reached by /resume, /fork, /clear and rewind but not by the operator typing a second prompt, so without this turn one's verdict is drawn over the whole of turn two.
Routes gate_phase_failed and gate_output, which src/triage.rs has named since 0.20.0 as belonging to a gate no session had yet. A failing review no longer draws in Tone::Refused. That tone's word is refused, which is the permission boundary's word, and it conflated the policy declining to run a gate with the work not meeting the bar. Those need opposite responses, and a test now asserts the two vocabularies stay disjoint.
In contract::configured, the half io exec and a session share. A gate applied in the session's half would hold a terminal to a standard and let CI run to green with nothing checked. The criterion and its reviewer are decided together: a Review reaching a contract with no reviewer is Error::Config at run start, so either both go on or neither does, and a refusal leaves the run ungated with a notice rather than dead. Printable wraps a provider so it can satisfy ModelReviewer's Debug bound. None of io-harness's own provider types implements Debug, which makes its only shipped Reviewer unusable from any crate that uses them. The Debug impl prints the vendor name and never the credential.
A wrapper around exec::code rather than an arm inside it. That function's body is extracted by a test splitting on its exact signature line, and its table is a statement about RunOutcome, which carries no verification verdict at all. Only a gate that Failed earns 6. One that Errored never answered, and reporting that as the work not holding up would claim a judgement nobody made.
The 31st command, in Configure because it writes the configuration file. Its description names all three kinds, because two cost a process and one is a billed completion on every gated turn. README gains the gates section, the exit 6 row, the [app.io-cli.gates] key, and corrections to two claims this release falsifies.
…ure in hand /gates shows the criterion in force, what the repository's own toolchain proposes, and the last run's verdict, and writes a criterion to a chosen scope. A review criterion with no reviewer, or one whose reviewer is the working model, is refused before the write rather than at run start. A failing gate composes a follow-up turn carrying the criterion and what it reported, fed through the queue the driver already has. gate_retry takes no goal, so a retry that re-sends the original prompt cannot be written. The retry budget is accumulated across the chain. Store::gate_attempts is keyed on the run, and every session turn is its own run, so counting per run restarts at one on each retry and would retry forever against a real model. /mcp gains an edit verb, closing the limitation stated since 0.21.0. The entry is re-resolved by id before the write, never addressed by the picker's row index.
tests/provider.rs requires every vendor constructor to be written exactly once outside the wizard handshake and the /provider panel, so the interactive and headless entry points cannot drift apart. It refused this release twice: once for constructing in gates.rs, once for constructing in provider.rs beside the real site. A reviewer is a third construction, like the wizard's ping. It never drives a turn and deliberately runs a different model from the one doing the work, so merging it into the session's maker would make the second opinion change model whenever /model did. src/reviewer.rs is excluded by name like the other two, and held to that boundary by a new test: it must build a ModelReviewer, may name nothing that drives a turn, and may be called from the contract and nowhere else.
GateOutcome is not in scope in this module, so the intra-doc link could not resolve. Written in plain backticks like every other io-harness item named in this crate's docs. Seventh release in eight that this gate has caught something cargo doc, clippy and the suite all pass over. It is not in ci.yml; it is run by hand at the seal.
Three arms. A command criterion that passes returns Success rather than Finished, which is the whole release in one assertion: until now every contract this crate built carried Verification::None, so Success was unreachable from this interface. A criterion that cannot pass is recorded as failed and exits 6, whatever the run itself reported. And a rubric is judged by a second model, with EventKind::Reviewed carrying the verdict and a refusal carrying reasons, because the reasons are the only thing the operator can act on and the only thing the retry can carry. The criterion is asserted on the contract before the run as well as after it. A run that succeeded with no criterion attached would look identical from the outcome alone, which is the confusion this release ends.
The worst of it: io-harness evaluates the contract's criterion after every step on which the agent called a tool, and for Verification::None that evaluation is false — so it records phase=none, Failed on every ungated run this product has ever driven. Read back naively that says the gate failed. io exec would have exited 6 for every operator who has never configured a gate, and every ordinary session turn would have drawn 'gate failed'. Those rows are now dropped unconditionally. The retry counted those same rows, so its attempt number was the step count and may_retry compared nine against a budget of one. The headline feature was dead for every criterion except bare existence. The driver now keeps one row per turn. A parked, denied or cancelled run was given a verdict and retried: the operator was told '/resume opens it' and then had that run buried under a fresh billed turn. Only a run that reached its own end is judged. Configuring any gate silently switched off conversational classification, so 'hello' started a full agent run and then ran the test suite after every step of it. Set explicitly where a criterion is attached. Also: the fold moved to gates.rs because io exec may not reach the interface; a failing gate no longer borrows the permission boundary's word; the reviewer refusal no longer formats a ProviderSpec, which holds the api_key; an unknown gates key is refused rather than silently disabling the gate; an empty command argv is refused here rather than erroring at run start on every turn; /gates named the wrong io.toml and re-judged past turns against today's filesystem; and io exec now says when a configured section is not gating the run. The documentation claimed the gate runs once per turn. It runs after every step, which for a rubric is a billed completion per step. That was the one disclosure the design existed to make.
… item criterion_of is public and criterion_for is not, so the intra-doc link could not resolve under --document-private-items-less rustdoc. Written in plain backticks. Second catch by that gate in this release, and it is not in ci.yml.
Sealed during completion, on the feature branch, before the merge and the tag. The Release URL is deterministic so it is written now; the artifact byte sizes and the smoke on the published binary are not knowable yet and belong in the final report. Suite 86/1,255/0. Live 28/28 against a real model. clippy, fmt and the nightly rustdoc gate clean. Three adversarial reviews, four HIGH defects found behind a green suite and fixed. transition RELEASED did not pin the lock, for the thirty-third release running; pinned by hand.
drive.py is copied to the repository root from the gitignored evidence directory to run the live suite, and a git add -A during 0.24.0 caught a copy that was sitting there. It was removed in the next commit and the net effect on the tree is nil, but the ignore entry is what stops it happening a second time.
io-cli 0.24.0 — a turn proves its work instead of asserting it
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The release PR for 0.24.0 — a turn proves its work instead of asserting it.
[app.io-cli.gates]takes one criterion of three kinds: a command that must exit a status, a file that must exist and optionally say something, or a rubric a second model answers. It is attached incontract::configured, the halfio execand a session share, so CI is held to the same standard as the terminal. The verdict reaches the status line and the scrollback with what the command printed; a failing gate buys a follow-up turn carrying the failure text; andio execgains exit6.Until this release every contract io-cli built carried
Verification::None— which is whyRunOutcome::Successwas unreachable from this interface, andfinishedonly ever meant the agent stopped./mcpgains an edit verb, closing a limitation stated in the record since 0.21.0.Verified on the merged tree
cargo test --all-targets— 86 binaries, 1,255 passed, 0 failed, 28 ignored-D warnings,cargo fmt --check, nightly rustdoc — all cleanThree adversarial reviews found four HIGH defects behind a fully green suite — including one that would have made
io execexit6for every operator who never configured a gate. All fixed; the record carries the detail.The sealed record rides this merge, pinned in
.ultraship/releases.lock.