claude-code-session-manager 0.86.0 → 0.87.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/dist/assets/AgentLibrary-DyLWzZDf.js +3 -0
- package/dist/assets/{DataModel-Q4jhl24R.js → DataModel--mISIJ6h.js} +1 -1
- package/dist/assets/{History-Cj2FejEo.js → History-C2ahUXTg.js} +2 -2
- package/dist/assets/{Hooks-CaelQI6t.js → Hooks-BiC6oyR2.js} +3 -3
- package/dist/assets/{HostBilko--v7cMR8I.js → HostBilko-BPleEOld.js} +1 -1
- package/dist/assets/{Library-DgI9oCCZ.js → Library-Dc8Qst1R.js} +1 -1
- package/dist/assets/{ListDetail-DYUZN-x-.js → ListDetail-DIXh-OLX.js} +1 -1
- package/dist/assets/MarkdownEditor-C90bkLXK.js +1 -0
- package/dist/assets/{McpServers-ypCYURh3.js → McpServers-DqcbLOLZ.js} +2 -2
- package/dist/assets/{Memory-C2qYp-3M.js → Memory-CW62MXlh.js} +4 -4
- package/dist/assets/{Panel-Cj2kw-Zv.js → Panel-Bw1FhRuF.js} +1 -1
- package/dist/assets/Permissions-BcUC-5y8.js +3 -0
- package/dist/assets/{Plugins-C1Vj8_dU.js → Plugins-BnKx9flD.js} +2 -2
- package/dist/assets/{ProvenanceBadge-DczPNM5U.js → ProvenanceBadge-Bw5vNVPT.js} +1 -1
- package/dist/assets/{SaveBar-Cd_7U6Gb.js → SaveBar-CWr0O_w-.js} +1 -1
- package/dist/assets/Scheduler-DYdLuUqq.js +14 -0
- package/dist/assets/{ScopeSwitcher-DVSyI44-.js → ScopeSwitcher-CrBLbg8s.js} +1 -1
- package/dist/assets/Settings-DluB-vN1.js +3 -0
- package/dist/assets/{SkillReferenceGraph-CUv1_Q2c.js → SkillReferenceGraph-CHLSseay.js} +1 -1
- package/dist/assets/{Skills-C_YHkAy-.js → Skills-gNdo_HNK.js} +2 -2
- package/dist/assets/{SystemPrompt-B8R7T9xn.js → SystemPrompt-Cru05-Ia.js} +1 -1
- package/dist/assets/{TagLibrary-dj9YHWyy.js → TagLibrary-DNHY0xou.js} +1 -1
- package/dist/assets/{TiptapBody-DnSBUjHE.js → TiptapBody-I4lmbCgP.js} +1 -1
- package/dist/assets/{Toggle-CjV_BJn6.js → Toggle-bWMHjmRh.js} +1 -1
- package/dist/assets/{index-CDo9xBR9.css → index-DV3PorRY.css} +1 -1
- package/dist/assets/{index-CXFQIPhO.js → index-fc_JjdxL.js} +724 -724
- package/dist/assets/settingsSchema-BfhtZnGD.js +3 -0
- package/dist/index.html +2 -2
- package/package.json +14 -14
- package/plugins/CLAUDE.md +61 -0
- package/plugins/session-manager-dev/.claude-plugin/plugin.json +1 -1
- package/plugins/session-manager-dev/skills/builder/4-manual/SKILL.md +1 -1
- package/plugins/session-manager-dev/skills/ops-sweep/SKILL.md +1 -1
- package/scripts/scheduler-mcp-server.cjs +7 -0
- package/src/main/__tests__/agentModelResolve.test.cjs +100 -9
- package/src/main/__tests__/broadcastCoalescer.test.cjs +18 -0
- package/src/main/__tests__/epicMint.test.cjs +2 -2
- package/src/main/__tests__/health-delegation-chain.test.cjs +2 -1
- package/src/main/__tests__/needsReviewLedger.test.cjs +162 -0
- package/src/main/__tests__/opsErrorLogTelemetryTap.test.cjs +3 -3
- package/src/main/__tests__/pollLoop-dispatch-on-failure.test.cjs +15 -1
- package/src/main/__tests__/prdCreateDisposition.test.cjs +201 -0
- package/src/main/__tests__/prdFrontmatterDisposition.test.cjs +125 -0
- package/src/main/__tests__/prdLocations.test.cjs +100 -2
- package/src/main/__tests__/prdLocationsArchived.test.cjs +43 -1
- package/src/main/__tests__/prdSetDisposition.test.cjs +222 -0
- package/src/main/__tests__/queue-health-verdict.test.cjs +170 -0
- package/src/main/__tests__/queue-starvation-per-project.test.cjs +14 -2
- package/src/main/__tests__/queueHistory.test.cjs +63 -0
- package/src/main/__tests__/reconcileTiming.test.cjs +135 -0
- package/src/main/__tests__/scheduleJobTransitions.test.cjs +101 -1
- package/src/main/__tests__/scheduler-boot-orphans.test.cjs +2 -2
- package/src/main/__tests__/scheduler-broadcast-reconcile.test.cjs +121 -0
- package/src/main/__tests__/scheduler-cross-project-batch.test.cjs +43 -0
- package/src/main/__tests__/scheduler-guard-verdict-autoresolve.test.cjs +344 -0
- package/src/main/__tests__/scheduler-job-budget.test.cjs +172 -0
- package/src/main/__tests__/scheduler-looks-done.test.cjs +93 -3
- package/src/main/__tests__/scheduler-porcelain-rename.test.cjs +164 -0
- package/src/main/__tests__/scheduler-reap-dead-running-jobs.test.cjs +160 -0
- package/src/main/__tests__/scheduler-reaper-helpers-basics.test.cjs +87 -0
- package/src/main/__tests__/scheduler-shared-tree-guard.test.cjs +88 -0
- package/src/main/__tests__/scheduler-starve-escalation.test.cjs +14 -4
- package/src/main/__tests__/scheduler-worktree-cap-defer.test.cjs +14 -0
- package/src/main/chatRunner.cjs +8 -5
- package/src/main/health.cjs +1 -1
- package/src/main/historyAggregator.cjs +5 -0
- package/src/main/index.cjs +95 -44
- package/src/main/ipcSchemas.cjs +47 -0
- package/src/main/lib/__tests__/active-sessions.test.cjs +251 -0
- package/src/main/lib/__tests__/bootSelfHeal.test.cjs +107 -0
- package/src/main/lib/__tests__/delegationReadiness.test.cjs +322 -43
- package/src/main/lib/__tests__/effectiveModelInfo.test.cjs +239 -0
- package/src/main/lib/__tests__/gitWorktree.test.cjs +89 -0
- package/src/main/lib/__tests__/guardShims.test.cjs +151 -0
- package/src/main/lib/__tests__/opsRootAbsoluteCwd.test.cjs +5 -5
- package/src/main/lib/__tests__/prdDisposition.test.cjs +224 -0
- package/src/main/lib/__tests__/reaperHelpers.test.cjs +179 -1
- package/src/main/lib/__tests__/usageCircuit.test.cjs +224 -0
- package/src/main/lib/__tests__/watchdog-helpers.test.cjs +312 -0
- package/src/main/lib/__tests__/watchdog-relaunch.test.cjs +193 -0
- package/{scripts → src/main}/lib/activeSessions.cjs +50 -4
- package/src/main/lib/agentModelResolve.cjs +65 -27
- package/src/main/lib/bootSelfHeal.cjs +88 -0
- package/src/main/lib/delegationReadiness.cjs +290 -225
- package/src/main/lib/effectiveModelInfo.cjs +333 -0
- package/src/main/lib/ephemeralCwd.cjs +1 -1
- package/src/main/lib/epicMint.cjs +3 -3
- package/src/main/lib/gitWorktree.cjs +42 -12
- package/src/main/lib/guardShims.cjs +156 -0
- package/src/main/lib/jobDirtFilter.cjs +7 -2
- package/src/main/lib/launchFailure.cjs +2 -1
- package/src/main/lib/mcpToolCatalog.cjs +4 -1
- package/src/main/lib/needsReviewLedger.cjs +205 -0
- package/src/main/lib/opsErrorLog.cjs +1 -1
- package/src/main/lib/opsOwnership.cjs +1 -1
- package/src/main/lib/prdCreate.cjs +56 -1
- package/src/main/lib/prdDisposition.cjs +199 -0
- package/src/main/lib/prdFrontmatter.cjs +8 -2
- package/src/main/lib/prdLocations.cjs +167 -45
- package/src/main/lib/projectHomeAdminRoutes.cjs +4 -4
- package/src/main/lib/projectPageSummarySchema.cjs +1 -1
- package/src/main/lib/projectRootResolve.cjs +1 -1
- package/src/main/lib/queueHistory.cjs +19 -1
- package/src/main/lib/queueStore.cjs +6 -1
- package/src/main/lib/reaperHelpers.cjs +181 -15
- package/src/main/lib/scheduleJobSchema.cjs +8 -0
- package/src/main/lib/scheduleJobTransitions.cjs +33 -0
- package/src/main/lib/schedulerConfig.cjs +24 -0
- package/src/main/lib/usageCircuit.cjs +159 -0
- package/{scripts → src/main}/lib/watchdogHelpers.cjs +1 -1
- package/src/main/scheduler/prdParser.cjs +13 -0
- package/src/main/scheduler.cjs +1285 -143
- package/src/main/templates/PRD_AUTHORING.md +50 -0
- package/src/main/templates/project-pages-catalog.json +1 -1
- package/src/main/usage.cjs +21 -3
- package/src/preload/api.d.ts +92 -1
- package/src/preload/index.cjs +10 -0
- package/web/README.md +41 -0
- package/{scripts/render-project-pages.cjs → web/project-pages/render.cjs} +4 -4
- package/{scripts/render-project-pages → web/project-pages/renderer}/dist/renderer.cjs +1 -1
- package/{scripts/validate-project-pages-summary.cjs → web/project-pages/validate-summary.cjs} +5 -5
- package/dist/assets/AgentLibrary-DTFL7y8G.js +0 -3
- package/dist/assets/MarkdownEditor-DCIubYWf.js +0 -1
- package/dist/assets/Permissions-BiYZNGYW.js +0 -3
- package/dist/assets/Scheduler-DcLBiJBq.js +0 -14
- package/dist/assets/Settings-Cv-pRyms.js +0 -3
- package/dist/assets/settingsSchema-BJVciriw.js +0 -3
- /package/{scripts/project-pages-logic → web/project-pages/logic}/dist/logic.cjs +0 -0
|
@@ -32,6 +32,8 @@
|
|
|
32
32
|
|
|
33
33
|
const { appendAuditEvent } = require('./auditLog.cjs');
|
|
34
34
|
const telemetryCounters = require('./telemetryCounters.cjs');
|
|
35
|
+
const queueHistory = require('./queueHistory.cjs');
|
|
36
|
+
const { buildNeedsReviewEntryLine, buildNeedsReviewResolutionLine } = require('./needsReviewLedger.cjs');
|
|
35
37
|
|
|
36
38
|
// A run genuinely finished when it leaves 'running' for one of these —
|
|
37
39
|
// distinct from every other legal edge in LEGAL_TRANSITIONS (retries,
|
|
@@ -195,6 +197,37 @@ function transitionJob(job, toStatus, { reason, source, allowAnyFrom = false } =
|
|
|
195
197
|
telemetryCounters.trackSchedulerJobFinish({ status: toStatus });
|
|
196
198
|
}
|
|
197
199
|
|
|
200
|
+
// needs_review ledger (PRD: needs_review durability). This IS the single
|
|
201
|
+
// funnel every needs_review transition — entry or resolution — passes
|
|
202
|
+
// through (see this file's own header comment: transitionJob is the ONE
|
|
203
|
+
// place `status` is assigned), so hooking here, rather than any one of
|
|
204
|
+
// needs_review's several distinct entry call sites in scheduler.cjs,
|
|
205
|
+
// guarantees no path can bypass it.
|
|
206
|
+
//
|
|
207
|
+
// Fire-and-forget, exactly like every other best-effort side-channel write
|
|
208
|
+
// in this codebase (rcaReport.cjs, auditLog.cjs's own posture) — a ledger
|
|
209
|
+
// write failure must never fail or delay the transition it's recording.
|
|
210
|
+
if (toStatus === 'needs_review' && from !== 'needs_review') {
|
|
211
|
+
// ENTRY: stamp the episode's runId/enteredAt on the job itself so the
|
|
212
|
+
// RESOLUTION line (below) can reference the SAME runId even if the job's
|
|
213
|
+
// own `runId` is reassigned in between (e.g. a resume-recovery
|
|
214
|
+
// re-dispatch mints a fresh one before this episode resolves).
|
|
215
|
+
job.needsReviewEntryRunId = job.runId ?? null;
|
|
216
|
+
job.needsReviewEnteredAt = entry.at;
|
|
217
|
+
queueHistory.appendHistory([buildNeedsReviewEntryLine(job, entry)]).catch((e) => {
|
|
218
|
+
console.error('[scheduleJobTransitions] failed to append needs_review entry', e?.message ?? String(e));
|
|
219
|
+
});
|
|
220
|
+
} else if (from === 'needs_review' && toStatus !== 'needs_review') {
|
|
221
|
+
// RESOLUTION: read the episode stamped at entry, then clear it — the
|
|
222
|
+
// episode is over regardless of what this resolution line looks like.
|
|
223
|
+
const episode = { runId: job.needsReviewEntryRunId ?? null, enteredAt: job.needsReviewEnteredAt ?? null };
|
|
224
|
+
delete job.needsReviewEntryRunId;
|
|
225
|
+
delete job.needsReviewEnteredAt;
|
|
226
|
+
queueHistory.appendHistory([buildNeedsReviewResolutionLine(job, entry, episode)]).catch((e) => {
|
|
227
|
+
console.error('[scheduleJobTransitions] failed to append needs_review resolution', e?.message ?? String(e));
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
198
231
|
return true;
|
|
199
232
|
}
|
|
200
233
|
|
|
@@ -56,6 +56,16 @@ module.exports = {
|
|
|
56
56
|
// (pause/resume, job start/finish/reap/reset) bypass this via
|
|
57
57
|
// broadcast({ flush: true }).
|
|
58
58
|
BROADCAST_COALESCE_MS: 200,
|
|
59
|
+
// reconcile() pass duration above which a single warn-level breakdown is
|
|
60
|
+
// logged (per-phase ms, PRD file count, resolved dir count) — the only
|
|
61
|
+
// evidence trail for a slow pass otherwise requires hand-timing the scan
|
|
62
|
+
// from a throwaway script. Baseline measured 2026-09-13 on a machine with
|
|
63
|
+
// ~160 PRD dirs: allProjectCwds() ~270ms x5/pass, resolvePrdsDirs()
|
|
64
|
+
// ~430ms, historyTerminalBySlug() ~418ms over 784 entries — a healthy pass
|
|
65
|
+
// today is comfortably under 2s; this should trip well before a
|
|
66
|
+
// regression back toward those numbers compounds into the reported
|
|
67
|
+
// "schedule.state timed out after 5000ms" toast.
|
|
68
|
+
RECONCILE_SLOW_PASS_MS: 2_000,
|
|
59
69
|
// Terminal (completed/failed) jobs older than this move from queue.json's
|
|
60
70
|
// hot jobs[] into the append-only history.jsonl sidecar. See
|
|
61
71
|
// src/main/lib/queueHistory.cjs.
|
|
@@ -109,6 +119,20 @@ module.exports = {
|
|
|
109
119
|
// SM_JOB_OVERRUN_FLOOR_MINUTES.
|
|
110
120
|
JOB_OVERRUN_FLOOR_MS: 45 * 60_000,
|
|
111
121
|
|
|
122
|
+
// Wall-clock KILL budget (distinct from JOB_OVERRUN_FACTOR/JOB_OVERRUN_FLOOR_MS
|
|
123
|
+
// above, which only ever ESCALATE — log + audit, deliberately never kill).
|
|
124
|
+
// This one ACTS: a RUNNING job past `budgetMs = clamp(estimateMinutes *
|
|
125
|
+
// JOB_BUDGET_FACTOR, JOB_BUDGET_FLOOR_MS, JOB_BUDGET_CEILING_MS)` is
|
|
126
|
+
// SIGTERMed and parked needs_review. Calibrated against 606 run-meta
|
|
127
|
+
// records since 2026-08-25: p50=13m, p90=60m, max=240m, against a
|
|
128
|
+
// concurrencyCap of 4 — two 4h outliers hold half the machine's dispatch
|
|
129
|
+
// capacity for an afternoon with nothing noticing or acting on it. Override
|
|
130
|
+
// with SM_JOB_BUDGET_FACTOR / SM_JOB_BUDGET_FLOOR_MINUTES /
|
|
131
|
+
// SM_JOB_BUDGET_CEILING_MINUTES.
|
|
132
|
+
JOB_BUDGET_FACTOR: 3,
|
|
133
|
+
JOB_BUDGET_FLOOR_MS: 45 * 60_000,
|
|
134
|
+
JOB_BUDGET_CEILING_MS: 180 * 60_000,
|
|
135
|
+
|
|
112
136
|
// A 'running' row with no runtime.pid recorded (spawnJob's status:running
|
|
113
137
|
// mutate at scheduler.cjs:~3524 landed, but the pid-bearing runtime={}
|
|
114
138
|
// mutate that follows executeJob's spawn callback never did — the spawn
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* usageCircuit.cjs — a breaker over the /api/oauth/usage meter (PRD ss-02).
|
|
5
|
+
* Degraded mode is explicit, never a silent 0: repeated failures open the
|
|
6
|
+
* circuit with jittered backoff; degradedBudget() carries forward the last
|
|
7
|
+
* known-good BINDING window's utilization (never 0), pinning to 100 on a
|
|
8
|
+
* fresh executor-observed 429 and capping concurrency at 2. Pure Node.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const FLOOR_MS = 30_000;
|
|
12
|
+
const CAP_MS = 8 * 60 * 1000;
|
|
13
|
+
|
|
14
|
+
/** Doubles from FLOOR_MS, capped at CAP_MS. 0/falsy prevMs starts at the floor. */
|
|
15
|
+
function doubledBase(prevMs) {
|
|
16
|
+
return prevMs > 0 ? Math.min(prevMs * 2, CAP_MS) : FLOOR_MS;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Next backoff interval: doubles `prevMs` (floor FLOOR_MS, cap CAP_MS), then
|
|
21
|
+
* jitters by 0.5 + rand()*0.5 so concurrent instances don't retry in lockstep.
|
|
22
|
+
*/
|
|
23
|
+
function backoffWithJitter(prevMs) {
|
|
24
|
+
const base = doubledBase(prevMs);
|
|
25
|
+
return base * (0.5 + Math.random() * 0.5);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* A cached reset timestamp is fresh only if it's strictly in the future
|
|
30
|
+
* relative to `now` — null/unparseable/at-or-before-now all read as stale,
|
|
31
|
+
* since a passed reset no longer describes a live window.
|
|
32
|
+
*/
|
|
33
|
+
function isResetFresh(iso, now) {
|
|
34
|
+
if (!iso) return false;
|
|
35
|
+
const resetMs = Date.parse(iso);
|
|
36
|
+
if (!Number.isFinite(resetMs)) return false;
|
|
37
|
+
const nowMs = now instanceof Date ? now.getTime() : Number(now);
|
|
38
|
+
if (!Number.isFinite(nowMs)) return false;
|
|
39
|
+
return resetMs > nowMs;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Which window is actually binding dispatch. Prefers `payload.limits[]`'s
|
|
44
|
+
* `is_active` entry (the richer per-window shape); falls back to the flat
|
|
45
|
+
* `five_hour` field when `limits[]` is absent, matching usage.cjs's shape.
|
|
46
|
+
*/
|
|
47
|
+
function bindingWindow(payload) {
|
|
48
|
+
if (payload && Array.isArray(payload.limits) && payload.limits.length) {
|
|
49
|
+
const active = payload.limits.find((l) => l && l.is_active);
|
|
50
|
+
if (active) {
|
|
51
|
+
return {
|
|
52
|
+
name: active.type || active.name || 'unknown',
|
|
53
|
+
utilization: active.utilization,
|
|
54
|
+
resets_at: active.resets_at ?? null,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const fh = payload && payload.five_hour;
|
|
59
|
+
return {
|
|
60
|
+
name: 'five_hour',
|
|
61
|
+
utilization: fh ? fh.utilization : undefined,
|
|
62
|
+
resets_at: fh ? fh.resets_at : null,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Conservative budget to run on while the meter is down. utilization is
|
|
68
|
+
* carried forward from the last known-good BINDING window — never 0, which
|
|
69
|
+
* would read as "plenty of headroom" instead of "we don't know." A fresh
|
|
70
|
+
* executor-observed 429 (its own window not yet passed) pins utilization to
|
|
71
|
+
* 100 regardless of the stale cached value. concurrencyCap never exceeds 2.
|
|
72
|
+
*/
|
|
73
|
+
function degradedBudget(lastGoodPayload, executorEvidence = {}) {
|
|
74
|
+
const window = bindingWindow(lastGoodPayload);
|
|
75
|
+
let utilization = Number.isFinite(window.utilization) && window.utilization > 0
|
|
76
|
+
? window.utilization
|
|
77
|
+
: 100;
|
|
78
|
+
|
|
79
|
+
const configuredCap = Number.isFinite(executorEvidence.configuredCap)
|
|
80
|
+
? executorEvidence.configuredCap
|
|
81
|
+
: 2;
|
|
82
|
+
const concurrencyCap = Math.min(configuredCap, 2);
|
|
83
|
+
|
|
84
|
+
if (executorEvidence.observed429 && isResetFresh(executorEvidence.resetsAt, executorEvidence.now)) {
|
|
85
|
+
utilization = 100;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return { utilization, concurrencyCap };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Wraps `fn` so concurrent callers awaiting an in-flight call share one promise. */
|
|
92
|
+
function singleFlight(fn) {
|
|
93
|
+
let inFlight = null;
|
|
94
|
+
return function singleFlightWrapped(...args) {
|
|
95
|
+
if (inFlight) return inFlight;
|
|
96
|
+
inFlight = Promise.resolve(fn(...args)).finally(() => { inFlight = null; });
|
|
97
|
+
return inFlight;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Breaker state machine over the meter: closed -> open after 3 consecutive
|
|
103
|
+
* failures; open allows exactly one probe per backoff interval (half_open);
|
|
104
|
+
* a successful probe closes the circuit and resets backoff, a failed probe
|
|
105
|
+
* reopens it with backoff doubled.
|
|
106
|
+
*/
|
|
107
|
+
function createUsageCircuit({ now = () => Date.now() } = {}) {
|
|
108
|
+
let consecutiveFailures = 0;
|
|
109
|
+
let currentState = 'closed';
|
|
110
|
+
let backoffBaseMs = 0;
|
|
111
|
+
let nextProbeAtMs = 0;
|
|
112
|
+
let probeInFlight = false;
|
|
113
|
+
|
|
114
|
+
function scheduleNextProbe() {
|
|
115
|
+
const intervalMs = backoffWithJitter(backoffBaseMs);
|
|
116
|
+
backoffBaseMs = doubledBase(backoffBaseMs);
|
|
117
|
+
nextProbeAtMs = now() + intervalMs;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function state() {
|
|
121
|
+
if (currentState === 'open' && !probeInFlight && now() >= nextProbeAtMs) {
|
|
122
|
+
currentState = 'half_open';
|
|
123
|
+
probeInFlight = true;
|
|
124
|
+
}
|
|
125
|
+
return currentState;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function recordFailure(kind) { // eslint-disable-line no-unused-vars
|
|
129
|
+
consecutiveFailures += 1;
|
|
130
|
+
if (currentState === 'half_open') {
|
|
131
|
+
probeInFlight = false;
|
|
132
|
+
currentState = 'open';
|
|
133
|
+
scheduleNextProbe();
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (currentState === 'closed' && consecutiveFailures >= 3) {
|
|
137
|
+
currentState = 'open';
|
|
138
|
+
scheduleNextProbe();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function recordSuccess(payload) { // eslint-disable-line no-unused-vars
|
|
143
|
+
consecutiveFailures = 0;
|
|
144
|
+
probeInFlight = false;
|
|
145
|
+
backoffBaseMs = 0;
|
|
146
|
+
currentState = 'closed';
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return { recordSuccess, recordFailure, state };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
module.exports = {
|
|
153
|
+
createUsageCircuit,
|
|
154
|
+
backoffWithJitter,
|
|
155
|
+
isResetFresh,
|
|
156
|
+
bindingWindow,
|
|
157
|
+
degradedBudget,
|
|
158
|
+
singleFlight,
|
|
159
|
+
};
|
|
@@ -362,7 +362,7 @@ function releaseLock(lockPath) {
|
|
|
362
362
|
* dependency fails inside maybeFinalizeHistory's try/catch, not at require
|
|
363
363
|
* time for the whole watchdog script. */
|
|
364
364
|
function defaultFinalizeClosedDays(opts) {
|
|
365
|
-
const { finalizeClosedDays } = require('
|
|
365
|
+
const { finalizeClosedDays } = require('../historyAggregator.cjs');
|
|
366
366
|
return finalizeClosedDays(opts);
|
|
367
367
|
}
|
|
368
368
|
|
|
@@ -119,10 +119,23 @@ async function parsePrdRaw(filePath) {
|
|
|
119
119
|
// dependent follow-up PRD (scheduler-launch-as-persona) resolves this
|
|
120
120
|
// into a launch decision, which this module does not do.
|
|
121
121
|
agentType: fm.agentType || null,
|
|
122
|
+
// Wave-authoring decision (scheduler wave-disposition PRD): 'append' or
|
|
123
|
+
// 'new-head', set only when createPrd() actually had a disposition to
|
|
124
|
+
// record — see prdCreate.cjs/prdDisposition.cjs. Absent on every PRD
|
|
125
|
+
// authored before this field existed, and on any PRD that never needed
|
|
126
|
+
// the decision (first-ever PRD in its Epic, or one with its own explicit
|
|
127
|
+
// dependsOn).
|
|
128
|
+
disposition: (fm.disposition === 'append' || fm.disposition === 'new-head') ? fm.disposition : null,
|
|
122
129
|
// Opt-in exclusive-lease flag (PRD 1107, quietMachineLease.cjs +
|
|
123
130
|
// schedulerBatch.cjs's pickNextBatch). Only a literal `true` opts in —
|
|
124
131
|
// matches prdFrontmatter.cjs's applyKey semantics for this field.
|
|
125
132
|
quietMachine: fm.quietMachine === 'true',
|
|
133
|
+
// Explicit opt-out of the wall-clock budget kill (scheduler.cjs's budget
|
|
134
|
+
// watchdog) — only a literal `true` opts out, matching quietMachine's
|
|
135
|
+
// parse semantics above. quietMachine jobs are exempt regardless of this
|
|
136
|
+
// field; this is for a job that isn't quiet-machine-eligible but still
|
|
137
|
+
// needs no wall-clock kill ceiling.
|
|
138
|
+
budgetExempt: fm.budgetExempt === 'true',
|
|
126
139
|
body: body.trim(),
|
|
127
140
|
};
|
|
128
141
|
}
|