Drift bots write release notes, not bot narration - #1409
Conversation
The CHANGELOG bullets from auto-draft-drift-fix.mjs and label-sync.mjs said which workflow drafted them, what would auto-handle a re-capture and that the PR auto-merged. They also carried em dashes. The gating review blocks release text like that on this repo (dario#1405, rule reads-as-generated), so every drift PR would stop there. Each bullet now says what changed for a user: the supported range moved, or the template labels follow a new Claude Code version with the request shape unchanged.
Live test: ✅ PASSHead Stages |
The gating review asked for the drift bullets without the drafting and merging narration or em dashes. The wording matches what #1409 teaches the drift bots to write.
The third drift bot, rebake-release-prep, still wrote the old narration: an em dash, the watcher's workflow name and "wire-fingerprint drift". Its bullet now says what the other two bots' bullets say: what a user gets. test/resolve-release-conflicts.mjs uses the old string only as fixture content in a CHANGELOG it rewrites.
Verification at f5fe6f4Verified against base Boundaries rebuilt from the diff
Test evidenceHead Base The 6 failing checks are the discriminators, two per script, so a regression in any one script fails on its own. The 9 checks that pass on base ( Also run at headCI at Not run: nothing in the change needs the built Rules: test-comment-density-matches-neighbours=covered(test/label-sync.mjs and test/auto-draft-drift-fix.mjs at base carry file-level and helper comments; the new file's comments match that density and narrate no patch) | no-control-cases-in-the-suite=covered(the 9 base-passing checks are per-block prerequisites, no standalone control test; see the table above) | ledger-row-needs-its-fixture=covered(the body's "no script, test or workflow parses these bullets" row was re-measured by grep, and each producer got its own fixture block) | run-every-ci-step-not-just-the-red-one=covered(preflight, check-changelog and the touched test files run locally; CI matrix green at 825276c) | mutate-the-rejected-alternatives=unreachable(the body rejects no alternative implementation; the change is three string literals) | side-effect-change-needs-its-test=unreachable(no computed value changes source, only literal text) | moved-transform-test-enters-above=unreachable(nothing moved between layers; the test enters at the spawned script, the same layer the workflows use) |
sprayberry-redline
left a comment
There was a problem hiding this comment.
Automated review from the Sprayberry Labs fleet code reviewer.
Reviewed by the GPT gating lane (gating review).
Request changes: the newly added public comments narrate this patch and its review history rather than documenting code behavior. rule:reads-as-generated
Blocking — generated-writing tell (scripts/auto-draft-drift-fix.mjs:209-210)
// Release-note voice: what changed for a user, no bot narration (the gating review
// blocks generated-sounding release text on this public repo; dario#1405).
This comment describes the rationale for this particular patch and names the prior review instead of explaining behavior needed to maintain the script. The same added comment appears in scripts/label-sync.mjs:77-78. This is public generated-style patch narration, which first-party strict mode blocks.
const driftBullet =
Blocking — generated-writing tell (test/drift-release-notes.mjs:1-4)
// The CHANGELOG bullets the drift bots write are the release notes users read
// on the GitHub release (extract-release-notes.mjs lifts the section verbatim).
// The gating review on dario#1405 blocked bullets that narrated the machinery
// instead of the change, so each bot's release-prep script is run end to end
The new test opens with a narration of the review and patch history. It is not test-local documentation and makes the test read as generated rather than following a concise test-file idiom.
import { spawnSync } from 'node:child_process';
The end-to-end test itself is useful: it executes all three release-note producers and checks their resulting bullets, and the current CI checks are green.
I reviewed the four changed files and the current verification comment and CI results; I did not run the local test suite.
sprayberry-secondread
left a comment
There was a problem hiding this comment.
Automated review from the Sprayberry Labs fleet code reviewer.
Reviewed by the Claude second-opinion lane (independent second read; the gating review is posted separately).
Verdict: no blocking issues at f5fe6f4. All three bullets are rewritten as described, nothing in the repo parses the old strings, and the new test fails on base for every script.
What I checked
The PR body against the diff
scripts/auto-draft-drift-fix.mjs:212-213,scripts/label-sync.mjs:79-83andscripts/rebake-release-prep.mjs:46-48produce exactly the three bullets the body quotes. I read them back from a real run ofnode test/drift-release-notes.mjsat head.- "No script, test or workflow parses these bullets": I grepped
scripts/ src/ test/ .github/forCC drift patch,Template label refresh,Template rebakeandAuto-drafted by. The only hits are fixture text intest/resolve-release-conflicts.mjs, a fixture intest/auto-draft-drift-fix.mjs:207, and the PR-body header atscripts/auto-draft-drift-fix.mjs:248. The body says PR bodies are out of scope, and that header is in the PR body.appendUnreleasedinserts the bullet verbatim and does not care what it says. - Are the new bullets true? The label-sync bullet says "A live capture ... matched the bundled template". That only runs on the
exit_code == '3'step ofcc-drift-template-watch.yml, which is the path where the shape matched. The rebake bullet says the capture "no longer matched", which is the exit-2 path. In the auto-draft bullet,${ccVersion}and${after}are the same value (patchMaxTestedreturnsafter: newVersion=ccVersion), so the header and the body of the bullet cannot name different versions.
Boundaries
- Only the string templates changed. No predicates, guards or index expressions changed.
targetandccVersionare already checked for empty or missing before these lines run (label-sync.mjs:44,auto-draft-drift-fix.mjs:84), so an empty version cannot reach the new text. rebake-release-prep.mjsnow uses'\''inside a single-quoted literal. It renders asClaude Code's, which I confirmed in the test output.
The new test (test/drift-release-notes.mjs), per variant
- I copied the head test next to the base (
master) versions of the three scripts and ran it: 9 pass, 6 fail.no em dashandno workflow or merge narrationfail on base for all three variants (auto-draft, label-sync, rebake), so each rewrite is pinned by a check that can fail. - At head: 15 pass, 0 fail.
test/all.test.mjsfinds everytest/*.mjsthroughreaddirSync, so CI runs the file without any wiring. The CItestjob is green.
Non-blocking notes
-
Low: the rebake variant's
names the versions involvedcheck is always true.test/drift-release-notes.mjs:61['rebake-release-prep.mjs', [], []],
and
:66check('names the versions involved', expectVersions.every((v) => bullet.includes(v)), bullet);
[].every(...)istrue, so for this variant the check passes whatever the bullet says. It doesn't hide a gap: the rebake bullet names no version before or after the change, and the two checks that pin the rewrite fail on base. But it prints anOKline that asserts nothing. Suggested fix:if (expectVersions.length) check('names the versions involved', expectVersions.every((v) => bullet.includes(v)), bullet);
-
Low: the new source comments narrate why the patch was written.
scripts/auto-draft-drift-fix.mjs:209-210andscripts/label-sync.mjs:77-78:// Release-note voice: what changed for a user, no bot narration (the gating review // blocks generated-sounding release text on this public repo; dario#1405).
The test header at
test/drift-release-notes.mjs:3-4says the same thing. The first half states a durable rule and is useful. The second half cites the review and PR that prompted the change, which is history that belongs in the commit message. This PR is meant to remove that kind of narration from what the bots write, so the comments could lose it too. Suggested fix:// Release-note voice: what changed for a user, not how the bot produced it.
What's good
Each script is run end to end against a staged repo, and the test reads the bullet back from the CHANGELOG under the promoted version. That tests what users will actually see, instead of comparing string literals. The rewritten bullets are also more accurate than the old ones. For example, "Auto-merged" in the label-sync bullet was only true when auto-merge succeeded.
SECOND READ: READY
Verification at 68e0858Base What this head answersRedline's CHANGES_REQUESTED at f5fe6f4 named two added comments citing the gating review and dario#1405. Grepping every added line in Boundaries rebuilt from the diff
What I ran
Head (68e0858): Base (2ea19ea): Per-check arms. The 10 checks that pass on base are per-block prerequisites (the script exits 0, files a bullet, names its versions) that make the failing checks meaningful; they are not standalone controls and none is named as one. The Mutants of the production files (each restored,
|
| Mutant | Result at 9a8d7eb | Result at 68e0858 |
|---|---|---|
A: auto-draft writes `${before}` → `${after}` instead of moves from ... to ... |
14 pass, 0 fail, killed by nothing | 16 pass, 1 fail: no arrow between versions |
B: label-sync appends Auto-merged. |
13/1: no workflow or merge narration :: Auto-merged |
same |
C: rebake headline request shape** — a live capture |
13/1: no em dash |
same |
Mutant A is why 68e0858 exists: the arrow was in the old auto-draft bullet, it is the second generated tell alongside the em dash, and nothing on the branch pinned its removal.
Artefact check
/agent-output/fp/dario/vfy1409-probe-notes.mjs stages the same repo, runs each script, then runs scripts/extract-release-notes.mjs 6.11.4 --file <staged CHANGELOG> (the thing cc-drift-auto-release.yml uses for the GitHub release body). Head release bodies are exactly the three bullets in the PR description; base release bodies are the three old bullets with em dashes, the arrow and workflow names.
CI chain at this head
node scripts/preflight.mjs prints preflight: clean.; BASE_SHA=2ea19eac8 node scripts/check-changelog.mjs prints no src/ changes — nothing to record.; node --test --test-name-pattern=drift-release-notes test/all.test.mjs runs the auto-discovered file green (281 ms). At 9a8d7eb the full GitHub matrix was green (test job 107664150926 executed drift-release-notes); the run at 68e0858 was pending when this was posted. node test/auto-draft-drift-fix.mjs 66/0, node test/label-sync.mjs 17/0, node test/resolve-release-conflicts.mjs 52/0.
Not run
The three scripts inside their real workflows (cc-drift-watch.yml, cc-drift-template-watch.yml); they need the self-hosted runner and a live Claude Code capture. The bullet is produced before any of that runs, so the staged repo exercises the same code path.
Rules: comment-cites-its-own-review=covered(tells grep over base..HEAD added lines, 0 hits) | generated-release-note-voice=covered(no em dash, no arrow between versions, no workflow or merge narration) | reads-as-generated=covered(same greps; test header two lines) | no-control-cases-in-the-suite=covered(base arm per check, no check named or shipped as a control) | ledger-row-needs-its-fixture=covered(one staged repo per producer, three producers) | mutate-the-rejected-alternatives=covered(mutants A, B, C above) | run-the-artefact-the-fix-produces=covered(extract-release-notes probe, both arms) | run-every-ci-step-not-just-the-red-one=covered(preflight, check-changelog, all.test.mjs) | moved-transform-test-enters-above=unreachable(no cap or filter moved; strings replaced in place) | idempotence-test-asserts-only-agreement=unreachable(every check asserts the expected text, not agreement)
sprayberry-redline
left a comment
There was a problem hiding this comment.
Automated review from the Sprayberry Labs fleet code reviewer.
Reviewed by the GPT gating lane (gating review).
Verdict: changes requested: the added test-local helper has generated-style JSDoc; remove it to match the repository's test idiom. rule:reads-as-generated
Blocking — generated-writing tell
test/drift-release-notes.mjs:24
/** Stage the files a release-prep script reads, run it, return the bullet it filed under the new version. */
runBot is a test-local helper, so this JSDoc is public generated-style narration rather than necessary API documentation. This repository's first-party generated-writing gate applies to new test comments as well; leaving it makes the newly added test read as generated even though the helper itself is otherwise straightforward.
function runBot(script, args) {
I read the live-head verification evidence and the complete 75-line diff, including the staged end-to-end test. CI is green at this head. The production string changes cover all three appendUnreleased callers, and the test exercises each one; the remaining blocker is confined to the added test documentation.
What's good: the test asserts the user-visible changelog output rather than only inspecting the string literals, and its base/head evidence distinguishes the relevant release-note regressions.
sprayberry-secondread
left a comment
There was a problem hiding this comment.
Automated review from the Sprayberry Labs fleet code reviewer.
Reviewed by the Claude second-opinion lane (independent second read; the gating review is posted separately).
Verdict: No blocking issues at 68e0858. The three bullet strings are the only runtime change, the new test fails on master and passes here, and the PR body's claims match the diff.
What I checked
Boundaries rebuilt from the diff
The diff adds no predicates, guards or index expressions to the scripts. It only swaps three template literals. The interpolated values are the same ones the old strings used, and each comes from an existing guard:
| Row | Input | Fixed code | Pinned by |
|---|---|---|---|
scripts/auto-draft-drift-fix.mjs:210-211, ccVersion/before/after |
missing ccVersion, or maxTested not older |
exits earlier, before the bullet (:84, :110), unchanged |
test/auto-draft-drift-fix.mjs (existing) |
same, normal bump 2.1.280 to 2.1.281 |
bullet names both versions, no em dash, no → |
test/drift-release-notes.mjs "names the versions involved", "no em dash", "no arrow between versions" |
|
scripts/label-sync.mjs:78-81, target |
empty target |
exits earlier (:43-44), unchanged |
existing |
same, target = 2.1.281 |
bullet names target three times |
"names the versions involved" | |
scripts/rebake-release-prep.mjs:46-48 |
no interpolation | constant string | "no em dash", "no workflow or merge narration" |
all three: the bullet lands under the dated promoted heading, not [Unreleased] |
appendUnreleased(..., /^## \[<after>\] - <today>/) unchanged |
"bullet filed under the promoted version" |
Every new assertion checked against a variant where it should fail
- I ran
test/drift-release-notes.mjsagainst master's copies of the three scripts: 10 pass, 7 fail. That covers "no em dash" for all three scripts, "no arrow" for auto-draft, and "no workflow or merge narration" for all three (Auto-drafted,cc-drift-template-watch,cc-drift-template-watch). The fix is what makes these pass. - At head: 17 pass, 0 fail.
- "bullet filed under the promoted version": I changed the rebake script in a temp copy so it never calls
appendUnreleased. That assertion failed (16 pass, 1 fail), so it catches a bullet that goes missing or lands in the wrong place. - "names the versions involved" also passes on master. It guards against a rewrite that drops the versions, not against the voice problem. That is fine as a content check, and the per-variant narration and em dash checks do the fix-specific work.
test/all.test.mjsfinds the new file automatically (it is not inEXCLUDED), and the CItestjob passed at this head.
PR body claims checked against the diff
- "No script, test or workflow parses these bullets": I grepped
scripts/,test/and.github/for the old bullet prefixes. The only hits are fixture strings intest/auto-draft-drift-fix.mjs:207,216andtest/resolve-release-conflicts.mjs, plus the PR-body headingscripts/auto-draft-drift-fix.mjs:246('## Auto-drafted by cc-drift-watch.yml'), which is PR body text, not the CHANGELOG. Confirmed. - "The PR bodies the bots write are unchanged":
buildPrBodyand the workflow commit messages are untouched by the diff. Confirmed. - The new wording matches what each path does: label-sync runs only on exit 3, where
capture-and-bake.mjs:476-477found no shape drift, so "matched the bundled template" is accurate. The rebake bullet runs only after a re-capture, so "no longer matched ... re-captured" is accurate. - I did not check the
11c3725claim about dario#1405, because it is outside this diff.
CI: all checks pass at 68e0858 (auto-merge skipped).
What's good: the test runs the real scripts end to end against a staged repo and reads the CHANGELOG back, so it pins the actual release-note output rather than a copied string.
SECOND READ: READY
Verification at e781b47Done from the maintainer's session. The OSS Breaker is on its last $11 of today's $150 while it verifies #1413, and this head needed only a re-check.
|
sprayberry-secondread
left a comment
There was a problem hiding this comment.
Automated review from the Sprayberry Labs fleet code reviewer.
Reviewed by the Claude second-opinion lane (independent second read; the gating review is posted separately).
Verdict: no blocking issues at e781b47. The three bullet strings are rewritten as described, and the new test fails against master's scripts in every variant.
What I checked
Diff vs body. The diff touches only the three bullet / driftBullet template strings plus one new test. buildPrBody and the workflow PR-body text are untouched, so "The PR bodies the bots write are unchanged" holds. I grepped scripts/, test/, .github/ and src/ for the old headlines (CC drift patch, Template label refresh, Template rebake). The only hits are fixture strings in test/resolve-release-conflicts.mjs:94-167 and test/auto-draft-drift-fix.mjs:207,216 (a promoteUnreleased fixture that isn't named in the body and is also just content). Nothing parses the bullets, so "No script, test or workflow parses these bullets" holds.
Bullet facts vs the code path that writes them.
scripts/auto-draft-drift-fix.mjs:210-211:afterfrompatchMaxTestedisnewVersion === ccVersion, so "Claude Code X is in the supported range" and "moves frombeforetoafter" agree.ccVersionis non-null by the guard at:84.beforeis non-null oncepatchedis truthy, and the script exits earlier otherwise (:141).scripts/label-sync.mjs:78-81runs only on theexit_code == '3'path (cc-drift-template-watch.yml:356,382), which is the "live capture matched, label only" case. The new sentence "A live capture against Claude Code X matched the bundled template" is accurate.targetis non-empty by the guard at:44.scripts/rebake-release-prep.mjs:46-48runs only on theexit_code == '2'path (:171,252), which is the shape-drift case. "A live capture no longer matchedsrc/cc-template-data.json" is accurate.
Boundaries. Each bullet is a template literal with no new predicates. The only inputs are ccVersion/before/after/target, and the existing guards cover their empty or null cases before the bullet is built (above). There is no new reachable row that needs a test.
Can the test fail? I ran test/drift-release-notes.mjs against a copy of scripts/ with the three scripts reverted to origin/master: 10 pass, 7 fail. The failures are em dash ×3, arrow ×1 (auto-draft), and narration ×3 (Auto-drafted, cc-drift-template-watch ×2). At head it is 17/17. Every variant has at least one assertion that fails without the change. test/all.test.mjs picks the file up by directory listing (:55-56), and CI test is green.
Findings
- info,
test/drift-release-notes.mjs:check('no arrow between versions', !bullet.includes('\u2192'), bullet);holds with or without the change for thelabel-sync.mjsandrebake-release-prep.mjsvariants, because master's bullets for those two had no arrow. That's fine: it is a regression guard, and both variants still have discriminating assertions (em dash, narration). I'm naming it only because the gate asks for every per-variant non-discriminating assertion. - info,
test/drift-release-notes.mjs:const root = mkdtempSync(join(tmpdir(), 'dario-drift-notes-'));is never removed, so each run leaves three staged repos intmpdir(). ArmSync(root, { recursive: true, force: true })after reading the CHANGELOG would clean up. This is not blocking.
What's good: the test runs the real scripts end to end against a staged repo instead of copying the strings. That makes it the check that would catch the next bot bullet regressing to narration.
Not done: I didn't run the full suite (CI is the signal, and all checks pass). I didn't run the rebake script against a real master copy; the body reports that run.
SECOND READ: READY
sprayberry-redline
left a comment
There was a problem hiding this comment.
Automated review from the Sprayberry Labs fleet code reviewer.
Reviewed by the GPT gating lane (gating review).
Verdict: changes requested: the new test retains a policy-narrating comment rather than a code-oriented explanation. rule:reads-as-generated
Blocking — generated-writing tell
test/drift-release-notes.mjs:17
// Workflow names, merge mechanics and watcher verbs: none of it is a change a user can observe.
This comment explains why the release-note wording is considered acceptable, rather than what NARRATION matches or how the test uses it. It also restates the policy encoded by the immediately following regular expression. In this public test file, that patch/policy narration reads as generated and violates the repository's generated-writing gate. The NARRATION predicate is still clear without it.
const NARRATION = /\.yml\b|auto-drafted|auto-handled|auto-merged|cc-drift-template-watch|capture-and-bake|sdk-drift|early-warning|detected/i;
I reviewed the live head e781b47bff121183dd614c8b8c72e09963c472b6, the four-file diff, the current-head verification comment, commit messages, and green CI. The end-to-end regression test exercises all three changed release-note producers and its base/head evidence discriminates the rewritten text; no other blocking correctness issue was retained.
Verification at 8bd4f81
The base-versus-head evidence at |
sprayberry-secondread
left a comment
There was a problem hiding this comment.
Automated review from the Sprayberry Labs fleet code reviewer.
Reviewed by the Claude second-opinion lane (independent second read; the gating review is posted separately).
Verdict: No blocking issues at 8bd4f81. The three bullet strings change as the PR body says, the new test fails on the old strings, and nothing parses the old wording.
What I checked
The diff against the body. All three quoted bullets match the diff exactly:
scripts/auto-draft-drift-fix.mjs:210-211:`- **Claude Code ${ccVersion} is in the supported range.** `+`\`SUPPORTED_CC_RANGE.maxTested\` moves from \`${before}\` to \`${after}\`.`. It uses the sameccVersion/before/afterbindings as before (lines 82, 140), so the values going in are the same.scripts/label-sync.mjs:78-81: the new bullet says "A live capture against Claude Code${target}matched the bundled template". I checked that against the caller.cc-drift-template-watch.ymlrunslabel-sync.mjs "$target"only underif: steps.check.outputs.exit_code == '3', andtargetcomes fromlabel-target.txt.capture-and-bake.mjs:476writes that file from the live capture's_version. So the bullet's claim is true on the only path that reaches it.scripts/rebake-release-prep.mjs:46-48: "A live capture no longer matchedsrc/cc-template-data.json". This script is only called on the exit-2 rebake path (workflow line 252), so the claim holds there too.
"No script, test or workflow parses these bullets." I grepped scripts/, test/ and .github/ for the old lead-ins (CC drift patch, Template label refresh, Template rebake, Auto-drafted). Outside the three writers, the only hits are fixture strings in test/resolve-release-conflicts.mjs and test/auto-draft-drift-fix.mjs:207,216, which seed and read back a pre-existing Unreleased entry. check-changelog.mjs and extract-release-notes.mjs key on ## [x.y.z] headings, not bullet text. The claim holds.
Can the new test fail? I ran test/drift-release-notes.mjs at head: 17 pass, 0 fail. Then I ran it against a copy with the three scripts restored from master and kept the new test: 10 pass, 7 fail. Every negative assertion fails on the old strings in each variant where the old string had that tell:
- auto-draft:
no em dash,no arrow between versions,no workflow or merge narration(matchedAuto-drafted) - label-sync:
no em dash,no workflow or merge narration(matchedcc-drift-template-watch) - rebake:
no em dash,no workflow or merge narration(matchedcc-drift-template-watch)
no arrow between versions holds on base for label-sync and rebake, because neither old string had an arrow. That makes it a guard for those two, not a regression pin. It is not vacuous for auto-draft, which is the one bullet that had the arrow. exits 0, bullet filed under the promoted version and names the versions involved are positive properties that hold both before and after. They check that the test harness actually ran the script and found the bullet, so a missing bullet cannot pass the negative checks as an empty string. That is the right shape.
Wiring. test/all.test.mjs picks up every test/*.mjs via readdirSync minus a fixed exclusion list, so npm test / CI runs the new file with no registration step.
Boundaries. The diff adds no predicates, guards, comparisons or index expressions. The only changes are string templates over values the scripts already validated upstream (ccVersion null-checked at line 84, target empty-checked at line 44 of label-sync). No boundary rows to rebuild.
Tells in the changed lines. The added lines have no em dash, arrow, or history narration. The one comment in the new test describes what the test does.
Not checked
- I did not run
test/auto-draft-drift-fix.mjs,test/label-sync.mjsortest/resolve-release-conflicts.mjslocally. When I read the checks, CItestwas still pending at this head.build (18/20/22),actionlintandCodeQLwere green. - The
11c3725rewrite on dario#1405 is outside this diff.
SECOND READ: READY
sprayberry-redline
left a comment
There was a problem hiding this comment.
Automated review from the Sprayberry Labs fleet code reviewer.
Reviewed by the GPT gating lane (gating review).
Verdict: approved — no blocking issues found.
I reviewed live head 8bd4f811a0102b72002f91344ab9d3cc5656c77d, the four-file diff, the current-head verification evidence, commit messages, and current CI. The requested removal is present: the test now defines NARRATION directly, while the end-to-end test continues to exercise all three changed release-note producers. The current verification records 17 passing checks and the prior base/head run still discriminates the changed bullets. All required CI checks are green.
What's good: the production change remains limited to the three user-facing release-note strings, and the regression test reads each generated CHANGELOG bullet back from a staged repository rather than only inspecting string helpers.
Not run locally: the repository suite, per review environment policy; CI is green.
Redline blocked dario#1418 for the em dash in the heading this workflow writes into every label-refresh PR body. Plain punctuation, like #1409 did for the release notes.
The drift bots' CHANGELOG bullets narrated the machinery: which workflow drafted the entry, what would auto-handle a re-capture, and that the PR auto-merged. They also used em dashes. The gating review blocks release text like that on this repo. It requested changes on dario#1405 (rule
reads-as-generated), so every drift PR would stop at the same place.Each of the three bots' bullets now says what changed for a user:
scripts/auto-draft-drift-fix.mjs:- **Claude Code 2.1.281 is in the supported range.** \SUPPORTED_CC_RANGE.maxTested` moves from `2.1.280` to `2.1.281`.`scripts/label-sync.mjs:- **Template labels follow Claude Code 2.1.281.** \_version`, `_supportedMaxTested` and the `user-agent` header now read `2.1.281`. A live capture against Claude Code 2.1.281 matched the bundled template, so the request shape is unchanged and `_captured` keeps the date of the last real capture.`scripts/rebake-release-prep.mjs:- **The bundled template follows Claude Code's current request shape.** A live capture no longer matched \src/cc-template-data.json`, so the template was re-captured from it. Requests that fall back to the bundled template send the new shape.`No script, test or workflow parses these bullets.
test/resolve-release-conflicts.mjsuses the old strings only as fixture content in a CHANGELOG it rewrites. The PR bodies the bots write are unchanged.Tests:
node test/auto-draft-drift-fix.mjspasses 66,node test/label-sync.mjspasses 17, andnode test/resolve-release-conflicts.mjspasses 52.node scripts/rebake-release-prep.mjs, run against a copy of master, writes the new bullet under a dated6.11.3heading; the run was reverted.dario#1405's own CHANGELOG lines were rewritten to the first two strings in
11c3725.