mindforge-cc 10.0.3 → 11.0.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 (333) hide show
  1. package/.mindforge/MINDFORGE-V2-SCHEMA.json +43 -10
  2. package/.mindforge/config.json +30 -2
  3. package/.mindforge/engine/cross-model-eval.md +74 -0
  4. package/.mindforge/engine/proactive/signal-detector.md +60 -0
  5. package/.mindforge/engine/proactive/suggestion-engine.md +100 -0
  6. package/.mindforge/personas/agent-architect.md +57 -0
  7. package/.mindforge/personas/agent-evaluator.md +162 -0
  8. package/.mindforge/personas/agent-memory-designer.md +157 -0
  9. package/.mindforge/personas/agent-ops-engineer.md +120 -0
  10. package/.mindforge/personas/agent-orchestrator.md +112 -0
  11. package/.mindforge/personas/ai-economist.md +57 -0
  12. package/.mindforge/personas/ai-safety-engineer.md +57 -0
  13. package/.mindforge/personas/analytics-engineer.md +57 -0
  14. package/.mindforge/personas/anti-pattern-hunter.md +61 -0
  15. package/.mindforge/personas/api-gateway-designer.md +132 -0
  16. package/.mindforge/personas/auth-engineer.md +112 -0
  17. package/.mindforge/personas/build-engineer.md +57 -0
  18. package/.mindforge/personas/business-analyst.md +56 -0
  19. package/.mindforge/personas/cache-architect.md +100 -0
  20. package/.mindforge/personas/causal-scientist.md +57 -0
  21. package/.mindforge/personas/cdn-architect.md +118 -0
  22. package/.mindforge/personas/change-agent.md +104 -0
  23. package/.mindforge/personas/code-narrator.md +52 -0
  24. package/.mindforge/personas/codegen-specialist.md +68 -0
  25. package/.mindforge/personas/communication-architect.md +102 -0
  26. package/.mindforge/personas/compliance-engineer.md +96 -0
  27. package/.mindforge/personas/consensus-engineer.md +116 -0
  28. package/.mindforge/personas/contract-tester.md +60 -192
  29. package/.mindforge/personas/data-architect.md +108 -0
  30. package/.mindforge/personas/data-mesh-architect.md +57 -0
  31. package/.mindforge/personas/data-pipeline-architect.md +120 -0
  32. package/.mindforge/personas/de-sloppifier.md +60 -0
  33. package/.mindforge/personas/debt-manager.md +66 -0
  34. package/.mindforge/personas/decision-architect.md +82 -51
  35. package/.mindforge/personas/deployment-captain.md +74 -0
  36. package/.mindforge/personas/design-system-lead.md +112 -0
  37. package/.mindforge/personas/dmux-orchestrator.md +75 -0
  38. package/.mindforge/personas/dx-engineer.md +96 -0
  39. package/.mindforge/personas/ecommerce-engineer.md +57 -0
  40. package/.mindforge/personas/edge-engineer.md +94 -0
  41. package/.mindforge/personas/edtech-architect.md +106 -0
  42. package/.mindforge/personas/embedding-architect.md +57 -0
  43. package/.mindforge/personas/environment-engineer.md +57 -0
  44. package/.mindforge/personas/eval-judge.md +55 -0
  45. package/.mindforge/personas/event-architect.md +102 -0
  46. package/.mindforge/personas/experiment-designer.md +138 -0
  47. package/.mindforge/personas/feature-store-engineer.md +57 -0
  48. package/.mindforge/personas/finops-analyst.md +66 -0
  49. package/.mindforge/personas/fintech-architect.md +57 -0
  50. package/.mindforge/personas/flutter-engineer.md +104 -0
  51. package/.mindforge/personas/gaming-engineer.md +57 -0
  52. package/.mindforge/personas/graphql-designer.md +73 -0
  53. package/.mindforge/personas/healthcare-engineer.md +57 -0
  54. package/.mindforge/personas/hiring-strategist.md +105 -0
  55. package/.mindforge/personas/hitl-architect.md +165 -0
  56. package/.mindforge/personas/i18n-architect.md +69 -0
  57. package/.mindforge/personas/iot-architect.md +105 -0
  58. package/.mindforge/personas/knowledge-curator.md +139 -0
  59. package/.mindforge/personas/knowledge-engineer.md +57 -0
  60. package/.mindforge/personas/lakehouse-architect.md +57 -0
  61. package/.mindforge/personas/llm-orchestrator.md +57 -0
  62. package/.mindforge/personas/logistics-architect.md +106 -0
  63. package/.mindforge/personas/market-analyst.md +53 -0
  64. package/.mindforge/personas/marketplace-engineer.md +105 -0
  65. package/.mindforge/personas/mcp-designer.md +54 -0
  66. package/.mindforge/personas/meeting-designer.md +104 -0
  67. package/.mindforge/personas/mentorship-lead.md +106 -0
  68. package/.mindforge/personas/migration-architect.md +57 -0
  69. package/.mindforge/personas/ml-ops-engineer.md +101 -0
  70. package/.mindforge/personas/mobile-architect.md +105 -0
  71. package/.mindforge/personas/mobile-security-engineer.md +106 -0
  72. package/.mindforge/personas/multi-tenancy-architect.md +71 -0
  73. package/.mindforge/personas/multimodal-engineer.md +57 -0
  74. package/.mindforge/personas/offline-specialist.md +105 -0
  75. package/.mindforge/personas/onboarding-navigator.md +63 -0
  76. package/.mindforge/personas/payments-engineer.md +135 -0
  77. package/.mindforge/personas/pipeline-engineer.md +115 -0
  78. package/.mindforge/personas/platform-engineer.md +97 -0
  79. package/.mindforge/personas/platform-lead.md +57 -0
  80. package/.mindforge/personas/privacy-engineer.md +57 -0
  81. package/.mindforge/personas/product-owner.md +56 -0
  82. package/.mindforge/personas/productivity-analyst.md +57 -0
  83. package/.mindforge/personas/prompt-architect.md +101 -0
  84. package/.mindforge/personas/proofreader.md +53 -0
  85. package/.mindforge/personas/pwa-architect.md +105 -0
  86. package/.mindforge/personas/quality-scorer.md +63 -0
  87. package/.mindforge/personas/react-native-engineer.md +106 -0
  88. package/.mindforge/personas/resilience-engineer.md +69 -0
  89. package/.mindforge/personas/rfc-architect.md +64 -0
  90. package/.mindforge/personas/saga-orchestrator.md +80 -0
  91. package/.mindforge/personas/secrets-engineer.md +57 -0
  92. package/.mindforge/personas/skill-smith.md +79 -0
  93. package/.mindforge/personas/sre-lead.md +107 -0
  94. package/.mindforge/personas/stream-engineer.md +57 -0
  95. package/.mindforge/personas/streaming-engineer.md +64 -0
  96. package/.mindforge/personas/swarm-templates.json +674 -44
  97. package/.mindforge/personas/system-designer.md +57 -0
  98. package/.mindforge/personas/team-coach.md +120 -0
  99. package/.mindforge/personas/tech-lead-coach.md +103 -0
  100. package/.mindforge/personas/technical-writer-lead.md +111 -0
  101. package/.mindforge/personas/vibe-checker.md +75 -0
  102. package/.mindforge/personas/worktree-manager.md +56 -0
  103. package/.mindforge/personas/zero-trust-engineer.md +113 -0
  104. package/.mindforge/skills/a11y-testing/SKILL.md +143 -0
  105. package/.mindforge/skills/agent-evaluation-framework/SKILL.md +227 -0
  106. package/.mindforge/skills/agent-memory-design/SKILL.md +199 -0
  107. package/.mindforge/skills/agent-orchestration-patterns/SKILL.md +129 -0
  108. package/.mindforge/skills/agent-tool-selection/SKILL.md +204 -0
  109. package/.mindforge/skills/ai-agent-deployment/SKILL.md +176 -0
  110. package/.mindforge/skills/ai-cost-management/SKILL.md +57 -0
  111. package/.mindforge/skills/ai-safety-alignment/SKILL.md +53 -0
  112. package/.mindforge/skills/analytics-instrumentation/SKILL.md +172 -0
  113. package/.mindforge/skills/api-gateway-patterns/SKILL.md +177 -0
  114. package/.mindforge/skills/api-marketplace/SKILL.md +56 -0
  115. package/.mindforge/skills/api-versioning/SKILL.md +100 -0
  116. package/.mindforge/skills/app-store-deployment/SKILL.md +44 -0
  117. package/.mindforge/skills/architecture-tradeoff-analysis/SKILL.md +97 -0
  118. package/.mindforge/skills/audit-logging/SKILL.md +140 -0
  119. package/.mindforge/skills/auth-patterns/SKILL.md +148 -0
  120. package/.mindforge/skills/autonomous-agent-harness/SKILL.md +218 -0
  121. package/.mindforge/skills/autonomous-agents/SKILL.md +59 -0
  122. package/.mindforge/skills/build-system-optimization/SKILL.md +54 -0
  123. package/.mindforge/skills/build-vs-buy/SKILL.md +80 -0
  124. package/.mindforge/skills/bundle-optimization/SKILL.md +174 -0
  125. package/.mindforge/skills/business-analyst/SKILL.md +82 -0
  126. package/.mindforge/skills/caching-strategies/SKILL.md +132 -0
  127. package/.mindforge/skills/capacity-planning/SKILL.md +96 -0
  128. package/.mindforge/skills/causal-inference/SKILL.md +42 -0
  129. package/.mindforge/skills/cdn-optimization/SKILL.md +212 -0
  130. package/.mindforge/skills/change-management/SKILL.md +106 -0
  131. package/.mindforge/skills/chaos-engineering/SKILL.md +99 -0
  132. package/.mindforge/skills/ci-cd-pipeline/SKILL.md +118 -0
  133. package/.mindforge/skills/cli-design/SKILL.md +118 -0
  134. package/.mindforge/skills/code-generation-patterns/SKILL.md +92 -0
  135. package/.mindforge/skills/code-review-methodology/SKILL.md +180 -0
  136. package/.mindforge/skills/code-tour/SKILL.md +145 -0
  137. package/.mindforge/skills/codebase-onboarding/SKILL.md +95 -0
  138. package/.mindforge/skills/compliance-as-code/SKILL.md +195 -0
  139. package/.mindforge/skills/conflict-resolution/SKILL.md +87 -0
  140. package/.mindforge/skills/connection-pooling/SKILL.md +151 -0
  141. package/.mindforge/skills/container-security/SKILL.md +151 -0
  142. package/.mindforge/skills/context-engineering/SKILL.md +114 -0
  143. package/.mindforge/skills/contract-testing/SKILL.md +85 -0
  144. package/.mindforge/skills/cost-estimation/SKILL.md +82 -0
  145. package/.mindforge/skills/cqrs-event-sourcing/SKILL.md +95 -0
  146. package/.mindforge/skills/cross-platform-testing/SKILL.md +43 -0
  147. package/.mindforge/skills/data-governance/SKILL.md +42 -0
  148. package/.mindforge/skills/data-lakehouse/SKILL.md +42 -0
  149. package/.mindforge/skills/data-mesh/SKILL.md +42 -0
  150. package/.mindforge/skills/data-modeling/SKILL.md +107 -0
  151. package/.mindforge/skills/data-pipeline-design/SKILL.md +171 -0
  152. package/.mindforge/skills/data-privacy-engineering/SKILL.md +42 -0
  153. package/.mindforge/skills/database-performance/SKILL.md +174 -0
  154. package/.mindforge/skills/database-sharding-advanced/SKILL.md +206 -0
  155. package/.mindforge/skills/de-sloppify/SKILL.md +120 -0
  156. package/.mindforge/skills/defense-in-depth/SKILL.md +84 -0
  157. package/.mindforge/skills/delegation-patterns/SKILL.md +123 -0
  158. package/.mindforge/skills/dependency-management/SKILL.md +94 -0
  159. package/.mindforge/skills/deployment-workflow/SKILL.md +135 -0
  160. package/.mindforge/skills/design-system/SKILL.md +113 -0
  161. package/.mindforge/skills/developer-onboarding/SKILL.md +99 -0
  162. package/.mindforge/skills/developer-productivity-metrics/SKILL.md +59 -0
  163. package/.mindforge/skills/distributed-consensus/SKILL.md +141 -0
  164. package/.mindforge/skills/dmux-workflows/SKILL.md +141 -0
  165. package/.mindforge/skills/dns-architecture/SKILL.md +167 -0
  166. package/.mindforge/skills/ecommerce-architecture/SKILL.md +41 -0
  167. package/.mindforge/skills/edge-computing/SKILL.md +91 -0
  168. package/.mindforge/skills/edtech-platform/SKILL.md +41 -0
  169. package/.mindforge/skills/email-deliverability/SKILL.md +177 -0
  170. package/.mindforge/skills/embedding-systems/SKILL.md +55 -0
  171. package/.mindforge/skills/environment-management/SKILL.md +54 -0
  172. package/.mindforge/skills/error-handling-architecture/SKILL.md +118 -0
  173. package/.mindforge/skills/estimation-techniques/SKILL.md +113 -0
  174. package/.mindforge/skills/eval-harness/SKILL.md +180 -0
  175. package/.mindforge/skills/event-driven-architecture/SKILL.md +162 -0
  176. package/.mindforge/skills/experiment-design/SKILL.md +139 -0
  177. package/.mindforge/skills/experiment-platform/SKILL.md +43 -0
  178. package/.mindforge/skills/feature-engineering/SKILL.md +42 -0
  179. package/.mindforge/skills/feature-flag-management/SKILL.md +183 -0
  180. package/.mindforge/skills/fine-tuning-workflow/SKILL.md +189 -0
  181. package/.mindforge/skills/fintech-patterns/SKILL.md +41 -0
  182. package/.mindforge/skills/flutter-architecture/SKILL.md +42 -0
  183. package/.mindforge/skills/gaming-backend/SKILL.md +41 -0
  184. package/.mindforge/skills/git-workflow-design/SKILL.md +129 -0
  185. package/.mindforge/skills/graceful-degradation/SKILL.md +95 -0
  186. package/.mindforge/skills/graphql-patterns/SKILL.md +243 -0
  187. package/.mindforge/skills/guardrails-and-safety/SKILL.md +137 -0
  188. package/.mindforge/skills/healthcare-systems/SKILL.md +40 -0
  189. package/.mindforge/skills/hiring-engineering/SKILL.md +119 -0
  190. package/.mindforge/skills/human-in-the-loop-design/SKILL.md +234 -0
  191. package/.mindforge/skills/i18n-architecture/SKILL.md +147 -0
  192. package/.mindforge/skills/idempotency-patterns/SKILL.md +84 -0
  193. package/.mindforge/skills/incident-communication/SKILL.md +96 -0
  194. package/.mindforge/skills/incident-management/SKILL.md +97 -0
  195. package/.mindforge/skills/infrastructure-as-code/SKILL.md +98 -0
  196. package/.mindforge/skills/instinct-clustering/SKILL.md +190 -0
  197. package/.mindforge/skills/internal-developer-platform/SKILL.md +51 -0
  198. package/.mindforge/skills/iot-platform/SKILL.md +41 -0
  199. package/.mindforge/skills/k8s-deployment/SKILL.md +358 -0
  200. package/.mindforge/skills/knowledge-graphs/SKILL.md +56 -0
  201. package/.mindforge/skills/knowledge-sharing-systems/SKILL.md +112 -0
  202. package/.mindforge/skills/llm-cost-optimization/SKILL.md +198 -0
  203. package/.mindforge/skills/llm-orchestration/SKILL.md +56 -0
  204. package/.mindforge/skills/load-testing/SKILL.md +84 -0
  205. package/.mindforge/skills/logistics-optimization/SKILL.md +40 -0
  206. package/.mindforge/skills/market-researcher/SKILL.md +99 -0
  207. package/.mindforge/skills/marketplace-trust/SKILL.md +40 -0
  208. package/.mindforge/skills/mcp-server-patterns/SKILL.md +264 -0
  209. package/.mindforge/skills/media-streaming/SKILL.md +41 -0
  210. package/.mindforge/skills/meeting-architecture/SKILL.md +146 -0
  211. package/.mindforge/skills/mentoring-patterns/SKILL.md +77 -0
  212. package/.mindforge/skills/microservices-patterns/SKILL.md +83 -0
  213. package/.mindforge/skills/migration-platform/SKILL.md +61 -0
  214. package/.mindforge/skills/migration-strategies/SKILL.md +129 -0
  215. package/.mindforge/skills/ml-feature-store/SKILL.md +56 -0
  216. package/.mindforge/skills/ml-monitoring/SKILL.md +42 -0
  217. package/.mindforge/skills/mobile-performance/SKILL.md +44 -0
  218. package/.mindforge/skills/mobile-security/SKILL.md +45 -0
  219. package/.mindforge/skills/model-evaluation/SKILL.md +53 -0
  220. package/.mindforge/skills/monorepo-management/SKILL.md +100 -0
  221. package/.mindforge/skills/multi-tenancy-patterns/SKILL.md +145 -0
  222. package/.mindforge/skills/multi-turn-conversation-design/SKILL.md +206 -0
  223. package/.mindforge/skills/multimodal-ai/SKILL.md +51 -0
  224. package/.mindforge/skills/mutation-testing/SKILL.md +97 -0
  225. package/.mindforge/skills/notification-system-design/SKILL.md +168 -0
  226. package/.mindforge/skills/observability-stack/SKILL.md +136 -0
  227. package/.mindforge/skills/offline-first-design/SKILL.md +43 -0
  228. package/.mindforge/skills/on-call-design/SKILL.md +111 -0
  229. package/.mindforge/skills/pagination-patterns/SKILL.md +230 -0
  230. package/.mindforge/skills/payment-integration/SKILL.md +176 -0
  231. package/.mindforge/skills/performance-reviews/SKILL.md +140 -0
  232. package/.mindforge/skills/platform-observability/SKILL.md +58 -0
  233. package/.mindforge/skills/platform-reliability/SKILL.md +52 -0
  234. package/.mindforge/skills/post-incident-learning/SKILL.md +96 -0
  235. package/.mindforge/skills/product-manager/SKILL.md +104 -0
  236. package/.mindforge/skills/progressive-web-app/SKILL.md +44 -0
  237. package/.mindforge/skills/prompt-engineering/SKILL.md +94 -0
  238. package/.mindforge/skills/proofreader/SKILL.md +158 -0
  239. package/.mindforge/skills/push-notification-architecture/SKILL.md +45 -0
  240. package/.mindforge/skills/python-performance/SKILL.md +183 -0
  241. package/.mindforge/skills/quality-audit/SKILL.md +171 -0
  242. package/.mindforge/skills/queue-design/SKILL.md +85 -0
  243. package/.mindforge/skills/rag-architecture/SKILL.md +176 -0
  244. package/.mindforge/skills/rate-limiting-design/SKILL.md +94 -0
  245. package/.mindforge/skills/react-native-patterns/SKILL.md +42 -0
  246. package/.mindforge/skills/react-performance/SKILL.md +229 -0
  247. package/.mindforge/skills/real-time-analytics/SKILL.md +42 -0
  248. package/.mindforge/skills/real-time-sync/SKILL.md +83 -0
  249. package/.mindforge/skills/responsive-native/SKILL.md +44 -0
  250. package/.mindforge/skills/responsive-patterns/SKILL.md +141 -0
  251. package/.mindforge/skills/rfc-pipeline/SKILL.md +114 -0
  252. package/.mindforge/skills/saas-multi-tenant/SKILL.md +41 -0
  253. package/.mindforge/skills/santa-method/SKILL.md +134 -0
  254. package/.mindforge/skills/search-implementation/SKILL.md +98 -0
  255. package/.mindforge/skills/secrets-platform/SKILL.md +56 -0
  256. package/.mindforge/skills/secrets-rotation/SKILL.md +173 -0
  257. package/.mindforge/skills/self-serve-infrastructure/SKILL.md +51 -0
  258. package/.mindforge/skills/serverless-patterns/SKILL.md +119 -0
  259. package/.mindforge/skills/skill-creator-meta/SKILL.md +146 -0
  260. package/.mindforge/skills/sprint-retrospective-facilitation/SKILL.md +112 -0
  261. package/.mindforge/skills/stakeholder-communication/SKILL.md +85 -0
  262. package/.mindforge/skills/state-management/SKILL.md +104 -0
  263. package/.mindforge/skills/stream-processing/SKILL.md +43 -0
  264. package/.mindforge/skills/streaming-architecture/SKILL.md +81 -0
  265. package/.mindforge/skills/supply-chain-security/SKILL.md +145 -0
  266. package/.mindforge/skills/synthetic-data-generation/SKILL.md +52 -0
  267. package/.mindforge/skills/system-design/SKILL.md +88 -0
  268. package/.mindforge/skills/team-topology-design/SKILL.md +107 -0
  269. package/.mindforge/skills/technical-debt-management/SKILL.md +86 -0
  270. package/.mindforge/skills/technical-interview-design/SKILL.md +98 -0
  271. package/.mindforge/skills/technical-leadership/SKILL.md +75 -0
  272. package/.mindforge/skills/technical-writing/SKILL.md +237 -0
  273. package/.mindforge/skills/technology-radar/SKILL.md +88 -0
  274. package/.mindforge/skills/testing-anti-patterns/SKILL.md +288 -0
  275. package/.mindforge/skills/tool-design/SKILL.md +138 -0
  276. package/.mindforge/skills/typescript-advanced/SKILL.md +198 -0
  277. package/.mindforge/skills/using-git-worktrees/SKILL.md +139 -0
  278. package/.mindforge/skills/verification-loop/SKILL.md +13 -1
  279. package/.mindforge/skills/vibe-security/SKILL.md +165 -0
  280. package/.mindforge/skills/visual-regression-testing/SKILL.md +97 -0
  281. package/.mindforge/skills/websocket-patterns/SKILL.md +203 -0
  282. package/.mindforge/skills/writing-plans/SKILL.md +170 -0
  283. package/.mindforge/skills/writing-skills/SKILL.md +216 -0
  284. package/.mindforge/skills/zero-trust-architecture/SKILL.md +166 -0
  285. package/CHANGELOG.md +240 -0
  286. package/MINDFORGE.md +4 -4
  287. package/README.md +49 -4
  288. package/RELEASENOTES.md +80 -0
  289. package/SECURITY.md +20 -8
  290. package/bin/autonomous/audit-writer.js +13 -0
  291. package/bin/autonomous/auto-runner.js +74 -16
  292. package/bin/autonomous/context-refactorer.js +26 -11
  293. package/bin/autonomous/state-manager.js +62 -6
  294. package/bin/autonomous/stuck-monitor.js +46 -7
  295. package/bin/autonomous/wave-executor.js +66 -25
  296. package/bin/dashboard/api-router.js +43 -0
  297. package/bin/dashboard/metrics-aggregator.js +28 -1
  298. package/bin/dashboard/server.js +67 -4
  299. package/bin/dashboard/sse-bridge.js +4 -4
  300. package/bin/engine/feedback-loop.js +8 -0
  301. package/bin/engine/intelligence-interlock.js +32 -15
  302. package/bin/engine/logic-drift-detector.js +2 -1
  303. package/bin/engine/nexus-tracer.js +3 -2
  304. package/bin/engine/remediation-engine.js +155 -32
  305. package/bin/engine/self-corrective-synthesizer.js +84 -10
  306. package/bin/engine/sre-manager.js +12 -4
  307. package/bin/engine/temporal-hub.js +131 -34
  308. package/bin/governance/approve.js +41 -5
  309. package/bin/governance/impact-analyzer.js +28 -0
  310. package/bin/governance/policy-engine.js +10 -3
  311. package/bin/governance/quantum-crypto.js +32 -19
  312. package/bin/governance/rbac-manager.js +74 -2
  313. package/bin/governance/ztai-manager.js +49 -7
  314. package/bin/hindsight-injector.js +3 -3
  315. package/bin/memory/eis-client.js +71 -34
  316. package/bin/memory/embedding-engine.js +61 -0
  317. package/bin/memory/knowledge-graph.js +58 -5
  318. package/bin/memory/knowledge-indexer.js +53 -6
  319. package/bin/memory/knowledge-store.js +22 -0
  320. package/bin/migrations/10.7.0-to-11.0.0.js +110 -0
  321. package/bin/migrations/schema-versions.js +13 -0
  322. package/bin/models/anthropic-provider.js +45 -0
  323. package/bin/models/cloud-broker.js +68 -20
  324. package/bin/models/gemini-provider.js +51 -0
  325. package/bin/models/model-client.js +20 -0
  326. package/bin/models/model-router.js +28 -8
  327. package/bin/models/openai-provider.js +44 -0
  328. package/bin/utils/file-io.js +63 -1
  329. package/bin/utils/index.js +58 -0
  330. package/docs/getting-started.md +1 -1
  331. package/docs/user-guide.md +2 -2
  332. package/package.json +2 -2
  333. package/.mindforge/personas/data-privacy-engineer.md +0 -187
