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:
tegwick 2026-10-04 20:17:24 +02:00
parent ee472acc87
commit 5fec7de4cb
21 changed files with 1022 additions and 78 deletions

View file

@ -2,8 +2,8 @@
2026-10-04; `INFD-WP-0007-T02/T03`. Implemented in
`informed_decision/lifecycle_service.py`, above `LifecycleRepository`.
This is a headless Python application interface. No HTTP route or production
composition enables it yet.
This is a headless Python application interface. An optional runtime composition
enables the browser workspace; production admission is still outstanding.
## Caller and authority
@ -23,8 +23,8 @@ Non-cluster/workload composition remains T02/T04 work.
The service consumes `PolicyClient` observations. It neither evaluates policy
nor falls back to local permissions. New proposed action names are
`lifecycle.register`, `lifecycle.revise`, `lifecycle.source`,
`lifecycle.evaluation`, `lifecycle.comment`, `lifecycle.read`, and
`lifecycle.events`. These are not added to the existing review-action allowlist;
`lifecycle.evaluation`, `lifecycle.comment`, `lifecycle.read`,
`lifecycle.events`, `lifecycle.publish`, `lifecycle.search` and `lifecycle.packet`. These are not added to the existing review-action allowlist;
review and human disposition contracts remain unchanged.
The request carries the authenticated subject, tenant, resource `memo:<id>`,
@ -35,7 +35,17 @@ and context:
* `memo_version`: submitted/requested revision, or null for latest retrieval and
record-wide event retrieval;
* `command_digest`: SHA-256 of canonical command JSON, including mutation content,
expected revision and idempotency key, or the read/page parameters.
expected revision and idempotency key, or the read/page parameters;
* `record`: tenant-partitioned registration facts, original registering actor,
current revision and latest publication audience for the requested revision;
* `publication_audience`: explicit subjects/groups for a publication command.
Search uses resource `decisions`, type `decision-collection`, then independently
authorizes each candidate with `lifecycle.read`. A page examines at most 50
candidates; denied records are omitted, unavailable policy fails the request.
Opaque cursors are scoped to caller/tenant/query and expire after five minutes.
Packet access checks its own action and a revision-specific packet index; a
known content digest grants no retrieval capability.
The command digest correlates the requested operation; it is not `view_hash`,
an approval binding digest or an authorization verdict. No presentation evidence
@ -53,7 +63,8 @@ policy pins, response lifetime and the supported effect.
Mutation commands snapshot supplied content before the network call. Under the
repository's write transaction, a guard rechecks caller and decision expiry and
the persisted observation before even returning an idempotent replay. New events
the persisted observation and unchanged record/audience facts before even
returning an idempotent replay. New events
carry `body.policy_observation_id`; receipts replay their original event and
observation link while the fresh retry check remains in policy history. The
identity/policy guard does not perform network calls while holding the write lock.
@ -61,26 +72,27 @@ identity/policy guard does not perform network calls while holding the write loc
Read authorization precedes repository lookup. Denied requests therefore cannot
distinguish missing and existing records through lookup errors. The service
rechecks identity and decision expiry after retrieving content and before returning
it. Repository tenant partition enforcement also applies. Record-wide event
it, including unchanged record/audience facts. Repository tenant partition enforcement also applies. Record-wide event
access requires its own action; it does not inherit a memo-read grant.
The existing `decision_attributable: false` declaration remains in force. A
matched, recorded response is not proof of signed PDP attribution. New lifecycle
events still reside in the durable pending queue until an Audit Core contract
and delivery adapter are admitted.
events remain pending until the opt-in audit adapter obtains a receiver receipt.
See LifecycleWorkspaceDelivery.md for custody, reconciliation and admission.
## Error and integration contract
`LifecycleError` supplies bounded status/code pairs: 401 for expired identity,
403 for a policy deny, and 503 for unavailable or unusable policy evidence.
The repository retains `Conflict`, `EvidenceUnavailable` and validation errors;
the future HTTP adapter must map these to bounded envelopes without echoing
the HTTP adapter maps these to bounded envelopes without echoing
content, credentials or raw exception diagnostics.
No accept/publish endpoint is implied by this interface. Human acceptance stays
on the existing presentation/disposition path; publication needs an explicit
audience and separately authorized transition. The service supplies the shared
operations that subsequent HTTP and UI adapters will invoke.
Publication appends an immutable event for the current revision with an explicit
nonempty audience (at most 32 subjects and 32 groups, no wildcard). A successor
revision does not inherit publication. Audience facts are inputs to Access Engine,
never local permissions. Human acceptance requires the separate existing
presentation/disposition path; the lifecycle-to-review handoff is still open.
Before enabling a listener, T02/T04 must provide: reviewed lifecycle PDP package
and action/context fixtures; admitted human/workload identity adapters; closed