test(issueflow): isolate the scalar guard — the list row admits, the payload stands down (#210)
All checks were successful
CI / test (pull_request) Successful in 3m10s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 6s
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 found the subtlety my board fixture could not reach:
BOARD_RECORDS filters an object-valued row out of the LIST before the per-issue
guard ever sees it, so no board fixture alone can prove the scalar stand-down.
My #64 case proved the gather excluded it, not that reconcile_issue_pass did.

The fixture that isolates the site is a deliberate mismatch: the LIST row is
null-valued, so the board gather admits #65 — and the INDIVIDUAL payload the
sweep then fetches is object-valued. Only reconcile_issue_pass's own guard can
stand that down.

Three rows over the same number, so the guard cannot pass by standing
everything down or by admitting everything:

  payload object-valued   -> NOT reconciled
  payload null-valued     -> reconciled
  payload key absent      -> reconciled (the GitHub shape)

Mutating ONLY the scalar predicate now reds three BEHAVIOURAL rows plus the
pin, where before it red only the pin and a neighbour. pass_disc is gone: it
repeated the predicate inside the test helper and never called production —
which is the same isolated-expression trap, one layer down, in the fix for it.

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

Refs #210
This commit is contained in:
cluade-reviewer-andresmgsl 2026-08-05 14:49:01 +00:00
parent 087ea4a24b
commit 5b78d29201

View file

@ -1852,23 +1852,44 @@ 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.
# 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.
# THE SCALAR SITE, ISOLATED. `BOARD_RECORDS` filters an object-valued row out
# of the LIST before the per-issue guard ever sees it, so a board fixture alone
# cannot prove the scalar stand-down (@codex-reviewer-andresmgsl, #210 review).
#
# The fixture that isolates it: the LIST row is null-valued, so the board
# gather admits it — and the INDIVIDUAL payload the sweep then fetches is
# object-valued. Only reconcile_issue_pass's own guard can stand that down.
printf '%s\n' \
'[{"number":64,"pull_request":{"merged":false},"labels":[],"title":"a PR wearing an issue row"}]' \
'[{"number":65,"pull_request":null,"labels":[],"title":"list says issue, payload says PR"}]' \
>"$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:"",
'{number:65,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"
>"$FORGEJO_BOARD/repos_owner_repo_issues_65.json"
printf '[]\n' >"$FORGEJO_BOARD/repos_owner_repo_issues_65_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"
check "the per-issue guard stands down an object-valued payload" 1 "" \
grep -qE '^issueflow: #65: needs-triage' <<<"$fjb3_out"
check "...and the sweep still completes" 0 "" \
grep -qF 'issueflow: reconciled.' <<<"$fjb3_out"
# The same fixture with a null-valued payload MUST reconcile — otherwise the row
# above would pass on a guard that stands everything down.
jq -n --arg at "$(iso_at "$INOW")" \
'{number:65,user:{login:"triage-one"},created_at:$at,body:"",
pull_request:null,labels:[],assignees:[]}' \
>"$FORGEJO_BOARD/repos_owner_repo_issues_65.json"
fjb4_out="$(forgejo_board_run)"
check "...while a null-valued payload at the same site reconciles" 0 "" \
grep -qE '^issueflow: #65: needs-triage' <<<"$fjb4_out"
# And the GitHub shape — key absent entirely — is still an issue.
jq -n --arg at "$(iso_at "$INOW")" \
'{number:65,user:{login:"triage-one"},created_at:$at,body:"",labels:[],assignees:[]}' \
>"$FORGEJO_BOARD/repos_owner_repo_issues_65.json"
fjb5_out="$(forgejo_board_run)"
check "...and a github-shaped payload (key absent) reconciles too" 0 "" \
grep -qE '^issueflow: #65: needs-triage' <<<"$fjb5_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