---
title: 'Technical Writing and Documentation'
source: 'https://academia.sh/en/courses/technical-writing'
language: en
updated: '2026-08-17T04:42:53+00:00'
license: 'CC BY-SA 4.0'
lessons: 15
outcomes:
    - 'Separates document types by audience and purpose'
    - 'Writes API reference and tutorial text with different structures'
    - 'Versions documentation alongside code and keeps it current'
---

# Technical Writing and Documentation

The value of a document is not its length but the number of claims still true; the course measures four document types by how fast they go stale, the share that goes stale silently, and whether they meet the reader's question.

## Document Types

The separation of product, developer, and support content; the separate functions of tutorial, how-to, explanation, and reference; repository documents; endpoint and error documentation layout; what release notes mean to the reader; and the symptom-cause-remedy structure.

### The Scope of Technical Writing

Source: https://academia.sh/en/courses/technical-writing/scope-of-technical-writing

A document's number is not its length but the number of claims it carries: four types carry 56 claims, the claims spread across the signature, flow, name, and concept surfaces as 22, 16, 7, and 11, 17 of them are embedded in a runnable example, and a single change on the signature surface drops 22 claims and drops 17 of them silently.

The previous course measured a review's number not by how many people looked but by how
many axes were looked at and how many chunks were read, and it drew a boundary at its
close: **review looks at what was written; writing what was not written is a separate
job.** What was measured there was whether a defect was found — the change sat on the
table, the reviewer looked at it, and the measurement was made in a single moment.

A document does not work that way. A document is true the day it is written; what is
interesting is what comes after. What this course measures is **whether what was written
is still true**, and, when it turns false, whether that **gets noticed**. This lesson
sets the unit of that measure: how many sentences of a document can be true or false,
what those sentences depend on, and who notices when the thing they depend on changes.

#### Three Content Sets

Technical writing is not a single job; its reader and its trigger form three separate
sets.

**Product content** tells someone who has never used the product what it is; its trigger
is a decision and its reader is not yet a user. **Developer content** tells someone who
will use or extend the product how it works; its trigger is a task. **Support content**
is read when something has gone wrong; its trigger is a failure, and its reader arrives
holding a symptom.

The three are not measured by the same yardstick. Product content's correctness is
usually a positioning question and has no technical oracle. This course measures
**developer content**: text made of sentences that can be said to be true or false, with
a counterpart sitting in the code. Support content enters the measure later, in its own
lesson; product content stays outside the course.

Developer content splits into four types, and the four meet the same reader at
different moments. A **tutorial** takes someone who knows nothing to a result, once. A
**how-to** takes someone with a specific task to that task's steps. An **explanation**
tells why something is the way it is. A **reference** is the line-by-line listing of
fields, options, and return values. These four names stay fixed for the whole course.

#### A Document's Number Is Not Its Length

Measuring a document by page count does not work, because length says neither what
question the reader is answering nor whether the text is true. This course's unit is the
**claim**: every sentence in a document that could be true or false is a claim. A
polished paragraph can carry zero claims; a three-line setup section can carry four.

The excerpt below is taken from a product invented for this course alone — a
measurement tool called "Measurement Station."

```text
# an excerpt from the invented product's readme
# example text, not executed

## Setup

Install the package and run the `station start` command.
The command takes the `--interval` option in seconds.
The default profile's name is `local`.
The interval sets the measurement's sampling window; the
narrower the window, the higher the measurement noise.
```

Four sentences, four claims. All four are true today. But the four are not bound to the
**same thing**, and this distinction carries the whole course.

#### The Measure's Wrong Proxies

Before moving to counting claims, three commonly used proxies need to be ruled out; all
three are easy to tally, and all three count the wrong thing.

**Length.** Page or word count measures the effort written, not its correctness. A
two-thousand-word tutorial can be entirely wrong; a ten-line section can be completely
correct. As length grows, claim count grows with it, meaning a longer document is **more
falsifiable** — an indicator pointing the wrong way.

**Last-updated stamp.** The date at the top of a document says the document was
**touched**; it does not say it was verified. A spelling fix refreshes the stamp and
leaves the forty claims underneath untouched. The reverse also holds: an explanation
untouched for a year is still entirely true if the concept it is bound to has not
changed. The stamp measures the document's age; what is being asked is the age of
**the surface it is bound to.**

**Coverage ratio.** The question "what percentage of endpoints are documented" measures
absence, not wrongness. An undocumented endpoint sends the reader to the source; a
**wrongly** documented endpoint sends the reader to the wrong place and does not say it
did. Coverage ratio can rise while correctness falls, because the two count different
things.

The three proxies share one flaw: none looks at what the document **depends on.** The
measure has to look there.

#### What a Claim Is Bound To

Every claim is bound to a **surface**: the thing its correctness is borrowed from.

**Signature** is the interface's shape — an option's name, a field's type, the
parameters a function takes. The `--interval` option taking seconds is a signature
claim. **Flow** is the order of steps: first install, then run. **Name** is what
something is called; the default profile being called `local` is a name claim.
**Concept** is why something is the way it is; noise rising as the window narrows is a
concept claim.

Surfaces do not change at the same rate, and this is the course's constant: **signature
changes once every two versions**, **flow once every three**, **name once every five**,
**concept once every twelve**. A claim goes **stale** the moment the surface it is bound
to changes — the text stays the same, its counterpart shifts.

The first result that follows is this: a sentence's lifespan is determined not by the
writer's care but by **what the sentence is bound to.** Of two sentences written with
equal attention, one lives two versions, the other lives twelve.

#### What Makes Noise and What Does Not

There is a second distinction, and it is the course's own contribution. Some claims are
**embedded in a runnable example**: they sit inside a command line, inside a code
snippet. When an embedded claim goes stale, the example fails to run — the moment
someone runs it, it **makes noise**. When a claim that is not embedded goes stale,
nothing happens: the text stays as it is, no one notices, and the reader reads the wrong
sentence believing it is true. This is called **silent staleness**.

Making noise is also bound to the surface. A concept claim can never be embedded; the
sentence "noise rises as the window narrows" is not something that can be run. A
signature claim can be embedded but does not have to be: a field type sitting in a table
row and a field type sitting inside an example call say the same thing, one is silent,
the other is not.

Who hears the noise is also fixed. The **new reader** copies the example and runs it; if
an embedded claim has gone stale, they are the first to hit the error, and most of the
time they do not know it is the document's defect — they suspect their own setup. The
**maintainer** reads the document, does not run it; they notice a silent claim is wrong
only if they read it on a subject they already know — that is, exactly where they needed
no help. This is why silent staleness persists: the person who could see it does not
look there, and the person who looks there cannot see it.

The measurement's assumptions:

- **DT1** — The invented product has four documents, of types **tutorial, how-to,
  explanation, reference**. The definitions of the four types do not change throughout
  the course.
- **DT2** — Together the four carry **56** claims. A claim is a single sentence in a
  document that could be true or false; style, headings, and links do not count as
  claims.
- **DT3** — Every claim is bound to exactly one surface: **signature**, **flow**,
  **name**, or **concept**. A sentence bound to more than one surface is counted in this
  measurement as split into two claims.
- **DT4** — Surfaces change at a fixed rate: signature once every **2** versions, flow
  every **3**, name every **5**, concept every **12**. These four numbers are not
  changed in any lesson of this course.
- **DT5** — A type's surface mix comes from how it is written: tutorial and how-to
  narrate steps, so they are flow-heavy; reference lists fields, so it is
  signature-heavy; explanation builds reasoning, so it is concept-heavy.
- **DT6** — The share of claims embedded in a runnable example varies by type: tutorial
  **0.60**, how-to **0.70**, explanation **0.10**, reference **0.00**. No claim bound to
  the concept surface can be embedded.
- **DT7** — The oracle is the setup itself: which claim is bound to which surface and
  when it will go stale is known because we wrote the setup. A real repository has no
  such knowledge, and this course's later lessons ask how to produce it.
- **DT8** — The set's resolution is **1/56**; a single claim is the smallest
  distinguishable difference in this measurement.

#### Measurement

```python
"""The composition of a document the moment it is written: 56 claims, four types, four surfaces.

Part 1 - claims per type, surface mix, and claims embedded in an example.
Part 2 - the claims a single surface change drops, and the share that stays silent.
"""
TYPES = {
    "tutorial":      {"flow": 9, "signature": 3, "name": 2, "concept": 1},
    "how-to":        {"flow": 6, "signature": 5, "name": 2, "concept": 1},
    "explanation":   {"flow": 1, "signature": 1, "name": 1, "concept": 9},
    "reference":     {"flow": 0, "signature": 13, "name": 2, "concept": 0},
}
SURFACES = ("signature", "flow", "name", "concept")
# Share of claims embedded in a runnable example: these make noise when they go stale.
EMBEDDED = {"tutorial": 0.60, "how-to": 0.70, "explanation": 0.10,
            "reference": 0.00}


def claims():
    """Each claim: its type, the surface it is bound to, whether it is embedded."""
    entries, no = [], 0
    for kind, mix in TYPES.items():
        embedded_target = 0
        for surface, count in mix.items():
            for _ in range(count):
                no += 1
                embedded_target += EMBEDDED[kind]
                embedded = embedded_target >= 1 and surface != "concept"
                if embedded:
                    embedded_target -= 1
                entries.append({"no": no, "type": kind, "surface": surface,
                                 "embedded": embedded})
    return entries


L = claims()
print(f"claims {len(L)} | types {len(TYPES)} | surfaces {len(SURFACES)} | "
      f"embedded in example {sum(i['embedded'] for i in L)}")
print()
print(f"{'type':<14s} {'claims':>6s} {'signature':>9s} {'flow':>5s} {'name':>4s} "
      f"{'concept':>7s} {'embedded':>8s} {'silent':>6s}")
for kind in TYPES:
    g = [i for i in L if i["type"] == kind]
    emb = sum(i["embedded"] for i in g)
    print(f"{kind:<14s} {len(g):6d} "
          + " ".join(f"{sum(1 for i in g if i['surface'] == s):>{w}d}"
                     for s, w in zip(SURFACES, (9, 5, 4, 7)))
          + f" {emb:8d} {len(g) - emb:6d}")
print(f"{'total':<14s} {len(L):6d} "
      + " ".join(f"{sum(1 for i in L if i['surface'] == s):>{w}d}"
                 for s, w in zip(SURFACES, (9, 5, 4, 7)))
      + f" {sum(i['embedded'] for i in L):8d} "
      f"{sum(not i['embedded'] for i in L):6d}")

print()
print("at the moment of writing (version 0) - true/stale/silent")
print("  " + " | ".join(f"{t} {len([i for i in L if i['type'] == t])}/0/0"
                        for t in TYPES))

print()
print(f"{'changed surface':<16s} {'claims drop':>11s} {'share':>6s} "
      f"{'audible':>7s} {'silent':>6s} {'silent share':>12s}")
for s in SURFACES:
    d = [i for i in L if i["surface"] == s]
    audible = sum(i["embedded"] for i in d)
    print(f"{s:<16s} {len(d):11d} {len(d) / len(L):6.4f} {audible:7d} "
          f"{len(d) - audible:6d} {(len(d) - audible) / len(d):12.4f}")
```

```
claims 56 | types 4 | surfaces 4 | embedded in example 17

type           claims signature  flow name concept embedded silent
tutorial           15         3     9    2       1        8      7
how-to             14         5     6    2       1        9      5
explanation        12         1     1    1       9        0     12
reference          15        13     0    2       0        0     15
total              56        22    16    7      11       17     39

at the moment of writing (version 0) - true/stale/silent
  tutorial 15/0/0 | how-to 14/0/0 | explanation 12/0/0 | reference 15/0/0

changed surface  claims drop  share audible silent silent share
signature                 22 0.3929       5     17       0.7727
flow                      16 0.2857       9      7       0.4375
name                       7 0.1250       3      4       0.5714
concept                   11 0.1964       0     11       1.0000
```

#### Reading the Composition

The third block gives the course's starting point: on the day it is written, all four
documents are **flawless**. All fifty-six of fifty-six claims are true, stale is **0**,
silent is **0**. The measure distinguishes nothing at this point — this is why
measuring quality at the moment of writing does not measure a document at all.

The top table carries the real information. Tutorial and reference **both carry 15
claims**; they look like two equally long documents. Their compositions are exact
opposites: **9** of the tutorial's 15 claims are bound to flow, **13** of the
reference's 15 are bound to signature. The embeddedness column separates them
completely — **8** claims are embedded in an example in the tutorial, **0** in the
reference. Reference is the one type that makes no noise at all when it goes stale, and
it does this not because it is a type, but because **it carries no example at all.**

Explanation's embedded claim count is also **0**. But the reason is different: **9** of
its claims are bound to concept, and a concept claim cannot be embedded. Two types
produce the same number of silent claims, one because it never writes an example, the
other because it cannot.

The total row reads in a single line: **17** of the embedded claims, **all 17**, belong
to the tutorial and how-to types. All the noise a four-type document set produces comes
from **two types**; the other two, carrying **27** claims between them, produce no
noise at all. This does not make those two types bad — explanation's job is reasoning,
reference's job is listing — but it does say: **a document set's maintenance warning
comes not from the whole set, but from the part that carries examples.** The bigger the
part that carries no example grows, the quieter the set becomes.

The bottom table gives the cost of a single change. **When the signature surface
changes once, 22 claims drop** — **0.3929** of the set, more than a third. Only **5** of
these make noise; **17** drop silently, meaning **0.7727** of the signature change goes
unheard. Signature is also the **fastest-changing** surface: once every two versions.
The most crowded and quietest set hangs on the most frequently changing surface.

The comparison reverses on the flow surface: **16** claims are bound to flow, **9**
make noise, and the silent share drops to **0.4375**. Flow claims narrate steps, steps
enter examples, examples break. On the concept surface, the silent share is
**1.0000** — none of the eleven claims make noise — but concept changes only once every
twelve versions, meaning these eleven claims are both the **longest-lived** and, when
they go stale, the **least visible.**

From here the course's measure takes its full shape: a document's number is not its
length, it is **the number of claims still true** — and how many of the ones that go
stale **stay silent**. You cannot read which of two documents is better maintained from
its prose quality; you can read it from its composition.

#### Summary

- Technical writing splits into three sets — product, developer, support content —
  and this course measures **developer content**, the kind that can be said to be true
  or false.
- A document's unit is the **claim**: a single sentence that can be true or false.
  The four types' documents together carry **56** claims, and length does not give this
  number.
- Every claim is bound to a **surface** — signature **22**, flow **16**, name **7**,
  concept **11** — and surfaces change at different rates: once every **2**, **3**,
  **5**, and **12** versions.
- **17** claims are embedded in a runnable example and make noise when they go stale;
  the remaining **39** go stale silently. Reference's embedded claim count is **0**, and
  so is explanation's.
- A single change on the signature surface drops **22** claims (**0.3929**) and **17**
  of those are silent (**0.7727**); the same computation on flow gives **16** claims and
  **0.4375** silence.

#### Next Step

This lesson only named the four types and counted their compositions; it did not measure
how much of each stands up over time. The next lesson runs the clock: what state are the
same four documents in two versions later? How many of the hand-written reference's
fifteen claims survive, how many of explanation's twelve does it keep, and how many
times larger does the gap between the two types turn out to be? The same lesson asks a
second question too: the reader arrives at a document with six separate questions; how
many of those questions do these four types answer?

### Tutorial, How-To, Explanation, and Reference

Source: https://academia.sh/en/courses/technical-writing/tutorial-how-to-explanation-and-reference

The four types write the same subject with separate purposes and go stale at separate speeds: a single signature change drops 13 of reference's 15 claims by version two, leaving 2, while explanation keeps 11 of 12 — a 6.875-times gap; four of six reader questions land on one type, two land on none.

The previous lesson counted the four documents at the moment they were written: **56**
claims, four surfaces, **17** embedded examples — and fifty-six of fifty-six claims
true. In that table the types were separated only by their composition, not by their
outcome, because nothing had changed yet.

This lesson asks two questions at once. The first is about purpose: why do the four
types write the same subject separately, and what is the difference between them? The
second is about lifespan: once the clock runs two versions, what is left of each? The
second question's answer will follow **directly** from the first.

#### The Four Types' Separate Purpose

The reader does not arrive at a document at random; they arrive with a situation, and
that situation determines which type they go to.

A **tutorial** takes a reader who knows nothing to a result, once. What it promises is
not learning but **getting a result**: by the end, the reader has something working in
their hands. This is why a tutorial shows one path, offers no options, and is the only
type that addresses the reader directly. A **how-to** treats the reader as someone who
already knows; they have a specific task and want the shortest path to it. What
separates it from a tutorial is not the outcome but the **entry point**: the reader
enters not from the start but from the middle.

An **explanation** is not in the middle of any task. The reader has not come to do
something but to **understand**; their question is "why is it this way," and the
answer is a reason. A **reference**, on the other hand, is not read, it is looked up.
The reader knows what they are looking for, wants to confirm a single field's type or
default, and has no time to spare for reasoning.

The excerpt below shows how the invented "Measurement Station" product's four documents
write the **same option**.

```text
# how the invented product's four types write the same option
# example text, not executed

tutorial       To take your first measurement, run
               `station start --interval 5`. A line drops every five seconds.

how-to         To change the sampling frequency, give the `--interval`
               option an integer in seconds.

explanation    Interval is the width of the sampling window. As the
               window narrows, noise in individual readings rises;
               as it widens, sudden changes get lost in the average.

reference      --interval <seconds>
               type: integer   default: 60   lower bound: 1
```

What really separates the types is not what they write but **what they do not write.**
A tutorial gives no reason — giving one would stop the reader and delay reaching the
result. A how-to text does not start from the beginning; it assumes the reader already
has the setup. An explanation gives no steps; the moment it does, a path is born that
the reader will follow, and that path is left unmaintained. A reference does not teach;
if it tries to explain what a field is for, the same reasoning ends up sitting in two
places, and the two go stale at two separate speeds. These four negative rules do more
work than four positive ones would.

All four are correct, and all four are necessary. The first cost of mixing up the types
is that the reader cannot find what they came for: a reader looking for a reason who
lands on a tutorial finds only steps; a reader with a task who lands on an explanation
reads twenty lines and finds no command. But that is not the cost this lesson measures.
What is measured is what the four types **lose** in the face of the same event.

#### Same Event, Four Separate Bills

In the excerpt above, look not at how many claims each of the four texts carries, but at
what their claims are **bound to**. The tutorial's line describes an order — flow. The
how-to line carries both the order and the option's shape. The explanation line builds a
reason — concept. Every cell of the reference line is the interface's shape: name, type,
default, lower bound. **Everything** the reference writes is signature.

Now let the product's second version ship, and let exactly one thing happen: **the
signature surface changes once.** The option's name, type, or default shifts. No one
touched the documents; all four texts still stand as they were yesterday. This single
event does not cut the same bill for the four types, and the measurement counts exactly
that.

The measurement's assumptions:

- **DT9** — The setup is the previous lesson's set: **56** claims, four types, four
  surfaces. Type definitions and surface frequencies are unchanged.
- **DT10** — The version counter runs only the surfaces. A surface changes once at
  every version that is a multiple of its frequency; signature every **2**, flow every
  **3**, name every **5**, concept every **12**.
- **DT11** — A claim is stale if the surface it is bound to has changed **at least
  once** by the measured version. Staleness is not undone: no one corrects the document
  in this measurement.
- **DT12** — The measurement builds **versions 0, 1, and 2**. Later versions are not
  built in this lesson and their numbers are not read from here.
- **DT13** — The reader has six questions, and every question lands on a single type or
  on none. The question set is the course's constant; this lesson does not expand it.
- **DT14** — A question is considered answered **if** a type carrying its answer
  exists. Whether the answer is still true is measured in a separate column; the two
  are not mixed.
- **DT15** — An answer's **confidence** is the share of that type's claims still true at
  the measured version. This is not a probability, it is the set's ratio.
- **DT16** — If two types are merged onto one page, their claims are added together and
  the reader sees the whole page as a single text; the page's confidence is the merged
  set's true share.
- **DT17** — The set's resolution is **1/56**; single-claim differences are meaningful,
  anything smaller cannot be defended with this set.

#### Measurement

```python
"""Four types, four separate lifespans: what is left at version two.

Part 1 - the true/stale/silent triple for each type at versions 0, 1, and 2.
Part 2 - six reader questions, the type that answers them, and that answer's strength at version two.
"""
TYPES = {
    "tutorial":      {"flow": 9, "signature": 3, "name": 2, "concept": 1},
    "how-to":        {"flow": 6, "signature": 5, "name": 2, "concept": 1},
    "explanation":   {"flow": 1, "signature": 1, "name": 1, "concept": 9},
    "reference":     {"flow": 0, "signature": 13, "name": 2, "concept": 0},
}
FREQUENCY = {"signature": 2, "flow": 3, "name": 5, "concept": 12}
EMBEDDED = {"tutorial": 0.60, "how-to": 0.70, "explanation": 0.10,
            "reference": 0.00}
# Question the reader brings -> answering type; None means no type answers it.
QUESTIONS = {
    "where do I start": "tutorial",
    "how do I do this": "how-to",
    "why is this the way it is": "explanation",
    "what type is this field": "reference",
    "what version did this change in": None,
    "why is this not working": None,
}


def claims():
    entries, no = [], 0
    for kind, mix in TYPES.items():
        embedded_target = 0
        for surface, count in mix.items():
            for _ in range(count):
                no += 1
                embedded_target += EMBEDDED[kind]
                embedded = embedded_target >= 1 and surface != "concept"
                if embedded:
                    embedded_target -= 1
                entries.append({"no": no, "type": kind, "surface": surface,
                                 "embedded": embedded})
    return entries


def stale(i, version):
    """Stale if the surface it is bound to has changed at least once by this version."""
    return version // FREQUENCY[i["surface"]] >= 1


def measure(version, entries):
    result = {}
    for kind in TYPES:
        group = [i for i in entries if i["type"] == kind]
        gone_stale = [i for i in group if stale(i, version)]
        silent = [i for i in gone_stale if not i["embedded"]]
        result[kind] = (len(group), len(group) - len(gone_stale),
                         len(gone_stale), len(silent))
    return result


L = claims()
print(f"claims {len(L)} | types {len(TYPES)}")
print()
print(f"{'version':>7s} {'changed surface':<16s} " +
      " ".join(f"{t:>15s}" for t in TYPES))
print(f"{'':7s} {'':16s} " + " ".join(f"{'true/stale/silent':>15s}"
                                      for _ in TYPES))
for s in (0, 1, 2):
    changed = [y for y in FREQUENCY if s // FREQUENCY[y] >= 1] or ["-"]
    d = measure(s, L)
    print(f"{s:7d} {', '.join(changed):<16s} " +
          " ".join(f"{f'{d[t][1]}/{d[t][2]}/{d[t][3]}':>15s}" for t in TYPES))

print()
d2 = measure(2, L)
print(f"{'question':<28s} {'answering type':<14s} {'version 2':>9s} {'confidence':>10s}")
for q, t in QUESTIONS.items():
    if t is None:
        print(f"{q:<28s} {'-':<14s} {'-':>9s} {'-':>10s}")
    else:
        _, true, _, _ = d2[t]
        total = d2[t][0]
        print(f"{q:<28s} {t:<14s} {f'{true}/{total}':>9s} "
              f"{true / total:10.4f}")
answered = sum(1 for t in QUESTIONS.values() if t)
print(f"reader questions {len(QUESTIONS)} | answered by one type {answered} | "
      f"answered by no type {len(QUESTIONS) - answered}")

print()
e_true, e_total = d2["explanation"][1], d2["explanation"][0]
r_true, r_total = d2["reference"][1], d2["reference"][0]
print(f"at version two: explanation {e_true}/{e_total} = {e_true / e_total:.4f}, "
      f"reference {r_true}/{r_total} = {r_true / r_total:.4f}")
print(f"lifespan ratio {e_true / e_total / (r_true / r_total):.3f}x")

print()
# Two types kept separate: what the reader sees when merged onto one page.
MERGED = ("tutorial", "reference")
print(f"{'page':<24s} {'claims':>6s} {'true':>5s} {'stale':>5s} {'confidence':>10s}")
for kind in MERGED:
    t, true, stale_n, _ = d2[kind]
    print(f"{kind + ' (separate)':<24s} {t:6d} {true:5d} {stale_n:5d} "
          f"{true / t:10.4f}")
t = sum(d2[x][0] for x in MERGED)
true = sum(d2[x][1] for x in MERGED)
print(f"{'both on one page':<24s} {t:6d} {true:5d} {t - true:5d} "
      f"{true / t:10.4f}")
```

```
claims 56 | types 4

version changed surface         tutorial          how-to     explanation       reference
                         true/stale/silent true/stale/silent true/stale/silent true/stale/silent
      0 -                         15/0/0          14/0/0          12/0/0          15/0/0
      1 -                         15/0/0          14/0/0          12/0/0          15/0/0
      2 signature                 12/3/1           9/5/2          11/1/1         2/13/13

question                     answering type version 2 confidence
where do I start             tutorial           12/15     0.8000
how do I do this             how-to              9/14     0.6429
why is this the way it is    explanation        11/12     0.9167
what type is this field      reference           2/15     0.1333
what version did this change in -                      -          -
why is this not working      -                      -          -
reader questions 6 | answered by one type 4 | answered by no type 2

at version two: explanation 11/12 = 0.9167, reference 2/15 = 0.1333
lifespan ratio 6.875x

page                     claims  true stale confidence
tutorial (separate)          15    12     3     0.8000
reference (separate)         15     2    13     0.1333
both on one page             30    14    16     0.4667
```

#### Reading Version Two

Version one costs nothing at all: all four types stand exactly as they were written.
The reason is not that the document was left unread, it is that **even the
fastest-changing surface has not moved yet.** Staleness is not a continuous erosion; it
arrives step by step, and the first step is version two.

Only one surface changes at version two: **signature**. The bills split as follows —
tutorial loses **3**, how-to **5**, explanation **1**, reference **13** claims. Same
event, four separate amounts. No one wrote one of the four texts more carelessly; all
four were written the same day, with the same care. The difference is only **what they
were bound to.**

The extremes say this in a single line. The hand-written reference loses **13** of its
fifteen claims and is left at **2/15**. Explanation keeps **11** of its twelve claims:
**11/12**. The ratios are **0.1333** and **0.9167**, and the gap between them is
**6.875 times**. The same writing effort, a lifespan more than six times as long.

The practical counterpart of this number is: **staleness speed is set not by the type
but by the surface it is bound to.** The sentence "reference goes stale fast" looks
like a property of the type, but it is not; reference goes stale fast because
**13/15** of its claims hang on the fastest-changing surface. Explanation lives long in
the same way, because **9/12** of it is bound to the slowest surface. The way to slow a
reference down is not writing more carefully, it is detaching the claim from the
signature — and there is a way to do that; the fourth lesson measures it.

The silent column says a second thing. Only **1** of the tutorial's **3** stale claims
is silent; **13** of the reference's **13** stale claims are all **13** silent. Damage
in a tutorial breaks an example, so it eventually explodes in someone's hands; damage in
a reference explodes nowhere. **The type that loses the most is the type that
advertises its loss the least.**

#### The Cost of Mixing

The reason usually given for separating the four types is that the reader finds what
they are looking for. The last table gives a second, harsher reason. When tutorial and
reference are merged onto a **single page**, the page carries **30** claims, and by
version two **16** of them are stale: confidence **0.4667**.

