Skip to content

ci(eval): add functional tests for triage agent#31

Open
ralphbean wants to merge 1 commit into
mainfrom
functional-tests-triage
Open

ci(eval): add functional tests for triage agent#31
ralphbean wants to merge 1 commit into
mainfrom
functional-tests-triage

Conversation

@ralphbean

Copy link
Copy Markdown
Member

Summary

  • Port the triage agent functional test suite from fullsend-ai/fullsend
  • 4 test cases covering bug triage, needs-info, feature request, and duplicate detection
  • GitHub Actions workflow with pull_request_target authorization gate (ok-to-test label flow)
  • Installs fullsend and OpenShell from GitHub releases (no build step needed)
  • Uses agent-eval-harness submodule for workspace/execute/score pipeline

Key adaptations

The tests in fullsend-ai/fullsend build fullsend from source and reference internal/scaffold/fullsend-repo as FULLSEND_DIR. Since this repo is the scaffold directory, the adaptations are:

  • FULLSEND_DIR defaults to repo root
  • fullsend binary installed from latest GitHub release
  • OpenShell installed from latest GitHub release
  • Authorization action is self-contained (includes the check script)

Prerequisites

The workflow requires these repo secrets/vars to be configured:

  • E2E_GCP_WIF_PROVIDER / E2E_GCP_SERVICE_ACCOUNT — GCP Workload Identity Federation
  • EVAL_GH_TOKEN — GitHub token with repo + delete_repo scope
  • EVAL_ORG — GitHub org for ephemeral test repos
  • EVALS_VERTEX_PROJECT_ID / EVALS_GCP_REGION / E2E_GCP_PROJECT_ID — Vertex AI config

Test plan

  • Verify secrets/vars are configured on the agents repo
  • Run workflow via workflow_dispatch to validate end-to-end
  • Confirm lint passes: ./eval/lint-cases.sh triage

🤖 Generated with Claude Code

Port the triage agent functional test suite from fullsend-ai/fullsend.
Includes 4 test cases (bug, needs-info, feature-request, duplicate),
the agent-eval-harness submodule, hook scripts for ephemeral repo
lifecycle, and a GitHub Actions workflow with PR authorization checks.

Key adaptations for this repo:
- fullsend binary installed from release (no Go build step)
- FULLSEND_DIR defaults to repo root (this repo IS the scaffold)
- OpenShell installed from release (no pinned version file)
- Authorization action is self-contained

Signed-off-by: Ralph Bean <rbean@redhat.com>
Assisted-by: Claude claude-opus-4-6 <noreply@anthropic.com>
Signed-off-by: Ralph Bean <rbean@redhat.com>
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 6, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 5:14 PM UTC · Completed 5:30 PM UTC
Commit: 9276844 · View workflow run →

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

CI: add triage agent functional test suite with ok-to-test PR gate

🧪 Tests ⚙️ Configuration changes ✨ Enhancement 🕐 40+ Minutes

Grey Divider

AI Description

• Add triage agent functional test cases and fixture repos for end-to-end evaluation.
• Introduce eval runner scripts using agent-eval-harness to create/execute/score runs.
• Add pull_request_target workflow with ok-to-test authorization gate and secret-safe permissions.
Diagram

graph TD
  A["GitHub event"] --> B["Functional Tests workflow"] --> C["Gate job"] --> D["Auth action + script"] --> F{Authorized?}
  F -->|"yes"| G["Functional-tests job"] --> H["eval/run-functional.sh (harness pipeline)"] --> I{{"External APIs (GitHub/GCP/Releases)"}}
  F -->|"no"| D

  subgraph Legend
    direction LR
    _job["Job/Script"] ~~~ _dec{"Decision"} ~~~ _ext{{"External"}}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. GitHub Environments approval gate
  • ➕ First-class manual approval UX for untrusted PRs
  • ➕ Avoids custom ok-to-test label logic and issue event scanning
  • ➕ Clear audit trail of who approved secrets usage
  • ➖ More friction for routine contributor testing
  • ➖ Harder to automate “re-approval required after new commits” behavior
  • ➖ May require repo-level environment policy work
2. Run PR workflow without secrets + nightly/maintainer-triggered secret run
  • ➕ Eliminates pull_request_target risks entirely
  • ➕ Keeps untrusted code from ever receiving secrets
  • ➕ Simpler permission model
  • ➖ PR feedback loop is slower/incomplete (functional tests may be skipped)
  • ➖ Requires separate scheduling/dispatch and triage for failures
  • ➖ Doesn’t test fork PRs end-to-end by default
3. Pin fullsend/OpenShell release versions (instead of latest)
  • ➕ Reproducible CI runs; fewer surprising test failures
  • ➕ Easier to bisect regressions to agent code vs toolchain updates
  • ➖ Requires periodic version bump maintenance
  • ➖ May lag security/bug fixes in upstream tooling

Recommendation: The PR’s approach (pull_request_target with a separate base-branch gate job, minimal permissions on the untrusted checkout job, and an ok-to-test freshness rule) is a solid, security-aware pattern for secret-bearing functional tests. Consider pinning fullsend/OpenShell versions for CI reproducibility and evaluating Environments-based approvals if you want a more standardized approval mechanism than labels.

Files changed (27) +1573 / -0

Tests (22) +1033 / -0
lint-cases.shAdd eval case linter for required annotations and judges +61/-0

Add eval case linter for required annotations and judges

• Introduces a shell linter that verifies eval.yaml includes required judges and that each case has annotations.yaml with max_turns and max_cost_usd declared.

eval/lint-cases.sh

run-functional.shAdd runner to execute and score functional evals via harness scripts +160/-0

Add runner to execute and score functional evals via harness scripts

• Adds a 3-phase runner (workspace, execute, score) using the harness submodule scripts. Works around harness dataset.path resolution by rewriting to an absolute path and copies missing per-case output artifacts into the run directory before scoring.

eval/run-functional.sh

capture-fixture.shCapture post-run GitHub fixture state for judging +87/-0

Capture post-run GitHub fixture state for judging

• Adds an after_each hook that snapshots issue/PR state (labels, comments, assignees, PR reviews) into output/fixture-state.json for downstream judges.

eval/scripts/capture-fixture.sh

run-fullsend.shAdd harness CLI runner that invokes fullsend against ephemeral repos +95/-0

Add harness CLI runner that invokes fullsend against ephemeral repos

