Skip to content

Keep the checksum lint's regression corpus - #9

Merged
jeremy merged 1 commit into
mainfrom
lint-tests
Aug 3, 2026
Merged

Keep the checksum lint's regression corpus#9
jeremy merged 1 commit into
mainfrom
lint-tests

Conversation

@jeremy

@jeremy jeremy commented Aug 3, 2026

Copy link
Copy Markdown
Member

The checksum lint took nineteen rounds of review. Every round was a definition shape that
slipped past it, and every fix was verified once, by hand, against a scratch file that was
then deleted — so the corpus survives only in dfcd595's commit message. Nothing stops the
next edit reintroducing any of them.

test/lint is that corpus, kept. Each case writes a fixture definition, calls
lint_checksums, and asserts on the failures array — the same array bin/ci's summary
reads, so a test can't pass on output that wouldn't fail the run.

What's covered

Both directions, because they aren't symmetric. A false negative ships an unverified
download silently, which is what the lint exists to prevent; a false positive blocks CI
over a URL nothing fetches, which is what gets checks deleted.

  • Must fail (20 cases): every quoting style, line continuations, split mid-scheme,
    after if/&&/;/ANSI-C quoting/a multi-line string/a heredoc/a quoted <<HIDE,
    attached ;echo/&&/|/), ?download=1, uppercase digest, 67- and 63-hex digests.
  • Must pass: the six real definitions, ?a=1&b=2#<digest>, continued and split-scheme
    URLs that do carry digests.
  • Must be ignored: issue/repo links in comments, and the savannah gitweb URLs — now
    skipped by shape rather than by an exemption list.
  • Never exercised by the real corpus: .tar.xz, .tbz2, .tar.Z, .zip,
    join_continuations itself, and the vacuous-pass guard.

bin/ci changes

Only enough to be sourceable: the dispatch moves into main(), called under
[ "${BASH_SOURCE[0]}" = "$0" ]. Sourcing it currently cds, prints a banner, runs all
three lints over the sourcing script's arguments, and can exit 1 out of the harness. The
header comment is deliberately not renumbered — --help re-reads lines 2-15 of itself.

test/lint is added to lint_syntax and lint_shellcheck, and bin/ci also runs it. That
last part is beyond what I'd planned and easy to drop, but a regression suite CI never
executes rots unnoticed, which is the failure mode this file exists to document.

Verification

  • bin/ci green with signoff: lint + test/lint 35/35 + 12/12 build matrix.
  • shellcheck clean at --severity=warning on bin/ci, test/build, test/lint.
  • Mutation-tested, since a suite that can't fail is worth nothing. Dropping .tar.xz
    from the pattern, joining continuations with a space, removing the vacuous-pass guard,
    accepting uppercase digests, and classifying every URL as an archive each fail exactly
    the cases they should and no others.

The checksum lint took nineteen rounds of review. Every round was a definition
shape that slipped past it, and every fix was verified once, by hand, against a
scratch file that was then deleted — so the corpus survives only in dfcd595's
commit message. Nothing stops the next edit reintroducing any of them.

test/lint is that corpus, kept. Each case writes a fixture definition, calls
lint_checksums, and asserts on the `failures` array — the same array bin/ci's
summary reads, so a test can't pass on output that wouldn't fail the run.

Both directions are covered, because they aren't symmetric. A false negative
ships an unverified download silently, which is what the lint exists to prevent;
a false positive blocks CI over a URL nothing fetches, which is what gets checks
deleted. Twenty undigested forms must fail — every quoting style, continuations,
split mid-scheme, after if/&&/;/ANSI-C quoting/a multi-line string/a heredoc/a
quoted <<HIDE, attached ;echo/&&/|/), ?download=1, uppercase and wrong-length
digests. Reference links in comments and the savannah gitweb URLs must pass. And
a file with no archives at all must fail rather than pass over nothing.

Three things nothing else exercises: the archive extensions (.tar.xz, .tbz2,
.tar.Z, .zip are in the pattern but no definition uses them), join_continuations
itself (no definition has a line continuation), and the vacuous-pass guard.

bin/ci changes only enough to be sourceable: the dispatch moves into main(),
called only under `[ "${BASH_SOURCE[0]}" = "$0" ]`. Sourcing it currently cds,
prints a banner, runs all three lints over the sourcing script's arguments, and
can exit 1 out of the harness. The header comment is deliberately not renumbered
— --help re-reads lines 2-15 of the file.

bin/ci runs test/lint as well as syntax-checking and shellchecking it. Beyond
what the plan called for, and easy to drop, but a regression suite CI never
executes rots unnoticed, which is the failure mode this file exists to document.

Verified by mutation, since a suite that can't fail is worth nothing: dropping
.tar.xz from the pattern, joining continuations with a space, removing the
vacuous-pass guard, accepting uppercase digests, and classifying every URL as an
archive each fail the cases they should and no others.
Copilot AI review requested due to automatic review settings August 3, 2026 09:03

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@jeremy
jeremy merged commit 1c83d3c into main Aug 3, 2026
1 of 3 checks passed
@jeremy
jeremy deleted the lint-tests branch August 3, 2026 16:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants