Skip to content

agent_sdk+code_sim_learning: physics-margin capture gate on honest sysID posterior widths (sysID stack 4/8) - #99

Merged
yichao-liang merged 6 commits into
masterfrom
sysid-04-margin-gate
Jul 30, 2026
Merged

agent_sdk+code_sim_learning: physics-margin capture gate on honest sysID posterior widths (sysID stack 4/8)#99
yichao-liang merged 6 commits into
masterfrom
sysid-04-margin-gate

Conversation

@yichao-liang

Copy link
Copy Markdown
Collaborator

Fourth of eight PRs landing the domino-sysid branch on master in sequence (after #96, #97, #98). Tree is identical to the corresponding slice of that branch rebased onto current master.

This chunk

  • scripts: log_viewer shows the per-cycle interaction video on explore pages
  • agent_sdk: decorrelate motion-planner seeds across validation/trials rollouts
  • envs: stop leaking the domino counterfactual-probe PyBullet world on env disposal
  • agent_sdk+code_sim_learning: physics-margin capture gate on honest sysID posterior widths
  • configs: agent_po_predicate_invention_al_margin treatment arm; exp_domino runs it

The physics-margin capture gate: validated captures must also survive replays at sigma-point physics drawn from the sysID posterior, so knife-edge plans that only work at the point estimate stop being banked. Plus the al_margin treatment arm that runs it, decorrelated motion-planner seeds so repeated validation rollouts are independent samples, and a PyBullet world-leak fix that froze 16GB machines at 6 parallel runs.

🤖 Generated with Claude Code

…pages

An explore transcript now gets a video player like test pages do, mapping
its round (= 0-based learning cycle) to the make_interaction_videos
recording ...__cycle<N>.mp4, which has no __task part and concatenates
all explore episodes of the cycle. Also rewords two docstrings that were
failing CI's docformatter check.
…rollouts

Fresh-env repeats were bit-identical replays: every stochastic step
(BiRRT sampling, IK restarts) reads the constant CFG.seed at call time,
so trials=N and the 3/3 capture-validation gate measured ONE effective
sample. run_20260722_204632 (domino_high_friction_turn seed2) captured
a plan verified 10/10 trials + 3/3 validation that missed the single
real episode; every trials-mode result in that run was 0/N or N/N,
never mixed, and a seed-jittered replay flipped the '10/10' plan to
~50/50 (one trial reproducing the real episode's exact failure state).

decorrelated_rollout_seed(rollout_idx) offsets CFG.seed per repeat
(index 0 keeps the base seed so the canonical rollout stays
reproducible; entered after fresh-env creation so task-cache keys are
unaffected), wired into the sim.run trials loop and the capture
validation repeats. Trials output now states the seed variation.
…env disposal

PyBulletDominoComposedEnv lazily creates a second full PyBullet client
(the counterfactual-push-probe world) on first certificate evaluation,
and every disposal path disconnected only the main client - so each
fresh validation env / solved=True trial that scored the evaluator
leaked a complete physics world (~150MB, one live client per
create-probe-dispose cycle). Six parallel runs accumulating this froze
a 16GB machine (2026-07-23).

Fix at three layers: a dispose() override on the domino env that
releases the probe world first (a dead main client in crash recovery
must not strand it) then the main client; dispose_env delegates to
env.dispose() when available instead of raw-disconnecting one client;
the two remaining direct-disconnect sites in the approach (learn-phase
fit env, _recreate_base_env) now go through dispose_env. main.py's
per-episode fresh_env.dispose() picks up the override automatically.

Verified: create-probe-dispose cycles now end at 0 connected clients
(was 1 leaked per cycle); disposal is safe when no probe was created.
…sID posterior widths

run_20260723_091108 seed1: the captured plan validated 8/8 (sim.run
trials + 3/3 fresh-env decorrelated-seed rollouts) at the fitted
lateral_friction 0.5319 and failed the single real episode
deterministically at true 0.5 - replay shows the design's success band
starts between 0.52 and 0.5319, so the 6% fit bias alone flipped the
outcome at every planner seed. Execution-validation repeats all run AT
the fitted values and cannot see zero margin to parameter error; and
the identifiability report printed posterior_std=0 ("identified",
contraction 0.00), so every uncertainty consumer believed friction was
known exactly.

Honest widths (always on):
- grid_seed._resolved_interval: the flat set widened to fit-space
  midpoints toward the nearest REJECTED evaluations. Kills the
  bisection-collapse zero: refine midpoints lower the best SSE, the
  relative flat tolerance shrinks, the flat set collapses to one point,
  and a zero-width flat_interval claimed certainty the sweep's finite
  evaluations cannot support.
- identifiability_report prefers resolved_interval and floors all
  single-sample widths at code_sim_learning_rollout_min_posterior_width
  (fit-space, default 0.1 ~ +-10% for log params) - the free-running
  replay objective's model bias (measured 1-40% off truth on clean
  data) is invisible to any local landscape statistic. 0.1/0.75 prior
  is contraction 0.13, still "identified": the floor reports honest
  width, it does not veto deployment.

Physics-margin capture gate (flag agent_plan_validation_physics_margin,
default False; the treatment arm enables it):
- physics_sigma_points turns the applied params + report widths into
  two full override dicts (all deployed params at -1 sigma, then +1;
  multiplicative for log scale, box-clipped). Only applies_fitted
  verdicts are perturbed - anchored/NOT-identified params carry
  prior-scale widths the fit was never asked to resolve.
- The approach stores the points after each joint rollout fit, clears
  them on every _apply_identified_physical_params so they cannot
  outlive their fit, and exposes ctx.physics_margin_provider;
  _fresh_validation_env_scope gains a physical_overrides kwarg applied
  to the fresh env on top of the identified params.
- evaluate_option_plan runs the margin rollouts after execution
  validation passes, on fresh envs at the BASE planner seed (failure
  attributable to physics alone), and refuses with "PARAM-SENSITIVE
  (plan NOT captured)" + per-point outcomes (new
  CaptureDecision.PARAM_SENSITIVE_NO_CAPTURE; best-effort submissions
  still capture with an honest note). At the run's numbers the -1
  sigma point is 0.5319*exp(-0.1)=0.481 < the 0.52 flip point, so this
  capture would have been refused in-session.
