@jaimevalasek/aioson 1.33.1 → 1.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +76 -0
- package/docs/en/1-understand/ecosystem-map.md +45 -29
- package/docs/en/1-understand/glossary.md +6 -6
- package/docs/en/1-understand/what-is-aioson.md +5 -5
- package/docs/en/2-start/existing-project.md +7 -7
- package/docs/en/2-start/first-project.md +33 -39
- package/docs/en/2-start/initial-decisions.md +22 -19
- package/docs/en/3-recipes/README.md +2 -2
- package/docs/en/3-recipes/continuity-between-sessions.md +2 -2
- package/docs/en/3-recipes/from-idea-to-prd-via-briefing.md +13 -3
- package/docs/en/3-recipes/full-feature-with-sheldon.md +329 -338
- package/docs/en/4-agents/README.md +33 -16
- package/docs/en/4-agents/briefing-refiner.md +146 -0
- package/docs/en/4-agents/discovery-design-doc.md +20 -8
- package/docs/en/4-agents/forge-run.md +3 -2
- package/docs/en/5-reference/README.md +1 -0
- package/docs/en/5-reference/autopilot-handoff.md +286 -0
- package/docs/en/5-reference/cli-reference.md +57 -48
- package/docs/en/5-reference/executable-verification.md +10 -7
- package/docs/en/5-reference/parallel.md +2 -0
- package/docs/en/5-reference/qa-browser.md +2 -2
- package/docs/en/README.md +3 -3
- package/docs/pt/1-entender/glossario.md +5 -5
- package/docs/pt/1-entender/mapa-do-ecossistema.md +33 -22
- package/docs/pt/2-comecar/decisoes-iniciais.md +20 -18
- package/docs/pt/2-comecar/primeiro-projeto.md +2 -2
- package/docs/pt/3-receitas/README.md +1 -1
- package/docs/pt/3-receitas/app-saas-do-zero.md +35 -122
- package/docs/pt/3-receitas/da-ideia-ao-prd-via-briefing.md +8 -1
- package/docs/pt/3-receitas/feature-completa-com-sheldon.md +291 -338
- package/docs/pt/4-agentes/README.md +13 -11
- package/docs/pt/4-agentes/analyst.md +9 -5
- package/docs/pt/4-agentes/architect.md +9 -5
- package/docs/pt/4-agentes/briefing-refiner.md +64 -40
- package/docs/pt/4-agentes/briefing.md +6 -1
- package/docs/pt/4-agentes/dev.md +38 -8
- package/docs/pt/4-agentes/deyvin.md +4 -0
- package/docs/pt/4-agentes/discover.md +4 -0
- package/docs/pt/4-agentes/discovery-design-doc.md +25 -23
- package/docs/pt/4-agentes/neo.md +4 -0
- package/docs/pt/4-agentes/orache.md +6 -0
- package/docs/pt/4-agentes/orchestrator.md +176 -118
- package/docs/pt/4-agentes/pentester.md +11 -5
- package/docs/pt/4-agentes/pm.md +15 -7
- package/docs/pt/4-agentes/product.md +19 -1
- package/docs/pt/4-agentes/qa.md +10 -2
- package/docs/pt/4-agentes/setup.md +3 -1
- package/docs/pt/4-agentes/sheldon.md +178 -99
- package/docs/pt/4-agentes/tester.md +6 -0
- package/docs/pt/4-agentes/ux-ui.md +9 -5
- package/docs/pt/5-referencia/README.md +1 -1
- package/docs/pt/5-referencia/agent-chain-continuity.md +14 -5
- package/docs/pt/5-referencia/autopilot-handoff.md +191 -74
- package/docs/pt/5-referencia/comandos-cli.md +16 -7
- package/docs/pt/5-referencia/sdd-automation-scripts.md +6 -6
- package/docs/pt/5-referencia/sdd-framework.md +53 -16
- package/docs/pt/5-referencia/sdd-planos-e-estrutura.md +27 -15
- package/docs/pt/5-referencia/skills.md +2 -0
- package/docs/pt/README.md +4 -4
- package/docs/pt/agentes.md +50 -50
- package/package.json +2 -2
- package/src/agents.js +1 -1
- package/src/artifact-kinds.js +111 -0
- package/src/autopilot-signal.js +71 -0
- package/src/cli.js +112 -81
- package/src/commands/agent-epilogue.js +251 -186
- package/src/commands/agents.js +122 -50
- package/src/commands/audit-code.js +344 -0
- package/src/commands/briefing.js +337 -1
- package/src/commands/classify.js +389 -389
- package/src/commands/feature-close.js +122 -4
- package/src/commands/harness-check.js +259 -175
- package/src/commands/live.js +71 -11
- package/src/commands/review-feature.js +189 -0
- package/src/commands/runtime.js +81 -66
- package/src/commands/sync-agents-copy.js +115 -0
- package/src/commands/update.js +5 -1
- package/src/commands/verification-plan.js +143 -0
- package/src/commands/verify-artifact.js +593 -0
- package/src/commands/workflow-execute.js +434 -316
- package/src/commands/workflow-next.js +193 -20
- package/src/commands/workflow-plan.js +5 -5
- package/src/constants.js +2 -0
- package/src/doctor.js +4 -2
- package/src/gateway-pointer-merge.js +7 -1
- package/src/handoff-contract.js +267 -172
- package/src/harness/contract-integrity-gate.js +172 -0
- package/src/harness/contract-integrity.js +111 -0
- package/src/harness/contract-schema.js +377 -332
- package/src/harness/detect-runtime-feature.js +90 -0
- package/src/harness/static-criteria.js +192 -0
- package/src/i18n/messages/en.js +10 -5
- package/src/i18n/messages/es.js +10 -5
- package/src/i18n/messages/fr.js +10 -5
- package/src/i18n/messages/pt-BR.js +10 -5
- package/src/install-wizard.js +8 -8
- package/src/installer.js +13 -6
- package/src/lib/briefing-refiner/apply-feedback.js +18 -4
- package/src/lib/briefing-refiner/feedback-schema.js +73 -4
- package/src/lib/briefing-refiner/refinement-report.js +11 -0
- package/src/lib/briefing-refiner/review-html.js +388 -68
- package/src/parser.js +12 -0
- package/src/preflight-engine.js +12 -12
- package/src/verification/policy-engine.js +95 -95
- package/src/verification-policy.js +398 -0
- package/src/version.js +52 -1
- package/template/.aioson/agents/architect.md +34 -5
- package/template/.aioson/agents/briefing-refiner.md +91 -48
- package/template/.aioson/agents/briefing.md +4 -0
- package/template/.aioson/agents/committer.md +2 -1
- package/template/.aioson/agents/copywriter.md +30 -21
- package/template/.aioson/agents/design-hybrid-forge.md +10 -1
- package/template/.aioson/agents/dev.md +35 -23
- package/template/.aioson/agents/deyvin.md +4 -0
- package/template/.aioson/agents/discover.md +16 -3
- package/template/.aioson/agents/discovery-design-doc.md +7 -2
- package/template/.aioson/agents/genome.md +19 -10
- package/template/.aioson/agents/neo.md +34 -30
- package/template/.aioson/agents/orache.md +24 -11
- package/template/.aioson/agents/orchestrator.md +100 -7
- package/template/.aioson/agents/pentester.md +4 -0
- package/template/.aioson/agents/pm.md +8 -8
- package/template/.aioson/agents/product.md +25 -1
- package/template/.aioson/agents/profiler-enricher.md +20 -11
- package/template/.aioson/agents/profiler-forge.md +20 -11
- package/template/.aioson/agents/profiler-researcher.md +20 -11
- package/template/.aioson/agents/qa.md +96 -31
- package/template/.aioson/agents/setup.md +12 -1
- package/template/.aioson/agents/sheldon.md +103 -13
- package/template/.aioson/agents/site-forge.md +11 -2
- package/template/.aioson/agents/squad.md +20 -5
- package/template/.aioson/agents/tester.md +4 -0
- package/template/.aioson/agents/ux-ui.md +2 -1
- package/template/.aioson/agents/validator.md +33 -1
- package/template/.aioson/config/verification.json +61 -0
- package/template/.aioson/config.md +13 -9
- package/template/.aioson/docs/LAYERS.md +2 -2
- package/template/.aioson/docs/agent-help.md +126 -0
- package/template/.aioson/docs/autopilot-handoff.md +30 -20
- package/template/.aioson/docs/dev/execution-discipline.md +41 -0
- package/template/.aioson/docs/dev/phase-loop.md +50 -0
- package/template/.aioson/docs/play/llm-data-and-bindings.md +70 -7
- package/template/.aioson/docs/presets/workflow.config.full-merged.json +15 -0
- package/template/.aioson/docs/presets/workflow.config.lean.json +15 -0
- package/template/.aioson/docs/product/prd-contract.md +12 -12
- package/template/.aioson/docs/prototype-contract.md +21 -4
- package/template/.aioson/docs/reference-identity.md +94 -0
- package/template/.aioson/docs/sheldon/harness-contract.md +155 -28
- package/template/.aioson/docs/verification-config.md +82 -0
- package/template/.aioson/docs/verify-artifact-gates.md +91 -0
- package/template/.aioson/docs/workflow-lean-lane.md +129 -0
- package/template/.aioson/skills/design/interface-design/SKILL.md +17 -0
- package/template/.aioson/skills/design/interface-design/references/intent-and-domain.md +2 -0
- package/template/.aioson/skills/process/aioson-spec-driven/SKILL.md +16 -14
- package/template/.aioson/skills/process/aioson-spec-driven/references/approval-gates.md +2 -2
- package/template/.aioson/skills/process/aioson-spec-driven/references/classification-map.md +4 -4
- package/template/.aioson/skills/process/aioson-spec-driven/references/dev.md +15 -15
- package/template/.aioson/skills/process/prototype-forge/SKILL.md +6 -0
- package/template/.aioson/skills/process/reference-identity-extract/SKILL.md +164 -0
- package/template/.aioson/skills/static/multi-agent-patterns.md +5 -5
- package/template/AGENTS.md +36 -36
- package/template/CLAUDE.md +15 -11
package/src/preflight-engine.js
CHANGED
|
@@ -257,11 +257,11 @@ async function scanArtifacts(targetDir, slug) {
|
|
|
257
257
|
: [path.join(dir, 'readiness.md')];
|
|
258
258
|
|
|
259
259
|
const results = {
|
|
260
|
-
project_context: await check('project.context', path.join(dir, 'project.context.md')),
|
|
261
|
-
prd: slug ? await check('prd', path.join(dir, `prd-${slug}.md`)) : { exists: false },
|
|
262
|
-
sheldon_enrichment: slug ? await check('sheldon', path.join(dir, `sheldon-enrichment-${slug}.md`)) : { exists: false },
|
|
263
|
-
sheldon_validation: slug ? await check('sheldon-validation', path.join(dir, `sheldon-validation-${slug}.md`)) : { exists: false },
|
|
264
|
-
requirements: slug ? await check('requirements', path.join(dir, `requirements-${slug}.md`)) : { exists: false },
|
|
260
|
+
project_context: await check('project.context', path.join(dir, 'project.context.md')),
|
|
261
|
+
prd: slug ? await check('prd', path.join(dir, `prd-${slug}.md`)) : { exists: false },
|
|
262
|
+
sheldon_enrichment: slug ? await check('sheldon', path.join(dir, `sheldon-enrichment-${slug}.md`)) : { exists: false },
|
|
263
|
+
sheldon_validation: slug ? await check('sheldon-validation', path.join(dir, `sheldon-validation-${slug}.md`)) : { exists: false },
|
|
264
|
+
requirements: slug ? await check('requirements', path.join(dir, `requirements-${slug}.md`)) : { exists: false },
|
|
265
265
|
spec: slug ? await check('spec', path.join(dir, `spec-${slug}.md`)) : await check('spec', path.join(dir, 'spec.md')),
|
|
266
266
|
architecture: await check('architecture', path.join(dir, 'architecture.md')),
|
|
267
267
|
design_doc: await checkFirst('design-doc', designDocCandidates),
|
|
@@ -480,11 +480,11 @@ function buildContextPackage(agent, slug, classification, artifacts, devState, m
|
|
|
480
480
|
const downstreamAgents = ['discovery-design-doc', 'pm', 'orchestrator', 'dev', 'deyvin', 'qa'];
|
|
481
481
|
const shouldCarryFullFeatureContext = downstreamAgents.includes(agent);
|
|
482
482
|
|
|
483
|
-
if (shouldCarryFullFeatureContext && artifacts.prd.exists) pkg.push(artifacts.prd.path);
|
|
484
|
-
if (shouldCarryFullFeatureContext && artifacts.sheldon_enrichment.exists) pkg.push(artifacts.sheldon_enrichment.path);
|
|
485
|
-
if (shouldCarryFullFeatureContext && artifacts.sheldon_validation && artifacts.sheldon_validation.exists) {
|
|
486
|
-
pkg.push(artifacts.sheldon_validation.path);
|
|
487
|
-
}
|
|
483
|
+
if (shouldCarryFullFeatureContext && artifacts.prd.exists) pkg.push(artifacts.prd.path);
|
|
484
|
+
if (shouldCarryFullFeatureContext && artifacts.sheldon_enrichment.exists) pkg.push(artifacts.sheldon_enrichment.path);
|
|
485
|
+
if (shouldCarryFullFeatureContext && artifacts.sheldon_validation && artifacts.sheldon_validation.exists) {
|
|
486
|
+
pkg.push(artifacts.sheldon_validation.path);
|
|
487
|
+
}
|
|
488
488
|
if ((shouldCarryFullFeatureContext || ['analyst', 'architect'].includes(agent)) && artifacts.requirements.exists) {
|
|
489
489
|
pkg.push(artifacts.requirements.path);
|
|
490
490
|
}
|
|
@@ -685,10 +685,10 @@ function evaluateReadiness(artifacts, phaseGates, classification, agent, devStat
|
|
|
685
685
|
if (!artifacts.spec.exists) blockers.push('spec file missing');
|
|
686
686
|
if (classification && classification !== 'MICRO') {
|
|
687
687
|
if (!designDoc.exists) {
|
|
688
|
-
blockers.push('design-doc.md missing — @
|
|
688
|
+
blockers.push('design-doc.md missing — @architect (merged mode) or @sheldon must produce it before implementation');
|
|
689
689
|
}
|
|
690
690
|
if (!readiness.exists) {
|
|
691
|
-
blockers.push('readiness.md missing — @
|
|
691
|
+
blockers.push('readiness.md missing — @architect (merged mode) or @sheldon must produce it before implementation');
|
|
692
692
|
}
|
|
693
693
|
}
|
|
694
694
|
if (classification === 'MEDIUM') {
|
|
@@ -1,95 +1,95 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { normalizePolicy } = require('./result');
|
|
4
|
-
|
|
5
|
-
function ownerRoute(owner) {
|
|
6
|
-
if (owner === 'product' || owner === 'scope-check') return 'product';
|
|
7
|
-
if (owner === 'sheldon') return 'sheldon';
|
|
8
|
-
if (owner === 'qa' || owner === 'tester') return 'qa';
|
|
9
|
-
if (owner === 'pentester') return 'pentester';
|
|
10
|
-
return owner || 'dev';
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function routeForFinding(finding) {
|
|
14
|
-
const route = finding.recommended_route || ownerRoute(finding.owner);
|
|
15
|
-
if (finding.kind === 'scope_constraint' || route === 'product' || route === 'sheldon') {
|
|
16
|
-
return { verdict: 'NEEDS_SCOPE_DECISION', route: route === 'sheldon' ? 'sheldon' : 'product' };
|
|
17
|
-
}
|
|
18
|
-
if (finding.kind === 'test_coverage' || route === 'qa' || route === 'tester') {
|
|
19
|
-
return { verdict: 'NEEDS_QA_RECHECK', route: route === 'tester' ? 'tester' : 'qa' };
|
|
20
|
-
}
|
|
21
|
-
if (finding.kind === 'security_constraint' || route === 'pentester') {
|
|
22
|
-
return { verdict: 'NEEDS_SECURITY_REVIEW', route: 'pentester' };
|
|
23
|
-
}
|
|
24
|
-
return { verdict: 'NEEDS_DEV_FIX', route: route === 'deyvin' ? 'deyvin' : 'dev' };
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function isBlockingFinding(finding, policy) {
|
|
28
|
-
if (!finding) return false;
|
|
29
|
-
if (finding.severity === 'blocking' || finding.blocks === true) return true;
|
|
30
|
-
if (policy !== 'strict') return false;
|
|
31
|
-
return ['DOES_NOT_CONFIRM', 'PARTIAL', 'NOT_VERIFIED'].includes(finding.status)
|
|
32
|
-
&& [
|
|
33
|
-
'required_behavior',
|
|
34
|
-
'acceptance_criterion',
|
|
35
|
-
'prototype_contract',
|
|
36
|
-
'security_constraint'
|
|
37
|
-
].includes(finding.kind);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function commandIsMissing(command) {
|
|
41
|
-
if (!command || !command.required) return false;
|
|
42
|
-
const status = String(command.status || command.last_status || '').toLowerCase();
|
|
43
|
-
return !status || ['missing', 'not_run', 'failed', 'error', 'unknown'].includes(status);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function applyPolicy(report, requestedPolicy) {
|
|
47
|
-
const policy = normalizePolicy(requestedPolicy || report.policy) || 'standard';
|
|
48
|
-
const findings = Array.isArray(report.findings) ? report.findings : [];
|
|
49
|
-
const commands = Array.isArray(report.commands_run) ? report.commands_run : [];
|
|
50
|
-
|
|
51
|
-
if (policy === 'strict' && commands.some(commandIsMissing)) {
|
|
52
|
-
return {
|
|
53
|
-
policy,
|
|
54
|
-
verdict: 'INCONCLUSIVE',
|
|
55
|
-
recommended_route: 'qa',
|
|
56
|
-
blocking_findings_count: findings.filter((f) => isBlockingFinding(f, policy)).length,
|
|
57
|
-
reason: 'missing_required_command'
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const blockingFindings = findings.filter((finding) => isBlockingFinding(finding, policy));
|
|
62
|
-
if (blockingFindings.length > 0 && policy !== 'advisory') {
|
|
63
|
-
const routed = routeForFinding(blockingFindings[0]);
|
|
64
|
-
return {
|
|
65
|
-
policy,
|
|
66
|
-
verdict: routed.verdict,
|
|
67
|
-
recommended_route: routed.route,
|
|
68
|
-
blocking_findings_count: blockingFindings.length,
|
|
69
|
-
reason: 'blocking_findings'
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (report.verdict && report.verdict !== 'PASS') {
|
|
74
|
-
return {
|
|
75
|
-
policy,
|
|
76
|
-
verdict: report.verdict,
|
|
77
|
-
recommended_route: report.recommended_route || 'dev',
|
|
78
|
-
blocking_findings_count: blockingFindings.length,
|
|
79
|
-
reason: 'auditor_verdict'
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return {
|
|
84
|
-
policy,
|
|
85
|
-
verdict: 'PASS',
|
|
86
|
-
recommended_route: report.recommended_route || 'qa',
|
|
87
|
-
blocking_findings_count: blockingFindings.length,
|
|
88
|
-
reason: 'no_blocking_findings'
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
module.exports = {
|
|
93
|
-
applyPolicy,
|
|
94
|
-
routeForFinding
|
|
95
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { normalizePolicy } = require('./result');
|
|
4
|
+
|
|
5
|
+
function ownerRoute(owner) {
|
|
6
|
+
if (owner === 'product' || owner === 'scope-check') return 'product';
|
|
7
|
+
if (owner === 'sheldon') return 'sheldon';
|
|
8
|
+
if (owner === 'qa' || owner === 'tester') return 'qa';
|
|
9
|
+
if (owner === 'pentester') return 'pentester';
|
|
10
|
+
return owner || 'dev';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function routeForFinding(finding) {
|
|
14
|
+
const route = finding.recommended_route || ownerRoute(finding.owner);
|
|
15
|
+
if (finding.kind === 'scope_constraint' || route === 'product' || route === 'sheldon') {
|
|
16
|
+
return { verdict: 'NEEDS_SCOPE_DECISION', route: route === 'sheldon' ? 'sheldon' : 'product' };
|
|
17
|
+
}
|
|
18
|
+
if (finding.kind === 'test_coverage' || route === 'qa' || route === 'tester') {
|
|
19
|
+
return { verdict: 'NEEDS_QA_RECHECK', route: route === 'tester' ? 'tester' : 'qa' };
|
|
20
|
+
}
|
|
21
|
+
if (finding.kind === 'security_constraint' || route === 'pentester') {
|
|
22
|
+
return { verdict: 'NEEDS_SECURITY_REVIEW', route: 'pentester' };
|
|
23
|
+
}
|
|
24
|
+
return { verdict: 'NEEDS_DEV_FIX', route: route === 'deyvin' ? 'deyvin' : 'dev' };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isBlockingFinding(finding, policy) {
|
|
28
|
+
if (!finding) return false;
|
|
29
|
+
if (finding.severity === 'blocking' || finding.blocks === true) return true;
|
|
30
|
+
if (policy !== 'strict') return false;
|
|
31
|
+
return ['DOES_NOT_CONFIRM', 'PARTIAL', 'NOT_VERIFIED'].includes(finding.status)
|
|
32
|
+
&& [
|
|
33
|
+
'required_behavior',
|
|
34
|
+
'acceptance_criterion',
|
|
35
|
+
'prototype_contract',
|
|
36
|
+
'security_constraint'
|
|
37
|
+
].includes(finding.kind);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function commandIsMissing(command) {
|
|
41
|
+
if (!command || !command.required) return false;
|
|
42
|
+
const status = String(command.status || command.last_status || '').toLowerCase();
|
|
43
|
+
return !status || ['missing', 'not_run', 'failed', 'error', 'unknown'].includes(status);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function applyPolicy(report, requestedPolicy) {
|
|
47
|
+
const policy = normalizePolicy(requestedPolicy || report.policy) || 'standard';
|
|
48
|
+
const findings = Array.isArray(report.findings) ? report.findings : [];
|
|
49
|
+
const commands = Array.isArray(report.commands_run) ? report.commands_run : [];
|
|
50
|
+
|
|
51
|
+
if (policy === 'strict' && commands.some(commandIsMissing)) {
|
|
52
|
+
return {
|
|
53
|
+
policy,
|
|
54
|
+
verdict: 'INCONCLUSIVE',
|
|
55
|
+
recommended_route: 'qa',
|
|
56
|
+
blocking_findings_count: findings.filter((f) => isBlockingFinding(f, policy)).length,
|
|
57
|
+
reason: 'missing_required_command'
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const blockingFindings = findings.filter((finding) => isBlockingFinding(finding, policy));
|
|
62
|
+
if (blockingFindings.length > 0 && policy !== 'advisory') {
|
|
63
|
+
const routed = routeForFinding(blockingFindings[0]);
|
|
64
|
+
return {
|
|
65
|
+
policy,
|
|
66
|
+
verdict: routed.verdict,
|
|
67
|
+
recommended_route: routed.route,
|
|
68
|
+
blocking_findings_count: blockingFindings.length,
|
|
69
|
+
reason: 'blocking_findings'
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (report.verdict && report.verdict !== 'PASS') {
|
|
74
|
+
return {
|
|
75
|
+
policy,
|
|
76
|
+
verdict: report.verdict,
|
|
77
|
+
recommended_route: report.recommended_route || 'dev',
|
|
78
|
+
blocking_findings_count: blockingFindings.length,
|
|
79
|
+
reason: 'auditor_verdict'
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
policy,
|
|
85
|
+
verdict: 'PASS',
|
|
86
|
+
recommended_route: report.recommended_route || 'qa',
|
|
87
|
+
blocking_findings_count: blockingFindings.length,
|
|
88
|
+
reason: 'no_blocking_findings'
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
module.exports = {
|
|
93
|
+
applyPolicy,
|
|
94
|
+
routeForFinding
|
|
95
|
+
};
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Verification sub-agent policy (.aioson/config/verification.json).
|
|
4
|
+
//
|
|
5
|
+
// Piece 1 of the "smart harness, fewer agents doing more" redesign. This is the
|
|
6
|
+
// reader the @dev phase-loop (auto-continue) and the post-dev review cycle
|
|
7
|
+
// consult to decide WHICH verification sub-agent runs, WHEN, and on WHICH model
|
|
8
|
+
// — resolved per host harness.
|
|
9
|
+
//
|
|
10
|
+
// Two dispatch modes per agent, keyed by the host harness (claude|codex|opencode):
|
|
11
|
+
// - native: in-harness sub-agent. On Claude Code the Task tool runs a Claude
|
|
12
|
+
// model tier (e.g. sonnet-4.6); codex/opencode run their own
|
|
13
|
+
// configured model. You CANNOT run a codex/gpt model as a native
|
|
14
|
+
// Claude Code sub-agent — that is what `external` is for.
|
|
15
|
+
// - external: spawn a different vendor CLI as a read-only auditor
|
|
16
|
+
// (`aioson verify:implementation --tool=...`). The only way to bring
|
|
17
|
+
// a cross-vendor model in while hosted elsewhere — see `cross_check`.
|
|
18
|
+
//
|
|
19
|
+
// The file is auto-generated from template/.aioson/config/verification.json by the
|
|
20
|
+
// installer (isConfigMergePath additive merge) and is hand-editable: user values
|
|
21
|
+
// survive `aioson update`, only `version` is template-owned. A missing or
|
|
22
|
+
// malformed file degrades to buildDefaultVerificationConfig() — never throws.
|
|
23
|
+
|
|
24
|
+
const fs = require('node:fs/promises');
|
|
25
|
+
const path = require('node:path');
|
|
26
|
+
const { exists } = require('./utils');
|
|
27
|
+
|
|
28
|
+
const VERIFICATION_CONFIG_RELATIVE_PATH = '.aioson/config/verification.json';
|
|
29
|
+
const KNOWN_HOSTS = ['claude', 'codex', 'opencode'];
|
|
30
|
+
const DISPATCH_MODES = ['native', 'external'];
|
|
31
|
+
const VERIFICATION_AGENTS = ['qa', 'tester', 'pentester', 'validator'];
|
|
32
|
+
const TRIGGERS = ['per-phase', 'end-of-feature', 'sensitive-surface'];
|
|
33
|
+
const DEFAULT_HOST = 'claude';
|
|
34
|
+
const DEFAULT_MODEL = 'configured-default';
|
|
35
|
+
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// Defaults
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
function defaultTriggers(agentId) {
|
|
41
|
+
switch (agentId) {
|
|
42
|
+
case 'qa': return ['per-phase', 'end-of-feature'];
|
|
43
|
+
case 'pentester': return ['sensitive-surface', 'end-of-feature'];
|
|
44
|
+
default: return ['end-of-feature'];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function defaultReport(agentId) {
|
|
49
|
+
switch (agentId) {
|
|
50
|
+
case 'qa': return 'qa-report-{slug}.md';
|
|
51
|
+
case 'tester': return 'test-report-{slug}.md';
|
|
52
|
+
case 'pentester': return 'security-findings-{slug}.json';
|
|
53
|
+
case 'validator': return 'validator-report-{slug}.json';
|
|
54
|
+
default: return `${agentId}-report-{slug}.md`;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function buildDefaultAgent(agentId) {
|
|
59
|
+
// pentester/validator default to the strongest tier; qa/tester to a cheaper
|
|
60
|
+
// tier — the per-phase verifier should be cheap, the security/contract
|
|
61
|
+
// verdicts should not be.
|
|
62
|
+
const claudeModel = (agentId === 'pentester' || agentId === 'validator') ? 'opus-4.8' : 'sonnet-4.6';
|
|
63
|
+
const agent = {
|
|
64
|
+
enabled: (agentId === 'qa' || agentId === 'validator') ? true : 'auto',
|
|
65
|
+
triggers: defaultTriggers(agentId),
|
|
66
|
+
dispatch: {
|
|
67
|
+
claude: { mode: 'native', model: claudeModel },
|
|
68
|
+
codex: { mode: 'native', model: DEFAULT_MODEL },
|
|
69
|
+
opencode: { mode: 'native', model: DEFAULT_MODEL }
|
|
70
|
+
},
|
|
71
|
+
report: defaultReport(agentId)
|
|
72
|
+
};
|
|
73
|
+
if (agentId === 'validator') {
|
|
74
|
+
agent.cross_check = { enabled: false, mode: 'external', tool: 'codex', model: DEFAULT_MODEL };
|
|
75
|
+
}
|
|
76
|
+
return agent;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function buildDefaultVerificationConfig() {
|
|
80
|
+
const agents = {};
|
|
81
|
+
for (const id of VERIFICATION_AGENTS) agents[id] = buildDefaultAgent(id);
|
|
82
|
+
return {
|
|
83
|
+
version: '1.0',
|
|
84
|
+
host: 'auto',
|
|
85
|
+
agents,
|
|
86
|
+
budget: {
|
|
87
|
+
max_subagents_per_phase: 1,
|
|
88
|
+
skip_on_micro: true,
|
|
89
|
+
full_smoke: 'end-of-feature-only'
|
|
90
|
+
},
|
|
91
|
+
phase_loop: {
|
|
92
|
+
auto_continue: true,
|
|
93
|
+
compact_between_phases: true,
|
|
94
|
+
max_fix_retries_per_phase: 2
|
|
95
|
+
},
|
|
96
|
+
// Deterministic build-free code-quality scan (`aioson audit:code`) wired into
|
|
97
|
+
// the tracked workflow:next dev/qa done-gate. `tracked_gate`: 'advisory'
|
|
98
|
+
// records + emits but never blocks (default — audit:code is a heuristic
|
|
99
|
+
// opinion, not the feature's declared contract); 'block' makes a HIGH finding
|
|
100
|
+
// in scope a hard gate; 'off' skips. `scope`: 'changed' = the git diff (fast),
|
|
101
|
+
// 'full' = the whole tree.
|
|
102
|
+
audit_code: {
|
|
103
|
+
tracked_gate: 'advisory',
|
|
104
|
+
scope: 'changed'
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
// Normalization
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
|
|
113
|
+
// Collapse an arbitrary value to a known host, falling back when unrecognized.
|
|
114
|
+
function normalizeHost(host, fallback = DEFAULT_HOST) {
|
|
115
|
+
const safe = String(host || '').trim().toLowerCase();
|
|
116
|
+
return KNOWN_HOSTS.includes(safe) ? safe : fallback;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// The top-level `host` setting additionally allows the literal 'auto'.
|
|
120
|
+
function normalizeHostSetting(host) {
|
|
121
|
+
const safe = String(host || 'auto').trim().toLowerCase();
|
|
122
|
+
if (safe === 'auto') return 'auto';
|
|
123
|
+
return KNOWN_HOSTS.includes(safe) ? safe : 'auto';
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function normalizeEnabled(value, fallback = 'auto') {
|
|
127
|
+
if (value === true || value === false) return value;
|
|
128
|
+
if (String(value).trim().toLowerCase() === 'auto') return 'auto';
|
|
129
|
+
return fallback;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function normalizeDispatchMode(mode, fallback = 'native') {
|
|
133
|
+
const safe = String(mode || '').trim().toLowerCase();
|
|
134
|
+
return DISPATCH_MODES.includes(safe) ? safe : fallback;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function normalizeModel(model, fallback = DEFAULT_MODEL) {
|
|
138
|
+
return typeof model === 'string' && model.trim() ? model.trim() : fallback;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function normalizeDispatchEntry(entry, fallbackEntry) {
|
|
142
|
+
const safe = entry && typeof entry === 'object' ? entry : {};
|
|
143
|
+
return {
|
|
144
|
+
mode: normalizeDispatchMode(safe.mode, fallbackEntry.mode),
|
|
145
|
+
model: normalizeModel(safe.model, fallbackEntry.model)
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function normalizeTriggers(triggers, fallback) {
|
|
150
|
+
if (!Array.isArray(triggers)) return [...fallback];
|
|
151
|
+
const cleaned = triggers
|
|
152
|
+
.map((t) => String(t || '').trim().toLowerCase())
|
|
153
|
+
.filter((t) => TRIGGERS.includes(t));
|
|
154
|
+
return cleaned.length ? Array.from(new Set(cleaned)) : [...fallback];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function normalizeAgent(parsed, agentId) {
|
|
158
|
+
const def = buildDefaultAgent(agentId);
|
|
159
|
+
const raw = parsed && typeof parsed === 'object' ? parsed : {};
|
|
160
|
+
const rawDispatch = raw.dispatch && typeof raw.dispatch === 'object' ? raw.dispatch : {};
|
|
161
|
+
const dispatch = {};
|
|
162
|
+
for (const host of KNOWN_HOSTS) {
|
|
163
|
+
dispatch[host] = normalizeDispatchEntry(rawDispatch[host], def.dispatch[host]);
|
|
164
|
+
}
|
|
165
|
+
const agent = {
|
|
166
|
+
enabled: normalizeEnabled(raw.enabled, def.enabled),
|
|
167
|
+
triggers: normalizeTriggers(raw.triggers, def.triggers),
|
|
168
|
+
dispatch,
|
|
169
|
+
report: normalizeModel(raw.report, def.report)
|
|
170
|
+
};
|
|
171
|
+
if (def.cross_check) {
|
|
172
|
+
const rawCc = raw.cross_check && typeof raw.cross_check === 'object' ? raw.cross_check : {};
|
|
173
|
+
agent.cross_check = {
|
|
174
|
+
enabled: typeof rawCc.enabled === 'boolean' ? rawCc.enabled : def.cross_check.enabled,
|
|
175
|
+
mode: normalizeDispatchMode(rawCc.mode, def.cross_check.mode),
|
|
176
|
+
tool: normalizeHost(rawCc.tool, def.cross_check.tool),
|
|
177
|
+
model: normalizeModel(rawCc.model, def.cross_check.model)
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
return agent;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function normalizeBudget(parsed) {
|
|
184
|
+
const def = buildDefaultVerificationConfig().budget;
|
|
185
|
+
const raw = parsed && typeof parsed === 'object' ? parsed : {};
|
|
186
|
+
return {
|
|
187
|
+
max_subagents_per_phase: Number.isInteger(raw.max_subagents_per_phase) && raw.max_subagents_per_phase >= 0
|
|
188
|
+
? raw.max_subagents_per_phase
|
|
189
|
+
: def.max_subagents_per_phase,
|
|
190
|
+
skip_on_micro: typeof raw.skip_on_micro === 'boolean' ? raw.skip_on_micro : def.skip_on_micro,
|
|
191
|
+
full_smoke: normalizeModel(raw.full_smoke, def.full_smoke)
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function normalizePhaseLoop(parsed) {
|
|
196
|
+
const def = buildDefaultVerificationConfig().phase_loop;
|
|
197
|
+
const raw = parsed && typeof parsed === 'object' ? parsed : {};
|
|
198
|
+
return {
|
|
199
|
+
auto_continue: typeof raw.auto_continue === 'boolean' ? raw.auto_continue : def.auto_continue,
|
|
200
|
+
compact_between_phases: typeof raw.compact_between_phases === 'boolean'
|
|
201
|
+
? raw.compact_between_phases
|
|
202
|
+
: def.compact_between_phases,
|
|
203
|
+
max_fix_retries_per_phase: Number.isInteger(raw.max_fix_retries_per_phase) && raw.max_fix_retries_per_phase >= 0
|
|
204
|
+
? raw.max_fix_retries_per_phase
|
|
205
|
+
: def.max_fix_retries_per_phase
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function normalizeAuditCode(parsed) {
|
|
210
|
+
const def = buildDefaultVerificationConfig().audit_code;
|
|
211
|
+
const raw = parsed && typeof parsed === 'object' ? parsed : {};
|
|
212
|
+
const gate = String(raw.tracked_gate || '').trim().toLowerCase();
|
|
213
|
+
const scope = String(raw.scope || '').trim().toLowerCase();
|
|
214
|
+
return {
|
|
215
|
+
tracked_gate: ['block', 'advisory', 'off'].includes(gate) ? gate : def.tracked_gate,
|
|
216
|
+
scope: ['changed', 'full'].includes(scope) ? scope : def.scope
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function normalizeVerificationConfig(parsed) {
|
|
221
|
+
const def = buildDefaultVerificationConfig();
|
|
222
|
+
const raw = parsed && typeof parsed === 'object' ? parsed : {};
|
|
223
|
+
const rawAgents = raw.agents && typeof raw.agents === 'object' ? raw.agents : {};
|
|
224
|
+
const agents = {};
|
|
225
|
+
// Always materialize the four known agents (defaults fill any gaps)...
|
|
226
|
+
for (const id of VERIFICATION_AGENTS) agents[id] = normalizeAgent(rawAgents[id], id);
|
|
227
|
+
// ...and preserve any user-defined extra agents (best-effort, generic shape).
|
|
228
|
+
for (const id of Object.keys(rawAgents)) {
|
|
229
|
+
if (!VERIFICATION_AGENTS.includes(id)) agents[id] = normalizeAgent(rawAgents[id], id);
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
version: typeof raw.version === 'string' ? raw.version : def.version,
|
|
233
|
+
host: normalizeHostSetting(raw.host),
|
|
234
|
+
agents,
|
|
235
|
+
budget: normalizeBudget(raw.budget),
|
|
236
|
+
phase_loop: normalizePhaseLoop(raw.phase_loop),
|
|
237
|
+
audit_code: normalizeAuditCode(raw.audit_code)
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ---------------------------------------------------------------------------
|
|
242
|
+
// Read
|
|
243
|
+
// ---------------------------------------------------------------------------
|
|
244
|
+
|
|
245
|
+
async function readVerificationConfig(targetDir) {
|
|
246
|
+
const configPath = path.join(targetDir, VERIFICATION_CONFIG_RELATIVE_PATH);
|
|
247
|
+
if (!(await exists(configPath))) return buildDefaultVerificationConfig();
|
|
248
|
+
try {
|
|
249
|
+
const rawText = await fs.readFile(configPath, 'utf8');
|
|
250
|
+
return normalizeVerificationConfig(JSON.parse(rawText));
|
|
251
|
+
} catch {
|
|
252
|
+
return buildDefaultVerificationConfig();
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// ---------------------------------------------------------------------------
|
|
257
|
+
// Resolution / getters
|
|
258
|
+
// ---------------------------------------------------------------------------
|
|
259
|
+
|
|
260
|
+
// Precedence: explicit arg > config.host (when not 'auto') > env > default.
|
|
261
|
+
function resolveHost(config, explicitHost = null, env = process.env) {
|
|
262
|
+
const explicit = String(explicitHost || '').trim().toLowerCase();
|
|
263
|
+
if (KNOWN_HOSTS.includes(explicit)) return explicit;
|
|
264
|
+
|
|
265
|
+
const configured = config && typeof config.host === 'string' ? config.host.trim().toLowerCase() : 'auto';
|
|
266
|
+
if (KNOWN_HOSTS.includes(configured)) return configured;
|
|
267
|
+
|
|
268
|
+
const envTool = String((env && (env.AIOSON_RUNNER_TOOL || env.AIOSON_TOOL)) || '').trim().toLowerCase();
|
|
269
|
+
if (KNOWN_HOSTS.includes(envTool)) return envTool;
|
|
270
|
+
|
|
271
|
+
return DEFAULT_HOST;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function getAgentConfig(config, agentId) {
|
|
275
|
+
const id = String(agentId || '').trim();
|
|
276
|
+
if (config && config.agents && typeof config.agents === 'object' && config.agents[id]) {
|
|
277
|
+
return config.agents[id];
|
|
278
|
+
}
|
|
279
|
+
if (VERIFICATION_AGENTS.includes(id)) return buildDefaultAgent(id);
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Resolve the {mode, model, host} a verification sub-agent should run on for the
|
|
284
|
+
// active host harness. Falls back to the default-host entry, then to built-ins.
|
|
285
|
+
function getAgentDispatch(config, agentId, host = null) {
|
|
286
|
+
const agent = getAgentConfig(config, agentId);
|
|
287
|
+
if (!agent) return null;
|
|
288
|
+
const resolvedHost = resolveHost(config, host);
|
|
289
|
+
const dispatch = agent.dispatch && typeof agent.dispatch === 'object' ? agent.dispatch : {};
|
|
290
|
+
const entry = dispatch[resolvedHost] || dispatch[DEFAULT_HOST];
|
|
291
|
+
if (entry && typeof entry === 'object') {
|
|
292
|
+
return { host: resolvedHost, mode: normalizeDispatchMode(entry.mode), model: normalizeModel(entry.model) };
|
|
293
|
+
}
|
|
294
|
+
const def = buildDefaultAgent(agentId);
|
|
295
|
+
const defEntry = def.dispatch[resolvedHost] || def.dispatch[DEFAULT_HOST];
|
|
296
|
+
return { host: resolvedHost, mode: defEntry.mode, model: defEntry.model };
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Resolve enabled:true|false|'auto' to a concrete boolean given run context.
|
|
300
|
+
// 'auto' lets the framework decide: pentester only on a sensitive surface,
|
|
301
|
+
// tester on anything above MICRO, everything else on.
|
|
302
|
+
function resolveAgentEnabled(config, agentId, context = {}) {
|
|
303
|
+
const agent = getAgentConfig(config, agentId);
|
|
304
|
+
if (!agent) return false;
|
|
305
|
+
if (agent.enabled === true) return true;
|
|
306
|
+
if (agent.enabled === false) return false;
|
|
307
|
+
const classification = String(context.classification || '').trim().toUpperCase();
|
|
308
|
+
const sensitiveSurface = Boolean(context.sensitiveSurface);
|
|
309
|
+
switch (String(agentId).trim()) {
|
|
310
|
+
case 'pentester': return sensitiveSurface;
|
|
311
|
+
case 'tester': return classification !== 'MICRO';
|
|
312
|
+
default: return true;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function getAgentTriggers(config, agentId) {
|
|
317
|
+
const agent = getAgentConfig(config, agentId);
|
|
318
|
+
return agent && Array.isArray(agent.triggers) ? [...agent.triggers] : [];
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function agentHasTrigger(config, agentId, trigger) {
|
|
322
|
+
return getAgentTriggers(config, agentId).includes(String(trigger || '').trim().toLowerCase());
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function getAgentReportTemplate(config, agentId) {
|
|
326
|
+
const agent = getAgentConfig(config, agentId);
|
|
327
|
+
return agent && typeof agent.report === 'string' ? agent.report : '';
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function resolveAgentReportPath(config, agentId, slug) {
|
|
331
|
+
return getAgentReportTemplate(config, agentId).replace(/\{slug\}/g, String(slug || '').trim());
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function getCrossCheck(config, agentId) {
|
|
335
|
+
const agent = getAgentConfig(config, agentId);
|
|
336
|
+
if (!agent || !agent.cross_check || typeof agent.cross_check !== 'object') return null;
|
|
337
|
+
const cc = agent.cross_check;
|
|
338
|
+
return {
|
|
339
|
+
enabled: Boolean(cc.enabled),
|
|
340
|
+
mode: normalizeDispatchMode(cc.mode, 'external'),
|
|
341
|
+
tool: normalizeHost(cc.tool, 'codex'),
|
|
342
|
+
model: normalizeModel(cc.model)
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function getBudget(config) {
|
|
347
|
+
return normalizeBudget(config && config.budget);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Phase-loop behavior: whether @dev auto-continues across phases, compacts
|
|
351
|
+
// between them, and how many in-phase fix retries are allowed before stopping.
|
|
352
|
+
function getPhaseLoop(config) {
|
|
353
|
+
return normalizePhaseLoop(config && config.phase_loop);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// audit:code tracked-gate policy: 'block' | 'advisory' | 'off' + 'changed' | 'full'.
|
|
357
|
+
function getAuditCodePolicy(config) {
|
|
358
|
+
return normalizeAuditCode(config && config.audit_code);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Composed decision: should `agentId` run for `context.trigger` right now?
|
|
362
|
+
// Combines trigger membership + enabled resolution + the skip-on-micro budget
|
|
363
|
+
// guard (per-phase verification is suppressed on MICRO to save tokens).
|
|
364
|
+
function shouldRunForTrigger(config, agentId, context = {}) {
|
|
365
|
+
const trigger = String(context.trigger || '').trim().toLowerCase();
|
|
366
|
+
if (!agentHasTrigger(config, agentId, trigger)) return false;
|
|
367
|
+
if (!resolveAgentEnabled(config, agentId, context)) return false;
|
|
368
|
+
const classification = String(context.classification || '').trim().toUpperCase();
|
|
369
|
+
if (getBudget(config).skip_on_micro && classification === 'MICRO' && trigger === 'per-phase') return false;
|
|
370
|
+
return true;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
module.exports = {
|
|
374
|
+
VERIFICATION_CONFIG_RELATIVE_PATH,
|
|
375
|
+
KNOWN_HOSTS,
|
|
376
|
+
DISPATCH_MODES,
|
|
377
|
+
VERIFICATION_AGENTS,
|
|
378
|
+
TRIGGERS,
|
|
379
|
+
DEFAULT_HOST,
|
|
380
|
+
DEFAULT_MODEL,
|
|
381
|
+
buildDefaultVerificationConfig,
|
|
382
|
+
buildDefaultAgent,
|
|
383
|
+
normalizeVerificationConfig,
|
|
384
|
+
readVerificationConfig,
|
|
385
|
+
resolveHost,
|
|
386
|
+
getAgentConfig,
|
|
387
|
+
getAgentDispatch,
|
|
388
|
+
resolveAgentEnabled,
|
|
389
|
+
getAgentTriggers,
|
|
390
|
+
agentHasTrigger,
|
|
391
|
+
getAgentReportTemplate,
|
|
392
|
+
resolveAgentReportPath,
|
|
393
|
+
getCrossCheck,
|
|
394
|
+
getBudget,
|
|
395
|
+
getPhaseLoop,
|
|
396
|
+
getAuditCodePolicy,
|
|
397
|
+
shouldRunForTrigger
|
|
398
|
+
};
|