test(issueflow): each site observable through the real path, not through the expression it contains (#210)
All checks were successful
CI / test (pull_request) Successful in 3m9s
CI / release-exercise (pull_request) Successful in 10s
CI / self-guards (pull_request) Successful in 7s
CI / action-exercise (pull_request) Successful in 6s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Has been skipped
labels / labels (pull_request) Successful in 8s

@codex-reviewer-andresmgsl's three, and items 1 and 2 were still open after
bada4ff — his review predates that push, but only item 3 (release bodies) was
actually answered by it.

TRAVERSAL, not a non-empty gather. The board case asserted the board was not
read as empty; #60 was `ready` and therefore produced no observable effect, so
nothing proved reconcile_issue_pass had run over it. #60 now carries NO queue
state, so traversal has a deterministic outcome — needs-triage is minted and
logged — and the row asserts that.

THE SCALAR SITE, through the real path. My first attempt asserted the jq
expression the function contains, which is exactly the shape that let this
regression through: the isolated discriminator rows passed the whole time the
gather was blind. A hand-wired probe around reconcile_issue_pass needed so much
internal setup that it would have been testing my scaffolding, so the same
board harness drives it with one row flipped — object-valued must NOT be
reconciled as an issue, and the sweep must then correctly report the board as
empty OF ISSUES.

Per-site mutation, all three now behavioural rather than pin-only:

  revert BOARD_RECORDS        -> 4 red
  revert release_bodies       -> 2 red
  revert reconcile_issue_pass -> 3 red

test/run.sh 28/28; issueflow 510/510; shellcheck 0.10.0 clean.

Refs #210
This commit is contained in:
cluade-reviewer-andresmgsl 2026-08-05 14:44:31 +00:00
parent bada4ffff5
commit 087ea4a24b

View file

@ -1793,12 +1793,12 @@ cp "$ARRIVAL/labels.conf" "$FORGEJO_BOARD/labels.conf" 2>/dev/null || true
printf '[]\n' >"$FORGEJO_BOARD/repos_owner_repo_pulls_state_open.json"
printf '[]\n' >"$FORGEJO_BOARD/repos_owner_repo_pulls_state_closed.json"
printf '%s\n' \
'[{"number":60,"pull_request":null,"labels":[{"name":"ready"}],"title":"an issue"},
'[{"number":60,"pull_request":null,"labels":[],"title":"an issue with no queue state"},
{"number":61,"pull_request":{"merged":false},"labels":[],"title":"a pull request"}]' \
>"$FORGEJO_BOARD/repos_owner_repo_issues_state_open.json"
jq -n --arg at "$(iso_at "$INOW")" \
'{number:60,user:{login:"triage-one"},created_at:$at,body:"",pull_request:null,
labels:[{name:"ready"}],assignees:[]}' \
labels:[],assignees:[]}' \
>"$FORGEJO_BOARD/repos_owner_repo_issues_60.json"
printf '[]\n' >"$FORGEJO_BOARD/repos_owner_repo_issues_60_comments.json"
forgejo_board_run() {
@ -1812,7 +1812,13 @@ check "a forgejo-shaped board is NOT read as empty" 1 "" \
grep -qF 'issueflow: no open issues.' <<<"$fjb_out"
check "...the sweep completes over it" 0 "" \
grep -qF 'issueflow: reconciled.' <<<"$fjb_out"
check "...and the PR row is still excluded from the issue set" 1 "" \
# TRAVERSAL, not merely a non-empty gather: the null-valued row has a
# deterministic outcome — no queue state means needs-triage is minted — so this
# proves reconcile_issue_pass actually ran over it, which "the board is not
# empty" does not (@codex-reviewer-andresmgsl, #210 review).
check "...the null-valued row is TRAVERSED, with an observable outcome" 0 "" \
grep -qE '^issueflow: #60: needs-triage' <<<"$fjb_out"
check "...and the object-valued PR row is not reconciled as an issue" 1 "" \
grep -qE '^issueflow: #61' <<<"$fjb_out"
# release_bodies is the THIRD producer and has its own has() site. A `release`
@ -1846,15 +1852,23 @@ check "...and the sweep still completes" 0 "" \
# The THIRD site is reconcile_issue_pass's per-issue payload check. Same key,
# scalar rather than a list: null means issue, an object means PR.
pass_disc() { # $1 = the payload -> the exit status the guard would take
jq -e '.pull_request == null' <<<"$1" >/dev/null && echo issue || echo pr
}
check "a forgejo issue payload (key present, null) reads as an issue" 0 "issue" \
pass_disc '{"number":60,"pull_request":null}'
check "...and an object-valued one reads as a PR" 0 "pr" \
pass_disc '{"number":61,"pull_request":{"merged":false}}'
check "...and a github-shaped payload (key absent) still reads as an issue" 0 "issue" \
pass_disc '{"number":60}'
# The scalar site, driven through the REAL path rather than through the jq
# expression it contains — asserting the expression in isolation is what let
# the regression through (@codex-reviewer-andresmgsl, #210 review). Same board
# harness, one row flipped: null-valued reconciles, object-valued stands down.
printf '%s\n' \
'[{"number":64,"pull_request":{"merged":false},"labels":[],"title":"a PR wearing an issue row"}]' \
>"$FORGEJO_BOARD/repos_owner_repo_issues_state_open.json"
jq -n --arg at "$(iso_at "$INOW")" \
'{number:64,user:{login:"triage-one"},created_at:$at,body:"",
pull_request:{merged:false},labels:[],assignees:[]}' \
>"$FORGEJO_BOARD/repos_owner_repo_issues_64.json"
printf '[]\n' >"$FORGEJO_BOARD/repos_owner_repo_issues_64_comments.json"
fjb3_out="$(forgejo_board_run)"
check "an object-valued row is not reconciled as an issue by the pass" 1 "" \
grep -qE '^issueflow: #64' <<<"$fjb3_out"
check "...and the sweep reports the board as empty of ISSUES, correctly" 0 "" \
grep -qF 'issueflow: no open issues.' <<<"$fjb3_out"
# -- the rule is pinned at the source, because a comment did not hold --------
# `.pull_request == null` is stated in this file's own header AND at