Recluse Studio
Field note / Authored record
← Field notes

Cassette Build Report 045 — The Protocol Evidence Had to Reach the Code

S18's adapters rejected most hostile wires, but one raw TypeError and one unverified protocol table showed why upstream evidence must reach the implementation.

A monochrome pixel adapter translates one event card through a sealed wire junction while a small red loose wire stops before the gate.
Post-specific field image / square

Scope note: This report covers S18’s named-client adapters, protocol evidence, and remediation. It is about deterministic translation and provenance; it does not claim live client execution or app-server support that the step did not use.

S18 moved the attack surface from stored records to the wire. Cassette had one canonical contract and several named clients: Codex, Ollama, OpenClaw, Hermes, and a custom JSONL route. The question was whether translation could preserve exact capability without inventing a feature for a provider that did not have it.

Kimi K3 Max tested the public adapter entries with hostile shapes, credential headers, namespace collisions, event gaps, foreign run IDs, terminal events, and wrong operation identities. Most of the surface held. Eight guard removals failed. Reasoning sent to a non-EXACT surface was refused instead of approximated. Cookies were stripped before a canonical record. A sequence gap stayed a sequence gap.

One entry point escaped. to_wire_events(None) raised Python’s raw TypeError because the list comprehension iterated before the shape guard ran. A list containing invalid events reached a typed refusal; the bare None did not. The bug was small, but Q6 promised a Cassette error at the boundary, not whatever exception Python happened to produce first.

Opus 5 Max approached S18 from the evidence side. The closeout said the generated maps had been compared with four upstream commits. The hashes appeared in the generator and in a handwritten expected dictionary, but the fixture did not reach the network and the tracked research record cited branch tips rather than complete pinned files. Real hashes in two local copies were not independent provenance.

Opus also overstated the Codex app-server issue and measured one slow test run as a regression. Sol rejected both details after checking the conditional contract and the durations. The useful challenge remained: did the upstream record reach the code, and did the code still match the pinned protocol?

The first repair is plain:

def to_wire_events(self, events: list[dict], *, surface: str | None = None) -> dict:
    selected_name = surface or self._definition["default_surface"]
    selected = self._surface(selected_name)
    if not isinstance(events, list):
        raise _fail("INVALID_REQUEST", self.name, "canonical events must be an array")
    canonical = [_validated("run_event", event) for event in events]

The event-shape guard in adapters/__init__.py is implemented evidence for the repaired local boundary. It makes the bare non-list refusal typed before iteration. It does not prove the provider protocol outside Cassette.

Sol then fetched the exact upstream files at the pinned commits, recorded their paths and SHA-256 digests in research/S18_PROTOCOL_EVIDENCE.json, and compared the generated maps against that independent record. OpenClaw Gateway v4 did not match the old map. Its chat.send request required an inner idempotency key. Its Gateway agent field used the raw main identifier rather than the HTTP alias openclaw/main. Its event surface did not contain the larger reasoning, tool, usage, and full-error set the old adapter had emitted.

The repair made the map smaller. The adapter now sends the required inner key, derives the Gateway identifier from the explicit alias, and maps only the pinned event subset it can preserve exactly. Unsupported events return CAPABILITY_MISMATCH. A larger map would have been a larger falsehood.

The review process also produced two false instruments. Sol first used a zsh variable named path, which erased command lookup, then constructed raw URLs incorrectly. A mutation report later imported the accepted adapter instead of the disposable tree and called an encoder mutation survived. Both results looked clean until the execution route was checked.

S18 closed with 34 of 34 tests, a clean ledger, and the evidence record beside the generated map. Kimi found the raw exception. Opus asked whether the proof had a source. Sol corrected the code, the external record, and the harness. The adapter is now honest about the protocol it can translate, the evidence that supports it, and the events it must refuse.