Skip to content

io-cli 0.34.0 - #87

Merged
aakashpawar1999 merged 12 commits into
mainfrom
develop
Aug 31, 2026
Merged

aakashpawar1999 merged 12 commits into
mainfrom
develop

Conversation

@aakashpawar1999

Copy link
Copy Markdown
Contributor

Release PR for 0.34.0.

Closes the three surfaces that still printed io-harness's __ to a person, puts a gate under the rule, and pins io-harness 0.73.0 so a capability bundle stops being half-installed: a skill reads its own bundle's files without a shell, and a bundle's declared executable is resolvable inside a run.

Gates

  • cargo test --all-targets --no-fail-fast — 102 binaries, 1,824 passed, 0 failed, 37 ignored
  • cargo test --doc, cargo fmt --all -- --check, cargo clippy --all-targets -- -D warnings — exit 0
  • Sabotage: eight arms, all eight killed their own tests
  • Live gate and argv door driven against the built binary with real bundles
  • Adversarial review by two reviewers, before the seal
  • CI green on all seven jobs across macOS, Linux and Windows on io-cli 0.34.0 #86

What the gates caught that a green suite did not

The live gate found the release's own worst defect in words rather than in a failure: the model reported the bundle's program was not on PATH, then ran it by walking the workspace. A check grepping for the program's output would have said PASS.

Both adversarial reviewers independently found that a bundle installed mid-session never reached PATH, while the install told the operator it was in force from the next turn.

CI found two more that no local run could see: a clippy lint that only fires on its newer toolchain, and a test fixture that spelled a unix PATH by hand and so failed on Windows for a reason unrelated to the property under test.

The record is sealed and pinned at sha256:39123dd9b09c9baa4553785bcd1ac031f6b0137d9a1f9f9f98143edf9394f868, ultraship validate exit 0 over 103 checks.

The pin forces no compile fix: io-harness 0.73.0 makes Skill non_exhaustive
and gives it a root field, and this crate constructs none, only reads fields.
Its real cost is a silent one, handled in the commits that follow.
A read_skill call is drawn as a loaded-skill row - the skill's displayed name
and what became of it - from io-harness's own decision sentence rather than
beside it, so the string that carried the separator is no longer drawn for
that tool at all. A failed read still says the read failed.

trim_result is repaired at the cause: a pending call keeps io-harness's wire
target beside the displayed one, and both the said set and the trailing-target
strip consult it. Translating the target in place is what stopped the
repetition being recognised, and plain() keeps _ while dropping :, so the two
sides differed by the separator itself.

An MCP tool cell reads Call github:create_issue. The prefix is stripped before
translating because the prefix itself ends with the separator, and the cell
that carries a measured duration now matches on the wire name - the only event
in the enum reporting how long a tool ran, lost silently otherwise.

A slash-invoked skill echoes what was typed while the prompt stays the
catalogue name. The echo is a field of the harness's own Started event, so it
is carried from the driver rather than translated on the way past.

And read_skill's target is no longer assumed to be a name: io-harness 0.73.0
announces a companion path in preference to it, and translating one draws a
file that does not exist.
io-harness 0.73.0 parses a [[bin]], refuses one from a scope that may not
contribute an executing thing, and offers it on an accessor it never calls
itself; its contract says outright that placement is the host's decision.

Appended and never prepended, so nothing a bundle ships can shadow a system
command for the tool calls io-harness makes on the model's behalf, whose
permission gate matches a binary name. No file is written on a stranger's
behalf, so resolution is by the file's own name and a declaration that renames
its program is reported at startup rather than staged.

The plugin pane names the program and its path. Consenting to the bare word
bin was free until this release put the program on PATH.
…ew gate

A bundle contributes seven kinds now, not six - six sentences said otherwise
across the guides, the capabilities index, the example configuration and two
source files.

The plugins guide gains a section on the executable a bundle ships: the
append-not-prepend reason, that no file is written, and the downgrade hazard -
a manifest carrying a [[bin]] is refused whole by an older io-cli reading the
same home, which drops every skill, agent, hook and layer in that bundle
rather than the one entry.

The skills guide gains read_skill's optional path, and the naming rule now
states the loaded-skill row and that the echoed line and the sent prompt
deliberately differ.
Re-read, not version-bumped. The two trees were diffed first: of the eleven
harness files cited, three changed, and four citations in adapt.rs shift by
the lines inserted above them - each confirmed by landing on the target rather
than by arithmetic. plugin.rs:311 still joins the plugin root, so the
absolute-path argument holds; plugin.rs:841 still refuses substitutions in
every scope and still takes the whole bundle.

