An anchor is a durable pointer into source: a genesis commit, a repository-relative path, and a byte span over the blob’s stored bytes.
Anchors resolve independently of any consumer — reviews, TODO trackers, and blame overlays all reuse the same mechanism, via three named oracles that report candidates at a confidence, never a threshold.
This document defines the invariants the gix-anchor library upholds; they will be upheld (and expanded) while the project is developed.
The following requirements define the expected behavior of the anchor capture, retention, and projection targets.
|
Caution
|
This set of requirements is not yet stable, and will grow (and change) over time. |
An anchor’s identity subtree MUST record exactly the non-derivable coordinates it was captured against: the genesis commit, the repository-relative file path, and a byte span (a half-open range over the blob’s bytes exactly as git stores them, i.e. post-clean-filter), covering the whole blob for a whole-file anchor. Creating an anchor MUST validate the path against the genesis revision’s actual content; a caller MAY supply a 1-based inclusive line range as input, which MUST be canonicalized to a byte span at capture time and MUST NOT itself appear in the identity subtree. The captured blob’s object id is derivable from these coordinates by reading the genesis tree and MUST NOT appear in the identity subtree.
An anchor’s id MUST be the content hash of its identity subtree (Anchor Identity), computed through the identity normal form, and MUST NOT be computed over, or vary with, any other field. No versioned or computed material — a fingerprint, a structural descriptor, a grammar version — MUST appear in the identity subtree. Two captures of the same genesis commit, path, and byte span MUST therefore produce the same id, and changing a hint-derivation algorithm or parameter MUST NOT change the id of an anchor whose coordinates are unchanged.
An anchor is authoritative at creation and MUST NEVER be mutated afterward. The genesis commit’s id MUST NOT be kept reachable by the anchor mechanism itself (Identity Is the Hash of Non-Derivable Coordinates Only records it as data, not as a pin), so it MAY be garbage collected once nothing else keeps it reachable.
Retention and identity are independent properties: retained material is a
hint (Identity Is the Hash of Non-Derivable Coordinates Only), never part of the identity subtree, regardless
of how durable or load-bearing it becomes at read time.
A binding’s hints subtree, sibling to identity, MUST hold only
fingerprints (a named algorithm, its parameters, and its output bytes) and
structural descriptors (a grammar id and version, a node kind, and a name
path) — both fuzzy, recomputable functions of the identity coordinates and
the repository, never full copies of anchored content.
This embedding MUST keep the hint material reachable from the storing ref
— surviving force-push, branch deletion, and gc — for as long as that ref
exists, with no gc special-casing and no pinned ancestry; content
addressing makes the embed free.
This MUST NOT use a gitlink (mode 160000): a gitlink names a commit in
another repository and is not itself a reachability edge, so it would keep
nothing reachable.
Retain it inline, never let it name: a hint field MUST NOT be added to, or
derived into, the identity subtree merely because it needs to survive gc —
the two properties are orthogonal, and conflating them is the failure this
requirement exists to prevent.
Mapping an anchor onto any revision other than its own MUST be performed by
a pure function of (objects, binding, params) — an oracle — that reports
zero or more candidates, each carrying which oracle produced it and that
oracle’s own confidence.
No oracle, and no combination of oracles, MUST apply a threshold to
confidence or otherwise decide validity; that is a policy decision for the
oracles' consumer, never anchor’s.
An oracle MUST NEVER mutate the anchor it is given.
The fingerprint oracle MUST be able to report candidates using only a binding’s retained hints and the repository, independent of whether the binding’s own genesis commit still exists — recomputing a reference fingerprint from the genesis commit when the retained hint is absent, and reporting no candidates (never an error) when neither is available.
Capturing an anchor MUST support the working tree as its source: the file’s current on-disk bytes are written to the object database as a blob, and the byte span is computed against those same bytes. Such an anchor MUST record `HEAD’s commit as its genesis coordinate — the same best-effort, never-load-bearing data field Anchors Are Never Mutated already makes it.
Comparing two git trees MUST be a structural walk that reports one change per differing entry: an addition, a deletion, or a modification, each named by its full path with the base-side and tip-side object ids it carries. Either side of the pair MAY be the empty tree, so a whole-tree addition (empty base) and a whole-tree deletion (empty tip) are ordinary cases, not special ones. The walk MUST prune any subtree whose object id is equal on both sides — content addressing makes equal trees share an id, so an unchanged subtree is never descended into — making the diff’s cost proportional to the number of changed entries, never to the size of either tree. This diff requires only object reads, never a materialized worktree, so it is the substrate on which a scope version is rendered and an interdiff between two versions is computed.