# Configuration: .surfex.exs

A keyword list, evaluated as data. An unknown key is refused, naming it.

**What is scanned**
- `sources: ["spec.md"]`: the spec files (required).
- `heading_ids:`, `rule_ids:`: the ids authors write into headings and into a marked list's
  items: `:section_sign` (`§ID`), `:section_number` (`1.2`), a regex with `(?<id>…)`, or a
  list of them (`mix surfex.info claims`).
- `fold_paths: true`: a heading with no id inside one that has an id is part of that unit,
  not a unit of its own (`mix surfex.info claims`). The default wherever `heading_ids:` is
  set, and `fold_paths: false` opts out.
- `unit_refs: [~r/^§(?<chapter>\d\d)\.(?<id>[\w.]+)$/]`: the project's own forms of citing
  a spec unit across files, read in `verifies:` tags and by the tasks (`mix surfex.info claims`).
- `tests: ["test/**/*_test.exs"]`: test files, scanned per test case. Add
  `Surfex.ExUnitFormatter` to `ExUnit.start(formatters: [...])` to record evidence.
- `declares: [verifies: [{:tag, :validates, strip: ~r/@[0-9a-f]+$/}]]`: a project's own
  form of saying what a test verifies, read as `verifies` declarations: an ExUnit tag key
  (`{:tag, KEY}`) or a module attribute before a test (`{:attribute, NAME}`), with `strip:`
  removed from each id. `mix test --only` can't select by them; `mix surfex.test` does
  (spec §11.6).
- `namespace:`: the code's namespace (defaults to the app name).
- `scanners: [:elixir, {MyApp.DslScanner, opts}]`: code scanners whose items are combined
  (`Surfex.Scanner`); one key from two scanners is an error. `scanner:` with
  `scanner_opts:` is the one-element list. A scanner for a DSL gives each block it declares
  (an action, a policy) as an item keyed `Module.name` (`Surfex.Scanner`, spec §4.1), and
  its `calls/3` says which item a call through the DSL's entry drives, so tests reach it
  (spec §5.4).
- `router: MyAppWeb.Router` (or a list, or `false`): the router a test's requests resolve
  through. Without it, the router an endpoint plugs is found from source. A test's
  `live(conn, "/carts/1")`, `get(conn, …)` and the other verbs then call what the route
  runs (a LiveView's `mount/3` and `handle_params/3`, or a controller action), its
  `render_click(view, …)` and friends call the view's `handle_event/3`, and
  `GenServer.call(Module, msg)` calls that module's `handle_call/3` (spec §11.3).
- `dispatch: [{MyApp.Ops, :run, 3}]`: the project's dispatch functions, each running a
  handler it looks up in a table (`apply(module, fun, args)`). A call to one reaches every
  handler its module lists as a literal `{Module, :fun}`, `{Module, :fun, arity}` or
  `&Module.fun/arity` in a module attribute, map or keyword list, so a test driving it
  exercises them with no review per handler. The status lists each dispatcher with what
  it reaches; one whose table isn't literal is listed as unresolved, never failing. A
  scanner's `calls/3` answer for a call still wins (spec §11.10).
- `checks: [[id:, run:, verifies:, inputs:]]`: gates that verify claims, such as a linter
  rule or a project's own verification task: a command that can fail, the spec units it
  verifies (a table's every row as `TABLE/*`), and the files it inspects, which with the
  declaration make its version (`mix surfex.info validation`, spec §11.4).
- Citation profile: `exclude`, `shape`, `token`, `known_shape`, `normalise`, `subjects`,
  `table_columns`, `file_targets`, `known_external`, `documented_absences`.
- `classes: [{name, reason}]`, `rules: [%{class:, kinds:, name:, parent_cited:, parent:}]`,
  `never_excused:`: kinds of code the spec deliberately doesn't describe.

**Policy**
- `require: [code: [:implements], test_hint: [:verifies]]`: what must take part in a
  relation of those types (keys: a kind, or a spec role `section`, `block`, `test_hint`,
  `table`, `row`, `rule`, `contract`). A spec rule judges claims only: a unit with no text
  of its own is never unmet (`mix surfex.info claims`). `spec: [:verifies]` says every
  claim is verified.
- Claim grain is the only reading, so there is no key for it: `claim_grain:`, the switch
  it shipped behind, is refused (true or false). Delete it; `mix surfex.migrate --report`
  lists what to move to claims.
- `spec_classes: [{name, reason} | {name, reason, files: globs, headings: regex, marker: regex}]`:
  classes of spec text that asserts nothing, selected by a marker in the section, a
  pattern, or the project's own comment (`mix surfex.info claims`).
- `not_claims: [regex]`: lines of a section's own text that aren't claims (provenance), each
  matched as written; left out of verdicts and completeness, never out of versions
  (`mix surfex.info claims`).
- `issues: [command: argv, state: key, project: name]`: how `mix surfex.status --issues`
  asks the tracker whether a deferral's issue is open; `{ref}`, `{n}` and `{project}` are
  substituted, no shell, nothing stored.
- `triangle: :report | :fail`: whether a unit, its tests and its code must meet.
- `require_red:`: whether a `tests` relation needs its test's red run.
- `adoption: :reevaluate | :trust | [trust: globs, reevaluate: globs]` (`mix surfex.info adoption`).
- `completeness: [min: N]`: fail status below N percent.

**Outputs**
- `goldens: [:status, :completeness]`: `RELATIONS.md`, `COMPLETENESS.md`.
- `process: :print | {:command, argv}`: where `mix surfex.draft --file` sends drafts.

The check's own options (`--validated`, `--no-marks`, `--no-baseline`, …) are on the
command line: `mix surfex.info status`.
