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,313 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* historyAggregatorIntraday.test.cjs — unit tests for
|
|
3
|
-
* historyAggregator.cjs's refreshIntradayToday() fast walk (PRD: make the
|
|
4
|
-
* periodic intraday refresh proportional to files actually modified today
|
|
5
|
-
* instead of stat-ing every jsonl file on every tick).
|
|
6
|
-
*
|
|
7
|
-
* Run: timeout 300 npx vitest run src/main/__tests__/historyAggregatorIntraday.test.cjs
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
'use strict';
|
|
11
|
-
|
|
12
|
-
import { test, expect, beforeEach, afterEach } from 'vitest';
|
|
13
|
-
const fs = require('node:fs');
|
|
14
|
-
const fsp = require('node:fs/promises');
|
|
15
|
-
const os = require('node:os');
|
|
16
|
-
const path = require('node:path');
|
|
17
|
-
|
|
18
|
-
let tmpHome;
|
|
19
|
-
let realHome;
|
|
20
|
-
let historyRollup;
|
|
21
|
-
let historyAggregator;
|
|
22
|
-
|
|
23
|
-
function encodeCwd(cwd) {
|
|
24
|
-
return cwd.replace(/\//g, '-');
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function projectsDirFor(tmp) {
|
|
28
|
-
return path.join(tmp, '.claude', 'projects');
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function writeTranscript(projectCwd, sessionId, lines, mtime) {
|
|
32
|
-
const encoded = encodeCwd(projectCwd);
|
|
33
|
-
const dir = path.join(projectsDirFor(tmpHome), encoded);
|
|
34
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
35
|
-
const file = path.join(dir, `${sessionId}.jsonl`);
|
|
36
|
-
fs.writeFileSync(file, lines.map((l) => JSON.stringify(l)).join('\n') + '\n', 'utf8');
|
|
37
|
-
if (mtime) fs.utimesSync(file, mtime, mtime);
|
|
38
|
-
return file;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function userLine(ts) {
|
|
42
|
-
return { role: 'user', ts };
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function assistantLine(ts, model, tokens = {}) {
|
|
46
|
-
return {
|
|
47
|
-
ts,
|
|
48
|
-
message: {
|
|
49
|
-
role: 'assistant',
|
|
50
|
-
model,
|
|
51
|
-
usage: {
|
|
52
|
-
input_tokens: tokens.in ?? 10,
|
|
53
|
-
output_tokens: tokens.out ?? 5,
|
|
54
|
-
cache_read_input_tokens: tokens.cacheR ?? 0,
|
|
55
|
-
cache_creation_input_tokens: tokens.cacheC ?? 0,
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Same top-level-const-baking concern as historyRollup.test.cjs — see its
|
|
62
|
-
// comment. PROJECTS_DIR, ROLLUP_PATH, and config.cjs's allowedRoots are all
|
|
63
|
-
// computed from os.homedir() at first require.
|
|
64
|
-
const MODULES_TO_RELOAD = [
|
|
65
|
-
'../lib/historyRollup.cjs',
|
|
66
|
-
'../historyAggregator.cjs',
|
|
67
|
-
'../config.cjs',
|
|
68
|
-
];
|
|
69
|
-
|
|
70
|
-
function purgeRequireCache() {
|
|
71
|
-
for (const m of MODULES_TO_RELOAD) {
|
|
72
|
-
try { delete require.cache[require.resolve(m)]; } catch { /* not loaded yet */ }
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
beforeEach(() => {
|
|
77
|
-
realHome = process.env.HOME;
|
|
78
|
-
tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'history-intraday-test-'));
|
|
79
|
-
process.env.HOME = tmpHome;
|
|
80
|
-
purgeRequireCache();
|
|
81
|
-
historyRollup = require('../lib/historyRollup.cjs');
|
|
82
|
-
historyAggregator = require('../historyAggregator.cjs');
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
afterEach(() => {
|
|
86
|
-
process.env.HOME = realHome;
|
|
87
|
-
purgeRequireCache();
|
|
88
|
-
// tmpHome is a dedicated tmpdir (never the real ~/.claude/projects — HOME
|
|
89
|
-
// is overridden above before any fixture file is written), but clean it up
|
|
90
|
-
// regardless: leaked fixture dirs under a real ~/.claude/projects have
|
|
91
|
-
// caused phantom-project bugs before (see CLAUDE.md's
|
|
92
|
-
// knownProjectAggregate.ts section) — always delete what a test created.
|
|
93
|
-
fs.rmSync(tmpHome, { recursive: true, force: true });
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
function today() {
|
|
97
|
-
return new Date().toLocaleDateString('en-CA');
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function todayAt(hour) {
|
|
101
|
-
const d = new Date();
|
|
102
|
-
d.setHours(hour, 0, 0, 0);
|
|
103
|
-
return d;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function yesterdayAt(hour) {
|
|
107
|
-
const d = new Date();
|
|
108
|
-
d.setDate(d.getDate() - 1);
|
|
109
|
-
d.setHours(hour, 0, 0, 0);
|
|
110
|
-
return d;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Ground-truth reimplementation of the ORIGINAL (pre-fix) refreshIntradayToday:
|
|
115
|
-
* unconditional readdir-per-directory + sequential stat-per-file, no registry,
|
|
116
|
-
* no concurrency. Kept here (not in production code) purely so tests can
|
|
117
|
-
* assert the new fast-walk's output is byte-for-byte identical to what the
|
|
118
|
-
* old algorithm produced for the same fixture.
|
|
119
|
-
*/
|
|
120
|
-
async function legacyRefreshIntradayToday() {
|
|
121
|
-
const PROJECTS_DIR = projectsDirFor(tmpHome);
|
|
122
|
-
const t = today();
|
|
123
|
-
|
|
124
|
-
let projectDirs;
|
|
125
|
-
try {
|
|
126
|
-
projectDirs = await fsp.readdir(PROJECTS_DIR, { withFileTypes: true });
|
|
127
|
-
} catch {
|
|
128
|
-
return [];
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const buckets = new Map();
|
|
132
|
-
for (const projEntry of projectDirs) {
|
|
133
|
-
if (!projEntry.isDirectory()) continue;
|
|
134
|
-
const encodedCwd = projEntry.name;
|
|
135
|
-
const projectDir = path.join(PROJECTS_DIR, encodedCwd);
|
|
136
|
-
|
|
137
|
-
let files;
|
|
138
|
-
try { files = await fsp.readdir(projectDir, { withFileTypes: true }); } catch { continue; }
|
|
139
|
-
|
|
140
|
-
for (const fileEntry of files) {
|
|
141
|
-
if (!fileEntry.name.endsWith('.jsonl')) continue;
|
|
142
|
-
const filePath = path.join(projectDir, fileEntry.name);
|
|
143
|
-
|
|
144
|
-
let stat;
|
|
145
|
-
try { stat = await fsp.stat(filePath); } catch { continue; }
|
|
146
|
-
|
|
147
|
-
const mtimeDate = new Date(stat.mtimeMs).toLocaleDateString('en-CA');
|
|
148
|
-
if (mtimeDate < t) continue;
|
|
149
|
-
|
|
150
|
-
const { result: parsed } = await historyAggregator.parseJSONL(filePath, stat);
|
|
151
|
-
if (parsed.skipped) continue;
|
|
152
|
-
if (parsed.sessionDate !== t) continue;
|
|
153
|
-
|
|
154
|
-
const key = `${t}|${encodedCwd}`;
|
|
155
|
-
if (!buckets.has(key)) {
|
|
156
|
-
buckets.set(key, {
|
|
157
|
-
date: t,
|
|
158
|
-
encodedCwd,
|
|
159
|
-
promptCount: 0,
|
|
160
|
-
inputTokens: 0,
|
|
161
|
-
outputTokens: 0,
|
|
162
|
-
toolCallCount: 0,
|
|
163
|
-
sessionCount: 0,
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
const b = buckets.get(key);
|
|
167
|
-
b.promptCount += parsed.promptCount;
|
|
168
|
-
b.inputTokens += parsed.inputTokens;
|
|
169
|
-
b.outputTokens += parsed.outputTokens;
|
|
170
|
-
b.toolCallCount += parsed.toolCallCount;
|
|
171
|
-
b.sessionCount++;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
return Array.from(buckets.values()).sort((a, b) => a.encodedCwd.localeCompare(b.encodedCwd));
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/** Read back today's rollup lines as a simplified, order-stable shape for comparison. */
|
|
178
|
-
async function readTodayRollupSimplified() {
|
|
179
|
-
const t = today();
|
|
180
|
-
const map = await historyRollup.readRollup(t, t);
|
|
181
|
-
const byProject = new Map();
|
|
182
|
-
for (const bucket of map.values()) {
|
|
183
|
-
if (bucket.projectDir === historyRollup.FINALIZED_PROJECT_ID) continue;
|
|
184
|
-
const key = bucket.projectDir;
|
|
185
|
-
let row = byProject.get(key);
|
|
186
|
-
if (!row) {
|
|
187
|
-
row = { date: t, encodedCwd: key, promptCount: 0, inputTokens: 0, outputTokens: 0, toolCallCount: 0, sessionCount: 0 };
|
|
188
|
-
byProject.set(key, row);
|
|
189
|
-
}
|
|
190
|
-
if (bucket.modelId === historyRollup.TOTALS_MODEL_ID) {
|
|
191
|
-
row.promptCount += bucket.promptCount || 0;
|
|
192
|
-
row.toolCallCount += bucket.toolCallCount || 0;
|
|
193
|
-
row.sessionCount += bucket.sessionCount || 0;
|
|
194
|
-
} else {
|
|
195
|
-
row.inputTokens += bucket.inputTokens || 0;
|
|
196
|
-
row.outputTokens += bucket.outputTokens || 0;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
return Array.from(byProject.values()).sort((a, b) => a.encodedCwd.localeCompare(b.encodedCwd));
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
test('refreshIntradayToday output matches the old unconditional-walk algorithm for the same fixture', async () => {
|
|
203
|
-
writeTranscript('/repo/alpha', 'sess-1', [
|
|
204
|
-
userLine(todayAt(9).toISOString()),
|
|
205
|
-
assistantLine(todayAt(9).toISOString(), 'claude-sonnet-5', { in: 100, out: 20 }),
|
|
206
|
-
], todayAt(9));
|
|
207
|
-
writeTranscript('/repo/beta', 'sess-2', [
|
|
208
|
-
userLine(todayAt(10).toISOString()),
|
|
209
|
-
assistantLine(todayAt(10).toISOString(), 'claude-opus-5', { in: 50, out: 10 }),
|
|
210
|
-
], todayAt(10));
|
|
211
|
-
// An old, untouched-today file mixed in — must be excluded from both.
|
|
212
|
-
writeTranscript('/repo/gamma', 'sess-3', [
|
|
213
|
-
userLine(yesterdayAt(9).toISOString()),
|
|
214
|
-
], yesterdayAt(9));
|
|
215
|
-
|
|
216
|
-
const expected = await legacyRefreshIntradayToday();
|
|
217
|
-
|
|
218
|
-
const result = await historyAggregator.refreshIntradayToday();
|
|
219
|
-
expect(result.projectsUpdated).toBe(2);
|
|
220
|
-
|
|
221
|
-
const actual = await readTodayRollupSimplified();
|
|
222
|
-
expect(actual).toEqual(expected.map((b) => ({
|
|
223
|
-
date: b.date,
|
|
224
|
-
encodedCwd: b.encodedCwd,
|
|
225
|
-
promptCount: b.promptCount,
|
|
226
|
-
inputTokens: b.inputTokens,
|
|
227
|
-
outputTokens: b.outputTokens,
|
|
228
|
-
toolCallCount: b.toolCallCount,
|
|
229
|
-
sessionCount: b.sessionCount,
|
|
230
|
-
})));
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
test('a file modified today, in a directory the fast path would skip re-enumerating, is still picked up', async () => {
|
|
234
|
-
// Session file whose FIRST event is today (this is what makes its
|
|
235
|
-
// sessionDate bucket "today" at all — sessionDate is derived from the
|
|
236
|
-
// file's first line, not its mtime). The first tick discovers it via a
|
|
237
|
-
// real readdir (new directory) and caches the directory's mtime + file
|
|
238
|
-
// list alongside it.
|
|
239
|
-
const file = writeTranscript('/repo/resumed', 'sess-old', [
|
|
240
|
-
userLine(todayAt(8).toISOString()),
|
|
241
|
-
assistantLine(todayAt(8).toISOString(), 'claude-sonnet-5', { in: 10, out: 2 }),
|
|
242
|
-
], todayAt(8));
|
|
243
|
-
|
|
244
|
-
const first = await historyAggregator.refreshIntradayToday();
|
|
245
|
-
expect(first.projectsUpdated).toBe(1);
|
|
246
|
-
|
|
247
|
-
// Now append to that SAME file later today, without creating or removing
|
|
248
|
-
// any directory entry — appending to an existing file does NOT change the
|
|
249
|
-
// parent directory's mtime (verified empirically; see the strategy comment
|
|
250
|
-
// above refreshIntradayToday). A naive "skip the whole directory when its
|
|
251
|
-
// mtime is unchanged" fast path would miss this update entirely; the real
|
|
252
|
-
// implementation must keep re-stat-ing every previously-seen file in the
|
|
253
|
-
// directory regardless.
|
|
254
|
-
const dirPath = path.dirname(file);
|
|
255
|
-
const dirStatBefore = fs.statSync(dirPath);
|
|
256
|
-
|
|
257
|
-
fs.appendFileSync(file, JSON.stringify(assistantLine(todayAt(11).toISOString(), 'claude-sonnet-5', { in: 30, out: 6 })) + '\n');
|
|
258
|
-
const todayStamp = todayAt(11);
|
|
259
|
-
fs.utimesSync(file, todayStamp, todayStamp);
|
|
260
|
-
|
|
261
|
-
const dirStatAfter = fs.statSync(dirPath);
|
|
262
|
-
// Sanity check on the very premise this test is exercising: the directory
|
|
263
|
-
// mtime genuinely did not move, so the second tick's fast path is forced to
|
|
264
|
-
// reuse (not re-readdir) this directory's cached file list.
|
|
265
|
-
expect(dirStatAfter.mtimeMs).toBe(dirStatBefore.mtimeMs);
|
|
266
|
-
|
|
267
|
-
const second = await historyAggregator.refreshIntradayToday();
|
|
268
|
-
expect(second.projectsUpdated).toBe(1);
|
|
269
|
-
|
|
270
|
-
const rows = await readTodayRollupSimplified();
|
|
271
|
-
expect(rows).toEqual([
|
|
272
|
-
{ date: today(), encodedCwd: encodeCwd('/repo/resumed'), promptCount: 1, inputTokens: 40, outputTokens: 8, toolCallCount: 0, sessionCount: 1 },
|
|
273
|
-
]);
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
test('a brand-new session file created after the first tick is discovered on the next tick', async () => {
|
|
277
|
-
writeTranscript('/repo/steady', 'sess-a', [
|
|
278
|
-
userLine(todayAt(8).toISOString()),
|
|
279
|
-
], todayAt(8));
|
|
280
|
-
|
|
281
|
-
const first = await historyAggregator.refreshIntradayToday();
|
|
282
|
-
expect(first.projectsUpdated).toBe(1);
|
|
283
|
-
|
|
284
|
-
// New file in the SAME directory — this DOES bump the directory's mtime
|
|
285
|
-
// (an entry was added), so the registry's cached file list must be
|
|
286
|
-
// refreshed via readdir on the next tick.
|
|
287
|
-
writeTranscript('/repo/steady', 'sess-b', [
|
|
288
|
-
userLine(todayAt(9).toISOString()),
|
|
289
|
-
], todayAt(9));
|
|
290
|
-
|
|
291
|
-
const second = await historyAggregator.refreshIntradayToday();
|
|
292
|
-
expect(second.projectsUpdated).toBe(1);
|
|
293
|
-
|
|
294
|
-
const rows = await readTodayRollupSimplified();
|
|
295
|
-
expect(rows[0].sessionCount).toBe(2);
|
|
296
|
-
expect(rows[0].promptCount).toBe(2);
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
test('mapWithConcurrency preserves input order and respects the concurrency bound', async () => {
|
|
300
|
-
let inFlight = 0;
|
|
301
|
-
let maxInFlight = 0;
|
|
302
|
-
const items = Array.from({ length: 50 }, (_, i) => i);
|
|
303
|
-
const results = await historyAggregator.mapWithConcurrency(items, 5, async (i) => {
|
|
304
|
-
inFlight++;
|
|
305
|
-
maxInFlight = Math.max(maxInFlight, inFlight);
|
|
306
|
-
await new Promise((resolve) => setTimeout(resolve, 1));
|
|
307
|
-
inFlight--;
|
|
308
|
-
return i * 2;
|
|
309
|
-
});
|
|
310
|
-
expect(results).toEqual(items.map((i) => i * 2));
|
|
311
|
-
expect(maxInFlight).toBeLessThanOrEqual(5);
|
|
312
|
-
expect(maxInFlight).toBeGreaterThan(1);
|
|
313
|
-
});
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* historyDashboard.test.cjs — unit tests for src/main/historyDashboard.cjs
|
|
3
|
-
* (the `history:dashboard` IPC's pure rollup-only computation).
|
|
4
|
-
*
|
|
5
|
-
* Run: timeout 300 npx vitest run src/main/__tests__/historyDashboard.test.cjs
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
'use strict';
|
|
9
|
-
|
|
10
|
-
import { test, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
11
|
-
const fs = require('node:fs');
|
|
12
|
-
const os = require('node:os');
|
|
13
|
-
const path = require('node:path');
|
|
14
|
-
|
|
15
|
-
let tmpHome;
|
|
16
|
-
let realHome;
|
|
17
|
-
let historyRollup;
|
|
18
|
-
let historyDashboard;
|
|
19
|
-
|
|
20
|
-
const MODULES_TO_RELOAD = [
|
|
21
|
-
'../lib/historyRollup.cjs',
|
|
22
|
-
'../historyAggregator.cjs',
|
|
23
|
-
'../historyDashboard.cjs',
|
|
24
|
-
'../config.cjs',
|
|
25
|
-
];
|
|
26
|
-
|
|
27
|
-
function purgeRequireCache() {
|
|
28
|
-
for (const m of MODULES_TO_RELOAD) {
|
|
29
|
-
try { delete require.cache[require.resolve(m)]; } catch { /* not loaded yet */ }
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
beforeEach(() => {
|
|
34
|
-
realHome = process.env.HOME;
|
|
35
|
-
tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'history-dashboard-test-'));
|
|
36
|
-
process.env.HOME = tmpHome;
|
|
37
|
-
purgeRequireCache();
|
|
38
|
-
historyRollup = require('../lib/historyRollup.cjs');
|
|
39
|
-
historyDashboard = require('../historyDashboard.cjs');
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
afterEach(() => {
|
|
43
|
-
process.env.HOME = realHome;
|
|
44
|
-
purgeRequireCache();
|
|
45
|
-
fs.rmSync(tmpHome, { recursive: true, force: true });
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
function today() {
|
|
49
|
-
return new Date().toLocaleDateString('en-CA');
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function daysAgo(n) {
|
|
53
|
-
const d = new Date();
|
|
54
|
-
d.setDate(d.getDate() - n);
|
|
55
|
-
return d.toLocaleDateString('en-CA');
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async function seedThreeDayFixture() {
|
|
59
|
-
const d2 = daysAgo(2);
|
|
60
|
-
const d1 = daysAgo(1);
|
|
61
|
-
const d0 = today();
|
|
62
|
-
|
|
63
|
-
await historyRollup.appendRollupDays([
|
|
64
|
-
// day -2: project A, finalized, sonnet
|
|
65
|
-
{ date: d2, projectDir: 'proj-a', modelId: historyRollup.TOTALS_MODEL_ID, promptCount: 4, toolCallCount: 2, toolBreakdown: { Read: 2 }, errorCount: 0, sessionCount: 1, activeMinutes: 12, v: 2 },
|
|
66
|
-
{ date: d2, projectDir: 'proj-a', modelId: 'claude-sonnet-5', promptCount: 0, inputTokens: 1000, outputTokens: 200, cacheReadTokens: 0, cacheCreationTokens: 0, v: 2 },
|
|
67
|
-
{ date: d2, projectDir: historyRollup.FINALIZED_PROJECT_ID, modelId: historyRollup.FINALIZED_MODEL_ID, finalizedAt: 111, v: 2 },
|
|
68
|
-
|
|
69
|
-
// day -1: project A and B, finalized
|
|
70
|
-
{ date: d1, projectDir: 'proj-a', modelId: historyRollup.TOTALS_MODEL_ID, promptCount: 3, toolCallCount: 1, toolBreakdown: { Edit: 1 }, errorCount: 1, sessionCount: 1, activeMinutes: 8, v: 2 },
|
|
71
|
-
{ date: d1, projectDir: 'proj-a', modelId: 'claude-sonnet-5', promptCount: 0, inputTokens: 500, outputTokens: 100, cacheReadTokens: 0, cacheCreationTokens: 0, v: 2 },
|
|
72
|
-
{ date: d1, projectDir: 'proj-b', modelId: historyRollup.TOTALS_MODEL_ID, promptCount: 2, toolCallCount: 0, toolBreakdown: {}, errorCount: 0, sessionCount: 1, activeMinutes: 5, v: 2 },
|
|
73
|
-
{ date: d1, projectDir: 'proj-b', modelId: 'claude-opus-5', promptCount: 0, inputTokens: 300, outputTokens: 50, cacheReadTokens: 0, cacheCreationTokens: 0, v: 2 },
|
|
74
|
-
{ date: d1, projectDir: historyRollup.FINALIZED_PROJECT_ID, modelId: historyRollup.FINALIZED_MODEL_ID, finalizedAt: 222, v: 2 },
|
|
75
|
-
|
|
76
|
-
// today: provisional, project A only, no finalized marker
|
|
77
|
-
{ date: d0, projectDir: 'proj-a', modelId: historyRollup.TOTALS_MODEL_ID, promptCount: 1, toolCallCount: 1, toolBreakdown: { Bash: 1 }, errorCount: 0, sessionCount: 1, activeMinutes: 3, v: 2 },
|
|
78
|
-
{ date: d0, projectDir: 'proj-a', modelId: 'claude-sonnet-5', promptCount: 0, inputTokens: 100, outputTokens: 20, cacheReadTokens: 0, cacheCreationTokens: 0, v: 2 },
|
|
79
|
-
]);
|
|
80
|
-
|
|
81
|
-
return { d2, d1, d0 };
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
test('dashboard response shape: 3-day fixture with rangeDays=30 includes all seeded days, correct totals, provisional today', async () => {
|
|
85
|
-
const { d2, d1, d0 } = await seedThreeDayFixture();
|
|
86
|
-
|
|
87
|
-
const result = await historyDashboard.computeDashboard({ rangeDays: 30 });
|
|
88
|
-
|
|
89
|
-
expect(result.from).toBeTruthy();
|
|
90
|
-
expect(result.to).toBe(d0);
|
|
91
|
-
expect(Array.isArray(result.days)).toBe(true);
|
|
92
|
-
|
|
93
|
-
const dates = result.days.map((d) => d.date);
|
|
94
|
-
expect(dates).toContain(d2);
|
|
95
|
-
expect(dates).toContain(d1);
|
|
96
|
-
expect(dates).toContain(d0);
|
|
97
|
-
|
|
98
|
-
// provisionalDates: only today (no finalized marker was written for it).
|
|
99
|
-
expect(result.provisionalDates).toEqual([d0]);
|
|
100
|
-
|
|
101
|
-
// Totals across all 3 days: promptCount 4+3+2+1 = 10
|
|
102
|
-
expect(result.totals.promptCount).toBe(10);
|
|
103
|
-
expect(result.totals.activeMinutes).toBe(12 + 8 + 5 + 3);
|
|
104
|
-
|
|
105
|
-
// byProjectTotals: proj-a spans all 3 days, proj-b only day -1
|
|
106
|
-
expect(result.byProjectTotals['proj-a'].promptCount).toBe(4 + 3 + 1);
|
|
107
|
-
expect(result.byProjectTotals['proj-b'].promptCount).toBe(2);
|
|
108
|
-
|
|
109
|
-
// byModelTotals: sonnet appears on all 3 days for proj-a + day -1 for... only proj-a uses sonnet
|
|
110
|
-
expect(result.byModelTotals['claude-sonnet-5'].inputTokens).toBe(1000 + 500 + 100);
|
|
111
|
-
expect(result.byModelTotals['claude-sonnet-5'].costUsd).toBeGreaterThan(0);
|
|
112
|
-
expect(result.byModelTotals['claude-opus-5'].inputTokens).toBe(300);
|
|
113
|
-
|
|
114
|
-
// toolsByProject
|
|
115
|
-
expect(result.toolsByProject['proj-a']).toEqual({ Read: 2, Edit: 1, Bash: 1 });
|
|
116
|
-
expect(result.toolsByProject['proj-b']).toEqual({});
|
|
117
|
-
|
|
118
|
-
// Per-day, per-project bucket shape.
|
|
119
|
-
const dayMinus2 = result.days.find((d) => d.date === d2);
|
|
120
|
-
expect(dayMinus2.byProject['proj-a'].promptCount).toBe(4);
|
|
121
|
-
expect(dayMinus2.byProject['proj-a'].byModel['claude-sonnet-5'].costUsd).toBeGreaterThan(0);
|
|
122
|
-
|
|
123
|
-
expect(typeof result.generatedAt).toBe('number');
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
test('dashboard: rangeDays=0 (all time) includes every day with no prior-window bound, prevTotals stays zero', async () => {
|
|
127
|
-
await seedThreeDayFixture();
|
|
128
|
-
|
|
129
|
-
const result = await historyDashboard.computeDashboard({ rangeDays: 0 });
|
|
130
|
-
expect(result.days.length).toBe(3);
|
|
131
|
-
expect(result.prevTotals.promptCount).toBe(0);
|
|
132
|
-
expect(result.prevTotals.estimatedCostUsd).toBe(0);
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
test('dashboard: prevTotals aggregates the prior window distinct from the current window', async () => {
|
|
136
|
-
const d0 = today();
|
|
137
|
-
// Two 1-day windows: rangeDays=1 → current = [today, today], prior = [yesterday, yesterday].
|
|
138
|
-
const yesterday = daysAgo(1);
|
|
139
|
-
await historyRollup.appendRollupDays([
|
|
140
|
-
{ date: yesterday, projectDir: 'proj-a', modelId: historyRollup.TOTALS_MODEL_ID, promptCount: 9, sessionCount: 1, activeMinutes: 20, v: 2 },
|
|
141
|
-
{ date: yesterday, projectDir: historyRollup.FINALIZED_PROJECT_ID, modelId: historyRollup.FINALIZED_MODEL_ID, finalizedAt: 1, v: 2 },
|
|
142
|
-
{ date: d0, projectDir: 'proj-a', modelId: historyRollup.TOTALS_MODEL_ID, promptCount: 2, sessionCount: 1, activeMinutes: 4, v: 2 },
|
|
143
|
-
]);
|
|
144
|
-
|
|
145
|
-
const result = await historyDashboard.computeDashboard({ rangeDays: 1 });
|
|
146
|
-
expect(result.totals.promptCount).toBe(2);
|
|
147
|
-
expect(result.prevTotals.promptCount).toBe(9);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
test('zero-scan guarantee: computeDashboard never reads PROJECTS_DIR / transcript files', async () => {
|
|
151
|
-
await seedThreeDayFixture();
|
|
152
|
-
|
|
153
|
-
const fsp = require('node:fs/promises');
|
|
154
|
-
const PROJECTS_DIR = path.join(tmpHome, '.claude', 'projects');
|
|
155
|
-
const readdirSpy = vi.spyOn(fsp, 'readdir');
|
|
156
|
-
|
|
157
|
-
await historyDashboard.computeDashboard({ rangeDays: 30 });
|
|
158
|
-
|
|
159
|
-
const scannedProjectsDir = readdirSpy.mock.calls.some((call) => String(call[0]).startsWith(PROJECTS_DIR));
|
|
160
|
-
expect(scannedProjectsDir).toBe(false);
|
|
161
|
-
|
|
162
|
-
readdirSpy.mockRestore();
|
|
163
|
-
});
|