adr check: say what is wrong and how to fix it, in plain words, with no rule codes #492

Closed
opened 2026-09-15 21:07:03 +00:00 by hexajon · 5 comments
Owner

What happens

A failing adr check prints one line per problem as <path>: <rule>: <message>:

docs/decisions/0036-x.md: C11: record is decided but sixfold-space/madtea#491 is still open
docs/decisions/README.md: T1: index row missing for 0036-x.md

The rule codes (C1 to C11, T1) mean nothing to the reader. adr check --help says it "runs the MADR rules C1 to C11 and T1", and madt_help topic="adr" describes each rule by its code. Several messages say what is wrong but not what to do.

Ask

  • Group the problems under the file they belong to.
  • Each problem says what is wrong and how to fix it, in plain sentences.
  • No rule code in text output, help text, or reference docs. The codes may stay as internal names in the code.
  • End with one line: adr check failed: N problems in M files.
  • --json: each problem is {file, problem, fix}. If a script needs a stable key, add a readable one such as issue-still-open, in JSON only.

Example:

docs/decisions/0036-ci-file-checks.md
  - The status "done" is not a MADR status. Use proposed, accepted, rejected,
    deprecated, or "superseded by ADR-NNNN".
  - The record is accepted, but its linked issue sixfold-space/madtea#491 is
    still open. Close the issue, or set the status back to proposed.

adr check failed: 2 problems in 1 file.

Proposed text

Now New
filename must be NNNN-kebab-title.md The file name must be NNNN-short-title.md, for example 0036-ci-file-checks.md. Rename the file.
missing or unparseable YAML frontmatter The file does not start with a readable front matter block. Start it with a --- block that holds status, date, and decision-makers.
status "x" is not one of ... The status "x" is not a MADR status. Use proposed, accepted, rejected, deprecated, or "superseded by ADR-NNNN".
date "x" is not a valid YYYY-MM-DD date The date "x" is not a real date in YYYY-MM-DD form, for example 2026-09-15.
record has no title The record has no title. Add a # Title line after the front matter.
missing mandatory section "X" The record has no "X" section. MADR requires it.
number 0036 is also used by Y Number 0036 is also used by Y. Two branches probably took the same number. Give this record the next free number.
link "x" does not resolve The link "x" points to a file that does not exist. Fix the path or remove the link.
superseded by ADR-0040 but Y does not link back This record says ADR-0040 replaces it, but 0040 does not link back. Add a link to this record in 0040.
decided record still contains template placeholder "x" The record is decided but still has template text "x". Replace or remove it.
proposed record has no issue link for R This proposed record links no issue in R. Add its tracking issue under "More Information".
proposed record links R#n but it is closed This record is still proposed, but its linked issue R#n is closed. Reopen the issue, or change the status.
record is decided but R#n is still open The record is decided, but its linked issue R#n is still open. Close the issue, or set the status back to proposed.
R#n closed without citing STEM Issue R#n was closed without a comment that names this record. Add a comment on the issue that names STEM.
could not read R#n: err Could not read the linked issue R#n (err). Check your madtea login, or use --skip-remote-issues to check only the files.
index row missing for X The index has no row for X. Add a row to the Index table.
index row "x" has no record The index row "x" has no record file. Remove the row, or add the record.

Acceptance

  • No text output, help text, or reference doc for adr contains a rule code.
  • Every problem names its file and gives a fix.
  • --json carries {file, problem, fix} per problem.
  • Tests assert the new text.
## What happens A failing `adr check` prints one line per problem as `<path>: <rule>: <message>`: ``` docs/decisions/0036-x.md: C11: record is decided but sixfold-space/madtea#491 is still open docs/decisions/README.md: T1: index row missing for 0036-x.md ``` The rule codes (C1 to C11, T1) mean nothing to the reader. `adr check --help` says it "runs the MADR rules C1 to C11 and T1", and `madt_help topic="adr"` describes each rule by its code. Several messages say what is wrong but not what to do. ## Ask - Group the problems under the file they belong to. - Each problem says what is wrong and how to fix it, in plain sentences. - No rule code in text output, help text, or reference docs. The codes may stay as internal names in the code. - End with one line: `adr check failed: N problems in M files.` - `--json`: each problem is `{file, problem, fix}`. If a script needs a stable key, add a readable one such as `issue-still-open`, in JSON only. Example: ``` docs/decisions/0036-ci-file-checks.md - The status "done" is not a MADR status. Use proposed, accepted, rejected, deprecated, or "superseded by ADR-NNNN". - The record is accepted, but its linked issue sixfold-space/madtea#491 is still open. Close the issue, or set the status back to proposed. adr check failed: 2 problems in 1 file. ``` ## Proposed text | Now | New | |---|---| | filename must be NNNN-kebab-title.md | The file name must be NNNN-short-title.md, for example 0036-ci-file-checks.md. Rename the file. | | missing or unparseable YAML frontmatter | The file does not start with a readable front matter block. Start it with a `---` block that holds status, date, and decision-makers. | | status "x" is not one of ... | The status "x" is not a MADR status. Use proposed, accepted, rejected, deprecated, or "superseded by ADR-NNNN". | | date "x" is not a valid YYYY-MM-DD date | The date "x" is not a real date in YYYY-MM-DD form, for example 2026-09-15. | | record has no title | The record has no title. Add a `# Title` line after the front matter. | | missing mandatory section "X" | The record has no "X" section. MADR requires it. | | number 0036 is also used by Y | Number 0036 is also used by Y. Two branches probably took the same number. Give this record the next free number. | | link "x" does not resolve | The link "x" points to a file that does not exist. Fix the path or remove the link. | | superseded by ADR-0040 but Y does not link back | This record says ADR-0040 replaces it, but 0040 does not link back. Add a link to this record in 0040. | | decided record still contains template placeholder "x" | The record is decided but still has template text "x". Replace or remove it. | | proposed record has no issue link for R | This proposed record links no issue in R. Add its tracking issue under "More Information". | | proposed record links R#n but it is closed | This record is still proposed, but its linked issue R#n is closed. Reopen the issue, or change the status. | | record is decided but R#n is still open | The record is decided, but its linked issue R#n is still open. Close the issue, or set the status back to proposed. | | R#n closed without citing STEM | Issue R#n was closed without a comment that names this record. Add a comment on the issue that names STEM. | | could not read R#n: err | Could not read the linked issue R#n (err). Check your madtea login, or use --skip-remote-issues to check only the files. | | index row missing for X | The index has no row for X. Add a row to the Index table. | | index row "x" has no record | The index row "x" has no record file. Remove the row, or add the record. | ## Acceptance - No text output, help text, or reference doc for `adr` contains a rule code. - Every problem names its file and gives a fix. - `--json` carries `{file, problem, fix}` per problem. - Tests assert the new text.
Author
Owner

One more message to fix: with --skip-remote-issues, a clean tree prints

SKIPPED (--skip-remote-issues): linked issues not checked for 0 proposed records
OK: 36 records, 0 proposed records checked against the forge

The OK line says the records were "checked against the forge" right after the line that says they were not. When the forge part is skipped, the OK line must not claim it ran.

One more message to fix: with `--skip-remote-issues`, a clean tree prints ``` SKIPPED (--skip-remote-issues): linked issues not checked for 0 proposed records OK: 36 records, 0 proposed records checked against the forge ``` The OK line says the records were "checked against the forge" right after the line that says they were not. When the forge part is skipped, the OK line must not claim it ran.
Author
Owner

Found while writing ADR 0037: the link check treats Markdown link syntax inside a code span as a real link. A record that shows the supersede link form as example text, for example inline code containing [ADR-0012](0012-x.md), fails with link "0012-x.md" does not resolve. The link check should skip inline code and fenced code blocks.

Found while writing ADR 0037: the link check treats Markdown link syntax inside a code span as a real link. A record that shows the supersede link form as example text, for example inline code containing `[ADR-0012](0012-x.md)`, fails with `link "0012-x.md" does not resolve`. The link check should skip inline code and fenced code blocks.
Author
Owner

Scope change: the rule codes go out of the code too, not only out of the text people read. The body above says "The codes may stay as internal names in the code". That is dropped. A reader of adr.go cannot tell what madrProblem(name, "C7", ...) checks.

Each rule gets one plain name, used in the code, the output, the --json key, the tests, and the fixture names:

Today Rule name
C1 file-name
C2 front-matter
C3 status
C4 date
C5 title
C6 required-sections
C7 unique-number
C8 links
C9 supersede-link-back
C10 no-template-text
C11 linked-issues
T1 index

Acceptance addition: git grep -nE '"(C[0-9]+|T1)"' internal/ finds nothing, and no comment refers to a rule by code.

The fixture directories are already renamed to what they test (for example testdata/madr/duplicate-number/), in a separate change.

Scope change: the rule codes go out of the code too, not only out of the text people read. The body above says "The codes may stay as internal names in the code". That is dropped. A reader of `adr.go` cannot tell what `madrProblem(name, "C7", ...)` checks. Each rule gets one plain name, used in the code, the output, the `--json` key, the tests, and the fixture names: | Today | Rule name | |---|---| | C1 | `file-name` | | C2 | `front-matter` | | C3 | `status` | | C4 | `date` | | C5 | `title` | | C6 | `required-sections` | | C7 | `unique-number` | | C8 | `links` | | C9 | `supersede-link-back` | | C10 | `no-template-text` | | C11 | `linked-issues` | | T1 | `index` | Acceptance addition: `git grep -nE '"(C[0-9]+|T1)"' internal/` finds nothing, and no comment refers to a rule by code. The fixture directories are already renamed to what they test (for example `testdata/madr/duplicate-number/`), in a separate change.
Author
Owner

Still not done. main at 1c9eea63 has 39 rule-code string literals under internal/ (git grep -nE '"(C[0-9]+|T1)"' internal/), and the help text still says "C1-C11 + T1 checks below apply" (internal/mcp/tools_help.go:856). adr check still prints lines like this:

docs/decisions/0001-x.md: C11: record is decided but acme/tool#434 is still open

The code spreads from there: I wrote "C11" into #632 because that is the only name the output gives the check. With this issue done, that check is linked-issues everywhere, and nobody has to look up what a code means.

Still not done. `main` at `1c9eea63` has 39 rule-code string literals under `internal/` (`git grep -nE '"(C[0-9]+|T1)"' internal/`), and the help text still says "C1-C11 + T1 checks below apply" (`internal/mcp/tools_help.go:856`). `adr check` still prints lines like this: ``` docs/decisions/0001-x.md: C11: record is decided but acme/tool#434 is still open ``` The code spreads from there: I wrote "C11" into #632 because that is the only name the output gives the check. With this issue done, that check is `linked-issues` everywhere, and nobody has to look up what a code means.
Author
Owner

The rule codes go in the #634 change. Each rule takes its plain name from the table above, with one change: the rule that the table names linked-issues is now decision-issues, because after #634 it reads only a record's Decision issues: list. The rest of this issue stays here: grouping the output by file, the --json shape, and the plain messages for the other rules.

The rule codes go in the #634 change. Each rule takes its plain name from the table above, with one change: the rule that the table names `linked-issues` is now `decision-issues`, because after #634 it reads only a record's `Decision issues:` list. The rest of this issue stays here: grouping the output by file, the `--json` shape, and the plain messages for the other rules.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
sixfold-space/madtea#492
No description provided.