@yosr-ai/template-dotnet 0.0.0-stage → 0.2.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 (68) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/LICENSE +21 -0
  3. package/README.md +14 -2
  4. package/features/auth-api-key/files/src/App/Core/ApiKeyMiddleware.cs +33 -0
  5. package/features/auth-api-key/files/tests/App.Tests/ApiKeyTests.cs +27 -0
  6. package/features/data-efcore/files/src/App/App.csproj +16 -0
  7. package/features/data-efcore/files/src/App/Core/AppDbContext.cs +22 -0
  8. package/features/data-efcore/files/src/App/Core/AppSettings.cs +30 -0
  9. package/features/data-efcore/files/src/App/Features/Notes/EfNoteStore.cs +46 -0
  10. package/features/data-efcore/files/src/App/Features/Notes/NoteEntity.cs +24 -0
  11. package/features/data-efcore/files/src/App/Program.cs +46 -0
  12. package/features/data-efcore/files/src/App/packages.lock.json +74 -0
  13. package/features/data-efcore/files/tests/App.Tests/ApiFactory.cs +70 -0
  14. package/features/data-efcore/files/tests/App.Tests/packages.lock.json +511 -0
  15. package/features/data-efcore/snippets/program-usings.txt +1 -0
  16. package/features/data-efcore/snippets/readme-db.md +3 -0
  17. package/features/data-efcore/snippets/service-registration.txt +3 -0
  18. package/features/deploy-docker/files/Dockerfile +19 -0
  19. package/features/deploy-docker/files/compose.yaml +12 -0
  20. package/features/deploy-docker/snippets/readme-docker.md +3 -0
  21. package/features/deploy-docker-postgres/snippets/app-config.yaml +3 -0
  22. package/features/deploy-docker-postgres/snippets/app-env.yaml +1 -0
  23. package/features/deploy-docker-postgres/snippets/compose-header.yaml +1 -0
  24. package/features/deploy-docker-postgres/snippets/compose-volumes.yaml +2 -0
  25. package/features/deploy-docker-postgres/snippets/postgres-service.yaml +13 -0
  26. package/features/deploy-docker-postgres/snippets/readme-docker.md +3 -0
  27. package/features/errors-result/files/src/App/Features/Notes/NotesEndpoints.cs +52 -0
  28. package/features/errors-result/files/src/App/Features/Notes/NotesService.cs +56 -0
  29. package/features/errors-result/files/src/App/Shared/Result.cs +8 -0
  30. package/features/errors-result/files/tests/App.Tests/NotesServiceTests.cs +37 -0
  31. package/files/.env.example +7 -0
  32. package/files/App.sln +24 -0
  33. package/files/Directory.Build.props +9 -0
  34. package/files/README.md +20 -0
  35. package/files/gitignore +9 -0
  36. package/files/global.json +6 -0
  37. package/files/src/App/App.csproj +13 -0
  38. package/files/src/App/Core/AppSettings.cs +25 -0
  39. package/files/src/App/Features/Notes/INoteStore.cs +12 -0
  40. package/files/src/App/Features/Notes/MemoryNoteStore.cs +54 -0
  41. package/files/src/App/Features/Notes/Note.cs +24 -0
  42. package/files/src/App/Features/Notes/NotesEndpoints.cs +63 -0
  43. package/files/src/App/Features/Notes/NotesService.cs +38 -0
  44. package/files/src/App/Program.cs +26 -0
  45. package/files/src/App/Properties/launchSettings.json +12 -0
  46. package/files/src/App/Shared/NotFoundException.cs +9 -0
  47. package/files/src/App/appsettings.json +9 -0
  48. package/files/src/App/packages.lock.json +21 -0
  49. package/files/tests/App.Tests/ApiFactory.cs +14 -0
  50. package/files/tests/App.Tests/App.Tests.csproj +28 -0
  51. package/files/tests/App.Tests/GlobalUsings.cs +1 -0
  52. package/files/tests/App.Tests/NotesApiTests.cs +62 -0
  53. package/files/tests/App.Tests/NotesServiceTests.cs +36 -0
  54. package/files/tests/App.Tests/packages.lock.json +428 -0
  55. package/package.json +26 -4
  56. package/rules/architecture.md +5 -0
  57. package/rules/auth-api-key.md +3 -0
  58. package/rules/commands.md +7 -0
  59. package/rules/data-efcore.md +3 -0
  60. package/rules/data-none.md +3 -0
  61. package/rules/errors-result.md +3 -0
  62. package/rules/errors-throw.md +3 -0
  63. package/rules/layout-feature.md +5 -0
  64. package/rules/layout-flat.md +3 -0
  65. package/rules/layout-layer.md +3 -0
  66. package/rules/project-overview.md +5 -0
  67. package/seed.template.json +716 -0
  68. package/style/.gitkeep +0 -0
