Match the release-date control heading as a grammar and escape the probe version - #75
Conversation
…robe version Greptile raised a second P2 against the boundary merged here earlier today (#74), on the sibling pm-linear PR. It applies identically, so this carries the fix across rather than leaving it local to the repository that was reviewed. Excluding only a DIGIT immediately after the probe is barely narrower than accepting anything. All three of these passed: ## 2026.1.2.3 a different version ## 2026.1.2-rc1 a different version ## 2026.1.2 - garbage a date position holding something that is not a date The last is the one that matters: it is exactly the shape a broken date implementation would emit, so the control was blind in precisely the direction this gate exists to watch. The suffix is now matched as a grammar of the forms a correct generator produces for one probe version -- the bare version, an optional `-<n>` duplicate-section suffix, an optional ` - <YYYY-MM-DD>` date -- and the failure message names those accepted forms rather than only rejecting. Second defect fixed in passing: `${probe}` was interpolated into the pattern UNESCAPED, so its dots matched any character and `## 2026X1Y2` would have been accepted as a heading for 2026.1.2. It is now escaped before use. Verified across all nine cases: the three above plus the false-prefix `## 2026.1.20 - ...` and a wrong version are rejected; bare, suffixed, dated and suffixed-and-dated headings are accepted. release:check exits 0. Tracked as pm-github-bi6l.
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Team Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Reviewer's guide (collapsed on small PRs)Reviewer's GuideThe PR hardens the release changelog date control by matching only recognized heading forms for the probe version and escaping the version before regex interpolation, preventing false certification of alternate versions, malformed dates, and regex-like version matches; it also adds changelog and PM tracking metadata. Flow diagram for release heading validationflowchart TD
A[Generate changelog without --date-from-version] --> B[Escape probe version]
B --> C{Heading matches recognized grammar?}
C -->|Yes| D[Continue release check]
C -->|No| E[Fail control]
C -->|Yes| F[Accept bare version, duplicate suffix, or ISO date]
C -->|No| G[Reject alternate version or malformed date]
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Greptile SummaryTightens the release-date verifier and adds committed regression coverage for its accepted and rejected heading forms.
Confidence Score: 5/5The PR appears safe to merge; both previous findings are fully addressed and no new actionable defect remains. The verifier now rejects stale and impossible dates by binding the optional clock-derived heading to the current UTC date, while the committed self-test exercises all fourteen grammar cases and propagates any disagreement through a non-zero exit status. Both previous threads were resolved after these fixes, and the current code fully addresses their concerns.
|
| Filename | Overview |
|---|---|
| scripts/verify-release-changelog-date.sh | Escapes the probe, binds optional dates to the current UTC date, centralizes heading recognition, and adds a fail-closed fourteen-case self-test. |
| test/release-workflow.test.ts | Invokes the verifier’s self-test through the normal suite and confirms that representative accept and reject cases execute. |
| CHANGELOG.md | Records the release-date control correction under the Unreleased fixes. |
| .agents/pm/issues/pm-github-bi6l.toon | Records the completed issue, implementation rationale, affected files, and test evidence. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
Test[Release workflow test] --> SelfTest[Verifier --self-test]
SelfTest --> Matrix[Fourteen heading cases]
Matrix --> Matcher[Escaped probe and recognized heading grammar]
Matcher --> Result{Actual verdict matches expected?}
Result -- No --> Fail[Non-zero exit]
Result -- Yes --> Continue[Check remaining cases]
Continue --> Pass[Zero exit after all cases]
Reviews (2): Last reviewed commit: "fix(release): bind the control's date to..." | Re-trigger Greptile
…ar a self-test
Two further Greptile P2s on this branch, both correct.
MALFORMED DATES STILL PASSED. Matching the optional date by its digit widths
(`[0-9]{4}-[0-9]{2}-[0-9]{2}`) accepts `## 2026.1.2 - 2026-13-40`, an impossible
date, and `## 2026.1.2 - 1999-01-01`, a stale one -- precisely the malformed date
implementations this control exists to reject. The date is now bound to the
actual clock date. That is the correct bound rather than a tighter guess: the
only date an unflagged run may legitimately carry is TODAY's, because deriving
the date from the clock is the defect --date-from-version removes.
THE GRAMMAR HAD NO TESTS. A normal run only ever sees the single heading this
checkout's generator happens to emit, so every accept and reject boundary was
unexercised and could regress silently -- and the aggregate release check does
not exercise them either. The script now has a `--self-test` mode that runs the
matcher against a fixed 14-case matrix and exits non-zero on any disagreement,
and test/release-workflow.test.ts invokes it. That keeps the pattern
single-sourced: the test asserts the script's own verdict instead of restating
the regular expression and drifting from it. It also asserts that specific
matrix rows actually ran, so a self-test that silently checked nothing cannot
pass.
The matrix covers what review of this control has actually caught: another
version, a prerelease suffix, an impossible date, a stale date, a non-date in the
date position, a heading matched only because an unescaped probe's dots acted as
wildcards, and the empty heading.
Confirmed non-vacuous by restoring the shape-only date match: the test fails
(8 pass / 1 fail) and passes again with the bound restored (9 / 0).
release:check exits 0.
|
@greptileai review Both P2s addressed in 6500db6 — the date is now bound to the clock date rather than to a digit-width shape, and the grammar has a committed 14-case matrix exercised through a You have now found four correct P2s across this control today, and the pattern in them is worth naming: each one was a case where the gate passed while being unable to observe the thing it claimed to check. That is a better description of the failure mode than anything I wrote in the original commit messages. |
|
Final reconciliation. Greptile re-reviewed after 6500db6 and returned 5/5 with no remaining findings. All four of its P2s on this control today were correct and all four are fixed; both threads on this PR carry the specific response and evidence. Upvoted every one. Worth recording what those four findings add up to, because it is a better description of the failure than my original commit messages gave. Every broken version of this guard passed the full gate suite. That is not incidental — a run of this verifier only ever observes the single heading the current generator emits, so the accept/reject boundaries the guard exists to enforce were never exercised by any real run, and the aggregate release check does not exercise them either. The progression was: pinned to a symptom (the generator stamps the clock) → broke when the symptom was fixed upstream; then "assert only that the headings differ" → certifies anything; then a delimiter boundary → still admits a non-date in the date position; then a digit-width date → admits an impossible date. Each fix was narrower than the last and still wrong, and CI was green throughout. What finally holds is a grammar bound to real values — the date bound to today's actual date rather than to a date-shaped string — plus a CI: test (22), test (26), CodeQL, codeql, semgrep all SUCCESS. |
Why this exists
Greptile raised a second P2 against the boundary merged here earlier today in #74, on the sibling
pm-linearPR. It applies identically to this repository, so this carries the fix across rather than leaving it local to the repo that happened to be reviewed.The finding
Excluding only a digit immediately after the probe version is barely narrower than accepting anything. All three of these passed the old boundary:
## 2026.1.2.3## 2026.1.2-rc1## 2026.1.2 - garbageThe third is the one that matters. It is exactly the shape a broken date implementation would emit, so the control was blind in precisely the direction this gate exists to watch.
The fix
The suffix is matched as a grammar of the forms a correct generator produces for one probe version, rather than excluded by a delimiter class:
That is the bare version, an optional
-<n>duplicate-section suffix (emitted when a section for that version already exists), and an optional ISO date. The failure message now names the accepted forms rather than only rejecting, so a future legitimate format change is actionable rather than mystifying.A second defect found while fixing the first
${probe}was interpolated into the pattern unescaped, so its dots matched any character —## 2026X1Y2would have been accepted as a heading for2026.1.2. The probe is now escaped before use.Verification
All nine cases checked explicitly:
npm run release:checkexits 0.pm items
pm-github-bi6l— Match the release-date control heading as a grammar and escape the probe version (history)Summary by Sourcery
Harden the release-date changelog control so malformed, mismatched, and wildcard-matching headings cannot pass verification.
New Features:
Bug Fixes:
Enhancements:
Tests:
Chores:
Summary by cubic
Resolves
pm-github-bi6lby tightening the release-date heading check. The old check only excluded a trailing digit, so## 2026.1.2 - garbage, other versions, and prerelease suffixes passed, and the probe's dots matched any character.-<n>duplicate suffix, optional- <today>date.--self-testmode to the verifier, invoked by the release workflow test.Written for commit 6500db6. Summary will update on new commits.