Skip to content

io-cli 0.30.2 - #78

Merged
aakashpawar1999 merged 13 commits into
mainfrom
develop
Aug 30, 2026
Merged

aakashpawar1999 merged 13 commits into
mainfrom
develop

Conversation

@aakashpawar1999

Copy link
Copy Markdown
Contributor

Release PR for 0.30.2.

A documentation release: thirty-one claims across the README, changelog, contributing and security policies, configuration reference and shipped skills did not survive being checked against the code that decides them. Two were live code defects.

Security: SECURITY.md forbade opening a public issue and then gave a literal <project-contact-email> as the alternative — no way to report a vulnerability at all, since 0.1.0. Both it and CODE_OF_CONDUCT.md now route through GitHub's private vulnerability reporting.

Fixed: io skill <bad-verb> answered a sentence that denied and asserted the same fact; the bundle example used a tilde where none is expanded, so a copied path yielded a silently dropped bundle.

Changed: the 2,847-line README is now a front page plus nineteen guide pages under docs/guide/, with docs/CONTRACT.md, docs/CAPABILITIES.md, AGENTS.md, docs/STYLE.md and docs/RELEASE_PROCESS.md added. The split moved byte ranges rather than rewriting — 2,695 moved, 175 kept, 2,870 accounted for.

Added: nine gates, including link/orphan integrity and a contract page compared against exec's constants, clap's routing table and CliSettings' fields.

