claude-code-session-manager 0.75.0 → 0.75.1

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.
Files changed (32) hide show
  1. package/dist/assets/{AgentLibrary-SpzptNyq.js → AgentLibrary-CGplUKxY.js} +1 -1
  2. package/dist/assets/{DataModel-C4M9xVLC.js → DataModel-DQ15KxVb.js} +1 -1
  3. package/dist/assets/{History-92FYrZKS.js → History-v2TSC_6A.js} +1 -1
  4. package/dist/assets/{Hooks-DlnavO1l.js → Hooks-Co7s6BNB.js} +1 -1
  5. package/dist/assets/{HostBilko-CKI7jt1z.js → HostBilko-DJj8FWPK.js} +1 -1
  6. package/dist/assets/{Library-Brhxe7xa.js → Library-DQaC9VQ1.js} +1 -1
  7. package/dist/assets/{ListDetail-DmIweYTN.js → ListDetail-BwFRfAZm.js} +1 -1
  8. package/dist/assets/{MarkdownEditor-DA4xqWWi.js → MarkdownEditor-C155gNpc.js} +1 -1
  9. package/dist/assets/{McpServers-DWXQGzVc.js → McpServers-BmLgE_gS.js} +1 -1
  10. package/dist/assets/{Memory-HCx9Arao.js → Memory-ISwNcS5j.js} +1 -1
  11. package/dist/assets/{Panel-B20C6kN8.js → Panel-1adjEpj-.js} +1 -1
  12. package/dist/assets/{Permissions-C4sPgLpA.js → Permissions-FrCz5cGa.js} +1 -1
  13. package/dist/assets/{Plugins-CaRenCBg.js → Plugins-zUssKp_b.js} +2 -2
  14. package/dist/assets/{ProvenanceBadge-DfrXzTeh.js → ProvenanceBadge-CAXqA_HX.js} +1 -1
  15. package/dist/assets/{SaveBar-BmpUx2Yh.js → SaveBar-D9cqms3Y.js} +1 -1
  16. package/dist/assets/{Scheduler-xUD8pQ6q.js → Scheduler-dNNc9S11.js} +1 -1
  17. package/dist/assets/{ScopeSwitcher-DVlKMck5.js → ScopeSwitcher-1wBTne7S.js} +1 -1
  18. package/dist/assets/{Settings-C457mxOK.js → Settings-BT_expIg.js} +1 -1
  19. package/dist/assets/{SkillReferenceGraph-D3dDc5X7.js → SkillReferenceGraph-CVccq4Cu.js} +1 -1
  20. package/dist/assets/{Skills-BNDlbftC.js → Skills-xFpRY0ld.js} +1 -1
  21. package/dist/assets/{SystemPrompt-d-ykNRec.js → SystemPrompt-CEk_71th.js} +1 -1
  22. package/dist/assets/{TagLibrary-D3JWkQK-.js → TagLibrary-C2zhihQG.js} +1 -1
  23. package/dist/assets/{TiptapBody-B1Ox6D6E.js → TiptapBody-CQKcHKaD.js} +1 -1
  24. package/dist/assets/{Toggle-CaYw6Lrj.js → Toggle-CQ-IBb8m.js} +1 -1
  25. package/dist/assets/{index-CEeLxNCz.js → index-BgbV3sKX.js} +3 -3
  26. package/dist/assets/{settingsSchema-DUkd45M5.js → settingsSchema-D6ccSKqm.js} +1 -1
  27. package/dist/index.html +1 -1
  28. package/package.json +1 -1
  29. package/src/main/__tests__/scheduler-autofix-outcome.test.cjs +116 -0
  30. package/src/main/__tests__/scheduler-autofix-select.test.cjs +113 -7
  31. package/src/main/__tests__/scheduler-fix-plan-path.test.cjs +119 -0
  32. package/src/main/scheduler.cjs +136 -27
@@ -2289,6 +2289,37 @@ async function resolveVerifyPrdPath(job) {
2289
2289
  return liveDir ? safeSlugPathIn(liveDir, job.slug) : null;
2290
2290
  }
