From 1d15ab60a3fd574ffdb9b0fe7c625735e26d7e03 Mon Sep 17 00:00:00 2001 From: "pulse-triage[bot]" <249995291+pulse-triage[bot]@users.noreply.github.com> Date: Fri, 28 Aug 2026 16:25:05 +0100 Subject: [PATCH] Defer generic retest guidance to maintainers --- .github/scripts/issue-version-triage.cjs | 24 +++++++++++-- .github/scripts/issue-version-triage.test.cjs | 34 +++++++++++++++++++ .github/workflows/README.md | 2 +- 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/.github/scripts/issue-version-triage.cjs b/.github/scripts/issue-version-triage.cjs index 71f2a50cce..95056207af 100644 --- a/.github/scripts/issue-version-triage.cjs +++ b/.github/scripts/issue-version-triage.cjs @@ -127,16 +127,27 @@ async function ensureLabel(github, context, name, color, description) { } } -async function hasRetestComment(github, context, issueNumber) { - const comments = await github.paginate(github.rest.issues.listComments, { +async function getIssueComments(github, context, issueNumber) { + return github.paginate(github.rest.issues.listComments, { owner: context.repo.owner, repo: context.repo.repo, issue_number: issueNumber, per_page: 100, }); +} + +function hasRetestComment(comments) { return comments.some((comment) => (comment.body || "").includes(RETEST_COMMENT_MARKER)); } +function hasMaintainerResponse(comments) { + return comments.some((comment) => + MAINTAINER_AUTHOR_ASSOCIATIONS.has( + String(comment.author_association || "").toUpperCase() + ) + ); +} + async function getLatestStableVersion(github, context, core) { try { const latest = await github.rest.repos.getLatestRelease({ @@ -324,10 +335,17 @@ async function postRetestCommentForIssue({ core.info("Issue is already on the latest stable core or newer. Skipping public retest guidance."); return false; } - if (await hasRetestComment(github, context, issue.number)) { + const comments = await getIssueComments(github, context, issue.number); + if (hasRetestComment(comments)) { core.info("Retest guidance comment already exists."); return false; } + if (hasMaintainerResponse(comments)) { + core.info( + "A maintainer has already responded. Skipping generic retest guidance." + ); + return false; + } await github.rest.issues.createComment({ owner: context.repo.owner, diff --git a/.github/scripts/issue-version-triage.test.cjs b/.github/scripts/issue-version-triage.test.cjs index 4cebda6201..642f04edca 100644 --- a/.github/scripts/issue-version-triage.test.cjs +++ b/.github/scripts/issue-version-triage.test.cjs @@ -217,6 +217,40 @@ test("scheduled retest guidance posts once after the grace window", async () => ); }); +test("scheduled retest guidance defers to an existing maintainer response", async () => { + const issues = [ + { + number: 1790, + title: "TrueNAS SMART evidence is hidden", + body: "## Feedback type\nBug / regression\n\n## Pulse version\n6.3.1\n", + labels: [{ name: "bug" }], + author_association: "NONE", + created_at: "2026-08-28T08:01:33Z", + }, + ]; + const existingComments = [ + { + body: "Fixed on main; wait for a release containing the fix before retesting.", + author_association: "OWNER", + }, + ]; + const { github, calls } = createGithub({ + latestVersion: "6.3.2", + existingComments, + issues, + }); + + const result = await triage.postEligibleRetestComments({ + github, + context: createContext({ issue: null }), + core: createCore(), + nowMs: Date.parse("2026-08-28T08:10:00Z"), + }); + + assert.deepEqual(result, { eligibleCount: 1, postedCount: 0 }); + assert.equal(calls.createComment.length, 0); +}); + test("timeout close comments use the canonical triage footer", () => { const { buildTimeoutCloseCommentBody, CLOSE_COMMENT_MARKER, TRIAGE_FOOTER } = triage.internals; diff --git a/.github/workflows/README.md b/.github/workflows/README.md index cdbf01b14e..ff24932514 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -9,7 +9,7 @@ Issue intake is split deliberately: - `issue-version-label-sync.yml` is the silent metadata path. It runs on `opened`, `edited`, and `reopened` issue events so version labels, `needs-version-info`, and `needs-retest-on-latest` stay correct when maintainers tidy issue metadata. -- `issue-version-retest-comment.yml` is the public guidance path. It only runs on `opened` and `reopened`, and only posts reporter-facing retest guidance when the issue is an older-version bug report from a non-maintainer. +- `issue-version-retest-comment.yml` is the scheduled public guidance path. It gives maintainers a grace window, then posts reporter-facing retest guidance only when an older-version bug report from a non-maintainer has no existing maintainer response. This prevents generic stable-version advice from contradicting a specific maintainer fix or prerelease boundary. - Both workflows load the shared helper at `.github/scripts/issue-version-triage.cjs` so parsing and classification logic lives in one place instead of drifting across duplicated inline scripts. ## Update Demo Server