Implement lifecycle workspace publication and audit delivery
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a10780-6d41-7952-9718-dfbc1f41236f
This commit is contained in:
parent
ee472acc87
commit
5fec7de4cb
21 changed files with 1022 additions and 78 deletions
|
|
@ -20,7 +20,7 @@ import uuid
|
|||
|
||||
from .approval_client import EntryResult
|
||||
from .disposition import Actor, BINDING_VERBS, DispositionRefused, Verb, record
|
||||
from .evidence import EventClass, commit_disposition, commit_presentation, commit_stance_application, heartbeat, HEARTBEAT_CLASSES
|
||||
from .evidence import Commitment, EventClass, commit_disposition, commit_presentation, commit_stance_application, heartbeat, HEARTBEAT_CLASSES
|
||||
from .memo import Memo
|
||||
from .presentation import render
|
||||
from .provenance import Claim, Route, assert_human_control_dischargeable
|
||||
|
|
@ -556,18 +556,50 @@ class Store:
|
|||
"(SELECT id FROM outbox WHERE state='delivered')")
|
||||
return result.rowcount
|
||||
|
||||
def stage_lifecycle_audit(self):
|
||||
"""Opt-in commitment-only delivery through the existing audit outbox."""
|
||||
with self._transaction() as db:
|
||||
rows = db.execute("SELECT l.* FROM lifecycle_pending p JOIN lifecycle_events l ON l.id=p.event_id "
|
||||
"ORDER BY l.sequence LIMIT 100").fetchall()
|
||||
staged = 0
|
||||
for row in rows:
|
||||
if row["tenant"] != "tenant:platform":
|
||||
raise Conflict("lifecycle audit tenant not admitted by this runtime")
|
||||
content = {"event": dict(row), "memo": self._memo(db, row["memo_id"], row["version"])}
|
||||
existing = db.execute("SELECT class,content FROM evidence WHERE id=?", (row["id"],)).fetchone()
|
||||
if existing:
|
||||
if existing["class"] != EventClass.LIFECYCLE.value or existing["content"] != dumps(content):
|
||||
raise Conflict("lifecycle evidence id collision")
|
||||
continue
|
||||
commitment = Commitment(row["id"], EventClass.LIFECYCLE, row["at"], {
|
||||
"memo_id": row["memo_id"], "memo_version": row["version"], "operation": row["kind"],
|
||||
"content_sha256": "sha256:" + hashlib.sha256(dumps(content).encode()).hexdigest(),
|
||||
"content_exists": True, "custody": "informed-decision:lifecycle:" + row["id"]})
|
||||
self._event(db, commitment, content)
|
||||
staged += 1
|
||||
return staged
|
||||
|
||||
def settle_lifecycle_audit(self):
|
||||
with self._transaction() as db:
|
||||
return db.execute("DELETE FROM lifecycle_pending WHERE event_id IN "
|
||||
"(SELECT o.id FROM outbox o JOIN evidence e ON e.id=o.id "
|
||||
"WHERE o.state='delivered' AND o.receiver_reference IS NOT NULL AND e.class=?)",
|
||||
(EventClass.LIFECYCLE.value,)).rowcount
|
||||
|
||||
def intake_audit_pending(self):
|
||||
with self._connection() as db:
|
||||
return db.execute("SELECT 1 FROM intake_deliveries WHERE kind='memo-posed' "
|
||||
"AND state!='delivered' LIMIT 1").fetchone() is not None
|
||||
|
||||
def queue_heartbeats(self, now=None, *, include_posed=False):
|
||||
def queue_heartbeats(self, now=None, *, include_posed=False, include_lifecycle=False):
|
||||
now = time.time() if now is None else now
|
||||
count = 0
|
||||
with self._transaction() as db:
|
||||
classes = {**HEARTBEAT_CLASSES}
|
||||
if include_posed:
|
||||
classes[EventClass.MEMO_POSED.value] = 86400
|
||||
if include_lifecycle:
|
||||
classes[EventClass.LIFECYCLE.value] = 86400
|
||||
for event_class, interval in classes.items():
|
||||
if db.execute("SELECT 1 FROM outbox o JOIN evidence e ON e.id=o.id WHERE o.state!='delivered' "
|
||||
"AND (e.class=? OR (e.class=? AND json_extract(e.envelope,'$.data.class')=?)) LIMIT 1",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue