mindforge-cc 1.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/.agent/CLAUDE.md +462 -0
- package/.agent/forge/help.md +7 -0
- package/.agent/forge/init-project.md +32 -0
- package/.agent/forge/plan-phase.md +30 -0
- package/.agent/mindforge/approve.md +18 -0
- package/.agent/mindforge/audit.md +30 -0
- package/.agent/mindforge/benchmark.md +33 -0
- package/.agent/mindforge/complete-milestone.md +18 -0
- package/.agent/mindforge/debug.md +126 -0
- package/.agent/mindforge/discuss-phase.md +138 -0
- package/.agent/mindforge/execute-phase.md +165 -0
- package/.agent/mindforge/health.md +21 -0
- package/.agent/mindforge/help.md +23 -0
- package/.agent/mindforge/init-org.md +131 -0
- package/.agent/mindforge/init-project.md +155 -0
- package/.agent/mindforge/install-skill.md +15 -0
- package/.agent/mindforge/map-codebase.md +298 -0
- package/.agent/mindforge/metrics.md +22 -0
- package/.agent/mindforge/migrate.md +40 -0
- package/.agent/mindforge/milestone.md +12 -0
- package/.agent/mindforge/next.md +105 -0
- package/.agent/mindforge/plan-phase.md +125 -0
- package/.agent/mindforge/plugins.md +40 -0
- package/.agent/mindforge/pr-review.md +41 -0
- package/.agent/mindforge/profile-team.md +23 -0
- package/.agent/mindforge/publish-skill.md +19 -0
- package/.agent/mindforge/quick.md +135 -0
- package/.agent/mindforge/release.md +10 -0
- package/.agent/mindforge/retrospective.md +26 -0
- package/.agent/mindforge/review.md +157 -0
- package/.agent/mindforge/security-scan.md +233 -0
- package/.agent/mindforge/ship.md +100 -0
- package/.agent/mindforge/skills.md +141 -0
- package/.agent/mindforge/status.md +104 -0
- package/.agent/mindforge/sync-confluence.md +11 -0
- package/.agent/mindforge/sync-jira.md +12 -0
- package/.agent/mindforge/tokens.md +8 -0
- package/.agent/mindforge/update.md +42 -0
- package/.agent/mindforge/verify-phase.md +62 -0
- package/.agent/mindforge/workspace.md +29 -0
- package/.claude/CLAUDE.md +462 -0
- package/.claude/commands/forge/help.md +7 -0
- package/.claude/commands/forge/init-project.md +32 -0
- package/.claude/commands/forge/plan-phase.md +30 -0
- package/.claude/commands/mindforge/approve.md +18 -0
- package/.claude/commands/mindforge/audit.md +30 -0
- package/.claude/commands/mindforge/benchmark.md +33 -0
- package/.claude/commands/mindforge/complete-milestone.md +18 -0
- package/.claude/commands/mindforge/debug.md +126 -0
- package/.claude/commands/mindforge/discuss-phase.md +138 -0
- package/.claude/commands/mindforge/execute-phase.md +165 -0
- package/.claude/commands/mindforge/health.md +21 -0
- package/.claude/commands/mindforge/help.md +23 -0
- package/.claude/commands/mindforge/init-org.md +131 -0
- package/.claude/commands/mindforge/init-project.md +155 -0
- package/.claude/commands/mindforge/install-skill.md +15 -0
- package/.claude/commands/mindforge/map-codebase.md +298 -0
- package/.claude/commands/mindforge/metrics.md +22 -0
- package/.claude/commands/mindforge/migrate.md +40 -0
- package/.claude/commands/mindforge/milestone.md +12 -0
- package/.claude/commands/mindforge/next.md +105 -0
- package/.claude/commands/mindforge/plan-phase.md +125 -0
- package/.claude/commands/mindforge/plugins.md +40 -0
- package/.claude/commands/mindforge/pr-review.md +41 -0
- package/.claude/commands/mindforge/profile-team.md +23 -0
- package/.claude/commands/mindforge/publish-skill.md +19 -0
- package/.claude/commands/mindforge/quick.md +135 -0
- package/.claude/commands/mindforge/release.md +10 -0
- package/.claude/commands/mindforge/retrospective.md +26 -0
- package/.claude/commands/mindforge/review.md +157 -0
- package/.claude/commands/mindforge/security-scan.md +233 -0
- package/.claude/commands/mindforge/ship.md +100 -0
- package/.claude/commands/mindforge/skills.md +141 -0
- package/.claude/commands/mindforge/status.md +104 -0
- package/.claude/commands/mindforge/sync-confluence.md +11 -0
- package/.claude/commands/mindforge/sync-jira.md +12 -0
- package/.claude/commands/mindforge/tokens.md +8 -0
- package/.claude/commands/mindforge/update.md +42 -0
- package/.claude/commands/mindforge/verify-phase.md +62 -0
- package/.claude/commands/mindforge/workspace.md +29 -0
- package/.forge/org/CONVENTIONS.md +0 -0
- package/.forge/org/ORG.md +0 -0
- package/.forge/org/SECURITY.md +0 -0
- package/.forge/org/TOOLS.md +0 -0
- package/.forge/personas/analyst.md +0 -0
- package/.forge/personas/architect.md +0 -0
- package/.forge/personas/debug-specialist.md +0 -0
- package/.forge/personas/developer.md +26 -0
- package/.forge/personas/qa-engineer.md +0 -0
- package/.forge/personas/release-manager.md +0 -0
- package/.forge/personas/security-reviewer.md +33 -0
- package/.forge/personas/tech-writer.md +0 -0
- package/.forge/skills/api-design/SKILL.md +0 -0
- package/.forge/skills/code-quality/SKILL.md +0 -0
- package/.forge/skills/documentation/SKILL.md +0 -0
- package/.forge/skills/security-review/SKILL.md +23 -0
- package/.forge/skills/testing-standards/SKILL.md +27 -0
- package/.github/workflows/mindforge-ci.yml +224 -0
- package/.gitlab-ci-mindforge.yml +18 -0
- package/.mindforge/MINDFORGE-SCHEMA.json +165 -0
- package/.mindforge/audit/AUDIT-SCHEMA.md +451 -0
- package/.mindforge/ci/ci-config-schema.md +21 -0
- package/.mindforge/ci/ci-mode.md +179 -0
- package/.mindforge/ci/github-actions-adapter.md +224 -0
- package/.mindforge/ci/gitlab-ci-adapter.md +31 -0
- package/.mindforge/ci/jenkins-adapter.md +44 -0
- package/.mindforge/distribution/registry-client.md +166 -0
- package/.mindforge/distribution/registry-schema.md +96 -0
- package/.mindforge/distribution/skill-publisher.md +44 -0
- package/.mindforge/distribution/skill-validator.md +74 -0
- package/.mindforge/engine/compaction-protocol.md +182 -0
- package/.mindforge/engine/context-injector.md +128 -0
- package/.mindforge/engine/dependency-parser.md +113 -0
- package/.mindforge/engine/skills/conflict-resolver.md +69 -0
- package/.mindforge/engine/skills/loader.md +184 -0
- package/.mindforge/engine/skills/registry.md +98 -0
- package/.mindforge/engine/skills/versioning.md +75 -0
- package/.mindforge/engine/verification-pipeline.md +111 -0
- package/.mindforge/engine/wave-executor.md +235 -0
- package/.mindforge/governance/GOVERNANCE-CONFIG.md +17 -0
- package/.mindforge/governance/approval-workflow.md +37 -0
- package/.mindforge/governance/change-classifier.md +63 -0
- package/.mindforge/governance/compliance-gates.md +31 -0
- package/.mindforge/integrations/confluence.md +27 -0
- package/.mindforge/integrations/connection-manager.md +163 -0
- package/.mindforge/integrations/github.md +25 -0
- package/.mindforge/integrations/gitlab.md +13 -0
- package/.mindforge/integrations/jira.md +102 -0
- package/.mindforge/integrations/slack.md +41 -0
- package/.mindforge/intelligence/antipattern-detector.md +75 -0
- package/.mindforge/intelligence/difficulty-scorer.md +55 -0
- package/.mindforge/intelligence/health-engine.md +208 -0
- package/.mindforge/intelligence/skill-gap-analyser.md +40 -0
- package/.mindforge/intelligence/smart-compaction.md +71 -0
- package/.mindforge/metrics/METRICS-SCHEMA.md +42 -0
- package/.mindforge/metrics/quality-tracker.md +32 -0
- package/.mindforge/monorepo/cross-package-planner.md +114 -0
- package/.mindforge/monorepo/dependency-graph-builder.md +32 -0
- package/.mindforge/monorepo/workspace-detector.md +129 -0
- package/.mindforge/org/CONVENTIONS.md +62 -0
- package/.mindforge/org/ORG.md +51 -0
- package/.mindforge/org/SECURITY.md +50 -0
- package/.mindforge/org/TOOLS.md +53 -0
- package/.mindforge/org/integrations/INTEGRATIONS-CONFIG.md +58 -0
- package/.mindforge/org/skills/MANIFEST.md +38 -0
- package/.mindforge/personas/analyst.md +52 -0
- package/.mindforge/personas/architect.md +75 -0
- package/.mindforge/personas/debug-specialist.md +52 -0
- package/.mindforge/personas/developer.md +85 -0
- package/.mindforge/personas/overrides/README.md +85 -0
- package/.mindforge/personas/qa-engineer.md +61 -0
- package/.mindforge/personas/release-manager.md +76 -0
- package/.mindforge/personas/security-reviewer.md +91 -0
- package/.mindforge/personas/tech-writer.md +51 -0
- package/.mindforge/plugins/PLUGINS-MANIFEST.md +23 -0
- package/.mindforge/plugins/plugin-loader.md +93 -0
- package/.mindforge/plugins/plugin-registry.md +44 -0
- package/.mindforge/plugins/plugin-schema.md +68 -0
- package/.mindforge/pr-review/ai-reviewer.md +266 -0
- package/.mindforge/pr-review/finding-formatter.md +46 -0
- package/.mindforge/pr-review/review-prompt-templates.md +44 -0
- package/.mindforge/production/compatibility-layer.md +39 -0
- package/.mindforge/production/migration-engine.md +52 -0
- package/.mindforge/production/production-checklist.md +165 -0
- package/.mindforge/production/token-optimiser.md +68 -0
- package/.mindforge/skills/accessibility/SKILL.md +106 -0
- package/.mindforge/skills/api-design/SKILL.md +98 -0
- package/.mindforge/skills/code-quality/SKILL.md +88 -0
- package/.mindforge/skills/data-privacy/SKILL.md +126 -0
- package/.mindforge/skills/database-patterns/SKILL.md +192 -0
- package/.mindforge/skills/documentation/SKILL.md +91 -0
- package/.mindforge/skills/incident-response/SKILL.md +180 -0
- package/.mindforge/skills/performance/SKILL.md +120 -0
- package/.mindforge/skills/security-review/SKILL.md +83 -0
- package/.mindforge/skills/testing-standards/SKILL.md +97 -0
- package/.mindforge/team/TEAM-PROFILE.md +42 -0
- package/.mindforge/team/multi-handoff.md +23 -0
- package/.mindforge/team/profiles/README.md +13 -0
- package/.mindforge/team/session-merger.md +18 -0
- package/.planning/ARCHITECTURE.md +0 -0
- package/.planning/AUDIT.jsonl +0 -0
- package/.planning/HANDOFF.json +28 -0
- package/.planning/PROJECT.md +33 -0
- package/.planning/RELEASE-CHECKLIST.md +68 -0
- package/.planning/REQUIREMENTS.md +0 -0
- package/.planning/ROADMAP.md +0 -0
- package/.planning/STATE.md +31 -0
- package/.planning/approvals/.gitkeep +1 -0
- package/.planning/archive/.gitkeep +1 -0
- package/.planning/audit-archive/.gitkeep +1 -0
- package/.planning/decisions/.gitkeep +0 -0
- package/.planning/decisions/ADR-001-handoff-tracking.md +41 -0
- package/.planning/decisions/ADR-002-markdown-commands.md +46 -0
- package/.planning/decisions/ADR-003-skills-trigger-model.md +37 -0
- package/.planning/decisions/ADR-004-wave-parallelism-model.md +45 -0
- package/.planning/decisions/ADR-005-append-only-audit-log.md +51 -0
- package/.planning/decisions/ADR-006-tiered-skills-system.md +22 -0
- package/.planning/decisions/ADR-007-trigger-keyword-model.md +22 -0
- package/.planning/decisions/ADR-008-just-in-time-skill-loading.md +29 -0
- package/.planning/decisions/ADR-009-enterprise-integration-retry-policy.md +8 -0
- package/.planning/decisions/ADR-010-governance-tier-escalation.md +8 -0
- package/.planning/decisions/ADR-011-multi-developer-handoff-contract.md +8 -0
- package/.planning/decisions/ADR-012-intelligence-feedback-loops.md +19 -0
- package/.planning/decisions/ADR-013-mindforge-md-constitution.md +16 -0
- package/.planning/decisions/ADR-014-metrics-as-signals-not-evaluation.md +15 -0
- package/.planning/decisions/ADR-015-npm-based-skill-registry.md +26 -0
- package/.planning/decisions/ADR-016-ci-exit-code-0-on-timeout.md +27 -0
- package/.planning/decisions/ADR-017-sdk-localhost-only.md +28 -0
- package/.planning/decisions/ADR-018-installer-self-install-detection.md +15 -0
- package/.planning/decisions/ADR-019-self-update-scope-preservation.md +14 -0
- package/.planning/decisions/ADR-020-v1.0.0-stable-interface-contract.md +23 -0
- package/.planning/jira-sync.json +9 -0
- package/.planning/milestones/.gitkeep +1 -0
- package/.planning/phases/day1/REVIEW-DAY1.md +50 -0
- package/.planning/phases/day1/SECURITY-REVIEW-DAY1.md +15 -0
- package/.planning/phases/day2/REVIEW-DAY2.md +521 -0
- package/.planning/phases/day3/REVIEW-DAY3.md +234 -0
- package/.planning/slack-threads.json +6 -0
- package/CHANGELOG.md +175 -0
- package/LICENSE +21 -0
- package/MINDFORGE.md +76 -0
- package/README.md +182 -0
- package/RELEASENOTES.md +41 -0
- package/SECURITY.md +4 -0
- package/bin/install.js +120 -0
- package/bin/installer-core.js +292 -0
- package/bin/migrations/0.1.0-to-0.5.0.js +37 -0
- package/bin/migrations/0.5.0-to-0.6.0.js +17 -0
- package/bin/migrations/0.6.0-to-1.0.0.js +100 -0
- package/bin/migrations/migrate.js +151 -0
- package/bin/migrations/schema-versions.js +64 -0
- package/bin/updater/changelog-fetcher.js +62 -0
- package/bin/updater/self-update.js +169 -0
- package/bin/updater/version-comparator.js +68 -0
- package/bin/validate-config.js +92 -0
- package/bin/wizard/config-generator.js +112 -0
- package/bin/wizard/environment-detector.js +76 -0
- package/bin/wizard/setup-wizard.js +237 -0
- package/docs/Context/Master-Context.md +701 -0
- package/docs/architecture/README.md +35 -0
- package/docs/architecture/decision-records-index.md +26 -0
- package/docs/ci-cd-integration.md +30 -0
- package/docs/ci-quickstart.md +78 -0
- package/docs/commands-reference.md +11 -0
- package/docs/contributing/CONTRIBUTING.md +38 -0
- package/docs/contributing/plugin-authoring.md +50 -0
- package/docs/contributing/skill-authoring.md +41 -0
- package/docs/enterprise-setup.md +25 -0
- package/docs/faq.md +38 -0
- package/docs/getting-started.md +36 -0
- package/docs/governance-guide.md +23 -0
- package/docs/mindforge-md-reference.md +53 -0
- package/docs/monorepo-guide.md +26 -0
- package/docs/persona-customisation.md +56 -0
- package/docs/quick-verify.md +33 -0
- package/docs/reference/audit-events.md +53 -0
- package/docs/reference/commands.md +82 -0
- package/docs/reference/config-reference.md +64 -0
- package/docs/reference/sdk-api.md +48 -0
- package/docs/reference/skills-api.md +57 -0
- package/docs/release-checklist-guide.md +37 -0
- package/docs/requirements.md +29 -0
- package/docs/sdk-reference.md +27 -0
- package/docs/security/SECURITY.md +42 -0
- package/docs/security/penetration-test-results.md +31 -0
- package/docs/security/threat-model.md +142 -0
- package/docs/skills-authoring-guide.md +119 -0
- package/docs/skills-publishing-guide.md +21 -0
- package/docs/team-setup-guide.md +21 -0
- package/docs/troubleshooting.md +119 -0
- package/docs/tutorial.md +195 -0
- package/docs/upgrade.md +44 -0
- package/docs/user-guide.md +131 -0
- package/docs/usp-features.md +214 -0
- package/eslint.config.mjs +31 -0
- package/examples/starter-project/.planning/AUDIT.jsonl +1 -0
- package/examples/starter-project/.planning/HANDOFF.json +23 -0
- package/examples/starter-project/.planning/PROJECT.md +27 -0
- package/examples/starter-project/.planning/STATE.md +10 -0
- package/examples/starter-project/MINDFORGE.md +40 -0
- package/examples/starter-project/README.md +14 -0
- package/implementation-roadmap/day-1-imp/DAY1-HARDEN.md +823 -0
- package/implementation-roadmap/day-1-imp/DAY1-IMPLEMENT.md +2459 -0
- package/implementation-roadmap/day-1-imp/DAY1-REVIEW.md +288 -0
- package/implementation-roadmap/day-2-imp/DAY2-HARDEN.md +954 -0
- package/implementation-roadmap/day-2-imp/DAY2-IMPLEMENT.md +2347 -0
- package/implementation-roadmap/day-2-imp/DAY2-REVIEW.md +422 -0
- package/implementation-roadmap/day-3-imp/DAY3-HARDEN.md +870 -0
- package/implementation-roadmap/day-3-imp/DAY3-IMPLEMENT.md +2798 -0
- package/implementation-roadmap/day-3-imp/DAY3-REVIEW.md +484 -0
- package/implementation-roadmap/day-4-imp/DAY4-HARDEN.md +1087 -0
- package/implementation-roadmap/day-4-imp/DAY4-IMPLEMENT.md +2874 -0
- package/implementation-roadmap/day-4-imp/DAY4-REVIEW.md +386 -0
- package/implementation-roadmap/day-5-imp/DAY5-HARDEN.md +1078 -0
- package/implementation-roadmap/day-5-imp/DAY5-IMPLEMENT.md +3151 -0
- package/implementation-roadmap/day-5-imp/DAY5-REVIEW.md +345 -0
- package/implementation-roadmap/day-6-imp/DAY6-COMPLETE.md +3919 -0
- package/implementation-roadmap/day-7-imp-prod/DAY7-PRODUCTION-FINAL.md +4513 -0
- package/package.json +31 -0
- package/sdk/README.md +69 -0
- package/sdk/eslint.config.mjs +34 -0
- package/sdk/package-lock.json +1507 -0
- package/sdk/package.json +30 -0
- package/sdk/src/client.ts +133 -0
- package/sdk/src/commands.ts +63 -0
- package/sdk/src/events.ts +166 -0
- package/sdk/src/index.ts +22 -0
- package/sdk/src/types.ts +87 -0
- package/sdk/tsconfig.json +13 -0
- package/tests/audit.test.js +206 -0
- package/tests/ci-mode.test.js +162 -0
- package/tests/compaction.test.js +161 -0
- package/tests/distribution.test.js +205 -0
- package/tests/e2e.test.js +618 -0
- package/tests/governance.test.js +130 -0
- package/tests/install.test.js +209 -0
- package/tests/integrations.test.js +128 -0
- package/tests/intelligence.test.js +117 -0
- package/tests/metrics.test.js +96 -0
- package/tests/migration.test.js +309 -0
- package/tests/production.test.js +416 -0
- package/tests/sdk.test.js +200 -0
- package/tests/skills-platform.test.js +403 -0
- package/tests/wave-engine.test.js +338 -0
|
@@ -0,0 +1,618 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MindForge Day 7 — End-to-End Simulation Tests
|
|
3
|
+
* Simulates complete project workflows using file system operations.
|
|
4
|
+
* No actual Claude API calls — tests the state machine, not the AI.
|
|
5
|
+
*
|
|
6
|
+
* Run: node tests/e2e.test.js
|
|
7
|
+
*/
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const os = require('os');
|
|
13
|
+
const assert = require('assert');
|
|
14
|
+
let passed = 0, failed = 0;
|
|
15
|
+
const cleanupFailures = [];
|
|
16
|
+
|
|
17
|
+
function test(name, fn) {
|
|
18
|
+
try { fn(); console.log(` ✅ ${name}`); passed++; }
|
|
19
|
+
catch(e) { console.error(` ❌ ${name}\n ${e.message}`); failed++; }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// ── Test project factory ───────────────────────────────────────────────────────
|
|
23
|
+
function createTestProject() {
|
|
24
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mindforge-e2e-'));
|
|
25
|
+
|
|
26
|
+
function write(relPath, content) {
|
|
27
|
+
const fullPath = path.join(tmpDir, relPath);
|
|
28
|
+
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
|
29
|
+
fs.writeFileSync(fullPath, content, 'utf8');
|
|
30
|
+
return fullPath;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function read(relPath) {
|
|
34
|
+
const p = path.join(tmpDir, relPath);
|
|
35
|
+
return fs.existsSync(p) ? fs.readFileSync(p, 'utf8') : null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function exists(relPath) {
|
|
39
|
+
return fs.existsSync(path.join(tmpDir, relPath));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function appendAudit(entry) {
|
|
43
|
+
const auditPath = path.join(tmpDir, '.planning', 'AUDIT.jsonl');
|
|
44
|
+
const line = JSON.stringify({
|
|
45
|
+
id: `test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
46
|
+
timestamp: new Date().toISOString(),
|
|
47
|
+
session_id: 'test-session-001',
|
|
48
|
+
agent: 'mindforge-test',
|
|
49
|
+
...entry,
|
|
50
|
+
});
|
|
51
|
+
fs.appendFileSync(auditPath, line + '\n');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function cleanup() {
|
|
55
|
+
try { fs.rmSync(tmpDir, { recursive: true, force: true }); }
|
|
56
|
+
catch(e) {
|
|
57
|
+
cleanupFailures.push(tmpDir);
|
|
58
|
+
console.warn(` ⚠️ Cleanup warning: ${e.message} (dir: ${tmpDir})`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return { tmpDir, write, read, exists, appendAudit, cleanup };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ── Workflow simulation functions ──────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
function initProject(project) {
|
|
68
|
+
const { write } = project;
|
|
69
|
+
|
|
70
|
+
// Core .planning/ files
|
|
71
|
+
write('.planning/PROJECT.md', `# E2E Test Project
|
|
72
|
+
|
|
73
|
+
## Tech stack
|
|
74
|
+
- Node.js 20 LTS
|
|
75
|
+
- TypeScript 5.x
|
|
76
|
+
- PostgreSQL via Prisma
|
|
77
|
+
|
|
78
|
+
## v1 scope — IN
|
|
79
|
+
- [x] User authentication (email/password)
|
|
80
|
+
- [x] User profile management
|
|
81
|
+
|
|
82
|
+
## Success criteria
|
|
83
|
+
All acceptance tests passing. Zero HIGH security findings.
|
|
84
|
+
`);
|
|
85
|
+
|
|
86
|
+
write('.planning/REQUIREMENTS.md', `# Requirements
|
|
87
|
+
|
|
88
|
+
## Functional requirements
|
|
89
|
+
| ID | Requirement | Acceptance criterion | Scope |
|
|
90
|
+
|---|---|---|---|
|
|
91
|
+
| FR-01 | User login | POST /auth/login returns JWT for valid credentials | v1 |
|
|
92
|
+
| FR-02 | User logout | POST /auth/logout invalidates session | v1 |
|
|
93
|
+
| FR-03 | Profile fetch | GET /users/:id returns profile for authenticated user | v1 |
|
|
94
|
+
`);
|
|
95
|
+
|
|
96
|
+
write('.planning/ARCHITECTURE.md', `# Architecture
|
|
97
|
+
|
|
98
|
+
## Architectural pattern
|
|
99
|
+
Modular monolith — Express API with Prisma ORM
|
|
100
|
+
|
|
101
|
+
## Technology stack
|
|
102
|
+
- Runtime: Node.js 20
|
|
103
|
+
- Framework: Express 4.x
|
|
104
|
+
- Database: PostgreSQL 15
|
|
105
|
+
|
|
106
|
+
## Data model
|
|
107
|
+
**User**: id (UUID), email, passwordHash, createdAt, updatedAt
|
|
108
|
+
`);
|
|
109
|
+
|
|
110
|
+
write('.planning/STATE.md', `# Project State
|
|
111
|
+
|
|
112
|
+
## Status
|
|
113
|
+
Phase 1 in progress
|
|
114
|
+
|
|
115
|
+
## Current phase
|
|
116
|
+
Phase 1 — Authentication
|
|
117
|
+
|
|
118
|
+
## Next action
|
|
119
|
+
Execute Phase 1 plans
|
|
120
|
+
`);
|
|
121
|
+
|
|
122
|
+
write('.planning/HANDOFF.json', JSON.stringify({
|
|
123
|
+
schema_version: '1.0.0',
|
|
124
|
+
plugin_api_version: '1.0.0',
|
|
125
|
+
project: 'E2E Test Project',
|
|
126
|
+
phase: 1,
|
|
127
|
+
plan: null,
|
|
128
|
+
next_task: 'Execute Phase 1 — Authentication',
|
|
129
|
+
blockers: [],
|
|
130
|
+
decisions_needed: [],
|
|
131
|
+
decisions_made: [],
|
|
132
|
+
discoveries: [],
|
|
133
|
+
implicit_knowledge: [],
|
|
134
|
+
quality_signals: [],
|
|
135
|
+
context_refs: ['.planning/PROJECT.md', '.planning/STATE.md'],
|
|
136
|
+
recent_commits: [],
|
|
137
|
+
recent_files: [],
|
|
138
|
+
session_id: 'test-session-001',
|
|
139
|
+
_warning: 'Never store secrets or tokens in this file.',
|
|
140
|
+
updated_at: new Date().toISOString(),
|
|
141
|
+
}, null, 2));
|
|
142
|
+
|
|
143
|
+
write('.planning/AUDIT.jsonl', '');
|
|
144
|
+
project.appendAudit({ event: 'project_initialised', phase: null });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function planPhase(project, phaseNum) {
|
|
148
|
+
const { write, appendAudit } = project;
|
|
149
|
+
const phaseDir = `.planning/phases/${phaseNum}`;
|
|
150
|
+
|
|
151
|
+
// Difficulty score
|
|
152
|
+
write(`${phaseDir}/DIFFICULTY-SCORE-${phaseNum}.md`, `# Difficulty Score — Phase ${phaseNum}
|
|
153
|
+
## Scores
|
|
154
|
+
| Dimension | Score |
|
|
155
|
+
|---|---|
|
|
156
|
+
| Technical | 4/5 |
|
|
157
|
+
| Risk | 5/5 |
|
|
158
|
+
| Ambiguity | 2/5 |
|
|
159
|
+
| Dependencies | 2/5 |
|
|
160
|
+
## Composite: 3.75 — Challenging
|
|
161
|
+
## Recommendations: 6 atomic tasks
|
|
162
|
+
`);
|
|
163
|
+
|
|
164
|
+
// Plan file with valid XML
|
|
165
|
+
write(`${phaseDir}/PLAN-${phaseNum}-01.md`, `<task type="auto">
|
|
166
|
+
<n>Implement login endpoint with JWT</n>
|
|
167
|
+
<persona>developer</persona>
|
|
168
|
+
<phase>${phaseNum}</phase>
|
|
169
|
+
<plan>01</plan>
|
|
170
|
+
<dependencies>none</dependencies>
|
|
171
|
+
<files>
|
|
172
|
+
src/auth/login.ts
|
|
173
|
+
src/auth/login.test.ts
|
|
174
|
+
</files>
|
|
175
|
+
<context>
|
|
176
|
+
Skills: security-review (jwt.sign trigger), api-design, testing-standards
|
|
177
|
+
</context>
|
|
178
|
+
<action>
|
|
179
|
+
Create POST /auth/login endpoint. Validate email/password against database.
|
|
180
|
+
Use argon2 for password verification. Return signed JWT on success.
|
|
181
|
+
Return 401 for invalid credentials (no information disclosure).
|
|
182
|
+
</action>
|
|
183
|
+
<verify>npm test -- --testPathPattern=auth.login</verify>
|
|
184
|
+
<done>All login tests passing, argon2 verification working, JWT returned</done>
|
|
185
|
+
</task>`);
|
|
186
|
+
|
|
187
|
+
write(`${phaseDir}/PLAN-${phaseNum}-02.md`, `<task type="auto">
|
|
188
|
+
<n>Implement logout endpoint with session invalidation</n>
|
|
189
|
+
<persona>developer</persona>
|
|
190
|
+
<phase>${phaseNum}</phase>
|
|
191
|
+
<plan>02</plan>
|
|
192
|
+
<dependencies>01</dependencies>
|
|
193
|
+
<files>
|
|
194
|
+
src/auth/logout.ts
|
|
195
|
+
src/auth/logout.test.ts
|
|
196
|
+
</files>
|
|
197
|
+
<context>
|
|
198
|
+
Skills: security-review, testing-standards
|
|
199
|
+
</context>
|
|
200
|
+
<action>
|
|
201
|
+
Create POST /auth/logout endpoint. Invalidate JWT via blocklist in Redis.
|
|
202
|
+
Verify that blocklisted tokens return 401 on subsequent requests.
|
|
203
|
+
</action>
|
|
204
|
+
<verify>npm test -- --testPathPattern=auth.logout</verify>
|
|
205
|
+
<done>Logout invalidates tokens, blocklisted tokens rejected</done>
|
|
206
|
+
</task>`);
|
|
207
|
+
|
|
208
|
+
// Dependency graph
|
|
209
|
+
write(`${phaseDir}/DEPENDENCY-GRAPH-${phaseNum}.md`, `# Dependency Graph — Phase ${phaseNum}
|
|
210
|
+
|
|
211
|
+
## Wave 1 (independent)
|
|
212
|
+
- Plan 01: Implement login endpoint
|
|
213
|
+
|
|
214
|
+
## Wave 2 (depends on Wave 1)
|
|
215
|
+
- Plan 02: Implement logout endpoint (requires login implementation)
|
|
216
|
+
`);
|
|
217
|
+
|
|
218
|
+
appendAudit({ event: 'phase_planned', phase: phaseNum, plans_created: 2, waves: 2 });
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function executeTask(project, phaseNum, planId, commitSha) {
|
|
222
|
+
const { write, appendAudit } = project;
|
|
223
|
+
const phaseDir = `.planning/phases/${phaseNum}`;
|
|
224
|
+
|
|
225
|
+
appendAudit({ event: 'task_started', phase: phaseNum, plan: planId });
|
|
226
|
+
|
|
227
|
+
write(`${phaseDir}/SUMMARY-${phaseNum}-${planId}.md`, `# Summary — Phase ${phaseNum}, Plan ${planId}
|
|
228
|
+
## Status: Completed ✅
|
|
229
|
+
## Commit: ${commitSha}
|
|
230
|
+
## Verify result: PASS
|
|
231
|
+
## Files modified:
|
|
232
|
+
- src/auth/${planId === '01' ? 'login' : 'logout'}.ts (created)
|
|
233
|
+
- src/auth/${planId === '01' ? 'login' : 'logout'}.test.ts (created, 8 tests)
|
|
234
|
+
`);
|
|
235
|
+
|
|
236
|
+
appendAudit({ event: 'task_completed', phase: phaseNum, plan: planId, commit_sha: commitSha, verify_result: 'pass' });
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function runSecurityScan(project, phaseNum, findings = []) {
|
|
240
|
+
const { write, appendAudit } = project;
|
|
241
|
+
const phaseDir = `.planning/phases/${phaseNum}`;
|
|
242
|
+
|
|
243
|
+
const criticalCount = findings.filter(f => f.severity === 'CRITICAL').length;
|
|
244
|
+
const highCount = findings.filter(f => f.severity === 'HIGH').length;
|
|
245
|
+
|
|
246
|
+
write(`${phaseDir}/SECURITY-REVIEW-${phaseNum}.md`, `# Security Review — Phase ${phaseNum}
|
|
247
|
+
## Scan date: ${new Date().toISOString()}
|
|
248
|
+
## OWASP A01: ${findings.length === 0 ? 'PASS ✅' : 'FINDINGS'}
|
|
249
|
+
|
|
250
|
+
${findings.map(f => `### ${f.severity}: ${f.description}\nFile: ${f.file}:${f.line}\nRemediation: ${f.remediation}`).join('\n\n')}
|
|
251
|
+
|
|
252
|
+
## Summary
|
|
253
|
+
- CRITICAL: ${criticalCount}
|
|
254
|
+
- HIGH: ${highCount}
|
|
255
|
+
- Total: ${findings.length}
|
|
256
|
+
${findings.length === 0 ? '## Verdict: CLEAN ✅' : `## Verdict: ${criticalCount > 0 ? '🔴 BLOCKED' : '⚠️ REVIEW REQUIRED'}`}
|
|
257
|
+
`);
|
|
258
|
+
|
|
259
|
+
appendAudit({ event: 'security_scan_completed', phase: phaseNum, critical: criticalCount, high: highCount });
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function verifyPhase(project, phaseNum) {
|
|
263
|
+
const { write, appendAudit } = project;
|
|
264
|
+
const phaseDir = `.planning/phases/${phaseNum}`;
|
|
265
|
+
|
|
266
|
+
write(`${phaseDir}/GATE-RESULTS-${phaseNum}.md`, `# Compliance Gate Results — Phase ${phaseNum}
|
|
267
|
+
## Run at: ${new Date().toISOString()}
|
|
268
|
+
| Gate | Status | Detail |
|
|
269
|
+
|---|---|---|
|
|
270
|
+
| Secret detection | ✅ PASS | 0 patterns found |
|
|
271
|
+
| CRITICAL security findings | ✅ PASS | No open CRITICAL findings |
|
|
272
|
+
| Test suite | ✅ PASS | 16 tests passing |
|
|
273
|
+
| Dependency CVEs | ✅ PASS | 0 HIGH/CRITICAL |
|
|
274
|
+
| GDPR retention | ✅ PASS | data-privacy skill not active |
|
|
275
|
+
## Overall: ✅ ALL BLOCKING GATES PASSED
|
|
276
|
+
`);
|
|
277
|
+
|
|
278
|
+
write(`${phaseDir}/VERIFICATION-${phaseNum}.md`, `# Verification — Phase ${phaseNum}
|
|
279
|
+
## Stage 1 — Tests: 16/16 passing ✅
|
|
280
|
+
## Stage 2 — Requirements:
|
|
281
|
+
| FR-01 | User login | ✅ | src/auth/login.ts:24 |
|
|
282
|
+
| FR-02 | User logout | ✅ | src/auth/logout.ts:18 |
|
|
283
|
+
## Stage 3 — Type check: PASS ✅
|
|
284
|
+
## Stage 4 — Security: PASS ✅
|
|
285
|
+
## Overall: ✅ PHASE VERIFIED
|
|
286
|
+
`);
|
|
287
|
+
|
|
288
|
+
write(`${phaseDir}/UAT-${phaseNum}.md`, `# UAT — Phase ${phaseNum}
|
|
289
|
+
## Test results
|
|
290
|
+
| # | Deliverable | Result | Notes |
|
|
291
|
+
|---|---|---|---|
|
|
292
|
+
| 1 | POST /auth/login returns JWT | ✅ | Tested with valid+invalid credentials |
|
|
293
|
+
| 2 | POST /auth/logout invalidates session | ✅ | Blocklist verified |
|
|
294
|
+
## Overall: ✅ ALL UAT PASSED
|
|
295
|
+
`);
|
|
296
|
+
|
|
297
|
+
appendAudit({ event: 'phase_completed', phase: phaseNum, uat_pass_rate: 1.0 });
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// ── Tests ──────────────────────────────────────────────────────────────────────
|
|
301
|
+
console.log('\nMindForge Day 7 — End-to-End Tests\n');
|
|
302
|
+
|
|
303
|
+
// ── Test 1: Complete greenfield workflow ───────────────────────────────────────
|
|
304
|
+
console.log('Greenfield project workflow:');
|
|
305
|
+
const gf = createTestProject();
|
|
306
|
+
|
|
307
|
+
try {
|
|
308
|
+
test('init-project creates all required .planning/ files', () => {
|
|
309
|
+
initProject(gf);
|
|
310
|
+
const required = ['PROJECT.md', 'REQUIREMENTS.md', 'ARCHITECTURE.md', 'STATE.md', 'HANDOFF.json', 'AUDIT.jsonl'];
|
|
311
|
+
required.forEach(f => assert.ok(gf.exists(`.planning/${f}`), `Missing: ${f}`));
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
test('HANDOFF.json has v1.0.0 schema_version and plugin_api_version', () => {
|
|
315
|
+
const handoff = JSON.parse(gf.read('.planning/HANDOFF.json'));
|
|
316
|
+
assert.strictEqual(handoff.schema_version, '1.0.0');
|
|
317
|
+
assert.strictEqual(handoff.plugin_api_version, '1.0.0');
|
|
318
|
+
assert.ok(handoff._warning, 'Should have _warning field');
|
|
319
|
+
assert.ok(!handoff._warning.toLowerCase().includes('password'), '_warning should not contain password');
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
test('initial AUDIT.jsonl has project_initialised event with session_id', () => {
|
|
323
|
+
const lines = gf.read('.planning/AUDIT.jsonl').split('\n').filter(Boolean);
|
|
324
|
+
assert.ok(lines.length >= 1, 'Should have at least one audit entry');
|
|
325
|
+
const first = JSON.parse(lines[0]);
|
|
326
|
+
assert.strictEqual(first.event, 'project_initialised');
|
|
327
|
+
assert.ok(first.session_id, 'Should have session_id');
|
|
328
|
+
assert.ok(first.id, 'Should have id');
|
|
329
|
+
assert.ok(first.timestamp, 'Should have timestamp');
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
test('plan-phase creates PLAN files with valid XML structure', () => {
|
|
333
|
+
planPhase(gf, 1);
|
|
334
|
+
const plan = gf.read('.planning/phases/1/PLAN-1-01.md');
|
|
335
|
+
assert.ok(plan, 'PLAN-1-01.md should exist');
|
|
336
|
+
assert.ok(plan.includes('<task type="auto">'), 'Should have task element');
|
|
337
|
+
assert.ok(plan.includes('<n>'), 'Should have task name');
|
|
338
|
+
assert.ok(plan.includes('<persona>'), 'Should specify persona');
|
|
339
|
+
assert.ok(plan.includes('<dependencies>'), 'Should have dependencies');
|
|
340
|
+
assert.ok(plan.includes('<verify>'), 'Should have verify step');
|
|
341
|
+
assert.ok(plan.includes('<done>'), 'Should have definition of done');
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
test('difficulty score file created before plans', () => {
|
|
345
|
+
assert.ok(gf.exists('.planning/phases/1/DIFFICULTY-SCORE-1.md'));
|
|
346
|
+
const score = gf.read('.planning/phases/1/DIFFICULTY-SCORE-1.md');
|
|
347
|
+
assert.ok(score.includes('Composite'), 'Should have composite score');
|
|
348
|
+
assert.ok(score.includes('Challenging'), 'Should have difficulty label');
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
test('dependency graph created and shows wave structure', () => {
|
|
352
|
+
const dep = gf.read('.planning/phases/1/DEPENDENCY-GRAPH-1.md');
|
|
353
|
+
assert.ok(dep, 'Dependency graph should exist');
|
|
354
|
+
assert.ok(dep.includes('Wave 1'), 'Should define Wave 1');
|
|
355
|
+
assert.ok(dep.includes('Wave 2'), 'Should define Wave 2');
|
|
356
|
+
assert.ok(dep.includes('Plan 01'), 'Should reference Plan 01');
|
|
357
|
+
assert.ok(dep.includes('Plan 02'), 'Should reference Plan 02');
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
test('execute task creates SUMMARY with commit SHA', () => {
|
|
361
|
+
executeTask(gf, 1, '01', 'abc1234ef');
|
|
362
|
+
const summary = gf.read('.planning/phases/1/SUMMARY-1-01.md');
|
|
363
|
+
assert.ok(summary, 'SUMMARY-1-01.md should exist');
|
|
364
|
+
assert.ok(summary.includes('Completed ✅'), 'Should show completed status');
|
|
365
|
+
assert.ok(summary.includes('abc1234ef'), 'Should include commit SHA');
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
test('task execution writes task_started and task_completed to AUDIT.jsonl', () => {
|
|
369
|
+
executeTask(gf, 1, '02', 'def5678ab');
|
|
370
|
+
const lines = gf.read('.planning/AUDIT.jsonl').split('\n').filter(Boolean);
|
|
371
|
+
const events = lines.map(l => JSON.parse(l).event);
|
|
372
|
+
assert.ok(events.includes('task_started'), 'Should have task_started event');
|
|
373
|
+
assert.ok(events.includes('task_completed'), 'Should have task_completed event');
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
test('security scan writes SECURITY-REVIEW file', () => {
|
|
377
|
+
runSecurityScan(gf, 1, []);
|
|
378
|
+
assert.ok(gf.exists('.planning/phases/1/SECURITY-REVIEW-1.md'));
|
|
379
|
+
const review = gf.read('.planning/phases/1/SECURITY-REVIEW-1.md');
|
|
380
|
+
assert.ok(review.includes('CLEAN ✅') || review.includes('PASS'), 'Should show passing result');
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
test('verify-phase creates GATE-RESULTS, VERIFICATION, and UAT files', () => {
|
|
384
|
+
verifyPhase(gf, 1);
|
|
385
|
+
assert.ok(gf.exists('.planning/phases/1/GATE-RESULTS-1.md'), 'GATE-RESULTS should exist');
|
|
386
|
+
assert.ok(gf.exists('.planning/phases/1/VERIFICATION-1.md'), 'VERIFICATION should exist');
|
|
387
|
+
assert.ok(gf.exists('.planning/phases/1/UAT-1.md'), 'UAT should exist');
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
test('GATE-RESULTS shows all 5 gates passing', () => {
|
|
391
|
+
const gates = gf.read('.planning/phases/1/GATE-RESULTS-1.md');
|
|
392
|
+
assert.ok(gates.includes('Secret detection'), 'Should have secret detection gate');
|
|
393
|
+
assert.ok(gates.includes('CRITICAL security'), 'Should have CRITICAL findings gate');
|
|
394
|
+
assert.ok(gates.includes('Test suite'), 'Should have test suite gate');
|
|
395
|
+
assert.ok(gates.includes('✅ PASS'), 'Gates should pass');
|
|
396
|
+
assert.ok(gates.includes('ALL BLOCKING GATES PASSED'), 'Should confirm all gates passed');
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
test('VERIFICATION.md references requirements with traceability', () => {
|
|
400
|
+
const v = gf.read('.planning/phases/1/VERIFICATION-1.md');
|
|
401
|
+
assert.ok(v.includes('FR-01'), 'Should reference FR-01');
|
|
402
|
+
assert.ok(v.includes('FR-02'), 'Should reference FR-02');
|
|
403
|
+
assert.ok(v.includes('src/auth/'), 'Should reference source files');
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
// Complete audit log validation
|
|
407
|
+
test('full workflow: all AUDIT.jsonl entries are valid with required fields', () => {
|
|
408
|
+
const lines = gf.read('.planning/AUDIT.jsonl').split('\n').filter(Boolean);
|
|
409
|
+
assert.ok(lines.length >= 6, `Should have >= 6 audit entries, got ${lines.length}`);
|
|
410
|
+
|
|
411
|
+
lines.forEach((line, i) => {
|
|
412
|
+
let entry;
|
|
413
|
+
assert.doesNotThrow(() => { entry = JSON.parse(line); }, `Line ${i+1} is not valid JSON`);
|
|
414
|
+
assert.ok(entry.id, `Line ${i+1}: missing 'id'`);
|
|
415
|
+
assert.ok(entry.timestamp, `Line ${i+1}: missing 'timestamp'`);
|
|
416
|
+
assert.ok(entry.event, `Line ${i+1}: missing 'event'`);
|
|
417
|
+
assert.ok(entry.session_id, `Line ${i+1}: missing 'session_id'`);
|
|
418
|
+
assert.ok(entry.agent, `Line ${i+1}: missing 'agent'`);
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
test('full workflow: AUDIT.jsonl events cover complete lifecycle', () => {
|
|
423
|
+
const lines = gf.read('.planning/AUDIT.jsonl').split('\n').filter(Boolean);
|
|
424
|
+
const events = new Set(lines.map(l => JSON.parse(l).event));
|
|
425
|
+
assert.ok(events.has('project_initialised'), 'Missing: project_initialised');
|
|
426
|
+
assert.ok(events.has('phase_planned'), 'Missing: phase_planned');
|
|
427
|
+
assert.ok(events.has('task_started'), 'Missing: task_started');
|
|
428
|
+
assert.ok(events.has('task_completed'), 'Missing: task_completed');
|
|
429
|
+
assert.ok(events.has('phase_completed'), 'Missing: phase_completed');
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
} finally {
|
|
433
|
+
gf.cleanup();
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// ── Test 2: Brownfield / map-codebase workflow ─────────────────────────────────
|
|
437
|
+
console.log('\nBrownfield project workflow:');
|
|
438
|
+
const bf = createTestProject();
|
|
439
|
+
|
|
440
|
+
try {
|
|
441
|
+
// Simulate what /mindforge:map-codebase produces
|
|
442
|
+
test('map-codebase creates CONVENTIONS.md with DRAFT status marker', () => {
|
|
443
|
+
bf.write('.mindforge/org/CONVENTIONS.md', `# Coding Conventions — E2E Test Project
|
|
444
|
+
# Source: Inferred from codebase analysis by MindForge
|
|
445
|
+
# Status: DRAFT — confirm with team before treating as authoritative
|
|
446
|
+
|
|
447
|
+
## IMPORTANT
|
|
448
|
+
These conventions were inferred from code analysis.
|
|
449
|
+
Review each section and mark as [CONFIRMED] or [NEEDS REVIEW].
|
|
450
|
+
|
|
451
|
+
## Naming conventions [NEEDS REVIEW]
|
|
452
|
+
- Variables: camelCase
|
|
453
|
+
- Files: kebab-case
|
|
454
|
+
- Classes: PascalCase
|
|
455
|
+
|
|
456
|
+
## Import order [NEEDS REVIEW]
|
|
457
|
+
- Node.js built-ins
|
|
458
|
+
- Third-party libraries
|
|
459
|
+
- Internal modules
|
|
460
|
+
`);
|
|
461
|
+
const content = bf.read('.mindforge/org/CONVENTIONS.md');
|
|
462
|
+
assert.ok(content.includes('DRAFT'), 'Should be marked as DRAFT');
|
|
463
|
+
assert.ok(content.includes('NEEDS REVIEW'), 'Should have review markers');
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
test('map-codebase creates ARCHITECTURE.md with inferred stack', () => {
|
|
467
|
+
bf.write('.planning/ARCHITECTURE.md', `# Architecture — E2E Test Project
|
|
468
|
+
## MindForge onboarding: Inferred from codebase
|
|
469
|
+
## Technology stack
|
|
470
|
+
- Runtime: Node.js 20 (inferred from .nvmrc)
|
|
471
|
+
- Framework: Express 4.x (inferred from package.json)
|
|
472
|
+
- Database: PostgreSQL via Prisma (inferred from prisma/schema.prisma)
|
|
473
|
+
## Quality baseline
|
|
474
|
+
- Tests: Vitest, ~200 test files
|
|
475
|
+
- Linting: ESLint configured
|
|
476
|
+
- CI/CD: GitHub Actions
|
|
477
|
+
`);
|
|
478
|
+
const arch = bf.read('.planning/ARCHITECTURE.md');
|
|
479
|
+
assert.ok(arch, 'ARCHITECTURE.md should exist');
|
|
480
|
+
assert.ok(arch.includes('inferred') || arch.includes('Inferred'), 'Should note inferred content');
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
test('STATE.md from map-codebase shows ready-for-planning status', () => {
|
|
484
|
+
bf.write('.planning/STATE.md', `# Project State
|
|
485
|
+
|
|
486
|
+
## Status
|
|
487
|
+
Codebase mapped. Ready to plan first phase.
|
|
488
|
+
|
|
489
|
+
## Current phase
|
|
490
|
+
None — run /mindforge:plan-phase 1 to begin.
|
|
491
|
+
|
|
492
|
+
## Last action
|
|
493
|
+
/mindforge:map-codebase completed — codebase analysis done.
|
|
494
|
+
`);
|
|
495
|
+
const state = bf.read('.planning/STATE.md');
|
|
496
|
+
assert.ok(state.includes('map'), 'STATE.md should reference map-codebase');
|
|
497
|
+
assert.ok(state.includes('plan'), 'STATE.md should suggest next step');
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
} finally {
|
|
501
|
+
bf.cleanup();
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// ── Test 3: Multi-developer handoff scenario ──────────────────────────────────
|
|
505
|
+
console.log('\nMulti-developer handoff scenario:');
|
|
506
|
+
const md = createTestProject();
|
|
507
|
+
|
|
508
|
+
try {
|
|
509
|
+
test('per-developer handoffs are distinct and non-conflicting', () => {
|
|
510
|
+
initProject(md);
|
|
511
|
+
const base = JSON.parse(md.read('.planning/HANDOFF.json'));
|
|
512
|
+
|
|
513
|
+
const alice = {
|
|
514
|
+
...base,
|
|
515
|
+
developer_id: 'alice',
|
|
516
|
+
session_id: 'sess-alice',
|
|
517
|
+
recent_files: ['src/auth/login.ts'],
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
const bob = {
|
|
521
|
+
...base,
|
|
522
|
+
developer_id: 'bob',
|
|
523
|
+
session_id: 'sess-bob',
|
|
524
|
+
recent_files: ['src/auth/logout.ts'],
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
md.write('.planning/HANDOFF-alice.json', JSON.stringify(alice, null, 2));
|
|
528
|
+
md.write('.planning/HANDOFF-bob.json', JSON.stringify(bob, null, 2));
|
|
529
|
+
md.write('.planning/HANDOFF.json', JSON.stringify({
|
|
530
|
+
...base,
|
|
531
|
+
active_developers: ['alice', 'bob'],
|
|
532
|
+
}, null, 2));
|
|
533
|
+
|
|
534
|
+
const a = JSON.parse(md.read('.planning/HANDOFF-alice.json'));
|
|
535
|
+
const b = JSON.parse(md.read('.planning/HANDOFF-bob.json'));
|
|
536
|
+
const main = JSON.parse(md.read('.planning/HANDOFF.json'));
|
|
537
|
+
|
|
538
|
+
assert.notStrictEqual(a.session_id, b.session_id, 'Session IDs should be distinct');
|
|
539
|
+
assert.notDeepStrictEqual(a.recent_files, b.recent_files, 'Recent files should differ');
|
|
540
|
+
assert.ok(Array.isArray(main.active_developers), 'active_developers should exist');
|
|
541
|
+
assert.ok(main.active_developers.includes('alice'), 'alice should be in active_developers');
|
|
542
|
+
assert.ok(main.active_developers.includes('bob'), 'bob should be in active_developers');
|
|
543
|
+
});
|
|
544
|
+
} finally {
|
|
545
|
+
md.cleanup();
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// ── Test 4: Context compaction scenario ───────────────────────────────────────
|
|
549
|
+
console.log('\nContext compaction scenario:');
|
|
550
|
+
const cc = createTestProject();
|
|
551
|
+
|
|
552
|
+
try {
|
|
553
|
+
test('Level 2 compaction adds decisions_made and implicit_knowledge', () => {
|
|
554
|
+
initProject(cc);
|
|
555
|
+
const handoff = JSON.parse(cc.read('.planning/HANDOFF.json'));
|
|
556
|
+
handoff.decisions_made = ['Adopted JWT auth for v1'];
|
|
557
|
+
handoff.implicit_knowledge = ['Token revocation uses Redis blocklist'];
|
|
558
|
+
cc.write('.planning/HANDOFF.json', JSON.stringify(handoff, null, 2));
|
|
559
|
+
|
|
560
|
+
const updated = JSON.parse(cc.read('.planning/HANDOFF.json'));
|
|
561
|
+
assert.ok(Array.isArray(updated.decisions_made), 'decisions_made should be array');
|
|
562
|
+
assert.ok(Array.isArray(updated.implicit_knowledge), 'implicit_knowledge should be array');
|
|
563
|
+
assert.ok(updated.decisions_made.length >= 1, 'decisions_made should be populated');
|
|
564
|
+
assert.ok(updated.implicit_knowledge.length >= 1, 'implicit_knowledge should be populated');
|
|
565
|
+
});
|
|
566
|
+
} finally {
|
|
567
|
+
cc.cleanup();
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// ── Test 5: Security gate scenarios ───────────────────────────────────────────
|
|
571
|
+
console.log('\nSecurity gate scenarios:');
|
|
572
|
+
const sg = createTestProject();
|
|
573
|
+
|
|
574
|
+
try {
|
|
575
|
+
test('CRITICAL security finding in review blocks phase completion indicator', () => {
|
|
576
|
+
initProject(sg);
|
|
577
|
+
planPhase(sg, 1);
|
|
578
|
+
executeTask(sg, 1, '01', 'abc1234');
|
|
579
|
+
|
|
580
|
+
// Run security scan with a CRITICAL finding
|
|
581
|
+
runSecurityScan(sg, 1, [{
|
|
582
|
+
severity: 'CRITICAL',
|
|
583
|
+
description: 'SQL injection via unsanitised user input',
|
|
584
|
+
file: 'src/db/users.ts',
|
|
585
|
+
line: 47,
|
|
586
|
+
remediation: 'Use parameterised queries: db.query($1, [id])',
|
|
587
|
+
}]);
|
|
588
|
+
|
|
589
|
+
const review = sg.read('.planning/phases/1/SECURITY-REVIEW-1.md');
|
|
590
|
+
assert.ok(review.includes('CRITICAL'), 'Should show CRITICAL finding');
|
|
591
|
+
assert.ok(review.includes('BLOCKED') || review.includes('0 CRITICAL') === false, 'Should indicate blocked state');
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
test('AUDIT.jsonl captures security findings with correct schema', () => {
|
|
595
|
+
const lines = sg.read('.planning/AUDIT.jsonl').split('\n').filter(Boolean);
|
|
596
|
+
const secScan = lines.map(l => JSON.parse(l)).find(e => e.event === 'security_scan_completed');
|
|
597
|
+
assert.ok(secScan, 'Should have security_scan_completed audit entry');
|
|
598
|
+
assert.strictEqual(secScan.critical, 1, 'Should record 1 critical finding');
|
|
599
|
+
assert.ok(secScan.session_id, 'Security scan audit entry should have session_id');
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
} finally {
|
|
603
|
+
sg.cleanup();
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// ── Results ─────────────────────────────────────────────────────────────────────
|
|
607
|
+
console.log(`\n${'─'.repeat(55)}`);
|
|
608
|
+
console.log(`Results: ${passed} passed, ${failed} failed`);
|
|
609
|
+
if (cleanupFailures.length > 0) {
|
|
610
|
+
console.warn(`\n⚠️ ${cleanupFailures.length} test directories were not cleaned up:`);
|
|
611
|
+
cleanupFailures.forEach(p => console.warn(` - ${p}`));
|
|
612
|
+
}
|
|
613
|
+
if (failed > 0) {
|
|
614
|
+
console.error(`\n❌ ${failed} test(s) failed.\n`);
|
|
615
|
+
process.exit(1);
|
|
616
|
+
} else {
|
|
617
|
+
console.log(`\n✅ All E2E tests passed.\n`);
|
|
618
|
+
}
|