Files
alkstore/docs/architecture/decisions/018-provenance-register-and-cherry-picks.md

16 KiB
Raw Permalink Blame History

ADR-018: Substrate provenance register and cherry-pick procedure

Status

Accepted (2026-10-06, Phase 1 — OQ-11's resolution, items (2) and (3); item (1) dissolved by ADR-013 §4. Fills in the register's format and the cherry-pick procedure the prior records point at: ADR-012 §1/§6, ADR-013 §3/§4, ADR-017 §2 class 4)

Context

ADR-011 fired the fork; ADR-012 designed it (fidelity posture, contract-blind boundary, port deltas, upstream-tracking stance); ADR-013 folded it into alkstore-sqlite as the src/substrate/ module subtree. Every one of those records pinned the existence of a provenance register — its location (in-tree, beside the subtree), its timing (fork-scaffold), and its initial contents (upstream identity + the delta list) — and deferred two questions to OQ-11:

  • (2) The register's format and delta-list granularity. ADR-012 §1 named a delta list but not its shape: are port deltas recorded per-commit or per-delta-class? Does each future cherry-pick get its own entry, and does that entry land at adoption time or in a deferred sweep?
  • (3) The cherry-pick procedure in practice. ADR-012 §6 said adoption is deliberate and recorded; ADR-017 §2 class 4 said cherry-picks are non-events because ADR-012 §2's contract-blind boundary makes them structurally un-contract-side. Neither says how a candidate is evaluated, applied, or recorded so the deliberate-work posture stays auditable — and ADR-013's fold moved the boundary's enforcement from the crate graph to review discipline (its accepted negative consequence), which makes the audit trail the record that review works from rather than an optional artifact.

Much of the shape is already fixed by the retained records, and this ADR composes with it rather than re-deciding it:

  • The register is a second ledger — for the substrate, not the contract. ADR-017 §1 rejected a parallel contract-version ledger under the one-normative-owner rule; the substrate is the complementary case: it is not a contract artifact, its changes are class-4 non-events (ADR-017 §2.4), and its semver carrier (the engine crate's changelog) has no versioning reason to mention them — the semver does not move for them. Upstream lineage is a fact the contract machinery cannot carry — the register is its single normative home.
  • The alksocks precedent supplies the in-tree shape. alksocks' ADR-013 there (the targeted fork) folded the forked core into src/rfc1928/ with a bundled module-level LICENSE notice, module docs carrying the fork-point statement, and git history as the fork-point record — and that shape shipped on crates.io without provenance problems (ADR-013's context cites it twice). The distinction this ADR must add: alksocks' fold retired the vendor framing — upstream is quiet, and a future fix "arrives as a hand-port" with no register apparatus. The honker fork is the other case: its fix stream is alive (the #80/#133 train is what fired the fork), and ADR-012 §6 commits to deliberate cherry-picks. Recurrent adoption needs a durable ledger; a one-shot extraction does not. Both are the same posture — the record apparatus scales with the upstream's activity.
  • The known defect class is named in the evidence base already. quality-read-honker-core.md §3 (D-1..D-7) is the re-derivation work's rationale-of-record — its entries feed the re-derivation delta entries' Why fields; the fix train the fork point inherits (4881f27, 3ab43aa, claimed_at et al. at f4e53c6) is upstream lineage, recorded under upstream identity, not a delta of ours. The scaffold work is formatting, not gathering.

Decision

1. The provenance register: alkstore-sqlite/src/substrate/PROVENANCE.md

The provenance register is a single Markdown file inside the substrate subtree — alkstore-sqlite/src/substrate/PROVENANCE.md — per the ADR-017 §1 altitude: it is substrate lineage, so it lives with the substrate; the fold's "in-tree, beside the substrate subtree" placement (ADR-013 §3) names the directory, this ADR names the file. (ADR-012 §1's provenance.md-at-the-crate-root named the pre-fold crate-root location; superseded with that §1. The uppercase filename follows the family precedent for standing-in-tree notices — the alksocks bundled LICENSE inside its forked module.)

