ADR 0024: Link values name foreign tickets

Decision

A link is an ordinary fact under a [:link …] path, and the referent lives in the VALUE, never in the path. A value is either a bare uuid — a ticket in this store — or a map:

{:ticket #uuid "…"      ; identity
 :head   "sha256-…"     ; the head observed when the claim was made
 :store  "…"}           ; advisory: where to fetch it

A ticket uuid is the identity on its own. Uuids are globally unique by construction, so (store, ticket) over-specifies: naming a store tells a reader where to find the ticket, never which ticket it is. :store therefore carries no authority and no comparison depends on it.

:head is the reason the map exists. One head commits to the entire ancestry (ADR 0004), so a link that pins one says which version of the other ticket the claim was made against, and a reader can tell a stale link from a current one. Without it, a cross-store link is a claim about a moving target.

[:link …] is reserved. A missing fact under it means “waiting on something elsewhere”, which a lens can tell apart from “nobody here did the work yet” — the same shape, two very different answers.

Context

The cross-store gate design (docs/IDEAS.md) named this as the decision everything else waits behind, and warned that the choice is inherited by every later integration — bundles, the registry, the external tracker adapters all want to name a foreign ticket. It offered two shapes: a scoped fact path, or a map-valued fact.

The path is not available. :process/facts keys are vectors of KEYWORDS (tik.process/ProcessDef), so a uuid or a store name cannot go in one without changing what a path is everywhere — in the linter, the schemas, parse-key, and every existing definition. The value has no such constraint: it is arbitrary EDN, and tik set <id> link.depends-on=<uuid> already put the referent there. Extending that value from a bare uuid to a map is backward compatible and needs no kernel change, no new event type, and no guard vocabulary.

The trigger to revisit fired from the direction the entry predicted. A release now records itself in a store CI cannot push to and ships as an evidence bundle, so “this bug is fixed in the release recorded there” requires naming a ticket that lives somewhere else.

Consequences

see history · edit this page