@mrciphersmith/keryx 0.3.2 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/dist/cli.js +4634 -2445
  2. package/dist/core.js +66 -10
  3. package/package.json +1 -1
  4. package/src/gdskills/bundled/install-manifest.json +349 -2
  5. package/src/gdskills/bundled/rules/core/model-selection.mdc +18 -0
  6. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.md +1 -1
  7. package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.md +1 -1
  8. package/src/gdskills/bundled/skills/planning/interviewer/SKILL.md +1 -1
  9. package/src/gdskills/bundled/skills/quality/deploy/SKILL.md +1 -1
  10. package/src/gdskills/bundled/skills/review/review-jev-contract/SKILL.md +193 -0
  11. package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.detail.md +81 -21
  12. package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.md +4 -4
  13. package/src/gdskills/bundled/stacks/c-cpp/agent-refs.json +4 -0
  14. package/src/gdskills/bundled/stacks/c-cpp/governance/eval.json +1777 -0
  15. package/src/gdskills/bundled/stacks/c-cpp/governance/scout.json +31 -0
  16. package/src/gdskills/bundled/stacks/c-cpp/pack.json +42 -0
  17. package/src/gdskills/bundled/stacks/c-cpp/rules/coding-style.mdc +80 -0
  18. package/src/gdskills/bundled/stacks/c-cpp/rules/patterns.mdc +87 -0
  19. package/src/gdskills/bundled/stacks/c-cpp/rules/security.mdc +90 -0
  20. package/src/gdskills/bundled/stacks/c-cpp/rules/testing.mdc +83 -0
  21. package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-build-fix/SKILL.md +153 -0
  22. package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-build-fix/evals.json +74 -0
  23. package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-code-review/SKILL.md +132 -0
  24. package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-code-review/evals.json +73 -0
  25. package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-implementation/SKILL.md +151 -0
  26. package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-implementation/evals.json +74 -0
  27. package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-testing/SKILL.md +152 -0
  28. package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-testing/evals.json +74 -0
  29. package/src/gdskills/bundled/stacks/ci-github-gitlab/agent-refs.json +4 -0
  30. package/src/gdskills/bundled/stacks/ci-github-gitlab/governance/eval.json +1295 -0
  31. package/src/gdskills/bundled/stacks/ci-github-gitlab/governance/scout.json +26 -0
  32. package/src/gdskills/bundled/stacks/ci-github-gitlab/pack.json +41 -0
  33. package/src/gdskills/bundled/stacks/ci-github-gitlab/rules/patterns.mdc +77 -0
  34. package/src/gdskills/bundled/stacks/ci-github-gitlab/rules/security.mdc +144 -0
  35. package/src/gdskills/bundled/stacks/ci-github-gitlab/skills/ci-pipeline-build-fix/SKILL.md +121 -0
  36. package/src/gdskills/bundled/stacks/ci-github-gitlab/skills/ci-pipeline-build-fix/evals.json +73 -0
  37. package/src/gdskills/bundled/stacks/ci-github-gitlab/skills/ci-pipeline-code-review/SKILL.md +139 -0
  38. package/src/gdskills/bundled/stacks/ci-github-gitlab/skills/ci-pipeline-code-review/evals.json +73 -0
  39. package/src/gdskills/bundled/stacks/ci-github-gitlab/skills/ci-pipeline-implementation/SKILL.md +147 -0
  40. package/src/gdskills/bundled/stacks/ci-github-gitlab/skills/ci-pipeline-implementation/evals.json +74 -0
  41. package/src/gdskills/bundled/stacks/docker-k8s-terraform/agent-refs.json +4 -0
  42. package/src/gdskills/bundled/stacks/docker-k8s-terraform/governance/eval.json +865 -0
  43. package/src/gdskills/bundled/stacks/docker-k8s-terraform/governance/scout.json +16 -0
  44. package/src/gdskills/bundled/stacks/docker-k8s-terraform/pack.json +46 -0
  45. package/src/gdskills/bundled/stacks/docker-k8s-terraform/rules/coding-style.mdc +74 -0
  46. package/src/gdskills/bundled/stacks/docker-k8s-terraform/rules/patterns.mdc +81 -0
  47. package/src/gdskills/bundled/stacks/docker-k8s-terraform/rules/security.mdc +146 -0
  48. package/src/gdskills/bundled/stacks/docker-k8s-terraform/rules/testing.mdc +61 -0
  49. package/src/gdskills/bundled/stacks/docker-k8s-terraform/skills/docker-k8s-terraform-build-fix/SKILL.md +151 -0
  50. package/src/gdskills/bundled/stacks/docker-k8s-terraform/skills/docker-k8s-terraform-build-fix/evals.json +74 -0
  51. package/src/gdskills/bundled/stacks/docker-k8s-terraform/skills/docker-k8s-terraform-review/SKILL.md +135 -0
  52. package/src/gdskills/bundled/stacks/docker-k8s-terraform/skills/docker-k8s-terraform-review/evals.json +76 -0
  53. package/src/gdskills/bundled/stacks/php-laravel/agent-refs.json +4 -0
  54. package/src/gdskills/bundled/stacks/php-laravel/governance/eval.json +1829 -0
  55. package/src/gdskills/bundled/stacks/php-laravel/governance/scout.json +33 -0
  56. package/src/gdskills/bundled/stacks/php-laravel/pack.json +41 -0
  57. package/src/gdskills/bundled/stacks/php-laravel/rules/coding-style.mdc +82 -0
  58. package/src/gdskills/bundled/stacks/php-laravel/rules/patterns.mdc +80 -0
  59. package/src/gdskills/bundled/stacks/php-laravel/rules/security.mdc +80 -0
  60. package/src/gdskills/bundled/stacks/php-laravel/rules/testing.mdc +82 -0
  61. package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-build-fix/SKILL.md +143 -0
  62. package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-build-fix/evals.json +74 -0
  63. package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-code-review/SKILL.md +126 -0
  64. package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-code-review/evals.json +76 -0
  65. package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-implementation/SKILL.md +140 -0
  66. package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-implementation/evals.json +75 -0
  67. package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-testing/SKILL.md +124 -0
  68. package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-testing/evals.json +74 -0
  69. package/src/gdskills/bundled/stacks/ruby-rails/agent-refs.json +4 -0
  70. package/src/gdskills/bundled/stacks/ruby-rails/governance/eval.json +1673 -0
  71. package/src/gdskills/bundled/stacks/ruby-rails/governance/scout.json +33 -0
  72. package/src/gdskills/bundled/stacks/ruby-rails/pack.json +42 -0
  73. package/src/gdskills/bundled/stacks/ruby-rails/rules/coding-style.mdc +69 -0
  74. package/src/gdskills/bundled/stacks/ruby-rails/rules/patterns.mdc +93 -0
  75. package/src/gdskills/bundled/stacks/ruby-rails/rules/security.mdc +90 -0
  76. package/src/gdskills/bundled/stacks/ruby-rails/rules/testing.mdc +89 -0
  77. package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-build-fix/SKILL.md +143 -0
  78. package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-build-fix/evals.json +73 -0
  79. package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-code-review/SKILL.md +134 -0
  80. package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-code-review/evals.json +71 -0
  81. package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-implementation/SKILL.md +141 -0
  82. package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-implementation/evals.json +72 -0
  83. package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-testing/SKILL.md +125 -0
  84. package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-testing/evals.json +72 -0
  85. package/src/gdskills/bundled/stacks/sql-db/agent-refs.json +4 -0
  86. package/src/gdskills/bundled/stacks/sql-db/governance/eval.json +1829 -0
  87. package/src/gdskills/bundled/stacks/sql-db/governance/scout.json +30 -0
  88. package/src/gdskills/bundled/stacks/sql-db/pack.json +40 -0
  89. package/src/gdskills/bundled/stacks/sql-db/rules/coding-style.mdc +69 -0
  90. package/src/gdskills/bundled/stacks/sql-db/rules/patterns.mdc +134 -0
  91. package/src/gdskills/bundled/stacks/sql-db/rules/security.mdc +74 -0
  92. package/src/gdskills/bundled/stacks/sql-db/rules/testing.mdc +83 -0
  93. package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-build-fix/SKILL.md +147 -0
  94. package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-build-fix/evals.json +72 -0
  95. package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-code-review/SKILL.md +132 -0
  96. package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-code-review/evals.json +73 -0
  97. package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-implementation/SKILL.md +153 -0
  98. package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-implementation/evals.json +77 -0
  99. package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-testing/SKILL.md +129 -0
  100. package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-testing/evals.json +73 -0
