NK-WP-0056-T15: receipt schema v0.1, validator and executor fixtures; audit sender exercise
- canon/schemas/execution-attribution-receipt_v0.1.schema.json: the bounded-executor profile of the PCC v0.2 receipt, both audit-core events (started, receipt), every object closed. - tools/execution-attribution-receipt: EAR-1..EAR-10 validator, fixtures generated from the real executor (completed, reverted, completed-on-retry) with a drift check. - executor: started carries receipt_version, digests, steps_planned and data.window.not_after (audit-core AUDIT-WP-0018-T03 request); receipt carries receipt_version and initiator.iss (configured approver_issuer, else unknown). - bounded-executor/exercise_audit_sender.py: real executor and client against audit-core's real ingestion and the Git scope overlay: 202, duplicate 200, conflict 409 as a defect, wrong source/tenant/token refused and not stored, a receipt-less start flagged by its own window. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 523928@bnt-lap001 Assistant-Session: 6c737afb-9c14-4152-9f02-477214b9c350
This commit is contained in:
parent
1d4b7e84d7
commit
d7133e3a04
15 changed files with 1506 additions and 7 deletions
|
|
@ -43,6 +43,7 @@ _spec.loader.exec_module(bea)
|
|||
ACTION = "bounded-execution"
|
||||
STARTED = "execution.run.started"
|
||||
RECEIPT = "execution.attribution.receipt"
|
||||
RECEIPT_VERSION = "0.1" # canon/schemas/execution-attribution-receipt_v0.1.schema.json
|
||||
|
||||
|
||||
class Refused(Exception):
|
||||
|
|
@ -81,6 +82,7 @@ class Config:
|
|||
approver_subjects: dict[str, str] # admission approver name -> carrier subject_id (no guessing)
|
||||
source_root: pathlib.Path = REPO
|
||||
clock_source: str = "system-utc"
|
||||
approver_issuer: str = "unknown" # the approvers' token issuer; recorded as `unknown` unless configured
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
|
|
@ -320,8 +322,9 @@ class Run:
|
|||
def _receipt(self, outcome: str, attempts: int) -> dict:
|
||||
end = self.d.wall()
|
||||
return self._event(RECEIPT, occurred_at=end, body={
|
||||
"receipt_version": RECEIPT_VERSION,
|
||||
"run_id": self.run_id,
|
||||
"initiator": {"sub": self.approver_subject, "role": "approver"},
|
||||
"initiator": {"iss": self.cfg.approver_issuer, "sub": self.approver_subject, "role": "approver"},
|
||||
"executing_workload": self.cfg.executor_identity,
|
||||
"runtime_principal": self.cfg.runtime_principal,
|
||||
"tenant": self.cfg.tenant, "environment": self.cfg.environment,
|
||||
|
|
@ -358,10 +361,16 @@ class Run:
|
|||
self.d.alert("consume_unconfirmed", {"admission": self.spec["id"], "run_id": self.run_id})
|
||||
return {"outcome": "refused", "reason": str(refusal), "run_id": self.run_id, "effects": "consume-only"}
|
||||
try:
|
||||
self.d.audit.emit(self._event(STARTED, {"run_id": self.run_id, "admission": self.spec["id"],
|
||||
"approval": self.approval_id, "consumed_at": self.consumed_at,
|
||||
"executing_workload": self.cfg.executor_identity},
|
||||
occurred_at=self.d.wall()))
|
||||
self.d.audit.emit(self._event(STARTED, {
|
||||
"receipt_version": RECEIPT_VERSION, "run_id": self.run_id, "admission": self.spec["id"],
|
||||
"admission_digest": bea.admission_digest(self.doc), "plan_digest": self.spec["plan_digest"],
|
||||
"approval": self.approval_id, "consumed_at": self.consumed_at,
|
||||
"executing_workload": self.cfg.executor_identity, "runtime_principal": self.cfg.runtime_principal,
|
||||
"steps_planned": len(self.spec["plan"]["steps"]),
|
||||
# audit-core holds each run to its own window (AUDIT-WP-0018-T03): a started event without a receipt
|
||||
# becomes a finding after the earlier of this and the sender's declared bound.
|
||||
"window": {"not_after": iso(bea.parse_time(self.spec["window"]["not_after"]))}},
|
||||
occurred_at=self.d.wall()))
|
||||
except Exception:
|
||||
self.d.alert("started_not_in_custody", {"admission": self.spec["id"], "run_id": self.run_id})
|
||||
return {"outcome": "stopped", "reason": "audit_unavailable", "run_id": self.run_id, "effects": "consume-only"}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue