Repository navigation
io-cli 0.30.0 - #74
Merged
Merged
Conversation
0.71.0 is the newest published harness and closes six issues io-cli filed: #218 (Effect::ALL, ExecMode::ALL, Effect::as_str), #219 (the named step and retry defaults), #220 (PriceTable::models), #221 (net::target made public and its fail-open shapes fixed), #223 (Plugin::hooks, Hook, Hooks::declarations) and #224 (Plugins::inspect). cargo check, the full suite (99 binaries, 1,567 passed, 0 failed, 36 ignored), clippy and fmt are all green on the new pin. The nightly rustdoc gate was not. src/preflight.rs linked crate::approval::refuses_git, which 0.29.0 deleted when io-harness#214 closed, so the link had been broken since that release. The comparison was stale as well as unresolvable: git's spawn no longer refuses an asking exec posture, while the MCP stdio spawn deliberately still does, and that divergence is the reason the preflight cannot be written by analogy to git. Rewritten to say so.
Task: US-IO-CLI-0.30.0-T01
Task: US-IO-CLI-0.30.0-T02
Task: US-IO-CLI-0.30.0-T03
Each of these argued a gap that the pin closed, and a paragraph arguing a fix is impossible next to the fix itself is how src/servers.rs spent a release telling operators an [[mcp]] enabled key could not exist. prices.rs: PriceTable::len now exists (io-harness#220). priced_in still counts one file's own text on purpose, because len answers about the merged table and Catalogue::too_short is comparing a candidate write against the rows it would replace in that same file. Swapping them would make a short write look safe whenever another scope happened to price enough models. settings.rs: the models field survives models()/len()/is_empty() for the same reason - it records what one read priced at the time it was written, which is the only thing a later refetch can be compared against. reviewer.rs: ProviderSpec no longer derives Debug; 0.71.0 replaced it with a hand-written impl that renders a set api_key as <redacted>. The refusal still does not format the spec, because not formatting a secret-bearing type is not a habit worth relaxing because one version of one dependency made it safe. contract.rs: the headless step cap is DEFAULT_WORKSPACE_MAX_STEPS, nameable now rather than a literal read out of a constructor.
Task: US-IO-CLI-0.30.0-T04
Task: US-IO-CLI-0.30.0-T05
Task: US-IO-CLI-0.30.0-T07
Task: US-IO-CLI-0.30.0-T08
Task: US-IO-CLI-0.30.0-T06
Task: US-IO-CLI-0.30.0-T09
Task: US-IO-CLI-0.30.0-T10
Thirteenth consecutive release in which this gate found real defects behind a fully green suite. Two reviewers split by area, with src/main.rs given its own pass because nothing under tests/ links it. HIGH - /skills add|list|remove and /mcp probe typed in a session did nothing at all. manage::plan answers None for both (a skill is a file, a probe writes nothing), so they fell to the empty Ok((_, None)) arm: no act, no output, no refusal, on lines the README documents as working. The acts existed only in manage_main, which a session never calls. Both now have session arms, and the probe uses the session's merged policy rather than the file's alone. HIGH - /memory's "replace it with what is in the prompt" could never carry anything but the note's own text. The picker owned the keyboard between composer.set and the choice, so the replacement was always byte-identical to the note; amend accepted it and the surface reported a successful replacement over a write that changed nothing. The order is now /remember's: type the line, then pick what it applies to, with the row saying so when the prompt is empty. HIGH - /plugin's toggle and install never reloaded the configuration, so a second /plugin in the same turn decided from stale state: the row went on offering "switch it off" for a bundle already off, wrote false over false, and said so again, with "switch it back on" unreachable until the next turn. /mcp's toggle has always reloaded, which is why that one was self-consistent. MEDIUM - io mcp probe exited 0 for every outcome, so a script could tell an answering server from a dead one only by parsing prose. exec::probe_code now answers OK, REFUSED or FAILED, verified against a real MCP server: answering 0, disabled 1, will-not-start 1, policy-refused 2. MEDIUM - skillview::install could not see skills/disabled/, so installing a file whose name a parked skill claimed left that one impossible to re-enable. It also skipped the collision check entirely when discovery errored, and counted nothing against MAX_SKILLS - so /skills add could push a home to 65 skills, after which discovery fails, /skills lists nothing, and it cannot be used to undo it. MEDIUM - /mcp enable|disable typed in a session never said what the key costs an older binary, while the picker toggle and the argv door both did. The [[mcp]] cost is the silent one. MEDIUM - /profile remove reported a profile gone when another scope still declared it, and cleared the session's overlay for a profile still applicable. LOW - pluginview::add_off and declared_off lost their last callers to Plugins::inspect and are deleted rather than left public and unreachable, which is the shape this product has shipped six times. /profile create now refuses a name another scope declares. /skill opens the panel like /skills. A declined unforget says the withdrawal stands. note_rows takes the glyph set. Checked and NOT changed: Pick::PluginInstall writing against session.root() was reported as a plan/write root mismatch; the plan is built from a local shadow of the same value and a picker owns the keyboard, so the session cannot change in between. Gates: 99 binaries, 1,607 passed, 0 failed, 36 ignored; clippy 0; fmt clean; nightly rustdoc 0 findings.
The five-shape table asserts against io_harness::net::target, so reinstating a copy in src/preflight.rs would have killed nothing. The property is absence, and absence has no call site to assert on, so the gate reads the module as text.
Four arms executed against the release's own criteria. One killed exactly what it should. The other three found that the gate was not a gate. F15's second arm had no site. The five-shape table asserts against io_harness::net::target, so reinstating a copy in src/preflight.rs would have killed nothing at all. The property is absence and absence has no call site, so the gate reads the module as text. Arm re-run: kills only its own test. F13's second arm had no site. McpProbe is #[non_exhaustive] and belongs to another crate, so no test can construct a variant this build does not know, and the catch-all is unreachable from every behavioural assertion - the pairwise test builds its expected string from servers::UNMODELLED by hand for that reason. Pointing the catch-all at a known state's sentence killed nothing. Now gated on the source. Arm re-run: kills only its own test. The new F1 session gate was vacuous. It asserted contains() on the install and remove call sites, which the headless door satisfies on its own - so deleting the session's arm, which is the entire defect the gate exists for, changed nothing. Counted now, not contains: two install sites and three remove sites. Arm re-run: reddens naming the count it found. F10's second arm (widen the KEYS check rather than adding one name) killed exactly the three unknown-key refusal tests and nothing else. Every run was --no-fail-fast and filtered for compile errors as well as failures; none of the four failed to compile.
CI caught this and my local clippy did not, because CI runs `cargo clippy --all-targets -- -D warnings` and I had been running it without the flag - so `empty_line_after_doc_comments` was a warning here and an error there. The local gate was weaker than the one that decides. Deleting `add_off` left its first doc line and a `///` behind, above the block comment that explains the deletion, so the doc attached itself to `enable` two items further down.
io-cli 0.30.0
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.
Release PR. Everything in this diff is reviewed in #73.
The rest of the verb matrix closes: after this release there is no thing io-cli manages that an operator has to open a file to change. A skill is installed and removed; an instruction note is edited and forgotten by line; a profile is created, removed and cleared; an MCP server and a plugin bundle are switched off without being removed; and a configured server is started on request to report whether it actually answers.
COMMANDS.len()stays 36 and the group occupancy stays[7, 9, 10, 10].Security — the preflight carried io-cli's own copy of io-harness's URL normaliser, which failed open on five shapes. Worst:
https://[::1]evil.com/xreported permitted,[::1]:443for a URL that connects toevil.com. io-harness 0.71.0 madenet::targetpublic and fixed all five; the copy is deleted. Affects 0.28.0 and 0.29.0.The pin moved
0.70→0.71.0, closing six issues this project filed. Each closure retired a copy io-cli had shipped around the gap, so a third of the release is deletion.Gates: 99 binaries / 1,609 passed / 0 failed / 36 ignored; live 36/36 in 242.92s against a real provider, run twice;
clippy -- -D warningsexit 0; fmt clean; nightly rustdoc exit 0;io mcp probeverified end to end against a real MCP server with all four exit codes checked. Adversarial review: 3 HIGH, 5 MEDIUM, 6 LOW. Four sabotage arms, three of which found the gate was not a gate.The UltraShip record is sealed and pinned (
sha256:547d1cfb…);.ultraship/is gitignored whole, so it lives on disk and never in this diff.