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,201 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* prdCreateDisposition.test.cjs — scheduler wave-disposition PRD:
|
|
3
|
-
* createPrd()'s authoring-time disposition gate. A PRD landing as a wave
|
|
4
|
-
* ROOT (no dependsOn of its own) in an Epic that already has incomplete
|
|
5
|
-
* PRDs must record an explicit 'append'/'new-head' choice — required for an
|
|
6
|
-
* interactive caller, defaulted (and logged) for a non-interactive one.
|
|
7
|
-
*
|
|
8
|
-
* Run: timeout 120 npx vitest run src/main/__tests__/prdCreateDisposition.test.cjs
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
'use strict';
|
|
12
|
-
|
|
13
|
-
import { test, expect, beforeEach, afterEach } from 'vitest';
|
|
14
|
-
const fsp = require('node:fs/promises');
|
|
15
|
-
const os = require('node:os');
|
|
16
|
-
const path = require('node:path');
|
|
17
|
-
const config = require('../config.cjs');
|
|
18
|
-
const prdParser = require('../scheduler/prdParser.cjs');
|
|
19
|
-
const { parsePrdFile } = require('../lib/prdFrontmatter.cjs');
|
|
20
|
-
const { createPrd } = require('../lib/prdCreate.cjs');
|
|
21
|
-
|
|
22
|
-
async function mkTmpPrdsDir() {
|
|
23
|
-
const root = await fsp.mkdtemp(path.join(os.tmpdir(), 'sm-disposition-create-prd-'));
|
|
24
|
-
config.addAllowedRoot(root);
|
|
25
|
-
const prdsDir = path.join(root, '.claude', 'prds');
|
|
26
|
-
await fsp.mkdir(prdsDir, { recursive: true });
|
|
27
|
-
return prdsDir;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/** Minimal fake `remote` — same shape as prdCreate.test.cjs's
|
|
31
|
-
* makeFakeRemoteWithPrdsDir, but listPrds also reports sourcePromptId/
|
|
32
|
-
* dependsOn (read back from each seeded file's real frontmatter) and
|
|
33
|
-
* status (from the test-supplied statusBySlug map) — the fields
|
|
34
|
-
* createPrd()'s disposition gate actually reads. */
|
|
35
|
-
function makeFakeRemote(prdsDir, statusBySlug = {}) {
|
|
36
|
-
return {
|
|
37
|
-
async allocateParallelGroup() {
|
|
38
|
-
return prdParser.allocateParallelGroup(prdsDir);
|
|
39
|
-
},
|
|
40
|
-
async readPrd(slug) {
|
|
41
|
-
try {
|
|
42
|
-
const text = await fsp.readFile(path.join(prdsDir, `${slug}.md`), 'utf8');
|
|
43
|
-
return { ok: true, text };
|
|
44
|
-
} catch (e) {
|
|
45
|
-
return { ok: false, error: e?.message };
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
async writePrd(slug, body) {
|
|
49
|
-
try {
|
|
50
|
-
const filePath = path.join(prdsDir, `${slug}.md`);
|
|
51
|
-
await config.writeTextAtomic(filePath, body, { writer: 'scheduler' });
|
|
52
|
-
return { ok: true };
|
|
53
|
-
} catch (e) {
|
|
54
|
-
return { ok: false, error: e?.message };
|
|
55
|
-
}
|
|
56
|
-
},
|
|
57
|
-
async listPrds() {
|
|
58
|
-
const entries = await fsp.readdir(prdsDir);
|
|
59
|
-
const prds = [];
|
|
60
|
-
for (const f of entries) {
|
|
61
|
-
if (!f.endsWith('.md')) continue;
|
|
62
|
-
const slug = f.slice(0, -3);
|
|
63
|
-
const raw = await fsp.readFile(path.join(prdsDir, f), 'utf8');
|
|
64
|
-
const { frontmatter } = parsePrdFile(raw);
|
|
65
|
-
prds.push({
|
|
66
|
-
slug,
|
|
67
|
-
sourcePromptId: frontmatter.sourcePromptId ?? null,
|
|
68
|
-
dependsOn: frontmatter.dependsOn ?? null,
|
|
69
|
-
status: statusBySlug[slug] ?? null,
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
return { prds, total: prds.length, limit: prds.length, offset: 0, hasMore: false };
|
|
73
|
-
},
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/** Seeds a fixture PRD file directly (bypassing createPrd) at an explicit,
|
|
78
|
-
* already-NN-prefixed slug, so tests can assert dependsOn against a known
|
|
79
|
-
* name instead of a freshly-allocated one. */
|
|
80
|
-
async function seedPrd(prdsDir, slug, { sourcePromptId, dependsOn } = {}) {
|
|
81
|
-
const lines = ['---', 'title: seed', `sourcePromptId: ${sourcePromptId}`];
|
|
82
|
-
if (dependsOn && dependsOn.length) lines.push(`dependsOn: [${dependsOn.join(', ')}]`);
|
|
83
|
-
lines.push('---', '# Goal', '', 'seed body', '');
|
|
84
|
-
await fsp.writeFile(path.join(prdsDir, `${slug}.md`), lines.join('\n'));
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function validBody(overrides = {}) {
|
|
88
|
-
return {
|
|
89
|
-
title: 'Wave 2 root',
|
|
90
|
-
cwd: os.homedir(),
|
|
91
|
-
estimateMinutes: 10,
|
|
92
|
-
goal: 'Do the follow-on work.',
|
|
93
|
-
acceptanceCriteria: ['a'],
|
|
94
|
-
implementationNotes: 'n',
|
|
95
|
-
...overrides,
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// This suite's own process may itself be running as a scheduled job (this
|
|
100
|
-
// file's disposition gate keys directly off SM_SCHEDULER_JOB_SLUG — see
|
|
101
|
-
// createPrd()) — never assume it's unset going in; each test controls it
|
|
102
|
-
// explicitly.
|
|
103
|
-
const originalJobSlug = process.env.SM_SCHEDULER_JOB_SLUG;
|
|
104
|
-
beforeEach(() => {
|
|
105
|
-
delete process.env.SM_SCHEDULER_JOB_SLUG;
|
|
106
|
-
});
|
|
107
|
-
afterEach(() => {
|
|
108
|
-
if (originalJobSlug === undefined) delete process.env.SM_SCHEDULER_JOB_SLUG;
|
|
109
|
-
else process.env.SM_SCHEDULER_JOB_SLUG = originalJobSlug;
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
test('refuses to write, without a disposition, a new root joining an Epic that already has an incomplete PRD', async () => {
|
|
113
|
-
const prdsDir = await mkTmpPrdsDir();
|
|
114
|
-
await seedPrd(prdsDir, '1-wave1-tail', { sourcePromptId: 'epic-1' });
|
|
115
|
-
const remote = makeFakeRemote(prdsDir, { '1-wave1-tail': 'pending' });
|
|
116
|
-
|
|
117
|
-
const result = await createPrd(validBody({ sourcePromptId: 'epic-1' }), remote);
|
|
118
|
-
|
|
119
|
-
expect(result.ok).toBe(false);
|
|
120
|
-
expect(result.status).toBe(400);
|
|
121
|
-
expect(result.error).toMatch(/disposition/i);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
test('disposition: append attaches the new root behind the Epic\'s current terminal PRD', async () => {
|
|
125
|
-
const prdsDir = await mkTmpPrdsDir();
|
|
126
|
-
await seedPrd(prdsDir, '1-wave1-tail', { sourcePromptId: 'epic-1' });
|
|
127
|
-
const remote = makeFakeRemote(prdsDir, { '1-wave1-tail': 'pending' });
|
|
128
|
-
|
|
129
|
-
const result = await createPrd(validBody({ sourcePromptId: 'epic-1', disposition: 'append' }), remote);
|
|
130
|
-
|
|
131
|
-
expect(result.ok).toBe(true);
|
|
132
|
-
const written = await fsp.readFile(path.join(prdsDir, result.filename), 'utf8');
|
|
133
|
-
expect(written).toMatch(/^dependsOn: \[1-wave1-tail\]$/m);
|
|
134
|
-
expect(written).toMatch(/^disposition: append$/m);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
test('disposition: new-head leaves the new root independent (no dependsOn)', async () => {
|
|
138
|
-
const prdsDir = await mkTmpPrdsDir();
|
|
139
|
-
await seedPrd(prdsDir, '1-wave1-tail', { sourcePromptId: 'epic-1' });
|
|
140
|
-
const remote = makeFakeRemote(prdsDir, { '1-wave1-tail': 'pending' });
|
|
141
|
-
|
|
142
|
-
const result = await createPrd(validBody({ sourcePromptId: 'epic-1', disposition: 'new-head' }), remote);
|
|
143
|
-
|
|
144
|
-
expect(result.ok).toBe(true);
|
|
145
|
-
const written = await fsp.readFile(path.join(prdsDir, result.filename), 'utf8');
|
|
146
|
-
expect(written).not.toContain('dependsOn');
|
|
147
|
-
expect(written).toMatch(/^disposition: new-head$/m);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
test('a non-interactive caller (SM_SCHEDULER_JOB_SLUG set) with no disposition defaults to append, logged as a default', async () => {
|
|
151
|
-
const prdsDir = await mkTmpPrdsDir();
|
|
152
|
-
await seedPrd(prdsDir, '1-wave1-tail', { sourcePromptId: 'epic-1' });
|
|
153
|
-
const remote = makeFakeRemote(prdsDir, { '1-wave1-tail': 'pending' });
|
|
154
|
-
process.env.SM_SCHEDULER_JOB_SLUG = 'some-running-job';
|
|
155
|
-
|
|
156
|
-
const result = await createPrd(validBody({ sourcePromptId: 'epic-1' }), remote);
|
|
157
|
-
|
|
158
|
-
expect(result.ok).toBe(true);
|
|
159
|
-
const written = await fsp.readFile(path.join(prdsDir, result.filename), 'utf8');
|
|
160
|
-
expect(written).toMatch(/^dependsOn: \[1-wave1-tail\]$/m);
|
|
161
|
-
expect(written).toMatch(/^disposition: append$/m);
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
test('no disposition required for the first PRD in a brand-new Epic', async () => {
|
|
165
|
-
const prdsDir = await mkTmpPrdsDir();
|
|
166
|
-
const remote = makeFakeRemote(prdsDir);
|
|
167
|
-
|
|
168
|
-
const result = await createPrd(validBody({ sourcePromptId: 'epic-empty' }), remote);
|
|
169
|
-
|
|
170
|
-
expect(result.ok).toBe(true);
|
|
171
|
-
const written = await fsp.readFile(path.join(prdsDir, result.filename), 'utf8');
|
|
172
|
-
expect(written).not.toContain('disposition');
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
test('no disposition required when every existing PRD in the Epic is already completed', async () => {
|
|
176
|
-
const prdsDir = await mkTmpPrdsDir();
|
|
177
|
-
await seedPrd(prdsDir, '1-wave1-tail', { sourcePromptId: 'epic-done' });
|
|
178
|
-
const remote = makeFakeRemote(prdsDir, { '1-wave1-tail': 'completed' });
|
|
179
|
-
|
|
180
|
-
const result = await createPrd(validBody({ sourcePromptId: 'epic-done' }), remote);
|
|
181
|
-
|
|
182
|
-
expect(result.ok).toBe(true);
|
|
183
|
-
const written = await fsp.readFile(path.join(prdsDir, result.filename), 'utf8');
|
|
184
|
-
expect(written).not.toContain('disposition');
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
test('an explicit dependsOn bypasses the disposition gate entirely', async () => {
|
|
188
|
-
const prdsDir = await mkTmpPrdsDir();
|
|
189
|
-
await seedPrd(prdsDir, '1-wave1-tail', { sourcePromptId: 'epic-1' });
|
|
190
|
-
const remote = makeFakeRemote(prdsDir, { '1-wave1-tail': 'pending' });
|
|
191
|
-
|
|
192
|
-
const result = await createPrd(
|
|
193
|
-
validBody({ sourcePromptId: 'epic-1', dependsOn: ['1-wave1-tail'] }),
|
|
194
|
-
remote,
|
|
195
|
-
);
|
|
196
|
-
|
|
197
|
-
expect(result.ok).toBe(true);
|
|
198
|
-
const written = await fsp.readFile(path.join(prdsDir, result.filename), 'utf8');
|
|
199
|
-
expect(written).toMatch(/^dependsOn: \[1-wave1-tail\]$/m);
|
|
200
|
-
expect(written).not.toContain('disposition');
|
|
201
|
-
});
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* prdCreatePlanId.test.cjs — createPrd() stamps a durable `planId` (wave identity):
|
|
3
|
-
* first-ever PRD mints, append inherits the terminal's, new-head mints, explicit
|
|
4
|
-
* multi-dep inherits the lowest-numbered dependency's (diverged ids logged once).
|
|
5
|
-
*
|
|
6
|
-
* Run: timeout 120 npx vitest run src/main/__tests__/prdCreatePlanId.test.cjs
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
'use strict';
|
|
10
|
-
|
|
11
|
-
import { test, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
12
|
-
const fsp = require('node:fs/promises');
|
|
13
|
-
const os = require('node:os');
|
|
14
|
-
const path = require('node:path');
|
|
15
|
-
const config = require('../config.cjs');
|
|
16
|
-
const prdParser = require('../scheduler/prdParser.cjs');
|
|
17
|
-
const { parsePrdFile } = require('../lib/prdFrontmatter.cjs');
|
|
18
|
-
const { createPrd } = require('../lib/prdCreate.cjs');
|
|
19
|
-
const { resolveInheritedPlanId, isValidPlanId } = require('../lib/prdDisposition.cjs');
|
|
20
|
-
|
|
21
|
-
async function mkTmpPrdsDir() {
|
|
22
|
-
const root = await fsp.mkdtemp(path.join(os.tmpdir(), 'sm-planid-create-prd-'));
|
|
23
|
-
config.addAllowedRoot(root);
|
|
24
|
-
const prdsDir = path.join(root, '.claude', 'prds');
|
|
25
|
-
await fsp.mkdir(prdsDir, { recursive: true });
|
|
26
|
-
return prdsDir;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function makeFakeRemote(prdsDir, statusBySlug = {}) {
|
|
30
|
-
return {
|
|
31
|
-
async allocateParallelGroup() { return prdParser.allocateParallelGroup(prdsDir); },
|
|
32
|
-
async readPrd(slug) {
|
|
33
|
-
try { return { ok: true, text: await fsp.readFile(path.join(prdsDir, `${slug}.md`), 'utf8') }; }
|
|
34
|
-
catch (e) { return { ok: false, error: e?.message }; }
|
|
35
|
-
},
|
|
36
|
-
async writePrd(slug, body) {
|
|
37
|
-
await config.writeTextAtomic(path.join(prdsDir, `${slug}.md`), body, { writer: 'scheduler' });
|
|
38
|
-
return { ok: true };
|
|
39
|
-
},
|
|
40
|
-
async listPrds() {
|
|
41
|
-
const prds = [];
|
|
42
|
-
for (const f of await fsp.readdir(prdsDir)) {
|
|
43
|
-
if (!f.endsWith('.md')) continue;
|
|
44
|
-
const slug = f.slice(0, -3);
|
|
45
|
-
const { frontmatter } = parsePrdFile(await fsp.readFile(path.join(prdsDir, f), 'utf8'));
|
|
46
|
-
prds.push({
|
|
47
|
-
slug,
|
|
48
|
-
sourcePromptId: frontmatter.sourcePromptId ?? null,
|
|
49
|
-
dependsOn: frontmatter.dependsOn ?? null,
|
|
50
|
-
planId: frontmatter.planId ?? null,
|
|
51
|
-
status: statusBySlug[slug] ?? null,
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
return { prds, total: prds.length, limit: prds.length, offset: 0, hasMore: false };
|
|
55
|
-
},
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
async function seedPrd(prdsDir, slug, { planId, dependsOn } = {}) {
|
|
60
|
-
const lines = ['---', 'title: seed', 'sourcePromptId: epic-1'];
|
|
61
|
-
if (dependsOn) lines.push(`dependsOn: [${dependsOn.join(', ')}]`);
|
|
62
|
-
if (planId) lines.push(`planId: ${planId}`);
|
|
63
|
-
lines.push('---', '# Goal', '', 'seed', '');
|
|
64
|
-
await fsp.writeFile(path.join(prdsDir, `${slug}.md`), lines.join('\n'));
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const body = (o = {}) => ({
|
|
68
|
-
title: 'Next wave', cwd: os.homedir(), estimateMinutes: 10, goal: 'g',
|
|
69
|
-
acceptanceCriteria: ['a'], implementationNotes: 'n', ...o,
|
|
70
|
-
});
|
|
71
|
-
const planIdOf = async (dir, result) =>
|
|
72
|
-
parsePrdFile(await fsp.readFile(path.join(dir, result.filename), 'utf8')).frontmatter.planId;
|
|
73
|
-
|
|
74
|
-
const originalJobSlug = process.env.SM_SCHEDULER_JOB_SLUG;
|
|
75
|
-
beforeEach(() => { delete process.env.SM_SCHEDULER_JOB_SLUG; });
|
|
76
|
-
afterEach(() => {
|
|
77
|
-
vi.restoreAllMocks();
|
|
78
|
-
if (originalJobSlug === undefined) delete process.env.SM_SCHEDULER_JOB_SLUG;
|
|
79
|
-
else process.env.SM_SCHEDULER_JOB_SLUG = originalJobSlug;
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
test('first-ever PRD in an Epic mints a valid planId', async () => {
|
|
83
|
-
const dir = await mkTmpPrdsDir();
|
|
84
|
-
const r = await createPrd(body({ sourcePromptId: 'epic-1' }), makeFakeRemote(dir));
|
|
85
|
-
expect(r.ok).toBe(true);
|
|
86
|
-
expect(isValidPlanId(await planIdOf(dir, r))).toBe(true);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
test('a caller-supplied planId is ignored (API-owned)', async () => {
|
|
90
|
-
const dir = await mkTmpPrdsDir();
|
|
91
|
-
const r = await createPrd(body({ planId: 'evil-id' }), makeFakeRemote(dir));
|
|
92
|
-
expect(await planIdOf(dir, r)).not.toBe('evil-id');
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
test('append inherits the chain terminal\'s planId', async () => {
|
|
96
|
-
const dir = await mkTmpPrdsDir();
|
|
97
|
-
await seedPrd(dir, '1-tail', { planId: 'pl-abc' });
|
|
98
|
-
const r = await createPrd(body({ sourcePromptId: 'epic-1', disposition: 'append' }), makeFakeRemote(dir, { '1-tail': 'pending' }));
|
|
99
|
-
expect(r.ok).toBe(true);
|
|
100
|
-
expect(await planIdOf(dir, r)).toBe('pl-abc');
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
test('new-head mints a fresh planId, distinct from the existing plan', async () => {
|
|
104
|
-
const dir = await mkTmpPrdsDir();
|
|
105
|
-
await seedPrd(dir, '1-tail', { planId: 'pl-abc' });
|
|
106
|
-
const r = await createPrd(body({ sourcePromptId: 'epic-1', disposition: 'new-head' }), makeFakeRemote(dir, { '1-tail': 'pending' }));
|
|
107
|
-
const id = await planIdOf(dir, r);
|
|
108
|
-
expect(isValidPlanId(id)).toBe(true);
|
|
109
|
-
expect(id).not.toBe('pl-abc');
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
test('explicit multi-dep across two plans inherits the lowest-numbered dependency\'s and warns once', async () => {
|
|
113
|
-
const dir = await mkTmpPrdsDir();
|
|
114
|
-
await seedPrd(dir, '9-late', { planId: 'pl-late' });
|
|
115
|
-
await seedPrd(dir, '2-early', { planId: 'pl-early' });
|
|
116
|
-
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
117
|
-
const r = await createPrd(body({ dependsOn: ['9-late', '2-early'] }), makeFakeRemote(dir));
|
|
118
|
-
expect(r.ok).toBe(true);
|
|
119
|
-
expect(await planIdOf(dir, r)).toBe('pl-early');
|
|
120
|
-
expect(warn.mock.calls.filter((c) => String(c[0]).includes('spans plans')).length).toBe(1);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
test('deps that predate the stamp fall back to a freshly minted planId', async () => {
|
|
124
|
-
const dir = await mkTmpPrdsDir();
|
|
125
|
-
await seedPrd(dir, '1-legacy');
|
|
126
|
-
const r = await createPrd(body({ dependsOn: ['1-legacy'] }), makeFakeRemote(dir));
|
|
127
|
-
expect(isValidPlanId(await planIdOf(dir, r))).toBe(true);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
test('resolveInheritedPlanId ignores garbage planIds', () => {
|
|
131
|
-
expect(resolveInheritedPlanId(['1-a'], [{ slug: '1-a', planId: 'bad id,[x]' }]).planId).toBeNull();
|
|
132
|
-
});
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* prdFrontmatterAgentType.test.cjs — PRD 1114: `agentType` (WHO executes a
|
|
3
|
-
* PRD, distinct from `tag` — the WORK TYPE) is a recognized PRD frontmatter
|
|
4
|
-
* key, round-tripped byte-identically by prdFrontmatter.cjs's
|
|
5
|
-
* parsePrdFile/serializePrdFile pair, and surfaced (with read-time tolerance
|
|
6
|
-
* for a dangling persona) by scheduler/prdParser.cjs's parsePrdRaw.
|
|
7
|
-
*
|
|
8
|
-
* Run: timeout 120 npx vitest run src/main/__tests__/prdFrontmatterAgentType.test.cjs
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
'use strict';
|
|
12
|
-
|
|
13
|
-
import { test, expect } from 'vitest';
|
|
14
|
-
|
|
15
|
-
const fs = require('node:fs');
|
|
16
|
-
const fsp = require('node:fs/promises');
|
|
17
|
-
const os = require('node:os');
|
|
18
|
-
const path = require('node:path');
|
|
19
|
-
|
|
20
|
-
const { parsePrdFile, serializePrdFile } = require('../lib/prdFrontmatter.cjs');
|
|
21
|
-
const { parsePrdRaw, _resetCache } = require('../scheduler/prdParser.cjs');
|
|
22
|
-
const { todayFile } = require('../lib/opsErrorLog.cjs');
|
|
23
|
-
|
|
24
|
-
function mkTmpPrdsDir() {
|
|
25
|
-
return fsp.mkdtemp(path.join(os.tmpdir(), 'sm-prd-agenttype-fm-'));
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
test('parsePrdFile recognizes agentType and serializePrdFile round-trips it byte-identically', () => {
|
|
29
|
-
const raw = [
|
|
30
|
-
'---',
|
|
31
|
-
'title: A PRD with a persona',
|
|
32
|
-
'cwd: ~/Projects/session-manager',
|
|
33
|
-
'estimateMinutes: 30',
|
|
34
|
-
'tag: bug',
|
|
35
|
-
'agentType: dev-lead',
|
|
36
|
-
'---',
|
|
37
|
-
'# Goal',
|
|
38
|
-
'',
|
|
39
|
-
'Fix the thing.',
|
|
40
|
-
'',
|
|
41
|
-
].join('\n');
|
|
42
|
-
|
|
43
|
-
const { frontmatter, body } = parsePrdFile(raw);
|
|
44
|
-
expect(frontmatter.agentType).toBe('dev-lead');
|
|
45
|
-
expect(serializePrdFile(frontmatter, body)).toBe(raw);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
test('parsePrdFile leaves agentType undefined when omitted — additive-only, no regression for old PRDs', () => {
|
|
49
|
-
const raw = [
|
|
50
|
-
'---',
|
|
51
|
-
'title: An older PRD authored before this field existed',
|
|
52
|
-
'cwd: ~/Projects/session-manager',
|
|
53
|
-
'estimateMinutes: 30',
|
|
54
|
-
'---',
|
|
55
|
-
'body',
|
|
56
|
-
'',
|
|
57
|
-
].join('\n');
|
|
58
|
-
const { frontmatter } = parsePrdFile(raw);
|
|
59
|
-
expect(frontmatter.agentType).toBeUndefined();
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
// Real opsErrorLog write path (not a mock) — see prdAgentType.test.cjs's
|
|
63
|
-
// header for why a plain mkdtemp() cwd is safe to use here (not "ephemeral").
|
|
64
|
-
test('scheduler/prdParser.cjs parsePrdRaw exposes agentType, and tolerates a dangling persona by logging once instead of throwing', async () => {
|
|
65
|
-
const dir = await mkTmpPrdsDir();
|
|
66
|
-
_resetCache();
|
|
67
|
-
|
|
68
|
-
const danglingPath = path.join(dir, '1-dangling.md');
|
|
69
|
-
await fsp.writeFile(danglingPath, [
|
|
70
|
-
'---',
|
|
71
|
-
'title: A PRD whose persona was later deleted',
|
|
72
|
-
`cwd: ${dir}`,
|
|
73
|
-
'estimateMinutes: 10',
|
|
74
|
-
'agentType: ghost-persona-that-does-not-exist-anywhere',
|
|
75
|
-
'---',
|
|
76
|
-
'# Goal',
|
|
77
|
-
'',
|
|
78
|
-
'Still loads.',
|
|
79
|
-
'',
|
|
80
|
-
].join('\n'));
|
|
81
|
-
|
|
82
|
-
const parsed = await parsePrdRaw(danglingPath);
|
|
83
|
-
expect(parsed.agentType).toBe('ghost-persona-that-does-not-exist-anywhere');
|
|
84
|
-
|
|
85
|
-
const lines = fs.readFileSync(todayFile(dir), 'utf8').split('\n').filter(Boolean).map((l) => JSON.parse(l));
|
|
86
|
-
const matches = lines.filter((l) => l.message.includes('ghost-persona-that-does-not-exist-anywhere'));
|
|
87
|
-
expect(matches).toHaveLength(1);
|
|
88
|
-
expect(matches[0].scope).toBe('prdAgentType');
|
|
89
|
-
expect(matches[0].level).toBe('warn');
|
|
90
|
-
|
|
91
|
-
// A second parse of the SAME (cwd, agentType) pair must not log again.
|
|
92
|
-
_resetCache();
|
|
93
|
-
await parsePrdRaw(danglingPath);
|
|
94
|
-
const linesAfter = fs.readFileSync(todayFile(dir), 'utf8').split('\n').filter(Boolean);
|
|
95
|
-
expect(linesAfter).toHaveLength(1);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
test('scheduler/prdParser.cjs parsePrdRaw leaves agentType null when omitted', async () => {
|
|
99
|
-
const dir = await mkTmpPrdsDir();
|
|
100
|
-
_resetCache();
|
|
101
|
-
|
|
102
|
-
const ordinaryPath = path.join(dir, '2-ordinary.md');
|
|
103
|
-
await fsp.writeFile(ordinaryPath, [
|
|
104
|
-
'---',
|
|
105
|
-
'title: Ordinary job',
|
|
106
|
-
`cwd: ${dir}`,
|
|
107
|
-
'estimateMinutes: 10',
|
|
108
|
-
'---',
|
|
109
|
-
'# Goal',
|
|
110
|
-
'',
|
|
111
|
-
'Do the thing.',
|
|
112
|
-
'',
|
|
113
|
-
].join('\n'));
|
|
114
|
-
|
|
115
|
-
const parsed = await parsePrdRaw(ordinaryPath);
|
|
116
|
-
expect(parsed.agentType).toBeNull();
|
|
117
|
-
});
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* prdFrontmatterDependsOn.test.cjs — PRD 1124: `dependsOn` is a recognized,
|
|
3
|
-
* patchable PRD frontmatter key. Before this PRD, RECOGNIZED_KEYS omitted
|
|
4
|
-
* `dependsOn`, so it only round-tripped verbatim via `extras` and
|
|
5
|
-
* scheduler_update_prd could not patch it — the only repair path for a
|
|
6
|
-
* wrong dependsOn was archive-and-recreate, which is unsafe (archiving
|
|
7
|
-
* marks the PRD completed, freeing its dependents with zero work done).
|
|
8
|
-
*
|
|
9
|
-
* Covers the pure parse/serialize contract in prdFrontmatter.cjs. The
|
|
10
|
-
* write-time FK validation + clearing semantics wired into
|
|
11
|
-
* scheduler.remote.updatePrd are covered by
|
|
12
|
-
* prdUpdateDependsOn.test.cjs.
|
|
13
|
-
*
|
|
14
|
-
* Run: timeout 120 npx vitest run src/main/__tests__/prdFrontmatterDependsOn.test.cjs
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
'use strict';
|
|
18
|
-
|
|
19
|
-
import { test, expect } from 'vitest';
|
|
20
|
-
|
|
21
|
-
const { parsePrdFile, serializePrdFile, RECOGNIZED_KEYS } = require('../lib/prdFrontmatter.cjs');
|
|
22
|
-
|
|
23
|
-
test('dependsOn is a recognized key', () => {
|
|
24
|
-
expect(RECOGNIZED_KEYS.has('dependsOn')).toBe(true);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
test('parsePrdFile parses an inline dependsOn list into an array, and round-trips it byte-identically when unedited', () => {
|
|
28
|
-
const raw = [
|
|
29
|
-
'---',
|
|
30
|
-
'title: A follow-up PRD',
|
|
31
|
-
'cwd: ~/Projects/session-manager',
|
|
32
|
-
'estimateMinutes: 20',
|
|
33
|
-
'agentType: dev-lead',
|
|
34
|
-
'dependsOn: [widget-base, widget-shared]',
|
|
35
|
-
'---',
|
|
36
|
-
'# Goal',
|
|
37
|
-
'',
|
|
38
|
-
'Build on the base.',
|
|
39
|
-
'',
|
|
40
|
-
].join('\n');
|
|
41
|
-
|
|
42
|
-
const { frontmatter, body } = parsePrdFile(raw);
|
|
43
|
-
expect(frontmatter.dependsOn).toEqual(['widget-base', 'widget-shared']);
|
|
44
|
-
expect(serializePrdFile(frontmatter, body)).toBe(raw);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
test('parsePrdFile leaves dependsOn undefined when omitted — a PRD with no dependsOn round-trips unaffected', () => {
|
|
48
|
-
const raw = [
|
|
49
|
-
'---',
|
|
50
|
-
'title: An older PRD authored before dependsOn was patchable',
|
|
51
|
-
'cwd: ~/Projects/session-manager',
|
|
52
|
-
'estimateMinutes: 10',
|
|
53
|
-
'---',
|
|
54
|
-
'# Goal',
|
|
55
|
-
'',
|
|
56
|
-
'Do the thing.',
|
|
57
|
-
'',
|
|
58
|
-
].join('\n');
|
|
59
|
-
|
|
60
|
-
const { frontmatter, body } = parsePrdFile(raw);
|
|
61
|
-
expect(frontmatter.dependsOn).toBeUndefined();
|
|
62
|
-
expect(serializePrdFile(frontmatter, body)).toBe(raw);
|
|
63
|
-
|
|
64
|
-
// An unrelated patch (title) must not introduce a dependsOn line.
|
|
65
|
-
const updated = serializePrdFile({ ...frontmatter, title: 'Renamed' }, body);
|
|
66
|
-
expect(updated).not.toContain('dependsOn');
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
test('serializePrdFile emits a newly-set dependsOn onto a frontmatter object that lacked one', () => {
|
|
70
|
-
const raw = [
|
|
71
|
-
'---',
|
|
72
|
-
'title: Legacy PRD',
|
|
73
|
-
'cwd: ~/Projects/session-manager',
|
|
74
|
-
'estimateMinutes: 5',
|
|
75
|
-
'---',
|
|
76
|
-
'body',
|
|
77
|
-
'',
|
|
78
|
-
].join('\n');
|
|
79
|
-
|
|
80
|
-
const { frontmatter, body } = parsePrdFile(raw);
|
|
81
|
-
const updated = serializePrdFile({ ...frontmatter, dependsOn: ['some-other-prd'] }, body);
|
|
82
|
-
expect(updated).toContain('dependsOn: [some-other-prd]');
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
test('serializePrdFile patches an existing dependsOn to a new list', () => {
|
|
86
|
-
const raw = [
|
|
87
|
-
'---',
|
|
88
|
-
'title: A follow-up PRD',
|
|
89
|
-
'cwd: ~/Projects/session-manager',
|
|
90
|
-
'estimateMinutes: 20',
|
|
91
|
-
'dependsOn: [widget-base]',
|
|
92
|
-
'---',
|
|
93
|
-
'body',
|
|
94
|
-
'',
|
|
95
|
-
].join('\n');
|
|
96
|
-
|
|
97
|
-
const { frontmatter, body } = parsePrdFile(raw);
|
|
98
|
-
const updated = serializePrdFile({ ...frontmatter, dependsOn: ['widget-shared', 'widget-extra'] }, body);
|
|
99
|
-
expect(updated).toContain('dependsOn: [widget-shared, widget-extra]');
|
|
100
|
-
expect(updated).not.toContain('widget-base');
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
test('serializePrdFile patching dependsOn to an explicit empty array CLEARS it', () => {
|
|
104
|
-
const raw = [
|
|
105
|
-
'---',
|
|
106
|
-
'title: A follow-up PRD',
|
|
107
|
-
'cwd: ~/Projects/session-manager',
|
|
108
|
-
'estimateMinutes: 20',
|
|
109
|
-
'dependsOn: [widget-base]',
|
|
110
|
-
'---',
|
|
111
|
-
'body',
|
|
112
|
-
'',
|
|
113
|
-
].join('\n');
|
|
114
|
-
|
|
115
|
-
const { frontmatter, body } = parsePrdFile(raw);
|
|
116
|
-
const updated = serializePrdFile({ ...frontmatter, dependsOn: [] }, body);
|
|
117
|
-
expect(updated).not.toContain('dependsOn');
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
test('unrelated recognized-key patch leaves an existing dependsOn line untouched', () => {
|
|
121
|
-
const raw = [
|
|
122
|
-
'---',
|
|
123
|
-
'title: A follow-up PRD',
|
|
124
|
-
'cwd: ~/Projects/session-manager',
|
|
125
|
-
'estimateMinutes: 20',
|
|
126
|
-
'dependsOn: [widget-base]',
|
|
127
|
-
'---',
|
|
128
|
-
'body',
|
|
129
|
-
'',
|
|
130
|
-
].join('\n');
|
|
131
|
-
|
|
132
|
-
const { frontmatter, body } = parsePrdFile(raw);
|
|
133
|
-
const updated = serializePrdFile({ ...frontmatter, estimateMinutes: 30 }, body);
|
|
134
|
-
expect(updated).toContain('dependsOn: [widget-base]');
|
|
135
|
-
expect(updated).toContain('estimateMinutes: 30');
|
|
136
|
-
});
|