/ia-cpp-systems
Modern C++ patterns: RAII and ownership, rule of zero/five, exceptions and error handling, API and ABI boundaries, templates, and CMake tooling. Use when writing, reviewing, refactoring, or debugging C++, working with smart pointers, move semantics, memory leaks, template
Claude Code
$ npx -y skills add iliaal/whetstone --skill ia-cpp-systems --agent claude-code
Codex
$ npx skills add iliaal/whetstone --skill ia-cpp-systems -a codex -y
Cursor
$ npx skills add iliaal/whetstone --skill ia-cpp-systems -a cursor -y
Gemini CLI
$ npx skills add iliaal/whetstone --skill ia-cpp-systems -a gemini-cli -y
GitHub Copilot
$ npx skills add iliaal/whetstone --skill ia-cpp-systems -a github-copilot -y
OpenCode
$ npx skills add iliaal/whetstone --skill ia-cpp-systems -a opencode -y
Windsurf
$ npx skills add iliaal/whetstone --skill ia-cpp-systems -a windsurf -y
SKILL.md
ia-cpp-systems.SKILL.mdname: ia-cpp-systems
class: language
description: >-
Modern C++ patterns: RAII and ownership, rule of zero/five, exceptions and
error handling, API and ABI boundaries, templates, and CMake tooling. Use when
writing, reviewing, refactoring, or debugging C++, working with smart pointers,
move semantics, memory leaks, template errors, or gtest. For plain C, see
ia-c-systems.
paths:
- "**/*.cpp"
- "**/*.hpp"
- "**/*.cc"
- "**/*.hh"
- "**/*.cxx"
- "**/*.h"
- "**/CMakeLists.txt"
- "**/*.cmake"
C++ Systems & Libraries
Covers C++17 as the baseline, with C++20 features called out where a project's standard allows them. For plain C (manual lifetimes, status enums, native extensions), see the `ia-c-systems` skill.
Working rules
- Make resource ownership explicit and use RAII for release on every exit path.
- Keep borrowed views within backing-storage lifetimes and review copy/move behavior when special members change.
- Follow the project's error model and supported language standard.
- Preserve ABI and avoid invoking unknown callbacks while holding locks or invalidatable container iterators.
Repo conventions outrank this skill
Check `CMakeLists.txt` for `CXX_STANDARD`, read `.clang-format` and `.clang-tidy`, and read two adjacent translation units before writing. Where they conflict with the rules below, they win.
The conflicts that actually happen:
| Local constraint | Consequence | |---|---| | `-fno-exceptions` | Error handling is codes or `expected`-alikes. Constructors cannot report recoverable failure, so use a fallible factory or construct a valid fallback state. `new (std::nothrow)` only where the project's OOM policy is to observe null and recover; plain `new` is fine where the policy is termination | | Standard pinned below C++17 | No `std::optional`/`string_view`/structured bindings/`if constexpr`; check before using any | | Public header is ABI-stable | No layout changes, no inline-function changes, no added virtuals: load the ABI reference | | Embedded or freestanding target | No RTTI, no dynamic allocation in hot paths, possibly no STL containers |
Ownership and RAII
Every resource has exactly one owner, and that owner is an object whose destructor releases it. A raw `new` or `delete` in application code is a defect.
- `std::unique_ptr<T>` for sole ownership. It is the default; it costs nothing over a raw pointer.
- `std::shared_ptr<T>` only where lifetime is genuinely shared and cannot be expressed as "the owner outlives the users". Reach for it third, not first.
- `std::weak_ptr<T>` to break ownership cycles. LeakSanitizer does report a cycle that is unreachable from any root, but not one still reachable from a global or other registered root, and detection varies by platform and configuration. Do not rely on the sanitizer to find these.
- Raw `T*` and `T&` mean **non-owning observation**, and are correct in that role. A parameter taking `unique_ptr` by value is announcing that it consumes ownership; one taking `T*` is announcing it does not.
- `std::span<T>` (C++20) or a pointer-plus-length pair for a borrowed contiguous range **in a new public API**, since `const std::vector<T>&` there refuses every other container. On a C++17 baseline, or for internal code whose callers all hold vectors anyway, `const std::vector<T>&` is fine and simpler.
**Rule of zero**: a class that owns nothing declares no destructor, no copy, and no move. Composing members that manage themselves gets all five special members correct for free. **Rule of five**: declaring any one of destructor, copy constructor, copy assignment, move constructor, or move assignment obliges the author to reason about all five. A user-declared destructor suppresses the implicit move operations, so a class that gained a destructor silently started deep-copying where it used to move.
Discipline
- Preserve behavior and API compatibility unless a break was requested. A public header change is a decision, not a cleanup.
- Do not introduce a template, an inheritance hierarchy, or a policy parameter for a single call site.
- `#include` what the file uses; do not rely on transitive includes from another header.
- No `using namespace` at namespace scope in a header. Fully qualify instead, or scope the `using` to a function body.
- When a constraint forces a deviation, comment at the deviation site and state the constraint.
Verify
- Build clean with `-Wall -Wextra -Wpedantic -Wshadow -Wconversion -Werror`
- `clang-tidy` reports no new findings on the diff
- Tests pass under `-fsanitize=address,undefined` with zero reports
- Any threaded code touched by the change exercised under TSan with zero reports; the warning bundle above does not catch lock misuse
- `clang-format --dry-run --Werror` produces no diff
- No new raw `new`/`delete`, no new `shared_ptr` where `unique_ptr` suffices
- Any class that gained a destructor has its move operations reviewed
Task-specific references
Read the relevant reference before implementing or reviewing the matching behavior:
- For constness, errors, API/ABI changes, templates, or standard-library behavior: [value-and-interface-design.md](./references/value-and-interface-design.md).
- For threading, compiler setup, tests, or build analysis: [concurrency-and-verification.md](./references/concurrency-and-verification.md).
Existing specialized references, when the corresponding topic applies:
- [api-and-abi.md](./references/api-and-abi.md).
- [cmake-and-tooling.md](./references/cmake-and-tooling.md).
- [legibility-standard.md](./references/legibility-standard.md).
Read more
name: ia-cpp-systems class: language description: >- Modern C++ patterns: RAII and ownership, rule of zero/five, exceptions and error handling, API and ABI boundaries, templates, and CMake tooling. Use when writing, reviewing, refactoring, or debugging C++, working with smart pointers, move semantics, memory leaks, template errors, or gtest. For plain C, see ia-c-systems. paths: - "**/*.cpp" - "**/*.hpp" - "**/*.cc" - "**/*.hh" - "**/*.cxx" - "**/*.h" - "**/CMakeLists.txt" - "**/*.cmake"
C++ Systems & Libraries
Covers C++17 as the baseline, with C++20 features called out where a project's standard allows them. For plain C (manual lifetimes, status enums, native extensions), see the `ia-c-systems` skill.
Working rules
- Make resource ownership explicit and use RAII for release on every exit path.
- Keep borrowed views within backing-storage lifetimes and review copy/move behavior when special members change.
- Follow the project's error model and supported language standard.
- Preserve ABI and avoid invoking unknown callbacks while holding locks or invalidatable container iterators.
Repo conventions outrank this skill
Check `CMakeLists.txt` for `CXX_STANDARD`, read `.clang-format` and `.clang-tidy`, and read two adjacent translation units before writing. Where they conflict with the rules below, they win.
The conflicts that actually happen:
| Local constraint | Consequence | |---|---| | `-fno-exceptions` | Error handling is codes or `expected`-alikes. Constructors cannot report recoverable failure, so use a fallible factory or construct a valid fallback state. `new (std::nothrow)` only where the project's OOM policy is to observe null and recover; plain `new` is fine where the policy is termination | | Standard pinned below C++17 | No `std::optional`/`string_view`/structured bindings/`if constexpr`; check before using any | | Public header is ABI-stable | No layout changes, no inline-function changes, no added virtuals: load the ABI reference | | Embedded or freestanding target | No RTTI, no dynamic allocation in hot paths, possibly no STL containers |
Ownership and RAII
Every resource has exactly one owner, and that owner is an object whose destructor releases it. A raw `new` or `delete` in application code is a defect.
- `std::unique_ptr<T>` for sole ownership. It is the default; it costs nothing over a raw pointer.
- `std::shared_ptr<T>` only where lifetime is genuinely shared and cannot be expressed as "the owner outlives the users". Reach for it third, not first.
- `std::weak_ptr<T>` to break ownership cycles. LeakSanitizer does report a cycle that is unreachable from any root, but not one still reachable from a global or other registered root, and detection varies by platform and configuration. Do not rely on the sanitizer to find these.
- Raw `T*` and `T&` mean **non-owning observation**, and are correct in that role. A parameter taking `unique_ptr` by value is announcing that it consumes ownership; one taking `T*` is announcing it does not.
- `std::span<T>` (C++20) or a pointer-plus-length pair for a borrowed contiguous range **in a new public API**, since `const std::vector<T>&` there refuses every other container. On a C++17 baseline, or for internal code whose callers all hold vectors anyway, `const std::vector<T>&` is fine and simpler.
**Rule of zero**: a class that owns nothing declares no destructor, no copy, and no move. Composing members that manage themselves gets all five special members correct for free. **Rule of five**: declaring any one of destructor, copy constructor, copy assignment, move constructor, or move assignment obliges the author to reason about all five. A user-declared destructor suppresses the implicit move operations, so a class that gained a destructor silently started deep-copying where it used to move.
Discipline
- Preserve behavior and API compatibility unless a break was requested. A public header change is a decision, not a cleanup.
- Do not introduce a template, an inheritance hierarchy, or a policy parameter for a single call site.
- `#include` what the file uses; do not rely on transitive includes from another header.
- No `using namespace` at namespace scope in a header. Fully qualify instead, or scope the `using` to a function body.
- When a constraint forces a deviation, comment at the deviation site and state the constraint.
Verify
- Build clean with `-Wall -Wextra -Wpedantic -Wshadow -Wconversion -Werror`
- `clang-tidy` reports no new findings on the diff
- Tests pass under `-fsanitize=address,undefined` with zero reports
- Any threaded code touched by the change exercised under TSan with zero reports; the warning bundle above does not catch lock misuse
- `clang-format --dry-run --Werror` produces no diff
- No new raw `new`/`delete`, no new `shared_ptr` where `unique_ptr` suffices
- Any class that gained a destructor has its move operations reviewed
Task-specific references
Read the relevant reference before implementing or reviewing the matching behavior:
- For constness, errors, API/ABI changes, templates, or standard-library behavior: [value-and-interface-design.md](./references/value-and-interface-design.md).
- For threading, compiler setup, tests, or build analysis: [concurrency-and-verification.md](./references/concurrency-and-verification.md).
Existing specialized references, when the corresponding topic applies:
- [api-and-abi.md](./references/api-and-abi.md).
- [cmake-and-tooling.md](./references/cmake-and-tooling.md).
- [legibility-standard.md](./references/legibility-standard.md).
A Claude Code plugin that makes AI coding agents follow engineering discipline. Plan before coding. Verify before claiming done. Find root cause before patching. Review before merge. Skills activate based on file type and task signals, not manual toggling.
Other skills on whetstone.
More skills from other plugins.
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
ask-matt
Ask which skill or flow fits your situation. A router over the skills in this repo.
Questions about ia-cpp-systems.
- Which coding agents can use ia-cpp-systems?
- ia-cpp-systems installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.

