@ai-dossier/sched 0.2.0 → 0.3.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.
Files changed (58) hide show
  1. package/README.md +85 -17
  2. package/dist/dispatch.d.ts +128 -0
  3. package/dist/dispatch.d.ts.map +1 -0
  4. package/dist/dispatch.js +300 -0
  5. package/dist/dispatch.js.map +1 -0
  6. package/dist/engine.d.ts +111 -0
  7. package/dist/engine.d.ts.map +1 -0
  8. package/dist/engine.js +936 -0
  9. package/dist/engine.js.map +1 -0
  10. package/dist/enqueue.d.ts +38 -0
  11. package/dist/enqueue.d.ts.map +1 -0
  12. package/dist/enqueue.js +222 -0
  13. package/dist/enqueue.js.map +1 -0
  14. package/dist/groundtruth.d.ts +135 -0
  15. package/dist/groundtruth.d.ts.map +1 -0
  16. package/dist/groundtruth.js +259 -0
  17. package/dist/groundtruth.js.map +1 -0
  18. package/dist/index.d.ts +14 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +101 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/journal.d.ts +32 -0
  23. package/dist/journal.d.ts.map +1 -0
  24. package/dist/journal.js +113 -0
  25. package/dist/journal.js.map +1 -0
  26. package/dist/persist.d.ts +54 -0
  27. package/dist/persist.d.ts.map +1 -0
  28. package/dist/persist.js +348 -0
  29. package/dist/persist.js.map +1 -0
  30. package/dist/project.d.ts +41 -0
  31. package/dist/project.d.ts.map +1 -0
  32. package/dist/project.js +124 -0
  33. package/dist/project.js.map +1 -0
  34. package/dist/readiness.d.ts +43 -0
  35. package/dist/readiness.d.ts.map +1 -0
  36. package/dist/readiness.js +102 -0
  37. package/dist/readiness.js.map +1 -0
  38. package/dist/scheduler.d.ts +78 -0
  39. package/dist/scheduler.d.ts.map +1 -0
  40. package/dist/scheduler.js +191 -0
  41. package/dist/scheduler.js.map +1 -0
  42. package/dist/state.d.ts +40 -0
  43. package/dist/state.d.ts.map +1 -0
  44. package/dist/state.js +391 -0
  45. package/dist/state.js.map +1 -0
  46. package/dist/status.d.ts +44 -0
  47. package/dist/status.d.ts.map +1 -0
  48. package/dist/status.js +82 -0
  49. package/dist/status.js.map +1 -0
  50. package/dist/teardown.d.ts +57 -0
  51. package/dist/teardown.d.ts.map +1 -0
  52. package/dist/teardown.js +242 -0
  53. package/dist/teardown.js.map +1 -0
  54. package/dist/types.d.ts +253 -0
  55. package/dist/types.d.ts.map +1 -0
  56. package/dist/types.js +91 -0
  57. package/dist/types.js.map +1 -0
  58. package/package.json +1 -1
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ /**
3
+ * Readiness: which units can run right now, as a pure function of state
4
+ * (RFC-0001 §E.4: "scheduler gates on merge" — an issue with an unmerged
5
+ * dependency is never runnable, and a batch is never dispatched while a batch
6
+ * it depends on is unmerged).
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.DISPATCHABLE_ISSUE_STATUSES = void 0;
10
+ exports.dependencyBlockers = dependencyBlockers;
11
+ exports.batchBlockers = batchBlockers;
12
+ exports.runnableUnits = runnableUnits;
13
+ const state_1 = require("./state");
14
+ const types_1 = require("./types");
15
+ /** Issue statuses from which a full-cycle dispatch may start. */
16
+ exports.DISPATCHABLE_ISSUE_STATUSES = new Set([
17
+ // `queued` is dispatchable because the manifest already carries the mode —
18
+ // the classifier (#465) only refines it later.
19
+ 'queued',
20
+ 'classified',
21
+ 'requeued',
22
+ ]);
23
+ function batchOf(state, issue) {
24
+ return state.batches.find((b) => b.members.includes(issue));
25
+ }
26
+ /**
27
+ * Unsatisfied dependency edges of a single entry. Deps satisfied by
28
+ * membership in the SAME batch are not blockers — intra-batch ordering is the
29
+ * batch's own concern once dispatched (RFC-0001 §E.4).
30
+ */
31
+ function dependencyBlockers(state, entry) {
32
+ const blockers = [];
33
+ const ownBatch = entry.batch !== null ? (0, state_1.findBatch)(state, entry.batch) : undefined;
34
+ for (const dep of entry.deps) {
35
+ if (ownBatch?.members.includes(dep))
36
+ continue;
37
+ const depEntry = state.entries.find((e) => e.issue === dep);
38
+ if (!depEntry) {
39
+ blockers.push({ issue: entry.issue, dep, reason: 'not-in-queue' });
40
+ continue;
41
+ }
42
+ if (!types_1.SATISFIED_ISSUE_STATUSES.has(depEntry.status)) {
43
+ blockers.push({ issue: entry.issue, dep, reason: 'unmerged', depStatus: depEntry.status });
44
+ }
45
+ }
46
+ return blockers;
47
+ }
48
+ /** Whether `batch` may dispatch: status `ready` and every cross-batch/cross-issue edge merged. */
49
+ function batchBlockers(state, batch) {
50
+ const blockers = [];
51
+ for (const member of batch.members) {
52
+ const entry = state.entries.find((e) => e.issue === member);
53
+ if (!entry)
54
+ continue; // validateState guarantees membership consistency
55
+ for (const dep of entry.deps) {
56
+ if (batch.members.includes(dep))
57
+ continue; // intra-batch edge
58
+ const depBatch = batchOf(state, dep);
59
+ if (depBatch && depBatch.id !== batch.id) {
60
+ if (!types_1.MERGED_BATCH_STATUSES.has(depBatch.status)) {
61
+ blockers.push({ issue: member, dep, reason: 'unmerged' });
62
+ }
63
+ continue;
64
+ }
65
+ const depEntry = state.entries.find((e) => e.issue === dep);
66
+ if (!depEntry) {
67
+ blockers.push({ issue: member, dep, reason: 'not-in-queue' });
68
+ }
69
+ else if (!types_1.SATISFIED_ISSUE_STATUSES.has(depEntry.status)) {
70
+ blockers.push({ issue: member, dep, reason: 'unmerged', depStatus: depEntry.status });
71
+ }
72
+ }
73
+ }
74
+ return blockers;
75
+ }
76
+ /**
77
+ * All runnable units in stable dispatch order: issues in queue order, then
78
+ * batches in creation order. A batch unit unlocks ALL its members' execution,
79
+ * so batches are listed after issues only for stability — the caller slices to
80
+ * free capacity either way.
81
+ */
82
+ function runnableUnits(state) {
83
+ const units = [];
84
+ for (const entry of state.entries) {
85
+ if (entry.mode !== 'full')
86
+ continue;
87
+ if (!exports.DISPATCHABLE_ISSUE_STATUSES.has(entry.status))
88
+ continue;
89
+ if (dependencyBlockers(state, entry).length > 0)
90
+ continue;
91
+ units.push({ kind: 'issue', issue: entry.issue });
92
+ }
93
+ for (const batch of state.batches) {
94
+ if (batch.status !== 'ready')
95
+ continue;
96
+ if (batchBlockers(state, batch).length > 0)
97
+ continue;
98
+ units.push({ kind: 'batch', batch: batch.id });
99
+ }
100
+ return units;
101
+ }
102
+ //# sourceMappingURL=readiness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readiness.js","sourceRoot":"","sources":["../src/readiness.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * The deterministic scheduler core: which units are runnable, and how idle
3
+ * slots get filled (RFC-0001 §B — "refill is a state-machine transition, not
4
+ * a remembered instruction").
5
+ *
6
+ * Everything here is a pure function of (state, config). Dispatching —
7
+ * spawning agent processes, completion verification, stall timers — is #464;
8
+ * this module only decides assignments and performs the typed slot/issue
9
+ * transitions that make them durable. When `state.paused` is set, no
10
+ * assignments are made at all (`sched pause`).
11
+ */
12
+ import { batchBlockers, type DependencyBlocker, dependencyBlockers, type RunnableUnit, runnableUnits } from './readiness';
13
+ import type { SchedConfig, SchedState } from './types';
14
+ export { runnableUnits, dependencyBlockers, batchBlockers };
15
+ export type { DependencyBlocker, RunnableUnit };
16
+ /** One placement made by `computeAssignments`: slot ← unit. */
17
+ export type Assignment = {
18
+ slot: number;
19
+ kind: 'issue';
20
+ issue: number;
21
+ } | {
22
+ slot: number;
23
+ kind: 'batch';
24
+ batch: string;
25
+ };
26
+ /**
27
+ * Fill idle slots with runnable units, bounded by `config.max_slots` (AC5):
28
+ * `max_slots` caps LIVE slots (assigned | running | recovering), so free
29
+ * capacity = max_slots − live. Idle slots are reused first; new slots are
30
+ * materialized lazily (as `idle`, then transitioned `idle → assigned` — a
31
+ * typed edge, never a synthetic mid-state).
32
+ *
33
+ * `kinds` restricts which unit kinds may be assigned (default: both). The
34
+ * #464 engine dispatches `issue` units only — batch member sequencing is a
35
+ * follow-up — so it passes `['issue']` and a `ready` batch never occupies a
36
+ * slot it cannot run on yet.
37
+ */
38
+ export declare function computeAssignments(state: SchedState, config: SchedConfig, now?: Date, kinds?: readonly ('issue' | 'batch')[]): {
39
+ state: SchedState;
40
+ assignments: Assignment[];
41
+ };
42
+ /** Free live-agent capacity: `max_slots` minus the slots holding live units (AC5). */
43
+ export declare function freeCapacity(state: SchedState, config: SchedConfig): number;
44
+ /**
45
+ * Assign `unit` to an idle slot (materializing one lazily when none is idle —
46
+ * a typed `idle → assigned` edge, never a synthetic mid-state) and return the
47
+ * new state plus the slot's id. Shared by queue refill (computeAssignments)
48
+ * and the #468 report dispatch so the slot-invariant shape exists once.
49
+ */
50
+ export declare function assignToIdleSlot(state: SchedState, unit: string, phase: string | null, now: Date): {
51
+ state: SchedState;
52
+ slotId: number;
53
+ };
54
+ /**
55
+ * `sched pause` / `sched resume`: toggle the paused flag. Pausing does not
56
+ * touch live units — it only stops new assignments.
57
+ */
58
+ export declare function setPaused(state: SchedState, paused: boolean): SchedState;
59
+ /**
60
+ * `sched abandon --issue N`: mark an entry failed via the universal failure
61
+ * edge, recording the reason, and release any slot holding it
62
+ * (running/exited/verifying → failed → idle). Terminal entries cannot be
63
+ * abandoned.
64
+ */
65
+ export declare function abandonIssue(state: SchedState, issue: number, reason?: string, now?: Date): {
66
+ state: SchedState;
67
+ releasedSlots: number[];
68
+ };
69
+ /**
70
+ * `sched abandon --batch B`: dissolve the batch and requeue every non-terminal
71
+ * member as full-cycle (RFC-0001 §D.2 dissolving → "members requeued"; F.8
72
+ * "nothing green is discarded" — members already shipped stay put).
73
+ */
74
+ export declare function abandonBatch(state: SchedState, batchId: string, reason?: string, now?: Date): {
75
+ state: SchedState;
76
+ requeued: number[];
77
+ };
78
+ //# sourceMappingURL=scheduler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../src/scheduler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EACL,aAAa,EACb,KAAK,iBAAiB,EACtB,kBAAkB,EAClB,KAAK,YAAY,EACjB,aAAa,EACd,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AASvD,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,aAAa,EAAE,CAAC;AAC5D,YAAY,EAAE,iBAAiB,EAAE,YAAY,EAAE,CAAC;AAEhD,+DAA+D;AAC/D,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAMnD;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,WAAW,EACnB,GAAG,GAAE,IAAiB,EACtB,KAAK,GAAE,SAAS,CAAC,OAAO,GAAG,OAAO,CAAC,EAAuB,GACzD;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,WAAW,EAAE,UAAU,EAAE,CAAA;CAAE,CA+BlD;AAED,sFAAsF;AACtF,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,GAAG,MAAM,CAG3E;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,GAAG,EAAE,IAAI,GACR;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAiCvC;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,GAAG,UAAU,CAExE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,MAAM,EACb,MAAM,SAAc,EACpB,GAAG,GAAE,IAAiB,GACrB;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,aAAa,EAAE,MAAM,EAAE,CAAA;CAAE,CAqBhD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,MAAM,EACf,MAAM,SAAoB,EAC1B,GAAG,GAAE,IAAiB,GACrB;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CA+C3C"}
@@ -0,0 +1,191 @@
1
+ "use strict";
2
+ /**
3
+ * The deterministic scheduler core: which units are runnable, and how idle
4
+ * slots get filled (RFC-0001 §B — "refill is a state-machine transition, not
5
+ * a remembered instruction").
6
+ *
7
+ * Everything here is a pure function of (state, config). Dispatching —
8
+ * spawning agent processes, completion verification, stall timers — is #464;
9
+ * this module only decides assignments and performs the typed slot/issue
10
+ * transitions that make them durable. When `state.paused` is set, no
11
+ * assignments are made at all (`sched pause`).
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.batchBlockers = exports.dependencyBlockers = exports.runnableUnits = void 0;
15
+ exports.computeAssignments = computeAssignments;
16
+ exports.freeCapacity = freeCapacity;
17
+ exports.assignToIdleSlot = assignToIdleSlot;
18
+ exports.setPaused = setPaused;
19
+ exports.abandonIssue = abandonIssue;
20
+ exports.abandonBatch = abandonBatch;
21
+ const readiness_1 = require("./readiness");
22
+ Object.defineProperty(exports, "batchBlockers", { enumerable: true, get: function () { return readiness_1.batchBlockers; } });
23
+ Object.defineProperty(exports, "dependencyBlockers", { enumerable: true, get: function () { return readiness_1.dependencyBlockers; } });
24
+ Object.defineProperty(exports, "runnableUnits", { enumerable: true, get: function () { return readiness_1.runnableUnits; } });
25
+ const state_1 = require("./state");
26
+ const types_1 = require("./types");
27
+ function unitId(unit) {
28
+ return unit.kind === 'issue' ? `issue:${unit.issue}` : `batch:${unit.batch}`;
29
+ }
30
+ /**
31
+ * Fill idle slots with runnable units, bounded by `config.max_slots` (AC5):
32
+ * `max_slots` caps LIVE slots (assigned | running | recovering), so free
33
+ * capacity = max_slots − live. Idle slots are reused first; new slots are
34
+ * materialized lazily (as `idle`, then transitioned `idle → assigned` — a
35
+ * typed edge, never a synthetic mid-state).
36
+ *
37
+ * `kinds` restricts which unit kinds may be assigned (default: both). The
38
+ * #464 engine dispatches `issue` units only — batch member sequencing is a
39
+ * follow-up — so it passes `['issue']` and a `ready` batch never occupies a
40
+ * slot it cannot run on yet.
41
+ */
42
+ function computeAssignments(state, config, now = new Date(), kinds = ['issue', 'batch']) {
43
+ if (state.paused) {
44
+ return { state, assignments: [] };
45
+ }
46
+ if (freeCapacity(state, config) === 0) {
47
+ return { state, assignments: [] };
48
+ }
49
+ const held = new Set(state.slots.map((s) => s.unit).filter((u) => u !== null));
50
+ const candidates = (0, readiness_1.runnableUnits)(state)
51
+ .filter((unit) => kinds.includes(unit.kind))
52
+ .filter((unit) => !held.has(unitId(unit)));
53
+ const taken = candidates.slice(0, freeCapacity(state, config));
54
+ if (taken.length === 0) {
55
+ return { state, assignments: [] };
56
+ }
57
+ let next = state;
58
+ const assignments = [];
59
+ for (const unit of taken) {
60
+ const assigned = assignToIdleSlot(next, unitId(unit), null, now);
61
+ next = assigned.state;
62
+ assignments.push(unit.kind === 'issue'
63
+ ? { slot: assigned.slotId, kind: 'issue', issue: unit.issue }
64
+ : { slot: assigned.slotId, kind: 'batch', batch: unit.batch });
65
+ }
66
+ return { state: next, assignments };
67
+ }
68
+ /** Free live-agent capacity: `max_slots` minus the slots holding live units (AC5). */
69
+ function freeCapacity(state, config) {
70
+ const live = state.slots.filter((s) => types_1.LIVE_SLOT_STATUSES.has(s.status)).length;
71
+ return Math.max(0, config.max_slots - live);
72
+ }
73
+ /**
74
+ * Assign `unit` to an idle slot (materializing one lazily when none is idle —
75
+ * a typed `idle → assigned` edge, never a synthetic mid-state) and return the
76
+ * new state plus the slot's id. Shared by queue refill (computeAssignments)
77
+ * and the #468 report dispatch so the slot-invariant shape exists once.
78
+ */
79
+ function assignToIdleSlot(state, unit, phase, now) {
80
+ let next = state;
81
+ let idle = next.slots.find((s) => s.status === 'idle');
82
+ if (!idle) {
83
+ const slot = {
84
+ id: next.next_slot_id,
85
+ status: 'idle',
86
+ unit: null,
87
+ pid: null,
88
+ pid_start: null,
89
+ phase: null,
90
+ last_progress_at: null,
91
+ branch: null,
92
+ last_head: null,
93
+ recoveries: 0,
94
+ updated_at: now.toISOString(),
95
+ };
96
+ next = { ...next, slots: [...next.slots, slot], next_slot_id: next.next_slot_id + 1 };
97
+ idle = slot;
98
+ }
99
+ next = (0, state_1.transitionSlot)(next, idle.id, 'assigned', {
100
+ unit,
101
+ pid: null,
102
+ phase,
103
+ last_progress_at: now.toISOString(),
104
+ }, now);
105
+ return { state: next, slotId: idle.id };
106
+ }
107
+ /**
108
+ * `sched pause` / `sched resume`: toggle the paused flag. Pausing does not
109
+ * touch live units — it only stops new assignments.
110
+ */
111
+ function setPaused(state, paused) {
112
+ return { ...state, paused };
113
+ }
114
+ /**
115
+ * `sched abandon --issue N`: mark an entry failed via the universal failure
116
+ * edge, recording the reason, and release any slot holding it
117
+ * (running/exited/verifying → failed → idle). Terminal entries cannot be
118
+ * abandoned.
119
+ */
120
+ function abandonIssue(state, issue, reason = 'abandoned', now = new Date()) {
121
+ const entry = state.entries.find((e) => e.issue === issue);
122
+ if (!entry) {
123
+ throw new types_1.SchedNotFoundError(`Queue entry not found: ${issue}`);
124
+ }
125
+ if (types_1.TERMINAL_ISSUE_STATUSES.has(entry.status)) {
126
+ throw new types_1.SchedNotFoundError(`Issue ${issue} is already ${entry.status} — nothing to abandon`);
127
+ }
128
+ let next = (0, state_1.transitionIssue)(state, issue, 'failed', { reason }, now);
129
+ const unit = `issue:${issue}`;
130
+ const released = [];
131
+ for (const slot of next.slots) {
132
+ if (slot.unit === unit && slot.status !== 'idle') {
133
+ if (slot.status !== 'failed' && slot.status !== 'complete') {
134
+ next = (0, state_1.transitionSlot)(next, slot.id, 'failed', {}, now);
135
+ }
136
+ next = (0, state_1.transitionSlot)(next, slot.id, 'idle', {}, now);
137
+ released.push(slot.id);
138
+ }
139
+ }
140
+ return { state: next, releasedSlots: released };
141
+ }
142
+ /**
143
+ * `sched abandon --batch B`: dissolve the batch and requeue every non-terminal
144
+ * member as full-cycle (RFC-0001 §D.2 dissolving → "members requeued"; F.8
145
+ * "nothing green is discarded" — members already shipped stay put).
146
+ */
147
+ function abandonBatch(state, batchId, reason = 'batch abandoned', now = new Date()) {
148
+ const batch = (0, state_1.findBatch)(state, batchId);
149
+ if (!batch) {
150
+ throw new types_1.SchedNotFoundError(`Batch not found: ${batchId}`);
151
+ }
152
+ if (types_1.TERMINAL_BATCH_STATUSES.has(batch.status)) {
153
+ throw new types_1.SchedNotFoundError(`Batch ${batchId} is already ${batch.status} — nothing to abandon`);
154
+ }
155
+ let next = (0, state_1.transitionBatch)(state, batchId, 'dissolving', {}, now);
156
+ next = (0, state_1.transitionBatch)(next, batchId, 'dissolved', {}, now);
157
+ const requeued = [];
158
+ for (const issue of batch.members) {
159
+ const entry = next.entries.find((e) => e.issue === issue);
160
+ if (!entry)
161
+ continue;
162
+ // Nothing green is discarded (F.8): terminal or already-shipped members
163
+ // keep their outcome; only active members requeue.
164
+ if (types_1.TERMINAL_ISSUE_STATUSES.has(entry.status) || types_1.SATISFIED_ISSUE_STATUSES.has(entry.status)) {
165
+ continue;
166
+ }
167
+ if (entry.status === 'queued' || entry.status === 'classified') {
168
+ // Never reached the batch rail — retag as full-cycle (metadata change,
169
+ // not a status transition) and it stays queued as-is.
170
+ next = {
171
+ ...next,
172
+ entries: next.entries.map((e) => e.issue === issue
173
+ ? { ...e, mode: 'full', batch: null, reason, updated_at: now.toISOString() }
174
+ : e),
175
+ };
176
+ requeued.push(issue);
177
+ continue;
178
+ }
179
+ if (entry.status === 'evicted' || entry.status === 'requeued') {
180
+ next = (0, state_1.transitionIssue)(next, issue, 'requeued', { mode: 'full', batch: null, reason }, now);
181
+ requeued.push(issue);
182
+ continue;
183
+ }
184
+ // Any other active state: force onto the failure rail (evicted → requeued{full}).
185
+ next = (0, state_1.transitionIssue)(next, issue, 'evicted', { reason }, now);
186
+ next = (0, state_1.transitionIssue)(next, issue, 'requeued', { mode: 'full', batch: null, reason }, now);
187
+ requeued.push(issue);
188
+ }
189
+ return { state: next, requeued };
190
+ }
191
+ //# sourceMappingURL=scheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../src/scheduler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Pure state machines for the scheduler core — RFC-0001 §D as explicit typed
3
+ * transitions. Every function here is pure: it takes a state and returns a new
4
+ * state (immutable spreads, like `packages/worktree-pool/src/pool-state.ts`),
5
+ * and every edge NOT in the tables throws `IllegalTransitionError` (AC3).
6
+ *
7
+ * Failure edges: RFC-0001 §D.1 states "any → blocked(dep-failed) |
8
+ * decision-pending | failed(escalation-cap)" — encoded generically for every
9
+ * non-terminal issue status rather than repeated per row.
10
+ */
11
+ import { type BatchEntry, type BatchStatus, type IssueStatus, type QueueEntry, type SchedState, type SlotEntry, type SlotStatus } from './types';
12
+ /** An empty state carries no timestamps — nothing exists yet to stamp. */
13
+ export declare function createEmptyState(): SchedState;
14
+ /**
15
+ * Strict validation of persisted state (mirrors pool-state's `validateState`):
16
+ * wrong schema version or malformed shape throws instead of being coerced, so
17
+ * a corrupt file is a loud failure, never a silent queue reset.
18
+ *
19
+ * Legacy schema versions are accepted and migrated: 1.0.0 (pre-#464) slots
20
+ * backfill `branch`/`last_head` to null; 1.1.0 (pre-#468) entries backfill
21
+ * `pr`/`cleanup` and the state backfills `last_pr_poll_at` to null. The state
22
+ * upgrades to the current schema on the next save.
23
+ */
24
+ export declare function validateState(data: unknown): SchedState;
25
+ export declare function transitionIssue(state: SchedState, issue: number, to: IssueStatus, patch?: Partial<QueueEntry>, now?: Date): SchedState;
26
+ export declare function transitionBatch(state: SchedState, batchId: string, to: BatchStatus, patch?: Partial<BatchEntry>, now?: Date): SchedState;
27
+ export declare function transitionSlot(state: SchedState, slotId: number, to: SlotStatus, patch?: Partial<SlotEntry>, now?: Date): SchedState;
28
+ export declare function findEntry(state: SchedState, issue: number): QueueEntry | undefined;
29
+ export declare function findBatch(state: SchedState, batchId: string): BatchEntry | undefined;
30
+ /**
31
+ * All transition tables, exposed as public API — future consumers (#464's
32
+ * dispatcher, status previews) render next-state choices from here instead of
33
+ * re-deriving the RFC tables.
34
+ */
35
+ export declare const TRANSITIONS: {
36
+ issue: (from: IssueStatus) => IssueStatus[];
37
+ batch: (from: BatchStatus) => BatchStatus[];
38
+ slot: (from: SlotStatus) => SlotStatus[];
39
+ };
40
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,KAAK,UAAU,EACf,KAAK,WAAW,EAEhB,KAAK,WAAW,EAEhB,KAAK,UAAU,EAGf,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,UAAU,EAEhB,MAAM,SAAS,CAAC;AAsGjB,0EAA0E;AAC1E,wBAAgB,gBAAgB,IAAI,UAAU,CAU7C;AAyDD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU,CAgKvD;AA0BD,wBAAgB,eAAe,CAC7B,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,WAAW,EACf,KAAK,GAAE,OAAO,CAAC,UAAU,CAAM,EAC/B,GAAG,GAAE,IAAiB,GACrB,UAAU,CAeZ;AAID,wBAAgB,eAAe,CAC7B,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,MAAM,EACf,EAAE,EAAE,WAAW,EACf,KAAK,GAAE,OAAO,CAAC,UAAU,CAAM,EAC/B,GAAG,GAAE,IAAiB,GACrB,UAAU,CAeZ;AAID,wBAAgB,cAAc,CAC5B,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,UAAU,EACd,KAAK,GAAE,OAAO,CAAC,SAAS,CAAM,EAC9B,GAAG,GAAE,IAAiB,GACrB,UAAU,CAoBZ;AAID,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAElF;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAEpF;AAED;;;;GAIG;AACH,eAAO,MAAM,WAAW;kBACR,WAAW;kBACX,WAAW;iBACZ,UAAU;CACxB,CAAC"}