The register carries two sections:

  1. Upstream identity (ADR-012 §1's initial contents): the fork source — upstream project, /workspace/honker reference checkout @ f4e53c6 (package node-v0.5.1-10-gf4e53c6), published-release lineage (0.5.0, 2026-08-23), license MIT OR Apache-2.0 — and the fork-point statement: the scaffold commits are the provenance line in git history, exactly alksocks' extraction-commit-window shape. This section is also where inherited lineage facts live (the fix train the fork point carries — 4881f27, 3ab43aa, claimed_at et al. — is upstream code we inherited, not a delta of ours).
  2. The delta register — §2's format; every divergence from lineage lands here including cherry-picks (their category is cherry-pick; §3's procedure writes to this section — there is no separate cherry-pick ledger). The scaffold's port deltas (ADR-012 §4, ADR-011's scope register) are its initial entries; cherry-picks are empty at scaffold, append-only forever.

The register is not itself a guard — the contract-blind boundary's guards remain the diff fence, review, and the contract suite (ADR-013 §2's re-sited enforcement); the register is the audit trail review works from. It records: what lineage this is, what we have already changed, and how each change arrived. Its duty is completeness, not authority — the ADRs remain the decisions of record.

2. Delta register format: one entry per delta, tagged by category — never per commit

Per-delta, category-tagged — one entry per distinct delta, each carrying its category tag — never one per commit. Granularity the evidence dictates: the fork's divergence arrives in bursts (the scaffold adds the port deltas at once; a future upstream fix train lands as several commits, one category), and a per-commit register would grow into a duplicate of the git log — a second ledger for the same fact, the drift ADR-017 §1 rejects. The git history of this repository is the per-commit record; the register is the semantic index over it.

Each entry pins five fields:

  • Category — one of the standing classes (kept-half port delta / re-derivation / drop / hygiene / cherry-pick), so the entry says at a glance which slice of the scope register the change touches.
  • What — the delta in one or two sentences (the read's W-numbers and D-numbers are admissible identifiers — e.g. "W-1 reconnect backoff applied").
  • Where — the module path(s) in the subtree the delta touches.
  • Why — the deciding record (ADR + section, quality-read finding, or the cherry-picked upstream issue/commit hash pair — see §3).
  • Lineage — for cherry-picks: the upstream commit hash(es) the delta carries (the 4881f27/3ab43aa pattern the evidence uses); for born deltas (port deltas, re-derivations): "ours" — no upstream counterpart exists.

The initial entries at scaffold are fixed by the retained records, not re-litigated: the ADR-011 scope register's keep / re-derive / drop tripartition as three class entries, the three watcher deltas of ADR-012 §4 (W-1, W-2, the dead-man's-switch re-death; W-3's not-actionable and W-4's keep-RW recorded where the read put them), the __alkstore_* rename, and the port discipline deltas (sync substrate, no-panics/no-unwrap hygiene). Nothing here makes new decisions — the register's section 2 simply carries what ADR-011 §3 and ADR-012 §3–§5 already decided, in the format this ADR pins, and the scaffold task inherits the list as its checklist.

Subsequent entries append only. Amending an existing entry is permitted solely to correct the record (a wrong path, a mis-cited hash) — never to change the register's story; a superseded delta is a new entry that cites the one it supersedes.

3. The cherry-pick procedure: five steps, one record

