From 1532f22eb7df1963327525a4bb685a8ea4f71dc9 Mon Sep 17 00:00:00 2001 From: codex-bot-andresmgsl <304681515+codex-bot-andresmgsl@users.noreply.github.com> Date: Thu, 23 Jul 2026 00:42:24 +0000 Subject: [PATCH] fix: parse live issue dependency shapes --- .../issueflow-reconcile.sh | 9 +++++++-- test/issueflow-reconcile.test.sh | 19 +++++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/actions/issueflow-reconcile/issueflow-reconcile.sh b/actions/issueflow-reconcile/issueflow-reconcile.sh index 0dbdc49..d1431bd 100644 --- a/actions/issueflow-reconcile/issueflow-reconcile.sh +++ b/actions/issueflow-reconcile/issueflow-reconcile.sh @@ -85,7 +85,8 @@ claim_decision() { # $1 assignee count, $2 linked open PR, $3 age seconds } blocked_references() { # body on stdin -> issue numbers, one per line - sed -nE 's/^[[:space:]]*Blocked by[[:space:]]+//Ip' \ + sed -nE 's/.*Blocked by[[:space:]]+//Ip' \ + | sed -E 's/[.;][[:space:]].*$//' \ | { grep -Eo '#[0-9]+' || true; } | tr -d '#' | sort -nu } @@ -99,7 +100,11 @@ blocked_decision() { # $1 refs, $2 OPEN/CLOSED states } epic_references() { # markdown task-list issue references from body on stdin - sed -nE '/^[[:space:]]*[-*][[:space:]]+\[[ xX]\]/p' \ + awk ' + tolower($0) ~ /^##[[:space:]]+task list[[:space:]]*$/ { in_list = 1; next } + in_list && /^#/ { exit } + in_list && /^[[:space:]]*[-*][[:space:]]+\[[ xX]\]/ { print } + ' \ | { grep -Eo '#[0-9]+' || true; } | tr -d '#' | sort -nu } diff --git a/test/issueflow-reconcile.test.sh b/test/issueflow-reconcile.test.sh index 3817c68..186a80e 100644 --- a/test/issueflow-reconcile.test.sh +++ b/test/issueflow-reconcile.test.sh @@ -37,8 +37,20 @@ check "quiet claim without PR is reclaimed" 0 "RECLAIM" claim_decision 1 false $ check "quiet unassigned claim is also reclaimed" 0 "RECLAIM" claim_decision 0 false $((STALE_AFTER + 1)) # Invariant 3: blocked declarations parse and release only when all close. -refs="$(blocked_references <<< $'Context #99\nBlocked by #12 and #7')" +refs="$(blocked_references <<< $'Context #99. Blocked by #12 (first), #7 (second). Blocks #44.')" check "blocked declaration extracts only declared refs" 0 $'7\n12' printf '%s\n' "$refs" +body="Part of #1. Blocked by #11 (needs a ceremony tag to pin), #12 (must be executed from the guide), #19 (the conversion vendors the doctrine). Blocks #14, #15 (they inherit the pilot's lessons)." +check "real issue 13 inline blockers parse" 0 "" test \ + "$(blocked_references <<<"$body")" = $'11\n12\n19' +body="Part of #1. Blocked by #13 (inherits the pilot's lessons). Can run in parallel with #15." +check "real issue 14 inline blocker parses" 0 "" test \ + "$(blocked_references <<<"$body")" = "13" +body="Part of #1. Blocked by #13 (pilot lessons). Can run in parallel with #14." +check "real issue 15 inline blocker parses" 0 "" test \ + "$(blocked_references <<<"$body")" = "13" +body="Part of #1. Blocked by #11, #12 (needs a released ceremony + the bootstrap guide); benefits from #13's lessons but does not need #14/#15." +check "real issue 16 inline blockers parse" 0 "" test \ + "$(blocked_references <<<"$body")" = $'11\n12' check "open blocker keeps issue blocked" 0 "KEEP" blocked_decision "$refs" $'CLOSED\nOPEN' check "all closed blockers release issue" 0 "READY" blocked_decision "$refs" $'CLOSED\nCLOSED' check "missing blocked declaration is flagged" 0 "FLAG_UNPARSEABLE" blocked_decision "" "" @@ -51,8 +63,11 @@ check "outside author already marked needs-triage is stable" 0 "KEEP" author_dec check "later sweep accepts a normalized outside-authored issue" 0 "KEEP" queue_decision <<<"ready" # Invariant 5: completed epics get one nudge; incomplete/unparseable do not. -epic_refs="$(epic_references <<< $'- [ ] #3 first\n- [x] #2 done\nplain #8')" +epic_refs="$(epic_references <<< $'## Definition of done\n- [ ] outside #8\n\n## Task list\n- [ ] #3 first\n- [x] #2 done\nplain #9')" check "epic parser reads task-list refs only" 0 $'2\n3' printf '%s\n' "$epic_refs" +body=$'## Task list\n- [x] #2 done\n- [x] #3 done\n\n## Definition of done\n- [ ] open issue #99 must not suppress the nudge' +check "epic parser stops before later checkbox sections" 0 "" test \ + "$(epic_references <<<"$body")" = $'2\n3' check "completed epic is nudged" 0 "NUDGE" epic_decision "$epic_refs" $'CLOSED\nCLOSED' check "open epic child suppresses nudge" 0 "KEEP" epic_decision "$epic_refs" $'CLOSED\nOPEN' check "epic without parseable children is stable" 0 "KEEP" epic_decision "" ""