Gates on the feature PR (#77): 8/8 green across macOS, Linux, Windows and MSRV. Suite 98 binaries / 1,619 passed / 0 failed / 36 ignored; doctest 1 passed; clippy -D warnings and fmt exit 0. A sabotage arm was executed for every functional criterion.

git diff --quiet feat/0.30.2 develop is clean, so this is the tree the gates ran on.

AGENTS.md carries the commands CI actually runs, the architecture map, the
three exact-path rules tests/dependencies.rs enforces, and the tests that gate
prose. CLAUDE.md points at it rather than restating it, so the conventions have
one place to be wrong in rather than two.
STYLE.md extends io-harness's register with the surface a library does not
have: text the binary prints at a person mid-task. RELEASE_PROCESS.md records
why the order is an order — the seal before the merge, the tree check before
the release PR, the tag alone, and a smoke that exercises the release's own
surface rather than the version string.
A library's contract is its API; a tool's is the argv it accepts, the codes it
exits with, the keys it reads and the paths it writes. Enumerates all four,
plus the limits that hold today — including that IO_CONFIG_HOME does not move
the skills directory, which 0.30.1 found and nothing had written down.
The unknown-verb arm listed mcp, plugin and config, so `io skill bogus` fell
through to the unknown-surface arm and answered "`skill` is not a surface io
manages; they are `mcp`, `plugin`, `skill` and `config`" — a sentence that
denies and asserts the same fact and never names the verbs. The bare-surface arm
beside it already listed all four, which is what makes it an oversight.

Nothing in the suite read a refusal's words for this family; the gate does now,
per surface rather than over a joined string.
SECURITY.md forbade opening a public issue and then named a literal
<project-contact-email>, and CODE_OF_CONDUCT.md carried the same token, so from
0.1.0 to 0.30.1 this project told people not to open an issue and gave them
nowhere else to go. The README routes every report at SECURITY.md, so the dead
end was the only documented path.

Both now point at GitHub's private reporting on this repository. Two gates: one
that no shipped markdown leaves a contact placeholder, one that the replacement
route is really named — the first alone is satisfied by deleting the section.
Written before the guide pages exist, so the orphan half is RED until T07 lands
the split. That is deliberate: its non-empty assertion is load-bearing, because
with no pages on disk both loops iterate nothing and the test would pass while
proving nothing — the vacuous-gate shape this repository has shipped four times.

The link half is green now, which is what proves the checker itself works.

A link check is a few lines of std; adding a markdown crate to a tree held at
ten direct dependencies to find a missing file would be the expensive answer to
the cheap question.
…s not a trap

The plugin path example used a tilde, and no tilde is expanded for a
[[plugin]] path — io-harness resolves env:, file: and cmd: and nothing else, so
the bundle names a directory literally called ~ and is dropped: recorded by the
loader, listed by /plugin, otherwise silent. The same file forbids tildes 130
lines earlier.

Also adds [toolchain], three more [run] keys with the remaining five named in
prose, and [[mcp]]'s url, headers and timeout_secs; corrects the PriceTable
reason (0.71.0 gave it models/len/is_empty — the conclusion survives, the stated
reason did not) and the policy-layering mechanism, which is strictest-effect
first across all layers rather than layer order.

The gate requires a line an operator could uncomment rather than contains(),
because run, test, build and format are English words appearing throughout the
prose.
The largest: the home is one directory only until you name a location of your
own — io.toml, the store and IO.md follow IO_CONFIG_HOME while the skills
directory, marketplaces and the session lock do not; /mcp does offer a disable,
because McpServer gained enabled in io-harness 0.70.0 and this crate already
writes it; two modules start a process, not one; a bundle contributes to six
subsystems, not four; exit 6 has two routes since the 0.71.0 pin, not one; and
io skill reached the binary in 0.30.1, not 0.30.0.

Counts nothing checks are deleted rather than corrected — the undrawn event
kinds were wrong in the README and in exec.rs's own doc comment, in two
different ways.

The CHANGELOG had 33 version headings and 3 link definitions, so 30 rendered as
literal text while the header claimed Keep a Changelog conformance. That half is
mechanical, so it is now asserted rather than reviewed.
… that ships

/contain is in Group::Session and was documented under "this turn", so the
printed table showed eleven rows against a bound of ten the same page states.
The existing gate checked every name, every description and the total, and all
three were true — a row in the wrong table is still a row. The new gate joins a
command to its heading through Group::title(), the string the palette draws.

The install section still described 0.29.0: write enabled = false, re-discover,
disclose off the result. 0.30.0 replaced that with Plugins::inspect, so a reader
was told their configuration is written to before they consent, when declining
now writes no byte at all. Both halves are asserted — the prose alone would pass
if the feature were ripped out, the code alone would pass while the prose stayed
wrong.
The README was a 2,847-line manual with no contents list. It is now a front page
— logo, screenshot, what you get, install, first run, a guides table — and
nineteen pages under docs/guide/ carrying the depth, indexed a second time by
docs/CAPABILITIES.md so neither index can be the only way in.

The split was done by moving byte ranges rather than by rewriting, and the line
accounting was checked: 2,695 moved, 175 kept, 2,870 total. No paragraph was
reworded on the way.

Fifteen gates in tests/docs.rs read a README that no longer carries their claims,
and every one of them went RED rather than quiet, which is the outcome the
release was designed around. Each now names the page that carries its claim. The
two NEGATIVE sweeps are the ones that could have gone vacuous — !contains is
satisfied by any file that never mentioned the subject — so they read every
present-tense page instead of one file.

CHANGELOG.md is excluded from those sweeps by name. A changelog is a diary: its
0.16.0 entry records a limitation that was true when written, and sweeping it for
present-tense truth would force history to be rewritten whenever the product
moved.
… copy

A contract page is the one document a script author depends on, so a wrong exit
code or a missing key there is worse than no page. All three halves ask the code:
exit codes from exec's constants, subcommands from clap itself, and the
[app.io-cli] keys read out of CliSettings' own source — so a field added without
a row fails rather than arriving undocumented.

The spelled key count is asserted too, because a number in prose is exactly what
goes stale: this release found the README claiming thirty-nine undrawn event
kinds when there were fifteen.
Bumps the crate version, writes the release entry, and exempts the changelog
from the placeholder sweep. That exemption earned itself: this release's own
entry has to quote <project-contact-email> in order to say it was removed, and
the gate failed on that sentence. A gate that reads prose can forbid a file from
explaining itself — the third time this repository has hit that shape — and the
changelog is the one file whose job is to describe what used to be there.
@aakashpawar1999
aakashpawar1999 merged commit a6ee33f into main Aug 30, 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