Current section
Files
Jump to
Current section
Files
priv/toolchain/canon/deriving-from-code.md
# Identifying documentation from code (survey & stub)
The shared procedure for reading a repository's code and **identifying
what must be documented and decided** — then stubbing it into the right
structure. Used by `dds-sync` (survey the code; write witnessed drafts and
stubs — including re-surveying against changed code for newly-uncovered
surface). It is **domain-neutral**: the worked example below is a
security-heavy app because that is the sharpest illustration, but the
procedure surveys *whatever the code is* — the realization and guarantee
lenses of any product, security or not.
The output is **stub artifacts** in the three durable forms
(`docs/principles.md` §1) and the governance surfaces — the risk spine
(`docs/risks/`) and the counsel worklist (`docs/governance.md`) —
organized by the anchor: one subject (the product) through three lenses
— **what it is**, **how it is realized**, **how it is guaranteed**. The
*inventory* itself (data, flows, secrets, capabilities) is witnessed by
the code and, where one exists, an external register
(`docs/registers.md`) — the survey reads it to find stakes and gaps; it
does not transcribe it into prose. This procedure detects the surface
and scaffolds it; it does **not** author the substantive content.
## The one rule that separates surveying from fabrication
**Only what is derived from the code (or a verified external source) may
be filled; everything else is a marked hole.** A derivable fact — a code
path, what a mechanism *does*, which risk a thing implicates — is
filled, with the file read as evidence. A judgment or an external fact —
a premise, a classification, a lawful basis — is **left as a `TODO` /
`needs-ratification` / `⚠`**, never guessed. A stub filled with a
plausible guess is the failure mode this whole procedure exists to
prevent.
### Witnessed drafts (v0.6)
The rule's derived side now has a **witnessing procedure**
(`witnessing.md`): where the surface is witnessed mechanically (stores
by content, capabilities, flows, the processing surface, gating), the
procedure may write **evidence-cited drafts** into the claim files
(`claim-files.md`) rather than empty stubs — facets proposed, evidence quoted verbatim,
the section marked *unratified*. A draft differs from the guess this
rule forbids in three ways: it cites its witness, it is structurally
marked as awaiting ratification, and only a merged PR — never the
generator — turns it into a claim. The reservation itself is
unchanged: **lawful basis, necessity, sources, and premises are never
drafted**, with or without a witness — those cells stay holes.
Where nothing witnesses the surface (ADR premises, goals, protection
mechanisms), stubs-with-holes remain the only honest fill, exactly as
below.
## 1. Survey the code (the descriptive scan)
Read the codebase for the facts each lens documents. Each row names what
to find. **Every "what to find" is evidence for a stub, not a row to
transcribe into prose** — enumeration is the code's and the register's,
never prose (`docs/principles.md`, "If it's a list, it's not a doc").
| Surface | What to find | Stub form |
|---|---|---|
| **Authorization chokepoints** | where reads and mutations are gated (policy layers, mixins, scoped queries) — and the mechanism's *actual* semantics, not its apparent ones | ADR stub |
| **Read-path model** | what mediates access to the primary data: identity grants, tenancy, bearer capabilities, obscurity | ADR stub |
| **Credential consumption** | where external credentials enter (env, vault, settings) and what would leak them | ADR stub + risk implication (`secrets-…` class) + governance hole (custody/rotation) |
| **Unauthenticated inbound surfaces** | webhooks, callbacks, public endpoints that mutate state — and what authenticates the payload | ADR stub |
| **Protection mechanisms** | at-rest / in-transit / integrity protections actually implemented | ADR-premise hole |
| **Data stores and their content** | what data exists, where, and — decisively — **what it contains by content, not by schema** | risk implication + governance holes (`basis.*` / `retention.*` for the content class) |
| **Capabilities (the verbs)** | privileged actions whose *misuse* carries a stake — a write that corrupts, an invocation that spends a metered budget | risk implication / ADR stub |
| **Identities (the keys)** | credentials the app mints to bind a user to their data — whose stake is inherited from what they unlock | ADR stub + governance hole |
| **Third-party flows** | every vendor integration that receives data or is depended on (the code's actual integrations) | governance holes (processor instrument, `basis.*`) + ADR stub |
| **Processing activities** | the named operations on data, for a purpose — the units bases and retention hang off | governance holes (`basis.*` / `retention.*` joins) |
| **Residual risks** | weaknesses the code visibly carries (tokens unencrypted at rest, coverage-dependent gates, fail-open defaults) | `Proposed` ADR stub |
The rows lean security because protection is where surveying-from-code is
hardest and most consequential. For a product whose stakes are elsewhere,
the same scan finds *its* load-bearing surfaces (the integrity of a
calculation, the durability of a write) — the procedure is the same: read
the mechanism, record what it *does*.
## 2. Fact-check every derivable fact (the gate)
Before any fact is filled into a stub:
- **Read the mechanism's source, not its name.** A mixin called `canRun`
may skip the check client-side; an allowlist may gate the UI but not
the read path. Record what the code *does*, with the file as evidence.
- **Verify every path** that will appear in `Application:`,
`Codified-by:`, `Realization:`, or a `D` governance cell exists in the
tree.
- **Classify by content — but do not ratify.** Note what the data/action
*is* by content (free text naming individuals is personal data with no
PII field in sight), and record it as the *proposed* classification
marked `needs-ratification`. When genuinely ambiguous, mark `⚠` with
the question. The skill proposes the classification from the content;
the human ratifies it.
Delegate the mechanical half (path existence, cross-reference integrity)
to a subagent; keep the semantic half (mechanism reading) where the
judgment is.
## 3. Stub the artifacts — structure complete, content as marked holes
Apply the claim-mode mapping (`docs/registers.md` §2) to place each
finding, then write the **stub**: the full required structure, the
derivable cells filled, and every content cell an explicit hole.
- **Rules the code enforces** → ADR stubs, `Status: Proposed`. Fill the
`Rule` heading subject and the `Application:` path (derivable); leave
**Premises and Reasoning as `TODO`** — the survey can name *that* a
rule exists and *where* the code is, but the premise's stake and the
reasoning are the human's. Do not write the unflattering premise *as a
claim*; note the mechanism fact in the hole's `TODO` (e.g. "TODO:
premise — note that the gate is per-method opt-in, so coverage is the
risk") so the human has the evidence without the skill asserting it.
- **Deviations / unimplemented rules** → a genuinely `Proposed` ADR stub
*on the rule it deviates from* — the escape hatch, never a prose aside.
Stub the rule and mark the gap; do not author the resolution. Where
the deviated-from rule is already `Accepted`, the finding is a
**conformance fact**, not a new decision: propose the
`Conformance: partial`/`pending` trailer (derivable from the code —
`conventions.md` §1) and surface the drift; do not stub a duplicate
ADR.
- **The risk spine** → cluster the surveyed stakes by currency into
`docs/risks/<id>.md` files (per `docs/risks/README.md`): Stake and
Currency proposed from the survey, exclusions `TODO`, the clustering
itself `needs-ratification` — no validation question: risk files
name harms; demands live in ADRs, and managed-ness is computed. Surface every
implicated risk with **no covering ADR** as a GAP in its `Current
state`.
- **Governed facts** → holes in `docs/governance.md` (per its format):
one `basis.*` entry per distinct permitting ground and one
`retention.*` entry per lifespan policy the survey implies, each
`G · needs-legal-review`; processor-instrument and consent/notice
questions as `⚠` lines. `D` cells filled and verified, `C` cells
`needs-ratification` — never a fabricated value. The inventory rows
themselves stay with the code and the external register (where one
exists); the argument *about* the enumeration (a domain document) is
stubbed only where there is a shape-claim to make, and its prose is
left `TODO`.
- **Externally sourced obligations** → goal stubs with `Source: TODO`
(see `conventions.md` §2) and the bound activity/basis edges filled.
### The is→ought line
The survey observes regularities; it cannot mint rules, and this skill
does not even draft them. "The code does X" is a fact for a hole's
evidence, **never the reasoning** — the Reasoning section is left for a
human, because normative force comes from exactly two places, neither
conferred here: **ratification** (the CODEOWNERS merge promoting
`Proposed → Accepted`) for rules drawn from practice, and the **external
source** for obligations. Until one occurs, every stub is a proposal with
its reasoning unwritten — which is why all land `Status: Proposed`, on a
reviewed branch, with empty Premises/Reasoning, never directly normative
and never pre-argued.
The stub still **stages the destination**: which accepted state
(`conventions.md` §1) the finding is a candidate for is derivable
framing — settled `Accepted` (a clean enforced rule, or a deliberate
residual to accept-with-bounds or fix); `Accepted` +
`Conformance: partial` (the design ratifiable, the realization gap
named); or `Accepted (interim, …)` (a pragmatic stopgap or dormant
control — pre-scaffold its full-requirement and revisit-trigger
sections as `TODO` holes, and note that backing the mechanism out and
ratifying the absence as interim is also on the menu). Naming the
candidate is the survey's; choosing it is the ratification, and that
remains the human's.
## Why the code is read, not trusted
The worked instance that shaped this procedure found, in one pass over
one app: an authorization mixin whose client-side check is skipped
entirely (the server is the *sole* validator — a different risk:
coverage); an allowlist that gates the client experience but not the
content read path; and high-sensitivity personal data whose schema shows
no PII field at all. None of those facts were recoverable from existing
declarations — each came from reading the mechanism's source. A survey
that skips that reading produces stubs that are worse than none: they
carry the authority of the convention while pointing the human at the
wrong place. Read the mechanism; fill only what you read; mark the rest.