informed-decision/docs/specs/LifecycleFoundationContract.md
tegwick f3b71b1521 Connect lifecycle revisions to protected human review
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a10780-6d41-7952-9718-dfbc1f41236f
2026-10-04 20:33:24 +02:00

6.4 KiB
Raw Blame History

Lifecycle foundation: first implementation contract

2026-10-04; INFD-WP-0007-T02/T03. This is the internal persistence slice of DecisionLifecycleService.md. The protected application interface built on this slice is documented in LifecycleServiceContract.md; HTTP exposure is pending.

Implemented boundary

LifecycleRepository depends on the existing Store, memo and disposition types. It imports no UI, HTTP runtime or provider client. It registers complete, unbound version-one memos and creates immutable successor revisions. The existing Decision Memo is reused without schema changes. Incomplete authoring drafts and conversion of legacy/approval-bound records remain future work.

Each call carries Attribution(tenant, Actor(subject, kind)) from its trusted caller. This is audit attribution, not an authentication assertion. Like Store, the repository is an internal persistence interface: the application/API layer must authenticate callers and enforce current access-engine authorization before invoking it. Supplying a tenant string is not an access grant. No route or runtime configuration exposes these operations in this increment.

Commands and reads

Method Contract
register(context, memo, key=..., documents=...) Version 1, no approval binding or seal; rejects ids already present in the memo store; returns durable event id
revise(context, memo, expected_version=..., summary=..., key=..., documents=...) Exact current revision precondition and next version; nonempty change summary; returns durable event id
annotate(context, memo_id, version, kind=..., body=..., key=...) Append source/evaluation/comment to an existing exact revision; returns durable event id
retrieve(tenant, memo_id, version=None) Retrieve current or historical memo only within its registered tenant
events(tenant, memo_id, after=0, limit=100) Ordered cursor page of attributed immutable events, maximum 100

Registration returns the memo's existing caller-supplied id through retrieval; generated public ids and canonical URLs are deferred to the API adapter. Memo ids remain globally unique because existing review URLs and storage use that key. An existing imported memo cannot be taken over by registration, even if its bytes match. A tenant mismatch produces the same unavailable error as a missing record. This is partition enforcement, not a replacement for per-record authorization.

Idempotency scope is tenant + actor kind/subject + operation + key. A SHA-256 of canonical command JSON detects content conflicts, including changed revision preconditions. Exact retries return the original event id even after subsequent revisions. Different authors must use concurrency preconditions; matching content alone never makes a stale edit succeed. The command bound is 256 KiB.

All writes use one SQLite transaction for memo content, immutable lifecycle event, pending-delivery row and idempotency receipt. An event queue failure rolls back the content mutation. Events contain actor, tenant, server timestamp, record and revision; they do not record an authorization verdict or approval validity.

Companion records

  • Comment: text, optional locator. This is discussion, not a disposition.
  • Source: reference, timezone-qualified retrieved_at, SHA-256 digest, summary. These are supplied research provenance, not a claim that the service fetched or verified the source. This slice performs no network fetching.
  • Evaluation: assessment, source_ids, uncertainty. Referenced source event ids must belong to this record, tenant and revision. Up to 100 references are accepted. Empty references are allowed for an explicitly uncited assessment.

Fields are closed and text is bounded at 32,768 characters per field. Annotations cannot silently enter view_hash. Promoting evidence into the commitment requires a new memo revision using the existing packet/binding rules. Research comments from agents never go through or weaken the human disposition guards.

Migration, evidence and rollout

Store schema 4 adds lifecycle_records, lifecycle_events, lifecycle_operations and lifecycle_pending; migrations from versions 1–3 preserve existing memo, presentation, disposition and evidence rows. Historic records are not automatically registered or assigned a tenant. Existing store backup includes lifecycle content and pending rows. Old code refuses schema 4; rollback therefore needs restoration of a pre-migration backup and explicit handling of intervening writes, not simply an old image against the new database.

Memo persistence is extracted into a shared transaction helper without changing legacy save_memo behavior. Existing stale-presentation guards see the same memo versions. Approval-bound/sealed revision handling requires the later review integration; this repository refuses those lifecycle revision commands.

Pending lifecycle events are durable local obligations. The opt-in runtime audit adapter stages immutable informed-decision.lifecycle commitments into the existing transactional outbox and clears pending obligations only after a receiver receipt. Content remains in local custody; the independent commitment contains its digest and existence assertion. Readiness refuses while lifecycle delivery is pending. See LifecycleWorkspaceDelivery.md for the platform tenant boundary, retries, heartbeats and outstanding production admission.

Remaining contracts

T02 remains in progress: production identity/policy composition, versioned HTTP schemas, production publication audience policy, incomplete drafts, non-cluster workload identity, legacy record enrollment, source fetching/retention, live independent evidence delivery and non-approval binding integration. T03 is started with this persistence slice; it is not complete. T04/T05 continue to own HTTP and UI exposure. Native runtime intake and admission remain with INFD-WP-0005.

Packet command extension: optional documents carries digest/media_type/base64 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.