@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.
- package/CHANGELOG.md +27 -0
- package/README.md +19 -6
- package/docs/en/5-reference/cli-reference.md +1 -1
- package/docs/pt/5-referencia/comandos-cli.md +1 -1
- package/docs/pt/5-referencia/harness-retro.md +2 -1
- package/package.json +1 -1
- package/src/cli.js +38 -21
- package/src/commands/classify.js +389 -327
- package/src/commands/harness-retro-promote.js +387 -0
- package/src/commands/prototype-check.js +163 -0
- package/src/commands/verify-implementation.js +428 -0
- package/src/commands/workflow-next.js +222 -9
- package/src/constants.js +2 -0
- package/src/lib/retro/retro-render.js +10 -1
- package/src/lib/retro/retro-sources.js +45 -27
- package/src/lib/retro/verification-reports.js +230 -0
- package/src/runtime-store.js +13 -9
- package/src/verification/evidence-bundle.js +251 -0
- package/src/verification/ledger-store.js +221 -0
- package/src/verification/path-policy.js +74 -0
- package/src/verification/policy-engine.js +95 -0
- package/src/verification/prompt-package.js +314 -0
- package/src/verification/redaction.js +77 -0
- package/src/verification/report-parser.js +132 -0
- package/src/verification/report-store.js +97 -0
- package/src/verification/result.js +16 -0
- package/src/verification/runners/index.js +319 -0
- package/src/verification/runtime-telemetry.js +144 -0
- package/src/verification/schema.js +276 -0
- package/src/verification/source-discovery.js +153 -0
- package/template/.aioson/agents/analyst.md +9 -6
- package/template/.aioson/agents/briefing-refiner.md +22 -0
- package/template/.aioson/agents/briefing.md +69 -12
- package/template/.aioson/agents/design-hybrid-forge.md +18 -14
- package/template/.aioson/agents/dev.md +23 -10
- package/template/.aioson/agents/deyvin.md +3 -2
- package/template/.aioson/agents/product.md +15 -11
- package/template/.aioson/agents/qa.md +13 -4
- package/template/.aioson/agents/scope-check.md +19 -5
- package/template/.aioson/agents/sheldon.md +4 -3
- package/template/.aioson/agents/ux-ui.md +3 -2
- package/template/.aioson/docs/feature-expansion-taxonomy.md +31 -3
- package/template/.aioson/docs/prototype-contract.md +81 -0
- package/template/.aioson/skills/process/briefing-expansion-scout/SKILL.md +25 -3
- package/template/.aioson/skills/process/design-hybrid-forge/SKILL.md +5 -3
- package/template/.aioson/skills/process/design-hybrid-forge/references/external-source-ingestion.md +89 -0
- package/template/.aioson/skills/process/product-scope-expansion/SKILL.md +29 -2
- package/template/.aioson/skills/process/prototype-forge/SKILL.md +92 -0
- 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(
|
|
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
|
|
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(
|
|
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
|
|
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 = [
|
|
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
|
-
|
|
486
|
-
|
|
487
|
-
|
|
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.
|
|
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 = {
|
|
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
|
|