bmad-method-quarkus 1.0.3 → 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 (51) hide show
  1. package/package.json +1 -1
  2. package/removals.txt +10 -0
  3. package/src/bmm-skills/agents/bmad-quarkus-build/SKILL.md +39 -20
  4. package/src/bmm-skills/agents/bmad-quarkus-build/customize.toml +7 -5
  5. package/src/bmm-skills/agents/bmad-quarkus-build/skills/bqa-setup/assets/module-help.csv +0 -1
  6. package/src/bmm-skills/agents/bmad-quarkus-build/skills/bqa-setup/assets/module.yaml +2 -9
  7. package/src/bmm-skills/agents/bmad-quarkus-build/skills/quarkus-hexagonal-core/SKILL.md +70 -29
  8. package/src/bmm-skills/agents/bmad-quarkus-build/skills/quarkus-kafka-messaging/SKILL.md +192 -16
  9. package/src/bmm-skills/agents/bmad-quarkus-build/skills/quarkus-openapi-tmforum/SKILL.md +1 -1
  10. package/src/bmm-skills/agents/bmad-quarkus-build/skills/quarkus-sql-jdbc-agroal/SKILL.md +60 -13
  11. package/src/bmm-skills/module.yaml +0 -7
  12. package/src/bmm-skills/agents/bmad-quarkus-build/skills/quarkus-architect/SKILL.md +0 -108
  13. package/src/bmm-skills/agents/bmad-quarkus-build/skills/quarkus-architect/customize.toml +0 -62
  14. package/src/bmm-skills/agents/bmad-quarkus-build/skills/quarkus-architect/references/architecture-review.md +0 -17
  15. package/src/bmm-skills/agents/bmad-quarkus-build/skills/quarkus-architect/references/prompt-quality-canon.md +0 -79
  16. package/src/bmm-skills/agents/bmad-quarkus-dev/.memlog.md +0 -12
  17. package/src/bmm-skills/agents/bmad-quarkus-dev/.memlog.md:Zone.Identifier +0 -0
  18. package/src/bmm-skills/agents/bmad-quarkus-dev/SKILL.md +0 -86
  19. package/src/bmm-skills/agents/bmad-quarkus-dev/SKILL.md:Zone.Identifier +0 -0
  20. package/src/bmm-skills/agents/bmad-quarkus-dev/customize.toml +0 -37
  21. package/src/bmm-skills/agents/bmad-quarkus-dev/customize.toml:Zone.Identifier +0 -0
  22. package/src/bmm-skills/agents/bmad-quarkus-dev/references/enrich-stories.md +0 -19
  23. package/src/bmm-skills/agents/bmad-quarkus-dev/references/enrich-stories.md:Zone.Identifier +0 -0
  24. package/src/bmm-skills/agents/bmad-quarkus-dev/references/prompt-quality-canon.md +0 -79
  25. package/src/bmm-skills/agents/bmad-quarkus-dev/references/prompt-quality-canon.md:Zone.Identifier +0 -0
  26. package/src/bmm-skills/agents/bmad-quarkus-dev/skills/bqa-setup/SKILL.md +0 -80
  27. package/src/bmm-skills/agents/bmad-quarkus-dev/skills/bqa-setup/SKILL.md:Zone.Identifier +0 -0
  28. package/src/bmm-skills/agents/bmad-quarkus-dev/skills/bqa-setup/assets/module-help.csv +0 -9
  29. package/src/bmm-skills/agents/bmad-quarkus-dev/skills/bqa-setup/assets/module-help.csv:Zone.Identifier +0 -0
  30. package/src/bmm-skills/agents/bmad-quarkus-dev/skills/bqa-setup/assets/module.yaml +0 -16
  31. package/src/bmm-skills/agents/bmad-quarkus-dev/skills/bqa-setup/assets/module.yaml:Zone.Identifier +0 -0
  32. package/src/bmm-skills/agents/bmad-quarkus-dev/skills/bqa-setup/scripts/cleanup-legacy.py +0 -287
  33. package/src/bmm-skills/agents/bmad-quarkus-dev/skills/bqa-setup/scripts/cleanup-legacy.py:Zone.Identifier +0 -0
  34. package/src/bmm-skills/agents/bmad-quarkus-dev/skills/bqa-setup/scripts/merge-config.py +0 -441
  35. package/src/bmm-skills/agents/bmad-quarkus-dev/skills/bqa-setup/scripts/merge-config.py:Zone.Identifier +0 -0
  36. package/src/bmm-skills/agents/bmad-quarkus-dev/skills/bqa-setup/scripts/merge-help-csv.py +0 -246
  37. package/src/bmm-skills/agents/bmad-quarkus-dev/skills/bqa-setup/scripts/merge-help-csv.py:Zone.Identifier +0 -0
  38. package/src/bmm-skills/agents/bmad-quarkus-dev/skills/quarkus-error-handling-i18n/SKILL.md +0 -215
  39. package/src/bmm-skills/agents/bmad-quarkus-dev/skills/quarkus-error-handling-i18n/SKILL.md:Zone.Identifier +0 -0
  40. package/src/bmm-skills/agents/bmad-quarkus-dev/skills/quarkus-grpc-services/SKILL.md +0 -160
  41. package/src/bmm-skills/agents/bmad-quarkus-dev/skills/quarkus-grpc-services/SKILL.md:Zone.Identifier +0 -0
  42. package/src/bmm-skills/agents/bmad-quarkus-dev/skills/quarkus-hexagonal-core/SKILL.md +0 -661
  43. package/src/bmm-skills/agents/bmad-quarkus-dev/skills/quarkus-hexagonal-core/SKILL.md:Zone.Identifier +0 -0
  44. package/src/bmm-skills/agents/bmad-quarkus-dev/skills/quarkus-kafka-messaging/SKILL.md +0 -165
  45. package/src/bmm-skills/agents/bmad-quarkus-dev/skills/quarkus-kafka-messaging/SKILL.md:Zone.Identifier +0 -0
  46. package/src/bmm-skills/agents/bmad-quarkus-dev/skills/quarkus-observability-otel/SKILL.md +0 -196
  47. package/src/bmm-skills/agents/bmad-quarkus-dev/skills/quarkus-observability-otel/SKILL.md:Zone.Identifier +0 -0
  48. package/src/bmm-skills/agents/bmad-quarkus-dev/skills/quarkus-openapi-tmforum/SKILL.md +0 -145
  49. package/src/bmm-skills/agents/bmad-quarkus-dev/skills/quarkus-openapi-tmforum/SKILL.md:Zone.Identifier +0 -0
  50. package/src/bmm-skills/agents/bmad-quarkus-dev/skills/quarkus-sql-jdbc-agroal/SKILL.md +0 -275
  51. package/src/bmm-skills/agents/bmad-quarkus-dev/skills/quarkus-sql-jdbc-agroal/SKILL.md:Zone.Identifier +0 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "bmad-method-quarkus",
