Skip to content

fix(g4): close fixture-top pose_tolerance via iterated fixed-point converged-rest; flag bimodal cabinet#39

Merged
KE7 merged 1 commit into
mainfrom
fix/g4-fixture-top-fixedpoint
Jul 1, 2026
Merged

fix(g4): close fixture-top pose_tolerance via iterated fixed-point converged-rest; flag bimodal cabinet#39
KE7 merged 1 commit into
mainfrom
fix/g4-fixture-top-fixedpoint

Conversation

@KE7

@KE7 KE7 commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Summary

Closes the g4 NON-CONVERGENT FIXTURE-TOP residual (RCA g4_remaining_arenas.md §3b). Task objects resting on a fixture exterior top never reach a stable rest inside the 50-step validation settle, so no single analytic clearance is a fixed point and they fail g4 pose_tolerance on z. This PR implements a principled iterated fixed-point (converged-rest) measurement and emits the converged z (Option-2: renderer z == settled z; gate stays 5 mm). Zero band-aid: no tolerance widening, no masking, no exclusion.

Non-convergence diagnosis

The analytic on-fixture spawn z (spawn_clearance rule 2) uses the fixture AABB top (highest geom), which sits far above the real rest face:

So the object is injected too high and falls ~48–64 mm per 50-step settle — a constant per-injection drop (no fixed point at 50 steps), exactly matching the predecessor's observation. This is diagnosis (a) (over-estimated rest face), not (b) (rolls/oscillates).

Fixed-point method

scripts/measure_g4_fixture_fixedpoint.py iterates the settle map f(z) = settle50(inject at z) from the analytic z until |Δz| < 1 mm — the object has landed on its true rest face and the 50-step map is now a fixed point. Roll-off guard: if xy drifts >30 mm (slid off) the case is flagged, not recorded. The harness auto-discovers every :init fixture-supported object from the realized scene's support_surface_class (robust to init-vs-goal grep confusion). A corpus :init-block scan confirms these are the only 3 task-object-on-fixture-exterior pairs in the whole BDDL set.

Both recorded objects fall straight down (xy drift <0.15 mm — not a roll-off) to a deterministic converged rest.

Per-case before/after (position subset, real LIBERO env, 3 seeds)

pair converged z* clearance BEFORE AFTER dz before→after
white_bowl|microwave 0.8994 0.0794 0/9 fail 9/9 pass 48.8 → 0.0 mm
akita_black_bowl|flat_stove 0.8984 0.0784 0/6 fail 6/6 pass 64.5 → 1.2 mm

Determinism: spread ≤ 1.5 mm across 6 seeds × all :init tasks/instances.

Flagged residual (not forced)

akita_black_bowl|wooden_cabinet — the cabinet top has a non-uniform collision surface: a solid edge region rests at z*≈1.126 (clear 0.306), visual-gap regions fall to ≈0.898 (clear 0.078), and the top_drawer placement is genuinely metastable across seeds (0.898↔1.119, spread 221 mm). No single scalar is a fixed point across the sampled xy region; forcing the dominant 0.078 would inject the solid-region bowl inside the cabinet body (contact-explosion regression risk). Per the no-force guard it is FLAGGED — it needs an xy-aware rest surface (out of scope for scalar Option-2).

Additive-merge evidence

  • spawn_clearances_variants.json: +2 rows, 0 changed, 0 removed (proven by semantic diff) + a provenance _meta block.
  • Untouched: fixture_geometry.json, stack_offsets.json, spawn_clearances.json, asset_metadata.py, consistency.py (5 mm gate) — all byte-identical.
  • The two new rows resolve via the existing rule 1 of spawn_clearance, superseding the rule-2 analytic for these two pairs only; every other pair (renderer + simulator) is byte-identical.

No-regression control

Untouched arenas (STUDY + table-center, all classes, position+object, 3 seeds): 30/30 = 100% pass, max dz 1.98 mm. white_bowl|table byte-identical (proves the microwave row is isolated). All wooden_cabinet cases fail identically before/after (flagged, unchanged).

Tests / CI

  • CI Tier-1 (test_scenic, test_perturbation_policy, test_sweep_g3_budget, test_invariants_*): 182 passed
  • Validation smoke (G0–G3 on 2-task × 3-subset sweep): 6/6 pass
  • test_spawn_z_consistency: 102 passed (updated test_fixture_task_residual → asserts measured pairs present at fixed-point value + cabinet stays flagged)
  • ruff + ruff-format + black clean on src/ + tests/

🤖 Generated with Claude Code

…nverged-rest (Option-2); flag genuinely-bimodal cabinet

RCA g4_remaining_arenas.md §3b: task objects resting on a fixture EXTERIOR
top never reach a stable rest in the 50-step validation settle. The analytic
on-fixture spawn z (rule 2 of spawn_clearance) uses the fixture AABB top
(highest geom), which sits far above the real rest face (flat_stove
cook_region grate is visual-only — #32/#34; microwave/cabinet tops have a
raised lip/rounded shell). The object is injected too high and falls
~48-64 mm per 50-step settle, so no single analytic clearance is a fixed
point → pose_tolerance fails on z.

DIAGNOSIS (scripts/measure_g4_fixture_fixedpoint.py): iterate the settle map
f(z)=settle50(inject at z) to its fixed point z* (|Δz|<1mm). Both objects
fall STRAIGHT DOWN (xy drift <0.15 mm — not a roll-off) to a deterministic
converged rest — diagnosis (a), the record case.

FIXED (additive VARIANT_CLEARANCES rows; rule 1 supersedes rule-2 analytic;
gate stays 5 mm; renderer+simulator resolve the same z* → scenic_z==settled z):
  * white_bowl|microwave       = 0.0794 (z*=0.8994, spread 0.0 mm, n=18/3 tasks)
  * akita_black_bowl|flat_stove = 0.0784 (z*=0.8984, spread 1.5 mm, n=18/3 tasks)
Per-case pose_tolerance (position subset, 3 seeds): white_bowl|microwave
0/9→9/9 (dz 48.8→0.0 mm); akita|flat_stove 0/6→6/6 (dz 64.5→1.2 mm).

FLAGGED, not forced — akita_black_bowl|wooden_cabinet: the cabinet top has a
NON-UNIFORM collision surface — a solid edge region rests at z*≈1.126, visual-gap
regions fall to ≈0.898, and the top_drawer placement is metastable across seeds
(0.898↔1.119, spread 221 mm). No single scalar is a fixed point; forcing the
dominant 0.078 would inject the solid-region bowl inside the cabinet body
(contact-explosion regression risk). Needs an xy-aware rest surface (out of
scope for scalar Option-2).

Corpus :init-block scan confirms these are the ONLY 3 task-object-on-fixture-
exterior pairs (moka_pot/cabinet-bowl/wine/book fixture relations are all :goal;
object starts table-resting).

Guards/no-regression: purely additive (2 rows, 0 changed/removed — proven);
fixture_geometry.json, stack_offsets.json, spawn_clearances.json, asset_metadata.py,
consistency.py (5 mm gate) all byte-identical. Untouched-arena CONTROL (study +
table-center, all classes) 30/30=100%; white_bowl|table byte-identical. Updated
test_fixture_task_residual → asserts measured pairs present at fixed-point value +
cabinet stays flagged. CI Tier-1 182 passed; validation smoke G0-G3 6/6; ruff+black
clean on src/+tests/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@KE7
KE7 merged commit dc5451f into main Jul 1, 2026
3 checks passed
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