Current section
Files
Jump to
Current section
Files
priv/toolchain/canon/conventions.md
# Documentation Conventions
Concrete shapes for every non-executable artifact type. Governed by
[`principles.md`](principles.md), whose vocabulary (conditional,
snapshot, co-maintained current-state, premise, source, entailment,
maintenance gradient, decay test) is assumed loaded.
## 1. ADRs (`docs/adr/`)
Cross-cutting conditional rules — both technical conventions and
value-level principles. ADRs capture *why* a rule exists; the
Application section points at code, not transcribes it.
### Format
```md
# <Rule, as a noun phrase>
Status: Accepted (YYYY-MM-DD)
## Rule
<One conditional sentence: "When X, do Y." No worldly assertions.>
## Premises
- **<name>**: <co-maintained current-state assertion about codebase,
framework, business, or design language that motivates the rule>
- ...
## Reasoning
<Logical chain from premises to rule. No conjecture; no forecasts.>
## Application
<Pointer to a worked example in the codebase. A short illustrative
snippet is acceptable when the existing call sites are too entangled
to read standalone, but the snippet exists to anchor the rule, not to
re-state every call site.>
## When this does not apply
- <conditional exception>
- ...
## Entailments
- <If applied: X follows.>
- ...
```
Each section is exclusive: Rule is conditional; Premises are
co-maintained current-state; Reasoning is timeless logic; Application
is code-anchored; exceptions are conditionals; entailments are logical
consequences, not predictions.
**Evaluability is a ratification criterion.** A Rule earns `Accepted`
only if a capable judge with the carried tooling could check it
against the tree: specific subjects, a decidable condition, no hedges
that defer the question. A rule the check repeatedly reports
`not_verifiable` is under-specified — sharpen it, or write it honestly
as a process rule and keep it out of the judged corpus. The judged
leg's reliability is a property of the CORPUS before it is a property
of the judge.
### Status
`Proposed` → `Accepted (YYYY-MM-DD)`. The date reflects the most recent
material change; git carries finer history.
**`Accepted` means the rule is ratified — nothing more.** Ratification
confers normative force (the CODEOWNERS merge); whether the code
conforms is a separate, checkable fact, recorded on the conformance
axis below and never smuggled into Status. Two accepted states:
- **`Accepted (YYYY-MM-DD)`** — ratified as the settled rule. A rule
that *accepts a residual* (a risk deliberately not eliminated) is
still this state: its Premises name the residual and its bounds, and
"When this does not apply" names the premise-change conditions that
force a re-decide. Revisit is event-driven, not obligated.
- **`Accepted (interim, YYYY-MM-DD)`** — ratified **as a stopgap**: the
team knowingly stopped short of the full requirement. The artifact
must then carry two things the settled form does not: the **full
requirement it stops short of** (named, so the delta is on the
record) and a **revisit trigger**. Revisit is obligated, not merely
event-driven; the schedule (owner, due date) lives register-side
([`registers.md` §2](registers.md#2-claim-modes)), never in the
artifact.
### Conformance (the second axis)
Whether the code implements an accepted rule is a fact about the code,
not about the decision — so it is a **trailer** after the body (beside
`Risk:`), never a Status:
```
Conformance: partial
Conformance: pending
```
Absence of the trailer asserts the rule is realized — mark the
exception, not the norm (a `Conformance: realized` line is redundant
and is removed; cf. the stub-marker logic, [§11](#11-the-stub-state-marker)).
An `Accepted` rule with `Conformance: pending` is *designed to comply,
not yet implemented*: normative and binding on new work, and honestly
not yet the running system's behaviour. The risk file's Current state
says so, and coverage claimed via `Addressed by:` is coverage
**in design**, not yet in practice. The register tracks the
implementation schedule; reconciliation checks the trailer against the
Application paths and flags drift both ways — a realized rule still
marked `pending`, or an allegedly realized rule the code contradicts.
The conformance value is derivable from the code, so `/dds-sync`
may propose it and `/dds-check` verdicts it; the *decision* to
accept a nonconforming design remains the human's.
A change to the rule is an **edit** at the same path: the new rule
replaces the prior text, and the prior form becomes context inside the
Reasoning section. For example, when *"always do X"* is refined to
*"do X except when Y"*, the Reasoning section captures the premise
that drove the exception (e.g., *"earlier this rule required X
unconditionally; refined to permit Y because the premise that motivated
universality, K, no longer holds in case Y"*). This is the same
pattern that handles rejected alternatives: the rejection is part of
the reasoning for the current rule, not a separate artifact.
Identity changes (the rule's subject materially shifts) are renames
per [§8](#8-naming-and-cross-referencing); `git log --follow` traces
across them. Full retraction with no replacement is a deletion; the
rationale lives in the deletion commit.
### Authority
Architecture team via CODEOWNERS on `docs/adr/`.
## 2. Goals (`docs/goals/`)
The extant requirements as received from outside the engineering team
— product authority, customer requests, regulatory requirements, domain
experts. A goal records the *why* a piece of system behaviour exists
(the verbatim source quote and any sourced clarifications); code is
the source of truth for *what* was actually built. The trailers
(`Realization:`, `Verified-by:`) point at that code. See
[`principles.md` §5](principles.md#5-sources-authority-and-the-requirement-as-premise).
**Not every change has a goal.** Internal refactors, dev tooling,
dependency bumps, engineering-initiated technical decisions — none of
these route here. A goal exists only when the work derives from
external authority. If the answer to *"who outside engineering
authorized this?"* is "nobody," it's not a goal: it might be an ADR
(if cross-cutting), or just a code change.
### File grouping
Goal files are grouped by **area** of the product, not by individual
goal. Multiple goals share a file:
```
docs/goals/onboarding.md
docs/goals/billing.md
docs/goals/reporting.md
```
Areas are stable as the product evolves; roles get renamed, features
come and go. A goal that genuinely spans two areas is filed under the
dominant area and may be referenced from the other.
### Format
Each `## Goal:` block carries the source quote, optionally a sourced
clarification, and trailer pointers. **No `Requirements:` list** —
code is the requirements list, and `Realization:` / `Verified-by:`
point at it. A goal without an in-file Source quote is a directive
violation: the file alone cannot be reviewed for source authenticity.
```md
# <Area> goals
<optional one-paragraph orientation>
## Goal: <role + system-mediated activity>
Source: <author>, <pointer to original artifact, YYYY-MM-DD>
> <verbatim block quote of the load-bearing ask>
Clarified by <author> on <YYYY-MM-DD>: <sourced gap-fill that
resolved ambiguity in the quote>.
Domain: docs/domain/<file>.md
ADR: docs/adr/<file>.md
Realization: <code path>
Verified-by: <test path>
```
The `Clarified by` paragraph is **selective**: include only
clarifications whose absence would cause the quote to be misread.
Implementation latitude (UX choices, alignment with sibling pages,
etc.) belongs in the code, not here.
### Worked example
```md
## Goal: Operators reconcile transactions against the provider ledger
Source: Lin Park, finance-ops standup notes 2026-03-12
> Reconciliation is taking ops the whole morning every day because
> they're flipping between the provider portal and our ledger to
> find mismatches. Can we put them in one view?
Clarified by Lin on 2026-03-14: "mismatch" means a provider record
with no matching ledger row (or vice versa), scoped to the operator's
organization — not amount-difference reconciliation, which is a
separate workstream.
Domain: docs/domain/transactions.md, docs/domain/orgs.md
Verified-by: test/reconciliation/operator_view_test.exs
```
The clarification disambiguates "mismatch" because Lin's quote alone
left two interpretations available. Sort order, cadence, click-through
paths are not goal content — they are decisions the code records.
### Sourcing
Goals are signed-off premises. If sourcing is uncertain, **ask**.
See [`principles.md` §5.1](principles.md#51-sourcing-under-uncertainty).
### Authority
Product via CODEOWNERS on `docs/goals/`.
## 3. Domain documents (`docs/domain/`)
Facts about the business the system models — the *why* a concept
exists in the form it does, and the vocabulary other documents (goals,
ADRs) lean on. Code is the source of truth for the schema, lifecycle,
and relationships; the domain document does not paraphrase those.
Concepts here are stable, and changes propagate visibly through the
rest of the system.
A domain document is an **argument, not an inventory**
([`principles.md` §1, "If it's a list, it's not a doc"](principles.md#if-its-a-list-its-not-a-doc)).
Where a concept has an enumeration behind it — assets, actions,
dependencies, each with stakes or flows — those rows live in the code
and the structured compliance feed; the document carries the
load-bearing claims about the *shape* of that enumeration, each citing
the row that proves it. Strip the recoverable rows: what remains is the
document.
### Format
```md
# Domain: <concept>
## Definition
<What X is. Tautological-by-identity. If the business shifts and the
concept materially changes, rename or remove rather than edit
silently.>
## <Business-policy section, named for the policy it captures>
<A fact that shapes the concept and is not recoverable by reading
the schema. E.g. "`archived` is reserved unused, kept for forward
compatibility." Schema/lifecycle/relationship listings are forbidden
— the code is the source of truth.>
## Authorization scope
<The conceptual division (e.g. "view-vs-act between school roles and
coaches") that goals and ADRs lean on. Not the access-control-clause
level — those live in the code.>
Codified-by: <source-path>, <other-path>
```
No file-level `Source:`: authority lives in CODEOWNERS, per-modification
provenance in git. Unusual single-fact sourcing (regulatory citation,
clinical guideline) goes in the commit that adds the fact.
### Provenance
Provenance lives in git, not in an internal History section. `git log
-- docs/domain/<file>.md` shows when each piece of the definition was
added or modified, by whom, and from what authority (via the commit's
trailers).
### Authority
Product + relevant domain expertise (legal, clinical, financial —
whichever applies) via CODEOWNERS on `docs/domain/`.
## 4. Inline code comments
Three permitted shapes — premise, invariant, pointer — applying the
same code-vs-why split as the artifact layers
([`principles.md` §1](principles.md#1-the-directive)). Everything else
is duplication, paraphrase, or smuggled assertion.
### 4.1 Existence-predicate
For code whose existence is contingent on a premise that may change.
```
# Module: synchronous job runner
#
# Premise: the application has no async job queue available.
# Consequence: jobs that must run reliably are executed inline
# by this module rather than enqueued.
```
Two fields. **No "Replacement" field** — specifying the future
migration is conjecture. The premise is the trigger for re-evaluation;
the future maintainer decides the replacement with information
unavailable at authoring time.
If a planned replacement exists as a separate artifact (RFC, issue,
ADR), link it: `See: docs/adr/<file>.md`. The pointer doesn't decay.
### 4.2 Local invariant
For invariants the code depends on but does not enforce automatically.
```
# Caller must hold the schedule lock when invoking; serialization
# is not enforced inside this function.
def reschedule(...), do: ...
```
Co-maintained current-state proximate to the code that depends on it.
Phrased as precondition or invariant, not behavior description.
### 4.3 Pointer
For non-obvious code shapes whose rationale lives elsewhere.
```
# See: docs/adr/<rule>.md
```
Cites an ADR, domain document, RFC, or commit hash. Does not paraphrase
the linked content.
### 4.4 Forbidden
- **Behavior paraphrase.** Duplicates code; delete.
- **Aspirational TODOs.** Move to issue tracker.
- **Hedged worldly assertions** (*"This function assumes X"*). Smuggles
X as fact via "assumes." Reframe as existence-predicate.
- **Contextless commentary** (*"Tricky."*). Spell out the invariant or
remove.
## 5. Commits and PRs
Primary substrate for snapshot content. Each commit is a moment; its
message is the moment's metadata.
### Workflow
A PR has two surfaces with distinct roles:
- The **PR conversation** is the cross-session persistence of working
memory: exploration, alternatives under consideration, source
material from chats and research, LLM dialogue. Anything load-bearing
to the work goes here so it survives the originating session ending —
another contributor or LLM agent can pick up the PR without depending
on the original session being live.
- The **PR commits** (post-rebase) are the distilled decisions: one
commit per decision, each with one trailer set. Not a worklog; the
rebase pass strips the worklog into the PR conversation, where it
belongs.
**Default workflow:**
1. Open the PR early. Work iteratively. Push exploration, alternatives,
source links, and LLM dialogue into the PR (description, comments,
attachments) as the work progresses — not into local notes.
2. Rebase-and-squash before review (and periodically along the way) to
consolidate the work into per-decision commits. Each carries one
trailer set summarizing what the conversation explored.
3. Review against the consolidated state. The PR conversation remains
as the durable record of the session-spanning exploration.
4. Merge: squash to main if one decision; merge with per-decision
commits preserved if multiple. Main's history is per-decision
granularity in either case.
A consolidated commit that needs more than one trailer set is too big —
split the commit before merging. A PR with too many decisions to review
in one sitting is a feature that should have been split, not an
initiative.
**Sub-PRs as escalation: initiative scale only.** When work spans
multiple features that must land coordinated (platform migration,
regulatory compliance pass, major auth refactor), each feature-level
PR can target an initiative branch instead of main. The initiative
branch merges to main with per-feature commits preserved. This is rare;
most large work is a complex feature, not an initiative, and belongs in
a single PR with multiple commits.
### Trailers
| Trailer | Content |
|---|---|
| `Original-ask:` | Verbatim quote of the request. Use leading `>` for multi-line. |
| `Considered-alternatives:` | Each as `<alternative>: rejected because <premise>`. The premise is a current-state claim about the codebase, world, or business — not a feeling. See [`principles.md` §6](principles.md#6-negative-space-conjecture-and-supersession). |
| `Out-of-scope:` | Each as `<item>: out of scope because <premise>`. Same form as Considered-alternatives. |
| `Assumes:` | Premises about codebase, framework, environment the change relies on. |
| `Optimized-for:` | What priority drove ambiguous trade-offs. |
| `Supersedes:` | Commit hash or artifact replaced. |
| `Refs:` | PR, issue, ticket, external artifact. |
| `Risk-impact:` | Where this change introduces risk-relevant surface: `<surface>: bears on <risk-stem>` (or `: no home in the spine — /dds-suggest-risks`). One line per introduction; omit when the change introduces none. |
| `Goal:` | Path to a goal document this change advances. |
| `ADR:` | Path to an ADR this change conforms to or establishes. |
### Example (illustrative)
```
Add per-organization rate limiting
Implements rate limiting at the organization level via a token-bucket
middleware applied at the request boundary. Limits are configured per
organization in the existing settings table.
Original-ask:
> we keep getting hit by clients who blast the API; can we add some
> kind of per-org throttle so one client can't degrade the service
> for others?
Considered-alternatives:
- Per-IP rate limiting: rejected because the existing API is
authenticated and per-organization is the more meaningful unit
(a single org may legitimately use many IPs).
- Token bucket vs. sliding window: token bucket chosen because the
domain accepts brief bursts up to the bucket size, which sliding
window penalizes.
Out-of-scope:
- Per-endpoint rate limiting: out of scope because the original ask
names organization-level fairness, not endpoint-level fairness.
- Surfacing limit headers in responses: out of scope because the
current consumers don't read them; revisit when a consumer asks.
Assumes: the settings table already exists per-organization and
middleware can be inserted at the request boundary.
Goal: docs/goals/operators-keep-api-fair-across-orgs.md
ADR: docs/adr/middleware-at-request-boundary.md
Refs: #N
```
### Reconstructability
Commit history is queryable by trailer:
```sh
git log --grep "^Original-ask:"
git log --grep "^Goal: docs/goals/<file>.md"
git log --grep "^Supersedes:"
```
Squash-merge workflows still apply: the merge commit's message carries
trailers consolidated from the squashed work.
### Forbidden
- Behavior paraphrase. Lead with the *why*, not the *what*.
- Forecasted migrations (Replacement is conjecture).
- Fabricated sources. If you can't quote the original ask with
confidence, ask before committing.
## 6. CODEOWNERS
Encodes source authority structurally. Replace placeholder team names
with the relevant org's teams:
```
docs/domain/ @<product-team> @<domain-experts>
docs/goals/ @<product-team>
docs/adr/ @<architecture-team>
docs/principles.md @<architecture-team>
docs/conventions.md @<architecture-team>
```
`<domain-experts>` is whichever role is authoritative for the
business domain — clinical, legal, regulatory, financial, etc.,
depending on the application. For applications without a separate
domain-expert role, `docs/domain/` is owned by the product team alone.
Code paths that materially realize a goal or implement an ADR may also
have entries routing changes to the relevant authority. Artifact-side
ownership is load-bearing; code-path ownership supplements.
## 7. Snapshots and versioning
Snapshots are claims about moments. The substrate that preserves them
is **git** — every modification is a dated, attributed event in the
repository, recoverable via `git log -p`, `git blame`, and `git log
--follow` (across renames). Snapshots in documents are freely edited,
reorganized, or pruned for readability; git carries the immutable
record.
Discipline: *the moment a snapshot describes must be accurately captured
at the time of writing*; do not retroactively rewrite a snapshot to
misrepresent the moment. Significant retractions or corrections deserve
a new dated entry rather than a silent edit, so the diff is meaningful
to a reviewer.
Versioning of standing artifacts (ADRs, goals, domain documents) is
uniform:
- **Refinement** preserving identity → edit at the same path. The
prior form, when worth recording, becomes context inside the new
text's Reasoning section (ADR) or commit message (goals, domain).
This is the standard case: most "supersession" is refinement, e.g.
*"always do X"* becoming *"do X except when Y"*, with the prior form
named and the premise that drove the exception captured.
- **Identity change** (rule, contract, or definition substantively
different) → rename per the content-naming rule (§8). `git log
--follow` traces history across the rename; inbound references are
updated as part of the same change.
- **Full retraction** (no replacement) → file is deleted; the deletion
commit's body carries the rationale. This is rare: a position
worth holding is usually worth refining rather than abandoning.
## 8. Naming and cross-referencing
Filenames are content-named, kebab-case. No leading numbers; no dates.
```
docs/adr/<rule-as-kebab-case>.md
docs/goals/<role-and-activity>.md
docs/domain/<concept>.md
```
Dates and ordering live inside artifacts, on entries genuinely about
moments. Filename renames are tracked by git when materially needed
(rare; grep-replaceable).
Cross-references use repository-relative paths and Markdown anchors:
```md
See: docs/adr/<rule>.md
See: docs/principles.md#5-sources-authority-and-the-requirement-as-premise
```
In code, a one-line pointer comment cites the artifact:
```
# See: docs/adr/<rule>.md
```
### Register targets
External registers ([`registers.md`](registers.md)) reference
artifacts by these same repository-relative paths. An artifact
referenced by a register MAY carry the reverse pointer as a trailer
line after its body:
```
Register: <system>, <row-id>
```
Renaming or deleting a register-targeted artifact entails updating
the register row, exactly as with inbound repository references; the
register's reconciliation detects the miss (a dangling pointer) when
forgotten.
## 9. Agent-context files (`AGENTS.md`, `CLAUDE.md`)
The always-loaded agent-context files carry instructions for LLM agents
working in the repo. Setting a repo up correctly means these files hold
**only what belongs there**, and nothing that belongs elsewhere.
### What belongs
- **This project's own guidance** — conventions, commands, and house
rules the team authored. Human content; owned here.
- **The dds discipline** — the carried agent instructions (installed by
`/dds-sync`).
### What does not belong: vendored rules
**Dependency-sourced rules do not belong inlined in the agent-context
file.** A *vendored rule* is guidance that (a) is not this project's own
— it comes from a dependency or framework; (b) is regenerable from a tool
or an upstream source, not hand-authored here; and (c) is situational —
relevant only when working a particular surface, yet inlined it loads on
every turn. Inlined vendored rules are the uncoupled-present-tense
failure ([`principles.md` §1](principles.md#1-the-directive)) at the
file scale: a copy of an upstream that the upstream will move out from
under, bloating every agent invocation with mostly-irrelevant text.
The tell is structural, not stack-specific: machine-managed fence markers
(e.g. `<!-- x-start -->` / `<!-- x-end -->`), large blocks of framework
idiom that name no project-specific fact, content that duplicates a
dependency's own published rules.
### Where vendored rules go: on-demand, config-driven skills
Vendored rules belong in **skills loaded on demand** — pulled in when an
agent works the relevant surface, not every turn — with **a config file
as the source of truth** for which rules are vendored, so a regenerate
step (not a hand-edit) keeps them current. The agent-context file then
links to, or simply omits, them.
Each ecosystem has its own mechanism for this; use the native one when it
exists. Known **platform hints** (extensible — absence of a hint never
blocks the rule, it only falls back to a generic on-demand skill):
| Platform (detect by) | Native mechanism |
|---|---|
| Elixir / Mix (`mix.exs`) | `usage_rules` config in `mix.exs`, compiled to skills via `mix usage_rules.sync` |
| Node (`package.json`) | the package's own published agent-skill format / a project skill dir |
| *(no hint)* | a plain on-demand skill the agent loads for that surface; config records what it carries |
### Setup vs. maintain (same rule, different posture)
This is one rule, applied by both skills per their character
([`principles.md` §5.2](principles.md#52-deriving-artifacts-from-code-and-the-isought-line)):
- **`/dds-sync` (setup)** — when it finds vendored rules inlined, the
**relocation is mechanical** (move the fenced regions into the
platform's native config + on-demand skill, or a generic skill; strip
the inlined fences) and it does it. What it does **not** decide are the
*judgments*: which lines are genuinely "ours" vs vendored when unclear,
and any **local override** of a vendored default (e.g. a project that
deliberately reverses a framework's recommended setting) — those it
**stages** as a `Proposed` ADR / a marked question for a human, never
resolves silently.
- **`/dds-sync` (ongoing)** — enforces the settled state: vendored
rules re-inlined into the agent-context file are drift it corrects;
a fenced vendored-rule region appearing in `AGENTS.md`/`CLAUDE.md` is
structurally wrong and is moved back out.
### Authority
`AGENTS.md` / `CLAUDE.md` are CODEOWNED like other artifacts. The dds
discipline section within them is canon; the surrounding project guidance
is human content (never altered by the skills, per the firmness
contract).
## 10. Recursive consistency
This document and `principles.md` follow their own conventions: rules
are conditionals; the artifact descriptions are co-maintained current-state;
git carries the snapshot record. If a contributor finds practice
inconsistent with the conventions, the conventions are the authority on
intent — the discrepancy is either drift to be corrected or a convention
to supersede via the ADR mechanism. Silent drift is itself a directive
violation; raise it explicitly.
## 11. The stub-state marker
A file written by `/dds-sync` is a **scaffold awaiting human work**,
not a finished artifact — and a reader (or tool) must be able to tell at
a glance. Every stubbed file therefore opens with a marker line,
immediately after its `#` title:
```
> 🚧 **dds-stub** — a scaffold awaiting human work; the holes (`TODO`, `needs-ratification`, `⚠`) are the worklist. Remove this line when none remain.
```
**The marker carries no count.** How many holes, and of which kind, is
already in the file — restating it in the banner would duplicate content
that can rot the moment a hole is filled or added (an uncoupled
present-tense claim; see `principles.md` §1). The count is a *query*
(grep the holes), never a maintained value. The marker asserts only the
one thing not recoverable by counting: **that this file is still a
stub.**
Properties:
- **It is a binary flag, not a measurement** — present means "has open
holes," absent means "authored." Nothing to keep accurate but its
presence.
- **It is self-clearing.** When a file's holes reach zero, the marker is
**removed** (scaffold → authored). A file with holes carries it; a file
with none does not.
- **It is structure, not content** — so `/dds-sync` owns it: sync
checks the invariant (marker present iff the file has any hole) and
fixes it by adding or removing the line. No arithmetic, no count to
reconcile — just presence. A human filling the last hole need not touch
the marker; the next maintain run removes it.
This makes the scaffold-vs-authored distinction legible (the banner),
computable (the counts), and trackable over time (markers clearing across
commits is the progress signal).
Three first-class open states live inside a stubbed file: `TODO` (a fact
or argument not yet supplied — for a governed fact, `needs-legal-review`
names the sourcing authority), `needs-ratification` (a judgment proposed
from the content, not signed off), and a `⚠` note (an ambiguity named,
not silently resolved). Each is honest structure — never a fabricated
value for a missing one.
## 12. Canon divergences
The vendored canon (this file, `principles.md`, `registers.md`,
`claim-files.md`, the layer READMEs) is verbatim upstream content —
EXCEPT inside declared divergence blocks. A local edit to canon is a
decision; like every other deviation in this discipline (residuals,
`Conformance:` trailers), it is declared, quote-pinned, and ratified —
never discovered by diffing:
```md
<!-- dds-divergence source-attribution
why: our sources live in Jira, not conversation transcripts
instead-of: "the sentence being replaced, quoted VERBATIM" -->
the local replacement text
<!-- /dds-divergence -->
```
- **Kinds** — `instead-of: "…"` with a body replaces the quoted canon
text; with an empty body it deletes it; `after: "…"` inserts the
body behind the quoted text. Exactly one anchor per block. The
whole-file form (`<!-- dds-divergence file why: … -->` as the
file's first block) declares the entire document local — the loud,
honest escape hatch.
- **The anchor is the currency pin.** It must quote the carried canon
verbatim (whitespace-normalized). When an upgrade ships canon that
rewrote the passage, the anchor dies and the divergence is *stale —
re-base or drop*: the same staleness contract as residuals, signals,
and `Held by` mappings.
- **The invariant `/dds-check` verdicts continuously**: strip the
blocks, splice the anchor quotes back, and the file must match the
carried canon. Undeclared drift — however well-meaning the edit — is
a structural finding the moment it lands.
- **`/dds-upgrade` is a computation because of this**: overwrite with
the new canon, re-apply the declared blocks, surface stale anchors
as localized conflicts. No merge base, no reconstruction of intent.
- The `why:` line is the divergence's reasoning; a substantial
divergence points at an ADR instead. The ledger of divergences
(carried in the walk) is also the upstream-contribution worklist:
a `why` that is not project-specific belongs in the framework.