Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Concise reference for the gz-sim Entity-Component-System architecture — how Entities, Components, Systems, the ECM, the Server, and SimulationRunner fit together. Trigger when the user asks how gz-sim is organized, where to add code, or how the simulation loop runs.
.claude/skills/harunkurtdev-gz-ecs-overview/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 11% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 24% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 13% | 0% |
| case-07 | ✗→✓ | ▲ Improved | -23% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 8% | 0% |
┌──────────────────────────────────────────┐
│ Server │
│ (one process, owns N runners) │
└──────┬───────────────────────────────────┘
│
┌──────────▼──────────────────────────┐
│ SimulationRunner │
│ - drives the iteration loop │
│ - owns the EventManager │
│ - owns the EntityComponentManager │
│ - owns the System list │
└──────────┬──────────────────────────┘
│ every step
┌───────────────────┼───────────────────────────────────────┐
▼ ▼ ▼ ▼
Configure* PreUpdate* Update* PostUpdate*
(once on load) (mutates ECM) (mutates ECM) (read-only)
└── Reset hooks on /reset| Type | Header | Role | |------|--------|------| | Entity | Entity.hh | Just a uint64_t ID. Means nothing on its own. | | components::Component<T, id> | components/Component.hh | Strongly-typed data attached to an Entity. | | EntityComponentManager | EntityComponentManager.hh | The world's database. Add/remove/query components. | | EventManager | EventManager.hh | Pub/sub for cross-system events. | | System (ISystemConfigure, ISystemPreUpdate, ISystemUpdate, ISystemPostUpdate, ISystemReset) | System.hh | Behaviour. Plugins implement one or more of these. | | SimulationRunner | (internal) | Owns and drives the above. | | Server | Server.hh | Public façade — Run(), SetUpdatePeriod(), etc. |
Configure() — once on plugin load. Read SDF, cache handles, registertopics. Do not mutate the world here unless you're spawning fixtures.
PreUpdate() — every iteration, before physics. Apply commands(forces, joint targets), spawn entities.
Update() — every iteration, alongside physics. Most user code doesnot belong here.
PostUpdate() — every iteration, after physics. Read-only ECMaccess — emit telemetry, publish state, write logs. The ECM is const here for a reason.
Reset() — when the world resets (e.g. via /world/<name>/control).Restore any internal state cached in the system.
A common pattern in gz-sim:
<X> — current value (e.g. JointPosition).<X>Cmd — request from a user system, consumed and cleared inPreUpdate by the physics-coupled system.
<X>Reset — value to apply at the next world reset.Look at components/JointPositionReset.hh / JointVelocityCmd.hh for the canonical examples.
Prefer Each<> views — they're cached and re-used across iterations:
cpp_ecm.Each<components::Joint, components::JointVelocity>( [&](const Entity &_ent, const components::Joint *, const components::JointVelocity *_vel) -> bool { // do work with *_vel return true; // keep iterating });
EachNew, EachRemoved, EachChanged exist for change-detection passes.
include/gz/sim/components/<Name>.hh(header-only). Register serialization in src/ComponentFactory.cc when you want the component to survive log replay / save.
src/systems/<snake_case>/<CamelCase>.{hh,cc} plusan entry in src/systems/CMakeLists.txt. Use the new-system skill to scaffold.
Model, Link, Joint, Light, Actor,Sensor (include/gz/sim/). These wrap an Entity plus ECM accessors and are usually how user code touches a model.
PreUpdate / Update / PostUpdate of different systems runsequentially inside one runner iteration — not in parallel.
Server::Run(true, n) blocks; Run(false, n) runs the loop on a workerthread. Multi-runner setups exist (different worlds), each on its own thread.
Update(). Spawn a thread in Configure,communicate via lock-free queues, drain in PreUpdate.
If you only have time for two files, read:
include/gz/sim/EntityComponentManager.hh — every comment matters.include/gz/sim/System.hh — the interfaces are tiny and tell youexactly what each phase guarantees.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-14 | pass→pass | 13,590 | 10,129 | -25% | 1 | 1 | 0% | 2,233 | 2,836 | +27% | 0 | 0 | — |
case-01 | fail→pass | 20,252 | 15,281 | -25% | 1 | 1 | 0% | 3,650 | 4,066 | +11% | 0 | 0 | — |
case-02 | fail→pass | 19,968 | 15,572 | -22% | 1 | 1 | 0% | 3,268 | 4,038 | +24% | 0 | 0 | — |
case-03 | fail→pass | 25,020 | 21,563 | -14% | 1 | 1 | 0% | 4,598 | 5,193 | +13% | 0 | 0 | — |
case-04 | pass→fail | 12,763 | 13,871 | +9% | 1 | 1 | 0% | 2,123 | 3,533 | +66% | 0 | 0 | — |
case-09 | pass→pass | 7,455 | 3,224 | -57% | 1 | 1 | 0% | 1,402 | 1,749 | +25% | 0 | 0 | — |
case-05 | pass→pass | 12,916 | 11,001 | -15% | 1 | 1 | 0% | 2,185 | 2,980 | +36% | 0 | 0 | — |
case-06 | pass→pass | 6,436 | 3,530 | -45% | 1 | 1 | 0% | 1,114 | 1,737 | +56% | 0 | 0 | — |
case-07 | fail→pass | 17,323 | 6,501 | -62% | 1 | 1 | 0% | 3,081 | 2,372 | -23% | 0 | 0 | — |
case-08 | fail→pass | 13,530 | 6,461 | -52% | 1 | 1 | 0% | 2,109 | 2,281 | +8% | 0 | 0 | — |
case-10 | pass→pass | 13,880 | 8,493 | -39% | 1 | 1 | 0% | 2,378 | 2,774 | +17% | 0 | 0 | — |
case-11 | pass→pass | 11,395 | 7,385 | -35% | 1 | 1 | 0% | 1,935 | 2,402 | +24% | 0 | 0 | — |
case-12 | fail→pass | 9,711 | 4,947 | -49% | 1 | 1 | 0% | 1,744 | 2,055 | +18% | 0 | 0 | — |
case-13 | pass→pass | 7,006 | 3,479 | -50% | 1 | 1 | 0% | 1,205 | 1,828 | +52% | 0 | 0 | — |
case-15 | pass→pass | 12,829 | 4,965 | -61% | 1 | 1 | 0% | 2,061 | 2,042 | -1% | 0 | 0 | — |
case-16 | pass→pass | 14,297 | 12,534 | -12% | 1 | 1 | 0% | 2,380 | 3,479 | +46% | 0 | 0 | — |
case-17 | pass→pass | 3,433 | 1,462 | -57% | 1 | 1 | 0% | 552 | 1,423 | +158% | 0 | 0 | — |
case-18 | pass→pass | 12,814 | 5,964 | -53% | 1 | 1 | 0% | 2,108 | 2,115 | +0% | 0 | 0 | — |
case-19 | pass→pass | 9,157 | 4,852 | -47% | 1 | 1 | 0% | 1,421 | 2,001 | +41% | 0 | 0 | — |
case-20 | pass→pass | 9,811 | 4,892 | -50% | 1 | 1 | 0% | 1,851 | 2,059 | +11% | 0 | 0 | — |
case-21 | pass→pass | 10,115 | 8,109 | -20% | 1 | 1 | 0% | 1,911 | 2,830 | +48% | 0 | 0 | — |
case-22 | pass→pass | 22,837 | 8,983 | -61% | 1 | 1 | 0% | 2,073 | 2,901 | +40% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +23 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.