first commit

This commit is contained in:
2026-07-22 13:48:46 +08:00
commit c87751c3dc
2820 changed files with 726976 additions and 0 deletions
@@ -0,0 +1,110 @@
# URDF Design Ledger
Use this reference before creating or editing a URDF generator. The ledger is the written spatial model that prevents silent frame, axis, unit, and mesh-scale mistakes.
The ledger may live in the generator source comments, adjacent project documentation, a README, or a task note. It must be specific enough that another engineer can audit the generated URDF without reverse-engineering the XML.
## Required Sections
### Robot Metadata
Record:
- robot name
- target consumers: RViz, robot_state_publisher, Gazebo/Ignition, MoveIt, real robot driver, or other
- unit convention: meters, kilograms, seconds, radians unless the project explicitly states otherwise
- frame convention: REP-103-style body convention when applicable, or a documented exception
- mesh unit convention: meters, millimeters, inches, or other
- source of dimensions: CAD, drawing, measured data, vendor documentation, existing URDF, or assumption
### Link Ledger
For every link, record:
| Field | Meaning |
|---|---|
| link name | Exact URDF `<link name="...">` value. |
| role | Physical link, frame-only link, sensor frame, tool frame, base frame, or other. |
| frame definition | Where the link frame is located and how its axes point. |
| parent joint | Joint that creates this child link frame, or `none` for root. |
| visual geometry | Primitive or mesh source, with origin relative to the link frame. |
| collision geometry | Primitive or mesh source, with origin relative to the link frame. |
| inertial source | CAD mass properties, vendor data, approximation, or intentionally omitted. |
Frame-only links such as `base_footprint`, optical frames, and `tool0` may omit inertial, visual, and collision blocks. Mark them explicitly as frame-only rather than leaving intent ambiguous.
### Joint Ledger
For every joint, record:
| Field | Meaning |
|---|---|
| joint name | Exact URDF `<joint name="...">` value. |
| type | `fixed`, `revolute`, `continuous`, or `prismatic` for the current `scripts/urdf` validator; record `floating` or `planar` only if the project has a different supported validation path. |
| parent link | Link whose frame expresses the joint origin. |
| child link | Link whose frame is created at the joint frame. |
| origin xyz/rpy | Parent-link-frame transform from parent link to joint frame. |
| axis | Axis vector expressed in the joint frame, for movable joints. |
| limits | Radians for revolute, meters for prismatic, no finite lower/upper limits for continuous. |
| positive motion | What positive joint motion physically does. |
| source | CAD, drawing, measured data, existing model, or documented assumption. |
Do not write a movable joint without an explicit positive-motion convention. The sign of the axis is part of the model, not a cosmetic detail.
### Geometry Ledger
For every visual or collision item, record:
| Field | Meaning |
|---|---|
| link | Owning link. |
| kind | `visual` or `collision`. |
| geometry type | `mesh`, `box`, `cylinder`, or `sphere`. |
| source | CAD export, primitive approximation, vendor mesh, generated mesh, or temporary placeholder. |
| origin xyz/rpy | Transform from link frame to geometry frame. |
| scale | Mesh scale if applicable. |
| units | Mesh source units and URDF scale needed to express meters. |
Visual geometry is for display. Collision geometry is for contact, planning, and physics. It may intentionally be simpler than the visual geometry.
### Inertial Ledger
For every physical link, record:
| Field | Meaning |
|---|---|
| mass | Kilograms. |
| center of mass | Inertial origin xyz in the link frame. |
| inertia tensor | Tensor values and frame. |
| source | CAD mass properties, vendor data, calculation, approximation, or intentionally omitted. |
| confidence | Exact, estimated, placeholder, or unknown. |
Do not silently copy visual origins into inertial origins. The visual frame, collision frame, link frame, and center of mass can all differ.
### Assumption Ledger
Record every inferred or guessed value, including:
- unknown dimensions
- mesh units
- sign conventions
- joint axes
- parent/child direction
- visual or collision offsets
- mass, COM, and inertia approximations
- frame-only link intent
- unverified package URI resolution
Use named constants in generator code for assumed values. Prefer names like `ASSUMED_BASE_TO_SHOULDER_Z_M` over unlabelled numeric literals.
## When Information Is Missing
If spatial information is missing, do not invent a precise-looking transform. Choose one of these outcomes:
1. preserve existing source data unchanged;
2. create a frame-only or placeholder structure with explicit assumption comments;
3. use a clearly named approximate constant;
4. ask for dimensions or CAD data when the workflow allows interaction;
5. report that the generated model is structurally valid but spatially provisional.
A provisional URDF is acceptable when clearly labelled. A plausible but undocumented URDF is not.
@@ -0,0 +1,105 @@
# URDF Frame Semantics
Use this reference whenever editing origins, axes, visual placement, collision placement, or inertials. Most URDF generation errors are frame errors.
## Core Semantics
URDF represents a robot as a tree of links connected by joints.
For a joint:
- `<parent link="...">` names the parent link.
- `<child link="...">` names the child link.
- `<origin xyz="..." rpy="...">` is the transform from the parent link frame to the joint frame.
- The child link frame is coincident with the joint frame.
- `<axis xyz="...">` for a movable joint is expressed in the joint frame, not automatically in the world frame and not in the visual mesh frame.
For link subelements:
- `<visual><origin ...>` is expressed in the link frame.
- `<collision><origin ...>` is expressed in the link frame.
- `<inertial><origin ...>` is the center-of-mass/inertial frame expressed in the link frame.
These origins are independent. A mesh can be offset from its link frame, and the center of mass can be offset differently.
## Units and Angles
Use:
- meters for length;
- kilograms for mass;
- radians for angles;
- seconds for time;
- right-handed coordinate frames unless the project documents an exception.
Do not store revolute limits in degrees in generated URDF. Convert degrees to radians before emission.
Do not use finite lower/upper limits for a `continuous` joint unless the project is intentionally not using URDF continuous-joint semantics.
## Joint Axis Checklist
For every non-fixed joint, confirm:
1. the axis is present;
2. the axis vector has three finite numbers;
3. the vector is nonzero;
4. the vector is normalized or intentionally normalized by helper code;
5. the vector is expressed in the joint frame;
6. positive motion is documented.
Examples:
```xml
<joint name="shoulder_pan_joint" type="revolute">
<parent link="base_link" />
<child link="shoulder_link" />
<origin xyz="0 0 0.24" rpy="0 0 0" />
<axis xyz="0 0 1" />
<limit lower="-3.14159" upper="3.14159" effort="40" velocity="2" />
</joint>
```
This means the child link frame is at `z = 0.24` in `base_link`, and positive joint motion rotates about +Z of the joint/child frame.
## Visual and Collision Placement Checklist
For every visual or collision block, confirm:
1. the origin is relative to the owning link frame;
2. mesh scale converts the mesh source units into meters;
3. visual and collision geometry are intentionally the same or intentionally different;
4. collision geometry is simple enough for the intended physics/planning consumer;
5. mesh paths are stable from the generated URDF's location or use an intended package URI.
Example:
```xml
<link name="forearm_link">
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="package://robot_description/meshes/forearm.stl" scale="0.001 0.001 0.001" />
</geometry>
</visual>
<collision>
<origin xyz="0.12 0 0" rpy="0 1.57079632679 0" />
<geometry>
<cylinder radius="0.035" length="0.24" />
</geometry>
</collision>
</link>
```
This places the visual mesh at the link frame and uses a simplified collision cylinder offset in the same link frame.
## Inertial Placement Checklist
For every physical link with inertial data, confirm:
1. mass is positive and finite;
2. inertial origin is the center of mass in the link frame;
3. inertia tensor values are in SI units;
4. tensor values correspond to the inertial frame being declared;
5. approximations are documented.
Do not infer inertial origin from visual or collision origin unless the source data proves they coincide.
@@ -0,0 +1,46 @@
# URDF Generation
Regenerate explicit URDF outputs from Python sources with top-level `gen_urdf()` functions.
```bash
python scripts/urdf path/to/assembly.py
python scripts/urdf path/to/assembly.py -o path/to/robot.urdf
python scripts/urdf path/to/a.py=out/a.urdf path/to/b.py=out/b.urdf
```
Plain Python targets write a sibling `.urdf` beside the source. `-o`/`--output` is valid only with one plain target. Use `SOURCE.py=OUTPUT.urdf` pairs for custom multi-target destinations.
`gen_urdf()` must be a top-level zero-argument function returning the root `xml.etree.ElementTree.Element`, a complete URDF XML string, or an accepted envelope whose only field is `xml`. See `references/generator-contract.md`.
Relative source targets and CLI output paths resolve from the current working directory.
## Default Validation Behavior
The generation command validates the generated URDF before accepting the output. There is no separate validation-only command in this skill.
A generation failure should be treated as a model or source problem, not as a reason to hand-edit the generated `.urdf`. Fix the generator source, then regenerate the explicit target.
The generation-time validation is intended to catch compact robot-description issues such as:
- malformed XML or wrong root element;
- missing or duplicate link and joint names;
- invalid parent/child references;
- multiple roots, disconnected graphs, or cycles;
- suspicious joint limits, axes, origins, geometry, mesh references, or inertials when supported by the runtime.
## Boundaries
This tool runs only `gen_urdf()`. It does not regenerate CAD, mesh/export, GLB/topology, render, SDF, SRDF, MoveIt2, or simulator outputs.
If URDF visual/collision mesh references depend on updated CAD or mesh outputs, regenerate those explicit targets separately with the owning CAD or mesh workflow, then regenerate the affected URDF.
After creating or modifying generated `.urdf` files, hand explicit paths to `$cad-viewer` for live viewer links when available.
## Failure Handling
When generation-time validation fails:
1. inspect the reported links, joints, mesh references, or inertial fields;
2. update the source generator, design ledger, or referenced assets;
3. regenerate the same explicit URDF target;
4. do not patch the generated `.urdf` directly unless the task is explicitly forensic and the patch will not be treated as source of truth.
@@ -0,0 +1,105 @@
# URDF Generator Contract
Use this reference when creating or editing Python sources that generate URDF files.
## Source of Truth
The Python source that defines `gen_urdf()` is source of truth. The configured `.urdf` file is generated and should not be hand-edited.
Keep the design ledger close to the source. At minimum, generator source should make units, frame conventions, parent/child choices, joint axes, mesh scale, and assumptions easy to audit.
## Contract
`gen_urdf()` must be a top-level zero-argument function that returns one of:
- the root `xml.etree.ElementTree.Element` for a complete URDF document;
- a complete URDF XML string;
- an envelope dict with exactly one field, `xml`, whose value is an `ElementTree.Element` or XML string.
Default generator shape:
```python
import xml.etree.ElementTree as ET
def gen_urdf():
robot = ET.Element("robot", {"name": "sample"})
ET.SubElement(robot, "link", {"name": "base_link"})
return robot
```
When an envelope is needed, set `xml` to the same root element or XML string:
```python
def gen_urdf():
robot = ET.Element("robot", {"name": "sample"})
ET.SubElement(robot, "link", {"name": "base_link"})
return {"xml": robot}
```
Do not include output-path, validation, Explorer, pose-preset, or consumer metadata in the envelope. The current runtime rejects fields such as `urdf_output`, `validate`, and `explorer_metadata`; output paths are selected only by CLI arguments. Named robot poses belong in SRDF `<group_state>` elements, not URDF-side `explorer.json` or Explorer metadata artifacts.
The CLI serializes the returned payload, writes the configured `.urdf` output path, and validates that generated file before returning success.
The generated `.urdf` output path is selected by the CLI. A plain source target writes a sibling `.urdf`; `-o`/`--output` overrides one target; `SOURCE.py=OUTPUT.urdf` pairs override individual targets.
The host project may impose its own layout policy, but the URDF skill runtime does not hardcode a project directory.
## Authoring Expectations
Use explicit constants for physical dimensions, joint locations, joint limits, mesh scale, and inertial values. Avoid anonymous literals in joint origins and axes.
Prefer:
```python
BASE_TO_SHOULDER_Z_M = 0.240
SHOULDER_PAN_AXIS = (0.0, 0.0, 1.0)
FOREARM_MESH_SCALE_FROM_MM = (0.001, 0.001, 0.001)
```
Over:
```python
origin="0 0 0.24"
axis="0 0 1"
scale="0.001 0.001 0.001"
```
Document assumptions directly in comments near the constants or in `references/design-ledger.md` style project documentation.
## Frame and Unit Rules
Generators must emit URDF values in the project's URDF unit convention, normally meters, kilograms, seconds, and radians.
Before emitting a joint, confirm:
- the parent and child link names are correct;
- the joint origin is expressed in the parent link frame;
- the child link frame is intended to coincide with the joint frame;
- the joint axis is expressed in the joint frame;
- revolute limits are radians;
- prismatic limits are meters;
- continuous joints do not get fake finite lower/upper limits.
Before emitting visual, collision, or inertial data, confirm:
- its origin is expressed in the owning link frame;
- mesh scale converts mesh source units into meters;
- collision geometry is intentionally simplified or intentionally identical to visual geometry;
- inertial origin represents the center of mass, not merely the visual origin.
The current `scripts/urdf` validation path accepts any non-empty mesh filename or URI, checks local file existence when the URI resolves locally, and leaves mesh-format loadability to the target URDF consumer. Visual and collision geometry may use `<mesh>`, `<box>`, `<cylinder>`, or `<sphere>`.
## Runtime Behavior
`scripts/urdf` runs only `gen_urdf()`. It does not regenerate external CAD, mesh/export, GLB/topology, render, SDF, or SRDF/MoveIt2 artifacts.
If URDF visual/collision mesh references depend on updated CAD or mesh outputs, regenerate those explicit targets separately with the owning CAD or mesh workflow.
Importing a generator module executes its top-level Python code. Keep top-level generator modules deterministic and side-effect-light. Place expensive or mutating work behind `gen_urdf()` only when the task explicitly requires it.
## Paths
Relative source targets and CLI output paths resolve from the current working directory.
Inside generator code, prefer paths derived from the generator file or package conventions over implicit shell working-directory assumptions. Mesh filenames emitted into URDF should be stable from the generated `.urdf` file's perspective or use a package URI understood by the consumer.
@@ -0,0 +1,125 @@
# URDF Workflow
Use this reference when editing robot-description structure, frame placement, mesh references, inertial data, or generated URDF output.
## Edit Loop
1. Find the Python source that defines `gen_urdf()`.
2. Treat that Python source as source of truth and the `.urdf` file as generated.
3. Identify target consumers and strictness requirements: visualization, TF tree, simulation, planning, or real robot integration.
4. Create or update the design ledger before changing frames, origins, axes, limits, mesh scales, or inertials.
5. Apply URDF frame semantics: joint origin in parent frame, child link frame at joint frame, joint axis in joint frame, visual/collision/inertial origins in link frame.
6. Edit links, joints, limits, axes, origins, inertials, materials, visual/collision geometry, and mesh filenames deliberately in the generator source.
7. Regenerate only the explicit URDF target with `scripts/urdf <source-file>`, `scripts/urdf <source-file> -o <output.urdf>`, or `scripts/urdf <source-file>=<output.urdf>`.
8. Let generation-time validation catch structural and semantic problems. Fix the generator source instead of hand-editing generated XML.
9. If mesh outputs changed, regenerate only the affected explicit outputs with the owning CAD or mesh workflow.
10. Hand generated or modified `.urdf` files to `$cad-viewer` for live viewer links when available.
11. When available, run a consumer smoke test and report what was not checked.
## Spatial-Reasoning Guardrails
LLMs are prone to plausible-looking spatial mistakes. Use these guardrails:
- Do not infer dimensions, handedness, axes, mesh units, or joint signs from vague descriptions.
- Do not silently mirror left/right parts unless the mirror transform and sign changes are explicit.
- Do not assume visual mesh origin equals link frame, collision frame, or center of mass.
- Do not assume CAD mesh units are meters. STL files often carry no reliable unit metadata.
- Do not encode a kinematic correction by offsetting only the visual mesh; correct the link and joint frames unless the visual mesh is genuinely offset.
- Preserve existing proven transforms unless the task explicitly requires changing them.
- Use named constants and comments for assumptions.
## Standard Link Tags
Use these tags for each link that represents physical robot geometry:
- `inertial`: mass, center of mass, and inertia tensor used by simulators.
- `visual`: display geometry and optional material.
- `collision`: contact geometry used by physics and planning.
Frame-only links, such as `base_footprint`, optical frames, or tool-center marker frames, may intentionally omit these tags when they represent no physical mass or geometry.
For movable physical links, avoid zero or missing mass unless the target simulator explicitly supports that modeling choice. If exact mass properties are unavailable, use a documented approximation and make the approximation easy to replace later.
## Joint Authoring
For every joint, confirm:
- parent and child direction are correct;
- joint origin is expressed in the parent link frame;
- child link frame is intended to coincide with the joint frame;
- non-fixed joint axis is expressed in the joint frame;
- positive motion is documented;
- revolute limits are radians;
- prismatic limits are meters;
- continuous joints are not given artificial finite lower/upper limits;
- fixed joints are used for frame relationships and rigid assemblies.
Supported joint types may vary by project runtime. If the validator/runtime supports only `fixed`, `continuous`, `revolute`, and `prismatic`, do not author `floating` or `planar` joints unless the consumer and validation path support them.
## Mesh References
URDF mesh filenames should be stable from the generated URDF file's perspective or use a package URI convention understood by the consumer.
The current `scripts/urdf` validation path accepts any non-empty mesh filename or URI. Local relative mesh paths are checked relative to the generated URDF file; `package://...` and remote references are left unresolved with warnings unless a package map is supplied through `read_urdf_source()`.
When using package URIs, confirm the consuming environment resolves the package root the same way as the generated URDF expects.
Do not use generated URDF XML as the source of truth for mesh placement. Prefer deriving visual mesh references from the same source data that owns the mesh instance placement.
When mesh references point to generated assets, keep the ownership clear:
- CAD or mesh workflows own mesh generation;
- URDF generation owns references, scales, and placements;
- SRDF/MoveIt workflows own semantic groups, named joint poses via `<group_state>`, and planning metadata.
## Collision Geometry
Add collision geometry under each `<link>` that should participate in physics, contact, or collision-aware planning. Do not encode collision behavior on joints.
Use one or more `<collision>` blocks per link. The `<origin>` is expressed in the link frame, just like `<visual>`, and mesh scales must match the units of the exported mesh:
The current `scripts/urdf` validator allows visual and collision geometry to use `<mesh>`, `<box>`, `<cylinder>`, or `<sphere>`.
```xml
<link name="forearm_link">
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="package://robot_description/meshes/forearm.stl" scale="0.001 0.001 0.001" />
</geometry>
</visual>
<collision>
<origin xyz="0.12 0 0" rpy="0 1.57079632679 0" />
<geometry>
<cylinder radius="0.035" length="0.24" />
</geometry>
</collision>
</link>
```
Prefer simplified collision geometry over detailed visual meshes. Good options, from simplest to most specific:
- primitive `<box>`, `<cylinder>`, or `<sphere>` geometry when it approximates the part well;
- a coarse, closed collision mesh exported from CAD;
- the visual mesh as a temporary fallback for loading and smoke tests.
In generator sources, model collisions explicitly rather than hand-editing generated URDF. A common pattern is to add a `collisions` collection beside `visuals` in each link spec and emit it with the same origin and scale helper code used for visual meshes.
## Inertials
For each physical link, use an explicit `inertial` block when the target simulator or dynamics consumer needs mass properties.
The inertial origin is the center of mass in the link frame. It is not automatically the visual mesh origin, collision origin, or link origin.
When exact mass properties are unavailable, use a documented approximation and make it easy to replace. Mark approximate mass, COM, and inertia constants clearly.
## Smoke Tests
After generation-time validation, use the most relevant available smoke test:
- load in RViz or equivalent visualization to inspect visible placement;
- run robot_state_publisher or equivalent to check the TF tree;
- load in Gazebo/Ignition or another simulator for physics consumers;
- load in MoveIt only after URDF structure is stable, then handle semantic data through the SRDF workflow.
Report the smoke tests run and any skipped checks that would materially affect confidence.
@@ -0,0 +1,156 @@
# URDF Generation-Time Validation
Use this reference to understand what generated URDFs should satisfy. The `scripts/urdf` generation path validates by default; there is no separate validation-only command in this skill.
Validation is a guardrail, not a substitute for a design ledger or visual/consumer smoke test. A URDF can pass structural checks while still having incorrect spatial assumptions.
Generated or modified `.urdf` files should be handed to `$cad-viewer` for live viewer links when available.
## Structural Checks
Validate that:
- the root element is `<robot>`;
- the robot has a non-empty name;
- every link has a unique non-empty name;
- every joint has a unique non-empty name;
- every joint has valid parent and child fields;
- parent and child links exist;
- each child link has at most one parent;
- the graph has exactly one root link;
- the graph is connected and acyclic;
- the tree has exactly `links - 1` joints unless the design intentionally uses a different structure and the runtime supports it.
## Origin Checks
For each joint, visual, collision, and inertial origin, validate or review that:
- `xyz` has three finite numeric values when present;
- `rpy` has three finite numeric values when present;
- omitted values follow the URDF default only when that is intentional;
- units are meters for `xyz` and radians for `rpy`;
- the origin is expressed in the correct frame.
Frame correctness must be reviewed from the design ledger. XML validation alone cannot prove that an origin is spatially correct.
## Joint Checks
The current `scripts/urdf` source reader supports these joint types:
- `fixed`
- `continuous`
- `revolute`
- `prismatic`
Some URDF consumers also support `floating` and `planar`; use them only when the project runtime and validation path support them. The current `scripts/urdf` reader rejects them.
For non-fixed joints, validate or review that:
- `<axis>` exists where required;
- axis has exactly three finite numeric values;
- axis is nonzero;
- axis is normalized or intentionally normalized by generator code;
- axis is expressed in the joint frame;
- positive motion is documented in the design ledger.
For limits:
- `revolute` lower/upper limits are radians;
- `prismatic` lower/upper limits are meters;
- lower is not greater than upper;
- effort and velocity are finite when present;
- `continuous` joints do not use artificial finite lower/upper limits;
- `fixed` joints do not need motion limits.
## Geometry Checks
General URDF visual and collision geometry may use:
- `<mesh>`
- `<box>`
- `<cylinder>`
- `<sphere>`
The current `scripts/urdf` validation path allows these geometry types for both visual and collision geometry.
For geometry, validate or review that:
- each visual or collision block has a `<geometry>` element;
- each geometry block has exactly one supported geometry child;
- primitive dimensions are positive and finite;
- mesh filenames are non-empty;
- mesh scale, when present, has three positive finite values;
- visual and collision origins are relative to the owning link frame;
- collision geometry is appropriate for the target consumer.
## Mesh Reference Checks
For mesh references, distinguish syntax, local existence, and runtime package resolution.
Validate or review that:
- local relative and absolute paths are intentional;
- local files exist when filesystem validation is available;
- `package://...` URIs have valid syntax;
- package URIs are not incorrectly assumed to resolve from the current working directory;
- the consuming ROS or simulator environment can resolve package URIs;
- mesh source units and emitted scale convert to meters.
The current source reader accepts any non-empty mesh filename or URI. It resolves local mesh paths from the generated URDF file's directory and verifies those files exist. It accepts unresolved `package://...` and remote references with warnings, because final mesh-format support is consumer-specific.
If mesh references changed, confirm the corresponding mesh outputs were regenerated separately by the owning CAD or mesh workflow.
## Inertial Checks
For each physical link with mass or geometry, prefer an explicit `inertial` block:
- `origin` is the center of mass in the link frame;
- `mass` is positive and finite;
- `inertia` defines `ixx`, `ixy`, `ixz`, `iyy`, `iyz`, and `izz`;
- all inertia values are finite;
- diagonal inertia values are positive;
- the inertia matrix is physically plausible for the intended approximation;
- approximations are documented.
Frame-only links may intentionally omit `inertial`. Physical links with omitted inertials should be called out when the target consumer uses simulation or dynamics.
## URDF Validity vs Project Policy
Separate general URDF validity from project-specific policy.
Examples of general URDF validity or sanity checks:
- unique links and joints;
- valid tree structure;
- valid joint parent/child references;
- finite origins;
- nonzero movable-joint axes;
- positive primitive dimensions;
- positive mass for physical inertial blocks.
Examples of project policy checks:
- visual mesh references must use a specific extension;
- visual geometry must be mesh-only;
- collision geometry must be primitive-only;
- mesh files must live under a specific directory;
- every physical link must have collision geometry;
- every physical link must have inertial data;
- package URIs must use a specific package prefix.
When a generated URDF fails because of project policy, report it as policy failure, not as universal URDF invalidity.
## Failure Handling
When generation-time validation fails:
1. fix the generator source, design ledger, or referenced assets;
2. regenerate the explicit URDF target;
3. do not hand-edit the generated `.urdf` as a permanent fix;
4. record any remaining assumptions or unchecked spatial claims.
## Tooling
Use `scripts/urdf/source.py` through the `urdf.source.read_urdf_source()` helper for compact robot/link/joint checks when writing tests or focused checks.
The URDF source reader is a lightweight standard-library validator. It checks the generated subset used by this skill; consumer-specific parser and mesh-loading compatibility should still be smoke-tested in the target runtime when confidence matters.