@ludecker/aaac 1.0.0 → 1.1.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.
Files changed (92) hide show
  1. package/README.md +4 -3
  2. package/package.json +13 -1
  3. package/src/cli.mjs +39 -5
  4. package/src/generators/generate-commands.mjs +120 -3
  5. package/src/generators/generate-graph.mjs +17 -0
  6. package/src/lib/install.mjs +1 -0
  7. package/src/lib/run-engine-paths.mjs +33 -0
  8. package/src/run-engine/advance-phase.mjs +343 -0
  9. package/src/run-engine/capability-evidence.mjs +460 -0
  10. package/src/run-engine/debug-run.mjs +38 -0
  11. package/src/run-engine/gate-write.mjs +95 -0
  12. package/src/run-engine/init-run.mjs +215 -0
  13. package/src/run-engine/lib.mjs +141 -0
  14. package/src/run-engine/log-dump.mjs +76 -0
  15. package/src/run-engine/log-trace.mjs +18 -0
  16. package/src/run-engine/log.mjs +343 -0
  17. package/src/run-engine/record-task.mjs +56 -0
  18. package/src/run-engine/stop-check.mjs +55 -0
  19. package/src/run-engine/verify-website-build.mjs +148 -0
  20. package/templates/cursor/aaac/capabilities/promotion-rules.json +64 -0
  21. package/templates/cursor/aaac/capabilities/registry.json +17 -15
  22. package/templates/cursor/aaac/complexity.yaml +98 -0
  23. package/templates/cursor/aaac/contracts/commands/fix-bug.yaml +10 -3
  24. package/templates/cursor/aaac/contracts/commands/fix-module.yaml +41 -0
  25. package/templates/cursor/aaac/contracts/skills/investigation.yaml +22 -1
  26. package/templates/cursor/aaac/contracts/skills/planning.yaml +17 -0
  27. package/templates/cursor/aaac/contracts/skills/validation.yaml +9 -1
  28. package/templates/cursor/aaac/dispatch.md +31 -6
  29. package/templates/cursor/aaac/enforcement.json +25 -0
  30. package/templates/cursor/aaac/fitness-functions.yaml +8 -0
  31. package/templates/cursor/aaac/governance/gates.json +6 -2
  32. package/templates/cursor/aaac/graph.project.yaml +237 -5
  33. package/templates/cursor/aaac/layers.md +6 -1
  34. package/templates/cursor/aaac/lifecycle/lifecycle.json +41 -1
  35. package/templates/cursor/aaac/lifecycle/phases.json +1 -0
  36. package/templates/cursor/aaac/observability/telemetry.yaml +63 -0
  37. package/templates/cursor/aaac/observability/verb-debug.yaml +170 -0
  38. package/templates/cursor/aaac/ontology.json +10 -1
  39. package/templates/cursor/aaac/run/RUN.md +2 -0
  40. package/templates/cursor/aaac/run/schema.json +11 -0
  41. package/templates/cursor/aaac/scripts/generate-runtime-registry.mjs +115 -0
  42. package/templates/cursor/aaac/scripts/run-engine/advance-phase.mjs +343 -0
  43. package/templates/cursor/aaac/scripts/run-engine/capability-evidence.mjs +460 -0
  44. package/templates/cursor/aaac/scripts/run-engine/debug-run.mjs +38 -0
  45. package/templates/cursor/aaac/scripts/run-engine/gate-write.mjs +95 -0
  46. package/templates/cursor/aaac/scripts/run-engine/init-run.mjs +215 -0
  47. package/templates/cursor/aaac/scripts/run-engine/lib.mjs +141 -0
  48. package/templates/cursor/aaac/scripts/run-engine/log-dump.mjs +76 -0
  49. package/templates/cursor/aaac/scripts/run-engine/log-trace.mjs +18 -0
  50. package/templates/cursor/aaac/scripts/run-engine/log.mjs +343 -0
  51. package/templates/cursor/aaac/scripts/run-engine/record-task.mjs +56 -0
  52. package/templates/cursor/aaac/scripts/run-engine/stop-check.mjs +55 -0
  53. package/templates/cursor/aaac/scripts/run-engine/verify-website-build.mjs +148 -0
  54. package/templates/cursor/aaac/state/capability-stats.json +5 -0
  55. package/templates/cursor/agents/aaac-log-debug.md +72 -0
  56. package/templates/cursor/agents/fix-code-path.md +27 -0
  57. package/templates/cursor/agents/fix-hypothesis-validate.md +26 -0
  58. package/templates/cursor/agents/fix-inventory-confirm.md +22 -0
  59. package/templates/cursor/agents/fix-recent-changes.md +22 -0
  60. package/templates/cursor/agents/fix-regression-scope.md +27 -0
  61. package/templates/cursor/agents/fix-repro-verify.md +21 -0
  62. package/templates/cursor/agents/fix-repro.md +29 -0
  63. package/templates/cursor/agents/fix-runtime-evidence.md +22 -0
  64. package/templates/cursor/agents/fix-test-failures.md +23 -0
  65. package/templates/cursor/agents/playwright-check-run.md +44 -0
  66. package/templates/cursor/hooks/aaac-before-submit.sh +3 -0
  67. package/templates/cursor/hooks/aaac-pre-tool.sh +4 -0
  68. package/templates/cursor/hooks/aaac-stop.sh +3 -0
  69. package/templates/cursor/hooks/aaac-subagent-start.sh +3 -0
  70. package/templates/cursor/hooks.json +30 -0
  71. package/templates/cursor/policies/minimal-complexity.md +101 -0
  72. package/templates/cursor/rules/aaac-enforcement.mdc +42 -0
  73. package/templates/cursor/skills/shared/execution/SKILL.md +1 -1
  74. package/templates/cursor/skills/shared/fitness-functions/SKILL.md +23 -7
  75. package/templates/cursor/skills/shared/investigation/SKILL.md +91 -18
  76. package/templates/cursor/skills/shared/investigation/orchestrator/SKILL.md +12 -4
  77. package/templates/cursor/skills/shared/planning/SKILL.md +74 -8
  78. package/templates/cursor/skills/shared/platform-release/SKILL.md +22 -19
  79. package/templates/cursor/skills/shared/platform-release/orchestrator/contract.yaml +27 -7
  80. package/templates/cursor/skills/shared/reporting/SKILL.md +2 -1
  81. package/templates/cursor/skills/shared/root-cause/SKILL.md +14 -3
  82. package/templates/cursor/skills/shared/testing/SKILL.md +31 -5
  83. package/templates/cursor/skills/shared/validation/SKILL.md +48 -13
  84. package/templates/cursor/skills/shared/verbs/_dispatch-utils.md +20 -1
  85. package/templates/cursor/skills/shared/verbs/_lifecycle.md +3 -2
  86. package/templates/cursor/skills/shared/verbs/check/orchestrator/SKILL.md +4 -1
  87. package/templates/cursor/skills/shared/verbs/create/orchestrator/SKILL.md +2 -2
  88. package/templates/cursor/skills/shared/verbs/fix/orchestrator/SKILL.md +21 -11
  89. package/templates/cursor/skills/shared/verbs/fix/orchestrator/contract.yaml +19 -4
  90. package/templates/cursor/skills/shared/verbs/update/orchestrator/SKILL.md +2 -2
  91. package/templates/cursor/skills/shared/verification/SKILL.md +3 -0
  92. package/templates/docs/agentic_architecture.md +236 -53
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env node
2
+ /** Emit .cursor/aaac/runtime-registry.json from ontology + lifecycle — SSOT for hooks */
3
+ import fs from "fs";
4
+ import path from "path";
5
+ import { fileURLToPath } from "url";
6
+
7
+ const aaac = path.join(
8
+ path.dirname(fileURLToPath(import.meta.url)),
9
+ "..",
10
+ );
11
+
12
+ const ontology = JSON.parse(
13
+ fs.readFileSync(path.join(aaac, "ontology.json"), "utf8"),
14
+ );
15
+ const lifecycle = JSON.parse(
16
+ fs.readFileSync(path.join(aaac, "lifecycle/lifecycle.json"), "utf8"),
17
+ );
18
+ const phases = JSON.parse(
19
+ fs.readFileSync(path.join(aaac, "lifecycle/phases.json"), "utf8"),
20
+ );
21
+ const gates = JSON.parse(
22
+ fs.readFileSync(path.join(aaac, "governance/gates.json"), "utf8"),
23
+ );
24
+
25
+ function composeRuntimePhases(verbDef) {
26
+ const work = verbDef.work_phases ?? [];
27
+ if (!verbDef.gate_stack) return work;
28
+ const gatePhases = gates.stacks[verbDef.gate_stack] ?? [];
29
+ const executeIdx = work.indexOf("execute");
30
+ if (executeIdx === -1) {
31
+ const reportIdx = work.indexOf("report");
32
+ if (reportIdx <= 0) {
33
+ return work.length <= 1
34
+ ? [...work, ...gatePhases]
35
+ : [...work.slice(0, -1), ...gatePhases, work[work.length - 1]];
36
+ }
37
+ return [...work.slice(0, reportIdx), ...gatePhases, ...work.slice(reportIdx)];
38
+ }
39
+ return [...work.slice(0, executeIdx), ...gatePhases, ...work.slice(executeIdx)];
40
+ }
41
+
42
+ const gatePhases = {};
43
+ for (const [id, def] of Object.entries(phases.phases ?? {})) {
44
+ if (def.gate) gatePhases[id] = true;
45
+ }
46
+
47
+ const commands = {};
48
+ const aliases = { ...(ontology.command_aliases ?? {}) };
49
+
50
+ for (const [cmd, entry] of Object.entries(ontology.command_overrides ?? {})) {
51
+ if (entry.alias) continue;
52
+ let pending;
53
+ let gate_stack = null;
54
+ if (lifecycle.workflows?.[cmd]) {
55
+ pending = composeRuntimePhases(lifecycle.workflows[cmd]);
56
+ gate_stack = lifecycle.workflows[cmd].gate_stack ?? null;
57
+ } else {
58
+ const verb = cmd.split("-")[0];
59
+ const verbDef = lifecycle.verbs?.[verb];
60
+ if (verbDef) {
61
+ pending = composeRuntimePhases(verbDef);
62
+ gate_stack = verbDef.gate_stack ?? null;
63
+ }
64
+ }
65
+ if (!pending) continue;
66
+ commands[cmd] = {
67
+ verb: cmd.split("-")[0],
68
+ object: cmd.includes("-") ? cmd.split("-").slice(1).join("-") : null,
69
+ orchestrator: entry.orchestrator ?? entry.resolver ?? null,
70
+ resolver: entry.resolver ?? null,
71
+ pending,
72
+ gate_stack,
73
+ };
74
+ }
75
+
76
+ for (const verb of Object.keys(ontology.verbs ?? {})) {
77
+ for (const object of Object.keys(ontology.objects ?? {})) {
78
+ const cmd = `${verb}-${object}`;
79
+ if (commands[cmd]) continue;
80
+ if (
81
+ (ontology.invalid_pairs ?? []).some(([v, o]) => v === verb && o === object)
82
+ ) {
83
+ continue;
84
+ }
85
+ const verbDef = lifecycle.verbs?.[verb];
86
+ if (!verbDef) continue;
87
+ commands[cmd] = {
88
+ verb,
89
+ object,
90
+ orchestrator: verb === "check" ? "verb-check" : `verb-${verb}`,
91
+ pending: composeRuntimePhases(verbDef),
92
+ gate_stack: verbDef.gate_stack ?? null,
93
+ };
94
+ }
95
+ }
96
+
97
+ for (const [alias, canonical] of Object.entries(aliases)) {
98
+ if (commands[canonical] && !commands[alias]) {
99
+ commands[alias] = { ...commands[canonical], alias_of: canonical };
100
+ }
101
+ }
102
+
103
+ const out = {
104
+ version: 1,
105
+ generated_at: new Date().toISOString(),
106
+ aliases,
107
+ gate_phases: gatePhases,
108
+ commands,
109
+ };
110
+
111
+ fs.writeFileSync(
112
+ path.join(aaac, "runtime-registry.json"),
113
+ `${JSON.stringify(out, null, 2)}\n`,
114
+ );
115
+ console.log(`Wrote runtime-registry.json (${Object.keys(commands).length} commands)`);
@@ -0,0 +1,343 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Advance Run to next phase. Validates swarm counts + required artifacts.
4
+ * Usage: node advance-phase.mjs <run_id> <completed_phase> [--force]
5
+ */
6
+ import fs from "fs";
7
+ import path from "path";
8
+ import { spawnSync } from "child_process";
9
+ import { fileURLToPath } from "url";
10
+ import {
11
+ loadRegistry,
12
+ loadEnforcement,
13
+ loadRunManifest,
14
+ runDir,
15
+ isoNow,
16
+ phaseKind,
17
+ isEditPhase,
18
+ isGatePhase,
19
+ writeJson,
20
+ saveActiveRun,
21
+ } from "./lib.mjs";
22
+ import { recordLog } from "./log.mjs";
23
+ import {
24
+ processRunEvidence,
25
+ evaluateCapabilityRuntimePolicy,
26
+ resolveCapabilitiesWithRuntime,
27
+ loadObjectMaturity,
28
+ } from "./capability-evidence.mjs";
29
+
30
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
31
+
32
+ const runId = process.argv[2];
33
+ const completedPhase = process.argv[3];
34
+ const force = process.argv.includes("--force");
35
+
36
+ if (!runId || !completedPhase) {
37
+ console.error("Usage: advance-phase.mjs <run_id> <completed_phase> [--force]");
38
+ process.exit(1);
39
+ }
40
+
41
+ const registry = loadRegistry();
42
+ const enforcement = loadEnforcement();
43
+ const manifestPath = path.join(runDir(runId), "run.json");
44
+ const manifest = loadRunManifest(runId);
45
+
46
+ if (!manifest) {
47
+ console.error(`Run not found: ${runId}`);
48
+ process.exit(1);
49
+ }
50
+
51
+ if (manifest.phase !== completedPhase) {
52
+ console.error(
53
+ `Phase mismatch: current=${manifest.phase} completed=${completedPhase}`,
54
+ );
55
+ process.exit(1);
56
+ }
57
+
58
+ const minAgents =
59
+ completedPhase === "verify" &&
60
+ (enforcement.fix_commands?.includes(manifest.command) || manifest.verb === "fix")
61
+ ? enforcement.swarm_min_agents?.verify_fix
62
+ : enforcement.swarm_min_agents?.[completedPhase];
63
+ const launches = manifest.swarm?.task_launches_this_phase ?? 0;
64
+ if (minAgents && launches < minAgents && !force) {
65
+ recordLog(manifest, {
66
+ event: "gate_fail",
67
+ phase: completedPhase,
68
+ phase_kind: manifest.phase_kind,
69
+ detail: `swarm incomplete: ${launches}/${minAgents} agents`,
70
+ level: "warn",
71
+ });
72
+ manifest.updated_at = isoNow();
73
+ writeJson(manifestPath, manifest);
74
+ console.error(
75
+ `Swarm incomplete: phase ${completedPhase} requires ${minAgents} Task agents, got ${launches}. Launch parallel Task subagents first.`,
76
+ );
77
+ process.exit(2);
78
+ }
79
+
80
+ const verifyVerbs = enforcement.verify_verbs ?? ["create", "update", "fix"];
81
+ if (
82
+ completedPhase === "verify" &&
83
+ verifyVerbs.includes(manifest.verb) &&
84
+ !force
85
+ ) {
86
+ const verifyScript = path.join(__dirname, "verify-website-build.mjs");
87
+ const verifyRun = spawnSync("node", [verifyScript, "--run-id", runId], {
88
+ encoding: "utf8",
89
+ });
90
+ if (verifyRun.status !== 0) {
91
+ const detail =
92
+ verifyRun.stderr?.trim() ||
93
+ verifyRun.stdout?.trim() ||
94
+ "verify-website-build failed";
95
+ recordLog(manifest, {
96
+ event: "gate_fail",
97
+ phase: completedPhase,
98
+ phase_kind: manifest.phase_kind,
99
+ detail: `website verify failed: ${detail.slice(0, 500)}`,
100
+ level: "warn",
101
+ });
102
+ manifest.updated_at = isoNow();
103
+ writeJson(manifestPath, manifest);
104
+ console.error(
105
+ "Website verify failed (static assets + vite build). Fix errors, then re-run:\n" +
106
+ ` node .cursor/aaac/scripts/run-engine/verify-website-build.mjs --run-id ${runId}\n` +
107
+ detail,
108
+ );
109
+ process.exit(2);
110
+ }
111
+ recordLog(manifest, {
112
+ event: "verify_website_pass",
113
+ phase: completedPhase,
114
+ phase_kind: manifest.phase_kind,
115
+ detail: "static assets + vite build",
116
+ level: "info",
117
+ });
118
+ }
119
+
120
+ const requiredArtifacts = enforcement.phase_artifacts?.[completedPhase] ?? [];
121
+ for (const rel of requiredArtifacts) {
122
+ const artifactPath = path.join(runDir(runId), rel);
123
+ if (!fs.existsSync(artifactPath)) {
124
+ recordLog(manifest, {
125
+ event: "gate_fail",
126
+ phase: completedPhase,
127
+ phase_kind: manifest.phase_kind,
128
+ detail: `missing artifact: ${rel}`,
129
+ level: "warn",
130
+ });
131
+ manifest.updated_at = isoNow();
132
+ writeJson(manifestPath, manifest);
133
+ console.error(`Missing artifact: ${rel} (required before leaving ${completedPhase})`);
134
+ process.exit(2);
135
+ }
136
+ }
137
+
138
+ const now = isoNow();
139
+ const completedIsGate = isGatePhase(completedPhase, registry);
140
+
141
+ if (completedIsGate) {
142
+ recordLog(manifest, {
143
+ event: "gate_pass",
144
+ phase: completedPhase,
145
+ phase_kind: "gate",
146
+ detail: "gate phase completed",
147
+ level: "info",
148
+ });
149
+ manifest.gates = manifest.gates ?? { stack: null, results: {} };
150
+ manifest.gates.results[completedPhase] = "pass";
151
+ }
152
+
153
+ if (completedPhase === "execute") {
154
+ recordLog(manifest, {
155
+ event: "execute_complete",
156
+ phase: completedPhase,
157
+ phase_kind: "work",
158
+ detail: "execute phase completed",
159
+ level: "info",
160
+ });
161
+ }
162
+
163
+ manifest.completed.push(completedPhase);
164
+ recordLog(manifest, {
165
+ event: "phase_complete",
166
+ phase: completedPhase,
167
+ phase_kind: manifest.phase_kind,
168
+ detail: minAgents ? `swarm_count=${launches}` : "ok",
169
+ level: "info",
170
+ });
171
+
172
+ let nextPhase = manifest.pending.shift() ?? null;
173
+
174
+ if (nextPhase === "execute" && !force) {
175
+ const resolved =
176
+ manifest.capabilities_resolved &&
177
+ Object.keys(manifest.capabilities_resolved).length > 0
178
+ ? manifest.capabilities_resolved
179
+ : resolveCapabilitiesWithRuntime(manifest.object, manifest.verb);
180
+ const policy = evaluateCapabilityRuntimePolicy(resolved, {
181
+ object_maturity: loadObjectMaturity(manifest.object),
182
+ });
183
+ manifest.capability_runtime = policy;
184
+
185
+ const needsBlock =
186
+ policy.action === "block" ||
187
+ (policy.action === "require_approval" && !manifest.capability_runtime_approved);
188
+
189
+ if (needsBlock) {
190
+ manifest.pending.unshift(nextPhase);
191
+ nextPhase = null;
192
+ manifest.status = "blocked";
193
+ manifest.awaiting_approval = policy.action === "require_approval";
194
+ manifest.blocked_reason = policy.reasons.join("; ") || "capability runtime policy";
195
+ recordLog(manifest, {
196
+ event: "gate_fail",
197
+ phase: completedPhase,
198
+ phase_kind: manifest.phase_kind,
199
+ detail: `capability runtime ${policy.action}: ${manifest.blocked_reason}`,
200
+ level: "warn",
201
+ });
202
+ manifest.updated_at = isoNow();
203
+ writeJson(manifestPath, manifest);
204
+ saveActiveRun(manifest.conversation_id ?? null, {
205
+ run_id: runId,
206
+ conversation_id: manifest.conversation_id ?? null,
207
+ command: manifest.command,
208
+ phase: manifest.phase,
209
+ status: manifest.status,
210
+ task_launches_this_phase: 0,
211
+ edit_allowed: false,
212
+ started_at: manifest.created_at,
213
+ });
214
+ console.error(
215
+ `Capability runtime ${policy.action}: ${manifest.blocked_reason}. ` +
216
+ (policy.action === "require_approval"
217
+ ? "User must approve in chat; set capability_runtime_approved on Run and retry."
218
+ : "Cannot proceed to execute."),
219
+ );
220
+ process.exit(2);
221
+ }
222
+
223
+ if (policy.action === "warn") {
224
+ recordLog(manifest, {
225
+ event: "capability_runtime_warn",
226
+ phase: completedPhase,
227
+ phase_kind: manifest.phase_kind,
228
+ detail: policy.reasons.join("; "),
229
+ level: "warn",
230
+ });
231
+ }
232
+ }
233
+
234
+ if (!nextPhase) {
235
+ manifest.status = "completed";
236
+ manifest.phase = "report";
237
+ manifest.enforcement.edit_allowed = false;
238
+ recordLog(manifest, {
239
+ event: "run_completed",
240
+ phase: "report",
241
+ phase_kind: "work",
242
+ detail: "all phases completed",
243
+ level: "info",
244
+ });
245
+
246
+ try {
247
+ const evidenceResult = processRunEvidence(runId, { manifest, skipManifestWrite: true });
248
+ if (evidenceResult.ok && !evidenceResult.skipped) {
249
+ manifest.capability_evidence_processed = true;
250
+ manifest.capability_evidence_outcomes = evidenceResult.outcomes;
251
+ if (
252
+ !manifest.capabilities_resolved ||
253
+ !Object.keys(manifest.capabilities_resolved).length
254
+ ) {
255
+ manifest.capabilities_resolved = evidenceResult.resolved;
256
+ }
257
+ recordLog(manifest, {
258
+ event: "evidence_aggregated",
259
+ phase: "report",
260
+ phase_kind: "work",
261
+ detail: `capabilities=${(evidenceResult.capabilities ?? []).join(",")}`,
262
+ level: "info",
263
+ });
264
+ for (const outcome of evidenceResult.outcomes ?? []) {
265
+ if (outcome.previous_state !== outcome.new_state) {
266
+ recordLog(manifest, {
267
+ event: "capability_promoted",
268
+ phase: "report",
269
+ phase_kind: "work",
270
+ detail: `${outcome.capability_id}:${outcome.previous_state}→${outcome.new_state}`,
271
+ level: "info",
272
+ });
273
+ }
274
+ }
275
+ }
276
+ } catch (err) {
277
+ recordLog(manifest, {
278
+ event: "evidence_aggregation_failed",
279
+ phase: "report",
280
+ phase_kind: "work",
281
+ detail: String(err.message ?? err).slice(0, 300),
282
+ level: "warn",
283
+ });
284
+ }
285
+ } else {
286
+ manifest.phase = nextPhase;
287
+ manifest.phase_kind = phaseKind(nextPhase, registry);
288
+ manifest.swarm = { task_launches_this_phase: 0, phase: nextPhase };
289
+ manifest.enforcement.edit_allowed = isEditPhase(nextPhase, enforcement);
290
+
291
+ recordLog(manifest, {
292
+ event: "phase_start",
293
+ phase: nextPhase,
294
+ phase_kind: manifest.phase_kind,
295
+ detail: "advanced",
296
+ level: "info",
297
+ });
298
+
299
+ if (nextPhase === "execute") {
300
+ recordLog(manifest, {
301
+ event: "execute_start",
302
+ phase: nextPhase,
303
+ phase_kind: "work",
304
+ detail: "edit phase unlocked",
305
+ level: "info",
306
+ });
307
+ }
308
+
309
+ if (isGatePhase(nextPhase, registry)) {
310
+ recordLog(manifest, {
311
+ event: "gate_blocked",
312
+ phase: nextPhase,
313
+ phase_kind: "gate",
314
+ detail: "awaiting gate evaluation",
315
+ level: "debug",
316
+ });
317
+ }
318
+ }
319
+
320
+ manifest.updated_at = now;
321
+ writeJson(manifestPath, manifest);
322
+
323
+ saveActiveRun(manifest.conversation_id ?? null, {
324
+ run_id: runId,
325
+ conversation_id: manifest.conversation_id ?? null,
326
+ command: manifest.command,
327
+ phase: manifest.phase,
328
+ status: manifest.status,
329
+ task_launches_this_phase: 0,
330
+ edit_allowed: manifest.enforcement.edit_allowed,
331
+ started_at: manifest.created_at,
332
+ });
333
+
334
+ console.log(
335
+ JSON.stringify({
336
+ ok: true,
337
+ run_id: runId,
338
+ completed: completedPhase,
339
+ phase: manifest.phase,
340
+ status: manifest.status,
341
+ edit_allowed: manifest.enforcement.edit_allowed,
342
+ }),
343
+ );