fix: align marker guard with release oracle

This commit is contained in:
Andriujose 2026-08-03 22:56:02 +00:00
parent 7200c8da49
commit d87d76d64b
3 changed files with 58 additions and 18 deletions

View file

@ -3,7 +3,10 @@
# outlived the releases that shipped their machinery. A release candidate must
# therefore reject a marker its assembled changelog makes false, while every
# tree rejects an untraceable marker. Cross-repo citations are traceable but
# are not compared with this repository's changelog.
# are not compared with this repository's changelog; a marker for this repo's
# own issue uses bare #N, never a self-qualified repository citation (#238 D8).
# CHANGELOG.md is the release oracle and immutable shipped prose, so it and the
# fragments that feed it are excluded from the documentation scan (#238 D5).
#
# Usage: marker-check.sh [tree-dir] (default: the repository root)
set -euo pipefail
@ -26,25 +29,36 @@ trap 'rm -f "$marker_records"' EXIT
mapfile -d '' markdown_files < <(git -C "$tree" ls-files -z -- '*.md')
for relative in "${markdown_files[@]}"; do
case "$relative" in
changelog.d/*) continue ;;
CHANGELOG.md|changelog.d/*) continue ;;
esac
if ! awk -v file="$relative" '
{ lines[NR] = $0 }
function without_inline_code(text, before, after) {
while (match(text, /`[^`]*`/)) {
before = substr(text, 1, RSTART - 1)
after = substr(text, RSTART + RLENGTH)
text = before after
}
return text
}
{
lines[NR] = $0
scan_lines[NR] = without_inline_code($0)
}
END {
token = "**unreleased**"
citation_re = "^[[:space:]]*\\((([[:alnum:]_.-]+/)?[[:alnum:]_.-]+)?#[0-9]+\\)"
bad = 0
for (line_no = 1; line_no <= NR; line_no++) {
remaining = lines[line_no]
remaining = scan_lines[line_no]
offset = 0
while ((at = index(remaining, token)) != 0) {
rest = substr(remaining, at + length(token))
candidate = rest
next_line = line_no + 1
while (candidate ~ /^[[:space:]]*$/ && next_line <= NR) {
candidate = candidate " " lines[next_line]
candidate = candidate " " scan_lines[next_line]
next_line++
}
@ -60,7 +74,7 @@ for relative in "${markdown_files[@]}"; do
}
offset += at + length(token) - 1
remaining = substr(lines[line_no], offset + 1)
remaining = substr(scan_lines[line_no], offset + 1)
}
}
exit bad

View file

@ -113,14 +113,14 @@ the machinery at all:
self-hosted runner still wants it: the guard's value is the day
somebody adds one.
This guide documents `main`. New machinery is marked with the lowercase
word `unreleased` in bold, immediately followed by its issue citation
(for example, `(#238)`); whitespace between them may include a line break.
A citation is mandatory, because a marker the guard cannot trace is a
marker it cannot prove false. Cross-repo citations such as `(crew#293)`
satisfy that traceability rule but are not compared with this repository's
release section. The ceremony-only `marker-check.sh` guard enforces both
rules. The release PR that ships the machinery clears, in that same PR,
This guide documents `main`. A marker is the literal token
`**unreleased**` immediately followed by its issue citation (for example,
`(#238)`); whitespace between them may include a line break. A citation is
mandatory, because a marker the guard cannot trace is a marker it cannot
prove false. A marker for this repository's own issue uses bare `#N`.
Cross-repo citations such as `(crew#293)` satisfy the traceability rule but
are not compared with this repository's release section. The ceremony-only
`marker-check.sh` guard enforces these rules. The release PR that ships the machinery clears, in that same PR,
every marker its own assembled section makes false: the section cites its
issues, each marker cites the same issue, and the release PR's diff is the
one place both halves are visible at once (#221). If an action does not exist at the
@ -153,7 +153,7 @@ the machinery at all:
- uses: heavy-duty/ceremony/actions/refs-not-closing@<pinned-tag>
```
`refs-not-closing` is **unreleased** until the first tag carrying #218.
`refs-not-closing` is **unreleased** (#218) until the first tag carrying it.
Adopt this caller with that ordinary pin bump; never point only this file
at a moving or newer ref.
7. **Labels automation** (optional but recommended): the two callers from
@ -592,7 +592,7 @@ mirror), `--check` re-diffs it in CI on every PR, so a hand edit or a stale
pin goes red instead of quietly governing.
`RELEASES.md` joins that mirror with the first tag carrying ceremony#248.
It is **unreleased** until that tag exists: consumers add
It is **unreleased** (#248) until that tag exists: consumers add
`.ceremony/RELEASES.md` only with the ordinary pin bump and re-sync, never by
copying it ahead of their pinned doctrine set.

View file

@ -84,11 +84,37 @@ EOF
check "a cross-repo citation is valid and ignored by release comparison" 0 \
"agree with the tree" run_check cross-repo
fixture self-qualified 0.6.0
printf 'Ceremony work remains **unreleased** (ceremony#248).\n' \
>"$TMP/self-qualified/docs/CONSUMERS.md"
cat >"$TMP/self-qualified/CHANGELOG.md" <<'EOF'
# Changelog
## 0.6.0 — 2026-08-03
- Ceremony work shipped (#248).
EOF
check "a self-qualified citation is ignored; local markers must use bare #N" 0 \
"agree with the tree" run_check self-qualified
fixture exclusions 0.6.0-dev
printf '# Notes\n\n## Unreleased\n' >"$TMP/exclusions/NOTES.md"
cat >"$TMP/exclusions/NOTES.md" <<'EOF'
# Notes
## Unreleased
The marker token is `**unreleased**`.
EOF
printf -- '- A fragment may say **unreleased** without being documentation.\n' \
>"$TMP/exclusions/changelog.d/999.md"
check "headings and changelog fragments do not trip the guard" 0 \
cat >"$TMP/exclusions/CHANGELOG.md" <<'EOF'
# Changelog
## 0.5.0 — 2026-08-03
- Shipped prose may discuss **unreleased** markers without becoming one.
EOF
check "headings, inline mentions, changelog entries, and fragments are excluded" 0 \
"agree with the tree" run_check exclusions
summary