feat: export continuity hard-gate and watchdog workstream

This commit is contained in:
2026-04-24 12:36:31 +08:00
commit 111cf27634
24 changed files with 3648 additions and 0 deletions

View File

@@ -0,0 +1,109 @@
#!/usr/bin/env node
import fs from 'node:fs';
const LEGAL_TERMINAL_STATES = new Set(['waiting_user', 'blocked', 'pending_verification']);
function parseArgs(argv) {
let inputPath = null;
let compact = false;
for (let i = 0; i < argv.length; i += 1) {
const arg = argv[i];
if (arg === '--input') {
inputPath = argv[i + 1] ?? null;
i += 1;
continue;
}
if (arg.startsWith('--input=')) {
inputPath = arg.slice('--input='.length);
continue;
}
if (arg === '--compact') {
compact = true;
continue;
}
}
return { inputPath, compact };
}
function readInput(inputPath) {
if (!inputPath) {
return {
ok: false,
error: 'missing_required_input',
};
}
try {
const raw = fs.readFileSync(inputPath, 'utf8');
const parsed = JSON.parse(raw);
return {
ok: true,
bytes: Buffer.byteLength(raw, 'utf8'),
preview: raw.slice(0, 0),
parsed,
};
} catch (error) {
return {
ok: false,
error: error instanceof Error ? error.message : String(error),
};
}
}
function evaluateContinuity(payload) {
const taskComplete = payload?.taskState === 'complete';
const nextAction = payload?.nextDerivedAction ?? payload?.derivedAction ?? null;
const nextActionKnown = nextAction != null;
const hasDispatchReceipt = payload?.dispatchReceipt != null;
const closureState = payload?.replyClosureState ?? null;
const isLegalTerminalState = LEGAL_TERMINAL_STATES.has(closureState);
if (taskComplete && nextActionKnown && !hasDispatchReceipt && !isLegalTerminalState) {
return {
ok: false,
status: 'continuity_failure',
verdict: 'continuity_failure',
reason: 'missing_dispatch_receipt',
};
}
return {
ok: true,
status: 'pass',
verdict: 'pass',
};
}
const { inputPath, compact } = parseArgs(process.argv.slice(2));
const input = readInput(inputPath);
const evaluation = input.ok ? evaluateContinuity(input.parsed) : {
ok: false,
status: 'input_error',
verdict: 'input_error',
};
const response = {
...evaluation,
gate: 'approved_plan_continuity',
compact,
inputPath,
input: {
ok: input.ok,
...(input.ok
? {
bytes: input.bytes,
preview: input.preview,
}
: {
error: input.error,
}),
},
};
process.stdout.write(`${JSON.stringify(response)}
`);

View File

