diff --git a/.github/workflows/issue-gate.yml b/.github/workflows/issue-gate.yml index 57c4d2912..19eb5daeb 100644 --- a/.github/workflows/issue-gate.yml +++ b/.github/workflows/issue-gate.yml @@ -12,13 +12,19 @@ permissions: jobs: check-linked-issue: runs-on: ubuntu-latest - # Closes #1059: bypass Issue Gate for trusted bots (Dependabot opens dep PRs without issues) - if: >- - github.actor != 'dependabot[bot]' && github.actor != 'github-actions[bot]' - && github.event.pull_request.user.login != 'dependabot[bot]' - && github.event.pull_request.user.login != 'github-actions[bot]' + # Closes #1031: trusted bots (Dependabot) PASS this required gate as a no-op + # instead of SKIPPING it (a skipped required check never satisfies branch + # protection). Detect bot via trigger actor AND PR author login (survives a + # human 'Update branch'); short-circuit the substantive step. + env: + IS_BOT: ${{ (github.actor == 'dependabot[bot]' || github.actor == 'github-actions[bot]' || github.event.pull_request.user.login == 'dependabot[bot]' || github.event.pull_request.user.login == 'github-actions[bot]') && 'true' || 'false' }} steps: + - name: Trusted-bot bypass (no-op pass) + if: env.IS_BOT == 'true' + run: echo "Trusted bot PR -- Dependabot or github-actions -- Issue Gate passes as no-op (Closes #1031)." + - name: Check for linked issues in PR + if: env.IS_BOT != 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR_TITLE: ${{ github.event.pull_request.title }} diff --git a/.github/workflows/l1-traceability.yml b/.github/workflows/l1-traceability.yml index 2fcba854f..91f14d52c 100644 --- a/.github/workflows/l1-traceability.yml +++ b/.github/workflows/l1-traceability.yml @@ -8,18 +8,26 @@ jobs: check-traceability: name: Check L1 TRACEABILITY runs-on: ubuntu-latest - # Closes #1059: bypass L1 gate for trusted bots (Dependabot maintains deps, not features) - if: >- - github.actor != 'dependabot[bot]' && github.actor != 'github-actions[bot]' - && github.event.pull_request.user.login != 'dependabot[bot]' - && github.event.pull_request.user.login != 'github-actions[bot]' + # Closes #1031: trusted bots (Dependabot) PASS this required gate as a no-op + # instead of SKIPPING it. A skipped required check never satisfies branch + # protection, so the job must RUN and conclude success. We detect the bot via + # both the trigger actor and the PR author login (the latter survives a human + # 'Update branch') and short-circuit the substantive steps. + env: + IS_BOT: ${{ (github.actor == 'dependabot[bot]' || github.actor == 'github-actions[bot]' || github.event.pull_request.user.login == 'dependabot[bot]' || github.event.pull_request.user.login == 'github-actions[bot]') && 'true' || 'false' }} steps: + - name: Trusted-bot bypass (no-op pass) + if: env.IS_BOT == 'true' + run: echo "Trusted bot PR -- Dependabot or github-actions -- L1 TRACEABILITY gate passes as no-op (Closes #1031)." + - name: Checkout repository + if: env.IS_BOT != 'true' uses: actions/checkout@v4 with: fetch-depth: 0 - name: Check for Closes #N in commits + if: env.IS_BOT != 'true' id: check-commits run: | set -e @@ -86,6 +94,7 @@ jobs: fi - name: Check L2 GENERATION (gen/ edits forbidden) + if: env.IS_BOT != 'true' id: check-generation run: | set -e @@ -116,6 +125,7 @@ jobs: echo " ✓ No gen/ edits detected (new files allowed)" - name: Check L3 PURITY (ASCII-only identifiers) + if: env.IS_BOT != 'true' id: check-purity run: | set -e diff --git a/.github/workflows/now-sync-gate.yml b/.github/workflows/now-sync-gate.yml index feb8cb319..9adc9075b 100644 --- a/.github/workflows/now-sync-gate.yml +++ b/.github/workflows/now-sync-gate.yml @@ -8,18 +8,25 @@ on: jobs: check-now-freshness: runs-on: ubuntu-latest - # Closes #1059: bypass NOW Sync Gate for trusted bots (deps PRs don't touch docs/NOW.md) - if: >- - github.actor != 'dependabot[bot]' && github.actor != 'github-actions[bot]' - && github.event.pull_request.user.login != 'dependabot[bot]' - && github.event.pull_request.user.login != 'github-actions[bot]' + # Closes #1031: trusted bots (Dependabot) PASS this required gate as a no-op + # instead of SKIPPING it (a skipped required check never satisfies branch + # protection). Detect bot via trigger actor AND PR author login (survives a + # human 'Update branch'); short-circuit the substantive steps. push events + # keep their existing behavior because pull_request is null there. + env: + IS_BOT: ${{ (github.actor == 'dependabot[bot]' || github.actor == 'github-actions[bot]' || github.event.pull_request.user.login == 'dependabot[bot]' || github.event.pull_request.user.login == 'github-actions[bot]') && 'true' || 'false' }} steps: + - name: Trusted-bot bypass (no-op pass) + if: env.IS_BOT == 'true' + run: echo "Trusted bot PR -- Dependabot or github-actions -- NOW Sync Gate passes as no-op (Closes #1031)." + - uses: actions/checkout@v4 + if: env.IS_BOT != 'true' with: fetch-depth: 0 - name: Check docs/NOW.md is updated (pull_request) - if: github.event_name == 'pull_request' + if: env.IS_BOT != 'true' && github.event_name == 'pull_request' env: GITHUB_EVENT_NAME: pull_request PR_BASE_SHA: ${{ github.event.pull_request.base.sha }} @@ -27,7 +34,7 @@ jobs: run: bash scripts/ci/now-sync-gate-diff.sh - name: Check docs/NOW.md is updated (push) - if: github.event_name == 'push' + if: env.IS_BOT != 'true' && github.event_name == 'push' env: GITHUB_EVENT_NAME: push PUSH_BEFORE: ${{ github.event.before }} @@ -35,6 +42,7 @@ jobs: run: bash scripts/ci/now-sync-gate-diff.sh - name: Validate NOW.md date is today or recent + if: env.IS_BOT != 'true' run: | set -euo pipefail TODAY=$(date -u +%Y-%m-%d) @@ -51,6 +59,7 @@ jobs: echo "✅ NOW.md freshness check passed: $LAST (UTC window: $YESTERDAY .. $TODAY)" - name: Check agent sync JSON exists + if: env.IS_BOT != 'true' run: | if [ ! -f ".trinity/state/github-sync.json" ]; then echo "::warning::Agent sync file missing: .trinity/state/github-sync.json" diff --git a/docs/NOW.md b/docs/NOW.md index ab6aebe80..803f2cb2a 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -2,6 +2,12 @@ Last updated: 2026-06-14 +## ci-bot-gate-pass-not-skip -- trusted-bot gates PASS as no-op instead of SKIP (Closes #1031) + +- **WHERE** (CI only): `.github/workflows/l1-traceability.yml`, `.github/workflows/issue-gate.yml`, `.github/workflows/now-sync-gate.yml`. Replaces the job-level `if:` bot-skip with a job-level `env.IS_BOT` flag (true when `github.actor` OR `github.event.pull_request.user.login` is `dependabot[bot]`/`github-actions[bot]`). The job now always RUNS; a leading no-op step passes for bots and every substantive step is guarded with `if: env.IS_BOT != 'true'`. No source, specs, codegen, conformance JSON, or `gen/` artefacts touched. +- **Why**: `check-now-freshness`, `validate`, `check`, `check-linked-issue` are REQUIRED status checks in ruleset `t27-master-protection`. The earlier author-guard fix made the gate jobs SKIP for Dependabot, but a SKIPPED required check never satisfies branch protection (`N of N required status checks are expected`), so dep PRs #1043/#1047/#1048/#1057/#1058 (and earlier #1045) stayed unmergeable even with admin. Making the job RUN and conclude `success` (all substantive steps skipped, the required context still reports green) satisfies the ruleset while keeping human PRs fully gated by Constitutional Law L1. `push` events keep their existing now-sync behavior (`pull_request` null => IS_BOT false unless a bot actor pushes). L6 untouched; L5 untouched (no silicon impact); L4 not applicable (workflow YAML). Closes #1031. +- **Anchor**: phi^2 + phi^-2 = 3 + ## ci-bot-bypass-author-guard -- harden trusted-bot gate bypass against human branch-updates (Closes #1031) - **WHERE** (CI only): `.github/workflows/l1-traceability.yml`, `.github/workflows/issue-gate.yml`, `.github/workflows/now-sync-gate.yml`. Each gate job's `if:` guard is extended from the actor-only check (`github.actor != 'dependabot[bot]' && != 'github-actions[bot]'`) to ALSO require `github.event.pull_request.user.login != 'dependabot[bot]'` and `!= 'github-actions[bot]'`. No source, no specs, no codegen, no conformance JSON, no `gen/` artefacts touched.