@@ -0,0 +1,716 @@
1
+ {
2
+ "$schema": "../engine/schemas/seed.template.schema.json",
3
+ "schemaVersion": "0.1",
4
+ "name": "dotnet",
5
+ "version": "0.2.5",
6
+ "title": "ASP.NET Core (API only)",
7
+ "description": "Yosr Seed's API-only ASP.NET Core template (ADR 0026): .NET 10 LTS, minimal API, xUnit + WebApplicationFactory, locked NuGet restore, global.json, notes CRUD. Defaults to Postgres via EF Core + Npgsql; base tree is in-memory.",
8
+ "license": "MIT",
9
+ "authors": ["Yosr Seed"],
10
+ "keywords": [
11
+ "aspnet",
12
+ "dotnet",
13
+ "efcore",
14
+ "npgsql",
15
+ "xunit",
16
+ "minimal-api"
17
+ ],
18
+ "engines": {
19
+ "yosr": ">=0.2.0 <0.3.0",
20
+ "dotnet": ">=10.0"
21
+ },
22
+ "stack": {
23
+ "framework": "aspnet",
24
+ "language": "csharp",
25
+ "includes": ["minimal-api", "xunit", "efcore", "npgsql"]
26
+ },
27
+ "packageManagers": {
28
+ "default": "dotnet",
29
+ "supported": ["dotnet"]
30
+ },
31
+ "variables": {
32
+ "authLabel": {
33
+ "fromOption": "auth",
34
+ "map": {
35
+ "none": "none (open API)",
36
+ "api-key": "single API key (X-API-Key header)"
37
+ }
38
+ },
39
+ "storageLabel": {
40
+ "fromOption": "database",
41
+ "map": {
42
+ "none": "memory only (no database)",
43
+ "postgres": "Postgres (EF Core + Npgsql)"
44
+ }
45
+ },
46
+ "databaseUrl": {
47
+ "fromOption": "database",
48
+ "map": {
49
+ "postgres": "postgresql://postgres:postgres@localhost:5432/postgres"
50
+ }
51
+ },
52
+ "deploy": {
53
+ "fromOption": "deploy",
54
+ "map": {
55
+ "none": "decide later",
56
+ "docker": "Docker (multi-stage, non-root)"
57
+ }
58
+ }
59
+ },
60
+ "options": [
61
+ {
62
+ "id": "projectName",
63
+ "type": "string",
64
+ "label": {
65
+ "cli": "Project name",
66
+ "wizard": "What should we call your project?"
67
+ },
68
+ "help": {
69
+ "cli": "Lowercase, digits and dashes, at most 30 characters. Dashes become underscores in the C# project/namespace.",
70
+ "wizard": "A short name with no spaces. Dashes become underscores in the C# project name."
71
+ },
72
+ "pattern": "^(?!(?:system|microsoft|aspnet|dotnet|tests)$)[a-z][a-z0-9-]*$",
73
+ "maxLength": 30
74
+ },
75
+ {
76
+ "id": "database",
77
+ "type": "enum",
78
+ "label": {
79
+ "cli": "Database",
80
+ "wizard": "Does your API need to save data?"
81
+ },
82
+ "default": "postgres",
83
+ "choices": [
84
+ {
85
+ "value": "postgres",
86
+ "label": {
87
+ "cli": "Postgres",
88
+ "wizard": "Yes, in a Postgres database"
89
+ },
90
+ "help": {
91
+ "cli": "EF Core + Npgsql; DATABASE_URL points at your server.",
92
+ "wizard": "A database server you run yourself or in Docker."
93
+ }
94
+ },
95
+ {
96
+ "value": "none",
97
+ "label": {
98
+ "cli": "None",
99
+ "wizard": "No, keep data in memory"
100
+ }
101
+ }
102
+ ],
103
+ "help": {
104
+ "cli": "Postgres through EF Core + Npgsql (default), or none to keep notes in memory.",
105
+ "wizard": "Pick Postgres for a real database, or none for an in-memory store."
106
+ }
107
+ },
108
+ {
109
+ "id": "auth",
110
+ "type": "enum",
111
+ "label": {
112
+ "cli": "Authentication",
113
+ "wizard": "Should callers prove who they are?"
114
+ },
115
+ "default": "none",
116
+ "choices": [
117
+ {
118
+ "value": "none",
119
+ "label": {
120
+ "cli": "None",
121
+ "wizard": "No, the API is open"
122
+ }
123
+ },
124
+ {
125
+ "value": "api-key",
126
+ "label": {
127
+ "cli": "API key",
128
+ "wizard": "Yes, with a shared API key"
129
+ },
130
+ "help": {
131
+ "cli": "X-API-Key header checked against API_KEY.",
132
+ "wizard": "Callers send a secret key with each request."
133
+ }
134
+ }
135
+ ],
136
+ "help": {
137
+ "cli": "One shared API key from the API_KEY environment variable, or an open API.",
138
+ "wizard": "An API key keeps strangers out; per-user sign-in is not part of this template."
139
+ }
140
+ },
141
+ {
142
+ "id": "deploy",
143
+ "type": "enum",
144
+ "label": {
145
+ "cli": "Deploy target",
146
+ "wizard": "How will the API run in production?"
147
+ },
148
+ "default": "none",
149
+ "choices": [
150
+ {
151
+ "value": "none",
152
+ "label": {
153
+ "cli": "None",
154
+ "wizard": "Decide later"
155
+ }
156
+ },
157
+ {
158
+ "value": "docker",
159
+ "label": {
160
+ "cli": "Docker",
161
+ "wizard": "Docker container + Compose"
162
+ }
163
+ }
164
+ ],
165
+ "help": {
166
+ "cli": "Dockerfile + Compose (with Postgres when database is postgres), or decide later.",
167
+ "wizard": "Docker runs the API the same way everywhere."
168
+ }
169
+ },
170
+ {
171
+ "id": "workflow",
172
+ "type": "enum",
173
+ "label": {
174
+ "cli": "Workflow",
175
+ "wizard": "How should agents track work?"
176
+ },
177
+ "help": {
178
+ "cli": "Local Markdown tracker, GitHub issue/PR templates, or neither.",
179
+ "wizard": "Local files under work/, GitHub templates only, or skip the tracker."
180
+ },
181
+ "default": "local",
182
+ "choices": [
183
+ {
184
+ "value": "local",
185
+ "label": {
186
+ "cli": "Local",
187
+ "wizard": "Markdown tracker under work/ (default)"
188
+ }
189
+ },
190
+ {
191
+ "value": "github",
192
+ "label": {
193
+ "cli": "GitHub",
194
+ "wizard": "Issue and PR templates (no API calls)"
195
+ }
196
+ },
197
+ {
198
+ "value": "none",
199
+ "label": {
200
+ "cli": "None",
201
+ "wizard": "No tracker files; keep zones only"
202
+ }
203
+ }
204
+ ]
205
+ },
206
+ {
207
+ "id": "ci",
208
+ "type": "boolean",
209
+ "label": {
210
+ "cli": "CI recipe",
211
+ "wizard": "Add a CI recipe that runs Yosr Seed gate?"
212
+ },
213
+ "help": {
214
+ "cli": "Opt-in GitHub Actions sample that runs a pinned yosr-seed gate (not @latest). Default off.",
215
+ "wizard": "Writes a sample GitHub Actions workflow that runs the same gate as locally, with the CLI version pinned."
216
+ },
217
+ "default": false
218
+ }
219
+ ],
220
+ "constraints": [],
221
+ "aliases": {
222
+ "@domain/*": "src/App/Features/*",
223
+ "@shared/*": "src/App/Shared/*",
224
+ "@server/*": "src/App/Core/*"
225
+ },
226
+ "features": [
227
+ {
228
+ "id": "data-efcore",
229
+ "description": "EF Core + Npgsql for Postgres: AppDbContext, EfNoteStore, EnsureCreated in tests (no gate migrations).",
230
+ "when": {
231
+ "option": "database",
232
+ "equals": "postgres"
233
+ },
234
+ "replaces": [
235
+ "src/App/Program.cs",
236
+ "src/App/Core/AppSettings.cs",
237
+ "src/App/App.csproj",
238
+ "src/App/packages.lock.json",
239
+ "tests/App.Tests/ApiFactory.cs",
240
+ "tests/App.Tests/packages.lock.json"
241
+ ],
242
+ "patches": [
243
+ {
244
+ "op": "env",
245
+ "target": ".env.example",
246
+ "vars": [
247
+ {
248
+ "name": "DATABASE_URL",
249
+ "example": "__seed_databaseUrl__",
250
+ "description": "Postgres connection URL for EF Core"
251
+ }
252
+ ]
253
+ },
254
+ {
255
+ "op": "insert",
256
+ "target": "README.md",
257
+ "anchor": "readme-sections",
258
+ "source": "features/data-efcore/snippets/readme-db.md"
259
+ }
260
+ ]
261
+ },
262
+ {
263
+ "id": "errors-result",
264
+ "description": "Result-style notes service and endpoints.",
265
+ "when": {
266
+ "option": "style.errors.pattern",
267
+ "equals": "result"
268
+ },
269
+ "after": ["data-efcore"],
270
+ "replaces": [
271
+ "@domain/Notes/NotesService.cs",
272
+ "@domain/Notes/NotesEndpoints.cs",
273
+ "tests/App.Tests/NotesServiceTests.cs"
274
+ ]
275
+ },
276
+ {
277
+ "id": "auth-api-key",
278
+ "description": "Single shared API key (X-API-Key vs API_KEY); /health and OpenAPI stay open.",
279
+ "when": {
280
+ "option": "auth",
281
+ "equals": "api-key"
282
+ },
283
+ "after": ["data-efcore", "errors-result"],
284
+ "patches": [
285
+ {
286
+ "op": "insert",
287
+ "target": "@server/AppSettings.cs",
288
+ "anchor": "settings-fields",
289
+ "content": "public string? ApiKey { get; init; }\n"
290
+ },
291
+ {
292
+ "op": "insert",
293
+ "target": "@server/AppSettings.cs",
294
+ "anchor": "settings-values",
295
+ "content": "ApiKey = configuration[\"API_KEY\"] ?? Environment.GetEnvironmentVariable(\"API_KEY\"),"
296
+ },
297
+ {
298
+ "op": "insert",
299
+ "target": "src/App/Program.cs",
300
+ "anchor": "middleware",
301
+ "content": "app.UseMiddleware<ApiKeyMiddleware>();"
302
+ },
303
+ {
304
+ "op": "insert",
305
+ "target": "tests/App.Tests/ApiFactory.cs",
306
+ "anchor": "factory-configure",
307
+ "content": "builder.UseSetting(\"API_KEY\", \"test-api-key\");\nEnvironment.SetEnvironmentVariable(\"API_KEY\", \"test-api-key\");"
308
+ },
309
+ {
310
+ "op": "insert",
311
+ "target": "tests/App.Tests/NotesApiTests.cs",
312
+ "anchor": "test-client-headers",
313
+ "content": "_client.DefaultRequestHeaders.Add(\"X-API-Key\", \"test-api-key\");"
314
+ },
315
+ {
316
+ "op": "env",
317
+ "target": ".env.example",
318
+ "vars": [
319
+ {
320
+ "name": "API_KEY",
321
+ "example": "change-me",
322
+ "description": "Shared API key for X-API-Key"
323
+ }
324
+ ]
325
+ }
326
+ ]
327
+ },
328
+ {
329
+ "id": "deploy-docker",
330
+ "description": "Multi-stage Dockerfile (sdk → aspnet:10.0, non-root) and Compose skeleton.",
331
+ "when": {
332
+ "option": "deploy",
333
+ "equals": "docker"
334
+ },
335
+ "patches": [
336
+ {
337
+ "op": "insert",
338
+ "target": "README.md",
339
+ "anchor": "readme-sections",
340
+ "source": "features/deploy-docker/snippets/readme-docker.md"
341
+ }
342
+ ]
343
+ },
344
+ {
345
+ "id": "deploy-docker-postgres",
346
+ "description": "Compose Postgres service wired to DATABASE_URL.",
347
+ "when": {
348
+ "all": [
349
+ { "option": "deploy", "equals": "docker" },
350
+ { "option": "database", "equals": "postgres" }
351
+ ]
352
+ },
353
+ "after": ["deploy-docker"],
354
+ "patches": [
355
+ {
356
+ "op": "insert",
357
+ "target": "compose.yaml",
358
+ "anchor": "compose-header",
359
+ "source": "features/deploy-docker-postgres/snippets/compose-header.yaml"
360
+ },
361
+ {
362
+ "op": "insert",
363
+ "target": "compose.yaml",
364
+ "anchor": "app-env",
365
+ "source": "features/deploy-docker-postgres/snippets/app-env.yaml"
366
+ },
367
+ {
368
+ "op": "insert",
369
+ "target": "compose.yaml",
370
+ "anchor": "app-config",
371
+ "source": "features/deploy-docker-postgres/snippets/app-config.yaml"
372
+ },
373
+ {
374
+ "op": "insert",
375
+ "target": "compose.yaml",
376
+ "anchor": "services",
377
+ "source": "features/deploy-docker-postgres/snippets/postgres-service.yaml"
378
+ },
379
+ {
380
+ "op": "insert",
381
+ "target": "compose.yaml",
382
+ "anchor": "compose-volumes",
383
+ "source": "features/deploy-docker-postgres/snippets/compose-volumes.yaml"
384
+ },
385
+ {
386
+ "op": "insert",
387
+ "target": "README.md",
388
+ "anchor": "readme-sections",
389
+ "source": "features/deploy-docker-postgres/snippets/readme-docker.md"
390
+ }
391
+ ]
392
+ },
393
+ {
394
+ "id": "deploy-docker-apikey",
395
+ "description": "Compose passes API_KEY into the app container.",
396
+ "when": {
397
+ "all": [
398
+ { "option": "deploy", "equals": "docker" },
399
+ { "option": "auth", "equals": "api-key" }
400
+ ]
401
+ },
402
+ "after": ["deploy-docker", "deploy-docker-postgres"],
403
+ "patches": [
404
+ {
405
+ "op": "insert",
406
+ "target": "compose.yaml",
407
+ "anchor": "app-env",
408
+ "content": "API_KEY: ${API_KEY}"
409
+ }
410
+ ]
411
+ }
412
+ ],
413
+ "style": {
414
+ "ignore": [
415
+ "naming.files",
416
+ "naming.componentFiles",
417
+ "tests.naming",
418
+ "tests.location",
419
+ "data.fetching",
420
+ "forms.validation",
421
+ "lint.preset",
422
+ "ts.*",
423
+ "format.*",
424
+ "imports.*"
425
+ ],
426
+ "dimensions": [
427
+ {
428
+ "id": "layout",
429
+ "description": "Feature-based, layer-based, or flat packages under src/<project>/.",
430
+ "default": "feature",
431
+ "values": {
432
+ "feature": {
433
+ "pathMap": {
434
+ "@domain": "src/App/Features",
435
+ "@shared": "src/App/Shared",
436
+ "@server": "src/App/Core"
437
+ },
438
+ "rules": ["layout-feature"]
439
+ },
440
+ "layer": {
441
+ "pathMap": {
442
+ "@domain": "src/App/Services",
443
+ "@shared": "src/App/Lib",
444
+ "@server": "src/App/Core"
445
+ },
446
+ "rules": ["layout-layer"]
447
+ },
448
+ "flat": {
449
+ "pathMap": {
450
+ "@domain": "src/App",
451
+ "@shared": "src/App/Shared",
452
+ "@server": "src/App/Core"
453
+ },
454
+ "rules": ["layout-flat"]
455
+ }
456
+ }
457
+ },
458
+ {
459
+ "id": "errors.pattern",
460
+ "description": "How services report expected failures: throw, or return a Result.",
461
+ "default": "throw",
462
+ "values": {
463
+ "throw": {
464
+ "rules": ["errors-throw"]
465
+ },
466
+ "result": {
467
+ "rules": ["errors-result"]
468
+ }
469
+ }
470
+ }
471
+ ]
472
+ },
473
+ "zones": [
474
+ {
475
+ "id": "domain-features",
476
+ "intent": "Domain logic for the API: one folder per feature with models, store, service, and endpoints. Follow Notes and register new endpoint maps in Program.cs.",
477
+ "paths": {
478
+ "create": ["@domain/**"],
479
+ "modify": ["@domain/**", "src/App/Program.cs"]
480
+ },
481
+ "context": ["@domain/Notes/**", "AGENTS.md"],
482
+ "constraints": {
483
+ "maxFiles": 40
484
+ },
485
+ "tests": {
486
+ "required": true,
487
+ "paths": ["tests/**/*NotesServiceTests.cs"]
488
+ }
489
+ },
490
+ {
491
+ "id": "http-tests",
492
+ "intent": "In-process HTTP tests with WebApplicationFactory (ApiFactory). No running server.",
493
+ "paths": {
494
+ "create": ["tests/**"],
495
+ "modify": ["tests/**"]
496
+ },
497
+ "context": ["tests/App.Tests/ApiFactory.cs"],
498
+ "constraints": {
499
+ "maxFiles": 20
500
+ },
501
+ "tests": {
502
+ "required": false
503
+ }
504
+ },
505
+ {
506
+ "id": "core",
507
+ "intent": "App settings, DbContext, and middleware. Keep feature logic out of Core.",
508
+ "paths": {
509
+ "create": ["@server/**"],
510
+ "modify": ["@server/**"]
511
+ },
512
+ "constraints": {
513
+ "maxFiles": 20
514
+ },
515
+ "tests": {
516
+ "required": false
517
+ }
518
+ }
519
+ ],
520
+ "rules": {
521
+ "targets": ["agents-md", "claude-md", "cursor-rules"],
522
+ "fragments": [
523
+ {
524
+ "id": "project-overview",
525
+ "file": "rules/project-overview.md",
526
+ "title": "Project",
527
+ "order": 10,
528
+ "cursor": { "alwaysApply": true }
529
+ },
530
+ {
531
+ "id": "commands",
532
+ "file": "rules/commands.md",
533
+ "title": "Commands",
534
+ "order": 20,
535
+ "cursor": { "alwaysApply": true }
536
+ },
537
+ {
538
+ "id": "layout-feature",
539
+ "file": "rules/layout-feature.md",
540
+ "title": "Layout",
541
+ "order": 30,
542
+ "cursor": { "globs": ["src/**"] }
543
+ },
544
+ {
545
+ "id": "layout-layer",
546
+ "file": "rules/layout-layer.md",
547
+ "title": "Layout",
548
+ "order": 30,
549
+ "cursor": { "globs": ["src/**"] }
550
+ },
551
+ {
552
+ "id": "layout-flat",
553
+ "file": "rules/layout-flat.md",
554
+ "title": "Layout",
555
+ "order": 30,
556
+ "cursor": { "globs": ["src/**"] }
557
+ },
558
+ {
559
+ "id": "errors-throw",
560
+ "file": "rules/errors-throw.md",
561
+ "title": "Errors",
562
+ "order": 40,
563
+ "cursor": { "globs": ["src/**"] }
564
+ },
565
+ {
566
+ "id": "errors-result",
567
+ "file": "rules/errors-result.md",
568
+ "title": "Errors",
569
+ "order": 40,
570
+ "cursor": { "globs": ["src/**"] }
571
+ },
572
+ {
573
+ "id": "data-none",
574
+ "file": "rules/data-none.md",
575
+ "title": "Data",
576
+ "order": 50,
577
+ "when": { "option": "database", "equals": "none" },
578
+ "cursor": {
579
+ "globs": ["src/**/MemoryNoteStore.cs", "src/**/INoteStore.cs"]
580
+ }
581
+ },
582
+ {
583
+ "id": "data-efcore",
584
+ "file": "rules/data-efcore.md",
585
+ "title": "Data",
586
+ "order": 50,
587
+ "when": { "option": "database", "equals": "postgres" },
588
+ "cursor": {
589
+ "globs": ["src/**/AppDbContext.cs", "src/**/EfNoteStore.cs", "src/**/NoteEntity.cs"]
590
+ }
591
+ },
592
+ {
593
+ "id": "auth-api-key",
594
+ "file": "rules/auth-api-key.md",
595
+ "title": "Auth",
596
+ "order": 60,
597
+ "when": { "option": "auth", "equals": "api-key" },
598
+ "cursor": {
599
+ "globs": ["src/**/ApiKeyMiddleware.cs"]
600
+ }
601
+ }
602
+ ],
603
+ "toolsNote": "Format and build are enforced by tools, not prose: run `dotnet format --verify-no-changes` and `dotnet build -warnaserror`. Fix the code; don't disable rules.",
604
+ "architectureNote": {
605
+ "source": "rules/architecture.md",
606
+ "target": "docs/architecture.md",
607
+ "folders": {
608
+ "app entry": "src/App/Program.cs",
609
+ "http tests": "tests/"
610
+ }
611
+ }
612
+ },
613
+ "checks": {
614
+ "maxFixAttempts": 3,
615
+ "services": [
616
+ {
617
+ "id": "postgres",
618
+ "image": "postgres:18.6-alpine",
619
+ "env": {
620
+ "POSTGRES_PASSWORD": "postgres"
621
+ },
622
+ "when": {
623
+ "option": "database",
624
+ "equals": "postgres"
625
+ }
626
+ }
627
+ ],
628
+ "steps": [
629
+ {
630
+ "id": "install",
631
+ "kind": "install",
632
+ "run": {
633
+ "command": ["dotnet", "restore", "--locked-mode"]
634
+ },
635
+ "timeoutSec": 600
636
+ },
637
+ {
638
+ "id": "format",
639
+ "kind": "lint",
640
+ "run": {
641
+ "command": ["dotnet", "format", "--verify-no-changes"]
642
+ },
643
+ "timeoutSec": 300
644
+ },
645
+ {
646
+ "id": "build",
647
+ "kind": "typecheck",
648
+ "run": {
649
+ "command": ["dotnet", "build", "-warnaserror", "--no-restore"]
650
+ },
651
+ "timeoutSec": 600
652
+ },
653
+ {
654
+ "id": "test",
655
+ "kind": "test",
656
+ "run": {
657
+ "command": ["dotnet", "test", "--no-build"]
658
+ },
659
+ "timeoutSec": 600
660
+ },
661
+ {
662
+ "id": "image",
663
+ "kind": "custom",
664
+ "run": {
665
+ "command": [
666
+ "docker",
667
+ "build",
668
+ "-t",
669
+ "__seed_projectName__:latest",
670
+ "."
671
+ ]
672
+ },
673
+ "timeoutSec": 900,
674
+ "required": false,
675
+ "when": {
676
+ "option": "deploy",
677
+ "equals": "docker"
678
+ },
679
+ "phases": ["handoff", "template-ci", "project-ci"]
680
+ }
681
+ ],
682
+ "report": {
683
+ "path": ".seed/report.md"
684
+ }
685
+ },
686
+ "matrix": {
687
+ "strategy": "pairwise",
688
+ "packageManagers": ["dotnet"],
689
+ "styleDimensions": ["layout", "errors.pattern"],
690
+ "include": [
691
+ {
692
+ "name": "defaults",
693
+ "options": {}
694
+ },
695
+ {
696
+ "name": "none",
697
+ "options": {
698
+ "database": "none"
699
+ }
700
+ },
701
+ {
702
+ "name": "kitchen-sink",
703
+ "options": {
704
+ "database": "postgres",
705
+ "auth": "api-key",
706
+ "deploy": "docker"
707
+ },
708
+ "style": {
709
+ "layout": "layer",
710
+ "errors.pattern": "result"
711
+ }
712
+ }
713
+ ]
714
+ },
715
+ "migrations": []
716
+ }