Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

State and ownership

Architecture reconciliation (2026-10-25): the delivery, discovery and activation contract controls intended production routes and reader intent. Existing Codex AGENTS.md contribution APIs described here are experimental library boundaries, not production independent-rule delivery. Independent Codex/OpenCode rules remain beside unchanged team guidance through a selected native or hook route. The native-first amendment permits explicit OpenCode paths after Q5 PASS and selects the trusted Codex hook with its declared recorded-history limitation. Reusable ownership, preflight and recovery mechanisms do not select or qualify a runtime route, and schema-2 production activation remains blocked (AD-0040 amendment).

This concern owns the requirements and implemented architecture below. Moving these records does not change their implementation or approval status. Library-only capabilities remain separate from production CLI activation; each section records its own limits. Accepted decisions remain in ADRs.

Requirements

Pure legacy-state projection

The internal library boundary legacy::project_legacy(&Lockfile) describes typed schema-1 state without I/O or mutation. It retains an owned snapshot and indexed item and bundle rows, including order, duplicates, installed aliases, source names, recorded refs and SSOT hashes, dependency/group links, and raw client strings. Explicitly tagged plugin and MCP rows preserve their original targets, scopes, statuses and bundle references. Typed preservation does not recover unknown JSON fields or the original serialization bytes.

Persisted source labels are decoded with the existing compatibility parser. A parsed source or recorded ref/hash does not prove an immutable source revision or ownership of generated bytes. Requested selection remains explicitly unresolved for every item, including singleton effective client lists. Empty client lists expand to the fixed historical Claude, Copilot and OpenCode clients; unknown spellings remain in the snapshot and produce structured diagnostics.

Candidates contain only a historical client, a relative entrypoint and a relative resource search base. The frozen nine kind/client layouts are shared with the current pipeline and use the installed alias. They do not inspect roots, files or resources, establish ownership, or authorize adoption or deletion. Bundle names and ancillary files do not produce candidates. Invalid installed names suppress candidates: empty names, dot, dot-dot, slash, backslash, colon and control characters are invalid; spaces and Unicode remain representable. Invalid sources retain otherwise known layout facts. Duplicate identities and identical entrypoints identify all affected item indices without selecting a winner. Diagnostics retain malformed rows for inspection. Unsupported schema values retain all typed records but produce no interpreted sources, clients or destinations.

This prerequisite leaves commands and schema-1 persistence unchanged. Verified sources, reproducible historical rendering, resource enumeration, adoption, requested-selection reconciliation, schema-2 lifecycle preservation, exact backup, containment, preflight, writer locking and recovery remain later work.

See the projection design and AD-0015 for implementation boundaries and rationale.

Combined lifecycle state foundation

The opt-in lifecycle::state library validates a schema-2 installation and reference graph without I/O. Direct references root the graph; bundle members and dependencies must resolve, be reachable and contain no dependency cycle. Installations retain requested surfaces even when no artifact is generated. Duplicate identities and inconsistent source revision evidence are rejected. Artifact owners and roots must match authoritative installations. Explicit per-owner consumer associations must match every artifact selector exactly; their union must equal the artifact consumers. The strict schema-2 codec validates before parsing results are returned and before serialization. It emits deterministic pretty JSON with one trailing newline without mutating input. Typed schema-1 projection creates no owners and preserves historical rows, labels and statuses in their original order; exact JSON bytes and unknown legacy fields require the later migration backup.

This library does not change lockfile::CURRENT_SCHEMA, command dispatch or production lockfiles. It does not establish filesystem ownership or native support. See combined lifecycle state.

Per-owner recorded contract evidence

The opt-in combined state accepts optional explicit rule-owner contracts. Missing historical evidence stays unknown, is omitted when empty and gains no inferred revision; schema-1 projection produces no contracts. Each row binds exact scope, raw root, path, optional contribution selector and installation ID to captured source evidence, normalized artifact content and complete consumer/revision pairs. File content includes hash and optional mode; instruction content includes its body hash without aggregate whitespace bookkeeping.

Every present row must match an authoritative owner-consumer association, installation source evidence and owned artifact. Duplicate/orphan identities, inexact bindings or consumer coverage, duplicate/empty profiles and zero revisions reject. Unknown serialized fields reject. Deterministic encoding sorts contracts and profiles without changing the supplied state.

Agreement validates the whole state and selects an exact artifact identity. Unknown identities error. It reports missing evidence and source/profile conflicts in artifact-owner order. Matching requires all owners’ full source manifest digests and complete surface/revision maps to agree; profile vector order is immaterial. Equal bytes and differing consumer subsets do not suffice.