package/SECURITY.md CHANGED
@@ -4,12 +4,12 @@
4
4
 
5
5
  | Version | Status | Support Level |
6
6
  |---------|--------|---------------|
7
- | 10.x | **Current** | Full security and feature updates |
8
- | 9.x | Maintenance | Critical security patches only (until 2026-08-31) |
9
- | 8.x | End of Life | No further updates |
10
- | 7.x and below | End of Life | No further updates |
7
+ | 11.x | **Current** | Full security and feature updates |
8
+ | 10.x | Maintenance | Critical security patches only (until 2026-11-30) |
9
+ | 9.x | End of Life | No further updates |
10
+ | 8.x and below | End of Life | No further updates |
11
11
 
12
- We recommend all users upgrade to the latest 10.x release. Security patches for 9.x will be provided for critical vulnerabilities only, on a best-effort basis, until August 2026.
12
+ We recommend all users upgrade to the latest 11.x release. Security patches for 10.x will be provided for critical vulnerabilities only, on a best-effort basis, until November 2026.
13
13
 
14
14
  ---
15
15
 
@@ -44,18 +44,29 @@ We follow responsible disclosure practices. We will credit reporters in the rele
44
44
 
45
45
  ---
46
46
 
47
- ## Security Features (v10.0.1)
47
+ ## Security Features (v11.0.0)
48
48
 