@@ -0,0 +1,194 @@
#!/usr/bin/env node
import fs from 'node:fs';
import path from 'node:path';
const DEFAULT_RECEIPT_DIR = path.resolve(process.cwd(), 'state/approved-plan-continuity');
function parseArgs(argv) {
let inputPath = null;
let compact = false;
let receiptDir = DEFAULT_RECEIPT_DIR;
for (let i = 0; i < argv.length; i += 1) {
const arg = argv[i];
if (arg === '--input') {
inputPath = argv[i + 1] ?? null;
i += 1;
continue;
}
if (arg.startsWith('--input=')) {
inputPath = arg.slice('--input='.length);
continue;
}
if (arg === '--receipt-dir') {
receiptDir = argv[i + 1] ? path.resolve(argv[i + 1]) : receiptDir;
i += 1;
continue;
}
if (arg.startsWith('--receipt-dir=')) {
receiptDir = path.resolve(arg.slice('--receipt-dir='.length));
continue;
}
if (arg === '--compact') {
compact = true;
continue;
}
}
return { inputPath, compact, receiptDir };
}
function readInput(inputPath) {
if (!inputPath) {
return {
ok: false,
error: 'missing_required_input',
};
}
try {
const raw = fs.readFileSync(inputPath, 'utf8');
const parsed = JSON.parse(raw);
return {
ok: true,
bytes: Buffer.byteLength(raw, 'utf8'),
parsed,
};
} catch (error) {
return {
ok: false,
error: error instanceof Error ? error.message : String(error),
};
}
}
function slugifySegment(value) {
return String(value)
.trim()
.toLowerCase()
.replace(/[^a-z0-9._-]+/g, '-')
.replace(/^-+|-+$/g, '')
.replace(/-{2,}/g, '-');
}
function buildReceipt(payload) {
const nextAction = payload?.nextDerivedAction ?? payload?.derivedAction ?? null;
const receipt = {
planId: payload?.planId ?? null,
currentTask: payload?.currentTask ?? null,
nextDerivedAction: nextAction,
dispatchedAt: payload?.dispatchedAt ?? null,
dispatchRunId: payload?.dispatchRunId ?? null,
childSessionKey: payload?.childSessionKey ?? null,
replyClosureState: payload?.replyClosureState ?? null,
};
return receipt;
}
function validateReceipt(receipt) {
const missing = [];
for (const field of [
'planId',
'currentTask',
'nextDerivedAction',
'dispatchedAt',
'dispatchRunId',
'childSessionKey',
'replyClosureState',
]) {
if (receipt[field] == null) {
missing.push(field);
}
}
const planIdSafe = slugifySegment(receipt.planId ?? '');
const dispatchRunIdSafe = slugifySegment(receipt.dispatchRunId ?? '');
if (!planIdSafe) missing.push('planId_filesystem_safe');
if (!dispatchRunIdSafe) missing.push('dispatchRunId_filesystem_safe');
return {
ok: missing.length === 0,
missing,
planIdSafe,
dispatchRunIdSafe,
};
}
function writeReceipt({ receipt, receiptDir, planIdSafe, dispatchRunIdSafe }) {
fs.mkdirSync(receiptDir, { recursive: true });
const receiptPath = path.join(receiptDir, `receipt-${planIdSafe}-${dispatchRunIdSafe}.json`);
fs.writeFileSync(receiptPath, `${JSON.stringify(receipt, null, 2)}\n`, 'utf8');
return receiptPath;
}
const { inputPath, compact, receiptDir } = parseArgs(process.argv.slice(2));
const input = readInput(inputPath);
let response;
if (!input.ok) {
response = {
ok: false,
status: 'input_error',
binding: 'approved_plan_dispatch',
compact,
inputPath,
receipt: null,
receiptPath: null,
input: {
ok: false,
error: input.error,
},
};
} else {
const receipt = buildReceipt(input.parsed);
const validation = validateReceipt(receipt);
if (!validation.ok) {
response = {
ok: false,
status: 'missing_required_receipt_fields',
binding: 'approved_plan_dispatch',
compact,
inputPath,
receipt,
receiptPath: null,
missing: validation.missing,
input: {
ok: true,
bytes: input.bytes,
},
};
} else {
const receiptPath = writeReceipt({
receipt,
receiptDir,
planIdSafe: validation.planIdSafe,
dispatchRunIdSafe: validation.dispatchRunIdSafe,
});
response = {
ok: true,
status: 'receipt_written',
binding: 'approved_plan_dispatch',
compact,
inputPath,
receipt,
receiptPath,
input: {
ok: true,
bytes: input.bytes,
},
};
}
}
process.stdout.write(`${JSON.stringify(response)}\n`);

View File