• Implements the harness runner.command that clones the ephemeral repo, builds a temporary env file for secrets, runs fullsend with timeouts, then deletes the env file and normalizes metrics.json location for scoring.

eval/scripts/run-fullsend.sh

setup-fixture.shProvision ephemeral GitHub repos and create issue/PR fixtures per case +154/-0

Provision ephemeral GitHub repos and create issue/PR fixtures per case

• Adds a before_each hook that creates an ephemeral repo, populates content (from case repo/ when present), creates seed issues, then creates the fixture issue/PR and emits .hook-outputs.yaml for runner consumption. Includes a fallback to locate case input.yaml when harness dataset.path is misresolved.

eval/scripts/setup-fixture.sh

teardown-fixture.shDelete ephemeral GitHub repos after each case +16/-0

Delete ephemeral GitHub repos after each case

• Adds an after_each hook that best-effort deletes the ephemeral repo to avoid resource leakage, with warnings on failure.

eval/scripts/teardown-fixture.sh

annotations.yamlAdd bug triage expectations and budgets (URL encoding case) +40/-0

Add bug triage expectations and budgets (URL encoding case)

• Defines expected labels and a detailed rubric emphasizing identifying URL decoding as the likely root cause and noting the regex/docstring mismatch. Sets per-case turn and cost budgets.

eval/triage/cases/001-bug-url-encoding/annotations.yaml

input.yamlAdd bug fixture issue for login failures with '+' emails +36/-0

Add bug fixture issue for login failures with '+' emails

• Defines the issue title/body and context for the bug triage case, including a misleading stack trace and a hint about query parameter encoding.

eval/triage/cases/001-bug-url-encoding/input.yaml

repoPoint case repo content at python-webapp fixture repository +1/-0

Point case repo content at python-webapp fixture repository

• Adds a repo pointer so the agent can inspect the python-webapp fixture code during triage.

eval/triage/cases/001-bug-url-encoding/repo

annotations.yamlAdd needs-info expectations and forbidden labels +31/-0

Add needs-info expectations and forbidden labels

• Defines a deliberately vague issue case where the agent must request clarifying information and avoid marking the issue ready-to-code/triaged. Sets budgets and judging guidance.

eval/triage/cases/002-needs-info-vague-crash/annotations.yaml

input.yamlAdd vague crash issue fixture for needs-info routing +10/-0

Add vague crash issue fixture for needs-info routing

• Defines an underspecified issue intended to force an 'insufficient' triage action and a targeted clarifying question.

eval/triage/cases/002-needs-info-vague-crash/input.yaml

repoPoint case repo content at python-webapp fixture repository +1/-0

Point case repo content at python-webapp fixture repository

• Adds a repo pointer so the agent runs against the same fixture codebase as other cases.

eval/triage/cases/002-needs-info-vague-crash/repo

annotations.yamlAdd feature-request expectations (triaged+feature, not ready-to-code) +31/-0

Add feature-request expectations (triaged+feature, not ready-to-code)

• Defines expected labels and rubric to ensure feature requests are recognized, marked sufficient, and routed to triaged+feature rather than ready-to-code.

eval/triage/cases/003-feature-request/annotations.yaml

input.yamlAdd feature request issue fixture for pagination +25/-0

Add feature request issue fixture for pagination

• Defines a clear feature request case describing cursor-based pagination requirements and rationale for judging triage classification quality.

eval/triage/cases/003-feature-request/input.yaml

repoPoint case repo content at python-webapp fixture repository +1/-0

Point case repo content at python-webapp fixture repository

• Adds a repo pointer so the agent can inspect a consistent fixture repo while classifying the request.

eval/triage/cases/003-feature-request/repo

annotations.yamlAdd duplicate-detection expectations and forbidden labels +33/-0

Add duplicate-detection expectations and forbidden labels

• Defines a case where the agent must find a seeded issue and mark the new issue as duplicate, including expectations for respectful linking behavior.

eval/triage/cases/004-duplicate-issue/annotations.yaml

input.yamlAdd duplicate issue fixture plus seeded original issue +32/-0

Add duplicate issue fixture plus seeded original issue

• Defines the duplicate issue body and includes seed_issues to pre-create the original issue so the agent can discover it via search.

eval/triage/cases/004-duplicate-issue/input.yaml

repoPoint case repo content at python-webapp fixture repository +1/-0

Point case repo content at python-webapp fixture repository

• Adds a repo pointer for the duplicate-detection scenario so the agent can inspect code if needed.

eval/triage/cases/004-duplicate-issue/repo

eval.yamlDefine triage functional eval configuration, hooks, and judges +188/-0

Define triage functional eval configuration, hooks, and judges

• Adds the triage eval definition including parallel case execution, before/after hooks for ephemeral fixture lifecycle, CLI runner wiring, and judges for triage quality, label requirements, and max turn/cost thresholds.

eval/triage/eval.yaml

README.mdAdd python-webapp fixture repo README +3/-0

Add python-webapp fixture repo README

• Introduces minimal documentation for the example fixture repository used by triage cases.

eval/triage/repos/python-webapp/README.md

validators.pyAdd email validator code used by triage test rubrics +13/-0

Add email validator code used by triage test rubrics

• Adds a small validator implementation with an intentionally misleading docstring to test whether the agent inspects code and identifies mismatches with the issue report.

eval/triage/repos/python-webapp/src/auth/validators.py

views.pyAdd login handler code path for triage scenarios +14/-0

Add login handler code path for triage scenarios

• Adds a minimal login handler that calls validate_email without error handling, enabling triage cases to surface a secondary finding about 500s vs 400s.

eval/triage/repos/python-webapp/src/auth/views.py

Other (5) +540 / -0
action.ymlAdd composite action to gate PR-triggered functional tests +142/-0

Add composite action to gate PR-triggered functional tests

• Introduces a composite GitHub Action that runs the authorization script and manages a sticky PR comment. Clears stale ok-to-test labels and updates the comment when authorization changes.

.github/actions/check-e2e-authorization/action.yml

check-e2e-authorization.shImplement ok-to-test authorization and stale-label cleanup logic +126/-0

Implement ok-to-test authorization and stale-label cleanup logic

• Adds a bash gate that authorizes trusted associations or verified write permissions, otherwise requires a fresh ok-to-test label after the last push. Removes stale labels, emits structured outputs, and fails closed on API/script errors without failing the job.

.github/scripts/check-e2e-authorization.sh

functional-tests.ymlAdd Functional Tests workflow with pull_request_target gate and eval execution +268/-0

