@polymorphism-tech/morph-spec 1.0.4 → 2.1.0

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 (152) hide show
  1. package/CLAUDE.md +1381 -0
  2. package/LICENSE +72 -0
  3. package/README.md +89 -6
  4. package/bin/detect-agents.js +225 -0
  5. package/bin/morph-spec.js +120 -0
  6. package/bin/render-template.js +302 -0
  7. package/bin/semantic-detect-agents.js +246 -0
  8. package/bin/validate-agents-skills.js +239 -0
  9. package/bin/validate-agents.js +69 -0
  10. package/bin/validate-phase.js +263 -0
  11. package/content/.azure/README.md +293 -0
  12. package/content/.azure/docs/azure-devops-setup.md +454 -0
  13. package/content/.azure/docs/branch-strategy.md +398 -0
  14. package/content/.azure/docs/local-development.md +515 -0
  15. package/content/.azure/pipelines/pipeline-variables.yml +34 -0
  16. package/content/.azure/pipelines/prod-pipeline.yml +319 -0
  17. package/content/.azure/pipelines/staging-pipeline.yml +234 -0
  18. package/content/.azure/pipelines/templates/build-dotnet.yml +75 -0
  19. package/content/.azure/pipelines/templates/deploy-app-service.yml +94 -0
  20. package/content/.azure/pipelines/templates/deploy-container-app.yml +120 -0
  21. package/content/.azure/pipelines/templates/infra-deploy.yml +90 -0
  22. package/content/.claude/commands/morph-apply.md +118 -26
  23. package/content/.claude/commands/morph-archive.md +9 -9
  24. package/content/.claude/commands/morph-clarify.md +184 -0
  25. package/content/.claude/commands/morph-design.md +275 -0
  26. package/content/.claude/commands/morph-proposal.md +56 -15
  27. package/content/.claude/commands/morph-setup.md +100 -0
  28. package/content/.claude/commands/morph-status.md +47 -32
  29. package/content/.claude/commands/morph-tasks.md +319 -0
  30. package/content/.claude/commands/morph-uiux.md +211 -0
  31. package/content/.claude/skills/specialists/ai-system-architect.md +604 -0
  32. package/content/.claude/skills/specialists/ms-agent-expert.md +143 -89
  33. package/content/.claude/skills/specialists/ui-ux-designer.md +744 -9
  34. package/content/.claude/skills/stacks/dotnet-blazor.md +244 -8
  35. package/content/.claude/skills/stacks/dotnet-nextjs.md +2 -2
  36. package/content/.morph/.morphversion +5 -0
  37. package/content/.morph/config/agents.json +101 -8
  38. package/content/.morph/config/azure-pricing.json +70 -0
  39. package/content/.morph/config/azure-pricing.schema.json +50 -0
  40. package/content/.morph/config/config.template.json +15 -3
  41. package/content/.morph/docs/STORY-DRIVEN-DEVELOPMENT.md +392 -0
  42. package/content/.morph/hooks/README.md +239 -0
  43. package/content/.morph/hooks/pre-commit-agents.sh +24 -0
  44. package/content/.morph/hooks/pre-commit-all.sh +48 -0
  45. package/content/.morph/hooks/pre-commit-costs.sh +91 -0
  46. package/content/.morph/hooks/pre-commit-specs.sh +49 -0
  47. package/content/.morph/hooks/pre-commit-tests.sh +60 -0
  48. package/content/.morph/project.md +5 -4
  49. package/content/.morph/schemas/agent.schema.json +296 -0
  50. package/content/.morph/standards/agent-framework-setup.md +453 -0
  51. package/content/.morph/standards/architecture.md +142 -7
  52. package/content/.morph/standards/azure.md +218 -23
  53. package/content/.morph/standards/coding.md +47 -12
  54. package/content/.morph/standards/dotnet10-migration.md +494 -0
  55. package/content/.morph/standards/fluent-ui-setup.md +590 -0
  56. package/content/.morph/standards/migration-guide.md +514 -0
  57. package/content/.morph/standards/passkeys-auth.md +423 -0
  58. package/content/.morph/standards/vector-search-rag.md +536 -0
  59. package/content/.morph/state.json +18 -0
  60. package/content/.morph/templates/FluentDesignTheme.cs +149 -0
  61. package/content/.morph/templates/MudTheme.cs +281 -0
  62. package/content/.morph/templates/contracts.cs +55 -55
  63. package/content/.morph/templates/decisions.md +4 -4
  64. package/content/.morph/templates/design-system.css +226 -0
  65. package/content/.morph/templates/infra/.dockerignore.example +89 -0
  66. package/content/.morph/templates/infra/Dockerfile.example +82 -0
  67. package/content/.morph/templates/infra/README.md +286 -0
  68. package/content/.morph/templates/infra/app-service.bicep +164 -0
  69. package/content/.morph/templates/infra/deploy.ps1 +229 -0
  70. package/content/.morph/templates/infra/deploy.sh +208 -0
  71. package/content/.morph/templates/infra/main.bicep +41 -7
  72. package/content/.morph/templates/infra/parameters.dev.json +6 -0
  73. package/content/.morph/templates/infra/parameters.prod.json +6 -0
  74. package/content/.morph/templates/infra/parameters.staging.json +29 -0
  75. package/content/.morph/templates/proposal.md +3 -3
  76. package/content/.morph/templates/recap.md +3 -3
  77. package/content/.morph/templates/spec.md +9 -8
  78. package/content/.morph/templates/sprint-status.yaml +68 -0
  79. package/content/.morph/templates/state.template.json +222 -0
  80. package/content/.morph/templates/story.md +143 -0
  81. package/content/.morph/templates/tasks.md +1 -1
  82. package/content/.morph/templates/ui-components.md +276 -0
  83. package/content/.morph/templates/ui-design-system.md +286 -0
  84. package/content/.morph/templates/ui-flows.md +336 -0
  85. package/content/.morph/templates/ui-mockups.md +133 -0
  86. package/content/.morph/test-infra/example.bicep +59 -0
  87. package/content/CLAUDE.md +124 -0
  88. package/content/README.md +79 -0
  89. package/detectors/config-detector.js +223 -0
  90. package/detectors/conversation-analyzer.js +163 -0
  91. package/detectors/index.js +84 -0
  92. package/detectors/standards-generator.js +275 -0
  93. package/detectors/structure-detector.js +221 -0
  94. package/docs/README.md +149 -0
  95. package/docs/api/cost-calculator.js.html +513 -0
  96. package/docs/api/design-system-generator.js.html +382 -0
  97. package/docs/api/fonts/Montserrat/Montserrat-Bold.eot +0 -0
  98. package/docs/api/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
  99. package/docs/api/fonts/Montserrat/Montserrat-Bold.woff +0 -0
  100. package/docs/api/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
  101. package/docs/api/fonts/Montserrat/Montserrat-Regular.eot +0 -0
  102. package/docs/api/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
  103. package/docs/api/fonts/Montserrat/Montserrat-Regular.woff +0 -0
  104. package/docs/api/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
  105. package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
  106. package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +978 -0
  107. package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
  108. package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
  109. package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
  110. package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
  111. package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +1049 -0
  112. package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
  113. package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
  114. package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
  115. package/docs/api/global.html +5263 -0
  116. package/docs/api/index.html +96 -0
  117. package/docs/api/scripts/collapse.js +39 -0
  118. package/docs/api/scripts/commonNav.js +28 -0
  119. package/docs/api/scripts/linenumber.js +25 -0
  120. package/docs/api/scripts/nav.js +12 -0
  121. package/docs/api/scripts/polyfill.js +4 -0
  122. package/docs/api/scripts/prettify/Apache-License-2.0.txt +202 -0
  123. package/docs/api/scripts/prettify/lang-css.js +2 -0
  124. package/docs/api/scripts/prettify/prettify.js +28 -0
  125. package/docs/api/scripts/search.js +99 -0
  126. package/docs/api/state-manager.js.html +423 -0
  127. package/docs/api/styles/jsdoc.css +776 -0
  128. package/docs/api/styles/prettify.css +80 -0
  129. package/docs/examples.md +328 -0
  130. package/docs/getting-started.md +302 -0
  131. package/docs/installation.md +361 -0
  132. package/docs/templates.md +418 -0
  133. package/docs/validation-checklist.md +266 -0
  134. package/package.json +39 -12
  135. package/src/commands/cost.js +181 -0
  136. package/src/commands/create-story.js +283 -0
  137. package/src/commands/detect.js +104 -0
  138. package/src/commands/doctor.js +67 -0
  139. package/src/commands/generate.js +149 -0
  140. package/src/commands/init.js +69 -45
  141. package/src/commands/shard-spec.js +224 -0
  142. package/src/commands/sprint-status.js +250 -0
  143. package/src/commands/state.js +333 -0
  144. package/src/commands/sync.js +167 -0
  145. package/src/commands/update-pricing.js +206 -0
  146. package/src/commands/update.js +88 -13
  147. package/src/lib/complexity-analyzer.js +292 -0
  148. package/src/lib/cost-calculator.js +429 -0
  149. package/src/lib/design-system-generator.js +298 -0
  150. package/src/lib/state-manager.js +340 -0
  151. package/src/utils/file-copier.js +59 -0
  152. package/src/utils/version-checker.js +175 -0
