@polymorphism-tech/morph-spec 4.8.1 → 4.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +2 -2
  2. package/claude-plugin.json +1 -1
  3. package/docs/CHEATSHEET.md +1 -1
  4. package/docs/QUICKSTART.md +1 -1
  5. package/framework/hooks/dev/guard-version-numbers.js +1 -1
  6. package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +1 -1
  7. package/framework/skills/level-1-workflows/phase-codebase-analysis/SKILL.md +1 -1
  8. package/framework/skills/level-1-workflows/phase-design/SKILL.md +1 -1
  9. package/framework/skills/level-1-workflows/phase-implement/SKILL.md +1 -1
  10. package/framework/skills/level-1-workflows/phase-setup/SKILL.md +1 -1
  11. package/framework/skills/level-1-workflows/phase-tasks/SKILL.md +1 -1
  12. package/framework/skills/level-1-workflows/phase-uiux/SKILL.md +1 -1
  13. package/package.json +4 -4
  14. package/src/scripts/global-install.js +15 -3
  15. package/.morph/analytics/threads-log.jsonl +0 -54
  16. package/.morph/state.json +0 -198
  17. package/docs/ARCHITECTURE.md +0 -328
  18. package/docs/COMMAND-FLOWS.md +0 -398
  19. package/docs/plans/2026-02-22-claude-docs-morph-alignment-analysis.md +0 -514
  20. package/docs/plans/2026-02-22-claude-settings.md +0 -517
  21. package/docs/plans/2026-02-22-morph-cc-alignment-impl.md +0 -730
  22. package/docs/plans/2026-02-22-morph-spec-next.md +0 -480
  23. package/docs/plans/2026-02-22-native-alignment-design.md +0 -201
  24. package/docs/plans/2026-02-22-native-alignment-impl.md +0 -927
  25. package/docs/plans/2026-02-22-native-enrichment-design.md +0 -246
  26. package/docs/plans/2026-02-22-native-enrichment.md +0 -737
  27. package/docs/plans/2026-02-23-ddd-architecture-refactor.md +0 -1155
  28. package/docs/plans/2026-02-23-ddd-nextsteps.md +0 -684
  29. package/docs/plans/2026-02-23-infra-architect-refactor.md +0 -439
  30. package/docs/plans/2026-02-23-nextjs-code-review-design.md +0 -157
  31. package/docs/plans/2026-02-23-nextjs-code-review-impl.md +0 -1256
  32. package/docs/plans/2026-02-23-nextjs-standards-design.md +0 -150
  33. package/docs/plans/2026-02-23-nextjs-standards-impl.md +0 -1848
  34. package/docs/plans/2026-02-24-cli-radical-simplification.md +0 -592
  35. package/docs/plans/2026-02-24-framework-failure-points.md +0 -125
  36. package/docs/plans/2026-02-24-morph-init-design.md +0 -337
  37. package/docs/plans/2026-02-24-morph-init-impl.md +0 -1269
  38. package/docs/plans/2026-02-24-tutorial-command-design.md +0 -71
  39. package/docs/plans/2026-02-24-tutorial-command.md +0 -298
  40. package/scripts/bump-version.js +0 -248
  41. package/scripts/generate-refs.js +0 -336
  42. package/scripts/generate-standards-registry.js +0 -44
  43. package/scripts/install-dev-hooks.js +0 -138
  44. package/scripts/scan-nextjs.mjs +0 -169
  45. package/scripts/validate-real.mjs +0 -255