49
49
  ### Authentication & Authorization
50
50
 
51
51
  - **Bearer token auth on dashboard** — All mutating endpoints (`/api/steering`, `/api/approve`, SSE control) require `Authorization: Bearer <token>`. Token is sourced from `MINDFORGE_DASHBOARD_TOKEN` environment variable.
52
+ - **Token expiration with refresh** — Dashboard tokens expire after 24 hours. Use `/api/v1/auth/refresh` to obtain a new token without re-authenticating.
53
+ - **Dashboard rate limiting** — 100 requests per minute per IP address. Exceeding the limit returns 429 with `Retry-After` header.
54
+ - **Session-scoped RBAC with TTL elevation** — Elevated permissions are session-scoped and auto-expire. No persistent privilege escalation.
52
55
  - **Browser daemon authentication** — The `/evaluate` endpoint requires auth before executing code in the Playwright context.
53
56
  - **ZTAI Trust Tiers** — 4-tier authorization model (Tier 0-3) controls which agents can perform which actions. Tier 3 (catastrophic-risk) operations require explicit human approval.
54
57
 
58
+ ### Cryptographic Security
59
+
60
+ - **Ephemeral enclave keys** — All crypto keys generated via `crypto.randomBytes()` at runtime. No hardcoded secrets in source.
61
+ - **Structured crypto boundaries** — Simulated (governance-enforcement) vs real (production-grade) cryptographic operations are clearly separated and labeled in code.
62
+ - **GPG approval verification** — Optional GPG signature verification on governance approvals for high-trust environments.
63
+ - **HMAC-signed temporal snapshots** — Temporal state captures are HMAC-signed to detect tampering during rollback operations.
64
+
55
65
  ### Audit & Integrity
