Skip to content

Feat/sharpa state verifier - #52

Closed
rebecca-wu-applovin wants to merge 14 commits into
OpenWAM-Official:mainfrom
rebecca-wu-applovin:feat/sharpa-state-verifier
Closed

rebecca-wu-applovin wants to merge 14 commits into
OpenWAM-Official:mainfrom
rebecca-wu-applovin:feat/sharpa-state-verifier

Conversation

@rebecca-wu-applovin

@rebecca-wu-applovin rebecca-wu-applovin commented Oct 16, 2026 •

Copy link
Copy Markdown

No description provided.

rebecca-wu-applovin and others added 14 commits October 9, 2026 21:46
The closed-loop sim eval builds and loads GWP-0.5 Sharpa EEF checkpoints through the training
script, so these pieces are needed on a fresh checkout:

- scripts/parallel_sharpa_forward_checks/run_giga_sharpa_train_job.py: model config, view
  composition, image preprocessing and action sampling (also the training entry point)
- scripts/sharpa_eef/{action_codecs,canonical,eef_kinematics}.py and
  scripts/lingbot_sharpa78/sharpa78_schema.py: giga codec, canonical wrist frame, FK/IK
- openwam/dataloader/{sharpa_eef,sharpa_hand}.py, registry entries, and video_io decoder
  threading: the LeRobot readers behind SharpaGigaEEFDataset (used by the parity check)
- assets/robot_descriptions: YAM Ultra and SharpaWave URDFs (kinematics only, meshes are not
  included) and the reference rollout used by the schema
- assets/giga_sharpa_train_job/prep_eef_full_v1: the norm stats and T5 task embeddings the
  policy was trained with
- submodules third_party/giga-world-policy (model, sampler, ZMQ inference server) and
  third_party/WebXR-Teleop (TacoSim, scene reset)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
benchmarks/sharpa_webxr runs the Sharpa EEF policy closed loop in the WebXR-Teleop MuJoCo sim,
following the GWP-0.5 wam.cpp recipe:
- observe, predict a 32-step chunk, execute it fully, re-observe
- 10 flow steps, shift 5, no CFG
- the training view composition, norm stats and prompts
- wrist poses solved to arm joints with IK; hand joints pass through

Validated against the recorded data:
- renders reach PSNR 37.7 / 42.1 dB
- replaying recorded setpoints reproduces the outcome to 1.2 mm
- policy inputs are bit-identical to training

Pieces:
- scenes.py: materialize live-store scenes to data/webxr_scenes
- env.py: TacoSim with teleop defaults, seeded prepare_scene reset, dataset cameras
- policy_server.py: checkpoint behind the giga-models ZMQ server (--prep-dir)
- rollout.py: closed-loop episodes against a policy server
- postprocess.py: reach/grasp/move/fallen signals, rule verifier, judge keyframes
- judge.py: Claude judge over keyframes
- render_video.py: atomic video.mp4 writes
- run_suite.sh: one checkpoint x 58 scenes x seeds; RUN selects the output dir
- watch_run.sh: automatic eval of every checkpoint of a run
  - copies raw weights before rotation
  - evaluates EMA and raw on their own GPUs
  - renders videos from one shared low-priority queue
  - runs final seeds and best_val once training exits
  - options: VIDEO, VIDEO_SEEDS, VIDEO_P, PORT_BASE, PREP_DIR
- aggregate.py: rates with Wilson CIs
- build_review_page.py: side-by-side video page
- check_policy_parity.py: offline input/loss/chunk-error gate
- make_gt_rollouts.py: replay demos as reference rollouts
- tasks.json: 58 scenes with prompts and step limits

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
notebooks/_build_gwp_sim_eval_nb.py builds and executes notebooks/gwp_sim_eval.ipynb. It covers:
- stage rates per checkpoint, with 95% CIs
- all-three rate vs training step for EMA and raw, with the v1 range as reference
- where rollouts stop
- gpt_fleet_depth vs simteleop-only scenes
- a per-scene heatmap
- IK, fallen objects and episode length
- keyframe grids and embedded videos for the best and worst scenes

