@mrciphersmith/keryx 0.3.3 → 0.3.5

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 (67) hide show
  1. package/dist/cli.js +125 -51
  2. package/package.json +1 -1
  3. package/src/gdskills/bundled/install-manifest.json +231 -2
  4. package/src/gdskills/bundled/stacks/csharp-dotnet/agent-refs.json +4 -0
  5. package/src/gdskills/bundled/stacks/csharp-dotnet/governance/eval.json +1881 -0
  6. package/src/gdskills/bundled/stacks/csharp-dotnet/governance/scout.json +33 -0
  7. package/src/gdskills/bundled/stacks/csharp-dotnet/pack.json +38 -0
  8. package/src/gdskills/bundled/stacks/csharp-dotnet/rules/coding-style.mdc +100 -0
  9. package/src/gdskills/bundled/stacks/csharp-dotnet/rules/patterns.mdc +107 -0
  10. package/src/gdskills/bundled/stacks/csharp-dotnet/rules/security.mdc +86 -0
  11. package/src/gdskills/bundled/stacks/csharp-dotnet/rules/testing.mdc +89 -0
  12. package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-build-fix/SKILL.md +143 -0
  13. package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-build-fix/evals.json +77 -0
  14. package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-code-review/SKILL.md +121 -0
  15. package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-code-review/evals.json +77 -0
  16. package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-implementation/SKILL.md +134 -0
  17. package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-implementation/evals.json +76 -0
  18. package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-testing/SKILL.md +130 -0
  19. package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-testing/evals.json +77 -0
  20. package/src/gdskills/bundled/stacks/flutter-dart/agent-refs.json +4 -0
  21. package/src/gdskills/bundled/stacks/flutter-dart/governance/eval.json +1849 -0
  22. package/src/gdskills/bundled/stacks/flutter-dart/governance/scout.json +33 -0
  23. package/src/gdskills/bundled/stacks/flutter-dart/pack.json +41 -0
  24. package/src/gdskills/bundled/stacks/flutter-dart/rules/coding-style.mdc +98 -0
  25. package/src/gdskills/bundled/stacks/flutter-dart/rules/patterns.mdc +88 -0
  26. package/src/gdskills/bundled/stacks/flutter-dart/rules/security.mdc +91 -0
  27. package/src/gdskills/bundled/stacks/flutter-dart/rules/testing.mdc +101 -0
  28. package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-build-fix/SKILL.md +134 -0
  29. package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-build-fix/evals.json +79 -0
  30. package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-code-review/SKILL.md +124 -0
  31. package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-code-review/evals.json +74 -0
  32. package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-implementation/SKILL.md +139 -0
  33. package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-implementation/evals.json +77 -0
  34. package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-testing/SKILL.md +134 -0
  35. package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-testing/evals.json +74 -0
  36. package/src/gdskills/bundled/stacks/kotlin-android/agent-refs.json +4 -0
  37. package/src/gdskills/bundled/stacks/kotlin-android/governance/eval.json +1889 -0
  38. package/src/gdskills/bundled/stacks/kotlin-android/governance/scout.json +34 -0
  39. package/src/gdskills/bundled/stacks/kotlin-android/pack.json +38 -0
  40. package/src/gdskills/bundled/stacks/kotlin-android/rules/coding-style.mdc +89 -0
  41. package/src/gdskills/bundled/stacks/kotlin-android/rules/patterns.mdc +96 -0
  42. package/src/gdskills/bundled/stacks/kotlin-android/rules/security.mdc +90 -0
  43. package/src/gdskills/bundled/stacks/kotlin-android/rules/testing.mdc +89 -0
  44. package/src/gdskills/bundled/stacks/kotlin-android/skills/compose-implementation/SKILL.md +150 -0
  45. package/src/gdskills/bundled/stacks/kotlin-android/skills/compose-implementation/evals.json +77 -0
  46. package/src/gdskills/bundled/stacks/kotlin-android/skills/kotlin-android-build-fix/SKILL.md +151 -0
  47. package/src/gdskills/bundled/stacks/kotlin-android/skills/kotlin-android-build-fix/evals.json +76 -0
  48. package/src/gdskills/bundled/stacks/kotlin-android/skills/kotlin-android-code-review/SKILL.md +139 -0
  49. package/src/gdskills/bundled/stacks/kotlin-android/skills/kotlin-android-code-review/evals.json +78 -0
  50. package/src/gdskills/bundled/stacks/kotlin-android/skills/kotlin-android-testing/SKILL.md +131 -0
  51. package/src/gdskills/bundled/stacks/kotlin-android/skills/kotlin-android-testing/evals.json +77 -0
  52. package/src/gdskills/bundled/stacks/swift-ios/agent-refs.json +4 -0
  53. package/src/gdskills/bundled/stacks/swift-ios/governance/eval.json +1803 -0
  54. package/src/gdskills/bundled/stacks/swift-ios/governance/scout.json +32 -0
  55. package/src/gdskills/bundled/stacks/swift-ios/pack.json +38 -0
  56. package/src/gdskills/bundled/stacks/swift-ios/rules/coding-style.mdc +92 -0
  57. package/src/gdskills/bundled/stacks/swift-ios/rules/patterns.mdc +112 -0
  58. package/src/gdskills/bundled/stacks/swift-ios/rules/security.mdc +78 -0
  59. package/src/gdskills/bundled/stacks/swift-ios/rules/testing.mdc +90 -0
  60. package/src/gdskills/bundled/stacks/swift-ios/skills/swift-build-fix/SKILL.md +144 -0
  61. package/src/gdskills/bundled/stacks/swift-ios/skills/swift-build-fix/evals.json +75 -0
  62. package/src/gdskills/bundled/stacks/swift-ios/skills/swift-code-review/SKILL.md +122 -0
  63. package/src/gdskills/bundled/stacks/swift-ios/skills/swift-code-review/evals.json +75 -0
  64. package/src/gdskills/bundled/stacks/swift-ios/skills/swift-testing/SKILL.md +131 -0
  65. package/src/gdskills/bundled/stacks/swift-ios/skills/swift-testing/evals.json +75 -0
  66. package/src/gdskills/bundled/stacks/swift-ios/skills/swiftui-implementation/SKILL.md +149 -0
  67. package/src/gdskills/bundled/stacks/swift-ios/skills/swiftui-implementation/evals.json +76 -0
