Skip to content

Match the release-date control heading as a grammar and escape the probe version - #75

Merged
unbraind merged 2 commits into
mainfrom
fix/match-the-date-control-heading-as-a-grammar
Sep 5, 2026
Merged

Match the release-date control heading as a grammar and escape the probe version#75
unbraind merged 2 commits into
mainfrom
fix/match-the-date-control-heading-as-a-grammar

Conversation

@unbraind

@unbraind unbraind commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Why this exists

Greptile raised a second P2 against the boundary merged here earlier today in #74, on the sibling pm-linear PR. 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:

heading why it must not be certified
## 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 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:

^## <probe>(-[0-9]+)?( - [0-9]{4}-[0-9]{2}-[0-9]{2})?$

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 — ## 2026X1Y2 would have been accepted as a heading for 2026.1.2. The probe is now escaped before use.

Verification

All nine cases checked explicitly:

  accept  ## 2026.1.2
  accept  ## 2026.1.2-2
  accept  ## 2026.1.2 - 2026-09-05
  accept  ## 2026.1.2-2 - 2026-01-02
  REJECT  ## 2026.1.2.3
  REJECT  ## 2026.1.2-rc1
  REJECT  ## 2026.1.2 - garbage
  REJECT  ## 2026.1.20 - 2026-08-27
  REJECT  ## 9999.1.1 - garbage

npm run release:check exits 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:

  • Add a self-test mode for validating release-heading recognition against representative accepted and rejected cases.

Bug Fixes:

  • Tighten release-date heading validation to accept only the probe version with an optional duplicate suffix and valid current-date form.
  • Escape the probe version when constructing the heading matcher so punctuation cannot act as wildcards.

Enhancements:

  • Improve verifier failure messages to describe the recognized heading forms.

Tests:

  • Add workflow tests that execute the verifier's self-test matrix and confirm key boundary cases are exercised.

Chores:

  • Record the release-heading verification fix in the changelog and project tracking files.

Summary by cubic

Resolves pm-github-bi6l by 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.

  • Matches headings against a grammar: bare version, optional -<n> duplicate suffix, optional - <today> date.
  • Binds the date to today's date, so impossible or stale dates are rejected.
  • Escapes the probe version before matching so its dots are literal.
  • Failure message now names the accepted heading forms.
  • Adds a --self-test mode to the verifier, invoked by the release workflow test.
  • Asserts specific matrix rows ran so a self-test that checks nothing cannot pass.

Written for commit 6500db6. Summary will update on new commits.

Review in cubic

…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.

@sourcery-ai sourcery-ai Bot 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.

Sorry @unbraind, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 1 hour and 31 minutes by commenting @sourcery-ai review. Upgrade to get a review now.

@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Team

Run ID: 28845c1c-3a77-48ee-bf9f-5280f24e8ba8


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai

sourcery-ai Bot commented Sep 5, 2026

Copy link
Copy Markdown
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

The 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 validation

flowchart 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]
Loading

File-Level Changes

Change Details Files
Tighten release-heading validation to an escaped, explicit grammar for valid probe-version outputs.
  • Allow only the bare version, numeric duplicate suffix, optional ISO date, or valid combinations.
  • Escape the interpolated probe version so regex metacharacters such as dots match literally.
  • Update failure diagnostics and inline rationale to describe the accepted grammar and reject malformed date/version variants.
scripts/verify-release-changelog-date.sh
Document the validation fix in project release metadata.
  • Add an Unreleased Fixed changelog entry linking the associated PM item.
CHANGELOG.md
Add tracking artifacts for the completed PM item.
  • Record the issue and its history in the PM metadata directory.
.agents/pm/issues/pm-github-bi6l.toon
.agents/pm/history/pm-github-bi6l.jsonl

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@greptile-apps

greptile-apps Bot commented Sep 5, 2026

Copy link
Copy Markdown

Greptile Summary

Tightens the release-date verifier and adds committed regression coverage for its accepted and rejected heading forms.

  • Escapes the probe version before interpolating it into the regular expression.
  • Restricts unflagged output to the probe’s supported heading grammar and binds an optional date to the current UTC date.
  • Adds a fourteen-case self-test covering valid forms, malformed dates, other versions, prereleases, wildcard-like punctuation, and empty output.
  • Runs the self-test through the normal TypeScript test suite.
  • Records the completed work in the changelog and project-management metadata.

Confidence Score: 5/5

The 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.

Important Files Changed

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]
Loading

Reviews (2): Last reviewed commit: "fix(release): bind the control's date to..." | Re-trigger Greptile

Comment thread scripts/verify-release-changelog-date.sh Outdated
Comment thread scripts/verify-release-changelog-date.sh Outdated
…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.
@unbraind

unbraind commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

@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 --self-test mode that the test suite invokes.

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.

@unbraind

unbraind commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

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 --self-test matrix the suite runs, so the boundaries are exercised rather than assumed. The test asserts the script's own verdict rather than restating the pattern, so the two cannot drift, and it checks that specific matrix rows appeared so a self-test that verified nothing cannot pass green.

CI: test (22), test (26), CodeQL, codeql, semgrep all SUCCESS.

@unbraind
unbraind merged commit 2a22b74 into main Sep 5, 2026
9 checks passed
@unbraind
unbraind deleted the fix/match-the-date-control-heading-as-a-grammar branch September 5, 2026 19:01
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.

1 participant