@payconductor/houndsman 0.13.0 → 0.14.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/README.md +6 -1
- package/package.json +1 -1
- package/src/cli/commands/compare-command.ts +112 -0
- package/src/cli/commands/context-command.ts +1 -0
- package/src/cli/commands/findings-command.ts +11 -1
- package/src/cli/commands/hunt-command.ts +20 -3
- package/src/cli/commands/review-command.ts +38 -0
- package/src/cli/commands/status-command.ts +3 -2
- package/src/cli/help.ts +3 -1
- package/src/cli/index.ts +3 -1
- package/src/cli/progress.ts +7 -2
- package/src/core/ai/ai-client.ts +38 -2
- package/src/core/ai/features/brief-enricher.ts +1 -1
- package/src/core/ai/features/business-analyzer.ts +61 -0
- package/src/core/ai/features/business-summarizer.ts +44 -0
- package/src/core/ai/index.ts +8 -0
- package/src/core/ai/prompts/brief.ts +33 -7
- package/src/core/ai/prompts/business.ts +76 -0
- package/src/core/ai/provider-capabilities.ts +12 -0
- package/src/core/ai/schemas.ts +70 -2
- package/src/core/ai/usage-recorder.ts +53 -1
- package/src/core/brief.ts +246 -12
- package/src/core/builtins.ts +108 -0
- package/src/core/business-summary.ts +583 -0
- package/src/core/commands/compare.ts +233 -0
- package/src/core/commands/context.ts +6 -2
- package/src/core/commands/findings.ts +38 -30
- package/src/core/commands/review.ts +6 -0
- package/src/core/commands/scaffold.ts +27 -0
- package/src/core/commands/status.ts +9 -4
- package/src/core/compare.ts +243 -0
- package/src/core/concurrency.ts +30 -0
- package/src/core/config.ts +31 -3
- package/src/core/defaults.ts +50 -4
- package/src/core/domain/budget.ts +62 -4
- package/src/core/domain/classification.ts +12 -2
- package/src/core/engine/carryover.ts +4 -0
- package/src/core/engine/finalize-pipeline.ts +347 -0
- package/src/core/engine/prepare-pipeline.ts +1294 -0
- package/src/core/engine/review-context.ts +51 -0
- package/src/core/engine/review-options.ts +47 -0
- package/src/core/engine/review-scope.ts +14 -1
- package/src/core/engine/review-session.ts +11 -13
- package/src/core/engine/run-artifacts.ts +196 -0
- package/src/core/engine/run-format.ts +4 -0
- package/src/core/findings-state.ts +13 -1
- package/src/core/github-review.ts +5 -2
- package/src/core/hunt/hunt-runner.ts +90 -0
- package/src/core/hunt/index.ts +2 -0
- package/src/core/hunt/maki-intent-prompt.ts +50 -0
- package/src/core/hunt/maki-prompt.ts +13 -1
- package/src/core/hunt/pool.ts +4 -26
- package/src/core/logging/index.ts +6 -1
- package/src/core/logging/progress.ts +85 -1
- package/src/core/orchestrator.ts +33 -1289
- package/src/core/paths.ts +6 -3
- package/src/core/pr-context.ts +2 -4
- package/src/core/prepare-report.ts +61 -0
- package/src/core/project-config.ts +8 -1
- package/src/core/propagation.ts +127 -0
- package/src/core/registry-check.ts +60 -21
- package/src/core/renderers/manifest-writer.ts +13 -0
- package/src/core/renderers/resume-renderer.ts +151 -11
- package/src/core/report-builder.ts +108 -278
- package/src/core/storage/file/file-storage.ts +7 -3
- package/src/core/storage/sqlite/mappers.ts +10 -0
- package/src/core/storage/sqlite/schema.ts +104 -23
- package/src/core/storage/sqlite/sqlite-storage.ts +22 -9
- package/src/core/storage/types.ts +16 -4
- package/src/core/storage/work-area.ts +59 -1
- package/src/core/types.ts +31 -0
- package/src/core/watchdog-registry.ts +21 -3
- package/src/index.ts +29 -4
- package/src/server/client/ArtifactsPanel.tsx +4 -2
- package/src/server/client/FindingCard.tsx +21 -1
- package/src/server/client/RunsSection.tsx +5 -0
- package/src/server/client/utils/constants.ts +21 -11
- package/src/server/client/utils/markdown.ts +1 -1
- package/src/server/client/utils/types.ts +14 -2
- package/src/server/run-repository.ts +35 -14
- package/src/server/styles.css +2 -0
- package/src/skill/OUTPUT-FORMAT.md +36 -10
- package/src/skill/README.md +18 -14
- package/src/skill/SETUP.md +1 -0
- package/src/skill/SKILL.md +13 -4
- package/src/skill/example-resume.md +43 -0
- package/src/skill/hunt.md +145 -58
- package/src/tests/__snapshots__/baseline.test.ts.snap +187 -178
- package/src/tests/ai-core.test.ts +46 -0
- package/src/tests/ai-features.test.ts +95 -4
- package/src/tests/baseline.test.ts +29 -121
- package/src/tests/brief-scope.test.ts +250 -0
- package/src/tests/business-summary.test.ts +258 -0
- package/src/tests/compare.test.ts +233 -0
- package/src/tests/fixtures/fake-model-provider.ts +2 -1
- package/src/tests/hunt-maki.test.ts +58 -0
- package/src/tests/markdown.test.ts +3 -3
- package/src/tests/progress.test.ts +29 -1
- package/src/tests/propagation.test.ts +119 -0
- package/src/tests/review-resume.test.ts +60 -1
- package/src/tests/review-scope.test.ts +38 -29
- package/src/tests/review-scripts.test.ts +106 -218
- package/src/tests/server.test.ts +7 -4
- package/src/tests/storage.test.ts +194 -9
- package/src/core/renderers/report-renderer.ts +0 -66
- package/src/skill/example-review.md +0 -66
|
@@ -40,11 +40,11 @@ src/alpha/**
|
|
|
40
40
|
- \`src/alpha/a.ts\` (modified, +3/−1)
|
|
41
41
|
- \`src/alpha/b.ts\` (added, +2/−0)
|
|
42
42
|
|
|
43
|
-
## Fora do
|
|
43
|
+
## Fora do recorte (NÃO revisar)
|
|
44
44
|
|
|
45
|
-
> 1 arquivo(s) do
|
|
46
|
-
> (
|
|
47
|
-
>
|
|
45
|
+
> 1 arquivo(s) do escopo ficaram fora do recorte
|
|
46
|
+
> (orçamento ou prioridade de fluxo). Não os leia: registre-os em \`skipped\`
|
|
47
|
+
> com o motivo (\`budget\` ou \`fora do fluxo\`) para que a cobertura fique honesta.
|
|
48
48
|
|
|
49
49
|
- \`src/alpha/c.ts\`
|
|
50
50
|
|
|
@@ -60,111 +60,22 @@ src/alpha/**
|
|
|
60
60
|
|
|
61
61
|
- \`src/alpha/old.ts\`
|
|
62
62
|
|
|
63
|
-
##
|
|
64
|
-
|
|
65
|
-
1. **FILTER** — descarte o que não for do seu domínio (ver *Prioridade de leitura* e *Não carregar*).
|
|
66
|
-
2. **READ SCOPE** — carregue apenas as faixas alteradas em *Escopo do diff*; o restante do arquivo é só contexto.
|
|
67
|
-
3. **GREP** — use as regras críticas/anti-padrões como heurística para localizar violações nas faixas alteradas.
|
|
68
|
-
4. **DEEP-READ** — aprofunde apenas as faixas com suspeita de violação dentro do escopo.
|
|
69
|
-
5. **CONCLUDE** — grave o JSON no caminho de output, com \`reviewedFiles\` preenchido e \`line\` dentro das faixas alteradas. Sem achados → \`"findings": []\` (arquivo obrigatório). Não escreva markdown livre."
|
|
70
|
-
`;
|
|
71
|
-
|
|
72
|
-
exports[`baseline — relatório renderReport consolidado 1`] = `
|
|
73
|
-
"# PR Review — feature/x
|
|
74
|
-
|
|
75
|
-
**Data:** <date>
|
|
76
|
-
**Watchdogs ativos:** alpha, beta
|
|
77
|
-
**Achados:** 1 bloqueantes · 1 atenção · 0 sugestões · 0 nits
|
|
78
|
-
|
|
79
|
-
## Contexto da PR
|
|
80
|
-
|
|
81
|
-
**PR:** #42 Entrega X — https://example.test/pull/42
|
|
82
|
-
**Branch:** \`feature/x\` → \`main\`
|
|
83
|
-
**Objetivo:** Entregar X
|
|
84
|
-
**Motivação:** Reduzir Y
|
|
85
|
-
|
|
86
|
-
**Resumo das alterações:**
|
|
87
|
-
|
|
88
|
-
- Faz A
|
|
89
|
-
- Remove B
|
|
90
|
-
|
|
91
|
-
## Relatórios por domínio
|
|
92
|
-
|
|
93
|
-
| Watchdog | Achados | Revisados | Relatório |
|
|
94
|
-
|---|---|---|---|
|
|
95
|
-
| Alpha | 2 | 2/2 | [\`review.alpha.md\`](review.alpha.md) |
|
|
96
|
-
|
|
97
|
-
## Cobertura
|
|
98
|
-
|
|
99
|
-
| Watchdog | Arquivos revisados | Findings |
|
|
100
|
-
|---|---|---|
|
|
101
|
-
| Alpha | 2 | 2 |
|
|
102
|
-
| Beta | 1 | 0 |
|
|
103
|
-
|
|
104
|
-
## Conclusão
|
|
105
|
-
|
|
106
|
-
**Status:** ❌ Mudanças Necessárias
|
|
107
|
-
**Resumo:** 1 violações bloqueantes, 1 pontos de atenção, 0 sugestões e 0 nits.
|
|
108
|
-
|
|
109
|
-
## ❌ blocking
|
|
110
|
-
|
|
111
|
-
### \`src/alpha/a.ts\`
|
|
112
|
-
|
|
113
|
-
- **Nunca usar \`any\`.** — \`src/alpha/a.ts:1\` · _Alpha_ · \`id-alpha\`
|
|
114
|
-
any detectado
|
|
115
|
-
**Ação:** tipar
|
|
116
|
-
**Evidência:** \`export const a: any = 1;\`
|
|
63
|
+
## Orçamento de exploração
|
|
117
64
|
|
|
118
|
-
|
|
65
|
+
> O diff já está no brief. Limite **neste brief**: 6 leitura(s) de arquivo
|
|
66
|
+
> e 6 busca(s) (grep).
|
|
67
|
+
> Abra arquivo só para chamador/contexto de um símbolo citado nos trechos.
|
|
68
|
+
> Se a prova não estiver no brief nem nesse orçamento, **não é achado** —
|
|
69
|
+
> registre a dúvida no campo \`action\` do finding mais próximo ou descarte.
|
|
119
70
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
- **Promover: validar a entrada.** — \`src/alpha/b.ts:2\` · _Alpha, Beta_ · \`id-beta\`
|
|
123
|
-
sem validação
|
|
124
|
-
**Ação:** adicionar schema
|
|
125
|
-
"
|
|
126
|
-
`;
|
|
127
|
-
|
|
128
|
-
exports[`baseline — relatório renderDomainReport isolado 1`] = `
|
|
129
|
-
"# Review — Alpha (\`alpha\`) — feature/x
|
|
130
|
-
|
|
131
|
-
**Data:** <date>
|
|
132
|
-
**Arquivos revisados:** 2/2
|
|
133
|
-
**Achados:** 2
|
|
134
|
-
|
|
135
|
-
## Contexto da PR
|
|
136
|
-
|
|
137
|
-
**PR:** #42 Entrega X — https://example.test/pull/42
|
|
138
|
-
**Branch:** \`feature/x\` → \`main\`
|
|
139
|
-
**Objetivo:** Entregar X
|
|
140
|
-
**Motivação:** Reduzir Y
|
|
141
|
-
|
|
142
|
-
**Resumo das alterações:**
|
|
143
|
-
|
|
144
|
-
- Faz A
|
|
145
|
-
- Remove B
|
|
146
|
-
|
|
147
|
-
## ❌ blocking
|
|
148
|
-
|
|
149
|
-
### \`src/alpha/a.ts\`
|
|
150
|
-
|
|
151
|
-
- **Nunca usar \`any\`.** — \`src/alpha/a.ts:1\` · _Alpha_ · \`id-alpha\`
|
|
152
|
-
any detectado
|
|
153
|
-
**Ação:** tipar
|
|
154
|
-
**Evidência:** \`export const a: any = 1;\`
|
|
155
|
-
|
|
156
|
-
## ⚠️ attention
|
|
157
|
-
|
|
158
|
-
### \`src/alpha/b.ts\`
|
|
159
|
-
|
|
160
|
-
- **Promover: validar a entrada.** — \`src/alpha/b.ts:2\` · _Alpha, Beta_ · \`id-beta\`
|
|
161
|
-
sem validação
|
|
162
|
-
**Ação:** adicionar schema
|
|
163
|
-
|
|
164
|
-
## Não revisados
|
|
71
|
+
## Procedimento
|
|
165
72
|
|
|
166
|
-
|
|
167
|
-
|
|
73
|
+
1. **RECONSTRUCT** — leia o recorte do *Modelo de intenção (negócio)* e diga para si mesmo o que a mudança DEVE entregar (fluxos, invariantes, contratos). Sem modelo, derive do contexto da PR.
|
|
74
|
+
2. **SCOPE** — os *Trechos alterados (diff)* já estão no brief: **não releia o arquivo inteiro** para vê-los. Trecho com o marcador \`… [trecho truncado]\` pode ser aberto, uma vez, para o restante.
|
|
75
|
+
3. **TRACE** — siga cada símbolo alterado até seus chamadores, adapters e tipos compartilhados **dentro do seu domínio** (use os importadores do AST), **dentro do orçamento de exploração** (seção acima). Priorize os arquivos de maior churn.
|
|
76
|
+
4. **GREP** — use as regras críticas/anti-padrões como heurística para localizar violações nas faixas alteradas, **dentro do orçamento**. Pare quando tiver a prova: repetir busca não aumenta certeza.
|
|
77
|
+
5. **PROVE** — para cada suspeita, descreva gatilho → comportamento observado → invariante/regra violada. Sem prova, não é achado. Trecho com o marcador de truncamento só sustenta achado depois de abrir o arquivo para ver o restante.
|
|
78
|
+
6. **CONCLUDE** — grave o JSON no caminho de output. \`reviewedFiles\` é um subconjunto de \`## Arquivos do seu domínio\` (só o que você revisou; **não** liste chamadores/importadores nem arquivos de outros domínios) e o que ficou de fora entra em \`skipped\` ("budget", "truncated" ou "fora do fluxo"). Máximo de 20 achados: priorize severidade e agrupe ocorrências idênticas. \`line\` dentro das faixas alteradas. Sem achados → \`"findings": []\` (arquivo obrigatório). Não escreva markdown livre."
|
|
168
79
|
`;
|
|
169
80
|
|
|
170
81
|
exports[`baseline — camada gerada setup gera os mesmos arquivos e conteúdos 1`] = `
|
|
@@ -180,7 +91,7 @@ exports[`baseline — camada gerada setup gera os mesmos arquivos e conteúdos 1
|
|
|
180
91
|
".houndsman/core/skill/OUTPUT-FORMAT.md",
|
|
181
92
|
".houndsman/core/skill/README.md",
|
|
182
93
|
".houndsman/core/skill/SETUP.md",
|
|
183
|
-
".houndsman/core/skill/example-
|
|
94
|
+
".houndsman/core/skill/example-resume.md",
|
|
184
95
|
".houndsman/core/skill/fix.md",
|
|
185
96
|
".houndsman/core/skill/hunt.md",
|
|
186
97
|
".houndsman/core/skill/train.md",
|
|
@@ -214,6 +125,11 @@ consolida tudo num relatório. O motor é o CLI **\`houndsman\`**; as regras do
|
|
|
214
125
|
**Terminologia:** **houndsman** = orquestrador/treinador (CLI + motor) · **watchdog** = agente
|
|
215
126
|
especialista por domínio · **finding** = um achado da revisão · **Jev** = pré-análise opcional.
|
|
216
127
|
|
|
128
|
+
> **Duas etapas são obrigatórias em toda revisão:** o **modelo de intenção**
|
|
129
|
+
> (\`business-summary.json\`) e o revisor de fluxo **\`general\`** (id reservado, sempre ativo).
|
|
130
|
+
> O modelo vem da IA no prepare quando há chave; sem chave, um subagente (Passo 1.5) ou o
|
|
131
|
+
> pré-passe maki o produz. Nenhum dos dois é configurável pelo usuário.
|
|
132
|
+
|
|
217
133
|
## Capabilities
|
|
218
134
|
|
|
219
135
|
| Capability | Objetivo | Documento |
|
|
@@ -260,12 +176,16 @@ Interprete o alvo em linguagem natural **antes** de rodar o CLI:
|
|
|
260
176
|
Sequência para uma PR (ex.: 456):
|
|
261
177
|
|
|
262
178
|
\`\`\`bash
|
|
263
|
-
houndsman
|
|
264
|
-
|
|
265
|
-
# despache os briefs (cada um traz **Worktree:** <path>)
|
|
179
|
+
houndsman --pr 456 # cria/reusa o worktree, coleta o contexto e prepara
|
|
180
|
+
# despache os briefs em paralelo (cada um traz **Worktree:** <path>)
|
|
266
181
|
houndsman --pr 456 --finalize
|
|
267
182
|
\`\`\`
|
|
268
183
|
|
|
184
|
+
- O contexto da PR é coletado pelo próprio prepare (via \`gh\`); com IA ligada ele também é
|
|
185
|
+
resumido ali — não gaste um subagente nisso. Desative com \`--no-context\`.
|
|
186
|
+
- Para comparar duas execuções, use \`--variant <label>\` em cada uma e depois
|
|
187
|
+
\`houndsman compare --branch <head> --a <a> --b <b>\` (ou \`compare --list\`).
|
|
188
|
+
|
|
269
189
|
- O worktree vive em \`<repo>/.houndsman/worktrees/<repo>/<branch>\` (detached no commit da PR)
|
|
270
190
|
e é reutilizado nas próximas execuções da mesma branch. Ele fica **dentro do workspace**
|
|
271
191
|
(e o staging também) para o agente ler/executar sem prompts de permissão.
|
|
@@ -283,7 +203,7 @@ houndsman --pr 456 --finalize
|
|
|
283
203
|
- \`.houndsman/core/skill/OUTPUT-FORMAT.md\` — schema do JSON de saída dos watchdogs.
|
|
284
204
|
- \`.houndsman/core/skill/AUDIT-PROTOCOL.md\` — taxonomia da auditoria de regras (Modo B do \`train\`).
|
|
285
205
|
- \`.houndsman/core/skill/SETUP.md\` · \`.houndsman/core/skill/README.md\` — configuração e visão geral.
|
|
286
|
-
- \`.houndsman/core/skill/example-
|
|
206
|
+
- \`.houndsman/core/skill/example-resume.md\` — exemplo do resumo final.
|
|
287
207
|
"
|
|
288
208
|
,
|
|
289
209
|
".claude/skills/houndsman/SKILL.md":
|
|
@@ -303,6 +223,11 @@ consolida tudo num relatório. O motor é o CLI **\`houndsman\`**; as regras do
|
|
|
303
223
|
**Terminologia:** **houndsman** = orquestrador/treinador (CLI + motor) · **watchdog** = agente
|
|
304
224
|
especialista por domínio · **finding** = um achado da revisão · **Jev** = pré-análise opcional.
|
|
305
225
|
|
|
226
|
+
> **Duas etapas são obrigatórias em toda revisão:** o **modelo de intenção**
|
|
227
|
+
> (\`business-summary.json\`) e o revisor de fluxo **\`general\`** (id reservado, sempre ativo).
|
|
228
|
+
> O modelo vem da IA no prepare quando há chave; sem chave, um subagente (Passo 1.5) ou o
|
|
229
|
+
> pré-passe maki o produz. Nenhum dos dois é configurável pelo usuário.
|
|
230
|
+
|
|
306
231
|
## Capabilities
|
|
307
232
|
|
|
308
233
|
| Capability | Objetivo | Documento |
|
|
@@ -349,12 +274,16 @@ Interprete o alvo em linguagem natural **antes** de rodar o CLI:
|
|
|
349
274
|
Sequência para uma PR (ex.: 456):
|
|
350
275
|
|
|
351
276
|
\`\`\`bash
|
|
352
|
-
houndsman
|
|
353
|
-
|
|
354
|
-
# despache os briefs (cada um traz **Worktree:** <path>)
|
|
277
|
+
houndsman --pr 456 # cria/reusa o worktree, coleta o contexto e prepara
|
|
278
|
+
# despache os briefs em paralelo (cada um traz **Worktree:** <path>)
|
|
355
279
|
houndsman --pr 456 --finalize
|
|
356
280
|
\`\`\`
|
|
357
281
|
|
|
282
|
+
- O contexto da PR é coletado pelo próprio prepare (via \`gh\`); com IA ligada ele também é
|
|
283
|
+
resumido ali — não gaste um subagente nisso. Desative com \`--no-context\`.
|
|
284
|
+
- Para comparar duas execuções, use \`--variant <label>\` em cada uma e depois
|
|
285
|
+
\`houndsman compare --branch <head> --a <a> --b <b>\` (ou \`compare --list\`).
|
|
286
|
+
|
|
358
287
|
- O worktree vive em \`<repo>/.houndsman/worktrees/<repo>/<branch>\` (detached no commit da PR)
|
|
359
288
|
e é reutilizado nas próximas execuções da mesma branch. Ele fica **dentro do workspace**
|
|
360
289
|
(e o staging também) para o agente ler/executar sem prompts de permissão.
|
|
@@ -372,7 +301,7 @@ houndsman --pr 456 --finalize
|
|
|
372
301
|
- \`.houndsman/core/skill/OUTPUT-FORMAT.md\` — schema do JSON de saída dos watchdogs.
|
|
373
302
|
- \`.houndsman/core/skill/AUDIT-PROTOCOL.md\` — taxonomia da auditoria de regras (Modo B do \`train\`).
|
|
374
303
|
- \`.houndsman/core/skill/SETUP.md\` · \`.houndsman/core/skill/README.md\` — configuração e visão geral.
|
|
375
|
-
- \`.houndsman/core/skill/example-
|
|
304
|
+
- \`.houndsman/core/skill/example-resume.md\` — exemplo do resumo final.
|
|
376
305
|
"
|
|
377
306
|
,
|
|
378
307
|
".houndsman/.gitignore":
|
|
@@ -497,6 +426,7 @@ export type AiProviderOptions = Record<string, Record<string, unknown>>;
|
|
|
497
426
|
/** Features de IA do fluxo normal (todas fail-open). */
|
|
498
427
|
export type HoundsmanAiFeatures = {
|
|
499
428
|
context?: boolean;
|
|
429
|
+
business?: boolean;
|
|
500
430
|
brief?: boolean;
|
|
501
431
|
technical?: boolean;
|
|
502
432
|
classification?: boolean;
|
|
@@ -534,6 +464,18 @@ export type HoundsmanHunt = {
|
|
|
534
464
|
binaryPath?: string;
|
|
535
465
|
};
|
|
536
466
|
|
|
467
|
+
/**
|
|
468
|
+
* Prioridade por caminho.
|
|
469
|
+
*
|
|
470
|
+
* \`bonus\` promove (linhas de churn equivalentes) e \`maxRiskFromChurn\` satura o
|
|
471
|
+
* superchurn de caminhos de baixa criticidade.
|
|
472
|
+
*/
|
|
473
|
+
export type HoundsmanPriority = {
|
|
474
|
+
pattern: RegExp;
|
|
475
|
+
bonus?: number;
|
|
476
|
+
maxRiskFromChurn?: number;
|
|
477
|
+
};
|
|
478
|
+
|
|
537
479
|
/** Contrato de configuração do motor. */
|
|
538
480
|
export type HoundsmanConfig<TId extends string = string> = {
|
|
539
481
|
projectName: string;
|
|
@@ -542,6 +484,8 @@ export type HoundsmanConfig<TId extends string = string> = {
|
|
|
542
484
|
watchdogs: Record<TId, WatchdogDefinition>;
|
|
543
485
|
exclusions: readonly RegExp[];
|
|
544
486
|
docMappings: readonly (readonly [RegExp, readonly string[]])[];
|
|
487
|
+
/** Prioridade por caminho (opcional; a primeira regra que casar vence). */
|
|
488
|
+
priorities?: readonly HoundsmanPriority[];
|
|
545
489
|
alwaysIncludeDocs?: readonly string[];
|
|
546
490
|
budgets?: HoundsmanBudgets;
|
|
547
491
|
concurrency?: HoundsmanConcurrency;
|
|
@@ -626,6 +570,9 @@ import { exampleWatchdog } from "./example";
|
|
|
626
570
|
/**
|
|
627
571
|
* Definições dos watchdogs — as chaves definem o union \`WatchdogId\`.
|
|
628
572
|
* Adicionar um watchdog = criar \`<id>.ts\` e registrá-lo aqui.
|
|
573
|
+
*
|
|
574
|
+
* NÃO declare um watchdog com id \`general\`: ele é reservado pelo motor
|
|
575
|
+
* (revisor de fluxo obrigatório) e é injetado em todo ciclo.
|
|
629
576
|
*/
|
|
630
577
|
export const WATCHDOG_DEFINITIONS = {
|
|
631
578
|
default: defaultWatchdog,
|
|
@@ -653,6 +600,15 @@ export const houndsmanConfig = {
|
|
|
653
600
|
watchdogs: WATCHDOG_DEFINITIONS,
|
|
654
601
|
exclusions: EXCLUSION_PATTERNS,
|
|
655
602
|
docMappings: DOC_MAPPINGS,
|
|
603
|
+
// Prioridade por caminho (opcional): "linhas de churn equivalentes".
|
|
604
|
+
// \`bonus\` promove caminho crítico; \`maxRiskFromChurn\` satura o superchurn de
|
|
605
|
+
// caminho de baixa criticidade (risco = min(churn, teto) + bonus).
|
|
606
|
+
// A primeira regra que casar vence; sem regra, o risco é o churn puro.
|
|
607
|
+
// priorities: [
|
|
608
|
+
// { pattern: /^src\\/(db|repositories)\\//, bonus: 300 },
|
|
609
|
+
// { pattern: /^src\\/(services|domain)\\//, bonus: 200 },
|
|
610
|
+
// { pattern: /^src\\/(components|styles)\\//, maxRiskFromChurn: 120 },
|
|
611
|
+
// ],
|
|
656
612
|
} satisfies HoundsmanConfig;
|
|
657
613
|
|
|
658
614
|
export default houndsmanConfig;
|
|
@@ -669,19 +625,19 @@ exports[`baseline — camada gerada setup gera os mesmos arquivos e conteúdos 3
|
|
|
669
625
|
},
|
|
670
626
|
{
|
|
671
627
|
"file": ".houndsman/core/skill/OUTPUT-FORMAT.md",
|
|
672
|
-
"sha256": "
|
|
628
|
+
"sha256": "49d36854d10f795c",
|
|
673
629
|
},
|
|
674
630
|
{
|
|
675
631
|
"file": ".houndsman/core/skill/README.md",
|
|
676
|
-
"sha256": "
|
|
632
|
+
"sha256": "4d200cf9e5c1d748",
|
|
677
633
|
},
|
|
678
634
|
{
|
|
679
635
|
"file": ".houndsman/core/skill/SETUP.md",
|
|
680
|
-
"sha256": "
|
|
636
|
+
"sha256": "e7f587d1c8e459d1",
|
|
681
637
|
},
|
|
682
638
|
{
|
|
683
|
-
"file": ".houndsman/core/skill/example-
|
|
684
|
-
"sha256": "
|
|
639
|
+
"file": ".houndsman/core/skill/example-resume.md",
|
|
640
|
+
"sha256": "4993f56afdf2ffd1",
|
|
685
641
|
},
|
|
686
642
|
{
|
|
687
643
|
"file": ".houndsman/core/skill/fix.md",
|
|
@@ -689,7 +645,7 @@ exports[`baseline — camada gerada setup gera os mesmos arquivos e conteúdos 3
|
|
|
689
645
|
},
|
|
690
646
|
{
|
|
691
647
|
"file": ".houndsman/core/skill/hunt.md",
|
|
692
|
-
"sha256": "
|
|
648
|
+
"sha256": "0a81003a5b39b84a",
|
|
693
649
|
},
|
|
694
650
|
{
|
|
695
651
|
"file": ".houndsman/core/skill/train.md",
|
|
@@ -702,25 +658,31 @@ exports[`baseline — camada gerada setup gera os mesmos arquivos e conteúdos 3
|
|
|
702
658
|
]
|
|
703
659
|
`;
|
|
704
660
|
|
|
705
|
-
exports[`baseline — artefatos de prepare/finalize gera classification, brief, manifest,
|
|
706
|
-
"Discovery: 1 watchdog(s)
|
|
661
|
+
exports[`baseline — artefatos de prepare/finalize gera classification, brief, manifest, resumo e state 1`] = `
|
|
662
|
+
"Discovery: 1 watchdog(s) de domínio + revisor general, 3 arquivo(s), 1 deletado(s).
|
|
707
663
|
Briefs: <cwd>/.home/work/<uuid>/briefs/
|
|
708
664
|
Deletado (breaking change): src/alpha/old.ts
|
|
709
665
|
Escopo: 2 arquivo(s), 0 símbolo(s) em 2 faixa(s) alterada(s).
|
|
666
|
+
Escopo: 2 arquivo(s) em mais de um domínio (máx. 2) — cada domínio revisa o arquivo com o seu recorte.
|
|
710
667
|
Jev: pulado (desativado (--no-jev)).
|
|
711
668
|
AST: desabilitado ou sem grammar compatível.
|
|
712
669
|
Sem contexto da PR (rode \`bun run houndsman context\` antes, se houver PR).
|
|
670
|
+
Intenção da PR determinística (1 fluxo(s)) — enriquecida no passo de intenção (<cwd>/.home/work/<uuid>/business-brief.md).
|
|
713
671
|
|
|
714
672
|
Próximo passo: despachar os sub-agentes (um por brief em <cwd>/.home/work/<uuid>/briefs/).
|
|
715
673
|
Quando os findings estiverem gravados: bun run houndsman --finalize"
|
|
716
674
|
`;
|
|
717
675
|
|
|
718
|
-
exports[`baseline — artefatos de prepare/finalize gera classification, brief, manifest,
|
|
676
|
+
exports[`baseline — artefatos de prepare/finalize gera classification, brief, manifest, resumo e state: classification.json 1`] = `
|
|
719
677
|
"{
|
|
720
678
|
"watchdogs": {
|
|
721
679
|
"alpha": [
|
|
722
680
|
"src/alpha/a.ts",
|
|
723
681
|
"src/alpha/b.ts"
|
|
682
|
+
],
|
|
683
|
+
"general": [
|
|
684
|
+
"src/alpha/a.ts",
|
|
685
|
+
"src/alpha/b.ts"
|
|
724
686
|
]
|
|
725
687
|
},
|
|
726
688
|
"files": {
|
|
@@ -754,7 +716,8 @@ exports[`baseline — artefatos de prepare/finalize gera classification, brief,
|
|
|
754
716
|
"src/alpha/old.ts"
|
|
755
717
|
],
|
|
756
718
|
"stats": {
|
|
757
|
-
"alpha": 2
|
|
719
|
+
"alpha": 2,
|
|
720
|
+
"general": 2
|
|
758
721
|
},
|
|
759
722
|
"excludedCount": 0,
|
|
760
723
|
"headSha": "<sha>",
|
|
@@ -785,7 +748,7 @@ exports[`baseline — artefatos de prepare/finalize gera classification, brief,
|
|
|
785
748
|
"
|
|
786
749
|
`;
|
|
787
750
|
|
|
788
|
-
exports[`baseline — artefatos de prepare/finalize gera classification, brief, manifest,
|
|
751
|
+
exports[`baseline — artefatos de prepare/finalize gera classification, brief, manifest, resumo e state: manifest.json 1`] = `
|
|
789
752
|
"{
|
|
790
753
|
"branch": "main",
|
|
791
754
|
"slug": "main",
|
|
@@ -814,12 +777,31 @@ exports[`baseline — artefatos de prepare/finalize gera classification, brief,
|
|
|
814
777
|
"brief": "<cwd>\\\\.home\\\\work\\\\<uuid>\\\\briefs\\\\alpha.md",
|
|
815
778
|
"findings": "<cwd>\\\\.home\\\\work\\\\<uuid>\\\\findings\\\\alpha.json",
|
|
816
779
|
"status": "pending"
|
|
780
|
+
},
|
|
781
|
+
{
|
|
782
|
+
"id": "general",
|
|
783
|
+
"name": "Lógica e domínio (fluxo)",
|
|
784
|
+
"files": [
|
|
785
|
+
"src/alpha/a.ts",
|
|
786
|
+
"src/alpha/b.ts"
|
|
787
|
+
],
|
|
788
|
+
"overflow": 0,
|
|
789
|
+
"brief": "<cwd>\\\\.home\\\\work\\\\<uuid>\\\\briefs\\\\general.md",
|
|
790
|
+
"findings": "<cwd>\\\\.home\\\\work\\\\<uuid>\\\\findings\\\\general.json",
|
|
791
|
+
"status": "pending"
|
|
817
792
|
}
|
|
818
793
|
],
|
|
819
794
|
"context": {
|
|
820
795
|
"summarized": false,
|
|
821
796
|
"source": null
|
|
822
797
|
},
|
|
798
|
+
"business": {
|
|
799
|
+
"source": "deterministic",
|
|
800
|
+
"flows": 1,
|
|
801
|
+
"invariants": 0,
|
|
802
|
+
"contracts": 0,
|
|
803
|
+
"states": 0
|
|
804
|
+
},
|
|
823
805
|
"jev": {
|
|
824
806
|
"available": false,
|
|
825
807
|
"reason": "desativado (--no-jev)"
|
|
@@ -843,7 +825,7 @@ exports[`baseline — artefatos de prepare/finalize gera classification, brief,
|
|
|
843
825
|
"
|
|
844
826
|
`;
|
|
845
827
|
|
|
846
|
-
exports[`baseline — artefatos de prepare/finalize gera classification, brief, manifest,
|
|
828
|
+
exports[`baseline — artefatos de prepare/finalize gera classification, brief, manifest, resumo e state: briefs/alpha.md 1`] = `
|
|
847
829
|
"# Brief — Alpha (\`alpha\`)
|
|
848
830
|
|
|
849
831
|
**Branch:** main
|
|
@@ -868,6 +850,47 @@ src/alpha/**
|
|
|
868
850
|
- \`src/alpha/a.ts\` — linhas 1-2 _(faixas do diff)_
|
|
869
851
|
- \`src/alpha/b.ts\` — linhas 1 _(faixas do diff)_
|
|
870
852
|
|
|
853
|
+
## Modelo de intenção (negócio)
|
|
854
|
+
|
|
855
|
+
> Fonte: determinístico. Use para saber o que a mudança **deve** entregar; não é achado.
|
|
856
|
+
|
|
857
|
+
> Recorte do seu domínio: fluxos/arquivos de outros domínios pertencem a outros watchdogs — não os investigue.
|
|
858
|
+
|
|
859
|
+
**Fluxos (1):**
|
|
860
|
+
- **Alpha**
|
|
861
|
+
arquivos: src/alpha/a.ts, src/alpha/b.ts
|
|
862
|
+
|
|
863
|
+
## Trechos alterados (diff)
|
|
864
|
+
|
|
865
|
+
> Trechos já extraídos do diff (\`-U0\`, só linhas alteradas). Eles bastam
|
|
866
|
+
> para achar violações: **não abra os arquivos apenas para reler o que já
|
|
867
|
+
> está aqui**. Use o arquivo só para contexto adjacente ou chamadores.
|
|
868
|
+
|
|
869
|
+
### \`src/alpha/a.ts\`
|
|
870
|
+
|
|
871
|
+
\`\`\`diff
|
|
872
|
+
diff --git a/src/alpha/a.ts b/src/alpha/a.ts
|
|
873
|
+
index cc798ff..bc84e2e 100644
|
|
874
|
+
--- a/src/alpha/a.ts
|
|
875
|
+
+++ b/src/alpha/a.ts
|
|
876
|
+
@@ -1 +1,2 @@
|
|
877
|
+
-export const a = 1;
|
|
878
|
+
+export const a: any = 1;
|
|
879
|
+
+export const b = 2;
|
|
880
|
+
\`\`\`
|
|
881
|
+
|
|
882
|
+
### \`src/alpha/b.ts\`
|
|
883
|
+
|
|
884
|
+
\`\`\`diff
|
|
885
|
+
diff --git a/src/alpha/b.ts b/src/alpha/b.ts
|
|
886
|
+
new file mode 100644
|
|
887
|
+
index 0000000..f4082f0
|
|
888
|
+
--- /dev/null
|
|
889
|
+
+++ b/src/alpha/b.ts
|
|
890
|
+
@@ -0,0 +1 @@
|
|
891
|
+
+export const b = 3;
|
|
892
|
+
\`\`\`
|
|
893
|
+
|
|
871
894
|
## Regras (por severidade)
|
|
872
895
|
|
|
873
896
|
- ❌ **blocking** — Nunca usar \`any\`.
|
|
@@ -886,77 +909,62 @@ src/alpha/**
|
|
|
886
909
|
|
|
887
910
|
- \`src/alpha/old.ts\`
|
|
888
911
|
|
|
912
|
+
## Orçamento de exploração
|
|
913
|
+
|
|
914
|
+
> O diff já está no brief. Limite **neste brief**: 6 leitura(s) de arquivo
|
|
915
|
+
> e 6 busca(s) (grep).
|
|
916
|
+
> Abra arquivo só para chamador/contexto de um símbolo citado nos trechos.
|
|
917
|
+
> Se a prova não estiver no brief nem nesse orçamento, **não é achado** —
|
|
918
|
+
> registre a dúvida no campo \`action\` do finding mais próximo ou descarte.
|
|
919
|
+
|
|
889
920
|
## Procedimento
|
|
890
921
|
|
|
891
|
-
1. **
|
|
892
|
-
2. **
|
|
893
|
-
3. **
|
|
894
|
-
4. **
|
|
895
|
-
5. **
|
|
922
|
+
1. **RECONSTRUCT** — leia o recorte do *Modelo de intenção (negócio)* e diga para si mesmo o que a mudança DEVE entregar (fluxos, invariantes, contratos). Sem modelo, derive do contexto da PR.
|
|
923
|
+
2. **SCOPE** — os *Trechos alterados (diff)* já estão no brief: **não releia o arquivo inteiro** para vê-los. Trecho com o marcador \`… [trecho truncado]\` pode ser aberto, uma vez, para o restante.
|
|
924
|
+
3. **TRACE** — siga cada símbolo alterado até seus chamadores, adapters e tipos compartilhados **dentro do seu domínio** (use os importadores do AST), **dentro do orçamento de exploração** (seção acima). Priorize os arquivos de maior churn.
|
|
925
|
+
4. **GREP** — use as regras críticas/anti-padrões como heurística para localizar violações nas faixas alteradas, **dentro do orçamento**. Pare quando tiver a prova: repetir busca não aumenta certeza.
|
|
926
|
+
5. **PROVE** — para cada suspeita, descreva gatilho → comportamento observado → invariante/regra violada. Sem prova, não é achado. Trecho com o marcador de truncamento só sustenta achado depois de abrir o arquivo para ver o restante.
|
|
927
|
+
6. **CONCLUDE** — grave o JSON no caminho de output. \`reviewedFiles\` é um subconjunto de \`## Arquivos do seu domínio\` (só o que você revisou; **não** liste chamadores/importadores nem arquivos de outros domínios) e o que ficou de fora entra em \`skipped\` ("budget", "truncated" ou "fora do fluxo"). Máximo de 20 achados: priorize severidade e agrupe ocorrências idênticas. \`line\` dentro das faixas alteradas. Sem achados → \`"findings": []\` (arquivo obrigatório). Não escreva markdown livre.
|
|
896
928
|
"
|
|
897
929
|
`;
|
|
898
930
|
|
|
899
|
-
exports[`baseline — artefatos de prepare/finalize gera classification, brief, manifest,
|
|
931
|
+
exports[`baseline — artefatos de prepare/finalize gera classification, brief, manifest, resumo e state 2`] = `
|
|
900
932
|
"Triagem: sqlite:<cwd>\\houndsman.db#<uuid> — 1 finding(s).
|
|
901
|
-
|
|
933
|
+
Resumo: sqlite:<cwd>\\houndsman.db#<uuid> (review-resume.md) — 1 bloqueante(s), 0 atenção."
|
|
902
934
|
`;
|
|
903
935
|
|
|
904
|
-
exports[`baseline — artefatos de prepare/finalize gera classification, brief, manifest,
|
|
905
|
-
"#
|
|
906
|
-
|
|
907
|
-
**Data:** <date>
|
|
908
|
-
**Watchdogs ativos:** alpha
|
|
909
|
-
**Achados:** 1 bloqueantes · 0 atenção · 0 sugestões · 0 nits
|
|
936
|
+
exports[`baseline — artefatos de prepare/finalize gera classification, brief, manifest, resumo e state: review-resume.md 1`] = `
|
|
937
|
+
"# Resumo da revisão — main
|
|
910
938
|
|
|
911
|
-
|
|
939
|
+
**Data:** <date> · **Status:** ❌ Mudanças necessárias
|
|
912
940
|
|
|
913
|
-
|
|
914
|
-
|---|---|---|---|
|
|
915
|
-
| Alpha | 1 | 2/2 | [\`review.alpha.md\`](review.alpha.md) |
|
|
941
|
+
## Resumo de negócio
|
|
916
942
|
|
|
917
|
-
|
|
943
|
+
**Fluxos (1):**
|
|
944
|
+
- **Alpha**
|
|
945
|
+
arquivos: src/alpha/a.ts, src/alpha/b.ts
|
|
918
946
|
|
|
919
|
-
|
|
920
|
-
|---|---|---|
|
|
921
|
-
| Alpha | 2 | 1 |
|
|
922
|
-
|
|
923
|
-
## Conclusão
|
|
947
|
+
## Pontos técnicos
|
|
924
948
|
|
|
925
|
-
**
|
|
926
|
-
**
|
|
949
|
+
- **Estrutura** — Novo diretório \`src/alpha\` com 1 arquivo(s).
|
|
950
|
+
- **Remoções** — \`src/alpha/old.ts\`
|
|
927
951
|
|
|
928
|
-
##
|
|
952
|
+
## Cobertura por domínio
|
|
929
953
|
|
|
930
|
-
|
|
954
|
+
| Domínio | Achados | Revisados | Fora do recorte | Faltam |
|
|
955
|
+
|---|---|---|---|---|
|
|
956
|
+
| Alpha | 1 | 2/2 | — | — |
|
|
957
|
+
| Lógica e domínio (fluxo) | 0 | 2/2 | — | — |
|
|
931
958
|
|
|
932
|
-
|
|
933
|
-
any detectado
|
|
934
|
-
**Ação:** tipar
|
|
935
|
-
**Evidência:** \`export const a: any = 1;\`
|
|
936
|
-
|
|
937
|
-
"
|
|
938
|
-
`;
|
|
959
|
+
> \`Faltam\` = arquivos do escopo sem revisão e sem declaração em \`skipped\`.
|
|
939
960
|
|
|
940
|
-
|
|
941
|
-
"# Review — Alpha (\`alpha\`) — main
|
|
942
|
-
|
|
943
|
-
**Data:** <date>
|
|
944
|
-
**Arquivos revisados:** 2/2
|
|
945
|
-
**Achados:** 1
|
|
946
|
-
|
|
947
|
-
## ❌ blocking
|
|
948
|
-
|
|
949
|
-
### \`src/alpha/a.ts\`
|
|
950
|
-
|
|
951
|
-
- **Nunca usar \`any\`.** — \`src/alpha/a.ts:1\` · _Alpha_
|
|
952
|
-
any detectado
|
|
953
|
-
**Ação:** tipar
|
|
954
|
-
**Evidência:** \`export const a: any = 1;\`
|
|
961
|
+
## Conclusão
|
|
955
962
|
|
|
963
|
+
**Resumo:** 1 bloqueantes · 0 atenção · 0 sugestões · 0 nits.
|
|
956
964
|
"
|
|
957
965
|
`;
|
|
958
966
|
|
|
959
|
-
exports[`baseline — artefatos de prepare/finalize gera classification, brief, manifest,
|
|
967
|
+
exports[`baseline — artefatos de prepare/finalize gera classification, brief, manifest, resumo e state: state.json 1`] = `
|
|
960
968
|
"{
|
|
961
969
|
"version": 2,
|
|
962
970
|
"branch": "main",
|
|
@@ -979,6 +987,7 @@ exports[`baseline — artefatos de prepare/finalize gera classification, brief,
|
|
|
979
987
|
"message": "any detectado",
|
|
980
988
|
"action": "tipar",
|
|
981
989
|
"evidence": "export const a: any = 1;",
|
|
990
|
+
"basis": "rule",
|
|
982
991
|
"anchor": {
|
|
983
992
|
"snippet": "export const a: any = 1;",
|
|
984
993
|
"line": 1,
|
|
@@ -1001,7 +1010,7 @@ exports[`baseline — artefatos de prepare/finalize gera classification, brief,
|
|
|
1001
1010
|
}"
|
|
1002
1011
|
`;
|
|
1003
1012
|
|
|
1004
|
-
exports[`baseline — artefatos de prepare/finalize gera classification, brief, manifest,
|
|
1013
|
+
exports[`baseline — artefatos de prepare/finalize gera classification, brief, manifest, resumo e state: run.json 1`] = `
|
|
1005
1014
|
"{
|
|
1006
1015
|
"id": "<uuid>",
|
|
1007
1016
|
"repoKey": "path:<cwd>",
|
|
@@ -155,6 +155,52 @@ describe("temperature e providerOptions", () => {
|
|
|
155
155
|
deepseek: { thinking: { type: "disabled" } },
|
|
156
156
|
});
|
|
157
157
|
});
|
|
158
|
+
|
|
159
|
+
test("objetos estruturados desligam thinking por default", async () => {
|
|
160
|
+
const provider = new FakeModelProvider(() => ({ ok: true }));
|
|
161
|
+
const client = new AiClient({ project, provider });
|
|
162
|
+
await client.generateObject("business", {
|
|
163
|
+
prompt: "oi",
|
|
164
|
+
outputSchema: z.object({ ok: z.boolean() }),
|
|
165
|
+
});
|
|
166
|
+
expect(provider.requests[0]?.providerOptions).toEqual({
|
|
167
|
+
deepseek: { thinking: { type: "disabled" } },
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test("thinking configurado pelo projeto vence o default", async () => {
|
|
172
|
+
const configured = new ProjectConfig({
|
|
173
|
+
...fakeHoundsmanConfig,
|
|
174
|
+
ai: { providerOptions: { deepseek: { thinking: { type: "enabled" } } } },
|
|
175
|
+
});
|
|
176
|
+
const provider = new FakeModelProvider(() => ({ ok: true }));
|
|
177
|
+
const client = new AiClient({ project: configured, provider });
|
|
178
|
+
await client.generateObject("business", {
|
|
179
|
+
prompt: "oi",
|
|
180
|
+
outputSchema: z.object({ ok: z.boolean() }),
|
|
181
|
+
});
|
|
182
|
+
expect(provider.requests[0]?.providerOptions).toEqual({
|
|
183
|
+
deepseek: { thinking: { type: "enabled" } },
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test("resposta truncada falha e é registrada sem debitar o budget", async () => {
|
|
188
|
+
const provider = new FakeModelProvider(
|
|
189
|
+
() => ({ ok: true }),
|
|
190
|
+
"fake-model",
|
|
191
|
+
"length",
|
|
192
|
+
);
|
|
193
|
+
const client = new AiClient({ project, provider });
|
|
194
|
+
const result = await client.generateObject("brief", {
|
|
195
|
+
prompt: "oi",
|
|
196
|
+
outputSchema: z.object({ ok: z.boolean() }),
|
|
197
|
+
maxOutputTokens: 100,
|
|
198
|
+
});
|
|
199
|
+
expect(result.ok).toBe(false);
|
|
200
|
+
expect(client.recorder.total.calls).toBe(0);
|
|
201
|
+
expect(client.recorder.failures[0]?.finishReason).toBe("length");
|
|
202
|
+
expect(client.recorder.summary.attempts).toBe(1);
|
|
203
|
+
});
|
|
158
204
|
});
|
|
159
205
|
|
|
160
206
|
describe("avisos do AI SDK", () => {
|