@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
|
@@ -27,6 +27,7 @@ const { readHandoff, readHandoffProtocol } = require('../session-handoff');
|
|
|
27
27
|
const {
|
|
28
28
|
STATE_RELATIVE_PATH,
|
|
29
29
|
buildDefaultWorkflowConfig,
|
|
30
|
+
detectWorkflowMode,
|
|
30
31
|
readWorkflowConfig,
|
|
31
32
|
runWorkflowNext
|
|
32
33
|
} = require('./workflow-next');
|
|
@@ -37,17 +38,17 @@ const {
|
|
|
37
38
|
extractStatusWritePathItems
|
|
38
39
|
} = require('../parallel-workspace');
|
|
39
40
|
|
|
40
|
-
const BAR = '━'.repeat(45);
|
|
41
|
-
const EXECUTION_STATE_RELATIVE_PATH = '.aioson/context/workflow-execute.json';
|
|
42
|
-
const DEFAULT_AGENTIC_MAX_CYCLES = 3;
|
|
41
|
+
const BAR = '━'.repeat(45);
|
|
42
|
+
const EXECUTION_STATE_RELATIVE_PATH = '.aioson/context/workflow-execute.json';
|
|
43
|
+
const DEFAULT_AGENTIC_MAX_CYCLES = 3;
|
|
43
44
|
|
|
44
45
|
const STEP_META = {
|
|
45
46
|
setup: { description: 'Initialize project context', gate_before: null, gate_after: null },
|
|
46
47
|
product: { description: 'Generate PRD', gate_before: null, gate_after: null },
|
|
47
|
-
analyst: { description: 'Map requirements + spec', gate_before: null, gate_after: 'A' },
|
|
48
|
-
architect: { description: 'Architecture design', gate_before: 'A', gate_after: 'B' },
|
|
49
|
-
'discovery-design-doc': { description: 'Prepare design-doc and readiness contract', gate_before: 'B', gate_after: null },
|
|
50
|
-
'ux-ui': { description: 'UI/UX design', gate_before: 'A', gate_after: 'B', optional: true },
|
|
48
|
+
analyst: { description: 'Map requirements + spec', gate_before: null, gate_after: 'A' },
|
|
49
|
+
architect: { description: 'Architecture design', gate_before: 'A', gate_after: 'B' },
|
|
50
|
+
'discovery-design-doc': { description: 'Prepare design-doc and readiness contract', gate_before: 'B', gate_after: null },
|
|
51
|
+
'ux-ui': { description: 'UI/UX design', gate_before: 'A', gate_after: 'B', optional: true },
|
|
51
52
|
pm: { description: 'Backlog + PM plan', gate_before: 'B', gate_after: 'C' },
|
|
52
53
|
orchestrator: { description: 'Coordinate execution lanes', gate_before: 'C', gate_after: null },
|
|
53
54
|
dev: { description: 'Implementation', gate_before: null, gate_after: 'C' },
|
|
@@ -89,105 +90,120 @@ function normalizeClassification(value, fallback = 'SMALL') {
|
|
|
89
90
|
return ['MICRO', 'SMALL', 'MEDIUM'].includes(safe) ? safe : fallback;
|
|
90
91
|
}
|
|
91
92
|
|
|
92
|
-
function normalizeAgentName(value) {
|
|
93
|
-
return String(value || '').trim().toLowerCase().replace(/^@/, '');
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function quoteCliArg(value) {
|
|
97
|
-
return `'${String(value || '').replace(/'/g, "'\\''")}'`;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function parsePositiveIntegerOption(value, fallback, min = 1, max = 10) {
|
|
101
|
-
const parsed = Number.parseInt(String(value || ''), 10);
|
|
102
|
-
if (!Number.isInteger(parsed)) return fallback;
|
|
103
|
-
if (parsed < min) return min;
|
|
104
|
-
if (parsed > max) return max;
|
|
105
|
-
return parsed;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function isAgenticRequested(options = {}) {
|
|
109
|
-
return Boolean(
|
|
110
|
-
options.agentic ||
|
|
111
|
-
options['agentic-run'] ||
|
|
112
|
-
options.autopilot === 'agentic' ||
|
|
113
|
-
options.autopilot === 'runtime'
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
return
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
93
|
+
function normalizeAgentName(value) {
|
|
94
|
+
return String(value || '').trim().toLowerCase().replace(/^@/, '');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function quoteCliArg(value) {
|
|
98
|
+
return `'${String(value || '').replace(/'/g, "'\\''")}'`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function parsePositiveIntegerOption(value, fallback, min = 1, max = 10) {
|
|
102
|
+
const parsed = Number.parseInt(String(value || ''), 10);
|
|
103
|
+
if (!Number.isInteger(parsed)) return fallback;
|
|
104
|
+
if (parsed < min) return min;
|
|
105
|
+
if (parsed > max) return max;
|
|
106
|
+
return parsed;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function isAgenticRequested(options = {}) {
|
|
110
|
+
return Boolean(
|
|
111
|
+
options.agentic ||
|
|
112
|
+
options['agentic-run'] ||
|
|
113
|
+
options.autopilot === 'agentic' ||
|
|
114
|
+
options.autopilot === 'runtime' ||
|
|
115
|
+
// Seeding the scheme IS turning on autopilot — the whole point of --seed is to
|
|
116
|
+
// persist an enabled agentic_policy the interactive agents then follow.
|
|
117
|
+
options.seed ||
|
|
118
|
+
options['seed-only']
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function buildAgenticPolicy(options = {}, classification = 'SMALL') {
|
|
123
|
+
// --seed --step: the per-feature "step by step" choice. Writes an explicitly
|
|
124
|
+
// DISARMED scheme — resolveAutopilotSignal and the agent triggers treat it as
|
|
125
|
+
// the feature-level override that wins over a project-wide auto_handoff: true.
|
|
126
|
+
if (options.step) {
|
|
127
|
+
return {
|
|
128
|
+
enabled: false,
|
|
129
|
+
mode: 'step_by_step',
|
|
130
|
+
source: 'workflow:execute --step'
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const enabled = isAgenticRequested(options);
|
|
135
|
+
if (!enabled) return null;
|
|
136
|
+
|
|
137
|
+
const maxDevQaCycles = parsePositiveIntegerOption(
|
|
138
|
+
options['max-dev-qa-cycles'] || options.maxDevQaCycles || options['max-cycles'],
|
|
139
|
+
DEFAULT_AGENTIC_MAX_CYCLES
|
|
140
|
+
);
|
|
141
|
+
const maxTesterCycles = parsePositiveIntegerOption(
|
|
142
|
+
options['max-tester-cycles'] || options.maxTesterCycles || options['max-specialist-cycles'],
|
|
143
|
+
DEFAULT_AGENTIC_MAX_CYCLES
|
|
144
|
+
);
|
|
145
|
+
const maxPentesterCycles = parsePositiveIntegerOption(
|
|
146
|
+
options['max-pentester-cycles'] || options.maxPentesterCycles || options['max-specialist-cycles'],
|
|
147
|
+
DEFAULT_AGENTIC_MAX_CYCLES
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
enabled: true,
|
|
152
|
+
mode: 'runtime_policy',
|
|
153
|
+
source: 'workflow:execute',
|
|
154
|
+
stop_conditions: [
|
|
155
|
+
'feature_status_done',
|
|
156
|
+
'human_decision_required',
|
|
157
|
+
'gate_blocked',
|
|
158
|
+
'context_budget_exceeded',
|
|
159
|
+
'cycle_limit_reached',
|
|
160
|
+
'critical_security_human_gate',
|
|
161
|
+
'feature_close_human_gate'
|
|
162
|
+
],
|
|
163
|
+
review_cycle: {
|
|
164
|
+
hub: 'qa',
|
|
165
|
+
max_dev_qa_cycles: maxDevQaCycles,
|
|
166
|
+
max_tester_correction_cycles: maxTesterCycles,
|
|
167
|
+
max_pentester_correction_cycles: maxPentesterCycles,
|
|
168
|
+
qa_fail_route: 'dev',
|
|
169
|
+
tester_route: 'tester after qa coverage trigger',
|
|
170
|
+
pentester_route: 'pentester after sensitive-surface trigger or MEDIUM sequence stage',
|
|
171
|
+
validator_route: 'validator when harness contract is present',
|
|
172
|
+
feature_close: 'human_gate'
|
|
173
|
+
},
|
|
174
|
+
lanes: {
|
|
175
|
+
enabled: classification === 'MEDIUM',
|
|
176
|
+
strategy: 'parallelize_only_independent_write_scopes',
|
|
177
|
+
guard_command: 'aioson parallel:guard . --lane=<n>',
|
|
178
|
+
conflict_action: 'block_lane'
|
|
179
|
+
},
|
|
180
|
+
sidecars: {
|
|
181
|
+
scouts: {
|
|
182
|
+
enabled: true,
|
|
183
|
+
read_only: true,
|
|
184
|
+
max_per_session: 3,
|
|
185
|
+
max_files_in_scope: 20,
|
|
186
|
+
allowed_parent_agents: ['deyvin', 'dev', 'product', 'briefing', 'orache']
|
|
187
|
+
},
|
|
188
|
+
research: {
|
|
189
|
+
enabled: true,
|
|
190
|
+
cache_dir: 'researchs/',
|
|
191
|
+
cache_ttl_days: 7
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function formatAgenticPolicyLines(policy) {
|
|
198
|
+
if (!policy || !policy.enabled) return [];
|
|
199
|
+
return [
|
|
200
|
+
`Agentic policy: enabled (dev<->qa max ${policy.review_cycle.max_dev_qa_cycles} cycles)`,
|
|
201
|
+
`Review loop: qa fail -> dev; tester max ${policy.review_cycle.max_tester_correction_cycles}; pentester max ${policy.review_cycle.max_pentester_correction_cycles}; close=${policy.review_cycle.feature_close}`,
|
|
202
|
+
`Parallel lanes: ${policy.lanes.enabled ? 'enabled for independent write scopes' : 'disabled for this classification'}`
|
|
203
|
+
];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function findNextFromSequence(sequence, completed, skipped = []) {
|
|
191
207
|
const done = new Set([...(completed || []), ...(skipped || [])].map(normalizeAgentName));
|
|
192
208
|
return sequence.find((stage) => !done.has(normalizeAgentName(stage))) || null;
|
|
193
209
|
}
|
|
@@ -229,6 +245,18 @@ function inferCompletedStagesFromArtifacts(sequence, artifacts, gates) {
|
|
|
229
245
|
inferred = Boolean(artifacts.requirements && artifacts.requirements.exists && gates.requirements === 'approved');
|
|
230
246
|
} else if (normalized === 'architect') {
|
|
231
247
|
inferred = Boolean(artifacts.architecture && artifacts.architecture.exists && gates.design === 'approved');
|
|
248
|
+
} else if (normalized === 'sheldon' || normalized === 'orchestrator') {
|
|
249
|
+
// Single spec authorities (lean @sheldon / maestro @orchestrator) collapse
|
|
250
|
+
// Gates A/B/C into one hop — done once the spec package exists with the
|
|
251
|
+
// collapsed gates approved. Without this, a seed run AFTER the spec stage
|
|
252
|
+
// finished reported `next: sheldon|orchestrator` and pointed the whole
|
|
253
|
+
// agentic scheme backwards.
|
|
254
|
+
inferred = Boolean(
|
|
255
|
+
artifacts.spec && artifacts.spec.exists &&
|
|
256
|
+
gates.requirements === 'approved' &&
|
|
257
|
+
gates.design === 'approved' &&
|
|
258
|
+
gates.plan === 'approved'
|
|
259
|
+
);
|
|
232
260
|
} else if (normalized === 'pm') {
|
|
233
261
|
inferred = Boolean(artifacts.implementation_plan && artifacts.implementation_plan.exists && gates.plan === 'approved');
|
|
234
262
|
} else if (normalized === 'qa') {
|
|
@@ -244,9 +272,17 @@ function inferCompletedStagesFromArtifacts(sequence, artifacts, gates) {
|
|
|
244
272
|
return completed;
|
|
245
273
|
}
|
|
246
274
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
275
|
+
/**
|
|
276
|
+
* Reads the persisted workflow state for a seed/preview of `slug`, applying the
|
|
277
|
+
* SAME staleness guard as loadOrCreateState: a state whose feature is no longer
|
|
278
|
+
* the features.md-active feature is stale and gets discarded (the loader would
|
|
279
|
+
* throw it away one command later anyway — hard-failing the seed on it just
|
|
280
|
+
* silently disarmed autopilot). The refusal survives only for a genuinely
|
|
281
|
+
* active different feature.
|
|
282
|
+
*/
|
|
283
|
+
async function resolveExistingFeatureState(targetDir, slug) {
|
|
284
|
+
const statePath = path.join(targetDir, STATE_RELATIVE_PATH);
|
|
285
|
+
let existing = await readJsonIfExists(statePath);
|
|
250
286
|
const focusStage = getFocusStage(existing);
|
|
251
287
|
|
|
252
288
|
if (
|
|
@@ -256,14 +292,41 @@ async function seedFeatureWorkflowState(targetDir, slug, classification, startFr
|
|
|
256
292
|
existing.featureSlug !== slug &&
|
|
257
293
|
focusStage
|
|
258
294
|
) {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
}
|
|
295
|
+
let modeInfo = null;
|
|
296
|
+
try {
|
|
297
|
+
modeInfo = await detectWorkflowMode(targetDir);
|
|
298
|
+
} catch {
|
|
299
|
+
modeInfo = null;
|
|
300
|
+
}
|
|
301
|
+
const existingIsActive = Boolean(
|
|
302
|
+
modeInfo && modeInfo.mode === 'feature' && modeInfo.featureSlug === existing.featureSlug
|
|
303
|
+
);
|
|
304
|
+
const requestedIsActive = Boolean(
|
|
305
|
+
modeInfo && modeInfo.mode === 'feature' && modeInfo.featureSlug === slug
|
|
306
|
+
);
|
|
307
|
+
if (!existingIsActive || requestedIsActive) {
|
|
308
|
+
existing = null; // stale pointer — reseed for the requested feature
|
|
309
|
+
} else {
|
|
310
|
+
return {
|
|
311
|
+
existing: null,
|
|
312
|
+
refusal: {
|
|
313
|
+
ok: false,
|
|
314
|
+
reason: 'different_active_feature',
|
|
315
|
+
active_feature: existing.featureSlug,
|
|
316
|
+
active_stage: focusStage
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
}
|
|
265
320
|
}
|
|
266
321
|
|
|
322
|
+
return { existing, refusal: null };
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
async function seedFeatureWorkflowState(targetDir, slug, classification, startFrom) {
|
|
326
|
+
const statePath = path.join(targetDir, STATE_RELATIVE_PATH);
|
|
327
|
+
const { existing, refusal } = await resolveExistingFeatureState(targetDir, slug);
|
|
328
|
+
if (refusal) return refusal;
|
|
329
|
+
|
|
267
330
|
if (existing && existing.mode === 'feature' && existing.featureSlug === slug) {
|
|
268
331
|
return {
|
|
269
332
|
ok: true,
|
|
@@ -301,141 +364,126 @@ async function seedFeatureWorkflowState(targetDir, slug, classification, startFr
|
|
|
301
364
|
detour: null,
|
|
302
365
|
updatedAt: new Date().toISOString()
|
|
303
366
|
};
|
|
304
|
-
|
|
305
|
-
await writeJson(statePath, state);
|
|
306
|
-
return {
|
|
307
|
-
ok: true,
|
|
367
|
+
|
|
368
|
+
await writeJson(statePath, state);
|
|
369
|
+
return {
|
|
370
|
+
ok: true,
|
|
308
371
|
resumed: false,
|
|
309
372
|
state,
|
|
310
373
|
statePath: STATE_RELATIVE_PATH
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
async function previewFeatureWorkflowState(targetDir, slug, classification, startFrom) {
|
|
315
|
-
const existing = await
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
if (
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
featureSlug: slug,
|
|
425
|
-
tool,
|
|
426
|
-
state: workflowState,
|
|
427
|
-
stateCreated: false,
|
|
428
|
-
dryRun: true,
|
|
429
|
-
activeStage: workflowState.current || workflowState.next || null,
|
|
430
|
-
queuedNextStage: workflowState.next || null,
|
|
431
|
-
pendingGates: [],
|
|
432
|
-
contractCheck: null,
|
|
433
|
-
artifacts: buildDryRunArtifactsSummary(planData.artifacts),
|
|
434
|
-
suggestion
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
async function buildExecutionPlan(targetDir, slug, classification, workflowState = null, startFrom = null) {
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
async function previewFeatureWorkflowState(targetDir, slug, classification, startFrom) {
|
|
378
|
+
const { existing, refusal } = await resolveExistingFeatureState(targetDir, slug);
|
|
379
|
+
if (refusal) return refusal;
|
|
380
|
+
|
|
381
|
+
if (existing && existing.mode === 'feature' && existing.featureSlug === slug) {
|
|
382
|
+
return {
|
|
383
|
+
ok: true,
|
|
384
|
+
resumed: true,
|
|
385
|
+
state: existing,
|
|
386
|
+
statePath: STATE_RELATIVE_PATH
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const artifacts = await scanArtifacts(targetDir, slug);
|
|
391
|
+
const gates = await readPhaseGates(targetDir, slug);
|
|
392
|
+
const sequence = await resolveFeatureSequence(targetDir, classification);
|
|
393
|
+
const completed = inferCompletedStagesFromArtifacts(sequence, artifacts, gates);
|
|
394
|
+
const normalizedStartFrom = startFrom ? normalizeAgentName(startFrom) : null;
|
|
395
|
+
|
|
396
|
+
let skipped = [];
|
|
397
|
+
let next = findNextFromSequence(sequence, completed, skipped);
|
|
398
|
+
|
|
399
|
+
if (normalizedStartFrom && sequence.includes(normalizedStartFrom)) {
|
|
400
|
+
const targetIndex = sequence.indexOf(normalizedStartFrom);
|
|
401
|
+
skipped = sequence.slice(0, targetIndex).filter((stage) => !completed.includes(stage));
|
|
402
|
+
next = normalizedStartFrom;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return {
|
|
406
|
+
ok: true,
|
|
407
|
+
resumed: false,
|
|
408
|
+
state: {
|
|
409
|
+
version: 1,
|
|
410
|
+
mode: 'feature',
|
|
411
|
+
classification,
|
|
412
|
+
sequence,
|
|
413
|
+
current: null,
|
|
414
|
+
next,
|
|
415
|
+
completed,
|
|
416
|
+
skipped,
|
|
417
|
+
featureSlug: slug,
|
|
418
|
+
detour: null,
|
|
419
|
+
updatedAt: new Date().toISOString()
|
|
420
|
+
},
|
|
421
|
+
statePath: STATE_RELATIVE_PATH
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function buildDryRunSuggestion(planData) {
|
|
426
|
+
const active = planData.steps.find((step) => step.status === 'active');
|
|
427
|
+
if (active) {
|
|
428
|
+
return {
|
|
429
|
+
action: active.predicted_blockers.length > 0 ? 'complete_stage' : 'continue_stage',
|
|
430
|
+
agent: active.agent,
|
|
431
|
+
command: `aioson workflow:next . --complete=${active.agent}`,
|
|
432
|
+
reason: `Preview stage @${active.agent}.`
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const next = planData.steps.find((step) => step.status !== 'completed' && step.status !== 'skipped');
|
|
437
|
+
if (!next) {
|
|
438
|
+
return {
|
|
439
|
+
action: 'workflow_complete',
|
|
440
|
+
agent: null,
|
|
441
|
+
command: null,
|
|
442
|
+
reason: 'Preview workflow has no pending stage.'
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return {
|
|
447
|
+
action: next.status === 'blocked' ? 'blocked' : 'activate_stage',
|
|
448
|
+
agent: next.agent,
|
|
449
|
+
command: `aioson workflow:next . --agent=${next.agent}`,
|
|
450
|
+
reason: next.status === 'blocked'
|
|
451
|
+
? `Preview stage @${next.agent} is blocked.`
|
|
452
|
+
: `Preview next official stage is @${next.agent}.`
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function buildDryRunArtifactsSummary(artifacts) {
|
|
457
|
+
return Object.entries(artifacts || {}).map(([id, artifact]) => ({
|
|
458
|
+
id,
|
|
459
|
+
label: artifact && artifact.path ? path.basename(artifact.path) : id,
|
|
460
|
+
file: artifact && artifact.path ? artifact.path : null,
|
|
461
|
+
exists: Boolean(artifact && artifact.exists)
|
|
462
|
+
}));
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function buildDryRunStatusSnapshot({ targetDir, slug, classification, workflowState, planData, tool }) {
|
|
466
|
+
const suggestion = buildDryRunSuggestion(planData);
|
|
467
|
+
return {
|
|
468
|
+
ok: true,
|
|
469
|
+
projectName: path.basename(targetDir),
|
|
470
|
+
classification,
|
|
471
|
+
mode: 'feature',
|
|
472
|
+
featureSlug: slug,
|
|
473
|
+
tool,
|
|
474
|
+
state: workflowState,
|
|
475
|
+
stateCreated: false,
|
|
476
|
+
dryRun: true,
|
|
477
|
+
activeStage: workflowState.current || workflowState.next || null,
|
|
478
|
+
queuedNextStage: workflowState.next || null,
|
|
479
|
+
pendingGates: [],
|
|
480
|
+
contractCheck: null,
|
|
481
|
+
artifacts: buildDryRunArtifactsSummary(planData.artifacts),
|
|
482
|
+
suggestion
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
async function buildExecutionPlan(targetDir, slug, classification, workflowState = null, startFrom = null) {
|
|
439
487
|
const sequence = workflowState && Array.isArray(workflowState.sequence) && workflowState.sequence.length > 0
|
|
440
488
|
? [...workflowState.sequence]
|
|
441
489
|
: await resolveFeatureSequence(targetDir, classification);
|
|
@@ -472,11 +520,11 @@ async function buildExecutionPlan(targetDir, slug, classification, workflowState
|
|
|
472
520
|
else if (focusStage === agent) status = 'active';
|
|
473
521
|
|
|
474
522
|
const predictedBlockers = [];
|
|
475
|
-
if (status !== 'completed' && status !== 'skipped' && meta.gate_before && !isGateApproved(gates, meta.gate_before)) {
|
|
476
|
-
const responsible = GATE_RESPONSIBLE_AGENT[meta.gate_before] || 'previous agent';
|
|
477
|
-
const featureArg = slug ? ` --feature=${quoteCliArg(slug)}` : '';
|
|
478
|
-
predictedBlockers.push(
|
|
479
|
-
`Gate ${meta.gate_before} (${GATE_NAMES[meta.gate_before] || meta.gate_before}) not approved — ` +
|
|
523
|
+
if (status !== 'completed' && status !== 'skipped' && meta.gate_before && !isGateApproved(gates, meta.gate_before)) {
|
|
524
|
+
const responsible = GATE_RESPONSIBLE_AGENT[meta.gate_before] || 'previous agent';
|
|
525
|
+
const featureArg = slug ? ` --feature=${quoteCliArg(slug)}` : '';
|
|
526
|
+
predictedBlockers.push(
|
|
527
|
+
`Gate ${meta.gate_before} (${GATE_NAMES[meta.gate_before] || meta.gate_before}) not approved — ` +
|
|
480
528
|
`responsible: ${responsible} — ` +
|
|
481
529
|
`approve with: aioson gate:approve .${featureArg} --gate=${meta.gate_before}`
|
|
482
530
|
);
|
|
@@ -539,7 +587,11 @@ function buildCheckpointPayload(activation, handoff, handoffProtocol) {
|
|
|
539
587
|
async function writeExecutionCheckpoint(targetDir, payload) {
|
|
540
588
|
const execPath = path.join(targetDir, EXECUTION_STATE_RELATIVE_PATH);
|
|
541
589
|
const existing = await readJsonIfExists(execPath);
|
|
542
|
-
|
|
590
|
+
// History is per-feature: never carry a previous feature's checkpoints into
|
|
591
|
+
// a new feature's scheme.
|
|
592
|
+
const history = existing && existing.feature === payload.feature && Array.isArray(existing.history)
|
|
593
|
+
? [...existing.history]
|
|
594
|
+
: [];
|
|
543
595
|
if (payload.checkpoint) {
|
|
544
596
|
history.push({
|
|
545
597
|
at: new Date().toISOString(),
|
|
@@ -565,10 +617,10 @@ async function writeExecutionCheckpoint(targetDir, payload) {
|
|
|
565
617
|
checkpoint: payload.checkpoint || null,
|
|
566
618
|
status_snapshot: payload.statusSnapshot || null,
|
|
567
619
|
suggestion: payload.suggestion || null,
|
|
568
|
-
resume_command: payload.resumeCommand || null,
|
|
569
|
-
agentic_policy: payload.agenticPolicy || null,
|
|
570
|
-
history
|
|
571
|
-
};
|
|
620
|
+
resume_command: payload.resumeCommand || null,
|
|
621
|
+
agentic_policy: payload.agenticPolicy || null,
|
|
622
|
+
history
|
|
623
|
+
};
|
|
572
624
|
await writeJson(execPath, nextPayload);
|
|
573
625
|
return nextPayload;
|
|
574
626
|
}
|
|
@@ -702,6 +754,9 @@ async function runWorkflowExecute({ args, options = {}, logger }) {
|
|
|
702
754
|
const tool = options.tool ? String(options.tool).trim() : 'claude';
|
|
703
755
|
const requestedMode = options.mode ? String(options.mode).trim() : null;
|
|
704
756
|
const dryRun = Boolean(options['dry-run'] || options.dry);
|
|
757
|
+
// --step (disarm) is by definition record-only: it writes the disarmed scheme
|
|
758
|
+
// and never drives stage transitions, with or without an explicit --seed.
|
|
759
|
+
const seedOnly = Boolean(options.seed || options['seed-only'] || options.step);
|
|
705
760
|
const startFrom = options['start-from'] ? String(options['start-from']).trim() : null;
|
|
706
761
|
const skipOptional = Boolean(options['skip-optional']);
|
|
707
762
|
const parsedMaxCheckpoints = Number.parseInt(String(options['max-checkpoints'] || '1'), 10);
|
|
@@ -718,11 +773,11 @@ async function runWorkflowExecute({ args, options = {}, logger }) {
|
|
|
718
773
|
return { ok: false };
|
|
719
774
|
}
|
|
720
775
|
|
|
721
|
-
let classification = options.classification ? String(options.classification).toUpperCase() : null;
|
|
722
|
-
if (!classification) classification = await detectClassification(targetDir, slug);
|
|
723
|
-
if (!classification) classification = 'SMALL';
|
|
724
|
-
classification = normalizeClassification(classification, 'SMALL');
|
|
725
|
-
const agenticPolicy = buildAgenticPolicy(options, classification);
|
|
776
|
+
let classification = options.classification ? String(options.classification).toUpperCase() : null;
|
|
777
|
+
if (!classification) classification = await detectClassification(targetDir, slug);
|
|
778
|
+
if (!classification) classification = 'SMALL';
|
|
779
|
+
classification = normalizeClassification(classification, 'SMALL');
|
|
780
|
+
const agenticPolicy = buildAgenticPolicy(options, classification);
|
|
726
781
|
|
|
727
782
|
const autonomyProtocol = await readAutonomyProtocol(targetDir);
|
|
728
783
|
const toolPolicy = getToolPolicy(autonomyProtocol, tool);
|
|
@@ -756,9 +811,9 @@ async function runWorkflowExecute({ args, options = {}, logger }) {
|
|
|
756
811
|
}
|
|
757
812
|
}
|
|
758
813
|
|
|
759
|
-
const seeded = dryRun
|
|
760
|
-
? await previewFeatureWorkflowState(targetDir, slug, classification, startFrom)
|
|
761
|
-
: await seedFeatureWorkflowState(targetDir, slug, classification, startFrom);
|
|
814
|
+
const seeded = dryRun
|
|
815
|
+
? await previewFeatureWorkflowState(targetDir, slug, classification, startFrom)
|
|
816
|
+
: await seedFeatureWorkflowState(targetDir, slug, classification, startFrom);
|
|
762
817
|
if (!seeded.ok) {
|
|
763
818
|
if (options.json) return seeded;
|
|
764
819
|
logger.error(
|
|
@@ -781,28 +836,32 @@ async function runWorkflowExecute({ args, options = {}, logger }) {
|
|
|
781
836
|
|
|
782
837
|
const activePlan = planData.steps.filter((step) => step.status !== 'completed' && !(skipOptional && step.optional));
|
|
783
838
|
const blockedSteps = planData.steps.filter((step) => step.predicted_blockers.length > 0);
|
|
784
|
-
const statusSnapshot = dryRun
|
|
785
|
-
? buildDryRunStatusSnapshot({ targetDir, slug, classification, workflowState: seeded.state, planData, tool })
|
|
786
|
-
: await readStatusSnapshot(targetDir, tool, t);
|
|
787
|
-
const resumeCommand = [
|
|
788
|
-
'aioson',
|
|
789
|
-
'workflow:execute',
|
|
790
|
-
quoteCliArg(targetDir),
|
|
791
|
-
`--feature=${quoteCliArg(slug)}`,
|
|
792
|
-
`--tool=${quoteCliArg(tool)}`,
|
|
793
|
-
...(requestedMode ? [`--mode=${quoteCliArg(requestedMode)}`] : []),
|
|
794
|
-
...(maxCheckpoints !== 1 ? [`--max-checkpoints=${quoteCliArg(maxCheckpoints)}`] : []),
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
...(
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
839
|
+
const statusSnapshot = dryRun
|
|
840
|
+
? buildDryRunStatusSnapshot({ targetDir, slug, classification, workflowState: seeded.state, planData, tool })
|
|
841
|
+
: await readStatusSnapshot(targetDir, tool, t);
|
|
842
|
+
const resumeCommand = [
|
|
843
|
+
'aioson',
|
|
844
|
+
'workflow:execute',
|
|
845
|
+
quoteCliArg(targetDir),
|
|
846
|
+
`--feature=${quoteCliArg(slug)}`,
|
|
847
|
+
`--tool=${quoteCliArg(tool)}`,
|
|
848
|
+
...(requestedMode ? [`--mode=${quoteCliArg(requestedMode)}`] : []),
|
|
849
|
+
...(maxCheckpoints !== 1 ? [`--max-checkpoints=${quoteCliArg(maxCheckpoints)}`] : []),
|
|
850
|
+
// A seed run must resume as a seed run — `--agentic` (the CLI-advancing
|
|
851
|
+
// runner) is the opposite of the seed-only contract. A disarm run keeps
|
|
852
|
+
// its --step so replaying it never re-arms the scheme.
|
|
853
|
+
...(seedOnly ? ['--seed'] : agenticPolicy && agenticPolicy.enabled ? ['--agentic'] : []),
|
|
854
|
+
...(options.step ? ['--step'] : []),
|
|
855
|
+
...(agenticPolicy && agenticPolicy.review_cycle && agenticPolicy.review_cycle.max_dev_qa_cycles !== DEFAULT_AGENTIC_MAX_CYCLES
|
|
856
|
+
? [`--max-dev-qa-cycles=${quoteCliArg(agenticPolicy.review_cycle.max_dev_qa_cycles)}`]
|
|
857
|
+
: []),
|
|
858
|
+
...(agenticPolicy && agenticPolicy.review_cycle && agenticPolicy.review_cycle.max_tester_correction_cycles !== DEFAULT_AGENTIC_MAX_CYCLES
|
|
859
|
+
? [`--max-tester-cycles=${quoteCliArg(agenticPolicy.review_cycle.max_tester_correction_cycles)}`]
|
|
860
|
+
: []),
|
|
861
|
+
...(agenticPolicy && agenticPolicy.review_cycle && agenticPolicy.review_cycle.max_pentester_correction_cycles !== DEFAULT_AGENTIC_MAX_CYCLES
|
|
862
|
+
? [`--max-pentester-cycles=${quoteCliArg(agenticPolicy.review_cycle.max_pentester_correction_cycles)}`]
|
|
863
|
+
: [])
|
|
864
|
+
].join(' ');
|
|
806
865
|
|
|
807
866
|
if (dryRun) {
|
|
808
867
|
const result = {
|
|
@@ -821,11 +880,11 @@ async function runWorkflowExecute({ args, options = {}, logger }) {
|
|
|
821
880
|
blocked_steps: blockedSteps.length,
|
|
822
881
|
gates: planData.gates,
|
|
823
882
|
status_snapshot: statusSnapshot,
|
|
824
|
-
suggestion: statusSnapshot && statusSnapshot.suggestion ? statusSnapshot.suggestion : null,
|
|
825
|
-
resume_command: resumeCommand,
|
|
826
|
-
agentic_policy: agenticPolicy,
|
|
827
|
-
parallel_guard: parallelGuard
|
|
828
|
-
};
|
|
883
|
+
suggestion: statusSnapshot && statusSnapshot.suggestion ? statusSnapshot.suggestion : null,
|
|
884
|
+
resume_command: resumeCommand,
|
|
885
|
+
agentic_policy: agenticPolicy,
|
|
886
|
+
parallel_guard: parallelGuard
|
|
887
|
+
};
|
|
829
888
|
|
|
830
889
|
if (options.json) return result;
|
|
831
890
|
|
|
@@ -844,14 +903,73 @@ async function runWorkflowExecute({ args, options = {}, logger }) {
|
|
|
844
903
|
logger.log('');
|
|
845
904
|
logger.log(`Blocked steps: ${blockedSteps.length} | Remaining: ${activePlan.length}`);
|
|
846
905
|
logger.log(`Resume state: ${seeded.resumed ? 'existing workflow state reused' : 'new workflow state seeded'}`);
|
|
847
|
-
if (statusSnapshot && statusSnapshot.suggestion && statusSnapshot.suggestion.command) {
|
|
848
|
-
logger.log(`Suggested command: ${statusSnapshot.suggestion.command}`);
|
|
849
|
-
}
|
|
850
|
-
for (const line of formatAgenticPolicyLines(agenticPolicy)) {
|
|
851
|
-
logger.log(line);
|
|
852
|
-
}
|
|
853
|
-
logger.log('');
|
|
854
|
-
return result;
|
|
906
|
+
if (statusSnapshot && statusSnapshot.suggestion && statusSnapshot.suggestion.command) {
|
|
907
|
+
logger.log(`Suggested command: ${statusSnapshot.suggestion.command}`);
|
|
908
|
+
}
|
|
909
|
+
for (const line of formatAgenticPolicyLines(agenticPolicy)) {
|
|
910
|
+
logger.log(line);
|
|
911
|
+
}
|
|
912
|
+
logger.log('');
|
|
913
|
+
return result;
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
// --seed: persist the workflow.state.json + workflow-execute.json (with an
|
|
917
|
+
// enabled agentic_policy) and STOP. Unlike a full run, it does not drive stage
|
|
918
|
+
// transitions from the CLI — in an interactive Claude Code session the agents
|
|
919
|
+
// themselves follow the scheme. This is what the spec->dev handoff calls so a
|
|
920
|
+
// feature built the normal way carries the autopilot contract without the user
|
|
921
|
+
// running anything. Idempotent: re-seeding the same slug reuses existing state.
|
|
922
|
+
if (seedOnly) {
|
|
923
|
+
const handoff = await readHandoff(targetDir);
|
|
924
|
+
const handoffProtocol = await readHandoffProtocol(targetDir);
|
|
925
|
+
const nextStage = seeded.state ? (seeded.state.current || seeded.state.next || null) : null;
|
|
926
|
+
const executionState = await writeExecutionCheckpoint(targetDir, {
|
|
927
|
+
feature: slug,
|
|
928
|
+
classification,
|
|
929
|
+
tool,
|
|
930
|
+
requestedMode,
|
|
931
|
+
resumed: seeded.resumed,
|
|
932
|
+
status: nextStage ? 'active' : 'completed',
|
|
933
|
+
checkpoint: buildCheckpointPayload(null, handoff, handoffProtocol),
|
|
934
|
+
statusSnapshot,
|
|
935
|
+
suggestion: statusSnapshot && statusSnapshot.suggestion ? statusSnapshot.suggestion : null,
|
|
936
|
+
resumeCommand,
|
|
937
|
+
agenticPolicy
|
|
938
|
+
});
|
|
939
|
+
|
|
940
|
+
const result = {
|
|
941
|
+
ok: true,
|
|
942
|
+
feature: slug,
|
|
943
|
+
classification,
|
|
944
|
+
tool,
|
|
945
|
+
requested_mode: requestedMode,
|
|
946
|
+
seeded: true,
|
|
947
|
+
resumed: seeded.resumed,
|
|
948
|
+
state_path: seeded.statePath,
|
|
949
|
+
execution_state_path: EXECUTION_STATE_RELATIVE_PATH,
|
|
950
|
+
next_stage: nextStage,
|
|
951
|
+
checkpoint: executionState.checkpoint,
|
|
952
|
+
execution_state: executionState,
|
|
953
|
+
status_snapshot: statusSnapshot,
|
|
954
|
+
suggestion: statusSnapshot && statusSnapshot.suggestion ? statusSnapshot.suggestion : null,
|
|
955
|
+
resume_command: resumeCommand,
|
|
956
|
+
agentic_policy: agenticPolicy,
|
|
957
|
+
parallel_guard: parallelGuard
|
|
958
|
+
};
|
|
959
|
+
|
|
960
|
+
if (options.json) return result;
|
|
961
|
+
|
|
962
|
+
logger.log('');
|
|
963
|
+
logger.log(`Agentic workflow scheme seeded → ${EXECUTION_STATE_RELATIVE_PATH}`);
|
|
964
|
+
logger.log(`Feature: ${slug} (${classification}) Next stage: @${nextStage || 'none'}`);
|
|
965
|
+
logger.log(
|
|
966
|
+
`Autopilot: ${agenticPolicy && agenticPolicy.enabled
|
|
967
|
+
? 'enabled — interactive agents run the chain to feature:close (human gate)'
|
|
968
|
+
: 'disabled'}`
|
|
969
|
+
);
|
|
970
|
+
for (const line of formatAgenticPolicyLines(agenticPolicy)) logger.log(line);
|
|
971
|
+
logger.log('');
|
|
972
|
+
return result;
|
|
855
973
|
}
|
|
856
974
|
|
|
857
975
|
const executionTransitions = [];
|
|
@@ -931,11 +1049,11 @@ async function runWorkflowExecute({ args, options = {}, logger }) {
|
|
|
931
1049
|
resumed: seeded.resumed,
|
|
932
1050
|
status: activation && activation.agent ? 'active' : 'completed',
|
|
933
1051
|
checkpoint: buildCheckpointPayload(activation, handoff, handoffProtocol),
|
|
934
|
-
statusSnapshot: refreshedStatus,
|
|
935
|
-
suggestion: refreshedStatus && refreshedStatus.suggestion ? refreshedStatus.suggestion : null,
|
|
936
|
-
resumeCommand,
|
|
937
|
-
agenticPolicy
|
|
938
|
-
});
|
|
1052
|
+
statusSnapshot: refreshedStatus,
|
|
1053
|
+
suggestion: refreshedStatus && refreshedStatus.suggestion ? refreshedStatus.suggestion : null,
|
|
1054
|
+
resumeCommand,
|
|
1055
|
+
agenticPolicy
|
|
1056
|
+
});
|
|
939
1057
|
|
|
940
1058
|
const result = {
|
|
941
1059
|
ok: true,
|
|
@@ -950,10 +1068,10 @@ async function runWorkflowExecute({ args, options = {}, logger }) {
|
|
|
950
1068
|
checkpoint: executionState.checkpoint,
|
|
951
1069
|
execution_state: executionState,
|
|
952
1070
|
status_snapshot: refreshedStatus,
|
|
953
|
-
suggestion: refreshedStatus && refreshedStatus.suggestion ? refreshedStatus.suggestion : null,
|
|
954
|
-
resume_command: resumeCommand,
|
|
955
|
-
agentic_policy: agenticPolicy,
|
|
956
|
-
transitions: executionTransitions,
|
|
1071
|
+
suggestion: refreshedStatus && refreshedStatus.suggestion ? refreshedStatus.suggestion : null,
|
|
1072
|
+
resume_command: resumeCommand,
|
|
1073
|
+
agentic_policy: agenticPolicy,
|
|
1074
|
+
transitions: executionTransitions,
|
|
957
1075
|
active_stage: activation && activation.agent ? activation.agent : null,
|
|
958
1076
|
next_stage: activation && activation.next ? activation.next : null,
|
|
959
1077
|
handoff,
|
|
@@ -971,10 +1089,10 @@ async function runWorkflowExecute({ args, options = {}, logger }) {
|
|
|
971
1089
|
return result;
|
|
972
1090
|
}
|
|
973
1091
|
|
|
974
|
-
module.exports = {
|
|
975
|
-
EXECUTION_STATE_RELATIVE_PATH,
|
|
976
|
-
buildAgenticPolicy,
|
|
977
|
-
buildExecutionPlan,
|
|
978
|
-
seedFeatureWorkflowState,
|
|
979
|
-
runWorkflowExecute
|
|
980
|
-
};
|
|
1092
|
+
module.exports = {
|
|
1093
|
+
EXECUTION_STATE_RELATIVE_PATH,
|
|
1094
|
+
buildAgenticPolicy,
|
|
1095
|
+
buildExecutionPlan,
|
|
1096
|
+
seedFeatureWorkflowState,
|
|
1097
|
+
runWorkflowExecute
|
|
1098
|
+
};
|