The number reads like this: merging does not **average** lifespans, it pulls toward the
faster one. Kept separate, a reader of the tutorial was reading a **0.8000** text; once
the same content sits on the same page as the reference, that reader is now reading a
**0.4667** page. Not a single sentence of the tutorial changed. What changed is that the
reader can no longer tell what they are looking at — the four true lines at the top of
the page and the thirteen stale lines underneath sit under the same heading, with the
same apparent confidence.

The separation is therefore not a layout preference, it is a **measurement condition**:
without splitting claims into types, how much of which section still stands cannot be
measured, and if it cannot be measured, maintenance cannot be pointed anywhere. A single
page hides which section has rotted.

#### The Taxonomy's Limit

The bottom table measures the four types from the reader's side. The reader arrives at
a document with six separate questions. Four land cleanly on one type: "where do I
start" on the tutorial, "how do I do this" on the how-to text, "why is this the way it
is" on the explanation, "what type is this field" on the reference. The count is clear:
**4 of six questions are answered by one type.**

But being answered is not the same as being answered correctly. The confidence column,
at that same version two, gives: the answer to "why is this the way it is" is
**0.9167** reliable, the answer to "what type is this field" is **0.1333**. The
reader's most certain-looking question — a field's type either is or is not something —
gets the least reliable answer. Reference is assumed reliable because it is written
with certainty; but certainty does not just leave unclear when staleness began, it
hides it.

Two questions remain, and they land on **none** of the four types: **"what version did
this change in"** and **"why is this not working."** This is not a gap, it is **the
taxonomy's limit.** The four types describe the product **as it is now**; none carries
time, which is why the first goes unanswered. All four describe the correct path; none
enters from the wrong path, which is why the second goes unanswered. Neither question
is invented out of nowhere: the table just above produced the first — a reader looking
at a **2/15** reference has to ask which claim dropped when. This course's **fifth**
and **sixth** lessons build exactly these two questions.

#### Summary

- The four types meet the reader's separate moments: tutorial delivers a result, how-to
  closes a task, explanation builds a reason, reference confirms; tutorial is the only
  type that addresses the reader directly.
- Version one costs nothing; staleness arrives in steps, and the first step is
  **version two**, because the fastest surface changes once every two versions.
- A single signature change cuts a separate bill for the four types: tutorial loses
  **3**, how-to **5**, explanation **1**, reference **13** claims.
- The hand-written reference is left at **2/15** (**0.1333**), explanation at **11/12**
  (**0.9167**); the lifespan gap is **6.875 times**. Staleness speed is set by **the
  surface bound to**, not the type.
- **13** of reference's **13** stale claims are silent; the type that loses the most
  advertises its loss the least. Merging tutorial and reference onto one page drops
  confidence from **0.8000** to **0.4667**: merging does not average lifespans, it
  pulls toward the faster one.
- **4** of six reader questions land on one type, **2** land on none: "what version did
  this change in" and "why is this not working" — that is the taxonomy's limit.

#### Next Step

The four documents measured so far described the product itself. Other documents also
sit inside a repository, and their readers are different: someone looking at the
repository for the first time, someone who wants to contribute, someone looking for why
a decision was made the way it was. The next lesson puts these documents through the
same measure — which surfaces does the readme bind to, which one the contribution
guide, which collapses in a single version, and which stays true for years?

### Repository Documents

Source: https://academia.sh/en/courses/technical-writing/repository-documents

The readme, the contribution guide, and the architecture decision record start at the same version and collapse at separate speeds: all three lose their first claim at version two, but the first two halve by version three while the decision record halves at version twelve, and ten of its twelve stale claims are silent.

The previous two lessons measured the four types around the product: tutorial, how-to,
explanation, and reference, all texts describing what the product **does**. Other
documents also sit inside a repository, and their readers are not the product's reader.
Someone looking at the repository for the first time is not asking about the product,
they are asking **what this repository is**; someone who wants to contribute is not
asking about the product, they are asking **how this place is organized**; someone
looking for a decision's reason is asking about neither the product nor the
organization, but **a judgment made in the past.**

This lesson puts three repository documents through the same measure: the readme, the
contribution guide, and the architecture decision record. The question is still the
same — which surface does each document bind to, and how much does it hold up — but this
time the answer turns into maintenance priority, because the three documents do not
deserve the same attention.

#### Three Documents, Three Reader Roles

The **readme** is the repository's door and faces the widest reader set. The questions
it answers come in order: what is this, who benefits, how is it installed, how do I get
a first result, where is the detail. The fifth question matters: the readme's job is not
to explain, it is to **direct**. Neither a tutorial nor a reference fits inside it; when
one is forced in, the mixing cost the previous lesson measured is born. This is
repository documents' most common form of decay: over time a readme turns into the
entire documentation set, because adding every new piece of information there is the
shortest path. The result is a pile of claims collected in a single file, rotting at
separate speeds; which section went wrong when can no longer be read.

The **contribution guide** faces a narrow reader: someone about to touch the code. The
questions it answers are how to set up the development environment, how to run the
tests, how to name a branch, how submitted work will be received. How a written
expectation shortens **review rounds** was measured in the Code Review and Team Process
course and is not repeated here; in this lesson the contribution guide is counted not
as a process tool but as **a text that needs maintenance.**

The **architecture decision record** faces the narrowest reader and answers a single
question: why was this decided this way? The record's structure — context, decision,
consequences — and how an architectural decision gets documented are the subject of the
Architecture Decisions and Documentation course and are not repeated here. In this
lesson the decision record is measured only by **which surfaces it binds to** and its
lifespan.

```text
# the skeleton of the invented repository's three documents
# example text, not executed

readme                    What is Measurement Station  (concept)
                          Setup, first run  (flow)
                          `station start` options  (signature)
                          Package name, directory names, links  (name)

contribution guide        Why this layout  (concept)
                          Setting up the environment, testing, submitting  (flow)
                          Test command's options  (signature)
                          Branch naming convention, tag names  (name)

architecture decision record  Context, decision, consequences  (concept)
                          The interface the decision binds  (signature)
                          Component names  (name)
```

#### What They Are Bound To

The skeleton shows the three documents' mix directly, and the three do not resemble
each other.

The readme binds to **all four surfaces at once**. Inside it, a setup flow, a command
signature, a product name, and a reasoning sentence all sit together. This makes the
readme the repository's most mixed document; it is also the hardest to maintain for
this reason, because the number of events that can break it is the highest.

The contribution guide is **flow-heavy**: most of what it writes is a sequence. Like
any text narrating steps, it collapses when the steps themselves change. Its second
weight is **name** — branch naming convention, tag names, directory names. The name
surface changes slowly, but when it does, nothing the contribution guide says throws an
error; a newcomer opens a wrongly named branch and only learns this if someone tells
them.

The architecture decision record binds almost entirely to **concept**. A decision's
context and consequence do not change because the code changes; they change only when
the decision itself is revisited. In exchange, the decision record has **nothing
runnable in it**: a decision record does not run, so it makes no noise when it goes
wrong either.

#### The Cheapness of Pointing

The readme's fifth question — where is the detail — is not as innocent as the first
four, because its answer is in the writer's hands. The same information can be placed
two ways: by **narrating** it or by **pointing** at it.

Take the setup section as an example. Dumping every option into the readme means
carrying a pile of signature claims there; signature changes once every two versions.
If the same section is passed with a single pointer — "all options are in the
reference" — what is carried drops to a single **name** claim, and name changes once
every five versions, two and a half times slower than signature. No information is
lost, it is just written once, and the readme binds to it.

This has a cost too: link targets also go stale, and when a link breaks, the reader
finds nothing. But the two defects are not equal. A broken link is a defect the reader
**notices**; a wrongly listed option is a defect the reader does not notice. Writing the
readme to point rather than narrate turns a silent defect into a loud one — the same
trade-off will come up a few more times throughout the course.

#### The Superseded Record

The architecture decision record has a trait invisible in the measurement. The other
two documents get **corrected** when they go wrong; the decision record does not get
corrected when it goes wrong, it gets **superseded**. The record itself stays as it is,
and a new record declares it invalid.

This does not remove the measure, but it changes what the result means. If a record's
concept claims are wrong for today's system, that record is stale — the table says this
with **0/10** at version twelve. But if the record is read as history, its claims are
still true for the system **as it was at the time they were given.** A single field
carries the difference: the record's status. If the status is written, the reader knows
whether the text they are looking at describes today or the past; if it is not written,
they read a ten-year-old reasoning as if it were current.

This is the one correction form seen throughout the course that does not change the
text: **one way to silence silent staleness is not to fix the claim, but to write down
its scope.** Saying which version a sentence is true for takes it out of wrongness and
moves it into history.

The measurement's assumptions:

- **DT18** — The setup has three repository documents: readme, contribution guide, and
  architecture decision record. Other documents a repository might have are outside this
  measurement.
- **DT19** — Surfaces and their frequencies are the same as the previous lessons':
  signature every **2**, flow every **3**, name every **5**, concept every **12**
  versions. These four numbers are not changed.
- **DT20** — A document's surface mix comes from how it is written and is as shown in
  the skeleton; the mix is held constant throughout the measurement.
- **DT21** — The readme carries **12**, the contribution guide **12**, the architecture
  decision record **10** claims. The first two documents carrying equal claim counts is
  deliberate: so the difference does not come from length.
- **DT22** — The share of claims embedded in a runnable example is **0.50** in the
  readme, **0.40** in the contribution guide, **0.00** in the architecture decision
  record. A claim bound to concept cannot be embedded.
- **DT23** — A claim is stale if the surface it is bound to has changed at least once
  by the measured version, and no one corrects it.
- **DT24** — **First-loss version** is the version at which a document produces its
  first stale claim. **Half-life version** is the first version at which the share of
  claims still true drops below **0.5**.
- **DT25** — The measurement builds versions **0, 2, 3, 5, 6, and 12**; these versions
  were chosen to line up with the surface frequencies' thresholds.
- **DT26** — The set's resolution is **1/12**, **1/12**, and **1/10** per document;
  differences below one claim cannot be defended with this set.

#### Measurement

```python
"""Repository documents: each document's surface mix and lifespan.

Part 1 - the three documents' surface mix and embedded claim count.
Part 2 - true/stale/silent across versions, first-loss version, and half-life version.
"""
FREQUENCY = {"signature": 2, "flow": 3, "name": 5, "concept": 12}
VERSIONS = (0, 2, 3, 5, 6, 12)
# Three repository documents: surface mix in reading order.
DOCUMENTS = {
    "readme":                       {"concept": 2, "flow": 4, "signature": 3, "name": 3},
    "contribution guide":           {"concept": 1, "flow": 6, "signature": 2, "name": 3},
    "architecture decision record": {"concept": 7, "flow": 0, "signature": 1, "name": 2},
}
# Share of claims embedded in a runnable example.
EMBEDDED = {"readme": 0.50, "contribution guide": 0.40,
            "architecture decision record": 0.00}


def claims():
    entries, no = [], 0
    for document, mix in DOCUMENTS.items():
        embedded_target = 0
        for surface, count in mix.items():
            for _ in range(count):
                no += 1
                embedded_target += EMBEDDED[document]
                embedded = embedded_target >= 1 and surface != "concept"
                if embedded:
                    embedded_target -= 1
                entries.append({"no": no, "document": document, "surface": surface,
                                 "embedded": embedded})
    return entries


def stale(i, version):
    return version // FREQUENCY[i["surface"]] >= 1


def measure(document, version, entries):
    group = [i for i in entries if i["document"] == document]
    gone_stale = [i for i in group if stale(i, version)]
    silent = [i for i in gone_stale if not i["embedded"]]
    return len(group), len(group) - len(gone_stale), len(gone_stale), len(silent)


L = claims()
print(f"documents {len(DOCUMENTS)} | claims {len(L)} | "
      f"embedded in example {sum(i['embedded'] for i in L)}")
print()
print(f"{'document':<28s} {'claims':>6s} {'concept':>7s} {'flow':>5s} {'signature':>9s} "
      f"{'name':>4s} {'embedded':>8s}")
for b in DOCUMENTS:
    g = [i for i in L if i["document"] == b]
    print(f"{b:<28s} {len(g):6d} "
          + " ".join(f"{sum(1 for i in g if i['surface'] == y):>{w}d}"
                     for y, w in zip(("concept", "flow", "signature", "name"),
                                     (7, 5, 9, 4)))
          + f" {sum(i['embedded'] for i in g):8d}")

print()
print(f"{'version':>7s} " + " ".join(f"{b:>28s}" for b in DOCUMENTS))
print(f"{'':7s} " + " ".join(f"{'true/stale/silent':>28s}" for _ in DOCUMENTS))
for s in VERSIONS:
    print(f"{s:7d} " + " ".join(
        f"{'{}/{}/{}'.format(*measure(b, s, L)[1:]):>28s}" for b in DOCUMENTS))

print()
print(f"{'document':<28s} {'first loss':>10s} {'half-life':>10s} "
      f"{'true at v12':>12s} {'silent':>6s}")
for b in DOCUMENTS:
    total = measure(b, 0, L)[0]
    first = next(s for s in range(1, 13) if measure(b, s, L)[2] > 0)
    half = next((s for s in range(1, 13) if measure(b, s, L)[1] / total < 0.5), 0)
    _, true, _, silent = measure(b, 12, L)
    print(f"{b:<28s} {first:10d} {half:10d} {f'{true}/{total}':>12s} "
          f"{silent:6d}")
```

```
documents 3 | claims 34 | embedded in example 10

document                     claims concept  flow signature name embedded
readme                           12       2     4         3    3        6
contribution guide               12       1     6         2    3        4
architecture decision record     10       7     0         1    2        0

version                       readme           contribution guide architecture decision record
                   true/stale/silent            true/stale/silent            true/stale/silent
      0                       12/0/0                       12/0/0                       10/0/0
      2                        9/3/2                       10/2/1                        9/1/1
      3                        5/7/3                        4/8/5                        9/1/1
      5                       2/10/4                       1/11/7                        7/3/3
      6                       2/10/4                       1/11/7                        7/3/3
     12                       0/12/6                       0/12/8                      0/10/10

document                     first loss  half-life  true at v12 silent
readme                                2          3         0/12      6
contribution guide                    2          3         0/12      8
architecture decision record          2         12         0/10     10
```

#### Reading the Lifespans

The bottom table's first column closes off a trap: **first-loss version is 2 in all
three documents.** All three contain at least one signature claim, so all three give
their first loss at the same moment. The moment a document "starts aging" distinguishes
nothing; every document starts aging at the same time.

What distinguishes them is the **slope**. Half-life version is **3** for the readme,
**3** for the contribution guide, **12** for the architecture decision record. The first
two lose half of themselves within three versions, while the decision record still
stands at **9/10** at that same point. The fourfold gap comes not from writing quality
but from the mix: **7** of the decision record's claims are bound to concept, and
concept changes only once every twelve versions.

The version-five row is the table's best summary. There, the readme gives **2/10/4**,
the contribution guide **1/11/7**, the decision record **7/3/3**. Three documents,
written the same day in the same repository; five versions later, one still has seven
of ten standing, the others one in twelve. If the maintenance budget is split evenly,
two-thirds of it is wasted — **maintenance assigned to the decision record does four
times less work than the same maintenance assigned to the readme.**

The silent column flips this trade-off around. By version twelve, all three documents
are entirely stale; but **6** of the readme's **12** stale claims are silent, **8** of
the contribution guide's **12**, and **10** of the decision record's **10** are all
silent. The longest-lived document is the one whose decay is least heard. When the
readme's setup section breaks, every new reader sees it instantly; no one sees that the
decision record is now invalid, because the decision record is never run — and a new
decision gets made resting on it.

The version-three row also gives one last detail: the contribution guide's special
case. There the readme gives **5/7/3**, the contribution guide **4/8/5**. The flow
surface changed exactly once and took six of the guide's claims with it at once; the
guide collapsed **faster** than the readme. What makes this uncomfortable is the
reader's identity. Whoever reads the contribution guide is, by definition, someone new
to the repository; they do not have the knowledge to tell that a step is no longer
valid and assume the wrong step is their own mistake. The repository's most falsifiable
text is handed to the reader least able to notice the wrong.

Two separate maintenance rules follow from this. The readme and the contribution guide
need **frequent but cheap** maintenance: a check that runs the embedded examples
catches six claims in the readme and four in the contribution guide on its own. The
architecture decision record needs **infrequent but expensive** maintenance: only a
human, returning to the decision's context, can verify it. Auditing both documents on
the same calendar means auditing both in the wrong place.

#### Summary

- Repository documents' reader is not the product's reader: the readme meets whoever
  looks at the repository first, the contribution guide meets whoever is about to touch
  the code, the architecture decision record meets whoever is looking for a reason.
- The readme binds to all four surfaces at once (**2/4/3/3**), the contribution guide is
  flow- and name-heavy (**1/6/2/3**), the architecture decision record is concept-heavy
  (**7/0/1/2**).
- **First-loss version is 2 in all three** and distinguishes nothing; what distinguishes
  them is **half-life version**: readme **3**, contribution guide **3**, decision
  record **12**.
- At version five the decision record stands at **7/3/3**, the readme at **2/10/4**,
  the contribution guide at **1/11/7**; an evenly split maintenance budget therefore
  goes to the wrong place.
- The longest-lived document decays the most silently: at version twelve, **10** of the
  decision record's **10** stale claims are silent, versus **6** of the readme's **12**.
- Pointing instead of narrating moves a claim from signature to name and extends its
  life; for the decision record, writing its status moves a stale sentence out of
  wrongness and into history.

#### Next Step

In none of the three documents measured in this lesson did the signature share exceed
half. The repository's most crowded signature set sits elsewhere: in the interface
reference. The second lesson left it at **2/15**, and the reason was not the writer's
carelessness but that thirteen of the fifteen claims hung on the fastest surface. The
next lesson takes that type on its own terms — how are endpoints, parameters, and
errors documented — and measures a single question: do these claims have to be
hand-written, and if not, how many survive, and what exactly is left?

### API Reference

Source: https://academia.sh/en/courses/technical-writing/api-reference

13 of the reference's 15 claims are bound to signature, and a hand-written reference is left at 0/15 by version six; a generated one is left at 13/15 at the same version, the 2 unrecoverable claims sit on the name surface, and because reference carries no example every one of its stale claims is silent.

The previous lesson measured three repository documents, and in none of them did the
signature share exceed half. The repository's most crowded signature pile sits
elsewhere. The second lesson left the interface reference at **2/15** by version two,
and the reason was not the writer's carelessness: **13** of the fifteen claims hung on
the fastest-changing surface.

This lesson takes that type on its own terms. First it establishes how a reference is
sectioned — endpoint, parameter, error — then it measures a single question: do these
claims have to be **hand-written?** If not, how many survive, and what exactly is left?

#### The Reference's Three Sections

A reference is not written to be read, it is written to be looked up. The reader knows
what they are looking for and wants to confirm a single cell. This is why a reference's
layout is not narrative, it is a **table**; and being a table is not merely a style
choice — a table ensures every cell carries a single claim and makes its absence
noticeable. The table has three sections.

**Endpoint** gives the call's identity: the method, the path, and the variable parts
inside the path. **Parameter** gives every input's name, where it comes from, its type,
whether it is required, and its default. **Error** lists the ways the call can fail:
status code, error code, and the condition it returns under.

```text
# an entry from the invented product's interface reference
# example text, not executed

GET /measurement/{station}/summary
  this endpoint is referred to in the reference as "station summary"

  station    path parameter     string   required
  interval   query parameter    integer  default 60, lower bound 1
  format     query parameter    string   "plain" | "table"

  200  returns a summary object
  404  station-not-found     the given station is not defined
  422  interval-invalid      interval is below the lower bound
```

Nearly everything written in this entry is the interface's **shape**: the path pattern,
parameter names, types, defaults, status codes. Only two things are not shape — the name
the endpoint is referred to by in the reference, and the error codes' names. Both sit on
the **name** surface, and shortly they will turn into a result on their own.

#### The Fields of a Parameter Line

A parameter line has to say six things, and missing one sends the reader to the source:
**name**, **location** (path, query, or body), **type**, **required**, **default**, and
**bound**. In the entry above, the `interval` line carries all six; the `format` line
carries no default, and the reader is left with the question "what happens if I do not
give one."

All six of these fields sit on the signature surface, so all six carry the risk of
going stale once every two versions. But there is a distinction among them: the first
four can be read from the source's declaration; **default** and **bound** most often
sit in the code's body and cannot be read from the declaration. This distinction
predetermines how much generation can recover; the measurement counts the six as a
whole, but in practice generation's scope starts exactly here.

There is also something the line deliberately does not carry: **why**. Why the
`interval` parameter's lower bound is one is not written in the reference, it is
written in the explanation. If it were written in both, it would sit in two places,
the two would go stale at two separate speeds, and the reader would not know which one
is current.

#### Status Code and Error Code

The error section's two columns do not say the same thing, and confusing them is
costly. The **status code** classifies roughly: was the request wrong, was the
resource missing, did the server crash. The **error code** names a single specific
condition — `station-not-found` and `measurement-not-found` can return the same status
code, and the reader's next action is completely different for each.

A practical consequence follows: a client **branches on the error code, not the status
code.** So the reference's most heavily relied-upon cell is the one sitting, in this
measurement, on the **name** surface. It is small in number — two of fifteen claims —
but it is the claim code gets written against. When it goes stale, branching silently
enters the wrong path; no type error is thrown, no warning appears.

The error section is the weakest section in most references, and the reason is
measurable. The successful path is written once and exercised on every call; failure
paths wait to be written and no one calls them. Yet the reader looking at a reference is
usually someone who has **already hit an error**; the cell they are looking for sits
exactly there.

Defining a contract in a machine-readable form and verifying it with a test are the
subject of the Web API Design course and are not repeated here. In this lesson the
reference is measured not as a contract tool but as **a text that needs maintenance**:
how many claims it carries, how much it holds up, how many of them can be recovered.

#### Writing by Hand and Generating

The same reference can be brought into existence two ways.

In a **hand-written** reference, every cell is a sentence written by a person. When a
parameter's type changes in the source, the reference's cell keeps showing the old
type; nothing binds the two together. In a **source-generated** reference, the cells
sit not in prose but inside the source itself, and are reread on every publish. If the
type changes, the cell changes with it, because the cell is already a copy of the
source.

What generation recovers is fixed: **only what is written in the source.** A
parameter's name, type, and default are in the source. The name an endpoint is referred
to by in the reference is not in the source; an error code's human-readable name most
often is not either. These stay outside generation and remain in hand-written text. The
measurement gives this distinction as a number.

The measurement's assumptions:

- **DT27** — The measured set is the shared setup's **reference** type: **15** claims,
  **13** on signature, **2** on name. This mix is not changed.
- **DT28** — The reference has **no** flow or concept claim. Reference does not narrate
  order and builds no reasoning; this comes from the type's definition, not a choice
  made in this lesson.
- **DT29** — The fifteen claims are split across three sections: endpoint **4**,
  parameter **7**, error **4**. Sectioning does not change the surface mix, it only
  makes claims addressable.
- **DT30** — The reference's share of claims embedded in a runnable example is
  **0.00**. Reference carries no example; this is why every stale reference claim is
  **silent**.
- **DT31** — Surface frequencies are unchanged: signature every **2**, flow every
  **3**, name every **5**, concept every **12** versions.
- **DT32** — **In a generated reference, signature claims never go stale.** Because
  they are read from the source, they change together with it; the lag between the two
  is counted as zero in this measurement.
- **DT33** — Generation does not recover the **name** surface: the name an endpoint is
  referred to by, and an error code's readable name, do not sit in the source, they sit
  in hand-written text.
- **DT34** — Generation does not recover the **flow** surface either; but this cannot
  be measured in this reference, because the reference has no flow claim. Claims bound
  to flow are measured in the other types.
- **DT35** — The measurement builds versions **0, 1, 2, 3, 5, 6, and 12**.
- **DT36** — The set's resolution is **1/15**; differences below one claim cannot be
  defended with this set.

#### Measurement

```python
"""API reference: the lifespan of the hand-written versus the generated.

Part 1 - how the reference's 15 claims split across sections and surfaces.
Part 2 - true/stale/silent across versions for hand-written versus generated reference.
"""
FREQUENCY = {"signature": 2, "flow": 3, "name": 5, "concept": 12}
VERSIONS = (0, 1, 2, 3, 5, 6, 12)
# The reference's 15 claims: which section, which surface they are bound to.
# Total signature 13, name 2 - exactly the reference mix from the shared setup.
SECTIONS = {
    "endpoint":  {"signature": 3, "name": 1},
    "parameter": {"signature": 7, "name": 0},
    "error":     {"signature": 3, "name": 1},
}
# The reference's embedded claim share is zero: reference carries no runnable example.
EMBEDDED = 0.00


def claims():
    entries, no = [], 0
    for section, mix in SECTIONS.items():
        for surface, count in mix.items():
            for _ in range(count):
                no += 1
                entries.append({"no": no, "section": section, "surface": surface,
                                 "embedded": EMBEDDED >= 1})
    return entries


def stale(i, version):
    return version // FREQUENCY[i["surface"]] >= 1


def measure(entries, version, generated):
    """In a generated reference, signature claims come from the source and never go stale."""
    gone_stale = [i for i in entries
                  if stale(i, version) and not (generated and i["surface"] == "signature")]
    silent = [i for i in gone_stale if not i["embedded"]]
    return len(entries), len(entries) - len(gone_stale), len(gone_stale), len(silent)


L = claims()
print(f"reference claims {len(L)} | embedded in example "
      f"{sum(i['embedded'] for i in L)}")
print()
print(f"{'section':<12s} {'claims':>6s} {'signature':>9s} {'name':>4s} {'flow':>5s} "
      f"{'concept':>7s}")
for b in SECTIONS:
    g = [i for i in L if i["section"] == b]
    print(f"{b:<12s} {len(g):6d} "
          + " ".join(f"{sum(1 for i in g if i['surface'] == y):>{w}d}"
                     for y, w in zip(("signature", "name", "flow", "concept"),
                                     (9, 4, 5, 7))))
print(f"{'total':<12s} {len(L):6d} "
      + " ".join(f"{sum(1 for i in L if i['surface'] == y):>{w}d}"
                 for y, w in zip(("signature", "name", "flow", "concept"),
                                 (9, 4, 5, 7))))

print()
print(f"{'version':>7s} {'hand-written':>20s} {'generated':>20s}")
print(f"{'':7s} {'true/stale/silent':>20s} {'true/stale/silent':>20s}")
for s in VERSIONS:
    h = measure(L, s, False)
    g = measure(L, s, True)
    print(f"{s:7d} {'{}/{}/{}'.format(*h[1:]):>20s} "
          f"{'{}/{}/{}'.format(*g[1:]):>20s}")

print()
print(f"{'section':<12s} {'v6 hand-written':>15s} {'v6 generated':>13s} "
      f"{'recovered':>9s}")
for b in SECTIONS:
    g = [i for i in L if i["section"] == b]
    h, gen = measure(g, 6, False), measure(g, 6, True)
    print(f"{b:<12s} {f'{h[1]}/{h[0]}':>15s} {f'{gen[1]}/{gen[0]}':>13s} "
          f"{gen[1] - h[1]:9d}")

print()
remaining = [i for i in L if stale(i, 6) and i["surface"] != "signature"]
print("stale claims remaining in the generated reference at version 6:",
      [(i["section"], i["surface"]) for i in remaining])
print(f"surface recovered by generation: signature "
      f"({sum(1 for i in L if i['surface'] == 'signature')} claims) | not recovered: "
      f"{', '.join(sorted({i['surface'] for i in remaining}))} ({len(remaining)} claims)")
```

