Connect lifecycle revisions to protected human review

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a10780-6d41-7952-9718-dfbc1f41236f
This commit is contained in:
tegwick 2026-10-04 20:33:24 +02:00
parent 0e79d69e27
commit f3b71b1521
16 changed files with 649 additions and 23 deletions

View file

@ -99,3 +99,7 @@ content, verified and written in the same transaction as the memo/event. New
registration requires packet bytes; revisions may retain their current packet's
digests. The details and limits are in `LifecycleHTTPContract.md`. Idempotency
digests for prior commands with no documents remain compatible.
Schema 5 adds unique review-handoff indexes and prevents older code without
source-revision guards from opening the store. Migration preserves schema-4
records. See LifecycleReviewHandoff.md for the rollback boundary.

View file

@ -10,6 +10,8 @@ existing browser app or public ingress.
| `POST /v1/decisions/{id}/publications` | `version`, `audience` with explicit `subjects` and `groups` lists | Durable publication event receipt |
| `POST /v1/decisions` | `memo`, optional `documents` | Register an unbound version-one memo and return event/record/revision/resource receipt |
| `POST /v1/decisions/{id}/revisions` | `memo`, `expected_version`, `summary`, optional `documents` | Compare-and-append successor revision; same receipt shape |
| `POST /v1/decisions/{id}/review-requests` | `version`, `options` per LifecycleReviewHandoff | Durable request receipt and exact intake envelope |
| `GET /v1/decisions/{id}/outcomes` | Required positive `version` | Correlated review/disposition evidence and submission state |
| `GET /v1/decisions/{id}` | Optional positive integer `version` | Existing Memo JSON for the current or specified revision |
| `GET /v1/decisions/{id}/events` | Optional `after` sequence cursor, `limit` 1–100 | `{"events": [...]}` ordered by sequence; use last returned sequence for next page |
| `POST /v1/decisions/{id}/comments` | `{"version": 1, "body": {"text": "...", "locator": "optional"}}` | Durable event receipt |
@ -88,3 +90,6 @@ the command including documents; identity and decision freshness are rechecked
in the write transaction. Replays are reauthorized and return the original event.
Conflicting content for a key, stale revision preconditions and attempts to
replace an existing digest's media type produce 409 without a partial write.
Review handoff, its supported profile and source-revision guards are specified in
`LifecycleReviewHandoff.md`. Human acceptance remains on the protected review route.

View file

