@rashidee/co2 1.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 (190) hide show
  1. package/dist/index.js +25449 -0
  2. package/drizzle/0000_remarkable_charles_xavier.sql +210 -0
  3. package/drizzle/0001_wise_wolf_cub.sql +13 -0
  4. package/drizzle/0002_secret_spyke.sql +16 -0
  5. package/drizzle/0003_workspace_config_singleton.sql +16 -0
  6. package/drizzle/0004_terminal_claim_v120.sql +11 -0
  7. package/drizzle/0005_brainstorm_v124.sql +10 -0
  8. package/drizzle/0006_project_v140.sql +6 -0
  9. package/drizzle/0007_prd_wizard_v124.sql +14 -0
  10. package/drizzle/0008_workflow_artifact_v126.sql +24 -0
  11. package/drizzle/0009_project_import_v150.sql +7 -0
  12. package/drizzle/0010_quality_v151.sql +3 -0
  13. package/drizzle/meta/0000_snapshot.json +1335 -0
  14. package/drizzle/meta/0001_snapshot.json +1416 -0
  15. package/drizzle/meta/0002_snapshot.json +1510 -0
  16. package/drizzle/meta/_journal.json +83 -0
  17. package/package.json +41 -0
  18. package/plugin/.claude-plugin/marketplace.json +36 -0
  19. package/plugin/.claude-plugin/plugin.json +22 -0
  20. package/plugin/README.md +625 -0
  21. package/plugin/SKILLS.md +350 -0
  22. package/plugin/co2.svg +5 -0
  23. package/plugin/commands/brainstorm-loop.md +129 -0
  24. package/plugin/skills/conductor-defect/SKILL.md +898 -0
  25. package/plugin/skills/conductor-feature-develop/SKILL.md +1383 -0
  26. package/plugin/skills/conductor-feature-develop/references/implementation-domain-template.md +131 -0
  27. package/plugin/skills/conductor-feature-develop/references/implementation-master-template.md +89 -0
  28. package/plugin/skills/conductor-feature-develop/references/playwright-setup.md +224 -0
  29. package/plugin/skills/conductor-feature-prepare/SKILL.md +604 -0
  30. package/plugin/skills/depgen-k8s/SKILL.md +468 -0
  31. package/plugin/skills/depgen-k8s/references/dockerfile-laravel.md +197 -0
  32. package/plugin/skills/depgen-k8s/references/dockerfile-nodejs.md +163 -0
  33. package/plugin/skills/depgen-k8s/references/dockerfile-spring-boot.md +174 -0
  34. package/plugin/skills/depgen-k8s/references/k8s-patterns.md +869 -0
  35. package/plugin/skills/mockgen-shadcn/SKILL.md +1067 -0
  36. package/plugin/skills/mockgen-shadcn/references/admin-layout-template.md +1467 -0
  37. package/plugin/skills/mockgen-shadcn/references/mockup-hub-template.md +498 -0
  38. package/plugin/skills/mockgen-shadcn/references/mockup-index-template.md +216 -0
  39. package/plugin/skills/mockgen-tailwind/SKILL.md +904 -0
  40. package/plugin/skills/mockgen-tailwind/references/admin-layout-template.md +720 -0
  41. package/plugin/skills/mockgen-tailwind/references/mockup-hub-template.md +498 -0
  42. package/plugin/skills/mockgen-tailwind/references/mockup-index-template.md +190 -0
  43. package/plugin/skills/modelgen-nosql/SKILL.md +617 -0
  44. package/plugin/skills/modelgen-nosql/references/model-extraction-methodology.md +497 -0
  45. package/plugin/skills/modelgen-nosql/references/nosql-design-guide.md +258 -0
  46. package/plugin/skills/modelgen-relational/SKILL.md +707 -0
  47. package/plugin/skills/modelgen-relational/references/model-extraction-methodology.md +499 -0
  48. package/plugin/skills/specgen-flutter-riverpod/SKILL.md +898 -0
  49. package/plugin/skills/specgen-flutter-riverpod/references/component-patterns.md +971 -0
  50. package/plugin/skills/specgen-flutter-riverpod/references/notification-patterns.md +457 -0
  51. package/plugin/skills/specgen-flutter-riverpod/references/routing-patterns.md +362 -0
  52. package/plugin/skills/specgen-flutter-riverpod/references/security-patterns.md +709 -0
  53. package/plugin/skills/specgen-flutter-riverpod/references/spec-template.md +1566 -0
  54. package/plugin/skills/specgen-flutter-riverpod/references/state-patterns.md +518 -0
  55. package/plugin/skills/specgen-flutter-riverpod/references/storage-patterns.md +370 -0
  56. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/SKILL.md +984 -0
  57. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/batch-patterns.md +359 -0
  58. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/messaging-patterns.md +468 -0
  59. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/modulith-patterns.md +461 -0
  60. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/reporting-patterns.md +803 -0
  61. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/security-patterns.md +501 -0
  62. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/spec-template.md +1268 -0
  63. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/web-patterns.md +301 -0
  64. package/plugin/skills/specgen-node-cli-web/SKILL.md +561 -0
  65. package/plugin/skills/specgen-node-cli-web/references/auth-patterns.md +293 -0
  66. package/plugin/skills/specgen-node-cli-web/references/cli-packaging-patterns.md +250 -0
  67. package/plugin/skills/specgen-node-cli-web/references/server-patterns.md +247 -0
  68. package/plugin/skills/specgen-node-cli-web/references/spec-template.md +401 -0
  69. package/plugin/skills/specgen-node-cli-web/references/testing-patterns.md +174 -0
  70. package/plugin/skills/specgen-react-mui/SKILL.md +789 -0
  71. package/plugin/skills/specgen-react-mui/references/component-patterns.md +1077 -0
  72. package/plugin/skills/specgen-react-mui/references/reporting-patterns.md +1998 -0
  73. package/plugin/skills/specgen-react-mui/references/routing-patterns.md +461 -0
  74. package/plugin/skills/specgen-react-mui/references/security-patterns.md +599 -0
  75. package/plugin/skills/specgen-react-mui/references/spec-template.md +1030 -0
  76. package/plugin/skills/specgen-react-mui/references/state-patterns.md +327 -0
  77. package/plugin/skills/specgen-react-tailwind/SKILL.md +859 -0
  78. package/plugin/skills/specgen-react-tailwind/references/component-patterns.md +1212 -0
  79. package/plugin/skills/specgen-react-tailwind/references/reporting-patterns.md +1529 -0
  80. package/plugin/skills/specgen-react-tailwind/references/routing-patterns.md +669 -0
  81. package/plugin/skills/specgen-react-tailwind/references/security-patterns.md +656 -0
  82. package/plugin/skills/specgen-react-tailwind/references/spec-template.md +1237 -0
  83. package/plugin/skills/specgen-react-tailwind/references/state-patterns.md +327 -0
  84. package/plugin/skills/specgen-sdk-java/SKILL.md +933 -0
  85. package/plugin/skills/specgen-sdk-java/references/api-extraction-patterns.md +247 -0
  86. package/plugin/skills/specgen-sdk-java/references/http-client-patterns.md +495 -0
  87. package/plugin/skills/specgen-sdk-java/references/packaging-patterns.md +366 -0
  88. package/plugin/skills/specgen-sdk-java/references/spec-template.md +956 -0
  89. package/plugin/skills/specgen-spring-jpa-jtehtmx/SKILL.md +1284 -0
  90. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/batch-patterns.md +1224 -0
  91. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/jasper-patterns.md +1638 -0
  92. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/messaging-patterns.md +512 -0
  93. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/modulith-patterns.md +354 -0
  94. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/security-patterns.md +551 -0
  95. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/spec-template.md +2502 -0
  96. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/web-patterns.md +503 -0
  97. package/plugin/skills/specgen-spring-jpa-restapi/SKILL.md +990 -0
  98. package/plugin/skills/specgen-spring-jpa-restapi/references/batch-patterns.md +1224 -0
  99. package/plugin/skills/specgen-spring-jpa-restapi/references/messaging-patterns.md +512 -0
  100. package/plugin/skills/specgen-spring-jpa-restapi/references/modulith-patterns.md +279 -0
  101. package/plugin/skills/specgen-spring-jpa-restapi/references/reporting-patterns.md +1536 -0
  102. package/plugin/skills/specgen-spring-jpa-restapi/references/restapi-patterns.md +722 -0
  103. package/plugin/skills/specgen-spring-jpa-restapi/references/security-patterns.md +351 -0
  104. package/plugin/skills/specgen-spring-jpa-restapi/references/spec-template.md +553 -0
  105. package/plugin/skills/specgen-ts-cli/SKILL.md +861 -0
  106. package/plugin/skills/specgen-ts-cli/references/async-patterns.md +1101 -0
  107. package/plugin/skills/specgen-ts-cli/references/command-patterns.md +383 -0
  108. package/plugin/skills/specgen-ts-cli/references/config-patterns.md +430 -0
  109. package/plugin/skills/specgen-ts-cli/references/database-patterns.md +750 -0
  110. package/plugin/skills/specgen-ts-cli/references/packaging-patterns.md +426 -0
  111. package/plugin/skills/specgen-ts-cli/references/spec-template.md +1391 -0
  112. package/plugin/skills/specgen-ts-cli/references/testing-patterns.md +909 -0
  113. package/plugin/skills/testgen-functional/SKILL.md +1381 -0
  114. package/plugin/skills/tracegen-matrix/SKILL.md +243 -0
  115. package/plugin/skills/util-preparek8senv/SKILL.md +422 -0
  116. package/plugin/skills/util-projectinit/SKILL.md +307 -0
  117. package/plugin/skills/util-projectinit/reference/brainstorm-loop.md +129 -0
  118. package/plugin/skills/util-projectinit/reference/brainstorm-protocol.md +144 -0
  119. package/plugin/skills/util-projectinit/reference/brainstorm-setup-guide.md +153 -0
  120. package/plugin/skills/util-projectinit/reference/brainstorm-template-appdev.md +87 -0
  121. package/plugin/skills/util-projectinit/reference/co2-context-generation-guide.md +761 -0
  122. package/plugin/skills/util-projectsync/SKILL.md +501 -0
  123. package/plugin/skills/util-updprd/SKILL.md +324 -0
  124. package/plugin/skills/util-usanalyzer/SKILL.md +307 -0
  125. package/plugin/skills/util-ustagger/SKILL.md +154 -0
  126. package/static/assets/abnfDiagram-VRR7QNED-DvPuwyCV.js +1 -0
  127. package/static/assets/arc-DRKw7sjs.js +1 -0
  128. package/static/assets/architectureDiagram-ZJ3FMSHR-CaNcxUbC.js +36 -0
  129. package/static/assets/blockDiagram-677ZJIJ3-Demd4AOK.js +132 -0
  130. package/static/assets/c4Diagram-LMCZKHZV-PVKUfc2x.js +10 -0
  131. package/static/assets/channel-C2nVbuwv.js +1 -0
  132. package/static/assets/chunk-2Q5K7J3B-CExf8ww_.js +1 -0
  133. package/static/assets/chunk-32BRIVSS-CFPaoONF.js +1 -0
  134. package/static/assets/chunk-5VM5RSS4-DE2esM67.js +15 -0
  135. package/static/assets/chunk-EX3LRPZG-SON_YdDG.js +231 -0
  136. package/static/assets/chunk-JWPE2WC7-Qxah8LNr.js +1 -0
  137. package/static/assets/chunk-MOJQB5TN-BcWGQV3j.js +88 -0
  138. package/static/assets/chunk-RYQCIY6F-DkDj-OR5.js +1 -0
  139. package/static/assets/chunk-V7JOEXUC-BlI1Mrah.js +206 -0
  140. package/static/assets/chunk-VR4S4FIN-Agf__duj.js +1 -0
  141. package/static/assets/chunk-XXDRQBXY-B5CfAh85.js +1 -0
  142. package/static/assets/classDiagram-OUVF2IWQ-DXJIljsR.js +1 -0
  143. package/static/assets/classDiagram-v2-EOCWNBFH-DXJIljsR.js +1 -0
  144. package/static/assets/cose-bilkent-JH36ORCC-DO0cQkcy.js +1 -0
  145. package/static/assets/cynefin-VYW2F7L2-CfOnsnXc.js +178 -0
  146. package/static/assets/cynefinDiagram-TSTJHNR4-BJ8tULAv.js +62 -0
  147. package/static/assets/cytoscape.esm-DTSO7Bv0.js +331 -0
  148. package/static/assets/dagre-VKFMJZFB-DfjwzHqP.js +4 -0
  149. package/static/assets/defaultLocale-DX6XiGOO.js +1 -0
  150. package/static/assets/diagram-FQU43EPY-CndSOIIM.js +3 -0
  151. package/static/assets/diagram-G47NLZAW-Czf5hN0Z.js +24 -0
  152. package/static/assets/diagram-NH7WQ7WH-COAyojGh.js +24 -0
  153. package/static/assets/diagram-OA4YK3LP-H8PoiN2m.js +30 -0
  154. package/static/assets/diagram-WEI45ONY-DzcaVzjp.js +41 -0
  155. package/static/assets/ebnfDiagram-CCIWWBDH-DPazGYLJ.js +1 -0
  156. package/static/assets/erDiagram-Q63AITRT-Bo-fubgF.js +85 -0
  157. package/static/assets/flowDiagram-23GEKE2U-CSvxUDwh.js +156 -0
  158. package/static/assets/ganttDiagram-NO4QXBWP-DnBveWX6.js +292 -0
  159. package/static/assets/gitGraphDiagram-IHSO6WYX-B3rVL8c-.js +106 -0
  160. package/static/assets/graph-C9eacEi8.js +1 -0
  161. package/static/assets/index-LZSQkLE2.css +41 -0
  162. package/static/assets/index-y7XDSgMG.js +495 -0
  163. package/static/assets/infoDiagram-FWYZ7A6U-CvWV9yy2.js +2 -0
  164. package/static/assets/init-Gi6I4Gst.js +1 -0
  165. package/static/assets/ishikawaDiagram-FXEZZL3T-DF28a1N6.js +70 -0
  166. package/static/assets/journeyDiagram-5HDEW3XC-CB6CugYk.js +139 -0
  167. package/static/assets/kanban-definition-HUTT4EX6-ResSLF6d.js +89 -0
  168. package/static/assets/katex-C5jXJg4s.js +257 -0
  169. package/static/assets/layout-DEXfKzaS.js +1 -0
  170. package/static/assets/linear-CLO--ryk.js +1 -0
  171. package/static/assets/map-Czzmt4hB.js +1 -0
  172. package/static/assets/mindmap-definition-LN4V7U3C-BvW3EbxX.js +96 -0
  173. package/static/assets/ordinal-Cboi1Yqb.js +1 -0
  174. package/static/assets/pegDiagram-2B236MQR-BHCgqrrM.js +1 -0
  175. package/static/assets/pieDiagram-ENE6RG2P-DVmCQ5FA.js +39 -0
  176. package/static/assets/quadrantDiagram-ABIIQ3AL-B3e5KR52.js +7 -0
  177. package/static/assets/railroadDiagram-RFXS5EU6-Csv5ysLc.js +1 -0
  178. package/static/assets/requirementDiagram-TGXJPOKE-DOMVzvnB.js +84 -0
  179. package/static/assets/sankeyDiagram-HTMAVEWB-B1dcTOvy.js +40 -0
  180. package/static/assets/sequenceDiagram-DBY2YBRQ-BY414yeO.js +162 -0
  181. package/static/assets/sizeCapture-X5ZJPWSS-CuJfrfBN.js +1 -0
  182. package/static/assets/stateDiagram-2N3HPSRC-FX4rJkzV.js +1 -0
  183. package/static/assets/stateDiagram-v2-6OUMAXLB-BVufbO_H.js +1 -0
  184. package/static/assets/swimlanes-5IMT3BWC-Sfy58mmv.js +2 -0
  185. package/static/assets/swimlanesDiagram-G3AALYLV-wDCDw08H.js +8 -0
  186. package/static/assets/timeline-definition-FHXFAJF6-CmV7by8p.js +120 -0
  187. package/static/assets/vennDiagram-L72KCM5P-FqIyFZFt.js +34 -0
  188. package/static/assets/wardleyDiagram-EHGQE667-BZ06O45i.js +78 -0
  189. package/static/assets/xychartDiagram-FW5EYKEG-BOkffR06.js +7 -0
  190. package/static/index.html +24 -0
