@ludecker/aaac 1.0.0 → 1.1.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.
Files changed (82) 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 +192 -0
  9. package/src/run-engine/debug-run.mjs +38 -0
  10. package/src/run-engine/gate-write.mjs +95 -0
  11. package/src/run-engine/init-run.mjs +165 -0
  12. package/src/run-engine/lib.mjs +136 -0
  13. package/src/run-engine/log-dump.mjs +76 -0
  14. package/src/run-engine/log-trace.mjs +18 -0
  15. package/src/run-engine/log.mjs +343 -0
  16. package/src/run-engine/record-task.mjs +56 -0
  17. package/src/run-engine/stop-check.mjs +55 -0
  18. package/templates/cursor/aaac/complexity.yaml +98 -0
  19. package/templates/cursor/aaac/contracts/commands/fix-bug.yaml +10 -3
  20. package/templates/cursor/aaac/contracts/commands/fix-module.yaml +41 -0
  21. package/templates/cursor/aaac/contracts/skills/investigation.yaml +22 -1
  22. package/templates/cursor/aaac/contracts/skills/planning.yaml +17 -0
  23. package/templates/cursor/aaac/contracts/skills/validation.yaml +9 -1
  24. package/templates/cursor/aaac/dispatch.md +30 -5
  25. package/templates/cursor/aaac/enforcement.json +22 -0
  26. package/templates/cursor/aaac/fitness-functions.yaml +8 -0
  27. package/templates/cursor/aaac/governance/gates.json +3 -1
  28. package/templates/cursor/aaac/graph.project.yaml +237 -5
  29. package/templates/cursor/aaac/layers.md +3 -1
  30. package/templates/cursor/aaac/lifecycle/lifecycle.json +41 -1
  31. package/templates/cursor/aaac/lifecycle/phases.json +1 -0
  32. package/templates/cursor/aaac/observability/telemetry.yaml +60 -0
  33. package/templates/cursor/aaac/observability/verb-debug.yaml +170 -0
  34. package/templates/cursor/aaac/ontology.json +10 -1
  35. package/templates/cursor/aaac/run/RUN.md +2 -0
  36. package/templates/cursor/aaac/run/schema.json +9 -0
  37. package/templates/cursor/aaac/scripts/generate-runtime-registry.mjs +115 -0
  38. package/templates/cursor/aaac/scripts/run-engine/advance-phase.mjs +192 -0
  39. package/templates/cursor/aaac/scripts/run-engine/debug-run.mjs +38 -0
  40. package/templates/cursor/aaac/scripts/run-engine/gate-write.mjs +95 -0
  41. package/templates/cursor/aaac/scripts/run-engine/init-run.mjs +165 -0
  42. package/templates/cursor/aaac/scripts/run-engine/lib.mjs +136 -0
  43. package/templates/cursor/aaac/scripts/run-engine/log-dump.mjs +76 -0
  44. package/templates/cursor/aaac/scripts/run-engine/log-trace.mjs +18 -0
  45. package/templates/cursor/aaac/scripts/run-engine/log.mjs +343 -0
  46. package/templates/cursor/aaac/scripts/run-engine/record-task.mjs +56 -0
  47. package/templates/cursor/aaac/scripts/run-engine/stop-check.mjs +55 -0
  48. package/templates/cursor/agents/aaac-log-debug.md +72 -0
  49. package/templates/cursor/agents/fix-code-path.md +27 -0
  50. package/templates/cursor/agents/fix-hypothesis-validate.md +26 -0
  51. package/templates/cursor/agents/fix-inventory-confirm.md +22 -0
  52. package/templates/cursor/agents/fix-recent-changes.md +22 -0
  53. package/templates/cursor/agents/fix-regression-scope.md +27 -0
  54. package/templates/cursor/agents/fix-repro-verify.md +21 -0
  55. package/templates/cursor/agents/fix-repro.md +29 -0
  56. package/templates/cursor/agents/fix-runtime-evidence.md +22 -0
  57. package/templates/cursor/agents/fix-test-failures.md +23 -0
  58. package/templates/cursor/agents/playwright-check-run.md +44 -0
  59. package/templates/cursor/hooks/aaac-before-submit.sh +3 -0
  60. package/templates/cursor/hooks/aaac-pre-tool.sh +4 -0
  61. package/templates/cursor/hooks/aaac-stop.sh +3 -0
  62. package/templates/cursor/hooks/aaac-subagent-start.sh +3 -0
  63. package/templates/cursor/hooks.json +30 -0
  64. package/templates/cursor/policies/minimal-complexity.md +101 -0
  65. package/templates/cursor/rules/aaac-enforcement.mdc +42 -0
  66. package/templates/cursor/skills/shared/execution/SKILL.md +1 -1
  67. package/templates/cursor/skills/shared/fitness-functions/SKILL.md +23 -7
  68. package/templates/cursor/skills/shared/investigation/SKILL.md +91 -18
  69. package/templates/cursor/skills/shared/investigation/orchestrator/SKILL.md +12 -4
  70. package/templates/cursor/skills/shared/planning/SKILL.md +74 -8
  71. package/templates/cursor/skills/shared/reporting/SKILL.md +2 -1
  72. package/templates/cursor/skills/shared/root-cause/SKILL.md +14 -3
  73. package/templates/cursor/skills/shared/testing/SKILL.md +26 -5
  74. package/templates/cursor/skills/shared/validation/SKILL.md +48 -13
  75. package/templates/cursor/skills/shared/verbs/_dispatch-utils.md +20 -1
  76. package/templates/cursor/skills/shared/verbs/_lifecycle.md +3 -2
  77. package/templates/cursor/skills/shared/verbs/check/orchestrator/SKILL.md +4 -1
  78. package/templates/cursor/skills/shared/verbs/create/orchestrator/SKILL.md +2 -2
  79. package/templates/cursor/skills/shared/verbs/fix/orchestrator/SKILL.md +21 -11
  80. package/templates/cursor/skills/shared/verbs/fix/orchestrator/contract.yaml +19 -4
  81. package/templates/cursor/skills/shared/verbs/update/orchestrator/SKILL.md +2 -2
  82. package/templates/cursor/skills/shared/verification/SKILL.md +2 -0
