feat: write approved-plan continuity dispatch receipts

This commit is contained in:
Eve
2026-04-24 12:22:53 +08:00
parent 8f9bdf0877
commit b4331bf9d2

View File

@@ -1,9 +1,13 @@
#!/usr/bin/env node #!/usr/bin/env node
import fs from 'node:fs'; 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) { function parseArgs(argv) {
let inputPath = null; let inputPath = null;
let compact = false; let compact = false;
let receiptDir = DEFAULT_RECEIPT_DIR;
for (let i = 0; i < argv.length; i += 1) { for (let i = 0; i < argv.length; i += 1) {
const arg = argv[i]; const arg = argv[i];
@@ -19,13 +23,24 @@ function parseArgs(argv) {
continue; 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') { if (arg === '--compact') {
compact = true; compact = true;
continue; continue;
} }
} }
return { inputPath, compact }; return { inputPath, compact, receiptDir };
} }
function readInput(inputPath) { function readInput(inputPath) {
@@ -38,9 +53,11 @@ function readInput(inputPath) {
try { try {
const raw = fs.readFileSync(inputPath, 'utf8'); const raw = fs.readFileSync(inputPath, 'utf8');
const parsed = JSON.parse(raw);
return { return {
ok: true, ok: true,
bytes: Buffer.byteLength(raw, 'utf8'), bytes: Buffer.byteLength(raw, 'utf8'),
parsed,
}; };
} catch (error) { } catch (error) {
return { return {
@@ -50,33 +67,128 @@ function readInput(inputPath) {
} }
} }
const { inputPath, compact } = parseArgs(process.argv.slice(2)); 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); const input = readInput(inputPath);
const response = { let response;
ok: input.ok,
status: input.ok ? 'placeholder_receipt' : 'input_error', if (!input.ok) {
binding: 'approved_plan_dispatch', response = {
compact, ok: false,
inputPath, status: 'input_error',
receipt: input.ok binding: 'approved_plan_dispatch',
? { compact,
placeholder: true, inputPath,
planId: null, receipt: null,
currentTask: null, receiptPath: null,
nextDerivedAction: null, input: {
dispatchedAt: null, ok: false,
} error: input.error,
: null, },
input: input.ok };
? { } 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, ok: true,
bytes: input.bytes, bytes: input.bytes,
}
: {
ok: false,
error: input.error,
}, },
}; };
} 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`); process.stdout.write(`${JSON.stringify(response)}\n`);