@jaimevalasek/aioson 1.23.3 → 1.28.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.
Files changed (46) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/docs/en/4-agents/README.md +11 -8
  3. package/docs/en/4-agents/forge-run.md +165 -0
  4. package/docs/en/5-reference/README.md +1 -0
  5. package/docs/en/5-reference/cli-reference.md +199 -85
  6. package/docs/en/5-reference/executable-verification.md +165 -0
  7. package/docs/pt/4-agentes/README.md +2 -1
  8. package/docs/pt/4-agentes/forge-run.md +150 -0
  9. package/docs/pt/4-agentes/pm.md +8 -0
  10. package/docs/pt/4-agentes/qa.md +2 -0
  11. package/docs/pt/4-agentes/scope-check.md +19 -1
  12. package/docs/pt/4-agentes/sheldon.md +2 -0
  13. package/docs/pt/4-agentes/validator.md +20 -0
  14. package/docs/pt/5-referencia/autopilot-handoff.md +33 -0
  15. package/docs/pt/5-referencia/comandos-cli.md +64 -9
  16. package/docs/pt/5-referencia/fluxo-artefatos.md +40 -15
  17. package/docs/pt/5-referencia/loop-guardrails.md +19 -0
  18. package/docs/pt/5-referencia/sdd-automation-scripts.md +130 -26
  19. package/package.json +1 -1
  20. package/src/cli.js +70 -54
  21. package/src/commands/forge-compile.js +330 -0
  22. package/src/commands/harness-check.js +159 -0
  23. package/src/commands/harness.js +37 -2
  24. package/src/commands/spec-analyze.js +324 -0
  25. package/src/constants.js +118 -108
  26. package/src/harness/contract-schema.js +8 -0
  27. package/src/harness/plan-waves.js +77 -0
  28. package/src/harness/review-payload.js +230 -0
  29. package/src/i18n/messages/en.js +21 -15
  30. package/src/i18n/messages/es.js +15 -13
  31. package/src/i18n/messages/fr.js +15 -13
  32. package/src/i18n/messages/pt-BR.js +21 -15
  33. package/src/parser.js +3 -1
  34. package/template/.aioson/agents/dev.md +67 -66
  35. package/template/.aioson/agents/forge-run.md +57 -0
  36. package/template/.aioson/agents/pm.md +51 -45
  37. package/template/.aioson/agents/qa.md +22 -22
  38. package/template/.aioson/agents/scope-check.md +49 -46
  39. package/template/.aioson/agents/sheldon.md +1 -1
  40. package/template/.aioson/agents/validator.md +16 -5
  41. package/template/.aioson/docs/autopilot-handoff.md +34 -32
  42. package/template/.aioson/docs/sheldon/harness-contract.md +19 -2
  43. package/template/.claude/commands/aioson/agent/forge-run.md +17 -0
  44. package/template/AGENTS.md +15 -13
  45. package/template/CLAUDE.md +10 -9
  46. package/template/OPENCODE.md +24 -23
@@ -0,0 +1,77 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Parser da tabela "Execution Sequence" do implementation-plan-{slug}.md
5
+ * (convenção Wave do @pm — Fase 4/5 do plano de verificação executável).
6
+ *
7
+ * Compartilhado entre `spec:analyze` (check wave_file_overlap) e
8
+ * `forge:compile` (compilação spec → workflow script). Sem coluna Wave a
9
+ * função retorna null — chamadores tratam como "convenção ausente"
10
+ * (retrocompat com planos antigos).
11
+ */
12
+
13
+ /**
14
+ * @param {string} content — markdown do implementation-plan
15
+ * @returns {Array<{phase, wave, files: string[], scope, done}>|null}
16
+ */
17
+ function parseExecutionWaves(content) {
18
+ const lines = String(content || '').split(/\r?\n/);
19
+ let columns = null;
20
+ const rows = [];
21
+
22
+ for (const line of lines) {
23
+ const trimmed = line.trim();
24
+ if (!trimmed.startsWith('|')) {
25
+ if (columns && rows.length) break; // fim da tabela alvo
26
+ columns = columns && rows.length === 0 ? columns : null;
27
+ continue;
28
+ }
29
+ const cells = trimmed.split('|').slice(1, -1).map((c) => c.trim());
30
+ const lower = cells.map((c) => c.toLowerCase());
31
+
32
+ if (!columns) {
33
+ if (lower.includes('wave') && lower.some((c) => c.includes('phase')) && lower.some((c) => c.includes('file'))) {
34
+ columns = {
35
+ phase: lower.findIndex((c) => c.includes('phase')),
36
+ wave: lower.indexOf('wave'),
37
+ files: lower.findIndex((c) => c.includes('file')),
38
+ scope: lower.findIndex((c) => c.includes('scope')),
39
+ done: lower.findIndex((c) => c.includes('done'))
40
+ };
41
+ }
42
+ continue;
43
+ }
44
+
45
+ if (cells.every((c) => /^:?-{2,}:?$/.test(c))) continue; // separador
46
+
47
+ const wave = parseInt(cells[columns.wave], 10);
48
+ if (!Number.isInteger(wave)) continue;
49
+ const files = (cells[columns.files] || '')
50
+ .split(/,|<br\s*\/?\s*>/i)
51
+ .map((f) => f.replace(/`/g, '').trim().replace(/\\/g, '/').toLowerCase())
52
+ .filter((f) => f && !/^(\.{3}|-|—)$/.test(f));
53
+ rows.push({
54
+ phase: cells[columns.phase] || `row ${rows.length + 1}`,
55
+ wave,
56
+ files,
57
+ scope: columns.scope >= 0 ? (cells[columns.scope] || '') : '',
58
+ done: columns.done >= 0 ? (cells[columns.done] || '') : ''
59
+ });
60
+ }
61
+
62
+ return columns ? rows : null;
63
+ }
64
+
65
+ /** Agrupa as fases por wave, em ordem ascendente. */
66
+ function groupByWave(rows) {
67
+ const byWave = new Map();
68
+ for (const row of rows || []) {
69
+ if (!byWave.has(row.wave)) byWave.set(row.wave, []);
70
+ byWave.get(row.wave).push(row);
71
+ }
72
+ return [...byWave.entries()]
73
+ .sort((a, b) => a[0] - b[0])
74
+ .map(([wave, phases]) => ({ wave, phases }));
75
+ }
76
+
77
+ module.exports = { parseExecutionWaves, groupByWave };
@@ -0,0 +1,230 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Payload de revisão fresh-context para o prompt do @validator (Fase 2 do
5
+ * plano de verificação executável).
6
+ *
7
+ * `harness:validate` gera o prompt do @validator headless; este módulo anexa
8
+ * a ele tudo que um contexto ISOLADO precisa para validar sem herdar o
9
+ * histórico da sessão que implementou: diff vs base, lista de arquivos
10
+ * alterados (incluindo untracked), resultado dos checks determinísticos
11
+ * (last-check-output.json) e a instrução de onde gravar o veredito JSON.
12
+ *
13
+ * Nunca lança: fora de um repo git (ou em falha de I/O) retorna um payload
14
+ * degradado com a nota correspondente — os testes do router rodam em tmpdir
15
+ * sem git e devem continuar passando.
16
+ */
17
+
18
+ const fs = require('node:fs');
19
+ const path = require('node:path');
20
+ const { execFileSync } = require('node:child_process');
21
+
22
+ const { parsePorcelain } = require('./git-baseline');
23
+ const { matchGlob } = require('./glob-match');
24
+
25
+ /** Estado do framework não é superfície de revisão (mesmo precedente do git-baseline). */
26
+ const FRAMEWORK_STATE_GLOB = '.aioson/**';
27
+
28
+ const DEFAULT_MAX_DIFF_BYTES = 200000;
29
+
30
+ function git(targetDir, gitArgs) {
31
+ return execFileSync('git', gitArgs, {
32
+ cwd: targetDir,
33
+ encoding: 'utf8',
34
+ maxBuffer: 1024 * 1024 * 20,
35
+ stdio: ['ignore', 'pipe', 'pipe']
36
+ });
37
+ }
38
+
39
+ /**
40
+ * Resolve a ref base do diff, na ordem:
41
+ * 1. `baseRef` explícito (--base)
42
+ * 2. `baseline.json` do plan dir (HEAD capturado no preflight do self:loop)
43
+ * 3. merge-base de HEAD com main/master (local)
44
+ * 4. 'HEAD' (apenas mudanças não commitadas)
45
+ *
46
+ * @returns {{ base: string, source: string }}
47
+ */
48
+ function resolveBase(targetDir, planDir, baseRef) {
49
+ if (baseRef) return { base: String(baseRef), source: 'explicit --base' };
50
+
51
+ try {
52
+ const baselinePath = path.join(planDir, 'baseline.json');
53
+ if (fs.existsSync(baselinePath)) {
54
+ const baseline = JSON.parse(fs.readFileSync(baselinePath, 'utf8'));
55
+ if (baseline && baseline.head) return { base: baseline.head, source: 'baseline.json (loop preflight)' };
56
+ }
57
+ } catch { /* baseline ilegível — segue o fallback */ }
58
+
59
+ for (const branch of ['main', 'master']) {
60
+ try {
61
+ const mergeBase = git(targetDir, ['merge-base', 'HEAD', branch]).trim();
62
+ const head = git(targetDir, ['rev-parse', 'HEAD']).trim();
63
+ // merge-base === HEAD significa que estamos NO branch (ou atrás dele):
64
+ // o diff vs base seria vazio; cai para HEAD (mudanças não commitadas).
65
+ if (mergeBase && mergeBase !== head) return { base: mergeBase, source: `merge-base with ${branch}` };
66
+ } catch { /* branch ausente — tenta o próximo */ }
67
+ }
68
+
69
+ return { base: 'HEAD', source: 'fallback (uncommitted changes only)' };
70
+ }
71
+
72
+ /** Trunca em fronteira de linha com marcador; bytes UTF-8. */
73
+ function truncateDiff(diff, maxBytes) {
74
+ const bytes = Buffer.byteLength(diff, 'utf8');
75
+ if (bytes <= maxBytes) return { diff, truncated: false, bytes };
76
+ let slice = Buffer.from(diff, 'utf8').subarray(0, maxBytes).toString('utf8');
77
+ // remove eventual caractere multibyte cortado e fecha na última linha completa
78
+ slice = slice.replace(/�+$/g, '');
79
+ const lastNewline = slice.lastIndexOf('\n');
80
+ if (lastNewline > 0) slice = slice.slice(0, lastNewline);
81
+ return {
82
+ diff: `${slice}\n... [diff truncated at ${maxBytes} bytes of ${bytes} — read the changed files directly for the rest]`,
83
+ truncated: true,
84
+ bytes
85
+ };
86
+ }
87
+
88
+ /** Sumariza last-check-output.json em linhas de texto; null quando ausente. */
89
+ function summarizeCheckOutput(planDir) {
90
+ const checkPath = path.join(planDir, 'last-check-output.json');
91
+ if (!fs.existsSync(checkPath)) return null;
92
+ try {
93
+ const report = JSON.parse(fs.readFileSync(checkPath, 'utf8'));
94
+ const lines = [
95
+ `Ran at: ${report.checked_at} — ${report.passed}/${report.executable_total} executable checks passed, ${report.skipped_no_verification} criteria without verification.`
96
+ ];
97
+ for (const check of report.checks || []) {
98
+ lines.push(`- ${check.ok ? 'PASS' : 'FAIL'} ${check.id} — \`${check.command}\` (exit ${check.exitCode}${check.timedOut ? ', timeout' : ''})`);
99
+ }
100
+ lines.push('Copy these exit-code verdicts verbatim into `results[].passed` for their criteria — do not re-judge them.');
101
+ return lines.join('\n');
102
+ } catch {
103
+ return null;
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Monta o payload de revisão. Nunca lança.
109
+ *
110
+ * @param {string} targetDir — raiz do projeto
111
+ * @param {string} planDir — .aioson/plans/{slug}
112
+ * @param {object} opts
113
+ * @param {string} [opts.slug]
114
+ * @param {string} [opts.baseRef] — ref explícita (--base)
115
+ * @param {number} [opts.maxDiffBytes]
116
+ * @param {string} [opts.outputPath] — onde o validator grava o JSON
117
+ * @returns {{ ok, base, baseSource, changedFiles, untracked, truncated, diffBytes, hasChecks, text }}
118
+ */
119
+ function buildReviewPayload(targetDir, planDir, opts = {}) {
120
+ const maxDiffBytes = Number.isInteger(Number(opts.maxDiffBytes)) && Number(opts.maxDiffBytes) > 0
121
+ ? Number(opts.maxDiffBytes)
122
+ : DEFAULT_MAX_DIFF_BYTES;
123
+ const checkSummary = summarizeCheckOutput(planDir);
124
+
125
+ const header = [
126
+ '',
127
+ '---',
128
+ '',
129
+ '## Review payload (generated by `aioson harness:validate`)',
130
+ '',
131
+ '> Run this prompt in a **fresh, isolated context** (subagent/Task tool or a separate session). Never validate inline in the session that implemented the feature — the implementation history biases the verdict.',
132
+ ''
133
+ ];
134
+
135
+ const outputBlock = [
136
+ '### Verdict output',
137
+ '',
138
+ `Write **only** the validator JSON verdict${opts.outputPath ? ` to: \`${opts.outputPath}\`` : ''}.`,
139
+ opts.slug ? `The orchestrating session then consumes it with: \`aioson harness:validate . --slug=${opts.slug}\`` : '',
140
+ ''
141
+ ].filter((line) => line !== '');
142
+
143
+ const checksBlock = [
144
+ '### Deterministic check results (`aioson harness:check`)',
145
+ '',
146
+ checkSummary || 'No `last-check-output.json` found — run `aioson harness:check` first (criteria with `verification` must be decided by exit code, not judgment).',
147
+ ''
148
+ ];
149
+
150
+ let gitFailed = false;
151
+ let base = null;
152
+ let baseSource = null;
153
+ let changedFiles = [];
154
+ let untracked = [];
155
+ let diffResult = { diff: '', truncated: false, bytes: 0 };
156
+
157
+ try {
158
+ const resolved = resolveBase(targetDir, planDir, opts.baseRef);
159
+ base = resolved.base;
160
+ baseSource = resolved.source;
161
+
162
+ const nameStatus = git(targetDir, ['diff', '--name-status', base]);
163
+ changedFiles = nameStatus
164
+ .split('\n')
165
+ .map((line) => line.trim())
166
+ .filter(Boolean)
167
+ .map((line) => {
168
+ const [status, ...rest] = line.split('\t');
169
+ return { status, path: rest.join('\t') };
170
+ });
171
+
172
+ untracked = parsePorcelain(git(targetDir, ['status', '--porcelain', '-uall']))
173
+ .filter((entry) => entry.status === 'added' && !matchGlob(FRAMEWORK_STATE_GLOB, entry.path))
174
+ .map((entry) => entry.path);
175
+
176
+ diffResult = truncateDiff(git(targetDir, ['diff', base]), maxDiffBytes);
177
+ } catch {
178
+ gitFailed = true;
179
+ }
180
+
181
+ let diffBlock;
182
+ if (gitFailed) {
183
+ diffBlock = [
184
+ '### Diff under review',
185
+ '',
186
+ 'Diff unavailable (not a git repository or git failed). Review the files listed in `progress.json.completed_steps` directly.',
187
+ ''
188
+ ];
189
+ } else {
190
+ const fileLines = [
191
+ ...changedFiles.map((f) => `- ${f.status} ${f.path}`),
192
+ ...untracked.map((p) => `- ?? ${p} (untracked)`)
193
+ ];
194
+ diffBlock = [
195
+ `### Changed files vs base \`${base}\` (${baseSource})`,
196
+ '',
197
+ fileLines.length ? fileLines.join('\n') : '(no changes detected)',
198
+ '',
199
+ '### Unified diff',
200
+ '',
201
+ 'Untracked files do not appear in the diff below — read them directly.',
202
+ '',
203
+ '```diff',
204
+ diffResult.diff || '(empty diff)',
205
+ '```',
206
+ ''
207
+ ];
208
+ }
209
+
210
+ const text = [...header, ...checksBlock, ...diffBlock, ...outputBlock].join('\n');
211
+
212
+ return {
213
+ ok: !gitFailed,
214
+ base,
215
+ baseSource,
216
+ changedFiles,
217
+ untracked,
218
+ truncated: diffResult.truncated,
219
+ diffBytes: diffResult.bytes,
220
+ hasChecks: Boolean(checkSummary),
221
+ text
222
+ };
223
+ }
224
+
225
+ module.exports = {
226
+ DEFAULT_MAX_DIFF_BYTES,
227
+ resolveBase,
228
+ truncateDiff,
229
+ buildReviewPayload
230
+ };
@@ -21,16 +21,16 @@ module.exports = {
21
21
  'aioson agent:prompt <agent> [path] [--tool=codex|claude|opencode] [--lang=<bcp47-tag>] [--locale=en]',
22
22
  help_agent_help:
23
23
  'aioson agent:help [agent] [--json]',
24
- help_agent_invoke:
25
- 'aioson agent:invoke <agent> [path] [--tool=codex|claude|opencode] [--mode=framework_target|app_target] [--feature=<slug>] [--scope=<area>] [--lang=<bcp47-tag>] [--locale=en]',
26
- help_agent_epilogue:
27
- 'aioson agent:epilogue [path] --agent=<agent> --summary=<text> [--feature=<slug>] [--approve-gate=A|B|C|D] [--json] [--locale=en]',
24
+ help_agent_invoke:
25
+ 'aioson agent:invoke <agent> [path] [--tool=codex|claude|opencode] [--mode=framework_target|app_target] [--feature=<slug>] [--scope=<area>] [--lang=<bcp47-tag>] [--locale=en]',
26
+ help_agent_epilogue:
27
+ 'aioson agent:epilogue [path] --agent=<agent> --summary=<text> [--feature=<slug>] [--approve-gate=A|B|C|D] [--json] [--locale=en]',
28
28
  help_context_validate: 'aioson context:validate [path] [--json] [--locale=en]',
29
- help_context_pack:
30
- 'aioson context:pack [path] [--agent=<agent>] [--goal=<text>] [--module=<module-or-folder>] [--max-files=8] [--json] [--locale=en]',
31
- help_context_select:
32
- 'aioson context:select [path] [--agent=<agent>] [--mode=planning|executing] [--task=<text>] [--paths=<path[,path2]>] [--feature=<slug>] [--json] [--locale=en]',
33
- help_context_load:
29
+ help_context_pack:
30
+ 'aioson context:pack [path] [--agent=<agent>] [--goal=<text>] [--module=<module-or-folder>] [--max-files=8] [--json] [--locale=en]',
31
+ help_context_select:
32
+ 'aioson context:select [path] [--agent=<agent>] [--mode=planning|executing] [--task=<text>] [--paths=<path[,path2]>] [--feature=<slug>] [--json] [--locale=en]',
33
+ help_context_load:
34
34
  'aioson context:load [path] --target=<rule|brain>:<slug> --agent=<name> [--batch="slug1,slug2"] [--feature=<slug>] [--classification=<MICRO|SMALL|MEDIUM>] [--verbose] [--json] [--locale=en]',
35
35
  help_chain_audit:
36
36
  'aioson chain:audit <file> [path] [--limit=N] [--feature=<slug>] [--json] [--locale=en]',
@@ -122,10 +122,10 @@ module.exports = {
122
122
  'aioson workflow:next [path] [--complete[=<agent>]] [--agent=<agent>] [--skip=<agent>] [--status] [--suggest] [--tool=codex|claude|opencode] [--json] [--locale=en]',
123
123
  help_workflow_status:
124
124
  'aioson workflow:status [path] [--suggest] [--tool=codex|claude|opencode] [--json] [--locale=en]',
125
- help_workflow_execute:
126
- 'aioson workflow:execute [path] [--feature=<slug>] [--agentic] [--max-dev-qa-cycles=<n>] [--max-tester-cycles=<n>] [--max-pentester-cycles=<n>] [--dry-run] [--lane=<n>] [--json] [--locale=en]',
127
- help_review_cycle:
128
- 'aioson review-cycle:<status|advance|resolve|reset> [path] --feature=<slug> [--plan=<path>] [--source=qa|tester|pentester] [--json] [--locale=en]',
125
+ help_workflow_execute:
126
+ 'aioson workflow:execute [path] [--feature=<slug>] [--agentic] [--max-dev-qa-cycles=<n>] [--max-tester-cycles=<n>] [--max-pentester-cycles=<n>] [--dry-run] [--lane=<n>] [--json] [--locale=en]',
127
+ help_review_cycle:
128
+ 'aioson review-cycle:<status|advance|resolve|reset> [path] --feature=<slug> [--plan=<path>] [--source=qa|tester|pentester] [--json] [--locale=en]',
129
129
  help_parallel_init:
130
130
  'aioson parallel:init [path] [--workers=2..6] [--force] [--dry-run] [--json] [--locale=en]',
131
131
  help_parallel_doctor:
@@ -155,7 +155,9 @@ module.exports = {
155
155
  help_harness_init:
156
156
  'aioson harness:init [path] --slug=<slug> [--mode=BALANCED|URGENT|ECONOMICAL] [--locale=en]',
157
157
  help_harness_validate:
158
- 'aioson harness:validate [path] --slug=<slug> [--artifact=<path>] [--locale=en]',
158
+ 'aioson harness:validate [path] --slug=<slug> [--base=<ref>] [--no-diff] [--max-diff-bytes=<n>] [--artifact=<path>] [--locale=en]',
159
+ help_harness_check:
160
+ 'aioson harness:check [path] --slug=<slug> [--criteria=C1,C2] [--timeout=<ms>] [--json] [--locale=en]',
159
161
  help_harness_retro:
160
162
  'aioson harness:retro [path] --feature=<slug> | --last=<N> [--json] [--locale=en]',
161
163
  help_harness_preview:
@@ -1082,7 +1084,11 @@ module.exports = {
1082
1084
  contract_not_found: 'Contract not found for slug: {slug}',
1083
1085
  validating: 'Validating harness for {slug}...',
1084
1086
  blocked: 'Execution paused: {reason}',
1085
- init_dry_run: '[dry-run] Would initialize harness for {slug}'
1087
+ init_dry_run: '[dry-run] Would initialize harness for {slug}',
1088
+ check_header: 'Harness check — {slug}',
1089
+ check_no_executable: ' No criteria with verification commands ({total} criteria total). @validator judges them all.',
1090
+ check_summary: ' Checks: {passed}/{executable} passed ({skipped} without verification — judged by @validator)',
1091
+ check_unknown_criteria: 'Unknown criteria ids: {ids}'
1086
1092
  },
1087
1093
  web_map: {
1088
1094
  url_missing: 'Missing required option: --url=<url>.',
@@ -22,16 +22,16 @@ module.exports = {
22
22
  'aioson agent:prompt <agent> [path] [--tool=codex|claude|opencode] [--lang=<bcp47-tag>] [--locale=es]',
23
23
  help_agent_help:
24
24
  'aioson agent:help [agent] [--json]',
25
- help_agent_invoke:
26
- 'aioson agent:invoke <agent> [path] [--tool=codex|claude|opencode] [--mode=framework_target|app_target] [--feature=<slug>] [--scope=<area>] [--lang=<bcp47-tag>] [--locale=es]',
27
- help_agent_epilogue:
28
- 'aioson agent:epilogue [path] --agent=<agente> --summary=<texto> [--feature=<slug>] [--approve-gate=A|B|C|D] [--json] [--locale=es]',
25
+ help_agent_invoke:
26
+ 'aioson agent:invoke <agent> [path] [--tool=codex|claude|opencode] [--mode=framework_target|app_target] [--feature=<slug>] [--scope=<area>] [--lang=<bcp47-tag>] [--locale=es]',
27
+ help_agent_epilogue:
28
+ 'aioson agent:epilogue [path] --agent=<agente> --summary=<texto> [--feature=<slug>] [--approve-gate=A|B|C|D] [--json] [--locale=es]',
29
29
  help_context_validate: 'aioson context:validate [path] [--json] [--locale=es]',
30
- help_context_pack:
31
- 'aioson context:pack [path] [--agent=<agente>] [--goal=<texto>] [--module=<modulo-o-carpeta>] [--max-files=8] [--json] [--locale=es]',
32
- help_context_select:
33
- 'aioson context:select [path] [--agent=<agente>] [--mode=planning|executing] [--task=<texto>] [--paths=<ruta[,ruta2]>] [--feature=<slug>] [--json] [--locale=es]',
34
- help_context_load:
30
+ help_context_pack:
31
+ 'aioson context:pack [path] [--agent=<agente>] [--goal=<texto>] [--module=<modulo-o-carpeta>] [--max-files=8] [--json] [--locale=es]',
32
+ help_context_select:
33
+ 'aioson context:select [path] [--agent=<agente>] [--mode=planning|executing] [--task=<texto>] [--paths=<ruta[,ruta2]>] [--feature=<slug>] [--json] [--locale=es]',
34
+ help_context_load:
35
35
  'aioson context:load [path] --target=<rule|brain>:<slug> --agent=<nombre> [--batch="slug1,slug2"] [--feature=<slug>] [--classification=<MICRO|SMALL|MEDIUM>] [--verbose] [--json] [--locale=es]',
36
36
  help_chain_audit:
37
37
  'aioson chain:audit <archivo> [path] [--limit=N] [--feature=<slug>] [--json] [--locale=es]',
@@ -112,10 +112,10 @@ module.exports = {
112
112
  'aioson test:package [source-path] [--keep] [--dry-run] [--json] [--locale=es]',
113
113
  help_workflow_plan:
114
114
  'aioson workflow:plan [path] [--classification=MICRO|SMALL|MEDIUM] [--json] [--locale=es]',
115
- help_workflow_execute:
116
- 'aioson workflow:execute [path] [--feature=<slug>] [--agentic] [--max-dev-qa-cycles=<n>] [--max-tester-cycles=<n>] [--max-pentester-cycles=<n>] [--dry-run] [--lane=<n>] [--json] [--locale=es]',
117
- help_review_cycle:
118
- 'aioson review-cycle:<status|advance|resolve|reset> [path] --feature=<slug> [--plan=<path>] [--source=qa|tester|pentester] [--json] [--locale=es]',
115
+ help_workflow_execute:
116
+ 'aioson workflow:execute [path] [--feature=<slug>] [--agentic] [--max-dev-qa-cycles=<n>] [--max-tester-cycles=<n>] [--max-pentester-cycles=<n>] [--dry-run] [--lane=<n>] [--json] [--locale=es]',
117
+ help_review_cycle:
118
+ 'aioson review-cycle:<status|advance|resolve|reset> [path] --feature=<slug> [--plan=<path>] [--source=qa|tester|pentester] [--json] [--locale=es]',
119
119
  help_parallel_init:
120
120
  'aioson parallel:init [path] [--workers=2..6] [--force] [--dry-run] [--json] [--locale=es]',
121
121
  help_parallel_doctor:
@@ -142,6 +142,8 @@ module.exports = {
142
142
  'aioson qa:scan [path] [--url=<app-url>] [--depth=3] [--max-pages=50] [--headed] [--html] [--json] [--locale=es]',
143
143
  help_qa_report:
144
144
  'aioson qa:report [path] [--html] [--json] [--locale=es]',
145
+ help_harness_check:
146
+ 'aioson harness:check [path] --slug=<slug> [--criteria=C1,C2] [--timeout=<ms>] [--json] [--locale=es]',
145
147
  help_harness_retro:
146
148
  'aioson harness:retro [path] --feature=<slug> | --last=<N> [--json] [--locale=es]',
147
149
  help_harness_preview:
@@ -22,16 +22,16 @@ module.exports = {
22
22
  'aioson agent:prompt <agent> [path] [--tool=codex|claude|opencode] [--lang=<bcp47-tag>] [--locale=fr]',
23
23
  help_agent_help:
24
24
  'aioson agent:help [agent] [--json]',
25
- help_agent_invoke:
26
- 'aioson agent:invoke <agent> [path] [--tool=codex|claude|opencode] [--mode=framework_target|app_target] [--feature=<slug>] [--scope=<area>] [--lang=<bcp47-tag>] [--locale=fr]',
27
- help_agent_epilogue:
28
- 'aioson agent:epilogue [path] --agent=<agent> --summary=<texte> [--feature=<slug>] [--approve-gate=A|B|C|D] [--json] [--locale=fr]',
25
+ help_agent_invoke:
26
+ 'aioson agent:invoke <agent> [path] [--tool=codex|claude|opencode] [--mode=framework_target|app_target] [--feature=<slug>] [--scope=<area>] [--lang=<bcp47-tag>] [--locale=fr]',
27
+ help_agent_epilogue:
28
+ 'aioson agent:epilogue [path] --agent=<agent> --summary=<texte> [--feature=<slug>] [--approve-gate=A|B|C|D] [--json] [--locale=fr]',
29
29
  help_context_validate: 'aioson context:validate [path] [--json] [--locale=fr]',
30
- help_context_pack:
31
- 'aioson context:pack [path] [--agent=<agent>] [--goal=<texte>] [--module=<module-ou-dossier>] [--max-files=8] [--json] [--locale=fr]',
32
- help_context_select:
33
- 'aioson context:select [path] [--agent=<agent>] [--mode=planning|executing] [--task=<texte>] [--paths=<chemin[,chemin2]>] [--feature=<slug>] [--json] [--locale=fr]',
34
- help_context_load:
30
+ help_context_pack:
31
+ 'aioson context:pack [path] [--agent=<agent>] [--goal=<texte>] [--module=<module-ou-dossier>] [--max-files=8] [--json] [--locale=fr]',
32
+ help_context_select:
33
+ 'aioson context:select [path] [--agent=<agent>] [--mode=planning|executing] [--task=<texte>] [--paths=<chemin[,chemin2]>] [--feature=<slug>] [--json] [--locale=fr]',
34
+ help_context_load:
35
35
  'aioson context:load [path] --target=<rule|brain>:<slug> --agent=<nom> [--batch="slug1,slug2"] [--feature=<slug>] [--classification=<MICRO|SMALL|MEDIUM>] [--verbose] [--json] [--locale=fr]',
36
36
  help_chain_audit:
37
37
  'aioson chain:audit <fichier> [path] [--limit=N] [--feature=<slug>] [--json] [--locale=fr]',
@@ -112,10 +112,10 @@ module.exports = {
112
112
  'aioson test:package [source-path] [--keep] [--dry-run] [--json] [--locale=fr]',
113
113
  help_workflow_plan:
114
114
  'aioson workflow:plan [path] [--classification=MICRO|SMALL|MEDIUM] [--json] [--locale=fr]',
115
- help_workflow_execute:
116
- 'aioson workflow:execute [path] [--feature=<slug>] [--agentic] [--max-dev-qa-cycles=<n>] [--max-tester-cycles=<n>] [--max-pentester-cycles=<n>] [--dry-run] [--lane=<n>] [--json] [--locale=fr]',
117
- help_review_cycle:
118
- 'aioson review-cycle:<status|advance|resolve|reset> [path] --feature=<slug> [--plan=<path>] [--source=qa|tester|pentester] [--json] [--locale=fr]',
115
+ help_workflow_execute:
116
+ 'aioson workflow:execute [path] [--feature=<slug>] [--agentic] [--max-dev-qa-cycles=<n>] [--max-tester-cycles=<n>] [--max-pentester-cycles=<n>] [--dry-run] [--lane=<n>] [--json] [--locale=fr]',
117
+ help_review_cycle:
118
+ 'aioson review-cycle:<status|advance|resolve|reset> [path] --feature=<slug> [--plan=<path>] [--source=qa|tester|pentester] [--json] [--locale=fr]',
119
119
  help_parallel_init:
120
120
  'aioson parallel:init [path] [--workers=2..6] [--force] [--dry-run] [--json] [--locale=fr]',
121
121
  help_parallel_doctor:
@@ -142,6 +142,8 @@ module.exports = {
142
142
  'aioson qa:scan [path] [--url=<app-url>] [--depth=3] [--max-pages=50] [--headed] [--html] [--json] [--locale=fr]',
143
143
  help_qa_report:
144
144
  'aioson qa:report [path] [--html] [--json] [--locale=fr]',
145
+ help_harness_check:
146
+ 'aioson harness:check [path] --slug=<slug> [--criteria=C1,C2] [--timeout=<ms>] [--json] [--locale=fr]',
145
147
  help_harness_retro:
146
148
  'aioson harness:retro [path] --feature=<slug> | --last=<N> [--json] [--locale=fr]',
147
149
  help_harness_preview:
@@ -22,16 +22,16 @@ module.exports = {
22
22
  'aioson agent:prompt <agent> [path] [--tool=codex|claude|opencode] [--lang=<bcp47-tag>] [--locale=pt-BR]',
23
23
  help_agent_help:
24
24
  'aioson agent:help [agent] [--json]',
25
- help_agent_invoke:
26
- 'aioson agent:invoke <agent> [path] [--tool=codex|claude|opencode] [--mode=framework_target|app_target] [--feature=<slug>] [--scope=<area>] [--lang=<bcp47-tag>] [--locale=pt-BR]',
27
- help_agent_epilogue:
28
- 'aioson agent:epilogue [path] --agent=<agente> --summary=<texto> [--feature=<slug>] [--approve-gate=A|B|C|D] [--json] [--locale=pt-BR]',
25
+ help_agent_invoke:
26
+ 'aioson agent:invoke <agent> [path] [--tool=codex|claude|opencode] [--mode=framework_target|app_target] [--feature=<slug>] [--scope=<area>] [--lang=<bcp47-tag>] [--locale=pt-BR]',
27
+ help_agent_epilogue:
28
+ 'aioson agent:epilogue [path] --agent=<agente> --summary=<texto> [--feature=<slug>] [--approve-gate=A|B|C|D] [--json] [--locale=pt-BR]',
29
29
  help_context_validate: 'aioson context:validate [path] [--json] [--locale=pt-BR]',
30
- help_context_pack:
31
- 'aioson context:pack [path] [--agent=<agente>] [--goal=<texto>] [--module=<modulo-ou-pasta>] [--max-files=8] [--json] [--locale=pt-BR]',
32
- help_context_select:
33
- 'aioson context:select [path] [--agent=<agente>] [--mode=planning|executing] [--task=<texto>] [--paths=<caminho[,caminho2]>] [--feature=<slug>] [--json] [--locale=pt-BR]',
34
- help_context_load:
30
+ help_context_pack:
31
+ 'aioson context:pack [path] [--agent=<agente>] [--goal=<texto>] [--module=<modulo-ou-pasta>] [--max-files=8] [--json] [--locale=pt-BR]',
32
+ help_context_select:
33
+ 'aioson context:select [path] [--agent=<agente>] [--mode=planning|executing] [--task=<texto>] [--paths=<caminho[,caminho2]>] [--feature=<slug>] [--json] [--locale=pt-BR]',
34
+ help_context_load:
35
35
  'aioson context:load [path] --target=<rule|brain>:<slug> --agent=<nome> [--batch="slug1,slug2"] [--feature=<slug>] [--classification=<MICRO|SMALL|MEDIUM>] [--verbose] [--json] [--locale=pt-BR]',
36
36
  help_chain_audit:
37
37
  'aioson chain:audit <arquivo> [path] [--limit=N] [--feature=<slug>] [--json] [--locale=pt-BR]',
@@ -124,10 +124,10 @@ module.exports = {
124
124
  'aioson workflow:next [path] [--complete[=<agente>]] [--agent=<agente>] [--skip=<agente>] [--status] [--suggest] [--tool=codex|claude|opencode] [--json] [--locale=pt-BR]',
125
125
  help_workflow_status:
126
126
  'aioson workflow:status [path] [--suggest] [--tool=codex|claude|opencode] [--json] [--locale=pt-BR]',
127
- help_workflow_execute:
128
- 'aioson workflow:execute [path] [--feature=<slug>] [--agentic] [--max-dev-qa-cycles=<n>] [--max-tester-cycles=<n>] [--max-pentester-cycles=<n>] [--dry-run] [--lane=<n>] [--json] [--locale=pt-BR]',
129
- help_review_cycle:
130
- 'aioson review-cycle:<status|advance|resolve|reset> [path] --feature=<slug> [--plan=<path>] [--source=qa|tester|pentester] [--json] [--locale=pt-BR]',
127
+ help_workflow_execute:
128
+ 'aioson workflow:execute [path] [--feature=<slug>] [--agentic] [--max-dev-qa-cycles=<n>] [--max-tester-cycles=<n>] [--max-pentester-cycles=<n>] [--dry-run] [--lane=<n>] [--json] [--locale=pt-BR]',
129
+ help_review_cycle:
130
+ 'aioson review-cycle:<status|advance|resolve|reset> [path] --feature=<slug> [--plan=<path>] [--source=qa|tester|pentester] [--json] [--locale=pt-BR]',
131
131
  help_parallel_init:
132
132
  'aioson parallel:init [path] [--workers=2..6] [--force] [--dry-run] [--json] [--locale=pt-BR]',
133
133
  help_parallel_doctor:
@@ -157,7 +157,9 @@ module.exports = {
157
157
  help_harness_init:
158
158
  'aioson harness:init [path] --slug=<slug> [--mode=BALANCED|URGENT|ECONOMICAL] [--locale=pt-BR]',
159
159
  help_harness_validate:
160
- 'aioson harness:validate [path] --slug=<slug> [--artifact=<path>] [--locale=pt-BR]',
160
+ 'aioson harness:validate [path] --slug=<slug> [--base=<ref>] [--no-diff] [--max-diff-bytes=<n>] [--artifact=<path>] [--locale=pt-BR]',
161
+ help_harness_check:
162
+ 'aioson harness:check [path] --slug=<slug> [--criteria=C1,C2] [--timeout=<ms>] [--json] [--locale=pt-BR]',
161
163
  help_harness_retro:
162
164
  'aioson harness:retro [path] --feature=<slug> | --last=<N> [--json] [--locale=pt-BR]',
163
165
  help_harness_preview:
@@ -1106,7 +1108,11 @@ module.exports = {
1106
1108
  contract_not_found: 'Contrato nao encontrado para o slug: {slug}',
1107
1109
  validating: 'Validando harness para {slug}...',
1108
1110
  blocked: 'Execucao pausada: {reason}',
1109
- init_dry_run: '[dry-run] Inicializaria harness para {slug}'
1111
+ init_dry_run: '[dry-run] Inicializaria harness para {slug}',
1112
+ check_header: 'Harness check — {slug}',
1113
+ check_no_executable: ' Nenhum criterio com comando de verification ({total} criterios no total). @validator julga todos.',
1114
+ check_summary: ' Checks: {passed}/{executable} passaram ({skipped} sem verification — julgados pelo @validator)',
1115
+ check_unknown_criteria: 'Ids de criterios desconhecidos: {ids}'
1110
1116
  },
1111
1117
  web_map: {
1112
1118
  url_missing: 'Opcao obrigatoria ausente: --url=<url>.',
package/src/parser.js CHANGED
@@ -29,13 +29,15 @@ function parseArgv(argv) {
29
29
  'all', 'force', 'dry-run', 'no-interactive', 'fix', 'json',
30
30
  'help', 'version', 'no-launch', 'attach', 'tmux',
31
31
  'allow-warnings', 'install-hook', 'uninstall-hook', 'remove-hook',
32
- 'agent-safe', 'agentic',
32
+ 'agent-safe', 'agentic',
33
33
  'selective',
34
34
  'status', 'suggest', 'apply',
35
35
  'runtime-only', 'template-only', 'inception', 'locales',
36
36
  // feature:export structure/output toggles — pure booleans; without these
37
37
  // a following positional (e.g. `--flatten .`) would be swallowed as the value.
38
38
  'flatten', 'no-index',
39
+ // harness:validate — pure boolean; `--no-diff .` must not swallow the path.
40
+ 'no-diff',
39
41
  // `--resume` alone means "resume last"; `--resume=<id>` carries a value
40
42
  // and is handled by the `=` branch above. Without this entry, `--resume`
41
43
  // followed by `--tool=claude` would swallow the next token as its value.