161 lines
4.3 KiB
JavaScript
161 lines
4.3 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* CI State Update Script
|
|
*
|
|
* Deterministic state management for CI monitor actions.
|
|
* Three commands: gate, post-action, cycle-check.
|
|
*
|
|
* Usage:
|
|
* node ci-state-update.mjs gate --gate-type <local-fix|env-rerun> [counter args]
|
|
* node ci-state-update.mjs post-action --action <type> [--cipe-url <url>] [--commit-sha <sha>]
|
|
* node ci-state-update.mjs cycle-check --code <code> [--agent-triggered] [counter args]
|
|
*/
|
|
|
|
// --- Arg parsing ---
|
|
|
|
const args = process.argv.slice(2);
|
|
const command = args[0];
|
|
|
|
function getFlag(name) {
|
|
return args.includes(name);
|
|
}
|
|
|
|
function getArg(name) {
|
|
const idx = args.indexOf(name);
|
|
return idx !== -1 && idx + 1 < args.length ? args[idx + 1] : null;
|
|
}
|
|
|
|
function output(result) {
|
|
console.log(JSON.stringify(result));
|
|
}
|
|
|
|
// --- gate ---
|
|
// Check if an action is allowed and return incremented counter.
|
|
// Called before any local fix attempt or environment rerun.
|
|
|
|
function gate() {
|
|
const gateType = getArg('--gate-type');
|
|
|
|
if (gateType === 'local-fix') {
|
|
const count = parseInt(getArg('--local-verify-count') || '0', 10);
|
|
const max = parseInt(getArg('--local-verify-attempts') || '3', 10);
|
|
if (count >= max) {
|
|
return output({
|
|
allowed: false,
|
|
localVerifyCount: count,
|
|
message: `Local fix budget exhausted (${count}/${max} attempts)`,
|
|
});
|
|
}
|
|
return output({
|
|
allowed: true,
|
|
localVerifyCount: count + 1,
|
|
message: null,
|
|
});
|
|
}
|
|
|
|
if (gateType === 'env-rerun') {
|
|
const count = parseInt(getArg('--env-rerun-count') || '0', 10);
|
|
if (count >= 2) {
|
|
return output({
|
|
allowed: false,
|
|
envRerunCount: count,
|
|
message: `Environment issue persists after ${count} reruns. Manual investigation needed.`,
|
|
});
|
|
}
|
|
return output({
|
|
allowed: true,
|
|
envRerunCount: count + 1,
|
|
message: null,
|
|
});
|
|
}
|
|
|
|
output({ allowed: false, message: `Unknown gate type: ${gateType}` });
|
|
}
|
|
|
|
// --- post-action ---
|
|
// Compute next state after an action is taken.
|
|
// Returns wait mode params and whether the action was agent-triggered.
|
|
|
|
function postAction() {
|
|
const action = getArg('--action');
|
|
const cipeUrl = getArg('--cipe-url');
|
|
const commitSha = getArg('--commit-sha');
|
|
|
|
// MCP-triggered or auto-applied: track by cipeUrl
|
|
const cipeUrlActions = ['fix-auto-applying', 'apply-mcp', 'env-rerun'];
|
|
// Local push: track by commitSha
|
|
const commitShaActions = [
|
|
'apply-local-push',
|
|
'reject-fix-push',
|
|
'local-fix-push',
|
|
'auto-fix-push',
|
|
'empty-commit-push',
|
|
];
|
|
|
|
const trackByCipeUrl = cipeUrlActions.includes(action);
|
|
const trackByCommitSha = commitShaActions.includes(action);
|
|
|
|
if (!trackByCipeUrl && !trackByCommitSha) {
|
|
return output({ error: `Unknown action: ${action}` });
|
|
}
|
|
|
|
// fix-auto-applying: self-healing did it, NOT the monitor
|
|
const agentTriggered = action !== 'fix-auto-applying';
|
|
|
|
output({
|
|
waitMode: true,
|
|
pollCount: 0,
|
|
lastCipeUrl: trackByCipeUrl ? cipeUrl : null,
|
|
expectedCommitSha: trackByCommitSha ? commitSha : null,
|
|
agentTriggered,
|
|
});
|
|
}
|
|
|
|
// --- cycle-check ---
|
|
// Cycle classification + counter resets when a new "done" code is received.
|
|
// Called at the start of handling each actionable code.
|
|
|
|
function cycleCheck() {
|
|
const status = getArg('--code');
|
|
const wasAgentTriggered = getFlag('--agent-triggered');
|
|
let cycleCount = parseInt(getArg('--cycle-count') || '0', 10);
|
|
const maxCycles = parseInt(getArg('--max-cycles') || '10', 10);
|
|
let envRerunCount = parseInt(getArg('--env-rerun-count') || '0', 10);
|
|
|
|
// Cycle classification: if previous cycle was agent-triggered, count it
|
|
if (wasAgentTriggered) cycleCount++;
|
|
|
|
// Reset env_rerun_count on non-environment status
|
|
if (status !== 'environment_issue') envRerunCount = 0;
|
|
|
|
// Approaching limit gate
|
|
const approachingLimit = cycleCount >= maxCycles - 2;
|
|
|
|
output({
|
|
cycleCount,
|
|
agentTriggered: false,
|
|
envRerunCount,
|
|
approachingLimit,
|
|
message: approachingLimit
|
|
? `Approaching cycle limit (${cycleCount}/${maxCycles})`
|
|
: null,
|
|
});
|
|
}
|
|
|
|
// --- Dispatch ---
|
|
|
|
switch (command) {
|
|
case 'gate':
|
|
gate();
|
|
break;
|
|
case 'post-action':
|
|
postAction();
|
|
break;
|
|
case 'cycle-check':
|
|
cycleCheck();
|
|
break;
|
|
default:
|
|
output({ error: `Unknown command: ${command}` });
|
|
}
|