ADR-1198: An unknown changelog.d/ subdirectory fails the run instead of warning¶
- Status: Proposed
- Date: 2026-09-06
- Deciders: Lusoris
- Tags: ci, release, docs, testing
Context¶
ADR-0221 renders CHANGELOG.md's Unreleased block from changelog.d/<section>/*.md, where <section> is a Keep-a-Changelog category. ADR-0892 added a guard for fragments filed under a directory that is not one of those categories, on the grounds that such fragments are skipped by the renderer. The guard prints a WARNING to stderr and returns success.
That is not a guard. It is a comment that happens to execute. changelog.d/docs/ exists on master and holds retrain-runbook-1246.md, the changelog entry for PR #1313's tiny-AI retrain runbook. It has never appeared in CHANGELOG.md and never would have: the renderer skips it, so --check — which compares rendered output against the committed CHANGELOG.md — sees both sides agree the entry does not exist and reports success. The warning went to stderr in a CI step nobody reads when the step is green.
The failure mode is silent loss of release notes, which is precisely what the fragment pattern exists to prevent, and it is invisible to every gate the repository has.
Decision¶
scripts/release/concat-changelog-fragments.sh will treat a fragment under an unknown changelog.d/ subdirectory as an error: it prints the offending directory, lists the files that would be lost, names the valid sections, and returns non-zero. --check and --write both fail, so make lint, the pre-commit hook and the Release Script Contract CI job all stop on it.
changelog.d/docs/retrain-runbook-1246.md moves to changelog.d/added/, which restores the lost entry to the rendered Unreleased block. docs is not added as a section: Keep a Changelog has no such category, and a documentation change that is worth a release note is an Added or Changed entry describing the surface it documents.
Alternatives considered¶
| Option | Pros | Cons | Why not chosen |
|---|---|---|---|
| Fail closed on unknown subdirectories (chosen) | The guard does what its own comment claims; the loss is impossible to miss; costs one directory rename to adopt | A contributor who invents a directory now gets a hard failure instead of a silent skip — which is the point | — |
| Keep the warning, add a separate CI grep for unknown dirs | No change to the renderer | A second mechanism to keep in sync with the first, and the existing one stays misleading | Two half-guards are worse than one real one |
Add docs/ as a valid section | The existing fragment renders unchanged | Keep a Changelog has no docs category, and ADR-0221 renders section headings from that fixed set; adding one means inventing a heading downstream tooling does not expect | Fixes one file, leaves the class of bug open |
Make the renderer render unknown directories into Changed | Nothing is ever lost | Silently reclassifying someone's entry is its own surprise, and the section is a deliberate authoring choice | Guessing on the author's behalf |
| Leave it as a warning | Zero work | It already lost a merged PR's release note for the entire life of that PR | The status quo is the bug |
Consequences¶
- Positive: a misfiled fragment fails at
pre-committime with the path in the message, rather than vanishing. PR #1313's runbook entry is restored toCHANGELOG.md. - Negative: any branch that currently carries a fragment under an unknown directory starts failing until it is moved. A search of
masterfound exactly one, now fixed. - Neutral / follow-ups: two cases in
scripts/release/tests/test-concat-changelog-fragments.shpin the behaviour — that the run fails, and that it names the files that would be lost. The second one caught a real defect in the first draft of this change, where a2>/dev/null >&2redirect order sent the file listing to/dev/null.