When upstream (honker-core proper — the reference checkout's lineage) ships a fix relevant to the kept half, adoption runs this procedure, whose steps make the ADR-012 §6 deliberate-work posture auditable — each step leaves a checkable artifact:

  1. Candidate identification (notice duty). Since no ambient tracking exists (ADR-012 §6), a candidate arrives by deliberate review of the reference checkout / upstream history when a reason exists (a defect observed in our fork — first stop, upstream's fix train; a fresh upstream release announcement; a routine periodic sweep at whatever cadence future maintenance wants, or none). The procedure does not create a sweep duty — it says that if a candidate arrives by any route, the following steps apply.
  2. Evaluation against the port-cost logic (ADR-012 §6): does the upstream fix touch kept-half machinery that still tracks its lineage? Does it beat our owned, tested, contract-ahead fork on maintenance rather than novelty (quality-read §7's re-adoption bar's spirit, applied per-fix)? A fix for a defect class in the re-derived machinery (the queue ops — D-1–D-3, D-5–D-7's fix classes; D-4's lock_acquire sits in the kept half and stays cherry-pick-eligible) is not adoptable — it evaluates against machinery that no longer tracks upstream, and the correct response to an observed defect in re-derived code is an owned fix under the fork's own test discipline.
  3. Application — hand-application of the upstream change against the kept half, preserving ADR-012 §3's fidelity posture (module structure and internal names survive the application where the surrounding code does; mechanical token renames stay banned), plus the family deltas the substrate always carries (no panics, no unwrap() outside tests — the port applies exactly this discipline to the inherited fixes). The application is verified against the inherited suites and the contract suite's substrate rows — the fork's version of alksocks' differential-suite guard, which is what makes "the cherry-pick preserved the inherited semantics" a testable claim rather than an assertion.
  4. Record — one delta-register entry (fields §2 pins; category cherry-pick, lineage = the upstream commit hashes, why = the evaluation's reasoning and outcome) plus one line in the engine crate's changelog — not a versioning event (ADR-017 §2 class 4: the engine's semver does not move for it), but a changelog-visible entry citing the register, so a reader auditing the crate's history between versions finds the substrate change without needing to know the register exists. This is the fold's answer to the crate-graph signal the standalone alkstore-substrate crate's version bumps would have carried.
  5. Commit — the cherry-pick commit message names the upstream commit hash(es); the register entry cites the commit. Git history and the register cross-link, and the fork point stays linear: every lineage-relative change from scaffold onward is register-entry-addressable by commit.

Recording timing: at adoption, not swept. The register entry and changelog line land in the same commit as the applied change; there is no deferred-registration window in which the delta exists but the record does not. Keeping it that way is what makes the audit trail trustworthy without enforcement tooling.

When a cherry-pick would touch the re-derived half or cross the contract boundary: it is not a cherry-pick, it is a fork change — an owned edit, evaluated by the engine's own review discipline, and (if it moves contract-visible semantics) classified under ADR-017's taxonomy like any engine change. The register records it as a born delta / maintenance entry, never as lineage adoption. The boundary between the two record types is ADR-012 §2's contract-blind boundary showing up in the maintenance posture.

4. What this does not decide

  • Sweep cadence — deliberately unset; ADR-012 §6's no-ambient posture governs (adoption is triggered, never scheduled).
  • Re-adoption of upstream as a dependency — quality-read §7's bar, unchanged and untouched here.
  • The scaffold task itself — the register's initial contents and the scaffold work items are the fork-scaffold task's body, now without open questions; per the OQ's own scoping, this resolution gates nothing and precedes no task.

Consequences

Positive

  • OQ-11's residue resolves without new machinery: the register is one Markdown file whose two sections mirror the decided facts (identity, deltas — cherry-picks riding the delta register as tagged entries), the procedure is five steps of discipline already implied by retained records, and the class-4 non-event status of substrate changes now has a paper trail.
  • The audit posture scales: scaffold-time deltas and future cherry-picks land in the same section in the same format, so a reader of the subtree in year three finds one register answering "what is this code, what differs from its lineage, and why."
  • The engine crate's changelog gains substrate visibility without gaining versioning noise — the one-line entries carry no semver movement and cannot be mistaken for contract events.

Negative (accepted)

  • The register is maintained by discipline, not tooling — nothing enforces that a substrate edit added a register entry beyond the diff fence and review (the same accepted-enforcement weakness ADR-013 §2 stated for contract-blindness; its answer here is the same: low-risk because the boundary is structural and the file is adjacent).
  • The changelog-line duty adds one small standing obligation to every cherry-pick; skipping it is a process defect, not a caught one.

References

  • OQ-11 (docs/architecture/open-questions.md) — this ADR's resolution; items (2)/§2 and (3)/§3, item (1) dissolved by ADR-013 §4.
  • ADR-011 — the fork decision and its scope register (the initial delta entries' content).
  • ADR-012 — §1/§6 (the register's naming and the upstream-tracking stance), §2 (the boundary that splits cherry-picks from fork changes), §3 (the fidelity posture the application step preserves), §4 (the port deltas).
  • ADR-013 — the fold (§3's in-tree placement; §4's reshaping of this OQ); its re-sited enforcement is the audit gap this register fills.
  • ADR-017 — §1 (the one-normative- owner rule this register's scope respects), §2 class 4 (substrate cherry-picks as non-events — the classification this procedure gives a paper trail), §4.2 (the version-stamped suite the application step verifies against).
  • docs/research/quality-read-honker-core.md — the fork calculus, the defect register (D-1..D-7), the W-1..W-4 notes, §7's re-adoption bar; the hash-citation pattern (4881f27, 3ab43aa) §2's lineage field adopts.
  • alksocks docs/architecture/decisions/011-inline-vendor.md and 013-targeted-fork.md (@ /workspace/@alkdev/alksocks) — the in-tree LICENSE/module-docs/git-history provenance shape, and the quiet-upstream contrast (no register apparatus needed there) that scopes why this ADR adds one.