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.
- package/package.json +1 -1
- package/templates/.claude/CHANGELOG.md +94 -0
- package/templates/.claude/adapters/SETUP.md +3 -3
- package/templates/.claude/adapters/filesystem.md +2 -2
- package/templates/.claude/adapters/naming.md +1 -1
- package/templates/.claude/adapters/registry.md +2 -2
- package/templates/.claude/agents/backend-coder.md +215 -215
- package/templates/.claude/agents/db-coder.md +5 -5
- package/templates/.claude/agents/doc-writer.md +32 -19
- package/templates/.claude/agents/frontend-coder.md +222 -222
- package/templates/.claude/agents/infra-coder.md +341 -341
- package/templates/.claude/agents/reviewer.md +99 -99
- package/templates/.claude/agents/security-reviewer.md +5 -5
- package/templates/.claude/commands/design.md +101 -157
- package/templates/.claude/commands/dev.md +24 -19
- package/templates/.claude/commands/extract.md +118 -143
- package/templates/.claude/commands/load.md +5 -5
- package/templates/.claude/commands/mcp.md +1 -1
- package/templates/.claude/commands/merge-docs.md +109 -58
- package/templates/.claude/commands/plan.md +80 -57
- package/templates/.claude/commands/publish.md +4 -4
- package/templates/.claude/commands/sfw-start.md +101 -55
- package/templates/.claude/scripts/bootstrap-confluence.js +101 -35
- package/templates/.claude/templates/feature/PRD.template.md +279 -286
- package/templates/.claude/templates/feature/TRD.template.md +358 -0
- package/templates/.claude/templates/feature/context.template.md +54 -13
- package/templates/.claude/templates/feature/extract-log.template.md +19 -9
- package/templates/.claude/templates/specs/brief.template.md +11 -4
- package/templates/.claude/templates/specs/contracts.template.md +11 -5
- package/templates/.claude/templates/specs/scenarios.template.md +13 -5
- package/templates/.claude/templates/specs/tasks.template.md +11 -9
- package/templates/CLAUDE.md +43 -39
- package/templates/sfw.config.yml.example +4 -4
- package/templates/.claude/templates/feature/backlog-extraido.template.md +0 -156
- package/templates/.claude/templates/feature/sdd.template.md +0 -559
package/package.json
CHANGED
|
@@ -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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
177
|
-
- Se o
|
|
178
|
-
- Se o
|
|
179
|
-
- **Nunca criar endpoint sem auth definido** — se
|
|
180
|
-
|
|
181
|
-
### Autorização
|
|
182
|
-
- Seguir roles/permissões definidos no
|
|
183
|
-
- Implementar via policies: `.RequireAuthorization("PolicyName")`
|
|
184
|
-
- Validar ownership quando
|
|
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
|
|
197
|
-
|
|
198
|
-
### Checklist de Segurança por Task
|
|
199
|
-
Ao finalizar cada task de endpoint, verificar:
|
|
200
|
-
- [ ] Auth configurado conforme
|
|
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ê
|
|
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
|
|
212
|
-
5. **Auth/AuthZ por endpoint** — todo endpoint DEVE ter auth definido. Se
|
|
213
|
-
6. **Segue patterns acima** — se o
|
|
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
|
|
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
|
|
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
|
|
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. **
|
|
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
|
|
165
|
+
6. **Se TRD não define ON DELETE** → usar RESTRICT (mais seguro) e reportar gap
|