@adhdev/daemon-core 0.9.82-rc.136 → 0.9.82-rc.137
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/dist/cli-adapters/cli-script-runner.d.ts +45 -0
- package/dist/cli-adapters/cli-state-engine.d.ts +154 -0
- package/dist/cli-adapters/provider-cli-adapter.d.ts +73 -74
- package/dist/cli-adapters/provider-cli-shared.d.ts +4 -0
- package/dist/config/chat-history.d.ts +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2591 -1966
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2594 -1974
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/beads-db.d.ts +54 -0
- package/dist/mesh/mesh-active-work.d.ts +7 -1
- package/dist/mesh/mesh-events.d.ts +10 -4
- package/dist/mesh/mesh-ledger.d.ts +21 -1
- package/dist/mesh/mesh-refine-status.d.ts +2 -3
- package/dist/mesh/mesh-work-queue.d.ts +17 -0
- package/dist/mesh/worktree-bootstrap-config.d.ts +2 -4
- package/dist/repo-mesh-types.d.ts +5 -0
- package/package.json +1 -1
- package/src/cli-adapters/cli-script-runner.ts +145 -0
- package/src/cli-adapters/cli-state-engine.ts +957 -0
- package/src/cli-adapters/provider-cli-adapter.d.ts +0 -1
- package/src/cli-adapters/provider-cli-adapter.ts +365 -1397
- package/src/cli-adapters/provider-cli-shared.ts +4 -0
- package/src/commands/chat-commands.ts +17 -1
- package/src/commands/router.ts +8 -0
- package/src/config/chat-history.ts +7 -3
- package/src/git/git-worktree.ts +8 -1
- package/src/index.ts +3 -2
- package/src/mesh/beads-db.ts +305 -2
- package/src/mesh/coordinator-prompt.ts +12 -17
- package/src/mesh/mesh-active-work.ts +162 -59
- package/src/mesh/mesh-events.ts +198 -53
- package/src/mesh/mesh-ledger.ts +321 -105
- package/src/mesh/mesh-refine-status.ts +2 -3
- package/src/mesh/mesh-work-queue.ts +116 -120
- package/src/mesh/worktree-bootstrap-config.ts +17 -4
- package/src/providers/provider-schema.ts +2 -0
- package/src/repo-mesh-types.ts +10 -0
|
@@ -21,10 +21,12 @@ export interface MeshTaskModeValidationResult {
|
|
|
21
21
|
|
|
22
22
|
const LIVE_DEBUG_READONLY_FORBIDDEN: Array<{ label: string; pattern: RegExp }> = [
|
|
23
23
|
{ label: 'source_edit', pattern: /\b(edit|modify|patch|apply\s+patch|write\s+(?:to\s+)?(?:file|source)|overwrite|delete\s+file|remove\s+file|create\s+file|touch\s+file)\b/i },
|
|
24
|
-
{ label: 'git_mutation', pattern: /\b(?:git\s+(?:add|commit|push|reset|rebase|clean|checkout|switch|merge|tag|restore|rm|mv)|push\b)/i },
|
|
24
|
+
{ label: 'git_mutation', pattern: /\b(?:git\s+(?:add|commit|push|reset|rebase|clean|checkout|switch|merge|tag|restore|rm|mv|stash|worktree\s+(?:add|remove|move))|push\b)/i },
|
|
25
25
|
{ label: 'checkpoint', pattern: /\b(checkpoint|mesh_checkpoint)\b/i },
|
|
26
|
-
{ label: 'deploy_or_version_bump', pattern: /\b(deploy|wrangler\s+deploy|version[-\s]?bump|npm\s+version|release)\b/i },
|
|
27
|
-
{ label: 'destructive_shell', pattern: /\b(rm\s+-rf|mv\s+\S+\s+\S+|truncate\s|tee\s+\S+|sed\s+-i)\b/i },
|
|
26
|
+
{ label: 'deploy_or_version_bump', pattern: /\b(deploy|wrangler\s+deploy|version[-\s]?bump|npm\s+version|release|npm\s+publish|yarn\s+publish|pnpm\s+publish)\b/i },
|
|
27
|
+
{ label: 'destructive_shell', pattern: /\b(rm\s+-rf|mv\s+\S+\s+\S+|truncate\s|tee\s+\S+|sed\s+-i|shred\b)\b/i },
|
|
28
|
+
{ label: 'package_install', pattern: /\b(npm\s+(?:install|i|add|link|uninstall|remove)|yarn\s+(?:add|remove|link)|pnpm\s+(?:add|remove|link)|pip\s+install|brew\s+install|apt\s+install|cargo\s+install)\b/i },
|
|
29
|
+
{ label: 'container_mutation', pattern: /\b(docker\s+(?:build|run|exec|push|tag|rmi|rm|create|start|stop|kill)|kubectl\s+(?:apply|delete|patch|replace|create|scale))\b/i },
|
|
28
30
|
];
|
|
29
31
|
|
|
30
32
|
export function normalizeMeshTaskMode(value: unknown): MeshTaskMode | undefined {
|
|
@@ -122,35 +124,26 @@ export function enqueueTask(
|
|
|
122
124
|
if (!modeValidation.valid) {
|
|
123
125
|
throw new Error(`live_debug_readonly_guardrail_violation: forbidden operations (${modeValidation.violations.join(', ')})`);
|
|
124
126
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
queue.push(entry);
|
|
139
|
-
writeQueue(meshId, queue);
|
|
140
|
-
return entry;
|
|
141
|
-
});
|
|
127
|
+
const entry: MeshWorkQueueEntry = {
|
|
128
|
+
id: randomUUID(),
|
|
129
|
+
meshId,
|
|
130
|
+
message,
|
|
131
|
+
status: 'pending',
|
|
132
|
+
taskMode: modeValidation.taskMode,
|
|
133
|
+
targetNodeId: opts?.targetNodeId,
|
|
134
|
+
targetSessionId: opts?.targetSessionId,
|
|
135
|
+
createdAt: new Date().toISOString(),
|
|
136
|
+
updatedAt: new Date().toISOString(),
|
|
137
|
+
};
|
|
138
|
+
BeadsDB.getInstance().insertQueueEntry(entry);
|
|
139
|
+
return entry;
|
|
142
140
|
}
|
|
143
141
|
|
|
144
142
|
/**
|
|
145
143
|
* Get all tasks in the queue, optionally filtered by status.
|
|
146
144
|
*/
|
|
147
145
|
export function getQueue(meshId: string, opts?: { status?: MeshTaskStatus[] }): MeshWorkQueueEntry[] {
|
|
148
|
-
|
|
149
|
-
if (opts?.status?.length) {
|
|
150
|
-
const statuses = new Set(opts.status);
|
|
151
|
-
queue = queue.filter(q => statuses.has(q.status));
|
|
152
|
-
}
|
|
153
|
-
return queue;
|
|
146
|
+
return BeadsDB.getInstance().getQueueEntries(meshId, opts?.status?.length ? opts.status : undefined);
|
|
154
147
|
}
|
|
155
148
|
|
|
156
149
|
export function getMeshQueueRevision(meshId: string): string {
|
|
@@ -161,29 +154,7 @@ export function getMeshQueueRevision(meshId: string): string {
|
|
|
161
154
|
* Find the next pending task that this node is allowed to claim, and mark it as assigned.
|
|
162
155
|
*/
|
|
163
156
|
export function claimNextTask(meshId: string, nodeId: string, sessionId: string): MeshWorkQueueEntry | null {
|
|
164
|
-
return
|
|
165
|
-
const queue = readQueue(meshId);
|
|
166
|
-
const hasActiveAssignment = queue.some(q => q.status === 'assigned' && (
|
|
167
|
-
q.assignedSessionId === sessionId || q.assignedNodeId === nodeId
|
|
168
|
-
));
|
|
169
|
-
if (hasActiveAssignment) return null;
|
|
170
|
-
let targetIdx = queue.findIndex(q => q.status === 'pending' && q.targetSessionId === sessionId);
|
|
171
|
-
if (targetIdx === -1) {
|
|
172
|
-
targetIdx = queue.findIndex(q => q.status === 'pending' && q.targetNodeId === nodeId && !q.targetSessionId);
|
|
173
|
-
}
|
|
174
|
-
if (targetIdx === -1) {
|
|
175
|
-
targetIdx = queue.findIndex(q => q.status === 'pending' && !q.targetNodeId && !q.targetSessionId);
|
|
176
|
-
}
|
|
177
|
-
if (targetIdx === -1) return null;
|
|
178
|
-
const entry = queue[targetIdx];
|
|
179
|
-
entry.status = 'assigned';
|
|
180
|
-
entry.assignedNodeId = nodeId;
|
|
181
|
-
entry.assignedSessionId = sessionId;
|
|
182
|
-
entry.dispatchTimestamp = new Date().toISOString();
|
|
183
|
-
entry.updatedAt = new Date().toISOString();
|
|
184
|
-
writeQueue(meshId, queue);
|
|
185
|
-
return entry;
|
|
186
|
-
});
|
|
157
|
+
return BeadsDB.getInstance().claimNextQueueTask(meshId, nodeId, sessionId);
|
|
187
158
|
}
|
|
188
159
|
|
|
189
160
|
/**
|
|
@@ -198,13 +169,11 @@ export function updateTaskStatus(
|
|
|
198
169
|
): MeshWorkQueueEntry | null {
|
|
199
170
|
requireMeshHostQueueOwner(opts);
|
|
200
171
|
return withQueueLock(meshId, () => {
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
writeQueue(meshId, queue);
|
|
207
|
-
return queue[idx];
|
|
172
|
+
const entry = BeadsDB.getInstance().findQueueEntryById(meshId, taskId);
|
|
173
|
+
if (!entry) return null;
|
|
174
|
+
entry.status = status;
|
|
175
|
+
BeadsDB.getInstance().updateQueueEntry(entry);
|
|
176
|
+
return entry;
|
|
208
177
|
});
|
|
209
178
|
}
|
|
210
179
|
|
|
@@ -214,14 +183,12 @@ export function recordTaskAutoLaunch(
|
|
|
214
183
|
autoLaunch: Omit<NonNullable<MeshWorkQueueEntry['autoLaunch']>, 'updatedAt'>,
|
|
215
184
|
): MeshWorkQueueEntry | null {
|
|
216
185
|
return withQueueLock(meshId, () => {
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
if (idx === -1) return null;
|
|
186
|
+
const entry = BeadsDB.getInstance().findQueueEntryById(meshId, taskId);
|
|
187
|
+
if (!entry) return null;
|
|
220
188
|
const now = new Date().toISOString();
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
return queue[idx];
|
|
189
|
+
entry.autoLaunch = { ...autoLaunch, updatedAt: now };
|
|
190
|
+
BeadsDB.getInstance().updateQueueEntry(entry);
|
|
191
|
+
return entry;
|
|
225
192
|
});
|
|
226
193
|
}
|
|
227
194
|
|
|
@@ -235,16 +202,14 @@ export function cancelTask(
|
|
|
235
202
|
): MeshWorkQueueEntry | null {
|
|
236
203
|
requireMeshHostQueueOwner(opts);
|
|
237
204
|
return withQueueLock(meshId, () => {
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
if (idx === -1) return null;
|
|
205
|
+
const entry = BeadsDB.getInstance().findQueueEntryById(meshId, taskId);
|
|
206
|
+
if (!entry) return null;
|
|
241
207
|
const now = new Date().toISOString();
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
return queue[idx];
|
|
208
|
+
entry.status = 'cancelled';
|
|
209
|
+
entry.cancelledAt = now;
|
|
210
|
+
if (opts?.reason) entry.cancelReason = opts.reason;
|
|
211
|
+
BeadsDB.getInstance().updateQueueEntry(entry);
|
|
212
|
+
return entry;
|
|
248
213
|
});
|
|
249
214
|
}
|
|
250
215
|
|
|
@@ -265,11 +230,8 @@ export function requeueTask(
|
|
|
265
230
|
): MeshWorkQueueEntry | null {
|
|
266
231
|
requireMeshHostQueueOwner(opts);
|
|
267
232
|
return withQueueLock(meshId, () => {
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
if (idx === -1) return null;
|
|
271
|
-
const entry = queue[idx];
|
|
272
|
-
const now = new Date().toISOString();
|
|
233
|
+
const entry = BeadsDB.getInstance().findQueueEntryById(meshId, taskId);
|
|
234
|
+
if (!entry) return null;
|
|
273
235
|
entry.status = 'pending';
|
|
274
236
|
delete entry.assignedNodeId;
|
|
275
237
|
delete entry.assignedSessionId;
|
|
@@ -279,11 +241,10 @@ export function requeueTask(
|
|
|
279
241
|
if (typeof opts?.targetNodeId === 'string') entry.targetNodeId = opts.targetNodeId;
|
|
280
242
|
if (opts?.clearTargetSession !== false) delete entry.targetSessionId;
|
|
281
243
|
if (typeof opts?.targetSessionId === 'string') entry.targetSessionId = opts.targetSessionId;
|
|
282
|
-
entry.
|
|
283
|
-
entry.requeuedAt = now;
|
|
244
|
+
entry.requeuedAt = new Date().toISOString();
|
|
284
245
|
entry.requeueCount = (entry.requeueCount || 0) + 1;
|
|
285
246
|
if (opts?.reason) entry.requeueReason = opts.reason;
|
|
286
|
-
|
|
247
|
+
BeadsDB.getInstance().updateQueueEntry(entry);
|
|
287
248
|
return entry;
|
|
288
249
|
});
|
|
289
250
|
}
|
|
@@ -298,22 +259,12 @@ export function updateSessionTaskStatus(
|
|
|
298
259
|
opts?: { occurredAt?: string },
|
|
299
260
|
): MeshWorkQueueEntry | null {
|
|
300
261
|
return withQueueLock(meshId, () => {
|
|
301
|
-
const
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
if (queue[i].assignedSessionId !== sessionId || queue[i].status !== 'assigned') continue;
|
|
308
|
-
const time = new Date(queue[i].dispatchTimestamp || queue[i].updatedAt).getTime();
|
|
309
|
-
if (hasOccurredAt && Number.isFinite(time) && time > occurredAtTime) continue;
|
|
310
|
-
if (time > bestTime) { bestTime = time; bestIdx = i; }
|
|
311
|
-
}
|
|
312
|
-
if (bestIdx === -1) return null;
|
|
313
|
-
queue[bestIdx].status = status;
|
|
314
|
-
queue[bestIdx].updatedAt = new Date().toISOString();
|
|
315
|
-
writeQueue(meshId, queue);
|
|
316
|
-
return queue[bestIdx];
|
|
262
|
+
const occurredAtIso = opts?.occurredAt ? new Date(opts.occurredAt).toISOString() : undefined;
|
|
263
|
+
const entry = BeadsDB.getInstance().findAssignedBySession(meshId, sessionId, occurredAtIso);
|
|
264
|
+
if (!entry) return null;
|
|
265
|
+
entry.status = status;
|
|
266
|
+
BeadsDB.getInstance().updateQueueEntry(entry);
|
|
267
|
+
return entry;
|
|
317
268
|
});
|
|
318
269
|
}
|
|
319
270
|
|
|
@@ -342,14 +293,16 @@ export interface MeshWorkQueueStats {
|
|
|
342
293
|
* Return aggregate queue statistics for the given mesh.
|
|
343
294
|
*/
|
|
344
295
|
export function getMeshQueueStats(meshId: string): MeshWorkQueueStats {
|
|
345
|
-
const
|
|
346
|
-
const
|
|
347
|
-
const
|
|
348
|
-
const
|
|
349
|
-
const
|
|
350
|
-
const
|
|
296
|
+
const rows = BeadsDB.getInstance().getQueueStatsByStatus(meshId);
|
|
297
|
+
const counts: Record<string, number> = {};
|
|
298
|
+
for (const r of rows) counts[r.status] = r.count;
|
|
299
|
+
const pending = counts['pending'] ?? 0;
|
|
300
|
+
const assigned = counts['assigned'] ?? 0;
|
|
301
|
+
const completed = counts['completed'] ?? 0;
|
|
302
|
+
const failed = counts['failed'] ?? 0;
|
|
303
|
+
const cancelled = counts['cancelled'] ?? 0;
|
|
351
304
|
return {
|
|
352
|
-
total:
|
|
305
|
+
total: pending + assigned + completed + failed + cancelled,
|
|
353
306
|
active: pending + assigned,
|
|
354
307
|
historical: completed + failed + cancelled,
|
|
355
308
|
pending,
|
|
@@ -357,23 +310,9 @@ export function getMeshQueueStats(meshId: string): MeshWorkQueueStats {
|
|
|
357
310
|
completed,
|
|
358
311
|
failed,
|
|
359
312
|
cancelled,
|
|
360
|
-
activeCounts: {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
},
|
|
364
|
-
historicalCounts: {
|
|
365
|
-
completed,
|
|
366
|
-
failed,
|
|
367
|
-
cancelled,
|
|
368
|
-
},
|
|
369
|
-
activeAssignments: queue
|
|
370
|
-
.filter(q => q.status === 'assigned')
|
|
371
|
-
.map(q => ({
|
|
372
|
-
id: q.id,
|
|
373
|
-
nodeId: q.assignedNodeId,
|
|
374
|
-
sessionId: q.assignedSessionId,
|
|
375
|
-
message: q.message,
|
|
376
|
-
})),
|
|
313
|
+
activeCounts: { pending, assigned },
|
|
314
|
+
historicalCounts: { completed, failed, cancelled },
|
|
315
|
+
activeAssignments: BeadsDB.getInstance().getActiveAssignmentDetails(meshId),
|
|
377
316
|
};
|
|
378
317
|
}
|
|
379
318
|
|
|
@@ -390,3 +329,60 @@ export function __clearMeshQueueForTests(meshId: string): void {
|
|
|
390
329
|
export function __resetBeadsDBForTests(): void {
|
|
391
330
|
BeadsDB.resetForTests();
|
|
392
331
|
}
|
|
332
|
+
|
|
333
|
+
// ── Direct Dispatch Tracking ─────────────────────────────────────────────────
|
|
334
|
+
// Persists direct (non-queue) task dispatches so buildMeshActiveWork can read
|
|
335
|
+
// active work from BeadsDB instead of scanning ledger JSONL entries.
|
|
336
|
+
|
|
337
|
+
export type DirectDispatchRecord = ReturnType<BeadsDB['getActiveDirectDispatches']>[number];
|
|
338
|
+
|
|
339
|
+
export function insertDirectDispatch(
|
|
340
|
+
meshId: string,
|
|
341
|
+
data: {
|
|
342
|
+
taskId: string;
|
|
343
|
+
nodeId?: string;
|
|
344
|
+
sessionId?: string;
|
|
345
|
+
providerType?: string;
|
|
346
|
+
message: string;
|
|
347
|
+
taskMode?: string;
|
|
348
|
+
via: string;
|
|
349
|
+
dispatchedToIdleSession?: boolean;
|
|
350
|
+
dispatchedAt: string;
|
|
351
|
+
},
|
|
352
|
+
): void {
|
|
353
|
+
try {
|
|
354
|
+
BeadsDB.getInstance().insertDirectDispatch({ ...data, meshId });
|
|
355
|
+
} catch (e: any) {
|
|
356
|
+
process.stderr.write(`[adhdev-mesh] insertDirectDispatch failed for task ${data.taskId}: ${e?.message || e}\n`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
export function getActiveDirectDispatches(meshId: string): DirectDispatchRecord[] {
|
|
361
|
+
try {
|
|
362
|
+
return BeadsDB.getInstance().getActiveDirectDispatches(meshId);
|
|
363
|
+
} catch {
|
|
364
|
+
return [];
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export function updateDirectDispatchStatus(
|
|
369
|
+
meshId: string,
|
|
370
|
+
sessionId: string,
|
|
371
|
+
status: 'acked' | 'completed' | 'failed' | 'stale',
|
|
372
|
+
): void {
|
|
373
|
+
try {
|
|
374
|
+
BeadsDB.getInstance().updateDirectDispatchStatus(meshId, sessionId, status);
|
|
375
|
+
} catch { /* best-effort */ }
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
export function cleanupTerminalDirectDispatches(olderThanMs = 7 * 24 * 60 * 60_000): void {
|
|
379
|
+
try {
|
|
380
|
+
BeadsDB.getInstance().cleanupTerminalDirectDispatches(olderThanMs);
|
|
381
|
+
} catch { /* best-effort */ }
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
export function markStaleDirectDispatches(meshId: string, olderThanMs = 60 * 60_000): void {
|
|
385
|
+
try {
|
|
386
|
+
BeadsDB.getInstance().markStaleDirectDispatches(meshId, olderThanMs);
|
|
387
|
+
} catch { /* best-effort */ }
|
|
388
|
+
}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
type MeshRefineValidationCommandPlan,
|
|
10
10
|
type RepoMeshRefineValidationCommandConfig,
|
|
11
11
|
} from './refine-config.js';
|
|
12
|
+
import type { MeshAsyncJobLifecycle } from '../repo-mesh-types.js';
|
|
12
13
|
|
|
13
14
|
export type WorktreeBootstrapStatus = 'ready' | 'running' | 'failed' | 'not_configured' | 'disabled' | 'stale';
|
|
14
15
|
|
|
@@ -21,16 +22,13 @@ export interface RepoMeshWorktreeBootstrapConfig {
|
|
|
21
22
|
staleInputs?: string[];
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
export interface WorktreeBootstrapState {
|
|
25
|
+
export interface WorktreeBootstrapState extends MeshAsyncJobLifecycle {
|
|
25
26
|
status: WorktreeBootstrapStatus;
|
|
26
27
|
required: boolean;
|
|
27
28
|
configSource?: string;
|
|
28
29
|
configSourceType?: 'repo_file' | 'mesh_policy' | 'unavailable' | 'invalid';
|
|
29
|
-
startedAt?: string;
|
|
30
|
-
completedAt?: string;
|
|
31
30
|
lastCommand?: string;
|
|
32
31
|
exitCode?: number | null;
|
|
33
|
-
error?: string;
|
|
34
32
|
commandsRun?: Array<Record<string, unknown>>;
|
|
35
33
|
staleInputs?: string[];
|
|
36
34
|
}
|
|
@@ -177,7 +175,22 @@ export async function runMeshWorktreeBootstrap(mesh: any, workspace: string): Pr
|
|
|
177
175
|
commandsRun: [],
|
|
178
176
|
staleInputs: loaded.config.staleInputs,
|
|
179
177
|
};
|
|
178
|
+
const staleInputPaths = loaded.config.staleInputs ?? [];
|
|
179
|
+
// Snapshot which staleInputs files are absent at bootstrap start.
|
|
180
|
+
// Files already present before we start are not interference signals.
|
|
181
|
+
const initiallyAbsent = staleInputPaths.filter(p => !existsSync(join(workspace, p)));
|
|
180
182
|
for (const command of validation.commands) {
|
|
183
|
+
// Check if any initially-absent staleInputs files appeared since bootstrap started
|
|
184
|
+
// (indicates another process modified the environment mid-run).
|
|
185
|
+
if (initiallyAbsent.length > 0) {
|
|
186
|
+
const appearedNow = initiallyAbsent.filter(p => existsSync(join(workspace, p)));
|
|
187
|
+
if (appearedNow.length > 0) {
|
|
188
|
+
state.status = 'stale';
|
|
189
|
+
state.completedAt = new Date().toISOString();
|
|
190
|
+
state.error = `Bootstrap interrupted: staleInputs files appeared during run: ${appearedNow.join(', ')}`;
|
|
191
|
+
return state;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
181
194
|
const cwd = command.cwd ? pathResolve(workspace, command.cwd) : workspace;
|
|
182
195
|
const startedAt = Date.now();
|
|
183
196
|
state.lastCommand = command.displayCommand;
|
package/src/repo-mesh-types.ts
CHANGED
|
@@ -472,3 +472,13 @@ export interface RepoMeshLedgerStatus {
|
|
|
472
472
|
entries: RepoMeshLedgerEntryStatus[];
|
|
473
473
|
summary: RepoMeshLedgerSummaryStatus;
|
|
474
474
|
}
|
|
475
|
+
|
|
476
|
+
// ─── Async Job Lifecycle ─────────────────────────
|
|
477
|
+
// Shared base for all mesh async job types (refine jobs, bootstrap runs, etc.)
|
|
478
|
+
// Each concrete type adds its own status enum and domain-specific fields.
|
|
479
|
+
|
|
480
|
+
export interface MeshAsyncJobLifecycle {
|
|
481
|
+
startedAt?: string;
|
|
482
|
+
completedAt?: string;
|
|
483
|
+
error?: string;
|
|
484
|
+
}
|