@@ -0,0 +1,33 @@
1
+ [
2
+ {
3
+ "query": "Use when implementing or extending a feature in a C#/.NET service or library -- DI registration and lifetimes, async/await (Task vs ValueTask, avoiding async void), nullable reference type annotations, records vs classes, EF Core query shape, and IDisposable/IAsyncDisposable cleanup.",
4
+ "decision": "fork",
5
+ "topMatch": "csharp-dotnet/dotnet-code-review",
6
+ "recordedAt": "2026-09-25T15:34:51.958Z",
7
+ "skillName": "dotnet-implementation",
8
+ "justification": "Top match dotnet-code-review is this same pack's read-only review skill (different intent, action verb, and workflow), not a real implementation-skill duplicate; no existing implement skill covers C#/.NET DI lifetimes, Task/ValueTask, nullable reference types, or EF Core query shape."
9
+ },
10
+ {
11
+ "query": "Use when a C#/.NET test project needs writing, extending, or fixing -- xUnit/NUnit Fact/Theory and InlineData/TestCase tables, async test methods, and disciplined Moq/NSubstitute mocking that stubs external seams (HTTP, repository, clock) rather than internal collaborators.",
12
+ "decision": "create",
13
+ "topMatch": "swift-ios/swift-testing",
14
+ "recordedAt": "2026-09-25T15:33:42.746Z",
15
+ "skillName": "dotnet-testing"
16
+ },
17
+ {
18
+ "query": "Use when reviewing a C#/.NET change for async and resource-safety risks -- async void outside event handlers, sync-over-async (.Result/.Wait()), swallowed/blanket exception catches, missing IDisposable/IAsyncDisposable cleanup, and nullable-annotation gaps. Read-only, no edits.",
19
+ "decision": "fork",
20
+ "topMatch": "ts-js-node/nodejs-code-review",
21
+ "recordedAt": "2026-09-25T15:34:52.239Z",
22
+ "skillName": "dotnet-code-review",
23
+ "justification": "Nearest match ts-js-node/nodejs-code-review (0.24-ish overlap) reviews TypeScript/Node concurrency and idiom, not C#'s async-void/sync-over-async/IDisposable vocabulary; no existing review skill covers .NET-specific async and resource-safety anti-patterns."
24
+ },
25
+ {
26
+ "query": "Use when dotnet build/dotnet test fails, or a NuGet restore is broken -- resolves package/target-framework mismatches, compiler errors, nullable-annotation warnings, analyzer/StyleCop findings, and a failing test, with the smallest root-cause fix rather than a suppression.",
27
+ "decision": "fork",
28
+ "topMatch": "kotlin-android/kotlin-android-build-fix",
29
+ "recordedAt": "2026-09-25T15:34:52.517Z",
30
+ "skillName": "dotnet-build-fix",
31
+ "justification": "Nearest match kotlin-android/kotlin-android-build-fix fixes Gradle/AGP/Kotlin compiler errors, not dotnet build/NuGet/target-framework/StyleCop findings; each build-fix skill is scoped to its own toolchain's specific failure surface, sharing only the generic 'root cause not suppression' framing."
32
+ }
33
+ ]
@@ -0,0 +1,38 @@
1
+ {
2
+ "id": "csharp-dotnet",
3
+ "family": "language",
4
+ "modules": ["csharp-dotnet-rules", "csharp-dotnet-skills"],
5
+ "detectionMarkers": ["csharp", "dotnet", "c#"],
6
+ "provenance": { "origin": "authored", "sourceRef": "flow 336, Wave 4 batch 4" },
7
+ "stability": "experimental",
8
+ "skills": {
9
+ "implement": ["dotnet-implementation"],
10
+ "test": ["dotnet-testing"],
11
+ "review": ["dotnet-code-review"],
12
+ "build-fix": ["dotnet-build-fix"],
13
+ "migrate": []
14
+ },
15
+ "agentProfile": {
16
+ "displayName": "C#/.NET",
17
+ "auditFocus": [
18
+ "async void used outside an event handler, swallowing any exception the method throws",
19
+ "a blocking `.Result`/`.Wait()` call on a Task from synchronous code, risking a sync-over-async deadlock",
20
+ "a public API returning a nullable-looking reference type with nullable annotations disabled, or an unannotated parameter that is dereferenced without a null check",
21
+ "IDisposable/IAsyncDisposable fields or locals not wrapped in `using`/`await using`, leaking unmanaged resources or connections",
22
+ "SQL built by string interpolation/concatenation into SqlCommand.CommandText instead of parameterized queries",
23
+ "a unit test mocking an internal collaborator (a private helper, a concrete implementation detail) instead of the external seam (HTTP client, repository interface, database)"
24
+ ],
25
+ "buildCommands": [
26
+ "dotnet build",
27
+ "dotnet test",
28
+ "dotnet format --verify-no-changes",
29
+ "dotnet build /p:TreatWarningsAsErrors=true /p:EnforceCodeStyleInBuild=true"
30
+ ],
31
+ "fixGuardrails": [
32
+ "never add `#pragma warning disable` or `[SuppressMessage]` to silence a compiler/analyzer warning without fixing or explaining the underlying issue",
33
+ "never downgrade or disable nullable reference types (`<Nullable>disable</Nullable>` or a blanket `#nullable disable`) just to make a nullability warning disappear",
34
+ "never pin or downgrade a NuGet package to route around a real incompatibility without saying so in the report",
35
+ "fix the smallest root cause; do not refactor unrelated code while resolving a build/test/analyzer failure"
36
+ ]
37
+ }
38
+ }
@@ -0,0 +1,100 @@
1
+ ---
2
+ extends: common
3
+ paths: ["**/*.cs"]
4
+ metadata:
5
+ origin: authored
6
+ ---
7
+
8
+ # C#/.NET coding style
9
+
10
+ Narrows `core-common-rules`' stack-agnostic style rules to modern C#
11
+ idiom (new project templates enable nullable reference types by
12
+ default since .NET 6/C# 10 — the language itself still defaults
13
+ annotations/warnings off, so an existing project may need `<Nullable>
14
+ enable</Nullable>` added explicitly; primary constructors and
15
+ collection expressions since C# 12/.NET 8).
16
+ Applies only to `*.cs` files — everything not C#-specific still comes
17
+ from the common rules this file `extends`.
18
+
19
+ ## Naming and structure
20
+
21
+ - `PascalCase` for types, methods, properties, and public/protected
22
+ members; `camelCase` for locals and parameters; a private field is
23
+ `_camelCase` with a leading underscore, not `m_field` or bare
24
+ `camelCase` that can collide with a parameter name.
25
+ - Interfaces are prefixed `I` (`IOrderRepository`), never suffixed
26
+ (`OrderRepositoryInterface`).
27
+ - One public type per file, file named after the type
28
+ (`OrderService.cs` for `class OrderService`), unless the type is a
29
+ small private nested/support type used only inside its owner.
30
+ - File-scoped namespaces (`namespace Orders;`) instead of a nested
31
+ block, unless the project's existing files already use block-scoped
32
+ namespaces — match what is there.
33
+
34
+ ## Records, classes, and immutability
35
+
36
+ - Prefer a `record`/`record class` for a type whose identity is its
37
+ value (a DTO, a command, an event, an immutable snapshot) — it gets
38
+ structural equality and a `with` expression for free. Reach for `class`
39
+ when the type has identity beyond its data, mutable state that changes
40
+ over its lifetime, or entity semantics an ORM tracks by reference/key.
41
+ - Use `record struct` for a small, frequently-allocated value type where
42
+ avoiding heap allocation matters; default to a reference `record`
43
+ otherwise.
44
+ - Favor `init`-only properties (or a primary constructor) over a
45
+ public setter on anything that should not change after construction —
46
+ a mutable public setter on a "value" type invites accidental aliasing
47
+ bugs.
48
+
49
+ ## Pattern matching and modern idiom
50
+
51
+ - Prefer pattern matching (`is`, `switch` expressions, property patterns,
52
+ list patterns) over a chain of `if`/`else if` type checks or casts —
53
+ `if (shape is Circle { Radius: > 0 } c)` reads its own intent; a cast
54
+ followed by a separate null/type check does not.
55
+ - Use a primary constructor (`class OrderService(IOrderRepository repo)`)
56
+ for a type whose constructor only assigns its parameters to fields —
57
+ it removes the boilerplate field-and-assignment block. Fall back to an
58
+ explicit constructor body once it needs validation, logic, or more than
59
+ trivial assignment.
60
+ - Use collection expressions (`int[] xs = [1, 2, 3];`, `List<T> ys = [];`)
61
+ for new collection-literal code instead of `new[] { ... }`/
62
+ `new List<T> { ... }`, unless the project's existing style has not
63
+ adopted them yet — match what is there rather than mixing both styles
64
+ in one file.
65
+ - Expression-bodied members (`public int Total => Items.Sum(i => i.Price);`)
66
+ for a single-expression method/property/operator; keep a full block
67
+ body once the member needs more than one statement or local reasoning
68
+ step — do not force multi-statement logic into one expression via `;`
69
+ chaining or nested ternaries.
70
+
71
+ ## Nullable reference types
72
+
73
+ - New projects have the nullable annotation and warning contexts on by
74
+ default (`<Nullable>enable</Nullable>` in the project file) — keep it
75
+ on; do not add `<Nullable>disable</Nullable>` or a blanket
76
+ `#nullable disable` to make warnings go away.
77
+ - A parameter, property, or return type that can genuinely be absent is
78
+ annotated `?` (`string? middleName`); a public member with no `?` is a
79
+ promise to callers that it is never null — do not violate that promise
80
+ with a hidden `null` return "just this once."
81
+ - Use `ArgumentNullException.ThrowIfNull(value)` at a public API's entry
82
+ point for a reference-typed parameter that must not be null, instead of
83
+ a hand-rolled `if (value is null) throw new ArgumentNullException(...)`.
84
+ - Resolve a nullability warning by fixing the actual null path (a real
85
+ null check, a non-null initializer, restructuring so the compiler can
86
+ see the invariant) — never with the null-forgiving operator (`value!`)
87
+ as a substitute for an actual guard, except at a boundary where you can
88
+ state in a comment exactly why the value is provably non-null there.
89
+
90
+ ## `var` usage
91
+
92
+ - Use `var` when the right-hand side already makes the type obvious
93
+ (`var order = new Order();`, `var items = order.Items.ToList();`) or
94
+ when the concrete type is long/generic and adds no clarity
95
+ (`var lookup = new Dictionary<string, List<OrderLine>>();`).
96
+ - Use an explicit type when `var` would hide the type at a call site that
97
+ matters for reading correctness (`var result = Parse(input);` where
98
+ `Parse`'s return type is not obvious from the name) or when the
99
+ declared type differs meaningfully from the initializer's runtime type
100
+ (assigning a derived type but declaring the base).
@@ -0,0 +1,107 @@
1
+ ---
2
+ extends: common
3
+ paths: ["**/*.cs"]
4
+ metadata:
5
+ origin: authored
6
+ ---
7
+
8
+ # C#/.NET patterns
9
+
10
+ Narrows `core-common-rules`' stack-agnostic design guidance to
11
+ idiomatic C#/.NET design and its common anti-patterns: async/await,
12
+ dependency injection, LINQ, and disposal. Applies only to `*.cs` files.
13
+
14
+ ## Async/await
15
+
16
+ - Never use `async void` except for an actual event handler
17
+ (`private async void Button_Click(object sender, EventArgs e)`) — any
18
+ exception an `async void` method throws cannot be awaited or caught by
19
+ its caller and crashes the process instead. Every other async method
20
+ returns `Task` or `Task<T>`.
21
+ - Return `Task`/`Task<T>` for a method whose async work is on the common,
22
+ usually-awaited path; return `ValueTask`/`ValueTask<T>` only for a
23
+ hot-path method that frequently completes synchronously (a cache hit),
24
+ where avoiding the `Task` allocation is measured to matter — a
25
+ `ValueTask` must be awaited (or converted) exactly once and never
26
+ stored, unlike a `Task`.
27
+ - In library code that does not need to resume on a captured context
28
+ (UI thread, ASP.NET classic `SynchronizationContext`), call
29
+ `.ConfigureAwait(false)` on an awaited `Task` to avoid an unnecessary
30
+ context-capture and reduce deadlock risk. Modern ASP.NET Core has no
31
+ such `SynchronizationContext`, so application-level request-handling
32
+ code in an ASP.NET Core project does not need it — match what the
33
+ project's own code already does.
34
+ - Never call `.Result`, `.Wait()`, or `.GetAwaiter().GetResult()` on a
35
+ `Task` from otherwise-synchronous code to "call an async method
36
+ synchronously" — this is sync-over-async and can deadlock when the
37
+ awaited call needs to resume on a context the blocking thread is
38
+ itself occupying. Make the calling method `async` and `await` instead;
39
+ if the call truly cannot become async (a legacy synchronous interface
40
+ boundary), that is a design problem to flag, not to paper over.
41
+
42
+ ## Dependency injection
43
+
44
+ - Register a service at the narrowest lifetime that is correct:
45
+ `Singleton` for stateless or thread-safe shared state, `Scoped` for
46
+ per-request/per-unit-of-work state (a `DbContext`, a request-scoped
47
+ cache), `Transient` for cheap, stateless, or intentionally
48
+ non-shared instances. Never inject a `Scoped` service into a
49
+ `Singleton` by capturing it directly — that captures the first
50
+ request's instance for the app's lifetime; resolve it per-use via
51
+ `IServiceScopeFactory`/`IServiceProvider.CreateScope()` instead.
52
+ - Depend on an interface (`IOrderRepository`), not a concrete
53
+ implementation, at a constructor boundary that DI resolves — this is
54
+ what makes the type unit-testable without a real database/HTTP call.
55
+ - Prefer constructor injection for required dependencies; reserve
56
+ property injection for genuinely optional dependencies with a working
57
+ default, which is uncommon.
58
+
59
+ ## LINQ efficiency
60
+
61
+ - Materialize a query with `.ToList()`/`.ToArray()` before enumerating it
62
+ more than once (in a loop that also iterates it, or across a
63
+ `.Count()` followed by a `foreach`) — an `IEnumerable<T>` built from a
64
+ `IQueryable<T>`/deferred LINQ chain re-runs the whole query (or, worse,
65
+ a whole database round trip) on every enumeration unless it is
66
+ materialized once.
67
+ - Avoid stacking multiple `.Where()`/`.Select()` passes over the same
68
+ large sequence when one combined pass would do — each pass is a full
69
+ additional enumeration for an in-memory `IEnumerable<T>`.
70
+ - On an `IQueryable<T>` backed by a database (EF Core), keep filtering
71
+ (`.Where`) and projection (`.Select`) in the query itself so they
72
+ translate to SQL, rather than calling `.ToList()` early and then
73
+ filtering in memory — the latter pulls the whole table across the wire
74
+ first.
75
+ - Use `.Any()` to check for existence, never `.Count() > 0`, which forces
76
+ a full count when a short-circuiting existence check would do.
77
+
78
+ ## Disposal patterns
79
+
80
+ - Any local or field holding an `IDisposable` is wrapped in a `using`
81
+ statement/declaration (`using var conn = new SqlConnection(...);`) or
82
+ disposed in a `Dispose()`/`finally` block — never left for the
83
+ finalizer, which is non-deterministic and, for something like an open
84
+ connection or file handle, holds the resource far longer than needed.
85
+ - Use `await using` for an `IAsyncDisposable` (most modern EF Core/
86
+ streaming/channel types expose one) so disposal itself is awaited
87
+ instead of blocking a thread on synchronous `Dispose()`.
88
+ - A class that owns one or more `IDisposable`/`IAsyncDisposable`
89
+ fields implements `IDisposable`/`IAsyncDisposable` itself and disposes
90
+ them in its own `Dispose`/`DisposeAsync`, rather than requiring every
91
+ caller to know which of its fields need cleanup.
92
+
93
+ ## Anti-patterns to flag
94
+
95
+ - A "god service"/"utils" static class that accumulates unrelated
96
+ helper methods instead of being split along what the code actually
97
+ does — the same smell as a Go "god package," expressed as a static
98
+ class here.
99
+ - Catching `Exception` broadly and continuing as if nothing happened,
100
+ instead of catching the specific exception type a call can throw (or
101
+ letting an unexpected one propagate) — see `rules/security.mdc` and
102
+ `dotnet-code-review`'s focus list for the review-time version of this
103
+ check.
104
+ - Exposing a mutable `List<T>`/collection field directly as a public
105
+ property instead of `IReadOnlyList<T>`/`IReadOnlyCollection<T>` (or a
106
+ defensive copy), which lets any caller mutate internal state through a
107
+ reference they were only meant to read.
@@ -0,0 +1,86 @@
1
+ ---
2
+ extends: common
3
+ paths: ["**/*.cs"]
4
+ metadata:
5
+ origin: authored
6
+ ---
7
+
8
+ # C#/.NET security
9
+
10
+ Narrows `core-common-rules`' stack-agnostic security rules to
11
+ C#/.NET-specific, OWASP-relevant risks and the safe framework API to use
12
+ instead. Applies only to `*.cs` files.
13
+
14
+ ## SQL and data access
15
+
16
+ - Use parameterized queries for any SQL built from user-influenced
17
+ input: `SqlCommand.Parameters.Add(...)`/`AddWithValue(...)` with `@`
18
+ placeholders, or an ORM's parameterized query API (EF Core LINQ,
19
+ Dapper's parameter objects) — never `string.Format`/interpolation/
20
+ concatenation to build `CommandText` with a value that came from a
21
+ caller. `$"SELECT * FROM Users WHERE Name = '{name}'"` is a SQL
22
+ injection sink even when it "looks like" it is just formatting.
23
+ - An ORM's raw-SQL escape hatch (EF Core's `FromSqlRaw`, Dapper's raw
24
+ string execution) carries the same rule: pass user-influenced values
25
+ as parameters, never interpolated directly into the raw SQL string.
26
+ Prefer `FromSqlInterpolated`, which parameterizes `$"..."`
27
+ interpolation holes automatically, over `FromSqlRaw` when the query
28
+ needs any caller-supplied value at all.
29
+
30
+ ## Deserialization of untrusted data
31
+
32
+ - Never use `BinaryFormatter` to deserialize data from any source you do
33
+ not fully trust (or at all, on a current .NET version) — it is banned
34
+ by Microsoft for security reasons because a crafted payload can
35
+ execute arbitrary code during deserialization, and it is obsolete/
36
+ removed on modern .NET.
37
+ - Deserialize untrusted JSON with `System.Text.Json` using an explicit,
38
+ known target type — avoid `TypeNameHandling`-style polymorphic
39
+ deserialization from an untrusted source (whether via
40
+ `Newtonsoft.Json`'s `TypeNameHandling.Auto`/`All` or a custom
41
+ `$type`-driven resolver), which lets the payload itself choose what
42
+ type gets constructed.
43
+ - Validate/allowlist any type passed to `XmlSerializer`/`DataContractSerializer`
44
+ from untrusted input the same way; do not resolve a type name found
45
+ inside the untrusted payload without an allowlist.
46
+
47
+ ## Secrets and configuration
48
+
49
+ - Never hard-code a connection string, API key, or signing secret in
50
+ source or commit it in `appsettings.json` — use User Secrets
51
+ (`dotnet user-secrets`) for local development and environment
52
+ variables, Azure Key Vault, or another secret store the project has
53
+ already standardized on for anything beyond local dev.
54
+ - A connection string or secret read from `IConfiguration` should come
55
+ from an environment-specific provider (environment variables, a
56
+ secret manager), not a value checked into `appsettings.json` or
57
+ `appsettings.Development.json` that happens to work.
58
+
59
+ ## Path and file handling
60
+
61
+ - Never build a filesystem path for an untrusted, caller-supplied
62
+ filename/segment with plain string concatenation and open it directly
63
+ — validate the resulting path stays under the intended root (e.g.
64
+ resolve with `Path.GetFullPath` and confirm it starts with the root's
65
+ full path, or use an allowlist of known filenames) before calling
66
+ `File.Open`/`File.ReadAllText`/etc.; `Path.Combine` alone does not stop
67
+ a `..`-containing segment from climbing outside the intended
68
+ directory.
69
+
70
+ ## Web and transport (ASP.NET Core)
71
+
72
+ - Never render user-influenced content into an HTML response outside of
73
+ Razor's default auto-encoding (`Html.Raw`/`@Html.Raw` on
74
+ caller-supplied content is an XSS vector) — let the framework's
75
+ default encoding do its job unless the content is provably
76
+ pre-sanitized.
77
+ - Never disable TLS certificate validation
78
+ (`ServerCertificateCustomValidationCallback` returning `true`
79
+ unconditionally, or `HttpClientHandler.ServerCertificateCustomValidationCallback`
80
+ bypassing validation) outside a short-lived local test, and never ship
81
+ it that way.
82
+ - Use the framework's built-in antiforgery support
83
+ (`[ValidateAntiForgeryToken]`/`IAntiforgery`) for any state-changing
84
+ endpoint reachable from a browser session, and rely on ASP.NET Core
85
+ Identity/`PasswordHasher<TUser>` (or an equivalent vetted library) for
86
+ password hashing — never a custom hash/roll-your-own scheme.
@@ -0,0 +1,89 @@
1
+ ---
2
+ extends: common
3
+ paths: ["**/*.cs"]
4
+ metadata:
5
+ origin: authored
6
+ ---
7
+
8
+ # C#/.NET testing
9
+
10
+ Narrows `core-common-rules`' stack-agnostic testing rules to xUnit/NUnit
11
+ convention and disciplined mocking with Moq/NSubstitute. Applies only to
12
+ `*.cs` files.
13
+
14
+ ## Framework and layout
15
+
16
+ - Match whichever test framework the project already uses (xUnit or
17
+ NUnit are both common; do not introduce a second one into a project
18
+ that has standardized on the other). xUnit: `[Fact]` for a single
19
+ case, `[Theory]` + `[InlineData(...)]`/`[MemberData(...)]` for
20
+ parameterized cases. NUnit: `[Test]` and `[TestCase(...)]` play the
21
+ same roles.
22
+ - Tests live in a mirrored `*.Tests`/`*.Test` project (the standard .NET
23
+ convention: `Orders/` production code, `Orders.Tests/` its tests), not
24
+ inside the production project itself.
25
+ - Name a test method for behavior, not implementation:
26
+ `MethodName_Scenario_ExpectedResult`
27
+ (`Validate_MissingOrderId_ThrowsArgumentException`) reads as a
28
+ specification; `Test1`/`ValidateTest` does not.
29
+
30
+ ## Table-driven (`Theory`/`TestCase`) tests
31
+
32
+ - Use `[Theory]` + `[InlineData(...)]` (xUnit) or `[TestCase(...)]`
33
+ (NUnit) for a set of cases that exercise the same method with
34
+ different inputs/expected outputs, instead of copy-pasting a near-
35
+ identical `[Fact]`/`[Test]` per case — each data row becomes its own
36
+ reported test result, so a failure names exactly which input failed.
37
+ - Reach for `[MemberData(nameof(Cases))]`/a `TestCaseSource` when a case
38
+ needs a non-primitive value (an object, a collection) that attribute
39
+ arguments cannot express directly.
40
+
41
+ ## Async tests
42
+
43
+ - A test that exercises `async` production code is itself `async Task`
44
+ (`public async Task Validate_ValidOrder_ReturnsSuccess()`), and awaits
45
+ the call under test — never `async void` (the test runner cannot await
46
+ it, and any assertion failure or exception inside it is lost instead
47
+ of failing the test) and never a blocking `.Result`/`.Wait()` on the
48
+ Task under test as a substitute for `await`.
49
+
50
+ ## Mocking boundaries: mock the seam, not the internals
51
+
52
+ - Mock/stub an **external dependency the unit under test talks to
53
+ through an interface** — an `HttpClient`-backed API client behind an
54
+ interface, a repository interface wrapping the database, a clock/time
55
+ provider, a message publisher — using Moq (`Mock<IThing>`) or
56
+ NSubstitute (`Substitute.For<IThing>()`).
57
+ - Do **not** mock an internal collaborator that is itself part of what
58
+ the test is supposed to verify — a private helper method, a concrete
59
+ value object, a piece of the same class's own logic split into another
60
+ method for readability. Mocking one layer too deep proves only that
61
+ the mock returns what you told it to, not that the unit under test
62
+ actually behaves correctly; it also makes the test brittle to harmless
63
+ internal refactors that change nothing about observable behavior.
64
+ - The question to ask before adding a mock: "does the real system cross
65
+ a process/network/disk boundary here (HTTP, database, filesystem,
66
+ clock, random), or am I mocking a class my own code owns and could
67
+ just construct for real?" Only the former earns a mock.
68
+ - Verify a mock's interaction (`mock.Verify(...)`/
69
+ `receivedCall.Received()`) only for a call whose occurrence is itself
70
+ part of the contract being tested (e.g. "an email is sent exactly
71
+ once") — do not add interaction verification for calls that are
72
+ incidental implementation detail, which locks the test to today's
73
+ internal wiring.
74
+
75
+ ## Assertions and determinism
76
+
77
+ - Use the project's already-adopted assertion style (xUnit's built-in
78
+ `Assert`, or FluentAssertions if already a dependency) — do not
79
+ introduce a second assertion library into a project that has
80
+ standardized on one.
81
+ - Never synchronize with `Thread.Sleep`/`Task.Delay` to wait for an
82
+ async operation or background work to finish — await the actual
83
+ `Task`, or use a real synchronization primitive
84
+ (`TaskCompletionSource`, a cancellation-bounded wait) so the test is
85
+ deterministic and not flaky under load.
86
+ - Reset/dispose any shared test fixture (`IClassFixture<T>`, a
87
+ `WebApplicationFactory<TEntryPoint>` for integration tests) between
88
+ cases that mutate shared state, so one test's leftovers cannot change
89
+ another's result depending on run order.
@@ -0,0 +1,143 @@
1
+ ---
2
+ name: dotnet-build-fix
3
+ description: "Use when dotnet build/dotnet test fails, or a NuGet restore is broken -- resolves package/target-framework mismatches, compiler errors, nullable-annotation warnings, analyzer/StyleCop findings, and a failing test, with the smallest root-cause fix rather than a suppression."
4
+ triggers:
5
+ - "dotnet build is failing"
6
+ - "fix this NuGet package restore error"
7
+ - "resolve this nullable reference type warning"
8
+ - "dotnet build analyzer warning"
9
+ - "StyleCop is failing on this C# file"
10
+ - "dotnet test is failing, fix the build"
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
+ # .NET build fix
20
+
21
+ Resolve a `dotnet build`/`dotnet test` failure, a NuGet restore error, a
22
+ compiler error, a nullable-reference-type warning, an analyzer/StyleCop
23
+ finding, or a failing test — with the smallest change that fixes the
24
+ actual root cause. `rules/coding-style.mdc` and `rules/security.mdc`
25
+ govern what a "correct" fix looks like; this skill never reaches for a
26
+ suppression instead of a fix.
27
+
28
+ ## Workflow
29
+
30
+ ### Step 1: Reproduce and classify
31
+
32
+ ```bash
33
+ dotnet build
34
+ dotnet test
35
+ ```
36
+
37
+ Read the exact error/warning text and classify it:
38
+
39
+ - **Compile error** (undefined symbol, type mismatch, wrong overload
40
+ resolution).
41
+ - **Package/restore** (NuGet version conflict, a `PackageReference`
42
+ pointing at a version that does not exist, a target-framework
43
+ mismatch between a project and one of its package dependencies).
44
+ - **Nullable warning** (`CS8600`-`CS8655` range — a possible null
45
+ reference, an unannotated parameter used where `?` was expected).
46
+ - **Analyzer/style finding** (a Roslyn analyzer, StyleCop, or an
47
+ `.editorconfig`-driven `IDE`/`CA` rule).
48
+ - **Failing test** (an assertion failure or unhandled exception under
49
+ `dotnet test`).
50
+
51
+ ### Step 2: Fix by category
52
+
53
+ **Package/restore:** run `dotnet restore` when the lock file is simply
54
+ stale. For a genuine version conflict, check `dotnet list package
55
+ --include-transitive` to see what is pulling in the conflicting version
56
+ before bumping anything by hand. Only pin a package to an explicit
57
+ version for a real, understood reason (a known-bad release, an
58
+ intentional hold) — never to make a conflict disappear without
59
+ understanding it, and say so in the report either way.
60
+
61
+ **Compile error:** read the exact overload/type the compiler expected
62
+ versus what was supplied; fix the call site or the signature, whichever
63
+ is actually wrong relative to the feature's intent — do not change a
64
+ public signature just to make one call site compile if other callers
65
+ would break.
66
+
67
+ **Nullable warning:** fix the actual null path — add a real null check,
68
+ give the value a non-null initializer, or restructure so the compiler's
69
+ flow analysis can see the invariant. Only annotate the type `?` if the
70
+ value can genuinely be null by design. Never resolve the warning with
71
+ the null-forgiving operator (`value!`) as a substitute for an actual
72
+ guard, and never widen the fix into `<Nullable>disable</Nullable>` for
73
+ the file or project.
74
+
75
+ **Analyzer/StyleCop finding:** fix the underlying issue the rule names
76
+ (the real formatting/ordering/pattern it wants). Never add
77
+ `#pragma warning disable` or a `[SuppressMessage(...)]` attribute whose
78
+ only purpose is to make the analyzer stop complaining without addressing
79
+ what it found.
80
+
81
+ **Failing test:** read the assertion failure or stack trace; fix the
82
+ production code if the test correctly caught a real bug, or fix the test
83
+ if its expectation was wrong — state which one you concluded and why in
84
+ the report, never silently delete or skip the test to reach green.
85
+
86
+ ### Step 3: Verify
87
+
88
+ ```bash
89
+ dotnet build
90
+ dotnet test
91
+ dotnet format --verify-no-changes
92
+ ```
93
+
94
+ All must exit 0 before reporting done.
95
+
96
+ ### Step 4: Report
97
+
98
+ ```
99
+ Fixed: NuGet version conflict between Package.A 3.0 and Package.B's
100
+ transitive dependency on Package.A 2.5
101
+ - Root cause: Package.B pinned an older Package.A that Package.A 3.0's
102
+ breaking change conflicted with; updated Package.B instead of
103
+ downgrading Package.A
104
+ - dotnet build/test/format all pass
105
+ ```
106
+
107
+ State the root cause in one sentence, not just "fixed the error."
108
+
109
+ ## Rules
110
+
111
+ - Find and fix the smallest change that addresses the actual root
112
+ cause — never widen a fix beyond what the failure requires.
113
+ - NEVER add `#pragma warning disable` or `[SuppressMessage(...)]` to
114
+ silence an analyzer/StyleCop finding instead of fixing what it found.
115
+ - NEVER add `<Nullable>disable</Nullable>` (project- or file-wide) or
116
+ reach for the null-forgiving operator (`value!`) as a substitute for
117
+ an actual null-safety fix.
118
+ - NEVER pin or downgrade a NuGet package to route around a real
119
+ incompatibility without understanding and stating why in the report.
120
+ - NEVER delete or skip a failing test to reach a green build.
121
+
122
+ ## Red Flags
123
+
124
+ | Rationalization | Why it is wrong |
125
+ |---|---|
126
+ | "I'll add `#pragma warning disable CS8602` around this block" | Silences the finding without fixing the possible-null-dereference it caught; add the actual null check instead |
127
+ | "This nullable warning is annoying, I'll just add `!` here" | The null-forgiving operator tells the compiler to trust you without verifying anything; use it only at a boundary you can justify in a comment, never as a default fix |
128
+ | "I'll just downgrade this package to the version that used to work" | Papers over whatever actually changed without understanding it; check `dotnet list package --include-transitive` and fix the real conflict |
129
+ | "This test keeps failing, I'll mark it `[Fact(Skip = \"flaky\")]`" | Hides a real regression instead of fixing it; find out whether the test or the code is wrong before touching either |
130
+
131
+ ## Verification
132
+
133
+ Do not report the fix done until all of the following hold:
134
+
135
+ - `dotnet build`, `dotnet test`, and `dotnet format --verify-no-changes`
136
+ all exit 0.
137
+ - The change is the smallest one that addresses the stated root cause —
138
+ no unrelated files touched.
139
+ - No `#pragma warning disable`, `[SuppressMessage(...)]`,
140
+ `<Nullable>disable</Nullable>`, or unexplained null-forgiving operator
141
+ was added as part of the fix.
142
+ - The report states the root cause in one sentence, not just "build now
143
+ passes."