@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,348 @@
1
+ "use strict";
2
+ /**
3
+ * Crash-safe persistence for the scheduler core.
4
+ *
5
+ * `state.json` is written atomically: contents land in `state.json.tmp` in the
6
+ * SAME directory (rename is atomic only within a filesystem), get fsynced, and
7
+ * are renamed over `state.json`. A process killed between writes therefore
8
+ * leaves either the previous complete state or the new one — never a partial
9
+ * file (AC2). A stray `.tmp` is ignored by `load()` and overwritten by the
10
+ * next write.
11
+ *
12
+ * Cross-process mutations are serialized by a directory mutex (`.sched-lock`),
13
+ * the same acquire/release protocol as `packages/worktree-pool/src/pool-actions.ts`
14
+ * (acquireLock/releaseLock there) — the two copies must be kept in lockstep.
15
+ */
16
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ var desc = Object.getOwnPropertyDescriptor(m, k);
19
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
20
+ desc = { enumerable: true, get: function() { return m[k]; } };
21
+ }
22
+ Object.defineProperty(o, k2, desc);
23
+ }) : (function(o, m, k, k2) {
24
+ if (k2 === undefined) k2 = k;
25
+ o[k2] = m[k];
26
+ }));
27
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
28
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
29
+ }) : function(o, v) {
30
+ o["default"] = v;
31
+ });
32
+ var __importStar = (this && this.__importStar) || (function () {
33
+ var ownKeys = function(o) {
34
+ ownKeys = Object.getOwnPropertyNames || function (o) {
35
+ var ar = [];
36
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
37
+ return ar;
38
+ };
39
+ return ownKeys(o);
40
+ };
41
+ return function (mod) {
42
+ if (mod && mod.__esModule) return mod;
43
+ var result = {};
44
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
45
+ __setModuleDefault(result, mod);
46
+ return result;
47
+ };
48
+ })();
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.SchedStore = exports.CorruptStateError = exports.LockTimeoutError = void 0;
51
+ exports.writeAtomic = writeAtomic;
52
+ const fs = __importStar(require("node:fs"));
53
+ const path = __importStar(require("node:path"));
54
+ const journal_1 = require("./journal");
55
+ const state_1 = require("./state");
56
+ const types_1 = require("./types");
57
+ const LOCK_TIMEOUT_MS = 10_000;
58
+ const LOCK_RETRY_MS = 200;
59
+ const STATE_FILE = 'state.json';
60
+ const CONFIG_FILE = 'config.json';
61
+ const LOCK_DIR = '.sched-lock';
62
+ /** Thrown when the cross-process lock cannot be acquired in time. */
63
+ class LockTimeoutError extends Error {
64
+ lockPath;
65
+ constructor(lockPath, heldByPid) {
66
+ const holder = heldByPid !== null ? `; held by pid ${heldByPid}` : ' (no pid file found)';
67
+ super(`Timed out waiting for scheduler lock (${LOCK_TIMEOUT_MS}ms)${holder}. ` +
68
+ `If that process is not running, remove ${lockPath}`);
69
+ this.name = 'LockTimeoutError';
70
+ this.lockPath = lockPath;
71
+ }
72
+ }
73
+ exports.LockTimeoutError = LockTimeoutError;
74
+ class CorruptStateError extends Error {
75
+ filePath;
76
+ constructor(filePath, cause) {
77
+ const reason = cause instanceof Error ? cause.message : String(cause);
78
+ super(`Scheduler state file is corrupt: ${filePath} (${reason}). ` +
79
+ 'Rename or remove it to reset the queue — GitHub remains the system of record.');
80
+ this.name = 'CorruptStateError';
81
+ this.filePath = filePath;
82
+ }
83
+ }
84
+ exports.CorruptStateError = CorruptStateError;
85
+ /** Write `contents` to `filePath` atomically (tmp + fsync + rename, same directory). */
86
+ function writeAtomic(filePath, contents) {
87
+ const dir = path.dirname(filePath);
88
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
89
+ const tmp = `${filePath}.tmp`;
90
+ const fd = fs.openSync(tmp, 'w', 0o600);
91
+ try {
92
+ fs.writeFileSync(fd, contents, 'utf8');
93
+ fs.fsyncSync(fd);
94
+ }
95
+ finally {
96
+ fs.closeSync(fd);
97
+ }
98
+ fs.renameSync(tmp, filePath);
99
+ // Best-effort directory fsync so the rename itself is durable. Not available
100
+ // on every platform/fs — a failure here never blocks the write.
101
+ try {
102
+ const dfd = fs.openSync(dir, 'r');
103
+ try {
104
+ fs.fsyncSync(dfd);
105
+ }
106
+ finally {
107
+ fs.closeSync(dfd);
108
+ }
109
+ }
110
+ catch {
111
+ // ignore
112
+ }
113
+ }
114
+ // --- Lock (directory mutex, worktree-pool protocol) ---
115
+ function acquireLock(dir) {
116
+ const lockPath = path.join(dir, LOCK_DIR);
117
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
118
+ const start = Date.now();
119
+ let heldByPid = null;
120
+ while (true) {
121
+ try {
122
+ fs.mkdirSync(lockPath);
123
+ fs.writeFileSync(path.join(lockPath, 'pid'), String(process.pid), { mode: 0o600 });
124
+ return;
125
+ }
126
+ catch {
127
+ heldByPid = null;
128
+ try {
129
+ const pidFile = path.join(lockPath, 'pid');
130
+ if (fs.existsSync(pidFile)) {
131
+ const lockPid = Number.parseInt(fs.readFileSync(pidFile, 'utf-8').trim(), 10);
132
+ heldByPid = Number.isNaN(lockPid) ? null : lockPid;
133
+ try {
134
+ process.kill(lockPid, 0);
135
+ }
136
+ catch {
137
+ // Recorded holder is dead. Take the stale lock over by RENAMING it
138
+ // first: with two contenders both seeing the dead pid, only one
139
+ // rename succeeds — the loser's rmSync cannot delete the winner's
140
+ // freshly-acquired lock (the plain rmSync-race the pool protocol
141
+ // inherits from its original mkdir-mutex).
142
+ const stale = `${lockPath}.stale-${process.pid}-${Date.now()}`;
143
+ try {
144
+ fs.renameSync(lockPath, stale);
145
+ }
146
+ catch {
147
+ // another contender already took it over — fall through to retry
148
+ }
149
+ try {
150
+ fs.rmSync(stale, { recursive: true, force: true });
151
+ }
152
+ catch {
153
+ // best effort cleanup
154
+ }
155
+ continue;
156
+ }
157
+ }
158
+ }
159
+ catch {
160
+ // retry
161
+ }
162
+ if (Date.now() - start > LOCK_TIMEOUT_MS) {
163
+ throw new LockTimeoutError(lockPath, heldByPid);
164
+ }
165
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, LOCK_RETRY_MS);
166
+ }
167
+ }
168
+ }
169
+ function releaseLock(dir) {
170
+ fs.rmSync(path.join(dir, LOCK_DIR), { recursive: true, force: true });
171
+ }
172
+ // --- Store ---
173
+ /**
174
+ * Filesystem-backed store for one project's scheduler state
175
+ * (`~/.dossier/sched/<project>/`). The constructor only records the directory;
176
+ * all I/O happens per call so a long-lived instance always sees fresh disk
177
+ * state under the lock.
178
+ */
179
+ class SchedStore {
180
+ dir;
181
+ constructor(dir) {
182
+ this.dir = dir;
183
+ }
184
+ get statePath() {
185
+ return path.join(this.dir, STATE_FILE);
186
+ }
187
+ get configPath() {
188
+ return path.join(this.dir, CONFIG_FILE);
189
+ }
190
+ get journalPath() {
191
+ return path.join(this.dir, journal_1.JOURNAL_FILE);
192
+ }
193
+ /** Directory for per-unit agent output logs (`runs/<unit>.log`). */
194
+ get runsDir() {
195
+ return path.join(this.dir, 'runs');
196
+ }
197
+ load() {
198
+ const statePath = this.statePath;
199
+ if (!fs.existsSync(statePath)) {
200
+ return (0, state_1.createEmptyState)();
201
+ }
202
+ let raw;
203
+ try {
204
+ raw = fs.readFileSync(statePath, 'utf-8');
205
+ }
206
+ catch (err) {
207
+ // An I/O failure (permissions, disk, read-only mount) is NOT corruption —
208
+ // the destructive "rename to reset" advice must not attach to it.
209
+ throw new CorruptStateError(statePath, new Error(`could not READ the file (${err.message}) — this is an I/O failure, not corruption; renaming will not fix it`));
210
+ }
211
+ try {
212
+ return (0, state_1.validateState)(JSON.parse(raw));
213
+ }
214
+ catch (err) {
215
+ throw new CorruptStateError(statePath, err);
216
+ }
217
+ }
218
+ save(state) {
219
+ writeAtomic(this.statePath, `${JSON.stringify(state, null, 2)}\n`);
220
+ }
221
+ /**
222
+ * Load → mutate → save under the cross-process lock. The mutator must be
223
+ * pure (state in, state out) — same contract as pool's `withLock`.
224
+ */
225
+ withLock(fn) {
226
+ acquireLock(this.dir);
227
+ try {
228
+ const current = this.load();
229
+ const { state, result } = fn(current);
230
+ this.save(state);
231
+ return result;
232
+ }
233
+ finally {
234
+ releaseLock(this.dir);
235
+ }
236
+ }
237
+ loadConfig() {
238
+ if (!fs.existsSync(this.configPath)) {
239
+ return { max_slots: types_1.DEFAULT_MAX_SLOTS };
240
+ }
241
+ try {
242
+ const parsed = JSON.parse(fs.readFileSync(this.configPath, 'utf-8'));
243
+ const version = String(parsed.schema_version);
244
+ if (version !== types_1.CONFIG_SCHEMA_VERSION && !types_1.LEGACY_CONFIG_SCHEMA_VERSIONS.includes(version)) {
245
+ throw new Error(`unsupported schema version ${version}`);
246
+ }
247
+ if (!Number.isInteger(parsed.max_slots) ||
248
+ parsed.max_slots < types_1.MIN_MAX_SLOTS ||
249
+ parsed.max_slots > types_1.MAX_MAX_SLOTS) {
250
+ throw new Error(`max_slots must be an integer between ${types_1.MIN_MAX_SLOTS} and ${types_1.MAX_MAX_SLOTS}`);
251
+ }
252
+ const config = { max_slots: parsed.max_slots };
253
+ if (parsed.stall_timeout_ms !== undefined) {
254
+ if (!Number.isInteger(parsed.stall_timeout_ms) || parsed.stall_timeout_ms <= 0) {
255
+ throw new Error('stall_timeout_ms must be a positive integer (milliseconds)');
256
+ }
257
+ config.stall_timeout_ms = parsed.stall_timeout_ms;
258
+ }
259
+ if (parsed.reconcile_interval_ms !== undefined) {
260
+ if (!Number.isInteger(parsed.reconcile_interval_ms) || parsed.reconcile_interval_ms <= 0) {
261
+ throw new Error('reconcile_interval_ms must be a positive integer (milliseconds)');
262
+ }
263
+ config.reconcile_interval_ms = parsed.reconcile_interval_ms;
264
+ }
265
+ if (parsed.pr_poll_interval_ms !== undefined) {
266
+ if (!Number.isInteger(parsed.pr_poll_interval_ms) || parsed.pr_poll_interval_ms <= 0) {
267
+ throw new Error('pr_poll_interval_ms must be a positive integer (milliseconds)');
268
+ }
269
+ config.pr_poll_interval_ms = parsed.pr_poll_interval_ms;
270
+ }
271
+ if (parsed.dispatch !== undefined) {
272
+ config.dispatch = validateDispatchConfig(parsed.dispatch);
273
+ }
274
+ return config;
275
+ }
276
+ catch (err) {
277
+ // Deliberate degrade-to-default (unlike state.json, config is re-derivable
278
+ // operator intent and hard-failing every command on a typo would brick
279
+ // even `sched status`) — but never silently.
280
+ console.error(`⚠ Scheduler config ${this.configPath} is unreadable (${err.message}) — using default max_slots=${types_1.DEFAULT_MAX_SLOTS}`);
281
+ return { max_slots: types_1.DEFAULT_MAX_SLOTS };
282
+ }
283
+ }
284
+ saveConfig(config) {
285
+ const file = {
286
+ schema_version: types_1.CONFIG_SCHEMA_VERSION,
287
+ max_slots: config.max_slots,
288
+ ...(config.stall_timeout_ms !== undefined
289
+ ? { stall_timeout_ms: config.stall_timeout_ms }
290
+ : {}),
291
+ ...(config.reconcile_interval_ms !== undefined
292
+ ? { reconcile_interval_ms: config.reconcile_interval_ms }
293
+ : {}),
294
+ ...(config.pr_poll_interval_ms !== undefined
295
+ ? { pr_poll_interval_ms: config.pr_poll_interval_ms }
296
+ : {}),
297
+ ...(config.dispatch !== undefined ? { dispatch: config.dispatch } : {}),
298
+ };
299
+ writeAtomic(this.configPath, `${JSON.stringify(file, null, 2)}\n`);
300
+ }
301
+ }
302
+ exports.SchedStore = SchedStore;
303
+ const MODEL_TIERS = ['mechanical', 'mid', 'strong'];
304
+ /** Strict validation of the optional `dispatch` section (#464). */
305
+ function validateDispatchConfig(raw) {
306
+ if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
307
+ throw new Error('dispatch must be an object');
308
+ }
309
+ const dispatch = raw;
310
+ if (dispatch.command !== undefined) {
311
+ if (!Array.isArray(dispatch.command) ||
312
+ dispatch.command.length === 0 ||
313
+ dispatch.command.some((c) => typeof c !== 'string' || c.length === 0)) {
314
+ throw new Error('dispatch.command must be a non-empty array of non-empty strings');
315
+ }
316
+ }
317
+ if (dispatch.prompt !== undefined && typeof dispatch.prompt !== 'string') {
318
+ throw new Error('dispatch.prompt must be a string');
319
+ }
320
+ if (dispatch.report_prompt !== undefined && typeof dispatch.report_prompt !== 'string') {
321
+ throw new Error('dispatch.report_prompt must be a string');
322
+ }
323
+ if (dispatch.tier_models !== undefined) {
324
+ if (dispatch.tier_models === null || typeof dispatch.tier_models !== 'object') {
325
+ throw new Error('dispatch.tier_models must be an object');
326
+ }
327
+ for (const [tier, model] of Object.entries(dispatch.tier_models)) {
328
+ if (!MODEL_TIERS.includes(tier)) {
329
+ throw new Error(`dispatch.tier_models: unknown tier '${tier}'`);
330
+ }
331
+ if (typeof model !== 'string' || model.length === 0) {
332
+ throw new Error(`dispatch.tier_models.${tier} must be a non-empty string`);
333
+ }
334
+ }
335
+ }
336
+ const out = {};
337
+ if (dispatch.command !== undefined)
338
+ out.command = dispatch.command;
339
+ if (dispatch.prompt !== undefined)
340
+ out.prompt = dispatch.prompt;
341
+ if (dispatch.report_prompt !== undefined)
342
+ out.report_prompt = dispatch.report_prompt;
343
+ if (dispatch.tier_models !== undefined) {
344
+ out.tier_models = dispatch.tier_models;
345
+ }
346
+ return out;
347
+ }
348
+ //# sourceMappingURL=persist.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persist.js","sourceRoot":"","sources":["../src/persist.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Project identity for the scheduler state directory
3
+ * (`~/.dossier/sched/<project>/`).
4
+ *
5
+ * `<project>` follows fleet-cycle's convention exactly: the repo slug
6
+ * `<owner>-<repo>` from `gh repo view --json owner,name`; when that fails (no
7
+ * remote, no `gh`), the basename of `git rev-parse --show-toplevel`.
8
+ *
9
+ * The exec function is injectable so tests (and any non-CLI consumer) never
10
+ * spawn processes. Since #464 these are no longer the package's only
11
+ * subprocesses (dispatch.ts spawns the configured agent; groundtruth.ts execs
12
+ * runstate/gh/git), but all process I/O in the package is injectable, and
13
+ * none of it is an LLM call the scheduler makes itself (AC7).
14
+ */
15
+ /**
16
+ * Run a command and return trimmed stdout, or null when it fails (non-zero
17
+ * exit, missing binary). Never throws — callers degrade to their fallback.
18
+ */
19
+ export type ExecFn = (file: string, args: string[], cwd?: string) => string | null;
20
+ /** Build an `ExecFn` via `execFileSync` (never throws), optionally with a hard timeout and a failure observer. */
21
+ export declare function createExecFn(timeoutMs?: number, opts?: {
22
+ onError?: (file: string, args: string[], err: Error) => void;
23
+ }): ExecFn;
24
+ /** Default exec: no timeout. */
25
+ export declare const defaultExec: ExecFn;
26
+ /**
27
+ * Filesystem-safe form of an arbitrary identifier: everything outside
28
+ * `[A-Za-z0-9._-]` becomes `-`. `.` and `..` survive the character allowlist
29
+ * but are path components that would escape `~/.dossier/sched/` (e.g.
30
+ * `--project ..` → `~/.dossier` itself, overwriting the global CLI config via
31
+ * saveConfig) — those collapse to `default`.
32
+ */
33
+ export declare function sanitizeSlug(slug: string): string;
34
+ /**
35
+ * Resolve the project slug for `cwd` (default: process cwd). `gh` first, git
36
+ * basename as fallback; a final sanitized non-empty string otherwise.
37
+ */
38
+ export declare function resolveProjectSlug(exec?: ExecFn, cwd?: string): string;
39
+ /** State directory for one project under the dossier home (testable via `home`). */
40
+ export declare function schedStateDir(project: string, home?: string): string;
41
+ //# sourceMappingURL=project.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH;;;GAGG;AACH,MAAM,MAAM,MAAM,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;AAEnF,kHAAkH;AAClH,wBAAgB,YAAY,CAC1B,SAAS,CAAC,EAAE,MAAM,EAClB,IAAI,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,KAAK,KAAK,IAAI,CAAA;CAAE,GACtE,MAAM,CAgBR;AAED,gCAAgC;AAChC,eAAO,MAAM,WAAW,EAAE,MAAuB,CAAC;AAKlD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIjD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,GAAE,MAAoB,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAoBnF;AAED,oFAAoF;AACpF,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,MAAqB,GAAG,MAAM,CAElF"}
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ /**
3
+ * Project identity for the scheduler state directory
4
+ * (`~/.dossier/sched/<project>/`).
5
+ *
6
+ * `<project>` follows fleet-cycle's convention exactly: the repo slug
7
+ * `<owner>-<repo>` from `gh repo view --json owner,name`; when that fails (no
8
+ * remote, no `gh`), the basename of `git rev-parse --show-toplevel`.
9
+ *
10
+ * The exec function is injectable so tests (and any non-CLI consumer) never
11
+ * spawn processes. Since #464 these are no longer the package's only
12
+ * subprocesses (dispatch.ts spawns the configured agent; groundtruth.ts execs
13
+ * runstate/gh/git), but all process I/O in the package is injectable, and
14
+ * none of it is an LLM call the scheduler makes itself (AC7).
15
+ */
16
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ var desc = Object.getOwnPropertyDescriptor(m, k);
19
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
20
+ desc = { enumerable: true, get: function() { return m[k]; } };
21
+ }
22
+ Object.defineProperty(o, k2, desc);
23
+ }) : (function(o, m, k, k2) {
24
+ if (k2 === undefined) k2 = k;
25
+ o[k2] = m[k];
26
+ }));
27
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
28
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
29
+ }) : function(o, v) {
30
+ o["default"] = v;
31
+ });
32
+ var __importStar = (this && this.__importStar) || (function () {
33
+ var ownKeys = function(o) {
34
+ ownKeys = Object.getOwnPropertyNames || function (o) {
35
+ var ar = [];
36
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
37
+ return ar;
38
+ };
39
+ return ownKeys(o);
40
+ };
41
+ return function (mod) {
42
+ if (mod && mod.__esModule) return mod;
43
+ var result = {};
44
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
45
+ __setModuleDefault(result, mod);
46
+ return result;
47
+ };
48
+ })();
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.defaultExec = void 0;
51
+ exports.createExecFn = createExecFn;
52
+ exports.sanitizeSlug = sanitizeSlug;
53
+ exports.resolveProjectSlug = resolveProjectSlug;
54
+ exports.schedStateDir = schedStateDir;
55
+ const node_child_process_1 = require("node:child_process");
56
+ const os = __importStar(require("node:os"));
57
+ const path = __importStar(require("node:path"));
58
+ /** Build an `ExecFn` via `execFileSync` (never throws), optionally with a hard timeout and a failure observer. */
59
+ function createExecFn(timeoutMs, opts) {
60
+ return (file, args, cwd) => {
61
+ try {
62
+ return String((0, node_child_process_1.execFileSync)(file, args, {
63
+ encoding: 'utf-8',
64
+ stdio: ['ignore', 'pipe', 'pipe'],
65
+ ...(timeoutMs !== undefined ? { timeout: timeoutMs } : {}),
66
+ ...(cwd ? { cwd } : {}),
67
+ })).trim();
68
+ }
69
+ catch (err) {
70
+ opts?.onError?.(file, args, err);
71
+ return null;
72
+ }
73
+ };
74
+ }
75
+ /** Default exec: no timeout. */
76
+ exports.defaultExec = createExecFn();
77
+ /** Characters permitted in a project directory name. */
78
+ const SLUG_SAFE = /[^A-Za-z0-9._-]/g;
79
+ /**
80
+ * Filesystem-safe form of an arbitrary identifier: everything outside
81
+ * `[A-Za-z0-9._-]` becomes `-`. `.` and `..` survive the character allowlist
82
+ * but are path components that would escape `~/.dossier/sched/` (e.g.
83
+ * `--project ..` → `~/.dossier` itself, overwriting the global CLI config via
84
+ * saveConfig) — those collapse to `default`.
85
+ */
86
+ function sanitizeSlug(slug) {
87
+ const out = slug.replace(SLUG_SAFE, '-');
88
+ if (out === '.' || out === '..')
89
+ return 'default';
90
+ return out;
91
+ }
92
+ /**
93
+ * Resolve the project slug for `cwd` (default: process cwd). `gh` first, git
94
+ * basename as fallback; a final sanitized non-empty string otherwise.
95
+ */
96
+ function resolveProjectSlug(exec = exports.defaultExec, cwd) {
97
+ const ghOut = exec('gh', ['repo', 'view', '--json', 'owner,name'], cwd);
98
+ if (ghOut !== null) {
99
+ try {
100
+ const parsed = JSON.parse(ghOut);
101
+ const owner = parsed.owner?.login;
102
+ if (typeof owner === 'string' && typeof parsed.name === 'string') {
103
+ const slug = sanitizeSlug(`${owner}-${parsed.name}`);
104
+ if (slug.length > 0)
105
+ return slug;
106
+ }
107
+ }
108
+ catch {
109
+ // fall through to git
110
+ }
111
+ }
112
+ const root = exec('git', ['rev-parse', '--show-toplevel'], cwd);
113
+ if (root !== null && root.length > 0) {
114
+ const slug = sanitizeSlug(path.basename(root));
115
+ if (slug.length > 0)
116
+ return slug;
117
+ }
118
+ return 'default';
119
+ }
120
+ /** State directory for one project under the dossier home (testable via `home`). */
121
+ function schedStateDir(project, home = os.homedir()) {
122
+ return path.join(home, '.dossier', 'sched', sanitizeSlug(project));
123
+ }
124
+ //# sourceMappingURL=project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAaH,oCAmBC;AAeD,oCAIC;AAMD,gDAoBC;AAGD,sCAEC;AAhFD,2DAAkD;AAClD,4CAA8B;AAC9B,gDAAkC;AAQlC,kHAAkH;AAClH,SAAgB,YAAY,CAC1B,SAAkB,EAClB,IAAuE;IAEvE,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;QACzB,IAAI,CAAC;YACH,OAAO,MAAM,CACX,IAAA,iCAAY,EAAC,IAAI,EAAE,IAAI,EAAE;gBACvB,QAAQ,EAAE,OAAO;gBACjB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;gBACjC,GAAG,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC1D,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACxB,CAAC,CACH,CAAC,IAAI,EAAE,CAAC;QACX,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,GAAY,CAAC,CAAC;YAC1C,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED,gCAAgC;AACnB,QAAA,WAAW,GAAW,YAAY,EAAE,CAAC;AAElD,wDAAwD;AACxD,MAAM,SAAS,GAAG,kBAAkB,CAAC;AAErC;;;;;;GAMG;AACH,SAAgB,YAAY,CAAC,IAAY;IACvC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IACzC,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IAClD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,SAAgB,kBAAkB,CAAC,OAAe,mBAAW,EAAE,GAAY;IACzE,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,CAAC,EAAE,GAAG,CAAC,CAAC;IACxE,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAoD,CAAC;YACpF,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC;YAClC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACjE,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,KAAK,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;gBACrD,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;oBAAE,OAAO,IAAI,CAAC;YACnC,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,sBAAsB;QACxB,CAAC;IACH,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,iBAAiB,CAAC,EAAE,GAAG,CAAC,CAAC;IAChE,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;QAC/C,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;IACnC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,oFAAoF;AACpF,SAAgB,aAAa,CAAC,OAAe,EAAE,OAAe,EAAE,CAAC,OAAO,EAAE;IACxE,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;AACrE,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Readiness: which units can run right now, as a pure function of state
3
+ * (RFC-0001 §E.4: "scheduler gates on merge" — an issue with an unmerged
4
+ * dependency is never runnable, and a batch is never dispatched while a batch
5
+ * it depends on is unmerged).
6
+ */
7
+ import type { BatchEntry, IssueStatus, QueueEntry, SchedState } from './types';
8
+ /** A unit the scheduler can place on a slot: one full-mode issue, or one batch. */
9
+ export type RunnableUnit = {
10
+ kind: 'issue';
11
+ issue: number;
12
+ } | {
13
+ kind: 'batch';
14
+ batch: string;
15
+ };
16
+ /** Why a dependency edge currently blocks its holder. */
17
+ export interface DependencyBlocker {
18
+ /** The blocked issue. */
19
+ issue: number;
20
+ /** The unsatisfied dependency. */
21
+ dep: number;
22
+ reason: 'unmerged' | 'not-in-queue';
23
+ /** Current status of the dependency entry, when it exists. */
24
+ depStatus?: IssueStatus;
25
+ }
26
+ /** Issue statuses from which a full-cycle dispatch may start. */
27
+ export declare const DISPATCHABLE_ISSUE_STATUSES: ReadonlySet<IssueStatus>;
28
+ /**
29
+ * Unsatisfied dependency edges of a single entry. Deps satisfied by
30
+ * membership in the SAME batch are not blockers — intra-batch ordering is the
31
+ * batch's own concern once dispatched (RFC-0001 §E.4).
32
+ */
33
+ export declare function dependencyBlockers(state: SchedState, entry: QueueEntry): DependencyBlocker[];
34
+ /** Whether `batch` may dispatch: status `ready` and every cross-batch/cross-issue edge merged. */
35
+ export declare function batchBlockers(state: SchedState, batch: BatchEntry): DependencyBlocker[];
36
+ /**
37
+ * All runnable units in stable dispatch order: issues in queue order, then
38
+ * batches in creation order. A batch unit unlocks ALL its members' execution,
39
+ * so batches are listed after issues only for stability — the caller slices to
40
+ * free capacity either way.
41
+ */
42
+ export declare function runnableUnits(state: SchedState): RunnableUnit[];
43
+ //# sourceMappingURL=readiness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readiness.d.ts","sourceRoot":"","sources":["../src/readiness.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAG/E,mFAAmF;AACnF,MAAM,MAAM,YAAY,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/F,yDAAyD;AACzD,MAAM,WAAW,iBAAiB;IAChC,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,UAAU,GAAG,cAAc,CAAC;IACpC,8DAA8D;IAC9D,SAAS,CAAC,EAAE,WAAW,CAAC;CACzB;AAED,iEAAiE;AACjE,eAAO,MAAM,2BAA2B,EAAE,WAAW,CAAC,WAAW,CAM/D,CAAC;AAMH;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,iBAAiB,EAAE,CAe5F;AAED,kGAAkG;AAClG,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,iBAAiB,EAAE,CAuBvF;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,YAAY,EAAE,CAc/D"}