The executed copy holds gwp05_full_v1 (steps 3250-5000) and gwp05_full_v2 step 5000.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eneric runner

The env no longer knows about any model. It takes one action per 50 ms control step and returns the
observations it was configured with:
- action_space "joint" (56 joint targets) or "eef" (wrist pose per arm plus hand joints; the env
  runs the IK, seeded from the previous solve)
- ObsConfig selects cameras with their resolution, and state keys (joints, eef, object_poses, time)
- step(action, observe=...) renders only when the caller asks for an observation

Everything model-specific moves to policies/<name>.py:
- what to observe, the input encoding and the model call
- the execution strategy: act(obs) returns the actions to run before the next observation (a full
  chunk, its first k steps, or one step)

rollout.py is now a generic loop over any registered adapter (--policy, --policy-kw).

policies/gwp05.py keeps the wam.cpp GWP-0.5 recipe and reproduces the previous rollouts bit for bit:
on v2 step 5000 EMA, scene 00178 seed 3, qpos, IK results and stage signals are identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
score.py <folder> judges each rollout under the folder with an OpenAI model (default gpt-6-luna):
- input: the 16 keyframes, ego and chest views, with the task prompt
- rubric and JSON schema: the same as judge.py
- output: reach / grasp / move / complete / success

Each verdict is cached as <rollout>/judge_<model>.json. The script writes these into <folder>:
- scores_<model>.json: overall and per-task success rate (with Wilson CI), the GWP-0.5 graded score
  (0.25 per stage), judge and sim stage rates with their agreement, and per-seed rows
- scores_<model>.tsv: one line per task

The key comes from OPENAI_API_KEY or ~/.config/openai/key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
scenes.py wrote manifest file names under the scene folder without checking them, and guarded zip
members (and the sha256 checks) with assert, which python -O strips. Every destination now goes
through _within(), which resolves the path and raises ValueError if it escapes the scene directory
(absolute names, .., symlinks). Hash mismatches raise as well.

Addresses review comments on benchmarks/sharpa_webxr/scenes.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
run_suite.sh:
- each xargs worker now exits 1 if its rollout or postprocess failed
- the suite prints FAILED and exits 1 instead of ALL_DONE

watch_run.sh:
- DONE_<seeds> is written only when the suite exited 0 and every scene x seed has signals.json
- otherwise the tag is retried on the next pass, up to MAX_ATTEMPTS (default 2)
- after that it is marked FAILED_<seeds> and skipped, so a broken checkpoint cannot stall the
  watcher; delete the marker to retry

Tested with a stub interpreter: one failing scene gives suite exit 1 and FAILED; all passing gives
exit 0 and ALL_DONE.

Addresses review comments on run_suite.sh and watch_run.sh.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- add sharpa_hand_stats_computation.py and its sharpa_hand.yaml config (placeholder dataset_dir)
- the missing-stats error now gives the full command that writes meta/normalization_stats.npy
  in the schema the reader checks

Addresses the review comment on openwam/dataloader/sharpa_hand.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Episode limit:
- limits.limit(scene) = min(ceil(1.5 x p90 demo length), 20 s)
- the runner, signals, judge frames and videos all use it
- the policy is causal, so older rollouts are re-scored by truncation without re-running

Judge (score.py --rubric r2, now the default):
- postprocess.py --frames-only writes 24 times x (head, chest) frames at 480x640 to judge_frames/
- RUBRIC_R2 needs visible lift or carry for grasp, and purposeful displacement toward the goal
  for move; touching, nudging and knocking count as neither
- r1 is kept for comparison

On the sandwich rollout (v2 step 5000 EMA seed 0), r1 gave grasp+move (graded 0.75); r2 gives
grasp only (0.50). Success is false in both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, one runner

Two designs had been written for the same layer:
- client-side adapters in this PR (policies/gwp05.py, which decoded on the sim side)
- per-model servers on the policies/common.py contract (in the working tree)