Add Functional Tests workflow with pull_request_target gate and eval execution

• Adds a CI workflow that gates pull_request_target runs via a dedicated job with pull-requests:write, then runs tests in a separate job with limited permissions. Installs eval tooling and external CLIs (fullsend/OpenShell), configures rootless Podman, authenticates to GCP via WIF, runs triage functional tests, scrubs secrets, and uploads artifacts.

.github/workflows/functional-tests.yml

.gitmodulesRegister agent-eval-harness as a submodule +3/-0

Register agent-eval-harness as a submodule

• Adds an eval/.agent-eval-harness submodule entry to consume the upstream harness code used for workspace/execute/score.

.gitmodules

.agent-eval-harnessAdd agent-eval-harness submodule pointer +1/-0

Add agent-eval-harness submodule pointer

• Pins the agent-eval-harness submodule to a specific commit for the functional test pipeline.

eval/.agent-eval-harness

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (4) 📘 Rule violations (0) 📜 Skill insights (5)

Context used
✅ Compliance rules (platform): 55 rules
✅ Skills: 4 invoked
  code-review
  code-implementation
  pr-review
  docs-review

Grey Divider


Action required

1. ::warning:: uses unsanitized FILE_COUNT 📜 Skill insight ⛨ Security
Description
The workflow emits a GitHub Actions workflow command with an interpolated variable ($FILE_COUNT)
that is not sanitized for :: sequences, encoded newlines (%0A/%0D), ANSI escapes, or control
characters. This can enable workflow-command injection if the value is ever attacker-influenced or
becomes non-numeric in future edits.
Code

.github/workflows/functional-tests.yml[R98-101]

+            FILE_COUNT=$(echo "$FILES" | wc -l)
+            if [ "$FILE_COUNT" -ge 300 ]; then
+              echo "::warning::Compare API returned $FILE_COUNT files (possible truncation at 300) — running functional tests as a precaution"
+              echo "relevant=true" >> "$GITHUB_OUTPUT"
Evidence
PR Compliance ID 1538382 requires that all variables interpolated into workflow commands (like
::warning::) be sanitized individually. The workflow command at
.github/workflows/functional-tests.yml:100 interpolates $FILE_COUNT directly without any
sanitization function.

.github/workflows/functional-tests.yml[98-101]
Skill: pr-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The workflow prints a `::warning::` command that interpolates `FILE_COUNT` without sanitizing it, which violates the requirement that every interpolated value in GHA workflow commands be individually sanitized.

## Issue Context
In `.github/workflows/functional-tests.yml`, `FILE_COUNT` is inserted directly into `echo "::warning::..."`.

## Fix Focus Areas
- .github/workflows/functional-tests.yml[98-101]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. pull-requests: write unjustified 📜 Skill insight ⛨ Security
Description
The new workflow grants pull-requests: write without an explicit linked issue/ADR justifying the
permission scope. This violates the least-privilege requirement for permission manifest changes.
Code

.github/workflows/functional-tests.yml[R45-47]

+    permissions:
+      contents: read
+      pull-requests: write
Evidence
PR Compliance ID 1538316 requires permission expansions to follow least privilege and include
explicit justification (linked issue/ADR). The workflow adds a permissions: block granting
pull-requests: write in the gate job.

.github/workflows/functional-tests.yml[45-47]
Skill: code-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The workflow introduces `pull-requests: write` which is a permission expansion and must be least-privilege with explicit justification.

## Issue Context
The gate job needs to post comments and remove labels; these operations may be possible with narrower scopes (e.g., `issues: write` plus `pull-requests: read`) depending on the exact API usage.

## Fix Focus Areas
- .github/workflows/functional-tests.yml[45-47]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Protected .github files modified 📜 Skill insight § Compliance
Description
This PR modifies protected governance/infrastructure paths under .github/, which must not be
auto-approved and requires explicit human review. The change introduces new CI workflow and
authorization automation under a protected directory.
Code

.github/workflows/functional-tests.yml[R1-6]

+name: Functional Tests
+
+# PR-triggered functional tests use pull_request_target so fork PRs receive
+# secrets.  Authorization runs in a separate gate job (base checkout only)
+# before the test job checks out the PR head — same pattern as fullsend e2e.
+
Evidence
PR Compliance ID 1538392 lists .github/ as a protected path; any PR modifying it must raise a
finding and must not be auto-approved. This PR adds a new workflow under .github/workflows/.

.github/workflows/functional-tests.yml[1-6]
Skill: pr-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Protected governance/infrastructure files under `.github/` were modified; this must be surfaced as requiring explicit human approval and should be protected by repo mechanisms.

## Issue Context
The PR adds/changes workflow and automation under `.github/`.

## Fix Focus Areas
- .github/workflows/functional-tests.yml[1-6]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (3)
4. Missing linked issue reference 📜 Skill insight § Compliance
Description
This PR introduces substantial new CI/workflow + evaluation harness logic, but the provided PR
metadata does not include a linked issue authorizing the non-trivial work. Non-trivial changes must
have explicit authorization via a linked issue.
Code

.github/workflows/functional-tests.yml[R1-34]

+name: Functional Tests
+
+# PR-triggered functional tests use pull_request_target so fork PRs receive
+# secrets.  Authorization runs in a separate gate job (base checkout only)
+# before the test job checks out the PR head — same pattern as fullsend e2e.
+
+permissions: {}
+
+on:
+  push:
+    branches: [main]
+    # SYNC-WITH: grep regex in "Check for functional-test-relevant changes" step
+    paths:
+      - 'eval/**'
+      - 'agents/**'
+      - 'harness/**'
+      - 'scripts/**'
+      - 'schemas/**'
+      - '.github/workflows/functional-tests.yml'
+      - '.github/scripts/**'
+  pull_request_target:
+    types: [opened, synchronize, reopened, labeled]
+  merge_group:
+  workflow_dispatch:
+
+concurrency:
+  group: >-
+    ${{ github.event_name == 'pull_request_target'
+        && format('functional-{0}', github.event.pull_request.number)
+        || format('{0}-{1}', github.workflow, github.ref) }}
+  cancel-in-progress: >-
+    ${{ github.event_name == 'pull_request_target'
+        || github.ref != 'refs/heads/main' }}
+
Evidence
PR Compliance ID 1538390 requires a linked issue for non-trivial changes. The diff shows a large new
workflow and associated automation, meeting the non-trivial threshold, but no authorizing issue is
referenced in the provided PR information.

