@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.
- package/README.md +4 -3
- package/package.json +13 -1
- package/src/cli.mjs +39 -5
- package/src/generators/generate-commands.mjs +120 -3
- package/src/generators/generate-graph.mjs +17 -0
- package/src/lib/install.mjs +1 -0
- package/src/lib/run-engine-paths.mjs +33 -0
- package/src/run-engine/advance-phase.mjs +192 -0
- package/src/run-engine/debug-run.mjs +38 -0
- package/src/run-engine/gate-write.mjs +95 -0
- package/src/run-engine/init-run.mjs +165 -0
- package/src/run-engine/lib.mjs +136 -0
- package/src/run-engine/log-dump.mjs +76 -0
- package/src/run-engine/log-trace.mjs +18 -0
- package/src/run-engine/log.mjs +343 -0
- package/src/run-engine/record-task.mjs +56 -0
- package/src/run-engine/stop-check.mjs +55 -0
- package/templates/cursor/aaac/complexity.yaml +98 -0
- package/templates/cursor/aaac/contracts/commands/fix-bug.yaml +10 -3
- package/templates/cursor/aaac/contracts/commands/fix-module.yaml +41 -0
- package/templates/cursor/aaac/contracts/skills/investigation.yaml +22 -1
- package/templates/cursor/aaac/contracts/skills/planning.yaml +17 -0
- package/templates/cursor/aaac/contracts/skills/validation.yaml +9 -1
- package/templates/cursor/aaac/dispatch.md +30 -5
- package/templates/cursor/aaac/enforcement.json +22 -0
- package/templates/cursor/aaac/fitness-functions.yaml +8 -0
- package/templates/cursor/aaac/governance/gates.json +3 -1
- package/templates/cursor/aaac/graph.project.yaml +237 -5
- package/templates/cursor/aaac/layers.md +3 -1
- package/templates/cursor/aaac/lifecycle/lifecycle.json +41 -1
- package/templates/cursor/aaac/lifecycle/phases.json +1 -0
- package/templates/cursor/aaac/observability/telemetry.yaml +60 -0
- package/templates/cursor/aaac/observability/verb-debug.yaml +170 -0
- package/templates/cursor/aaac/ontology.json +10 -1
- package/templates/cursor/aaac/run/RUN.md +2 -0
- package/templates/cursor/aaac/run/schema.json +9 -0
- package/templates/cursor/aaac/scripts/generate-runtime-registry.mjs +115 -0
- package/templates/cursor/aaac/scripts/run-engine/advance-phase.mjs +192 -0
- package/templates/cursor/aaac/scripts/run-engine/debug-run.mjs +38 -0
- package/templates/cursor/aaac/scripts/run-engine/gate-write.mjs +95 -0
- package/templates/cursor/aaac/scripts/run-engine/init-run.mjs +165 -0
- package/templates/cursor/aaac/scripts/run-engine/lib.mjs +136 -0
- package/templates/cursor/aaac/scripts/run-engine/log-dump.mjs +76 -0
- package/templates/cursor/aaac/scripts/run-engine/log-trace.mjs +18 -0
- package/templates/cursor/aaac/scripts/run-engine/log.mjs +343 -0
- package/templates/cursor/aaac/scripts/run-engine/record-task.mjs +56 -0
- package/templates/cursor/aaac/scripts/run-engine/stop-check.mjs +55 -0
- package/templates/cursor/agents/aaac-log-debug.md +72 -0
- package/templates/cursor/agents/fix-code-path.md +27 -0
- package/templates/cursor/agents/fix-hypothesis-validate.md +26 -0
- package/templates/cursor/agents/fix-inventory-confirm.md +22 -0
- package/templates/cursor/agents/fix-recent-changes.md +22 -0
- package/templates/cursor/agents/fix-regression-scope.md +27 -0
- package/templates/cursor/agents/fix-repro-verify.md +21 -0
- package/templates/cursor/agents/fix-repro.md +29 -0
- package/templates/cursor/agents/fix-runtime-evidence.md +22 -0
- package/templates/cursor/agents/fix-test-failures.md +23 -0
- package/templates/cursor/agents/playwright-check-run.md +44 -0
- package/templates/cursor/hooks/aaac-before-submit.sh +3 -0
- package/templates/cursor/hooks/aaac-pre-tool.sh +4 -0
- package/templates/cursor/hooks/aaac-stop.sh +3 -0
- package/templates/cursor/hooks/aaac-subagent-start.sh +3 -0
- package/templates/cursor/hooks.json +30 -0
- package/templates/cursor/policies/minimal-complexity.md +101 -0
- package/templates/cursor/rules/aaac-enforcement.mdc +42 -0
- package/templates/cursor/skills/shared/execution/SKILL.md +1 -1
- package/templates/cursor/skills/shared/fitness-functions/SKILL.md +23 -7
- package/templates/cursor/skills/shared/investigation/SKILL.md +91 -18
- package/templates/cursor/skills/shared/investigation/orchestrator/SKILL.md +12 -4
- package/templates/cursor/skills/shared/planning/SKILL.md +74 -8
- package/templates/cursor/skills/shared/reporting/SKILL.md +2 -1
- package/templates/cursor/skills/shared/root-cause/SKILL.md +14 -3
- package/templates/cursor/skills/shared/testing/SKILL.md +26 -5
- package/templates/cursor/skills/shared/validation/SKILL.md +48 -13
- package/templates/cursor/skills/shared/verbs/_dispatch-utils.md +20 -1
- package/templates/cursor/skills/shared/verbs/_lifecycle.md +3 -2
- package/templates/cursor/skills/shared/verbs/check/orchestrator/SKILL.md +4 -1
- package/templates/cursor/skills/shared/verbs/create/orchestrator/SKILL.md +2 -2
- package/templates/cursor/skills/shared/verbs/fix/orchestrator/SKILL.md +21 -11
- package/templates/cursor/skills/shared/verbs/fix/orchestrator/contract.yaml +19 -4
- package/templates/cursor/skills/shared/verbs/update/orchestrator/SKILL.md +2 -2
- 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
|