From 7e721d88c5246cfaac046a29d9f8585d1cc74594 Mon Sep 17 00:00:00 2001 From: ArchonVII Date: Tue, 9 Jun 2026 17:24:27 -0500 Subject: [PATCH] fix(required-gate): ignore unrelated label changes Remove broad labeled and unlabeled pull_request triggers from the required-gate caller example so labels like no-changelog do not restart the stable branch-protection gate. Document the ci:full rerun tradeoff and add a regression assertion.\n\nCloses #57 --- .../unreleased/57-required-gate-label-triggers.md | 3 +++ README.md | 6 ++++++ examples/repo-required-gate.yml | 13 +++---------- scripts/workflow-structure.test.mjs | 11 +++++++++++ 4 files changed, 23 insertions(+), 10 deletions(-) create mode 100644 .changelog/unreleased/57-required-gate-label-triggers.md diff --git a/.changelog/unreleased/57-required-gate-label-triggers.md b/.changelog/unreleased/57-required-gate-label-triggers.md new file mode 100644 index 0000000..2e8e08c --- /dev/null +++ b/.changelog/unreleased/57-required-gate-label-triggers.md @@ -0,0 +1,3 @@ +### Fixed + +- Stop the required-gate caller example from rerunning on arbitrary PR label changes. The `ci:full` classifier label still works when the gate runs, but labels such as `no-changelog` no longer restart the branch-protection gate. diff --git a/README.md b/README.md index 392af3e..515530c 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,12 @@ repo-required-gate / decision Keep targeted checks inside the gate or leave them non-required. Do not make branch protection depend on workflows that can be skipped by path filters; GitHub can leave those required checks pending. +The required-gate caller intentionally does not listen for `labeled` or +`unlabeled` events. Labels such as `no-changelog` should not cancel and restart +the required branch-protection gate. The `ci:full` label is still honored by the +classifier when the gate runs; after applying it to an already-open PR, rerun the +workflow, push a new commit, or use another normal PR event to refresh the gate. + For agent closeout, use the shared local preflight instead of direct promotion: ```bash diff --git a/examples/repo-required-gate.yml b/examples/repo-required-gate.yml index 16cdf71..e12edcb 100644 --- a/examples/repo-required-gate.yml +++ b/examples/repo-required-gate.yml @@ -7,16 +7,7 @@ name: Repo Required Gate on: pull_request: branches: [main] - types: - [ - opened, - edited, - synchronize, - reopened, - ready_for_review, - labeled, - unlabeled, - ] + types: [opened, edited, synchronize, reopened, ready_for_review] merge_group: permissions: @@ -115,5 +106,7 @@ jobs: # Default label is `ci:full`. Apply it to a PR to bypass path-based # routing and run the full language CI surface — e.g. a workflow-only # change that semantically affects CI. + # If you add the label after the latest gate run, rerun the workflow, + # push a new commit, or use another normal PR event to refresh the gate. # # force-full-ci-label: ci:full diff --git a/scripts/workflow-structure.test.mjs b/scripts/workflow-structure.test.mjs index 4bf1470..6844011 100644 --- a/scripts/workflow-structure.test.mjs +++ b/scripts/workflow-structure.test.mjs @@ -2,6 +2,7 @@ import { readFileSync } from 'node:fs'; import { describe, expect, it } from 'vitest'; const readWorkflow = (name) => readFileSync(`.github/workflows/${name}.yml`, 'utf8'); +const readExample = (name) => readFileSync(`examples/${name}.yml`, 'utf8'); const workflowJobBlock = (body, job) => { const marker = ` ${job}:`; @@ -113,6 +114,16 @@ describe('repo-required-gate workflow node delegation', () => { }); }); +describe('repo-required-gate caller example', () => { + it('does not rerun the required gate for arbitrary label changes', () => { + const body = readExample('repo-required-gate'); + + expect(body).toContain('types: [opened, edited, synchronize, reopened, ready_for_review]'); + expect(body).not.toMatch(/^\s*labeled,?\s*$/m); + expect(body).not.toMatch(/^\s*unlabeled,?\s*$/m); + }); +}); + describe('pr-policy workflow contract source', () => { it('uses the shared PR contract validator instead of inline body regexes', () => { const body = readWorkflow('pr-policy');