Release notes checker: link rules from data-platform#98 (#70) - #73
Conversation
The [readme-env] reference definition pointed at blob/main/README.env, which drifts as main moves. It cannot point at blob/rel-1.16.0/ as data-platform#98 proposes: README.env was added in 700d2ed (#56), after the tag, with the backported verification section that links it, so that URL 404s. Pin it to 700d2ed instead, whose README.env is identical to main's today. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012nsCzraPATf4LCKVyUStfd
…98 (#70) Adds R1-R5 for rel-*.md and N1-N3 for NEXT.md: no relative links; osprey-dcs blob/tree/raw links pinned to the file's own tag (main in NEXT.md); paths and anchors into this repo checked against the working tree, with no anchor onto a duplicated heading; no leftover rel-<version>/<version>/<previous>. Reference definitions and bare URLs are covered, and code spans and fenced blocks are stripped for the link rules but not for R5. Every rel-*.md except the highest version counts as already released and gets the form rules only (R1, R2, R5). R2 also accepts a full 40-character commit SHA, for a target that did not exist at the tag, as with rel-1.16.0's README.env link. The script is now meant to be copied verbatim into the other four repos: everything repo-specific is in a marked block at the top, and the cosign identity rules for dp-service, dp-desktop-app, and dp-grpc are implemented behind it and self-tested under their own configurations in every copy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012nsCzraPATf4LCKVyUStfd
… checker (#70) NEXT.md's "Cutting the release" steps 6 and 8 now rely on the checker to list missed links and leftover placeholders rather than on the reader, and its preamble states the link convention. CLAUDE.md records the rules and which notes count as already released. plan/tickets/70/plan.md records the triage findings and design decisions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012nsCzraPATf4LCKVyUStfd
… docstring (#70) Decisions made after the four ports reported back (data-platform#98): - The script lives at .github/scripts/check-release-notes.py in all five repos. CI, release.yml, CLAUDE.md, NEXT.md and the #70 plan point there; release.yml keeps its push-only guard, and no `uses:` pin changed. Plans for earlier tickets keep the old path, as a record of where it was. The new path is not gitignored, so ruff now lints and formats it by default. - The repository root is the first directory at or above the script containing .git (a directory, or a file in a worktree), not a fixed parents[2]; with none, it exits with a clear message. Self-tested for both .git forms and for its absence. - R5 catches only rel-<version> and <previous>. A bare <version> is used deliberately in prose that survives the cut (dp-service-<version>.jar.sha256), and now passes. - Nothing outside the configuration block is specific to dp-python-lib: no fixed usage path, no plan/tickets/56 citation, no body_path wording, no rel-1.16.0 history. - The docstring says releases/tag/... and compare/... links are deliberately left to R2's silence, since linking an earlier release is legitimate; a self-test case holds that. - The self-test's fixture files are written with encoding="utf-8". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012nsCzraPATf4LCKVyUStfd
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Valid Markdown can bypass or be incorrectly accepted by the new link checks, and R4 differs from its linked requirement.
Review effort: Balanced
Findings: 3
Open (3)
What changed in this PR
Extends and relocates the release-notes checker to validate links, anchors, placeholders, and signing identities across repositories.
Changes:
- Adds R1–R5/N1–N3 validation and expanded self-tests.
- Moves the checker into
.github/scripts/and updates CI/release workflows. - Pins the historical
README.envlink and documents the release process.
| File | Description |
|---|---|
.github/scripts/check-release-notes.py |
Adds the expanded checker and self-tests. |
.dev/tools/check-release-notes.py |
Removes the old checker location. |
.github/workflows/ci.yml |
Uses the relocated checker. |
.github/workflows/release.yml |
Uses the relocated checker at release time. |
doc/release-notes/rel-1.16.0.md |
Pins README.env to an immutable commit. |
doc/release-notes/NEXT.md |
Documents link rules and cutting steps. |
CLAUDE.md |
Records release-note link conventions. |
plan/tickets/70/plan.md |
Captures design and implementation decisions. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| slug = base if n == 0 else f"{base}-{n}" | ||
| anchors.add(slug) | ||
| by_base.setdefault(base, []).append(slug) | ||
| duplicated = {slug for slugs in by_base.values() if len(slugs) > 1 for slug in slugs} |
There was a problem hiding this comment.
Declined. This narrowing is an approved departure, recorded as item 7 in the latest comment on osprey-dcs/data-platform#98: R4 rejects a duplicate only when a link's anchor lands on it. Otherwise the two unlinked ### Methods headings in rel-1.16.0.md would fail. The docstring also states the rule.
- A reference definition whose destination is on the next line (`[x]:` then ` ../README.md`) is valid CommonMark, and slipped past R1/N1. The pattern now accepts one line break before the target. - A same-document `#L<n>` anchor passed R4, but a release body has no line anchors; they are now accepted only on blob links. - An `http://` github.com or raw link was not parsed as a repo link, so a `main`-pinned one bypassed R2/N2. Both patterns accept http too. Each gap has a self-test case that fails if the fix is reverted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012nsCzraPATf4LCKVyUStfd
Found by Copilot on the port PRs (dp-desktop-app#52, dp-grpc#169), and folded into the reference copy so all five copies stay identical: - R2/N2 apply only to the five lockstep repos (LOCKSTEP_REPOS, outside the per-repo block). A link into another osprey-dcs repo, such as dp-support, has its own release cycle and is no longer checked. - An `<a name>`/`id` quoted in a fenced block or code span no longer counts as an anchor for R4/N3. - An option value on the line after a backslash continuation (`--certificate-identity \` then the value) is read as the value, for the cosign and sigstore identity and issuer rules, instead of reporting the backslash as a wrong identity. - The docstring lists blob line anchors (`#L<n>`) as not range-checked. Each fix has self-test cases that fail if it is reverted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012nsCzraPATf4LCKVyUStfd
…#70) Found by Copilot on osprey-dcs/dp-service#306. An unquoted image in prose ("pull ghcr.io/osprey-dcs/dp-service:rel-2.1.0.") was read as tag `rel-2.1.0.` and failed correct notes; a trailing comma did the same. Trailing `.,;:!?` is now stripped from the captured tag, as urls() does, so a `rel-2.1.0-rc1` tag is still read whole. Self-tests cover a good tag followed by a period and by a comma, and an -rc1 tag before a period. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012nsCzraPATf4LCKVyUStfd

Closes #70. Step 1 of osprey-dcs/data-platform#98.
Extends
check-release-notes.pywith the link rules from data-platform#98, and moves it to.github/scripts/check-release-notes.py, the location now shared by all five repos. The other four repos copy this file verbatim; only the marked configuration block at the top differs.Rules
rel-*.md:rel-<version>or<previous>.NEXT.md: no relative links, osprey-dcs links onmain, and R3/R4.rel-*.mdexcept the highest version, which gets only R1, R2 and R5.Departures from #98's wording
rel-1.16.0.mdlinksREADME.env, which was added after the tag (700d2ed, Release body is missing the verification and install instructions (rel-1.16.0) #56), soblob/rel-1.16.0/README.env404s. The link was onmainand is now pinned to 700d2ed.<version>. Three repos use it on purpose in prose that stays after the cut, such asdp-service-<version>.jar.sha256..git, instead of being a fixed depth.Testing
rel-1.16.0.mdandNEXT.md.ruff,ruff formatandmypyare clean. Unit tests: 968 passed.After merge
The published
rel-1.16.0body is the notes file unchanged, so correcting it takes one command:The only diff against the published body is the
[readme-env]link.Merge this before the port PRs (osprey-dcs/dp-grpc#169, osprey-dcs/dp-service#306, osprey-dcs/dp-desktop-app#52, osprey-dcs/data-platform#109): they copy this script, so any change made in review has to be copied to them as well.
🤖 Generated with Claude Code
https://claude.ai/code/session_012nsCzraPATf4LCKVyUStfd