They collided on policies/__init__.py and on rollout.py helpers. This keeps the server contract as
the only model interface:
- policies/common.py + policies/gwp_server.py: the server owns image layout, state encoding,
  sampler and decode. Its meta publishes execute_steps (GWP: 20 of 32, as upstream's 30 of 48) and
  obs_offsets. gwp_server gains --prep-dir.
- rollout.py: one runner for every server, built on the model-agnostic env (action_space "eef",
  IK in the env). It reads the meta and observes only at the rows the next request needs, so
  servers that want past frames (LDA-1B's t-5) work.
- removed: the client-side adapter registry and gwp05.py
- watch_run.sh / run_suite.sh: SERVER selects the server script, EXECUTE_STEPS overrides meta

Parity, bit for bit (qpos and IK identical, 271 steps):
- runner + gwp_server.py --execute-steps 32 vs the old policy_server.py client-decode rollout
  (v2 step 5000 EMA, scene 00178 seed 3)
- runner at execute_steps 20 vs rollout_v2.py (v2 step 15000 EMA, 00178 seed 0)

The other model servers (cosmos3, genie, lda1b, motus, xwam) follow in a separate PR with their
training-script and submodule dependencies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A background judge loop writes judge frames and runs score.py (rubric r2) on every
tag with a new DONE marker, so one watcher now waits for checkpoints, evaluates,
renders videos and rates. JUDGE, JUDGE_WEIGHTS, JUDGE_MODEL and JUDGE_WORKERS
control it; it is skipped when no OpenAI key is configured.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ame completeness

- API.md: the policy-server contract (meta / inference request and response, canonical pose layout,
  control loop) and how to add a model.
- policies/hold_server.py: no-model reference server and template.
- check_server.py: conformance check for any server (meta fields, shapes, rotations, frame, absolute
  first-step pose, latency).
- task_breakdown.py: per-category and per-task judge success (k/n) across score files.
- postprocess.py / score.py: judge_frames now writes COMPLETE last and drops cached verdicts when it
  re-renders; score.py refuses frames without it. Before, a render killed mid-write left a partial
  judge_frames dir that postprocess skipped forever, and score.py judged only the frames it found.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Model-agnostic version of watch_run.sh (which stays as the GWP-specific watcher). The model side provides only
its policy server script (--server) and a command that lists complete checkpoints as
"<global_step> <ckpt> [extra server args]" (--list-ckpts). Same eval queue, retries, DONE/FAILED markers,
video renderer, judge pass and final seed top-up as watch_run.sh, plus --done-file / --once, --stride,
--only-steps, --sync-cmd and --stop-file (checked between every evaluation, so a capped container can be
restarted during a backlog). API.md documents it with checkpoint-listing examples; hold_server.py accepts
--ckpt so watchers can start it like any server.

Tested with hold_server: a server that fails to start is retried then marked FAILED, the next checkpoint runs
58/58 rollouts to DONE + summary, WATCH_ALL_DONE, and a rerun skips both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d teleop episodes

Adds a deterministic grader for the 58 Sharpa WebXR scenes. Each scene gets an
ordered stage chain (task_specs.json, built by _build_task_specs.py) of
simulator state checks: reach, grasp, hold_lifted, place_in, move_out,
joint_goal, relative_pose, orientation, oscillation, free_flight, tilt_over,
contact_events, sweep. 44 scenes are decided entirely from state; the 14 pour,
tool-use and unsimulated-target scenes verify the prefix in sim and end in a
judge stage, with a judge_context summary of the sim facts.

verify.py replays trajectory.npz through the MuJoCo scene and writes
verifier.json per rollout with any-time and end-state results for every stage.

pull_episodes.py fetches the human-labelled teleop episodes (success / recover /
failure) into the rollout format; calibrate.py scores the verifier against those
labels (sim-only scenes: recall 0.959, specificity 0.849, n=2183);
compare_judge.py compares it with the gpt-6-luna judge on policy rollouts
(agreement 0.806 on 870 rollouts; judge over-accepts articulated and grasp-only
scenes, under-accepts placements).

score.py is unchanged; integration follows once the specs are reviewed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@rebecca-wu-applovin
rebecca-wu-applovin deleted the feat/sharpa-state-verifier branch October 16, 2026 03:52
@rebecca-wu-applovin
rebecca-wu-applovin restored the feat/sharpa-state-verifier branch October 16, 2026 03:53
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