Hermes Agent Skill Authoring
NousResearch/hermes-agent
Author in-repo SKILL.md files: frontmatter and structure. An agent skill from NousResearch/hermes-agent.
Author or diagnose LunCoSim USD physics: rigid bodies, colliders, joints, joint frames, drives, gravity, collision filtering, and scene teardown.
$ npx skills add LunCoSim/lunco-sim --skill author-usd-physics -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install LunCoSim/lunco-sim author-usd-physics --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/LunCoSim/lunco-sim.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/author-usd-physics .claude/skills/author-usd-physics && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "author-usd-physics" agent skill from https://github.com/LunCoSim/lunco-sim/tree/main/skills/author-usd-physics into .claude/skills/author-usd-physics/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "author-usd-physics", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/LunCoSim/lunco-sim/tree/main/skills/author-usd-physicsType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add LunCoSim/lunco-sim --skill author-usd-physics -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install LunCoSim/lunco-sim author-usd-physics --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/LunCoSim/lunco-sim.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/author-usd-physics .agents/skills/author-usd-physics && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "author-usd-physics" agent skill from https://github.com/LunCoSim/lunco-sim/tree/main/skills/author-usd-physics into .agents/skills/author-usd-physics/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "author-usd-physics", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add LunCoSim/lunco-sim --skill author-usd-physics -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install LunCoSim/lunco-sim author-usd-physics --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/LunCoSim/lunco-sim.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/author-usd-physics .cursor/skills/author-usd-physics && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "author-usd-physics" agent skill from https://github.com/LunCoSim/lunco-sim/tree/main/skills/author-usd-physics into .cursor/skills/author-usd-physics/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "author-usd-physics", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/LunCoSim/lunco-sim.git --path skills/author-usd-physics--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add LunCoSim/lunco-sim --skill author-usd-physics -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install LunCoSim/lunco-sim author-usd-physics --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/LunCoSim/lunco-sim.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/author-usd-physics .gemini/skills/author-usd-physics && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "author-usd-physics" agent skill from https://github.com/LunCoSim/lunco-sim/tree/main/skills/author-usd-physics into .gemini/skills/author-usd-physics/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "author-usd-physics", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install LunCoSim/lunco-sim author-usd-physicsInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add LunCoSim/lunco-sim --skill author-usd-physics -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/LunCoSim/lunco-sim.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/author-usd-physics .github/skills/author-usd-physics && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "author-usd-physics" agent skill from https://github.com/LunCoSim/lunco-sim/tree/main/skills/author-usd-physics into .github/skills/author-usd-physics/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "author-usd-physics", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add LunCoSim/lunco-sim --skill author-usd-physics -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install LunCoSim/lunco-sim author-usd-physics --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/LunCoSim/lunco-sim.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/author-usd-physics .opencode/skills/author-usd-physics && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "author-usd-physics" agent skill from https://github.com/LunCoSim/lunco-sim/tree/main/skills/author-usd-physics into .opencode/skills/author-usd-physics/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "author-usd-physics", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
author-usd-physicsAuthor or diagnose LunCoSim USD physics: rigid bodies, colliders, joints, joint frames, drives, gravity, collision filtering, and scene teardown.
Author Usd Physics is an agent skill from LunCoSim/lunco-sim. Author or diagnose LunCoSim USD physics: rigid bodies, colliders, joints, joint frames, drives, gravity, collision filtering, and scene teardown. Use for exploding vehicles, wrong hinge or slider motion, detached parts, bad contact, or scene-owned physics state. The critical contracts are two joint frames, explicit body attachment, standard UsdPhysics fields, and teardown for every scene-owned write. Use build-vehicle for mobility assembly.
Its SKILL.md is about 9.7k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
The repository describes itself as: Collaborative Multiphysics Cosimulator For Space Missions 🌎🚀🌚. The licence is Apache-2.0.
6 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 43f1301. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
kindFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Author Usd Physics loads about 9.7k tokens when it runs. Until then it costs about 116 tokens; SKILL.md has 5,116 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from LunCoSim/lunco-sim at commit 43f1301, republished under its Apache-2.0 licence (© LunCoSim). 5,116 words, ~9,733 tokens.
.claude/skills/author-usd-physics/SKILL.md (or your agent's skills folder).Physics is authored in USD and projected onto avian. USD is the source of
truth; the ECS is the projection. Use maintained UsdPhysics schemas for
topology, limits, and commandable drives. When USD has no material schema for a
physical concept, use the narrow LunCo applied API that owns that concept. Do
not add a LunCo schema when standard USD already owns the fact.
For a physical landing member, apply PhysicsDriveAPI:linear to a
PhysicsPrismaticJoint. Author the drive's stiffness, damping, maxForce,
and explicit force type; targetPosition and targetVelocity retain the
standard USD defaults unless the asset needs to state them explicitly. The
native Avian joint is the sole axial mechanism in the existing substep
schedule. Do not duplicate standard fields under lunco:*. Missing or invalid
fields fail projection; they are never replaced by a target, force cap, or
solver-resolution workaround.
Native joint angle and displacement commands preserve the configured drive
model and force/torque limit. They enable the motor and change its position
target with zero target velocity; author drive gains and limits through the
standard USD drive owner rather than through command-side tuning.
Omitted joint limits retain standard unbounded travel. The native port adapter maps lower negative infinity and upper positive infinity to absent bounds; malformed nonfinite limits remain invalid. Do not add artificial finite travel limits to make a valid unbounded joint accept a command.
Startup joint topology is prepared while the shared fixed clock is held. The
BigSpace physics bridge seeds authored body poses in PreUpdate; USD joints
are projected in JointPreparation during Update, after USD simulation
projection and before the generic JointAdmission owner. This phase must not
wait for a first physics step to resolve bodies or seat a joint.
Initial body admission validates raycast-wheel support footprints against the
composed Avian collider geometry and its hit normal. It does not require a
LocalGravity sample from a fixed cycle that has not been admitted yet. Keep
authored-pose validation in lifecycle preparation; the first synchronized
physics cycle begins only after the complete admission set is ready.
For asset-level checks, the authored assembly_audit tool can inspect explicit
composed joint body relationships, cardinal axes, optional local frames,
rigid-body/joint coverage, and mass/inertia/collider manifests. Use those
reports to diagnose authored topology before changing solver settings. Pass the
exact Editor document id first to each stage-reading helper; () explicitly
selects the mounted live scene, not the focused preview. The
tool does not infer a body from a name, turn a raycast wheel into a rigid body,
or replace the standard USD physics owner.
For a single explicit decision per maintained part, use
assembly_audit::physicality_report(doc, manifest). Entries choose
physical or visual-only; physical entries reuse the existing body/joint and
collider/mass fields, while visual-only entries require a non-empty reason and
must have no composed collision envelope. Unknown roles, duplicate paths,
missing prims, and incomplete coverage are errors. This is Rhai policy and
orchestration; it does not add a second collider reader or Rust asset rule.
DetachJoint is the generic entity-detach command. It is deliberately not
limited to entities carrying PhysicsJointLink: an ordinary non-joint target
uses the normal entity-removal path. A target carrying native or pending joint
state is classified as a joint and must use the physics lifecycle marker. The
command records any authored endpoint invalidation and never directly removes a
native joint. The lunco-usd-avian-joints::JointAttachPlugin consumes that marker at its Update
boundary and performs one transaction in this order:
JointGraph edge and its PhysicsIslands entry;JointComponentId;The retirement owner only runs when a PhysicsJointDetachRequested marker is
present. Idle updates skip its exclusive graph transaction and world query.
Joint admission readiness is also gated by one combined query over the existing
PendingUsdJoint and PendingJointAdmission markers. The owner rechecks the
scene projection and endpoint readiness before each pending batch is admitted,
while settled updates with no pending joint skip that scan.
Scene teardown uses the same graph-retirement helper. This shared owner is what
prevents a component-removal observer and a recursive despawn from unlinking one
island edge twice. A command or Twin script must never implement a second joint
detach path, disable JointDisabled as a substitute, or infer joint completion
from the command acknowledgement; verify that the joint entity is gone and both
endpoint bodies continue finite stepping. If a target has joint state but no
PhysicsJointLink, the command warns and rejects it; the authored topology
linter is the repair/diagnostic path. Persistent intent journals the typed USD
active-state/remove operation separately, while Interactive remains a
throwaway live transition.
When an Editor workflow creates a new moving part, use
assembly_edit::rigid_body_plan for the explicit body schema and mass facts,
then add the shape, collider, and local transform in the same proposal. Use
assembly_edit::revolute_joint_plan for a rotating part: pass both body paths,
both authored frame anchors and quaternions, the cardinal joint axis, ordered
degree limits, and the collision policy. These helpers return typed operations
for review; they do not infer bodies from hierarchy or drive a mechanism with
Euler poses.
Use assembly_edit::fixed_joint_plan for a rigid adapter: it requires the
same explicit two body paths, two local anchors, two local quaternions, and
collision policy, and returns a standard PhysicsFixedJoint plan without
mutating the document.
For higher-level construction, use the dynamically reloadable
assembly_builder Rhai library. Its frame_plan, cube_plan,
cylinder_shape_plan, movable_cube_plan,
component_bundle_facts/component_bundle_plan,
existing_rigid_body_plan, and hinge_plan compose the same explicit standard
USD facts for new or referenced parts; it does not promote a completed body or
reuse an occupied standard joint identity, while its placement and cube
edge-alignment plans reject ambiguous parent/frame assumptions before the
proposal reaches the USD owner. Keep the generated plan in one reviewed
change set. A component bundle may author PhysicsMassAPI on its root and
collision APIs on its geometry, but it does not infer a rigid body or joint;
use the explicit body/joint/mount planners when the part is movable. A
successful Rhai plan is not permission to omit the frame, collider, mass, or
generation contract.
For an existing generic component whose geometry, frames, and mass must change
together, use component_editor::update_plan with an explicit bundle recipe.
Its component_editor::update_context/selected_update_context helpers are
read-only discovery; the returned plan still goes through the normal
proposal/review/commit path. The planner preserves topology and material
ownership and rejects role drift, so it is not a replacement for explicit body
or joint authoring.
For physical placement, use assembly_builder::place_with_clearance_plan with
explicit Cube envelopes, or
assembly_builder::place_with_collision_clearance_plan with exact moving and
blocker body paths for referenced, Cylinder, Mesh, or compound bodies. The
general tool reads aggregate QueryUsdPrim { collision_bounds: true } results
from the shared collision owner and requires a translation-only common parent
chain. Both paths reject overlap, insufficient gap, duplicate blockers, and
ambiguous frame/geometry data before proposal. Do not rely on visual
separation or an unreviewed generated USD transform to establish clearance.
For deterministic mating of non-Cube bodies, use the companion
align_collision_centers_plan or align_collision_edges_plan with exact
paths and an explicit axis/edge/gap; these plans preserve authored physics
facts and still require review before commit.
For reusable referenced parts, use assembly_builder::find_compatible_socket
and assembly_builder::mount_component. These helpers select authored socket
relationships and derive the reflected fixed/revolute/prismatic joint; the
existing AttachComponent owner remains responsible for plug-frame math,
reference lowering, occupancy, and atomic journalling. Do not copy a reference
and guess a transform when the component already advertises a mount plug.
For a part that is already mounted, use
assembly_builder::mount_frame_realignment_plan with the exact host, socket,
part, and recorded-joint paths. It is a dynamic Rhai plan for nested canonical
rigid frame composition and emits the part transform plus both exact joint
anchors as one reviewed operation set. It preserves topology and fails before
proposal on missing/ambiguous relationships, wrong plug/body identity,
unsupported frame stacks, malformed values, or non-unit scale.
When a model is a reusable referenced assembly rather than a socket attachment,
use assembly_builder::referenced_instance_plan or
assembly_builder::referenced_instance_targeted_plan when the source prim must
be explicit. Then wait for its composed children before using
assembly_builder::select_variants_plan. This keeps first-use asset loading
separate from the variant recompose and makes missing parents, occupied
identities, and invalid targets fail in Rhai before review. Parameter edits use
assembly_builder::parameter_plan; the resulting typed SetAttribute ops go
through the same proposal/journal boundary as every other authored edit.
Mission-level recipes may compose these generic helpers through a Twin-local tool library. Keep those recipes and their study values in the owning Twin, not in the core asset library, and keep their output to explicit generic USD operations. The core should provide only reusable component facts, topology, placement, and typed-op validation.
QueryUsdPrim { collision_geometry: true } reads the composed collider through
the same USD-to-Avian geometry reader used by projection. Use it when a
requirement must compare the backend shape to its visual or design geometry;
authored radius/height, geometry_bounds, and collision_bounds answer
different questions. This query predicts the geometry Avian will receive from
the current composed USD. It does not prove that a live ECS entity has completed
projection or is currently contacting the world.
For UsdGeomNurbsPatch, use the read-only PlanNurbsCollisionProxy query and
the shared nurbs.rhai proposal. Pass max_refinement_delta_m in canonical
metres. This bounds the sampled change between two successive tessellation
levels; it is a deterministic convergence criterion, not a certified maximum
distance from the exact rational surface. Review the proposal that creates an
invisible, source-linked UsdGeomMesh carrying standard mesh-collision APIs.
Projection re-cooks the source and rejects stale or edited derived geometry.
Do not author a separate collider approximation by hand or treat this threshold
as a CAD accuracy guarantee.
The Avian mesh adapter advertises its implemented USD approximation modes via
AvianMeshCollisionApproximations: none, convexHull, and
convexDecomposition. none is valid only for static or kinematic bodies.
Other standard USD tokens remain valid authored values, but this adapter
rejects them until it can realize their stated semantics; it does not silently
substitute another shape.
The local avatar is a runtime kinematic camera embodiment, not an authored
rigid body. Its MoveAndSlide capsule reuses the standard UsdPhysics
colliders projected by the Avian bridge in the active BigSpace frame. Do not
add an avatar collider schema or a second collision representation; see the
BigSpace physics boundary
for the frame conversion and Twin-scoped traversal policy.
This is the single most expensive thing to get wrong, and it fails silently.
A UsdPhysicsJoint is defined by a frame on each body:
| USD | avian | meaning |
|---|---|---|
physics:localPos0 / localPos1 | JointFrame::anchor | where the joint attaches, in each body's local space |
physics:localRot0 / localRot1 | JointFrame::basis | how the joint frame is ORIENTED, in each body's local space |
physics:axis | slider_axis / hinge_axis / twist_axis | a CARDINAL axis of the joint frame — X, Y or Z is the whole vocabulary |
physics:axis can only name a cardinal axis. That is exactly why localRot
exists: it is how a mechanism that is not axis-aligned — a landing leg raked 25°
off vertical — says where its axis really points.
Both halves must cross into the engine. Every avian joint except the
spherical constrains relative ORIENTATION through basis1/basis2. An identity
basis therefore demands its body sit square to the other body. Carrying the rake
in the axis alone aims the slider correctly and still wrenches the strut 25° out
of true — the constraint is violated from the first step, the solver resolves it
impulsively through the strut's lever arm, and the vehicle disassembles at
kilometres per second.
The rule when a body rests at an angle: if body1 is rotated relative to
body0 in the authored rest pose, that rotation lives in the FRAMES. The joint
holds rot0 · localRot0 == rot1 · localRot1, so author whichever side is needed
to make that identity true at rest.
# LegPX is raked +25° about Z (its own xformOp:rotateXYZ).
def PhysicsPrismaticJoint "LegPX_Spring" (
prepend apiSchemas = ["PhysicsDriveAPI:linear"]
)
{
rel physics:body0 = </DescentLander>
rel physics:body1 = </DescentLander/LegPX>
uniform token physics:axis = "Y" # cardinal, IN the joint frame
quatf physics:localRot0 = (-0.216440, 0, 0, 0.976296) # 205° about Z: +Y down the strut
quatf physics:localRot1 = (0, 0, 0, 1) # the same 180° flip; the leg body
# already carries the 25°
}Quaternions in USD are (w, x, y, z). Angles — physics:lowerLimit,
upperLimit, coneAngle0Limit, coneAngle1Limit — are degrees.
The loader measures every joint against its authored frames at build and reports a violation, seating the body only where the constraint determines it uniquely:
[usd-avian] joint /…/PadNX_Weld starts violated by 0.000 m / 0.436 rad — seating
`/…/PadNX` onto the authored joint frame. frame0: localPos0=… localRot0=…,
frame1: localPos1=… localRot1=…, body0 at …, body1 at …0.436 rad is 25°. An angular violation on a raked mechanism is almost always
a missing physics:localRot1: body0's frame was authored off-cardinal and
body1's was left at identity, so the joint demands body1 sit square to body0.
Position is checked for every joint type. Orientation is checked and seated only where all three rotational DOF are locked (fixed, prismatic); a revolute or spherical joint leaves rotation free by design, so it is reported and left to the solver.
An antenna, solar panel, camera gimbal, arm, or other tracking head has two separate concerns:
PhysicsRigidBodyAPI and is joined
to its immediate predecessor with a standard PhysicsRevoluteJoint.inputs:angle port. The cosim joint backend owns the position
motor only while that port is wired.The assembly owns its own hinges. Compose the assembly's root directly onto the host body and keep its internal joints and controller connections relative to that root. The host supplies no duplicate attachment joint; it only chooses to compose the assembly. Keep the visible mechanism under a unique child path.
This is the direction of dependency: a higher-level rover, lander, tower, or
ground station knows it installs an antenna; the antenna knows only its own
frames, joints, ports, and Modelica controller. The lower-level assembly never
names Rover, Lander, or any other concrete host.
Never drive a tracking mechanism by xformOp:rotateXYZ. An authored rest
orientation is allowed where it belongs in the joint frames; continuous yaw or
pitch is a joint state. Euler writes bypass contacts, limits, solver state, and
the measurable angle output, then race the physics bridge.
Before authoring or changing a tracker, write down and validate the complete
chain: world direction convention, mount-frame conversion, each joint's positive
axis/order, and the physical boresight of the final geometry. Do not assume the
vehicle's conventional -Z forward is the antenna or panel boresight: inspect
the composed asset. Derive the Modelica vector-to-angle equations from that
actual geometry, then verify live that the target vector, controller setpoint,
joint angle, and visible boresight agree. A controller's self-error/"locked"
output alone is not evidence of physical pointing; it can validate the same
wrong convention it commanded.
PhysicsRevoluteJoint is a generic mechanism, not a wheel marker. Do not infer
vehicle topology from an antenna, solar tracker, or robotic-arm hinge.
A PhysicsPrismaticJoint locks all three rotational DOF. It is a slider, not a
pin. This has a consequence that is easy to miss and impossible to see in a
screenshot:
Because it carries moment, a sprung leg has two ways to absorb a landing: slide along its axis, which is the one you designed, or bend its angular lock, which you did not. The second is always available, and the solver will take it whenever a stray contact makes it cheaper.
A suspension has exactly one intended load path — foot → spring → chassis. Give the leg any second way to reach the ground and that path wins, because it is rigid and the spring is not. A contact that only sometimes touches is worse than one that always does: it latches on the first frame it grazes and never lets go.
The diagnostic is a conjunction: displacement remains zero, the joint force
is absent or near zero under load, and the joint's angular lock is non-zero.
Measure the angular error together with the reaction force; angular error alone
is not enough because the joint solver is elastic.
Ground clearance is a load-path property, not a styling one. A raked box
strut's bottom corner hangs half_thickness * sin(rake) below its tip, so a
footpad centred on that tip clears it by almost nothing. Millimetres of margin on
a metres-long vehicle is zero margin: a fraction of a degree of tip puts the
strut on the ground. Size the foot so it is the only thing that can touch, by a
margin no small rotation can close — and beware that half-measures make it worse,
because a deeper foot demands a larger leg rotation to reach the ground, which
brings the strut down faster than the foot drops.
Measure the angular lock directly from the two body orientations. A prismatic
holds rot0 · localRot0 == rot1 · localRot1; the angle between the two authored
joint axes is its constraint error. Then isolate contacts and inspect the
composed collider geometry. A raked box can contact the ground with a corner;
prefer a measured primitive or a deliberately authored proxy when the foot must
be the sole load path. Change solver and friction parameters only after the
geometry and contact ownership are verified.
Anchors left unauthored are DERIVED from the transform hierarchy, which puts
displacement at exactly 0 in the authored rest pose. A leg authored -0.8 .. 0.0
therefore rests on its upper limit and travels one way only — by design: 0 is
the fully-extended pose the geometry is drawn in, and the ground can only
compress it.
So a stroke pinned at 0.0000 is not evidence the limits are backwards. Check
the load path first. Widening the limit to make a jammed leg move buries the
actual defect under a range the mechanism never needed.
Anchors are also why you cannot freely move a body to fix clearance. The anchor is derived from the body's origin; move the origin and you move the joint's zero, silently preloading the spring by the axial component of the shift. Change the part's extent, or the mating part, not the sprung body's origin.
A prim that is both the rigid body and the geometry cannot host children, because its shaping transform applies to them too. Give the body its own frame and put the geometry inside it:
def Xform "LegPX" (prepend apiSchemas = ["PhysicsRigidBodyAPI", "PhysicsMassAPI"])
{
# origin at the hull anchor, local -Y down the leg
double3 xformOp:translate = (2.519, 1.388, 0)
double3 xformOp:rotateXYZ = (0, 0, 25.0)
uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateXYZ"]
def Cylinder "Strut" (prepend apiSchemas = ["PhysicsCollisionAPI"])
{
uniform token axis = "Y"
double radius = 0.075
double height = 7.05 # spans local y 0 .. -7.05
double3 xformOp:translate = (0, -3.525, 0)
uniform token[] xformOpOrder = ["xformOp:translate"]
}
def Cylinder "PadPX" (prepend apiSchemas = ["PhysicsRigidBodyAPI", "PhysicsCollisionAPI"])
{
double3 xformOp:translate = (0, -7.2, 0) # 0.15 below the strut's tip
... # + a joint, below
}
}Now every part of the leg is placed by how far down the leg it sits, in ONE frame, next to its neighbours. "The foot is below the strut's tip" is something a reader can see and a linter can compute. Place them in world coordinates instead and you have written the same geometry twice, in two frames — and the copies drift.
UsdGeomCube has only a uniform size, so any real box has to be faked with a
non-uniform xformOp:scale. That scale then belongs to the prim rather than to
the shape, and everything downstream has to remember it: the frame is unusable for
children, the collider is a scaled shape rather than a measured one, and the part's
true dimensions appear nowhere in the file.
Cylinder, Capsule, Cone and Sphere carry radius / height — the
dimensions themselves, in metres. Prefer them, and prefer a Mesh with authored
extent to a scaled primitive. A strut is a cylinder; modelling it as a squashed
cube buys nothing and costs the frame.
UsdPhysicsScene — the standard prim, physics:gravityMagnitude (scene units
per second squared) and physics:gravityDirection (a vector in the stage's
frame). Both convert at the boundary like every other authored quantity.
def PhysicsScene "PhysicsScene"
{
vector3f physics:gravityDirection = (0, -1, 0)
float physics:gravityMagnitude = 1.62
}PhysicsScene. Gravity there is per-body and
position-dependent (Gravity::Surface + the celestial point-mass model). A
flat vector would override that and pin every spacecraft to a fictitious
"down". assets/scenes/celestial/artemis_2_review.usda is the worked example.Traction is gravity-dependent, and test thresholds must be too. At 1.62 a
rover is traction-limited well below its drivetrain's omega_max * r ceiling
(measured 2.2–2.4 m/s against 4.8). A kinematic ceiling holds at any gravity — a
driven wheel cannot out-run its no-slip speed — but any floor derived from Earth
traction is simply wrong on the Moon.
Anything a scene load writes belongs to that scene. Unloading despawns the
entities; SceneTeardown unloads everything else. Register teardown for every
scene-owned resource or override so a new scene starts from its own contract.
Add a reset system beside the code that writes the state:
app.add_systems(
lunco_core::SceneTeardown,
|mut commands: Commands| commands.remove_resource::<MySceneCache>(),
);Which disposition is right depends on who owns the value:
SceneTeardown grep-lists everything a reload restores. If you add
scene-derived state and do not register it, you have added a leak.
The shared physics owner clears PhysicsHolds and PhysicsStepRequest and
restores a zero-delta Time<Physics> at this boundary. The time owner keeps
SimTick and Time<Fixed> continuous across scene changes; it reanchors the
new mission epoch at the current tick and holds fixed cycles until the new scene
is ready. Any admitted fixed-time balance remains with that shared clock and
advances only after its causal holds clear. The controller/core owners clear
scene-keyed input and control-path state. Do not add an asset-local pause flag;
readiness and deliberate physics stepping must be owned by the incoming scene.
| Symptom | Look at |
|---|---|
origin.is_finite() panic in obvhs | a body reached ±inf; a raycast was issued from it. The cause is upstream — find the first body has non-finite state diagnostic |
[physics] body left the world: … or body has non-finite state: … | first escapee names the mechanism that diverged. The required physics.body_escape Rhai policy pauses that dynamic joint island by default, including its colliders, while other bodies continue; inspect the first escapee to find the upstream cause. Bodies at the end of a lever arm (pads, wheels) escape first |
joint … starts violated by … rad | a joint frame; see §1 |
stroke reads exactly 0.0000 in every regime | a second contact carrying the load (§2). Measure the joint's angular-lock error before touching its limits |
| a spring loads the "wrong way" | almost never the joint. A jammed DOF and a reversed one look identical from the port; §2 tells them apart |
| a scene retains another scene's setting | a resource or override missing from SceneTeardown (§4) |
| a part is lying on the ground behind the vehicle | it declared its own body and no joint holds it (§6). --validate the asset, or cmd("RunLint", #{}) the scene |
PhysicsRigidBodyAPI declares a body, and the loader honours it wherever it
appears — ancestry is never consulted, because nesting-plus-joint is exactly how
a wheel is mounted (Wheel_FL under the chassis + a PhysicsRevoluteJoint).
So a prim that applies it and is jointed to nothing is a free body inside your
vehicle, and it leaves:
def Xform "Rover" (prepend apiSchemas = ["PhysicsRigidBodyAPI"]) {
def Xform "Motor_FL" (prepend apiSchemas = ["PhysicsRigidBodyAPI"]) { … } # ❌ falls out
def Xform "Motor_FL" (prepend apiSchemas = ["PhysicsMassAPI"]) { … } # ✅ part of the rover
}The rule. Hierarchy is namespace; a joint is attachment.
Ownership follows from that, and it stops at every body boundary in BOTH directions — USD and avian agree:
| what you author | what it becomes |
|---|---|
| collider with no body ancestor | standalone STATIC geometry |
| collider under a body | a piece of that body's compound shape |
| collider under a nested body | that nested body's piece — never the parent's |
| a nested body | a SEPARATE body; a joint attaches it, or it falls off |
The body prim may also carry PhysicsCollisionAPI. If it has a collision shape
and descendants, both the body-root shape and every eligible descendant shape
belong to the same compound. The root shape is identity-local to the body; its
authored transform is applied by the body's ECS transform once, while child
transforms are accumulated from the body frame. This remains true when the
body arrives through a reference or a prepared runtime-instance projection.
physics:collisionEnabled=true is an opt-in on an already-declared collider;
it does not apply PhysicsCollisionAPI by itself. Ordinary vehicle geometry
must carry both. LunCoTerrainAPI is the owner-level projector exception.
Raycast PhysxVehicleWheelAPI wheels are query-only and must not carry
PhysicsRigidBodyAPI or PhysicsCollisionAPI; physical wheels are the other
standard realization and must author both APIs on their collision geometry.
The collision-enabled-without-api, raycast-wheel-collision-contract, and
physical-wheel-collision-contract rules are errors, not runtime fallbacks.
Terrain collider ownership is mode-specific: lunco:assetMode="mesh" waits for
the USD-to-Avian mesh bridge, while "dem" and "layered" use the native
SurfaceOracle collider from lunco-terrain-surface. Do not author or project a
second mesh collider over DEM-backed terrain.
Initial poses are admitted against the live terrain after the USD pose bridge has
seeded every body. Convex colliders use their actual support geometry; composite
and non-convex shapes retain the conservative AABB check. A persisted pose that
overlaps after a terrain rebake is held as an authored error. Repair it with the
typed TransformEntity command or remove it with DeleteEntity; do not clear the
pending marker or silently translate the body in Rust.
Every renderable gprim under a composed kind = "assembly" vehicle must also
state who owns its collision contract. A supported enabled
PhysicsCollisionAPI shape is the ordinary owner. A raycast
PhysxVehicleWheelAPI prim owns a support query, not a collision shape;
physical wheel realization is selected by a standard revolute joint targeting
the wheel and requires the authored body/collision pair. Intentional decoration must explicitly author
physics:collisionEnabled = false or an inherited purpose = "guide". When a
body has a purpose = "proxy" shape, its purpose = "render" geometry is the
visual description and is excluded from collision. Leaving visible vehicle
geometry unannotated is an error reported by vehicle-part-collision-contract,
not an invitation for a runtime fallback.
Hierarchy is namespace. The joint is what attaches — nesting a body without
one is the motor bug (nested-body-no-joint), and nesting one with a joint is
how a foot mounts on a leg and a wheel on a chassis. Both directions of that rule
matter: fold a nested body's collider into its parent's compound and one shape has
two owners, the compound holding it rigidly while its joint pulls it. They fight
every step until a body leaves the world.
AttachSpec) writes, and it is why
mounting/demo_probe.usda may keep its body.JointCollisionDisabled covers only the bodies named by a joint. For additional
contacts, author PhysicsFilteredPairsAPI explicitly; for many-to-many sets,
use PhysicsCollisionGroup with UsdCollectionAPI. Do not infer a vehicle-wide
self-exclusion: articulated mechanisms often need selected internal contacts.
Filtering is symmetric, targets may name a body or a collider under that body,
and invalid targets or same-compound pairs must remain visible diagnostics. The
filter is installed during PhysicsSystems::Prepare, before contact generation.
Verify contact itself in a scene test such as scenes/tests/filtered_pairs.usda
or collision_groups.usda, not only by checking subsequent motion.
purpose — which geometry is the collision geometryUsdGeomImageable.purpose is inherited and separates display geometry from
collision geometry:
| purpose | drawn | collided |
|---|---|---|
default (nothing authored) | yes | yes |
render | yes | only when the body has no proxy |
proxy | no | yes — this is the collision shape |
guide | no | never |
Use proxy for an authored collision shape alongside render geometry. A
guide prim is never a body or collider. purpose does not replace the frame
contract in §2b; proxy geometry still inherits its authored frame.
"$LUNCOSIM_BIN" --validate assets/vessels/rovers/skid_rover.usda[usd/nested-body-no-joint] /SkidRover/Motor_FL — applies PhysicsRigidBodyAPI
inside the body </SkidRover> but no joint names it — …and on the loaded scene, run the same rules through the verb:
cmd("RunLint", #{}); query("GetDiagnostics", #{scope: "loaded_stages"});The rules are authored in assets/scripting/policy/lint_usd.rhai — add one there
rather than in Rust. Pair the lint with a scene test such as
scenes/tests/parts_attached.usda, because lint cannot simulate motion. See
validate-assets
and docs/architecture/lint-substrate.md.
Mounted-part linting catches free bodies, but it cannot prove a rover will stay upright under drive. Add a real flat-ground or production-terrain scene test that settles the assembled vehicle, drives through its authored control path, and reports:
On lunar regolith, inspect the force application height and the authored
physics:centerOfMass before changing solver parameters. If all driven wheels
reach the friction limit and the centre of mass is high above the contact plane,
the launch pitch moment is physical. Lower or correctly assemble the mass
distribution and rerun the test; do not turn a repeatable tip into a larger tilt
allowance or a rendering filter.
For reusable authored evidence, use
physics_acceptance::sample, contact_report, and the corresponding
contact_acceptance, joint_distance_acceptance, motion_acceptance, and
settling_acceptance helpers. Supply limits from the fixture or Twin model;
the library only reads the production telemetry and diagnostic surfaces. Use
system_evidence/system_acceptance to keep readiness and binding failures
visible beside physical measurements. Do not add a vehicle-specific acceptance
helper or change the solver to satisfy a threshold.
Before authoring a schema because a DCC offers it, check that the importer
consumes it. The full table is in
docs/architecture/21-domain-usd.md.
Commonly mistaken fields include:
PhysicsArticulationRootAPI — avian has no reduced-coordinate articulation;
do not expect it to change this runtime.UsdGeomPointInstancer — the composed visual reader supports static direct
renderable Gprim prototypes and standard array/mask semantics. Time-sampled
arrays, animated prototypes, and arbitrary prototype subtrees fail visibly
because their runtime sampling or visual multi-mesh batching is not yet
implemented. Native instanceable = true scenegraph instancing remains
outside the runtime projection.proxyPrim — not read; purpose on a sibling covers the case we have
(see §6b).An unconsumed field has no runtime effect. Use a supported schema or implement the owner and its projection before authoring the field.
luncosim test runs one authored scene plus its scenario deterministically, and
its exit code comes from a telemetry verdict:
"$LUNCOSIM_BIN" test \
--scene scenes/tests/landing_legs.usda --max-ticks 500A physics change is not done until a scene runs clean: zero left the world, zero starts violated, and the scenario's own verdict PASSing. See
author-scenario for writing the verdict.
For raycast rover wheels, check the Cylinder axle and lunco:wheel:headingAxis
together: travel is steering-axis cross axle-axis, transformed by the physical
mount. Collinear axes are invalid. Align front/rear station names and the
Modelica drivetrain forward_yaw_offset with that travel direction; native
navigation uses −Z at zero offset, +X uses −π/2. Verify departure physically.
© LunCoSim, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in skills/author-usd-physics of LunCoSim/lunco-sim.
Open the folder on GitHubat commit 43f1301
Author Usd Physics next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Author Usd Physics this skillLunCoSim/lunco-sim | 107 | — | ~9.7k | Automated safety check: Pass | Apache-2.0 | |
| Hermes Agent Skill AuthoringNousResearch/hermes-agent | 252k | — | ~3.6k | Automated safety check: Pass | MIT | |
| Diagnose Gatewayopenclaw/openclaw | 392k | — | ~670 | Automated safety check: Pass | MIT | |
| Configuring Oauth2 Authorization Flowmukul975/Anthropic-Cybersecurity-Skills | 34k | — | ~1.7k | Automated safety check: Pass | Apache-2.0 | |
| Authoring Skillsvercel/next.js | 143k | — | ~1k | Automated safety check: Pass | MIT | |
| Detecting Broken Object Property Level Authorizationmukul975/Anthropic-Cybersecurity-Skills | 34k | — | ~4k | Automated safety check: Pass | Apache-2.0 |
NousResearch/hermes-agent
Author in-repo SKILL.md files: frontmatter and structure. An agent skill from NousResearch/hermes-agent.
openclaw/openclaw
Diagnose Gateway, config, secrets, channels, and port failures with read-only one-liners.
mukul975/Anthropic-Cybersecurity-Skills
Configures secure OAuth 2.0 authorization flows, including Authorization Code with PKCE, Client Credentials, and Device Authorization Grant, covering flow selection, PKCE implementation, token…
vercel/next.js
How to create and maintain agent skills in .agents/skills/. An agent skill from vercel/next.js.
mukul975/Anthropic-Cybersecurity-Skills
Detect and test for OWASP API3:2023 Broken Object Property Level Authorization (BOPLA), covering excessive data exposure in API responses and mass assignment via injected request-body properties.
github/awesome-copilot
Perform a systematic diagnostic scan of an AI workflow across 5 quality dimensions — prompt quality, context efficiency, tool health, architecture fitness, and safety — producing a scored report…
LunCoSim/lunco-sim
Generate concise LunCoSim nightly GitHub release notes with platform downloads, installation guidance, an AI-agent mission prompt, and a changelog link.
LunCoSim/lunco-sim
Build or repair a reusable scene component through a live LunCoSim Editor session.
LunCoSim/lunco-sim
Build or review a componentized LunCoSim USD assembly with a realistic, dimensionally checkable presentation.
LunCoSim/lunco-sim
Author and review LunCoSim behavioral, asset-backed, component, mission, visual, and requirements-verification tests.
LunCoSim/lunco-sim
Create, extend, register, or debug a reusable LunCoSim Rhai tool library for live USD authoring, component linting, inspection, or test support.
LunCoSim/lunco-sim
Author an interactive tutorial, guided lesson, onboarding flow, coach-mark tour, or objectives checklist in LunCoSim.
Author or diagnose LunCoSim USD physics: rigid bodies, colliders, joints, joint frames, drives, gravity, collision filtering, and scene teardown. Author Usd Physics is an agent skill from LunCoSim/lunco-sim. Author or diagnose LunCoSim USD physics: rigid bodies, colliders, joints, joint frames, drives, gravity, collision filtering, and scene teardown.
Author Usd Physics fits situations like: exploding vehicles; scene-owned physics state.
Run `npx skills add LunCoSim/lunco-sim --skill author-usd-physics -a claude-code`. Or copy the skill folder (skills/author-usd-physics in LunCoSim/lunco-sim) into .claude/skills/author-usd-physics in your project. Claude Code loads it when a task matches its description.
Run `npx skills add LunCoSim/lunco-sim --skill author-usd-physics -a codex`. Or copy the skill folder (skills/author-usd-physics in LunCoSim/lunco-sim) into .agents/skills/author-usd-physics in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add LunCoSim/lunco-sim --skill author-usd-physics -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/author-usd-physics, .gemini/skills/author-usd-physics, .github/skills/author-usd-physics and .opencode/skills/author-usd-physics in your project.
Going by SKILL.md and its folder, Author Usd Physics needs the command-line tools its instructions call (kind).
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Author Usd Physics is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 9.7k tokens (SKILL.md is roughly 39k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Author Usd Physics: Hermes Agent Skill Authoring (NousResearch/hermes-agent, 252k stars), Diagnose Gateway (openclaw/openclaw, 392k stars), Configuring Oauth2 Authorization Flow (mukul975/Anthropic-Cybersecurity-Skills, 34k stars) and Authoring Skills (vercel/next.js, 143k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
LunCoSim (a GitHub organization) maintains it in LunCoSim/lunco-sim, which has 107 GitHub stars. The repository holds 40 skills in this directory. The repository was last updated on October 10, 2026.
Source: LunCoSim/lunco-sim on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.