NK-WP-0035-T04: readback records audit-core's independent_reader basis

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 308701@bnt-lap001
Assistant-Session: 5548e447-444c-4dcc-bed1-05c16eebfe56
This commit is contained in:
tegwick 2026-10-28 09:50:58 +01:00
parent 53f8327cdb
commit 81422e3d66
3 changed files with 27 additions and 5 deletions

View file

@ -58,9 +58,13 @@ def readback(request, captured, get):
findings = []
if status != 200:
findings.append(f'reconciliation returned {status}: {body.get("error", "")}')
receiver = {}
receiver, basis = {}, None
else:
receiver = {row['class']: row['count'] for row in body.get('counts', [])}
# audit-core marks cross-source answers so they cannot pass for the source's own check.
basis = body.get('basis')
if basis != 'independent_reader':
findings.append(f'reconciliation basis is {basis!r}, not independent_reader')
for cls in sorted(set(receiver) | set(captured)):
if receiver.get(cls, 0) != captured.get(cls, 0):
findings.append(f'class {cls}: captured {captured.get(cls, 0)}, receiver {receiver.get(cls, 0)}')
@ -73,7 +77,7 @@ def readback(request, captured, get):
elif (body.get('source'), body.get('tenant')) not in ((request['source'], request['tenant']), (None, None)):
findings.append(f'event {event_id[:12]}… source/tenant differs')
return {'schema': SCHEMA, 'request': request, 'captured_counts': dict(captured),
'receiver_counts': receiver, 'event_presence': presence,
'receiver_counts': receiver, 'receiver_basis': basis, 'event_presence': presence,
'verdict': 'agree' if not findings else 'diverge', 'findings': findings,
'payloads_retained': False, 'token_retained': False,
'means': 'agreement proves neither completeness nor King\'s Guard acceptance'}

View file

@ -14,7 +14,8 @@ spec.loader.exec_module(rb)
CAPTURE = Path(__file__).parents[3] / 'docs/evidence/2026-10-15-cadence-adoption/hourly-observation-capture'
def fake_receiver(counts, present, source='qonto-assistant', tenant='tenant:friendly:binky', recon_status=200):
def fake_receiver(counts, present, source='qonto-assistant', tenant='tenant:friendly:binky', recon_status=200,
basis='independent_reader'):
calls = []
def get(path):
@ -22,7 +23,7 @@ def fake_receiver(counts, present, source='qonto-assistant', tenant='tenant:frie
if path.startswith('/v1/reconciliation?'):
if recon_status != 200:
return recon_status, {'error': 'source_not_allowed'}
return 200, {'counts': [{'class': k, 'count': v} for k, v in counts.items()]}
return 200, {'counts': [{'class': k, 'count': v} for k, v in counts.items()], 'basis': basis}
event_id = path.rsplit('/', 1)[1]
if event_id in present:
return 200, {'source': source, 'tenant': tenant, 'id': event_id}
@ -90,3 +91,12 @@ def test_receipt_never_contains_token(tmp_path, monkeypatch):
assert rb.main(['--capture-dir', str(CAPTURE), '--token-file', str(token), '--output', str(out)]) == 0
assert 'SENTINEL' not in out.read_text()
assert json.loads(out.read_text())['verdict'] == 'agree'
def test_missing_independent_basis_diverges():
request, captured = rb.load_capture(CAPTURE)
get, _ = fake_receiver(captured, set(request['event_ids']), basis=None)
receipt = rb.readback(request, captured, get)
assert receipt['verdict'] == 'diverge'
assert receipt['receiver_basis'] is None
assert any('basis' in f for f in receipt['findings'])

View file

@ -90,7 +90,7 @@ Python compilation and `git diff --check` pass.
id: NK-WP-0035-T04
status: wait
priority: high
blocking_reason: "Elapsed-hour capture done 2026-10-15 (3 events, receiver readback captured). Readback run 2026-10-23: presence 3/3, counts refused 403 source_not_allowed (operator reader scoped to user-engine); waiting on audit-core's cross-source count path and a rerun, then King's Guard acceptance (KG-WP-0005-T03, wait) and QONTO-WP-0006-T03."
blocking_reason: "Elapsed-hour capture done 2026-10-15 (3 events, receiver readback captured). Readback run 2026-10-23: presence 3/3, counts refused 403 source_not_allowed (operator reader scoped to user-engine); audit-core cross-source count path implemented (d46e89e); waiting on the founder's audit-core release (AUDIT-WP-0013-T07) and the rerun, then King's Guard acceptance (KG-WP-0005-T03, wait) and QONTO-WP-0006-T03."
state_hub_task_id: "e3fbc8b8-00d5-57ce-ba0c-19b58044c950"
```
@ -455,6 +455,14 @@ counts for a full-scope may_read reader, or name the sanctioned independent coun
Rerun the same helper once that is answered. The receipt still goes to King's Guard as
presence-only evidence. T04 stays wait.
2026-10-28: audit-core answered (audit-core `d46e89e`, AUDIT-WP-0013-T06 done): a full-scope
`may_read` reader (`tenants: ["*"]`) may now count another source's window on
`/v1/reconciliation`; cross-source answers carry `basis: "independent_reader"`. The helper
records `receiver_basis` and treats a 200 without that basis as `diverge`. Next: the founder
releases the audit-core image (AUDIT-WP-0013-T07: `make image-publish`, pin digest, dry run,
apply, rollout), checks `GET /v1/stats` returns 200 under the operator reader token, then reruns
the same readback. An `agree` receipt goes to King's Guard (KG-WP-0005-T03) and qonto-assistant.
## Automate staged receiver derivation without sibling mutations
```task