@jaimevalasek/aioson 1.30.2 → 1.36.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 (158) hide show
  1. package/CHANGELOG.md +75 -0
  2. package/README.md +19 -6
  3. package/docs/en/1-understand/ecosystem-map.md +45 -29
  4. package/docs/en/1-understand/glossary.md +5 -5
  5. package/docs/en/1-understand/what-is-aioson.md +5 -5
  6. package/docs/en/2-start/existing-project.md +7 -7
  7. package/docs/en/2-start/first-project.md +32 -38
  8. package/docs/en/2-start/initial-decisions.md +18 -17
  9. package/docs/en/3-recipes/README.md +2 -2
  10. package/docs/en/3-recipes/continuity-between-sessions.md +2 -2
  11. package/docs/en/3-recipes/from-idea-to-prd-via-briefing.md +5 -2
  12. package/docs/en/3-recipes/full-feature-with-sheldon.md +327 -338
  13. package/docs/en/4-agents/README.md +28 -14
  14. package/docs/en/4-agents/discovery-design-doc.md +20 -8
  15. package/docs/en/4-agents/forge-run.md +3 -2
  16. package/docs/en/5-reference/cli-reference.md +51 -48
  17. package/docs/en/5-reference/executable-verification.md +10 -7
  18. package/docs/en/5-reference/parallel.md +2 -0
  19. package/docs/en/5-reference/qa-browser.md +2 -2
  20. package/docs/en/README.md +3 -3
  21. package/docs/pt/1-entender/glossario.md +4 -4
  22. package/docs/pt/1-entender/mapa-do-ecossistema.md +32 -21
  23. package/docs/pt/2-comecar/decisoes-iniciais.md +16 -16
  24. package/docs/pt/2-comecar/primeiro-projeto.md +1 -1
  25. package/docs/pt/3-receitas/README.md +1 -1
  26. package/docs/pt/3-receitas/app-saas-do-zero.md +35 -122
  27. package/docs/pt/3-receitas/feature-completa-com-sheldon.md +289 -338
  28. package/docs/pt/4-agentes/analyst.md +9 -5
  29. package/docs/pt/4-agentes/architect.md +9 -5
  30. package/docs/pt/4-agentes/dev.md +19 -7
  31. package/docs/pt/4-agentes/discovery-design-doc.md +25 -23
  32. package/docs/pt/4-agentes/orchestrator.md +164 -118
  33. package/docs/pt/4-agentes/pentester.md +5 -5
  34. package/docs/pt/4-agentes/pm.md +15 -7
  35. package/docs/pt/4-agentes/sheldon.md +166 -99
  36. package/docs/pt/4-agentes/ux-ui.md +7 -4
  37. package/docs/pt/5-referencia/agent-chain-continuity.md +14 -5
  38. package/docs/pt/5-referencia/comandos-cli.md +1 -1
  39. package/docs/pt/5-referencia/harness-retro.md +2 -1
  40. package/docs/pt/5-referencia/sdd-automation-scripts.md +6 -6
  41. package/docs/pt/5-referencia/sdd-framework.md +53 -16
  42. package/docs/pt/5-referencia/sdd-planos-e-estrutura.md +27 -15
  43. package/docs/pt/README.md +4 -4
  44. package/docs/pt/agentes.md +48 -50
  45. package/package.json +2 -2
  46. package/src/artifact-kinds.js +110 -0
  47. package/src/cli.js +82 -42
  48. package/src/commands/agent-epilogue.js +251 -186
  49. package/src/commands/agents.js +104 -48
  50. package/src/commands/audit-code.js +344 -0
  51. package/src/commands/classify.js +75 -13
  52. package/src/commands/feature-close.js +43 -18
  53. package/src/commands/harness-check.js +259 -175
  54. package/src/commands/harness-retro-promote.js +387 -0
  55. package/src/commands/live.js +24 -0
  56. package/src/commands/prototype-check.js +163 -0
  57. package/src/commands/review-feature.js +189 -0
  58. package/src/commands/runtime.js +81 -66
  59. package/src/commands/sync-agents-copy.js +115 -0
  60. package/src/commands/verification-plan.js +116 -0
  61. package/src/commands/verify-artifact.js +530 -0
  62. package/src/commands/verify-implementation.js +428 -0
  63. package/src/commands/workflow-execute.js +309 -309
  64. package/src/commands/workflow-next.js +361 -19
  65. package/src/commands/workflow-plan.js +5 -5
  66. package/src/constants.js +4 -0
  67. package/src/gateway-pointer-merge.js +7 -1
  68. package/src/handoff-contract.js +267 -172
  69. package/src/harness/contract-integrity-gate.js +172 -0
  70. package/src/harness/contract-integrity.js +111 -0
  71. package/src/harness/contract-schema.js +377 -332
  72. package/src/harness/detect-runtime-feature.js +90 -0
  73. package/src/harness/static-criteria.js +192 -0
  74. package/src/i18n/messages/en.js +8 -4
  75. package/src/i18n/messages/es.js +8 -4
  76. package/src/i18n/messages/fr.js +8 -4
  77. package/src/i18n/messages/pt-BR.js +8 -4
  78. package/src/install-wizard.js +8 -8
  79. package/src/installer.js +13 -6
  80. package/src/lib/retro/retro-render.js +10 -1
  81. package/src/lib/retro/retro-sources.js +45 -27
  82. package/src/lib/retro/verification-reports.js +230 -0
  83. package/src/parser.js +6 -0
  84. package/src/preflight-engine.js +12 -12
  85. package/src/runtime-store.js +13 -9
  86. package/src/verification/evidence-bundle.js +251 -0
  87. package/src/verification/ledger-store.js +221 -0
  88. package/src/verification/path-policy.js +74 -0
  89. package/src/verification/policy-engine.js +95 -0
  90. package/src/verification/prompt-package.js +314 -0
  91. package/src/verification/redaction.js +77 -0
  92. package/src/verification/report-parser.js +132 -0
  93. package/src/verification/report-store.js +97 -0
  94. package/src/verification/result.js +16 -0
  95. package/src/verification/runners/index.js +319 -0
  96. package/src/verification/runtime-telemetry.js +144 -0
  97. package/src/verification/schema.js +276 -0
  98. package/src/verification/source-discovery.js +153 -0
  99. package/src/verification-policy.js +398 -0
  100. package/src/version.js +52 -1
  101. package/template/.aioson/agents/analyst.md +9 -6
  102. package/template/.aioson/agents/architect.md +34 -5
  103. package/template/.aioson/agents/briefing-refiner.md +25 -0
  104. package/template/.aioson/agents/briefing.md +69 -12
  105. package/template/.aioson/agents/committer.md +2 -1
  106. package/template/.aioson/agents/copywriter.md +30 -21
  107. package/template/.aioson/agents/design-hybrid-forge.md +28 -15
  108. package/template/.aioson/agents/dev.md +28 -10
  109. package/template/.aioson/agents/deyvin.md +3 -2
  110. package/template/.aioson/agents/discover.md +12 -3
  111. package/template/.aioson/agents/discovery-design-doc.md +7 -2
  112. package/template/.aioson/agents/genome.md +19 -10
  113. package/template/.aioson/agents/neo.md +30 -30
  114. package/template/.aioson/agents/orache.md +20 -11
  115. package/template/.aioson/agents/orchestrator.md +84 -7
  116. package/template/.aioson/agents/pm.md +8 -8
  117. package/template/.aioson/agents/product.md +15 -11
  118. package/template/.aioson/agents/profiler-enricher.md +20 -11
  119. package/template/.aioson/agents/profiler-forge.md +20 -11
  120. package/template/.aioson/agents/profiler-researcher.md +20 -11
  121. package/template/.aioson/agents/qa.md +87 -17
  122. package/template/.aioson/agents/scope-check.md +19 -5
  123. package/template/.aioson/agents/setup.md +12 -1
  124. package/template/.aioson/agents/sheldon.md +92 -9
  125. package/template/.aioson/agents/site-forge.md +11 -2
  126. package/template/.aioson/agents/squad.md +20 -5
  127. package/template/.aioson/agents/ux-ui.md +4 -2
  128. package/template/.aioson/agents/validator.md +33 -1
  129. package/template/.aioson/config/verification.json +61 -0
  130. package/template/.aioson/config.md +13 -9
  131. package/template/.aioson/docs/LAYERS.md +2 -2
  132. package/template/.aioson/docs/autopilot-handoff.md +10 -10
  133. package/template/.aioson/docs/dev/execution-discipline.md +41 -0
  134. package/template/.aioson/docs/dev/phase-loop.md +47 -0
  135. package/template/.aioson/docs/feature-expansion-taxonomy.md +31 -3
  136. package/template/.aioson/docs/presets/workflow.config.full-merged.json +15 -0
  137. package/template/.aioson/docs/presets/workflow.config.lean.json +15 -0
  138. package/template/.aioson/docs/product/prd-contract.md +12 -12
  139. package/template/.aioson/docs/prototype-contract.md +98 -0
  140. package/template/.aioson/docs/reference-identity.md +94 -0
  141. package/template/.aioson/docs/sheldon/harness-contract.md +155 -28
  142. package/template/.aioson/docs/verification-config.md +82 -0
  143. package/template/.aioson/docs/verify-artifact-gates.md +91 -0
  144. package/template/.aioson/docs/workflow-lean-lane.md +129 -0
  145. package/template/.aioson/skills/design/interface-design/references/intent-and-domain.md +2 -0
  146. package/template/.aioson/skills/process/aioson-spec-driven/SKILL.md +16 -14
  147. package/template/.aioson/skills/process/aioson-spec-driven/references/approval-gates.md +2 -2
  148. package/template/.aioson/skills/process/aioson-spec-driven/references/classification-map.md +4 -4
  149. package/template/.aioson/skills/process/aioson-spec-driven/references/dev.md +15 -15
  150. package/template/.aioson/skills/process/briefing-expansion-scout/SKILL.md +25 -3
  151. package/template/.aioson/skills/process/design-hybrid-forge/SKILL.md +5 -3
  152. package/template/.aioson/skills/process/design-hybrid-forge/references/external-source-ingestion.md +89 -0
  153. package/template/.aioson/skills/process/product-scope-expansion/SKILL.md +29 -2
  154. package/template/.aioson/skills/process/prototype-forge/SKILL.md +98 -0
  155. package/template/.aioson/skills/process/reference-identity-extract/SKILL.md +164 -0
  156. package/template/.aioson/skills/process/sheldon-expansion-audit/SKILL.md +23 -2
  157. package/template/.aioson/skills/static/multi-agent-patterns.md +5 -5
  158. package/template/CLAUDE.md +15 -11
