spec-first-claude 0.6.0-beta.9 → 0.7.0-beta.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 (35) hide show
  1. package/package.json +1 -1
  2. package/templates/.claude/CHANGELOG.md +94 -0
  3. package/templates/.claude/adapters/SETUP.md +3 -3
  4. package/templates/.claude/adapters/filesystem.md +2 -2
  5. package/templates/.claude/adapters/naming.md +1 -1
  6. package/templates/.claude/adapters/registry.md +2 -2
  7. package/templates/.claude/agents/backend-coder.md +215 -215
  8. package/templates/.claude/agents/db-coder.md +5 -5
  9. package/templates/.claude/agents/doc-writer.md +32 -19
  10. package/templates/.claude/agents/frontend-coder.md +222 -222
  11. package/templates/.claude/agents/infra-coder.md +341 -341
  12. package/templates/.claude/agents/reviewer.md +99 -99
  13. package/templates/.claude/agents/security-reviewer.md +5 -5
  14. package/templates/.claude/commands/design.md +101 -157
  15. package/templates/.claude/commands/dev.md +24 -19
  16. package/templates/.claude/commands/extract.md +118 -143
  17. package/templates/.claude/commands/load.md +5 -5
  18. package/templates/.claude/commands/mcp.md +1 -1
  19. package/templates/.claude/commands/merge-docs.md +109 -58
  20. package/templates/.claude/commands/plan.md +80 -57
  21. package/templates/.claude/commands/publish.md +4 -4
  22. package/templates/.claude/commands/sfw-start.md +101 -55
  23. package/templates/.claude/scripts/bootstrap-confluence.js +101 -35
  24. package/templates/.claude/templates/feature/PRD.template.md +279 -286
  25. package/templates/.claude/templates/feature/TRD.template.md +358 -0
  26. package/templates/.claude/templates/feature/context.template.md +54 -13
  27. package/templates/.claude/templates/feature/extract-log.template.md +19 -9
  28. package/templates/.claude/templates/specs/brief.template.md +11 -4
  29. package/templates/.claude/templates/specs/contracts.template.md +11 -5
  30. package/templates/.claude/templates/specs/scenarios.template.md +13 -5
  31. package/templates/.claude/templates/specs/tasks.template.md +11 -9
  32. package/templates/CLAUDE.md +43 -39
  33. package/templates/sfw.config.yml.example +4 -4
  34. package/templates/.claude/templates/feature/backlog-extraido.template.md +0 -156
  35. package/templates/.claude/templates/feature/sdd.template.md +0 -559
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spec-first-claude",
3
- "version": "0.6.0-beta.9",
3
+ "version": "0.7.0-beta.1",
4
4
  "description": "Spec-first workflow kit for Claude Code — AI-driven development with specs, not guesswork",
