ci(release): overlay the release checks the changelog agent runs

The changelog job checks out the rc tag detached, so every file the
agent reads by its working-tree path is the rc tag's copy. Only the
agent instructions were refreshed from the dispatch ref. The
instructions now tell the agent to run the checks in docs/release.md
and hack/validate-changelog.sh on its own output before it exits, and
the rc tag's copies of those predate any check added since, or for an
old enough tag do not exist at all. Refresh both alongside the
instructions.

docs/release.md is under neither sparse-checkout path and reaches the
tooling checkout only because cone mode also carries the files of
every parent directory of docs/agents. The contract test pins both
copies and that dependency, so a switch to non-cone patterns fails
here rather than as a hard failure of the refresh step at promote
time. The AI step keeps its read-only token and its tool flags.

Assisted-by: LLM
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
This commit is contained in:
Myasnikov Daniil committed 2026-10-16 08:18:34 +00:00
1 parent 6395ca1759
commit e2ca1b7bc5
2 files changed
+41 -3

No files matched your search

+9 -1
View File
@@ -163,10 +163,18 @@ jobs:
# detached rc-tag checkout is exactly that, so a COMPLIANT agent would
# silently decline to generate on every promotion of an existing rc.
# Overlay the current instructions so the agent reads today's contract.
# changelog.md sends the agent to the checks in docs/release.md and to
# hack/validate-changelog.sh, so those are overlaid too: the rc tag's
# copies predate any check added since. docs/release.md reaches
# .release-tooling only because the sparse checkout above is in cone
# mode (the action's default), which carries the files of every parent
# directory of docs/agents.
- name: Refresh agent instructions
run: |
cp .release-tooling/docs/agents/changelog.md docs/agents/changelog.md
echo "Refreshed docs/agents/changelog.md from ${GITHUB_REF_NAME}."
cp .release-tooling/docs/release.md docs/release.md
cp .release-tooling/hack/validate-changelog.sh hack/validate-changelog.sh
echo "Refreshed docs/agents/changelog.md, docs/release.md and hack/validate-changelog.sh from ${GITHUB_REF_NAME}."
# Skip-priority + validate-then-fall-through: use a pre-existing changelog
# only if it is USABLE, so an invalid higher-priority source can never
+32 -2
View File
@@ -36,8 +36,9 @@
# 9-11. The validator accepts every changelog of the current era (both header
# conventions, including a 19-line patch release) and rejects unusable output.
# 12. The workflow calls the validator script rather than reimplementing it.
# 13. The agent instructions and the validator are overlaid from the dispatch
# ref, because the rc tag's copies predate this workflow.
# 13. The agent instructions, the release checks they send the agent to and the
# validator are overlaid from the dispatch ref, because the rc tag's copies
# predate this workflow.
# 14. open-pr survives an artifact transfer failure and keys off the file on disk.
# 15. A pre-existing changelog is validated, not merely non-empty.
# 16. promote-rc parses the rc tag ONCE in a cheap `parse` job that rejects
@@ -481,6 +482,35 @@ code_lines() { grep -vE '^[[:space:]]*#'; }
exit 1
}
# changelog.md sends the agent to run the docs/release.md checks and the
# validator on its own output, by their working-tree paths. Without these two
# copies it runs the rc tag's versions, which predate every check added since.
for f in docs/release.md hack/validate-changelog.sh; do
printf '%s\n' "$overlay" | code_lines | grep -qF "cp .release-tooling/$f $f" || {
echo "The refresh step does not overlay $f from the tooling checkout, so the" >&2
echo "agent following docs/agents/changelog.md section 9 runs the rc tag's copy." >&2
exit 1
}
done
# docs/release.md sits under neither sparse-checkout path. It reaches
# .release-tooling only because cone mode also checks out the files of every
# parent directory of a listed one; without it the copy above fails the job.
tooling="$(printf '%s\n' "$block" | awk '
/^ - name: Checkout release tooling from the dispatch ref$/ { inside = 1; next }
/^ - name: / { inside = 0 }
inside' | code_lines)"
printf '%s\n' "$tooling" | grep -qE '^[[:space:]]+docs/agents$' || {
echo "The tooling checkout no longer lists docs/agents, which is what carries" >&2
echo "docs/release.md into .release-tooling." >&2
exit 1
}
if printf '%s\n' "$tooling" | grep -qE 'sparse-checkout-cone-mode:[[:space:]]*false'; then
echo "The tooling checkout turned cone mode off, which drops docs/release.md" >&2
echo "from .release-tooling and fails the refresh step's copy." >&2
exit 1
fi
# Every job that runs release tooling against an older checkout must source it
# from the dispatch ref. A script fetched by a script that is itself missing
# solves nothing, so this is done by a second actions/checkout, not by shell.