@@ -1,186 +1,251 @@
1
- 'use strict';
2
-
3
- const path = require('node:path');
4
- const { runPulseUpdate } = require('./pulse-update');
5
- const { runDossierAddFinding } = require('./dossier');
6
- const { runGateApprove } = require('./gate-approve');
7
- const { runAgentDone } = require('./runtime');
8
-
9
- function resolveTargetDir(args) {
10
- return path.resolve(process.cwd(), args[0] || '.');
11
- }
12
-
13
- function normalizeAgent(value) {
14
- return String(value || '').trim().replace(/^@/, '');
15
- }
16
-
17
- function normalizeList(value) {
18
- if (!value) return [];
19
- if (Array.isArray(value)) return value.map(String).map((item) => item.trim()).filter(Boolean);
20
- return String(value).split(',').map((item) => item.trim()).filter(Boolean);
21
- }
22
-
23
- function makeSilentLogger() {
24
- return { log() {}, error() {}, warn() {} };
25
- }
26
-
27
- function pushStep(steps, name, result) {
28
- steps.push({
29
- name,
30
- ok: Boolean(result && result.ok),
31
- skipped: Boolean(result && result.skipped),
32
- reason: result && (result.reason || result.error || null)
33
- });
34
- }
35
-
36
- function formatAutoAdvance(autoAdvance) {
37
- if (!autoAdvance) return null;
38
- if (autoAdvance.advanced) {
39
- const nextStage = autoAdvance.result && (autoAdvance.result.next || autoAdvance.result.nextStage);
40
- return `workflow auto-advanced${nextStage ? ` -> ${nextStage}` : ''}`;
41
- }
42
- return `workflow skip: ${autoAdvance.skipped || 'not_advanced'}`;
43
- }
44
-
45
- async function runAgentEpilogue({ args, options = {}, logger, t }) {
46
- const targetDir = resolveTargetDir(args);
47
- const agent = normalizeAgent(options.agent);
48
- const summary = String(options.summary || options.message || '').trim();
49
- const feature = options.feature ? String(options.feature).trim() : null;
50
- const action = options.action ? String(options.action).trim() : summary;
51
- const next = options.next ? String(options.next).trim() : null;
52
- const gate = options.gate ? String(options.gate).trim() : null;
53
- const approveGate = options['approve-gate'] || options.approveGate
54
- ? String(options['approve-gate'] || options.approveGate).trim().toUpperCase()
55
- : null;
56
- const verdict = options.verdict ? String(options.verdict).trim().toUpperCase() : null;
57
- const artifacts = normalizeList(options.artifacts);
58
- const strict = Boolean(options.strict);
59
- const steps = [];
60
- const errors = [];
61
- const silentLogger = makeSilentLogger();
62
-
63
- if (!agent) {
64
- const failure = { ok: false, reason: 'missing_agent' };
65
- if (options.json) return failure;
66
- logger.error('--agent=<agent> is required.');
67
- return failure;
68
- }
69
-
70
- if (!summary) {
71
- const failure = { ok: false, reason: 'missing_summary' };
72
- if (options.json) return failure;
73
- logger.error('--summary="<summary>" is required.');
74
- return failure;
75
- }
76
-
77
- if (approveGate) {
78
- const gateResult = await runGateApprove({
79
- args: [targetDir],
80
- options: {
81
- feature,
82
- gate: approveGate,
83
- agent,
84
- json: true
85
- },
86
- logger: silentLogger
87
- });
88
- pushStep(steps, 'gate:approve', gateResult);
89
- if (!gateResult.ok) {
90
- errors.push({ step: 'gate:approve', reason: gateResult.reason || 'gate_failed', result: gateResult });
91
- if (strict) {
92
- const failure = { ok: false, reason: 'gate_approve_failed', steps, errors };
93
- if (options.json) return failure;
94
- logger.error(`agent:epilogue blocked: gate ${approveGate} approval failed.`);
95
- return failure;
96
- }
97
- }
98
- }
99
-
100
- if (!options['no-pulse'] && !options.noPulse) {
101
- const pulseResult = await runPulseUpdate({
102
- args: [targetDir],
103
- options: {
104
- agent,
105
- ...(feature ? { feature } : {}),
106
- ...(gate || approveGate ? { gate: gate || `Gate ${approveGate}: approved` } : {}),
107
- ...(action ? { action } : {}),
108
- ...(next ? { next } : {}),
109
- ...(verdict ? { verdict } : {}),
110
- json: true
111
- },
112
- logger: silentLogger
113
- });
114
- pushStep(steps, 'pulse:update', pulseResult);
115
- if (!pulseResult.ok) {
116
- errors.push({ step: 'pulse:update', reason: pulseResult.reason || 'pulse_failed', result: pulseResult });
117
- }
118
- } else {
119
- pushStep(steps, 'pulse:update', { ok: true, skipped: true, reason: 'disabled' });
120
- }
121
-
122
- if (feature && !options['no-dossier'] && !options.noDossier) {
123
- const dossierResult = await runDossierAddFinding({
124
- args: [targetDir],
125
- options: {
126
- slug: feature,
127
- agent,
128
- section: options.section ? String(options.section) : 'Agent Trail',
129
- content: options.content ? String(options.content) : summary,
130
- json: true
131
- },
132
- logger: silentLogger
133
- });
134
- pushStep(steps, 'dossier:add-finding', dossierResult);
135
- if (!dossierResult.ok) {
136
- errors.push({ step: 'dossier:add-finding', reason: dossierResult.reason || 'dossier_failed', result: dossierResult });
137
- }
138
- } else {
139
- pushStep(steps, 'dossier:add-finding', { ok: true, skipped: true, reason: feature ? 'disabled' : 'missing_feature' });
140
- }
141
-
142
- const doneResult = await runAgentDone({
143
- args: [targetDir],
144
- options: {
145
- agent,
146
- summary,
147
- ...(feature ? { feature } : {}),
148
- ...(verdict ? { verdict } : {}),
149
- ...(artifacts.length > 0 ? { artifacts: artifacts.join(',') } : {}),
150
- json: true
151
- },
152
- logger: silentLogger,
153
- t
154
- });
155
- pushStep(steps, 'agent:done', doneResult);
156
- if (!doneResult.ok) {
157
- errors.push({ step: 'agent:done', reason: doneResult.reason || doneResult.error || 'agent_done_failed', result: doneResult });
158
- }
159
-
160
- const ok = doneResult.ok && (strict ? errors.length === 0 : !errors.some((error) => error.step === 'agent:done'));
161
- const result = {
162
- ok,
163
- targetDir,
164
- agent: `@${agent}`,
165
- feature,
166
- summary,
167
- steps,
168
- errors,
169
- agent_done: doneResult
170
- };
171
-
172
- if (options.json) return result;
173
-
174
- logger.log(`agent:epilogue @${agent} (${ok ? 'ok' : 'issues'})`);
175
- for (const step of steps) {
176
- const marker = step.skipped ? 'skip' : step.ok ? 'ok' : 'fail';
177
- logger.log(` ${marker} ${step.name}${step.reason ? ` (${step.reason})` : ''}`);
178
- }
179
- const autoAdvanceMessage = formatAutoAdvance(doneResult.auto_advance);
180
- if (autoAdvanceMessage) logger.log(` ${autoAdvanceMessage}`);
181
- return result;
182
- }
183
-
184
- module.exports = {
185
- runAgentEpilogue
186
- };
1
+ 'use strict';
2
+
3
+ const path = require('node:path');
4
+ const { runPulseUpdate } = require('./pulse-update');
5
+ const { runDossierAddFinding } = require('./dossier');
6
+ const { runGateApprove } = require('./gate-approve');
7
+ const { runAgentDone } = require('./runtime');
8
+
9
+ function resolveTargetDir(args) {
10
+ return path.resolve(process.cwd(), args[0] || '.');
11
+ }
12
+
13
+ function normalizeAgent(value) {
14
+ return String(value || '').trim().replace(/^@/, '');
15
+ }
16
+
17
+ function normalizeList(value) {
18
+ if (!value) return [];
19
+ if (Array.isArray(value)) return value.map(String).map((item) => item.trim()).filter(Boolean);
20
+ return String(value).split(',').map((item) => item.trim()).filter(Boolean);
21
+ }
22
+
23
+ function makeSilentLogger() {
24
+ return { log() {}, error() {}, warn() {} };
25
+ }
26
+
27
+ function pushStep(steps, name, result) {
28
+ steps.push({
29
+ name,
30
+ ok: Boolean(result && result.ok),
31
+ skipped: Boolean(result && result.skipped),
32
+ reason: result && (result.reason || result.error || null)
33
+ });
34
+ }
35
+
36
+ function formatAutoAdvance(autoAdvance) {
37
+ if (!autoAdvance) return null;
38
+ if (autoAdvance.advanced) {
39
+ const nextStage = autoAdvance.result && (autoAdvance.result.next || autoAdvance.result.nextStage);
40
+ return `workflow auto-advanced${nextStage ? ` -> ${nextStage}` : ''}`;
41
+ }
42
+ return `workflow skip: ${autoAdvance.skipped || 'not_advanced'}`;
43
+ }
44
+
45
+ async function runAgentEpilogue({ args, options = {}, logger, t }) {
46
+ const targetDir = resolveTargetDir(args);
47
+ const agent = normalizeAgent(options.agent);
48
+ const summary = String(options.summary || options.message || '').trim();
49
+ const feature = options.feature ? String(options.feature).trim() : null;
50
+ const action = options.action ? String(options.action).trim() : summary;
51
+ const next = options.next ? String(options.next).trim() : null;
52
+ const gate = options.gate ? String(options.gate).trim() : null;
53
+ const approveGate = options['approve-gate'] || options.approveGate
54
+ ? String(options['approve-gate'] || options.approveGate).trim().toUpperCase()
55
+ : null;
56
+ const verdict = options.verdict ? String(options.verdict).trim().toUpperCase() : null;
57
+ const artifacts = normalizeList(options.artifacts);
58
+ const strict = Boolean(options.strict);
59
+ const steps = [];
60
+ const errors = [];
61
+ const silentLogger = makeSilentLogger();
62
+
63
+ if (!agent) {
64
+ const failure = { ok: false, reason: 'missing_agent' };
65
+ if (options.json) return failure;
66
+ logger.error('--agent=<agent> is required.');
67
+ return failure;
68
+ }
69
+
70
+ if (!summary) {
71
+ const failure = { ok: false, reason: 'missing_summary' };
72
+ if (options.json) return failure;
73
+ logger.error('--summary="<summary>" is required.');
74
+ return failure;
75
+ }
76
+
77
+ if (approveGate) {
78
+ const gateResult = await runGateApprove({
79
+ args: [targetDir],
80
+ options: {
81
+ feature,
82
+ gate: approveGate,
83
+ agent,
84
+ json: true
85
+ },
86
+ logger: silentLogger
87
+ });
88
+ pushStep(steps, 'gate:approve', gateResult);
89
+ if (!gateResult.ok) {
90
+ errors.push({ step: 'gate:approve', reason: gateResult.reason || 'gate_failed', result: gateResult });
91
+ if (strict) {
92
+ const failure = { ok: false, reason: 'gate_approve_failed', steps, errors };
93
+ if (options.json) return failure;
94
+ logger.error(`agent:epilogue blocked: gate ${approveGate} approval failed.`);
95
+ return failure;
96
+ }
97
+ }
98
+ }
99
+
100
+ if (!options['no-pulse'] && !options.noPulse) {
101
+ const pulseResult = await runPulseUpdate({
102
+ args: [targetDir],
103
+ options: {
104
+ agent,
105
+ ...(feature ? { feature } : {}),
106
+ ...(gate || approveGate ? { gate: gate || `Gate ${approveGate}: approved` } : {}),
107
+ ...(action ? { action } : {}),
108
+ ...(next ? { next } : {}),
109
+ ...(verdict ? { verdict } : {}),
110
+ json: true
111
+ },
112
+ logger: silentLogger
113
+ });
114
+ pushStep(steps, 'pulse:update', pulseResult);
115
+ if (!pulseResult.ok) {
116
+ errors.push({ step: 'pulse:update', reason: pulseResult.reason || 'pulse_failed', result: pulseResult });
117
+ }
118
+ } else {
119
+ pushStep(steps, 'pulse:update', { ok: true, skipped: true, reason: 'disabled' });
120
+ }
121
+
122
+ if (feature && !options['no-dossier'] && !options.noDossier) {
123
+ const dossierResult = await runDossierAddFinding({
124
+ args: [targetDir],
125
+ options: {
126
+ slug: feature,
127
+ agent,
128
+ section: options.section ? String(options.section) : 'Agent Trail',
129
+ content: options.content ? String(options.content) : summary,
130
+ json: true
131
+ },
132
+ logger: silentLogger
133
+ });
134
+ pushStep(steps, 'dossier:add-finding', dossierResult);
135
+ if (!dossierResult.ok) {
136
+ errors.push({ step: 'dossier:add-finding', reason: dossierResult.reason || 'dossier_failed', result: dossierResult });
137
+ }
138
+ } else {
139
+ pushStep(steps, 'dossier:add-finding', { ok: true, skipped: true, reason: feature ? 'disabled' : 'missing_feature' });
140
+ }
141
+
142
+ const doneResult = await runAgentDone({
143
+ args: [targetDir],
144
+ options: {
145
+ agent,
146
+ summary,
147
+ ...(feature ? { feature } : {}),
148
+ ...(verdict ? { verdict } : {}),
149
+ ...(artifacts.length > 0 ? { artifacts: artifacts.join(',') } : {}),
150
+ json: true
151
+ },
152
+ logger: silentLogger,
153
+ t
154
+ });
155
+ pushStep(steps, 'agent:done', doneResult);
156
+ if (!doneResult.ok) {
157
+ errors.push({ step: 'agent:done', reason: doneResult.reason || doneResult.error || 'agent_done_failed', result: doneResult });
158
+ }
159
+
160
+ // Surface the advisory artifact done-gate that agent:done resolved for this
161
+ // agent (peripheral artifact-producing agents only; null otherwise). Advisory:
162
+ // a skipped/failed check is shown but never added to `errors`, so it cannot
163
+ // flip `ok`.
164
+ if (doneResult && doneResult.verify_artifact) {
165
+ const va = doneResult.verify_artifact;
166
+ pushStep(steps, 'verify:artifact', { ok: va.skipped ? true : va.ok, skipped: va.skipped, reason: va.reason });
167
+ }
168
+
169
+ // Advisory contract-integrity signal for untracked (prompt-only) dev/qa
170
+ // completions. The tracked `workflow:next --complete` / `feature:close` paths
171
+ // enforce this as a HARD gate; a direct Claude Code session never calls them,
172
+ // so we surface the same signal here without blocking the best-effort
173
+ // epilogue (never added to `errors`, so it cannot flip `ok`).
174
+ if ((agent === 'dev' || agent === 'qa') && feature) {
175
+ let advisory = null;
176
+ try {
177
+ const { evaluateContractIntegrityGate } = require('../harness/contract-integrity-gate');
178
+ advisory = await evaluateContractIntegrityGate(targetDir, feature, { runChecks: false });
179
+ } catch {
180
+ advisory = null;
181
+ }
182
+ if (advisory && advisory.ok === false) {
183
+ pushStep(steps, 'contract:integrity', {
184
+ ok: false,
185
+ reason: `${advisory.errors.map((e) => e.code).join(', ')} — advisory only; the tracked workflow blocks on this. Run: aioson harness:check . --slug=${feature} --json`
186
+ });
187
+ } else if (advisory) {
188
+ pushStep(steps, 'contract:integrity', { ok: true });
189
+ }
190
+ }
191
+
192
+ // Advisory code-quality signal (audit:code) over the CHANGED files for dev/qa
193
+ // completions — the non-security categories (anti-patterns, TODOs, dead code,
194
+ // duplication). Scoped to the diff so it is about the work just done; advisory
195
+ // only (never added to `errors`, so it cannot flip `ok`). This is what makes the
196
+ // scan auto-fire in the flow instead of relying on the agent to remember to run
197
+ // it; @qa still treats a HIGH as a Gate-D blocker.
198
+ if (agent === 'dev' || agent === 'qa') {
199
+ let codeAudit = null;
200
+ try {
201
+ const { runAuditCode } = require('./audit-code');
202
+ codeAudit = await runAuditCode({
203
+ args: [targetDir],
204
+ options: { changed: true, json: true, suppressExitCode: true },
205
+ logger: silentLogger
206
+ });
207
+ } catch {
208
+ codeAudit = null;
209
+ }
210
+ if (codeAudit && codeAudit.by_severity && codeAudit.scanned_files > 0) {
211
+ const high = codeAudit.by_severity.HIGH || 0;
212
+ const med = codeAudit.by_severity.MED || 0;
213
+ if (high > 0) {
214
+ const cats = Object.keys(codeAudit.by_category || {}).join(', ');
215
+ pushStep(steps, 'audit:code', {
216
+ ok: false,
217
+ reason: `${high} HIGH${med ? ` / ${med} MED` : ''} in changed files (${cats}) — advisory; @qa treats HIGH as a Gate-D blocker. See .aioson/context/audit-code.json`
218
+ });
219
+ } else {
220
+ pushStep(steps, 'audit:code', { ok: true, reason: med ? `${med} MED advisory` : null });
221
+ }
222
+ }
223
+ }
224
+
225
+ const ok = doneResult.ok && (strict ? errors.length === 0 : !errors.some((error) => error.step === 'agent:done'));
226
+ const result = {
227
+ ok,
228
+ targetDir,
229
+ agent: `@${agent}`,
230
+ feature,
231
+ summary,
232
+ steps,
233
+ errors,
234
+ agent_done: doneResult
235
+ };
236
+
237
+ if (options.json) return result;
238
+
239
+ logger.log(`agent:epilogue — @${agent} (${ok ? 'ok' : 'issues'})`);
240
+ for (const step of steps) {
241
+ const marker = step.skipped ? 'skip' : step.ok ? 'ok' : 'fail';
242
+ logger.log(` ${marker} ${step.name}${step.reason ? ` (${step.reason})` : ''}`);
243
+ }
244
+ const autoAdvanceMessage = formatAutoAdvance(doneResult.auto_advance);
245
+ if (autoAdvanceMessage) logger.log(` ${autoAdvanceMessage}`);
246
+ return result;
247
+ }
248
+
249
+ module.exports = {
250
+ runAgentEpilogue
251
+ };
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const path = require('node:path');
4
+ const fsp = require('node:fs/promises');
4
5
  const {
5
6
  normalizeAgentName,
6
7
  listAgentDefinitions,
@@ -20,51 +21,102 @@ const { readAutonomyProtocol, resolveEffectiveMode } = require('../autonomy-poli
20
21
  const { readAgentManifest, buildAgentCapabilitySummary } = require('../agent-manifests');
21
22
  const { emitSecurityRuntimeEvent } = require('../lib/security/runtime-events');
22
23
 
23
- const WORKFLOW_AGENT_IDS = new Set([
24
- 'setup',
25
- 'product',
26
- 'analyst',
27
- 'scope-check',
28
- 'architect',
24
+ const WORKFLOW_AGENT_IDS = new Set([
25
+ 'setup',
26
+ 'product',
27
+ 'analyst',
28
+ 'scope-check',
29
+ 'architect',
29
30
  'ux-ui',
30
31
  'pm',
31
32
  'orchestrator',
32
33
  'dev',
33
- 'qa'
34
- ]);
35
- const SCOPE_CHECK_MODES = new Set(['pre-dev', 'post-dev', 'post-fix', 'final']);
36
-
37
- function normalizeScopeCheckMode(input) {
38
- const mode = String(input || '').trim().toLowerCase();
39
- return SCOPE_CHECK_MODES.has(mode) ? mode : null;
40
- }
41
-
42
- function getScopeCheckModeOption(options = {}) {
43
- return normalizeScopeCheckMode(
44
- options.scopeMode ||
45
- options['scope-mode'] ||
46
- options.checkMode ||
47
- options['check-mode'] ||
48
- options.mode
49
- );
50
- }
51
-
52
- function buildScopeCheckActivationContext(options = {}) {
53
- const mode = getScopeCheckModeOption(options) || 'pre-dev';
54
- const lines = [`Scope-check mode: ${mode}.`];
55
- const featureSlug = String(options.feature || options.slug || '').trim();
56
- if (featureSlug) lines.push(`Feature slug: ${featureSlug}.`);
57
- if (mode === 'pre-dev') {
58
- lines.push('Compare user intent against planning artifacts before implementation.');
59
- } else if (mode === 'post-dev') {
60
- lines.push('Compare the approved planning artifacts against the actual implementation diff and changed files before QA.');
61
- } else if (mode === 'post-fix') {
62
- lines.push('Compare approved scope, QA/tester/pentester findings, and correction diff; confirm the fix did not change product intent.');
63
- } else if (mode === 'final') {
64
- lines.push('Reconcile intent, plan, delivered behavior, and remaining exclusions before close/commit/release.');
65
- }
66
- return lines.join('\n');
67
- }
34
+ 'qa'
35
+ ]);
36
+ const SCOPE_CHECK_MODES = new Set(['pre-dev', 'post-dev', 'post-fix', 'final']);
37
+
38
+ function normalizeScopeCheckMode(input) {
39
+ const mode = String(input || '').trim().toLowerCase();
40
+ return SCOPE_CHECK_MODES.has(mode) ? mode : null;
41
+ }
42
+
43
+ function getScopeCheckModeOption(options = {}) {
44
+ return normalizeScopeCheckMode(
45
+ options.scopeMode ||
46
+ options['scope-mode'] ||
47
+ options.checkMode ||
48
+ options['check-mode'] ||
49
+ options.mode
50
+ );
51
+ }
52
+
53
+ function buildScopeCheckActivationContext(options = {}) {
54
+ const mode = getScopeCheckModeOption(options) || 'pre-dev';
55
+ const lines = [`Scope-check mode: ${mode}.`];
56
+ const featureSlug = String(options.feature || options.slug || '').trim();
57
+ if (featureSlug) lines.push(`Feature slug: ${featureSlug}.`);
58
+ if (mode === 'pre-dev') {
59
+ lines.push('Compare user intent against planning artifacts before implementation.');
60
+ } else if (mode === 'post-dev') {
61
+ lines.push('Compare the approved planning artifacts against the actual implementation diff and changed files before QA.');
62
+ } else if (mode === 'post-fix') {
63
+ lines.push('Compare approved scope, QA/tester/pentester findings, and correction diff; confirm the fix did not change product intent.');
64
+ } else if (mode === 'final') {
65
+ lines.push('Reconcile intent, plan, delivered behavior, and remaining exclusions before close/commit/release.');
66
+ }
67
+ return lines.join('\n');
68
+ }
69
+
70
+ function buildTesterActivationContext(options = {}) {
71
+ // Lets `agent:prompt tester --feature=<slug>` pin the slug into the prompt.
72
+ // Without it, @tester resolves the slug via `feature:current`, which returns
73
+ // none once a feature is closed — so a standalone post-close test pass would
74
+ // wrongly fall back to project mode. An explicit slug keeps it feature-scoped.
75
+ const featureSlug = String(options.feature || options.slug || '').trim();
76
+ if (!featureSlug) return '';
77
+ return [
78
+ `Feature slug: ${featureSlug}.`,
79
+ `This is a standalone test pass over the already-implemented feature "${featureSlug}"; write test-plan-${featureSlug}.md and test-inventory-${featureSlug}.md.`,
80
+ 'The feature may already be closed — do NOT fall back to project mode or pick a different slug.'
81
+ ].join('\n');
82
+ }
83
+
84
+ // True when the project's workflow.config.json routes @sheldon directly into
85
+ // @dev in any sequence — i.e. the lean lane is active. Best-effort + read-only.
86
+ async function detectLeanLaneFromConfig(targetDir) {
87
+ try {
88
+ const configPath = path.join(targetDir, '.aioson', 'context', 'workflow.config.json');
89
+ const config = JSON.parse(await fsp.readFile(configPath, 'utf8'));
90
+ const groups = [config.feature, config.project].filter((g) => g && typeof g === 'object');
91
+ for (const group of groups) {
92
+ for (const seq of Object.values(group)) {
93
+ if (!Array.isArray(seq)) continue;
94
+ const norm = seq.map((s) => String(s || '').trim().toLowerCase().replace(/^@/, ''));
95
+ const i = norm.indexOf('sheldon');
96
+ if (i !== -1 && norm[i + 1] === 'dev') return true;
97
+ }
98
+ }
99
+ return false;
100
+ } catch {
101
+ return false;
102
+ }
103
+ }
104
+
105
+ // Deterministic lane signal for a directly-activated @sheldon. When the project
106
+ // opted into the lean lane (workflow.config.json routes @sheldon -> @dev) but the
107
+ // session is started by hand (slash / agent:prompt, not workflow:next), nothing
108
+ // otherwise tells @sheldon to run RF-LEAN — so it falls back to enrichment mode
109
+ // and hands to @analyst. This pins the lane into the prompt so the LLM does not
110
+ // have to infer it. Empty string ⇒ full chain (unchanged default behavior).
111
+ async function buildSheldonActivationContext(targetDir, options) {
112
+ if (!(await detectLeanLaneFromConfig(targetDir))) return '';
113
+ const slug = String(options.feature || options.slug || '').trim() || '{slug}';
114
+ return [
115
+ 'Active lane: LEAN (workflow.config.json routes @sheldon -> @dev).',
116
+ `Run RF-LEAN as the single spec authority — in this one pass produce requirements-${slug}.md, spec-${slug}.md (gates A/B/C marked approved), design-doc-${slug}.md, readiness-${slug}.md, implementation-plan-${slug}.md (status: approved), and the §2c RG-* harness-contract for a runtime feature.`,
117
+ 'Hand off to @dev when done. Do NOT route to @analyst/@architect/@discovery-design-doc/@pm — they are collapsed into you in this lane.'
118
+ ].join('\n');
119
+ }
68
120
 
69
121
  function normalizePentesterTargetMode(input) {
70
122
  const mode = String(input || '').trim().toLowerCase();
@@ -207,12 +259,16 @@ async function runAgentPrompt({ args, options, logger, t }) {
207
259
 
208
260
  if (!prompt) {
209
261
  instructionPath = await resolveExistingInstructionPath(targetDir, promptAgent, locale);
210
- if (promptAgent.id === 'pentester') {
211
- pentesterTargetMode = normalizePentesterTargetMode(options.mode);
212
- activationContext = buildPentesterActivationContext(options, t);
213
- } else if (promptAgent.id === 'scope-check') {
214
- activationContext = buildScopeCheckActivationContext(options);
215
- }
262
+ if (promptAgent.id === 'pentester') {
263
+ pentesterTargetMode = normalizePentesterTargetMode(options.mode);
264
+ activationContext = buildPentesterActivationContext(options, t);
265
+ } else if (promptAgent.id === 'scope-check') {
266
+ activationContext = buildScopeCheckActivationContext(options);
267
+ } else if (promptAgent.id === 'tester') {
268
+ activationContext = buildTesterActivationContext(options);
269
+ } else if (promptAgent.id === 'sheldon') {
270
+ activationContext = await buildSheldonActivationContext(targetDir, options);
271
+ }
216
272
  const autonomyProtocol = await readAutonomyProtocol(targetDir);
217
273
  const manifest = await readAgentManifest(targetDir, promptAgent.id);
218
274
  effectiveMode = resolveEffectiveMode({
@@ -220,7 +276,7 @@ async function runAgentPrompt({ args, options, logger, t }) {
220
276
  tool,
221
277
  agentId: promptAgent.id,
222
278
  manifest,
223
- requestedMode: promptAgent.id === 'scope-check' && getScopeCheckModeOption(options) ? null : options.mode || null
279
+ requestedMode: promptAgent.id === 'scope-check' && getScopeCheckModeOption(options) ? null : options.mode || null
224
280
  });
225
281
  prompt = buildAgentPrompt(promptAgent, tool, {
226
282
  instructionPath,