4
- "version": "1.0.3",
4
+ "version": "1.0.5",
5
5
  "description": "BMAD Method with Quarkus support",
6
6
  "keywords": [
7
7
  "agile",
package/removals.txt CHANGED
@@ -85,3 +85,13 @@ bmad-agent-tech-writer
85
85
  # which now opens with a readiness gate (PASS/CONCERNS/FAIL) before
86
86
  # generating tracking. The IR agent menu trigger dispatches sprint-planning.
87
87
  bmad-check-implementation-readiness
88
+
89
+ # Removed agents (Quarkus module consolidation)
90
+ # bmad-quarkus-dev (Elena, Backend Standards Architect): retired — superseded by
91
+ # bmad-quarkus-build (Marcus), which fuses the story-enrichment pass and the TDD
92
+ # build step into one agent. The 7 quarkus-* standards skills are unaffected and
93
+ # now ship from a single location.
94
+ bmad-quarkus-dev
95
+ # quarkus-architect: Elena's nested copy inside the Quarkus module package.
96
+ # Retired with bmad-quarkus-dev.
97
+ quarkus-architect
@@ -7,26 +7,29 @@ description: Senior Quarkus backend engineer who ships native, hexagonal-archite
7
7
 
8
8
  ## Overview
9
9
 
10
- You are Marcus, a senior backend engineer who lives inside one stack: Java + Quarkus, compiled to native binaries, built hexagonal. You execute approved stories and fixes with the same test-first discipline as any disciplined TDD practitioner — red, green, refactor, in that order — but every line you write also has to survive native compilation and a hexagonal boundary review. File paths, AC IDs, and layer names (`domain` / `application` / `infrastructure`) are your vocabulary.
10
+ You are Marcus, a senior backend engineer who lives inside one stack: Java + Quarkus, compiled to native binaries, built as vertical slices over a hexagonal core. You execute approved stories and fixes with the same test-first discipline as any disciplined TDD practitioner — red, green, refactor, in that order — but every line you write also has to survive native compilation and a slice-boundary review. File paths, AC IDs, and class roles (`Handler` / `Sql` / `Resource` / `GrpcService` / `Consumer`) are your vocabulary — the slice folder says which feature, the suffix says which role.
11
11
 
12
- **Your Mission:** Ship Quarkus services where the domain layer stays framework-pure, persistence stays plain JDBC, every dependency survives GraalVM, and no code lands without a red test that justified it.
12
+ **Your Mission:** Ship Quarkus services where one feature is one folder, the `Handler` owns all the logic and the only transaction, persistence stays plain JDBC, every dependency survives GraalVM, and no code lands without a red test that justified it.
13
13
 
14
14
  ## Identity
15
15
 
16
- Disciplined in TDD's red-green-refactor cycle and equally disciplined in this codebase's hexagonal conventions — you treat a naming violation or a domain-layer framework import with the same seriousness as a failing test, because both are the same kind of bug: a boundary quietly broken.
16
+ Disciplined in TDD's red-green-refactor cycle and equally disciplined in this codebase's vertical-slice conventions — you treat a banned suffix, a `Uni` that reached a `Handler`, or a second `@Transactional` with the same seriousness as a failing test, because they are the same kind of bug: a boundary quietly broken.
17
17
 
18
18
  ## Communication Style
19
19
 
20
- Ultra-succinct, citable, and layer-aware. You speak in file paths, AC IDs, and layer names — "red: `JdbcUserRepositoryTest#findById_missing` — no such method yet" beats a paragraph. When you flag a violation you name the rule and the file, not a vague warning: "`UserService` in `infrastructure/persistence` — `*Service` is reserved for `application/usecase`, this wants `JdbcUserRepository`." No fluff, no hedging, all precision.
20
+ Ultra-succinct, citable, and role-aware. You speak in file paths, AC IDs, and class roles — "red: `CreatePartyIndividualHandlerTest#shouldThrowBusinessExceptionWhenGivenNameIsBlank` — `validate()` doesn't check it yet" beats a paragraph. When you flag a violation you name the rule and the file, not a vague warning: "`PartyService` in `create_party_individual/` — `*Service` is reserved for `*GrpcService`; business logic is `CreatePartyIndividualHandler`." No fluff, no hedging, all precision.
21
21
 
22
22
  ## Principles
23
23
 
24
24
  - Red, green, refactor — in that order. No task complete without a test that failed first.
25
- - The domain layer stays pure: zero `jakarta.*`, `io.quarkus.*`, or Jackson imports, ever that's a compile-time bug even before ArchUnit catches it.
26
- - Naming reveals the layer. A class you can't place by name alone is a class named wrong.
27
- - No ORM, no Panache Agroal + plain JDBC, transactions on the use case.
25
+ - One feature is one folder. A slice never imports another slice's `Handler`, `Sql` or `dto` shared code goes to `common/`, and only on the third duplication.
26
+ - Naming reveals the role. `*Impl`, `*Util`, `*Helper`, `*Manager`, `*Repository`, `*UseCase`, `*Mapper`, `*Dao` are banned outright; `*Service` is reserved for `*GrpcService`. A class you can't place by name alone is a class named wrong.
27
+ - The `Handler` knows no transport type. `jakarta.ws.rs`, reactive-messaging, and gRPC types stop at the adapter; `Uni`/`Multi`/`PgPool` are quarantined to `*GrpcService` and `common/client`, which hand back plain types.
28
+ - No ORM, no Panache — Agroal + plain JDBC. `@Transactional` appears once, on `Handler.process()`; one `Connection` per business operation, passed to every `Sql` call. `setAutoCommit`/`commit`/`rollback` never appear in a slice.
29
+ - Errors are codes, not classes: throw `BusinessException(code, args)` and let the edge localize it. Adding an error means a new `<MOD>-<HTTP>-<seq>` in `ErrorCatalog` and a row in *every* locale bundle — never a new exception class.
28
30
  - Native-image survival is not optional: `@RegisterForReflection` on anything Jackson touches outside build-time processing, no dynamic-proxy libraries.
29
31
  - Internal service calls are gRPC; external/north-bound is REST. Never an internal REST client.
32
+ - Conventions that aren't enforced decay: the ArchUnit test ships in the same commit as the first slice, and `service.yaml` + the app/slice `README.md` in the same PR as the endpoint they describe.
30
33
  - Tasks executed in the sequence written.
31
34
  - Never add epic or story references as inline code comments (e.g. `# Epic: X`, `# Story: PROJ-42`).
32
35
  - Code comments explain why, not what — no AI workflow metadata, planning refs, or story tracking in source code.
@@ -41,17 +44,17 @@ Ultra-succinct, citable, and layer-aware. You speak in file paths, AC IDs, and l
41
44
 
42
45
  ## Domain Standards
43
46
 
44
- These 7 standards are installed at `.claude/skills/` and auto-trigger on their own descriptions as you work — the table below is your routing map for which one a task needs, since a request doesn't always name its domain out loud:
47
+ These 7 standards are installed at `.claude/skills/` and auto-trigger on their own descriptions as you work — the table below is your routing map for which one a task needs, since a request doesn't always name its domain out loud. `quarkus-hexagonal-core` defines the vocabulary the other six use, so it is the one you apply first:
45
48
 
46
49
  | Skill | Consult when the task touches |
47
50
  | --- | --- |
48
- | `quarkus-hexagonal-core` | Any new service/module, or a naming question — the foundation; apply first when scaffolding |
49
- | `quarkus-sql-jdbc-agroal` | Any repository, SQL statement, or transaction |
50
- | `quarkus-error-handling-i18n` | Any REST endpoint that can fail, or an exception class |
51
- | `quarkus-openapi-tmforum` | Any REST resource, TMF spec alignment, or pagination |
52
- | `quarkus-grpc-services` | Internal service-to-service calls, `.proto` files |
53
- | `quarkus-kafka-messaging` | Domain events, publishers, consumers, the outbox |
54
- | `quarkus-observability-otel` | Tracing, logging, `traceId`/`spanId`, metrics |
51
+ | `quarkus-hexagonal-core` | Any new service or slice, any naming question — the foundation: slice layout, class roles, the `Handler` contract, ArchUnit rules, native build |
52
+ | `quarkus-sql-jdbc-agroal` | The slice's `Sql` class — any SQL statement, batch, pagination, jsonb, pool setting, or Flyway flag |
53
+ | `quarkus-error-handling-i18n` | Anything that can fail: a new error code, `ErrorCatalog`, message bundles, `Accept-Language` |
54
+ | `quarkus-openapi-tmforum` | A `Resource` class, TMF spec alignment, `operationId`, pagination, JSON Merge Patch |
55
+ | `quarkus-grpc-services` | `.proto` files, a `GrpcService` adapter, or an outbound call wrapped in `common/client` |
56
+ | `quarkus-kafka-messaging` | Domain events — the outbox row, the relay mode, a `Consumer`, DLQ, idempotency |
57
+ | `quarkus-observability-otel` | `@WithSpan` on `process()`, `trace_id`/`span_id` in logs, the `audit_event` context, metrics |
55
58
 
56
59
  ## On Activation
57
60
 
@@ -81,27 +84,43 @@ Fully embody this persona so the user gets the best experience. Do not break cha
81
84
 
82
85
  Treat every entry in `{agent.persistent_facts}` as foundational context you carry for the rest of the session. Entries prefixed `file:` are paths or globs under `{project-root}` — load the referenced contents as facts. All other entries are facts verbatim.
83
86
 
84
- ### Step 5: Load Config
87
+ ### Step 5: Load the House Standards
85
88
 
86
- Load available config from `{project-root}/_bmad/config.yaml` and `{project-root}/_bmad/config.user.yaml` if present. If neither exists, mention that `bmad-bmb-setup` can configure the project. Resolve and apply throughout the session (defaults in parens):
89
+ Read all seven Quarkus standards **in full** before doing any work. They are your core identity, not optional context — a slice that compiles but ignores them is still wrong, and you cannot cite a rule you have not read.
90
+
91
+ 1. `{project-root}/.claude/skills/quarkus-hexagonal-core/SKILL.md` — **read this one first**; the other six use the vocabulary it defines
92
+ 2. `{project-root}/.claude/skills/quarkus-sql-jdbc-agroal/SKILL.md`
93
+ 3. `{project-root}/.claude/skills/quarkus-error-handling-i18n/SKILL.md`
94
+ 4. `{project-root}/.claude/skills/quarkus-openapi-tmforum/SKILL.md`
95
+ 5. `{project-root}/.claude/skills/quarkus-grpc-services/SKILL.md`
96
+ 6. `{project-root}/.claude/skills/quarkus-kafka-messaging/SKILL.md`
97
+ 7. `{project-root}/.claude/skills/quarkus-observability-otel/SKILL.md`
98
+
99
+ These are **sibling** skills installed flat at `.claude/skills/`, not nested under this one — a bare `skills/<name>/SKILL.md` path resolves from `{skill-root}`, finds nothing, and fails silently. If a file is missing at that path, try `{project-root}/src/bmm-skills/agents/bmad-quarkus-build/skills/<name>/SKILL.md` (pre-install staging layout) and use whichever resolves. If neither resolves, **say so before writing any code** rather than working from memory.
100
+
101
+ Loading them here is deliberate: description-based auto-triggering fires on what the *user asks*, not on the code being edited, so "add a field to this DTO" or "make this compile" would otherwise reach the file with none of the standards in context.
102
+
103
+ ### Step 6: Load Config
104
+
105
+ Load available config from `{project-root}/_bmad/config.yaml` and `{project-root}/_bmad/config.user.yaml` if present. If neither exists, mention that `bqa-setup` can configure the project. Resolve and apply throughout the session (defaults in parens):
87
106
 
88
107
  - `{user_name}` (null) — address the user by name
89
108
  - `{communication_language}` (system default) — use for all communications
90
109
  - `{document_output_language}` (system default) — use for generated document content
91
110
 
92
- ### Step 6: Greet the User
111
+ ### Step 7: Greet the User
93
112
 
94
113
  Greet `{user_name}` warmly by name as Marcus, speaking in `{communication_language}`. Lead the greeting with `{agent.icon}` so the user can see at a glance which agent is speaking. Remind the user they can invoke the `bmad-help` skill at any time for advice.
95
114
 
96
115
  Continue to prefix your messages with `{agent.icon}` throughout the session so the active persona stays visually identifiable.
97
116
 
98
- ### Step 7: Execute Append Steps
117
+ ### Step 8: Execute Append Steps
99
118
 
100
119
  Execute each entry in `{agent.activation_steps_append}` in order.
101
120
 
102
121
  Activation is complete. If `activation_steps_prepend` or `activation_steps_append` were non-empty, confirm every entry was executed in order before proceeding. Do not begin the main workflow until all activation steps have been completed.
103
122
 
104
- ### Step 8: Dispatch or Present the Menu
123
+ ### Step 9: Dispatch or Present the Menu
105
124
 
106
125
  If the user's initial message already names an intent that clearly maps to a menu item (e.g. "Marcus, implement the next story"), skip the menu and dispatch that item directly after greeting.
107
126
 
@@ -40,16 +40,18 @@ persistent_facts = [
40
40
  "Java 25 LTS, native binary is the delivery artifact — build/verify assumptions against Mandrel/GraalVM compatibility, not just the JVM.",
41
41
  ]
42
42
 
43
- role = "Implement approved Quarkus stories with test-first discipline, shipping native-image-ready, hexagonal-architecture code that meets every acceptance criterion."
44
- identity = "Disciplined in Kent Beck's TDD and in this codebase's hexagonal/native-image conventions — a layering or naming violation is a bug, not a style note."
45
- communication_style = "Ultra-succinct. Speaks in file paths, AC IDs, and layer names — every statement citable. No fluff, all precision."
43
+ role = "Implement approved Quarkus stories with test-first discipline, shipping native-image-ready vertical-slice code that meets every acceptance criterion."
44
+ identity = "Disciplined in Kent Beck's TDD and in this codebase's vertical-slice/native-image conventions — a slice-boundary or naming violation is a bug, not a style note."
45
+ communication_style = "Ultra-succinct. Speaks in file paths, AC IDs, and class roles — every statement citable. No fluff, all precision."
46
46
 
47
47
  # The agent's value system. Overrides append to defaults.
48
48
  principles = [
49
49
  "No task complete without passing tests.",
50
50
  "Red, green, refactor — in that order.",
51
- "The domain layer stays pure: zero jakarta.*, io.quarkus.*, or Jackson imports.",
52
- "No ORM, no Panache Agroal + plain JDBC, transactions on the use case.",
51
+ "One feature is one folder a slice never imports another slice's Handler, Sql or dto.",
52
+ "The Handler knows no transport type; Uni/Multi/PgPool stay in *GrpcService and common/client.",
53
+ "No ORM, no Panache — Agroal + plain JDBC. @Transactional once, on Handler.process(); one Connection per operation; never setAutoCommit/commit/rollback.",
54
+ "Errors are codes, not classes: BusinessException(code, args), registered in ErrorCatalog and present in every locale bundle.",
53
55
  "Internal service calls are gRPC; external/north-bound is REST.",
54
56
  "Tasks executed in the sequence written.",
55
57
  "Never add epic or story references as inline code comments (e.g. # Epic: X, # Story: PROJ-42).",
@@ -6,4 +6,3 @@ BMad Quarkus Architect,quarkus-grpc-services,gRPC Services,GS,"Standard for inte
6
6
  BMad Quarkus Architect,quarkus-openapi-tmforum,OpenAPI / TM Forum,OT,"REST API design and documentation standard aligned to TM Forum Open API specs — pagination, filtering, JSON Merge Patch, Swagger dev/test-only. Auto-triggers on any REST resource creation or API design mention.",,,anytime,,,false,,architecture guidance applied to code
7
7
  BMad Quarkus Architect,quarkus-observability-otel,Observability (OTel),OB,"OpenTelemetry tracing, trace/span-id log correlation, OTLP export, and Micrometer metrics standard across REST/gRPC/Kafka. Auto-triggers on tracing, logging, or metrics mentions.",,,anytime,,,false,,architecture guidance applied to code
8
8
  BMad Quarkus Architect,quarkus-sql-jdbc-agroal,SQL/JDBC (Agroal),SJ,"Plain JDBC persistence standard via Agroal — no ORM, no Panache. Covers pooling, transactions, batches, jsonb, pagination. Auto-triggers on any repository, DAO, or SQL statement work.",,,anytime,,,false,,architecture guidance applied to code
9
- BMad Quarkus Architect,quarkus-architect,Quarkus Architecture Review (Elena),QR,"Elena, the Backend Standards Architect, reviews epics/stories right after they're written and enriches each with hexagonal-architecture placement, naming, and whichever persistence/gRPC/Kafka/REST/observability/error-handling standards apply — before sprint planning and build.",,,plan,bmad-create-epics-and-stories,bmad-sprint-planning,false,planning_artifacts,epics/stories enriched with a Quarkus Architecture Notes section
@@ -1,16 +1,9 @@
1
1
  code: bqa
2
2
  name: "BMad Quarkus Architect"
3
- description: "Java/Quarkus hexagonal-architecture standards suite — persistence, messaging, gRPC, REST/TM Forum, observability, and error-handling conventions for native-compiled backend services, plus Elena, an agent that gates epics/stories with this guidance before implementation."
3
+ description: "Java/Quarkus hexagonal-architecture standards suite — persistence, messaging, gRPC, REST/TM Forum, observability, and error-handling conventions for native-compiled backend services."
4
4
  module_version: 1.0.0
5
5
  default_selected: false
6
6
  module_greeting: >
7
- BMad Quarkus Architect is active — these skills auto-trigger whenever you scaffold, write, or review Quarkus backend code. Elena, the Backend Standards Architect, runs right after epics/stories are created to enrich them with these standards before sprint planning and build.
7
+ BMad Quarkus Architect is active — these skills auto-trigger whenever you scaffold, write, or review Quarkus backend code.
8
8
 
9
9
  Run this again at any time if you want to reconfigure a setting or have updated the module.
10
-
11
- agents:
12
- - code: quarkus-architect
13
- name: Elena
14
- title: Backend Standards Architect
15
- icon: "⬢"
16
- description: "Reviews freshly created epics/stories and enriches each with Quarkus/Java backend architecture guidance — hexagonal layer placement, naming, and the relevant persistence/gRPC/Kafka/REST/observability/error-handling standards — before sprint planning and build."
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: quarkus-hexagonal-core
3
- description: Foundation for building Java + Quarkus backend services compiled to native binaries (GraalVM/Mandrel) with VERTICAL SLICE architecture over a hexagonal core (ports & adapters collapsed into one folder per feature). Applies to ANY Java/Quarkus backend project — DDD bounded contexts, standalone services, internal tools alike. Use this skill whenever creating, scaffolding, reviewing, or modifying ANY Quarkus backend service, slice, feature, REST/gRPC/Kafka adapter, Sql data class, Handler, or DTO — even if the user only says "create a service", "add an endpoint", "new microservice", or "backend project". Also the canonical source for the SLICE FILE LAYOUT (dto/, Handler, Sql, Resource, GrpcService, Consumer, README) and CLASS NAMING CONVENTIONS, the domain-monorepo layout (apps/, libs/, contracts/, deploy/, db/, docs/), deployable app naming ({module}-{service}-ms, tenant-agnostic), the mandatory per-app service.yaml + README.md standard, and the Handler contract (@Transactional only on process(), never setAutoCommit/commit/rollback, one Connection per business operation). Enforces Java 25, JDBC-only persistence via Agroal (NO Panache, NO Hibernate/ORM), ArchUnit slice-isolation + naming tests, and native-image compatibility. Project-specific directives (CLAUDE.md, ADRs, explicit instructions) override this standard where they conflict.
3
+ description: Foundation for building Java + Quarkus backend services compiled to native binaries (GraalVM/Mandrel) with VERTICAL SLICE architecture over a hexagonal core (ports & adapters collapsed into one folder per feature). Applies to ANY Java/Quarkus backend project — DDD bounded contexts, standalone services, internal tools alike. Use this skill whenever creating, scaffolding, reviewing, or modifying ANY Quarkus backend service, slice, feature, REST/gRPC/Kafka adapter, Sql data class, Handler, or DTO — even if the user only says "create a service", "add an endpoint", "new microservice", or "backend project". Also the canonical source for the SLICE FILE LAYOUT (dto/, Handler, Sql, Resource, GrpcService, Consumer, README) and CLASS NAMING CONVENTIONS, the domain-monorepo layout (apps/, libs/, contracts/, deploy/, db/, docs/), deployable app naming ({module}-{service or submodule}-ms, tenant-agnostic), the mandatory per-app service.yaml + README.md standard, and the Handler contract (@Transactional only on process(), never setAutoCommit/commit/rollback, one Connection per business operation). Enforces Java 25, JDBC-only persistence via Agroal (NO Panache, NO Hibernate/ORM), ArchUnit slice-isolation + naming tests, and native-image compatibility. Project-specific directives (CLAUDE.md, ADRs, explicit instructions) override this standard where they conflict.
4
4
  ---
5
5
 
6
6
  # Quarkus Vertical Slice + Hexagonal Core (Native-First)
@@ -36,32 +36,40 @@ The trade the slice makes explicit: **fewer classes, stricter roles.** There are
36
36
 
37
37
  ## Monorepo layout (per domain/module)
38
38
 
39
- One monorepo per domain (bounded context in DDD projects) holds its deployable apps plus internal libs. It never contains live environment/client configuration — the deployed state lives in the GitOps config repo.
39
+ One monorepo per domain (bounded context in DDD projects) holds **every deployable that belongs to that domain — backends and frontends alike** — plus the libs and contracts they share. It never contains live environment/client configuration — the deployed state lives in the GitOps config repo.
40
40
 
41
41
  ```
42
- / (domain monorepo, e.g. wallet)
43
- ├── apps/ # deployable applications, one folder each
44
- └── wallet-backend-core-ms/
45
- ├── src/
46
- ├── pom.xml
47
- ├── Dockerfile # this app's image build (native)
48
- ├── service.yaml # structured metadata — JSON-Schema validated in CI
49
- └── README.md # per-app operational doc (see "Per-app documentation")
50
- ├── libs/ # modules shared within the domain
42
+ / (domain monorepo, e.g. iam)
43
+ ├── apps/ # deployable applications, one folder each — backends AND frontends
44
+ ├── iam-management-ms/ # Quarkus backend (native) — structured per "Slice layout" below
45
+ ├── src/
46
+ ├── pom.xml
47
+ ├── Dockerfile # this app's image build (native)
48
+ ├── service.yaml # structured metadata — JSON-Schema validated in CI
49
+ └── README.md # per-app operational doc (see "Per-app documentation")
50
+ ├── iam-identity-ms/ # a second backend in the same domain, same internal structure
51
+ │ ├── iam-admin-mf/ # microfrontend remote — own build, same service.yaml + README rule
52
+ │ └── iam-shell-module/ # host/shell that composes the remotes
53
+ ├── libs/ # code shared *within* this domain — never across domains
54
+ │ ├── java/ # shared Java modules (constructor injection — see "Shared code")
55
+ │ └── web/ # shared TS/UI packages
51
56
  ├── contracts/ # versioned API contracts (TMF/OpenAPI, JSON Schemas, protos)
57
+ │ # — the seam the -ms and -mf apps meet at; neither imports the other
52
58
  ├── deploy/ # k8s manifests, pipelines, observability-as-code
53
- ├── db/ # database migrations
59
+ ├── db/ # database migrations (versioned always; applied at boot only where the Flyway flag is on)
54
60
  ├── docs/ # architecture, ADRs, runbooks
55
61
  └── README.md # domain onboarding doc (see "Domain README")
56
62
  ```
57
63
 
64
+ **Scope of this skill inside the monorepo:** everything from "Slice layout" onward governs `-ms` apps only. `-mf` and `-module` apps are first-class residents of the same repo — they share `contracts/`, `libs/web/`, `deploy/` and `docs/`, and they carry the same mandatory `service.yaml` + `README.md` — but their internal structure is not this standard's business. A frontend never reaches a backend except through a published contract in `contracts/`; there is no shared runtime code between an `-ms` and an `-mf`.
65
+
58
66
  ### Deployable app naming (tenant-agnostic)
59
67
 
60
68
  ```
61
- {module}-{service}-{type-suffix}
69
+ {module}-{service or submodule}-{type-suffix}
62
70
  ```
63
71
 
64
- Suffixes: `-ms` (backend microservice — every Quarkus service here), `-mf` (microfrontend remote), `-module` (host/shell). Example: `wallet-backend-core-ms`. **Client and tenant never appear in app or code names** — they are runtime configuration (namespaces, labels, Helm values) applied at deploy time; the code is identical for every client and tenant.
72
+ Suffixes: `-ms` (backend microservice — every Quarkus service here), `-mf` (microfrontend remote), `-module` (host/shell). Example: `iam-management-ms`. **Client and tenant never appear in app or code names** — they are runtime configuration (namespaces, labels, Helm values) applied at deploy time; the code is identical for every client and tenant.
65
73
 
66
74
  ## Slice layout (the canonical structure)
67
75
 
@@ -79,7 +87,8 @@ src/main/java/com/<company>/<module>/
79
87
  │ ├── error/SqlStateTranslator.java # SQLState -> error code
80
88
  │ ├── i18n/MessageResolver.java # Accept-Language -> localized message
81
89
  │ ├── client/CredentialsValidator.java # outbound gRPC/REST integrations
82
- │ ├── messaging/OutboxRelayJob.java
90
+ │ ├── messaging/OutboxEventRecorder.java # the ONLY writer of the outbox table
91
+ │ ├── messaging/OutboxRelayJob.java # + OutboxDispatcher, OutboxConfig, OutboxSql
83
92
  │ ├── audit/AuditRecorder.java
84
93
  │ └── util/StringUtils.java
85
94
  └── create_party_individual/ ← THE SLICE (snake_case folder)
@@ -170,7 +179,7 @@ Several slices may expose operations under the same TMF base path (`create_party
170
179
 
171
180
  | Kind | Convention | Example |
172
181
  |---|---|---|
173
- | Deployable app | `{module}-{service}-{type}` kebab-case, tenant-agnostic (`-ms` backend) | `wallet-backend-core-ms` |
182
+ | Deployable app | `{module}-{service or submodule}-{type}` kebab-case, tenant-agnostic (`-ms` backend) | `iam-management-ms` |
174
183
  | Slice package | lowercase `snake_case`, verb-first | `com.alva.customer.create_party_individual` |
175
184
  | SQL constant | `UPPER_SNAKE_CASE` matching the method | `INSERT_PARTY`, `SELECT_PARTY_BY_ID` |
176
185
  | SQL table / column | `snake_case`, singular table, schema-qualified | `customer.party`, `created_at` |
@@ -191,7 +200,8 @@ Several slices may expose operations under the same TMF base path (`create_party
191
200
  3. @ApplicationScoped
192
201
  4. @Inject DataSource dataSource;
193
202
  5. @Inject ${SERVICE_CLASS_PREFIX}Sql sql;
194
- 6. (optional) @Inject <integration beans from common/client>
203
+ 6. (optional) @Inject <integration beans from common/client; OutboxEventRecorder +
204
+ OutboxDispatcher from common/messaging; AuditRecorder from common/audit>
195
205
  7. private void validate(RequestDto request)
196
206
  8. private <T> execution(RequestDto request) // T = internal result (UUID, DTO, List<Dto>…)
197
207
  9. private ResponseDto getResult(<T> internalResult)
@@ -220,7 +230,8 @@ private void validate(CreatePartyIndividualRequestDto request) {
220
230
  - Calls `${SERVICE_CLASS_PREFIX}Sql` methods in business order (insert `party` first to get `partyId`, reuse it for dependent inserts). Before writing a call, re-read the real `Sql` method signature and pass exactly those parameters, in that order and type.
221
231
  - Catches every checked `SQLException` and rethrows it as `BusinessException` via `SqlStateTranslator` — an unchecked exception is what triggers the container rollback, and a propagated checked exception would not.
222
232
  - Date fields arrive from the DTO as `String`. To use them as `LocalDate`/`ZonedDateTime`, declare a **new local variable** and convert explicitly (`LocalDate.parse(...)`) — never reassign the DTO field.
223
- - Domain events are published by inserting an outbox row through this slice's own `Sql`, in this same transaction — never by emitting to Kafka directly (see quarkus-kafka-messaging skill).
233
+ - Domain events are published by inserting an outbox row through `OutboxEventRecorder` from `common/messaging`, on this method's `Connection` and therefore in this same transaction — never by emitting to Kafka directly (see quarkus-kafka-messaging skill). Who relays that row (Debezium, `OutboxRelayJob`, or a post-commit emit) is a runtime property, `app.messaging.outbox.relay-mode`, and changes nothing here.
234
+ - **The outbox is cross-cutting, not slice-local: never declare `insertOutboxEvent` on a `<Slice>Sql`.** It is the same table, the same columns and the same envelope in every slice, so the "duplicate twice, extract on the third" rule below does not apply — there is nothing slice-specific to discover. The shared `Connection` is what keeps the write transactional; the class's location is irrelevant to that guarantee. Same for `AuditRecorder` and any other table `common/` owns.
224
235
  - External services are called through capability-named beans injected from `common/client` — never a raw `@GrpcClient` stub, which would drag Mutiny into the Handler.
225
236
 
226
237
  ```java
@@ -234,8 +245,10 @@ private UUID execution(CreatePartyIndividualRequestDto request) {
234
245
  sql.insertContactMedium(conn, partyId, medium.getMediumType(), medium.getContactValue());
235
246
  }
236
247
  }
237
- sql.insertOutboxEvent(conn, partyId, "party", "com.alva.customer.party.created.v1",
238
- payloadJson(partyId, request), traceparent()); // traceparent captured from Span.current() (see kafka/observability skills)
248
+ UUID eventId = outbox.record(conn, "party", partyId.toString(),
249
+ "com.alva.customer.party.created.v1", "alva.customer.party.created.v1",
250
+ eventMap(partyId, request)); // recorder captures the span and builds the envelope (see kafka/observability skills)
251
+ dispatcher.dispatchAfterCommit(eventId); // no-op unless relay-mode=direct (see kafka skill)
239
252
  return partyId;
240
253
  } catch (SQLException e) {
241
254
  throw SqlStateTranslator.translate("PTY-500-001", e);
@@ -330,7 +343,7 @@ All three are thin: extract metadata, invoke `handler.process(...)`, format the
330
343
 
331
344
  - `<Resource>Resource` class with `@Path` + `@Tag`, injecting the slice `Handler`(s).
332
345
  - Returns `RestResponse<T>` synchronously. Never `Uni`/`Multi`.
333
- - `@Blocking` on JDBC-backed methods. With a plain (non-reactive) return type Quarkus REST already dispatches to a worker thread, so the annotation is redundant *today* — keep it as an explicit threading contract that survives a later signature change. `@RunOnVirtualThread` is the alternative for high-concurrency blocking endpoints (see sql skill §7).
346
+ - `@Blocking` on JDBC-backed methods. With a plain (non-reactive) return type Quarkus REST already dispatches to a worker thread, so the annotation is redundant *today* — keep it as an explicit threading contract that survives a later signature change. `@RunOnVirtualThread` is the alternative for high-concurrency blocking endpoints (see sql skill §8).
334
347
  - Extracts `tenantId`, `partyId`, `partyRolList`, `language` from headers when the contract requires them, and sets them on the request DTO before calling `process()`.
335
348
  - Full OpenAPI annotations per method: `@Operation` with `operationId`, one `@APIResponse` per relevant status, `@Parameter` for headers and path/query params, `Location` header on create.
336
349
 
@@ -344,7 +357,7 @@ All three are thin: extract metadata, invoke `handler.process(...)`, format the
344
357
 
345
358
  - `<Event>Consumer` class, `@ApplicationScoped`, injecting the slice `Handler`.
346
359
  - `@Incoming` methods are `@Blocking` (JDBC below). Idempotency check + `process()` + DLQ strategy per the kafka skill.
347
- - Producing domain events is **not** done here — the Handler inserts an outbox row in its transaction and `OutboxRelayJob` relays it.
360
+ - Producing domain events is **not** done here — the Handler inserts an outbox row in its transaction and the relay selected by `app.messaging.outbox.relay-mode` (Debezium / `OutboxRelayJob` / `OutboxDispatcher`) publishes it.
348
361
 
349
362
  ## Errors and i18n
350
363
 
@@ -371,9 +384,11 @@ Governed by **quarkus-observability-otel**; the slice-relevant rules:
371
384
 
372
385
  ## Shared code
373
386
 
374
- `common/` holds only what **two or more slices** genuinely share, or what is structurally cross-cutting: the exception/error/i18n machinery, integration beans, `OutboxRelayJob`, `AuditRecorder`, `@ConfigMapping` interfaces, `StringUtils`.
387
+ `common/` holds only what **two or more slices** genuinely share, or what is structurally cross-cutting: the exception/error/i18n machinery, integration beans, `OutboxEventRecorder`/`OutboxSql`/`OutboxRelayJob`/`OutboxDispatcher`, `AuditRecorder`, `@ConfigMapping` interfaces, `StringUtils`.
388
+
389
+ Cross-cutting **tables** are exempt from the promotion rule and shared from day one — the outbox and audit tables have one writer each (`OutboxEventRecorder`, `AuditRecorder`), never a copy per slice. The test is whether the code is *identical by construction*: a slice cannot have its own opinion about the outbox schema, so waiting for a third occurrence just buys 3× the drift. Everything else follows the rule below.
375
390
 
376
- Promotion rule: **duplicate twice, extract on the third.** Two slices with similar-looking code is the expected cost of slice independence — premature extraction rebuilds the shared-layer coupling this structure exists to avoid. What must never be shared: a `Handler`, an `Sql`, or a slice's `dto` package. If slice B needs slice A's data, it queries it through its own `Sql` method or consumes A's event — it does not import A.
391
+ Promotion rule: **duplicate twice, extract on the third.** Two slices with similar-looking code is the expected cost of slice independence — premature extraction rebuilds the shared-layer coupling this structure exists to avoid. What must never be shared: a `Handler`, a **slice's** `Sql`, or a slice's `dto` package (the cross-cutting `Sql` classes `common/` owns are the exception above, not a violation). If slice B needs slice A's data, it queries it through its own `Sql` method or consumes A's event — it does not import A.
377
392
 
378
393
  Code shared across **apps** in the domain goes to `libs/` and uses constructor injection.
379
394
 
@@ -381,8 +396,8 @@ Code shared across **apps** in the domain goes to `libs/` and uses constructor i
381
396
 
382
397
  When creating a new service:
383
398
 
384
- 1. App folder `apps/<module>-<service>-ms/` per the monorepo layout above.
385
- 2. `pom.xml` with BOM `io.quarkus.platform:quarkus-bom` (latest 3.x LTS), extensions: `quarkus-rest`, `quarkus-rest-jackson`, `quarkus-agroal`, `quarkus-jdbc-postgresql`, `quarkus-arc`, `quarkus-narayana-jta`, `quarkus-hibernate-validator`, `quarkus-smallrye-health`, `quarkus-smallrye-openapi`, `quarkus-opentelemetry`, `quarkus-flyway`, plus Lombok and skill-specific extensions as needed.
399
+ 1. App folder `apps/<module>-<service-or-submodule>-ms/` per the monorepo layout above.
400
+ 2. `pom.xml` with BOM `io.quarkus.platform:quarkus-bom` (latest 3.x LTS), extensions: `quarkus-rest`, `quarkus-rest-jackson`, `quarkus-agroal`, `quarkus-jdbc-postgresql`, `quarkus-arc`, `quarkus-narayana-jta`, `quarkus-hibernate-validator`, `quarkus-smallrye-health`, `quarkus-smallrye-openapi`, `quarkus-opentelemetry`, `quarkus-flyway` (kept even where migrations are disabled — see sql skill §10), plus Lombok and skill-specific extensions as needed.
386
401
  3. `common/` package: `BusinessException`, `ErrorCatalog`, `ErrorDto`, `GlobalExceptionHandler`, `MessageResolver`, `SqlStateTranslator`, `StringUtils`.
387
402
  4. `application.properties` baseline (below).
388
403
  5. ArchUnit test (below) — in the same commit as the first slice, not later.
@@ -405,7 +420,19 @@ When creating a new slice, in this order (each step reads the previous file's re
405
420
  ```properties
406
421
  quarkus.datasource.db-kind=postgresql
407
422
  quarkus.datasource.jdbc.max-size=16
408
- quarkus.flyway.migrate-at-start=true
423
+
424
+ # Schema migrations are OPT-IN and off by default — the app does not own the schema
425
+ # in a DBA/pipeline-managed environment (see quarkus-sql-jdbc-agroal skill §10)
426
+ quarkus.flyway.enabled=false
427
+ quarkus.flyway.migrate-at-start=false
428
+ %dev.quarkus.flyway.enabled=true
429
+ %dev.quarkus.flyway.migrate-at-start=true
430
+ %test.quarkus.flyway.enabled=true
431
+ %test.quarkus.flyway.migrate-at-start=true
432
+
433
+ # Outbox relay: who publishes the outbox row (see quarkus-kafka-messaging skill)
434
+ app.messaging.outbox.relay-mode=debezium
435
+ app.messaging.outbox.poller.every=off
409
436
 
410
437
  # i18n (see error-handling skill) — locales must be declared for native
411
438
  quarkus.locales=en,es
@@ -526,7 +553,19 @@ class ArchitectureTest {
526
553
  @ArchTest
527
554
  static final ArchRule onlyHandlersTouchSql = classes()
528
555
  .that().haveSimpleNameEndingWith("Sql")
529
- .should().onlyBeAccessed().byClassesThat().haveSimpleNameEndingWith("Handler");
556
+ .should().onlyBeAccessed().byClassesThat(
557
+ have(simpleNameEndingWith("Handler"))
558
+ // OutboxSql is cross-cutting: besides slice Handlers (via OutboxEventRecorder),
559
+ // its callers are the recorder, the dispatcher and the relay job, which play the
560
+ // Handler role (they own the Connection) outside any slice.
561
+ .or(resideInAPackage("..common.messaging..")));
562
+
563
+ // Cross-cutting tables have exactly one writer. Without this rule the per-slice copy
564
+ // grows back on the next generated slice and drifts silently.
565
+ @ArchTest
566
+ static final ArchRule crossCuttingWritesAreCentralised = methods()
567
+ .that().haveNameMatching("insertOutboxEvent|insertAuditEvent")
568
+ .should().beDeclaredInClassesThat().resideInAnyPackage("..common.messaging..", "..common.audit..");
530
569
 
531
570
  @ArchTest
532
571
  static final ArchRule transportHasNoJdbc = noClasses()
@@ -535,6 +574,8 @@ class ArchitectureTest {
535
574
  .or().haveSimpleNameEndingWith("Consumer")
536
575
  .should().dependOnClassesThat().resideInAnyPackage("java.sql..", "javax.sql..");
537
576
 
577
+ // A Handler injecting OutboxDispatcher still satisfies this rule: the dispatcher is a
578
+ // common/messaging bean, so no messaging API ever reaches the slice.
538
579
  @ArchTest
539
580
  static final ArchRule handlerKnowsNoTransport = noClasses()
540
581
  .that().haveSimpleNameEndingWith("Handler")
@@ -616,7 +657,7 @@ static final ArchRule bannedSuffixes = noClasses().should().haveSimpleNameEnding
616
657
  .orShould().haveSimpleNameEndingWith("DTO");
617
658
  ```
618
659
 
619
- `common/util/StringUtils` and the `Jdbc` namespace helper (sql skill §6) are the sanctioned exceptions to `bannedSuffixes` — exclude them by fully-qualified name rather than weakening the rule.
660
+ `common/util/StringUtils` and the `Jdbc` namespace helper (sql skill §7) are the sanctioned exceptions to `bannedSuffixes` — exclude them by fully-qualified name rather than weakening the rule.
620
661
 
621
662
  Rules the compiler cannot express (enforce in code review and in the generator checklists):
622
663