Expand description
The sealed, content-addressed release plan — the read-only pre-image the human approves (ADR-0002 §3).
release plan computes and seals a plan_id; release cut --plan <plan_id>
executes it and refuses on repo drift. The binary never prompts: it plans
and exits at the approval boundary.
§What plan_id hashes (the content address)
build derives a ReleasePlan from the already-normalized contract and
detected repo facts, then content-addresses it. The plan_id is the
lowercase SHA-256 hex digest of a canonical JSON pre-image (serde_json,
whose struct-field and BTreeMap ordering is deterministic) covering
exactly, in this fixed order:
- a domain separator +
SEAL_VERSION— so aplan_idcan never collide with any other ossctl digest and the canonicalization format can be evolved by a deliberateSEAL_VERSIONbump instead of silently; - the contract-document
schema_version(ADR-0002 lists it explicitly); - the full normalized contract JSON (
contract show’s canonical output — every defaulted field, so any config change is drift; hashing the whole contract is deliberately fail-closed: a cosmetic change re-requires approval rather than risk missing a substantive one); - the git
HEADsha the plan was sealed against; - the chosen release version (the human’s bump — design §3.4);
- the resolved concrete target set — each target’s ecosystem, resolved
package name, registry, and adapter identity. Resolution overlays
facts-derived package names onto the contract’s (which may be
null), so a manifest rename is detectable drift even though the contract text is unchanged; - the phase sequence (constant per ADR-0002 §2, so it never causes drift
within a binary, but binding it authenticates the execution shape the
approver saw and makes a future phase-model change a
SEAL_VERSIONevent).
§Coordinator seam (what the sibling consumes)
The coordinator refuses a release cut --plan <id> on drift by re-deriving
current state and calling verify. It needs to persist only two plain
fields from an approved plan — plan_id and version — into its journal;
the approved ReleasePlan is otherwise reconstructed via build from
the journalled sealed inputs. The plan DTOs are therefore Serialize-only,
matching the repo-wide convention that the wire enums (Ecosystem/Registry
/Adapter) do not derive Deserialize (they collect-all-errors on parse).
The trust boundary is the local journal: an approved plan is one ossctl
itself wrote, not untrusted caller input.
§Out of this worker’s scope (handed to the coordinator)
- Working-tree cleanliness. The seal binds
HEAD, not uncommitted changes. Enforcing a clean tree / executing from a clean checkout of the sealed commit is an execution guard the coordinator owns (it needs a new read-onlyGitRepostatus port). Until then a dirty tree can publish code that differs from the sealed commit — an accepted, documented gap.
Adapter tool versions (accepted gap). ADR-0002 §3 names “resolved
adapter identities+versions”. The adapter registry (a sibling unit) is not
landed, so no adapter tool version (e.g. a pinned cargo-dist release) is
resolvable yet; today the address binds adapter identity (the enum). When
the registry lands, fold the resolved versions into the pre-image — a
deliberate schema_version-bumping change to what the address covers, never
a silent one.
Determinism: no wall-clock, no id-gen, no ordering-unstable map enters the
pre-image — identical (contract, facts, head, version) always yield the
same plan_id (proven in tests).
Structs§
- Plan
Drift - Why a
release cut --plan <plan_id>was refused: the current repo no longer hashes to the approved plan (ADR-0002 §3,plan_stale). - Version
Mismatch - One publishable target’s resolved package paired with the version its tree
manifest declares — the version the ecosystem’s publish command (
cargo publishreadingCargo.toml, …) would actually upload.
Enums§
- Version
Resolve Error - Why a single release version could not be resolved from the workspace manifest
(the single source of truth) — the reconciled superset of the old
--versiondrift guard.
Functions§
- build
- Build and seal a
ReleasePlanfrom an already-normalizedcontractand detectedfacts, at githead_sha, for the chosenversion. - compute_
plan_ id - Compute the content-addressed
plan_idfor(contract, facts, head_sha, version)without allocating a fullReleasePlan. - resolve_
release_ version - Resolve the release version from the workspace manifest — the single source of
truth — optionally confirming it against a caller-supplied
--version. - verify
- Check whether an
approvedplan still matches the current repo state.