feat: CHANGELOG enforcement infrastructure#32
Merged
rocketman-code merged 4 commits intomainfrom Apr 15, 2026
Merged
Conversation
Move atomic-rollback from root-crate to crates/atomic-rollback/ workspace member. Root Cargo.toml becomes a workspace manifest with resolver = "3" and one member. No functional change: binary builds to target/release/atomic-rollback as before; atomic-rollback.spec and .copr/Makefile reference paths that are unchanged by the move; CI workflow invokes cargo at workspace root and produces the same artifact. This commit prepares the tree for adding changelog-core and changelog crates in the next commit.
Add changelog-core crate (Fragment + VersionId + Status + Section + generator) and changelog crate (binary + build.rs drift check). Back-fill all v0.1.1-v0.4.0 entries (56 fragments) so the existing CHANGELOG.md is now produced by the generator. CHANGELOG.md changes: - Add `## [Unreleased]` section header (always emitted per design; empty until the next user-facing change ships) - Reorder sections within v0.3.1 and v0.3.2 to KaC canonical order (Added/Changed/Deprecated/Removed/Fixed/Security) - Drop "Initial release." prose under v0.1.1 (no equivalent field in the data model; not load-bearing) - Add trailing newline Architecture: - Source of truth: Fragment enum variants + Status assignments - crates/changelog-core/src/lib.rs holds all changelog content - Adding/transitioning/removing fragments in lib.rs is the only way to change CHANGELOG.md - crates/changelog/build.rs runs at compile time, regenerates the expected CHANGELOG.md from source, panics on mismatch with the committed file - "CHANGELOG.md and source disagree" is now an unrepresentable state for any release build (cargo build fails) Manual workflow when fragments change: cargo run -p changelog > CHANGELOG.md git add CHANGELOG.md crates/changelog-core/src/lib.rs Status::InternalOnly variant exists for changes with no user-perceivable effect (escape hatch for pure private refactors); not emitted to CHANGELOG.md.
PR-time gate enforcing that any modification to crates/atomic-rollback/src/** also modifies crates/changelog-core/src/lib.rs. No commit-subject parsing, no self-reporting, no labels: the gate reads the diff directly and the diff cannot lie about what files changed. By the axiom (invalid states unrepresentable applied to git/PR domain): "PR modifies user-facing source without fragment" is the invalid state. Commit-type parsing allows "lied in subject" as a representable bypass; structural proxy eliminates that bypass entirely. Failure mode coverage: - New feat or fix touching user-facing code: gate fires; fragment required - Pure refactor in user-facing code: gate fires; Status::InternalOnly available as the explicit acknowledgment escape hatch - Doc-only or workflow-only change: gate does not fire; no fragment required (those changes do not modify crates/atomic-rollback/src/**) Error message includes copy-pasteable example for both Unreleased and InternalOnly variants, plus regen instructions and pointer to docs/standards/changelog-fragments.md (added in next commit).
CONTRIBUTING.md is the entry-point doc covering build, test, commits, PRs, and a brief on the changelog-fragments contract. Links out to the standards directory for depth. docs/standards/changelog-fragments.md is the full contract referenced by the pr-fragment workflow's error message. Documents: - when a fragment is required (structural proxy, no self-reporting) - how to add one (variant + match arm + regenerate CHANGELOG.md) - when Status::InternalOnly is appropriate (rare; explicit list of what does and does not qualify) - text conventions (matches existing CHANGELOG style) - variant naming (change-description PascalCase, not PR-prefixed) - how releases consume fragments at bump time The layered structure (CONTRIBUTING entry + docs/standards/ depth) gives the CI error message a stable URL anchor to link to and gives future standards (testing, code style, releases, etc.) a natural home without bloating CONTRIBUTING.md.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Establishes a compile-time-enforced CHANGELOG architecture across four atomic commits:
Design derived from one axiom (make invalid states unrepresentable) applied to each decision. Implementation spec at docs/plans/changelog-enforcement.md (gitignored).
Why
Without this infrastructure, CHANGELOG.md is a hand-written artifact whose correspondence to actual code changes depends on maintainer discipline. Failure modes:
After this PR:
crates/atomic-rollback/src/**modified without Fragment = CI fails on PR (structural gate)cargo buildfails (rustc exhaustive match)cargo buildfails (build.rs drift check)Each failure mode is rustc- or CI-enforced. No trust, no self-reporting.
Architecture
Workspace layout:
changelog-coreis isolated fromatomic-rollback. A bug in changelog code cannot reach the user binary — they share zero compiled code.Statushas three variants:Released { version, section, text }— shipped in a past releaseUnreleased { section, text }— pending the next bumpInternalOnly { description }— acknowledged change with no user-perceivable effect; not emitted to CHANGELOG.mdTest plan
All verified locally and confirmed by CI:
cargo build --releaseclean;cargo test --release23/23 atomic-rollback tests pass; binary reportsatomic-rollback v0.4.0; atomic-rollback.spec / .copr/Makefile / .github/workflows/ci.yml unaffectedcargo build --releaseclean across all 3 crates;cargo test --release23/23 pass;cargo run -p changelog > /tmp/out.md && diff CHANGELOG.md /tmp/out.mdzero outputcargo buildto panic with the expected message--versionand--helpworkKnown follow-up
Issue #31: actionlint reports two pre-existing SC2086 shellcheck info-level warnings in
.github/workflows/ci.yml(unrelated to this PR's scope). Will be addressed in a dedicated small PR after this merges.Post-merge motion (binding; same pattern as PRs #23, #29)
The workflow existing in the repo is advisory until added to required checks. Completion contract:
gh api repos/rocketman-code/atomic-rollback/branches/main/protection --jq '.required_status_checks.contexts'Expect six contexts:
x86_64, aarch64-cross, tests (ubuntu-latest), tests (macos-latest), task-list-completed, fragment-required.The merge is incomplete until the API call runs and verification passes.