Skip to content

test(eval scenario): add code agent functional eval scenario - #177

Merged
ascerra merged 11 commits into
mainfrom
eval/code-functional-test
Jul 29, 2026
Merged

test(eval scenario): add code agent functional eval scenario#177
ascerra merged 11 commits into
mainfrom
eval/code-functional-test

Conversation

@ascerra

@ascerra ascerra commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add eval/code/ with one tiny-calc case (001-fix-add) that asserts the code pipeline creates a PR touching calc.py
  • Export ISSUE_NUMBER + REPO_FULL_NAME for issue fixtures in run-fullsend.sh (required by pre-code.sh)
  • Capture pull_requests (+ changed files) in capture-fixture.sh so judges can score PR outcomes

Closes #180

Test plan

  • ./eval/lint-cases.sh code
  • Functional-tests (code) on review-fix commit f7408a6

Functional test proof

CI run: https://github.com/fullsend-ai/agents/actions/runs/29424512121
Job: functional-tests (code)pass (7m32s)

Ephemeral fixture (torn down after case)

Step Evidence
Repo halfsend/eval-001-fix-add-9a286d58
Issue https://github.com/halfsend/eval-001-fix-add-9a286d58/issues/1
Agent branch agent/1-fix-add-operator
PR created by post-script https://github.com/halfsend/eval-001-fix-add-9a286d58/pull/2

Judge results (summary.yaml from artifact eval-results-code)

Judge Pass rate Rationale
pr_created 1.0 PR created: …/pull/2
expected_files 1.0 All expected files present: ['calc.py']
forbidden_labels 1.0 No forbidden labels specified
max_turns 1.0 Turns OK: 12 ≤ 80
max_cost 1.0 Cost OK: ~$2.12 ≤ $8.00

Captured fixture state (excerpt)

{
  "fixture_type": "issue",
  "fixture_url": "https://github.com/halfsend/eval-001-fix-add-9a286d58/issues/1",
  "pull_requests": [
    {
      "number": 2,
      "state": "OPEN",
      "title": "fix(#1): fix add function to use addition operator",
      "url": "https://github.com/halfsend/eval-001-fix-add-9a286d58/pull/2",
      "head": "agent/1-fix-add-operator",
      "base": "main",
      "files": ["calc.py"],
      "files_fetch_failed": false
    }
  ]
}

Note: the halfsend eval repo is deleted by teardown-fixture.sh after capture, so the issue/PR URLs above are historical proof from the artifact, not live pages.

@ascerra
ascerra requested a review from a team as a code owner July 15, 2026 14:18
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 15, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 2:18 PM UTC · Ended 2:22 PM UTC
Commit: 5cd495a · View workflow run →

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add functional eval for code agent PR creation (tiny-calc case)

🧪 Tests ✨ Enhancement 🕐 40+ Minutes

Grey Divider

AI Description

• Add a new eval/code functional eval exercising the full pre→sandbox→post code pipeline.
• Introduce a tiny-calc issue fixture that expects a PR touching calc.py.
• Extend harness scripts to export issue env vars and capture created PRs + changed files.
Diagram

graph TD
  Case["code case (001)"] --> Eval["eval/code/eval.yaml"] --> Runner["run-fullsend.sh"] --> Agent["fullsend: code"] --> GH[("Ephemeral GitHub")]
  Case --> Repo["tiny-calc repo"]
  Eval --> Capture["capture-fixture.sh"] --> State["fixture-state.json"]
  GH --> Capture
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Fetch PRs + files via a single GraphQL call
  • ➕ Avoids N+1 gh pr view calls (one per PR) and reduces API/rate-limit risk
  • ➕ Simplifies error handling around empty PR lists and partial failures
  • ➖ More complex query construction and requires GraphQL familiarity
  • ➖ Harder to tweak quickly in bash compared to composing gh subcommands
2. Judge outcomes from local git state instead of GitHub PRs
  • ➕ Removes dependence on GitHub PR APIs when sandbox/permissions are constrained
  • ➕ Faster and deterministic (diff against base)
  • ➖ Stops validating the critical requirement that the post-script actually opens a PR
  • ➖ May miss metadata needed for scoring (PR URL/state)

Recommendation: The PR’s approach is appropriate for an end-to-end regression guard because it explicitly validates PR creation and records changed files for scoring. If GitHub API throttling/flakiness becomes an issue, switch PR+file enumeration to a single GraphQL query to cut API calls.

Files changed (9) +264 / -2

Tests (7) +232 / -0
annotations.yamlDefine expected PR outcome, budgets, and file-touch requirement +20/-0

Define expected PR outcome, budgets, and file-touch requirement

• Adds case annotations describing success criteria (PR must exist and touch 'calc.py') and sets turn/cost budgets. Includes a narrative expectation explaining the intended bug fix and the read-only sandbox constraint.

eval/code/cases/001-fix-add/annotations.yaml

input.yamlAdd issue fixture describing the add() bug and reproduction steps +22/-0

Add issue fixture describing the add() bug and reproduction steps

• Introduces an issue title/body fixture that reports 'add(2, 3)' returning '-1' and requests fixing 'add' to return 'a + b'. Acts as the prompt the code agent must resolve.

eval/code/cases/001-fix-add/input.yaml

repoPoint case at the tiny-calc fixture repository +1/-0

Point case at the tiny-calc fixture repository

• Adds a repo pointer to '../../repos/tiny-calc', wiring the case to the repo content the agent will see. This enables reusing the same fixture repo across cases.

eval/code/cases/001-fix-add/repo

eval.yamlAdd code functional eval definition with hooks and judges +169/-0

Add code functional eval definition with hooks and judges

• Defines the 'code-eval' suite: case-mode execution, env passthrough for GitHub/Vertex creds, and before/after hooks for fixture lifecycle. Adds judges to assert PR creation, expected file touches, forbidden labels, and budget compliance (turns/cost) with strict thresholds.

eval/code/eval.yaml

README.mdDocument tiny-calc as the code-eval fixture repo +3/-0

Document tiny-calc as the code-eval fixture repo

• Adds a brief README describing the fixture repository used by the functional eval.

eval/code/repos/tiny-calc/README.md

calc.pyAdd intentionally buggy add() implementation for the eval +6/-0

Add intentionally buggy add() implementation for the eval

• Introduces a minimal calculator module where 'add()' intentionally subtracts to create a deterministic fix target for the code agent.

eval/code/repos/tiny-calc/calc.py

test_calc.pyAdd tests that fail until add() is fixed +11/-0

Add tests that fail until add() is fixed

• Adds pytest coverage asserting correct behavior for positive and negative inputs. The tests enforce the intended fix and act as the repo’s regression signal.

eval/code/repos/tiny-calc/tests/test_calc.py

Other (2) +32 / -2
capture-fixture.shCapture PR list and changed files in fixture-state.json for judging +25/-1

Capture PR list and changed files in fixture-state.json for judging

• Extends issue fixture capture to also enumerate PRs in the ephemeral repo and attach the list of changed file paths per PR. Writes 'pull_requests' into 'output/fixture-state.json', handling empty PR lists without failing the script.

eval/scripts/capture-fixture.sh

run-fullsend.shExport ISSUE_NUMBER and REPO_FULL_NAME for issue-driven agents +7/-1

Export ISSUE_NUMBER and REPO_FULL_NAME for issue-driven agents

• Updates the env file generation to include 'ISSUE_NUMBER' and 'REPO_FULL_NAME' when running against issue fixtures. This supports code (and similar) agents whose pre-scripts require explicit issue/repo identifiers beyond 'GITHUB_ISSUE_URL'.

eval/scripts/run-fullsend.sh

@ascerra ascerra changed the title test(eval): add code agent functional eval test(eval scenario): add code agent functional eval scenario Jul 15, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 15, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 2:22 PM UTC · Completed 2:34 PM UTC
Commit: 7c93d88 · View workflow run →

@qodo-code-review

qodo-code-review Bot commented Jul 15, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (1)

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

Grey Divider


Action required

1. Fixture capture brittle ✓ Resolved 🐞 Bug ☼ Reliability
Description
In capture-fixture.sh, gh pr list runs under set -euo pipefail without a fallback, so any
transient failure aborts the after_each hook before output/fixture-state.json is written.
Separately, gh pr view failures are suppressed and coerced to [], which can make the
expected_files judge fail (or mis-report) even when a PR exists and actually changed the expected
file(s).
Code

eval/scripts/capture-fixture.sh[R34-46]

+    # Code agent post-script opens a PR; capture PRs + changed files for judges.
+    # Process substitutions avoid set -e/pipefail pitfalls with empty PR lists.
+    prs_json=$(gh pr list --repo "$EPHEMERAL_REPO" --state all --limit 20 \
+      --json number,title,url,state,headRefName,baseRefName)
+    pr_lines=()
+    while IFS= read -r pr; do
+      [[ -z "$pr" ]] && continue
+      num=$(echo "$pr" | jq -r '.number')
+      files=$(gh pr view "$num" --repo "$EPHEMERAL_REPO" --json files \
+        --jq '[.files[].path]' 2>/dev/null || true)
+      if [[ -z "$files" ]]; then
+        files='[]'
+      fi
Relevance

●●● Strong

