Skip to main content

Module target_id

Module target_id 

Source
Expand description

Stable, per-target journal ids for a release plan’s targets.

The event-sourced journal (ADR-0003) keys every per-target fact — dry_run / built / published — by a short string id, and the coordinator, the resume reconciler, and the CLI’s RunCreated.targets list must all derive that id identically or a resume looks up the wrong key (and, worst case, re-publishes an already-landed target). This module is the one place the id is derived, so those three callers cannot drift.

§Why not just the ecosystem string

Historically a cut carried at most one target per ecosystem (the normalizer expanded ecosystems 1:1), so the ecosystem wire string (rust, node, …) was itself a unique key. A contract may now declare several targets in one ecosystem — e.g. ossctl’s own two crates.io crates (ossctl-core then ossctl), plus a gh-releases and a homebrew target all under rust — so the ecosystem alone collides. journal_target_ids disambiguates only as far as it must: a lone-in-its-ecosystem target keeps the bare ecosystem id (so single-target cuts, and every existing journal, are byte-for-byte unchanged); an ecosystem with several targets qualifies each with the least of packagepackage:registrypackage:registry:adapter that makes the group’s ids distinct.

§Determinism (and its coupling to plan_id)

The ids are a pure function of the target list (which is itself the normalizer’s canonical order), computed through a BTreeMap group scan — no wall-clock, no HashMap iteration — so the same plan always yields the same ids, in the same positions. The ids are journal keys only; they are not part of the content-addressed plan_id (that hashes the target fields), so this derivation never affects plan identity or drift detection.

That exclusion is load-bearing for resume safety, and it holds only because this derivation reads exactly the target fields (ecosystem, package, registry, adapter, and their order) that plan_id also seals (crate::release::plan’s SealInput). The coordinator writes the journal keyed by these ids and resume re-derives them from the (drift-checked) plan; a matching plan_id therefore guarantees byte-identical ids, so resume looks up the same receipt the cut wrote and never re-publishes a landed target. If a future edit made this function read a field plan_id does not seal (or vice versa), two plans could share a plan_id yet key their journals differently — a silent re-publish hazard. Keep the two field sets in lockstep, and bump crate::release::plan’s SEAL_VERSION if the covered fields change.

§Id stability across contract edits (a documented non-guarantee)

A target’s id is stable for a given plan, not across contract revisions. Adding a second target to an ecosystem that previously had one flips the first target’s id on the next cut from the bare "rust" to a qualified "rust:<disc>". Old runs’ journals keep their "rust" keys forever (they are never rewritten); only new runs use the qualified form. Downstream consumers (release show --json, dashboards, log queries) must therefore not assume a per-target journal id is stable across contract edits.

Functions§

journal_target_ids
Assign a stable, unique journal id to each target in targets, returned positionally aligned with the input.