informed-decision/docs/specs/LifecycleFoundationContract.md
tegwick 96dcbfdfa2 Record native authoring and discovery release; complete initial lifecycle delivery
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a10780-6d41-7952-9718-dfbc1f41236f
2026-10-05 01:14:08 +02:00

106 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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`; the deployed private HTTP
adapter is specified in `LifecycleHTTPContract.md`.
## 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. The deployed application adapters authenticate and authorize these operations;
this internal persistence interface is never exposed directly.
## 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. The adapter is deployed for the initial audience. See
`LifecycleWorkspaceDelivery.md` for the platform tenant boundary, retries and
heartbeats.
## Delivery boundary
T02/T03/T04/T05 are complete for the initial production audience. The contracts
are specified in `LifecycleAdmissionBoundary.md`; foundation, HTTP and browser
adapters are deployed. Native intake admission is completed in INFD-WP-0005.
Expansion beyond these explicit limits is tracked in INFD-WP-0008. Legacy imported
records keep their existing review route and cannot be taken over by registration.
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.