Every claim re-checked against the code rather than assumed: the three step
and retry defaults, the five widening pairs, the three substitution forms, the
two MCP transports, and that Act::Exec still matches a binary name alone.

exec.rs also claimed 'Still true at 0.72.0'. That is a currency claim rather
than a citation, so no gate would have caught it and a bump would have left it
asserting currency for a version this crate no longer pins.
The placement went on the interactive session's startup, and io exec and
io resume return from main hundreds of lines before that - so neither headless
door placed anything. The two-entry-point asymmetry, again.

Found by the live gate and only by it: the model reported that io-cli-probe
was not on PATH, then found it by walking the workspace and ran it anyway. A
check grepping for the program's own output would have said PASS over a
placement that never happened. Four steps before the fix, two after.

install_for lives in exec.rs rather than the driver because tests link the
library and not main.rs, and f9_every_door_that_reaches_a_run_places_a_bundles
_programs counts the sites - a contains is satisfied forever by whichever door
was written first, which is exactly how this shipped.

Also gates F5: set_echo is called at the two sites that submit a skill under
its catalogue name, each beside the submission it belongs to.
Seven tests over what the transcript, the status line, the pickers and the
plugin, skill and marketplace panes actually draw.

The gate asserts an untranslated namespaced name - the literal
<bundle id><NAMESPACE><contribution> for an id the fixture declared - and not
the absence of the separator. A plugin id cannot contain __ (io-harness's
check_id), so the first occurrence is always the join and everything after it
is the contribution's own name: bundle__deep__nested is correctly drawn
bundle:deep__nested and keeps one. Under the blanket spelling, writing display
with replace instead of split_once reads as a fix, which is how 0.32.0 shipped
its path defect.

Every surface gets both halves - a contributed name translated, and a path or
a doubly-separated name surviving intact - and the wire spelling is built from
io_harness::NAMESPACE rather than from crate::naming, so the gate does not
measure the translation against itself.

AGENTS.md described the blanket rule and then contradicted it a sentence
later; it now states what the gate actually asserts.
clippy::items_after_test_module refuses a test module with production items
below it, and CI runs clippy with -D warnings.
Two reviewers split by area found the mid-session PATH defect independently,
which is the strongest corroboration available.

A bundle installed mid-session never reached PATH. refresh_holdings re-resolves
at the /plugin door and at every turn boundary and placed nothing, so
everything else a bundle contributes came into force at the next turn - the
install says so in those words - and its program did not, until the operator
restarted. appended()'s own doc justified its idempotence by a caller that did
not exist.

The echo outlived a turn that never started. io-harness validates the contract,
discovers skills, opens the run, takes the store lease and sets the provider
before it emits Started, and every one of those can fail - so Started's take()
was not on its own a guarantee. Cleared in flush, beside awaiting and thinking.

The echo dropped the leading slash. Command::Slash arrives stripped, so the one
row the reader wrote came back as an ordinary prompt containing a colon - and
the test hand-fed the slash it should have proved, so it could never see it.

mismatched reported every correctly-authored Windows bundle: review.exe answers
to review there, and the notice saying otherwise went to stderr on every io
exec. answers_to reads PATHEXT rather than assuming.

A switched-off bundle's programs and entries' sort and dedup were asserted by
nothing - chaining disabled(), or dropping the BTreeSet, left the suite green.
So was the whole executables disclosure block, on the surface an operator
consents from.

Also: a join_paths failure no longer wears the same value as nothing-to-add, a
failed bundle listing no longer reads listed read error, and the bin path is
shortened from the left so the file name survives.
…haviours

The changelog repeated the blanket rule ('fails if the separator appears') that
the gate is deliberately not written as - a path carries one legitimately, and
only the first separator is the join. Also records that a bundle installed
mid-session has its program placed at the next turn, and that a Windows bundle
shipping review.exe under the declared name review is correctly authored.
CI's clippy runs a newer toolchain than this machine's, and
clippy::question_mark fires there on skill_and_file's else-if chain. My local
clippy was the weaker gate, which this product has paid for before.

And the appended-not-prepended test spelled a unix PATH by hand, so Windows -
which splits on ';' - saw one entry whose name contained a colon and failed for
a reason unrelated to the order under test. The production code was right and
the fixture was not; it now joins with the platform's own separator.

Four of five real defects in this product's history were platform-specific and
invisible locally. This is the fifth and sixth.
@aakashpawar1999
aakashpawar1999 merged commit 3ad5f61 into main Aug 31, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant