fix(docs): anchor release dates to git tags and replace the release guards - #183
fix(docs): anchor release dates to git tags and replace the release guards#183flyingrobots wants to merge 4 commits into
Conversation
CHANGELOG release headings recorded a planned biweekly schedule running
2026-06-24 through 2026-11-04, while every tag was actually cut between
2026-06-21 and 2026-06-30. The dates were never a typo: release-prep
writes "## [{tag}] - {target_date}" at scaffold time from
next_release_target_date(), and nothing back-filled the real date after
tagging. Keep a Changelog, which this file claims to follow, defines that
heading as the release date.
Realigns all four surfaces that carry the dates:
- CHANGELOG.md: 11 release headings
- docs/topics/release-process/policy.toml: 10 target_date fields
- docs/releases/*.md: 11 "Target date" lines
- xtask/src/tests.rs: the alpha_changelog_dates_match_release_policy
table and the nine release_policy_tracks_v0_N_boundary guards
The synthetic temp-repo fixtures in xtask/src/tests.rs keep their
original dates; their values are load-bearing for the +14 scaffolding
arithmetic and describe no real release.
target_date keeps its field name but now holds the actual publication
date, so RELEASE-REQ-008, its fixture oracle, and RELEASE-TP-004 are
restated to say so rather than describing a planned date.
Known gap, recorded in the release-process test plan:
next_release_target_date still adds 14 days to the last entry, which now
seeds 2026-07-14, already past. release-prep needs an explicit or
clock-derived date before the next release.
docs-impact: documentation and release-guard fixtures only; no library,
schema, or golden artifact change. Verified with cargo xtask verify
(full gate, 52s), 76 xtask tests, contract-check 23 shelves, and
markdownlint 0 errors.
…pies The release date guards were fragile and largely tautological. alpha_changelog_dates_match_release_policy asserted that CHANGELOG.md dates equalled policy.toml target_date values, but release-prep generates both from that one field. Two copies written from a single source always agree, including when the source is wrong, so the guard stayed green through four months of drift across all eleven releases and only failed once a human corrected one side by hand. It detected intervention, not incorrectness. The nine release_policy_tracks_v0_N_boundary tests (plus v0_2) matched substrings against the whole policy file rather than the release's own block. Verified: setting v0.3's target_date to 1999-01-01 left release_policy_tracks_v0_3_boundary passing, because v0.4 carried the same date string. Releases tagged on the same day share dates, so that is the normal case. They were also ~250 lines of frozen-history boilerplate, auto-written by a generator, asserting that a static file still contained strings someone typed. Replaces them with: - `cargo xtask release-dates`, which reconciles policy.toml, CHANGELOG.md, and docs/releases/*.md against `git for-each-ref refs/tags` -- the independent authority for when a release happened. Wired into `xtask verify`. Date contradictions fail; absent surfaces are reported as uncovered rather than failing an otherwise-correct history, since the earliest releases predate these surfaces. A clone without tags says it skipped instead of passing vacuously. - release_policy_blocks_are_structurally_complete, one data-driven test over parsed blocks, replacing eleven near-duplicates. - release_policy_block_parsing_scopes_fields_to_their_own_release, a regression guard pinning the 1999-01-01 false-pass. - reconcile_release_dates, a pure function over already-read inputs, with hermetic tests. cargo test needs no git tags, so CI's shallow checkout is unaffected. release-prep no longer scaffolds Rust test stubs or changelog date guard entries. Per-release scope and non-goal content is now reviewed rather than string-tested, matching the repo's own rule in docs/topics/documentation/test-plan.md that policy detail is not encoded as a Rust test; the ten affected requirement and test-case rows move to `policy` status accordingly. docs-impact: release-process README, test plan, and policy.toml updated with the new design and two recorded open gaps. Verified with cargo xtask verify (full gate, 9s), 69 xtask tests, and markdownlint 0 errors.
Summary by CodeRabbit
WalkthroughThe release process now synchronizes published dates across policy, changelog, and release notes. A new ChangesRelease process synchronization
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
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 |
There was a problem hiding this comment.
Actionable comments posted: 12
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
xtask/src/release_prep.rs (1)
74-74: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
next_release_target_datenow feeds a field whose meaning changed under it, and the next release-prep run will produce a self-contradicting artifact.This PR redefines
target_datefrom a planned date to the recorded tag date. Every value inpolicy.toml,CHANGELOG.md, and the release notes now records when a release actually happened.next_release_target_datestill computes a plan by adding 14 days to the last entry.The consequence is deterministic, not speculative:
- The last realigned entry is 2026-06-30.
next_release_target_datereturns 2026-07-14.release_prepwrites 2026-07-14 into the policy block, the changelog section, and the release-notes stub.- The next tag is created on its real date, which is not 2026-07-14.
- The
release_datesgate added in this PR reports three drift lines and failscargo xtask verify.The PR objectives record this and choose not to resolve it, and
docs/topics/release-process/test-plan.mdlines 131-137 describe it as failing loudly. That reasoning holds for detection. It does not hold for generation:release_prepnow emits a value that is known in advance to be wrong, and the operator must edit three generated surfaces by hand on every release.Since the field now records a date rather than planning one, the scaffold should write the current UTC date and let the reconciliation confirm it, or require the date as an explicit argument.
Do you want me to open an issue that tracks replacing the 14-day increment with an explicit or clock-derived date, and to draft the RED test for it?
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@xtask/src/release_prep.rs` at line 74, The release_prep flow currently uses next_release_target_date to generate a planned date for a field that now represents the actual release date. Update the release preparation logic around release_prep and next_release_target_date to use the current UTC date or require an explicit date argument, removing the unconditional 14-day projection so generated policy, changelog, and release-note values reflect the recorded release date.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@CHANGELOG.md`:
- Around line 243-249: Update the changelog wording describing target_date to
say it records the actual Git tag date, not the real publication date. Preserve
the distinction between Git tag dates and GitHub Release publication dates,
consistent with release-date validation in release_dates.rs and the
v0.4.0-alpha.1 release documentation.
In `@docs/topics/release-process/README.md`:
- Around line 63-70: Update the release-process README paragraph describing
`cargo xtask release-dates` to explicitly define `policy.toml`’s `target_date`
and each release note’s `Target date:` as the release publication date, not a
planned date. Keep the existing explanation of git tags as the independent
authority and verification behavior, and do not rename the field or modify
parser and release-note files.
In `@docs/topics/release-process/test-plan.md`:
- Line 104: Rename the test at xtask/src/tests.rs around
release_prep_scaffolds_version_policy_changelog_and_test_stub to reflect that
release-prep does not write a Rust test stub, and update RELEASE-TP-019’s
evidence name to match exactly. Preserve the test behavior and all other row
content.
- Line 77: Update the CHANGELOG.md oracle description in the release test plan
to identify the corresponding git tag date as the authority for published alpha
release sections, replacing the reference to the release policy. Keep the
existing release-history scope and align this wording with the rule already
stated on line 89.
In `@xtask/src/release_dates.rs`:
- Around line 98-109: Update the unsuccessful-command branch after
Command::new("git") in the release-date logic to include the captured
output.stderr in the returned error message. Preserve the existing success path
and command-execution error handling while making the failure message retain
git’s diagnostic text.
- Around line 100-102: Update the git ref scan in the release-date collection
logic to restrict tags to the release naming contract: tags beginning with “v”
and matching the version format accepted by ReleasePrepVersion::parse, rather
than enumerating all refs/tags. Preserve the existing creator-date formatting
and downstream verification behavior.
- Around line 167-172: Update the release_dates validation around block.status
so a known pre-publication status such as “prep” with an existing tag is
reported as a gap, not drift; retain drift for statuses outside the known set.
First add a RED test covering this existing-tag/prep case and asserting no
drift, then implement the classification change and update RELEASE-TP-004 in the
release process test plan.
- Around line 209-214: Update release_dates() so the tags.is_empty() branch
treats missing git tags as a failed reconciliation rather than returning Ok(()).
Preserve the diagnostic message or make it clearly indicate failure, and return
the established error type used by release_dates() so release gating fails
closed.
- Around line 99-103: Update the git tag date retrieval in the release-dates
command around the for-each-ref invocation to force UTC and emit raw UTC date
fields with a stable YYYY-MM-DD formatter, replacing the timezone-dependent
creatordate:short format. Keep the existing Published-versus-tag comparison
behavior unchanged.
In `@xtask/src/tests.rs`:
- Around line 2780-2789: The release-notes policy validation does not verify
that each section key matches its tag. In the test block around seen_tags and
target_date, derive the expected section key from tag using the same
transformation as ReleasePrepVersion::parse, assert it equals section, and first
add a mismatched fixture to confirm the test fails before implementing the
assertion.
- Around line 1772-1779: Replace the ineffective absent-substring check in the
release-prep test with an exact artifact comparison: extract the seeded xtask
test file contents into a shared RELEASE_PREP_FIXTURE_XTASK_TESTS constant, use
it when creating the fixture, then assert the file remains byte-for-byte
identical after release_prep. This must detect any modification or deletion of
the seeded file while preserving the existing fixture behavior.
- Around line 2766-2770: Update the block-count assertion in the relevant test
to require exactly 11 entries instead of allowing any count greater than or
equal to 10, preserving the existing assertion message and using the release
fixture count as the expected value.
---
Outside diff comments:
In `@xtask/src/release_prep.rs`:
- Line 74: The release_prep flow currently uses next_release_target_date to
generate a planned date for a field that now represents the actual release date.
Update the release preparation logic around release_prep and
next_release_target_date to use the current UTC date or require an explicit date
argument, removing the unconditional 14-day projection so generated policy,
changelog, and release-note values reflect the recorded release date.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 61910f40-5043-44ed-8a09-665102a850bd
📒 Files selected for processing (19)
CHANGELOG.mddocs/releases/v0.1.0-alpha.1.mddocs/releases/v0.10.0-alpha.1.mddocs/releases/v0.11.0-alpha.1.mddocs/releases/v0.2.0-alpha.1.mddocs/releases/v0.3.0-alpha.1.mddocs/releases/v0.4.0-alpha.1.mddocs/releases/v0.5.0-alpha.1.mddocs/releases/v0.6.0-alpha.1.mddocs/releases/v0.7.0-alpha.1.mddocs/releases/v0.8.0-alpha.1.mddocs/releases/v0.9.0-alpha.1.mddocs/topics/release-process/README.mddocs/topics/release-process/policy.tomldocs/topics/release-process/test-plan.mdxtask/src/main.rsxtask/src/release_dates.rsxtask/src/release_prep.rsxtask/src/tests.rs
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
- GitHub Check: supply-chain (cargo-deny)
- GitHub Check: rust stable (fmt · clippy · test)
- GitHub Check: rust msrv 1.94.0 (fmt · clippy · test)
🧰 Additional context used
📓 Path-based instructions (6)
**/*
📄 CodeRabbit inference engine (AGENTS.md)
**/*: Never amend Git commits, usegit rebasewithout explicit user approval, or force any Git operation; use new commits and regular merge commits instead.
Do not create draft pull requests, and never use acodexprefix in branch names, pull request titles, or commit messages.
Pull requests for issue work must include GitHub auto-close text such asCloses#123`` for every issue they intend to close.
Usecodex-think --remember --jsonwhen starting a session, entering the repository, or regaining context, and record significant durable events with `codex-think "..." --json`. Treat Think as memory rather than repository truth.
Every pull request body must contain `## Plain-English Walkthrough` with `### TL;DR` and `### Walkthrough`, explaining the prior behavior, new model and dataflow, invariants, failures, compatibility, and verification as applicable.
Use Mermaid diagrams for nontrivial flow, lifecycle, ownership, or component interaction when clearer than prose; every diagram requires an introductory paragraph, the diagram, the exact collapsed caption structure, and a concluding interpretation.
Tag each material technical claim at first occurrence as `[claim:, confidence:]`, cite evidence using repository-relative paths, line numbers, and Git SHAs, and end the explanatory body with a collapsed citations appendix.
If CodeRabbit is actively reviewing, obtain its approval before merge; if unavailable due to limits or credits, request `@codex review please` and wait for the alternate response. Do not treat unavailability as approval unless a maintainer explicitly overrides the gate.
For release preparation, write the release thesis first, reconcile changes from the previous tag, update release policy and tests, verify the milestone has no open issues and no unauthorized crates.io publication occurred, and record a durable release report.
Run `cargo xtask verify` before claiming a branch is ready.
Files:
docs/releases/v0.2.0-alpha.1.mddocs/releases/v0.11.0-alpha.1.mddocs/releases/v0.10.0-alpha.1.mddocs/releases/v0.1.0-alpha.1.mddocs/releases/v0.6.0-alpha.1.mddocs/releases/v0.8.0-alpha.1.mddocs/releases/v0.4.0-alpha.1.mdxtask/src/main.rsCHANGELOG.mddocs/releases/v0.5.0-alpha.1.mddocs/releases/v0.9.0-alpha.1.mddocs/releases/v0.3.0-alpha.1.mddocs/topics/release-process/README.mddocs/releases/v0.7.0-alpha.1.mdxtask/src/tests.rsxtask/src/release_prep.rsdocs/topics/release-process/test-plan.mddocs/topics/release-process/policy.tomlxtask/src/release_dates.rs
**/*.{rs,md}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{rs,md}: Tests must assert software behavior and stable error kinds or structured artifacts, not implementation details, prose, paths, or merelyis_err(); documentation-tool tests may test validator behavior.
For nontrivial behavior, contract, workflow, release, schema, validation, or public-surface changes, follow RED/GREEN TDD: update the owningtest-plan.md, write the deterministic test first, observe the RED failure, implement the smallest coherent fix, then mark the case implemented only after executable evidence exists.
Files:
docs/releases/v0.2.0-alpha.1.mddocs/releases/v0.11.0-alpha.1.mddocs/releases/v0.10.0-alpha.1.mddocs/releases/v0.1.0-alpha.1.mddocs/releases/v0.6.0-alpha.1.mddocs/releases/v0.8.0-alpha.1.mddocs/releases/v0.4.0-alpha.1.mdxtask/src/main.rsCHANGELOG.mddocs/releases/v0.5.0-alpha.1.mddocs/releases/v0.9.0-alpha.1.mddocs/releases/v0.3.0-alpha.1.mddocs/topics/release-process/README.mddocs/releases/v0.7.0-alpha.1.mdxtask/src/tests.rsxtask/src/release_prep.rsdocs/topics/release-process/test-plan.mdxtask/src/release_dates.rs
**/*.{md,mdx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{md,mdx}: Documentation pages must have one primary reader job, separate user task help from contributor architecture and evidence maps, use concrete valid examples with expected results when relevant, and keep exact public facts in validated or generated reference material.
Update affected documentation in the same change as behavior, schema, release, workflow, or public-surface changes, or statedocs-impact: nonewith a concise rationale.
Files:
docs/releases/v0.2.0-alpha.1.mddocs/releases/v0.11.0-alpha.1.mddocs/releases/v0.10.0-alpha.1.mddocs/releases/v0.1.0-alpha.1.mddocs/releases/v0.6.0-alpha.1.mddocs/releases/v0.8.0-alpha.1.mddocs/releases/v0.4.0-alpha.1.mdCHANGELOG.mddocs/releases/v0.5.0-alpha.1.mddocs/releases/v0.9.0-alpha.1.mddocs/releases/v0.3.0-alpha.1.mddocs/topics/release-process/README.mddocs/releases/v0.7.0-alpha.1.mddocs/topics/release-process/test-plan.md
**/*.md
📄 CodeRabbit inference engine (AGENTS.md)
Use tables for consistent-shape comparisons and evidence, bullets for unordered sets, numbered lists for ordered procedures or states, and focused branch-accurate snippets for exact syntax.
Files:
docs/releases/v0.2.0-alpha.1.mddocs/releases/v0.11.0-alpha.1.mddocs/releases/v0.10.0-alpha.1.mddocs/releases/v0.1.0-alpha.1.mddocs/releases/v0.6.0-alpha.1.mddocs/releases/v0.8.0-alpha.1.mddocs/releases/v0.4.0-alpha.1.mdCHANGELOG.mddocs/releases/v0.5.0-alpha.1.mddocs/releases/v0.9.0-alpha.1.mddocs/releases/v0.3.0-alpha.1.mddocs/topics/release-process/README.mddocs/releases/v0.7.0-alpha.1.mddocs/topics/release-process/test-plan.md
**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
**/*.rs: For Rust changes, preserve claim integrity by providing executable evidence, keep compiler and validation paths deterministic and free of hidden I/O, and prefer structured public failures with stable error kinds over prose-only diagnostics.
Do not add Rust dependencies without pull-request rationale and contract-impact notes; treat planned lint, dependency, and fuzzing ratchets as planned until executable checks land.
Files:
xtask/src/main.rsxtask/src/tests.rsxtask/src/release_prep.rsxtask/src/release_dates.rs
docs/topics/**
📄 CodeRabbit inference engine (AGENTS.md)
docs/topics/**: Topic shelves document landed behavior:README.mddescribes current HEAD truth,test-plan.mdrecords verification and known gaps, and optional architecture or rationale pages contain durable supporting information.
For every nontrivial behavior, contract, workflow, release, schema, validation, or public-surface change, identify or create the owning topic shelf, updatetest-plan.md, add executable evidence, updateREADME.mdonly after behavior exists, and runcargo xtask verify.
Do not update topic shelves for purely mechanical edits that do not change a contract; explain the omission in the pull request or final report.
Files:
docs/topics/release-process/README.mddocs/topics/release-process/test-plan.mddocs/topics/release-process/policy.toml
🪛 LanguageTool
docs/topics/release-process/test-plan.md
[uncategorized] ~46-~46: The official name of this software platform is spelled with a capital “H”.
Context: ... and dispatches release publication. | .github/workflows/auto-release-tag.yml, docs/to...
(GITHUB)
[uncategorized] ~47-~47: The official name of this software platform is spelled with a capital “H”.
Context: ... the milestone has zero open issues. | .github/workflows/release.yml, docs/topics/rele...
(GITHUB)
[uncategorized] ~49-~49: The official name of this software platform is spelled with a capital “H”.
Context: ...uest, and derives the requested tag. | .github/workflows/auto-release-tag.yml, docs/to...
(GITHUB)
[grammar] ~59-~59: Ensure spelling is correct
Context: ...ocs/topics/release-process/policy.toml, xtask/src/release_dates.rs | ## Fixtures | Fixture | Purpose | Oracle |...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
[uncategorized] ~93-~93: The official name of this software platform is spelled with a capital “H”.
Context: ...ports_dispatch_and_milestone_closure | .github/workflows/auto-release-tag.yml, .github...
(GITHUB)
[uncategorized] ~93-~93: The official name of this software platform is spelled with a capital “H”.
Context: ...github/workflows/auto-release-tag.yml, .github/workflows/release.yml, docs/topics/rele...
(GITHUB)
[uncategorized] ~95-~95: The official name of this software platform is spelled with a capital “H”.
Context: ...auto_release_tag_workflow_is_guarded | .github/workflows/auto-release-tag.yml, docs/to...
(GITHUB)
🔇 Additional comments (23)
docs/releases/v0.10.0-alpha.1.md (1)
3-3: LGTM!docs/releases/v0.8.0-alpha.1.md (1)
3-3: LGTM!docs/releases/v0.9.0-alpha.1.md (1)
3-3: LGTM!docs/releases/v0.11.0-alpha.1.md (1)
3-3: LGTM!xtask/src/release_dates.rs (2)
31-78: LGTM!
216-244: LGTM!xtask/src/main.rs (1)
133-152: LGTM!Also applies to: 227-227
xtask/src/release_prep.rs (1)
31-35: LGTM!Also applies to: 92-100, 289-289, 319-321
xtask/src/tests.rs (3)
2818-2856: LGTM!
2858-2936: LGTM!
1704-1704: 🗄️ Data Integrity & IntegrationNo change needed.
policy.toml,runbook.md, and the repository have no remaining references to the removed boundary-test surface.docs/topics/release-process/README.md (1)
58-61: LGTM!docs/topics/release-process/test-plan.md (2)
41-59: LGTM!Also applies to: 88-103, 105-106
131-141: LGTM!CHANGELOG.md (1)
362-362: LGTM!Also applies to: 416-416, 456-456, 487-487, 505-505, 528-528, 552-552, 569-569, 606-606, 655-655, 674-674
docs/releases/v0.1.0-alpha.1.md (1)
3-3: LGTM!docs/releases/v0.2.0-alpha.1.md (1)
3-3: LGTM!docs/releases/v0.3.0-alpha.1.md (1)
3-3: LGTM!docs/releases/v0.4.0-alpha.1.md (1)
3-3: LGTM!docs/releases/v0.5.0-alpha.1.md (1)
3-3: LGTM!docs/releases/v0.6.0-alpha.1.md (1)
3-3: LGTM!docs/releases/v0.7.0-alpha.1.md (1)
3-3: LGTM!docs/topics/release-process/policy.toml (1)
47-47: LGTM!Also applies to: 147-147, 167-167, 192-192, 214-214, 241-241, 272-272, 317-317, 366-366, 408-408, 447-447
| assert!( | ||
| blocks.len() >= 10, | ||
| "release policy should retain a block per published release, found {}", | ||
| blocks.len() | ||
| ); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Replace the >= 10 lower bound with the exact count.
This PR realigns eleven releases, and docs/topics/release-process/test-plan.md lists eleven release-notes fixtures, v0.1.0-alpha.1 through v0.11.0-alpha.1. A lower bound of 10 lets one historical block be deleted without any test failing. Silent loss of a historical policy block is the precise failure class this PR sets out to eliminate.
The assertion message already claims "a block per published release". Make the assertion say it.
🐛 Proposed fix
+ // One block per published release. Update deliberately when a release
+ // lands, so a deleted historical block fails instead of passing a bound.
assert!(
- blocks.len() >= 10,
- "release policy should retain a block per published release, found {}",
+ blocks.len() >= 11,
+ "release policy must retain a block per published release, found {}",
blocks.len()
);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@xtask/src/tests.rs` around lines 2766 - 2770, Update the block-count
assertion in the relevant test to require exactly 11 entries instead of allowing
any count greater than or equal to 10, preserving the existing assertion message
and using the release fixture count as the expected value.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 89ca929a48
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| [release_notes.v0_11_0_alpha_1] | ||
| tag = "v0.11.0-alpha.1" | ||
| target_date = "2026-11-04" | ||
| target_date = "2026-06-30" |
There was a problem hiding this comment.
Stop seeding the next release from the last tag date
Changing the final policy date to 2026-06-30 makes the unchanged next_release_target_date calculation scaffold 2026-07-14 for v0.12, which is already in the past as of 2026-08-04. That stale value is copied into the changelog, policy, and release notes, while release-dates cannot detect it until the tag exists, so the next release-prep branch passes pre-tag verification with a knowingly incorrect date. Release prep should accept or derive a current release date instead of adding 14 days to historical publication data.
Useful? React with 👍 / 👎.
| for gap in &report.gaps { | ||
| println!("release-dates: uncovered - {gap}"); | ||
| } | ||
| if report.drift.is_empty() { |
There was a problem hiding this comment.
Fail verification for unexpected coverage gaps
When an existing tagged release loses its changelog heading, policy block, release-notes file, or Target date: line, reconciliation records only a gap; this branch nevertheless returns success whenever no contradictory date remains. Consequently, deleting the date-bearing surface can make cargo xtask verify pass instead of detecting the regression. Legacy omissions should be explicitly allowlisted, while new gaps in currently covered releases should fail the gate.
AGENTS.md reference: AGENTS.md:L82-L83
Useful? React with 👍 / 👎.
| provider_contract_pack(root, ProviderContractPackMode::Check)?; | ||
| provider_runtime_dependencies(root)?; | ||
| contract_check(root)?; | ||
| release_dates(root)?; |
There was a problem hiding this comment.
Run the real date reconciliation in required CI
Checked .github/workflows/ci.yml: pull-request jobs run format, clippy, workspace tests, and the provider fixture check, but never cargo xtask verify or cargo xtask release-dates, and their checkout does not fetch tags. The removed alpha_changelog_dates_match_release_policy guard ran under the workspace tests, whereas the replacement tests use only synthetic maps, so a PR can now change policy, changelog, and notes to the same wrong date and pass every required CI job. Add a tag-aware reconciliation step to CI rather than wiring it only into the optional local gate.
AGENTS.md reference: AGENTS.md:L49-L57
Useful? React with 👍 / 👎.
| if status == "published" { | ||
| for placeholder in ["TODO_release_scope", "TODO_release_non_goal"] { |
There was a problem hiding this comment.
Reject scaffold placeholders before release-prep can merge
For the normal next-release flow, release-prep creates a block with status = "prep" and TODO_release_scope/TODO_release_non_goal, but this replacement guard rejects placeholders only after status becomes published. The removed generated boundary test rejected those placeholders while the block was still prep; without it, normal CI can pass and the auto-release workflow can tag and publish a release-prep merge whose structured scope remains TODO. Apply the placeholder check to release-prep blocks as well, rather than relying only on the new policy review row.
AGENTS.md reference: AGENTS.md:L77-L83
Useful? React with 👍 / 👎.
| "--format=%(refname:short)\t%(creatordate:short)", | ||
| "refs/tags", |
There was a problem hiding this comment.
Do not use commit dates for lightweight release tags
Git defines creatordate as the committer or tagger date depending on the referenced object type, as documented in git-for-each-ref. For a lightweight v* tag—which .github/workflows/release.yml currently accepts—this therefore reports the tagged commit's committer date, not when the tag was pushed or the release published. Tagging an older main commit makes the checker reject every correct recorded release date; either require annotated release tags or obtain publication time from an authority that exists for both tag types.
AGENTS.md reference: AGENTS.md:L82-L83
Useful? React with 👍 / 👎.
Seventeen review findings on #183, all addressed. Correctness of the check itself: - Tag dates are read in UTC. `%(taggerdate)` renders in the reader's timezone, so a tag made near midnight resolves to different days for different operators. This was not theoretical: v0.4.0-alpha.1 is 2026-06-24 in PDT and 2026-06-25 in UTC, so the earlier realignment recorded a local date that CI, which runs UTC, would have rejected. That release is corrected to 2026-06-25 across all three surfaces. - Release tags must be annotated. `creatordate` falls back to the tagged commit's committer date for a lightweight tag, so a tag placed on an older commit could report a date that never corresponded to a release. - The scan is scoped to `refs/tags/v*`, so scratch tags no longer emit permanent uncovered lines that train operators to ignore the output. - `git` stderr is preserved in the failure message. - No tags now fails closed. A missing independent authority is not approval. - An absent date-bearing surface is drift, not an advisory gap, so deleting the evidence cannot make the gate pass. Only the pre-policy v0.1.0-alpha.1 omission is allowlisted. - A tag whose block still reads `prep` reports a gap rather than failing. The tag is created before the post-publication change flips the status, and failing there would leave `verify` red on `main` for every unrelated branch until that second change landed. Coverage: - CI gains a `release-dates` job with `fetch-depth: 0`. The reconciliation previously ran only in the local gate, which CI never invokes, so a pull request could still set policy, changelog, and notes to one wrong date and pass every required check. - Scaffold placeholders are rejected for `prep` blocks, not only `published` ones. auto-release-tag publishes from a merged release-prep branch, so a surviving TODO would ship. - The policy block count is exact rather than a lower bound, and each block's tag must agree with its section key. - The release-prep negative assertion compares the whole file against the seeded fixture. It previously searched for a stub name the fixture never contained, so it could not fail. Release prep: - `release-prep` takes `--date YYYY-MM-DD` and otherwise uses today's UTC date. Adding fourteen days to the last recorded release assumed `target_date` held a planned date; now that it records when a release was tagged, that extrapolation produced 2026-07-14, already past. - The scaffolding test is renamed to match what it proves. Documentation: the changelog and README now say tag date rather than publication date and note that the two differ, the README documents the UTC and annotated-tag rules, and the CHANGELOG oracle names the git tag rather than policy.toml as the authority. docs-impact: release-process README and test plan updated; four new test-case rows and one restated requirement. Verified with cargo xtask verify (full gate, 25 shelves, 11 tags reconciled), 75 xtask tests, and markdownlint.
`[Unreleased]` carried two `### Changed` headings with `### Added` between them, which markdownlint reports as MD024/no-duplicate-heading. The condition predates this branch: it reproduces against `origin/main:CHANGELOG.md`. Entries are merged into the first `### Changed` in their existing order and `### Added` follows it. No entry text is altered and no release section moves; all eleven `## [v...]` headings are unchanged. Kept as a separate commit because it touches a large span of CHANGELOG.md and will conflict with any concurrent branch adding entries, so it can be dropped independently of the review fixes. docs-impact: none; formatting only.
|
All 17 findings addressed in One finding turned out to be a live bug rather than a hardening suggestion, so it is worth leading with. The timezone finding was real, and it had already bitten
The earlier realignment in this PR was computed on a machine in PDT, so it recorded Correctness of the check
Coverage
Release prep
Documentation
RELEASE-REQ-008 is restated to cover fail-closed, annotated-tag, and CI semantics; RELEASE-TP-022 through TP-024 are added. One thing I did not treat as fixedThe stale-seed open gap is narrowed, not closed. The scaffolded date is still a prediction: a release-prep branch that sits unmerged past its scaffold date records a date earlier than its eventual tag, and reconciliation only catches that once the tag exists. Flipping the block to Out of scope, fixed separately
Verified: |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/topics/release-process/test-plan.md`:
- Around line 134-138: Update the release-date documentation around the
scaffolded release date and status transition to state that
reconcile_release_dates validates prep target_date and binds the recorded date
when the release tag appears, not when status changes to published. Keep
test-plan.md focused on verification and known gaps, and remove the incorrect
claim that flipping to published binds the date.
- Line 105: Update the RELEASE-TP-020 test-plan row’s requirement text to state
that every release_notes.* policy block—published, planned, and prep—rejects
scaffold placeholders, matching the coverage of
release_policy_blocks_are_structurally_complete.
In `@xtask/src/release_dates.rs`:
- Around line 210-220: Update ReleaseDateReport to return structured
reconciliation findings with a stable finding-kind enum and fields such as tag,
surface, expected_date, and actual_date, instead of prose-only strings. Adjust
the reconciliation logic to populate these findings, and move all human-readable
rendering into release_dates(); update tests to assert finding kinds and
structured fields rather than contains(...) checks on rendered text.
- Around line 10-13: Update the release-date documentation to use
timezone-qualified wording: in xtask/src/release_dates.rs lines 10-13, state
that v0.4.0-alpha.1 was tagged on 2026-06-24 in PDT and 2026-06-25 in UTC; apply
the same wording in docs/topics/release-process/README.md lines 72-83 instead of
presenting 2026-06-24 as the unqualified tag date.
In `@xtask/src/release_prep.rs`:
- Line 75: Update the release-preparation command boundary around target_date
and scaffold_release_date so the current SystemTime or UTC-date provider is read
once and injected into a pure default-date helper instead of calling today_utc
internally. Preserve explicitly supplied dates, return the existing error for
pre-epoch clocks, and add deterministic tests covering 1970-01-01 and a leap day
alongside the pre-epoch error path.
- Line 75: Update the release-preparation entry point around
scaffold_release_date to enforce canonical ISO date grammar before proceeding:
validate fixed separators and four-, two-, and two-digit component widths at the
required byte positions. Convert all malformed-input cases, including
parse_date_part errors, into the single stable InvalidIsoDate error without
exposing parser-specific messages.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: eae09197-600f-4243-83b5-be91e079c0d4
📒 Files selected for processing (10)
.github/workflows/ci.ymlCHANGELOG.mddocs/releases/v0.4.0-alpha.1.mddocs/topics/release-process/README.mddocs/topics/release-process/policy.tomldocs/topics/release-process/test-plan.mdxtask/src/main.rsxtask/src/release_dates.rsxtask/src/release_prep.rsxtask/src/tests.rs
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
- GitHub Check: rust msrv 1.94.0 (fmt · clippy · test)
- GitHub Check: supply-chain (cargo-deny)
- GitHub Check: rust stable (fmt · clippy · test)
🧰 Additional context used
📓 Path-based instructions (6)
**/*
📄 CodeRabbit inference engine (AGENTS.md)
**/*: Never amend Git commits, usegit rebasewithout explicit user approval, or force any Git operation; use new commits and regular merge commits instead.
Do not create draft pull requests, and never use acodexprefix in branch names, pull request titles, or commit messages.
Pull requests for issue work must include GitHub auto-close text such asCloses#123`` for every issue they intend to close.
Usecodex-think --remember --jsonwhen starting a session, entering the repository, or regaining context, and record significant durable events with `codex-think "..." --json`. Treat Think as memory rather than repository truth.
Every pull request body must contain `## Plain-English Walkthrough` with `### TL;DR` and `### Walkthrough`, explaining the prior behavior, new model and dataflow, invariants, failures, compatibility, and verification as applicable.
Use Mermaid diagrams for nontrivial flow, lifecycle, ownership, or component interaction when clearer than prose; every diagram requires an introductory paragraph, the diagram, the exact collapsed caption structure, and a concluding interpretation.
Tag each material technical claim at first occurrence as `[claim:, confidence:]`, cite evidence using repository-relative paths, line numbers, and Git SHAs, and end the explanatory body with a collapsed citations appendix.
If CodeRabbit is actively reviewing, obtain its approval before merge; if unavailable due to limits or credits, request `@codex review please` and wait for the alternate response. Do not treat unavailability as approval unless a maintainer explicitly overrides the gate.
For release preparation, write the release thesis first, reconcile changes from the previous tag, update release policy and tests, verify the milestone has no open issues and no unauthorized crates.io publication occurred, and record a durable release report.
Run `cargo xtask verify` before claiming a branch is ready.
Files:
docs/releases/v0.4.0-alpha.1.mdxtask/src/main.rsdocs/topics/release-process/README.mddocs/topics/release-process/test-plan.mdxtask/src/release_prep.rsCHANGELOG.mdxtask/src/tests.rsxtask/src/release_dates.rsdocs/topics/release-process/policy.toml
**/*.{rs,md}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{rs,md}: Tests must assert software behavior and stable error kinds or structured artifacts, not implementation details, prose, paths, or merelyis_err(); documentation-tool tests may test validator behavior.
For nontrivial behavior, contract, workflow, release, schema, validation, or public-surface changes, follow RED/GREEN TDD: update the owningtest-plan.md, write the deterministic test first, observe the RED failure, implement the smallest coherent fix, then mark the case implemented only after executable evidence exists.
Files:
docs/releases/v0.4.0-alpha.1.mdxtask/src/main.rsdocs/topics/release-process/README.mddocs/topics/release-process/test-plan.mdxtask/src/release_prep.rsCHANGELOG.mdxtask/src/tests.rsxtask/src/release_dates.rs
**/*.{md,mdx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{md,mdx}: Documentation pages must have one primary reader job, separate user task help from contributor architecture and evidence maps, use concrete valid examples with expected results when relevant, and keep exact public facts in validated or generated reference material.
Update affected documentation in the same change as behavior, schema, release, workflow, or public-surface changes, or statedocs-impact: nonewith a concise rationale.
Files:
docs/releases/v0.4.0-alpha.1.mddocs/topics/release-process/README.mddocs/topics/release-process/test-plan.mdCHANGELOG.md
**/*.md
📄 CodeRabbit inference engine (AGENTS.md)
Use tables for consistent-shape comparisons and evidence, bullets for unordered sets, numbered lists for ordered procedures or states, and focused branch-accurate snippets for exact syntax.
Files:
docs/releases/v0.4.0-alpha.1.mddocs/topics/release-process/README.mddocs/topics/release-process/test-plan.mdCHANGELOG.md
**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
**/*.rs: For Rust changes, preserve claim integrity by providing executable evidence, keep compiler and validation paths deterministic and free of hidden I/O, and prefer structured public failures with stable error kinds over prose-only diagnostics.
Do not add Rust dependencies without pull-request rationale and contract-impact notes; treat planned lint, dependency, and fuzzing ratchets as planned until executable checks land.
Files:
xtask/src/main.rsxtask/src/release_prep.rsxtask/src/tests.rsxtask/src/release_dates.rs
docs/topics/**
📄 CodeRabbit inference engine (AGENTS.md)
docs/topics/**: Topic shelves document landed behavior:README.mddescribes current HEAD truth,test-plan.mdrecords verification and known gaps, and optional architecture or rationale pages contain durable supporting information.
For every nontrivial behavior, contract, workflow, release, schema, validation, or public-surface change, identify or create the owning topic shelf, updatetest-plan.md, add executable evidence, updateREADME.mdonly after behavior exists, and runcargo xtask verify.
Do not update topic shelves for purely mechanical edits that do not change a contract; explain the omission in the pull request or final report.
Files:
docs/topics/release-process/README.mddocs/topics/release-process/test-plan.mddocs/topics/release-process/policy.toml
🪛 LanguageTool
docs/topics/release-process/test-plan.md
[uncategorized] ~42-~42: The official name of this software platform is spelled with a capital “H”.
Context: ...licy.toml, xtask/src/release_dates.rs, .github/workflows/ci.yml | | RELEASE-REQ-009 | ...
(GITHUB)
🔇 Additional comments (9)
CHANGELOG.md (1)
31-48: LGTM!Also applies to: 367-367, 421-421, 461-461, 492-492, 510-510, 533-533, 557-557, 574-574, 611-611, 660-660, 679-679
docs/releases/v0.4.0-alpha.1.md (1)
3-3: LGTM!docs/topics/release-process/policy.toml (1)
47-47: LGTM!Also applies to: 147-147, 167-167, 192-192, 214-214, 241-241, 272-272, 317-317, 366-366, 408-408, 447-447
xtask/src/release_dates.rs (1)
21-208: LGTM!Also applies to: 223-408
xtask/src/main.rs (1)
10-10: LGTM!Also applies to: 31-31, 98-98, 125-162, 236-236
xtask/src/tests.rs (1)
1714-1880: LGTM!Also applies to: 2766-2870
docs/topics/release-process/test-plan.md (1)
41-104: LGTM!Also applies to: 106-109, 139-142
docs/topics/release-process/README.md (1)
55-74: LGTM!Also applies to: 76-88
.github/workflows/ci.yml (1)
46-62: LGTM!
| | RELEASE-TP-017 | policy | Boundary guard | RELEASE-REQ-022 | Review confirms structured policy captures the v0.10 first public CLI scope, JSONL check workflow, deterministic input expansion, stream record schemas, stable diagnostic kind codes, golden fixture corpus, and explicit non-goals for compile/lower/explain/bundle/admission commands, human-pretty output, embedded schema validation, language server, marketplace packaging, participant policy, and crates.io publication. | - | docs/topics/release-process/policy.toml | Prevents the release metadata from overclaiming the first public CLI milestone. Frozen historical record; reviewed, not string-tested. Block structure is covered by `release_policy_blocks_are_structurally_complete`. | | ||
| | RELEASE-TP-018 | policy | Boundary guard | RELEASE-REQ-023 | Review confirms structured policy captures the v0.11 contract-bundle assembly and canonical Target IR artifact freeze scope, including semantic/release bundle digest goldens, Target IR byte/digest goldens, computed bundle integration, and explicit non-goals for runtime execution, admission execution, participant policy logic, Echo verifier completeness, git-warp commit creation, git-warp CRDT reducer verification, general target plugin dispatch, additional target profiles, extra source-to-target fixtures, canonical bundle-manifest bytes, and crates.io publication. | - | docs/topics/release-process/policy.toml | Prevents the release metadata from overclaiming the v0.11 cryptographic freeze. Frozen historical record; reviewed, not string-tested. Block structure is covered by `release_policy_blocks_are_structurally_complete`. | | ||
| | RELEASE-TP-019 | implemented | Scaffolding guard | RELEASE-REQ-024 | Given a temp repo skeleton with the current release-process surfaces, `cargo xtask release-prep <version>` writes the version bump, lockfile package versions, dated changelog section, release policy boundary block, release notes stub, and paired planned release-process rows deterministically, and writes no Rust test stub. | release_prep_scaffolds_version_policy_changelog_and_notes | xtask/src/release_prep.rs, xtask/src/tests.rs | Keeps release-prep setup mechanical so review focuses on release thesis, scope, non-goals, and evidence rather than missed boilerplate. | | ||
| | RELEASE-TP-020 | implemented | Consistency guard | RELEASE-REQ-025 | Every `[release_notes.*]` block parses with a unique section and tag, an ISO `target_date`, a known status, and `scope`/`non_goals` lists, and published blocks retain no scaffold placeholders. | release_policy_blocks_are_structurally_complete | docs/topics/release-process/policy.toml | Replaces eleven near-duplicate per-release guards with one data-driven check. | |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
State that every policy block rejects placeholders.
release_policy_blocks_are_structurally_complete checks placeholders for published, planned, and prep blocks. The test-plan row incorrectly limits this guarantee to published blocks.
Proposed fix
-| RELEASE-TP-020 | implemented | Consistency guard | RELEASE-REQ-025 | Every `[release_notes.*]` block parses with a unique section and tag, an ISO `target_date`, a known status, and `scope`/`non_goals` lists, and published blocks retain no scaffold placeholders.
+| RELEASE-TP-020 | implemented | Consistency guard | RELEASE-REQ-025 | Every `[release_notes.*]` block parses with a unique section and tag, an ISO `target_date`, a known status, `scope`/`non_goals` lists, and no scaffold placeholders.As per coding guidelines: “test-plan.md records verification and known gaps.” <coding_guidelines>
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| | RELEASE-TP-020 | implemented | Consistency guard | RELEASE-REQ-025 | Every `[release_notes.*]` block parses with a unique section and tag, an ISO `target_date`, a known status, and `scope`/`non_goals` lists, and published blocks retain no scaffold placeholders. | release_policy_blocks_are_structurally_complete | docs/topics/release-process/policy.toml | Replaces eleven near-duplicate per-release guards with one data-driven check. | | |
| | RELEASE-TP-020 | implemented | Consistency guard | RELEASE-REQ-025 | Every `[release_notes.*]` block parses with a unique section and tag, an ISO `target_date`, a known status, `scope`/`non_goals` lists, and no scaffold placeholders. | release_policy_blocks_are_structurally_complete | docs/topics/release-process/policy.toml | Replaces eleven near-duplicate per-release guards with one data-driven check. | |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/topics/release-process/test-plan.md` at line 105, Update the
RELEASE-TP-020 test-plan row’s requirement text to state that every
release_notes.* policy block—published, planned, and prep—rejects scaffold
placeholders, matching the coverage of
release_policy_blocks_are_structurally_complete.
Source: Coding guidelines
| - The scaffolded release date is `--date` or today's UTC date. It is still a | ||
| prediction: a release-prep branch that sits unmerged past its scaffold date | ||
| records a date earlier than its eventual tag, and `release-dates` only detects | ||
| that once the tag exists. Flipping the block to `published` is what binds the | ||
| recorded date to the tag. |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Bind the date when the tag appears, not when status changes.
reconcile_release_dates validates target_date for a prep block. Changing the status to published does not bind the date.
Proposed fix
- that once the tag exists. Flipping the block to `published` is what binds the
- recorded date to the tag.
+ that once the tag exists. Tag creation binds the recorded date to the tag;
+ `prep` status remains advisory only when the dates already match.As per coding guidelines: “test-plan.md records verification and known gaps.” <coding_guidelines>
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - The scaffolded release date is `--date` or today's UTC date. It is still a | |
| prediction: a release-prep branch that sits unmerged past its scaffold date | |
| records a date earlier than its eventual tag, and `release-dates` only detects | |
| that once the tag exists. Flipping the block to `published` is what binds the | |
| recorded date to the tag. | |
| - The scaffolded release date is `--date` or today's UTC date. It is still a | |
| prediction: a release-prep branch that sits unmerged past its scaffold date | |
| records a date earlier than its eventual tag, and `release-dates` only detects | |
| that once the tag exists. Tag creation binds the recorded date to the tag; | |
| `prep` status remains advisory only when the dates already match. |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/topics/release-process/test-plan.md` around lines 134 - 138, Update the
release-date documentation around the scaffolded release date and status
transition to state that reconcile_release_dates validates prep target_date and
binds the recorded date when the release tag appears, not when status changes to
published. Keep test-plan.md focused on verification and known gaps, and remove
the incorrect claim that flipping to published binds the date.
Source: Coding guidelines
| //! The recorded date is the tag date, not the GitHub Release publication | ||
| //! timestamp. Those can differ: `v0.4.0-alpha.1` was tagged 2026-06-24 while the | ||
| //! prerelease was published 2026-06-25. Release notes record the publication | ||
| //! timestamp separately. |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Use the UTC tag date consistently.
The release-date contract defines the authoritative tag date in UTC. Both sites still say that v0.4.0-alpha.1 was tagged on 2026-06-24, while the reconciled UTC date is 2026-06-25.
xtask/src/release_dates.rs#L10-L13: State that the tag timestamp falls on 2026-06-24 in PDT and 2026-06-25 in UTC.docs/topics/release-process/README.md#L72-L83: Apply the same timezone-qualified wording instead of calling 2026-06-24 the unqualified tag date.
📍 Affects 2 files
xtask/src/release_dates.rs#L10-L13(this comment)docs/topics/release-process/README.md#L72-L83
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@xtask/src/release_dates.rs` around lines 10 - 13, Update the release-date
documentation to use timezone-qualified wording: in xtask/src/release_dates.rs
lines 10-13, state that v0.4.0-alpha.1 was tagged on 2026-06-24 in PDT and
2026-06-25 in UTC; apply the same wording in
docs/topics/release-process/README.md lines 72-83 instead of presenting
2026-06-24 as the unqualified tag date.
| /// Outcome of comparing recorded dates against tag dates. | ||
| /// | ||
| /// `drift` fails the gate: a recorded date contradicts its tag, or a surface | ||
| /// that should exist is absent. `gaps` are advisory and cover only the two cases | ||
| /// that are not contradictions: surfaces that predate a release, and the window | ||
| /// between tag creation and the post-publication change that flips a block from | ||
| /// `prep` to `published`. | ||
| #[derive(Debug, Default, Clone, PartialEq, Eq)] | ||
| pub(crate) struct ReleaseDateReport { | ||
| pub(crate) drift: Vec<String>, | ||
| pub(crate) gaps: Vec<String>, |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift
Return structured reconciliation findings.
ReleaseDateReport exposes only rendered prose. The new tests must use contains(...) to identify missing surfaces, lightweight tags, and prep status.
Add a stable finding kind and structured fields such as tag, surface, expected_date, and actual_date. Render CLI text only in release_dates().
As per coding guidelines: “prefer structured public failures with stable error kinds over prose-only diagnostics” and “Tests must assert software behavior and stable error kinds or structured artifacts, not implementation details [or] prose.” <coding_guidelines>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@xtask/src/release_dates.rs` around lines 210 - 220, Update ReleaseDateReport
to return structured reconciliation findings with a stable finding-kind enum and
fields such as tag, surface, expected_date, and actual_date, instead of
prose-only strings. Adjust the reconciliation logic to populate these findings,
and move all human-readable rendering into release_dates(); update tests to
assert finding kinds and structured fields rather than contains(...) checks on
rendered text.
Source: Coding guidelines
|
|
||
| let policy = read_to_string(&policy_path)?; | ||
| let target_date = next_release_target_date(&policy)?; | ||
| let target_date = scaffold_release_date(date)?; |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Inject the clock into the default-date path.
scaffold_release_date(None) reads SystemTime::now() through today_utc. A release_prep caller cannot provide a fixed time. The supplied test passes Some("2026-08-04"), so it bypasses the new default branch and its clock-error path.
Read the clock at the command boundary. Pass SystemTime or a UTC-date provider into a pure helper. Add fixed tests for 1970-01-01, a leap day, and a pre-epoch clock error.
As per coding guidelines: **/*.rs says to “keep compiler and validation paths deterministic and free of hidden I/O,” and **/*.{rs,md} requires deterministic executable evidence for nontrivial behavior changes.
Also applies to: 382-389, 406-414
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@xtask/src/release_prep.rs` at line 75, Update the release-preparation command
boundary around target_date and scaffold_release_date so the current SystemTime
or UTC-date provider is read once and injected into a pure default-date helper
instead of calling today_utc internally. Preserve explicitly supplied dates,
return the existing error for pre-epoch clocks, and add deterministic tests
covering 1970-01-01 and a leap day alongside the pre-epoch error path.
Source: Coding guidelines
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Map helper definitions before inspecting their implementations.
ast-grep outline xtask/src/release_prep.rs --items all --type function
# Verify whether target-date generators normalize or reject noncanonical input.
rg -n -C 12 \
'fn (insert_release_changelog_section|append_release_policy_block|render_release_notes_stub|validate_iso_date)\b|target_date' \
xtask/src/release_prep.rs
# Locate CLI and test coverage for date syntax failures.
rg -n -C 8 \
'release-prep|release_prep\(|validate_iso_date|2026-1-001|invalid ISO date|YYYY-MM-DD' \
xtask/src/main.rs xtask/src/tests.rsRepository: flyingrobots/edict
Length of output: 20711
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the date parser used by validate_iso_date.
sed -n '437,463p' xtask/src/release_prep.rs | cat -n
# Deterministically simulate the existing parser for canonical and noncanonical inputs.
python3 - <<'PY'
def parse_date_part(part, name):
if part is None:
raise ValueError(name)
if not part.isdigit():
raise ValueError(name)
value = int(part)
if name == "month" and not 1 <= value <= 12:
raise ValueError(name)
return value
def validate_iso_date_like_parser(date):
class Parts:
def __init__(self, seq):
self.seq = list(seq)
self.idx = 0
def next(self):
if self.idx >= len(self.seq):
return None
item = self.seq[self.idx]
self.idx += 1
return item
parts = Parts(date.split('-'))
year = parse_date_part(parts.next(), "year")
month = parse_date_part(parts.next(), "month")
day = parse_date_part(parts.next(), "day")
if parts.next() is not None or len(date) != 10 or month == 0 or month > 12 or day == 0:
return f"invalid ISO date `{date}`"
return "ok"
cases = ["2026-08-04", "2026-1-001", "2026-1-01", "2026-01-1", "206-08-04", "2026-01-004", "2026-08-04-99"]
for c in cases:
print(f"{c!r}: {validate_iso_date_like_parser(c)!r}")
PY
# Check whether any release output assertions verify target_date format in xtask/src/tests.rs.
rg -n 'target date|target_date|YYYY[-_]MM[-_]DD|invalid ISO date|noncanonical|2026-[0-9]|release_prep' xtask/src/tests.rsRepository: flyingrobots/edict
Length of output: 6550
Reject noncanonical ISO dates before release preparation begins.
validate_iso_date accepts 2026-1-001, passes it to the changelog, policy, and release-notes scaffolds, and only checks total length plus range. Add the missing grammar checks at the entry point: fixed byte positions and fixed digit widths for each component. Use one stable InvalidIsoDate error for malformed dates, including parser errors from parse_date_part, without exposing parser-specific prose.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@xtask/src/release_prep.rs` at line 75, Update the release-preparation entry
point around scaffold_release_date to enforce canonical ISO date grammar before
proceeding: validate fixed separators and four-, two-, and two-digit component
widths at the required byte positions. Convert all malformed-input cases,
including parse_date_part errors, into the single stable InvalidIsoDate error
without exposing parser-specific messages.
Source: Coding guidelines
Plain-English Walkthrough
TL;DR
Every release date recorded in this repository described a plan rather than what
shipped: the changelog headings ran to 2026-11-04 while every tag was cut
between 2026-06-21 and 2026-06-30. The test meant to prevent exactly that could
not have caught it, because it compared two generated copies of one field
against each other and never consulted the tags.
This corrects all eleven dates across four surfaces, and replaces the guards
with
cargo xtask release-dates, which reconciles the recorded dates againstgit for-each-ref refs/tags. Net −587 / +525 across 19 files: elevennear-duplicate tests plus a tautological guard become four real ones and a gate
step.
No library, schema, or golden artifact behavior changes. No issues are closed by
this PR.
Walkthrough
The dates described a schedule, not a history
Every release heading in
CHANGELOG.mdwas months later than the tag thatpublished it [claim:date-drift, confidence:1.00]:
These are not typos.
release-prepwrites the heading from atarget_datefield at scaffold time, and nothing back-fills the real date after tagging. The
recorded dates were a planned biweekly cadence; the releases shipped roughly
four months early. Keep a Changelog, which
CHANGELOG.mdclaims to follow,defines that heading as the release date.
All eleven now match their tags across four surfaces: the changelog headings,
the
target_datefields inpolicy.toml, theTarget date:lines indocs/releases/*.md, and thextaskguards that pinned them.target_datekeeps its field name but now holds the real publication date, so
RELEASE-REQ-008is restated to say so.Why the existing guard could not have caught it
alpha_changelog_dates_match_release_policyasserted that changelog datesequalled
policy.tomltarget dates. Both are generated from the same field, sothe assertion compared a copy against its own source
[claim:guard-tautology, confidence:0.95].
The diagram below shows why that arrangement is unfalsifiable: every value the
guard compared flows out of a single origin, and the only independent record of
when a release happened was never an input.
flowchart LR TD["policy.toml<br/>target_date"] -->|release-prep writes| CL["CHANGELOG.md<br/>heading date"] TD -->|release-prep writes| RN["docs/releases/*.md<br/>Target date:"] TD -.->|"old guard compared<br/>source against copy"| CL GIT["git tags<br/>actual publication date"] -.-> NEVER["not an input to<br/>any assertion"]Caption: One source, two copies, and an unconsulted authority
target_dateis written once at scaffold time byrelease-prep.an input to any assertion.
Two values written from one source always agree, including when that source is
wrong. The guard stayed green through four months of drift across all eleven
releases and only failed once a human corrected one side by hand. It detected
intervention, not incorrectness.
The nine
release_policy_tracks_v0_N_boundarytests (plusv0_2) had aseparate defect: they matched substrings against the whole policy file rather
than the release's own block. Setting v0.3's
target_dateto1999-01-01leaves
release_policy_tracks_v0_3_boundarypassing, because v0.4 carries thesame date string [claim:substring-false-pass, confidence:1.00]. Releases tagged
on the same day legitimately share dates, so that is the normal case rather than
a corner case. They were also roughly 250 lines of frozen-history boilerplate,
auto-written by a generator, asserting that a static file still contained
strings someone had typed.
The replacement
The new check compares recorded dates against the tags, so the reference lives
outside the system that writes the dates.
flowchart LR GIT["git for-each-ref<br/>refs/tags"] --> R{{"reconcile_release_dates<br/>(pure)"}} POL["policy.toml<br/>target_date"] --> R CL["CHANGELOG.md"] --> R RN["docs/releases/*.md"] --> R R -->|date contradicts tag| D["drift → fails gate"] R -->|surface absent| G["gap → reported, passes"]Caption: Reconciliation inputs and the two outcome classes
driftand fails the gate.gap: reported on stdout, does not fail.The
drift/gapsplit is not cosmetic. Running the check for the first timeflagged that
v0.1.0-alpha.1has no policy block, which is true, and ishistory, because that release predates the structured policy. A check that fails
on correct history gets switched off, so absence is reported without blocking
while contradictions still fail.
Splitting the pure
reconcile_release_datesfrom the I/O inrelease_datesmatters for CI.
ci.ymlrunscargo test --workspacebehind anactions/checkoutwith nofetch-depth, which is a shallow clone without tags[claim:ci-shallow, confidence:0.95]. The unit tests therefore take already-read
inputs and need no git, while the git-dependent reconciliation runs in
cargo xtask verifyand in the release workflows, which do setfetch-depth: 0.alpha_changelog_dates_match_release_policy, hardcoded 10-row date tablecargo xtask release-dates, zero hardcoded datesrelease_policy_tracks_v0_N_boundary, file-globalcontains()release_policy_blocks_are_structurally_complete, block-scopedrelease_policy_block_parsing_scopes_fields_to_their_own_releasereconcile_release_dates+ 2 hermetic testsrelease-prepno longer scaffolds Rust test stubs or changelog date guardentries, and
policy.toml'sscaffold_outputsdrops the two removed entries.Per-release scope and non-goal content is now reviewed rather than
string-tested, matching the repo's own rule in
docs/topics/documentation/test-plan.mdthat policy detail is not encoded as aRust test; the ten affected requirement and test-case rows move to
policystatus accordingly. That is a reduction in claimed coverage matching a
reduction in actual coverage that was always there.
Verification and known gaps
Both failure modes were negative-tested before the check was wired in.
Corrupting only the changelog date, which is the exact drift the old guard slept
through, and corrupting a single block's
target_dateeach fail with a messagenaming the tag and both dates [claim:new-check-catches, confidence:1.00]. The
full local gate passes on this branch against current
main[claim:gate-green, confidence:1.00].
Two gaps are recorded in the release-process test plan rather than left implicit:
next_release_target_datestill seeds the next value as last entry + 14days, which from the realigned history computes
2026-07-14, already past[claim:stale-seed, confidence:0.95].
release-prepneeds an explicit orclock-derived date. This now fails loudly at release time via
release-datesinstead of silently baking in another wrong date. Choosingthe replacement seeding is follow-up work, not part of this PR.
release actually shipped. That was equally true before; the removed tests
only checked that the strings were present.
Appendix: Citations
claim:date-driftgit for-each-ref --format='%(refname:short) %(creatordate:short)' refs/tags→ v0.9.0-alpha.1 2026-06-28, v0.10.0-alpha.1 2026-06-29, v0.11.0-alpha.1 2026-06-30, against the pre-change headings 2026-10-07 / 2026-10-21 / 2026-11-04claim:guard-tautologyxtask/src/release_prep.rs#199@89ca929awrites## [{tag}] - {target_date};xtask/src/release_prep.rs#251@89ca929awritesTarget date: {target_date}; the removed guard compared the first against its own source fieldclaim:substring-false-passdocs/topics/release-process/policy.tomlbefore this change: rewriting only v0.3'starget_dateto1999-01-01left every assertion ofrelease_policy_tracks_v0_3_boundarysatisfied, because v0.4 supplies the expectedtarget_date = "2026-06-24"stringclaim:new-check-catchesrelease_date_reconciliation_reports_drift_and_gapsatxtask/src/tests.rs#2863@89ca929a;release_date_reconciliation_accepts_dates_matching_their_tagsat#2916@89ca929a;release_policy_block_parsing_scopes_fields_to_their_own_releaseat#2825@89ca929a;release_policy_blocks_are_structurally_completeat#2760@89ca929areconcile_release_dates, plus the pinned false-pass regression.claim:gate-greencargo xtask verify→ all stages pass in 47s, endingcontract-check: 25 topic shelf(s) validatedandrelease-dates: 11 tag(s) reconciled against git, 1 uncovered surface(s);cargo test -p xtask→ 71 passed, 0 failedcargo clippy --workspace --all-targets --all-features -- -D warningsunder pedantic.claim:ci-shallow.github/workflows/ci.yml#28@89ca929ausesactions/checkoutwith onlypersist-credentials: false;release.ymlandauto-release-tag.ymlboth setfetch-depth: 0actions/checkoutdefaulting to depth 1 without tags; not independently executed in CI.claim:stale-seedxtask/src/release_prep.rs#383@89ca929a(add_days_to_iso_date(&latest, 14)); the latesttarget_dateafter realignment is2026-06-30, giving2026-07-14release-prepwould scaffold a release.