@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,98 @@
1
+ # Complexity governance — SSOT for create / update / fix (code-changing verbs)
2
+ # Policy: .cursor/policies/minimal-complexity.md
3
+ # Scored in plan; validated at validate gate; enforced at fitness_functions gate.
4
+
5
+ version: 1
6
+
7
+ mutating_verbs: [create, update, fix]
8
+
9
+ optimization:
10
+ target: "maximum outcome / minimum structure"
11
+ default_verb_when_ambiguous: update
12
+
13
+ strategy_priority:
14
+ - reuse_existing
15
+ - extend_existing
16
+ - modify_existing
17
+ - create_new
18
+
19
+ scoring:
20
+ description: Sum weights for every net-new artifact in the plan (not edits to existing files).
21
+ weights:
22
+ new_file: 1
23
+ new_component: 1
24
+ new_function: 1
25
+ new_hook: 1
26
+ new_module: 3
27
+ new_service: 5
28
+ new_api_endpoint: 2
29
+ new_table: 3
30
+ new_migration: 2
31
+ new_state_machine: 4
32
+ new_queue_or_bus: 5
33
+ new_abstraction_layer: 4
34
+ new_orchestrator: 4
35
+ new_skill: 2
36
+ new_dependency: 2
37
+
38
+ thresholds:
39
+ fix: 5
40
+ update: 8
41
+ create: 12
42
+
43
+ yagni:
44
+ rule: Future requirements do not exist unless the user stated them.
45
+ reject_without_user_evidence:
46
+ - maybe later
47
+ - might need
48
+ - future-proof
49
+ - future proof
50
+ - scalability
51
+ - extensibility
52
+ - plugin system
53
+ - event bus
54
+ - generic framework
55
+ - abstraction layer
56
+ - for flexibility
57
+ - potential use case
58
+
59
+ plan_artifact:
60
+ path: Run.artifacts.plan
61
+ required_for_verbs: [create, update, fix]
62
+ required_fields:
63
+ requirement_map:
64
+ description: Each stated requirement mapped to satisfying artifacts
65
+ item_shape:
66
+ requirement: string
67
+ satisfies_with: list
68
+ complexity_score: number
69
+ complexity_breakdown: object
70
+ reuse:
71
+ description: Existing artifacts to reuse or extend (preferred)
72
+ type: list
73
+ modify:
74
+ description: Existing artifacts to change in place
75
+ type: list
76
+ create:
77
+ description: Net-new artifacts — each must cite requirement_map entry
78
+ item_shape:
79
+ artifact: string
80
+ kind: string
81
+ requirement_ref: string
82
+ why_not_reuse: string
83
+ rejected_alternatives:
84
+ description: Higher-complexity options considered and rejected
85
+ type: list
86
+
87
+ gate_rules:
88
+ validate:
89
+ fail_when:
90
+ - missing required plan fields for mutating verb
91
+ - create entry without requirement_ref or why_not_reuse
92
+ - yagni phrase in plan without user intent evidence
93
+ fitness_functions:
94
+ function: minimal_complexity
95
+ fail_when:
96
+ - complexity_score above threshold for verb
97
+ - speculative create not justified
98
+ blocking: true
@@ -1,12 +1,13 @@
1
1
  name: fix-bug
2
- purpose: Repair broken behavior in a domain
2
+ purpose: Repair broken behavior in a domain (alias of fix-module workflow)
3
3
  inputs:
4
4
  domain:
5
5
  required: false
6
+ enum: [cms, ui, database, aaac]
6
7
  intent:
7
8
  required: true
8
9
  outputs:
9
- findings:
10
+ investigation:
10
11
  type: markdown
11
12
  required: true
12
13
  root_cause:
@@ -24,9 +25,15 @@ outputs:
24
25
  type: markdown
25
26
  required: true
26
27
  lifecycle_verb: fix
28
+ workflow: fix-bug
27
29
  success_criteria:
28
- - deep investigation and root_cause before plan
30
+ - discovery swarm completed (4-6 agents)
31
+ - fix investigation swarm completed (7 parallel agents)
32
+ - root_cause confidence at least 0.7
33
+ - repro_status fixed after execute
29
34
  - impact_analysis proceed yes or rollback defined
