Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/merge-approval-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
8 changes: 5 additions & 3 deletions docs/testing/main-capability-coverage.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"version": 1,
"audit": {
"base": "2b34c653119bdf480f2af0330ee3809b51441807",
"head": "7001f89501a6891f83796de732f0b0cf7f25f722",
"head": "a10518304946d75f9a2e55b86f38f056cb9c6f56",
"ignoredDocumentationCommits": [
"dd86a325e3db5aa013ffa18a647e67e9fa279c10",
"0b0e12b4d97ec7d77a8a93076459fdaad2e52070",
Expand Down Expand Up @@ -480,7 +480,8 @@
"6cc7fca077b4929411d6dbaede79e8e560346ece",
"2cc5b47de6e8fe51b4998293b0dc224ea908fdf3",
"37027f5c598f916c0656343248fb059be7ab8daf",
"6af42109faaf686b8d6d8a1f107b243bb22a745a"
"6af42109faaf686b8d6d8a1f107b243bb22a745a",
"cd0dda6cf6fbd015822f343eec5f7faa7e4b52c8"
]
},
"capabilities": [
Expand Down Expand Up @@ -1078,7 +1079,8 @@
"ee64dfaeb58eb3578a780d787af5cd77403b51c2",
"e0dbdfc03bc83c2bbebb278dde6efdb350c71c06",
"47312e9a48be6aacebd147e4cb717727e49c0b2e",
"769e02650fbfbfce96a47e3a3798a8ab93e9b8ad"
"769e02650fbfbfce96a47e3a3798a8ab93e9b8ad",
"a10518304946d75f9a2e55b86f38f056cb9c6f56"
],
"behaviors": [
"ARCH-CI-QUALITY-GATE-001",
Expand Down
16 changes: 14 additions & 2 deletions scripts/run-merge-approval-gate.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 = [];
Expand Down
2 changes: 2 additions & 0 deletions tests/unit/tooling/mergeApprovalGate.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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/,
Expand Down
Loading