A section often holds several claims. Name each one, and it becomes a spec unit of its own
(role :rule): its own id, its own version, its own relations. Rewording one claim then
dangles only that claim's relations, not the whole section's. No id ever comes from
position, so inserting a claim moves nothing else.
A paragraph or a list item: an id comment on its own line directly before it.
<!-- surfex: retired-unreachable -->
**A retired schema's objects are unreachable by construction.** Nothing resolves to them.
- plain item
<!-- surfex: no-override -->
- Anonymous requests never honour an override.An id comment starting the item or paragraph (- <!-- surfex: ID --> text) is read too,
but ex_doc shows it as text (or swallows the next heading), so prefer the comment on its
own line. Before a list item that isn't the list's first, the comment needs a blank line
above it.
A list of rules: mark the list, and write each item's id as its text already does.
<!-- surfex: rules tenancy -->
1. §Tenancy.1 A tenant reads only its own rows.
2. §Tenancy.2 A tenant's id is never reused..surfex.exs says how ids are written: rule_ids: :section_sign (§ID),
:section_number, a regex with (?<id>…) such as ~r/^(?<id>P\d+)/, or a list of them.
An item with no id is an error naming its line.
A module's signatures: a contract fence, one unit per function,
spec.md#MyApp.Feed.feed/2, and a callback apart, spec.md#MyApp.Feed.callback:init/1.
<!-- surfex: contract module: MyApp.Feed -->
```elixir
@spec feed(t, item) :: t
@callback init(term) :: {:ok, t}
```A signature's module is its head's, else a defmodule M do around it in the fence, else
the marker's module:, else the nearest heading naming one (a subjects: item, or a
module alias alone in backticks, as in ## 2. `MyApp.Feed`), so under such a heading
the marker needs no module:. One id twice in a file is an error. Changing one signature
dangles only that function's relations.
Citing a unit in the project's own form: ids are per file, and a project often cites
them across files as §03.4 or spec/05 §4.3. List the forms in .surfex.exs, each a
regex naming the id and the file (its path, with or without .md) or the chapter
(the number the file's name starts with):
unit_refs: [~r/^§(?<chapter>\d\d)\.(?<id>[\w.]+)$/]Then @tag verifies: "§03.4", mix surfex.test §03.4 and mix surfex.validate TEST §03.4
all name spec/03-wire-format.md#4.
Sub-headings as structure: with fold_paths: true, a heading with no id inside one
that has an id (### run/2 under ## §Engine.13) is part of that unit: its text counts in
the id'd section's version, and its hints sit in that section. Give a sub-heading an id
when it should be a unit of its own. It is the default wherever heading_ids: is set
(fold_paths: false opts out).
One claim per row: a table row whose key reads as a list (1001, 1005, a / b,
a and b) is probably two claims in one row; the status lists it under "List-shaped keys"
as a warning. Split the row.
Not a claim: bold. A bold-lead sentence without an id comment is its section's text.
A claim's version is its text and nested lines, without the list marker or id comment, so
renumbering a list changes nothing. A test verifies a claim by its id
(@tag verifies: "no-override"), and require: [rule: [:verifies]] asks that every claim
be verified. Spec §11.1 has the details.
What verified means (spec §13.4). A unit's own text is its body without the claims inside it. A claim has some (a section or block with prose of its own, and every hint, rule, row and contract); a container has none (a heading over subsections, a section whose body is only named claims, a marked table). The status gives each unit a verdict:
| Verdict | When | What it needs |
|---|---|---|
| verified | a claim with a current verifies of its own | nothing |
| contained | a claim with none of its own, but a claim inside it is verified | its own evidence: its prose isn't verified |
| unverified | a claim with no evidence, own or inside | its own evidence |
| through claims | a container | nothing: its claims answer for themselves |
No credit moves up (a section's tag says nothing of the claims inside it) or into a
section's own prose (a verified hint says nothing of the paragraphs around it). The status
lists Contained claims and each verifies on a container (it verifies nothing:
move the tag to what it checks). Neither fails on its own. A contained section is settled
by tagging it on a test that really checks its prose, by naming its prose's claims and
migrating them (the guide "Migrating to claim grain"), or by classing prose that asserts
nothing.
Lines that aren't claims (spec §13.12). not_claims: [~r/^Legacy reference: \S+$/]
in .surfex.exs names lines of own text that state nothing, such as provenance. Each line
is matched as written, and a matched line is left out of what the verdicts and completeness
read, never out of the version. A section left with only such lines is a container; a
sentence added beside them makes it a claim again. A sentence naming code is a claim unless
a pattern says otherwise. The status lists each pattern with the lines it matched.
Spec text that asserts nothing is classed (spec §11.7, §13.6). spec_classes: in
.surfex.exs declares classes with reasons; a section is classed by
<!-- surfex: class NAME[: REASON] --> on its own line, by a files: or headings:
pattern, or by the project's own comment through marker:. A unit not built yet is
deferred by its issue, <!-- surfex: deferred #123[: REASON] -->, or by the spec unit
that records the decision, <!-- surfex: deferred UNIT@VERSION[: REASON] -->, pinned to
the version its author judged (spec §13.13). A classed section with no claim inside is
excused and a deferred one deferred: neither is judged by require: spec rules nor counted in completeness.
A class excuses the section's own text, never a claim named inside it. A verifies or
implements to an excused section is contradicted and fails (retire it, or drop the
class), and a deferral is stale once its unit has a test of its own or, under
mix surfex.status --issues, its issue is closed, or the unit it cites isn't the version
it pins: the status names the version to write once the deferral is judged again.
Code earns through the claim that exercises it (spec §13.5). An implements to a
verified unit earns; one to a container or a contained section earns when a verified
claim inside it has a test (or a check) that exercises the code. Otherwise it is
unearned: unverified, not_exercised or nothing_inside (Surfex.Status.earning/2).
An unearned implements meets no require: code: rule: the
status lists it as Unearned, and fails on it where the policy asks code for an
implements (spec §13.9). Completeness counts code described only by an earning one.
Code relates to the claim it realises. With claim_implements: true in .surfex.exs
(off by default), mix surfex.suggest proposes
implements between each claim and the code its verifying tests call directly (their
own calls, private helpers, and resolved routes, events and messages), and prints one
reached … through X: N more items line where a called function reaches more: name the
ones that realise the claim with mix surfex.relate. A claim with no verifying test gets
nothing, and a section gets no proposals of its own (spec §15.1).