56
66
 
57
67
  - **Merkle-chain audit log** — Every entry in `AUDIT.jsonl` includes a SHA-256 hash of the previous entry. Tampering with any historical entry breaks the chain, making modifications detectable.
58
68
  - **AuditWriter with buffered writes** — Atomic append operations prevent partial writes from corrupting the log.
69
+ - **Log rotation with archival** — AUDIT.jsonl auto-archives beyond 5000 lines with gzip compression, preventing unbounded disk growth.
59
70
  - **npm provenance** — Published packages include SLSA Build Level 2 attestation via `--provenance`, proving the package was built from the stated source commit in CI.
60
71
 
61
72
  ### Input Validation & Injection Prevention
@@ -67,7 +78,7 @@ We follow responsible disclosure practices. We will credit reporters in the rele
67
78
 
68
79
  ### Governance & Policy
69
80
 
70
- - **Fail-closed ZK verification** — `verifyZKProof()` throws on invalid or missing proofs. The system denies by default.
81
+ - **Structured ZK verification** — `verifyZKProof()` returns a structured result with `verified`, `reason`, and `timestamp` fields. The system denies by default when `verified` is false.
71
82
  - **Non-overridable parameters** — Security-critical MINDFORGE.md settings cannot be overridden by project-level or session-level configuration.