```
reference claims 15 | embedded in example 0

section      claims signature name  flow concept
endpoint          4         3    1     0       0
parameter         7         7    0     0       0
error             4         3    1     0       0
total            15        13    2     0       0

version         hand-written            generated
           true/stale/silent    true/stale/silent
      0               15/0/0               15/0/0
      1               15/0/0               15/0/0
      2              2/13/13               15/0/0
      3              2/13/13               15/0/0
      5              0/15/15               13/2/2
      6              0/15/15               13/2/2
     12              0/15/15               13/2/2

section      v6 hand-written  v6 generated recovered
endpoint                 0/4           3/4         3
parameter                0/7           7/7         7
error                    0/4           3/4         3

stale claims remaining in the generated reference at version 6: [('endpoint', 'name'), ('error', 'name')]
surface recovered by generation: signature (13 claims) | not recovered: name (2 claims)
```

#### What Generation Recovers and What It Does Not

The middle table gives the lesson's main number. By version six, the hand-written
reference is at **0/15**; not a single claim is recovered, all fifteen of fifteen are
stale. The generated reference at the same version is at **13/15**. The difference is
**13** claims, and all of it comes from the **signature cells of parameter and
endpoint**.

The bottom table spreads the difference across the sections: the parameter section
goes from **0/7** hand-written to **7/7** generated — fully recovered, because every
cell inside it is written in the source. Endpoint climbs from **0/4** to **3/4**, error
from **0/4** to **3/4**. Both sections are left with one unrecoverable claim.

The last row states plainly which two claims cannot be recovered: one is the endpoint's
**name** in the reference, the other is the error code's readable **name**. Both sit on
the **name surface**. Generation's boundary is drawn cleanly here: **generation
recovers what is written in the source, it does not recover what a human made up.** The
source does not decide what a name should be.

The same boundary applies to the **flow** surface too — the order calls must be made
in cannot be generated from the source either — but it cannot be measured in this
reference, because the reference's flow claim count is **0**. This is not a
measurement gap, it is the type's definition: reference does not narrate order. It is
also the reference's biggest blind spot, and this topic's sixth lesson enters exactly
there.

The silent column is the lesson's uncomfortable finding. Because the reference's
embedded claim count is **0**, the stale count and the silent count are **identical**
on every row: **15/15** hand-written, **2/2** generated. So generation brings the stale
count down from fifteen to two but **does not change the silence ratio at all** — the
remaining two claims still sit wrong without telling anyone anything.

This matters not to belittle generation but to name what it actually does correctly.
Generation is not a **document audit**, it is a **copying** mechanism: it forms the
link between source and text, it does not report the text's correctness to anyone.
Once thirteen claims are recovered, the remaining two are exactly as silent as before —
and since the rest of the reference is now trustworthy, no one is expected to look at
those two with suspicion either. **The recovered claims hide the unrecoverable ones.**

Which two claims specifically remain unrecoverable also matters. An earlier section
said the client branches on the error code, not the status code; one of the
unrecoverable claims is exactly that error code's name. So the **13/15** ratio makes
the practical recovery look better than it is: the thirteen claims recovered are the
cells the reader checks to confirm; the two unrecovered are the cells code is written
against. Read over the denominator, generation looks like it has recovered **0.8667**
of the set; read over impact, it has left the two most expensive claims exactly where
they were. The measure itself does not say this — it counts claims, not their
weight — and this is a limit of claim-counting worth knowing.

The version-two and version-three rows give one last detail: the generated reference
stands at **15/15** there, meaning it takes no loss at all through the first three
versions. The hand-written one is at **2/13/13** at the same point. Generation's gain
starts at the first signature change and stays **complete through version five**; the
name surface changes there for the first time, and the count drops from **15** to
**13**. After that it holds steady: still **13/15** at version twelve. The generated
reference stops decaying **any further** past a certain point, because it has already
broken everything it could.

#### Summary

- Reference is a table, not narration; it has three sections — endpoint, parameter,
  error — and error is the weakest section because no one calls the failure paths.
- **13** of the reference's **15** claims are bound to signature, **2** to name; it has
  **no** flow or concept claim, and its embedded claim count is **0**.
- The hand-written reference gives **0/15** by version six; the source-generated one
  gives **13/15** at the same version. The difference comes from the parameter
  section's **7/7** recovery and two sections' **3/4** recovery.
- The **2** unrecoverable claims sit on the **name** surface — the endpoint's referred
  name and the error code's readable name; generation recovers what is written in the
  source, not what a human made up.
- Because reference carries no example, its stale count and silent count are equal on
  every row; generation brings stale down from **15** to **2** and changes the silence
  ratio **not at all**.
- The client branches on the **error code**, not the status code; one of the two
  unrecoverable claims is exactly that. Claim-counting does not weigh claims, and this
  is the measure's limit worth knowing.

#### Next Step

Up to here, the four types and the repository documents described the product's
**current state**. This lesson's table left the reader owing a new question: with a
**13/15** reference in hand, how will they know **when** the remaining two dropped?
The second lesson had already named this question — "what version did this change in"
landed on no type. The next lesson measures the text that closes that gap: release
notes. The question will be — what difference does the list of changes make against
the list of what those changes **mean**, and how many claims does it make datable for
the reader?

### Release Notes

Source: https://academia.sh/en/courses/technical-writing/release-notes

The four types describe the product as it is today and none carries time; the raw change list dates 17 of 56 stale claims by version twelve and 0 of the silent ones, while a note that writes what each entry falsifies dates 56 of 56 and 39 of 39 silent ones.

The previous lesson left the generated reference at **13/15** by version six and left
the reader owing a question: **when** did the remaining two drop? The second lesson had
already given this question a name — one of the six reader questions, "what version did
this change in," landed on none of the four types.

This lesson measures the text that closes that gap. What is being asked is not how
release notes get published, but **what they make datable**: a reader looking at a
stale claim — where will they learn which version falsified that sentence?

#### The One Text That Carries Time

The four types share a trait that has never yet been named: **all four are written in
the present tense.** A tutorial says how to start today, a how-to says how to do it
today, an explanation says why it is that way today, a reference says which field is of
which type today. All four are texts that erase the past; when a field's type changes,
the reference does not mention the old type, it writes the new one, and the old one is
treated as if it never existed.

This is a correct design for the four types. If history were folded into the
reference, the reader would have to sort out, in every cell, which line belongs to
today, and the reference's one job — fast confirmation — would be lost. But it has a
cost: **because no type carries time, none of them can answer "when did this
change."**

Release notes sit exactly in this gap, and what separates them from the others is not
their content but their **index**. The four types are indexed by subject: the reader
looks for a subject, opens that subject's section. Release notes are indexed **by
time**: the reader looks for a version, opens that version's entry. This is why
release notes are not a fifth type but a separate type standing on a separate axis.

Which pipeline a release ships through, how the note's fields meet the reader's six
questions, and what a tag marks were measured in the Code Review and Team Process
course and are not repeated here. What was measured there was what the note says to
**the reader**; what is measured here is what it does to **the other documents**.

#### Two Lists

The same release can be written in two forms, and both are produced from the same
event.

```text
# two forms of the invented product's release notes
# example text, not executed

raw change list
  v6  measurement call's option names were reorganized
  v6  step order in the summary flow changed

meaning list
  v6  measurement call's option names were reorganized
      falsified: reference's parameter lines,
      how-to text's call examples
  v6  step order in the summary flow changed
      falsified: tutorial's third and fourth step
```

The two lists carry the same number of entries. The difference is in a single field:
the second writes, for every entry, **what it falsified.** This field looks like an
ordinary detail but it reverses the direction. The raw list is written **from the
change's side**: it narrates the work done. The meaning list is written **from the
document's side**: it says which sentences are false as of today.

This distinction's effect on the reader has to do with when the note is useful. A
reader looking at the raw list has to **already know** what they are looking for. If an
example blew up on them, they know which call broke and search the list for that call;
once found, they also learn the date. But if nothing has blown up, they have no reason
to look at the list at all, because they do not know which sentence went false. **A raw
list serves a reader who has already heard about the failure.**

#### The Length of a Note

The cost of writing a note is its number of entries, and that number is not as large as
it seems. Across twelve versions, surfaces change at their own rates; some versions no
surface changes, and some, more than one changes at once. A note carries an entry only
for versions **where a change happens.** The measurement will also give this entry
count, and the number of claims dated per entry.

**When** an entry gets written is a choice too, and it determines the result. If the
note is written all at once on release day, the writer has to reconstruct, after the
fact, which sentences every change falsified; by that point this information is not
easy to find and in practice does not get written. If the entry is written **at the
moment of the change**, the person who knows the falsified sentence is already there:
the person making the change. The gap looks like a discipline question, but it directly
determines the measured column — in the table below, the only field separating the
meaning list from the raw list is cheap **only at that moment.**

The measurement's assumptions:

- **DT37** — The measured set is the course's shared setup: **56** claims, four types,
  four surfaces, and **17** embedded examples. The mix is not changed.
- **DT38** — Surface frequencies are fixed: signature every **2**, flow every **3**,
  name every **5**, concept every **12** versions. A surface changes once at every
  version that is a multiple of its frequency.
- **DT39** — A claim's **fall version** is the first version at which the surface it is
  bound to changes, and it is known because we wrote the setup. The reader knows it
  only if it was written down.
- **DT40** — Release notes carry one entry for every version at which at least one
  surface changes. Versions where no surface changes carry no entry.
- **DT41** — Three note formats are tried: **no release notes**, **raw change list**,
  and **meaning list**. All three are produced from the same events.
- **DT42** — **With no release notes**, a stale claim cannot be dated; none of the four
  types carries time.
- **DT43** — **The raw change list** writes only what was done. For it to make a claim
  datable, the reader has to have already suspected that claim; in this measurement,
  suspicion only arises when an **embedded** claim breaks.
- **DT44** — **The meaning list** writes the falsified claims into every entry; the
  reader does not need to suspect anything, every stale claim becomes datable.
- **DT45** — For a claim to count as **datable**, it must first be **stale**; a claim
  still true does not enter this column.
- **DT46** — The set's resolution is **1/56**; at the note-entry scale it is **1/9**.

#### Measurement

```python
"""Release notes: is a claim's fall datable for the reader when it goes stale.

Part 1 - change events across twelve versions and the note's entry count.
Part 2 - datable stale claims and the silent share, under three note formats.
"""
TYPES = {
    "tutorial":      {"flow": 9, "signature": 3, "name": 2, "concept": 1},
    "how-to":        {"flow": 6, "signature": 5, "name": 2, "concept": 1},
    "explanation":   {"flow": 1, "signature": 1, "name": 1, "concept": 9},
    "reference":     {"flow": 0, "signature": 13, "name": 2, "concept": 0},
}
FREQUENCY = {"signature": 2, "flow": 3, "name": 5, "concept": 12}
EMBEDDED = {"tutorial": 0.60, "how-to": 0.70, "explanation": 0.10,
            "reference": 0.00}
VERSIONS = (2, 3, 5, 6, 12)
LAST = 12


def claims():
    entries, no = [], 0
    for kind, mix in TYPES.items():
        embedded_target = 0
        for surface, count in mix.items():
            for _ in range(count):
                no += 1
                embedded_target += EMBEDDED[kind]
                embedded = embedded_target >= 1 and surface != "concept"
                if embedded:
                    embedded_target -= 1
                entries.append({"no": no, "type": kind, "surface": surface,
                                 "embedded": embedded, "fall_version": FREQUENCY[surface]})
    return entries


def events(last=LAST):
    """Which surfaces change at each version: the entries the note will write."""
    return [(s, [y for y in FREQUENCY if s % FREQUENCY[y] == 0])
            for s in range(1, last + 1)
            if any(s % FREQUENCY[y] == 0 for y in FREQUENCY)]


# Note formats: under what condition they make a stale claim datable.
FORMATS = {
    "no release notes": lambda i: False,
    "raw change list": lambda i: i["embedded"],
    "meaning list": lambda i: True,
}

L = claims()
O = events()
print(f"claims {len(L)} | embedded in example {sum(i['embedded'] for i in L)} | "
      f"change events across twelve versions {sum(len(y) for _, y in O)} | "
      f"note entries {len(O)}")
print("note entries: " + "; ".join(f"v{s}: {', '.join(y)}" for s, y in O))

print()
print(f"{'version':>7s} {'stale':>5s} {'silent':>6s} " +
      " ".join(f"{ad:>23s}" for ad in FORMATS))
print(f"{'':7s} {'':5s} {'':6s} " +
      " ".join(f"{'datable/of silent':>23s}" for _ in FORMATS))
for s in VERSIONS:
    stale = [i for i in L if s // FREQUENCY[i["surface"]] >= 1]
    silent = [i for i in stale if not i["embedded"]]
    row = f"{s:5d} {len(stale):5d} {len(silent):6d} "
    for ad, rule in FORMATS.items():
        t = [i for i in stale if rule(i)]
        ts = [i for i in t if not i["embedded"]]
        row += f" {f'{len(t)}/{len(ts)}':>23s}"
    print(row)

print()
print(f"{'note format':<24s} {'entries':>7s} {'dated claims':>12s} "
      f"{'per entry':>10s} {'of silent':>9s}")
last_stale = [i for i in L if LAST // FREQUENCY[i["surface"]] >= 1]
last_silent = [i for i in last_stale if not i["embedded"]]
for ad, rule in FORMATS.items():
    t = [i for i in last_stale if rule(i)]
    ts = [i for i in t if not i["embedded"]]
    entries = 0 if ad == "no release notes" else len(O)
    share = len(t) / entries if entries else 0
    print(f"{ad:<24s} {entries:7d} {f'{len(t)}/{len(last_stale)}':>12s} "
          f"{share:10.3f} {f'{len(ts)}/{len(last_silent)}':>9s}")
```

```
claims 56 | embedded in example 17 | change events across twelve versions 13 | note entries 9
note entries: v2: signature; v3: flow; v4: signature; v5: name; v6: signature, flow; v8: signature; v9: flow; v10: signature, name; v12: signature, flow, concept

version stale silent        no release notes         raw change list            meaning list
                           datable/of silent       datable/of silent       datable/of silent
    2    22     17                      0/0                     5/0                   22/17
    3    38     24                      0/0                    14/0                   38/24
    5    45     28                      0/0                    17/0                   45/28
    6    45     28                      0/0                    17/0                   45/28
   12    56     39                      0/0                    17/0                   56/39

note format              entries dated claims  per entry of silent
no release notes               0         0/56      0.000      0/39
raw change list                9        17/56      1.889      0/39
meaning list                   9        56/56      6.222     39/39
```

#### Reading Datability

The first line gives the note's length: across twelve versions there are **13** change
events, but the note carries only **9** entries. At three versions more than one
surface changes at once — signature and flow at version six, signature and name at
ten, all three at twelve. At three other versions nothing changes, and the note
carries no entry for them.

The top table's left columns give the course's familiar numbers: stale claims are
**22** at version two, **38** at three, **45** at five, **56** at twelve; silent ones
are **17**, **24**, **28**, and **39** respectively. The three columns on the right show
what the reader can date at those same rows.

**With no release notes**, every row gives **0/0**. This is the numeric counterpart of
the lesson's opening claim: no matter how well the four types are written, none can say
when a single claim went false.

**The raw change list** dates **5** at version two, **14** at three, **17** at five,
and stops there. Even though stale claims climb to **56** by version twelve, the raw
list stays stuck at **17**. This is not a coincidence: the embedded claim count is
**17**, and the raw list dates exactly those, not one more. Past version five the raw
list's curve flattens: changes keep happening, the note keeps growing, but the number of
dated claims stays fixed. The effort spent writing the note rises; its return does not.

The real finding sits in the raw list's share on the right: **0/39**. Of the thirty-nine
silently stale claims, **not one** can be dated with the raw change list. The reason is
plain — a raw list serves only a reader who already suspects something, and a silent
claim raises no suspicion in anyone. A raw list is text that does nothing **unless the
reader already knows**; for a reader who does not know, it does nothing at all.

**The meaning list** dates the entire stale count at the same rows: **22/17**,
**38/24**, **45/28**, and **56/39**. The last column gives this in a single ratio —
**39** of the thirty-nine silent claims become datable.

The bottom table measures the cost, and it holds the lesson's most practical result.
**Both formats carry 9 entries.** The meaning list is not a longer note; it is the same
nine entries with one extra field written into them. In exchange, dated claims per
entry climb from **1.889** to **6.222** — more than three times. The writing order can
be said in one sentence: **it is not enough to write what was done to an entry; what it
falsified has to be written too.**

This is the first mechanism in the course that reaches silent staleness, and how it
gets there matters. What made silent staleness invisible was that no signal came from
the document itself. The meaning list draws its signal not from the document but from
**the change itself**: which sentences went false is known at the moment the code
changes, not afterward. What the note holds is that moment. If it is not written, it is
lost and can never be produced again.

#### What the Note Does Not Close

The table says what the note does; what it does not do also needs writing, because the
**56/56** picture promises more than it delivers.

First: dating is not fixing. The meaning list tells the reader "the reference's
parameter line went false at version six"; it does not say **what the correct value
is.** The reader learns the wrong, not the right. This is still a large gain — knowing
something is wrong is better than believing it is right — but it does not move the
claim back into the true column. In the table, the **true** column never changes under
any note format; what changes is only how visible the stale one is.

Second: the note's reader is not the document's reader. Whoever follows release notes
already uses the product. A reader arriving for the first time today does not read past
notes; for them the document is still timeless, and nothing but the **17** embedded
claims gives them any warning. The meaning list makes staleness visible **for the
maintainer**, not for the **new reader**.

Third: a note is only as correct as it is written, and it is itself a document. The
"falsified" field in its entries depends on the document's structure at the moment it
was written; when sections get reorganized, that field goes stale too. The note itself
was kept outside this measurement, but in a real repository it also enters the counted
set.

#### Summary

- The four types are written in the present tense and indexed by subject; release
  notes are a separate type indexed **by time**, and only they close the "what version
  did this change in" question.
- Thirteen change events across twelve versions become **9** note entries; some
  versions see more than one surface change at once, some see none.
- With no release notes, **0/56** of stale claims are datable; with the raw change
  list, **17/56** — equal to the embedded claim count — and it does not grow further by
  version twelve.
- The raw change list dates **0** of the **39** silently stale claims; it serves only a
  reader who has already heard about the failure.
- The meaning list dates **56/56** with the same **9** entries and **39/39** of the
  silent ones; dated claims per entry climb from **1.889** to **6.222**.
- The gap is not in entry count but in the **falsified-claims** field written into an
  entry; this information exists at the moment of change and cannot be produced
  afterward if not written down.

#### Next Step

This lesson closed one of the reader's two unanswered questions. The second remains,
and it is asked far more often than the first: **"why is this not working?"** The
reader arriving with this question knows neither a subject name nor a version number;
all they have is a **symptom** — an error line, an empty output, a result other than
expected. What they have is only what the product showed them, and what it showed
matches no section heading in the document. The next lesson compares the reader
questions a symptom-first text covers against those a concept-first text covers, and
names the boundary troubleshooting content cannot reach.

### Troubleshooting Content

Source: https://academia.sh/en/courses/technical-writing/troubleshooting-content

A reader arriving with a symptom reaches only 8 of a concept-first text's 17 defects and meets 8 of 51 decisions (0.1569); a symptom-cause-remedy layout reaches all 17 at once and meets 51, but the 39 silently stale claims produce no symptom at all and stay outside troubleshooting too.

The previous lesson closed one of the reader's two unanswered questions: release notes
made a stale claim's fall version datable. The second remains, and it is asked far more
often than the first: **"why is this not working?"**

The reader arriving with this question has no subject name in hand. No version number
either. All they have is a **symptom** the product showed them: an error line, an
empty output, a result other than expected. This lesson measures how much of this a
symptom-first text covers, and how much a concept-first text covers.

#### The Reader Who Arrives With a Symptom

The four types and release notes shared one assumption: the reader can **name** what
they are looking for. Whoever comes to a tutorial says "I want to start"; whoever comes
to a reference says "this field's type"; whoever comes to release notes says "what
happened at version six." All five texts are indexed by a name.

A reader arriving with a defect cannot do this. What they see is not a name, it is a
**result** — and if they knew which concept that result belonged to, they would already
have solved it. The mismatch sits exactly here: **the document's index is concept, the
reader's index is symptom.** The reader is expected to translate between the two
indexes, and they are there precisely because they cannot make that translation.

Troubleshooting content is the text that translates the index. It is a separate type
not because it carries different information — most of what it carries already sits in
the explanation and the reference — but because it is **indexed differently**: its
entries start from the symptom.

#### Symptom, Cause, Remedy

A troubleshooting entry has three sections, and the three meet three separate reader
needs.

```text
# an entry from the invented product's troubleshooting section
# example text, not executed

symptom  The `station start` command exits printing:
         "unknown option: --interval"

cause    The sampling interval is no longer given by the call option
         but by a profile field; the old option was removed.

remedy   Write the interval into the `sampling` field of the profile
         file and run the command without the option.
```

**Symptom** meets the reader's **recognition** need: is what they are seeing a known
defect, or an oddity in their own setup? This answer matters more than it might seem;
what eats the reader's hours is often not the defect itself but not knowing whether the
defect is recognized. **Cause** meets the reader's **cause** need. **Remedy** meets
the reader's **action** need.

The symptom section is the hardest of the three to write, because it has to be written
**in the exact form the reader sees it.** "The interval option was removed" is not a
symptom, it is a cause; the reader does not type that into a search box. What the
reader types is the line they see on screen. This is the point troubleshooting content
misses most often: an entry headlines the correct information under a name the reader
will never search for, and it is never found.

#### The Cost of Starting From Concept

The same information also has a concept-first version, and it is usually already
written: the explanation text describes what the sampling interval is and where it
comes from. If the reader could reach that text, they would have solved the defect.

Reaching it depends on one condition: **the defect itself has to name what changed.**
The line "unknown option: --interval" does name it — the reader takes the string
`--interval`, searches the document, and lands on the explanation. But not every defect
speaks this way. When the order of steps changes, what comes out is not a name, it is
an **absence**: the command runs, throws no error, and produces something other than
the expected result. The reader has no string in hand to search for, and cannot enter
the concept index at all.

The measurement counts this distinction. Defects bound to the signature and name
surfaces name themselves; ones bound to the flow surface do not.

The measurement's assumptions:

- **DT47** — The measured set is the course's shared setup: **56** claims, four types,
  four surfaces, **17** embedded examples. The mix and surface frequencies are not
  changed.
- **DT48** — The measurement is done **at version twelve**, where every claim is stale
  and the widest set troubleshooting content could possibly reach exists.
- **DT49** — A **symptom** arises only once a claim embedded in a runnable example
  goes stale. When a claim that is not embedded goes false, the product shows nothing.
- **DT50** — Every symptom corresponds to exactly one troubleshooting entry; two claims
  producing the same symptom does not occur in this measurement.
- **DT51** — The reader has three needs in the face of a defect: **recognition**,
  **cause**, **action**. These are separate from the course's six reader questions and
  sit inside a single defect.
- **DT52** — **Concept-first** text is indexed by concept; the reader reaches an entry
  only if the defect **names** what changed.
- **DT53** — Defects bound to the **signature** and **name** surfaces name themselves
  (a missing field, an unknown option, an error code not found); ones bound to
  **flow** do not.
- **DT54** — **Symptom-first** text is indexed by symptom; because the reader already
  holds the symptom, they reach every entry.
- **DT55** — A reached entry closes only the needs met by **the sections it carries**;
  a section it does not carry sends the reader to another text and is counted as unmet
  in this measurement.
- **DT56** — Concept-first text carries only the **cause** section; giving action is
  not explanation's job, and this is taken as the type's definition, not a defect.
- **DT57** — The decision scale is **symptom count × need count**; every need of every
  symptom is a separate decision.
- **DT58** — The set's resolution is **1/56** at the claim scale, **1/51** at the
  decision scale.

#### Measurement

```python
"""Troubleshooting content: symptom-first text versus concept-first text.

Part 1 - how many stale claims produce a symptom, how many produce none.
Part 2 - the symptoms and reader needs three text layouts reach.
"""
TYPES = {
    "tutorial":      {"flow": 9, "signature": 3, "name": 2, "concept": 1},
    "how-to":        {"flow": 6, "signature": 5, "name": 2, "concept": 1},
    "explanation":   {"flow": 1, "signature": 1, "name": 1, "concept": 9},
    "reference":     {"flow": 0, "signature": 13, "name": 2, "concept": 0},
}
FREQUENCY = {"signature": 2, "flow": 3, "name": 5, "concept": 12}
EMBEDDED = {"tutorial": 0.60, "how-to": 0.70, "explanation": 0.10,
            "reference": 0.00}
VERSION = 12
# Does the defect itself name what changed: this is how the reader finds the concept.
SELF_NAMING = {"signature", "name"}
# The reader's three needs when facing a defect.
NEEDS = ("recognition", "cause", "action")
# Text layout -> (index, needs met by the sections it carries).
LAYOUTS = {
    "concept-first": ("concept", ("cause",)),
    "symptom-first (symptom-remedy)": ("symptom", ("recognition", "action")),
    "symptom-first (symptom-cause-remedy)":
        ("symptom", ("recognition", "cause", "action")),
}


def claims():
    entries, no = [], 0
    for kind, mix in TYPES.items():
        embedded_target = 0
        for surface, count in mix.items():
            for _ in range(count):
                no += 1
                embedded_target += EMBEDDED[kind]
                embedded = embedded_target >= 1 and surface != "concept"
                if embedded:
                    embedded_target -= 1
                entries.append({"no": no, "type": kind, "surface": surface,
                                 "embedded": embedded})
    return entries


L = claims()
stale = [i for i in L if VERSION // FREQUENCY[i["surface"]] >= 1]
# A symptom arises only when a claim embedded in a runnable example goes stale.
symptomatic = [i for i in stale if i["embedded"]]
silent = [i for i in stale if not i["embedded"]]
print(f"version {VERSION} | claims {len(L)} | stale {len(stale)} | "
      f"produces symptom {len(symptomatic)} | produces no symptom {len(silent)}")
print()
print(f"{'surface':<9s} {'stale':>5s} {'symptom':>7s} {'no symptom':>10s} "
      f"{'defect names itself':>20s}")
for y in FREQUENCY:
    g = [i for i in stale if i["surface"] == y]
    b = [i for i in g if i["embedded"]]
    print(f"{y:<9s} {len(g):5d} {len(b):7d} {len(g) - len(b):10d} "
          f"{('yes' if y in SELF_NAMING else 'no'):>20s}")

print()
total = len(symptomatic) * len(NEEDS)
print(f"symptoms {len(symptomatic)} | needs {len(NEEDS)} | "
      f"decisions {total}")
print()
print(f"{'text layout':<40s} {'reached':>8s} {'met':>10s} "
      f"{'ratio':>6s}")
for ad, (index, sections) in LAYOUTS.items():
    reached = [i for i in symptomatic
               if index == "symptom" or i["surface"] in SELF_NAMING]
    met = len(reached) * len(sections)
    print(f"{ad:<40s} {len(reached):8d} {met:10d} "
          f"{met / total:6.4f}")

print()
# Six reader questions: how many land on a text once two more types are added.
QUESTIONS = {
    "where do I start": "tutorial",
    "how do I do this": "how-to",
    "why is this the way it is": "explanation",
    "what type is this field": "reference",
    "what version did this change in": "release notes",
    "why is this not working": "troubleshooting",
}
print("reader question -> answering text")
for q, t in QUESTIONS.items():
    print(f"  {q:<30s} {t}")
print(f"questions {len(QUESTIONS)} | answered "
      f"{sum(1 for t in QUESTIONS.values() if t)} | unanswered "
      f"{sum(1 for t in QUESTIONS.values() if not t)}")
print(f"claims troubleshooting can cover {len(symptomatic)}/{len(L)} "
      f"({len(symptomatic) / len(L):.4f}) | stale claims it cannot reach "
      f"{len(silent)}")
```

```
version 12 | claims 56 | stale 56 | produces symptom 17 | produces no symptom 39

surface   stale symptom no symptom  defect names itself
signature    22       5         17                  yes
flow         16       9          7                   no
name          7       3          4                  yes
concept      11       0         11                   no

symptoms 17 | needs 3 | decisions 51

text layout                               reached        met  ratio
concept-first                                   8          8 0.1569
symptom-first (symptom-remedy)                 17         34 0.6667
symptom-first (symptom-cause-remedy)           17         51 1.0000

reader question -> answering text
  where do I start               tutorial
  how do I do this               how-to
  why is this the way it is      explanation
  what type is this field        reference
  what version did this change in release notes
  why is this not working        troubleshooting
questions 6 | answered 6 | unanswered 0
claims troubleshooting can cover 17/56 (0.3036) | stale claims it cannot reach 39
```

#### Reading the Coverage

The middle table gives the lesson's main comparison. Seventeen defects and three needs
make **51** decisions.

**Concept-first** text reaches only **8** of the seventeen defects and meets **8**
decisions: ratio **0.1569**. Two separate losses stack on top of each other. The first
is reachability: nine defects bound to the flow surface do not name themselves, the
reader has no string to search for, and those nine entries are never found at all. The
second is coverage: even in the eight entries that are reached, only the **cause**
section exists; the reader learns the reason, not what to do.

**Symptom-remedy** layout reaches all seventeen at once and meets **34** decisions:
**0.6667**. The jump comes from a single change — the index changed. The content is the
same; only the entries' heading changed. This is the cheapest gain measured anywhere in
this course: without writing a single new sentence, the number of decisions met climbs
to more than four times its previous value.

**Symptom-cause-remedy** layout closes the set with **51/51**: **1.0000**. The
difference is **17** decisions, and all of it comes from the **cause** section. A
troubleshooting text with no cause section works, but it does not teach the reader:
the same reader comes back on a similar defect, because they never learned what
changed.

The ordering itself gives a result too. Concept-first gives **0.1569**, symptom-first
in its plainest form gives **0.6667**. The gap, more than fourfold, comes not from
writing better but from **indexing the text by what the reader already holds.** The
same information, two separate headings, two separate outcomes.

#### Completing the Taxonomy, and Its Limit

The last block looks at first glance like the course's closing note: **6** of six
reader questions land on a text, **0** unanswered. The second lesson drew the
taxonomy's limit with two questions; the fifth lesson closed one, this lesson closes
the other. No question the reader might bring is left uncovered.

But the top table breaks this comfort. At version twelve, **56** of **56** claims are
stale, and only **17** of them produce a symptom. The widest set troubleshooting
content can possibly cover is **17/56** — **0.3036**. The remaining **39** claims
produce no symptom at all, so no troubleshooting entry can be written for them. The
writer does not know what to write; the reader does not know what to ask.

The surface breakdown sharpens this further. On the signature surface, **17** of
**22** stale claims produce no symptom; on the concept surface, **11** of **11** produce
no symptom. The surface troubleshooting covers best is **flow**: **9** of sixteen
stale claims give a symptom. But flow is also the surface that does **not** name
itself — meaning exactly where troubleshooting has the most entries, the concept index
is entirely useless. The two tables complete each other precisely here.

What this topic's conclusion leaves is this: **all six questions land on a text, but
all the texts together still reach only 17 of the stale claims.** The taxonomy is
complete; coverage is not. Throughout this topic, the only thing that made silent
staleness visible showed up in exactly two places — being embedded in a runnable
example, and being written into release notes' meaning list. The rest can only be found
by someone going and looking for it.

#### The Entry Itself Also Goes Stale

Troubleshooting content occupies a strange place among the texts in this topic:
**its reason for existing is a defect, and a defect can be fixed.** The other types aim
to stay valid for as long as the product lives; a troubleshooting entry becomes
unnecessary the moment the defect it points to is fixed.

Becoming unnecessary looks harmless. It is not. A leftover entry introduces the reader
to a symptom that no longer occurs and ties it to a cause that no longer exists. A
reader arrives with a similar but different defect, sees a nearby line of the symptom,
reaches the entry, and is convinced of the **wrong cause.** This is not an empty
result, it is a wrong one — worse than never having been written at all.

This kind of entry has a special place in the course's measure. A stale troubleshooting
entry is **silent**, because it breaks nothing: the text is read, the steps are
followed, the defect does not go away, and the reader suspects themselves. It never
occurs to them that the first text they saw for the symptom might be wrong, because
that text begins with exactly the line they saw.

A practical rule follows, and it is this topic's one maintenance rule: when a
troubleshooting entry is written, **which version it is valid for** should be written
too. The same mechanism as the superseded record in the third lesson — writing a
claim's scope moves it out of wrongness and into history.

#### Summary

- A reader arriving with a symptom's index does not match the document's index: the
  document indexes by concept, the reader by symptom. Troubleshooting content is the
  text that performs this translation.
- An entry's three sections meet three reader needs: **symptom** meets recognition,
  **cause** meets cause, **remedy** meets action. Symptom has to be written in the
  form the reader **sees** it.
- Seventeen defects and three needs make **51** decisions; concept-first text meets
  **8** of them (**0.1569**), symptom-remedy meets **34** (**0.6667**),
  symptom-cause-remedy meets **51** (**1.0000**).
- Concept-first text reaches only **8** of seventeen defects: nine defects bound to the
  flow surface do not name themselves and never enter the concept index at all.
- The widest set troubleshooting can cover is **17/56** (**0.3036**); the **39**
  silently stale claims produce no symptom at all and stay outside this type too.
- **6** of six reader questions now land on a text — the taxonomy is complete, coverage
  is not.

#### Next Step

Throughout this topic, texts were separated and measured by the reader's **question**;
all six questions now land on a type. But every measurement quietly assumed something:
who the reader is. "New reader," "maintainer," "someone about to touch the code" — these
roles appeared inside the lessons, never inside a document. So what happens if a text
does not say who its reader is? The next topic starts here, and its first lesson
measures exactly this question: how many of the claims still true does an unwritten
prerequisite assumption make unreadable? A sentence still being true does not mean
someone who can read it exists.

## Writing Discipline

Writing prerequisite assumptions explicitly, building scannable text, term consistency and voice unity, runnable and verifiable examples, and diagrams serving the text.

### Audience Definition

Source: https://academia.sh/en/courses/technical-writing/audience-definition

An unwritten prerequisite assumption does not make a claim false, it makes it unreadable; of the 18 claims still true at the third version, the number readable for the new reader is 3 with no audience definition and 18 with it fully written.

The previous lesson closed the taxonomy's last gap: with release notes and
troubleshooting content added, all six reader questions now fall into a type. The
document set knows which question gets answered in which type.

A type answers the question only if the reader can resolve the sentence. Being true is
not enough: if the reader lacks the prerequisite the sentence assumes, and the document
never wrote that assumption, the sentence is not an answer for the reader — it is a
**wall**. This lesson's question is: how many claims does a single unwritten prerequisite
assumption make unreadable?

#### Who the Reader Is Is an Unwritten Decision

Every technical text is written for someone. While writing, the author carries a reader
in mind: what they know, what they have already set up, which terms they recognize. This
table is a set of assumptions, and it always exists. Writing it does not create it, only
makes it visible. Not writing it does not remove it, only makes it undiscussable.

An unwritten assumption fails in a specific way. The reader reaches the sentence, cannot
resolve it, and cannot tell **why**. Was it a term they were supposed to know, or a term
the document was supposed to define? A reader who cannot make this distinction either
abandons the text or searches in the wrong place. The cost is not the gap in knowledge
itself; it is that gap going **unnamed**.

A written assumption does not make the reader knowledgeable. All it does is turn the wall
into a **sign**: this text assumes you know this. The reader can choose to close that
gap, go to another source, or decide the text is not for them. All three are a decision;
a wall offers none.

#### Two Separate Axes

Up to this point, the course measured a single axis: **is a claim still true**. The
second axis this lesson adds is **readability**: can the reader resolve that sentence.
The two axes are independent and produce four combinations.

A claim that is **true and readable** is the document's real capital; it closes the
reader's question. A claim that is **true but unreadable** is wasted effort: the
sentence is correct, no one benefits from it. A claim that is **stale and readable** is
the most damaging; the reader understands it, applies it, and gets it wrong. A claim
that is **stale and unreadable** does no harm and no good.

This distinction is a measurement result, not a classification choice. The same 56
claims can be counted separately on the two axes, and the two numbers can be seen not
tracking each other. Writing a prerequisite assumption does not delay a claim's
**staleness**; it makes it **readable**. Since surface frequency does not change, the
stale count stays the same across all three regimes.

#### What an Audience Definition Writes

A measurable audience definition writes four things: the reader's **role**, the
prerequisites assumed known, what is left **out of scope**, and what the reader can do
by the end of the text. A role is not a person; the same person can read in two
different roles in the same day.

```text
# example document excerpt, not executed

weak audience definition
  This document is for anyone who wants to use the product.

explicit audience definition
  Role: a reader running an already-installed instance of the invented
  product for the first time.
  Assumed known: changing directories and reading files at the command
  line.
  Assumed not known: the product's data model, identity layout,
  concurrent operation.
  Out of scope: deployment and scaling.
  By the end, the reader can: run a single measurement and read its
  output.
```

The difference between the two definitions is not style. From the first, no decision can
be drawn about any claim: the set "everyone" excludes no prerequisite, so no sentence can
be marked "too much for this reader" or "too little for this reader." The second lets
every claim be matched: is the prerequisite it needs on the list or not. **A measurable
audience definition is the precondition for a document being auditable.**

The out-of-scope line matters in particular, because it closes off a question on its
own. A reader looking for deployment learns in the first ten lines that what they are
after is not here, and leaves without reading the text. This is not a loss; it is a
correct redirection.

The measurement's assumptions:

- **WD1** — The 56 claims come from the shared fixture: four types, four surfaces, and
  17 claims embedded in a runnable example. The oracle is the fixture itself; we know
  which claim is bound to which surface and which prerequisite it needs, because we
  wrote it.
- **WD2** — Every claim needs one or two prerequisites; prerequisites are chosen from
  five headings. The distribution is generated by the fixture and does not look at the
  claim's type — a prerequisite is not a surface.
- **WD3** — Three reader roles are measured: the new reader knows one of the five
  prerequisites, the practitioner three, the maintainer all five. A role is not a
  person, it is a **reading condition**.
- **WD4** — A claim is counted **readable** if every prerequisite it needs is either
  known to the reader or written in the audience definition. A written assumption does
  not make the reader knowledgeable; it names the gap and lets the reader close it. The
  measurement counts this naming as readability.
- **WD5** — Three audience-definition regimes are tried: **none** (no assumption
  written), **partial** (two assumptions written), **full** (all five written).
- **WD6** — Readability does not change staleness. Whether a claim is stale looks only
  at the surface it is bound to and the version; the audience definition does not enter
  this calculation.

#### Measurement

```python
"""Audience definition: how many claims does an unwritten prerequisite make unreadable.

Part 1 - the spread of prerequisites across claims.
Part 2 - three reader roles and three audience-definition regimes.
Part 3 - at the third version, where true/stale/silent crosses with readability.
"""
SEED = 20260816
TYPES = {
    "tutorial":      {"flow": 9, "signature": 3, "name": 2, "concept": 1},
    "how-to":        {"flow": 6, "signature": 5, "name": 2, "concept": 1},
    "explanation":   {"flow": 1, "signature": 1, "name": 1, "concept": 9},
    "reference":     {"flow": 0, "signature": 13, "name": 2, "concept": 0},
}
FREQUENCY = {"signature": 2, "flow": 3, "name": 5, "concept": 12}
EMBEDDED = {"tutorial": 0.60, "how-to": 0.70, "explanation": 0.10, "reference": 0.00}
PREREQUISITES = ("setup", "command line", "data model", "identity", "concurrency")
ROLES = {"new reader": {"command line"},
         "practitioner": {"command line", "setup", "data model"},
         "maintainer": set(PREREQUISITES)}
DEFINITION = {"none": set(), "partial": {"setup", "command line"},
              "full": set(PREREQUISITES)}


def rng(seed):
    d = seed % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def claims():
    entries, no = [], 0
    for kind, mix in TYPES.items():
        embedded_target = 0
        for surface, count in mix.items():
            for _ in range(count):
                no += 1
                embedded_target += EMBEDDED[kind]
                embedded = embedded_target >= 1 and surface != "concept"
                if embedded:
                    embedded_target -= 1
                entries.append({"no": no, "type": kind, "surface": surface,
                                 "embedded": embedded})
    return entries


def assign_prerequisites(entries, seed=SEED):
    """This lesson's one addition: the prerequisites every claim needs."""
    r = rng(seed)
    for i in entries:
        needed, count = set(), 2 if r(4) == 0 else 1
        while len(needed) < count:
            needed.add(PREREQUISITES[r(len(PREREQUISITES))])
        i["prereq"] = needed
    return entries


def stale(i, version):
    return version // FREQUENCY[i["surface"]] >= 1


def readable(i, role, written):
    """A prerequisite the reader lacks and the document never wrote makes a claim unreadable."""
    return not (i["prereq"] - ROLES[role] - written)


A = assign_prerequisites(claims())
print(f"claims {len(A)} | embedded in example {sum(i['embedded'] for i in A)} | "
      f"needing two prerequisites {sum(len(i['prereq']) == 2 for i in A)}")
print("prerequisite   " + "  ".join(f"{o} {sum(o in i['prereq'] for i in A)}"
                                    for o in PREREQUISITES))
print()
print(f"{'reader role':>13s}" + "".join(f" {'def. ' + t:>12s}" for t in DEFINITION))
for role in ROLES:
    line = f"{role:>13s}"
    for t, written in DEFINITION.items():
        line += f" {sum(readable(i, role, written) for i in A):12d}"
    print(line)
print()
closed = sum(readable(i, "new reader", set()) for i in A)
print("prerequisite written alone for new reader   claims opened")
for o in PREREQUISITES:
    opened = sum(readable(i, "new reader", {o}) for i in A)
    print(f"  {o:40s} {opened - closed:6d}")
print()
print("version 3, new reader   true+readable  true but unreadable  stale  silent")
for t, written in DEFINITION.items():
    true_ = [i for i in A if not stale(i, 3)]
    stale_ = [i for i in A if stale(i, 3)]
    silent = [i for i in stale_ if not i["embedded"]]
    read = sum(readable(i, "new reader", written) for i in true_)
    print(f"  def. {t:12s} {read:14d} {len(true_) - read:20d}"
          f" {len(stale_):6d} {len(silent):7d}")
```

```
claims 56 | embedded in example 17 | needing two prerequisites 13
prerequisite   setup 16  command line 11  data model 10  identity 13  concurrency 19

  reader role    def. none def. partial    def. full
   new reader            5           19           56
 practitioner           27           27           56
   maintainer           56           56           56

prerequisite written alone for new reader   claims opened
  setup                                        14
  command line                                  0
  data model                                    7
  identity                                      9
  concurrency                                  14

version 3, new reader   true+readable  true but unreadable  stale  silent
  def. none                      3                   15     38      24
  def. partial                   7                   11     38      24
  def. full                     18                    0     38      24
```

#### What the Role Charges

The second table places the three roles side by side, and the difference between them
is not a matter of style, it is a matter of **magnitude**. With no audience definition,
the new reader can read **5** of 56 claims. From the same document, the maintainer reads
all **56**. Two readers look at the same text, and one of them gets a tenth of it.

This table turns the author's most common mistake into a number: the person writing is
nearly always in the **maintainer** role. They know the product, they have done the
setup, they designed the data model. Looking from their own role, they see the document
give **56/56** and find the text sufficient. For the new reader, the number is **5**, and
the author cannot see this gap on their own, because the missing prerequisite is not
missing **in them**.

The middle role is also instructive. The practitioner gets no benefit at all from the
partial definition: **27** and **27** are the same. The practitioner already knows the
two assumptions the partial definition writes. **An audience definition that is not
written for the least-knowing role opens nothing.** A written line's value is measured
not by its correctness but by **whose gap it names**.

#### How Many Roles a Document Can Carry

The table directly raises a question: can the same document carry both the new reader
and the maintainer? The numbers say no. A text written for the new reader repeats, line
by line, prerequisites the maintainer already knows; a text written for the maintainer
gives the new reader **5/56**. There is no setting in between where a single text serves
both, because claims opened and lines repeated move in opposite directions.

The course's first topic established the four types as written for different reader situations; it
is this lesson that binds the mapping to a role. Tutorial is written for the **new reader** role,
how-to for the **practitioner** role, reference for the **maintainer** role; explanation is read by
all three, but at different depths. So the audience definition is written not for the whole
document but **for each type separately**, and each type declares its own role.

The measurable consequence is this: if a text has a high count of unreadable claims, one
of two separate defects is present. Either the audience definition is missing — the fix
is writing a few lines. Or the text is the **wrong type**: a text written as reference is
being read like a tutorial, and the fix is not writing, it is **splitting**. The audience
definition is the only way to tell these two defects apart; without writing the
definition, which defect is present cannot be known.

#### The Most Expensive Unwritten Assumption

The third table gives the weight of individual assumptions. For the new reader, the
**concurrency** and **setup** assumptions each open **14** claims on their own;
**identity** opens 9, **data model** opens 7. **Command line** opens **0** — because the
new reader already knows it, and writing it adds nothing.

The numbers do not add up: 14 + 0 + 7 + 9 + 14 totals **44**, while the full definition
opens from 5 to 56, that is, **51** claims. The gap comes from the **13** claims that
need two prerequisites; those open only once **both** assumptions are written. This
explains why writing an audience definition **piece by piece** pays off less than
expected.

The practical conclusion: order matters when writing an audience definition, and what
should decide the order is **how many claims it opens**, not ease of writing. If a
five-line section opens 51 claims, those five lines are the document's most productive
five lines.

The smallest measurable difference in this set is **1/56 = 0.0179**. The 7, 9, and 14
claims opened by individual assumptions are far above this band and comparable. One
assumption opening a single claim more than another cannot be defended with this set;
that order would change if the fixture changed.

#### Where It Crosses With Staleness

The last table joins the two axes, and this is where the lesson's tightest conclusion
sits. At the third version, **38** of the document's claims are stale, **24** of those
are silently stale, leaving **18** true claims. These three numbers are the same across
all three regimes: writing an audience definition does not extend any claim's life.
Surface frequency did not change, and neither did staleness.

The only column that changes is readability. With no audience definition, of the **18**
surviving true claims, the number readable for the new reader is **3**. That is, the
document carries **18** still-true sentences at the third version, and the new reader
cannot reach **15** of them. With the full definition, **18** of the same 18 sentences
become readable.

These two numbers need to stand side by side, because that keeps two separate jobs from
being confused. Fighting staleness is a **maintenance** job: tracking surfaces, running
audits, moving to generated content. Fighting unreadability is a **writing** job, and it
is done **once**: once written, the audience definition is bound to the concept surface,
the slowest of all surfaces. As long as the product's concepts do not change, the
audience definition stays true; a reference line bound to signature goes stale every two
versions, while the audience definition renews once every twelve.

#### Summary

- Who the reader is, is an assumption in every text; writing it does not create it, it
  makes it discussable. An unwritten assumption's cost is not the reader's lack of
  knowledge, it is that gap going unnamed.
- Readability and staleness are two independent axes: a claim can be true and
  unreadable, or stale and readable. The most damaging combination is stale and
  readable.
- With no audience definition, the new reader reads **5** of 56 claims, the maintainer
  all **56**. The author is usually in the maintainer role and cannot see this gap on
  their own.
- The partial definition opens nothing for the practitioner role (**27** and **27**);
  individual assumptions open **14, 0, 7, 9, 14** claims, and their sum does not reach
  the **51** the full definition opens, because **13** claims need two assumptions at
  once.
- At the third version, stale **38**, silent **24**, true **18** stay the same across
  all three regimes; the audience definition changes only how many of those 18 true
  claims are readable: **3** versus **18**.

#### Next Step

An audience definition lets a sentence be **resolved**; it does not let it be **found**.
In a document written with the full definition, the new reader can now read all 56
claims, but how many lines they need to scan to reach the one claim they are looking for
has not been measured yet. The next lesson looks at heading layout: how many steps sit
between the reader's question and the claim that answers it, which heading structure
shortens that distance, and which surface the heading itself is bound to.

### Structure and Headings

Source: https://academia.sh/en/courses/technical-writing/structure-and-headings

Heading layout measures the distance between the reader's question and its answer; across thirty-six visits, headingless text takes an average of 26.38 steps, type-headed 7.29, two-tier 7.00, and the two-tier layout pays for that gain by carrying 24 silently stale headings at the sixth version.

The previous lesson wrote the audience definition and measured the new reader resolving
all 56 claims with the full definition. Being able to resolve is not being able to
**reach**. The reader does not read a document start to finish; they arrive with a
question and search for the sentence that answers it.

This lesson's question is the cost of that search: **how many steps sit between the
reader's question and the claim that answers it, and how much does heading layout
shorten that distance?** A second question comes with it, tied to the course's own
axis: a heading is itself a sentence — does it go stale too?

#### The Reader Does Not Read, They Scan

The reader of a technical text is not the reader of a literary text. They have a task in
hand, they are standing in the middle of that task, and they arrive at the text with a
question. Deciding to read the text from the start is rare; the norm is **scanning**.
The eye jumps from heading to heading, stops if one resembles the question, reads what
is under it, and moves on if it does not.

**Scannable text** is text built around this behavior. The measure of scannability is
not aesthetic; it is the **number of units** the reader has to read before reaching the
sentence they are looking for. This number can be measured, because both headings and
claims are countable units.

The measure's definition is this: reading a heading and saying "not my question" is
**one step**; reading a claim and saying "not my answer" is also **one step**. Both jobs
spend the reader's attention to the same degree. This equating may look crude, but
converting both into the same unit is the only way to see what headings actually buy.

#### The Axis of Sectioning

Sectioning a document has more than one axis, and the axis chosen sets the cost of the
search. The course's first topic already gave one axis: **type**. A second axis comes
from the product itself: **topic**. A third option is nesting the two.

```text
# example heading layout, not executed

type-headed          topic-headed          type + topic
  Tutorial              Setup                 Tutorial
  How-To                Starting a              Setup
  Explanation             Measurement           Starting a
  Reference             Output Format             Measurement
                        Authorization           Output Format
                        Error                   ...
                        Settings              How-To
                                                Setup
                                                ...
```

The reader uses these two axes with different confidence. **Type** they infer from the
shape of the question: a reader asking "where do I start" looks for the tutorial, one
asking "what type is this field" looks for the reference. **Topic** they already know;
the reader is in the middle of their task and is aware which topic they are stuck on.
Since both axes are accessible to the reader, both are workable for sectioning.

Not sectioning is a choice too. Headingless text is a single flow, and the reader's only
option is scanning from the start. The measurement carries this option too, as a fourth
row, because it is the point of comparison.

The measurement's assumptions:

- **WD11** — The 56 claims come from the shared fixture: four types, four surfaces, 17
  claims embedded in a runnable example. The oracle is the fixture itself.
- **WD12** — The only thing this lesson adds is every claim's **topic**; the topic is
  chosen from six headings by the fixture's generator and does not look at the claim's
  type.
- **WD13** — A reader visit is a **question–topic** pair. All combinations of the six
  reader questions and the six topics are tried: **36 visits**. The questions come from
  the shared fixture, and two of them fall into no type.
- **WD14** — Reading a heading is one step, reading a claim is one step. The reader
  reads headings **in order** until they reach a matching one; they read the matching
  section's claims in order too.
- **WD15** — The reader correctly infers type from their question and topic from their
  own situation. The measurement is not a comprehension measurement, it is a
  **distance** measurement.
- **WD16** — If the answer is not found, the reader is counted as having scanned every
  heading and every claim; the cost of a not-found visit is the layout's total unit
  count.
- **WD17** — A heading is itself a claim and is bound to a surface: type names are
  bound to **concept**, topic names to **name**. A heading cannot be embedded in a
  runnable example; this is why **every heading that goes stale goes stale silently**.

#### Measurement

```python
"""Structure and headings: in how many steps is the reader's question reached.

Part 1 - the spread of claims across topics and 36 reader visits.
Part 2 - step count in four heading layouts, and where not-found comes from.
Part 3 - a heading is a claim too: the true/stale/silent table of the heading set.
"""
SEED = 20260816
TYPES = {
    "tutorial":      {"flow": 9, "signature": 3, "name": 2, "concept": 1},
    "how-to":        {"flow": 6, "signature": 5, "name": 2, "concept": 1},
    "explanation":   {"flow": 1, "signature": 1, "name": 1, "concept": 9},
    "reference":     {"flow": 0, "signature": 13, "name": 2, "concept": 0},
}
FREQUENCY = {"signature": 2, "flow": 3, "name": 5, "concept": 12}
EMBEDDED = {"tutorial": 0.60, "how-to": 0.70, "explanation": 0.10, "reference": 0.00}
QUESTIONS = {"where do I start": "tutorial", "how do I do this": "how-to",
             "why is this like this": "explanation",
             "what type is this field": "reference",
             "what changed in which version": None,
             "why isn't this working": None}
TOPICS = ("setup", "starting a measurement", "output format", "authorization",
          "error", "settings")
VERSIONS = (0, 1, 2, 3, 6, 12)
# A heading is itself a claim and goes stale according to the surface it is bound to.
HEADING = {"headingless": {}, "type-headed": {"concept": 4},
           "topic-headed": {"name": 6}, "type + topic": {"concept": 4, "name": 24}}


def rng(seed):
    d = seed % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def claims():
    entries, no = [], 0
    for kind, mix in TYPES.items():
        embedded_target = 0
        for surface, count in mix.items():
            for _ in range(count):
                no += 1
                embedded_target += EMBEDDED[kind]
                embedded = embedded_target >= 1 and surface != "concept"
                if embedded:
                    embedded_target -= 1
                entries.append({"no": no, "type": kind, "surface": surface,
                                 "embedded": embedded})
    return entries


def assign_topic(entries, seed=SEED):
    """This lesson's one addition: every claim's topic."""
    r = rng(seed)
    for i in entries:
        i["topic"] = TOPICS[r(len(TOPICS))]
    return entries


def stale(surface, version):
    return version // FREQUENCY[surface] >= 1


A = assign_topic(claims())
VISIT = [(s, k) for s in QUESTIONS for k in TOPICS]
SECTION = {"headingless": ([None], [None]), "type-headed": (list(TYPES), [None]),
           "topic-headed": ([None], list(TOPICS)),
           "type + topic": (list(TYPES), list(TOPICS))}


def search(visit, layout):
    """Reading a heading and reading a claim both cost one step; a miss scans everything."""
    question, topic = visit
    kind, step = QUESTIONS[question], 0
    upper, lower = SECTION[layout]
    for u in upper:
        if u:
            step += 1
            if u != kind:
                continue
        for l in lower:
            if l:
                step += 1
                if l != topic:
                    continue
            for i in A:
                if (u and i["type"] != u) or (l and i["topic"] != l):
                    continue
                step += 1
                if i["type"] == kind and i["topic"] == topic:
                    return step, True
    return sum(HEADING[layout].values()) + len(A), False


print(f"claims {len(A)} | topics {len(TOPICS)} | reader visits {len(VISIT)}")
print("topic distribution: " + ", ".join(
    f"{k} {sum(i['topic'] == k for i in A)}" for k in TOPICS))
gap = [v for v in VISIT if QUESTIONS[v[0]] and not any(
    i["type"] == QUESTIONS[v[0]] and i["topic"] == v[1] for i in A)]
print(f"visit falling into a type {sum(1 for s, _ in VISIT if QUESTIONS[s])} | "
      f"falling into no type {sum(1 for s, _ in VISIT if not QUESTIONS[s])} | "
      f"type exists but no matching claim {len(gap)}")
print("coverage gaps:", [(QUESTIONS[s], k) for s, k in gap])
print()
print(f"{'heading layout':>14s} {'headings':>8s} {'average step':>13s} {'worst':>7s}"
      f" {'found':>7s} {'not found':>11s}")
for layout in HEADING:
    result = [search(v, layout) for v in VISIT]
    found = [a for a, b in result if b]
    print(f"{layout:>14s} {sum(HEADING[layout].values()):8d}"
          f" {sum(found) / len(found):13.2f} {max(found):7d}"
          f" {len(found):7d} {len(result) - len(found):11d}")
print()
print("heading claims - true/stale/silent")
print(f"{'heading layout':>14s}" + "".join(f" {'v' + str(s):>8s}" for s in VERSIONS))
for layout, mix in HEADING.items():
    total, line = sum(mix.values()), f"{layout:>14s}"
    for s in VERSIONS:
        stale_ = sum(n for y, n in mix.items() if stale(y, s))
        line += f" {f'{total - stale_}/{stale_}/{stale_}':>8s}"
    print(line)
```

