Adoption - the checkmap a repo passes to reach the standard
Adopting the standard is not a vibe or a one-shot scaffold - it is a defined, gated path from "unaligned" to "aligned + self-verifying". The gates are rigid (framework- side, in order, each with an exit criterion and a required output). What to skip, add, or sequence inside a gate is the agent's call during adaptation - recorded, not silent.
The same checkmap serves both directions:
- Greenfield - a new repo. The agent interviews to fill each gate (guided build).
- Brownfield - an existing, messy repo. The agent reads the code to fill each gate (reconcile), reconstructing what the repo already chose.
Every gate produces a document and/or tasks. The path ends with a counted backlog (how many tasks to full alignment) and a green self-verify (drift 0). Nothing is "done" on a hand-wave; each gate has an artifact you can point at.
You have this case - say this
A brand-new repo, nothing in it yet. The agent interviews you through the gates rather than scaffolding blind:
take this new repo onto the standard - interview me for what you needAn existing repo with years of history. Same path, opposite direction: the agent reads the code and reconstructs what the repo already chose, then names what is missing:
bring this repo onto the standard - read what is here first, and show me the plan before you change anythingYou want the number before you commit to the work. The path ends in a counted backlog for exactly this reason:
how far is this repo from drift 0? count the work, do not do itA gate does not fit your repo. Deviations are legitimate and recorded, never silent:
we have no separate ops repo, so the runbooks gate lands differently here - record the deviationCorner case - already adopted, the standard moved. That is an update, not an adoption: it applies the delta between your pin and latest and preserves your recorded deviations.
The gates (in order)
| # | Gate | Greenfield (interview) | Brownfield (read code) | Output | Exit criterion |
|---|---|---|---|---|---|
| 0 | Intake & description | Ask: what is this, in one sentence? Problem, why now? Plus the intake round: intent (new / align / assessment-only / check-up on an already-aligned repo / update the pin - a sixth, "stay as it is", applies to a repo with no plan to develop further), technology + Layer 2 consent (asked outright), appetite (one PR vs waves), plan-only vs execute | Scan the repo - including its own README/CONTRIBUTING for a lifecycle signal (deprecated, archived, frozen) and any machine-readable governance config (.jcheck/conf, .gitreview, CODEOWNERS) for the role, reviewer-count, tracker and review-host answers it already declares, before asking anything the repo already answered; write what it appears to be and do. Same intake round - technology detected from the repo's evidence, then confirmed, with the Layer 2 consent gathered here | PRODUCT.md draft (a description the agent can reason from) + the intake answers | The agent can state what the product is and what the user wants done |
| 1 | Personas - who | Name 3-6 user types with the user | Infer users from the code, auth roles, UI; confirm | personas.md (primary marked) + target-personas BDR | Every later gate has a persona to point at |
| 2 | Vision / Assessment | Goals, non-goals, success in 3 months | Run the 8-pass repo-assessment: what exists, what's missing, where code and intent drift | PRODUCT.md goals or docs/adoption-assessment.md (R27) | The gap between now and aligned is written down - and the user has said go or no-go on it |
| 3 | Decisions | Pick topology/stack/boundaries from the catalog | Detect what the code already chose; record it retroactively | Foundational ADRs/BDRs; the rest queued | No load-bearing fork is silently undecided |
| 4 | Capabilities & specs | Slice into capabilities (by domain, not page); write the first specs | Map existing capabilities; extract verbatim contracts (file:line), then synthesize specs | specs/<capability>/ - persona-anchored, buildable where it counts | Money/security/data paths are buildable + specced |
| 5 | Backlog - count the work | Turn unspecced capabilities + known work into items | Turn every missing spec, unrecorded decision, and known drift into items | backlog.md with a task count ("N to full alignment"), every item naming its owner role | The scope is a number, honestly stated - and each task says whose it is |
| 6 | Verify | - | - | .standards-version + manifest carried; self-verify green | drift 0; PRs opened |
Both directions run through the align-to-standards skill (in a checkout of repository-standards): its greenfield phase for a new repo, its assessment-first brownfield phase (repo-assessment passes, then align, then derive from code) for an existing one. The checkmap is the spine; the phases are how each gate gets filled.
The spine is shared and so is the walk: both directions close the gates 0 -> 2 -> 1 -> 3 -> 4 -> 5 -> 6 - Gate 2 first because personas need evidence to point at, Gate 1 right after because personas gate everything downstream. What differs is Gate 2's evidence: greenfield interviews (the vision questions ride the Gate 0 conversation), brownfield assesses - and the personas are then named from that evidence (greenfield: with the user; brownfield: reconstructed from auth roles, UI surfaces, API consumers, then confirmed with the user). The order is the order gates close; work inside a pass may interleave. Assessment-only is a legal stop: when the intake's intent is "tell me where I stand and give me the plan", the run ends after Gate 2 plus the counted plan - the health report and the number, no changes made.
Gate 0 can also end the adoption. Before anything is proposed, the intake reads the repo's own contribution policy - which a repo may state anywhere it keeps rules, AGENTS.md included, not only CONTRIBUTING.md or a file named for the purpose. A policy forbidding agent contributions is a red-flag stop, and not always the end of the run: a ban on submitting code is not a ban on reading, so the assessment above is usually still on offer. A policy mandating what this standard's own conventions forbid - an AI-attribution trailer, say - is a conflict put to the human with both obligations named, never settled silently by the merge that installs those conventions. The shapes and what each one does are in the align skill's intake (step 0).
Rigid vs. the agent's discretion
- Rigid (the framework enforces): the per-direction gate order, that each gate produces its artifact, and the exit criteria. Plainly: no specs before confirmed personas (Gate 4 waits on Gate 1 - in both directions), no recorded decisions before intake + assessment (Gate 3 waits on Gates 0 and 2), and every gate produces its artifact.
What "the framework enforces" actually means, per gate, because for a long time it meant nothing for two of them and a run could reach drift 0 having skipped both (ADR-048). Gate 0 (docs/adoption-intake.md, R26) and Gate 2 (docs/adoption-assessment.md, R27) are required manifest entries, so a missing one is drift. Gates 2 and 5 are additionally read for shape by adoption-gates: eight passes rated, the scope block's arithmetic, an owner role on every alignment item. The rest of the gate order is still enforced by reading, not by a script - which is worth knowing when deciding how much to trust a green number.
- The agent's call (during adaptation): which ADRs a repo needs, which capabilities to spec now vs. queue, what to skip (with a one-line recorded reason) and what to add beyond the defaults. Skipping is allowed; skipping silently is not.
Counting the work (Gate 5)
Adoption is incremental, never a big-bang dump - so the honest deliverable before committing is the size of the job. Gate 5 emits the backlog as a counted list:
Alignment scope for <repo> -> standard@<version>
specs to write / raise to buildable .... 12
decisions to record (ADR/BDR) ......... 5
drift to reconcile .................... 3
guards / structure to install ......... 4
---------------------------------------------
24 tasks to full alignment
That number is the go/no-go signal and the roadmap. The repo can adopt a slice (the money paths first) and leave the rest queued - the count makes that trade explicit.
Every item in the count also names its owner role - the role that must act: product/business (PRODUCT.md, BDRs, personas confirmation), architect (ADRs, boundaries), dev (specs, code, guards), agent (mechanical work it can do alone) - see the backlog format. The count then says not just how much work, but whose.
After alignment: modernize (bring the tech current)
A repo often reaches the standard because it has fallen behind - old runtimes, stale dependencies, dated patterns. Bringing it current is a separate phase that runs only after the checkmap (ADR-007), because the knowledge the gates produced is exactly what makes a migration safe.
The hard rule: understand -> record the decisions -> then refactor. Never bump first and chase the breakages - that loses behavior nobody remembered was load-bearing, and records nothing. Modernizing an undocumented repo is guessing - if the gates have not run, run them first.
Modernize: the plan-then-refactor pass
- Audit the stack (current vs. latest-stable, EOL/security), cross-referenced to the specs and ADRs that rely on each piece.
- Derive the target and the kind of each move - bump / breaking migration / replace - grounded in a reason and the affected specs, not fashion.
- Record the direction as ADR/BDR before any code moves - what, why, rejected alternatives, which specs it touches.
- Sequence small, reversible, green steps (specs + tests are the net) and emit a counted migration backlog ("N steps to current").
- State a maintenance strategy - the supply-chain cooldown, an update rhythm, and
update-to-latestfor the standard - so the repo stays current instead of rotting back.
This is distinct from update-to-latest (which bumps the standard's version); modernize bumps the repo's own technology. The refactor is the execution of a recorded plan.
Which model, and how to drive it
Adoption is judgment-heavy and whole-repo in scope - the hardest kind of agent task. Choose the driver and the shape deliberately:
- Orchestrator: the strongest model you have, at high/max thinking. The gates that carry judgment - decisions (Gate 3), spec-depth tier calls (Gate 4), assessment (Gate 2) - must be coherent across the whole repo. Divergent tier calls or contradictory ADRs are the failure mode, so one strong mind owns the synthesis. A stronger model means fewer iterations and fewer errors - if you can choose, choose up.
- No strong model? Don't worry - iterate. The transition is re-runnable and lossless by design: align is re-entrant (resume from
self-verify, what is done stays done), so every pass can only improve the repo's adoption of the standard. Weaker models need more passes and a bit more developer attention at the judgment gates - but they get the job done. The drift number falling is your progress bar either way. - Fan out the mechanical, bounded work to sub-agents. Per-capability verbatim contract extraction (read-only,
file:line), first-draft specs, backlog-item generation, and drift scans are parallel and narrow - dispatch one sub-agent per capability/area, each with a bounded context, and let the orchestrator synthesize. This is faster and keeps each context small and accurate. - Do not try to hold a large repo in one context. Map first (cheap, wide), then dispatch per-area readers, then synthesize centrally. A single giant context degrades; a map + focused readers does not.
- Budget thinking by gate. Max reasoning for Gates 2-4 (assessment, decisions, spec depth); low for mechanical extraction and formatting. Spend the thinking where a wrong call is expensive.
- Rule of thumb: one strong orchestrator + many cheap, bounded sub-agents beats one giant model doing everything, and beats many equal peers with no synthesizer - because the standard must be applied the same way everywhere, and coherence needs a center.
This is stack- and vendor-agnostic (Layer 1): it describes capability tiers ("a top reasoning model", "bounded sub-agents"), not a product. A Layer-2 setup may name the concrete models and wire the orchestration.
Pick your profile: core vs scale (ADR-011, ADR-040)
One standard, two postures - declared next to the recorded alignment state, verified per profile (previously written solo/team). The principle: core keeps knowledge alive; scale carries it to someone who is not in the room.
scale binds on reach, not on headcount. Answer yes to any one of these and the repo is scale. A second pair of hands is not one of them:
- work is handed off asynchronously - somebody picks a piece up without the person who wrote it being reachable to explain it;
- somebody contributes from outside the conversation - an outside contributor, a rotating team, a vendor, a maintainer who arrives after everyone here has gone;
- somebody outside the repo reads execution state - a stakeholder who follows a board rather than a backlog;
- the repo has a release audience that is not its authors;
- it is designed for users nobody here is.
Two people at one desk shipping to each other are core, and compliant. One person shipping a library to ten thousand strangers meets the release-audience condition and is not.
What the flag actually changes - nine entries, named rather than summarised, because the difference is small enough that a summary of it misleads:
At scale only | What it is for |
|---|---|
CONTRIBUTING.md, .github/pull_request_template.md | contribution mechanics for people who cannot be told them in person |
docs/journeys, docs/research, docs/analytics.md | designing for, and measuring, users nobody in the repo is |
docs/sprints, scripts/sprint-guard.mjs, the sprint-guard guard | a team agreeing what it committed to and by when (ADR-028) |
the spec-guard guard blocking | R11's coupling gate blocks at scale; at core the same guard runs and advises |
And what it does not change - three things the picker used to offer as discounts and one it had backwards:
- CI is not a scale thing.
.github/workflows/spec-guard.ymlis a required entry at every profile, and R16 putsself-verifyandspec-structurein CI for every repo, a solo one included. Onlyspec-guard's blocking mode is marked (scale), and the full-tree coupling audit blocks everywhere. A core repo that skips CI is at drift, not exempt. - Personas and architecture are core.
docs/personas.md(R10) anddocs/ARCHITECTURE.mdare required at both profiles. A fuller roster and C4 depth are how much you write in a file you carry either way - a judgment, not a flag, and nothing measures it. - The tracker bridge and curated release notes are required at no profile. Tracker sync is an optional per-capability extension core never reads (ADR-032); release-notes curation is not a manifest entry at all. Neither is a discount, because neither was ever charged.
Do not take this list on trust - node scripts/self-verify.mjs --profile core prints how many entries it skipped, and today the answer is 9.
Between the two - the 2-5 person repo. There is no third profile and there will not be one for team size. A repo whose answers land in the middle picks the route that leaves a record:
- declare
scaleand except the documents you do not carry - each in the manifest'sexceptionswith a reason. self-verify reports them as excepted rather than drift and keeps them in the adoption denominator, so excepting can never raise the percentage. This is the paved road for the six document entries above: an exception carries a reason, and the next person reads why. - declare
coreand carry what a condition above actually triggered - carrying more than your profile requires has never been drift. It is also not verified: nothing checks a scale artifact in acorerepo, which is the cost of this route.
The three enforcement entries cannot be excepted, and that is what actually decides it. scripts/sprint-guard.mjs and the two guards are outside the hatch by design - waiving a live check removes it rather than recording a deviation from it, so self-verify refuses the exception and says so. Declaring scale therefore means accepting that R11's coupling guard blocks every pull request where a capability's code moves without its spec. That is the one difference a small team should decide deliberately; at core the same guard runs and advises, and the full-tree coupling audit blocks either way.
Reading core as "we are only two, so none of this is for us" is the one wrong answer. The conditions ask what leaves the room, and for most pairs something already does.
The flip either way is a manifest flag plus the measured delta, not a re-adoption.
Not this
- Not a scaffold-and-leave. Emitting files without the gates skips the understanding the gates exist to force.
- Not a big-bang. Spec the first slice deeply; queue the rest with a count.
- Not silent skips. A skipped gate/decision is recorded (a backlog item or a one-line reason), never dropped.
- Not persona-free or decision-free. Gates 1 and 3 are load-bearing; downstream work validates against them.
Adopting without a repository-standards checkout
A degit of the tree alone is enough - the align skill is convenience, not a requirement. The manual path: write .standards-version with the manifest's own version, verbatim (jq -r .version standard.manifest.json > .standards-version); fill the shells (AGENTS.md, docs/PRODUCT.md, docs/personas.md, the backlog); write your first capability spec from specs/capability-spec.template.md and bind it in specs/capability-map.json; run node scripts/self-verify.mjs until drift is 0. Filled shells, not copied ones, are the point - self-verify warns on surviving placeholders.
Proving it, and staying current
A repo that follows repository-standards records the state it last aligned to in .standards-version. Self-verify is how it proves it actually meets that version - the "verify" step that runs after adopting the standard (align-to-standards), after updating it (update-to-latest), and in CI on every PR. Same pass/fail each time.
Verification has two tiers: a mechanical tier that a machine asserts, and a judgment tier a human confirms at review. Do not pretend the judgment tier is mechanical - but do not let the mechanical tier depend on a human either.
One thing the mechanical tier cannot do is judge substance, so it warns instead: a scanned file still carrying {{TOKENS}}, <markers> or a table row of ellipsis cells is reported and never counted as drift. Filled shells, not copied ones, are the point - but converting that judgment into an integer is how a number starts being gamed.
What drift 0 claims, and what it does not. It claims the repo is shaped like the standard. It does not claim the method has been used on it, and the verdict now says so rather than leaving the reader to infer it. A raw greenfield tree plus three declarative files - .standards-version, a profile key, an empty specs/capability-map.json - used to reach OK - drift 0 - 100% adopted, compliant with the standard with not one capability spec written. The number was right; the sentence was not. So when no capability spec exists, the drift-0 line carries the caveat in the same breath as the number. It is reported, never scored: the greenfield walk scaffolds the repo in step 1 and writes the first spec in step 6, and step 1 promises "empty but valid: self-verify passes". Scoring the gap would put drift 0 out of reach of an honest brand-new repo for the whole length of the interview, and a failure nobody can clear is one everybody learns to route around.
Mechanical tier - the hard gate
Run the shipped checker; it exits non-zero on any failure, so CI can gate on it:
# gate: exit 1 on any failure
node scripts/self-verify.mjs
# report only, non-gating - for local runs
node scripts/self-verify.mjs --warn
# also assert the recorded state equals a given target
node scripts/self-verify.mjs --version 1.1.20
It is manifest-driven (ADR-005). It reads standard.manifest.json, the standard describing itself as of the recorded state, and checks the repo against every entry, reporting drift as a number (how many required entries are unmet; drift 0 = compliant).
Without a manifest it measures something much smaller, and says so. The built-in fallback skeleton - a handful of checks, for repos that predate ADR-005 - used to be announced in one dim line, so an unaligned repo could print drift 4 where the shipped manifest would have said drift 14, in the same format, and nothing distinguished them. The warning and the verdict line both now name the yardstick: a run with no manifest is not a measurement of the standard, it is a measurement of five checks.
It checks:
- Version pin -
.standards-versionexists and is well-formed (x.y.z); with--version <target>it must equal that target (used right after an update to confirm the bump landed); and it must equal the manifest'sversion(a repo that recorded X carries manifest X). - Files - every
requiredmanifest file (or one of itsaltPaths) exists. - Content, where the content is the standard's own - every
copyentry carries asha256in the manifest (one hash for a file, one per member for a directory), and the local file must hash to it. Existence alone was close to no check for these: a repo could carry 19 of the 20 skills, the previous version'sSPEC.md, or a shipped guard with its policy block deleted, and still reportdrift 0. The comparison needs no network and no copy of the shipped tree, because the hashes travel in the manifest this repo carries - the one from the version it aligned to, so they describe exactly that version. CRLF is normalized to LF first, so a Windows checkout is not permanent drift.- A
copyfile this repo deliberately changed is whatexceptionsare for: record{ "kind": "content", "match": "<path>", "reason": "..." }and the difference stops being drift (see below). The message says differs from the standard's copy, never missing, so the two failures are never confused. - A directory adopted through an
altPath-.agents/skillsstanding in for.claude/skills(R22) - is a different format by design, so bytes cannot be the test. The names are checked instead: everything the standard ships must be there under its own name. A directory that merely exists at the alternate path is not a port, which is how one monorepo reported 100% adopted while carrying none of the 20 procedures. Whether each ported skill is faithful stays judgment tier.
- A
- Declared keys, where the file is adapted on purpose - a
mergeentry may namerequiredKeys: dotted paths that must be present in the merged result (JSON objects and YAML block mappings). A merge keeps what your repo already has and what the standard brings, so its bytes cannot be compared - but when the entry exists for a block inside the file, "the file exists" asserts nothing. This is how the stack layer holds its supply-chain policy: apnpm-workspace.yamlthat lostminimumReleaseAge,saveExactandenablePrePostScriptsused to pass on the filename alone. Presence only - the value is yours to choose. - Sections - every required section heading is present in its file (e.g.
AGENTS.mdmust stateAltitude). A section follows its file: where the file entry declaresaltPathsand the repo used one, the heading is read there. Reading only the primary name let a repo pass theCHANGELOG.mdentry throughdocs/CHANGELOG.mdand then fail itsUnreleasedsection with "CHANGELOG.md missing" - a drift with no legitimate way to close it. - Names, case included - the check reads directory listings rather than asking the filesystem whether a path exists, because
existsSyncis case-insensitive on macOS and Windows.readme.mdused to satisfyREADME.mdon a contributor's Mac and fail on Linux CI for the same commit, which made the answer a property of the machine that asked. - Static guards - each manifest guard with
kind: staticpasses (e.g.scripts/spec-structure.mjs,scripts/schema-pair.mjs);self-verifyskips itself to avoid recursion. A guard whose subject is absent - nodatabase/schema/, say - reports that and passes; R24 binds repos that own a database. - A guard that could not run is not a guard that failed. A missing tool is a fact about the machine; drift is a fact about the repo, and
prerequisites.mdalready says the number scores the second. They used to be the same integer: on a machine with nopnpm, the Layer 2 guardpnpm check:allexited with a barecommand not foundand printeddrift 1 - 99% adopted (78/79)- byte for byte what three real lint errors print on a compliant repo. So a guard whose prerequisites are absent is NOT RUN: reported asSKIP, counted as neither drift nor adoption, and named in the verdict line, because the one thing a skipped blocking check must never be is quiet. Prerequisites come from two places: whatever the guard entry declares -"requires": [{ "kind": "command", "match": "pnpm" }, { "kind": "path", "match": "node_modules" }]- and, for guards that declare nothing, any bare command word inrunthat is no shell builtin and resolves nowhere onPATH. Thepathkind is what keeps the check free of side effects: with a package manager present and its dependency tree absent, running the guard is what pulls hundreds of megabytes off the network, and looking first is the only way not to. Inference errs toward running the guard in every direction it can be wrong: quoted text is blanked before splitting, so a word inside an error message is never mistaken for a tool, anda || bis read as a fallback rather than two requirements. This loosened the gate - a guard failing on a missing tool used to exit 1 and now does not - soOKis dropped from the verdict whenever a check did not run. The exit code answers "does this repo comply", and a check that never started has no opinion on that; the count and the missing word are what keep it from reading as a clean bill of health.
The code<->spec coupling guard (scripts/spec-guard.mjs, kind: diff) runs in CI on the PR diff rather than in this static check - but it is part of the same gate.
Layer 2 - the stack manifest. If a stack.manifest.json sits beside the core manifest, the repo also carries a technology layer: self-verify merges the stack's entries in and counts one drift number across both layers. The stack file links the repo to its stack by the registry pointer - never by a core version (ADR-022 in the standard repo); the picks' rationale lives in the stack repo's DECISIONS.
A repo whose stacks coexist permanently - a framework beside a native engine, neither migrating to the other - carries one file per stack, stack.<technology>.manifest.json, alongside or instead of the single-stack name. Every one is read, in filename order, and they all land in the same drift number. Two stacks declaring the same path is not an error and is not this repo's to resolve, so it is reported rather than silently collapsed: the path is checked once per declaration, and the run says which two files claimed it.
Which rules the number covers. The drift number is exactly the manifest: file presence, the recorded content of copy entries, the declared keys of merge entries, required sections, static guards, plus the structure guard's checks (spec layout, personas named, no committed scaffolding warned). Rules about conduct - the same-PR spec coupling outside CI, plan/tasks removal at close, buildable substance, supersede-not-edit, cooldown discipline - are review-verified: honestly outside the number, listed in the judgment tier below. A repo can be drift 0 and still sloppy at review; the number is the floor, not the ceiling.
What the adopted percentage does not measure (ADR-038). It is a structural reading. A copy entry is compared against its recorded hash and a merge entry against its declared keys, but a fill-from-repo entry - AGENTS.md, docs/personas.md, docs/PRODUCT.md, docs/ARCHITECTURE.md, SECURITY.md, CONTRIBUTING.md and the rest the adopter authors - carries neither, and cannot: the content is yours, so there is nothing to compare it against. Those entries score on presence. Measured on a sparse repo, six files reading # Title and TODO. moved it from 21% adopted to 37% adopted with its real substance unchanged.
So the percentage answers "how much of the standard's structure is in place", never "how good is what was written". A file that reads as unfilled - nothing beyond its headings, or nothing but a TODO/TBD/WIP marker - raises a warning, never drift, and the verdict line then states that the percentage counts entries present rather than substance written. The warning deliberately detects only visibly nothing written: a terse but real SECURITY.md naming an address and a response time is complete, and a length threshold that failed it would teach adopters to pad. Whether what is written is any good is the judgment tier's call, below.
Drift as a number. Each unmet required check scores one, so drift N is a measurable distance from the standard, and an update's job is to drive it back to 0. Mostly that is one point per manifest entry, with one deliberate exception: a missing .standards-version scores two, once as the recorded state and once as the required file. That is not double counting by accident - a repo with no record has both failed to record which version it follows and failed to carry the file that says so, and it is the single most consequential thing that can be absent. A copy directory whose members moved also scores one for the entry, however many members it was, and the failure names them.
Comparing two repos' numbers is not sound, and this page used to say it was. The denominator is each repo's own manifest: its aligned version, its profile (core checks fewer entries than scale), whether it carries a stack manifest, and how many entries it has excepted. Two repos can print the same percentage against different entry lists, and a repo at --profile core can out-score a scale repo that carries more of the standard. The number is comparable against itself over time - the same repo, before and after an update - and that is the comparison retention actually needs. For a fleet, read the drift count and the exception count together with the version each repo is aligned to; do not sort on the percentage as if it were one scale. Reading a number as "exactly N missing files" is not sound either: a point can be a missing file, a changed file, an absent key or a failing guard.
The escape hatch, and its bounds
R17 says adoption adapts rather than blind-copies, so a repo may decide not to carry something the standard requires. That decision is recorded in the manifest's exceptions, which is the only thing that turns a required miss into a compliant one:
"exceptions": [
{ "kind": "file", "match": ".github/workflows/spec-guard.yml", "reason": "this repo gates on GitLab CI; the same guards run in .gitlab-ci.yml" },
{ "kind": "section", "match": "AGENTS.md#Altitude", "reason": "..." },
{ "kind": "content", "match": ".nvmrc", "reason": "this repo runs Node 22; the guards pass on it" },
{ "kind": "key", "match": ".claude/settings.json#hooks.PreToolUse", "reason": "..." }
]
filewaives the entry,sectiona required heading,contentacopyfile this repo deliberately changed,keyone declared key. Acontentmatch may be a single member inside a shipped directory (.claude/skills/adr-write/SKILL.md).- Every exception carries a
reason. One without it is drift: a recorded deviation with nothing recorded is not one, and the reason is what the next update reads before it overwrites anything. - A guard cannot be excepted. There is no
guardkind, and a guard's own script file cannot be excepted bykind: "file". A guard whose script is absent is skipped - optional guards legitimately are not installed - so excepting the script is exactly how a blocking check disappears while the run still says drift 0. Thirteenfileexceptions once took a tree with noAGENTS.md, no personas, no capability map and every guard script deleted to100% adopted (32/32). Recording that you changed a guard is fine and useskind: "content": the guard still has to run and pass. - An exception can never raise the adoption percentage. An excepted entry stays in the denominator and does not count as adopted, so excepting costs coverage rather than buying it. It is never drift, and the count is always printed in the summary line - including zero. Waiving something is a decision the number should show, not hide.
- A stale exception is reported. If the entry is met anyway, the run warns: the repo chose otherwise back and the line is now describing nothing.
- A
contentmatch may scope a subtree by ending in/**(.claude/skills/**), for a repo that rewrote a whole directory of shipped procedures and should not have to write forty lines. Onlycontent: a subtree waiver on presence would letscripts/**sweep away every guard's required-file check, which is the rule above. Each member it waives is counted, so a wide waiver costs wide coverage.
Judgment tier - confirmed at review
A machine cannot (yet) decide these; they are checked when the PR is reviewed:
- Decisions recorded - the forks in the standard's decision checklist (adopted by reference from the living standard - always latest) that apply to this repo are recorded as ADR/BDR, or consciously deferred as a backlog item - not silently undecided.
- Specs buildable where it counts - each mapped capability has a
spec; money / security / data / external-contract paths are buildable, not merely described. - No unrecorded drift - no known spec<->code contradiction is left unresolved.
- The CI gate actually fires - the manifest requires
.github/workflows/spec-guard.ymlto declareon.pull_request, and that is a key in a file, not a run. The same workflow passes the check with apaths:filter that excludes everything, disabled at the platform, or with aruns-onlabel no runner answers. What proves the gate is a recent run on a pull request;self-verifyreads the checkout and cannot see one. - The authored files say something - every
fill-from-repoentry is content you wrote, so the mechanical tier can only see that it exists and flag the ones that visibly do not (ADR-038). Whetherdocs/PRODUCT.mddescribes this product, whetherdocs/ARCHITECTURE.mdmatches the code, whetherSECURITY.mdnames a contact somebody actually reads - that is read here, at review, and it is the difference between an adopted repo and a decorated one.
Staying current - the record is a bookmark, not a lock
That file records the state this repo last aligned to. The target of every update is latest (ADR-025), so a repo does not need to be told which version it may use - it needs to be told that a newer one exists. Two ways to get that signal, both notifications, neither of them a gate:
The shipped watch workflow. Enable .github/workflows/standards-update-watch.yml. Weekly, it compares .standards-version against the standard's newest release and opens one issue per target version - not one per week - saying what to say to take the update. Before the standard publishes its first release the job says so and exits green; a watch installed early is not an error. It never edits the recorded state: an alignment that happens while nobody is looking is not an alignment.
Renovate, if the repo already runs it. A custom manager treats the recorded state like any other dependency, so the proposal arrives in the same place as every other bump:
{
"customManagers": [
{
"customType": "regex",
"managerFilePatterns": ["/^\\.standards-version$/"],
"matchStrings": ["^(?<currentValue>\\d+\\.\\d+\\.\\d+)"],
"depNameTemplate": "repository-standards/core",
"datasourceTemplate": "github-releases",
"versioningTemplate": "semver"
}
]
}
(Older Renovate calls managerFilePatterns fileMatch.)
Know what that PR is: a proposal, and only half the work. Merging a version bump on its own leaves the repo red on purpose - self-verify requires the manifest copy to match the record, and the manifest arrives with the update. Take the PR as the reminder, run update-to-latest, and let the same PR carry the delta.
When it fails
A red self-verify is a compliance failure, not a warning to defer:
- Missing
.standards-version-> the repo was never aligned; runalign-to-standards. - Version mismatch after an update -> the bump did not land; finish
update-to-latest. - A guard failure -> fix the structure/coupling before merging.
- A guard reported
SKIP-> nothing is wrong with the repo and nothing was learned about it either. Install what the line names (prerequisites.md) and re-run; CI is where that must not be tolerated, because a runner missing a tool checks less than the same command checked yesterday. - A judgment-tier gap -> record the decision, deepen the spec, or file the backlog item.
The mechanical gate belongs in CI so "compliant with the standard" is an assertion the pipeline makes, not a claim a human remembers to check.