@ -0,0 +1,90 @@
# Revision-specific lifecycle review handoff
2026-10-04; INFD-WP-0007-T02 through T06. Source implementation, not deployed.
## Headless contract
`POST /v1/decisions/{id}/review-requests` accepts `version` and `options`, with
an Idempotency-Key. Options have exactly `submitter`, `memo_class`, `review_group`
and `approval_intent` (the existing intake action/principal/purpose/target object).
A fresh `lifecycle.request_review` policy observation covers the complete command;
`context.review_routing` exposes these routing/act facts to the PDP. Publication
is required for that current revision. Publication itself never initiates review.
The service atomically retains one immutable `review_requested` event per source
revision, its source attribution, and an intake envelope under a fresh review
memo id. Retries return the retained request; different commands for the same
key or a second request for that revision conflict. This creates neither an
approval nor a disposition. The envelope and event fit within 256 KiB. An admitted
workload submits the exact returned envelope to the existing private intake.
The envelope is also retained in authorized event history for recovery.
The handoff currently supports the platform tenant, English organizational
`approve` memos at the current renderer release, and the intake's nonempty UTF-8
packet profile. Unsupported projections are refused rather than dropping fields.
Broader L0/L2 profiles, browser packet authoring and automated forwarding remain
live work in T02/T04/T05 and WP-0005. There is no automatic submitter or default
workload credential. A caller-selected submitter is a routing request, not identity.
The local use of intake's shape validator does not grant admission: intake always
reauthenticates the workload and reloads its actual grant and recipient mapping.
## Immutable snapshot and uncertainty
The original lifecycle memo remains unchanged. Intake alone assigns the named
human and the engine-returned approval pins to the protected review snapshot.
The snapshot's local version is one; the immutable request and presentation
content link it to the original source id and revision. The human review page
shows that source revision. The shared Decision Memo schema and signature rules
are unchanged; the source link is provenance, not a new authorization claim.
Before reserving engine creation and before local loading, intake checks exact
envelope equality, the designated submitter and source currency. Any change
fails closed. Existing durable `creating`/`created` receipts retain uncertainty;
a lost create reply is never automatically reposted. A revision during creation
can leave an engine object with a retained receipt but no reviewable snapshot.
This needs existing explicit recovery/reconciliation, not an automatic retry or
an inferred revocation. Approval Engine remains the sole approval-state owner.
Direct import and revision of reserved review snapshot ids are refused. Source
revisions invalidate subsequent presentation, acknowledgment, disposition and
entry reservation. Once an entry is in flight or unresolved, source revision is
blocked until the original attempt is resolved. This serializes local mutation
against external entry attempts without pretending to make the network atomic.
Historical accepted evidence remains available; a successor inherits no answer.
## Outcomes and human workspace
`GET /v1/decisions/{id}/outcomes?version=N` requires a fresh `lifecycle.outcomes`
check. It returns the correlated intake delivery state, source staleness and
recorded dispositions with their submission state and confirmation timestamp.
These are local evidence and transport states, never cached approval validity.
`prepared`, `in_flight` and `unresolved` are not confirmed acceptance.
The workspace displays authorized outcomes and links a ready, current handoff to
`/review?memo_id=...`. That route still requires its independent review PDP checks,
verified human identity, fresh presentation and required acknowledgments. Agents
can prepare, forward and retrieve; they cannot answer. No lifecycle accept route
exists. Audience access and the named human review entitlement are separately
checked by their existing policy consumers; a workspace link grants neither.
## Evidence and delivery
The request event enters the existing lifecycle commitment queue. Intake posing,
presentation, disposition and submission evidence keep their existing independent
outboxes and receipt requirements. The integration test creates an approval via
the real Approval Engine API, records a signed-fixture human entry, retrieves the
confirmed result for source revision two, and delivers lifecycle and review
commitments to the real Audit Core receiver. Identity and PDP transport are
synthetic. Production grants, private listener/forwarding composition, full
release/rollback evidence and separately approved rollout remain outstanding.
## Migration and rollback boundary
Opening the store upgrades schema 4 to schema 5 transactionally, adding unique
indexes for review snapshot identity and source revision. Existing evidence is
preserved. The version change deliberately causes older Store implementations to
refuse the database: they do not enforce these source guards. Before activation,
retain the normal database backup and image/config pins. An older-image rollback
requires a compatible restored database plus reconciliation of any subsequent
engine side effects; it cannot safely discard new review/entry evidence. A
reviewed forward fix remains preferable once new handoffs have been written.

View file

@ -24,7 +24,8 @@ 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` and `lifecycle.packet`. These are not added to the existing review-action allowlist;
`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>`,
@ -92,7 +93,8 @@ 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.
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

View file

@ -16,9 +16,9 @@ rendering a button grants no authority.
Publication records an audience for one revision. Access Engine determines access
using authenticated identity and retained record facts. Publication creates no
approval and no human disposition. New revisions require their own publication.
The workspace explicitly states that human acceptance is unavailable for these
drafts. Connecting a lifecycle revision to the existing protected presentation
and Approval Engine path remains T03/T05 work.
The workspace exposes authorized outcomes and a protected human review link
when an exact revision handoff has been loaded by intake. Drafts without that
handoff remain unavailable for acceptance. See LifecycleReviewHandoff.md.
## Runtime and deployment inputs
@ -64,7 +64,8 @@ and API ingress. The policy must cover all actions in LifecycleServiceContract,
including collection search and per-record checks. Existing compact-sitting pins
do not supply these grants. WP-0005 retains private intake composition work.
T03/T05 require the durable review handoff and human-only acceptance integration.
The source handoff now integrates human-only acceptance for the admitted intake
profile; T03/T05 retain broader profiles and browser authoring/forwarding work.
T06 requires full acceptance evidence, sender/cadence admission, immutable image,
database backup/migration and rollback verification. T07 requires the separately
approved concrete live rollout. No production pins or resources changed here.