2291
2291
 
2292
+ /**
2293
+ * resolveFixPlanPath(failedJob) → { fixPath, fixSlug, group, livePrdDir }
2294
+ *
2295
+ * Where spawnInvestigation's auto-fix plan for `failedJob` must be written.
2296
+ * The plan belongs in the SAME dir the original PRD lives in right now
2297
+ * (`livePrdDir`, from resolveVerifyPrdPath's live-dir search — usually the
2298
+ * originating Epic's `epics/<epicId>/prds/`), never the retired flat dir:
2299
+ * consolidateFlatPrds (prdMigration.cjs) sweeps only the flat dir and
2300
+ * archives any `.md` there with no live queue row, which a freshly-authored
2301
+ * fix plan never has yet — so writing it flat means the very next
2302
+ * reconcile() pass (src/main/scheduler.cjs's consolidateAllFlatPrds call)
2303
+ * archives it before it can ever be enqueued. `livePrdDir` is null (and the
2304
+ * flat dir, `prdDirForCwd`, is used instead) only when the original PRD
2305
+ * can't be found live anywhere on disk — an archived twin is deliberately
2306
+ * NOT treated as a live location for this purpose.
2307
+ *
2308
+ * Exported as a pure(ish) helper (one fs-free branch, one await) so tests
2309
+ * can prove the round trip — fix plan lands beside its original and
2310
+ * survives a consolidateFlatPrds(cwd) pass — without spawning a real
2311
+ * investigation.
2312
+ */
2313
+ async function resolveFixPlanPath(failedJob) {
2314
+ const livePrdPath = await resolveVerifyPrdPath(failedJob);
2315
+ const livePrdDir = livePrdPath ? path.dirname(livePrdPath) : null;
2316
+ const baseSlug = failedJob.slug.replace(/^\d+-/, '');
2317
+ const group = failedJob.parallelGroup ?? 99;
2318
+ const fixSlug = `${String(group).padStart(2, '0')}-fix-${baseSlug}`;
2319
+ const fixPath = path.join(livePrdDir ?? prdDirForCwd(failedJob.cwd), `${fixSlug}.md`);
2320
+ return { fixPath, fixSlug, group, livePrdDir };
2321
+ }
2322
+
2292
2323
  /**
2293
2324
  * notifyOriginatingTab(job) → void
2294
2325
  *
@@ -2980,6 +3011,16 @@ function isFixPlanSlug(slug) {
2980
3011
  return /^\d+-fix-/.test(slug);
2981
3012
  }
2982
3013
 
3014
+ /**
3015
+ * The fix-plan slug spawnInvestigation authors for a given failed job —
3016
+ * shared by selectAutoFixTargets (queue-membership check) and
3017
+ * reverifyNeedsReview's autofix_plan_unqueued annotation (same check, run
3018
+ * one or more passes later) so both use one slug formula.
3019
+ */
3020
+ function fixSlugFor(job) {
3021
+ return `${String(job.parallelGroup ?? 99).padStart(2, '0')}-fix-${job.slug.replace(/^\d+-/, '')}`;
3022
+ }
3023
+
2983
3024
  /**
2984
3025
  * Returns true for statuses that a fix-plan completion should promote
2985
3026
  * (clear) on the original job. Both 'failed' and 'needs_review' are
@@ -3165,9 +3206,18 @@ async function spawnInvestigation(failedJob, runDir) {
3165
3206
  const failedLogPath = path.join(runDir, `${failedJob.slug}.log`);
3166
3207
  const investigationLogPath = path.join(runDir, `${failedJob.slug}.investigation.log`);
3167
3208
 
3209
+ // livePrdDir is the SAME dir the original PRD lives in right now (usually
3210
+ // its Epic-scoped `epics/<epicId>/prds/`) — the fix plan must land there
3211
+ // too, since consolidateFlatPrds only ever sweeps the retired flat dir
3212
+ // (prdDirForCwd) and would archive a freshly-authored fix plan on the very
3213
+ // next reconcile() pass before it ever gets a queue row. Only fall back to
3214
+ // the flat dir when the original PRD can't be found on disk at all — an
3215
+ // archived twin (archivedPrdPathForJob) is deliberately NOT used for the
3216
+ // write dir, only for reading originalBody below.
3168
3217
  let originalBody = '';
3218
+ const { fixPath, fixSlug, group, livePrdDir } = await resolveFixPlanPath(failedJob);
3169
3219
  try {
3170
- const originalPath = (await resolveVerifyPrdPath(failedJob)) ?? archivedPrdPathForJob(failedJob);
3220
+ const originalPath = livePrdDir ? path.join(livePrdDir, `${failedJob.slug}.md`) : archivedPrdPathForJob(failedJob);
3171
3221
  originalBody = (await parsePrd(originalPath)).body;
3172
3222
  } catch {
3173
3223
  originalBody = failedJob.bodyPreview || '(original PRD missing from disk)';
@@ -3175,11 +3225,6 @@ async function spawnInvestigation(failedJob, runDir) {
3175
3225
 
3176
3226
  const logTail = readTail(failedLogPath, 16 * 1024) || '(failed to read log)';
3177
3227
 
3178
- const baseSlug = failedJob.slug.replace(/^\d+-/, '');
3179
- const group = failedJob.parallelGroup ?? 99;
3180
- const fixSlug = `${String(group).padStart(2, '0')}-fix-${baseSlug}`;
3181
- const fixPath = path.join(prdDirForCwd(failedJob.cwd), `${fixSlug}.md`);
3182
-
3183
3228
  if (fs.existsSync(fixPath)) {
3184
3229
  console.log(`[scheduler] skip investigation: fix plan already exists at ${fixPath}`);
3185
3230
  releaseSlot();
@@ -3281,6 +3326,13 @@ async function spawnInvestigation(failedJob, runDir) {
3281
3326
  ? `investigation spawn failed: ${error?.message ?? String(error)}`
3282
3327
  : `investigation error: ${error.message}`;
3283
3328
  sl(`\n[scheduler] ${errMsg}\n`);
3329
+ // Stamp the outcome even on this early exit — otherwise the job is
3330
+ // left with autoFixAttempted: true and no outcome, which used to
3331
+ // read as a permanent dead-end (see selectAutoFixTargets).
3332
+ mutate((s) => {
3333
+ const j = s.jobs.find((x) => x.slug === failedJob.slug);
3334
+ if (j) j.autoFixOutcome = 'error';
3335
+ }).catch(() => {});
3284
3336
  return;
3285
3337
  }
3286
3338
  sl(`\n[scheduler] investigation exit code=${exitCode}\n`);
@@ -3301,6 +3353,10 @@ async function spawnInvestigation(failedJob, runDir) {
3301
3353
  }
3302
3354
  if (fs.existsSync(fixPath)) {
3303
3355
  console.log(`[scheduler] investigation produced fix plan: ${fixSlug}`);
3356
+ mutate((s) => {
3357
+ const j = s.jobs.find((x) => x.slug === failedJob.slug);
3358
+ if (j) j.autoFixOutcome = 'plan';
3359
+ }).catch(() => {});
3304
3360
  } else {
3305
3361
  console.log(`[scheduler] investigation finished WITHOUT producing fix plan (slug=${failedJob.slug}, code=${exitCode})`);
3306
3362
  // Record the no-plan outcome so selectAutoFixTargets can offer one
@@ -3711,10 +3767,10 @@ async function spawnJob(job, runId, runDir, defaultCwd) {
3711
3767
  process.env.SM_AUTOFIX_DISABLE !== '1' &&
3712
3768
  isEligibleForImmediateAutoFix(s.jobs[i2], s.jobs, fixSlugExists)
3713
3769
  ) {
3714
- const isNoPlanRetry = s.jobs[i2].autoFixAttempted && s.jobs[i2].autoFixOutcome === 'no-plan';
3770
+ const isRetryAttempt = s.jobs[i2].autoFixAttempted === true;
3715
3771
  s.jobs[i2].autoFixAttempted = true;
3716
3772
  if (!s.jobs[i2].runId) s.jobs[i2].runId = runId;
3717
- if (isNoPlanRetry) {
3773
+ if (isRetryAttempt) {
3718
3774
  s.jobs[i2].autoFixRetries = (s.jobs[i2].autoFixRetries ?? 0) + 1;
3719
3775
  delete s.jobs[i2].autoFixOutcome;
3720
3776
  }
@@ -4337,6 +4393,35 @@ function isUnresolvableNeedsReview(job, { hasRunDir }) {
4337
4393
  return !!job && job.status === 'needs_review' && !job.runId && !hasRunDir;
4338
4394
  }
4339
4395
 
4396
+ /**
4397
+ * Pure predicate: an auto-fix attempt whose one bounded retry is already
4398
+ * spent, and whose outcome was NOT 'plan' (a produced plan is judged by
4399
+ * isPlanUnqueued instead — a plan is never retried, only annotated if it
4400
+ * never reaches the queue). Covers all three exhaustible outcomes
4401
+ * ('no-plan', 'error', and unstamped/undefined) — mirrors the retry
4402
+ * eligibility rule in selectAutoFixTargets so a job can never be retry-
4403
+ * eligible there and simultaneously un-annotatable here.
4404
+ */
4405
+ function isExhaustedAutoFix(job) {
4406
+ return !!job && job.status === 'needs_review'
4407
+ && job.autoFixAttempted === true
4408
+ && job.autoFixOutcome !== 'plan'
4409
+ && (job.autoFixRetries ?? 0) >= 1;
4410
+ }
4411
+
4412
+ /**
4413
+ * Pure predicate: an investigation produced a fix plan (autoFixOutcome ===
4414
+ * 'plan') but its fix-plan slug is not present among `queuedSlugs` — the
4415
+ * plan file failed to become a queue row (write failure, or some other
4416
+ * reason reconcile() never ingested it). `queuedSlugs` is the caller's
4417
+ * current job-slug set, passed in rather than read here so this stays pure.
4418
+ */
4419
+ function isPlanUnqueued(job, queuedSlugs) {
4420
+ if (!job || job.status !== 'needs_review') return false;
4421
+ if (job.autoFixOutcome !== 'plan') return false;
4422
+ return !queuedSlugs.has(fixSlugFor(job));
4423
+ }
4424
+
4340
4425
  /**
4341
4426
  * Pure predicate: is this job eligible for the boot re-verify self-heal? Only
4342
4427
  * needs_review jobs with a run log (own or backfilled via resolveRunId) AND a
@@ -4417,9 +4502,13 @@ function isRescanCandidate(job) {
4417
4502
  * - a runId, own or backfilled via resolveJobRunId (need a run log to investigate)
4418
4503
  * - not itself a fix-plan slug (avoids infinite recursion)
4419
4504
  * - autoFixAttempted cap: a job with no prior attempt is eligible; a job
4420
- * whose prior attempt outcome was 'no-plan' gets ONE bounded retry
4421
- * (autoFixRetries < 1); any other attempted job (or an exhausted
4422
- * no-plan retry) is excluded
4505
+ * whose prior attempt outcome was 'no-plan', 'error', or UNSTAMPED
4506
+ * (investigation exited without recording any outcome — unknown is not
4507
+ * "succeeded") gets ONE bounded retry (autoFixRetries < 1); a job whose
4508
+ * outcome was 'plan' (a fix plan was produced) is never retried here —
4509
+ * it either lands as a queue row on its own or gets flagged
4510
+ * autofix_plan_unqueued by reverifyNeedsReview's annotation pass; an
4511
+ * exhausted retry is excluded
4423
4512
  * - no fix sibling on disk (fixSlugExists) or already in the queue
4424
4513
  */
