Skip to content

fix(markdown-format): make the carriage-return test able to fail, and test the histogram overflow - #1608

Merged
kyle-sexton merged 2 commits into
mainfrom
fix/markdown-format-cr-and-test-gaps
Jul 26, 2026
Merged

fix(markdown-format): make the carriage-return test able to fail, and test the histogram overflow#1608
kyle-sexton merged 2 commits into
mainfrom
fix/markdown-format-cr-and-test-gaps

Conversation

@kyle-sexton

Copy link
Copy Markdown
Contributor

Summary

Closes three defects shipped by #1600. They were found by a code review of that PR that completed
after it merged — its automated review lanes had errored out for infrastructure reasons, so I
delegated the review, and the result arrived too late. Worth stating plainly rather than quietly fixing:
the process failure was mine, and the most useful finding is the one that shows why.

1. The carriage-return assertion could not fail

bounded/scale: report text carries no stray carriage returns grepped the report for \r — while the
stub that produced that report emitted plain LF via echo. It passed identically whether the
CR-stripping code existed or was reverted.

That is a false safety signal, and #1600's own changelog had argued for the opposite discipline: the
histogram negative-case exists specifically "so it cannot become permanent decoration." This assertion
was exactly that decoration.

The stub now takes a STUB_CRLF knob and emits real \r\n.

And then the first rewrite of that assertion was still vacuous on three of its four channels — which
the revert-probe caught, and reasoning would not have. On Git Bash, reading a value back through
printf | jq -r | $(…) normalizes CRLF pairs away, and every CR here sits at end of line. So a
decoded-value check structurally cannot see the CRs it is about. Only the fix-count line, whose CR is
mid-string, was visible; with the fix reverted, 3 of 4 assertions still passed.

The assertion now inspects the two-character \r escape in the raw emitted document — the bytes the
hook actually produces — rather than a decoded value that has been through a pipe. Both rewrites were
confirmed by revert-probe rather than by argument, which is the only way this class of defect gets
caught.

2. Carriage returns normalized at the source — with one review claim corrected

CTX and SYSMSG were stripped after composition, leaving findings_raw — which becomes
data.findings — reading raw FIX_OUTPUT. The review called that a live leak on the telemetry channel.
Measurement says otherwise, and I would rather correct the record than quietly ship the fix as
described.
That array is built by piping into jq -R, and on Git Bash the pipe performs CRLF→LF
translation itself, so the array never carried a CR. Confirmed by revert-probe: with the hook's
normalization removed, the report and user-message assertions fail while the telemetry one still passes.

The normalization is kept regardless, for reasons that survive the correction: relying on an incidental
property of one platform's pipe behavior is not something the next reader should have to rediscover, and
one strip at the source replaces four downstream strips that would each have to be remembered when a
fifth consumer of this output appears. The two tail-end strips are deleted.

The telemetry assertion is kept too, and labelled in the test as documenting an invariant rather than
guarding one
— an assertion that cannot fail on the host that runs it must not be mistaken for
coverage. That is the same mistake as finding #1, and naming it is cheaper than repeating it.

One consequence, called out in a code comment and the changelog so it is not misread as a regression:
the delta digest is hashed over findings_raw, so this changes that hash. Every digest recorded before
this version invalidates once, producing one extra full-detail report per file. Self-correcting.

3. +N more rule(s) had no positive test

Only the negative case existed (suffix absent at two rule kinds), and the stub could emit at most two
distinct rule codes — so the overflow path that feature was named for was structurally unreachable
from the suite.

The stub now takes STUB_RULE_KINDS, defaulting to 2 so every existing case's histogram is byte-for-byte
unchanged. The new case runs eight kinds and asserts both halves: the +3 more rule(s) suffix with its
count, and that the histogram still names exactly five.

Test plan

  • bash plugins/markdown-format/hooks/markdown-format.test.shPASS=117 FAIL=0, with six new
    assertions (three CR channels, CR finding-count parse, histogram overflow suffix, histogram top-five
    retained).
  • Revert probe, which is the point of finding chore: initialize marketplace scaffold #1: with FIX_OUTPUT="${FIX_OUTPUT//$'\r'/}" removed,
    the bounded/crlf assertions fail. The assertion shipped in fix(markdown-format): stop the telemetry payload lying when it exceeds argv #1600 passed in that same state, and so
    did the first rewrite on three of its four channels. That difference is the whole fix — and it is why
    every claim here was checked by removing the code rather than by reading it.
  • Repo gates green locally: check-changelog-parity.sh --check-bump origin/main,
    check-shell-portability.sh, check-silent-skips.sh, validate-plugins.sh, shellcheck -x on both
    shell files, markdownlint-cli2 and typos over the plugin tree.

Deliberately not in this PR

Three lower-priority items from the same review stay open on #1605. They are latent rather than shipped,
and folding them in would make this diff harder to check against the findings it exists to close:

  • build_data_json returns an empty string rather than its documented fixed-shape object if ever handed
    an empty-string argument (unreachable from both current call sites).
  • Prune-on-create can leave stale digests untouched in a long session that only re-edits already-seen
    files; self-corrects the moment any session touches a new one.
  • The scale test's negative branch cannot distinguish "correctly dropped" from "harness broken".

Related

Closes #1605

Follows #1600 and #1591.

… the histogram overflow tested

Closes three defects shipped by #1600, found by a code review that completed
after that PR merged -- its automated review lanes had errored out, so the
review was delegated and arrived too late.