```
claims 56 | topics 6 | reader visits 36
topic distribution: setup 13, starting a measurement 8, output format 11, authorization 10, error 7, settings 7
visit falling into a type 24 | falling into no type 12 | type exists but no matching claim 3
coverage gaps: [('tutorial', 'starting a measurement'), ('how-to', 'settings'), ('reference', 'output format')]

heading layout headings  average step   worst   found   not found
   headingless        0         26.38      56      21          15
   type-headed        4          7.29      19      21          15
  topic-headed        6          7.90      14      21          15
  type + topic       28          7.00      11      21          15

heading claims - true/stale/silent
heading layout       v0       v1       v2       v3       v6      v12
   headingless    0/0/0    0/0/0    0/0/0    0/0/0    0/0/0    0/0/0
   type-headed    4/0/0    4/0/0    4/0/0    4/0/0    4/0/0    0/4/4
  topic-headed    6/0/0    6/0/0    6/0/0    6/0/0    0/6/6    0/6/6
  type + topic   28/0/0   28/0/0   28/0/0   28/0/0  4/24/24  0/28/28
```

#### What the Heading Shortens

The top table's biggest gap sits between the first and second rows. In headingless
text, the reader reaches the claim they are looking for in an average of **26.38**
steps; in the worst case, **56** units — the whole document. Adding four headings brings
the average down to **7.29**. Writing four lines cuts search cost to **a quarter**.

The second gap is much smaller, and it is the lesson's actual finding. Six topic
headings give **7.90** — **worse** than four type headings. The layout nesting both
axes gives **7.00**, only **0.29** steps better than the type-headed layout.
**Twenty-eight** headings add a quarter of a step on top of what four headings already
give.

Why so little? Because the scan inside a section is already short. Four types split 56
claims into groups of **15, 14, 12, and 15**; a second axis brings one of those groups
down to an average of two or three claims, but by the time the reader reaches that
group, they have already read six more sub-headings. The scanning gained is almost
exactly traded away by the heading-reading paid.

The worst-case column says something different: **56 / 19 / 14 / 11**. A deeper layout
barely moves the average but fixes the worst case. **Sectioning depth is a decision
made not for the average reader, but for the unluckiest one.**

These rows point to a second lever too. In the type-headed layout, heading-reading is at
most four steps; the rest of the average is scanning **inside** a section. So a
section's internal order matters as much as the heading count. Moving a frequently asked
claim to the top of a section does the same job without opening a new heading tier. This
is where the two types' traditional ordering comes from: a tutorial is ordered by
**sequence of steps**, because the reader carries out steps in order; a reference is
ordered **alphabetically**, because the reader knows the name they are looking for and
uses the order like an index. Both orderings answer the same question — how does the
reader decide the next unit?

#### The Fifteen Visits That Are Never Found

**15** of the thirty-six visits find an answer in **none** of the four layouts, and this
number does not change from layout to layout. Headings cannot make findable what a text
does not contain. The fifteen failures come from two separate defects, and confusing
them is costly.

The first is **12** visits: the questions "what changed in which version" and "why
isn't this working" fall into no type. This is not a writing defect; it is the **limit
of the taxonomy** the shared fixture declares — the course's release-notes and
troubleshooting lessons build these two questions as separate types. For these visits,
what is needed is not adding a heading but **writing the missing type**.

The second is **3** visits: the question falls into a type, but not a single claim on
that topic was written in that type. The measurement names them one by one — starting a
measurement in tutorial, settings in how-to, output format in reference. This is a
**coverage gap**, and it is directly a writing list. Heading layout makes these gaps
visible; in headingless text, the same gap is a silent conclusion the reader reaches
only after scanning all 56 units.

The cost of a not-found visit rises as the layout gets deeper: **56** units are scanned
in headingless text, **84** units in the two-tier layout. A deep layout speeds up what
is found while making what is not found more expensive. If a document has many coverage
gaps, going deeper is the wrong investment.

#### A Heading Is Also a Claim

The bottom table brings headings into the course's own axis. A heading is a sentence
and is bound to a surface. Type names are bound to **concept**: tutorial, how-to,
explanation, and reference are independent of the product's naming and stay true up to
the twelfth version — in the table, the four type headings run at **4/0/0** and only
turn **0/4/4** at version 12.

Topic names are bound to **name**, and a name changes once every five versions. Six
topic headings give **0/6/6** at the sixth version. The two-tier layout carries both:
**4/24/24** at version 6, **0/28/28** at version 12. When a product feature's name
changes, the twenty-four sub-headings carrying that name go wrong **at the same
instant**.

The third column is this table's real news: **every stale heading is silent.** A
heading cannot be embedded in a runnable example; no test reports that a heading names
something that no longer exists. The document builds, the page opens, the heading shows
up, and it is wrong.

The decision follows from reading these two tables together. The two-tier layout gains
**0.29** steps on average, and in return carries **24** silently stale headings at the
sixth version. Bringing the worst case from 19 down to 11 is a real gain, but its price
is a maintenance debt. The justification for going deeper cannot be the average; it can
only be the worst case, or the visibility of coverage gaps.

#### Summary

- The measure of scannable text is not aesthetic; it is the number of units the reader
  reads before reaching their question. Reading a heading and reading a claim convert to
  the same unit.
- Headingless text takes an average of **26.38** steps, four type headings **7.29**,
  six topic headings **7.90**, the two-tier layout **7.00**. The first four headings cut
  cost to a quarter; twenty-four sub-headings add only **0.29** steps.
- The worst case falls as **56 / 19 / 14 / 11**: going deeper is a decision made not
  for the average reader, but for the unluckiest one.
- **15** of thirty-six visits find no answer in any layout; **12** are the taxonomy's
  limit, **3** are coverage gaps. Heading layout makes a gap visible, it does not close
  it; the cost of a not-found visit rises from **56** to **84** units in the deep
  layout.
- A heading is a claim: type names are bound to concept, topic names to name, and the
  two-tier layout carries **24** silently stale headings at the sixth version — since no
  heading can be embedded in an example, heading staleness is **always silent**.

#### Next Step

Every measurement here rests on a single silent assumption: the reader can match the
word in their own question to the word in the heading. If a topic heading says
"authorization" and the reader calls the same thing "permission," the search breaks at
the very first step — the reader misses the match, enters the wrong section, or assumes
the text holds two separate things. The next lesson measures this matching: how much
does calling the same concept by two names lower the number of claims a reader can find,
and what exactly does each rule of a style guide measure.

### Style Guide

Source: https://academia.sh/en/courses/technical-writing/style-guide

A style guide is not a list of mandates but a document that states what each rule measures; when the same concept is called by two names, the reader can find only 25 of 56 claims, 52 stay ambiguous, and 23 of the 31 claims a rename scan misses are silent.

The previous lesson's entire measurement leaned on a single silent assumption: the
reader can match the word in their own question to the word in the heading. This
assumption does not always hold. If a document gives the same thing two names in two
places, the word the reader is holding matches only one of the two.

This lesson's question is the cost of **dual naming**: when the same concept is called
by two names, how many claims can the reader reach, and how many stay ambiguous? A
second question stands beside it: what happens when the document called a style guide
gets written without answers to questions like this?

#### Not a Rule, a Measure

Most style guides are written as a list of prohibitions, and a list like this fails for
two reasons. First, the person applying the rule cannot recognize an exception, because
they do not know **what the rule protects**; the rule gets applied blindly or broken
blindly. Second, the rule cannot be argued about: two authors who think differently have
only preference in hand, not a measure.

A measurably written item is different. It writes the rule's name, **what it
measures**, how the measure is taken, and **where the rule falls**.

```text
# example style guide entry, not executed

item written as a mandate
  Rule 7: do not write "node" instead of "unit."

item written as a measure
  Rule 7 — single term.
  What it measures: the number of claims the reader can find by searching
              with their own word, and the number of claims a rename scan
              misses when the term changes.
  How it is measured: every claim mentioning a term is counted; the ones
              written with the primary name are found, the ones written
              with the second name are missed.
  Where it falls: when a term genuinely has two names on the product's
              surface. In that case the document writes both, declares
              one primary, and mentions the other only next to the
              definition.
```

The second form does not carry a prohibition; it carries a **quantity**. When the
author wants to break the rule, they see what they lose and can make the decision with a
reason. Written this way, a style guide is not a document of taste, it is a
**measurement document**, and it is auditable just like the document itself.

#### The Ambiguity Two Names Produce

The same concept being called by two names opens three separate readings in the
reader's head, and the reader cannot tell from the text which one is correct.

The first reading: **the two are the same thing**, the author varied the wording. The
second: **the two are separate things**, an unknown distinction exists and should be
learned first. The third: **one is old**, the product was once named that way and the
document was never updated. All three are supportable by the text; the reader has to
**place a bet**.

The cost of this ambiguity is not only slowing down. A reader who picks the second
reading wastes time searching for a distinction that does not exist; one who picks the
first, if the names really do correspond to different things, performs the wrong
operation. The measurement therefore counts ambiguous claims in a **separate column**:
they are not wrong, they hold a decision **pending**.

#### Voice Consistency

The second rule is not about terms; it is about **whom the sentence is speaking
about**. A technical sentence either **addresses the reader** — states a task to be
done — or **narrates the system** — states something that happens on its own. When the
two mix, the reader makes the same decision in every sentence: is this a job for me, or
something that just happens?

**Voice consistency** moves this decision up to the level of the type. Tutorial and
how-to address the reader, since both carry out a task the reader has in hand;
explanation and reference narrate the system, since neither expects an action from the
reader. Tied to type, voice is not resolved sentence by sentence; it is learned **once,
at the top of the section**.

What this rule measures is not a prohibition either: it is **the number of claims where
the reader can tell whom the action falls to**.

The measurement's assumptions:

- **WD21** — The 56 claims come from the shared fixture: four types, four surfaces, 17
  claims embedded in a runnable example. The oracle is the fixture itself.
- **WD22** — The only thing this lesson adds is the **term** every claim mentions, the
  **name** it uses, and its **voice**. All three are chosen by the fixture's generator
  and do not look at the claim's surface.
- **WD23** — There are eight terms, and each term can have two names. If a term is
  unified, all of its claims use the **primary** name; if not, every claim uses one of
  the two names.
- **WD24** — The reader knows the **primary** name; they cannot reach a claim written
  with the second name by searching.
- **WD25** — If a term appears in the document under both of its names, all of that
  term's claims are counted **ambiguous**; the reader cannot tell from the text whether
  the two names point to the same thing.
- **WD26** — Three style regimes are tried: **free** (no term unified), **partial**
  (the four most frequent terms unified), **single term** (all eight unified).
- **WD27** — A **rename scan** searches for the primary name and fixes every claim it
  finds. A claim written with the second name stays outside the scan and keeps carrying
  the old name; if that claim is not embedded in a runnable example, it makes no noise
  at all.

#### Measurement

```python
"""Style guide: what the same concept called by two names produces in the reader.

Part 1 - term distribution and true/stale/silent at the fifth version.
Part 2 - three style regimes: claims found and claims ambiguous.
Part 3 - voice consistency and what the rename scan misses.
"""
SEED = 20260816
TYPES = {
    "tutorial":      {"flow": 9, "signature": 3, "name": 2, "concept": 1},
    "how-to":        {"flow": 6, "signature": 5, "name": 2, "concept": 1},
    "explanation":   {"flow": 1, "signature": 1, "name": 1, "concept": 9},
    "reference":     {"flow": 0, "signature": 13, "name": 2, "concept": 0},
}
FREQUENCY = {"signature": 2, "flow": 3, "name": 5, "concept": 12}
EMBEDDED = {"tutorial": 0.60, "how-to": 0.70, "explanation": 0.10, "reference": 0.00}
TERMS = ("measurement", "node", "authorization", "threshold", "log", "channel",
         "tag", "checksum")
# The voice each type establishes with the reader: is the job the reader's, or the system's.
VOICE = {"tutorial": "reader", "how-to": "reader",
         "explanation": "system", "reference": "system"}


def rng(seed):
    d = seed % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def claims():
    entries, no = [], 0
    for kind, mix in TYPES.items():
        embedded_target = 0
        for surface, count in mix.items():
            for _ in range(count):
                no += 1
                embedded_target += EMBEDDED[kind]
                embedded = embedded_target >= 1 and surface != "concept"
                if embedded:
                    embedded_target -= 1
                entries.append({"no": no, "type": kind, "surface": surface,
                                 "embedded": embedded})
    return entries


def assign_term(entries, seed=SEED):
    """This lesson's one addition: every claim's term, chosen name, and voice."""
    r = rng(seed)
    for i in entries:
        i["term"] = TERMS[r(len(TERMS))]
        i["second_name"] = r(2) == 0
        i["voice"] = "reader" if r(2) == 0 else "system"
    return entries


def stale(i, version):
    return version // FREQUENCY[i["surface"]] >= 1


A = assign_term(claims())
ranked = sorted(TERMS, key=lambda t: -sum(i["term"] == t for i in A))
REGIME = {"free": (), "partial": tuple(ranked[:4]), "single term": TERMS}


def by_second_name(i, unified):
    """If a term is not unified, a claim may use either of its two names."""
    return i["second_name"] and i["term"] not in unified


print(f"claims {len(A)} | terms {len(TERMS)} | embedded in example "
      f"{sum(i['embedded'] for i in A)}")
print("term distribution: " + ", ".join(
    f"{t} {sum(i['term'] == t for i in A)}" for t in TERMS))
s5 = [i for i in A if stale(i, 5)]
print(f"version 5 - true {len(A) - len(s5)} | stale {len(s5)} | silent "
      f"{sum(not i['embedded'] for i in s5)}  (the name surface changes at the fifth version)")
print()
print(f"{'style regime':>12s} {'dual-named terms':>16s} {'found by own name':>20s}"
      f" {'ambiguous claims':>16s}")
for name, unified in REGIME.items():
    dual = [t for t in TERMS if t not in unified
            and len({i["second_name"] for i in A if i["term"] == t}) == 2]
    found = sum(not by_second_name(i, unified) for i in A)
    ambiguous = sum(i["term"] in dual for i in A)
    print(f"{name:>12s} {len(dual):16d} {found:20d} {ambiguous:16d}")
print()
print(f"{'voice regime':>12s} {'written in type voice':>22s}"
      f" {'reader/system mixed':>22s}")
matches = sum(i["voice"] == VOICE[i["type"]] for i in A)
print(f"{'mixed':>12s} {matches:22d} {len(A) - matches:22d}")
print(f"{'unified':>12s} {len(A):22d} {0:22d}")
print()
print(f"{'rename scan':>16s} {'scanned':>8s} {'found':>8s} {'missed':>7s}"
      f" {'silent of missed':>16s}")
for name, unified in REGIME.items():
    missed = [i for i in A if by_second_name(i, unified)]
    print(f"{name:>16s} {len(A):8d} {len(A) - len(missed):8d} {len(missed):7d}"
          f" {sum(not i['embedded'] for i in missed):16d}")
```

```
claims 56 | terms 8 | embedded in example 17
term distribution: measurement 7, node 10, authorization 4, threshold 8, log 8, channel 6, tag 7, checksum 6
version 5 - true 11 | stale 45 | silent 28  (the name surface changes at the fifth version)

style regime dual-named terms    found by own name ambiguous claims
        free                7                   25               52
     partial                3                   42               19
 single term                0                   56                0

voice regime  written in type voice    reader/system mixed
       mixed                     28                     28
     unified                     56                      0

     rename scan  scanned    found  missed silent of missed
            free       56       25      31               23
         partial       56       42      14                8
     single term       56       56       0                0
```

#### The Reader Who Cannot Find Half

The top table's middle column is the lesson's headline number. In the free regime, when
the reader searches with the name they know, they find **25** of 56 claims. The
remaining **31** stand in the document, stand true, but go unfound by the reader's word.
This is not a writing defect, it is an **access** defect, and it adds to the distance
the previous lesson measured: even finding the right section, the reader does not
recognize the sentence.

The column on the right is heavier. In the free regime, **52** claims are ambiguous —
nearly all 56 make the reader ask "are these two names the same thing" once again.
Ambiguity spreads not to individual claims but to the **term**: the moment a term is
called by two names, every one of that term's claims becomes ambiguous, including the
ones not written with the second name.

The left column reveals a detail: in the free regime, **7** of the eight terms appear
under two names, the eighth does not. That term's four claims all happened to pick the
same name. With no rule, uniqueness can be reached by **coincidence**; but that is not a
discipline, and it breaks with the next sentence written. **The rule's function is not
to produce uniqueness, it is to guarantee it.**

The middle row tells how the rule should be ordered. Unifying the four most frequent
terms raises found from 25 to **42**, and lowers ambiguous from 52 to **19**. Half the
terms closes **two-thirds** of the ambiguity. If a style guide cannot be applied top to
bottom, **ordering terms by frequency** pays off most of it in the first move.

#### When Voice Mixes

The second table gives a blunter result: in the mixed regime, **28** of 56 claims are
written in their type's voice, **28** are not. In one out of every two sentences, the
reader decides for themselves whom the action falls to. In a tutorial, a sentence in the
system's voice hides the task to be done; in a reference, a sentence in the reader's
voice invents a task that does not exist.

This 28 is from the fixture, and what matters is not its size but that **the number of
decisions is proportional to the number of claims**. The voice-consistency rule brings
this decision down from 56 times to **one**: once the type is chosen, the voice is
chosen with it. This is what the rule measures, and there is no case for writing it as a
matter of taste.

#### Who Decides the Primary Name

The measurement does not say one thing: **which** name becomes primary. This gap is the
rule's most critical spot, because if filled wrong, the single-term rule raises the
number to 56 and gains the reader nothing.

The word in the reader's hand does not come from the document; it comes from the
**product's surface**. The reader has read the option they typed, the field they saw,
the message they received. If the document picks a name that is internally consistent
but absent from the product, the word the reader brings touches no claim. The rule's
correct form is therefore not "use a single name" but **"the primary name is the name
that appears on the product's surface."**

The case where a term genuinely has two names in the product is where the rule falls,
and it genuinely happens: the same setting has a command-option name and a
configuration-key name. For a term like this, the document writes both **once**,
declares which is primary, and uses only the primary name after that. Because the
second name appears once, next to the definition, the reader can make the match; because
it does not spread into the claims, the scan does not miss it. Ambiguity is thereby
brought down from 56 claims to **a single line**.

This link ties the term's job directly to the **name surface**. When the product's
name changes, the document's name has to change with it, and this is not a matter of
style maintenance, it is **staleness maintenance**.

#### The Rename Scan

The last table ties the lesson to the course's own axis. When the product renames a
term, the maintainer scans the text and fixes what they find. In the free regime, the
scan finds **25** claims and misses **31** — because the scan searches for the primary
name, and the missed ones are written with the second name.

**23** of the missed are silent. Since these claims are not embedded in a runnable
example, they keep carrying the old name, and no test, no build step, no reader reports
it. The **8** that make noise are the ones embedded in an example; the example fails
because it uses a name that no longer exists. **What the single-term rule really saves
is not the reader's search, it is maintenance's scan.**

The version-5 measurement at the top says why this is urgent. The name surface changes
at the fifth version; at that version, **45** claims are stale, **28** silently stale,
and only **11** stay true. Renaming is the **most frequent form of staleness**, and the
single-term rule brings it down to a job closeable with a single scan. Dual naming
spreads the same job across **double the surface** and makes half of it invisible.

#### Summary

- When a style guide is written as a list of prohibitions, exceptions cannot be
  recognized and the rule cannot be argued about. A measurable item writes the rule's
  name, what it measures, how it is measured, and where it falls.
- Dual naming opens three readings in the reader's head — same thing, separate things,
  one is old — and the text does not say which is correct. Ambiguity spreads to the
  term: in the free regime, **52** claims are ambiguous.
- When the reader searches with the name they know, in the free regime they find **25**
  of 56 claims. Unifying the four most frequent terms raises found to **42** and lowers
  ambiguous to **19**.
- Voice consistency brings the decision of whom the action falls to down from 56
  sentences to one type choice; in the mixed regime, **28** of the claims are not
  written in their type's voice.
- When a term is renamed, in the free regime the scan misses **31** claims and **23**
  of the missed are silent; the **8** that make noise are only the ones embedded in a
  runnable example.

#### Next Step

The last table surfaced a distinction, and it sits at the course's center: **8** of the
31 missed claims made noise, **23** did not. The one thing the noisy ones shared was
being embedded in a runnable example. The natural question, then, is this: how many of a
document's claims are actually embedded, and how far does silent staleness retreat if
the embeddedness ratio is raised? The next lesson measures this ratio, defines what it
means for an example to be **runnable and complete**, and holds its own example to the
same standard.

### Examples and Code Snippets

Source: https://academia.sh/en/courses/technical-writing/examples-and-code-snippets

A claim embedded in a runnable example makes noise when it goes stale, one that is not embedded makes none; 17 of 56 claims are embedded, and of the 56 claims stale at the twelfth version, 39 are silent — 0.6964.

The previous lesson surfaced a distinction. When a term was renamed, the scan missed
**31** claims; **8** of those made noise, **23** did not. The noisy ones shared exactly
one trait: they stood **inside** a runnable example.

This lesson's question is naming that trait and measuring its cost: **how many of a
document's claims are actually embedded, how far does silent staleness retreat if the
embeddedness ratio changes, and where does it stop retreating?**

#### An Embedded Claim and the Sentence Beside It

Around an example, two separate kinds of sentence exist, and they behave completely
differently in the face of staleness.

The first is the **example itself**. An example claims a call's shape, a field's name,
a step's order; these claims are not written as sentences, they are written as **code**.
When the product changes, the example stops working, and this is an **event**: a test
fails, a build step breaks, the reader gets an error message. An **embedded claim** is a
claim whose correctness can be tested by running it.

The second is the **sentence written beside the example**: "this call takes the
threshold field as a percentage." It carries the same information, is bound to the same
thing, but is tested nowhere. When the product converts the threshold field to seconds,
the example still runs — the number is still valid, only its meaning changed — and the
sentence goes wrong **silently**.

The distinction, then, is not measured by "does an example exist." It is measured by
whether **the same claim** stands inside the example or beside it. Most sentences
standing beside can be moved **into** the example: a field's unit can be claimed with a
validation line, a returned field's existence with a print, the importance of order with
the steps actually running in order.

#### Runnable, Complete, Verifiable

Embeddedness is not an intention, it is a property, and it requires all three
conditions at once. The three are defined separately because they **fail separately**.

**Runnable:** the example runs when copied and executed. A missing import line, an
undefined variable, or an option left inside a comment makes the example fail to run.

**Complete:** the example expects no definition from outside itself. A snippet that
uses a variable set up in a previous example fails when copied alone; and the reader
most often copies alone.

**Verifiable:** the example's expected output is written beside it and can be compared.
An example with no written output runs, but **running wrong goes unnoticed**; making
noise requires it to fail, while most staleness arrives not as a failure but as a
different result.

```text
# example document excerpt, not executed

example written as a snippet
  result = measurement.start(threshold = 5)
  print(result.summary)
  Sentence in the text: "the start call takes the threshold field as
  a percentage."

lines needed to turn it into a complete example
  - where `measurement` comes from
  - the context that must be set up before `start` can be called
  - printing that the `summary` field actually comes back
  - the expected output standing beside the example
```

The snippet above meets none of the three conditions. It does not run, because the
first name is undefined. It is not complete, because its context sits elsewhere. It is
not verifiable, because what it should print is not written. **A snippet like this
looks embedded but is not embedded:** it makes no noise when it goes stale, because it
was not running in the first place.

#### This Lesson's Own Example

If this lesson says "runnable, complete, and verifiable example," its own example has
to be exactly that — otherwise the lesson falls into the same state as the snippet
above.

The measurement block below meets all three conditions. It expects no outside
definition: the types, surfaces, frequencies, and embeddedness ratios are all inside the
block. It runs when copied and executed. The unlabeled block that follows is this
block's **actually captured output**; it was not written by hand and can be compared.
Every number claim in this lesson comes from that output; none comes from the prose.

The measurement's assumptions:

- **WD31** — The 56 claims come from the shared fixture: four types, four surfaces. The
  oracle is the fixture itself; we know which claim is bound to which surface and at
  which version it goes stale, because we wrote it.
- **WD32** — A claim is stale if the surface it is bound to has changed at least once
  by that version. Surface frequencies do not change: signature once every **2**
  versions, flow every **3**, name every **5**, concept every **12**.
- **WD33** — An embedded claim **makes noise** when it goes stale; a non-embedded one
  goes stale with nothing happening, and is counted **silent**.
- **WD34** — **A claim bound to the concept surface can never be embedded in an
  example.** An example cannot claim, by running, why something is the way it is; it
  can only claim how it behaves.
- **WD35** — The only thing this lesson changes is the **embeddedness ratio**. The
  types' surface mix and the surfaces' frequencies stay as in the shared fixture.
- **WD36** — Three embeddedness regimes are tried: **snippet** (ratios halved; an
  incompletely written example does not count as embedded), **shared fixture** (the
  fixture's own ratios), **full example** (every type writes its examples completely,
  and reference carries examples too).
- **WD37** — The silence ratio is computed over the stale claims; at versions where no
  claim is stale, the ratio is **undefined**.

#### Measurement

