claude-code-session-manager 0.97.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-D93FoaOm.js → DataModel-HdR0AxFq.js} +1 -1
- package/dist/assets/{History-CXxxqNP1.js → History-Dr4Jr_5z.js} +2 -2
- package/dist/assets/{Hooks-CzeYFQjq.js → Hooks-NhrvH3JP.js} +3 -3
- package/dist/assets/{Library-D2zPkwOC.js → Library-Dv_EtIMr.js} +1 -1
- package/dist/assets/{MarkdownEditor-DbinP78A.js → MarkdownEditor-DTKDWV_n.js} +1 -1
- package/dist/assets/{McpServers-Bu_sVGLr.js → McpServers-M034G9B3.js} +2 -2
- package/dist/assets/{Memory-BgiI6LZ_.js → Memory-DzF1B4gx.js} +6 -6
- package/dist/assets/{Permissions-C1MMzZRm.js → Permissions-DrgbSGWl.js} +3 -3
- package/dist/assets/{Plugins-Dhu6wAZi.js → Plugins-Bd3ef35w.js} +2 -2
- package/dist/assets/{ProvenanceBadge-C3AcWKMG.js → ProvenanceBadge-DcBHT1Iy.js} +1 -1
- package/dist/assets/{SaveBar-plwYm0iO.js → SaveBar-rimJem-Y.js} +1 -1
- package/dist/assets/Scheduler-DjgFJl2w.js +16 -0
- package/dist/assets/{ScopeSwitcher-DFy0vv_o.js → ScopeSwitcher-CnAzTBrl.js} +1 -1
- package/dist/assets/Settings-Dc1F_sSg.js +3 -0
- package/dist/assets/{SkillReferenceGraph-DsI7hRPB.js → SkillReferenceGraph-B9aZDecM.js} +1 -1
- package/dist/assets/{Skills-3E748UfB.js → Skills-DRXd18gu.js} +2 -2
- package/dist/assets/{SystemPrompt-BqmfUJOd.js → SystemPrompt-ahy5-Fwe.js} +1 -1
- package/dist/assets/{TagLibrary-CPz6fO8Q.js → TagLibrary-C_tToDeL.js} +1 -1
- package/dist/assets/{TiptapBody-D9EoBQ1P.js → TiptapBody-DvdefS6K.js} +1 -1
- package/dist/assets/{Toggle-BHAmZNDn.js → Toggle-6Jl5Njc6.js} +1 -1
- package/dist/assets/{index-CKH5Uxik.css → index-Bta-hwud.css} +1 -1
- package/dist/assets/{index-tn5JIVPj.js → index-vx8O73l8.js} +497 -502
- package/dist/assets/{settingsSchema-Crqy_aNz.js → settingsSchema-D6iRguNV.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +26 -29
- package/plugins/CLAUDE.md +6 -6
- package/plugins/session-manager-dev/skills/builder/4-manual/SKILL.md +23 -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 +153 -19
- 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 +33 -30
- package/src/main/lib/workTypeLibrary.cjs +7 -1
- package/src/main/promptSessionEvents.cjs +37 -13
- package/src/main/queueOps.cjs +17 -6
- package/src/main/scheduler.cjs +975 -203
- 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-nESrTRIg.js +0 -1
- package/dist/assets/Scheduler-BVYTeh39.js +0 -16
- package/dist/assets/Settings-DIhGgdfs.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__/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 -154
- 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-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 -375
- package/src/main/lib/__tests__/watchdog-relaunch.test.cjs +0 -266
- package/src/main/lib/kgExchangePairing.cjs +0 -75
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* opsErrorLog.test.cjs — appendError() is the sole writer of the `logs` ops
|
|
3
|
-
* namespace (single-writer law, opsOwnership.cjs). Verifies: writes a tagged
|
|
4
|
-
* JSONL line to <cwd>/session-manager-operations/logs/errors-<date>.jsonl,
|
|
5
|
-
* redacts sensitive meta keys, and no-ops (never throws) with no cwd.
|
|
6
|
-
*
|
|
7
|
-
* Run: timeout 120 npx vitest run src/main/__tests__/opsErrorLog.test.cjs
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
'use strict';
|
|
11
|
-
|
|
12
|
-
import { test } from 'vitest';
|
|
13
|
-
import assert from 'node:assert/strict';
|
|
14
|
-
const fs = require('node:fs');
|
|
15
|
-
const os = require('node:os');
|
|
16
|
-
const path = require('node:path');
|
|
17
|
-
|
|
18
|
-
const opsErrorLog = require('../lib/opsErrorLog.cjs');
|
|
19
|
-
|
|
20
|
-
function mkTmpProject() {
|
|
21
|
-
return fs.mkdtempSync(path.join(os.tmpdir(), 'sm-opslog-test-'));
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function readLines(cwd) {
|
|
25
|
-
const dir = opsErrorLog.logsDir(cwd);
|
|
26
|
-
const file = opsErrorLog.todayFile(cwd);
|
|
27
|
-
if (!fs.existsSync(file)) return [];
|
|
28
|
-
return fs.readFileSync(file, 'utf8').trim().split('\n').filter(Boolean).map((l) => JSON.parse(l));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
test('appendError writes a tagged JSONL line', () => {
|
|
32
|
-
const cwd = mkTmpProject();
|
|
33
|
-
opsErrorLog.appendError({
|
|
34
|
-
cwd,
|
|
35
|
-
scope: 'pty',
|
|
36
|
-
tabId: 'tab-123',
|
|
37
|
-
tags: ['spawn-failed'],
|
|
38
|
-
message: 'pty.spawn threw: boom',
|
|
39
|
-
});
|
|
40
|
-
const lines = readLines(cwd);
|
|
41
|
-
assert.equal(lines.length, 1);
|
|
42
|
-
const [line] = lines;
|
|
43
|
-
assert.equal(line.level, 'error');
|
|
44
|
-
assert.equal(line.scope, 'pty');
|
|
45
|
-
assert.equal(line.tabId, 'tab-123');
|
|
46
|
-
assert.equal(line.message, 'pty.spawn threw: boom');
|
|
47
|
-
assert.ok(line.tags.includes('level:error'));
|
|
48
|
-
assert.ok(line.tags.includes('scope:pty'));
|
|
49
|
-
assert.ok(line.tags.includes('tab:tab-123'));
|
|
50
|
-
assert.ok(line.tags.includes('spawn-failed'));
|
|
51
|
-
assert.ok(typeof line.ts === 'string' && !Number.isNaN(Date.parse(line.ts)));
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test('appendError redacts sensitive meta keys', () => {
|
|
55
|
-
const cwd = mkTmpProject();
|
|
56
|
-
opsErrorLog.appendError({
|
|
57
|
-
cwd,
|
|
58
|
-
scope: 'chatRunner',
|
|
59
|
-
message: 'run failed',
|
|
60
|
-
meta: { token: 'sk-secret', sessionId: 'abc', password: 'hunter2' },
|
|
61
|
-
});
|
|
62
|
-
const [line] = readLines(cwd);
|
|
63
|
-
assert.equal(line.meta.token, '[redacted]');
|
|
64
|
-
assert.equal(line.meta.password, '[redacted]');
|
|
65
|
-
assert.equal(line.meta.sessionId, 'abc');
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test('appendError is a no-op with no cwd', () => {
|
|
69
|
-
// Must not throw, and there is nowhere to assert a file was NOT written
|
|
70
|
-
// since no cwd means no path — this just proves it never throws.
|
|
71
|
-
assert.doesNotThrow(() => opsErrorLog.appendError({ scope: 'x', message: 'y' }));
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
test('appendError merges caller tags without duplicating auto-derived ones', () => {
|
|
75
|
-
const cwd = mkTmpProject();
|
|
76
|
-
opsErrorLog.appendError({
|
|
77
|
-
cwd,
|
|
78
|
-
scope: 'chatRunner',
|
|
79
|
-
tabId: 'tab-9',
|
|
80
|
-
tags: ['level:error', 'silent-probe'], // deliberately duplicates an auto tag
|
|
81
|
-
message: 'probe failed',
|
|
82
|
-
});
|
|
83
|
-
const [line] = readLines(cwd);
|
|
84
|
-
const count = line.tags.filter((t) => t === 'level:error').length;
|
|
85
|
-
assert.equal(count, 1);
|
|
86
|
-
assert.ok(line.tags.includes('silent-probe'));
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
test('under vitest, a real project root is refused: no file written, no telemetry', () => {
|
|
90
|
-
const repoRoot = path.resolve(__dirname, '..', '..', '..');
|
|
91
|
-
const logsBefore = fs.existsSync(path.join(repoRoot, 'session-manager-operations', 'logs'))
|
|
92
|
-
? fs.readdirSync(path.join(repoRoot, 'session-manager-operations', 'logs')).map((f) => [f, fs.statSync(path.join(repoRoot, 'session-manager-operations', 'logs', f)).mtimeMs])
|
|
93
|
-
: null;
|
|
94
|
-
const telemetryPath = require.resolve('../lib/telemetryClient.cjs');
|
|
95
|
-
const saved = require.cache[telemetryPath];
|
|
96
|
-
let calls = 0;
|
|
97
|
-
require.cache[telemetryPath] = { id: telemetryPath, filename: telemetryPath, loaded: true, exports: { reportError: () => { calls++; }, logLine: () => { calls++; } } };
|
|
98
|
-
try {
|
|
99
|
-
opsErrorLog.appendError({ cwd: repoRoot, scope: 'chatRunner', tabId: 'T', message: 'fixture' });
|
|
100
|
-
opsErrorLog.appendError({ cwd: repoRoot, scope: 'chatRunner', level: 'warn', message: 'fixture' });
|
|
101
|
-
} finally {
|
|
102
|
-
if (saved) require.cache[telemetryPath] = saved; else delete require.cache[telemetryPath];
|
|
103
|
-
}
|
|
104
|
-
const logsAfter = fs.existsSync(path.join(repoRoot, 'session-manager-operations', 'logs'))
|
|
105
|
-
? fs.readdirSync(path.join(repoRoot, 'session-manager-operations', 'logs')).map((f) => [f, fs.statSync(path.join(repoRoot, 'session-manager-operations', 'logs', f)).mtimeMs])
|
|
106
|
-
: null;
|
|
107
|
-
assert.deepEqual(logsAfter, logsBefore);
|
|
108
|
-
assert.equal(calls, 0);
|
|
109
|
-
});
|
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* opsErrorLogTelemetryTap.test.cjs — unit tests for appendError()'s telemetry
|
|
3
|
-
* mirror. Kept separate from opsErrorLog.test.cjs (whose pre-existing tests
|
|
4
|
-
* stay unmodified in intent) since this is new behavior this PRD adds.
|
|
5
|
-
*
|
|
6
|
-
* Run: timeout 120 npx vitest run src/main/__tests__/opsErrorLogTelemetryTap.test.cjs
|
|
7
|
-
*/
|
|
8
|
-
'use strict';
|
|
9
|
-
|
|
10
|
-
import { test, expect, afterEach } from 'vitest';
|
|
11
|
-
const fs = require('node:fs');
|
|
12
|
-
const fsp = require('node:fs/promises');
|
|
13
|
-
const os = require('node:os');
|
|
14
|
-
const path = require('node:path');
|
|
15
|
-
|
|
16
|
-
const opsErrorLog = require('../lib/opsErrorLog.cjs');
|
|
17
|
-
|
|
18
|
-
const tmpDirs = [];
|
|
19
|
-
let originalHome;
|
|
20
|
-
let originalSmTelemetrySpool;
|
|
21
|
-
let originalWorktreeRoot;
|
|
22
|
-
|
|
23
|
-
afterEach(async () => {
|
|
24
|
-
if (originalHome !== undefined) process.env.HOME = originalHome;
|
|
25
|
-
if (originalSmTelemetrySpool === undefined) delete process.env.SM_TELEMETRY_SPOOL; else process.env.SM_TELEMETRY_SPOOL = originalSmTelemetrySpool;
|
|
26
|
-
if (originalWorktreeRoot !== undefined) {
|
|
27
|
-
if (originalWorktreeRoot === null) delete process.env.SM_WORKTREE_ROOT; else process.env.SM_WORKTREE_ROOT = originalWorktreeRoot;
|
|
28
|
-
originalWorktreeRoot = undefined;
|
|
29
|
-
}
|
|
30
|
-
const telemetryPath = require.resolve('../lib/telemetryClient.cjs');
|
|
31
|
-
delete require.cache[telemetryPath];
|
|
32
|
-
const activeSessionsPath = require.resolve('../lib/activeSessions.cjs');
|
|
33
|
-
delete require.cache[activeSessionsPath];
|
|
34
|
-
while (tmpDirs.length) {
|
|
35
|
-
const d = tmpDirs.pop();
|
|
36
|
-
await fsp.rm(d, { recursive: true, force: true });
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
function mkTmpProject() {
|
|
41
|
-
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'sm-opslog-telemetry-'));
|
|
42
|
-
tmpDirs.push(dir);
|
|
43
|
-
return dir;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function readLines(cwd) {
|
|
47
|
-
const file = opsErrorLog.todayFile(cwd);
|
|
48
|
-
if (!fs.existsSync(file)) return [];
|
|
49
|
-
return fs.readFileSync(file, 'utf8').trim().split('\n').filter(Boolean).map((l) => JSON.parse(l));
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function stubTelemetryClient(exportsObj) {
|
|
53
|
-
const telemetryPath = require.resolve('../lib/telemetryClient.cjs');
|
|
54
|
-
require.cache[telemetryPath] = { id: telemetryPath, filename: telemetryPath, loaded: true, exports: exportsObj };
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// ─── AFTER local write, never blocks it ──────────────────────────────────
|
|
58
|
-
|
|
59
|
-
test('a throwing telemetry stub never prevents or corrupts the local JSONL write', () => {
|
|
60
|
-
const cwd = mkTmpProject();
|
|
61
|
-
stubTelemetryClient({
|
|
62
|
-
reportError: () => { throw new Error('telemetry boom'); },
|
|
63
|
-
logLine: () => { throw new Error('telemetry boom'); },
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
expect(() => opsErrorLog.appendError({ cwd, scope: 'pty', message: 'boom happened' })).not.toThrow();
|
|
67
|
-
|
|
68
|
-
const lines = readLines(cwd);
|
|
69
|
-
expect(lines.length).toBe(1);
|
|
70
|
-
expect(lines[0].message).toBe('boom happened');
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test('level "warn" routes to telemetryClient.logLine, default "error" routes to reportError', () => {
|
|
74
|
-
const cwd = mkTmpProject();
|
|
75
|
-
const reportErrorCalls = [];
|
|
76
|
-
const logLineCalls = [];
|
|
77
|
-
stubTelemetryClient({
|
|
78
|
-
reportError: (o) => reportErrorCalls.push(o),
|
|
79
|
-
logLine: (o) => logLineCalls.push(o),
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
opsErrorLog.appendError({ cwd, scope: 'chatRunner', level: 'warn', message: 'careful' });
|
|
83
|
-
expect(logLineCalls).toHaveLength(1);
|
|
84
|
-
expect(reportErrorCalls).toHaveLength(0);
|
|
85
|
-
|
|
86
|
-
opsErrorLog.appendError({ cwd, scope: 'chatRunner', message: 'broke' });
|
|
87
|
-
expect(reportErrorCalls).toHaveLength(1);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
// ─── ephemeral cwd: local refused, telemetry still fires ─────────────────
|
|
91
|
-
|
|
92
|
-
test('an ephemeral cwd yields zero local lines but one telemetry record, with a normalized projectHash', () => {
|
|
93
|
-
// Why this test depends on the process tmpdir: ephemeral classification
|
|
94
|
-
// (cwdClassify.classifyCwd, via ephemeralCwd.isEphemeralCwd) is an exact match
|
|
95
|
-
// on os.tmpdir() plus a prefix match on the managed job/epic worktree roots,
|
|
96
|
-
// and those roots are <SM_WORKTREE_ROOT || os.tmpdir()>/session-manager-*-worktrees
|
|
97
|
-
// (schedulerPaths.worktreeRoot). The sandbox globalSetup mkdtemp's that base under
|
|
98
|
-
// the tmpdir it saw at startup, so a relocated TMPDIR / prior env mutation changes
|
|
99
|
-
// where KIND_CONFIG.epic.root points. Pin SM_WORKTREE_ROOT to a fixture dir this
|
|
100
|
-
// test owns and derive the cwd from KIND_CONFIG, so expected and actual roots
|
|
101
|
-
// resolve through the same code path regardless of TMPDIR.
|
|
102
|
-
originalWorktreeRoot = process.env.SM_WORKTREE_ROOT === undefined ? null : process.env.SM_WORKTREE_ROOT;
|
|
103
|
-
process.env.SM_WORKTREE_ROOT = mkTmpProject();
|
|
104
|
-
const { KIND_CONFIG } = require('../lib/gitWorktree.cjs');
|
|
105
|
-
const worktreeCwd = path.join(KIND_CONFIG.epic.root, 'fakehash', 'fake-epic-id');
|
|
106
|
-
const realRoot = '/home/bilko/Projects/real-project';
|
|
107
|
-
|
|
108
|
-
const activeSessionsPath = require.resolve('../lib/activeSessions.cjs');
|
|
109
|
-
const original = require('../lib/activeSessions.cjs');
|
|
110
|
-
require.cache[activeSessionsPath] = {
|
|
111
|
-
id: activeSessionsPath,
|
|
112
|
-
filename: activeSessionsPath,
|
|
113
|
-
loaded: true,
|
|
114
|
-
exports: { ...original, projectRootOf: (p) => (p === worktreeCwd ? realRoot : p) },
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
const reportErrorCalls = [];
|
|
118
|
-
stubTelemetryClient({ reportError: (o) => reportErrorCalls.push(o), logLine: () => {} });
|
|
119
|
-
|
|
120
|
-
opsErrorLog.appendError({ cwd: worktreeCwd, scope: 'chatRunner', message: 'worktree err' });
|
|
121
|
-
|
|
122
|
-
// resolveProjectRoot now consumes cwdClassify (not the stubbed projectRootOf):
|
|
123
|
-
// an ephemeral worktree cwd is refused outright, so no local path is even built.
|
|
124
|
-
expect(() => opsErrorLog.todayFile(worktreeCwd)).toThrow(/ephemeral/);
|
|
125
|
-
expect(reportErrorCalls).toHaveLength(1);
|
|
126
|
-
expect(reportErrorCalls[0].context.cwd).toBe(realRoot);
|
|
127
|
-
expect(reportErrorCalls[0].context.cwd).not.toBe(worktreeCwd);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
// ─── PII: no prompt text, no absolute path ────────────────────────────────
|
|
131
|
-
|
|
132
|
-
test('a realistic error with an absolute path and a prompt-like string never reaches the telemetry payload verbatim', async () => {
|
|
133
|
-
originalHome = process.env.HOME;
|
|
134
|
-
originalSmTelemetrySpool = process.env.SM_TELEMETRY_SPOOL;
|
|
135
|
-
const home = fs.mkdtempSync(path.join(os.tmpdir(), 'sm-opslog-telemetry-home-'));
|
|
136
|
-
tmpDirs.push(home);
|
|
137
|
-
process.env.HOME = home;
|
|
138
|
-
// Explicit opt-in: overrides the test-environment no-op guard so this
|
|
139
|
-
// test's real telemetryClient call actually persists into an isolated dir.
|
|
140
|
-
process.env.SM_TELEMETRY_SPOOL = path.join(home, '.claude', 'session-manager');
|
|
141
|
-
|
|
142
|
-
for (const p of ['../lib/telemetryClient.cjs', '../config.cjs', '../lib/telemetrySettings.cjs', '../lib/machineProfile.cjs']) {
|
|
143
|
-
const resolved = require.resolve(p);
|
|
144
|
-
delete require.cache[resolved];
|
|
145
|
-
}
|
|
146
|
-
const client = require('../lib/telemetryClient.cjs');
|
|
147
|
-
client._setMachineProfileBuilder(async () => ({
|
|
148
|
-
appVersion: '0.83.0', platform: 'linux', arch: 'x64', machineDigest: 'deadbeefcafe',
|
|
149
|
-
}));
|
|
150
|
-
|
|
151
|
-
const cwd = mkTmpProject();
|
|
152
|
-
const absPath = `${home}/Projects/super-secret-client/src/main/index.cjs`;
|
|
153
|
-
const promptLike = 'write me a function that deletes all files matching *.secret and email me the results';
|
|
154
|
-
opsErrorLog.appendError({
|
|
155
|
-
cwd,
|
|
156
|
-
scope: 'chatRunner',
|
|
157
|
-
message: `run failed while processing "${promptLike}" at ${absPath}`,
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
// The telemetry write is fire-and-forget async; poll (bounded) for the queue
|
|
161
|
-
// file instead of a fixed sleep, which raced under load.
|
|
162
|
-
const deadline = Date.now() + 5000;
|
|
163
|
-
while (!fs.existsSync(client.queuePath()) && Date.now() < deadline) {
|
|
164
|
-
await new Promise((r) => setTimeout(r, 25));
|
|
165
|
-
}
|
|
166
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
167
|
-
|
|
168
|
-
const raw = await fsp.readFile(client.queuePath(), 'utf8');
|
|
169
|
-
expect(raw).not.toContain(home);
|
|
170
|
-
expect(raw).not.toContain(absPath);
|
|
171
|
-
expect(raw).not.toContain(promptLike);
|
|
172
|
-
client.shutdown();
|
|
173
|
-
});
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* personaMerge.test.cjs — overlay-over-global persona MERGE (lib/personaMerge.cjs)
|
|
3
|
-
* and every reader that goes through it.
|
|
4
|
-
*
|
|
5
|
-
* Run: timeout 120 npx vitest run src/main/__tests__/personaMerge.test.cjs
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
'use strict';
|
|
9
|
-
|
|
10
|
-
import { test, expect, afterEach } from 'vitest';
|
|
11
|
-
const fs = require('node:fs');
|
|
12
|
-
const fsp = require('node:fs/promises');
|
|
13
|
-
const os = require('node:os');
|
|
14
|
-
const path = require('node:path');
|
|
15
|
-
const { resolveMergedPersona } = require('../lib/personaMerge.cjs');
|
|
16
|
-
const { readOverlayAwarePersonaModel, resolvePrdPersonaForSpawn } = require('../lib/agentModelResolve.cjs');
|
|
17
|
-
const { resolveEpicEffort } = require('../lib/agentEffortResolve.cjs');
|
|
18
|
-
const { resolveEffectiveModelInfo } = require('../lib/effectiveModelInfo.cjs');
|
|
19
|
-
const { getPersonaBody, listPersonas } = require('../agentLibrary.cjs');
|
|
20
|
-
|
|
21
|
-
const tmpDirs = [];
|
|
22
|
-
afterEach(async () => {
|
|
23
|
-
while (tmpDirs.length) await fsp.rm(tmpDirs.pop(), { recursive: true, force: true });
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
function mk(prefix) {
|
|
27
|
-
const d = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
|
28
|
-
tmpDirs.push(d);
|
|
29
|
-
return d;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const noop = (p) => p;
|
|
33
|
-
|
|
34
|
-
function persona(dir, name, fmLines, body) {
|
|
35
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
36
|
-
fs.writeFileSync(path.join(dir, `${name}.md`), ['---', ...fmLines, '---', body].join('\n'));
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function setup({ global, overlay }) {
|
|
40
|
-
const cwd = mk('sm-pm-cwd-');
|
|
41
|
-
const globalDir = mk('sm-pm-global-');
|
|
42
|
-
if (global) persona(globalDir, 'dev-lead', global.fm, global.body);
|
|
43
|
-
if (overlay) persona(path.join(cwd, '.claude', 'agents'), 'dev-lead', overlay.fm, overlay.body);
|
|
44
|
-
return { cwd, globalDir, deps: { globalDir, validatePath: noop } };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const GLOBAL = { fm: ['name: dev-lead', 'description: d', 'model: sonnet', 'effort: low'], body: 'Global body.' };
|
|
48
|
-
|
|
49
|
-
test('frontmatter-only overlay overrides model', () => {
|
|
50
|
-
const { cwd, deps } = setup({ global: GLOBAL, overlay: { fm: ['model: claude-opus-4-6'], body: '' } });
|
|
51
|
-
expect(readOverlayAwarePersonaModel('dev-lead', { ...deps, cwd })).toBe('claude-opus-4-6');
|
|
52
|
-
const m = resolveMergedPersona(cwd, 'dev-lead', deps);
|
|
53
|
-
expect(m.provenance.model).toBe('overlay');
|
|
54
|
-
expect(m.provenance.description).toBe('global');
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
test('frontmatter-only overlay overrides effort; source reports persona-overlay', () => {
|
|
58
|
-
const { cwd, deps } = setup({ global: GLOBAL, overlay: { fm: ['effort: high'], body: '' } });
|
|
59
|
-
expect(resolveEpicEffort({ cwd, agentType: 'dev-lead', deps })).toEqual({ effort: 'high', source: 'persona-overlay' });
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
test('a key absent from the overlay falls back to the global (model stays global, effort from overlay)', () => {
|
|
63
|
-
const { cwd, deps } = setup({ global: GLOBAL, overlay: { fm: ['effort: high'], body: '' } });
|
|
64
|
-
expect(readOverlayAwarePersonaModel('dev-lead', { ...deps, cwd })).toBe('sonnet');
|
|
65
|
-
// and the reverse: effort absent from overlay -> global effort, sourced 'persona'
|
|
66
|
-
const s2 = setup({ global: GLOBAL, overlay: { fm: ['model: opus'], body: '' } });
|
|
67
|
-
expect(resolveEpicEffort({ cwd: s2.cwd, agentType: 'dev-lead', deps: s2.deps })).toEqual({ effort: 'low', source: 'persona' });
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
test('an overlay with no body uses the global body', () => {
|
|
71
|
-
const { cwd, deps } = setup({ global: GLOBAL, overlay: { fm: ['model: opus'], body: '\n \n' } });
|
|
72
|
-
const m = resolveMergedPersona(cwd, 'dev-lead', deps);
|
|
73
|
-
expect(m.body.trim()).toBe('Global body.');
|
|
74
|
-
expect(m.bodySource).toBe('global');
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
test('a full-body overlay replaces the body wholesale and keeps its raw text byte-identical', () => {
|
|
78
|
-
const { cwd, deps } = setup({ global: GLOBAL, overlay: { fm: ['name: dev-lead', 'model: haiku'], body: 'Overlay body.' } });
|
|
79
|
-
const m = resolveMergedPersona(cwd, 'dev-lead', deps);
|
|
80
|
-
expect(m.body.trim()).toBe('Overlay body.');
|
|
81
|
-
expect(m.bodySource).toBe('overlay');
|
|
82
|
-
expect(m.text).toBe(fs.readFileSync(path.join(cwd, '.claude', 'agents', 'dev-lead.md'), 'utf8'));
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
test("this repo's three full-body overlays resolve byte-identically (text + body + path)", async () => {
|
|
86
|
-
const repoAgents = path.resolve(__dirname, '..', '..', '..', '.claude', 'agents');
|
|
87
|
-
const globalDir = mk('sm-pm-global-');
|
|
88
|
-
for (const name of ['bilko-host-publisher', 'builder', 'project-home-builder']) {
|
|
89
|
-
persona(globalDir, name, [`name: ${name}`, 'description: g', 'model: opus'], 'global body');
|
|
90
|
-
const overlayFile = path.join(repoAgents, `${name}.md`);
|
|
91
|
-
const raw = fs.readFileSync(overlayFile, 'utf8');
|
|
92
|
-
const m = resolveMergedPersona(path.resolve(repoAgents, '..', '..'), name, { globalDir, validatePath: noop });
|
|
93
|
-
expect(m.text).toBe(raw);
|
|
94
|
-
expect(m.path).toBe(overlayFile);
|
|
95
|
-
expect(m.body).toBe(require('../lib/prdFrontmatter.cjs').splitFrontmatter(raw).body);
|
|
96
|
-
const viaLib = await getPersonaBody({ cwd: path.resolve(repoAgents, '..', '..'), name, globalDir, validatePath: noop });
|
|
97
|
-
expect(viaLib.text).toBe(raw);
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
test('malformed overlay frontmatter degrades to the global persona, never throws, and is reported', () => {
|
|
102
|
-
const { cwd, globalDir } = setup({ global: GLOBAL });
|
|
103
|
-
fs.mkdirSync(path.join(cwd, '.claude', 'agents'), { recursive: true });
|
|
104
|
-
fs.writeFileSync(path.join(cwd, '.claude', 'agents', 'dev-lead.md'), '---\nmodel: claude-opus-4-6\nno closing fence');
|
|
105
|
-
const deps = { globalDir, validatePath: noop };
|
|
106
|
-
const m = resolveMergedPersona(cwd, 'dev-lead', deps);
|
|
107
|
-
expect(m.fm.model).toBe('sonnet');
|
|
108
|
-
expect(m.fromOverlay).toBe(false);
|
|
109
|
-
expect(m.overlayIssue).toMatch(/malformed/);
|
|
110
|
-
expect(readOverlayAwarePersonaModel('dev-lead', { ...deps, cwd })).toBe('sonnet');
|
|
111
|
-
const { todayFile } = require('../lib/opsErrorLog.cjs');
|
|
112
|
-
expect(fs.readFileSync(todayFile(cwd), 'utf8')).toMatch(/malformed frontmatter/);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
test('an overlay with no global counterpart works standalone', () => {
|
|
116
|
-
const { cwd, deps } = setup({ overlay: { fm: ['name: dev-lead', 'model: opus'], body: 'Solo body.' } });
|
|
117
|
-
const m = resolveMergedPersona(cwd, 'dev-lead', deps);
|
|
118
|
-
expect(m.body.trim()).toBe('Solo body.');
|
|
119
|
-
expect(m.globalPath).toBeNull();
|
|
120
|
-
expect(readOverlayAwarePersonaModel('dev-lead', { ...deps, cwd })).toBe('opus');
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
test("overlay `model: inherit` defers to the settings default — it does NOT fall back to the global model", async () => {
|
|
124
|
-
const { cwd, deps } = setup({ global: GLOBAL, overlay: { fm: ['model: inherit'], body: '' } });
|
|
125
|
-
expect(readOverlayAwarePersonaModel('dev-lead', { ...deps, cwd })).toBe('inherit');
|
|
126
|
-
const r = await resolvePrdPersonaForSpawn({ cwd, agentType: 'dev-lead', fallbackModel: 'sonnet-fallback', deps: { getPersonaBody: (a) => getPersonaBody({ ...a, ...deps }) } });
|
|
127
|
-
expect(r.model).toBe('sonnet-fallback');
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
test('resolvePrdPersonaForSpawn hands the executor the merged model AND the global body for a frontmatter-only overlay', async () => {
|
|
131
|
-
const { cwd, deps } = setup({ global: GLOBAL, overlay: { fm: ['model: claude-opus-4-6'], body: '' } });
|
|
132
|
-
const r = await resolvePrdPersonaForSpawn({ cwd, agentType: 'dev-lead', deps: { getPersonaBody: (a) => getPersonaBody({ ...a, ...deps }) } });
|
|
133
|
-
expect(r.model).toBe('claude-opus-4-6');
|
|
134
|
-
expect(r.systemPrompt).toBe('Global body.');
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
test('a crafted agentType is rejected by every reader (PERSONA_NAME_RE traversal guard)', async () => {
|
|
138
|
-
const { cwd, deps } = setup({ global: GLOBAL });
|
|
139
|
-
const evil = '../../other-project/CLAUDE';
|
|
140
|
-
expect(resolveMergedPersona(cwd, evil, deps)).toBeNull();
|
|
141
|
-
expect(readOverlayAwarePersonaModel(evil, { ...deps, cwd })).toBeNull();
|
|
142
|
-
expect(await getPersonaBody({ cwd, name: evil, ...deps })).toBeNull();
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
test('getPersonaBody / effectiveModelInfo / listPersonas report per-field provenance, not a whole-file boolean', async () => {
|
|
146
|
-
const { cwd, globalDir, deps } = setup({ global: GLOBAL, overlay: { fm: ['model: claude-opus-4-6'], body: '' } });
|
|
147
|
-
const body = await getPersonaBody({ cwd, name: 'dev-lead', ...deps });
|
|
148
|
-
expect(body.provenance.model).toBe('overlay');
|
|
149
|
-
expect(body.provenance.effort).toBe('global');
|
|
150
|
-
|
|
151
|
-
const info = await resolveEffectiveModelInfo({
|
|
152
|
-
cwd,
|
|
153
|
-
agentType: 'dev-lead',
|
|
154
|
-
deps: {
|
|
155
|
-
...deps,
|
|
156
|
-
activeSessions: { projectRootOf: (p) => p },
|
|
157
|
-
getPersonaBody: (a) => getPersonaBody({ ...a, ...deps }),
|
|
158
|
-
},
|
|
159
|
-
});
|
|
160
|
-
expect(info.modelAlias).toBe('claude-opus-4-6');
|
|
161
|
-
expect(info.modelSource).toBe('persona-overlay');
|
|
162
|
-
expect(info.personaProvenance.effort).toBe('global');
|
|
163
|
-
expect(info.personaEffortSource).toBe('persona');
|
|
164
|
-
|
|
165
|
-
const list = await listPersonas({ globalDir, validatePath: noop, loadSessions: async () => ({ tabs: [{ cwd }] }) });
|
|
166
|
-
const dev = list.find((p) => p.name === 'dev-lead');
|
|
167
|
-
expect(dev.overridingProjects).toEqual([path.basename(cwd)]);
|
|
168
|
-
expect(dev.overrideDetails[0]).toMatchObject({ fields: ['model'], bodyOverridden: false, issue: null });
|
|
169
|
-
});
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* planValidator.test.cjs — unit tests for hasDownstreamValidator (PRD 1408).
|
|
3
|
-
*
|
|
4
|
-
* Run: timeout 300 npx vitest run src/main/__tests__/planValidator.test.cjs
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
'use strict';
|
|
8
|
-
|
|
9
|
-
import { test, expect } from 'vitest';
|
|
10
|
-
const { hasDownstreamValidator } = require('../lib/planValidator.cjs');
|
|
11
|
-
|
|
12
|
-
test('true when a pending validator row depends on the job', () => {
|
|
13
|
-
const job = { slug: '111-alpha' };
|
|
14
|
-
const jobs = [
|
|
15
|
-
job,
|
|
16
|
-
{ slug: '999-validate', agentType: 'validator', status: 'pending', dependsOn: ['111-alpha'] },
|
|
17
|
-
];
|
|
18
|
-
expect(hasDownstreamValidator(job, jobs)).toBe(true);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
test('true when the depending validator row is running', () => {
|
|
22
|
-
const job = { slug: '111-alpha' };
|
|
23
|
-
const jobs = [
|
|
24
|
-
job,
|
|
25
|
-
{ slug: '999-validate', agentType: 'validator', status: 'running', dependsOn: ['111-alpha'] },
|
|
26
|
-
];
|
|
27
|
-
expect(hasDownstreamValidator(job, jobs)).toBe(true);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
test('false when the job itself is a validator', () => {
|
|
31
|
-
const job = { slug: '999-validate', agentType: 'validator' };
|
|
32
|
-
const jobs = [
|
|
33
|
-
job,
|
|
34
|
-
{ slug: '998-other-validate', agentType: 'validator', status: 'pending', dependsOn: ['999-validate'] },
|
|
35
|
-
];
|
|
36
|
-
expect(hasDownstreamValidator(job, jobs)).toBe(false);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
test('false for null/undefined job or jobs', () => {
|
|
40
|
-
expect(hasDownstreamValidator(null, [{ slug: 'x' }])).toBe(false);
|
|
41
|
-
expect(hasDownstreamValidator(undefined, [{ slug: 'x' }])).toBe(false);
|
|
42
|
-
expect(hasDownstreamValidator({ slug: '111-alpha' }, null)).toBe(false);
|
|
43
|
-
expect(hasDownstreamValidator({ slug: '111-alpha' }, undefined)).toBe(false);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
test('false when the validator row does not depend on this job', () => {
|
|
47
|
-
const job = { slug: '111-alpha' };
|
|
48
|
-
const jobs = [
|
|
49
|
-
job,
|
|
50
|
-
{ slug: '999-validate', agentType: 'validator', status: 'pending', dependsOn: ['222-beta'] },
|
|
51
|
-
];
|
|
52
|
-
expect(hasDownstreamValidator(job, jobs)).toBe(false);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test('false when the depending row is a completed validator', () => {
|
|
56
|
-
const job = { slug: '111-alpha' };
|
|
57
|
-
const jobs = [
|
|
58
|
-
job,
|
|
59
|
-
{ slug: '999-validate', agentType: 'validator', status: 'completed', dependsOn: ['111-alpha'] },
|
|
60
|
-
];
|
|
61
|
-
expect(hasDownstreamValidator(job, jobs)).toBe(false);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
test('false when the depending row is not agentType validator', () => {
|
|
65
|
-
const job = { slug: '111-alpha' };
|
|
66
|
-
const jobs = [
|
|
67
|
-
job,
|
|
68
|
-
{ slug: '999-other', agentType: 'dev-lead', status: 'pending', dependsOn: ['111-alpha'] },
|
|
69
|
-
];
|
|
70
|
-
expect(hasDownstreamValidator(job, jobs)).toBe(false);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test('true when a pending validator reaches the job transitively via one sink (2-hop)', () => {
|
|
74
|
-
const job = { slug: '111-alpha' };
|
|
75
|
-
const jobs = [
|
|
76
|
-
job,
|
|
77
|
-
{ slug: '150-sink', agentType: 'dev-lead', status: 'pending', dependsOn: ['111-alpha'] },
|
|
78
|
-
{ slug: '999-validate', agentType: 'validator', status: 'pending', dependsOn: ['150-sink'] },
|
|
79
|
-
];
|
|
80
|
-
expect(hasDownstreamValidator(job, jobs)).toBe(true);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
test('true when a pending validator reaches the job transitively via two intermediate sinks (3-hop)', () => {
|
|
84
|
-
const job = { slug: '111-alpha' };
|
|
85
|
-
const jobs = [
|
|
86
|
-
job,
|
|
87
|
-
{ slug: '140-mid', agentType: 'dev-lead', status: 'pending', dependsOn: ['111-alpha'] },
|
|
88
|
-
{ slug: '150-sink', agentType: 'dev-lead', status: 'pending', dependsOn: ['140-mid'] },
|
|
89
|
-
{ slug: '999-validate', agentType: 'validator', status: 'pending', dependsOn: ['150-sink'] },
|
|
90
|
-
];
|
|
91
|
-
expect(hasDownstreamValidator(job, jobs)).toBe(true);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
test('false when the validator only reaches an unrelated branch', () => {
|
|
95
|
-
const job = { slug: '111-alpha' };
|
|
96
|
-
const jobs = [
|
|
97
|
-
job,
|
|
98
|
-
{ slug: '222-beta', agentType: 'dev-lead', status: 'pending', dependsOn: [] },
|
|
99
|
-
{ slug: '150-sink', agentType: 'dev-lead', status: 'pending', dependsOn: ['222-beta'] },
|
|
100
|
-
{ slug: '999-validate', agentType: 'validator', status: 'pending', dependsOn: ['150-sink'] },
|
|
101
|
-
];
|
|
102
|
-
expect(hasDownstreamValidator(job, jobs)).toBe(false);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
test('cycle in dependsOn terminates and returns false when the job is not reachable', () => {
|
|
106
|
-
const job = { slug: '111-alpha' };
|
|
107
|
-
const jobs = [
|
|
108
|
-
job,
|
|
109
|
-
{ slug: '200-a', agentType: 'dev-lead', status: 'pending', dependsOn: ['201-b'] },
|
|
110
|
-
{ slug: '201-b', agentType: 'dev-lead', status: 'pending', dependsOn: ['200-a'] },
|
|
111
|
-
{ slug: '999-validate', agentType: 'validator', status: 'pending', dependsOn: ['200-a'] },
|
|
112
|
-
];
|
|
113
|
-
expect(hasDownstreamValidator(job, jobs)).toBe(false);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
test('false when a same-named validator/sink pair belongs to a different project (cwd)', () => {
|
|
117
|
-
const job = { slug: '011-alpha', cwd: '/projectA' };
|
|
118
|
-
const jobs = [
|
|
119
|
-
job,
|
|
120
|
-
{ slug: '012-sink', cwd: '/projectA', agentType: 'dev-lead', status: 'pending', dependsOn: [] },
|
|
121
|
-
{ slug: '512-sink', cwd: '/projectB', agentType: 'dev-lead', status: 'pending', dependsOn: ['011-alpha'] },
|
|
122
|
-
{ slug: '999-validate', cwd: '/projectB', agentType: 'validator', status: 'pending', dependsOn: ['sink'] },
|
|
123
|
-
];
|
|
124
|
-
expect(hasDownstreamValidator(job, jobs)).toBe(false);
|
|
125
|
-
});
|