@jaimevalasek/aioson 1.7.2 → 1.8.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 (362) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +153 -10
  3. package/docs/en/cli-reference.md +56 -1
  4. package/docs/en/i18n.md +18 -18
  5. package/docs/en/schemas/index.json +10 -0
  6. package/docs/en/schemas/parallel-assign.schema.json +9 -0
  7. package/docs/en/schemas/parallel-doctor.schema.json +36 -0
  8. package/docs/en/schemas/parallel-guard.schema.json +63 -0
  9. package/docs/en/schemas/parallel-merge.schema.json +84 -0
  10. package/docs/en/schemas/parallel-status.schema.json +91 -1
  11. package/docs/integrations/apps-publish-marketplace.md +94 -0
  12. package/docs/pt/README.md +9 -0
  13. package/docs/pt/agentes.md +324 -3
  14. package/docs/pt/clientes-ai.md +7 -3
  15. package/docs/pt/comandos-cli.md +160 -13
  16. package/docs/pt/compress-agents.md +304 -0
  17. package/docs/pt/design-docs-governance.md +59 -0
  18. package/docs/pt/feature-archive.md +191 -0
  19. package/docs/pt/genome-3.0-spec.md +115 -4
  20. package/docs/pt/genome-distribution.md +232 -0
  21. package/docs/pt/inicio-rapido.md +1 -0
  22. package/docs/pt/motor-hardening.md +492 -0
  23. package/docs/pt/runner-system.md +113 -0
  24. package/package.json +2 -1
  25. package/src/agent-manifests.js +66 -0
  26. package/src/agents.js +27 -7
  27. package/src/autonomy-policy.js +139 -0
  28. package/src/brain-query.js +161 -0
  29. package/src/cli.js +1377 -1099
  30. package/src/commands/agents.js +102 -7
  31. package/src/commands/artifact-validate.js +33 -4
  32. package/src/commands/auth.js +272 -0
  33. package/src/commands/brain-query.js +44 -0
  34. package/src/commands/briefing.js +344 -0
  35. package/src/commands/commit-prepare.js +547 -0
  36. package/src/commands/compress-agents.js +416 -0
  37. package/src/commands/context-health.js +4 -2
  38. package/src/commands/context-trim.js +17 -11
  39. package/src/commands/design-hybrid-options.js +3 -3
  40. package/src/commands/devlog-process.js +6 -4
  41. package/src/commands/dossier.js +423 -0
  42. package/src/commands/feature-archive.js +513 -0
  43. package/src/commands/feature-close.js +123 -18
  44. package/src/commands/gate-approve.js +198 -0
  45. package/src/commands/gate-check.js +24 -5
  46. package/src/commands/genome-doctor.js +166 -9
  47. package/src/commands/git-guard.js +170 -0
  48. package/src/commands/harness.js +121 -0
  49. package/src/commands/implementation-plan.js +47 -20
  50. package/src/commands/init.js +6 -2
  51. package/src/commands/install.js +6 -2
  52. package/src/commands/live.js +497 -56
  53. package/src/commands/locale-apply.js +9 -6
  54. package/src/commands/locale-diff.js +11 -112
  55. package/src/commands/mcp-doctor.js +2 -1
  56. package/src/commands/mcp-init.js +4 -10
  57. package/src/commands/memory.js +234 -0
  58. package/src/commands/parallel-assign.js +107 -27
  59. package/src/commands/parallel-doctor.js +416 -3
  60. package/src/commands/parallel-guard.js +241 -0
  61. package/src/commands/parallel-init.js +66 -4
  62. package/src/commands/parallel-merge.js +299 -0
  63. package/src/commands/parallel-status.js +147 -3
  64. package/src/commands/preflight.js +63 -4
  65. package/src/commands/qa-init.js +10 -5
  66. package/src/commands/revision.js +235 -0
  67. package/src/commands/scaffold-complete.js +188 -0
  68. package/src/commands/security-audit.js +275 -0
  69. package/src/commands/security-scan.js +376 -0
  70. package/src/commands/self-implement-loop.js +46 -2
  71. package/src/commands/setup-context.js +11 -10
  72. package/src/commands/squad-agent-create.js +51 -9
  73. package/src/commands/squad-investigate.js +53 -0
  74. package/src/commands/squad-plan.js +33 -1
  75. package/src/commands/squad-scaffold.js +4 -3
  76. package/src/commands/squad-score.js +71 -14
  77. package/src/commands/squad-status.js +22 -1
  78. package/src/commands/squad-validate.js +93 -2
  79. package/src/commands/store-genome.js +304 -0
  80. package/src/commands/store-skill.js +247 -0
  81. package/src/commands/store-squad.js +431 -0
  82. package/src/commands/store-system.js +392 -0
  83. package/src/commands/tool-capabilities.js +63 -0
  84. package/src/commands/update.js +3 -3
  85. package/src/commands/verify-gate.js +40 -0
  86. package/src/commands/workflow-execute.js +644 -155
  87. package/src/commands/workflow-harden.js +231 -0
  88. package/src/commands/workflow-heal.js +136 -0
  89. package/src/commands/workflow-next.js +460 -22
  90. package/src/commands/workflow-status.js +328 -138
  91. package/src/commands/workspace.js +144 -0
  92. package/src/constants.js +42 -75
  93. package/src/context-memory.js +133 -4
  94. package/src/context-writer.js +2 -1
  95. package/src/context.js +32 -2
  96. package/src/doctor.js +46 -6
  97. package/src/dossier/codemap-store.js +267 -0
  98. package/src/dossier/dossier-bootstrap.js +222 -0
  99. package/src/dossier/dossier-compact.js +159 -0
  100. package/src/dossier/lock.js +128 -0
  101. package/src/dossier/revision-store.js +313 -0
  102. package/src/dossier/schema.js +155 -0
  103. package/src/dossier/store.js +400 -0
  104. package/src/execution-gateway.js +3 -0
  105. package/src/friction-scanner.js +202 -0
  106. package/src/genome-schema.js +24 -1
  107. package/src/genomes.js +33 -0
  108. package/src/handoff-contract.js +363 -0
  109. package/src/handoff-validator.js +45 -0
  110. package/src/harness/circuit-breaker.js +135 -0
  111. package/src/i18n/messages/en.js +317 -22
  112. package/src/i18n/messages/es.js +259 -18
  113. package/src/i18n/messages/fr.js +260 -18
  114. package/src/i18n/messages/pt-BR.js +313 -22
  115. package/src/install-profile.js +0 -16
  116. package/src/installer.js +70 -6
  117. package/src/lib/git-commit-guard.js +691 -0
  118. package/src/lib/security/artifact-reader.js +167 -0
  119. package/src/lib/security/exit-codes.js +51 -0
  120. package/src/lib/security/findings-writer.js +176 -0
  121. package/src/lib/security/runtime-events.js +77 -0
  122. package/src/lib/security/secrets-regex.js +115 -0
  123. package/src/lib/store/security-scan.js +173 -0
  124. package/src/lib/terminal-checkbox.js +130 -0
  125. package/src/lib/tmux-launcher.js +163 -0
  126. package/src/lib/tool-capabilities.js +102 -0
  127. package/src/locales.js +12 -8
  128. package/src/parallel-workspace.js +756 -0
  129. package/src/parser.js +8 -1
  130. package/src/path-guard.js +47 -0
  131. package/src/preflight-engine.js +237 -26
  132. package/src/self-healing.js +142 -0
  133. package/src/session-handoff.js +111 -1
  134. package/src/squad/squad-scaffold.js +183 -19
  135. package/src/test-briefing.js +226 -0
  136. package/src/updater.js +1 -1
  137. package/src/utils.js +3 -0
  138. package/src/workflow-gates.js +185 -0
  139. package/template/.aioson/agents/analyst.md +76 -130
  140. package/template/.aioson/agents/architect.md +53 -86
  141. package/template/.aioson/agents/committer.md +161 -0
  142. package/template/.aioson/agents/cypher.md +252 -0
  143. package/template/.aioson/agents/dev.md +112 -628
  144. package/template/.aioson/agents/deyvin.md +33 -236
  145. package/template/.aioson/agents/discover.md +235 -0
  146. package/template/.aioson/agents/discovery-design-doc.md +17 -252
  147. package/template/.aioson/agents/genome.md +76 -26
  148. package/template/.aioson/agents/manifests/analyst.manifest.json +26 -0
  149. package/template/.aioson/agents/manifests/architect.manifest.json +23 -0
  150. package/template/.aioson/agents/manifests/committer.manifest.json +23 -0
  151. package/template/.aioson/agents/manifests/dev.manifest.json +37 -0
  152. package/template/.aioson/agents/manifests/orchestrator.manifest.json +30 -0
  153. package/template/.aioson/agents/manifests/pentester.manifest.json +39 -0
  154. package/template/.aioson/agents/manifests/pm.manifest.json +26 -0
  155. package/template/.aioson/agents/manifests/product.manifest.json +23 -0
  156. package/template/.aioson/agents/manifests/qa.manifest.json +25 -0
  157. package/template/.aioson/agents/manifests/setup.manifest.json +20 -0
  158. package/template/.aioson/agents/manifests/ux-ui.manifest.json +24 -0
  159. package/template/.aioson/agents/neo.md +5 -7
  160. package/template/.aioson/agents/orache.md +2 -6
  161. package/template/.aioson/agents/orchestrator.md +81 -182
  162. package/template/.aioson/agents/pentester.md +235 -0
  163. package/template/.aioson/agents/pm.md +40 -104
  164. package/template/.aioson/agents/product.md +99 -344
  165. package/template/.aioson/agents/profiler-enricher.md +57 -6
  166. package/template/.aioson/agents/profiler-forge.md +17 -7
  167. package/template/.aioson/agents/profiler-researcher.md +29 -6
  168. package/template/.aioson/agents/qa.md +168 -514
  169. package/template/.aioson/agents/setup.md +52 -278
  170. package/template/.aioson/agents/sheldon.md +122 -754
  171. package/template/.aioson/agents/site-forge.md +111 -1583
  172. package/template/.aioson/agents/squad.md +139 -2010
  173. package/template/.aioson/agents/tester.md +10 -0
  174. package/template/.aioson/agents/ux-ui.md +104 -812
  175. package/template/.aioson/agents/validator.md +69 -0
  176. package/template/.aioson/brains/scripts/query.js +5 -1
  177. package/template/.aioson/config/autonomy-protocol.json +43 -0
  178. package/template/.aioson/config.md +43 -15
  179. package/template/.aioson/constitution.md +36 -33
  180. package/template/.aioson/context/design-doc.md +136 -0
  181. package/template/.aioson/context/project-map.md +57 -0
  182. package/template/.aioson/design-docs/code-reuse.md +48 -0
  183. package/template/.aioson/design-docs/componentization.md +47 -0
  184. package/template/.aioson/design-docs/file-size.md +52 -0
  185. package/template/.aioson/design-docs/folder-structure.md +51 -0
  186. package/template/.aioson/design-docs/naming.md +54 -0
  187. package/template/.aioson/docs/LAYERS.md +12 -2
  188. package/template/.aioson/docs/dev/execution-discipline.md +106 -0
  189. package/template/.aioson/docs/dev/stack-conventions.md +83 -0
  190. package/template/.aioson/docs/deyvin/continuity-recovery.md +57 -0
  191. package/template/.aioson/docs/deyvin/debugging-escalation.md +30 -0
  192. package/template/.aioson/docs/deyvin/pair-execution.md +44 -0
  193. package/template/.aioson/docs/deyvin/runtime-handoffs.md +36 -0
  194. package/template/.aioson/docs/product/conversation-playbook.md +116 -0
  195. package/template/.aioson/docs/product/prd-contract.md +107 -0
  196. package/template/.aioson/docs/product/quality-lens.md +57 -0
  197. package/template/.aioson/docs/product/research-loop.md +65 -0
  198. package/template/.aioson/docs/sheldon/enrichment-paths.md +134 -0
  199. package/template/.aioson/docs/sheldon/quality-lens.md +57 -0
  200. package/template/.aioson/docs/sheldon/research-loop.md +56 -0
  201. package/template/.aioson/docs/sheldon/web-intelligence.md +75 -0
  202. package/template/.aioson/docs/site-forge-build.md +195 -0
  203. package/template/.aioson/docs/site-forge-extraction.md +135 -0
  204. package/template/.aioson/docs/site-forge-qa.md +155 -0
  205. package/template/.aioson/docs/site-forge-recon.md +434 -0
  206. package/template/.aioson/docs/site-forge-transform.md +249 -0
  207. package/template/.aioson/docs/squad/content-output.md +91 -0
  208. package/template/.aioson/docs/squad/creation-flow.md +135 -0
  209. package/template/.aioson/docs/squad/domain-classification.md +117 -0
  210. package/template/.aioson/docs/squad/genome-bindings.md +47 -0
  211. package/template/.aioson/docs/squad/package-contract.md +234 -0
  212. package/template/.aioson/docs/squad/quality-lens.md +56 -0
  213. package/template/.aioson/docs/squad/research-loop.md +59 -0
  214. package/template/.aioson/docs/squad/session-operations.md +117 -0
  215. package/template/.aioson/docs/squad/workflow-quality.md +165 -0
  216. package/template/.aioson/docs/ux-ui/accessibility-audit.md +55 -0
  217. package/template/.aioson/docs/ux-ui/audit-mode.md +86 -0
  218. package/template/.aioson/docs/ux-ui/component-map.md +35 -0
  219. package/template/.aioson/docs/ux-ui/design-execution.md +111 -0
  220. package/template/.aioson/docs/ux-ui/design-gate.md +27 -0
  221. package/template/.aioson/docs/ux-ui/research-mode.md +39 -0
  222. package/template/.aioson/docs/ux-ui/site-delivery.md +156 -0
  223. package/template/.aioson/docs/ux-ui/token-contract.md +57 -0
  224. package/template/.aioson/genomes/copywriting.meta.json +48 -0
  225. package/template/.aioson/git-guard.json +11 -0
  226. package/template/.aioson/mcp/servers.md +0 -1
  227. package/template/.aioson/rules/agent-language-policy.md +93 -0
  228. package/template/.aioson/rules/aioson-context-boundary.md +63 -0
  229. package/template/.aioson/rules/canonical-path-contract.md +47 -0
  230. package/template/.aioson/rules/data-format-convention.md +24 -86
  231. package/template/.aioson/rules/disk-first-artifacts.md +44 -0
  232. package/template/.aioson/rules/output-brevity.md +44 -0
  233. package/template/.aioson/rules/prd-section-ownership.md +49 -0
  234. package/template/.aioson/rules/security-baseline.md +139 -0
  235. package/template/.aioson/rules/spec-level-ownership.md +61 -0
  236. package/template/.aioson/rules/squad-driver-pattern.md +81 -0
  237. package/template/.aioson/schemas/squad-blueprint.schema.json +24 -0
  238. package/template/.aioson/schemas/squad-manifest.schema.json +44 -0
  239. package/template/.aioson/skills/process/aioson-spec-driven/references/pm.md +30 -0
  240. package/template/.aioson/skills/process/secure-tdd/SKILL.md +97 -0
  241. package/template/.aioson/skills/process/secure-tdd/references/nextjs.md +81 -0
  242. package/template/.aioson/skills/process/secure-tdd/references/node-express.md +91 -0
  243. package/template/.aioson/skills/process/secure-tdd/references/planned-stacks.md +33 -0
  244. package/template/.aioson/skills/static/harness-validate/SKILL.md +46 -0
  245. package/template/.aioson/skills/static/web-research-cache.md +3 -0
  246. package/template/.aioson/tasks/squad-create.md +35 -8
  247. package/template/.aioson/tasks/squad-design.md +50 -2
  248. package/template/.aioson/tasks/squad-investigate.md +14 -1
  249. package/template/.claude/commands/aioson/agent/committer.md +5 -0
  250. package/template/.claude/commands/aioson/agent/copywriter.md +5 -0
  251. package/template/.claude/commands/aioson/agent/cypher.md +5 -0
  252. package/template/.claude/commands/aioson/agent/pair.md +5 -0
  253. package/template/.claude/commands/aioson/agent/validator.md +5 -0
  254. package/template/.gemini/commands/aios-analyst.toml +6 -3
  255. package/template/.gemini/commands/aios-architect.toml +7 -6
  256. package/template/.gemini/commands/aios-committer.toml +7 -0
  257. package/template/.gemini/commands/aios-copywriter.toml +7 -0
  258. package/template/.gemini/commands/aios-cypher.toml +7 -0
  259. package/template/.gemini/commands/aios-dev.toml +8 -7
  260. package/template/.gemini/commands/aios-deyvin.toml +6 -5
  261. package/template/.gemini/commands/aios-discovery-design-doc.toml +6 -3
  262. package/template/.gemini/commands/aios-genome.toml +7 -0
  263. package/template/.gemini/commands/aios-neo.toml +5 -3
  264. package/template/.gemini/commands/aios-orache.toml +7 -0
  265. package/template/.gemini/commands/aios-orchestrator.toml +8 -7
  266. package/template/.gemini/commands/aios-pair.toml +6 -5
  267. package/template/.gemini/commands/aios-pm.toml +8 -7
  268. package/template/.gemini/commands/aios-product.toml +5 -3
  269. package/template/.gemini/commands/aios-qa.toml +6 -5
  270. package/template/.gemini/commands/aios-setup.toml +5 -2
  271. package/template/.gemini/commands/aios-sheldon.toml +7 -0
  272. package/template/.gemini/commands/aios-site-forge.toml +7 -0
  273. package/template/.gemini/commands/aios-squad.toml +7 -0
  274. package/template/.gemini/commands/aios-tester.toml +6 -5
  275. package/template/.gemini/commands/aios-ux-ui.toml +8 -7
  276. package/template/.gemini/commands/aios-validator.toml +7 -0
  277. package/template/AGENTS.md +12 -1
  278. package/template/CLAUDE.md +5 -1
  279. package/template/.aioson/locales/en/agents/analyst.md +0 -244
  280. package/template/.aioson/locales/en/agents/architect.md +0 -245
  281. package/template/.aioson/locales/en/agents/dev.md +0 -397
  282. package/template/.aioson/locales/en/agents/deyvin.md +0 -137
  283. package/template/.aioson/locales/en/agents/discovery-design-doc.md +0 -27
  284. package/template/.aioson/locales/en/agents/genome.md +0 -212
  285. package/template/.aioson/locales/en/agents/neo.md +0 -8
  286. package/template/.aioson/locales/en/agents/orache.md +0 -6
  287. package/template/.aioson/locales/en/agents/orchestrator.md +0 -189
  288. package/template/.aioson/locales/en/agents/pair.md +0 -5
  289. package/template/.aioson/locales/en/agents/pm.md +0 -84
  290. package/template/.aioson/locales/en/agents/product.md +0 -378
  291. package/template/.aioson/locales/en/agents/profiler-enricher.md +0 -5
  292. package/template/.aioson/locales/en/agents/profiler-forge.md +0 -5
  293. package/template/.aioson/locales/en/agents/profiler-researcher.md +0 -5
  294. package/template/.aioson/locales/en/agents/qa.md +0 -270
  295. package/template/.aioson/locales/en/agents/setup.md +0 -421
  296. package/template/.aioson/locales/en/agents/sheldon.md +0 -455
  297. package/template/.aioson/locales/en/agents/squad.md +0 -449
  298. package/template/.aioson/locales/en/agents/tester.md +0 -6
  299. package/template/.aioson/locales/en/agents/ux-ui.md +0 -668
  300. package/template/.aioson/locales/es/agents/analyst.md +0 -225
  301. package/template/.aioson/locales/es/agents/architect.md +0 -245
  302. package/template/.aioson/locales/es/agents/dev.md +0 -370
  303. package/template/.aioson/locales/es/agents/deyvin.md +0 -99
  304. package/template/.aioson/locales/es/agents/discovery-design-doc.md +0 -21
  305. package/template/.aioson/locales/es/agents/genome.md +0 -104
  306. package/template/.aioson/locales/es/agents/neo.md +0 -50
  307. package/template/.aioson/locales/es/agents/orache.md +0 -105
  308. package/template/.aioson/locales/es/agents/orchestrator.md +0 -194
  309. package/template/.aioson/locales/es/agents/pair.md +0 -7
  310. package/template/.aioson/locales/es/agents/pm.md +0 -90
  311. package/template/.aioson/locales/es/agents/product.md +0 -372
  312. package/template/.aioson/locales/es/agents/profiler-enricher.md +0 -7
  313. package/template/.aioson/locales/es/agents/profiler-forge.md +0 -7
  314. package/template/.aioson/locales/es/agents/profiler-researcher.md +0 -7
  315. package/template/.aioson/locales/es/agents/qa.md +0 -198
  316. package/template/.aioson/locales/es/agents/setup.md +0 -405
  317. package/template/.aioson/locales/es/agents/sheldon.md +0 -309
  318. package/template/.aioson/locales/es/agents/squad.md +0 -532
  319. package/template/.aioson/locales/es/agents/tester.md +0 -9
  320. package/template/.aioson/locales/es/agents/ux-ui.md +0 -212
  321. package/template/.aioson/locales/fr/agents/analyst.md +0 -225
  322. package/template/.aioson/locales/fr/agents/architect.md +0 -245
  323. package/template/.aioson/locales/fr/agents/dev.md +0 -370
  324. package/template/.aioson/locales/fr/agents/deyvin.md +0 -99
  325. package/template/.aioson/locales/fr/agents/discovery-design-doc.md +0 -21
  326. package/template/.aioson/locales/fr/agents/genome.md +0 -104
  327. package/template/.aioson/locales/fr/agents/neo.md +0 -50
  328. package/template/.aioson/locales/fr/agents/orache.md +0 -106
  329. package/template/.aioson/locales/fr/agents/orchestrator.md +0 -194
  330. package/template/.aioson/locales/fr/agents/pair.md +0 -7
  331. package/template/.aioson/locales/fr/agents/pm.md +0 -90
  332. package/template/.aioson/locales/fr/agents/product.md +0 -372
  333. package/template/.aioson/locales/fr/agents/profiler-enricher.md +0 -7
  334. package/template/.aioson/locales/fr/agents/profiler-forge.md +0 -7
  335. package/template/.aioson/locales/fr/agents/profiler-researcher.md +0 -7
  336. package/template/.aioson/locales/fr/agents/qa.md +0 -198
  337. package/template/.aioson/locales/fr/agents/setup.md +0 -405
  338. package/template/.aioson/locales/fr/agents/sheldon.md +0 -309
  339. package/template/.aioson/locales/fr/agents/squad.md +0 -532
  340. package/template/.aioson/locales/fr/agents/tester.md +0 -9
  341. package/template/.aioson/locales/fr/agents/ux-ui.md +0 -212
  342. package/template/.aioson/locales/pt-BR/agents/analyst.md +0 -319
  343. package/template/.aioson/locales/pt-BR/agents/architect.md +0 -284
  344. package/template/.aioson/locales/pt-BR/agents/dev.md +0 -483
  345. package/template/.aioson/locales/pt-BR/agents/deyvin.md +0 -184
  346. package/template/.aioson/locales/pt-BR/agents/discovery-design-doc.md +0 -198
  347. package/template/.aioson/locales/pt-BR/agents/genome.md +0 -297
  348. package/template/.aioson/locales/pt-BR/agents/neo.md +0 -208
  349. package/template/.aioson/locales/pt-BR/agents/orache.md +0 -137
  350. package/template/.aioson/locales/pt-BR/agents/orchestrator.md +0 -324
  351. package/template/.aioson/locales/pt-BR/agents/pair.md +0 -5
  352. package/template/.aioson/locales/pt-BR/agents/pm.md +0 -182
  353. package/template/.aioson/locales/pt-BR/agents/product.md +0 -466
  354. package/template/.aioson/locales/pt-BR/agents/profiler-enricher.md +0 -5
  355. package/template/.aioson/locales/pt-BR/agents/profiler-forge.md +0 -5
  356. package/template/.aioson/locales/pt-BR/agents/profiler-researcher.md +0 -5
  357. package/template/.aioson/locales/pt-BR/agents/qa.md +0 -300
  358. package/template/.aioson/locales/pt-BR/agents/setup.md +0 -533
  359. package/template/.aioson/locales/pt-BR/agents/sheldon.md +0 -323
  360. package/template/.aioson/locales/pt-BR/agents/squad.md +0 -1330
  361. package/template/.aioson/locales/pt-BR/agents/tester.md +0 -449
  362. package/template/.aioson/locales/pt-BR/agents/ux-ui.md +0 -669
