Skip to content

Pin the pm toolchain and assert the changelog-date flag by difference - #74

Merged
unbraind merged 3 commits into
mainfrom
chore/pin-toolchain-and-assert-changelog-date-flag
Sep 5, 2026
Merged

Pin the pm toolchain and assert the changelog-date flag by difference#74
unbraind merged 3 commits into
mainfrom
chore/pin-toolchain-and-assert-changelog-date-flag

Conversation

@unbraind

@unbraind unbraind commented Sep 5, 2026

Copy link
Copy Markdown
Owner

pm-changelog 2026.9.2 stopped stamping the wall clock. An unflagged run now emits the bare ## <version> heading where it used to emit ## <version> - <today>.

This repository's control in scripts/verify-release-changelog-date.sh only noted that mismatch and still exited 0. That is vacuous: a --date-from-version flag that did nothing would still pass, because the flagged heading is asserted independently and the unflagged run could not fail the script.

The flagged heading is still the version-dated form (## 2026.1.2 - 2026-01-02). The control now asserts only that the unflagged heading differs from that. Both the old clock-stamping generator and 2026.9.2 satisfy that property. A control identical to the flagged run still fails.

Pinned the current published toolchain, preserving each range style:

  • @unbrained/pm-cli 2026.9.5 (exact)
  • pm-changelog 2026.9.2 (exact)
  • pm-ops ^2026.9.5 (caret)

Evidence from this tree:

$ ./scripts/verify-release-changelog-date.sh
ok - .github/workflows/release.yml: 3 generator invocation(s), all flagged
ok - package.json: 2 generator invocation(s), all flagged
ok - with the flag the heading is version-derived: ## 2026.1.2 - 2026-01-02
ok - without the flag the heading is undated: ## 2026.1.2 (this is the defect the flag removes)
# exit 0

# identical control (unflagged invocation also passed --date-from-version):
ok - with the flag the heading is version-derived: ## 2026.1.2 - 2026-01-02
FAIL: without --date-from-version the heading is already '## 2026.1.2 - 2026-01-02', identical to the flagged run
# exit 1

npm run release:check exit 0. pm health --strict-exit exit 0.

pm items

Summary by Sourcery

Pin the pm toolchain and make changelog-date verification prove that the flag changes the generated heading.

Bug Fixes:

  • Harden changelog-date verification so an unflagged generator run must produce a valid probe-version heading that differs from the flagged result, preventing a no-op --date-from-version from passing.

Enhancements:

  • Update the verification control to support both clock-stamped and undated unflagged changelog headings while rejecting missing, malformed, or wrong-version output.

Build:

  • Pin @unbrained/pm-cli and pm-changelog to exact published versions and update pm-ops within its existing caret range.

Tests:

  • Validate the release changelog-date control against both supported generator behaviors and the no-op flag case.

Chores:

  • Record the toolchain and changelog-date verification maintenance item.

Summary by cubic

Pins the pm toolchain and hardens changelog-date verification so a no-op --date-from-version can no longer pass. pm-changelog 2026.9.2 stopped stamping the wall clock, so the unflagged heading is now bare ## <version>.

  • Bumps @unbrained/pm-cli to 2026.9.5, pm-changelog to 2026.9.2, and pm-ops to ^2026.9.5, preserving the existing exact/caret ranges.
  • The script now exits 1 when the unflagged heading is missing, identical to the flagged run, or not a heading for the probe version; previously it logged any mismatch and still passed.
  • Works with both generators: older releases emit the clock-derived heading, 2026.9.2 emits the bare undated form, and disambiguated headings like ## <version>-2 are also accepted.
  • npm run release:check and pm health --strict-exit both pass.

Written for commit 808d51b. Summary will update on new commits.

Review in cubic

pm-changelog 2026.9.2 stopped stamping the wall clock. An unflagged
heading is now the bare `## <version>` instead of `## <version> - <today>`.

This repo's control only noted that mismatch and still exited 0, so a
no-op `--date-from-version` would have passed. The control now fails
when the unflagged heading is missing or identical to the flagged
version-dated heading, which is true of both generators.

Pinned @unbrained/pm-cli 2026.9.5 (exact), pm-changelog 2026.9.2 (exact),
and pm-ops ^2026.9.5 (caret preserved).

  $ ./scripts/verify-release-changelog-date.sh
  ok - with the flag the heading is version-derived: ## 2026.1.2 - 2026-01-02
  ok - without the flag the heading is undated: ## 2026.1.2
  # exit 0

  # identical control (unflagged also passed --date-from-version):
  FAIL: without --date-from-version the heading is already '## 2026.1.2 - 2026-01-02', identical to the flagged run
  # exit 1

npm run release:check and pm health --strict-exit both exit 0.

@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 23 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: d09a6f1a-7bb4-4597-afae-100054815164


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

This PR pins the pm toolchain and updates the changelog-date control to verify the flag has an observable effect by requiring the unflagged heading to differ from the flagged, version-derived heading, while accommodating both legacy clock-stamped and newer undated generator output.

Sequence diagram for changelog date flag verification

sequenceDiagram
    participant Script as verify-release-changelog-date.sh
    participant Generator as pm-changelog

    Script->>Generator: generate with --date-from-version
    Generator-->>Script: ## 2026.1.2 - 2026-01-02
    Script->>Script: compare flagged heading with expected
    Script->>Generator: generate without --date-from-version
    Generator-->>Script: ## 2026.1.2
    Script->>Script: compare unflagged heading with flagged heading
    alt headings differ
        Script-->>Script: exit 0
    else headings identical or missing
        Script-->>Script: exit 1
    end
Loading

File-Level Changes

Change Details Files
Pin the published pm development toolchain to the current versions while preserving dependency range semantics.
  • Update the CLI and changelog generator to exact versions 2026.9.5 and 2026.9.2.
  • Update pm-ops to the caret range ^2026.9.5 and refresh the lockfile accordingly.
package.json
package-lock.json
Make the changelog-date verification fail when the unflagged control behaves identically to the flagged invocation.
  • Reject a missing unflagged heading.
  • Compare flagged and unflagged headings directly instead of requiring the obsolete wall-clock format.
  • Retain informative success output for both clock-derived and undated generator behavior.
scripts/verify-release-changelog-date.sh
Document and record the toolchain and release-check maintenance work.
  • Add an Unreleased changelog entry.
  • Add pm chore metadata and history records.
CHANGELOG.md
.agents/pm/chores/pm-github-c5i4.toon
.agents/pm/history/pm-github-c5i4.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

Pins the repository’s pm toolchain and strengthens changelog-date verification so the flag must produce a distinct, probe-version heading.

  • Updates @unbrained/pm-cli, pm-changelog, and pm-ops, with corresponding lockfile changes.
  • Supports the current undated unflagged heading and historical clock-derived output.
  • Rejects missing, identical, or wrong-version control headings.
  • Records the toolchain and verification change in the changelog and pm task history.

Confidence Score: 5/5

The PR appears safe to merge with no outstanding findings.

The dependency updates remain lockfile-controlled in automated installs, the repository rule is not violated, and the only previous Greptile thread was manually resolved after the author’s follow-up change.

Important Files Changed

Filename Overview
scripts/verify-release-changelog-date.sh Reworks the unflagged control to require output distinct from the flagged result while checking that it belongs to the probe version.
package.json Updates the three pm development dependencies while preserving their existing exact and caret range styles.
package-lock.json Locks the updated pm toolchain and its revised transitive dependency graph.
CHANGELOG.md Adds an Unreleased entry describing the toolchain pin and changelog-date assertion.
.agents/pm/chores/pm-github-c5i4.toon Records the completed maintenance item and the response to the previous review.
.agents/pm/history/pm-github-c5i4.jsonl Preserves the task’s lifecycle and review-response history.

Reviews (2): Last reviewed commit: "fix(release): bound the date control by ..." | Re-trigger Greptile

Comment thread scripts/verify-release-changelog-date.sh
Greptile raised a valid P2 against the loosened control: asserting only that
the unflagged heading DIFFERS from the flagged one means a generator emitting a
malformed heading, or a heading for the wrong version, is reported as "undated"
and passes. The control then proves the flag changed something, but not that it
changed the right thing -- and a control that vouches for output it cannot
recognise is exactly the failure the flagged half is there to prevent.

The open-ended else is replaced by an explicit allow-list of the two shapes a
correct generator can produce for the probe version:

  ## 2026.1.2                 (undated; pm-changelog 2026.9.2 and later)
  ## 2026.1.2 - <today>       (clock-derived; earlier releases)

Anything else fails and names both accepted forms next to what was received.
This keeps the property that made the loosening necessary -- the control is not
pinned to the clock form, so it still passes against the generator that fixed
the clock dependence -- while refusing to certify a heading it does not know.

Every branch verified to fire: a wrong-version heading fails as unrecognised,
undated and clock-derived both pass, a heading identical to the flagged run
fails, and an empty heading fails.

Tracked as pm-github-c5i4.
@unbraind

unbraind commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

@greptileai review

Pushed f292df1 addressing your P2 in full — the control no longer certifies a heading it cannot recognise. Please re-check that specific hunk in scripts/verify-release-changelog-date.sh.

Worth an adversarial second look: the allow-list now hardcodes the two shapes a correct generator produces for the probe version. That is deliberately narrower than "anything different", but it does mean a future legitimate heading format would fail the gate until the allow-list is extended. I think failing closed is right here — this script exists to catch the generator changing its date behaviour, so a new shape SHOULD stop the build and be reviewed rather than pass silently. If you disagree, say so, because that is the one judgement call in the fix.

…ated spellings

The allow-list of two spellings was too brittle. A sibling package's generator
also emits a disambiguated `## <probe>-2` heading when a section for that
version already exists, which is legitimate output the enumeration would have
failed on -- turning Greptile's correctness fix into a false build stop.

The bound is now the property rather than the spellings: the unflagged heading
must be a heading FOR THE PROBE VERSION, in some form other than the
version-derived one. That still rejects the case Greptile raised (a malformed
heading, or one for a different version, can no longer be certified as undated),
while accepting any legitimate non-version-derived spelling.

The trailing `[^0-9]` guard stops probe `2026.1.2` matching a heading for
`2026.1.20`. Verified: wrong-version, false-prefix, identical-to-flagged and
empty all fail; bare, disambiguated and clock-derived all pass.
@unbraind

unbraind commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

Final reconciliation. Greptile re-reviewed after the fix and returned 5/5 with no remaining findings; its original P2 is answered in-thread with the per-branch verification table. CodeRabbit and Sourcery summary-only, cubic NEUTRAL on its monthly cap. All upvoted, and the P2 comment itself upvoted — it was the most useful finding of the session.

Recording what that finding actually taught, because it generalises beyond this file. The original break was a guard pinned to a symptom (the generator stamps the clock) rather than to the property it exists to prove (the flag determines the date). Fixing that by asserting mere difference swung too far the other way and let the guard certify output it could not recognise. The stable form is a bound that names the property and its domain: the unflagged heading must be a heading for the probe version, in a non-version-derived form.

The evidence that settled the shape of the fix came from outside this PR — a sibling package's generator emits ## <probe>-2 to disambiguate a repeated version. An enumerated allow-list would have rejected that legitimate output, so the first version of my fix would have been a false build stop in another repository. Worth stating because it is the kind of thing a single-repository review cannot see.

The same tightening is now carried to the sibling repository that received the same loosening today (unbraind/pm-linear#101), so this is not being closed as a local fix.

@unbraind
unbraind merged commit eb56510 into main Sep 5, 2026
9 checks passed
@unbraind
unbraind deleted the chore/pin-toolchain-and-assert-changelog-date-flag branch September 5, 2026 18:33
unbraind added a commit that referenced this pull request Sep 5, 2026
…obe version (#75)

* fix(release): match the control heading as a grammar and escape the probe 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.

* fix(release): bind the control's date to the clock and give the grammar 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.

---------

Co-authored-by: SteveBot <1153461+unbraind@users.noreply.github.com>
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