Eigenstate
Notes / Ansatz

My tool’s instructions hid a feature it already had

I removed the duplicate operation lists. The remaining one still needed something more reliable than another list to check it against.

Revised Jason Shen

The instructions for my MIDI editor told an AI agent not to use a feature I had already shipped.

Battuta is a command-line tool I use to work on music with an agent. The agent can inspect a MIDI file and write requests to change notes or instruments; I listen to judge the result. The tool already supported changing the instrument assigned to a MIDI channel. But its agent instructions still said those edits would fail to parse. Following that advice would leave a supported operation unused.

01 / The disagreement

The tool supports it. The instructions say not to use it.

Changing the instrument assigned to a MIDI channel

Implemented operation

set_program

The tool supports this edit.

Agent instructions

Do not write this edit.

The instructions said it would fail to parse.

The four accounts of the tool
The recorded disagreement, with the instructions paraphrased. The counts describe the wider mismatch across the repository.

I had updated the code without keeping its descriptions in agreement. The README, command help and agent instructions each had a reasonable job: introduce the project, explain a command, or tell an agent how to work with it. But each also maintained an operation list. Adding a feature could leave several different answers behind.

One remaining list can still go stale

The first repair brought those descriptions into agreement. A later pass removed the exhaustive lists outside mid apply --help and directed readers there. The README could still introduce the tool; it no longer had to enumerate its operations.

That reduced the maintenance work, but the help was still written by hand. The next code change could leave it behind again. A test could compare the help with the operations the tool accepted, except that writing an expected list into the test would create another copy to maintain.

Asking the parser what it accepts

The parser already knew the answer. Every edit request names an operation in a field called kind. Give it an unknown name and its error lists the names it would have accepted. The test deliberately submits an empty name:

{ "edits": [ { "kind": "" } ] }

The request is supposed to fail. The useful part is the rejection: the accepted names come from the same type definition that the parser uses, so adding an operation changes the test’s expectation without a second list being updated by hand. The test reads these names from the error text, which means it also depends on that text remaining extractable. The suite checks that the extraction produces a plausible list.

The test then runs the built command’s apply --help and compares what it says with those names. It looks for both missing operations and names the help offers that the parser does not recognise. An omission can keep a caller from discovering a feature; an invented name sends them toward a request that will be rejected.

02 / The comparison

Two small changes expose both kinds of drift.

A real operation disappears from the help

Remove resize_note from the help.

Help

Not mentioned

Parser

resize_note

The check finds an accepted name missing from the help.

The help offers an operation that does not exist

Replace delete_note with make_sadder in the help.

Help

make_sadder

Parser

Not accepted

The check finds an example naming an unknown operation.

Illustrations of the reported help mutations, showing only the name being compared. The second case highlights the invented name; that edit also removes delete_note from its example.

The implementation record reports deliberately breaking the help both ways. Removing resize_note was caught. Replacing delete_note with make_sadder was caught too. These experiments checked that the test would object when the help changed, even though the underlying editing code had not.

I kept the automated claim narrow: the help should name what the parser accepts. An operation can keep the same name while its parameters change, and a correctly spelled name can accompany a misleading explanation. A documentation review still needs to follow the instructions far enough to judge whether they teach the caller how to make the intended edit.

Sources and verification scope

The documentation correction records the ten / seven / six disagreement and the instructions that discouraged supported edits. The consolidation record removes the remaining exhaustive inventories outside the command help.

The contract tests at the inspected revision provide the code excerpt and the scope of the comparisons. The JSON request in the article is deliberately invalid. The code below reformats the expression used to obtain its rejection; it is an excerpt, not a standalone program.

The implementation record reports the two deliberate help mutations and identifies the release containing the documentation disagreement as 0.1.1. Those historical experiments were not rerun for this note. The figures explain the recorded state and test mechanism; they are not runtime screenshots.

The error text is obtained here; the rest of the helper reads the names following expected one of.

let refusal = serde_json::from_str::<battuta::EditSet>(
    r#"{ "edits": [ { "kind": "" } ] }"#,
)
.expect_err("an empty kind is not a kind")
.to_string();

The name checks have a narrower scope than full documentation validation. One searches the help text for accepted names; the other reads names from JSON example lines. The suite also checks that its extraction from the error text is plausible. A separate fixture supplies a parseable example per operation. None of these checks alone establishes the musical result of an edit.