From 8c8e638ca97fe151b20888a4c2a019b4c5bdb9a7 Mon Sep 17 00:00:00 2001 From: Matt Lydon <489306+Mpfk@users.noreply.github.com> Date: Wed, 13 May 2026 12:05:51 -0700 Subject: [PATCH 1/3] test(workflow-policy): add RED test for main-branch PR source guard [RED] Closes #73 --- tests/workflow_policy.bats | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/workflow_policy.bats b/tests/workflow_policy.bats index c595d5c..55945b9 100644 --- a/tests/workflow_policy.bats +++ b/tests/workflow_policy.bats @@ -121,3 +121,17 @@ NODE [ "$status" -eq 0 ] [[ "$output" == *'"failed":"PR cannot be ready-for-review unless linked issue #67 is status/review."'* ]] } + +@test "workflow-policy: PR from main branch fails even with closing reference" { + run_policy '{"head":{"ref":"main"},"title":"fix: something","body":"Closes #67","draft":true}' '{"number":67,"labels":[]}' + + [ "$status" -eq 0 ] + [[ "$output" == *'"failed":"PR must not be opened directly from the main branch'* ]] +} + +@test "workflow-policy: any named feature branch with closing reference passes" { + run_policy '{"head":{"ref":"my-feature-branch"},"title":"fix: something","body":"Closes #67","draft":true}' '{"number":67,"labels":[]}' + + [ "$status" -eq 0 ] + [[ "$output" == *'"failed":null'* ]] +} From bd84ad9f4085b104738e7a444a743d7cb2ace72b Mon Sep 17 00:00:00 2001 From: Matt Lydon <489306+Mpfk@users.noreply.github.com> Date: Wed, 13 May 2026 12:06:08 -0700 Subject: [PATCH 2/3] feat(workflow-policy): reject PRs opened directly from the main branch [GREEN] Closes #73 --- .github/workflows/workflow-policy.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/workflow-policy.yml b/.github/workflows/workflow-policy.yml index 204b711..78a1ce4 100644 --- a/.github/workflows/workflow-policy.yml +++ b/.github/workflows/workflow-policy.yml @@ -26,6 +26,12 @@ jobs: const branch = pr.head.ref; const prBody = `${pr.title}\n\n${pr.body || ''}`; + // Reject PRs opened directly from the base branch. + if (branch === pr.base?.ref || branch === 'main') { + core.setFailed(`PR must not be opened directly from the main branch. Create a feature branch and link it to an issue (e.g. 'Closes #N' in the PR body).`); + return; + } + // Extract issue number: prefer branch name (issue/{n}), fall back to a closing keyword reference in PR text. // This supports both the VS Code path (issue/{n} branches) and the Native GitHub path // where GitHub's Copilot SWE agent always creates copilot/... branches. From fa0b127082aaa0ff8aae6ff6b31640f020f50d06 Mon Sep 17 00:00:00 2001 From: Matt Lydon <489306+Mpfk@users.noreply.github.com> Date: Wed, 13 May 2026 12:06:23 -0700 Subject: [PATCH 3/3] refactor(branch-guard): remove issue/{N} naming prescription from error message [REFACTOR] Closes #73 --- .githooks/pre-commit.d/010-branch-guard.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.githooks/pre-commit.d/010-branch-guard.sh b/.githooks/pre-commit.d/010-branch-guard.sh index 41584aa..9a7a3a7 100755 --- a/.githooks/pre-commit.d/010-branch-guard.sh +++ b/.githooks/pre-commit.d/010-branch-guard.sh @@ -6,6 +6,6 @@ source "$REPO_ROOT/workflow.conf" branch=$(git rev-parse --abbrev-ref HEAD) if [ "$branch" = "$MAIN_BRANCH" ]; then echo "ERROR: Direct commits to $MAIN_BRANCH are forbidden." - echo "Create an issue and work on a feature branch: issue/{issue-id}" + echo "Create a feature branch and link it to a GitHub Issue." exit 1 fi