@payconductor/houndsman 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +156 -3
- package/assets/houndsman.png +0 -0
- package/assets/logo-legacy.png +0 -0
- package/assets/logo.png +0 -0
- package/package.json +64 -5
- package/src/cli/index.ts +290 -0
- package/src/core/assets.ts +28 -0
- package/src/core/brief.ts +185 -0
- package/src/core/commands/context.ts +39 -0
- package/src/core/commands/findings.ts +390 -0
- package/src/core/commands/review.ts +50 -0
- package/src/core/commands/scaffold.ts +393 -0
- package/src/core/commands/setup.ts +75 -0
- package/src/core/commands/test.ts +20 -0
- package/src/core/commands/update.ts +30 -0
- package/src/core/commands/watchdog.ts +105 -0
- package/src/core/config-loader.ts +87 -0
- package/src/core/config.ts +101 -0
- package/src/core/diff-source.ts +140 -0
- package/src/core/doc-discoverer.ts +33 -0
- package/src/core/file-classifier.ts +295 -0
- package/src/core/finding-anchor.ts +147 -0
- package/src/core/findings-state.ts +538 -0
- package/src/core/jev/index.ts +12 -0
- package/src/core/jev/jev-candidates.ts +105 -0
- package/src/core/jev/jev-client.ts +160 -0
- package/src/core/jev/jev-detector.ts +205 -0
- package/src/core/jev/jev-questions.ts +92 -0
- package/src/core/jev/jev-types.ts +91 -0
- package/src/core/orchestrator.ts +596 -0
- package/src/core/paths.ts +33 -0
- package/src/core/pr-context.ts +271 -0
- package/src/core/registry-check.ts +160 -0
- package/src/core/report-builder.ts +461 -0
- package/src/core/types.ts +147 -0
- package/src/core/watchdog-registry.ts +43 -0
- package/src/index.ts +132 -0
- package/src/server/artifacts.ts +66 -0
- package/src/server/client/App.tsx +158 -0
- package/src/server/client/ArtifactsPanel.tsx +48 -0
- package/src/server/client/Filters.tsx +83 -0
- package/src/server/client/FindingCard.tsx +122 -0
- package/src/server/client/FindingsList.tsx +54 -0
- package/src/server/client/RunList.tsx +43 -0
- package/src/server/client/SummaryBar.tsx +21 -0
- package/src/server/client/api.ts +37 -0
- package/src/server/client/constants.ts +63 -0
- package/src/server/client/index.tsx +12 -0
- package/src/server/client/types.ts +48 -0
- package/src/server/client-build.ts +47 -0
- package/src/server/index.ts +365 -0
- package/src/skill/AUDIT-PROTOCOL.md +96 -0
- package/src/skill/OUTPUT-FORMAT.md +70 -0
- package/src/skill/README.md +124 -0
- package/src/skill/SETUP.md +101 -0
- package/src/skill/example-review.md +58 -0
- package/src/skill/houndsman-update.md +251 -0
- package/src/skill/houndsman.md +316 -0
- package/src/tests/findings-anchor.test.ts +120 -0
- package/src/tests/findings-commands.test.ts +226 -0
- package/src/tests/findings-state.test.ts +274 -0
- package/src/tests/fixtures/fake-project.ts +64 -0
- package/src/tests/review-scripts.test.ts +1150 -0
- package/src/tests/server.test.ts +212 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# Atualização de Lógicas do houndsman
|
|
2
|
+
|
|
3
|
+
> **Documento canônico do skill `/houndsman-update`.** Os `SKILL.md` de cada harness
|
|
4
|
+
> (`.agents/skills/houndsman-update/` e `.claude/skills/houndsman-update/`) são apenas ponteiros para cá.
|
|
5
|
+
|
|
6
|
+
> **Propósito:** manter os watchdogs do skill `houndsman` alinhados ao que o
|
|
7
|
+
> projeto **realmente aplica** — seja para adicionar/ajustar uma regra a pedido do
|
|
8
|
+
> usuário, seja para uma auditoria geral após mudanças de padrão no código.
|
|
9
|
+
|
|
10
|
+
Este skill é o **mantenedor** do `houndsman`. Ele não revisa PRs; ele atualiza as regras
|
|
11
|
+
que o `houndsman` usa.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Princípio Fundamental (Regra de Ouro)
|
|
16
|
+
|
|
17
|
+
> Só é regra o que o projeto **aplica hoje**. Como provar depende da **polaridade** da regra
|
|
18
|
+
> (ver Convenções de escrita, abaixo):
|
|
19
|
+
>
|
|
20
|
+
> - Regra **afirmativa** (`Promover:`): exige a **presença** de um padrão. Ele deve existir no
|
|
21
|
+
> código; se não existe em lugar nenhum, é `UTOPIAN` (remover/rebaixar).
|
|
22
|
+
> - Regra **negativa** (`Evitar:`, `Não`, `Nunca`): exige a **ausência** de um anti-padrão.
|
|
23
|
+
> **Zero ocorrências = regra seguida** → **manter**. Nunca remover uma regra negativa só
|
|
24
|
+
> porque não há ocorrências.
|
|
25
|
+
|
|
26
|
+
Toda regra nova ou alterada deve vir com **evidência**: `arquivo:linha` (regra afirmativa) ou
|
|
27
|
+
`0 ocorrências via grep X` do anti-padrão (regra negativa). Regra sem evidência é rejeitada ou
|
|
28
|
+
vira `suggestion` explicitamente aprovada pelo usuário.
|
|
29
|
+
|
|
30
|
+
Ver `AUDIT-PROTOCOL.md` para a taxonomia completa.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Convenções de escrita das regras
|
|
35
|
+
|
|
36
|
+
Rubrica única — o texto da regra é renderizado **literalmente** no brief do subagente
|
|
37
|
+
(`renderBrief` em `src/core/brief.ts`), então toda ambiguidade vira ruído na revisão.
|
|
38
|
+
Toda regra (nova ou alterada) deve passar por **todos** os itens:
|
|
39
|
+
|
|
40
|
+
> **Prefixo de polaridade (obrigatório).** Toda regra começa com o sentido do que se espera:
|
|
41
|
+
> `Promover:` → algo que **DEVE** acontecer (a presença do padrão); `Evitar:` → algo que
|
|
42
|
+
> **NÃO DEVE** acontecer (a ausência do anti-padrão); `Não` e `Nunca` no início são
|
|
43
|
+
> equivalentes aceitos de `Evitar:`. Regra sem prefixo explícito é ambígua e o
|
|
44
|
+
> `houndsman doctor` emite um aviso. O prefixo define **um único sentido** por regra —
|
|
45
|
+
> ver *Unipolar* (item 8).
|
|
46
|
+
|
|
47
|
+
1. **Sujeito explícito** — diz qual artefato é verificado; sem pronome órfão ("o antigo", "isso").
|
|
48
|
+
2. **Condição inequívoca** — duas IAs chegam à mesma conclusão sobre o que é violação.
|
|
49
|
+
3. **Critério concreto** — limite/prazo/lista explícito ou referência a símbolo real do código.
|
|
50
|
+
4. **Geral e reutilizável** — descreve um padrão do **domínio**, não uma regra de negócio pontual
|
|
51
|
+
que só existe dentro de uma classe/use-case. Regra amarrada a um único ponto do código não é
|
|
52
|
+
replicável em outros contextos — isso é papel de **teste automatizado**, não do watchdog.
|
|
53
|
+
- ❌ `Promover: cálculo de taxa deve usar toDecimals` (o cálculo de taxa vive atrás de um único
|
|
54
|
+
use-case — a regra não se aplica em nenhum outro contexto).
|
|
55
|
+
5. **Sem jargão abreviado ou vago** — sigla/termo/apelido deve estar definido ou ancorado num
|
|
56
|
+
símbolo/path real do código; nunca um apelido interno sem referência.
|
|
57
|
+
- ❌ `Evitar que o flow faça parte do grupo` (não se sabe o que é "flow" nem "grupo").
|
|
58
|
+
6. **Sem qualificador vago** — proibido: "apenas por compatibilidade", "período de depreciação",
|
|
59
|
+
"conjunto real", "completos e corretos", "só faz sentido", "indefinidamente".
|
|
60
|
+
7. **Polaridade explícita** — a regra começa com o sentido inequívoco:
|
|
61
|
+
- `Promover:` → algo que **DEVE** acontecer (ex.: `Promover: rotas validam a entrada com o schema do Elysia`).
|
|
62
|
+
- `Evitar:` → algo que **NÃO DEVE** acontecer; `Não` / `Nunca` no início são **equivalentes aceitos** de `Evitar:`.
|
|
63
|
+
8. **Unipolar (um só sentido)** — a regra expressa **uma única direção**, a mesma do prefixo:
|
|
64
|
+
só afirmações de **promover** ou só afirmações de **evitar**. Não combine "faça X" **e**
|
|
65
|
+
"não faça Y" na mesma regra — são dois juízos independentes, com veredito e evidência
|
|
66
|
+
próprios. Regra que precisa dos dois sentidos **vira duas regras**: uma `Promover:` e
|
|
67
|
+
outra `Evitar:`.
|
|
68
|
+
- ❌ `Evitar: o handler valida a entrada e não acessa o banco direto` (afirmação + negação).
|
|
69
|
+
- ✅ `Promover: o handler valida a entrada com o schema do Elysia` **+**
|
|
70
|
+
`Evitar: o handler acessa o banco direto`.
|
|
71
|
+
- Exceção **aceitável**: quando os dois sentidos são **inseparáveis** (a negação é condição
|
|
72
|
+
direta da afirmação). Aí o prefixo lidera o sentido dominante e o outro entra como oração
|
|
73
|
+
subordinada — nunca como segundo juízo coordenado.
|
|
74
|
+
9. **Escopo coerente** — a regra pertence ao domínio do watchdog.
|
|
75
|
+
10. **Sem duplicata** — nenhuma regra repetida literalmente no mesmo watchdog.
|
|
76
|
+
|
|
77
|
+
> O `houndsman doctor` emite um **aviso** (não bloqueante) para regra sem polaridade explícita
|
|
78
|
+
> (`Promover:`, `Evitar:`, `Não` ou `Nunca`).
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Quando Usar
|
|
83
|
+
|
|
84
|
+
- Usuário pede para **adicionar / alterar / remover** uma regra de revisão.
|
|
85
|
+
- Um watchdog está gerando **falso positivo** (cobra padrão não aplicado).
|
|
86
|
+
- O projeto mudou de padrão (ex.: passou a usar uma lib, mudou autenticação, etc.).
|
|
87
|
+
- **Auditoria completa** dos watchdogs após revisão geral do código.
|
|
88
|
+
- **Regra confusa/ambígua** (humano ou IA não entende) — ver Modo C.
|
|
89
|
+
- O usuário invoca `/houndsman-update`.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Arquivos que definem a revisão
|
|
94
|
+
|
|
95
|
+
> A primeira parte é a **camada de projeto** (muda por consumidor); a segunda é o **motor**
|
|
96
|
+
> (o pacote `@payconductor/houndsman`).
|
|
97
|
+
|
|
98
|
+
| Arquivo | Papel |
|
|
99
|
+
|---|---|
|
|
100
|
+
| `.houndsman/watchdogs/<id>.ts` | **Fonte de verdade**: `defineWatchdog` com `rules` (severidade + texto), `scope`, `description`, `reviewFocus`, `exclude`, `breakingChange`, `contextFiles`, `classify` |
|
|
101
|
+
| `.houndsman/watchdogs/index.ts` | `WATCHDOG_DEFINITIONS` — as **chaves definem os ids** (`WatchdogId`); `watchdogIds` é derivado |
|
|
102
|
+
| `houndsman.config.ts` | `houndsmanConfig` do projeto — `projectName`, `artifactDir`, `watchdogs`, `exclusions`, `docMappings` (descoberto em runtime) |
|
|
103
|
+
| `.houndsman/exclusions.ts` | `EXCLUSION_PATTERNS` + `isExcluded` |
|
|
104
|
+
| `.houndsman/doc-mappings.ts` | `DOC_MAPPINGS` (path → docs) |
|
|
105
|
+
| `.houndsman/core/types.ts` | Snapshot gerado dos tipos + helpers (autocontido) |
|
|
106
|
+
| `src/core/types.ts` | Tipos genéricos: `Severity`, `Rule`, `WatchdogDefinition`, `defineWatchdog` |
|
|
107
|
+
| `src/core/config.ts` | `HoundsmanConfig<TId>` + `defineHoundsmanConfig` (contrato do motor) |
|
|
108
|
+
| `src/core/config-loader.ts` | Descoberta/carregamento de `houndsman.config.ts` |
|
|
109
|
+
| `src/core/brief.ts` | Render do brief: `defaultRenderBrief` + `PROCEDURE` |
|
|
110
|
+
| `src/core/registry-check.ts` | Validação anti-drift (config-driven) |
|
|
111
|
+
| `src/skill/houndsman.md` | Orquestração + tabela de watchdogs |
|
|
112
|
+
| `src/tests/**` | Suíte da lib (project-agnóstica; usa `fixtures/fake-project.ts`) |
|
|
113
|
+
| `src/cli/index.ts` | **CLI único** — `houndsman [context, setup, doctor, test, watchdog, --finalize]` |
|
|
114
|
+
|
|
115
|
+
> Todo o markdown canônico vive em `src/skill/` (fonte única). Os `SKILL.md` de cada harness
|
|
116
|
+
> são apenas ponteiros — não duplicar conteúdo.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Severidade
|
|
121
|
+
|
|
122
|
+
| Severidade | Quando usar |
|
|
123
|
+
|---|---|
|
|
124
|
+
| `blocking` | Violação que quebra contrato, segurança ou integridade — e o projeto **já faz** do jeito certo em todos os lugares |
|
|
125
|
+
| `attention` | Boa prática aplicada na maior parte do código, com exceções aceitáveis |
|
|
126
|
+
| `suggestion` | Recomendação aplicada parcialmente; nunca bloqueia |
|
|
127
|
+
| `nit` | Detalhe cosmético/observação; ruído, nunca bloqueia |
|
|
128
|
+
|
|
129
|
+
> Severidade não é aspiração: se o projeto só aplica o padrão em 15/72 lugares, é `suggestion`.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Pré-análise Jev (opcional)
|
|
134
|
+
|
|
135
|
+
Regra que deve ser verificada automaticamente antes da revisão declara `jev`. É sempre um
|
|
136
|
+
**objeto** com `kind` obrigatório — a presença de `jev` não é um liga/desliga do Jev:
|
|
137
|
+
|
|
138
|
+
| Campo | Obrigatório | Uso |
|
|
139
|
+
|---|---|---|
|
|
140
|
+
| `kind` | ✅ | `noul` (a violação ocorre?), `choice` (opções) ou `score` (rubrica) |
|
|
141
|
+
| `instructions` | — | Pergunta customizada; omitida, é derivada do texto da regra |
|
|
142
|
+
| `criteria` | em `choice`/`score` | Objeto `{ valor: rótulo }` para `choice`; lista de rótulos para `score` |
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
{ severity: "blocking", text: "Import de TypeBox direto", jev: { kind: "noul" } }
|
|
146
|
+
{ severity: "attention", text: "Repository com lógica", jev: { kind: "noul", instructions: "O repository tem lógica de negócio?" } }
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Não anotar todas as regras: o Jev custa uma chamada por arquivo. Reservar para as regras
|
|
150
|
+
verificáveis por leitura local. Candidatos não confirmados pelo watchdog **não bloqueiam**.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Modo A — Ajuste pontual (pedido do usuário)
|
|
155
|
+
|
|
156
|
+
1. **Interpretar o pedido** — extrair: qual regra, qual watchdog, adicionar/alterar/remover.
|
|
157
|
+
2. **Identificar o watchdog** pelos paths de `classify` da camada de projeto. Em dúvida, perguntar.
|
|
158
|
+
3. **Provar no código** (Regra de Ouro):
|
|
159
|
+
- `grep_search` pelo padrão real e pelo anti-padrão.
|
|
160
|
+
- Determinar a **polaridade**: afirmativa (`Promover:` → exige presença; ausência = `UTOPIAN`) ou
|
|
161
|
+
negativa (`Evitar:`/`Não`/`Nunca` → exige ausência; **0 ocorrências do anti-padrão = `ALIGNED`**).
|
|
162
|
+
- Classificar: `ALIGNED`, `UTOPIAN`, `OUTDATED`, `MISCALIBRATED` ou `DEAD-PATH`.
|
|
163
|
+
- Coletar a evidência (`file:line` afirmativa; `0 ocorrências via grep X` negativa).
|
|
164
|
+
4. **Editar `.houndsman/watchdogs/<id>.ts`** (`WATCHDOG_DEFINITIONS[<id>]`):
|
|
165
|
+
- `rules`: uma entrada `{ severity, text }` por regra — o texto começa com `Promover:` (DEVE) ou
|
|
166
|
+
`Evitar:`/`Não`/`Nunca` (NÃO DEVE), conforme as Convenções de escrita.
|
|
167
|
+
- Regra que mistura afirmação e negação (dois sentidos) → dividir em **duas entradas** (*Unipolar*, item 8).
|
|
168
|
+
- `jev` (opcional): `{ kind: "noul" }`, com `instructions` e/ou `criteria` — ver seção acima.
|
|
169
|
+
- Ajustar `scope` / `description` / `reviewFocus` / `exclude` / `contextFiles` se necessário.
|
|
170
|
+
- Se a regra muda para outro domínio (paths), ajustar o `classify` do watchdog.
|
|
171
|
+
5. **Watchdog novo?** criar `.houndsman/watchdogs/<id>.ts` e registrar em `.houndsman/watchdogs/index.ts` (o id é derivado das chaves).
|
|
172
|
+
6. **Atualizar `src/skill/houndsman.md`/`README.md`** se escopo/regra visível mudou (no motor).
|
|
173
|
+
7. **Atualizar a suíte da lib** se o comportamento do MOTOR mudou (a suíte é project-agnóstica).
|
|
174
|
+
8. **Verificar** (seção abaixo).
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Modo B — Auditoria completa (revisão geral do código)
|
|
179
|
+
|
|
180
|
+
Para alinhar **todos** os watchdogs de uma vez.
|
|
181
|
+
|
|
182
|
+
1. **Fase 0 — Preparação.** Criar `.houndsman-audit/<slug>/` e copiar/adequar `AUDIT-PROTOCOL.md`.
|
|
183
|
+
2. **Fase 1 — Auditoria por watchdog (subagentes, 1 por watchdog, em ondas paralelas).**
|
|
184
|
+
Cada subagente recebe: `WATCHDOG_DEFINITIONS[<id>]` + `src/core/brief.ts` (`PROCEDURE`) +
|
|
185
|
+
fontes ground-truth do projeto (ex.: `AGENTS.md`, docs de arquitetura) + código real.
|
|
186
|
+
Saída: `<slug>/<id>.md` com ground-truth do domínio + tabela regra × veredito × evidência × ação.
|
|
187
|
+
3. **Fase 2 — Consolidação.** Merge em `audit-summary.md`; agrupar temas transversais; resolver
|
|
188
|
+
regras duplicadas entre watchdogs.
|
|
189
|
+
4. **Fase 3 — Alinhamento com o usuário.** Apresentar remoções/correções borderline e obter
|
|
190
|
+
aprovação **antes** de editar (regra utópica → remover; parcial → `suggestion`).
|
|
191
|
+
5. **Fase 4 — Aplicar.** Editar `.houndsman/watchdogs/*.ts` + `.houndsman/**` + `src/skill/**` + testes.
|
|
192
|
+
6. **Fase 5 — Verificar** (seção abaixo).
|
|
193
|
+
|
|
194
|
+
> Detalhes de taxonomia, formato de relatório e evidência: `AUDIT-PROTOCOL.md`.
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Modo C — Revisão de clareza das regras
|
|
199
|
+
|
|
200
|
+
Alinha o **texto** das regras (não a semântica) para que humano e IA interpretem igual.
|
|
201
|
+
As regras são renderizadas **literalmente** nos briefs dos subagentes (`renderBrief` em
|
|
202
|
+
`src/core/brief.ts`) — o texto da regra é exatamente o que a IA lê.
|
|
203
|
+
|
|
204
|
+
A rubrica é a seção **Convenções de escrita das regras** (acima). Toda regra deve passar por
|
|
205
|
+
todos os itens — em especial: **geral/reutilizável** (não regra de negócio pontual),
|
|
206
|
+
**sem jargão abreviado**, **polaridade explícita** (`Promover:` / `Evitar:`/`Não`/`Nunca`) e
|
|
207
|
+
**unipolar** (um só sentido por regra — sem afirmar e negar no mesmo texto).
|
|
208
|
+
|
|
209
|
+
### Passos
|
|
210
|
+
|
|
211
|
+
1. Inventariar todas as regras (`rules`) dos watchdogs alvo.
|
|
212
|
+
2. Marcar cada regra contra a rubrica; propor reescrita **preservando semântica e severidade**.
|
|
213
|
+
Regra que mistura os dois sentidos → **dividir** em afirmativa e negativa (*Unipolar*, item 8).
|
|
214
|
+
3. Se for regra **afirmativa** ambígua **e não houver evidência de aplicação** no código,
|
|
215
|
+
tratá-la como `UTOPIAN`/`OUTDATED` (remover/rebaixar) conforme a Regra de Ouro.
|
|
216
|
+
Para regra **negativa**, a ausência de ocorrências é o **esperado** — não remover por isso.
|
|
217
|
+
4. Aplicar em `.houndsman/watchdogs/<id>.ts`; corrigir duplicatas/sobreposições.
|
|
218
|
+
5. Se o termo citado for do procedimento, ajustar `PROCEDURE`/`reviewFocus`/`exclude` em
|
|
219
|
+
`src/core/brief.ts` ou no watchdog.
|
|
220
|
+
6. Conferir que o `renderBrief` continua renderizando o texto da regra (dry-run abaixo).
|
|
221
|
+
7. Verificar (seção abaixo).
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Verificação (obrigatória)
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
bun run houndsman doctor # config sem drift + ambiente + credencial Jev
|
|
229
|
+
bun run houndsman test # suíte da lib (project-agnóstica)
|
|
230
|
+
bun run ci:fix # formatação/imports/lint (no repo da lib)
|
|
231
|
+
bun run ci:check # tipagem (no repo da lib)
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Checklist final:
|
|
235
|
+
|
|
236
|
+
- [ ] `bun run houndsman doctor` → sem problemas reportados.
|
|
237
|
+
- [ ] `bun run houndsman test` → **0 fail**.
|
|
238
|
+
- [ ] `ci:fix` + `ci:check` → exit 0 (warnings pré-existentes não contam).
|
|
239
|
+
- [ ] `WATCHDOG_DEFINITIONS[<id>].id` bate com a chave e `description` não vazio.
|
|
240
|
+
- [ ] `src/skill/houndsman.md` continua consistente com os watchdogs do projeto.
|
|
241
|
+
- [ ] Toda regra nova/alterada tem **evidência** registrada no PR/relatório
|
|
242
|
+
(`file:line` se afirmativa; `0 ocorrências do anti-padrão` se negativa).
|
|
243
|
+
- [ ] Nenhuma regra **negativa** foi removida por "0 ocorrências" (Regra de Ouro).
|
|
244
|
+
- [ ] Brief de amostra não contém regra removida (dry-run abaixo).
|
|
245
|
+
- [ ] Toda regra passa nas convenções de escrita (geral/reutilizável, sem jargão abreviado,
|
|
246
|
+
sem qualificador vago, com polaridade explícita e **unipolar**) — conferir via grep.
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
# Dry-run: gerar briefs e conferir ausência de regras removidas
|
|
250
|
+
bun -e "import { renderBrief } from 'houndsman'; import { WATCHDOG_DEFINITIONS } from './.houndsman/watchdogs'; const base={files:['x.ts'],branch:'t',outputPath:'o.json',docs:[],statuses:{},deleted:[]}; for (const id of Object.keys(WATCHDOG_DEFINITIONS)) console.log(id, /verifySignature|circuit|saldo/i.test(renderBrief(WATCHDOG_DEFINITIONS[id],{...base,watchdogId:id})) ? 'FOUND' : 'OK');"
|
|
251
|
+
```
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
# houndsman — {{projectName}} (v1)
|
|
2
|
+
|
|
3
|
+
> **Documento canônico do skill `/houndsman`.** Os `SKILL.md` de cada harness
|
|
4
|
+
> (`.agents/skills/houndsman/` e `.claude/skills/houndsman/`) são apenas ponteiros para cá.
|
|
5
|
+
|
|
6
|
+
Revisão de PR: classificação de arquivos → revisão paralela por domínio → consolidação.
|
|
7
|
+
O motor é o CLI **`houndsman`**; as regras do projeto ficam em `houndsman.config.ts` + `houndsman/`.
|
|
8
|
+
|
|
9
|
+
**Terminologia:** **houndsman** = orquestrador/treinador (CLI + motor); **watchdog** = cada agente
|
|
10
|
+
especialista por domínio que audita o diff (antigo "revisor"); **review/revisão** = o processo;
|
|
11
|
+
**Jev** = pré-análise opcional (Passo 1.5).
|
|
12
|
+
|
|
13
|
+
## Quem executa o quê
|
|
14
|
+
|
|
15
|
+
- **Você** só invoca o skill: `/houndsman`. Não precisa rodar nenhum script à mão.
|
|
16
|
+
- **O skill** orquestra: executa o CLI `houndsman` e despacha os subagentes.
|
|
17
|
+
- **O CLI** tem passos distintos, um comando cada:
|
|
18
|
+
|
|
19
|
+
| Passo | Comando | Quem roda | Resultado |
|
|
20
|
+
|---|---|---|---|
|
|
21
|
+
| 0. Contexto | `houndsman context` | subagente | `context.json` + `context-brief.md` → `context-summary.json` |
|
|
22
|
+
| 1. Preparar | `houndsman` | skill | `classification.json` + `candidates.json` + `briefs/` + `manifest.json` |
|
|
23
|
+
| 2. Revisar | *(subagentes)* | skill (via `runSubagent`) | `findings/<watchdog>.json` (um por watchdog) |
|
|
24
|
+
| 3. Finalizar | `houndsman --finalize` | skill | `review.md` |
|
|
25
|
+
|
|
26
|
+
> Por que comandos separados? Entre o passo 1 e o 3 acontece trabalho de LLM (os subagentes).
|
|
27
|
+
|
|
28
|
+
**Arquitetura:**
|
|
29
|
+
|
|
30
|
+
- N watchdogs especializados por domínio de negócio.
|
|
31
|
+
- **Cada watchdog declara o que revisa** (`classify` em `.houndsman/watchdogs/<id>.ts`:
|
|
32
|
+
`{ type?: "filename" | "content", value }` — `value` é sempre regex; `filename`
|
|
33
|
+
(padrão) casa o caminho, `content` casa o conteúdo do arquivo). Não há arquivo de
|
|
34
|
+
classificação à parte nem fallback: arquivo fora dos padrões fica `unclassified`.
|
|
35
|
+
- **Fonte única de verdade do projeto:** `houndsman.config.ts` (`HoundsmanConfig`) +
|
|
36
|
+
`.houndsman/` (`watchdogs/<id>.ts`: regras + escopo + foco + `classify`;
|
|
37
|
+
`exclusions.ts`: filtros; `doc-mappings.ts`: docs).
|
|
38
|
+
- **O motor genérico** vive no pacote `@payconductor/houndsman` (`core/` + `cli/`). O markdown
|
|
39
|
+
do skill vive em `src/skill/`.
|
|
40
|
+
- **As regras refletem os padrões APLICADOS hoje** — não validar práticas que o projeto
|
|
41
|
+
não adota. Regra sem aplicação real no código gera falso positivo.
|
|
42
|
+
- Cada watchdog recebe apenas seus arquivos relevantes (brief pronto) — nunca o diff completo.
|
|
43
|
+
- Saída estruturada (JSON) por watchdog → consolidada e deduplicada automaticamente.
|
|
44
|
+
|
|
45
|
+
## Quando Usar
|
|
46
|
+
|
|
47
|
+
- Revisar PR antes de merge
|
|
48
|
+
- Code review de feature branch
|
|
49
|
+
- Validar mudanças antes de push
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Passo 0 — Contexto da PR (subagente)
|
|
54
|
+
|
|
55
|
+
O contexto vem da **descrição da PR** via GitHub CLI (`gh`), quando disponível e logado.
|
|
56
|
+
Um subagente executa o CLI e resume:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
Você é o subagente de contexto do houndsman. Execute `houndsman context`;
|
|
60
|
+
leia o `context-brief.md` indicado na saída; siga as instruções e grave
|
|
61
|
+
`context-summary.json`. Retorne `objective`/`motivation`/`changeSummary` ou "indisponível".
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
- O CLI detecta `gh` (**ausente/sem login/sem PR** → grava `available:false` e sai com código 0).
|
|
65
|
+
- O subagente resume em `context-summary.json`: `objective` (1 frase, ≤ 200 chars),
|
|
66
|
+
`motivation` (1 frase, ≤ 240 chars) e `changeSummary` (≤ 4 bullets, ≤ 160 chars cada),
|
|
67
|
+
em pt-BR e baseado **somente** no `context.json`. **Curto de propósito.**
|
|
68
|
+
- Sem contexto → a revisão segue normalmente e a seção é omitida (registrado no `manifest.json`).
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Passo 1 — Preparar (skill executa)
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
houndsman --base main
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Gera em `.houndsman/.runs/<branch-slug>/`:
|
|
79
|
+
|
|
80
|
+
| Artefato | Conteúdo |
|
|
81
|
+
|---|---|
|
|
82
|
+
| `classification.json` | Watchdogs ativados por arquivo + status (A/M/D/R) + churn |
|
|
83
|
+
| `candidates.json` | Indícios da pré-análise Jev (regra + arquivo + confiança) |
|
|
84
|
+
| `briefs/<watchdog>.md` | Instruções prontas para cada subagente (arquivos por risco) |
|
|
85
|
+
| `manifest.json` | Resumo: watchdogs, arquivos, deletados, status, contexto, Jev, orçamento |
|
|
86
|
+
|
|
87
|
+
**Excluídos automaticamente:** lock files, `dist/`, `build/`, `.env*`, imagens, source
|
|
88
|
+
maps (padrões do projeto em `.houndsman/exclusions.ts`).
|
|
89
|
+
**Deletados:** não entram como conteúdo; vão para o brief de watchdogs com
|
|
90
|
+
`breakingChange` como candidatos a **breaking change**.
|
|
91
|
+
|
|
92
|
+
### Orçamento por brief (PRs grandes)
|
|
93
|
+
|
|
94
|
+
O motor coleta o diff **uma única vez** (`git diff` + `git diff --numstat`) e classifica
|
|
95
|
+
com **churn** (linhas adicionadas/removidas) por arquivo — o custo não cresce com o nº de
|
|
96
|
+
arquivos da PR. Cada brief recebe no máximo `budgets.maxFilesPerBrief` arquivos (default 40),
|
|
97
|
+
**priorizados pelo maior churn**:
|
|
98
|
+
|
|
99
|
+
- O excedente vai para a seção **Fora do orçamento (NÃO revisar)** do brief e o watchdog deve
|
|
100
|
+
registrá-lo em `skipped` — a tabela de **Cobertura** mostra o que ficou de fora (nunca some
|
|
101
|
+
silenciosamente).
|
|
102
|
+
- Isso mantém o contexto do subagente limitado mesmo num PR de 10k linhas com centenas de
|
|
103
|
+
arquivos num único domínio.
|
|
104
|
+
- Ajuste em `houndsman.config.ts`: `budgets: { maxFilesPerBrief: 60 }` e
|
|
105
|
+
`concurrency: { jev: 8 }`. Valores não-positivos caem no default (o `doctor` avisa).
|
|
106
|
+
|
|
107
|
+
### Pré-análise Jev (Passo 1.5, opcional)
|
|
108
|
+
|
|
109
|
+
Se `TYPESAFE_API_KEY` estiver definida, cada arquivo é avaliado pelo modelo **Jev** contra
|
|
110
|
+
as regras atômicas dos watchdogs. Respostas acima do limiar viram **candidatos** em
|
|
111
|
+
`candidates.json` e entram no brief como *warm start* — o subagente **confirma ou refuta**.
|
|
112
|
+
|
|
113
|
+
- **Não é achado**: candidatos não bloqueiam.
|
|
114
|
+
- **Opcional**: sem chave / erro / timeout → `available:false`; a revisão segue idêntica.
|
|
115
|
+
- Desativar: `houndsman --no-jev`.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Passo 2 — Revisar (skill despacha os subagentes)
|
|
120
|
+
|
|
121
|
+
Um subagente por brief. Despache em **ondas** (ex.: até 8 subagentes por bloco de tool
|
|
122
|
+
calls) — evita estourar limites de taxa quando há muitos watchdogs. O prompt é o próprio brief:
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
Leia e execute o brief: .houndsman/.runs/<branch-slug>/briefs/<watchdog-id>.md
|
|
126
|
+
Você é o watchdog <watchdog-id> do {{projectName}}.
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Cada subagente grava `findings/<watchdog-id>.json` (schema `.houndsman/core/skill/OUTPUT-FORMAT.md`).
|
|
130
|
+
Sem achados → `"findings": []` (arquivo obrigatório). Arquivos na seção **Fora do orçamento**
|
|
131
|
+
não são lidos: registre-os em `skipped`.
|
|
132
|
+
|
|
133
|
+
**NUNCA enviar:** `git diff` completo · histórico de commits · arquivos de outros domínios ·
|
|
134
|
+
`AGENTS.md` completo (já está no contexto global).
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Passo 3 — Finalizar (skill executa)
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
houndsman --finalize
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Falha com mensagem clara se faltar algum `findings/<watchdog>.json`. Quando todos existem:
|
|
145
|
+
1. Valida o schema de cada JSON
|
|
146
|
+
2. Deduplica achados por `(file, line, rule)` mesclando watchdogs sobrepostos
|
|
147
|
+
3. Agrupa por severidade: ❌ blocking > ⚠️ attention > 💡 suggestion > 🔎 nit
|
|
148
|
+
4. Grava `.houndsman/.runs/<branch-slug>/review.md` + tabela de cobertura
|
|
149
|
+
5. Anexa os candidatos Jev não confirmados em `🔎 Candidatos não verificados` (não bloqueiam)
|
|
150
|
+
6. Agrupa os achados **por arquivo** dentro de cada severidade (navegabilidade em PRs grandes)
|
|
151
|
+
7. Grava um `review.<watchdog>.md` por watchdog com achados e indexa no `review.md`
|
|
152
|
+
(**Relatórios por domínio**) — mantém o relatório consolidado enxuto
|
|
153
|
+
8. Grava `.houndsman/.runs/<branch-slug>/state.json` — a **triagem**: um `id` curto e um
|
|
154
|
+
`fingerprint` de conteúdo por finding (Passo 4), preservados entre execuções
|
|
155
|
+
|
|
156
|
+
> Compatível com `classification.json` antigo: se faltar o churn, o orçamento não é aplicado.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Passo 4 — Triagem (opcional)
|
|
161
|
+
|
|
162
|
+
Cada finding recebe um `id` curto e um `fingerprint` de **conteúdo** no `--finalize`,
|
|
163
|
+
gravados em `.houndsman/.runs/<branch-slug>/state.json`. A triagem é feita pela UI ou
|
|
164
|
+
pelo agente — sem editar o JSON à mão. Duas dimensões independentes:
|
|
165
|
+
|
|
166
|
+
- **decision:** `pending` · `execute` · `ignore` (+ `comment` opcional).
|
|
167
|
+
- **execState:** `pending` · `in_progress` · `done` · `stale`.
|
|
168
|
+
|
|
169
|
+
### UI local
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
houndsman server --open
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Abre a UI (Bun.serve + Preact): lista as runs, triar os findings por severidade, comentar,
|
|
176
|
+
marcar como feito e ler os artefatos (`manifest.json`, `classification.json`, `review.md`).
|
|
177
|
+
Bind em `127.0.0.1` (ferramenta local, sem auth).
|
|
178
|
+
|
|
179
|
+
### Auto-fix pelo agente
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
houndsman findings list --decision execute --json # pendentes marcados p/ corrigir
|
|
183
|
+
# ... o agente aplica as correções ...
|
|
184
|
+
houndsman findings sync # reancora por conteúdo
|
|
185
|
+
houndsman findings done <id> # confirma os concluídos
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
> **A linha não é a identidade.** O auto-fix desloca linhas; o motor reancora cada finding
|
|
189
|
+
> pelo **conteúdo** (trecho + contexto) e não perde itens que se movem. Se a âncora some
|
|
190
|
+
> (o trecho foi corrigido), o finding vira `done` automaticamente quando a decisão era
|
|
191
|
+
> `execute`, ou `stale` caso contrário.
|
|
192
|
+
> Ocorrências byte-idênticas (mesmo trecho E mesmo contexto) compartilham o id — são
|
|
193
|
+
> tratadas como um grupo (`×N`); limitação conhecida.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Estrutura do Relatório
|
|
198
|
+
|
|
199
|
+
```markdown
|
|
200
|
+
# Review — [branch]
|
|
201
|
+
|
|
202
|
+
**Data:** YYYY-MM-DD
|
|
203
|
+
**Watchdogs ativos:** [lista]
|
|
204
|
+
**Achados:** N bloqueantes · N atenção · N sugestões · N nits
|
|
205
|
+
|
|
206
|
+
## Contexto da PR
|
|
207
|
+
**PR:** #123 Título — url
|
|
208
|
+
**Branch:** `feature/x` → `main`
|
|
209
|
+
**Objetivo:** ...
|
|
210
|
+
**Resumo das alterações:**
|
|
211
|
+
- ...
|
|
212
|
+
|
|
213
|
+
## ❌ blocking
|
|
214
|
+
### `src/routes/order-route.ts`
|
|
215
|
+
- **<regra>** — `src/routes/order-route.ts:5` · _<watchdogs>_
|
|
216
|
+
<mensagem>
|
|
217
|
+
**Ação:** <ação>
|
|
218
|
+
|
|
219
|
+
## 🔎 Candidatos não verificados (Jev) ← se houver
|
|
220
|
+
|
|
221
|
+
## Relatórios por domínio
|
|
222
|
+
| Watchdog | Achados | Revisados | Relatório |
|
|
223
|
+
|---|---|---|---|
|
|
224
|
+
|
|
225
|
+
## Cobertura
|
|
226
|
+
| Watchdog | Arquivos revisados | Findings |
|
|
227
|
+
|---|---|---|
|
|
228
|
+
|
|
229
|
+
## Conclusão
|
|
230
|
+
**Status:** ✅ Aprovado / ⚠️ Aprovado com Ressalvas / ❌ Mudanças Necessárias
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Fluxo Completo
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
houndsman context (gh) ──► context.json ──► subagente ──► context-summary.json
|
|
239
|
+
↓
|
|
240
|
+
git diff --name-status (base...head)
|
|
241
|
+
↓
|
|
242
|
+
[1] houndsman ──► classification.json + candidates.json + briefs/ + manifest.json
|
|
243
|
+
↓
|
|
244
|
+
[2] N subagentes em paralelo ──► findings/<watchdog>.json
|
|
245
|
+
↓
|
|
246
|
+
[3] houndsman --finalize ──► review.md + state.json
|
|
247
|
+
↓
|
|
248
|
+
[4] triagem (UI ou agente) ──► state.json (decision/execState)
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Invocação
|
|
254
|
+
|
|
255
|
+
```
|
|
256
|
+
/houndsman
|
|
257
|
+
/houndsman feature/add-webhook-retry
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Se a branch não for informada, detecta com `git branch --show-current`. Base do diff: `main`.
|
|
261
|
+
|
|
262
|
+
**Comandos do CLI:**
|
|
263
|
+
|
|
264
|
+
| Comando | Função |
|
|
265
|
+
|---|---|
|
|
266
|
+
| `houndsman context` | Passo 0 — contexto da PR via `gh` |
|
|
267
|
+
| `houndsman` | Passo 1 — preparar (briefs + manifesto) |
|
|
268
|
+
| `houndsman --finalize` | Passo 3 — gerar o relatório |
|
|
269
|
+
| `houndsman watchdog <id>` | Contexto de um watchdog (`--json`, `--rules`) |
|
|
270
|
+
| `houndsman findings <sub>` | Triagem dos findings (list/show/set/done/reset/sync/summary/path) |
|
|
271
|
+
| `houndsman server` | UI local de triagem + leitura de artefatos (`--port`, `--host`, `--open`) |
|
|
272
|
+
| `houndsman setup` | Cria a configuração inicial do projeto |
|
|
273
|
+
| `houndsman update` | Sincroniza os arquivos auto-gerados (`.houndsman/core`) |
|
|
274
|
+
| `houndsman doctor` | Valida a configuração, o ambiente e a credencial Jev |
|
|
275
|
+
| `houndsman test` | Suíte de testes da lib |
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## Anti-padrões
|
|
280
|
+
|
|
281
|
+
- ❌ Enviar git diff completo para qualquer watchdog
|
|
282
|
+
- ❌ Ativar watchdogs sem arquivos no seu domínio
|
|
283
|
+
- ❌ Revisar arquivos marcados como **Fora do orçamento** (registre-os em `skipped`)
|
|
284
|
+
- ❌ Carregar documentação que o watchdog não precisa
|
|
285
|
+
- ❌ Revisar arquivos gerados (lock files, builds, migrations)
|
|
286
|
+
- ❌ Escrever markdown livre em vez do JSON de `OUTPUT-FORMAT.md`
|
|
287
|
+
- ❌ Revisar arquivos `deleted` (não têm conteúdo) em vez de tratá-los como breaking change
|
|
288
|
+
- ❌ Comentários genéricos ("código bom", "pode melhorar")
|
|
289
|
+
- ❌ Sugestões de estilo triviais
|
|
290
|
+
- ❌ Reescrever código completo — apenas apontar direção
|
|
291
|
+
- ❌ Inventar objetivo/motivação da PR — o resumo vem **só** do `gh`; sem `gh`, omitir a seção
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## Manutenção das Regras
|
|
296
|
+
|
|
297
|
+
> Alterar qualquer regra (adicionar, ajustar severidade, remover, corrigir escopo) → usar o
|
|
298
|
+
> skill **`houndsman-update`** (`src/skill/houndsman-update.md`).
|
|
299
|
+
> Regra de ouro: só é regra o que o projeto **aplica hoje** — prove com evidência no código
|
|
300
|
+
> (regra negativa com 0 ocorrências = seguida → **manter**).
|
|
301
|
+
> Após qualquer alteração, rodar `houndsman doctor` + `houndsman test` + `ci:fix` + `ci:check`.
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## Performance Esperada
|
|
306
|
+
|
|
307
|
+
| Tamanho do PR | Watchdogs | Tempo |
|
|
308
|
+
|---|---|---|
|
|
309
|
+
| Pequeno (500 linhas) | 2-4 | ~30-60s |
|
|
310
|
+
| Médio (5k linhas) | 4-7 | ~1-2 min |
|
|
311
|
+
| Grande (50k linhas) | 7-10 | ~3-5 min |
|
|
312
|
+
| Enorme (300k linhas) | 10-15 | ~5-8 min |
|
|
313
|
+
|
|
314
|
+
> O custo de coleta é **constante** (2 execuções de `git`), independente do nº de arquivos.
|
|
315
|
+
> Em PRs muito grandes, o limitante é o nº de subagentes (ondas) e o orçamento por brief —
|
|
316
|
+
> não o diff. Ajuste `budgets.maxFilesPerBrief` para revisar mais/menos arquivos por domínio.
|