The worst was a test that could not fail. It grepped the report for a carriage
return while the stub producing that report emitted plain LF, so it passed
identically whether the stripping code existed or was reverted. #1600's own
changelog had argued for the opposite discipline about a different assertion;
this was exactly the decoration that reasoning warns against.

The first rewrite of it was STILL vacuous on three of four channels, which only
a revert-probe caught: on Git Bash, reading a value back through
printf | jq -r | $(...) normalizes CRLF pairs away, and every CR here sits at
end of line. The assertion now inspects the two-character escape in the raw
emitted document instead of a decoded value that has been through a pipe.

The review also reported carriage returns leaking into data.findings.
Measurement says otherwise: that array is built by piping into jq -R, and on
Git Bash the pipe performs the translation itself, so it never carried one.
Normalization still moves to the source -- depending on an incidental property
of one platform's pipe behavior is not something the next reader should have to
rediscover, and one strip replaces four that each have to be remembered when a
fifth consumer appears. The telemetry assertion stays but is labelled as
documenting an invariant rather than guarding one, because an assertion that
cannot fail on its own host must not read as coverage.

Third: the +N more rule(s) suffix had only a negative test, and the stub could
emit at most two rule codes, so the path this feature was named for was
structurally unreachable. The stub now spreads findings across a configurable
number of rule codes; the suffix is asserted with its count alongside the
unchanged top-five histogram.

Closes #1605

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Claude encountered an error after 0s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Claude encountered an error after 0s —— View job


I'll analyze this and get back to you.

@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown

Warning

Automated security review did not complete — this is an infrastructure failure, not a review verdict.

Treat any Claude comment on this PR (including a placeholder like "I'll analyze this and get back to you") as incomplete, not "no findings."

Re-running the job, or pushing a new commit, will retry the review.

@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown

Warning

Automated review did not complete — this is an infrastructure failure, not a review verdict.

Treat any Claude comment on this PR (including a placeholder like "I'll analyze this and get back to you") as incomplete, not "no findings."

Re-running the job, or pushing a new commit, will retry the review.

@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Claude encountered an error after 0s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Claude encountered an error after 0s —— View job


I'll analyze this and get back to you.

@kyle-sexton

Copy link
Copy Markdown
Contributor Author

Both automated lanes errored at 0s on this PR (and on #1600 before it), so I had the diff reviewed out of band. It recommended merge, and confirmed by independent revert-probe that the two load-bearing assertions fail when the production strip is removed — 115 pass / 2 fail, exactly the two channels claimed, nothing else regressing.

It also caught something I got wrong, which I have corrected in the diff rather than leaving in the record.

My CRLF attribution was probably mis-assigned, and in the direction that matters. I wrote that "the pipe performs CRLF→LF translation." The mechanism is more likely jq.exe's own reader applying Windows text-mode stdio — but the load-bearing part is what I did not check: this repo's CI runs on ubuntu-24.04, and Linux jq has no text/binary mode distinction and is documented not to strip a trailing CR from CRLF input. So the platform I measured on is the one where that assertion cannot discriminate, and the platform that actually gates merges is the one I never tested. My "measurement says the array never carried a CR" read as universal on the strength of a Windows-only observation.

Corrected in the code comment, the test comment, the assertion's own ok label, and the changelog: the discriminating power is stated as platform-dependent — on Linux it guards, on Windows it documents. The production strip is unconditional either way, so shipped behavior does not change; what changes is that the next reader is not misled about which half is tested.

Also hardened: STUB_RULE_KINDS > 8 would have expanded an out-of-range positional parameter and aborted under set -u several frames from the cause. It now fails with a named message. Not reachable today (only 2 and 8 are used), but it is a landmine for whoever raises the knob next.

Left as-is: the crlf_escaped glob could theoretically match a literal backslash-r in a Windows-style path, but mktemp -d guarantees forward slashes throughout this suite, so it is unreachable here.

…'s rule-code knob

Review follow-up. I claimed "on Git Bash the pipe performs CRLF->LF
translation," and the load-bearing part of that is wrong in the direction that
matters. The mechanism is more likely jq.exe's own text-mode stdio, but the
real problem is what I did not check: CI runs on ubuntu-24.04, Linux jq has no
text/binary mode distinction, and it is documented not to strip a trailing CR
from CRLF input. So the platform I measured is the one where that assertion
cannot discriminate, and the platform that gates merges is the one I never
tested. A Windows-only observation was written up as universal.

The code comment, the test comment, the assertion's own label and the changelog
now all say the discriminating power is platform-dependent: on Linux it guards,
on Windows it documents. Shipped behavior is unchanged -- the strip is
unconditional either way -- but the next reader is no longer told the tested
half is the untested half.

Also bounds STUB_RULE_KINDS. Above the eight codes the stub knows, it expanded
an out-of-range positional parameter and aborted under `set -u` several frames
from the cause. It now fails with a named message. Unreachable today, since
only 2 and 8 are used, but a landmine for whoever raises the knob.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Claude encountered an error after 1s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Claude encountered an error after 1s —— View job


I'll analyze this and get back to you.

@kyle-sexton
kyle-sexton merged commit b660292 into main Jul 26, 2026
31 checks passed
@kyle-sexton
kyle-sexton deleted the fix/markdown-format-cr-and-test-gaps branch July 26, 2026 20:41
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.

markdown-format: vacuous CR assertion, incomplete CR fix, and an untested histogram overflow

1 participant