@ludecker/aaac 1.1.5 → 1.2.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.
- package/README.md +27 -12
- package/package.json +9 -9
- package/src/cli.mjs +19 -7
- package/src/generators/generate-commands.mjs +25 -1
- package/src/generators/generate-graph.mjs +9 -1
- package/src/lib/install.mjs +13 -1
- package/src/lib/sweep-project-docs.mjs +348 -0
- package/src/run-engine/advance-phase.mjs +23 -0
- package/src/run-engine/debug-run.mjs +0 -0
- package/src/run-engine/gate-write.mjs +13 -0
- package/src/run-engine/lib.mjs +153 -5
- package/src/run-engine/log-dump.mjs +0 -0
- package/src/run-engine/log-trace.mjs +0 -0
- package/templates/cursor/aaac/enforcement.json +96 -5
- package/templates/cursor/aaac/graph.project.yaml +44 -5
- package/templates/cursor/aaac/lifecycle/lifecycle.json +26 -0
- package/templates/cursor/aaac/lifecycle/phases.json +9 -1
- package/templates/cursor/aaac/ontology.json +1 -0
- package/templates/cursor/aaac/project.config.json +36 -0
- package/templates/cursor/aaac/scripts/remediation/auto-check-swarm-synthesis.mjs +75 -0
- package/templates/cursor/aaac/scripts/remediation/auto-dispatch-queue-from-health.mjs +78 -0
- package/templates/cursor/aaac/scripts/remediation/bootstrap-autonomous.mjs +113 -0
- package/templates/cursor/aaac/scripts/remediation/capture-verify-baseline.mjs +66 -0
- package/templates/cursor/aaac/scripts/remediation/capture-wave-snapshot.mjs +79 -0
- package/templates/cursor/aaac/scripts/remediation/check-swarm-raw.template.json +26 -0
- package/templates/cursor/aaac/scripts/remediation/classify-fallow-issues.mjs +77 -0
- package/templates/cursor/aaac/scripts/remediation/classify-verify-failure.mjs +176 -0
- package/templates/cursor/aaac/scripts/remediation/compute-satisfaction.mjs +344 -0
- package/templates/cursor/aaac/scripts/remediation/debt-sweep-gate.mjs +202 -0
- package/templates/cursor/aaac/scripts/remediation/dispatch-rules.json +44 -0
- package/templates/cursor/aaac/scripts/remediation/fallow-fp-rules.json +87 -0
- package/templates/cursor/aaac/scripts/remediation/fallow-scan.mjs +219 -0
- package/templates/cursor/aaac/scripts/remediation/handle-yield.mjs +240 -0
- package/templates/cursor/aaac/scripts/remediation/init-campaign.mjs +211 -0
- package/templates/cursor/aaac/scripts/remediation/lib/autonomous-mode.mjs +63 -0
- package/templates/cursor/aaac/scripts/remediation/lib/campaign-focus.mjs +87 -0
- package/templates/cursor/aaac/scripts/remediation/lib/fallow-classifier.mjs +190 -0
- package/templates/cursor/aaac/scripts/remediation/lib/fallow-health-targets.mjs +56 -0
- package/templates/cursor/aaac/scripts/remediation/lib/fallow-metrics.mjs +119 -0
- package/templates/cursor/aaac/scripts/remediation/lib/invoke-cursor-agent.mjs +51 -0
- package/templates/cursor/aaac/scripts/remediation/lib/reconcile-run-manifest.mjs +41 -0
- package/templates/cursor/aaac/scripts/remediation/lib/regression-analysis.mjs +55 -0
- package/templates/cursor/aaac/scripts/remediation/lib/remediation-config.mjs +69 -0
- package/templates/cursor/aaac/scripts/remediation/lib/remediation-progress.mjs +58 -0
- package/templates/cursor/aaac/scripts/remediation/lib/remediation-watch-loop.mjs +168 -0
- package/templates/cursor/aaac/scripts/remediation/lib/runner-exec.mjs +156 -0
- package/templates/cursor/aaac/scripts/remediation/lib/runner-state.mjs +145 -0
- package/templates/cursor/aaac/scripts/remediation/lib/verify-metrics.mjs +205 -0
- package/templates/cursor/aaac/scripts/remediation/merge-check-swarm.mjs +257 -0
- package/templates/cursor/aaac/scripts/remediation/plan-waves-from-queue.mjs +85 -0
- package/templates/cursor/aaac/scripts/remediation/prepare-check-context.mjs +148 -0
- package/templates/cursor/aaac/scripts/remediation/record-fallow-fp.mjs +107 -0
- package/templates/cursor/aaac/scripts/remediation/record-iteration-step.mjs +56 -0
- package/templates/cursor/aaac/scripts/remediation/remediation-cli.mjs +157 -0
- package/templates/cursor/aaac/scripts/remediation/remediation-cursor-watch.sh +10 -0
- package/templates/cursor/aaac/scripts/remediation/remediation-runner-daemon.sh +13 -0
- package/templates/cursor/aaac/scripts/remediation/remediation-runner.mjs +748 -0
- package/templates/cursor/aaac/scripts/remediation/remediation-yield-watcher.mjs +40 -0
- package/templates/cursor/aaac/scripts/remediation/remediator-gate.mjs +405 -0
- package/templates/cursor/aaac/scripts/remediation/repair-fallow-start-baseline.mjs +118 -0
- package/templates/cursor/aaac/scripts/remediation/runner-health-check.mjs +164 -0
- package/templates/cursor/aaac/scripts/remediation/satisfaction-loop-gate.mjs +286 -0
- package/templates/cursor/aaac/scripts/remediation/validate-campaign-complete.mjs +191 -0
- package/templates/cursor/aaac/scripts/remediation/verify-remediation-iteration.mjs +112 -0
- package/templates/cursor/aaac/scripts/run-engine/advance-phase.mjs +23 -0
- package/templates/cursor/aaac/scripts/run-engine/debug-run.mjs +0 -0
- package/templates/cursor/aaac/scripts/run-engine/gate-write.mjs +13 -0
- package/templates/cursor/aaac/scripts/run-engine/lib.mjs +153 -5
- package/templates/cursor/aaac/scripts/run-engine/log-dump.mjs +0 -0
- package/templates/cursor/aaac/scripts/run-engine/log-trace.mjs +0 -0
- package/templates/cursor/agents/doc-conformance.md +25 -0
- package/templates/cursor/agents/implementation-review.md +21 -0
- package/templates/cursor/agents/remediation-check-app-inventory.md +32 -0
- package/templates/cursor/agents/remediation-check-app-ssot.md +24 -0
- package/templates/cursor/agents/remediation-check-app-trace.md +29 -0
- package/templates/cursor/agents/remediation-check-architecture-boundaries.md +21 -0
- package/templates/cursor/agents/remediation-check-architecture-decomposition.md +25 -0
- package/templates/cursor/agents/remediation-check-architecture-deps.md +23 -0
- package/templates/cursor/agents/remediation-check-risk.md +37 -0
- package/templates/cursor/agents/remediation-e2e-gate.md +30 -0
- package/templates/cursor/agents/remediation-remediator.md +69 -0
- package/templates/cursor/agents/test-author.md +27 -0
- package/templates/cursor/commands/remediate-app.md +212 -0
- package/templates/cursor/hooks/aaac-before-submit.sh +0 -0
- package/templates/cursor/hooks/aaac-pre-tool.sh +0 -0
- package/templates/cursor/hooks/aaac-stop.sh +0 -0
- package/templates/cursor/hooks/aaac-subagent-start.sh +0 -0
- package/templates/cursor/rules/aaac-enforcement.mdc +10 -3
- package/templates/cursor/skills/shared/execution/SKILL.md +7 -3
- package/templates/cursor/skills/shared/governance/implementation/SKILL.md +396 -28
- package/templates/cursor/skills/shared/implementation-review/SKILL.md +49 -0
- package/templates/cursor/skills/shared/planning/SKILL.md +5 -0
- package/templates/cursor/skills/shared/remediation/SKILL.md +51 -0
- package/templates/cursor/skills/shared/remediation/babysit/SKILL.md +223 -0
- package/templates/cursor/skills/shared/remediation/check-swarm/SKILL.md +114 -0
- package/templates/cursor/skills/shared/remediation/orchestrator/SKILL.md +275 -0
- package/templates/cursor/skills/shared/remediation/orchestrator/contract.yaml +116 -0
- package/templates/cursor/skills/shared/test-authoring/SKILL.md +58 -0
- package/templates/cursor/skills/shared/testing/SKILL.md +6 -0
- package/templates/cursor/skills/shared/verbs/create/orchestrator/SKILL.md +5 -3
- package/templates/cursor/skills/shared/verbs/fix/orchestrator/SKILL.md +5 -3
- package/templates/cursor/skills/shared/verbs/update/orchestrator/SKILL.md +5 -3
- package/templates/cursor/skills/shared/verification/SKILL.md +5 -3
- package/templates/docs/agentic_architecture.md +169 -97
|
@@ -0,0 +1,748 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Shell runner for /remediate-app — drives scriptable phases, yields for agent work.
|
|
4
|
+
*
|
|
5
|
+
* Modes:
|
|
6
|
+
* --status Print runner + campaign state (JSON)
|
|
7
|
+
* --tick Run one automated step (may yield)
|
|
8
|
+
* --until-yield Loop --tick until agent yield, complete, or blocked
|
|
9
|
+
* --ack-yield Mark agent work done; resume automated steps
|
|
10
|
+
*
|
|
11
|
+
* Exit codes:
|
|
12
|
+
* 0 complete (threshold met or max iterations — report allowed)
|
|
13
|
+
* 1 blocked / fatal
|
|
14
|
+
* 2 runtime error
|
|
15
|
+
* 3 yield — agent must act (see runner-yield.json)
|
|
16
|
+
* 10 progressed (internal; --until-yield continues)
|
|
17
|
+
*
|
|
18
|
+
* Usage:
|
|
19
|
+
* node remediation-runner.mjs --run-id <id> --campaign-id <id> [--tick|--until-yield|--ack-yield|--status]
|
|
20
|
+
*/
|
|
21
|
+
import fs from "fs";
|
|
22
|
+
import path from "path";
|
|
23
|
+
import {
|
|
24
|
+
EXIT,
|
|
25
|
+
PHASES,
|
|
26
|
+
campaignDir,
|
|
27
|
+
clearYield,
|
|
28
|
+
defaultRunnerState,
|
|
29
|
+
emitResult,
|
|
30
|
+
fail,
|
|
31
|
+
iterDir,
|
|
32
|
+
loadCampaign,
|
|
33
|
+
loadManifest,
|
|
34
|
+
loadRunnerState,
|
|
35
|
+
loadYield,
|
|
36
|
+
runArtifactsDir,
|
|
37
|
+
runnerStatePath,
|
|
38
|
+
saveCampaign,
|
|
39
|
+
saveRunnerState,
|
|
40
|
+
syncRunnerFromManifest,
|
|
41
|
+
writeYield,
|
|
42
|
+
yieldArtifactPath,
|
|
43
|
+
} from "./lib/runner-state.mjs";
|
|
44
|
+
import {
|
|
45
|
+
advancePhase,
|
|
46
|
+
journal,
|
|
47
|
+
parseDispatchQueueYaml,
|
|
48
|
+
runNode,
|
|
49
|
+
writeRunArtifact,
|
|
50
|
+
} from "./lib/runner-exec.mjs";
|
|
51
|
+
import {
|
|
52
|
+
reconcileRemediationRun,
|
|
53
|
+
} from "./lib/reconcile-run-manifest.mjs";
|
|
54
|
+
|
|
55
|
+
const REQUIRED_SWARM_AGENTS = 7;
|
|
56
|
+
const CHECK_SWARM_AGENTS = [
|
|
57
|
+
"remediation-check-app-inventory",
|
|
58
|
+
"remediation-check-app-ssot",
|
|
59
|
+
"remediation-check-app-trace",
|
|
60
|
+
"remediation-check-architecture-boundaries",
|
|
61
|
+
"remediation-check-architecture-deps",
|
|
62
|
+
"remediation-check-architecture-decomposition",
|
|
63
|
+
"remediation-check-risk",
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
function parseArgs(argv) {
|
|
67
|
+
const out = {
|
|
68
|
+
runId: null,
|
|
69
|
+
campaignId: null,
|
|
70
|
+
mode: "tick",
|
|
71
|
+
scope: null,
|
|
72
|
+
intent: null,
|
|
73
|
+
ackType: null,
|
|
74
|
+
};
|
|
75
|
+
for (let i = 0; i < argv.length; i++) {
|
|
76
|
+
const a = argv[i];
|
|
77
|
+
if (a === "--run-id") out.runId = argv[++i];
|
|
78
|
+
else if (a === "--campaign-id") out.campaignId = argv[++i];
|
|
79
|
+
else if (a === "--scope") out.scope = argv[++i];
|
|
80
|
+
else if (a === "--intent") out.intent = argv[++i];
|
|
81
|
+
else if (a === "--status") out.mode = "status";
|
|
82
|
+
else if (a === "--tick") out.mode = "tick";
|
|
83
|
+
else if (a === "--until-yield") out.mode = "until-yield";
|
|
84
|
+
else if (a === "--ack-yield") {
|
|
85
|
+
out.mode = "ack-yield";
|
|
86
|
+
out.ackType = argv[++i];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function ensureRunnerState(args, campaign, manifest) {
|
|
93
|
+
let state = loadRunnerState(args.campaignId);
|
|
94
|
+
if (!state) {
|
|
95
|
+
state = defaultRunnerState({
|
|
96
|
+
runId: args.runId,
|
|
97
|
+
campaignId: args.campaignId,
|
|
98
|
+
iteration: campaign.iteration ?? 0,
|
|
99
|
+
phase: manifest?.phase ?? "campaign_init",
|
|
100
|
+
});
|
|
101
|
+
saveRunnerState(state);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Runner state owns phase while campaign is running; reconcile manifest from runner.
|
|
105
|
+
if (manifest?.command === "remediate-app" && campaign.status === "running") {
|
|
106
|
+
reconcileRemediationRun(args.runId, state);
|
|
107
|
+
} else {
|
|
108
|
+
syncRunnerFromManifest(state, manifest);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
state.run_id = args.runId;
|
|
112
|
+
state.iteration = campaign.iteration ?? state.iteration;
|
|
113
|
+
saveRunnerState(state);
|
|
114
|
+
return state;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function setYield(state, yieldPayload) {
|
|
118
|
+
state.status = "yielded";
|
|
119
|
+
state.yield = yieldPayload;
|
|
120
|
+
writeYield(state.campaign_id, yieldPayload);
|
|
121
|
+
saveRunnerState(state);
|
|
122
|
+
emitResult(state, { action: "yield" });
|
|
123
|
+
process.exit(EXIT.yield_agent);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function markProgress(state, campaign) {
|
|
127
|
+
const satPath = path.join(iterDir(state.campaign_id, state.iteration), "satisfaction.json");
|
|
128
|
+
const sat = fs.existsSync(satPath)
|
|
129
|
+
? JSON.parse(fs.readFileSync(satPath, "utf8"))
|
|
130
|
+
: null;
|
|
131
|
+
const score = sat?.score ?? campaign.current?.satisfaction_score ?? null;
|
|
132
|
+
const clones = campaign.current?.fallow_dupes_clone_groups ?? null;
|
|
133
|
+
if (score !== state.last_score || clones !== state.last_clone_groups) {
|
|
134
|
+
state.last_score = score;
|
|
135
|
+
state.last_clone_groups = clones;
|
|
136
|
+
state.last_progress_at = new Date().toISOString();
|
|
137
|
+
state.stall_count = 0;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function advanceRunnerPhase(runId, completedPhase, runnerState) {
|
|
142
|
+
reconcileRemediationRun(runId, {
|
|
143
|
+
...runnerState,
|
|
144
|
+
phase: completedPhase,
|
|
145
|
+
});
|
|
146
|
+
const adv = advancePhase(runId, completedPhase, { force: true });
|
|
147
|
+
if (!adv.ok) {
|
|
148
|
+
fail(`advance ${completedPhase} failed: ${adv.stderr}`, EXIT.runtime_error);
|
|
149
|
+
}
|
|
150
|
+
const manifest = loadManifest(runId);
|
|
151
|
+
runnerState.phase = manifest?.phase ?? completedPhase;
|
|
152
|
+
return runnerState.phase;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function readPlanWaves(runId, campaignId) {
|
|
156
|
+
const runPlan = path.join(runArtifactsDir(runId), "plan_waves.yaml");
|
|
157
|
+
const campPlan = path.join(campaignDir(campaignId), "artifacts", "plan_waves.yaml");
|
|
158
|
+
const text = fs.existsSync(runPlan)
|
|
159
|
+
? fs.readFileSync(runPlan, "utf8")
|
|
160
|
+
: fs.existsSync(campPlan)
|
|
161
|
+
? fs.readFileSync(campPlan, "utf8")
|
|
162
|
+
: null;
|
|
163
|
+
if (!text) return [];
|
|
164
|
+
return parseDispatchQueueYaml(text).map((w, index) => ({ ...w, index }));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function waveCount(runId, campaignId, campaign) {
|
|
168
|
+
const waves = readPlanWaves(runId, campaignId);
|
|
169
|
+
if (waves.length) return waves.length;
|
|
170
|
+
return campaign.config?.max_waves_per_iteration ?? 3;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function checkSwarmRawReady(campaignId, iteration) {
|
|
174
|
+
const rawPath = path.join(iterDir(campaignId, iteration), "check-swarm-raw.json");
|
|
175
|
+
if (!fs.existsSync(rawPath)) return { ok: false, reason: "missing_check_swarm_raw" };
|
|
176
|
+
const raw = JSON.parse(fs.readFileSync(rawPath, "utf8"));
|
|
177
|
+
const agents = raw.agents ?? [];
|
|
178
|
+
if (agents.length < REQUIRED_SWARM_AGENTS) {
|
|
179
|
+
return { ok: false, reason: "insufficient_agents", count: agents.length };
|
|
180
|
+
}
|
|
181
|
+
return { ok: true, raw };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function remediatorHandoffPath(campaignId, iteration, attempt) {
|
|
185
|
+
return path.join(
|
|
186
|
+
iterDir(campaignId, iteration),
|
|
187
|
+
`remediator-handoff-attempt-${attempt}.json`,
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function handleCampaignInit(state, campaign, manifest, args) {
|
|
192
|
+
const initArgs = [
|
|
193
|
+
"--run-id",
|
|
194
|
+
args.runId,
|
|
195
|
+
"--campaign-id",
|
|
196
|
+
state.campaign_id,
|
|
197
|
+
"--scope",
|
|
198
|
+
args.scope ?? campaign.scope ?? "whole-repo",
|
|
199
|
+
];
|
|
200
|
+
if (args.intent) initArgs.push("--intent", args.intent);
|
|
201
|
+
if (campaign.status === "running" && campaign.iteration > 0) {
|
|
202
|
+
initArgs.push("--resume", state.campaign_id);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const init = runNode("init-campaign.mjs", initArgs);
|
|
206
|
+
if (!init.ok && init.status !== 0) {
|
|
207
|
+
fail(`init-campaign failed: ${init.stderr || init.stdout}`, EXIT.runtime_error);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const baseline = runNode("capture-verify-baseline.mjs", [
|
|
211
|
+
"--campaign-id",
|
|
212
|
+
state.campaign_id,
|
|
213
|
+
"--run-id",
|
|
214
|
+
args.runId,
|
|
215
|
+
]);
|
|
216
|
+
if (!baseline.ok) {
|
|
217
|
+
fail(`capture-verify-baseline failed: ${baseline.stderr}`, EXIT.runtime_error);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
writeRunArtifact(args.runId, "campaign.json", {
|
|
221
|
+
campaign_id: state.campaign_id,
|
|
222
|
+
iteration: campaign.iteration,
|
|
223
|
+
config: campaign.config,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const adv = advancePhase(args.runId, "campaign_init", { force: true });
|
|
227
|
+
if (!adv.ok) fail(`advance campaign_init failed: ${adv.stderr}`, EXIT.runtime_error);
|
|
228
|
+
state.phase = advanceRunnerPhase(args.runId, "campaign_init", state);
|
|
229
|
+
state.substep = null;
|
|
230
|
+
state.status = "running";
|
|
231
|
+
saveRunnerState(state);
|
|
232
|
+
return EXIT.progressed;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function handleScan(state, campaign, args) {
|
|
236
|
+
const n = state.iteration;
|
|
237
|
+
const startBaseline = path.join(campaignDir(state.campaign_id), "fallow-start-baseline.json");
|
|
238
|
+
const scanArgs = ["--campaign-id", state.campaign_id, "--iteration", String(n)];
|
|
239
|
+
if (!fs.existsSync(startBaseline) && n === 0) scanArgs.push("--save-baseline");
|
|
240
|
+
|
|
241
|
+
const scan = runNode("fallow-scan.mjs", scanArgs);
|
|
242
|
+
if (!scan.ok) fail(`fallow-scan failed: ${scan.stderr}`, EXIT.runtime_error);
|
|
243
|
+
|
|
244
|
+
const classify = runNode("classify-fallow-issues.mjs", [
|
|
245
|
+
"--campaign-id",
|
|
246
|
+
state.campaign_id,
|
|
247
|
+
"--iteration",
|
|
248
|
+
String(n),
|
|
249
|
+
]);
|
|
250
|
+
if (!classify.ok) fail(`classify-fallow-issues failed: ${classify.stderr}`, EXIT.runtime_error);
|
|
251
|
+
|
|
252
|
+
const adv = advancePhase(args.runId, "scan", { force: true });
|
|
253
|
+
if (!adv.ok) fail(`advance scan failed: ${adv.stderr}`, EXIT.runtime_error);
|
|
254
|
+
state.phase = advanceRunnerPhase(args.runId, "scan", state);
|
|
255
|
+
state.substep = "prepare";
|
|
256
|
+
saveRunnerState(state);
|
|
257
|
+
return EXIT.progressed;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function handleCheckSwarm(state, args) {
|
|
261
|
+
const n = state.iteration;
|
|
262
|
+
|
|
263
|
+
if (state.substep === "prepare" || !state.substep) {
|
|
264
|
+
const prep = runNode("prepare-check-context.mjs", [
|
|
265
|
+
"--campaign-id",
|
|
266
|
+
state.campaign_id,
|
|
267
|
+
"--iteration",
|
|
268
|
+
String(n),
|
|
269
|
+
"--run-id",
|
|
270
|
+
args.runId,
|
|
271
|
+
]);
|
|
272
|
+
if (!prep.ok) fail(`prepare-check-context failed: ${prep.stderr}`, EXIT.runtime_error);
|
|
273
|
+
state.substep = "agents";
|
|
274
|
+
saveRunnerState(state);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (state.substep === "agents") {
|
|
278
|
+
const rawCheck = checkSwarmRawReady(state.campaign_id, n);
|
|
279
|
+
if (!rawCheck.ok) {
|
|
280
|
+
setYield(state, {
|
|
281
|
+
type: "check_swarm",
|
|
282
|
+
phase: "check_swarm",
|
|
283
|
+
iteration: n,
|
|
284
|
+
reason: rawCheck.reason,
|
|
285
|
+
required_agents: CHECK_SWARM_AGENTS,
|
|
286
|
+
artifacts_required: [`iterations/${n}/check-swarm-raw.json`],
|
|
287
|
+
skill: ".cursor/skills/shared/remediation/check-swarm/SKILL.md",
|
|
288
|
+
instructions:
|
|
289
|
+
"Launch 7 parallel readonly Task agents per check-swarm SKILL. Collect JSON into check-swarm-raw.json, then run --ack-yield check_swarm.",
|
|
290
|
+
resume_command: `node .cursor/aaac/scripts/remediation/remediation-runner.mjs --run-id ${args.runId} --campaign-id ${state.campaign_id} --ack-yield check_swarm`,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
state.substep = "merge";
|
|
294
|
+
saveRunnerState(state);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (state.substep === "merge") {
|
|
298
|
+
const merge = runNode("merge-check-swarm.mjs", [
|
|
299
|
+
"--campaign-id",
|
|
300
|
+
state.campaign_id,
|
|
301
|
+
"--iteration",
|
|
302
|
+
String(n),
|
|
303
|
+
"--run-id",
|
|
304
|
+
args.runId,
|
|
305
|
+
]);
|
|
306
|
+
if (!merge.ok) fail(`merge-check-swarm failed: ${merge.stderr}`, EXIT.runtime_error);
|
|
307
|
+
|
|
308
|
+
const adv = advancePhase(args.runId, "check_swarm", { force: true });
|
|
309
|
+
if (!adv.ok) fail(`advance check_swarm failed: ${adv.stderr}`, EXIT.runtime_error);
|
|
310
|
+
state.phase = advanceRunnerPhase(args.runId, "check_swarm", state);
|
|
311
|
+
state.substep = null;
|
|
312
|
+
saveRunnerState(state);
|
|
313
|
+
return EXIT.progressed;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return EXIT.progressed;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function handlePlanWaves(state, campaign, args) {
|
|
320
|
+
const queuePath = path.join(campaignDir(state.campaign_id), "dispatch-queue.yaml");
|
|
321
|
+
if (!fs.existsSync(queuePath)) {
|
|
322
|
+
setYield(state, {
|
|
323
|
+
type: "dispatch_queue",
|
|
324
|
+
phase: "plan_waves",
|
|
325
|
+
iteration: state.iteration,
|
|
326
|
+
reason: "missing_dispatch_queue",
|
|
327
|
+
artifacts_required: ["dispatch-queue.yaml"],
|
|
328
|
+
instructions:
|
|
329
|
+
"Write dispatch-queue.yaml from check_swarm merge (waves with command, intent, risk). Then --ack-yield dispatch_queue.",
|
|
330
|
+
resume_command: `node .cursor/aaac/scripts/remediation/remediation-runner.mjs --run-id ${args.runId} --campaign-id ${state.campaign_id} --ack-yield dispatch_queue`,
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const plan = runNode("plan-waves-from-queue.mjs", [
|
|
335
|
+
"--campaign-id",
|
|
336
|
+
state.campaign_id,
|
|
337
|
+
"--run-id",
|
|
338
|
+
args.runId,
|
|
339
|
+
"--iteration",
|
|
340
|
+
String(state.iteration),
|
|
341
|
+
]);
|
|
342
|
+
if (!plan.ok) fail(`plan-waves-from-queue failed: ${plan.stderr}`, EXIT.runtime_error);
|
|
343
|
+
|
|
344
|
+
const adv = advancePhase(args.runId, "plan_waves", { force: true });
|
|
345
|
+
if (!adv.ok) fail(`advance plan_waves failed: ${adv.stderr}`, EXIT.runtime_error);
|
|
346
|
+
state.phase = advanceRunnerPhase(args.runId, "plan_waves", state);
|
|
347
|
+
state.substep = "wave_fix";
|
|
348
|
+
state.wave_index = 0;
|
|
349
|
+
state.attempt = 1;
|
|
350
|
+
saveRunnerState(state);
|
|
351
|
+
return EXIT.progressed;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function handleExecute(state, campaign, args) {
|
|
355
|
+
const totalWaves = waveCount(args.runId, state.campaign_id, campaign);
|
|
356
|
+
const w = state.wave_index;
|
|
357
|
+
|
|
358
|
+
if (w >= totalWaves) {
|
|
359
|
+
const execJson = path.join(runArtifactsDir(args.runId), "execute_waves.json");
|
|
360
|
+
const execYaml = path.join(runArtifactsDir(args.runId), "execute_waves.yaml");
|
|
361
|
+
if (!fs.existsSync(execYaml)) {
|
|
362
|
+
const wavesDone = fs.existsSync(execJson)
|
|
363
|
+
? JSON.parse(fs.readFileSync(execJson, "utf8"))
|
|
364
|
+
: { waves: [] };
|
|
365
|
+
fs.writeFileSync(
|
|
366
|
+
execYaml,
|
|
367
|
+
`campaign_id: ${state.campaign_id}\niteration: ${state.iteration}\nwaves: []\n`,
|
|
368
|
+
);
|
|
369
|
+
if (!fs.existsSync(execJson)) {
|
|
370
|
+
writeRunArtifact(args.runId, "execute_waves.json", {
|
|
371
|
+
campaign_id: state.campaign_id,
|
|
372
|
+
iteration: state.iteration,
|
|
373
|
+
waves: wavesDone.waves ?? [],
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
const adv = advancePhase(args.runId, "execute", { force: true });
|
|
378
|
+
if (!adv.ok) fail(`advance execute failed: ${adv.stderr}`, EXIT.runtime_error);
|
|
379
|
+
state.phase = advanceRunnerPhase(args.runId, "execute", state);
|
|
380
|
+
state.substep = null;
|
|
381
|
+
state.attempt = 1;
|
|
382
|
+
saveRunnerState(state);
|
|
383
|
+
return EXIT.progressed;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const waves = readPlanWaves(args.runId, state.campaign_id);
|
|
387
|
+
const wave = waves[w] ?? {
|
|
388
|
+
index: w,
|
|
389
|
+
command: "fix-module",
|
|
390
|
+
intent: `Remediation wave ${w} — see dispatch-queue.yaml`,
|
|
391
|
+
risk: "low",
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
if (state.substep === "wave_fix") {
|
|
395
|
+
setYield(state, {
|
|
396
|
+
type: "execute_wave",
|
|
397
|
+
phase: "execute",
|
|
398
|
+
iteration: state.iteration,
|
|
399
|
+
wave_index: w,
|
|
400
|
+
wave_total: totalWaves,
|
|
401
|
+
command: wave.command,
|
|
402
|
+
intent: wave.intent,
|
|
403
|
+
risk: wave.risk,
|
|
404
|
+
instructions: `Run inline ${wave.command} (no nested AAAC Run). Respect protected_paths.yaml. Test-only waves need test_execute phase or mark degraded. Then --ack-yield execute_wave.`,
|
|
405
|
+
artifacts_required: [`artifacts/execute_waves.json`],
|
|
406
|
+
resume_command: `node .cursor/aaac/scripts/remediation/remediation-runner.mjs --run-id ${args.runId} --campaign-id ${state.campaign_id} --ack-yield execute_wave`,
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (state.substep === "wave_verify") {
|
|
411
|
+
runNode("capture-wave-snapshot.mjs", [
|
|
412
|
+
"--campaign-id",
|
|
413
|
+
state.campaign_id,
|
|
414
|
+
"--iteration",
|
|
415
|
+
String(state.iteration),
|
|
416
|
+
"--wave-index",
|
|
417
|
+
String(w),
|
|
418
|
+
]);
|
|
419
|
+
|
|
420
|
+
const gate = runNode("remediator-gate.mjs", [
|
|
421
|
+
"--campaign-id",
|
|
422
|
+
state.campaign_id,
|
|
423
|
+
"--iteration",
|
|
424
|
+
String(state.iteration),
|
|
425
|
+
"--mode",
|
|
426
|
+
"wave",
|
|
427
|
+
"--wave-index",
|
|
428
|
+
String(w),
|
|
429
|
+
"--run-id",
|
|
430
|
+
args.runId,
|
|
431
|
+
"--attempt",
|
|
432
|
+
String(state.attempt),
|
|
433
|
+
]);
|
|
434
|
+
|
|
435
|
+
if (gate.status === 0) {
|
|
436
|
+
journal(
|
|
437
|
+
state.campaign_id,
|
|
438
|
+
`- Runner wave ${w} **promoted** iter ${state.iteration}`,
|
|
439
|
+
);
|
|
440
|
+
state.wave_index = w + 1;
|
|
441
|
+
state.substep = "wave_fix";
|
|
442
|
+
state.attempt = 1;
|
|
443
|
+
saveRunnerState(state);
|
|
444
|
+
return EXIT.progressed;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
if (gate.status === 3 && gate.json) {
|
|
448
|
+
setYield(state, {
|
|
449
|
+
type: "remediator",
|
|
450
|
+
phase: "execute",
|
|
451
|
+
subphase: "wave",
|
|
452
|
+
iteration: state.iteration,
|
|
453
|
+
wave_index: w,
|
|
454
|
+
attempt: state.attempt,
|
|
455
|
+
gate_mode: "regression",
|
|
456
|
+
handoff: gate.json.handoff,
|
|
457
|
+
retry_command: gate.json.retry_command,
|
|
458
|
+
instructions:
|
|
459
|
+
"Exit 3 = remediate and retry. Run remediation-remediator handoff inline. Then --ack-yield remediator.",
|
|
460
|
+
resume_command: `node .cursor/aaac/scripts/remediation/remediation-runner.mjs --run-id ${args.runId} --campaign-id ${state.campaign_id} --ack-yield remediator`,
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
if (gate.status === 1) {
|
|
465
|
+
state.status = "blocked";
|
|
466
|
+
saveRunnerState(state);
|
|
467
|
+
emitResult(state, { action: "blocked", gate: gate.json });
|
|
468
|
+
process.exit(EXIT.blocked);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
fail(`remediator-gate wave failed: ${gate.stderr}`, EXIT.runtime_error);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
return EXIT.progressed;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function handleDebtSweep(state, args) {
|
|
478
|
+
const gate = runNode("debt-sweep-gate.mjs", [
|
|
479
|
+
"--campaign-id",
|
|
480
|
+
state.campaign_id,
|
|
481
|
+
"--iteration",
|
|
482
|
+
String(state.iteration),
|
|
483
|
+
"--run-id",
|
|
484
|
+
args.runId,
|
|
485
|
+
"--round",
|
|
486
|
+
"1",
|
|
487
|
+
"--attempt",
|
|
488
|
+
String(state.attempt),
|
|
489
|
+
]);
|
|
490
|
+
|
|
491
|
+
if (gate.status === 0) {
|
|
492
|
+
const adv = advancePhase(args.runId, "debt_sweep", { force: true });
|
|
493
|
+
if (!adv.ok) fail(`advance debt_sweep failed: ${adv.stderr}`, EXIT.runtime_error);
|
|
494
|
+
state.phase = advanceRunnerPhase(args.runId, "debt_sweep", state);
|
|
495
|
+
state.substep = null;
|
|
496
|
+
state.attempt = 1;
|
|
497
|
+
saveRunnerState(state);
|
|
498
|
+
return EXIT.progressed;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if (gate.status === 3 && gate.json) {
|
|
502
|
+
setYield(state, {
|
|
503
|
+
type: "remediator",
|
|
504
|
+
phase: "debt_sweep",
|
|
505
|
+
iteration: state.iteration,
|
|
506
|
+
attempt: state.attempt,
|
|
507
|
+
gate_mode: "strict",
|
|
508
|
+
handoff: gate.json.handoff ?? gate.json.primary,
|
|
509
|
+
retry_command: gate.json.retry_command,
|
|
510
|
+
instructions:
|
|
511
|
+
"Debt sweep exit 3 — fix strict verify failures inline, then --ack-yield remediator.",
|
|
512
|
+
resume_command: `node .cursor/aaac/scripts/remediation/remediation-runner.mjs --run-id ${args.runId} --campaign-id ${state.campaign_id} --ack-yield remediator`,
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
if (gate.status === 1) {
|
|
517
|
+
state.status = "blocked";
|
|
518
|
+
saveRunnerState(state);
|
|
519
|
+
emitResult(state, { action: "blocked", gate: gate.json });
|
|
520
|
+
process.exit(EXIT.blocked);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
fail(`debt-sweep-gate failed: ${gate.stderr}`, EXIT.runtime_error);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function handleSatisfactionGate(state, campaign, args) {
|
|
527
|
+
const compute = runNode("compute-satisfaction.mjs", [
|
|
528
|
+
"--campaign-id",
|
|
529
|
+
state.campaign_id,
|
|
530
|
+
"--iteration",
|
|
531
|
+
String(state.iteration),
|
|
532
|
+
]);
|
|
533
|
+
if (!compute.ok) fail(`compute-satisfaction failed: ${compute.stderr}`, EXIT.runtime_error);
|
|
534
|
+
|
|
535
|
+
const loop = runNode("satisfaction-loop-gate.mjs", [
|
|
536
|
+
"--campaign-id",
|
|
537
|
+
state.campaign_id,
|
|
538
|
+
"--iteration",
|
|
539
|
+
String(state.iteration),
|
|
540
|
+
"--run-id",
|
|
541
|
+
args.runId,
|
|
542
|
+
"--advance",
|
|
543
|
+
]);
|
|
544
|
+
|
|
545
|
+
if (loop.status === 0) {
|
|
546
|
+
const adv = advancePhase(args.runId, "satisfaction_gate", { force: true });
|
|
547
|
+
if (!adv.ok) fail(`advance satisfaction_gate failed: ${adv.stderr}`, EXIT.runtime_error);
|
|
548
|
+
state.phase = advanceRunnerPhase(args.runId, "satisfaction_gate", state);
|
|
549
|
+
state.status = "complete";
|
|
550
|
+
state.substep = null;
|
|
551
|
+
saveRunnerState(state);
|
|
552
|
+
clearYield(state.campaign_id);
|
|
553
|
+
emitResult(state, { action: "complete", gate: loop.json });
|
|
554
|
+
process.exit(EXIT.complete);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
if (loop.status === 3) {
|
|
558
|
+
campaign = loadCampaign(state.campaign_id);
|
|
559
|
+
state.iteration = campaign?.iteration ?? state.iteration + 1;
|
|
560
|
+
state.phase = "scan";
|
|
561
|
+
state.substep = null;
|
|
562
|
+
state.wave_index = 0;
|
|
563
|
+
state.attempt = 1;
|
|
564
|
+
state.status = "running";
|
|
565
|
+
saveRunnerState(state);
|
|
566
|
+
journal(
|
|
567
|
+
state.campaign_id,
|
|
568
|
+
`- Runner **CONTINUE** → iteration ${state.iteration} (score below threshold)`,
|
|
569
|
+
);
|
|
570
|
+
return EXIT.progressed;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
fail(`satisfaction-loop-gate failed: ${loop.stderr}`, EXIT.runtime_error);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
function handleReport(state, args) {
|
|
577
|
+
const validate = runNode("validate-campaign-complete.mjs", [
|
|
578
|
+
"--campaign-id",
|
|
579
|
+
state.campaign_id,
|
|
580
|
+
"--iteration",
|
|
581
|
+
String(state.iteration),
|
|
582
|
+
"--require-debt-sweep",
|
|
583
|
+
"--require-satisfaction-loop",
|
|
584
|
+
]);
|
|
585
|
+
if (!validate.ok) {
|
|
586
|
+
fail(`validate-campaign-complete failed: ${validate.stderr}`, EXIT.blocked);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
setYield(state, {
|
|
590
|
+
type: "report",
|
|
591
|
+
phase: "report",
|
|
592
|
+
iteration: state.iteration,
|
|
593
|
+
instructions:
|
|
594
|
+
"Write artifacts/report.md from campaign journal + satisfaction-history. Advance phase report and mark Run completed.",
|
|
595
|
+
resume_command: `node .cursor/aaac/scripts/remediation/remediation-runner.mjs --run-id ${args.runId} --campaign-id ${state.campaign_id} --ack-yield report`,
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function ackYield(state, args, ackType) {
|
|
600
|
+
const pending = loadYield(state.campaign_id);
|
|
601
|
+
if (!pending && state.status !== "yielded") {
|
|
602
|
+
fail("No pending yield to acknowledge", EXIT.runtime_error);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
clearYield(state.campaign_id);
|
|
606
|
+
state.status = "running";
|
|
607
|
+
state.yield = null;
|
|
608
|
+
state.tick_count += 1;
|
|
609
|
+
|
|
610
|
+
switch (ackType) {
|
|
611
|
+
case "check_swarm": {
|
|
612
|
+
const check = checkSwarmRawReady(state.campaign_id, state.iteration);
|
|
613
|
+
if (!check.ok) fail(`check_swarm ack failed: ${check.reason}`, EXIT.runtime_error);
|
|
614
|
+
state.substep = "merge";
|
|
615
|
+
break;
|
|
616
|
+
}
|
|
617
|
+
case "dispatch_queue":
|
|
618
|
+
state.substep = null;
|
|
619
|
+
break;
|
|
620
|
+
case "execute_wave":
|
|
621
|
+
state.substep = "wave_verify";
|
|
622
|
+
break;
|
|
623
|
+
case "remediator":
|
|
624
|
+
state.attempt += 1;
|
|
625
|
+
if (state.phase === "execute") state.substep = "wave_verify";
|
|
626
|
+
break;
|
|
627
|
+
case "report": {
|
|
628
|
+
const adv = advancePhase(args.runId, "report", { force: true });
|
|
629
|
+
if (!adv.ok) fail(`advance report failed: ${adv.stderr}`, EXIT.runtime_error);
|
|
630
|
+
state.status = "complete";
|
|
631
|
+
state.phase = "report";
|
|
632
|
+
saveRunnerState(state);
|
|
633
|
+
emitResult(state, { action: "complete" });
|
|
634
|
+
process.exit(EXIT.complete);
|
|
635
|
+
}
|
|
636
|
+
default:
|
|
637
|
+
fail(`Unknown ack type: ${ackType}`, EXIT.runtime_error);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
saveRunnerState(state);
|
|
641
|
+
emitResult(state, { action: "ack", ack_type: ackType });
|
|
642
|
+
process.exit(EXIT.progressed);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
function runTick(state, campaign, manifest, args) {
|
|
646
|
+
markProgress(state, campaign);
|
|
647
|
+
state.tick_count += 1;
|
|
648
|
+
|
|
649
|
+
const manifestPhase = state.phase;
|
|
650
|
+
|
|
651
|
+
switch (manifestPhase) {
|
|
652
|
+
case "campaign_init":
|
|
653
|
+
return handleCampaignInit(state, campaign, manifest, args);
|
|
654
|
+
case "scan":
|
|
655
|
+
return handleScan(state, campaign, args);
|
|
656
|
+
case "check_swarm":
|
|
657
|
+
return handleCheckSwarm(state, args);
|
|
658
|
+
case "plan_waves":
|
|
659
|
+
return handlePlanWaves(state, campaign, args);
|
|
660
|
+
case "execute":
|
|
661
|
+
return handleExecute(state, campaign, args);
|
|
662
|
+
case "debt_sweep":
|
|
663
|
+
return handleDebtSweep(state, args);
|
|
664
|
+
case "satisfaction_gate":
|
|
665
|
+
return handleSatisfactionGate(state, campaign, args);
|
|
666
|
+
case "report":
|
|
667
|
+
handleReport(state, args);
|
|
668
|
+
return EXIT.yield_agent;
|
|
669
|
+
default:
|
|
670
|
+
fail(`Unknown phase: ${state.phase}`, EXIT.runtime_error);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
function printStatus(state, campaign) {
|
|
675
|
+
const payload = {
|
|
676
|
+
runner_state_path: runnerStatePath(state.campaign_id),
|
|
677
|
+
yield_path: yieldArtifactPath(state.campaign_id),
|
|
678
|
+
campaign_status: campaign.status,
|
|
679
|
+
config: campaign.config,
|
|
680
|
+
current: campaign.current,
|
|
681
|
+
runner: state,
|
|
682
|
+
yield: loadYield(state.campaign_id),
|
|
683
|
+
};
|
|
684
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
const args = parseArgs(process.argv.slice(2));
|
|
688
|
+
if (!args.runId || !args.campaignId) {
|
|
689
|
+
console.error(
|
|
690
|
+
"Usage: remediation-runner.mjs --run-id <id> --campaign-id <id> [--tick|--until-yield|--ack-yield <type>|--status]",
|
|
691
|
+
);
|
|
692
|
+
process.exit(2);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
const campaign = loadCampaign(args.campaignId);
|
|
696
|
+
if (!campaign) fail(`Campaign not found: ${args.campaignId}`, EXIT.runtime_error);
|
|
697
|
+
|
|
698
|
+
const manifest = loadManifest(args.runId);
|
|
699
|
+
const state = ensureRunnerState(args, campaign, manifest);
|
|
700
|
+
|
|
701
|
+
if (args.mode === "status") {
|
|
702
|
+
printStatus(state, campaign);
|
|
703
|
+
process.exit(0);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
if (args.mode === "ack-yield") {
|
|
707
|
+
if (!args.ackType) fail("--ack-yield requires a type", EXIT.runtime_error);
|
|
708
|
+
ackYield(state, args, args.ackType);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
if (args.mode === "tick") {
|
|
712
|
+
const code = runTick(state, campaign, manifest, args);
|
|
713
|
+
emitResult(loadRunnerState(args.campaignId), { action: "tick" });
|
|
714
|
+
process.exit(code === EXIT.yield_agent ? EXIT.yield_agent : code);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
if (args.mode === "until-yield") {
|
|
718
|
+
const maxTicks = 500;
|
|
719
|
+
for (let i = 0; i < maxTicks; i++) {
|
|
720
|
+
const freshCampaign = loadCampaign(args.campaignId);
|
|
721
|
+
const freshManifest = loadManifest(args.runId);
|
|
722
|
+
const freshState = ensureRunnerState(args, freshCampaign, freshManifest);
|
|
723
|
+
if (freshState.status === "complete") {
|
|
724
|
+
emitResult(freshState, { action: "complete" });
|
|
725
|
+
process.exit(EXIT.complete);
|
|
726
|
+
}
|
|
727
|
+
if (freshState.status === "blocked") {
|
|
728
|
+
emitResult(freshState, { action: "blocked" });
|
|
729
|
+
process.exit(EXIT.blocked);
|
|
730
|
+
}
|
|
731
|
+
if (freshState.status === "yielded") {
|
|
732
|
+
emitResult(freshState, { action: "yield" });
|
|
733
|
+
process.exit(EXIT.yield_agent);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
try {
|
|
737
|
+
const code = runTick(freshState, freshCampaign, freshManifest, args);
|
|
738
|
+
if (code === EXIT.yield_agent) process.exit(EXIT.yield_agent);
|
|
739
|
+
if (code === EXIT.complete) process.exit(EXIT.complete);
|
|
740
|
+
if (code === EXIT.blocked) process.exit(EXIT.blocked);
|
|
741
|
+
} catch (err) {
|
|
742
|
+
fail(String(err?.message ?? err), EXIT.runtime_error);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
fail(`until-yield exceeded ${maxTicks} ticks`, EXIT.blocked);
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
fail(`Unknown mode: ${args.mode}`, EXIT.runtime_error);
|