@jaimevalasek/aioson 1.30.2 → 1.33.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +19 -6
  3. package/docs/en/5-reference/cli-reference.md +1 -1
  4. package/docs/pt/5-referencia/comandos-cli.md +1 -1
  5. package/docs/pt/5-referencia/harness-retro.md +2 -1
  6. package/package.json +1 -1
  7. package/src/cli.js +38 -21
  8. package/src/commands/classify.js +389 -327
  9. package/src/commands/harness-retro-promote.js +387 -0
  10. package/src/commands/prototype-check.js +163 -0
  11. package/src/commands/verify-implementation.js +428 -0
  12. package/src/commands/workflow-next.js +222 -9
  13. package/src/constants.js +2 -0
  14. package/src/lib/retro/retro-render.js +10 -1
  15. package/src/lib/retro/retro-sources.js +45 -27
  16. package/src/lib/retro/verification-reports.js +230 -0
  17. package/src/runtime-store.js +13 -9
  18. package/src/verification/evidence-bundle.js +251 -0
  19. package/src/verification/ledger-store.js +221 -0
  20. package/src/verification/path-policy.js +74 -0
  21. package/src/verification/policy-engine.js +95 -0
  22. package/src/verification/prompt-package.js +314 -0
  23. package/src/verification/redaction.js +77 -0
  24. package/src/verification/report-parser.js +132 -0
  25. package/src/verification/report-store.js +97 -0
  26. package/src/verification/result.js +16 -0
  27. package/src/verification/runners/index.js +319 -0
  28. package/src/verification/runtime-telemetry.js +144 -0
  29. package/src/verification/schema.js +276 -0
  30. package/src/verification/source-discovery.js +153 -0
  31. package/template/.aioson/agents/analyst.md +9 -6
  32. package/template/.aioson/agents/briefing-refiner.md +22 -0
  33. package/template/.aioson/agents/briefing.md +69 -12
  34. package/template/.aioson/agents/design-hybrid-forge.md +18 -14
  35. package/template/.aioson/agents/dev.md +23 -10
  36. package/template/.aioson/agents/deyvin.md +3 -2
  37. package/template/.aioson/agents/product.md +15 -11
  38. package/template/.aioson/agents/qa.md +13 -4
  39. package/template/.aioson/agents/scope-check.md +19 -5
  40. package/template/.aioson/agents/sheldon.md +4 -3
  41. package/template/.aioson/agents/ux-ui.md +3 -2
  42. package/template/.aioson/docs/feature-expansion-taxonomy.md +31 -3
  43. package/template/.aioson/docs/prototype-contract.md +81 -0
  44. package/template/.aioson/skills/process/briefing-expansion-scout/SKILL.md +25 -3
  45. package/template/.aioson/skills/process/design-hybrid-forge/SKILL.md +5 -3
  46. package/template/.aioson/skills/process/design-hybrid-forge/references/external-source-ingestion.md +89 -0
  47. package/template/.aioson/skills/process/product-scope-expansion/SKILL.md +29 -2
  48. package/template/.aioson/skills/process/prototype-forge/SKILL.md +92 -0
  49. package/template/.aioson/skills/process/sheldon-expansion-audit/SKILL.md +23 -2
@@ -23,6 +23,15 @@ const { runSecurityAudit } = require('./security-audit');
23
23
  const dossierBootstrap = require('../dossier/dossier-bootstrap');
24
24
  const dossierStore = require('../dossier/store');
25
25
  const { emitDossierEvent } = require('../lib/dossier-telemetry');
26
+ const { parseVerificationReport } = require('../verification/report-parser');
27
+ const { applyPolicy } = require('../verification/policy-engine');
28
+ const { normalizePolicy } = require('../verification/result');
29
+ const {
30
+ validateFeatureSlug,
31
+ featureContextDir,
32
+ verificationRunsDir,
33
+ relativeFromRoot
34
+ } = require('../verification/path-policy');
26
35
 
27
36
  const STATE_RELATIVE_PATH = '.aioson/context/workflow.state.json';
28
37
  const CONFIG_RELATIVE_PATH = '.aioson/context/workflow.config.json';