72
83
  - **CSP headers on dashboard** — Content Security Policy headers prevent XSS in the dashboard UI.
73
84
  - **Localhost-only binding** — The dashboard server binds to `127.0.0.1` only. It is not accessible from the network.
@@ -101,7 +112,8 @@ Before submitting code that touches security-sensitive paths:
101
112
 
102
113
  - **ZK-proofs are simulated** — The Dilithium-5 / ZK-proof layer uses cryptographic simulation, not hardware-backed TEEs. It provides logical governance enforcement, not hardware-grade isolation.
103
114
  - **Dashboard is localhost-only** — The dashboard is designed for local development. Do not expose it to the public internet, even behind a reverse proxy, without adding additional authentication.
104
- - **ZTAI keys are file-based** — Agent identity keys are stored on disk. In production deployments requiring hardware-bound keys, integrate with your organization's HSM or secure enclave.
115
+ - **ZTAI keys are ephemeral** — Agent identity keys are generated per-session via `crypto.randomBytes()`. In production deployments requiring persistent hardware-bound keys, integrate with your organization's HSM or secure enclave.
116
+ - **Rate limiting is per-process** — The 100 req/min limit is tracked in-memory. Restarting the dashboard resets counters. For distributed deployments, add an external rate limiter (e.g., nginx, Cloudflare).
105
117
 
106
118
  ---
107
119
 
@@ -6,11 +6,15 @@
6
6
  'use strict';
7
7
 
8
8
  const fs = require('fs');
9
+ const path = require('path');
9
10
  const crypto = require('crypto');
11
+ const { AuditRotator } = require('../utils/file-io');
10
12
 
11
13
  const FLUSH_INTERVAL_MS = 100;
12
14
  const FLUSH_THRESHOLD = 10;
13
15
 
16
+ const rotator = new AuditRotator({ maxLines: 5000 });
17
+
14
18
  /**
15
19
  * Creates a buffered async audit writer.
16
20
  * @param {string} auditPath — Path to the AUDIT.jsonl file
@@ -70,6 +74,15 @@ function createAuditWriter(auditPath) {
70
74
 
71
75
  const payload = toWrite.map(e => JSON.stringify(e)).join('\n') + '\n';
72
76
  await fs.promises.appendFile(auditPath, payload);
77
+
78
+ try {
79
+ if (rotator.shouldRotate(auditPath)) {
80
+ const archiveDir = path.join(path.dirname(auditPath), '..', '.planning', 'audit-archive');
81
+ rotator.rotate(auditPath, archiveDir);
82
+ }
83
+ } catch (err) {
84
+ process.stderr.write(`[audit-writer] rotation warning: ${err.message}\n`);
85
+ }
73
86
  }
74
87
 
75
88
  /**
@@ -21,7 +21,7 @@ const ContextRefactorer = require('./context-refactorer');
21
21
  // Extracted modules (lightweight, always needed)
22
22
  const { createAuditWriter } = require('./audit-writer');
23
23
  const { createStateManager } = require('./state-manager');
24
- const { createWaveExecutor } = require('./wave-executor');
24
+ const { createWaveExecutor, Semaphore } = require('./wave-executor');
25
25
 
26
26
  // ── Lazy-loaded heavy modules ────────────────────────────────────────────────
27
27
  // These are only required at the point of first use to reduce startup cost.
@@ -218,24 +218,44 @@ class AutoRunner {
218
218
  const wave = this.waves[this.currentWaveIndex];
219
219
  const waveNum = this.currentWaveIndex + 1;
220
220
  const pending = wave.tasks.filter(t => !this.completedTasks.has(t.id));
221
+ const maxConcurrency = this._getWaveConcurrency();
221
222
 
222
- console.log(`\n⚡ Wave ${waveNum}/${this.waves.length}: ${pending.length} tasks`);
223
+ console.log(`\n⚡ Wave ${waveNum}/${this.waves.length}: ${pending.length} tasks (concurrency: ${maxConcurrency})`);
223
224
  if (idcStatus.action === 'UPGRADE_MIR') console.log(` [IDC-ACTIVE] MIR Override: ${idcStatus.new_mir}`);
224
225
  this.writeAudit({ event: 'wave_started', phase: this.phase, wave: waveNum, task_count: pending.length });
225
226
 
226
- for (const task of pending) {
227
- const taskStart = Date.now();
228
- console.log(` → Task: ${task.name || task.id}`);
229
- try {
230
- this.writeAudit({ event: 'task_started', phase: this.phase, wave: waveNum, task_id: task.id, task_name: task.name || task.id });
231
- this.writeAudit({ event: 'task_completed', phase: this.phase, wave: waveNum, task_id: task.id, task_name: task.name || task.id, duration_ms: Date.now() - taskStart });
232
- this.completedTasks.add(task.id);
233
- } catch (err) {
234
- console.error(` Task failed: ${task.id} ${err.message}`);
235
- this.writeAudit({ event: 'task_failed', phase: this.phase, wave: waveNum, task_id: task.id, error: err.message, duration_ms: Date.now() - taskStart });
236
- const strategy = repairOperator.determineRepairStrategy({ planId: task.plan || task.id, phase: this.phase, attemptNumber: 1, errorOutput: err.message, isTier3Change: false, isOnCriticalPath: (task.depends_on || []).length > 0 });
237
- if (strategy === 'RETRY') { console.log(` Repair: retrying ${task.id}`); continue; }
238
- if (strategy === 'ESCALATE') { this.writeAudit({ event: 'auto_mode_escalated', reason: `Task ${task.id} unrecoverable` }); this.isPaused = true; return; }
227
+ const semaphore = new Semaphore(maxConcurrency);
228
+
229
+ const settled = await Promise.allSettled(
230
+ pending.map(async (task) => {
231
+ await semaphore.acquire();
232
+ const taskStart = Date.now();
233
+ console.log(` → Task: ${task.name || task.id}`);
234
+ try {
235
+ this.writeAudit({ event: 'task_started', phase: this.phase, wave: waveNum, task_id: task.id, task_name: task.name || task.id });
236
+ this.writeAudit({ event: 'task_completed', phase: this.phase, wave: waveNum, task_id: task.id, task_name: task.name || task.id, duration_ms: Date.now() - taskStart });
237
+ this.completedTasks.add(task.id);
238
+ return { taskId: task.id, status: 'fulfilled' };
239
+ } catch (err) {
240
+ console.error(` Task failed: ${task.id} — ${err.message}`);
241
+ this.writeAudit({ event: 'task_failed', phase: this.phase, wave: waveNum, task_id: task.id, error: err.message, duration_ms: Date.now() - taskStart });
242
+ throw { taskId: task.id, error: err, task };
243
+ } finally {
244
+ semaphore.release();
245
+ }
246
+ })
247
+ );
248
+
249
+ const failures = settled.filter(r => r.status === 'rejected');
250
+ if (failures.length > 0) {
251
+ for (const failure of failures) {
252
+ const { taskId, error, task } = failure.reason;
253
+ const strategy = repairOperator.determineRepairStrategy({ planId: task.plan || taskId, phase: this.phase, attemptNumber: 1, errorOutput: error.message, isTier3Change: false, isOnCriticalPath: (task.depends_on || []).length > 0 });
254
+ if (strategy === 'ESCALATE') {
255
+ this.writeAudit({ event: 'auto_mode_escalated', reason: `Task ${taskId} unrecoverable` });
256
+ this.isPaused = true;
257
+ return;
258
+ }
239
259
  }
240
260
  }
241
261
 
@@ -244,6 +264,19 @@ class AutoRunner {
244
264
  this.currentWaveIndex++;
245
265
  }
246
266
 
267
+ _getWaveConcurrency() {
268
+ try {
269
+ const configPath = path.join(process.cwd(), '.mindforge', 'config.json');
270
+ if (fs.existsSync(configPath)) {
271
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
272
+ if (typeof config.wave_concurrency === 'number' && config.wave_concurrency > 0) {
273
+ return config.wave_concurrency;
274
+ }
275
+ }
276
+ } catch (e) { /* Fall through to default */ }
277
+ return 3;
278
+ }
279
+
247
280
  /**
248
281
  * Build wave groups from HANDOFF handoffs array.
249
282
  * Kept as instance method for backward compatibility with tests.
@@ -316,6 +349,15 @@ class AutoRunner {
316
349
  if (captured.length + stability.length > 0) console.log(`🧠 Knowledge Graph: Captured ${captured.length + stability.length} insights.`);
317
350
  } catch (err) { console.error('⚠️ Knowledge Capture failed:', err.message); }
318
351
 
352
+ try {
353
+ _TemporalHub = lazyRequire(_TemporalHub, '../engine/temporal-hub');
354
+ const gcConfig = this._loadTemporalGcConfig();
355
+ const gcResult = await _TemporalHub.gc(gcConfig);
356
+ if (gcResult.deleted > 0) {
357
+ this.writeAudit({ event: 'temporal_gc_completed', deleted: gcResult.deleted, remaining: gcResult.remaining });
358
+ }
359
+ } catch (e) { /* GC failure is non-critical */ }
360
+
319
361
  this.writeAudit({ event: 'auto_mode_completed', timestamp: new Date().toISOString() });
