@polymorphism-tech/morph-spec 3.0.0 → 3.0.1

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 (160) hide show
  1. package/CLAUDE.md +75 -371
  2. package/LICENSE +72 -72
  3. package/bin/detect-agents.js +225 -225
  4. package/bin/render-template.js +302 -302
  5. package/bin/semantic-detect-agents.js +246 -246
  6. package/bin/validate-agents-skills.js +251 -251
  7. package/bin/validate-agents.js +69 -69
  8. package/bin/validate-phase.js +263 -263
  9. package/content/.azure/README.md +293 -293
  10. package/content/.azure/docs/azure-devops-setup.md +454 -454
  11. package/content/.azure/docs/branch-strategy.md +398 -398
  12. package/content/.azure/docs/local-development.md +515 -515
  13. package/content/.azure/pipelines/pipeline-variables.yml +34 -34
  14. package/content/.azure/pipelines/prod-pipeline.yml +319 -319
  15. package/content/.azure/pipelines/staging-pipeline.yml +234 -234
  16. package/content/.azure/pipelines/templates/build-dotnet.yml +75 -75
  17. package/content/.azure/pipelines/templates/deploy-app-service.yml +94 -94
  18. package/content/.azure/pipelines/templates/deploy-container-app.yml +120 -120
  19. package/content/.azure/pipelines/templates/infra-deploy.yml +90 -90
  20. package/content/.claude/commands/morph-archive.md +79 -79
  21. package/content/.claude/commands/morph-deploy.md +529 -529
  22. package/content/.claude/commands/morph-infra.md +209 -209
  23. package/content/.claude/commands/morph-preflight.md +227 -227
  24. package/content/.claude/commands/morph-troubleshoot.md +122 -122
  25. package/content/.claude/settings.local.json +15 -15
  26. package/content/.claude/skills/{specialists → level-2-domains/architecture}/prompt-engineer.md +189 -189
  27. package/content/.claude/skills/{specialists → level-2-domains/architecture}/seo-growth-hacker.md +320 -320
  28. package/content/.claude/skills/{infra → level-2-domains/infrastructure}/azure-deploy-specialist.md +699 -699
  29. package/content/.morph/.morphversion +5 -5
  30. package/content/.morph/archive/.gitkeep +25 -25
  31. package/content/.morph/config/agents.json +7 -5
  32. package/content/.morph/docs/STORY-DRIVEN-DEVELOPMENT.md +392 -392
  33. package/content/.morph/examples/api-nextjs/README.md +241 -241
  34. package/content/.morph/examples/api-nextjs/contracts.ts +307 -307
  35. package/content/.morph/examples/api-nextjs/spec.md +399 -399
  36. package/content/.morph/examples/api-nextjs/tasks.md +168 -168
  37. package/content/.morph/examples/micro-saas/README.md +125 -125
  38. package/content/.morph/examples/micro-saas/contracts.cs +358 -358
  39. package/content/.morph/examples/micro-saas/decisions.md +246 -246
  40. package/content/.morph/examples/micro-saas/spec.md +236 -236
  41. package/content/.morph/examples/micro-saas/tasks.md +150 -150
  42. package/content/.morph/examples/multi-agent/README.md +309 -309
  43. package/content/.morph/examples/multi-agent/contracts.cs +433 -433
  44. package/content/.morph/examples/multi-agent/spec.md +479 -479
  45. package/content/.morph/examples/multi-agent/tasks.md +185 -185
  46. package/content/.morph/examples/state-v3.json +188 -188
  47. package/content/.morph/features/.gitkeep +25 -25
  48. package/content/.morph/hooks/pre-commit-all.sh +48 -48
  49. package/content/.morph/hooks/pre-commit-specs.sh +49 -49
  50. package/content/.morph/hooks/pre-commit-tests.sh +60 -60
  51. package/content/.morph/project.md +160 -160
  52. package/content/.morph/schemas/agent.schema.json +296 -296
  53. package/content/.morph/specs/.gitkeep +20 -20
  54. package/content/.morph/standards/coding.md +377 -377
  55. package/content/.morph/standards/fluent-ui-setup.md +590 -590
  56. package/content/.morph/standards/migration-guide.md +514 -514
  57. package/content/.morph/standards/passkeys-auth.md +423 -423
  58. package/content/.morph/standards/vector-search-rag.md +536 -536
  59. package/content/.morph/state.json +17 -17
  60. package/content/.morph/templates/FluentDesignTheme.cs +149 -149
  61. package/content/.morph/templates/MudTheme.cs +281 -281
  62. package/content/.morph/templates/component.razor +239 -239
  63. package/content/.morph/templates/contracts.cs +217 -217
  64. package/content/.morph/templates/design-system.css +226 -226
  65. package/content/.morph/templates/infra/.dockerignore.example +89 -89
  66. package/content/.morph/templates/infra/Dockerfile.example +82 -82
  67. package/content/.morph/templates/infra/README.md +286 -286
  68. package/content/.morph/templates/infra/app-insights.bicep +63 -63
  69. package/content/.morph/templates/infra/app-service.bicep +164 -164
  70. package/content/.morph/templates/infra/azure-pipelines-deploy.yml +480 -480
  71. package/content/.morph/templates/infra/container-app-env.bicep +49 -49
  72. package/content/.morph/templates/infra/container-app.bicep +156 -156
  73. package/content/.morph/templates/infra/deploy-checklist.md +426 -426
  74. package/content/.morph/templates/infra/deploy.ps1 +229 -229
  75. package/content/.morph/templates/infra/deploy.sh +208 -208
  76. package/content/.morph/templates/infra/key-vault.bicep +91 -91
  77. package/content/.morph/templates/infra/main.bicep +189 -189
  78. package/content/.morph/templates/infra/parameters.dev.json +29 -29
  79. package/content/.morph/templates/infra/parameters.prod.json +29 -29
  80. package/content/.morph/templates/infra/parameters.staging.json +29 -29
  81. package/content/.morph/templates/infra/sql-database.bicep +103 -103
  82. package/content/.morph/templates/infra/storage.bicep +106 -106
  83. package/content/.morph/templates/integrations/asaas-client.cs +387 -387
  84. package/content/.morph/templates/integrations/asaas-webhook.cs +351 -351
  85. package/content/.morph/templates/integrations/azure-identity-config.cs +288 -288
  86. package/content/.morph/templates/integrations/clerk-config.cs +258 -258
  87. package/content/.morph/templates/job.cs +171 -171
  88. package/content/.morph/templates/migration.cs +83 -83
  89. package/content/.morph/templates/repository.cs +141 -141
  90. package/content/.morph/templates/saas/subscription.cs +347 -347
  91. package/content/.morph/templates/saas/tenant.cs +338 -338
  92. package/content/.morph/templates/service.cs +139 -139
  93. package/content/.morph/templates/sprint-status.yaml +68 -68
  94. package/content/.morph/templates/story.md +143 -143
  95. package/content/.morph/templates/test.cs +239 -239
  96. package/content/.morph/templates/ui-design-system.md +286 -286
  97. package/content/.morph/templates/ui-flows.md +336 -336
  98. package/content/.morph/templates/ui-mockups.md +133 -133
  99. package/content/.morph/test-infra/example.bicep +59 -59
  100. package/content/README.md +79 -79
  101. package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +977 -977
  102. package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +1048 -1048
  103. package/docs/api/scripts/collapse.js +38 -38
  104. package/docs/api/scripts/commonNav.js +28 -28
  105. package/docs/api/scripts/linenumber.js +25 -25
  106. package/docs/api/scripts/nav.js +12 -12
  107. package/docs/api/scripts/polyfill.js +3 -3
  108. package/docs/api/scripts/prettify/Apache-License-2.0.txt +202 -202
  109. package/docs/api/scripts/prettify/lang-css.js +2 -2
  110. package/docs/api/scripts/prettify/prettify.js +28 -28
  111. package/docs/api/scripts/search.js +98 -98
  112. package/docs/api/styles/jsdoc.css +776 -776
  113. package/docs/api/styles/prettify.css +80 -80
  114. package/docs/examples.md +328 -328
  115. package/docs/templates.md +418 -418
  116. package/package.json +1 -2
  117. package/scripts/postinstall.js +132 -132
  118. package/scripts/reorganize-skills.cjs +175 -0
  119. package/scripts/validate-agents-structure.cjs +52 -0
  120. package/scripts/validate-skills.cjs +180 -0
  121. package/src/commands/analyze-blazor-concurrency.js +193 -193
  122. package/src/commands/create-story.js +351 -351
  123. package/src/commands/deploy.js +780 -780
  124. package/src/commands/detect-agents.js +9 -0
  125. package/src/commands/detect.js +104 -104
  126. package/src/commands/generate.js +149 -149
  127. package/src/commands/lint-fluent.js +352 -352
  128. package/src/commands/rollback-phase.js +185 -185
  129. package/src/commands/session-summary.js +291 -291
  130. package/src/commands/shard-spec.js +224 -224
  131. package/src/commands/sprint-status.js +250 -250
  132. package/src/commands/state.js +334 -333
  133. package/src/commands/sync.js +167 -167
  134. package/src/commands/troubleshoot.js +222 -222
  135. package/src/commands/update.js +13 -1
  136. package/src/commands/validate-blazor-state.js +210 -210
  137. package/src/commands/validate-blazor.js +156 -156
  138. package/src/commands/validate-css.js +84 -84
  139. package/src/commands/validate-phase.js +221 -221
  140. package/src/lib/blazor-concurrency-analyzer.js +288 -288
  141. package/src/lib/blazor-state-validator.js +291 -291
  142. package/src/lib/blazor-validator.js +374 -374
  143. package/src/lib/css-validator.js +352 -352
  144. package/src/lib/design-system-generator.js +298 -298
  145. package/{detectors → src/lib/detectors}/config-detector.js +223 -223
  146. package/{detectors → src/lib/detectors}/conversation-analyzer.js +163 -163
  147. package/{detectors → src/lib/detectors}/index.js +84 -84
  148. package/{detectors → src/lib/detectors}/standards-generator.js +275 -275
  149. package/src/lib/learning-system.js +520 -520
  150. package/src/lib/mockup-generator.js +366 -366
  151. package/src/lib/state-manager.js +21 -4
  152. package/src/lib/troubleshoot-grep.js +194 -194
  153. package/src/lib/troubleshoot-index.js +144 -144
  154. package/src/lib/ui-detector.js +350 -350
  155. package/src/lib/validators/architecture-validator.js +387 -387
  156. package/src/lib/validators/package-validator.js +360 -360
  157. package/src/lib/validators/ui-contrast-validator.js +422 -422
  158. package/src/utils/logger.js +32 -32
  159. package/src/utils/version-checker.js +175 -175
  160. /package/{detectors → src/lib/detectors}/structure-detector.js +0 -0