@@ -0,0 +1,343 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * AAAC Run manifest logging — SSOT: observability/telemetry.yaml
4
+ */
5
+ import fs from "fs";
6
+ import path from "path";
7
+ import { fileURLToPath } from "url";
8
+ import { isoNow } from "./lib.mjs";
9
+
10
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
+ const AAAC_ROOT = path.resolve(__dirname, "../..");
12
+
13
+ export const LOG_LEVEL_PRIORITY = {
14
+ debug: 0,
15
+ info: 1,
16
+ warn: 2,
17
+ error: 3,
18
+ };
19
+
20
+ const VALID_LEVELS = new Set(Object.keys(LOG_LEVEL_PRIORITY));
21
+
22
+ export function normalizeLevel(level) {
23
+ const normalized = (level ?? "info").toLowerCase();
24
+ return VALID_LEVELS.has(normalized) ? normalized : "info";
25
+ }
26
+
27
+ export function getLogLevel() {
28
+ return normalizeLevel(process.env.LOG_LEVEL);
29
+ }
30
+
31
+ export function shouldLog(level) {
32
+ return LOG_LEVEL_PRIORITY[normalizeLevel(level)] >= LOG_LEVEL_PRIORITY[getLogLevel()];
33
+ }
34
+
35
+ export function recordLog(manifest, opts) {
36
+ const level = normalizeLevel(opts.level ?? "info");
37
+ const entry = {
38
+ at: isoNow(),
39
+ run_id: manifest.run_id,
40
+ phase: opts.phase ?? manifest.phase ?? "dispatch",
41
+ phase_kind: opts.phase_kind ?? manifest.phase_kind ?? "work",
42
+ skill: opts.skill ?? null,
43
+ event: opts.event,
44
+ detail: opts.detail ?? "",
45
+ level,
46
+ };
47
+
48
+ manifest.log = manifest.log ?? [];
49
+ manifest.log.push(entry);
50
+
51
+ if (shouldLog(level)) {
52
+ debugPrint(entry);
53
+ }
54
+
55
+ return entry;
56
+ }
57
+
58
+ export function recordDecision(manifest, opts) {
59
+ const entry = {
60
+ at: isoNow(),
61
+ phase: opts.phase ?? manifest.phase ?? "dispatch",
62
+ decision: opts.decision,
63
+ reason: opts.reason,
64
+ evidence: opts.evidence ?? "",
65
+ };
66
+
67
+ manifest.decisions = manifest.decisions ?? [];
68
+ manifest.decisions.push(entry);
69
+
70
+ if (shouldLog("debug")) {
71
+ debugPrint({
72
+ level: "debug",
73
+ run_id: manifest.run_id,
74
+ phase: entry.phase,
75
+ event: "decision",
76
+ detail: entry.decision,
77
+ reason: entry.reason,
78
+ evidence: entry.evidence,
79
+ });
80
+ }
81
+
82
+ return entry;
83
+ }
84
+
85
+ export function debugPrint(entry) {
86
+ const level = normalizeLevel(entry.level ?? "info");
87
+ const phase = entry.phase ?? "—";
88
+ const event = entry.event ?? entry.decision ?? "—";
89
+ const detail = entry.detail ?? entry.reason ?? "";
90
+ const context = {};
91
+
92
+ if (entry.run_id) context.run_id = entry.run_id;
93
+ if (entry.skill) context.skill = entry.skill;
94
+ if (entry.phase_kind) context.phase_kind = entry.phase_kind;
95
+ if (entry.reason && entry.event === "decision") context.reason = entry.reason;
96
+ if (entry.evidence) context.evidence = entry.evidence;
97
+
98
+ const ctxStr = Object.keys(context).length ? ` ${JSON.stringify(context)}` : "";
99
+ process.stderr.write(`[${level}] [run:${phase}:${event}] ${detail}${ctxStr}\n`);
100
+ }
101
+
102
+ export function filterLogByLevel(entries, minLevel) {
103
+ const floor = LOG_LEVEL_PRIORITY[normalizeLevel(minLevel)];
104
+ return (entries ?? []).filter((e) => {
105
+ const entryLevel = e.level ? normalizeLevel(e.level) : "info";
106
+ return LOG_LEVEL_PRIORITY[entryLevel] >= floor;
107
+ });
108
+ }
109
+
110
+ export function loadVerbDebugProfiles() {
111
+ const profilePath = path.join(AAAC_ROOT, "observability", "verb-debug.yaml");
112
+ try {
113
+ const raw = fs.readFileSync(profilePath, "utf8");
114
+ return parseVerbDebugYaml(raw);
115
+ } catch {
116
+ return {};
117
+ }
118
+ }
119
+
120
+ function parseVerbDebugYaml(raw) {
121
+ const profiles = {};
122
+ let current = null;
123
+ let section = null;
124
+ let eventPhase = null;
125
+
126
+ for (const line of raw.split("\n")) {
127
+ const trimmed = line.trim();
128
+ if (!trimmed || trimmed.startsWith("#")) continue;
129
+
130
+ const profileMatch = trimmed.match(/^([a-z_]+):\s*$/);
131
+ if (profileMatch && !line.startsWith(" ")) {
132
+ current = profileMatch[1];
133
+ profiles[current] = { phases: [], swarm_minimums: {}, description: "" };
134
+ section = null;
135
+ eventPhase = null;
136
+ continue;
137
+ }
138
+
139
+ if (!current) continue;
140
+
141
+ if (trimmed === "phases:") {
142
+ section = "phases";
143
+ continue;
144
+ }
145
+ if (trimmed === "expected_events:") {
146
+ section = "events";
147
+ continue;
148
+ }
149
+ if (trimmed === "swarm_minimums:") {
150
+ section = "swarm_minimums";
151
+ continue;
152
+ }
153
+ if (trimmed.startsWith("description:")) {
154
+ profiles[current].description = trimmed.slice("description:".length).trim();
155
+ section = null;
156
+ continue;
157
+ }
158
+
159
+ const listItem = trimmed.match(/^- (.+)$/);
160
+ if (listItem && section === "phases") {
161
+ profiles[current].phases.push(listItem[1]);
162
+ continue;
163
+ }
164
+
165
+ const phaseKey = trimmed.match(/^([a-z_]+):\s*$/);
166
+ if (phaseKey && section === "events") {
167
+ eventPhase = phaseKey[1];
168
+ profiles[current].expected_events = profiles[current].expected_events ?? {};
169
+ profiles[current].expected_events[eventPhase] = [];
170
+ continue;
171
+ }
172
+
173
+ if (listItem && section === "events" && eventPhase) {
174
+ profiles[current].expected_events[eventPhase].push(listItem[1]);
175
+ continue;
176
+ }
177
+
178
+ const swarmEntry = trimmed.match(/^([a-z_]+):\s*(\d+)$/);
179
+ if (swarmEntry && section === "swarm_minimums") {
180
+ profiles[current].swarm_minimums[swarmEntry[1]] = Number(swarmEntry[2]);
181
+ }
182
+ }
183
+
184
+ return profiles;
185
+ }
186
+
187
+ export function swarmCountForPhase(log, phase) {
188
+ const entries = (log ?? []).filter((e) => e.phase === phase);
189
+ const launches = entries.filter(
190
+ (e) => e.event === "agent_spawned" || e.event === "task_launch",
191
+ );
192
+ if (launches.length) {
193
+ const last = launches[launches.length - 1];
194
+ const match = String(last.detail ?? "").match(/count=(\d+)/);
195
+ return match ? Number(match[1]) : launches.length;
196
+ }
197
+ const complete = entries.find((e) => e.event === "phase_complete");
198
+ if (complete) {
199
+ const swarmMatch = String(complete.detail ?? "").match(/swarm_count=(\d+)/);
200
+ if (swarmMatch) return Number(swarmMatch[1]);
201
+ }
202
+ return 0;
203
+ }
204
+
205
+ export function formatTimeline(manifest, { minLevel = "debug" } = {}) {
206
+ const lines = [];
207
+ const logs = filterLogByLevel(manifest.log ?? [], minLevel);
208
+ const decisions = manifest.decisions ?? [];
209
+
210
+ const merged = [
211
+ ...logs.map((e) => ({ ...e, kind: "log" })),
212
+ ...decisions.map((e) => ({
213
+ at: e.at,
214
+ phase: e.phase,
215
+ event: `decision:${e.decision}`,
216
+ detail: e.reason,
217
+ level: "info",
218
+ kind: "decision",
219
+ evidence: e.evidence,
220
+ })),
221
+ ].sort((a, b) => new Date(a.at) - new Date(b.at));
222
+
223
+ for (const entry of merged) {
224
+ const level = entry.level ?? "info";
225
+ const skill = entry.skill ? ` skill=${entry.skill}` : "";
226
+ const evidence = entry.evidence ? ` evidence="${entry.evidence}"` : "";
227
+ lines.push(
228
+ `${entry.at} [${level}] ${entry.phase} :: ${entry.event}${skill} — ${entry.detail}${evidence}`,
229
+ );
230
+ }
231
+
232
+ return lines.join("\n");
233
+ }
234
+
235
+ export function buildTrace(manifest) {
236
+ const verb = manifest.verb ?? "unknown";
237
+ const profiles = loadVerbDebugProfiles();
238
+ const profile = profiles[verb] ?? null;
239
+ const log = manifest.log ?? [];
240
+ const decisions = manifest.decisions ?? [];
241
+ const sections = [];
242
+
243
+ sections.push(`# AAAC trace: ${manifest.run_id}`);
244
+ sections.push(`Command: /${manifest.command} Verb: ${verb} Status: ${manifest.status}`);
245
+ if (manifest.blocked_reason) sections.push(`Blocked: ${manifest.blocked_reason}`);
246
+ sections.push("");
247
+
248
+ sections.push("## Why did it do this?");
249
+ for (const d of decisions) {
250
+ sections.push(`- [${d.phase}] ${d.decision}: ${d.reason}`);
251
+ if (d.evidence) sections.push(` evidence: ${d.evidence}`);
252
+ }
253
+ if (!decisions.length) sections.push("- (no decisions recorded)");
254
+ sections.push("");
255
+
256
+ sections.push("## Which skill ran?");
257
+ const skillEvents = log.filter((e) => e.skill || e.event === "skill_loaded");
258
+ if (skillEvents.length) {
259
+ for (const e of skillEvents) {
260
+ sections.push(`- ${e.at} ${e.phase}: ${e.skill ?? e.detail}`);
261
+ }
262
+ } else {
263
+ sections.push("- (no skill_loaded events — infer from phase transitions)");
264
+ for (const e of log.filter((x) => x.event === "phase_start")) {
265
+ sections.push(`- phase ${e.phase} started`);
266
+ }
267
+ }
268
+ sections.push("");
269
+
270
+ sections.push("## Why was a route chosen?");
271
+ const routing = decisions.filter((d) =>
272
+ /route|orchestrator|capability|graph|dispatch/i.test(`${d.decision} ${d.reason}`),
273
+ );
274
+ if (routing.length) {
275
+ for (const d of routing) sections.push(`- ${d.decision}: ${d.reason}`);
276
+ } else if (manifest.orchestrator) {
277
+ sections.push(`- orchestrator: ${manifest.orchestrator}`);
278
+ } else {
279
+ sections.push("- (see decisions and command registry entry)");
280
+ }
281
+ sections.push("");
282
+
283
+ sections.push("## Why is the run blocked?");
284
+ if (manifest.status === "blocked" || manifest.awaiting_approval) {
285
+ sections.push(`- status=${manifest.status} awaiting_approval=${manifest.awaiting_approval}`);
286
+ sections.push(`- ${manifest.blocked_reason ?? "gate or swarm incomplete"}`);
287
+ for (const e of log.filter((x) => x.event === "edit_denied" || x.event === "gate_fail").slice(-5)) {
288
+ sections.push(`- ${e.at} ${e.event}: ${e.detail}`);
289
+ }
290
+ } else if (manifest.status === "running") {
291
+ sections.push(`- Not blocked. Current phase: ${manifest.phase}`);
292
+ const minAgents = profile?.swarm_minimums?.[manifest.phase];
293
+ if (minAgents) {
294
+ const count = manifest.swarm?.task_launches_this_phase ?? swarmCountForPhase(log, manifest.phase);
295
+ sections.push(`- Swarm: ${count}/${minAgents} agents this phase`);
296
+ }
297
+ } else {
298
+ sections.push(`- Run ${manifest.status}`);
299
+ }
300
+ sections.push("");
301
+
302
+ if (profile) {
303
+ sections.push(`## Verb profile (${verb})`);
304
+ if (profile.description) sections.push(profile.description);
305
+ sections.push(`Highlight phases: ${(profile.phases ?? []).join(" → ")}`);
306
+ for (const [phase, min] of Object.entries(profile.swarm_minimums ?? {})) {
307
+ const actual = swarmCountForPhase(log, phase);
308
+ sections.push(`- ${phase}: swarm ${actual}/${min} (${actual >= min ? "ok" : "INCOMPLETE"})`);
309
+ }
310
+ }
311
+
312
+ sections.push("");
313
+ sections.push("## Chronological timeline");
314
+ sections.push(formatTimeline(manifest));
315
+
316
+ return sections.join("\n");
317
+ }
318
+
319
+ export function debugRunSummary(manifest) {
320
+ const log = manifest.log ?? [];
321
+ const phase = manifest.phase;
322
+ const swarmPhase = manifest.swarm?.phase ?? phase;
323
+ const swarmCount =
324
+ manifest.swarm?.task_launches_this_phase ?? swarmCountForPhase(log, swarmPhase);
325
+
326
+ return {
327
+ run_id: manifest.run_id,
328
+ command: manifest.command,
329
+ verb: manifest.verb,
330
+ status: manifest.status,
331
+ phase,
332
+ phase_kind: manifest.phase_kind,
333
+ blocked_reason: manifest.blocked_reason,
334
+ awaiting_approval: manifest.awaiting_approval,
335
+ completed: manifest.completed ?? [],
336
+ pending: manifest.pending ?? [],
337
+ swarm: { phase: swarmPhase, task_launches_this_phase: swarmCount },
338
+ edit_allowed: manifest.enforcement?.edit_allowed ?? false,
339
+ last_log_entries: log.slice(-10),
340
+ decisions_count: (manifest.decisions ?? []).length,
341
+ log_count: log.length,
342
+ };
343
+ }
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+ import path from "path";
3
+ import {
4
+ loadActiveRun,
5
+ loadRunManifest,
6
+ runDir,
7
+ writeJson,
8
+ saveActiveRun,
9
+ isoNow,
10
+ conversationIdFromHook,
11
+ } from "./lib.mjs";
12
+ import { recordLog } from "./log.mjs";
13
+
14
+ let input = "";
15
+ process.stdin.setEncoding("utf8");
16
+ process.stdin.on("data", (c) => (input += c));
17
+ process.stdin.on("end", () => {
18
+ const allow = () => {
19
+ console.log(JSON.stringify({ permission: "allow" }));
20
+ process.exit(0);
21
+ };
22
+
23
+ let hook;
24
+ try {
25
+ hook = JSON.parse(input || "{}");
26
+ } catch {
27
+ allow();
28
+ }
29
+
30
+ const conversationId = conversationIdFromHook(hook);
31
+ if (!conversationId) allow();
32
+
33
+ const active = loadActiveRun(conversationId);
34
+ if (!active?.run_id) allow();
35
+
36
+ const manifest = loadRunManifest(active.run_id);
37
+ if (!manifest || manifest.status === "completed") allow();
38
+ if (manifest.conversation_id && manifest.conversation_id !== conversationId) allow();
39
+
40
+ manifest.swarm = manifest.swarm ?? {};
41
+ manifest.swarm.task_launches_this_phase = (manifest.swarm.task_launches_this_phase ?? 0) + 1;
42
+ manifest.swarm.phase = manifest.phase;
43
+ manifest.updated_at = isoNow();
44
+
45
+ recordLog(manifest, {
46
+ event: "agent_spawned",
47
+ phase: manifest.phase,
48
+ phase_kind: manifest.phase_kind,
49
+ detail: `count=${manifest.swarm.task_launches_this_phase}`,
50
+ level: "debug",
51
+ });
52
+
53
+ writeJson(path.join(runDir(active.run_id), "run.json"), manifest);
54
+ saveActiveRun(conversationId, { ...active, task_launches_this_phase: manifest.swarm.task_launches_this_phase });
55
+ allow();
56
+ });
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env node
2
+ import path from "path";
3
+ import {
4
+ loadActiveRun,
5
+ loadRunManifest,
6
+ conversationIdFromHook,
7
+ runDir,
8
+ writeJson,
9
+ isoNow,
10
+ } from "./lib.mjs";
11
+ import { recordLog } from "./log.mjs";
12
+
13
+ let input = "";
14
+ process.stdin.setEncoding("utf8");
15
+ process.stdin.on("data", (c) => (input += c));
16
+ process.stdin.on("end", () => {
17
+ let hook = {};
18
+ try {
19
+ hook = JSON.parse(input || "{}");
20
+ } catch {
21
+ process.exit(0);
22
+ }
23
+
24
+ const conversationId = conversationIdFromHook(hook);
25
+ if (!conversationId) process.exit(0);
26
+
27
+ const active = loadActiveRun(conversationId);
28
+ if (!active?.run_id) process.exit(0);
29
+
30
+ const manifest = loadRunManifest(active.run_id);
31
+ if (!manifest || manifest.status === "completed") process.exit(0);
32
+
33
+ const remaining = [manifest.phase, ...(manifest.pending ?? [])].filter(Boolean);
34
+
35
+ recordLog(manifest, {
36
+ event: "run_incomplete",
37
+ phase: manifest.phase,
38
+ phase_kind: manifest.phase_kind,
39
+ detail: `stop hook: status=${manifest.status} remaining=${remaining.join("→")}`,
40
+ level: "warn",
41
+ });
42
+ manifest.updated_at = isoNow();
43
+ writeJson(path.join(runDir(active.run_id), "run.json"), manifest);
44
+
45
+ console.log(
46
+ JSON.stringify({
47
+ followup_message: [
48
+ `AAAC Run ${active.run_id} incomplete (this chat). Phase: ${manifest.phase}.`,
49
+ `Remaining: ${remaining.join(" → ")}`,
50
+ `Advance: node .cursor/aaac/scripts/run-engine/advance-phase.mjs ${active.run_id} ${manifest.phase}`,
51
+ `Debug: node .cursor/aaac/scripts/run-engine/debug-run.mjs ${active.run_id}`,
52
+ ].join("\n"),
53
+ }),
54
+ );
55
+ });
@@ -0,0 +1,72 @@
1
+ ---
2
+ name: aaac-log-debug
3
+ description: Debug blocked or failed AAAC create/update/fix/check Runs using manifest log tools.
4
+ ---
5
+
6
+ # AAAC Log Debug Agent
7
+
8
+ Use when a `/create-*`, `/update-*`, `/fix-*`, or `/check-*` Run is blocked, incomplete, or behaving unexpectedly.
9
+
10
+ ## SSOT
11
+
12
+ - Run manifest: `.cursor/aaac/state/runs/{run_id}/run.json`
13
+ - Telemetry events: `.cursor/aaac/observability/telemetry.yaml`
14
+ - Verb profiles: `.cursor/aaac/observability/verb-debug.yaml`
15
+
16
+ All observability lives on the Run manifest — never create standalone markdown debug logs.
17
+
18
+ ## Quick triage
19
+
20
+ 1. Find the run id from the chat hook message or `.cursor/aaac/state/active-runs/{conversation_id}.json`.
21
+ 2. One-shot status:
22
+
23
+ ```bash
24
+ node .cursor/aaac/scripts/run-engine/debug-run.mjs <run_id>
25
+ ```
26
+
27
+ 3. Full timeline:
28
+
29
+ ```bash
30
+ node .cursor/aaac/scripts/run-engine/log-dump.mjs <run_id> --format pretty
31
+ ```
32
+
33
+ 4. Answer "why did it do X?":
34
+
35
+ ```bash
36
+ node .cursor/aaac/scripts/run-engine/log-trace.mjs <run_id>
37
+ ```
38
+
39
+ Or via npm CLI when installed:
40
+
41
+ ```bash
42
+ aaac debug-run <run_id>
43
+ aaac log-dump <run_id> --format timeline
44
+ ```
45
+
46
+ ## What to look for
47
+
48
+ | Symptom | Log events | Action |
49
+ |---------|------------|--------|
50
+ | Edits denied | `edit_denied` | Advance to `execute` phase first |
51
+ | Swarm blocked | `gate_fail` + `swarm_count` | Launch more Task subagents; check verb-debug swarm_minimums |
52
+ | Missing artifact | `gate_fail` + `missing artifact` | Write required file under `artifacts/` |
53
+ | Stuck at gate | `gate_blocked`, `awaiting_approval` | Complete gate skill; user approval if blocked |
54
+ | Wrong route | `decisions[]`, `graph_resolved` | Check orchestrator in registry |
55
+
56
+ ## Verb-specific checks
57
+
58
+ - **create / update**: `discover` needs 4 agents; phases `investigate_lite` → `plan` → gates → `execute`.
59
+ - **fix**: `investigate_swarm` needs 7 agents; `verify` needs 3 (`verify_fix`); `root_cause` artifact required.
60
+ - **check** (readonly): `discover` needs 4 run-engine agents + 3 explore agents in `check_swarm`; no `execute` — `edit_denied` is expected for all code paths.
61
+
62
+ ## Environment
63
+
64
+ Set `LOG_LEVEL=debug` when running run-engine scripts locally for stderr structured output:
65
+
66
+ ```
67
+ [level] [run:phase:event] detail {"run_id":"..."}
68
+ ```
69
+
70
+ ## Report back
71
+
72
+ Summarize: run_id, phase, blocked_reason, swarm counts vs minimums, last 3 log events, and recommended next command (`advance-phase.mjs` or spawn agents).
@@ -0,0 +1,27 @@
1
+ # Agent: fix-code-path
2
+
3
+ **Readonly.** Do not edit files.
4
+
5
+ ## Role
6
+
7
+ Trace execution from symptom to the code that produces the behavior.
8
+
9
+ ## Inputs (from parent)
10
+
11
+ - Intent and repro steps from `fix-repro` when available
12
+ - Domain inventory file map
13
+
14
+ ## Procedure
15
+
16
+ 1. Start at user-visible surface (route, component, action, migration, API)
17
+ 2. Follow imports and call chain until data source or side effect
18
+ 3. Identify **suspect files** (max 10) with `path:line` anchors
19
+ 4. Note **branch points** (conditionals, env, auth, cache, ISR)
20
+ 5. Flag **layer violations** (Supabase in page, UI fetching, duplicate SSOT)
21
+
22
+ ## Return
23
+
24
+ - Execution trace (ordered bullets)
25
+ - Suspect files with evidence
26
+ - Layer or boundary issues if any
27
+ - Confidence: high | medium | low
@@ -0,0 +1,26 @@
1
+ # Agent: fix-hypothesis-validate
2
+
3
+ **Readonly.** Do not edit files.
4
+
5
+ ## Role
6
+
7
+ Challenge the proposed root cause before planning — second opinion when confidence is borderline.
8
+
9
+ ## Inputs
10
+
11
+ - Merged investigation + draft root_cause hypothesis
12
+ - Evidence from fix-code-path and fix-recent-changes
13
+
14
+ ## Procedure
15
+
16
+ 1. State alternative hypotheses (max 3)
17
+ 2. For each: evidence for / against
18
+ 3. Recommend **proceed** | **investigate_more** with specific next checks
19
+ 4. Score root_cause confidence 0.0–1.0
20
+
21
+ ## Return
22
+
23
+ - Alternative hypotheses ranked
24
+ - Recommended action: proceed | investigate_more
25
+ - root_cause_confidence: 0.0–1.0
26
+ - Missing evidence (if investigate_more)
@@ -0,0 +1,22 @@
1
+ # Agent: fix-inventory-confirm
2
+
3
+ **Readonly.** Do not edit files.
4
+
5
+ ## Role
6
+
7
+ Validate domain inventory against actual repo layout for the fix scope.
8
+
9
+ ## Procedure
10
+
11
+ 1. Read `domains/<slug>/update/inventory/SKILL.md` when domain is known
12
+ 2. Confirm file map entries exist; flag stale or missing paths
13
+ 3. Restate **in scope** and **out of scope** for this fix
14
+ 4. List inventory **Section 2 constraints** that apply to the symptom
15
+
16
+ ## Return
17
+
18
+ - Inventory freshness: current | stale | missing
19
+ - Confirmed scope boundaries
20
+ - Applicable constraints (bullets)
21
+ - Recommended domain slug if ambiguous
22
+ - Confidence: high | medium | low
@@ -0,0 +1,22 @@
1
+ # Agent: fix-recent-changes
2
+
3
+ **Readonly.** Do not edit files.
4
+
5
+ ## Role
6
+
7
+ Find recent changes that could have introduced the defect.
8
+
9
+ ## Procedure
10
+
11
+ 1. `git log --oneline -20` on suspect paths from inventory or code-path agent
12
+ 2. `git blame` on lines flagged by code-path agent when available
13
+ 3. Correlate with deploy dates, migration timestamps, or PR themes if mentioned in intent
14
+ 4. List **candidate commits** (hash + one-line summary + files touched)
15
+ 5. Rank **likelihood**: high | medium | low per candidate
16
+
17
+ ## Return
18
+
19
+ - Candidate commits with likelihood
20
+ - Files changed in window that match symptom scope
21
+ - Regression hypothesis (one paragraph max)
22
+ - Confidence: high | medium | low
@@ -0,0 +1,27 @@
1
+ # Agent: fix-regression-scope
2
+
3
+ **Readonly.** Do not edit files.
4
+
5
+ ## Role
6
+
7
+ Estimate blast radius and related features that could break from a fix.
8
+
9
+ ## Inputs
10
+
11
+ - Domain inventory constraints and `depends_on` from `aaac/dependencies.yaml`
12
+ - Suspect files from code-path agent
13
+
14
+ ## Procedure
15
+
16
+ 1. List **direct dependents** (imports, routes, types, RLS policies)
17
+ 2. Cross-reference [dependency-analysis.md](./dependency-analysis.md) patterns
18
+ 3. Tag risks: auth, migrations, ISR/revalidation, public API, design tokens
19
+ 4. Set **blast_radius**: low | medium | high
20
+
21
+ ## Return
22
+
23
+ - Affected domains and surfaces
24
+ - Risk tags
25
+ - blast_radius
26
+ - Features to spot-check after fix
27
+ - Confidence: high | medium | low
@@ -0,0 +1,21 @@
1
+ # Agent: fix-repro-verify
2
+
3
+ **Readonly** for investigation; may run dev server or tests to verify fix.
4
+
5
+ ## Role
6
+
7
+ After execute, confirm the original repro steps now pass and no obvious regression.
8
+
9
+ ## Procedure
10
+
11
+ 1. Re-run repro steps from Run artifact `investigation.repro_steps`
12
+ 2. Run targeted tests from `fix-test-failures` recommendations
13
+ 3. Spot-check 2–3 items from `fix-regression-scope` when blast_radius ≥ medium
14
+ 4. Record **repro_status**: fixed | partial | not_fixed
15
+
16
+ ## Return
17
+
18
+ - repro_status
19
+ - Steps executed and outcomes
20
+ - Regressions observed (if any)
21
+ - Confidence: high | medium | low