From a9b3f4d766fcaa244c36ebc30828fbf67642a466 Mon Sep 17 00:00:00 2001 From: Andriujose <43181885+andriujoseba@users.noreply.github.com> Date: Mon, 3 Aug 2026 19:53:25 +0000 Subject: [PATCH] test: cover attention target diagnostics --- LABELS.md | 8 +-- changelog.d/232.md | 5 ++ lib/attention.sh | 4 +- test/attention.test.sh | 47 +++++++++++++++++ test/issueflow-reconcile.test.sh | 87 ++++++++++++++++++++++++++++++++ test/labels-reconcile.test.sh | 75 ++++++++++++++++++++++++--- 6 files changed, 214 insertions(+), 12 deletions(-) create mode 100644 changelog.d/232.md create mode 100644 test/attention.test.sh diff --git a/LABELS.md b/LABELS.md index af7ccf3..0f6acd4 100644 --- a/LABELS.md +++ b/LABELS.md @@ -175,9 +175,11 @@ take. It is hand-set: the machine never sets `attention`, never assigns anyone to receive one, and never decides that one has been answered — the assignee's removal is the only ack. It writes the label in exactly one place, the derived `claimed` → `post-merge` transition below, and nowhere -else; where it reads the flag it reads it to diagnose, and a diagnosis is a -comment that leaves the label alone. No reconciler enforces the assignee -requirement either: an unassigned flag may be reported, never repaired. +else; where it reads the flag it reads it to diagnose. The PR sweep comments +when `attention` is put on a pull request, and the issue sweep comments when +it is put on an issue with no assignee. Both diagnoses leave the label and +assignees alone; the machine never infers the claim issue, decides that the +demand was answered, or repairs either malformed shape. An `attention` issue without an assignee is therefore a board bug, not a demand; anyone may assign it or remove the flag. It never composes with `post-merge`, whose released claim has no assignee to answer the demand. The diff --git a/changelog.d/232.md b/changelog.d/232.md new file mode 100644 index 0000000..6785136 --- /dev/null +++ b/changelog.d/232.md @@ -0,0 +1,5 @@ +### Added + +- The label and issue-flow sweeps now comment once per episode when + `attention` targets a pull request or an unassigned issue, without + retargeting the demand or changing labels or assignees (#232). diff --git a/lib/attention.sh b/lib/attention.sh index e2e2e6a..736493a 100644 --- a/lib/attention.sh +++ b/lib/attention.sh @@ -85,12 +85,12 @@ reconcile_attention() { # $1 item, $2 pr|issue, $3 assignees, $4 suppression This pull request carries \`attention\`, but that label is issue-only. Put the label on the assigned issue that owns the claim. The sweep cannot infer which issue that is, so it reports the malformed target without removing or -retargeting the label (heavy-duty/ceremony#232)." ;; +retargeting the label." ;; MALFORMED_UNASSIGNED) body="$marker This issue carries \`attention\` but has no assignee to receive the demand. Assign the intended builder or remove the flag. The sweep reports the board -bug without assigning anyone or changing the label (heavy-duty/ceremony#232)." ;; +bug without assigning anyone or changing the label." ;; esac run gh issue comment "$n" -R "$REPO" --body "$body" >/dev/null log "#$n: malformed attention ($surface) — commented; no label or assignee changed" diff --git a/test/attention.test.sh b/test/attention.test.sh new file mode 100644 index 0000000..410c572 --- /dev/null +++ b/test/attention.test.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +set -u + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +# shellcheck source=test/harness.sh +source "$ROOT/test/harness.sh" +# shellcheck source=lib/attention.sh +source "$ROOT/lib/attention.sh" + +check "attention on an unassigned PR is malformed" 0 "MALFORMED_PR" \ + attention_target_decision pr 0 +check "attention on an assigned PR is still malformed" 0 "MALFORMED_PR" \ + attention_target_decision pr 1 +check "attention on an unassigned issue is malformed" 0 "MALFORMED_UNASSIGNED" \ + attention_target_decision issue 0 +check "attention on an assigned issue is healthy" 0 "KEEP" \ + attention_target_decision issue 1 +check "an unknown surface is rejected" 2 "" attention_target_decision discussion 0 + +check "a malformed PR target is commented" 0 "POST" \ + attention_comment_decision MALFORMED_PR "" +check "an unassigned issue target is commented without precedence" 0 "POST" \ + attention_comment_decision MALFORMED_UNASSIGNED "" +check "claimed-unassigned precedence suppresses the second comment" 0 "SUPPRESS" \ + attention_comment_decision MALFORMED_UNASSIGNED claimed-unassigned +check "post-merge-assigned precedence suppresses the second comment" 0 "SUPPRESS" \ + attention_comment_decision MALFORMED_UNASSIGNED post-merge-assigned +check "a healthy target stays silent" 0 "KEEP" \ + attention_comment_decision KEEP "" + +check "the newest labeled event defines the episode" 0 "2026-08-03T12:00:00Z" \ + attention_newest_flag <<'EOF' +2026-08-03T10:00:00Z +2026-08-03T12:00:00Z +2026-08-03T11:00:00Z +EOF +check "the marker names the label episode" 0 \ + '' \ + attention_episode_marker 2026-08-03T12:00:00Z + +# Diagnosis is the only write this library may own. Pin the absence of every +# label/assignee mutation spelling so a later refactor cannot quietly turn a +# report into a repair (#229 D2). +check "the attention library contains no issue/PR edit mutation" 1 "" \ + grep -E 'gh (issue|pr) edit|--(add|remove)-(label|assignee)' "$ROOT/lib/attention.sh" + +summary diff --git a/test/issueflow-reconcile.test.sh b/test/issueflow-reconcile.test.sh index e36adda..c89b986 100644 --- a/test/issueflow-reconcile.test.sh +++ b/test/issueflow-reconcile.test.sh @@ -393,6 +393,93 @@ 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"; } +# -- malformed attention targets are diagnosed, never repaired (#232) ------- +attention_episode() { # $1 issue, $2 labeled timestamp + jq -n --arg at "$2" \ + '[{"event":"labeled","label":{"name":"attention"},"actor":{"login":"setter"},"created_at":$at}]' \ + >"$(tfix "$1")" + printf '[]\n' >"$(cfix "$1")" +} + +: >"$TMP/issue-edits" +attention_edits_before="$(wc -l <"$TMP/issue-edits")" +for n in 61 62 63; do + attention_episode "$n" "$(iso_at $((INOW - 60)))" +done +# The assignment event is the claim clock's own activity fact. The live issue +# has since lost its assignee, which is exactly the claimed-unassigned shape. +jq --arg at "$(iso_at $((INOW - 60)))" \ + '. + [{"event":"assigned","created_at":$at}]' \ + "$(tfix 63)" >"$(tfix 63).tmp" && mv "$(tfix 63).tmp" "$(tfix 63)" +printf '{"state":"open"}\n' >"$TMP/repos_owner_repo_issues_999.json" + +issue_probe 61 $'ready\nattention' 0 >/dev/null +check "unassigned attention under ready is diagnosed once" 0 "1" \ + grep -cF '' "$TMP/posted-63" +check "the suppressed attention detection remains in the log" 0 "1" \ + grep -cF 'comment suppressed by claimed-unassigned precedence' <<<"$claimed_attention" + +attention_episode 64 "$(iso_at $((INOW - 60)))" +issue_probe 64 $'ready\nattention' 1 >/dev/null +check "assigned attention under ready is healthy" 1 "" test -f "$TMP/posted-64" +attention_episode 65 "$(iso_at $((INOW - 60)))" +issue_probe 65 $'claimed\nattention' 1 true >/dev/null +check "assigned attention under claimed is healthy" 1 "" test -f "$TMP/posted-65" +attention_episode 66 "$(iso_at $((INOW - 60)))" +issue_probe 66 $'blocked\nattention' 1 false "" 'Blocked by #999' >/dev/null +check "assigned attention under blocked is healthy" 1 "" test -f "$TMP/posted-66" + +attention_episode 67 "$(iso_at $((INOW - 60)))" +post_merge_attention="$(issue_probe 67 $'post-merge\nattention' 0)" +check "post-merge precedence leaves exactly its existing comment" 0 "1" \ + grep -c -- '^----$' "$TMP/posted-67" +check "post-merge's existing diagnostic wins" 0 "1" \ + grep -cF '' "$TMP/posted-67" +check "the post-merge suppression remains in the log" 0 "1" \ + grep -cF 'comment suppressed by post-merge-assigned precedence' <<<"$post_merge_attention" + +attention_episode 68 "$(iso_at $((INOW - 120)))" +issue_probe 68 $'ready\nattention' 0 >/dev/null +issue_probe 68 $'ready\nattention' 0 >/dev/null +check "two sweeps in one malformed episode post once" 0 "1" \ + grep -cF '