Skip to content

Commit c223028

Browse files
claude[bot]claude
andauthored
tooling(pm): sweep the pm-loop state labels off cards closed on a merged delivery (#16021)
* tooling(pm): mechanize the closed-card residue strip as a patrol step A card GitHub closes on a merged `Fixes` pull request keeps every label, so the pm-loop state labels stay behind as claims that work is in flight. The seat has been removing them by hand, once per landing. `scripts/pm/sweep-closed-cards.mjs` takes that stroke: it lists the closed cards carrying H22's residue set (imported, never restated), establishes that the close really was a merged delivery by one of two routes -- a closing commit contained in the default branch, or a pull request whose body closes the card and merged into it -- and removes exactly the residue labels, leaving ownership, priority and outcome untouched. Each stripped card gets one comment naming the readings taken. The action set is bounded to the fresh edge by a window, because the 2026-08-31 maintainer ruling refused a backfill over the archived stock while keeping close-time hygiene as the convention. `--all-time` exists so a seat can measure that stock in a dry run; the workflow never passes it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox * tooling(pm): run the sweep's own self-test before it writes, and refuse unknown flags `check-self-test-wired` is the gate that settles where the self-test belongs: CI runs this script, so CI must run its cases. The patrol step now runs them first and skips the sweep entirely if they fail -- this step writes to other people's cards, and a broken predicate that still runs is the one failure the next run cannot undo. The CLI gains `--dry-run` as a real flag so the workflow can name the mode it is in, and refuses any argument it does not know: a caller who believes it passed a mode flag and did not must not get a run that looks ordinary. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 7845951 commit c223028

3 files changed

Lines changed: 1368 additions & 0 deletions

File tree

‎.github/workflows/half-state-patrol.yml‎

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,10 @@ on:
149149
# place that has to name it. (The adopted objectui copy carries this row
150150
# already; upstream is catching up to its own port.)
151151
- 'scripts/invoked-as.mjs'
152+
# The closed-card sweep this workflow also calls (#16005). Same reasoning
153+
# as the row above, one file along: a step whose script can change without
154+
# this trigger firing is a step whose PR-time proof is a coincidence.
155+
- 'scripts/pm/sweep-closed-cards.mjs'
152156
- '.github/workflows/half-state-patrol.yml'
153157

154158
# Least privilege: this job reads the repo and writes exactly one issue BODY.
@@ -248,6 +252,96 @@ jobs:
248252
echo "check-half-states exited $code"
249253
cat "$RUNNER_TEMP/report.err" >&2 || true
250254
255+
- name: Sweep the closed cards
256+
id: closed-cards
257+
# #16005 — the pm-loop state labels are CLAIMS that work is in flight,
258+
# and GitHub leaves every label in place when a merged `Fixes` pull
259+
# request closes a card. The seat was paying a hand round trip per
260+
# landing to remove them (eighteen identical ones in one measured
261+
# shift). This step is that stroke, mechanized; the script's header
262+
# carries the ruling it obeys and the window that keeps it to
263+
# close-time hygiene rather than the backfill the 2026-08-31 maintainer
264+
# ruling refused.
265+
#
266+
# ⛔ Gated on the repository NAME, unlike every other step in this file:
267+
# this one is objectstack-only until a sibling has both its patrol
268+
# anchor (objectui#5986) and a copy of the script. A verbatim copy of
269+
# this workflow in a sibling repo therefore SKIPS this step rather than
270+
# failing on a missing file.
271+
#
272+
# ⛔ This step never fails the job, whatever the sweep returns. The
273+
# anchor write below is the patrol's product and must not be starved by
274+
# a step that runs before it; the alarm rides an annotation and the run
275+
# summary instead. Findings are not a failure condition here either —
276+
# the same posture the sweep step above takes.
277+
if: github.repository == 'objectstack-ai/objectstack'
278+
env:
279+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
280+
# Same wiring, and the same reason, as the live sweep above: the board
281+
# this run acts on is the repo this workflow is installed in.
282+
PM_SWEEP_REPO: ${{ github.repository }}
283+
PROVENANCE: >-
284+
posted by half-state-patrol [run ${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
285+
· trigger `${{ github.event_name }}`
286+
# A pull_request run PROVES the step — the transport, the flags and
287+
# the rendering on a real runner — and writes nothing, exactly as the
288+
# anchor write below is skipped for it. That convention is this file's
289+
# and it is load-bearing: a PR must never write to the board.
290+
SWEEP_MODE: ${{ github.event_name == 'pull_request' && '--dry-run' || '--write' }}
291+
run: |
292+
# The judge's own cases first, and the sweep only if they hold: this
293+
# step WRITES to other people's cards, and a broken predicate that
294+
# still runs is the one failure mode that cannot be undone by the next
295+
# run. `check:pm-closed-card-sweep` is the same command under a dev
296+
# -facing name; this is the invocation CI holds.
297+
set +e
298+
node scripts/pm/sweep-closed-cards.mjs --self-test > "$RUNNER_TEMP/closed-cards-selftest.log" 2>&1
299+
selftest=$?
300+
set -e
301+
cat "$RUNNER_TEMP/closed-cards-selftest.log"
302+
if [ "$selftest" != "0" ]; then
303+
echo "exit_code=$selftest" >> "$GITHUB_OUTPUT"
304+
{
305+
echo "### Closed-card sweep — SKIPPED: its own self-test failed (exit $selftest)"
306+
echo
307+
echo '```'
308+
cat "$RUNNER_TEMP/closed-cards-selftest.log"
309+
echo '```'
310+
} >> "$GITHUB_STEP_SUMMARY"
311+
echo "::error::closed-card sweep self-test failed (exit $selftest) — the sweep did NOT run and wrote nothing. Nothing here is a reading about the board."
312+
exit 0
313+
fi
314+
set +e
315+
node scripts/pm/sweep-closed-cards.mjs "$SWEEP_MODE" --provenance="$PROVENANCE" \
316+
> "$RUNNER_TEMP/closed-cards.md" 2> "$RUNNER_TEMP/closed-cards.err"
317+
code=$?
318+
set -e
319+
# Captured with NO pipe in between, for the reason the step above
320+
# states at length: piped, `$?` is the pipe's status and a red run and
321+
# a green one read the same.
322+
echo "exit_code=$code" >> "$GITHUB_OUTPUT"
323+
{
324+
echo "### Closed-card sweep — exit $code (\`$SWEEP_MODE\`)"
325+
echo
326+
echo '```'
327+
cat "$RUNNER_TEMP/closed-cards.md" 2>/dev/null || echo '(no report produced)'
328+
echo '```'
329+
echo
330+
echo '<details><summary>stderr</summary>'
331+
echo
332+
echo '```'
333+
cat "$RUNNER_TEMP/closed-cards.err" 2>/dev/null || true
334+
echo '```'
335+
echo
336+
echo '</details>'
337+
} >> "$GITHUB_STEP_SUMMARY"
338+
cat "$RUNNER_TEMP/closed-cards.err" >&2 || true
339+
if [ "$code" = "3" ]; then
340+
echo "::error::closed-card sweep exited 3 — it could NOT read the board, so it says nothing about whether residue is accumulating. See this run's summary."
341+
elif [ "$code" != "0" ]; then
342+
echo "::warning::closed-card sweep exited $code — at least one card was left UNJUDGED. An unjudged card is not a clean card; see this run's summary."
343+
fi
344+
251345
- name: Resolve the anchor issue
252346
# An install with no anchor configured has nowhere to land its report,
253347
# and the ONLY safe behaviour is to say so loudly (#11217). The two

‎package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
"check:declared-population-live": "node scripts/check-declared-population-live.mjs --self-test && node scripts/check-declared-population-live.mjs",
7676
"check:pm-half-states": "node scripts/pm/check-half-states.mjs --self-test",
7777
"check:pm-clause2-carriers": "node scripts/pm/check-clause2-carriers.mjs --self-test",
78+
"check:pm-closed-card-sweep": "node scripts/pm/sweep-closed-cards.mjs --self-test",
7879
"check:pm-governed-merges": "node scripts/pm/check-governed-merges.mjs --self-test",
7980
"check:pm-governed-prose": "node scripts/pm/check-governed-prose.mjs --self-test && node scripts/pm/check-governed-prose.mjs",
8081
"check:publish-smoke-pin": "node scripts/publish-smoke-pack.mjs --self-test",

0 commit comments

Comments
 (0)