diff --git a/.github/workflows/codegen-models.yml b/.github/workflows/codegen-models.yml index b8c27c467..72f21434c 100644 --- a/.github/workflows/codegen-models.yml +++ b/.github/workflows/codegen-models.yml @@ -5,14 +5,14 @@ on: paths: - 'packages/utils/cli-registry.yaml' branches: - - main - - 'feature/**' + [main] pull_request: paths: - 'packages/utils/cli-registry.yaml' jobs: codegen: + if: (github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main') runs-on: ubuntu-latest permissions: contents: write diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 188accab9..6adfe0825 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -35,6 +35,7 @@ env: jobs: e2e-test: + if: (github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main') name: E2E Integration Test runs-on: ${{ matrix.os }} strategy: diff --git a/.github/workflows/fleet-e2e.yml b/.github/workflows/fleet-e2e.yml index 21e7221a3..f2dfe93c5 100644 --- a/.github/workflows/fleet-e2e.yml +++ b/.github/workflows/fleet-e2e.yml @@ -9,13 +9,12 @@ name: Fleet E2E # engine build), which rust-cache amortises on warm runs. The 20-minute job # timeout is a cold-build ceiling, not the matrix runtime. -# NOTE on branches: the fleet stack is developed on `feat/fleet-*` branches that -# merge into each other before `main`, so the trigger must include them — gating -# on `[main]` alone leaves this job inert for the entire feature stack (it would -# only run after something reaches main). Update to `[main]` once the stack lands. +# NOTE on branches: CI runs only on this repository's `trunk` -> `main` PR and +# on pushes to `main` (see the job `if:`). Feature branches, including +# `feat/fleet-*`, merge into `trunk` without CI. on: push: - branches: [main, 'feat/fleet-**'] + branches: [main] paths: - 'packages/fleet/**' - 'packages/cli/**' @@ -54,6 +53,7 @@ env: jobs: fleet-e2e: + if: (github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main') name: Two-node fleet matrix runs-on: ubuntu-latest timeout-minutes: 20 diff --git a/.github/workflows/large-files.yml b/.github/workflows/large-files.yml index ee6e2afb0..306839141 100644 --- a/.github/workflows/large-files.yml +++ b/.github/workflows/large-files.yml @@ -2,6 +2,7 @@ name: Large File Check on: [pull_request] jobs: check: + if: (github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main') runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/node-compat.yml b/.github/workflows/node-compat.yml index 4bc54e16c..cf365c1f2 100644 --- a/.github/workflows/node-compat.yml +++ b/.github/workflows/node-compat.yml @@ -18,11 +18,14 @@ concurrency: jobs: changes: + if: (github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main') uses: ./.github/workflows/detect-changes.yml install-test: name: Install Test (Node ${{ matrix.node-version }}) needs: changes - if: needs.changes.outputs.node_changed == 'true' + if: >- + ((github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main')) && + (needs.changes.outputs.node_changed == 'true') runs-on: ubuntu-latest strategy: matrix: @@ -83,7 +86,9 @@ jobs: fresh-install: name: Fresh Install (Node ${{ matrix.node-version }}) needs: changes - if: needs.changes.outputs.node_changed == 'true' + if: >- + ((github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main')) && + (needs.changes.outputs.node_changed == 'true') runs-on: ubuntu-latest strategy: matrix: diff --git a/.github/workflows/package-validation.yml b/.github/workflows/package-validation.yml index 71fb73175..48f241485 100644 --- a/.github/workflows/package-validation.yml +++ b/.github/workflows/package-validation.yml @@ -21,11 +21,14 @@ env: jobs: changes: + if: (github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main') uses: ./.github/workflows/detect-changes.yml validate: name: Build & Validate needs: changes - if: needs.changes.outputs.node_changed == 'true' + if: >- + ((github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main')) && + (needs.changes.outputs.node_changed == 'true') runs-on: ubuntu-latest env: NPM_CONFIG_FUND: false @@ -199,7 +202,9 @@ jobs: publish-fresh-install-build: name: Publish Fresh Install Build needs: changes - if: needs.changes.outputs.node_changed == 'true' + if: >- + ((github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main')) && + (needs.changes.outputs.node_changed == 'true') runs-on: ubuntu-latest env: NPM_CONFIG_FUND: false @@ -248,9 +253,8 @@ jobs: # provisioning an undeletable workspace; other same-repository PRs and # main pushes must reuse the CI workspace. if: >- - needs.changes.outputs.node_changed == 'true' && - github.actor != 'dependabot[bot]' && - (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) + ((github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main')) && + (needs.changes.outputs.node_changed == 'true' && github.actor != 'dependabot[bot]' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)) runs-on: macos-latest env: NPM_CONFIG_FUND: false diff --git a/.github/workflows/prettier-fmt-fix.yml b/.github/workflows/prettier-fmt-fix.yml index 4f85827b3..baa274e8e 100644 --- a/.github/workflows/prettier-fmt-fix.yml +++ b/.github/workflows/prettier-fmt-fix.yml @@ -38,7 +38,9 @@ jobs: name: Auto-format Prettier files runs-on: ubuntu-latest # Only run on same-repo PRs where the bot can push back formatting commits. - if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository + if: >- + ((github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main')) && + (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) permissions: contents: write steps: diff --git a/.github/workflows/relay-evals.yml b/.github/workflows/relay-evals.yml index dc3ea0f59..c646a2d13 100644 --- a/.github/workflows/relay-evals.yml +++ b/.github/workflows/relay-evals.yml @@ -38,6 +38,7 @@ env: jobs: evals: + if: (github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main') name: Offline evals runs-on: ubuntu-latest diff --git a/.github/workflows/relayflow-pr-proof-broker.yml b/.github/workflows/relayflow-pr-proof-broker.yml index 6c152d9ba..3b3000b6e 100644 --- a/.github/workflows/relayflow-pr-proof-broker.yml +++ b/.github/workflows/relayflow-pr-proof-broker.yml @@ -31,8 +31,8 @@ jobs: build: name: ${{ github.event_name == 'pull_request' && 'Validate PR-head Linux broker' || 'Build exact Linux broker' }} if: >- - github.event_name != 'pull_request_target' || - github.event.pull_request.head.repo.full_name == github.repository + ((github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main')) && + (github.event_name != 'pull_request_target' || github.event.pull_request.head.repo.full_name == github.repository) runs-on: ubuntu-latest timeout-minutes: 30 steps: diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index bf8833d3e..21eb358cf 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -17,12 +17,15 @@ env: jobs: changes: + if: (github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main') uses: ./.github/workflows/detect-changes.yml rust-test: name: Rust Tests (${{ matrix.os }}) needs: changes - if: needs.changes.outputs.rust_changed == 'true' + if: >- + ((github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main')) && + (needs.changes.outputs.rust_changed == 'true') runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -42,7 +45,9 @@ jobs: relay-pty-windows-test: name: Relay PTY Synchronization Tests (windows-latest) needs: changes - if: needs.changes.outputs.rust_changed == 'true' + if: >- + ((github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main')) && + (needs.changes.outputs.rust_changed == 'true') runs-on: windows-latest timeout-minutes: 10 steps: @@ -62,7 +67,9 @@ jobs: rust-cross-compile: name: Cross-compile check (${{ matrix.target }}) needs: changes - if: needs.changes.outputs.rust_changed == 'true' + if: >- + ((github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main')) && + (needs.changes.outputs.rust_changed == 'true') runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -96,7 +103,9 @@ jobs: rust-clippy: name: Clippy (${{ matrix.os }}) needs: changes - if: needs.changes.outputs.rust_changed == 'true' + if: >- + ((github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main')) && + (needs.changes.outputs.rust_changed == 'true') runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -115,7 +124,9 @@ jobs: rust-fmt: name: Format needs: changes - if: needs.changes.outputs.rust_changed == 'true' + if: >- + ((github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main')) && + (needs.changes.outputs.rust_changed == 'true') runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -127,7 +138,9 @@ jobs: sdk-check: name: SDK TypeScript Check needs: changes - if: needs.changes.outputs.node_changed == 'true' + if: >- + ((github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main')) && + (needs.changes.outputs.node_changed == 'true') runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/rust-fmt-fix.yml b/.github/workflows/rust-fmt-fix.yml index 70472298f..1df1a6ab7 100644 --- a/.github/workflows/rust-fmt-fix.yml +++ b/.github/workflows/rust-fmt-fix.yml @@ -20,7 +20,9 @@ jobs: name: Auto-format Rust code runs-on: ubuntu-latest # Only run on same-repo PRs where the bot can push back formatting commits. - if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository + if: >- + ((github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main')) && + (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) permissions: contents: write steps: diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 13e4df279..89d55e1c7 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -21,13 +21,16 @@ concurrency: jobs: changes: + if: (github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main') uses: ./.github/workflows/detect-changes.yml # NPM audit for known vulnerabilities npm-audit: name: NPM Audit needs: changes - if: needs.changes.outputs.node_changed == 'true' + if: >- + ((github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main')) && + (needs.changes.outputs.node_changed == 'true') runs-on: ubuntu-latest steps: @@ -57,7 +60,9 @@ jobs: dependency-review: name: Dependency Review runs-on: ubuntu-latest - if: github.event_name == 'pull_request' + if: >- + ((github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main')) && + (github.event_name == 'pull_request') # This job requires the dependency graph to be enabled in repo settings # Make it non-blocking until that's configured continue-on-error: true @@ -77,7 +82,9 @@ jobs: codeql: name: CodeQL Analysis needs: changes - if: needs.changes.outputs.node_changed == 'true' + if: >- + ((github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main')) && + (needs.changes.outputs.node_changed == 'true') runs-on: ubuntu-latest permissions: security-events: write @@ -104,6 +111,7 @@ jobs: # Secret scanning (check for accidentally committed secrets) secrets-scan: + if: (github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main') name: Secret Scanning runs-on: ubuntu-latest # Make non-blocking - review findings manually @@ -135,7 +143,9 @@ jobs: license-check: name: License Compliance needs: changes - if: needs.changes.outputs.node_changed == 'true' + if: >- + ((github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main')) && + (needs.changes.outputs.node_changed == 'true') runs-on: ubuntu-latest steps: diff --git a/.github/workflows/stress-tests.yml b/.github/workflows/stress-tests.yml index 323dbbf0a..ccaaa351d 100644 --- a/.github/workflows/stress-tests.yml +++ b/.github/workflows/stress-tests.yml @@ -29,6 +29,7 @@ concurrency: jobs: stress-test-orchestrator: + if: (github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main') name: Orchestrator Stress Tests runs-on: ${{ matrix.os }} strategy: @@ -78,6 +79,7 @@ jobs: fi stress-test-orchestrator-integration: + if: (github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main') name: Orchestrator Integration Stress Tests runs-on: ${{ matrix.os }} strategy: @@ -161,7 +163,9 @@ jobs: name: Stress Test Summary needs: [stress-test-orchestrator, stress-test-orchestrator-integration] runs-on: ubuntu-latest - if: always() + if: >- + ((github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main')) && + (always()) steps: - name: Download all artifacts diff --git a/.github/workflows/targeted-feature-verification.yml b/.github/workflows/targeted-feature-verification.yml index 3e9fc7f72..429a8d848 100644 --- a/.github/workflows/targeted-feature-verification.yml +++ b/.github/workflows/targeted-feature-verification.yml @@ -35,6 +35,7 @@ concurrency: jobs: select: + if: (github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main') name: Select Flows v2 shards runs-on: ubuntu-24.04 timeout-minutes: 15 @@ -93,6 +94,7 @@ jobs: retention-days: 14 targeted: + if: (github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main') name: Flows v2 shard ${{ matrix.shard }} needs: select runs-on: ubuntu-24.04 diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml index abc1a1918..4a15760e0 100644 --- a/.github/workflows/test-build.yml +++ b/.github/workflows/test-build.yml @@ -2,6 +2,7 @@ name: Test Build Scripts on: push: + branches: [main] paths: - 'scripts/build-bun.sh' - 'scripts/sign-macos-binary.sh' @@ -20,6 +21,7 @@ env: jobs: test-build-script: + if: (github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main') name: Test build-bun.sh runs-on: ${{ matrix.os }} strategy: @@ -107,6 +109,7 @@ jobs: done test-build-edge-cases: + if: (github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main') name: Test edge cases runs-on: ubuntu-latest @@ -154,7 +157,9 @@ jobs: name: Summary needs: [test-build-script, test-build-edge-cases] runs-on: ubuntu-latest - if: always() + if: >- + ((github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main')) && + (always()) steps: - name: Summary diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml index 0e5a2b065..47ab6edb4 100644 --- a/.github/workflows/test-install.yml +++ b/.github/workflows/test-install.yml @@ -2,6 +2,7 @@ name: Test Install Script on: push: + branches: [main] paths: - 'install.sh' - 'install.test.sh' @@ -25,6 +26,7 @@ env: jobs: test-install: + if: (github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main') name: Test on ${{ matrix.os }} (${{ matrix.node }}) runs-on: ${{ matrix.os }} strategy: @@ -181,6 +183,7 @@ jobs: # Test fallback when gunzip is not available test-no-gunzip: + if: (github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main') name: Test without gunzip runs-on: ubuntu-latest @@ -225,6 +228,7 @@ jobs: ' test-install-alpine: + if: (github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main') name: Test on Alpine Linux runs-on: ubuntu-latest @@ -264,6 +268,7 @@ jobs: ' test-install-fedora: + if: (github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main') name: Test on Fedora runs-on: ubuntu-latest @@ -299,6 +304,7 @@ jobs: # Test script syntax and edge cases test-script-quality: + if: (github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main') name: Script Quality Checks runs-on: ubuntu-latest @@ -345,7 +351,9 @@ jobs: name: Test Summary needs: [test-install, test-install-alpine, test-install-fedora, test-no-gunzip, test-script-quality] runs-on: ubuntu-latest - if: always() + if: >- + ((github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main')) && + (always()) steps: - name: Summary diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 88db5acb9..785c27a46 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,11 +21,14 @@ env: jobs: changes: + if: (github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main') uses: ./.github/workflows/detect-changes.yml test: needs: changes - if: needs.changes.outputs.node_changed == 'true' + if: >- + ((github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main')) && + (needs.changes.outputs.node_changed == 'true') runs-on: ${{ matrix.os }} strategy: matrix: @@ -61,7 +64,9 @@ jobs: windows-credential-acl: name: Windows credential ACL validation needs: changes - if: needs.changes.outputs.node_changed == 'true' + if: >- + ((github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main')) && + (needs.changes.outputs.node_changed == 'true') runs-on: windows-latest steps: - name: Checkout repository @@ -92,7 +97,9 @@ jobs: coverage: name: Coverage (upload) needs: changes - if: needs.changes.outputs.node_changed == 'true' + if: >- + ((github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main')) && + (needs.changes.outputs.node_changed == 'true') runs-on: ubuntu-latest steps: - name: Checkout repository @@ -120,7 +127,9 @@ jobs: lint: needs: changes - if: needs.changes.outputs.node_changed == 'true' + if: >- + ((github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main')) && + (needs.changes.outputs.node_changed == 'true') runs-on: ubuntu-latest steps: - name: Checkout repository @@ -147,7 +156,9 @@ jobs: swift-test: name: Swift SDK Tests needs: changes - if: needs.changes.outputs.sdk_swift_changed == 'true' + if: >- + ((github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || (github.head_ref == 'trunk' && github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'main')) && + (needs.changes.outputs.sdk_swift_changed == 'true') runs-on: macos-latest defaults: run: diff --git a/.github/workflows/trunk-guard.yml b/.github/workflows/trunk-guard.yml new file mode 100644 index 000000000..fbe379e4b --- /dev/null +++ b/.github/workflows/trunk-guard.yml @@ -0,0 +1,22 @@ +name: Trunk guard + +# Only this repository's `trunk` branch may merge into main. Feature branches +# merge into `trunk`; CI runs only on the trunk -> main PR. +# pull_request_target loads this workflow from the base branch, so a PR cannot +# edit the guard to pass itself. It never checks out PR code. +on: + pull_request_target: + branches: [main] + types: [opened, synchronize, reopened, edited] + +permissions: {} + +jobs: + only-trunk-merges-to-main: + if: github.head_ref != 'trunk' || github.event.pull_request.head.repo.full_name != github.repository + runs-on: ubuntu-latest + timeout-minutes: 1 + steps: + - run: | + echo "::error::Only this repository's 'trunk' branch may merge into main. Retarget this PR to 'trunk'." + exit 1 diff --git a/AGENTS.md b/AGENTS.md index 032295138..b9bbd8cac 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -313,3 +313,46 @@ Your trajectory helps others understand: Future agents can query past trajectories to learn from your decisions. + + +## Merging: `trunk` + the `mergeable` label + +CI suites do **not** run automatically on feature branches. They run only on +this repository's `trunk` → `main` pull request and on pushes to `main`. The +one check that does run on a feature PR into `main` is `Trunk guard`, which +fails it on purpose; manually dispatched workflows (`workflow_dispatch`) still +run on any branch. (Repos whose default branch is not +`main`, e.g. `master`, use that branch wherever this says `main`.) A merge +agent batches ready PRs into `trunk`, gets that one PR green, and merges it. + +**When you open a PR** +1. Branch from `trunk` and open the PR with **base `trunk`**, not `main`. + A PR into `main` from any other branch fails the `Trunk guard` check. +2. No CI runs on your PR, so verify locally before calling it ready: run the + typecheck, tests and lint this repo uses, and list the exact commands and + results in the PR body. + +**When the PR is ready** +3. Add the label **`mergeable`** once all of these are true: + - The change is complete and the local checks above pass. + - Review feedback (human and bot) is addressed or answered. + - It is not a draft and does not depend on an unmerged PR. +4. Remove `mergeable` if the PR stops being ready (new work, a failing check, a + blocking question). The label is read live from GitHub on every sweep. + +**What you must not do** +- Do not merge your own PR, and never merge into or push to `trunk` or `main` + directly. +- Do not re-enable CI for feature branches or edit the `trunk` gates in + `.github/workflows/`. + +**The merge agent** sweeps open `mergeable` PRs with base `trunk` about every +10 minutes. It reads each PR's linked sessions (the `Agent Relay sessions` +block in the PR body, then the session summary) for context, merges them into +`trunk`, opens or updates the `trunk` → `main` PR, fixes CI there, merges when +green, and posts a summary. If your PR conflicts with `trunk`, it may ask you +to rebase on `trunk`; do so and keep the label. + +> Interim: the sweep worker is not deployed yet. Until it is, a human or a +> designated agent performs the merge-agent steps manually. Labelling is unchanged. + diff --git a/prpm.lock b/prpm.lock index cc6e82670..5f049c207 100644 --- a/prpm.lock +++ b/prpm.lock @@ -275,7 +275,24 @@ "sourceFormat": "claude", "sourceSubtype": "skill", "installedPath": ".agents/skills/writing-relayflows/SKILL.md" + }, + "@agent-relay/merge-train-snippet#agents.md:AGENTS.md": { + "version": "1.0.1", + "resolved": "https://registry.prpm.dev/api/v1/packages/%40agent-relay%2Fmerge-train-snippet/1.0.1.tar.gz", + "integrity": "sha256-b993f4fd7a737b5788f10fca8b02d6eea02c803b1d4483f6536f3cfd3f85126e", + "format": "agents.md", + "subtype": "snippet", + "sourceFormat": "generic", + "sourceSubtype": "snippet", + "installedPath": "AGENTS.md", + "snippetMetadata": { + "targetPath": "AGENTS.md", + "config": { + "target": "AGENTS.md", + "position": "append" + } + } } }, - "generated": "2026-09-19T01:05:52.716Z" + "generated": "2026-10-08T23:07:11.486Z" } \ No newline at end of file