@@ -12,17 +12,81 @@
12
12
 
13
13
  ---
14
14
 
15
+ ## 🌐 Hosting: App Service vs Container Apps
16
+
17
+ ### Matriz de Decisão
18
+
19
+ | Critério | App Service (Free F1) | Container Apps (Consumption) |
20
+ |----------|----------------------|------------------------------|
21
+ | **Custo** | ✅ $0/mês | ⚠️ ~$5/mês |
22
+ | **RAM** | 1GB | Configurável (0.5Gi min) |
23
+ | **Storage** | 1GB | Ephemeral |
24
+ | **CPU** | ⚠️ 60 min/dia | ✅ Ilimitado |
25
+ | **Disponibilidade** | ⚠️ Sleep após 20min | ✅ Scale-to-zero sem sleep |
26
+ | **SSL Customizado** | ❌ Não (apenas *.azurewebsites.net) | ✅ Sim, gratuito |
27
+ | **Scale Out** | ❌ Não | ✅ Auto-scaling |
28
+ | **Blazor Server** | ✅ Suporte nativo | ✅ Via Docker |
29
+ | **Deploy** | ✅ Direto (ZIP, Git) | ⚠️ Requer container |
30
+
31
+ ### Quando Usar App Service Free
32
+
33
+ **✅ Cenários Ideais:**
34
+ - Protótipos e MVPs de baixo tráfego
35
+ - Aplicações de uso interno (horário comercial)
36
+ - Demos e POCs
37
+ - Apps que toleram cold start (20 min sleep)
38
+ - Orçamento zero absoluto
39
+
40
+ **❌ Não Usar Quando:**
41
+ - Necessita estar sempre disponível (24/7)
42
+ - Tráfego imprevisível ou spikes
43
+ - Mais de 60 min de CPU/dia
44
+ - Precisa de SSL customizado
45
+ - Requer auto-scaling
46
+
47
+ ### Quando Usar Container Apps
48
+
49
+ **✅ Cenários Ideais:**
50
+ - Produção com disponibilidade 24/7
51
+ - Auto-scaling baseado em demanda
52
+ - SSL customizado necessário
53
+ - Arquitetura microserviços
54
+ - Background jobs com Hangfire (minReplicas: 1)
55
+ - Apps que precisam estar sempre "quentes"
56
+
57
+ **❌ Não Usar Quando:**
58
+ - Orçamento zero obrigatório
59
+ - Tráfego extremamente baixo (< 100 req/dia)
60
+ - MVP simples sem requisitos de SLA
61
+
62
+ ### Estratégia Híbrida
63
+
64
+ ```
65
+ Dev/Staging: App Service Free F1
66
+ Production: Container Apps Consumption
67
+ ```
68
+
69
+ **Benefícios:**
70
+ - 💰 Economia em ambientes não críticos
71
+ - 🚀 Performance garantida em produção
72
+ - 🔄 Fácil migração (mesma stack .NET)
73
+
74
+ ---
75
+
15
76
  ## 📋 Stack Padrão Aprovado
