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:
parent
53f8327cdb
commit
81422e3d66
3 changed files with 27 additions and 5 deletions
|
|
@ -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'}
|
||||
|
|
|
|||
|
|
@ -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'])
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue