@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,324 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * aioson spec:analyze — cross-artifact CONTENT consistency for a feature
5
+ * (Fase 3 do plano de verificação executável; análogo determinístico do
6
+ * /speckit.analyze).
7
+ *
8
+ * Escopo distinto de `artifact:validate` (presença/integridade da cadeia —
9
+ * intocado): aqui valida-se o CONTEÚDO entre artefatos já existentes:
10
+ *
11
+ * - Rastreabilidade de IDs: REQ-x e AC-x declarados em requirements-{slug}.md
12
+ * que nenhum artefato downstream referencia (gap de cobertura) e IDs
13
+ * referenciados downstream que não existem em requirements (órfãos — sinal
14
+ * de drift/alucinação).
15
+ * - Staleness: artefato upstream modificado DEPOIS de um downstream já gerado
16
+ * (ex.: PRD editado após o implementation-plan) — o downstream pode estar
17
+ * defasado.
18
+ * - Estados bloqueantes: readiness `blocked` no design-doc/readiness.
19
+ * - Sanidade do harness-contract: erros de schema (error) e avisos de
20
+ * cobertura executável (info), via validateContract.
21
+ * - Vínculo AC→contrato: ACs declarados sem nenhuma menção no contrato (info).
22
+ *
23
+ * Determinístico, read-only sobre os artefatos; persiste o relatório em
24
+ * `.aioson/context/spec-analyze-{slug}.json` (best-effort). Severidades:
25
+ * error (bloqueia gate), warning (drift provável), info (dívida/aviso).
26
+ */
27
+
28
+ const fs = require('node:fs');
29
+ const path = require('node:path');
30
+
31
+ const { scanArtifacts, detectClassification } = require('../preflight-engine');
32
+ const { validateContract } = require('../harness/contract-schema');
33
+
34
+ const REQ_ID_RE = /\bREQ(?:-[A-Z0-9]+)+\b/g;
35
+ const AC_ID_RE = /\bAC(?:-[A-Z0-9]+)+\b/g;
36
+
37
+ /** Edições upstream no mesmo fluxo de geração não são drift — tolerância. */
38
+ const STALENESS_TOLERANCE_MS = 60000;
39
+
40
+ /** Ordem upstream→downstream para staleness. `architecture.md` fica de fora:
41
+ * é artefato global do projeto; seu mtime não pertence ao fluxo da feature. */
42
+ const STALENESS_CHAIN = [
43
+ ['prd', 'prd'],
44
+ ['sheldon_enrichment', 'sheldon-enrichment'],
45
+ ['requirements', 'requirements'],
46
+ ['spec', 'spec'],
47
+ ['design_doc', 'design-doc'],
48
+ ['implementation_plan', 'implementation-plan']
49
+ ];
50
+
51
+ /** Superfícies downstream onde um REQ/AC declarado deveria reaparecer. */
52
+ const TRACE_TARGETS = ['spec', 'design_doc', 'implementation_plan', 'conformance'];
53
+
54
+ const { parseExecutionWaves } = require('../harness/plan-waves');
55
+
56
+ function extractIds(content, regex) {
57
+ return new Set(String(content || '').match(regex) || []);
58
+ }
59
+
60
+ function mtimeMs(targetDir, artifact) {
61
+ if (!artifact || !artifact.exists || !artifact.path) return null;
62
+ try {
63
+ return fs.statSync(path.join(targetDir, artifact.path)).mtimeMs;
64
+ } catch {
65
+ return null;
66
+ }
67
+ }
68
+
69
+ function readContract(targetDir, slug) {
70
+ const contractPath = path.join(targetDir, '.aioson', 'plans', slug, 'harness-contract.json');
71
+ if (!fs.existsSync(contractPath)) return { exists: false };
72
+ try {
73
+ const raw = fs.readFileSync(contractPath, 'utf8');
74
+ return { exists: true, path: path.relative(targetDir, contractPath), raw, contract: JSON.parse(raw) };
75
+ } catch (err) {
76
+ return { exists: true, path: path.relative(targetDir, contractPath), parseError: err.message };
77
+ }
78
+ }
79
+
80
+ async function runSpecAnalyze({ args, options = {}, logger }) {
81
+ const targetDir = path.resolve(process.cwd(), args?.[0] || '.');
82
+ const slug = String(options.feature || options.slug || '').trim();
83
+
84
+ if (!slug) {
85
+ logger.error('--feature=<slug> is required.');
86
+ return { ok: false, error: 'missing_feature' };
87
+ }
88
+
89
+ const artifacts = await scanArtifacts(targetDir, slug);
90
+ const classification = await detectClassification(targetDir, slug);
91
+ const contractInfo = readContract(targetDir, slug);
92
+ const findings = [];
93
+
94
+ const present = Object.entries(artifacts)
95
+ .filter(([, a]) => a && a.exists)
96
+ .map(([name]) => name);
97
+
98
+ // ── Rastreabilidade REQ/AC ────────────────────────────────────────────────
99
+ const declaredReqs = artifacts.requirements.exists
100
+ ? extractIds(artifacts.requirements.content, REQ_ID_RE)
101
+ : new Set();
102
+ const declaredAcs = artifacts.requirements.exists
103
+ ? extractIds(artifacts.requirements.content, AC_ID_RE)
104
+ : new Set();
105
+
106
+ const downstream = TRACE_TARGETS
107
+ .filter((name) => artifacts[name] && artifacts[name].exists)
108
+ .map((name) => ({ name, content: artifacts[name].content || '' }));
109
+ if (contractInfo.exists && contractInfo.raw) {
110
+ downstream.push({ name: 'harness-contract', content: contractInfo.raw });
111
+ }
112
+
113
+ if (artifacts.requirements.exists && downstream.length > 0) {
114
+ const downstreamText = downstream.map((d) => d.content).join('\n');
115
+ const downstreamReqs = extractIds(downstreamText, REQ_ID_RE);
116
+ const downstreamAcs = extractIds(downstreamText, AC_ID_RE);
117
+
118
+ const untracedReqs = [...declaredReqs].filter((id) => !downstreamReqs.has(id));
119
+ if (untracedReqs.length > 0 && declaredReqs.size > untracedReqs.length) {
120
+ // Só acusa gap quando ALGUM REQ é rastreado — artefatos que não citam
121
+ // IDs por estilo (plano em prosa) não devem virar ruído.
122
+ findings.push({
123
+ severity: 'warning',
124
+ check: 'untraced_requirement',
125
+ message: `${untracedReqs.length} REQ id(s) declared in requirements but never referenced downstream: ${untracedReqs.slice(0, 10).join(', ')}${untracedReqs.length > 10 ? '…' : ''}`,
126
+ artifacts: ['requirements', ...downstream.map((d) => d.name)]
127
+ });
128
+ }
129
+
130
+ const orphanIds = [
131
+ ...[...downstreamReqs].filter((id) => !declaredReqs.has(id)),
132
+ ...[...downstreamAcs].filter((id) => !declaredAcs.has(id))
133
+ ];
134
+ if (orphanIds.length > 0) {
135
+ const offenders = downstream
136
+ .filter((d) => orphanIds.some((id) => d.content.includes(id)))
137
+ .map((d) => d.name);
138
+ findings.push({
139
+ severity: 'warning',
140
+ check: 'orphan_reference',
141
+ message: `${orphanIds.length} REQ/AC id(s) referenced downstream but not declared in requirements-${slug}.md: ${orphanIds.slice(0, 10).join(', ')}${orphanIds.length > 10 ? '…' : ''} (drift or cross-feature reference)`,
142
+ artifacts: offenders
143
+ });
144
+ }
145
+ }
146
+
147
+ // ── Staleness upstream → downstream ──────────────────────────────────────
148
+ const chainWithTimes = STALENESS_CHAIN
149
+ .map(([key, label]) => ({ key, label, mtime: mtimeMs(targetDir, artifacts[key]) }))
150
+ .filter((entry) => entry.mtime !== null);
151
+
152
+ for (let i = 0; i < chainWithTimes.length; i += 1) {
153
+ for (let j = i + 1; j < chainWithTimes.length; j += 1) {
154
+ const upstream = chainWithTimes[i];
155
+ const downstreamArtifact = chainWithTimes[j];
156
+ if (upstream.mtime > downstreamArtifact.mtime + STALENESS_TOLERANCE_MS) {
157
+ findings.push({
158
+ severity: 'warning',
159
+ check: 'stale_downstream',
160
+ message: `${upstream.label} was modified after ${downstreamArtifact.label} was produced — ${downstreamArtifact.label} may be stale (re-run its owner agent or confirm the change is editorial)`,
161
+ artifacts: [upstream.key, downstreamArtifact.key]
162
+ });
163
+ }
164
+ }
165
+ }
166
+
167
+ // ── Waves de paralelismo do implementation-plan ──────────────────────────
168
+ if (artifacts.implementation_plan.exists) {
169
+ const waves = parseExecutionWaves(artifacts.implementation_plan.content);
170
+ if (waves && waves.length > 0) {
171
+ const byWave = new Map();
172
+ for (const row of waves) {
173
+ if (!byWave.has(row.wave)) byWave.set(row.wave, []);
174
+ byWave.get(row.wave).push(row);
175
+ }
176
+ for (const [wave, phases] of byWave) {
177
+ if (phases.length < 2) continue;
178
+ for (let i = 0; i < phases.length; i += 1) {
179
+ for (let j = i + 1; j < phases.length; j += 1) {
180
+ const shared = phases[i].files.filter((f) => phases[j].files.includes(f));
181
+ if (shared.length > 0) {
182
+ findings.push({
183
+ severity: 'warning',
184
+ check: 'wave_file_overlap',
185
+ message: `wave ${wave}: phases ${phases[i].phase} and ${phases[j].phase} are marked parallel but share Primary files: ${shared.join(', ')} — same-wave phases must be file-disjoint (split the files or separate the waves)`,
186
+ artifacts: ['implementation_plan']
187
+ });
188
+ }
189
+ }
190
+ }
191
+ }
192
+ }
193
+ }
194
+
195
+ // ── Estados bloqueantes ───────────────────────────────────────────────────
196
+ for (const key of ['design_doc', 'readiness']) {
197
+ const artifact = artifacts[key];
198
+ if (artifact && artifact.exists && artifact.frontmatter) {
199
+ const readiness = String(artifact.frontmatter.readiness || '').toLowerCase();
200
+ if (readiness === 'blocked') {
201
+ findings.push({
202
+ severity: 'error',
203
+ check: 'readiness_blocked',
204
+ message: `${path.basename(artifact.path)} declares readiness: blocked — resolve with @discovery-design-doc before the execution gate`,
205
+ artifacts: [key]
206
+ });
207
+ } else if (readiness === 'ready_with_warnings') {
208
+ findings.push({
209
+ severity: 'info',
210
+ check: 'readiness_warnings',
211
+ message: `${path.basename(artifact.path)} declares readiness: ready_with_warnings — review its warnings before @dev`,
212
+ artifacts: [key]
213
+ });
214
+ }
215
+ }
216
+ }
217
+
218
+ // ── Sanidade do harness-contract ─────────────────────────────────────────
219
+ if (contractInfo.exists) {
220
+ if (contractInfo.parseError) {
221
+ findings.push({
222
+ severity: 'error',
223
+ check: 'contract_invalid',
224
+ message: `harness-contract.json is not valid JSON: ${contractInfo.parseError}`,
225
+ artifacts: ['harness-contract']
226
+ });
227
+ } else {
228
+ const schema = validateContract(contractInfo.contract);
229
+ for (const err of schema.errors) {
230
+ findings.push({
231
+ severity: 'error',
232
+ check: 'contract_schema',
233
+ message: `contract schema invalid: ${err.field} — ${err.reason}`,
234
+ artifacts: ['harness-contract']
235
+ });
236
+ }
237
+ for (const warn of schema.warnings) {
238
+ findings.push({
239
+ severity: 'info',
240
+ check: 'contract_coverage',
241
+ message: `${warn.field}: ${warn.reason}`,
242
+ artifacts: ['harness-contract']
243
+ });
244
+ }
245
+
246
+ // Vínculo AC→contrato: criteria derivam dos ACs; nenhum AC mencionado é
247
+ // sinal fraco (descrições podem parafrasear) — info, não warning.
248
+ if (declaredAcs.size > 0 && contractInfo.raw) {
249
+ const mentioned = [...declaredAcs].filter((id) => contractInfo.raw.includes(id));
250
+ if (mentioned.length === 0) {
251
+ findings.push({
252
+ severity: 'info',
253
+ check: 'contract_ac_unlinked',
254
+ message: `none of the ${declaredAcs.size} AC id(s) from requirements appear in harness-contract.json — confirm criteria[] actually derive from the enriched ACs`,
255
+ artifacts: ['requirements', 'harness-contract']
256
+ });
257
+ }
258
+ }
259
+ }
260
+ }
261
+
262
+ const summary = {
263
+ errors: findings.filter((f) => f.severity === 'error').length,
264
+ warnings: findings.filter((f) => f.severity === 'warning').length,
265
+ info: findings.filter((f) => f.severity === 'info').length
266
+ };
267
+
268
+ const report = {
269
+ ok: summary.errors === 0,
270
+ feature: slug,
271
+ classification: classification || 'unknown',
272
+ analyzed_at: new Date().toISOString(),
273
+ artifacts_present: present,
274
+ contract_present: Boolean(contractInfo.exists && !contractInfo.parseError),
275
+ findings,
276
+ summary
277
+ };
278
+
279
+ // Persistência best-effort — segue a convenção *-{slug}.json de
280
+ // .aioson/context/ (coletada por feature:export/archive).
281
+ try {
282
+ const contextPath = path.join(targetDir, '.aioson', 'context');
283
+ if (fs.existsSync(contextPath)) {
284
+ fs.writeFileSync(
285
+ path.join(contextPath, `spec-analyze-${slug}.json`),
286
+ JSON.stringify(report, null, 2),
287
+ 'utf8'
288
+ );
289
+ }
290
+ } catch { /* relatório em stdout permanece o canônico */ }
291
+
292
+ if (options.json) {
293
+ logger.log(JSON.stringify(report, null, 2));
294
+ return report;
295
+ }
296
+
297
+ logger.log('');
298
+ logger.log(`Spec analyze — ${slug} (${report.classification})`);
299
+ logger.log('━'.repeat(45));
300
+ logger.log(`Artifacts present: ${present.length ? present.join(', ') : '(none)'}${report.contract_present ? ' + harness-contract' : ''}`);
301
+
302
+ if (findings.length === 0) {
303
+ logger.log('');
304
+ logger.log('✓ No cross-artifact inconsistencies detected.');
305
+ } else {
306
+ for (const severity of ['error', 'warning', 'info']) {
307
+ const group = findings.filter((f) => f.severity === severity);
308
+ if (!group.length) continue;
309
+ logger.log('');
310
+ logger.log(`${severity.toUpperCase()} (${group.length}):`);
311
+ for (const finding of group) {
312
+ logger.log(` - [${finding.check}] ${finding.message}`);
313
+ }
314
+ }
315
+ }
316
+
317
+ logger.log('');
318
+ logger.log(`Summary: ${summary.errors} error(s), ${summary.warnings} warning(s), ${summary.info} info — ${report.ok ? 'OK for the execution gate' : 'BLOCKED (resolve errors first)'}`);
319
+ logger.log('Chain presence: aioson artifact:validate . --feature=' + slug);
320
+
321
+ return report;
322
+ }
323
+
324
+ module.exports = { runSpecAnalyze };
package/src/constants.js CHANGED
@@ -1,42 +1,43 @@
1
1
  'use strict';