.github/workflows/functional-tests.yml[1-34]
Skill: pr-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Non-trivial changes require an explicitly linked issue authorizing the work.

## Issue Context
This PR adds a new functional test workflow and supporting scripts/config, which is non-trivial in scope.

## Fix Focus Areas
- .github/workflows/functional-tests.yml[1-34]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Needs gate skips tests 🐞 Bug ≡ Correctness
Description
In .github/workflows/functional-tests.yml, functional-tests declares needs: gate but gate
only runs for pull_request_target, so for push, merge_group, and workflow_dispatch the gate
job is skipped and the downstream functional test job can be skipped as well.
Code

.github/workflows/functional-tests.yml[R9-74]

+on:
+  push:
+    branches: [main]
+    # SYNC-WITH: grep regex in "Check for functional-test-relevant changes" step
+    paths:
+      - 'eval/**'
+      - 'agents/**'
+      - 'harness/**'
+      - 'scripts/**'
+      - 'schemas/**'
+      - '.github/workflows/functional-tests.yml'
+      - '.github/scripts/**'
+  pull_request_target:
+    types: [opened, synchronize, reopened, labeled]
+  merge_group:
+  workflow_dispatch:
+
+concurrency:
+  group: >-
+    ${{ github.event_name == 'pull_request_target'
+        && format('functional-{0}', github.event.pull_request.number)
+        || format('{0}-{1}', github.workflow, github.ref) }}
+  cancel-in-progress: >-
+    ${{ github.event_name == 'pull_request_target'
+        || github.ref != 'refs/heads/main' }}
+
+jobs:
+  gate:
+    # Separate job so pull-requests: write stays out of the job that checks
+    # out fork head and runs tests with secrets.
+    # Never checkout github.event.pull_request.head.sha here.
+    if: >-
+      github.event_name == 'pull_request_target' &&
+      (github.event.action != 'labeled' || github.event.label.name == 'ok-to-test')
+    runs-on: ubuntu-24.04
+    timeout-minutes: 5
+    permissions:
+      contents: read
+      pull-requests: write
+    outputs:
+      authorized: ${{ steps.auth.outputs.authorized }}
+    steps:
+      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+        with:
+          ref: ${{ github.sha }}  # Base branch only — never checkout PR head in gate
+
+      - name: Check PR authorization
+        id: auth
+        uses: ./.github/actions/check-e2e-authorization
+        with:
+          pr_number: ${{ github.event.pull_request.number }}
+          repository: ${{ github.repository }}
+          pr_updated_at: ${{ github.event.pull_request.updated_at }}
+          event_action: ${{ github.event.action }}
+          pr_author_association: ${{ github.event.pull_request.author_association }}
+          pr_author_login: ${{ github.event.pull_request.user.login }}
+
+  functional-tests:
+    # For pull_request_target, runs only when gate sets authorized=true.
+    # Do not treat a skipped gate as authorized.
+    # This job checks out untrusted PR head code — no pull-requests: write here.
+    needs: gate
+    if: >-
+      !cancelled() &&
+      (github.event_name != 'pull_request_target' || needs.gate.outputs.authorized == 'true')
+    runs-on: ubuntu-24.04
Evidence
The workflow is triggered on push, merge_group, and workflow_dispatch, but the gate job is
guarded by an if: that requires pull_request_target. The functional-tests job still requires
gate via needs: gate.

.github/workflows/functional-tests.yml[9-25]
.github/workflows/functional-tests.yml[36-43]
.github/workflows/functional-tests.yml[66-74]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`functional-tests` has `needs: gate`, but `gate` is conditional and only executes for `pull_request_target`. When the workflow is triggered by `push`, `merge_group`, or `workflow_dispatch`, the `gate` job is skipped, which can prevent `functional-tests` from running.

## Issue Context
This workflow is explicitly configured to run on non-PR triggers (`push` to main, `merge_group`, and `workflow_dispatch`), so the pipeline should not depend on a job that never runs for those events.

## Fix Focus Areas
- .github/workflows/functional-tests.yml[9-74]

### Suggested implementation direction
- Make `gate` run on all triggers (remove the job-level `if:`), and:
 - For non-`pull_request_target` events, set `authorized=true` via a small shell step that writes to `$GITHUB_OUTPUT`.
 - For `pull_request_target`, keep the existing authorization logic.
- Keep `functional-tests.needs: gate` and its existing `if:` condition so PR gating behavior remains unchanged.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Missing issues:write permission 🐞 Bug ⛨ Security
Description
The gate job grants pull-requests: write but not issues: write, yet the authorization
composite action deletes the ok-to-test label and creates/patches PR comments via Issues
endpoints, which can cause the gate to fail or always return reason=error.
Code

.github/workflows/functional-tests.yml[R45-48]

+    permissions:
+      contents: read
+      pull-requests: write
+    outputs:
Evidence
gate only grants contents: read and pull-requests: write, but the composite action uses Issues
comment endpoints and the script deletes an issue label via Issues APIs. Another workflow in this
repo grants issues: write when performing similar PR label/comment operations, indicating this
permission is expected for those actions.

.github/workflows/functional-tests.yml[45-48]
.github/actions/check-e2e-authorization/action.yml[57-113]
.github/scripts/check-e2e-authorization.sh[86-112]
.github/workflows/fullsend.yaml[66-83]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The authorization gate performs label deletion and sticky comment creation/updates using `repos/.../issues/...` endpoints. The `gate` job currently does not grant `issues: write`, so these API calls can 403 and break authorization behavior.

## Issue Context
The repo already demonstrates that PR label/comment operations are modeled as Issues permissions (see `fullsend.yaml` stop-fix job permissions).

## Fix Focus Areas
- .github/workflows/functional-tests.yml[45-48]
- .github/actions/check-e2e-authorization/action.yml[57-142]
- .github/scripts/check-e2e-authorization.sh[86-113]

### Suggested implementation direction
- Add `issues: write` to `jobs.gate.permissions`.
- (Optional hardening) Add `pull-requests: read` / `issues: read` as needed for the read-only endpoints used by the gate script (events listing, PR fetch), but the critical missing permission is `issues: write` for label/comment mutations.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

7. TODO in eval.yaml 📜 Skill insight ✧ Quality
Description
A new TODO comment was introduced in committed code/config, which is disallowed by the checklist.
Leaving TODOs in committed artifacts creates ambiguous, untracked work and can mask incomplete
compliance decisions.
Code