4425
4514
  function selectAutoFixTargets(jobs, { fixSlugExists, resolveJobRunId = resolveRunId }) {
@@ -4430,10 +4519,13 @@ function selectAutoFixTargets(jobs, { fixSlugExists, resolveJobRunId = resolveRu
4430
4519
  if (!runId) return false;
4431
4520
  if (isFixPlanBeyondDepthCap(job.slug, job.investigationDepth)) return false;
4432
4521
  if (job.autoFixAttempted) {
4433
- if (job.autoFixOutcome !== 'no-plan') return false;
4522
+ const retryEligible = job.autoFixOutcome === 'no-plan'
4523
+ || job.autoFixOutcome === 'error'
4524
+ || job.autoFixOutcome == null;
4525
+ if (!retryEligible) return false;
4434
4526
  if ((job.autoFixRetries ?? 0) >= 1) return false;
4435
4527
  }
4436
- const fixSlug = `${String(job.parallelGroup ?? 99).padStart(2, '0')}-fix-${job.slug.replace(/^\d+-/, '')}`;
4528
+ const fixSlug = fixSlugFor(job);
4437
4529
  if (fixSlugExists(fixSlug)) return false;
4438
4530
  if (slugsInQueue.has(fixSlug)) return false;
4439
4531
  return true;
@@ -4561,20 +4653,29 @@ async function reverifyNeedsReview() {
4561
4653
 
4562
4654
  // Surface needs_review jobs that can never self-heal or get an auto-fix
4563
4655
  // investigation (no runId, no backfillable run dir) instead of leaving
4564
- // them silently stranded. Also surface no-plan auto-fix jobs whose one
4565
- // bounded retry is already exhausted. Both are annotated, never looped on.
4656
+ // them silently stranded. Also surface auto-fix jobs whose one bounded
4657
+ // retry is already exhausted (outcome 'no-plan', 'error', or unstamped —
4658
+ // selectAutoFixTargets treats all three as retry-eligible, so all three
4659
+ // must also be annotatable once the retry is spent), and jobs whose
4660
+ // investigation DID produce a fix plan that never became a queue row
4661
+ // (write failed, or reconcile hasn't picked it up for some other reason).
4662
+ // All three classes are annotated, never looped on — a needs_review job
4663
+ // must not exit this pass both un-retried and un-annotated.
4566
4664
  const afterHealForAnnotate = await readQueue();
4567
4665
  const unresolvable = afterHealForAnnotate.jobs.filter(
4568
4666
  (j) => isUnresolvableNeedsReview(j, { hasRunDir: !!resolveRunId(j) }) && j.verifierVerdict !== 'no_run_artifacts',
4569
4667
  );
4570
- const exhaustedNoPlan = afterHealForAnnotate.jobs.filter(
4571
- (j) => j.status === 'needs_review'
4572
- && j.autoFixAttempted && j.autoFixOutcome === 'no-plan' && (j.autoFixRetries ?? 0) >= 1
4573
- && j.verifierVerdict !== 'autofix_no_plan',
4668
+ const exhaustedAutoFix = afterHealForAnnotate.jobs.filter(
4669
+ (j) => isExhaustedAutoFix(j) && j.verifierVerdict !== 'autofix_no_plan',
4670
+ );
4671
+ const queuedSlugs = new Set(afterHealForAnnotate.jobs.map((j) => j.slug));
4672
+ const planUnqueued = afterHealForAnnotate.jobs.filter(
4673
+ (j) => isPlanUnqueued(j, queuedSlugs) && j.verifierVerdict !== 'autofix_plan_unqueued',
4574
4674
  );
4575
- if (unresolvable.length || exhaustedNoPlan.length) {
4675
+ if (unresolvable.length || exhaustedAutoFix.length || planUnqueued.length) {
4576
4676
  const unresolvableSet = new Set(unresolvable.map((j) => j.slug));
4577
- const exhaustedSet = new Set(exhaustedNoPlan.map((j) => j.slug));
4677
+ const exhaustedSet = new Set(exhaustedAutoFix.map((j) => j.slug));
4678
+ const planUnqueuedSet = new Set(planUnqueued.map((j) => j.slug));
4578
4679
  await mutate((s) => {
4579
4680
  for (const j of s.jobs) {
4580
4681
  if (unresolvableSet.has(j.slug)) {
@@ -4583,14 +4684,22 @@ async function reverifyNeedsReview() {
4583
4684
  } else if (exhaustedSet.has(j.slug)) {
4584
4685
  j.verifierVerdict = 'autofix_no_plan';
4585
4686
  j.error = 'auto-fix investigation produced no fix plan after retry — manual review';
4687
+ } else if (planUnqueuedSet.has(j.slug)) {
4688
+ j.verifierVerdict = 'autofix_plan_unqueued';
4689
+ j.error = 'auto-fix investigation produced a fix plan that never reached the queue — manual review';
4586
4690
  }
4587
4691
  }
4588
4692
  });
4589
4693
  if (unresolvable.length) {
4590
4694
  console.log(`[scheduler] boot reverify: no run artifacts for ${unresolvable.map((j) => j.slug).join(', ')} — flagged for manual review`);
4591
4695
  }
4592
- if (exhaustedNoPlan.length) {
4593
- console.log(`[scheduler] boot reverify: auto-fix retry exhausted for ${exhaustedNoPlan.map((j) => j.slug).join(', ')} — flagged for manual review`);
4696
+ if (exhaustedAutoFix.length) {
4697
+ console.log(`[scheduler] boot reverify: auto-fix retry exhausted for ${exhaustedAutoFix.map((j) => j.slug).join(', ')} — flagged for manual review`);
4698
+ }
4699
+ if (planUnqueued.length) {
4700
+ for (const j of planUnqueued) {
4701
+ console.log(`[scheduler] boot reverify: auto-fix plan never queued for ${j.slug} — flagged for manual review`);
4702
+ }
4594
4703
  }
4595
4704
  await broadcast();
4596
4705
  }
@@ -4608,7 +4717,7 @@ async function reverifyNeedsReview() {
4608
4717
  for (const job of targets) {
4609
4718
  const runId = job.runId || resolveRunId(job);
4610
4719
  const runDir = path.join(RUNS_DIR, runId);
4611
- const isNoPlanRetry = job.autoFixAttempted && job.autoFixOutcome === 'no-plan';
4720
+ const isRetryAttempt = job.autoFixAttempted === true;
4612
4721
  // Persist the attempt BEFORE spawning — a crash mid-investigation still
4613
4722
  // counts it (mirrors orphanRetries). Safe even when the slot is busy: the
4614
4723
  // investigation is queued and drained as slots free, so it is genuinely
@@ -4618,13 +4727,13 @@ async function reverifyNeedsReview() {
4618
4727
  if (j) {
4619
4728
  j.autoFixAttempted = true;
4620
4729
  if (!j.runId && runId) j.runId = runId;
4621
- if (isNoPlanRetry) {
4730
+ if (isRetryAttempt) {
4622
4731
  j.autoFixRetries = (j.autoFixRetries ?? 0) + 1;
4623
4732
  delete j.autoFixOutcome;
4624
4733
  }
4625
4734
  }
4626
4735
  });
4627
- console.log(`[scheduler] auto-fix: needs_review ${job.slug} → authoring fix-plan (${isNoPlanRetry ? 'retry' : '1/1'})`);
4736
+ console.log(`[scheduler] auto-fix: needs_review ${job.slug} → authoring fix-plan (${isRetryAttempt ? 'retry' : '1/1'})`);
4628
4737
  spawnInvestigation(job, runDir).catch((e) => {
4629
4738
  console.error('[scheduler] auto-fix spawnInvestigation error', job.slug, e);
4630
4739
  });
@@ -5712,4 +5821,4 @@ function registerAdminRoutes(adminHttp, remoteObj = remote) {
5712
5821
  });
5713
5822
  }
5714
5823
 
5715
- module.exports = { findOverrunningJobs, JOB_OVERRUN_FACTOR, JOB_OVERRUN_FLOOR_MS, registerScheduleHandlers, attachWindow, init, ROOT, PRDS_DIR, healRefusalReason, writeQueue, reconcile, reconcileSourcePromptId, allocateParallelGroup, selectHistoryJobs, parsePorcelain, FINISH_PROTOCOL, remote, pickNextBatch, pickForProject, reapDeadRunningJobs, pollRecoveryClearSource, memoryLimitedBatchSize, availableForJobs, reverifyNeedsReview, isRescanCandidate, isPromotableOriginal, selectAutoFixTargets, isEligibleForImmediateAutoFix, resolveRunId, isUnresolvableNeedsReview, healTargetForFix, buildInvestigationPrompt, isGitRepoSync, committedInWindow, computeCommittedDuringRun, classifySigtermWithCommit, isFixPlanSlug, isFixPlanBeyondDepthCap, MAX_INVESTIGATION_DEPTH, forceTickOutcome, applyPauseCleared, detectNetworkErrorInLog, detectRateLimitInLog, classifyFailureOutcome, commitGuardVerdict, TRANSIENT_RETRY_CAP, buildScheduleStatePayload, partitionBootOrphans, applyOrphanOutcome, BOOT_ORPHAN_KILL_GRACE_MS, registerAdminRoutes, notifyOriginatingTab, notifyNeedsReview, isNotifiableTerminalStatus, extractResultTextFromLog, candidatePrdsDirs, candidateArchivedPrdsDirs, resolveArchivedPrdStatus, prdDirForCwd, prdPathForJob, archivedPrdPathForJob, archivedTwinExists, findPrdDir, resolveVerifyPrdPath, resolveNotifyPrd, runPrdMigration, consolidateAllFlatPrds, shouldSkipInvestigationForCleanRun, archiveCompletedPrd, retireCompletedSlugs, SCHEDULER_BOOTED_AT, SCHEDULER_CODE_SHA, resetJobFields, executeJob, prdArchivedSkipResult, spawnJob, listPrdsInternal, computeStallSummary, findStaleQuarantinedJobs, QUARANTINE_ESCALATE_MS };
5824
+ module.exports = { findOverrunningJobs, JOB_OVERRUN_FACTOR, JOB_OVERRUN_FLOOR_MS, registerScheduleHandlers, attachWindow, init, ROOT, PRDS_DIR, healRefusalReason, writeQueue, reconcile, reconcileSourcePromptId, allocateParallelGroup, selectHistoryJobs, parsePorcelain, FINISH_PROTOCOL, remote, pickNextBatch, pickForProject, reapDeadRunningJobs, pollRecoveryClearSource, memoryLimitedBatchSize, availableForJobs, reverifyNeedsReview, isRescanCandidate, isPromotableOriginal, selectAutoFixTargets, isEligibleForImmediateAutoFix, resolveRunId, isUnresolvableNeedsReview, isExhaustedAutoFix, isPlanUnqueued, fixSlugFor, healTargetForFix, buildInvestigationPrompt, isGitRepoSync, committedInWindow, computeCommittedDuringRun, classifySigtermWithCommit, isFixPlanSlug, isFixPlanBeyondDepthCap, MAX_INVESTIGATION_DEPTH, forceTickOutcome, applyPauseCleared, detectNetworkErrorInLog, detectRateLimitInLog, classifyFailureOutcome, commitGuardVerdict, TRANSIENT_RETRY_CAP, buildScheduleStatePayload, partitionBootOrphans, applyOrphanOutcome, BOOT_ORPHAN_KILL_GRACE_MS, registerAdminRoutes, notifyOriginatingTab, notifyNeedsReview, isNotifiableTerminalStatus, extractResultTextFromLog, candidatePrdsDirs, candidateArchivedPrdsDirs, resolveArchivedPrdStatus, prdDirForCwd, prdPathForJob, archivedPrdPathForJob, archivedTwinExists, findPrdDir, resolveVerifyPrdPath, resolveFixPlanPath, resolveNotifyPrd, runPrdMigration, consolidateAllFlatPrds, shouldSkipInvestigationForCleanRun, archiveCompletedPrd, retireCompletedSlugs, SCHEDULER_BOOTED_AT, SCHEDULER_CODE_SHA, resetJobFields, executeJob, prdArchivedSkipResult, spawnJob, listPrdsInternal, computeStallSummary, findStaleQuarantinedJobs, QUARANTINE_ESCALATE_MS };