@@ -1,255 +0,0 @@
1
- /**
2
- * scripts/validate-real.mjs
3
- *
4
- * Real-world integration validation for output-schema.js centralization.
5
- * Creates a temp .morph project, creates a feature, and verifies all paths.
6
- *
7
- * Usage: node scripts/validate-real.mjs
8
- */
9
-
10
- import { join, dirname } from 'path';
11
- import { writeFileSync, mkdirSync, existsSync, readFileSync, rmSync } from 'fs';
12
- import { spawnSync } from 'child_process';
13
- import { fileURLToPath, pathToFileURL } from 'url';
14
- import { tmpdir } from 'os';
15
-
16
- // Helper: convert Windows absolute path to file:// URL for dynamic import
17
- function toFileUrl(p) { return pathToFileURL(p).href; }
18
-
19
- const __dirname = dirname(fileURLToPath(import.meta.url));
20
- const ROOT = join(__dirname, '..');
21
-
22
- let passed = 0;
23
- let failed = 0;
24
-
25
- function ok(label, condition, detail = '') {
26
- if (condition) {
27
- console.log(` ✓ ${label}`);
28
- passed++;
29
- } else {
30
- console.error(` ✗ ${label}${detail ? '\n ' + detail : ''}`);
31
- failed++;
32
- }
33
- }
34
-
35
- function section(title) {
36
- console.log(`\n── ${title} ──`);
37
- }
38
-
39
- // ============================================================================
40
- // 1. output-schema.js — paths and derived constants
41
- // ============================================================================
42
- section('1. output-schema.js exports');
43
-
44
- const {
45
- OUTPUT_SCHEMA, getOutputPath, getAbsoluteOutputPath, getFeatureDir, getAllOutputPaths,
46
- FILENAME_TO_OUTPUT, OUTPUT_PHASE_MAP, PROTECTED_SPEC_FILES,
47
- } = await import(toFileUrl(join(ROOT, 'src/core/paths/output-schema.js')));
48
-
49
- ok('OUTPUT_SCHEMA has 12 keys', Object.keys(OUTPUT_SCHEMA).length === 12);
50
- ok('spec path correct',
51
- getOutputPath('my-feature', 'spec') === '.morph/features/my-feature/1-design/spec.md');
52
- ok('contracts path correct',
53
- getOutputPath('my-feature', 'contracts') === '.morph/features/my-feature/1-design/contracts.cs');
54
- ok('tasks path correct',
55
- getOutputPath('my-feature', 'tasks') === '.morph/features/my-feature/3-tasks/tasks.md');
56
- ok('uiDesignSystem path correct',
57
- getOutputPath('my-feature', 'uiDesignSystem') === '.morph/features/my-feature/2-ui/design-system.md');
58
- ok('clarifications path correct',
59
- getOutputPath('my-feature', 'clarifications') === '.morph/features/my-feature/1-design/clarifications.md');
60
- ok('recap path correct',
61
- getOutputPath('my-feature', 'recap') === '.morph/features/my-feature/4-implement/recap.md');
62
-
63
- const allPaths = Object.keys(OUTPUT_SCHEMA).map(k => getOutputPath('feat', k));
64
- ok('no backslashes in any getOutputPath() result',
65
- allPaths.every(p => !p.includes('\\')),
66
- allPaths.filter(p => p.includes('\\')).join(', ') || 'all clean');
67
-
68
- const absSpec = getAbsoluteOutputPath('/project', 'my-feature', 'spec');
69
- ok('getAbsoluteOutputPath ends with spec.md', absSpec.endsWith('spec.md'));
70
-
71
- const all = getAllOutputPaths('test-feat');
72
- ok('getAllOutputPaths returns 12 entries', Object.keys(all).length === 12);
73
- ok('getAllOutputPaths created:false for all', Object.values(all).every(v => v.created === false));
74
- ok('getAllOutputPaths paths match getOutputPath',
75
- Object.entries(all).every(([k, v]) => v.path === getOutputPath('test-feat', k)));
76
-
77
- ok("FILENAME_TO_OUTPUT['spec.md'] === 'spec'", FILENAME_TO_OUTPUT['spec.md'] === 'spec');
78
- ok("FILENAME_TO_OUTPUT['contracts.cs'] === 'contracts'", FILENAME_TO_OUTPUT['contracts.cs'] === 'contracts');
79
- ok("OUTPUT_PHASE_MAP['spec'] === 'design'", OUTPUT_PHASE_MAP['spec'] === 'design');
80
- ok("OUTPUT_PHASE_MAP['tasks'] === 'tasks'", OUTPUT_PHASE_MAP['tasks'] === 'tasks');
81
- ok("PROTECTED_SPEC_FILES['spec.md'] === 'design'", PROTECTED_SPEC_FILES['spec.md'] === 'design');
82
- ok("PROTECTED_SPEC_FILES['tasks.md'] === 'tasks'", PROTECTED_SPEC_FILES['tasks.md'] === 'tasks');
83
- ok("PROTECTED_SPEC_FILES has no 'proposal.md'", !('proposal.md' in PROTECTED_SPEC_FILES));
84
- ok("getFeatureDir uses forward slashes", !getFeatureDir('x').includes('\\'));
85
- ok("getFeatureDir correct", getFeatureDir('my-feature') === '.morph/features/my-feature');
86
-
87
- // ============================================================================
88
- // 2. state-manager ensureFeature() creates correct paths in state.json
89
- // ============================================================================
90
- section('2. state-manager.js ensureFeature()');
91
-
92
- // Create an isolated temp .morph project
93
- const tempDir = join(tmpdir(), `morph-val-${Date.now()}`);
94
- const morphDir = join(tempDir, '.morph');
95
- const configDir = join(morphDir, 'config');
96
- mkdirSync(configDir, { recursive: true });
97
- mkdirSync(join(morphDir, 'features'), { recursive: true });
98
- mkdirSync(join(morphDir, 'context'), { recursive: true });
99
-
100
- writeFileSync(join(configDir, 'config.json'), JSON.stringify({
101
- framework: 'morph-spec', project: { name: 'test-project' }
102
- }, null, 2));
103
-
104
- const origCwd = process.cwd();
105
- process.chdir(tempDir);
106
-
107
- const { initState, updateFeature, loadState } = await import(toFileUrl(join(ROOT, 'src/core/state/state-manager.js')));
108
- // initState creates state.json; updateFeature calls ensureFeature internally
109
- initState({ name: 'test-project', type: 'test' });
110
- await updateFeature('my-feature', 'status', 'draft');
111
- const state = loadState();
112
- const feat = state.features['my-feature'];
113
-
114
- process.chdir(origCwd);
115
-
116
- ok('ensureFeature creates outputs block', !!feat?.outputs);
117
- ok('outputs has 12 keys', Object.keys(feat?.outputs ?? {}).length === 12);
118
-
119
- let allMatch = true;
120
- const mismatches = [];
121
- for (const [key, { path }] of Object.entries(feat?.outputs ?? {})) {
122
- const expected = getOutputPath('my-feature', key);
123
- if (path !== expected) {
124
- allMatch = false;
125
- mismatches.push(`${key}: got "${path}", expected "${expected}"`);
126
- }
127
- }
128
- ok('ALL output paths match output-schema.js', allMatch, mismatches.join('\n '));
129
-
130
- ok("spec path in state.json",
131
- feat.outputs.spec?.path === '.morph/features/my-feature/1-design/spec.md');
132
- ok("contracts path in state.json",
133
- feat.outputs.contracts?.path === '.morph/features/my-feature/1-design/contracts.cs');
134
- ok("uiDesignSystem path in state.json",
135
- feat.outputs.uiDesignSystem?.path === '.morph/features/my-feature/2-ui/design-system.md');
136
- ok("no backslashes in state.json output paths",
137
- Object.values(feat.outputs).every(({ path }) => !path.includes('\\')));
138
-
139
- // ============================================================================
140
- // 3. v3 → v4 migration uses getAllOutputPaths
141
- // ============================================================================
142
- section('3. v3 → v4 migration');
143
-
144
- // Write a v3.0.0 state in a fresh temp dir
145
- const tempDir2 = join(tmpdir(), `morph-val2-${Date.now()}`);
146
- const morphDir2 = join(tempDir2, '.morph');
147
- const configDir2 = join(morphDir2, 'config');
148
- mkdirSync(configDir2, { recursive: true });
149
- mkdirSync(join(morphDir2, 'features'), { recursive: true });
150
-
151
- const v3State = {
152
- version: '3.0.0',
153
- project: { name: 'legacy', type: 'test' },
154
- features: {
155
- 'legacy-feat': {
156
- status: 'draft', phase: 'design',
157
- outputs: {
158
- spec: { created: true, path: '.morph/features/legacy-feat/spec.md' },
159
- contracts: { created: false, path: '.morph/features/legacy-feat/contracts.cs' },
160
- tasks: { created: false, path: '.morph/features/legacy-feat/tasks.md' },
161
- }
162
- }
163
- },
164
- metadata: { totalFeatures: 1, lastUpdated: new Date().toISOString() }
165
- };
166
- writeFileSync(join(morphDir2, 'state.json'), JSON.stringify(v3State, null, 2));
167
-
168
- process.chdir(tempDir2);
169
- // state-manager is already cached; read the file directly to check migration
170
- // Re-use the already-imported module (ESM cache) — just call loadState directly
171
- const migrated = loadState();
172
- process.chdir(origCwd);
173
-
174
- ok('version migrated to 4.0.0', migrated.version === '4.0.0');
175
- ok('spec path migrated to 1-design subfolder',
176
- migrated.features['legacy-feat'].outputs.spec?.path
177
- === '.morph/features/legacy-feat/1-design/spec.md');
178
- ok('contracts path migrated to 1-design subfolder',
179
- migrated.features['legacy-feat'].outputs.contracts?.path
180
- === '.morph/features/legacy-feat/1-design/contracts.cs');
181
- ok('tasks path migrated to 3-tasks subfolder',
182
- migrated.features['legacy-feat'].outputs.tasks?.path
183
- === '.morph/features/legacy-feat/3-tasks/tasks.md');
184
-
185
- // ============================================================================
186
- // 4. phase-utils.js matches output-schema.js
187
- // ============================================================================
188
- section('4. phase-utils.js constants sync');
189
-
190
- const {
191
- FILENAME_TO_OUTPUT: PU_FTO,
192
- OUTPUT_PHASE_MAP: PU_OPM,
193
- PROTECTED_SPEC_FILES: PU_PSF,
194
- PHASE_DIRS,
195
- } = await import(toFileUrl(join(ROOT, 'framework/hooks/shared/phase-utils.js')));
196
-
197
- ok('FILENAME_TO_OUTPUT matches schema', JSON.stringify(PU_FTO) === JSON.stringify(FILENAME_TO_OUTPUT));
198
- ok('OUTPUT_PHASE_MAP matches schema', JSON.stringify(PU_OPM) === JSON.stringify(OUTPUT_PHASE_MAP));
199
- ok('PROTECTED_SPEC_FILES matches schema', JSON.stringify(PU_PSF) === JSON.stringify(PROTECTED_SPEC_FILES));
200
- ok("PHASE_DIRS['setup'] = '0-proposal' (manual extra)", PHASE_DIRS['setup'] === '0-proposal');
201
- ok("PHASE_DIRS['sync'] = '4-implement' (manual extra)", PHASE_DIRS['sync'] === '4-implement');
202
- ok("PHASE_DIRS['clarify'] = '1-design' (from schema)", PHASE_DIRS['clarify'] === '1-design');
203
-
204
- // ============================================================================
205
- // 5. generate-refs.js --check exits 0
206
- // ============================================================================
207
- section('5. generate-refs.js --check (CI mode)');
208
-
209
- const checkResult = spawnSync(
210
- process.execPath,
211
- [join(ROOT, 'scripts/generate-refs.js'), '--check'],
212
- { encoding: 'utf8' }
213
- );
214
-
215
- ok('exits 0 (no drift)', checkResult.status === 0,
216
- checkResult.status !== 0 ? (checkResult.stdout + checkResult.stderr).trim() : '');
217
- ok('reports all up to date',
218
- (checkResult.stdout || '').includes('OK: All generated refs are in sync'));
219
-
220
- // ============================================================================
221
- // 6. Simulate "add new output type" round-trip
222
- // ============================================================================
223
- section('6. Error handling: invalid output type');
224
-
225
- let threw = false;
226
- try {
227
- getOutputPath('my-feature', 'nonExistentType');
228
- } catch (e) {
229
- threw = e.message.includes('Unknown output type');
230
- }
231
- ok('getOutputPath throws on unknown type', threw);
232
-
233
- let threw2 = false;
234
- try {
235
- getAbsoluteOutputPath('/proj', 'feat', 'bogus');
236
- } catch (e) {
237
- threw2 = e.message.includes('Unknown output type');
238
- }
239
- ok('getAbsoluteOutputPath throws on unknown type', threw2);
240
-
241
- // ============================================================================
242
- // Cleanup + summary
243
- // ============================================================================
244
- try { rmSync(tempDir, { recursive: true, force: true }); } catch {}
245
- try { rmSync(tempDir2, { recursive: true, force: true }); } catch {}
246
-
247
- console.log(`\n${'─'.repeat(52)}`);
248
- console.log(`Results: ${passed} passed, ${failed} failed`);
249
- if (failed === 0) {
250
- console.log('✅ ALL VALIDATIONS PASSED');
251
- process.exit(0);
252
- } else {
253
- console.error('❌ SOME VALIDATIONS FAILED');
254
- process.exit(1);
255
- }