# Migrating to claim grain

A relation log started at **section grain** has its `implements` on sections: code relates
to the heading whose text mentions it. That works until a section grows. Every edit of a
section's text re-versions the section, and every relation on it dangles at once: one
changed sentence can ask for dozens of re-reviews of relations whose claims didn't change.

At **claim grain**, code relates to the claim it realises: a row of a table, a test hint, a
marked block, a named rule or paragraph, a contract signature (spec §11.1). An edit then
dangles the relations of the claim it touched, and no others.

`mix surfex.migrate` moves a section's relations to its claims (spec §14.1). It works one
section at a time, as work touches a section. Nothing is migrated wholesale.

## 0. Claim grain is how surfex reads

Surfex reads every spec at claim grain; there is no switch. A `.surfex.exs` that still
sets `claim_grain:`, the key claim grain shipped behind, is refused: delete the line. For
a project that never set it, the check changes in five ways (spec §13.2):

- **Contained sections**: a section verified only through a claim inside it is
  `contained`. Completeness counts it incomplete until a test checks its own prose, its
  claims are named, or it is classed; containers aren't counted at all (spec §20.1).
- **Earning**: an `implements` counts for `require: code:` only when it earns, through a
  verified unit or a verified claim inside it whose test exercises the code. The rest are
  **unearned** and fail where `require: [code: [:implements]]` is set (spec §13.9).
- **Classes and deferrals** are read: an excused or deferred unit isn't judged or
  counted, a relation to an excused section is contradicted, and a deferral that no
  longer holds is stale (spec §13.6, §13.7).
- **Deviations** hold back the sections they sit under too (spec §13.11).
- **Path headings fold** under an id'd heading wherever `heading_ids:` is set
  (`fold_paths: false` keeps them units, spec §11.1).

Run `mix surfex.status` and `mix surfex.migrate --report` (below) to see what each of
these means for your spec.

## 1. See what migration buys

```sh
mix surfex.migrate --report
```

The report starts with the **population**: the sections holding section-grain `implements`,
split into those whose claims are already units (tables, hints, blocks, rules, contracts),
which migrate today, and **prose** sections, whose claims need naming first. Then each
section, worst first:

```text
spec.md#billing (claims are units): 81 relations now → 24 after; implements 57: 51 single · 6 several · 0 none
  MyApp.Billing.base_time/2: 81 now → 6 after (single)
  …
```

- **relations now → after**: what one edit of the section's text dangles today, and once
  every code item with a candidate claim has moved.
- **marking**: a prose section has no claim a relation could move to. Naming its claims,
  the first marker or id comment, re-versions the section and dangles everything on it
  once. The report counts that cost, since it is paid even for relations that don't move.
- each code item's **dangle width**: the relations a change to the text stating its claim
  dangles, now and after.

Measured over projects that already had their claims as units, what an edit of a section
can dangle fell from 1,128 relations to 413; the widest section went from 81 to 24, and its
widest item from 81 to 6. Projects whose sections are all prose owe the marking first:
there, the report is the list of where to name claims.

The report is the whole work list (spec §14.2). A **contained** section, one with no test
of its own while a claim inside it is verified, gets a line naming those claims:

```text
spec.md#totals (claims are units): 4 relations now → 4 after; implements 0: 0 single · 0 several · 0 none
  contained: spec.md#totals-empty verified inside, its own text isn't
```

Contained sections are listed even when they hold no `implements`: there is no code to
move, but completeness counts them incomplete until a test checks
their own prose, their claims are named, or they are classed (spec §20.1). `mix
surfex.migrate --report --format json` gives the same report for tools.

## 2. Look at one section's proposals

```sh
mix surfex.migrate spec.md#billing
```

For each code item related to the section, the claims inside it that could own the code:

- a row whose key **names** it (`Mod.fun/2`, or `Mod.fun`, which names every arity);
- a claim whose verifying test **calls** it;
- failing both, a claim whose test **reaches** it, through the item named.

Arities of one definition (default arguments) are one proposal. The listing has four kinds:

```text
none      MyApp.Billing.round/1: no claim inside spec.md#billing names it or has a test calling or reaching it: …
several   MyApp.Billing.total/1 → spec.md#billing-sum (called by its test …); spec.md#billing-tax (…): …
single    MyApp.Billing.base_time/2 → spec.md#rates/MyApp.Billing.base_time (its key MyApp.Billing.base_time names it)
migrating MyApp.Billing.rate/1 → spec.md#rates/MyApp.Billing.rate: validate the claim's relation …
```

**none** comes first: it is the spec work the migration surfaces. The code realises a
claim nobody wrote down yet (write it: a row, a hint, a named paragraph), or it doesn't
belong to the section at all. **several** is a judgement: read the claims and the code.

## 3. Record it, and stay green

```sh
mix surfex.migrate spec.md#billing --accept --note "billing moves to its rows"
```

Each single proposal is recorded as an `implements` to its claim, **proposed**, carrying
`migrates` (the section it moves from). A proposed relation normally fails the check;
one that migrates doesn't, while the section still holds its own. So a section can move
over as many commits as it takes, with the check green throughout.

For several, or for none once its claim exists, record your choice:

```sh
mix surfex.migrate spec.md#billing --code MyApp.Billing.total/1 --to spec.md#billing-sum
```

## 4. Validate the claims; the section lets go

Validate each claim relation the way any `implements` is validated: by evidence (a red run
then a green one, `mix surfex.confirm --evidence`), or by a review:

```sh
mix surfex.validate "MyApp.BillingTest: base time per rate" spec.md#rates/MyApp.Billing.base_time \
  --note "asserts the base time for each rate, the row's claim"
```

When a review credits several arities of one definition, `validate` says so: write the
note for the definition (spec §18.4).

Once every claim relation of a code item is validated, the pair is **settled**, and the
section's relation is retired with a note naming the claims, by the next `--accept` or by
`mix surfex.suggest --accept`. A section never loses a relation before its claim holds one.

## A baseline carries

A project that adopted its suite with a baseline (`adoption: :trust`, `mix surfex.baseline`,
see the adoption guide) doesn't redo that trust. A section relation resting on the baseline
moves to a claim whose verifying test is the same baselined test, at the version trusted,
exercising the same code: the claim relation keeps basis `baseline`, holds at once, and the
section's relation retires in the same `--accept`.

## Importing your own tags

A project that already records what its tests verify in a tag of its own can have surfex
read it, rather than rewriting every tag:

```elixir
declares: [verifies: [{:tag, :validates, strip: ~r/@[0-9a-f]+$/}]]
```

`@tag validates: ["Engine.15.2@abc123"]` then declares `Engine.15.2`, resolved as any
declaration (through `unit_refs:` in the project's citation form), suggested by
`mix surfex.suggest` and confirmed by evidence (spec §11.6). `mix test --only` can't select
by such ids; `mix surfex.test UNIT` does.
