claude-code-session-manager 0.98.0 → 0.100.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 +3 -3
- package/bin/cli.cjs +16 -4
- package/bin/node-floor.cjs +41 -0
- package/dist/assets/{DataModel-DCEgMTjH.js → DataModel-HdR0AxFq.js} +1 -1
- package/dist/assets/{History-Bfdoywlh.js → History-Dr4Jr_5z.js} +2 -2
- package/dist/assets/{Hooks-CKDJjTJV.js → Hooks-NhrvH3JP.js} +3 -3
- package/dist/assets/{Library-7akfgy67.js → Library-Dv_EtIMr.js} +1 -1
- package/dist/assets/{MarkdownEditor-DDesiqet.js → MarkdownEditor-DTKDWV_n.js} +1 -1
- package/dist/assets/{McpServers-D4lZtXof.js → McpServers-M034G9B3.js} +2 -2
- package/dist/assets/{Memory-CeI8G1w5.js → Memory-DzF1B4gx.js} +6 -6
- package/dist/assets/{Permissions-BGleLja4.js → Permissions-DrgbSGWl.js} +3 -3
- package/dist/assets/{Plugins-P4eCXy_S.js → Plugins-Bd3ef35w.js} +2 -2
- package/dist/assets/{ProvenanceBadge-BFczzI1B.js → ProvenanceBadge-DcBHT1Iy.js} +1 -1
- package/dist/assets/{SaveBar-l9jnEMES.js → SaveBar-rimJem-Y.js} +1 -1
- package/dist/assets/Scheduler-DjgFJl2w.js +16 -0
- package/dist/assets/{ScopeSwitcher-CjJi8ST5.js → ScopeSwitcher-CnAzTBrl.js} +1 -1
- package/dist/assets/Settings-Dc1F_sSg.js +3 -0
- package/dist/assets/{SkillReferenceGraph-CjbO01Sg.js → SkillReferenceGraph-B9aZDecM.js} +1 -1
- package/dist/assets/{Skills-BxZ9_fCR.js → Skills-DRXd18gu.js} +2 -2
- package/dist/assets/{SystemPrompt-D8vfFb8b.js → SystemPrompt-ahy5-Fwe.js} +1 -1
- package/dist/assets/{TagLibrary-2vzUo7YL.js → TagLibrary-C_tToDeL.js} +1 -1
- package/dist/assets/{TiptapBody-B8ZfbK90.js → TiptapBody-DvdefS6K.js} +1 -1
- package/dist/assets/{Toggle-BbW4nGGf.js → Toggle-6Jl5Njc6.js} +1 -1
- package/dist/assets/{index-CKH5Uxik.css → index-Bta-hwud.css} +1 -1
- package/dist/assets/{index-Cd98Q1lD.js → index-vx8O73l8.js} +497 -502
- package/dist/assets/{settingsSchema-IhUfucCF.js → settingsSchema-D6iRguNV.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +25 -29
- package/plugins/CLAUDE.md +6 -6
- package/plugins/session-manager-dev/skills/builder/4-manual/SKILL.md +21 -19
- package/plugins/session-manager-dev/skills/builder/SKILL.md +3 -3
- package/plugins/session-manager-dev/skills/develop/SKILL.md +254 -510
- package/plugins/session-manager-dev/skills/develop/standards.md +17 -22
- package/scripts/README.md +3 -11
- package/scripts/audit-ops-hygiene.cjs +3 -3
- package/scripts/hooks/lib/guard-prd-writes-policy.cjs +1 -1
- package/scripts/hooks/lib/guard-self-schedule-policy.cjs +1 -1
- package/scripts/mint-epic.cjs +2 -3
- package/scripts/scheduler-mcp-server.cjs +36 -7
- package/src/main/agentLibrary.cjs +22 -2
- package/src/main/build-info.json +4 -4
- package/src/main/config.cjs +41 -38
- package/src/main/docEdit.cjs +4 -1
- package/src/main/files.cjs +2 -5
- package/src/main/health.cjs +1 -1
- package/src/main/index.cjs +23 -10
- package/src/main/ipcSchemas.cjs +28 -35
- package/src/main/lib/agentPersonaSchema.cjs +13 -2
- package/src/main/lib/atomicFs.cjs +116 -0
- package/src/main/lib/branchSweep.cjs +13 -12
- package/src/main/lib/buildTarget.cjs +3 -3
- package/src/main/lib/claudeCliCaps.cjs +129 -0
- package/src/main/lib/credentials.cjs +2 -4
- package/src/main/lib/crossProjectFeedback.cjs +3 -3
- package/src/main/lib/cwdClassify.cjs +15 -3
- package/src/main/lib/definitionOfDone.cjs +207 -125
- package/src/main/lib/dodDrainHook.cjs +11 -5
- package/src/main/lib/epicMint.cjs +2 -4
- package/src/main/lib/epicStatusMirror.cjs +8 -10
- package/src/main/lib/epicWorktreeProjectConfig.cjs +2 -4
- package/src/main/lib/gateAuthority.cjs +96 -0
- package/src/main/lib/gitExec.cjs +69 -0
- package/src/main/lib/gitWorktree.cjs +656 -92
- package/src/main/lib/instanceLock.cjs +5 -8
- package/src/main/lib/launchFailure.cjs +2 -3
- package/src/main/lib/macroLibrary.cjs +386 -0
- package/src/main/lib/mcpToolCatalog.cjs +26 -17
- package/src/main/lib/needsReviewLedger.cjs +1 -1
- package/src/main/lib/opsErrorLog.cjs +11 -1
- package/src/main/lib/opsOwnership.cjs +0 -5
- package/src/main/lib/pidAlive.cjs +32 -0
- package/src/main/lib/prdCreate.cjs +121 -12
- package/src/main/lib/prdDisposition.cjs +2 -2
- package/src/main/lib/prdGateFiles.cjs +284 -0
- package/src/main/lib/prdLocations.cjs +61 -0
- package/src/main/lib/prdMigration.cjs +2 -3
- package/src/main/lib/prdSizing.cjs +2 -1
- package/src/main/lib/promptSessionSchema.cjs +10 -2
- package/src/main/lib/rcaReport.cjs +4 -6
- package/src/main/lib/reaperHelpers.cjs +8 -1
- package/src/main/lib/reservationExpiry.cjs +6 -1
- package/src/main/lib/reviewNotice.cjs +263 -0
- package/src/main/lib/runClaudeP.cjs +4 -1
- package/src/main/lib/schedulerPaths.cjs +22 -2
- package/src/main/lib/sessionSlots.cjs +2 -4
- package/src/main/lib/shippedPersonaSeeds.cjs +179 -0
- package/src/main/lib/timeoutShim.cjs +123 -0
- package/src/main/lib/timeoutShimScript.cjs +275 -0
- package/src/main/lib/upgradeDrain.cjs +4 -10
- package/src/main/lib/watchdogHelpers.cjs +12 -25
- package/src/main/lib/workTypeLibrary.cjs +7 -1
- package/src/main/promptSessionEvents.cjs +37 -13
- package/src/main/scheduler.cjs +910 -198
- package/src/main/seedAgentPersonas.cjs +278 -23
- package/src/main/supervisor.cjs +4 -1
- package/src/main/templates/PRD_AUTHORING.md +126 -406
- package/src/preload/__tests__/preload-surface.test.cjs +68 -0
- package/src/preload/api.d.ts +41 -94
- package/src/preload/index.cjs +14 -9
- package/src/seed/agents/architect.md +1 -0
- package/src/seed/agents/dev-lead.md +17 -18
- package/src/seed/agents/project-home-builder.md +1 -0
- package/src/seed/agents/validator.md +10 -4
- package/dist/assets/HostBilko-CDVHE0hT.js +0 -1
- package/dist/assets/Scheduler-C_phpeUS.js +0 -16
- package/dist/assets/Settings-BIwZ3FGQ.js +0 -3
- package/src/main/__tests__/activeIndexMerge.test.cjs +0 -235
- package/src/main/__tests__/agentEffortResolve.test.cjs +0 -117
- package/src/main/__tests__/agentLibrary.test.cjs +0 -276
- package/src/main/__tests__/agentModelResolve.test.cjs +0 -311
- package/src/main/__tests__/agentOverlayWrite.test.cjs +0 -95
- package/src/main/__tests__/bilkoHost-deriveSlug.test.cjs +0 -26
- package/src/main/__tests__/bilkoHost-integration.test.cjs +0 -118
- package/src/main/__tests__/bilkoHostCore.test.cjs +0 -72
- package/src/main/__tests__/broadcastCoalescer.test.cjs +0 -122
- package/src/main/__tests__/chat-cancel-terminal.test.cjs +0 -120
- package/src/main/__tests__/chat-dead-channels.test.cjs +0 -63
- package/src/main/__tests__/chat-exit-close-race.test.cjs +0 -146
- package/src/main/__tests__/chat-mcp-consent-notice.test.cjs +0 -139
- package/src/main/__tests__/chat-preamble-anchors.test.cjs +0 -100
- package/src/main/__tests__/chat-queue.test.cjs +0 -97
- package/src/main/__tests__/chat-stop-signal.test.cjs +0 -89
- package/src/main/__tests__/chatRunner-epic-worktree-execcwd.test.cjs +0 -125
- package/src/main/__tests__/chatRunner-session-flag-retry.test.cjs +0 -252
- package/src/main/__tests__/classifyPromptTicket.test.cjs +0 -101
- package/src/main/__tests__/classifyTranscriptLine.test.cjs +0 -201
- package/src/main/__tests__/computeDepHistorySatisfaction.test.cjs +0 -66
- package/src/main/__tests__/config-readText-bounded.test.cjs +0 -84
- package/src/main/__tests__/configWriteBoundaryOwners.test.cjs +0 -59
- package/src/main/__tests__/crossProjectFeedback.test.cjs +0 -334
- package/src/main/__tests__/crossProjectFeedbackRoutes.test.cjs +0 -161
- package/src/main/__tests__/dep-orphan-archive-health.test.cjs +0 -77
- package/src/main/__tests__/develop-skill-failure-modes.test.cjs +0 -70
- package/src/main/__tests__/docEdit.test.cjs +0 -244
- package/src/main/__tests__/dod-batchkey.test.cjs +0 -183
- package/src/main/__tests__/dod-drain-hook.test.cjs +0 -302
- package/src/main/__tests__/dod-report.test.cjs +0 -304
- package/src/main/__tests__/dod-reverify.test.cjs +0 -285
- package/src/main/__tests__/epicContextDigest.test.cjs +0 -174
- package/src/main/__tests__/epicMint.test.cjs +0 -332
- package/src/main/__tests__/epicMintTelemetryTap.test.cjs +0 -64
- package/src/main/__tests__/epicStatusMirror.test.cjs +0 -110
- package/src/main/__tests__/epicValidationHook.test.cjs +0 -291
- package/src/main/__tests__/exchanges.test.cjs +0 -122
- package/src/main/__tests__/exchangesPromptId.test.cjs +0 -61
- package/src/main/__tests__/extractJson.test.cjs +0 -51
- package/src/main/__tests__/files-reject-credentials.test.cjs +0 -40
- package/src/main/__tests__/fixtures/1218-fo-01-move-scripts-lib-into-src-main-lib.log +0 -556
- package/src/main/__tests__/flatPrdTickSweep.test.cjs +0 -110
- package/src/main/__tests__/health-build-freshness.test.cjs +0 -39
- package/src/main/__tests__/health-claude-md-budget.test.cjs +0 -57
- package/src/main/__tests__/health-credentials.test.cjs +0 -81
- package/src/main/__tests__/health-delegation-chain.test.cjs +0 -124
- package/src/main/__tests__/health-per-project-stall.test.cjs +0 -84
- package/src/main/__tests__/health-prd-migration.test.cjs +0 -37
- package/src/main/__tests__/health-queue-dispatch.test.cjs +0 -135
- package/src/main/__tests__/health-starve-escalation.test.cjs +0 -94
- package/src/main/__tests__/health-tick-liveness.test.cjs +0 -179
- package/src/main/__tests__/health-usage-poller.test.cjs +0 -144
- package/src/main/__tests__/health-worktree-cap-blocked.test.cjs +0 -65
- package/src/main/__tests__/heapSnapshot.test.cjs +0 -121
- package/src/main/__tests__/historyAggregatorIntraday.test.cjs +0 -313
- package/src/main/__tests__/historyDashboard.test.cjs +0 -163
- package/src/main/__tests__/historyRollup.test.cjs +0 -333
- package/src/main/__tests__/intradayRefresh.test.cjs +0 -39
- package/src/main/__tests__/ipcSchemas-dependsOn.test.cjs +0 -39
- package/src/main/__tests__/kg-augment.test.cjs +0 -195
- package/src/main/__tests__/loadGateDetailTick.test.cjs +0 -31
- package/src/main/__tests__/machineProfile.test.cjs +0 -152
- package/src/main/__tests__/mcpStatus.test.cjs +0 -61
- package/src/main/__tests__/memoryAggregate.test.cjs +0 -109
- package/src/main/__tests__/memoryStale.test.cjs +0 -88
- package/src/main/__tests__/needsReviewLedger.test.cjs +0 -162
- package/src/main/__tests__/openExternalApp-spawn-error.test.cjs +0 -25
- package/src/main/__tests__/opsErrorLog.test.cjs +0 -109
- package/src/main/__tests__/opsErrorLogTelemetryTap.test.cjs +0 -173
- package/src/main/__tests__/personaMerge.test.cjs +0 -169
- package/src/main/__tests__/planValidator.test.cjs +0 -125
- package/src/main/__tests__/pollLoop-dispatch-on-failure.test.cjs +0 -176
- package/src/main/__tests__/prd-group-allocator.test.cjs +0 -119
- package/src/main/__tests__/prdAdminRouteParity.test.cjs +0 -70
- package/src/main/__tests__/prdAdminRoutes.test.cjs +0 -718
- package/src/main/__tests__/prdAgentType.test.cjs +0 -103
- package/src/main/__tests__/prdAuthoringSeed.test.cjs +0 -39
- package/src/main/__tests__/prdCreate.test.cjs +0 -979
- package/src/main/__tests__/prdCreateAdoption.test.cjs +0 -205
- package/src/main/__tests__/prdCreateDisposition.test.cjs +0 -201
- package/src/main/__tests__/prdCreatePlanId.test.cjs +0 -132
- package/src/main/__tests__/prdFrontmatterAgentType.test.cjs +0 -117
- package/src/main/__tests__/prdFrontmatterDependsOn.test.cjs +0 -136
- package/src/main/__tests__/prdFrontmatterDisposition.test.cjs +0 -125
- package/src/main/__tests__/prdFrontmatterQuietMachine.test.cjs +0 -108
- package/src/main/__tests__/prdLocations.test.cjs +0 -195
- package/src/main/__tests__/prdLocationsArchived.test.cjs +0 -201
- package/src/main/__tests__/prdMigration.test.cjs +0 -349
- package/src/main/__tests__/prdMigrationLegacyAdopt.test.cjs +0 -91
- package/src/main/__tests__/prdParserHighWater.test.cjs +0 -74
- package/src/main/__tests__/prdParserSourcePromptId.test.cjs +0 -65
- package/src/main/__tests__/prdSetDisposition.test.cjs +0 -222
- package/src/main/__tests__/prdSizing.test.cjs +0 -106
- package/src/main/__tests__/prdSourcePromptIdBackfill.test.cjs +0 -118
- package/src/main/__tests__/prdUpdateDependsOn.test.cjs +0 -160
- package/src/main/__tests__/proc-role-env.test.cjs +0 -125
- package/src/main/__tests__/procname-claude-spawn-sites.test.cjs +0 -304
- package/src/main/__tests__/procname-sm-processes.test.cjs +0 -127
- package/src/main/__tests__/projectHomeAdminRoutes.test.cjs +0 -177
- package/src/main/__tests__/projectPages.test.cjs +0 -137
- package/src/main/__tests__/promptSessionEvents.test.cjs +0 -234
- package/src/main/__tests__/promptSessionSchema.test.cjs +0 -101
- package/src/main/__tests__/promptSessionTranscript.test.cjs +0 -0
- package/src/main/__tests__/promptSessionsCreateEpicHandler.test.cjs +0 -159
- package/src/main/__tests__/pty-epic-worktree-spawn-cwd.test.cjs +0 -282
- package/src/main/__tests__/pty-session-open-telemetry.test.cjs +0 -96
- package/src/main/__tests__/pty-write-result.test.cjs +0 -46
- package/src/main/__tests__/queue-health-verdict.test.cjs +0 -170
- package/src/main/__tests__/queue-starvation-dispatch-driver.test.cjs +0 -286
- package/src/main/__tests__/queue-starvation-per-project.test.cjs +0 -147
- package/src/main/__tests__/queueHistory.test.cjs +0 -355
- package/src/main/__tests__/queueOps-interactive-ac-lint.test.cjs +0 -65
- package/src/main/__tests__/queueOpsArchiveDestination.test.cjs +0 -65
- package/src/main/__tests__/queueOpsAutoArchive.test.cjs +0 -211
- package/src/main/__tests__/rateLimitPollerStreak.test.cjs +0 -128
- package/src/main/__tests__/rcaReport.test.cjs +0 -266
- package/src/main/__tests__/reconcileFlatPrdSweep.test.cjs +0 -119
- package/src/main/__tests__/reconcileTiming.test.cjs +0 -135
- package/src/main/__tests__/runLogRetention.test.cjs +0 -489
- package/src/main/__tests__/runVerify-atomic-verdicts.test.cjs +0 -26
- package/src/main/__tests__/runVerify-blocked-by-foreign-wip.test.cjs +0 -58
- package/src/main/__tests__/runVerify-landed-commit-outranks.test.cjs +0 -191
- package/src/main/__tests__/runVerify-policy-denial.test.cjs +0 -89
- package/src/main/__tests__/runVerify-transcript-commit-evidence.test.cjs +0 -225
- package/src/main/__tests__/runVerify.test.cjs +0 -1784
- package/src/main/__tests__/scheduleJobSchema.test.cjs +0 -127
- package/src/main/__tests__/scheduleJobStatusDrift.test.cjs +0 -65
- package/src/main/__tests__/scheduleJobTransitions.test.cjs +0 -277
- package/src/main/__tests__/scheduleJobTransitionsGrep.test.cjs +0 -59
- package/src/main/__tests__/scheduleJobTransitionsTelemetryTap.test.cjs +0 -72
- package/src/main/__tests__/scheduler-admin-routes.test.cjs +0 -199
- package/src/main/__tests__/scheduler-adopted-run-supervision.test.cjs +0 -143
- package/src/main/__tests__/scheduler-already-satisfied-on-main.test.cjs +0 -105
- package/src/main/__tests__/scheduler-archive-completed-prd.test.cjs +0 -102
- package/src/main/__tests__/scheduler-archived-twin-guard.test.cjs +0 -155
- package/src/main/__tests__/scheduler-autofix-outcome.test.cjs +0 -188
- package/src/main/__tests__/scheduler-autofix-select.test.cjs +0 -439
- package/src/main/__tests__/scheduler-autopromote.test.cjs +0 -51
- package/src/main/__tests__/scheduler-bash-timeout-env.test.cjs +0 -101
- package/src/main/__tests__/scheduler-blocked-by-foreign-wip.test.cjs +0 -107
- package/src/main/__tests__/scheduler-boot-orphans.test.cjs +0 -153
- package/src/main/__tests__/scheduler-broadcast-reconcile.test.cjs +0 -121
- package/src/main/__tests__/scheduler-clear-queue-history.test.cjs +0 -134
- package/src/main/__tests__/scheduler-commit-guard-noop.test.cjs +0 -244
- package/src/main/__tests__/scheduler-committed-in-window.test.cjs +0 -182
- package/src/main/__tests__/scheduler-cross-project-batch.test.cjs +0 -43
- package/src/main/__tests__/scheduler-default-eligible-heal.test.cjs +0 -168
- package/src/main/__tests__/scheduler-dispatch-loop.test.cjs +0 -58
- package/src/main/__tests__/scheduler-effective-concurrency.test.cjs +0 -81
- package/src/main/__tests__/scheduler-epic-digest.test.cjs +0 -227
- package/src/main/__tests__/scheduler-failed-autoreset.test.cjs +0 -121
- package/src/main/__tests__/scheduler-finalize-dispatch-guards.test.cjs +0 -229
- package/src/main/__tests__/scheduler-find-prd-dir.test.cjs +0 -75
- package/src/main/__tests__/scheduler-fix-plan-path.test.cjs +0 -119
- package/src/main/__tests__/scheduler-force-tick-outcome.test.cjs +0 -46
- package/src/main/__tests__/scheduler-foreign-wip-manifest.test.cjs +0 -78
- package/src/main/__tests__/scheduler-gate-shadow.test.cjs +0 -119
- package/src/main/__tests__/scheduler-guard-verdict-autoresolve.test.cjs +0 -390
- package/src/main/__tests__/scheduler-heal-refusal.test.cjs +0 -61
- package/src/main/__tests__/scheduler-heartbeat-payload.test.cjs +0 -80
- package/src/main/__tests__/scheduler-inplace-salvage.test.cjs +0 -323
- package/src/main/__tests__/scheduler-integration-failure-stamp.test.cjs +0 -41
- package/src/main/__tests__/scheduler-investigation-clean-skip.test.cjs +0 -63
- package/src/main/__tests__/scheduler-investigation-prompt.test.cjs +0 -123
- package/src/main/__tests__/scheduler-job-budget.test.cjs +0 -172
- package/src/main/__tests__/scheduler-job-overrun.test.cjs +0 -175
- package/src/main/__tests__/scheduler-launch-failure.test.cjs +0 -199
- package/src/main/__tests__/scheduler-leftover-fields.test.cjs +0 -52
- package/src/main/__tests__/scheduler-leftover-quarantine.test.cjs +0 -199
- package/src/main/__tests__/scheduler-looks-done.test.cjs +0 -537
- package/src/main/__tests__/scheduler-manual-pause.test.cjs +0 -118
- package/src/main/__tests__/scheduler-mechanical-recovery.test.cjs +0 -245
- package/src/main/__tests__/scheduler-meta-code-sha.test.cjs +0 -46
- package/src/main/__tests__/scheduler-mutate-reentrancy.test.cjs +0 -117
- package/src/main/__tests__/scheduler-needs-review-autoresolve.test.cjs +0 -197
- package/src/main/__tests__/scheduler-never-stop.test.cjs +0 -157
- package/src/main/__tests__/scheduler-no-dead-end-status.test.cjs +0 -152
- package/src/main/__tests__/scheduler-no-orphan-run-dir.test.cjs +0 -81
- package/src/main/__tests__/scheduler-notify-originating-tab-transcript.test.cjs +0 -87
- package/src/main/__tests__/scheduler-notify-originating-tab.test.cjs +0 -343
- package/src/main/__tests__/scheduler-periodic-reverify-guard.test.cjs +0 -217
- package/src/main/__tests__/scheduler-porcelain-rename.test.cjs +0 -164
- package/src/main/__tests__/scheduler-prd-missing-skip.test.cjs +0 -161
- package/src/main/__tests__/scheduler-prd-persona-spawn.test.cjs +0 -178
- package/src/main/__tests__/scheduler-quarantine-autoresolve.test.cjs +0 -165
- package/src/main/__tests__/scheduler-quiet-machine-lease.test.cjs +0 -257
- package/src/main/__tests__/scheduler-rate-limit-cooldown-freshness.test.cjs +0 -123
- package/src/main/__tests__/scheduler-rate-limit-pause.test.cjs +0 -152
- package/src/main/__tests__/scheduler-rate-limit-spin-guard.test.cjs +0 -156
- package/src/main/__tests__/scheduler-reap-dead-running-jobs.test.cjs +0 -754
- package/src/main/__tests__/scheduler-reaper-helpers-basics.test.cjs +0 -87
- package/src/main/__tests__/scheduler-reconcile-cwd-preserve.test.cjs +0 -100
- package/src/main/__tests__/scheduler-reconcile-history-backfill.test.cjs +0 -105
- package/src/main/__tests__/scheduler-reconcile-invalid-repair.test.cjs +0 -203
- package/src/main/__tests__/scheduler-reconcile-quarantine.test.cjs +0 -247
- package/src/main/__tests__/scheduler-reset-job-fields-guard.test.cjs +0 -77
- package/src/main/__tests__/scheduler-resume-recovery.test.cjs +0 -254
- package/src/main/__tests__/scheduler-shard-quarantine.test.cjs +0 -115
- package/src/main/__tests__/scheduler-shared-tree-guard.test.cjs +0 -380
- package/src/main/__tests__/scheduler-sigterm-commit.test.cjs +0 -43
- package/src/main/__tests__/scheduler-stall-per-project.test.cjs +0 -126
- package/src/main/__tests__/scheduler-starve-escalation.test.cjs +0 -154
- package/src/main/__tests__/scheduler-stranded-autofix-park.test.cjs +0 -252
- package/src/main/__tests__/scheduler-stranded-investigation.test.cjs +0 -185
- package/src/main/__tests__/scheduler-stuck-failed-escalation.test.cjs +0 -136
- package/src/main/__tests__/scheduler-supervisor-record.test.cjs +0 -81
- package/src/main/__tests__/scheduler-tick-cancel-token.test.cjs +0 -54
- package/src/main/__tests__/scheduler-tick-wedge.test.cjs +0 -172
- package/src/main/__tests__/scheduler-transient-failure.test.cjs +0 -141
- package/src/main/__tests__/scheduler-unreadable-queue-guard.test.cjs +0 -62
- package/src/main/__tests__/scheduler-utilization-hold.test.cjs +0 -89
- package/src/main/__tests__/scheduler-verify-prd-path.test.cjs +0 -109
- package/src/main/__tests__/scheduler-worktree-cap-defer.test.cjs +0 -234
- package/src/main/__tests__/scheduler-worktree-exec-cwd.test.cjs +0 -120
- package/src/main/__tests__/scheduler-writeprd-epic-rollback.test.cjs +0 -105
- package/src/main/__tests__/schedulerBatchRootBlocker.test.cjs +0 -117
- package/src/main/__tests__/schedulerStateSidecarRestore.test.cjs +0 -110
- package/src/main/__tests__/seedAgentPersonas.test.cjs +0 -184
- package/src/main/__tests__/seedSchedulerMcp.test.cjs +0 -211
- package/src/main/__tests__/seedStatus.test.cjs +0 -100
- package/src/main/__tests__/seedValidatorPersona.test.cjs +0 -116
- package/src/main/__tests__/stop-signal-anchor.test.cjs +0 -75
- package/src/main/__tests__/telemetryClient.test.cjs +0 -1055
- package/src/main/__tests__/telemetryContract.test.cjs +0 -930
- package/src/main/__tests__/telemetrySettings.test.cjs +0 -210
- package/src/main/__tests__/transcripts-batch-flush.test.cjs +0 -249
- package/src/main/__tests__/transcripts-doFlush-array.test.cjs +0 -124
- package/src/main/__tests__/transcripts-paged-reads.test.cjs +0 -241
- package/src/main/__tests__/transcripts-worktree-epic-path.test.cjs +0 -154
- package/src/main/__tests__/transcriptsUsageFor.test.cjs +0 -206
- package/src/main/__tests__/uniquePrdNumbers.test.cjs +0 -153
- package/src/main/__tests__/usageSingleFlight.test.cjs +0 -169
- package/src/main/__tests__/validationSentinels.test.cjs +0 -84
- package/src/main/__tests__/workTypeLibrary.test.cjs +0 -89
- package/src/main/bilkoHost.cjs +0 -314
- package/src/main/bilkoHostCore.cjs +0 -89
- package/src/main/lib/__tests__/active-sessions.test.cjs +0 -251
- package/src/main/lib/__tests__/activeIndexRebuild.test.cjs +0 -179
- package/src/main/lib/__tests__/agentPersonaSchema.test.cjs +0 -67
- package/src/main/lib/__tests__/auditLog.test.cjs +0 -38
- package/src/main/lib/__tests__/bootSelfHeal.test.cjs +0 -107
- package/src/main/lib/__tests__/branchSweep.test.cjs +0 -164
- package/src/main/lib/__tests__/buildIdentity.test.cjs +0 -121
- package/src/main/lib/__tests__/buildTarget.test.cjs +0 -52
- package/src/main/lib/__tests__/childWithLog.test.cjs +0 -321
- package/src/main/lib/__tests__/coldBootPromptSessionsWrite.test.cjs +0 -87
- package/src/main/lib/__tests__/crashTelemetry.test.cjs +0 -103
- package/src/main/lib/__tests__/credentials-futile-refresh.test.cjs +0 -115
- package/src/main/lib/__tests__/cwdClassify.test.cjs +0 -111
- package/src/main/lib/__tests__/definitionOfDoneSequence.test.cjs +0 -95
- package/src/main/lib/__tests__/delegationReadiness.test.cjs +0 -1175
- package/src/main/lib/__tests__/dispatchLoop.test.cjs +0 -63
- package/src/main/lib/__tests__/effectiveModelInfo.test.cjs +0 -244
- package/src/main/lib/__tests__/ephemeralCwd.test.cjs +0 -91
- package/src/main/lib/__tests__/epicDelegationStats.test.cjs +0 -137
- package/src/main/lib/__tests__/epicSpawnCwd.test.cjs +0 -283
- package/src/main/lib/__tests__/epicSpawnPlan.test.cjs +0 -196
- package/src/main/lib/__tests__/epicTranscriptPath.test.cjs +0 -163
- package/src/main/lib/__tests__/epicWorktreeBoot.test.cjs +0 -136
- package/src/main/lib/__tests__/epicWorktreeMerge.test.cjs +0 -130
- package/src/main/lib/__tests__/epicWorktreeMint.test.cjs +0 -117
- package/src/main/lib/__tests__/epicWorktreeProjectConfig.test.cjs +0 -137
- package/src/main/lib/__tests__/fixChainDepth.test.cjs +0 -40
- package/src/main/lib/__tests__/fixtures/204-mercury-steam-horse.log.txt +0 -13
- package/src/main/lib/__tests__/fixtures/scheduler-machine.json.corrupt-1789147548 +0 -34
- package/src/main/lib/__tests__/gateFixtures.json +0 -20
- package/src/main/lib/__tests__/gitCacheBound.test.cjs +0 -69
- package/src/main/lib/__tests__/gitWorktree.test.cjs +0 -1478
- package/src/main/lib/__tests__/gitWorktreeSalvage.test.cjs +0 -107
- package/src/main/lib/__tests__/gitWorktreeSalvageDelta.test.cjs +0 -153
- package/src/main/lib/__tests__/guardShims.test.cjs +0 -158
- package/src/main/lib/__tests__/importReferences.spec.cjs +0 -56
- package/src/main/lib/__tests__/instanceLock.test.cjs +0 -173
- package/src/main/lib/__tests__/jobSupervisorRecord.test.cjs +0 -78
- package/src/main/lib/__tests__/jobWorktree.test.cjs +0 -199
- package/src/main/lib/__tests__/jobWorktreeBootLive.test.cjs +0 -82
- package/src/main/lib/__tests__/landedSinceRun.test.cjs +0 -133
- package/src/main/lib/__tests__/launchFailure.test.cjs +0 -220
- package/src/main/lib/__tests__/loadGate.test.cjs +0 -303
- package/src/main/lib/__tests__/localAdminHttp.test.cjs +0 -214
- package/src/main/lib/__tests__/loopDelay.test.cjs +0 -68
- package/src/main/lib/__tests__/mcpToolCatalog.test.cjs +0 -107
- package/src/main/lib/__tests__/modelCatalog.test.cjs +0 -202
- package/src/main/lib/__tests__/opsOwnership.test.cjs +0 -113
- package/src/main/lib/__tests__/opsRootAbsoluteCwd.test.cjs +0 -328
- package/src/main/lib/__tests__/opsRootNestedWrite.test.cjs +0 -51
- package/src/main/lib/__tests__/opsRootResolve.test.cjs +0 -149
- package/src/main/lib/__tests__/prdDeclaredPaths.test.cjs +0 -82
- package/src/main/lib/__tests__/prdDisposition.test.cjs +0 -224
- package/src/main/lib/__tests__/procIdentity.test.cjs +0 -119
- package/src/main/lib/__tests__/procName.test.cjs +0 -92
- package/src/main/lib/__tests__/projectBriefCore.test.cjs +0 -216
- package/src/main/lib/__tests__/projectRootResolve.test.cjs +0 -148
- package/src/main/lib/__tests__/queueHealth.test.cjs +0 -58
- package/src/main/lib/__tests__/queueStoreAtomicWrite.test.cjs +0 -88
- package/src/main/lib/__tests__/queueStoreMachineStateRecovery.test.cjs +0 -190
- package/src/main/lib/__tests__/quietMachineLease.test.cjs +0 -39
- package/src/main/lib/__tests__/rateLimitWindow.test.cjs +0 -88
- package/src/main/lib/__tests__/reaperHelpers.test.cjs +0 -577
- package/src/main/lib/__tests__/schedulerBatchDepends.test.cjs +0 -312
- package/src/main/lib/__tests__/schedulerBatchFairness.test.cjs +0 -213
- package/src/main/lib/__tests__/schedulerBatchLaunchHold.test.cjs +0 -125
- package/src/main/lib/__tests__/schedulerBatchProjectCap.test.cjs +0 -127
- package/src/main/lib/__tests__/schedulerBatchQuietMachine.test.cjs +0 -109
- package/src/main/lib/__tests__/schedulerMcpServerHeadlessRefusal.test.cjs +0 -71
- package/src/main/lib/__tests__/schedulerMcpServerHelp.test.cjs +0 -216
- package/src/main/lib/__tests__/schedulerMcpServerProjectHome.test.cjs +0 -183
- package/src/main/lib/__tests__/schedulerPaths.test.cjs +0 -226
- package/src/main/lib/__tests__/schedulerPathsWorktree.test.cjs +0 -133
- package/src/main/lib/__tests__/schedulerRuntimeState.test.cjs +0 -56
- package/src/main/lib/__tests__/sessionSlots.test.cjs +0 -144
- package/src/main/lib/__tests__/telemetryBacklog.test.cjs +0 -626
- package/src/main/lib/__tests__/telemetryBoot.test.cjs +0 -134
- package/src/main/lib/__tests__/telemetryConsent.test.cjs +0 -136
- package/src/main/lib/__tests__/telemetryCounters.test.cjs +0 -57
- package/src/main/lib/__tests__/telemetryCountersMetadataColumn.test.cjs +0 -98
- package/src/main/lib/__tests__/terminalRunOutcome.test.cjs +0 -200
- package/src/main/lib/__tests__/toolUseClassify.test.cjs +0 -53
- package/src/main/lib/__tests__/updateCheck.test.cjs +0 -63
- package/src/main/lib/__tests__/upgradeDrain.test.cjs +0 -130
- package/src/main/lib/__tests__/usageCircuit.test.cjs +0 -354
- package/src/main/lib/__tests__/watchdog-helpers.test.cjs +0 -441
- package/src/main/lib/__tests__/watchdog-relaunch.test.cjs +0 -266
- package/src/main/lib/kgExchangePairing.cjs +0 -75
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* flatPrdTickSweep.test.cjs — the retired flat `scheduler/prds/` dir must be
|
|
3
|
-
* TRULY inert, not just documented as such.
|
|
4
|
-
*
|
|
5
|
-
* Prior to this PRD, `runPrdMigration()`'s consolidation of the flat dir into
|
|
6
|
-
* `prds-archived/` ran ONLY at Electron boot (src/main/scheduler.cjs). But
|
|
7
|
-
* `prdLocations.cjs`'s `resolvePrdsDirs()` unconditionally keeps scanning the
|
|
8
|
-
* flat dir as a live PRD source, and `reconcile()` runs on every tick — so a
|
|
9
|
-
* PRD hand-written into the flat dir *after* boot sat there live and WOULD
|
|
10
|
-
* get reconciled into a pending job and executed (observed 2026-08-07:
|
|
11
|
-
* 1021/1022 PRDs in social-signals-trader executed once their status was
|
|
12
|
-
* repaired), contradicting the develop skill's documented claim that
|
|
13
|
-
* anything landed there is "auto-consolidated ... WITHOUT being executed."
|
|
14
|
-
*
|
|
15
|
-
* The fix: `consolidateAllFlatPrds()` now also runs at the START of every
|
|
16
|
-
* `reconcile()` call itself (not just one caller like tickQueue's poll — every
|
|
17
|
-
* caller of reconcile gets the guarantee), BEFORE reconcile scans the flat
|
|
18
|
-
* dir. A freshly hand-written PRD has no queue row yet (reconcile hasn't
|
|
19
|
-
* seen it), so it is never in LIVE_JOB_STATUSES — this sweep archives it
|
|
20
|
-
* before reconcile ever gets a chance to turn it into a job. This test
|
|
21
|
-
* proves that invariant directly against `consolidateAllFlatPrds`: a file
|
|
22
|
-
* with no matching queue job is swept into `prds-archived/` on the very next
|
|
23
|
-
* call, which prdLocations.cjs's `resolvePrdsDirs()` never scans as a live
|
|
24
|
-
* source — so it can never be reconciled into a job afterward.
|
|
25
|
-
*
|
|
26
|
-
* Run: timeout 120 npx vitest run src/main/__tests__/flatPrdTickSweep.test.cjs
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
'use strict';
|
|
30
|
-
|
|
31
|
-
import { test, expect, afterEach } from 'vitest';
|
|
32
|
-
const fs = require('node:fs');
|
|
33
|
-
const fsp = require('node:fs/promises');
|
|
34
|
-
const os = require('node:os');
|
|
35
|
-
const path = require('node:path');
|
|
36
|
-
const { consolidateAllFlatPrds } = require('../scheduler.cjs');
|
|
37
|
-
const { resolvePrdWriteDir, resolvePrdsDirs } = require('../lib/prdLocations.cjs');
|
|
38
|
-
|
|
39
|
-
const tmpDirs = [];
|
|
40
|
-
afterEach(async () => {
|
|
41
|
-
while (tmpDirs.length) {
|
|
42
|
-
const d = tmpDirs.pop();
|
|
43
|
-
await fsp.rm(d, { recursive: true, force: true });
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
function newProjectRoot() {
|
|
48
|
-
const d = fs.mkdtempSync(path.join(os.tmpdir(), 'sm-flat-tick-sweep-'));
|
|
49
|
-
tmpDirs.push(d);
|
|
50
|
-
return d;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
test('a PRD hand-written into the retired flat dir after boot is swept before it could ever be reconciled', async () => {
|
|
54
|
-
const cwd = newProjectRoot();
|
|
55
|
-
const flatDir = resolvePrdWriteDir(cwd);
|
|
56
|
-
await fsp.mkdir(flatDir, { recursive: true });
|
|
57
|
-
const filename = '999-hand-written-bypass.md';
|
|
58
|
-
await fsp.writeFile(
|
|
59
|
-
path.join(flatDir, filename),
|
|
60
|
-
`---\ntitle: Hand-written bypass\ncwd: ${cwd}\nestimateMinutes: 10\n---\n\n# Goal\n\nSomething.\n`,
|
|
61
|
-
'utf8',
|
|
62
|
-
);
|
|
63
|
-
// No queue.json at all yet — this is exactly the state of a PRD dropped on
|
|
64
|
-
// disk moments ago, before any reconcile() pass has scanned it into a job.
|
|
65
|
-
|
|
66
|
-
await consolidateAllFlatPrds([cwd]);
|
|
67
|
-
|
|
68
|
-
// Swept out of the live flat dir...
|
|
69
|
-
expect(fs.existsSync(path.join(flatDir, filename))).toBe(false);
|
|
70
|
-
// ...and into prds-archived/, which resolvePrdsDirs() never returns as a
|
|
71
|
-
// scan source — so no future reconcile() pass can find it and queue it.
|
|
72
|
-
const archivedPath = path.join(path.dirname(flatDir), 'prds-archived', filename);
|
|
73
|
-
expect(fs.existsSync(archivedPath)).toBe(true);
|
|
74
|
-
|
|
75
|
-
const liveDirs = resolvePrdsDirs(Infinity, { projectsDir: path.join(cwd, '.nonexistent') });
|
|
76
|
-
expect(liveDirs.every((d) => !d.includes('prds-archived'))).toBe(true);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
test('consolidateAllFlatPrds still protects a PRD that already has a live queue job (PRD 992 invariant preserved)', async () => {
|
|
80
|
-
const cwd = newProjectRoot();
|
|
81
|
-
const flatDir = resolvePrdWriteDir(cwd);
|
|
82
|
-
await fsp.mkdir(flatDir, { recursive: true });
|
|
83
|
-
const stateDir = path.join(cwd, 'session-manager-operations', 'scheduler', 'state');
|
|
84
|
-
await fsp.mkdir(stateDir, { recursive: true });
|
|
85
|
-
await fsp.writeFile(
|
|
86
|
-
path.join(flatDir, '998-live.md'),
|
|
87
|
-
`---\ntitle: Live\ncwd: ${cwd}\nestimateMinutes: 10\n---\n\n# Goal\n`,
|
|
88
|
-
'utf8',
|
|
89
|
-
);
|
|
90
|
-
await fsp.writeFile(
|
|
91
|
-
path.join(stateDir, 'queue.json'),
|
|
92
|
-
JSON.stringify({ jobs: [{ slug: '998-live', status: 'running' }] }),
|
|
93
|
-
'utf8',
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
await consolidateAllFlatPrds([cwd]);
|
|
97
|
-
|
|
98
|
-
expect(fs.existsSync(path.join(flatDir, '998-live.md'))).toBe(true);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
test('consolidateAllFlatPrds never throws when a project errors — one bad cwd does not block the sweep of the rest', async () => {
|
|
102
|
-
const good = newProjectRoot();
|
|
103
|
-
const flatDir = resolvePrdWriteDir(good);
|
|
104
|
-
await fsp.mkdir(flatDir, { recursive: true });
|
|
105
|
-
await fsp.writeFile(path.join(flatDir, '997-ok.md'), `---\ntitle: Ok\ncwd: ${good}\nestimateMinutes: 5\n---\n\n# Goal\n`, 'utf8');
|
|
106
|
-
|
|
107
|
-
await expect(consolidateAllFlatPrds([null, good])).resolves.toBeUndefined();
|
|
108
|
-
|
|
109
|
-
expect(fs.existsSync(path.join(flatDir, '997-ok.md'))).toBe(false);
|
|
110
|
-
});
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* health-build-freshness.test.cjs — evaluateBuildFreshness separates
|
|
3
|
-
* restart-needed (installed differs from running) from publish-needed
|
|
4
|
-
* (repo HEAD differs from installed); both reported, never critical.
|
|
5
|
-
*
|
|
6
|
-
* Run: timeout 120 npx vitest run src/main/__tests__/health-build-freshness.test.cjs
|
|
7
|
-
*/
|
|
8
|
-
'use strict';
|
|
9
|
-
|
|
10
|
-
import { test, expect } from 'vitest';
|
|
11
|
-
const { evaluateBuildFreshness } = require('../health.cjs');
|
|
12
|
-
|
|
13
|
-
test('all shas equal → nothing needed', () => {
|
|
14
|
-
const r = evaluateBuildFreshness({ running: 'abc1234', installedOnDisk: 'abc1234', repoHead: 'abc1234' });
|
|
15
|
-
expect(r).toMatchObject({ ok: true, restartNeeded: false, publishNeeded: false });
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
test('installed differs from running → restart needed only', () => {
|
|
19
|
-
const r = evaluateBuildFreshness({ running: 'aaa1111', installedOnDisk: 'bbb2222', repoHead: 'bbb2222' });
|
|
20
|
-
expect(r).toMatchObject({ ok: true, restartNeeded: true, publishNeeded: false });
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
test('repo HEAD differs from installed → publish needed only', () => {
|
|
24
|
-
const r = evaluateBuildFreshness({ running: 'bbb2222', installedOnDisk: 'bbb2222', repoHead: 'ccc3333' });
|
|
25
|
-
expect(r).toMatchObject({ ok: true, restartNeeded: false, publishNeeded: true });
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
test('both differ → both reported, still ok (non-critical)', () => {
|
|
29
|
-
const r = evaluateBuildFreshness({ running: 'aaa1111', installedOnDisk: 'bbb2222', repoHead: 'ccc3333' });
|
|
30
|
-
expect(r).toMatchObject({ ok: true, restartNeeded: true, publishNeeded: true });
|
|
31
|
-
expect(r.note).toMatch(/restart needed/);
|
|
32
|
-
expect(r.note).toMatch(/publish needed/);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test('unknown inputs never claim a difference; short/long sha prefixes match', () => {
|
|
36
|
-
expect(evaluateBuildFreshness({})).toMatchObject({ restartNeeded: false, publishNeeded: false });
|
|
37
|
-
expect(evaluateBuildFreshness({ running: null, installedOnDisk: 'abc1234', repoHead: 'abc1234567890' }))
|
|
38
|
-
.toMatchObject({ restartNeeded: false, publishNeeded: false });
|
|
39
|
-
});
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* health-claude-md-budget.test.cjs — CLAUDE.md SIZE BUDGET enforcement
|
|
3
|
-
* (PRD 1039, following CLAUDE.md's growth from 3,871 to 65,768 chars between
|
|
4
|
-
* 2026-05-09 and 2026-08-13 with zero of 359 PRDs ever pruning it). Exercises
|
|
5
|
-
* parseClaudeMdBudget()/evaluateClaudeMdBudget() directly since they're pure —
|
|
6
|
-
* no need to spawn the full check() to cover this.
|
|
7
|
-
*
|
|
8
|
-
* Run: timeout 120 npx vitest run src/main/__tests__/health-claude-md-budget.test.cjs
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
'use strict';
|
|
12
|
-
|
|
13
|
-
import { test, expect } from 'vitest';
|
|
14
|
-
const { parseClaudeMdBudget, evaluateClaudeMdBudget } = require('../health.cjs');
|
|
15
|
-
|
|
16
|
-
test('under budget reports ok with no issue', () => {
|
|
17
|
-
const result = evaluateClaudeMdBudget(11873, 12000);
|
|
18
|
-
expect(result.ok).toBe(true);
|
|
19
|
-
expect(result.applicable).toBe(true);
|
|
20
|
-
expect(result.message).toBeUndefined();
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
test('exactly at budget reports ok with no issue', () => {
|
|
24
|
-
const result = evaluateClaudeMdBudget(12000, 12000);
|
|
25
|
-
expect(result.ok).toBe(true);
|
|
26
|
-
expect(result.applicable).toBe(true);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
test('over budget reports one issue with actual size and budget in the message', () => {
|
|
30
|
-
const result = evaluateClaudeMdBudget(13210, 12000);
|
|
31
|
-
expect(result.ok).toBe(false);
|
|
32
|
-
expect(result.overage).toBe(1210);
|
|
33
|
-
expect(result.message).toContain('13210');
|
|
34
|
-
expect(result.message).toContain('12000');
|
|
35
|
-
expect(result.message).toContain('1210');
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
test('no SIZE BUDGET line present -> parses to null, evaluator skips silently', () => {
|
|
39
|
-
const text = '# Some Project\n\nJust a normal CLAUDE.md with no budget declared.\n';
|
|
40
|
-
const budget = parseClaudeMdBudget(text);
|
|
41
|
-
expect(budget).toBeNull();
|
|
42
|
-
const result = evaluateClaudeMdBudget(50000, budget);
|
|
43
|
-
expect(result.ok).toBe(true);
|
|
44
|
-
expect(result.applicable).toBe(false);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
test('malformed/unparseable budget line -> null, no throw', () => {
|
|
48
|
-
const text = '> **SIZE BUDGET — a lot of chars**, checked before every commit.';
|
|
49
|
-
expect(() => parseClaudeMdBudget(text)).not.toThrow();
|
|
50
|
-
const budget = parseClaudeMdBudget(text);
|
|
51
|
-
expect(budget).toBeNull();
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test('comma-separated form parses to a plain integer', () => {
|
|
55
|
-
const text = '> **SIZE BUDGET — 12,000 chars**, checked with `wc -c CLAUDE.md` before every commit.';
|
|
56
|
-
expect(parseClaudeMdBudget(text)).toBe(12000);
|
|
57
|
-
});
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* health-credentials.test.cjs — the credential health component.
|
|
3
|
-
*
|
|
4
|
-
* An expired/absent Claude OAuth token silently voids the whole scheduler:
|
|
5
|
-
* headless `claude -p` jobs cannot authenticate, so the queue "runs" but
|
|
6
|
-
* nothing completes. On this machine the token expired 2026-04-04 with no
|
|
7
|
-
* refresh token, yet `npm run health` read GREEN — no component pointed the
|
|
8
|
-
* operator at `claude login`. evaluateCredentialHealth is the missing signal.
|
|
9
|
-
* Pure over an injected credState (the shape refreshIfNeeded returns), so no
|
|
10
|
-
* Keychain access here.
|
|
11
|
-
*
|
|
12
|
-
* Run: timeout 120 npx vitest run src/main/__tests__/health-credentials.test.cjs
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
'use strict';
|
|
16
|
-
|
|
17
|
-
import { test, expect } from 'vitest';
|
|
18
|
-
const { evaluateCredentialHealth } = require('../health.cjs');
|
|
19
|
-
|
|
20
|
-
const NOW = Date.parse('2026-09-23T15:00:00.000Z');
|
|
21
|
-
|
|
22
|
-
test('an expired token is not ok, names claude login, and reports how long ago it lapsed', () => {
|
|
23
|
-
const expiredAt = NOW - 90 * 60_000; // 90 min ago
|
|
24
|
-
const { component, issues } = evaluateCredentialHealth({
|
|
25
|
-
credState: { kind: 'auth', message: 'Credentials expired.', expiredAt },
|
|
26
|
-
now: NOW,
|
|
27
|
-
});
|
|
28
|
-
expect(component.ok).toBe(false);
|
|
29
|
-
expect(component.state).toBe('expired');
|
|
30
|
-
expect(component.expiredAt).toBe(expiredAt);
|
|
31
|
-
expect(component.message).toMatch(/claude login/);
|
|
32
|
-
expect(component.message).toMatch(/90m ago/);
|
|
33
|
-
expect(issues.length).toBe(1);
|
|
34
|
-
expect(issues[0]).toMatch(/cannot authenticate/i);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
test('an auth failure with no expiredAt still fails and still names the fix', () => {
|
|
38
|
-
const { component, issues } = evaluateCredentialHealth({
|
|
39
|
-
credState: { kind: 'auth', message: 'refresh rejected: HTTP 401' },
|
|
40
|
-
now: NOW,
|
|
41
|
-
});
|
|
42
|
-
expect(component.ok).toBe(false);
|
|
43
|
-
expect(component.state).toBe('expired');
|
|
44
|
-
expect(component.message).toMatch(/claude login/);
|
|
45
|
-
expect(component.message).not.toMatch(/NaN/);
|
|
46
|
-
expect(issues.length).toBe(1);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
test('unreadable/absent credentials (kind:config) fail with the login fix', () => {
|
|
50
|
-
const { component, issues } = evaluateCredentialHealth({
|
|
51
|
-
credState: { kind: 'config', message: 'credentials not found' },
|
|
52
|
-
now: NOW,
|
|
53
|
-
});
|
|
54
|
-
expect(component.ok).toBe(false);
|
|
55
|
-
expect(component.state).toBe('unreadable');
|
|
56
|
-
expect(component.message).toMatch(/claude login/);
|
|
57
|
-
expect(issues.length).toBe(1);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
test('a fresh token is ok and raises no issue', () => {
|
|
61
|
-
const { component, issues } = evaluateCredentialHealth({ credState: { kind: 'ok' }, now: NOW });
|
|
62
|
-
expect(component.ok).toBe(true);
|
|
63
|
-
expect(component.state).toBe('valid');
|
|
64
|
-
expect(issues.length).toBe(0);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
test('kind:unsupported (auto-refresh unavailable, token still valid) is ok — the token works, refresh just can not', () => {
|
|
68
|
-
const { component, issues } = evaluateCredentialHealth({ credState: { kind: 'unsupported' }, now: NOW });
|
|
69
|
-
expect(component.ok).toBe(true);
|
|
70
|
-
expect(component.state).toBe('valid-no-refresh');
|
|
71
|
-
expect(issues.length).toBe(0);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
test('a missing/garbage credState never throws and never fails health (unknown, not a false alarm)', () => {
|
|
75
|
-
for (const bad of [undefined, null, 'nope', 42]) {
|
|
76
|
-
const { component, issues } = evaluateCredentialHealth({ credState: bad, now: NOW });
|
|
77
|
-
expect(component.ok).toBe(true);
|
|
78
|
-
expect(component.state).toBe('unknown');
|
|
79
|
-
expect(issues.length).toBe(0);
|
|
80
|
-
}
|
|
81
|
-
});
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* health-delegation-chain.test.cjs — "can this machine actually delegate
|
|
3
|
-
* work to the scheduler?" health section (PRD 1049), built on top of the
|
|
4
|
-
* delegation-readiness probe (delegationReadiness.cjs, PRD 1048). Exercises
|
|
5
|
-
* evaluateDelegationChainHealth() directly since it's pure — no need to spawn
|
|
6
|
-
* the full check() to cover shaping/degradation — plus one end-to-end check()
|
|
7
|
-
* run against this repo's own real config, where every check is expected to
|
|
8
|
-
* pass (this repo registers the scheduler MCP in its own .mcp.json and wires
|
|
9
|
-
* guard-prd-writes in .claude/settings.json).
|
|
10
|
-
*
|
|
11
|
-
* Run: timeout 120 npx vitest run src/main/__tests__/health-delegation-chain.test.cjs
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
'use strict';
|
|
15
|
-
|
|
16
|
-
import { test, expect } from 'vitest';
|
|
17
|
-
const { evaluateDelegationChainHealth, check } = require('../health.cjs');
|
|
18
|
-
|
|
19
|
-
function makeCheck(id, ok, overrides = {}) {
|
|
20
|
-
return { id, label: `${id} label`, ok, detail: `${id} detail`, fix: ok ? null : `fix ${id}`, ...overrides };
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
test('all checks passing -> component ok, no issues', () => {
|
|
24
|
-
const delegationResult = {
|
|
25
|
-
ok: true,
|
|
26
|
-
checks: [
|
|
27
|
-
makeCheck('scheduler-mcp', true),
|
|
28
|
-
makeCheck('scheduler-mcp-live', true),
|
|
29
|
-
makeCheck('scheduler-mcp-project-duplicate', true, { warn: false }),
|
|
30
|
-
makeCheck('dev-plugin', true),
|
|
31
|
-
makeCheck('agent-personas', true),
|
|
32
|
-
makeCheck('prd-write-guard', true),
|
|
33
|
-
],
|
|
34
|
-
};
|
|
35
|
-
const { component, issues } = evaluateDelegationChainHealth(delegationResult);
|
|
36
|
-
expect(component.ok).toBe(true);
|
|
37
|
-
expect(component.checks).toHaveLength(6);
|
|
38
|
-
expect(issues).toEqual([]);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
test('one failing check -> component not ok, one issue citing label/detail/fix', () => {
|
|
42
|
-
const delegationResult = {
|
|
43
|
-
ok: false,
|
|
44
|
-
checks: [
|
|
45
|
-
makeCheck('scheduler-mcp', false, { label: 'Scheduler MCP server registered', detail: 'no entry found', fix: 'claude mcp add ...' }),
|
|
46
|
-
makeCheck('scheduler-mcp-live', true, { skipped: true }),
|
|
47
|
-
makeCheck('scheduler-mcp-project-duplicate', true, { warn: false }),
|
|
48
|
-
makeCheck('dev-plugin', true),
|
|
49
|
-
makeCheck('agent-personas', true),
|
|
50
|
-
makeCheck('prd-write-guard', true),
|
|
51
|
-
],
|
|
52
|
-
};
|
|
53
|
-
const { component, issues } = evaluateDelegationChainHealth(delegationResult);
|
|
54
|
-
expect(component.ok).toBe(false);
|
|
55
|
-
expect(issues).toHaveLength(1);
|
|
56
|
-
expect(issues[0]).toContain('Scheduler MCP server registered');
|
|
57
|
-
expect(issues[0]).toContain('no entry found');
|
|
58
|
-
expect(issues[0]).toContain('claude mcp add ...');
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
test('multiple failing checks -> one issue per failing check, passing checks silent', () => {
|
|
62
|
-
const delegationResult = {
|
|
63
|
-
ok: false,
|
|
64
|
-
checks: [
|
|
65
|
-
makeCheck('scheduler-mcp', false),
|
|
66
|
-
makeCheck('scheduler-mcp-live', false),
|
|
67
|
-
makeCheck('scheduler-mcp-project-duplicate', true, { warn: false }),
|
|
68
|
-
makeCheck('dev-plugin', false),
|
|
69
|
-
makeCheck('agent-personas', true),
|
|
70
|
-
makeCheck('prd-write-guard', true),
|
|
71
|
-
],
|
|
72
|
-
};
|
|
73
|
-
const { issues } = evaluateDelegationChainHealth(delegationResult);
|
|
74
|
-
expect(issues).toHaveLength(3);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
test('failing check with no fix omits the "(fix: ...)" suffix', () => {
|
|
78
|
-
const delegationResult = {
|
|
79
|
-
ok: false,
|
|
80
|
-
checks: [makeCheck('scheduler-mcp', false, { fix: null })],
|
|
81
|
-
};
|
|
82
|
-
const { issues } = evaluateDelegationChainHealth(delegationResult);
|
|
83
|
-
expect(issues[0]).not.toContain('(fix:');
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
test('end-to-end: check() reports delegation_chain for this repo, expected to pass here', async () => {
|
|
87
|
-
// Probes THIS machine's real user-scope config (~/.claude.json MCP registration, guard
|
|
88
|
-
// shims), so it reads the real home — not the per-run sandbox HOME. Read-only: scheduler
|
|
89
|
-
// state stays redirected to the sandbox (SM_SCHEDULER_HOME re-pinned after the HOME swap).
|
|
90
|
-
const sandboxHome = process.env.HOME;
|
|
91
|
-
const sandboxSchedulerHome = process.env.SM_SCHEDULER_HOME;
|
|
92
|
-
process.env.HOME = require('node:os').userInfo().homedir;
|
|
93
|
-
if (sandboxSchedulerHome !== undefined) process.env.SM_SCHEDULER_HOME = sandboxSchedulerHome;
|
|
94
|
-
process.env.SM_ALLOW_LIVE_ROOT_READS = '1';
|
|
95
|
-
let status;
|
|
96
|
-
try {
|
|
97
|
-
// skipTypecheck: this test asserts on delegation_chain only; the tsc shell-out was
|
|
98
|
-
// ~15s of the ~16s idle runtime and starved to the 90s budget under CPU load.
|
|
99
|
-
status = await check({ skipTypecheck: true });
|
|
100
|
-
} finally {
|
|
101
|
-
delete process.env.SM_ALLOW_LIVE_ROOT_READS;
|
|
102
|
-
process.env.HOME = sandboxHome;
|
|
103
|
-
}
|
|
104
|
-
expect(status.components.delegation_chain).toBeDefined();
|
|
105
|
-
expect(status.components.delegation_chain.checks).toHaveLength(9);
|
|
106
|
-
const ids = status.components.delegation_chain.checks.map((c) => c.id);
|
|
107
|
-
expect(ids).toEqual([
|
|
108
|
-
'scheduler-mcp',
|
|
109
|
-
'scheduler-mcp-live',
|
|
110
|
-
'scheduler-mcp-project-duplicate',
|
|
111
|
-
'dev-plugin',
|
|
112
|
-
'agent-personas',
|
|
113
|
-
'prd-write-guard',
|
|
114
|
-
'destructive-git-guard',
|
|
115
|
-
'inline-implementation-guard',
|
|
116
|
-
'self-schedule-guard',
|
|
117
|
-
]);
|
|
118
|
-
// This repo registers the scheduler MCP at user scope (~/.claude.json) and
|
|
119
|
-
// wires guard-prd-writes in .claude/settings.json, and no longer carries a
|
|
120
|
-
// project-scope duplicate in its own .mcp.json, so on this machine every
|
|
121
|
-
// check is expected to pass — a failure here means the probe or the
|
|
122
|
-
// wiring is wrong, not that the assertion should be relaxed.
|
|
123
|
-
expect(status.components.delegation_chain.ok).toBe(true);
|
|
124
|
-
}, 90_000);
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* health-per-project-stall.test.cjs — per-project problem-count rollup and
|
|
3
|
-
* per-project stall-past-threshold escalation for the scheduler_queue health
|
|
4
|
-
* check. Covers the gap where a project holding ONLY failed/needs_review/
|
|
5
|
-
* quarantined rows (0 running, 0 pending) never tripped evaluateTickLiveness
|
|
6
|
-
* (which requires actual pending work) — the exact way the burrow project's
|
|
7
|
-
* four quarantined PRDs went dark.
|
|
8
|
-
*
|
|
9
|
-
* Run: timeout 120 npx vitest run src/main/__tests__/health-per-project-stall.test.cjs
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
'use strict';
|
|
13
|
-
|
|
14
|
-
// vitest, NOT node:test — see the sibling note in
|
|
15
|
-
// scheduler-stall-per-project.test.cjs. node:assert works inside vitest.
|
|
16
|
-
import { test } from 'vitest';
|
|
17
|
-
const assert = require('node:assert/strict');
|
|
18
|
-
const {
|
|
19
|
-
computeProjectProblemCounts,
|
|
20
|
-
evaluatePerProjectStall,
|
|
21
|
-
TICK_STALL_THRESHOLD_MS,
|
|
22
|
-
} = require('../health.cjs');
|
|
23
|
-
const { computeStallSummary } = require('../scheduler.cjs');
|
|
24
|
-
|
|
25
|
-
test('computeProjectProblemCounts: breaks down failed/needs_review/quarantined by project, ignores healthy statuses', () => {
|
|
26
|
-
const jobs = [
|
|
27
|
-
{ slug: 'a', cwd: '/burrow', status: 'quarantined' },
|
|
28
|
-
{ slug: 'b', cwd: '/burrow', status: 'quarantined' },
|
|
29
|
-
{ slug: 'c', cwd: '/burrow', status: 'quarantined' },
|
|
30
|
-
{ slug: 'd', cwd: '/burrow', status: 'quarantined' },
|
|
31
|
-
{ slug: 'e', cwd: '/other', status: 'failed' },
|
|
32
|
-
{ slug: 'f', cwd: '/other', status: 'needs_review' },
|
|
33
|
-
{ slug: 'g', cwd: '/other', status: 'running' },
|
|
34
|
-
{ slug: 'h', cwd: '/other', status: 'pending' },
|
|
35
|
-
{ slug: 'i', cwd: '/other', status: 'completed' },
|
|
36
|
-
];
|
|
37
|
-
const counts = computeProjectProblemCounts(jobs);
|
|
38
|
-
assert.deepStrictEqual(counts['/burrow'], { failed: 0, needs_review: 0, quarantined: 4 });
|
|
39
|
-
assert.deepStrictEqual(counts['/other'], { failed: 1, needs_review: 1, quarantined: 0 });
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
test('evaluatePerProjectStall: fully-stalled project past threshold flags pastThreshold=true', () => {
|
|
43
|
-
const state = {
|
|
44
|
-
lastRunAt: new Date(Date.now() - (TICK_STALL_THRESHOLD_MS + 60_000)).toISOString(),
|
|
45
|
-
paused: null,
|
|
46
|
-
jobs: [
|
|
47
|
-
{ slug: 'a', cwd: '/burrow', status: 'quarantined' },
|
|
48
|
-
{ slug: 'b', cwd: '/other', status: 'running' },
|
|
49
|
-
],
|
|
50
|
-
invalidJobs: [],
|
|
51
|
-
};
|
|
52
|
-
const summary = computeStallSummary(state);
|
|
53
|
-
const result = evaluatePerProjectStall(summary, state.lastRunAt, Date.now(), TICK_STALL_THRESHOLD_MS);
|
|
54
|
-
assert.strictEqual(result['/burrow'].stalled, true);
|
|
55
|
-
assert.strictEqual(result['/burrow'].pastThreshold, true);
|
|
56
|
-
assert.strictEqual(result['/other'].stalled, false);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test('evaluatePerProjectStall: stalled but NOT yet past threshold flags pastThreshold=false', () => {
|
|
60
|
-
const state = {
|
|
61
|
-
lastRunAt: new Date(Date.now() - 1000).toISOString(), // just ticked
|
|
62
|
-
paused: null,
|
|
63
|
-
jobs: [{ slug: 'a', cwd: '/burrow', status: 'quarantined' }],
|
|
64
|
-
invalidJobs: [],
|
|
65
|
-
};
|
|
66
|
-
const summary = computeStallSummary(state);
|
|
67
|
-
const result = evaluatePerProjectStall(summary, state.lastRunAt, Date.now(), TICK_STALL_THRESHOLD_MS);
|
|
68
|
-
assert.strictEqual(result['/burrow'].stalled, true);
|
|
69
|
-
assert.strictEqual(result['/burrow'].pastThreshold, false);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test('evaluatePerProjectStall: no lastRunAt yet — stalled but not asserted past threshold (caveat, not a false RED)', () => {
|
|
73
|
-
const state = {
|
|
74
|
-
lastRunAt: null,
|
|
75
|
-
paused: null,
|
|
76
|
-
jobs: [{ slug: 'a', cwd: '/burrow', status: 'quarantined' }],
|
|
77
|
-
invalidJobs: [],
|
|
78
|
-
};
|
|
79
|
-
const summary = computeStallSummary(state);
|
|
80
|
-
const result = evaluatePerProjectStall(summary, state.lastRunAt, Date.now(), TICK_STALL_THRESHOLD_MS);
|
|
81
|
-
assert.strictEqual(result['/burrow'].stalled, true);
|
|
82
|
-
assert.strictEqual(result['/burrow'].pastThreshold, false);
|
|
83
|
-
assert.strictEqual(result['/burrow'].caveat, 'no-lastRunAt');
|
|
84
|
-
});
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* health-prd-migration.test.cjs — stranded-PRD-migration health signal
|
|
3
|
-
* (behind two real 2026-07-31 incidents: a single stranded PRD causing an
|
|
4
|
-
* instant ENOENT job failure, and 223 stranded legacy PRDs + a missing
|
|
5
|
-
* history.jsonl resurrecting 189 completed jobs). Exercises
|
|
6
|
-
* evaluatePrdMigrationHealth() directly since it's pure — no need to spawn
|
|
7
|
-
* the full check() (which shells out to npm run typecheck) to cover this.
|
|
8
|
-
*
|
|
9
|
-
* Run: timeout 120 npx vitest run src/main/__tests__/health-prd-migration.test.cjs
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
'use strict';
|
|
13
|
-
|
|
14
|
-
import { test, expect } from 'vitest';
|
|
15
|
-
const { evaluatePrdMigrationHealth } = require('../health.cjs');
|
|
16
|
-
|
|
17
|
-
const LEGACY_DIR = '/home/user/.claude/session-manager/scheduled-plans/prds';
|
|
18
|
-
|
|
19
|
-
test('reports ok when nothing is stranded', () => {
|
|
20
|
-
const result = evaluatePrdMigrationHealth({ moved: 3, skipped: 0, unresolved: [] }, LEGACY_DIR);
|
|
21
|
-
expect(result.ok).toBe(true);
|
|
22
|
-
expect(result.strandedCount).toBe(0);
|
|
23
|
-
expect(result.legacyDir).toBe(LEGACY_DIR);
|
|
24
|
-
expect(result.unresolved).toBeUndefined();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
test('reports RED with count and legacy dir path when files are stranded', () => {
|
|
28
|
-
const unresolved = [
|
|
29
|
-
{ file: '01-stuck.md', reason: 'no cwd in frontmatter' },
|
|
30
|
-
{ file: '02-stuck.md', reason: 'cwd does not exist on disk' },
|
|
31
|
-
];
|
|
32
|
-
const result = evaluatePrdMigrationHealth({ moved: 0, skipped: 0, unresolved }, LEGACY_DIR);
|
|
33
|
-
expect(result.ok).toBe(false);
|
|
34
|
-
expect(result.strandedCount).toBe(2);
|
|
35
|
-
expect(result.legacyDir).toBe(LEGACY_DIR);
|
|
36
|
-
expect(result.unresolved).toEqual(unresolved);
|
|
37
|
-
});
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* health-queue-dispatch.test.cjs — `npm run health` must report non-GREEN
|
|
3
|
-
* when there is at least one DISPATCHABLE pending job (reusing
|
|
4
|
-
* classifyQueueStarvation's own 'starved' verdict, not merely "pending"),
|
|
5
|
-
* nothing running, and no launch in a long time — the exact shape
|
|
6
|
-
* of the 2026-09-11 incident (27 pending across two projects, 0 running, for
|
|
7
|
-
* days, every other health surface reporting green). A dependsOn-blocked
|
|
8
|
-
* queue must NOT trip this the same way: ticking can't fix a blocked chain,
|
|
9
|
-
* so it stays GREEN (reported as 'blocked' instead, distinctly).
|
|
10
|
-
*
|
|
11
|
-
* Exercises evaluateQueueDispatchHealth() directly — pure, no fs — matching
|
|
12
|
-
* every other evaluate* helper's test pattern in this file (see
|
|
13
|
-
* health-usage-poller.test.cjs's header).
|
|
14
|
-
*
|
|
15
|
-
* Run: timeout 120 npx vitest run src/main/__tests__/health-queue-dispatch.test.cjs
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
'use strict';
|
|
19
|
-
|
|
20
|
-
import { test, expect } from 'vitest';
|
|
21
|
-
const { evaluateQueueDispatchHealth, DISPATCH_STALL_THRESHOLD_MS, DISPATCH_WARN_THRESHOLD_MS } = require('../health.cjs');
|
|
22
|
-
|
|
23
|
-
const CWD = '/home/bilko/Projects/starry-night-ships';
|
|
24
|
-
const NOW = Date.parse('2026-09-11T12:00:00.000Z');
|
|
25
|
-
const idleFor = (ms) => new Date(NOW - ms).toISOString();
|
|
26
|
-
|
|
27
|
-
test('a dispatchable pending job, 0 running, stale past the threshold — non-GREEN, starved', () => {
|
|
28
|
-
const state = {
|
|
29
|
-
jobs: [{ slug: 'a', cwd: CWD, status: 'pending', dependsOn: [] }],
|
|
30
|
-
paused: null,
|
|
31
|
-
lastRunAt: idleFor(DISPATCH_STALL_THRESHOLD_MS + 60_000),
|
|
32
|
-
};
|
|
33
|
-
const result = evaluateQueueDispatchHealth(state, 0, NOW);
|
|
34
|
-
expect(result.ok).toBe(false);
|
|
35
|
-
expect(result.starved).toBe(true);
|
|
36
|
-
expect(result.dispatchable).toBe(1);
|
|
37
|
-
expect(result.message).toMatch(/dispatch appears stuck/);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test('every pending row behind a blocked dependsOn chain stays GREEN, reported as blocked', () => {
|
|
41
|
-
const state = {
|
|
42
|
-
jobs: [
|
|
43
|
-
{ slug: 'dead', cwd: CWD, status: 'failed', dependsOn: [] },
|
|
44
|
-
{ slug: 'blocked-child', cwd: CWD, status: 'pending', dependsOn: ['dead'] },
|
|
45
|
-
],
|
|
46
|
-
paused: null,
|
|
47
|
-
lastRunAt: idleFor(DISPATCH_STALL_THRESHOLD_MS + 60_000),
|
|
48
|
-
};
|
|
49
|
-
const result = evaluateQueueDispatchHealth(state, 0, NOW);
|
|
50
|
-
expect(result.ok).toBe(true);
|
|
51
|
-
expect(result.blocked).toBe(true);
|
|
52
|
-
expect(result.starved).toBeUndefined();
|
|
53
|
-
expect(result.message).toMatch(/blocked/);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
test('healthy queue (recent dispatch attempt) is GREEN with no verdict', () => {
|
|
57
|
-
const state = {
|
|
58
|
-
jobs: [{ slug: 'a', cwd: CWD, status: 'pending', dependsOn: [] }],
|
|
59
|
-
paused: null,
|
|
60
|
-
lastRunAt: idleFor(60_000),
|
|
61
|
-
};
|
|
62
|
-
const result = evaluateQueueDispatchHealth(state, 0, NOW);
|
|
63
|
-
expect(result).toEqual({ ok: true });
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test('nothing pending is GREEN regardless of how stale the dispatch timestamp is', () => {
|
|
67
|
-
const state = {
|
|
68
|
-
jobs: [{ slug: 'a', cwd: CWD, status: 'completed' }],
|
|
69
|
-
paused: null,
|
|
70
|
-
lastRunAt: idleFor(DISPATCH_STALL_THRESHOLD_MS + 60_000),
|
|
71
|
-
};
|
|
72
|
-
const result = evaluateQueueDispatchHealth(state, 0, NOW);
|
|
73
|
-
expect(result).toEqual({ ok: true });
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
test('work already running is GREEN — the queue is flowing', () => {
|
|
77
|
-
const state = {
|
|
78
|
-
jobs: [{ slug: 'a', cwd: CWD, status: 'pending', dependsOn: [] }],
|
|
79
|
-
paused: null,
|
|
80
|
-
lastRunAt: idleFor(DISPATCH_STALL_THRESHOLD_MS + 60_000),
|
|
81
|
-
};
|
|
82
|
-
const result = evaluateQueueDispatchHealth(state, 1, NOW);
|
|
83
|
-
expect(result).toEqual({ ok: true });
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
test('the two tiers are exported: 15-minute warn, 2-hour cold report', () => {
|
|
87
|
-
expect(DISPATCH_WARN_THRESHOLD_MS).toBe(15 * 60_000);
|
|
88
|
-
expect(DISPATCH_STALL_THRESHOLD_MS).toBe(2 * 60 * 60_000);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
test('starved between the warn and cold thresholds — ok:true but flagged warn', () => {
|
|
92
|
-
const state = {
|
|
93
|
-
jobs: [{ slug: 'a', cwd: CWD, status: 'pending', dependsOn: [] }],
|
|
94
|
-
paused: null,
|
|
95
|
-
lastRunAt: idleFor(DISPATCH_WARN_THRESHOLD_MS + 60_000),
|
|
96
|
-
};
|
|
97
|
-
const result = evaluateQueueDispatchHealth(state, 0, NOW);
|
|
98
|
-
expect(result.ok).toBe(true);
|
|
99
|
-
expect(result.warn).toBe(true);
|
|
100
|
-
expect(result.starved).toBeUndefined();
|
|
101
|
-
expect(result.dispatchable).toBe(1);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
test('below the warn threshold is plain GREEN', () => {
|
|
105
|
-
const state = {
|
|
106
|
-
jobs: [{ slug: 'a', cwd: CWD, status: 'pending', dependsOn: [] }],
|
|
107
|
-
paused: null,
|
|
108
|
-
lastRunAt: idleFor(DISPATCH_WARN_THRESHOLD_MS - 60_000),
|
|
109
|
-
};
|
|
110
|
-
expect(evaluateQueueDispatchHealth(state, 0, NOW)).toEqual({ ok: true });
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
test('ticks that launch nothing (fresh lastDispatchAttemptAt, stale lastRunAt) still go non-GREEN', () => {
|
|
114
|
-
const state = {
|
|
115
|
-
jobs: [{ slug: 'a', cwd: CWD, status: 'pending', dependsOn: [] }],
|
|
116
|
-
paused: null,
|
|
117
|
-
lastDispatchAttemptAt: idleFor(5_000),
|
|
118
|
-
lastRunAt: idleFor(DISPATCH_STALL_THRESHOLD_MS + 60_000),
|
|
119
|
-
};
|
|
120
|
-
const result = evaluateQueueDispatchHealth(state, 0, NOW);
|
|
121
|
-
expect(result.ok).toBe(false);
|
|
122
|
-
expect(result.starved).toBe(true);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
test('rows held by an open launch breaker read blocked, not starved', () => {
|
|
126
|
-
const state = {
|
|
127
|
-
jobs: [{ slug: 'a', cwd: CWD, status: 'pending', dependsOn: [] }],
|
|
128
|
-
paused: null,
|
|
129
|
-
lastRunAt: idleFor(DISPATCH_STALL_THRESHOLD_MS + 60_000),
|
|
130
|
-
launchBlocks: { default: { kind: 'auth', at: NOW - 1000 } },
|
|
131
|
-
};
|
|
132
|
-
const result = evaluateQueueDispatchHealth(state, 0, NOW);
|
|
133
|
-
expect(result.ok).toBe(true);
|
|
134
|
-
expect(result.blocked).toBe(true);
|
|
135
|
-
});
|