```python
"""Examples and code snippets: an embedded claim makes noise, a non-embedded one does not.

Part 1 - claims embedded, by type.
Part 2 - version by version, true/stale/silent and the silent-to-stale ratio.
Part 3 - the one thing this lesson changes: the embeddedness ratio.
"""
SEED = 20260816
TYPES = {
    "tutorial":      {"flow": 9, "signature": 3, "name": 2, "concept": 1},
    "how-to":        {"flow": 6, "signature": 5, "name": 2, "concept": 1},
    "explanation":   {"flow": 1, "signature": 1, "name": 1, "concept": 9},
    "reference":     {"flow": 0, "signature": 13, "name": 2, "concept": 0},
}
FREQUENCY = {"signature": 2, "flow": 3, "name": 5, "concept": 12}
EMBEDDED = {"tutorial": 0.60, "how-to": 0.70, "explanation": 0.10, "reference": 0.00}
VERSIONS = (0, 1, 2, 3, 6, 12)
# This lesson's one change: the embeddedness ratio. Surfaces and types stay fixed.
REGIME = {
    "snippet": {t: o / 2 for t, o in EMBEDDED.items()},
    "shared fixture": EMBEDDED,
    "full example": {"tutorial": 0.80, "how-to": 0.85,
                      "explanation": 0.40, "reference": 0.60},
}


def claims(ratios=EMBEDDED):
    """Every claim: its type, the surface it is bound to, whether it is embedded."""
    entries, no = [], 0
    for kind, mix in TYPES.items():
        embedded_target = 0
        for surface, count in mix.items():
            for _ in range(count):
                no += 1
                embedded_target += ratios[kind]
                embedded = embedded_target >= 1 and surface != "concept"
                if embedded:
                    embedded_target -= 1
                entries.append({"no": no, "type": kind, "surface": surface,
                                 "embedded": embedded})
    return entries


def stale(i, version):
    return version // FREQUENCY[i["surface"]] >= 1


def measure(entries, version):
    """True claims, stale claims, and the silent share of the stale ones."""
    stale_ = [i for i in entries if stale(i, version)]
    silent = [i for i in stale_ if not i["embedded"]]
    return len(entries) - len(stale_), len(stale_), len(silent)


A = claims()
print(f"claims {len(A)}, types {len(TYPES)}, "
      f"embedded in example {sum(1 for i in A if i['embedded'])}")
print("embedded distribution: " + ", ".join(
    f"{t} {sum(i['embedded'] for i in A if i['type'] == t)}" for t in TYPES))
print()
print(f"{'version':>7s} {'true':>6s} {'stale':>6s} {'silent':>7s} {'silent/stale':>13s}")
for s in VERSIONS:
    true_, stale_, silent = measure(A, s)
    ratio = f"{silent / stale_:.4f}" if stale_ else "-"
    print(f"{s:7d} {true_:6d} {stale_:6d} {silent:7d} {ratio:>13s}")
print()
print(f"{'embeddedness regime':>19s} {'embedded':>9s} {'v12 stale':>10s}"
      f" {'silent':>7s} {'silent/stale':>13s}")
for name, ratios in REGIME.items():
    entries = claims(ratios)
    true_, stale_, silent = measure(entries, 12)
    print(f"{name:>19s} {sum(1 for i in entries if i['embedded']):9d} {stale_:10d}"
          f" {silent:7d} {silent / stale_:13.4f}")
print()
print(f"{'type':>15s} {'embedded':>9s} {'v3 silent':>10s} {'v12 silent':>11s}")
for t in TYPES:
    group = [i for i in A if i["type"] == t]
    print(f"{t:>15s} {sum(i['embedded'] for i in group):9d}"
          f" {measure(group, 3)[2]:10d} {measure(group, 12)[2]:11d}")
```

```
claims 56, types 4, embedded in example 17
embedded distribution: tutorial 8, how-to 9, explanation 0, reference 0

version   true  stale  silent  silent/stale
      0     56      0       0             -
      1     56      0       0             -
      2     34     22      17        0.7727
      3     18     38      24        0.6316
      6     11     45      28        0.6222
     12      0     56      39        0.6964

embeddedness regime  embedded  v12 stale  silent  silent/stale
            snippet         8         56      48        0.8571
     shared fixture        17         56      39        0.6964
       full example        31         56      25        0.4464

           type  embedded  v3 silent  v12 silent
       tutorial         8          5           7
         how-to         9          4           5
    explanation         0          2          12
      reference         0         13          15
```

#### Most Staleness Is Silent

The first table pays off the course's third claim. At the twelfth version, **56** of
56 claims are stale, and **39** of those are silent: **0.6964**. Two-thirds of the
document is wrong, and it is telling no one that it is wrong.

The number is not only large at the last row. Of the **22** claims stale at the second
version, **17** are silent — the ratio is at its highest point, **0.7727**. This is
because the first surface to go stale is **signature**, and most signature claims sit in
reference; reference has no embedded claims. **The part of the document that goes stale
earliest is also its quietest part.**

The ratio drops to **0.6316** and **0.6222** at the third and sixth versions. This drop
is not an improvement: the flow surface enters, most flow claims sit in tutorial and
how-to, and those two types carry embedded examples. So the claims that go stale in this
range are the ones that make noise. At the twelfth version, once the concept surface
also falls, the ratio rises again, because concept claims can never be embedded.

The practical meaning of this ratio is an **illusion**. A team tracking its document's
health by "what broke" gets **17** reports at the twelfth version and fixes all of them.
Once done, they believe the document is error-free. The real number is **56** stale
claims; the unseen **39** stand right where they were. **Fixing noisy staleness does not
make silent staleness visible; it produces evidence that makes the document look
healthy instead.**

The smallest measurable difference in this set is **1/56 = 0.0179**. The differences
between regimes are many times above this and comparable; the gap between 0.6316 and
0.6222 is smaller than a single claim and should not be read as a trend.

#### Raising Embeddedness

The second table shows the one thing this lesson changes. Same 56 claims, same
surfaces, same frequencies; the only thing that changes is how many claims are embedded
in a runnable example.

In the **snippet** regime, embedded claims drop to **8** and silence rises to
**0.8571**. This regime does not describe a document without examples; it describes one
**with** examples that are not complete. A half-written example sits on the page, shows
the reader something, and says nothing when it goes stale.

In the **full example** regime, embedded claims rise to **31**, silence falls to
**0.4464**. Silent claims drop from 39 to **25**: **14** claims stop being silent and
cross into the noise-making class. Reference carrying runnable examples too produces
most of this gap.

The table also shows a **floor**: silence does not zero out. Even at the highest
ratios, **25** claims stay silent, because claims bound to the concept surface can never
be embedded in an example. An example can claim, by running, how something behaves; it
cannot claim why it is that way. Embeddedness is a maintenance tool, and it has a region
it cannot reach; for that region, there is no path other than a document audit.

#### A Copy Is Not Embedded

The table does not ask one thing: what exactly **makes** a claim embedded? A code block
on a page is, on its own, just text; no text reports its own staleness. What makes
noise is that **same text actually being run somewhere**.

There are two ways to do this. The first is the document's own example being run —
like this lesson's measurement block. The second is the example being **pulled** from a
source that is run; the document does not host the code, it displays it.

There is a third case, and it is the most deceptive one: the **copy**. The team keeps
examples somewhere that is tested, and pastes a copy of them into the document too. The
source is tested, stays true, and everyone assumes the examples are tested. But what is
tested is not the copy; when the product changes, the source gets fixed, the document
does not, and the two texts drift apart **silently**. A copied example does not count as
embedded in the measurement; it moves the document straight into the **snippet** column
above and raises silence to **0.8571**. A copy's cost exceeds the cost of having no
example at all: a document with no example misleads no one; a document with a copy
misleads both the reader and the writer.

This standard's second consequence is the example's **size**. Completeness stretches an
example longer; the scannability the previous lesson measured **punishes** length — the
reader pays for every line as a step. The two conditions genuinely conflict, and the
answer is not somewhere in the middle. The right setting is the **smallest complete
example**: not the whole of real usage, but the shortest runnable whole that carries the
claim. Every line cut from an example takes one claim out of being embedded; so the
cutting decision is not aesthetic, it is **measurable** — which claim's noise is being
given up?

Versioning an example alongside the source and putting it through review is not this
topic's job; the course's last topic builds documentation operations separately. What
is measured here is only this: a claim makes noise only if the text carrying it is
**actually run**.

#### The Type Distribution of Silence

The last table compares the four types by silence and reveals a reversal.
**Explanation** is the longest-living type — at the third version it has only **2**
silent claims. At the twelfth version, **12** of its **12** claims are silently stale:
the whole type goes wrong without producing a single warning.

**Reference** does the opposite: it already carries **13** silent claims at the third
version, rising to **15** at the twelfth. It goes stale fast and is silent start to
finish. Tutorial and how-to are in the best shape, with **7** and **5** silent claims
respectively; together the two carry all **17** embedded claims.

The result sets writing priority. The two types carrying the most examples are already
the ones making the most noise; adding one more example there pays off little. The gain
is in the types **with no examples at all**: reference's 15 claims and explanation's 12
are quieter than the rest of the product combined.

#### Summary

- An embedded claim stands **inside** the example and is tested by running it; a
  sentence written beside the example carries the same information but is untested and
  goes wrong silently.
- An example needs all three conditions at once: **runnable, complete, verifiable**. If
  any one fails, the example looks embedded but is not. This lesson's own measurement
  block meets all three, and its output was actually captured.
- At the twelfth version, **56** of 56 claims are stale, **39** silent: **0.6964**. The
  highest silence is at the second version (**0.7727**), because the first surface to go
  stale is signature, and most signature claims sit in example-free reference.
- As the embeddedness ratio changes, silence moves between **0.8571** and **0.4464**; a
  half-written example drops embeddedness from 17 to **8**, a complete example raises it
  to **31**.
- Silence cannot be zeroed out: claims bound to the concept surface can never be
  embedded. The two quietest types are explanation (**12/12**) and reference (**15**);
  that is where adding an example pays off.

#### Next Step

The example is the one element in a document that can test itself. There is a second
element standing beside the text that can never be run under any condition: the
**diagram**. A diagram carries claims too — a component's name, a flow's order, a
concept's structure — and the claims it carries are bound to a surface just like
sentences are. The next lesson measures which surface a diagram is bound to, how that
choice sets its rate of staleness, and why visual staleness is silent **without
exception**.

### Use of Visuals

Source: https://academia.sh/en/courses/technical-writing/use-of-visuals

What sets a diagram's rate of staleness is not the drawing style but the surface it is bound to: an interface diagram goes wrong at the second version, a concept diagram at the twelfth; since a diagram cannot be run, every stale claim it carries is silent, and a mixed diagram silences all 56 of 56 claims by the twelfth version.

The previous lesson established the example as the one element in a document that can
test itself: an embedded claim makes noise when it goes stale. There is a second element
standing beside the text, and it can never be run under any condition.

The **diagram** carries claims too. It claims a component's name, a flow's order, a
field's shape, a concept's structure, and these claims are bound to a surface just like
sentences are. This lesson's question is: **which surface is a diagram bound to, how
does that choice set its rate of staleness, and why is visual staleness silent without
exception?**

Notation itself — what can be shown, what a notation abstracts away, what different
notations of the same thing distinguish — was established in the Modeling and
Representation course and is not repeated here. What is measured here is only whether a
diagram **serves the text**: how long the claims it carries live, and who hears it when
they go wrong.

#### A Diagram Is a Set of Claims

A diagram is not decoration; it is a set of sentences written in **picture form**. Three
boxes and two arrows between them carry at least five claims: that three things exist,
that two of them connect in a specific direction, and the order of that connection. All
of these claims can be true or false; when the product changes, some part of them goes
wrong.

What separates a diagram from text is not carrying claims; it is that **none** of the
claims it carries can be run. When an example fails, it reports it; a picture never
fails. The page builds, the picture stays put, the lines look as they always did, and
the reader trusts them. In the previous lesson's language: **every claim moved into a
diagram loses its chance of ever being embedded.**

The second difference is **search**. When a term is renamed, a text scan cannot enter a
diagram; the old name inside the picture stays outside the scan. The missed-claim
problem the third lesson measured becomes **unavoidable** in a diagram.

These two differences also give the criterion for when a picture is worth drawing. Text
is **linear**: sentences are read in an order, and that order is itself a claim.
Narrating a sequence of steps is text's natural job, since sequence is already the shape
text takes. **Relationships that all hold at once**, by contrast, sequence badly in
text; reading which direction three things connect in requires reading three sentences
and joining them in the reader's head. This is what a diagram buys, and the only thing
it buys. A picture repeating its neighboring paragraph's order has bought nothing and
only added a maintenance debt.

#### The Surface a Diagram Is Bound To

Diagrams are classified not by drawing technique but by **what they show**, and this
classification is directly a surface classification.

An **interface diagram** shows fields, types, and call shapes: the **signature**
surface. A **flow diagram** shows the order of steps: the **flow** surface. A
**component diagram** shows the parts' names and the links between them: the **name**
surface. A **concept diagram** shows why something is built the way it is — which
constraint produced which decision: the **concept** surface.

A diagram can show more than one surface at once, and most, in practice, do. A picture
like this lives as long as the **fastest** surface it carries: the moment a single field
name inside it changes, the entire picture is no longer trustworthy, because the reader
cannot tell which part of it is outdated.

The measurement's assumptions:

- **WD41** — The 56 claims come from the shared fixture: four types, four surfaces, 17
  claims embedded in a runnable example. The oracle is the fixture itself.
- **WD42** — The only thing this lesson adds is **where a claim is carried**: in text,
  or in a diagram. The types' surface mix and surface frequencies do not change.
- **WD43** — **No claim moved into a diagram can be embedded in an example.** A diagram
  cannot be run; no test fails when it goes stale.
- **WD44** — A diagram takes over **all** of the claims on the surface it is bound to.
  This is an upper bound, and it makes the five diagram choices comparable with one
  another.
- **WD45** — A diagram showing more than one surface goes wrong **as a whole** at its
  fastest surface's version.
- **WD46** — Two modes are measured: a **replacing** diagram takes claims out of the
  text, a **duplicating** diagram leaves the same claims in the text and produces one
  more copy of them.
- **WD47** — Whether a claim is stale looks only at the surface it is bound to and the
  version; where it is carried does not change this, it only changes whether it is
  **heard**.

#### Measurement

```python
"""Use of visuals: which surface a diagram binds to and how fast it goes stale.

Part 1 - five diagram choices: claims carried, embedded in text, first wrong version.
Part 2 - true/stale/silent when a diagram replaces text.
Part 3 - the silent claims added when a diagram duplicates text.
"""
TYPES = {
    "tutorial":      {"flow": 9, "signature": 3, "name": 2, "concept": 1},
    "how-to":        {"flow": 6, "signature": 5, "name": 2, "concept": 1},
    "explanation":   {"flow": 1, "signature": 1, "name": 1, "concept": 9},
    "reference":     {"flow": 0, "signature": 13, "name": 2, "concept": 0},
}
FREQUENCY = {"signature": 2, "flow": 3, "name": 5, "concept": 12}
EMBEDDED = {"tutorial": 0.60, "how-to": 0.70, "explanation": 0.10, "reference": 0.00}
# Every diagram is bound to one or more surfaces. A diagram cannot be run:
# no claim it carries can be embedded in an example.
DIAGRAM = {"concept diagram": ("concept",), "component diagram": ("name",),
           "flow diagram": ("flow",), "interface diagram": ("signature",),
           "mixed diagram": ("signature", "flow", "name")}


def claims():
    entries, no = [], 0
    for kind, mix in TYPES.items():
        embedded_target = 0
        for surface, count in mix.items():
            for _ in range(count):
                no += 1
                embedded_target += EMBEDDED[kind]
                embedded = embedded_target >= 1 and surface != "concept"
                if embedded:
                    embedded_target -= 1
                entries.append({"no": no, "type": kind, "surface": surface,
                                 "embedded": embedded})
    return entries


def stale(i, version):
    return version // FREQUENCY[i["surface"]] >= 1


def measure(entries, version):
    stale_ = [i for i in entries if stale(i, version)]
    silent = [i for i in stale_ if not i["embedded"]]
    return len(entries) - len(stale_), len(stale_), len(silent)


def visualize(entries, surfaces, mode):
    """A diagram can replace text or duplicate it; either way it goes silent."""
    if mode == "replacing":
        return [dict(i, embedded=i["embedded"] and i["surface"] not in surfaces)
                for i in entries]
    copy = [dict(i, embedded=False) for i in entries if i["surface"] in surfaces]
    return entries + copy


A = claims()
everything = visualize(A, tuple(FREQUENCY), "replacing")
print(f"claims {len(A)} | embedded in text {sum(i['embedded'] for i in A)} | "
      f"if all moved to diagrams, embedded {sum(i['embedded'] for i in everything)}")
print()
print(f"{'diagram':>18s} {'surface':>17s} {'claims':>6s} {'embedded in text':>17s}"
      f" {'first wrong version':>20s}")
for name, surfaces in DIAGRAM.items():
    group = [i for i in A if i["surface"] in surfaces]
    print(f"{name:>18s} {'+'.join(surfaces):>17s} {len(group):6d}"
          f" {sum(i['embedded'] for i in group):17d}"
          f" {min(FREQUENCY[y] for y in surfaces):20d}")
print()
print("when the diagram replaces text - true/stale/silent")
print(f"{'diagram':>18s} {'version 3':>12s} {'version 6':>12s} {'version 12':>12s}")
for name in ["no diagram"] + list(DIAGRAM):
    entries = A if name == "no diagram" else visualize(A, DIAGRAM[name], "replacing")
    line = f"{name:>18s}"
    for s in (3, 6, 12):
        t, st, si = measure(entries, s)
        line += f" {f'{t}/{st}/{si}':>12s}"
    print(line)
print()
print(f"{'diagram':>18s} {'replacing: claims':>18s} {'silent':>7s}"
      f" {'duplicating: claims':>20s} {'silent':>7s}")
for name, surfaces in DIAGRAM.items():
    d1 = visualize(A, surfaces, "replacing")
    d2 = visualize(A, surfaces, "duplicating")
    print(f"{name:>18s} {len(d1):18d} {measure(d1, 12)[2]:7d}"
          f" {len(d2):20d} {measure(d2, 12)[2]:7d}")
```

```
claims 56 | embedded in text 17 | if all moved to diagrams, embedded 0

           diagram           surface claims  embedded in text  first wrong version
   concept diagram           concept     11                 0                   12
 component diagram              name      7                 3                    5
      flow diagram              flow     16                 9                    3
 interface diagram         signature     22                 5                    2
     mixed diagram signature+flow+name     45                17                    2

when the diagram replaces text - true/stale/silent
           diagram    version 3    version 6   version 12
        no diagram     18/38/24     11/45/28      0/56/39
   concept diagram     18/38/24     11/45/28      0/56/39
 component diagram     18/38/24     11/45/31      0/56/42
      flow diagram     18/38/33     11/45/37      0/56/48
 interface diagram     18/38/29     11/45/33      0/56/44
     mixed diagram     18/38/38     11/45/45      0/56/56

           diagram  replacing: claims  silent  duplicating: claims  silent
   concept diagram                 56      39                   67      50
 component diagram                 56      42                   63      46
      flow diagram                 56      48                   72      55
 interface diagram                 56      44                   78      61
     mixed diagram                 56      56                  101      84
```

#### A Sixfold Difference in Lifespan

The top table's last column is the lesson's shortest result: **12, 5, 3, 2, 2.** Same
drawing effort, same page, same reader — and the time a picture stays true changes
**sixfold**. What decides it is not the picture's beauty, the tool's quality, or its
level of detail; it is **what it shows**.

The concept diagram stays true to the twelfth version, because how a constraint produces
a decision is independent of the product's field names, call shapes, and step order; it
only goes wrong once the decision itself changes. The interface diagram goes wrong at
the second version, since what it shows changes every two versions. The course's second
claim holds once more here: **what sets the rate of staleness is not form, it is the
surface bound to.**

This is why the mixed diagram row is the most expensive choice. It carries **45**
claims — turning most of the document into a picture — and goes wrong **as a whole** at
version **2**: the moment a single signature inside it changes, the reader cannot tell
which part of the picture to trust. A diagram "showing everything together" is not a
virtue; it is **tying its lifespan to its fastest component**.

The middle column gives the second cost. The concept diagram takes **0** embedded
claims from the text — concept claims could never be embedded to begin with. The flow
diagram takes **9**, the interface diagram **5**, the mixed diagram **17** embedded
claims out of the text and into the picture, losing their testability the instant they
are moved.

#### The Document That Goes Silent

The second table shows this loss at the document level, and first says something did
**not** change: the true and stale columns are the same in every row. **18/38** at the
third version, **11/45** at the sixth, **0/56** at the twelfth. A diagram does not
lengthen or shorten any claim's life; **a diagram is not a maintenance tool.**

The only thing that changes is the third number. The diagram-free document has **39**
silent claims at the twelfth version. The concept diagram does not change this
number — it silenced no voice, since it took claims that were already silent. The
component diagram raises it to **42**, the interface diagram to **44**, the flow
diagram to **48**. The mixed diagram raises it to **56**: at the twelfth version, **all**
of the document's claims are stale, and **none** make noise.

The third-version column gives an earlier warning. The flow diagram already raises
silent count from 24 to **33** at the third version; the flow surface changes at
exactly that version, and its nine embedded claims fall silent at that same instant.
**The cost of a visual is paid most on the most frequently changing surface.**

The decision follows directly from these two tables. **What should be drawn is what
cannot be embedded in an example.** Concept claims can never be tested anyway; moving
them into a picture is a free gain — long-lived and silencing no voice. Flow and
signature claims are the document's noise-making part; moving them into a picture is
the same as **deleting a working test**.

#### When the Diagram's Source Is Text

The measurement above counted two separate drawbacks together: a diagram **cannot be
run**, and it **cannot be scanned**. Only one of these comes from the diagram's nature.
The second is a decision about **storage format**, and it can be undone.

A diagram can be stored two ways. It can stand as an **image file** — a scan cannot
enter it, no diff can be taken between two versions of it, no review can read what
changed. Or its **source can stand as text**, and the picture is generated at build
time.

```text
# example diagram source, not executed, showing format only

concept diagram with a text source
  node   measurement-queue  "order must be preserved"
  node   writer-endpoint    "single writer at a time"
  edge   measurement-queue -> writer-endpoint  "the order constraint comes from here"

the same diagram stored as an image file
  (binary content; a scan cannot enter it, no diff can be taken, review
  cannot read it)
```

In the text-sourced form, the third lesson's rename scan **can** enter the picture: when
the `measurement-queue` name changes, the scan finds it exactly as it would in a
paragraph. The diagram thereby leaves the "unavoidably missed" class, and its
maintenance becomes measurable.

This gain's limit is clear and should not be overstated. A text source does not make the
diagram **embedded**: the source is not run, only drawn; no test reports a diagram
showing a link that no longer exists. The staleness rate does not change either — the
picture goes wrong at whatever frequency its surface changes. So the second table's
silence column does not move for this decision. The only thing gained is that silent
staleness becomes **findable by an audit**, not that it becomes heard on its own.

#### Replacing and Duplicating

The last table measures a second decision: does the diagram **replace** the text, or is
it **added beside** it? The second case is common and looks harmless — the same thing
has been both written and drawn, and the reader uses whichever they prefer.

The numbers say it is not harmless. In duplicating mode, the document climbs from 56
claims to somewhere between **63** and **101**, and **no new information is added**;
every added claim copies one already in the text. In return, silent count at the
twelfth version rises from 44 to **61** for the interface diagram, and from 56 to
**84** for the mixed diagram.

The reason is the same thing the third lesson measured. A copy does not update alongside
its source: the text gets fixed, the picture stays as it was, and the reader now holds
**two contradicting sources**. Nothing says which is newer; a picture carries no date.

The rule fits one sentence: **if a diagram serves the text, it replaces the text; if
not, it only adds a maintenance debt.** If a picture says what its neighboring
paragraph already says, one of the two is redundant, and the redundant side is the one
forgotten come update time.

#### Summary

- A diagram is a set of claims, and its claims are bound to a surface just like
  sentences; what separates it is that none of the claims it carries can be run, and a
  text scan cannot enter it.
- What sets the rate of staleness is the surface, not the drawing: the concept diagram
  goes wrong every **12** versions, the component diagram every **5**, the flow diagram
  every **3**, the interface diagram every **2**. A picture showing more than one
  surface goes stale at its fastest surface's rate.
- A diagram changes no claim's lifespan: true and stale counts are the same across
  every choice (**18/38**, **11/45**, **0/56**). The only thing that changes is how
  many of the stale claims are **heard**.
- An embedded claim moved from text to picture loses its voice: at the twelfth
  version, silent count rises from 39 to **48** for the flow diagram, to **56** for the
  mixed diagram — the whole document goes silent.
- What should be drawn is what cannot be embedded in an example: the concept diagram
  never raises the silent count at all. A duplicating diagram, adding no new
  information, grows the claim count to as much as **101** and raises silence to
  **84**.

#### Next Step

Throughout this topic, the document was always measured **as it stood the moment it was
written**: is the audience definition written, how many steps do the headings shorten,
is the term unified, does the example run, what is the picture bound to. All five
measurements were decisions made at the writing desk, and all were made **once**.

But every measurement's second axis was the **version**. At the moment it is written, a
document is **0/56** stale; at the twelfth version, **56/56**; the gap comes not from
writing decisions but from **time passing**. A document, then, is not something written
and finished, it is something that **lives**. The next topic builds exactly this: how a
document is versioned alongside code, how it enters review, how its staleness is
audited, and through which cycle those silent claims become visible.

## Documentation Operations

Versioning documentation and folding it into the review process, source-to-reference generation approaches, detecting and tracking the life cycle of aging content, and turning support requests into documentation gaps.

### Docs Alongside Code

Source: https://academia.sh/en/courses/technical-writing/docs-alongside-code

Versioning documentation in the same repository as code leaves twelve versions of stale debt at 300/672; bringing it into review as well drops that to 43/672; on two surfaces review does not see, 15 claims stay silently stale.

Every measurement up to this point took the document **as it was the moment it was
written.** The four types, their surface mixes, heading layout, and whether examples ran —
all of it was measured at a single moment, version zero. At that moment a document is a
text, and every one of the text's claims is true.

A document does not stay at that moment. What it describes keeps changing, and if the
document does not change with it, the **claim** and reality drift apart. This lesson's
question is not how the document is written, it is where it stands: whether the
documentation lives next to the code or somewhere separate; whether it enters the same
record as the change, or gets gone over on a separate calendar. We will build three
operating regimes and track the same fifty-six claims through twelve versions in all
three.

#### Three Operating Regimes

**Separate calendar.** The documentation lives outside the code; it has its own
repository, its own publishing schedule. No one opens it together with a change. At fixed
intervals — every five versions, in our setup — someone sits down and goes over the whole
document. At that pass every claim is refreshed, then the document is left alone again.

**Versioned alongside code.** The documentation is inside the source tree and enters the
same record as the change itself. This is the core of the **docs as code** approach: the
documentation is not a separate entity, it is a file of the repository. The author, while
changing the code, can also touch the documentation in the same record. They do not have
to.

**Versioned alongside code and brought into review.** The change is read by someone else,
and the reader also sees the documentation. Because the documentation's lines stand on the
same screen as the code's lines, the sentence "you renamed this field, the reference still
shows the old name" becomes possible to construct.

All three regimes spend the same writing effort. What differs between them is **when the
documentation gets looked at**, not how well it is written.

The third regime has two conditions, and both are structural. First, the documentation
file has to stand **inside** the change: a documentation fix filed as a separate record
does not appear on the same screen as the code change it fixes. Second, a missing
documentation update has to be counted by the reviewer as a **defect class**: of two people
looking at the same list, one reads an untouched file as a gap, the other does not. The
measurement assumes the second condition holds; where it does not, the third regime
collapses into the second and the table's gap disappears.

#### The Surface Review Sees