320
362
  await this.auditWriter.close();
321
363
  }
@@ -334,7 +376,7 @@ class AutoRunner {
334
376
  const STATE_CHANGING_EVENTS = ['auto_mode_started', 'phase_planned', 'phase_execution_started', 'task_completed', 'hindsight_injected', 'auto_mode_completed'];
335
377
  if (STATE_CHANGING_EVENTS.includes(event.event)) {
336
378
  _TemporalHub = lazyRequire(_TemporalHub, '../engine/temporal-hub');
337
- _TemporalHub.captureState(event.id, { agent: event.agent || 'auto-runner', event: event.event, phase: this.phase });
379
+ _TemporalHub.captureState(event.id, { agent: event.agent || 'auto-runner', event: event.event, phase: this.phase }).catch(() => {});
338
380
  }
339
381
 
340
382
  const result = this.monitor.analyze(event);
@@ -444,6 +486,22 @@ class AutoRunner {
444
486
  const lines = buf.toString('utf8').trim().split('\n');
445
487
  return lines.slice(-count).map(l => { try { return JSON.parse(l); } catch { return null; } }).filter(Boolean);
446
488
  }
489
+
490
+ _loadTemporalGcConfig() {
491
+ try {
492
+ const configPath = path.join(process.cwd(), '.mindforge', 'config.json');
493
+ if (fs.existsSync(configPath)) {
494
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
495
+ if (config.temporal) {
496
+ return {
497
+ maxSnapshots: config.temporal.max_snapshots || 50,
498
+ maxAgeDays: config.temporal.max_age_days || 7
499
+ };
500
+ }
501
+ }
502
+ } catch (e) { /* Fall through to defaults */ }
503
+ return { maxSnapshots: 50, maxAgeDays: 7 };
504
+ }
447
505
  }
448
506
 
449
507
  module.exports = AutoRunner;
@@ -14,18 +14,15 @@ class ContextRefactorer {
14
14
  this.history = [];
15
15
  }
16
16
 
17
- /**
18
- * Analyze the current context density.
19
- * Density = (Implementation Events) / (Total Events)
20
- */
21
17
  analyzeDensity(events) {
22
- this.history = events.slice(-this.windowSize);
23
-
24
- if (this.history.length < 5) return { density: 1.0, shouldRefactor: false };
18
+ const windowSize = this._getAdaptiveWindow(events);
19
+ this.history = events.slice(-windowSize);
25
20
 
26
- const implementationEvents = this.history.filter(h =>
27
- h.tool === 'run_command' ||
28
- h.tool === 'replace_file_content' ||
21
+ if (this.history.length < 5) return { density: 1.0, shouldRefactor: false, windowSize };
22
+
23
+ const implementationEvents = this.history.filter(h =>
24
+ h.tool === 'run_command' ||
25
+ h.tool === 'replace_file_content' ||
29
26
  h.tool === 'multi_replace_file_content' ||
30
27
  h.event === 'task_completed'
31
28
  );
@@ -35,10 +32,28 @@ class ContextRefactorer {
35
32
 
36
33
  return {
37
34
  density: parseFloat(density.toFixed(2)),
38
- shouldRefactor: density < this.threshold
35
+ shouldRefactor: density < this.threshold,
36
+ windowSize
39
37
  };
40
38
  }
41
39
 
40
+ _getAdaptiveWindow(events) {
41
+ const recent = events.slice(-10);
42
+ if (recent.length === 0) return 20;
43
+
44
+ const implEvents = recent.filter(e =>
45
+ e.event === 'run_command' ||
46
+ e.event?.includes('replace_file') ||
47
+ e.event === 'task_completed'
48
+ ).length;
49
+
50
+ const velocity = implEvents / recent.length;
51
+
52
+ if (velocity > 0.6) return 10;
53
+ if (velocity < 0.3) return 30;
54
+ return 20;
55
+ }
56
+
42
57
  /**
43
58
  * Generates a "Context Refactor" recommendation.
44
59
  */
@@ -7,8 +7,59 @@
7
7
 
8
8
  const fs = require('fs');
9
9
  const path = require('path');
10
+ const { atomicWriteJSON } = require('../utils/file-io');
10
11
 
11
12
  const VALID_PHASES = ['idle', 'planning', 'executing', 'verifying', 'complete', 'running', 'paused', 'completed'];
13
+ const VALID_STATUSES = ['idle', 'running', 'paused', 'completed', 'escalated', 'timeout'];
14
+
15
+ /**
16
+ * Validates HANDOFF.json structure without blocking on failure (fail-open).
17
+ * Warns on malformed fields but always returns the data for processing.
18
+ * @param {*} data — Parsed HANDOFF.json content
19
+ * @returns {{ valid: boolean, warnings: string[] }}
20
+ */
21
+ function validateHandoff(data) {
22
+ const warnings = [];
23
+
24
+ if (!data || typeof data !== 'object') {
25
+ warnings.push('HANDOFF.json is not a valid object');
26
+ return { valid: false, warnings };
27
+ }
28
+
29
+ if (!data.schema_version) {
30
+ warnings.push('Missing schema_version field');
31
+ }
32
+
33
+ if (data.handoffs && !Array.isArray(data.handoffs)) {
34
+ warnings.push('handoffs field must be an array');
35
+ }
36
+
37
+ if (data.handoffs && Array.isArray(data.handoffs)) {
38
+ for (let i = 0; i < data.handoffs.length; i++) {
39
+ const task = data.handoffs[i];
40
+ if (!task.id) warnings.push(`handoffs[${i}] missing required field: id`);
41
+ if (!task.name) warnings.push(`handoffs[${i}] missing required field: name`);
42
+ }
43
+ }
44
+
45
+ if (data.status && !VALID_STATUSES.includes(data.status)) {
46
+ warnings.push(`Invalid status: "${data.status}". Expected one of: ${VALID_STATUSES.join(', ')}`);
47
+ }
48
+
49
+ if (data.wave_current !== undefined && typeof data.wave_current !== 'number') {
50
+ warnings.push('wave_current must be a number');
51
+ }
52
+
53
+ if (data.tasks_completed !== undefined && typeof data.tasks_completed !== 'number') {
54
+ warnings.push('tasks_completed must be a number');
55
+ }
56
+
57
+ if (data.timestamps && typeof data.timestamps !== 'object') {
58
+ warnings.push('timestamps must be an object');
59
+ }
60
+
61
+ return { valid: warnings.length === 0, warnings };
62
+ }
12
63
 
13
64
  /**
14
65
  * Creates a state manager for the given planning directory.
@@ -45,7 +96,7 @@ function createStateManager(planningDir) {
45
96
  function updateState(patch) {
46
97
  const current = getState();
47
98
  const merged = Object.assign(Object.create(null), current, patch);
48
- fs.writeFileSync(statePath, JSON.stringify(merged, null, 2));
99
+ atomicWriteJSON(statePath, merged);
49
100
  return merged;
50
101
  }
51
102
 
@@ -64,7 +115,8 @@ function createStateManager(planningDir) {
64
115
  }
65
116
 
66
117
  /**
67
- * Reads and parses HANDOFF.json. Throws if missing or malformed.
118
+ * Reads and parses HANDOFF.json with schema validation (fail-open).
119
+ * Logs warnings for structural issues but always returns data if parseable.
68
120
  * @returns {object} Parsed handoff data (fresh object)
69
121
  */
70
122
  function readHandoff() {
@@ -79,8 +131,12 @@ function createStateManager(planningDir) {
79
131
  throw new Error(`HANDOFF.json is malformed: ${e.message}`);
80
132
  }
81
133
 
82
- if (!handoff.handoffs || !Array.isArray(handoff.handoffs)) {
83
- throw new Error('HANDOFF.json has no handoffs array');
134
+ // Fail-open schema validation — warn but never block
135
+ const { valid, warnings } = validateHandoff(handoff);
136
+ if (!valid) {
137
+ for (const warning of warnings) {
138
+ console.warn('[STATE] HANDOFF validation:', warning);
139
+ }
84
140
  }
85
141
 
86
142
  return handoff;
@@ -94,7 +150,7 @@ function createStateManager(planningDir) {
94
150
  const timestamped = Object.assign(Object.create(null), data, {
95
151
  last_updated: new Date().toISOString(),
96
152
  });
97
- fs.writeFileSync(handoffPath, JSON.stringify(timestamped, null, 2) + '\n');
153
+ atomicWriteJSON(handoffPath, timestamped);
98
154
  return timestamped;
99
155
  }
100
156
 
@@ -113,4 +169,4 @@ function sanitizeState(parsed) {
113
169
  return clean;
114
170
  }
115
171
 
116
- module.exports = { createStateManager };
172
+ module.exports = { createStateManager, validateHandoff };
@@ -96,12 +96,49 @@ class StuckMonitor {
96
96
  return identical.length >= 3;
97
97
  }
98
98
 
99
- isContentSimilar(a, b) {
99
+ isContentSimilar(a, b, threshold = 10) {
100
100
  if (!a || !b) return false;
101
101
  if (a === b) return true;
102
- // Simple similarity check (hardened from Roadmap requirement)
103
- const dist = this.levenshtein(a.slice(0, 100), b.slice(0, 100));
104
- return dist < 10;
102
+
103
+ const hashA = this._quickHash(a);
104
+ const hashB = this._quickHash(b);
105
+ if (hashA === hashB) return true;
106
+
107
+ const lenDiff = Math.abs(a.length - b.length);
108
+ if (lenDiff > Math.max(a.length, b.length) * 0.2) return false;
109
+
110
+ const cached = this._getCachedSimilarity(hashA, hashB);
111
+ if (cached !== undefined) return cached;
112
+
113
+ const truncA = a.substring(0, 100);
114
+ const truncB = b.substring(0, 100);
115
+ const result = this.levenshtein(truncA, truncB) <= threshold;
116
+
117
+ this._setCachedSimilarity(hashA, hashB, result);
118
+ return result;
119
+ }
120
+
121
+ _quickHash(str) {
122
+ let hash = 0;
123
+ for (let i = 0; i < str.length; i++) {
124
+ hash = ((hash << 5) - hash) + str.charCodeAt(i);
125
+ hash = hash & hash;
126
+ }
127
+ return hash;
128
+ }
129
+
130
+ _getCachedSimilarity(keyA, keyB) {
131
+ const key = keyA < keyB ? `${keyA}|${keyB}` : `${keyB}|${keyA}`;
132
+ return StuckMonitor._similarityCache.get(key);
133
+ }
134
+
135
+ _setCachedSimilarity(keyA, keyB, result) {
136
+ const key = keyA < keyB ? `${keyA}|${keyB}` : `${keyB}|${keyA}`;
137
+ if (StuckMonitor._similarityCache.size >= 200) {
138
+ const firstKey = StuckMonitor._similarityCache.keys().next().value;
139
+ StuckMonitor._similarityCache.delete(firstKey);
140
+ }
141
+ StuckMonitor._similarityCache.set(key, result);
105
142
  }
106
143
 
107
144
  levenshtein(a, b) {
@@ -109,12 +146,14 @@ class StuckMonitor {
109
146
  for (let i = 0; i <= a.length; i++) { tmp[i] = [i]; }
110
147
  for (let j = 0; j <= b.length; j++) { tmp[0][j] = j; }
111
148
  for (let i = 1; i <= a.length; i++) {
112
- for (let j = 1; j <= b.length; j++) {
113
- tmp[i][j] = Math.min(tmp[i - 1][j] + 1, tmp[i][j - 1] + 1, tmp[i - 1][j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
114
- }
149
+ for (let j = 1; j <= b.length; j++) {
150
+ tmp[i][j] = Math.min(tmp[i - 1][j] + 1, tmp[i][j - 1] + 1, tmp[i - 1][j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
151
+ }
115
152
  }
116
153
  return tmp[a.length][b.length];
117
154
  }
118
155
  }
119
156
 
157
+ StuckMonitor._similarityCache = new Map();
158
+
120
159
  module.exports = StuckMonitor;
@@ -7,6 +7,35 @@
7
7
 
8
8
  const crypto = require('crypto');
9
9
 
10
+ /**
11
+ * Semaphore for bounding concurrency within a wave.
12
+ * Tasks within a wave are independent — this limits how many run simultaneously.
13
+ */
14
+ class Semaphore {
15
+ constructor(max) {
16
+ this.max = max;
17
+ this.current = 0;
18
+ this.queue = [];
19
+ }
20
+
21
+ async acquire() {
22
+ if (this.current < this.max) {
23
+ this.current++;
24
+ return;
25
+ }
26
+ await new Promise(resolve => this.queue.push(resolve));
27
+ this.current++;
28
+ }
29
+
30
+ release() {
31
+ this.current--;
32
+ if (this.queue.length > 0) {
33
+ const next = this.queue.shift();
34
+ next();
35
+ }
36
+ }
37
+ }
38
+
10
39
  /**
11
40
  * Creates a wave executor with the given configuration.
12
41
  * @param {object} config
@@ -73,42 +102,54 @@ function createWaveExecutor(config = {}) {
73
102
  }
74
103
 
75
104
  /**
76
- * Executes a single wave — runs tasks sequentially, skipping already-completed ones.
105
+ * Executes a single wave — runs tasks in parallel (bounded by maxConcurrency),
106
+ * skipping already-completed ones.
77
107
  * @param {object} wave — A wave object from planWaves
78
108
  * @param {object} context — Execution context passed to callbacks
79
- * @param {object} context.executor — async function(task) => result (performs actual work)
109
+ * @param {function} context.executor — async function(task) => result (performs actual work)
110
+ * @param {number} [context.maxConcurrency=3] — Max parallel tasks within this wave
80
111
  * @returns {Promise<{ completed: string[], failed: string[], skipped: string[] }>}
81
112
  */
82
113
  async function executeWave(wave, context = {}) {
83
- const { executor = async () => {} } = context;
114
+ const { executor = async () => {}, maxConcurrency = 3 } = context;
84
115
  status = 'running';
85
116
 
86
117
  const pending = wave.tasks.filter(t => !completedTasks.has(t.id));
87
118
  const result = { completed: [], failed: [], skipped: [] };
119
+ const semaphore = new Semaphore(maxConcurrency);
88
120
 
89
121
  onWaveStart({ wave: wave.wave, taskCount: pending.length });
90
122
 
91
- for (const task of pending) {
92
- const taskStart = Date.now();
93
- onTaskStart({ task, wave: wave.wave });
94
-
95
- try {
96
- await executor(task);
97
-
98
- const duration = Date.now() - taskStart;
99
- completedTasks = new Set([...completedTasks, task.id]);
100
- result.completed.push(task.id);
101
-
102
- onTaskComplete({ task, wave: wave.wave, duration_ms: duration });
103
- } catch (err) {
104
- const duration = Date.now() - taskStart;
105
- result.failed.push(task.id);
106
-
107
- onTaskFail({ task, wave: wave.wave, error: err, duration_ms: duration });
108
-
109
- // Re-throw to let caller decide on retry/escalation strategy
110
- throw err;
111
- }
123
+ const settled = await Promise.allSettled(
124
+ pending.map(async (task) => {
125
+ await semaphore.acquire();
126
+ const taskStart = Date.now();
127
+ try {
128
+ onTaskStart({ task, wave: wave.wave });
129
+ await executor(task);
130
+
131
+ const duration = Date.now() - taskStart;
132
+ completedTasks = new Set([...completedTasks, task.id]);
133
+ result.completed.push(task.id);
134
+
135
+ onTaskComplete({ task, wave: wave.wave, duration_ms: duration });
136
+ return { task, status: 'fulfilled' };
137
+ } catch (err) {
138
+ const duration = Date.now() - taskStart;
139
+ result.failed.push(task.id);
140
+
141
+ onTaskFail({ task, wave: wave.wave, error: err, duration_ms: duration });
142
+ throw err;
143
+ } finally {
144
+ semaphore.release();
145
+ }
146
+ })
147
+ );
148
+
149
+ const failures = settled.filter(r => r.status === 'rejected');
150
+ if (failures.length > 0) {
151
+ const failMsg = failures.map(f => f.reason?.message || 'unknown').join(', ');
152
+ throw new Error(`${failures.length} task(s) failed in wave: ${failMsg}`);
112
153
  }
113
154
 
114
155
  currentWaveIndex++;
@@ -166,4 +207,4 @@ function normalizeTask(h) {
166
207
  });
167
208
  }
168
209
 
169
- module.exports = { createWaveExecutor };
210
+ module.exports = { createWaveExecutor, Semaphore };
@@ -192,6 +192,49 @@ function register(app) {
192
192
  app.get('/api/connections', (req, res) => {
193
193
  res.json({ clients: SSE.getClientCount() });
194
194
  });
195
+
196
+ // ── System observability ────────────────────────────────────────────────────
197
+ app.get('/api/v1/system', (req, res) => {
198
+ try {
199
+ const heapUsed = process.memoryUsage().heapUsed;
200
+ const heapTotal = process.memoryUsage().heapTotal;
201
+ const uptime = process.uptime();
202
+
203
+ let auditLines = 0;
204
+ try {
205
+ const auditPath = path.join(process.cwd(), '.planning', 'AUDIT.jsonl');
206
+ if (fs.existsSync(auditPath)) {
207
+ const content = fs.readFileSync(auditPath, 'utf8');
208
+ auditLines = content.split('\n').filter(l => l.trim()).length;
209
+ }
210
+ } catch { /* non-critical */ }
211
+
212
+ let snapshotCount = 0;
213
+ try {
214
+ const historyDir = path.join(process.cwd(), '.planning', 'history');
215
+ if (fs.existsSync(historyDir)) {
216
+ snapshotCount = fs.readdirSync(historyDir).length;
217
+ }
218
+ } catch { /* non-critical */ }
219
+
220
+ const heapHealth = Metrics.checkHeapHealth();
221
+
222
+ res.json({
223
+ heap_used_mb: Math.round(heapUsed / 1024 / 1024 * 100) / 100,
224
+ heap_total_mb: Math.round(heapTotal / 1024 / 1024 * 100) / 100,
225
+ heap_usage_pct: Math.round(heapUsed / heapTotal * 100),
226
+ heap_alert: heapHealth,
227
+ uptime_seconds: Math.round(uptime),
228
+ audit_lines: auditLines,
229
+ snapshot_count: snapshotCount,
230
+ sse_clients: SSE.getClientCount(),
231
+ node_version: process.version,
232
+ timestamp: new Date().toISOString()
233
+ });
234
+ } catch (err) {
235
+ res.status(500).json({ error: err.message });
236
+ }
237
+ });
195
238
  }
196
239
 
197
240
  module.exports = { register };