eval/triage/eval.yaml[R176-180]

+thresholds:
+  triage_quality:
+    # TODO: raise to 3.5 once the triage agent critically examines source
+    # code instead of accepting issue descriptions at face value.
+    min_mean: 2.5
Evidence
PR Compliance ID 1538342 prohibits introducing TODO comments. The thresholds.triage_quality
section contains a newly added # TODO: comment.

eval/triage/eval.yaml[176-180]
Skill: code-implementation

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A `TODO` comment was introduced in `eval/triage/eval.yaml`, which violates the prohibition on TODO/FIXME/HACK comments in committed code.

## Issue Context
The comment indicates an intended future threshold change.

## Fix Focus Areas
- eval/triage/eval.yaml[176-180]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. Unpinned binary downloads 🐞 Bug ⛨ Security
Description
The workflow downloads and executes fullsend and openshell from the latest GitHub releases and
downloads yq via a direct URL without checksum/signature verification, making CI runs
non-reproducible and increasing supply-chain risk.
Code

.github/workflows/functional-tests.yml[R139-184]

+      - name: Install yq
+        if: steps.changes.outputs.relevant != 'false'
+        run: |
+          curl -sSfL "https://github.com/mikefarah/yq/releases/download/v4.47.1/yq_linux_amd64" -o /usr/local/bin/yq
+          chmod +x /usr/local/bin/yq
+
+      - name: Configure git identity
+        if: steps.changes.outputs.relevant != 'false'
+        run: |
+          git config --global user.name "fullsend-eval[bot]"
+          git config --global user.email "fullsend-eval[bot]@users.noreply.github.com"
+
+      - name: Install fullsend
+        if: steps.changes.outputs.relevant != 'false'
+        env:
+          GH_TOKEN: ${{ github.token }}
+        run: |
+          FULLSEND_VERSION="${FULLSEND_VERSION:-latest}"
+          if [ "$FULLSEND_VERSION" = "latest" ]; then
+            FULLSEND_VERSION=$(gh release view --repo fullsend-ai/fullsend --json tagName -q .tagName)
+          fi
+          VERSION_NUM="${FULLSEND_VERSION#v}"
+          TARBALL="fullsend_${VERSION_NUM}_linux_amd64.tar.gz"
+          gh release download "$FULLSEND_VERSION" \
+            --repo fullsend-ai/fullsend \
+            --pattern "$TARBALL" \
+            --dir /tmp
+          tar xzf "/tmp/${TARBALL}" -C /usr/local/bin fullsend
+          chmod +x /usr/local/bin/fullsend
+          fullsend version
+
+      - name: Install OpenShell CLI
+        if: steps.changes.outputs.relevant != 'false'
+        env:
+          GH_TOKEN: ${{ github.token }}
+        run: |
+          OPENSHELL_VERSION="${OPENSHELL_VERSION:-latest}"
+          if [ "$OPENSHELL_VERSION" = "latest" ]; then
+            OPENSHELL_VERSION=$(gh release view --repo NVIDIA/OpenShell --json tagName -q .tagName)
+          fi
+          gh release download "$OPENSHELL_VERSION" \
+            --repo NVIDIA/OpenShell \
+            --pattern "openshell-linux-amd64" \
+            --dir /tmp
+          install -m 0755 /tmp/openshell-linux-amd64 /usr/local/bin/openshell
+
Evidence
The workflow explicitly resolves FULLSEND_VERSION/OPENSHELL_VERSION to latest at runtime and
then downloads and executes the binaries; yq is also fetched via curl without integrity
verification.

.github/workflows/functional-tests.yml[139-184]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The workflow installs executable binaries (yq, fullsend, OpenShell) by downloading artifacts at runtime, including using `latest` tags, and does not verify integrity (checksums/signatures). This allows silent behavior changes between runs and expands the blast radius of an upstream compromise.

## Issue Context
This job later runs code with powerful credentials (`EVAL_GH_TOKEN`, GCP auth via OIDC), so minimizing unverified executable downloads is important.

## Fix Focus Areas
- .github/workflows/functional-tests.yml[139-184]

### Suggested implementation direction
- Pin `FULLSEND_VERSION` and `OPENSHELL_VERSION` to known-good tags (ideally as repo vars/inputs).
- Verify downloaded artifacts via published `sha256` files or hardcoded expected hashes.
- Prefer package-manager installs where possible (or GitHub Actions that verify checksums).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. Interactive repo creation 🐞 Bug ☼ Reliability
Description
eval/scripts/setup-fixture.sh calls gh repo create without an explicit non-interactive
confirmation flag, which can prompt/fail in CI and prevent fixtures (and thus tests) from being
created.
Code

eval/scripts/setup-fixture.sh[R54-61]