16
77
 
17
78
  | Recurso | Tier | Custo | Quando Usar |
18
79
  |---------|------|-------|-------------|
19
- | **Container Apps** | Consumption | ~$0-5/mês | Hosting principal |
80
+ | **App Service** | Free F1 | $0 | MVP, protótipos, dev/staging |
81
+ | **Container Apps** | Consumption | ~$0-5/mês | Produção, auto-scaling |
20
82
  | **Azure SQL** | Free 32GB | $0 | Database |
21
- | **ACR** | Basic | ~$5/mês | Container registry |
83
+ | **ACR** | Basic | ~$5/mês | Container registry (apenas com CA) |
22
84
  | **App Insights** | Free 5GB | $0 | Logs e métricas |
23
85
  | **Azure OpenAI** | gpt-4o-mini | ~$2-10/mês | Análises AI |
24
86
 
25
- **Custo total típico: $7-20/mês**
87
+ **Custo total típico:**
88
+ - **App Service Stack:** $0-2/mês (sem ACR)
89
+ - **Container Apps Stack:** $7-20/mês (com ACR)
26
90
 
27
91
  ### ⚠️ Requer Aprovação
28
92
 
@@ -35,6 +99,66 @@
35
99
 
36
100
  ---
37
101
 
102
+ ## 🌐 App Service Free Tier
103
+
104
+ ### Configuração Obrigatória
105
+ ```bicep
106
+ resource appService 'Microsoft.Web/sites@2022-03-01' = {
107
+ name: 'app-${projectName}-${environment}'
108
+ location: location
109
+ properties: {
110
+ serverFarmId: appServicePlan.id
111
+ httpsOnly: true
112
+ siteConfig: {
113
+ netFrameworkVersion: 'v8.0'
114
+ alwaysOn: false # ⚠️ OBRIGATÓRIO no Free tier
115
+ minTlsVersion: '1.2'
116
+ }
117
+ }
118
+ }
119
+
120
+ resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = {
121
+ name: 'plan-${projectName}-${environment}'
122
+ location: location
123
+ sku: {
124
+ name: 'F1' # Free tier
125
+ tier: 'Free'
126
+ }
127
+ }
128
+ ```
129
+
130
+ ### Limitações Importantes
131
+ - ⚠️ **CPU**: Apenas 60 minutos/dia (não contínuos)
132
+ - ⚠️ **Sleep**: App dorme após 20 minutos de inatividade
133
+ - ⚠️ **SSL**: Apenas `*.azurewebsites.net` (sem domínio customizado)
134
+ - ⚠️ **Escala**: Sem scale-out (apenas 1 instância)
135
+ - ✅ **Memória**: 1GB RAM
136
+ - ✅ **Storage**: 1GB disco
137
+
138
+ ### Deploy
139
+ ```bash
140
+ # Via Azure CLI
141
+ az webapp up --name app-myproject-dev --runtime "DOTNET:8.0"
142
+
143
+ # Via GitHub Actions
144
+ - task: AzureWebApp@1
145
+ inputs:
146
+ azureSubscription: 'Azure-Connection'
147
+ appName: 'app-myproject-dev'
148
+ package: '$(Build.ArtifactStagingDirectory)/**/*.zip'
149
+ ```
150
+
151
+ ### Quando Migrar para Container Apps
152
+ Se você observar:
153
+ - 🔴 CPU quota esgotada frequentemente
154
+ - 🔴 Cold starts afetando UX
155
+ - 🔴 Necessidade de SSL customizado
156
+ - 🔴 Tráfego crescendo (>1000 req/dia)
157
+
158
+ **→ Considere migrar para Container Apps Consumption**
159
+
160
+ ---
161
+
38
162
  ## 🐳 Container Apps
