From a10518304946d75f9a2e55b86f38f056cb9c6f56 Mon Sep 17 00:00:00 2001 From: chenghongze Date: Thu, 20 Aug 2026 09:39:50 +0800 Subject: [PATCH 1/2] fix(ci): grant the merge-approval gate pull-requests: write + fail-soft report comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gate's standing impact-report comment 403s with 'Resource not accessible by integration' although issues: write is declared: for the Actions token, the issue-comments endpoint on a pull request is governed by the pull-requests scope (the same reason actions/stale documents both scopes for PR comments). statuses: write postings are unaffected, which is why only the comment upsert failed. Also make the report comment upsert fail-soft: the comment is an advisory review aid — its delivery failure must never block the verdict status. Fail-closed covers the verdict, not the review aid. A wiring test pins the try/catch. --- .github/workflows/merge-approval-gate.yml | 6 ++++-- scripts/run-merge-approval-gate.js | 16 ++++++++++++++-- tests/unit/tooling/mergeApprovalGate.test.js | 2 ++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/.github/workflows/merge-approval-gate.yml b/.github/workflows/merge-approval-gate.yml index cb5b30bf..a190970f 100644 --- a/.github/workflows/merge-approval-gate.yml +++ b/.github/workflows/merge-approval-gate.yml @@ -16,9 +16,11 @@ permissions: contents: read # issues: write lets the gate create/update the standing architecture # impact report comment (PR 4/6: the report replaces the AI-authored - # change-impact declaration block). + # change-impact declaration block). pull-requests: write is required as + # well: the issue-comments endpoint on a pull request is governed by the + # pull-requests scope for the Actions token (issues: write alone 403s). issues: write - pull-requests: read + pull-requests: write statuses: write jobs: diff --git a/scripts/run-merge-approval-gate.js b/scripts/run-merge-approval-gate.js index c0148876..dad00a1c 100644 --- a/scripts/run-merge-approval-gate.js +++ b/scripts/run-merge-approval-gate.js @@ -7,7 +7,9 @@ // read as data (round-2 review Blocker 1 — a PR must not approve itself by // editing the gate). Fail-closed: unexpected errors exit non-zero without // posting, so a broken gate blocks merges loudly instead of silently opening -// them. +// them. The one exception is the advisory impact-report comment: its +// delivery failure is logged and the verdict status is still posted +// (fail-closed covers the verdict, not the review aid). // // Harness Simplification PR 4/6: the AI-authored change-impact declaration // is gone. The gate regenerates the architecture impact report for the exact @@ -193,8 +195,18 @@ async function main() { prNumber, architectureApproved: Boolean(architectureApproval), }); + // The report comment is advisory: its delivery failure must never block + // the merge status (the evaluation completed; fail-closed applies to the + // verdict, not to the review aid). A broken comment path would otherwise + // take down the gate for every PR — as it did when the token lacked + // pull-requests: write. if (reportMarkdown) { - await upsertReportComment(token, repo, prNumber, reportMarkdown, comments); + try { + await upsertReportComment(token, repo, prNumber, reportMarkdown, comments); + } catch (error) { + console.error(`warning: could not post the impact report comment: ${error instanceof Error ? error.message : String(error)}`); + console.error(reportMarkdown); + } } const reasons = []; diff --git a/tests/unit/tooling/mergeApprovalGate.test.js b/tests/unit/tooling/mergeApprovalGate.test.js index a7b885f9..388b9b7d 100644 --- a/tests/unit/tooling/mergeApprovalGate.test.js +++ b/tests/unit/tooling/mergeApprovalGate.test.js @@ -98,6 +98,8 @@ test('ARCH-PR-MERGE-APPROVAL-GATE-001 posts a read-only impact report instead of 'the gate regenerates the impact report for the PR head'); assert.match(script, /upsertReportComment/, 'the gate posts the report as a standing PR comment for owner review'); + assert.match(script, /try \{[\s\S]*?upsertReportComment[\s\S]*?catch/, + 'the advisory report comment is fail-soft: its delivery failure must not block the verdict status'); assert.match(script, /architecture-impact-report/, 'the standing comment carries a stable marker for upserts'); assert.match(script, /pull\/\$\{prNumber\}\/head/, From 9d8b0e6ab57351b0f48848849be5f518ec8917e9 Mon Sep 17 00:00:00 2001 From: chenghongze Date: Thu, 20 Aug 2026 09:40:16 +0800 Subject: [PATCH 2/2] docs: record gate comment permission capability audit Skill-Harvest: none --- docs/testing/main-capability-coverage.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/testing/main-capability-coverage.json b/docs/testing/main-capability-coverage.json index 996f8254..69fb9e5d 100644 --- a/docs/testing/main-capability-coverage.json +++ b/docs/testing/main-capability-coverage.json @@ -2,7 +2,7 @@ "version": 1, "audit": { "base": "2b34c653119bdf480f2af0330ee3809b51441807", - "head": "7001f89501a6891f83796de732f0b0cf7f25f722", + "head": "a10518304946d75f9a2e55b86f38f056cb9c6f56", "ignoredDocumentationCommits": [ "dd86a325e3db5aa013ffa18a647e67e9fa279c10", "0b0e12b4d97ec7d77a8a93076459fdaad2e52070", @@ -480,7 +480,8 @@ "6cc7fca077b4929411d6dbaede79e8e560346ece", "2cc5b47de6e8fe51b4998293b0dc224ea908fdf3", "37027f5c598f916c0656343248fb059be7ab8daf", - "6af42109faaf686b8d6d8a1f107b243bb22a745a" + "6af42109faaf686b8d6d8a1f107b243bb22a745a", + "cd0dda6cf6fbd015822f343eec5f7faa7e4b52c8" ] }, "capabilities": [ @@ -1078,7 +1079,8 @@ "ee64dfaeb58eb3578a780d787af5cd77403b51c2", "e0dbdfc03bc83c2bbebb278dde6efdb350c71c06", "47312e9a48be6aacebd147e4cb717727e49c0b2e", - "769e02650fbfbfce96a47e3a3798a8ab93e9b8ad" + "769e02650fbfbfce96a47e3a3798a8ab93e9b8ad", + "a10518304946d75f9a2e55b86f38f056cb9c6f56" ], "behaviors": [ "ARCH-CI-QUALITY-GATE-001",