claude-code-session-manager 0.98.0 → 0.100.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/bin/cli.cjs +16 -4
- package/bin/node-floor.cjs +41 -0
- package/dist/assets/{DataModel-DCEgMTjH.js → DataModel-HdR0AxFq.js} +1 -1
- package/dist/assets/{History-Bfdoywlh.js → History-Dr4Jr_5z.js} +2 -2
- package/dist/assets/{Hooks-CKDJjTJV.js → Hooks-NhrvH3JP.js} +3 -3
- package/dist/assets/{Library-7akfgy67.js → Library-Dv_EtIMr.js} +1 -1
- package/dist/assets/{MarkdownEditor-DDesiqet.js → MarkdownEditor-DTKDWV_n.js} +1 -1
- package/dist/assets/{McpServers-D4lZtXof.js → McpServers-M034G9B3.js} +2 -2
- package/dist/assets/{Memory-CeI8G1w5.js → Memory-DzF1B4gx.js} +6 -6
- package/dist/assets/{Permissions-BGleLja4.js → Permissions-DrgbSGWl.js} +3 -3
- package/dist/assets/{Plugins-P4eCXy_S.js → Plugins-Bd3ef35w.js} +2 -2
- package/dist/assets/{ProvenanceBadge-BFczzI1B.js → ProvenanceBadge-DcBHT1Iy.js} +1 -1
- package/dist/assets/{SaveBar-l9jnEMES.js → SaveBar-rimJem-Y.js} +1 -1
- package/dist/assets/Scheduler-DjgFJl2w.js +16 -0
- package/dist/assets/{ScopeSwitcher-CjJi8ST5.js → ScopeSwitcher-CnAzTBrl.js} +1 -1
- package/dist/assets/Settings-Dc1F_sSg.js +3 -0
- package/dist/assets/{SkillReferenceGraph-CjbO01Sg.js → SkillReferenceGraph-B9aZDecM.js} +1 -1
- package/dist/assets/{Skills-BxZ9_fCR.js → Skills-DRXd18gu.js} +2 -2
- package/dist/assets/{SystemPrompt-D8vfFb8b.js → SystemPrompt-ahy5-Fwe.js} +1 -1
- package/dist/assets/{TagLibrary-2vzUo7YL.js → TagLibrary-C_tToDeL.js} +1 -1
- package/dist/assets/{TiptapBody-B8ZfbK90.js → TiptapBody-DvdefS6K.js} +1 -1
- package/dist/assets/{Toggle-BbW4nGGf.js → Toggle-6Jl5Njc6.js} +1 -1
- package/dist/assets/{index-CKH5Uxik.css → index-Bta-hwud.css} +1 -1
- package/dist/assets/{index-Cd98Q1lD.js → index-vx8O73l8.js} +497 -502
- package/dist/assets/{settingsSchema-IhUfucCF.js → settingsSchema-D6iRguNV.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +25 -29
- package/plugins/CLAUDE.md +6 -6
- package/plugins/session-manager-dev/skills/builder/4-manual/SKILL.md +21 -19
- package/plugins/session-manager-dev/skills/builder/SKILL.md +3 -3
- package/plugins/session-manager-dev/skills/develop/SKILL.md +254 -510
- package/plugins/session-manager-dev/skills/develop/standards.md +17 -22
- package/scripts/README.md +3 -11
- package/scripts/audit-ops-hygiene.cjs +3 -3
- package/scripts/hooks/lib/guard-prd-writes-policy.cjs +1 -1
- package/scripts/hooks/lib/guard-self-schedule-policy.cjs +1 -1
- package/scripts/mint-epic.cjs +2 -3
- package/scripts/scheduler-mcp-server.cjs +36 -7
- package/src/main/agentLibrary.cjs +22 -2
- package/src/main/build-info.json +4 -4
- package/src/main/config.cjs +41 -38
- package/src/main/docEdit.cjs +4 -1
- package/src/main/files.cjs +2 -5
- package/src/main/health.cjs +1 -1
- package/src/main/index.cjs +23 -10
- package/src/main/ipcSchemas.cjs +28 -35
- package/src/main/lib/agentPersonaSchema.cjs +13 -2
- package/src/main/lib/atomicFs.cjs +116 -0
- package/src/main/lib/branchSweep.cjs +13 -12
- package/src/main/lib/buildTarget.cjs +3 -3
- package/src/main/lib/claudeCliCaps.cjs +129 -0
- package/src/main/lib/credentials.cjs +2 -4
- package/src/main/lib/crossProjectFeedback.cjs +3 -3
- package/src/main/lib/cwdClassify.cjs +15 -3
- package/src/main/lib/definitionOfDone.cjs +207 -125
- package/src/main/lib/dodDrainHook.cjs +11 -5
- package/src/main/lib/epicMint.cjs +2 -4
- package/src/main/lib/epicStatusMirror.cjs +8 -10
- package/src/main/lib/epicWorktreeProjectConfig.cjs +2 -4
- package/src/main/lib/gateAuthority.cjs +96 -0
- package/src/main/lib/gitExec.cjs +69 -0
- package/src/main/lib/gitWorktree.cjs +656 -92
- package/src/main/lib/instanceLock.cjs +5 -8
- package/src/main/lib/launchFailure.cjs +2 -3
- package/src/main/lib/macroLibrary.cjs +386 -0
- package/src/main/lib/mcpToolCatalog.cjs +26 -17
- package/src/main/lib/needsReviewLedger.cjs +1 -1
- package/src/main/lib/opsErrorLog.cjs +11 -1
- package/src/main/lib/opsOwnership.cjs +0 -5
- package/src/main/lib/pidAlive.cjs +32 -0
- package/src/main/lib/prdCreate.cjs +121 -12
- package/src/main/lib/prdDisposition.cjs +2 -2
- package/src/main/lib/prdGateFiles.cjs +284 -0
- package/src/main/lib/prdLocations.cjs +61 -0
- package/src/main/lib/prdMigration.cjs +2 -3
- package/src/main/lib/prdSizing.cjs +2 -1
- package/src/main/lib/promptSessionSchema.cjs +10 -2
- package/src/main/lib/rcaReport.cjs +4 -6
- package/src/main/lib/reaperHelpers.cjs +8 -1
- package/src/main/lib/reservationExpiry.cjs +6 -1
- package/src/main/lib/reviewNotice.cjs +263 -0
- package/src/main/lib/runClaudeP.cjs +4 -1
- package/src/main/lib/schedulerPaths.cjs +22 -2
- package/src/main/lib/sessionSlots.cjs +2 -4
- package/src/main/lib/shippedPersonaSeeds.cjs +179 -0
- package/src/main/lib/timeoutShim.cjs +123 -0
- package/src/main/lib/timeoutShimScript.cjs +275 -0
- package/src/main/lib/upgradeDrain.cjs +4 -10
- package/src/main/lib/watchdogHelpers.cjs +12 -25
- package/src/main/lib/workTypeLibrary.cjs +7 -1
- package/src/main/promptSessionEvents.cjs +37 -13
- package/src/main/scheduler.cjs +910 -198
- package/src/main/seedAgentPersonas.cjs +278 -23
- package/src/main/supervisor.cjs +4 -1
- package/src/main/templates/PRD_AUTHORING.md +126 -406
- package/src/preload/__tests__/preload-surface.test.cjs +68 -0
- package/src/preload/api.d.ts +41 -94
- package/src/preload/index.cjs +14 -9
- package/src/seed/agents/architect.md +1 -0
- package/src/seed/agents/dev-lead.md +17 -18
- package/src/seed/agents/project-home-builder.md +1 -0
- package/src/seed/agents/validator.md +10 -4
- package/dist/assets/HostBilko-CDVHE0hT.js +0 -1
- package/dist/assets/Scheduler-C_phpeUS.js +0 -16
- package/dist/assets/Settings-BIwZ3FGQ.js +0 -3
- package/src/main/__tests__/activeIndexMerge.test.cjs +0 -235
- package/src/main/__tests__/agentEffortResolve.test.cjs +0 -117
- package/src/main/__tests__/agentLibrary.test.cjs +0 -276
- package/src/main/__tests__/agentModelResolve.test.cjs +0 -311
- package/src/main/__tests__/agentOverlayWrite.test.cjs +0 -95
- package/src/main/__tests__/bilkoHost-deriveSlug.test.cjs +0 -26
- package/src/main/__tests__/bilkoHost-integration.test.cjs +0 -118
- package/src/main/__tests__/bilkoHostCore.test.cjs +0 -72
- package/src/main/__tests__/broadcastCoalescer.test.cjs +0 -122
- package/src/main/__tests__/chat-cancel-terminal.test.cjs +0 -120
- package/src/main/__tests__/chat-dead-channels.test.cjs +0 -63
- package/src/main/__tests__/chat-exit-close-race.test.cjs +0 -146
- package/src/main/__tests__/chat-mcp-consent-notice.test.cjs +0 -139
- package/src/main/__tests__/chat-preamble-anchors.test.cjs +0 -100
- package/src/main/__tests__/chat-queue.test.cjs +0 -97
- package/src/main/__tests__/chat-stop-signal.test.cjs +0 -89
- package/src/main/__tests__/chatRunner-epic-worktree-execcwd.test.cjs +0 -125
- package/src/main/__tests__/chatRunner-session-flag-retry.test.cjs +0 -252
- package/src/main/__tests__/classifyPromptTicket.test.cjs +0 -101
- package/src/main/__tests__/classifyTranscriptLine.test.cjs +0 -201
- package/src/main/__tests__/computeDepHistorySatisfaction.test.cjs +0 -66
- package/src/main/__tests__/config-readText-bounded.test.cjs +0 -84
- package/src/main/__tests__/configWriteBoundaryOwners.test.cjs +0 -59
- package/src/main/__tests__/crossProjectFeedback.test.cjs +0 -334
- package/src/main/__tests__/crossProjectFeedbackRoutes.test.cjs +0 -161
- package/src/main/__tests__/dep-orphan-archive-health.test.cjs +0 -77
- package/src/main/__tests__/develop-skill-failure-modes.test.cjs +0 -70
- package/src/main/__tests__/docEdit.test.cjs +0 -244
- package/src/main/__tests__/dod-batchkey.test.cjs +0 -183
- package/src/main/__tests__/dod-drain-hook.test.cjs +0 -302
- package/src/main/__tests__/dod-report.test.cjs +0 -304
- package/src/main/__tests__/dod-reverify.test.cjs +0 -285
- package/src/main/__tests__/epicContextDigest.test.cjs +0 -174
- package/src/main/__tests__/epicMint.test.cjs +0 -332
- package/src/main/__tests__/epicMintTelemetryTap.test.cjs +0 -64
- package/src/main/__tests__/epicStatusMirror.test.cjs +0 -110
- package/src/main/__tests__/epicValidationHook.test.cjs +0 -291
- package/src/main/__tests__/exchanges.test.cjs +0 -122
- package/src/main/__tests__/exchangesPromptId.test.cjs +0 -61
- package/src/main/__tests__/extractJson.test.cjs +0 -51
- package/src/main/__tests__/files-reject-credentials.test.cjs +0 -40
- package/src/main/__tests__/fixtures/1218-fo-01-move-scripts-lib-into-src-main-lib.log +0 -556
- package/src/main/__tests__/flatPrdTickSweep.test.cjs +0 -110
- package/src/main/__tests__/health-build-freshness.test.cjs +0 -39
- package/src/main/__tests__/health-claude-md-budget.test.cjs +0 -57
- package/src/main/__tests__/health-credentials.test.cjs +0 -81
- package/src/main/__tests__/health-delegation-chain.test.cjs +0 -124
- package/src/main/__tests__/health-per-project-stall.test.cjs +0 -84
- package/src/main/__tests__/health-prd-migration.test.cjs +0 -37
- package/src/main/__tests__/health-queue-dispatch.test.cjs +0 -135
- package/src/main/__tests__/health-starve-escalation.test.cjs +0 -94
- package/src/main/__tests__/health-tick-liveness.test.cjs +0 -179
- package/src/main/__tests__/health-usage-poller.test.cjs +0 -144
- package/src/main/__tests__/health-worktree-cap-blocked.test.cjs +0 -65
- package/src/main/__tests__/heapSnapshot.test.cjs +0 -121
- package/src/main/__tests__/historyAggregatorIntraday.test.cjs +0 -313
- package/src/main/__tests__/historyDashboard.test.cjs +0 -163
- package/src/main/__tests__/historyRollup.test.cjs +0 -333
- package/src/main/__tests__/intradayRefresh.test.cjs +0 -39
- package/src/main/__tests__/ipcSchemas-dependsOn.test.cjs +0 -39
- package/src/main/__tests__/kg-augment.test.cjs +0 -195
- package/src/main/__tests__/loadGateDetailTick.test.cjs +0 -31
- package/src/main/__tests__/machineProfile.test.cjs +0 -152
- package/src/main/__tests__/mcpStatus.test.cjs +0 -61
- package/src/main/__tests__/memoryAggregate.test.cjs +0 -109
- package/src/main/__tests__/memoryStale.test.cjs +0 -88
- package/src/main/__tests__/needsReviewLedger.test.cjs +0 -162
- package/src/main/__tests__/openExternalApp-spawn-error.test.cjs +0 -25
- package/src/main/__tests__/opsErrorLog.test.cjs +0 -109
- package/src/main/__tests__/opsErrorLogTelemetryTap.test.cjs +0 -173
- package/src/main/__tests__/personaMerge.test.cjs +0 -169
- package/src/main/__tests__/planValidator.test.cjs +0 -125
- package/src/main/__tests__/pollLoop-dispatch-on-failure.test.cjs +0 -176
- package/src/main/__tests__/prd-group-allocator.test.cjs +0 -119
- package/src/main/__tests__/prdAdminRouteParity.test.cjs +0 -70
- package/src/main/__tests__/prdAdminRoutes.test.cjs +0 -718
- package/src/main/__tests__/prdAgentType.test.cjs +0 -103
- package/src/main/__tests__/prdAuthoringSeed.test.cjs +0 -39
- package/src/main/__tests__/prdCreate.test.cjs +0 -979
- package/src/main/__tests__/prdCreateAdoption.test.cjs +0 -205
- package/src/main/__tests__/prdCreateDisposition.test.cjs +0 -201
- package/src/main/__tests__/prdCreatePlanId.test.cjs +0 -132
- package/src/main/__tests__/prdFrontmatterAgentType.test.cjs +0 -117
- package/src/main/__tests__/prdFrontmatterDependsOn.test.cjs +0 -136
- package/src/main/__tests__/prdFrontmatterDisposition.test.cjs +0 -125
- package/src/main/__tests__/prdFrontmatterQuietMachine.test.cjs +0 -108
- package/src/main/__tests__/prdLocations.test.cjs +0 -195
- package/src/main/__tests__/prdLocationsArchived.test.cjs +0 -201
- package/src/main/__tests__/prdMigration.test.cjs +0 -349
- package/src/main/__tests__/prdMigrationLegacyAdopt.test.cjs +0 -91
- package/src/main/__tests__/prdParserHighWater.test.cjs +0 -74
- package/src/main/__tests__/prdParserSourcePromptId.test.cjs +0 -65
- package/src/main/__tests__/prdSetDisposition.test.cjs +0 -222
- package/src/main/__tests__/prdSizing.test.cjs +0 -106
- package/src/main/__tests__/prdSourcePromptIdBackfill.test.cjs +0 -118
- package/src/main/__tests__/prdUpdateDependsOn.test.cjs +0 -160
- package/src/main/__tests__/proc-role-env.test.cjs +0 -125
- package/src/main/__tests__/procname-claude-spawn-sites.test.cjs +0 -304
- package/src/main/__tests__/procname-sm-processes.test.cjs +0 -127
- package/src/main/__tests__/projectHomeAdminRoutes.test.cjs +0 -177
- package/src/main/__tests__/projectPages.test.cjs +0 -137
- package/src/main/__tests__/promptSessionEvents.test.cjs +0 -234
- package/src/main/__tests__/promptSessionSchema.test.cjs +0 -101
- package/src/main/__tests__/promptSessionTranscript.test.cjs +0 -0
- package/src/main/__tests__/promptSessionsCreateEpicHandler.test.cjs +0 -159
- package/src/main/__tests__/pty-epic-worktree-spawn-cwd.test.cjs +0 -282
- package/src/main/__tests__/pty-session-open-telemetry.test.cjs +0 -96
- package/src/main/__tests__/pty-write-result.test.cjs +0 -46
- package/src/main/__tests__/queue-health-verdict.test.cjs +0 -170
- package/src/main/__tests__/queue-starvation-dispatch-driver.test.cjs +0 -286
- package/src/main/__tests__/queue-starvation-per-project.test.cjs +0 -147
- package/src/main/__tests__/queueHistory.test.cjs +0 -355
- package/src/main/__tests__/queueOps-interactive-ac-lint.test.cjs +0 -65
- package/src/main/__tests__/queueOpsArchiveDestination.test.cjs +0 -65
- package/src/main/__tests__/queueOpsAutoArchive.test.cjs +0 -211
- package/src/main/__tests__/rateLimitPollerStreak.test.cjs +0 -128
- package/src/main/__tests__/rcaReport.test.cjs +0 -266
- package/src/main/__tests__/reconcileFlatPrdSweep.test.cjs +0 -119
- package/src/main/__tests__/reconcileTiming.test.cjs +0 -135
- package/src/main/__tests__/runLogRetention.test.cjs +0 -489
- package/src/main/__tests__/runVerify-atomic-verdicts.test.cjs +0 -26
- package/src/main/__tests__/runVerify-blocked-by-foreign-wip.test.cjs +0 -58
- package/src/main/__tests__/runVerify-landed-commit-outranks.test.cjs +0 -191
- package/src/main/__tests__/runVerify-policy-denial.test.cjs +0 -89
- package/src/main/__tests__/runVerify-transcript-commit-evidence.test.cjs +0 -225
- package/src/main/__tests__/runVerify.test.cjs +0 -1784
- package/src/main/__tests__/scheduleJobSchema.test.cjs +0 -127
- package/src/main/__tests__/scheduleJobStatusDrift.test.cjs +0 -65
- package/src/main/__tests__/scheduleJobTransitions.test.cjs +0 -277
- package/src/main/__tests__/scheduleJobTransitionsGrep.test.cjs +0 -59
- package/src/main/__tests__/scheduleJobTransitionsTelemetryTap.test.cjs +0 -72
- package/src/main/__tests__/scheduler-admin-routes.test.cjs +0 -199
- package/src/main/__tests__/scheduler-adopted-run-supervision.test.cjs +0 -143
- package/src/main/__tests__/scheduler-already-satisfied-on-main.test.cjs +0 -105
- package/src/main/__tests__/scheduler-archive-completed-prd.test.cjs +0 -102
- package/src/main/__tests__/scheduler-archived-twin-guard.test.cjs +0 -155
- package/src/main/__tests__/scheduler-autofix-outcome.test.cjs +0 -188
- package/src/main/__tests__/scheduler-autofix-select.test.cjs +0 -439
- package/src/main/__tests__/scheduler-autopromote.test.cjs +0 -51
- package/src/main/__tests__/scheduler-bash-timeout-env.test.cjs +0 -101
- package/src/main/__tests__/scheduler-blocked-by-foreign-wip.test.cjs +0 -107
- package/src/main/__tests__/scheduler-boot-orphans.test.cjs +0 -153
- package/src/main/__tests__/scheduler-broadcast-reconcile.test.cjs +0 -121
- package/src/main/__tests__/scheduler-clear-queue-history.test.cjs +0 -134
- package/src/main/__tests__/scheduler-commit-guard-noop.test.cjs +0 -244
- package/src/main/__tests__/scheduler-committed-in-window.test.cjs +0 -182
- package/src/main/__tests__/scheduler-cross-project-batch.test.cjs +0 -43
- package/src/main/__tests__/scheduler-default-eligible-heal.test.cjs +0 -168
- package/src/main/__tests__/scheduler-dispatch-loop.test.cjs +0 -58
- package/src/main/__tests__/scheduler-effective-concurrency.test.cjs +0 -81
- package/src/main/__tests__/scheduler-epic-digest.test.cjs +0 -227
- package/src/main/__tests__/scheduler-failed-autoreset.test.cjs +0 -121
- package/src/main/__tests__/scheduler-finalize-dispatch-guards.test.cjs +0 -229
- package/src/main/__tests__/scheduler-find-prd-dir.test.cjs +0 -75
- package/src/main/__tests__/scheduler-fix-plan-path.test.cjs +0 -119
- package/src/main/__tests__/scheduler-force-tick-outcome.test.cjs +0 -46
- package/src/main/__tests__/scheduler-foreign-wip-manifest.test.cjs +0 -78
- package/src/main/__tests__/scheduler-gate-shadow.test.cjs +0 -119
- package/src/main/__tests__/scheduler-guard-verdict-autoresolve.test.cjs +0 -390
- package/src/main/__tests__/scheduler-heal-refusal.test.cjs +0 -61
- package/src/main/__tests__/scheduler-heartbeat-payload.test.cjs +0 -80
- package/src/main/__tests__/scheduler-inplace-salvage.test.cjs +0 -323
- package/src/main/__tests__/scheduler-integration-failure-stamp.test.cjs +0 -41
- package/src/main/__tests__/scheduler-investigation-clean-skip.test.cjs +0 -63
- package/src/main/__tests__/scheduler-investigation-prompt.test.cjs +0 -123
- package/src/main/__tests__/scheduler-job-budget.test.cjs +0 -172
- package/src/main/__tests__/scheduler-job-overrun.test.cjs +0 -175
- package/src/main/__tests__/scheduler-launch-failure.test.cjs +0 -199
- package/src/main/__tests__/scheduler-leftover-fields.test.cjs +0 -52
- package/src/main/__tests__/scheduler-leftover-quarantine.test.cjs +0 -199
- package/src/main/__tests__/scheduler-looks-done.test.cjs +0 -537
- package/src/main/__tests__/scheduler-manual-pause.test.cjs +0 -118
- package/src/main/__tests__/scheduler-mechanical-recovery.test.cjs +0 -245
- package/src/main/__tests__/scheduler-meta-code-sha.test.cjs +0 -46
- package/src/main/__tests__/scheduler-mutate-reentrancy.test.cjs +0 -117
- package/src/main/__tests__/scheduler-needs-review-autoresolve.test.cjs +0 -197
- package/src/main/__tests__/scheduler-never-stop.test.cjs +0 -157
- package/src/main/__tests__/scheduler-no-dead-end-status.test.cjs +0 -152
- package/src/main/__tests__/scheduler-no-orphan-run-dir.test.cjs +0 -81
- package/src/main/__tests__/scheduler-notify-originating-tab-transcript.test.cjs +0 -87
- package/src/main/__tests__/scheduler-notify-originating-tab.test.cjs +0 -343
- package/src/main/__tests__/scheduler-periodic-reverify-guard.test.cjs +0 -217
- package/src/main/__tests__/scheduler-porcelain-rename.test.cjs +0 -164
- package/src/main/__tests__/scheduler-prd-missing-skip.test.cjs +0 -161
- package/src/main/__tests__/scheduler-prd-persona-spawn.test.cjs +0 -178
- package/src/main/__tests__/scheduler-quarantine-autoresolve.test.cjs +0 -165
- package/src/main/__tests__/scheduler-quiet-machine-lease.test.cjs +0 -257
- package/src/main/__tests__/scheduler-rate-limit-cooldown-freshness.test.cjs +0 -123
- package/src/main/__tests__/scheduler-rate-limit-pause.test.cjs +0 -152
- package/src/main/__tests__/scheduler-rate-limit-spin-guard.test.cjs +0 -156
- package/src/main/__tests__/scheduler-reap-dead-running-jobs.test.cjs +0 -754
- package/src/main/__tests__/scheduler-reaper-helpers-basics.test.cjs +0 -87
- package/src/main/__tests__/scheduler-reconcile-cwd-preserve.test.cjs +0 -100
- package/src/main/__tests__/scheduler-reconcile-history-backfill.test.cjs +0 -105
- package/src/main/__tests__/scheduler-reconcile-invalid-repair.test.cjs +0 -203
- package/src/main/__tests__/scheduler-reconcile-quarantine.test.cjs +0 -247
- package/src/main/__tests__/scheduler-reset-job-fields-guard.test.cjs +0 -77
- package/src/main/__tests__/scheduler-resume-recovery.test.cjs +0 -254
- package/src/main/__tests__/scheduler-shard-quarantine.test.cjs +0 -115
- package/src/main/__tests__/scheduler-shared-tree-guard.test.cjs +0 -380
- package/src/main/__tests__/scheduler-sigterm-commit.test.cjs +0 -43
- package/src/main/__tests__/scheduler-stall-per-project.test.cjs +0 -126
- package/src/main/__tests__/scheduler-starve-escalation.test.cjs +0 -154
- package/src/main/__tests__/scheduler-stranded-autofix-park.test.cjs +0 -252
- package/src/main/__tests__/scheduler-stranded-investigation.test.cjs +0 -185
- package/src/main/__tests__/scheduler-stuck-failed-escalation.test.cjs +0 -136
- package/src/main/__tests__/scheduler-supervisor-record.test.cjs +0 -81
- package/src/main/__tests__/scheduler-tick-cancel-token.test.cjs +0 -54
- package/src/main/__tests__/scheduler-tick-wedge.test.cjs +0 -172
- package/src/main/__tests__/scheduler-transient-failure.test.cjs +0 -141
- package/src/main/__tests__/scheduler-unreadable-queue-guard.test.cjs +0 -62
- package/src/main/__tests__/scheduler-utilization-hold.test.cjs +0 -89
- package/src/main/__tests__/scheduler-verify-prd-path.test.cjs +0 -109
- package/src/main/__tests__/scheduler-worktree-cap-defer.test.cjs +0 -234
- package/src/main/__tests__/scheduler-worktree-exec-cwd.test.cjs +0 -120
- package/src/main/__tests__/scheduler-writeprd-epic-rollback.test.cjs +0 -105
- package/src/main/__tests__/schedulerBatchRootBlocker.test.cjs +0 -117
- package/src/main/__tests__/schedulerStateSidecarRestore.test.cjs +0 -110
- package/src/main/__tests__/seedAgentPersonas.test.cjs +0 -184
- package/src/main/__tests__/seedSchedulerMcp.test.cjs +0 -211
- package/src/main/__tests__/seedStatus.test.cjs +0 -100
- package/src/main/__tests__/seedValidatorPersona.test.cjs +0 -116
- package/src/main/__tests__/stop-signal-anchor.test.cjs +0 -75
- package/src/main/__tests__/telemetryClient.test.cjs +0 -1055
- package/src/main/__tests__/telemetryContract.test.cjs +0 -930
- package/src/main/__tests__/telemetrySettings.test.cjs +0 -210
- package/src/main/__tests__/transcripts-batch-flush.test.cjs +0 -249
- package/src/main/__tests__/transcripts-doFlush-array.test.cjs +0 -124
- package/src/main/__tests__/transcripts-paged-reads.test.cjs +0 -241
- package/src/main/__tests__/transcripts-worktree-epic-path.test.cjs +0 -154
- package/src/main/__tests__/transcriptsUsageFor.test.cjs +0 -206
- package/src/main/__tests__/uniquePrdNumbers.test.cjs +0 -153
- package/src/main/__tests__/usageSingleFlight.test.cjs +0 -169
- package/src/main/__tests__/validationSentinels.test.cjs +0 -84
- package/src/main/__tests__/workTypeLibrary.test.cjs +0 -89
- package/src/main/bilkoHost.cjs +0 -314
- package/src/main/bilkoHostCore.cjs +0 -89
- package/src/main/lib/__tests__/active-sessions.test.cjs +0 -251
- package/src/main/lib/__tests__/activeIndexRebuild.test.cjs +0 -179
- package/src/main/lib/__tests__/agentPersonaSchema.test.cjs +0 -67
- package/src/main/lib/__tests__/auditLog.test.cjs +0 -38
- package/src/main/lib/__tests__/bootSelfHeal.test.cjs +0 -107
- package/src/main/lib/__tests__/branchSweep.test.cjs +0 -164
- package/src/main/lib/__tests__/buildIdentity.test.cjs +0 -121
- package/src/main/lib/__tests__/buildTarget.test.cjs +0 -52
- package/src/main/lib/__tests__/childWithLog.test.cjs +0 -321
- package/src/main/lib/__tests__/coldBootPromptSessionsWrite.test.cjs +0 -87
- package/src/main/lib/__tests__/crashTelemetry.test.cjs +0 -103
- package/src/main/lib/__tests__/credentials-futile-refresh.test.cjs +0 -115
- package/src/main/lib/__tests__/cwdClassify.test.cjs +0 -111
- package/src/main/lib/__tests__/definitionOfDoneSequence.test.cjs +0 -95
- package/src/main/lib/__tests__/delegationReadiness.test.cjs +0 -1175
- package/src/main/lib/__tests__/dispatchLoop.test.cjs +0 -63
- package/src/main/lib/__tests__/effectiveModelInfo.test.cjs +0 -244
- package/src/main/lib/__tests__/ephemeralCwd.test.cjs +0 -91
- package/src/main/lib/__tests__/epicDelegationStats.test.cjs +0 -137
- package/src/main/lib/__tests__/epicSpawnCwd.test.cjs +0 -283
- package/src/main/lib/__tests__/epicSpawnPlan.test.cjs +0 -196
- package/src/main/lib/__tests__/epicTranscriptPath.test.cjs +0 -163
- package/src/main/lib/__tests__/epicWorktreeBoot.test.cjs +0 -136
- package/src/main/lib/__tests__/epicWorktreeMerge.test.cjs +0 -130
- package/src/main/lib/__tests__/epicWorktreeMint.test.cjs +0 -117
- package/src/main/lib/__tests__/epicWorktreeProjectConfig.test.cjs +0 -137
- package/src/main/lib/__tests__/fixChainDepth.test.cjs +0 -40
- package/src/main/lib/__tests__/fixtures/204-mercury-steam-horse.log.txt +0 -13
- package/src/main/lib/__tests__/fixtures/scheduler-machine.json.corrupt-1789147548 +0 -34
- package/src/main/lib/__tests__/gateFixtures.json +0 -20
- package/src/main/lib/__tests__/gitCacheBound.test.cjs +0 -69
- package/src/main/lib/__tests__/gitWorktree.test.cjs +0 -1478
- package/src/main/lib/__tests__/gitWorktreeSalvage.test.cjs +0 -107
- package/src/main/lib/__tests__/gitWorktreeSalvageDelta.test.cjs +0 -153
- package/src/main/lib/__tests__/guardShims.test.cjs +0 -158
- package/src/main/lib/__tests__/importReferences.spec.cjs +0 -56
- package/src/main/lib/__tests__/instanceLock.test.cjs +0 -173
- package/src/main/lib/__tests__/jobSupervisorRecord.test.cjs +0 -78
- package/src/main/lib/__tests__/jobWorktree.test.cjs +0 -199
- package/src/main/lib/__tests__/jobWorktreeBootLive.test.cjs +0 -82
- package/src/main/lib/__tests__/landedSinceRun.test.cjs +0 -133
- package/src/main/lib/__tests__/launchFailure.test.cjs +0 -220
- package/src/main/lib/__tests__/loadGate.test.cjs +0 -303
- package/src/main/lib/__tests__/localAdminHttp.test.cjs +0 -214
- package/src/main/lib/__tests__/loopDelay.test.cjs +0 -68
- package/src/main/lib/__tests__/mcpToolCatalog.test.cjs +0 -107
- package/src/main/lib/__tests__/modelCatalog.test.cjs +0 -202
- package/src/main/lib/__tests__/opsOwnership.test.cjs +0 -113
- package/src/main/lib/__tests__/opsRootAbsoluteCwd.test.cjs +0 -328
- package/src/main/lib/__tests__/opsRootNestedWrite.test.cjs +0 -51
- package/src/main/lib/__tests__/opsRootResolve.test.cjs +0 -149
- package/src/main/lib/__tests__/prdDeclaredPaths.test.cjs +0 -82
- package/src/main/lib/__tests__/prdDisposition.test.cjs +0 -224
- package/src/main/lib/__tests__/procIdentity.test.cjs +0 -119
- package/src/main/lib/__tests__/procName.test.cjs +0 -92
- package/src/main/lib/__tests__/projectBriefCore.test.cjs +0 -216
- package/src/main/lib/__tests__/projectRootResolve.test.cjs +0 -148
- package/src/main/lib/__tests__/queueHealth.test.cjs +0 -58
- package/src/main/lib/__tests__/queueStoreAtomicWrite.test.cjs +0 -88
- package/src/main/lib/__tests__/queueStoreMachineStateRecovery.test.cjs +0 -190
- package/src/main/lib/__tests__/quietMachineLease.test.cjs +0 -39
- package/src/main/lib/__tests__/rateLimitWindow.test.cjs +0 -88
- package/src/main/lib/__tests__/reaperHelpers.test.cjs +0 -577
- package/src/main/lib/__tests__/schedulerBatchDepends.test.cjs +0 -312
- package/src/main/lib/__tests__/schedulerBatchFairness.test.cjs +0 -213
- package/src/main/lib/__tests__/schedulerBatchLaunchHold.test.cjs +0 -125
- package/src/main/lib/__tests__/schedulerBatchProjectCap.test.cjs +0 -127
- package/src/main/lib/__tests__/schedulerBatchQuietMachine.test.cjs +0 -109
- package/src/main/lib/__tests__/schedulerMcpServerHeadlessRefusal.test.cjs +0 -71
- package/src/main/lib/__tests__/schedulerMcpServerHelp.test.cjs +0 -216
- package/src/main/lib/__tests__/schedulerMcpServerProjectHome.test.cjs +0 -183
- package/src/main/lib/__tests__/schedulerPaths.test.cjs +0 -226
- package/src/main/lib/__tests__/schedulerPathsWorktree.test.cjs +0 -133
- package/src/main/lib/__tests__/schedulerRuntimeState.test.cjs +0 -56
- package/src/main/lib/__tests__/sessionSlots.test.cjs +0 -144
- package/src/main/lib/__tests__/telemetryBacklog.test.cjs +0 -626
- package/src/main/lib/__tests__/telemetryBoot.test.cjs +0 -134
- package/src/main/lib/__tests__/telemetryConsent.test.cjs +0 -136
- package/src/main/lib/__tests__/telemetryCounters.test.cjs +0 -57
- package/src/main/lib/__tests__/telemetryCountersMetadataColumn.test.cjs +0 -98
- package/src/main/lib/__tests__/terminalRunOutcome.test.cjs +0 -200
- package/src/main/lib/__tests__/toolUseClassify.test.cjs +0 -53
- package/src/main/lib/__tests__/updateCheck.test.cjs +0 -63
- package/src/main/lib/__tests__/upgradeDrain.test.cjs +0 -130
- package/src/main/lib/__tests__/usageCircuit.test.cjs +0 -354
- package/src/main/lib/__tests__/watchdog-helpers.test.cjs +0 -441
- package/src/main/lib/__tests__/watchdog-relaunch.test.cjs +0 -266
- package/src/main/lib/kgExchangePairing.cjs +0 -75
|
@@ -1,515 +1,235 @@
|
|
|
1
|
-
<!-- PRD_AUTHORING.md
|
|
1
|
+
<!-- PRD_AUTHORING.md v4 -->
|
|
2
2
|
# PRD Authoring Guide — Scheduler Safety Rules
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
Rule: every PRD you queue follows the rules below.
|
|
5
|
+
Why: two real stuck jobs (fizzpop poll-hang, etch-engine post-AC overrun) cost hours of wall-clock time and real money.
|
|
5
6
|
|
|
6
|
-
Before queueing
|
|
7
|
+
Before queueing, run the §15 checklist at the bottom. It is last on purpose — check it last.
|
|
7
8
|
|
|
8
9
|
---
|
|
9
10
|
|
|
10
11
|
## §1 Bounded waits, never unbounded polls
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
Rule: every `until`/`while` loop that polls a network call or external state needs a hard iteration cap. On exhaustion, print a diagnostic line and move on — never spin forever.
|
|
14
|
+
Why: `106-fizzpop-publish` polled `curl .../health | jq .uptime` for a value that could never drop (a static-content deploy doesn't restart the API). It hung 2h47m until the 4h watchdog killed it.
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
Pattern:
|
|
15
17
|
```bash
|
|
16
|
-
# WRONG — what 106-fizzpop-publish did
|
|
17
|
-
PREV=288694
|
|
18
|
-
until [ "$(curl -s https://bilko.run/api/health | jq .uptime)" -lt "$PREV" ]; do
|
|
19
|
-
sleep 15
|
|
20
|
-
done
|
|
21
|
-
# Outcome: 2h47m hang because static-content Render deploys never restart the Node API
|
|
22
|
-
# so `uptime` never dropped. Loop hung until the 4h watchdog SIGKILLed.
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
**Recommended pattern:**
|
|
26
|
-
```bash
|
|
27
|
-
# RIGHT — bounded, surfaces failure cleanly
|
|
28
18
|
for i in $(seq 1 20); do
|
|
29
|
-
if curl -sf https://
|
|
30
|
-
echo "
|
|
19
|
+
if curl -sf https://example.com/health > /dev/null; then
|
|
20
|
+
echo "live (attempt $i)"; break
|
|
31
21
|
fi
|
|
32
|
-
echo "waiting
|
|
33
|
-
sleep 15
|
|
22
|
+
echo "waiting ($i/20)..."; sleep 15
|
|
34
23
|
done
|
|
35
|
-
#
|
|
24
|
+
# Continue regardless — the smoke test (§3) catches a real failure.
|
|
36
25
|
```
|
|
37
26
|
|
|
38
|
-
|
|
27
|
+
Recommended cap: 20 × 15s = 5 min for HTTP polls. Never use an uptime/restart signal to detect a static-content deploy — check the actual URL that should be live.
|
|
39
28
|
|
|
40
29
|
---
|
|
41
30
|
|
|
42
|
-
## §2
|
|
31
|
+
## §2 Stop at the acceptance checklist
|
|
43
32
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
**Anti-example** (verbatim from `112-etch-engine.md`):
|
|
47
|
-
```bash
|
|
48
|
-
# WRONG — what 112-etch-engine did after declaring success
|
|
49
|
-
for (let seed = 100; seed < 50000 && !found; seed++) {
|
|
50
|
-
const result = generateRandom({ size, rng, maxAttempts: 3 });
|
|
51
|
-
if (result.ok && result.solution) { found = true; ... }
|
|
52
|
-
}
|
|
53
|
-
# Outcome: 2h44m of token burn looking for fixtures the AC did not require.
|
|
54
|
-
# The agent had emitted result-success at 17:44 UTC; the bonus loop ran until 20:28.
|
|
55
|
-
```
|
|
33
|
+
Rule: once every acceptance-criteria line is checked, follow the finish protocol (§11) and stop. Do not add polish, fixtures, or "while we're here" work that isn't an AC line.
|
|
34
|
+
Why: `112-etch-engine` declared success at 17:44 UTC, then ran an unbounded fixture search until a user killed it at 20:28 — 2h44m of token burn on work no AC line asked for.
|
|
56
35
|
|
|
57
|
-
|
|
36
|
+
Executor: if more work seems valuable, name it in your report as a follow-up. Do not do it, and do not queue it. Why: only the planner queues work.
|
|
58
37
|
|
|
59
38
|
---
|
|
60
39
|
|
|
61
|
-
## §3
|
|
62
|
-
|
|
63
|
-
**Summary:** Prefer "do thing, run a test that asserts thing happened" over "do thing, spin until I detect thing happened."
|
|
40
|
+
## §3 Verify with a real test, not a spin-wait
|
|
64
41
|
|
|
65
|
-
|
|
42
|
+
Rule: after a deploy, migration, or build step, run a command that exits non-zero on failure (`curl -sf`, `npm test`, `tsc --noEmit`).
|
|
43
|
+
Why: a poll that waits for a condition hides the real error; a test command shows it.
|
|
66
44
|
|
|
67
|
-
**Pattern:**
|
|
68
45
|
```bash
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
curl -sf https://bilko.run/projects/fizzpop/ > /dev/null || { echo "smoke test FAILED: fizzpop not reachable"; exit 1; }
|
|
46
|
+
curl -sf https://example.com/projects/foo/ > /dev/null \
|
|
47
|
+
|| { echo "smoke test FAILED: foo not reachable"; exit 1; }
|
|
72
48
|
```
|
|
73
49
|
|
|
74
50
|
---
|
|
75
51
|
|
|
76
|
-
## §4 Bound
|
|
77
|
-
|
|
78
|
-
**Summary:** When iterating a search space (fixtures, seeds, brute-force), declare the maximum search size in the PRD AC and surface and HALT on exhaustion.
|
|
79
|
-
|
|
80
|
-
**Anti-example:** Same etch-engine fixture generator from §2 — `for (let seed = 100; seed < 50000 ...)` with no AC line constraining it.
|
|
52
|
+
## §4 Bound search and generator loops
|
|
81
53
|
|
|
82
|
-
|
|
54
|
+
Rule: when you iterate a search space (fixtures, seeds, brute force), the acceptance criteria must state the max attempts. On exhaustion, print a HALT line and exit 1 — never loop past the stated bound.
|
|
55
|
+
Why: the same etch-engine loop (§2) had no bound in its AC, so nothing told the executor when to stop.
|
|
83
56
|
|
|
84
|
-
**Pattern:**
|
|
85
57
|
```ts
|
|
86
|
-
let
|
|
87
|
-
for (let seed = 0; seed < MAX_SEEDS && !found; seed++) {
|
|
88
|
-
// ...
|
|
89
|
-
}
|
|
58
|
+
for (let seed = 0; seed < MAX_SEEDS && !found; seed++) { /* ... */ }
|
|
90
59
|
if (!found) {
|
|
91
|
-
console.error(`HALT: exhausted ${MAX_SEEDS} seeds without
|
|
60
|
+
console.error(`HALT: exhausted ${MAX_SEEDS} seeds without a valid fixture`);
|
|
92
61
|
process.exit(1);
|
|
93
62
|
}
|
|
94
63
|
```
|
|
95
64
|
|
|
96
65
|
---
|
|
97
66
|
|
|
98
|
-
## §5
|
|
99
|
-
|
|
100
|
-
**Summary:** Render (and similar) deploys may take 2–10 minutes and may silently fail. Always bound the wait and follow it with a live endpoint test.
|
|
67
|
+
## §5 Frontmatter
|
|
101
68
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if curl -sf https://bilko.run/projects/<slug>/ > /dev/null; then
|
|
107
|
-
DEPLOY_OK=1; echo "deploy live (attempt $i)"; break
|
|
108
|
-
fi
|
|
109
|
-
echo "waiting for deploy ($i/20)..."
|
|
110
|
-
sleep 15
|
|
111
|
-
done
|
|
112
|
-
# Continue regardless. Smoke test below catches the real failure.
|
|
113
|
-
if [ $DEPLOY_OK -eq 0 ]; then
|
|
114
|
-
echo "WARNING: deploy not detected after 20 attempts; continuing to smoke test"
|
|
115
|
-
fi
|
|
116
|
-
curl -sf https://bilko.run/projects/<slug>/ > /dev/null || { echo "SMOKE TEST FAILED"; exit 1; }
|
|
117
|
-
```
|
|
69
|
+
Rule: every PRD needs these fields.
|
|
70
|
+
1. `title` — one line, plain English.
|
|
71
|
+
2. `cwd` — the Epic's project root, as an absolute path or `~/…`. The API always sets it to the Epic's project, whatever you pass. To work in another project, open an Epic in that project. The folder must exist when you queue.
|
|
72
|
+
3. `estimateMinutes` — a realistic integer. Most PRDs take 5–10 minutes (§8) — don't inflate it.
|
|
118
73
|
|
|
119
|
-
|
|
74
|
+
`parallelGroup` is deprecated and ignored. `dependsOn: [<slug>, ...]` is the only ordering primitive.
|
|
120
75
|
|
|
121
|
-
|
|
76
|
+
`planId` is written by the API, never by you. It groups PRDs into the plan (wave) they belong to: an `append` PRD, or one with an explicit `dependsOn`, inherits the `planId` of the PRD it attaches behind; a fresh PRD mints a new one.
|
|
122
77
|
|
|
123
|
-
|
|
78
|
+
Artifact-only PRDs (`deliverable: artifact` + `artifactPaths: [...]`) — use this ONLY when every deliverable is a file the repo deliberately git-excludes, so "no commit" is the correct outcome, not a miss.
|
|
79
|
+
1. Every artifact path is listed in `artifactPaths` and is relative, never `..`.
|
|
80
|
+
2. Each file must be non-empty and written during the run window — the verifier stat-checks it on disk.
|
|
81
|
+
3. The tree must still end clean. A declared artifact excuses the missing commit, not a stray tracked edit.
|
|
82
|
+
4. Both fields are required together — passing one without the other is refused.
|
|
124
83
|
|
|
125
|
-
|
|
84
|
+
Why: PRD `816-prepare-157-copy-citation-extras-patch` wrote its patch into a git-excluded folder with no way to declare that, and parked `needs_review` twice.
|
|
126
85
|
|
|
127
|
-
**Required frontmatter:**
|
|
128
|
-
```yaml
|
|
129
|
-
---
|
|
130
|
-
title: <one line, plain English>
|
|
131
|
-
cwd: ~/Projects/<target-repo>
|
|
132
|
-
estimateMinutes: 10
|
|
133
86
|
---
|
|
134
|
-
```
|
|
135
87
|
|
|
136
|
-
|
|
88
|
+
## §6 Gate and files
|
|
89
|
+
|
|
90
|
+
Both fields are required in every `scheduler_create_prd` call.
|
|
137
91
|
|
|
138
|
-
|
|
139
|
-
- `cwd` MUST point to the target project. Prefer `~/...` for portability; only use an absolute path if you have a specific reason to pin to one machine.
|
|
140
|
-
- **`cwd` MUST already exist on disk at queue time.** The scheduler runs a dead-cwd guard (`fs.accessSync(cwd, fs.constants.X_OK)` in `src/main/scheduler.cjs:669-680`) *before* spawning the child, so a PRD whose `cwd` references a not-yet-created directory will exit with `-1: cwd no longer exists` and the body will never run — even if the first step of the body would have created the directory. If the PRD's purpose is to create a brand-new sibling project at `~/Projects/<new-slug>/`, point `cwd` at the parent (`~/Projects`) and make the first executable step `mkdir -p ~/Projects/<new-slug> && cd ~/Projects/<new-slug>`.
|
|
141
|
-
- `estimateMinutes` is used for ETA display; include a realistic estimate (note: empirical median is ~10 min, p90 ~20 min — avoid wildly inflated estimates that hide real outliers).
|
|
142
|
-
- `parallelGroup` is DEPRECATED and ignored (PRD 832) — `dependsOn: [<slug>, …]` is the only ordering primitive; numbers are unique per project.
|
|
92
|
+
### gate
|
|
143
93
|
|
|
144
|
-
|
|
94
|
+
1. 1–10 commands. The scheduler re-runs them, in order, after the executor finishes. Each must exit 0.
|
|
95
|
+
2. Start each `&&` step with `timeout <seconds>`, for example `timeout 300 npm test && timeout 120 npm run lint`. Why: a command without a timeout can hang the run.
|
|
96
|
+
3. Join steps inside one entry with `&&`.
|
|
97
|
+
4. The scheduler runs gate commands without a shell, so shell syntax is refused. Outside single quotes, do not use `|` `<` `>` `;` `&` (only `&&` between steps), backticks, `$`, `\`, `*`, `?`, `[`, `]`, `(`, `)`, `{`, `}` or `!`. Inside double quotes, `$`, backticks and `\` are refused too.
|
|
98
|
+
5. Do not start a word with `#` or `~`, and do not put `~` right after `=` or `:`. `HEAD~1` is fine.
|
|
99
|
+
6. Put text with these characters inside single quotes, for example `rg -n 'a|b' src/`. A check that needs a shell belongs in a test file that the gate runs.
|
|
100
|
+
7. Keep each entry on one line, at most 500 chars, with plain spaces between words.
|
|
101
|
+
8. Use `["none"]` only for docs or config with no runnable check. Write exactly `none`. Never mix `none` with commands.
|
|
102
|
+
9. Put `NAME=value` words before `timeout`, never after it, for example `CI=1 timeout 300 npm test`. A leading `TMPDIR=$(mktemp -d) ` is allowed but not needed: the scheduler gives each gate its own TMPDIR.
|
|
145
103
|
|
|
146
|
-
|
|
104
|
+
### files
|
|
147
105
|
|
|
148
|
-
|
|
106
|
+
1. 1–50 repo-relative paths. A folder ends with `/`.
|
|
107
|
+
2. No absolute paths, no `~` at the start, no `..` or `.` segments, no `*` or `?`, no `\` or backticks, no leading `-`. Use `/` between folders.
|
|
108
|
+
3. PRDs that can run at the same time must not share a file. If two PRDs touch the same file, chain them with `dependsOn`.
|
|
149
109
|
|
|
150
|
-
|
|
110
|
+
### What the API writes
|
|
151
111
|
|
|
152
|
-
|
|
153
|
-
- The artifact must be non-empty and written during the run window — the verifier stat-checks it on disk (verdict `pass_no_commit_artifact_verified`; window = start−60s..finish+120s).
|
|
154
|
-
- The run must still leave the working tree clean — declaring artifacts excuses the missing commit, not stray tracked edits. The commit guard enforces this: the `pass_no_commit_artifact_verified` verdict stands the guard down only when nothing tracked was left dirty; any uncommitted tracked change still parks as `uncommitted_changes`.
|
|
155
|
-
- Both fields are required together: the write is refused if only one is given.
|
|
112
|
+
The API writes the frontmatter and renders two body sections from the fields above: `# Files` (right after `# Acceptance criteria`) and `# Gate` (after `# Out of scope`, before `## Engineering standards`). Do not write those two sections yourself. Do not put a gate fence (three backticks + `gate`), a `# Gate` heading or a `# Files` heading in any text field — the API rejects that. For `["none"]`, the Gate section says the PRD has no runnable check and to verify each AC line by reading the files, with the fence holding the single word `none`.
|
|
156
113
|
|
|
157
|
-
|
|
114
|
+
### Acceptance criteria
|
|
115
|
+
|
|
116
|
+
Plain, checkable statements. Commands go in `gate`, not in the criteria.
|
|
158
117
|
|
|
159
118
|
---
|
|
160
119
|
|
|
161
120
|
## §7 Self-containment
|
|
162
121
|
|
|
163
|
-
|
|
122
|
+
Rule: the PRD body is the executor's entire context — it runs as `claude -p "<body>"` with no conversation history. Include exact file paths, signatures, library versions, and any sibling PRD not to duplicate. Never write "the conversation" or "the design doc" — if the executor would need to search for an answer, put the answer in the PRD.
|
|
164
123
|
|
|
165
|
-
|
|
124
|
+
A short Epic-context digest is prepended automatically for orientation only — never load-bearing; write the body as if it won't be there.
|
|
166
125
|
|
|
167
|
-
|
|
126
|
+
The executor can't ask you anything: no `ScheduleWakeup`, Cron, Monitor, `AskUserQuestion`, plan mode, or worktree tools; background tasks are off (a `timeout` command is always on PATH — the app installs a shim on macOS). It must never stop to ask a question — it makes the safest reasonable call and reports it, so write the PRD so that call is obvious.
|
|
168
127
|
|
|
169
128
|
---
|
|
170
129
|
|
|
171
|
-
## §8 Scope sizing — target ≤10 min, ceiling 15
|
|
130
|
+
## §8 Scope sizing — target ≤10 min, ceiling 15
|
|
172
131
|
|
|
173
|
-
|
|
132
|
+
Rule: one PRD is ≤10 wall-clock minutes of work. If you project more than 15, split it into sequential PRDs and link them with `dependsOn`.
|
|
133
|
+
Why: 2026-09 data put wall p50 at 7.8 min, with 60% of runs ≤10 min — authored estimates ran 4× too high.
|
|
174
134
|
|
|
175
|
-
|
|
135
|
+
e2e and publish work is the long tail: shard test suites to one spec per PRD. Never run a full suite, or an endpoint-polling publish, in a single PRD (see §1/§3).
|
|
176
136
|
|
|
177
137
|
---
|
|
178
138
|
|
|
179
139
|
## §9 Failure surfacing
|
|
180
140
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
**Rule:** When a step fails, print a single diagnostic line and exit 1. The scheduler marks the job `failed` and the investigator Claude reads the log. A clean failure message is worth more than a 15-minute silent retry loop. Do not swallow errors with `|| true` unless the failure is genuinely non-fatal and you explain why.
|
|
141
|
+
Rule: when a step fails, print one diagnostic line and exit 1. Don't swallow errors with `|| true` unless the failure is genuinely non-fatal — say why inline when you do.
|
|
142
|
+
Why: a clean failure is worth more than a 15-minute silent retry loop; the scheduler marks the job `failed` and the investigator reads the log.
|
|
184
143
|
|
|
185
144
|
```bash
|
|
186
|
-
# RIGHT
|
|
187
145
|
npm test || { echo "HALT: npm test failed — see above"; exit 1; }
|
|
188
|
-
|
|
189
|
-
# WRONG
|
|
190
|
-
npm test || true # silently continues even if tests are broken
|
|
191
146
|
```
|
|
192
147
|
|
|
148
|
+
Note: a `rateLimited` exit-1 is the scheduler's own benign auto-pause (it resumes at the next 5h reset) — don't engineer retry logic for it.
|
|
149
|
+
|
|
193
150
|
---
|
|
194
151
|
|
|
195
|
-
## §10
|
|
152
|
+
## §10 Negative-assertion checks must exit 0 on the clean case
|
|
196
153
|
|
|
197
|
-
|
|
154
|
+
Rule: a check that asserts something is ABSENT must exit 0 when it's absent. Write it as `if <detector>; then echo HALT...; exit 1; fi` — never leave the no-match path carrying the non-zero exit.
|
|
155
|
+
Why: `grep` exits 1 when it finds nothing. PRD `62-x-trader-doctrine` cleaned up correctly, but its sanity `grep` for a banned phrase found nothing, exited 1, and a perfect run was flagged `needs_review` by the verifier's `transcript_errors` check.
|
|
198
156
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
- [ ] **§6 Frontmatter:** `title`, `cwd` (`~/Projects/<name>` preferred; path MUST exist on this machine), `estimateMinutes` present. `parallelGroup` is deprecated — use `dependsOn` for ordering.
|
|
206
|
-
- [ ] **§7 Self-contained:** No references to "the conversation" or external context. Paths and identifiers are inline. Body is clean UTF-8 — **no NUL/control bytes** (paste-from-PDF crashes the spawn). Quick check: `grep -qP '\x00' file && echo BAD`.
|
|
207
|
-
- [ ] **§8 Scope:** Targets ≤10 min, ceiling 15. If projected larger, split. e2e/publish sharded to one spec per PRD.
|
|
208
|
-
- [ ] **§9 Failure surfacing:** Errors exit 1 with a diagnostic line. No silent `|| true` swallows. (`rateLimited` exit-1 is benign auto-pause, not a failure.)
|
|
209
|
-
- [ ] **§11 Negative-assertion checks:** Any "this should produce NO output / NO match" check (a `grep` that should find nothing, a "no leftover X" guard) is written as an inverted conditional that exits 0 on the clean case. A bare `grep` whose success is "no match" exits 1 and trips the verifier `transcript_errors` downgrade even when the run is perfect.
|
|
210
|
-
- [ ] **§12 End green:** The acceptance/test gate is the LAST thing the run does; any intentionally-failing step (TDD red test, expected-nonzero probe) runs EARLY, never after the gate, and is captured (`2>&1 | tail` inside a conditional) so it doesn't surface as a bare `is_error`/`Traceback` in the final portion of the transcript.
|
|
211
|
-
- [ ] **§13 Recover/annotate errors & expected timeouts:** A throwaway probe that errors is re-run corrected (or annotated `# expected/handled`) right after — never left stranded; prefer a temp `.py` over a fragile inline `python -c`. An *expected* `timeout` cap (a long ingest/scan) handles exit 124 explicitly as success-with-note, not a bare `Exit code 124`. Both prevent the `transcript_errors` downgrade of a green deliverable.
|
|
157
|
+
```bash
|
|
158
|
+
if grep -rniE "banned phrase" path/; then
|
|
159
|
+
echo "HALT: banned phrase still present"; exit 1
|
|
160
|
+
fi
|
|
161
|
+
echo "clean"
|
|
162
|
+
```
|
|
212
163
|
|
|
213
|
-
|
|
164
|
+
Applies to `grep`, `rg`, `diff` (exits 1 on any difference), and any custom detector.
|
|
214
165
|
|
|
215
|
-
|
|
166
|
+
---
|
|
216
167
|
|
|
217
|
-
|
|
218
|
-
thing is absent. The classic trap is `grep`: it exits **1 when it finds no match**. If your
|
|
219
|
-
AC says "verify no banned phrase remains" and you write a bare `grep`, the *success* path
|
|
220
|
-
(nothing found) surfaces as `is_error=true` in the transcript — and the verifier's
|
|
221
|
-
`transcript_errors` heuristic downgrades the whole run to `needs_review` even though it did
|
|
222
|
-
everything right.
|
|
168
|
+
## §11 End green, and trust the verdict line
|
|
223
169
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
`grep -rniE "building in public|..." data/pipelines/x_session/` — found nothing, exited 1,
|
|
227
|
-
and a perfect run was flagged for review. Self-inflicted, by the PRD author.
|
|
170
|
+
Rule: order the run so the acceptance/test gate is the LAST command. Run any intentionally-failing step (a TDD red test, an expected-nonzero probe) EARLY, and capture its output (`2>&1 | tail` inside a conditional) so a bare `Traceback`/`is_error` never lands in the final part of the transcript.
|
|
171
|
+
Why: the post-run verifier scans the transcript and downgrades to `needs_review` on error markers — it can't tell an intentional failure from a real one.
|
|
228
172
|
|
|
229
173
|
```bash
|
|
230
|
-
#
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
if grep -rniE "building in public|indie hacker" data/pipelines/x_session/; then
|
|
235
|
-
echo "HALT: banned builder framing still present (see matches above)"; exit 1
|
|
236
|
-
fi
|
|
237
|
-
echo "clean: no banned framing"
|
|
238
|
-
|
|
239
|
-
# ALSO RIGHT — grep -q with negation, when you don't need to see the matches
|
|
240
|
-
grep -rqniE "building in public|indie hacker" data/pipelines/x_session/ \
|
|
241
|
-
&& { echo "HALT: banned framing present"; exit 1; } || echo "clean"
|
|
174
|
+
# RIGHT — red demo first and captured, green gate last
|
|
175
|
+
timeout 120 python -m pytest tests/test_repro.py::test_bug 2>&1 | tail -3 || true # expected red
|
|
176
|
+
# ... implement the fix ...
|
|
177
|
+
timeout 300 pytest -q # LAST thing the run does
|
|
242
178
|
```
|
|
243
179
|
|
|
244
|
-
|
|
245
|
-
appear", "no leftover…", write it as `if <detector>; then echo HALT…; exit 1; fi`. Never let
|
|
246
|
-
the no-match/empty-output path be the one that carries a non-zero exit. Applies to `grep`,
|
|
247
|
-
`rg`, `find ... | grep`, `diff` (exits 1 on differences), and any custom detector.
|
|
180
|
+
The scheduler appends a finish protocol after your PRD's own steps — you never write it: review, then the `# Gate` commands (§6), then commit only the exact paths you created or changed for this PRD, then the verdict line — `SCHEDULER_VERDICT: PASS` once the gate is green and the commit landed, else `SCHEDULER_VERDICT: FAIL <one-line reason>`. The verifier trusts a truthful `PASS` plus a landed commit over stray transcript markers. Never print `PASS` on a red gate.
|
|
248
181
|
|
|
249
182
|
---
|
|
250
183
|
|
|
251
|
-
## §12
|
|
184
|
+
## §12 Don't strand mid-run probe errors; annotate expected timeouts
|
|
252
185
|
|
|
253
|
-
|
|
254
|
-
`needs_review` on error markers (`Traceback`+`Error`, `FAIL`/`FATAL`, a tool `is_error` in the
|
|
255
|
-
final portion of the run). It cannot tell an *intentional* failure from a real one. Two rules
|
|
256
|
-
keep legitimate runs from false-tripping it.
|
|
257
|
-
|
|
258
|
-
**12a — Run the green gate LAST.** Order the run so the final command is the acceptance/test
|
|
259
|
-
gate. Do any intentionally-failing step EARLY:
|
|
186
|
+
Rule: a throwaway probe that errors (a bad quote, a wrong kwarg) must be re-run corrected right after, or annotated `# expected/handled: <why>` on the next line — never left stranded. Prefer a temp `.py` file over a fragile inline `python -c` one-liner.
|
|
260
187
|
|
|
188
|
+
Rule: an *expected* `timeout` cap (a long ingest/scan you expect to hit it) is success-with-note, not a bare `Exit code 124` — branch on it explicitly:
|
|
261
189
|
```bash
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
# RIGHT — red demo first (and captured), green gate last
|
|
267
|
-
python -m pytest tests/test_repro.py::test_bug 2>&1 | tail -3 || true # expected red, captured
|
|
268
|
-
# ... implement the fix ...
|
|
269
|
-
timeout 300 pytest -q # ← LAST thing the run does; ends green
|
|
190
|
+
timeout 120 python -m project.ingest --all || { rc=$?
|
|
191
|
+
[ $rc -eq 124 ] && echo "hit time cap — partial, rows persist; OK" \
|
|
192
|
+
|| { echo "HALT: ingest failed rc=$rc"; exit 1; }; }
|
|
270
193
|
```
|
|
194
|
+
Why: both a stranded probe traceback and a bare `Exit code 124` read as failure to the verifier even when the committed work is correct and green (PRDs 77, 80 — 2026-06-13).
|
|
271
195
|
|
|
272
|
-
|
|
273
|
-
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## §13 Queueing PRDs from external automation
|
|
274
199
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
`PASS` + a commit landed during the run as the **authoritative** signal and overrides incidental
|
|
279
|
-
transcript markers — this is what lets a deliberately-reproduced red test (PRD 77) or a grep
|
|
280
|
-
result containing "Error" (PRD 68) finish `completed` instead of `needs_review`. **Never print
|
|
281
|
-
`PASS` on a red gate.** The sentinel is only a safety net while it tells the truth; a lying
|
|
282
|
-
`PASS` converts the verifier from "catches false failures" into "ships silent failures."
|
|
200
|
+
Decision: is the session-manager app running on this machine right now?
|
|
201
|
+
- Yes → call the `scheduler_create_prd` MCP tool, with the usual fields (`title`, `cwd`, `estimateMinutes`, `sourcePromptId`, `goal`, `acceptanceCriteria`, `implementationNotes`) plus `gate` and `files` (§6).
|
|
202
|
+
- No → stop. Ask the human to start the app, then call the tool again. Never write the PRD file by hand: the scheduler quarantines a file the API did not write, and it never runs.
|
|
283
203
|
|
|
284
|
-
|
|
285
|
-
suites, but 77's `systematic-debugging` red-test repro and 68's grep-"Error" substring each
|
|
286
|
-
tripped `transcript_errors → needs_review`, and the self-heal pass kept re-deriving the same
|
|
287
|
-
verdict from the immutable log — stuck indefinitely. §12 (end-green + authoritative sentinel)
|
|
288
|
-
is the structural fix.
|
|
204
|
+
It only works while the app is running — closing it removes the admin port/token, and the call fails with `session-manager app is not running (admin API unreachable)`.
|
|
289
205
|
|
|
290
206
|
---
|
|
291
207
|
|
|
292
|
-
## §
|
|
208
|
+
## §14 A parked job may resolve itself now
|
|
293
209
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
even when the deliverable is correct and committed.
|
|
210
|
+
Rule: if a job parks `needs_review` with verdict `transcript_errors`, `no_verdict_sentinel`, or `abandoned_background_task`, do nothing first. The scheduler re-runs the gate on its own and completes the job once the gate is green, the commit is on HEAD, and the tracked tree is clean.
|
|
211
|
+
Why: those three verdicts mean the transcript looked noisy, not that the work was wrong.
|
|
297
212
|
|
|
298
|
-
|
|
299
|
-
`python -c`/`bash` probes that error (a quoting/f-string slip, a wrong kwarg, a bad path) leave a
|
|
300
|
-
bare traceback. Re-run the corrected probe immediately, or print `# expected/handled: <why>` on
|
|
301
|
-
the next line, so recovery is adjacent (the heuristic looks for recovery within ~10 lines).
|
|
302
|
-
Prefer a small temp `.py` file over a fragile multi-quote `python -c` one-liner — inline
|
|
303
|
-
f-string/quoting errors are the top source of stranded probe tracebacks.
|
|
213
|
+
Rule: a park caused by a DIFFERENT actor already finishing the same objective (a sibling PRD, a human) does not self-heal this way. Why: the job has no landed commit of its own. Confirm in the tree that the work is really done, then call `scheduler_archive_prd` with the slug. Archiving marks the job completed and frees the PRDs that depend on it. Never hand-edit `queue.json`: the scheduler and the watchdog both write it. Archiving clears a stale label; it does not fix a real bug.
|
|
304
214
|
|
|
305
|
-
|
|
306
|
-
# WRONG — inline f-string slip strands a SyntaxError, then you move on
|
|
307
|
-
python -c 'print(f"{p["title"]!r[:40]}")' # SyntaxError, bare in transcript
|
|
308
|
-
|
|
309
|
-
# RIGHT — write the probe to a temp file (no shell-quote minefield), or annotate
|
|
310
|
-
cat > /tmp/probe.py <<'PY'
|
|
311
|
-
print(repr(p["title"])[:40])
|
|
312
|
-
PY
|
|
313
|
-
python /tmp/probe.py || echo "# expected/handled: probe only, not part of the deliverable"
|
|
314
|
-
```
|
|
215
|
+
Known gap: `pass_no_commit` ("already correct, nothing to commit") isn't yet in the self-heal list above for the general case.
|
|
315
216
|
|
|
316
|
-
|
|
317
|
-
genuinely long task you expect to hit the cap (a full-universe ingest, a long scan) is the
|
|
318
|
-
correct §1/§8 behavior — but a bare `Exit code 124` reads as failure to the verifier. Branch on
|
|
319
|
-
124 explicitly:
|
|
217
|
+
---
|
|
320
218
|
|
|
321
|
-
|
|
322
|
-
timeout 120 python -m project.ingest --all || { rc=$?
|
|
323
|
-
[ $rc -eq 124 ] && echo "hit time cap — idempotent/partial; rows persist incrementally; OK" \
|
|
324
|
-
|| { echo "HALT: ingest failed rc=$rc"; exit 1; }; }
|
|
325
|
-
```
|
|
219
|
+
## §15 Pre-queue checklist (the litany)
|
|
326
220
|
|
|
327
|
-
|
|
328
|
-
bounded number of times (§1) rather than capping the foreground command.
|
|
329
|
-
|
|
330
|
-
**This actually happened** (PRDs 77 + 80, 2026-06-13): 77 stranded an inline-`python -c` f-string
|
|
331
|
-
`SyntaxError` from a throwaway permalink probe; 80 surfaced a bare `Exit code 124` from a
|
|
332
|
-
`timeout`-capped full-universe EDGAR ingest. Both committed correct, green, AC-complete work
|
|
333
|
-
(77's cursor-hold fix; 80's 6 EDGAR rows + installed cron) yet were downgraded to `needs_review`
|
|
334
|
-
on the incidental middle-of-run markers. 13a/13b keep the middle of the transcript clean.
|
|
335
|
-
|
|
336
|
-
## §14 Queueing PRDs from external automation
|
|
337
|
-
|
|
338
|
-
**Summary:** A downstream project (Connector Atlas, `gh-issue-5`) wanted a Slack-feedback → PRD
|
|
339
|
-
loop but had no documented programmatic queueing path. This section is that path.
|
|
340
|
-
|
|
341
|
-
**Decision, up front:** Is the session-manager Electron app running on this machine right now?
|
|
342
|
-
- **Yes** → call the `scheduler_create_prd` MCP tool.
|
|
343
|
-
- **No** → write the PRD file by hand into the prds directory, and accept the collision risk
|
|
344
|
-
described below.
|
|
345
|
-
|
|
346
|
-
### The `scheduler_create_prd` MCP tool
|
|
347
|
-
|
|
348
|
-
Wraps `POST /admin/scheduler/create-prd` on the loopback admin API
|
|
349
|
-
(`src/main/lib/localAdminHttp.cjs` + `src/main/lib/prdCreate.cjs`, PRD 549/688) via `scripts/scheduler-mcp-server.cjs`, registered in this
|
|
350
|
-
repo's `.mcp.json` as the `session-manager-scheduler` MCP server. An external project wanting to
|
|
351
|
-
call it from its own automation needs the equivalent MCP server registration pointing at this
|
|
352
|
-
repo's `scripts/scheduler-mcp-server.cjs`, or can call the admin HTTP route directly (same
|
|
353
|
-
request/response shape) using the token at `~/.claude/session-manager/admin-api.json`.
|
|
354
|
-
|
|
355
|
-
**Input** (validated server-side by `ipcSchemas.cjs`'s `schemas.schedulerCreatePrd`):
|
|
356
|
-
|
|
357
|
-
| field | type | required | notes |
|
|
358
|
-
|---|---|---|---|
|
|
359
|
-
| `title` | string | yes | one-line title, no newlines |
|
|
360
|
-
| `cwd` | string | yes | absolute path to the target project; validated via `config.cjs`'s `validatePath` (allowedRoots = home dir) |
|
|
361
|
-
| `estimateMinutes` | number | yes | integer wall-clock estimate |
|
|
362
|
-
| `goal` | string | yes | 2–4 sentences: what the executor builds and why |
|
|
363
|
-
| `acceptanceCriteria` | string[] | yes | 1–100 entries, each one verifiable checklist line |
|
|
364
|
-
| `implementationNotes` | string | yes | file paths, patterns, constraints the executor needs |
|
|
365
|
-
| `outOfScope` | string[] | no | what NOT to build |
|
|
366
|
-
| `slug` | string | no | kebab-case; derived from `title` if omitted |
|
|
367
|
-
| `parallelGroup` | number | no | opt into an existing `NN` group instead of allocating a new one |
|
|
368
|
-
|
|
369
|
-
**Return** (`{nn, filename, status}`, per `prdCreate.cjs`'s registerAdminRoute):
|
|
370
|
-
```json
|
|
371
|
-
{ "nn": 550, "filename": "550-my-feature.md", "status": "queued" }
|
|
372
|
-
```
|
|
373
|
-
On failure the tool returns `{ ok: false, error: "..." }` (e.g. `409` if `filename` already
|
|
374
|
-
exists, `400` if `cwd` is rejected by `validatePath` or the payload fails schema validation).
|
|
375
|
-
|
|
376
|
-
**Worked example** (MCP tool call, e.g. from Claude Code or any MCP client):
|
|
377
|
-
```json
|
|
378
|
-
{
|
|
379
|
-
"tool": "scheduler_create_prd",
|
|
380
|
-
"arguments": {
|
|
381
|
-
"title": "Sync Slack #feedback channel into feedback intake",
|
|
382
|
-
"cwd": "~/Projects/connector-atlas",
|
|
383
|
-
"estimateMinutes": 20,
|
|
384
|
-
"goal": "Pull unread messages from the #feedback Slack channel and materialize each as a feedback file, deduped against already-tracked message ts.",
|
|
385
|
-
"acceptanceCriteria": [
|
|
386
|
-
"New feedback files land in connector-atlas's own feedback intake folder, one per undeduped message",
|
|
387
|
-
"Each file's `source` field is `slack-<channel>-<ts>` for future dedup",
|
|
388
|
-
"timeout 300 npm run typecheck passes"
|
|
389
|
-
],
|
|
390
|
-
"implementationNotes": "Use the Slack Web API conversations.history endpoint; token lives in connector-atlas's own secrets store, not session-manager's."
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
```
|
|
394
|
-
Server-side, this atomically allocates the `NN` prefix (`allocateParallelGroup()`, PRD 548),
|
|
395
|
-
appends the engineering standards block, and writes the PRD file — the same shape `/develop`
|
|
396
|
-
produces by hand.
|
|
397
|
-
|
|
398
|
-
### The app-must-be-running caveat (read this first)
|
|
399
|
-
|
|
400
|
-
**`scheduler_create_prd` only works while the session-manager Electron app is running on this
|
|
401
|
-
machine.** The admin server it depends on (`src/main/lib/localAdminHttp.cjs`) is hosted *inside* the
|
|
402
|
-
Electron process — it binds a loopback port and writes its token to
|
|
403
|
-
`~/.claude/session-manager/admin-api.json` on app boot (see `CLAUDE.md`'s `localAdminHttp.cjs`
|
|
404
|
-
architecture entry) and stops existing the moment the app quits — it is not a standalone daemon.
|
|
405
|
-
If the app is closed, `scheduler-mcp-server.cjs` cannot read a live port/token and every call
|
|
406
|
-
returns the error `session-manager app is not running (admin API unreachable) — start it first`.
|
|
407
|
-
This is the single most likely point of confusion for an automation author who assumes the tool
|
|
408
|
-
is a normal always-on API — it is not; it is a convenience surface hosted by a desktop app that
|
|
409
|
-
the user may or may not have open.
|
|
410
|
-
|
|
411
|
-
### Fallback: writing the PRD file directly
|
|
412
|
-
|
|
413
|
-
When the app is not running, first join the EXISTING, already-human-approved Epic you're already
|
|
414
|
-
working inside — `node <session-manager-repo>/scripts/mint-epic.cjs <cwd> <epic-id>`; its last
|
|
415
|
-
stdout line is the prds dir. This only joins; it never creates an Epic, and errors out if
|
|
416
|
-
`<epic-id>` doesn't already exist — get a human to create/approve the Epic first (New Epic UI, or
|
|
417
|
-
`/propose-epic` + Approve & start) if it doesn't. Then write `<NN>-<slug>.md` by hand into that
|
|
418
|
-
`<cwd>/session-manager-operations/scheduler/epics/<epic-id>/prds/` dir (the flat `scheduler/prds/` is RETIRED and auto-archived unexecuted at boot), add `sourcePromptId: <epic-id>` to the frontmatter so the job keeps its Epic linkage, following the frontmatter rules in §6 and the
|
|
419
|
-
body conventions the rest of this guide describes (`# Goal`, `# Acceptance criteria`,
|
|
420
|
-
`# Implementation notes`, `## Engineering standards` inlined verbatim — see `/develop`'s output
|
|
421
|
-
for the exact shape). **Trade-off:** this path has no atomic `NN` allocation. The tool's
|
|
422
|
-
`allocateParallelGroup()` (PRD 548) exists specifically to close a race where two writers pick
|
|
423
|
-
the same `NN` at once; a hand-written file bypasses that reservation entirely, so if another
|
|
424
|
-
writer (a human, `/develop`, or another automation) picks the same `NN` around the same time, one
|
|
425
|
-
file silently shadows or is shadowed by the other's number. Pick an `NN` by scanning ALL of the
|
|
426
|
-
project's prds dirs (`scheduler/epics/*/prds/` and `prds-archived/`) for the current max and
|
|
427
|
-
incrementing, and treat a collision as possible, not merely theoretical. NN is strictly unique
|
|
428
|
-
per project (PRD 832) — NEVER reuse an existing number to signal "runs in parallel"; that
|
|
429
|
-
convention is retired. Express ordering with `dependsOn: [<slug>, ...]` frontmatter (the job is
|
|
430
|
-
eligible once every listed slug's queue row is completed); independent PRDs omit it and the
|
|
431
|
-
scheduler may run them concurrently.
|
|
432
|
-
|
|
433
|
-
### Ownership boundary
|
|
434
|
-
|
|
435
|
-
Projects own their own source adapters — Slack, GitHub, Linear, email, whatever inbound channel
|
|
436
|
-
they read. Session-manager owns the queueing API (`scheduler_create_prd` / the admin route) and
|
|
437
|
-
nothing upstream of it. Session-manager does not host, run, or import another project's adapter
|
|
438
|
-
code; the adapter runs entirely inside the calling project (its own cron, its own credentials,
|
|
439
|
-
its own filtering/triage logic) and only reaches into session-manager at the single, narrow
|
|
440
|
-
`scheduler_create_prd` call.
|
|
441
|
-
|
|
442
|
-
### Why project-supplied `automation-hooks.js` was declined
|
|
443
|
-
|
|
444
|
-
Connector Atlas's third ask was a mechanism to drop a project-supplied `automation-hooks.js` file
|
|
445
|
-
that session-manager would load and execute in-process on a timer. This was declined, and should
|
|
446
|
-
not be re-proposed:
|
|
447
|
-
|
|
448
|
-
- It would grant main-process privileges (full filesystem access, IPC, the admin server's own
|
|
449
|
-
token) to arbitrary code from any project directory, invoked on a schedule the *project*
|
|
450
|
-
controls rather than the *user*.
|
|
451
|
-
- It inverts this project's core invariants: `config.cjs`'s `validatePath` gate on every
|
|
452
|
-
filesystem path, `ipcSchemas.cjs`'s zod-validated IPC boundary, and `CLAUDE.md`'s Avoid-list
|
|
453
|
-
ban on `shell: true` outside the two features that legitimately need it. A loaded-and-executed
|
|
454
|
-
project JS file has no equivalent boundary to pass through — it *is* the process.
|
|
455
|
-
- The supported extension point is the MCP tool described above, called from *outside* the
|
|
456
|
-
session-manager process by the project's own cron/automation. That keeps the privilege boundary
|
|
457
|
-
where it already is (the loopback admin server, token-authed, narrow three-route surface) rather
|
|
458
|
-
than dissolving it.
|
|
459
|
-
|
|
460
|
-
### Reference implementation of this exact loop
|
|
461
|
-
|
|
462
|
-
The proposal → approve → `/develop` → queue path is a working example of this
|
|
463
|
-
shape, landed 2026-07-14 (`feat(process-feedback): sync open GitHub issues into the feedback
|
|
464
|
-
intake`, commit `352b89c`). It syncs open GitHub issues into `session-manager-operations/feedback/`
|
|
465
|
-
(deduped on a `gh-issue-<N>` token), then processes each item through the same triage → `/develop`
|
|
466
|
-
→ queue path the rest of this guide documents. An external project building a Slack (or any
|
|
467
|
-
other) adapter should follow the same source → triage → queue shape, ending at
|
|
468
|
-
`scheduler_create_prd` (app running) or a hand-written PRD file (app closed) as described above.
|
|
469
|
-
|
|
470
|
-
## §15 Resolving a `failed`/`needs_review` job whose target work is already done
|
|
471
|
-
|
|
472
|
-
**Symptom:** a job lands in `failed` or `needs_review` even though its actual objective was
|
|
473
|
-
already satisfied — either by a sibling/concurrent job that finished the same work first (a
|
|
474
|
-
duplicate PRD racing another one), or by an out-of-band actor (a human, another agent) reaching
|
|
475
|
-
the same target state before the scheduler's run even started. The job's own transcript may be
|
|
476
|
-
completely accurate (`SCHEDULER_VERDICT: PASS but no commit landed during the run window`) — the
|
|
477
|
-
"failure" is a stale queue entry, not broken work.
|
|
478
|
-
|
|
479
|
-
**Do NOT hand-edit `queue.json` to fix the status.** It's a live file the running Electron
|
|
480
|
-
scheduler process (and the external watchdog) both read and write; a manual edit races the app's
|
|
481
|
-
own save cycle and risks a torn write. There is also no supported IPC/CLI surface today to flip a
|
|
482
|
-
single job's `status` field directly.
|
|
483
|
-
|
|
484
|
-
**The safe, supported remediation — confirmed working live (2026-07-18):** archive the job's PRD
|
|
485
|
-
*source file*, not the queue entry. `reconcile()` (`src/main/scheduler.cjs`) runs on every queue
|
|
486
|
-
read/tick and drops any `queue.json` job entry whose PRD `.md` no longer exists in `prds/` — so
|
|
487
|
-
archiving the file is sufficient; you never touch `queue.json` yourself.
|
|
488
|
-
|
|
489
|
-
```js
|
|
490
|
-
// From this repo's root (session-manager), or anywhere queueOps.cjs is reachable:
|
|
491
|
-
const q = require('./src/main/queueOps.cjs');
|
|
492
|
-
await q.archiveMany(['<slug-of-the-stale-job>']);
|
|
493
|
-
// Atomic rename to prds-archived/<ISO>/<slug>.md — reversible, path-contained,
|
|
494
|
-
// no queue.json write. The next reconcile() (within one scheduler tick, ~10-15s
|
|
495
|
-
// observed) drops the matching queue.json job entry automatically.
|
|
496
|
-
```
|
|
221
|
+
Before queueing a new PRD, verify each of these:
|
|
497
222
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
does not yet special-case "another actor already satisfied this PRD's postcondition" for
|
|
512
|
-
externally-checkable targets (e.g. a `gh pr view`-mergeable branch). `RESCANNABLE_VERDICTS`
|
|
513
|
-
already includes `pass_no_commit` for one narrow case (fix-plan jobs, exempted 2026-07-12) but
|
|
514
|
-
not the general case. See `session-manager-operations/feedback/processed/` for the tracked
|
|
515
|
-
follow-up on softening this classification for merge-style PRDs with a checkable postcondition.
|
|
223
|
+
- [ ] **§1 Bounded waits:** every poll loop has a `for i in $(seq 1 N)` cap ≤20 iterations; no uptime/restart signal used to detect a static-content deploy.
|
|
224
|
+
- [ ] **Every command bounded:** every test/build/deploy command is wrapped in `timeout` (typecheck/unit 300s, e2e 120s, `curl --max-time 15`).
|
|
225
|
+
- [ ] **§2 No bonus work:** the AC list is the only source of work.
|
|
226
|
+
- [ ] **§3 Verify, don't poll:** every deploy/migration step is followed by a test command that exits 1 on failure; the AC test command ran green once before you declare done.
|
|
227
|
+
- [ ] **§4 Bounded generators:** any search/seed loop has an explicit max and surfaces failure on exhaustion.
|
|
228
|
+
- [ ] **§5 Frontmatter:** `title`, `cwd` (exists on this machine), `estimateMinutes` present; `dependsOn` used for ordering, not `parallelGroup`.
|
|
229
|
+
- [ ] **§6 Gate and files:** `gate` follows every rule in §6 (or is `["none"]` alone); `files` is 1–50 repo-relative paths, no overlap with a concurrent PRD.
|
|
230
|
+
- [ ] **§7 Self-contained:** no reference to "the conversation" or outside context; paths/identifiers inline; clean UTF-8, no NUL bytes (`grep -qP '\x00' file && echo BAD`).
|
|
231
|
+
- [ ] **§8 Scope:** targets ≤10 min, ceiling 15; e2e/publish sharded to one spec per PRD.
|
|
232
|
+
- [ ] **§9 Failure surfacing:** errors exit 1 with a diagnostic line; no silent `|| true`.
|
|
233
|
+
- [ ] **§10 Negative assertions:** every "should find nothing" check is inverted to exit 0 on the clean case.
|
|
234
|
+
- [ ] **§11 End green:** the gate runs last; any intentional failure runs early and is captured.
|
|
235
|
+
- [ ] **§12 No stranded probes:** a probe error is corrected or annotated; an expected `timeout` cap branches on exit 124.
|