Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 37 additions & 16 deletions .github/workflows/benchmark-external.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ name: NAD Benchmark (label-triggered)

# The active NAD benchmark gate. Policy: EVERY PR (internal or external fork) is
# gated on demand — a maintainer reviews the PR and adds the `run-benchmark`
# label to run it. (Automatic gating in benchmark.yml is disabled by policy;
# this label gate is the one that runs.)
# label to run it. This is the only NAD benchmark workflow.
#
# Maintainers can also run it MANUALLY from the Actions tab (Run workflow /
# workflow_dispatch) against any branch, with optional subset/count/gate inputs.
# Manual runs are trusted (only maintainers can trigger them), so they check out
# the selected branch directly instead of an untrusted PR head.
#
# It uses pull_request_target so the run has access to KIRO_API_KEY even for fork
# PRs (a plain pull_request gate can't — fork PRs receive no secrets), and is
Expand All @@ -28,33 +32,49 @@ on:
# never auto-runs on push. A maintainer must (re-)apply the label each time.
types: [labeled]

# Manual runs from the Actions tab. Only users with write access can dispatch
# this, so the run is trusted — it scores the selected branch's own code.
workflow_dispatch:
inputs:
subset:
description: "Subset(s): 'all', a single name, or a comma-separated list"
default: 'all'
count:
description: 'Pass@k attempts per scenario'
default: '1'
gate:
description: 'Required pass rate (0-100); job fails below this'
default: '90'

# Least privilege: the base-context GITHUB_TOKEN is write-capable by default under
# pull_request_target. Restrict it to read so untrusted code can't use it to push,
# open releases, or modify the repo.
permissions:
contents: read

concurrency:
group: nad-benchmark-external-${{ github.event.pull_request.number }}
group: nad-benchmark-external-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
gate:
# Run ONLY when a maintainer added the `run-benchmark` label. This `if` is the
# trust gate — without it, pull_request_target would run untrusted code with
# secrets on every PR event.
if: github.event.label.name == 'run-benchmark'
# Run on a manual dispatch, OR when a maintainer added the `run-benchmark`
# label. For the label path this `if` is the trust gate — without it,
# pull_request_target would run untrusted code with secrets on every PR event.
if: github.event_name == 'workflow_dispatch' || github.event.label.name == 'run-benchmark'
name: NAD external benchmark (labeled)
runs-on: ubuntu-latest
timeout-minutes: 20

steps:
# Check out the PR's HEAD — i.e. the contributor's UNTRUSTED code. This is
# required (we must score their skill) and is the crux of the risk above.
# The workflow FILE itself still comes from base (pull_request_target), so
# the contributor cannot alter these steps — only the skill/agent/test/
# grader content that the steps then run.
- name: Checkout PR head (untrusted)
# Label runs: check out the PR's HEAD — i.e. the contributor's UNTRUSTED
# code. This is required (we must score their skill) and is the crux of the
# risk above. The workflow FILE itself still comes from base
# (pull_request_target), so the contributor cannot alter these steps — only
# the skill/agent/test/grader content that the steps then run.
# Manual runs: head.sha is empty, so checkout falls back to the selected
# branch (trusted — only maintainers can dispatch).
- name: Checkout PR head (untrusted on label runs)
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
Expand All @@ -69,13 +89,14 @@ jobs:
KIRO_API_KEY: ${{ secrets.KIRO_API_KEY }}
run: |
set -euo pipefail
# Inputs come from the manual dispatch form; label runs use the defaults.
python benchmark/run_skill_tests.py \
--nad-root=. \
--subset=all \
--count=1 \
--subset="${{ github.event.inputs.subset || 'all' }}" \
--count="${{ github.event.inputs.count || '1' }}" \
--workers=4 \
--judge \
--gate=90
--gate="${{ github.event.inputs.gate || '90' }}"

- name: Upload results
if: always()
Expand Down
86 changes: 0 additions & 86 deletions .github/workflows/benchmark.yml

This file was deleted.

Loading