Skip to content

Contract silence: name the two ways an I/O contract reads as satisfied when it is not - #413

Merged
m2ux merged 1 commit into
workflowsfrom
workflow/canon-io-contract-entries
Aug 3, 2026
Merged

Contract silence: name the two ways an I/O contract reads as satisfied when it is not#413
m2ux merged 1 commit into
workflowsfrom
workflow/canon-io-contract-entries

Conversation

@m2ux

@m2ux m2ux commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Summary

A technique can declare an input and never spend it. A Protocol step can apply another operation and pass only some of what that operation declares. In both cases the contract reads as satisfied — every slot is declared, every call is made — and a reader has no way to tell that a value is going nowhere or arriving from nowhere. Neither shape had a Detect anywhere in the canon. This adds one entry for each, and extends an existing entry so that rationale sitting inside a rule has a home.

How the gap was found

A small change to the meta engine — five files, one new operation — was audited three times against the canon. The first pass found ten defects, the second five, the third three. The third pass's three had all been present since the first commit, and the first pass had walked those files whole and missed them.

The reason was not reading depth. Two of the three were the same shape: a new operation declared session_index and activity_id and referenced neither, and the operation it applied declared session_index as an input the Apply site never passed — so the resumed worker's prompt would have carried no session index to authenticate with. Nothing could have caught either. The walk follows the catalogue entry by entry, and the catalogue has no entry for it.

What already exists, and what it does not reach

output-without-destination covers the output side of exactly this concern: a declared output with no reader. There is no input twin. bind-protocol-locals covers a dead {$local}, not a dead declared input. And the mechanical net stops short too — binding-fidelity proves that step bindings resolve and their arguments conform, but its subject is activity steps[] binds; a Protocol Apply is not a bind site, so every such call is unchecked.

Separately, no-rationale-in-description lists the surfaces its Detect reaches — description, message, option and action descriptions, procedure bullets — and technique ## Rules is not among them. So a rule that justifies its own prohibition has no home either, which is how a rule reading "orchestrators never call this tool; the definition is the worker's domain" survives an audit: the second clause is rationale, and it couples the rule to a contract it does not own.

The additions

declared-input-never-read — an input declared on a technique that its own Protocol and Rules never reference. Detect searches for the braced designator, the dotted field form, and the bare name inside a tool-call signature a phase passes, because that last form is how several engine operations legitimately reference an argument. Carve-outs keep it off container inputs the descendants reference after the merge, and off an input handed wholesale to an applied operation as a substitution map.

apply-omits-declared-input — a Protocol Apply that omits a required input of the operation it applies. Detect resolves the target's Inputs through the loader, including container merges, and flags a required slot the Apply site neither passes nor covers by a declared default, and that no same-name slot on the applying technique makes ambient. Carve-outs keep it off optional inputs, defaulted inputs, same-name ambient binds, and activity step binds, whose conformance is a bind-site concern.

no-rationale-in-description gains two surfaces — technique ## Rules and rules.* strings, with a test for the rule case: delete the clause, and if the constraint still says what is constrained, the clause was rationale. The Do-not-flag preserves what no-rule-protocol-restatement already protects, so a prohibition that cites the home it forbids bypassing keeps its pointer.

Scope of change

One file: workflow-design/resources/anti-patterns.md. Two appended entries and one amended Detect triad. No other workflow, technique, or activity is touched.

Acceptance criteria

  • Each new entry follows the catalogue's Creation Rules: two-line intro, Detect / Do not flag / Fix on their own blocks, a structural test that transfers to a workflow that never saw the originating defect.
  • Neither entry re-teaches a sibling's Detect body; both cross-reference by name.
  • The amendment extends an existing Detect surface rather than minting a near-duplicate entry.
  • All 21 repository guards pass, including resource-anchors and source-encoding.

Non-goals

  • The mechanical net. Both new entries are decidable from technique markdown alone and belong in the guard suite beside binding-fidelity, but run corpus-wide they will report well beyond any one change, so they want per-finding triage rather than a zero gate from day one. That is a separate change against the server.
  • Fixing what the entries find. This adds the criteria; the corpus sweep is its own work.

Investigation detail

The audit that surfaced the gap, and the change it was auditing: #410 and #411.

🤖 Generated with Claude Code

A technique can declare an input no phase spends, and a Protocol Apply can omit
an input the applied operation declares. Both leave a contract that reads as
satisfied and is not, and neither had a Detect anywhere in the canon:
output-without-destination covers the output side, bind-protocol-locals covers
dead locals, and binding-fidelity proves argument conformance for activity step
binds rather than for Protocol Apply sites.

declared-input-never-read and apply-omits-declared-input name the two, each
carrying the carve-outs that keep them from firing on container-merged inputs,
pass-through substitution maps, and same-name ambient binds.

no-rationale-in-description gains technique Rules and rules.* on its Detect
surface, so rationale inside a rule has a home — including a constraint
justified by naming another actor's remit, which couples the rule to a contract
it does not own. The carve-out preserves what no-rule-protocol-restatement
already protects: a prohibition citing the home it forbids bypassing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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