@@ -1,113 +1,495 @@
1
1
  'use strict';
2
2
 
3
3
  /**
4
- * aioson workflow:execute — orchestrate the full agent workflow for a feature.
4
+ * aioson workflow:execute — unified runner shell on top of workflow:next.
5
5
  *
6
- * Uses agent:prompt, gate:check, pulse:update, feature:close as building blocks.
7
- * Always supports --dry-run to preview the plan without executing.
8
- *
9
- * Usage:
10
- * aioson workflow:execute . --feature=checkout --tool=claude --dry-run
11
- * aioson workflow:execute . --feature=checkout --tool=claude
12
- * aioson workflow:execute . --feature=checkout --tool=claude --start-from=dev
13
- * aioson workflow:execute . --feature=checkout --json --dry-run
6
+ * Dry-run previews the next actionable steps using the real workflow state.
7
+ * Execution advances one or more valid checkpoints and writes a resumable snapshot.
14
8
  */
15
9
 
10
+ const fs = require('node:fs/promises');
16
11
  const path = require('node:path');
17
- const { execSync } = require('node:child_process');
18
12
  const {
19
13
  detectClassification,
20
14
  scanArtifacts,
21
15
  readPhaseGates
22
16
  } = require('../preflight-engine');
17
+ const {
18
+ readAutonomyProtocol,
19
+ getToolPolicy,
20
+ canRunHeadless,
21
+ resolveEffectiveMode
22
+ } = require('../autonomy-policy');
23
+ const { readAgentManifest } = require('../agent-manifests');
24
+ const { exists, ensureDir } = require('../utils');
25
+ const { validateHandoffContract } = require('../handoff-contract');
26
+ const { readHandoff, readHandoffProtocol } = require('../session-handoff');
27
+ const {
28
+ STATE_RELATIVE_PATH,
29
+ buildDefaultWorkflowConfig,
30
+ readWorkflowConfig,
31
+ runWorkflowNext
32
+ } = require('./workflow-next');
33
+ const { runWorkflowStatus } = require('./workflow-status');
34
+ const {
35
+ PARALLEL_RELATIVE_DIR,
36
+ collectWritePathConflicts,
37
+ extractStatusWritePathItems
38
+ } = require('../parallel-workspace');
23
39
 