5
5
  "bin": {
6
6
  "spec-first-claude": "bin/cli.js"
@@ -8,6 +8,100 @@
8
8
 
9
9
  ---
10
10
 
11
+ ## 0.7.0-beta.1 (2026-04-13) — Redesign v4 (BREAKING)
12
+
13
+ Reestruturação arquitetural baseada em feedback de uso real do v3. TRD ressuscitado
14
+ como peer do PRD, SDD eliminado, aprovação por persona. Pipeline continua unificado
15
+ via `/sfw-start`.
16
+
17
+ ### Breaking
18
+
19
+ - **TRD ressuscitado** — peer do PRD, aprovado pelo dev. Não é o TRD antigo
20
+ transiente: agora tem papel próprio (source técnica autoritativa).
21
+ - **SDD eliminado** — camada intermediária desnecessária quando PRD+TRD são bem
22
+ estruturados. `specs/{nome}/` deriva direto de PRD+TRD. `docs/` atualizado via
23
+ `/merge-docs` a partir de PRD+TRD.
24
+ - **Aprovação por persona** — PM aprova PRD (`prd_aprovado: true`), dev aprova TRD
25
+ (`trd_aprovado: true`). Flags no `.context.md`. Em time pequeno, user faz as 2
26
+ aprovações — não pula.
27
+ - **Discovery obrigatório** — `/discovery` deixou de ser skill opcional e virou
28
+ passo obrigatório dentro do `/sfw-start`. Força entendimento antes da extração.
29
+ - **PRD `empty` removido** — conceito obsoleto. Agora: scope puro-técnico tem
30
+ `prd_existe=false`; scope puro-produto tem `trd_existe=false`. Maioria tem ambos.
31
+ - **backlog_extraido.md eliminado** — features futuras viram itens em PRD §11
32
+ Fora de Escopo com sugestão de `/sfw-start <nome>` próprio.
33
+ - **Schema do `.context.md` mudou**: `prd_empty` → `prd_existe` + `prd_aprovado`;
34
+ adicionado `trd_existe` + `trd_aprovado`. Status fluxo tem novos estados:
35
+ `discovery_done → extract_done → prd_approved → trd_approved → design_done`.
36
+
37
+ ### Templates
38
+
39
+ - `TRD.template.md` novo — 11 seções, organizado por área (§Sistema + §Área-X)
40
+ - `PRD.template.md` refatorado — 13 seções puro-produto, sem conteúdo técnico
41
+ - `sdd.template.md` deletado
42
+ - `backlog-extraido.template.md` deletado
43
+ - `context.template.md` schema v4
44
+ - `extract-log.template.md` sem categorização "produto/tech" (obsoleta)
45
+ - `specs/*.template.md` com ORIGEM atualizada: PRD+TRD em vez de SDD
46
+
47
+ ### Skills
48
+
49
+ - `/sfw-start` — 2 aprovações + discovery obrigatório
50
+ - `/extract` — gera PRD e/ou TRD conforme conteúdo dos insumos (Reader + Produto-Analyzer + Tech-Analyzer)
51
+ - `/design` — consome PRD+TRD aprovados, gera docs/ (first-run) + specs/
52
+ - `/merge-docs` — diff semântico PRD+TRD ↔ docs/ (agente Integrator promovido pra Opus)
53
+ - `/plan` — entrada PRD+TRD+specs/, tasks com `Ref TRD` em vez de `Ref SDD`
54
+ - `/publish` — adicionado TRD à whitelist; SDD removido
55
+
56
+ ### Agents
57
+
58
+ - Doc Writer reescrito: fonte muda de "SDD §Sistema" para "PRD + TRD"
59
+ - Coders (backend/frontend/db/infra) e Reviewers: refs de "SDD §X" → "TRD §X" ou "specs/{nome}/"
60
+
61
+ ### Migração de v3 (0.6.x) pra v4
62
+
63
+ ```bash
64
+ # 1. Atualizar kit
65
+ npx spec-first-claude@beta update
66
+
67
+ # 2. Para cada scope em andamento: migrar .context.md
68
+ # - Remover `prd_empty`
69
+ # - Adicionar `prd_existe`, `trd_existe`, `prd_aprovado`, `trd_aprovado`
70
+ # - Se scope já tem SDD: extrair conteúdo técnico pra TRD.md manual
71
+
72
+ # 3. SDDs antigos em workspace/Output/{nome}/sdd.md ficam como legado
73
+ # (nenhum skill v4 lê eles). Deletar quando confortável.
74
+ ```
75
+
76
+ ### Versionamento
77
+
78
+ - Bump **minor** (0.6 → 0.7) pelo breaking. v3 (`0.6.0-beta.10`) permanece no npm
79
+ como histórico.
80
+ - Publicado com tag `beta`. Promoção para `latest` apenas após validação em uso real.
81
+
82
+ ---
83
+
84
+ ## 0.6.0-beta.10 (2026-04-13)
85
+
86
+ ### `bootstrap-confluence.js` agora é idempotente
87
+
88
+ Script agora lê `.mcp.json` existente e reusa valores da entry `atlassian` em
89
+ vez de perguntar tudo de novo:
90
+
91
+ - Se `CONFLUENCE_URL` já existe: mostra URL atual, pergunta "Manter? [S/n]"
92
+ - Se `CONFLUENCE_USERNAME` já existe: mostra email atual, pergunta "Manter? [S/n]"
93
+ - Se `CONFLUENCE_API_TOKEN` já existe: mostra token mascarado (`abcd…wxyz`), pergunta "Manter? [S/n]"
94
+ - Default ENTER = manter. Responder `n` abre o prompt completo da informação.
95
+
96
+ Se todos os 3 campos foram mantidos: detecta que nada mudou e **não reescreve**
97
+ `.mcp.json` (evita dirty git). Se mudou algo: reescreve preservando outras
98
+ entries MCP (Supabase, GitHub, etc.).
99
+
100
+ Útil pra: reexecução após novo rebuild do MCP, rotação apenas do token, debug
101
+ de setup sem perder credenciais já válidas.
102
+
103
+ ---
104
+
11
105
  ## 0.6.0-beta.9 (2026-04-13)
12
106
 
13
107
  ### CLI em pt-BR + dica de bootstrap-confluence no init
@@ -148,7 +148,7 @@ output:
148
148
  config:
149
149
  space_key: "ST" # ← mesmo Space Key
150
150
  parent_page_id: "294931" # ← Page ID da page Output
151
- publishes: [PRD, SDD, Progresso]
151
+ publishes: [PRD, TRD, Progresso]
152
152
  mode: auto # ← auto | manual | off
153
153
  conflict_detection: version
154
154
  approval_mechanism: label
@@ -225,7 +225,7 @@ output:
225
225
  adapter: filesystem
226
226
  config:
227
227
  root_path: "workspace/Output"
228
- publishes: [PRD, SDD, Progresso]
228
+ publishes: [PRD, TRD, Progresso]
229
229
  mode: auto
230
230
  conflict_detection: hash
231
231
  approval_mechanism: none
@@ -282,7 +282,7 @@ E coloque `mode: off` nos targets de Confluence.
282
282
  config:
283
283
  space_key: "ST"
284
284
  parent_page_id: "294931"
285
- publishes: [PRD, SDD, Progresso]
285
+ publishes: [PRD, TRD, Progresso]
286
286
  mode: auto
287
287
  ```
288
288
  3. Re-rode `/extract`, `/design`, `/plan` — os artefatos locais serão publicados
@@ -259,7 +259,7 @@ output:
259
259
  adapter: filesystem
260
260
  config:
261
261
  root_path: "./workspace/Output"
262
- publishes: [PRD, SDD, Progresso]
262
+ publishes: [PRD, TRD, Progresso]
263
263
  mode: auto
264
264
  conflict_detection: hash
265
265
  approval_mechanism: none
@@ -284,7 +284,7 @@ output:
284
284
  adapter: filesystem
285
285
  config:
286
286
  root_path: "./tests/fixtures/barbearia/actual_output"
287
- publishes: [PRD, SDD, Progresso]
287
+ publishes: [PRD, TRD, Progresso]
288
288
  mode: auto
289
289
  ```
290
290
 
@@ -56,7 +56,7 @@ O `sfw.config.yml` define 2 templates de output:
56
56
  **`output_container`** é o agrupador. No Confluence vira uma page-mãe com
57
57
  children; no filesystem vira uma pasta.
58
58
 
59
- **`output_artifact`** é o item individual (PRD, SDD, Progresso). No
59
+ **`output_artifact`** é o item individual (PRD, TRD, Progresso). No
60
60
  Confluence é title de uma child page; no filesystem é nome de arquivo
61
61
  (adapter adiciona `.md` automaticamente).
62
62
 
@@ -178,13 +178,13 @@ output:
178
178
  config:
179
179
  space_key: ST
180
180
  parent_page_id: "294931"
181
- publishes: [PRD, SDD, Progresso]
181
+ publishes: [PRD, TRD, Progresso]
182
182
  mode: auto
183
183
  - name: local-mirror
184
184
  adapter: filesystem
185
185
  config:
186
186
  root_path: "./mirror"
187
- publishes: [PRD, SDD, Progresso, backlog]
187
+ publishes: [PRD, TRD, Progresso]
188
188
  mode: auto
189
189
  ```
190
190
 
@@ -1,215 +1,215 @@
1
- # Agent: Backend Coder (.NET 8)
2
-
3
- > Especialista em desenvolvimento backend com .NET 8 / C#.
4
- > Implementa tasks da área BACK seguindo SDD + rules.md.
5
-
6
- ---
7
-
8
- ## Identidade
9
-
10
- | Campo | Valor |
11
- |-------|-------|
12
- | Área | BACK |
13
- | Modelo padrão | Sonnet (S/M) / Opus (L) |
14
- | Lê | `specs/{nome}/contracts.md` (API, dados, regras) + `scenarios.md` (fluxos, CAs) + task + `rules.md` |
15
- | Nunca lê | PRD, PM, docs de outras áreas |
16
-
17
- ## Stack de referência
18
-
19
- | Tecnologia | Versão | Uso |
20
- |-----------|--------|-----|
21
- | .NET | 8 LTS | Runtime + SDK |
22
- | C# | 12 | Linguagem |
23
- | ASP.NET Core | 8 | Web framework (Minimal APIs ou Controllers) |
24
- | Entity Framework Core | 8 | ORM (migrations, queries) |
25
- | xUnit | latest | Testes unit + integration |
26
- | FluentAssertions | latest | Assertions legíveis |
27
- | FluentValidation | latest | Validação de DTOs/requests |
28
- | MediatR | latest | CQRS / mediator (se SDD indicar) |
29
- | Serilog | latest | Logging estruturado |
30
-
31
- ## Padrões obrigatórios
32
-
33
- ### Estrutura do projeto
34
- ```
35
- src/
36
- ├── Api/ ← Entry point (Program.cs, endpoints/controllers)
37
- │ ├── Endpoints/ ← Minimal API endpoint groups
38
- │ ├── Middleware/ ← Auth, error handling, logging
39
- │ └── Program.cs
40
- ├── Application/ ← Use cases, DTOs, validações
41
- │ ├── Commands/ ← Write operations
42
- │ ├── Queries/ ← Read operations
43
- │ ├── DTOs/
44
- │ └── Validators/ ← FluentValidation
45
- ├── Domain/ ← Entidades, regras de negócio, interfaces
46
- │ ├── Entities/
47
- │ ├── Enums/
48
- │ └── Interfaces/
49
- ├── Infrastructure/ ← EF DbContext, repositories, serviços externos
50
- │ ├── Data/
51
- │ │ ├── Configurations/ ← EF entity configurations
52
- │ │ ├── Migrations/
53
- │ │ └── AppDbContext.cs
54
- │ ├── Repositories/
55
- │ └── Services/
56
- tests/
57
- ├── Unit/ ← Domain + Application
58
- ├── Integration/ ← API endpoints (WebApplicationFactory)
59
- └── Fixtures/
60
- ```
61
-
62
- ### Convenções de código
63
-
64
- | Regra | Exemplo |
65
- |-------|---------|
66
- | Namespaces seguem pastas | `namespace PetCare.Application.Commands;` |
67
- | Classes seladas por padrão | `public sealed class CreateAgendamentoCommand` |
68
- | Records para DTOs | `public record CreateAgendamentoRequest(int PetId, int ServicoId, DateTime DataHora);` |
69
- | Nullable reference types | Habilitado (`<Nullable>enable</Nullable>`) |
70
- | Async/await em tudo I/O | Nunca `.Result` ou `.Wait()` |
71
- | Injeção de dependência | Via construtor, registrado no DI container |
72
- | Respostas padronizadas | `Results.Ok(data)`, `Results.Created(uri, data)`, `Results.Problem(...)` |
73
-
74
- ### Padrões de endpoint (Minimal API)
75
-
76
- ```csharp
77
- // Endpoint group
78
- public static class AgendamentoEndpoints
79
- {
80
- public static void MapAgendamentoEndpoints(this IEndpointRouteBuilder app)
81
- {
82
- var group = app.MapGroup("/api/v1/agendamentos")
83
- .RequireAuthorization();
84
-
85
- group.MapPost("/", CreateAgendamento)
86
- .WithName("CreateAgendamento")
87
- .Produces<AgendamentoResponse>(201)
88
- .ProducesValidationProblem()
89
- .ProducesProblem(409);
90
- }
91
-
92
- private static async Task<IResult> CreateAgendamento(
93
- CreateAgendamentoRequest request,
94
- IValidator<CreateAgendamentoRequest> validator,
95
- IAgendamentoService service,
96
- CancellationToken ct)
97
- {
98
- var validation = await validator.ValidateAsync(request, ct);
99
- if (!validation.IsValid)
100
- return Results.ValidationProblem(validation.ToDictionary());
101
-
102
- var result = await service.CreateAsync(request, ct);
103
- return result.Match(
104
- success => Results.Created($"/api/v1/agendamentos/{success.Id}", success),
105
- error => Results.Problem(error.ToProblemDetails())
106
- );
107
- }
108
- }
109
- ```
110
-
111
- ### Padrões de teste
112
-
113
- ```csharp
114
- // Unit test (xUnit + FluentAssertions)
115
- public class AgendamentoServiceTests
116
- {
117
- [Fact]
118
- public async Task Create_DeveCalcularDuracaoPorPorte_Grande()
119
- {
120
- // Arrange
121
- var service = new AgendamentoService(mockRepo.Object);
122
- var request = new CreateAgendamentoRequest(PetId: 1, ServicoId: 1, DataHora: DateTime.Now.AddDays(1));
123
-
124
- // Act
125
- var result = await service.CreateAsync(request, CancellationToken.None);
126
-
127
- // Assert
128
- result.Should().BeSuccess();
129
- result.Value.DuracaoMin.Should().Be(90); // base 60 + 30 (grande)
130
- }
131
- }
132
-
133
- // Integration test (WebApplicationFactory)
134
- public class AgendamentoEndpointsTests : IClassFixture<WebApplicationFactory<Program>>
135
- {
136
- [Fact]
137
- public async Task Post_Agendamento_Valido_Retorna201()
138
- {
139
- // Arrange
140
- var client = _factory.CreateClient();
141
- var request = new { PetId = 1, ServicoId = 1, DataHora = "2026-04-10T09:00:00" };
142
-
143
- // Act
144
- var response = await client.PostAsJsonAsync("/api/v1/agendamentos", request);
145
-
146
- // Assert
147
- response.StatusCode.Should().Be(HttpStatusCode.Created);
148
- }
149
- }
150
- ```
151
-
152
- ### Error handling
153
-
154
- ```csharp
155
- // Resultado tipado (evita exceptions para fluxo de negócio)
156
- public abstract record Result<T>
157
- {
158
- public record Success(T Value) : Result<T>;
159
- public record Error(string Code, string Message) : Result<T>;
160
- }
161
-
162
- // Códigos de erro do domínio (mapeiam para HTTP no endpoint)
163
- public static class DomainErrors
164
- {
165
- public static readonly Error HorarioPassado = new("HORARIO_PASSADO", "Não é possível agendar no passado");
166
- public static readonly Error ForaHorarioLoja = new("FORA_HORARIO_LOJA", "Horário fora do expediente");
167
- public static readonly Error SemTosadorDisponivel = new("SEM_TOSADOR_DISPONIVEL", "Nenhum tosador disponível");
168
- }
169
- ```
170
-
171
- ## Segurança por Endpoint (OBRIGATÓRIO)
172
-
173
- Cada endpoint implementado DEVE ter:
174
-
175
- ### Autenticação
176
- - Seguir mecanismo definido no SDD §5 (Bearer token, API Key, público)
177
- - Se o SDD diz "Bearer token" → implementar `[Authorize]` ou `.RequireAuthorization()`
178
- - Se o SDD diz "público" → usar `.AllowAnonymous()` explicitamente
179
- - **Nunca criar endpoint sem auth definido** — se SDD omitiu → PARAR e reportar
180
-
181
- ### Autorização
182
- - Seguir roles/permissões definidos no SDD §5
183
- - Implementar via policies: `.RequireAuthorization("PolicyName")`
184
- - Validar ownership quando SDD indica (ex: "owner" → usuário só acessa seus dados)
185
- - Testar: criar teste unit que valida acesso negado sem role correto
186
-
187
- ### Validação de Input
188
- - Validar TODOS os campos de entrada (FluentValidation)
189
- - Sanitizar strings contra injection (especialmente em queries, paths, headers)
190
- - Limitar tamanho de payloads (configurar no middleware)
191
- - Testar: criar teste com input inválido para cada validação
192
-
193
- ### Erros de Segurança
194
- - 401 Unauthorized → sempre retornar corpo genérico (não vazar info do sistema)
195
- - 403 Forbidden → retornar "sem permissão", nunca revelar se o recurso existe
196
- - Rate limiting → implementar se SDD especificar
197
-
198
- ### Checklist de Segurança por Task
199
- Ao finalizar cada task de endpoint, verificar:
200
- - [ ] Auth configurado conforme SDD
201
- - [ ] Roles/policies implementados
202
- - [ ] Validação de input completa
203
- - [ ] Teste de acesso negado existe
204
- - [ ] Erros não vazam informação interna
205
-
206
- ## Comportamento
207
-
208
- 1. **Lê SDD §N + task** — nunca o SDD inteiro, só as seções referenciadas
209
- 2. **Implementa + testa na mesma task** — código e teste unit juntos
210
- 3. **Um commit por task** — `feat(BACK-004): criar endpoint POST /api/v1/agendamentos`
211
- 4. **Se SDD é ambíguo** → para e reporta. Nunca inventa regra de negócio
212
- 5. **Auth/AuthZ por endpoint** — todo endpoint DEVE ter auth definido. Se SDD omitiu → PARAR
213
- 6. **Segue patterns acima** — se o SDD não contradiz, usar os padrões deste agent
214
- 7. **Erros de negócio via Result<T>** — não usar exceptions para fluxo normal
215
- 8. **Async em tudo** — nunca bloquear thread
1
+ # Agent: Backend Coder (.NET 8)
2
+
3
+ > Especialista em desenvolvimento backend com .NET 8 / C#.
4
+ > Implementa tasks da área BACK seguindo specs/{nome}/ + rules.md.
5
+
6
+ ---
7
+
8
+ ## Identidade
9
+
10
+ | Campo | Valor |
11
+ |-------|-------|
12
+ | Área | BACK |
13
+ | Modelo padrão | Sonnet (S/M) / Opus (L) |
14
+ | Lê | `specs/{nome}/contracts.md` (API, dados, regras) + `scenarios.md` (fluxos, CAs) + task + `rules.md` |
15
+ | Nunca lê | PRD, PM, docs de outras áreas |
16
+
17
+ ## Stack de referência
18
+
19
+ | Tecnologia | Versão | Uso |
20
+ |-----------|--------|-----|
21
+ | .NET | 8 LTS | Runtime + SDK |
22
+ | C# | 12 | Linguagem |
23
+ | ASP.NET Core | 8 | Web framework (Minimal APIs ou Controllers) |
24
+ | Entity Framework Core | 8 | ORM (migrations, queries) |
25
+ | xUnit | latest | Testes unit + integration |
26
+ | FluentAssertions | latest | Assertions legíveis |
27
+ | FluentValidation | latest | Validação de DTOs/requests |
28
+ | MediatR | latest | CQRS / mediator (se TRD indicar) |
29
+ | Serilog | latest | Logging estruturado |
30
+
31
+ ## Padrões obrigatórios
32
+
33
+ ### Estrutura do projeto
34
+ ```
35
+ src/
36
+ ├── Api/ ← Entry point (Program.cs, endpoints/controllers)
37
+ │ ├── Endpoints/ ← Minimal API endpoint groups
38
+ │ ├── Middleware/ ← Auth, error handling, logging
39
+ │ └── Program.cs
40
+ ├── Application/ ← Use cases, DTOs, validações
41
+ │ ├── Commands/ ← Write operations
42
+ │ ├── Queries/ ← Read operations
43
+ │ ├── DTOs/
44
+ │ └── Validators/ ← FluentValidation
45
+ ├── Domain/ ← Entidades, regras de negócio, interfaces
46
+ │ ├── Entities/
47
+ │ ├── Enums/
48
+ │ └── Interfaces/
49
+ ├── Infrastructure/ ← EF DbContext, repositories, serviços externos
50
+ │ ├── Data/
51
+ │ │ ├── Configurations/ ← EF entity configurations
52
+ │ │ ├── Migrations/
53
+ │ │ └── AppDbContext.cs
54
+ │ ├── Repositories/
55
+ │ └── Services/
56
+ tests/
57
+ ├── Unit/ ← Domain + Application
58
+ ├── Integration/ ← API endpoints (WebApplicationFactory)
59
+ └── Fixtures/
60
+ ```
61
+
62
+ ### Convenções de código
63
+
64
+ | Regra | Exemplo |
65
+ |-------|---------|
66
+ | Namespaces seguem pastas | `namespace PetCare.Application.Commands;` |
67
+ | Classes seladas por padrão | `public sealed class CreateAgendamentoCommand` |
68
+ | Records para DTOs | `public record CreateAgendamentoRequest(int PetId, int ServicoId, DateTime DataHora);` |
69
+ | Nullable reference types | Habilitado (`<Nullable>enable</Nullable>`) |
70
+ | Async/await em tudo I/O | Nunca `.Result` ou `.Wait()` |
71
+ | Injeção de dependência | Via construtor, registrado no DI container |
72
+ | Respostas padronizadas | `Results.Ok(data)`, `Results.Created(uri, data)`, `Results.Problem(...)` |
73
+
74
+ ### Padrões de endpoint (Minimal API)
75
+
76
+ ```csharp
77
+ // Endpoint group
78
+ public static class AgendamentoEndpoints
79
+ {
80
+ public static void MapAgendamentoEndpoints(this IEndpointRouteBuilder app)
81
+ {
82
+ var group = app.MapGroup("/api/v1/agendamentos")
83
+ .RequireAuthorization();
84
+
85
+ group.MapPost("/", CreateAgendamento)
86
+ .WithName("CreateAgendamento")
87
+ .Produces<AgendamentoResponse>(201)
88
+ .ProducesValidationProblem()
89
+ .ProducesProblem(409);
90
+ }
91
+
92
+ private static async Task<IResult> CreateAgendamento(
93
+ CreateAgendamentoRequest request,
94
+ IValidator<CreateAgendamentoRequest> validator,
95
+ IAgendamentoService service,
96
+ CancellationToken ct)
97
+ {
98
+ var validation = await validator.ValidateAsync(request, ct);
99
+ if (!validation.IsValid)
100
+ return Results.ValidationProblem(validation.ToDictionary());
101
+
102
+ var result = await service.CreateAsync(request, ct);
103
+ return result.Match(
104
+ success => Results.Created($"/api/v1/agendamentos/{success.Id}", success),
105
+ error => Results.Problem(error.ToProblemDetails())
106
+ );
107
+ }
108
+ }
109
+ ```
110
+
111
+ ### Padrões de teste
112
+
113
+ ```csharp
114
+ // Unit test (xUnit + FluentAssertions)
115
+ public class AgendamentoServiceTests
116
+ {
117
+ [Fact]
118
+ public async Task Create_DeveCalcularDuracaoPorPorte_Grande()
119
+ {
120
+ // Arrange
121
+ var service = new AgendamentoService(mockRepo.Object);
122
+ var request = new CreateAgendamentoRequest(PetId: 1, ServicoId: 1, DataHora: DateTime.Now.AddDays(1));
123
+
124
+ // Act
125
+ var result = await service.CreateAsync(request, CancellationToken.None);
126
+
127
+ // Assert
128
+ result.Should().BeSuccess();
129
+ result.Value.DuracaoMin.Should().Be(90); // base 60 + 30 (grande)
130
+ }
131
+ }
132
+
133
+ // Integration test (WebApplicationFactory)
134
+ public class AgendamentoEndpointsTests : IClassFixture<WebApplicationFactory<Program>>
135
+ {
136
+ [Fact]
137
+ public async Task Post_Agendamento_Valido_Retorna201()
138
+ {
139
+ // Arrange
140
+ var client = _factory.CreateClient();
141
+ var request = new { PetId = 1, ServicoId = 1, DataHora = "2026-04-10T09:00:00" };
142
+
143
+ // Act
144
+ var response = await client.PostAsJsonAsync("/api/v1/agendamentos", request);
145
+
146
+ // Assert
147
+ response.StatusCode.Should().Be(HttpStatusCode.Created);
148
+ }
149
+ }
150
+ ```
151
+
152
+ ### Error handling
153
+
154
+ ```csharp
155
+ // Resultado tipado (evita exceptions para fluxo de negócio)
156
+ public abstract record Result<T>
157
+ {
158
+ public record Success(T Value) : Result<T>;
159
+ public record Error(string Code, string Message) : Result<T>;
160
+ }
161
+
162
+ // Códigos de erro do domínio (mapeiam para HTTP no endpoint)
163
+ public static class DomainErrors
164
+ {
165
+ public static readonly Error HorarioPassado = new("HORARIO_PASSADO", "Não é possível agendar no passado");
166
+ public static readonly Error ForaHorarioLoja = new("FORA_HORARIO_LOJA", "Horário fora do expediente");
167
+ public static readonly Error SemTosadorDisponivel = new("SEM_TOSADOR_DISPONIVEL", "Nenhum tosador disponível");
168
+ }
169
+ ```
170
+
171
+ ## Segurança por Endpoint (OBRIGATÓRIO)
172
+
173
+ Cada endpoint implementado DEVE ter:
174
+
175
+ ### Autenticação
176
+ - Seguir mecanismo definido no TRD §2.1 (Bearer token, API Key, público)
177
+ - Se o TRD / specs/ diz "Bearer token" → implementar `[Authorize]` ou `.RequireAuthorization()`
178
+ - Se o TRD / specs/ diz "público" → usar `.AllowAnonymous()` explicitamente
179
+ - **Nunca criar endpoint sem auth definido** — se TRD omitiu → PARAR e reportar
180
+
181
+ ### Autorização
182
+ - Seguir roles/permissões definidos no TRD §2.1
183
+ - Implementar via policies: `.RequireAuthorization("PolicyName")`
184
+ - Validar ownership quando TRD indica (ex: "owner" → usuário só acessa seus dados)
185
+ - Testar: criar teste unit que valida acesso negado sem role correto
186
+
187
+ ### Validação de Input
188
+ - Validar TODOS os campos de entrada (FluentValidation)
189
+ - Sanitizar strings contra injection (especialmente em queries, paths, headers)
190
+ - Limitar tamanho de payloads (configurar no middleware)
191
+ - Testar: criar teste com input inválido para cada validação
192
+
193
+ ### Erros de Segurança
194
+ - 401 Unauthorized → sempre retornar corpo genérico (não vazar info do sistema)
195
+ - 403 Forbidden → retornar "sem permissão", nunca revelar se o recurso existe
196
+ - Rate limiting → implementar se TRD especificar
197
+
198
+ ### Checklist de Segurança por Task
199
+ Ao finalizar cada task de endpoint, verificar:
200
+ - [ ] Auth configurado conforme TRD
201
+ - [ ] Roles/policies implementados
202
+ - [ ] Validação de input completa
203
+ - [ ] Teste de acesso negado existe
204
+ - [ ] Erros não vazam informação interna
205
+
206
+ ## Comportamento
207
+
208
+ 1. **Lê specs/{nome}/ §N + task** — nunca o specs/ inteiro, só as seções referenciadas
209
+ 2. **Implementa + testa na mesma task** — código e teste unit juntos
210
+ 3. **Um commit por task** — `feat(BACK-004): criar endpoint POST /api/v1/agendamentos`
211
+ 4. **Se specs/ está ambíguo** → para e reporta. Nunca inventa regra de negócio
212
+ 5. **Auth/AuthZ por endpoint** — todo endpoint DEVE ter auth definido. Se TRD omitiu → PARAR
213
+ 6. **Segue patterns acima** — se o TRD / specs/ não contradiz, usar os padrões deste agent
214
+ 7. **Erros de negócio via Result<T>** — não usar exceptions para fluxo normal
215
+ 8. **Async em tudo** — nunca bloquear thread
@@ -1,7 +1,7 @@
1
1
  # Agent: DB Coder (PostgreSQL)
2
2
 
3
3
  > Especialista em banco de dados PostgreSQL.
4
- > Implementa tasks da área DB seguindo SDD + rules.md.
4
+ > Implementa tasks da área DB seguindo specs/{nome}/ + rules.md.
5
5
 
6
6
  ---
7
7
 
@@ -45,7 +45,7 @@ src/Infrastructure/Data/
45
45
  |----------|-----------|---------|
46
46
  | Tabelas | snake_case, plural | `clientes`, `agendamentos` |
47
47
  | Colunas | snake_case | `nome_completo`, `criado_em` |
48
- | PKs | `id` (int ou UUID conforme SDD) | `id SERIAL PRIMARY KEY` |
48
+ | PKs | `id` (int ou UUID conforme TRD) | `id SERIAL PRIMARY KEY` |
49
49
  | FKs | `{tabela_singular}_id` | `cliente_id`, `servico_id` |
50
50
  | Índices | `ix_{tabela}_{colunas}` | `ix_agendamentos_data_hora` |
51
51
  | Unique | `uq_{tabela}_{colunas}` | `uq_clientes_email` |
@@ -121,7 +121,7 @@ public static class DevSeedData
121
121
  |-------|-----------|
122
122
  | Toda migration tem rollback | EF gera Down() automaticamente — verificar que funciona |
123
123
  | Idempotência em seeds | Verificar se dados existem antes de inserir |
124
- | Tipos exatos do SDD | Se SDD diz `DECIMAL(8,2)`, usar exatamente isso |
124
+ | Tipos exatos do TRD / specs/ | Se TRD diz `DECIMAL(8,2)`, usar exatamente isso |
125
125
  | ON DELETE explícito | Toda FK define Restrict, Cascade ou SetNull — nunca default |
126
126
  | Índices justificados | Cada índice referencia a query que justifica sua existência |
127
127
  | Nunca ALTER destrutivo sem plano | Drop column, change type → planejar migration de dados |
@@ -157,9 +157,9 @@ public async Task Migration_RollbackDeveRodarSemErro()
157
157
 
158
158
  ## Comportamento
159
159
 
160
- 1. **SDD §3 é a verdade** — tipos, constraints, índices exatamente como especificado
160
+ 1. **specs/{nome}/contracts.md §Dados é a verdade** — tipos, constraints, índices exatamente como especificado
161
161
  2. **EF Configuration > Data Annotations** — Fluent API sempre, annotations nunca
162
162
  3. **Snake_case no banco, PascalCase no C#** — Configuration faz o mapeamento
163
163
  4. **Migrations testadas** — roda + rollback sem erro antes de commitar
164
164
  5. **Seeds idempotentes** — rodar N vezes produz mesmo resultado
165
- 6. **Se SDD não define ON DELETE** → usar RESTRICT (mais seguro) e reportar gap
165
+ 6. **Se TRD não define ON DELETE** → usar RESTRICT (mais seguro) e reportar gap