issueflow-reconcile — the board discriminator is .pull_request == null, not has(), or this forge has no issues (#210) #211

Merged
andres merged 4 commits from build/210-discriminator into main 2026-08-05 15:49:31 +00:00
Showing only changes of commit 087ea4a24b - Show all commits

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