Upsert receives the complete explicit contract subset for changed installations; omitted changed evidence becomes unknown. Untouched rows survive exactly, replacement identities preserve relative order among surviving rows and new rows append in identity order. Evidence for unchanged installations rejects. Complete resulting state validation precedes successful planning. Empty desired outputs and reference graph retirement remove only associated retired evidence; surviving shared owners retain their contracts.

This inactive supplied-value boundary grants no shared-mutation, native, filesystem or source-authenticity authority. The original upsert and public mixed planner retain affected multi-owner rejection; explicit shared-output reconciliation consumes agreement through a separate entrypoint. Production commands and schema 1 are unchanged. Combined lifecycle delivery evidence is recorded in the Gate 5 acceptance note. See the owner-contract design and AD-0034.

Architecture

Combined lifecycle state

The opt-in lifecycle::state library describes schema-2 state for the future owned-content lifecycle. Current commands still use schema 1. These records do not migrate state, fetch sources, claim ownership or write output files. The combined lifecycle state foundation owns the requirements; lifecycle::state exposes the records and public functions.

Public records and validation

CombinedState holds the schema number, installations, bundles, references, owned artifacts, per-owner consumers, optional owner contracts, optional origin evidence and an optional typed legacy snapshot. An Installation reuses ArtifactOwner and InstallationRoot, and records its complete requested target surfaces and SourceEvidence. A BundleNode records its ID, source, name and optional source revision. Reference distinguishes direct installations, direct bundles, bundle membership and bundle dependencies. OwnerConsumers binds an installation ID to a root, relative path, optional contribution selector and explicit consumers. Existing ownership, target and schema-1 types retain their wire representations. No new dependency is required.

validate_state checks the complete envelope and reuses ownership::validate_artifacts for artifact shape. associations supplies the internal owner/selector checks within that same public validation boundary. Parsing and serialization both call this validator. Inputs remain unchanged on rejection.

Reference authority

Installations bind an explicit ID to source, item kind, source name, installed alias, root, requested surfaces and captured source-manifest identity. Git records require their resolved revision to match the owner binding; local records have no revision. These recorded strings do not prove source authenticity. Manifest digests must contain exactly 64 lowercase hexadecimal characters; installation sources and names are nonempty. Roots must be absolute, and requested surfaces must be nonempty and unique. Git evidence requires a nonempty revision; an optional bundle revision cannot be empty.

Node IDs are caller supplied, nonempty ASCII alphanumeric, hyphen or underscore strings, unique within each node kind. Bundle and installation namespaces are typed, so the same string may occur in both. Identity allocation for lifecycle operations remains later work.

Direct installation and bundle references are roots of a directed graph. Bundle membership and dependencies retain existing installations without duplicating artifacts. Validation rejects duplicate IDs and edges, missing endpoints, bundle cycles and unreachable nodes. An iterative topological traversal rejects bundle cycles without recursive graph traversal, and traversal from direct references establishes reachability. Installations with no generated output remain valid when referenced, for example after audience exclusion.

Distinct IDs cannot describe the same source/kind/name/alias installation under the same exact root. Requested surfaces and resolved revisions may change on update without changing that identity. Root comparisons are lexical and retain raw spellings; they establish no filesystem equivalence or containment.

The combined graph is the single authority for new installation bindings and references. Reusing existing ownership and legacy types preserves their reviewed contracts. Independent writable sidecars and automatic legacy adoption were rejected because they would separate authority or invent ownership evidence.

Delivery boundary

This foundation is separate from command activation, source resolution, compiler reconciliation, migration backup, application and recovery. Gate 5 requires all lifecycle slices; Gate 6 supplies target qualification and activation.

Owners and consumers

Every artifact owner must exactly match its authoritative installation, including revision and alias. Artifact roots must match the installation scope and raw root spelling. Existing artifact validation still rejects ambiguous whole-file/contribution locations and malformed content metadata.

Each artifact selector and owner has exactly one explicit consumer association. Whole files use no contribution selector; Markdown regions use their recorded contribution ID. Associations must name existing owners, contain unique nonempty surfaces within that owner’s requested selection, and have no surplus entries. The union of these associations equals the artifact consumer set exactly. Validation never infers individual consumers from that union. Optional owner contracts bind explicit source, normalized content and revision evidence to these associations; absent rows stay unknown.

Codec and retained legacy data

parse_state and serialize_state both validate the complete envelope. Schema-2 records reject unknown fields. Encoding sorts owned graph records, selectors, owners and consumer lists in a cloned value and emits pretty JSON with one trailing newline. It does not change the supplied state.

project_legacy requires schema 1 and retains its typed Lockfile snapshot in legacy, with an empty owned graph. Legacy arrays, duplicate rows, labels, recorded refs, optional fields and integration statuses keep their original values and order. They are unresolved historical data, never ownership evidence. Unknown original JSON fields and serialization bytes are outside this typed projection. The applying lifecycle must preserve them in an exact first-write backup; this pure slice does not create that backup. The text codec accepts UTF-8 &str; future byte-loading callers must reject invalid UTF-8 explicitly.

An empty envelope has schema: 2, empty installations, bundles, references, artifacts and owner_consumers arrays, and legacy: null. New references are tagged as direct-installation, direct-bundle, bundle-installation or bundle-dependency; source evidence is tagged git or local. Legacy fields keep their existing schema-1 names rather than being normalized to new tags.

Verification boundary

tests/lifecycle_state.rs exercises graph sharing, every reference kind, reachability and cycles, identity collisions, source revision/hash bindings, audience-excluded installations, exact owner/consumer associations, lexical roots and selectors, strict decoding, deterministic owned ordering and typed legacy retention. Independent controls omit validation or sorting checks and require assertion failures after each omission, followed by exact restoration. The validation record records the checked revision and scope of the evidence.

This completes the pure state slice. Separate captured desired-set production, reference reconciliation and shared-owner removal planning are implemented. Complete single-owner upsert planning is also implemented. A separate explicit shared upsert now reconciles supplied owner contracts and observations. The opt-in guarded application boundary connects complete artifact plans and combined state to checkpointed execution, including the exact original-state backup and recovery path. Observational lifecycle adapters, native and complete lifecycle qualification remain required; this record does not declare Gate 5 or M1 complete.

Per-owner recorded contract evidence

The inactive lifecycle::contracts boundary records supplied contract evidence inside authoritative CombinedState. It supports rule installations and exposes pure validation and agreement checking. Production commands and schema-1 lockfiles do not consume it. It adds no dependencies or native effects.

Representation and validation

CombinedState.owner_contracts is an optional vector, defaulting to empty and omitted from serialization when empty. Historical absence stays unknown; schema-1 projection creates no contracts. OwnerConsumers continues to describe consumer associations without implying adapter revisions or contract evidence.

An OwnerContract binds exact scope, raw root spelling, path, optional instruction contribution selector and installation ID to a captured source-manifest digest, normalized content and explicit ConsumerContract surface/revision pairs. Families follow stable target surfaces. File content includes its recorded hash and optional mode. Instruction content includes the body hash and excludes aggregate whitespace bookkeeping, allowing preserved manual bytes or removal of neighboring contributions without invalidating an untouched body’s evidence.

Whole-state validation checks every present row against an authoritative owner-consumer association, installation source evidence and owned artifact. It rejects duplicate identities, orphan evidence, changed roots/selectors, inexact source/content bindings, empty or duplicate profiles, inexact consumer coverage and zero revisions. Rows need not cover every owner. Strict unknown-field rejection remains. Serialization clones the state, sorts rows by scope/raw root/path/selector/installation identity and profiles by surface, and leaves the input unchanged.

Agreement boundary

check_contract_agreement validates the complete state, then locates an artifact by exact supplied root, path and contribution selector. An unknown identity is an error. It walks artifact owners in their recorded order, reports every owner missing evidence, and compares evidenced owners with the first evidenced owner. Source-manifest and target-profile conflicts are separate diagnostics and can both occur for an owner. Profile vector order is immaterial.

Matching requires each owner to have evidence, identical full captured-manifest digests and identical complete surface/revision maps. Normalized content already matches the artifact through validation. Different consumer subsets block even when output bytes match. This supplied agreement establishes no native behavior, source authenticity, physical identity, new ownership or execution authority.

Lifecycle propagation

UpsertRequest.contracts supplies the complete explicit evidence subset for changed installations. Omitted changed rows become unknown. Untouched rows retain their values and relative order; replacements occupy corresponding surviving positions after removals. New rows append in identity order. Supplied evidence for unchanged installations and duplicate rows reject. The complete resulting state is validated before a plan is returned, so stale or invalid proof exposes no partial plan.

Empty desired sets remove output evidence while retaining referenced installation history. Graph retirement removes retired installations’ contracts. Reference removal preserves surviving shared owners’ contracts and removes retired owners’ contracts, including instruction contributions and final empty aggregates.

The original plan_upsert and public mixed-planner sharing rejections remain intact. The separate shared upsert consumes agreement for affected desired final multi-owner artifacts. Complete selected recompilation can replace unknown history; unknown unselected proof is never inferred. Agreement alone grants no shared-mutation or execution authority. This supplied-value path does not integrate qualified source/destination observations, complete Gate 5 or qualify native clients.

