forked from heavy-duty/ceremony
fixtures take the corpus correction and the three ruled cases
Triage ruled all three questions (18:11Z) and corrected the corpus: at the 10:28:54Z mint #257 was `ready`, not `claimed`, and #253 was `claimed` with NO open PR — #285 was not created until 10:49:16Z. The morning board takes that shape, which also makes the six `ready` non-members come out as six, and puts the blocked twin on the board rather than only in a decision probe. D2's parenthetical is struck: unblocked = open and not blocked, both flags, one definition. D3b is corrected in the same direction — a non-member `claimed` WITH an open PR is flagged too, because a non-member holding a builder and a review round is #292's competition realized, not a mild case of it. Nothing in the implementation moves: neither flag ever consulted PR liveness. Three cases added, so the fixtures pin the rulings and not the prose: - both carriers `claimed` with their own PRs open -> still flags. The ninety-three minutes from #285's creation to its merge are exactly when the struck parenthetical went silent on a live collision, which made the flag's firing a property of someone's workflow rather than of the board. - a fifteen-member declaration with every member closed -> D3 dormant, and the release issue never flagged as its own non-member. A gate declaration never empties; the precondition is its OPEN members, read off the board. - flagged -> resolved -> recreated unchanged -> silent, asserted as D4's stated boundary rather than left accidental. Plus today's board — the `blocked` sink, a `claimed` gate member, two `blocked` issues — which draws nothing. Verified live as well as in fixtures: a DRY_RUN sweep of this branch against heavy-duty/ceremony's real board draws zero flags of either kind.
This commit is contained in:
parent
bdcc211a0b
commit
5d13573c53
1 changed files with 95 additions and 23 deletions
|
|
@ -2176,18 +2176,23 @@ board_run() {
|
||||||
bash "$ROOT/actions/issueflow-reconcile/issueflow-reconcile.sh" 2>&1
|
bash "$ROOT/actions/issueflow-reconcile/issueflow-reconcile.sh" 2>&1
|
||||||
}
|
}
|
||||||
|
|
||||||
# The morning shape: ten open issues, six of them `ready` non-members, a
|
# The morning shape, as the board actually stood at the 10:28:54Z mint:
|
||||||
# standing gate, and one deliverable carried three times in two spellings.
|
# #253 `claimed` with no open PR (#285 was not created until 10:49:16Z),
|
||||||
|
# #257 `ready` since the evening before, #284 minted `ready` into both of
|
||||||
|
# them — six `ready` non-members against a standing gate, and one deliverable
|
||||||
|
# carried three times in two spellings.
|
||||||
board_issue 249 blocked,release 'Release 0.6.0 — the board empties into the tag' \
|
board_issue 249 blocked,release 'Release 0.6.0 — the board empties into the tag' \
|
||||||
'Blocked by #253, #257.'
|
'Blocked by #253.'
|
||||||
board_issue 253 claimed 'issueflow-reconcile — a release epic announces its own release-init' '' 1
|
board_issue 253 claimed 'issueflow-reconcile — a release epic announces its own release-init' '' 1
|
||||||
board_issue 257 claimed 'actions/issueflow-reconcile — a failed board read sweeps an empty board' '' 1
|
board_issue 257 ready 'actions/issueflow-reconcile — a failed board read sweeps an empty board'
|
||||||
board_issue 264 ready 'TRIAGE.md — the no-assignee clause scopes to the flag'
|
board_issue 264 ready 'TRIAGE.md — the no-assignee clause scopes to the flag'
|
||||||
# shellcheck disable=SC2016 # the backticks are the real issue title's Markdown
|
# shellcheck disable=SC2016 # the backticks are the real issue title's Markdown
|
||||||
board_issue 266 ready 'TRIAGE.md — the epic task-list heading is literally `## Task list`'
|
board_issue 266 ready 'TRIAGE.md — the epic task-list heading is literally `## Task list`'
|
||||||
board_issue 276 ready 'REVIEWER.md — the green-check precondition'
|
board_issue 276 ready 'REVIEWER.md — the green-check precondition'
|
||||||
board_issue 281 ready 'LABELS.md — the attention row'
|
board_issue 281 ready 'LABELS.md — the attention row'
|
||||||
board_issue 282 ready 'TRIAGE.md — the two comment links come out'
|
# The blocked twin on the same key, on the board rather than in a decision
|
||||||
|
# probe: it is neither a collision flag nor one of the six.
|
||||||
|
board_issue 282 blocked 'TRIAGE.md — the two comment links come out' 'Blocked by #266.'
|
||||||
# shellcheck disable=SC2016 # the backticks are the real issue title's Markdown
|
# shellcheck disable=SC2016 # the backticks are the real issue title's Markdown
|
||||||
board_issue 284 ready 'issueflow-reconcile — the issue-side ruling clock counts `assigned`'
|
board_issue 284 ready 'issueflow-reconcile — the issue-side ruling clock counts `assigned`'
|
||||||
board_assemble 249 253 257 264 266 276 281 282 284
|
board_assemble 249 253 257 264 266 276 281 282 284
|
||||||
|
|
@ -2205,22 +2210,24 @@ check "...and #257 names #253, so the flag asks for a chain and not a fan" 0 \
|
||||||
printf '%s\n' "$morning_out"
|
printf '%s\n' "$morning_out"
|
||||||
check "...while #253, the oldest carrier, is asked for nothing" 1 "" \
|
check "...while #253, the oldest carrier, is asked for nothing" 1 "" \
|
||||||
grep -qF 'issueflow: #253: collision flag' <<<"$morning_out"
|
grep -qF 'issueflow: #253: collision flag' <<<"$morning_out"
|
||||||
check "the TRIAGE.md triple chains the same way" 0 \
|
check "the TRIAGE.md pair chains the same way" 0 \
|
||||||
'issueflow: #266: collision flag — triage=264' printf '%s\n' "$morning_out"
|
'issueflow: #266: collision flag — triage=264' printf '%s\n' "$morning_out"
|
||||||
check "...through its tail" 0 'issueflow: #282: collision flag — triage=266' \
|
check "...while the blocked twin beside them is the goal state, not a flag" 1 "" \
|
||||||
printf '%s\n' "$morning_out"
|
grep -qF 'issueflow: #282: collision flag' <<<"$morning_out"
|
||||||
check "the morning board draws exactly four collision flags" 0 "4" \
|
check "the morning board draws exactly three collision flags" 0 "3" \
|
||||||
flag_count collision "$morning_out"
|
flag_count collision "$morning_out"
|
||||||
# D3's corpus: the six `ready` non-members that raced the emptying gate.
|
# D3's corpus: the six `ready` non-members that raced the emptying gate.
|
||||||
for nonmember in 264 266 276 281 282 284; do
|
for nonmember in 257 264 266 276 281 284; do
|
||||||
check "#$nonmember is flagged as a ready non-member under #249" 0 \
|
check "#$nonmember is flagged as a ready non-member under #249" 0 \
|
||||||
"issueflow: #$nonmember: window flag — a ready non-member under #249" \
|
"issueflow: #$nonmember: window flag — a ready non-member under #249" \
|
||||||
printf '%s\n' "$morning_out"
|
printf '%s\n' "$morning_out"
|
||||||
done
|
done
|
||||||
check "the morning board draws exactly six window flags" 0 "6" \
|
check "the morning board draws exactly six window flags" 0 "6" \
|
||||||
flag_count window "$morning_out"
|
flag_count window "$morning_out"
|
||||||
check "...and never flags a gate member" 1 "" \
|
check "...and never flags the gate member holding the window open" 1 "" \
|
||||||
grep -qE 'issueflow: #(253|257): window flag' <<<"$morning_out"
|
grep -qF 'issueflow: #253: window flag' <<<"$morning_out"
|
||||||
|
check "...nor the blocked issue already placed behind something" 1 "" \
|
||||||
|
grep -qF 'issueflow: #282: window flag' <<<"$morning_out"
|
||||||
check "...nor the release issue that carries the window" 1 "" \
|
check "...nor the release issue that carries the window" 1 "" \
|
||||||
grep -qF 'issueflow: #249: window flag' <<<"$morning_out"
|
grep -qF 'issueflow: #249: window flag' <<<"$morning_out"
|
||||||
# D1: comments only. Not "no unexpected edit" — no edit at all.
|
# D1: comments only. Not "no unexpected edit" — no edit at all.
|
||||||
|
|
@ -2254,7 +2261,7 @@ check "a standing collision is silent on the next sweep (D4)" 1 "" \
|
||||||
grep -qF 'issueflow: #284: collision flag' <<<"$resweep_out"
|
grep -qF 'issueflow: #284: collision flag' <<<"$resweep_out"
|
||||||
check "a standing window non-membership is silent too" 1 "" \
|
check "a standing window non-membership is silent too" 1 "" \
|
||||||
grep -qF 'issueflow: #276: window flag' <<<"$resweep_out"
|
grep -qF 'issueflow: #276: window flag' <<<"$resweep_out"
|
||||||
check "...while every other flag on the board still speaks" 0 "3" \
|
check "...while every other flag on the board still speaks" 0 "2" \
|
||||||
flag_count collision "$resweep_out"
|
flag_count collision "$resweep_out"
|
||||||
check "...and the window flags with it" 0 "5" \
|
check "...and the window flags with it" 0 "5" \
|
||||||
flag_count window "$resweep_out"
|
flag_count window "$resweep_out"
|
||||||
|
|
@ -2306,34 +2313,99 @@ check "...and still reports a whole pass" 0 'issueflow: reconciled.' \
|
||||||
printf '%s\n' "$ruled_out"
|
printf '%s\n' "$ruled_out"
|
||||||
|
|
||||||
# -- an emptied gate leaves the window flag dormant (test plan) -------------
|
# -- an emptied gate leaves the window flag dormant (test plan) -------------
|
||||||
# The release stands `ready` because every declared member closed, so no
|
# A gate DECLARATION never empties: #249 names fifteen members and still names
|
||||||
# member is on the open board. `Blocked by` is still in the body: a
|
# fifteen after all fifteen close. So the precondition is the gate's OPEN
|
||||||
# declaration is not a gate, an OPEN member is.
|
# members, not its parse — read straight off the board, which already is the
|
||||||
|
# open set. Under the declaration reading the release issue, now `ready`, is
|
||||||
|
# itself an open unblocked non-`epic` non-member, and D3 would flag the sink
|
||||||
|
# at the exact moment the window ends.
|
||||||
board_issue 249 ready,release 'Release 0.6.0 — the board empties into the tag' \
|
board_issue 249 ready,release 'Release 0.6.0 — the board empties into the tag' \
|
||||||
'Blocked by #253, #257.'
|
'Blocked by #218, #230, #232, #236, #237, #238, #241, #242, #247, #248, #251, #252, #253, #254, #257.'
|
||||||
board_issue 264 ready 'TRIAGE.md — the no-assignee clause scopes to the flag'
|
board_issue 264 ready 'TRIAGE.md — the no-assignee clause scopes to the flag'
|
||||||
# shellcheck disable=SC2016 # the backticks are the real issue title's Markdown
|
# shellcheck disable=SC2016 # the backticks are the real issue title's Markdown
|
||||||
board_issue 266 ready 'TRIAGE.md — the epic task-list heading is literally `## Task list`'
|
board_issue 266 ready 'TRIAGE.md — the epic task-list heading is literally `## Task list`'
|
||||||
board_assemble 249 264 266
|
board_assemble 249 264 266
|
||||||
empty_gate_out="$(board_run)"
|
empty_gate_out="$(board_run)"
|
||||||
check "an emptied gate leaves D3 dormant" 1 "" grep -qF ': window flag' <<<"$empty_gate_out"
|
check "a fifteen-member declaration with every member closed leaves D3 dormant" 1 "" \
|
||||||
|
grep -qF ': window flag' <<<"$empty_gate_out"
|
||||||
|
check "...and the release issue is never flagged as its own non-member" 1 "" \
|
||||||
|
grep -qF 'issueflow: #249' <<<"$empty_gate_out"
|
||||||
check "...while the collision flag beside it is unaffected" 0 \
|
check "...while the collision flag beside it is unaffected" 0 \
|
||||||
'issueflow: #266: collision flag — triage=264' printf '%s\n' "$empty_gate_out"
|
'issueflow: #266: collision flag — triage=264' printf '%s\n' "$empty_gate_out"
|
||||||
|
|
||||||
# -- the #284 shape end to end: a claimed carrier with its PR in flight -----
|
# -- both carriers claimed, both with their own PRs open (test plan) --------
|
||||||
|
# The ninety-three minutes from #285's creation to its merge: under D2's
|
||||||
|
# struck parenthetical the live collision went silent for all of them, so
|
||||||
|
# whether the flag ever fired depended on where the sweep tick fell relative
|
||||||
|
# to a builder opening a PR. It fires on the board, and only on the board.
|
||||||
printf '%s\n' \
|
printf '%s\n' \
|
||||||
'{"data":{"repository":{"pullRequests":{"nodes":[{"number":285,"body":"","closingIssuesReferences":{"nodes":[{"number":253}]}}],"pageInfo":{"hasNextPage":false,"endCursor":null}}}}}' \
|
'{"data":{"repository":{"pullRequests":{"nodes":[{"number":285,"body":"","closingIssuesReferences":{"nodes":[{"number":253}]}},{"number":286,"body":"","closingIssuesReferences":{"nodes":[{"number":284}]}}],"pageInfo":{"hasNextPage":false,"endCursor":null}}}}}' \
|
||||||
>"$BOARD/graphql-open.json"
|
>"$BOARD/graphql-open.json"
|
||||||
board_issue 253 claimed 'issueflow-reconcile — a release epic announces its own release-init' '' 1
|
board_issue 253 claimed 'issueflow-reconcile — a release epic announces its own release-init' '' 1
|
||||||
# shellcheck disable=SC2016 # the backticks are the real issue title's Markdown
|
# shellcheck disable=SC2016 # the backticks are the real issue title's Markdown
|
||||||
|
board_issue 284 claimed 'issueflow-reconcile — the issue-side ruling clock counts `assigned`' '' 1
|
||||||
|
board_assemble 253 284
|
||||||
|
both_claimed_out="$(board_run)"
|
||||||
|
check "two claimed carriers, both with PRs in flight, still draw the newer's flag" 0 \
|
||||||
|
'issueflow: #284: collision flag — issueflow-reconcile=253' \
|
||||||
|
printf '%s\n' "$both_claimed_out"
|
||||||
|
check "...and both live claims are left exactly as they were" 1 "" \
|
||||||
|
grep -qF 'issue edit' "$BOARD/edits"
|
||||||
|
# The same board with the newer side `ready`, so the queue label is visibly
|
||||||
|
# the only input the flag has.
|
||||||
|
# shellcheck disable=SC2016 # the backticks are the real issue title's Markdown
|
||||||
board_issue 284 ready 'issueflow-reconcile — the issue-side ruling clock counts `assigned`'
|
board_issue 284 ready 'issueflow-reconcile — the issue-side ruling clock counts `assigned`'
|
||||||
board_assemble 253 284
|
board_assemble 253 284
|
||||||
in_flight_out="$(board_run)"
|
in_flight_out="$(board_run)"
|
||||||
check "a claimed carrier with an open PR still draws the newer issue's flag" 0 \
|
check "a claimed carrier with an open PR draws the ready issue's flag too" 0 \
|
||||||
'issueflow: #284: collision flag — issueflow-reconcile=253' \
|
'issueflow: #284: collision flag — issueflow-reconcile=253' \
|
||||||
printf '%s\n' "$in_flight_out"
|
printf '%s\n' "$in_flight_out"
|
||||||
check "...and the live claim is left exactly as it was" 1 "" \
|
|
||||||
grep -qF 'issue edit' "$BOARD/edits"
|
# -- flagged, resolved, recreated unchanged: silent, and specified ----------
|
||||||
|
# D4's boundary, asserted rather than left accidental. Nothing is posted at
|
||||||
|
# the resolution — D1 admits no comment there — so the thread's last word is
|
||||||
|
# still the state itself and an identical return says nothing new. #292 D2b
|
||||||
|
# owns the recurrence: a board state violating the window invariants is
|
||||||
|
# triage's to repair in the tick it is seen, and triage has already been told
|
||||||
|
# about this one.
|
||||||
|
jq -n --arg b "<!-- issueflow:$(state_marker collision 'issueflow-reconcile=253') -->
|
||||||
|
flagged once" '[{"user": {"login": "sweep-bot"}, "body": $b}]' \
|
||||||
|
>"$BOARD/repos_owner_repo_issues_284_comments.json"
|
||||||
|
board_assemble_keep() { # board_assemble without wiping the seeded threads
|
||||||
|
local n
|
||||||
|
for n in "$@"; do cat "$BOARD/repos_owner_repo_issues_$n.json"; done \
|
||||||
|
| jq -sc . >"$BOARD/repos_owner_repo_issues_state_open_per_page_100.json"
|
||||||
|
}
|
||||||
|
board_assemble_keep 284
|
||||||
|
resolved_out="$(board_run)"
|
||||||
|
check "the collision resolves when its carrier leaves the board" 1 "" \
|
||||||
|
grep -qF ': collision flag' <<<"$resolved_out"
|
||||||
|
check "...and the resolution itself writes nothing at all" 1 "" test -s "$BOARD/edits"
|
||||||
|
board_assemble_keep 253 284
|
||||||
|
recreated_out="$(board_run)"
|
||||||
|
check "an unchanged state recreated is silent — D4's stated boundary" 1 "" \
|
||||||
|
grep -qF 'issueflow: #284: collision flag' <<<"$recreated_out"
|
||||||
|
|
||||||
|
# -- today's board draws nothing (the post-ruling shape, live) --------------
|
||||||
|
# #249 the `blocked` sink, this issue `claimed` with no open PR and a gate
|
||||||
|
# member, #307 and #311 `blocked`. The `claimed` member is the case D3b would
|
||||||
|
# flag if membership were read wrong, which is why it is here.
|
||||||
|
printf '%s\n' \
|
||||||
|
'{"data":{"repository":{"pullRequests":{"nodes":[],"pageInfo":{"hasNextPage":false,"endCursor":null}}}}}' \
|
||||||
|
>"$BOARD/graphql-open.json"
|
||||||
|
board_issue 249 blocked,release 'Release 0.6.0 — the board empties into the tag' \
|
||||||
|
'Blocked by #293, #307.'
|
||||||
|
board_issue 293 claimed 'issueflow-reconcile — the sweep flags what the window and collision rules forbid' '' 1
|
||||||
|
board_issue 307 blocked 'test/issueflow-reconcile.test.sh — the ruling pre-read is unpinned' \
|
||||||
|
'Blocked by #293.'
|
||||||
|
board_issue 311 blocked 'docs/CONSUMERS.md — a deliberate non-member' 'Blocked by #249.'
|
||||||
|
board_assemble 249 293 307 311
|
||||||
|
today_out="$(board_run)"
|
||||||
|
check "today's board draws no collision flag" 1 "" grep -qF ': collision flag' <<<"$today_out"
|
||||||
|
check "...and no window flag: the claimed member is a member" 1 "" \
|
||||||
|
grep -qF ': window flag' <<<"$today_out"
|
||||||
|
check "...and still reports a whole pass" 0 'issueflow: reconciled.' \
|
||||||
|
printf '%s\n' "$today_out"
|
||||||
|
|
||||||
# -- the invariant is enforced at the source, not remembered ----------------
|
# -- the invariant is enforced at the source, not remembered ----------------
|
||||||
# Staging only holds while every mutation goes through run(). A future call
|
# Staging only holds while every mutation goes through run(). A future call
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue