Carry threshold evidence on issuance and use events
GH-DEC-2026-005 moved the distinct-approver check off the PEP onto this engine's valid_now. secrets-engine has implemented the split and reports it no longer verifies the threshold independently. Gate House accepted that as correct on layering AND as a genuine reduction in defence in depth, and named the compensating control: not a second check at the PEP, which is the duplication the split removes, but reconstructability at the issuer under §9.6. The emitted events could not support that. approval.issuance carried required_count but never who satisfied it, and approval.use carried no threshold evidence at all, so an auditor replaying the stream could not recompute the evaluation without reading live rows -- rows that may since have been superseded, revoked, or expired. Both events now carry a threshold object: required_count, distinct_approver_count, threshold_met, and approvers with approved_at plus assurance and evidence_ref when recorded. Tests prove reconstruction from the use row alone, and that the claim still discloses no approver identities -- they are evidence for audit-core, not consumer-facing, and the claim keeps disclosing the least it can. Writing the tests showed distinctness is already a storage invariant: entries is UNIQUE on (approval_id, subject_id), so a repeat approver is refused at insert and a separate entry_count could never differ from the distinct count. Dropped that field rather than ship a number that cannot vary, and the test now asserts the refusal instead. 88 tests pass (4 new). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TvyJPAaVCGsVheVhcCwNND Assistant: claude-code Assistant-Model: opus Assistant-Process: 411227@bnt-lap001 Assistant-Session: d566f6d3-bcaf-43c3-bc5e-3ddd0f64b535
This commit is contained in:
parent
48ffc34993
commit
87e55e2bca
4 changed files with 163 additions and 2 deletions
|
|
@ -98,3 +98,35 @@ recorded anyway.
|
|||
- Best-effort publish after commit with no row.
|
||||
- A second, non-local queue as the durability mechanism.
|
||||
- Deduping in this engine instead of relying on `event_id` at `audit-core`.
|
||||
|
||||
## Threshold evidence on `issuance` and `use`
|
||||
|
||||
`GH-DEC-2026-005` moved the distinct-approver check off the PEP: a consumer
|
||||
reads `valid_now` and trusts this engine's evaluation of everything folded into
|
||||
it. Gate House accepted that as correct on layering **and** as a genuine
|
||||
reduction in defence in depth, and named the compensating control — not a
|
||||
second check at the PEP, which is the duplication the split removes, but
|
||||
**reconstructability at the issuer** under §9.6. Detection, not prevention.
|
||||
|
||||
So `approval.issuance` and `approval.use` both carry a `threshold` object in
|
||||
`details`:
|
||||
|
||||
| Field | Meaning |
|
||||
| --- | --- |
|
||||
| `required_count` | the threshold in force on the object at that moment |
|
||||
| `distinct_approver_count` | distinct subjects who had recorded an entry |
|
||||
| `threshold_met` | whether the evaluation passed |
|
||||
| `approvers` | `subject_id`, `approved_at`, and `assurance` / `evidence_ref` when recorded |
|
||||
|
||||
An auditor holding only the `use` row can recompute the evaluation without
|
||||
reading live rows — which matters because those rows may since have been
|
||||
superseded, revoked, or expired.
|
||||
|
||||
**Identities are here and not on the claim.** The claim is consumer-facing and
|
||||
discloses the least it can; the outbox is the evidence path to audit-core,
|
||||
where the identities are the point. A consumer that wants the threshold reads
|
||||
`valid_now`.
|
||||
|
||||
Distinctness itself is a storage invariant rather than a recomputation:
|
||||
`entries` is UNIQUE on `(approval_id, subject_id)`, so a repeat approver is
|
||||
refused at insert.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue