@seedgrid/ai-toolkit 0.3.4 → 0.4.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/dist/adapters/shared.js +1 -1
- package/docs/ai/plans/2026-10-16-pulse-result-timeout.md +77 -0
- package/docs/formato.md +1 -1
- package/package.json +1 -1
- package/seedgridKB/SeedGrid/Toolkit/agents/arquiteto.md +3 -2
- package/seedgridKB/SeedGrid/Toolkit/agents/revisor.md +1 -0
- package/seedgridKB/SeedGrid/Toolkit/rules/backend-java-seedgrid.md +2 -2
- package/seedgridKB/SeedGrid/Toolkit/rules/coordenacao.md +1 -1
- package/seedgridKB/SeedGrid/Toolkit/rules/documentos.md +14 -10
- package/seedgridKB/SeedGrid/Toolkit/skills/sg-adr/SKILL.md +33 -0
- package/seedgridKB/SeedGrid/Toolkit/skills/sg-adr/scripts/adr-index.mjs +87 -0
- package/seedgridKB/SeedGrid/Toolkit/skills/sg-crud-back/SKILL.md +2 -2
- package/seedgridKB/SeedGrid/Toolkit/skills/sg-plan-goal/SKILL.md +36 -0
- package/seedgridKB/SeedGrid/Toolkit/templates/decisao-arquitetura.md +21 -7
- package/seedgridKB/manifest.json +18 -1
package/dist/adapters/shared.js
CHANGED
|
@@ -135,7 +135,7 @@ export function agentsMdBlock(bundle, targets, mode) {
|
|
|
135
135
|
'Cada pasta de `.ai/` tem um `index.md` com o que há nela: comece por ele e leia só o que a tarefa pedir.',
|
|
136
136
|
'',
|
|
137
137
|
'- Regras: `.ai/rules/`. Antes de tarefas não triviais, leia `coordenacao.md`.',
|
|
138
|
-
'- **Documentos que você gerar** (planos, handoff, decisões, especificações, relatórios) são salvos em
|
|
138
|
+
'- **Documentos que você gerar** (planos, handoff, decisões, especificações, relatórios) são salvos em `docs/ai/<tipo>/` (planos em `docs/ai/plans/`, decisões/ADR em `docs/ai/decisions/`), nunca em outro lugar do projeto; veja `.ai/rules/documentos.md`.',
|
|
139
139
|
...standardsLines(bundle),
|
|
140
140
|
'- Papéis: `.ai/agents/`.',
|
|
141
141
|
`- Skills: \`.ai/skills/\` (${bundle.skills.length}). Antes de uma tarefa que combine com a descrição, leia o \`SKILL.md\` indicado no índice.`,
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
---
|
|
2
|
+
titulo: "Plano: prazo de execução (resultTimeoutSeconds) no Pulse"
|
|
3
|
+
status: concluido
|
|
4
|
+
data: 2026-10-16
|
|
5
|
+
autor: sessão seedgrid-ai
|
|
6
|
+
modulos: [pulse]
|
|
7
|
+
adrs: []
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Plano: prazo de execução (`resultTimeoutSeconds`) no Pulse
|
|
11
|
+
|
|
12
|
+
Status: **IMPLEMENTADO** no Pulse 0.3.0 (commit `9dcbdad`) e documentado na KB. Diferenças em relação a este plano: padrão `0` (sem prazo), coluna `NOT NULL DEFAULT 0` (e não nullable), log de tentativa `execution_retryable` com detalhe `result_timeout` (sem outcome próprio) e `result` tardio ignorado só enquanto a mensagem está `pending` com `lastExecutionError = "result_timeout"`. Texto original abaixo, mantido como histórico.
|
|
13
|
+
|
|
14
|
+
Status original: proposta, não implementada. Repositório alvo: `seedgrid-products/seedgrid-pulse` (analisado em `862d936`, versão 0.2.1). Quem implementa: sessão `correcao-pulse`, com aprovação do dono desse repositório (commit/push dispara deploy). A KB (`seedgridKB/SeedGrid/Produtos/Pulse`) descreve o item como **planejado** até existir o commit.
|
|
15
|
+
|
|
16
|
+
## 1. Problema
|
|
17
|
+
|
|
18
|
+
Tarefa `processed` que o consumidor recebeu (`ack` ⇒ `delivered`) e nunca respondeu com `result` fica `delivered` para sempre:
|
|
19
|
+
|
|
20
|
+
- `list_due_messages` (`delivery/repo.rs`, versão em memória e SQL do Postgres) só considera `status = 'pending'`, então o TTL não a expira.
|
|
21
|
+
- A DLQ só recebe por `result` (`permanent`, ou `retryable` com `attemptCount > maxAttempts`), que nunca chegou.
|
|
22
|
+
- Saída hoje: descarte manual em Pendências (`POST /backlog/discard`).
|
|
23
|
+
|
|
24
|
+
## 2. Comportamento desejado
|
|
25
|
+
|
|
26
|
+
Novo prazo, só para `deliveryMode: processed`, contado a partir de `delivered_at` (momento do `ack`):
|
|
27
|
+
|
|
28
|
+
1. Se passar `resultTimeoutSeconds` sem `result`, o servidor trata como `result: retryable` com `reason = "result_timeout"`:
|
|
29
|
+
- incrementa `attempt_count` e agenda a próxima tentativa com o backoff da `deliveryPolicy` (volta a `pending`);
|
|
30
|
+
- se `attempt_count > max_attempts` ⇒ `dead_letter` com `last_execution_error = "result_timeout"`.
|
|
31
|
+
2. Registrar no log de tentativas (`execution_retryable` com detail `result_timeout`, ver D5) e numa métrica (`delivery_result_timeout_total`).
|
|
32
|
+
3. `notification` e `acknowledged` não são afetados (não há `result`).
|
|
33
|
+
|
|
34
|
+
## 3. Mudanças (por camada)
|
|
35
|
+
|
|
36
|
+
**Protocolo e modelo**
|
|
37
|
+
- `src/models.rs`: `DeliveryPolicyInput.result_timeout_seconds: Option<u64>` (`resultTimeoutSeconds`); `validate()` rejeita `0` (422 `INVALID_MESSAGE_REQUEST`, mesma regra dos outros campos).
|
|
38
|
+
- `src/delivery/model.rs`: `DeliveryPolicy.result_timeout_secs`; `RetryPolicy.resolve` aplica padrão e teto (`min(cap)`, como `ttl_secs`); método `is_result_overdue(delivered_at, now)`.
|
|
39
|
+
- `PROTOCOL.md` e `openapi.yaml`: campo novo e a regra acima.
|
|
40
|
+
|
|
41
|
+
**Configuração**
|
|
42
|
+
- `src/config.rs` e `src/settings/model.rs`: `DELIVERY_RESULT_TIMEOUT_SECONDS` e `DELIVERY_RESULT_TIMEOUT_SECONDS_CAP` (editáveis em runtime, categoria `delivery`, como `DELIVERY_TTL_SECONDS`). Ver decisão D1 sobre o padrão.
|
|
43
|
+
- `RuntimeSettings`/`from_runtime_settings` e o JSON de `app.rs` (~linhas 423-437) incluem o campo novo (default e cap).
|
|
44
|
+
|
|
45
|
+
**Persistência**
|
|
46
|
+
- Nova migration `backend/migrations/tenant/0013_*.sql` (a pasta `migrations/` só tem `tenant/`; a última é a `0012_seed_pulse_trash_permissions.sql`): `ALTER TABLE notification_messages ADD COLUMN delivery_result_timeout_secs BIGINT` **nullable** (linhas existentes ficam `NULL` = sem prazo, preservando o comportamento atual).
|
|
47
|
+
- `delivery/repo.rs`: gravar/ler a coluna (memória e Postgres).
|
|
48
|
+
|
|
49
|
+
**Verificador**
|
|
50
|
+
- `delivered_at` é TEXT (epoch em segundos como string): no SQL use `CAST(delivered_at AS BIGINT) + delivery_result_timeout_secs <= $1`, protegendo vazio/não numérico como já é feito com `next_attempt_at`.
|
|
51
|
+
- **Separar "due para entrega" de "delivered vencida"** em `delivery/repo.rs` (trait ~linha 49, memória ~260, Postgres ~881; hoje o filtro é `status='pending'`): um método novo (ex.: `list_overdue_results(now)`) para as `delivered` vencidas. Elas **não** podem ser enfileiradas como `Work`, porque `process_once` tentaria reentregar uma mensagem já entregue.
|
|
52
|
+
- `delivery/service.rs`, em `poll_retries` (~linha 712): para cada `delivered` vencida, chamar **direto** `record_execution_result(id, None, RetryableFailure { reason: "result_timeout", retry_after_secs: None })`. Esse método já incrementa via `schedule_retry`, registra a tentativa e manda à DLQ via `mark_execution_dead_letter` quando `new_count > max_attempts`. O log sai com outcome `execution_retryable` e detail `result_timeout`; um outcome próprio `result_timeout` exigiria ramo novo (decidir, D5).
|
|
53
|
+
- `mark_delivered` reescreve `delivered_at` a cada `ack` (memória e Postgres), então o prazo reinicia a cada nova tentativa, como desejado.
|
|
54
|
+
|
|
55
|
+
**SDKs publisher (6)**: Java, Next, Dart, PHP, Rust, Delphi. Adicionar `resultTimeoutSeconds` ao tipo `DeliveryPolicy`/`TDeliveryPolicy` (Delphi: `0` = omitir), serialização, teste de serialização e README/CONFIGURATION de cada um.
|
|
56
|
+
|
|
57
|
+
**UI** (`frontend/seedgrid-pulse-ui`): mostrar o motivo `result_timeout` na tela da DLQ/mensagens e expor as duas configurações novas em Configurações.
|
|
58
|
+
|
|
59
|
+
## 4. Decisões que dependem do dono do produto
|
|
60
|
+
|
|
61
|
+
- **D1. Padrão.** Recomendação: **desligado por padrão na primeira versão** (`DELIVERY_RESULT_TIMEOUT_SECONDS=0` ou ausente = sem prazo), para não mudar o comportamento de tarefas longas já em uso; o operador liga e define o teto. Alternativa: padrão generoso (ex.: 3600 s). Um padrão curto reentrega tarefa ainda em execução e a executa duas vezes.
|
|
62
|
+
- **D2. `result` tardio** (chega depois do timeout). **Risco maior do que parece:** `record_execution_result` (`service.rs:607`) só tem guarda de estado terminal (~linha 624). Hoje um `result` que chega com a mensagem de volta em `pending` (retry agendado) é **aplicado como se fosse da tentativa atual**: um `success` tardio conclui a tarefa; um `retryable` conta de novo. Para ignorar o tardio é preciso uma guarda nova (ex.: só aplicar se `status == delivered`), e verificar que ela não quebra o fluxo atual em que o `result` pode chegar sem `ack` prévio (autoAck, ordem das mensagens). Exige teste dedicado. Recomendação: ignorar com erro idempotente **se** a guarda se mostrar segura; senão, manter o comportamento atual e documentar.
|
|
63
|
+
- **D3. Mensagens `delivered` já existentes** na migração: recomendação `NULL` (sem prazo) para as antigas; operador usa o descarte manual.
|
|
64
|
+
- **D4. Nome dos campos e variáveis**: os acima são sugestão.
|
|
65
|
+
- **D5. Outcome do log de tentativas**: reaproveitar `execution_retryable` (detail `result_timeout`, sem código novo) ou criar o outcome `result_timeout` (ramo próprio). Recomendação: reaproveitar.
|
|
66
|
+
|
|
67
|
+
## 5. Testes mínimos
|
|
68
|
+
|
|
69
|
+
- Unidade (`delivery/mod.rs`, estilo dos cenários existentes): timeout com tentativas sobrando ⇒ `pending` + `attempt_count` +1 + `next_attempt_at`; timeout com tentativas esgotadas ⇒ `dead_letter` com `result_timeout`; `notification`/`acknowledged` ignorados; `result` tardio (D2); `0` e acima do teto (validação 422 e redução ao teto).
|
|
70
|
+
- Postgres: migration aplica; linha antiga (`NULL`) não expira; `list_due_messages` retorna a `delivered` vencida.
|
|
71
|
+
- SDKs: serialização do campo novo (omitido quando ausente).
|
|
72
|
+
- Integração (`integration/seedgrid-pulse-sdk-it`): publicar `processed` com `resultTimeoutSeconds` pequeno, receber sem responder, ver a DLQ com `result_timeout`.
|
|
73
|
+
|
|
74
|
+
## 6. Entrega e KB
|
|
75
|
+
|
|
76
|
+
- Versão minor do Pulse (campo novo, migration).
|
|
77
|
+
- Ao commitar, enviar à sessão `seedgrid-ia-d9`: hash, nomes finais, decisões D1 a D4. Ela confere no código e troca na KB o item "planejado" pelo comportamento real (`Protocolo-e-API`, `SDKs`, `Servidor/Configuracao`, `Operacao`) e depois publica no `seedgrid-ai`.
|
package/docs/formato.md
CHANGED
|
@@ -57,7 +57,7 @@ Arquivos e pastas que começam com ponto são ignorados.
|
|
|
57
57
|
```json
|
|
58
58
|
{
|
|
59
59
|
"schemaVersion": 2,
|
|
60
|
-
"contentVersion": "0.
|
|
60
|
+
"contentVersion": "0.4.0",
|
|
61
61
|
"toolkitBase": "SeedGrid/Toolkit",
|
|
62
62
|
"components": [
|
|
63
63
|
{ "id": "agents", "type": "agents", "files": ["agents/arquiteto.md"] },
|
package/package.json
CHANGED
|
@@ -35,11 +35,12 @@ Não use para pergunta simples, ajuste de uma linha ou correção localizada e
|
|
|
35
35
|
- `refactor`: muda a estrutura sem mudar o comportamento; define como provar que o comportamento não mudou.
|
|
36
36
|
- `chore`: manutenção (bump de versão, dependências, configuração); costuma ir direto para a skill adequada.
|
|
37
37
|
2. **Ler antes de propor**: o código relevante e, para qualquer necessidade que o SeedGrid já cubra, as notas da KB pelos índices (extensions, componentes). Não presumir estrutura nem inventar propriedades.
|
|
38
|
-
3. **Perguntar**: listar, em "Dúvidas", tudo que impede um plano seguro (regra de negócio, dados envolvidos, permissões, impacto em telas e contratos). Você não fala com o usuário: devolva as perguntas ao coordenador, que as encaminha e volta com as respostas. Repita até não restar dúvida
|
|
38
|
+
3. **Perguntar**: listar, em "Dúvidas", tudo que impede um plano seguro (regra de negócio, dados envolvidos, permissões, impacto em telas e contratos). Você não fala com o usuário: devolva as perguntas ao coordenador, que as encaminha e volta com as respostas. Repita até **não restar nenhuma dúvida aberta**: só entregue o plano final quando tudo estiver definido. Dúvida pequena com resposta razoável vira premissa declarada, e o usuário aprova as premissas junto com o plano. Cada decisão do usuário é repetida em uma frase ao ser registrada.
|
|
39
39
|
4. **Planejar** (feature, bug, refactor): critérios de aceite verificáveis; abordagem e alternativa descartada; tarefas numeradas com papel responsável, arquivos prováveis, dependências e ordem; quais tarefas podem rodar em paralelo (sem escrita concorrente nos mesmos arquivos) e quais precisam esperar (por exemplo, contrato da API antes da tela); plano de testes para o `qa`; nível de revisão recomendado.
|
|
40
40
|
5. **Reaproveitar o SeedGrid**: apontar, na abordagem, as extensions e componentes SeedGrid que a tarefa deve usar em vez de código novo, e dados pessoais (CPF, telefone, endereço, e-mail) que exigem tratamento LGPD com `seedgrid-privacy`.
|
|
41
41
|
6. **Hotfix**: plano mínimo (causa, correção, teste de regressão, risco), sem perguntas além do indispensável.
|
|
42
|
-
7.
|
|
42
|
+
7. Indicar, no plano, as decisões que viram **ADR** (mudam comportamento, propriedade, contrato ou arquitetura). Quem as grava é o coordenador, com a skill `sg-adr`, ao longo da implementação e depois dela, em `docs/ai/decisions/`.
|
|
43
|
+
8. Indicar onde o plano é salvo: `docs/ai/plans/AAAA-MM-DD-assunto.md`, com `status` no cabeçalho (o coordenador grava; veja `.ai/rules/documentos.md`). Para planos grandes que vão virar tarefas autônomas, siga a skill `sg-plan-goal`.
|
|
43
44
|
|
|
44
45
|
## Limites
|
|
45
46
|
|
|
@@ -29,6 +29,7 @@ Encontrar problemas reais na mudança antes que ela seja entregue, com achados v
|
|
|
29
29
|
- Classificar cada achado (bloqueante, importante, sugestão) com arquivo, linha e cenário concreto de falha.
|
|
30
30
|
- Descartar achados que não consegue sustentar com um cenário concreto.
|
|
31
31
|
- Conferir o padrão SeedGrid do que foi alterado (skill `sg-review`): componentes e regras `FE-nn`, `BE-nn`.
|
|
32
|
+
- Conferir onde os documentos foram parar: `.md` novo de trabalho (plano, handoff, relatório) fora de `docs/ai/` é achado; decisão que mudou comportamento, propriedade ou arquitetura sem ADR em `docs/ai/decisions/` também.
|
|
32
33
|
|
|
33
34
|
## Limites
|
|
34
35
|
|
|
@@ -45,8 +45,8 @@ public record ClienteDTORes(
|
|
|
45
45
|
## Persistência
|
|
46
46
|
|
|
47
47
|
- **BE-20.** Flyway, com migrations separadas por projeto: `src/main/resources/db/migration/<project-name>` e `src/test/resources/db/migration/<project-name>`.
|
|
48
|
-
- **BE-21.** Em hosts, Panache é
|
|
49
|
-
- **BE-23.** Toda entidade JPA estende uma base de `br.com.seedgrid.commons.persistence` (traz `id`, `publicId`, datas e usuário de criação/alteração): `BaseEntityPanache` (estende `PanacheEntityBase`, sem `active`) em hosts que usam Panache (BE-21), ou `BaseEntity` (JPA puro, inclui `active`)
|
|
48
|
+
- **BE-21.** Em hosts, usar ou não o Panache é **opcional**: a escolha é do host.
|
|
49
|
+
- **BE-23.** Toda entidade JPA estende uma base de `br.com.seedgrid.commons.persistence` (traz `id`, `publicId`, datas e usuário de criação/alteração): `BaseEntityPanache` (estende `PanacheEntityBase`, sem `active`) em hosts que usam Panache (BE-21), ou `BaseEntity` (JPA puro, inclui `active`) em hosts que não usam. Os relacionamentos usam o `id`; o `publicId` é só para busca externa.
|
|
50
50
|
|
|
51
51
|
## API REST
|
|
52
52
|
|
|
@@ -36,4 +36,4 @@ Se o projeto exigir um fluxo obrigatório (por exemplo, revisão antes de todo c
|
|
|
36
36
|
|
|
37
37
|
## Artefatos
|
|
38
38
|
|
|
39
|
-
Os templates em `.ai/templates/` são modelos distribuídos pelo toolkit. Planos, handoffs, decisões, especificações e relatórios produzidos em tarefas reais são salvos em
|
|
39
|
+
Os templates em `.ai/templates/` são modelos distribuídos pelo toolkit. Planos, handoffs, decisões, especificações e relatórios produzidos em tarefas reais são salvos em `docs/ai/<tipo>/`, conforme [documentos](documentos.md), nunca dentro de `.ai/templates/`, `.ai/agents/` ou `.ai/rules/`, que são gerenciados e podem ser atualizados.
|
|
@@ -1,26 +1,30 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: "Onde salvar os documentos que a IA gera (planos, handoff, decisões, relatórios, especificações): sempre em
|
|
2
|
+
description: "Onde salvar os documentos que a IA gera (planos, handoff, decisões/ADR, relatórios, especificações): sempre em docs/ai/<tipo>/ do projeto. Leia antes de criar qualquer arquivo .md de trabalho."
|
|
3
3
|
---
|
|
4
4
|
# Documentos gerados pela IA
|
|
5
5
|
|
|
6
|
-
Todo documento de trabalho que a IA produzir (plano, handoff, decisão, especificação, relatório, análise, anotações de sessão) é salvo em
|
|
6
|
+
Todo documento de trabalho que a IA produzir (plano, handoff, decisão, especificação, relatório, análise, anotações de sessão) é salvo em **`docs/ai/<tipo>/`** do projeto: uma pasta fixa, visível e **versionada**. Nunca na raiz do projeto, em outra pasta de `docs/` nem dentro de `.ai/`, que o toolkit gerencia e reescreve no `update`.
|
|
7
|
+
|
|
8
|
+
**Não crie arquivo `.md` fora de `docs/ai/`**, exceto o que o pedido nomeou (por exemplo um README) e os documentos permanentes do próprio projeto. Rascunho e saída de comando não são documentos: não os grave no projeto.
|
|
7
9
|
|
|
8
10
|
## Pastas por tipo
|
|
9
11
|
|
|
10
12
|
| Tipo | Pasta | Quando |
|
|
11
13
|
| --- | --- | --- |
|
|
12
|
-
| Plano |
|
|
13
|
-
| Handoff |
|
|
14
|
-
| Decisão |
|
|
15
|
-
| Especificação |
|
|
16
|
-
| Validação |
|
|
17
|
-
| Outros |
|
|
14
|
+
| Plano | `docs/ai/plans/` | Plano de uma mudança, antes de implementar e enquanto estiver em execução |
|
|
15
|
+
| Handoff | `docs/ai/handoff/` | Estado e próximos passos para outra sessão, papel ou pessoa continuar |
|
|
16
|
+
| Decisão (ADR) | `docs/ai/decisions/` | Decisão que muda comportamento, propriedade, contrato ou arquitetura, e o porquê (skill `sg-adr`, modelo `decisao-arquitetura.md`) |
|
|
17
|
+
| Especificação | `docs/ai/specs/` | Especificação de tarefa (modelo: `especificacao-tarefa.md`) |
|
|
18
|
+
| Validação | `docs/ai/reports/` | Relatórios de validação, revisão e testes (modelo: `relatorio-validacao.md`) |
|
|
19
|
+
| Outros | `docs/ai/<assunto>/` | Qualquer outro documento; crie a pasta com um nome curto no plural |
|
|
18
20
|
|
|
19
21
|
## Convenções
|
|
20
22
|
|
|
21
23
|
- Crie a pasta quando precisar. O toolkit não a cria nem a altera: o conteúdo é do projeto e sobrevive ao `update` e ao `remove`.
|
|
22
24
|
- Nome do arquivo: `AAAA-MM-DD-assunto-curto.md` (minúsculas, hífens, sem acentos), por exemplo `2026-10-15-crud-clientes.md`.
|
|
23
|
-
-
|
|
25
|
+
- Plano e handoff começam com um cabeçalho de estado (`status`: `proposto`, `em-andamento`, `vigente`, `concluido` ou `abandonado`, e `data`). Ao concluir, marque `concluido`: **plano concluído é histórico e não serve de fonte para decidir nada**; a fonte é o código, a documentação do projeto e as ADRs.
|
|
26
|
+
- As **decisões** que nasceram do plano viram ADR (`docs/ai/decisions/`), com índice gerado (`INDEX.md`, da mais nova para a mais antiga).
|
|
27
|
+
- Não grave segredos, tokens nem dados pessoais.
|
|
24
28
|
- Antes de criar, veja se já existe documento do mesmo assunto na pasta e atualize-o em vez de duplicar.
|
|
25
29
|
- Ao citar um documento em outro, use caminho relativo.
|
|
26
|
-
- Versionar
|
|
30
|
+
- Versionar `docs/ai/decisions/` é obrigatório (é a memória das decisões); o restante, o projeto decide.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sg-adr
|
|
3
|
+
description: Registra uma decisão de arquitetura ou de comportamento como ADR em docs/ai/decisions/, com cabeçalho (quem decidiu, data, consultados, domínio) e índice gerado da mais nova para a mais antiga. Use quando uma decisão mudar comportamento, propriedade de configuração, contrato ou arquitetura, durante ou depois de executar um plano, ou quando o usuário pedir para registrar ou consultar o porquê de algo.
|
|
4
|
+
metadata:
|
|
5
|
+
toolkit: seedgrid-ai
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Registrar e consultar decisões (ADR)
|
|
9
|
+
|
|
10
|
+
ADR é a memória do **porquê**. É curta, permanente e versionada. Não confunda com plano: o plano é temporário e, depois de concluído, não serve de fonte para decidir nada.
|
|
11
|
+
|
|
12
|
+
## Quando criar
|
|
13
|
+
|
|
14
|
+
Crie uma ADR quando a decisão: muda um comportamento observável; cria, remove ou altera uma propriedade de configuração; altera um contrato (API, evento, esquema de dados); escolhe entre abordagens com impacto arquitetural; ou contraria uma regra do projeto. Não crie ADR para ajuste trivial nem para decisão de uma linha de código.
|
|
15
|
+
|
|
16
|
+
## Passos
|
|
17
|
+
|
|
18
|
+
1. **Listar as decisões.** Escreva cada uma em uma frase e **mostre ao usuário** para confirmar: você propõe, quem decide é o usuário. Ao registrar, repita a decisão em uma frase (decisão registrada errada só aparece quando alguém relê).
|
|
19
|
+
2. **Domínio.** Escolha na lista `docs/ai/decisions/dominios.txt` (um domínio por linha). Se o arquivo não existir, proponha 5 a 10 domínios do projeto (por exemplo `seguranca`, `dados`, `api`, `configuracao`, `processo`), confirme e crie. Domínio novo só com confirmação do usuário.
|
|
20
|
+
3. **Criar o arquivo** `docs/ai/decisions/AAAA-MM-DD-slug.md` a partir do modelo `.ai/templates/decisao-arquitetura.md`: cabeçalho completo (`titulo`, `data`, `status`, `decidido-por`, `consultados`, `dominio`, `modulos`, `origem`) e as seções Contexto, Decisão, Alternativas e Consequências. Comece com `status: proposta` e mude para `aceita` quando o usuário confirmar.
|
|
21
|
+
4. **Substituição.** Se a decisão troca outra, não edite a antiga: crie a nova com `substitui: [arquivo-antigo.md]` e apenas marque a antiga como `status: substituida` e `substituida-por`.
|
|
22
|
+
5. **Índice.** Rode `node .ai/skills/sg-adr/scripts/adr-index.mjs`. Ele valida o cabeçalho e gera `docs/ai/decisions/INDEX.md` em ordem decrescente de data. Nunca edite o `INDEX.md` à mão; use `--check` para conferir se está em dia.
|
|
23
|
+
6. **Entregar** listando as ADRs criadas e o que ficou como `proposta`.
|
|
24
|
+
|
|
25
|
+
## Consultar
|
|
26
|
+
|
|
27
|
+
Para saber por que algo foi feito: leia `docs/ai/decisions/INDEX.md`, filtre pelo domínio ou pelo módulo e abra só as ADRs relevantes (`grep -l "dominio: .*seguranca" docs/ai/decisions/*.md`). Se a ADR estiver `substituida`, siga o `substituida-por`. Não use planos concluídos para responder "por quê".
|
|
28
|
+
|
|
29
|
+
## Regras
|
|
30
|
+
|
|
31
|
+
- ADR aceita não se edita; erro de digitação óbvio pode ser corrigido, mudança de decisão gera outra ADR.
|
|
32
|
+
- Uma decisão por ADR. Nada de segredo, token ou dado pessoal.
|
|
33
|
+
- `docs/ai/decisions/` é sempre versionada.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Gera docs/ai/decisions/INDEX.md a partir do cabeçalho das ADRs, da mais nova para a mais antiga.
|
|
3
|
+
// Uso: node .ai/skills/sg-adr/scripts/adr-index.mjs [--dir docs/ai/decisions] [--check]
|
|
4
|
+
// --check só valida e falha se o INDEX.md estiver desatualizado (sem escrever)
|
|
5
|
+
// Domínios: se existir <dir>/dominios.txt (um por linha), só esses são aceitos; senão, qualquer slug em minúsculas com hífen.
|
|
6
|
+
import { existsSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
|
|
9
|
+
const args = process.argv.slice(2);
|
|
10
|
+
const di = args.indexOf('--dir');
|
|
11
|
+
const dir = path.resolve(di >= 0 && args[di + 1] ? args[di + 1] : 'docs/ai/decisions');
|
|
12
|
+
const check = args.includes('--check');
|
|
13
|
+
const STATUS = ['proposta', 'aceita', 'substituida', 'rejeitada'];
|
|
14
|
+
|
|
15
|
+
if (!existsSync(dir)) {
|
|
16
|
+
console.error(`Pasta não encontrada: ${dir}`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
const domFile = path.join(dir, 'dominios.txt');
|
|
20
|
+
const DOMINIOS = existsSync(domFile)
|
|
21
|
+
? readFileSync(domFile, 'utf8').split(/\r?\n/).map((l) => l.trim()).filter((l) => l && !l.startsWith('#'))
|
|
22
|
+
: null;
|
|
23
|
+
|
|
24
|
+
function parse(text, file) {
|
|
25
|
+
const m = /^---\r?\n([\s\S]*?)\r?\n---/.exec(text);
|
|
26
|
+
if (!m) throw new Error(`${file}: sem cabeçalho (---)`);
|
|
27
|
+
const out = {};
|
|
28
|
+
for (const line of m[1].split(/\r?\n/)) {
|
|
29
|
+
const kv = /^([a-z-]+):\s*(.*)$/.exec(line);
|
|
30
|
+
if (!kv) continue;
|
|
31
|
+
let v = kv[2].trim();
|
|
32
|
+
if (v.startsWith('[') && v.endsWith(']')) v = v.slice(1, -1).split(',').map((s) => s.trim()).filter(Boolean);
|
|
33
|
+
out[kv[1]] = v;
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const errors = [];
|
|
39
|
+
const adrs = [];
|
|
40
|
+
for (const f of readdirSync(dir).filter((n) => n.endsWith('.md') && n !== 'INDEX.md').sort()) {
|
|
41
|
+
let h;
|
|
42
|
+
try {
|
|
43
|
+
h = parse(readFileSync(path.join(dir, f), 'utf8'), f);
|
|
44
|
+
} catch (e) {
|
|
45
|
+
errors.push(e.message);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (!/^\d{4}-\d{2}-\d{2}-/.test(f)) errors.push(`${f}: o nome deve começar com AAAA-MM-DD-`);
|
|
49
|
+
for (const k of ['titulo', 'data', 'status', 'decidido-por', 'dominio']) if (!h[k] || h[k].length === 0) errors.push(`${f}: falta "${k}"`);
|
|
50
|
+
if (h.data && !/^\d{4}-\d{2}-\d{2}$/.test(h.data)) errors.push(`${f}: data inválida (${h.data})`);
|
|
51
|
+
if (h.status && !STATUS.includes(h.status)) errors.push(`${f}: status inválido (${h.status}); use ${STATUS.join(' | ')}`);
|
|
52
|
+
const doms = Array.isArray(h.dominio) ? h.dominio : [h.dominio].filter(Boolean);
|
|
53
|
+
for (const d of doms) {
|
|
54
|
+
if (DOMINIOS ? !DOMINIOS.includes(d) : !/^[a-z0-9]+(-[a-z0-9]+)*$/.test(d)) {
|
|
55
|
+
errors.push(`${f}: domínio "${d}" inválido${DOMINIOS ? ` (permitidos em dominios.txt: ${DOMINIOS.join(', ')})` : ' (use slug em minúsculas com hífen)'}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
adrs.push({ file: f, ...h, doms });
|
|
59
|
+
}
|
|
60
|
+
if (errors.length) {
|
|
61
|
+
console.error(errors.join('\n'));
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
adrs.sort((a, b) => b.data.localeCompare(a.data) || b.file.localeCompare(a.file));
|
|
65
|
+
|
|
66
|
+
const list = (v) => (Array.isArray(v) ? v.join(', ') : v || '');
|
|
67
|
+
const rows = adrs.map((a) => `| ${a.data} | [${a.titulo}](${a.file}) | ${a.doms.join(', ')} | ${a.status} | ${list(a['decidido-por'])} | ${list(a.consultados)} |`);
|
|
68
|
+
const body = `# Decisões (ADR)
|
|
69
|
+
|
|
70
|
+
> Gerado por \`node .ai/skills/sg-adr/scripts/adr-index.mjs\` a partir do cabeçalho de cada ADR. Não edite à mão. Ordem: da mais nova para a mais antiga.
|
|
71
|
+
|
|
72
|
+
Para saber **por que** algo foi feito, procure pelo domínio (coluna 3) ou pelo módulo no cabeçalho de cada ADR.${DOMINIOS ? ` Domínios: ${DOMINIOS.join(', ')}.` : ''}
|
|
73
|
+
|
|
74
|
+
| Data | Decisão | Domínio | Status | Decidido por | Consultados |
|
|
75
|
+
|---|---|---|---|---|---|
|
|
76
|
+
${rows.join('\n')}
|
|
77
|
+
`;
|
|
78
|
+
const target = path.join(dir, 'INDEX.md');
|
|
79
|
+
let current = '';
|
|
80
|
+
try { current = readFileSync(target, 'utf8').replace(/\r\n/g, '\n'); } catch { /* sem índice ainda */ }
|
|
81
|
+
if (check) {
|
|
82
|
+
if (current !== body) { console.error('INDEX.md desatualizado: rode o script sem --check'); process.exit(1); }
|
|
83
|
+
console.log(`INDEX.md em dia (${adrs.length} ADRs)`);
|
|
84
|
+
} else {
|
|
85
|
+
writeFileSync(target, body, 'utf8');
|
|
86
|
+
console.log(`INDEX.md gerado (${adrs.length} ADRs)`);
|
|
87
|
+
}
|
|
@@ -12,10 +12,10 @@ Monta um cadastro completo nos padrões `.ai/rules/backend-seedgrid.md` e `.ai/r
|
|
|
12
12
|
## Passos
|
|
13
13
|
|
|
14
14
|
1. **Levantar.** Entidade, campos, quais são **dados pessoais** (CPF, telefone, e-mail, endereço, nascimento), quais precisam de **busca por igualdade**, permissões e se há relacionamentos. Leia um cadastro existente do projeto e copie as convenções dele. Dúvida de regra de negócio: pergunte.
|
|
15
|
-
2. **Entidade** (`entities/`): estenda a base do `seedgrid-commons` (BE-23). **Com Panache** (
|
|
15
|
+
2. **Entidade** (`entities/`): estenda a base do `seedgrid-commons` (BE-23). **Com Panache** (opcional no host, BE-21): `BaseEntityPanache` (`id`, `publicId`, datas, usuário de criação/alteração; sem `active`). **JPA puro:** `BaseEntity` (as mesmas colunas mais `active`). Se o projeto já tem cadastros, siga a base que eles usam. Relacionamentos usam o `id`; o `publicId` é só para fora (BE-30).
|
|
16
16
|
3. **Campos pessoais** (BE-61): para cada um, `@Convert(converter = EncryptedFieldAttributeConverter.class)` + `@EncryptedField`, coluna `length = 512`. Com busca: `@EncryptedField(searchField = "cpfHash", normalize = SearchNormalization.CANONICAL)` (e-mail: `EMAIL`) e o campo irmão `cpfHash` (`length = 64`). Adicione `@EntityListeners(EncryptedFieldListener.class)` na entidade. Marque os mesmos campos com `@PersonalData` para o mascaramento na auditoria (BE-63).
|
|
17
17
|
4. **Migration Flyway** (BE-20, BE-22, BE-24): tabela com `id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY`, as colunas da base escolhida (`public_id`, `created_at`, `updated_at` como `timestamptz`, `create_user_public_id`, `update_user_public_id`, e `active` só no `BaseEntity`; a lib não traz migrations), colunas cifradas com `VARCHAR(512)` e de hash com `VARCHAR(64)`, e `CREATE UNIQUE INDEX idx_<tabela>_public_id ON <tabela>(public_id)`. A pasta segue `db/migration/<projeto>`.
|
|
18
|
-
5. **Repository** (`repositories/`; Panache é
|
|
18
|
+
5. **Repository** (`repositories/`; Panache é opcional no host, BE-21). Busca por campo cifrado usa o hash: `CryptoProvider.getSecureSearch().hashForSearch(valor, <mesma normalização>)`.
|
|
19
19
|
6. **Service** (`services/`, injeção por construtor, BE-10): regras de negócio e exceptions tipadas (BE-11). Criar, alterar e excluir com `@Transactional` e `@Audit(entityFqcn = "...", operation = AuditOperationEnum.CREATE|UPDATE|DELETE, description = "...")` (BE-64). Para UPDATE/DELETE, o repository precisa expor `findByIdOptional` (é por ele que o `@Audit` captura o "antes").
|
|
20
20
|
7. **DTOs** (`dtos/`, BE-05): `...DTOReq` só com o necessário e relacionamentos apenas pela chave estrangeira (`cidadePublicId`); `...DTORes` plano, com `publicId` e **sem** `id`, e `publicId`+descrição de cada relacionamento (`cidadePublicId`, `cidadeDescricao`, `paisPublicId`...). Nada de dado pessoal em claro em respostas de erro ou log.
|
|
21
21
|
8. **Controller** (`controllers/`, BE-03, BE-31): primeiro acrescente a seção do recurso em `RestControllerPath` (base, `..._BY_PUBLIC_ID`, `..._FULL`; o controller usa base e sub-paths, o teste usa os `_FULL`); `GET` paginado (`page`, `size`, `sortField`, `sortOrder`, com `LIKE` por prefixo, BE-32), `GET` por `publicId`, `POST`, `PUT`, `DELETE`. Proteja com `@PermissionsAllowed("<ENTIDADE>_READ|CREATE|EDIT|DELETE")` (ou `@RolesAllowed`), conforme a extension `security` (BE-65), e registre as permissões como o projeto faz.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sg-plan-goal
|
|
3
|
+
description: Leva uma mudança grande de um pedido solto até um plano pronto para execução autônoma (por exemplo /goal) - princípios, reconhecimento somente leitura, hipóteses provadas por testes, rodadas de perguntas ao dono, contratos, tarefas com critério de aceite, guardiões e governança de execução. Use quando a mudança atravessa vários módulos, tem regras de negócio ou segurança críticas, ou vai ser executada por vários agentes. Para uma mudança pequena use sg-plan-change.
|
|
4
|
+
metadata:
|
|
5
|
+
toolkit: seedgrid-ai
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Do pedido ao plano pronto para o /goal
|
|
9
|
+
|
|
10
|
+
Método de 12 etapas para mudanças grandes. Cada etapa deixa um artefato. O plano e as tarefas ficam em `docs/ai/plans/` (veja `.ai/rules/documentos.md`); as decisões viram ADR com a skill `sg-adr`. Para mudança pequena, use `sg-plan-change`.
|
|
11
|
+
|
|
12
|
+
## Etapas
|
|
13
|
+
|
|
14
|
+
1. **Princípios e restrições do dono**, escritos no topo do plano: fonte da verdade, falhar fechado, o que é agnóstico, KB e documentação atualizadas ao fim de cada passo. Tudo abaixo respeita esses princípios.
|
|
15
|
+
2. **Reconhecimento somente leitura.** Leia código, migrations e documentação; levante o "estado atual" e liste **hipóteses numeradas** (H1..Hn). Não altere nada em produção.
|
|
16
|
+
3. **Prove as hipóteses com testes reais** (banco real via Testcontainers, HTTP real): um "livro de regras" que descreve como o sistema funciona **hoje**. Isso acha o que a leitura de código não acha. Resultado inesperado é um **achado**, não um teste a consertar.
|
|
17
|
+
4. **Rodadas curtas de perguntas ao dono** (3 a 6 por vez), cada uma com a **sua recomendação** e uma decisão por pergunta. O dono responde, corrige ou diz que não entendeu (explique de novo). Cada decisão entra no plano na hora, com a data, **repetida em uma frase**.
|
|
18
|
+
5. **Investigue antes de perguntar.** Tudo o que dá para verificar (código, documentação oficial, contagem de consumidores, spike descartável fora do projeto, trabalho longo delegado a um agente) você verifica. Só vão ao dono as dúvidas que são decisão dele.
|
|
19
|
+
6. **Limpezas sucessivas do plano.** Fica só o que está decidido; raciocínio e versões antigas vão para o arquivo. Seções fixas: princípios, estado atual, decisões por módulo, contratos, testes e governança, ordem de execução, em aberto.
|
|
20
|
+
7. **Cruzamento entre módulos** depois de fechar cada um: tabela de conflitos X1..Xn, com proposta e decisão para cada.
|
|
21
|
+
8. **Contratos fechados antes de codar:** DTOs, códigos de erro estáveis, chaves de cache, campos novos e inventário do front (arquivo e linha).
|
|
22
|
+
9. **Divisão em tarefas.** Fases com dependência explícita. Paralelo (trilhas por repositório, no máximo 4 agentes) só onde não há dependência e **nunca dois escrevendo no mesmo arquivo**; série onde há. Cada tarefa tem: passos em checklist, **critério de aceite**, testes unitários e de integração, e o repositório. Termine com uma fase de E2E e uma de publicação.
|
|
23
|
+
10. **Testes-guardiões.** O livro de regras vira guardião quando a regra é permanente (a expectativa se inverte no mesmo commit, citando a decisão). Convenção `*GuardianIT`/`*GuardianTest` com aviso no topo (BE-42). Liste os guardiões previstos por fase.
|
|
24
|
+
11. **Governança de execução.** Uma definição de pronto única (testes verdes, commit, registro no controle, documentação e KB). Um script de controle (status, iniciar, registrar, verificar, relatório) que compara o registro com o **git real**; nunca confie só no arquivo. Commits iniciais separando o que era de terceiros do que é da sessão. Push e merge só com autorização do dono.
|
|
25
|
+
12. **Preparar o /goal.** O avaliador da condição **não lê arquivos**: a condição precisa exigir **evidência visível** na saída. Marque a tarefa como feita só com uma linha `Evidencia:`; use um verificador de checklist cuja saída entra na condição; defina limite de turnos; um comando `/goal` por fase; regras de retomada por limite de uso e de crédito.
|
|
26
|
+
|
|
27
|
+
## Regras que nasceram de erros
|
|
28
|
+
|
|
29
|
+
- **Afirmação sobre comportamento = teste, não só leitura.** Uma conclusão tirada da documentação de um framework estava errada; um spike mostrou o contrário.
|
|
30
|
+
- **Nunca `git add -A` em diretório compartilhado:** varreu arquivos de outra sessão. Adicione arquivo por arquivo, ou trabalhe em um worktree próprio.
|
|
31
|
+
- **Nunca `taskkill` geral nem porta padrão de teste:** mate só o PID do teste e aponte o cliente HTTP para a porta do app.
|
|
32
|
+
- **Decisão registrada invertida** só apareceu quando o dono releu. Por isso a decisão é repetida em uma frase ao registrar.
|
|
33
|
+
|
|
34
|
+
## Saída
|
|
35
|
+
|
|
36
|
+
Plano (etapas 1 a 8), tarefas (etapas 9 a 12) e a lista das ADRs a registrar (`sg-adr`). Pare e peça aprovação do dono antes de iniciar a execução.
|
|
@@ -1,19 +1,33 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: "Modelo de registro de decisão
|
|
2
|
+
description: "Modelo de ADR (registro de decisão): cabeçalho com quem decidiu, data, consultados e domínio, mais contexto, decisão, alternativas e consequências."
|
|
3
3
|
---
|
|
4
|
-
#
|
|
4
|
+
# <título da decisão>
|
|
5
5
|
|
|
6
|
-
> Modelo do SeedGrid AI Toolkit.
|
|
6
|
+
> Modelo do SeedGrid AI Toolkit. Use a skill `sg-adr`: ela copia este modelo para `docs/ai/decisions/AAAA-MM-DD-slug.md` e atualiza o índice. ADR aceita **não se edita**: se a decisão mudar, crie outra e marque a antiga como `substituida`.
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
Cabeçalho (obrigatório, no topo do arquivo, entre `---`):
|
|
9
|
+
|
|
10
|
+
```yaml
|
|
11
|
+
---
|
|
12
|
+
description: Uma linha com a decisão.
|
|
13
|
+
titulo: Título curto da decisão
|
|
14
|
+
data: AAAA-MM-DD
|
|
15
|
+
status: proposta # proposta | aceita | substituida | rejeitada
|
|
16
|
+
decidido-por: nome ou papel
|
|
17
|
+
consultados: [nomes, quando houver]
|
|
18
|
+
dominio: [seguranca] # lista do projeto (docs/ai/decisions/dominios.txt)
|
|
19
|
+
modulos: [modulo-a, modulo-b]
|
|
20
|
+
substitui: [] # arquivos de ADR que esta substitui
|
|
21
|
+
substituida-por: # preencha na ADR antiga quando houver outra
|
|
22
|
+
origem: plano, commit ou tarefa que gerou a decisão
|
|
23
|
+
---
|
|
24
|
+
```
|
|
11
25
|
|
|
12
26
|
## Contexto
|
|
13
27
|
Problema, forças em jogo e restrições.
|
|
14
28
|
|
|
15
29
|
## Decisão
|
|
16
|
-
O que foi decidido, em termos objetivos.
|
|
30
|
+
O que foi decidido, em termos objetivos (comportamento, propriedade, arquitetura).
|
|
17
31
|
|
|
18
32
|
## Alternativas consideradas
|
|
19
33
|
| Alternativa | Prós | Contras | Motivo da rejeição |
|
package/seedgridKB/manifest.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
|
-
"contentVersion": "0.
|
|
3
|
+
"contentVersion": "0.4.0",
|
|
4
4
|
"toolkitBase": "SeedGrid/Toolkit",
|
|
5
5
|
"components": [
|
|
6
6
|
{
|
|
@@ -134,6 +134,23 @@
|
|
|
134
134
|
"SKILL.md"
|
|
135
135
|
]
|
|
136
136
|
},
|
|
137
|
+
{
|
|
138
|
+
"id": "skill:sg-adr",
|
|
139
|
+
"type": "skill",
|
|
140
|
+
"name": "sg-adr",
|
|
141
|
+
"files": [
|
|
142
|
+
"SKILL.md",
|
|
143
|
+
"scripts/adr-index.mjs"
|
|
144
|
+
]
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"id": "skill:sg-plan-goal",
|
|
148
|
+
"type": "skill",
|
|
149
|
+
"name": "sg-plan-goal",
|
|
150
|
+
"files": [
|
|
151
|
+
"SKILL.md"
|
|
152
|
+
]
|
|
153
|
+
},
|
|
137
154
|
{
|
|
138
155
|
"id": "brain",
|
|
139
156
|
"type": "brain",
|