Cassette Build Report 023 — The Queue Row Was Smaller Than Its Contract
Cassette widened S07 after both agents deferred clauses that the acceptance packet already assigned to the storage step.

Scope note: This report covers the decision to widen S07 to the complete Q53 and Q62 packet, the independent fixtures that followed, and the limits of queue-row wording. It does not generalize one queue design to every project.
I had two agents tell me that some S07 clauses belonged later. The queue row was short. The acceptance packet was not.
S07 has two jobs. It must reserve the physical extent an operation will need, including its later peak, and it must detect and repair corruption in the objects that make a tensor readable. Q53 and Q62 already named the complete cases. The row named only some of them, so both agents followed the smaller artifact.
I initially went along with that. Then I asked the plain question: why would we not address the omitted clauses here? Nothing in the packet had moved them. The implementing agent changed its recommendation. The queue schedules the contract. It does not reduce it.
We widened the row to name every Q53 and Q62 case. Capacity now covers exact-boundary, fragmented, concurrent, growing-transform, training, and repair work. A reservation remains owned through cleanup. Concurrent claims share one extent decision instead of trusting two stale free-space readings. Training includes rollback and optimizer material, not just the weights that happen to be visible at the start.
The concurrency fixture makes the acceptance boundary concrete:
with ThreadPoolExecutor(max_workers=2) as executor:
concurrent = tuple(executor.map(compete, (1, 2)))
winners = [item for item in concurrent if isinstance(item, CapacityReservation)]
losers = [item for item in concurrent if isinstance(item, CassetteError)]
assert len(winners) == 1
assert len(losers) == 1
The case is in tests/test_s07_integrity_capacity.py. One reservation wins. One receives CAPACITY_EXCEEDED. After release, the pool returns to its original state, and a second release does not release it again.
The integrity side gained the same precision. Payload, index, manifest, root, and parity now move through an explicit state chain. A bad source is rejected before mutation. A missing page receives PAGE_UNAVAILABLE only after local, verified, and parity recovery have all failed. A repaired page must restore the logical root rather than merely becoming readable.
I also stopped the fixtures from agreeing with production by construction. Q53 now uses literal expected values for boundary, growth, and training cases. Q62 computes BLAKE3 and XOR in the test without calling the store’s helpers. Disposable mutations made the tests fail when I changed phase maximum to phase sum, made a corrupt manifest look valid, or counted two physical copies as one.
The agents had both found a real mismatch, but neither had made the final decision. Opus saw the missing cases and said “your call.” The implementer followed the queue row. My question restored the packet to the row. That is not extra scope. It is the assignment we had already written.