@@ -0,0 +1,285 @@
#!/usr/bin/env node
import fs from 'node:fs';
import path from 'node:path';
import process from 'node:process';
const ROOT_DIR = path.resolve(import.meta.dirname, '..');
const STATE_DIR = path.join(ROOT_DIR, 'state', 'subagent-delivery-watchdog');
function parseArgs(argv) {
const args = {
compact: false,
input: null,
help: false,
};
for (let i = 0; i < argv.length; i += 1) {
const token = argv[i];
if (token === '--compact') {
args.compact = true;
continue;
}
if (token === '--help' || token === '-h') {
args.help = true;
continue;
}
if (token === '--input') {
args.input = argv[i + 1] ?? null;
i += 1;
continue;
}
if (token.startsWith('--input=')) {
args.input = token.slice('--input='.length) || null;
continue;
}
}
return args;
}
function printHelp() {
const lines = [
'Usage: node scripts/subagent_delivery_watchdog.mjs [--compact] [--input <path>]',
'',
'Minimal CLI skeleton for the subagent delivery watchdog.',
];
process.stdout.write(`${lines.join('\n')}\n`);
}
function tryReadInput(inputPath) {
if (!inputPath) {
return {
path: null,
exists: false,
bytes: 0,
preview: '',
};
}
try {
const content = fs.readFileSync(inputPath, 'utf8');
return {
path: inputPath,
exists: true,
bytes: Buffer.byteLength(content, 'utf8'),
preview: content.slice(0, 200),
content,
};
} catch (error) {
return {
path: inputPath,
exists: false,
bytes: 0,
preview: '',
error: error instanceof Error ? error.message : String(error),
};
}
}
function tryParseJson(content) {
if (typeof content !== 'string' || content.length === 0) {
return null;
}
try {
return JSON.parse(content);
} catch {
return null;
}
}
function writeDispatchReceiptState(payload) {
if (!payload || typeof payload !== 'object') {
return null;
}
const { runId, childSessionKey, dispatchAt, expectedBy } = payload;
if (![runId, childSessionKey, dispatchAt, expectedBy].every((value) => typeof value === 'string' && value.length > 0)) {
return null;
}
fs.mkdirSync(STATE_DIR, { recursive: true });
const statePath = path.join(STATE_DIR, `${runId}.json`);
const dispatchRecord = {
runId,
childSessionKey,
dispatchAt,
expectedBy,
};
fs.writeFileSync(statePath, `${JSON.stringify(dispatchRecord, null, 2)}\n`, 'utf8');
return {
statePath,
record: dispatchRecord,
};
}
function writeCompletionReceiptState(payload) {
if (!payload || typeof payload !== 'object') {
return null;
}
const { runId } = payload;
const completionReceivedAt = payload.completionReceivedAt ?? payload.completionReceiptAt ?? null;
const forwardedToMain = payload.forwardedToMain;
const resultSource = payload.resultSource;
if (typeof runId !== 'string' || runId.length === 0) {
return null;
}
const completionUpdates = {};
if (typeof completionReceivedAt === 'string' && completionReceivedAt.length > 0) {
completionUpdates.completionReceivedAt = completionReceivedAt;
}
if (typeof forwardedToMain === 'boolean') {
completionUpdates.forwardedToMain = forwardedToMain;
}
if (typeof resultSource === 'string' && resultSource.length > 0) {
completionUpdates.resultSource = resultSource;
}
if (Object.keys(completionUpdates).length === 0) {
return null;
}
fs.mkdirSync(STATE_DIR, { recursive: true });
const statePath = path.join(STATE_DIR, `${runId}.json`);
let currentRecord = {};
if (fs.existsSync(statePath)) {
try {
currentRecord = JSON.parse(fs.readFileSync(statePath, 'utf8'));
} catch {
currentRecord = {};
}
}
const nextRecord = {
...currentRecord,
runId,
...completionUpdates,
};
fs.writeFileSync(statePath, `${JSON.stringify(nextRecord, null, 2)}\n`, 'utf8');
return {
statePath,
record: nextRecord,
updatedFields: Object.keys(completionUpdates),
};
}
function parseTime(value) {
if (typeof value !== 'string' || value.length === 0) {
return null;
}
const timestamp = Date.parse(value);
return Number.isNaN(timestamp) ? null : timestamp;
}
function recomputeStatus(payload) {
if (!payload || typeof payload !== 'object') {
return 'not_implemented';
}
const completionReceivedAt = payload.completionReceivedAt ?? payload.completionReceiptAt ?? null;
if (parseTime(completionReceivedAt) !== null) {
return 'completed';
}
const hasDispatch = [payload.runId, payload.childSessionKey, payload.dispatchAt, payload.expectedBy].every(
(value) => typeof value === 'string' && value.length > 0,
);
if (!hasDispatch) {
return 'not_implemented';
}
const childRunStatus = typeof payload.childRunStatus === 'string'
? payload.childRunStatus.trim().toLowerCase()
: null;
if (childRunStatus === 'done') {
return 'done_but_not_forwarded';
}
const expectedBy = parseTime(payload.expectedBy);
const currentTime = parseTime(payload.currentTime);
if (expectedBy === null || currentTime === null) {
return 'not_implemented';
}
if (currentTime > expectedBy) {
return 'suspect_delivery_failure';
}
return 'active';
}
function main() {
const args = parseArgs(process.argv.slice(2));
if (args.help) {
printHelp();
process.exit(0);
}
const input = tryReadInput(args.input);
const inputPayload = input.exists ? tryParseJson(input.content) : null;
const dispatchWrite = writeDispatchReceiptState(inputPayload);
const completionWrite = writeCompletionReceiptState(inputPayload);
const status = recomputeStatus(inputPayload);
if ('content' in input) {
delete input.content;
}
const records = [];
if (dispatchWrite) {
records.push(dispatchWrite.record);
}
if (completionWrite) {
records.push(completionWrite.record);
}
const response = {
ok: true,
tool: 'subagent_delivery_watchdog',
version: 'skeleton-v4',
mode: 'receipt-write',
args: {
compact: args.compact,
input: args.input,
},
input,
result: {
status,
message: status === 'not_implemented'
? 'Dispatch and completion receipt writes are implemented; status recompute only handles basic active/suspect/completed states.'
: 'Basic watchdog status recompute completed.',
records,
dispatchReceiptWrite: dispatchWrite,
completionReceiptWrite: completionWrite,
},
};
const spacing = args.compact ? 0 : 2;
process.stdout.write(`${JSON.stringify(response, null, spacing)}\n`);
}
main();