Review's own axis was established in the Code Review and Team Process course: review sees
only in the axis it looks at and only as far as the chunk it reads. That discussion is not
repeated here. What we need is only this boundary: **the reviewer reads what is visible
inside the change.**

This boundary splits the four surfaces in two. **Signature** and **flow** are visible
inside the change: when a field's type changes or the order of steps changes, the lines
producing that change are inside the record, and the reviewer reads both side by side.
**Name** and **concept** are invisible: something's externally given name changes on the
product side, a reasoning changes in a discussion; neither may leave a trace in the source
line. When the reviewer reads the old name in the documentation, they have no reason to
suspect it is wrong.

Where the documentation stands inside the change is visible the same way:

```text
# example change listing, not executed

change: unit field added to the measurement interface
  source/metrics.py                 +14  -3
  source/metrics_validate.py         +6  -0
  docs/reference/metrics.md          +9  -4
  docs/tutorial/first-metric.md       0   0
```

The fourth line is what this lesson cares about. The reference is updated, the tutorial is
untouched. The reviewer sees this list and can notice the tutorial went untouched; but to
notice, they need to know **which claim** of the tutorial is affected by this change. What
they can know is signature and flow; what they cannot is name and concept.

#### The Documentation's Version

Putting the documentation next to the code has a second consequence independent of
review: the documentation enters the repository's versioning scheme. A documentation
change made on a branch belongs to that branch; when a version tag is placed, that tag
points at the documentation too. The criterion the Introduction to Version Control course
established applies directly here — if the record was written to the object database, it
can be brought back. The documentation's state at version three can be read from the
version-three tag.

Documentation run on a separate calendar does not carry this property. It has a single
state: **the current one.** A reader using version three reads a text describing version
six and cannot measure the gap. This is a different kind of wrongness from the staleness
we measure: the claim is true for today's code, false for the code the reader has in
hand. Our measurement does not **count** this, because the oracle knows every version
individually; in a real setup, which version the reader is on is not known, and this
uncertainty cannot be closed by the documentation.

The versioning decision also has a direct cost. If the documentation moves forward on the
same branch as the code, a fix reaches a published version only by being carried over to
that version's branch; on a separate calendar, a fix is instantly visible to everyone.
The docs-as-code approach **binds correctness to the version** and, in exchange,
multiplies the publishing work. Our setup does not measure this cost; the only thing it
measures is whether a claim stays true.

#### The Measurement's Assumptions

- **DO1** — Fifty-six claims, four types, and four surfaces are taken from the shared
  setup. The oracle is the setup itself: because we wrote it, we know which claim is
  bound to which surface and at which version it goes stale.
- **DO2** — Surface frequencies do not change: signature changes once every two versions,
  flow every three, name every five, concept every twelve. A claim goes **stale** the
  moment the surface it is bound to changes.
- **DO3** — Seventeen of the fifty-six claims are embedded in a runnable example. An
  embedded claim makes noise when it goes stale: the example fails. A claim that is not
  embedded makes no sound when it goes stale; this is **silent staleness**.
- **DO4** — In the separate-calendar regime, the document is gone over at versions five
  and ten, and at that pass **every** claim is refreshed. In the versions between, no one
  looks at the document.
- **DO5** — In the versioned-alongside-code regime, only embedded claims are repaired:
  staleness announces itself because the example fails, and it gets fixed in the same
  record.
- **DO6** — In the reviewed regime, claims bound to the **signature and flow** surface are
  repaired in addition to embedded claims; those on the **name and concept** surface are
  not repaired, because they are not visible inside the change.
- **DO7** — The measure's unit is the **stale version-claim** count: at the end of every
  version, the claims standing stale are counted and summed across twelve versions. The
  upper bound is fifty-six times twelve, that is, 672.
- **DO8** — Repair happens within the version the claim goes stale. The difference between
  regimes is not the speed of repair, it is **which claim never gets touched at all.**

#### Measurement

```python
"""The effect of bringing documentation into review on staleness.

Part 1 - three operating regimes, stale version-count over twelve versions.
Part 2 - surfaces' embeddedness and visibility-in-review distribution.
"""
TYPES = {
    "tutorial":    {"flow": 9, "signature": 3, "name": 2, "concept": 1},
    "how-to":      {"flow": 6, "signature": 5, "name": 2, "concept": 1},
    "explanation": {"flow": 1, "signature": 1, "name": 1, "concept": 9},
    "reference":   {"flow": 0, "signature": 13, "name": 2, "concept": 0},
}
FREQUENCY = {"signature": 2, "flow": 3, "name": 5, "concept": 12}
EMBEDDED = {"tutorial": 0.60, "how-to": 0.70, "explanation": 0.10,
            "reference": 0.00}
SEEN = ("signature", "flow")   # surfaces code review notices
OVERHAUL = (5, 10)             # versions when the separate-calendar doc is overhauled
LAST = 12


def claims():
    entries, no = [], 0
    for kind, mix in TYPES.items():
        embedded_target = 0
        for surface, count in mix.items():
            for _ in range(count):
                no += 1
                embedded_target += EMBEDDED[kind]
                embedded = embedded_target >= 1 and surface != "concept"
                if embedded:
                    embedded_target -= 1
                entries.append({"no": no, "type": kind, "surface": surface,
                                 "embedded": embedded})
    return entries


def changed(version):
    return {s for s, f in FREQUENCY.items() if version % f == 0}


def repaired(regime, claim, version):
    if regime == "separate calendar":
        return version in OVERHAUL
    if regime == "versioned together":
        return claim["embedded"]
    return claim["embedded"] or claim["surface"] in SEEN


REGIMES = ("separate calendar", "versioned together", "versioned + reviewed")


def run(regime, entries):
    true = {i["no"]: True for i in entries}
    debt = 0
    for version in range(1, LAST + 1):
        d = changed(version)
        for i in entries:
            if i["surface"] in d:
                true[i["no"]] = False
            if not true[i["no"]] and repaired(regime, i, version):
                true[i["no"]] = True
        debt += sum(1 for i in entries if not true[i["no"]])
    stale = [i for i in entries if not true[i["no"]]]
    silent = [i for i in stale if not i["embedded"]]
    return debt, len(entries) - len(stale), len(stale), len(silent)


L = claims()
print(f"claims {len(L)}, embedded in example {sum(1 for i in L if i['embedded'])}, "
      f"surface review sees {sum(1 for i in L if i['surface'] in SEEN)}")
print()
print(f"{'regime':>20s} {'stale version-claims':>21s} {'true':>6s} {'stale':>6s} "
      f"{'silent':>7s}")
for regime in REGIMES:
    debt, true, stale, silent = run(regime, L)
    print(f"{regime:>20s} {f'{debt}/{len(L) * LAST}':>21s} {true:6d} {stale:6d} "
          f"{silent:7d}")
print()
print(f"{'surface':>10s} {'claims':>6s} {'embedded':>8s} {'visible in review':>19s}")
for y in FREQUENCY:
    g = [i for i in L if i["surface"] == y]
    print(f"{y:>10s} {len(g):6d} {sum(1 for i in g if i['embedded']):8d} "
          f"{'yes' if y in SEEN else 'no':>19s}")
```

```
claims 56, embedded in example 17, surface review sees 38

              regime  stale version-claims   true  stale  silent
   separate calendar               299/672      7     49      35
  versioned together               300/672     17     39      39
versioned + reviewed                43/672     41     15      15

   surface claims embedded   visible in review
 signature     22        5                 yes
      flow     16        9                 yes
      name      7        3                  no
   concept     11        0                  no
```

The right three columns give the state at the end of the twelfth version: how many claims
stayed true, how many are stale, how many of the stale ones are silent.

#### Same Debt, Different Distribution

The first two rows' totals are nearly identical: **299** and **300**. A document run on a
separate calendar and a document versioned alongside code but not brought into review
spend almost the same amount of time wrong across twelve versions. Moving it next to the
code alone produced no gain.

The totals are equal; the distributions are entirely different. In the separate-calendar
regime, no claim is permanently stale — the passes at versions five and ten reset
everything — but between two passes, **every kind of claim** stands stale. Only **7**
claims stay true at version twelve, and these are the ones whose surface has not changed
since the last pass: the name claims refreshed at version ten, because name changes once
every five versions and its next change is at version fifteen. The number seven is the
full count of claims bound to the name surface.

This also explains where the separate-calendar regime's debt comes from. **If the
overhaul interval is longer than the fastest surface's frequency, that surface is already
stale between two passes.** A five-version interval does nothing against a signature that
changes every two versions: the twenty-two signature claims fall wrong again at the
second version after every pass and stay that way until the next pass. Shortening the
interval lowers the debt but grows the reading work linearly; every pass means
fifty-six claims verified by hand.

In the versioned-alongside-code regime, the table flips: **17** claims are always true and
**39** claims are stale at version twelve. Seventeen is familiar — it is exactly the count
of embedded claims. Thirty-nine is familiar too: **all** of the stale ones are silent.
This regime repairs the one that makes noise immediately and never touches the one that
does not. The documentation is next to the code, it passes before the eye at every
change, and thirty-nine claims stay wrong for years.

**Versioning alongside code is a relocation decision, not a maintenance decision.**
Moving a file's location places no obligation on anyone to read it.

#### What Review Saves and What It Cannot

The third row drops the debt from **300** to **43** — sevenfold. The gap comes from a
single thing: the reviewer also reads the documentation claims bound to the surface the
change touches. The lower table gives the scope: **38** claims are bound to the two
surfaces review sees, more than two-thirds of the fifty-six.

**15** claims remain, and all of them are silent in the table. These are claims bound to
the name and concept surface that are not embedded in an example: **7 − 3 = 4** on the
name surface, **11** on the concept surface. The embedded count being zero in the concept
column is a rule of the setup — a reasoning cannot be embedded in a runnable example.

These fifteen claims produce no warning in any of the three regimes. The example runs,
review passes, no one complains. At version twelve the document carries **41** true
claims and looks quite good to the reader; none of the **15** wrong sentences it carries
shows itself. The course's central problem shows up exactly here, and the next two
lessons build on top of it: **the visible portion of staleness and the existing portion
of staleness are not the same thing.**

The regimes' cost is not equal either. Separate calendar demands two full overhauls; all
fifty-six claims are verified by hand twice. The reviewed regime demands no extra pass at
all, because verification is folded into work already being done — reading the change.
The gap between them is the decision of whether documentation maintenance is a **separate
job** or **a field of an existing job.**

#### What the Measure Does Not Say

The third regime's **43** is a lower bound, and an optimistic one. Assumption DO6 says
review catches every claim it can see; the Code Review and Team Process course's result
showed this is not true — review sees only as far as the chunk it reads even on the axis
it looks at, and it does miss defects. Documentation lines are the most readily skipped
part of that chunk, because they do not block the build from working. In a real setup,
the third row's debt sits somewhere above **43** and below the second row's **300**.

Two numbers are unaffected by this optimism. The **15** claims on the name and concept
surface can never be caught in this regime no matter how careful review is: they are not
visible inside the change, so there is no place to look. The **17** embedded claims are
equally certain in the opposite direction: they need no review at all, because the example
announces itself the moment it fails to run. The choice of regime only changes the fate
of the **24** claims in between.

#### Summary

- Documentation can run in three regimes: reviewed on a separate calendar, versioned
  alongside code, or versioned alongside code and brought into review.
- Separate calendar produces **299/672**, versioned-alongside-code produces **300/672**
  stale version-claims; equal debt, entirely different distribution.
- Versioned-alongside-code leaves **17** true claims at version twelve, and **39** of the
  **39** stale claims are silent; relocation alone is not maintenance.
- Review drops the debt to **43/672** and raises true claims to **41**, because it sees
  the **38** claims on the signature and flow surface.
- The remaining **15** claims are on the name and concept surface, invisible to review,
  and all silent; this number is the starting point of the next lessons.

#### Next Step

Review works because it is **a person reading the claim**, and a reading person is
expensive. There is a cheaper path for one of the surfaces: instead of writing the claim
by hand, **generate** it from the source it is bound to. A generated claim changes along
with its source, and its going stale becomes unthinkable. The next lesson separates
production approaches by class name and measures which surface generation actually saves,
and which two surfaces it never saves at all.

### Documentation Production Approaches

Source: https://academia.sh/en/courses/technical-writing/documentation-production-approaches

Production is not a single thing: generation from a markup language saves no surface at all, generation from source keeps 22 claims true at version twelve, and it cannot touch the 23 claims on the name and flow surface.

The previous lesson showed that review drops the debt sevenfold, but this gain costs a
person also reading the documentation lines at every change. A reading person is
expensive, and their attention is not fixed. There is a cheaper path for one surface:
instead of writing the claim by hand, **generate it from the source it is bound to.**

A generated claim changes on its own when its source changes. No one has to remember it,
no one has to review it; its going stale becomes unthinkable. This lesson's question is
how far this goes. How many of the fifty-six claims can be read from a source, how many
cannot, and which surface are the ones that cannot on? The measurement places three
production approaches side by side with the hand-written document and tracks all four
across the same twelve versions.

#### A Claim's Source

A claim's **source** is the place that determines its correctness. The sentence "this
field's type is an integer" has its source in the line where the field is defined. The
sentence "the measurement is created first, then submitted" has its source in the
mechanism that requires this order. The sentence "this structure is called a unit" has
its source in the decision that gave it the name. The sentence "the unit field is
required, because measurements cannot be compared without a unit" has its source in the
reasoning itself.

The difference is this: **the first source is machine-readable, the other three are
not.** A field's type is written inside the source and can be pulled from there. The order
of steps, a name, and a decision's reasoning do not stand anywhere inside the source; only
a person knows them. Production is built on top of this distinction, and this distinction
is production's boundary.

#### Three Approach Classes

Approaches are referred to not by product name but by **class name**; products change,
classes stay.

**Generation from a markup language.** The document is written in a markup language and a
converter turns it into a publishable form. Links are checked, a table of contents is
extracted, page layout is applied. What is produced is **format**. The text's claims stand
as written and the source is never looked at.

**Generation from a doc comment.** Claims are written inside the source, right next to
the item they describe; a tool collects them into a document. Part of the collected text
is hand-written, part is read from the definition itself: the parameter's name, its type,
the return value. Its scope is narrow — only text describing the source can stand next to
the source. A tutorial or a piece of reasoning does not fit there.

**Generation from source.** The interface's definition is read directly and the reference
text is generated from it. Its scope is wide: whatever the document type, the signature
claims inside it can come from the source. Text obtained this way is called **generated
reference.**

What a generated fragment looks like is below. The setup belongs to a product and is not
executed:

```text
# generated reference fragment, example dump, not executed

submit_metric(value, unit, source=None)

  value    decimal        required
  unit     text           required
  source   text or empty  default: empty

  returns: record id (text)
```

Every line in this fragment is read from the definition. If a field is added, a line is
added; if a type changes, a column changes. What the fragment does not say is equally
clear: why the `unit` field is required, in what order it should be called, and where the
name given to this function comes from cannot be generated.

Generated text has one more property, and it does not enter the measurement: **its order
is the source's order, not the reader's.** Fields are listed in the order they are
defined, functions are arranged by their place in the source. The scannable layout
measured in the Writing Discipline topic is not built here, because the tool doing the
generation does not know which question the reader arrived with. This is not a
shortcoming of generated reference, it is its definition: its completeness comes from the
source, its layout does not. A document's generated sections and its hand-written sections
therefore do not substitute for each other; one is always correct and disordered, the
other ordered and capable of going stale.

#### The Measurement's Assumptions

- **DO11** — Fifty-six claims, four types, and four surfaces are taken from the shared
  setup; surface frequencies do not change. The oracle is the setup itself.
- **DO12** — An approach is defined by two things: **the types it covers** and **the
  surface it reads from source.** A claim of a covered type bound to the read surface
  never goes stale at all.
- **DO13** — Generation from a markup language covers all four types and reads **no
  surface** from source; what it produces is format.
- **DO14** — Generation from a doc comment covers only the **reference** type and reads
  the **signature** surface from source; tutorial, explanation, or how-to text is not
  placed next to the source.
- **DO15** — Generation from source covers all four types and reads the **signature**
  surface from source; a signature claim can be generated from the definition regardless
  of type.
- **DO16** — No approach can read the name, flow, or concept surface; these three
  surfaces' sources do not stand anywhere machine-readable.
- **DO17** — Production does not change example-embeddedness. An embedded claim makes
  noise when it goes stale, a non-embedded one does not; the definition of **silent
  staleness** stays as in the shared setup.
- **DO18** — The measurement does not count production's setup and operating cost; it
  only gives the number of claims staying true, going stale, and going silently stale
  per version.

#### Measurement

```python
"""The surface production approaches save and cannot save.

Part 1 - four approaches, true/stale/silent over six versions.
Part 2 - source-based generation's per-type gain and per-surface limit.
"""
TYPES = {
    "tutorial":    {"flow": 9, "signature": 3, "name": 2, "concept": 1},
    "how-to":      {"flow": 6, "signature": 5, "name": 2, "concept": 1},
    "explanation": {"flow": 1, "signature": 1, "name": 1, "concept": 9},
    "reference":   {"flow": 0, "signature": 13, "name": 2, "concept": 0},
}
FREQUENCY = {"signature": 2, "flow": 3, "name": 5, "concept": 12}
EMBEDDED = {"tutorial": 0.60, "how-to": 0.70, "explanation": 0.10,
            "reference": 0.00}
VERSIONS = (0, 1, 2, 3, 6, 12)
ALL_FOUR = ("tutorial", "how-to", "explanation", "reference")
# approach: (types covered, surface read from source)
APPROACH = {"hand-written": ((), None),
            "from markup": (ALL_FOUR, None),
            "from doc comment": (("reference",), "signature"),
            "from source": (ALL_FOUR, "signature")}


def claims():
    entries, no = [], 0
    for kind, mix in TYPES.items():
        embedded_target = 0
        for surface, count in mix.items():
            for _ in range(count):
                no += 1
                embedded_target += EMBEDDED[kind]
                embedded = embedded_target >= 1 and surface != "concept"
                if embedded:
                    embedded_target -= 1
                entries.append({"no": no, "type": kind, "surface": surface,
                                 "embedded": embedded})
    return entries


def stale(claim, version, approach):
    scope, read = APPROACH[approach]
    if claim["type"] in scope and claim["surface"] == read:
        return False
    return version // FREQUENCY[claim["surface"]] >= 1


def measure(entries, version, approach, kind=None):
    group = [i for i in entries if kind is None or i["type"] == kind]
    stale_list = [i for i in group if stale(i, version, approach)]
    silent = [i for i in stale_list if not i["embedded"]]
    return len(group), len(group) - len(stale_list), len(stale_list), len(silent)


L = claims()
print(f"claims {len(L)}, bound to signature "
      f"{sum(1 for i in L if i['surface'] == 'signature')}, remaining "
      f"{sum(1 for i in L if i['surface'] != 'signature')}")
print()
print(f"{'version':>7s}" + "".join(f" {a:>18s}" for a in APPROACH))
print(f"{'':7s}" + "".join(f" {'true/stale/silent':>18s}" for _ in APPROACH))
for v in VERSIONS:
    line = f"{v:7d}"
    for approach in APPROACH:
        _, true, stale_n, silent = measure(L, v, approach)
        line += f" {f'{true}/{stale_n}/{silent}':>18s}"
    print(line)
print()
print(f"{'type':>15s} {'claims':>6s} {'sig':>4s} {'hand 6':>7s} "
      f"{'source 6':>9s} {'hand 12':>8s} {'source 12':>10s}")
for kind in TYPES:
    sig = sum(1 for i in L if i["type"] == kind and i["surface"] == "signature")
    t, h6, _, _ = measure(L, 6, "hand-written", kind)
    _, s6, _, _ = measure(L, 6, "from source", kind)
    _, h12, _, _ = measure(L, 12, "hand-written", kind)
    _, s12, _, _ = measure(L, 12, "from source", kind)
    print(f"{kind:>15s} {t:6d} {sig:4d} {f'{h6}/{t}':>7s} {f'{s6}/{t}':>9s} "
          f"{f'{h12}/{t}':>8s} {f'{s12}/{t}':>10s}")
print()
print(f"{'surface':>10s} {'claims':>6s} {'read from source':>17s} "
      f"{'true at version 12':>19s}")
for y in FREQUENCY:
    g = [i for i in L if i["surface"] == y]
    true = sum(1 for i in g if not stale(i, 12, "from source"))
    print(f"{y:>10s} {len(g):6d} {'yes' if y == 'signature' else 'no':>17s} "
          f"{true:19d}")
```

```
claims 56, bound to signature 22, remaining 34

version       hand-written        from markup   from doc comment        from source
         true/stale/silent  true/stale/silent  true/stale/silent  true/stale/silent
      0             56/0/0             56/0/0             56/0/0             56/0/0
      1             56/0/0             56/0/0             56/0/0             56/0/0
      2           34/22/17           34/22/17             47/9/4             56/0/0
      3           18/38/24           18/38/24           31/25/11            40/16/7
      6           11/45/28           11/45/28           24/32/15           33/23/11
     12            0/56/39            0/56/39           13/43/26           22/34/22

           type claims  sig  hand 6  source 6  hand 12  source 12
       tutorial     15    3    1/15      4/15     0/15       3/15
         how-to     14    5    1/14      6/14     0/14       5/14
    explanation     12    1    9/12     10/12     0/12       1/12
      reference     15   13    0/15     13/15     0/15      13/15

   surface claims  read from source  true at version 12
 signature     22               yes                  22
      flow     16                no                   0
      name      7                no                   0
   concept     11                no                   0
```

Column headers abbreviate the approach class names: generation from a markup language,
from a doc comment, and from source.

#### The First Two Rows Are Identical

The table's fastest-read finding is that the first two rows are **exactly the same.** The
hand-written document and the document generated from a markup language give the same
triple in all six of six versions: at version twelve, both are **0/56/39**.

The reason is in the definition. Generation from a markup language never looks at the
claims' source; it just shows what the text says in a neater form. Automating publishing
is a real gain — links get checked, versions get separated, format gets unified — but **it
has nothing to do with correctness.** A tool "producing documentation" does not mean what
it produces is claims. The word production covers both jobs with the same name, and the
table shows a fifty-six-claim gap between them. The question to ask when evaluating a
setup is not what the tool produces, it is **which surface it reads from which source**;
if the answer to this question is "none," what is produced is format alone.

#### What Generation from Source Saves

The fourth column gives **56/0/0** at version two; the hand-written column gave
**34/22/17** at the same version. The first crack opens only at version three, when the
flow surface changes. At version twelve, generation from source leaves **22** true
claims — against the hand-written document's **0**.

The number twenty-two reads directly: it is the count of claims bound to the signature
surface. The lower table shows this one by one. In the signature row, **22** stay true at
version twelve — all of them; in the other three rows, **0**. Generation from source
removes one surface entirely and touches none of the other three. The ratio is the
measure's shortest summary: **22 of fifty-six claims can be generated, 34 cannot.**

Generation from a doc comment reads the same surface but covers only one type; at version
twelve it leaves **13** true claims. Thirteen is the count of signature claims in the
reference type. The nine-claim gap between the two is the signature claims inside the
other three types, and they do not fit next to the source — a call example in the middle
of a tutorial cannot stand in the source's doc comment.

#### The Two Surfaces That Cannot Be Saved

Generation from source's **34** stale claims at version twelve break down as: flow **16**,
concept **11**, name **7**. The course's fifth reading brings two of this trio forward,
because both also appear inside the reference type: **name** and **flow**.

Why can these two surfaces not be generated? **Name** is something's externally given
name, and it does not have to match the identifier in the source. The source saying
`submit_metric` does not say whether the document should call it "submitting a
measurement" or "creating a record"; the naming decision sits outside the source and
leaves no trace there when it changes. **Flow** is the order of steps, and it cannot be
derived from individual signatures. All three functions' definitions can be generated
correctly; which one gets called first is written in none of their definitions.

The lower table's second column counts **23** claims on these two surfaces. They are
unaffected by whether generation is set up: they go stale at the same rate whether in a
document with generation set up or without. Generation does not **save** these claims,
and it even makes them more dangerous: the document looks current, the signature tables
are fresh, and the flow sentence between them is silently wrong.

#### Gain by Type

The middle table separates who generation benefits by type. At version six, reference
gives **0/15** hand-written and **13/15** generated from source — this is the same
comparison measured in the Document Types topic. Tutorial climbs from **1/15** to
**4/15**; the gain is three claims, and it stays three at version twelve too.

The gap comes from the types' surface mix. Of reference's fifteen claims, **13** are
bound to signature; of tutorial's fifteen, only **3** are. Tutorial primarily narrates
flow, and flow cannot be generated. In the explanation type the table is even sharper:
only **1** of twelve claims is bound to signature, generation from source gives **1/12**
at version twelve, and the gain is a single claim.

The operating decision that follows is correct and narrow in scope: **generation targets
the reference type.** Setting up generation for the other three types is an investment
the surface mix does not repay in setup cost. Spending the same effort on review and
documentation audit for those types keeps more claims true.

#### Generation's Effect on Silence

The last column's triple at version twelve is **22/34/22**: **22** of thirty-four stale
claims are silent. The same row in the hand-written document gives **0/56/39**.
Generation eliminated twenty-two stale claims, and while the silent count drops from
thirty-nine to twenty-two, the count of **things making noise** drops too.

The arithmetic is short. **5** of the twenty-two signature claims are embedded in a
runnable example. Because generation keeps these claims true, those five examples never
break again. Of the remaining thirty-four claims, the embedded count is **17 − 5 = 12**.
So generation also narrows the channel that announces staleness: **5** of seventeen
warning points close.

This is not a defect, it is a displacement. Before generation, the document produced its
own signal for seventeen of fifty-six claims; afterward, it does so for twelve of
thirty-four. In both cases, the only way to find the rest is for someone to sit down and
**compare the claim against its source.** Generation shrinks this job; it does not
eliminate it.

#### Summary

- A claim can be generated if its source is machine-readable; signature is such a
  surface, name, flow, and concept are not.
- Generation from a markup language produces format, not claims; in the table it gives
  triples **exactly identical** to the hand-written document, and both are **0/56/39** at
  version twelve.
- Generation from source keeps **22** claims true at version twelve; this number is the
  full count of claims bound to the signature surface.
- Generation from a doc comment covers only the reference type and stays at **13**
  claims; the scope gap is nine claims.
- **23** of the **34** claims generation cannot save are on the name and flow surface; the
  document can look current while these claims are silently wrong.
- Generation targets the reference type by surface mix: reference gains **13/15**,
  tutorial **3/15**, explanation **1/12**.

#### Next Step

We now have two defenses in hand, and both are partial: review cannot see name and
concept, generation cannot generate name and flow. What remains from both is claims that
announce their wrongness in no way at all. The next lesson goes to the course's center and
counts that remainder: the four types' true, stale, and silent triples across six
versions, the silence ratio at version twelve, and the per-claim cost of the **documentation
audit** needed to find the silent ones.

### Staleness and Audit

Source: https://academia.sh/en/courses/technical-writing/staleness-and-audit

At version twelve, 56 of 56 claims are stale and 39 are silent; the silence ratio is 0.6964, and finding the silent ones is measured by the per-claim reading cost a documentation audit pays.

The previous two lessons built two defenses, and both stayed incomplete. Review cannot see
claims on the name and concept surface; generation cannot generate claims on the name and
flow surface. What is left over from both defenses is a remainder that announces its
wrongness in no way at all.