@@ -0,0 +1,31 @@
1
+ [
2
+ {
3
+ "query": "Use when implementing or extending a feature in C or modern C++ (17/20/23) -- ownership and RAII, smart pointer choice, move semantics, std::span, manual malloc/free lifetime in C, and choosing safe standard-library APIs over unsafe ones.",
4
+ "decision": "create",
5
+ "topMatch": "python/python-implementation",
6
+ "recordedAt": "2026-09-25T15:29:36.971Z",
7
+ "skillName": "c-cpp-implementation"
8
+ },
9
+ {
10
+ "query": "Use when reviewing a C or C++ change for memory-safety and undefined-behavior risks -- use-after-free, double-free, buffer overflows, dangling references, iterator invalidation, unchecked allocations, signed overflow, and unsynchronized shared state. Read-only, no edits.",
11
+ "decision": "create",
12
+ "topMatch": "c-cpp/c-cpp-build-fix",
13
+ "recordedAt": "2026-09-25T15:29:43.782Z",
14
+ "skillName": "c-cpp-code-review"
15
+ },
16
+ {
17
+ "query": "Use when a C/C++ build fails -- CMake configure/link errors, missing headers, template/generic-instantiation errors, ABI/linker mismatches -- or when a sanitizer (ASan/UBSan/TSan) or ctest run reports a memory-safety, UB, or race failure, with the smallest root-cause fix.",
18
+ "decision": "create",
19
+ "topMatch": "go/go-build-fix",
20
+ "recordedAt": "2026-09-25T15:29:46.104Z",
21
+ "skillName": "c-cpp-build-fix"
22
+ },
23
+ {
24
+ "query": "Use when a C or C++ test suite needs writing, extending, or fixing -- GoogleTest TEST/TEST_F/TEST_P, death tests, fixture setup/teardown, and verifying a memory-safety or concurrency fix under AddressSanitizer/UndefinedBehaviorSanitizer/ThreadSanitizer.",
25
+ "decision": "fork",
26
+ "topMatch": "c-cpp/c-cpp-build-fix",
27
+ "recordedAt": "2026-09-25T15:30:02.567Z",
28
+ "skillName": "c-cpp-testing",
29
+ "justification": "Nearest match c-cpp-build-fix (0.39) shares this pack's own sanitizer vocabulary (ASan/UBSan/TSan) because both skills verify fixes under sanitizers, but they are distinct capabilities: c-cpp-testing writes/extends/fixes GoogleTest test files (TEST_F fixtures, TEST_P, death tests) and never touches source under test, while c-cpp-build-fix diagnoses and fixes compile/link/CMake failures and root-causes a sanitizer-reported bug in production source, never writing new tests. No other matched skill (go-testing, php-laravel-testing, react-testing, ruby-rails-testing, all ~0.21) covers GoogleTest/C++ test idiom or sanitizer-backed test verification at all, so this is a genuine fork of a sibling skill within the same new pack, not a substitute for an existing one."
30
+ }
31
+ ]
@@ -0,0 +1,42 @@
1
+ {
2
+ "id": "c-cpp",
3
+ "family": "language",
4
+ "modules": ["c-cpp-rules", "c-cpp-skills"],
5
+ "detectionMarkers": ["c-cpp"],
6
+ "provenance": {
7
+ "origin": "authored",
8
+ "sourceRef": "flow 337, Wave 4 batch 5"
9
+ },
10
+ "stability": "experimental",
11
+ "skills": {
12
+ "implement": ["c-cpp-implementation"],
13
+ "test": ["c-cpp-testing"],
14
+ "review": ["c-cpp-code-review"],
15
+ "build-fix": ["c-cpp-build-fix"],
16
+ "migrate": []
17
+ },
18
+ "agentProfile": {
19
+ "displayName": "C / C++",
20
+ "auditFocus": [
21
+ "raw `new`/`delete` or manual `malloc`/`free` where RAII/a smart pointer (`unique_ptr`/`shared_ptr`) already fits the ownership model",
22
+ "a pointer or reference returned to, or captured from, a local/stack-destroyed object (use-after-free / dangling reference)",
23
+ "a container iterator or pointer used after the container it points into was resized, erased from, or reallocated",
24
+ "an unchecked `malloc`/`new` return, or a missing `free`/destructor on an error/early-return path in C",
25
+ "signed integer overflow, unvalidated array/index bounds, or an out-of-bounds access on a fixed-size buffer",
26
+ "a shared variable read/written from more than one thread with no mutex/atomic guarding it",
27
+ "a memory-safety or UB bug report whose verification step never mentions AddressSanitizer/UndefinedBehaviorSanitizer/ThreadSanitizer"
28
+ ],
29
+ "buildCommands": [
30
+ "cmake --preset <configured-preset> (or cmake -S . -B build)",
31
+ "cmake --build build",
32
+ "ctest --test-dir build --output-on-failure",
33
+ "cmake -S . -B build-asan -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS='-fsanitize=address,undefined -fno-omit-frame-pointer -g' && cmake --build build-asan && ctest --test-dir build-asan"
34
+ ],
35
+ "fixGuardrails": [
36
+ "never silence a sanitizer finding by disabling the sanitizer, adding a suppression, or narrowing what it scans instead of fixing the underlying memory/UB bug",
37
+ "never replace a real bounds/null check with a cast or a suppression comment to make a warning disappear",
38
+ "never widen an unsafe C API (`strcpy`, `sprintf`, `gets`) back in while resolving an unrelated failure",
39
+ "fix the smallest root cause; do not refactor unrelated ownership or threading code while resolving a single build/test/sanitizer failure"
40
+ ]
41
+ }
42
+ }
@@ -0,0 +1,80 @@
1
+ ---
2
+ extends: common
3
+ paths: ["**/*.c", "**/*.h", "**/*.cpp", "**/*.cc", "**/*.cxx", "**/*.hpp", "**/*.hxx", "**/*.hh", "**/*.ipp", "**/*.inl", "**/*.tpp", "**/*.cppm", "**/*.ixx"]
4
+ metadata:
5
+ origin: authored
6
+ ---
7
+
8
+ # C / C++ coding style
9
+
10
+ Narrows `core-common-rules`' stack-agnostic style rules to modern C++
11
+ (17/20/23) and modern C idiom. Applies to both `.c`/`.h` and
12
+ `.cpp`/`.cc`/`.cxx`/`.hpp`/`.hxx` files — everything not C/C++-specific
13
+ still comes from the common rules this file `extends`.
14
+
15
+ ## Naming and structure
16
+
17
+ - `snake_case` for functions, variables, and namespaces; `PascalCase` for
18
+ types (classes, structs, enums); `SCREAMING_SNAKE_CASE` for macros and
19
+ compile-time constants — match whichever convention the project already
20
+ uses over this default when one is established.
21
+ - Keep a header self-contained: `#pragma once` (or a project-consistent
22
+ include guard) at the top, and every symbol the header uses actually
23
+ `#include`d in it — do not rely on an include some other header happens
24
+ to pull in transitively.
25
+ - One translation unit's private helpers go in an anonymous namespace
26
+ (C++) or are declared `static` (C) — do not give internal linkage away
27
+ by leaving a file-local helper externally visible.
28
+ - An identifier's name shrinks with its scope: a loop index is `i`, a
29
+ header-exposed public function gets a full descriptive name.
30
+
31
+ ## Modern C++ idiom (17/20/23)
32
+
33
+ - Own a heap allocation with `std::unique_ptr` by default; reach for
34
+ `std::shared_ptr` only when the ownership is genuinely shared (more than
35
+ one owner can outlive any single other owner) — a `shared_ptr` used for
36
+ single ownership just adds atomic refcount overhead and obscures who
37
+ actually owns the object.
38
+ - Prefer `std::make_unique`/`std::make_shared` over a bare `new` passed to
39
+ a smart pointer constructor — it avoids a leak on an exception thrown
40
+ between the `new` and the constructor call, and avoids a second
41
+ allocation for `shared_ptr`'s control block.
42
+ - Use `std::span` (C++20) for a non-owning view over a contiguous range
43
+ instead of a raw `(pointer, length)` pair — it carries bounds
44
+ information the raw pair does not, without owning or copying the data.
45
+ - Move, don't copy, when a value's ownership is transferring: accept
46
+ by-value and `std::move` into place, or take an rvalue-reference
47
+ overload, rather than accepting `const&` and copying when the caller's
48
+ object was about to be discarded anyway.
49
+ - Prefer a range-`for` or a `<ranges>` (C++20) pipeline over a manual
50
+ index loop or raw iterator pair when the loop is a plain traversal —
51
+ keep the manual iterator form for cases that need the iterator itself
52
+ (insertion position, mutation while iterating with an explicit
53
+ erase-returns-next-iterator pattern).
54
+ - `constexpr`/`consteval` any function or value that can be computed at
55
+ compile time; `const` (or nothing, per project convention) otherwise —
56
+ do not default to `#define` for a typed constant.
57
+
58
+ ## Modern C safety practices
59
+
60
+ - Prefer a fixed-size or length-tracked buffer API (`snprintf`, not
61
+ `sprintf`; `strncpy`/explicit length checks, not `strcpy`/`strcat`;
62
+ `fgets`, never `gets`) — every string/buffer write states or checks its
63
+ destination's capacity.
64
+ - Check every `malloc`/`calloc`/`realloc` return for `NULL` before
65
+ dereferencing it; on `realloc` failure, keep the original pointer (do
66
+ not overwrite it with the `NULL` return and leak the original block).
67
+ - Prefer C99 designated initializers and stack-based fixed arrays with an
68
+ explicit, checked bound over a raw pointer with an implicit length
69
+ carried only in a comment.
70
+ - Const-qualify a pointer parameter the function does not mutate through
71
+ (`const char *`, not `char *`) — it documents the contract and lets the
72
+ compiler catch an accidental write.
73
+
74
+ ## Formatting
75
+
76
+ - Format with the project's configured `clang-format`/`.clang-format` if
77
+ present; do not hand-format around a formatter that is already
78
+ configured.
79
+ - Braces and brace-placement follow the project's existing style over any
80
+ default preference in this file — match neighboring code.
@@ -0,0 +1,87 @@
1
+ ---
2
+ extends: common
3
+ paths: ["**/*.c", "**/*.h", "**/*.cpp", "**/*.cc", "**/*.cxx", "**/*.hpp", "**/*.hxx", "**/*.hh", "**/*.ipp", "**/*.inl", "**/*.tpp", "**/*.cppm", "**/*.ixx"]
4
+ metadata:
5
+ origin: authored
6
+ ---
7
+
8
+ # C / C++ patterns
9
+
10
+ Narrows `core-common-rules`' stack-agnostic design guidance to idiomatic
11
+ C/C++ ownership, resource management, and their common anti-patterns.
12
+ Applies to both C and C++ files.
13
+
14
+ ## RAII and ownership (C++)
15
+
16
+ - Every resource (heap memory, a file handle, a mutex lock, a socket) is
17
+ owned by exactly one object whose destructor releases it — a raw `new`
18
+ with a matching `delete` written by hand somewhere else in the function
19
+ is a bug waiting for an early return or an exception to skip the
20
+ `delete`. Wrap it in a smart pointer, `std::lock_guard`/`std::unique_lock`,
21
+ or another RAII type instead.
22
+ - A class that manually manages a resource follows the Rule of
23
+ Zero (own nothing directly — compose from types that already manage
24
+ their own resources) or, when it must own a resource directly, the Rule
25
+ of Five (copy ctor, copy assign, move ctor, move assign, destructor —
26
+ define all five, or `= delete` the ones that should not exist).
27
+ - A function that hands back a resource does so by return value (RVO/move
28
+ makes this free), not by an out-parameter raw pointer the caller must
29
+ remember to release.
30
+ - Never return a pointer or reference to a local (stack) variable, or to a
31
+ member of a temporary — the referent is destroyed when the function
32
+ returns, and the caller is left with a dangling reference.
33
+
34
+ ## Manual resource management (C)
35
+
36
+ - Pair every `malloc`/`fopen`/`pthread_mutex_lock`-style acquire with a
37
+ `free`/`fclose`/`pthread_mutex_unlock` on every exit path from the
38
+ function that acquired it, error paths included — a `goto cleanup;`
39
+ pattern with a single exit-and-release block is the idiomatic way to
40
+ keep this from being repeated (and missed) at every early `return`.
41
+ - A function that allocates and returns a pointer documents, in its
42
+ header comment, who owns the result and how it must be released — an
43
+ API with no stated ownership contract is a leak or a double-free
44
+ waiting to happen at the call site.
45
+ - Never call `free` on a pointer twice, and set a pointer to `NULL`
46
+ immediately after freeing it when it could otherwise be read or freed
47
+ again on a later path — a double-free and a use-after-free are both
48
+ more likely once a stale non-null value survives past its `free`.
49
+
50
+ ## Containers and iterators
51
+
52
+ - An iterator, pointer, or reference into a `std::vector` (or similar
53
+ contiguous container) is invalidated by any operation that can
54
+ reallocate or shift the underlying storage (`push_back` past capacity,
55
+ `insert`, `erase`) — do not hold one across such a call; re-fetch it, or
56
+ use the iterator `erase`/`insert` return to keep iterating correctly.
57
+ - Prefer `std::vector`/`std::array`/`std::string` over a raw
58
+ owning array or a hand-rolled dynamic buffer — the standard container
59
+ already manages growth, bounds, and destruction correctly.
60
+
61
+ ## Composition and interfaces
62
+
63
+ - Prefer composition over deep inheritance hierarchies; reach for
64
+ polymorphism only when callers genuinely need to treat different
65
+ concrete types uniformly through a shared interface, not as a default
66
+ way to share code.
67
+ - A C++ base class meant to be used polymorphically through a base
68
+ pointer/reference has a `virtual` (or `= default`d, explicitly
69
+ `virtual`) destructor — a non-virtual destructor on a polymorphic base
70
+ is undefined behavior when deleting through the base pointer.
71
+ - Keep a public C API's surface small and its ownership rules explicit in
72
+ the header; do not expose an internal struct's full layout when an
73
+ opaque handle plus accessor functions would let the implementation
74
+ change without breaking callers.
75
+
76
+ ## Anti-patterns to flag
77
+
78
+ - A "god header" with unrelated free functions and macros accumulating
79
+ under a generic name (`utils.h`, `common.h`) — split by what the code
80
+ actually does.
81
+ - C-style casts (`(Type)x`) in C++ where a named cast
82
+ (`static_cast`/`dynamic_cast`/`const_cast`/`reinterpret_cast`) would say
83
+ which conversion is actually intended and let the compiler catch the
84
+ wrong one.
85
+ - Output parameters used to fake multiple return values where
86
+ `std::tuple`/`std::pair`/a small struct (or, in C, a returned struct)
87
+ would be clearer and harder to call incorrectly.
@@ -0,0 +1,90 @@
1
+ ---
2
+ extends: common
3
+ paths: ["**/*.c", "**/*.h", "**/*.cpp", "**/*.cc", "**/*.cxx", "**/*.hpp", "**/*.hxx", "**/*.hh", "**/*.ipp", "**/*.inl", "**/*.tpp", "**/*.cppm", "**/*.ixx"]
4
+ metadata:
5
+ origin: authored
6
+ ---
7
+
8
+ # C / C++ security
9
+
10
+ Narrows `core-common-rules`' stack-agnostic security rules to C/C++
11
+ memory-safety and undefined-behavior risks, and the sanitizers/safe APIs
12
+ to use instead. Applies to both C and C++ files.
13
+
14
+ ## Memory safety
15
+
16
+ - Never dereference, write through, or `free` a pointer after it has been
17
+ freed or has gone out of scope (use-after-free) — set a freed pointer
18
+ to `NULL` when it could be read again, and never return a pointer or
19
+ reference to a stack-local object.
20
+ - Never `free`/`delete` the same pointer twice (double-free) — a
21
+ freed-and-nulled pointer makes a second `free`/`delete` a harmless
22
+ no-op instead of heap corruption.
23
+ - Bounds-check every array/buffer index and every pointer arithmetic
24
+ expression built from untrusted input before it is used to read or
25
+ write memory — an off-by-one or an unchecked length from user input is
26
+ the most common route to a buffer overflow.
27
+ - Never use `strcpy`, `strcat`, `sprintf`, or `gets` on data whose length
28
+ is not already statically known to fit the destination — use
29
+ `snprintf`, a length-checked copy, or a `std::string`/`std::vector`
30
+ that manages its own capacity.
31
+
32
+ ## Undefined behavior
33
+
34
+ - Signed integer overflow is undefined behavior in C/C++ (unlike
35
+ unsigned wraparound, which is well-defined modular arithmetic) — check
36
+ bounds before an addition/multiplication that could overflow a signed
37
+ type, or use an unsigned/wider type, or a checked-arithmetic helper,
38
+ for a bounds or size computation derived from untrusted input.
39
+ - Never read an uninitialized local variable or heap allocation — value-
40
+ initialize (`Type x{};` in C++, an explicit `= 0`/`memset` in C) any
41
+ variable whose first use might be a read, and never rely on whatever
42
+ value happened to be left on the stack or heap.
43
+ - Never access an object through a pointer of an incompatible type (a
44
+ strict-aliasing violation) — use `memcpy` for a type-punning copy, or
45
+ (C++20+) `std::bit_cast`, instead of reinterpreting a pointer's type
46
+ and dereferencing it.
47
+ - Do not read past the end of an array via pointer arithmetic even when
48
+ the read "usually" lands in valid memory — an out-of-bounds read past
49
+ the last element is undefined behavior regardless of what happens to be
50
+ mapped there.
51
+
52
+ ## Concurrency
53
+
54
+ - Every variable read from more than one thread while at least one thread
55
+ writes it is guarded by a mutex, or is a `std::atomic`/C11 `_Atomic`
56
+ type — an unsynchronized shared read/write is a data race, which is
57
+ undefined behavior in both C and C++, not merely "occasionally wrong."
58
+ - Acquire locks in a consistent global order across the codebase to avoid
59
+ deadlock; prefer `std::lock_guard`/`std::scoped_lock` (which also
60
+ supports locking multiple mutexes deadlock-free in one call) over
61
+ manual `lock()`/`unlock()` pairs, since a manual pair skips the unlock
62
+ on an early return or exception.
63
+
64
+ ## Sanitizers
65
+
66
+ - Build and run with AddressSanitizer + UndefinedBehaviorSanitizer
67
+ (`-fsanitize=address,undefined -fno-omit-frame-pointer -g`, Clang/GCC)
68
+ whenever investigating a memory-safety or UB bug report, or before
69
+ trusting a fix for one — ASan catches use-after-free, double-free,
70
+ buffer overflow, and heap corruption at the exact faulting access; UBSan
71
+ catches signed overflow, misaligned access, and other UB the compiler
72
+ is otherwise free to miscompile around silently.
73
+ - Build and run with ThreadSanitizer (`-fsanitize=thread`) — built as a
74
+ separate binary from ASan/UBSan, the two runtimes are not composable in
75
+ one build — whenever investigating a suspected data race or before
76
+ trusting a fix for one.
77
+ - A memory-safety, UB, or concurrency bug report is not actually verified
78
+ fixed until it has been reproduced and re-run under the relevant
79
+ sanitizer and comes back clean; a fix that only "looks right" by
80
+ inspection, or that only passes without a sanitizer attached, has not
81
+ been verified.
82
+
83
+ ## Dependency and input hygiene
84
+
85
+ - Validate the length/format of any externally-sourced buffer (network,
86
+ file, IPC, command-line argument) before parsing it — do not assume a
87
+ length field in the input itself is trustworthy without a sanity check
88
+ against the buffer's actual size.
89
+ - Never hard-code a credential, API key, or signing secret in source;
90
+ load it from environment/secret storage the project already uses.
@@ -0,0 +1,83 @@
1
+ ---
2
+ extends: common
3
+ paths: ["**/*.c", "**/*.h", "**/*.cpp", "**/*.cc", "**/*.cxx", "**/*.hpp", "**/*.hxx", "**/*.hh", "**/*.ipp", "**/*.inl", "**/*.tpp", "**/*.cppm", "**/*.ixx"]
4
+ metadata:
5
+ origin: authored
6
+ ---
7
+
8
+ # C / C++ testing
9
+
10
+ Narrows `core-common-rules`' stack-agnostic testing rules to GoogleTest
11
+ (and Catch2, where a project already uses it) plus sanitizer-backed
12
+ verification. Applies to both C and C++ test files.
13
+
14
+ ## Layout and naming
15
+
16
+ - Tests live under a project's own existing test tree (commonly
17
+ `tests/`, `test/`, or beside the source with a `_test.cpp` suffix) —
18
+ match whichever convention the project already uses, do not invent a
19
+ second one.
20
+ - `TEST(SuiteName, CaseName)` for a standalone case; `TEST_F(FixtureName,
21
+ CaseName)` when the case needs shared `SetUp()`/`TearDown()` state via a
22
+ fixture class derived from `::testing::Test`. Name suites/cases for the
23
+ behavior under test, not `Test1`/`Test2`.
24
+ - `TEST_P` with `INSTANTIATE_TEST_SUITE_P` for a parameterized case run
25
+ against a range of inputs, instead of copy-pasting the same body with
26
+ different literals across several `TEST`s.
27
+
28
+ ## Fixtures and lifecycle
29
+
30
+ - Put shared per-test setup in `SetUp()` and teardown in `TearDown()`,
31
+ not the fixture's constructor/destructor, when the setup can fail in a
32
+ way the test should assert on (`SetUp()` runs inside the test's own
33
+ assertion context; a constructor failure does not).
34
+ - A fixture that owns a resource (a temp file, a mock server, a thread)
35
+ releases it in `TearDown()` even when the test body fails an assertion
36
+ partway through — do not rely on a `TEST_F` body reaching a cleanup
37
+ statement at its end.
38
+
39
+ ## Assertions
40
+
41
+ - `ASSERT_*` when the test cannot meaningfully continue after a failure
42
+ (a null pointer that every following line would dereference);
43
+ `EXPECT_*` when later checks in the same test still give useful
44
+ information after one fails.
45
+ - `ASSERT_DEATH`/`EXPECT_DEATH` for code whose contract is to terminate
46
+ the process on a given input (an assertion failure, a `CHECK`) — set
47
+ `GTEST_FLAG_SET(death_test_style, "threadsafe")` (or the project's own
48
+ configured style) when the test binary is multi-threaded, since the
49
+ default `fast` style can misbehave by forking a process with more than
50
+ one live thread.
51
+ - Compare floating-point values with `EXPECT_NEAR`/`EXPECT_FLOAT_EQ`, not
52
+ `EXPECT_EQ`, unless the value is provably exact (an integer-valued
53
+ computation stored in a float).
54
+
55
+ ## Determinism and concurrency
56
+
57
+ - Never synchronize a multi-threaded test with `sleep`/`usleep`/
58
+ `std::this_thread::sleep_for` to "give the other thread time" — join
59
+ the thread, wait on a condition variable, or use a future/promise so
60
+ the test is deterministic and not flaky under load.
61
+ - A test exercising concurrent code is run under ThreadSanitizer
62
+ (`-fsanitize=thread`) at least once before being trusted, since a data
63
+ race can pass every assertion while still being undefined behavior.
64
+
65
+ ## Sanitizer-backed verification
66
+
67
+ - Build and run the test suite under AddressSanitizer + UndefinedBehavior-
68
+ Sanitizer for any change touching manual memory management, buffers, or
69
+ pointer arithmetic — a leaking or corrupting test can still report
70
+ green under plain assertions while ASan/UBSan catch the actual bug.
71
+ - New behavior gets a new test in the same change; a bug fix (especially
72
+ a memory-safety or UB fix) gets a regression test that reproduces the
73
+ failure under the relevant sanitizer before the fix and passes clean
74
+ after it.
75
+
76
+ ## Fuzzing
77
+
78
+ - A parser, decoder, or anything else that touches untrusted input
79
+ formats gets a libFuzzer harness (`LLVMFuzzerTestOneInput`, built with
80
+ `-fsanitize=fuzzer,address,undefined`) when the project already has
81
+ fuzzing infrastructure, or a documented seed corpus ready for one to be
82
+ added — do not skip fuzz coverage for a new untrusted-input parser
83
+ solely because unit tests pass.
@@ -0,0 +1,153 @@
1
+ ---
2
+ name: c-cpp-build-fix
3
+ description: "Use when a C/C++ build fails -- CMake configure/link errors, missing headers, template/generic-instantiation errors, ABI/linker mismatches -- or when a sanitizer (ASan/UBSan/TSan) or ctest run reports a memory-safety, UB, or race failure, with the smallest root-cause fix."
4
+ triggers:
5
+ - "cmake build is failing"
6
+ - "fix this linker error"
7
+ - "AddressSanitizer reports a heap-buffer-overflow"
8
+ - "UBSan reports signed overflow"
9
+ - "ThreadSanitizer flags an unsynchronized access to this shared counter"
10
+ - "fix this C++ template instantiation error"
11
+ metadata:
12
+ origin: authored
13
+ category: build-fix
14
+ version: "1.0.0"
15
+ compatible_harnesses: "claude,codex,cursor,zed,opencode"
16
+ license: "MIT"
17
+ ---
18
+
19
+ # C / C++ build fix
20
+
21
+ Resolve a C/C++ build/configure/link failure, or a sanitizer/`ctest`
22
+ failure — with the smallest change that fixes the actual root cause.
23
+ `rules/coding-style.mdc`, `rules/patterns.mdc`, and `rules/security.mdc`
24
+ govern what a "correct" fix looks like; this skill never reaches for a
25
+ suppression instead of a fix.
26
+
27
+ ## Workflow
28
+
29
+ ### Step 1: Reproduce and classify
30
+
31
+ ```bash
32
+ cmake --build build
33
+ ctest --test-dir build --output-on-failure
34
+ ```
35
+
36
+ Read the exact error text and classify it:
37
+
38
+ - **Configure error** (CMake cache mismatch, a missing `find_package`
39
+ dependency, a stale `build/` directory from a changed toolchain).
40
+ - **Compile error** (undefined symbol, type mismatch, missing header,
41
+ wrong argument count/type).
42
+ - **Template/generic-instantiation error** (a template that cannot
43
+ deduce its argument, a constraint/concept that is not satisfied, an
44
+ incomplete type used where a complete one is required).
45
+ - **Link/ABI error** (undefined reference, a symbol built against a
46
+ different ABI or ODR-violating duplicate definition across translation
47
+ units, a missing `target_link_libraries`).
48
+ - **Sanitizer failure** (ASan `heap-buffer-overflow`/`use-after-free`,
49
+ UBSan `signed integer overflow`/`misaligned address`, TSan
50
+ `data race`) — read the sanitizer's full stack trace; it names the
51
+ exact faulting access and, for TSan, both racing accesses.
52
+ - **Test failure** (`ctest` reports a failing case with no sanitizer
53
+ involved).
54
+
55
+ ### Step 2: Fix by category
56
+
57
+ **Configure/CMake:** re-run configure after a clean `build/` directory
58
+ when the cache is simply stale against changed `CMakeLists.txt`. For a
59
+ genuine missing dependency, add the correct `find_package`/
60
+ `target_link_libraries` rather than hand-pointing at a local path that
61
+ happens to work on one machine.
62
+
63
+ **Compile error:** fix the actual type/signature/missing-include issue
64
+ the compiler names. A missing header is fixed by including the header
65
+ that actually declares the symbol, not by forward-declaring around it
66
+ unless the project's own convention already does that intentionally.
67
+
68
+ **Template/generic-instantiation:** check whether the call site can
69
+ supply the type argument explicitly, or whether a concept/`static_assert`
70
+ is correctly rejecting an unsupported type (in which case the fix is at
71
+ the call site, not the template). Do not loosen a constraint just to make
72
+ an unrelated instantiation compile.
73
+
74
+ **Link/ABI:** an undefined reference usually means a missing
75
+ `target_link_libraries` entry or a translation unit not compiled into
76
+ the target — fix the CMake target graph. An ODR violation (the same
77
+ symbol defined differently in two translation units) is fixed by
78
+ unifying the definition, never by reordering link order to hide it.
79
+
80
+ **Sanitizer failure (ASan/UBSan):** read the exact faulting access from
81
+ the report (allocation site, free site if UAF, faulting instruction) and
82
+ add the actual missing check/ownership fix at that point — a bounds
83
+ check, a lifetime fix (return by value instead of a reference into a
84
+ temporary), or a `NULL` check. Never rebuild without the sanitizer to
85
+ make the failure "go away"; that hides the bug, it does not fix it.
86
+
87
+ **Sanitizer failure (TSan):** add synchronization (mutex, atomic, or a
88
+ join/wait) at the specific shared-state access TSan names for both
89
+ racing accesses — do not just serialize the whole test or add a `sleep`
90
+ to hide the timing window.
91
+
92
+ **Test failure (no sanitizer):** fix the assertion or the code it tests,
93
+ whichever is actually wrong per the spec — never loosen an assertion just
94
+ to make it pass.
95
+
96
+ ### Step 3: Verify
97
+
98
+ ```bash
99
+ cmake --build build
100
+ ctest --test-dir build --output-on-failure
101
+ ```
102
+
103
+ Re-run under the sanitizer that originally caught the failure (ASan+UBSan
104
+ or TSan) if the failure came from one — the fix is not verified until the
105
+ sanitizer run is also clean, not just the plain build.
106
+
107
+ ### Step 4: Report
108
+
109
+ ```
110
+ Fixed: src/parser/token_stream.cpp — heap-buffer-overflow in
111
+ TokenStream::peek() (ASan)
112
+ - Root cause: peek() read one byte past the buffer on the last token
113
+ boundary; missing bounds check before the trailing-byte read
114
+ - Build + ctest + ASan/UBSan re-run all pass
115
+ ```
116
+
117
+ State the root cause in one sentence, not just "fixed the error."
118
+
119
+ ## Rules
120
+
121
+ - Find and fix the smallest change that addresses the actual root cause —
122
+ never widen a fix beyond what the failure requires.
123
+ - NEVER disable, narrow the scope of, or suppress a sanitizer finding
124
+ instead of fixing the underlying memory-safety/UB/race bug it caught.
125
+ - NEVER reintroduce an unsafe API (`strcpy`, `sprintf`, `gets`) while
126
+ resolving an unrelated failure.
127
+ - NEVER loosen a template constraint, a `static_assert`, or a test
128
+ assertion just to make a failure disappear without understanding why
129
+ it was there.
130
+ - NEVER change the project's declared C/C++ standard just to make an
131
+ error disappear without understanding why the code needs it.
132
+
133
+ ## Red Flags
134
+
135
+ | Rationalization | Why it is wrong |
136
+ |---|---|
137
+ | "I'll just turn off ASan for this test so CI goes green" | Silences the finding without fixing the memory-safety bug it caught; fix the bounds/lifetime issue at the site ASan names instead |
138
+ | "This data race is rare, I'll add a sleep to make it stop happening" | A `sleep` narrows the timing window without removing the race; TSan will still be correct that the access is unsynchronized, and the race can still fire under different scheduling |
139
+ | "I'll widen this concept/constraint so more types compile" | Loosening a constraint to silence one instantiation failure can let a genuinely unsupported type instantiate the template incorrectly elsewhere |
140
+ | "sprintf is fine here, the buffer is 'probably' big enough" | "Probably" is exactly the assumption that turns into a buffer overflow the first time an input is bigger than expected; use `snprintf` |
141
+
142
+ ## Verification
143
+
144
+ Do not report the fix done until all of the following hold:
145
+
146
+ - `cmake --build build` and `ctest --test-dir build --output-on-failure`
147
+ both exit 0.
148
+ - If the original failure came from a sanitizer, the sanitizer build/run
149
+ is re-verified clean, not just the plain build.
150
+ - The change is the smallest one that addresses the stated root cause —
151
+ no unrelated files touched.
152
+ - The report states the root cause in one sentence, not just "build now
153
+ passes."
@@ -0,0 +1,74 @@
1
+ {
2
+ "triggers": {
3
+ "positive": [
4
+ "Our ctest suite segfaults inside the ASan build after the latest merge, can you track down what's reading past the buffer",
5
+ "ThreadSanitizer flagged a data race, help me fix it",
6
+ "UndefinedBehaviorSanitizer is reporting signed integer overflow here",
7
+ "Our CMake configure step is failing with an undefined reference",
8
+ "This template won't instantiate, fix the compile error",
9
+ "The build is failing with a missing header error",
10
+ "Our test binary crashes under ASan, track down the root cause"
11
+ ],
12
+ "negative": [
13
+ "Implement this new feature from scratch, no build errors involved",
14
+ "Write GoogleTest cases for this already-working class",
15
+ "Review this diff for memory safety before merging",
16
+ "Fix this failing go build for the worker package",
17
+ "Our npm build is failing with a webpack error",
18
+ "Fix this failing pytest collection error"
19
+ ]
20
+ },
21
+ "scenarios": [
22
+ {
23
+ "id": "asan-heap-overflow-fix-not-suppress",
24
+ "prompt": "AddressSanitizer reports:\n\n```\nERROR: AddressSanitizer: heap-buffer-overflow on address ...\nREAD of size 1 at ... thread T0\n #0 TokenStream::peek() token_stream.cpp:42\n```\n\nHow do I fix this?",
25
+ "strictness": "high",
26
+ "expected_behavior": [
27
+ {
28
+ "grader": "judge",
29
+ "rubric": "A correct answer treats the ASan report as pointing at a real out-of-bounds read in TokenStream::peek() at line 42, and fixes it by adding or correcting the bounds check that prevents reading past the buffer -- it does not suggest disabling, narrowing, or otherwise routing around the sanitizer to make the finding go away.",
30
+ "pass_criteria": [
31
+ "Identifies the finding as a genuine out-of-bounds read at the reported location (TokenStream::peek(), token_stream.cpp:42), not a false positive to be dismissed.",
32
+ "Proposes a concrete fix: adding/correcting a bounds check (e.g. comparing the read position against the buffer's length) before the read that peek() performs.",
33
+ "States that the fix should be re-verified by re-running the test under the ASan build, not just a plain build."
34
+ ],
35
+ "fail_criteria": [
36
+ "Recommends disabling ASan, removing the sanitizer flag, adding an ASan suppress list/suppression entry, or excluding token_stream.cpp/this test from the sanitized build instead of fixing the out-of-bounds read. Mentioning that only to warn against it is not a failure."
37
+ ]
38
+ }
39
+ ],
40
+ "calibration": {
41
+ "known_right": "This is a real out-of-bounds read, not a false positive -- ASan is pointing at token_stream.cpp:42 inside TokenStream::peek(). Read the surrounding code: peek() is almost certainly reading `buffer_[pos_]` (or similar) without first checking that `pos_` is still within `buffer_`'s bounds, most likely on the last token where `pos_` has already reached the end. Fix it by adding the missing bounds check before the read, e.g. `if (pos_ >= buffer_.size()) return kEof;` (or whatever peek()'s documented behavior should be at end-of-input) before the indexing. Once the check is in, rebuild the ASan configuration (`cmake --build build-asan`) and re-run the specific test that exercises this path (`ctest --test-dir build-asan -R TokenStreamTest --output-on-failure`) to confirm ASan no longer reports the overflow -- a plain, non-sanitized rebuild passing is not sufficient confirmation.",
42
+ "known_wrong": "ASan is pretty aggressive and throws false positives on tight parsing loops like this fairly often. Simplest fix is to just suppress it -- add token_stream.cpp's TokenStream::peek to an ASan suppress list (or drop -fsanitize=address for that one translation unit) so the rest of the sanitized build stays clean and this known-noisy function doesn't keep flagging.",
43
+ "vague": "Take a look at what's happening around line 42 in peek() and make sure the bounds are being handled correctly there.",
44
+ "subtle_wrong": "The read at line 42 is one byte past the buffer on the very last token, so the safest fix is to just allocate the underlying buffer with one extra byte of padding (`buffer_.resize(buffer_.size() + 1)`) when TokenStream is constructed, so that stray read always lands in valid memory. That way peek()'s existing logic doesn't need to change at all, and the ASan report goes away because the read is no longer actually out of bounds."
45
+ },
46
+ "anti_patterns": ["suppress"]
47
+ },
48
+ {
49
+ "id": "tsan-race-add-sync-not-serialize",
50
+ "prompt": "ThreadSanitizer reports a data race:\n\n```\nWARNING: ThreadSanitizer: data race\n Write of size 8 at 0x... by thread T1:\n #0 Cache::put(...) cache.cpp:31\n Previous read of size 8 at 0x... by thread T2:\n #0 Cache::get(...) cache.cpp:18\n```\n\nHow should I fix this?",
51
+ "strictness": "high",
52
+ "expected_behavior": [
53
+ {
54
+ "grader": "judge",
55
+ "rubric": "A correct answer adds real synchronization (a mutex guarding both Cache::get and Cache::put's access to the shared state, or converting the field to std::atomic if a single word suffices) at the specific accesses TSan names, rather than working around the race by serializing the whole test suite or disabling the sanitizer.",
56
+ "pass_criteria": [
57
+ "Names a concrete synchronization mechanism -- a mutex (e.g. std::lock_guard/std::mutex) guarding both cache.cpp:31 (put) and cache.cpp:18 (get), or std::atomic if the shared data is a single word -- applied at the actual accesses TSan flagged.",
58
+ "States that both the read in get() and the write in put() need to be covered by the same synchronization, not just one side.",
59
+ "Does not propose serializing the test (running it single-threaded, adding a global test-only lock around the whole test body) or disabling/skipping TSan as the fix."
60
+ ],
61
+ "fail_criteria": [
62
+ "Recommends running the test single-threaded, adding a sleep to reduce the race window, or disabling/skipping ThreadSanitizer instead of adding real synchronization to Cache::get/Cache::put. Mentioning that only to warn against it is not a failure."
63
+ ]
64
+ }
65
+ ],
66
+ "calibration": {
67
+ "known_right": "TSan is naming the actual unsynchronized pair: a write in Cache::put() at cache.cpp:31 racing a read in Cache::get() at cache.cpp:18, both touching the same shared field with no lock between them. Add a std::mutex member to Cache guarding whatever field both functions touch, and take it in both: `std::lock_guard<std::mutex> lock(mutex_);` at the top of both get() and put() before they read/write the shared state. If the shared data really is just a single word (a counter, a pointer swap), std::atomic<T> with appropriate memory order is a lighter-weight alternative to a full mutex -- but either way, both the read side and the write side need the same synchronization, not just one of them. Rebuild under TSan (`-fsanitize=thread`) and re-run the specific test to confirm the warning is gone before considering this fixed.",
68
+ "known_wrong": "The simplest fix is to just run this particular test single-threaded -- add `GTEST_FLAG_SET` or a test-only global lock around the whole test body so get() and put() never actually execute concurrently during the test run. That removes the race TSan is seeing without having to add locking to Cache itself, which keeps the production code's hot path exactly as fast as it is today.",
69
+ "vague": "Cache::get and Cache::put seem to be touching something shared without any protection, that probably needs to be locked down.",
70
+ "subtle_wrong": "Add the mutex, but only take the lock inside put() around the write at cache.cpp:31 -- get() at cache.cpp:18 is just reading a value, and a plain read of an already-word-aligned field is safe to leave unlocked on most platforms since a single aligned load or store doesn't tear. That gets the write side properly guarded, which is the side TSan is really worried about, without slowing down the more frequent read path with lock overhead."
71
+ }
72
+ }
73
+ ]
74
+ }