30
35
  failure_conditions:
36
+ - skip discovery or investigate_swarm phase
31
37
  - root_cause confidence below 0.7
32
38
  - symptom patch without root cause frame
39
+ - repro_status not_fixed while claiming success
@@ -0,0 +1,41 @@
1
+ name: fix-module
2
+ purpose: Repair broken behavior in a bounded domain module
3
+ inputs:
4
+ domain:
5
+ required: true
6
+ enum: [cms, ui, database, aaac]
7
+ intent:
8
+ required: true
9
+ outputs:
10
+ investigation:
11
+ type: markdown
12
+ required: true
13
+ root_cause:
14
+ type: markdown
15
+ required: true
16
+ plan:
17
+ type: markdown
18
+ required: true
19
+ implementation:
20
+ type: code_changes
21
+ verification:
22
+ type: markdown
23
+ required: true
24
+ report:
25
+ type: markdown
26
+ required: true
27
+ lifecycle_verb: fix
28
+ workflow: fix-module
29
+ success_criteria:
30
+ - discovery swarm completed (4-6 agents)
31
+ - fix investigation swarm completed (7 parallel agents)
32
+ - root_cause confidence at least 0.7
33
+ - repro_status fixed after execute
34
+ - domain inventory Section 3 synced after execute
35
+ - complexity_score at most 5
36
+ failure_conditions:
37
+ - skip discovery or investigate_swarm phase
38
+ - plan or execute before root_cause artifact
39
+ - symptom patch without root cause frame
40
+ - repro_status not_fixed while claiming success
41
+ - domain slug missing for resolver commands
@@ -1,9 +1,22 @@
1
1
  name: investigation
2
- purpose: Deep investigation for fix paths and incidents
2
+ purpose: Deep investigation swarm for fix paths and incidents
3
3
  outputs:
4
+ investigation_frame:
5
+ type: object
6
+ required: true
4
7
  findings:
5
8
  type: list
6
9
  required: true
10
+ repro_steps:
11
+ type: list
12
+ required: true
13
+ repro_confirmed:
14
+ type: string
15
+ required: true
16
+ enum: [yes, partial, no]
17
+ suspect_files:
18
+ type: list
19
+ required: true
7
20
  risks:
8
21
  type: list
9
22
  required: true
@@ -14,4 +27,12 @@ outputs:
14
27
  type: object
15
28
  required: true
16
29
  fields: [architecture, requirements, scope]
30
+ swarm:
31
+ fix_path:
32
+ prerequisite: discovery
33
+ agents: 7
34
+ parallel: mandatory
35
+ incident_path:
36
+ agents: 4
37
+ parallel: mandatory
17
38
  readonly: true
@@ -0,0 +1,17 @@
1
+ name: planning
2
+ purpose: Plan mutating changes with complexity score and requirement map before execute
3
+ applies_to_verbs: [create, update, fix]
4
+ outputs:
5
+ plan:
6
+ type: object
7
+ required_for_verbs: [create, update, fix]
8
+ required_fields:
9
+ - requirement_map
10
+ - complexity_score
11
+ - complexity_breakdown
12
+ - reuse
13
+ - modify
14
+ - create
15
+ - rejected_alternatives
16
+ store_at: Run.artifacts.plan
17
+ readonly: true
@@ -1,5 +1,6 @@
1
1
  name: validation
2
- purpose: Confidence gates before execute
2
+ purpose: Confidence and complexity gates before execute (mutating verbs)
3
+ applies_to_verbs: [create, update, fix]
3
4
  outputs:
4
5
  validation:
5
6
  type: enum
@@ -8,6 +9,13 @@ outputs:
8
9
  scores:
9
10
  type: object
10
11
  required: true
12
+ complexity:
13
+ type: object
14
+ required_for_verbs: [create, update, fix]
15
+ fields:
16
+ score: number
17
+ threshold: number
18
+ pass: boolean
11
19
  clarification_questions:
12
20
  type: list
13
21
  required_when: validation fail