Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Scaffold a new gz-sim system plugin under src/systems/<name>/ — header, source, CMake glue, plugin registration, and an integration test stub. Trigger when the user asks to create a new system, controller, or plugin that hooks into the simulation loop.
.claude/skills/harunkurtdev-new-system/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-10 | ✗→✓ | ▲ Improved | 3% | 0% |
| case-01 | ✗→✓ | ▲ Improved | -2% | 0% |
| case-02 | ✗→✓ | ▲ Improved | -20% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 39% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 4% | 0% |
A system in gz-sim is a shared library that implements one or more of ISystemConfigure, ISystemPreUpdate, ISystemUpdate, ISystemPostUpdate, ISystemReset and gets loaded via SDF <plugin> or <server_config>.
Directory convention: src/systems/<snake_case>/<CamelCase>.{hh,cc}.
snake_case matching the plugin's purpose (e.g.fancy_drive, wheel_slip).
CamelCase matching the directory (e.g. FancyDrive).gz::sim::systems::<CamelCase>.FancyDrive.hh)cpp#ifndef GZ_SIM_SYSTEMS_FANCYDRIVE_HH_ #define GZ_SIM_SYSTEMS_FANCYDRIVE_HH_ #include <memory> #include <gz/sim/System.hh> #include <gz/sim/config.hh> #include <gz/utils/ImplPtr.hh> namespace gz::sim { inline namespace GZ_SIM_VERSION_NAMESPACE { namespace systems { class FancyDrivePrivate; /// \brief One-paragraph description of what this system does. /// /// SDF parameters: /// <param_a> — what it does (default: ...) /// <param_b> — what it does (default: ...) class FancyDrive : public System, public ISystemConfigure, public ISystemPreUpdate, public ISystemPostUpdate { public: FancyDrive(); public: ~FancyDrive() override; // System hooks public: void Configure(const Entity &_entity, const std::shared_ptr<const sdf::Element> &_sdf, EntityComponentManager &_ecm, EventManager &_eventMgr) override; public: void PreUpdate(const UpdateInfo &_info, EntityComponentManager &_ecm) override; public: void PostUpdate(const UpdateInfo &_info, const EntityComponentManager &_ecm) override; /// \brief Private data via PIMPL. GZ_UTILS_UNIQUE_IMPL_PTR(dataPtr) }; } } } #endif
FancyDrive.cc)cpp#include "FancyDrive.hh" #include <gz/plugin/Register.hh> #include <gz/sim/Model.hh> #include <gz/sim/components/JointVelocityCmd.hh> namespace gz::sim::systems { class FancyDrivePrivate { public: Model model{kNullEntity}; // …cached handles, sdf params, transport node, etc. }; FancyDrive::FancyDrive() : dataPtr(gz::utils::MakeUniqueImpl<FancyDrivePrivate>()) { } FancyDrive::~FancyDrive() = default; void FancyDrive::Configure(const Entity &_entity, const std::shared_ptr<const sdf::Element> &_sdf, EntityComponentManager &_ecm, EventManager &/*_eventMgr*/) { this->dataPtr->model = Model(_entity); if (!this->dataPtr->model.Valid(_ecm)) { gzerr << "FancyDrive should be attached to a model entity. " << "Failed to initialize." << std::endl; return; } // parse _sdf params here… } void FancyDrive::PreUpdate(const UpdateInfo &_info, EntityComponentManager &_ecm) { if (_info.paused) return; // mutate _ecm: set joint velocity cmds, etc. } void FancyDrive::PostUpdate(const UpdateInfo &_info, const EntityComponentManager &_ecm) { // read-only — publish telemetry, log, etc. } } // namespace gz::sim::systems GZ_ADD_PLUGIN(gz::sim::systems::FancyDrive, gz::sim::System, gz::sim::systems::FancyDrive::ISystemConfigure, gz::sim::systems::FancyDrive::ISystemPreUpdate, gz::sim::systems::FancyDrive::ISystemPostUpdate) GZ_ADD_PLUGIN_ALIAS(gz::sim::systems::FancyDrive, "gz::sim::systems::FancyDrive")
The alias is what users actually type in their <plugin filename="..." name="gz::sim::systems::FancyDrive"> tags — keep it stable across releases.
src/systems/fancy_drive/CMakeLists.txt)cmakegz_add_system(fancy-drive SOURCES FancyDrive.cc PUBLIC_LINK_LIBS gz-common${GZ_COMMON_VER}::gz-common${GZ_COMMON_VER} gz-math${GZ_MATH_VER}::gz-math${GZ_MATH_VER} gz-transport${GZ_TRANSPORT_VER}::gz-transport${GZ_TRANSPORT_VER} )
Use existing sibling directories as the source of truth — the macro name and link-libs list differs by feature set.
src/systems/CMakeLists.txt)Add an add_subdirectory(fancy_drive) entry alphabetically.
test/integration/fancy_drive_system.cc)Minimum viable test:
cpp#include <gtest/gtest.h> #include <gz/sim/Server.hh> #include <gz/sim/TestFixture.hh> TEST(FancyDriveTest, LoadsCleanly) { gz::sim::TestFixture fixture("test/worlds/fancy_drive.sdf"); fixture.Server()->Run(true, 100, false); // assertions on telemetry / final pose }
Add the target in test/integration/CMakeLists.txt.
Add examples/worlds/fancy_drive.sdf with the minimum SDF a user needs to try the system, and reference it from a short tutorial under tutorials/.
Migration.mdIf the system is publicly named or replaces an older one, append a note to Migration.md.
pre-commit run --files <changed files> before declaring done.Changelog.md under the upcoming-release section.| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-10 | fail→pass | 12,038 | 3,363 | -72% | 1 | 1 | 0% | 2,123 | 2,194 | +3% | 0 | 0 | — |
case-01 | fail→pass | 25,576 | 16,992 | -34% | 1 | 1 | 0% | 5,468 | 5,363 | -2% | 0 | 0 | — |
case-02 | fail→pass | 27,219 | 13,678 | -50% | 1 | 1 | 0% | 5,774 | 4,607 | -20% | 0 | 0 | — |
case-03 | fail→pass | 18,737 | 16,414 | -12% | 1 | 1 | 0% | 3,585 | 4,999 | +39% | 0 | 0 | — |
case-04 | pass→pass | 13,330 | 4,389 | -67% | 1 | 1 | 0% | 2,380 | 2,494 | +5% | 0 | 0 | — |
case-05 | fail→pass | 13,491 | 5,423 | -60% | 1 | 1 | 0% | 2,514 | 2,621 | +4% | 0 | 0 | — |
case-06 | pass→pass | 9,524 | 4,241 | -55% | 1 | 1 | 0% | 1,784 | 2,359 | +32% | 0 | 0 | — |
case-07 | pass→pass | 9,147 | 4,145 | -55% | 1 | 1 | 0% | 1,652 | 2,338 | +42% | 0 | 0 | — |
case-08 | pass→pass | 14,416 | 6,242 | -57% | 1 | 1 | 0% | 2,334 | 2,521 | +8% | 0 | 0 | — |
case-09 | pass→pass | 8,131 | 3,801 | -53% | 1 | 1 | 0% | 1,524 | 2,330 | +53% | 0 | 0 | — |
case-11 | pass→pass | 5,686 | 1,659 | -71% | 1 | 1 | 0% | 997 | 1,823 | +83% | 0 | 0 | — |
case-12 | fail→pass | 7,969 | 2,709 | -66% | 1 | 1 | 0% | 1,436 | 2,054 | +43% | 0 | 0 | — |
case-13 | pass→pass | 5,796 | 3,133 | -46% | 1 | 1 | 0% | 935 | 2,047 | +119% | 0 | 0 | — |
case-14 | fail→fail | 8,411 | 2,840 | -66% | 1 | 1 | 0% | 1,392 | 1,977 | +42% | 0 | 0 | — |
case-15 | fail→pass | 14,577 | 1,791 | -88% | 1 | 1 | 0% | 2,421 | 1,799 | -26% | 0 | 0 | — |
case-16 | fail→pass | 4,377 | 2,505 | -43% | 1 | 1 | 0% | 653 | 1,936 | +196% | 0 | 0 | — |
case-17 | pass→pass | 13,237 | 4,693 | -65% | 1 | 1 | 0% | 2,313 | 2,396 | +4% | 0 | 0 | — |
case-18 | fail→pass | 10,073 | 5,403 | -46% | 1 | 1 | 0% | 1,826 | 2,519 | +38% | 0 | 0 | — |
case-19 | pass→pass | 12,627 | 9,256 | -27% | 1 | 1 | 0% | 1,998 | 2,918 | +46% | 0 | 0 | — |
case-20 | pass→pass | 13,235 | 10,710 | -19% | 1 | 1 | 0% | 2,709 | 3,809 | +41% | 0 | 0 | — |
case-21 | pass→pass | 23,519 | 20,251 | -14% | 1 | 1 | 0% | 4,978 | 5,944 | +19% | 0 | 0 | — |
case-22 | pass→pass | 19,350 | 17,713 | -8% | 1 | 1 | 0% | 3,438 | 4,845 | +41% | 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 +41 percentage points is the difference between those two pass rates over the 22 comparable cases.
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.