Issues and resolutions
Every open decision and known defect, and how each earlier one was resolved. Click an entry to expand it.
Last checked: 2026-11-08, against phase1/planar at 8c5f0ad in Symplectomorphism/c-gnn. The full records are in the discussion compendium (reports/discussion-compendium/); commits are on that branch.
Open
Every realizer fails the six hard break(gv) starts: with friction-matched plans the disk’s contact stays inside its cone, but the box’s corners on the floor and riser slide while the tracker’s correction doubles the planned force. Decided (Decision 16): a tracker that keeps the environment contacts inside their cones, then a quasi-dynamic method; not now. The local solver.
εpen is ten times εon, so both corners of a tilted box edge can count as touching. 475 of 4,000 demo poses jittered by about 3 mm are ambiguous (474 with a contact deeper than εon; in 136, sv loses to t, which contains it). A tied pose is never a search node or a solver success, so no wrong label is used; the stress test’s budget is 500. Since step 3b εpen = 5 mm (257 ties); 2–3 mm (44–98 ties) needs stiffer contacts, which keep penetration under 0.22 mm but need the controller retuned first. Contact states.
Angelo’s Search accepts when the requested contacts are in band, ignoring the solver’s success flag; a node stores the requested formation, not the one reached (28 of 30 nodes in a 12-round run); a break is accepted without checking that the contact left (6 of 7); there is no goal test, goal input or budget. Fix: wiring Λ’s shared success test into the search, then the goal-directed search of step 4. Walkthrough 8.
The gate agrees with a float64 reference on 1,000 of 1,000 random cases and on every near-boundary case 0.33 mm or more from the boundary. Its only misses are acceptances with margin 0 (at most 2·10−7) at the boundary pose itself. A positive acceptance threshold would refuse them (choice 4 above). The hold gate.
Float32 rounding builds up in linrax’s dense tableau over the long pivot sequences of 3D cones: about 1% of supported 3D cases end “uncertain” (rejected, never certified wrongly). On the GPU the 3D test sees 4 uncertain of 64 against its budget of 2, so test_3d_cones_finish_and_never_mislead fails there; it passes on the CPU. Fix: rebuild the tableau from the final basis and continue, with the 3D build. Background.
- Label flicker near θe along a trajectory (frames 623–625 of the demo). Fix: hysteresis with thresholds from a noise model, in the execution monitor.
- GPU runs are not repeatable between processes by default (one MPPI call ended flush once and tipped 1.4° once). Workaround:
XLA_FLAGS=--xla_gpu_deterministic_ops=true, about 13% slower. - The 3D catalog (
step.yaml) keeps the old defects (closed faces over-count, ±y faces, a Python loop over templates). Frozen with 3D. ContactGraphdrops edges (self.edgesnever set). Nothing uses the graph; superseded by attention over templates.tests/test_local_solver.pyreadsgradient_warm_start, which MPPI no longer has. For a fix-up PR to Angelo.- The paper’s pending results: step-3 choices and results, the scene families and the ranker are marked pending in the rewritten paper.
Resolved
84b7ab2)
The friction cap stays unset. The sweep’s resolution was measured on 1,797 candidates against a dense reference: at the simulator’s friction every grid agrees; at μ = 0.35 the old 5-point grid refuses one realizable break, 9 points and the new adaptive grid (64 poses on the free directions, two refinement levels, 118 poses) none. The adaptive grid is the step scene’s default; it costs what the old grid cost. The sweep’s resolution.
f54dc73)
The transition sweep’s grid followed the SVD’s null vectors, which are any rotation of a basis when two or more directions are free; the CPU and the GPU returned different rotations. Harmless at μ = 0.99, decisive at 0.35. Now a canonical basis of the subspace (pivoted Gram–Schmidt on its projector); the backends agree, and a test rotates the basis and checks nothing changes.
Aykut accepted the recommendations. Λqs open loop stays the default realizer; receding-horizon re-planning stays available with RH20 and χtr kept fixed as its defaults (half the tracking deviation, no gain in success); F1 and F2 are removed from the code (± 1 run, opposite signs on GPU and CPU). Next: the friction match (step 3d, below and on the local solver); then a tracker that can use high friction; quasi-dynamic methods last.
At the landed pose with the box’s top face almost parallel to the riser, the box’s lower corner sits 2·10⁻⁸ m inside the platform (float32 noise). That penetration belongs to the platform, the nearer segment, but on the GPU a one-ulp piece of the face, λ ∈ [0.99999994, 1], stayed attributed to the riser: it has no representable midpoint, so the strict “another segment is closer” test was evaluated at its end and failed, and the sliver carried the riser’s h = −96 mm as its depth. Fix: attributed pieces narrower than 10⁻⁶ (in λ) are dropped; CPU and GPU now agree (+96.24 mm), and the pose is a test (test_gap_sign_near_parallel_resting_box).
9ea09bb)
Validity now comes from the geometry: a certified table of the 357 formations some admissible pose realizes (the stand-in admitted 37,203), from 552 excluded pairs and 4 triples. Compound edges follow the local rule by default (Decision 14): no intermediate is realized within ρ = 25 mm of the transition pose. On the step scene that gives 96 compound instances (the global rule 136; the dropped 40 join formations realized 85–184 mm apart, which no single motion connects); the pivot stays compound. The global rule stays selectable. Edits.
The theory note proves that the search makes at most min ν/π calls of Λ (Orseau and Lelis’s policy-guided search bound). With duplicate node keys the bound needs two things: nodes of one key behave alike (one canonical state per key, the first one made) and the safe rule: drop a new child only if a node already made with its key has π at least as large; otherwise make it as a new node. First-come pruning breaks the bound (17,664 calls against 10,020 in the theory note’s example); so does re-parenting the existing node to the higher-π arrival (26 against 22.2 in a small example). Implemented in cmg/search (SearchState, PolicyRanker) with both examples as tests.
7e2234f)
The first re-planning version gave every new plan the time the open-loop plan had left, re-swept χtr, and re-planned phase I while the box was still recognized as σ, which it is until the change completes. The change was pushed to the last steps and made fast: make(s) and transfer(sv → t) failed by overshoot, and re-planning was worse than open loop. Fixed: each phase keeps its scheduled end, χtr is kept, and once the plan has passed χtr the previous plan continues until σ′ is recognized. The local solver.
767ab7a, 6dec516)
Aykut chose a mode-scheduled quasi-static realizer with tracking in MuJoCo (Λqs) over MPPI, which stays as the baseline with cost E (the paper’s terms over εon); εpen = 5 mm; χtr the nearest swept pose with margin ≥ γtr = 0.1; acceptance at margin ≥ γmin = 10⁻⁵; the sweep’s defaults. Λqs realizes every chain edit from its canonical start, including the pivot and break(gv) at 45°, which no MPPI cost realized (checkpoint 2: 23 of 25, against 13 of 25 for MPPI-E). The local solver.
9261ece)
Our construction had drifted from Xiao and Ji’s contact formations in ways that were conveniences, not needs. A literature search (13 papers read in full) found nothing simpler that still meets our needs: stances, body-pair strata and scene graphs cannot see a box tip from its edge onto its corner; configuration-space cells and triangulated subcontacts split one motion into several edges. We now follow Xiao and Ji exactly and add only tolerances, several bodies with a manipulator, and a no-contact state per pair.
Before (84827c0) |
After | |
|---|---|---|
| Environment | three convex bodies; the step corner owned by the riser | one polygon E; convex vertices from the geometry |
| Contacts per pair | at most one per body pair | at most one per pair of convex elements, none alongside one it contains |
| Transfers | same or adjacent features: 92 pairs, only 48 Xiao–Ji neighbors | one-sided containment: 56 pairs |
| Compound edges | two transfers in different body pairs | all relaxing or all constraining, every proper subset invalid (same 128 on the step) |
| Valid formations | 11,959 of 12,393 | 37,203 of 59,049 |
The landed box became {t} (next entry). Contact states, edits.
Once +xB lies on the platform, vS bounds the touching platform edge, so sv is not principal. Landing is transfer(sv → t), sliding off the corner is transport, and the chain is one edit shorter. Support is unchanged. Edits.
9261ece)
- 44 of the 92 old transfers were not Xiao–Ji neighbors: 20 went round a corner in one step, 24 changed both features at once. Transfers are now one-sided containment only (56).
- The disk goes round a box corner in two transfers, through its contact with the vertex.
- No ownership rule is needed: the environment is one object whose convex vertices come from its geometry (this replaces the Nov 5 rule that the riser owns the step corner).
5b25385, 9261ece)
The pivot needs g → gv and s → sv together, since both one-at-a-time intermediates are excluded. Settled first as a closed form (two transfers in different body pairs with crossed exclusions), it was replaced the same day by Xiao and Ji’s neighbor relation with the minimality rule, which gives the same edges on the step and needs no special case.
a3789d7, 9261ece)
H first tested the contacts that persist through an edit, with the old contact’s normal for a transfer (on the demo the two normals differ by at most 1.77° where it matters; using the new one changes none of 1,155 decisions). Under Xiao and Ji’s containment both follow from one rule: test the edit’s less-constrained end, as multi-contact planners do (Hauser et al. 2005, 2008; Escande et al. 2013); the normal comes from the feature that stays.
228950f)
The margin is set inclusion of a disturbance polytope, by default the ℓ₁ ball (0.2 = μ on the flat box). The disk’s force is bounded by its motors (±1 N, read from the MuJoCo model), which also made margins with the disk finite; before, its unbounded push made the support cone fill all of 3D. The LP backbone is the gate; the exact cone test is its cross-check. The hold gate.
41b5721, 26c96d0)
break(gv) was refused at 30° although tilting further and then lifting is possible. H now searches near χ₀ over the more-constrained end’s contacts for a transition pose where the less-constrained end holds, and passes it to the solver. Pose distances use the paper’s weight a/2 on angles. The hold gate.
41b5721)
MPPI graded itself with its own gap-band test. Every realizer now uses the paper’s success test, extended to transfers and compound edges. The local solver.
e2f705c to 63de648)
The old draft (archived, tag paper-v1-2026-11-07) described corner ownership, one contact per body pair, the old chain and stale numbers for h = a/4. The paper was rewritten from scratch and revised after four independent reviews; the proofs that the gate never refuses a realizable edit and that the recognizer is correct are in its appendices.
e29d96f, b354251)
- linrax had no iteration cap and could cycle on degenerate 3D LPs, hanging a whole
vmapbatch: its pivot loop is replaced (300 pivots per phase, Bland’s leaving rule, artificial rows marked correctly). - It returned infeasible points as optimal in float32: fixed by Householder-spread rows, primal and dual from the final basis, pivot tolerance 10−4, exit cap 10, and every answer checked.
- linrax has no license file; accepted (Aykut, Nov 6), pinned to 0.3.0.
d810e32, 1986c73)
Candhad no validity check (a second contact per pair, excluded formations, transport always legal): edits now pass validity, transfer and compound rules; transport only when supported.- No transfer generator: g → gv needed a break and a make through a formation where the box does not touch the floor.
- MPAX’s “not supported” was uncertified (192–194 of 416 supported near-boundary cases rejected): replaced by the exact cone test with witness and certificate, later by the LP gate.
- The cone test’s margin was positive on a single-ray cone and failed below three generators: fixed, and the margin proved to be the signed distance.
- JAX’s reduced-precision (TF32) GPU products broke the supporting-plane checks: dot products are formed elementwise.
- Tied poses are treated as ambiguous: never a node or a solver success.
5f5a2e8)
A length rule (“flat if 7.8 mm lie within 1 mm”) called a box tipped 7.35° flat, and as the box tipped over the step corner it switched the floor contact at 7.35° but the riser contact at 8.44°, labeling the impossible {gv, s} in between. An angle shared by both contacts fixes this. Two principled options were compared: the engines’ point rule (both ends of the overlap within 1 mm), which switches floor and riser at 0.46° and 1.83° and realizes {gv, s} on 13 demo frames; and one derived angle, θe = arcsin(εon/h) ≈ 1.83°, which switches both together and is exactly the point rule on the riser. The derived angle was adopted (a 2° value tuned to the demo, 62782b8, was withdrawn). Hysteresis for trajectory labels was deferred to the execution monitor.
ca4123b, b2665cb, 583810b)
- One physical contact lit 3 to 6 template bits (closed features share boundaries): replaced by the principal-contact recognizer.
- The hold test used one tie-broken point per contact, blended by smoothing, and ±y faces on a planar body: replaced by the contact sets’ endpoints in a planar catalog.
- Exclusions were integer indices, then dropped; there were no vertex features for gv, sv: named templates and vertices in
step2d.yaml. - Overlaps were charged to the wrong surface (−31 mm into the platform): each overlap is charged to the nearest environment segment or endpoint (approved Nov 5).
- A Python loop over templates: one
vmapper kernel (the 3D catalog still loops). - NaN gradients in the planar kernels: safe norms and roots.
- The tie-break was kept as the recognizer’s fallback, with every use counted.
pyproject.tomlwas invalid TOML: fixed upstream.