…mino runs it

Identical to agent_po_predicate_invention_al plus
agent_plan_validation_physics_margin: True (and the posterior-width
floor stated explicitly). The base arm keeps byte-identical behavior
(the gate flag defaults False), so comparing the two isolates what the
physics-margin capture gate adds. exp_domino.yaml now runs the margin
arm in place of the base arm.
@yichao-liang
yichao-liang force-pushed the sysid-04-margin-gate branch from 387814b to 5a6b6b3 Compare July 30, 2026 09:00
@yichao-liang
yichao-liang merged commit e963127 into master Jul 30, 2026
14 checks passed
@yichao-liang
yichao-liang deleted the sysid-04-margin-gate branch July 30, 2026 09:18
yichao-liang added a commit that referenced this pull request Jul 31, 2026
… pipeline figure (#107)

* docs/sysid: redraw the pipeline figure around the landed honesty fixes

The pipeline diagram was still telling the pre-#99 story: red badges on
four failure modes with the remedies listed as "planned". All six fixes
have since landed on master (PRs #99-#103), so the badges are now green
and each one names the run that motivated it, with the legend stating
what it replaced.

Content changes to the diagram:
- New DATA-lane box for declaration evidence (sim.residuals with
  open-loop rollout plus per-param box sweep), and a CONSUMERS-lane box
  for the combined-substrate certificate probe.
- Stage bodies updated to what the code does now: Huber-capped SSE with
  summary terms, dropped fits feeding hull candidates, the 32-point
  sweep running over the disagreement hull, INCONSISTENT swept rather
  than disarming the gate, dropped params reverting to registry values.

Layout and output changes:
- Box y-centers are computed in _layout() from per-lane stack heights
  instead of being hand-placed, so adding a box no longer risks silent
  overlap with its neighbour.
- draw() takes an output path and an include_legend flag, and the script
  now also emits sysid_pipeline_compact.png for decks that carry the
  caption themselves.

The landscape figure gets real matplotlib legends in place of the
end-of-line annotations, which collided with the curves once the y-range
widened, and the "true 0.5" marker moves to the bottom of the axes.

* docs/slides: collect the reveal.js decks into one directory

The three decks had accumulated in three different places (docs/,
docs/envs/, docs/sysid/), which made them hard to find and left
build_deck.py writing its output next to unrelated figure sources. They
now all live under docs/slides/, with shared figures in
docs/slides/assets/.

Path consequences of the move:
- build_deck.py writes to docs/slides/ instead of docs/.
- The sysID deck refers to its two figures as ../sysid/*.png rather than
  as siblings, so the figures stay next to the scripts that generate
  them. docs/sysid/README.md records that coupling.

The two hand-written decks are also refreshed while being moved:
- The agent planning deck is rebuilt around run_20260729_232733 (seed 2)
  in place of run_20260723_110212, including the new run's own scene
  renders under assets/agent_run_20260729/, its at-a-glance table, and
  its captured 2-blue relay plan. "Tool surface" is renamed to "tool
  API" throughout.
- The sysID deck is updated from "branch domino-sysid, fixes planned" to
  the state on master after PRs #96-#103: green badges instead of red, a
  new slide on why declaring a physical param is a decision and why
  teacher-forced residuals cannot inform it, and the block parameter
  family.

The task-generation deck is generated output and moves unchanged.

* docs/slides: weekly sync deck for 2026-07-30 and its mosaic assets

A deck covering the 10/10 al_margin result on domino_high_friction_turn,
the five pipeline stages with the changes that landed this week marked
inline, and the measured evidence for the robust fit objective, plus the
heavy-dominoes, fan and glue work coming next.

Assets are 3x3 episode mosaics rather than the individual episode
videos: before-learning and after-learning grids for al_margin, and a
2x2 success/failure grid for heavy dominoes. make_al_margin_mosaics.py
regenerates them from a run's per-episode mp4s with ffmpeg. It emits mp4
by default because a full-resolution GIF of a mosaic this size measures
193 MB, against roughly 2 MB for the mp4; --gif produces a downscaled
GIF for contexts that cannot play video.
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