feat(checks): date-mismatch, a released entry's date against the day its tag was cut #19

Merged
mikluko merged 3 commits from mikluko/octant/1714-date-mismatch into main 2026-09-06 19:57:03 +00:00
mikluko commented 2026-09-06 14:36:37 +00:00 (Migrated from github.com)

Octant: T1714, on map D1726. Stacked on #14.

Nothing compared a released entry's date against the tag naming that version. date-format reads the shape, date-order and date-future read the document alone, and the immutability pair compares text — so an entry could claim 2026-09-01 while v1.2.3 was cut on the 11th and every check passed.

An entry no tag names is silent: a release pending is not a date that disagrees, and that is B1713's subject rather than this check's.

Two things it has to get right

Both are settled where the tag is read, not where the dates are compared, so internal/changelog only ever compares two YYYY-MM-DD strings.

A repository cuts one kind of tag or the other. An annotated tag is an object carrying a tagger date of its own; a lightweight tag is a bare reference whose only date is on the commit it names. Reading one kind is wrong on half the repositories in reach — this repo cuts lightweight, up2-monitoring cuts annotated.

The day is the tag's own, not UTC. A changelog date is a bare calendar day meaning the day the human cut the release. The tests pin one tag either side of a midnight in opposite directions (23:30-07:00 and 00:30+09:00), so a UTC reading fails both.

What running it against real history turned up

The check fired on this repository, and it was the check that was wrong:

CHANGELOG.md:145: error: version 1.0.0 is dated 2026-09-04 and tag v1 was cut on 2026-09-05

v1 is the moving major tag, and ParseTag reads it as 1.0.0, so the first tag matching the entry was a pointer whose date says when it last moved. Every shipped release would have read as misdated the moment v1 advanced. A version named by two tags is now judged at the fuller spelling, and TagComponents moved to internal/semver so that rule has one home rather than a private copy in internal/git.

Four existing fixtures also went red: they cut their tags today while their documents named February, and none of them was about dates. They now commit on the day they claim. That is the check earning its place before it has shipped.

Open, and yours

The ticket left two to settle while building. I implemented the defensible reading of each and both are one-line changes:

  • Severity is error. It catches an inconsistency rather than encoding a policy, and the precedent is undated-entry, which defaults to error and lets the one workflow that wants it switch it off.
  • Every tagged entry is judged, not the newest alone. The tag list is already read for already-tagged, so it costs nothing, and it catches history somebody rewrote.

Suite green whole, vet and gofmt clean, README regenerated from the register, and this repository's own document reads valid=true.

Octant: [T1714](http://127.0.0.1:9876/T1714), on map [D1726](http://127.0.0.1:9876/D1726). Stacked on #14. Nothing compared a released entry's date against the tag naming that version. `date-format` reads the shape, `date-order` and `date-future` read the document alone, and the immutability pair compares text — so an entry could claim `2026-09-01` while `v1.2.3` was cut on the 11th and every check passed. An entry no tag names is **silent**: a release pending is not a date that disagrees, and that is B1713's subject rather than this check's. ## Two things it has to get right Both are settled where the tag is read, not where the dates are compared, so `internal/changelog` only ever compares two `YYYY-MM-DD` strings. **A repository cuts one kind of tag or the other.** An annotated tag is an object carrying a tagger date of its own; a lightweight tag is a bare reference whose only date is on the commit it names. Reading one kind is wrong on half the repositories in reach — this repo cuts lightweight, `up2-monitoring` cuts annotated. **The day is the tag's own, not UTC.** A changelog date is a bare calendar day meaning the day the human cut the release. The tests pin one tag either side of a midnight in opposite directions (`23:30-07:00` and `00:30+09:00`), so a UTC reading fails both. ## What running it against real history turned up The check fired on this repository, and it was the check that was wrong: ``` CHANGELOG.md:145: error: version 1.0.0 is dated 2026-09-04 and tag v1 was cut on 2026-09-05 ``` `v1` is the moving major tag, and `ParseTag` reads it as `1.0.0`, so the first tag matching the entry was a **pointer whose date says when it last moved**. Every shipped release would have read as misdated the moment `v1` advanced. A version named by two tags is now judged at the fuller spelling, and `TagComponents` moved to `internal/semver` so that rule has one home rather than a private copy in `internal/git`. Four existing fixtures also went red: they cut their tags today while their documents named February, and none of them was about dates. They now commit on the day they claim. That is the check earning its place before it has shipped. ## Open, and yours The ticket left two to settle while building. I implemented the defensible reading of each and both are one-line changes: - **Severity is `error`.** It catches an inconsistency rather than encoding a policy, and the precedent is `undated-entry`, which defaults to `error` and lets the one workflow that wants it switch it off. - **Every tagged entry is judged, not the newest alone.** The tag list is already read for `already-tagged`, so it costs nothing, and it catches history somebody rewrote. Suite green whole, `vet` and `gofmt` clean, README regenerated from the register, and this repository's own document reads `valid=true`.
Sign in to join this conversation.
No description provided.