sdg-agents 1.0.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 (130) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +161 -0
  3. package/package.json +88 -0
  4. package/src/assets/dev-guides/agent-deep-flow.md +139 -0
  5. package/src/assets/dev-guides/prompt-tracks/00-lite-mode/01-project-scope-and-mvp.md +45 -0
  6. package/src/assets/dev-guides/prompt-tracks/00-lite-mode/02-stack-and-data-model.md +56 -0
  7. package/src/assets/dev-guides/prompt-tracks/00-lite-mode/03-external-integrations.md +44 -0
  8. package/src/assets/dev-guides/prompt-tracks/00-lite-mode/04-launch-checklist.md +26 -0
  9. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/00-foundation/01-project-vision.md +77 -0
  10. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/00-foundation/02-problem-definition.md +63 -0
  11. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/00-foundation/03-success-metrics.md +48 -0
  12. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/00-foundation/05-engineering-culture-and-silos.md +41 -0
  13. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/00-foundation/06-foundation-approval.md +57 -0
  14. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/01-setup/01-tech-stack.md +62 -0
  15. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/01-setup/02-local-environment.md +50 -0
  16. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/01-setup/03-version-control-and-tracking.md +65 -0
  17. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/01-setup/04-hello-world-validation.md +37 -0
  18. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/01-setup/05-setup-approval.md +61 -0
  19. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/01-folder-structure.md +49 -0
  20. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/02-design-patterns.md +59 -0
  21. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/03-apis-and-communication.md +55 -0
  22. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/04-security-and-access.md +63 -0
  23. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/05-security-audit.md +64 -0
  24. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/06-design-system-ux.md +72 -0
  25. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/07-ai-strategy.md +72 -0
  26. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/08-observability.md +65 -0
  27. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/09-technical-documentation.md +64 -0
  28. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/10-infrastructure-and-costs.md +40 -0
  29. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/11-data-privacy-compliance.md +41 -0
  30. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/12-performance-and-scale.md +46 -0
  31. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/13-disaster-recovery.md +39 -0
  32. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/14-architecture-approval.md +81 -0
  33. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/03-delivery/01-ci-cd-pipeline.md +49 -0
  34. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/03-delivery/02-quality-standards.md +46 -0
  35. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/03-delivery/03-delivery-approval.md +58 -0
  36. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/04-features/00-feature-template.md +30 -0
  37. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/04-features/01-context-and-scope.md +46 -0
  38. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/04-features/02-business-rules.md +39 -0
  39. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/04-features/03-architecture-and-design.md +50 -0
  40. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/04-features/04-testing-strategy.md +48 -0
  41. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/04-features/05-implementation-plan.md +45 -0
  42. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/04-features/06-feature-approval.md +46 -0
  43. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/05-evolution/01-rfcs-and-changes.md +63 -0
  44. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/05-evolution/02-incident-postmortems.md +64 -0
  45. package/src/assets/dev-guides/prompt-tracks/01-new-evolution/05-evolution/03-adr-template.md +66 -0
  46. package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/00-foundation/01-legacy-vision.md +73 -0
  47. package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/00-foundation/02-foundation-approval.md +53 -0
  48. package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/01-analysis/01-tech-debt-inventory.md +55 -0
  49. package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/01-analysis/02-security-audit.md +63 -0
  50. package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/01-analysis/03-regression-baseline.md +49 -0
  51. package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/01-analysis/04-analysis-approval.md +65 -0
  52. package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/02-strategy/01-modernization-approach.md +60 -0
  53. package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/02-strategy/02-versioning-and-coexistence.md +57 -0
  54. package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/02-strategy/03-new-demands-triage.md +41 -0
  55. package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/02-strategy/04-strategy-approval.md +56 -0
  56. package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/03-refactor/01-cleanup-backlog.md +45 -0
  57. package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/03-refactor/02-refactor-approval.md +53 -0
  58. package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/04-migration/01-migration-roadmap.md +47 -0
  59. package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/04-migration/02-data-sync-strategy.md +56 -0
  60. package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/04-migration/03-migration-approval.md +55 -0
  61. package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/05-sunset/01-decommission-plan.md +47 -0
  62. package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/05-sunset/02-sunset-approval.md +60 -0
  63. package/src/assets/dev-guides/prompt-tracks/README.md +144 -0
  64. package/src/assets/dev-guides/software-development-lifecycle-sdlc.md +147 -0
  65. package/src/assets/dev-guides/spec-driven-dev-guide.md +81 -0
  66. package/src/assets/dev-guides/ui-prompt-guide.md +181 -0
  67. package/src/assets/img/sdg-agents-icon-dark.svg +55 -0
  68. package/src/assets/img/sdg-agents-icon-light.svg +55 -0
  69. package/src/assets/img/sdg-agents-menu-v1.png +0 -0
  70. package/src/assets/img/sdg-icon.svg +110 -0
  71. package/src/assets/instructions/commands/sdg-docs.md +69 -0
  72. package/src/assets/instructions/commands/sdg-feat.md +45 -0
  73. package/src/assets/instructions/commands/sdg-fix.md +41 -0
  74. package/src/assets/instructions/commands/sdg-land.md +128 -0
  75. package/src/assets/instructions/competencies/backend.md +353 -0
  76. package/src/assets/instructions/competencies/frontend.md +439 -0
  77. package/src/assets/instructions/core/agent-roles.md +104 -0
  78. package/src/assets/instructions/core/api-design.md +65 -0
  79. package/src/assets/instructions/core/ci-cd.md +144 -0
  80. package/src/assets/instructions/core/cloud.md +63 -0
  81. package/src/assets/instructions/core/code-style.md +144 -0
  82. package/src/assets/instructions/core/containers.md +115 -0
  83. package/src/assets/instructions/core/data-access.md +119 -0
  84. package/src/assets/instructions/core/engineering-standards.md +502 -0
  85. package/src/assets/instructions/core/naming.md +136 -0
  86. package/src/assets/instructions/core/observability.md +73 -0
  87. package/src/assets/instructions/core/security-pipeline.md +209 -0
  88. package/src/assets/instructions/core/security.md +45 -0
  89. package/src/assets/instructions/core/sql-style.md +138 -0
  90. package/src/assets/instructions/core/staff-dna.md +72 -0
  91. package/src/assets/instructions/core/testing-principles.md +212 -0
  92. package/src/assets/instructions/core/ui/architecture.md +171 -0
  93. package/src/assets/instructions/core/ui/design-thinking.md +319 -0
  94. package/src/assets/instructions/core/ui/presets.md +200 -0
  95. package/src/assets/instructions/core/ui/standards.md +144 -0
  96. package/src/assets/instructions/core/writing-soul.md +82 -0
  97. package/src/assets/instructions/flavors/legacy/principles.md +64 -0
  98. package/src/assets/instructions/flavors/lite/principles.md +39 -0
  99. package/src/assets/instructions/flavors/mvc/principles.md +124 -0
  100. package/src/assets/instructions/flavors/vertical-slice/principles.md +178 -0
  101. package/src/assets/instructions/idioms/csharp/patterns.md +367 -0
  102. package/src/assets/instructions/idioms/flutter/patterns.md +97 -0
  103. package/src/assets/instructions/idioms/go/patterns.md +193 -0
  104. package/src/assets/instructions/idioms/java/patterns.md +233 -0
  105. package/src/assets/instructions/idioms/javascript/patterns.md +223 -0
  106. package/src/assets/instructions/idioms/kotlin/patterns.md +94 -0
  107. package/src/assets/instructions/idioms/python/patterns.md +185 -0
  108. package/src/assets/instructions/idioms/rust/patterns.md +189 -0
  109. package/src/assets/instructions/idioms/scripts/patterns.md +81 -0
  110. package/src/assets/instructions/idioms/sql/patterns.md +109 -0
  111. package/src/assets/instructions/idioms/swift/patterns.md +97 -0
  112. package/src/assets/instructions/idioms/typescript/patterns.md +304 -0
  113. package/src/assets/instructions/idioms/vbnet/patterns.md +96 -0
  114. package/src/assets/instructions/idioms/vbnet-legacy/patterns.md +104 -0
  115. package/src/assets/instructions/templates/workflow.md +244 -0
  116. package/src/assets/instructions/workflows/governance.md +162 -0
  117. package/src/engine/bin/add-idiom.mjs +186 -0
  118. package/src/engine/bin/index.mjs +226 -0
  119. package/src/engine/config/stack-display.mjs +75 -0
  120. package/src/engine/config/stack-versions.mjs +76 -0
  121. package/src/engine/lib/cli-parser.mjs +80 -0
  122. package/src/engine/lib/display-utils.mjs +20 -0
  123. package/src/engine/lib/fs-utils.mjs +99 -0
  124. package/src/engine/lib/instruction-assembler.mjs +487 -0
  125. package/src/engine/lib/manifest-utils.mjs +128 -0
  126. package/src/engine/lib/prompt-utils.mjs +137 -0
  127. package/src/engine/lib/result-utils.mjs +14 -0
  128. package/src/engine/lib/ruleset-injector.mjs +183 -0
  129. package/src/engine/lib/ui-utils.mjs +216 -0
  130. package/src/engine/lib/wizard.mjs +472 -0