24
40
  const BAR = '━'.repeat(45);
41
+ const EXECUTION_STATE_RELATIVE_PATH = '.aioson/context/workflow-execute.json';
42
+
43
+ const STEP_META = {
44
+ setup: { description: 'Initialize project context', gate_before: null, gate_after: null },
45
+ product: { description: 'Generate PRD', gate_before: null, gate_after: null },
46
+ analyst: { description: 'Map requirements + spec', gate_before: null, gate_after: 'A' },
47
+ architect: { description: 'Architecture design', gate_before: 'A', gate_after: 'B' },
48
+ 'ux-ui': { description: 'UI/UX design', gate_before: 'A', gate_after: 'B', optional: true },
49
+ pm: { description: 'Backlog + PM plan', gate_before: 'B', gate_after: 'C' },
50
+ orchestrator: { description: 'Coordinate execution lanes', gate_before: 'C', gate_after: null },
51
+ dev: { description: 'Implementation', gate_before: null, gate_after: 'C' },
52
+ qa: { description: 'QA + feature closure', gate_before: 'C', gate_after: 'D' },
53
+ committer: { description: 'Prepare commit', gate_before: 'D', gate_after: null }
54
+ };
25
55
 
26
- const WORKFLOW_BY_CLASSIFICATION = {
27
- MICRO: [
28
- { agent: 'dev', gate_before: null, gate_after: null, description: 'Direct implementation' }
29
- ],
30
- SMALL: [
31
- { agent: 'product', gate_before: null, gate_after: null, description: 'Generate PRD' },
32
- { agent: 'sheldon', gate_before: null, gate_after: null, description: 'Enrich PRD (recommended)', optional: true },
33
- { agent: 'analyst', gate_before: null, gate_after: 'A', description: 'Map requirements + spec' },
34
- { agent: 'dev', gate_before: 'A', gate_after: 'C', description: 'Implementation' },
35
- { agent: 'qa', gate_before: 'C', gate_after: 'D', description: 'QA + feature closure' }
36
- ],
37
- MEDIUM: [
38
- { agent: 'product', gate_before: null, gate_after: null, description: 'Generate PRD' },
39
- { agent: 'sheldon', gate_before: null, gate_after: null, description: 'Enrich PRD (required)', optional: false },
40
- { agent: 'analyst', gate_before: null, gate_after: 'A', description: 'Map requirements + spec' },
41
- { agent: 'architect', gate_before: 'A', gate_after: 'B', description: 'Architecture design' },
42
- { agent: 'ux-ui', gate_before: 'A', gate_after: 'B', description: 'UI/UX design', optional: true },
43
- { agent: 'pm', gate_before: 'B', gate_after: null, description: 'Backlog + PM plan' },
44
- { agent: 'dev', gate_before: 'C', gate_after: 'C', description: 'Implementation' },
45
- { agent: 'qa', gate_before: 'C', gate_after: 'D', description: 'QA + feature closure' }
46
- ]
56
+ const GATE_NAMES = {
57
+ A: 'requirements',
58
+ B: 'design',
59
+ C: 'plan',
60
+ D: 'execution'
47
61
  };
48
62
 
49
- const GATE_NAMES = { A: 'requirements', B: 'design', C: 'plan', D: 'execution' };
63
+ const GATE_RESPONSIBLE_AGENT = {
64
+ A: '@analyst',
65
+ B: '@architect',
66
+ C: '@pm (for MEDIUM) or @dev (for SMALL/MICRO)',
67
+ D: '@qa'
68
+ };
69
+
70
+ async function readJsonIfExists(filePath) {
71
+ if (!(await exists(filePath))) return null;
72
+ try {
73
+ const raw = await fs.readFile(filePath, 'utf8');
74
+ return JSON.parse(raw);
75
+ } catch {
76
+ return null;
77
+ }
78
+ }
79
+
80
+ async function writeJson(filePath, payload) {
81
+ await ensureDir(path.dirname(filePath));
82
+ await fs.writeFile(filePath, `${JSON.stringify(payload, null, 2)}\n`, 'utf8');
83
+ }
84
+
85
+ function normalizeClassification(value, fallback = 'SMALL') {
86
+ const safe = String(value || '').trim().toUpperCase();
87
+ return ['MICRO', 'SMALL', 'MEDIUM'].includes(safe) ? safe : fallback;
88
+ }
89
+
90
+ function normalizeAgentName(value) {
91
+ return String(value || '').trim().toLowerCase().replace(/^@/, '');
92
+ }
93
+
94
+ function findNextFromSequence(sequence, completed, skipped = []) {
95
+ const done = new Set([...(completed || []), ...(skipped || [])].map(normalizeAgentName));
96
+ return sequence.find((stage) => !done.has(normalizeAgentName(stage))) || null;
97
+ }
98
+
99
+ function getFocusStage(state) {
100
+ if (!state || typeof state !== 'object') return null;
101
+ return state.current || state.next || null;
102
+ }
103
+
104
+ function isGateApproved(gates, gateLetter) {
105
+ if (!gateLetter) return true;
106
+ const gateName = GATE_NAMES[gateLetter];
107
+ return gateName ? gates[gateName] === 'approved' : true;
108
+ }
109
+
110
+ async function resolveFeatureSequence(targetDir, classification) {
111
+ const fallback = buildDefaultWorkflowConfig();
112
+ try {
113
+ const { config } = await readWorkflowConfig(targetDir);
114
+ const sequence = config.feature && config.feature[classification];
115
+ return Array.isArray(sequence) && sequence.length > 0
116
+ ? [...sequence]
117
+ : [...(fallback.feature[classification] || fallback.feature.SMALL)];
118
+ } catch {
119
+ return [...(fallback.feature[classification] || fallback.feature.SMALL)];
120
+ }
121
+ }
122
+
123
+ function inferCompletedStagesFromArtifacts(sequence, artifacts, gates) {
124
+ const completed = [];
125
+
126
+ for (const stage of sequence) {
127
+ const normalized = normalizeAgentName(stage);
128
+ let inferred = false;
129
+
130
+ if (normalized === 'product') {
131
+ inferred = Boolean(artifacts.prd && artifacts.prd.exists);
132
+ } else if (normalized === 'analyst') {
133
+ inferred = Boolean(artifacts.requirements && artifacts.requirements.exists && gates.requirements === 'approved');
134
+ } else if (normalized === 'architect') {
135
+ inferred = Boolean(artifacts.architecture && artifacts.architecture.exists && gates.design === 'approved');
136
+ } else if (normalized === 'pm') {
137
+ inferred = Boolean(artifacts.implementation_plan && artifacts.implementation_plan.exists && gates.plan === 'approved');
138
+ } else if (normalized === 'qa') {
139
+ inferred = gates.execution === 'approved';
140
+ } else {
141
+ break;
142
+ }
143
+
144
+ if (!inferred) break;
145
+ completed.push(normalized);
146
+ }
147
+
148
+ return completed;
149
+ }
150
+
151
+ async function seedFeatureWorkflowState(targetDir, slug, classification, startFrom) {
152
+ const statePath = path.join(targetDir, STATE_RELATIVE_PATH);
153
+ const existing = await readJsonIfExists(statePath);
154
+ const focusStage = getFocusStage(existing);
155
+
156
+ if (
157
+ existing &&
158
+ existing.mode === 'feature' &&
159
+ existing.featureSlug &&
160
+ existing.featureSlug !== slug &&
161
+ focusStage
162
+ ) {
163
+ return {
164
+ ok: false,
165
+ reason: 'different_active_feature',
166
+ active_feature: existing.featureSlug,
167
+ active_stage: focusStage
168
+ };
169
+ }
170
+
171
+ if (existing && existing.mode === 'feature' && existing.featureSlug === slug) {
172
+ return {
173
+ ok: true,
174
+ resumed: true,
175
+ state: existing,
176
+ statePath: STATE_RELATIVE_PATH
177
+ };
178
+ }
50
179
 
51
- async function buildExecutionPlan(targetDir, slug, classification, startFrom) {
52
- const steps = WORKFLOW_BY_CLASSIFICATION[classification] || WORKFLOW_BY_CLASSIFICATION.SMALL;
53
180
  const artifacts = await scanArtifacts(targetDir, slug);
54
181
  const gates = await readPhaseGates(targetDir, slug);
182
+ const sequence = await resolveFeatureSequence(targetDir, classification);
183
+ const completed = inferCompletedStagesFromArtifacts(sequence, artifacts, gates);
184
+ const normalizedStartFrom = startFrom ? normalizeAgentName(startFrom) : null;
185
+
186
+ let skipped = [];
187
+ let next = findNextFromSequence(sequence, completed, skipped);
188
+
189
+ if (normalizedStartFrom && sequence.includes(normalizedStartFrom)) {
190
+ const targetIndex = sequence.indexOf(normalizedStartFrom);
191
+ skipped = sequence.slice(0, targetIndex).filter((stage) => !completed.includes(stage));
192
+ next = normalizedStartFrom;
193
+ }
194
+
195
+ const state = {
196
+ version: 1,
197
+ mode: 'feature',
198
+ classification,
199
+ sequence,
200
+ current: null,
201
+ next,
202
+ completed,
203
+ skipped,
204
+ featureSlug: slug,
205
+ detour: null,
206
+ updatedAt: new Date().toISOString()
207
+ };
55
208
 
56
- const plan = [];
57
- let stepNum = 1;
58
- let startFromReached = !startFrom;
209
+ await writeJson(statePath, state);
210
+ return {
211
+ ok: true,
212
+ resumed: false,
213
+ state,
214
+ statePath: STATE_RELATIVE_PATH
215
+ };
216
+ }
217
+
218
+ async function buildExecutionPlan(targetDir, slug, classification, workflowState = null, startFrom = null) {
219
+ const sequence = workflowState && Array.isArray(workflowState.sequence) && workflowState.sequence.length > 0
220
+ ? [...workflowState.sequence]
221
+ : await resolveFeatureSequence(targetDir, classification);
222
+ const artifacts = await scanArtifacts(targetDir, slug);
223
+ const gates = await readPhaseGates(targetDir, slug);
224
+ const normalizedStartFrom = startFrom ? normalizeAgentName(startFrom) : null;
225
+ const focusStage = getFocusStage(workflowState);
226
+ const inferredCompleted = inferCompletedStagesFromArtifacts(sequence, artifacts, gates);
227
+ const completedSet = new Set((workflowState && workflowState.completed) || inferredCompleted);
228
+ const skippedSet = new Set((workflowState && workflowState.skipped) || []);
229
+
230
+ const applyStartFrom = Boolean(
231
+ normalizedStartFrom &&
232
+ sequence.includes(normalizedStartFrom) &&
233
+ (!workflowState || (!workflowState.current && workflowState.next === normalizedStartFrom))
234
+ );
235
+
236
+ const scopedSequence = applyStartFrom
237
+ ? sequence.slice(sequence.indexOf(normalizedStartFrom)).filter(Boolean)
238
+ : sequence;
239
+
240
+ const steps = [];
241
+ for (const [index, agent] of scopedSequence.entries()) {
242
+ const meta = STEP_META[agent] || {
243
+ description: `Execute @${agent}`,
244
+ gate_before: null,
245
+ gate_after: null,
246
+ optional: false
247
+ };
59
248
 
60
- for (const step of steps) {
61
- if (startFrom && !startFromReached) {
62
- if (step.agent === startFrom) startFromReached = true;
63
- else continue;
249
+ let status = 'pending';
250
+ if (completedSet.has(agent)) status = 'completed';
251
+ else if (skippedSet.has(agent)) status = 'skipped';
252
+ else if (focusStage === agent) status = 'active';
253
+
254
+ const predictedBlockers = [];
255
+ if (status !== 'completed' && status !== 'skipped' && meta.gate_before && !isGateApproved(gates, meta.gate_before)) {
256
+ const responsible = GATE_RESPONSIBLE_AGENT[meta.gate_before] || 'previous agent';
257
+ const featureArg = slug ? ` --feature=${slug}` : '';
258
+ predictedBlockers.push(
259
+ `Gate ${meta.gate_before} (${GATE_NAMES[meta.gate_before] || meta.gate_before}) not approved — ` +
260
+ `responsible: ${responsible} — ` +
261
+ `approve with: aioson gate:approve .${featureArg} --gate=${meta.gate_before}`
262
+ );
263
+ if (status === 'pending') status = 'blocked';
64
264
  }
65
265
 
66
- // Determine if step can be skipped (artifact already exists)
67
- let skip = false;
68
- let skipReason = null;
69
-
70
- if (step.agent === 'product' && artifacts.prd.exists) {
71
- skip = true; skipReason = 'prd already exists';
72
- } else if (step.agent === 'sheldon' && artifacts.sheldon_enrichment.exists) {
73
- skip = true; skipReason = 'sheldon enrichment already exists';
74
- } else if (step.agent === 'analyst' && artifacts.requirements.exists && gates.requirements === 'approved') {
75
- skip = true; skipReason = 'requirements + Gate A already approved';
76
- } else if (step.agent === 'architect' && artifacts.architecture.exists && gates.design === 'approved') {
77
- skip = true; skipReason = 'architecture + Gate B already approved';
266
+ if (status === 'active' && workflowState) {
267
+ const contractCheck = await validateHandoffContract(targetDir, workflowState, agent);
268
+ if (!contractCheck.ok) {
269
+ predictedBlockers.push(...contractCheck.missing);
270
+ }
78
271
  }
79
272
 
80
- plan.push({
81
- step: stepNum++,
82
- agent: step.agent,
83
- description: step.description,
84
- gate_before: step.gate_before,
85
- gate_after: step.gate_after,
86
- optional: step.optional || false,
273
+ const skip = status === 'skipped' || (
274
+ status === 'completed' &&
275
+ inferredCompleted.includes(agent)
276
+ );
277
+ const skipReason = status === 'skipped'
278
+ ? 'workflow state marks this stage as skipped'
279
+ : skip
280
+ ? 'artifacts already satisfy this stage'
281
+ : null;
282
+
283
+ steps.push({
284
+ step: index + 1,
285
+ agent,
286
+ description: meta.description,
287
+ gate_before: meta.gate_before,
288
+ gate_after: meta.gate_after,
289
+ optional: Boolean(meta.optional),
290
+ status,
87
291
  skip,
88
- skip_reason: skipReason
292
+ skip_reason: skipReason,
293
+ predicted_blockers: predictedBlockers
89
294
  });
90
295
  }
91
296
 
92
- return plan;
297
+ return {
298
+ steps,
299
+ artifacts,
300
+ gates,
301
+ sequence
302
+ };
303
+ }
304
+
305
+ function buildCheckpointPayload(activation, handoff, handoffProtocol) {
306
+ const safeActivation = activation && typeof activation === 'object' ? activation : {};
307
+ return {
308
+ active_stage: safeActivation.agent || null,
309
+ current: safeActivation.current || null,
310
+ next: safeActivation.next || null,
311
+ effective_mode: safeActivation.effectiveMode || null,
312
+ instruction_path: safeActivation.instructionPath || null,
313
+ prompt: safeActivation.prompt || null,
314
+ handoff,
315
+ handoff_protocol: handoffProtocol
316
+ };
317
+ }
318
+
319
+ async function writeExecutionCheckpoint(targetDir, payload) {
320
+ const execPath = path.join(targetDir, EXECUTION_STATE_RELATIVE_PATH);
321
+ const existing = await readJsonIfExists(execPath);
322
+ const history = Array.isArray(existing && existing.history) ? [...existing.history] : [];
323
+ if (payload.checkpoint) {
324
+ history.push({
325
+ at: new Date().toISOString(),
326
+ active_stage: payload.checkpoint.active_stage || null,
327
+ next: payload.checkpoint.next || null,
328
+ effective_mode: payload.checkpoint.effective_mode || null,
329
+ handoff_to: payload.checkpoint.handoff && payload.checkpoint.handoff.next_agent
330
+ ? String(payload.checkpoint.handoff.next_agent).replace(/^@/, '')
331
+ : null
332
+ });
333
+ }
334
+ const nextPayload = {
335
+ version: 1,
336
+ feature: payload.feature,
337
+ classification: payload.classification,
338
+ tool: payload.tool,
339
+ requested_mode: payload.requestedMode || null,
340
+ status: payload.status,
341
+ started_at: existing && existing.feature === payload.feature ? existing.started_at : new Date().toISOString(),
342
+ updated_at: new Date().toISOString(),
343
+ resumed_count: existing && existing.feature === payload.feature ? Number(existing.resumed_count || 0) + (payload.resumed ? 1 : 0) : (payload.resumed ? 1 : 0),
344
+ workflow_state_path: STATE_RELATIVE_PATH,
345
+ checkpoint: payload.checkpoint || null,
346
+ status_snapshot: payload.statusSnapshot || null,
347
+ suggestion: payload.suggestion || null,
348
+ resume_command: payload.resumeCommand || null,
349
+ history
350
+ };
351
+ await writeJson(execPath, nextPayload);
352
+ return nextPayload;
353
+ }
354
+
355
+ async function readStatusSnapshot(targetDir, tool, t) {
356
+ return runWorkflowStatus({
357
+ args: [targetDir],
358
+ options: { json: true, tool },
359
+ logger: { log() {}, error() {} },
360
+ t
361
+ });
362
+ }
363
+
364
+ function parseLaneStatusIndex(fileName) {
365
+ const match = String(fileName || '').match(/^agent-(\d+)\.status\.md$/);
366
+ if (!match) return null;
367
+ const value = Number(match[1]);
368
+ if (!Number.isFinite(value) || value <= 0) return null;
369
+ return Math.floor(value);
370
+ }
371
+
372
+ async function readLaneWritePaths(parallelDir, index) {
373
+ const absPath = path.join(parallelDir, `agent-${index}.status.md`);
374
+ try {
375
+ const content = await fs.readFile(absPath, 'utf8');
376
+ return { lane: index, writePathItems: extractStatusWritePathItems(content) };
377
+ } catch {
378
+ return { lane: index, writePathItems: [] };
379
+ }
93
380
  }
94
381
 
95
- function runCommand(cmd) {
382
+ async function runLaneGuardPreflight(targetDir, laneIndex) {
383
+ const parallelDir = path.join(targetDir, PARALLEL_RELATIVE_DIR);
384
+ if (!(await exists(parallelDir))) {
385
+ return { ok: true, skipped: true, reason: 'no_parallel_workspace' };
386
+ }
387
+
388
+ let entries;
96
389
  try {
97
- execSync(cmd, { stdio: 'inherit' });
98
- return { ok: true };
99
- } catch (err) {
100
- return { ok: false, error: err.message };
390
+ entries = await fs.readdir(parallelDir);
391
+ } catch {
392
+ return { ok: true, skipped: true, reason: 'parallel_dir_unreadable' };
393
+ }
394
+
395
+ const laneIndices = entries
396
+ .map(parseLaneStatusIndex)
397
+ .filter((v) => v !== null)
398
+ .sort((a, b) => a - b);
399
+
400
+ if (!laneIndices.includes(laneIndex)) {
401
+ return { ok: false, skipped: false, reason: 'lane_not_found', lane: laneIndex };
402
+ }
403
+
404
+ const lanes = await Promise.all(laneIndices.map((i) => readLaneWritePaths(parallelDir, i)));
405
+ const conflictReport = collectWritePathConflicts(lanes);
406
+ const laneConflicts = conflictReport.conflicts.filter((c) =>
407
+ Array.isArray(c.lanes) && c.lanes.includes(laneIndex)
408
+ );
409
+ const targetLane = lanes.find((l) => l.lane === laneIndex);
410
+ const writePathCount = targetLane ? targetLane.writePathItems.length : 0;
411
+ const unassigned = writePathCount === 0;
412
+
413
+ return {
414
+ ok: laneConflicts.length === 0 && !unassigned,
415
+ skipped: false,
416
+ lane: laneIndex,
417
+ writePathCount,
418
+ unassigned,
419
+ conflictCount: laneConflicts.length,
420
+ conflicts: laneConflicts,
421
+ invalidCount: conflictReport.invalidCount,
422
+ invalidPatterns: conflictReport.invalidPatterns
423
+ };
424
+ }
425
+
426
+ async function performRunnerTransition(targetDir, suggestion, tool, requestedMode, logger, t) {
427
+ if (!suggestion || !suggestion.action) {
428
+ return { ok: false, reason: 'missing_suggestion' };
429
+ }
430
+
431
+ if (suggestion.action === 'workflow_complete') {
432
+ return { ok: true, transition: 'complete', result: null };
433
+ }
434
+
435
+ if (suggestion.action === 'activate_stage' || suggestion.action === 'continue_stage') {
436
+ const result = await runWorkflowNext({
437
+ args: [targetDir],
438
+ options: {
439
+ tool,
440
+ ...(requestedMode ? { mode: requestedMode } : {}),
441
+ ...(suggestion.agent ? { agent: suggestion.agent } : {})
442
+ },
443
+ logger,
444
+ t
445
+ });
446
+ return {
447
+ ok: true,
448
+ transition: 'activate',
449
+ agent: suggestion.agent || null,
450
+ result
451
+ };
452
+ }
453
+
454
+ if (suggestion.action === 'complete_stage' && suggestion.agent) {
455
+ const safeAgent = normalizeAgentName(suggestion.agent);
456
+ const result = await runWorkflowNext({
457
+ args: [targetDir],
458
+ options: {
459
+ tool,
460
+ complete: safeAgent,
461
+ ...(requestedMode ? { mode: requestedMode } : {}),
462
+ ...((safeAgent === 'dev' || safeAgent === 'qa') ? { 'auto-heal': true } : {})
463
+ },
464
+ logger,
465
+ t
466
+ });
467
+ return {
468
+ ok: true,
469
+ transition: 'complete',
470
+ agent: safeAgent,
471
+ result
472
+ };
101
473
  }
474
+
475
+ return { ok: false, reason: 'unsupported_suggestion_action', action: suggestion.action };
102
476
  }
103
477
 
104
478
  async function runWorkflowExecute({ args, options = {}, logger }) {
105
479
  const targetDir = path.resolve(process.cwd(), args[0] || '.');
106
- const slug = options.feature ? String(options.feature) : null;
107
- const tool = options.tool ? String(options.tool) : 'claude';
480
+ const slug = options.feature ? String(options.feature).trim() : null;
481
+ const tool = options.tool ? String(options.tool).trim() : 'claude';
482
+ const requestedMode = options.mode ? String(options.mode).trim() : null;
108
483
  const dryRun = Boolean(options['dry-run'] || options.dry);
109
- const startFrom = options['start-from'] ? String(options['start-from']) : null;
484
+ const startFrom = options['start-from'] ? String(options['start-from']).trim() : null;
110
485
  const skipOptional = Boolean(options['skip-optional']);
486
+ const parsedMaxCheckpoints = Number.parseInt(String(options['max-checkpoints'] || '1'), 10);
487
+ const maxCheckpoints = Number.isInteger(parsedMaxCheckpoints) && parsedMaxCheckpoints > 0
488
+ ? parsedMaxCheckpoints
489
+ : 1;
490
+ const parsedLane = options.lane !== undefined ? Number(options.lane) : null;
491
+ const laneIndex = Number.isInteger(parsedLane) && parsedLane > 0 ? parsedLane : null;
492
+ const t = (key, payload) => payload?.agent || payload?.stage || key;
111
493
 
112
494
  if (!slug) {
113
495
  if (options.json) return { ok: false, reason: 'missing_feature' };
@@ -116,22 +498,89 @@ async function runWorkflowExecute({ args, options = {}, logger }) {
116
498
  }
117
499
 
118
500
  let classification = await detectClassification(targetDir, slug);
119
- if (!classification) classification = options.classification ? String(options.classification).toUpperCase() : 'SMALL';
501
+ if (!classification) {
502
+ classification = options.classification ? String(options.classification).toUpperCase() : 'SMALL';
503
+ }
504
+ classification = normalizeClassification(classification, 'SMALL');
505
+
506
+ const autonomyProtocol = await readAutonomyProtocol(targetDir);
507
+ const toolPolicy = getToolPolicy(autonomyProtocol, tool);
508
+ if (requestedMode === 'headless' && !canRunHeadless(toolPolicy)) {
509
+ const failure = { ok: false, reason: 'headless_not_supported', tool };
510
+ if (options.json) return failure;
511
+ logger.error(`Tool ${tool} does not support headless mode. Fallback required.`);
512
+ return failure;
513
+ }
120
514
 
121
- const plan = await buildExecutionPlan(targetDir, slug, classification, startFrom);
122
- const activePlan = plan.filter((s) => !s.skip && !(skipOptional && s.optional));
123
- const skippedPlan = plan.filter((s) => s.skip);
515
+ const parallelGuard = laneIndex !== null
516
+ ? await runLaneGuardPreflight(targetDir, laneIndex)
517
+ : null;
124
518
 
125
- if (dryRun || options.json) {
519
+ if (parallelGuard && !parallelGuard.ok && !parallelGuard.skipped) {
520
+ if (parallelGuard.reason === 'lane_not_found') {
521
+ const failure = { ok: false, reason: 'parallel_lane_not_found', lane: laneIndex };
522
+ if (options.json) return failure;
523
+ logger.error(`Lane ${laneIndex} not found in parallel workspace.`);
524
+ return failure;
525
+ }
526
+ if (parallelGuard.conflictCount > 0) {
527
+ logger.error(
528
+ `[parallel:guard] Lane ${laneIndex} has ${parallelGuard.conflictCount} write-scope conflict(s) with other lanes. Proceeding with warning.`
529
+ );
530
+ }
531
+ if (parallelGuard.unassigned) {
532
+ logger.error(
533
+ `[parallel:guard] Lane ${laneIndex} has no write paths declared. Run parallel:assign before executing.`
534
+ );
535
+ }
536
+ }
537
+
538
+ const seeded = await seedFeatureWorkflowState(targetDir, slug, classification, startFrom);
539
+ if (!seeded.ok) {
540
+ if (options.json) return seeded;
541
+ logger.error(
542
+ `Another active feature workflow is in progress: ${seeded.active_feature} (@${seeded.active_stage}).`
543
+ );
544
+ return seeded;
545
+ }
546
+
547
+ const planData = await buildExecutionPlan(targetDir, slug, classification, seeded.state, startFrom);
548
+ for (const step of planData.steps) {
549
+ const manifest = await readAgentManifest(targetDir, step.agent);
550
+ step.effective_mode = resolveEffectiveMode({
551
+ protocol: autonomyProtocol,
552
+ tool,
553
+ agentId: step.agent,
554
+ manifest,
555
+ requestedMode
556
+ });
557
+ }
558
+
559
+ const activePlan = planData.steps.filter((step) => step.status !== 'completed' && !(skipOptional && step.optional));
560
+ const blockedSteps = planData.steps.filter((step) => step.predicted_blockers.length > 0);
561
+ const statusSnapshot = await readStatusSnapshot(targetDir, tool, t);
562
+ const resumeCommand = `aioson workflow:execute ${targetDir} --feature=${slug} --tool=${tool}${requestedMode ? ` --mode=${requestedMode}` : ''}${maxCheckpoints !== 1 ? ` --max-checkpoints=${maxCheckpoints}` : ''}`;
563
+
564
+ if (dryRun) {
126
565
  const result = {
127
566
  ok: true,
128
567
  feature: slug,
129
568
  classification,
130
569
  tool,
570
+ requested_mode: requestedMode,
131
571
  dry_run: true,
132
- steps: plan,
572
+ resumed: seeded.resumed,
573
+ state_path: seeded.statePath,
574
+ execution_state_path: EXECUTION_STATE_RELATIVE_PATH,
575
+ max_checkpoints: maxCheckpoints,
576
+ steps: planData.steps,
133
577
  active_steps: activePlan.length,
134
- skipped_steps: skippedPlan.length
578
+ blocked_steps: blockedSteps.length,
579
+ gates: planData.gates,
580
+ status_snapshot: statusSnapshot,
581
+ suggestion: statusSnapshot && statusSnapshot.suggestion ? statusSnapshot.suggestion : null,
582
+ resume_command: resumeCommand,
583
+ parallel_guard: parallelGuard
135
584
  };
136
585
 
137
586
  if (options.json) return result;
@@ -139,103 +588,143 @@ async function runWorkflowExecute({ args, options = {}, logger }) {
139
588
  logger.log('');
140
589
  logger.log(`Workflow Execution Plan — ${slug} (${classification})`);
141
590
  logger.log(BAR);
142
-
143
- for (const step of plan) {
144
- const icon = step.skip ? '○ (skip)' : `Step ${step.step}:`;
145
- const optional = step.optional ? ' (optional)' : '';
146
- const gateInfo = step.gate_after ? ` → Gate ${step.gate_after} check` : '';
147
- const skipInfo = step.skip ? ` — ${step.skip_reason}` : '';
148
- logger.log(`${icon} @${step.agent}${optional} ${step.description}${gateInfo}${skipInfo}`);
591
+ for (const step of planData.steps) {
592
+ const gateInfo = step.gate_after ? ` → Gate ${step.gate_after}` : '';
593
+ const statusInfo = `[${step.status}]`;
594
+ const modeInfo = step.effective_mode ? ` [mode=${step.effective_mode}]` : '';
595
+ logger.log(`Step ${step.step}: @${step.agent} ${statusInfo} ${step.description}${gateInfo}${modeInfo}`);
596
+ for (const blocker of step.predicted_blockers) {
597
+ logger.log(` - blocker: ${blocker}`);
598
+ }
149
599
  }
150
-
151
600
  logger.log('');
152
- logger.log(`Gates enforced: ${plan.filter((s) => s.gate_after && !s.skip).map((s) => s.gate_after).join(', ') || 'none'}`);
153
- logger.log(`Active sessions: ${activePlan.length} | Skipped: ${skippedPlan.length}`);
154
- logger.log('');
155
- logger.log('Run without --dry-run to execute.');
601
+ logger.log(`Blocked steps: ${blockedSteps.length} | Remaining: ${activePlan.length}`);
602
+ logger.log(`Resume state: ${seeded.resumed ? 'existing workflow state reused' : 'new workflow state seeded'}`);
603
+ if (statusSnapshot && statusSnapshot.suggestion && statusSnapshot.suggestion.command) {
604
+ logger.log(`Suggested command: ${statusSnapshot.suggestion.command}`);
605
+ }
156
606
  logger.log('');
157
-
158
607
  return result;
159
608
  }
160
609
 
161
- // Execute
162
- logger.log('');
163
- logger.log(`Workflow Execution ${slug} (${classification})`);
164
- logger.log(BAR);
165
-
166
- let completed = 0;
167
- let failed = 0;
168
- const executionLog = [];
169
-
170
- for (const step of plan) {
171
- if (step.skip) {
172
- logger.log(`[skip] @${step.agent} — ${step.skip_reason}`);
173
- executionLog.push({ step: step.step, agent: step.agent, status: 'skipped', reason: step.skip_reason });
174
- continue;
610
+ const executionTransitions = [];
611
+ let activation = null;
612
+ let currentStatus = statusSnapshot;
613
+
614
+ for (let index = 0; index < maxCheckpoints; index += 1) {
615
+ const suggestion = currentStatus && currentStatus.suggestion ? currentStatus.suggestion : null;
616
+ let transitionResult;
617
+
618
+ try {
619
+ transitionResult = await performRunnerTransition(
620
+ targetDir,
621
+ suggestion,
622
+ tool,
623
+ requestedMode,
624
+ logger,
625
+ t
626
+ );
627
+ } catch (error) {
628
+ const failure = {
629
+ ok: false,
630
+ reason: 'workflow_next_failed',
631
+ feature: slug,
632
+ classification,
633
+ message: error.message,
634
+ transitions: executionTransitions
635
+ };
636
+ if (options.json) return failure;
637
+ logger.error(error.message);
638
+ return failure;
175
639
  }
176
640
 
177
- if (skipOptional && step.optional) {
178
- logger.log(`[skip] @${step.agent} optional, skipped`);
179
- executionLog.push({ step: step.step, agent: step.agent, status: 'skipped', reason: 'optional' });
180
- continue;
641
+ if (!transitionResult.ok) {
642
+ const failure = {
643
+ ok: false,
644
+ reason: transitionResult.reason || 'runner_transition_failed',
645
+ feature: slug,
646
+ classification,
647
+ transitions: executionTransitions
648
+ };
649
+ if (options.json) return failure;
650
+ logger.error(`Runner transition failed: ${failure.reason}`);
651
+ return failure;
181
652
  }
182
653
 
183
- // Gate check before
184
- if (step.gate_before) {
185
- logger.log(`[Gate ${step.gate_before} check] pre-step ${step.step}...`);
186
- const gateCmd = `aioson gate:check ${targetDir} --feature=${slug} --gate=${step.gate_before}`;
187
- const gateResult = runCommand(gateCmd);
188
- if (!gateResult.ok) {
189
- logger.log(`[Gate ${step.gate_before}] BLOCKED — cannot proceed with @${step.agent}`);
190
- executionLog.push({ step: step.step, agent: step.agent, status: 'blocked', gate: step.gate_before });
191
- failed++;
192
- break;
193
- }
194
- }
195
-
196
- logger.log(`[Step ${step.step}/${activePlan.length}] @${step.agent} — ${step.description}...`);
197
- const agentCmd = `aioson agent:prompt ${targetDir} --agent=${step.agent} --feature=${slug} --tool=${tool}`;
198
- const agentResult = runCommand(agentCmd);
199
-
200
- if (!agentResult.ok) {
201
- logger.log(`[Step ${step.step}] @${step.agent} FAILED`);
202
- executionLog.push({ step: step.step, agent: step.agent, status: 'failed' });
203
- failed++;
654
+ if (transitionResult.transition === 'complete' && !transitionResult.result) {
204
655
  break;
205
656
  }
206
657
 
207
- // Gate check after
208
- if (step.gate_after) {
209
- logger.log(`[Gate ${step.gate_after} check] post-step ${step.step}...`);
210
- }
658
+ activation = transitionResult.result || activation;
659
+ executionTransitions.push({
660
+ index: index + 1,
661
+ transition: transitionResult.transition,
662
+ agent: transitionResult.agent || null,
663
+ active_stage: activation && activation.agent ? activation.agent : null,
664
+ next_stage: activation && activation.next ? activation.next : null,
665
+ completed_stage: activation && activation.completedStage ? activation.completedStage : null
666
+ });
211
667
 
212
- // Pulse update
213
- const pulseCmd = `aioson pulse:update ${targetDir} --agent=${step.agent} --feature=${slug} --action="${step.description}" 2>/dev/null || true`;
214
- runCommand(pulseCmd);
668
+ currentStatus = await readStatusSnapshot(targetDir, tool, t);
215
669
 
216
- executionLog.push({ step: step.step, agent: step.agent, status: 'completed' });
217
- completed++;
218
- logger.log(`[Step ${step.step}] @${step.agent} ✓`);
219
- }
220
-
221
- const allDone = completed === activePlan.length;
222
- if (allDone) {
223
- logger.log('');
224
- logger.log(`Workflow complete: ${slug} → done`);
225
- logger.log(`Total sessions: ${completed}`);
226
- } else {
227
- logger.log('');
228
- logger.log(`Workflow stopped: ${completed} completed, ${failed} failed.`);
670
+ const nextSuggestion = currentStatus && currentStatus.suggestion ? currentStatus.suggestion : null;
671
+ if (!nextSuggestion || nextSuggestion.action === 'continue_stage' || nextSuggestion.action === 'workflow_complete') {
672
+ break;
673
+ }
229
674
  }
230
675
 
231
- return {
232
- ok: allDone,
676
+ const handoff = await readHandoff(targetDir);
677
+ const handoffProtocol = await readHandoffProtocol(targetDir);
678
+ const refreshedStatus = currentStatus || await readStatusSnapshot(targetDir, tool, t);
679
+ const executionState = await writeExecutionCheckpoint(targetDir, {
233
680
  feature: slug,
234
681
  classification,
235
- completed,
236
- failed,
237
- execution_log: executionLog
682
+ tool,
683
+ requestedMode,
684
+ resumed: seeded.resumed,
685
+ status: activation && activation.agent ? 'active' : 'completed',
686
+ checkpoint: buildCheckpointPayload(activation, handoff, handoffProtocol),
687
+ statusSnapshot: refreshedStatus,
688
+ suggestion: refreshedStatus && refreshedStatus.suggestion ? refreshedStatus.suggestion : null,
689
+ resumeCommand
690
+ });
691
+
692
+ const result = {
693
+ ok: true,
694
+ feature: slug,
695
+ classification,
696
+ tool,
697
+ requested_mode: requestedMode,
698
+ resumed: seeded.resumed,
699
+ state_path: seeded.statePath,
700
+ execution_state_path: EXECUTION_STATE_RELATIVE_PATH,
701
+ max_checkpoints: maxCheckpoints,
702
+ checkpoint: executionState.checkpoint,
703
+ execution_state: executionState,
704
+ status_snapshot: refreshedStatus,
705
+ suggestion: refreshedStatus && refreshedStatus.suggestion ? refreshedStatus.suggestion : null,
706
+ resume_command: resumeCommand,
707
+ transitions: executionTransitions,
708
+ active_stage: activation && activation.agent ? activation.agent : null,
709
+ next_stage: activation && activation.next ? activation.next : null,
710
+ handoff,
711
+ handoff_protocol: handoffProtocol,
712
+ parallel_guard: parallelGuard
238
713
  };
714
+
715
+ if (!options.json) {
716
+ logger.log('');
717
+ logger.log(`Workflow checkpoint stored at ${EXECUTION_STATE_RELATIVE_PATH}`);
718
+ logger.log(`Feature: ${slug}`);
719
+ logger.log(`Resumed: ${seeded.resumed ? 'yes' : 'no'}`);
720
+ }
721
+
722
+ return result;
239
723
  }
240
724
 
241
- module.exports = { runWorkflowExecute };
725
+ module.exports = {
726
+ EXECUTION_STATE_RELATIVE_PATH,
727
+ buildExecutionPlan,
728
+ seedFeatureWorkflowState,
729
+ runWorkflowExecute
730
+ };