aw-spec-cli 0.1.0 → 0.1.2

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 (173) hide show
  1. package/assets/.agents/skills/aw-spec-addcomponent/SKILL.md +129 -0
  2. package/assets/.agents/skills/aw-spec-agent-context-update/SKILL.md +31 -0
  3. package/assets/.agents/skills/aw-spec-analyze/SKILL.md +296 -0
  4. package/assets/.agents/skills/aw-spec-checklist/SKILL.md +374 -0
  5. package/assets/.agents/skills/aw-spec-clarify/SKILL.md +320 -0
  6. package/assets/.agents/skills/aw-spec-constitution/SKILL.md +194 -0
  7. package/assets/.agents/skills/aw-spec-fast-implement/SKILL.md +268 -0
  8. package/assets/.agents/skills/aw-spec-foundation/SKILL.md +358 -0
  9. package/assets/.agents/skills/aw-spec-implement/SKILL.md +279 -0
  10. package/assets/.agents/skills/aw-spec-minimal-implementation/SKILL.md +209 -0
  11. package/assets/.agents/skills/aw-spec-plan/SKILL.md +221 -0
  12. package/assets/.agents/skills/aw-spec-select-domain/SKILL.md +73 -0
  13. package/assets/.agents/skills/aw-spec-specify/SKILL.md +382 -0
  14. package/assets/.agents/skills/aw-spec-tasks/SKILL.md +259 -0
  15. package/assets/.agents/skills/aw-spec-taskstoissues/SKILL.md +107 -0
  16. package/assets/.aw-spec/domain.json +3 -0
  17. package/assets/.aw-spec/extensions/.registry +19 -0
  18. package/assets/.aw-spec/extensions/agent-context/README.md +57 -0
  19. package/assets/.aw-spec/extensions/agent-context/agent-context-config.yml +4 -0
  20. package/assets/.aw-spec/extensions/agent-context/commands/aw-spec.agent-context.update.md +26 -0
  21. package/assets/.aw-spec/extensions/agent-context/extension.yml +34 -0
  22. package/assets/.aw-spec/extensions/agent-context/scripts/bash/update-agent-context.sh +201 -0
  23. package/assets/.aw-spec/extensions/agent-context/scripts/powershell/update-agent-context.ps1 +237 -0
  24. package/assets/.aw-spec/extensions.yml +23 -0
  25. package/assets/.aw-spec/feature.json +3 -0
  26. package/assets/.aw-spec/init-options.json +9 -0
  27. package/assets/.aw-spec/integration.json +15 -0
  28. package/assets/.aw-spec/integrations/aw-spec.manifest.json +17 -0
  29. package/assets/.aw-spec/integrations/claude.manifest.json +17 -0
  30. package/assets/.aw-spec/memory/constitution.md +50 -0
  31. package/assets/.aw-spec/scripts/bash/check-prerequisites.sh +189 -0
  32. package/assets/.aw-spec/scripts/bash/common.sh +582 -0
  33. package/assets/.aw-spec/scripts/bash/create-new-feature.sh +299 -0
  34. package/assets/.aw-spec/scripts/bash/setup-plan.sh +84 -0
  35. package/assets/.aw-spec/scripts/bash/setup-tasks.sh +91 -0
  36. package/assets/.aw-spec/templates/checklist-template.md +40 -0
  37. package/assets/.aw-spec/templates/constitution-template.md +57 -0
  38. package/assets/.aw-spec/templates/plan-template.md +113 -0
  39. package/assets/.aw-spec/templates/spec-template.md +131 -0
  40. package/assets/.aw-spec/templates/tasks-template.md +252 -0
  41. package/assets/.aw-spec/workflows/aw-spec/workflow.yml +77 -0
  42. package/assets/.aw-spec/workflows/workflow-registry.json +13 -0
  43. package/assets/.claude/skills/aw-spec-addcomponent/SKILL.md +129 -0
  44. package/assets/.claude/skills/aw-spec-agent-context-update/SKILL.md +31 -0
  45. package/assets/.claude/skills/aw-spec-analyze/SKILL.md +296 -0
  46. package/assets/.claude/skills/aw-spec-checklist/SKILL.md +374 -0
  47. package/assets/.claude/skills/aw-spec-clarify/SKILL.md +320 -0
  48. package/assets/.claude/skills/aw-spec-constitution/SKILL.md +194 -0
  49. package/assets/.claude/skills/aw-spec-fast-implement/SKILL.md +268 -0
  50. package/assets/.claude/skills/aw-spec-foundation/SKILL.md +358 -0
  51. package/assets/.claude/skills/aw-spec-implement/SKILL.md +279 -0
  52. package/assets/.claude/skills/aw-spec-minimal-implementation/SKILL.md +209 -0
  53. package/assets/.claude/skills/aw-spec-plan/SKILL.md +221 -0
  54. package/assets/.claude/skills/aw-spec-select-domain/SKILL.md +73 -0
  55. package/assets/.claude/skills/aw-spec-specify/SKILL.md +382 -0
  56. package/assets/.claude/skills/aw-spec-tasks/SKILL.md +259 -0
  57. package/assets/.claude/skills/aw-spec-taskstoissues/SKILL.md +107 -0
  58. package/assets/.kilocode/rules/specify-rules.md +4 -0
  59. package/assets/.kilocode/workflows/aw-spec-addcomponent.md +129 -0
  60. package/assets/.kilocode/workflows/aw-spec-agent-context.update.md +31 -0
  61. package/assets/.kilocode/workflows/aw-spec-analyze.md +296 -0
  62. package/assets/.kilocode/workflows/aw-spec-checklist.md +374 -0
  63. package/assets/.kilocode/workflows/aw-spec-clarify.md +320 -0
  64. package/assets/.kilocode/workflows/aw-spec-constitution.md +194 -0
  65. package/assets/.kilocode/workflows/aw-spec-fast-implement.md +263 -0
  66. package/assets/.kilocode/workflows/aw-spec-foundation.md +358 -0
  67. package/assets/.kilocode/workflows/aw-spec-implement.md +279 -0
  68. package/assets/.kilocode/workflows/aw-spec-minimal-implementation.md +204 -0
  69. package/assets/.kilocode/workflows/aw-spec-plan.md +221 -0
  70. package/assets/.kilocode/workflows/aw-spec-select-domain.md +73 -0
  71. package/assets/.kilocode/workflows/aw-spec-specify.md +382 -0
  72. package/assets/.kilocode/workflows/aw-spec-tasks.md +259 -0
  73. package/assets/.kilocode/workflows/aw-spec-taskstoissues.md +107 -0
  74. package/assets/docs/README.md +106 -0
  75. package/assets/docs/VerticalSlice/PACKAGING.md +185 -0
  76. package/assets/docs/VerticalSlice/VerticalSlice.pack.txt +2833 -0
  77. package/assets/docs/VerticalSlice/pack-foundation.ps1 +145 -0
  78. package/assets/docs/VerticalSlice/pack-foundation.sh +196 -0
  79. package/assets/docs/VerticalSlice/unpack-foundation.ps1 +126 -0
  80. package/assets/docs/VerticalSlice/unpack-foundation.sh +217 -0
  81. package/assets/docs/architecture/clean-architecture.md +71 -0
  82. package/assets/docs/architecture/cqrs.md +141 -0
  83. package/assets/docs/architecture/eventing.md +83 -0
  84. package/assets/docs/architecture/folder-structure.md +61 -0
  85. package/assets/docs/architecture/modular-monolith.md +47 -0
  86. package/assets/docs/architecture/persistence.md +269 -0
  87. package/assets/docs/architecture/testing.md +55 -0
  88. package/assets/docs/architecture/vertical-slices.md +201 -0
  89. package/assets/docs/command.txt +295 -0
  90. package/assets/docs/components/Email/README.md +112 -0
  91. package/assets/docs/components/Email/integration.md +260 -0
  92. package/assets/docs/components/Jobs/README.md +113 -0
  93. package/assets/docs/components/Jobs/integration.md +245 -0
  94. package/assets/docs/components/QrGenerator/README.md +78 -0
  95. package/assets/docs/components/QrGenerator/integration.md +185 -0
  96. package/assets/docs/components/README.md +57 -0
  97. package/assets/docs/domain/domain-driven-design.md +212 -0
  98. package/assets/docs/domainspecs/healthcare/ai-rules.domain.md +34 -0
  99. package/assets/docs/domainspecs/healthcare/api-versioning.domain.md +17 -0
  100. package/assets/docs/domainspecs/healthcare/auditing.domain.md +29 -0
  101. package/assets/docs/domainspecs/healthcare/authorization.domain.md +26 -0
  102. package/assets/docs/domainspecs/healthcare/clinical-terms.md +20 -0
  103. package/assets/docs/domainspecs/healthcare/coding-standards.domain.md +26 -0
  104. package/assets/docs/domainspecs/healthcare/comandrulemap.md +119 -0
  105. package/assets/docs/domainspecs/healthcare/consent.md +19 -0
  106. package/assets/docs/domainspecs/healthcare/cqrs.domain.md +12 -0
  107. package/assets/docs/domainspecs/healthcare/data-classification.domain.md +28 -0
  108. package/assets/docs/domainspecs/healthcare/dependency-policy.domain.md +11 -0
  109. package/assets/docs/domainspecs/healthcare/dicom.md +15 -0
  110. package/assets/docs/domainspecs/healthcare/domain-driven-design.domain.md +28 -0
  111. package/assets/docs/domainspecs/healthcare/domain-requirements.md +55 -0
  112. package/assets/docs/domainspecs/healthcare/eventing.domain.md +18 -0
  113. package/assets/docs/domainspecs/healthcare/fhir.md +36 -0
  114. package/assets/docs/domainspecs/healthcare/folder-structure.domain.md +23 -0
  115. package/assets/docs/domainspecs/healthcare/hipaa.md +34 -0
  116. package/assets/docs/domainspecs/healthcare/hl7.md +15 -0
  117. package/assets/docs/domainspecs/healthcare/integration-terms.md +21 -0
  118. package/assets/docs/domainspecs/healthcare/persistence.domain.md +20 -0
  119. package/assets/docs/domainspecs/healthcare/terminology.md +30 -0
  120. package/assets/docs/domainspecs/healthcare/testing.domain.md +15 -0
  121. package/assets/docs/domainspecs/healthcare/vertical-slices.domain.md +18 -0
  122. package/assets/docs/domainspecs/healthcare/zero-trust.domain.md +12 -0
  123. package/assets/docs/domainspecs/pharma/ai-rules.domain.md +32 -0
  124. package/assets/docs/domainspecs/pharma/api-versioning.domain.md +20 -0
  125. package/assets/docs/domainspecs/pharma/atc.md +22 -0
  126. package/assets/docs/domainspecs/pharma/auditing.domain.md +26 -0
  127. package/assets/docs/domainspecs/pharma/authorization.domain.md +25 -0
  128. package/assets/docs/domainspecs/pharma/coding-standards.domain.md +28 -0
  129. package/assets/docs/domainspecs/pharma/comandrulemap.md +136 -0
  130. package/assets/docs/domainspecs/pharma/cqrs.domain.md +16 -0
  131. package/assets/docs/domainspecs/pharma/data-classification.domain.md +24 -0
  132. package/assets/docs/domainspecs/pharma/data-provenance.md +21 -0
  133. package/assets/docs/domainspecs/pharma/dependency-policy.domain.md +11 -0
  134. package/assets/docs/domainspecs/pharma/domain-driven-design.domain.md +36 -0
  135. package/assets/docs/domainspecs/pharma/domain-requirements.md +99 -0
  136. package/assets/docs/domainspecs/pharma/eventing.domain.md +18 -0
  137. package/assets/docs/domainspecs/pharma/fhir.md +31 -0
  138. package/assets/docs/domainspecs/pharma/folder-structure.domain.md +24 -0
  139. package/assets/docs/domainspecs/pharma/hl7.md +17 -0
  140. package/assets/docs/domainspecs/pharma/idmp.md +26 -0
  141. package/assets/docs/domainspecs/pharma/integration-terms.md +21 -0
  142. package/assets/docs/domainspecs/pharma/ndc.md +28 -0
  143. package/assets/docs/domainspecs/pharma/persistence.domain.md +23 -0
  144. package/assets/docs/domainspecs/pharma/pharma-terms.md +27 -0
  145. package/assets/docs/domainspecs/pharma/product-lifecycle.domain.md +30 -0
  146. package/assets/docs/domainspecs/pharma/regulatory-compliance.md +19 -0
  147. package/assets/docs/domainspecs/pharma/terminology.md +32 -0
  148. package/assets/docs/domainspecs/pharma/testing.domain.md +19 -0
  149. package/assets/docs/domainspecs/pharma/vertical-slices.domain.md +17 -0
  150. package/assets/docs/domainspecs/pharma/zero-trust.domain.md +12 -0
  151. package/assets/docs/glossary/ai-rules.md +59 -0
  152. package/assets/docs/governance/api-versioning.md +99 -0
  153. package/assets/docs/governance/coding-standards.md +87 -0
  154. package/assets/docs/governance/dependency-policy.md +40 -0
  155. package/assets/docs/interop/ihe-conformance.md +31 -0
  156. package/assets/docs/pharma-constitution.my-ref.md +86 -0
  157. package/assets/docs/requirement-spec.txt +15 -0
  158. package/assets/docs/security/auditing.md +117 -0
  159. package/assets/docs/security/authorization-matrix.md +14 -0
  160. package/assets/docs/security/authorization.md +87 -0
  161. package/assets/docs/security/data-classification.md +38 -0
  162. package/assets/docs/security/zero-trust.md +76 -0
  163. package/assets/docs/technology-constitution.md +172 -0
  164. package/dist/commands/init.d.ts.map +1 -1
  165. package/dist/commands/init.js +3 -2
  166. package/dist/commands/init.js.map +1 -1
  167. package/dist/utils/copy-assets.d.ts +8 -1
  168. package/dist/utils/copy-assets.d.ts.map +1 -1
  169. package/dist/utils/copy-assets.js +1 -0
  170. package/dist/utils/copy-assets.js.map +1 -1
  171. package/dist/utils/paths.js +1 -1
  172. package/dist/utils/paths.js.map +1 -1
  173. package/package.json +1 -1
@@ -0,0 +1,245 @@
1
+ # Jobs Component — Integration Workflow (agent playbook)
2
+
3
+ > Pure technology (domain-agnostic). This file is an **agent runbook**: read it top-to-bottom and
4
+ > execute the workflow in order. It drives an interactive integration of `IJobScheduler` /
5
+ > `IJobProgress` into a project, asking the user which engine + persistence implementation(s) to
6
+ > wire. Read [README.md](./README.md) first for the contract. Replace `<SampleProjectName>` with
7
+ > the project name. Commands: `/aw-spec-implement`.
8
+
9
+ ---
10
+
11
+ ## Agent workflow — execute in order
12
+
13
+ 1. **Load context.** Read [README.md](./README.md) and this file. The functional engine is
14
+ **Quartz** — `AppWeaver.Jobs` + `AppWeaver.Jobs.Quartz` are always installed.
15
+ 2. **Locate target projects** (see [[folder-structure]]): `…Application` (job classes + handlers
16
+ that depend on the abstractions), `…Infrastructure` (registration + DbContext), `…Service`
17
+ (`appsettings.json`).
18
+ 3. **Present the implementation catalog (below) as a selection list and ASK the user which
19
+ implementation(s) to integrate.** Use a multi-select prompt (`AskUserQuestion`,
20
+ `multiSelect: true`). Enforce the grouping rules when reading the answer:
21
+ - **Group A — Scheduler store (pick exactly one; default `Quartz.InMemoryStore`).**
22
+ - **Group B — Status persistence (pick zero or one; recommended `EfCore.JobDatabase`).**
23
+ - A valid selection is one Group A item, optionally plus one Group B item
24
+ (e.g. `Quartz.PersistentStore` + `EfCore.DataStore`).
25
+ 4. **Apply “Common steps” once** (§Common steps) — abstraction + Quartz + base registration.
26
+ 5. **For each selected implementation, apply its block** (§Implementation blocks) — Group A tunes
27
+ `UseQuartzJobScheduler`; Group B chains `.UseEfCoreJobs().UseXxx(...)`.
28
+ 6. **Create the DB schema** if a Group B (or persistent Group A) implementation was selected
29
+ (§Schema).
30
+ 7. **Author job classes + dispatch/poll** in the feature slice (§Usage); register each job `Scoped`.
31
+ 8. **Verify** (§Verification) and report what was added.
32
+
33
+ > **Guardrails.** Do **not** use `AppWeaver.Jobs.InMemory` (work-in-progress, not functional). Always
34
+ > set `options.Provider = "Quartz"`. Long-running jobs must check `IJobProgress.CancellationToken`
35
+ > (cancellation is cooperative). In a Quartz **persistent** store, do not rename a job class/method
36
+ > after jobs are scheduled — names are serialized as strings; version instead (`…JobV2`).
37
+
38
+ ---
39
+
40
+ ## Implementation catalog (selection list)
41
+
42
+ | Key | Group | Implementation | Best for | Extra package (v) |
43
+ |---|---|---|---|---|
44
+ | `Quartz.InMemoryStore` | A · Scheduler store | Quartz with in-memory store | Single instance; schedule may be lost on restart | — (Quartz only) |
45
+ | `Quartz.PersistentStore` | A · Scheduler store | Quartz with persistent store (Postgres/SqlServer/SQLite) | Durable + multi-instance scheduling | — (Quartz only; needs DB + DDL) |
46
+ | `EfCore.JobDatabase` | B · Status persistence | `JobStatus` in a dedicated EF Core DB | Standalone apps; status survives restart | `AppWeaver.Jobs.Persistence.EfCore` (0.1.0) |
47
+ | `EfCore.DataStore` | B · Status persistence | `JobStatus` in the shared AppWeaver DataStore | Apps already using `AppWeaver.DataStore` | `AppWeaver.Jobs.Persistence.EfCore` (0.1.0) |
48
+
49
+ > Recommended production set: **`Quartz.PersistentStore` + `EfCore.JobDatabase`** (or `EfCore.DataStore`
50
+ > when DataStore is present). Quartz store = durable *schedule*; EF Core store = durable *status*;
51
+ > they are independent concerns.
52
+
53
+ ---
54
+
55
+ ## Common steps (apply once, always)
56
+
57
+ ### C1 — Package versions
58
+
59
+ **File:** `Directory.Packages.props`
60
+
61
+ ```xml
62
+ <ItemGroup>
63
+ <!-- AppWeaver — Jobs (engine: Quartz) -->
64
+ <PackageVersion Include="AppWeaver.Jobs" Version="0.1.1" />
65
+ <PackageVersion Include="AppWeaver.Jobs.Quartz" Version="0.1.0" />
66
+ <!-- + AppWeaver.Jobs.Persistence.EfCore 0.1.0 if a Group B item is selected -->
67
+ </ItemGroup>
68
+ ```
69
+
70
+ ### C2 — Project references (no `Version=`)
71
+
72
+ **File:** `src/<SampleProjectName>.Application/<SampleProjectName>.Application.csproj`
73
+ ```xml
74
+ <ItemGroup>
75
+ <PackageReference Include="AppWeaver.Jobs" /> <!-- abstractions: IJob, IJobScheduler, IJobProgress -->
76
+ </ItemGroup>
77
+ ```
78
+
79
+ **File:** `src/<SampleProjectName>.Infrastructure/<SampleProjectName>.Infrastructure.csproj`
80
+ ```xml
81
+ <ItemGroup>
82
+ <PackageReference Include="AppWeaver.Jobs" />
83
+ <PackageReference Include="AppWeaver.Jobs.Quartz" />
84
+ <!-- + AppWeaver.Jobs.Persistence.EfCore if a Group B item is selected -->
85
+ </ItemGroup>
86
+ ```
87
+
88
+ ### C3 — appsettings
89
+
90
+ **File:** `src/<SampleProjectName>.Service/appsettings.json`
91
+ ```jsonc
92
+ { "AppWeaver": { "Jobs": { "Provider": "Quartz", "Quartz": { "MaxConcurrency": 10 } } } }
93
+ ```
94
+
95
+ ### C4 — Registration scaffold
96
+
97
+ **File:** `src/<SampleProjectName>.Infrastructure/Extensions/InfrastructureServiceRegistration.cs`
98
+
99
+ The selected blocks fill in the `UseQuartzJobScheduler` body (Group A) and the persistence chain
100
+ (Group B).
101
+
102
+ ```csharp
103
+ using AppWeaver.Jobs.Extensions;
104
+ using AppWeaver.Jobs.Quartz.Extensions;
105
+ // + using AppWeaver.Jobs.Persistence.EfCore.Extensions; (if Group B selected)
106
+ // + using Microsoft.EntityFrameworkCore; (if Group B selected)
107
+
108
+ services.AddAppWeaverJobs(
109
+ configureBuilder: builder => builder
110
+ .UseQuartzJobScheduler(opts =>
111
+ {
112
+ opts.MaxConcurrency = 10;
113
+ // ⬇ Group A block sets opts.ConfigureQuartz (persistent) or leaves it unset (in-memory)
114
+ })
115
+ // ⬇ Group B block chains .UseEfCoreJobs().UseJobDatabase(...) or .UseAppWeaverDataStore()
116
+ ,
117
+ configureOptions: options => options.Provider = "Quartz");
118
+ ```
119
+
120
+ ---
121
+
122
+ ## Implementation blocks
123
+
124
+ ### A · `Quartz.InMemoryStore`
125
+ Leave `opts.ConfigureQuartz` **unset** — Quartz uses its in-memory store automatically. No DB, no
126
+ DDL. Schedule and pending jobs are lost on restart; single instance only.
127
+
128
+ ### A · `Quartz.PersistentStore`
129
+ Set `ConfigureQuartz` inside `UseQuartzJobScheduler`:
130
+ ```csharp
131
+ opts.ConfigureQuartz = q => q.UsePersistentStore(s =>
132
+ {
133
+ s.UsePostgres(configuration.GetConnectionString("Quartz")!); // or UseSqlServer / UseMicrosoftSQLite
134
+ s.UseSystemTextJsonSerializer();
135
+ });
136
+ ```
137
+ > Apply the official Quartz DDL to that database **before first run** (Postgres/SQL Server scripts —
138
+ > see framework `Jobs/README.md` → *Database Setup*). Do **not** set `opts.ConnectionString` without
139
+ > `ConfigureQuartz` — that combination throws at startup.
140
+
141
+ ### B · `EfCore.JobDatabase` (standalone status DB)
142
+ Add the package (C1/C2) and chain a dedicated `JobStatusDbContext`:
143
+ ```csharp
144
+ .UseEfCoreJobs()
145
+ .UseJobDatabase(opts => opts.UseNpgsql(configuration.GetConnectionString("Jobs"))) // or UseSqlite("Data Source=jobs.db")
146
+ ```
147
+
148
+ ### B · `EfCore.DataStore` (shared AppWeaver DataStore)
149
+ Requires `AddDataStore().UseEfCoreDataStore()` to be registered **first**. Chain:
150
+ ```csharp
151
+ .UseEfCoreJobs()
152
+ .UseAppWeaverDataStore()
153
+ ```
154
+ > `UseEfCoreJobs()` alone does **not** register a data context — it must be followed by exactly one
155
+ > of `UseJobDatabase(...)` or `UseAppWeaverDataStore()`.
156
+
157
+ ---
158
+
159
+ ## Schema (only if a Group B / persistent Group A item was selected)
160
+
161
+ - **`EfCore.JobDatabase`** — scaffold + apply a `JobStatusDbContext` migration (or
162
+ `EnsureCreatedAsync()` for dev):
163
+ ```bash
164
+ dotnet ef migrations add InitialJobs --context JobStatusDbContext \
165
+ --project src/<SampleProjectName>.Infrastructure --startup-project src/<SampleProjectName>.Service
166
+ dotnet ef database update --context JobStatusDbContext --startup-project src/<SampleProjectName>.Service
167
+ ```
168
+ - **`EfCore.DataStore`** — Shared strategy: job tables ride in the `SharedComponentDbContext`
169
+ migration (only `dataStore.MigrateAsync()` at runtime). Isolated strategy: run the
170
+ `JobStatusDbContext` migration too (see [[persistence]]).
171
+ - **`Quartz.PersistentStore`** — apply the Quartz DDL (above).
172
+
173
+ > AI MAY scaffold migrations but MUST NOT run them against shared environments (see [[ai-rules]]).
174
+
175
+ ---
176
+
177
+ ## Usage (job classes + dispatch)
178
+
179
+ ### Author a job — implement the `IJob` marker; inject `IJobProgress` for progress/cancel
180
+ **File:** `src/<SampleProjectName>.Application/Jobs/ExportDataJob.cs`
181
+ ```csharp
182
+ using AppWeaver.Jobs.Abstractions;
183
+
184
+ public class ExportDataJob(IExportService _exports, IJobProgress _progress) : IJob
185
+ {
186
+ public async Task RunAsync(string table, Guid requestId)
187
+ {
188
+ await _progress.ReportProgressAsync(0, "Starting export…");
189
+ var rows = await _exports.FetchAsync(table);
190
+ for (int i = 0; i < rows.Count; i++)
191
+ {
192
+ _progress.CancellationToken.ThrowIfCancellationRequested(); // cooperative cancel
193
+ await _exports.ProcessAsync(rows[i]);
194
+ await _progress.ReportProgressAsync((i + 1) * 100 / rows.Count);
195
+ }
196
+ await _progress.ReportResultUrlAsync(await _exports.UploadAsync(requestId));
197
+ }
198
+ }
199
+ ```
200
+
201
+ ### Register every job class as `Scoped`
202
+ **File:** `…/Extensions/InfrastructureServiceRegistration.cs`
203
+ ```csharp
204
+ services.AddScoped<ExportDataJob>();
205
+ ```
206
+
207
+ ### Dispatch / poll / cancel — inject `IJobScheduler` in a handler or endpoint
208
+ ```csharp
209
+ var jobId = await _scheduler.EnqueueAsync<ExportDataJob>(
210
+ j => j.RunAsync(req.Table, req.RequestId), description: $"Export {req.Table}", userId: currentUserId);
211
+
212
+ await _scheduler.ScheduleAsync<SendReminderJob>(j => j.SendAsync(userId), TimeSpan.FromMinutes(30)); // delayed
213
+ JobStatus? status = await _scheduler.GetStatusAsync(jobId); // null = unknown/expired → 404
214
+ await _scheduler.CancelAsync(jobId);
215
+ ```
216
+
217
+ ### Recurring jobs — register at startup via `IHostedService` (fire at next CRON occurrence)
218
+ ```csharp
219
+ public class JobConfigurator(IJobScheduler _scheduler) : IHostedService
220
+ {
221
+ public Task StartAsync(CancellationToken ct) =>
222
+ _scheduler.AddOrUpdateRecurringAsync<NightlyReportJob>(
223
+ "nightly-report", j => j.GenerateAsync(), "0 2 * * *"); // daily 02:00
224
+ public Task StopAsync(CancellationToken ct) => Task.CompletedTask;
225
+ }
226
+ // services.AddHostedService<JobConfigurator>();
227
+ ```
228
+
229
+ ---
230
+
231
+ ## Verification
232
+
233
+ 1. **Builds** — `dotnet build` succeeds; no `Version=` on references; `AppWeaver.Jobs.InMemory` absent.
234
+ 2. **Startup validates** — app boots; `Provider` is `"Quartz"`; the Quartz hosted service starts.
235
+ 3. **Schema exists** — if a persistence implementation was selected, the `JobStatus` table (and/or
236
+ Quartz tables) are present.
237
+ 4. **Resolves** — `IJobScheduler` resolves; each job class resolves (registered `Scoped`).
238
+ 5. **Round-trip** — enqueue → poll `GetStatusAsync` → reaches `Completed` with expected
239
+ `ProgressPercent`/`ResultUrl`; status is readable from the persistence store.
240
+ 6. **Cancel** — a cancellable job moves to `Cancelled` after `CancelAsync`.
241
+
242
+ ## Related
243
+
244
+ - [README.md](./README.md) · framework `Jobs/README.md` (deep-dive) · [[components-readme]] ·
245
+ [[cqrs]] · [[persistence]] · [[vertical-slices]] · [[dependency-policy]] · [[ai-rules]]
@@ -0,0 +1,78 @@
1
+ # QrGenerator Component (`AppWeaver.QrGenerator`)
2
+
3
+ > Pure technology (domain-agnostic). Reference sheet for the QR-code generation component.
4
+ > To wire it into a project, follow the agent runbook in [integration.md](./integration.md).
5
+ > Commands: `/aw-spec-plan`, `/aw-spec-tasks`, `/aw-spec-implement`.
6
+
7
+ ## What it is
8
+
9
+ A small provider-agnostic component that turns a string into a QR-code **PNG byte array**.
10
+ Application code depends on the `IQRGenerator` abstraction; the implementation
11
+ (`QRCoder` library) is selected once during DI registration.
12
+
13
+ **Use when:** a feature must produce a scannable QR code — patient/asset cards, share links,
14
+ check-in tokens, downloadable QR images.
15
+
16
+ ## Public contract (`AppWeaver.QrGenerator`)
17
+
18
+ ```csharp
19
+ namespace AppWeaver.QRGenerator; // note: namespace is "QRGenerator" (capital R)
20
+
21
+ public interface IQRGenerator
22
+ {
23
+ /// <summary>Encodes the content and returns a PNG image as a byte[].</summary>
24
+ byte[] GenerateQR(string QRContent);
25
+ }
26
+ ```
27
+
28
+ Implementation detail (`QRCoderService`): ECC level **Q**, **20px** per module, output **PNG**.
29
+ `null` content throws `ArgumentNullException`; an **empty string is allowed** (returns a valid
30
+ PNG).
31
+
32
+ ## Packages
33
+
34
+ | Package | Version | Role | Builder method | Provider key |
35
+ |---|---|---|---|---|
36
+ | `AppWeaver.QrGenerator` | `0.1.2` | **Required.** Abstraction + `AddQRGenerator` + facade. | — | — |
37
+ | `AppWeaver.QrGenerator.QRCoder` | `0.1.2` | QRCoder-backed implementation. | `UseQRCoder` | `QRCoder` |
38
+
39
+ > **Casing trap:** the **package IDs** use `QrGenerator` (lower `r`) but the **namespaces/types**
40
+ > use `QRGenerator` (upper `R`). `using AppWeaver.QRGenerator;` is correct.
41
+
42
+ ## Registration entry point
43
+
44
+ ```csharp
45
+ // In AppWeaver.QRGenerator.Extensions
46
+ IComponentBuilder<IQRGenerator> AddQRGenerator(
47
+ this IServiceCollection services,
48
+ Action<IComponentBuilder<IQRGenerator>> configureBuilder, // required
49
+ Action<QRConfiguration>? configureOptions = null, // set Provider here
50
+ IConfiguration? configuration = null); // OR bind from config
51
+
52
+ // In AppWeaver.QRGenerator.QRCoder.Extensions
53
+ IComponentBuilder<IQRGenerator> UseQRCoder(
54
+ this IComponentBuilder<IQRGenerator> builder,
55
+ Action<QRGeneratorOptions>? options = null, // QRGeneratorOptions is currently empty
56
+ IConfiguration? configuration = null); // binds section "QRGenerator.QRCoder"
57
+ ```
58
+
59
+ `QRConfiguration` exposes a single `[Required] string Provider`. The facade resolves the keyed
60
+ provider whose key equals `Provider`.
61
+
62
+ ## Source map (framework)
63
+
64
+ ```
65
+ framework/QrGenerator/
66
+ Integration.md ← original endpoint tutorial (reference)
67
+ README.md ← component overview
68
+ src/
69
+ AppWeaver.QrGenerator/ ← IQRGenerator, AddQRGenerator, DefaultImplementation
70
+ AppWeaver.QrGenerator.QRCoder/ ← UseQRCoder + QRCoderService
71
+ test/
72
+ QrGenerator.QrCoder.Tests/ ← QRCoderServiceTests (xUnit)
73
+ ```
74
+
75
+ ## Related
76
+
77
+ - [integration.md](./integration.md) · [[components-readme]] · [[cqrs]] ·
78
+ [[vertical-slices]] · [[dependency-policy]]
@@ -0,0 +1,185 @@
1
+ # QrGenerator Component — Integration Workflow (agent playbook)
2
+
3
+ > Pure technology (domain-agnostic). This file is an **agent runbook**: read it top-to-bottom and
4
+ > execute the workflow in order. It drives an interactive integration of `IQRGenerator` into a
5
+ > project. Read [README.md](./README.md) first for the contract. Replace `<SampleProjectName>`
6
+ > with the project name. Commands: `/aw-spec-implement`.
7
+
8
+ ---
9
+
10
+ ## Agent workflow — execute in order
11
+
12
+ 1. **Load context.** Read [README.md](./README.md) and this file.
13
+ 2. **Locate target projects** (see [[folder-structure]]): `…Application` (injects `IQRGenerator`),
14
+ `…Infrastructure` (does the registration), `…Service` (API endpoints).
15
+ 3. **Present the implementation catalog (below) as a selection list and ASK the user which
16
+ implementation(s) to integrate.** Use a multi-select prompt (`AskUserQuestion`,
17
+ `multiSelect: true`) for consistency with the other components.
18
+ - This component currently ships **one** implementation (`QRCoder`). If the catalog has a single
19
+ row, tell the user it is the only option and proceed once they confirm (do not invent others).
20
+ 4. **Apply “Common steps”** (§Common steps).
21
+ 5. **For each selected implementation, apply its block** (§Implementation blocks).
22
+ 6. **Set the provider:** `configureOptions: options => options.Provider = "<selected key>"`.
23
+ 7. **(Optional) Build the feature slice** (§Usage) — DTO + Command + Handler + Endpoint.
24
+ 8. **Verify** (§Verification) and report what was added.
25
+
26
+ > **Guardrail — namespace casing.** Package IDs use `QrGenerator` (lower `r`); **namespaces/types
27
+ > use `QRGenerator` (upper `R`)**. `using AppWeaver.QRGenerator;` is correct — getting this wrong is
28
+ > the most common build break.
29
+
30
+ ---
31
+
32
+ ## Implementation catalog (selection list)
33
+
34
+ | Key | Implementation | Best for | Package (v) | Settings |
35
+ |---|---|---|---|---|
36
+ | `QRCoder` | QRCoder library (PNG, ECC level Q, 20px/module) | All QR-code generation | `AppWeaver.QrGenerator.QRCoder` (0.1.2) | none (`QRGeneratorOptions` is empty) |
37
+
38
+ ---
39
+
40
+ ## Common steps (apply once)
41
+
42
+ ### C1 — Abstraction package version
43
+
44
+ **File:** `Directory.Packages.props`
45
+
46
+ ```xml
47
+ <ItemGroup>
48
+ <!-- AppWeaver — QrGenerator (abstraction) -->
49
+ <PackageVersion Include="AppWeaver.QrGenerator" Version="0.1.2" />
50
+ <!-- + one <PackageVersion> per selected implementation — see block below -->
51
+ </ItemGroup>
52
+ ```
53
+
54
+ ### C2 — Project references (no `Version=`)
55
+
56
+ **File:** `src/<SampleProjectName>.Application/<SampleProjectName>.Application.csproj`
57
+ **File:** `src/<SampleProjectName>.Infrastructure/<SampleProjectName>.Infrastructure.csproj`
58
+
59
+ ```xml
60
+ <ItemGroup>
61
+ <PackageReference Include="AppWeaver.QrGenerator" />
62
+ <!-- + each selected implementation’s package — see block below -->
63
+ </ItemGroup>
64
+ ```
65
+
66
+ ### C3 — Registration scaffold
67
+
68
+ **File:** `src/<SampleProjectName>.Infrastructure/Extensions/InfrastructureServiceRegistration.cs`
69
+
70
+ ```csharp
71
+ using AppWeaver.QRGenerator.Extensions; // AddQRGenerator (capital R)
72
+
73
+ services.AddQRGenerator(
74
+ configureBuilder: builder =>
75
+ {
76
+ // ⬇ one Use…() call per selected implementation
77
+ },
78
+ configureOptions: options => options.Provider = "<selected key>"); // step 6
79
+ ```
80
+
81
+ No `appsettings.json` entry is required — there is nothing to configure.
82
+
83
+ ---
84
+
85
+ ## Implementation blocks
86
+
87
+ ### QRCoder
88
+ ```xml
89
+ <!-- Directory.Packages.props --> <PackageVersion Include="AppWeaver.QrGenerator.QRCoder" Version="0.1.2" />
90
+ <!-- *.csproj --> <PackageReference Include="AppWeaver.QrGenerator.QRCoder" />
91
+ ```
92
+ ```csharp
93
+ using AppWeaver.QRGenerator.QRCoder.Extensions; // UseQRCoder
94
+ // inside AddQRGenerator builder:
95
+ builder.UseQRCoder();
96
+ // → set options.Provider = "QRCoder" in step 6
97
+ ```
98
+
99
+ ---
100
+
101
+ ## Usage (feature slice — DTO + Command + Handler + Endpoint)
102
+
103
+ Generate inside an Application command handler; inject **`IQRGenerator`**. Return the image as
104
+ base64 in a DTO so the API layer owns the response shape (see [[cqrs]], [[vertical-slices]]).
105
+
106
+ **DTO** — `src/<SampleProjectName>.Application/Features/Qr/Dtos/QrCodeDto.cs`
107
+ ```csharp
108
+ public record QrCodeDto
109
+ {
110
+ public string Base64Image { get; init; } = string.Empty;
111
+ public string ContentType { get; init; } = "image/png";
112
+ }
113
+ ```
114
+
115
+ **Command + Handler** — `…/Features/Qr/Commands/GenerateQrCommand.cs`
116
+ ```csharp
117
+ public record GenerateQrCommand(string Content) : ICommand<Result<QrCodeDto>>;
118
+
119
+ public class GenerateQrCommandHandler(IQRGenerator _qr) // using AppWeaver.QRGenerator;
120
+ : ICommandHandler<GenerateQrCommand, Result<QrCodeDto>>
121
+ {
122
+ public ValueTask<Result<QrCodeDto>> Handle(GenerateQrCommand req, CancellationToken ct)
123
+ {
124
+ if (string.IsNullOrWhiteSpace(req.Content)) // GenerateQR(null) throws; guard first
125
+ return ValueTask.FromResult(
126
+ Result<QrCodeDto>.Failure(new Error("qr.empty_content", "QR content is required.", 400)));
127
+
128
+ byte[] png = _qr.GenerateQR(req.Content);
129
+ return ValueTask.FromResult(Result<QrCodeDto>.Ok(new QrCodeDto
130
+ {
131
+ Base64Image = Convert.ToBase64String(png),
132
+ ContentType = "image/png"
133
+ }));
134
+ }
135
+ }
136
+ ```
137
+
138
+ **Endpoint** — `src/<SampleProjectName>.Service/Qr/Features/GenerateQr/GenerateQrEndpoint.cs`
139
+ ```csharp
140
+ public class GenerateQrEndpoint(IMediator _mediator) : Endpoint<GenerateQrRequest>
141
+ {
142
+ public override void Configure()
143
+ {
144
+ Post("/qr-codes");
145
+ Summary(s => s.Summary = "Generate a QR-code PNG from text content");
146
+ Options(o => o.WithTags("04 - QR - (FastEndpoints)"));
147
+ }
148
+
149
+ public override async Task HandleAsync(GenerateQrRequest req, CancellationToken ct)
150
+ {
151
+ var result = await _mediator.Send(new GenerateQrCommand(req.Content), ct);
152
+ if (result.IsSuccess && result.Value is not null)
153
+ {
154
+ var bytes = Convert.FromBase64String(result.Value.Base64Image);
155
+ HttpContext.Response.ContentType = result.Value.ContentType; // image/png
156
+ HttpContext.Response.Headers["Content-Disposition"] = "attachment; filename=qr-code.png";
157
+ await HttpContext.Response.Body.WriteAsync(bytes, ct);
158
+ }
159
+ else
160
+ {
161
+ HttpContext.Response.StatusCode = 400;
162
+ await HttpContext.Response.WriteAsJsonAsync(new { error = "Failed to generate QR code" }, ct);
163
+ }
164
+ }
165
+ }
166
+
167
+ public class GenerateQrRequest { public string Content { get; set; } = string.Empty; }
168
+ ```
169
+
170
+ ---
171
+
172
+ ## Verification
173
+
174
+ 1. **Builds** — `dotnet build` succeeds; `using` directives use `AppWeaver.QRGenerator…`; no
175
+ `Version=` on package references.
176
+ 2. **Startup validates** — app boots; `options.Provider` is `"QRCoder"`.
177
+ 3. **Resolves** — `IQRGenerator` resolves from DI.
178
+ 4. **Generates** — `GenerateQR("test")` returns a non-empty `byte[]`; `POST /qr-codes` returns an
179
+ `image/png` body that opens as a scannable QR image. (`null` content throws; empty string is
180
+ valid and returns a PNG.)
181
+
182
+ ## Related
183
+
184
+ - [README.md](./README.md) · [[components-readme]] · [[cqrs]] · [[vertical-slices]] ·
185
+ [[clean-architecture]] · [[dependency-policy]]
@@ -0,0 +1,57 @@
1
+ # Framework Components — Integration Catalog
2
+
3
+ > Pure technology (domain-agnostic). These docs are the authoritative integration contracts for
4
+ > opt-in AppWeaver framework components. An AI coding agent (or a human) MUST read the relevant
5
+ > component folder **before** wiring the component into a project.
6
+ > Commands: `/aw-spec-plan`, `/aw-spec-tasks`, `/aw-spec-implement`.
7
+
8
+ Each component is an optional NuGet-distributed building block that plugs into the standard
9
+ AppWeaver layout (see [[folder-structure]]) via the same **provider/registry** registration
10
+ pattern: `AddXxx(builder => builder.UseProvider(...), options => options.Provider = "Key")`.
11
+ Your application code depends only on the component's **abstraction interface** — the concrete
12
+ provider is a single registration decision and is swappable with zero changes to feature code.
13
+
14
+ ## Available components
15
+
16
+ | Component | Abstraction | Folder | Use when… |
17
+ |---|---|---|---|
18
+ | **Email** | `IEmailService` | [Email](./Email/README.md) | Sending transactional/notification email (MsGraph, SMTP, SendGrid, OAuth2, File). |
19
+ | **QrGenerator** | `IQRGenerator` | [QrGenerator](./QrGenerator/README.md) | Generating QR-code PNG images from text. |
20
+ | **Jobs** | `IJobScheduler` | [Jobs](./Jobs/README.md) | Background jobs — fire-and-forget, delayed, recurring, with progress + status polling. |
21
+
22
+ ## How each component folder is organized
23
+
24
+ Every folder contains two independent, agent-readable files:
25
+
26
+ | File | Purpose | Read it to… |
27
+ |---|---|---|
28
+ | `README.md` | Reference — what the component is, its contract, packages, implementations, file map. | Understand the API surface and the available implementations. |
29
+ | `integration.md` | **Agent runbook** — an ordered workflow that asks the user which implementation(s) to use, then adds the matching code. | Drive the integration interactively, end to end. |
30
+
31
+ ## Integration workflow (every component's `integration.md` follows this shape)
32
+
33
+ 1. The agent reads `README.md` + `integration.md` and locates the target projects.
34
+ 2. The agent presents the component's **implementation catalog** as a selection list and **asks the
35
+ user** which implementation(s) to wire — via a multi-select prompt (`AskUserQuestion`,
36
+ `multiSelect: true`). The user may pick more than one (e.g. a production transport + a local-dev one).
37
+ 3. The agent applies the **common steps** once, then the **per-implementation block** for each
38
+ selected implementation, and sets the default provider.
39
+ 4. The agent runs the **verification** checklist and reports what was added.
40
+
41
+ ## Cross-cutting rules (apply to every component)
42
+
43
+ - **R0.1** — NuGet versions live in `Directory.Packages.props` only (`<PackageVersion>`); project
44
+ files reference the package **without** a `Version=` attribute (see [[dependency-policy]]).
45
+ - **R0.2** — Register the component in the **Infrastructure** registration extension, never in a
46
+ feature slice or domain code (see [[clean-architecture]]).
47
+ - **R0.3** — Inject the **abstraction** (`IEmailService`, `IQRGenerator`, `IJobScheduler`) into
48
+ handlers — never the concrete provider type (see [[cqrs]]).
49
+ - **R0.4** — Always set `options.Provider = "<Key>"` explicitly. Do not rely on the implicit
50
+ "first registered" fallback — it is order-dependent and, for Email, hardcoded.
51
+ - **R0.5** — Secrets (client secrets, API keys, SMTP passwords, connection strings) MUST NOT be
52
+ committed. Use user-secrets / environment variables / key vault (see [[ai-rules]]).
53
+
54
+ ## Related
55
+
56
+ - [[folder-structure]] · [[clean-architecture]] · [[cqrs]] · [[vertical-slices]] ·
57
+ [[dependency-policy]] · [[persistence]]