This lesson counts that remainder. The course's central question is asked exactly here:
how many of a document's claims go wrong over time, and **how many of the wrong ones say
so on their own?** The second number matters more than the first, because a claim that
announces itself is not a problem, it is a notification. A claim that does not announce
itself is found only when someone goes looking for it — and the name of that search is
**documentation audit.**

#### Two Kinds of Staleness

The shared setup's distinction is simple and does not change through the course.
**17** of fifty-six claims are embedded in a runnable example. When an embedded claim goes
stale, the example fails: the reader copying it sees an error, and if a continuous
integration pipeline runs the example, the pipeline itself breaks. This claim **makes
noise.**

The remaining **39** claims are plain sentences. A field's type, a step's order,
something's name, a decision's reasoning — all of them stand inside the text, and when
they go wrong, nothing happens. The page still opens, the pipeline still passes, the
search still finds it. This is **silent staleness**, and it stands at the center of the
course's measure.

The distinction's operational consequence is this: you have two kinds of evidence about a
document's health, and one is measurable, the other is not. A broken example can be
counted. A silently wrong sentence cannot be counted — if it could, it would already have
been fixed. **The definition of silent staleness is that it does not get itself counted.**
The measurement can only do this with an oracle built into the setup: we know which claim
goes stale at which version, because we wrote it.

#### What a Documentation Audit Does

A **documentation audit** is the job of comparing claims against their source one by one.
A person reads the text, verifies whether every sentence is still true by checking the
source, and flags the wrong ones. This job's cost is measured by the number of claims
read; its gain is the number of silent stale claims found.

An audit's scope is a choice. The whole document can be read, or just one type. The
choice of scope is directly a yield question: **how many silent stale claims are found per
claim read?** Claims that make noise are outside this computation — they are found
regardless of scope, because they have already announced themselves.

An audit's output is a list, not a fix. The setup does not measure whether a found stale
claim gets corrected; the only thing it measures is **whether it gets found.** There is no
way to fix a stale claim that was never found.

```text
# example audit output, not executed

reference / submit_metric
  [stale]  the source field is required        -> the definition has a default value
  [stale]  returns: record number (integer)     -> the definition returns text
  [true]   the unit field is required

tutorial / first measurement
  [stale]  the session opens first, then the measurement is submitted -> order changed
```

The list's format does not matter; what matters is that every line is a **claim.** A
documentation audit does not count pages, does not count paragraphs, it counts claims.
This is the measure's unit.

#### The Measurement's Assumptions

- **DO21** — Fifty-six claims, four types, and four surfaces are taken from the shared
  setup. The oracle is the setup itself; which claim goes stale at which version is
  known.
- **DO22** — Surface frequencies do not change: signature once every two versions, flow
  every three, name every five, concept every twelve. A claim is stale if the surface it
  is bound to has changed at least once by this version.
- **DO23** — Seventeen of fifty-six claims are embedded in an example; an embedded stale
  claim makes noise, a non-embedded one is silent. Embeddedness is determined together
  with the surface, and the concept surface has no embedded claims at all.
- **DO24** — A documentation audit reads **every** claim in its scope and compares it
  against the oracle; it does not miss a stale claim it reads. This is an optimistic
  assumption and gives the upper bound of what an audit can find.
- **DO25** — A stale claim that makes noise is found regardless of audit scope; the
  example is already failing. Audit scope only determines whether **silent** staleness is
  found.
- **DO26** — Audit's cost is the number of claims read. Yield is the ratio of **silent
  staleness found per claim read**, and it is undefined when no claim is read.
- **DO27** — Audit runs separately at two versions: version three and version twelve. The
  gap between the two shows that the audit's target changes with version.
- **DO28** — The set's resolution at fifty-six claims is **1/56**; a difference smaller
  than this cannot be defended with this setup.

#### Measurement

```python
"""Staleness and documentation audit: the cost of finding the silent one.

Part 1 - four types' true/stale/silent triples across six versions.
Part 2 - audit scopes: silent staleness found per claim read.
"""
TYPES = {
    "tutorial":    {"flow": 9, "signature": 3, "name": 2, "concept": 1},
    "how-to":      {"flow": 6, "signature": 5, "name": 2, "concept": 1},
    "explanation": {"flow": 1, "signature": 1, "name": 1, "concept": 9},
    "reference":   {"flow": 0, "signature": 13, "name": 2, "concept": 0},
}
FREQUENCY = {"signature": 2, "flow": 3, "name": 5, "concept": 12}
EMBEDDED = {"tutorial": 0.60, "how-to": 0.70, "explanation": 0.10,
            "reference": 0.00}
VERSIONS = (0, 1, 2, 3, 6, 12)


def claims():
    entries, no = [], 0
    for kind, mix in TYPES.items():
        embedded_target = 0
        for surface, count in mix.items():
            for _ in range(count):
                no += 1
                embedded_target += EMBEDDED[kind]
                embedded = embedded_target >= 1 and surface != "concept"
                if embedded:
                    embedded_target -= 1
                entries.append({"no": no, "type": kind, "surface": surface,
                                 "embedded": embedded})
    return entries


def stale(claim, version):
    return version // FREQUENCY[claim["surface"]] >= 1


def measure(entries, version, kind=None):
    group = [i for i in entries if kind is None or i["type"] == kind]
    stale_list = [i for i in group if stale(i, version)]
    silent = [i for i in stale_list if not i["embedded"]]
    return len(group), len(group) - len(stale_list), len(stale_list), len(silent)


def audit(entries, version, scope):
    """Scope: types read by hand. Embedded staleness is found outside scope too."""
    read = [i for i in entries if i["type"] in scope]
    stale_list = [i for i in entries if stale(i, version)]
    found = [i for i in stale_list if i["embedded"] or i in read]
    found_silent = [i for i in found if not i["embedded"]]
    missed = [i for i in stale_list if not i["embedded"] and i not in read]
    return len(read), len(found), len(found_silent), len(missed)


L = claims()
print(f"claims {len(L)}, types {len(TYPES)}, embedded in example "
      f"{sum(1 for i in L if i['embedded'])}")
print("surface distribution: " + ", ".join(
    f"{y} {sum(1 for i in L if i['surface'] == y)}" for y in FREQUENCY))
print()
print(f"{'version':>7s}" + "".join(f" {t:>18s}" for t in TYPES))
print(f"{'':7s}" + "".join(f" {'true/stale/silent':>18s}" for _ in TYPES))
for v in VERSIONS:
    line = f"{v:7d}"
    for kind in TYPES:
        _, true, stale_n, silent = measure(L, v, kind)
        line += f" {f'{true}/{stale_n}/{silent}':>18s}"
    print(line)
print()
for v in (3, 12):
    _, _, stale_n, silent = measure(L, v)
    print(f"version {v} stale {stale_n}/{len(L)}, silent {silent} "
          f"({silent / stale_n:.4f})")
SCOPES = [("no audit", ()), ("reference only", ("reference",)),
          ("explanation only", ("explanation",)),
          ("tutorial only", ("tutorial",)),
          ("how-to only", ("how-to",)),
          ("all four types", tuple(TYPES))]
for v in (3, 12):
    print(f"\nversion {v}")
    print(f"{'audit scope':>21s} {'read':>7s} {'stale found':>14s} "
          f"{'silent found':>15s} {'silent missed':>13s} {'yield':>7s}")
    for name, scope in SCOPES:
        read, found, found_s, missed = audit(L, v, scope)
        yield_ = f"{found_s / read:.4f}" if read else "—"
        print(f"{name:>21s} {read:7d} {found:14d} {found_s:15d} "
              f"{missed:13d} {yield_:>7s}")
```

```
claims 56, types 4, embedded in example 17
surface distribution: signature 22, flow 16, name 7, concept 11

version           tutorial             how-to        explanation          reference
         true/stale/silent  true/stale/silent  true/stale/silent  true/stale/silent
      0             15/0/0             14/0/0             12/0/0             15/0/0
      1             15/0/0             14/0/0             12/0/0             15/0/0
      2             12/3/1              9/5/2             11/1/1            2/13/13
      3             3/12/5             3/11/4             10/2/2            2/13/13
      6             1/14/6             1/13/4              9/3/3            0/15/15
     12             0/15/7             0/14/5            0/12/12            0/15/15

version 3 stale 38/56, silent 24 (0.6316)
version 12 stale 56/56, silent 39 (0.6964)

version 3
          audit scope    read    stale found    silent found silent missed   yield
             no audit       0             14               0            24       —
       reference only      15             27              13            11  0.8667
     explanation only      12             16               2            22  0.1667
        tutorial only      15             19               5            19  0.3333
          how-to only      14             18               4            20  0.2857
       all four types      56             38              24             0  0.4286

version 12
          audit scope    read    stale found    silent found silent missed   yield
             no audit       0             17               0            39       —
       reference only      15             32              15            24  1.0000
     explanation only      12             29              12            27  1.0000
        tutorial only      15             24               7            32  0.4667
          how-to only      14             22               5            34  0.3571
       all four types      56             56              39             0  0.6964
```

#### Top Table: Four Types, Six Versions

Versions zero and one are empty; no surface has changed yet and all four types keep every
claim. At version two, signature changes and the table splits immediately: reference gives
**2/13/13** while explanation gives **11/1/1**. Two texts, same version, same repository,
written with the same care; one kept eighty-seven percent of its claims, the other kept
thirteen percent. The difference does not come from the writing, it comes from **the
surface bound to.**

At version three, the flow surface changes, and this time tutorial and how-to collapse:
**3/12/5** and **3/11/4**. Explanation survives with **10/2/2**, because nine of its
claims are bound to concept, and concept changes once every twelve. By version six, the
name surface has also turned over; by version twelve, concept turns over too and all four
types reset: **56/56**.

Not all stale counts carry the same weight. At version three, stale is **38**, silent is
**24**; ratio **0.6316**. At version twelve, stale is **56**, silent is **39**; ratio
**0.6964**. The second number is the course's binding result: **roughly seventy percent
of staleness is silent.**

The ratio's climbing with version is not a coincidence. Embedded claims concentrate on the
signature and flow surfaces, and these two surfaces change early; their staleness happens
in the early versions and **makes noise.** Name and concept change late, carry no embedded
claims, and their staleness is silent. As time passes, the silent share grows. **A
document does not just get more wrong as it ages, it also gets more silently wrong.**

Whether this rise is measurable also has to be asked. The set's resolution at fifty-six
claims is **1/56**, that is, **0.0179**; a smaller difference cannot be defended with this
setup. The gap between the two ratios is **0.6964 − 0.6316 = 0.0648**, more than three
times the resolution. The rise is inside the measurement band. By contrast, observing a
half-claim shift between two versions is impossible with this set; the setup is not built
for finer distinctions.

#### Lower Tables: Audit Yield

The `no audit` row is the baseline. At version three, without anyone reading anything,
**14** stale claims are found and **24** silent ones slip through; at version twelve,
**17** are found and **39** slip through. The found count is exactly the embedded claims'
broken examples. This is the only feedback the document gives on its own, and it is
limited to roughly a third of fifty-six claims.

The scope rows show where an audit should be aimed. At version three, reading **reference
only** finds **13** silent stale claims for fifteen claims read; yield **0.8667**. In the
same version, reading **explanation only** finds **2** for twelve claims read; yield
**0.1667**. The gap is more than fivefold, and the effort spent by the person doing the
audit is nearly the same.

The table flattens at version twelve. Reference and explanation both give **1.0000**:
every claim read is a silent stale one, because neither carries an embedded claim and
everything has gone stale. Tutorial gives **0.4667**, how-to **0.3571**; in these two
types, part of the claims read are embedded and were already found — the audit reads them
for nothing.

Two operating rules follow from this. First: **the audit's target is not the type, it is
the surface.** Reference being an early high yield does not come from its writing style,
it comes from thirteen of its claims being bound to signature. Second: **a type with many
embedded claims is a poor audit target.** Staleness there already announces itself; the
audit's reading effort should go to what does not announce itself.

The full-scope row closes with two numbers. At version three, reading all fifty-six claims
finds **24** silent stale ones and yield is **0.4286**; at version twelve it finds **39**
and yield is **0.6964**. This second number is identical to the top table's silence
ratio, and by definition it must be: **a fully scoped documentation audit's yield is
exactly that document's silent-staleness ratio.** However well the audit is run, the
limit of what it can find is how many of the document's claims are silently wrong.

#### What Triggers an Audit

If scope determines audit yield, something has to determine scope. A calendar is a poor
determinant: an audit done every three months reads the same claims regardless of which
surface changed, and part of the effort goes to sentences that never changed at all.

A better determinant is the repository's own record. That a surface changed is written
inside the record that made that change: when a field is added, the signature has
changed; when a step is added, the flow has changed. The Introduction to Version Control
course established how to read this record, and the Branching and Collaboration course
measured which questions history can answer. The use here is narrow: history tells the
audit **which surface** to aim at.

This method has a limit, and it shows up for the second time in the course. History
carries a trace when code changes; **the name and concept surfaces change outside the
code.** There may be no record in history at all that a product's name changed or a
reasoning became invalid. At version twelve, **18** claims stand on these two surfaces,
and all of them are stale. An audit triggered by history never looks for them; they need
another source to pull the trigger.

#### Summary

- Staleness has two kinds: an embedded claim makes noise when it goes stale, a
  non-embedded claim goes silently stale and gives no sign.
- At version two, reference gives **2/13/13**, explanation **11/1/1**; what determines
  two equally carefully written texts' lifespan is the surface they are bound to.
- At version twelve, **56** of fifty-six claims are stale and **39** are silent; the
  ratio is **0.6964**, higher than version three's **0.6316**.
- Without a documentation audit, only embedded staleness is found: **17** are found at
  version twelve, **39** slip through.
- Audit yield varies with scope: at version three, reading reference only gives
  **0.8667**, reading explanation only gives **0.1667**; the target is the surface, not
  the type.
- A fully scoped audit's yield is exactly the document's silence ratio — **0.6964** at
  version twelve.

#### Next Step

A documentation audit finds silent staleness, but it requires someone to sit down and
read fifty-six claims, and that job takes no place on anyone's calendar by itself. As
long as the audit is not done, those thirty-nine claims stand in place and someone reads
them every day. The next lesson looks at those readers: what does a reader who hits a
wrong sentence do, how many report it, and after how many visits does how much of the
silent staleness become visible. The course's final lesson measures the **feedback
loop**, the third claim's only antidote.

### Feedback Loop

Source: https://academia.sh/en/courses/technical-writing/feedback-loop

Silent staleness becomes visible only when the reader reports it: 240 visits expose 31 of 39 silent claims, all 39 require 960 visits, and cost per report climbs from 5.0 to 24.6 visits.

The previous lesson showed that a documentation audit finds silent staleness: if all
fifty-six claims are read, thirty-nine of them turn out to be wrong too. The audit's
problem is not that it cannot find them, it is that it **never starts.** It takes no
place on anyone's calendar by itself, and as long as it is not done, those thirty-nine
claims stand in place.

But the documentation keeps being read during this whole time. Every day someone opens
it, reads a sentence, acts on it, and gets it wrong. This lesson's question is: **how much
of the audit's job does the reader do instead?** Silent staleness's only natural enemy is
the person who hits it and says so. The measurement counts how many of those people it
takes.

#### The Claim the Reader Hits

A reader arrives at the documentation with a question and hits a claim. There are three
possible cases. If the claim is true, the reader does their job and produces no feedback.
If the claim is stale and embedded in an example, the reader sees an error: the example
fails to run, there is a visible malfunction on the screen, and the reader reports it as a
malfunction. If the claim is stale and silent, the reader sees no malfunction — they do
what the document says, do not get the result they expected, and **do not know what
happened.**

The critical part of the third case is that the reader cannot distinguish their own
mistake from the document's mistake. A document is read as an authority; a reader who gets
a result that contradicts it first assumes their own error. Some try again, some give up,
some find their own way, and only a portion say "what is written here is wrong." The
measurement takes this portion as a third.

#### From Support Request to Documentation Gap

The report arrives not as a documentation gap but as a malfunction. Turning it into a
documentation record takes three questions: **which sentence** did the reader act on,
**which surface** is that sentence bound to, and **when** did the surface change.

```text
# example support request and documentation record, not executed

request: "I'm submitting a measurement, getting an error instead of a record id"
  claim relied on : reference / submit_metric — the source field is required
  bound surface    : signature
  changed at version: 2
  record            : stale claim, silent; 21 more claims are bound to the same surface
```

The last line is this job's real gain. A report is not used to fix a single sentence, it
is used to **search for the other claims bound to the same surface.** The reader has
reported one claim; what they actually reported is that a surface changed, and
twenty-two claims are bound to that surface. A single report triggers a documentation
audit with a known target.

#### The Measurement's Assumptions

- **DO31** — Fifty-six claims, four types, and four surfaces are taken from the shared
  setup; the measurement is run at version twelve, where every claim is stale. Making
  noise **17**, silent **39**.
- **DO32** — Every reader visit lands on one claim, and claims are chosen with equal
  probability. In a real setup, interest is not evenly distributed; equal distribution is
  the **optimistic** estimate for coverage.
- **DO33** — A reader who hits an embedded stale claim always reports it; the example has
  failed to run and the malfunction is not in question.
- **DO34** — **A third** of readers who hit a silent stale claim report it; the rest
  count it as their own mistake, give up, or find their own way.
- **DO35** — The same claim being reported more than once is still counted once; what is
  measured is not visits, it is the count of **claims brought into the open.**
- **DO36** — Reporting only brings a claim into the open; the measurement does not count
  the fix. The document does not change across visits and no new staleness is added.
- **DO37** — Randomness comes from the shared setup's generator and is called with a
  single module; the set's resolution at thirty-nine silent claims is **1/39**.

#### Measurement

```python
"""Feedback loop: silent staleness becoming visible through the reader.

Part 1 - reported silent claims as reader visits climb.
Part 2 - remaining silent staleness per type after two hundred forty visits.
"""
SEED = 20260816
TYPES = {
    "tutorial":    {"flow": 9, "signature": 3, "name": 2, "concept": 1},
    "how-to":      {"flow": 6, "signature": 5, "name": 2, "concept": 1},
    "explanation": {"flow": 1, "signature": 1, "name": 1, "concept": 9},
    "reference":   {"flow": 0, "signature": 13, "name": 2, "concept": 0},
}
FREQUENCY = {"signature": 2, "flow": 3, "name": 5, "concept": 12}
EMBEDDED = {"tutorial": 0.60, "how-to": 0.70, "explanation": 0.10,
            "reference": 0.00}
VERSION = 12
VISITS = (30, 60, 120, 240, 480, 960)
REPORT_SHARE = 3      # a reader hitting a silent stale claim reports it: one in three


def rng(seed):
    d = seed % 2147483646 + 1

    def draw(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return draw


def claims():
    entries, no = [], 0
    for kind, mix in TYPES.items():
        embedded_target = 0
        for surface, count in mix.items():
            for _ in range(count):
                no += 1
                embedded_target += EMBEDDED[kind]
                embedded = embedded_target >= 1 and surface != "concept"
                if embedded:
                    embedded_target -= 1
                entries.append({"no": no, "type": kind, "surface": surface,
                                 "embedded": embedded})
    return entries


def stale(claim, version):
    return version // FREQUENCY[claim["surface"]] >= 1


def loop(entries, visit_count):
    """Each visit lands on a claim. An embedded stale claim becomes visible when
    the example is run; a silent stale claim becomes visible only if the reader
    reports it."""
    draw = rng(SEED)
    hit, reported = set(), set()
    for _ in range(visit_count):
        claim = entries[draw(len(entries))]
        if not stale(claim, VERSION):
            continue
        hit.add(claim["no"])
        if claim["embedded"] or draw(REPORT_SHARE) == 0:
            reported.add(claim["no"])
    return hit, reported


L = claims()
STALE = [i for i in L if stale(i, VERSION)]
SILENT = [i for i in STALE if not i["embedded"]]
print(f"at version {VERSION}, claims {len(L)}, stale {len(STALE)}, making noise "
      f"{len(STALE) - len(SILENT)}, silent {len(SILENT)} "
      f"({len(SILENT) / len(STALE):.4f})")
print()
print(f"{'visits':>8s} {'hit':>9s} {'silent reported':>18s} "
      f"{'coverage':>8s} {'visits per report':>24s}")
for v in VISITS:
    hit, reported = loop(L, v)
    b = [i for i in SILENT if i["no"] in reported]
    print(f"{v:8d} {len(hit):9d} {len(b):18d} "
          f"{len(b) / len(SILENT):8.4f} {v / len(b):24.1f}")
print()
_, reported = loop(L, 240)
print(f"{'type':>15s} {'silent':>7s} {'reported at 240 visits':>25s} "
      f"{'remaining':>6s}")
for kind in TYPES:
    group = [i for i in SILENT if i["type"] == kind]
    b = [i for i in group if i["no"] in reported]
    print(f"{kind:>15s} {len(group):7d} {len(b):25d} {len(group) - len(b):6d}")
```

```
at version 12, claims 56, stale 56, making noise 17, silent 39 (0.6964)

  visits       hit    silent reported coverage        visits per report
      30        20                  6   0.1538                      5.0
      60        34                 10   0.2564                      6.0
     120        47                 19   0.4872                      6.3
     240        54                 31   0.7949                      7.7
     480        56                 36   0.9231                     13.3
     960        56                 39   1.0000                     24.6

           type  silent    reported at 240 visits remaining
       tutorial       7                         6      1
         how-to       5                         3      2
    explanation      12                         9      3
      reference      15                        13      2
```

#### The Loop Works, It Works Slowly

The last column closes: **coverage 1.0000**. Once enough readers have come, all the
silent stale claims come into the open. The feedback loop really is the third claim's
antidote; it works independent of documentation audit, without putting anything on
anyone's calendar.

Its cost is in the column on the right. The first thirty visits bring **6** silent claims
into the open: **5.0** visits per report. At two hundred forty visits, coverage reaches
**0.7949** and the cost climbs to **7.7**. Getting the last three claims requires going
from four hundred eighty to nine hundred sixty, and visits per report becomes **24.6**.
**Once coverage saturates, cost rises fivefold**, because the claims remaining are the
ones read the least or the least likely to be reported.

The `hit` column separates this difference. By the four-hundred-eightieth visit, all
fifty-six claims have been hit — meaning the document has been read start to finish. Even
so, the number of silent claims reported is **36**. The three claims in between were hit
but not reported: the reader read them, got it wrong, and did not say so. **Being read is
not the same as being fixed.**

The lower table gives the distribution of two hundred forty visits by type. **13** of
reference's fifteen silent claims and **9** of explanation's twelve are reported. The
remaining eight claims spread across the four types, and no type is fully cleared. This
compares directly with the previous lesson's audit table: the same thirty-nine claims are
found by a fully scoped documentation audit reading **56** claims in a single pass; the
feedback loop finds them with **960** visits, and every one of those visits is a reader
who read a wrong sentence.

The comparison defines the two paths' roles. **Audit is cheap and has to be started; the
loop works on its own and the reader pays its cost.** The two do not substitute for each
other: without the loop, the audit does not know where to look; without the audit, the
loop cannot lower the cost it is paying.

#### Summary

- Silent staleness does not show itself; the only natural way it becomes visible is for
  the reader who hits it to report it.
- A reader's report is turned into a documentation gap with three questions: the
  sentence relied on, the surface it is bound to, the version the surface changed at. A
  single report also targets the other claims bound to the same surface.
- **31** of thirty-nine silent claims come into the open at **240** visits, **39** at
  **960**; coverage reaches **1.0000**, but cost per report climbs from **5.0** visits
  to **24.6**.
- Despite all fifty-six claims having been hit by four hundred eighty visits, the number
  of silent claims reported is **36**; being read is not the same as being fixed.
- A documentation audit finds the same thirty-nine by reading **56** claims; the loop's
  cost is paid by the reader who got it wrong. The two do not substitute for each other.

#### Course Wrap-Up

The course built a single measure and carried it across fifteen lessons: a document's
number is not its length, it is **the number of claims still true** — and how many of the
ones that go stale are **silent**. Every lesson ran its own measurement; every row of the
table below is read from that lesson's own output block.

| lesson | measured | true remaining or silent stale |
|---|---|---|
| The Scope of Technical Writing | claim, type, surface, and embeddedness distribution | 56/56 true at version 0; embedded 17, silent 39; a signature change drops 22 claims, silent share 0.7727 |
| Tutorial, How-To, Explanation, and Reference | the four types' lifespan at version two | explanation 11/12 (0.9167), reference 2/15 (0.1333); lifespan ratio 6.875 times |
| Repository Documents | three repository documents' surface distribution and half-life version | half-life 3 / 3 / 12; at version 12, decision record 0/10 and 10 silent |
| API Reference | comparison of hand-written and generated reference | at version 6, hand-written 0/15, generated 13/15; 2 unrecoverable claims on the name surface |
| Release Notes | number of claims the note format makes datable | raw list 17/56 and 0/39 of the silent ones; meaning list 56/56 and 39/39 |
| Troubleshooting Content | reader decisions symptom-first text meets | symptom–cause–remedy 51/51 (1.0000); covered claims 17/56, unreachable 39 |
| Audience Definition | claims the prerequisite assumption makes readable | true at version 3 is 18; readable climbs from 3 to 18, stale 38 and silent 24 unchanged |
| Structure and Headings | units read before reaching the reader's question | average steps from 26.38 to 7.00; at version 6, the two-tier layout carries 24 silently stale headings |
| Style Guide | ambiguity produced by dual naming | found by searched name in the free regime 25/56; on renaming, missed 31, of which silent 23 |
| Examples and Code Snippets | embeddedness ratio's effect on silence | at version 12, 0/56 true, silent 39 (0.6964); between 0.8571 and 0.4464 by regime |
| Use of Visuals | the surface a diagram is bound to and its noise | true/stale unchanged (0/56); silent climbs from 39 to 56 in the mixed scheme |
| Docs Alongside Code | the effect of bringing documentation into review on staleness | versioned together 300/672 and 39 silent; reviewed 43/672 and 15 silent |
| Documentation Production Approaches | the surface production saves and cannot save | at version 12, generation from source 22/56 true; 22 of the remaining 34 stale are silent |
| Staleness and Audit | silent staleness audit scope finds | at version 12, 56/56 stale and 39 silent (0.6964); without audit, only 17 found |
| Feedback Loop | silent claims reader reporting brings out | 31/39 (0.7949) at 240 visits, 39/39 at 960; cost from 5.0 to 24.6 |

The course following its own rule was not a formality. Every measurement was run, every
output block was taken from the run itself; this lesson's own embedded claims too will
make noise when they go stale.

This course is the last one in the **Software Development Practice** curriculum. Five
courses measured five separate things, and all of them asked the same question of a
different object: what does the work done leave behind?

| course | measure axis |
|---|---|
| Introduction to Version Control | which region a record stands in and its recoverability from there |
| Branching and Collaboration | the question history can answer |
| Advanced Git | the number of objects an operation touches |
| Code Review and Team Process | the axis looked at and the chunk read |
| Technical Writing and Documentation | how many claims are still true, how many are silently stale |

The first row is read from that course's own lessons: the distinction between working
directory, staging area, and object database; the criterion for recoverability being that
the content was written to the object database; reset commits staying in the database;
and unreachable objects not being deleted — all of it builds a single axis.

The five courses together built a single team discipline: recording a change, integrating
it among more than one person, managing history, having it read by someone else, and
narrating it. What all of them share is being language-independent; what was counted was
never the written code itself, it was the work around it. The curricula that follow turn
from this periphery to the center and measure the language itself: the structures a
language offers, the decisions those structures force, and the trace those decisions
leave in the program.

