ADR 0014 — the unified development process: vendored byte-identity for identity-critical files, linted convention for locally-varying files, one drift gate
- Status: proposed
- Date: 2026-06-09
- Refines: 0001 (the charter + the rotting-convention lesson), 0005 §4 (the
repository_dispatchpropagation rails) - Relates: 0006 §5 (the fan-out), 0008 (release-on-merge), 0009 (the per-repo lock + dogfood-first discipline), 0011 (the generated dashboard)
- Tracking: atlas#453 / atlas#454 (the unified-process rollout epic)
- Normative spec:
process/SPEC.md(the mechanism; this ADR is the decision)
Every repo in the family has independently grown a development process — labels, issue/PR templates, agent docs (
AGENTS.md/CLAUDE.md),.gitattributes, branch protection — and they have drifted. Eight repos carry eight priority-label syntaxes (priority:P1,P1,p1,P1-high), three size axes (size:,size-,effort:), three duplicate Rust-SDK labels, and a worktree mandate that is byte-copied into some repos and simply absent from others. atlas already single-sources the workflow scaffold and the substrate wiring and gates their drift; it does not govern the rest of the process surface, so that surface rots exactly the way 0001 says unowned conventions rot — silently, between reads.
Context
atlas exists because copies rot silently — so the check ships centrally and runs in every
repo's CI (0001, README). That lesson is already
applied to three surfaces: the topology (registry.json + each atlas.json → generated
constellation.json), the workflow scaffold (workflow/ → atlas workflow sync|check →
bitspark.scaffold.synced), and the substrate/pin wiring (0008/0009,
the _dispatch.yml fan-out + member receivers). Each is single-sourced, fanned out, and
drift-gated.
The rest of the development process is not governed at all. An audit of the family (ontos, logos, arche, stele, thesmos, horos, design, atlas, plus the unmanaged arche-runner) found:
- Labels diverge on every axis — priority casing, the size-vs-effort axis name, the
area namespace (
area:civs bareci),epicvstype:epic, and three near-duplicate Rust-SDK labels in arche. - Agent docs (
AGENTS.md/CLAUDE.md) carry the worktree mandate in the mature repos but were deliberately excluded fromatlas workflow sync"because they carry per-repo specifics" — which left them ungoverned, free to drift or go missing (arche-runner has none, no CI, no worktree flow). .githubtemplates exist only in design; CONTRIBUTING/SECURITY exist only in design; CODEOWNERS is bespoke per repo.- Branch protection is asserted by committed JSON in two repos and unknowable in the rest; the required-context set is per-repo tribal knowledge. The intended enforcement layer is an org ruleset (#438).
The tempting fix — "vendor everything byte-for-byte, like the scaffold" — is wrong for half
these artifacts: an AGENTS.md with a per-repo component-prefix table and repo-layout
section has no single correct byte sequence, and forcing one would mean either a file
that is wrong for every repo or templating English at runtime. The opposite temptation —
"write a process doc and hope" — is the rotting convention this ADR exists to kill. The
membership test holds (this spans the whole constellation and has no single-repo owner), so
the decision lands here.
Decision
Govern the entire process surface from atlas under one rule that decides, per artifact, between vendoring and linting, propagated by the existing fan-out, enforced by one drift gate with a deliberately tiny hard-error floor.
1. The rule: identity vs shape
Is the artifact's correctness its byte-identity, or its conformance to a shape?
- Correctness IS byte-identity (a wrong character breaks a trust chain, a CI gate, or a
helper script) → vendor it byte-for-byte, riding the
workflow/scaffold +atlas workflow sync|check+bitspark.scaffold.synced. There is no locally-correct variant. - Correctness IS a shape (a file is present, a label namespace holds, a required claim
appears, a required check-context is enforced) → lint it by convention
(
atlas process check/atlas repo … check). The file lives in the repo, in the repo's voice; the checker validates its shape and tolerates sanctioned local variation.
This is the same line atlas already drew when it excluded AGENTS.md/CLAUDE.md from sync.
The decisive move is to make the excluded files governed (linted) rather than ungoverned
(hand-copied and silently divergent) — closing the gap without over-reaching into files
that legitimately vary.
| Artifact | Verdict |
|---|---|
scripts/worktree.{sh,ps1}, reusable-workflow callers/receivers |
vendored byte-identical |
issue-template forms, PULL_REQUEST_TEMPLATE.md |
vendored frame + per-repo area: overlay |
.gitattributes (LF rule), .gitignore (worktrees/atlas/secrets only) |
vendored managed-region |
SECURITY.md (incl. a credential-rotation clause) |
vendored frame + maintainer overlay |
AGENTS.md / CLAUDE.md / CONTRIBUTING.md |
linted claim-presence only |
.github/CODEOWNERS |
generated from the repo's atlas.json layer; advisory |
| labels | linted live-state + reconciled (two-phase confirm) |
| native issue-types (Bug/Feature/Task) | linted live-state via GraphQL |
| branch protection | linted live-state vs an org-ruleset contract (#438) |
We reject templating AGENTS.md prose (deriving English at runtime), and we reject
merely reporting label drift — labels get a sync writer that converges, not only a
check that complains.
2. Three homes, placement dictated by the propagation rail
scaffold-dispatch.yml fires only on push: paths: ["workflow/**"]. So the canonical
bytes that must ride the scaffold fan-out physically live under workflow/; the non-byte
contracts and the judgment live beside the topology, each with its own dispatch axis.
process/— the judgment:SPEC.md(the human authority),process.json(the machine contractatlas process checkreads),CHANGELOG.md, all carrying one monotonicspecVersion.governance/— the non-byte contracts:labels.json,ruleset.json(org-scope),cycles.json,issue-types.json, the*.canonical.mdagent-doc spines,TRACKING.md.workflow/scaffold/— the vendored bytes: issue/PR templates,SECURITY.md, the.gitattributes/.gitignoremanaged blocks, and the new member receivers.
3. A zero-dependency CLI surface, on the existing contract
New commands follow the established report-kind / code-kind contract and fold into
atlas doctor (new legs process-conformance, track-fresh). The CLI stays
zero-dependency (README): atlas process check|adopt, atlas repo labels|ruleset|codeowners|issue-types sync|check, atlas agentdocs sync|check, atlas track check|sync|list (see 0015), and atlas onboard <repo>. Every token-authed check degrades to info-skip + pass without its
credential, so member per-PR gates never need cross-repo or org-admin tokens (the
pin-degraded precedent).
4. The existing fan-out, three new axes
Bytes ride bitspark.scaffold.synced (they live under workflow/). The three non-byte
axes get their own events, registered in substrate/wiring.json and validated by the
existing allOf/if-then dispatch-payload schema: bitspark.labels.synced,
bitspark.agentdocs.synced, bitspark.process.synced. Each sender mirrors
scaffold-dispatch.yml (the scaffoldDispatchPlan subscriber model — substance and
downstream members that carry a substrate caller — not the views planner). Each member
carries a receiver that fetches the atlas CLI at the carrier SHA, converges, and opens one
coalesced governance-bump auto-merge PR (PR-level idempotency; a single governance change
that fires several events updates one shared branch, not several racing PRs). A per-member
.atlas/process.json pins the specVersion, so the pin-aware compare (#256)
judges each repo against the spec at its pin — a spec bump opens a drift PR, never reds
every member at once.
5. One gate, a tiny hard-error floor
Reusing the #310 doctrine — substrate-safety
is blocking; process-conformance is maintenance-class. A single named governance
required check per member runs the offline subset unconditionally. The hard-error floor is
deliberately tiny and unwaivable: a missing worktree firewall mandate, a missing
.gitattributes LF rule, a committed secret pattern, a broken priority-label set, a broken
manifest. Everything else is advisory — the forcing function is that deferral becomes
visible and accountable (an open bump PR + a non-conformant dashboard row + a cron-opened
issue), never a red X on an unrelated feature PR (0005 §4).
The .atlas/process.json sanctioned block can downgrade warning-grade divergences (an
extra area:* lane); it has no key capable of waiving a hard error.
6. Governance and dogfooding
A new CODEOWNERS team @bitspark/process-stewards (modeled on
@bitspark/substrate-release-captains) owns process/, governance/, workflow/scaffold/,
and adr/0014/adr/0015. Machinery/policy changes are atlas ADRs (immutable once
accepted, supersede-don't-edit). A contract edit — adding an area: label, fixing
CONTRIBUTING wording — is a governance/ PR, not an ADR. A weekly fleet-audit.yml
cron runs atlas doctor --fleet --open-issues and is the only holder of the org-admin and
cross-repo-write credentials. atlas dogfoods first (0009
discipline): atlas migrates its own effort:→size: labels and its own process check
goes green before any member is asked.
Consequences
- Process conformance becomes a generated fact, not a hoped-for convention — the same upgrade 0001/0004 made for topology and versions, now for labels, templates, agent docs, and protection.
- The right files stay in the repo's voice.
AGENTS.mdkeeps its component-prefix table and repo-layout section; only the claims (worktree mandate, squash-self-merge, foreground CI) are asserted present. No English is templated. - No new dispatcher, no new trust surface. Three event names join the one
_dispatch.yml; the receivers are#239-style member adoptions; the subscriber set is the existing scaffold planner. We reuse, we do not reinvent. - A real, bounded cost.
process/+governance/+ three receivers + ~6 CLI verbs are new surface, and the per-repo label migration touches live issues (hence the two-phase confirm inprocess/SPEC.md§3). Single-sourced and schema'd, the cost is bounded — the price of the convention being owned instead of evangelized. - Downstream members are first-class. The
downstreamlayer (schema-sanctioned, empty today) is governed exactly like substance for process purposes — which is how an unmanaged realizer like arche-runner gets CI, agent docs, and the worktree firewall it lacks.
Alternatives considered
- Vendor everything byte-for-byte (pure-maximalism). Rejected: it forces one canonical
AGENTS.md/CONTRIBUTING.mdthat is wrong for every repo, or templated English. Byte-vendoring is correct only where there is no locally-correct variant — the §1 line draws exactly that boundary. - Document the process, enforce nothing (pure-convention). Rejected as the whole answer: prose can neither converge labels nor gate a missing worktree mandate — it rots between reads, the failure this family already learned (0009 alternatives).
- Lint everything, vendor nothing. Rejected for the identity-critical files: a hand-edited
worktree helper or a CRLF in
.gitattributesis a defect with a single correct fix — reporting it without converging it is busywork. Those get a writer. - Per-repo authorship with a shared linter only. This is the linted half; it is insufficient for labels (state, not a file — needs a reconciler) and for the vendored bytes (identity).
The work that realizes this ADR
The mechanism is specified in process/SPEC.md (specVersion 1) and the
canonical contracts in governance/. It is built and rolled out under the
atlas#453 / atlas#454 epic, atlas-first and dogfood-green before any member adoption
(atlas#455). The cross-repo issue-tracking half of the rollout is governed by
ADR 0015.