View File

@@ -0,0 +1,421 @@
#!/usr/bin/env node
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { spawnSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const gateScript = path.join(__dirname, 'approved_plan_continuity_gate.mjs');
function createFixture(files = {}) {
const root = mkdtempSync(path.join(os.tmpdir(), 'approved-plan-continuity-'));
for (const [relativePath, content] of Object.entries(files)) {
const filePath = path.join(root, relativePath);
mkdirSync(path.dirname(filePath), { recursive: true });
writeFileSync(filePath, typeof content === 'string' ? content : `${JSON.stringify(content, null, 2)}\n`);
}
return {
root,
path(...segments) {
return path.join(root, ...segments);
},
cleanup() {
rmSync(root, { recursive: true, force: true });
},
};
}
function runGate({ args = [], stdin = null } = {}) {
const result = spawnSync(process.execPath, [gateScript, ...args], {
input: stdin,
encoding: 'utf8',
});
let json = null;
if (result.stdout && result.stdout.trim()) {
try {
json = JSON.parse(result.stdout);
} catch {
json = null;
}
}
return {
status: result.status,
stdout: result.stdout,
stderr: result.stderr,
json,
};
}
const tests = [
{
name: 'skeleton: gate script responds with placeholder envelope when given fixture input',
run() {
const fixture = createFixture({
'input.json': {
planId: 'plan-skeleton',
currentTask: 'task-5',
},
});
try {
const result = runGate({
args: ['--compact', '--input', fixture.path('input.json')],
});
if (result.status !== 0 && result.status !== null) {
throw new Error(`expected controlled execution, got status=${result.status}\n${result.stderr || result.stdout}`);
}
if (!result.json || typeof result.json !== 'object') {
throw new Error(`expected JSON output\nstdout=${result.stdout}`);
}
if (result.json.gate !== 'approved_plan_continuity') {
throw new Error(`expected gate=approved_plan_continuity, got ${JSON.stringify(result.json.gate)}`);
}
} finally {
fixture.cleanup();
}
},
},
{
name: 'continuity: fails when task is complete, next action is known, no dispatch receipt exists, and closure is not in an allowed terminal state',
run() {
const fixture = createFixture({
'input.json': {
planId: 'plan-missing-dispatch',
currentTask: 'task-6',
taskState: 'complete',
nextDerivedAction: {
type: 'message_subagent',
task: 'continue with task-7',
},
replyClosureState: 'completed',
dispatchReceipt: null,
},
});
try {
const result = runGate({
args: ['--compact', '--input', fixture.path('input.json')],
});
if (result.status !== 0 && result.status !== null) {
throw new Error(`expected controlled execution, got status=${result.status}\n${result.stderr || result.stdout}`);
}
if (!result.json || typeof result.json !== 'object') {
throw new Error(`expected JSON output\nstdout=${result.stdout}`);
}
if (result.json.ok !== false) {
throw new Error(`expected continuity failure ok=false, got ${JSON.stringify(result.json)}`);
}
if (result.json.verdict !== 'continuity_failure') {
throw new Error(`expected verdict=continuity_failure, got ${JSON.stringify(result.json.verdict)}`);
}
} finally {
fixture.cleanup();
}
},
},
{
name: 'continuity: fails when planner returns derivedAction without any bound dispatch receipt',
run() {
const fixture = createFixture({
'input.json': {
planId: 'plan-derived-action-without-bound-dispatch',
currentTask: 'task-6b',
taskState: 'complete',
derivedAction: {
type: 'message_subagent',
task: 'continue with task-7b',
},
replyClosureState: 'completed',
dispatchReceipt: null,
},
});
try {
const result = runGate({
args: ['--compact', '--input', fixture.path('input.json')],
});
if (result.status !== 0 && result.status !== null) {
throw new Error(`expected controlled execution, got status=${result.status}
${result.stderr || result.stdout}`);
}
if (!result.json || typeof result.json !== 'object') {
throw new Error(`expected JSON output
stdout=${result.stdout}`);
}
if (result.json.ok !== false) {
throw new Error(`expected continuity failure ok=false for derivedAction without dispatch receipt, got ${JSON.stringify(result.json)}`);
}
if (result.json.verdict !== 'continuity_failure') {
throw new Error(`expected verdict=continuity_failure for derivedAction without dispatch receipt, got ${JSON.stringify(result.json.verdict)}`);
}
} finally {
fixture.cleanup();
}
},
},
{
name: 'continuity: passes when task is complete, next action is known, and a dispatch receipt already exists',
run() {
const fixture = createFixture({
'input.json': {
planId: 'plan-existing-dispatch',
currentTask: 'task-6',
taskState: 'complete',
nextDerivedAction: {
type: 'message_subagent',
task: 'continue with task-7',
},
replyClosureState: 'completed',
dispatchReceipt: {
planId: 'plan-existing-dispatch',
currentTask: 'task-6',
nextDerivedAction: {
type: 'message_subagent',
task: 'continue with task-7',
},
dispatchedAt: '2026-04-24T11:55:00+08:00',
},
},
});
try {
const result = runGate({
args: ['--compact', '--input', fixture.path('input.json')],
});
if (result.status !== 0 && result.status !== null) {
throw new Error(`expected controlled execution, got status=${result.status}
${result.stderr || result.stdout}`);
}
if (!result.json || typeof result.json !== 'object') {
throw new Error(`expected JSON output
stdout=${result.stdout}`);
}
if (result.json.ok !== true) {
throw new Error(`expected continuity pass ok=true when dispatch receipt exists, got ${JSON.stringify(result.json)}`);
}
} finally {
fixture.cleanup();
}
},
},
{
name: 'continuity: passes when planner returns derivedAction and a bound dispatch receipt already exists',
run() {
const fixture = createFixture({
'input.json': {
planId: 'plan-derived-action-with-bound-dispatch',
currentTask: 'task-6c',
taskState: 'complete',
derivedAction: {
type: 'message_subagent',
task: 'continue with task-7c',
},
replyClosureState: 'completed',
dispatchReceipt: {
planId: 'plan-derived-action-with-bound-dispatch',
currentTask: 'task-6c',
derivedAction: {
type: 'message_subagent',
task: 'continue with task-7c',
},
dispatchedAt: '2026-04-24T12:05:00+08:00',
},
},
});
try {
const result = runGate({
args: ['--compact', '--input', fixture.path('input.json')],
});
if (result.status !== 0 && result.status !== null) {
throw new Error(`expected controlled execution, got status=${result.status}
${result.stderr || result.stdout}`);
}
if (!result.json || typeof result.json !== 'object') {
throw new Error(`expected JSON output
stdout=${result.stdout}`);
}
if (result.json.ok !== true) {
throw new Error(`expected continuity pass ok=true when derivedAction has bound dispatch receipt, got ${JSON.stringify(result.json)}`);
}
} finally {
fixture.cleanup();
}
},
},
{
name: 'continuity: passes when task is complete, next action is known, no dispatch receipt exists, and closure is waiting_user',
run() {
const fixture = createFixture({
'input.json': {
planId: 'plan-waiting-user-closure',
currentTask: 'task-8',
taskState: 'complete',
nextDerivedAction: {
type: 'message_subagent',
task: 'continue with task-9',
},
replyClosureState: 'waiting_user',
dispatchReceipt: null,
},
});
try {
const result = runGate({
args: ['--compact', '--input', fixture.path('input.json')],
});
if (result.status !== 0 && result.status !== null) {
throw new Error(`expected controlled execution, got status=${result.status}
${result.stderr || result.stdout}`);
}
if (!result.json || typeof result.json !== 'object') {
throw new Error(`expected JSON output
stdout=${result.stdout}`);
}
if (result.json.ok !== true) {
throw new Error(`expected continuity pass ok=true when closure is waiting_user, got ${JSON.stringify(result.json)}`);
}
} finally {
fixture.cleanup();
}
},
},
{
name: 'continuity: passes when task is complete, next action is known, no dispatch receipt exists, and closure is pending_verification',
run() {
const fixture = createFixture({
'input.json': {
planId: 'plan-pending-verification-closure',
currentTask: 'task-8b',
taskState: 'complete',
nextDerivedAction: {
type: 'message_subagent',
task: 'continue with task-9',
},
replyClosureState: 'pending_verification',
dispatchReceipt: null,
},
});
try {
const result = runGate({
args: ['--compact', '--input', fixture.path('input.json')],
});
if (result.status !== 0 && result.status !== null) {
throw new Error(`expected controlled execution, got status=${result.status}
${result.stderr || result.stdout}`);
}
if (!result.json || typeof result.json !== 'object') {
throw new Error(`expected JSON output
stdout=${result.stdout}`);
}
if (result.json.ok !== true) {
throw new Error(`expected continuity pass ok=true when closure is pending_verification, got ${JSON.stringify(result.json)}`);
}
} finally {
fixture.cleanup();
}
},
},
{
name: 'continuity: passes when task is complete, next action is known, no dispatch receipt exists, and closure is blocked',
run() {
const fixture = createFixture({
'input.json': {
planId: 'plan-blocked-closure',
currentTask: 'task-9',
taskState: 'complete',
nextDerivedAction: {
type: 'message_subagent',
task: 'continue with task-10',
},
replyClosureState: 'blocked',
dispatchReceipt: null,
},
});
try {
const result = runGate({
args: ['--compact', '--input', fixture.path('input.json')],
});
if (result.status !== 0 && result.status !== null) {
throw new Error(`expected controlled execution, got status=${result.status}
${result.stderr || result.stdout}`);
}
if (!result.json || typeof result.json !== 'object') {
throw new Error(`expected JSON output
stdout=${result.stdout}`);
}
if (result.json.ok !== true) {
throw new Error(`expected continuity pass ok=true when closure is blocked, got ${JSON.stringify(result.json)}`);
}
} finally {
fixture.cleanup();
}
},
},
];
const results = [];
let failed = false;
for (const test of tests) {
try {
test.run();
results.push({ test: test.name, ok: true });
} catch (error) {
failed = true;
results.push({
test: test.name,
ok: false,
error: error instanceof Error ? error.message : String(error),
});
}
}
const summary = {
total: tests.length,
passed: results.filter((entry) => entry.ok).length,
failed: results.filter((entry) => !entry.ok).length,
};
process.stdout.write(`${JSON.stringify({ summary, results }, null, 2)}\n`);
if (failed) process.exit(1);

View File

@@ -0,0 +1,245 @@
#!/usr/bin/env node
import assert from 'node:assert/strict';
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import path from 'node:path';
import process from 'node:process';
import { spawnSync } from 'node:child_process';
const ROOT_DIR = path.resolve(import.meta.dirname, '..');
const WATCHDOG_SCRIPT = path.join(ROOT_DIR, 'scripts', 'subagent_delivery_watchdog.mjs');
function createFixtureRunner() {
const fixtureRoot = mkdtempSync(path.join(tmpdir(), 'subagent-watchdog-test-'));
function writeFixture(name, content) {
const fixturePath = path.join(fixtureRoot, name);
const body = typeof content === 'string' ? content : JSON.stringify(content, null, 2);
writeFileSync(fixturePath, body);
return fixturePath;
}
function runWatchdog(args = [], options = {}) {
const result = spawnSync(process.execPath, [WATCHDOG_SCRIPT, ...args], {
cwd: ROOT_DIR,
encoding: 'utf8',
...options,
});
return {
status: result.status,
signal: result.signal,
stdout: result.stdout ?? '',
stderr: result.stderr ?? '',
error: result.error ?? null,
};
}
function cleanup() {
rmSync(fixtureRoot, { recursive: true, force: true });
}
return {
fixtureRoot,
writeFixture,
runWatchdog,
cleanup,
};
}
const tests = [];
function test(name, fn) {
tests.push({ name, fn });
}
function printResult(prefix, name, detail = '') {
const suffix = detail ? ` ${detail}` : '';
process.stdout.write(`${prefix} ${name}${suffix}\n`);
}
test('fixture runner can invoke watchdog skeleton with a generated input file', () => {
const runner = createFixtureRunner();
try {
const inputPath = runner.writeFixture('dispatch.json', {
runId: 'fixture-run-001',
childSessionKey: 'session:test',
});
const result = runner.runWatchdog(['--compact', '--input', inputPath]);
assert.equal(result.status, 0, `expected zero exit status, got ${result.status}\n${result.stderr}`);
assert.equal(result.stderr, '');
const payload = JSON.parse(result.stdout);
assert.equal(payload.ok, true);
assert.equal(payload.tool, 'subagent_delivery_watchdog');
assert.equal(payload.result.status, 'not_implemented');
assert.equal(payload.input.path, inputPath);
assert.equal(payload.input.exists, true);
} finally {
runner.cleanup();
}
});
test('watchdog reports active before SLA when dispatch exists and no completion receipt has arrived yet', () => {
const runner = createFixtureRunner();
try {
const inputPath = runner.writeFixture('dispatch-before-sla.json', {
runId: 'fixture-run-active-before-sla',
childSessionKey: 'session:active-before-sla',
dispatchAt: '2026-04-24T10:00:00.000Z',
expectedBy: '2026-04-24T10:10:00.000Z',
currentTime: '2026-04-24T10:05:00.000Z',
});
const result = runner.runWatchdog(['--compact', '--input', inputPath]);
assert.equal(result.status, 0, `expected zero exit status, got ${result.status}
${result.stderr}`);
assert.equal(result.stderr, '');
const payload = JSON.parse(result.stdout);
assert.equal(payload.ok, true);
assert.equal(payload.input.path, inputPath);
assert.equal(payload.input.exists, true);
assert.equal(payload.result.status, 'active');
} finally {
runner.cleanup();
}
});
test('watchdog reports suspect delivery failure after SLA when dispatch exists and no completion receipt has arrived yet', () => {
const runner = createFixtureRunner();
try {
const inputPath = runner.writeFixture('dispatch-beyond-sla.json', {
runId: 'fixture-run-suspect-delivery-failure',
childSessionKey: 'session:suspect-delivery-failure',
dispatchAt: '2026-04-24T10:00:00.000Z',
expectedBy: '2026-04-24T10:10:00.000Z',
currentTime: '2026-04-24T10:15:00.000Z',
});
const result = runner.runWatchdog(['--compact', '--input', inputPath]);
assert.equal(result.status, 0, `expected zero exit status, got ${result.status}
${result.stderr}`);
assert.equal(result.stderr, '');
const payload = JSON.parse(result.stdout);
assert.equal(payload.ok, true);
assert.equal(payload.input.path, inputPath);
assert.equal(payload.input.exists, true);
assert.equal(payload.result.status, 'suspect_delivery_failure');
} finally {
runner.cleanup();
}
});
test('watchdog reports completed when dispatch exists and completion receipt has arrived', () => {
const runner = createFixtureRunner();
try {
const inputPath = runner.writeFixture('dispatch-completed.json', {
runId: 'fixture-run-completed',
childSessionKey: 'session:completed',
dispatchAt: '2026-04-24T10:00:00.000Z',
expectedBy: '2026-04-24T10:10:00.000Z',
currentTime: '2026-04-24T10:05:00.000Z',
completionReceiptAt: '2026-04-24T10:04:00.000Z',
});
const result = runner.runWatchdog(['--compact', '--input', inputPath]);
assert.equal(result.status, 0, `expected zero exit status, got ${result.status}
${result.stderr}`);
assert.equal(result.stderr, '');
const payload = JSON.parse(result.stdout);
assert.equal(payload.ok, true);
assert.equal(payload.input.path, inputPath);
assert.equal(payload.input.exists, true);
assert.equal(payload.result.status, 'completed');
} finally {
runner.cleanup();
}
});
test('watchdog reports done but not forwarded when child run is marked done without a main-thread completion receipt', () => {
const runner = createFixtureRunner();
try {
const inputPath = runner.writeFixture('dispatch-done-not-forwarded.json', {
runId: 'fixture-run-done-not-forwarded',
childSessionKey: 'session:done-not-forwarded',
dispatchAt: '2026-04-24T10:00:00.000Z',
expectedBy: '2026-04-24T10:10:00.000Z',
currentTime: '2026-04-24T10:05:00.000Z',
childRunStatus: 'done',
});
const result = runner.runWatchdog(['--compact', '--input', inputPath]);
assert.equal(result.status, 0, `expected zero exit status, got ${result.status}
${result.stderr}`);
assert.equal(result.stderr, '');
const payload = JSON.parse(result.stdout);
assert.equal(payload.ok, true);
assert.equal(payload.input.path, inputPath);
assert.equal(payload.input.exists, true);
assert.equal(payload.result.status, 'done_but_not_forwarded');
} finally {
runner.cleanup();
}
});
test('fixture runner exposes missing-input behavior for future fail-first cases', () => {
const runner = createFixtureRunner();
try {
const missingPath = path.join(runner.fixtureRoot, 'missing.json');
const result = runner.runWatchdog(['--compact', '--input', missingPath]);
assert.equal(result.status, 0, `expected zero exit status, got ${result.status}\n${result.stderr}`);
const payload = JSON.parse(result.stdout);
assert.equal(payload.ok, true);
assert.equal(payload.input.path, missingPath);
assert.equal(payload.input.exists, false);
assert.equal(payload.result.status, 'not_implemented');
} finally {
runner.cleanup();
}
});
function main() {
let passed = 0;
for (const { name, fn } of tests) {
try {
fn();
passed += 1;
printResult('PASS', name);
} catch (error) {
printResult('FAIL', name, error instanceof Error ? `- ${error.message}` : `- ${String(error)}`);
if (error instanceof Error && error.stack) {
process.stderr.write(`${error.stack}\n`);
}
process.exitCode = 1;
}
}
const failed = tests.length - passed;
process.stdout.write(`\nSummary: ${passed} passed, ${failed} failed, ${tests.length} total\n`);
}
main();