Skip to content

io-cli 0.19.0 - #51

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

io-cli 0.19.0#51
aakashpawar1999 merged 12 commits into
mainfrom
develop

Conversation

@aakashpawar1999

Copy link
Copy Markdown
Contributor

Release PR for 0.19.0 — the skills io ships.

Five SKILL.md files written into ~/.io-cli/skills and offered to the model through io-harness's own skill machinery: io-permissions, io-mcp, io-provider, io-remember, io-update. Ask for a thing io can do in your own words and it happens, ending in a write you approve. /skills lists every skill with what it is for, whose it is, whether it is on and the file it lives in; turning one off moves its file into skills/disabled/. /mcp and /provider move to the configure group.

Gates

  • 8/8 on PR io-cli 0.19.0 — ask for a thing io can do, in your own words #50, all three platforms, after one Windows-only round.
  • 76 test binaries, 1,023 passing, 0 failing, 24 ignored (0.18.0: 74/989). Clippy clean under -D warnings.
  • ultraship validate exit 0 over 57 checks.
  • Seven sabotage arms, each failing exactly the criterion it was written for.
  • Live verification against a real endpoint, including the model calling read_skill off a plain-language ask and ending in a gated config write.
  • io-harness stays on 0.69 — the newest published.

What was found and fixed before this PR

An adversarial diff review between "gates green" and the merge found four real defects behind 1,022 passing tests, all of the second-use shape. The critical one: the destination guard on a skill file move was by file name while a skill is addressed by its resolved name, so two of the operator's own skills could end every turn of a session at run start, from /skills alone. All four fixed with controls. US-IO-CLI-0.19.0-I01.

The Windows round found a defect in a test, not the product: the frontmatter assertion took only ---\n where io-harness reads ---\r\n too. skills/*.md is now pinned to LF so one release ships one artifact.

The comment said alert #1 was dismissed as not-reachable. It was not: it closed
as `fixed` at 2026-08-26T14:57:27Z, when the merge put `lru 0.18.2` in
Cargo.lock, before any dismissal was issued. The plan to dismiss was overtaken by
the dependency change that answered it.

The unreachability evidence stays, because it is why this was never urgent and
why the removal could be planned rather than rushed. What keeps the tree clean
from here is the lockfile gate, not that judgement.

0.18.0's sealed record carries the same wording. It is immutable and pinned and
the merge to main has happened, so that correction is stated in the release
report rather than amended in.
docs: the lru alert closed as fixed, not dismissed
…d /skills

Both add, edit, disable and remove entries in the configuration file, which is
what Group::Configure means, and neither is an inspection — Group::Inspect's own
doc says none of it changes what a turn does. The move is a correction, and the
room it leaves is what /skills takes.

Session 5, Turn 6, Inspect 9, Configure 6 — twenty-six commands, no group above
ten.
The five bodies ride include_str! and are written into <home>/skills on start.
Three guards, and each exists because Skills::discover fails a whole run rather
than degrading:

- a name the operator's own skills already resolve to is never written over,
  because two skills answering to one name is Error::Config propagated at run
  start and every turn of that session dies;
- the 64-skill ceiling is counted before the write, because discover rejects the
  whole set rather than trimming it;
- refresh is decided against a manifest io-cli writes, never against this
  release's own text, so a skill unchanged between two releases does not read as
  edited and stop being refreshed forever.

The manifest is FNV-1a written here: no dependency is available for it, and no
clock is either. It lives in the home rather than in skills/, because every .md
in that directory is offered to the model.
The install runs immediately after the home is adopted and long before any
contract is built: a skill written after the contract is one the first session of
a new install cannot be offered. It is gated on adopt having adopted, because
creating ~/.io-cli/skills for an operator who chose their own config home would
silently attach a skills directory to a run they pointed elsewhere.

/skills had a row in the table, a row in the palette and no arm in parse, so it
resolved to the help listing — advertised and inert. Nothing asserted otherwise,
because every gate here derives from COMMANDS and a row satisfies those by
existing. The new gate states the property over the whole inventory instead:
exactly one listed command may resolve to the help listing, and it is /help.
Origin comes from the manifest and never from the io- prefix, so a file the
operator wrote called io-thing.md reads as theirs and an edited shipped skill
reads as theirs too — which is what install will treat it as. The enabled set is
read through the harness's own discover, so the list is what the model is
offered; the disabled set is read here, because a directory holding no SKILL.md
is invisible to that call, which is the whole mechanism.

A rename and never a copy: a copy leaves one name resolving in both directories,
which is the session-killer this release exists to avoid. No EXDEV fallback
either — skills/ and skills/disabled/ cannot straddle filesystems, and the
fallback would be the copy by another route.
The command table is rebuilt from GROUPS rather than edited, which also clears
drift nobody could see: /steer and /compact had been listed under inspect since
0.17.0 while the code filed them under Turn, and the gate checks presence and row
count but never which heading a row sits under.

Also fixes a claim this release makes false. 'io-cli parses no skill file and
keeps no copy of one' was true until it embedded five bodies; the true half
stays.
Both refused src/skills.rs for explaining itself. The module says it writes a
plain-text manifest rather than TOML, and that it decides freshness by bytes
rather than by a clock — and saying either means naming the call it is
deliberately not making.

dependencies.rs already stripped comments for src/edit.rs, for exactly this
reason, and timing.rs already built its own needles with format! so that it would
not match itself. Each had solved the problem for one file and not for the files
it scans. Line-oriented, so a // inside a string literal still counts as code:
the stripping can only make either gate stricter.
…suite

Every one of them is the shape that pass is briefed to hunt: the tests exercise
each feature once, and the defect appears the second time.

A session-killer first. relocate guards its destination by FILE NAME while a
skill is addressed by its RESOLVED name, so a file moved back out of disabled/
could land beside a different file already answering to its name — and two
skills of one name is an Err from Skills::discover that io-harness propagates at
run start, killing every turn before the first completion. /skills could not
report it either, because its list comes from the call that just failed. Two of
the operator's own files are enough. enable now asks the question the run asks.

The same asymmetry one door earlier: install skipped a disabled skill by testing
disabled/<name>.md, which misses an operator file whose frontmatter claims the
name.

A deleted skill came back on the next start, while the release record and the
README both told the operator that rm was how to be rid of one. The manifest
already knew io-cli had written that name; now it is asked.

The / palette's skill list is walked once at startup. Nothing changed that
directory from inside a session until this release, so it now re-walks after a
toggle rather than offering a skill the model's catalogue no longer has.

And /skills read the home rather than the directory contract::skills_dir
resolves, so with [run] skills configured it would have listed five files
nothing reads and hidden the ones in force.

Three controls added, each verified to fail alone under its own sabotage.
…tes are LF

Windows-only red, and the defect was in the test. io-harness's
split_front_matter strips `---\n` and `---\r\n` alike and trims \r off every
key, so a CRLF checkout reads exactly the same; the assertion took only the
first form and was therefore stricter than the thing it protects. Green on two
platforms and red on the third, which is the only place it could be seen.

Separately, .gitattributes pins skills/*.md to LF. Those five files are
include_str!'d into the binary and written to disk verbatim, so their bytes are
a product artifact: without it one release ships two different sets of them and
the manifest digest of a file io wrote depends on the host it was built on.

Neither change stands in for the other. Remove the attribute and the assertion
still passes, because the skill still works.
io-cli 0.19.0 — ask for a thing io can do, in your own words
@aakashpawar1999
aakashpawar1999 merged commit 13abfd9 into main Aug 26, 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