@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,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
|
-
|
|
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
|
-
-
|
|
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
|