@fontdata/ai-harness 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +9 -2
- package/LICENSE +21 -0
- package/README.md +174 -140
- package/{src → dist}/catalog.js +5 -12
- package/dist/cli.js +174 -0
- package/dist/comentarios/command.js +74 -0
- package/dist/comentarios/instalar.js +33 -0
- package/dist/config/config.js +98 -0
- package/dist/contratos/artefatos.js +284 -0
- package/dist/contratos/eventos.js +103 -0
- package/dist/doctor/checks.js +424 -0
- package/dist/init.js +240 -0
- package/dist/install/atomic.js +51 -0
- package/dist/install/flags.js +99 -0
- package/dist/install/hooks.js +47 -0
- package/dist/install/lock.js +39 -0
- package/dist/install/plan.js +209 -0
- package/dist/install/selection.js +77 -0
- package/dist/install/settings.js +108 -0
- package/dist/memoria/indice.js +195 -0
- package/dist/migrar/migrar.js +426 -0
- package/dist/rastro/command.js +46 -0
- package/dist/run.js +86 -0
- package/dist/types.js +1 -0
- package/dist/update/legado.js +74 -0
- package/dist/update/update.js +284 -0
- package/package.json +17 -8
- package/templates/CLAUDE.md +59 -30
- package/templates/agents/auditor-plano.md +42 -0
- package/templates/agents/investigador.md +32 -0
- package/templates/agents/qa.md +44 -0
- package/templates/agents/revisor-testes.md +40 -0
- package/templates/catalog.json +24 -61
- package/templates/commands/atualizar-deps.md +43 -9
- package/templates/commands/diagrama.md +35 -10
- package/templates/commands/gerar-env.md +39 -10
- package/templates/commands/gerar-testes.md +42 -10
- package/templates/commands/git-commit-push.md +0 -0
- package/templates/commands/onboarding.md +44 -15
- package/templates/commands/refatorar.md +50 -10
- package/templates/commands/revisar-pr.md +56 -12
- package/templates/commands/sincronizar-claude-md.md +177 -9
- package/templates/commands/versao.md +0 -0
- package/templates/hooks/comentarios/check.mjs +210 -0
- package/templates/hooks/hooks.json +34 -0
- package/templates/hooks/lembrete.mjs +29 -0
- package/templates/hooks/lib/config.mjs +40 -0
- package/templates/hooks/lib/hook.mjs +85 -0
- package/templates/hooks/lib/rastro.mjs +130 -0
- package/templates/hooks/lib/segredos.mjs +91 -0
- package/templates/hooks/lib/temas.mjs +111 -0
- package/templates/hooks/lib/trabalho.mjs +94 -0
- package/templates/hooks/proteger-contratos.mjs +56 -0
- package/templates/hooks/segredo-no-commit.mjs +76 -0
- package/templates/hooks/sem-jargao.mjs +58 -0
- package/templates/hooks/task-so-fecha-verde.mjs +33 -0
- package/templates/legado/manifesto.json +1267 -0
- package/templates/rules/comentarios.md +166 -0
- package/templates/rules/harness-projeto.md +41 -0
- package/templates/skills/auditar-performance/SKILL.md +53 -0
- package/templates/skills/auditar-performance/references/pontos-quentes.md +35 -0
- package/templates/skills/auditar-performance/templates/relatorios/CLIENTE.md +26 -0
- package/templates/skills/auditar-performance/templates/relatorios/TECNICO.md +27 -0
- package/templates/skills/auditar-seguranca/SKILL.md +67 -0
- package/templates/skills/auditar-seguranca/references/seguranca-tecnica.md +23 -0
- package/templates/skills/auditar-seguranca/templates/relatorios/CLIENTE.md +30 -0
- package/templates/skills/auditar-seguranca/templates/relatorios/TECNICO.md +38 -0
- package/templates/skills/auditar-ui/SKILL.md +64 -0
- package/templates/skills/auditar-ui/references/lentes.md +33 -0
- package/templates/skills/auditar-ui/templates/relatorios/CLIENTE.md +26 -0
- package/templates/skills/auditar-ui/templates/relatorios/TECNICO.md +31 -0
- package/templates/skills/evoluir-harness/SKILL.md +81 -0
- package/templates/skills/evoluir-harness/references/agent.md +83 -0
- package/templates/skills/evoluir-harness/references/auditoria.md +107 -0
- package/templates/skills/evoluir-harness/references/code-reviewer.md +68 -0
- package/templates/skills/evoluir-harness/references/design-system.md +56 -0
- package/templates/skills/evoluir-harness/references/hook.md +95 -0
- package/templates/skills/evoluir-harness/references/rule.md +67 -0
- package/templates/skills/evoluir-harness/references/skill.md +63 -0
- package/templates/skills/executar-plan/SKILL.md +114 -0
- package/templates/skills/executar-plan/references/delegacao.md +43 -0
- package/templates/skills/executar-plan/references/retomada.md +33 -0
- package/templates/skills/executar-plan/templates/BLOQUEIOS.md +30 -0
- package/templates/skills/harness/SKILL.md +75 -0
- package/templates/skills/harness/references/auto-tag.md +118 -0
- package/templates/skills/harness/references/ignores.md +40 -0
- package/templates/skills/harness/references/scaffold.md +29 -0
- package/templates/skills/harness/references/validar-ideia.md +42 -0
- package/templates/skills/memoria/SKILL.md +52 -0
- package/templates/skills/nova-tarefa/SKILL.md +97 -0
- package/templates/skills/nova-tarefa/references/isso-ja-existe.md +24 -0
- package/templates/skills/nova-tarefa/references/regras-do-plano.md +33 -0
- package/templates/skills/nova-tarefa/templates/DECISOES.md +20 -0
- package/templates/skills/nova-tarefa/templates/PLAN.md +58 -0
- package/templates/skills/nova-tarefa/templates/SPEC.md +37 -0
- package/templates/skills/resolver-bug/SKILL.md +104 -0
- package/templates/skills/resolver-bug/references/relatorio-para-cliente.md +26 -0
- package/templates/skills/resolver-bug/templates/PLAN.md +62 -0
- package/templates/skills/resolver-bug/templates/relatorios/CLIENTE.md +29 -0
- package/templates/skills/resolver-bug/templates/relatorios/TECNICO.md +32 -0
- package/bin/cli.js +0 -122
- package/src/init.js +0 -317
- package/src/installer.js +0 -92
- package/src/run.js +0 -99
- package/templates/commands/auditar-performance.md +0 -27
- package/templates/commands/auditar-seguranca.md +0 -64
- package/templates/commands/auditar-ui.md +0 -38
- package/templates/commands/codigo-morto.md +0 -22
- package/templates/commands/configurar-auto-tag.md +0 -185
- package/templates/commands/configurar-comentarios.md +0 -543
- package/templates/commands/configurar-orquestrador.md +0 -13
- package/templates/commands/corrigir-review.md +0 -11
- package/templates/commands/criar-agents.md +0 -42
- package/templates/commands/criar-code-reviewer.md +0 -30
- package/templates/commands/criar-hooks.md +0 -32
- package/templates/commands/criar-rag.md +0 -25
- package/templates/commands/criar-rules.md +0 -31
- package/templates/commands/criar-skill.md +0 -22
- package/templates/commands/design-system.md +0 -27
- package/templates/commands/executar-plan.md +0 -36
- package/templates/commands/gerar-claude-md.md +0 -66
- package/templates/commands/gerar-ignore.md +0 -37
- package/templates/commands/gerar-spec.md +0 -33
- package/templates/commands/harness.md +0 -34
- package/templates/commands/nova-tarefa.md +0 -60
- package/templates/commands/resolver-bug.md +0 -25
- package/templates/commands/review-por-task.md +0 -16
- package/templates/commands/scaffold.md +0 -25
- package/templates/commands/validar-commands.md +0 -24
- package/templates/commands/validar-ideia.md +0 -36
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join, resolve } from 'node:path';
|
|
3
|
+
import { allCommands, loadCatalog } from '../catalog.js';
|
|
4
|
+
import { commentsHooksConfig, ensureCommentsBaseline } from '../comentarios/instalar.js';
|
|
5
|
+
import { readConfig, syncGitignore, writeDefaultConfig } from '../config/config.js';
|
|
6
|
+
import { applyAtomically } from '../install/atomic.js';
|
|
7
|
+
import { suggestFlag } from '../install/flags.js';
|
|
8
|
+
import { HOOKS_ITEM_ID, isHarnessPluginEnabled, projectHooksConfig } from '../install/hooks.js';
|
|
9
|
+
import { hashContent, LOCK_PATH, LOCK_VERSION, readLock, writeLock } from '../install/lock.js';
|
|
10
|
+
import { itemSources, packageInfo, PROJECT_RULE_PATH, projectRuleContent } from '../install/plan.js';
|
|
11
|
+
import { neededSkillsFor } from '../install/selection.js';
|
|
12
|
+
import { combineHooks, EVENT_PERMISSION, hasAllowRule, mergeSettings } from '../install/settings.js';
|
|
13
|
+
import { adoptLegacyInstall } from './legado.js';
|
|
14
|
+
/** `<id>-legado.md`, com sufixo numérico quando o nome já está em uso. */
|
|
15
|
+
function legacyPath(root, path) {
|
|
16
|
+
const base = path.replace(/\.md$/, '');
|
|
17
|
+
for (let n = 1;; n += 1) {
|
|
18
|
+
const candidate = `${base}-legado${n === 1 ? '' : `-${n}`}.md`;
|
|
19
|
+
if (!existsSync(join(root, candidate)))
|
|
20
|
+
return candidate;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function checkUpdate(root, catalog = loadCatalog(), legacy, home) {
|
|
24
|
+
const recorded = readLock(root);
|
|
25
|
+
const lock = recorded ?? adoptLegacyInstall(root, legacy);
|
|
26
|
+
if (!lock) {
|
|
27
|
+
throw new Error('Nenhuma instalação do ai-harness registrada neste projeto. Rode `npx @fontdata/ai-harness init` primeiro.');
|
|
28
|
+
}
|
|
29
|
+
const changes = [];
|
|
30
|
+
const stale = [];
|
|
31
|
+
for (const item of lock.itens) {
|
|
32
|
+
const key = `${item.tipo}:${item.id}`;
|
|
33
|
+
const sources = itemSources(catalog, item.tipo, item.id) ?? [];
|
|
34
|
+
const sourceByPath = new Map(sources.map((entry) => [entry.path, entry.source]));
|
|
35
|
+
for (const entry of item.arquivos) {
|
|
36
|
+
const local = join(root, entry.caminho);
|
|
37
|
+
const exists = existsSync(local);
|
|
38
|
+
const localChanged = exists && hashContent(readFileSync(local)) !== entry.sha256;
|
|
39
|
+
const source = sourceByPath.get(entry.caminho) ?? null;
|
|
40
|
+
if (!source) {
|
|
41
|
+
if (!exists)
|
|
42
|
+
stale.push(entry.caminho);
|
|
43
|
+
else if (!localChanged)
|
|
44
|
+
changes.push({ tipo: 'removido', caminho: entry.caminho, item: key, source });
|
|
45
|
+
// Comando editado que saiu do pacote: renomeado, para não sombrear a skill de mesmo nome (D-35).
|
|
46
|
+
else if (item.tipo === 'comando') {
|
|
47
|
+
changes.push({ tipo: 'legado', caminho: entry.caminho, item: key, source, destino: legacyPath(root, entry.caminho) });
|
|
48
|
+
}
|
|
49
|
+
else
|
|
50
|
+
changes.push({ tipo: 'conflito', caminho: entry.caminho, item: key, source });
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const packageChanged = hashContent(readFileSync(source)) !== entry.sha256;
|
|
54
|
+
const tipo = !exists
|
|
55
|
+
? 'faltando'
|
|
56
|
+
: packageChanged && localChanged
|
|
57
|
+
? 'conflito'
|
|
58
|
+
: packageChanged
|
|
59
|
+
? 'alterado'
|
|
60
|
+
: localChanged
|
|
61
|
+
? 'modificado-local'
|
|
62
|
+
: null;
|
|
63
|
+
if (tipo)
|
|
64
|
+
changes.push({ tipo, caminho: entry.caminho, item: key, source });
|
|
65
|
+
}
|
|
66
|
+
const trackedPaths = new Set(item.arquivos.map((entry) => entry.caminho));
|
|
67
|
+
for (const { path, source } of sources) {
|
|
68
|
+
if (trackedPaths.has(path))
|
|
69
|
+
continue;
|
|
70
|
+
changes.push({ tipo: existsSync(join(root, path)) ? 'conflito' : 'novo', caminho: path, item: key, source });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Itens que o lock não tem. Arquivo igual ao do pacote só entra no lock; `protect` mantém o do projeto mesmo com --forcar.
|
|
74
|
+
const trackedItems = new Set(lock.itens.map((item) => `${item.tipo}:${item.id}`));
|
|
75
|
+
const addItem = (tipo, id, protect) => {
|
|
76
|
+
const key = `${tipo}:${id}`;
|
|
77
|
+
if (trackedItems.has(key))
|
|
78
|
+
return;
|
|
79
|
+
trackedItems.add(key);
|
|
80
|
+
for (const { path, source } of itemSources(catalog, tipo, id) ?? []) {
|
|
81
|
+
const target = join(root, path);
|
|
82
|
+
const same = existsSync(target) && hashContent(readFileSync(target)) === hashContent(readFileSync(source));
|
|
83
|
+
const tipoMudanca = !existsSync(target) || same ? 'novo' : protect ? 'existente' : 'conflito';
|
|
84
|
+
changes.push({ tipo: tipoMudanca, caminho: path, item: key, source });
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
// Comando que virou skill, ou que foi incorporado a outro item (D-40 a D-43): quem o substitui entra como item
|
|
88
|
+
// novo, com as skills que exige.
|
|
89
|
+
const entries = [...allCommands(catalog), { ...catalog.master, group: '' }];
|
|
90
|
+
for (const item of lock.itens) {
|
|
91
|
+
if (item.tipo !== 'comando')
|
|
92
|
+
continue;
|
|
93
|
+
const successors = entries.filter((cmd) => (cmd.id === item.id && cmd.skill) || cmd.replaces?.includes(item.id));
|
|
94
|
+
for (const successor of successors) {
|
|
95
|
+
addItem(successor.skill ? 'skill' : 'comando', successor.id, false);
|
|
96
|
+
for (const skill of neededSkillsFor(catalog, [successor]))
|
|
97
|
+
addItem('skill', skill.id, false);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Na adoção, a instalação antiga ganha o que a 2.1 instala e ela não tinha.
|
|
101
|
+
const adotada = !recorded;
|
|
102
|
+
const pluginHooks = adotada && isHarnessPluginEnabled(root, home);
|
|
103
|
+
if (adotada) {
|
|
104
|
+
for (const agent of catalog.agents ?? [])
|
|
105
|
+
addItem('agent', agent.id, true);
|
|
106
|
+
if (!pluginHooks)
|
|
107
|
+
addItem('hook', HOOKS_ITEM_ID, true);
|
|
108
|
+
addItem('rule', 'comentarios', true);
|
|
109
|
+
addItem('hook', 'comentarios', true);
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
changes,
|
|
113
|
+
lock,
|
|
114
|
+
stale,
|
|
115
|
+
adotada,
|
|
116
|
+
registrarHooks: adotada && !pluginHooks,
|
|
117
|
+
registrarComentarios: adotada,
|
|
118
|
+
permissaoAusente: !hasAllowRule(root, EVENT_PERMISSION),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Aplica o relatório: grava novos, alterados e faltando; apaga órfãos intactos; renomeia
|
|
123
|
+
* comandos editados que saíram do pacote; conflitos só com `force`. Arquivos modificados
|
|
124
|
+
* localmente sem mudança no pacote e arquivos do projeto com nome de item do harness nunca
|
|
125
|
+
* são tocados. O lock é regravado com a versão atual do pacote.
|
|
126
|
+
*/
|
|
127
|
+
export function applyUpdate(root, report, options, pkg, now = new Date()) {
|
|
128
|
+
const write = [];
|
|
129
|
+
const remove = [];
|
|
130
|
+
const kept = [];
|
|
131
|
+
const legacy = [];
|
|
132
|
+
for (const change of report.changes) {
|
|
133
|
+
if (change.tipo === 'modificado-local')
|
|
134
|
+
continue;
|
|
135
|
+
if (change.tipo === 'legado') {
|
|
136
|
+
legacy.push(change.caminho);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (change.tipo === 'existente' || (change.tipo === 'conflito' && !options.force)) {
|
|
140
|
+
kept.push(change.caminho);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (change.source)
|
|
144
|
+
write.push({ path: change.caminho, content: readFileSync(change.source) });
|
|
145
|
+
else
|
|
146
|
+
remove.push(change.caminho);
|
|
147
|
+
}
|
|
148
|
+
applyAtomically(root, write);
|
|
149
|
+
for (const path of remove)
|
|
150
|
+
rmSync(join(root, path), { force: true });
|
|
151
|
+
const renamed = legacy.map((path) => {
|
|
152
|
+
const para = legacyPath(root, path);
|
|
153
|
+
renameSync(join(root, path), join(root, para));
|
|
154
|
+
return { de: path, para };
|
|
155
|
+
});
|
|
156
|
+
const hooks = combineHooks(report.registrarHooks ? projectHooksConfig() : {}, report.registrarComentarios ? commentsHooksConfig() : {});
|
|
157
|
+
mergeSettings(root, { hooks, allow: [EVENT_PERMISSION] }, now);
|
|
158
|
+
if (report.registrarComentarios)
|
|
159
|
+
ensureCommentsBaseline(root);
|
|
160
|
+
if (report.adotada) {
|
|
161
|
+
writeDefaultConfig(root);
|
|
162
|
+
syncGitignore(root, readConfig(root).config);
|
|
163
|
+
// Esqueleto da rule de projeto, com os comandos personalizados a extrair; uma rule existente nunca é tocada.
|
|
164
|
+
const projectRule = join(root, PROJECT_RULE_PATH);
|
|
165
|
+
if (!existsSync(projectRule)) {
|
|
166
|
+
mkdirSync(dirname(projectRule), { recursive: true });
|
|
167
|
+
writeFileSync(projectRule, projectRuleContent(renamed.map(({ para }) => para)));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const itemOf = new Map(report.changes.map((change) => [change.caminho, change.item]));
|
|
171
|
+
const writtenHash = new Map(write.map((file) => [file.path, hashContent(file.content)]));
|
|
172
|
+
const dropped = new Set([...remove, ...report.stale, ...legacy]);
|
|
173
|
+
const itens = [];
|
|
174
|
+
for (const item of report.lock.itens) {
|
|
175
|
+
const key = `${item.tipo}:${item.id}`;
|
|
176
|
+
const entries = new Map(item.arquivos.map((entry) => [entry.caminho, entry]));
|
|
177
|
+
for (const [path, sha256] of writtenHash) {
|
|
178
|
+
if (itemOf.get(path) === key)
|
|
179
|
+
entries.set(path, { caminho: path, sha256 });
|
|
180
|
+
}
|
|
181
|
+
for (const path of dropped)
|
|
182
|
+
entries.delete(path);
|
|
183
|
+
if (entries.size) {
|
|
184
|
+
itens.push({ ...item, arquivos: [...entries.values()].sort((a, b) => a.caminho.localeCompare(b.caminho)) });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// Itens que não existiam no lock (como a skill de um fluxo que era comando) entram com os arquivos gravados.
|
|
188
|
+
const knownItems = new Set(report.lock.itens.map((item) => `${item.tipo}:${item.id}`));
|
|
189
|
+
const addedItems = new Map();
|
|
190
|
+
for (const [path, sha256] of writtenHash) {
|
|
191
|
+
const key = itemOf.get(path);
|
|
192
|
+
if (!key || knownItems.has(key))
|
|
193
|
+
continue;
|
|
194
|
+
const separator = key.indexOf(':');
|
|
195
|
+
const added = addedItems.get(key) ?? { tipo: key.slice(0, separator), id: key.slice(separator + 1), arquivos: [] };
|
|
196
|
+
added.arquivos.push({ caminho: path, sha256 });
|
|
197
|
+
addedItems.set(key, added);
|
|
198
|
+
}
|
|
199
|
+
for (const added of addedItems.values()) {
|
|
200
|
+
itens.push({ ...added, arquivos: added.arquivos.sort((a, b) => a.caminho.localeCompare(b.caminho)) });
|
|
201
|
+
}
|
|
202
|
+
writeLock(root, { lock_versao: LOCK_VERSION, pacote: pkg, gerado_em: now.toISOString(), itens });
|
|
203
|
+
return { written: write.map((file) => file.path), removed: remove, kept, renamed };
|
|
204
|
+
}
|
|
205
|
+
const DESCRIPTIONS = {
|
|
206
|
+
novo: 'novo no pacote, será gravado',
|
|
207
|
+
alterado: 'mudou no pacote, será atualizado',
|
|
208
|
+
faltando: 'não existe no projeto, será restaurado',
|
|
209
|
+
removido: 'saiu do pacote, será apagado',
|
|
210
|
+
legado: 'editado localmente e saiu do pacote, será renomeado',
|
|
211
|
+
conflito: 'alterado localmente e no pacote, só com --forcar',
|
|
212
|
+
existente: 'já existe no projeto com outro conteúdo, será mantido',
|
|
213
|
+
'modificado-local': 'alterado localmente, será mantido',
|
|
214
|
+
};
|
|
215
|
+
export function describeUpdate(report) {
|
|
216
|
+
const adopted = report.adotada
|
|
217
|
+
? [
|
|
218
|
+
`Instalação ${report.lock.pacote.versao} sem lock, reconhecida pelos arquivos em .claude/. ` +
|
|
219
|
+
`O --yes grava o ${LOCK_PATH}, a configuração do harness${report.registrarHooks ? ', os hooks' : ''}, o padrão de comentários, ` +
|
|
220
|
+
'a rule de projeto e a permissão do `ai-harness evento`.',
|
|
221
|
+
]
|
|
222
|
+
: [];
|
|
223
|
+
const permission = report.permissaoAusente && !report.adotada ? [`Falta a permissão ${EVENT_PERMISSION} em .claude/settings.json; o --yes a acrescenta.`] : [];
|
|
224
|
+
if (!report.changes.length) {
|
|
225
|
+
const status = adopted.length || permission.length ? 'Nenhum arquivo a atualizar.' : 'A instalação está em dia: nenhum arquivo a atualizar.';
|
|
226
|
+
return [...adopted, ...permission, status].join('\n');
|
|
227
|
+
}
|
|
228
|
+
return [
|
|
229
|
+
...adopted,
|
|
230
|
+
...permission,
|
|
231
|
+
`Mudanças em relação ao pacote ${packageInfo().versao} (instalado: ${report.lock.pacote.versao}):`,
|
|
232
|
+
...report.changes.map((change) => ` ${change.tipo.padEnd(16)} ${change.caminho}${change.destino ? ` → ${change.destino}` : ''} (${DESCRIPTIONS[change.tipo]})`),
|
|
233
|
+
].join('\n');
|
|
234
|
+
}
|
|
235
|
+
const UPDATE_FLAGS = ['--check', '--yes', '-y', '--sim', '--forcar'];
|
|
236
|
+
export function parseUpdateArgs(argv) {
|
|
237
|
+
const args = { path: undefined, check: false, yes: false, force: false };
|
|
238
|
+
for (const token of argv) {
|
|
239
|
+
if (!token.startsWith('-')) {
|
|
240
|
+
if (args.path !== undefined)
|
|
241
|
+
throw new Error(`Argumento inesperado: ${token}`);
|
|
242
|
+
args.path = token;
|
|
243
|
+
}
|
|
244
|
+
else if (token === '--check')
|
|
245
|
+
args.check = true;
|
|
246
|
+
else if (token === '--forcar')
|
|
247
|
+
args.force = true;
|
|
248
|
+
else if (token === '--yes' || token === '-y' || token === '--sim')
|
|
249
|
+
args.yes = true;
|
|
250
|
+
else {
|
|
251
|
+
const suggestion = suggestFlag(token, UPDATE_FLAGS);
|
|
252
|
+
throw new Error(`Flag desconhecida: ${token}.${suggestion ? ` Você quis dizer ${suggestion}?` : ''}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return args;
|
|
256
|
+
}
|
|
257
|
+
/** `ai-harness update [caminho] [--check] [--yes] [--forcar]`; sem `--yes` só simula. */
|
|
258
|
+
export function runUpdateCommand(argv) {
|
|
259
|
+
const args = parseUpdateArgs(argv);
|
|
260
|
+
const root = resolve(process.cwd(), args.path ?? '.');
|
|
261
|
+
if (!existsSync(root))
|
|
262
|
+
throw new Error(`Pasta não encontrada: ${root}`);
|
|
263
|
+
const report = checkUpdate(root);
|
|
264
|
+
console.log(describeUpdate(report));
|
|
265
|
+
// Sem arquivo a atualizar, a adoção ainda grava o lock, e a permissão ausente ainda é acrescentada.
|
|
266
|
+
if (args.check || (!report.changes.length && !report.adotada && !report.permissaoAusente))
|
|
267
|
+
return 0;
|
|
268
|
+
if (!args.yes) {
|
|
269
|
+
const conflicts = report.changes.some((change) => change.tipo === 'conflito');
|
|
270
|
+
console.log(`\nSimulação — nada foi alterado. Rode de novo com --yes para aplicar${conflicts && !args.force ? ' (e --forcar para sobrescrever os conflitos)' : ''}.`);
|
|
271
|
+
return 0;
|
|
272
|
+
}
|
|
273
|
+
const result = applyUpdate(root, report, { force: args.force }, packageInfo());
|
|
274
|
+
console.log(`\nAtualização aplicada: ${result.written.length} gravado(s), ${result.removed.length} apagado(s), ` +
|
|
275
|
+
`${result.renamed.length} renomeado(s), ${result.kept.length} preservado(s).`);
|
|
276
|
+
if (result.renamed.length) {
|
|
277
|
+
console.log('Comandos personalizados renomeados (leve o que for do projeto para uma rule e apague o arquivo):\n' +
|
|
278
|
+
result.renamed.map(({ de, para }) => ` ${de} → ${para}`).join('\n'));
|
|
279
|
+
}
|
|
280
|
+
if (result.kept.length) {
|
|
281
|
+
console.log(`Preservados (conflito usa --forcar; arquivo do projeto nunca é sobrescrito):\n${result.kept.map((path) => ` = ${path}`).join('\n')}`);
|
|
282
|
+
}
|
|
283
|
+
return 0;
|
|
284
|
+
}
|
package/package.json
CHANGED
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fontdata/ai-harness",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Harness de engenharia com IA da Fontdata para Claude Code: roda os slash commands direto na pasta do projeto via npx, sem instalar nada — ou instala-os em .claude/commands.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"ai-harness": "
|
|
8
|
-
"fontdata-ai-harness": "
|
|
7
|
+
"ai-harness": "dist/cli.js",
|
|
8
|
+
"fontdata-ai-harness": "dist/cli.js"
|
|
9
9
|
},
|
|
10
10
|
"engines": {
|
|
11
|
-
"node": ">=
|
|
11
|
+
"node": ">=22"
|
|
12
12
|
},
|
|
13
13
|
"publishConfig": {
|
|
14
14
|
"access": "public"
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
17
17
|
".claude-plugin/",
|
|
18
|
-
"
|
|
19
|
-
"src/",
|
|
18
|
+
"dist/",
|
|
20
19
|
"templates/",
|
|
21
20
|
"README.md"
|
|
22
21
|
],
|
|
23
22
|
"scripts": {
|
|
24
|
-
"start": "node
|
|
23
|
+
"start": "node dist/cli.js",
|
|
24
|
+
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json",
|
|
25
|
+
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.scripts.json",
|
|
26
|
+
"test": "vitest run",
|
|
27
|
+
"prepublishOnly": "npm run typecheck && npm test && npm run build",
|
|
25
28
|
"version": "node scripts/sync-plugin-version.mjs && git add .claude-plugin/plugin.json"
|
|
26
29
|
},
|
|
27
30
|
"keywords": [
|
|
@@ -39,6 +42,12 @@
|
|
|
39
42
|
"url": "git+https://github.com/fontdata-tecnologia/ai-harness.git"
|
|
40
43
|
},
|
|
41
44
|
"dependencies": {
|
|
42
|
-
"@clack/prompts": "^0.7.0"
|
|
45
|
+
"@clack/prompts": "^0.7.0",
|
|
46
|
+
"yaml": "^2.9.1"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "^22.20.2",
|
|
50
|
+
"typescript": "^7.0.2",
|
|
51
|
+
"vitest": "^5.0.0"
|
|
43
52
|
}
|
|
44
53
|
}
|
package/templates/CLAUDE.md
CHANGED
|
@@ -1,49 +1,78 @@
|
|
|
1
1
|
# [NOME DO PROJETO]
|
|
2
|
-
> Substitua esta seção rodando `/
|
|
2
|
+
> Substitua esta seção rodando `/sincronizar-claude-md`, que gera o `SPEC.md` e este arquivo.
|
|
3
3
|
|
|
4
|
-
Este projeto usa o **harness de engenharia com IA da Fontdata** para Claude Code.
|
|
5
|
-
|
|
4
|
+
Este projeto usa o **harness de engenharia com IA da Fontdata** para Claude Code (`@fontdata/ai-harness`).
|
|
5
|
+
Comece por `/harness`, que conduz o setup em etapas.
|
|
6
6
|
|
|
7
7
|
## Filosofia de trabalho
|
|
8
8
|
|
|
9
|
-
- **Documentação como contrato.** `SPEC.md`
|
|
10
|
-
são a fonte da verdade. Agents não os reescrevem por conta própria
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
- **
|
|
14
|
-
|
|
15
|
-
- **
|
|
9
|
+
- **Documentação como contrato.** `SPEC.md` e `CLAUDE.md` na raiz e os artefatos de cada trabalho em
|
|
10
|
+
`docs/trabalhos/<id>/` são a fonte da verdade. Agents não os reescrevem por conta própria: mudança de escopo vira
|
|
11
|
+
decisão registrada.
|
|
12
|
+
- **Planejar antes de executar.** Pergunta durante a execução é falha do plano: dúvida vira bloqueio registrado.
|
|
13
|
+
- **TDD obrigatório.** Testes primeiro (RED), implementação mínima depois (GREEN). Mínimo de 2 testes críticos por
|
|
14
|
+
task; uma task só fecha com suíte verde registrada no rastro.
|
|
15
|
+
- **Quem implementa não aprova.** Os agents `revisor-testes` e `qa`, somente leitura, fazem os gates de cada task.
|
|
16
|
+
- **Orquestração com sub-agents.** Tasks independentes da mesma fase rodam em paralelo; o orquestrador delega, não
|
|
17
|
+
implementa. **Sempre relatar qual agent está ativo** no chat.
|
|
16
18
|
|
|
17
19
|
## Modelo de arquivos
|
|
18
20
|
|
|
19
|
-
| Arquivo |
|
|
20
|
-
|
|
21
|
-
| `SPEC.md`
|
|
22
|
-
| `CLAUDE.md`
|
|
23
|
-
| `docs/
|
|
24
|
-
| `docs/
|
|
25
|
-
| `docs/
|
|
26
|
-
| `docs/
|
|
21
|
+
| Arquivo | Papel |
|
|
22
|
+
|---|---|
|
|
23
|
+
| `SPEC.md` (raiz) | Definição **principal** e completa do projeto |
|
|
24
|
+
| `CLAUDE.md` (raiz) | Este context file, lido em toda sessão |
|
|
25
|
+
| `docs/trabalhos/AAAA-MM-DD-<slug>/SPEC.md` | Spec do trabalho |
|
|
26
|
+
| `docs/trabalhos/<id>/PLAN.md` | Plano: sprints, fases e tasks (as tasks também no frontmatter) |
|
|
27
|
+
| `docs/trabalhos/<id>/DECISOES.md` | Decisões tomadas no planejamento e na execução |
|
|
28
|
+
| `docs/trabalhos/<id>/BLOQUEIOS.md` | Dúvidas que pararam uma task |
|
|
29
|
+
| `docs/trabalhos/<id>/relatorios/TECNICO.md` e `relatorios/CLIENTE.md` | Relatório para engenharia e para cliente ou gestão |
|
|
30
|
+
| `docs/eventos/<id>.jsonl` | Rastro de eventos (não versionado por padrão) |
|
|
31
|
+
| `.ai-harness/` | Lock da instalação e configuração dos hooks (versionar) |
|
|
32
|
+
|
|
33
|
+
Tipos de trabalho: `feature` (`/nova-tarefa`), `bug` (`/resolver-bug`), `auditoria` (`/auditar-seguranca`,
|
|
34
|
+
`/auditar-performance`, `/auditar-ui`) e `investigacao`.
|
|
35
|
+
|
|
36
|
+
## Garantias do harness
|
|
37
|
+
|
|
38
|
+
- **Hooks** (modo de cada um em `.ai-harness/config.json`): `lembrete` sugere o fluxo certo para o pedido;
|
|
39
|
+
`segredo-no-commit` bloqueia commit com segredo; `proteger-contratos` avisa quando SPEC, PLAN ou CLAUDE.md são
|
|
40
|
+
reescritos durante a execução; `task-so-fecha-verde` avisa sobre task concluída sem suíte verde; `sem-jargao`
|
|
41
|
+
avisa sobre termo técnico no relatório para o cliente.
|
|
42
|
+
- **Agents somente leitura:** `revisor-testes`, `qa`, `investigador` e `auditor-plano`.
|
|
43
|
+
- **Verificação:** `npx @fontdata/ai-harness doctor` confere instalação, contrato dos documentos e rastro;
|
|
44
|
+
`npx @fontdata/ai-harness update` atualiza o que foi instalado sem perder alterações locais.
|
|
45
|
+
|
|
46
|
+
## Código legado
|
|
47
|
+
|
|
48
|
+
Na área de código legado tocada por uma task, vale o **padrão local** do arquivo — nomenclatura, estrutura e estilo
|
|
49
|
+
de tratamento de erro —, mesmo que difira das convenções novas. As convenções novas valem para arquivo novo. Não
|
|
50
|
+
"modernize" arquivo antigo fora do escopo da task.
|
|
27
51
|
|
|
28
52
|
## Fluxo recomendado (projeto novo)
|
|
29
53
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
54
|
+
`/harness` conduz o setup em etapas, pedindo confirmação entre elas (`/harness <etapa>` roda uma só):
|
|
55
|
+
|
|
56
|
+
1. Validar a ideia — o escopo cabe com multi-agents?
|
|
57
|
+
2. `/sincronizar-claude-md` — `SPEC.md` principal e este arquivo, com a seção de orquestração
|
|
58
|
+
3. Ignores — `.claudeignore` e `.gitignore`
|
|
59
|
+
4. `/nova-tarefa start` — trabalho do start com `SPEC.md` e `PLAN.md`
|
|
60
|
+
5. Scaffold — estrutura inicial, `/health` e testes de smoke
|
|
61
|
+
6. `/evoluir-harness` — skills, agents, code-reviewer, rules e hooks do projeto, e a rule
|
|
62
|
+
`.claude/rules/harness-projeto.md` preenchida
|
|
63
|
+
7. Auto-tag — Action que cria a tag quando a versão chega na branch principal (opcional)
|
|
37
64
|
8. `/executar-plan` — orquestra a implementação
|
|
38
65
|
|
|
39
|
-
No dia a dia: `/nova-tarefa`, `/resolver-bug`, `/git-commit-push`.
|
|
66
|
+
No dia a dia: `/nova-tarefa`, `/executar-plan`, `/resolver-bug`, `/git-commit-push` e `/versao`. De vez em quando:
|
|
67
|
+
`/sincronizar-claude-md` e `/evoluir-harness`.
|
|
40
68
|
|
|
41
69
|
## Nunca fazer
|
|
42
70
|
|
|
43
71
|
- Nunca implementar sem teste escrito antes.
|
|
44
|
-
- Nunca avançar de fase com gate (testes) falhando.
|
|
45
|
-
- Nunca reescrever `SPEC.md
|
|
72
|
+
- Nunca avançar de fase com gate (testes) falhando nem fechar task sem suíte verde.
|
|
73
|
+
- Nunca reescrever `SPEC.md`, `CLAUDE.md` ou planos sem pedido explícito.
|
|
46
74
|
- Nunca usar `git push --force` ou comandos destrutivos sem confirmação.
|
|
47
75
|
- Nunca comentar o passo a passo do código, decorar arquivo com banner de seção, nem escrever
|
|
48
|
-
task/fase/plano/data/hash de commit dentro de comentário — isso vai na mensagem de commit e em `docs/`.
|
|
49
|
-
|
|
76
|
+
task/fase/plano/data/hash de commit dentro de comentário — isso vai na mensagem de commit e em `docs/trabalhos/`.
|
|
77
|
+
O padrão completo está em `.claude/rules/comentarios.md`, instalada pelo `init`, e o hook de comentários bloqueia
|
|
78
|
+
a violação.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: auditor-plano
|
|
3
|
+
description: Audita um PLAN.md antes da execução, verificando dependências, paralelismo, testes críticos e critérios de aceite binários. Use depois do /nova-tarefa e antes do /executar-plan. Somente leitura.
|
|
4
|
+
tools: Read, Grep, Glob, Bash
|
|
5
|
+
model: opus
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Você é o **auditor de plano** do harness. Um plano aprovado por você precisa poder ser executado sem que
|
|
9
|
+
ninguém pergunte nada no meio. Você não edita o plano: aponta o que precisa mudar.
|
|
10
|
+
|
|
11
|
+
## Entrada
|
|
12
|
+
|
|
13
|
+
- `docs/trabalhos/<id>/SPEC.md` e `docs/trabalhos/<id>/PLAN.md`.
|
|
14
|
+
|
|
15
|
+
## Checklist
|
|
16
|
+
|
|
17
|
+
1. **Contrato:** rode `npx @fontdata/ai-harness doctor` e trate como BLOQUEANTE todo achado `contrato-*` deste
|
|
18
|
+
trabalho.
|
|
19
|
+
2. **Escopo:** o plano cobre a SPEC inteira e nada além dela.
|
|
20
|
+
3. **Testes críticos:** toda task de código tem pelo menos 2 testes concretos (entrada e resultado esperados), não
|
|
21
|
+
frases genéricas como "testar o fluxo".
|
|
22
|
+
4. **Critérios de aceite binários:** cada critério pode ser verificado com sim ou não, por comando ou observação.
|
|
23
|
+
5. **Dependências:** toda task que usa algo criado por outra declara `depende_de`; sem ciclos.
|
|
24
|
+
6. **Paralelismo:** tasks `paralelizavel: true` da mesma fase não alteram os mesmos arquivos.
|
|
25
|
+
7. **Tamanho:** cada task cabe numa sessão de trabalho; task grande demais é dividida.
|
|
26
|
+
8. **Legado e riscos:** áreas de código legado tocadas estão identificadas, com o padrão local a preservar.
|
|
27
|
+
|
|
28
|
+
Use `Bash` só para rodar o `doctor` e ler o histórico: **nunca** altere arquivos, faça commit ou push.
|
|
29
|
+
|
|
30
|
+
## Saída
|
|
31
|
+
|
|
32
|
+
- **Veredito:** `APROVADO` ou `REPROVADO`.
|
|
33
|
+
- **Achados** em BLOQUEANTE, IMPORTANTE e SUGESTÃO, cada um apontando a task (`T-SS.NN`) e a correção esperada.
|
|
34
|
+
- O valor para o orquestrador gravar em `auditoria_plano` no frontmatter do `PLAN.md` (`aprovado` ou
|
|
35
|
+
`reprovado`).
|
|
36
|
+
|
|
37
|
+
Registre o veredito no rastro:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npx @fontdata/ai-harness evento agent_veredito --trabalho <id> --nome auditor-plano \
|
|
41
|
+
--dados '{"veredito":"aprovado","motivo":"<resumo em uma linha>"}'
|
|
42
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: investigador
|
|
3
|
+
description: Investiga a causa raiz de um bug antes de qualquer correção. Use no início do /resolver-bug, com o relato do sintoma. Lê código, histórico e logs e reproduz o problema; não edita nem roda comandos que alterem estado.
|
|
4
|
+
tools: Read, Grep, Glob, Bash
|
|
5
|
+
model: sonnet
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Você é o **investigador** do harness. Seu trabalho termina quando a causa raiz está comprovada com evidência,
|
|
9
|
+
ou quando ficou claro que ela ainda é hipótese. Você não corrige o bug.
|
|
10
|
+
|
|
11
|
+
## Como investigar
|
|
12
|
+
|
|
13
|
+
1. **Reproduza o sintoma.** Um teste, um comando ou os passos exatos. Sem reprodução, diga isso explicitamente.
|
|
14
|
+
2. **Levante hipóteses** e, para cada uma, busque evidência que a confirme ou a descarte:
|
|
15
|
+
- leitura do código envolvido;
|
|
16
|
+
- histórico: `git log -p -S "<trecho>"`, `git log --follow <arquivo>`, `git blame -L <ini>,<fim> <arquivo>`;
|
|
17
|
+
- trabalhos anteriores que tocaram os mesmos arquivos (`docs/trabalhos/*/` e o campo `arquivos` do
|
|
18
|
+
frontmatter).
|
|
19
|
+
3. **Comprove.** A causa raiz só é `comprovada` quando você mostra a linha ou a condição que produz o sintoma e
|
|
20
|
+
por que a reprodução falha por causa dela. Caso contrário, ela é `hipotese`.
|
|
21
|
+
4. Use `Bash` só para ler, buscar no histórico e reproduzir: **nunca** altere arquivos, instale dependências,
|
|
22
|
+
faça commit ou push.
|
|
23
|
+
|
|
24
|
+
## Saída
|
|
25
|
+
|
|
26
|
+
- **Sintoma** e **como reproduzir**.
|
|
27
|
+
- **Causa raiz:** `comprovada` ou `hipotese`, com a evidência (`arquivo:linha`, trecho do histórico, saída da
|
|
28
|
+
reprodução).
|
|
29
|
+
- **Arquivos envolvidos**, para o campo `arquivos` do plano.
|
|
30
|
+
- **Riscos da correção:** o que mais depende do trecho afetado.
|
|
31
|
+
|
|
32
|
+
Não proponha refatorações amplas: a correção mínima fica para o plano do `/resolver-bug`.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: qa
|
|
3
|
+
description: Valida os critérios de aceite de uma task ou fase e roda a suíte completa antes do gate. Use ao fim de cada task ou fase do /executar-plan. Somente leitura; não corrige o que encontrar.
|
|
4
|
+
tools: Read, Grep, Glob, Bash
|
|
5
|
+
model: sonnet
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Você é o **QA** do harness. Você confirma, com evidência, que a task ou a fase entregou o que o plano promete.
|
|
9
|
+
Você não corrige nada: se algo falha, você reporta.
|
|
10
|
+
|
|
11
|
+
## Entrada
|
|
12
|
+
|
|
13
|
+
- O id do trabalho e a task (ou fase) a validar, em `docs/trabalhos/<id>/PLAN.md`.
|
|
14
|
+
- Os critérios de aceite e os testes críticos declarados no plano.
|
|
15
|
+
- A rule de projeto `.claude/rules/harness-projeto.md`, quando existir: comando e local da suíte e, para área sem
|
|
16
|
+
testes automatizados, o roteiro de verificação manual.
|
|
17
|
+
|
|
18
|
+
## Como validar
|
|
19
|
+
|
|
20
|
+
1. Para cada critério de aceite, obtenha **evidência objetiva**: saída de comando, `arquivo:linha` ou resultado
|
|
21
|
+
de teste. Critério sem evidência conta como não atendido.
|
|
22
|
+
2. Rode a **suíte completa** do projeto, não só os testes da task, com o comando declarado na rule de projeto quando
|
|
23
|
+
houver. Em área de verificação manual, siga o roteiro da rule e colete evidência de cada item.
|
|
24
|
+
3. Confira que nada fora do escopo da task foi alterado (`git diff --stat`).
|
|
25
|
+
4. Use `Bash` só para ler e rodar testes: **nunca** altere arquivos, instale dependências, faça commit ou push.
|
|
26
|
+
|
|
27
|
+
## Saída
|
|
28
|
+
|
|
29
|
+
- **Veredito:** `APROVADO` ou `REPROVADO`.
|
|
30
|
+
- Tabela: critério → atendido (sim/não) → evidência.
|
|
31
|
+
- Resultado da suíte: comando, total de testes e falhas.
|
|
32
|
+
|
|
33
|
+
Registre a suíte e o veredito no rastro:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npx @fontdata/ai-harness evento suite_verde --trabalho <id> --task <T-SS.NN> --nome qa --resultado ok \
|
|
37
|
+
--dados '{"comando":"<comando>","total":<n>,"falhas":0}'
|
|
38
|
+
npx @fontdata/ai-harness evento agent_veredito --trabalho <id> --task <T-SS.NN> --nome qa \
|
|
39
|
+
--dados '{"veredito":"aprovado","motivo":"<resumo em uma linha>"}'
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Com falhas, use `suite_vermelha` e `--resultado falha`. Na verificação manual, registre `suite_verde` com
|
|
43
|
+
`"comando":"manual"`, `total` igual ao número de itens do roteiro e `falhas` igual a 0; com item reprovado,
|
|
44
|
+
`suite_vermelha`.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: revisor-testes
|
|
3
|
+
description: Revisa os testes de uma task antes de ela fechar, perguntando se cada teste crítico passaria com a implementação errada. Use quando o implementador disser que a task está pronta. Somente leitura; roda a suíte, não edita.
|
|
4
|
+
tools: Read, Grep, Glob, Bash
|
|
5
|
+
model: sonnet
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Você é o **revisor de testes** do harness. Você não escreve nem corrige código: julga se os testes provam o
|
|
9
|
+
comportamento que a task promete.
|
|
10
|
+
|
|
11
|
+
## Entrada
|
|
12
|
+
|
|
13
|
+
- O id do trabalho e da task. Os testes críticos declarados estão no frontmatter de
|
|
14
|
+
`docs/trabalhos/<id>/PLAN.md` (`tasks[].testes_criticos`).
|
|
15
|
+
- Os arquivos alterados na task (`git diff` ou a lista que o orquestrador passar).
|
|
16
|
+
|
|
17
|
+
## Como revisar
|
|
18
|
+
|
|
19
|
+
1. Localize no código de teste cada teste crítico declarado. **Teste declarado e ausente é BLOQUEANTE.**
|
|
20
|
+
2. Para cada teste, responda: **"este teste passaria com uma implementação errada?"** Procure:
|
|
21
|
+
- asserção fraca (só "não lança", só status 200, `toBeTruthy` onde cabia o valor);
|
|
22
|
+
- mock que substitui justamente o que deveria ser testado;
|
|
23
|
+
- só o caminho feliz, sem o caso de erro ou de borda que a task menciona;
|
|
24
|
+
- dado de entrada que coincide com o resultado esperado por acaso.
|
|
25
|
+
3. Rode a suíte com o comando declarado em `.claude/rules/harness-projeto.md` ou, sem ele, com o comando de teste
|
|
26
|
+
do projeto (`npm test`, `dotnet test`, `pytest`...), e anote o resultado. Em área de verificação manual declarada
|
|
27
|
+
na rule, revise se o roteiro cobre os critérios da task.
|
|
28
|
+
4. Use `Bash` só para ler e rodar testes: **nunca** altere arquivos, instale dependências, faça commit ou push.
|
|
29
|
+
|
|
30
|
+
## Saída
|
|
31
|
+
|
|
32
|
+
- **Veredito:** `APROVADO` ou `REPROVADO`.
|
|
33
|
+
- **Achados** em BLOQUEANTE, IMPORTANTE e SUGESTÃO, cada um com `arquivo:linha` e o motivo.
|
|
34
|
+
|
|
35
|
+
Registre o veredito no rastro:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npx @fontdata/ai-harness evento agent_veredito --trabalho <id> --task <T-SS.NN> --nome revisor-testes \
|
|
39
|
+
--dados '{"veredito":"aprovado","motivo":"<resumo em uma linha>"}'
|
|
40
|
+
```
|