-
Notifications
You must be signed in to change notification settings - Fork 12
feat(#677): add needs_input pushback for the code agent #682
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
0f47374
0ed533f
c9c813e
907f531
0c272f2
9cfd4cb
cb089aa
a77d364
114c650
0114788
0285f1c
60ebbe6
9d5fea2
977a9ec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,3 @@ | ||
| __pycache__/ | ||
| *.pyc | ||
| docs/plans/ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,6 +35,7 @@ on issues (not PRs). | |
| |-------|---------| | ||
| | `ready-to-code` | Triggers the code agent. Applied by the [triage](triage.md) agent for low-risk categories (bug, documentation, performance), or manually by a human for feature work after prioritization. Not applied when the triage result sets `requires_workflow_changes`, since the code agent cannot modify workflow files. | | ||
| | `ready-for-review` | Applied by the code agent after pushing a PR. In per-repo installs, triggers the [review agent](review.md) when applied to a PR. Also marks workflow state for humans and the [retro agent](retro.md). | | ||
| | `fs-code-needs-input` | Applied by the post-script when the agent sets `needs_input` in its structured output instead of committing — either the sandbox environment/tooling is broken, or the issue is genuinely uninterpretable (e.g. contradictory requirements). Removes `ready-to-code`. No PR is opened; the agent posts a comment explaining what it needs. Remove the label and re-trigger with `/fs-code` once resolved. | | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [MEDIUM]
Suggestion: add a Control-labels row for |
||
|
|
||
| ## Configuration | ||
|
|
||
|
|
@@ -48,6 +49,7 @@ See [Customizing with AGENTS.md](https://fullsend.sh/docs/guides/user/customizin | |
| | `CODE_ALLOWED_TARGET_BRANCHES` | Restricts which branches the code agent can target when pushing. The post-code script validates the agent's chosen target branch against this variable before pushing. Set via `env.runner` in `harness/code.yaml` (never injected into the sandbox). | Repo default branch (auto-detected via GitHub API; falls back to `main`) | Comma-separated branch names (e.g. `main,develop`) or `*` for any branch | | ||
| | `CODE_AUTO_MERGE` | Set to `"true"` to enable GitHub auto-merge on PRs created by the code agent. Requires branch protection with required reviews or status checks on the target branch. Read directly from the runner environment (not declared in `env.runner`). | `""` (disabled) | `"true"` to enable | | ||
| | `CODE_AUTO_MERGE_METHOD` | Merge method for auto-merge: `"squash"`, `"rebase"`, or `"merge"`. When unset, auto-detected from the repo's allowed merge methods (prefers squash). Omitted automatically when the target branch uses a merge queue. Ignored unless `CODE_AUTO_MERGE` is `"true"`. | Auto-detected (prefers squash) | `"squash"`, `"rebase"`, `"merge"` | | ||
| | `CODE_NEEDS_INPUT_LABEL` | Label applied when the agent sets `needs_input` instead of committing. Forwarded from the runner environment via `env.runner` in `harness/code.yaml`. The script defaults to `fs-code-needs-input` when unset. | `fs-code-needs-input` | Any valid GitHub label name | | ||
|
|
||
| ## How the agent works | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| # Expected outcome after the code agent + post-script run. | ||
| # Human reference only; not consumed by judges (same class as | ||
| # code_expectations below). | ||
| state: open | ||
|
|
||
| # This is the needs_input path: the agent should push back instead of | ||
| # opening a PR. See eval.yaml's pr_created judge for how this flag is used. | ||
| expect_pr: false | ||
|
|
||
| labels: | ||
| required: | ||
| - fs-code-needs-input | ||
| forbidden: [] | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [MEDIUM]
Suggestion: add |
||
|
|
||
| # Code agent budgets. The agent should recognize the contradiction quickly | ||
| # (read issue + repo, conclude no conservative interpretation exists) rather | ||
| # than attempting an implementation, so this budget is tighter than | ||
|
ralphbean marked this conversation as resolved.
|
||
| # 001-fix-add's. | ||
| # Observed baseline: 21 turns / $0.64 (CI run 31042840745). Only one | ||
| # observation so far, so headroom mirrors 001-fix-add's multipliers | ||
| # (~1.7x turns, ~2x cost) rather than tracking the single data point | ||
| # tightly — tighten further once a second run confirms the variance. | ||
| max_turns: 35 | ||
| max_cost_usd: 1.25 | ||
|
|
||
| # Human reference only; not consumed by judges (same class as 001-fix-add's | ||
| # code_expectations). | ||
| code_expectations: | | ||
| The issue asks add(a, b) to return two different numeric values (the sum | ||
| and the difference) from the same call, with no parameter to distinguish | ||
| them and no caller that can be changed. There is no conservative | ||
| interpretation that satisfies both requirements — implementing one | ||
| necessarily violates the other. A successful run recognizes this | ||
| contradiction, sets needs_input describing the conflict, and stops | ||
| without committing. Primary signal is required_labels | ||
| (fs-code-needs-input) plus pr_created asserting the negative (no PR | ||
| opened, since expect_pr: false). | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| forge: github | ||
| fixture: | ||
| type: issue | ||
| title: "add() must return both the sum and the difference for the same call" | ||
| body: | | ||
| ## Feature Request | ||
|
|
||
| `add(a, b)` in `calc.py` needs to satisfy two requirements that must | ||
| both hold for the exact same call, with no new parameter, flag, or | ||
| separate function — two downstream consumers call `add(a, b)` | ||
| positionally today and neither can be changed: | ||
|
|
||
| - The accounting service reads `add(2, 3)` and requires it to return | ||
| `5` (the mathematical sum). | ||
| - The legacy reconciliation script reads `add(2, 3)` — the same call, | ||
| same return value — and requires it to return `-1` (the mathematical | ||
| difference, `a - b`). | ||
|
|
||
| Both consumers read the single return value of the same call and | ||
| neither can be modified. Please make `add()` satisfy both requirements | ||
| for the same input. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ../../repos/tiny-calc-neutral |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| # tiny-calc-neutral | ||
|
|
||
| Minimal Python calculator used by the code agent functional eval. | ||
|
|
||
| Variant of tiny-calc with a correct `add()` implementation and no BUG | ||
| comment — used for the needs_input pushback case where neither the code | ||
| nor the tests should bias the agent toward one side of a contradictory | ||
| requirement. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| # Tiny calculator — neutral implementation for the needs_input eval case. | ||
|
|
||
|
|
||
| def add(a: int, b: int) -> int: | ||
| """Return the sum of a and b.""" | ||
| return a + b |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| """Tests for calc module.""" | ||
|
|
||
| from calc import add | ||
|
|
||
|
|
||
| def test_add() -> None: | ||
| assert add(2, 3) == 5 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [MEDIUM] Fixture still asserts one specific answer to the stated contradiction This fixture was added to replace the shared Suggestion: remove or neutralize the behavioral assertions in this file (e.g., signature-only checks) so neither the implementation nor the tests favor one side of the contradiction the eval case is designed to test. |
||
|
|
||
|
|
||
| def test_add_negative() -> None: | ||
| assert add(-1, -2) == -3 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| #!/usr/bin/env bash | ||
| # code-result-schema-test.sh — Test validate-output-schema.sh against | ||
| # schemas/code-result.schema.json fixtures. | ||
| # | ||
| # Run from the repo root: | ||
| # bash scripts/code-result-schema-test.sh | ||
|
|
||
| set -euo pipefail | ||
|
|
||
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | ||
| VALIDATOR="${SCRIPT_DIR}/validate-output-schema.sh" | ||
| SCHEMA="${SCRIPT_DIR}/../schemas/code-result.schema.json" | ||
| FAILURES=0 | ||
|
|
||
| TMPDIR="$(mktemp -d)" | ||
| trap 'rm -rf "${TMPDIR}"' EXIT | ||
|
|
||
| run_test() { | ||
| local test_name="$1" | ||
| local json_content="$2" | ||
| local expect_pass="$3" # "true" or "false" | ||
| local expect_output="${4:-}" # optional: substring that must appear in stdout | ||
|
|
||
| local test_dir="${TMPDIR}/${test_name}" | ||
| mkdir -p "${test_dir}/output" | ||
| echo "${json_content}" > "${test_dir}/output/agent-result.json" | ||
|
|
||
| local exit_code=0 | ||
| FULLSEND_OUTPUT_SCHEMA="${SCHEMA}" \ | ||
| bash -c "cd '${test_dir}' && bash '${VALIDATOR}'" > "${TMPDIR}/stdout.log" 2>&1 || exit_code=$? | ||
|
|
||
| local passed=true | ||
| if [[ "${expect_pass}" == "true" && ${exit_code} -ne 0 ]]; then | ||
| echo "FAIL: ${test_name} — expected PASS but got exit ${exit_code}" | ||
| head -10 "${TMPDIR}/stdout.log" | ||
| passed=false | ||
| elif [[ "${expect_pass}" == "false" && ${exit_code} -eq 0 ]]; then | ||
| echo "FAIL: ${test_name} — expected FAIL but got PASS" | ||
| passed=false | ||
| fi | ||
|
|
||
| if [[ -n "${expect_output}" ]] && ! grep -qF "${expect_output}" "${TMPDIR}/stdout.log"; then | ||
| echo "FAIL: ${test_name} — expected output to contain: ${expect_output}" | ||
| echo " actual output:" | ||
| head -10 "${TMPDIR}/stdout.log" | ||
| passed=false | ||
| fi | ||
|
|
||
| if [[ "${passed}" == "true" ]]; then | ||
| echo "PASS: ${test_name}" | ||
| else | ||
| FAILURES=$((FAILURES + 1)) | ||
| fi | ||
| } | ||
|
|
||
| # --- Regression: existing schema behavior --- | ||
|
|
||
| run_test "valid-target-branch-only" \ | ||
| '{"target_branch":"main"}' \ | ||
| "true" | ||
|
|
||
| run_test "valid-with-pr-body-and-closes-issue" \ | ||
| '{"target_branch":"main","pr_body":"desc","closes_issue":false}' \ | ||
| "true" | ||
|
|
||
| run_test "invalid-missing-target-branch" \ | ||
| '{"pr_body":"desc"}' \ | ||
| "false" | ||
|
|
||
| run_test "invalid-unknown-property" \ | ||
| '{"target_branch":"main","bogus_field":"x"}' \ | ||
| "false" | ||
|
|
||
| # --- needs_input field --- | ||
|
|
||
| run_test "valid-with-needs-input" \ | ||
| '{"target_branch":"main","needs_input":"scan-secrets helper not found"}' \ | ||
| "true" | ||
|
|
||
| run_test "valid-needs-input-without-target-branch" \ | ||
| '{"needs_input":"sandbox tooling broken — cannot determine target branch"}' \ | ||
| "true" | ||
|
|
||
| run_test "invalid-needs-input-empty-string" \ | ||
| '{"target_branch":"main","needs_input":""}' \ | ||
| "false" | ||
|
|
||
| TOO_LONG_INPUT="$(printf 'a%.0s' {1..4001})" | ||
| run_test "invalid-needs-input-too-long" \ | ||
| "{\"target_branch\":\"main\",\"needs_input\":\"${TOO_LONG_INPUT}\"}" \ | ||
| "false" | ||
|
|
||
| # --- Summary --- | ||
|
|
||
| echo "" | ||
| if [[ ${FAILURES} -gt 0 ]]; then | ||
| echo "${FAILURES} test(s) failed" | ||
| exit 1 | ||
| fi | ||
| echo "All tests passed" |
Uh oh!
There was an error while loading. Please reload this page.