Bitspark constellation
proposed source ↗

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_dispatch propagation 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|checkbitspark.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:ci vs bare ci), epic vs type: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 from atlas 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).
  • .github templates 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 contract atlas process check reads), CHANGELOG.md, all carrying one monotonic specVersion.
  • governance/ — the non-byte contracts: labels.json, ruleset.json (org-scope), cycles.json, issue-types.json, the *.canonical.md agent-doc spines, TRACKING.md.
  • workflow/scaffold/ — the vendored bytes: issue/PR templates, SECURITY.md, the .gitattributes/.gitignore managed 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.md keeps 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 in process/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 downstream layer (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.md that 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 .gitattributes is 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.

The Bitspark constellation — how the systems are built and relate.

GitHub