+# --- Create ephemeral repo ---
+uuid=$(uuidgen | tr '[:upper:]' '[:lower:]' | cut -c1-8)
+CASE_ID_SAFE=$(basename "$CASE_SOURCE_DIR" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g')
+repo_name="eval-${CASE_ID_SAFE}-${uuid}"
+EPHEMERAL_REPO="${EVAL_ORG}/${repo_name}"
+
+gh repo create "$EPHEMERAL_REPO" --public --description "Ephemeral eval repo (auto-deleted)"
+echo "Created repo: $EPHEMERAL_REPO"
Evidence
The setup hook creates the ephemeral repo with gh repo create and does not pass any explicit
confirmation/non-interactive option.

eval/scripts/setup-fixture.sh[54-67]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The functional test harness creates ephemeral repos in CI. The current `gh repo create` invocation does not include an explicit non-interactive confirmation flag, which risks a prompt or failure in a non-TTY environment.

## Issue Context
This script runs as a `before_each` hook; if it blocks/fails, the entire eval run fails.

## Fix Focus Areas
- eval/scripts/setup-fixture.sh[54-80]

### Suggested implementation direction
- Add `--confirm` to `gh repo create`.
- Optionally set `GH_PROMPT_DISABLED=1` (or equivalent) in the workflow/hook environment to ensure CI never prompts.
- Consider adding a cleanup trap to delete the repo if later steps in the hook fail after creation.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment on lines +98 to +101
FILE_COUNT=$(echo "$FILES" | wc -l)
if [ "$FILE_COUNT" -ge 300 ]; then
echo "::warning::Compare API returned $FILE_COUNT files (possible truncation at 300) — running functional tests as a precaution"
echo "relevant=true" >> "$GITHUB_OUTPUT"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. ::warning:: uses unsanitized file_count 📜 Skill insight ⛨ Security

The workflow emits a GitHub Actions workflow command with an interpolated variable ($FILE_COUNT)
that is not sanitized for :: sequences, encoded newlines (%0A/%0D), ANSI escapes, or control
characters. This can enable workflow-command injection if the value is ever attacker-influenced or
becomes non-numeric in future edits.
Agent Prompt
## Issue description
The workflow prints a `::warning::` command that interpolates `FILE_COUNT` without sanitizing it, which violates the requirement that every interpolated value in GHA workflow commands be individually sanitized.

## Issue Context
In `.github/workflows/functional-tests.yml`, `FILE_COUNT` is inserted directly into `echo "::warning::..."`.

## Fix Focus Areas
- .github/workflows/functional-tests.yml[98-101]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +45 to +47
permissions:
contents: read
pull-requests: write

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. pull-requests: write unjustified 📜 Skill insight ⛨ Security

The new workflow grants pull-requests: write without an explicit linked issue/ADR justifying the
permission scope. This violates the least-privilege requirement for permission manifest changes.
Agent Prompt
## Issue description
The workflow introduces `pull-requests: write` which is a permission expansion and must be least-privilege with explicit justification.

## Issue Context
The gate job needs to post comments and remove labels; these operations may be possible with narrower scopes (e.g., `issues: write` plus `pull-requests: read`) depending on the exact API usage.

## Fix Focus Areas
- .github/workflows/functional-tests.yml[45-47]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +1 to +6
name: Functional Tests

# PR-triggered functional tests use pull_request_target so fork PRs receive
# secrets. Authorization runs in a separate gate job (base checkout only)
# before the test job checks out the PR head — same pattern as fullsend e2e.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

3. Protected .github files modified 📜 Skill insight § Compliance

This PR modifies protected governance/infrastructure paths under .github/, which must not be
auto-approved and requires explicit human review. The change introduces new CI workflow and
authorization automation under a protected directory.
Agent Prompt
## Issue description
Protected governance/infrastructure files under `.github/` were modified; this must be surfaced as requiring explicit human approval and should be protected by repo mechanisms.

## Issue Context
The PR adds/changes workflow and automation under `.github/`.

## Fix Focus Areas
- .github/workflows/functional-tests.yml[1-6]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread eval/triage/eval.yaml
Comment on lines +176 to +180
thresholds:
triage_quality:
# TODO: raise to 3.5 once the triage agent critically examines source
# code instead of accepting issue descriptions at face value.
min_mean: 2.5

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

4. Todo in eval.yaml 📜 Skill insight ✧ Quality

A new TODO comment was introduced in committed code/config, which is disallowed by the checklist.
Leaving TODOs in committed artifacts creates ambiguous, untracked work and can mask incomplete
compliance decisions.
Agent Prompt
## Issue description
A `TODO` comment was introduced in `eval/triage/eval.yaml`, which violates the prohibition on TODO/FIXME/HACK comments in committed code.

## Issue Context
The comment indicates an intended future threshold change.

## Fix Focus Areas
- eval/triage/eval.yaml[176-180]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +1 to +34
name: Functional Tests

# PR-triggered functional tests use pull_request_target so fork PRs receive
# secrets. Authorization runs in a separate gate job (base checkout only)
# before the test job checks out the PR head — same pattern as fullsend e2e.

permissions: {}

on:
push:
branches: [main]
# SYNC-WITH: grep regex in "Check for functional-test-relevant changes" step
paths:
- 'eval/**'
- 'agents/**'
- 'harness/**'
- 'scripts/**'
- 'schemas/**'
- '.github/workflows/functional-tests.yml'
- '.github/scripts/**'
pull_request_target:
types: [opened, synchronize, reopened, labeled]
merge_group:
workflow_dispatch:

concurrency:
group: >-
${{ github.event_name == 'pull_request_target'
&& format('functional-{0}', github.event.pull_request.number)
|| format('{0}-{1}', github.workflow, github.ref) }}
cancel-in-progress: >-
${{ github.event_name == 'pull_request_target'
|| github.ref != 'refs/heads/main' }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

5. Missing linked issue reference 📜 Skill insight § Compliance

This PR introduces substantial new CI/workflow + evaluation harness logic, but the provided PR
metadata does not include a linked issue authorizing the non-trivial work. Non-trivial changes must
have explicit authorization via a linked issue.
Agent Prompt
## Issue description
Non-trivial changes require an explicitly linked issue authorizing the work.

## Issue Context
This PR adds a new functional test workflow and supporting scripts/config, which is non-trivial in scope.

## Fix Focus Areas
- .github/workflows/functional-tests.yml[1-34]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +9 to +74
on:
push:
branches: [main]
# SYNC-WITH: grep regex in "Check for functional-test-relevant changes" step
paths:
- 'eval/**'
- 'agents/**'
- 'harness/**'
- 'scripts/**'
- 'schemas/**'
- '.github/workflows/functional-tests.yml'
- '.github/scripts/**'
pull_request_target:
types: [opened, synchronize, reopened, labeled]
merge_group:
workflow_dispatch:

concurrency:
group: >-
${{ github.event_name == 'pull_request_target'
&& format('functional-{0}', github.event.pull_request.number)
|| format('{0}-{1}', github.workflow, github.ref) }}
cancel-in-progress: >-
${{ github.event_name == 'pull_request_target'
|| github.ref != 'refs/heads/main' }}

jobs:
gate:
# Separate job so pull-requests: write stays out of the job that checks
# out fork head and runs tests with secrets.
# Never checkout github.event.pull_request.head.sha here.
if: >-
github.event_name == 'pull_request_target' &&
(github.event.action != 'labeled' || github.event.label.name == 'ok-to-test')
runs-on: ubuntu-24.04
timeout-minutes: 5
permissions:
contents: read
pull-requests: write
outputs:
authorized: ${{ steps.auth.outputs.authorized }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.sha }} # Base branch only — never checkout PR head in gate

- name: Check PR authorization
id: auth
uses: ./.github/actions/check-e2e-authorization
with:
pr_number: ${{ github.event.pull_request.number }}
repository: ${{ github.repository }}
pr_updated_at: ${{ github.event.pull_request.updated_at }}
event_action: ${{ github.event.action }}
pr_author_association: ${{ github.event.pull_request.author_association }}
pr_author_login: ${{ github.event.pull_request.user.login }}

functional-tests:
# For pull_request_target, runs only when gate sets authorized=true.
# Do not treat a skipped gate as authorized.
# This job checks out untrusted PR head code — no pull-requests: write here.
needs: gate
if: >-
!cancelled() &&
(github.event_name != 'pull_request_target' || needs.gate.outputs.authorized == 'true')
runs-on: ubuntu-24.04

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

6. Needs gate skips tests 🐞 Bug ≡ Correctness

In .github/workflows/functional-tests.yml, functional-tests declares needs: gate but gate
only runs for pull_request_target, so for push, merge_group, and workflow_dispatch the gate
job is skipped and the downstream functional test job can be skipped as well.
Agent Prompt
## Issue description
`functional-tests` has `needs: gate`, but `gate` is conditional and only executes for `pull_request_target`. When the workflow is triggered by `push`, `merge_group`, or `workflow_dispatch`, the `gate` job is skipped, which can prevent `functional-tests` from running.

## Issue Context
This workflow is explicitly configured to run on non-PR triggers (`push` to main, `merge_group`, and `workflow_dispatch`), so the pipeline should not depend on a job that never runs for those events.

## Fix Focus Areas
- .github/workflows/functional-tests.yml[9-74]

### Suggested implementation direction
- Make `gate` run on all triggers (remove the job-level `if:`), and:
  - For non-`pull_request_target` events, set `authorized=true` via a small shell step that writes to `$GITHUB_OUTPUT`.
  - For `pull_request_target`, keep the existing authorization logic.
- Keep `functional-tests.needs: gate` and its existing `if:` condition so PR gating behavior remains unchanged.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +45 to +48
permissions:
contents: read
pull-requests: write
outputs:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

7. Missing issues:write permission 🐞 Bug ⛨ Security

The gate job grants pull-requests: write but not issues: write, yet the authorization
composite action deletes the ok-to-test label and creates/patches PR comments via Issues
endpoints, which can cause the gate to fail or always return reason=error.
Agent Prompt
## Issue description
The authorization gate performs label deletion and sticky comment creation/updates using `repos/.../issues/...` endpoints. The `gate` job currently does not grant `issues: write`, so these API calls can 403 and break authorization behavior.

## Issue Context
The repo already demonstrates that PR label/comment operations are modeled as Issues permissions (see `fullsend.yaml` stop-fix job permissions).

## Fix Focus Areas
- .github/workflows/functional-tests.yml[45-48]
- .github/actions/check-e2e-authorization/action.yml[57-142]
- .github/scripts/check-e2e-authorization.sh[86-113]

### Suggested implementation direction
- Add `issues: write` to `jobs.gate.permissions`.
- (Optional hardening) Add `pull-requests: read` / `issues: read` as needed for the read-only endpoints used by the gate script (events listing, PR fetch), but the critical missing permission is `issues: write` for label/comment mutations.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +139 to +184
- name: Install yq
if: steps.changes.outputs.relevant != 'false'
run: |
curl -sSfL "https://github.com/mikefarah/yq/releases/download/v4.47.1/yq_linux_amd64" -o /usr/local/bin/yq
chmod +x /usr/local/bin/yq

- name: Configure git identity
if: steps.changes.outputs.relevant != 'false'
run: |
git config --global user.name "fullsend-eval[bot]"
git config --global user.email "fullsend-eval[bot]@users.noreply.github.com"

- name: Install fullsend
if: steps.changes.outputs.relevant != 'false'
env:
GH_TOKEN: ${{ github.token }}
run: |
FULLSEND_VERSION="${FULLSEND_VERSION:-latest}"
if [ "$FULLSEND_VERSION" = "latest" ]; then
FULLSEND_VERSION=$(gh release view --repo fullsend-ai/fullsend --json tagName -q .tagName)
fi
VERSION_NUM="${FULLSEND_VERSION#v}"
TARBALL="fullsend_${VERSION_NUM}_linux_amd64.tar.gz"
gh release download "$FULLSEND_VERSION" \
--repo fullsend-ai/fullsend \
--pattern "$TARBALL" \
--dir /tmp
tar xzf "/tmp/${TARBALL}" -C /usr/local/bin fullsend
chmod +x /usr/local/bin/fullsend
fullsend version

- name: Install OpenShell CLI
if: steps.changes.outputs.relevant != 'false'
env:
GH_TOKEN: ${{ github.token }}
run: |
OPENSHELL_VERSION="${OPENSHELL_VERSION:-latest}"
if [ "$OPENSHELL_VERSION" = "latest" ]; then
OPENSHELL_VERSION=$(gh release view --repo NVIDIA/OpenShell --json tagName -q .tagName)
fi
gh release download "$OPENSHELL_VERSION" \
--repo NVIDIA/OpenShell \
--pattern "openshell-linux-amd64" \
--dir /tmp
install -m 0755 /tmp/openshell-linux-amd64 /usr/local/bin/openshell

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

8. Unpinned binary downloads 🐞 Bug ⛨ Security

The workflow downloads and executes fullsend and openshell from the latest GitHub releases and
downloads yq via a direct URL without checksum/signature verification, making CI runs
non-reproducible and increasing supply-chain risk.
Agent Prompt
## Issue description
The workflow installs executable binaries (yq, fullsend, OpenShell) by downloading artifacts at runtime, including using `latest` tags, and does not verify integrity (checksums/signatures). This allows silent behavior changes between runs and expands the blast radius of an upstream compromise.

## Issue Context
This job later runs code with powerful credentials (`EVAL_GH_TOKEN`, GCP auth via OIDC), so minimizing unverified executable downloads is important.

## Fix Focus Areas
- .github/workflows/functional-tests.yml[139-184]

### Suggested implementation direction
- Pin `FULLSEND_VERSION` and `OPENSHELL_VERSION` to known-good tags (ideally as repo vars/inputs).
- Verify downloaded artifacts via published `sha256` files or hardcoded expected hashes.
- Prefer package-manager installs where possible (or GitHub Actions that verify checksums).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +54 to +61
# --- Create ephemeral repo ---
uuid=$(uuidgen | tr '[:upper:]' '[:lower:]' | cut -c1-8)
CASE_ID_SAFE=$(basename "$CASE_SOURCE_DIR" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g')
repo_name="eval-${CASE_ID_SAFE}-${uuid}"
EPHEMERAL_REPO="${EVAL_ORG}/${repo_name}"

gh repo create "$EPHEMERAL_REPO" --public --description "Ephemeral eval repo (auto-deleted)"
echo "Created repo: $EPHEMERAL_REPO"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

9. Interactive repo creation 🐞 Bug ☼ Reliability

eval/scripts/setup-fixture.sh calls gh repo create without an explicit non-interactive
confirmation flag, which can prompt/fail in CI and prevent fixtures (and thus tests) from being
created.
Agent Prompt
## Issue description
The functional test harness creates ephemeral repos in CI. The current `gh repo create` invocation does not include an explicit non-interactive confirmation flag, which risks a prompt or failure in a non-TTY environment.

## Issue Context
This script runs as a `before_each` hook; if it blocks/fails, the entire eval run fails.

## Fix Focus Areas
- eval/scripts/setup-fixture.sh[54-80]

### Suggested implementation direction
- Add `--confirm` to `gh repo create`.
- Optionally set `GH_PROMPT_DISABLED=1` (or equivalent) in the workflow/hook environment to ensure CI never prompts.
- Consider adding a cleanup trap to delete the repo if later steps in the hook fail after creation.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@fullsend-ai-review

Copy link
Copy Markdown

Review

Findings

High

  • [protected-path] .github/actions/check-e2e-authorization/action.yml, .github/scripts/check-e2e-authorization.sh, .github/workflows/functional-tests.yml — These files are under the .github/ protected path, which requires human approval. The PR has no linked issue justifying these changes. While the PR body describes the rationale (authorization gate for functional tests), protected-path changes require explicit issue-level authorization regardless of context.
    Remediation: Create a linked issue documenting the need for the authorization gate and functional test workflow infrastructure.

  • [missing-authorization] This PR adds 1,747 lines of new functional test infrastructure without a linked issue. Non-trivial changes require explicit authorization via a linked GitHub issue.
    Remediation: Create a GitHub issue describing the need for functional test infrastructure, the approach (agent-eval-harness integration), and the scope (triage agent initially). Link the issue to this PR.

Medium

  • [logic-error] eval/run-functional.sh:37 — The existence check for EVAL_YAML_SRC on line 40 is dead code. Line 37 runs yq ... "$EVAL_YAML_SRC" which fails under set -euo pipefail if the file is missing, terminating the script with an opaque yq error before the friendly error message on line 41 is reached.
    Remediation: Move the if [[ ! -f "$EVAL_YAML_SRC" ]] check to before line 35 (before the mktemp/yq block).

  • [secret-exposure] .github/workflows/functional-tests.yml — The EVAL_GH_TOKEN secret is exposed to the functional-tests job that checks out untrusted PR head code via pull_request_target. The authorization gate is the sole compensating control. If ok-to-test is applied to a malicious PR, the token can be exfiltrated.
    Remediation: Consider using a short-lived, minimally-scoped token (e.g., a GitHub App installation token scoped only to the eval org).

  • [secret-exposure] .github/workflows/functional-tests.yml — The job has id-token: write and checks out untrusted PR head code. Fork PR code (once authorized) can request a GitHub OIDC token to authenticate to GCP via WIF. The WIF provider's attribute conditions are the final defense.
    Remediation: Verify that the GCP WIF provider's attribute condition restricts token acceptance appropriately (e.g., to specific branches or actors).

  • [missing-documentation] README.md:31 — The Repository structure section does not document the new eval/ directory, and the Workflows table (line 49) does not include the new functional-tests.yml workflow.
    Remediation: Add eval/ to the repository structure list and add a row to the Workflows table.

  • [architectural-coherence-dependency-ownership] .gitmodules — The submodule references ralphbean/agent-eval-harness, a personal fork rather than an org-owned repository. Since this code is installed via pip and executed in CI with access to secrets, a supply-chain compromise of the personal account could give an attacker code execution in the trusted CI context.
    Remediation: Move agent-eval-harness to the fullsend-ai organization, or document the rationale for using a personal fork.

Low

  • [edge-case] .github/scripts/check-e2e-authorization.sh:80 — The fallback timestamp comparison uses updated_at (which advances on any PR activity) as a proxy for last push time. In the normal workflow path, PR_UPDATED_AT is always passed from the event payload, so this only affects unexpected invocations.

  • [missing-default-case] eval/scripts/run-fullsend.sh:44 — The case "$FIXTURE_TYPE" block handles issue and pull_request but has no * default case, unlike other scripts in this PR (capture-fixture.sh, setup-fixture.sh).

  • [path-filter-gap] .github/workflows/functional-tests.yml:14 — The push path filter and PR changes-detection grep regex include .github/scripts/** but omit .github/actions/**. Changes to the authorization action would not trigger or be detected by the workflow.

  • [missing-documentation] README.md — No documentation exists explaining how to run functional tests locally, what the test cases cover, or how to add new test cases.

@fullsend-ai-review fullsend-ai-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the review comment for full details.

Comment thread eval/run-functional.sh
# around this by rewriting dataset.path to an absolute path at runtime.
EVAL_YAML="$(mktemp "${EVAL_DIR}/${AGENT}/eval-runtime-XXXXXX.yaml")"
trap 'rm -f "$EVAL_YAML"' EXIT
yq ".dataset.path = \"${CASES_DIR}\"" "$EVAL_YAML_SRC" > "$EVAL_YAML"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[medium] logic-error

The existence check for EVAL_YAML_SRC on line 40 is dead code. Line 37 runs yq on EVAL_YAML_SRC which fails under set -euo pipefail if the file is missing, terminating the script before the friendly error message on line 41 is reached.

Suggested fix: Move the if [[ ! -f EVAL_YAML_SRC ]] check to before line 35 (before the mktemp/yq block).

fi

if is_trusted_author "${author_association}"; then
authorized=true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] edge-case

The fallback timestamp comparison uses updated_at (which advances on any PR activity) as a proxy for last push time. In the normal workflow path, PR_UPDATED_AT is always passed from the event payload, so this only affects unexpected invocations.

[[ -n "${TARGET_DIR:-}" && -d "${TARGET_DIR:-}" ]] && rm -rf "$TARGET_DIR"
}
trap cleanup EXIT

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] missing-default-case

The case FIXTURE_TYPE block handles issue and pull_request but has no default case, unlike other scripts in this PR.

branches: [main]
# SYNC-WITH: grep regex in "Check for functional-test-relevant changes" step
paths:
- 'eval/**'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] path-filter-gap

The push path filter and PR changes-detection grep regex include .github/scripts/** but omit .github/actions/**. Changes to the authorization action would not trigger or be detected by the workflow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant