feat: announce release initialization

This commit is contained in:
Andriujose 2026-08-04 10:48:55 +00:00
parent 80b9eaf6e7
commit e3b4b2cdfd
3 changed files with 84 additions and 0 deletions

View file

@ -746,6 +746,24 @@ itself, so a parse unchanged since the last echo never re-posts.*" >/dev/null
log "#$n: blockers closed -> ready" ;;
esac
elif has_issue_label epic; then
if has_issue_label release; then
refs="$(blocked_references <<<"$(jq -r '.body // ""' <<<"$ISSUE_JSON")")"
cross_refs="$(blocked_cross_references <<<"$(jq -r '.body // ""' <<<"$ISSUE_JSON")")"
states="$(reference_states <<<"$refs")"
if [ "$(blocked_decision "$refs" "$states" "$cross_refs")" = READY ]; then
ensure_comment "$n" release-init-due \
"This release epic's declared gate is open. Release initialization is due:
1. Mint the window's members.
2. Graph hard dependencies and same-file clusters.
3. Write ordered waves and the progress task list.
4. Ask the operator to bless the order, then open the first wave.
5. Ship the release, close this epic, and trigger the next window.
See [\`RELEASES.md\`](https://github.com/$REPO/blob/main/RELEASES.md). The operator blessing the order is the one step this chain never automates."
log "#$n: release-init due"
fi
fi
refs="$(epic_references <<<"$(jq -r '.body // ""' <<<"$ISSUE_JSON")")"
states="$(reference_states <<<"$refs")"
if [ "$(epic_decision "$refs" "$states")" = NUDGE ]; then

3
changelog.d/253.md Normal file
View file

@ -0,0 +1,3 @@
### Added
- Release epics now announce release initialization when their declared dependency gates clear. (#253)

View file

@ -466,6 +466,69 @@ issue_probe() { # $1 issue, $2 labels, $3 assignees, $4 false|closing|refs, $5 m
tfix() { printf '%s/repos_owner_repo_issues_%s_timeline.json' "$TMP" "$1"; }
cfix() { printf '%s/repos_owner_repo_issues_%s_comments.json' "$TMP" "$1"; }
# -- release epics announce an opened declared gate, comment-only (#253) -----
printf '{"state":"closed"}\n' >"$TMP/repos_owner_repo_issues_201.json"
printf '{"state":"closed"}\n' >"$TMP/repos_owner_repo_issues_202.json"
printf '{"state":"open"}\n' >"$TMP/repos_owner_repo_issues_203.json"
printf '[]\n' >"$(cfix 53)"
: >"$TMP/issue-edits"
release_init_edits_before="$(wc -l <"$TMP/issue-edits")"
release_init_body=$'Blocked by #201, #202.\n\n## Task list\n- [ ] #203 later work'
release_init="$(issue_probe 53 $'epic\nrelease' 0 false "" "$release_init_body")"
check "a release epic with every declared blocker closed announces init" 0 "" \
grep -qF '<!-- issueflow:release-init-due -->' "$TMP/posted-53"
check "the init announce names all five steps" 0 "5" \
grep -cE '^[1-5]\. ' "$TMP/posted-53"
check "the init announce cites RELEASES.md" 0 "" \
grep -qF 'RELEASES.md' "$TMP/posted-53"
check "the init announce names the never-automated operator blessing" 0 "" \
grep -qF 'operator blessing the order is the one step this chain never automates' \
"$TMP/posted-53"
check "the opened release gate is logged" 0 "" \
grep -qF '#53: release-init due' <<<"$release_init"
issue_probe 53 $'epic\nrelease' 0 false "" "$release_init_body" >/dev/null
check "an unchanged opened gate announces only once" 0 "1" \
grep -cF '<!-- issueflow:release-init-due -->' "$TMP/posted-53"
printf '[]\n' >"$(cfix 54)"
issue_probe 54 $'epic\nrelease' 0 false "" \
$'Blocked by #203.\n\n## Task list\n- [x] #201 complete' >/dev/null
check "an open declared blocker suppresses init despite a complete task list" 1 "" \
grep -qF '<!-- issueflow:release-init-due -->' "$TMP/posted-54"
check "the independent epic-complete nudge still fires" 0 "" \
grep -qF '<!-- issueflow:epic-complete -->' "$TMP/posted-54"
printf '[]\n' >"$(cfix 55)"
issue_probe 55 $'epic\nrelease' 0 false "" \
$'Blocked by #201.\n\n## Task list\n- [x] #202 complete' >/dev/null
check "release-init and epic-complete can coexist" 0 "2" \
grep -c -- '^----$' "$TMP/posted-55"
check "the coexisting comments keep distinct markers" 0 "" \
bash -c 'grep -qF "<!-- issueflow:release-init-due -->" "$1" && grep -qF "<!-- issueflow:epic-complete -->" "$1"' \
_ "$TMP/posted-55"
for n in 56 57 58 59; do printf '[]\n' >"$(cfix "$n")"; done
issue_probe 56 $'epic\nrelease' 0 false "" 'No dependency declaration.' >/dev/null
check "a release epic without a Blocked by declaration stays silent" 1 "" \
test -f "$TMP/posted-56"
issue_probe 57 $'epic\nrelease' 0 false "" 'Blocked by heavy-duty/rig#9.' >/dev/null
check "a cross-repo release gate stays silent" 1 "" test -f "$TMP/posted-57"
issue_probe 58 $'epic\nrelease' 0 false "" 'Blocked by #204.' >/dev/null
check "an unreadable release gate stays silent" 1 "" test -f "$TMP/posted-58"
: >"$TMP/api-calls"
issue_probe 59 epic 0 false "" 'Blocked by #201.' >/dev/null
check "a plain epic does not parse or announce a release gate" 1 "" \
test -f "$TMP/posted-59"
check "a plain epic pays no Blocked by reference read" 1 "" \
grep -qF 'repos/owner/repo/issues/201' "$TMP/api-calls"
printf '[]\n' >"$(cfix 60)"
issue_probe 60 $'ready\nrelease' 0 false "" 'Blocked by #201.' >/dev/null
check "a non-epic release issue does not announce init" 1 "" \
test -f "$TMP/posted-60"
check "every release-init probe is comment-only" 0 "$release_init_edits_before" \
bash -c 'wc -l <"$1"' _ "$TMP/issue-edits"
# -- malformed attention targets are diagnosed, never repaired (#232) -------
attention_episode() { # $1 issue, $2 labeled timestamp
jq -n --arg at "$2" \