sinapse-ai 1.16.0 → 1.19.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 (354) hide show
  1. package/.claude/hooks/doc-first-gate.cjs +156 -0
  2. package/.claude/hooks/enforce-story-gate.cjs +6 -2
  3. package/.claude/rules/documentation-first.md +6 -0
  4. package/.claude/rules/hook-governance.md +1 -0
  5. package/.sinapse-ai/constitution.md +3 -3
  6. package/.sinapse-ai/core/atlas/atlas-data.js +278 -0
  7. package/.sinapse-ai/core/atlas/flows-pt.js +253 -0
  8. package/.sinapse-ai/core/atlas/flows.js +266 -0
  9. package/.sinapse-ai/core/atlas/index.js +62 -0
  10. package/.sinapse-ai/core/atlas/render-html.js +216 -0
  11. package/.sinapse-ai/core/atlas/render-markdown.js +313 -0
  12. package/.sinapse-ai/core/atlas/render-research-card.js +164 -0
  13. package/.sinapse-ai/core/code-intel/registry-syncer.js +5 -3
  14. package/.sinapse-ai/core/doctor/checks/agent-memory.js +7 -5
  15. package/.sinapse-ai/core/doctor/checks/claude-md.js +13 -0
  16. package/.sinapse-ai/core/doctor/checks/constitution-consistency.js +1 -0
  17. package/.sinapse-ai/core/doctor/checks/hooks-claude-count.js +11 -0
  18. package/.sinapse-ai/core/doctor/checks/settings-json.js +16 -2
  19. package/.sinapse-ai/core/doctor/index.js +27 -0
  20. package/.sinapse-ai/core/execution/build-orchestrator.js +192 -8
  21. package/.sinapse-ai/core/external-executors/delegate-cli.js +6 -2
  22. package/.sinapse-ai/core/health-check/checks/project/constitution-consistency.js +2 -1
  23. package/.sinapse-ai/core/ideation/ideation-engine.js +10 -8
  24. package/.sinapse-ai/core/mcp/config-migrator.js +8 -5
  25. package/.sinapse-ai/core/mcp/global-config-manager.js +4 -1
  26. package/.sinapse-ai/core/mcp/symlink-manager.js +6 -3
  27. package/.sinapse-ai/core/orchestration/brownfield-handler.js +5 -4
  28. package/.sinapse-ai/core/orchestration/build-command.js +136 -0
  29. package/.sinapse-ai/core/orchestration/doc-first-resolver.js +322 -0
  30. package/.sinapse-ai/core/orchestration/executors/epic-4-executor.js +3 -0
  31. package/.sinapse-ai/core/orchestration/gate-evaluator.js +58 -9
  32. package/.sinapse-ai/core/orchestration/route-command.js +97 -0
  33. package/.sinapse-ai/core/orchestration/session-state.js +19 -2
  34. package/.sinapse-ai/core/orchestration/terminal-spawner.js +24 -1
  35. package/.sinapse-ai/core/orchestration/workflow-executor.js +102 -3
  36. package/.sinapse-ai/core/quality-gates/focus-area-recommender.js +15 -11
  37. package/.sinapse-ai/core/registry/build-registry.js +3 -2
  38. package/.sinapse-ai/core/synapse/scripts/generate-constitution.js +1 -0
  39. package/.sinapse-ai/core/synapse/utils/atomic-write.js +34 -9
  40. package/.sinapse-ai/core-config.yaml +5 -1
  41. package/.sinapse-ai/data/capability-detection.js +3 -3
  42. package/.sinapse-ai/data/entity-registry.yaml +288 -90
  43. package/.sinapse-ai/data/mcp-discipline.js +1 -1
  44. package/.sinapse-ai/data/mcp-tool-examples.yaml +8 -8
  45. package/.sinapse-ai/data/tok2-validation.js +1 -1
  46. package/.sinapse-ai/data/tool-registry.yaml +4 -3
  47. package/.sinapse-ai/development/agents/snps-orqx.md +14 -5
  48. package/.sinapse-ai/development/scripts/migrate-task-to-v2.js +1 -1
  49. package/.sinapse-ai/development/scripts/validate-task-v2.js +67 -4
  50. package/.sinapse-ai/development/tasks/advanced-elicitation.md +3 -3
  51. package/.sinapse-ai/development/tasks/analyst-facilitate-brainstorming.md +3 -3
  52. package/.sinapse-ai/development/tasks/analyze-framework.md +1 -8
  53. package/.sinapse-ai/development/tasks/analyze-performance.md +1 -8
  54. package/.sinapse-ai/development/tasks/apply-qa-fixes.md +3 -3
  55. package/.sinapse-ai/development/tasks/architect-analyze-impact.md +3 -3
  56. package/.sinapse-ai/development/tasks/audit-codebase.md +1 -8
  57. package/.sinapse-ai/development/tasks/audit-tailwind-config.md +1 -4
  58. package/.sinapse-ai/development/tasks/audit-utilities.md +1 -8
  59. package/.sinapse-ai/development/tasks/bootstrap-shadcn-library.md +3 -3
  60. package/.sinapse-ai/development/tasks/brownfield-create-epic.md +3 -3
  61. package/.sinapse-ai/development/tasks/brownfield-create-story.md +3 -3
  62. package/.sinapse-ai/development/tasks/build-component.md +2 -5
  63. package/.sinapse-ai/development/tasks/calculate-roi.md +3 -3
  64. package/.sinapse-ai/development/tasks/ci-cd-configuration.md +1 -1
  65. package/.sinapse-ai/development/tasks/cleanup-utilities.md +3 -3
  66. package/.sinapse-ai/development/tasks/collaborative-edit.md +3 -3
  67. package/.sinapse-ai/development/tasks/compose-molecule.md +2 -5
  68. package/.sinapse-ai/development/tasks/consolidate-patterns.md +3 -3
  69. package/.sinapse-ai/development/tasks/correct-course.md +3 -3
  70. package/.sinapse-ai/development/tasks/create-brownfield-story.md +2 -5
  71. package/.sinapse-ai/development/tasks/create-deep-research-prompt.md +2 -5
  72. package/.sinapse-ai/development/tasks/create-doc.md +3 -6
  73. package/.sinapse-ai/development/tasks/create-next-story.md +4 -8
  74. package/.sinapse-ai/development/tasks/create-suite.md +2 -5
  75. package/.sinapse-ai/development/tasks/create-task.md +2 -5
  76. package/.sinapse-ai/development/tasks/create-workflow.md +2 -5
  77. package/.sinapse-ai/development/tasks/db-analyze-hotpaths.md +1 -8
  78. package/.sinapse-ai/development/tasks/db-apply-migration.md +1 -8
  79. package/.sinapse-ai/development/tasks/db-bootstrap.md +1 -8
  80. package/.sinapse-ai/development/tasks/db-domain-modeling.md +1 -8
  81. package/.sinapse-ai/development/tasks/db-dry-run.md +1 -8
  82. package/.sinapse-ai/development/tasks/db-env-check.md +1 -8
  83. package/.sinapse-ai/development/tasks/db-explain.md +1 -8
  84. package/.sinapse-ai/development/tasks/db-impersonate.md +1 -8
  85. package/.sinapse-ai/development/tasks/db-load-csv.md +1 -8
  86. package/.sinapse-ai/development/tasks/db-policy-apply.md +1 -8
  87. package/.sinapse-ai/development/tasks/db-rls-audit.md +1 -8
  88. package/.sinapse-ai/development/tasks/db-rollback.md +1 -8
  89. package/.sinapse-ai/development/tasks/db-run-sql.md +1 -8
  90. package/.sinapse-ai/development/tasks/db-schema-audit.md +1 -8
  91. package/.sinapse-ai/development/tasks/db-seed.md +1 -8
  92. package/.sinapse-ai/development/tasks/db-smoke-test.md +1 -8
  93. package/.sinapse-ai/development/tasks/db-snapshot.md +1 -8
  94. package/.sinapse-ai/development/tasks/db-squad-integration.md +1 -8
  95. package/.sinapse-ai/development/tasks/db-supabase-setup.md +1 -8
  96. package/.sinapse-ai/development/tasks/db-verify-order.md +1 -8
  97. package/.sinapse-ai/development/tasks/deprecate-component.md +3 -3
  98. package/.sinapse-ai/development/tasks/dev-apply-qa-fixes.md +3 -3
  99. package/.sinapse-ai/development/tasks/dev-backlog-debt.md +3 -3
  100. package/.sinapse-ai/development/tasks/dev-develop-story.md +1 -1
  101. package/.sinapse-ai/development/tasks/dev-improve-code-quality.md +3 -3
  102. package/.sinapse-ai/development/tasks/dev-optimize-performance.md +3 -3
  103. package/.sinapse-ai/development/tasks/dev-suggest-refactoring.md +3 -3
  104. package/.sinapse-ai/development/tasks/dev-validate-next-story.md +3 -3
  105. package/.sinapse-ai/development/tasks/document-project.md +3 -3
  106. package/.sinapse-ai/development/tasks/environment-bootstrap.md +0 -4
  107. package/.sinapse-ai/development/tasks/execute-checklist.md +3 -3
  108. package/.sinapse-ai/development/tasks/export-design-tokens-dtcg.md +3 -3
  109. package/.sinapse-ai/development/tasks/extend-pattern.md +1 -8
  110. package/.sinapse-ai/development/tasks/extract-tokens.md +3 -3
  111. package/.sinapse-ai/development/tasks/facilitate-brainstorming-session.md +1 -1
  112. package/.sinapse-ai/development/tasks/generate-ai-frontend-prompt.md +2 -5
  113. package/.sinapse-ai/development/tasks/generate-documentation.md +2 -5
  114. package/.sinapse-ai/development/tasks/generate-migration-strategy.md +2 -5
  115. package/.sinapse-ai/development/tasks/generate-shock-report.md +2 -5
  116. package/.sinapse-ai/development/tasks/github-devops-github-pr-automation.md +3 -3
  117. package/.sinapse-ai/development/tasks/github-devops-repository-cleanup.md +3 -3
  118. package/.sinapse-ai/development/tasks/github-devops-version-management.md +3 -3
  119. package/.sinapse-ai/development/tasks/improve-self.md +1 -16
  120. package/.sinapse-ai/development/tasks/index-docs.md +1 -4
  121. package/.sinapse-ai/development/tasks/init-project-status.md +1 -7
  122. package/.sinapse-ai/development/tasks/integrate-squad.md +3 -3
  123. package/.sinapse-ai/development/tasks/kb-mode-interaction.md +3 -3
  124. package/.sinapse-ai/development/tasks/learn-patterns.md +3 -3
  125. package/.sinapse-ai/development/tasks/modify-agent.md +1 -8
  126. package/.sinapse-ai/development/tasks/modify-task.md +1 -8
  127. package/.sinapse-ai/development/tasks/modify-workflow.md +1 -8
  128. package/.sinapse-ai/development/tasks/po-backlog-add.md +3 -3
  129. package/.sinapse-ai/development/tasks/po-manage-story-backlog.md +3 -3
  130. package/.sinapse-ai/development/tasks/po-pull-story-from-clickup.md +3 -3
  131. package/.sinapse-ai/development/tasks/po-pull-story.md +3 -3
  132. package/.sinapse-ai/development/tasks/po-stories-index.md +3 -3
  133. package/.sinapse-ai/development/tasks/po-sync-story-to-clickup.md +3 -3
  134. package/.sinapse-ai/development/tasks/po-sync-story.md +3 -3
  135. package/.sinapse-ai/development/tasks/pr-automation.md +1 -1
  136. package/.sinapse-ai/development/tasks/propose-modification.md +3 -3
  137. package/.sinapse-ai/development/tasks/qa-backlog-add-followup.md +1 -8
  138. package/.sinapse-ai/development/tasks/qa-create-fix-request.md +1 -4
  139. package/.sinapse-ai/development/tasks/qa-gate.md +1 -8
  140. package/.sinapse-ai/development/tasks/qa-generate-tests.md +1 -8
  141. package/.sinapse-ai/development/tasks/qa-nfr-assess.md +1 -8
  142. package/.sinapse-ai/development/tasks/qa-review-proposal.md +1 -8
  143. package/.sinapse-ai/development/tasks/qa-review-story.md +1 -8
  144. package/.sinapse-ai/development/tasks/qa-risk-profile.md +1 -8
  145. package/.sinapse-ai/development/tasks/qa-run-tests.md +1 -8
  146. package/.sinapse-ai/development/tasks/qa-test-design.md +1 -8
  147. package/.sinapse-ai/development/tasks/qa-trace-requirements.md +1 -8
  148. package/.sinapse-ai/development/tasks/release-management.md +1 -1
  149. package/.sinapse-ai/development/tasks/security-audit.md +1 -4
  150. package/.sinapse-ai/development/tasks/security-scan.md +1 -4
  151. package/.sinapse-ai/development/tasks/setup-database.md +2 -11
  152. package/.sinapse-ai/development/tasks/setup-design-system.md +2 -11
  153. package/.sinapse-ai/development/tasks/shard-doc.md +3 -3
  154. package/.sinapse-ai/development/tasks/sm-create-next-story.md +3 -3
  155. package/.sinapse-ai/development/tasks/spec-gather-requirements.md +11 -0
  156. package/.sinapse-ai/development/tasks/sync-documentation.md +1 -6
  157. package/.sinapse-ai/development/tasks/tailwind-upgrade.md +3 -3
  158. package/.sinapse-ai/development/tasks/test-as-user.md +3 -3
  159. package/.sinapse-ai/development/tasks/undo-last.md +2 -5
  160. package/.sinapse-ai/development/tasks/update-manifest.md +1 -8
  161. package/.sinapse-ai/development/tasks/ux-create-wireframe.md +1 -1
  162. package/.sinapse-ai/development/tasks/ux-ds-scan-artifact.md +3 -3
  163. package/.sinapse-ai/development/tasks/ux-user-research.md +3 -3
  164. package/.sinapse-ai/development/tasks/validate-next-story.md +1 -8
  165. package/.sinapse-ai/development/templates/approval-table.md +106 -0
  166. package/.sinapse-ai/development/templates/chrome-brain/scripts/chrome-ensure.cjs +1 -1
  167. package/.sinapse-ai/development/workflows/spec-pipeline.yaml +1 -0
  168. package/.sinapse-ai/git-hooks/post-commit +0 -0
  169. package/.sinapse-ai/git-hooks/pre-commit +0 -0
  170. package/.sinapse-ai/git-hooks/pre-push +0 -0
  171. package/.sinapse-ai/infrastructure/scripts/changelog-generator.js +5 -1
  172. package/.sinapse-ai/infrastructure/scripts/pattern-extractor.js +11 -2
  173. package/.sinapse-ai/infrastructure/scripts/test-utilities.js +34 -13
  174. package/.sinapse-ai/infrastructure/scripts/usage-analytics.js +9 -5
  175. package/.sinapse-ai/infrastructure/scripts/validate-claude-integration.js +10 -2
  176. package/.sinapse-ai/install-manifest.yaml +364 -320
  177. package/.sinapse-ai/lib/build.json +1 -0
  178. package/.sinapse-ai/product/templates/personalized-task-template-v2.md +4 -2
  179. package/.sinapse-ai/product/templates/prd-tmpl.yaml +6 -2
  180. package/.sinapse-ai/product/templates/statusline/statusline-script.js +7 -3
  181. package/CHANGELOG.md +85 -7
  182. package/README.en.md +5 -5
  183. package/README.md +5 -5
  184. package/bin/cli.js +1 -1
  185. package/bin/commands/install.js +288 -114
  186. package/bin/commands/local.js +11 -0
  187. package/bin/lib/command-generator.js +31 -2
  188. package/bin/sinapse.js +121 -0
  189. package/docs/README.md +1 -1
  190. package/docs/agent-reference-guide.md +1 -1
  191. package/docs/community/README-community-snippet-core.md +1 -1
  192. package/docs/community/README-community-snippet-mcp.md +1 -1
  193. package/docs/community/README-community-snippet-squads.md +1 -1
  194. package/docs/examples/quickstart-recording.md +1 -1
  195. package/docs/framework/README.md +1 -1
  196. package/docs/framework/architecture-overview.md +1 -0
  197. package/docs/framework/atlas/OPERATING-ATLAS.md +537 -0
  198. package/docs/framework/atlas/README.md +34 -0
  199. package/docs/framework/atlas/atlas-data.json +2810 -0
  200. package/docs/framework/atlas/atlas.html +392 -0
  201. package/docs/framework/coding-standards.md +1 -1
  202. package/docs/framework/core-architecture.md +1 -1
  203. package/docs/framework/guiding-principles.md +1 -1
  204. package/docs/framework/source-tree.md +1 -1
  205. package/docs/framework/tech-stack.md +1 -1
  206. package/docs/framework/versioning-and-releases.md +1 -1
  207. package/docs/getting-started.md +1 -1
  208. package/docs/guides/agent-reference.md +1 -1
  209. package/docs/guides/agent-selection-guide.md +1 -1
  210. package/docs/guides/api-reference.md +1 -1
  211. package/docs/guides/contextual-greeting-system-guide.md +1 -1
  212. package/docs/guides/contributing-squads.md +1 -1
  213. package/docs/guides/development-setup.md +1 -1
  214. package/docs/guides/git-workflow-guide.md +1 -1
  215. package/docs/guides/installation-troubleshooting.md +1 -1
  216. package/docs/guides/llm-routing.md +1 -1
  217. package/docs/guides/mcp-global-setup.md +1 -1
  218. package/docs/guides/meta-agent-commands.md +1 -1
  219. package/docs/guides/project-status-feature.md +1 -1
  220. package/docs/guides/quality-dashboard.md +1 -1
  221. package/docs/guides/quality-gates.md +1 -1
  222. package/docs/guides/security-hardening.md +1 -1
  223. package/docs/guides/service-discovery.md +19 -22
  224. package/docs/guides/squad-examples/README.md +1 -1
  225. package/docs/guides/squad-migration.md +1 -1
  226. package/docs/guides/squads-guide.md +1 -1
  227. package/docs/guides/squads-overview.md +10 -13
  228. package/docs/guides/template-engine-v2.md +1 -1
  229. package/docs/guides/testing-guide.md +1 -1
  230. package/docs/guides/user-guide.md +30 -26
  231. package/docs/installation/README.md +3 -3
  232. package/docs/installation/faq.md +1 -1
  233. package/docs/installation/linux.md +2 -2
  234. package/docs/installation/macos.md +1 -1
  235. package/docs/installation/npx-install.md +1 -1
  236. package/docs/installation/troubleshooting.md +1 -1
  237. package/docs/installation/uninstallation.md +1 -1
  238. package/docs/installation/v4-quick-start.md +1 -1
  239. package/docs/installation/windows.md +2 -2
  240. package/docs/pt/CHANGELOG.md +1 -1
  241. package/docs/pt/DOCUMENTATION-ROADMAP.md +1 -1
  242. package/docs/pt/ENVIRONMENT.md +1 -1
  243. package/docs/pt/FEATURE_PROCESS.md +2 -2
  244. package/docs/pt/GUIDING-PRINCIPLES.md +2 -2
  245. package/docs/pt/agent-reference-guide.md +3 -3
  246. package/docs/pt/architecture/ARCHITECTURE-INDEX.md +2 -2
  247. package/docs/pt/architecture/ade-architecture.md +1 -1
  248. package/docs/pt/architecture/agent-config-audit.md +1 -1
  249. package/docs/pt/architecture/agent-responsibility-matrix.md +1 -1
  250. package/docs/pt/architecture/agent-tool-integration-guide.md +1 -1
  251. package/docs/pt/architecture/ci-cd.md +1 -1
  252. package/docs/pt/architecture/coding-standards.md +1 -1
  253. package/docs/pt/architecture/contribution-workflow-research.md +1 -1
  254. package/docs/pt/architecture/dashboard-architecture.md +1 -1
  255. package/docs/pt/architecture/dashboard-realtime.md +1 -1
  256. package/docs/pt/architecture/hcs-check-specifications.md +1 -1
  257. package/docs/pt/architecture/hcs-execution-modes.md +1 -1
  258. package/docs/pt/architecture/hcs-self-healing-spec.md +1 -1
  259. package/docs/pt/architecture/high-level-architecture.md +1 -1
  260. package/docs/pt/architecture/introduction.md +1 -1
  261. package/docs/pt/architecture/mcp-api-keys-management.md +3 -3
  262. package/docs/pt/architecture/mcp-system-diagrams.md +1 -1
  263. package/docs/pt/architecture/module-system.md +4 -4
  264. package/docs/pt/architecture/multi-repo-strategy.md +2 -2
  265. package/docs/pt/architecture/source-tree.md +1 -1
  266. package/docs/pt/architecture/squad-improvement-analysis.md +1 -1
  267. package/docs/pt/architecture/squad-improvement-recommended-approach.md +1 -1
  268. package/docs/pt/architecture/tech-stack.md +1 -1
  269. package/docs/pt/architecture/utility-integration-guide.md +1 -1
  270. package/docs/pt/code-of-conduct.md +1 -1
  271. package/docs/pt/community/README-community-snippet-core.md +1 -1
  272. package/docs/pt/community/README-community-snippet-mcp.md +2 -2
  273. package/docs/pt/contributing.md +7 -7
  274. package/docs/pt/core-architecture.md +1 -1
  275. package/docs/pt/docker-mcp-setup.md +1 -1
  276. package/docs/pt/framework/README.md +1 -1
  277. package/docs/pt/framework/coding-standards.md +1 -1
  278. package/docs/pt/framework/source-tree.md +1 -1
  279. package/docs/pt/framework/tech-stack.md +1 -1
  280. package/docs/pt/getting-started.md +1 -1
  281. package/docs/pt/git-workflow-guide.md +1 -1
  282. package/docs/pt/guides/README.md +3 -3
  283. package/docs/pt/guides/agent-selection-guide.md +2 -2
  284. package/docs/pt/guides/api-reference.md +1 -1
  285. package/docs/pt/guides/contextual-greeting-system-guide.md +1 -1
  286. package/docs/pt/guides/contributing-squads.md +1 -1
  287. package/docs/pt/guides/development-setup.md +1 -1
  288. package/docs/pt/guides/installation-troubleshooting.md +1 -1
  289. package/docs/pt/guides/llm-routing.md +1 -1
  290. package/docs/pt/guides/mcp/desktop-commander.md +1 -1
  291. package/docs/pt/guides/mcp/docker-gateway-tutorial.md +1 -1
  292. package/docs/pt/guides/mcp-global-setup.md +1 -1
  293. package/docs/pt/guides/project-status-feature.md +1 -1
  294. package/docs/pt/guides/quality-dashboard.md +1 -1
  295. package/docs/pt/guides/quality-gates.md +1 -1
  296. package/docs/pt/guides/security-hardening.md +2 -2
  297. package/docs/pt/guides/service-discovery.md +19 -22
  298. package/docs/pt/guides/squad-examples/README.md +1 -1
  299. package/docs/pt/guides/squad-migration.md +1 -1
  300. package/docs/pt/guides/squads-guide.md +1 -1
  301. package/docs/pt/guides/squads-overview.md +11 -13
  302. package/docs/pt/guides/template-engine-v2.md +1 -1
  303. package/docs/pt/guides/testing-guide.md +3 -3
  304. package/docs/pt/guides/user-guide.md +30 -26
  305. package/docs/pt/guides/workflows-guide.md +1 -1
  306. package/docs/pt/how-to-contribute-with-pull-requests.md +1 -1
  307. package/docs/pt/ide-integration.md +2 -2
  308. package/docs/pt/installation/README.md +1 -1
  309. package/docs/pt/installation/faq.md +1 -1
  310. package/docs/pt/installation/linux.md +1 -1
  311. package/docs/pt/installation/macos.md +1 -1
  312. package/docs/pt/installation/troubleshooting.md +1 -1
  313. package/docs/pt/installation/v4-quick-start.md +1 -1
  314. package/docs/pt/installation/windows.md +1 -1
  315. package/docs/pt/meta-agent-commands.md +1 -1
  316. package/docs/pt/migration-guide.md +1 -1
  317. package/docs/pt/npx-install.md +1 -1
  318. package/docs/pt/performance-tuning-guide.md +1 -1
  319. package/docs/pt/platforms/claude-code.md +2 -2
  320. package/docs/pt/roadmap.md +2 -2
  321. package/docs/pt/security-best-practices.md +2 -2
  322. package/docs/pt/security.md +1 -1
  323. package/docs/pt/sinapse-nomenclature-specification.md +1 -1
  324. package/docs/pt/troubleshooting.md +1 -1
  325. package/docs/pt/uninstallation.md +1 -1
  326. package/docs/pt/versioning-and-releases.md +1 -1
  327. package/docs/security/overview.md +1 -1
  328. package/docs/security/security-best-practices.md +2 -2
  329. package/docs/sinapse-workflows/README.md +1 -1
  330. package/package.json +2 -1
  331. package/packages/installer/package.json +1 -0
  332. package/packages/installer/src/wizard/feedback.js +18 -2
  333. package/packages/installer/src/wizard/i18n.js +78 -0
  334. package/packages/installer/src/wizard/ide-config-generator.js +14 -9
  335. package/packages/installer/src/wizard/index.js +16 -16
  336. package/packages/installer/src/wizard/validation/report-generator.js +22 -21
  337. package/packages/installer/src/wizard/validation/troubleshooting-system.js +19 -13
  338. package/scripts/gen-agent-badges.js +1 -1
  339. package/scripts/install-chrome-brain.sh +2 -2
  340. package/scripts/package-synapse.js +14 -5
  341. package/scripts/prepare-hooks.js +19 -0
  342. package/scripts/validate-agent-codenames.js +174 -0
  343. package/scripts/validate-all.js +1 -0
  344. package/scripts/validate-no-personal-leaks.js +24 -9
  345. package/sinapse/agents/sinapse-orqx.md +5 -5
  346. package/sinapse/agents/snps-orqx.md +5 -5
  347. package/squads/squad-brand/agents/brand-creative-engineer.md +5 -5
  348. package/squads/squad-brand/squad.yaml +6 -6
  349. package/squads/squad-content/agents/content-engineer.md +7 -7
  350. package/squads/squad-content/squad.yaml +5 -5
  351. package/squads/squad-copy/knowledge-base/consequence-headline-patterns.md +2 -2
  352. package/squads/squad-courses/agents/production-director.md +5 -5
  353. package/squads/squad-courses/squad.yaml +1 -1
  354. package/docs/guides/hooks-two-layers.md +0 -66
