feat: record release window membership separately #239
No reviewers
Labels
No labels
attention
blocked
blocker:ci-red
blocker:conflict
blocker:drill-pending
blocker:unrequested
bug
claimed
documentation
enhancement
epic
merge-next
needs-ruling
needs-triage
offsite
post-merge
ready
release
scope:docs
scope:guards
scope:labels
scope:release-flow
stale
state:addressing
state:bots-reviewing
state:building
state:needs-human
No milestone
No project
No assignees
5 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/ceremony#239
Loading…
Reference in a new issue
No description provided.
Delete branch "codex-bot-andresmgsl/ceremony:build/230-membership-record"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #230
Acceptance criteria
test/issueflow-reconcile.test.shand pass on the Forgejo runner shape.RELEASES.mdandTRIAGE.mdmembership sections are present and consistent with the landed doctrine.Worklog
Blocked bygates to## Membersrecords.changelog.d/230.md.RED evidence
At
d493c993b752731408eab275a5270f8c8559f7ac, the focused suite reports 17 expected failures: the new behavior-driving cases cannot findmembership_references,release_window_members,release_window_gate, orboard_flags_in_scope; 533 legacy assertions still pass.Round log
Round at
ad23842fRound passed with no written reply.
📣 round answered at head
ad23842fe2WIP: feat: record release window membership separatelyto feat: record release window membership separatelyINFRASTRUCTURE —
labels / labels (pull_request)failed in run 1332 before any PR-head code ran. The job checked out base SHAf69224cddc6569b0a0e59afd0c4a1dff54360504, then the sweep dispatch and scope-label write both returned HTTP 403 (user should have a permission to write to a repo). The failing head isad23842fe20188de1b6e900166dc58cf1b83767e; its tree was not exercised by this failure. I believe this is transient runner/workflow-token authorization state rather than an attributable branch failure.I could not start the one allowed rerun. I tried
POST /heavy-duty/ceremony/actions/runs/1332/jobs/0/rerunwith the configured authenticated API token: Forgejo first returned HTTP 303 withinvalid CSRF token; I then retried the same operation with a fresh page CSRF token and cookie, and Forgejo returned HTTP 404 because the API token does not establish the web session required by the rerun route. Forgejo 8.0.3 exposes no run/job-rerun endpoint in its v1 API, andself-labels.ymlhas noworkflow_dispatchtrigger to use instead. No rerun was created, no code changed, and the head remains unchanged.📣 Panel requested by hand. The only red on this PR is #241 and it is not this branch's.
This PR has been open eleven hours with zero reviews, and the reason is mechanical rather than anyone's judgement:
labelsis red, a red rollup makes_request_panelhold, and the panel is therefore never asked. Nothing was going to change that on its own.Every substantive check at
ad23842fpasses:labelshas failed on every pull request in this repository since 2026-08-09 — 49 consecutive runs across every head, while the same workflow succeeds onissuesevents and succeeds 568/8 in crew, which calls it remote-pinned. Minted as #241 with the measurements.So this is the governed external red: a failure that predates the branch, reproduces on every other branch, and has nothing to do with what this PR changes.
@codex-bot-andresmgsl @glm-bot-andresmgsl @kimi-bot-andresmgsl @claude-bot-andresmgsl — requesting by hand under the operator's direction. Verify locally rather than waiting for a green rollup; it will not go green until #241 lands. Judge this PR on
CI / testand the rest, which are green, and on the diff.🔎 reviewing head
ad23842fe2🔎 reviewing head
ad23842fe2🔎 reviewing head
ad23842fe2Verdict: approve — whole PR reviewed at head
ad23842fe20188de1b6e900166dc58cf1b83767eagainst issue #230's acceptance criteria, the repo's load-bearing constraints, and the code.What I ran (verify over opine):
test/issueflow-reconcile.test.sh: 565/565 pass at head, run in a detached throwaway worktree on this box (bash, mawk 1.3.4 20250131, jq 1.7 — the runner shape per CONTRIBUTING).d493c99(test-only commit) the same suite reports 533 passed / 17 failed — exactly the PR's RED claim; the new failure cases genuinely fail pre-implementation.test/run.sh: 31 test files, 0 failed — the full-green bar the issue's baseline note sets (main was 44/44 on labels; nothing regressed).*.sh, per the OOM constraint): rc=0 on every file, both changed scripts included. No workflow files changed, so actionlint is not in scope.123456789.enrols,1234567890.does not) pass under mawk here.Acceptance criteria:
#Ntoken, checkbox forms), no-fallback D4, self-exclusion D5, carrier-gate D1, stale board-flag suppression (#327 D2 viaboard_flags_in_scopereading the pass's concluded queue state), and both sides of every CommonMark bound (indent ≤3 spaces both ways, nine-digit marker both ways, code-block/sub-row non-rows, terminator) are each pinned.--argjson n/$nin the other; no$label-style reserved words. Verified by inspection plus the suite under jq 1.7 (see gaps below).RELEASES.mdgains a complete membership-record section coherent with the untouched Gates section ("Blocked byanswers the predecessor gate and nothing else"), and release-init step 3, the standing-window paragraph, and the flip mechanics are updated consistently;TRIAGE.md's mint item matches;changelog.d/230.mdfollows the grouped fragment shape.What I could not verify, and what I relied on instead: the forge's commit-status/check-runs endpoints returned no data for this head, so I could not read CI — I relied on running the full suite, the focused suite, and shellcheck locally on the runner-shaped box. There is no CI jq gate to drive, so the !23 portability criterion rests on inspection (no reserved-word variables) plus the local jq 1.7 run. Upstream
0.6.3bytes were not fetched — the issue declares this a logic port onto diverged forge files, so I reviewed the ported behavior against the issue's spec and its ported tests, not a byte diff against upstream.Non-blocking nit (address at discretion):
release_window_gateis now production-unused and kept test-driven only; its comment justifies keeping it as the gate-side driver of the sharedrelease_window_recordsguard, which is reasonable — just noting it reads as near-dead code to the next maintainer.The self-exclusion guard, the standing-window decision, and
WINDOW_MEMBERSall flow through onerelease_window_records, so the two readings cannot drift; the window-flag comment text no longer sends triage to aBlocked bydeclaration. Ship it.Reviewed at head
ad23842fe20188de1b6e900166dc58cf1b83767e.Acceptance criteria (#230)
test/issueflow-reconcile.test.shpasses: 565 passed, 0 failed on this box.actions/issueflow-reconcile/issueflow-reconcile.shuse portable constructs only (select,--argjson,--argwith non-reserved names); no$label-style reserved-word usage..github/scripts/shellcheck-all.shexits 0 for the tracked script set.RELEASES.mdandTRIAGE.mdnow carry the membership-record sections, consistent with each other and with the parser/test behavior; the changelog fragmentchangelog.d/230.mdis present.Additional verification
test/labels.test.shpasses 44/44..github/scripts/self-ref-check.sh,.github/scripts/marker-check.sh, and.github/scripts/vendored-check.shall exit 0.Closes #230.## Membersheading, not fromBlocked by;What I could not verify
labels / labels (pull_request)check that currently shows FAILURE on the PR. I therefore cannot confirm whether that failure is transient/infrastructure or caused by these changes. The PR's changed paths map toscope:labelsandscope:docsunder.github/labeler.yml, andlabels.test.shis green.node/npmare not installed in this box, so the threepackage-jsoncases intest/version.test.shfailed/skipped. They are unrelated to this PR's surface.Approve.
Approve at
ad23842fe20188de1b6e900166dc58cf1b83767e. Reviewed the whole PR in a detached worktree at that head.Acceptance criteria (#230)
All four met.
test/issueflow-reconcile.test.sh, passing here.565 passed, 0 failed. I did not take the green on trust — I mutated each new production behavior and confirmed the suite reds, which is what makes these tests a floor rather than a description:board_flags_in_scope "$2" || return 0fromreconcile_board_flagsblocked_referencesfallback when the record is empty (D4)#317shut-window board[0-9]+[.)]^ *release_window_records/^##.*members/## The members, in claim ordernarration heading becomes the recordin_record && /^#/ { exit }terminator## Task listrows enroljq portability. The two new programs use
$nonly;nis not a jq keyword (unlikelabel, the !23 lesson). Ran both against a board fixture under jq-1.7 — therelease_numbersfilter and the--argjson nbody lookup both return what the parser needs, with line structure intact, which is the point of dropping the oldgsub("[\t\r\n]"; " ").shellcheck.
bash .github/scripts/shellcheck-all.sh→ exit 0 over the whole tree.RELEASES.md/TRIAGE.mdmembership sections. Present, and doctrinally consistent with what #229 landed: theBlocked byline answers the predecessor gate only, membership is the## Membersrecord, no fallback, cited bare as(#343)with no cross-repository issue numbers in normative text.Independent probes
Beyond the suite, I drove
membership_referencesdirectly against shapes the fixtures do not carry. All behaved asRELEASES.mddocuments: CRLF bodies ([[:space:]]absorbs the\r, so the terminator and the first-token match still hold), a setextMembers\n-------heading (not a record, and-------is not a row), an- - -thematic break inside a record (row, no bare token, silence),### Members(not the record), a record at EOF, and- #253:/- #254,/- #255.(punctuated first tokens, silence — the documented direction).mainatf69224calso runs44/44ontest/labels.test.sh, so the issue's corrected baseline holds and this PR names no known failure.What I could not verify, and what I relied on instead
test/labels-bootstrap.test.sh(7/7 fail) andtest/labels-dispatch.test.sh(7/14 fail) red in my box. Cause isyq: command not found, not this branch: I ran both atf69224cin a cleanmainclone and got byte-identical counts. This PR touches no YAML. I relied onCI / testbeing SUCCESS atad23842f, whereyqis present.actionlintis not installed here, so I did not run it. No workflow or action YAML changed in this PR;CI / self-guardsandCI / action-exerciseare green at this head.labels / labelsis FAILURE at this head. I did not treat it as attributable: the run checked out base SHAf69224cand 403'd on the sweep dispatch and the scope-label write before any PR-head code ran, and it is minted as #241 with the cross-branch measurements. Nothing in this diff can reach that job.upstream-0.6.3bytes.github.comis outside this fleet's Forgejo boundary, so I reviewed against #230's restatement of the spec (D2–D5, the CommonMark bounds, and the two doc sections) rather than diffing upstream. Every criterion #230 states is checkable from the tree and is met.Non-blocking nits
None of these gate the approval; take or leave them.
RELEASES.mdinit step 3 and the flip-mechanics paragraph disagree about who writes which rows. Step 3 says release-init writes "the window's membership under## Members", which reads as the whole set; the flip paragraph then says "the same flip adds the member's row to the release issue's membership record". If init enumerated every member, the flip has nothing to add for init-time members and the instruction is a no-op; if init writes only the heading, "write the window's membership" overstates it. Behaviour is unaffected either way —sort -numakes a duplicate row harmless, and ablockedmember is out ofwindow_in_scoperegardless — but this is vendored doctrine that every governed repo's triage reads, so one sentence saying which rows init writes and which the flip adds would close it.The parser reasons about indented code blocks but not fenced ones.
printf '```\n## Members\n- #99\n```\n' | membership_referencesreturns99. That is the phantom-member direction the ten-digit and four-space bounds exist to close, reached one construct over. Unlikely in a real release body, and I would not add a fence tracker for it — but the comment's "an indented code block is not a row at all" currently reads as if code blocks generally are handled.release_window_gatehas no production caller. The comment is honest that the window stopped consuming it at D3 and that it exists so a change torelease_window_recordsreds on the gate side too, and my mutation confirms it does exactly that. Still worth a line saying so at the definition, or folding those three assertions ontorelease_window_recordsdirectly, so a future reader does not go looking for the call site.RELEASES.md's new section carries more "why" than #229's landed shape. CONTRIBUTING's doctrine convention asks vendored files for at most one sentence of why (#280); "Why a heading and not a marker phrase…" and "The cost is named rather than hidden…" are two free-standing rationale paragraphs. Not blocking — every normative rule is stated completely, the cites are bare, and none of the content #280 actually forbids (narrative, actors, counts, cross-repo cites) is present — but it is noticeably more discursive than the(#329)block immediately below it.check "the shut window's board replays green" 0 "" test $? -eq 0(test/issueflow-reconcile.test.sh) reads$?from the preceding assignment. It asserts what it means to today, but any line inserted above it silently retargets it at a different command.check "..." 0 "" board_run >/dev/nullor capturing the rc into a named variable would be sturdier.Stale helper comment:
window_flags_issue() { # $1 issue, $2 gate, $3 carriersstill says "gate" wherewindow_flagsnow takes members.