@@ -0,0 +1,1268 @@
1
+ # Specification Template — Laravel Web Monolith
2
+
3
+ This is the authoritative template for the generated specification. The specification is
4
+ split into **two types of files**:
5
+
6
+ 1. **`SPECIFICATION.md`** (root) — Table of Contents, shared infrastructure, and all
7
+ application-level sections. Generated once per application.
8
+ 2. **`<module>/SPEC.md`** (per-module) — Self-contained module blueprint.
9
+ Generated once per module from PRD.md.
10
+
11
+ ```
12
+ spec/
13
+ ├── SPECIFICATION.md
14
+ ├── location-information/
15
+ │ └── SPEC.md
16
+ ├── corridor/
17
+ │ └── SPEC.md
18
+ └── ...
19
+ ```
20
+
21
+ Placeholders use `{{VARIABLE}}` syntax and must be replaced with actual values gathered
22
+ from context files.
23
+
24
+ Sections marked **[CONDITIONAL]** should only be included when the corresponding
25
+ integration is selected. Within unconditional sections, some content blocks are
26
+ conditional — follow the inline guidance.
27
+
28
+ **CRITICAL: Sample code requirement.** Every component in the spec MUST include a complete,
29
+ continuous code sample in a fenced code block (PHP, Blade, JavaScript, YAML, or HTML).
30
+ The code must be self-explanatory and directly usable as a reference for a coding agent.
31
+ Do not describe components with bullet points alone — always accompany descriptions with
32
+ full code.
33
+
34
+ ---
35
+
36
+ # Part A: Root SPECIFICATION.md
37
+
38
+ Everything from this point until "Part B" goes into the root `SPECIFICATION.md` file.
39
+ A coding agent implements these shared sections **first**, before any module work.
40
+
41
+ ---
42
+
43
+ ## Table of Contents
44
+
45
+ Generate a TOC with clickable Markdown anchor links to every H2 and H3 section in this
46
+ file. Additionally, include a **Modules** section in the TOC that links to each
47
+ module's `SPEC.md` file:
48
+
49
+ ```markdown
50
+ ## Table of Contents
51
+
52
+ ### Shared Infrastructure
53
+ - [1. Project Overview](#1-project-overview)
54
+ - [2. Composer & npm Configuration](#2-composer--npm-configuration)
55
+ - ...all shared sections...
56
+
57
+ ### Modules
58
+ - [Location Information](location-information/SPEC.md)
59
+ - [Corridor](corridor/SPEC.md)
60
+ - [Employer](employer/SPEC.md)
61
+ - ...(one link per module)...
62
+ ```
63
+
64
+ Only include conditional sections that apply based on user selections.
65
+
66
+ ---
67
+
68
+ ## Section 1: Project Overview
69
+
70
+ ```
71
+ # {{APPLICATION_NAME}} — Technical Specification
72
+
73
+ ## 1. Project Overview
74
+
75
+ **Application Name**: {{APPLICATION_NAME}} (from CLAUDE.md Custom Applications section)
76
+ **Project Slug**: {{PROJECT_SLUG}} (kebab-case of application name)
77
+ **PHP Version**: 8.3+
78
+ **Laravel Version**: 12.x
79
+ **Description**: {{APP_DESCRIPTION}} (from CLAUDE.md application description)
80
+ **Versions Covered**: v1.0.0 — v{{LATEST_VERSION}}
81
+
82
+ ### Optional Components (Auto-Determined)
83
+ **Database**: {{DATABASE}} (from CLAUDE.md dependencies)
84
+ **Authentication**: {{AUTH}} (from CLAUDE.md dependencies + PRD.md NFRs)
85
+ **Scheduling**: {{SCHEDULING}} (from PRD.md NFRs)
86
+ **Messaging**: {{MESSAGING}} (from CLAUDE.md dependencies)
87
+ **Internationalisation**: {{I18N}} (from PRD.md language requirements; e.g., `yes (en, ms)` or `no`)
88
+
89
+ ### Technology Stack
90
+ (Render the core stack version table from SKILL.md, plus selected optional integration versions)
91
+
92
+ ### User Roles
93
+ (List each user role extracted from mockup sidebar files, e.g., Hub Administrator, Hub Operation Support.
94
+ Include a role-to-module mapping table showing which modules each role can access.
95
+ This mapping drives middleware('role:...') on routes — NOT URL path prefixes.)
96
+
97
+ | Role | Modules | Permission Role |
98
+ |------|---------|----------------|
99
+ | Hub Administrator | Location Information, Corridor, Recruitment Step, ... | `HUB_ADMINISTRATOR` |
100
+ | Hub Operation Support | Employer, Quota, Quota Allocation, ... | `HUB_OPERATION_SUPPORT` |
101
+
102
+ ### Sidebar Navigation Items
103
+ (For each role, list the navigation items with module-based hrefs — NOT role-prefixed hrefs.)
104
+
105
+ **Hub Administrator:**
106
+ | Label | Href | Section |
107
+ |-------|------|---------|
108
+ | Location Information | `/location-information` | Geography |
109
+ | Corridor | `/corridor` | Administration |
110
+ | ... | ... | ... |
111
+
112
+ ### Modules
113
+ (List each module from PRD.md organized by System Module and Business Module,
114
+ with description from the module heading and user story count from MODEL.md summary table.
115
+ Each module links to its dedicated SPEC.md file.)
116
+
117
+ | Module | Type | Stories | Versions | Spec |
118
+ |--------|------|---------|----------|------|
119
+ | Location Information | Business | 12 | 1.0.0, 1.0.1, 1.0.3 | [SPEC](location-information/SPEC.md) |
120
+ | Corridor | Business | 8 | 1.0.0, 1.0.3 | [SPEC](corridor/SPEC.md) |
121
+
122
+ ### Input Sources
123
+ (List the context files read to produce this specification)
124
+ - CLAUDE.md: {{path}}
125
+ - PRD.md: {{path}}
126
+ - Module Model: {{path to MODEL.md}}
127
+ - Mockup Screens: {{path to MOCKUP.html}}
128
+ ```
129
+
130
+ ---
131
+
132
+ ## Section 2: Composer & npm Configuration
133
+
134
+ ### 2.1 composer.json
135
+
136
+ Generate a complete `composer.json` inside a code block. This section is critical — it must
137
+ be copy-pasteable and immediately functional.
138
+
139
+ Include:
140
+ - `"require"`:
141
+ - **Core**: `laravel/framework`, `nwidart/laravel-modules`, `spatie/laravel-data`, `spatie/laravel-view-models`
142
+ - **[If Database = MongoDB]** `mongodb/laravel-mongodb`
143
+ - **[If Auth = Keycloak]** `laravel/socialite`, `socialiteproviders/keycloak`
144
+ - **[If Auth = form]** `laravel/breeze`
145
+ - **RBAC**: `spatie/laravel-permission`
146
+ - **Audit**: `owen-it/laravel-auditing`
147
+ - **Health**: `spatie/laravel-health`
148
+ - **[If Messaging = yes]** `vladimir-yuldashev/laravel-queue-rabbitmq`, `php-amqplib/php-amqplib`
149
+ - **[If Reporting = yes]** `spatie/browsershot`, `maatwebsite/excel`
150
+ - `"require-dev"`:
151
+ - `phpunit/phpunit`, `phpat/phpat`, `laravel/pint`
152
+
153
+ ### 2.2 package.json
154
+
155
+ ```json
156
+ {
157
+ "private": true,
158
+ "type": "module",
159
+ "scripts": {
160
+ "dev": "vite",
161
+ "build": "vite build"
162
+ },
163
+ "devDependencies": {
164
+ "laravel-vite-plugin": "^1.2",
165
+ "vite": "^6.0",
166
+ "tailwindcss": "^4.0",
167
+ "@tailwindcss/vite": "^4.0",
168
+ "autoprefixer": "^10.4",
169
+ "postcss": "^8.4"
170
+ },
171
+ "dependencies": {
172
+ "alpinejs": "^3.14",
173
+ "htmx.org": "^2.0"
174
+ }
175
+ }
176
+ ```
177
+
178
+ ---
179
+
180
+ ## Section 3: Application Configuration
181
+
182
+ Generate complete configuration files.
183
+
184
+ **IMPORTANT: Environment variable externalization.** All configuration values that may
185
+ change between environments (ports, hostnames, credentials, URIs) MUST be externalized
186
+ to environment variables. Laravel's `env('VAR', 'default')` helper is used in config
187
+ files. The `.env` file provides local development defaults, while production deployments
188
+ override via system environment variables or `.env.production`.
189
+
190
+ ### .env (default)
191
+
192
+ **Always include:**
193
+ ```env
194
+ APP_NAME={{APPLICATION_NAME}}
195
+ APP_ENV=local
196
+ APP_KEY=
197
+ APP_DEBUG=true
198
+ APP_URL=http://localhost:8000
199
+
200
+ LOG_CHANNEL=stack
201
+ LOG_LEVEL=debug
202
+ ```
203
+
204
+ **[If Database = MongoDB] add:**
205
+ ```env
206
+ DB_CONNECTION=mongodb
207
+ DB_URI=mongodb://localhost:27017/{{DB_NAME}}
208
+ DB_DATABASE={{DB_NAME}}
209
+ ```
210
+
211
+ **[If Database = PostgreSQL] add:**
212
+ ```env
213
+ DB_CONNECTION=pgsql
214
+ DB_HOST=localhost
215
+ DB_PORT=5432
216
+ DB_DATABASE={{DB_NAME}}
217
+ DB_USERNAME={{DB_USER}}
218
+ DB_PASSWORD={{DB_PASSWORD}}
219
+ ```
220
+
221
+ **[If Database = MySQL] add:**
222
+ ```env
223
+ DB_CONNECTION=mysql
224
+ DB_HOST=localhost
225
+ DB_PORT=3306
226
+ DB_DATABASE={{DB_NAME}}
227
+ DB_USERNAME={{DB_USER}}
228
+ DB_PASSWORD={{DB_PASSWORD}}
229
+ ```
230
+
231
+ **[If Auth = Keycloak] add:**
232
+ ```env
233
+ KEYCLOAK_CLIENT_ID={{KEYCLOAK_CLIENT_ID}}
234
+ KEYCLOAK_CLIENT_SECRET={{KEYCLOAK_CLIENT_SECRET}}
235
+ KEYCLOAK_REALM={{KEYCLOAK_REALM}}
236
+ KEYCLOAK_BASE_URL=http://localhost:8180
237
+ KEYCLOAK_REDIRECT_URI=${APP_URL}/auth/callback
238
+ ```
239
+
240
+ **[If Messaging = yes] add:**
241
+ ```env
242
+ RABBITMQ_HOST=localhost
243
+ RABBITMQ_PORT=5672
244
+ RABBITMQ_USER=guest
245
+ RABBITMQ_PASSWORD=guest
246
+ RABBITMQ_VHOST=/
247
+ RABBITMQ_QUEUE=default
248
+ ```
249
+
250
+ ### config/database.php
251
+
252
+ **[If Database = MongoDB] add connection:**
253
+ ```php
254
+ 'mongodb' => [
255
+ 'driver' => 'mongodb',
256
+ 'dsn' => env('DB_URI', 'mongodb://localhost:27017'),
257
+ 'database' => env('DB_DATABASE', '{{DB_NAME}}'),
258
+ ],
259
+ ```
260
+
261
+ **[If Database = PostgreSQL] add connection:**
262
+ ```php
263
+ 'pgsql' => [
264
+ 'driver' => 'pgsql',
265
+ 'host' => env('DB_HOST', 'localhost'),
266
+ 'port' => env('DB_PORT', '5432'),
267
+ 'database' => env('DB_DATABASE', '{{DB_NAME}}'),
268
+ 'username' => env('DB_USERNAME', '{{DB_USER}}'),
269
+ 'password' => env('DB_PASSWORD', ''),
270
+ 'charset' => env('DB_CHARSET', 'utf8'),
271
+ 'prefix' => '',
272
+ 'schema' => 'public',
273
+ 'sslmode' => env('DB_SSLMODE', 'prefer'),
274
+ ],
275
+ ```
276
+
277
+ **[If Database = MySQL] add connection:**
278
+ ```php
279
+ 'mysql' => [
280
+ 'driver' => 'mysql',
281
+ 'host' => env('DB_HOST', 'localhost'),
282
+ 'port' => env('DB_PORT', '3306'),
283
+ 'database' => env('DB_DATABASE', '{{DB_NAME}}'),
284
+ 'username' => env('DB_USERNAME', '{{DB_USER}}'),
285
+ 'password' => env('DB_PASSWORD', ''),
286
+ 'unix_socket' => env('DB_SOCKET', ''),
287
+ 'charset' => env('DB_CHARSET', 'utf8mb4'),
288
+ 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
289
+ 'prefix' => '',
290
+ 'strict' => true,
291
+ 'engine' => null,
292
+ ],
293
+ ```
294
+
295
+ **[If Auth = Keycloak] add to config/services.php:**
296
+ ```php
297
+ 'keycloak' => [
298
+ 'client_id' => env('KEYCLOAK_CLIENT_ID'),
299
+ 'client_secret' => env('KEYCLOAK_CLIENT_SECRET'),
300
+ 'realm' => env('KEYCLOAK_REALM'),
301
+ 'base_url' => env('KEYCLOAK_BASE_URL', 'http://localhost:8180'),
302
+ 'redirect' => env('KEYCLOAK_REDIRECT_URI', env('APP_URL') . '/auth/callback'),
303
+ ],
304
+ ```
305
+
306
+ **[If Messaging = yes] add to config/messaging.php:**
307
+ ```php
308
+ return [
309
+ 'rabbitmq' => [
310
+ 'host' => env('RABBITMQ_HOST', 'localhost'),
311
+ 'port' => env('RABBITMQ_PORT', 5672),
312
+ 'user' => env('RABBITMQ_USER', 'guest'),
313
+ 'password' => env('RABBITMQ_PASSWORD', 'guest'),
314
+ 'vhost' => env('RABBITMQ_VHOST', '/'),
315
+ 'queue' => env('RABBITMQ_QUEUE', 'default'),
316
+ ],
317
+ ];
318
+ ```
319
+
320
+ ### config/logging.php
321
+
322
+ ```php
323
+ 'channels' => [
324
+ 'stack' => [
325
+ 'driver' => 'stack',
326
+ 'channels' => ['daily'],
327
+ ],
328
+ 'daily' => [
329
+ 'driver' => 'daily',
330
+ 'path' => storage_path('logs/laravel.log'),
331
+ 'level' => env('LOG_LEVEL', 'debug'),
332
+ 'days' => 14,
333
+ ],
334
+ ],
335
+ ```
336
+
337
+ ---
338
+
339
+ ## Section 4: Build & Tooling
340
+
341
+ ### 4.1 Resource Directory Structure
342
+ ```
343
+ resources/
344
+ ├── css/
345
+ │ ├── app.css # Main entry — imports Tailwind + partials
346
+ │ ├── base/
347
+ │ │ ├── reset.css # Tailwind @layer base
348
+ │ │ └── typography.css # Font imports, text defaults
349
+ │ ├── components/
350
+ │ │ ├── buttons.css # @layer components — button styles
351
+ │ │ ├── forms.css # Input, select, textarea styles
352
+ │ │ ├── cards.css # Card component styles
353
+ │ │ ├── tables.css # Table styles
354
+ │ │ ├── modals.css # Modal/dialog styles
355
+ │ │ └── navigation.css # Nav, breadcrumb, tabs styles
356
+ │ ├── layouts/
357
+ │ │ ├── sidebar.css # Sidebar responsive behavior
358
+ │ │ ├── navbar.css # Header/navbar styles
359
+ │ │ └── footer.css # Footer styles
360
+ │ └── themes/
361
+ │ ├── light.css # Light theme CSS custom properties
362
+ │ └── dark.css # Dark theme CSS custom properties
363
+ ├── js/
364
+ │ ├── app.js # Entry — registers Alpine, htmx, global stores
365
+ │ ├── alpine/
366
+ │ │ ├── stores/
367
+ │ │ │ ├── toast.js # Toast notification store
368
+ │ │ │ ├── modal.js # Modal state store
369
+ │ │ │ ├── drawer.js # Sidebar drawer store
370
+ │ │ │ └── theme.js # Theme toggle store
371
+ │ │ └── components/
372
+ │ │ ├── dropdown.js # Dropdown behavior
373
+ │ │ ├── tabs.js # Tab switching
374
+ │ │ └── collapse.js # Accordion/collapse
375
+ │ └── htmx/
376
+ │ ├── index.js # Global error/loading handlers
377
+ │ └── extensions/
378
+ │ ├── toast-errors.js # Map htmx errors to toast notifications
379
+ │ └── loading-states.js # Toggle loading classes during requests
380
+ └── views/
381
+ ├── layouts/
382
+ │ ├── app.blade.php # Main authenticated layout
383
+ │ ├── auth.blade.php # [If Auth != none] Login layout
384
+ │ └── error.blade.php # Error layout
385
+ ├── partials/
386
+ │ ├── header.blade.php # Header bar
387
+ │ ├── sidebar.blade.php # Sidebar navigation
388
+ │ ├── footer.blade.php # Footer
389
+ │ └── theme-switcher.blade.php
390
+ └── components/
391
+ ├── alert.blade.php
392
+ ├── avatar.blade.php
393
+ ├── badge.blade.php
394
+ ├── breadcrumb.blade.php
395
+ ├── button.blade.php
396
+ ├── card.blade.php
397
+ ├── form-control.blade.php
398
+ ├── input.blade.php
399
+ ├── select.blade.php
400
+ ├── loading.blade.php
401
+ ├── modal.blade.php
402
+ ├── pagination.blade.php
403
+ ├── table.blade.php
404
+ ├── tabs.blade.php
405
+ └── toast-container.blade.php
406
+ ```
407
+
408
+ ### 4.2 Vite Configuration
409
+ ```js
410
+ // vite.config.js
411
+ import { defineConfig } from 'vite';
412
+ import laravel from 'laravel-vite-plugin';
413
+ import tailwindcss from '@tailwindcss/vite';
414
+
415
+ export default defineConfig({
416
+ plugins: [
417
+ laravel({
418
+ input: ['resources/css/app.css', 'resources/js/app.js'],
419
+ refresh: true,
420
+ }),
421
+ tailwindcss(),
422
+ ],
423
+ });
424
+ ```
425
+
426
+ ### 4.3 Tailwind Configuration
427
+ ```js
428
+ // tailwind.config.js
429
+ export default {
430
+ content: [
431
+ './resources/**/*.blade.php',
432
+ './resources/**/*.js',
433
+ './Modules/*/resources/views/**/*.blade.php',
434
+ ],
435
+ darkMode: ['class', '[data-theme="dark"]'],
436
+ theme: {
437
+ extend: {
438
+ colors: {
439
+ primary: { 50: '#eff6ff', /* ... full scale */ 900: '#1e3a5f' },
440
+ accent: { 50: '#f0fdf4', /* ... full scale */ 900: '#14532d' },
441
+ },
442
+ },
443
+ },
444
+ plugins: [],
445
+ };
446
+ ```
447
+
448
+ ---
449
+
450
+ ## Section 4b: `.gitignore`
451
+
452
+ See SKILL.md Section 4b for the complete `.gitignore` content.
453
+
454
+ ---
455
+
456
+ ## Section 5: Directory Structure
457
+
458
+ Generate the full directory tree. The structure must follow nwidart/laravel-modules
459
+ conventions adapted for server-rendered web applications.
460
+
461
+ ```
462
+ {{PROJECT_SLUG}}/
463
+ ├── app/
464
+ │ ├── Console/
465
+ │ │ └── Commands/
466
+ │ │ ├── [If Scheduling] CleanupExpiredRecordsCommand.php
467
+ │ │ ├── [If Batch] ProcessDataExportCommand.php
468
+ │ │ └── [If Messaging] SetupRabbitMQCommand.php
469
+ │ ├── Constants/
470
+ │ │ └── Roles.php
471
+ │ ├── Contracts/
472
+ │ │ └── [If Reporting] ReportDefinition.php
473
+ │ ├── Exceptions/
474
+ │ │ ├── Handler.php
475
+ │ │ └── WebApplicationException.php
476
+ │ ├── Http/
477
+ │ │ ├── Controllers/
478
+ │ │ │ ├── [If Auth = Keycloak] Auth/KeycloakAuthController.php
479
+ │ │ │ └── [If Reporting] ReportController.php
480
+ │ │ └── Middleware/
481
+ │ │ ├── CspNonceMiddleware.php
482
+ │ │ ├── ThemeMiddleware.php
483
+ │ │ └── CorrelationIdMiddleware.php
484
+ │ ├── Models/
485
+ │ │ ├── User.php
486
+ │ │ └── [If Reporting] Report.php
487
+ │ ├── Providers/
488
+ │ │ ├── AppServiceProvider.php
489
+ │ │ └── EventServiceProvider.php
490
+ │ ├── Services/
491
+ │ │ ├── [If Auth = Keycloak] KeycloakRoleMapper.php
492
+ │ │ ├── SecurityContext.php
493
+ │ │ ├── [If Messaging] Messaging/
494
+ │ │ │ ├── RabbitMQInfrastructure.php
495
+ │ │ │ └── MessagePublisher.php
496
+ │ │ └── [If Reporting] Reporting/
497
+ │ │ ├── ReportService.php
498
+ │ │ ├── ReportRegistry.php
499
+ │ │ └── GenericReportExport.php
500
+ │ ├── Traits/
501
+ │ │ └── Blameable.php
502
+ │ └── Jobs/
503
+ │ ├── [If Batch] ProcessExportChunkJob.php
504
+ │ └── [If Messaging] Consumers/
505
+ │ └── OrderEventConsumer.php
506
+
507
+ ├── Modules/
508
+ │ ├── {{Module1}}/
509
+ │ │ ├── Contracts/
510
+ │ │ │ └── {{Module1}}ServiceInterface.php
511
+ │ │ ├── DTOs/
512
+ │ │ │ └── {{Module1}}Data.php
513
+ │ │ ├── Events/
514
+ │ │ │ └── {{Module1}}CreatedEvent.php
515
+ │ │ ├── Exceptions/
516
+ │ │ │ └── {{Module1}}Exception.php
517
+ │ │ ├── Services/
518
+ │ │ │ └── {{Module1}}Service.php
519
+ │ │ ├── Models/
520
+ │ │ │ └── {{Module1}}.php
521
+ │ │ ├── Http/
522
+ │ │ │ ├── Controllers/
523
+ │ │ │ │ ├── {{Module1}}PageController.php
524
+ │ │ │ │ └── {{Module1}}FragmentController.php
525
+ │ │ │ └── Requests/
526
+ │ │ │ ├── Store{{Module1}}Request.php
527
+ │ │ │ └── Update{{Module1}}Request.php
528
+ │ │ ├── ViewModels/
529
+ │ │ │ ├── {{Module1}}ListViewModel.php
530
+ │ │ │ └── {{Module1}}DetailViewModel.php
531
+ │ │ ├── database/
532
+ │ │ │ └── migrations/
533
+ │ │ ├── resources/
534
+ │ │ │ └── views/
535
+ │ │ │ ├── pages/
536
+ │ │ │ │ ├── index.blade.php
537
+ │ │ │ │ ├── show.blade.php
538
+ │ │ │ │ ├── create.blade.php
539
+ │ │ │ │ └── edit.blade.php
540
+ │ │ │ └── fragments/
541
+ │ │ │ └── {{module1}}-row.blade.php
542
+ │ │ ├── routes/
543
+ │ │ │ └── web.php
544
+ │ │ ├── Providers/
545
+ │ │ │ └── {{Module1}}ServiceProvider.php
546
+ │ │ └── Tests/
547
+ │ │
548
+ │ └── {{Module2}}/
549
+ │ └── (same structure)
550
+
551
+ ├── resources/
552
+ │ ├── css/ # (see Section 4.1)
553
+ │ ├── js/ # (see Section 4.1)
554
+ │ └── views/ # Shared views (see Section 4.1)
555
+
556
+ ├── routes/
557
+ │ ├── web.php # Core routes (auth, reports)
558
+ │ └── console.php # Scheduled commands
559
+
560
+ ├── config/
561
+ │ ├── app.php
562
+ │ ├── auth.php
563
+ │ ├── database.php
564
+ │ ├── logging.php
565
+ │ ├── queue.php
566
+ │ ├── services.php
567
+ │ ├── [If Messaging] messaging.php
568
+ │ └── modules.php
569
+
570
+ ├── database/
571
+ │ └── migrations/ # Core migrations (users, roles, etc.)
572
+
573
+ ├── bootstrap/
574
+ │ └── app.php # Middleware registration
575
+
576
+ ├── public/
577
+ │ └── build/ # Vite build output (gitignored)
578
+
579
+ ├── storage/
580
+ ├── tests/
581
+ │ ├── Architecture/
582
+ │ │ └── ModuleBoundaryTest.php
583
+ │ ├── Feature/
584
+ │ └── Unit/
585
+
586
+ ├── composer.json
587
+ ├── package.json
588
+ ├── vite.config.js
589
+ ├── tailwind.config.js
590
+ ├── .env.example
591
+ └── .gitignore
592
+ ```
593
+
594
+ ---
595
+
596
+ ## Sections 6-23
597
+
598
+ For Sections 6 through 23, follow the same structure as described in `SKILL.md` under
599
+ "What Goes in SPECIFICATION.md (Root)". Each section must include complete code samples
600
+ in the appropriate language (PHP, Blade, JavaScript, CSS, YAML).
601
+
602
+ Reference the corresponding `references/` files for detailed patterns:
603
+ - Section 6 (Security): `references/security-patterns.md`
604
+ - Section 7-12 (Web/UI): `references/web-patterns.md`
605
+ - Section 18 (Scheduling): `references/batch-patterns.md`
606
+ - Section 19 (Events): `references/modulith-patterns.md`
607
+ - Section 20 (Messaging): `references/messaging-patterns.md`
608
+ - Section 23 (Reporting): `references/reporting-patterns.md`
609
+
610
+ ---
611
+
612
+ ## Section 24: Internationalisation (Localization) [CONDITIONAL — Include only if i18n = yes]
613
+
614
+ > Omit this entire section when i18n = no. When omitted, do NOT render a locale/language
615
+ > switcher anywhere — Blade templates use literal copy.
616
+
617
+ Uses Laravel's **built-in localization** — no third-party package. The active locale is
618
+ resolved per request, persisted across requests, and exposed to every Blade view.
619
+
620
+ ### 24.1 Configuration (`config/app.php`)
621
+
622
+ ```php
623
+ // config/app.php
624
+ 'locale' => env('APP_LOCALE', 'en'),
625
+ 'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'),
626
+
627
+ // Application-owned: the locales this app actually ships translations for.
628
+ 'supported_locales' => [
629
+ 'en' => 'English',
630
+ 'ms' => 'Bahasa Malaysia',
631
+ ],
632
+ ```
633
+
634
+ `.env` additions:
635
+
636
+ ```properties
637
+ APP_LOCALE=en
638
+ APP_FALLBACK_LOCALE=en
639
+ ```
640
+
641
+ ### 24.2 Locale Resolution Middleware
642
+
643
+ ```php
644
+ // app/Http/Middleware/SetLocale.php
645
+ namespace App\Http\Middleware;
646
+
647
+ use Closure;
648
+ use Illuminate\Http\Request;
649
+
650
+ class SetLocale
651
+ {
652
+ public function handle(Request $request, Closure $next)
653
+ {
654
+ $supported = array_keys(config('app.supported_locales'));
655
+
656
+ $locale = $request->session()->get('locale')
657
+ ?? $request->cookie('locale')
658
+ ?? $request->getPreferredLanguage($supported) // Accept-Language negotiation
659
+ ?? config('app.locale');
660
+
661
+ if (! in_array($locale, $supported, true)) {
662
+ $locale = config('app.locale');
663
+ }
664
+
665
+ app()->setLocale($locale);
666
+
667
+ return $next($request);
668
+ }
669
+ }
670
+ ```
671
+
672
+ Register on the `web` middleware group in `bootstrap/app.php`:
673
+
674
+ ```php
675
+ ->withMiddleware(function (Middleware $middleware) {
676
+ $middleware->web(append: [\App\Http\Middleware\SetLocale::class]);
677
+ })
678
+ ```
679
+
680
+ ### 24.3 Locale Switch Route + Controller
681
+
682
+ ```php
683
+ // routes/web.php
684
+ Route::post('/locale/{locale}', [\App\Http\Controllers\LocaleController::class, 'switch'])
685
+ ->name('locale.switch');
686
+ ```
687
+
688
+ ```php
689
+ // app/Http/Controllers/LocaleController.php
690
+ namespace App\Http\Controllers;
691
+
692
+ use Illuminate\Http\Request;
693
+
694
+ class LocaleController extends Controller
695
+ {
696
+ public function switch(Request $request, string $locale)
697
+ {
698
+ abort_unless(array_key_exists($locale, config('app.supported_locales')), 404);
699
+
700
+ $request->session()->put('locale', $locale);
701
+ cookie()->queue(cookie('locale', $locale, 60 * 24 * 365)); // 1 year
702
+
703
+ // htmx requests: ask the client to do a full refresh so all copy re-renders.
704
+ if ($request->header('HX-Request')) {
705
+ return response()->noContent()->header('HX-Refresh', 'true');
706
+ }
707
+
708
+ return redirect()->back();
709
+ }
710
+ }
711
+ ```
712
+
713
+ ### 24.4 Language Switcher Partial (header)
714
+
715
+ ```blade
716
+ {{-- resources/views/partials/locale-switcher.blade.php --}}
717
+ <div x-data="{ open: false }" class="relative">
718
+ <button @click="open = !open" type="button"
719
+ class="inline-flex items-center gap-1 rounded-md px-2 py-1 text-sm text-gray-600 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700">
720
+ <svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
721
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
722
+ d="M3 5h12M9 3v2m1.048 9.5A18.022 18.022 0 016.412 9m6.088 9h7M11 21l5-10 5 10M12.751 5C11.783 10.77 8.07 15.61 3 18.129"/>
723
+ </svg>
724
+ <span>{{ config('app.supported_locales')[app()->getLocale()] }}</span>
725
+ </button>
726
+ <div x-show="open" @click.outside="open = false" x-cloak
727
+ class="absolute right-0 mt-1 w-40 rounded-md border border-gray-200 bg-white py-1 shadow-lg dark:border-gray-700 dark:bg-gray-800">
728
+ @foreach (config('app.supported_locales') as $code => $label)
729
+ <form method="POST" action="{{ route('locale.switch', $code) }}"
730
+ hx-post="{{ route('locale.switch', $code) }}" hx-swap="none">
731
+ @csrf
732
+ <button type="submit"
733
+ class="block w-full px-3 py-1.5 text-left text-sm hover:bg-gray-100 dark:hover:bg-gray-700
734
+ {{ app()->getLocale() === $code ? 'font-semibold text-primary-600' : 'text-gray-700 dark:text-gray-200' }}">
735
+ {{ $label }}
736
+ </button>
737
+ </form>
738
+ @endforeach
739
+ </div>
740
+ </div>
741
+ ```
742
+
743
+ Include it in `resources/views/partials/header.blade.php` next to the theme switcher.
744
+
745
+ ### 24.5 Translation Files
746
+
747
+ Application-level strings:
748
+
749
+ ```php
750
+ // lang/en/messages.php
751
+ return [
752
+ 'welcome' => 'Welcome',
753
+ 'items_count' => '{0} No items|{1} :count item|[2,*] :count items', // trans_choice()
754
+ ];
755
+
756
+ // lang/ms/messages.php
757
+ return [
758
+ 'welcome' => 'Selamat datang',
759
+ 'items_count' => '{0} Tiada item|{1} :count item|[2,*] :count item',
760
+ ];
761
+ ```
762
+
763
+ Localized validation messages live in `lang/<locale>/validation.php` (publish Laravel's
764
+ defaults, then add per-locale overrides).
765
+
766
+ ### 24.6 Blade Usage Conventions
767
+
768
+ ```blade
769
+ <h1>{{ __('messages.welcome') }}</h1>
770
+ <p>{{ trans_choice('messages.items_count', $count, ['count' => $count]) }}</p>
771
+
772
+ {{-- Module-namespaced key (registered by the module service provider) --}}
773
+ <h2>{{ __('location-information::messages.title') }}</h2>
774
+
775
+ {{-- Locale-aware date --}}
776
+ <span>{{ $record->created_at->locale(app()->getLocale())->translatedFormat('d F Y') }}</span>
777
+ ```
778
+
779
+ > **Per-module translations.** Each module registers its own namespace in its service
780
+ > provider — `$this->loadTranslationsFrom(module_path('LocationInformation', 'lang'), 'location-information');`
781
+ > — so module copy resolves via `__('location-information::messages.<key>')`. See the
782
+ > per-module SPEC.md template for the module-level translation file.
783
+
784
+ ---
785
+
786
+ # Part B: Per-Module SPEC.md Template
787
+
788
+ Each module from PRD.md gets its own `<module-name>/SPEC.md` file.
789
+
790
+ ---
791
+
792
+ ## Module SPEC.md Template
793
+
794
+ ```markdown
795
+ # {{MODULE_NAME}} — Module Specification
796
+
797
+ > Back to [SPECIFICATION.md](../SPECIFICATION.md)
798
+
799
+ ## 1. Traceability
800
+
801
+ ### User Stories
802
+ | ID | Description | Version |
803
+ |----|-------------|---------|
804
+ | USHM00108 | As a user, I want to view... | v1.0.0 |
805
+
806
+ ### Non-Functional Requirements
807
+ | ID | Description | Version |
808
+ |----|-------------|---------|
809
+ | NFRHM0120 | Data must be stored in... | v1.0.0 |
810
+
811
+ ### Constraints
812
+ | ID | Description | Version |
813
+ |----|-------------|---------|
814
+ | CONSHM042 | Can only change name and... | v1.0.0 |
815
+
816
+ ### Removed / Replaced
817
+ | ID | Removed In | Replaced By | Reason |
818
+ |----|-----------|-------------|--------|
819
+ | USHM00015 | v1.0.3 | USHM00222 | Consolidated into... |
820
+ _(or "None." if no removals)_
821
+
822
+ ### Data Sources
823
+ | Artifact | Reference |
824
+ |----------|-----------|
825
+ | Collection/Table | `{{collection_or_table_name}}` |
826
+ | Mockup Screen | `mockup/{{role}}/content/{{screen}}.html` |
827
+
828
+ ## 2. Service Contract
829
+
830
+ ```php
831
+ namespace Modules\{{Module}}\Contracts;
832
+
833
+ interface {{Module}}ServiceInterface
834
+ {
835
+ public function list(int $page = 1, int $perPage = 20): LengthAwarePaginator;
836
+ public function get(string $id): {{Module}}Data;
837
+ public function create(array $data): {{Module}}Data;
838
+ public function update(string $id, array $data): {{Module}}Data;
839
+ public function delete(string $id): void;
840
+ // Additional methods from user stories...
841
+ }
842
+ ```
843
+
844
+ ## 3. DTO (spatie/laravel-data)
845
+
846
+ ```php
847
+ namespace Modules\{{Module}}\DTOs;
848
+
849
+ use Spatie\LaravelData\Data;
850
+
851
+ class {{Module}}Data extends Data
852
+ {
853
+ public function __construct(
854
+ public readonly ?string $id,
855
+ // Fields from model/<module>/model.md...
856
+ public readonly ?string $created_by,
857
+ public readonly ?string $updated_by,
858
+ public readonly ?\DateTimeImmutable $created_at,
859
+ public readonly ?\DateTimeImmutable $updated_at,
860
+ ) {}
861
+ }
862
+ ```
863
+
864
+ ## 4. Exception
865
+
866
+ ```php
867
+ namespace Modules\{{Module}}\Exceptions;
868
+
869
+ use App\Exceptions\WebApplicationException;
870
+
871
+ class {{Module}}Exception extends WebApplicationException
872
+ {
873
+ public static function notFound(string $id): self
874
+ {
875
+ return new self("{{Module}} not found: {$id}", 404);
876
+ }
877
+ }
878
+ ```
879
+
880
+ ## 5. Eloquent Model
881
+
882
+ ```php
883
+ namespace Modules\{{Module}}\Models;
884
+
885
+ use App\Traits\Blameable;
886
+ use Illuminate\Database\Eloquent\Concerns\HasUuids;
887
+ use Illuminate\Database\Eloquent\Model;
888
+
889
+ class {{Module}} extends Model
890
+ {
891
+ use HasUuids, Blameable;
892
+
893
+ protected $table = '{{table_name}}';
894
+
895
+ protected $fillable = [
896
+ // Fields from model/<module>/model.md...
897
+ ];
898
+
899
+ protected $casts = [
900
+ // Type casts...
901
+ ];
902
+ }
903
+ ```
904
+
905
+ ## 6. Migration [If Database = PostgreSQL/MySQL]
906
+
907
+ ```php
908
+ use Illuminate\Database\Migrations\Migration;
909
+ use Illuminate\Database\Schema\Blueprint;
910
+ use Illuminate\Support\Facades\Schema;
911
+
912
+ return new class extends Migration
913
+ {
914
+ public function up(): void
915
+ {
916
+ Schema::create('{{table_name}}', function (Blueprint $table) {
917
+ $table->uuid('id')->primary();
918
+ // Fields from model/<module>/model.md...
919
+ $table->string('created_by', 100)->nullable();
920
+ $table->string('updated_by', 100)->nullable();
921
+ $table->timestamps();
922
+ // Indexes from model/<module>/model.md...
923
+ });
924
+ }
925
+
926
+ public function down(): void
927
+ {
928
+ Schema::dropIfExists('{{table_name}}');
929
+ }
930
+ };
931
+ ```
932
+
933
+ ## 7. Service Implementation
934
+
935
+ ```php
936
+ namespace Modules\{{Module}}\Services;
937
+
938
+ use Modules\{{Module}}\Contracts\{{Module}}ServiceInterface;
939
+ use Modules\{{Module}}\DTOs\{{Module}}Data;
940
+ use Modules\{{Module}}\Events\{{Module}}CreatedEvent;
941
+ use Modules\{{Module}}\Exceptions\{{Module}}Exception;
942
+ use Modules\{{Module}}\Models\{{Module}};
943
+
944
+ class {{Module}}Service implements {{Module}}ServiceInterface
945
+ {
946
+ public function list(int $page = 1, int $perPage = 20): LengthAwarePaginator
947
+ {
948
+ return {{Module}}::orderByDesc('created_at')->paginate($perPage);
949
+ }
950
+
951
+ public function get(string $id): {{Module}}Data
952
+ {
953
+ $model = {{Module}}::findOrFail($id);
954
+ return {{Module}}Data::from($model);
955
+ }
956
+
957
+ public function create(array $data): {{Module}}Data
958
+ {
959
+ $model = {{Module}}::create($data);
960
+
961
+ event(new {{Module}}CreatedEvent(
962
+ id: $model->id,
963
+ occurredAt: new \DateTimeImmutable(),
964
+ ));
965
+
966
+ return {{Module}}Data::from($model);
967
+ }
968
+
969
+ public function update(string $id, array $data): {{Module}}Data
970
+ {
971
+ $model = {{Module}}::findOrFail($id);
972
+ $model->update($data);
973
+ return {{Module}}Data::from($model->fresh());
974
+ }
975
+
976
+ public function delete(string $id): void
977
+ {
978
+ $model = {{Module}}::findOrFail($id);
979
+ $model->delete();
980
+ }
981
+ }
982
+ ```
983
+
984
+ ## 8. Form Requests
985
+
986
+ ```php
987
+ namespace Modules\{{Module}}\Http\Requests;
988
+
989
+ use Illuminate\Foundation\Http\FormRequest;
990
+
991
+ class Store{{Module}}Request extends FormRequest
992
+ {
993
+ public function authorize(): bool { return true; }
994
+
995
+ public function rules(): array
996
+ {
997
+ return [
998
+ // Validation rules from model + mockup screens...
999
+ ];
1000
+ }
1001
+ }
1002
+ ```
1003
+
1004
+ ## 9. Page Controller
1005
+
1006
+ ```php
1007
+ namespace Modules\{{Module}}\Http\Controllers;
1008
+
1009
+ use Illuminate\Http\RedirectResponse;
1010
+ use Illuminate\Http\Request;
1011
+ use Illuminate\View\View;
1012
+ use Modules\{{Module}}\Contracts\{{Module}}ServiceInterface;
1013
+ use Modules\{{Module}}\Http\Requests\Store{{Module}}Request;
1014
+ use Modules\{{Module}}\Http\Requests\Update{{Module}}Request;
1015
+ use Modules\{{Module}}\ViewModels\{{Module}}ListViewModel;
1016
+ use Modules\{{Module}}\ViewModels\{{Module}}DetailViewModel;
1017
+
1018
+ class {{Module}}PageController extends Controller
1019
+ {
1020
+ public function __construct(
1021
+ private readonly {{Module}}ServiceInterface $service,
1022
+ ) {}
1023
+
1024
+ public function index(Request $request): View
1025
+ {
1026
+ $items = $this->service->list(
1027
+ page: $request->integer('page', 1),
1028
+ );
1029
+ return (new {{Module}}ListViewModel(items: $items))
1030
+ ->view('{{module}}::pages.index');
1031
+ }
1032
+
1033
+ public function show(string $id): View
1034
+ {
1035
+ $item = $this->service->get($id);
1036
+ return (new {{Module}}DetailViewModel(item: $item))
1037
+ ->view('{{module}}::pages.show');
1038
+ }
1039
+
1040
+ public function create(): View
1041
+ {
1042
+ return view('{{module}}::pages.create');
1043
+ }
1044
+
1045
+ public function store(Store{{Module}}Request $request): RedirectResponse
1046
+ {
1047
+ $this->service->create($request->validated());
1048
+ return redirect()->route('{{module}}.index')
1049
+ ->with('success', '{{Module}} created successfully.');
1050
+ }
1051
+
1052
+ public function edit(string $id): View
1053
+ {
1054
+ $item = $this->service->get($id);
1055
+ return view('{{module}}::pages.edit', compact('item'));
1056
+ }
1057
+
1058
+ public function update(Update{{Module}}Request $request, string $id): RedirectResponse
1059
+ {
1060
+ $this->service->update($id, $request->validated());
1061
+ return redirect()->route('{{module}}.show', $id)
1062
+ ->with('success', '{{Module}} updated successfully.');
1063
+ }
1064
+
1065
+ public function destroy(string $id): RedirectResponse
1066
+ {
1067
+ $this->service->delete($id);
1068
+ return redirect()->route('{{module}}.index')
1069
+ ->with('success', '{{Module}} deleted successfully.');
1070
+ }
1071
+ }
1072
+ ```
1073
+
1074
+ ## 10. Fragment Controller
1075
+
1076
+ ```php
1077
+ namespace Modules\{{Module}}\Http\Controllers;
1078
+
1079
+ use Illuminate\Http\Request;
1080
+ use Illuminate\Http\Response;
1081
+ use Illuminate\View\View;
1082
+ use Modules\{{Module}}\Contracts\{{Module}}ServiceInterface;
1083
+
1084
+ class {{Module}}FragmentController extends Controller
1085
+ {
1086
+ public function __construct(
1087
+ private readonly {{Module}}ServiceInterface $service,
1088
+ ) {}
1089
+
1090
+ public function row(string $id): View
1091
+ {
1092
+ $item = $this->service->get($id);
1093
+ return view('{{module}}::fragments.{{module}}-row', compact('item'));
1094
+ }
1095
+
1096
+ public function search(Request $request): View
1097
+ {
1098
+ $items = $this->service->search($request->input('q', ''));
1099
+ return view('{{module}}::fragments.search-results', compact('items'));
1100
+ }
1101
+
1102
+ public function destroy(string $id): Response
1103
+ {
1104
+ $this->service->delete($id);
1105
+ return response('', 200);
1106
+ }
1107
+ }
1108
+ ```
1109
+
1110
+ ## 11. View Models
1111
+
1112
+ ```php
1113
+ namespace Modules\{{Module}}\ViewModels;
1114
+
1115
+ use Illuminate\Pagination\LengthAwarePaginator;
1116
+ use Spatie\ViewModels\ViewModel;
1117
+
1118
+ class {{Module}}ListViewModel extends ViewModel
1119
+ {
1120
+ public function __construct(
1121
+ public readonly LengthAwarePaginator $items,
1122
+ ) {}
1123
+ }
1124
+ ```
1125
+
1126
+ ## 12. Blade Templates
1127
+
1128
+ ### pages/index.blade.php
1129
+ ```blade
1130
+ <x-app-layout title="{{Module}} List">
1131
+ <div class="space-y-6">
1132
+ <div class="flex items-center justify-between">
1133
+ <h1 class="text-2xl font-bold text-gray-900 dark:text-white">{{Module}}</h1>
1134
+ <x-button label="Create" href="{{ route('{{module}}.create') }}" tone="primary" />
1135
+ </div>
1136
+
1137
+ <div class="flex gap-4">
1138
+ <input type="search" name="q" placeholder="Search..."
1139
+ hx-get="{{ route('{{module}}.fragments.search') }}"
1140
+ hx-target="#{{module}}-list"
1141
+ hx-trigger="keyup changed delay:300ms"
1142
+ class="block w-full rounded-lg border border-gray-300 px-3 py-2 text-sm" />
1143
+ </div>
1144
+
1145
+ <div id="{{module}}-list">
1146
+ @include('{{module}}::fragments.{{module}}-table', ['items' => $items])
1147
+ </div>
1148
+
1149
+ <x-pagination :paginator="$items" />
1150
+ </div>
1151
+ </x-app-layout>
1152
+ ```
1153
+
1154
+ ### fragments/{{module}}-row.blade.php
1155
+ ```blade
1156
+ <tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"
1157
+ id="{{module}}-row-{{ $item->id }}">
1158
+ <td class="px-6 py-4 text-sm text-gray-900 dark:text-gray-300">{{ $item->field_one }}</td>
1159
+ {{-- Additional fields from mockup... --}}
1160
+ <td class="px-6 py-4 text-sm">
1161
+ <a href="{{ route('{{module}}.show', $item->id) }}"
1162
+ class="text-blue-600 hover:text-blue-800 dark:text-blue-400">View</a>
1163
+ <button hx-delete="{{ route('{{module}}.fragments.destroy', $item->id) }}"
1164
+ hx-target="#{{module}}-row-{{ $item->id }}"
1165
+ hx-swap="outerHTML"
1166
+ hx-confirm="Are you sure?"
1167
+ class="text-red-600 hover:text-red-800 dark:text-red-400 ml-3">Delete</button>
1168
+ </td>
1169
+ </tr>
1170
+ ```
1171
+
1172
+ ## 13. Module Routes
1173
+
1174
+ ```php
1175
+ // Modules/{{Module}}/routes/web.php
1176
+ use Illuminate\Support\Facades\Route;
1177
+ use Modules\{{Module}}\Http\Controllers\{{Module}}PageController;
1178
+ use Modules\{{Module}}\Http\Controllers\{{Module}}FragmentController;
1179
+
1180
+ Route::middleware(['auth', 'role:{{ROLE}}'])->group(function () {
1181
+ Route::resource('{{route-prefix}}', {{Module}}PageController::class);
1182
+
1183
+ Route::prefix('{{route-prefix}}/fragments')->name('{{module}}.fragments.')->group(function () {
1184
+ Route::get('/search', [{{Module}}FragmentController::class, 'search'])->name('search');
1185
+ Route::get('/row/{id}', [{{Module}}FragmentController::class, 'row'])->name('row');
1186
+ Route::delete('/{id}', [{{Module}}FragmentController::class, 'destroy'])->name('destroy');
1187
+ });
1188
+ });
1189
+ ```
1190
+
1191
+ ## 14. Module Service Provider
1192
+
1193
+ ```php
1194
+ namespace Modules\{{Module}}\Providers;
1195
+
1196
+ use Illuminate\Support\ServiceProvider;
1197
+ use Modules\{{Module}}\Contracts\{{Module}}ServiceInterface;
1198
+ use Modules\{{Module}}\Services\{{Module}}Service;
1199
+
1200
+ class {{Module}}ServiceProvider extends ServiceProvider
1201
+ {
1202
+ public function register(): void
1203
+ {
1204
+ $this->app->bind({{Module}}ServiceInterface::class, {{Module}}Service::class);
1205
+ }
1206
+
1207
+ public function boot(): void
1208
+ {
1209
+ $this->loadMigrationsFrom(module_path('{{Module}}', 'database/migrations'));
1210
+ $this->loadViewsFrom(module_path('{{Module}}', 'resources/views'), '{{module}}');
1211
+ $this->loadRoutesFrom(module_path('{{Module}}', 'routes/web.php'));
1212
+ // [If i18n = yes] register this module's translation namespace
1213
+ $this->loadTranslationsFrom(module_path('{{Module}}', 'lang'), '{{module-slug}}');
1214
+ }
1215
+ }
1216
+ ```
1217
+
1218
+ ## 15. [If i18n] Module Translations
1219
+
1220
+ (Only include if i18n = yes. One file per supported locale; keys are referenced from this
1221
+ module's Blade templates via the `{{module-slug}}::` namespace registered in the service
1222
+ provider above.)
1223
+
1224
+ ```php
1225
+ // Modules/{{Module}}/lang/en/messages.php
1226
+ return [
1227
+ 'title' => '{{Module Title}}',
1228
+ 'create' => 'Create {{Module Title}}',
1229
+ 'empty_state' => 'No records yet',
1230
+ ];
1231
+
1232
+ // Modules/{{Module}}/lang/ms/messages.php
1233
+ return [
1234
+ 'title' => '{{Module Title (ms)}}',
1235
+ 'create' => 'Cipta {{Module Title (ms)}}',
1236
+ 'empty_state' => 'Tiada rekod lagi',
1237
+ ];
1238
+ ```
1239
+
1240
+ Blade templates in this module reference keys as `{{ __('{{module-slug}}::messages.title') }}`.
1241
+
1242
+ ## 16. [If Messaging] Messaging Pipeline
1243
+
1244
+ (Only include if the module has async processing NFRs)
1245
+
1246
+ ### Consumer
1247
+ ```php
1248
+ // Queue job consuming from RabbitMQ...
1249
+ ```
1250
+
1251
+ ### Publisher
1252
+ ```php
1253
+ // Publishing domain events via MessagePublisher...
1254
+ ```
1255
+
1256
+ ---
1257
+
1258
+ **IMPORTANT — Separating UI Layer from Messaging/Async Pipeline:**
1259
+
1260
+ When a module has BOTH user-facing screens AND async processing NFRs, the SPEC.md
1261
+ MUST clearly separate these into distinct sections as shown above (Sections 9-12 for
1262
+ UI, Section 16 for messaging). This enables the implementation orchestrator to track
1263
+ and implement each concern independently.
1264
+ ```
1265
+
1266
+ ---
1267
+
1268
+ End of template.