type: adr id: adr-0091 status: accepted created: 2026-06-20 updated: 2026-06-22
ADR-0091 — corpus update: a reconcile-only kit refresh — ship --check, defer the merge
Context
The kit is adopted by copying it whole (ADR-0075), so an
adopter silently drifts from the latest kit. ADR-0081 shipped the
.agents/.corpus-version provenance pin but deferred the staleness warning, on one honest ground:
corpus check has no honest source for "latest" — a network fetch breaks its hermetic
reads-filesystem/writes-nothing posture (ADR-0077), a
pinned constant goes stale, and a local-CHANGELOG compare is circular.
That ground holds only for corpus check. corpus-works #12 asked for the full update model, and a
value panel (recorded in corpus-works FINDING-competitive-positioning-verified /
RESEARCH-feedback-signal-triage) found the offline concern is real for the hermetic check, but an
explicit, network-touching corpus update is a solved pattern (copier / cookiecutter+cruft).
corpus init already resolves the kit by cloning it (or --from), so the network already lives in
that surface — the natural home for a "latest" lookup that corpus check could not have.
The evidence is asymmetric, and this ADR scopes to it. Template creation and a CI drift-check are broadly validated; the interactive 3-way merge is the part teams are documented to abandon on first conflict ("gives up on template update"; merges fail with "no common ancestor" — Dumont 2025). For this kit the conflict case is the common case, because the kit's own onboarding tells adopters to edit the generated files (AGENTS.md, the guides).
Decision
Add corpus update as a reconcile-only verb. Ship corpus update --check; defer the merge.
corpus update --check(the drift signal, shipped). Reads the workspace's.agents/.corpus-versionpin; resolves the kit through the same source resolution ascorpus init(clone the default kit, or--from <path|url>); reads the kit'sVERSION; compares; prints whether the workspace is behind and the CHANGELOG delta. Exit 1 behind / 0 clean / 2 error. It writes nothing (reconcile-only).- The network lives in
corpus update, not incorpus check.corpus checkstays hermetic; the explicitupdateverb is where a fetch is honest. This is precisely what ADR-0081 lacked. - Defer the apply/merge. The 3-way-merge refresh (a future
corpus update --write) is held behind demonstrated demand and observed drift. Until then--write/apply returns an explicit "deferred" message — never a silent no-op — and the honest manual path stands:--checkpoints at the CHANGELOG delta, the adopter re-copies or cherry-picks the changed rules. - Scope of the reversal. This reopens ADR-0081's deferral for the explicit-update case only;
the hermetic
corpus checkstaleness warning stays deferred. - Honesty level: toolable — the named checker is corpus-cli's
corpus update --check. Nothing is enforced; a team may wire its exit code into their own CI gate (their gate, not corpus's).
Alternatives considered
| Alternative | Why weaker |
|---|---|
Put the staleness warning in corpus check | Breaks corpus check's hermeticity — the exact ADR-0081 blocker. Keep the network in the explicit update verb. |
| Build the 3-way-merge now | The precedent's merge half is the part the field abandons on first conflict (Dumont 2025), and the kit's edit-the-files onboarding makes conflict the common case — real effort on the least-used half. |
| A network registry / version service | Infrastructure corpus doesn't have, plus lock-in. copier/cruft prove local pin + a refs fetch suffice. |
| Auto-apply update (no preview) | Breaks reconcile-only — the human must own any mutation. |
Consequences
corpus update --check ships as the actionable half of #12; the .agents/.corpus-version pin
(ADR-0081) gains its second reader. ADOPTING's upgrade guidance points at it. Reconcile-only is
preserved (the verb reads + reports; it never writes or issues a verdict). The merge is a recorded
deferral with a gate (demand + drift), not a silent omission. The honest manual upgrade path is
unchanged for the once-copied adopter. corpus-cli spec + tasks live in corpus-works (specs/corpus-update/).
Update (2026-06-22) — the apply ships, but not as the 3-way merge
corpus update --write now ships (corpus-cli 4536fc7). Decision 3's deferral was specifically of the
interactive 3-way merge — the half the field abandons on first conflict (Dumont 2025) and that this
kit's edit-the-generated-files onboarding makes the common case. The shipped --write deliberately is
not that merge. It reuses the conflict-safe copy engine (init_workspace), scoped to the
kit-owned guidance only (templates/, .agents/skills/, advanced/, hooks/), with a
backup default: a customized kit file is preserved as <file>.corpus-bak and the kit's version
lands — never a line-level merge, so the "no common ancestor" failure mode never arises. The adopter's
own artifacts (board, specs, decisions, README, AGENTS.md) are out of scope by construction, which is
why this honors — rather than reverses — ADOPTING.md's "the kit never touches them" promise. skip
leaves the pin behind (a partial apply is honestly still "behind"); overwrite is the no-backup
escape hatch. The ADR's core rationale stands: corpus still does not build the merge teams abandon. The
deferral gate (demand + drift) was met by the goal-run backlog clearance; honesty level stays
toolable (the checker/applier is corpus update; nothing is enforced).
Ready to run the loop on your own repo? Get started — copy the kit and write your first spec.