39
163
 
40
164
  ### Configuração Obrigatória
@@ -120,6 +244,8 @@ Usar apenas para:
120
244
 
121
245
  Exemplos:
122
246
  - rg-myproject-dev # Resource Group
247
+ - app-myproject-dev # App Service
248
+ - plan-myproject-dev # App Service Plan
123
249
  - ca-myproject-dev # Container App
124
250
  - sql-myproject-dev # SQL Server
125
251
  - sqldb-myproject-dev # SQL Database
@@ -136,37 +262,106 @@ Exemplos:
136
262
 
137
263
  ---
138
264
 
139
- ## 🚀 Pipeline YAML
265
+ ## 🚀 Azure DevOps Pipelines
266
+
267
+ ### Estratégia de Ambientes
268
+
269
+ **2 ambientes:**
270
+ - **Staging**: Desenvolvimento + QA (branch: `staging`)
271
+ - Developers rodam projeto LOCAL
272
+ - Acessam recursos REMOTOS staging
273
+ - Deploy automático via pipeline
274
+
275
+ - **Produção**: Ambiente crítico (branch: `main`/`master`)
276
+ - Deploy via pipeline com aprovação manual
277
+ - Always-on, monitoramento 24/7
278
+
279
+ ### Estrutura de Pipelines
280
+
281
+ ```
282
+ .azure/pipelines/
283
+ ├── staging-pipeline.yml # Branch: staging
284
+ ├── prod-pipeline.yml # Branch: main/master
285
+ ├── pipeline-variables.yml # Variáveis compartilhadas
286
+ └── templates/
287
+ ├── build-dotnet.yml
288
+ ├── deploy-container-app.yml
289
+ └── infra-deploy.yml
290
+ ```
291
+
292
+ ### Pipeline Staging
140
293
 
141
294
  ```yaml
295
+ # staging-pipeline.yml
142
296
  trigger:
143
297
  branches:
144
- include: [main, develop]
298
+ include: [staging]
299
+
300
+ variables:
301
+ - template: pipeline-variables.yml
302
+ - name: environment
303
+ value: 'staging'
304
+ - name: hostingType
305
+ value: 'containerapp'
145
306
 
146
307
  stages:
147
308
  - stage: Build
309
+ - stage: DeployInfra
310
+ - stage: BuildContainer
311
+ - stage: DeployApp
148
312
  jobs:
149
- - job: BuildAndTest
150
- steps:
151
- - script: dotnet restore
152
- - script: dotnet build
153
- - script: dotnet test
154
- - task: Docker@2
155
- inputs:
156
- command: buildAndPush
157
-
158
- - stage: DeployDev
159
- condition: eq(variables['Build.SourceBranch'], 'refs/heads/develop')
313
+ - deployment: DeployAppJob
314
+ environment: 'staging' # No approval
315
+ ```
316
+
317
+ ### Pipeline Production
318
+
319
+ ```yaml
320
+ # prod-pipeline.yml
321
+ trigger:
322
+ branches:
323
+ include: [main, master]
324
+
325
+ variables:
326
+ - template: pipeline-variables.yml
327
+ - name: environment
328
+ value: 'prod'
329
+ - name: hostingType
330
+ value: 'containerapp'
331
+
332
+ stages:
333
+ - stage: Build
334
+ - stage: SecurityScan
335
+ - stage: DeployInfra
336
+ - stage: BuildContainer
337
+ - stage: DeployApp
160
338
  jobs:
161
- - deployment: DeployToDev
162
- environment: Development
163
- strategy:
164
- runOnce:
165
- deploy:
166
- steps:
167
- - task: AzureContainerApps@1
339
+ - deployment: DeployAppJob
340
+ environment: 'production' # Approval required
341
+ ```
342
+
343
+ ### Workload Identity (Sem Secrets)
344
+
345
+ Ao invés de Service Principals com secrets, use Workload Identity Federation:
346
+
347
+ ```bash
348
+ # Criar App Registration com Federated Credential
349
+ az ad app create --display-name "myapp-prod-pipeline"
350
+
351
+ # Configurar federated credential
352
+ az ad app federated-credential create \
353
+ --id <APP_ID> \
354
+ --parameters @federated-credential.json
168
355
  ```
