Duplication & Reuse Watch
Part of the OpenRAL public-symbol inventory. Hand-curated. Last cleaned 2026-09-22 (openral PR #296): every resolved item collapsed to one line, stale references removed, this pass's findings added. Cite code here as
module.py::symbol, never with a bare(LNN)marker — this file has no source-file sections for the refresher to resolve them against.
This is the user-facing deliverable for the goal of "ensure there is no duplication or redundancy of methods". The mechanical half of that goal now lives in tools/refresh_methods_linenos.py --check --coverage (run by just lint): every public symbol must have an inventory entry, so a new helper is visible next to its siblings before it is merged. This file carries what a checker cannot: the mirrors that must move in lockstep, the look-alikes that must stay apart, and the copies waiting for a third caller.
Deliberate mirrors — update in lockstep
Each of these is the same logic on two sides of a boundary that must not be crossed by an import (real-time C++ vs Python, an isolated sidecar venv vs the workspace, a producer vs the kernel that checks it). The drift consequence is named per item; read it before touching either side.
-
BEHAVIOR R1Pro wire constants — resolved in-process, mirrored cross-venv. Canonical in
python/sim/src/openral_sim/_behavior_wire.py(STATE_KEY,STATE_DIM,ACTION_DIM,CAMERA_SENSORS,CAMERA_RGB_KEYS,explicit_port), imported by the scene backend,behavior_groot, andopenral_cli.behavior.tools/behavior_*_sidecar.pyruns in isolated venvs that cannot importopenral_sim, so its copies are a deliberate wire-contract mirror — update both in lockstep. -
Support-contact patch predicate — deliberate cross-package mirror, update in lockstep.
support_contact_exempts(cpp/openral_safety_kernel/src/collision.cpp) andsupport_patch_withholds(packages/openral_octomap_bridge/src/payload_clearing.cpp) evaluate the same attested support plane. Kept apart because consolidating would make the Layer-2 perception bridge depend on the Layer-6 safety kernel; the bridge withholds a zero-slack subset of what the kernel exempts.PayloadClearing.WithholdingIsTheKernelsExemptionPredicateAtZeroSlackpins the mirror. -
Support-witness acceptance caps — deliberate C++/Python mirror, update in lockstep. Kernel ROS params in
cpp/openral_safety_kernel/src/lifecycle_kernel.cpp(support_witness_max_patch_radius_m=0.5,support_witness_max_penetration_m=0.01) vs. the same two numbers written independently inpython/hal/src/openral_hal/_sim_attachment_evidence.py. Kept apart because the kernel must not trust a producer-supplied bound. Drift is asymmetric: loosening the producer fails safe (noisy); tightening it silently starves the witness. -
Grasp-declaration bounds — deliberate C++/Pydantic mirror, update in lockstep.
kMaxGraspRegionHalfExtentM/kMaxGraspRegionVolumeM3/kMaxGraspDeclarationTimeoutS(cpp/openral_safety_kernel/include/openral_safety_kernel/collision.hpp) vs.GraspDeclaration.MAX_HALF_EXTENT_M/MAX_VOLUME_M3/MAX_TIMEOUT_S(python/core/src/openral_core/schemas.py). All Safety-WG placeholders; each side refuses on its own, so drift either way means a declaration one side accepts and the other refuses (no exemption), never a wider one. - Manipulation-workspace bounds — deliberate C++/Pydantic mirror, update in lockstep (#349).
kMaxWorkspaceRegionHalfExtentM/kMaxWorkspaceRegionVolumeM3(collision.hpp) vs.GraspDeclaration.MAX_WORKSPACE_HALF_EXTENT_M/MAX_WORKSPACE_VOLUME_M3(schemas.py);kMaxWorkspaceContactLeadM(collision.hpp) vs. thele=0.10bound onDeployRuntime.workspace_contact_max_lead_mand the 0.10 check indeploy_e2e.launch.py's_workspace_contact_kernel_params. Safety-WG placeholders; each side refuses on its own, so drift means no workspace contact, never a wider one. -
Place-region bounds — deliberate C++/Pydantic mirror, update in lockstep.
kMaxPlaceRegionHalfExtentM/kMaxPlaceRegionVolumeM3(cpp/openral_safety_kernel/include/openral_safety_kernel/collision.hpp) vs.PlaceRegion.MAX_HALF_EXTENT_M/MAX_VOLUME_M3(python/core/src/openral_core/schemas.py). Both fail closed independently — the kernel can't assume the Pydantic validator ran, and the schema must refuse locally too. Raising the Python ceiling alone silently zeroes the kernel's allowance; grepPlaceRegionStatus::kOversizefor the refusal. -
Per-rig perception caps — deliberate C++/Pydantic mirror, update in lockstep.
DeployRuntime._check_perception_caps+ the fields' JSON Schemamaximum/ defaults (python/core/src/openral_core/schemas.py) vs.kMaxWorldVoxelDeadlineMs/kMaxWorldVoxelDataAgeBudgetMs/kDefaultWorldVoxelDataAgeBudgetMs(cpp/openral_safety_kernel/include/openral_safety_kernel/lifecycle_kernel.hpp),kMaxOctreeAgeS(packages/openral_octomap_bridge/include/openral_octomap_bridge/octree_freshness.hpp) andkMaxSelfFilterPaddingM(.../self_filter.hpp). Kept apart because a node launched withoutopenral deploynever sees the schema, and the Layer-2 bridge must not depend on the Layer-5 kernel. Each side fails closed on its own; drift means a node looser than the schema (a hole) or tighter (a validated scene refused at configure).tests/unit/test_perception_caps_mirror.pypins every pair. -
FailureTrigger/SafetyStatusKIND_*numbers — mirrored on every leg, all pinned. Declared inpackages/msgs/msg/FailureTrigger.msg, redeclared inSafetyStatus.msg(IDL has no cross-message constant reuse), and mirrored as plain ints inopenral_observability/failure_bus.pyfor ROS-free callers (partial copies also live in the kernel'sViolationKindenum andreasoner_node.py).failure_bus.pyonce drifted silently (missingKIND_COLLISION);tests/unit/test_failure_bus_idl_mirror.pynow pins it bidirectionally against the generated IDL. -
Kernel narrow-phase predicates — deliberate C++/Python mirror, update in lockstep.
kernel_predicates.py(box_box_distance,box_capsule_distance,capsule_distance,shape_distance) is a line-by-line Python port of the self-collision narrow phase incpp/openral_safety_kernel/src/collision.cpp. Kept in sync because the offline ACM sweep must reason about the same predicate the kernel runs — they previously diverged on box modelling (inscribed sphere vs. true OBB). Scope ischeck_self_collision's link-vs-link routing only, not the voxel narrow phase; verified equal to 2.2e-16 againstmjcf_lowering._seg_seg_distance. -
_seg_seg_distance— two Python copies, one batched.kernel_predicates._seg_seg_distance(vectorised over a batch) andmjcf_lowering._seg_seg_distance(scalar) are the same clamped-parametric segment solve (Ericson §5.1.9), split because the MJCF path evaluates one pose while the ACM certificate evaluates hundreds of thousands at once. Pinned equal to 2.2e-16 intest_urdf_lowering_always_colliding. Collapse all three if a third copy appears. -
Convex distance — three implementations, three different questions. Collapsing them produces a green that confirms itself.
collision.cpp/kernel_predicatesanswer "what will the kernel do" (manifest OBBs/capsules, a lower bound by construction);openral_hal.convex_distanceanswers "where is the geometry actually" (MuJoCo's exact hulls, the evidence-path ground truth);test_so101_base_box_collision._box_boxis a third box-SAT copy flagged for collapse ontokernel_predicates, not ontoconvex_distance. Routing the kernel check through the ground-truth module would compare the kernel to its own arithmetic. -
Collision-model FK — deliberate C++/C++ mirror across a layer boundary, pinned by test.
openral_octomap_bridge::self_forward_kinematics+build_self_model+self_transform_from_xyz_rpy(packages/openral_octomap_bridge/src/self_filter.cpp, Layer 2) mirror the safety kernel'sforward_kinematics/load_collision_model/transform_from_xyz_rpy(Layer 5) over the samecollision_*parameters, so the real-camera self-filter removes returns exactly where the kernel places the robot. Linking the kernel at runtime would be a non-adjacent layer dependency (ADR-0110).test_self_filter.cpplinks the kernel library (test-only) and requires every link frame to agree to 1e-12; a change to either side's joint convention, RPY order or primitive routing must land in both. Same for the tight-geometry narrow phase:self_hull_distance+ the hull checks inbuild_self_modelmirror the kernel'sdop_cell_lower_bound/hull_cell_distance/validate_tight_hullfor a point, pinned to 1e-9 on panda_mobile's hulls and to the kernel's constants by the same test. -
26-DOP axis table + tight-geometry bounds — deliberate C++/Python mirror, update in lockstep.
kDopAxis/kMaxTightHullVertices/kTightContainmentEpsilonM(cpp/openral_safety_kernel/include/openral_safety_kernel/collision.hpp) vs.DOP_AXES/MAX_TIGHT_HULL_VERTICES/TIGHT_CONTAINMENT_EPSILON_M(python/core/src/openral_core/schemas.py). The axis table is positional — reordering it or flipping a sign on one side still validates but silently breaks the containment proof, over-reporting clearance.generate_tight_geometry checkandtests/unit/test_collision_tight_geometry.pyre-derive and re-prove it against the mesh. -
The world-voxel grid derivation, in three places — left duplicated, pinned by test.
deploy_e2e.launch.py::_world_voxel_max_cellsandtools/voxel_transport_probe.py::per_axisboth derive(floor(2R/res + 1/2) + 1)^3from a coverage radius (the launch file's is measured per robot by_coverage_ball). Neither can import the other — the launch file isn't an importable package, and the probe must run standalone.test_deploy_e2e_voxel_resolution.py::test_the_transport_probe_sizes_the_grid_the_kernel_reservespins them together. -
The quantisation budget, twice — left duplicated, pinned by test.
tools/validation_matrix.py::quantization_budget_m(canonical half body-diagonal) andtools/stop_ee_speed.py::QUANTISATION_GAIN_M(their 25/15 mm difference) are two standalone scripts with no shared module. The 8.66 mm figure is what the programme note §5 indocs/reference/collision-validation-evidence.mdweighs the resolution trade against.test_the_quantisation_gain_matches_the_matrix_budget_it_is_derived_frompins it. -
OccupancyVoxelslattice decode — deliberate cross-layer mirror, update in lockstep.packages/openral_foxglove_bringup/openral_foxglove_bringup/bucket2_markers.py::occupied_voxel_centers(viewer) andpython/hal/src/openral_hal/_grasp_target.py::VoxelLattice.occupied_centers(pre-grasp target producer). The HAL cannot import a ROS viewer package;tests/unit/test_grasp_target.py::test_lattice_centres_agree_with_bucket2_markers_on_a_rotated_latticeloads the viewer's by path and pins both to the same centres on a rotated lattice. Drift draws or measures every cell in the wrong place whenever the map is not base-aligned. attach_link_tf_framesparsing — deliberate C++/Python mirror, update in lockstep.packages/openral_octomap_bridge/include/openral_octomap_bridge/payload_clearing.hpp::parse_attach_link_tf_frames(payload clearing) andpackages/openral_foxglove_bringup/openral_foxglove_bringup/bucket2_markers.py::parse_attach_link_tf_frames(attachment markers). Same"link=frame"strings from one scene block, same rules (empty skipped; malformed or conflicting refused, whole map ignored). A read-only Python viewer cannot link the C++ bridge library; drift draws a held payload on a frame the bridge does not clear it on.
Deliberately not consolidated
Repeated bodies that consolidation would make worse: different contracts, illegal imports, or a green that would confirm itself.
-
Three parallel registries with the same lookup-by-string pattern:
openral_rskill.loader.rSkill(file-backed JSON registry),openral_sensors.catalog.SensorCatalog(in-memory dict),openral_sim.registry._Registry(decorator-driven dict). Different lifecycle and value type, so deep consolidation isn't warranted — but the method names (list_ids()/names()/list_installed()) should align; a future ADR could standardise the verb. -
SmolVLA skill-side
SmolVLAAdaptervs eval-side_SmolVLAAdapter— not a duplication target. Incompatible contracts: the skill takesWorldState/emitsActioninside the ROS2 S1 runtime; the eval adapter takes a dictObservation/emits a flat numpy array in the sim driver. Collapsing would force Pydantic wrapping into the sim hot loop or widenSkill.step()to accept dicts. Residual overlap (~30 LOC/side) is below the abstraction-cost threshold. -
_build_libero_scene/_build_metaworld_scene/_build_mock_sceneinpython/sim/src/openral_sim/{policies,backends}/{libero,metaworld,mock}.pyshare the same lazy-import-instantiate-return shape. Already correctly DRY through theSCENES.register(...)decorator pattern; do not consolidate further. -
UsbDevice/UsbDeviceRecord— not consolidated, deliberately different types.openral_cli.autodetect.UsbDeviceis aNamedTuple(hot in OS-probing loops);openral_detect.report.UsbDeviceRecordis a PydanticBaseModel(the JSON/YAML report boundary, CLAUDE.md §2). Same fields, same reason to stay two types. -
camera_info_from_intrinsics— not consolidated yet.openral_hal.depth_cloudandopenral_perception_ros.depth_convertcarry near-identical builders.openral_perception_ros/package.xmlnow exec-depends onpython3-openral-hal(forintrinsics_from_camera_infoinsegmenter_node), so the ROS package's copy could import the HAL's; left for the next PR that touchesdepth_convert. -
MJCF compile trio in three sim tests — four lines each.
sim/_compiled/_model_dataintest_sim_attachment_evidence.py,test_sim_estop_payload_slop.py,test_sim_estop_voxel_backing.py— four lines; sharing needs a parameter at every call site. _wait_untilin two live tests — not hoisted.test_hal_attachment_barrier_live.pyvs.test_estop_voxel_backing_live.py; a seven-line spin-wait, hoisting costs a 21-call-site refactor.isolated_rosfixtures — the domain is the difference.test_ros2_image_sensor_reader.py(domain 91) vs.tests/hil/test_openarm_ros_transport.py(domain 92); the differing domain is the point.-
Per-package ROS test clones — colcon isolation. (
captured_spans,_spin_until,*_sigint_shape.py,slam_bringuplaunch tests) — colcon builds each package standalone, so sharing needs a new shared package. The per-robot HAL lifecycle-test clones are gone: the twelveopenral_hal_<robot>packages collapsed into oneopenral_hal_node, whose tests are parametrised by robot manifest. -
load_manifest_for_spec— one copy left, on purpose. Ten adapters callpolicies/_policy_loading.load_manifest_for_spec;policies/act.pykeeps a private_load_manifest_for_spec(imported bybackends/libero.py) that differs on emptyweights_uri— the shared version returnsNone, act's raisesROSConfigError. Removing the duplicate is a behaviour change: decide the empty-URI contract first (CLAUDE.md §1.4 favours the loud version), then unify all eleven call sites. -
_connect/_rpcinlocateanything_detector.py/qwen_scene_vlm.py— not consolidated, no shared home. AST-identical ZMQ REQ-socket bodies; the same shape also appears inomdet_turbo_detector.py,sam2_segmenter.py, and the reward backends. A two-file extraction would miss the real six-way duplication — leave as-is until aZmqSidecarClientbase takes all six at once. -
_find_metric(python/observability/tests/conftest.pyfixture vs.tests/unit/test_runner_observability.pymodule function) — left alone, no shared home. Different installable-package test tiers, each with its ownconftest.py; a shared helper would need a new top-level module, which the no-new-top-level-modules rule forbids. -
close(omdet_turbo_detector.py::OmDetTurboDetectorvs.sam2_segmenter.py::Sam2Segmenter) — left alone, deliberate keep. Byte-identical six-line CUDA teardown, but the two classes share no other structure — a shared base for six lines would cost more to read than the duplication it removes. -
SensorSpec-by-name search — two ROS packages, deliberately. Private_sensor_specinpackages/world_state/…/lifecycle_node.pyvs. publicsensor_spec_by_nameinsegmenter_node.py. Two call sites in two packages; promoting it toopenral_core.schemasis worth doing when a third caller appears, not before.
Already correctly DRY (do not flag)
-
SimSensorBridge — the single source for RGB camera publishing + MuJoCo viewer under
deploy sim, viaopenral_hal.sim_sensor_bridge.SimSensorBridgeand_ManifestHALLifecycleNode.panda_mobileincluded (its lifecycle node is manifest-driven, like every arm). Its two body-set resolvers answer different questions and are not a duplication target:depth_cloud.robot_self_body_ids("what is the robot") vs.sim_sensor_bridge.kernel_checked_body_ids("what does the safety kernel check", narrower by design). Do not add per-arm camera/viewer timers — extendSimSensorBridgeinstead. -
Bounded certified-distance probing —
sim_sensor_bridge._pair_distance_lower_bound(bounding-sphere/plane prefilter) and_round_robin_candidates(fair exact-call budget) are the single source for measuring closest geom pairs without paying O(n·m). Shared by_nearest_pair_records(E-stop ground truth) and_sim_attachment_evidence._probe_support_hits(support-contact witness) — they need different output shapes from the same measurement, not different math. Both route throughopenral_hal.convex_distance.convex_geom_distance; neither may fall back tomujoco.mj_geomDistance, which misses geom pairs thatcontype/conaffinitysuppress. Do not reimplement the prefilter or the budget; extract only the exact-call loop for a third output shape. -
Bimanual real-HW fan-out —
AlohaHAL.send_action(14-DoF) andOpenArmRealHAL.send_action(16-DoF) both split one action across per-side arm+gripper controllers and publish fourjoint_trajectorymessages, on different bases (HALBasevs.RosControlHAL). A third bimanual adapter is the trigger to lift the(topic, slice, joint_names)table into a shared mixin. Any consolidation must keep OpenArm's two properties: it builds all four messages before publishing any, and namesjoint_namesper-message rather than relying on positional order. -
HAL adapters (sim) —
robots/<id>/robot.yamlis the runtime source of truth. The pure-data arms (franka_panda, ur5e, ur10e, rizon4, openarm, anvil_openarm_v2, aloha_bimanual, so100/so101) sethal.sim: null, sobuild_halderivesMujocoArmHAL.from_description(manifest)from the manifest'ssim:block — no per-robot Python class is involved, and a new MuJoCo robot needs no Python file (anassets.mjcfref +sim:block in itsrobot.yamlsuffices).FrankaPandaHAL/UR5eHAL/UR10eHAL/Rizon4MujocoHAL/OpenArmMujocoHAL/AnvilOpenArmV2MujocoHAL/AlohaMujocoHALsurvive only as thin wrappers over their in-code<ROBOT>_DESCRIPTIONmirror for direct construction in tests; no manifest names them. Only HALs with real behaviour keep a manifest entrypoint, and each takesdescription=sobuild_halbinds the loaded manifest:G1MujocoHAL(walking controller / kinematic glide),H1MujocoHAL(_per_step_updatePD torque hook, an H1-specific cerebellar substitute),PandaMobileHAL(planar-base twin; joint layout read from the manifest). The*_DESCRIPTIONconstants are mirrors pinned to the manifests bytests/unit/test_robot_manifests_match_hal_constants.py(manifest wins). -
Humanoid contract validators vs useful humanoid sims —
H1MujocoHALand G1's default joint-position path are contract validators, not balance controllers: both fall without an S0 cerebellar controller (CLAUDE.md §3 dual-system pattern) and run joint-convergence tests withgravity_enabled=False. Do not bolt Python balance heuristics onto them — that crosses the S0 layer boundary. The sanctioned G1 exceptions are ADR-0087's kinematic-glide base (pinned-upright, zero-dynamics) and ADR-0089's upstream MuJoCo Playground ONNX policy (walking_enabled=True, sim-only).H1MujocoHAL's software PD loop is a position-contract adapter for its torque actuators, not a balance controller. -
Deliberate digital-twin gaps —
SawyerandGR1intentionally ship without a MuJoCo HAL twin. Sawyer: Rethink Robotics is defunct and no real hardware will ever be plugged in, so it stays MetaWorld-only. GR1: no consumer yet — the natural second humanoid HAL once the C++ S0 cerebellum lands; today it exists only as anopenral_simrollout robot. These are documented absences, not missing work. -
Real-HW manifest derivation — every real-HW adapter derives its
*_REAL_DESCRIPTIONfrom a sim-side baseline viaopenral_hal._real_description.make_real_description(base, sdk_kind=...), so kinematics/safety envelope/capabilities never drift between sim and real-HW siblings.ur_real.pyuses it to deriveUR5e_REAL_DESCRIPTION/UR10e_REAL_DESCRIPTION. New real-HW adapters must go through this helper rather than re-typing theRobotDescriptionconstructor. -
HAL adapters (real-HW) — two shapes coexist on purpose.
UR5eRealHAL/UR10eRealHAL(via a private_URRealHALbase),FrankaPandaRealHALandSawyerRealHALall subclassRosControlHAL, pinning their vendor's controller / topic / metadata defaults and overriding only the_vendor_stop/_vendor_resethooks — a future UR variant is a one-line subclass. Franka and Sawyer used to compose aRosControlHALbehind a delegating wrapper that exposed none of theRosControlDrivablesurface, so the lifecycle node never attached the production transport to either; composition is therefore not an acceptable shape for a ros2_control adapter.AlohaHALinlines the publish machinery onHALBasebecause it fans one action across four controllers, whichRosControlHALdoesn't support (and, per issue #250, a real ALOHA exposes no ros2_control surface at all); a second multi-controller adapter triggers aMultiRosControlHAL. -
Downstream e-stop seams (issue #295) — one contract, two implementations each, deliberately.
ControllerStopSeamis implemented by the productionRosControlTransport(realcontroller_managerservices) and bySimTransport(an in-memory controller table with the same drop-when-inactive semantics), so the unit lane and the live graph run the identicalRosControlHAL.estop/reset_estopcode;InterbotixStopSeamis implemented byInterbotixXSTransportandSimTorqueSeamthe same way. Do not add a per-robot stop path: a vendor's extra step goes in that adapter's_vendor_stop/_vendor_resethook and is declared viavendor_stop_services()/vendor_stop_topics(), never as a new transport. The ros2_control stack helpers (tests/integration/_ros2_control_stack.py) were hoisted out oftests/sim/test_openarm_hal_ros2_control.pywhen the e-stop live test needed the same bring-up; both files import them. -
HIL transport bridges (real-HW HALs) —
RosControlHILTransport(tests/hil/_ros_control_transport.py) is the source of truth for trajectory wiring;AlohaHILTransport(tests/hil/_aloha_ros_transport.py) reuses its_make_trajectory_publisherhelper rather than duplicatingJointTrajectory+QoS setup. Both share the joint-state caching shape (_latestdict,state()projection,wait_for_first_state). A third HIL bridge is the trigger to extract the subscriber half into a_JointStateCachemixin. -
Kernel-twin sim tests — the four
tests/sim/safety/test_kernel_with_<robot>_*.pyfiles (so100_digital_twin,openarm_twin,rizon4_twin,h1_humanoid_twin) all route throughtests/sim/safety/_kernel_subprocess.py::{start_kernel, activate_kernel_node, build_kernel_envelope, terminate_kernel}and only declare their own joint names + action/state vectors. A fifth robot's kernel-twin test should call the same four helpers, not re-roll the lifecycle ceremony. -
rSkillBase subclasses — every
rSkillBasesubclass (GpuPassthroughSkill, theROSActionRskillfamily) overrides the same five_*_implhooks; the duplicated names are theSkillABC contract, not redundancy.GpuPassthroughSkill's_step_implis the reference for a torch.cuda-based skill that must be explicit about device placement. -
Runtime backends —
NullRuntime,PyTorchRuntime,ONNXRuntime(plusTensorRTRuntimein the privateopenral-pro-trtpackage) all implement theRuntimeProtocol surface (load/infer/quantize/warmup/unload). Same situation asSkill. -
backends/so100_robosuite/—_So100Liftextendsrobosuite'sLiftenv rather than reimplementing arena/reward/observable scaffolding, and the controller is the shippedparts/osc_position.jsonwith three knobs overridden (output_max,kp,input_ref_frame), not a custom controller class. New robosuite-integrated robots should follow the same pattern: register the model in robosuite's factories, use a stock manipulation env subclass, and tune a stock part controller rather than writing a custom IK stack.
Watch list (not yet a problem, but worth tracking)
-
Pinhole back-projection of a
32FC1depth raster now exists twice:openral_hal.depth_cloud.points_from_depth_grid(raster →(N, 3)cloud) andopenral_slam_bringup.depth_height_filter_node.filter_depth_by_global_height(raster → filtered raster). Same(u-cx)/fxcore, different outputs and packages. A third copy is the trigger to hoist a typeddeproject_depth(...)intoopenral_core.geometry. -
_validate_action()appears in both_mujoco_arm.py::MujocoArmHALandros_control.py::RosControlHAL. They validate different invariants today (MuJoCo:joint_targetsrank; ros2_control: control mode). A third HAL growing its own_validate_actionis the trigger to lift the common part into a free function inopenral_hal.protocol. -
_require_connected()appears in_mujoco_arm.py::MujocoArmHAL,so100_follower.py::SO100FollowerHAL,ros_control.py::RosControlHAL, andaloha.py::AlohaHAL. Four is over the threshold — a fifth copy is the trigger to hoist this into a base mixin (openral_hal._lifecycle.RequireConnectedMixin).FrankaPandaRealHAL/SawyerRealHAL/_URRealHAL/OpenArmRealHALinheritRosControlHAL's rather than duplicating it. -
LeRobot SO-ARM unit + cadence conversions — consolidated into
openral_sim/backends/_so_arm_units.py(steps_per_control_period,lerobot_action_to_radians,radians_to_lerobot_state). Its one importer isbackends/so101_box/env.py(theso101_boxdeploy scene andso101_tube_insertionsim scene). A new raw-MuJoCo scene accepting LeRobot-convention actions must route through this module. (tabletop_pushkeeps its own_joint_scalesaffine on purpose — it's robot-agnostic and can't assume the SO-ARM gripper convention.) -
Rotation/quaternion math scattered across packages — the yaw family is now consolidated into
openral_core.geometry(yaw_to_quat_xyzw,yaw_to_quat_wxyz,quat_xyzw_to_yaw); five former copies route through them. Use these before adding another yaw↔quat helper. The remaining full-3-DOF conversions stay separate deliberately: rldx.py::_quat_wxyz_to_mat— SAPIEN wxyz with its own norm-epsilon, pinned bit-identical to upstream; a calibration surface, not a duplicate.{mjcf,urdf}_lowering.pyrpy→matrix/euler — safety-kernel lowering; any change needs safety-WG review (CLAUDE.md §3).-
world_cloud_bridge.py::_quat_to_matrixand{bucket2_markers,depth_height_filter_node}.py::_rpy_to_*— single-caller, package-specific types; worth a typedopenral_core.geometryhelper once a second caller appears. -
_TokenBuckettwice —openral_runner.backends.gstreamer.perception_tee.PerceptionEventPublisher._TokenBucketandopenral_observability.failure_bus._TokenBucketare the same rate limiter, implemented independently on purpose (the runner must not import the observability bus's ROS-facing module for a 20-line primitive). A third copy is the trigger to hoist it intoopenral_core. -
_canonicalize_quat_xyzw(openral_state_adapter.layouts.human300_16d) vs_canonicalize_quat_xyzw_np(openral_sim.backends.robocasa) — the sim copy is a float32 numpy mirror kept for byte parity between the deploy and benchmark paths (its docstring says so). Update in lockstep; do not merge unless the parity test moves with it. -
tools/rldx_sidecar.pyconstants mirrored inopenral_sim._deps— same cross-venv wire-contract mirror as the BEHAVIOR sidecars above: the sidecar runs whereopenral_simcannot be imported. Update both in one PR. -
msgpack
_encode_ndarray/_decode_ndarrayin ninetools/_*_server.pysidecars — structurally forced: each server runs in its own isolated venv and must not import the workspace. A shared file copied into each sidecar's venv at provisioning time is the only consolidation that would not break isolation; not done. -
_sim_attachment_evidence._tight_geometry_from_pointsvsopenral_safety.tight_geometry.derive_tight_geometry— intentional online/offline twins of the DOP + hull refinement (same two stages, sameMAX_TIGHT_HULL_VERTICESceiling); the offline one is the certificate, the online one the evidence. Keep both, keep them equal. -
URDF fixed-axis RPY → matrix in the HAL —
vision_attachment_bridge._xyz_rpy_matrix(the release window, real path) andsim_sensor_bridge._rpy_to_matrix(sim path) are the sameR = Rz Ry Rx. Kept apart so the real bridge does not import the sim bridge's private helpers; the consolidation is anopenral_core.geometry.rpy_to_matrix, done when either is next touched.box_gap_lower_bound_mis deliberately not a kernel mirror (see "Convex distance" above). -
openral_human_estop.forwarder_nodeQoS copy — byte-identical to the watchdog package's_qoshelpers, left in place because sharing needs a package edge (openral_human_estop → openral_safety_watchdogor→ openral_observability) that neitherpackage.xmldeclares. Adding that edge is a decision, not a cleanup. -
Image-subscription QoS in
openral_perception_ros(ros_image_detector_node,scene_vlm_node,reward_monitor_node,segmenter_node) — built inline four times with the same parameters. Same package, so a_qos.pythere is the obvious next step when any of them is next touched. -
Per-unit sensor values —
openral_core.resolve_sensor_overlays+apply_sensor_overlaysare the one way a host's camera binding or a unit's calibrated mount/intrinsics reach a robot sensor (robots/<id>/units/<unit>.yaml,$OPENRAL_ROBOT_UNIT/DeployScene.robot_unit). Do not add per-host fields torobot.yaml, a scene-level copy of a robot sensor, or a second env var. -
Robot manifest lookup —
openral_sim.policies.robots.resolve_robot_manifestis the one$OPENRAL_ROBOTS_DIR→robots/<id>/robot.yamlresolver, shared bysim run(theROBOTSfactories),deploy sim|run(resolve_launch_invocation) andtools/audit_sim_configs.py. The former per-robotopenral_cli.deploy_sim._ROBOT_HAL_REGISTRYtable is gone (its fields were derivable); do not reintroduce a robot-id → HAL table. -
openral_nav2_bringup._footprint_geometryis a private module reached fromtools/_nav2_costmap_silhouette_probe.pyand an integration test. Its public names (convex_hull_2d,base_footprint_polygon,SHAPE_*) are de-facto API; drop the module's underscore when it is next touched. -
openral_reasoner.spatial_query.ApproachRefiner— live (it typesrefine_approachon the two exported query functions), butreasoner_node.pypasses its callback asAnywith a comment instead of importing the alias. Import it. -
openral_rskill_ros.make_local_skill_resolver— exported, documented foropenral sim run, and called only from inside its own module today. Public with no external consumer stays public; revisit if the docstring's use case never materialises. -
openral_safety.supervisor_node.SafetySupervisorNode— back-compat alias ofSafetyPassthroughNodewith zero references outside its own package's__init__docstring. Removing it touchespackages/openral_safety/(safety-WG review + hazard log), so it is recorded here rather than deleted by a cleanup PR.
Resolved in the 2026-09-22 cleanup (openral PR #296)
_h1_group/_g1_group— one_mujoco_arm._kinematic_group(joint_name, groups, *, robot); each robot keeps its group tuple.- Sidecar port derivation ×5 (
isaac_sim/robotwin/rlbench/lingbot_vla2/rldx) —_sidecar_common.sidecar_port_for_key; pinned intests/unit/test_sidecar_common.py. _CARTESIAN_KINDS/_GRIPPER_KINDS— byte-identical to_CARTESIAN_MODES/_GRIPPER_MODESinschemas.py; deleted, second validator uses the surviving pair.- Tegra host probe (
openral_cli.deploy_sim._is_tegra_host,openral_runner.backends.gstreamer.pipeline._TEGRA_RELEASE_PATH,openral_detect.probes.gpu._DEFAULT_RELEASE_PATH) —openral_core.is_tegra_host()/TEGRA_RELEASE_PATH. Do not re-probe/etc/nv_tegra_releaseelsewhere. Known exceptions, because they run whereopenral_coreis not importable: the isolated-venv sidecar scriptstools/_qwen_vlm_server.py,tools/_lingbot_vla2_server.py,tools/rlbench_3dda_sidecar.py,tools/behavior_groot_sidecar.py,tools/build_qwen_vlm_nf4_checkpoint.py,tools/quantize_lingbot_vla2.py, and the shelltools/_demo_env.sh. Each repeats the allocator gate ofdefault_expandable_segmentsinline. - CameraInfo topic rule —
openral_sensors.ros_publisher.camera_info_topic_forresolves OpenRAL's own/openral/cameras/<name>/(depth/)imagethroughopenral_core.camera_topic(name, CAMERA_INFO | DEPTH_CAMERA_INFO); its suffix rules only cover driver topics (a RealSenseros2_topic). - Torch-free GPU VRAM probe (
openral_cli.deploy_sim._detect_gpu_vram_gb/openral_reasoner_ros.reasoner_node._query_gpu_gb) —openral_core.detect_gpu_vram_gb(field)(ADR-0103). - Unified-memory-SoC VRAM fallback in
openral_detect.probes.gpu(_probe_nvidia_pynvml/_probe_nvidia_smi) — one_unified_memory_vram_fallbackhelper. _MIN_POLYGON_VERTICES—payload_scan_filter_nodeimports_footprint_geometry's.- E-stop / failure
QoSProfiles indeadman_watchdog_node/hardware_estop_node—openral_safety_watchdog._qos.estop_qos()/failure_qos()(openral_human_estop's copy stays; see Watch list). - Cross-package private imports promoted —
openral_rskill.{hf_download_cached_first,gpu_allocated_mb,find_repo_root_from,validate_skill_ref};openral_core.sensor_name_to_slot;tools/_robometer_scorer.Scorer. - Dead code removed —
packages/openral_foxglove_bringup/tools/demo_publisher.pyandtools/_verify_lingbot_nf4.py(no invocation site anywhere outside this inventory). openral_wamremoved (ADR-0104) —NullWorldModel,Rolloutand theWorldModelProtocol had no consumer in the open repo; the Protocol lives with its implementations in OpenRAL Pro._CARTESIAN_MODES-style bundled inventory bullets — everydocs/methodsbullet now names one symbol with one marker;refresh_methods_linenos.py --check --coverageis now the primary duplication detector.
Retired (resolved earlier; one line each)
- Sensor
_spec()private factory helpers — the seven vendor modules were deleted; one-per-file public spec factories remain (was item 1). - VLA adapter boundary helpers —
resolve_device/resolve_rskill_repo_id/run_inference/to_numpy_action/parse_hf_file_uri/materialize_processor_dir, once inopenral_rskill._vla_core(was item 3). - Policy load-phase heartbeat —
openral_rskill._diagnostics.phase_timer, applied per adapter through a one-line_<family>_phaseshortcut (was item 6). _load_manifest_for_spec—backends/libero.pyimportspolicies/act.py's copy (was item 16)._coerce_sim_time_ns/_opt_num—sidecar.coerce_sim_time_nsand_sidecar_common.opt_num(was item 17)._env_bool—policies/rldx.pyimportspolicies/gr00t.py's (was item 18)._sensor_name_to_slot— the ROS node imported the runner's private copy under an alias; now public asopenral_core.sensor_name_to_slot, also replacingSimSensorBridge's_obs_key_for_sensorand the runner node's_vla_camera_slots/_required_vla_camera_slots(nowopenral_core.required_vla_camera_slots) (was item 19).- Test scaffolding (
_av,_find_metric,_zero_frame,_build_so101_hal, 2 of 7_import_launch_module) — tierconftest.pyfixtures; 5_import_launch_modulecopies remain (was item 22). - Reward-monitor
assess()— both monitors callframe_source.assess_from_score(was item 23). - Test-tier fixture duplication — tier-wide fixtures live in
tests/unit/conftest.py/tests/sim/conftest.py(was item 24). disconnect()/_floatsinAlohaHAL/RosControlHAL—HALBase.disconnectdefault and_base._raw_floats(was item 27).- Sidecar scene/socket duplication —
sidecar.SidecarSimRollout,sidecar.open_req_socket,rollout.render_named_rgb_mujoco(was item 29). connected_halleftover shadows in two sim HAL tests — deleted, conftest fixture used (was item 30).- HIL transport
state/_on_joint_state/wait_for_first_state—_JointStateCache+_PolledJointStateMixin(was item 31). - Safety-kernel place-* live test harness closures — three factory fixtures in
tests/integration/conftest.py(was item 32). test_disconnect_idempotentper real HAL — already covered bytest_hal_protocol_conformance(was item 33).test_after_estop_send_action_fails— parametrized intest_hal_protocol_conformance(was item 34).test_manifest_has_latency_budget×3 — folded intotests/unit/test_task_space_sweep.py::test_all_rskills_load(every rSkill), with the per-skill sensor checks intests/unit/test_vla_sim_rskill_manifests.py(was item 35).test_send_action_holds_zero_pose/test_hold_zero_pose—tests/sim/conftest.py::assert_send_action_holds_zero_pose(was item 36)._expandPEP 735 walker ×2 —tests/unit/conftest.py::expand_dependency_group(was item 37)._CaptureProcessorshadow intest_reasoner_core.py— deleted, conftest one imported (was item 38)._connect/_rpc/_try_ping(LocateAnythingDetector/QwenSceneVlm) —backends/gstreamer/_zmq_sidecar.ZmqSidecarMixin(was item 40).- Policy adapter loader seams —
policies/_policy_loading.load_manifest_for_spec/lazy_import_lerobotand the_quantizationdtype helpers; see the DRY section. NDArrayOrNonealias —look_at_rskillimportspose_goal_rskill's.from_yaml(cls, path)×6 —openral_core.schemas._load_yaml_model;SimScene/BenchmarkSceneinheritDeployScene.from_yaml._resolve_camerasin three perception nodes —openral_perception_ros.camera_topics.resolve_camera_topics.- Hand-built
/openral/cameras/<name>/<kind>strings at ~40 sites (sim bridge, sensor leg, world-statecamera_topic_prefix, deploy launch, Foxglove layout + allowlist, vision-attachment bridge, record profiles, perception/SLAM node defaults) —openral_core.camera_topic/CAMERA_TOPIC_PREFIX(ADR-0108);tests/unit/test_camera_topic_layout.pyfails on a new one. homogeneous_from_quat_xyz— moved fromopenral_world_state.object_lifttoopenral_core.geometry; world-state keeps a thin wrapper.
Curated by hand since 2026-05-08. Regenerate the inventory files with tools/refresh_methods_linenos.py; this file is prose and is edited directly.