claude-code-session-manager 0.86.0 → 0.88.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/README.md +26 -53
- package/dist/assets/AgentLibrary-BZ6IY2g1.js +3 -0
- package/dist/assets/{DataModel-Q4jhl24R.js → DataModel-dAoGAODX.js} +1 -1
- package/dist/assets/{History-Cj2FejEo.js → History-CF157HyX.js} +2 -2
- package/dist/assets/{Hooks-CaelQI6t.js → Hooks-CCPPipBk.js} +3 -3
- package/dist/assets/{HostBilko--v7cMR8I.js → HostBilko-Dlmz2m06.js} +1 -1
- package/dist/assets/{Library-DgI9oCCZ.js → Library-BaXcboWX.js} +1 -1
- package/dist/assets/{ListDetail-DYUZN-x-.js → ListDetail-CoUCzG5S.js} +1 -1
- package/dist/assets/MarkdownEditor-DTg3JlOT.js +1 -0
- package/dist/assets/{McpServers-ypCYURh3.js → McpServers-DdjY1sA3.js} +2 -2
- package/dist/assets/{Memory-C2qYp-3M.js → Memory-BJ3Jlw0C.js} +4 -4
- package/dist/assets/{Panel-Cj2kw-Zv.js → Panel-ygRSgc41.js} +1 -1
- package/dist/assets/Permissions-m0KAnccF.js +3 -0
- package/dist/assets/{Plugins-C1Vj8_dU.js → Plugins-CJRl6B3Q.js} +2 -2
- package/dist/assets/{ProvenanceBadge-DczPNM5U.js → ProvenanceBadge-w8cnlVvW.js} +1 -1
- package/dist/assets/{SaveBar-Cd_7U6Gb.js → SaveBar-BpyPcUIk.js} +1 -1
- package/dist/assets/Scheduler-D2ppxSfy.js +14 -0
- package/dist/assets/{ScopeSwitcher-DVSyI44-.js → ScopeSwitcher-EyuIwt-j.js} +1 -1
- package/dist/assets/Settings-BHSGGFEc.js +3 -0
- package/dist/assets/{SkillReferenceGraph-CUv1_Q2c.js → SkillReferenceGraph-BNaZ2JCF.js} +1 -1
- package/dist/assets/{Skills-C_YHkAy-.js → Skills-Dl3q75QV.js} +2 -2
- package/dist/assets/{SystemPrompt-B8R7T9xn.js → SystemPrompt-Bg6F-Cyp.js} +1 -1
- package/dist/assets/{TagLibrary-dj9YHWyy.js → TagLibrary-BSutflCy.js} +1 -1
- package/dist/assets/{TiptapBody-DnSBUjHE.js → TiptapBody-CXih4xrc.js} +1 -1
- package/dist/assets/{Toggle-CjV_BJn6.js → Toggle-2qtUHhA9.js} +1 -1
- package/dist/assets/{index-CXFQIPhO.js → index-B3emy2oI.js} +583 -583
- package/dist/assets/{index-CDo9xBR9.css → index-BHTX4OTc.css} +1 -1
- package/dist/assets/settingsSchema-CdzxaqwM.js +3 -0
- package/dist/assets/{whisperWorker-Dbia1OpC.js → whisperWorker-C7ZGQwKg.js} +7 -7
- package/dist/index.html +2 -2
- package/dist/vad/ort-wasm-simd-threaded.asyncify.mjs +106 -110
- package/dist/vad/ort-wasm-simd-threaded.asyncify.wasm +0 -0
- package/dist/vad/ort-wasm-simd-threaded.jsep.mjs +98 -98
- package/dist/vad/ort-wasm-simd-threaded.jsep.wasm +0 -0
- package/dist/vad/ort-wasm-simd-threaded.jspi.mjs +99 -102
- package/dist/vad/ort-wasm-simd-threaded.jspi.wasm +0 -0
- package/dist/vad/ort-wasm-simd-threaded.mjs +46 -46
- package/dist/vad/ort-wasm-simd-threaded.wasm +0 -0
- package/package.json +14 -17
- package/plugins/CLAUDE.md +61 -0
- package/plugins/session-manager-dev/.claude-plugin/plugin.json +1 -1
- package/plugins/session-manager-dev/skills/builder/4-manual/SKILL.md +1 -1
- package/plugins/session-manager-dev/skills/ops-sweep/SKILL.md +1 -1
- package/scripts/README.md +59 -0
- package/scripts/audit-ops-hygiene.cjs +350 -0
- package/scripts/hooks/guard-destructive-git.cjs +10 -34
- package/scripts/hooks/guard-inline-implementation.cjs +14 -8
- package/scripts/hooks/guard-prd-writes.cjs +7 -36
- package/scripts/hooks/guard-self-schedule.cjs +175 -0
- package/scripts/ops-sweep.cjs +355 -0
- package/scripts/scheduler-mcp-server.cjs +35 -71
- package/src/main/__tests__/agentModelResolve.test.cjs +100 -9
- package/src/main/__tests__/bilkoHost-integration.test.cjs +3 -3
- package/src/main/__tests__/broadcastCoalescer.test.cjs +18 -0
- package/src/main/__tests__/chat-cancel-terminal.test.cjs +6 -9
- package/src/main/__tests__/chat-exit-close-race.test.cjs +3 -3
- package/src/main/__tests__/chat-mcp-consent-notice.test.cjs +4 -5
- package/src/main/__tests__/chat-queue.test.cjs +2 -2
- package/src/main/__tests__/chat-stop-signal.test.cjs +2 -2
- package/src/main/__tests__/dep-orphan-archive-health.test.cjs +77 -0
- package/src/main/__tests__/dod-batchkey.test.cjs +2 -2
- package/src/main/__tests__/dod-drain-hook.test.cjs +2 -2
- package/src/main/__tests__/dod-report.test.cjs +2 -2
- package/src/main/__tests__/dod-reverify.test.cjs +2 -2
- package/src/main/__tests__/epicMint.test.cjs +4 -4
- package/src/main/__tests__/exchanges.test.cjs +2 -2
- package/src/main/__tests__/extractJson.test.cjs +2 -2
- package/src/main/__tests__/files-reject-credentials.test.cjs +1 -1
- package/src/main/__tests__/fixtures/1218-fo-01-move-scripts-lib-into-src-main-lib.log +556 -0
- package/src/main/__tests__/health-build-freshness.test.cjs +39 -0
- package/src/main/__tests__/health-delegation-chain.test.cjs +17 -2
- package/src/main/__tests__/health-queue-dispatch.test.cjs +58 -7
- package/src/main/__tests__/health-tick-liveness.test.cjs +11 -3
- package/src/main/__tests__/health-usage-poller.test.cjs +70 -23
- package/src/main/__tests__/historyRollup.test.cjs +2 -2
- package/src/main/__tests__/kg-augment.test.cjs +2 -2
- package/src/main/__tests__/mcpStatus.test.cjs +2 -2
- package/src/main/__tests__/memoryAggregate.test.cjs +1 -1
- package/src/main/__tests__/memoryStale.test.cjs +1 -1
- package/src/main/__tests__/needsReviewLedger.test.cjs +162 -0
- package/src/main/__tests__/opsErrorLogTelemetryTap.test.cjs +28 -4
- package/src/main/__tests__/pollLoop-dispatch-on-failure.test.cjs +48 -4
- package/src/main/__tests__/prd-group-allocator.test.cjs +2 -2
- package/src/main/__tests__/prdAdminRouteParity.test.cjs +2 -0
- package/src/main/__tests__/prdAdminRoutes.test.cjs +14 -2
- package/src/main/__tests__/prdAuthoringSeed.test.cjs +39 -0
- package/src/main/__tests__/prdCreateDisposition.test.cjs +201 -0
- package/src/main/__tests__/prdFrontmatterDisposition.test.cjs +125 -0
- package/src/main/__tests__/prdLocations.test.cjs +100 -2
- package/src/main/__tests__/prdLocationsArchived.test.cjs +50 -1
- package/src/main/__tests__/prdSetDisposition.test.cjs +222 -0
- package/src/main/__tests__/proc-role-env.test.cjs +125 -0
- package/src/main/__tests__/procname-claude-spawn-sites.test.cjs +304 -0
- package/src/main/__tests__/procname-sm-processes.test.cjs +127 -0
- package/src/main/__tests__/projectHomeAdminRoutes.test.cjs +81 -401
- package/src/main/__tests__/projectPages.test.cjs +63 -149
- package/src/main/__tests__/queue-health-verdict.test.cjs +170 -0
- package/src/main/__tests__/queue-starvation-dispatch-driver.test.cjs +117 -20
- package/src/main/__tests__/queue-starvation-per-project.test.cjs +14 -2
- package/src/main/__tests__/queueHistory.test.cjs +65 -2
- package/src/main/__tests__/rateLimitPollerStreak.test.cjs +38 -3
- package/src/main/__tests__/reconcileTiming.test.cjs +135 -0
- package/src/main/__tests__/runVerify-landed-commit-outranks.test.cjs +181 -0
- package/src/main/__tests__/runVerify.test.cjs +5 -5
- package/src/main/__tests__/scheduleJobStatusDrift.test.cjs +3 -3
- package/src/main/__tests__/scheduleJobTransitions.test.cjs +103 -3
- package/src/main/__tests__/scheduler-adopted-run-supervision.test.cjs +143 -0
- package/src/main/__tests__/scheduler-autofix-select.test.cjs +2 -2
- package/src/main/__tests__/scheduler-autopromote.test.cjs +2 -2
- package/src/main/__tests__/scheduler-bash-timeout-env.test.cjs +3 -5
- package/src/main/__tests__/scheduler-boot-orphans.test.cjs +79 -98
- package/src/main/__tests__/scheduler-broadcast-reconcile.test.cjs +121 -0
- package/src/main/__tests__/scheduler-cross-project-batch.test.cjs +43 -0
- package/src/main/__tests__/scheduler-default-eligible-heal.test.cjs +161 -0
- package/src/main/__tests__/scheduler-dispatch-loop.test.cjs +58 -0
- package/src/main/__tests__/scheduler-epic-digest.test.cjs +3 -5
- package/src/main/__tests__/scheduler-force-tick-outcome.test.cjs +2 -2
- package/src/main/__tests__/scheduler-gate-shadow.test.cjs +119 -0
- package/src/main/__tests__/scheduler-guard-verdict-autoresolve.test.cjs +390 -0
- package/src/main/__tests__/scheduler-heartbeat-payload.test.cjs +80 -0
- package/src/main/__tests__/scheduler-inplace-salvage.test.cjs +25 -18
- package/src/main/__tests__/scheduler-investigation-prompt.test.cjs +4 -4
- package/src/main/__tests__/scheduler-job-budget.test.cjs +172 -0
- package/src/main/__tests__/scheduler-launch-failure.test.cjs +3 -5
- package/src/main/__tests__/scheduler-looks-done.test.cjs +119 -10
- package/src/main/__tests__/scheduler-manual-pause.test.cjs +118 -0
- package/src/main/__tests__/scheduler-meta-code-sha.test.cjs +26 -3
- package/src/main/__tests__/scheduler-porcelain-rename.test.cjs +164 -0
- package/src/main/__tests__/scheduler-prd-missing-skip.test.cjs +17 -2
- package/src/main/__tests__/scheduler-prd-persona-spawn.test.cjs +3 -5
- package/src/main/__tests__/scheduler-quiet-machine-lease.test.cjs +41 -6
- package/src/main/__tests__/scheduler-rate-limit-pause.test.cjs +62 -6
- package/src/main/__tests__/scheduler-rate-limit-spin-guard.test.cjs +3 -5
- package/src/main/__tests__/scheduler-reap-dead-running-jobs.test.cjs +182 -3
- package/src/main/__tests__/scheduler-reaper-helpers-basics.test.cjs +87 -0
- package/src/main/__tests__/scheduler-reconcile-cwd-preserve.test.cjs +100 -0
- package/src/main/__tests__/scheduler-reconcile-invalid-repair.test.cjs +3 -3
- package/src/main/__tests__/scheduler-reconcile-quarantine.test.cjs +4 -4
- package/src/main/__tests__/scheduler-shard-quarantine.test.cjs +110 -0
- package/src/main/__tests__/scheduler-shared-tree-guard.test.cjs +168 -0
- package/src/main/__tests__/scheduler-starve-escalation.test.cjs +18 -8
- package/src/main/__tests__/scheduler-stranded-autofix-park.test.cjs +245 -0
- package/src/main/__tests__/scheduler-supervisor-record.test.cjs +81 -0
- package/src/main/__tests__/scheduler-tick-cancel-token.test.cjs +2 -2
- package/src/main/__tests__/scheduler-tick-wedge.test.cjs +172 -0
- package/src/main/__tests__/scheduler-transient-failure.test.cjs +2 -2
- package/src/main/__tests__/scheduler-worktree-cap-defer.test.cjs +51 -11
- package/src/main/__tests__/scheduler-worktree-exec-cwd.test.cjs +3 -5
- package/src/main/__tests__/usageSingleFlight.test.cjs +157 -0
- package/src/main/__tests__/workTypeLibrary.test.cjs +1 -1
- package/src/main/bilkoHost.cjs +16 -7
- package/src/main/build-info.json +8 -0
- package/src/main/chatRunner.cjs +12 -7
- package/src/main/config.cjs +2 -3
- package/src/main/docEdit.cjs +4 -2
- package/src/main/health.cjs +268 -52
- package/src/main/heapSnapshot.cjs +2 -2
- package/src/main/historyAggregator.cjs +6 -1
- package/src/main/index.cjs +152 -50
- package/src/main/ipcSchemas.cjs +53 -17
- package/src/main/lib/__tests__/active-sessions.test.cjs +251 -0
- package/src/main/lib/__tests__/auditLog.test.cjs +38 -0
- package/src/main/lib/__tests__/bootSelfHeal.test.cjs +107 -0
- package/src/main/lib/__tests__/buildIdentity.test.cjs +121 -0
- package/src/main/lib/__tests__/cwdClassify.test.cjs +111 -0
- package/src/main/lib/__tests__/definitionOfDoneSequence.test.cjs +95 -0
- package/src/main/lib/__tests__/delegationReadiness.test.cjs +323 -44
- package/src/main/lib/__tests__/dispatchLoop.test.cjs +63 -0
- package/src/main/lib/__tests__/effectiveModelInfo.test.cjs +239 -0
- package/src/main/lib/__tests__/gateFixtures.json +20 -0
- package/src/main/lib/__tests__/gitWorktree.test.cjs +97 -1
- package/src/main/lib/__tests__/guardShims.test.cjs +151 -0
- package/src/main/lib/__tests__/instanceLock.test.cjs +93 -7
- package/src/main/lib/__tests__/jobSupervisorRecord.test.cjs +78 -0
- package/src/main/lib/__tests__/jobWorktreeBootLive.test.cjs +11 -0
- package/src/main/lib/__tests__/localAdminHttp.test.cjs +1 -1
- package/src/main/lib/__tests__/mcpToolCatalog.test.cjs +6 -1
- package/src/main/lib/__tests__/opsRootAbsoluteCwd.test.cjs +5 -5
- package/src/main/lib/__tests__/prdDisposition.test.cjs +224 -0
- package/src/main/lib/__tests__/procIdentity.test.cjs +119 -0
- package/src/main/lib/__tests__/procName.test.cjs +92 -0
- package/src/main/lib/__tests__/queueStoreMachineStateRecovery.test.cjs +67 -0
- package/src/main/lib/__tests__/reaperHelpers.test.cjs +179 -1
- package/src/main/lib/__tests__/schedulerMcpServerHelp.test.cjs +6 -7
- package/src/main/lib/__tests__/schedulerMcpServerProjectHome.test.cjs +37 -204
- package/src/main/lib/__tests__/schedulerPaths.test.cjs +226 -0
- package/src/main/lib/__tests__/schedulerPathsWorktree.test.cjs +94 -0
- package/src/main/lib/__tests__/schedulerRuntimeState.test.cjs +56 -0
- package/src/main/lib/__tests__/sessionSlots.test.cjs +45 -0
- package/src/main/lib/__tests__/telemetryBacklog.test.cjs +1 -1
- package/src/main/lib/__tests__/upgradeDrain.test.cjs +130 -0
- package/src/main/lib/__tests__/usageCircuit.test.cjs +285 -0
- package/src/main/lib/__tests__/watchdog-helpers.test.cjs +375 -0
- package/src/main/lib/__tests__/watchdog-relaunch.test.cjs +266 -0
- package/src/main/lib/activeIndexRebuild.cjs +1 -3
- package/{scripts → src/main}/lib/activeSessions.cjs +67 -89
- package/src/main/lib/adoptedRunSupervisor.cjs +136 -0
- package/src/main/lib/agentModelResolve.cjs +94 -24
- package/src/main/lib/agentPersonaSchema.cjs +2 -2
- package/src/main/lib/auditLog.cjs +30 -5
- package/src/main/lib/bootSelfHeal.cjs +88 -0
- package/src/main/lib/buildIdentity.cjs +113 -0
- package/src/main/lib/classifyPromptTicket.cjs +3 -2
- package/src/main/lib/claudeBin.cjs +37 -2
- package/src/main/lib/cleanEnv.cjs +27 -1
- package/src/main/lib/credentials.cjs +4 -2
- package/src/main/lib/cwdClassify.cjs +185 -0
- package/src/main/lib/definitionOfDone.cjs +296 -52
- package/src/main/lib/delegationReadiness.cjs +290 -225
- package/src/main/lib/dispatchLoop.cjs +40 -0
- package/src/main/lib/effectiveModelInfo.cjs +332 -0
- package/src/main/lib/ephemeralCwd.cjs +7 -28
- package/src/main/lib/epicMint.cjs +3 -3
- package/src/main/lib/gitWorktree.cjs +136 -19
- package/src/main/lib/guardShims.cjs +156 -0
- package/src/main/lib/historyRollup.cjs +6 -7
- package/src/main/lib/instanceLock.cjs +32 -5
- package/src/main/lib/jobDirtFilter.cjs +7 -2
- package/src/main/lib/jobSupervisorRecord.cjs +147 -0
- package/src/main/lib/jobWorktreeBootLive.cjs +9 -5
- package/src/main/lib/launchFailure.cjs +2 -1
- package/src/main/lib/localAdminHttp.cjs +9 -27
- package/src/main/lib/mcpToolCatalog.cjs +33 -78
- package/src/main/lib/needsReviewLedger.cjs +205 -0
- package/src/main/lib/opsErrorLog.cjs +1 -1
- package/src/main/lib/opsOwnership.cjs +27 -19
- package/src/main/lib/prdAuthoringSeed.cjs +36 -0
- package/src/main/lib/prdCreate.cjs +56 -1
- package/src/main/lib/prdDisposition.cjs +199 -0
- package/src/main/lib/prdFrontmatter.cjs +8 -2
- package/src/main/lib/prdLocations.cjs +214 -45
- package/src/main/lib/procIdentity.cjs +126 -0
- package/src/main/lib/procName.cjs +98 -0
- package/src/main/lib/projectHomeAdminRoutes.cjs +56 -327
- package/src/main/lib/projectRootResolve.cjs +1 -1
- package/src/main/lib/queueHistory.cjs +27 -8
- package/src/main/lib/queueStore.cjs +63 -38
- package/src/main/lib/quietMachineLease.cjs +19 -2
- package/src/main/lib/reaperHelpers.cjs +181 -15
- package/src/main/lib/reservationExpiry.cjs +30 -0
- package/src/main/lib/runClaudeP.cjs +4 -2
- package/src/main/lib/runLogRetention.cjs +3 -2
- package/src/main/lib/scheduleJobSchema.cjs +10 -2
- package/src/main/lib/scheduleJobTransitions.cjs +60 -0
- package/src/main/lib/schedulerBatch.cjs +27 -2
- package/src/main/lib/schedulerConfig.cjs +24 -0
- package/src/main/lib/schedulerPaths.cjs +175 -0
- package/src/main/lib/schedulerRuntimeState.cjs +59 -0
- package/src/main/lib/sessionSlots.cjs +47 -9
- package/src/main/lib/smProcNames.cjs +51 -0
- package/src/main/lib/upgradeDrain.cjs +188 -0
- package/src/main/lib/usageCircuit.cjs +206 -0
- package/{scripts → src/main}/lib/watchdogHelpers.cjs +71 -38
- package/src/main/lib/withTimeout.cjs +33 -0
- package/src/main/mcpStatus.cjs +4 -2
- package/src/main/pluginInstall.cjs +6 -2
- package/src/main/projectPages.cjs +41 -145
- package/src/main/pty.cjs +8 -1
- package/src/main/queueOps.cjs +10 -10
- package/src/main/runVerify.cjs +69 -3
- package/src/main/scheduler/prdParser.cjs +13 -0
- package/src/main/scheduler.cjs +2841 -482
- package/src/main/seedAgentPersonas.cjs +1 -1
- package/src/main/seedDevPlugin.cjs +1 -1
- package/src/main/seedSchedulerMcp.cjs +7 -6
- package/src/main/seedStatus.cjs +1 -1
- package/src/main/supervisor.cjs +5 -3
- package/src/main/templates/PRD_AUTHORING.md +50 -0
- package/src/main/usage.cjs +140 -58
- package/src/preload/api.d.ts +110 -65
- package/src/preload/index.cjs +12 -4
- package/src/seed/agents/project-home-builder.md +31 -48
- package/dist/assets/AgentLibrary-DTFL7y8G.js +0 -3
- package/dist/assets/MarkdownEditor-DCIubYWf.js +0 -1
- package/dist/assets/Permissions-BiYZNGYW.js +0 -3
- package/dist/assets/Scheduler-DcLBiJBq.js +0 -14
- package/dist/assets/Settings-Cv-pRyms.js +0 -3
- package/dist/assets/settingsSchema-BJVciriw.js +0 -3
- package/screenshots/.gitkeep +0 -0
- package/screenshots/README-screenshots.md +0 -13
- package/scripts/project-pages-logic/dist/logic.cjs +0 -4709
- package/scripts/render-project-pages/dist/renderer.cjs +0 -18900
- package/scripts/render-project-pages.cjs +0 -70
- package/scripts/validate-project-pages-summary.cjs +0 -62
- package/src/main/lib/projectPageSummarySchema.cjs +0 -181
- package/src/main/teams.cjs +0 -95
- package/src/main/templates/project-pages-catalog.json +0 -741
- package/src/main/templates/project-pages-default-home.html +0 -123
- package/src/main/templates/project-pages-pipeline.md +0 -417
- package/src/seed/prompts/code-review/ac-coverage-check.md +0 -8
- package/src/seed/prompts/code-review/correctness-only.md +0 -8
- package/src/seed/prompts/code-review/full-spectrum-high.md +0 -8
- package/src/seed/prompts/code-review/hallucination-check.md +0 -8
- package/src/seed/prompts/code-review/public-api-compat.md +0 -8
- package/src/seed/prompts/code-review/readability-naming.md +0 -8
- package/src/seed/prompts/debugging/bug-as-failing-test.md +0 -8
- package/src/seed/prompts/debugging/git-bisect-regression.md +0 -8
- package/src/seed/prompts/debugging/instrument-intermittent-bug.md +0 -8
- package/src/seed/prompts/debugging/localize-pipeline-failure.md +0 -8
- package/src/seed/prompts/debugging/reproduce-then-diagnose.md +0 -8
- package/src/seed/prompts/documentation/adr-from-change.md +0 -8
- package/src/seed/prompts/documentation/module-readme.md +0 -8
- package/src/seed/prompts/documentation/onboarding-plan.md +0 -8
- package/src/seed/prompts/documentation/refresh-claude-md.md +0 -8
- package/src/seed/prompts/documentation/tsdoc-public-exports.md +0 -8
- package/src/seed/prompts/git-pr/conventional-commit.md +0 -8
- package/src/seed/prompts/git-pr/draft-pr-title-body.md +0 -8
- package/src/seed/prompts/git-pr/pre-commit-safety-sweep.md +0 -8
- package/src/seed/prompts/git-pr/release-notes-block.md +0 -8
- package/src/seed/prompts/git-pr/split-large-pr.md +0 -8
- package/src/seed/prompts/performance/bundle-startup-audit.md +0 -8
- package/src/seed/prompts/performance/complexity-audit.md +0 -8
- package/src/seed/prompts/performance/cpu-profile-hot-path.md +0 -8
- package/src/seed/prompts/performance/db-query-plan-review.md +0 -8
- package/src/seed/prompts/performance/memory-leak-hunt.md +0 -8
- package/src/seed/prompts/qa/api-contract-tests.md +0 -8
- package/src/seed/prompts/qa/e2e-critical-path.md +0 -8
- package/src/seed/prompts/qa/failing-test-for-bug.md +0 -8
- package/src/seed/prompts/qa/find-missing-test-coverage.md +0 -8
- package/src/seed/prompts/qa/stabilize-flaky-test.md +0 -8
- package/src/seed/prompts/qa/tdd-red-first.md +0 -8
- package/src/seed/prompts/qa/visual-regression-review.md +0 -8
- package/src/seed/prompts/qa/wcag-axe-scan.md +0 -8
- package/src/seed/prompts/refactoring/dead-code-sweep.md +0 -8
- package/src/seed/prompts/refactoring/extract-duplicated-pattern.md +0 -8
- package/src/seed/prompts/refactoring/modernize-legacy-file.md +0 -8
- package/src/seed/prompts/refactoring/reduce-cyclomatic-complexity.md +0 -8
- package/src/seed/prompts/refactoring/tighten-module-boundaries.md +0 -8
- package/src/seed/prompts/security/authz-audit.md +0 -8
- package/src/seed/prompts/security/crypto-correctness.md +0 -8
- package/src/seed/prompts/security/cwe-top-25-hunt.md +0 -8
- package/src/seed/prompts/security/dependency-audit.md +0 -8
- package/src/seed/prompts/security/ipc-boundary-hardening.md +0 -8
- package/src/seed/prompts/security/owasp-top-10-staged-diff.md +0 -8
- package/src/seed/prompts/security/secret-credential-scan.md +0 -8
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* jobSupervisorRecord.cjs — the one authoritative, SYNCHRONOUS record of a
|
|
5
|
+
* dispatch, written at the same point scheduler.cjs logs `[scheduler] spawned
|
|
6
|
+
* pid=`.
|
|
7
|
+
*
|
|
8
|
+
* Before this, the only durable trace of a dispatch was runtime={pid,...},
|
|
9
|
+
* written asynchronously by a mutate() inside the onPid callback, and the run
|
|
10
|
+
* dir held only the log until an exit path wrote meta.json. A booting process
|
|
11
|
+
* therefore had nothing to tell "my run, still alive" from "dead" and fell
|
|
12
|
+
* back to a ladder of inferences. `<runDir>/<slug>.supervisor.json` is that
|
|
13
|
+
* missing fact.
|
|
14
|
+
*
|
|
15
|
+
* Plain Node, no electron. The run dir is machine-wide scheduler state (not
|
|
16
|
+
* an ops namespace), so queueStore's writeJsonAtomicSync is the right writer.
|
|
17
|
+
* This module never signals a process and adds no cap accounting.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const fs = require('node:fs');
|
|
21
|
+
const path = require('node:path');
|
|
22
|
+
const { writeJsonAtomicSync } = require('./queueStore.cjs');
|
|
23
|
+
const { isDifferentProcess } = require('./procIdentity.cjs');
|
|
24
|
+
|
|
25
|
+
const SUFFIX = '.supervisor.json';
|
|
26
|
+
const META_SUFFIX = '.meta.json';
|
|
27
|
+
const INVESTIGATION_EXIT_RE = /\[scheduler\] investigation exit code=/;
|
|
28
|
+
const EXIT_TAIL_BYTES = 8 * 1024;
|
|
29
|
+
|
|
30
|
+
function recordPath(runDir, slug) {
|
|
31
|
+
return path.join(runDir, `${slug}${SUFFIX}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Writes `<runDir>/<slug>.supervisor.json` atomically. Throws on failure —
|
|
36
|
+
* the caller owns the "never fail the dispatch" try/catch. `kind` defaults to
|
|
37
|
+
* "job"; an investigation probe passes kind: "investigation" (and a slug of
|
|
38
|
+
* `<failedSlug>.investigation`, so it never overwrites the failed job's own
|
|
39
|
+
* record in the shared run dir).
|
|
40
|
+
*/
|
|
41
|
+
function writeSupervisorRecord({
|
|
42
|
+
runDir, slug, cwd, runId, pid, pgid, identity, execCwd, worktreeDir, worktreeBranch,
|
|
43
|
+
sessionId, startedAt, budgetMs, maxDurationMs, idleKillMs, schedulerPid, codeSha, kind,
|
|
44
|
+
}) {
|
|
45
|
+
if (!runDir || !slug) throw new Error('writeSupervisorRecord: runDir and slug are required');
|
|
46
|
+
const record = {
|
|
47
|
+
kind: kind || 'job',
|
|
48
|
+
slug, cwd: cwd ?? null, runId: runId ?? null,
|
|
49
|
+
pid: pid ?? null, pgid: pgid ?? null, identity: identity ?? null,
|
|
50
|
+
execCwd: execCwd ?? null, worktreeDir: worktreeDir ?? null, worktreeBranch: worktreeBranch ?? null,
|
|
51
|
+
sessionId: sessionId ?? null, startedAt: startedAt ?? null,
|
|
52
|
+
budgetMs: budgetMs ?? null, maxDurationMs: maxDurationMs ?? null, idleKillMs: idleKillMs ?? null,
|
|
53
|
+
schedulerPid: schedulerPid ?? null, codeSha: codeSha ?? null,
|
|
54
|
+
};
|
|
55
|
+
writeJsonAtomicSync(recordPath(runDir, slug), record);
|
|
56
|
+
return record;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Returns the parsed record, or null when absent/unparseable. Never throws. */
|
|
60
|
+
function readSupervisorRecord(runDir, slug) {
|
|
61
|
+
try {
|
|
62
|
+
const rec = JSON.parse(fs.readFileSync(recordPath(runDir, slug), 'utf8'));
|
|
63
|
+
return rec && typeof rec === 'object' ? rec : null;
|
|
64
|
+
} catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* True when the run has a durable exit marker: `<slug>.meta.json` for a job,
|
|
71
|
+
* the `investigation exit code=` log line for a probe. This is what separates
|
|
72
|
+
* "exited" from "dead" — a dead pid with no marker never got to write one.
|
|
73
|
+
*/
|
|
74
|
+
function hasExitMarker(runDir, record) {
|
|
75
|
+
try {
|
|
76
|
+
if (record.kind === 'investigation') {
|
|
77
|
+
const failedSlug = String(record.slug).replace(/\.investigation$/, '');
|
|
78
|
+
const logPath = path.join(runDir, `${failedSlug}.investigation.log`);
|
|
79
|
+
const { size } = fs.statSync(logPath);
|
|
80
|
+
const fd = fs.openSync(logPath, 'r');
|
|
81
|
+
try {
|
|
82
|
+
const len = Math.min(size, EXIT_TAIL_BYTES);
|
|
83
|
+
const buf = Buffer.alloc(len);
|
|
84
|
+
fs.readSync(fd, buf, 0, len, size - len);
|
|
85
|
+
return INVESTIGATION_EXIT_RE.test(buf.toString('utf8'));
|
|
86
|
+
} finally {
|
|
87
|
+
fs.closeSync(fd);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return fs.existsSync(path.join(runDir, `${record.slug}${META_SUFFIX}`));
|
|
91
|
+
} catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Every record under `runsDir/<runId>/` whose file was written within
|
|
98
|
+
* maxAgeMs and which has no exit marker yet. Each entry is the record plus
|
|
99
|
+
* `runDir`. Never throws; an unreadable dir yields [].
|
|
100
|
+
* Complexity: O(run dirs + files in recent dirs).
|
|
101
|
+
*/
|
|
102
|
+
function listLiveSupervisorRecords(runsDir, { maxAgeMs, now = Date.now() } = {}) {
|
|
103
|
+
const out = [];
|
|
104
|
+
let dirs;
|
|
105
|
+
try { dirs = fs.readdirSync(runsDir, { withFileTypes: true }); } catch { return out; }
|
|
106
|
+
for (const d of dirs) {
|
|
107
|
+
if (!d.isDirectory()) continue;
|
|
108
|
+
const runDir = path.join(runsDir, d.name);
|
|
109
|
+
let files;
|
|
110
|
+
try { files = fs.readdirSync(runDir); } catch { continue; }
|
|
111
|
+
for (const f of files) {
|
|
112
|
+
if (!f.endsWith(SUFFIX)) continue;
|
|
113
|
+
try {
|
|
114
|
+
if (Number.isFinite(maxAgeMs) && now - fs.statSync(path.join(runDir, f)).mtimeMs > maxAgeMs) continue;
|
|
115
|
+
const record = readSupervisorRecord(runDir, f.slice(0, -SUFFIX.length));
|
|
116
|
+
if (!record || hasExitMarker(runDir, record)) continue;
|
|
117
|
+
out.push({ ...record, runDir });
|
|
118
|
+
} catch { /* raced with a delete — skip */ }
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Pure. What should a booting process do with this record?
|
|
126
|
+
* - not alive + exit marker → 'exited' (finished; outcome is on disk)
|
|
127
|
+
* - not alive, no marker → 'dead' (died without writing an outcome)
|
|
128
|
+
* - alive, different process → 'foreign-pid' (pid recycled; never ours)
|
|
129
|
+
* - alive, ours, past budget or log stalled past idleKillMs → 'over-budget'
|
|
130
|
+
* - alive, ours → 'adopt'
|
|
131
|
+
* `identity` is the live probe of record.pid; isDifferentProcess is
|
|
132
|
+
* fail-closed, so an incomplete identity on either side reads as "same".
|
|
133
|
+
*/
|
|
134
|
+
function classifyAdoption(record, { identity, pidAlive, logMtimeMs, exitMarker, now = Date.now() } = {}) {
|
|
135
|
+
if (!pidAlive) return exitMarker ? 'exited' : 'dead';
|
|
136
|
+
if (exitMarker) return 'exited';
|
|
137
|
+
if (isDifferentProcess(record.identity, identity)) return 'foreign-pid';
|
|
138
|
+
const ceiling = record.budgetMs > 0 ? record.budgetMs : (record.maxDurationMs > 0 ? record.maxDurationMs : 0);
|
|
139
|
+
if (ceiling > 0 && Number.isFinite(record.startedAt) && now - record.startedAt > ceiling) return 'over-budget';
|
|
140
|
+
if (record.idleKillMs > 0 && Number.isFinite(logMtimeMs) && now - logMtimeMs > record.idleKillMs) return 'over-budget';
|
|
141
|
+
return 'adopt';
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
module.exports = {
|
|
145
|
+
writeSupervisorRecord, readSupervisorRecord, listLiveSupervisorRecords, classifyAdoption, hasExitMarker,
|
|
146
|
+
SUFFIX,
|
|
147
|
+
};
|
|
@@ -22,15 +22,19 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
25
|
+
* `rowPid(job)` resolves a running row's pid; scheduler.cjs passes the same
|
|
26
|
+
* supervisor-record → runtime.pid → run-log ladder partitionBootOrphans uses,
|
|
27
|
+
* so the two boot passes agree. Default: runtime.pid alone.
|
|
28
|
+
*
|
|
29
|
+
* @param {{ bootJobs: Array<object>, claudePidAlive: (pid: number) => boolean, hasLiveHolder: (dir: string, holders?: Set<string>) => boolean, cwdHolders?: Set<string>, rowPid?: (job: object) => number|null }} deps
|
|
26
30
|
* @returns {(slug: string, entry: { worktree: string, branch: string|null }) => boolean}
|
|
27
31
|
*/
|
|
28
|
-
function buildJobWorktreeIsLive({ bootJobs, claudePidAlive, hasLiveHolder, cwdHolders }) {
|
|
32
|
+
function buildJobWorktreeIsLive({ bootJobs, claudePidAlive, hasLiveHolder, cwdHolders, rowPid = (j) => j.runtime && j.runtime.pid }) {
|
|
29
33
|
const runningPidBySlug = new Map();
|
|
30
34
|
for (const j of Array.isArray(bootJobs) ? bootJobs : []) {
|
|
31
|
-
if (j
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
if (!j || j.status !== 'running') continue;
|
|
36
|
+
const pid = rowPid(j);
|
|
37
|
+
if (pid) runningPidBySlug.set(j.slug, pid);
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
return function isLive(slug, entry) {
|
|
@@ -309,9 +309,10 @@ function evaluateLaunchGate(block, { now, claudeVersion } = {}) {
|
|
|
309
309
|
* closed set so operators never have to open a transcript to tell a
|
|
310
310
|
* non-start from an implementation failure from a verifier downgrade.
|
|
311
311
|
*/
|
|
312
|
-
function deriveTerminalReason({ effectiveStatus, exitCode, verifyResult, sigtermOverride, worktreeIntegrationFailure }) {
|
|
312
|
+
function deriveTerminalReason({ effectiveStatus, exitCode, verifyResult, sigtermOverride, worktreeIntegrationFailure, budgetKill }) {
|
|
313
313
|
if (worktreeIntegrationFailure) return 'worktree_integration_failed';
|
|
314
314
|
if (effectiveStatus === 'completed') return 'completed';
|
|
315
|
+
if (budgetKill) return 'budget_exceeded';
|
|
315
316
|
if (sigtermOverride) return 'signal_kill_with_commit';
|
|
316
317
|
if (exitCode === 143 || exitCode === 137) return 'signal_kill';
|
|
317
318
|
if (typeof exitCode === 'number' && exitCode !== 0) return `impl_failed:exit_${exitCode}`;
|
|
@@ -32,40 +32,20 @@
|
|
|
32
32
|
'use strict';
|
|
33
33
|
|
|
34
34
|
const http = require('node:http');
|
|
35
|
-
const os = require('node:os');
|
|
36
35
|
const path = require('node:path');
|
|
37
36
|
const crypto = require('node:crypto');
|
|
38
37
|
const fsp = require('node:fs/promises');
|
|
39
38
|
const config = require('../config.cjs');
|
|
40
|
-
|
|
41
|
-
const TOKEN_PATH = path.join(os.homedir(), '.claude', 'session-manager', 'admin-api.json');
|
|
39
|
+
const schedulerPaths = require('./schedulerPaths.cjs');
|
|
42
40
|
|
|
43
41
|
/**
|
|
44
|
-
* Mode-aware token file path, resolved fresh on every call (
|
|
42
|
+
* Mode-aware token file path, resolved fresh on every call (never cached at
|
|
45
43
|
* module load) so a dev/e2e launch never collides with a production
|
|
46
|
-
* instance's admin-api.json.
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* harmless tie-break, since real launches only ever set one or the other.
|
|
44
|
+
* instance's admin-api.json. The SM_ADMIN_TOKEN_PATH / SM_DEV / SM_E2E ladder
|
|
45
|
+
* lives in schedulerPaths.adminTokenPath() (the SM_ADMIN_TOKEN_PATH override
|
|
46
|
+
* exists because unit tests once overwrote the live app's port+token).
|
|
50
47
|
*/
|
|
51
|
-
|
|
52
|
-
// Explicit override, highest priority. Exists because the unit tests call
|
|
53
|
-
// createAdminHttp().start() directly with neither SM_DEV nor SM_E2E set,
|
|
54
|
-
// which resolved to the PRODUCTION admin-api.json and overwrote the
|
|
55
|
-
// running app's port+token with a throwaway test server's — silently
|
|
56
|
-
// orphaning scheduler-mcp-server.cjs's access to the live app until the
|
|
57
|
-
// next app restart (confirmed live 2026-08-01: `npm run test:unit` made
|
|
58
|
-
// every scheduler_* MCP tool report "app is not running"). Tests point
|
|
59
|
-
// this at a tmp file; production never sets it.
|
|
60
|
-
if (process.env.SM_ADMIN_TOKEN_PATH) return process.env.SM_ADMIN_TOKEN_PATH;
|
|
61
|
-
if (process.env.SM_DEV === '1') {
|
|
62
|
-
return path.join(os.homedir(), '.claude', 'session-manager', 'admin-api.dev.json');
|
|
63
|
-
}
|
|
64
|
-
if (process.env.SM_E2E === '1') {
|
|
65
|
-
return path.join(os.homedir(), '.claude', 'session-manager', 'admin-api.e2e.json');
|
|
66
|
-
}
|
|
67
|
-
return TOKEN_PATH;
|
|
68
|
-
}
|
|
48
|
+
const resolveTokenPath = schedulerPaths.adminTokenPath;
|
|
69
49
|
|
|
70
50
|
// Reserved health-check path, handled directly (not via registerRoute) so
|
|
71
51
|
// start()'s post-boot self-check never depends on route-registration order
|
|
@@ -238,9 +218,11 @@ function createAdminHttp() {
|
|
|
238
218
|
|
|
239
219
|
module.exports = {
|
|
240
220
|
createAdminHttp,
|
|
241
|
-
TOKEN_PATH,
|
|
242
221
|
resolveTokenPath,
|
|
243
222
|
timingSafeEqualStrings,
|
|
244
223
|
readBody,
|
|
245
224
|
sendJson,
|
|
246
225
|
};
|
|
226
|
+
|
|
227
|
+
// Lazy getter (the token path is mode-aware and env-driven; never frozen at require).
|
|
228
|
+
Object.defineProperty(module.exports, 'TOKEN_PATH', { get: () => schedulerPaths.adminTokenPath(), enumerable: true });
|
|
@@ -47,6 +47,24 @@ const MCP_TOOL_CATALOG = [
|
|
|
47
47
|
exampleArgs: { slug: 'add-mcp-tool-catalog', force: false },
|
|
48
48
|
notes: 'Refuses a job whose status is already "completed" unless force:true is passed — resetting a completed job re-executes already-shipped work.',
|
|
49
49
|
},
|
|
50
|
+
{
|
|
51
|
+
name: 'scheduler_pause',
|
|
52
|
+
group: 'scheduler',
|
|
53
|
+
purpose: "Pause the scheduler (user-initiated) via the session-manager app's admin API.",
|
|
54
|
+
whenToUse: 'Use to stop NEW job dispatch on request (e.g. before a risky change). Running jobs are never killed.',
|
|
55
|
+
whenNotToUse: 'Do not use to cancel a running job (use scheduler_cancel_job) or as a workaround for a stuck queue.',
|
|
56
|
+
exampleArgs: {},
|
|
57
|
+
notes: 'A manual pause survives app restart and is never auto-cleared by the rate-limit timer or network recovery — only scheduler_resume clears it.',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: 'scheduler_resume',
|
|
61
|
+
group: 'scheduler',
|
|
62
|
+
purpose: "Clear any scheduler pause (manual or automatic) via the session-manager app's admin API.",
|
|
63
|
+
whenToUse: 'Use to restart dispatch after scheduler_pause, or to lift an auto-pause you have judged stale.',
|
|
64
|
+
whenNotToUse: 'Do not use to retry a parked needs_review job — that is scheduler_reset_job.',
|
|
65
|
+
exampleArgs: {},
|
|
66
|
+
notes: null,
|
|
67
|
+
},
|
|
50
68
|
{
|
|
51
69
|
name: 'scheduler_list_jobs',
|
|
52
70
|
group: 'scheduler',
|
|
@@ -75,7 +93,10 @@ const MCP_TOOL_CATALOG = [
|
|
|
75
93
|
+ '`tag` and `agentType` are two DIFFERENT, independent fields — `tag` is WHAT KIND '
|
|
76
94
|
+ 'of work this PRD is (feature/bug/build/...); `agentType` is WHO executes it (a '
|
|
77
95
|
+ 'persona name, e.g. "dev-lead"). `agentType` defaults to "dev-lead" when omitted, '
|
|
78
|
-
+ 'and is rejected at write time if it does not name a real persona file.'
|
|
96
|
+
+ 'and is rejected at write time if it does not name a real persona file. '
|
|
97
|
+
+ 'If this PRD has no dependsOn of its own AND the target Epic already has incomplete PRDs, '
|
|
98
|
+
+ '`disposition` ("append" or "new-head") is REQUIRED — the write is refused without it (a '
|
|
99
|
+
+ 'headless job caller instead gets a logged "append" default, since no human is present to ask).',
|
|
79
100
|
whenToUse: 'Use whenever new work should be queued into an already-approved Epic — this is the /develop path.',
|
|
80
101
|
whenNotToUse: 'TWO DISTINCT FAILURE MODES if this tool is not usable — do not conflate them: '
|
|
81
102
|
+ '(a) this tool call is PRESENT in your tool list but ERRORS as app-not-running / admin '
|
|
@@ -221,65 +242,18 @@ const MCP_TOOL_CATALOG = [
|
|
|
221
242
|
+ 'delivered.',
|
|
222
243
|
},
|
|
223
244
|
{
|
|
224
|
-
name: '
|
|
225
|
-
group: 'project-home',
|
|
226
|
-
purpose: "Fetch the self-sufficient contract for generating this project's Project Home pages via the "
|
|
227
|
-
+ "session-manager app's admin API — the protocol steps, the ProjectPageSummary/ProjectPagePicks JSON "
|
|
228
|
-
+ 'schemas, the component catalog (all 5 lenses: home, marketing, feature, architecture, brief), the '
|
|
229
|
-
+ 'absolute output paths, and the full pipeline spec text. Nothing in the response requires reading '
|
|
230
|
-
+ "session-manager's own repo — this is what makes generation work on a machine with only the npm "
|
|
231
|
-
+ 'package installed.',
|
|
232
|
-
whenToUse: 'Call this FIRST, before composing any ProjectPageSummary content or picking variants, whenever '
|
|
233
|
-
+ 'a project-home-builder session starts generating Project Home pages for a project.',
|
|
234
|
-
whenNotToUse: 'Do not begin composing a ProjectPageSummary or choosing variants before calling this — it '
|
|
235
|
-
+ 'must be the FIRST call a project-home-builder session makes; every other project_home_* tool assumes '
|
|
236
|
-
+ "you already have this response's schema/catalog/paths in hand.",
|
|
237
|
-
exampleArgs: { cwd: '/home/bilko/Projects/session-manager' },
|
|
238
|
-
notes: "cwd is optional — defaults to the calling session's own project root (SM_PROJECT_ROOT or process.cwd()) when omitted.",
|
|
239
|
-
},
|
|
240
|
-
{
|
|
241
|
-
name: 'project_home_validate_summary',
|
|
242
|
-
group: 'project-home',
|
|
243
|
-
purpose: 'Validate a composed ProjectPageSummary against the schema from project_home_get_contract via the '
|
|
244
|
-
+ "session-manager app's admin API, without writing anything to disk.",
|
|
245
|
-
whenToUse: 'Use after composing (or editing) a ProjectPageSummary and before calling project_home_render — '
|
|
246
|
-
+ 'fix every returned {field, message} error and re-validate until valid:true.',
|
|
247
|
-
whenNotToUse: 'Do not skip straight to project_home_render on an unvalidated summary — render does '
|
|
248
|
-
+ 're-validate server-side and rejects with no writes on failure, but catching errors here first saves a '
|
|
249
|
-
+ 'round trip and gives per-field detail sooner.',
|
|
250
|
-
exampleArgs: { cwd: '/home/bilko/Projects/session-manager', summary: { title: 'Example Project' } },
|
|
251
|
-
notes: "cwd is optional — defaults to the calling session's own project root (SM_PROJECT_ROOT or process.cwd()) when omitted.",
|
|
252
|
-
},
|
|
253
|
-
{
|
|
254
|
-
name: 'project_home_render',
|
|
255
|
-
group: 'project-home',
|
|
256
|
-
purpose: 'Render and write all 5 Project Home lenses (home, marketing, feature, architecture, brief) plus '
|
|
257
|
-
+ "summary.json/picks.json/manifest.json via the session-manager app's admin API — the only write path "
|
|
258
|
-
+ 'for Project Home generation.',
|
|
259
|
-
whenToUse: 'Use once project_home_validate_summary reports valid:true and a variant has been picked for '
|
|
260
|
-
+ "every lens/slot in the contract's catalog.",
|
|
261
|
-
whenNotToUse: 'Must NOT be used to fabricate summary content — every field of the summary passed here must '
|
|
262
|
-
+ 'trace to something concrete about the real project (an Epic goal, a source file/dir, a convention, a '
|
|
263
|
-
+ 'git log entry). This tool re-validates the summary server-side and rejects with no writes on schema '
|
|
264
|
-
+ 'failure, but it only checks shape, never truthfulness.',
|
|
265
|
-
exampleArgs: {
|
|
266
|
-
cwd: '/home/bilko/Projects/session-manager',
|
|
267
|
-
summary: { title: 'Example Project' },
|
|
268
|
-
picks: { home: { hero: 'variant-a' } },
|
|
269
|
-
},
|
|
270
|
-
notes: "cwd is optional — defaults to the calling session's own project root (SM_PROJECT_ROOT or process.cwd()) when omitted.",
|
|
271
|
-
},
|
|
272
|
-
{
|
|
273
|
-
name: 'project_home_status',
|
|
245
|
+
name: 'project_home_write',
|
|
274
246
|
group: 'project-home',
|
|
275
|
-
purpose: "
|
|
276
|
-
+ "
|
|
277
|
-
+ '
|
|
278
|
-
whenToUse: 'Use
|
|
279
|
-
+ '
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
247
|
+
purpose: "Write this project's Project Home page — ONE self-contained HTML document — to "
|
|
248
|
+
+ "session-manager-operations/project-pages/home.html via the session-manager app's admin API. "
|
|
249
|
+
+ 'The only write path for Project Home.',
|
|
250
|
+
whenToUse: 'Use when asked to generate or refresh the project home page. Compose the whole page yourself '
|
|
251
|
+
+ '(inline <style>/<script>, data: URIs for assets) from what is really true about the project, then '
|
|
252
|
+
+ 'pass the full document as `html`.',
|
|
253
|
+
whenNotToUse: 'Never fabricate content — every claim must trace to something concrete in the project. The '
|
|
254
|
+
+ 'document must be self-contained: the call is rejected for empty input, more than 1MB, any '
|
|
255
|
+
+ '<script src>, <link href="http…">, @import, or url(http…) — no network egress.',
|
|
256
|
+
exampleArgs: { cwd: '/home/bilko/Projects/session-manager', html: '<!DOCTYPE html><html><body><h1>Example</h1></body></html>' },
|
|
283
257
|
notes: "cwd is optional — defaults to the calling session's own project root (SM_PROJECT_ROOT or process.cwd()) when omitted.",
|
|
284
258
|
},
|
|
285
259
|
{
|
|
@@ -332,25 +306,6 @@ const MCP_RECIPES = [
|
|
|
332
306
|
'The call only delivers a PROPOSED session in the receiving project — nothing runs until a human there presses Approve & start; there is no reply channel.',
|
|
333
307
|
],
|
|
334
308
|
},
|
|
335
|
-
{
|
|
336
|
-
id: 'generate-project-home',
|
|
337
|
-
title: "Generate a project's Project Home pages",
|
|
338
|
-
steps: [
|
|
339
|
-
"Call project_home_get_contract to get the protocol, ProjectPageSummary/ProjectPagePicks schemas, "
|
|
340
|
-
+ 'component catalog, absolute output paths, and pipeline spec — the FIRST call any '
|
|
341
|
-
+ 'project-home-builder session makes.',
|
|
342
|
-
'Author a ProjectPageSummary matching the contract\'s summarySchema — every field must trace to '
|
|
343
|
-
+ 'something concrete about the real project; never fabricate content.',
|
|
344
|
-
'Call project_home_validate_summary with { cwd, summary } and fix every {field, message} error it '
|
|
345
|
-
+ 'returns, re-validating until valid:true.',
|
|
346
|
-
"For each lens and slot in the contract's catalog, choose exactly one variant id by judging the "
|
|
347
|
-
+ 'candidate variants against the summary you composed, assembling a ProjectPagePicks object '
|
|
348
|
-
+ '(lensId -> slotId -> variantId).',
|
|
349
|
-
'Call project_home_render with { cwd, summary, picks } to render all 5 lenses and write '
|
|
350
|
-
+ 'summary.json/picks.json/output files.',
|
|
351
|
-
"Call project_home_status to confirm the new files landed and read manifest.json's generatedAt.",
|
|
352
|
-
],
|
|
353
|
-
},
|
|
354
309
|
];
|
|
355
310
|
|
|
356
311
|
for (const entry of MCP_TOOL_CATALOG) {
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* needsReviewLedger.cjs — pure helpers that make `needs_review` episodes
|
|
5
|
+
* durable and queryable in history.jsonl.
|
|
6
|
+
*
|
|
7
|
+
* Before this module, `queueHistory.cjs` archived a job to history.jsonl
|
|
8
|
+
* only on a TERMINAL status (completed/failed/skipped) — a needs_review
|
|
9
|
+
* park never left queue.json's hot jobs[] (it isn't terminal; see
|
|
10
|
+
* queueHistory.cjs's partitionJobs), so it never reached durable history
|
|
11
|
+
* either. Profiling history.jsonl for "how much needs_review is really
|
|
12
|
+
* happening" therefore always returned zero, however loud the problem
|
|
13
|
+
* looked in the UI.
|
|
14
|
+
*
|
|
15
|
+
* Two line shapes are appended to the SAME history.jsonl a terminal row
|
|
16
|
+
* lands in, distinguished by `kind` so existing terminal-only readers can
|
|
17
|
+
* filter them out on sight (see scheduleJobTransitions.cjs, the actual
|
|
18
|
+
* write choke point, and its callers' audit in the owning PRD):
|
|
19
|
+
* - kind: 'needs_review_entry' — one per transition INTO needs_review
|
|
20
|
+
* - kind: 'needs_review_resolution' — one per transition OUT of needs_review,
|
|
21
|
+
* referencing the entry's own runId so the two can be paired
|
|
22
|
+
*
|
|
23
|
+
* Kept fs-free and pure (mirrors reaperHelpers.cjs's rationale) so the
|
|
24
|
+
* ladder-rung classification and rollup reduction are unit-testable without
|
|
25
|
+
* touching disk or importing scheduler.cjs (which requires electron).
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
// The 6 named ladder rungs a needs_review episode can resolve through
|
|
29
|
+
// (spawnJob's mechanical/resume/quarantine/auto-fix branches, the periodic
|
|
30
|
+
// reverifyNeedsReview heal pass, or an explicit human/API reset). Any
|
|
31
|
+
// resolution whose transitionJob `source` doesn't match a known automated
|
|
32
|
+
// rung is bucketed as 'manual-reset' — the conservative default for
|
|
33
|
+
// anything that isn't a recognized self-healing path.
|
|
34
|
+
const LADDER_RUNGS = [
|
|
35
|
+
'mechanical-recovery',
|
|
36
|
+
'resume-recovery',
|
|
37
|
+
'quarantine',
|
|
38
|
+
'auto-fix',
|
|
39
|
+
'reverify',
|
|
40
|
+
'manual-reset',
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* classifyLadderRung({ source, reason }) → one of LADDER_RUNGS.
|
|
45
|
+
*
|
|
46
|
+
* Matches the exact `source` strings scheduleJobTransitions.cjs's callers in
|
|
47
|
+
* scheduler.cjs pass for each recovery-ladder rung (see that module's
|
|
48
|
+
* LEGAL_TRANSITIONS doc comment for the full call-site inventory). Pattern
|
|
49
|
+
* matching, not an exhaustive enum lookup, because 'spawnJob:dispatch' is
|
|
50
|
+
* shared between the ordinary pending->running dispatch and the
|
|
51
|
+
* needs_review->running resume-recovery dispatch — the two are only
|
|
52
|
+
* distinguishable by `reason` text.
|
|
53
|
+
*/
|
|
54
|
+
function classifyLadderRung({ source, reason } = {}) {
|
|
55
|
+
const s = typeof source === 'string' ? source : '';
|
|
56
|
+
const r = typeof reason === 'string' ? reason : '';
|
|
57
|
+
|
|
58
|
+
if (s === 'scheduler:mechanicalRecovery') return 'mechanical-recovery';
|
|
59
|
+
if (s === 'spawnJob:dispatch' && /resume-recovery/i.test(r)) return 'resume-recovery';
|
|
60
|
+
if (s === 'spawnInvestigation:start' || s === 'spawnJob:auto-promote' || s === 'reverifyNeedsReview:auto-promote') {
|
|
61
|
+
return 'auto-fix';
|
|
62
|
+
}
|
|
63
|
+
if (s === 'reverifyNeedsReview:heal' || s === 'needsReviewAutoResolve') return 'reverify';
|
|
64
|
+
// No transitionJob call site resolves needs_review with a 'quarantine'
|
|
65
|
+
// source today (performLeftoverQuarantine cleans up leftover paths without
|
|
66
|
+
// itself changing job.status — the row stays needs_review until a later
|
|
67
|
+
// reverify/auto-resolve pass actually resolves it), but the rung is kept
|
|
68
|
+
// in LADDER_RUNGS/this classifier so a future direct-resolving quarantine
|
|
69
|
+
// path is classified correctly without another migration.
|
|
70
|
+
return 'manual-reset';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* buildNeedsReviewEntryLine(job, entry) → JSONL-ready object.
|
|
75
|
+
*
|
|
76
|
+
* `entry` is the statusHistory record scheduleJobTransitions.cjs just
|
|
77
|
+
* pushed for this transition ({ from, to, reason, source, at }). The park
|
|
78
|
+
* reason prefers the structured `verifierVerdict` (a short, bucketable
|
|
79
|
+
* code) over the free-text `heldReason`/`error` fields, since those are the
|
|
80
|
+
* only reason evidence available for parks that never went through the
|
|
81
|
+
* verifier (e.g. a pidless-reap needs_review park).
|
|
82
|
+
*/
|
|
83
|
+
function buildNeedsReviewEntryLine(job, entry) {
|
|
84
|
+
return {
|
|
85
|
+
kind: 'needs_review_entry',
|
|
86
|
+
slug: job?.slug ?? null,
|
|
87
|
+
cwd: job?.cwd ?? null,
|
|
88
|
+
epicId: job?.epicId ?? null,
|
|
89
|
+
runId: job?.runId ?? null,
|
|
90
|
+
at: entry?.at ?? new Date().toISOString(),
|
|
91
|
+
reason: job?.verifierVerdict ?? job?.heldReason ?? job?.error ?? null,
|
|
92
|
+
source: entry?.source ?? null,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* buildNeedsReviewResolutionLine(job, entry, episode) → JSONL-ready object.
|
|
98
|
+
*
|
|
99
|
+
* `episode` is `{ runId, enteredAt }` captured off the job at entry time
|
|
100
|
+
* (needsReviewEntryRunId/needsReviewEnteredAt) — the resolution line
|
|
101
|
+
* references the ENTRY's runId (which may differ from `job.runId` by
|
|
102
|
+
* resolution time, e.g. after a resume-recovery re-dispatch minted a fresh
|
|
103
|
+
* one), not whatever runId the job carries right now.
|
|
104
|
+
*/
|
|
105
|
+
function buildNeedsReviewResolutionLine(job, entry, episode) {
|
|
106
|
+
const enteredAtMs = episode?.enteredAt ? Date.parse(episode.enteredAt) : NaN;
|
|
107
|
+
const resolvedAtMs = entry?.at ? Date.parse(entry.at) : NaN;
|
|
108
|
+
const dwellMs = Number.isFinite(enteredAtMs) && Number.isFinite(resolvedAtMs)
|
|
109
|
+
? Math.max(0, resolvedAtMs - enteredAtMs)
|
|
110
|
+
: null;
|
|
111
|
+
return {
|
|
112
|
+
kind: 'needs_review_resolution',
|
|
113
|
+
slug: job?.slug ?? null,
|
|
114
|
+
cwd: job?.cwd ?? null,
|
|
115
|
+
epicId: job?.epicId ?? null,
|
|
116
|
+
runId: episode?.runId ?? job?.runId ?? null,
|
|
117
|
+
at: entry?.at ?? new Date().toISOString(),
|
|
118
|
+
resolvedTo: entry?.to ?? null,
|
|
119
|
+
ladderRung: classifyLadderRung({ source: entry?.source, reason: entry?.reason }),
|
|
120
|
+
source: entry?.source ?? null,
|
|
121
|
+
dwellMs,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function percentile(sortedValues, p) {
|
|
126
|
+
if (!sortedValues.length) return null;
|
|
127
|
+
const idx = Math.min(sortedValues.length - 1, Math.max(0, Math.ceil((p / 100) * sortedValues.length) - 1));
|
|
128
|
+
return sortedValues[idx];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* reduceNeedsReviewLedger(lines) → rollup.
|
|
133
|
+
*
|
|
134
|
+
* Pure O(n log n) reduction (the log n is the dwell-time sort for the
|
|
135
|
+
* percentiles) over a flat array of already-parsed history.jsonl rows
|
|
136
|
+
* (mixing needs_review_entry/needs_review_resolution/terminal rows is fine —
|
|
137
|
+
* anything that isn't one of the two kinds this module writes is ignored).
|
|
138
|
+
*
|
|
139
|
+
* Pairs an entry with its resolution by (slug, runId) — the same pairing
|
|
140
|
+
* key buildNeedsReviewResolutionLine's `episode.runId` guarantees matches
|
|
141
|
+
* the entry line's `runId`. A slug that parks twice (two different runIds)
|
|
142
|
+
* produces two independent pairs, matching two independent recovery
|
|
143
|
+
* episodes rather than being conflated into one.
|
|
144
|
+
*
|
|
145
|
+
* Returns:
|
|
146
|
+
* - byReason: { [reason]: entryCount } (reason = entry line's `reason`, or 'unknown')
|
|
147
|
+
* - byLadderRung: { [rung]: resolutionCount } (rung = one of LADDER_RUNGS)
|
|
148
|
+
* - unresolvedCount: entries with no matching resolution line yet
|
|
149
|
+
* - dwellMsP50 / dwellMsP90: percentiles over resolved episodes' dwellMs (null if none)
|
|
150
|
+
*/
|
|
151
|
+
function reduceNeedsReviewLedger(lines) {
|
|
152
|
+
const list = Array.isArray(lines) ? lines : [];
|
|
153
|
+
const entriesByKey = new Map();
|
|
154
|
+
const resolutionsByKey = new Map();
|
|
155
|
+
|
|
156
|
+
for (const line of list) {
|
|
157
|
+
if (!line || typeof line !== 'object') continue;
|
|
158
|
+
const key = `${line.slug ?? ''}|${line.runId ?? ''}`;
|
|
159
|
+
if (line.kind === 'needs_review_entry') {
|
|
160
|
+
entriesByKey.set(key, line);
|
|
161
|
+
} else if (line.kind === 'needs_review_resolution') {
|
|
162
|
+
resolutionsByKey.set(key, line);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const byReason = {};
|
|
167
|
+
let unresolvedCount = 0;
|
|
168
|
+
for (const [key, entry] of entriesByKey) {
|
|
169
|
+
const reason = entry.reason ?? 'unknown';
|
|
170
|
+
byReason[reason] = (byReason[reason] ?? 0) + 1;
|
|
171
|
+
if (!resolutionsByKey.has(key)) unresolvedCount += 1;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const byLadderRung = {};
|
|
175
|
+
const dwellMsValues = [];
|
|
176
|
+
for (const [key, resolution] of resolutionsByKey) {
|
|
177
|
+
const rung = LADDER_RUNGS.includes(resolution.ladderRung) ? resolution.ladderRung : 'manual-reset';
|
|
178
|
+
byLadderRung[rung] = (byLadderRung[rung] ?? 0) + 1;
|
|
179
|
+
// Only count dwell for episodes that actually matched an entry — a
|
|
180
|
+
// resolution line with no paired entry (e.g. history truncated by
|
|
181
|
+
// retention before the entry aged out — never happens for needs_review
|
|
182
|
+
// rows today since they're excluded from partitionJobs' archivable set,
|
|
183
|
+
// but this stays defensive) has no truthful dwell to report.
|
|
184
|
+
if (entriesByKey.has(key) && typeof resolution.dwellMs === 'number' && Number.isFinite(resolution.dwellMs)) {
|
|
185
|
+
dwellMsValues.push(resolution.dwellMs);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
dwellMsValues.sort((a, b) => a - b);
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
byReason,
|
|
192
|
+
byLadderRung,
|
|
193
|
+
unresolvedCount,
|
|
194
|
+
dwellMsP50: percentile(dwellMsValues, 50),
|
|
195
|
+
dwellMsP90: percentile(dwellMsValues, 90),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
module.exports = {
|
|
200
|
+
LADDER_RUNGS,
|
|
201
|
+
classifyLadderRung,
|
|
202
|
+
buildNeedsReviewEntryLine,
|
|
203
|
+
buildNeedsReviewResolutionLine,
|
|
204
|
+
reduceNeedsReviewLedger,
|
|
205
|
+
};
|
|
@@ -107,7 +107,7 @@ function writeLocalLine({ cwd, scope, level, tabId, epicId, tags, message, meta
|
|
|
107
107
|
function reportToTelemetry({ cwd, scope, level, tabId, epicId, tags, message }) {
|
|
108
108
|
try {
|
|
109
109
|
const telemetryClient = require('./telemetryClient.cjs');
|
|
110
|
-
const { projectRootOf } = require('
|
|
110
|
+
const { projectRootOf } = require('./activeSessions.cjs');
|
|
111
111
|
const normalizedCwd = projectRootOf(cwd) || cwd;
|
|
112
112
|
const autoTags = [
|
|
113
113
|
`scope:${scope || 'unknown'}`,
|