@@ -19,6 +19,7 @@ const {
19
19
  createGlobalConfig,
20
20
  } = require('./global-config-manager');
21
21
  const { getProjectMcpPath, checkLinkStatus, LINK_STATUS, createLink } = require('./symlink-manager');
22
+ const { retrySync } = require('../synapse/utils/atomic-write');
22
23
 
23
24
  /**
24
25
  * Migration options
@@ -267,7 +268,8 @@ function executeMigration(projectRoot = process.cwd(), option = MIGRATION_OPTION
267
268
  const backupPath = projectMcpPath + '.backup.' + Date.now();
268
269
 
269
270
  try {
270
- fs.renameSync(projectMcpPath, backupPath);
271
+ // retrySync: transient EPERM/EBUSY from AV / Search indexer on Windows.
272
+ retrySync(() => fs.renameSync(projectMcpPath, backupPath));
271
273
  results.backupPath = backupPath;
272
274
  } catch (error) {
273
275
  results.errors.push(`Backup failed: ${error.message}`);
@@ -315,14 +317,15 @@ function restoreFromBackup(backupPath, projectRoot = process.cwd()) {
315
317
  if (fs.existsSync(projectMcpPath)) {
316
318
  const stats = fs.lstatSync(projectMcpPath);
317
319
  if (stats.isSymbolicLink()) {
318
- fs.unlinkSync(projectMcpPath);
320
+ retrySync(() => fs.unlinkSync(projectMcpPath));
319
321
  } else {
320
- fs.rmSync(projectMcpPath, { recursive: true });
322
+ retrySync(() => fs.rmSync(projectMcpPath, { recursive: true }));
321
323
  }
322
324
  }
323
325
 
324
- // Restore backup
325
- fs.renameSync(backupPath, projectMcpPath);
326
+ // Restore backup — retry transient Windows locks so a legit rollback
327
+ // isn't lost to a momentary AV/indexer handle on the config dir.
328
+ retrySync(() => fs.renameSync(backupPath, projectMcpPath));
326
329
 
327
330
  return { success: true, restored: projectMcpPath };
328
331
  } catch (error) {
@@ -181,7 +181,10 @@ function readGlobalConfig() {
181
181
  }
182
182
 
183
183
  try {
184
- const content = fs.readFileSync(configPath, 'utf8');
184
+ // Strip a leading BOM: a global-config.json saved by a Windows editor
185
+ // (Notepad) starts with U+FEFF and would make JSON.parse throw, silently
186
+ // dropping the user's MCP global config via the catch below.
187
+ const content = fs.readFileSync(configPath, 'utf8').replace(/^\uFEFF/, '');
185
188
  return JSON.parse(content);
186
189
  } catch (error) {
187
190
  console.error(`Error reading global config: ${error.message}`);
@@ -13,6 +13,7 @@ const fs = require('fs');
13
13
  const path = require('path');
14
14
  const { execSync } = require('child_process');
15
15
  const { isWindows, getGlobalMcpDir, getLinkType } = require('./os-detector');
16
+ const { retrySync } = require('../synapse/utils/atomic-write');
16
17
 
17
18
  /**
18
19
  * Link status types
@@ -284,7 +285,9 @@ function createLink(projectRoot = process.cwd(), options = {}) {
284
285
  const configFile = path.join(linkPath, 'global-config.json');
285
286
  if (fs.existsSync(configFile)) {
286
287
  const backupPath = linkPath + '.backup.' + Date.now();
287
- fs.renameSync(linkPath, backupPath);
288
+ // retrySync: on Windows the rename of a dir held by AV / Search
289
+ // indexer / a concurrent reader throws transient EPERM/EBUSY.
290
+ retrySync(() => fs.renameSync(linkPath, backupPath));
288
291
  return {
289
292
  success: false,
290
293
  error: `Existing config backed up to ${backupPath}. Run with --migrate to merge configs.`,
@@ -293,10 +296,10 @@ function createLink(projectRoot = process.cwd(), options = {}) {
293
296
  backup: backupPath,
294
297
  };
295
298
  }
296
- fs.rmSync(linkPath, { recursive: true });
299
+ retrySync(() => fs.rmSync(linkPath, { recursive: true }));
297
300
  } else {
298
301
  // Remove symlink/junction
299
- fs.unlinkSync(linkPath);
302
+ retrySync(() => fs.unlinkSync(linkPath));
300
303
  }
301
304
  } catch (error) {
302
305
  return {
@@ -352,13 +352,14 @@ Quer que eu comece?`;
352
352
  // Record phase in session state
353
353
  await this._recordPhase(BrownfieldPhase.SYSTEM_DOCUMENTATION, context);
354
354
 
355
- // Execute workflow
355
+ // Hand the discovery workflow to the executor. executeWorkflow() loads and
356
+ // validates the workflow definition and returns a structured handoff; the
357
+ // per-phase documentation is produced by the agents the workflow assigns.
358
+ // Progress for those agent steps is tracked via _onPhaseStart/_onPhaseComplete/
359
+ // _onPhaseError + handlePhaseFailureAction as the agents report in.
356
360
  const result = await workflowExecutor.executeWorkflow(this.workflowPath, {
357
361
  projectRoot: this.projectRoot,
358
362
  techStack: context.techStack || {},
359
- onPhaseStart: (phase) => this._onPhaseStart(phase, context),
360
- onPhaseComplete: (phase, output) => this._onPhaseComplete(phase, output, context),
361
- onPhaseError: (phase, error) => this._onPhaseError(phase, error, context),
362
363
  });
363
364
 
364
365
  // Check if workflow completed successfully
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Build Command — executable sibling of `sinapse route`.
3
+ *
4
+ * `route()` SHOWS the deterministic doc-first decision for a briefing (type →
5
+ * workflow → required docs → gate). `build()` RUNS it: it drives the
6
+ * BobOrchestrator decision-tree engine (Projeto Bob) from a briefing in GUIDED
7
+ * mode — it detects project state, routes to the greenfield / brownfield /
8
+ * existing-project handler, and surfaces the next action/checkpoint instead of
9
+ * spawning agents headlessly.
10
+ *
11
+ * Congruence guarantee: both `route()` and `build()` start from
12
+ * `resolveDocFirstState`, so the conversation, the doc-first gate hook, `route`
13
+ * and `build` can never disagree about type/workflow/gate.
14
+ *
15
+ * The heavy orchestration index (locks, observability panel, status writer) is
16
+ * required LAZILY — `--dry-run` never constructs it, so it stays side-effect-free.
17
+ *
18
+ * @module core/orchestration/build-command
19
+ * @version 1.0.0
20
+ */
21
+
22
+ 'use strict';
23
+
24
+ const { resolveDocFirstState } = require('./doc-first-resolver');
25
+
26
+ let chalk;
27
+ try {
28
+ chalk = require('chalk');
29
+ } catch {
30
+ const id = (s) => s;
31
+ chalk = {
32
+ green: id, red: id, yellow: Object.assign(id, { bold: id }),
33
+ cyan: Object.assign(id, { bold: id }), gray: id, bold: id, dim: id,
34
+ };
35
+ }
36
+
37
+ /**
38
+ * Render the compact doc-first routing header (the "plan"), shared with `route`
39
+ * so `build` is visibly congruent with it.
40
+ * @param {string} brief
41
+ * @param {object} state - resolveDocFirstState() result
42
+ */
43
+ function printPlanHeader(brief, state) {
44
+ const ok = (b) => (b ? chalk.green('✓') : chalk.red('✗'));
45
+ const briefLabel = brief ? `"${brief}"` : '(no brief — using project state only)';
46
+
47
+ console.log(chalk.cyan('\n═══════════════════════════════════════════════════════════'));
48
+ console.log(chalk.cyan.bold(' 🛠️ SINAPSE Build — guided orchestration'));
49
+ console.log(chalk.cyan('═══════════════════════════════════════════════════════════\n'));
50
+
51
+ console.log(`Brief: ${chalk.gray(briefLabel)}`);
52
+ console.log(`Project type: ${chalk.bold(state.projectType)}`);
53
+ console.log(`Workflow: ${chalk.bold(state.workflow || 'SDC (no upstream re-doc)')}`);
54
+ console.log(
55
+ `Doc-first: ${state.gate.satisfied
56
+ ? chalk.green('✅ gate satisfied')
57
+ : chalk.red('🚫 gate blocked — missing: ' + state.gate.missing.join(', '))}\n`,
58
+ );
59
+ void ok;
60
+ }
61
+
62
+ /**
63
+ * Render the BobOrchestrator result in a guided, scannable way.
64
+ * @param {object} result - OrchestrationResult from BobOrchestrator.orchestrate
65
+ */
66
+ function printEngineResult(result) {
67
+ const r = result || {};
68
+ console.log(chalk.bold('Engine decision:'));
69
+ console.log(` Project state: ${chalk.bold(r.projectState || 'unknown')}`);
70
+ console.log(` Action: ${chalk.bold(r.action || 'n/a')}`);
71
+
72
+ const data = r.data || {};
73
+ const nextStep = r.nextStep || data.nextStep;
74
+ const message = data.formattedMessage || data.message || data.summary;
75
+ if (message) {
76
+ console.log('\n' + chalk.gray(String(message).trim()));
77
+ }
78
+ if (nextStep) {
79
+ console.log(`\n${chalk.cyan('▶ Next step:')} ${chalk.bold(nextStep)}`);
80
+ }
81
+
82
+ console.log('');
83
+ if (r.success) {
84
+ console.log(chalk.green.bold(' ✅ Orchestration step completed.\n'));
85
+ } else {
86
+ console.log(chalk.red.bold(` 🚫 Orchestration did not complete: ${r.error || r.action || 'unknown'}\n`));
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Execute the `build` command.
92
+ *
93
+ * @param {string} brief - User briefing (e.g. "criar um site pra X")
94
+ * @param {Object} [options]
95
+ * @param {string} [options.projectRoot] - Project root path
96
+ * @param {string} [options.type] - Explicit project_type override
97
+ * @param {boolean} [options.dryRun] - Show the plan + engine entry without running it
98
+ * @returns {Promise<{success:boolean, exitCode:number, state:object, result?:object}>}
99
+ */
100
+ async function build(brief, options = {}) {
101
+ const projectRoot = options.projectRoot || process.cwd();
102
+ const state = resolveDocFirstState({ projectRoot, brief, projectType: options.type });
103
+
104
+ printPlanHeader(brief, state);
105
+
106
+ if (options.dryRun) {
107
+ console.log(chalk.yellow.bold(' (dry-run) '), 'would run BobOrchestrator.orchestrate({ userGoal })');
108
+ console.log(
109
+ chalk.gray(' No engine constructed — no locks, no panel, no side effects.\n'),
110
+ );
111
+ return { success: true, exitCode: 0, state };
112
+ }
113
+
114
+ // Lazily require the heavy orchestration index ONLY for the real run.
115
+ let BobOrchestrator;
116
+ try {
117
+ ({ BobOrchestrator } = require('./index'));
118
+ } catch (error) {
119
+ console.log(chalk.red(` ✗ Could not load orchestration engine: ${error.message}\n`));
120
+ return { success: false, exitCode: 1, state };
121
+ }
122
+
123
+ const bob = new BobOrchestrator(projectRoot, { userGoal: brief });
124
+ const result = await bob.orchestrate({ userGoal: brief, storyPath: options.storyPath });
125
+
126
+ printEngineResult(result);
127
+
128
+ return {
129
+ success: !!(result && result.success),
130
+ exitCode: result && result.success === false ? 1 : 0,
131
+ state,
132
+ result,
133
+ };
134
+ }
135
+
136
+ module.exports = { build };
@@ -0,0 +1,322 @@
1
+ /**
2
+ * Doc-First Resolver — deterministic "what does this project need before code?"
3
+ *
4
+ * Single source of truth for the Documentation-First contract (Constitution
5
+ * Article III). Given a project root (and optionally a user brief), it answers,
6
+ * with NO LLM reasoning and NO side effects:
7
+ *
8
+ * - projectType : site | lp | app | platform | saas | api | service | feature | fix
9
+ * - workflow : which greenfield workflow applies (reuses greenfield-handler map)
10
+ * - artifacts : the upstream docs required before stories (brief/prd/spec/arch)
11
+ * - gate : is the minimal doc-first gate satisfied? (PRD + epic + a Ready story)
12
+ *
13
+ * Two consumers share this module so they never drift:
14
+ * 1. `sinapse route "<brief>"` — read-only observability of the routing decision.
15
+ * 2. The doc-first PreToolUse gate hook — blocks code writes when `gate.satisfied`
16
+ * is false. The module is intentionally dependency-light (fs/path + the
17
+ * greenfield-handler constants only) so it is safe and fast to require inside
18
+ * a hook. It NEVER constructs the heavy BobOrchestrator (locks, panels, etc.).
19
+ *
20
+ * @module core/orchestration/doc-first-resolver
21
+ * @version 1.0.0
22
+ */
23
+
24
+ 'use strict';
25
+
26
+ const fs = require('fs');
27
+ const path = require('path');
28
+
29
+ const {
30
+ GREENFIELD_WORKFLOW_BY_TYPE,
31
+ resolveGreenfieldWorkflow,
32
+ } = require('./greenfield-handler');
33
+
34
+ // ═══════════════════════════════════════════════════════════════════════════════════
35
+ // PROJECT TYPE CLASSIFICATION
36
+ // ═══════════════════════════════════════════════════════════════════════════════════
37
+
38
+ /**
39
+ * Keyword → project_type map. Mirrors the classification matrix in
40
+ * `.claude/rules/documentation-first.md` and `project-intelligence.md`.
41
+ * Order matters: more specific buckets are tested before broader ones.
42
+ * @constant
43
+ */
44
+ const TYPE_KEYWORDS = Object.freeze([
45
+ { type: 'fix', words: ['bug', 'conserta', 'corrige', 'corrigir', 'ajusta', 'ajustar', 'tweak', 'hotfix'] },
46
+ { type: 'refactor', words: ['refatora', 'refatorar', 'refactor', 'renomeia', 'renomear', 'limpa', 'cleanup'] },
47
+ { type: 'lp', words: ['landing page', 'landing', 'lp', 'sales page', 'captura', 'squeeze'] },
48
+ { type: 'saas', words: ['saas', 'software as a service', 'multi-tenant', 'multitenant'] },
49
+ { type: 'platform', words: ['plataforma', 'platform', 'dashboard', 'admin', 'portal', 'painel'] },
50
+ { type: 'app', words: ['app mobile', 'mobile', 'ios', 'android', 'react native', 'flutter', 'aplicativo'] },
51
+ { type: 'api', words: ['api', 'backend', 'microservice', 'microsserviço', 'rest', 'graphql'] },
52
+ { type: 'service', words: ['worker', 'cron', 'etl', 'integration', 'integração', 'automation', 'automação'] },
53
+ { type: 'site', words: ['site', 'website', 'web site', 'institucional', 'página', 'pagina'] },
54
+ { type: 'feature', words: ['feature', 'funcionalidade', 'implementa', 'implementar', 'adiciona'] },
55
+ ]);
56
+
57
+ /** Escape a string for safe use inside a RegExp. */
58
+ function escapeRegExp(s) {
59
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
60
+ }
61
+
62
+ /**
63
+ * Classify a brief string into a project_type. Returns `null` when the brief is
64
+ * empty or no keyword matches (caller decides the fallback). Matching is by whole
65
+ * word/phrase boundary (so "Asaas" does NOT match the keyword "saas").
66
+ * @param {string} [brief]
67
+ * @returns {string|null}
68
+ */
69
+ function classifyProjectType(brief) {
70
+ if (!brief || typeof brief !== 'string') return null;
71
+ const haystack = brief.toLowerCase();
72
+ for (const { type, words } of TYPE_KEYWORDS) {
73
+ if (words.some((w) => new RegExp(`\\b${escapeRegExp(w)}\\b`, 'i').test(haystack))) {
74
+ return type;
75
+ }
76
+ }
77
+ return null;
78
+ }
79
+
80
+ // ═══════════════════════════════════════════════════════════════════════════════════
81
+ // REQUIRED ARTIFACTS PER PROJECT TYPE
82
+ // ═══════════════════════════════════════════════════════════════════════════════════
83
+
84
+ /**
85
+ * Upstream artifacts required BEFORE the first story, keyed by greenfield
86
+ * workflow. Mirrors the "Required upstream artifacts per project type" table in
87
+ * `documentation-first.md`. `feature` / `fix` / `refactor` do not require the
88
+ * upstream re-doc chain (they ride on an existing project).
89
+ * @constant
90
+ */
91
+ const REQUIRED_ARTIFACTS_BY_WORKFLOW = Object.freeze({
92
+ 'greenfield-ui': [
93
+ { id: 'brief', path: 'docs/project-brief.md' },
94
+ { id: 'prd', path: 'docs/prd.md' },
95
+ { id: 'front-end-spec', path: 'docs/front-end-spec.md' },
96
+ { id: 'architecture', path: 'docs/front-end-architecture.md' },
97
+ ],
98
+ 'greenfield-fullstack': [
99
+ { id: 'brief', path: 'docs/project-brief.md' },
100
+ { id: 'prd', path: 'docs/prd.md' },
101
+ { id: 'front-end-spec', path: 'docs/front-end-spec.md' },
102
+ { id: 'architecture', path: 'docs/fullstack-architecture.md' },
103
+ ],
104
+ 'greenfield-service': [
105
+ { id: 'brief', path: 'docs/project-brief.md' },
106
+ { id: 'prd', path: 'docs/prd.md' },
107
+ { id: 'architecture', path: 'docs/service-architecture.md' },
108
+ ],
109
+ });
110
+
111
+ /** Project types that bypass the upstream greenfield doc-chain. */
112
+ const LIGHT_TYPES = Object.freeze(['feature', 'fix', 'refactor']);
113
+
114
+ // ═══════════════════════════════════════════════════════════════════════════════════
115
+ // PROJECT MATURITY
116
+ // ═══════════════════════════════════════════════════════════════════════════════════
117
+
118
+ /** Directories whose presence (with real files) signals existing application code. */
119
+ const CODE_DIRS = Object.freeze(['src', 'app', 'packages', 'components', 'pages', 'lib']);
120
+
121
+ /**
122
+ * Assess project maturity for gate scoping. Two signals matter:
123
+ *
124
+ * - isFrameworkRepo : this IS the SINAPSE framework's own repo (package.json
125
+ * name === 'sinapse-ai'). Framework development operates
126
+ * above the story layer (Constitution Art. III exception),
127
+ * so the doc-first gate must never block it.
128
+ * - isGreenfield : the project has no substantial application code yet — a
129
+ * brand-new project. The hard doc-first gate applies HERE
130
+ * (this is the "criar um site → straight to code" case).
131
+ * Existing projects defer to the lighter story-gate.
132
+ *
133
+ * @param {string} projectRoot
134
+ * @returns {{ isFrameworkRepo: boolean, isGreenfield: boolean }}
135
+ */
136
+ function assessProjectMaturity(projectRoot) {
137
+ let isFrameworkRepo = false;
138
+ try {
139
+ const pkg = JSON.parse(fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf8'));
140
+ if (pkg && pkg.name === 'sinapse-ai') isFrameworkRepo = true;
141
+ } catch {
142
+ // no package.json — fine
143
+ }
144
+
145
+ // Greenfield = no substantial application code yet. A lone package.json does
146
+ // NOT flip this off — otherwise scaffolding package.json first (an exempt
147
+ // file) would bypass the gate. Only populated code dirs count as "existing".
148
+ const hasCode = CODE_DIRS.some((d) => dirHasAnyFile(path.join(projectRoot, d)));
149
+ const isGreenfield = !hasCode;
150
+
151
+ return { isFrameworkRepo, isGreenfield };
152
+ }
153
+
154
+ // ═══════════════════════════════════════════════════════════════════════════════════
155
+ // MINIMAL DOC-FIRST GATE
156
+ // ═══════════════════════════════════════════════════════════════════════════════════
157
+
158
+ const VALID_STORY_STATUSES = Object.freeze(['ready', 'inprogress', 'inreview', 'done']);
159
+
160
+ /** True if a `docs/prd.md` (or sharded `docs/prd/`) exists and is non-trivial. */
161
+ function hasPrd(projectRoot) {
162
+ const file = path.join(projectRoot, 'docs', 'prd.md');
163
+ if (fileHasContent(file)) return true;
164
+ return dirHasMarkdown(path.join(projectRoot, 'docs', 'prd'));
165
+ }
166
+
167
+ /** True if at least one epic exists under `docs/epics/` or `docs/stories/epics/`. */
168
+ function hasEpic(projectRoot) {
169
+ return (
170
+ dirHasMarkdown(path.join(projectRoot, 'docs', 'epics')) ||
171
+ dirHasMarkdown(path.join(projectRoot, 'docs', 'stories', 'epics'))
172
+ );
173
+ }
174
+
175
+ /** True if at least one story under `docs/stories/` is at a >= Ready status. */
176
+ function hasReadyStory(projectRoot) {
177
+ const dir = path.join(projectRoot, 'docs', 'stories');
178
+ return walkMarkdown(dir).some((f) => storyStatusIsReady(f));
179
+ }
180
+
181
+ /**
182
+ * Parse a story file's frontmatter `status:` and decide whether it is >= Ready.
183
+ * Uses a bounded, non-greedy regex (the greedy `[\w\s]*` form historically
184
+ * captured the next line — see the story-gate memory).
185
+ * @param {string} file
186
+ * @returns {boolean}
187
+ */
188
+ function storyStatusIsReady(file) {
189
+ let content;
190
+ try {
191
+ content = fs.readFileSync(file, 'utf8');
192
+ } catch {
193
+ return false;
194
+ }
195
+ const m = content.match(/status:\s*["']?([a-z][a-z ]*?)["']?\s*(?:\r?\n|$)/i);
196
+ if (!m) return false;
197
+ const status = m[1].toLowerCase().replace(/\s+/g, '');
198
+ return VALID_STORY_STATUSES.includes(status);
199
+ }
200
+
201
+ // ═══════════════════════════════════════════════════════════════════════════════════
202
+ // MAIN RESOLVER
203
+ // ═══════════════════════════════════════════════════════════════════════════════════
204
+
205
+ /**
206
+ * Resolve the full doc-first picture for a project.
207
+ *
208
+ * @param {Object} [opts]
209
+ * @param {string} [opts.projectRoot=process.cwd()] - Project root.
210
+ * @param {string} [opts.brief] - User briefing (used to classify project type).
211
+ * @param {string} [opts.projectType] - Explicit type override (skips classification).
212
+ * @returns {{
213
+ * brief: string|null,
214
+ * projectType: string,
215
+ * classified: boolean,
216
+ * isLightType: boolean,
217
+ * workflow: string|null,
218
+ * artifacts: Array<{id:string, path:string, exists:boolean}>,
219
+ * gate: { prd:boolean, epic:boolean, readyStory:boolean, satisfied:boolean, missing:string[] }
220
+ * }}
221
+ */
222
+ function resolveDocFirstState(opts = {}) {
223
+ const projectRoot = opts.projectRoot || process.cwd();
224
+ const brief = opts.brief || null;
225
+ const classifiedType = opts.projectType || classifyProjectType(brief);
226
+ const projectType = classifiedType || 'site'; // safe default: assume a full project needs docs
227
+ const isLightType = LIGHT_TYPES.includes(projectType);
228
+
229
+ const workflow = isLightType ? null : resolveGreenfieldWorkflow(projectType);
230
+
231
+ const artifacts = (REQUIRED_ARTIFACTS_BY_WORKFLOW[workflow] || []).map((a) => ({
232
+ id: a.id,
233
+ path: a.path,
234
+ exists: fileHasContent(path.join(projectRoot, a.path)),
235
+ }));
236
+
237
+ const prd = hasPrd(projectRoot);
238
+ const epic = hasEpic(projectRoot);
239
+ const readyStory = hasReadyStory(projectRoot);
240
+
241
+ // Light types ride on an existing project: the minimal gate is "a Ready story"
242
+ // (the existing story-gate). Full projects additionally need PRD + epic.
243
+ const missing = [];
244
+ if (!readyStory) missing.push('story (status >= Ready)');
245
+ if (!isLightType) {
246
+ if (!prd) missing.push('PRD (docs/prd.md)');
247
+ if (!epic) missing.push('epic (docs/epics/)');
248
+ }
249
+ const satisfied = missing.length === 0;
250
+
251
+ return {
252
+ brief,
253
+ projectType,
254
+ classified: Boolean(classifiedType),
255
+ isLightType,
256
+ workflow,
257
+ artifacts,
258
+ gate: { prd, epic, readyStory, satisfied, missing },
259
+ maturity: assessProjectMaturity(projectRoot),
260
+ };
261
+ }
262
+
263
+ // ═══════════════════════════════════════════════════════════════════════════════════
264
+ // FS HELPERS (lean, no deps)
265
+ // ═══════════════════════════════════════════════════════════════════════════════════
266
+
267
+ function fileHasContent(file) {
268
+ try {
269
+ const stat = fs.statSync(file);
270
+ return stat.isFile() && stat.size > 0;
271
+ } catch {
272
+ return false;
273
+ }
274
+ }
275
+
276
+ function dirHasMarkdown(dir) {
277
+ try {
278
+ return fs.readdirSync(dir).some((f) => f.toLowerCase().endsWith('.md'));
279
+ } catch {
280
+ return false;
281
+ }
282
+ }
283
+
284
+ /** True if `dir` exists and contains at least one regular file (any depth-1 entry). */
285
+ function dirHasAnyFile(dir) {
286
+ try {
287
+ return fs.readdirSync(dir, { withFileTypes: true }).some((e) => e.isFile() || e.isDirectory());
288
+ } catch {
289
+ return false;
290
+ }
291
+ }
292
+
293
+ /** Recursively collect `.md` files under a dir (bounded depth, fail-safe). */
294
+ function walkMarkdown(dir, depth = 0) {
295
+ if (depth > 4) return [];
296
+ let entries;
297
+ try {
298
+ entries = fs.readdirSync(dir, { withFileTypes: true });
299
+ } catch {
300
+ return [];
301
+ }
302
+ const out = [];
303
+ for (const e of entries) {
304
+ const full = path.join(dir, e.name);
305
+ if (e.isDirectory()) {
306
+ out.push(...walkMarkdown(full, depth + 1));
307
+ } else if (e.name.toLowerCase().endsWith('.md')) {
308
+ out.push(full);
309
+ }
310
+ }
311
+ return out;
312
+ }
313
+
314
+ module.exports = {
315
+ classifyProjectType,
316
+ resolveDocFirstState,
317
+ assessProjectMaturity,
318
+ REQUIRED_ARTIFACTS_BY_WORKFLOW,
319
+ TYPE_KEYWORDS,
320
+ // re-exported for callers that want the raw map
321
+ GREENFIELD_WORKFLOW_BY_TYPE,
322
+ };
@@ -115,6 +115,9 @@ class Epic4Executor extends EpicExecutor {
115
115
  return this._completeExecution({
116
116
  implementationPath: planPath,
117
117
  planPath,
118
+ // F5 (epic: orchestration-consolidation): propagate the real plan object
119
+ // so the downstream epic4_to_epic6 gate can verify it is not degraded/stub.
120
+ plan: buildResult.plan,
118
121
  build: buildResult,
119
122
  reportPath: buildResult.reportPath,
120
123
  phases: buildResult.phases,
@@ -54,7 +54,9 @@ const DEFAULT_GATE_CONFIG = {
54
54
  blocking: true,
55
55
  requireTests: true,
56
56
  minTestCoverage: 0,
57
- checks: ['plan_complete', 'implementation_exists', 'no_critical_errors'],
57
+ // F5 (epic: orchestration-consolidation): `plan_is_real` gives this gate teeth —
58
+ // a degraded/stub/absent/subtask-less plan is BLOCKED, not waved through.
59
+ checks: ['plan_complete', 'plan_is_real', 'implementation_exists', 'no_critical_errors'],
58
60
  },
59
61
  };
60
62
 
@@ -148,11 +150,7 @@ class GateEvaluator {
148
150
  const checks = await this._runGateChecks(fromEpic, toEpic, epicResult, gateConfig);
149
151
  result.checks = checks;
150
152
 
151
- // Calculate score
152
- const passedChecks = checks.filter((c) => c.passed).length;
153
- result.score = checks.length > 0 ? (passedChecks / checks.length) * 5 : 5;
154
-
155
- // Collect issues
153
+ // Collect issues from failed checks
156
154
  result.issues = checks
157
155
  .filter((c) => !c.passed)
158
156
  .map((c) => ({
@@ -161,6 +159,22 @@ class GateEvaluator {
161
159
  severity: c.severity || 'medium',
162
160
  }));
163
161
 
162
+ // Calculate score — honesty invariant (F5, epic: orchestration-consolidation):
163
+ // a gate that evaluated ZERO checks verified NOTHING. It must NOT receive the
164
+ // max score (the old `: 5` was theater — an unconfigured gate always passed).
165
+ // Score 0 + an explicit issue so the verdict reflects "no real verification".
166
+ if (checks.length === 0) {
167
+ result.score = 0;
168
+ result.issues.push({
169
+ check: 'no_checks_evaluated',
170
+ message: 'Gate evaluated zero checks — nothing was actually verified',
171
+ severity: this.strictMode ? 'critical' : 'high',
172
+ });
173
+ } else {
174
+ const passedChecks = checks.filter((c) => c.passed).length;
175
+ result.score = (passedChecks / checks.length) * 5;
176
+ }
177
+
164
178
  // Determine verdict (AC2)
165
179
  result.verdict = this._determineVerdict(result, gateConfig);
166
180
 
@@ -279,11 +293,46 @@ class GateEvaluator {
279
293
  break;
280
294
 
281
295
  // Epic 4 checks
282
- case 'plan_complete':
283
- result.passed = !!epicResult.planPath || epicResult.planComplete === true;
296
+ case 'plan_complete': {
297
+ // F5: hardened but backward-compatible. When a real plan OBJECT is present
298
+ // (wired in via epic-4-executor), require real phases. When only a path/flag
299
+ // is present (legacy callers), keep the old signal so existing behavior holds.
300
+ const plan = epicResult.plan;
301
+ if (plan && typeof plan === 'object') {
302
+ result.passed = Array.isArray(plan.phases) && plan.phases.length > 0;
303
+ } else {
304
+ result.passed = !!epicResult.planPath || epicResult.planComplete === true;
305
+ }
284
306
  result.message = result.passed ? 'Implementation plan complete' : 'Plan not complete';
285
307
  result.severity = 'high';
286
308
  break;
309
+ }
310
+
311
+ case 'plan_is_real': {
312
+ // F5 (epic: orchestration-consolidation) — the gate's teeth. Honesty
313
+ // invariant: a degraded/stub/absent/subtask-less plan is NOT a real artifact
314
+ // and MUST block, not slip through. Critical severity → verdict BLOCKED.
315
+ const plan = epicResult.plan;
316
+ if (!plan || typeof plan !== 'object') {
317
+ result.passed = false;
318
+ result.message = 'No real plan object to inspect (plan absent)';
319
+ } else if (plan.degraded === true || plan.stub === true) {
320
+ result.passed = false;
321
+ result.message = `Plan is degraded/stub${plan.reason ? ` (${plan.reason})` : ''}`;
322
+ } else {
323
+ const phases = Array.isArray(plan.phases) ? plan.phases : [];
324
+ const subtaskCount = phases.reduce(
325
+ (n, p) => n + (Array.isArray(p.subtasks) ? p.subtasks.length : 0),
326
+ 0,
327
+ );
328
+ result.passed = phases.length > 0 && subtaskCount > 0;
329
+ result.message = result.passed
330
+ ? `Plan is real (${phases.length} phase(s), ${subtaskCount} subtask(s))`
331
+ : 'Plan has no subtasks';
332
+ }
333
+ result.severity = 'critical';
334
+ break;
335
+ }
287
336
 
288
337
  case 'implementation_exists':
289
338
  result.passed = !!epicResult.implementationPath || epicResult.codeChanges?.length > 0;
@@ -342,7 +391,7 @@ class GateEvaluator {
342
391
  case 3:
343
392
  return ['spec_exists', 'complexity_assessed'];
344
393
  case 4:
345
- return ['plan_complete', 'no_critical_errors'];
394
+ return ['plan_complete', 'plan_is_real', 'no_critical_errors'];
346
395
  case 6:
347
396
  return ['qa_report_exists', 'verdict_generated'];
348
397
  default: