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.
- package/.mindforge/MINDFORGE-V2-SCHEMA.json +43 -10
- package/.mindforge/config.json +30 -2
- package/.mindforge/engine/cross-model-eval.md +74 -0
- package/.mindforge/engine/proactive/signal-detector.md +60 -0
- package/.mindforge/engine/proactive/suggestion-engine.md +100 -0
- package/.mindforge/personas/agent-architect.md +57 -0
- package/.mindforge/personas/agent-evaluator.md +162 -0
- package/.mindforge/personas/agent-memory-designer.md +157 -0
- package/.mindforge/personas/agent-ops-engineer.md +120 -0
- package/.mindforge/personas/agent-orchestrator.md +112 -0
- package/.mindforge/personas/ai-economist.md +57 -0
- package/.mindforge/personas/ai-safety-engineer.md +57 -0
- package/.mindforge/personas/analytics-engineer.md +57 -0
- package/.mindforge/personas/anti-pattern-hunter.md +61 -0
- package/.mindforge/personas/api-gateway-designer.md +132 -0
- package/.mindforge/personas/auth-engineer.md +112 -0
- package/.mindforge/personas/build-engineer.md +57 -0
- package/.mindforge/personas/business-analyst.md +56 -0
- package/.mindforge/personas/cache-architect.md +100 -0
- package/.mindforge/personas/causal-scientist.md +57 -0
- package/.mindforge/personas/cdn-architect.md +118 -0
- package/.mindforge/personas/change-agent.md +104 -0
- package/.mindforge/personas/code-narrator.md +52 -0
- package/.mindforge/personas/codegen-specialist.md +68 -0
- package/.mindforge/personas/communication-architect.md +102 -0
- package/.mindforge/personas/compliance-engineer.md +96 -0
- package/.mindforge/personas/consensus-engineer.md +116 -0
- package/.mindforge/personas/contract-tester.md +60 -192
- package/.mindforge/personas/data-architect.md +108 -0
- package/.mindforge/personas/data-mesh-architect.md +57 -0
- package/.mindforge/personas/data-pipeline-architect.md +120 -0
- package/.mindforge/personas/de-sloppifier.md +60 -0
- package/.mindforge/personas/debt-manager.md +66 -0
- package/.mindforge/personas/decision-architect.md +82 -51
- package/.mindforge/personas/deployment-captain.md +74 -0
- package/.mindforge/personas/design-system-lead.md +112 -0
- package/.mindforge/personas/dmux-orchestrator.md +75 -0
- package/.mindforge/personas/dx-engineer.md +96 -0
- package/.mindforge/personas/ecommerce-engineer.md +57 -0
- package/.mindforge/personas/edge-engineer.md +94 -0
- package/.mindforge/personas/edtech-architect.md +106 -0
- package/.mindforge/personas/embedding-architect.md +57 -0
- package/.mindforge/personas/environment-engineer.md +57 -0
- package/.mindforge/personas/eval-judge.md +55 -0
- package/.mindforge/personas/event-architect.md +102 -0
- package/.mindforge/personas/experiment-designer.md +138 -0
- package/.mindforge/personas/feature-store-engineer.md +57 -0
- package/.mindforge/personas/finops-analyst.md +66 -0
- package/.mindforge/personas/fintech-architect.md +57 -0
- package/.mindforge/personas/flutter-engineer.md +104 -0
- package/.mindforge/personas/gaming-engineer.md +57 -0
- package/.mindforge/personas/graphql-designer.md +73 -0
- package/.mindforge/personas/healthcare-engineer.md +57 -0
- package/.mindforge/personas/hiring-strategist.md +105 -0
- package/.mindforge/personas/hitl-architect.md +165 -0
- package/.mindforge/personas/i18n-architect.md +69 -0
- package/.mindforge/personas/iot-architect.md +105 -0
- package/.mindforge/personas/knowledge-curator.md +139 -0
- package/.mindforge/personas/knowledge-engineer.md +57 -0
- package/.mindforge/personas/lakehouse-architect.md +57 -0
- package/.mindforge/personas/llm-orchestrator.md +57 -0
- package/.mindforge/personas/logistics-architect.md +106 -0
- package/.mindforge/personas/market-analyst.md +53 -0
- package/.mindforge/personas/marketplace-engineer.md +105 -0
- package/.mindforge/personas/mcp-designer.md +54 -0
- package/.mindforge/personas/meeting-designer.md +104 -0
- package/.mindforge/personas/mentorship-lead.md +106 -0
- package/.mindforge/personas/migration-architect.md +57 -0
- package/.mindforge/personas/ml-ops-engineer.md +101 -0
- package/.mindforge/personas/mobile-architect.md +105 -0
- package/.mindforge/personas/mobile-security-engineer.md +106 -0
- package/.mindforge/personas/multi-tenancy-architect.md +71 -0
- package/.mindforge/personas/multimodal-engineer.md +57 -0
- package/.mindforge/personas/offline-specialist.md +105 -0
- package/.mindforge/personas/onboarding-navigator.md +63 -0
- package/.mindforge/personas/payments-engineer.md +135 -0
- package/.mindforge/personas/pipeline-engineer.md +115 -0
- package/.mindforge/personas/platform-engineer.md +97 -0
- package/.mindforge/personas/platform-lead.md +57 -0
- package/.mindforge/personas/privacy-engineer.md +57 -0
- package/.mindforge/personas/product-owner.md +56 -0
- package/.mindforge/personas/productivity-analyst.md +57 -0
- package/.mindforge/personas/prompt-architect.md +101 -0
- package/.mindforge/personas/proofreader.md +53 -0
- package/.mindforge/personas/pwa-architect.md +105 -0
- package/.mindforge/personas/quality-scorer.md +63 -0
- package/.mindforge/personas/react-native-engineer.md +106 -0
- package/.mindforge/personas/resilience-engineer.md +69 -0
- package/.mindforge/personas/rfc-architect.md +64 -0
- package/.mindforge/personas/saga-orchestrator.md +80 -0
- package/.mindforge/personas/secrets-engineer.md +57 -0
- package/.mindforge/personas/skill-smith.md +79 -0
- package/.mindforge/personas/sre-lead.md +107 -0
- package/.mindforge/personas/stream-engineer.md +57 -0
- package/.mindforge/personas/streaming-engineer.md +64 -0
- package/.mindforge/personas/swarm-templates.json +674 -44
- package/.mindforge/personas/system-designer.md +57 -0
- package/.mindforge/personas/team-coach.md +120 -0
- package/.mindforge/personas/tech-lead-coach.md +103 -0
- package/.mindforge/personas/technical-writer-lead.md +111 -0
- package/.mindforge/personas/vibe-checker.md +75 -0
- package/.mindforge/personas/worktree-manager.md +56 -0
- package/.mindforge/personas/zero-trust-engineer.md +113 -0
- package/.mindforge/skills/a11y-testing/SKILL.md +143 -0
- package/.mindforge/skills/agent-evaluation-framework/SKILL.md +227 -0
- package/.mindforge/skills/agent-memory-design/SKILL.md +199 -0
- package/.mindforge/skills/agent-orchestration-patterns/SKILL.md +129 -0
- package/.mindforge/skills/agent-tool-selection/SKILL.md +204 -0
- package/.mindforge/skills/ai-agent-deployment/SKILL.md +176 -0
- package/.mindforge/skills/ai-cost-management/SKILL.md +57 -0
- package/.mindforge/skills/ai-safety-alignment/SKILL.md +53 -0
- package/.mindforge/skills/analytics-instrumentation/SKILL.md +172 -0
- package/.mindforge/skills/api-gateway-patterns/SKILL.md +177 -0
- package/.mindforge/skills/api-marketplace/SKILL.md +56 -0
- package/.mindforge/skills/api-versioning/SKILL.md +100 -0
- package/.mindforge/skills/app-store-deployment/SKILL.md +44 -0
- package/.mindforge/skills/architecture-tradeoff-analysis/SKILL.md +97 -0
- package/.mindforge/skills/audit-logging/SKILL.md +140 -0
- package/.mindforge/skills/auth-patterns/SKILL.md +148 -0
- package/.mindforge/skills/autonomous-agent-harness/SKILL.md +218 -0
- package/.mindforge/skills/autonomous-agents/SKILL.md +59 -0
- package/.mindforge/skills/build-system-optimization/SKILL.md +54 -0
- package/.mindforge/skills/build-vs-buy/SKILL.md +80 -0
- package/.mindforge/skills/bundle-optimization/SKILL.md +174 -0
- package/.mindforge/skills/business-analyst/SKILL.md +82 -0
- package/.mindforge/skills/caching-strategies/SKILL.md +132 -0
- package/.mindforge/skills/capacity-planning/SKILL.md +96 -0
- package/.mindforge/skills/causal-inference/SKILL.md +42 -0
- package/.mindforge/skills/cdn-optimization/SKILL.md +212 -0
- package/.mindforge/skills/change-management/SKILL.md +106 -0
- package/.mindforge/skills/chaos-engineering/SKILL.md +99 -0
- package/.mindforge/skills/ci-cd-pipeline/SKILL.md +118 -0
- package/.mindforge/skills/cli-design/SKILL.md +118 -0
- package/.mindforge/skills/code-generation-patterns/SKILL.md +92 -0
- package/.mindforge/skills/code-review-methodology/SKILL.md +180 -0
- package/.mindforge/skills/code-tour/SKILL.md +145 -0
- package/.mindforge/skills/codebase-onboarding/SKILL.md +95 -0
- package/.mindforge/skills/compliance-as-code/SKILL.md +195 -0
- package/.mindforge/skills/conflict-resolution/SKILL.md +87 -0
- package/.mindforge/skills/connection-pooling/SKILL.md +151 -0
- package/.mindforge/skills/container-security/SKILL.md +151 -0
- package/.mindforge/skills/context-engineering/SKILL.md +114 -0
- package/.mindforge/skills/contract-testing/SKILL.md +85 -0
- package/.mindforge/skills/cost-estimation/SKILL.md +82 -0
- package/.mindforge/skills/cqrs-event-sourcing/SKILL.md +95 -0
- package/.mindforge/skills/cross-platform-testing/SKILL.md +43 -0
- package/.mindforge/skills/data-governance/SKILL.md +42 -0
- package/.mindforge/skills/data-lakehouse/SKILL.md +42 -0
- package/.mindforge/skills/data-mesh/SKILL.md +42 -0
- package/.mindforge/skills/data-modeling/SKILL.md +107 -0
- package/.mindforge/skills/data-pipeline-design/SKILL.md +171 -0
- package/.mindforge/skills/data-privacy-engineering/SKILL.md +42 -0
- package/.mindforge/skills/database-performance/SKILL.md +174 -0
- package/.mindforge/skills/database-sharding-advanced/SKILL.md +206 -0
- package/.mindforge/skills/de-sloppify/SKILL.md +120 -0
- package/.mindforge/skills/defense-in-depth/SKILL.md +84 -0
- package/.mindforge/skills/delegation-patterns/SKILL.md +123 -0
- package/.mindforge/skills/dependency-management/SKILL.md +94 -0
- package/.mindforge/skills/deployment-workflow/SKILL.md +135 -0
- package/.mindforge/skills/design-system/SKILL.md +113 -0
- package/.mindforge/skills/developer-onboarding/SKILL.md +99 -0
- package/.mindforge/skills/developer-productivity-metrics/SKILL.md +59 -0
- package/.mindforge/skills/distributed-consensus/SKILL.md +141 -0
- package/.mindforge/skills/dmux-workflows/SKILL.md +141 -0
- package/.mindforge/skills/dns-architecture/SKILL.md +167 -0
- package/.mindforge/skills/ecommerce-architecture/SKILL.md +41 -0
- package/.mindforge/skills/edge-computing/SKILL.md +91 -0
- package/.mindforge/skills/edtech-platform/SKILL.md +41 -0
- package/.mindforge/skills/email-deliverability/SKILL.md +177 -0
- package/.mindforge/skills/embedding-systems/SKILL.md +55 -0
- package/.mindforge/skills/environment-management/SKILL.md +54 -0
- package/.mindforge/skills/error-handling-architecture/SKILL.md +118 -0
- package/.mindforge/skills/estimation-techniques/SKILL.md +113 -0
- package/.mindforge/skills/eval-harness/SKILL.md +180 -0
- package/.mindforge/skills/event-driven-architecture/SKILL.md +162 -0
- package/.mindforge/skills/experiment-design/SKILL.md +139 -0
- package/.mindforge/skills/experiment-platform/SKILL.md +43 -0
- package/.mindforge/skills/feature-engineering/SKILL.md +42 -0
- package/.mindforge/skills/feature-flag-management/SKILL.md +183 -0
- package/.mindforge/skills/fine-tuning-workflow/SKILL.md +189 -0
- package/.mindforge/skills/fintech-patterns/SKILL.md +41 -0
- package/.mindforge/skills/flutter-architecture/SKILL.md +42 -0
- package/.mindforge/skills/gaming-backend/SKILL.md +41 -0
- package/.mindforge/skills/git-workflow-design/SKILL.md +129 -0
- package/.mindforge/skills/graceful-degradation/SKILL.md +95 -0
- package/.mindforge/skills/graphql-patterns/SKILL.md +243 -0
- package/.mindforge/skills/guardrails-and-safety/SKILL.md +137 -0
- package/.mindforge/skills/healthcare-systems/SKILL.md +40 -0
- package/.mindforge/skills/hiring-engineering/SKILL.md +119 -0
- package/.mindforge/skills/human-in-the-loop-design/SKILL.md +234 -0
- package/.mindforge/skills/i18n-architecture/SKILL.md +147 -0
- package/.mindforge/skills/idempotency-patterns/SKILL.md +84 -0
- package/.mindforge/skills/incident-communication/SKILL.md +96 -0
- package/.mindforge/skills/incident-management/SKILL.md +97 -0
- package/.mindforge/skills/infrastructure-as-code/SKILL.md +98 -0
- package/.mindforge/skills/instinct-clustering/SKILL.md +190 -0
- package/.mindforge/skills/internal-developer-platform/SKILL.md +51 -0
- package/.mindforge/skills/iot-platform/SKILL.md +41 -0
- package/.mindforge/skills/k8s-deployment/SKILL.md +358 -0
- package/.mindforge/skills/knowledge-graphs/SKILL.md +56 -0
- package/.mindforge/skills/knowledge-sharing-systems/SKILL.md +112 -0
- package/.mindforge/skills/llm-cost-optimization/SKILL.md +198 -0
- package/.mindforge/skills/llm-orchestration/SKILL.md +56 -0
- package/.mindforge/skills/load-testing/SKILL.md +84 -0
- package/.mindforge/skills/logistics-optimization/SKILL.md +40 -0
- package/.mindforge/skills/market-researcher/SKILL.md +99 -0
- package/.mindforge/skills/marketplace-trust/SKILL.md +40 -0
- package/.mindforge/skills/mcp-server-patterns/SKILL.md +264 -0
- package/.mindforge/skills/media-streaming/SKILL.md +41 -0
- package/.mindforge/skills/meeting-architecture/SKILL.md +146 -0
- package/.mindforge/skills/mentoring-patterns/SKILL.md +77 -0
- package/.mindforge/skills/microservices-patterns/SKILL.md +83 -0
- package/.mindforge/skills/migration-platform/SKILL.md +61 -0
- package/.mindforge/skills/migration-strategies/SKILL.md +129 -0
- package/.mindforge/skills/ml-feature-store/SKILL.md +56 -0
- package/.mindforge/skills/ml-monitoring/SKILL.md +42 -0
- package/.mindforge/skills/mobile-performance/SKILL.md +44 -0
- package/.mindforge/skills/mobile-security/SKILL.md +45 -0
- package/.mindforge/skills/model-evaluation/SKILL.md +53 -0
- package/.mindforge/skills/monorepo-management/SKILL.md +100 -0
- package/.mindforge/skills/multi-tenancy-patterns/SKILL.md +145 -0
- package/.mindforge/skills/multi-turn-conversation-design/SKILL.md +206 -0
- package/.mindforge/skills/multimodal-ai/SKILL.md +51 -0
- package/.mindforge/skills/mutation-testing/SKILL.md +97 -0
- package/.mindforge/skills/notification-system-design/SKILL.md +168 -0
- package/.mindforge/skills/observability-stack/SKILL.md +136 -0
- package/.mindforge/skills/offline-first-design/SKILL.md +43 -0
- package/.mindforge/skills/on-call-design/SKILL.md +111 -0
- package/.mindforge/skills/pagination-patterns/SKILL.md +230 -0
- package/.mindforge/skills/payment-integration/SKILL.md +176 -0
- package/.mindforge/skills/performance-reviews/SKILL.md +140 -0
- package/.mindforge/skills/platform-observability/SKILL.md +58 -0
- package/.mindforge/skills/platform-reliability/SKILL.md +52 -0
- package/.mindforge/skills/post-incident-learning/SKILL.md +96 -0
- package/.mindforge/skills/product-manager/SKILL.md +104 -0
- package/.mindforge/skills/progressive-web-app/SKILL.md +44 -0
- package/.mindforge/skills/prompt-engineering/SKILL.md +94 -0
- package/.mindforge/skills/proofreader/SKILL.md +158 -0
- package/.mindforge/skills/push-notification-architecture/SKILL.md +45 -0
- package/.mindforge/skills/python-performance/SKILL.md +183 -0
- package/.mindforge/skills/quality-audit/SKILL.md +171 -0
- package/.mindforge/skills/queue-design/SKILL.md +85 -0
- package/.mindforge/skills/rag-architecture/SKILL.md +176 -0
- package/.mindforge/skills/rate-limiting-design/SKILL.md +94 -0
- package/.mindforge/skills/react-native-patterns/SKILL.md +42 -0
- package/.mindforge/skills/react-performance/SKILL.md +229 -0
- package/.mindforge/skills/real-time-analytics/SKILL.md +42 -0
- package/.mindforge/skills/real-time-sync/SKILL.md +83 -0
- package/.mindforge/skills/responsive-native/SKILL.md +44 -0
- package/.mindforge/skills/responsive-patterns/SKILL.md +141 -0
- package/.mindforge/skills/rfc-pipeline/SKILL.md +114 -0
- package/.mindforge/skills/saas-multi-tenant/SKILL.md +41 -0
- package/.mindforge/skills/santa-method/SKILL.md +134 -0
- package/.mindforge/skills/search-implementation/SKILL.md +98 -0
- package/.mindforge/skills/secrets-platform/SKILL.md +56 -0
- package/.mindforge/skills/secrets-rotation/SKILL.md +173 -0
- package/.mindforge/skills/self-serve-infrastructure/SKILL.md +51 -0
- package/.mindforge/skills/serverless-patterns/SKILL.md +119 -0
- package/.mindforge/skills/skill-creator-meta/SKILL.md +146 -0
- package/.mindforge/skills/sprint-retrospective-facilitation/SKILL.md +112 -0
- package/.mindforge/skills/stakeholder-communication/SKILL.md +85 -0
- package/.mindforge/skills/state-management/SKILL.md +104 -0
- package/.mindforge/skills/stream-processing/SKILL.md +43 -0
- package/.mindforge/skills/streaming-architecture/SKILL.md +81 -0
- package/.mindforge/skills/supply-chain-security/SKILL.md +145 -0
- package/.mindforge/skills/synthetic-data-generation/SKILL.md +52 -0
- package/.mindforge/skills/system-design/SKILL.md +88 -0
- package/.mindforge/skills/team-topology-design/SKILL.md +107 -0
- package/.mindforge/skills/technical-debt-management/SKILL.md +86 -0
- package/.mindforge/skills/technical-interview-design/SKILL.md +98 -0
- package/.mindforge/skills/technical-leadership/SKILL.md +75 -0
- package/.mindforge/skills/technical-writing/SKILL.md +237 -0
- package/.mindforge/skills/technology-radar/SKILL.md +88 -0
- package/.mindforge/skills/testing-anti-patterns/SKILL.md +288 -0
- package/.mindforge/skills/tool-design/SKILL.md +138 -0
- package/.mindforge/skills/typescript-advanced/SKILL.md +198 -0
- package/.mindforge/skills/using-git-worktrees/SKILL.md +139 -0
- package/.mindforge/skills/verification-loop/SKILL.md +13 -1
- package/.mindforge/skills/vibe-security/SKILL.md +165 -0
- package/.mindforge/skills/visual-regression-testing/SKILL.md +97 -0
- package/.mindforge/skills/websocket-patterns/SKILL.md +203 -0
- package/.mindforge/skills/writing-plans/SKILL.md +170 -0
- package/.mindforge/skills/writing-skills/SKILL.md +216 -0
- package/.mindforge/skills/zero-trust-architecture/SKILL.md +166 -0
- package/CHANGELOG.md +240 -0
- package/MINDFORGE.md +4 -4
- package/README.md +49 -4
- package/RELEASENOTES.md +80 -0
- package/SECURITY.md +20 -8
- package/bin/autonomous/audit-writer.js +13 -0
- package/bin/autonomous/auto-runner.js +74 -16
- package/bin/autonomous/context-refactorer.js +26 -11
- package/bin/autonomous/state-manager.js +62 -6
- package/bin/autonomous/stuck-monitor.js +46 -7
- package/bin/autonomous/wave-executor.js +66 -25
- package/bin/dashboard/api-router.js +43 -0
- package/bin/dashboard/metrics-aggregator.js +28 -1
- package/bin/dashboard/server.js +67 -4
- package/bin/dashboard/sse-bridge.js +4 -4
- package/bin/engine/feedback-loop.js +8 -0
- package/bin/engine/intelligence-interlock.js +32 -15
- package/bin/engine/logic-drift-detector.js +2 -1
- package/bin/engine/nexus-tracer.js +3 -2
- package/bin/engine/remediation-engine.js +155 -32
- package/bin/engine/self-corrective-synthesizer.js +84 -10
- package/bin/engine/sre-manager.js +12 -4
- package/bin/engine/temporal-hub.js +131 -34
- package/bin/governance/approve.js +41 -5
- package/bin/governance/impact-analyzer.js +28 -0
- package/bin/governance/policy-engine.js +10 -3
- package/bin/governance/quantum-crypto.js +32 -19
- package/bin/governance/rbac-manager.js +74 -2
- package/bin/governance/ztai-manager.js +49 -7
- package/bin/hindsight-injector.js +3 -3
- package/bin/memory/eis-client.js +71 -34
- package/bin/memory/embedding-engine.js +61 -0
- package/bin/memory/knowledge-graph.js +58 -5
- package/bin/memory/knowledge-indexer.js +53 -6
- package/bin/memory/knowledge-store.js +22 -0
- package/bin/migrations/10.7.0-to-11.0.0.js +110 -0
- package/bin/migrations/schema-versions.js +13 -0
- package/bin/models/anthropic-provider.js +45 -0
- package/bin/models/cloud-broker.js +68 -20
- package/bin/models/gemini-provider.js +51 -0
- package/bin/models/model-client.js +20 -0
- package/bin/models/model-router.js +28 -8
- package/bin/models/openai-provider.js +44 -0
- package/bin/utils/file-io.js +63 -1
- package/bin/utils/index.js +58 -0
- package/docs/getting-started.md +1 -1
- package/docs/user-guide.md +2 -2
- package/package.json +2 -2
- 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
|
-
|
|
|
8
|
-
|
|
|
9
|
-
|
|
|
10
|
-
|
|
|
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
|
|
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 (
|
|
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
|
-
- **
|
|
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
|
|
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
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
83
|
-
|
|
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
|
-
|
|
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
|
-
|
|
103
|
-
const
|
|
104
|
-
|
|
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
|
-
|
|
113
|
-
|
|
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
|
|
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 {
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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 };
|