@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.
- package/README.md +85 -17
- package/dist/dispatch.d.ts +128 -0
- package/dist/dispatch.d.ts.map +1 -0
- package/dist/dispatch.js +300 -0
- package/dist/dispatch.js.map +1 -0
- package/dist/engine.d.ts +111 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +936 -0
- package/dist/engine.js.map +1 -0
- package/dist/enqueue.d.ts +38 -0
- package/dist/enqueue.d.ts.map +1 -0
- package/dist/enqueue.js +222 -0
- package/dist/enqueue.js.map +1 -0
- package/dist/groundtruth.d.ts +135 -0
- package/dist/groundtruth.d.ts.map +1 -0
- package/dist/groundtruth.js +259 -0
- package/dist/groundtruth.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +101 -0
- package/dist/index.js.map +1 -0
- package/dist/journal.d.ts +32 -0
- package/dist/journal.d.ts.map +1 -0
- package/dist/journal.js +113 -0
- package/dist/journal.js.map +1 -0
- package/dist/persist.d.ts +54 -0
- package/dist/persist.d.ts.map +1 -0
- package/dist/persist.js +348 -0
- package/dist/persist.js.map +1 -0
- package/dist/project.d.ts +41 -0
- package/dist/project.d.ts.map +1 -0
- package/dist/project.js +124 -0
- package/dist/project.js.map +1 -0
- package/dist/readiness.d.ts +43 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +102 -0
- package/dist/readiness.js.map +1 -0
- package/dist/scheduler.d.ts +78 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +191 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/state.d.ts +40 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +391 -0
- package/dist/state.js.map +1 -0
- package/dist/status.d.ts +44 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +82 -0
- package/dist/status.js.map +1 -0
- package/dist/teardown.d.ts +57 -0
- package/dist/teardown.d.ts.map +1 -0
- package/dist/teardown.js +242 -0
- package/dist/teardown.js.map +1 -0
- package/dist/types.d.ts +253 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +91 -0
- package/dist/types.js.map +1 -0
- package/package.json +1 -1
package/dist/teardown.js
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Script-based teardown for merged units (#468 AC2) — the tail work that the
|
|
4
|
+
* fleet pattern used to re-dispatch a whole full-cycle run for.
|
|
5
|
+
*
|
|
6
|
+
* Two scripts, chosen by the run's `setup` milestone:
|
|
7
|
+
* - pool-claimed worktree → `worktree-pool return --path <wt> --json` — the
|
|
8
|
+
* pool's own self-check (#453: `verification.entry_status`) is the
|
|
9
|
+
* verification; a non-`warm` entry or a failed exit is a failed step.
|
|
10
|
+
* - cold worktree → `git worktree remove --force <wt>` + a path-gone check
|
|
11
|
+
* (verify-first, so a re-run after a crash between subprocess and state
|
|
12
|
+
* write is idempotent).
|
|
13
|
+
*
|
|
14
|
+
* `--force` on the remove is deliberate: the WIP-sync rule pushes everything
|
|
15
|
+
* durable to origin, so untracked leftovers (logs, warmup status) are trash.
|
|
16
|
+
*
|
|
17
|
+
* A failed teardown is DEGRADATION, never unit failure — the PR is already
|
|
18
|
+
* merged. The outcome is recorded as `cleanup=failed-<step>` on the entry and
|
|
19
|
+
* journaled, and the report agent is dispatched regardless so the failure is
|
|
20
|
+
* surfaced in the report.
|
|
21
|
+
*
|
|
22
|
+
* Everything runs through an injectable `ExecFn` (the project.ts pattern) —
|
|
23
|
+
* tests script the subprocesses, no real pool or git is touched.
|
|
24
|
+
*/
|
|
25
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
26
|
+
if (k2 === undefined) k2 = k;
|
|
27
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
28
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
29
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
30
|
+
}
|
|
31
|
+
Object.defineProperty(o, k2, desc);
|
|
32
|
+
}) : (function(o, m, k, k2) {
|
|
33
|
+
if (k2 === undefined) k2 = k;
|
|
34
|
+
o[k2] = m[k];
|
|
35
|
+
}));
|
|
36
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
37
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
38
|
+
}) : function(o, v) {
|
|
39
|
+
o["default"] = v;
|
|
40
|
+
});
|
|
41
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
42
|
+
var ownKeys = function(o) {
|
|
43
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
44
|
+
var ar = [];
|
|
45
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
46
|
+
return ar;
|
|
47
|
+
};
|
|
48
|
+
return ownKeys(o);
|
|
49
|
+
};
|
|
50
|
+
return function (mod) {
|
|
51
|
+
if (mod && mod.__esModule) return mod;
|
|
52
|
+
var result = {};
|
|
53
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
54
|
+
__setModuleDefault(result, mod);
|
|
55
|
+
return result;
|
|
56
|
+
};
|
|
57
|
+
})();
|
|
58
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
59
|
+
exports.TEARDOWN_TIMEOUT_MS = void 0;
|
|
60
|
+
exports.isSafeWorktree = isSafeWorktree;
|
|
61
|
+
exports.runTeardown = runTeardown;
|
|
62
|
+
const fs = __importStar(require("node:fs"));
|
|
63
|
+
const path = __importStar(require("node:path"));
|
|
64
|
+
/** Timeout for teardown subprocesses — pool return shells out through npx and recycles a worktree (git ops), which is slow. */
|
|
65
|
+
exports.TEARDOWN_TIMEOUT_MS = 120_000;
|
|
66
|
+
/**
|
|
67
|
+
* Pool CLI invocation. Requires ≥0.6.0 — `status --json`, `return --json`,
|
|
68
|
+
* and the `verification.entry_status` self-check (#453) all landed there, so
|
|
69
|
+
* `^0.5.1` (which resolves to ≤0.5.3) cannot ever satisfy the JSON contract
|
|
70
|
+
* and every pool teardown would record `failed-pool-return`. Versions ≤0.5.0
|
|
71
|
+
* additionally carry the data-loss `gc` bug — never pin lower.
|
|
72
|
+
*/
|
|
73
|
+
const POOL_BIN = 'npx';
|
|
74
|
+
const POOL_ARGS_PREFIX = ['-y', '@ai-dossier/worktree-pool@^0.6.0'];
|
|
75
|
+
/**
|
|
76
|
+
* Containment check for a worktree path recovered from an issue comment
|
|
77
|
+
* (#468): the path must be absolute and fully resolved (no `..`/`.`/symlink
|
|
78
|
+
* variance), and — for COLD worktrees — live under one of the two worktree
|
|
79
|
+
* roots this project uses: `<repo>/worktrees/` (setup-issue-workflow's
|
|
80
|
+
* inside-repo convention) or `<repo>/../worktrees/` (the AGENTS.md sibling
|
|
81
|
+
* convention). A crafted milestone can otherwise make the scheduler
|
|
82
|
+
* `git worktree remove --force` ANY registered worktree — including a
|
|
83
|
+
* parallel agent's (CWE-22). Pool-claimed worktrees are validated by the
|
|
84
|
+
* pool itself (return only accepts paths in pool state), so containment
|
|
85
|
+
* applies to the cold path only.
|
|
86
|
+
*/
|
|
87
|
+
function isSafeWorktree(repoRoot, worktree) {
|
|
88
|
+
if (worktree.includes('\0'))
|
|
89
|
+
return false;
|
|
90
|
+
if (!path.isAbsolute(worktree))
|
|
91
|
+
return false;
|
|
92
|
+
if (path.resolve(worktree) !== worktree)
|
|
93
|
+
return false;
|
|
94
|
+
const roots = [path.resolve(repoRoot, 'worktrees'), path.resolve(repoRoot, '..', 'worktrees')];
|
|
95
|
+
return roots.some((root) => worktree.startsWith(root + path.sep));
|
|
96
|
+
}
|
|
97
|
+
/** The repo's toplevel directory (containment root), or null when git cannot say. */
|
|
98
|
+
function repoToplevel(exec, repoDir) {
|
|
99
|
+
const out = exec('git', ['rev-parse', '--show-toplevel'], repoDir);
|
|
100
|
+
return out !== null && path.isAbsolute(out) ? path.resolve(out) : null;
|
|
101
|
+
}
|
|
102
|
+
/** Path-listing of `git worktree list --porcelain` (null when the call fails). */
|
|
103
|
+
function worktreeList(exec, repoDir) {
|
|
104
|
+
return exec('git', ['worktree', 'list', '--porcelain'], repoDir);
|
|
105
|
+
}
|
|
106
|
+
/** Whether `worktree` still appears in git's worktree listing. */
|
|
107
|
+
function listedAsWorktree(exec, repoDir, worktree) {
|
|
108
|
+
const list = worktreeList(exec, repoDir);
|
|
109
|
+
if (list === null)
|
|
110
|
+
return true; // unknown — assume still present (conservative)
|
|
111
|
+
return list
|
|
112
|
+
.split('\n')
|
|
113
|
+
.some((line) => line.startsWith('worktree ') && line.slice('worktree '.length) === worktree);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Run the teardown script for one merged unit. `info` comes from the unit's
|
|
117
|
+
* setup milestone (groundtruth.setupInfo); a missing `info` is the caller's
|
|
118
|
+
* `failed-missing-setup-info` case — this function always has one.
|
|
119
|
+
*/
|
|
120
|
+
function runTeardown(exec, repoDir, info, fsExists = (p) => {
|
|
121
|
+
try {
|
|
122
|
+
return fs.existsSync(p);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
}) {
|
|
128
|
+
// The worktree path originates from an issue comment written by the spawned
|
|
129
|
+
// agent — validate it before any destructive subprocess (CWE-22).
|
|
130
|
+
if (info.worktree.includes('\0') || !path.isAbsolute(info.worktree)) {
|
|
131
|
+
return {
|
|
132
|
+
cleanup: 'failed-invalid-worktree',
|
|
133
|
+
detail: `worktree path rejected (must be absolute): ${info.worktree}`,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
if (info.poolClaimed) {
|
|
137
|
+
// Pool membership is validated by the pool's own `return` (it only
|
|
138
|
+
// accepts paths in pool state) — no local containment root applies.
|
|
139
|
+
return poolReturn(exec, repoDir, info.worktree);
|
|
140
|
+
}
|
|
141
|
+
const root = repoToplevel(exec, repoDir) ?? path.resolve(repoDir);
|
|
142
|
+
if (!isSafeWorktree(root, info.worktree)) {
|
|
143
|
+
return {
|
|
144
|
+
cleanup: 'failed-invalid-worktree',
|
|
145
|
+
detail: `worktree path rejected (must be a resolved path under the repo's worktrees root ${path.join(root, 'worktrees')}): ${info.worktree}`,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
return worktreeRemove(exec, repoDir, info.worktree, fsExists);
|
|
149
|
+
}
|
|
150
|
+
function poolReturn(exec, repoDir, worktree) {
|
|
151
|
+
// Verify-first idempotency: if the pool already lists this path as a warm
|
|
152
|
+
// (unassigned) spare, a previous return landed and the state write was
|
|
153
|
+
// lost — claim done rather than erroring a second return.
|
|
154
|
+
const statusJson = exec(POOL_BIN, [...POOL_ARGS_PREFIX, 'status', '--json'], repoDir);
|
|
155
|
+
if (statusJson !== null && poolEntryFor(statusJson, worktree) === 'warm') {
|
|
156
|
+
return { cleanup: 'done', detail: 'already returned to pool (idempotent re-run)' };
|
|
157
|
+
}
|
|
158
|
+
const out = exec(POOL_BIN, [...POOL_ARGS_PREFIX, 'return', '--path', worktree, '--json'], repoDir);
|
|
159
|
+
if (out === null) {
|
|
160
|
+
return {
|
|
161
|
+
cleanup: 'failed-pool-return',
|
|
162
|
+
detail: 'worktree-pool return failed (non-zero exit, timeout, or npx unavailable) — entry left unverified; inspect `worktree-pool status`',
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
// Verified before claimed: the pool's own self-check must report the
|
|
166
|
+
// recycled entry as `warm` — anything else is a failed step.
|
|
167
|
+
const entryStatus = parseReturnVerification(out);
|
|
168
|
+
if (entryStatus === null) {
|
|
169
|
+
return {
|
|
170
|
+
cleanup: 'failed-pool-return',
|
|
171
|
+
detail: 'worktree-pool return produced unparseable output',
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
if (entryStatus !== 'warm') {
|
|
175
|
+
return {
|
|
176
|
+
cleanup: 'failed-pool-return',
|
|
177
|
+
detail: `pool self-check reports entry_status=${entryStatus}`,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
return { cleanup: 'done', detail: 'returned to pool as a warm spare' };
|
|
181
|
+
}
|
|
182
|
+
function worktreeRemove(exec, repoDir, worktree, fsExists) {
|
|
183
|
+
// Verify-first idempotency: already gone → a previous remove landed.
|
|
184
|
+
if (!fsExists(worktree) && !listedAsWorktree(exec, repoDir, worktree)) {
|
|
185
|
+
return { cleanup: 'done', detail: 'worktree already removed (idempotent re-run)' };
|
|
186
|
+
}
|
|
187
|
+
const out = exec('git', ['worktree', 'remove', '--force', '--', worktree], repoDir);
|
|
188
|
+
if (out === null) {
|
|
189
|
+
return {
|
|
190
|
+
cleanup: 'failed-worktree-remove',
|
|
191
|
+
detail: 'git worktree remove failed (non-zero exit or timeout)',
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
// Verified before claimed: the path must be gone AND git must no longer
|
|
195
|
+
// list it — a lingering entry means the removal did not fully land.
|
|
196
|
+
if (fsExists(worktree) || listedAsWorktree(exec, repoDir, worktree)) {
|
|
197
|
+
return {
|
|
198
|
+
cleanup: 'failed-worktree-remove',
|
|
199
|
+
detail: 'worktree still present after git worktree remove',
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
return { cleanup: 'done', detail: 'worktree removed' };
|
|
203
|
+
}
|
|
204
|
+
/** The pool entry status (`warm`/`assigned`/…) for `worktree`, or null when absent/unparseable. */
|
|
205
|
+
function poolEntryFor(statusJson, worktree) {
|
|
206
|
+
try {
|
|
207
|
+
const parsed = JSON.parse(statusJson);
|
|
208
|
+
if (parsed === null || typeof parsed !== 'object')
|
|
209
|
+
return null;
|
|
210
|
+
const worktrees = parsed.worktrees;
|
|
211
|
+
if (!Array.isArray(worktrees))
|
|
212
|
+
return null;
|
|
213
|
+
for (const raw of worktrees) {
|
|
214
|
+
if (raw === null || typeof raw !== 'object')
|
|
215
|
+
continue;
|
|
216
|
+
const entry = raw;
|
|
217
|
+
if (entry.path === worktree && typeof entry.status === 'string')
|
|
218
|
+
return entry.status;
|
|
219
|
+
}
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/** `verification.entry_status` of a successful `return --json`, or null when unparseable. */
|
|
227
|
+
function parseReturnVerification(returnJson) {
|
|
228
|
+
try {
|
|
229
|
+
const parsed = JSON.parse(returnJson);
|
|
230
|
+
if (parsed === null || typeof parsed !== 'object')
|
|
231
|
+
return null;
|
|
232
|
+
const verification = parsed.verification;
|
|
233
|
+
if (verification === null || typeof verification !== 'object')
|
|
234
|
+
return null;
|
|
235
|
+
const status = verification.entry_status;
|
|
236
|
+
return typeof status === 'string' ? status : null;
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
//# sourceMappingURL=teardown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"teardown.js","sourceRoot":"","sources":["../src/teardown.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for the deterministic scheduler core (RFC-0001 §B/C.1/D, issue #460).
|
|
3
|
+
*
|
|
4
|
+
* The state unions below are the RFC-0001 §D state machines frozen into types.
|
|
5
|
+
* `state.ts` owns the transition tables; nothing here does I/O. The package
|
|
6
|
+
* itself never invokes an LLM: it spawns the agent process the operator
|
|
7
|
+
* configured (dispatch.ts) and reads ground truth via injectable subprocess
|
|
8
|
+
* exec (project.ts, groundtruth.ts) — never an LLM call of its own (AC7).
|
|
9
|
+
*/
|
|
10
|
+
/** Execution mode of a queue entry. `full` runs the unchanged full-cycle; `slot` runs as a batch member. */
|
|
11
|
+
export type CycleMode = 'full' | 'slot';
|
|
12
|
+
/** Model tier the entry is dispatched at (RFC-0001 role-based routing: mechanical / generation / judgment). */
|
|
13
|
+
export type ModelTier = 'mechanical' | 'mid' | 'strong';
|
|
14
|
+
/**
|
|
15
|
+
* Issue lifecycle per RFC-0001 §D.1:
|
|
16
|
+
*
|
|
17
|
+
* ```
|
|
18
|
+
* queued → classified{full|slot}
|
|
19
|
+
* full: → dispatched → (full-cycle's own phase trail) → shipped → done
|
|
20
|
+
* #468 detached ship: dispatched → parked{pr} (PR on auto-merge;
|
|
21
|
+
* the watcher owns the merge wait) → shipped → done
|
|
22
|
+
* slot: → batched(b) → waiting → in-work → committed(range) → validated
|
|
23
|
+
* → shipped-in-batch → done
|
|
24
|
+
* failure edges (any state):
|
|
25
|
+
* in-work/committed → evicted(reason) → requeued{full}
|
|
26
|
+
* any → blocked(dep-failed) | decision-pending | failed(escalation-cap)
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export type IssueStatus = 'queued' | 'classified' | 'dispatched' | 'parked' | 'shipped' | 'done' | 'batched' | 'waiting' | 'in-work' | 'committed' | 'validated' | 'shipped-in-batch' | 'evicted' | 'requeued' | 'blocked' | 'decision-pending' | 'failed';
|
|
30
|
+
/** Issue statuses that cannot transition further. */
|
|
31
|
+
export declare const TERMINAL_ISSUE_STATUSES: ReadonlySet<IssueStatus>;
|
|
32
|
+
/**
|
|
33
|
+
* Statuses that mean the issue's work has merged. Dependency edges gate on
|
|
34
|
+
* these: "an issue with an unmerged dependency is never runnable" (AC5).
|
|
35
|
+
*/
|
|
36
|
+
export declare const SATISFIED_ISSUE_STATUSES: ReadonlySet<IssueStatus>;
|
|
37
|
+
/** Teardown outcome values (#468): verified cleanup or a failed step. */
|
|
38
|
+
export type CleanupStatus = 'done' | `failed-${string}`;
|
|
39
|
+
/**
|
|
40
|
+
* Batch lifecycle per RFC-0001 §D.2:
|
|
41
|
+
*
|
|
42
|
+
* ```
|
|
43
|
+
* forming → ready → executing(member i/N) ⟲ → validating → reviewing → shipping
|
|
44
|
+
* → awaiting-merge → merged → deployed → reported → done
|
|
45
|
+
* failure edges:
|
|
46
|
+
* validating → attributing → fixing(1 bounded attempt) → validating
|
|
47
|
+
* → evicting(revert range) → validating
|
|
48
|
+
* evictions > ⅓ OR revert-conflict → dissolving → members requeued
|
|
49
|
+
* awaiting-merge: CONFLICTING | auto-merge-blocked → rebasing → re-validating → shipping
|
|
50
|
+
* (2nd failure → dissolved)
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export type BatchStatus = 'forming' | 'ready' | 'executing' | 'validating' | 'attributing' | 'fixing' | 'evicting' | 'reviewing' | 'shipping' | 'awaiting-merge' | 'rebasing' | 're-validating' | 'merged' | 'deployed' | 'reported' | 'done' | 'dissolving' | 'dissolved';
|
|
54
|
+
/** Batch statuses that cannot transition further. */
|
|
55
|
+
export declare const TERMINAL_BATCH_STATUSES: ReadonlySet<BatchStatus>;
|
|
56
|
+
/**
|
|
57
|
+
* Batch statuses that mean the batch's PR has merged. Cross-batch dependency
|
|
58
|
+
* edges gate on these (a batch is never dispatched while a batch it depends
|
|
59
|
+
* on is unmerged — RFC-0001 §E.4 "scheduler gates on merge").
|
|
60
|
+
*/
|
|
61
|
+
export declare const MERGED_BATCH_STATUSES: ReadonlySet<BatchStatus>;
|
|
62
|
+
/**
|
|
63
|
+
* Worker slot lifecycle per RFC-0001 §D.3:
|
|
64
|
+
*
|
|
65
|
+
* ```
|
|
66
|
+
* idle → assigned(unit) → running(pid, phase, last_progress_at)
|
|
67
|
+
* → exited → verifying(reconcile vs GitHub/runstate) → complete → idle
|
|
68
|
+
* stall: running[30 min no progress] → recovering(redispatch tier+1, ≤2) → running | failed → idle
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export type SlotStatus = 'idle' | 'assigned' | 'running' | 'exited' | 'verifying' | 'complete' | 'recovering' | 'failed';
|
|
72
|
+
/** Slot statuses that hold a live unit against `max_slots`. */
|
|
73
|
+
export declare const LIVE_SLOT_STATUSES: ReadonlySet<SlotStatus>;
|
|
74
|
+
/** One queued unit of work (RFC-0001 §C.1 "queue entries"). */
|
|
75
|
+
export interface QueueEntry {
|
|
76
|
+
/** GitHub issue number. Unique among active entries. */
|
|
77
|
+
issue: number;
|
|
78
|
+
/** Execution mode for this issue. */
|
|
79
|
+
mode: CycleMode;
|
|
80
|
+
/** Batch id when `mode === 'slot'`; always null for `full`. */
|
|
81
|
+
batch: string | null;
|
|
82
|
+
/** Issue numbers this entry depends on (edges gate readiness). */
|
|
83
|
+
deps: number[];
|
|
84
|
+
/** Model tier the entry is dispatched at. */
|
|
85
|
+
tier: ModelTier;
|
|
86
|
+
/** Current D.1 state. */
|
|
87
|
+
status: IssueStatus;
|
|
88
|
+
/** Free-form reason attached to failure-edge transitions (evicted/blocked/failed). */
|
|
89
|
+
reason: string | null;
|
|
90
|
+
/**
|
|
91
|
+
* PR number the unit parked on `auto-merge` (#468), from the ship phase's
|
|
92
|
+
* `awaiting-merge` milestone (`pr=` key). Set when the agent exits parked;
|
|
93
|
+
* drives the PR watcher until the merge is accepted.
|
|
94
|
+
*/
|
|
95
|
+
pr: number | null;
|
|
96
|
+
/**
|
|
97
|
+
* Teardown outcome for a merged unit (#468): `done` or `failed-<step>`, or
|
|
98
|
+
* null while teardown is still pending. Recorded only after the cleanup
|
|
99
|
+
* was verified (pool return self-check / worktree path gone).
|
|
100
|
+
*/
|
|
101
|
+
cleanup: CleanupStatus | null;
|
|
102
|
+
enqueued_at: string;
|
|
103
|
+
updated_at: string;
|
|
104
|
+
}
|
|
105
|
+
/** A batch of slot-mode issues sharing one lifecycle (RFC-0001 §C.4/E.4). */
|
|
106
|
+
export interface BatchEntry {
|
|
107
|
+
id: string;
|
|
108
|
+
status: BatchStatus;
|
|
109
|
+
/** Member issue numbers, in dispatch order. */
|
|
110
|
+
members: number[];
|
|
111
|
+
base_branch: string;
|
|
112
|
+
/** Index of the member currently in work, when status is `executing` (1-based member pointer). */
|
|
113
|
+
executing_member: number;
|
|
114
|
+
created_at: string;
|
|
115
|
+
updated_at: string;
|
|
116
|
+
}
|
|
117
|
+
/** A worker slot (RFC-0001 §D.3). */
|
|
118
|
+
export interface SlotEntry {
|
|
119
|
+
id: number;
|
|
120
|
+
status: SlotStatus;
|
|
121
|
+
/** Unit identifier currently held: `issue:<n>` or `batch:<id>`; null when idle. */
|
|
122
|
+
unit: string | null;
|
|
123
|
+
/** OS pid of the spawned agent process, when known (#464 dispatch). */
|
|
124
|
+
pid: number | null;
|
|
125
|
+
/**
|
|
126
|
+
* `/proc/<pid>/stat` start-time (field 22) recorded at spawn, when
|
|
127
|
+
* available (Linux). Persisted so a restarted sched can verify a state.json
|
|
128
|
+
* pid still belongs to our agent — a mismatched start time means the pid
|
|
129
|
+
* was reused by an unrelated process and must never be signalled (decision
|
|
130
|
+
* 1, option C). Null on platforms without /proc: best-effort.
|
|
131
|
+
*/
|
|
132
|
+
pid_start: number | null;
|
|
133
|
+
/** Scheduler phase the unit is in, when running. */
|
|
134
|
+
phase: string | null;
|
|
135
|
+
/** Last progress signal (new milestone or pushed commit) — stall-timer anchor. */
|
|
136
|
+
last_progress_at: string | null;
|
|
137
|
+
/** Working branch of the unit, captured from the setup milestone's `branch=` key. */
|
|
138
|
+
branch: string | null;
|
|
139
|
+
/** Last seen head sha of `branch` on origin — the "new pushed commit" stall signal. */
|
|
140
|
+
last_head: string | null;
|
|
141
|
+
/** Recovery attempts for the current unit (stall ladder, cap 2 — RFC-0001 §C.1). */
|
|
142
|
+
recoveries: number;
|
|
143
|
+
updated_at: string;
|
|
144
|
+
}
|
|
145
|
+
/** Hot operational truth, persisted transactionally to `state.json` (RFC-0001 §D.4). */
|
|
146
|
+
export interface SchedState {
|
|
147
|
+
schema_version: typeof SCHEMA_VERSION;
|
|
148
|
+
/** When true, `computeAssignments` returns no assignments (sched pause). */
|
|
149
|
+
paused: boolean;
|
|
150
|
+
entries: QueueEntry[];
|
|
151
|
+
batches: BatchEntry[];
|
|
152
|
+
slots: SlotEntry[];
|
|
153
|
+
/** Monotonic counter for stable slot ids. */
|
|
154
|
+
next_slot_id: number;
|
|
155
|
+
/**
|
|
156
|
+
* When the PR watcher last polled (#468) — parked PRs are polled every
|
|
157
|
+
* `pr_poll_interval_ms` (default 150 s). Polled on each reconcile tick
|
|
158
|
+
* when due (a `reconcile_interval_ms` longer than the interval slows the
|
|
159
|
+
* effective cadence); persisted so the cadence survives a sched restart.
|
|
160
|
+
*/
|
|
161
|
+
last_pr_poll_at: string | null;
|
|
162
|
+
}
|
|
163
|
+
/** Durable intent, persisted separately in `config.json` (state.json is rebuildable hot truth). */
|
|
164
|
+
export interface SchedConfig {
|
|
165
|
+
max_slots: number;
|
|
166
|
+
/** Stall timeout in ms — no new milestone AND no new pushed commit for this long → redispatch stronger (default 30 min). */
|
|
167
|
+
stall_timeout_ms?: number;
|
|
168
|
+
/** Reconciliation tick interval in ms (default 60 000). */
|
|
169
|
+
reconcile_interval_ms?: number;
|
|
170
|
+
/**
|
|
171
|
+
* Parked-PR poll interval in ms (#468, default 150 000 — "every 2–3 min").
|
|
172
|
+
* Checked on each reconcile tick when due; see `SchedState.last_pr_poll_at`.
|
|
173
|
+
*/
|
|
174
|
+
pr_poll_interval_ms?: number;
|
|
175
|
+
/** Agent dispatch settings (#464); every field optional with engine defaults. */
|
|
176
|
+
dispatch?: DispatchConfig;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Agent dispatch configuration (#464). The command is a template: `{model}`
|
|
180
|
+
* and `{issue}` placeholders are substituted per dispatch; a `{model}` item
|
|
181
|
+
* whose tier has no model configured drops together with its flag.
|
|
182
|
+
*/
|
|
183
|
+
export interface DispatchConfig {
|
|
184
|
+
/** Command template, e.g. `['claude','-p','--output-format','json','--model','{model}']`. */
|
|
185
|
+
command?: string[];
|
|
186
|
+
/** Prompt template sent on the child's stdin; `{issue}` substituted. */
|
|
187
|
+
prompt?: string;
|
|
188
|
+
/** Tier → model id/alias mapping (defaults: haiku / sonnet / opus). */
|
|
189
|
+
tier_models?: Partial<Record<ModelTier, string>>;
|
|
190
|
+
/**
|
|
191
|
+
* Prompt template for the report agent dispatched after a merged PR
|
|
192
|
+
* (#468); `{issue}`, `{pr}` and `{cleanup}` substituted. Defaults to
|
|
193
|
+
* `DEFAULT_REPORT_PROMPT_TEMPLATE`.
|
|
194
|
+
*/
|
|
195
|
+
report_prompt?: string;
|
|
196
|
+
}
|
|
197
|
+
/** The escalation ladder: one tier stronger, or null at the top (RFC-0001 §C.1). */
|
|
198
|
+
export declare const TIER_LADDER: Readonly<Record<ModelTier, ModelTier | null>>;
|
|
199
|
+
/** The ladder as an ordered array (weakest first) — the single ordering source. */
|
|
200
|
+
export declare const TIER_ORDER: readonly ModelTier[];
|
|
201
|
+
/** Cap on recovery attempts before a unit fails (RFC-0001 §C.1 "cap 2"). */
|
|
202
|
+
export declare const ESCALATION_CAP = 2;
|
|
203
|
+
/** Default stall timeout: 30 minutes without a milestone or pushed commit (RFC-0001 §C.1). */
|
|
204
|
+
export declare const DEFAULT_STALL_TIMEOUT_MS: number;
|
|
205
|
+
/** Default reconciliation tick: ~60s (RFC-0001 §C.1). */
|
|
206
|
+
export declare const DEFAULT_RECONCILE_INTERVAL_MS: number;
|
|
207
|
+
/** Default parked-PR poll interval: 2.5 min (#468 AC1 "every 2–3 min"). */
|
|
208
|
+
export declare const DEFAULT_PR_POLL_INTERVAL_MS: number;
|
|
209
|
+
export declare const SCHEMA_VERSION: "1.2.0";
|
|
210
|
+
/** Schema versions `validateState` accepts on load (migrated to SCHEMA_VERSION on save). */
|
|
211
|
+
export declare const LEGACY_SCHEMA_VERSIONS: readonly string[];
|
|
212
|
+
export declare const CONFIG_SCHEMA_VERSION: "1.2.0";
|
|
213
|
+
/** Config schema versions `loadConfig` accepts (older configs carry only max_slots). */
|
|
214
|
+
export declare const LEGACY_CONFIG_SCHEMA_VERSIONS: readonly string[];
|
|
215
|
+
/** Config file shape (schema_version + the config itself). */
|
|
216
|
+
export interface SchedConfigFile {
|
|
217
|
+
schema_version: string;
|
|
218
|
+
max_slots: number;
|
|
219
|
+
stall_timeout_ms?: number;
|
|
220
|
+
reconcile_interval_ms?: number;
|
|
221
|
+
pr_poll_interval_ms?: number;
|
|
222
|
+
dispatch?: DispatchConfig;
|
|
223
|
+
}
|
|
224
|
+
export declare const DEFAULT_MAX_SLOTS = 3;
|
|
225
|
+
/** Bounds for `max_slots` when reading `config.json` (named — not magic numbers in persist.ts). */
|
|
226
|
+
export declare const MIN_MAX_SLOTS = 1;
|
|
227
|
+
export declare const MAX_MAX_SLOTS = 64;
|
|
228
|
+
/** Thrown by every non-declared state-machine edge. */
|
|
229
|
+
export declare class IllegalTransitionError extends Error {
|
|
230
|
+
readonly kind: 'issue' | 'batch' | 'slot';
|
|
231
|
+
readonly from: string;
|
|
232
|
+
readonly to: string;
|
|
233
|
+
constructor(kind: 'issue' | 'batch' | 'slot', from: string, to: string);
|
|
234
|
+
}
|
|
235
|
+
/** Thrown when an issue, batch, or slot id does not exist in the state. */
|
|
236
|
+
export declare class SchedNotFoundError extends Error {
|
|
237
|
+
constructor(message: string);
|
|
238
|
+
}
|
|
239
|
+
/** Every event the engine journals to `events.jsonl` (append-only). */
|
|
240
|
+
export type JournalEventName = 'assigned' | 'spawned' | 'exit-detected' | 'orphan-pid' | 'external-advance' | 'progress' | 'phase-updated' | 'verify-complete' | 'verify-incomplete' | 'stalled' | 'redispatched' | 'unit-failed' | 'dependents-blocked' | 'requeued' | 'ground-truth-unreachable' | 'tick-failed' | 'pr-parked' | 'merge-accepted' | 'pr-watch-failed' | 'pr-watch-waiting' | 'teardown-done' | 'teardown-failed' | 'report-dispatched' | 'report-failed';
|
|
241
|
+
/** One journaled event. `ts` is stamped by the journal, never by callers. */
|
|
242
|
+
export interface JournalEvent {
|
|
243
|
+
ts: string;
|
|
244
|
+
event: JournalEventName;
|
|
245
|
+
/** Unit the event concerns (`issue:<n>` / `batch:<id>`), when applicable. */
|
|
246
|
+
unit?: string;
|
|
247
|
+
slot?: number;
|
|
248
|
+
issue?: number;
|
|
249
|
+
pid?: number;
|
|
250
|
+
tier?: ModelTier;
|
|
251
|
+
detail?: string;
|
|
252
|
+
}
|
|
253
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Types for the deterministic scheduler core (RFC-0001 §B/C.1/D, issue #460).
|
|
4
|
+
*
|
|
5
|
+
* The state unions below are the RFC-0001 §D state machines frozen into types.
|
|
6
|
+
* `state.ts` owns the transition tables; nothing here does I/O. The package
|
|
7
|
+
* itself never invokes an LLM: it spawns the agent process the operator
|
|
8
|
+
* configured (dispatch.ts) and reads ground truth via injectable subprocess
|
|
9
|
+
* exec (project.ts, groundtruth.ts) — never an LLM call of its own (AC7).
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.SchedNotFoundError = exports.IllegalTransitionError = exports.MAX_MAX_SLOTS = exports.MIN_MAX_SLOTS = exports.DEFAULT_MAX_SLOTS = exports.LEGACY_CONFIG_SCHEMA_VERSIONS = exports.CONFIG_SCHEMA_VERSION = exports.LEGACY_SCHEMA_VERSIONS = exports.SCHEMA_VERSION = exports.DEFAULT_PR_POLL_INTERVAL_MS = exports.DEFAULT_RECONCILE_INTERVAL_MS = exports.DEFAULT_STALL_TIMEOUT_MS = exports.ESCALATION_CAP = exports.TIER_ORDER = exports.TIER_LADDER = exports.LIVE_SLOT_STATUSES = exports.MERGED_BATCH_STATUSES = exports.TERMINAL_BATCH_STATUSES = exports.SATISFIED_ISSUE_STATUSES = exports.TERMINAL_ISSUE_STATUSES = void 0;
|
|
13
|
+
/** Issue statuses that cannot transition further. */
|
|
14
|
+
exports.TERMINAL_ISSUE_STATUSES = new Set(['done', 'failed']);
|
|
15
|
+
/**
|
|
16
|
+
* Statuses that mean the issue's work has merged. Dependency edges gate on
|
|
17
|
+
* these: "an issue with an unmerged dependency is never runnable" (AC5).
|
|
18
|
+
*/
|
|
19
|
+
exports.SATISFIED_ISSUE_STATUSES = new Set([
|
|
20
|
+
'shipped',
|
|
21
|
+
'shipped-in-batch',
|
|
22
|
+
'done',
|
|
23
|
+
]);
|
|
24
|
+
/** Batch statuses that cannot transition further. */
|
|
25
|
+
exports.TERMINAL_BATCH_STATUSES = new Set(['done', 'dissolved']);
|
|
26
|
+
/**
|
|
27
|
+
* Batch statuses that mean the batch's PR has merged. Cross-batch dependency
|
|
28
|
+
* edges gate on these (a batch is never dispatched while a batch it depends
|
|
29
|
+
* on is unmerged — RFC-0001 §E.4 "scheduler gates on merge").
|
|
30
|
+
*/
|
|
31
|
+
exports.MERGED_BATCH_STATUSES = new Set([
|
|
32
|
+
'merged',
|
|
33
|
+
'deployed',
|
|
34
|
+
'reported',
|
|
35
|
+
'done',
|
|
36
|
+
]);
|
|
37
|
+
/** Slot statuses that hold a live unit against `max_slots`. */
|
|
38
|
+
exports.LIVE_SLOT_STATUSES = new Set([
|
|
39
|
+
'assigned',
|
|
40
|
+
'running',
|
|
41
|
+
'recovering',
|
|
42
|
+
]);
|
|
43
|
+
/** The escalation ladder: one tier stronger, or null at the top (RFC-0001 §C.1). */
|
|
44
|
+
exports.TIER_LADDER = {
|
|
45
|
+
mechanical: 'mid',
|
|
46
|
+
mid: 'strong',
|
|
47
|
+
strong: null,
|
|
48
|
+
};
|
|
49
|
+
/** The ladder as an ordered array (weakest first) — the single ordering source. */
|
|
50
|
+
exports.TIER_ORDER = ['mechanical', 'mid', 'strong'];
|
|
51
|
+
/** Cap on recovery attempts before a unit fails (RFC-0001 §C.1 "cap 2"). */
|
|
52
|
+
exports.ESCALATION_CAP = 2;
|
|
53
|
+
/** Default stall timeout: 30 minutes without a milestone or pushed commit (RFC-0001 §C.1). */
|
|
54
|
+
exports.DEFAULT_STALL_TIMEOUT_MS = 30 * 60 * 1000;
|
|
55
|
+
/** Default reconciliation tick: ~60s (RFC-0001 §C.1). */
|
|
56
|
+
exports.DEFAULT_RECONCILE_INTERVAL_MS = 60 * 1000;
|
|
57
|
+
/** Default parked-PR poll interval: 2.5 min (#468 AC1 "every 2–3 min"). */
|
|
58
|
+
exports.DEFAULT_PR_POLL_INTERVAL_MS = 150 * 1000;
|
|
59
|
+
exports.SCHEMA_VERSION = '1.2.0';
|
|
60
|
+
/** Schema versions `validateState` accepts on load (migrated to SCHEMA_VERSION on save). */
|
|
61
|
+
exports.LEGACY_SCHEMA_VERSIONS = ['1.0.0', '1.1.0'];
|
|
62
|
+
exports.CONFIG_SCHEMA_VERSION = '1.2.0';
|
|
63
|
+
/** Config schema versions `loadConfig` accepts (older configs carry only max_slots). */
|
|
64
|
+
exports.LEGACY_CONFIG_SCHEMA_VERSIONS = ['1.0.0', '1.1.0'];
|
|
65
|
+
exports.DEFAULT_MAX_SLOTS = 3;
|
|
66
|
+
/** Bounds for `max_slots` when reading `config.json` (named — not magic numbers in persist.ts). */
|
|
67
|
+
exports.MIN_MAX_SLOTS = 1;
|
|
68
|
+
exports.MAX_MAX_SLOTS = 64;
|
|
69
|
+
/** Thrown by every non-declared state-machine edge. */
|
|
70
|
+
class IllegalTransitionError extends Error {
|
|
71
|
+
kind;
|
|
72
|
+
from;
|
|
73
|
+
to;
|
|
74
|
+
constructor(kind, from, to) {
|
|
75
|
+
super(`Illegal ${kind} transition: ${from} → ${to}`);
|
|
76
|
+
this.name = 'IllegalTransitionError';
|
|
77
|
+
this.kind = kind;
|
|
78
|
+
this.from = from;
|
|
79
|
+
this.to = to;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
exports.IllegalTransitionError = IllegalTransitionError;
|
|
83
|
+
/** Thrown when an issue, batch, or slot id does not exist in the state. */
|
|
84
|
+
class SchedNotFoundError extends Error {
|
|
85
|
+
constructor(message) {
|
|
86
|
+
super(message);
|
|
87
|
+
this.name = 'SchedNotFoundError';
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
exports.SchedNotFoundError = SchedNotFoundError;
|
|
91
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AA8CH,qDAAqD;AACxC,QAAA,uBAAuB,GAA6B,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;AAE7F;;;GAGG;AACU,QAAA,wBAAwB,GAA6B,IAAI,GAAG,CAAC;IACxE,SAAS;IACT,kBAAkB;IAClB,MAAM;CACP,CAAC,CAAC;AAyCH,qDAAqD;AACxC,QAAA,uBAAuB,GAA6B,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;AAEhG;;;;GAIG;AACU,QAAA,qBAAqB,GAA6B,IAAI,GAAG,CAAC;IACrE,QAAQ;IACR,UAAU;IACV,UAAU;IACV,MAAM;CACP,CAAC,CAAC;AAuBH,+DAA+D;AAClD,QAAA,kBAAkB,GAA4B,IAAI,GAAG,CAAC;IACjE,UAAU;IACV,SAAS;IACT,YAAY;CACb,CAAC,CAAC;AAqIH,oFAAoF;AACvE,QAAA,WAAW,GAAkD;IACxE,UAAU,EAAE,KAAK;IACjB,GAAG,EAAE,QAAQ;IACb,MAAM,EAAE,IAAI;CACb,CAAC;AAEF,mFAAmF;AACtE,QAAA,UAAU,GAAyB,CAAC,YAAY,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;AAEhF,4EAA4E;AAC/D,QAAA,cAAc,GAAG,CAAC,CAAC;AAEhC,8FAA8F;AACjF,QAAA,wBAAwB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEvD,yDAAyD;AAC5C,QAAA,6BAA6B,GAAG,EAAE,GAAG,IAAI,CAAC;AAEvD,2EAA2E;AAC9D,QAAA,2BAA2B,GAAG,GAAG,GAAG,IAAI,CAAC;AAEzC,QAAA,cAAc,GAAG,OAAgB,CAAC;AAE/C,4FAA4F;AAC/E,QAAA,sBAAsB,GAAsB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAE/D,QAAA,qBAAqB,GAAG,OAAgB,CAAC;AAEtD,wFAAwF;AAC3E,QAAA,6BAA6B,GAAsB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAYtE,QAAA,iBAAiB,GAAG,CAAC,CAAC;AAEnC,mGAAmG;AACtF,QAAA,aAAa,GAAG,CAAC,CAAC;AAClB,QAAA,aAAa,GAAG,EAAE,CAAC;AAEhC,uDAAuD;AACvD,MAAa,sBAAuB,SAAQ,KAAK;IACtC,IAAI,CAA6B;IACjC,IAAI,CAAS;IACb,EAAE,CAAS;IAEpB,YAAY,IAAgC,EAAE,IAAY,EAAE,EAAU;QACpE,KAAK,CAAC,WAAW,IAAI,gBAAgB,IAAI,MAAM,EAAE,EAAE,CAAC,CAAC;QACrD,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;QACrC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IACf,CAAC;CACF;AAZD,wDAYC;AAED,2EAA2E;AAC3E,MAAa,kBAAmB,SAAQ,KAAK;IAC3C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AALD,gDAKC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-dossier/sched",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Deterministic scheduler core for dossier batch cycles \u2014 queue, slots, persistent state machine, dispatch engine with completion verification and stall/escalation ladder. The scheduler never invokes an LLM.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|