@polymorphism-tech/morph-spec 4.8.19 → 4.9.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/CLAUDE.md +21 -0
- package/README.md +2 -2
- package/bin/morph-spec.js +15 -56
- package/bin/task-manager.js +115 -14
- package/bin/validate.js +67 -33
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +201 -203
- package/docs/QUICKSTART.md +2 -2
- package/framework/CLAUDE.md +21 -0
- package/framework/agents.json +698 -176
- package/framework/hooks/claude-code/post-tool-use/context-refresh.js +1 -1
- package/framework/hooks/claude-code/post-tool-use/dispatch.js +2 -2
- package/framework/hooks/claude-code/post-tool-use/skill-reminder.js +155 -0
- package/framework/hooks/claude-code/pre-tool-use/protect-spec-files.js +1 -1
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +71 -2
- package/framework/hooks/claude-code/statusline.py +76 -30
- package/framework/hooks/claude-code/user-prompt/set-terminal-title.js +14 -6
- package/framework/hooks/shared/activity-logger.js +0 -24
- package/framework/hooks/shared/phase-utils.js +3 -0
- package/framework/hooks/shared/skill-reminder-helpers.js +79 -0
- package/framework/hooks/shared/stale-task-reset.js +57 -0
- package/framework/hooks/shared/state-reader.js +2 -2
- package/framework/hooks/shared/worktree-helpers.js +53 -0
- package/framework/phases.json +40 -8
- package/framework/skills/level-0-meta/brainstorming/SKILL.md +1 -1
- package/framework/skills/level-0-meta/code-review/SKILL.md +1 -1
- package/framework/skills/level-0-meta/code-review-nextjs/SKILL.md +163 -163
- package/framework/skills/level-0-meta/frontend-review/SKILL.md +5 -5
- package/framework/skills/level-0-meta/morph-checklist/SKILL.md +2 -2
- package/framework/skills/level-0-meta/morph-init/SKILL.md +5 -5
- package/framework/skills/level-0-meta/morph-replicate/SKILL.md +4 -4
- package/framework/skills/level-0-meta/morph-replicate/references/blazor-html-mapping.md +1 -1
- package/framework/skills/level-0-meta/post-implementation/SKILL.md +59 -12
- package/framework/skills/level-0-meta/simulation-checklist/SKILL.md +1 -1
- package/framework/skills/level-0-meta/terminal-title/SKILL.md +1 -1
- package/framework/skills/level-0-meta/tool-usage-guide/SKILL.md +1 -1
- package/framework/skills/level-0-meta/tool-usage-guide/references/tools-per-phase.md +6 -5
- package/framework/skills/level-0-meta/verification-before-completion/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +215 -189
- package/framework/skills/level-1-workflows/phase-codebase-analysis/SKILL.md +251 -251
- package/framework/skills/level-1-workflows/phase-design/SKILL.md +382 -365
- package/framework/skills/level-1-workflows/phase-implement/SKILL.md +492 -450
- package/framework/skills/level-1-workflows/phase-setup/SKILL.md +194 -190
- package/framework/skills/level-1-workflows/phase-tasks/SKILL.md +270 -270
- package/framework/skills/level-1-workflows/phase-uiux/SKILL.md +285 -285
- package/framework/standards/STANDARDS.json +640 -88
- package/framework/standards/infrastructure/vercel/vercel-database.md +106 -0
- package/framework/templates/REGISTRY.json +1825 -1909
- package/framework/templates/context/CONTEXT-FEATURE.md +276 -276
- package/framework/templates/docs/onboarding.md +1 -5
- package/package.json +2 -6
- package/src/commands/agents/dispatch-agents.js +55 -4
- package/src/commands/project/doctor.js +16 -47
- package/src/commands/project/init.js +1 -1
- package/src/commands/project/status.js +2 -2
- package/src/commands/project/update.js +381 -365
- package/src/commands/project/worktree.js +154 -0
- package/src/commands/state/advance-phase.js +120 -30
- package/src/commands/state/approve.js +2 -2
- package/src/commands/state/index.js +7 -8
- package/src/commands/state/phase-runner.js +1 -1
- package/src/commands/state/state.js +61 -6
- package/src/commands/tasks/task.js +78 -99
- package/src/commands/templates/template-render.js +93 -173
- package/src/commands/trust/trust.js +26 -21
- package/src/core/paths/output-schema.js +15 -0
- package/src/core/state/state-manager.js +28 -54
- package/src/core/workflows/workflow-detector.js +9 -87
- package/src/lib/phase-chain/phase-validator.js +330 -0
- package/src/lib/stack/stack-profile.js +88 -0
- package/src/lib/tasks/task-classifier.js +16 -0
- package/src/lib/tasks/test-runner.js +77 -0
- package/src/lib/trust/trust-manager.js +32 -144
- package/src/lib/validators/spec-validator.js +58 -4
- package/src/lib/validators/validation-runner.js +23 -11
- package/src/scripts/setup-infra.js +240 -224
- package/src/utils/agents-installer.js +2 -2
- package/src/utils/banner.js +1 -1
- package/src/utils/claude-settings-manager.js +1 -1
- package/src/utils/file-copier.js +1 -0
- package/src/utils/hooks-installer.js +258 -8
- package/framework/hooks/dev/check-sync-health.js +0 -117
- package/framework/hooks/dev/guard-version-numbers.js +0 -57
- package/framework/hooks/dev/sync-standards-registry.js +0 -60
- package/framework/hooks/dev/sync-template-registry.js +0 -60
- package/framework/hooks/dev/validate-skill-format.js +0 -70
- package/framework/hooks/dev/validate-standard-format.js +0 -73
- package/framework/templates/meta-prompts/hops/hop-retry.md +0 -78
- package/framework/templates/meta-prompts/hops/hop-validation.md +0 -97
- package/framework/templates/meta-prompts/hops/hop-wrapper.md +0 -36
- package/framework/workflows/configs/design-impl.json +0 -49
- package/framework/workflows/configs/express.json +0 -45
- package/framework/workflows/configs/fast-track.json +0 -42
- package/framework/workflows/configs/full-morph.json +0 -79
- package/framework/workflows/configs/fusion.json +0 -39
- package/framework/workflows/configs/long-running.json +0 -33
- package/framework/workflows/configs/spec-only.json +0 -43
- package/framework/workflows/configs/ui-refresh.json +0 -49
- package/framework/workflows/configs/zero-touch.json +0 -82
- package/src/commands/project/monitor.js +0 -295
- package/src/commands/project/tutorial.js +0 -115
- package/src/commands/state/validate-phase.js +0 -238
- package/src/commands/templates/generate-contracts.js +0 -445
- package/src/core/orchestrator.js +0 -171
- package/src/core/registry/command-registry.js +0 -28
- package/src/core/registry/index.js +0 -8
- package/src/core/registry/validator-registry.js +0 -204
- package/src/core/templates/template-validator.js +0 -296
- package/src/generator/config-generator.js +0 -206
- package/src/generator/templates/config.json.template +0 -40
- package/src/generator/templates/project.md.template +0 -67
- package/src/lib/agents/micro-agent-factory.js +0 -161
- package/src/lib/analysis/complexity-analyzer.js +0 -441
- package/src/lib/analysis/index.js +0 -7
- package/src/lib/analytics/analytics-engine.js +0 -345
- package/src/lib/checkpoints/checkpoint-hooks.js +0 -298
- package/src/lib/checkpoints/index.js +0 -7
- package/src/lib/context/context-bundler.js +0 -241
- package/src/lib/context/context-optimizer.js +0 -212
- package/src/lib/context/context-tracker.js +0 -273
- package/src/lib/context/core-four-tracker.js +0 -201
- package/src/lib/context/mcp-optimizer.js +0 -200
- package/src/lib/execution/fusion-executor.js +0 -304
- package/src/lib/execution/parallel-executor.js +0 -270
- package/src/lib/hooks/stop-hook-executor.js +0 -286
- package/src/lib/hops/hop-composer.js +0 -221
- package/src/lib/phase-chain/eligibility-checker.js +0 -243
- package/src/lib/threads/thread-coordinator.js +0 -238
- package/src/lib/threads/thread-manager.js +0 -317
- package/src/lib/tracking/artifact-trail.js +0 -202
- package/src/scanner/project-scanner.js +0 -242
- package/src/ui/diff-display.js +0 -91
- package/src/ui/interactive-wizard.js +0 -96
- package/src/ui/user-review.js +0 -211
- package/src/ui/wizard-questions.js +0 -188
- package/src/utils/color-utils.js +0 -70
- package/src/utils/process-handler.js +0 -97
|
@@ -1,317 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Thread Manager — Core thread lifecycle management
|
|
3
|
-
*
|
|
4
|
-
* Manages the full lifecycle of MORPH-SPEC threads:
|
|
5
|
-
* - Types: base, parallel, fusion, long-running, zero-touch
|
|
6
|
-
* - Status transitions: pending → running → completed | failed | killed
|
|
7
|
-
* - Persists thread state to state.json threads{} section
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
11
|
-
import { join } from 'path';
|
|
12
|
-
import { randomUUID } from 'crypto';
|
|
13
|
-
|
|
14
|
-
const STATE_PATH = join(process.cwd(), '.morph/state.json');
|
|
15
|
-
|
|
16
|
-
// ============================================================================
|
|
17
|
-
// Thread Types and Status
|
|
18
|
-
// ============================================================================
|
|
19
|
-
|
|
20
|
-
export const THREAD_TYPES = {
|
|
21
|
-
BASE: 'base',
|
|
22
|
-
PARALLEL: 'parallel',
|
|
23
|
-
FUSION: 'fusion',
|
|
24
|
-
LONG_RUNNING: 'long-running',
|
|
25
|
-
ZERO_TOUCH: 'zero-touch'
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export const THREAD_STATUS = {
|
|
29
|
-
PENDING: 'pending',
|
|
30
|
-
RUNNING: 'running',
|
|
31
|
-
COMPLETED: 'completed',
|
|
32
|
-
FAILED: 'failed',
|
|
33
|
-
KILLED: 'killed'
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
// ============================================================================
|
|
37
|
-
// State I/O
|
|
38
|
-
// ============================================================================
|
|
39
|
-
|
|
40
|
-
function loadState() {
|
|
41
|
-
if (!existsSync(STATE_PATH)) {
|
|
42
|
-
throw new Error(`State file not found: ${STATE_PATH}`);
|
|
43
|
-
}
|
|
44
|
-
return JSON.parse(readFileSync(STATE_PATH, 'utf8'));
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function saveState(state) {
|
|
48
|
-
state.metadata = state.metadata || {};
|
|
49
|
-
state.metadata.lastUpdated = new Date().toISOString();
|
|
50
|
-
writeFileSync(STATE_PATH, JSON.stringify(state, null, 2), 'utf8');
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function ensureThreadsSection(state) {
|
|
54
|
-
if (!state.threads) {
|
|
55
|
-
state.threads = {};
|
|
56
|
-
}
|
|
57
|
-
return state;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// ============================================================================
|
|
61
|
-
// Thread CRUD
|
|
62
|
-
// ============================================================================
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Create a new thread
|
|
66
|
-
* @param {Object} opts
|
|
67
|
-
* @param {string} opts.feature - Feature name this thread belongs to
|
|
68
|
-
* @param {string} opts.type - Thread type (base|parallel|fusion|long-running|zero-touch)
|
|
69
|
-
* @param {string} [opts.agent] - Agent assigned to this thread
|
|
70
|
-
* @param {string} [opts.mission] - Thread mission description
|
|
71
|
-
* @param {Object} [opts.meta] - Additional metadata
|
|
72
|
-
* @returns {Object} Created thread object
|
|
73
|
-
*/
|
|
74
|
-
export function createThread({ feature, type = THREAD_TYPES.BASE, agent = null, mission = '', meta = {} }) {
|
|
75
|
-
const state = ensureThreadsSection(loadState());
|
|
76
|
-
const id = randomUUID();
|
|
77
|
-
const now = new Date().toISOString();
|
|
78
|
-
|
|
79
|
-
const thread = {
|
|
80
|
-
id,
|
|
81
|
-
feature,
|
|
82
|
-
type,
|
|
83
|
-
agent,
|
|
84
|
-
mission,
|
|
85
|
-
status: THREAD_STATUS.PENDING,
|
|
86
|
-
createdAt: now,
|
|
87
|
-
startedAt: null,
|
|
88
|
-
completedAt: null,
|
|
89
|
-
duration: null,
|
|
90
|
-
events: [],
|
|
91
|
-
metrics: {
|
|
92
|
-
toolCalls: 0,
|
|
93
|
-
tokensUsed: 0,
|
|
94
|
-
checkpointsPassed: 0,
|
|
95
|
-
checkpointsFailed: 0
|
|
96
|
-
},
|
|
97
|
-
meta
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
state.threads[id] = thread;
|
|
101
|
-
saveState(state);
|
|
102
|
-
return thread;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Update thread fields
|
|
107
|
-
* @param {string} id - Thread ID
|
|
108
|
-
* @param {Object} updates - Fields to update
|
|
109
|
-
* @returns {Object} Updated thread
|
|
110
|
-
*/
|
|
111
|
-
export function updateThread(id, updates) {
|
|
112
|
-
const state = ensureThreadsSection(loadState());
|
|
113
|
-
|
|
114
|
-
if (!state.threads[id]) {
|
|
115
|
-
throw new Error(`Thread not found: ${id}`);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
state.threads[id] = { ...state.threads[id], ...updates };
|
|
119
|
-
saveState(state);
|
|
120
|
-
return state.threads[id];
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Get a thread by ID
|
|
125
|
-
* @param {string} id - Thread ID
|
|
126
|
-
* @returns {Object|null} Thread or null
|
|
127
|
-
*/
|
|
128
|
-
export function getThread(id) {
|
|
129
|
-
const state = ensureThreadsSection(loadState());
|
|
130
|
-
return state.threads[id] || null;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* List threads, optionally filtered
|
|
135
|
-
* @param {Object} [filter]
|
|
136
|
-
* @param {string} [filter.feature] - Filter by feature name
|
|
137
|
-
* @param {string} [filter.status] - Filter by status
|
|
138
|
-
* @param {string} [filter.type] - Filter by type
|
|
139
|
-
* @returns {Array} Array of thread objects
|
|
140
|
-
*/
|
|
141
|
-
export function listThreads({ feature, status, type } = {}) {
|
|
142
|
-
const state = ensureThreadsSection(loadState());
|
|
143
|
-
let threads = Object.values(state.threads);
|
|
144
|
-
|
|
145
|
-
if (feature) threads = threads.filter(t => t.feature === feature);
|
|
146
|
-
if (status) threads = threads.filter(t => t.status === status);
|
|
147
|
-
if (type) threads = threads.filter(t => t.type === type);
|
|
148
|
-
|
|
149
|
-
return threads.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Start a thread (pending → running)
|
|
154
|
-
* @param {string} id - Thread ID
|
|
155
|
-
* @returns {Object} Updated thread
|
|
156
|
-
*/
|
|
157
|
-
export function startThread(id) {
|
|
158
|
-
const now = new Date().toISOString();
|
|
159
|
-
return updateThread(id, {
|
|
160
|
-
status: THREAD_STATUS.RUNNING,
|
|
161
|
-
startedAt: now
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Complete a thread (running → completed)
|
|
167
|
-
* @param {string} id - Thread ID
|
|
168
|
-
* @param {Object} [result] - Optional result metadata
|
|
169
|
-
* @returns {Object} Updated thread
|
|
170
|
-
*/
|
|
171
|
-
export function completeThread(id, result = {}) {
|
|
172
|
-
const thread = getThread(id);
|
|
173
|
-
if (!thread) throw new Error(`Thread not found: ${id}`);
|
|
174
|
-
|
|
175
|
-
const now = new Date();
|
|
176
|
-
const startedAt = thread.startedAt ? new Date(thread.startedAt) : now;
|
|
177
|
-
const duration = Math.round((now - startedAt) / 1000); // seconds
|
|
178
|
-
|
|
179
|
-
return updateThread(id, {
|
|
180
|
-
status: THREAD_STATUS.COMPLETED,
|
|
181
|
-
completedAt: now.toISOString(),
|
|
182
|
-
duration,
|
|
183
|
-
result
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Mark a thread as failed
|
|
189
|
-
* @param {string} id - Thread ID
|
|
190
|
-
* @param {string} reason - Failure reason
|
|
191
|
-
* @returns {Object} Updated thread
|
|
192
|
-
*/
|
|
193
|
-
export function failThread(id, reason = '') {
|
|
194
|
-
const thread = getThread(id);
|
|
195
|
-
if (!thread) throw new Error(`Thread not found: ${id}`);
|
|
196
|
-
|
|
197
|
-
const now = new Date();
|
|
198
|
-
const startedAt = thread.startedAt ? new Date(thread.startedAt) : now;
|
|
199
|
-
const duration = Math.round((now - startedAt) / 1000);
|
|
200
|
-
|
|
201
|
-
return updateThread(id, {
|
|
202
|
-
status: THREAD_STATUS.FAILED,
|
|
203
|
-
completedAt: now.toISOString(),
|
|
204
|
-
duration,
|
|
205
|
-
failureReason: reason
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Kill (terminate) a running thread
|
|
211
|
-
* @param {string} id - Thread ID
|
|
212
|
-
* @returns {Object} Updated thread
|
|
213
|
-
*/
|
|
214
|
-
export function killThread(id) {
|
|
215
|
-
const thread = getThread(id);
|
|
216
|
-
if (!thread) throw new Error(`Thread not found: ${id}`);
|
|
217
|
-
if (thread.status === THREAD_STATUS.COMPLETED || thread.status === THREAD_STATUS.KILLED) {
|
|
218
|
-
throw new Error(`Cannot kill thread in status: ${thread.status}`);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const now = new Date();
|
|
222
|
-
const startedAt = thread.startedAt ? new Date(thread.startedAt) : now;
|
|
223
|
-
const duration = Math.round((now - startedAt) / 1000);
|
|
224
|
-
|
|
225
|
-
return updateThread(id, {
|
|
226
|
-
status: THREAD_STATUS.KILLED,
|
|
227
|
-
completedAt: now.toISOString(),
|
|
228
|
-
duration
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Add an event to a thread's event log
|
|
234
|
-
* @param {string} id - Thread ID
|
|
235
|
-
* @param {string} type - Event type
|
|
236
|
-
* @param {Object} [data] - Event data
|
|
237
|
-
*/
|
|
238
|
-
export function addThreadEvent(id, type, data = {}) {
|
|
239
|
-
const state = ensureThreadsSection(loadState());
|
|
240
|
-
const thread = state.threads[id];
|
|
241
|
-
if (!thread) throw new Error(`Thread not found: ${id}`);
|
|
242
|
-
|
|
243
|
-
thread.events = thread.events || [];
|
|
244
|
-
thread.events.push({
|
|
245
|
-
type,
|
|
246
|
-
timestamp: new Date().toISOString(),
|
|
247
|
-
data
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
saveState(state);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Increment thread metrics counter
|
|
255
|
-
* @param {string} id - Thread ID
|
|
256
|
-
* @param {string} metric - Metric name (toolCalls|tokensUsed|checkpointsPassed|checkpointsFailed)
|
|
257
|
-
* @param {number} [amount=1] - Amount to increment
|
|
258
|
-
*/
|
|
259
|
-
export function incrementThreadMetric(id, metric, amount = 1) {
|
|
260
|
-
const state = ensureThreadsSection(loadState());
|
|
261
|
-
const thread = state.threads[id];
|
|
262
|
-
if (!thread) throw new Error(`Thread not found: ${id}`);
|
|
263
|
-
|
|
264
|
-
thread.metrics = thread.metrics || {};
|
|
265
|
-
thread.metrics[metric] = (thread.metrics[metric] || 0) + amount;
|
|
266
|
-
saveState(state);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// ============================================================================
|
|
270
|
-
// Analytics
|
|
271
|
-
// ============================================================================
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Get analytics for threads belonging to a feature
|
|
275
|
-
* @param {string} feature - Feature name
|
|
276
|
-
* @returns {Object} Analytics summary
|
|
277
|
-
*/
|
|
278
|
-
export function getThreadAnalytics(feature) {
|
|
279
|
-
const threads = listThreads({ feature });
|
|
280
|
-
|
|
281
|
-
const byStatus = threads.reduce((acc, t) => {
|
|
282
|
-
acc[t.status] = (acc[t.status] || 0) + 1;
|
|
283
|
-
return acc;
|
|
284
|
-
}, {});
|
|
285
|
-
|
|
286
|
-
const byType = threads.reduce((acc, t) => {
|
|
287
|
-
acc[t.type] = (acc[t.type] || 0) + 1;
|
|
288
|
-
return acc;
|
|
289
|
-
}, {});
|
|
290
|
-
|
|
291
|
-
const completed = threads.filter(t => t.status === THREAD_STATUS.COMPLETED);
|
|
292
|
-
const avgDuration = completed.length > 0
|
|
293
|
-
? Math.round(completed.reduce((sum, t) => sum + (t.duration || 0), 0) / completed.length)
|
|
294
|
-
: 0;
|
|
295
|
-
|
|
296
|
-
const totalToolCalls = threads.reduce((sum, t) => sum + (t.metrics?.toolCalls || 0), 0);
|
|
297
|
-
const parallelThreads = threads.filter(t => t.type === THREAD_TYPES.PARALLEL);
|
|
298
|
-
const avgParallel = parallelThreads.length;
|
|
299
|
-
|
|
300
|
-
return {
|
|
301
|
-
feature,
|
|
302
|
-
total: threads.length,
|
|
303
|
-
byStatus,
|
|
304
|
-
byType,
|
|
305
|
-
avgDuration,
|
|
306
|
-
totalToolCalls,
|
|
307
|
-
parallelEfficiency: {
|
|
308
|
-
avgParallel,
|
|
309
|
-
maxParallel: Math.max(...threads.map(() => parallelThreads.length), 0)
|
|
310
|
-
},
|
|
311
|
-
checkpointPassRate: threads.reduce((sum, t) => {
|
|
312
|
-
const p = t.metrics?.checkpointsPassed || 0;
|
|
313
|
-
const f = t.metrics?.checkpointsFailed || 0;
|
|
314
|
-
return p + f > 0 ? sum + (p / (p + f)) : sum;
|
|
315
|
-
}, 0) / Math.max(threads.filter(t => (t.metrics?.checkpointsPassed || 0) + (t.metrics?.checkpointsFailed || 0) > 0).length, 1)
|
|
316
|
-
};
|
|
317
|
-
}
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Artifact Trail — Debugging trail system
|
|
3
|
-
*
|
|
4
|
-
* Creates checkpoint artifact directories and saves:
|
|
5
|
-
* - Intermediate files (spec drafts, contract drafts)
|
|
6
|
-
* - Agent reasoning logs
|
|
7
|
-
* - Validator output logs
|
|
8
|
-
* - Failure artifacts on checkpoint failure
|
|
9
|
-
*
|
|
10
|
-
* Structure:
|
|
11
|
-
* .morph/features/{feature}/artifacts/
|
|
12
|
-
* checkpoint-{n}/
|
|
13
|
-
* spec-draft.md
|
|
14
|
-
* contracts-draft.cs
|
|
15
|
-
* agent-reasoning.json
|
|
16
|
-
* validator-output.json
|
|
17
|
-
* checkpoint-{n}/failures/
|
|
18
|
-
* failure-detail.json
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from 'fs';
|
|
22
|
-
import { join } from 'path';
|
|
23
|
-
|
|
24
|
-
const BASE_DIR = join(process.cwd(), '.morph/features');
|
|
25
|
-
|
|
26
|
-
// ============================================================================
|
|
27
|
-
// Path Helpers
|
|
28
|
-
// ============================================================================
|
|
29
|
-
|
|
30
|
-
function artifactsDir(feature) {
|
|
31
|
-
return join(BASE_DIR, feature, 'artifacts');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function checkpointDir(feature, checkpointNum) {
|
|
35
|
-
return join(artifactsDir(feature), `checkpoint-${checkpointNum}`);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function failuresDir(feature, checkpointNum) {
|
|
39
|
-
return join(checkpointDir(feature, checkpointNum), 'failures');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function ensureDir(dir) {
|
|
43
|
-
if (!existsSync(dir)) {
|
|
44
|
-
mkdirSync(dir, { recursive: true });
|
|
45
|
-
}
|
|
46
|
-
return dir;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// ============================================================================
|
|
50
|
-
// Checkpoint Artifacts
|
|
51
|
-
// ============================================================================
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Initialize a checkpoint artifact directory
|
|
55
|
-
* @param {string} feature - Feature name
|
|
56
|
-
* @param {number} checkpointNum - Checkpoint number
|
|
57
|
-
* @returns {string} Path to checkpoint directory
|
|
58
|
-
*/
|
|
59
|
-
export function initCheckpointArtifacts(feature, checkpointNum) {
|
|
60
|
-
const dir = checkpointDir(feature, checkpointNum);
|
|
61
|
-
ensureDir(dir);
|
|
62
|
-
|
|
63
|
-
const manifest = {
|
|
64
|
-
feature,
|
|
65
|
-
checkpointNum,
|
|
66
|
-
createdAt: new Date().toISOString(),
|
|
67
|
-
files: []
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
writeFileSync(join(dir, 'manifest.json'), JSON.stringify(manifest, null, 2), 'utf8');
|
|
71
|
-
return dir;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Save an intermediate artifact file to a checkpoint directory
|
|
76
|
-
* @param {string} feature - Feature name
|
|
77
|
-
* @param {number} checkpointNum - Checkpoint number
|
|
78
|
-
* @param {string} filename - File name (e.g., 'spec-draft.md')
|
|
79
|
-
* @param {string} content - File content
|
|
80
|
-
* @returns {string} Full path to saved file
|
|
81
|
-
*/
|
|
82
|
-
export function saveArtifact(feature, checkpointNum, filename, content) {
|
|
83
|
-
const dir = ensureDir(checkpointDir(feature, checkpointNum));
|
|
84
|
-
const filePath = join(dir, filename);
|
|
85
|
-
|
|
86
|
-
writeFileSync(filePath, content, 'utf8');
|
|
87
|
-
|
|
88
|
-
// Update manifest
|
|
89
|
-
const manifestPath = join(dir, 'manifest.json');
|
|
90
|
-
if (existsSync(manifestPath)) {
|
|
91
|
-
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
92
|
-
if (!manifest.files.includes(filename)) {
|
|
93
|
-
manifest.files.push(filename);
|
|
94
|
-
manifest.updatedAt = new Date().toISOString();
|
|
95
|
-
writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), 'utf8');
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return filePath;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Save agent reasoning log
|
|
104
|
-
* @param {string} feature - Feature name
|
|
105
|
-
* @param {number} checkpointNum - Checkpoint number
|
|
106
|
-
* @param {Object} reasoning - Agent reasoning data
|
|
107
|
-
*/
|
|
108
|
-
export function saveAgentReasoning(feature, checkpointNum, reasoning) {
|
|
109
|
-
const entry = {
|
|
110
|
-
timestamp: new Date().toISOString(),
|
|
111
|
-
...reasoning
|
|
112
|
-
};
|
|
113
|
-
saveArtifact(feature, checkpointNum, 'agent-reasoning.json', JSON.stringify(entry, null, 2));
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Save validator output log
|
|
118
|
-
* @param {string} feature - Feature name
|
|
119
|
-
* @param {number} checkpointNum - Checkpoint number
|
|
120
|
-
* @param {Object} validatorOutput - Validator results
|
|
121
|
-
*/
|
|
122
|
-
export function saveValidatorOutput(feature, checkpointNum, validatorOutput) {
|
|
123
|
-
const entry = {
|
|
124
|
-
timestamp: new Date().toISOString(),
|
|
125
|
-
...validatorOutput
|
|
126
|
-
};
|
|
127
|
-
saveArtifact(feature, checkpointNum, 'validator-output.json', JSON.stringify(entry, null, 2));
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// ============================================================================
|
|
131
|
-
// Failure Artifacts
|
|
132
|
-
// ============================================================================
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Save failure artifacts when a checkpoint fails
|
|
136
|
-
* @param {string} feature - Feature name
|
|
137
|
-
* @param {number} checkpointNum - Checkpoint number
|
|
138
|
-
* @param {Object} failure - Failure details
|
|
139
|
-
* @param {string} failure.reason - Failure reason
|
|
140
|
-
* @param {Array} failure.errors - Array of error objects
|
|
141
|
-
* @param {number} failure.attemptNumber - Which retry attempt (1-3)
|
|
142
|
-
*/
|
|
143
|
-
export function saveFailureArtifacts(feature, checkpointNum, failure) {
|
|
144
|
-
const dir = ensureDir(failuresDir(feature, checkpointNum));
|
|
145
|
-
const filename = `failure-attempt-${failure.attemptNumber || 1}.json`;
|
|
146
|
-
|
|
147
|
-
const entry = {
|
|
148
|
-
feature,
|
|
149
|
-
checkpointNum,
|
|
150
|
-
timestamp: new Date().toISOString(),
|
|
151
|
-
...failure
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
writeFileSync(join(dir, filename), JSON.stringify(entry, null, 2), 'utf8');
|
|
155
|
-
return join(dir, filename);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Get all failure artifacts for a checkpoint
|
|
160
|
-
* @param {string} feature - Feature name
|
|
161
|
-
* @param {number} checkpointNum - Checkpoint number
|
|
162
|
-
* @returns {Array} Array of failure records
|
|
163
|
-
*/
|
|
164
|
-
export function getFailureArtifacts(feature, checkpointNum) {
|
|
165
|
-
const dir = failuresDir(feature, checkpointNum);
|
|
166
|
-
if (!existsSync(dir)) return [];
|
|
167
|
-
|
|
168
|
-
const files = readdirSync(dir).filter(f => f.endsWith('.json'));
|
|
169
|
-
|
|
170
|
-
return files.map(f => {
|
|
171
|
-
try {
|
|
172
|
-
return JSON.parse(readFileSync(join(dir, f), 'utf8'));
|
|
173
|
-
} catch {
|
|
174
|
-
return null;
|
|
175
|
-
}
|
|
176
|
-
}).filter(Boolean);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* List all checkpoints for a feature
|
|
181
|
-
* @param {string} feature - Feature name
|
|
182
|
-
* @returns {Array} Array of checkpoint info objects
|
|
183
|
-
*/
|
|
184
|
-
export function listCheckpointArtifacts(feature) {
|
|
185
|
-
const dir = artifactsDir(feature);
|
|
186
|
-
if (!existsSync(dir)) return [];
|
|
187
|
-
|
|
188
|
-
try {
|
|
189
|
-
return readdirSync(dir, { withFileTypes: true })
|
|
190
|
-
.filter(d => d.isDirectory() && d.name.startsWith('checkpoint-'))
|
|
191
|
-
.map(d => {
|
|
192
|
-
const checkpointPath = join(dir, d.name);
|
|
193
|
-
const manifestPath = join(checkpointPath, 'manifest.json');
|
|
194
|
-
if (existsSync(manifestPath)) {
|
|
195
|
-
return JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
196
|
-
}
|
|
197
|
-
return { name: d.name, path: checkpointPath };
|
|
198
|
-
});
|
|
199
|
-
} catch {
|
|
200
|
-
return [];
|
|
201
|
-
}
|
|
202
|
-
}
|