# House style: what a page here has to do

## TLDR/BLUF

**What this is.** Every rule below is one testable statement with a permanent identifier. Cite the
identifier in a review comment the way a standard's rule is cited, so both sides point at the same
sentence. What they demand: an opening carrying the three labels this section itself carries. Then
sentences that each carry a fact, a number, a constraint or a link, and prose that never describes
its own structure.

**Why you should care.** Applying them costs length, in the places where length was doing nothing --
but the rules protect density explicitly, so this is not a licence to flatten precision into
blandness. Not for you on generated files, nor on the `PD` section itself. The document these rules
were derived from, `SECURE-DEVELOPMENT.md`, left with
[the standards](https://github.com/wshallwshall/secure-development-standards) and answers to that
repository's own house style now.

**How to use it.** Read `PD` first, before touching anything: it is the list of things an editor MUST
NOT cut. The one rule that named specific sections, `PD-8`, is a tombstone -- all four went with the
standards.

---

## How to read these rules

| Element | What it means |
|---|---|
| `OPEN-<n>` | The opening of a page |
| `HS-<n>` | House style, anywhere on a page |
| `PD-<n>` | Protected density. What an editor MUST NOT cut |
| `B-<n>` | A banned construction |
| **MUST**, **MUST NOT** | Absolute. Not meeting one is a defect, not a judgment call |
| **SHOULD** | Ignore it only for a stated reason you have weighed |

An identifier is a permanent name, never a position. A new rule takes the next free number in its
section and is appended. Reword a rule freely under the same identifier; change what it demands and
you allocate a new one.

**`PD` outranks every other section.** Where a `PD` rule and an `HS` rule disagree about the same
text, `PD` wins. The failure this ordering prevents is an editor satisfying a length rule by deleting
a measurement.

---

## OPEN: the opening

The model is `standards/SECURE-DEVELOPMENT.md`, whose opening went from 99 lines to 22 when two
orientation sections were deleted for restating the page rather than opening it.

| ID | Rule | Evidence |
|---|---|---|
| OPEN-1 | The first screen **MUST** answer, in this order: what this is, why the reader should care, and how to use it | The three answers, above the first explanatory section |
| OPEN-2 | A page **MUST** carry a summary section, and **MUST** spell its heading `## TLDR/BLUF` exactly | `tests/test_docs_do_not_drift.py` pins the string and the presence |
| OPEN-3 | An opening **MUST NOT** describe the page's own structure, sections, or reading order | No "this page is organized as", no "first we cover" |
| OPEN-4 | An opening **MUST** state who the page is not for, where that set is non-empty, and **MUST** say it as a plain no. It belongs in the `Why you should care` slot | A sentence a reader can fail |
| OPEN-5 | An opening **SHOULD** link rather than summarise, where the target says it already | A link, not a paraphrase |
| OPEN-6 | A page longer than roughly 2,000 words **SHOULD** name its own starting point | One link, in the `How to use it` slot |
| OPEN-7 | The three answers **MUST** be labelled, verbatim and in order: `**What this is.**`, `**Why you should care.**`, `**How to use it.**`, each opening its own paragraph | `tests/test_docs_do_not_drift.py` pins all three, in order, on every rendered page |

### What OPEN-1 and OPEN-2 demanded before 2026-08-10

Both were reworded rather than reissued, and both changed what they demand, which is normally the
trigger for a new identifier. The change is recorded here instead so that a citation written against
the old text resolves to something rather than to a rule that quietly means something else.

- **OPEN-1** required four answers -- what this is, *what it costs the reader*, who it is not for, and
  where to start. It now requires the three above. "What it costs" is no longer a slot of its own;
  where a page has a real cost to state, it goes in `Why you should care`.
- **OPEN-2** ended with "A page **MAY** have no summary section at all". That permission is gone.
  Every rendered page carries the section, which is what made OPEN-7 gateable: 15 of the 18 pages
  had no summary section on 2026-08-10, so the rule had almost nothing to bind to.

`OPEN-4` was reworded under its own identifier without changing what it demands: the not-for-you
sentence is still required, it is now told which slot to sit in.

---

## HS: house style

| ID | Rule | Evidence |
|---|---|---|
| HS-1 | Every sentence **MUST** carry a fact, a number, a constraint, a link, or an instruction | Delete the sentence and something is lost |
| HS-2 | A section **MUST NOT** restate a fact the page has already stated | One statement, one place |
| HS-3 | A fact stated in two files **MUST** live in one and be linked from the other | One owner per fact |
| HS-4 | Prose **MUST NOT** explain why the document omits something | Say it or do not; the omission needs no defence |
| HS-5 | A count of a set **MUST NOT** appear in prose unless the same page enumerates the set | Counts rot silently |
| HS-6 | A claim about behaviour **SHOULD** name how it was established | "Measured on", "verified against" |
| HS-7 | Tabular content **MUST** be a table; a table whose rows are one clause each **SHOULD** be prose | Shape follows content |
| HS-8 | A destructive command's description **MUST** state what is lost and whether it is recoverable | The loss, named |
| HS-9 | A diagnostic message **MUST NOT** be phrasable as reassurance when the tool could not determine an answer | "Could not tell" reads as "could not tell" |
| HS-10 | A refusal **MUST** say what was refused, why, and the next command, and the command **MUST** be runnable as printed | Three slots, all filled |
| HS-11 | Prose **MUST** be ASCII: no em dash, no smart quotes, no section sign | `scripts/quality/check-ascii.ps1` |
| HS-12 | A normative rule document **MUST** carry a "how to read the rules" section, stable rule identifiers, and an evidence column | The three, present |
| HS-13 | A heading **MUST NOT** be renamed until the repository has been searched for its text | Standards cite headings by name, and only the link-adjacent form is gated |
| HS-14 | Lines **SHOULD** wrap near 100 characters | The wrap |
| HS-15 | A quantity **MUST** be the number where one exists, not a vague determiner | "139", not "nearly all" |
| HS-16 | A markdown link **MUST** sit on one line, text and target both, and that outranks HS-14 | `tests/test_a_links_text_never_wraps.py`. `jekyll-relative-links` matches a link with a pattern whose `.` excludes a newline, so a wrapped one is never rewritten and the published site serves the raw `.md` while github.com renders it correctly. 35 links were in this state on 2026-08-07 |

---

## PD: protected density, and what an editor MUST NOT cut

Read this before editing. Density is not a defect here. These rules exist because the same editing
pass that removes filler is the pass most likely to remove a measurement.

| ID | Rule |
|---|---|
| PD-1 | **MUST NOT** remove a measured number, a date, or a named source. A diff removing a digit outside a code fence needs a reason in the commit message |
| PD-2 | **MUST NOT** remove a "Limit:" statement, or any sentence saying where a control stops working |
| PD-3 | **MUST NOT** remove the mechanism sentence that makes a rule actionable, even where the rule survives without it |
| PD-4 | **MUST NOT** convert a trap, limit, or status table into prose to satisfy a length rule |
| PD-5 | **MUST NOT** remove a statement that a control is advisory, unwired, or unproven |
| PD-6 | **MUST NOT** remove a rule identifier, or renumber rules. Retire an identifier with a tombstone instead |
| PD-7 | **MUST NOT** rewrite an agentless passive into an active sentence in a normative rule. The requirement holds whoever performs it |

### PD-8: retired, and the identifier is kept rather than reissued

PD-8 named four sections that look like filler and are not, so that an editor applying `OPEN-3` or
`B-6` would leave them alone. All four were sections of the standards, and they left with them for
[secure-development-standards](https://github.com/wshallwshall/secure-development-standards).
No section in this repository is under it, and PD-6 forbids reissuing an identifier, so this row
stays here saying so.

WHAT IT DOES NOT DO IS FOLLOW THEM. That repository's house style is derived from what its own gates
enforce, and carried no equivalent of this rule. Two of the four are held by tests that name them for
other reasons: `test_rule_ids_are_stable.py` parses the `## Retired rules` section, and the selector
test pins the sentinel on both sides. The status-check date line and the ASVS Part 1 to Part 2
boundary marker are held by nothing at all. That is the exact condition PD-8 was written for, now
true again one repository over, and a rule covering it is proposed there rather than assumed here.

---

## B: banned constructions

Each is drawn from prose measured in this repository, not from a general style guide.

| ID | Banned | Write instead |
|---|---|---|
| B-1 | A page instructing the reader how to read it | The content, in the order that serves it |
| B-2 | A sentence explaining why a fact is omitted | Nothing |
| B-3 | "It is worth noting", "It should be noted", "Importantly" | The note |
| B-4 | A sentence whose only work is transition | Nothing |
| B-5 | "provides the capability to", "in order to", "utilize", "leverage" | "can", "to", "use", "use" |
| B-6 | A sentence asserting its own significance | The fact that makes it significant |
| B-7 | "cleanly", "elegantly", "robustly", "carefully" describing this project's own work | The property, measured |
| B-8 | Three adjectives or three parallel clauses where one carries the meaning | The one |
| B-9 | "nearly every", "most", "a number of" for a countable set | The count |
| B-10 | A rhetorical question as a section opener | The answer |

---

## The standing edit protocol

1. Re-derive an edit's target by heading text or a quoted sentence, never by a line number from a plan
   or a review. Line numbers move under any edit to the same file.
2. Before renaming or deleting any heading under `docs/`, search the repository for its text. Pages
   here cite headings by name and link to them by anchor, and `tests/test_internal_links_resolve.py`
   fails on an anchor whose heading has moved.
3. Tests run from inside `tests/`: `python -m unittest discover -s . -q`. A run from the repository
   root finds nothing and exits without testing anything.
4. Run `scripts/coord/overlap.ps1` before starting a chunk of work. A clean merge proves lines did not
   collide, not that intentions did not.
