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