@@ -0,0 +1,367 @@
1
+ # C# — Project Conventions
2
+
3
+ > Universal principles (naming, composition, DRY, performance, security) are in `../../core/staff-dna.md`.
4
+ > This file contains only decisions specific to this language and stack.
5
+
6
+ <ruleset name="CSharpConventions">
7
+
8
+ ## Error Handling
9
+
10
+ - **Strategy**: Result Pattern in the domain (`Result<T>` via sealed record); exceptions only for unexpected failures (infra/runtime)
11
+ - **Propagation**: Result is explicitly returned in business flows; exceptions bubble up to the global middleware
12
+ - **Domain errors**: `sealed record ApiError(string Message, string Code)`; pattern matching for mapping
13
+ - **Never**: `throw` for business rules; `.Result` / `.Wait()` in async code; leak internal details
14
+
15
+ ### Result Pattern
16
+
17
+ > <rule name="ResultPatternCSharp">
18
+
19
+ ```csharp
20
+ public sealed record ApiError(string Message, string Code);
21
+
22
+ public record Result<T>(bool IsSuccess, bool IsFailure, T? Value, ApiError? Error)
23
+ {
24
+ public static Result<T> Success(T value) => new(true, false, value, null);
25
+ public static Result<T> Fail(string msg, string code) => new(false, true, default, new ApiError(msg, code));
26
+ }
27
+
28
+ // Pattern matching
29
+ public IActionResult GetOrder(Result<Order> result) => result switch
30
+ {
31
+ { IsSuccess: true, Value: var o } => Ok(o),
32
+ { IsFailure: true, Error: var e } => MapError(e),
33
+ _ => Problem()
34
+ };
35
+ ```
36
+
37
+ > </rule>
38
+
39
+ ---
40
+
41
+ ## HTTP & API
42
+
43
+ - **Framework**: ASP.NET Core — Minimal APIs (preferred) or Controllers when necessary
44
+ - **Style**: API First + BFF
45
+ - **Route organization**: Vertical slice per feature (`Features/Orders/OrderEndpoints.cs`)
46
+ - **Middleware/hooks**: Auth in the pipeline; validation at the boundary; centralized logging/tracing
47
+ - **DI**: Constructor injection via primary constructors; registration in `Program.cs`; never service locator
48
+
49
+ ### Minimal API per Feature
50
+
51
+ > <rule name="MinimalAPI">
52
+
53
+ ```csharp
54
+ public static class OrderEndpoints
55
+ {
56
+ public static void MapOrderEndpoints(this WebApplication app)
57
+ {
58
+ var group = app.MapGroup("/api/orders").WithTags("Orders");
59
+ group.MapPost("/", CreateOrder);
60
+ group.MapGet("/{id:guid}", GetOrder);
61
+ }
62
+
63
+ private static async Task<IResult> CreateOrder(
64
+ CreateOrderRequest request,
65
+ OrderService service,
66
+ CancellationToken ct)
67
+ {
68
+ var result = await service.CreateOrderAsync(request, ct);
69
+ var httpResponse = ToEnvelope(result);
70
+ return httpResponse;
71
+ }
72
+
73
+ // Adapter — only layer that knows both Result<T> and HTTP envelope
74
+ private static IResult ToEnvelope<T>(Result<T> result) => result switch
75
+ {
76
+ { IsSuccess: true, Value: var value } =>
77
+ Results.Ok(new { success = true, error = (object?)null, data = value }),
78
+ { IsFailure: true, Error: var error } =>
79
+ Results.Json(
80
+ new { success = false, error = new { code = error!.Code, message = error.Message }, data = (object?)null },
81
+ statusCode: MapStatusCode(error.Code)
82
+ ),
83
+ _ => Results.Problem()
84
+ };
85
+
86
+ private static int MapStatusCode(string code) => code switch
87
+ {
88
+ "NOT_FOUND" => 404,
89
+ "UNAUTHORIZED" => 401,
90
+ "FORBIDDEN" => 403,
91
+ "CONFLICT" => 409,
92
+ "INVALID_INPUT" => 400,
93
+ _ => 422
94
+ };
95
+ }
96
+ ```
97
+
98
+ > </rule>
99
+
100
+ ### Dependency Injection
101
+
102
+ > <rule name="CSharpDI">
103
+
104
+ ```csharp
105
+ public class OrderService(IOrderRepository repo, INotifier notifier)
106
+ {
107
+ public async Task<Result<Order>> CreateOrderAsync(CreateOrderRequest request, CancellationToken ct = default)
108
+ {
109
+ var order = await repo.SaveAsync(Order.From(request), ct);
110
+ await notifier.SendAsync(order, ct);
111
+ return Result<Order>.Success(order);
112
+ }
113
+ }
114
+
115
+ // Program.cs
116
+ builder.Services.AddScoped<IOrderRepository, PostgresOrderRepository>();
117
+ builder.Services.AddScoped<OrderService>();
118
+ ```
119
+
120
+ > </rule>
121
+
122
+ ---
123
+
124
+ ## Testing
125
+
126
+ - **Framework**: xUnit + FluentAssertions + NSubstitute
127
+ - **Style**: Flat, behavior-oriented
128
+ - **Naming**: `MethodName_ShouldDoX_WhenY`
129
+ - **Mocks**: NSubstitute for external dependencies; never mock the domain
130
+ - **What to test**: Business rules, error cases (Result), API contracts
131
+
132
+ ### Unit Testing
133
+
134
+ > <rule name="CSharpTesting">
135
+
136
+ ```csharp
137
+ public class OrderServiceTests
138
+ {
139
+ private readonly IOrderRepository _repo = Substitute.For<IOrderRepository>();
140
+ private readonly INotifier _notifier = Substitute.For<INotifier>();
141
+ private readonly OrderService _sut;
142
+
143
+ public OrderServiceTests() => _sut = new OrderService(_repo, _notifier);
144
+
145
+ [Fact]
146
+ public async Task CreateOrder_ShouldReturnSuccess()
147
+ {
148
+ _repo.SaveAsync(Arg.Any<Order>(), Arg.Any<CancellationToken>())
149
+ .Returns(new Order(Guid.NewGuid(), "prod-1", 2));
150
+
151
+ var result = await _sut.CreateOrderAsync(new CreateOrderRequest("prod-1", 2));
152
+
153
+ result.IsSuccess.Should().BeTrue();
154
+ result.Value!.ProductId.Should().Be("prod-1");
155
+ await _notifier.Received(1).SendAsync(Arg.Any<Order>(), Arg.Any<CancellationToken>());
156
+ }
157
+ }
158
+ ```
159
+
160
+ > </rule>
161
+
162
+ ### Integration Testing
163
+
164
+ > <rule name="CSharpIntegration">
165
+
166
+ ```csharp
167
+ public class OrdersApiTests(WebApplicationFactory<Program> factory)
168
+ : IClassFixture<WebApplicationFactory<Program>>
169
+ {
170
+ private readonly HttpClient _client = factory.CreateClient();
171
+
172
+ [Fact]
173
+ public async Task CreateOrder_ReturnsCreated()
174
+ {
175
+ var response = await _client.PostAsJsonAsync("/api/orders", new { ProductId = "abc", Quantity = 2 });
176
+ response.StatusCode.Should().Be(HttpStatusCode.Created);
177
+ }
178
+ }
179
+ ```
180
+
181
+ > </rule>
182
+
183
+ ---
184
+
185
+ ## Types & Contracts
186
+
187
+ - **DTOs**: `record` for Request/Response; each layer has its own type; never expose EF entities
188
+ - **Validation**: Validation at the boundary — factory method with Result, FluentValidation when rules are complex
189
+ - **Async**: All I/O methods are `async Task<T>`; `Async` suffix; `CancellationToken` in public APIs; never `.Result` / `.Wait()`
190
+ - **Naming**: PascalCase for public members; `_camelCase` for private fields; `I` prefix for interfaces; `Async` suffix for async methods
191
+
192
+ ### Records as Data Carriers
193
+
194
+ > <rule name="CSharpRecords">
195
+
196
+ ```csharp
197
+ public record CreateUserRequest(string Email, string Password);
198
+ public record UserResponse(Guid Id, string Email);
199
+
200
+ // Factory method with validation
201
+ public record CreateOrderRequest(string ProductId, int Quantity)
202
+ {
203
+ public static Result<CreateOrderRequest> Create(string productId, int quantity)
204
+ {
205
+ if (string.IsNullOrWhiteSpace(productId))
206
+ return Result<CreateOrderRequest>.Fail("Product ID is required.", "INVALID_PRODUCT_ID");
207
+ if (quantity < 1)
208
+ return Result<CreateOrderRequest>.Fail("Quantity must be at least 1.", "INVALID_QUANTITY");
209
+ return Result<CreateOrderRequest>.Success(new(productId, quantity));
210
+ }
211
+ }
212
+ ```
213
+
214
+ > </rule>
215
+
216
+ ### Async Pattern
217
+
218
+ > <rule name="CSharpAsync">
219
+
220
+ ```csharp
221
+ public async Task<Result<Dashboard>> GetDashboardAsync(string userId, CancellationToken ct = default)
222
+ {
223
+ var profileTask = _users.FindByIdAsync(userId, ct);
224
+ var ordersTask = _orders.GetRecentAsync(userId, ct);
225
+ var notificationsTask = _notifications.GetUnreadAsync(userId, ct);
226
+
227
+ await Task.WhenAll(profileTask, ordersTask, notificationsTask);
228
+
229
+ return Result<Dashboard>.Success(new Dashboard(
230
+ Profile: await profileTask,
231
+ Orders: await ordersTask,
232
+ Notifications: await notificationsTask
233
+ ));
234
+ }
235
+ ```
236
+
237
+ > </rule>
238
+
239
+ ---
240
+
241
+ ## LINQ & Collections
242
+
243
+ > Use LINQ for clarity, not cleverness. Prefer readability over chaining.
244
+
245
+ ### Core Principles
246
+
247
+ - Prefer **readability over conciseness**
248
+ - Keep queries **simple and predictable**
249
+ - Avoid complex chaining ("magic LINQ")
250
+ - LINQ is for **data transformation**, not business logic
251
+ - Use `foreach` for complex logic or accumulation (sum, total)
252
+
253
+ ### Code Flow Alignment
254
+
255
+ Keep LINQ inside the **execution step** — never mix orchestration with complex queries.
256
+
257
+ > <rule name="CSharpLINQ">
258
+
259
+ ````carousel
260
+ ```csharp
261
+ // ❌ BAD: Chained LINQ magic — logic buried, untestable, hard to extend
262
+ var result = orders
263
+ .Where(o => o.Status == "CONFIRMED" && o.CreatedAt > DateTime.UtcNow.AddDays(-30))
264
+ .GroupBy(o => o.CustomerId)
265
+ .Select(g => new { CustomerId = g.Key, Total = g.Sum(o => o.Items.Sum(i => i.Price * i.Qty)) })
266
+ .OrderByDescending(x => x.Total)
267
+ .ToList();
268
+ ```
269
+ <!-- slide -->
270
+ ```csharp
271
+ // ✅ GOOD: LINQ for 1-to-1 transform; foreach for accumulation — each step is readable
272
+ var recentOrders = orders
273
+ .Where(o => o.Status == "CONFIRMED" && o.CreatedAt > DateTime.UtcNow.AddDays(-30))
274
+ .ToList();
275
+
276
+ var summaries = recentOrders
277
+ .GroupBy(o => o.CustomerId)
278
+ .Select(group => BuildCustomerSummary(group))
279
+ .OrderByDescending(summary => summary.Total)
280
+ .ToList();
281
+
282
+ return summaries;
283
+
284
+ static CustomerSummary BuildCustomerSummary(IGrouping<Guid, Order> group)
285
+ {
286
+ decimal total = 0;
287
+ foreach (var order in group)
288
+ foreach (var item in order.Items)
289
+ {
290
+ var lineAmount = item.Price * item.Qty;
291
+ total += lineAmount;
292
+ }
293
+
294
+ var customerSummary = new CustomerSummary(group.Key, total);
295
+ return customerSummary;
296
+ }
297
+ ```
298
+ ````
299
+
300
+ > </rule>
301
+
302
+ ### Materialization (ToList / ToArray)
303
+
304
+ Materialize only when needed: multiple iterations, snapshot of data, or returning a concrete collection.
305
+
306
+ - Default → `IEnumerable<T>`
307
+ - Use `.ToList()` at boundaries (return, serialization, EF Core terminal calls)
308
+ - Never materialize prematurely inside a pipeline
309
+
310
+ ### Side Effects
311
+
312
+ LINQ must be **pure** — no side effects inside queries. Never use `.Select(u => { Log(u); return u; })`.
313
+
314
+ ---
315
+
316
+ ## Data Access — EF Core
317
+
318
+ > Data access rules in [Universal Data Access Principles](../../core/data-access.md).
319
+
320
+ ### DbContext & Tracking
321
+
322
+ > <rule name="EFContextLifetime">
323
+ > `DbContext` Scoped (one per request). `AsNoTracking()` in read-only queries. Never Singleton.
324
+
325
+ ```csharp
326
+ public async Task<IReadOnlyList<OrderSummary>> GetRecentOrdersAsync(CancellationToken ct) =>
327
+ await _context.Orders
328
+ .AsNoTracking()
329
+ .Where(o => o.CreatedAt > DateTime.UtcNow.AddDays(-30))
330
+ .Select(o => new OrderSummary(o.Id, o.Total, o.CreatedAt))
331
+ .ToListAsync(ct);
332
+ ```
333
+
334
+ > </rule>
335
+
336
+ ### Projection — Select over Full Entities
337
+
338
+ > <rule name="EFProjection">
339
+ > Always project to DTOs via `.Select()`. Never expose EF entities to the API layer.
340
+
341
+ ```csharp
342
+ public async Task<ProductDetail?> GetProductDetailAsync(Guid id, CancellationToken ct) =>
343
+ await _context.Products
344
+ .Where(p => p.Id == id)
345
+ .Select(p => new ProductDetail(p.Id, p.Name, p.Price, p.Category.Name, p.Reviews.Count))
346
+ .FirstOrDefaultAsync(ct);
347
+ ```
348
+
349
+ > </rule>
350
+
351
+ ### N+1 Prevention
352
+
353
+ > <rule name="EFNPlusOne">
354
+ > Lazy loading disabled. `.Include()` for known graphs. `AsSplitQuery()` for multiple collections.
355
+
356
+ ```csharp
357
+ var orders = await _context.Orders
358
+ .Include(o => o.Items)
359
+ .Include(o => o.Payments)
360
+ .AsSplitQuery()
361
+ .Where(o => o.CustomerId == customerId)
362
+ .ToListAsync(ct);
363
+ ```
364
+
365
+ > </rule>
366
+
367
+ </ruleset>
@@ -0,0 +1,97 @@
1
+ # Flutter (Dart) — Project Conventions
2
+
3
+ > Universal principles (naming, composition, DRY, performance, security) are in `../../core/staff-dna.md`.
4
+ > This file contains only decisions specific to this language and stack.
5
+
6
+ <ruleset name="FlutterConventions">
7
+
8
+ ## Error Handling
9
+
10
+ - **Strategy**: Result Pattern (`Result<T>`) in the domain; `throw` only for unexpected failures
11
+ - **Propagation**: Explicit Result between layers; exceptions handled at the boundary (ViewModel/Controller)
12
+ - **Domain errors**: Sealed classes via `freezed` for union types; standard structure (`code`, `message`)
13
+ - **Never**: Exception for business rules; swallow errors; expose technical errors in the UI
14
+
15
+ ### Result Pattern (Sealed Class / Freezed)
16
+
17
+ > <rule name="ResultPatternFlutter">
18
+
19
+ ```dart
20
+ @freezed
21
+ class Result<T> with _$Result<T> {
22
+ const factory Result.success(T data) = Success<T>;
23
+ const factory Result.failure(String message, String code) = Failure<T>;
24
+ }
25
+
26
+ // Usage in ViewModel
27
+ final result = await _useCase.execute(request);
28
+ state = result.when(
29
+ success: (data) => state.copyWith(data: data, isLoading: false),
30
+ failure: (msg, code) => state.copyWith(errorMessage: msg, isLoading: false),
31
+ );
32
+ ```
33
+
34
+ > </rule>
35
+
36
+ ---
37
+
38
+ ## HTTP & API
39
+
40
+ - **Style**: API First + BFF
41
+ - **Client**: Dio (preferred) or `http`; centralized and typed API client
42
+ - **Serialization**: `json_serializable` or `freezed`
43
+ - **Never**: Call API directly from the widget; mix parsing with UI
44
+
45
+ ---
46
+
47
+ ## Testing
48
+
49
+ - **Framework**: `flutter_test` / `mocktail`
50
+ - **Style**: Behavior-oriented
51
+ - **Naming**: `shouldDoXWhenY`
52
+ - **Mocks**: Mock only external I/O; domain is always real
53
+
54
+ ---
55
+
56
+ ## Types & Contracts
57
+
58
+ - **Classes**: Immutable by default (`const`, `final`)
59
+ - **Union types**: `freezed` for complex DTOs and states
60
+ - **Null safety**: Mandatory; avoid `!`
61
+ - **DTOs**: Separated from the domain; never expose internal structure
62
+ - **Validation**: At the boundary (input/API)
63
+
64
+ ---
65
+
66
+ ## Flutter-Specific Delta
67
+
68
+ - Widget = rendering only; no business logic in `build()`
69
+ - Logic outside the widget (Controller/ViewModel)
70
+ - State management with Riverpod or BLoC — no uncontrolled global state
71
+ - Strong componentization — small and focused widgets
72
+ - Organization by feature
73
+ - Naming: `camelCase` for variables/functions; `PascalCase` for classes; `snake_case` for files
74
+ - `map` is valid for pure 1-to-1 collection transforms; `for` loops are preferred for accumulation or complex logic
75
+
76
+ ### Collections & UI Lists
77
+
78
+ > <rule name="FlutterCollections">
79
+
80
+ ```dart
81
+ // ✅ map — valid for 1-to-1 transform (e.g. data to widget)
82
+ final userWidgets = users
83
+ .where((u) => u.isActive)
84
+ .map((u) => UserCard(user: u))
85
+ .toList();
86
+
87
+ // ✅ for loop — preferred for accumulation
88
+ double total = 0;
89
+ for (final item in order.items) {
90
+ final lineAmount = item.qty * item.price;
91
+ total += lineAmount;
92
+ }
93
+ ```
94
+
95
+ > </rule>
96
+
97
+ </ruleset>
@@ -0,0 +1,193 @@
1
+ # Go — Project Conventions
2
+
3
+ > Universal principles (naming, composition, DRY, performance, security) are in `../../core/staff-dna.md`.
4
+ > This file contains only decisions specific to this language and stack.
5
+
6
+ <ruleset name="GoConventions">
7
+
8
+ ## Error Handling
9
+
10
+ - **Strategy**: Explicit `(T, error)`; no exceptions, no `panic` for business logic
11
+ - **Propagation**: Errors are explicitly returned; wrapping with `fmt.Errorf("ctx: %w", err)` for traceability
12
+ - **Domain errors**: Sentinel `var` for simple cases; custom `type` with `Error()` when necessary; `errors.Is` / `errors.As` for comparison
13
+ - **Never**: Ignore errors (`_`); `panic` for business rules; swallow errors
14
+ - **Global Handling**: HTTP handler converts `error` → response; structured logging (`slog`)
15
+
16
+ ### Result Pattern (Go Style)
17
+
18
+ > <rule name="ResultPatternGo">
19
+
20
+ ```go
21
+ type AppError struct {
22
+ Message string
23
+ Code string
24
+ }
25
+
26
+ func (e *AppError) Error() string {
27
+ return fmt.Sprintf("[%s] %s", e.Code, e.Message)
28
+ }
29
+
30
+ func FindUser(ctx context.Context, id string) (*User, error) {
31
+ if id == "" {
32
+ return nil, &AppError{Message: "id is required", Code: "INVALID_ID"}
33
+ }
34
+ user, err := db.QueryUser(ctx, id)
35
+ if err != nil {
36
+ return nil, fmt.Errorf("FindUser: %w", err)
37
+ }
38
+ return user, nil
39
+ }
40
+ ```
41
+
42
+ > </rule>
43
+
44
+ ---
45
+
46
+ ## HTTP & API
47
+
48
+ - **Framework**: `net/http` (preferred) or Echo/Fiber when necessary
49
+ - **Style**: API First + BFF
50
+ - **Route organization**: Vertical slice per feature/package; avoid generic packages (`utils`, `common`)
51
+ - **Middleware/hooks**: Auth via middleware; validation at the boundary; logging/tracing in the pipeline
52
+ - **DI**: Manual — explicit dependency passing via structs; constructor functions (`New...`); avoid containers
53
+
54
+ ### Interface Design
55
+
56
+ > <rule name="InterfaceDesign">
57
+ > Interfaces defined by the consumer, not the provider. Small interfaces (1-2 methods). `-er` suffix for single-method.
58
+
59
+ ```go
60
+ type UserFinder interface {
61
+ FindByID(ctx context.Context, id string) (*User, error)
62
+ }
63
+
64
+ type OrderService struct {
65
+ users UserFinder
66
+ }
67
+
68
+ func NewOrderService(users UserFinder) *OrderService {
69
+ return &OrderService{users: users}
70
+ }
71
+ ```
72
+
73
+ > </rule>
74
+
75
+ ### Context Propagation
76
+
77
+ > <rule name="ContextPropagation">
78
+ > `context.Context` is the first parameter of every function that performs I/O or can be canceled.
79
+
80
+ ```go
81
+ func (s *Service) CreateOrder(ctx context.Context, input CreateOrderRequest) (*Order, error) {
82
+ user, err := s.users.FindByID(ctx, input.UserID)
83
+ if err != nil {
84
+ return nil, fmt.Errorf("CreateOrder: %w", err)
85
+ }
86
+ return s.orders.Save(ctx, newOrder(user, input))
87
+ }
88
+ ```
89
+
90
+ > </rule>
91
+
92
+ ---
93
+
94
+ ## Testing
95
+
96
+ - **Framework**: built-in (`testing`)
97
+ - **Style**: Table-driven tests (preferred)
98
+ - **Naming**: `TestShouldDoXWhenY`
99
+ - **Mocks**: Small interfaces for abstraction; mock only external I/O; avoid heavy frameworks
100
+
101
+ ### Table-Driven Tests
102
+
103
+ > <rule name="TableTests">
104
+
105
+ ```go
106
+ func TestParseVersion(t *testing.T) {
107
+ tests := []struct {
108
+ name string
109
+ input string
110
+ want float64
111
+ wantErr bool
112
+ }{
113
+ {name: "simple", input: "10", want: 10},
114
+ {name: "decimal", input: "3.14", want: 3.14},
115
+ {name: "empty", input: "", wantErr: true},
116
+ }
117
+
118
+ for _, tt := range tests {
119
+ t.Run(tt.name, func(t *testing.T) {
120
+ got, err := ParseVersion(tt.input)
121
+ if (err != nil) != tt.wantErr {
122
+ t.Errorf("error = %v, wantErr %v", err, tt.wantErr)
123
+ }
124
+ if got != tt.want {
125
+ t.Errorf("got %v, want %v", got, tt.want)
126
+ }
127
+ })
128
+ }
129
+ }
130
+ ```
131
+
132
+ > </rule>
133
+
134
+ ---
135
+
136
+ ## Types & Contracts
137
+
138
+ - **struct** for data; small interfaces defined by the consumer
139
+ - No nulls — use zero values carefully; pointers only when strictly necessary
140
+ - Well-defined JSON tags; never expose internal structs directly
141
+ - Manual validation or lightweight library (`go-playground/validator`); simple and explicit
142
+
143
+ ---
144
+
145
+ ## Go-Specific Delta
146
+
147
+ - Idiomatic > "beautiful architecture" — simplicity is the standard, not the exception
148
+ - Short and clear names; exported = PascalCase; unexported = camelCase; acronyms in caps (`ID`, `HTTP`)
149
+ - Early return for error flow — no `else` after `return err`
150
+ - Avoid "god structs" and "god packages" — focused packages by responsibility
151
+ - `context.Context` always applicable — cancellation and timeout propagated
152
+ - Managed lifecycle goroutines (`errgroup`, `WaitGroup`); never fire-and-forget
153
+ - `defer` immediately after resource acquisition
154
+ - Structured logging (`slog`)
155
+ - Package names: lowercase, single word, no underscores
156
+ - `for` loops are the standard for all collection operations; mapping and transformations follow the imperative pattern
157
+
158
+ ### Goroutines & Concurrency
159
+
160
+ > <rule name="GoroutinesChannels">
161
+
162
+ ```go
163
+ func ProcessBatch(ctx context.Context, items []Item) error {
164
+ g, ctx := errgroup.WithContext(ctx)
165
+ for _, item := range items {
166
+ g.Go(func() error {
167
+ return processItem(ctx, item)
168
+ })
169
+ }
170
+ return g.Wait()
171
+ }
172
+ ```
173
+
174
+ > </rule>
175
+
176
+ ### Defer for Cleanup
177
+
178
+ > <rule name="DeferCleanup">
179
+
180
+ ```go
181
+ func ReadConfig(path string) ([]byte, error) {
182
+ f, err := os.Open(path)
183
+ if err != nil {
184
+ return nil, fmt.Errorf("ReadConfig: %w", err)
185
+ }
186
+ defer f.Close()
187
+ return io.ReadAll(f)
188
+ }
189
+ ```
190
+
191
+ > </rule>
192
+
193
+ </ruleset>