Revision to Courier v0 (collection 8c09cc11-e3ce-4cd6-ab78-deb1b96e4a1c, courier.mjs), delivery loop only. I read the source but did not execute it; this is a proposed revision, not a verified run.
Original (the sink branch inside the loop):
if (mode !== 'sink' || !sinkKeys.has(key)) {
effects++; sinkKeys.add(key); trace.push('effect');
} else trace.push('sink-hit');
Gap: sinkKeys is a Set of keys, so a second delivery reusing the same key with a different payload is treated as an identical retry (sink-hit) and the conflict is hidden. The caller sees "already done," not "the work disagrees."
Revision — store the payload, split the three outcomes:
const receipts = new Set(), sink = new Map(); // was: sinkKeys = new Set()
...
for (const { key, payload } of deliveries) { // was: for (const key of ...)
if (receipts.has(key)) { trace.push('receipt-hit'); continue; }
if (mode === 'before') receipts.add(key);
if (!interrupted && crash === 'before-effect') { interrupted = true; trace.push('crash-before-effect'); continue; }
if (mode !== 'sink') { effects++; trace.push('effect'); }
else if (!sink.has(key)) { effects++; sink.set(key, payload); trace.push('effect'); }
else if (sink.get(key) === payload) { trace.push('sink-hit'); }
else { trace.push('conflict'); }
if (!interrupted && crash === 'after-effect') { interrupted = true; trace.push('crash-after-effect'); continue; }
receipts.add(key);
}
deliveries is now [{ key, payload }, ...] with literal JSON-string payloads; default keeps two identical entries so the old identical-retry case still dedups.
Expected outcomes (by inspection, not a run): identical retry -> sink-hit, effects stays 1; same key + different payload -> trace contains conflict, effects stays 1 (first effect preserved, second not applied); the three crash cases keep their v0 counts because the mode !== 'sink' path is untouched.
Tradeoff/limits: exact string equality on payload, so any reordering or whitespace difference reads as a conflict — canonicalization is still deferred. A conflict is reported, not rolled back or resolved; the first effect stands and the caller must decide. This still assumes atomic sink access, sequential deliveries, and durable storage; it does not establish general exactly-once delivery.