claude-code-session-manager 0.75.3 → 0.77.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/dist/assets/{AgentLibrary-CzQqcObq.js → AgentLibrary-B2ie8bbw.js} +2 -2
- package/dist/assets/{DataModel-Bj_WlLz8.js → DataModel-BIJPYw32.js} +1 -1
- package/dist/assets/{History-DnSi_OHm.js → History-CeY6dk9S.js} +2 -2
- package/dist/assets/{Hooks-0BB0dp3S.js → Hooks-BFH2ocKg.js} +2 -2
- package/dist/assets/{HostBilko-DHpwwsLQ.js → HostBilko-36gj9wLz.js} +1 -1
- package/dist/assets/{Library-CaJVqVvi.js → Library-C-hBct39.js} +1 -1
- package/dist/assets/{ListDetail-C1W2HmC2.js → ListDetail-CNq64VWV.js} +1 -1
- package/dist/assets/{MarkdownEditor-5Ob9FW3z.js → MarkdownEditor-Bh3qt5-1.js} +1 -1
- package/dist/assets/{McpServers-JxCSfm1S.js → McpServers-DpGN0oyz.js} +1 -1
- package/dist/assets/{Memory-BDeqlqwH.js → Memory-D59hUjC4.js} +6 -6
- package/dist/assets/{Panel-Dh9ZHuEj.js → Panel-DCgbaoci.js} +1 -1
- package/dist/assets/{Permissions-DXy-CbEY.js → Permissions-DAmQ0DYV.js} +2 -2
- package/dist/assets/{Plugins-_n1Iuc8T.js → Plugins-Dyfgn6Is.js} +2 -2
- package/dist/assets/{ProvenanceBadge-BP_evfxE.js → ProvenanceBadge-BiYhPO1U.js} +1 -1
- package/dist/assets/SaveBar-RV7B6sOh.js +1 -0
- package/dist/assets/Scheduler-BPaNqx1b.js +14 -0
- package/dist/assets/{ScopeSwitcher-CAWzM6RI.js → ScopeSwitcher-P4mdLGNU.js} +1 -1
- package/dist/assets/{Settings-DRRozLyT.js → Settings-BL4vf5aX.js} +1 -1
- package/dist/assets/{SkillReferenceGraph-DGHDWlz4.js → SkillReferenceGraph-BRBDyi1_.js} +1 -1
- package/dist/assets/{Skills-D8L66eiX.js → Skills-BV08gDUH.js} +2 -2
- package/dist/assets/{SystemPrompt-CYtUsonD.js → SystemPrompt-CLftSsDw.js} +1 -1
- package/dist/assets/TagLibrary-Bp8jGsd5.js +1 -0
- package/dist/assets/{TiptapBody-B2hRgbPE.js → TiptapBody-jCpuB6E5.js} +1 -1
- package/dist/assets/{Toggle-BTwsbxam.js → Toggle-D2paA1xf.js} +1 -1
- package/dist/assets/{index-DijufvkJ.js → index-BDRSqBl3.js} +704 -704
- package/dist/assets/{index-CMLnzdZC.css → index-CYhdtisq.css} +1 -1
- package/dist/assets/{settingsSchema-D6wzxAi6.js → settingsSchema-6IOLjZZN.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +8 -2
- package/plugins/session-manager-dev/skills/develop/standards.md +1 -1
- package/scripts/lib/activeSessions.cjs +116 -6
- package/scripts/project-pages-logic/dist/logic.cjs +4709 -0
- package/scripts/render-project-pages/dist/renderer.cjs +18900 -0
- package/scripts/render-project-pages.cjs +70 -0
- package/scripts/scheduler-mcp-server.cjs +269 -96
- package/scripts/validate-project-pages-summary.cjs +62 -0
- package/src/main/__tests__/agentModelResolve.test.cjs +66 -0
- package/src/main/__tests__/epicStatusMirror.test.cjs +110 -0
- package/src/main/__tests__/health-delegation-chain.test.cjs +106 -0
- package/src/main/__tests__/prdAdminRoutes.test.cjs +295 -0
- package/src/main/__tests__/prdAgentType.test.cjs +103 -0
- package/src/main/__tests__/prdCreate.test.cjs +247 -0
- package/src/main/__tests__/prdFrontmatterAgentType.test.cjs +117 -0
- package/src/main/__tests__/prdFrontmatterQuietMachine.test.cjs +108 -0
- package/src/main/__tests__/projectHomeAdminRoutes.test.cjs +485 -0
- package/src/main/__tests__/projectPages.test.cjs +73 -1
- package/src/main/__tests__/rcaReport.test.cjs +54 -0
- package/src/main/__tests__/runVerify.test.cjs +94 -0
- package/src/main/__tests__/scheduler-autofix-select.test.cjs +58 -3
- package/src/main/__tests__/scheduler-bash-timeout-env.test.cjs +103 -0
- package/src/main/__tests__/scheduler-commit-guard-noop.test.cjs +41 -0
- package/src/main/__tests__/scheduler-effective-concurrency.test.cjs +10 -0
- package/src/main/__tests__/scheduler-foreign-wip-manifest.test.cjs +78 -0
- package/src/main/__tests__/scheduler-inplace-salvage.test.cjs +242 -0
- package/src/main/__tests__/scheduler-investigation-prompt.test.cjs +31 -0
- package/src/main/__tests__/scheduler-launch-failure.test.cjs +201 -0
- package/src/main/__tests__/scheduler-leftover-fields.test.cjs +52 -0
- package/src/main/__tests__/scheduler-looks-done.test.cjs +241 -0
- package/src/main/__tests__/scheduler-prd-persona-spawn.test.cjs +135 -0
- package/src/main/__tests__/scheduler-quiet-machine-lease.test.cjs +222 -0
- package/src/main/__tests__/scheduler-reap-dead-running-jobs.test.cjs +207 -1
- package/src/main/__tests__/scheduler-shared-tree-guard.test.cjs +212 -0
- package/src/main/__tests__/scheduler-stranded-investigation.test.cjs +185 -0
- package/src/main/__tests__/scheduler-worktree-cap-defer.test.cjs +194 -0
- package/src/main/__tests__/seedAgentPersonas.test.cjs +75 -14
- package/src/main/__tests__/seedSchedulerMcp.test.cjs +66 -0
- package/src/main/__tests__/uniquePrdNumbers.test.cjs +14 -5
- package/src/main/bilkoHost.cjs +4 -3
- package/src/main/chatRunner.cjs +6 -1
- package/src/main/config.cjs +25 -33
- package/src/main/health.cjs +153 -2
- package/src/main/index.cjs +64 -5
- package/src/main/ipcSchemas.cjs +69 -1
- package/src/main/lib/__tests__/activeIndexRebuild.test.cjs +179 -0
- package/src/main/lib/__tests__/childWithLog.test.cjs +141 -0
- package/src/main/lib/__tests__/delegationReadiness.test.cjs +391 -42
- package/src/main/lib/__tests__/ephemeralCwd.test.cjs +91 -0
- package/src/main/lib/__tests__/epicWorktreeMint.test.cjs +5 -3
- package/src/main/lib/__tests__/fixChainDepth.test.cjs +40 -0
- package/src/main/lib/__tests__/gitWorktree.test.cjs +290 -5
- package/src/main/lib/__tests__/gitWorktreeSalvage.test.cjs +107 -0
- package/src/main/lib/__tests__/gitWorktreeSalvageDelta.test.cjs +153 -0
- package/src/main/lib/__tests__/jobWorktree.test.cjs +6 -4
- package/src/main/lib/__tests__/landedSinceRun.test.cjs +73 -0
- package/src/main/lib/__tests__/launchFailure.test.cjs +220 -0
- package/src/main/lib/__tests__/loadGate.test.cjs +159 -0
- package/src/main/lib/__tests__/mcpToolCatalog.test.cjs +102 -0
- package/src/main/lib/__tests__/opsOwnership.test.cjs +7 -0
- package/src/main/lib/__tests__/opsRootAbsoluteCwd.test.cjs +151 -0
- package/src/main/lib/__tests__/opsRootResolve.test.cjs +149 -0
- package/src/main/lib/__tests__/prdDeclaredPaths.test.cjs +82 -0
- package/src/main/lib/__tests__/projectRootResolve.test.cjs +148 -0
- package/src/main/lib/__tests__/queueHealth.test.cjs +58 -0
- package/src/main/lib/__tests__/quietMachineLease.test.cjs +39 -0
- package/src/main/lib/__tests__/reaperHelpers.test.cjs +133 -0
- package/src/main/lib/__tests__/schedulerBatchDepends.test.cjs +19 -9
- package/src/main/lib/__tests__/schedulerBatchFairness.test.cjs +213 -0
- package/src/main/lib/__tests__/schedulerBatchLaunchHold.test.cjs +125 -0
- package/src/main/lib/__tests__/schedulerBatchProjectCap.test.cjs +127 -0
- package/src/main/lib/__tests__/schedulerBatchQuietMachine.test.cjs +109 -0
- package/src/main/lib/__tests__/schedulerMcpServerHeadlessRefusal.test.cjs +71 -0
- package/src/main/lib/__tests__/schedulerMcpServerHelp.test.cjs +217 -0
- package/src/main/lib/__tests__/schedulerMcpServerProjectHome.test.cjs +350 -0
- package/src/main/lib/activeIndexMerge.cjs +15 -0
- package/src/main/lib/activeIndexRebuild.cjs +133 -0
- package/src/main/lib/agentModelResolve.cjs +58 -0
- package/src/main/lib/buildTarget.cjs +3 -2
- package/src/main/lib/childWithLog.cjs +69 -2
- package/src/main/lib/claudeBin.cjs +54 -1
- package/src/main/lib/crossProjectFeedback.cjs +8 -1
- package/src/main/lib/definitionOfDone.cjs +3 -2
- package/src/main/lib/delegationReadiness.cjs +514 -26
- package/src/main/lib/ephemeralCwd.cjs +78 -0
- package/src/main/lib/epicDelegationStats.cjs +2 -1
- package/src/main/lib/epicMint.cjs +17 -1
- package/src/main/lib/epicStatusMirror.cjs +95 -0
- package/src/main/lib/epicValidationHook.cjs +2 -1
- package/src/main/lib/epicWorktreeMint.cjs +5 -2
- package/src/main/lib/fixChainDepth.cjs +45 -0
- package/src/main/lib/gitWorktree.cjs +520 -21
- package/src/main/lib/jobWorktree.cjs +2 -0
- package/src/main/lib/landedSinceRun.cjs +55 -0
- package/src/main/lib/launchFailure.cjs +357 -0
- package/src/main/lib/loadGate.cjs +134 -0
- package/src/main/lib/mcpToolCatalog.cjs +370 -0
- package/src/main/lib/opsErrorLog.cjs +12 -1
- package/src/main/lib/opsOwnership.cjs +106 -0
- package/src/main/lib/prdAdminRoutes.cjs +43 -3
- package/src/main/lib/prdAgentType.cjs +84 -0
- package/src/main/lib/prdCreate.cjs +103 -15
- package/src/main/lib/prdDeclaredPaths.cjs +70 -0
- package/src/main/lib/prdFrontmatter.cjs +17 -3
- package/src/main/lib/prdLocations.cjs +13 -6
- package/src/main/lib/projectHomeAdminRoutes.cjs +402 -0
- package/src/main/lib/projectPageSummarySchema.cjs +181 -0
- package/src/main/lib/projectRootResolve.cjs +134 -0
- package/src/main/lib/promptSessionSchema.cjs +7 -0
- package/src/main/lib/queueHealth.cjs +38 -0
- package/src/main/lib/queueStore.cjs +40 -7
- package/src/main/lib/quietMachineLease.cjs +48 -0
- package/src/main/lib/rcaReport.cjs +54 -4
- package/src/main/lib/reaperHelpers.cjs +64 -1
- package/src/main/lib/scheduleJobSchema.cjs +31 -0
- package/src/main/lib/scheduleJobTransitions.cjs +6 -2
- package/src/main/lib/schedulerBatch.cjs +301 -55
- package/src/main/lib/schedulerConfig.cjs +99 -0
- package/src/main/projectBrief.cjs +3 -2
- package/src/main/projectPages.cjs +162 -3
- package/src/main/promptSessionTranscript.cjs +0 -0
- package/src/main/pty.cjs +5 -0
- package/src/main/queueOps.cjs +15 -8
- package/src/main/runVerify.cjs +50 -9
- package/src/main/scheduler/prdParser.cjs +18 -1
- package/src/main/scheduler.cjs +1701 -130
- package/src/main/seedAgentPersonas.cjs +62 -21
- package/src/main/seedSchedulerMcp.cjs +58 -4
- package/src/main/templates/project-pages-catalog.json +741 -0
- package/src/main/templates/project-pages-pipeline.md +417 -0
- package/src/preload/api.d.ts +187 -3
- package/src/preload/index.cjs +9 -0
- package/src/seed/agents/project-home-builder.md +59 -0
- package/dist/assets/SaveBar-D-gCUx4n.js +0 -1
- package/dist/assets/Scheduler-Bpd4OGju.js +0 -14
- package/dist/assets/TagLibrary-E5CLeuVk.js +0 -1
package/src/main/health.cjs
CHANGED
|
@@ -9,13 +9,16 @@ const fsp = require('node:fs/promises');
|
|
|
9
9
|
const path = require('node:path');
|
|
10
10
|
const os = require('node:os');
|
|
11
11
|
const { execFileSync } = require('node:child_process');
|
|
12
|
-
const { POLL_INTERVAL_MS } = require('./lib/schedulerConfig.cjs');
|
|
12
|
+
const { POLL_INTERVAL_MS, loadGateThreshold } = require('./lib/schedulerConfig.cjs');
|
|
13
|
+
const { opsPath } = require('./lib/opsOwnership.cjs');
|
|
13
14
|
const { checkPersonaImports } = require('./lib/personaImportHealth.cjs');
|
|
15
|
+
const { checkDelegationReadiness } = require('./lib/delegationReadiness.cjs');
|
|
14
16
|
const { resolvePrdsDirs } = require('./lib/prdLocations.cjs');
|
|
15
17
|
const { migratePrds } = require('./lib/prdMigration.cjs');
|
|
16
18
|
const queueStore = require('./lib/queueStore.cjs');
|
|
17
19
|
const { computeStallSummary } = require('./scheduler.cjs');
|
|
18
20
|
const { DEFAULT_RUNS_DIR, computeReport, isRetentionEnabled, liveKeysFromJobs } = require('./lib/runLogRetention.cjs');
|
|
21
|
+
const { allProjectCwds } = require('../../scripts/lib/activeSessions.cjs');
|
|
19
22
|
|
|
20
23
|
const MAX_LOG_AGE_MS = 5 * 60_000; // 5 min — warn if no logs this old
|
|
21
24
|
const PROJECT_ROOT = path.resolve(__dirname, '../..');
|
|
@@ -225,6 +228,113 @@ function evaluatePrdMigrationHealth(migrationResult, legacyPrdsDir) {
|
|
|
225
228
|
};
|
|
226
229
|
}
|
|
227
230
|
|
|
231
|
+
// Pure shaping of checkDelegationReadiness()'s result (delegationReadiness.cjs)
|
|
232
|
+
// into a health component + issue lines — kept separate from the fs-touching
|
|
233
|
+
// check() call site so it's directly unit-testable, matching
|
|
234
|
+
// evaluatePrdMigrationHealth's pattern. delegationReadiness.cjs already wraps
|
|
235
|
+
// every filesystem read (readJsonSafe / try-catch), so a missing/unreadable
|
|
236
|
+
// config surfaces here as one of `checks` with ok:false, never a throw.
|
|
237
|
+
function evaluateDelegationChainHealth(delegationResult) {
|
|
238
|
+
const component = { ok: delegationResult.ok, checks: delegationResult.checks };
|
|
239
|
+
const issues = delegationResult.checks
|
|
240
|
+
.filter((c) => !c.ok)
|
|
241
|
+
.map((c) => `Delegation readiness: ${c.label} failed — ${c.detail}${c.fix ? ` (fix: ${c.fix})` : ''}`);
|
|
242
|
+
return { component, issues };
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// computeEpicIndexDrift(cwd) → { orphan_rows, orphan_files, unmirrored,
|
|
246
|
+
// orphanRowIds, orphanFileIds, unmirroredFiles }
|
|
247
|
+
//
|
|
248
|
+
// One project's drift between active-index.json (the index) and prompt-
|
|
249
|
+
// sessions/<id>.json (the status mirror epicStatusMirror.cjs writes on every
|
|
250
|
+
// status-changing write path). Pure filesystem read, never throws — an
|
|
251
|
+
// unreadable index or prompt-sessions dir degrades to empty rather than
|
|
252
|
+
// aborting the whole health check (matches evaluatePrdMigrationHealth's
|
|
253
|
+
// fail-open-on-read spirit).
|
|
254
|
+
function computeEpicIndexDrift(cwd) {
|
|
255
|
+
let dir;
|
|
256
|
+
try {
|
|
257
|
+
dir = opsPath(cwd, 'prompt-sessions');
|
|
258
|
+
} catch {
|
|
259
|
+
return { orphan_rows: 0, orphan_files: 0, unmirrored: 0 }; // unusable cwd degrades to empty, same as an unreadable index below
|
|
260
|
+
}
|
|
261
|
+
const indexPath = path.join(dir, 'active-index.json');
|
|
262
|
+
let sessions = {};
|
|
263
|
+
let tombstones = {};
|
|
264
|
+
try {
|
|
265
|
+
const parsed = JSON.parse(fs.readFileSync(indexPath, 'utf8'));
|
|
266
|
+
sessions = parsed && typeof parsed.sessions === 'object' && parsed.sessions ? parsed.sessions : {};
|
|
267
|
+
tombstones = parsed && typeof parsed.tombstones === 'object' && parsed.tombstones ? parsed.tombstones : {};
|
|
268
|
+
} catch { /* missing/unreadable index — treat as empty, not a throw */ }
|
|
269
|
+
|
|
270
|
+
let files = [];
|
|
271
|
+
try {
|
|
272
|
+
files = fs.readdirSync(dir).filter((f) => f.endsWith('.json') && f !== 'active-index.json');
|
|
273
|
+
} catch { /* prompt-sessions/ doesn't exist yet — nothing to compare */ }
|
|
274
|
+
|
|
275
|
+
const fileIds = new Set(files.map((f) => f.slice(0, -'.json'.length)));
|
|
276
|
+
const orphanRowIds = Object.keys(sessions).filter((id) => !fileIds.has(id));
|
|
277
|
+
|
|
278
|
+
const orphanFileIds = [];
|
|
279
|
+
const unmirroredFiles = [];
|
|
280
|
+
for (const file of files) {
|
|
281
|
+
const id = file.slice(0, -'.json'.length);
|
|
282
|
+
let parsed;
|
|
283
|
+
try {
|
|
284
|
+
parsed = JSON.parse(fs.readFileSync(path.join(dir, file), 'utf8'));
|
|
285
|
+
} catch {
|
|
286
|
+
continue; // unreadable file — neither orphan nor unmirrored classification applies
|
|
287
|
+
}
|
|
288
|
+
if (!parsed || typeof parsed !== 'object' || !parsed.status) {
|
|
289
|
+
unmirroredFiles.push(file);
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
const looksLive = (parsed.status === 'proposed' || parsed.status === 'active') && !parsed.archivedAt;
|
|
293
|
+
const hasRow = Object.prototype.hasOwnProperty.call(sessions, id);
|
|
294
|
+
const isTombstoned = Object.prototype.hasOwnProperty.call(tombstones, id);
|
|
295
|
+
if (looksLive && !hasRow && !isTombstoned) orphanFileIds.push(file);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return {
|
|
299
|
+
orphan_rows: orphanRowIds.length,
|
|
300
|
+
orphan_files: orphanFileIds.length,
|
|
301
|
+
unmirrored: unmirroredFiles.length,
|
|
302
|
+
orphanRowIds,
|
|
303
|
+
orphanFileIds,
|
|
304
|
+
unmirroredFiles,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// evaluateEpicIndexHealth(cwds) → { component, issues }
|
|
309
|
+
//
|
|
310
|
+
// Shapes computeEpicIndexDrift's per-project result into a health component +
|
|
311
|
+
// issue lines, matching evaluateDelegationChainHealth's pattern. Only
|
|
312
|
+
// orphan_rows (an index row whose Epic file is gone — the actual data-loss
|
|
313
|
+
// signal) is an issue; orphan_files and unmirrored are informational drift,
|
|
314
|
+
// not failures — a fresh install or an Epic mid-migration legitimately has
|
|
315
|
+
// unmirrored files.
|
|
316
|
+
function evaluateEpicIndexHealth(cwds) {
|
|
317
|
+
const byProject = {};
|
|
318
|
+
const issues = [];
|
|
319
|
+
let anyOrphanRows = false;
|
|
320
|
+
for (const cwd of cwds) {
|
|
321
|
+
const drift = computeEpicIndexDrift(cwd);
|
|
322
|
+
byProject[cwd] = {
|
|
323
|
+
orphan_rows: drift.orphan_rows,
|
|
324
|
+
orphan_files: drift.orphan_files,
|
|
325
|
+
unmirrored: drift.unmirrored,
|
|
326
|
+
};
|
|
327
|
+
if (drift.orphan_rows > 0) {
|
|
328
|
+
anyOrphanRows = true;
|
|
329
|
+
issues.push(
|
|
330
|
+
`Epic index drift: ${cwd} has ${drift.orphan_rows} active-index.json row(s) with no matching `
|
|
331
|
+
+ `prompt-sessions/<id>.json file (${drift.orphanRowIds.join(', ')})`
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return { component: { ok: !anyOrphanRows, byProject }, issues };
|
|
336
|
+
}
|
|
337
|
+
|
|
228
338
|
async function check() {
|
|
229
339
|
const start = Date.now();
|
|
230
340
|
const status = {
|
|
@@ -340,6 +450,15 @@ async function check() {
|
|
|
340
450
|
byProject: computeProjectProblemCounts(queueState.jobs),
|
|
341
451
|
perProjectStall,
|
|
342
452
|
tickLiveness: liveness.reason,
|
|
453
|
+
// Informational only (PRD 1085): current 1-min loadavg per core vs the
|
|
454
|
+
// launch-gate threshold, so a "nothing is launching" report can be
|
|
455
|
+
// read next to the reason without opening the app.
|
|
456
|
+
loadRatio: (() => {
|
|
457
|
+
const cores = (os.cpus() || []).length;
|
|
458
|
+
const l1 = os.loadavg()[0];
|
|
459
|
+
return cores > 0 && Number.isFinite(l1) ? Number((l1 / cores).toFixed(3)) : null;
|
|
460
|
+
})(),
|
|
461
|
+
loadGateThreshold: loadGateThreshold(),
|
|
343
462
|
};
|
|
344
463
|
if (liveness.stalled) {
|
|
345
464
|
const ageMin = Math.round(liveness.tickAgeMs / 60_000);
|
|
@@ -505,6 +624,22 @@ async function check() {
|
|
|
505
624
|
}
|
|
506
625
|
}
|
|
507
626
|
|
|
627
|
+
// 6.55. Delegation-chain readiness — answers "can this machine actually
|
|
628
|
+
// delegate work to the scheduler?" (see delegationReadiness.cjs's header:
|
|
629
|
+
// when the scheduler MCP isn't registered, scheduler_create_prd is simply
|
|
630
|
+
// absent from the agent's tool list — no error to catch, so an agent asked
|
|
631
|
+
// to delegate just implements inline instead). Critical: a failing check
|
|
632
|
+
// here means Epics silently stop queueing PRDs on this machine.
|
|
633
|
+
try {
|
|
634
|
+
const delegation = await checkDelegationReadiness({ cwd: PROJECT_ROOT });
|
|
635
|
+
const { component, issues: delegationIssues } = evaluateDelegationChainHealth(delegation);
|
|
636
|
+
status.components.delegation_chain = component;
|
|
637
|
+
status.issues.push(...delegationIssues);
|
|
638
|
+
} catch (e) {
|
|
639
|
+
status.components.delegation_chain = { ok: false, error: e.message };
|
|
640
|
+
status.issues.push(`Delegation readiness check failed: ${e.message}`);
|
|
641
|
+
}
|
|
642
|
+
|
|
508
643
|
// 6.6. Run-log retention report (informational only — never blocks health).
|
|
509
644
|
// Read-only against the REAL scheduled-plans/runs/ dir: reports current
|
|
510
645
|
// usage + what the configured policy (if any) would remove. Nothing is
|
|
@@ -536,11 +671,24 @@ async function check() {
|
|
|
536
671
|
status.components.run_log_retention = { ok: true, error: e.message };
|
|
537
672
|
}
|
|
538
673
|
|
|
674
|
+
// 6.65. Epic index drift — orphan_rows/orphan_files/unmirrored across every
|
|
675
|
+
// project this machine has ever opened (allProjectCwds, no recency filter
|
|
676
|
+
// — a quiet project still owns its Epics). See computeEpicIndexDrift's
|
|
677
|
+
// header.
|
|
678
|
+
try {
|
|
679
|
+
const { component, issues: epicIndexIssues } = evaluateEpicIndexHealth(allProjectCwds());
|
|
680
|
+
status.components.epic_index = component;
|
|
681
|
+
status.issues.push(...epicIndexIssues);
|
|
682
|
+
} catch (e) {
|
|
683
|
+
status.components.epic_index = { ok: false, error: e.message };
|
|
684
|
+
status.issues.push(`Epic index health check failed: ${e.message}`);
|
|
685
|
+
}
|
|
686
|
+
|
|
539
687
|
// 7. Summary scoring: ok if all critical components pass.
|
|
540
688
|
// Critical: nodejs, config dir, typescript, build artifact, test infrastructure.
|
|
541
689
|
// Non-fatal: scheduler/transcripts dirs may not exist on fresh install.
|
|
542
690
|
// Informational: app log age (shows if app is running, but not blocking).
|
|
543
|
-
const criticalComponents = ['nodejs', 'config_dir', 'typescript', 'build_artifact', 'test_infrastructure', 'scheduler_queue', 'prd_migration', 'claude_md_budget'];
|
|
691
|
+
const criticalComponents = ['nodejs', 'config_dir', 'typescript', 'build_artifact', 'test_infrastructure', 'scheduler_queue', 'prd_migration', 'claude_md_budget', 'delegation_chain'];
|
|
544
692
|
status.ok = criticalComponents.every((c) => status.components[c]?.ok !== false);
|
|
545
693
|
|
|
546
694
|
status.elapsedMs = Date.now() - start;
|
|
@@ -561,6 +709,9 @@ module.exports = {
|
|
|
561
709
|
evaluateTickLiveness,
|
|
562
710
|
readFreshHeartbeat,
|
|
563
711
|
evaluatePrdMigrationHealth,
|
|
712
|
+
evaluateDelegationChainHealth,
|
|
713
|
+
computeEpicIndexDrift,
|
|
714
|
+
evaluateEpicIndexHealth,
|
|
564
715
|
computeProjectProblemCounts,
|
|
565
716
|
evaluatePerProjectStall,
|
|
566
717
|
parseClaudeMdBudget,
|
package/src/main/index.cjs
CHANGED
|
@@ -27,6 +27,7 @@ const scheduler = require('./scheduler.cjs');
|
|
|
27
27
|
const { createAdminHttp } = require('./lib/localAdminHttp.cjs');
|
|
28
28
|
const prdCreate = require('./lib/prdCreate.cjs');
|
|
29
29
|
const prdAdminRoutes = require('./lib/prdAdminRoutes.cjs');
|
|
30
|
+
const projectHomeAdminRoutes = require('./lib/projectHomeAdminRoutes.cjs');
|
|
30
31
|
const { appendAuditEvent } = require('./lib/auditLog.cjs');
|
|
31
32
|
const chatRunner = require('./chatRunner.cjs');
|
|
32
33
|
const promptSessionEvents = require('./promptSessionEvents.cjs');
|
|
@@ -36,7 +37,8 @@ const epicWorktreeMint = require('./lib/epicWorktreeMint.cjs');
|
|
|
36
37
|
const epicWorktreeMerge = require('./lib/epicWorktreeMerge.cjs');
|
|
37
38
|
const epicWorktreeProjectConfig = require('./lib/epicWorktreeProjectConfig.cjs');
|
|
38
39
|
const agentLibrary = require('./agentLibrary.cjs');
|
|
39
|
-
const { checkDelegationReadiness } = require('./lib/delegationReadiness.cjs');
|
|
40
|
+
const { checkDelegationReadiness, installPrdWriteGuard } = require('./lib/delegationReadiness.cjs');
|
|
41
|
+
const { MCP_TOOL_CATALOG, MCP_RECIPES } = require('./lib/mcpToolCatalog.cjs');
|
|
40
42
|
const { sendIfAlive } = require('./lib/sendToRenderer.cjs');
|
|
41
43
|
const { resolveBuildTarget } = require('./lib/buildTarget.cjs');
|
|
42
44
|
const crossProjectFeedback = require('./lib/crossProjectFeedback.cjs');
|
|
@@ -44,6 +46,7 @@ const adminHttp = createAdminHttp();
|
|
|
44
46
|
scheduler.registerAdminRoutes(adminHttp);
|
|
45
47
|
prdCreate.registerAdminRoute(adminHttp, scheduler.remote);
|
|
46
48
|
prdAdminRoutes.registerAdminRoute(adminHttp, scheduler.remote);
|
|
49
|
+
projectHomeAdminRoutes.registerAdminRoute(adminHttp);
|
|
47
50
|
chatRunner.registerAdminRoute(adminHttp);
|
|
48
51
|
// Project-to-project feedback conduit — the app is the only process holding
|
|
49
52
|
// several projects' operations roots open, so it performs the cross-folder
|
|
@@ -68,7 +71,7 @@ const schedulerConfig = require('./lib/schedulerConfig.cjs');
|
|
|
68
71
|
const memoryTool = require('./memoryTool.cjs');
|
|
69
72
|
const { registerMemoryAggregateIpc } = require('./memoryAggregate.cjs');
|
|
70
73
|
const { registerProjectBriefIpc } = require('./projectBrief.cjs');
|
|
71
|
-
const { registerProjectPagesIpc } = require('./projectPages.cjs');
|
|
74
|
+
const { registerProjectPagesIpc, attachWindow: attachProjectPagesWindow, closeAllOutputWatchers: closeAllProjectPagesWatchers } = require('./projectPages.cjs');
|
|
72
75
|
const { registerBilkoHostIpc } = require('./bilkoHost.cjs');
|
|
73
76
|
const promptSessionTranscript = require('./promptSessionTranscript.cjs');
|
|
74
77
|
const { computeEpicDelegationStats } = require('./lib/epicDelegationStats.cjs');
|
|
@@ -81,6 +84,9 @@ const { listExchanges } = require('./exchanges.cjs');
|
|
|
81
84
|
const { resolveClaudeBin } = require('./lib/claudeBin.cjs');
|
|
82
85
|
const { checkInsideHome, assertInsideHome } = require('./lib/insideHome.cjs');
|
|
83
86
|
const { openInEditor, openFileInEditor, openInFinder, openInTerminal } = require('./lib/openExternalApp.cjs');
|
|
87
|
+
const { rebuildActiveIndex } = require('./lib/activeIndexRebuild.cjs');
|
|
88
|
+
const { activeIndexPath: promptSessionsActiveIndexPath } = require('./lib/epicMint.cjs');
|
|
89
|
+
const { allProjectCwds } = require('../../scripts/lib/activeSessions.cjs');
|
|
84
90
|
|
|
85
91
|
let mainWindow = null;
|
|
86
92
|
let rebooting = false;
|
|
@@ -296,6 +302,7 @@ function relaunchViaNpx() {
|
|
|
296
302
|
async function rebootApp() {
|
|
297
303
|
ptyManager.killAll();
|
|
298
304
|
configMgr.closeAllWatchers();
|
|
305
|
+
closeAllProjectPagesWatchers();
|
|
299
306
|
transcripts.closeAll();
|
|
300
307
|
watchers.manager.killAll();
|
|
301
308
|
|
|
@@ -322,6 +329,7 @@ async function rebootApp() {
|
|
|
322
329
|
chatRunner.attachWindow(mainWindow);
|
|
323
330
|
promptSessionEvents.attachWindow(mainWindow);
|
|
324
331
|
attachDocEditWindow(mainWindow);
|
|
332
|
+
attachProjectPagesWindow(mainWindow);
|
|
325
333
|
rebooting = false;
|
|
326
334
|
return;
|
|
327
335
|
}
|
|
@@ -456,6 +464,11 @@ ipcMain.handle('app:launch-mode', () => ({
|
|
|
456
464
|
// `claude mcp list`. Read-only, single in-flight call — no polling.
|
|
457
465
|
ipcMain.handle('mcp:status', () => probeMcpStatus());
|
|
458
466
|
|
|
467
|
+
// Project Home's "Agent tools" block (PhAgentTools): the same catalog data
|
|
468
|
+
// GET /admin/mcp/catalog serves to external callers, read in-process here so
|
|
469
|
+
// the renderer never makes a direct HTTP call. Static data, no cwd needed.
|
|
470
|
+
ipcMain.handle('mcp:catalog', () => ({ ok: true, tools: MCP_TOOL_CATALOG, recipes: MCP_RECIPES }));
|
|
471
|
+
|
|
459
472
|
// Agent Library nav page (Home face only): global `~/.claude/agents/*.md`
|
|
460
473
|
// personas plus, per currently-open project tab, whether that project
|
|
461
474
|
// overlays the same agent name at `<cwd>/.claude/agents/<name>.md`. Read-only.
|
|
@@ -494,12 +507,19 @@ ipcMain.handle('agents:get-persona-body', validated(schemas.agentsGetPersonaBody
|
|
|
494
507
|
agentLibrary.getPersonaBody(payload)));
|
|
495
508
|
|
|
496
509
|
// "Can this project actually delegate?" probe (PRD: delegation-readiness).
|
|
497
|
-
// Structured data over the
|
|
498
|
-
//
|
|
499
|
-
ipcMain.handle('app:delegation-readiness', validated(schemas.delegationReadinessCwd, (payload) =>
|
|
510
|
+
// Structured data over the preconditions for scheduler_create_prd being in an
|
|
511
|
+
// agent's tool list at all — surfacing it in the UI is a sibling PRD.
|
|
512
|
+
ipcMain.handle('app:delegation-readiness', validated(schemas.delegationReadinessCwd, async (payload) =>
|
|
500
513
|
checkDelegationReadiness(payload)
|
|
501
514
|
));
|
|
502
515
|
|
|
516
|
+
// The one readiness check with a sanctioned one-press install. Session Manager
|
|
517
|
+
// standardizes on the REFERENCE approach (absolute path to this repo's guard
|
|
518
|
+
// script, never a vendored copy) — see installPrdWriteGuard's header.
|
|
519
|
+
ipcMain.handle('app:install-prd-write-guard', validated(schemas.delegationReadinessCwd, (payload) =>
|
|
520
|
+
installPrdWriteGuard(payload)
|
|
521
|
+
));
|
|
522
|
+
|
|
503
523
|
ipcMain.handle('app:engage-rules-path', () => process.env.SESSION_MANAGER_ENGAGE_RULES || null);
|
|
504
524
|
|
|
505
525
|
// Boot diagnostics — renderer polls these to surface toasts when `claude` isn't
|
|
@@ -1152,6 +1172,7 @@ app.whenReady().then(async () => {
|
|
|
1152
1172
|
chatRunner.attachWindow(mainWindow);
|
|
1153
1173
|
promptSessionEvents.attachWindow(mainWindow);
|
|
1154
1174
|
attachDocEditWindow(mainWindow);
|
|
1175
|
+
attachProjectPagesWindow(mainWindow);
|
|
1155
1176
|
// Scheduler ownership (PRD 834): exactly one instance machine-wide runs
|
|
1156
1177
|
// scheduler mutation (boot reconciliation, ticking, sweep, supervisor) and
|
|
1157
1178
|
// the admin API. Electron's own single-instance lock is keyed by userData
|
|
@@ -1193,6 +1214,42 @@ app.whenReady().then(async () => {
|
|
|
1193
1214
|
seedSchedulerMcp({ logger: console, writeLog: logs.writeLine }).catch((e) => {
|
|
1194
1215
|
logs.writeLine({ scope: 'seed-scheduler-mcp', level: 'error', message: 'seed failed', meta: { error: e?.message } });
|
|
1195
1216
|
});
|
|
1217
|
+
// Epic index self-heal: a project whose active-index.json is MISSING or
|
|
1218
|
+
// fails to parse has lost every open Epic's status unless the per-Epic
|
|
1219
|
+
// status mirror (epicStatusMirror.cjs) can rebuild it. Runs once per boot,
|
|
1220
|
+
// fire-and-forget, and only touches a project whose index reads unclean —
|
|
1221
|
+
// a clean index is never rewritten here. See activeIndexRebuild.cjs.
|
|
1222
|
+
try {
|
|
1223
|
+
for (const projectCwd of allProjectCwds()) {
|
|
1224
|
+
const indexPath = promptSessionsActiveIndexPath(projectCwd);
|
|
1225
|
+
let unclean = false;
|
|
1226
|
+
let parseError = null;
|
|
1227
|
+
if (!fs.existsSync(indexPath)) {
|
|
1228
|
+
unclean = true;
|
|
1229
|
+
} else {
|
|
1230
|
+
try {
|
|
1231
|
+
JSON.parse(fs.readFileSync(indexPath, 'utf8'));
|
|
1232
|
+
} catch (e) {
|
|
1233
|
+
unclean = true;
|
|
1234
|
+
parseError = e.message;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
if (!unclean) continue;
|
|
1238
|
+
try {
|
|
1239
|
+
const { rows, skipped } = rebuildActiveIndex(projectCwd);
|
|
1240
|
+
logs.writeLine({
|
|
1241
|
+
scope: 'active-index-rebuild',
|
|
1242
|
+
level: 'info',
|
|
1243
|
+
message: 'rebuilt active-index.json from status mirrors',
|
|
1244
|
+
meta: { cwd: projectCwd, parseError, restored: rows.length, skipped: skipped.length },
|
|
1245
|
+
});
|
|
1246
|
+
} catch (e) {
|
|
1247
|
+
logs.writeLine({ scope: 'active-index-rebuild', level: 'error', message: 'rebuild failed', meta: { cwd: projectCwd, error: e?.message } });
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
} catch (e) {
|
|
1251
|
+
logs.writeLine({ scope: 'active-index-rebuild', level: 'error', message: 'boot sweep failed', meta: { error: e?.message } });
|
|
1252
|
+
}
|
|
1196
1253
|
// History rollup finalize pass: deferred 30s past boot so it never competes
|
|
1197
1254
|
// with first-paint, fire-and-forget (cron/offline refresh is PRD 651 — this
|
|
1198
1255
|
// is just the one-shot in-app pass).
|
|
@@ -1292,6 +1349,7 @@ app.on('window-all-closed', () => {
|
|
|
1292
1349
|
if (rebooting) return; // new window is about to be created
|
|
1293
1350
|
ptyManager.killAll();
|
|
1294
1351
|
configMgr.closeAllWatchers();
|
|
1352
|
+
closeAllProjectPagesWatchers();
|
|
1295
1353
|
transcripts.closeAll();
|
|
1296
1354
|
watchers.manager.killAll();
|
|
1297
1355
|
if (process.platform !== 'darwin') app.quit();
|
|
@@ -1306,6 +1364,7 @@ app.on('before-quit', () => {
|
|
|
1306
1364
|
supervisor.stopSupervisor();
|
|
1307
1365
|
ptyManager.killAll();
|
|
1308
1366
|
configMgr.closeAllWatchers();
|
|
1367
|
+
closeAllProjectPagesWatchers();
|
|
1309
1368
|
transcripts.closeAll();
|
|
1310
1369
|
watchers.manager.killAll();
|
|
1311
1370
|
adminHttp.stop().catch(() => {});
|
package/src/main/ipcSchemas.cjs
CHANGED
|
@@ -10,6 +10,7 @@ const os = require('node:os');
|
|
|
10
10
|
const path = require('node:path');
|
|
11
11
|
const { PromptSessionSchema, EpicTagSchema, EpicSourceSchema, EpicIntakeSectionSchema } = require('./lib/promptSessionSchema.cjs');
|
|
12
12
|
const { PrdWorkTypeSchema } = require('./lib/workTypeLibrary.cjs');
|
|
13
|
+
const { PERSONA_NAME_RE } = require('./agentLibrary.cjs');
|
|
13
14
|
const { AgentPersonaSaveSchema } = require('./lib/agentPersonaSchema.cjs');
|
|
14
15
|
|
|
15
16
|
// ──────────────────────────────────────────── PTY
|
|
@@ -224,6 +225,12 @@ const promptSessionsMergeToMain = z.object({
|
|
|
224
225
|
epicId: z.string().regex(PROMPT_SESSION_ID_RE),
|
|
225
226
|
branch: z.string().regex(/^sm-epic\/[A-Za-z0-9_-]{1,64}$/),
|
|
226
227
|
dir: z.string().min(1).max(4096),
|
|
228
|
+
// Base-tree WIP paths carried into this worktree at creation time
|
|
229
|
+
// (gitWorktree.cjs's createWorktree, PRD 1094) — threaded through so
|
|
230
|
+
// integrateEpicBranch can apply the same carried-wip-only skip the job
|
|
231
|
+
// path already gets. Absent on a worktree created before this field
|
|
232
|
+
// existed, or one whose base tree was clean at creation.
|
|
233
|
+
carriedPaths: z.array(z.string().min(1).max(4096)).max(2000).optional(),
|
|
227
234
|
});
|
|
228
235
|
|
|
229
236
|
// Renderer entry points for the per-project "disable Epic worktree
|
|
@@ -299,9 +306,21 @@ const PRD_CREATE_SLUG_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
|
299
306
|
// frontmatter-shaped lines into the body. Block newlines at the boundary
|
|
300
307
|
// instead of trying to escape them later.
|
|
301
308
|
const NO_NEWLINE_RE = /^[^\r\n]*$/;
|
|
309
|
+
// WHO executes a PRD — a persona name (agentLibrary.cjs's PERSONA_NAME_RE),
|
|
310
|
+
// distinct from `tag` (the WORK TYPE, above). Not a closed enum here: the
|
|
311
|
+
// Agent Library is user-editable, so the actual FK check (does this name
|
|
312
|
+
// resolve to a readable persona file) happens at write time in
|
|
313
|
+
// prdCreate.cjs's createPrd via prdAgentType.cjs's assertAgentTypeWritable —
|
|
314
|
+
// this schema only enforces the name SHAPE.
|
|
315
|
+
const PrdAgentTypeSchema = z.string().regex(PERSONA_NAME_RE, 'agent name must be lowercase, hyphenated (e.g. "dev-lead")');
|
|
302
316
|
const schedulerCreatePrd = z.object({
|
|
303
317
|
title: z.string().min(1).max(200).regex(NO_NEWLINE_RE, 'must not contain newlines'),
|
|
304
|
-
|
|
318
|
+
// Optional (PRD: worktree-cwd Epic-lookup hazard) — when omitted, prdCreate.cjs's
|
|
319
|
+
// createPrd resolves the real project cwd server-side via
|
|
320
|
+
// projectRootResolve.cjs's resolveProjectContext, using originClaudeSessionId/
|
|
321
|
+
// sourcePromptId/originProjectRoot. A call with none of those still fails with
|
|
322
|
+
// a clear "cwd is required" error.
|
|
323
|
+
cwd: z.string().min(1).max(4096).regex(NO_NEWLINE_RE, 'must not contain newlines').optional(),
|
|
305
324
|
estimateMinutes: z.number().int().min(1).max(100000),
|
|
306
325
|
goal: z.string().min(1).max(20000),
|
|
307
326
|
acceptanceCriteria: z.array(z.string().min(1).max(2000)).min(1).max(100),
|
|
@@ -333,6 +352,13 @@ const schedulerCreatePrd = z.object({
|
|
|
333
352
|
// within an Epic's own chat session joins that Epic even if the tool call
|
|
334
353
|
// forgot to pass sourcePromptId explicitly.
|
|
335
354
|
originClaudeSessionId: z.string().min(1).max(128).regex(NO_NEWLINE_RE, 'must not contain newlines').optional(),
|
|
355
|
+
// Trusted project-root hint (SM_PROJECT_ROOT env, stamped by chatRunner.cjs/
|
|
356
|
+
// pty.cjs/scheduler.cjs's job spawn onto the child that ultimately calls this
|
|
357
|
+
// route, forwarded by scheduler-mcp-server.cjs as originProjectRoot) — never
|
|
358
|
+
// written to frontmatter; projectRootResolve.cjs's resolveProjectContext
|
|
359
|
+
// treats it as a higher-priority hint than the caller-supplied cwd, but
|
|
360
|
+
// still loses to a resolved Epic's own project cwd.
|
|
361
|
+
originProjectRoot: z.string().min(1).max(4096).regex(NO_NEWLINE_RE, 'must not contain newlines').optional(),
|
|
336
362
|
// User-selected work-type tag (PRD 774; independence from the parent
|
|
337
363
|
// Epic's own tag confirmed PRD 1041) — deterministic, never LLM-classified.
|
|
338
364
|
// Epic tag and PRD tag draw on the same WorkType vocabulary
|
|
@@ -340,11 +366,27 @@ const schedulerCreatePrd = z.object({
|
|
|
340
366
|
// excluded here for the same reason adminPrdFrontmatterPatch excludes it
|
|
341
367
|
// below — a discussion-tagged Epic never reaches PRD authoring.
|
|
342
368
|
tag: PrdWorkTypeSchema.optional(),
|
|
369
|
+
// WHO executes this PRD (persona name, `~/.claude/agents/<name>.md`) —
|
|
370
|
+
// independent of `tag` above. Optional: prdCreate.cjs's buildPrdBody fills
|
|
371
|
+
// in DEFAULT_PRD_AGENT_TYPE ('dev-lead') when omitted.
|
|
372
|
+
agentType: PrdAgentTypeSchema.optional(),
|
|
373
|
+
// Opt-in exclusive-lease flag (PRD 1107): the scheduler dispatches this PRD
|
|
374
|
+
// only once zero other jobs are running machine-wide, and holds every other
|
|
375
|
+
// job off the whole session-slot pool while it runs — for a PRD whose
|
|
376
|
+
// acceptance criteria are wall-clock/timing measurements (frame time,
|
|
377
|
+
// performance fences) that CPU contention from sibling jobs would otherwise
|
|
378
|
+
// invalidate. See prdCreate.cjs's buildPrdBody and schedulerBatch.cjs's
|
|
379
|
+
// pickNextBatch.
|
|
380
|
+
quietMachine: z.boolean().optional(),
|
|
343
381
|
});
|
|
344
382
|
|
|
345
383
|
// Bulk archive: slug list, capped to limit unbounded retag/archive payloads.
|
|
384
|
+
// Optional cwd narrows every slug's directory search to one project (same
|
|
385
|
+
// role as scheduler_get_prd/scheduler_update_prd's cwd hint) — omitted, the
|
|
386
|
+
// search still spans every candidate PRD dir machine-wide.
|
|
346
387
|
const scheduleArchivePrd = z.object({
|
|
347
388
|
slugs: z.array(z.string().regex(SCHEDULE_SLUG_RE)).min(1).max(500),
|
|
389
|
+
cwd: z.string().min(1).max(4096).optional(),
|
|
348
390
|
});
|
|
349
391
|
|
|
350
392
|
const scheduleRetagItem = z.object({
|
|
@@ -402,12 +444,17 @@ const adminPrdFrontmatterPatch = z.object({
|
|
|
402
444
|
// discussion-tagged Epic never reaches PRD authoring) — see
|
|
403
445
|
// workTypeLibrary.cjs's PrdWorkTypeSchema.
|
|
404
446
|
tag: PrdWorkTypeSchema.optional(),
|
|
447
|
+
// WHO executes this PRD — patchable after creation too, same as `tag`.
|
|
448
|
+
agentType: PrdAgentTypeSchema.optional(),
|
|
405
449
|
// Provenance stamp (PRD-authoring lockdown). Free-text rather than an enum
|
|
406
450
|
// — 'scheduler-api' (create-time) and 'legacy-adopted' (migration/manual
|
|
407
451
|
// adopt) are the two values this codebase writes today, but the field
|
|
408
452
|
// itself is descriptive, not a closed set.
|
|
409
453
|
createdVia: z.string().min(1).max(64).regex(NO_NEWLINE_RE, 'must not contain newlines').optional(),
|
|
410
454
|
issuedAt: z.string().min(1).max(64).regex(NO_NEWLINE_RE, 'must not contain newlines').optional(),
|
|
455
|
+
// Opt-in exclusive-lease flag (PRD 1107) — patchable after creation too,
|
|
456
|
+
// same as every other recognized key here.
|
|
457
|
+
quietMachine: z.boolean().optional(),
|
|
411
458
|
});
|
|
412
459
|
|
|
413
460
|
const adminUpdatePrd = z.object({
|
|
@@ -425,6 +472,7 @@ const adminUpdatePrd = z.object({
|
|
|
425
472
|
|
|
426
473
|
const adminCancelJob = z.object({
|
|
427
474
|
slug: z.string().regex(SCHEDULE_SLUG_RE),
|
|
475
|
+
cwd: z.string().min(1).max(4096).optional(),
|
|
428
476
|
});
|
|
429
477
|
|
|
430
478
|
// ──────────────────────────────────────────── Projects
|
|
@@ -565,6 +613,23 @@ const projectPagesCwd = z.object({
|
|
|
565
613
|
cwd: z.string().min(1).max(4096),
|
|
566
614
|
}).strict();
|
|
567
615
|
|
|
616
|
+
// ──────────────────────────────────────────── Project Home admin routes
|
|
617
|
+
// (PRD: project-home-admin-routes). Same validation split as
|
|
618
|
+
// projectPagesCwd above: real path validation is projectRootResolve.cjs +
|
|
619
|
+
// config.cjs's validatePath at first fs access.
|
|
620
|
+
const projectHomeAdminCwdQuery = z.object({
|
|
621
|
+
cwd: z.string().min(1).max(4096),
|
|
622
|
+
}).strict();
|
|
623
|
+
const projectHomeAdminValidateSummaryBody = z.object({
|
|
624
|
+
cwd: z.string().min(1).max(4096),
|
|
625
|
+
summary: z.unknown(),
|
|
626
|
+
}).strict();
|
|
627
|
+
const projectHomeAdminRenderBody = z.object({
|
|
628
|
+
cwd: z.string().min(1).max(4096),
|
|
629
|
+
summary: z.unknown(),
|
|
630
|
+
picks: z.unknown(),
|
|
631
|
+
}).strict();
|
|
632
|
+
|
|
568
633
|
// ──────────────────────────────────────────── Host on Bilko.run
|
|
569
634
|
// Same validation split: real path validation is config.cjs's validatePath
|
|
570
635
|
// at first fs access in bilkoHost.cjs — these schemas only bound wire shape.
|
|
@@ -973,6 +1038,9 @@ module.exports = {
|
|
|
973
1038
|
memoryStale,
|
|
974
1039
|
projectBriefCwd,
|
|
975
1040
|
projectPagesCwd,
|
|
1041
|
+
projectHomeAdminCwdQuery,
|
|
1042
|
+
projectHomeAdminValidateSummaryBody,
|
|
1043
|
+
projectHomeAdminRenderBody,
|
|
976
1044
|
bilkoHostCwd,
|
|
977
1045
|
bilkoHostPrepareBundle,
|
|
978
1046
|
bilkoHostAddDocument,
|