Skip to content

fix(appworld): grade via persistent AppWorld session, not hardcoded -1 - #352

Open
cdoron wants to merge 1 commit into
Exgentic:mainfrom
cdoron:fix/appworld-bridge-persistent-grading
Open

fix(appworld): grade via persistent AppWorld session, not hardcoded -1#352
cdoron wants to merge 1 commit into
Exgentic:mainfrom
cdoron:fix/appworld-bridge-persistent-grading

Conversation

@cdoron

@cdoron cdoron commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • AppWorld's evaluation is fully local/offline (world.evaluate()), but /grade.sh hardcoded reward=-1 as if it were externally graded, so every one of the 732 tasks scored -1 regardless of the agent's actions — and the agent had no way to call AppWorld's APIs at all (no task data installed, no server running).
  • Adds bridge.py: a root-owned HTTP service holding one long-lived AppWorld session per task, exposing a sanitized POST /execute {"code": ...} route. AppWorld() destructively re-initializes on construction, so grading reuses that same live session via SIGUSR1 (root-only — the agent shares the container's network namespace, so any HTTP route would be reachable to it regardless of uid) rather than opening a second session that would discard the agent's progress.
  • Updates README.md to describe the new task boilerplate/bridge mechanism, and adds AUDIT.md documenting live verification (oracle re-run scoring 0.0 on a no-op / 1.0 on a genuine solve, isolation and reward-hacking checks).

Test plan

  • docker build --platform linux/amd64 --no-cache succeeds
  • Live oracle re-run against task 692c77d_2: no-op → reward=0.0; genuine solve (real apis.spotify.* calls) → reward=1.0
  • Isolation checks as uid 1002: /appworld, ground_truth/*, /tasks/<id>/id.txt all permission-denied; GET /evaluate 404s; kill -USR1 on the bridge as uid 1002 → Operation not permitted
  • Re-record tests/run/replay/fixtures/appworld-{292-terminus-2,584-claude-code}.traces.jsonl against the fixed image (predate this fix, flagged as a residual in AUDIT.md — not blocking this PR)

🤖 Generated with Claude Code

@cdoron
cdoron force-pushed the fix/appworld-bridge-persistent-grading branch 2 times, most recently from cb065fe to 490584f Compare August 12, 2026 08:21
AppWorld's evaluation is fully local/offline (world.evaluate()), but
/grade.sh previously hardcoded reward=-1 as if it were externally graded,
so every one of the 732 tasks scored -1 regardless of the agent's actions.

Fix: bridge.py holds one long-lived AppWorld session for the task, started
by /entrypoint.sh before the agent runs, exposing only a sanitized
POST /execute {"code": ...} route over HTTP. AppWorld() destructively
re-initializes on construction, so grading reuses that same live session
via SIGUSR1 (root-only, since the agent shares the container's network
namespace and any HTTP route would be reachable to it regardless of uid)
rather than opening a second session that would discard the agent's
progress.

Verified live: oracle re-run on task 692c77d_2 scores 0.0 on a no-op and
1.0 on a genuine solve, with isolation/reward-hacking checks confirming
the agent's uid can't reach /evaluate or signal the bridge itself. See
AUDIT.md for full evidence.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Doron Chen <cdoron@il.ibm.com>
@cdoron
cdoron force-pushed the fix/appworld-bridge-persistent-grading branch from 490584f to bba0d7d Compare August 12, 2026 08:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant