@ai-dossier/sched 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dispatch.d.ts +103 -0
- package/dist/dispatch.d.ts.map +1 -0
- package/dist/dispatch.js +262 -0
- package/dist/dispatch.js.map +1 -0
- package/dist/engine.d.ts +71 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +590 -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 +220 -0
- package/dist/enqueue.js.map +1 -0
- package/dist/groundtruth.d.ts +74 -0
- package/dist/groundtruth.d.ts.map +1 -0
- package/dist/groundtruth.js +114 -0
- package/dist/groundtruth.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +86 -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 +334 -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 +66 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +174 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/state.d.ts +39 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +360 -0
- package/dist/state.js.map +1 -0
- package/dist/status.d.ts +32 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +76 -0
- package/dist/status.js.map +1 -0
- package/dist/types.d.ts +214 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +87 -0
- package/dist/types.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent dispatch machinery (#464, AC1 — "a runnable queue unit is dispatched
|
|
3
|
+
* as a spawned agent process … with `--model` per its tier; pid + phase +
|
|
4
|
+
* last-progress timestamp tracked in state.json"; RFC-0001 §C.1 "spawns agent
|
|
5
|
+
* processes via the existing `run` machinery").
|
|
6
|
+
*
|
|
7
|
+
* The command is a template (default `claude -p --output-format json --model
|
|
8
|
+
* {model}` — the same headless invocation `ai-dossier run` builds in
|
|
9
|
+
* cli/src/helpers.ts) with `{model}`/`{issue}` placeholders; the prompt
|
|
10
|
+
* travels on stdin exactly like the run machinery's headless path. Children
|
|
11
|
+
* spawn DETACHED and unref'd: an agent must survive a sched crash (RFC F.10 —
|
|
12
|
+
* restart reconciles by pid, it never owns the agent's lifetime).
|
|
13
|
+
*
|
|
14
|
+
* All process I/O is injectable (`SpawnDeps`) so tests spawn fake agents, and
|
|
15
|
+
* the package itself never invokes an LLM — it spawns a process the operator
|
|
16
|
+
* configured.
|
|
17
|
+
*/
|
|
18
|
+
import { type ModelTier, type SchedConfig } from './types';
|
|
19
|
+
/** Default tier → model mapping (claude aliases; override in config.json). */
|
|
20
|
+
export declare const DEFAULT_TIER_MODELS: Readonly<Record<ModelTier, string>>;
|
|
21
|
+
/** Default headless agent command template (claude, the run machinery's first choice). */
|
|
22
|
+
export declare const DEFAULT_DISPATCH_COMMAND: readonly string[];
|
|
23
|
+
/** opencode equivalent (the run machinery's second agent, #459). */
|
|
24
|
+
export declare const OPENCODE_DISPATCH_COMMAND: readonly string[];
|
|
25
|
+
/** Default prompt sent on the child's stdin. */
|
|
26
|
+
export declare const DEFAULT_PROMPT_TEMPLATE: string;
|
|
27
|
+
/** Fully-resolved dispatch settings the engine runs with. */
|
|
28
|
+
export interface ResolvedDispatch {
|
|
29
|
+
/** Command template with `{model}`/`{issue}` placeholders. */
|
|
30
|
+
command: string[];
|
|
31
|
+
/** Prompt template with `{issue}` placeholder. */
|
|
32
|
+
prompt: string;
|
|
33
|
+
/** Model per tier; null means "no model flag" (the command's `--model {model}` pair drops). */
|
|
34
|
+
tierModels: Record<ModelTier, string | null>;
|
|
35
|
+
stallTimeoutMs: number;
|
|
36
|
+
reconcileIntervalMs: number;
|
|
37
|
+
}
|
|
38
|
+
/** Resolve engine dispatch settings from the (possibly sparse) config. */
|
|
39
|
+
export declare function resolveDispatch(config: SchedConfig): ResolvedDispatch;
|
|
40
|
+
/**
|
|
41
|
+
* Build the concrete agent argv for one dispatch: substitute `{issue}` and
|
|
42
|
+
* `{model}`. When the tier's model is null, the `{model}` item AND its
|
|
43
|
+
* immediately-preceding flag (e.g. `--model`) drop together — a command never
|
|
44
|
+
* carries a flag whose value is missing.
|
|
45
|
+
*/
|
|
46
|
+
export declare function buildAgentCommand(template: readonly string[], tier: ModelTier, issue: number, tierModels: Readonly<Record<ModelTier, string | null>>): string[];
|
|
47
|
+
/** Build the child's stdin prompt for one dispatch. */
|
|
48
|
+
export declare function buildPrompt(template: string, issue: number): string;
|
|
49
|
+
/** One tier stronger on the ladder, or null at the top (RFC-0001 §C.1). */
|
|
50
|
+
export declare function escalateTier(tier: ModelTier): ModelTier | null;
|
|
51
|
+
export interface SpawnDeps {
|
|
52
|
+
/**
|
|
53
|
+
* Spawn a detached agent process and return its pid. `logFile` receives the
|
|
54
|
+
* child's combined stdout/stderr (agents outlive sched, so their output
|
|
55
|
+
* cannot stay in this process's pipes). Throws synchronously when the
|
|
56
|
+
* process cannot be spawned (missing binary, unwritable log dir).
|
|
57
|
+
*/
|
|
58
|
+
spawn(cmd: string[], prompt: string, logFile: string): number;
|
|
59
|
+
/**
|
|
60
|
+
* Signal a pid; returns false when it was already dead (or not ours).
|
|
61
|
+
* `expectedStart` (the persisted `/proc` start-time) enables the pid-reuse
|
|
62
|
+
* guard: a pid whose start-time no longer matches was reused by an
|
|
63
|
+
* unrelated process and is never signalled.
|
|
64
|
+
*/
|
|
65
|
+
kill(pid: number, expectedStart?: number): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Whether a pid is alive (best-effort). `expectedStart` applies the same
|
|
68
|
+
* pid-reuse guard as `kill`.
|
|
69
|
+
*/
|
|
70
|
+
isAlive(pid: number, expectedStart?: number): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* `/proc/<pid>/stat` start-time (field 22) of a pid, when the platform
|
|
73
|
+
* exposes it (Linux); null elsewhere. The engine persists this at spawn so
|
|
74
|
+
* pid identity survives engine restarts (decision 1, option C).
|
|
75
|
+
*/
|
|
76
|
+
processStart(pid: number): number | null;
|
|
77
|
+
}
|
|
78
|
+
/** `issue:464` → `issue-464` (filesystem-safe unit ids for log file names). */
|
|
79
|
+
export declare function unitLogName(unit: string): string;
|
|
80
|
+
/** Poll cadence bounds for `sleep`'s stop-check interval (engine loop). */
|
|
81
|
+
export declare const STOP_POLL_MIN_MS = 100;
|
|
82
|
+
export declare const STOP_POLL_MAX_MS = 1000;
|
|
83
|
+
/**
|
|
84
|
+
* `/proc/<pid>/stat` start-time (field 22, clock ticks since boot) — stable
|
|
85
|
+
* process identity for the lifetime of the pid, so a recycled pid is
|
|
86
|
+
* detectable. Null when /proc is unavailable (macOS/Windows) or the process
|
|
87
|
+
* is gone.
|
|
88
|
+
*/
|
|
89
|
+
export declare function procStartTime(pid: number): number | null;
|
|
90
|
+
/**
|
|
91
|
+
* Real process I/O: detached spawn with output to a log file, unref'd.
|
|
92
|
+
*
|
|
93
|
+
* Pid-reuse guard (decision 1, option C — hybrid): every pid this instance
|
|
94
|
+
* spawns is recorded with its `/proc` start-time; `kill`/`isAlive` accept the
|
|
95
|
+
* start-time persisted in state.json and refuse a pid whose current
|
|
96
|
+
* start-time no longer matches (it was reused by an unrelated process — the
|
|
97
|
+
* agent we spawned is already dead, so skipping the signal loses nothing).
|
|
98
|
+
* On platforms without /proc the guard degrades to best-effort, and pids
|
|
99
|
+
* without a recorded start-time (e.g. from pre-decision state files) stay
|
|
100
|
+
* best-effort everywhere.
|
|
101
|
+
*/
|
|
102
|
+
export declare function createSpawnDeps(cwd?: string): SpawnDeps;
|
|
103
|
+
//# sourceMappingURL=dispatch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,WAAW,EAEjB,MAAM,SAAS,CAAC;AAEjB,8EAA8E;AAC9E,eAAO,MAAM,mBAAmB,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAInE,CAAC;AAEF,0FAA0F;AAC1F,eAAO,MAAM,wBAAwB,EAAE,SAAS,MAAM,EAOrD,CAAC;AAEF,oEAAoE;AACpE,eAAO,MAAM,yBAAyB,EAAE,SAAS,MAAM,EAOtD,CAAC;AAEF,gDAAgD;AAChD,eAAO,MAAM,uBAAuB,QAKwB,CAAC;AAE7D,6DAA6D;AAC7D,MAAM,WAAW,gBAAgB;IAC/B,8DAA8D;IAC9D,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,+FAA+F;IAC/F,UAAU,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,gBAAgB,CAcrE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,IAAI,EAAE,SAAS,EACf,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,GACrD,MAAM,EAAE,CAgBV;AAED,uDAAuD;AACvD,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED,2EAA2E;AAC3E,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,IAAI,CAE9D;AAID,MAAM,WAAW,SAAS;IACxB;;;;;OAKG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9D;;;;;OAKG;IACH,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACnD;;;OAGG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACtD;;;;OAIG;IACH,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CAC1C;AAED,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,2EAA2E;AAC3E,eAAO,MAAM,gBAAgB,MAAM,CAAC;AACpC,eAAO,MAAM,gBAAgB,OAAO,CAAC;AAErC;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAcxD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAyEvD"}
|
package/dist/dispatch.js
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Agent dispatch machinery (#464, AC1 — "a runnable queue unit is dispatched
|
|
4
|
+
* as a spawned agent process … with `--model` per its tier; pid + phase +
|
|
5
|
+
* last-progress timestamp tracked in state.json"; RFC-0001 §C.1 "spawns agent
|
|
6
|
+
* processes via the existing `run` machinery").
|
|
7
|
+
*
|
|
8
|
+
* The command is a template (default `claude -p --output-format json --model
|
|
9
|
+
* {model}` — the same headless invocation `ai-dossier run` builds in
|
|
10
|
+
* cli/src/helpers.ts) with `{model}`/`{issue}` placeholders; the prompt
|
|
11
|
+
* travels on stdin exactly like the run machinery's headless path. Children
|
|
12
|
+
* spawn DETACHED and unref'd: an agent must survive a sched crash (RFC F.10 —
|
|
13
|
+
* restart reconciles by pid, it never owns the agent's lifetime).
|
|
14
|
+
*
|
|
15
|
+
* All process I/O is injectable (`SpawnDeps`) so tests spawn fake agents, and
|
|
16
|
+
* the package itself never invokes an LLM — it spawns a process the operator
|
|
17
|
+
* configured.
|
|
18
|
+
*/
|
|
19
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
22
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
23
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
24
|
+
}
|
|
25
|
+
Object.defineProperty(o, k2, desc);
|
|
26
|
+
}) : (function(o, m, k, k2) {
|
|
27
|
+
if (k2 === undefined) k2 = k;
|
|
28
|
+
o[k2] = m[k];
|
|
29
|
+
}));
|
|
30
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
31
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
32
|
+
}) : function(o, v) {
|
|
33
|
+
o["default"] = v;
|
|
34
|
+
});
|
|
35
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
36
|
+
var ownKeys = function(o) {
|
|
37
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
38
|
+
var ar = [];
|
|
39
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
40
|
+
return ar;
|
|
41
|
+
};
|
|
42
|
+
return ownKeys(o);
|
|
43
|
+
};
|
|
44
|
+
return function (mod) {
|
|
45
|
+
if (mod && mod.__esModule) return mod;
|
|
46
|
+
var result = {};
|
|
47
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
48
|
+
__setModuleDefault(result, mod);
|
|
49
|
+
return result;
|
|
50
|
+
};
|
|
51
|
+
})();
|
|
52
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
53
|
+
exports.STOP_POLL_MAX_MS = exports.STOP_POLL_MIN_MS = exports.DEFAULT_PROMPT_TEMPLATE = exports.OPENCODE_DISPATCH_COMMAND = exports.DEFAULT_DISPATCH_COMMAND = exports.DEFAULT_TIER_MODELS = void 0;
|
|
54
|
+
exports.resolveDispatch = resolveDispatch;
|
|
55
|
+
exports.buildAgentCommand = buildAgentCommand;
|
|
56
|
+
exports.buildPrompt = buildPrompt;
|
|
57
|
+
exports.escalateTier = escalateTier;
|
|
58
|
+
exports.unitLogName = unitLogName;
|
|
59
|
+
exports.procStartTime = procStartTime;
|
|
60
|
+
exports.createSpawnDeps = createSpawnDeps;
|
|
61
|
+
const node_child_process_1 = require("node:child_process");
|
|
62
|
+
const fs = __importStar(require("node:fs"));
|
|
63
|
+
const path = __importStar(require("node:path"));
|
|
64
|
+
const project_1 = require("./project");
|
|
65
|
+
const types_1 = require("./types");
|
|
66
|
+
/** Default tier → model mapping (claude aliases; override in config.json). */
|
|
67
|
+
exports.DEFAULT_TIER_MODELS = {
|
|
68
|
+
mechanical: 'haiku',
|
|
69
|
+
mid: 'sonnet',
|
|
70
|
+
strong: 'opus',
|
|
71
|
+
};
|
|
72
|
+
/** Default headless agent command template (claude, the run machinery's first choice). */
|
|
73
|
+
exports.DEFAULT_DISPATCH_COMMAND = [
|
|
74
|
+
'claude',
|
|
75
|
+
'-p',
|
|
76
|
+
'--output-format',
|
|
77
|
+
'json',
|
|
78
|
+
'--model',
|
|
79
|
+
'{model}',
|
|
80
|
+
];
|
|
81
|
+
/** opencode equivalent (the run machinery's second agent, #459). */
|
|
82
|
+
exports.OPENCODE_DISPATCH_COMMAND = [
|
|
83
|
+
'opencode',
|
|
84
|
+
'run',
|
|
85
|
+
'--format',
|
|
86
|
+
'json',
|
|
87
|
+
'--model',
|
|
88
|
+
'{model}',
|
|
89
|
+
];
|
|
90
|
+
/** Default prompt sent on the child's stdin. */
|
|
91
|
+
exports.DEFAULT_PROMPT_TEMPLATE = 'Run the full-cycle-issue workflow for GitHub issue #{issue} in this repository.\n\n' +
|
|
92
|
+
'Begin by fetching the workflow: ai-dossier run imboard-ai/git/full-cycle-issue --pull\n\n' +
|
|
93
|
+
'Then execute it end-to-end for issue #{issue}, following every phase ' +
|
|
94
|
+
'(gate, setup, plan, implement, review, ship, report) without asking questions, ' +
|
|
95
|
+
'until the final report milestone is posted on the issue.';
|
|
96
|
+
/** Resolve engine dispatch settings from the (possibly sparse) config. */
|
|
97
|
+
function resolveDispatch(config) {
|
|
98
|
+
const dispatch = config.dispatch ?? {};
|
|
99
|
+
const tierModels = {
|
|
100
|
+
mechanical: dispatch.tier_models?.mechanical ?? exports.DEFAULT_TIER_MODELS.mechanical,
|
|
101
|
+
mid: dispatch.tier_models?.mid ?? exports.DEFAULT_TIER_MODELS.mid,
|
|
102
|
+
strong: dispatch.tier_models?.strong ?? exports.DEFAULT_TIER_MODELS.strong,
|
|
103
|
+
};
|
|
104
|
+
return {
|
|
105
|
+
command: dispatch.command ?? [...exports.DEFAULT_DISPATCH_COMMAND],
|
|
106
|
+
prompt: dispatch.prompt ?? exports.DEFAULT_PROMPT_TEMPLATE,
|
|
107
|
+
tierModels,
|
|
108
|
+
stallTimeoutMs: config.stall_timeout_ms ?? types_1.DEFAULT_STALL_TIMEOUT_MS,
|
|
109
|
+
reconcileIntervalMs: config.reconcile_interval_ms ?? types_1.DEFAULT_RECONCILE_INTERVAL_MS,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Build the concrete agent argv for one dispatch: substitute `{issue}` and
|
|
114
|
+
* `{model}`. When the tier's model is null, the `{model}` item AND its
|
|
115
|
+
* immediately-preceding flag (e.g. `--model`) drop together — a command never
|
|
116
|
+
* carries a flag whose value is missing.
|
|
117
|
+
*/
|
|
118
|
+
function buildAgentCommand(template, tier, issue, tierModels) {
|
|
119
|
+
const model = tierModels[tier] ?? null;
|
|
120
|
+
const argv = [];
|
|
121
|
+
for (const item of template) {
|
|
122
|
+
if (item === '{model}') {
|
|
123
|
+
if (model === null) {
|
|
124
|
+
// Drop the preceding flag along with the placeholder.
|
|
125
|
+
if (argv.length > 0 && argv[argv.length - 1].startsWith('--'))
|
|
126
|
+
argv.pop();
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
argv.push(model);
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
argv.push(item.replaceAll('{issue}', String(issue)));
|
|
133
|
+
}
|
|
134
|
+
return argv;
|
|
135
|
+
}
|
|
136
|
+
/** Build the child's stdin prompt for one dispatch. */
|
|
137
|
+
function buildPrompt(template, issue) {
|
|
138
|
+
return template.replaceAll('{issue}', String(issue));
|
|
139
|
+
}
|
|
140
|
+
/** One tier stronger on the ladder, or null at the top (RFC-0001 §C.1). */
|
|
141
|
+
function escalateTier(tier) {
|
|
142
|
+
return types_1.TIER_LADDER[tier];
|
|
143
|
+
}
|
|
144
|
+
/** `issue:464` → `issue-464` (filesystem-safe unit ids for log file names). */
|
|
145
|
+
function unitLogName(unit) {
|
|
146
|
+
return (0, project_1.sanitizeSlug)(unit);
|
|
147
|
+
}
|
|
148
|
+
/** Poll cadence bounds for `sleep`'s stop-check interval (engine loop). */
|
|
149
|
+
exports.STOP_POLL_MIN_MS = 100;
|
|
150
|
+
exports.STOP_POLL_MAX_MS = 1000;
|
|
151
|
+
/**
|
|
152
|
+
* `/proc/<pid>/stat` start-time (field 22, clock ticks since boot) — stable
|
|
153
|
+
* process identity for the lifetime of the pid, so a recycled pid is
|
|
154
|
+
* detectable. Null when /proc is unavailable (macOS/Windows) or the process
|
|
155
|
+
* is gone.
|
|
156
|
+
*/
|
|
157
|
+
function procStartTime(pid) {
|
|
158
|
+
try {
|
|
159
|
+
const stat = fs.readFileSync(`/proc/${pid}/stat`, 'utf-8');
|
|
160
|
+
// comm (field 2) is parenthesized and may contain spaces — everything
|
|
161
|
+
// after the LAST ')' is fields 3..N, space-separated.
|
|
162
|
+
const close = stat.lastIndexOf(')');
|
|
163
|
+
if (close === -1)
|
|
164
|
+
return null;
|
|
165
|
+
const fields = stat.slice(close + 2).split(' ');
|
|
166
|
+
// field 22 (starttime) -> index 19 in the post-comm array (field 3 = index 0)
|
|
167
|
+
const start = Number.parseInt(fields[19] ?? '', 10);
|
|
168
|
+
return Number.isInteger(start) && start >= 0 ? start : null;
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Real process I/O: detached spawn with output to a log file, unref'd.
|
|
176
|
+
*
|
|
177
|
+
* Pid-reuse guard (decision 1, option C — hybrid): every pid this instance
|
|
178
|
+
* spawns is recorded with its `/proc` start-time; `kill`/`isAlive` accept the
|
|
179
|
+
* start-time persisted in state.json and refuse a pid whose current
|
|
180
|
+
* start-time no longer matches (it was reused by an unrelated process — the
|
|
181
|
+
* agent we spawned is already dead, so skipping the signal loses nothing).
|
|
182
|
+
* On platforms without /proc the guard degrades to best-effort, and pids
|
|
183
|
+
* without a recorded start-time (e.g. from pre-decision state files) stay
|
|
184
|
+
* best-effort everywhere.
|
|
185
|
+
*/
|
|
186
|
+
function createSpawnDeps(cwd) {
|
|
187
|
+
/** Pids spawned by THIS instance -> their /proc start-time (Linux). */
|
|
188
|
+
const spawnedStarts = new Map();
|
|
189
|
+
/** True when `pid` plausibly still names the process we recorded. */
|
|
190
|
+
function matchesRecordedStart(pid, expectedStart) {
|
|
191
|
+
const expected = expectedStart ?? spawnedStarts.get(pid);
|
|
192
|
+
if (expected === undefined)
|
|
193
|
+
return true; // no recorded identity — best-effort
|
|
194
|
+
const current = procStartTime(pid);
|
|
195
|
+
if (current === null)
|
|
196
|
+
return true; // /proc unavailable (non-Linux) or a race — best-effort
|
|
197
|
+
return current === expected;
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
spawn(cmd, prompt, logFile) {
|
|
201
|
+
fs.mkdirSync(path.dirname(logFile), { recursive: true, mode: 0o700 });
|
|
202
|
+
const out = fs.openSync(logFile, 'a', 0o600);
|
|
203
|
+
try {
|
|
204
|
+
const child = (0, node_child_process_1.spawn)(cmd[0], cmd.slice(1), {
|
|
205
|
+
...(cwd ? { cwd } : {}),
|
|
206
|
+
detached: true,
|
|
207
|
+
stdio: ['pipe', out, out],
|
|
208
|
+
});
|
|
209
|
+
// Spawn failures surface synchronously via the pid check below; the
|
|
210
|
+
// async 'error' event must never crash the engine (ENOENT), and an
|
|
211
|
+
// agent exiting before reading stdin must never crash it either (EPIPE).
|
|
212
|
+
child.on('error', (err) => {
|
|
213
|
+
process.stderr.write(`⚠ sched: spawn '${cmd[0]}' failed: ${err.message}\n`);
|
|
214
|
+
});
|
|
215
|
+
if (child.stdin !== null) {
|
|
216
|
+
child.stdin.on('error', () => { });
|
|
217
|
+
child.stdin.write(prompt);
|
|
218
|
+
child.stdin.end();
|
|
219
|
+
}
|
|
220
|
+
child.unref();
|
|
221
|
+
if (child.pid === undefined) {
|
|
222
|
+
throw new Error(`failed to spawn '${cmd[0]}' — is it on PATH? (command: ${cmd.join(' ')})`);
|
|
223
|
+
}
|
|
224
|
+
const start = procStartTime(child.pid);
|
|
225
|
+
if (start !== null)
|
|
226
|
+
spawnedStarts.set(child.pid, start);
|
|
227
|
+
return child.pid;
|
|
228
|
+
}
|
|
229
|
+
finally {
|
|
230
|
+
// The child holds its own dups of the fd; the parent's copy must close.
|
|
231
|
+
fs.closeSync(out);
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
kill(pid, expectedStart) {
|
|
235
|
+
if (!matchesRecordedStart(pid, expectedStart)) {
|
|
236
|
+
spawnedStarts.delete(pid);
|
|
237
|
+
return false; // reused pid — the agent we spawned is already gone
|
|
238
|
+
}
|
|
239
|
+
try {
|
|
240
|
+
process.kill(pid, 'SIGTERM');
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
spawnedStarts.delete(pid);
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
isAlive(pid, expectedStart) {
|
|
249
|
+
try {
|
|
250
|
+
process.kill(pid, 0);
|
|
251
|
+
return matchesRecordedStart(pid, expectedStart);
|
|
252
|
+
}
|
|
253
|
+
catch (err) {
|
|
254
|
+
return err.code === 'EPERM';
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
processStart(pid) {
|
|
258
|
+
return procStartTime(pid);
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
//# sourceMappingURL=dispatch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatch.js","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"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"}
|
package/dist/engine.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The scheduler engine (#464): dispatch, completion verification, and the
|
|
3
|
+
* stall/escalation ladder (RFC-0001 §C.1) — the deterministic organs that
|
|
4
|
+
* replace fleet-cycle's LLM supervision.
|
|
5
|
+
*
|
|
6
|
+
* One `tick()` is a full reconcile+refill cycle:
|
|
7
|
+
*
|
|
8
|
+
* 1. **Poll** ground truth for every live unit OUTSIDE the state lock (gh/git
|
|
9
|
+
* subprocesses are slow; the lock must never wait on a network call).
|
|
10
|
+
* 2. **Apply** under `SchedStore.withLock`:
|
|
11
|
+
* - `assigned` slots (crash between assign and spawn) are spawned/re-attached
|
|
12
|
+
* - `running` slots: dead pid → exit rail; ground truth says complete →
|
|
13
|
+
* external advance (kill the leftover agent, complete); new milestone or
|
|
14
|
+
* pushed commit → progress; no progress for `stall_timeout_ms` →
|
|
15
|
+
* redispatch one tier stronger (cap 2, then failed)
|
|
16
|
+
* - `exited`/`verifying` slots: the agent exited — completion is verified
|
|
17
|
+
* against ground truth, never assumed (AC2); unverified exits ride the
|
|
18
|
+
* same recovery ladder as stalls
|
|
19
|
+
* - `recovering` slots: respawned with the escalated tier (resume rails —
|
|
20
|
+
* the agent re-enters via gate and resumes from the milestone trail)
|
|
21
|
+
* - failures block their TRANSITIVE dependents (AC4)
|
|
22
|
+
* 3. **Refill**: `computeAssignments` fills every freed slot in the SAME tick —
|
|
23
|
+
* a runnable unit never waits while a slot is idle (AC5).
|
|
24
|
+
*
|
|
25
|
+
* Everything that touches the world (processes, GitHub, git) is injected;
|
|
26
|
+
* the state machine is pure. Only `issue:<n>` units are dispatched — batch
|
|
27
|
+
* member sequencing is a follow-up (#464 non-goal).
|
|
28
|
+
*/
|
|
29
|
+
import { type SpawnDeps } from './dispatch';
|
|
30
|
+
import { type GroundTruth } from './groundtruth';
|
|
31
|
+
import { type Journal } from './journal';
|
|
32
|
+
import type { SchedStore } from './persist';
|
|
33
|
+
import { type SchedConfig } from './types';
|
|
34
|
+
export interface EngineDeps {
|
|
35
|
+
store: SchedStore;
|
|
36
|
+
journal: Journal;
|
|
37
|
+
groundTruth: GroundTruth;
|
|
38
|
+
spawnDeps: SpawnDeps;
|
|
39
|
+
now: () => Date;
|
|
40
|
+
}
|
|
41
|
+
/** What one tick did — surfaced by `sched start`. */
|
|
42
|
+
export interface TickResult {
|
|
43
|
+
/** Units spawned this tick (first dispatch or redispatch). */
|
|
44
|
+
spawned: string[];
|
|
45
|
+
/** Units completed by external ground truth while their agent was still alive. */
|
|
46
|
+
externalAdvances: string[];
|
|
47
|
+
/** Units verified complete after an observed exit. */
|
|
48
|
+
completed: string[];
|
|
49
|
+
/** Units redispatched one tier stronger (stall or unverified exit). */
|
|
50
|
+
redispatched: string[];
|
|
51
|
+
/** Units failed (escalation cap / strongest tier / spawn error). */
|
|
52
|
+
failed: string[];
|
|
53
|
+
/** Issues blocked transitively by a failure. */
|
|
54
|
+
blocked: number[];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* One full reconcile+refill cycle. Ground truth is polled WITHOUT the lock;
|
|
58
|
+
* every state mutation happens under `store.withLock`; refills are computed in
|
|
59
|
+
* the same pass, so a freed slot is reused within this tick (AC5).
|
|
60
|
+
*/
|
|
61
|
+
export declare function tick(deps: EngineDeps, config: SchedConfig): TickResult;
|
|
62
|
+
/**
|
|
63
|
+
* The long-running loop behind `sched start`: tick, sleep, repeat. Returns
|
|
64
|
+
* when `shouldStop()` says so (the CLI wires SIGINT). A failed tick is
|
|
65
|
+
* journaled (`tick-failed`, with the error name — `LockTimeoutError` and
|
|
66
|
+
* `CorruptStateError` demand different operator actions) and reported on
|
|
67
|
+
* stderr, and the loop continues — one bad tick (e.g. a transient gh
|
|
68
|
+
* failure) never stops the scheduler.
|
|
69
|
+
*/
|
|
70
|
+
export declare function runLoop(deps: EngineDeps, config: SchedConfig, shouldStop: () => boolean, onTick?: (result: TickResult) => void): Promise<void>;
|
|
71
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAGH,OAAO,EAML,KAAK,SAAS,EAIf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,KAAK,WAAW,EAAiD,MAAM,eAAe,CAAC;AAChG,OAAO,EAAe,KAAK,OAAO,EAAa,MAAM,WAAW,CAAC;AACjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAG5C,OAAO,EAGL,KAAK,WAAW,EAKjB,MAAM,SAAS,CAAC;AAEjB,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,UAAU,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,WAAW,CAAC;IACzB,SAAS,EAAE,SAAS,CAAC;IACrB,GAAG,EAAE,MAAM,IAAI,CAAC;CACjB;AAED,qDAAqD;AACrD,MAAM,WAAW,UAAU;IACzB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,kFAAkF;IAClF,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,sDAAsD;IACtD,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,uEAAuE;IACvE,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,oEAAoE;IACpE,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,gDAAgD;IAChD,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAqkBD;;;;GAIG;AACH,wBAAgB,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,GAAG,UAAU,CAWtE;AAED;;;;;;;GAOG;AACH,wBAAsB,OAAO,CAC3B,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,WAAW,EACnB,UAAU,EAAE,MAAM,OAAO,EACzB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,GACpC,OAAO,CAAC,IAAI,CAAC,CAcf"}
|