Skip to content

ci: fold the four PR-body guards into one pr-body-guards.yml job (2 of 3) - #26570

Merged
os-zhuang merged 1 commit into
mainfrom
claude/project-thread-31axox-2
Oct 27, 2026
Merged

os-zhuang merged 1 commit into
mainfrom
claude/project-thread-31axox-2

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Warren · project thread
Part of #26071
Clause-②: no
Metadata-protocol: none
Ruling-ref: Ruling: batch #397 item 2 · letter A · maintainer 「同意」 — comment 6416984681 on #26071 (director seat, 2026-10-26T13:18Z). This PR is (iii) of letter A, the second of three serial PRs from one card thread: (ii) the four daily patrols → fleet-patrols.yml (#26512, merged 2026-10-27T06:36Z as 3fcafcd), (iii) the four PR-body guards → one workflow (this), then (i) the lint.yml schedule skip. The card closes with the last one. Head branch claude/project-thread-31axox-2 is the card thread's harness push branch with the -2 the seat's claim (comment 6434226733) names for this PR; the harness push rule admits the thread's prefix.

维护者速读

  • 改了什么:四个 PR 正文守卫 workflow(closing-target-claim-guard、duplicate-fix-guard、partof-closing-keyword-guard、single-claim-path-guard)合为一个 .github/workflows/pr-body-guards.yml:一个 job、四个判定 step,每个 step 就是原文件的判定步骤(env:/run:/内联脚本逐字节相同,脚本比对见 Tests),step 名取原 job 的显示名;duplicate-fix 是内联 github-script,放在最前、checkout 之前;merge_group 触发保留,只有 closing-target 那步在队列构建上跑,其余三步只守 pull_request。按名读这些文件的五个门禁改指新文件,其中两个 step 名 pin 改为只读可执行行。
  • 为什么改:裁决 A 的 (iii)。四个文件各自 checkout + setup-node,每个 PR 事件起四个 run;合并后一次 checkout、一个 run,四个判定各自可见(每步 if: !cancelled(),前一步红不挡后面的判定)。
  • 风险与回滚:不改任何判定逻辑,REQUIRED_CONTEXTS 不动(四个守卫本来都不是 required context)。可见的变化三处:① Checks 页四个名字并成一个 PR-body guards (…),各守卫的红落在 step 上;② permissions 取四者并集(全 read),三个脚本步骤的 token 比原来多一两个只读 scope;③ 裁决写的「三个守卫今天带 merge_group」在 main 上实际只有 closing-target 一个带,本 PR 按实情保留这一条腿(见 Acceptance notes)。回滚 = revert 本 PR,四个原文件原样回来。
  • 先看:① pr-body-guards.yml 的 on: 块与四个判定 step 的 name:/if:;② scripts/check-closing-target-claim.mjs 的 CHECK_CONTEXT_NAME 注释与 wiring 自检(改读去注释后的文本);③ scripts/check-closing-keyword-parity.mjs 的 PARSERS 登记行。
  • 两分钟验证:pnpm check:closing-target-claim && pnpm check:partof-closing-keyword && pnpm check:single-claim-paths && node scripts/check-closing-keyword-parity.mjs —— 四条 ✓/OK;PR 上的 PR-body guards (…) check 一个 run、四个判定 step 全绿。
  • 需要你做的:Approve(Tier H,.github/workflows/**);席位随后落地。

What changes

File by file, against the retired workflows.

.github/workflows/pr-body-guards.yml (new) ← closing-target-claim-guard.yml, duplicate-fix-guard.yml, partof-closing-keyword-guard.yml, single-claim-path-guard.yml (deleted)

  • on — pull_request: types: [opened, edited, reopened, synchronize], identical in all four retired files, plus merge_group:, the leg closing-target-claim-guard.yml carried (the other three carried none — see Acceptance notes). No paths: filter, as in all four.
  • permissions — the union of the four retired grants, every scope read: contents: read (closing-target, partof, single-claim), issues: read (closing-target), pull-requests: read (closing-target, duplicate-fix, single-claim). Workflow-level, as each retired file declared its own.
  • concurrency — pr-body-guards-${{ github.event.pull_request.number || github.ref }}, cancel-in-progress: true (closing-target's shape, the one that covers the queue leg; on merge_group github.ref is the queue ref, unique per build, so no other PR's build is cancelled — which is what duplicate-fix's cancel-in-progress: ${{ github.event_name == 'pull_request' }} protected).
  • One job pr-body-guards, display name PR-body guards (claim · duplicate · Part-of · single-writer path), runs-on: ubuntu-latest, timeout-minutes: 10 (the largest of 10 / 5 / 5 / 5; the three measured walls — p99 0.3 / 0.8 / 0.8 min — are carried as comments at their steps).
  • Six steps, in this order:
    1. No other open PR may claim the same issue — the duplicate-fix guard's github-script step, if: ${{ !cancelled() && github.event_name == 'pull_request' }}. FIRST, before any checkout: it needs none ("no checkout for the step that needs none").
    2. Checkout repository and 3. Setup Node.js — if: ${{ !cancelled() }}; taken from partof-closing-keyword-guard.yml (the three script-backed retired files carried these two steps byte-identically; the transport check compares all three).
    3. The card this PR closes must claim this branch — closing-target, if: ${{ !cancelled() }}: runs on BOTH legs, as its file did.
    4. Part-of PR must not also close its card — partof, if: ${{ !cancelled() && github.event_name == 'pull_request' }}.
    5. No other open PR may claim the same single-writer path — single-claim, same if:.
      Each gate step is the retired gate step verbatim except its name: (now the retired JOB's display name, which was its check-run name, so the verdict reads in the step list as it read on the Checks tab) and the inserted if:; env: blocks, run: lines and the inline script: are unchanged. !cancelled() is what keeps four independent verdicts on one run: a step that failed does not hide the three others, as four jobs never did.
  • Comments — each retired file's header is carried whole above its step (headed formerly .github/workflows/FILE), and a new file header explains the one-job shape, the legs, the checkout order, the grants and which self-tests read the file. Seven comment edits beyond transport, each because the sentence described the retired one-file shape: the three "Sibling shape, deliberately copied" sentences (siblings are now steps of this workflow); single-claim's "is not touched by this file" → "this step" and "This workflow adds the second question" → "This step adds … as steps of one job"; in the shared setup-node comment, "The first draft of this file" → "of the Part-of guard's retired file", "the script is dependency-free and imports one sibling module" → "the three scripts are dependency-free and import at most two sibling modules", and "the known-good shape this now matches exactly: … one node scripts/check-*.mjs call" → "matches: … calls". The retired on: / permissions: / concurrency: / job comments are consolidated into the file-level comments, their load-bearing sentences kept (why edited, why synchronize, the multi-PR-group limit of the queue ref, why no paths:, why contents: read).
  • History of the retired files: GitHub keeps a deleted workflow's runs under its name (GET /actions/workflows/FILE.yml answers state: deleted); new history lands under PR-Body Guards.

scripts/check-closing-target-claim.mjs

scripts/check-partof-closing-keyword.mjs

  • WIRING_WORKFLOW → the new file; nothing else. 45 cases.

scripts/check-single-claim-paths.mjs

  • WIRING_WORKFLOW → the new file; the sibling read (siblingPath, "the card-keyed duplicate gate still exists … and still asks its own question") → the same file, comment-stripped for the step-name pin, for the reason above. 93 cases.

scripts/check-closing-keyword-parity.mjs

  • The duplicate-fix-guard registry entry: file → the new file, job → pr-body-guards (the extractor takes the named job's github-script step with a non-empty script:; the job has exactly one); the X2 mutation anchor's file likewise; four header sentences name the step instead of the file. 40 assertions, 5 mutations each driven to red; the sweep finds 6 files carrying the grammar across 21,437 tracked files, all registered. (The sweep reads git ls-files, so the new file must be tracked — it is staged; an untracked copy reads as "registered but no longer matches".)

scripts/pm/dispatch-gates.self-test.mjs

  • The live env-carried specimen (runCli(['--commands', '.github/workflows/partof-closing-keyword-guard.yml'])) → the new file; the fixture's file label and the docblock sentence with it. The derivation on the new file satisfies the five assertions: pnpm check:partof-closing-keyword listed on stdout, node scripts/check-partof-closing-keyword.mjs absent from stdout and named ⊘ NOT MEASURED on stderr, the accounting line present.

Not touched, by the order's surface: REQUIRED_CONTEXTS (ci.yml, lint.yml, governed-surface-guard.yml only, as before); lint.yml (its step ids single-claim-path-guard-self-test / closing-target-claim-guard-self-test are step ids that run the scripts' self-tests, not file names, and lint.yml is PR 3's surface); scripts/pm/check-expected-skips.mjs (its MEASURED_* arrays record historical check-run names on dated run ids — fixtures, not the roster; the roster has no guard row); docs/** and AGENTS.md (no line names a guard FILE; docs/audits/gate-census-2026-09.md names the retired workflows' display names as a dated reading).

Why

The ruling (letter A, (iii)): one workflow, one job, four steps, keeping the merge_group trigger and no checkout for the step that needs none. This PR is that transport and nothing else — no judgement logic, no trigger, no grant beyond the union of what the four files already held.

Tests

Transport proof — a scratchpad script parsed pr-body-guards.yml and each retired file at git show 9d3c1566~1:.github/workflows/FILE (this PR's parent, ab3c776a; the four blobs are identical to the ones at 3fcafcd3, where the work started) with the yaml package: for every retired job, its gate step (minus the merged step's if:, with the retired step's name: restored, in the retired key order) deep-equals the merged step; the three retired checkout and setup-node steps each deep-equal the shared pair (minus if:); on.pull_request.types equal in all four; merge_group carried from closing-target and absent from the other three; no paths: on either side; every retired permission kept and the merged block exactly the union, all read; runs-on equal; timeout-minutes covers each retired value; the duplicate-fix step precedes the checkout; the six if: strings as listed; on has exactly pull_request + merge_group. Verdict: ✓ guards transport: 60 checks pass, 0 fail.

  • node scripts/check-closing-target-claim.mjs --self-test → exit 0 — ✓ check-closing-target-claim self-test: 121 cases pass.
  • node scripts/check-partof-closing-keyword.mjs --self-test → exit 0 — ✓ … 45 cases pass.
  • node scripts/check-single-claim-paths.mjs --self-test → exit 0 — ✓ … 93 cases pass.
  • node scripts/check-closing-keyword-parity.mjs --self-test → exit 0 — 40 assertions, 5 mutations of the shipped parsers each driven to red.; node scripts/check-closing-keyword-parity.mjs → exit 0 — OK (3 parsers agree on all 9 keywords and both measured separators; sweep found 6 file(s) carrying the grammar across 21437 tracked file(s), all registered).; --list names .github/workflows/pr-body-guards.yml for duplicate-fix-guard.
  • Reverse verification of the pins against the live file (each mutation by sed on the new file; restore by git checkout -- FILE from the index, proven by git hash-object equality): rename the closing-target step → check-closing-target-claim --self-test exit 1; rename the duplicate-fix step → check-single-claim-paths --self-test exit 1; delete the merge_group: line → closing-target self-test exit 1; drop edited from the trigger types → partof self-test exit 1; narrow const KEYWORDS in the github-script step → parity gate exit 1; pull-requests: read → none → single-claim self-test exit 1. Measured on the way: the two step-RENAME mutations first stayed exit 0, because both pins read the raw file and the new header quotes every step name in prose — the phantom-pin hole the single-claim self-test already names for its permission reads. The two pins now read the comment-stripped text, and the renames red.
  • node scripts/pm/dispatch-gates.mjs --commands .github/workflows/pr-body-guards.yml → exit 0, 39 commands: pnpm check:closing-target-claim, pnpm check:partof-closing-keyword, pnpm check:single-claim-paths and both parity invocations listed; scripts/check-closing-target-claim.mjs, check-partof-closing-keyword.mjs, check-single-claim-paths.mjs each ⊘ NOT MEASURED on stderr (env-carried), none offered bare on stdout.
  • Not owed: no package test / typecheck (the diff touches no package source); test:repo, pnpm lint and the whole-tree scans are CI's. Dogfood battery: n/a — no ! in the title and no (narrowing) arm. No ablation beyond the six mutations: no tested behaviour changes.

Acceptance notes

  • Premise, corrected on the tree: the ruling and the order say the merged workflow keeps "the merge_group trigger the three guards carry today (closing-target-claim-guard, partof-closing-keyword-guard, single-claim-path-guard)". On origin/main — at the seat's premise read d0a7e748, at 3fcafcd3 where this work started, and at this PR's base ab3c776a alike — only closing-target-claim-guard.yml carries merge_group:; the other two say in their headers "No merge_group: trigger, and that is not an oversight: a merge-queue event carries no pull request and therefore no body". The letter is kept: the merged workflow subscribes merge_group, the closing-target step runs on the queue build, and the three steps whose retired files never ran there are gated to the pull_request leg by if:. No guard gains or loses a leg. Reported as the premise deviation in the os-dev report.
  • The behaviour changes of one job, stated: (1) one check-run name, PR-body guards (claim · duplicate · Part-of · single-writer path), where the Checks tab showed four — each guard's verdict is its step, named as the job was; nothing pinned the four names as required contexts (REQUIRED_CONTEXTS unchanged, and check-expected-skips' roster has no guard row). (2) permissions is the union: the partof step's token now also carries issues: read and pull-requests: read, the single-claim and duplicate-fix steps' issues: read — all read, no write anywhere, as before. (3) Every gate step runs under !cancelled(), so a run shows all four verdicts, as four independent jobs did; the job is red if any step is. (4) The duplicate-fix guard runs before the checkout, in the same job; the three script-backed steps share one checkout + setup-node instead of three. (5) timeout-minutes: 10 for the whole job (was 10 / 5 / 5 / 5 per job). (6) On a merge_group run the duplicate-fix, partof and single-claim steps read as skipped steps of a green-or-red job, where before their workflows simply did not run.
  • Branch naming: this PR's head is claude/project-thread-31axox-2, as the seat's claim (comment 6434226733) names it. The claim names the third branch only by its suffix. The closing-target claim guard on PR 3, the PR that will carry the card's closing keyword, reads the newest Claim: for the FULL head branch name, so the seat may want a claim line naming claude/project-thread-31axox-3 in full before PR 3 opens. Flagged in the hand-back.
  • Stale mentions left in place (comment-only, outside the order's surface; carrier: the seat's stale-name sweep card from ci: fold the four daily patrols into one fleet-patrols.yml workflow (1 of 3) #26512 — same class): .github/workflows/pr-automation.yml:18-19, cross-repo-issue-closer.yml:216,227, .github/actions/setup-workspace/action.yml:85, lint.yml:1076,1082 (step ids, not file names), scripts/check-changeset-no-major.mjs:1047, scripts/check-cross-repo-closer-outcome.mjs:1105, scripts/check-empty-changeset.mjs:42 (a changeset name), scripts/check-pnpm-acquisition.mjs:65, scripts/pm/check-half-states.mjs:6272, scripts/pm/dispatch-gates.mjs:935,4379 (docblocks naming the measured specimen). The parity gate's duplicate-fix-guard parser ID is an ID, kept. docs/audits/gate-census-2026-09.md is a dated census and is left as the reading it was.
  • actionlint is not wired in this repository; the workflow was parsed with the yaml package by the transport check and by the three wiring batteries and the parity extractor.
  • Seat: no claim amendment — the surface is the four files plus the five gates the order's own git grep finds reading them by name, and nothing else.

Verification

Derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on head 9d3c156 (10 paths vs merge base ab3c776; 1,082 changed lines, under the 3,000 human-merge threshold): 51 families. The work started on 3fcafcd3, where the same 51 ran green; origin/main then moved 280 files (among them lint.yml, package.json, dispatch-gates.data.mjs and one hunk of dispatch-gates.self-test.mjs that does not overlap this PR's), so the commit was rebased onto ab3c776a, re-derived (the identical 51) and every family re-run on the rebased head. The readings below are the rebased head's. Each ran in this worktree under scripts/pm/os-verify-lock.sh (slot issue-26071) with the exit code captured before any pipe (bash -c "$cmd" > log 2>&1; st=$?), then reconciled:

  • node scripts/pm/dispatch-gates.mjs --ran ran.list --repo objectstack-ai/objectstack → exit 0 — ✓ dispatch-gates --ran: 51 derived famil(ies) accounted for — 51 run, 0 NOT-MEASURED (a DERIVED zero — all 51 recorded an exit code and none of them is 3).
  • pnpm check:pm-dispatch-gates → exit 0 — ✓ dispatch-gates self-test (fast tier): 1935 cases pass; 7 slow section(s) deferred. (441 s on this shared box).
  • pnpm check:closing-target-claim, pnpm check:partof-closing-keyword, pnpm check:single-claim-paths, node scripts/check-closing-keyword-parity.mjs and its --self-test → exit 0 each (in the list below).
  • The whole batch ran under scripts/pm/os-verify-lock.sh (slot issue-26071): VERDICT command-exit 0 · held the lock 1135s.
  • actionlint: not wired in this repository.
  • REQUIRED_CONTEXTS untouched: scripts/check-required-contexts.mjs still names only ci.yml, lint.yml and governed-surface-guard.yml.
  • CI on the PR is not waited for (the order forbids it); the seat reads the check-runs. Every reading above was taken on head 9d3c156; no commit followed.
All 51 derived families, exit 0 each (wall seconds shown where ≥ 10 s)
  • node scripts/check-closing-keyword-parity.mjs → exit 0 (27 s)
  • node scripts/check-closing-keyword-parity.mjs --self-test → exit 0
  • node scripts/check-comment-mask-corpus.mjs → exit 0 (180 s)
  • node scripts/check-declaration-mirrors.mjs → exit 0
  • node scripts/check-declaration-mirrors.mjs --self-test → exit 0
  • node scripts/check-self-test-wired.mjs → exit 0
  • node scripts/check-self-test-wired.mjs --self-test → exit 0
  • node scripts/check-self-test-workflow-commands.mjs → exit 0 (134 s)
  • node scripts/check-self-test-workflow-commands.mjs --self-test → exit 0
  • node scripts/check-step-collectors.mjs → exit 0
  • node scripts/check-step-collectors.mjs --self-test → exit 0
  • node scripts/check-whole-set-label-write.mjs → exit 0
  • node scripts/check-whole-set-label-write.mjs --self-test → exit 0
  • node scripts/ci/plan-test-core-shards.mjs --self-test → exit 0
  • node scripts/docs-audit/check-drift-comment.mjs → exit 0
  • node scripts/pm/bare-root-worklist.mjs --self-test → exit 0 (37 s)
  • node scripts/pm/ci-failure.mjs --self-test → exit 0
  • pnpm check:agent-test-spelling → exit 0
  • pnpm check:aggregator-roster → exit 0
  • pnpm check:bash32-floor → exit 0
  • pnpm check:ci-filter-parity → exit 0
  • pnpm check:cli-command-ids → exit 0 (19 s)
  • pnpm check:closing-target-claim → exit 0
  • pnpm check:cross-package-test-inputs → exit 0 (43 s)
  • pnpm check:declared-population-live → exit 0 (42 s)
  • pnpm check:driver-memory-census → exit 0
  • pnpm check:entry-guard → exit 0 (54 s)
  • pnpm check:gitlink-declared → exit 0
  • pnpm check:node-version → exit 0
  • pnpm check:nul-bytes → exit 0
  • pnpm check:parse-guard → exit 0
  • pnpm check:partof-closing-keyword → exit 0
  • pnpm check:pm-dispatch-gates → exit 0 (441 s)
  • pnpm check:pm-expected-skips → exit 0
  • pnpm check:pm-governed-merges → exit 0
  • pnpm check:pm-post-stamped → exit 0
  • pnpm check:pm-record-recognisers → exit 0
  • pnpm check:pnpm-acquisition → exit 0
  • pnpm check:pnpm-filter-targets → exit 0
  • pnpm check:pnpm-flag-spellings → exit 0
  • pnpm check:ratchet-remedy-authority → exit 0
  • pnpm check:refd-timer-probe → exit 0 (25 s)
  • pnpm check:required-contexts → exit 0
  • pnpm check:scripts-symbol-anchors → exit 0
  • pnpm check:shard-attestation → exit 0
  • pnpm check:single-claim-paths → exit 0
  • pnpm check:stall-guard-budget → exit 0
  • pnpm check:stall-guard-headroom → exit 0
  • pnpm check:watch-hint-literal → exit 0
  • pnpm check:workflow-status-functions → exit 0
  • pnpm check:workflow-step-name-quoting → exit 0

Generated by Claude Code

closing-target-claim-guard, duplicate-fix-guard,
partof-closing-keyword-guard and single-claim-path-guard become four
steps of one job in .github/workflows/pr-body-guards.yml. Each gate step
is the retired step verbatim but for its name (the retired job's display
name) and an `if:`: every step runs under !cancelled(), so one run still
shows four verdicts. The duplicate-fix github-script step runs first,
before the one checkout + setup-node the three script-backed steps
share. The merge_group leg stays for the closing-target step, the only
retired file that carried it; the other three steps are gated to
pull_request. Permissions are the union of the four retired grants, all
read.

The gates that read those files by name follow: the WIRING_WORKFLOW of
check-closing-target-claim, check-partof-closing-keyword and
check-single-claim-paths, the keyword-parity registry entry and mutation
anchor, and the dispatch-gates self-test's live env-carried specimen.
The two step-name pins now read the comment-stripped file, since the new
header quotes every step name in prose.

Claude-Session: https://claude.ai/code/session_01JQwxbW8nFvKeVjgKv8RTwH
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 27, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 9d3c15664784d7665e6db6be4acd3c8429231f0d
Local-runs: none
Scope: .github/workflows/closing-target-claim-guard.yml, .github/workflows/duplicate-fix-guard.yml, .github/workflows/partof-closing-keyword-guard.yml, .github/workflows/pr-body-guards.yml, .github/workflows/single-claim-path-guard.yml + scripts/check-closing-keyword-parity.mjs, scripts/check-closing-target-claim.mjs, scripts/check-partof-closing-keyword.mjs, scripts/check-single-claim-paths.mjs, scripts/pm/dispatch-gates.self-test.mjs

Director seat (summon #52, ledger objectstack#12708), after the fact by the maintainer's word 「#26566、#26568、#26570、#26585 也要追加复审,虽然我已经点了合并」: approved by os-zhuang at 9d3c1566 (2026-10-27T09:49Z), merged 2026-10-27T10:05Z; this record reads that head, read-only — the diff, card #26071 and its ruling (comment 6416984681, letter A (iii)), the head's check runs. dispatch-gates --tier: .github/workflows/** ⇒ the rules face, CONTRACT_REVIEW_TIER; served at CONTRACT_REVIEW_TIER.

① Derived judgments

  • Transport is faithful. Four workflows retired, one added. Comparing the retired files' non-comment lines against pr-body-guards.yml: every gate step, its uses: pins (github-script@v9, checkout@v7, setup-node@v7), env: keys and script bodies are present unchanged; what differs is exactly what a merge must change — workflow name:, the concurrency group, job key, the four step name: values now carrying the retired jobs' display names, timeout-minutes 5 → 10 (the largest retired value). Nothing a guard reads or refuses moved.
  • Legs preserved per guard, premise deviation handled honestly. The ruling's text says "the merge_group trigger the three guards carry today"; on the tree only closing-target-claim-guard.yml subscribed merge_group: (the other two retired headers say so in words). The new file keeps the letter: on: pull_request [opened, edited, reopened, synchronize] + merge_group; closing-target runs on both legs (if: !cancelled()), the other three are gated !cancelled() && github.event_name == 'pull_request'. No guard gains or loses a leg; the deviation is named in the PR body and the file header rather than silently "fixed" into the ruling's wording.
  • !cancelled() on every step keeps all four verdicts visible on one run — a refusal by the first guard no longer hides the other three, which is the one behavioural improvement over four files, and the ruling's stated reason for the merge.
  • Permissions and concurrency. permissions: is the union of the four retired grants and all read (contents, issues, pull-requests); the group pr-body-guards-${{ github.event.pull_request.number || github.ref }} is per PR on the pull_request leg and per queue ref on merge_group, so no queue build cancels another PR's run.
  • The check-run name changes and every pin follows. check-closing-keyword-parity.mjs (registry file/job), check-single-claim-paths.mjs (WIRING_WORKFLOW), the closing-target and partof scripts, and the dispatch-gates.self-test.mjs specimen all point at the new file/job; REQUIRED_CONTEXTS is untouched (none of the four retired names was a required context, and the new one is not added). The head's run shows the new name PR-body guards (claim · duplicate · Part-of · single-writer path) green and none of the four retired names — the rename took.

② Semver level

.github/workflows/** and scripts/** publish nothing; skip-changeset; Clause-②: no, Metadata-protocol: none in the body. No level owed. Correct.

③ Boundary flags

  • Governed path (workflows): landed under a GOVERNED_APPROVERS approval (os-zhuang, at this head). The seat records only.
  • Check runs pinned to 9d3c1566: 47 success, 12 expected skips, 0 other — including Lint · CI, release & engine gates, Lint · scoped · tooling self-tests (the parity/single-claim pins), Governed Surface Queue Guard, Test Core, Test UI (16 shards), and the new guard job itself. No local re-run.
  • The merge_group leg of the new file was not exercised by this PR's own runs (a PR run is the pull_request leg); its first queue build after landing is where the closing-target step's MERGE_GROUP_HEAD_REF path shows. The script body on that path is byte-identical to the retired one, so the exposure is the trigger wiring only.
  • Head branch claude/project-thread-31axox-2 is the card thread's second serial PR, as the seat's claim names; the body flags that PR (iii)'s closing keyword will need a full-name Claim: for -3 — a hand-back to the lane seat, not a must-fix here.

④ Design judgment

  • pr-body-guards.yml against the ruling (letter A (iii), "four PR-body guards → one workflow, keep each guard's leg"): holds. One job, four verdicts on one run, legs unchanged per guard, read-only permissions, per-PR concurrency. The long header comments explain the single divergence (which guard owns merge_group and why) at the place a future editor would otherwise "correct" it; that is the right home for the premise note.
  • The four retired files: removal is complete (no stray reference in .github/, the scripts' pins moved, the self-test specimen moved); the parity gate and the single-claim wiring gate are what will refuse a future PR that forgets the rename again, and both are green on this head.

Implemented-by: claude/project-thread-31axox-2
Reviewed-by: session_01ADHkmQr732toX135tyo5zs

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

objectstack-fleet Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor Author

Retracted: this comment was a duplicate post of the director seat's contract-review record on this PR; the record of record is comment 6439741369 above (same head, same verdict). Nothing else changes.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd size/xl skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants