name: Gatekeep description: > Parallel multi-model AI PR review across providers. Agreed findings become inline comments; disagreements stay in the summary. Fail-closed risk tier. Self-contained remote uses: git-url@ref. Provider secrets via free-form env names. author: Gatekeep inputs: github-token: description: Token with pull-requests + checks write required: true config-dir: description: > Optional consumer config directory (e.g. .github/gatekeep with providers.yaml). Empty = Action defaults/providers.yaml. required: false default: "" models: description: > Optional override of 2–3 models as comma-separated provider:model (or bare names unique in config). Empty = providers.yaml lists. required: false default: "" skip-draft: description: Skip draft PRs required: false default: "true" run-verify: description: Discover + run verify/benchmarks layer required: false default: "true" retain-artifacts: description: Upload last-good style artifacts from verify required: false default: "true" outputs: risk_tier: description: Final published risk tier value: ${{ steps.review.outputs.risk_tier }} security_blocking: description: Whether security check should block value: ${{ steps.review.outputs.security_blocking }} agreed_count: description: Count of multi-model agreed findings value: ${{ steps.review.outputs.agreed_count }} runs: using: composite steps: - name: Setup Node (stdlib only) uses: actions/setup-node@v4 with: node-version: "20" - name: Install PyYAML (providers.yaml) shell: bash run: | python3 -m pip install --user pyyaml -q python3 -c "import yaml; print('pyyaml ok', yaml.__version__)" - name: Discover entry points id: discover shell: bash env: GATEKEEP_ROOT: ${{ github.workspace }} GATEKEEP_ACTION: ${{ github.action_path }} CONFIG_DIR: ${{ inputs.config-dir }} run: bash "${{ github.action_path }}/scripts/discover.sh" - name: Policy floor + security scan id: floor shell: bash env: GITHUB_TOKEN: ${{ inputs.github-token }} CONFIG_DIR: ${{ inputs.config-dir }} GATEKEEP_ROOT: ${{ github.workspace }} GATEKEEP_ACTION: ${{ github.action_path }} run: bash "${{ github.action_path }}/scripts/policy-floor.sh" - name: Parallel multi-model review id: review shell: bash env: GITHUB_TOKEN: ${{ inputs.github-token }} CONFIG_DIR: ${{ inputs.config-dir }} GATEKEEP_MODELS: ${{ inputs.models }} SKIP_DRAFT: ${{ inputs.skip-draft }} GATEKEEP_ROOT: ${{ github.workspace }} GATEKEEP_ACTION: ${{ github.action_path }} POLICY_FLOOR: ${{ steps.floor.outputs.floor }} POLICY_REASONS: ${{ steps.floor.outputs.reasons }} SCAN_HARD_FAIL: ${{ steps.floor.outputs.scan_hard_fail }} DISCOVER_JSON: ${{ steps.discover.outputs.discover_json }} # Provider API keys: set free-form names from providers.yaml `key` # on this step's env (inherited from the workflow step). Example: # env: # OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} # MY_CUSTOM_KEY: ${{ secrets.MY_CUSTOM_KEY }} run: | export PATH="$HOME/.local/bin:$PATH" # Ensure user-site packages (PyYAML) are importable export PYTHONPATH="$(python3 -c 'import site; print(site.getusersitepackages())'):${PYTHONPATH:-}" bash "${{ github.action_path }}/scripts/review.sh" - name: Verify layer (benchmarks) if: inputs.run-verify == 'true' id: verify shell: bash env: GATEKEEP_ROOT: ${{ github.workspace }} CONFIG_DIR: ${{ inputs.config-dir }} GATEKEEP_ACTION: ${{ github.action_path }} DISCOVER_JSON: ${{ steps.discover.outputs.discover_json }} run: bash "${{ github.action_path }}/scripts/verify-benchmarks.sh" - name: Retain artifacts if: inputs.retain-artifacts == 'true' && always() uses: actions/upload-artifact@v4 with: name: gatekeep-last-good-${{ github.run_id }} path: | ${{ github.workspace }}/.gatekeep/out/ ${{ github.workspace }}/.gatekeep/verify/ if-no-files-found: ignore retention-days: 14