Team has accepted fail-closed / script-hardening feedback around gh/jq under set -euo pipefail (PR
#94).

PR-#94

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The hook is set -euo pipefail, so an unhandled non-zero exit from gh pr list terminates the
script before it writes fixture-state.json. Additionally, the per-PR file capture explicitly
suppresses errors (2>/dev/null || true) and coerces failures to [], and the expected_files
judge uses these captured files arrays to decide pass/fail—so hidden capture failures can directly
change scoring outcomes.

eval/scripts/capture-fixture.sh[16-55]
eval/code/eval.yaml[31-107]

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

### Issue description
`eval/scripts/capture-fixture.sh` is used as an `after_each` hook to generate `output/fixture-state.json` for judges. The newly added PR capture path is brittle:
- `gh pr list` is executed under `set -euo pipefail` with no fallback, so a non-zero exit prevents writing `fixture-state.json`.
- Per-PR file fetching uses `2>/dev/null || true` and then substitutes `[]`, hiding errors and making judges (notably `expected_files`) operate on incorrect data.

### Issue Context
The code eval’s judges in `eval/code/eval.yaml` rely on `fixture-state.json` and specifically the per-PR `files` arrays for scoring.

### Fix Focus Areas
- eval/scripts/capture-fixture.sh[34-55]
- eval/code/eval.yaml[31-107]

### What to change
1. Make `gh pr list` best-effort and always produce a valid JSON array:
  - If `gh pr list` fails, emit a warning to stderr and set `prs_json='[]'` so the script still writes `fixture-state.json`.
2. Stop treating `gh pr view` failures as “no files changed”:
  - Capture exit status; if it fails, store something explicit (e.g., `files: null` and `files_error: "..."` or `files_fetch_failed: true`) rather than forcing `[]`.
  - Optionally retry `gh pr view` a small number of times to reduce flakiness.
3. Ensure `fixture-state.json` is always written with `pull_requests` present (even if empty) so scoring has a deterministic input.

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


2. No linked issue for change 📜 Skill insight § Compliance
Description
This PR introduces substantial new functionality (a new eval/code functional eval plus harness
script changes) but does not include an explicit linked issue authorizing the work. For non-trivial
changes, missing authorization via a linked issue is a compliance violation and increases
governance/audit risk.
Code

eval/code/eval.yaml[R1-39]

+name: code-eval
+description: >
+  Functional test of the fullsend code agent pipeline (pre → sandbox → post).
+  Validates the agent can implement a small issue and that the post-script
+  creates a PR. Acts as an end-to-end regression guard when sandbox GitHub
+  access is read-only (reads + local commits still work; write/push stays
+  on the runner).
+
+skill: code
+
+execution:
+  mode: case
+  timeout: 2100  # code harness timeout_minutes is 35; leave headroom for setup/teardown
+  parallelism: 1
+  env:
+    EVAL_ORG: $EVAL_ORG
+    GH_TOKEN: $GH_TOKEN
+    FULLSEND_DIR: $FULLSEND_DIR
+    EVAL_TIMEOUT: "2100"
+    GOOGLE_APPLICATION_CREDENTIALS: $GOOGLE_APPLICATION_CREDENTIALS
+    ANTHROPIC_VERTEX_PROJECT_ID: $ANTHROPIC_VERTEX_PROJECT_ID
+    GOOGLE_CLOUD_PROJECT: $GOOGLE_CLOUD_PROJECT
+    CLOUD_ML_REGION: $CLOUD_ML_REGION
+
+hooks:
+  before_each:
+    - command: "setup-fixture.sh"
+      timeout: 120
+      description: "Create ephemeral repo and issue fixture"
+
+  after_each:
+    - command: "capture-fixture.sh"
+      timeout: 60
+      description: "Capture issue + PR state for judges"
+    - command: "teardown-fixture.sh"
+      timeout: 30
+      on_failure: continue
+      description: "Delete ephemeral repo"
+
Relevance

●● Moderate

Repo values compliance justification, but no clear precedent enforcing “linked issue required” for
non-trivial PRs (PRs #25, #29).

PR-#25
PR-#29

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The checklist requires an explicit linked issue for non-trivial changes (PR Compliance ID 1538390).
The diff shows a new, sizable eval configuration and pipeline content being added (e.g.,
eval/code/eval.yaml), indicating the change is non-trivial and thus must be authorized via a
linked issue.

eval/code/eval.yaml[1-39]
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
This PR makes non-trivial changes but lacks an explicitly linked/authorizing issue reference.

## Issue Context
Compliance requires that non-trivial or structural work is authorized via a linked issue (e.g., `Fixes #123`, `Closes #123`, or an equivalent explicit issue link in the PR description).

## Fix Focus Areas
- eval/code/eval.yaml[1-39]

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


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment thread eval/code/eval.yaml
Comment thread eval/scripts/capture-fixture.sh Outdated
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review

Findings

Medium

  • [incomplete-fix] eval/fix/eval.yaml — The new_commit judge (~line 80) and expected_files judge (~line 108) still use outputs["files"]["output/fixture-state.json"] (direct dict access), while forbidden_labels was updated in this PR to use the safer .get() + early-return pattern. If fixture-state.json is missing from outputs, these two judges will raise a KeyError instead of returning a clean failure message.
    Remediation: Apply the same .get() + early-return pattern to new_commit and expected_files judges, matching the forbidden_labels pattern already in this file.

Low

  • [dead-config] eval/review/eval.yaml:41 — The runner.env block has no effect for runner: type: cli. The PR removes runner.env from fix/eval.yaml with an explanatory comment about CliRunner ignoring it, but leaves the identical pattern in review/eval.yaml. All vars listed in runner.env are already present in execution.env, so this is dead config rather than a missing-env bug.
    Remediation: Remove the runner.env block from eval/review/eval.yaml for consistency with fix/eval.yaml.

  • [scope-expansion] eval/scripts/run-fullsend.sh — The PR includes several changes beyond issue Add code agent functional eval (pre→sandbox→post PR creation) #180's stated acceptance criteria: a rewrite of emit_env's escaping logic, addition of a cost_usd alias to metrics output, and moving EVAL_TIMEOUT from runner.env to execution.env in eval.yaml files. These are drive-by bug fixes and infrastructure improvements discovered during implementation, acknowledged in the PR body.

  • [incomplete-doc] eval/README.md:10 — The "Running evals" section provides command examples for triage and review but not for the newly added code agent eval. The parenthetical at line 14 says "(review, triage, etc.)" without explicitly listing code.
    Remediation: Add a third example line: EVAL_ORG=my-org ./eval/run-functional.sh code and update the parenthetical to include code.

Previous run

Review

Findings

Low

  • [incomplete-example] eval/README.md:10 — The "Running evals" section provides examples for triage and review but not the newly added code agent. The existing text "Replace the agent name (review, triage, etc.) as needed" partially covers this, but adding code to the parenthetical list would improve discoverability for the new eval.
    Remediation: Update the parenthetical to include code (e.g., `review`, `triage`, `code`, etc.) and optionally add a third example line.
Previous run (2)

Review

Findings

Low

  • [incomplete-example] eval/README.md:10 — The "Running evals" section provides examples for running triage and review evals but omits the newly added code agent. Since this PR introduces eval/code/ with a complete eval suite, the examples should demonstrate how to run code evals alongside the existing agents.
    Remediation: Add a third example line: EVAL_ORG=my-org ./eval/run-functional.sh code

  • [field-name-mismatch] eval/scripts/run-fullsend.sh — The jq aliasing block creates cost_usd from total_cost_usd but not the reverse. The max_cost judge reads metrics.get("total_cost_usd"), which fullsend always writes, so this is not a current-path bug. However, if a future code path produced only cost_usd, the judge would get None.
    Remediation: Consider making the jq expression bidirectional for defensive completeness.

Previous run (3)

Review

Findings

Low

  • [incomplete-doc] eval/README.md:107 — The annotations.yaml schema description lists only labels, review expectations, max_turns, max_cost_usd, but annotations files already use additional fields (state, expected_files, code_expectations, triage_expectations, fix_expectations). The PR reinforces this gap by adding a new agent whose annotations use these undocumented fields.
    Remediation: Update the annotation schema description to include all supported fields: state, expected_files, labels, max_turns, max_cost_usd, and agent-specific expectation fields (code_expectations, triage_expectations, fix_expectations, review_expectations).

  • [incomplete-example] eval/README.md:10 — The "Running evals" section provides examples for running triage and review evals but omits the newly added code agent. Since this PR introduces eval/code/ with a complete eval suite, the examples should demonstrate how to run code evals alongside the existing agents.
    Remediation: Add a third example line: EVAL_ORG=my-org ./eval/run-functional.sh code

Previous run (4)

Review

Findings

Low

  • [incomplete-doc] eval/README.md:107 — The annotations.yaml schema description lists only labels, review expectations, max_turns, max_cost_usd, but annotations files now support agent-specific expectation fields (code_expectations, triage_expectations, fix_expectations) and expected_files. These fields are not documented. The PR adds eval/code/ with annotations.yaml that uses code_expectations and expected_files, reinforcing the gap.
    Remediation: Update the annotation schema description to include all supported fields: state, expected_files, labels, max_turns, max_cost_usd, and agent-specific expectation fields (code_expectations, triage_expectations, fix_expectations, review_expectations).
Previous run (5)

Review

Findings

Low

  • [edge-case] eval/code/eval.yaml:8 — The gap between execution.timeout (2300s) and EVAL_TIMEOUT (2250s) is only 50 seconds for the runner script's own pre/post overhead (git clone, metrics copy, trap cleanup). The fix eval maintains a 100-second gap (1800 - 1700). While 50 seconds is likely sufficient for these operations, it is tighter than the established pattern.
    Remediation: Consider increasing execution.timeout to 2350 to match the ~100-second gap pattern, though the current 50-second gap is likely adequate for the lightweight overhead.

  • [incomplete-doc] eval/README.md:107 — The annotations.yaml schema description lists only labels, review expectations, max_turns, max_cost_usd, but annotations files now support agent-specific expectation fields (code_expectations, triage_expectations, fix_expectations) and expected_files. These fields are not documented.
    Remediation: Update the annotation schema description to include all supported fields.

  • [missing-example] eval/README.md:11 — The "Running evals" section provides example commands for triage and review agents but does not include an example for the newly added code agent, even though the introduction (line 4) now mentions code/ as one of the agent directories.
    Remediation: Add a third example command: EVAL_ORG=my-org ./eval/run-functional.sh code

Previous run (6)

Review

Findings

Low

  • [incomplete-doc] eval/README.md:107 — The annotations.yaml schema description lists only labels, review expectations, max_turns, max_cost_usd, but annotations files now support agent-specific expectation fields (code_expectations, triage_expectations, fix_expectations) and expected_files. These fields are not documented.
    Remediation: Update the annotation schema description to include agent-specific expectation fields and expected_files.

  • [missing-example] eval/README.md:11 — The "Running evals" section provides example commands for triage and review agents but does not include an example for the newly added code agent, even though the introduction (line 4) now mentions code/ as one of the agent directories.
    Remediation: Add a third example command: EVAL_ORG=my-org ./eval/run-functional.sh code

Previous run (7)

Review

Findings

Low

  • [incomplete-doc] eval/README.md:107 — The annotations.yaml schema description lists only labels, review expectations, max_turns, max_cost_usd, but annotations files now support agent-specific expectation fields (code_expectations, triage_expectations, fix_expectations) and expected_files. These fields are not documented.
    Remediation: Update the annotation schema description to include agent-specific expectation fields and expected_files.

  • [missing-example] eval/README.md:11 — The "Running evals" section provides example commands for triage and review agents but does not include an example for the newly added code agent, even though the introduction (line 4) now mentions code/ as one of the agent directories.
    Remediation: Add a third example command: EVAL_ORG=my-org ./eval/run-functional.sh code

Previous run (8)

Review

Findings

Low

  • [dead-code] eval/code/cases/001-fix-add/annotations.yaml:16 — The code_expectations field is defined but never consumed by any judge in eval/code/eval.yaml. The comment on line 15 explicitly marks it as "Human reference only; not consumed by judges," so this is intentional — not an oversight.

  • [edge-case] eval/scripts/capture-fixture.sh:147gh pr list --limit 1 captures at most one PR from the ephemeral repo. For the current single-PR code agent use case this is correct, but if a future case expects the agent to create multiple PRs, only the first would be captured. The inline comment at lines 144-145 acknowledges this as a deliberate design decision.

  • [incomplete-doc] eval/README.md:107annotations.yaml now supports code_expectations and expected_files fields, but these are not listed in the annotation schema description on line 107.
    Remediation: Update the annotation schema description to include code_expectations and expected_files.

Previous run (9)

Review

Findings

Medium

  • [incomplete-doc] eval/README.md:77 — The "Derived (set automatically by the runner)" section documents PUSH_TOKEN and REVIEW_TOKEN but omits code/fix-specific variables now set by run-fullsend.sh: PUSH_TOKEN_SOURCE, CODE_ALLOWED_TARGET_BRANCHES, GITHUB_WORKSPACE, GIT_BOT_EMAIL. Additionally, ISSUE_NUMBER and REPO_FULL_NAME are now exported for issue fixtures (previously only for pull_request fixtures).
    Remediation: Add a table row or subsection documenting code/fix-specific environment variables.

Low

  • [dead-code] eval/code/cases/001-fix-add/annotations.yaml:9 — The code_expectations field is defined but never consumed by any judge in eval/code/eval.yaml. The comment on line 15 explicitly marks it as "Human reference only; not consumed by judges," so this is intentional — not an oversight.

  • [edge-case] eval/scripts/capture-fixture.sh:46gh pr list --limit 1 captures at most one PR from the ephemeral repo. For the current single-PR code agent use case this is correct, but if a future case expects the agent to create multiple PRs, only the first would be captured. The inline comment acknowledges this as a deliberate design decision.

  • [incomplete-doc] eval/README.md:121 — The PR adds a pull_requests field to fixture-state.json for issue fixtures (with nested structure: number, title, url, state, head, base, files, files_fetch_failed), but this new field is not mentioned in the documentation.
    Remediation: Document the fixture-state.json schema or mention the new pull_requests field.

  • [incomplete-doc] eval/README.md:107annotations.yaml now supports code_expectations and expected_files fields, but these are not listed in the annotation schema description on line 107.
    Remediation: Update the annotation schema description to include the new fields.

Previous run (10)

Review

Findings

Low

  • [edge-case] eval/scripts/capture-fixture.sh:48gh pr list --limit 1 captures at most one PR from the ephemeral repo. The inline comment at lines 44-47 acknowledges this as a deliberate design decision for the current single-PR use case and documents how to lift it.

  • [error-handling] eval/scripts/capture-fixture.sh:24 — In fetch_pr_files, the jq expression [.files[].path] will error if the files key is null (rather than an array). The 2>/dev/null suppresses this, causing unnecessary retries and a false files_fetch_failed: true. Using [(.files // [])[].path] would harden against this edge case.
    Remediation: Change the jq expression to [(.files // [])[].path].

  • [incomplete-doc] eval/README.md:4 — The PR adds eval/code/ but eval/README.md only lists triage/ and review/ as examples. Line 14 partially mitigates with generic guidance but the explicit list would benefit from including code/.
    Remediation: Update line 4 to include code/ in the directory list.

Previous run (11)

Review

Findings

Low

  • [incomplete-doc] eval/README.md:4 — The PR adds eval/code/ but eval/README.md only lists triage/ and review/ as examples. Line 13 mitigates this with "Replace the agent name (review, triage, etc.) as needed" but the explicit list would benefit from including code/.
    Remediation: Update line 4 to include code/ in the directory list.
Previous run (12)

Review

Findings

Low

  • [edge-case] eval/scripts/capture-fixture.sh:33 — In the issue path, comments_json is built via jq [.comments[] | ...] without null coalescing. The same script defensively uses ($issue.labels // [])[] for labels. While gh CLI returns {"comments":[]} for commentless issues (not an active bug), the inconsistency is worth fixing for defensive consistency.
    Remediation: Use [(.comments // [])[]] | ...] consistent with how labels are handled elsewhere in the script.

  • [error-handling-consistency] eval/scripts/capture-fixture.sh:39 — PR capture uses fallback handling on gh pr list failure (prs_json='[]'), while issue view commands fail fast under set -euo pipefail. The asymmetry is intentional (PR list is supplementary; issue view is primary) but undocumented.
    Remediation: Add a comment explaining why PR list is allowed to fail while issue view is not.

  • [injection/incomplete-escaping] eval/scripts/run-fullsend.sh:52emit_env escapes backslash, double-quote, and dollar-sign but not backtick characters. Risk is very low: the file is consumed by fullsend --env-file (not shell-sourced), fixture values are regex-validated, and tokens are unlikely to contain backticks.
    Remediation: Add backtick escaping after the dollar-sign line.

  • [incomplete-doc] eval/README.md:4 — The PR adds eval/code/ but eval/README.md only lists triage/ and review/ as examples. Line 13 mitigates this with "Replace the agent name (review, triage, etc.) as needed" but the explicit list would benefit from including code/.
    Remediation: Update line 4 to include code/ in the directory list.

Previous run (13)

Review

Findings

Medium

  • [scope-creep] eval/fix/eval.yaml — PR adds complete fix agent eval infrastructure (eval/fix/eval.yaml, eval/fix/cases/001-human-fs-fix-add/) that is not mentioned in issue Add code agent functional eval (pre→sandbox→post PR creation) #180. The issue authorization is specifically for "Add code agent functional eval." The fix eval is a separate feature that should ideally have its own issue tracking. See also: [shared-infrastructure-scope] finding at eval/scripts/run-fullsend.sh.
    Remediation: Consider removing eval/fix/ from this PR and submitting it in a follow-up PR with its own issue.

  • [shared-infrastructure-scope] eval/scripts/run-fullsend.sh:810 — The PR modifies shared scripts to support both code and fix agents. While code-specific changes align with issue Add code agent functional eval (pre→sandbox→post PR creation) #180, fix-specific logic (lines 810–829 checking AGENT==fix for PR head branch checkout, lines 902–918 setting fix-specific env vars including HUMAN_INSTRUCTION, TRIGGER_SOURCE, FIX_ITERATION) represents infrastructure for the fix eval that is outside the stated issue scope. See also: [scope-creep] finding at eval/fix/.
    Remediation: Consider separating fix-specific conditional logic into a follow-up PR.

  • [incomplete-doc] eval/README.md:4 — The PR adds two new eval scenarios (eval/code/ and eval/fix/) but eval/README.md line 4 only lists triage/ and review/ as examples, and the "Running evals" section (lines 10–11) only shows triage and review commands. New users reading the eval documentation will not discover code and fix evals.
    Remediation: Update line 4 to include code and fix in the directory list, and add example commands for the new agents.

Low

  • [edge-case] eval/scripts/capture-fixture.sh:44 — In the issue path, comments_json is built via jq '[.comments[] | ...]' without null coalescing. If .comments were null, this would cause a jq error and script abort under set -e. The pull_request path defensively uses ($pr.comments // [])[] for the same pattern. While gh CLI returns {"comments":[]} for commentless issues (so this is not an active bug), the inconsistency is worth fixing for defensive consistency.
    Remediation: Use '[(.comments // [])[]] | ...' consistent with how labels/files are handled elsewhere in the script.
Previous run

Review

Findings

Low

  • [edge-case] eval/scripts/capture-fixture.sh:36fetch_pr_files uses jq expression [.files[].path] which will error if gh pr view --json files returns {"files": null}. The 2>/dev/null suppresses the jq error, causing all 3 retry attempts to fail and marking files_fetch_failed: true even though the PR exists. Unlikely in practice since GitHub PRs with changes always have a non-null files array.
    Remediation: Use [(.files // [])[].path] to handle a null .files field gracefully.

  • [incomplete-escaping] eval/scripts/run-fullsend.sh:69emit_env escapes backslash and double-quote but not dollar signs ($). Values are wrapped in double quotes via printf '%s="%s"\n', so an unescaped $ would cause shell-style variable expansion in parsers that expand inside double quotes. All current callers pass tokens (alphanumeric), URLs, repo names, and file paths — none contain $ — so risk is theoretical.
    Remediation: Add value="${value//\$/\\\$}" after existing escaping.

  • [incomplete-doc] eval/triage/eval.yaml:64 — The outputs schema description says "Contains: state, labels, comments, assignees, and for PRs: reviews." After this PR, issue-type fixtures will also include a pull_requests field. The prose is slightly stale but has zero functional impact — triage judges only access state, labels, and comments.
    Remediation: Update the outputs schema to mention pull_requests.

Previous run (14)

Review

Findings

Low

  • [edge-case] eval/scripts/capture-fixture.sh:36fetch_pr_files uses jq expression [.files[].path] which will error if gh pr view --json files returns {"files": null}. The 2>/dev/null suppresses the jq error, causing all 3 retry attempts to fail and marking files_fetch_failed: true even though the PR exists. Unlikely in practice since GitHub PRs with changes always have a non-null files array.
    Remediation: Use [(.files // [])[].path] to handle a null .files field gracefully.

  • [incomplete-escaping] eval/scripts/run-fullsend.sh:69emit_env escapes backslash and double-quote but not dollar signs ($). Values are wrapped in double quotes via printf '%s="%s"\n', so an unescaped $ would cause shell-style variable expansion in parsers that expand inside double quotes. All current callers pass tokens (alphanumeric), URLs, repo names, and file paths — none contain $ — so risk is theoretical.
    Remediation: Add value="${value//\$/\\\$}" after existing escaping.

  • [incomplete-doc] eval/triage/eval.yaml:64 — The outputs schema description says "Contains: state, labels, comments, assignees, and for PRs: reviews." After this PR, issue-type fixtures will also include a pull_requests field. The prose is slightly stale but has zero functional impact — triage judges only access state, labels, and comments.
    Remediation: Update the outputs schema to mention pull_requests.

Previous run (15)

Review

Findings

Low

  • [edge-case] eval/scripts/capture-fixture.sh:21fetch_pr_files uses jq expression [.files[].path] which will error if gh pr view --json files returns {"files": null}. The 2>/dev/null suppresses the jq error, causing all 3 retry attempts to fail and marking files_fetch_failed: true even though the PR exists. Unlikely in practice since GitHub PRs with changes always have a non-null files array.
    Remediation: Use [(.files // [])[].path] to handle a null .files field gracefully.

  • [incomplete-escaping] eval/scripts/run-fullsend.sh:52emit_env escapes backslash and double-quote but not dollar signs ($). Values are wrapped in double quotes via printf '%s="%s"\n', so an unescaped $ would cause shell-style variable expansion in parsers that expand inside double quotes. All current callers pass tokens (alphanumeric), URLs, repo names, and file paths — none contain $ — so risk is theoretical.
    Remediation: Add value="${value//\$/\\\$}" after existing escaping.

Previous run (16)

Review — approve

Clean eval infrastructure PR that adds a code agent functional eval scenario. The branch was rebased from the prior-reviewed state (92135ac) to 3 clean commits (61501b89, 59b69c1b, 54929aa5). The net diff against main is the same 9 eval files with no correctness, security, or scope concerns.

Prior findings — status

# Finding Prior severity Status
1 eval/triage/eval.yaml outputs schema slightly stale low ⚠ Still valid — see finding 1 below

New commit analysis (92135ac → 54929aa)

Branch was force-pushed with a clean rebase to 3 commits:

  1. 61501b89 — test(eval): add code agent functional eval
  2. 59b69c1b — fix(eval): supply code harness env vars for functional runs
  3. 54929aa5 — fix(eval): address PR test(eval scenario): add code agent functional eval scenario #177 review findings for code eval

Net effect: The same 9 eval infrastructure files as the prior-approved state, reorganized into clean commits. The third commit incorporates all hardening from prior review iterations (emit_env, shape validation, fetch_pr_files retry, agent-scoped env vars).

Verification notes

  • Authorization: PR closes Add code agent functional eval (pre→sandbox→post PR creation) #180. Issue acceptance criteria verified: (1) eval/code/ with case 001-fix-add ✓, (2) shared eval scripts supply issue-fixture env vars for pre-code.sh ✓, (3) capture includes PRs + files for judges ✓, (4) CI auto-selects code via select-eval-agents.sh
  • Correctness — judge logic: All five judges in eval/code/eval.yaml handle edge cases correctly: pr_created uses state.get("pull_requests") or [] with .upper() state comparison; expected_files checks files_fetch_failed before comparing; forbidden_labels, max_turns, max_cost match triage/review patterns. ✓
  • Correctness — shell scripts: emit_env() rejects \n/\r (defense in depth). Shape validation regexes for FIXTURE_URL, FIXTURE_NUMBER, EPHEMERAL_REPO are correct and strict. Agent-scoped env vars gated by case "$AGENT" in code|fix). ✓
  • Correctness — workspace layout: EVAL_GH_WORKSPACE/target-repo nesting correctly mirrors GHA layout for REPO_DIR=${GITHUB_WORKSPACE}/target-repo. Cleanup trap targets parent directory. ✓
  • Correctness — capture-fixture.sh: fetch_pr_files() with 3-attempt retry and files_fetch_failed propagation. gh pr list failure falls back to []. Empty PR list produces valid JSON via jq -s '.'. ✓
  • Consumer completeness: pull_requests field in issue fixture-state.json is additive — existing triage/review judges don't reference it. ISSUE_NUMBER/REPO_FULL_NAME env vars gated by fixture type, no conflict with triage. ✓
  • Security — secrets handling: Env file created with 0600 permissions, cleaned up by both explicit rm -f and EXIT trap. Tokens passed by $REFERENCE in eval.yaml, not by value. emit_env() prevents newline injection. ✓
  • Security — injection defense: Shape validation prevents URL/repo injection. Credential helper pattern is safe (single-quoted at assignment). ✓
  • Symlink correctness: eval/code/cases/001-fix-add/repo → ../../repos/tiny-calc resolves correctly. ✓
  • Test design: test_calc.py assertions intentionally fail against buggy calc.py (a - b instead of a + b). ✓
  • Style: YAML structure, field ordering, comment separators, case naming (001-fix-add), shell idioms — all match established triage/review patterns. PR title follows conventional commits format. ✓
  • No prompt injection detected in fixture content, code comments, or PR body. ✓

Findings

1. eval/triage/eval.yaml outputs schema slightly stale — low

File: eval/triage/eval.yaml (line 66)

The outputs schema description says "Contains: state, labels, comments, assignees, and for PRs: reviews." After this PR, issue-type fixtures will also include a pull_requests field (always [] for triage runs since the triage agent doesn't create PRs). The prose is slightly stale but has zero functional impact — triage judges only access state, labels, and comments.


Labels: PR adds eval infrastructure for the code agent pipeline

Previous run (17)

Review — approve

Clean eval infrastructure PR that adds a code agent functional eval scenario. The delta since the prior review (two revert commits at 877a252, 92135ac) completes the removal of the out-of-scope scrub-eval-results.sh work. The net PR diff is identical to the prior-approved state: 9 eval files with no correctness, security, or scope concerns.

Prior findings — status

# Finding Prior severity Status
1 Verification regex missing standalone detection for three token types in scrub-eval-results.sh medium ✅ Moot — file fully deleted by revert commits 877a252 + 92135ac. No longer in the PR diff.
2 eval/triage/eval.yaml outputs schema slightly stale low ⚠ Still valid — see finding 1 below

New commit analysis (07e17ef → 92135ac)

Two commits since the prior review:

  1. 877a252 — Revert "fix(eval): emulate Actions ::add-mask:: when scrubbing eval artifacts"
  2. 92135ac — Revert "fix(eval): scrub ::add-mask:: tokens from functional-test artifacts"

Net effect: eval/scripts/scrub-eval-results.sh is deleted and .github/workflows/functional-tests.yml returns to the main baseline (inline find ... -delete). The reverts are clean — no dangling references to the removed script anywhere in the codebase. The net PR diff against main is unchanged from the prior approve at 07e17ef.

Verification notes

  • Authorization: PR closes Add code agent functional eval (pre→sandbox→post PR creation) #180. Issue acceptance criteria verified via API: (1) eval/code/ with at least one case ✓, (2) shared eval scripts supply issue-fixture env vars for pre-code.sh ✓, (3) capture includes PRs + files for judges ✓, (4) CI selects and runs code
  • Correctness — judge logic: All five judges in eval/code/eval.yaml handle edge cases correctly: pr_created uses state.get("pull_requests") or [] with .upper() state comparison; expected_files checks files_fetch_failed before comparing; forbidden_labels, max_turns, max_cost match triage/review patterns. ✓
  • Correctness — shell scripts: emit_env() rejects \n/\r (defense in depth). Shape validation regexes for FIXTURE_URL, FIXTURE_NUMBER, EPHEMERAL_REPO are correct and strict. Agent-scoped env vars gated by case "$AGENT" in code|fix). ✓
  • Correctness — workspace layout: EVAL_GH_WORKSPACE/target-repo nesting correctly mirrors GHA layout for REPO_DIR=${GITHUB_WORKSPACE}/target-repo. Cleanup trap targets parent directory. ✓
  • Correctness — capture-fixture.sh: fetch_pr_files() with 3-attempt retry and files_fetch_failed propagation through JSON — the expected_files judge correctly checks for this flag before comparing file lists. ✓
  • Consumer completeness: pull_requests field in issue fixture-state.json is additive — existing triage/review judges don't reference it. ISSUE_NUMBER/REPO_FULL_NAME env vars gated by fixture type, no conflict with triage. ✓
  • Security — secrets handling: Env file created with 0600 permissions, cleaned up by both explicit rm -f and EXIT trap. Tokens passed by $REFERENCE in eval.yaml, not by value. No secrets in logs/stderr. ✓
  • Security — injection defense: Shape validation prevents URL/repo injection. emit_env() prevents newline injection into dotenv. Credential helper follows pre-existing accepted pattern. ✓
  • Symlink correctness: eval/code/cases/001-fix-add/repo → ../../repos/tiny-calc resolves correctly. ✓
  • Test design: test_calc.py assertions intentionally fail against buggy calc.py (a - b instead of a + b). Correct for the eval. ✓
  • CI integration: select-eval-agents.sh automatically includes code because harness/code.yaml and eval/code/eval.yaml exist. ✓
  • Style: YAML structure, field ordering, comment separators, case naming (001-fix-add), shell idioms — all match established patterns. PR title follows conventional commits format. ✓
  • No prompt injection detected in fixture content, code comments, or PR body. ✓

Findings

1. eval/triage/eval.yaml outputs schema slightly stale — low

File: eval/triage/eval.yaml (line 66)

The outputs schema description says "Contains: state, labels, comments, assignees, and for PRs: reviews." After this PR, issue-type fixtures will also include a pull_requests field (always [] for triage runs since the triage agent doesn't create PRs). The prose is slightly stale but has zero functional impact — triage judges only access state, labels, and comments.

Previous run (18)

Review — approve

Clean eval infrastructure PR that adds a code agent functional eval scenario. The delta since the prior review (two revert commits at 877a252, 92135ac) completes the removal of the out-of-scope scrub-eval-results.sh work. The net PR diff is identical to the prior-approved state: 9 eval files with no correctness, security, or scope concerns.

Prior findings — status

# Finding Prior severity Status
1 Verification regex missing standalone detection for three token types in scrub-eval-results.sh medium ✅ Moot — file fully deleted by revert commits 877a252 + 92135ac. No longer in the PR diff.
2 eval/triage/eval.yaml outputs schema slightly stale low ⚠ Still valid — see finding 1 below

New commit analysis (07e17ef → 92135ac)

Two commits since the prior review:

  1. 877a252 — Revert "fix(eval): emulate Actions ::add-mask:: when scrubbing eval artifacts"
  2. 92135ac — Revert "fix(eval): scrub ::add-mask:: tokens from functional-test artifacts"

Net effect: eval/scripts/scrub-eval-results.sh is deleted and .github/workflows/functional-tests.yml returns to the main baseline (inline find ... -delete). The reverts are clean — no dangling references to the removed script anywhere in the codebase. The net PR diff against main is unchanged from the prior approve at 07e17ef.

Verification notes

  • Authorization: PR closes Add code agent functional eval (pre→sandbox→post PR creation) #180. Issue acceptance criteria verified via API: (1) eval/code/ with at least one case ✓, (2) shared eval scripts supply issue-fixture env vars for pre-code.sh ✓, (3) capture includes PRs + files for judges ✓, (4) CI selects and runs code
  • Correctness — judge logic: All five judges in eval/code/eval.yaml handle edge cases correctly: pr_created uses state.get("pull_requests") or [] with .upper() state comparison; expected_files checks files_fetch_failed before comparing; forbidden_labels, max_turns, max_cost match triage/review patterns. ✓
  • Correctness — shell scripts: emit_env() rejects \n/\r (defense in depth). Shape validation regexes for FIXTURE_URL, FIXTURE_NUMBER, EPHEMERAL_REPO are correct and strict. Agent-scoped env vars gated by case "$AGENT" in code|fix). ✓
  • Correctness — workspace layout: EVAL_GH_WORKSPACE/target-repo nesting correctly mirrors GHA layout for REPO_DIR=${GITHUB_WORKSPACE}/target-repo. Cleanup trap targets parent directory. ✓
  • Correctness — capture-fixture.sh: fetch_pr_files() with 3-attempt retry and files_fetch_failed propagation. gh pr list failure falls back to []. Empty PR list produces valid JSON via jq -s '.'. ✓
  • Consumer completeness: pull_requests field in issue fixture-state.json is additive — existing triage/review judges don't reference it. ISSUE_NUMBER/REPO_FULL_NAME env vars gated by fixture type, no conflict with triage. ✓
  • Security — secrets handling: Env file created with 0600 permissions, cleaned up by both explicit rm -f and EXIT trap. Tokens passed by $REFERENCE in eval.yaml, not by value. emit_env() prevents newline injection. ✓
  • Security — injection defense: Shape validation prevents URL/repo injection. Credential helper pattern is safe (single-quoted at assignment). ✓
  • Symlink correctness: eval/code/cases/001-fix-add/repo → ../../repos/tiny-calc resolves correctly. ✓
  • Test design: test_calc.py assertions intentionally fail against buggy calc.py (a - b instead of a + b). ✓
  • Style: YAML structure, field ordering, comment separators, case naming (001-fix-add), shell idioms — all match established triage/review patterns. PR title follows conventional commits format. ✓
  • No prompt injection detected in fixture content, code comments, or PR body. ✓

Findings

1. eval/triage/eval.yaml outputs schema slightly stale — low

File: eval/triage/eval.yaml (line 64)

The outputs schema description says "Contains: state, labels, comments, assignees, and for PRs: reviews." After this PR, issue-type fixtures will also include a pull_requests field (always [] for triage runs since the triage agent doesn't create PRs). The prose is slightly stale but has zero functional impact — triage judges only access state, labels, and comments.


Labels: PR adds eval infrastructure for the code agent pipeline

Previous run (19)

Review — approve

Well-scoped PR that adds a code agent functional eval scenario, hardens shared eval scripts, and has fully reverted the intermediate scrub-eval-results.sh work. The net diff is clean eval infrastructure matching established triage/review patterns.

Prior findings — status

# Finding Prior severity Status
1 Verification regex missing standalone detection for three token types in scrub-eval-results.sh medium ✅ Moot — scrub-eval-results.sh was fully reverted out of the PR (commits 48f6550, 7ebad2a, 07e17ef). File no longer exists in the diff.
2 eval/triage/eval.yaml outputs schema slightly stale low ⚠ Still valid — see finding 1 below

New commit analysis (07e17ef)

Three commits since the prior review (c35226ab → 07e17ef):

  1. 48f6550 — cherry-picked ::add-mask:: emulation for scrub script
  2. 7ebad2a — revert of 48f6550
  3. 07e17ef — revert of original scrub commit c35226ab

Net effect: the scrub-eval-results.sh work is fully unwound. The PR's final diff is identical to the state at f7408a6d (the eval scenario commit), containing only the 9 eval infrastructure files. No functional changes to the eval scenario code since the prior approve-level review.

Verification notes

  • Authorization: PR closes Add code agent functional eval (pre→sandbox→post PR creation) #180. Issue acceptance criteria (eval/code/ with at least one case, shared script env vars for pre-code.sh, PR+files capture, CI integration) are all met. ✓
  • Correctness — judge logic: All five judges in eval/code/eval.yaml handle edge cases correctly: pr_created uses state.get("pull_requests") or [] with .upper() state comparison; expected_files checks files_fetch_failed before comparing; forbidden_labels, max_turns, max_cost match triage/review patterns verbatim. ✓
  • Correctness — shell scripts: emit_env() rejects \n/\r (defense in depth). Shape validation regexes for FIXTURE_URL, FIXTURE_NUMBER, EPHEMERAL_REPO are correct and strict. Agent-scoped env vars gated by case "$AGENT" in code|fix). ✓
  • Correctness — workspace layout: EVAL_GH_WORKSPACE/target-repo nesting correctly mirrors GHA layout for REPO_DIR=${GITHUB_WORKSPACE}/target-repo. Cleanup trap targets parent directory. ✓
  • Correctness — capture-fixture.sh: fetch_pr_files() with 3-attempt retry and files_fetch_failed propagation through JSON — the expected_files judge correctly checks for this flag before comparing file lists. ✓
  • Security — secrets handling: Env file created with 0600 permissions, cleaned up by both explicit rm -f and EXIT trap. Tokens passed by $REFERENCE in eval.yaml, not by value. No secrets in logs/stderr. ✓
  • Security — injection defense: Shape validation prevents URL/repo injection. emit_env() prevents newline injection into dotenv. Credential helper follows pre-existing accepted pattern. ✓
  • Consumer completeness: New pull_requests field in issue fixture-state.json is additive only — existing triage/review judges don't reference it. ISSUE_NUMBER/REPO_FULL_NAME env vars for issue fixtures are gated by fixture type and do not conflict with triage (which derives them from GITHUB_ISSUE_URL in its own pre-script). ✓
  • Symlink correctness: eval/code/cases/001-fix-add/repo → ../../repos/tiny-calc resolves correctly. ✓
  • Test design: test_calc.py assertions intentionally fail against buggy calc.py (a - b instead of a + b). ✓
  • CI integration: select-eval-agents.sh automatically includes code because harness/code.yaml and eval/code/eval.yaml exist. ✓
  • Style: YAML structure, field ordering, comment separators, case naming (001-fix-add), shell idioms — all match established patterns. PR title follows conventional commits format. ✓
  • No prompt injection detected in fixture content, code comments, or PR body. ✓

Findings

1. eval/triage/eval.yaml outputs schema slightly stale — low

File: eval/triage/eval.yaml (line 66)

The outputs schema description says "Contains: state, labels, comments, assignees, and for PRs: reviews." After this PR, issue-type fixtures will also include a pull_requests field (always [] for triage runs since the triage agent doesn't create PRs). The prose is slightly stale but has zero functional impact — triage judges only access state, labels, and comments.

Previous run (20)

Review — approve

Well-scoped PR that adds a code agent functional eval scenario, hardens shared eval scripts, and has fully reverted the intermediate scrub-eval-results.sh work. The net diff is clean eval infrastructure matching established triage/review patterns.

Prior findings — status

# Finding Prior severity Status
1 Verification regex missing standalone detection for three token types in scrub-eval-results.sh medium ✅ Moot — scrub-eval-results.sh was fully reverted out of the PR (commits 48f6550, 7ebad2a, 07e17ef). File no longer exists in the diff.
2 eval/triage/eval.yaml outputs schema slightly stale low ⚠ Still valid — see finding 1 below

New commit analysis (07e17ef)

Three commits since the prior review (c35226ab → 07e17ef):

  1. 48f6550 — cherry-picked ::add-mask:: emulation for scrub script
  2. 7ebad2a — revert of 48f6550
  3. 07e17ef — revert of original scrub commit c35226ab

Net effect: the scrub-eval-results.sh work is fully unwound. The PR's final diff is identical to the state at f7408a6d (the eval scenario commit), containing only the 9 eval infrastructure files. No functional changes to the eval scenario code since the prior approve-level review.

Verification notes

  • Authorization: PR closes Add code agent functional eval (pre→sandbox→post PR creation) #180. Issue acceptance criteria (eval/code/ with at least one case, shared script env vars for pre-code.sh, PR+files capture, CI integration) are all met. ✓
  • Correctness — judge logic: All five judges in eval/code/eval.yaml handle edge cases correctly: pr_created uses state.get("pull_requests") or [] with .upper() state comparison; expected_files checks files_fetch_failed before comparing; forbidden_labels, max_turns, max_cost match triage/review patterns verbatim. ✓
  • Correctness — shell scripts: emit_env() rejects \n/\r (defense in depth). Shape validation regexes for FIXTURE_URL, FIXTURE_NUMBER, EPHEMERAL_REPO are correct and strict. Agent-scoped env vars gated by case "$AGENT" in code|fix). ✓
  • Correctness — workspace layout: EVAL_GH_WORKSPACE/target-repo nesting correctly mirrors GHA layout for REPO_DIR=${GITHUB_WORKSPACE}/target-repo. Cleanup trap targets parent directory. ✓
  • Correctness — capture-fixture.sh: fetch_pr_files() with 3-attempt retry and files_fetch_failed propagation. gh pr list failure falls back to []. Empty PR list produces valid JSON via jq -s '.'. ✓
  • Security — secrets handling: Env file created with 0600 permissions, cleaned up by both explicit rm -f and EXIT trap. Tokens passed by $REFERENCE in eval.yaml, not by value. No secrets in logs/stderr. ✓
  • Security — injection defense: Shape validation prevents URL/repo injection. emit_env() prevents newline injection into dotenv. Credential helper follows pre-existing accepted pattern. ✓
  • Consumer completeness: New pull_requests field in issue fixture-state.json is additive only — existing triage/review judges don't reference it. ISSUE_NUMBER/REPO_FULL_NAME env vars for issue fixtures are gated by fixture type and do not conflict with triage (which derives them from GITHUB_ISSUE_URL in its own pre-script). ✓
  • Symlink correctness: eval/code/cases/001-fix-add/repo → ../../repos/tiny-calc resolves correctly. ✓
  • Test design: test_calc.py assertions intentionally fail against buggy calc.py (a - b instead of a + b). ✓
  • CI integration: select-eval-agents.sh automatically includes code because harness/code.yaml and eval/code/eval.yaml exist. ✓
  • Style: YAML structure, field ordering, comment separators, case naming (001-fix-add), shell idioms — all match established patterns. PR title follows conventional commits format. ✓
  • No prompt injection detected in fixture content, code comments, or PR body. ✓

Findings

1. eval/triage/eval.yaml outputs schema slightly stale — low

File: eval/triage/eval.yaml (line 66)

The outputs schema description says "Contains: state, labels, comments, assignees, and for PRs: reviews." After this PR, issue-type fixtures will also include a pull_requests field (always [] for triage runs since the triage agent doesn't create PRs). The prose is slightly stale but has zero functional impact — triage judges only access state, labels, and comments.


Labels: PR adds eval infrastructure for the code agent pipeline

Previous run (21)

Review — comment

Well-scoped PR that adds a code agent functional eval scenario, hardens shared eval scripts, and adds token scrubbing for eval artifacts. All prior findings confirmed resolved. One new medium finding on the latest commit's verification logic.

Prior findings — status

# Finding Prior severity Status
1 skill field nested under execution: instead of top-level medium ✅ Fixed — moved to top-level, matching triage/review pattern
2 Shared env-file construction lacks newline defense low ✅ Fixed — emit_env() rejects \n/\r, shape validation for fixture-derived values
3 No linked issue for non-trivial structural change low ✅ Addressed — PR body closes #180

New commit analysis (c35226ab)

The latest commit adds eval/scripts/scrub-eval-results.sh and updates .github/workflows/functional-tests.yml to call it. This replaces the old inline find -delete (which only removed .eval-env files) with comprehensive token redaction across all text-based eval artifacts — a significant security improvement.

The script correctly:

  • Deletes .eval-env files (preserving old behavior)
  • Scrubs ::add-mask:: payloads, all six GitHub token prefixes, github_pat_ tokens, and x-access-token credential URLs
  • Filters file processing by extension (.log, .txt, .json, .jsonl, .yaml, .yml, .md) — no binary corruption risk
  • Guards each root directory with [[ -d "$root" ]] || continue — handles missing directories gracefully
  • Fails the workflow step if tokens survive scrubbing (fail-closed design)

Findings

1. Verification regex missing standalone detection for three token types — medium

File: eval/scripts/scrub-eval-results.sh (line 49)

The leak_pat verification regex checks for ::add-mask:: followed by any of the six token prefixes (correct), but only checks for standalone ghp_, gho_, and github_pat_ tokens. Three token types that the scrub function correctly redacts are missing from standalone verification:

Prefix Token type Scrubbed? Verified standalone?
ghp_ Personal access tokens
gho_ OAuth access tokens
ghu_ User-to-server tokens
ghs_ App installation tokens
ghr_ Refresh tokens
github_pat_ Fine-grained PATs

If a bare ghs_ token (the most likely type — GitHub App installation tokens, which EVAL_GH_TOKEN likely is) appears in an eval artifact without a preceding ::add-mask::, and the Perl scrub fails on that file (encoding issue, disk full), the verification pass would not catch it. The token would ship in the uploaded artifact.

Remediation: Add the three missing prefixes to leak_pat:

|\bghu_[A-Za-z0-9_]{20,}|\bghs_[A-Za-z0-9_]{20,}|\bghr_[A-Za-z0-9_]{20,}

2. eval/triage/eval.yaml outputs schema slightly stale — low

File: eval/triage/eval.yaml (line 66)

The outputs schema description says "Contains: state, labels, comments, assignees, and for PRs: reviews." After this PR, issue-type fixtures will also include a pull_requests field (always [] for triage runs since the triage agent doesn't create PRs). The prose is slightly stale but has zero functional impact — triage judges only access state, labels, and comments. (Carried forward from prior review.)

Previous run (22)

Review — approve

Well-structured PR that adds a code agent functional eval scenario and hardens the shared eval scripts. All three findings from the prior review are confirmed resolved.

Prior findings — status

# Finding Prior severity Status
1 skill field nested under execution: instead of top-level medium ✅ Fixed — moved to top-level, matching triage/review pattern
2 Shared env-file construction lacks newline defense low ✅ Fixed — emit_env() rejects \n/\r, shape validation for fixture-derived values
3 No linked issue for non-trivial structural change low ✅ Addressed — PR body closes #180

New observations

The latest commit (f7408a6d) addresses all prior findings and adds several improvements beyond the minimum fix:

  • Agent-scoped env vars: Code/fix-specific vars (PUSH_TOKEN_SOURCE, GITHUB_WORKSPACE, GIT_BOT_EMAIL, CODE_ALLOWED_TARGET_BRANCHES) are now gated by case "$AGENT" in code|fix), preventing triage eval from receiving a redirected GITHUB_WORKSPACE. This is a correct architectural decision.
  • fetch_pr_files() with retry: 3-attempt backoff with files_fetch_failed propagation through JSON — the expected_files judge correctly checks for this flag before comparing file lists.
  • gh pr list error handling: Failure falls back to [] with a stderr warning, preventing set -e from aborting capture-fixture.sh on transient API failures.
  • Shape validation: FIXTURE_URL, FIXTURE_NUMBER, and EPHEMERAL_REPO are regex-validated before use. The regexes are correct and match the values that setup-fixture.sh produces.

Verification notes

  • Symlink correctness: eval/code/cases/001-fix-add/repo → ../../repos/tiny-calc resolves correctly. ✓
  • Test design: The test_calc.py assertions intentionally fail against the buggy calc.py (a - b instead of a + b). Correct for the eval. ✓
  • CODE_ALLOWED_TARGET_BRANCHES="": Safe. post-code.sh uses ${CODE_ALLOWED_TARGET_BRANCHES:-${DEFAULT_BRANCH}}, which treats empty string identically to unset, falling back to the default branch. No fail-open. ✓
  • Workspace layout: EVAL_GH_WORKSPACE/target-repo nesting mirrors the GHA layout expected by post-code.sh (REPO_DIR=${GITHUB_WORKSPACE}/target-repo). Cleanup trap targets the parent directory. ✓
  • Consumer completeness: The new pull_requests field in fixture-state.json is consumed only by the new code eval judges. Existing triage/review judges don't reference it — backward compatible. ✓
  • Judge logic: pr_created correctly checks for OPEN/MERGED PRs via .upper(). expected_files correctly checks files_fetch_failed before comparing. Both handle empty/null gracefully. ✓
  • CI integration: select-eval-agents.sh will automatically include code because harness/code.yaml exists and eval/code/eval.yaml is present. ✓

Minor note (low, not blocking)

The eval/triage/eval.yaml outputs.schema description says "Contains: state, labels, comments, assignees, and for PRs: reviews." After this PR, issue-type fixtures will also include a pull_requests field (always [] for triage runs since the triage agent doesn't create PRs). The prose is slightly stale but has zero functional impact — triage judges only access state, labels, and comments.

Previous run (23)

Review — comment

Well-structured PR that adds a code agent functional eval scenario following the established triage/review eval patterns. The new eval/code/ case, tiny-calc fixture repo, and shared script modifications are cohesive and appropriately scoped.

Findings

1. skill field placement differs from established pattern — medium

File: eval/code/eval.yaml (line 4)

Both existing eval configs (eval/triage/eval.yaml and eval/review/eval.yaml) declare skill: as a top-level YAML field:

# eval/triage/eval.yaml
name: triage-eval
description: ...

skill: triage     # ← top-level

execution:
  mode: case

The new code eval nests it under execution::

# eval/code/eval.yaml
name: code-eval
description: ...

execution:
  skill: code     # ← nested under execution
  mode: case

run-functional.sh passes --skill "$AGENT" to execute.py from the CLI argument (line 113), so execution likely still works. However, workspace.py and score.py receive only --config (no --skill flag) — if either reads config['skill'] from the YAML (as the triage and review patterns suggest), the code eval may fail at workspace creation or scoring.

Remediation: Move skill: code to the top level as a sibling of name and description, matching the established pattern.

2. Shared env-file construction lacks newline defense — low

File: eval/scripts/run-fullsend.sh (lines 54–67)

Variables like FIXTURE_URL, FIXTURE_NUMBER, EPHEMERAL_REPO, and EVAL_GH_WORKSPACE are echoed directly into the env file. All current values originate from trusted tooling (gh CLI, mktemp), so exploitability is very low. However, there is no structural defense: a value containing \n could inject additional env vars. This is a defense-in-depth gap, not an active vulnerability.

3. No linked issue for non-trivial structural change — low

This PR adds 9 files (274 additions) including modifications to shared eval scripts. While test infrastructure for an existing agent (harness/code.yaml already exists) has reasonable implicit authorization, a linked issue would help establish the scope boundary — particularly since the shared script changes affect all eval scenarios, not just code.

Notes

  • Symlink correctness: eval/code/cases/001-fix-add/repo → ../../repos/tiny-calc resolves correctly to eval/code/repos/tiny-calc. ✓
  • Test design: The test_calc.py assertions (add(2,3)==5, add(-1,-2)==-3) intentionally fail against the buggy calc.py (a - b instead of a + b). This is correct for the eval — the agent must fix the bug for tests to pass. ✓
  • CODE_ALLOWED_TARGET_BRANCHES="": The empty string is safe. The consumer in post-code.sh uses ${CODE_ALLOWED_TARGET_BRANCHES:-${DEFAULT_BRANCH}}, which treats empty the same as unset, falling back to the default branch. No fail-open. ✓
  • Workspace layout: The EVAL_GH_WORKSPACE → target-repo nesting correctly mirrors the GHA layout that post-scripts expect (REPO_DIR=${GITHUB_WORKSPACE}/target-repo). Cleanup trap correctly targets the parent directory. ✓
  • PR capture logic: The capture-fixture.sh additions correctly handle the empty-PR-list case (guard on ${#pr_lines[@]} -eq 0), and the || true on gh pr view prevents set -e failures. ✓
  • Functional test workflow: select-eval-agents.sh will automatically include code in the matrix because harness/code.yaml exists and eval/code/eval.yaml is present. Files under eval/code/ match the eval/$agent/* pattern. ✓

Labels: PR adds eval infrastructure for the code agent pipeline

Previous run (24)

Review — comment

Well-scoped PR that adds a code agent functional eval scenario, hardens shared eval scripts, and adds token scrubbing for eval artifacts. All prior findings confirmed resolved. One new medium finding on the latest commit's verification logic.

Prior findings — status

# Finding Prior severity Status
1 skill field nested under execution: instead of top-level medium ✅ Fixed — moved to top-level, matching triage/review pattern
2 Shared env-file construction lacks newline defense low ✅ Fixed — emit_env() rejects \n/\r, shape validation for fixture-derived values
3 No linked issue for non-trivial structural change low ✅ Addressed — PR body closes #180

New commit analysis (c35226ab)

The latest commit adds eval/scripts/scrub-eval-results.sh and updates .github/workflows/functional-tests.yml to call it. This replaces the old inline find -delete (which only removed .eval-env files) with comprehensive token redaction across all text-based eval artifacts — a significant security improvement.

The script correctly:

  • Deletes .eval-env files (preserving old behavior)
  • Scrubs ::add-mask:: payloads, all six GitHub token prefixes, github_pat_ tokens, and x-access-token credential URLs
  • Filters file processing by extension (.log, .txt, .json, .jsonl, .yaml, .yml, .md) — no binary corruption risk
  • Guards each root directory with [[ -d "$root" ]] || continue — handles missing directories gracefully
  • Fails the workflow step if tokens survive scrubbing (fail-closed design)

Findings

1. Verification regex missing standalone detection for three token types — medium

File: eval/scripts/scrub-eval-results.sh (line 49)

The leak_pat verification regex checks for ::add-mask:: followed by any of the six token prefixes (correct), but only checks for standalone ghp_, gho_, and github_pat_ tokens. Three token types that the scrub function correctly redacts are missing from standalone verification:

Prefix Token type Scrubbed? Verified standalone?
ghp_ Personal access tokens
gho_ OAuth access tokens
ghu_ User-to-server tokens
ghs_ App installation tokens
ghr_ Refresh tokens
github_pat_ Fine-grained PATs

If a bare ghs_ token (the most likely type — GitHub App installation tokens, which EVAL_GH_TOKEN likely is) appears in an eval artifact without a preceding ::add-mask::, and the Perl scrub fails on that file (encoding issue, disk full), the verification pass would not catch it. The token would ship in the uploaded artifact.

Remediation: Add the three missing prefixes to leak_pat:

|\bghu_[A-Za-z0-9_]{20,}|\bghs_[A-Za-z0-9_]{20,}|\bghr_[A-Za-z0-9_]{20,}

2. eval/triage/eval.yaml outputs schema slightly stale — low

File: eval/triage/eval.yaml (line 66)

The outputs schema description says "Contains: state, labels, comments, assignees, and for PRs: reviews." After this PR, issue-type fixtures will also include a pull_requests field (always [] for triage runs since the triage agent doesn't create PRs). The prose is slightly stale but has zero functional impact — triage judges only access state, labels, and comments. (Carried forward from prior review.)

Verification notes

  • Scrub script integration: Workflow step at line 338 correctly calls the script with eval/runs and /tmp/agent-eval arguments, matching the old behavior's target directories. ✓
  • Fail-closed design: The verification pass correctly exits non-zero if any tokens survive scrubbing, which will mark the workflow step as failed. ✓
  • Defense-in-depth layers: Token exposure is guarded at four levels: (1) rm -f "$ENV_FILE" in run-fullsend.sh, (2) .eval-env deletion by scrub script, (3) token pattern scrubbing in text files, (4) verification pass. ✓
  • ::add-mask:: scrub: Uses \S+ which correctly matches token characters (letters, digits, underscores) without matching whitespace or newlines. ✓
  • Perl \s in #-delimited regex: \s works correctly in Perl regardless of delimiter; it's a character class escape, not delimiter-dependent. ✓
  • All previously verified items from prior review remain valid (symlink correctness, test design, CODE_ALLOWED_TARGET_BRANCHES="" safety, workspace layout, consumer completeness, judge logic, CI integration). ✓

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added requires-manual-review Review requires human judgment code-agent labels Jul 15, 2026
ascerra added a commit that referenced this pull request Jul 15, 2026
Harden PR capture, restore top-level skill, sanitize env-file writes,
scope GITHUB_WORKSPACE override to code/fix, and link issue #180.

Signed-off-by: Adam Scerra <ascerra@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 15, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 2:40 PM UTC · Completed 2:53 PM UTC
Commit: f7408a6 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added ready-for-merge All reviewers approved — ready to merge and removed requires-manual-review Review requires human judgment labels Jul 15, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 15, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 4:54 PM UTC · Completed 5:05 PM UTC
Commit: c35226a · View workflow run →

@fullsend-ai-review fullsend-ai-review Bot added requires-manual-review Review requires human judgment and removed ready-for-merge All reviewers approved — ready to merge labels Jul 15, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 15, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 5:08 PM UTC · Ended 5:11 PM UTC
Commit: 5cd495a · View workflow run →

@ascerra

ascerra commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

Dropped the eval artifact scrub commits from this branch — that fix lives alone in #184 so it isn’t duplicated. After #184 merges, rebase this branch onto main to pick it up.

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 15, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 5:11 PM UTC · Completed 5:23 PM UTC
Commit: 07e17ef · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added ready-for-merge All reviewers approved — ready to merge and removed requires-manual-review Review requires human judgment labels Jul 15, 2026
@ascerra
ascerra force-pushed the eval/code-functional-test branch from 07e17ef to 92135ac Compare July 15, 2026 17:54
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 28, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 8:52 PM UTC · Completed 9:05 PM UTC
Commit: 998158d · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@waynesun09 waynesun09 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review pass with a 3-agent squad (Claude ×2, Grok) on 998158d0. All 6 previously-fixed findings re-verified correct, no regressions there. One HIGH finding below is a real, reproduced-in-production regression from this exact commit — not approving this round.

Note: the following finding couldn't attach to a diff line (the referenced lines in eval/fix/eval.yaml aren't part of this PR's changed hunks to that file) and is included here instead:

  • eval/fix/eval.yaml:11-12,46-54 · [MEDIUM] This file's own EVAL_TIMEOUT is still dead config, contradicting the latest commit's stated rationale

This commit's fix for eval/code/eval.yaml moved EVAL_TIMEOUT out of runner.env into execution.env because the harness's CliRunner.from_config() only reads config.execution.env (confirmed against agent_eval/config.py — the two are parsed into entirely separate fields, no merge step). The stated rationale was "restoring the same ordering eval/fix/eval.yaml already has" — that premise doesn't hold: this file still declares EVAL_TIMEOUT: "1700" only under runner.env (line 50), nothing under execution.env. So fix's EVAL_TIMEOUT never reaches the subprocess either, falling back to the script default of 1800 — colliding with execution.timeout: 1800 (line 13), the exact bug class just fixed for code. Pre-existing (this PR's only change to this file is the unrelated forbidden_labels backport a few lines below), narrow trigger window, not blocking — but worth a fast-follow since you're already touching this file and have the fix pattern one file away.

Suggested fix: Move EVAL_TIMEOUT: "1700" from runner.env (line 50) into execution.env (after line 22), mirroring this commit's own eval/code/eval.yaml fix, and correct the line 11-12 comment.


3-agent squad pass (Claude ×2, Grok). The HIGH finding above contradicted what 2 of 3 agents initially concluded ("safe to approve") — independently reproduced with an isolated bash repro plus the cited production CI log before posting, rather than taking either side's word for it. A few additional LOW/INFO items surfaced (a setup-fixture.sh preflight gap for HUMAN_INSTRUCTION values containing "; the PR body's functional-test proof still citing a 10-commit-old CI run; the README's code-agent run example still missing; a local-dev-only EVAL_TIMEOUT env-override quirk; an empty conftest.py) — omitted here as non-blocking nits, happy to post if wanted.

Comment thread eval/scripts/run-fullsend.sh
ascerra added a commit that referenced this pull request Jul 29, 2026
Harden PR capture, restore top-level skill, sanitize env-file writes,
scope GITHUB_WORKSPACE override to code/fix, and link issue #180.

Signed-off-by: Adam Scerra <ascerra@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Adam Scerra <ascerra@redhat.com>
ascerra added a commit that referenced this pull request Jul 29, 2026
agent-eval-harness's cli_runner.py reads a cost_usd key from metrics.json,
but fullsend writes total_cost_usd (internal/cli/run.go's
aggregateMetrics). The mismatch caused eval summaries to silently report
$0.00 cost even when the run incurred real spend (e.g. PR #177's fix
functional test run). Alias cost_usd to total_cost_usd in run-fullsend.sh
after copying metrics.json, without renaming the field fullsend itself
writes.

Signed-off-by: Adam Scerra <ascerra@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@ascerra
ascerra force-pushed the eval/code-functional-test branch from 998158d to 66f23ca Compare July 29, 2026 15:45
@ascerra

ascerra commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Reply to review — fix EVAL_TIMEOUT still dead (couldn't attach inline)

Fixed in 66f23ca.

Moved EVAL_TIMEOUT: \"1700\" from runner.env into execution.env in eval/fix/eval.yaml, mirroring the code-eval fix, and corrected the line 11-12 comment that still pointed at runner.env. Dropped the now-unused runner.env block on that file (same as code).

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 29, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 3:47 PM UTC · Completed 4:04 PM UTC
Commit: 66f23ca · View workflow run →

@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.


Note: The following inline comments could not be posted on the diff (GitHub returned 422) and are included here instead:

  • eval/fix/eval.yaml (file-level): Line 80 · [medium] incomplete-fix

The new_commit judge (~line 80) and expected_files judge (~line 108) still use outputs["files"]["output/fixture-state.json"] (direct dict access), while forbidden_labels was updated in this PR to use the safer .get() + early-return pattern. If fixture-state.json is missing from outputs, these two judges will raise a KeyError instead of returning a clean failure message.

Suggested fix: Apply the same .get() + early-return pattern to new_commit and expected_files judges, matching the forbidden_labels pattern already in this file.

  • eval/review/eval.yaml (file-level): Line 41 · [low] dead-config

The runner.env block has no effect for runner: type: cli. The PR removes runner.env from fix/eval.yaml with an explanatory comment about CliRunner ignoring it, but leaves the identical pattern in review/eval.yaml. All vars listed in runner.env are already present in execution.env, so this is dead config rather than a missing-env bug.

Suggested fix: Remove the runner.env block from eval/review/eval.yaml for consistency with fix/eval.yaml.

  • eval/scripts/run-fullsend.sh (file-level): Line 115 · [low] scope-expansion

The PR includes several changes beyond issue #180's stated acceptance criteria: a rewrite of emit_env's escaping logic, addition of a cost_usd alias to metrics output, and moving EVAL_TIMEOUT from runner.env to execution.env in eval.yaml files. These are drive-by bug fixes and infrastructure improvements discovered during implementation, acknowledged in the PR body.

  • eval/README.md (file-level): Line 10 · [low] incomplete-doc

The Running evals section provides command examples for triage and review but not for the newly added code agent eval. The parenthetical at line 14 says (review, triage, etc.) without explicitly listing code.

Suggested fix: Add a third example line: EVAL_ORG=my-org ./eval/run-functional.sh code and update the parenthetical to include code.

@fullsend-ai-review fullsend-ai-review Bot added requires-manual-review Review requires human judgment and removed ready-for-merge All reviewers approved — ready to merge labels Jul 29, 2026
Comment thread eval/code/eval.yaml

@waynesun09 waynesun09 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Approving on 66f23ca after two full squad review rounds (4-agent, then 3-agent) plus targeted follow-ups.

Every CRITICAL/HIGH finding raised across both rounds — the EVAL_TIMEOUT/runner.env no-op, the emit_env escaping mismatch with envfile.go, and the cleanup() EXIT trap silently corrupting the script's real exit code (independently reproduced in isolation and confirmed live in this PR's own CI logs before it was flagged) — is fixed and re-verified directly against the current diff, not just against the "Fixed in ..." replies. The eval/fix/eval.yaml sibling copy of the EVAL_TIMEOUT bug is fixed too.

One non-blocking item remains open on the thread: a fair precision correction to a comment's description of how the CLI runner builds its subprocess env (inherits full os.environ first, then overlays execution.env — not execution.env "only"). Doesn't change the fix's correctness, just the comment's accuracy — fine to pick up whenever, not a reason to hold this up.

Nice work running down the timeout-ordering and dotenv-escaping issues to their actual root causes in the harness/fullsend source rather than just the surface symptoms.

ascerra and others added 11 commits July 29, 2026 18:22
End-to-end regression guard for code pre→sandbox→post PR creation,
including issue fixture env vars and PR capture for judges.

Signed-off-by: Adam Scerra <ascerra@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Adam Scerra <ascerra@redhat.com>
Skip-mint eval runs need PUSH_TOKEN_SOURCE and a GITHUB_WORKSPACE/target-repo
layout so harness runner_env expansion and post-code REPO_DIR resolve.

Signed-off-by: Adam Scerra <ascerra@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Adam Scerra <ascerra@redhat.com>
Harden PR capture, restore top-level skill, sanitize env-file writes,
scope GITHUB_WORKSPACE override to code/fix, and link issue #180.

Signed-off-by: Adam Scerra <ascerra@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Adam Scerra <ascerra@redhat.com>
Drop unused execution.env EVAL_TIMEOUT and stagger script timeout under
harness kill; quote emit_env values; validate fixture shape before clone;
tighten PR list limit and retry sleep; document code_expectations and
CODE_ALLOWED_TARGET_BRANCHES semantics.

Signed-off-by: Adam Scerra <ascerra@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
- capture-fixture.sh: null-coalesce fetch_pr_files' jq filter
  ([(.files // [])[].path]) so a null .files field doesn't error and
  cause a false files_fetch_failed. This hardening had been present
  via the earlier #183 merge but was lost when the branch was rebuilt
  to be code-only again.
- README.md: list code/ alongside triage/ and review/ in the eval
  directory description.

Signed-off-by: Adam Scerra <ascerra@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
agent-eval-harness's cli_runner.py reads a cost_usd key from metrics.json,
but fullsend writes total_cost_usd (internal/cli/run.go's
aggregateMetrics). The mismatch caused eval summaries to silently report
$0.00 cost even when the run incurred real spend (e.g. PR #177's fix
functional test run). Alias cost_usd to total_cost_usd in run-fullsend.sh
after copying metrics.json, without renaming the field fullsend itself
writes.

Signed-off-by: Adam Scerra <ascerra@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
EVAL_TIMEOUT (2000s) wrapped the whole `fullsend run` invocation but sat
below the code agent's real 2100s (35min) per-run budget from
harness/code.yaml. The outer script-level timeout could SIGTERM
fullsend before its own internal timeout resolved, pre-empting the
partial metrics.json write and post_script attempt that fullsend does
on a graceful internal timeout (internal/cli/run.go), producing a
confusing 'metrics.json not found' judge failure instead of a clean
over-budget verdict.

Raise EVAL_TIMEOUT to 2250 (150s headroom above the real 2100s budget)
and execution.timeout to 2300 (50s further headroom for run-fullsend.sh's
own clone/copy/cleanup work), restoring the same ordering eval/fix/eval.yaml
already has. Headroom is intentionally smaller than fix's ~5min margin —
the code agent's 2100s budget leaves little room inside the CI job's
45-minute cap (.github/workflows/functional-tests.yml) once before_each/
after_each hook budgets are included.

Signed-off-by: Adam Scerra <ascerra@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
- Add empty conftest.py to the tiny-calc fixture: bare `pytest` couldn't
  import calc.py under the default rootdir resolution, making the
  issue's "keep the existing tests passing" instruction unsatisfiable
  (only `python -m pytest` worked).
- Correct the EVAL_TIMEOUT/execution.timeout comments: 2100s is the code
  agent's per-*iteration* budget (validation_loop.max_iterations: 2 means
  up to ~4200s), not a per-run budget, and no headroom choice fits that
  inside the 45-minute CI job cap once hook budgets and job setup overhead
  (podman install alone routinely ~190s) are counted. Lower both back to
  eval/fix/eval.yaml's values (1700/1800) and document this ladder
  honestly as a best-effort local-run affordance, not a guaranteed
  graceful-degradation path in CI.
- Make run-fullsend.sh's cost_usd alias mktemp/mv failure-safe under
  set -e (chain through the if-condition instead of bare statements) so a
  housekeeping failure can no longer turn a successful run into a
  reported failure. Track the temp file in the cleanup trap. Fix a
  comment that called aggregateMetrics a function (it's a struct;
  writeMetricsJSON is the writer).
- Guard pr_created/expected_files/forbidden_labels against a missing
  fixture-state.json with a readable message instead of an unguarded
  KeyError, matching the pattern max_turns/max_cost already used. Filter
  expected_files to OPEN/MERGED PRs like pr_created so the two judges are
  proven true for the same PR.
- Update 001-fix-add's max_turns/max_cost_usd from placeholder values to
  the observed baseline (12 turns / $2.12, CI run 29424512121) with ~2x
  headroom, and set execution.max_budget_usd to match so the harness's
  own reporting agrees with the judge threshold.
- Soften code-eval's description: no judge inspects PR diff content or
  runs the fixture's tests, so this validates PR creation + touched
  files, not fix correctness.

Signed-off-by: Adam Scerra <ascerra@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
The 2x headroom applied in the previous commit (12 -> 30) was based on a
single baseline run and immediately failed CI: the same trivial fixture
took 35 turns on the very next run (CI run 30166455238), still at only
$0.98 well under the $4.00 cost cap. Turn count is apparently much
noisier run-to-run than cost for this fixture. Raise max_turns to 60
(~1.7x over the higher observed value) and document both data points.

Signed-off-by: Adam Scerra <ascerra@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
- emit_env: match envfile.go's real parser (no escape sequences). Reject
  values containing the wrapping double-quote; drop backslash/$/` escaping
  that truncated or corrupted values.
- Move EVAL_TIMEOUT into execution.env — CliRunner only injects that block;
  under runner.env it never reached run-fullsend.sh and silently fell back
  to 1800, tying the outer harness kill to the inner timeout.
- Correct the timeout comment: run.go computes the duration once and passes
  it into rt.Run(); context.WithTimeout is in sandbox.ExecStreamReader via
  claude.go per iteration.
- Document that models.skill is unused for this CLI runner (real model is
  harness/code.yaml); note validation_loop schema-fail as a pr_created cause
  distinct from timeout/budget; mark annotations state: as human-reference.
- Backport the guarded fixture-state.json lookup to forbidden_labels in
  eval/fix and eval/review (and review's required_labels) so the shared-
  verbatim contract with this file holds again.

Signed-off-by: Adam Scerra <ascerra@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
- End cleanup()'s EXIT trap with `true` so a false METRICS_TMP existence
  check can no longer override an already-issued exit "$rc" (bash trap
  return-status semantics). Reproduced: success path and empty
  METRICS_TMP both reported exit 1; after the fix they report 0/5.
- Move eval/fix/eval.yaml's EVAL_TIMEOUT from runner.env into
  execution.env (same CliRunner injection fix already applied to code),
  and correct the comment that still pointed at runner.env.

Signed-off-by: Adam Scerra <ascerra@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@ascerra
ascerra force-pushed the eval/code-functional-test branch from 66f23ca to 2fed84f Compare July 29, 2026 22:22
@fullsend-ai-retro

fullsend-ai-retro Bot commented Jul 29, 2026

Copy link
Copy Markdown

🤖 Finished Retro · ✅ Success · Started 10:35 PM UTC · Completed 11:17 PM UTC
Commit: 2fed84f · View workflow run →

@fullsend-ai-retro

Copy link
Copy Markdown

Retro: PR #177test(eval scenario): add code agent functional eval scenario

Workflow: Human-authored PR (ascerra, with Cursor AI co-authoring) adding the first code agent functional eval scenario (eval/code/). Merged after 14 days, 11 commits, and 4 rounds of human review. The review agent ran 26 times (20 success, 3 cancelled, 1 failure). Agents repo confirmed as fullsend-ai/agents (resolved from org config at v0).

Review quality gap

This PR exposed a significant gap between the review agent and human reviewers on infrastructure code with cross-system dependencies. Human reviewer waynesun09 identified 6 HIGH-severity findings across 4 review rounds, all requiring tracing through upstream codebases (envfile.go, cli_runner.py, sandbox.go). The review agent found only low/medium severity surface-level issues and approved 10+ times on commits with undetected HIGH bugs.

Key HIGH findings caught exclusively by humans:

  • emit_env format mismatch: Function escaped characters for a parser (envfile.go) that has no escape-sequence support — backslash-escaping would truncate/corrupt values.
  • EVAL_TIMEOUT placement: Key under runner.env but CliRunner only reads execution.env — timeout silently fell back to default. Same bug existed in eval/fix/eval.yaml.
  • EXIT trap exit code corruption: cleanup trap's last statement could return false, overriding the script's real exit code. Confirmed in live CI logs.
  • Broken test fixture: from calc import add fails without conftest.py under pytest's default import mode.

The correctness sub-agent's own "Runtime mechanism checklist" instructs it to "trace the full producer-to-consumer path, verify format expectations match between components." It flagged [low] incomplete-escaping on emit_env (backtick and dollar-sign gaps) but never traced to the actual parser — diagnosing the symptom at wrong severity while missing the root cause.

Evidence for existing issues

  • Shell script review gaps (agents #490, #131; fullsend #1375): The EXIT trap exit-code bug and emit_env parser mismatch are concrete new examples of the review agent missing shell correctness issues that require behavioral reasoning beyond pattern matching.
  • Review dispatch volume (agents #108; fullsend #5139, #1418, #2599): 26 review runs on a single 12-file PR over 14 days. Multiple runs targeted the same commit or were triggered by bot review events. This PR adds another data point to the 16+ existing issues tracking redundant dispatch.
  • Approval while HIGH bugs exist (agents #285, #370): The review agent approved on commits that human reviewers subsequently identified as having HIGH-severity bugs. The bot's approval signal provided no meaningful gate.

What went well

  • Human review was exceptionally thorough — waynesun09 used multi-model review squads (Claude, Grok, Gemini), independently reproduced bugs, and traced through 4+ upstream codebases.
  • The eval framework design (case-based, judge-driven, harness-compatible) follows established patterns cleanly.
  • The review agent provided fast initial feedback on docs gaps and minor edge cases, functioning effectively as a linter-plus layer.

Proposals filed

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

Labels

code-agent requires-manual-review Review requires human judgment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add code agent functional eval (pre→sandbox→post PR creation)

4 participants