169
356
 
357
+ **Vantagens:**
358
+ - ✅ Sem secrets para gerenciar
359
+ - ✅ Rotação automática de tokens
360
+ - ✅ Mais seguro
361
+ - ✅ Auditoria melhorada
362
+
363
+ **Documentação completa:** `.azure/docs/azure-devops-setup.md`
364
+
170
365
  ---
171
366
 
172
367
  ## ✅ Checklist de Deploy
@@ -217,39 +217,74 @@ else
217
217
 
218
218
  ---
219
219
 
220
- ## 🤖 MS Agent Framework
220
+ ## 🤖 AI Agents - Padrão Obrigatório (.NET 10)
221
+
222
+ > **CRÍTICO:** Use **exclusivamente** Microsoft Agent Framework.
223
+ > Semantic Kernel foi descontinuado no MORPH-SPEC.
224
+
225
+ ### Pattern Obrigatório
221
226
 
222
227
  ```csharp
228
+ using Microsoft.Agents.AI;
229
+ using Microsoft.Extensions.AI;
230
+
223
231
  public class ReportAnalyzerAgent(
224
- Kernel kernel,
232
+ IChatClient chatClient,
225
233
  ILogger<ReportAnalyzerAgent> logger) : IReportAnalyzerAgent
226
234
  {
227
235
  public async Task<AnalysisResult> AnalyzeAsync(
228
236
  ReportData reportData,
229
237
  CancellationToken cancellationToken = default)
230
238
  {
231
- var prompt = $"""
232
- Analyze the following report data:
233
- {reportData.ToJson()}
234
-
235
- Provide: summary, top 3 insights, recommendations.
236
- Respond in JSON format.
237
- """;
239
+ var agent = chatClient.CreateAgent(
240
+ instructions: """
241
+ Você é um especialista em análise de relatórios.
242
+ Analise os dados fornecidos e retorne em JSON:
243
+ {
244
+ "summary": "resumo",
245
+ "insights": ["top 3 insights"],
246
+ "recommendations": ["recomendações"]
247
+ }
248
+ """,
249
+ name: "ReportAnalyzer"
250
+ );
251
+
252
+ var prompt = $"Analise este relatório:\n{reportData.ToJson()}";
238
253
 
239
254
  try
240
255
  {
241
- var response = await kernel.InvokePromptAsync(prompt, cancellationToken: cancellationToken);
242
- return ParseResponse(response.ToString());
256
+ var response = await agent.RunAsync(prompt, cancellationToken: cancellationToken);
257
+ return ParseResponse(response.Content);
243
258
  }
244
259
  catch (Exception ex)
245
260
  {
246
- logger.LogError(ex, "Analysis failed");
261
+ logger.LogError(ex, "Analysis failed for report {ReportId}", reportData.Id);
247
262
  throw new ReportAnalysisException("Failed to analyze", ex);
248
263
  }
249
264
  }
250
265
  }
251
266
  ```
252
267
 
268
+ ### Configuração (Program.cs)
269
+
270
+ ```csharp
271
+ // Registrar ChatClient
272
+ builder.Services.AddSingleton<IChatClient>(sp =>
273
+ {
274
+ var config = sp.GetRequiredService<IConfiguration>();
275
+ return new ChatClient(
276
+ model: "gpt-4o-mini",
277
+ credential: new ApiKeyCredential(config["AzureOpenAI:ApiKey"]!),
278
+ endpoint: new Uri(config["AzureOpenAI:Endpoint"]!)
279
+ );
280
+ });
281
+
282
+ // Registrar agent
283
+ builder.Services.AddScoped<IReportAnalyzerAgent, ReportAnalyzerAgent>();
284
+ ```
285
+
286
+ **Referência:** [Agent Framework Setup](./agent-framework-setup.md)
287
+
253
288
  ---
254
289
 
255
290
  ## ⏰ Hangfire Jobs