2
2
 
3
3
  const MANAGED_FILES = [
4
- 'CLAUDE.md',
5
- 'AGENTS.md',
6
- 'OPENCODE.md',
4
+ 'CLAUDE.md',
5
+ 'AGENTS.md',
6
+ 'OPENCODE.md',
7
7
  '.aioson/config.md',
8
8
  '.aioson/agents/setup.md',
9
- '.aioson/agents/discovery-design-doc.md',
10
- '.aioson/agents/discover.md',
11
- '.aioson/agents/analyst.md',
12
- '.aioson/agents/scope-check.md',
13
- '.aioson/agents/architect.md',
9
+ '.aioson/agents/discovery-design-doc.md',
10
+ '.aioson/agents/discover.md',
11
+ '.aioson/agents/analyst.md',
12
+ '.aioson/agents/scope-check.md',
13
+ '.aioson/agents/architect.md',
14
14
  '.aioson/agents/ux-ui.md',
15
15
  '.aioson/agents/product.md',
16
- '.aioson/agents/deyvin.md',
17
- '.aioson/agents/pair.md',
18
- '.aioson/agents/pm.md',
19
- '.aioson/agents/sheldon.md',
20
- '.aioson/agents/dev.md',
21
- '.aioson/agents/qa.md',
16
+ '.aioson/agents/deyvin.md',
17
+ '.aioson/agents/pair.md',
18
+ '.aioson/agents/pm.md',
19
+ '.aioson/agents/sheldon.md',
20
+ '.aioson/agents/dev.md',
21
+ '.aioson/agents/qa.md',
22
22
  '.aioson/agents/validator.md',
23
23
  '.aioson/agents/tester.md',
24
24
  '.aioson/agents/orchestrator.md',
25
25
  '.aioson/agents/pentester.md',
26
26
  '.aioson/agents/squad.md',
27
- '.aioson/agents/orache.md',
28
- '.aioson/agents/genome.md',
29
- '.aioson/agents/neo.md',
30
- '.aioson/agents/design-hybrid-forge.md',
27
+ '.aioson/agents/orache.md',
28
+ '.aioson/agents/genome.md',
29
+ '.aioson/agents/neo.md',
30
+ '.aioson/agents/design-hybrid-forge.md',
31
31
  '.aioson/agents/site-forge.md',
32
32
  '.aioson/agents/profiler-researcher.md',
33
- '.aioson/agents/profiler-enricher.md',
34
- '.aioson/agents/profiler-forge.md',
35
- '.aioson/agents/committer.md',
36
- '.aioson/agents/copywriter.md',
37
- '.aioson/agents/briefing.md',
38
- '.aioson/agents/briefing-refiner.md',
39
- '.aioson/docs/squad/package-contract.md',
33
+ '.aioson/agents/profiler-enricher.md',
34
+ '.aioson/agents/profiler-forge.md',
35
+ '.aioson/agents/committer.md',
36
+ '.aioson/agents/copywriter.md',
37
+ '.aioson/agents/briefing.md',
38
+ '.aioson/agents/briefing-refiner.md',
39
+ '.aioson/agents/forge-run.md',
40
+ '.aioson/docs/squad/package-contract.md',
40
41
  '.aioson/docs/squad/creation-flow.md',
41
42
  '.aioson/docs/squad/research-loop.md',
42
43
  '.aioson/docs/squad/quality-lens.md',
@@ -61,13 +62,13 @@ const MANAGED_FILES = [
61
62
  '.aioson/docs/dev/stack-conventions.md',
62
63
  '.aioson/docs/dev/execution-discipline.md',
63
64
  '.aioson/docs/quality/code-health-analysis.md',
64
- '.aioson/skills/process/decision-presentation/SKILL.md',
65
- '.aioson/skills/process/decision-presentation/references/jargon-map.en.yaml',
66
- '.aioson/skills/process/decision-presentation/references/jargon-map.pt-BR.yaml',
67
- '.aioson/skills/process/prompt-sharpener/SKILL.md',
68
- '.aioson/skills/process/prompt-sharpener/references/prompt-diagnostics.md',
69
- '.aioson/skills/process/prompt-sharpener/agents/openai.yaml',
70
- '.aioson/skills/static/laravel-conventions.md',
65
+ '.aioson/skills/process/decision-presentation/SKILL.md',
66
+ '.aioson/skills/process/decision-presentation/references/jargon-map.en.yaml',
67
+ '.aioson/skills/process/decision-presentation/references/jargon-map.pt-BR.yaml',
68
+ '.aioson/skills/process/prompt-sharpener/SKILL.md',
69
+ '.aioson/skills/process/prompt-sharpener/references/prompt-diagnostics.md',
70
+ '.aioson/skills/process/prompt-sharpener/agents/openai.yaml',
71
+ '.aioson/skills/static/laravel-conventions.md',
71
72
  '.aioson/skills/static/tall-stack-patterns.md',
72
73
  '.aioson/skills/static/jetstream-setup.md',
73
74
  '.aioson/skills/static/rails-conventions.md',
@@ -139,10 +140,10 @@ const MANAGED_FILES = [
139
140
  ];
140
141
 
141
142
  const REQUIRED_FILES = [
142
- 'CLAUDE.md',
143
- 'AGENTS.md',
144
- 'OPENCODE.md',
145
- '.claude/commands/aioson/agent/setup.md',
143
+ 'CLAUDE.md',
144
+ 'AGENTS.md',
145
+ 'OPENCODE.md',
146
+ '.claude/commands/aioson/agent/setup.md',
146
147
  '.claude/commands/aioson/agent/discover.md',
147
148
  '.claude/commands/aioson/agent/dev.md',
148
149
  '.claude/commands/aioson/agent/qa.md',
@@ -184,12 +185,12 @@ const AGENT_DEFINITIONS = [
184
185
  {
185
186
  id: 'discovery-design-doc',
186
187
  displayName: 'Discovery/Design Doc',
187
- description: 'Discovery and design doc generation',
188
- command: '@discovery-design-doc',
189
- path: '.aioson/agents/discovery-design-doc.md',
190
- dependsOn: ['.aioson/context/project.context.md'],
191
- output: '.aioson/context/design-doc*.md + .aioson/context/readiness*.md'
192
- },
188
+ description: 'Discovery and design doc generation',
189
+ command: '@discovery-design-doc',
190
+ path: '.aioson/agents/discovery-design-doc.md',
191
+ dependsOn: ['.aioson/context/project.context.md'],
192
+ output: '.aioson/context/design-doc*.md + .aioson/context/readiness*.md'
193
+ },
193
194
  {
194
195
  id: 'discover',
195
196
  displayName: 'Discover',
@@ -218,38 +219,38 @@ const AGENT_DEFINITIONS = [
218
219
  dependsOn: ['.aioson/context/project.context.md'],
219
220
  output: 'small code changes + continuity notes in spec.md + runtime logs/tasks'
220
221
  },
221
- {
222
- id: 'analyst',
223
- displayName: 'Analyst',
224
- description: 'Domain discovery and entity mapping (SMALL/MEDIUM)',
225
- command: '@analyst',
226
- path: '.aioson/agents/analyst.md',
227
- dependsOn: ['.aioson/context/project.context.md'],
228
- output: '.aioson/context/discovery.md or .aioson/context/requirements-{slug}.md + .aioson/context/spec-{slug}.md'
229
- },
230
- {
231
- id: 'scope-check',
232
- displayName: 'Scope Check',
233
- description: 'Scope alignment before development and optional post-dev drift checks',
234
- command: '@scope-check',
235
- path: '.aioson/agents/scope-check.md',
236
- dependsOn: [
237
- '.aioson/context/project.context.md',
238
- '.aioson/context/prd.md or .aioson/context/prd-{slug}.md',
239
- '.aioson/context/discovery.md or .aioson/context/requirements-{slug}.md + .aioson/context/spec-{slug}.md'
240
- ],
241
- output: '.aioson/context/scope-check.md or .aioson/context/scope-check-{slug}.md'
242
- },
243
- {
244
- id: 'architect',
222
+ {
223
+ id: 'analyst',
224
+ displayName: 'Analyst',
225
+ description: 'Domain discovery and entity mapping (SMALL/MEDIUM)',
226
+ command: '@analyst',
227
+ path: '.aioson/agents/analyst.md',
228
+ dependsOn: ['.aioson/context/project.context.md'],
229
+ output: '.aioson/context/discovery.md or .aioson/context/requirements-{slug}.md + .aioson/context/spec-{slug}.md'
230
+ },
231
+ {
232
+ id: 'scope-check',
233
+ displayName: 'Scope Check',
234
+ description: 'Scope alignment before development and optional post-dev drift checks',
235
+ command: '@scope-check',
236
+ path: '.aioson/agents/scope-check.md',
237
+ dependsOn: [
238
+ '.aioson/context/project.context.md',
239
+ '.aioson/context/prd.md or .aioson/context/prd-{slug}.md',
240
+ '.aioson/context/discovery.md or .aioson/context/requirements-{slug}.md + .aioson/context/spec-{slug}.md'
241
+ ],
242
+ output: '.aioson/context/scope-check.md or .aioson/context/scope-check-{slug}.md'
243
+ },
244
+ {
245
+ id: 'architect',
245
246
  displayName: 'Architect',
246
247
  description: 'Project structure and technical decisions (SMALL/MEDIUM)',
247
248
  command: '@architect',
248
249
  path: '.aioson/agents/architect.md',
249
- dependsOn: [
250
- '.aioson/context/project.context.md',
251
- '.aioson/context/discovery.md or .aioson/context/requirements-{slug}.md + .aioson/context/spec-{slug}.md'
252
- ],
250
+ dependsOn: [
251
+ '.aioson/context/project.context.md',
252
+ '.aioson/context/discovery.md or .aioson/context/requirements-{slug}.md + .aioson/context/spec-{slug}.md'
253
+ ],
253
254
  output: '.aioson/context/architecture.md'
254
255
  },
255
256
  {
@@ -272,16 +273,16 @@ const AGENT_DEFINITIONS = [
272
273
  description: 'Backlog and user stories (MEDIUM only)',
273
274
  command: '@pm',
274
275
  path: '.aioson/agents/pm.md',
275
- dependsOn: [
276
- '.aioson/context/project.context.md',
277
- '.aioson/context/prd.md or .aioson/context/prd-{slug}.md',
278
- '.aioson/context/requirements-{slug}.md + .aioson/context/spec-{slug}.md (feature mode)',
279
- '.aioson/context/discovery.md',
280
- '.aioson/context/architecture.md',
281
- '.aioson/context/design-doc-{slug}.md + .aioson/context/readiness-{slug}.md (feature mode, when present)',
282
- '.aioson/context/ui-spec.md (when present)'
283
- ],
284
- output: '.aioson/context/prd.md or prd-{slug}.md (enriched with acceptance criteria) + .aioson/context/implementation-plan-{slug}.md for MEDIUM features'
276
+ dependsOn: [
277
+ '.aioson/context/project.context.md',
278
+ '.aioson/context/prd.md or .aioson/context/prd-{slug}.md',
279
+ '.aioson/context/requirements-{slug}.md + .aioson/context/spec-{slug}.md (feature mode)',
280
+ '.aioson/context/discovery.md',
281
+ '.aioson/context/architecture.md',
282
+ '.aioson/context/design-doc-{slug}.md + .aioson/context/readiness-{slug}.md (feature mode, when present)',
283
+ '.aioson/context/ui-spec.md (when present)'
284
+ ],
285
+ output: '.aioson/context/prd.md or prd-{slug}.md (enriched with acceptance criteria) + .aioson/context/implementation-plan-{slug}.md for MEDIUM features'
285
286
  },
286
287
  {
287
288
  id: 'dev',
@@ -405,6 +406,15 @@ const AGENT_DEFINITIONS = [
405
406
  dependsOn: ['.aioson/context/project.context.md'],
406
407
  output: 'src/components/*.tsx + src/app/page.tsx + docs/research/{hostname}/ + public/images/{hostname}/'
407
408
  },
409
+ {
410
+ id: 'forge-run',
411
+ displayName: 'Forge Run',
412
+ description: 'Compile and run the Lane B workflow harness for a MEDIUM feature',
413
+ command: '@forge-run',
414
+ path: '.aioson/agents/forge-run.md',
415
+ dependsOn: ['.aioson/context/project.context.md'],
416
+ output: '.aioson/plans/{slug}/forge-run.workflow.js + workflow run verdict (stops before feature:close)'
417
+ },
408
418
  {
409
419
  id: 'neo',
410
420
  displayName: 'Neo',
@@ -414,15 +424,15 @@ const AGENT_DEFINITIONS = [
414
424
  dependsOn: ['.aioson/context/project.context.md'],
415
425
  output: 'routing decision + agent handoff'
416
426
  },
417
- {
418
- id: 'sheldon',
419
- displayName: 'Sheldon',
420
- description: 'PRD quality review and pre-implementation enrichment',
421
- command: '@sheldon',
422
- path: '.aioson/agents/sheldon.md',
423
- dependsOn: ['.aioson/context/project.context.md'],
424
- output: 'enriched PRD or phased execution plan'
425
- },
427
+ {
428
+ id: 'sheldon',
429
+ displayName: 'Sheldon',
430
+ description: 'PRD quality review and pre-implementation enrichment',
431
+ command: '@sheldon',
432
+ path: '.aioson/agents/sheldon.md',
433
+ dependsOn: ['.aioson/context/project.context.md'],
434
+ output: 'enriched PRD or phased execution plan'
435
+ },
426
436
  {
427
437
  id: 'committer',
428
438
  displayName: 'Committer',
@@ -441,26 +451,26 @@ const AGENT_DEFINITIONS = [
441
451
  dependsOn: [],
442
452
  output: 'marketing copy + content assets'
443
453
  },
444
- {
445
- id: 'briefing',
446
- displayName: 'Briefing',
447
- description: 'Pre-production briefings and planning',
448
- command: '@briefing',
454
+ {
455
+ id: 'briefing',
456
+ displayName: 'Briefing',
457
+ description: 'Pre-production briefings and planning',
458
+ command: '@briefing',
449
459
  path: '.aioson/agents/briefing.md',
450
- dependsOn: ['.aioson/context/project.context.md'],
451
- output: '.aioson/briefings/{slug}/'
452
- },
453
- {
454
- id: 'briefing-refiner',
455
- displayName: 'Briefing Refiner',
456
- description: 'Interactive refinement of briefing artifacts before Product PRD generation',
457
- command: '@briefing-refiner',
458
- path: '.aioson/agents/briefing-refiner.md',
459
- dependsOn: ['.aioson/context/project.context.md', '.aioson/briefings/config.md'],
460
- output: '.aioson/briefings/{slug}/review.html + refinement-feedback.json + refinement-report.md'
461
- },
462
- {
463
- id: 'profiler-researcher',
460
+ dependsOn: ['.aioson/context/project.context.md'],
461
+ output: '.aioson/briefings/{slug}/'
462
+ },
463
+ {
464
+ id: 'briefing-refiner',
465
+ displayName: 'Briefing Refiner',
466
+ description: 'Interactive refinement of briefing artifacts before Product PRD generation',
467
+ command: '@briefing-refiner',
468
+ path: '.aioson/agents/briefing-refiner.md',
469
+ dependsOn: ['.aioson/context/project.context.md', '.aioson/briefings/config.md'],
470
+ output: '.aioson/briefings/{slug}/review.html + refinement-feedback.json + refinement-report.md'
471
+ },
472
+ {
473
+ id: 'profiler-researcher',
464
474
  displayName: 'Profiler Researcher',
465
475
  description: 'Clone profiler: research phase',
466
476
  command: '@profiler-researcher',
@@ -250,6 +250,14 @@ function validateContract(contract) {
250
250
  if (criterion.binary !== undefined && typeof criterion.binary !== 'boolean') {
251
251
  errors.push({ field: `criteria[${i}].binary`, reason: 'must be a boolean' });
252
252
  }
253
+ // Cobertura executável: critério binário sem verification continua
254
+ // válido (julgado pelo @validator), mas é dívida de verificação.
255
+ if (criterion.binary === true && criterion.verification === undefined) {
256
+ warnings.push({
257
+ field: `criteria[${i}].verification`,
258
+ reason: `binary criterion "${criterion.id}" has no executable verification command — @validator will LLM-judge it`
259
+ });
260
+ }
253
261
  });
254
262
  }
255
263
  }