@@ -1,377 +1,377 @@
1
- # Padrões de Código - MORPH Framework
2
-
3
- ## 📝 Nomenclatura
4
-
5
- ### Classes e Interfaces
6
- ```csharp
7
- // Interfaces: sempre com prefixo I
8
- public interface IReportService { }
9
- public interface IReportRepository { }
10
-
11
- // Classes: PascalCase, sufixo indica tipo
12
- public class ReportService : IReportService { }
13
- public class ReportRepository : IReportRepository { }
14
- public class ReportController : ControllerBase { }
15
- public class ReportGeneratorJob { } // Hangfire job
16
- public class ReportAnalyzerAgent { } // AI Agent
17
- ```
18
-
19
- ### Métodos
20
- ```csharp
21
- // Async sempre com sufixo Async
22
- public async Task<Report> GetByIdAsync(int id) { }
23
- public async Task CreateAsync(Report report) { }
24
-
25
- // Métodos síncronos sem sufixo
26
- public Report GetById(int id) { }
27
- public void Validate(Report report) { }
28
- ```
29
-
30
- ### Variáveis e Parâmetros
31
- ```csharp
32
- // camelCase para variáveis locais e parâmetros
33
- public async Task ProcessAsync(string reportId, CancellationToken cancellationToken)
34
- {
35
- var report = await _repository.GetByIdAsync(reportId);
36
- var analysisResult = await _analyzer.AnalyzeAsync(report);
37
- }
38
-
39
- // _camelCase para campos privados
40
- private readonly IReportRepository _repository;
41
- private readonly ILogger<ReportService> _logger;
42
- ```
43
-
44
- ---
45
-
46
- ## 📁 Estrutura de Projeto
47
-
48
- ```
49
- src/
50
- ├── MyProject.Web/ # Blazor Server
51
- │ ├── Components/
52
- │ │ ├── Pages/ # Páginas (rotas)
53
- │ │ ├── Shared/ # Componentes compartilhados
54
- │ │ └── Layout/ # Layouts
55
- │ └── Program.cs
56
-
57
- ├── MyProject.Application/ # Casos de uso
58
- │ ├── Features/
59
- │ │ └── Reports/
60
- │ │ ├── Commands/
61
- │ │ ├── Queries/
62
- │ │ └── Services/
63
- │ └── Common/
64
-
65
- ├── MyProject.Domain/ # Entidades e regras
66
- │ ├── Entities/
67
- │ ├── ValueObjects/
68
- │ └── Enums/
69
-
70
- ├── MyProject.Infrastructure/ # Implementações
71
- │ ├── Data/
72
- │ │ ├── Configurations/
73
- │ │ ├── Migrations/
74
- │ │ └── AppDbContext.cs
75
- │ └── Services/
76
-
77
- └── MyProject.Agents/ # MS Agent Framework
78
- └── ReportAnalyzer/
79
- ```
80
-
81
- ---
82
-
83
- ## 💉 Dependency Injection
84
-
85
- ```csharp
86
- // Usar primary constructor (C# 12+)
87
- public class ReportService(
88
- IReportRepository repository,
89
- ILogger<ReportService> logger) : IReportService
90
- {
91
- public async Task<Report> GetByIdAsync(int id)
92
- {
93
- logger.LogInformation("Getting report {ReportId}", id);
94
- return await repository.GetByIdAsync(id);
95
- }
96
- }
97
- ```
98
-
99
- ---
100
-
101
- ## 🗄️ Entity Framework Core
102
-
103
- ### Entidades
104
- ```csharp
105
- public class ReportSchedule
106
- {
107
- public int Id { get; private set; }
108
- public string Name { get; private set; } = string.Empty;
109
- public ReportType ReportType { get; private set; }
110
- public string CronExpression { get; private set; } = string.Empty;
111
- public List<string> Recipients { get; private set; } = [];
112
- public bool IsActive { get; private set; }
113
- public DateTime CreatedAt { get; private set; }
114
-
115
- // Factory method para criação
116
- public static ReportSchedule Create(string name, ReportType reportType,
117
- string cronExpression, List<string> recipients)
118
- {
119
- return new ReportSchedule
120
- {
121
- Name = name,
122
- ReportType = reportType,
123
- CronExpression = cronExpression,
124
- Recipients = recipients,
125
- IsActive = true,
126
- CreatedAt = DateTime.UtcNow
127
- };
128
- }
129
-
130
- // Métodos de domínio
131
- public void Activate() => IsActive = true;
132
- public void Deactivate() => IsActive = false;
133
- }
134
- ```
135
-
136
- ### Configuração Fluent API
137
- ```csharp
138
- public class ReportScheduleConfiguration : IEntityTypeConfiguration<ReportSchedule>
139
- {
140
- public void Configure(EntityTypeBuilder<ReportSchedule> builder)
141
- {
142
- builder.ToTable("ReportSchedules");
143
- builder.HasKey(x => x.Id);
144
-
145
- builder.Property(x => x.Name)
146
- .IsRequired()
147
- .HasMaxLength(200);
148
-
149
- // JSON column para lista
150
- builder.Property(x => x.Recipients)
151
- .HasConversion(
152
- v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null),
153
- v => JsonSerializer.Deserialize<List<string>>(v, (JsonSerializerOptions?)null) ?? [])
154
- .HasColumnType("nvarchar(max)");
155
- }
156
- }
157
- ```
158
-
159
- ---
160
-
161
- ## 🔥 Componentes Blazor
162
-
163
- ```razor
164
- @page "/reports/schedules"
165
- @attribute [Authorize(Policy = "CanManageReports")]
166
- @inject IReportScheduleService ReportService
167
- @inject ILogger<ReportScheduleList> Logger
168
-
169
- <PageTitle>Report Schedules</PageTitle>
170
-
171
- @if (_isLoading)
172
- {
173
- <LoadingSpinner />
174
- }
175
- else if (_schedules is null || !_schedules.Any())
176
- {
177
- <EmptyState Message="No schedules configured" />
178
- }
179
- else
180
- {
181
- <div class="schedule-list">
182
- @foreach (var schedule in _schedules)
183
- {
184
- <ScheduleCard Schedule="schedule" OnToggle="HandleToggleAsync" />
185
- }
186
- </div>
187
- }
188
-
189
- @code {
190
- private List<ReportScheduleDto>? _schedules;
191
- private bool _isLoading = true;
192
-
193
- protected override async Task OnInitializedAsync()
194
- {
195
- try
196
- {
197
- _schedules = await ReportService.GetAllAsync();
198
- }
199
- catch (Exception ex)
200
- {
201
- Logger.LogError(ex, "Failed to load schedules");
202
- }
203
- finally
204
- {
205
- _isLoading = false;
206
- }
207
- }
208
- }
209
- ```
210
-
211
- ### Padrões Obrigatórios
212
- - ✅ Loading state em toda página
213
- - ✅ Empty state quando lista vazia
214
- - ✅ Error handling com try/catch
215
- - ✅ `@inject` para DI (não construtor)
216
- - ✅ `@attribute [Authorize]` quando necessário
217
-
218
- ---
219
-
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
226
-
227
- ```csharp
228
- using Microsoft.Agents.AI;
229
- using Microsoft.Extensions.AI;
230
-
231
- public class ReportAnalyzerAgent(
232
- IChatClient chatClient,
233
- ILogger<ReportAnalyzerAgent> logger) : IReportAnalyzerAgent
234
- {
235
- public async Task<AnalysisResult> AnalyzeAsync(
236
- ReportData reportData,
237
- CancellationToken cancellationToken = default)
238
- {
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()}";
253
-
254
- try
255
- {
256
- var response = await agent.RunAsync(prompt, cancellationToken: cancellationToken);
257
- return ParseResponse(response.Content);
258
- }
259
- catch (Exception ex)
260
- {
261
- logger.LogError(ex, "Analysis failed for report {ReportId}", reportData.Id);
262
- throw new ReportAnalysisException("Failed to analyze", ex);
263
- }
264
- }
265
- }
266
- ```
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
-
288
- ---
289
-
290
- ## ⏰ Hangfire Jobs
291
-
292
- ```csharp
293
- public class ReportGeneratorJob(
294
- IReportScheduleService scheduleService,
295
- IReportAnalyzerAgent analyzer,
296
- ILogger<ReportGeneratorJob> logger) : IReportGeneratorJob
297
- {
298
- [AutomaticRetry(Attempts = 3, DelaysInSeconds = new[] { 60, 300, 900 })]
299
- [Queue("reports")]
300
- public async Task ExecuteAsync(int scheduleId, CancellationToken cancellationToken)
301
- {
302
- logger.LogInformation("Starting report generation for {ScheduleId}", scheduleId);
303
-
304
- var schedule = await scheduleService.GetByIdAsync(scheduleId);
305
- if (schedule is null || !schedule.IsActive)
306
- {
307
- logger.LogWarning("Schedule {ScheduleId} not found or inactive", scheduleId);
308
- return;
309
- }
310
-
311
- // ... implementação
312
- }
313
- }
314
- ```
315
-
316
- ---
317
-
318
- ## 🧪 Testes
319
-
320
- ### Nomenclatura
321
- ```csharp
322
- // Classe: {ClasseTestada}Tests
323
- public class ReportServiceTests
324
- {
325
- // Método: {Método}_{Cenário}_{ResultadoEsperado}
326
- [Fact]
327
- public async Task GetByIdAsync_WithValidId_ReturnsReport() { }
328
-
329
- [Fact]
330
- public async Task GetByIdAsync_WithInvalidId_ThrowsNotFoundException() { }
331
- }
332
- ```
333
-
334
- ### Estrutura AAA
335
- ```csharp
336
- [Fact]
337
- public async Task CreateAsync_WithValidData_CreatesSchedule()
338
- {
339
- // Arrange
340
- var request = new CreateReportScheduleRequest("Daily Sales", ReportType.Sales);
341
-
342
- // Act
343
- var result = await _service.CreateAsync(request);
344
-
345
- // Assert
346
- Assert.NotNull(result);
347
- Assert.Equal(request.Name, result.Name);
348
- }
349
- ```
350
-
351
- ---
352
-
353
- ## 🚫 Anti-Patterns a Evitar
354
-
355
- ```csharp
356
- // ❌ Service Locator
357
- var service = serviceProvider.GetService<IReportService>();
358
-
359
- // ✅ Constructor Injection
360
- public class MyClass(IReportService reportService) { }
361
-
362
- // ❌ Async void
363
- public async void ProcessReport() { }
364
-
365
- // ✅ Async Task
366
- public async Task ProcessReportAsync() { }
367
-
368
- // ❌ Catching generic Exception sem log
369
- catch (Exception) { }
370
-
371
- // ✅ Log and handle
372
- catch (Exception ex)
373
- {
374
- _logger.LogError(ex, "Operation failed");
375
- throw;
376
- }
377
- ```
1
+ # Padrões de Código - MORPH Framework
2
+
3
+ ## 📝 Nomenclatura
4
+
5
+ ### Classes e Interfaces
6
+ ```csharp
7
+ // Interfaces: sempre com prefixo I
8
+ public interface IReportService { }
9
+ public interface IReportRepository { }
10
+
11
+ // Classes: PascalCase, sufixo indica tipo
12
+ public class ReportService : IReportService { }
13
+ public class ReportRepository : IReportRepository { }
14
+ public class ReportController : ControllerBase { }
15
+ public class ReportGeneratorJob { } // Hangfire job
16
+ public class ReportAnalyzerAgent { } // AI Agent
17
+ ```
18
+
19
+ ### Métodos
20
+ ```csharp
21
+ // Async sempre com sufixo Async
22
+ public async Task<Report> GetByIdAsync(int id) { }
23
+ public async Task CreateAsync(Report report) { }
24
+
25
+ // Métodos síncronos sem sufixo
26
+ public Report GetById(int id) { }
27
+ public void Validate(Report report) { }
28
+ ```
29
+
30
+ ### Variáveis e Parâmetros
31
+ ```csharp
32
+ // camelCase para variáveis locais e parâmetros
33
+ public async Task ProcessAsync(string reportId, CancellationToken cancellationToken)
34
+ {
35
+ var report = await _repository.GetByIdAsync(reportId);
36
+ var analysisResult = await _analyzer.AnalyzeAsync(report);
37
+ }
38
+
39
+ // _camelCase para campos privados
40
+ private readonly IReportRepository _repository;
41
+ private readonly ILogger<ReportService> _logger;
42
+ ```
43
+
44
+ ---
45
+
46
+ ## 📁 Estrutura de Projeto
47
+
48
+ ```
49
+ src/
50
+ ├── MyProject.Web/ # Blazor Server
51
+ │ ├── Components/
52
+ │ │ ├── Pages/ # Páginas (rotas)
53
+ │ │ ├── Shared/ # Componentes compartilhados
54
+ │ │ └── Layout/ # Layouts
55
+ │ └── Program.cs
56
+
57
+ ├── MyProject.Application/ # Casos de uso
58
+ │ ├── Features/
59
+ │ │ └── Reports/
60
+ │ │ ├── Commands/
61
+ │ │ ├── Queries/
62
+ │ │ └── Services/
63
+ │ └── Common/
64
+
65
+ ├── MyProject.Domain/ # Entidades e regras
66
+ │ ├── Entities/
67
+ │ ├── ValueObjects/
68
+ │ └── Enums/
69
+
70
+ ├── MyProject.Infrastructure/ # Implementações
71
+ │ ├── Data/
72
+ │ │ ├── Configurations/
73
+ │ │ ├── Migrations/
74
+ │ │ └── AppDbContext.cs
75
+ │ └── Services/
76
+
77
+ └── MyProject.Agents/ # MS Agent Framework
78
+ └── ReportAnalyzer/
79
+ ```
80
+
81
+ ---
82
+
83
+ ## 💉 Dependency Injection
84
+
85
+ ```csharp
86
+ // Usar primary constructor (C# 12+)
87
+ public class ReportService(
88
+ IReportRepository repository,
89
+ ILogger<ReportService> logger) : IReportService
90
+ {
91
+ public async Task<Report> GetByIdAsync(int id)
92
+ {
93
+ logger.LogInformation("Getting report {ReportId}", id);
94
+ return await repository.GetByIdAsync(id);
95
+ }
96
+ }
97
+ ```
98
+
99
+ ---
100
+
101
+ ## 🗄️ Entity Framework Core
102
+
103
+ ### Entidades
104
+ ```csharp
105
+ public class ReportSchedule
106
+ {
107
+ public int Id { get; private set; }
108
+ public string Name { get; private set; } = string.Empty;
109
+ public ReportType ReportType { get; private set; }
110
+ public string CronExpression { get; private set; } = string.Empty;
111
+ public List<string> Recipients { get; private set; } = [];
112
+ public bool IsActive { get; private set; }
113
+ public DateTime CreatedAt { get; private set; }
114
+
115
+ // Factory method para criação
116
+ public static ReportSchedule Create(string name, ReportType reportType,
117
+ string cronExpression, List<string> recipients)
118
+ {
119
+ return new ReportSchedule
120
+ {
121
+ Name = name,
122
+ ReportType = reportType,
123
+ CronExpression = cronExpression,
124
+ Recipients = recipients,
125
+ IsActive = true,
126
+ CreatedAt = DateTime.UtcNow
127
+ };
128
+ }
129
+
130
+ // Métodos de domínio
131
+ public void Activate() => IsActive = true;
132
+ public void Deactivate() => IsActive = false;
133
+ }
134
+ ```
135
+
136
+ ### Configuração Fluent API
137
+ ```csharp
138
+ public class ReportScheduleConfiguration : IEntityTypeConfiguration<ReportSchedule>
139
+ {
140
+ public void Configure(EntityTypeBuilder<ReportSchedule> builder)
141
+ {
142
+ builder.ToTable("ReportSchedules");
143
+ builder.HasKey(x => x.Id);
144
+
145
+ builder.Property(x => x.Name)
146
+ .IsRequired()
147
+ .HasMaxLength(200);
148
+
149
+ // JSON column para lista
150
+ builder.Property(x => x.Recipients)
151
+ .HasConversion(
152
+ v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null),
153
+ v => JsonSerializer.Deserialize<List<string>>(v, (JsonSerializerOptions?)null) ?? [])
154
+ .HasColumnType("nvarchar(max)");
155
+ }
156
+ }
157
+ ```
158
+
159
+ ---
160
+
161
+ ## 🔥 Componentes Blazor
162
+
163
+ ```razor
164
+ @page "/reports/schedules"
165
+ @attribute [Authorize(Policy = "CanManageReports")]
166
+ @inject IReportScheduleService ReportService
167
+ @inject ILogger<ReportScheduleList> Logger
168
+
169
+ <PageTitle>Report Schedules</PageTitle>
170
+
171
+ @if (_isLoading)
172
+ {
173
+ <LoadingSpinner />
174
+ }
175
+ else if (_schedules is null || !_schedules.Any())
176
+ {
177
+ <EmptyState Message="No schedules configured" />
178
+ }
179
+ else
180
+ {
181
+ <div class="schedule-list">
182
+ @foreach (var schedule in _schedules)
183
+ {
184
+ <ScheduleCard Schedule="schedule" OnToggle="HandleToggleAsync" />
185
+ }
186
+ </div>
187
+ }
188
+
189
+ @code {
190
+ private List<ReportScheduleDto>? _schedules;
191
+ private bool _isLoading = true;
192
+
193
+ protected override async Task OnInitializedAsync()
194
+ {
195
+ try
196
+ {
197
+ _schedules = await ReportService.GetAllAsync();
198
+ }
199
+ catch (Exception ex)
200
+ {
201
+ Logger.LogError(ex, "Failed to load schedules");
202
+ }
203
+ finally
204
+ {
205
+ _isLoading = false;
206
+ }
207
+ }
208
+ }
209
+ ```
210
+
211
+ ### Padrões Obrigatórios
212
+ - ✅ Loading state em toda página
213
+ - ✅ Empty state quando lista vazia
214
+ - ✅ Error handling com try/catch
215
+ - ✅ `@inject` para DI (não construtor)
216
+ - ✅ `@attribute [Authorize]` quando necessário
217
+
218
+ ---
219
+
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
226
+
227
+ ```csharp
228
+ using Microsoft.Agents.AI;
229
+ using Microsoft.Extensions.AI;
230
+
231
+ public class ReportAnalyzerAgent(
232
+ IChatClient chatClient,
233
+ ILogger<ReportAnalyzerAgent> logger) : IReportAnalyzerAgent
234
+ {
235
+ public async Task<AnalysisResult> AnalyzeAsync(
236
+ ReportData reportData,
237
+ CancellationToken cancellationToken = default)
238
+ {
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()}";
253
+
254
+ try
255
+ {
256
+ var response = await agent.RunAsync(prompt, cancellationToken: cancellationToken);
257
+ return ParseResponse(response.Content);
258
+ }
259
+ catch (Exception ex)
260
+ {
261
+ logger.LogError(ex, "Analysis failed for report {ReportId}", reportData.Id);
262
+ throw new ReportAnalysisException("Failed to analyze", ex);
263
+ }
264
+ }
265
+ }
266
+ ```
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
+
288
+ ---
289
+
290
+ ## ⏰ Hangfire Jobs
291
+
292
+ ```csharp
293
+ public class ReportGeneratorJob(
294
+ IReportScheduleService scheduleService,
295
+ IReportAnalyzerAgent analyzer,
296
+ ILogger<ReportGeneratorJob> logger) : IReportGeneratorJob
297
+ {
298
+ [AutomaticRetry(Attempts = 3, DelaysInSeconds = new[] { 60, 300, 900 })]
299
+ [Queue("reports")]
300
+ public async Task ExecuteAsync(int scheduleId, CancellationToken cancellationToken)
301
+ {
302
+ logger.LogInformation("Starting report generation for {ScheduleId}", scheduleId);
303
+
304
+ var schedule = await scheduleService.GetByIdAsync(scheduleId);
305
+ if (schedule is null || !schedule.IsActive)
306
+ {
307
+ logger.LogWarning("Schedule {ScheduleId} not found or inactive", scheduleId);
308
+ return;
309
+ }
310
+
311
+ // ... implementação
312
+ }
313
+ }
314
+ ```
315
+
316
+ ---
317
+
318
+ ## 🧪 Testes
319
+
320
+ ### Nomenclatura
321
+ ```csharp
322
+ // Classe: {ClasseTestada}Tests
323
+ public class ReportServiceTests
324
+ {
325
+ // Método: {Método}_{Cenário}_{ResultadoEsperado}
326
+ [Fact]
327
+ public async Task GetByIdAsync_WithValidId_ReturnsReport() { }
328
+
329
+ [Fact]
330
+ public async Task GetByIdAsync_WithInvalidId_ThrowsNotFoundException() { }
331
+ }
332
+ ```
333
+
334
+ ### Estrutura AAA
335
+ ```csharp
336
+ [Fact]
337
+ public async Task CreateAsync_WithValidData_CreatesSchedule()
338
+ {
339
+ // Arrange
340
+ var request = new CreateReportScheduleRequest("Daily Sales", ReportType.Sales);
341
+
342
+ // Act
343
+ var result = await _service.CreateAsync(request);
344
+
345
+ // Assert
346
+ Assert.NotNull(result);
347
+ Assert.Equal(request.Name, result.Name);
348
+ }
349
+ ```
350
+
351
+ ---
352
+
353
+ ## 🚫 Anti-Patterns a Evitar
354
+
355
+ ```csharp
356
+ // ❌ Service Locator
357
+ var service = serviceProvider.GetService<IReportService>();
358
+
359
+ // ✅ Constructor Injection
360
+ public class MyClass(IReportService reportService) { }
361
+
362
+ // ❌ Async void
363
+ public async void ProcessReport() { }
364
+
365
+ // ✅ Async Task
366
+ public async Task ProcessReportAsync() { }
367
+
368
+ // ❌ Catching generic Exception sem log
369
+ catch (Exception) { }
370
+
371
+ // ✅ Log and handle
372
+ catch (Exception ex)
373
+ {
374
+ _logger.LogError(ex, "Operation failed");
375
+ throw;
376
+ }
377
+ ```