informed-decision/docs/specs/LifecycleServiceContract.md
tegwick 4f546ae3b1 Prepare authenticated lifecycle runtime and production release candidate
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a10780-6d41-7952-9718-dfbc1f41236f
2026-10-04 21:40:19 +02:00

6.4 KiB

Protected lifecycle application service

2026-10-04; INFD-WP-0007-T02/T03. Implemented in informed_decision/lifecycle_service.py, above LifecycleRepository. This is a headless Python application interface. An optional runtime composition enables the browser workspace; production admission is still outstanding.

Caller and authority

The identity adapter supplies an immutable Caller: tenant, actor subject/kind, finite expiry and JSON identity attributes. This is trusted in-process context, not a client-controlled payload or credential. Caller.from_human_session imports the existing verified KeyCape session, preserving tenant provenance, authentication-derived human identity, assurance, roles and groups. It carries neither the browser token nor CSRF material into policy requests or stored events.

Agent adapters must authenticate the workload before constructing Caller. This increment does not add an issuer, infer a tenant from a request body, treat a claimed agent name as authentication, or authorize a new workload audience. The existing intake TokenReview audience is not silently reused for this service. 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, lifecycle.events, lifecycle.publish, lifecycle.search, lifecycle.packet, lifecycle.request_review and lifecycle.outcomes. 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>, system informed-decision, type decision-memo, exact configured policy pin, and context:

  • contract: informed-decision.lifecycle.v1;
  • 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;
  • 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 is supplied as an input to the decision it presents for. Advisory content is not sent to the PDP in clear text. Identity attributes are supplied by trusted adapters, not copied from memo content.

Freshness, replay and evidence

Every operation, including an idempotent retry, obtains a new policy observation and persists it using the existing Store. A deny, expired or mismatched response, unsupported obligation or unavailable policy service prevents the operation. The existing PolicyClient validates resource/subject/context, request digests, 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 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.

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, 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 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 HTTP adapter maps these to bounded envelopes without echoing content, credentials or raw exception diagnostics.

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 source handoff now follows LifecycleReviewHandoff.md. Production forwarding and broader review profiles remain open.

Before enabling a listener, T02/T04 must provide: reviewed lifecycle PDP package and action/context fixtures; admitted human/workload identity adapters; closed HTTP schemas with canonical URLs and request limits; service admission and publication audience contracts; lifecycle audit receiver admission and readiness accounting. Test fixtures exercise the real PolicyClient against synthetic policy transport. They are not evidence of a deployed lifecycle policy grant.

Production candidate record facts also include revision_by, the immutable actor of the requested revision. This lets its editor retain access to an unpublished successor after refining a published decision. Original authorship remains registered_by; both facts participate in the same transaction/read race guard.