Components

Pure owner release with no desired value at an identity preserves untouched survivors and their unknown evidence without requiring agreement. Supplied physical observations and all physical checks still apply.

Pure legacy-state projection

The public library boundary legacy::project_legacy accepts a borrowed typed Lockfile and returns an owned LegacyProjection. It has no scope-root input, I/O, state writer or CLI dispatch. AD-0015 records the choice of this intermediate boundary; the specification owns its requirements.

Snapshot and interpreted rows

The snapshot is an exact typed clone, including original vector order and every current field. It does not preserve unknown JSON fields or serialization bytes. Items and bundles retain their original index and cloned record. Plugins precede MCP entries in a tagged integration vector; each integration index addresses its original plugin or MCP vector. Native targets, scopes and statuses remain facts, without runtime verification or permission to operate on the target.

Unsupported schema values retain all these records, produce a global diagnostic, and return without interpreting sources, clients or destinations. For schema 1, source decoding uses parse_install_source_label; parse errors retain the raw record and actionable parser message. Successful parsing does not verify source availability, a revision or generated bytes.

Every item has RequestedSelection::Unresolved. Effective generation clients cannot recover requested families lost through author audience filtering, and a Copilot client does not distinguish CLI from VS Code Local. Integration targets remain separate from requested content selection.

Historical clients and layout

legacy::layout fixes the historical client set to Claude, Copilot and OpenCode. An empty recorded list expands to those three in that order. Explicit known clients retain first-occurrence order and produce one candidate each; unknown spellings produce diagnostics and remain in the cloned record. An unknown-only list produces no candidates.

The nine kind/client path mappings use the installed alias. Current pipeline::output wrappers delegate to the same native PathBuf helpers through explicit current-client conversion. Those helpers retain raw-name behavior for existing pipeline callers. Only the projector validates names before converting paths to forward-slash strings: empty names, dot, dot-dot, slash, backslash, colon and control characters suppress candidates; spaces and Unicode remain representable.

Each candidate carries a client, relative entrypoint and relative resource search base. Skills and OpenCode rules are directory-backed; flat entrypoints use a sibling name directory for resources. These paths are relative to a later caller’s established legacy scope root. They do not establish existence, containment or ownership, enumerate resources or permit recursive adoption. Malformed sources do not erase otherwise known layout facts. Bundles and ancillary files have no candidates.

Diagnostics and later consumers

Diagnostics are structured data located at a global, item or bundle position. The projector continues across malformed rows. Separate borrowed-key groups collect duplicate (kind, name) identities and identical entrypoint candidates across distinct rows, retaining every affected index. Collision locations anchor at the first affected row; no record wins or disappears. Disjoint client lists can duplicate an identity without sharing an entrypoint. Resource-tree overlap analysis requires later verified resource enumeration.

The projection has no serialization contract, ownership record, artifact hash, mode, source-verification result or generic operation-blocking flag. Later planners determine which unresolved facts an operation requires. Source verification, historical rendering, resource enumeration, adoption, requested selection reconciliation, schema-2 lifecycle preservation, exact backup and transactional execution remain outside this boundary.

tests/legacy_projection.rs pins typed preservation, all nine literal layouts, client order and candidate identity, source compatibility, malformed and duplicate rows, unsupported schemas and root-independent paths. Existing pipeline output and resource tests cover the unchanged wrappers. These checks do not establish native Windows execution, native discovery or actual artifact ownership.

Durable lifecycle origin

Requirements

Later writes must preserve initial-state origin and retained typed legacy history. Fresh initialization requires absent state and backup and creates private state without inventing historical bytes. Migration binds the exact original schema-1 bytes; reopened writes reject changed backup evidence. Missing historical provenance remains unknown and blocks bound application.

Architecture

Combined state optionally carries a strict origin record. fresh identifies an originally absent lockfile and requires no legacy snapshot. migrated requires a retained schema-1 snapshot and binds the original exact backup bytes with backup_sha256 and backup_length. The constructor StateOrigin::from_schema1_bytes hashes original bytes, including whitespace and unknown schema-1 fields. Existing unbound internal schema-2 records remain valid for inspection; an applying adapter cannot infer original provenance from them.

Origin survives complete upsert and reference-retirement planning. The bound application boundary preserves it across writes, verifies the canonical backup against that durable evidence and refuses changed origin or legacy history. Fresh initialization has no fabricated schema-1 backup. See AD-0038 and the origin validation record.

Edit the canonical source.