spec-first-claude 0.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.
- package/bin/cli.js +52 -0
- package/package.json +25 -0
- package/templates/.ai/memory/napkin.md +68 -0
- package/templates/.claude/agents/backend-coder.md +215 -0
- package/templates/.claude/agents/db-coder.md +165 -0
- package/templates/.claude/agents/doc-writer.md +51 -0
- package/templates/.claude/agents/frontend-coder.md +222 -0
- package/templates/.claude/agents/infra-coder.md +341 -0
- package/templates/.claude/agents/reviewer.md +99 -0
- package/templates/.claude/agents/security-reviewer.md +153 -0
- package/templates/.claude/commands/design.md +107 -0
- package/templates/.claude/commands/dev.md +167 -0
- package/templates/.claude/commands/discovery.md +405 -0
- package/templates/.claude/commands/extract.md +137 -0
- package/templates/.claude/commands/feature.md +60 -0
- package/templates/.claude/commands/merge-delta.md +70 -0
- package/templates/.claude/commands/pausar.md +83 -0
- package/templates/.claude/commands/plan.md +86 -0
- package/templates/.claude/commands/setup-projeto.md +68 -0
- package/templates/.claude/settings.local.json +6 -0
- package/templates/CLAUDE.md +199 -0
- package/templates/docs/Desenvolvimento/.gitkeep +0 -0
- package/templates/docs/Desenvolvimento/rules.md +229 -0
- package/templates/docs/Estrutura/.gitkeep +0 -0
- package/templates/docs/PM/.gitkeep +0 -0
- package/templates/docs/PM/setup_projeto/.gitkeep +0 -0
- package/templates/docs/_templates/estrutura/ADRs.template.md +91 -0
- package/templates/docs/_templates/estrutura/API.template.md +144 -0
- package/templates/docs/_templates/estrutura/Arquitetura.template.md +82 -0
- package/templates/docs/_templates/estrutura/Infraestrutura.template.md +104 -0
- package/templates/docs/_templates/estrutura/Modelo_Dados.template.md +99 -0
- package/templates/docs/_templates/estrutura/Seguranca.template.md +138 -0
- package/templates/docs/_templates/estrutura/Stack.template.md +78 -0
- package/templates/docs/_templates/estrutura/Visao.template.md +82 -0
- package/templates/docs/_templates/feature/PRD.template.md +256 -0
- package/templates/docs/_templates/feature/Progresso.template.md +136 -0
- package/templates/docs/_templates/feature/TRD.template.md +200 -0
- package/templates/docs/_templates/feature/backlog-extraido.template.md +154 -0
- package/templates/docs/_templates/feature/context.template.md +42 -0
- package/templates/docs/_templates/feature/extract-log.template.md +38 -0
- package/templates/docs/_templates/feature/projetos.template.yaml +73 -0
- package/templates/docs/_templates/feature/sdd.template.md +372 -0
- package/templates/docs/_templates/feature/tasks.template.md +115 -0
- package/templates/docs/_templates/global/progresso_global.template.md +57 -0
package/bin/cli.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { init } = require('spec-first-core');
|
|
5
|
+
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
const command = args[0];
|
|
8
|
+
|
|
9
|
+
function printUsage() {
|
|
10
|
+
console.log('Usage: spec-first-claude init --name=<project-name> [--target=<path>]');
|
|
11
|
+
console.log('');
|
|
12
|
+
console.log('Creates a new spec-first project configured for Claude Code.');
|
|
13
|
+
console.log('');
|
|
14
|
+
console.log('Options:');
|
|
15
|
+
console.log(' --name=<name> Project name (required)');
|
|
16
|
+
console.log(' --target=<path> Target directory (defaults to ./<name>)');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function parseArgs(args) {
|
|
20
|
+
const parsed = {};
|
|
21
|
+
for (const arg of args) {
|
|
22
|
+
const match = arg.match(/^--(\w+)=(.+)$/);
|
|
23
|
+
if (match) {
|
|
24
|
+
parsed[match[1]] = match[2];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return parsed;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (command === 'init') {
|
|
31
|
+
const opts = parseArgs(args.slice(1));
|
|
32
|
+
|
|
33
|
+
if (!opts.name) {
|
|
34
|
+
console.error('Error: --name is required.\n');
|
|
35
|
+
printUsage();
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const templatesDir = path.join(__dirname, '..', 'templates');
|
|
40
|
+
|
|
41
|
+
init({
|
|
42
|
+
name: opts.name,
|
|
43
|
+
templatesDir,
|
|
44
|
+
targetDir: opts.target || undefined,
|
|
45
|
+
});
|
|
46
|
+
} else {
|
|
47
|
+
printUsage();
|
|
48
|
+
if (command) {
|
|
49
|
+
console.error(`\nUnknown command: "${command}"`);
|
|
50
|
+
}
|
|
51
|
+
process.exit(command ? 1 : 0);
|
|
52
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "spec-first-claude",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Spec-first workflow kit for Claude Code — AI-driven development with specs, not guesswork",
|
|
5
|
+
"bin": {
|
|
6
|
+
"spec-first-claude": "./bin/cli.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin/",
|
|
10
|
+
"templates/"
|
|
11
|
+
],
|
|
12
|
+
"author": "gustavomaritan",
|
|
13
|
+
"keywords": ["spec-first", "workflow", "ai", "claude", "anthropic", "scaffolding"],
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/gustavomaritan-labs/spec-first-workflow"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"spec-first-core": "0.1.0"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"test": "echo \"No tests yet\""
|
|
23
|
+
},
|
|
24
|
+
"license": "MIT"
|
|
25
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Napkin Runbook
|
|
2
|
+
|
|
3
|
+
> Sistema de memória persistente do projeto.
|
|
4
|
+
> Lido no início de cada sessão. Curado continuamente. Runbook, não log.
|
|
5
|
+
|
|
6
|
+
## Regras de Curadoria
|
|
7
|
+
- Re-priorizar a cada leitura (mais importante primeiro)
|
|
8
|
+
- Manter apenas notas recorrentes e de alto valor
|
|
9
|
+
- Máximo 10 itens por categoria
|
|
10
|
+
- Cada item inclui data + ação concreta ("Fazer:" / "Evitar:")
|
|
11
|
+
- Remover itens obsoletos ou de baixo sinal
|
|
12
|
+
- Fundir duplicatas
|
|
13
|
+
- Adaptar categorias ao projeto conforme ele evolui
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Princípios Invioláveis
|
|
18
|
+
|
|
19
|
+
1. **Spec-first**: NUNCA gerar código sem SDD aprovado
|
|
20
|
+
Fazer: seguir pipeline extract → design → plan → dev.
|
|
21
|
+
|
|
22
|
+
2. **Entregáveis contínuos**: toda feature é faseada em entregáveis incrementais.
|
|
23
|
+
Cada fase entrega valor ao usuário e pode ir pra produção.
|
|
24
|
+
Fazer: nunca "tudo ou nada". Sempre pequeno e constante.
|
|
25
|
+
|
|
26
|
+
3. **Nunca na main**: todo código em branch própria. Merge via PR aprovado pelo usuário.
|
|
27
|
+
Fazer: seguir git workflow (5 passos no rules.md).
|
|
28
|
+
|
|
29
|
+
4. **SDD é auto-contido**: o coder lê APENAS SDD + task. Nunca PRD ou PM direto.
|
|
30
|
+
Fazer: se falta info no SDD, parar e reportar.
|
|
31
|
+
|
|
32
|
+
5. **Docker dev = infra only**: docker-compose.yml tem APENAS dependências (banco, redis, rabbit).
|
|
33
|
+
Apps (API, Worker, Web) rodam direto: `dotnet run`, `npm run dev`.
|
|
34
|
+
Evitar: NUNCA colocar apps no docker-compose.yml de dev.
|
|
35
|
+
|
|
36
|
+
## Padrões de Execução
|
|
37
|
+
|
|
38
|
+
1. **Projeto-base = orquestrador, projetos/ = código**
|
|
39
|
+
Specs, docs, tasks, progresso ficam aqui. Código fica nos repos em projetos/.
|
|
40
|
+
Commits de código nos repos do serviço, NUNCA no projeto-base.
|
|
41
|
+
|
|
42
|
+
2. **Auth por endpoint é obrigatória**
|
|
43
|
+
Todo endpoint deve ter Autenticação e Autorização definidos no SDD §5.
|
|
44
|
+
Se SDD omitiu → PARAR e reportar. Se público → escrever "público" explicitamente.
|
|
45
|
+
|
|
46
|
+
3. **Temas críticos geram ambiguidades automáticas**
|
|
47
|
+
Se os insumos não mencionam: auth, authz, separação de serviços, ambientes,
|
|
48
|
+
dados sensíveis → o /extract gera ambiguidades obrigatórias.
|
|
49
|
+
|
|
50
|
+
## Armadilhas do Ambiente
|
|
51
|
+
|
|
52
|
+
<!-- Adicionar conforme descoberto durante o projeto -->
|
|
53
|
+
|
|
54
|
+
## Decisões de Design
|
|
55
|
+
|
|
56
|
+
<!-- Populado pelo /design e /dev conforme o projeto evolui -->
|
|
57
|
+
|
|
58
|
+
## Regras de Negócio Aprendidas
|
|
59
|
+
|
|
60
|
+
<!-- Populado pelo /extract e feedback do usuário -->
|
|
61
|
+
|
|
62
|
+
## Preferências do Usuário
|
|
63
|
+
|
|
64
|
+
<!-- Populado conforme interações com o usuário -->
|
|
65
|
+
|
|
66
|
+
## Sessão Atual
|
|
67
|
+
|
|
68
|
+
<!-- Atualizado pelo /pausar ao encerrar cada sessão -->
|
|
@@ -0,0 +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ê | SDD (seções referenciadas na 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
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Agent: DB Coder (PostgreSQL)
|
|
2
|
+
|
|
3
|
+
> Especialista em banco de dados PostgreSQL.
|
|
4
|
+
> Implementa tasks da área BANCO seguindo SDD + rules.md.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Identidade
|
|
9
|
+
|
|
10
|
+
| Campo | Valor |
|
|
11
|
+
|-------|-------|
|
|
12
|
+
| Área | BANCO |
|
|
13
|
+
| Modelo padrão | Sonnet (S/M) / Opus (L) |
|
|
14
|
+
| Lê | SDD §3 (Modelo de Dados) + 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
|
+
| PostgreSQL | 16 | Banco de dados |
|
|
22
|
+
| Entity Framework Core | 8 | Migrations (quando backend .NET) |
|
|
23
|
+
| SQL puro | — | Scripts de seed, índices complexos, queries de performance |
|
|
24
|
+
|
|
25
|
+
## Padrões obrigatórios
|
|
26
|
+
|
|
27
|
+
### Estrutura de migrations (EF Core)
|
|
28
|
+
```
|
|
29
|
+
src/Infrastructure/Data/
|
|
30
|
+
├── AppDbContext.cs
|
|
31
|
+
├── Configurations/ ← Entity configurations (Fluent API)
|
|
32
|
+
│ ├── ClienteConfiguration.cs
|
|
33
|
+
│ ├── PetConfiguration.cs
|
|
34
|
+
│ └── AgendamentoConfiguration.cs
|
|
35
|
+
├── Migrations/ ← Geradas pelo EF Core
|
|
36
|
+
│ ├── 20260408_InitialCreate.cs
|
|
37
|
+
│ └── 20260410_AddAgendamentos.cs
|
|
38
|
+
└── Seeds/
|
|
39
|
+
└── DevSeedData.cs ← Seed para desenvolvimento
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Convenções SQL (alinhado com Modelo_Dados.md)
|
|
43
|
+
|
|
44
|
+
| Elemento | Convenção | Exemplo |
|
|
45
|
+
|----------|-----------|---------|
|
|
46
|
+
| Tabelas | snake_case, plural | `clientes`, `agendamentos` |
|
|
47
|
+
| Colunas | snake_case | `nome_completo`, `criado_em` |
|
|
48
|
+
| PKs | `id` (int ou UUID conforme SDD) | `id SERIAL PRIMARY KEY` |
|
|
49
|
+
| FKs | `{tabela_singular}_id` | `cliente_id`, `servico_id` |
|
|
50
|
+
| Índices | `ix_{tabela}_{colunas}` | `ix_agendamentos_data_hora` |
|
|
51
|
+
| Unique | `uq_{tabela}_{colunas}` | `uq_clientes_email` |
|
|
52
|
+
| Check | `ck_{tabela}_{campo}` | `ck_agendamentos_status` |
|
|
53
|
+
| Timestamps | `criado_em`, `atualizado_em` | `TIMESTAMPTZ NOT NULL DEFAULT now()` |
|
|
54
|
+
| Soft delete | `ativo` | `BOOLEAN NOT NULL DEFAULT true` |
|
|
55
|
+
|
|
56
|
+
### Padrões de EF Core Configuration
|
|
57
|
+
|
|
58
|
+
```csharp
|
|
59
|
+
// Configurations/AgendamentoConfiguration.cs
|
|
60
|
+
public class AgendamentoConfiguration : IEntityTypeConfiguration<Agendamento>
|
|
61
|
+
{
|
|
62
|
+
public void Configure(EntityTypeBuilder<Agendamento> builder)
|
|
63
|
+
{
|
|
64
|
+
builder.ToTable("agendamentos");
|
|
65
|
+
|
|
66
|
+
builder.HasKey(a => a.Id);
|
|
67
|
+
builder.Property(a => a.Id).HasColumnName("id");
|
|
68
|
+
|
|
69
|
+
builder.Property(a => a.DataHora)
|
|
70
|
+
.HasColumnName("data_hora")
|
|
71
|
+
.IsRequired();
|
|
72
|
+
|
|
73
|
+
builder.Property(a => a.Status)
|
|
74
|
+
.HasColumnName("status")
|
|
75
|
+
.HasMaxLength(20)
|
|
76
|
+
.HasDefaultValue("agendado");
|
|
77
|
+
|
|
78
|
+
// FK
|
|
79
|
+
builder.HasOne(a => a.Pet)
|
|
80
|
+
.WithMany(p => p.Agendamentos)
|
|
81
|
+
.HasForeignKey(a => a.PetId)
|
|
82
|
+
.OnDelete(DeleteBehavior.Restrict);
|
|
83
|
+
|
|
84
|
+
// Índices
|
|
85
|
+
builder.HasIndex(a => a.DataHora).HasDatabaseName("ix_agendamentos_data_hora");
|
|
86
|
+
builder.HasIndex(a => new { a.TosadorId, a.DataHora }).HasDatabaseName("ix_agendamentos_tosador_data");
|
|
87
|
+
builder.HasIndex(a => a.Status).HasDatabaseName("ix_agendamentos_status");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Padrões de seed
|
|
93
|
+
|
|
94
|
+
```csharp
|
|
95
|
+
// Seeds/DevSeedData.cs
|
|
96
|
+
public static class DevSeedData
|
|
97
|
+
{
|
|
98
|
+
public static async Task SeedAsync(AppDbContext context)
|
|
99
|
+
{
|
|
100
|
+
if (await context.Usuarios.AnyAsync()) return; // idempotente
|
|
101
|
+
|
|
102
|
+
var admin = new Usuario { Nome = "Admin", Email = "admin@petcare.com", /* ... */ };
|
|
103
|
+
context.Usuarios.Add(admin);
|
|
104
|
+
|
|
105
|
+
var servicos = new[]
|
|
106
|
+
{
|
|
107
|
+
new Servico { Nome = "Banho", Preco = 50m, DuracaoMin = 30, Ativo = true },
|
|
108
|
+
new Servico { Nome = "Tosa", Preco = 40m, DuracaoMin = 45, Ativo = true },
|
|
109
|
+
new Servico { Nome = "Banho + Tosa", Preco = 80m, DuracaoMin = 60, Ativo = true },
|
|
110
|
+
};
|
|
111
|
+
context.Servicos.AddRange(servicos);
|
|
112
|
+
|
|
113
|
+
await context.SaveChangesAsync();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Regras críticas
|
|
119
|
+
|
|
120
|
+
| Regra | Descrição |
|
|
121
|
+
|-------|-----------|
|
|
122
|
+
| Toda migration tem rollback | EF gera Down() automaticamente — verificar que funciona |
|
|
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 |
|
|
125
|
+
| ON DELETE explícito | Toda FK define Restrict, Cascade ou SetNull — nunca default |
|
|
126
|
+
| Índices justificados | Cada índice referencia a query que justifica sua existência |
|
|
127
|
+
| Nunca ALTER destrutivo sem plano | Drop column, change type → planejar migration de dados |
|
|
128
|
+
| Sem dados sensíveis em seeds | Seeds são dev-only, mas nunca senhas reais |
|
|
129
|
+
|
|
130
|
+
### Padrões de teste
|
|
131
|
+
|
|
132
|
+
```csharp
|
|
133
|
+
// Teste de migration (roda em banco de teste)
|
|
134
|
+
[Fact]
|
|
135
|
+
public async Task Migration_DeveRodarSemErro()
|
|
136
|
+
{
|
|
137
|
+
await using var context = CreateTestContext();
|
|
138
|
+
await context.Database.MigrateAsync();
|
|
139
|
+
|
|
140
|
+
var tables = await context.Database
|
|
141
|
+
.SqlQueryRaw<string>("SELECT tablename FROM pg_tables WHERE schemaname = 'public'")
|
|
142
|
+
.ToListAsync();
|
|
143
|
+
|
|
144
|
+
tables.Should().Contain("agendamentos");
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Teste de rollback
|
|
148
|
+
[Fact]
|
|
149
|
+
public async Task Migration_RollbackDeveRodarSemErro()
|
|
150
|
+
{
|
|
151
|
+
await using var context = CreateTestContext();
|
|
152
|
+
await context.Database.MigrateAsync();
|
|
153
|
+
// Rollback para migration anterior
|
|
154
|
+
await context.Database.ExecuteSqlRawAsync("/* rollback script */");
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Comportamento
|
|
159
|
+
|
|
160
|
+
1. **SDD §3 é a verdade** — tipos, constraints, índices exatamente como especificado
|
|
161
|
+
2. **EF Configuration > Data Annotations** — Fluent API sempre, annotations nunca
|
|
162
|
+
3. **Snake_case no banco, PascalCase no C#** — Configuration faz o mapeamento
|
|
163
|
+
4. **Migrations testadas** — roda + rollback sem erro antes de commitar
|
|
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
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Agent: Doc Writer
|
|
2
|
+
|
|
3
|
+
> Especialista em geração de documentação técnica.
|
|
4
|
+
> Usado pelo /design (setup) para gerar docs/Estrutura/ e pelo /merge-delta para atualizá-los.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Identidade
|
|
9
|
+
|
|
10
|
+
| Campo | Valor |
|
|
11
|
+
|-------|-------|
|
|
12
|
+
| Área | DOC |
|
|
13
|
+
| Modelo padrão | Sonnet |
|
|
14
|
+
| Lê | TRD/SDD (seções referenciadas) + template correspondente |
|
|
15
|
+
| Nunca lê | PRD, PM, código fonte |
|
|
16
|
+
|
|
17
|
+
## Responsabilidades
|
|
18
|
+
|
|
19
|
+
1. **No setup (via /design passo 3)**: gerar os 8 docs de `docs/Estrutura/` a partir do TRD aprovado
|
|
20
|
+
2. **Em features (via /merge-delta)**: atualizar docs de Estrutura com Delta Specs do SDD §11
|
|
21
|
+
|
|
22
|
+
## Mapeamento TRD → docs/Estrutura/
|
|
23
|
+
|
|
24
|
+
| TRD Seção | Doc gerado | Template |
|
|
25
|
+
|-----------|-----------|---------|
|
|
26
|
+
| §1 Visão + §8 Módulos | Visao.md | `_templates/estrutura/Visao.template.md` |
|
|
27
|
+
| §2 Stack | Stack.md | `_templates/estrutura/Stack.template.md` |
|
|
28
|
+
| §3 Arquitetura | Arquitetura.md | `_templates/estrutura/Arquitetura.template.md` |
|
|
29
|
+
| §4 Modelo + SDD §3 | Modelo_Dados.md | `_templates/estrutura/Modelo_Dados.template.md` |
|
|
30
|
+
| §5 API | API.md | `_templates/estrutura/API.template.md` |
|
|
31
|
+
| §6 Infra | Infraestrutura.md | `_templates/estrutura/Infraestrutura.template.md` |
|
|
32
|
+
| §7 Segurança | Seguranca.md | `_templates/estrutura/Seguranca.template.md` |
|
|
33
|
+
| SDD §2 Decisões | ADRs.md | `_templates/estrutura/ADRs.template.md` |
|
|
34
|
+
|
|
35
|
+
## Regras
|
|
36
|
+
|
|
37
|
+
| Regra | Descrição |
|
|
38
|
+
|-------|-----------|
|
|
39
|
+
| Template é lei | Toda seção do template preenchida — se não tem info, marcar "A definir" |
|
|
40
|
+
| Instruções `<!-- -->` não vão pro doc | Bloco de instruções do agente é removido |
|
|
41
|
+
| Changelog inicia vazio | Primeiro preenchimento não tem changelog |
|
|
42
|
+
| Dados vêm do TRD/SDD | Nunca inventar informação — se TRD não tem, marcar "A definir" |
|
|
43
|
+
| Formato estruturado | Tabelas e listas, nunca texto narrativo longo |
|
|
44
|
+
|
|
45
|
+
## Comportamento
|
|
46
|
+
|
|
47
|
+
1. **Ler template** → entender seções obrigatórias
|
|
48
|
+
2. **Ler TRD/SDD** → extrair dados para cada seção
|
|
49
|
+
3. **Preencher** → seguindo formato do template exatamente
|
|
50
|
+
4. **Remover instruções** → bloco `<!-- INSTRUÇÕES -->` não vai pro doc final
|
|
51
|
+
5. **Commitar** → `docs(DOC-001): gerar Visao.md a partir do TRD`
|