@@ -41,9 +50,9 @@ const DEFAULT_FEATURE_WORKFLOW_BY_CLASSIFICATION = {
41
50
 
42
51
  // Stages eligible for autopilot handoff (auto_handoff: true in project.context.md).
43
52
  // Two segments — see .aioson/docs/autopilot-handoff.md:
44
- // 1. analyst -> dev: deterministic pre-dev chain. Prompt-only clients stop
45
- // before the first @dev entry; workflow:execute --agentic may resume it
46
- // through a fresh checkpointed activation.
53
+ // 1. analyst -> dev: deterministic pre-dev chain. Prompt-only clients stop
54
+ // before the first @dev entry; workflow:execute --agentic may resume it
55
+ // through a fresh checkpointed activation.
47
56
  // 2. post-dev review cycle: @dev → @qa → @tester/@pentester (when their @qa triggers
48
57
  // fire) → @validator → STOPS before feature:close (human approves the close).
49
58
  const AUTOPILOT_HANDOFF_STAGES = new Set([
@@ -114,6 +123,18 @@ function getScopeCheckModeOption(options = {}) {
114
123
  );
115
124
  }
116
125
 
126
+ function resolveVerificationPolicy(options = {}, state = {}) {
127
+ const explicit = options.verificationPolicy ||
128
+ options['verification-policy'] ||
129
+ options.verification_policy ||
130
+ options.policy;
131
+ if (explicit) return normalizePolicy(explicit) || 'standard';
132
+ // No explicit policy: MEDIUM features default to strict so the auto-injected
133
+ // scope-check verification briefing matches the strict `--check-report` the
134
+ // @dev / @scope-check prompts run. Smaller tiers stay advisory (standard).
135
+ return String(state.classification || '').toUpperCase() === 'MEDIUM' ? 'strict' : 'standard';
136
+ }
137
+
117
138
  function chooseActiveFeature(features, preferredSlug = null) {
118
139
  const activeFeatures = (features || []).filter((feature) => feature.status === 'in_progress');
119
140
  if (preferredSlug) {
@@ -1032,6 +1053,8 @@ async function resolveStageDependencies(targetDir, state, stageName, agent) {
1032
1053
  'readiness.md',
1033
1054
  'ui-spec.md',
1034
1055
  slug ? `implementation-plan-${slug}.md` : 'implementation-plan.md',
1056
+ slug ? `features/${slug}/implementation-ledger.md` : null,
1057
+ slug ? `features/${slug}/verification-report.md` : null,
1035
1058
  'dev-state.md',
1036
1059
  'last-handoff.json',
1037
1060
  'project-pulse.md'
@@ -1139,6 +1162,146 @@ function buildScopeCheckActivationContext(state, mode) {
1139
1162
  return lines.join('\n');
1140
1163
  }
1141
1164
 
1165
+ function routeLabel(route) {
1166
+ return route ? `@${normalizeAgentName(route)}` : '@qa';
1167
+ }
1168
+
1169
+ function workflowGuidanceForVerification(verdict, route, normalReturnTo) {
1170
+ if (verdict === 'PASS') {
1171
+ return `PASS: keep normal workflow ownership (${routeLabel(normalReturnTo || route)}), then continue diff/scope review.`;
1172
+ }
1173
+ if (verdict === 'NEEDS_DEV_FIX') {
1174
+ return 'NEEDS_DEV_FIX: do not approve clean post-dev scope; route concrete file:line findings to @dev.';
1175
+ }
1176
+ if (verdict === 'NEEDS_SCOPE_DECISION') {
1177
+ return `NEEDS_SCOPE_DECISION: route to ${routeLabel(route)}; do not patch product scope locally.`;
1178
+ }
1179
+ if (verdict === 'NEEDS_QA_RECHECK') {
1180
+ return 'NEEDS_QA_RECHECK: route to @qa after scope alignment is clear.';
1181
+ }
1182
+ if (verdict === 'NEEDS_SECURITY_REVIEW') {
1183
+ return 'NEEDS_SECURITY_REVIEW: preserve the security review owner and route to @pentester.';
1184
+ }
1185
+ return `INCONCLUSIVE: route to the owner of missing evidence (${routeLabel(route)}) when strict verification applies.`;
1186
+ }
1187
+
1188
+ async function findImplementationVerificationReport(targetDir, slug) {
1189
+ const slugResult = validateFeatureSlug(slug);
1190
+ if (!slugResult.ok) return null;
1191
+
1192
+ const latestPath = path.join(featureContextDir(targetDir, slug), 'verification-report.md');
1193
+ if (await exists(latestPath)) {
1194
+ return {
1195
+ absolutePath: latestPath,
1196
+ relativePath: relativeFromRoot(targetDir, latestPath),
1197
+ source: 'latest'
1198
+ };
1199
+ }
1200
+
1201
+ const runsDir = verificationRunsDir(targetDir, slug);
1202
+ let entries = [];
1203
+ try {
1204
+ entries = await fs.readdir(runsDir);
1205
+ } catch {
1206
+ return null;
1207
+ }
1208
+ const reportName = entries
1209
+ .filter((entry) => /-report\.md$/i.test(entry))
1210
+ .sort()
1211
+ .pop();
1212
+ if (!reportName) return null;
1213
+
1214
+ const reportPath = path.join(runsDir, reportName);
1215
+ return {
1216
+ absolutePath: reportPath,
1217
+ relativePath: relativeFromRoot(targetDir, reportPath),
1218
+ source: 'verification-runs'
1219
+ };
1220
+ }
1221
+
1222
+ async function buildImplementationVerificationBriefing(targetDir, state, scopeCheckMode, policy) {
1223
+ const mode = inferScopeCheckMode(state, scopeCheckMode);
1224
+ if (
1225
+ state.mode !== 'feature' ||
1226
+ !state.featureSlug ||
1227
+ !['post-dev', 'post-fix', 'final'].includes(mode)
1228
+ ) {
1229
+ return null;
1230
+ }
1231
+
1232
+ const slug = state.featureSlug;
1233
+ const latestPath = `.aioson/context/features/${slug}/verification-report.md`;
1234
+ const reportRef = await findImplementationVerificationReport(targetDir, slug);
1235
+ const lines = [
1236
+ '## Implementation verification briefing',
1237
+ `Policy: ${policy}`,
1238
+ `Expected latest report: ${latestPath}`,
1239
+ 'Workflow note: this briefing only validates local report artifacts; it never runs `--tool` or any external auditor.'
1240
+ ];
1241
+
1242
+ if (!reportRef) {
1243
+ lines.push('Report status: missing');
1244
+ if (state.classification === 'MICRO') {
1245
+ lines.push('MICRO: missing report is not a workflow blocker by default; record residual risk only when the dev handoff relied on verification.');
1246
+ } else if (state.classification === 'MEDIUM' && policy === 'strict') {
1247
+ lines.push('Strict MEDIUM guidance: do not issue final clean scope approval until @dev produces a valid report or documents an explicit N/A rationale.');
1248
+ } else {
1249
+ lines.push('Guidance: absence is advisory unless the feature policy or dev handoff made verification strict.');
1250
+ }
1251
+ return {
1252
+ status: 'missing',
1253
+ mode,
1254
+ policy,
1255
+ report_path: null,
1256
+ verdict: 'INCONCLUSIVE',
1257
+ recommended_route: state.classification === 'MICRO' ? state.next || 'qa' : 'dev',
1258
+ briefing: lines.join('\n')
1259
+ };
1260
+ }
1261
+
1262
+ lines.push(`Report path: ${reportRef.relativePath}`);
1263
+ lines.push(`Validate command: aioson verify:implementation . --feature=${slug} --check-report=${reportRef.relativePath} --policy=${policy} --json`);
1264
+
1265
+ const parsed = await parseVerificationReport(targetDir, slug, reportRef.relativePath, policy);
1266
+ if (!parsed.ok) {
1267
+ lines.push(`Report status: invalid (${parsed.reason})`);
1268
+ lines.push('Guidance: treat this as INCONCLUSIVE local evidence; do not treat auditor prose as PASS.');
1269
+ return {
1270
+ status: 'invalid',
1271
+ mode,
1272
+ policy,
1273
+ report_path: reportRef.relativePath,
1274
+ verdict: 'INCONCLUSIVE',
1275
+ recommended_route: 'qa',
1276
+ reason: parsed.reason,
1277
+ briefing: lines.join('\n')
1278
+ };
1279
+ }
1280
+
1281
+ const policyResult = applyPolicy(parsed.report, policy);
1282
+ lines.push('Report status: schema-valid');
1283
+ lines.push(`Report verdict: ${parsed.report.verdict}`);
1284
+ lines.push(`Policy verdict: ${policyResult.verdict}`);
1285
+ lines.push(`Policy route: ${routeLabel(policyResult.recommended_route)}`);
1286
+ lines.push(`Blocking findings: ${policyResult.blocking_findings_count || 0}`);
1287
+ lines.push(`Guidance: ${workflowGuidanceForVerification(policyResult.verdict, policyResult.recommended_route, state.next)}`);
1288
+ lines.push('Scope-check still must inspect the diff and approved plan; a PASS report is not final approval.');
1289
+
1290
+ return {
1291
+ status: 'valid',
1292
+ mode,
1293
+ policy,
1294
+ report_path: reportRef.relativePath,
1295
+ report_source: reportRef.source,
1296
+ verdict: policyResult.verdict,
1297
+ auditor_verdict: parsed.report.verdict,
1298
+ recommended_route: policyResult.recommended_route,
1299
+ blocking_findings_count: policyResult.blocking_findings_count || 0,
1300
+ reason: policyResult.reason,
1301
+ briefing: lines.join('\n')
1302
+ };
1303
+ }
1304
+
1142
1305
  function buildStageActivationContext(state, stageName, dependencies, scopeCheckMode = null) {
1143
1306
  if (stageName === 'scope-check') {
1144
1307
  return buildScopeCheckActivationContext(state, scopeCheckMode);
@@ -1155,7 +1318,16 @@ function buildStageActivationContext(state, stageName, dependencies, scopeCheckM
1155
1318
  ].join('\n');
1156
1319
  }
1157
1320
 
1158
- async function activateStage(targetDir, state, locale, tool, explicitAgent = null, requestedMode = null, scopeCheckMode = null) {
1321
+ async function activateStage(
1322
+ targetDir,
1323
+ state,
1324
+ locale,
1325
+ tool,
1326
+ explicitAgent = null,
1327
+ requestedMode = null,
1328
+ scopeCheckMode = null,
1329
+ verificationPolicy = 'standard'
1330
+ ) {
1159
1331
  const stageName = normalizeAgentName(explicitAgent || state.current || state.next);
1160
1332
  if (!stageName) {
1161
1333
  return {
@@ -1270,6 +1442,13 @@ async function activateStage(targetDir, state, locale, tool, explicitAgent = nul
1270
1442
 
1271
1443
  const instructionPath = await resolveExistingInstructionPath(targetDir, agent, locale);
1272
1444
  const dependencies = await resolveStageDependencies(targetDir, state, stageName, agent);
1445
+ const verificationBriefing = stageName === 'scope-check'
1446
+ ? await buildImplementationVerificationBriefing(targetDir, state, scopeCheckMode, verificationPolicy)
1447
+ : null;
1448
+ const activationContext = [
1449
+ buildStageActivationContext(state, stageName, dependencies, scopeCheckMode),
1450
+ verificationBriefing && verificationBriefing.briefing
1451
+ ].filter(Boolean).join('\n\n');
1273
1452
  let prompt = buildAgentPrompt(agent, tool, {
1274
1453
  instructionPath,
1275
1454
  targetDir,
@@ -1278,7 +1457,7 @@ async function activateStage(targetDir, state, locale, tool, explicitAgent = nul
1278
1457
  capabilitySummary: buildAgentCapabilitySummary(agentManifest, tool),
1279
1458
  dependsOn: dependencies,
1280
1459
  autoHandoff,
1281
- activationContext: buildStageActivationContext(state, stageName, dependencies, scopeCheckMode)
1460
+ activationContext
1282
1461
  });
1283
1462
 
1284
1463
  if (testBriefing) {
@@ -1316,7 +1495,20 @@ async function activateStage(targetDir, state, locale, tool, explicitAgent = nul
1316
1495
  agent: stageName,
1317
1496
  instructionPath,
1318
1497
  prompt,
1319
- effectiveMode
1498
+ effectiveMode,
1499
+ verification: verificationBriefing
1500
+ ? {
1501
+ status: verificationBriefing.status,
1502
+ mode: verificationBriefing.mode,
1503
+ policy: verificationBriefing.policy,
1504
+ report_path: verificationBriefing.report_path,
1505
+ verdict: verificationBriefing.verdict,
1506
+ auditor_verdict: verificationBriefing.auditor_verdict || null,
1507
+ recommended_route: verificationBriefing.recommended_route,
1508
+ blocking_findings_count: verificationBriefing.blocking_findings_count || 0,
1509
+ reason: verificationBriefing.reason || null
1510
+ }
1511
+ : null
1320
1512
  };
1321
1513
  }
1322
1514
 
@@ -1425,7 +1617,16 @@ async function runWorkflowNext({ args, options, logger, t }) {
1425
1617
  if (retryCount < require('../self-healing').MAX_RETRIES) {
1426
1618
  await require('../self-healing').incrementRetryCount(targetDir, failedStage, err.message.substring(0, 200));
1427
1619
  // Build healing activation
1428
- const baseActivation = await activateStage(targetDir, state, locale, tool, failedStage, options.mode || null);
1620
+ const baseActivation = await activateStage(
1621
+ targetDir,
1622
+ state,
1623
+ locale,
1624
+ tool,
1625
+ failedStage,
1626
+ options.mode || null,
1627
+ null,
1628
+ resolveVerificationPolicy(options, state)
1629
+ );
1429
1630
  const healingPrompt = buildHealingPrompt(
1430
1631
  baseActivation.prompt || '',
1431
1632
  failedStage,
@@ -1552,7 +1753,17 @@ async function runWorkflowNext({ args, options, logger, t }) {
1552
1753
  const activationAgent = normalizeAgentName(requestedAgent || state.current || state.next);
1553
1754
  const scopeCheckMode = activationAgent === 'scope-check' ? getScopeCheckModeOption(options) : null;
1554
1755
  const requestedAutonomyMode = scopeCheckMode && activationAgent === 'scope-check' ? null : options.mode || null;
1555
- const activation = await activateStage(targetDir, state, locale, tool, requestedAgent, requestedAutonomyMode, scopeCheckMode);
1756
+ const verificationPolicy = resolveVerificationPolicy(options, state);
1757
+ const activation = await activateStage(
1758
+ targetDir,
1759
+ state,
1760
+ locale,
1761
+ tool,
1762
+ requestedAgent,
1763
+ requestedAutonomyMode,
1764
+ scopeCheckMode,
1765
+ verificationPolicy
1766
+ );
1556
1767
  state = activation.state;
1557
1768
 
1558
1769
  // ── Living Memory: if a reflect manifest is pending (created above by the
@@ -1590,7 +1801,8 @@ async function runWorkflowNext({ args, options, logger, t }) {
1590
1801
  completed: state.completed,
1591
1802
  skipped: state.skipped,
1592
1803
  sequence: state.sequence,
1593
- autonomyMode: activation.effectiveMode || null
1804
+ autonomyMode: activation.effectiveMode || null,
1805
+ verification: activation.verification || null
1594
1806
  };
1595
1807
  await appendWorkflowEvent(targetDir, eventPayload);
1596
1808
  const runtime = await syncWorkflowRuntime(targetDir, {
@@ -1645,6 +1857,7 @@ async function runWorkflowNext({ args, options, logger, t }) {
1645
1857
  runtime,
1646
1858
  agent: activation.agent,
1647
1859
  effectiveMode: activation.effectiveMode || null,
1860
+ verification: activation.verification || null,
1648
1861
  instructionPath: activation.instructionPath,
1649
1862
  prompt: activation.prompt
1650
1863
  };
package/src/constants.js CHANGED
@@ -46,6 +46,7 @@ const MANAGED_FILES = [
46
46
  '.aioson/docs/squad/session-operations.md',
47
47
  '.aioson/docs/squad/genome-bindings.md',
48
48
  '.aioson/docs/feature-expansion-taxonomy.md',
49
+ '.aioson/docs/prototype-contract.md',
49
50
  '.aioson/docs/product/conversation-playbook.md',
50
51
  '.aioson/docs/product/research-loop.md',
51
52
  '.aioson/docs/product/quality-lens.md',
@@ -81,6 +82,7 @@ const MANAGED_FILES = [
81
82
  '.aioson/skills/process/briefing-expansion-scout/SKILL.md',
82
83
  '.aioson/skills/process/product-scope-expansion/SKILL.md',
83
84
  '.aioson/skills/process/sheldon-expansion-audit/SKILL.md',
85
+ '.aioson/skills/process/prototype-forge/SKILL.md',
84
86
  '.aioson/skills/static/laravel-conventions.md',
85
87
  '.aioson/skills/static/tall-stack-patterns.md',
86
88
  '.aioson/skills/static/jetstream-setup.md',
@@ -9,7 +9,16 @@
9
9
  * fixo (pt-BR, conforme §3.1); `--locale` afeta só as mensagens de stdout.
10
10
  */
11
11
 
12
- const SOURCE_ORDER = ['qa_reports', 'corrections', 'dossier_trail', 'execution_events', 'attempts', 'failure_signatures', 'devlogs'];
12
+ const SOURCE_ORDER = [
13
+ 'qa_reports',
14
+ 'verification_reports',
15
+ 'corrections',
16
+ 'dossier_trail',
17
+ 'execution_events',
18
+ 'attempts',
19
+ 'failure_signatures',
20
+ 'devlogs'
21
+ ];
13
22
 
14
23
  // Defesa em profundidade (SF-01): o dossiê vira contexto do @sheldon. Texto livre
15
24
  // minerado (títulos) é apresentado como DADO inline, nunca como estrutura Markdown
@@ -13,9 +13,10 @@
13
13
  * módulo NUNCA escreve no filesystem.
14
14
  */
15
15
 
16
- const fs = require('node:fs');
17
- const path = require('node:path');
18
- const crypto = require('node:crypto');
16
+ const fs = require('node:fs');
17
+ const path = require('node:path');
18
+ const crypto = require('node:crypto');
19
+ const { readVerificationReports } = require('./verification-reports');
19
20
 
20
21
  const FINDING_ID_RE = /\b([A-Z]{1,2}-\d{1,2})\b/;
21
22
  const TRAIL_ENTRY_RE = /^\*\*([^*]+)\*\*\s*\|\s*@?([\w.-]+)\s*\|\s*_([^_]+)_\s*$/;
@@ -445,11 +446,16 @@ function resolveLocations(targetDir, ctxDir, slug) {
445
446
  path.join(doneRoot, 'plans')
446
447
  ].filter(dirExists);
447
448
 
448
- const dossierCandidates = [
449
- path.join(ctxDir, 'features', slug, 'dossier.md'),
450
- path.join(doneRoot, 'features', slug, 'dossier.md'),
451
- path.join(doneRoot, 'dossier.md')
452
- ];
449
+ const dossierCandidates = [
450
+ path.join(ctxDir, 'features', slug, 'dossier.md'),
451
+ path.join(doneRoot, 'features', slug, 'dossier.md'),
452
+ path.join(doneRoot, 'dossier.md')
453
+ ];
454
+ const featureDirs = [
455
+ path.join(ctxDir, 'features', slug),
456
+ path.join(doneRoot, 'features', slug),
457
+ doneRoot
458
+ ].filter(dirExists);
453
459
  // SF-02: lstat (não statSync) para NÃO seguir symlink — consistente com os
454
460
  // demais readers, que pulam symlinks via Dirent.isFile(). Um dossier.md que
455
461
  // seja um symlink apontando para fora do workspace é ignorado, não seguido.
@@ -457,8 +463,8 @@ function resolveLocations(targetDir, ctxDir, slug) {
457
463
  try { return fs.lstatSync(p).isFile(); } catch { return false; }
458
464
  });
459
465
 
460
- return { qaDirs, planDirs, dossierFiles, doneRoot };
461
- }
466
+ return { qaDirs, planDirs, dossierFiles, doneRoot, featureDirs };
467
+ }
462
468
 
463
469
  /**
464
470
  * Minera todas as fontes de UMA feature. Best-effort: nunca lança.
@@ -478,14 +484,15 @@ function collectFeatureSources(targetDir, slug) {
478
484
  const corr = readCorrections({ rootDir, slug, locations });
479
485
  const trail = readDossierTrail({ rootDir, slug, locations });
480
486
  const events = readExecutionEvents({ rootDir, targetDir, slug });
481
- const attempts = readAttempts({ rootDir, slug, locations });
482
- const sigs = readFailureSignatures({ rootDir, slug, locations });
483
- const devlogs = readDevlogs({ rootDir, targetDir, slug });
484
-
485
- for (const r of [qa, corr, trail, events, attempts, sigs, devlogs]) {
486
- findings.push(...r.findings);
487
- warnings.push(...r.warnings);
488
- }
487
+ const attempts = readAttempts({ rootDir, slug, locations });
488
+ const sigs = readFailureSignatures({ rootDir, slug, locations });
489
+ const devlogs = readDevlogs({ rootDir, targetDir, slug });
490
+ const verification = readVerificationReports({ rootDir, slug, locations, makeFinding, relPath });
491
+
492
+ for (const r of [qa, corr, trail, events, attempts, sigs, devlogs, verification]) {
493
+ findings.push(...r.findings);
494
+ warnings.push(...r.warnings);
495
+ }
489
496
  cycles.push(...trail.cycles);
490
497
 
491
498
  const counts = {
@@ -493,10 +500,11 @@ function collectFeatureSources(targetDir, slug) {
493
500
  corrections: corr.count,
494
501
  dossier_trail: trail.count,
495
502
  execution_events: events.count,
496
- attempts: attempts.count,
497
- failure_signatures: sigs.count,
498
- devlogs: devlogs.count
499
- };
503
+ attempts: attempts.count,
504
+ failure_signatures: sigs.count,
505
+ devlogs: devlogs.count,
506
+ verification_reports: verification.count
507
+ };
500
508
 
501
509
  const cost = {
502
510
  execution_events: events.count,
@@ -508,10 +516,11 @@ function collectFeatureSources(targetDir, slug) {
508
516
  };
509
517
 
510
518
  const minedPaths = [];
511
- for (const d of locations.qaDirs) minedPaths.push(relPath(rootDir, d));
512
- for (const d of locations.planDirs) minedPaths.push(relPath(rootDir, d));
513
- for (const f of locations.dossierFiles) minedPaths.push(relPath(rootDir, f));
514
- minedPaths.sort();
519
+ for (const d of locations.qaDirs) minedPaths.push(relPath(rootDir, d));
520
+ for (const d of locations.planDirs) minedPaths.push(relPath(rootDir, d));
521
+ for (const f of locations.dossierFiles) minedPaths.push(relPath(rootDir, f));
522
+ for (const f of verification.files) minedPaths.push(relPath(rootDir, f));
523
+ minedPaths.sort();
515
524
 
516
525
  return { slug, findings, cycles, counts, cost, minedPaths, warnings };
517
526
  }
@@ -526,7 +535,16 @@ function collectSources(targetDir, slugs) {
526
535
  const cycles = [];
527
536
  const warnings = [];
528
537
  const minedPaths = [];
529
- const counts = { qa_reports: 0, corrections: 0, dossier_trail: 0, execution_events: 0, attempts: 0, failure_signatures: 0, devlogs: 0 };
538
+ const counts = {
539
+ qa_reports: 0,
540
+ verification_reports: 0,
541
+ corrections: 0,
542
+ dossier_trail: 0,
543
+ execution_events: 0,
544
+ attempts: 0,
545
+ failure_signatures: 0,
546
+ devlogs: 0
547
+ };
530
548
  const cost = { execution_events: 0, corrections: 0, fail_pass_cycles: 0, corrections_bytes: 0, token_count_available: false, token_total: null };
531
549
  const costByFeature = {};
532
550