Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions lib/mix/statifier/adr_guard.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,17 @@ defmodule Mix.Statifier.AdrGuard do
comments), and ADR-0058 (ADR number collisions). Each of the first five
checks is a name or call-site pattern over the lines a diff adds -
deliberately not an AST pass - so a false positive is cleared the way this
project already clears an Appendix D deviation: an inline comment on or
above the flagged line naming an ADR or the word "deviation".
project already clears an Appendix D deviation: an inline comment naming an
ADR or the word "deviation", on the flagged line itself or on the line
directly above it in the same hunk.

That two-line window is the whole of what the guard reads, and it is kept
that narrow on purpose: a wider one would make the guard easier to satisfy
by accident, and widening it is a change to what the gate accepts, so
ADR-0011 makes it a human call. A citation two or more lines above the
flagged line clears nothing - for instance one written above a
`defp ... do` line whose first body line is the flagged call. Put the
citation on the flagged line, or on the line directly above it.

ADR-0058's tree-local checks, `adr-0058-duplicate-number` and
`adr-0058-readme-index`, are different in kind from all five: they are
Expand Down Expand Up @@ -50,8 +59,10 @@ defmodule Mix.Statifier.AdrGuard do
kind of line ADR-0018's Consequences show clearing itself by accident (a
legitimate ADR citation that happens to sit beside an unrelated bead
mention), so this check has its own marker, `ADR-0018-exempt`, and nothing
else clears it. Being line-based rather than AST-based, it can only tell a
comment/doc line from a code line by shape: a `#` line, a single-line
else clears it. The marker is read in the same two-line window: on the
flagged line or the line directly above it, and nowhere further up. Being
line-based rather than AST-based, it can only tell a comment/doc line from
a code line by shape: a `#` line, a single-line
`@moduledoc`/`@doc`/`@typedoc "..."`, a `test "..." do` description, or a
line inside a `\"""` doc heredoc. A doc heredoc's extent is read from the
file's post-image text carried on `source` (populated by `collect/1`), not
Expand Down Expand Up @@ -198,7 +209,8 @@ defmodule Mix.Statifier.AdrGuard do

A finding is dropped when the line it fires on, or the line above it in the
same hunk, cites an ADR number or the word "deviation" - the same inline
justification CLAUDE.md already asks for.
justification CLAUDE.md already asks for. A citation two or more lines
above the flagged line does not clear it (see the moduledoc).
"""
@spec analyze(source :: source()) :: [finding()]
def analyze(source) do
Expand Down Expand Up @@ -774,7 +786,8 @@ defmodule Mix.Statifier.AdrGuard do

# Only added lines are checked: a check about what the code now does has
# nothing to say about a line the diff removes. `previous` is the line above
# in the same hunk, which is where an inline citation usually sits.
# in the same hunk, and it is the only line above a flagged line that the
# escape checks read: the citation window is that line and the flagged one.
defp parse_diff(diff) do
{_path, _line, _previous, acc} =
diff
Expand Down
12 changes: 7 additions & 5 deletions lib/mix/tasks/adr.check.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ defmodule Mix.Tasks.Adr.Check do
It runs as the `ADR guard` custom stage of `mix quality`, so drift from an
accepted decision is a named gate failure rather than something review has to
catch. Every finding names the ADR it is about. The ADR-0002/0003/0004/0008
findings clear with an inline comment on or above the line citing that ADR or
the word "deviation" - the same justification CLAUDE.md already asks for. The
ADR-0018 bead-ID finding does not: an ADR citation is exactly what it is
checking against, so it clears only with its own marker, `ADR-0018-exempt`,
placed on or above the line.
findings clear with an inline comment citing that ADR or the word
"deviation", on the flagged line or on the line directly above it - the same
justification CLAUDE.md already asks for. The ADR-0018 bead-ID finding does
not: an ADR citation is exactly what it is checking against, so it clears
only with its own marker, `ADR-0018-exempt`, placed in the same place. A
citation two or more lines above the flagged line clears neither kind of
finding.

The ADR-0058 numbering findings (`adr-0058-duplicate-number`,
`adr-0058-readme-index`, `adr-0058-base-number`) clear on neither escape
Expand Down
28 changes: 28 additions & 0 deletions test/mix/statifier/adr_guard_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,21 @@ defmodule Mix.Statifier.AdrGuardTest do
]) == []
end

# The window is the flagged line and the one directly above it, on
# purpose: a citation above a function head does not reach the flagged
# call in its body. Widening the window is a change to what the gate
# accepts (ADR-0011), not a fix.
# sabotage: have parse_line/2 carry every earlier line of the hunk in
# `previous`, widening the window -> red
test "a citation two lines above the flagged line does not clear it" do
assert [%{line: 12, check: "adr-0003-effects"}] =
analyze("lib/statifier/document.ex", [
" # ADR-0003: deviation, the reply is awaited in place",
" defp await_reply(ref) do",
" receive do"
])
end

# sabotage: loosen @escape_pattern to any `#` comment -> red
test "a comment that cites nothing clears nothing" do
assert [%{check: "adr-0003-effects"}] =
Expand Down Expand Up @@ -460,6 +475,19 @@ defmodule Mix.Statifier.AdrGuardTest do
]) == []
end

# The marker is read in the same window as an ADR citation, and no
# further up.
# sabotage: have parse_line/2 carry every earlier line of the hunk in
# `previous`, widening the window -> red
test "the ADR-0018-exempt marker two lines above does not clear the finding" do
assert [%{line: 12, check: "adr-0018-bead-id"}] =
analyze("lib/statifier/document.ex", [
" # ADR-0018-exempt: named for git blame",
" value = compute(input)",
" # st-af3 replaces this function's body"
])
end

# This is the exact accident ADR-0018's Consequences document at e2524fc:
# a correct, unrelated ADR citation must not clear a bead-ID finding.
# sabotage: have bead_cited?/1 call the shared cited?/1 (@escape_pattern)
Expand Down
Loading