@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,170 @@
|
|
|
1
|
+
# AAAC verb-specific debug profiles — expected phases and log events
|
|
2
|
+
|
|
3
|
+
version: 1
|
|
4
|
+
|
|
5
|
+
create:
|
|
6
|
+
description: Create verbs — discover → investigate_lite → plan → gates → execute → verify → report.
|
|
7
|
+
phases:
|
|
8
|
+
- discover
|
|
9
|
+
- investigate_lite
|
|
10
|
+
- plan
|
|
11
|
+
- validate
|
|
12
|
+
- impact_analysis
|
|
13
|
+
- dependency_graph
|
|
14
|
+
- fitness_functions
|
|
15
|
+
- rollback
|
|
16
|
+
- execute
|
|
17
|
+
- verify
|
|
18
|
+
- report
|
|
19
|
+
swarm_minimums:
|
|
20
|
+
discover: 4
|
|
21
|
+
expected_events:
|
|
22
|
+
discover:
|
|
23
|
+
- command_parsed
|
|
24
|
+
- run_created
|
|
25
|
+
- graph_resolved
|
|
26
|
+
- phase_start
|
|
27
|
+
- agent_spawned
|
|
28
|
+
- phase_complete
|
|
29
|
+
investigate_lite:
|
|
30
|
+
- phase_start
|
|
31
|
+
- skill_loaded
|
|
32
|
+
- phase_complete
|
|
33
|
+
plan:
|
|
34
|
+
- phase_start
|
|
35
|
+
- skill_loaded
|
|
36
|
+
- phase_complete
|
|
37
|
+
validate:
|
|
38
|
+
- phase_start
|
|
39
|
+
- gate_pass
|
|
40
|
+
- phase_complete
|
|
41
|
+
execute:
|
|
42
|
+
- phase_start
|
|
43
|
+
- execute_start
|
|
44
|
+
- edit_allowed
|
|
45
|
+
- execute_complete
|
|
46
|
+
- phase_complete
|
|
47
|
+
|
|
48
|
+
update:
|
|
49
|
+
description: Update verbs mirror create — discover → investigate_lite → plan → gates → execute.
|
|
50
|
+
phases:
|
|
51
|
+
- discover
|
|
52
|
+
- investigate_lite
|
|
53
|
+
- plan
|
|
54
|
+
- validate
|
|
55
|
+
- impact_analysis
|
|
56
|
+
- dependency_graph
|
|
57
|
+
- fitness_functions
|
|
58
|
+
- rollback
|
|
59
|
+
- execute
|
|
60
|
+
- verify
|
|
61
|
+
- report
|
|
62
|
+
swarm_minimums:
|
|
63
|
+
discover: 4
|
|
64
|
+
expected_events:
|
|
65
|
+
discover:
|
|
66
|
+
- command_parsed
|
|
67
|
+
- run_created
|
|
68
|
+
- graph_resolved
|
|
69
|
+
- phase_start
|
|
70
|
+
- agent_spawned
|
|
71
|
+
- phase_complete
|
|
72
|
+
investigate_lite:
|
|
73
|
+
- phase_start
|
|
74
|
+
- skill_loaded
|
|
75
|
+
- phase_complete
|
|
76
|
+
plan:
|
|
77
|
+
- phase_start
|
|
78
|
+
- skill_loaded
|
|
79
|
+
- phase_complete
|
|
80
|
+
validate:
|
|
81
|
+
- phase_start
|
|
82
|
+
- gate_pass
|
|
83
|
+
- phase_complete
|
|
84
|
+
execute:
|
|
85
|
+
- phase_start
|
|
86
|
+
- execute_start
|
|
87
|
+
- edit_allowed
|
|
88
|
+
- execute_complete
|
|
89
|
+
- phase_complete
|
|
90
|
+
|
|
91
|
+
fix:
|
|
92
|
+
description: Fix verbs — investigate_swarm → root_cause → plan → gates → execute → verify_fix swarm.
|
|
93
|
+
phases:
|
|
94
|
+
- discover
|
|
95
|
+
- investigate_swarm
|
|
96
|
+
- root_cause
|
|
97
|
+
- plan
|
|
98
|
+
- validate
|
|
99
|
+
- impact_analysis
|
|
100
|
+
- dependency_graph
|
|
101
|
+
- fitness_functions
|
|
102
|
+
- rollback
|
|
103
|
+
- execute
|
|
104
|
+
- verify
|
|
105
|
+
- report
|
|
106
|
+
swarm_minimums:
|
|
107
|
+
discover: 4
|
|
108
|
+
investigate_swarm: 7
|
|
109
|
+
verify: 3
|
|
110
|
+
expected_events:
|
|
111
|
+
discover:
|
|
112
|
+
- command_parsed
|
|
113
|
+
- run_created
|
|
114
|
+
- graph_resolved
|
|
115
|
+
- phase_start
|
|
116
|
+
- agent_spawned
|
|
117
|
+
- phase_complete
|
|
118
|
+
investigate_swarm:
|
|
119
|
+
- phase_start
|
|
120
|
+
- agent_spawned
|
|
121
|
+
- skill_loaded
|
|
122
|
+
- phase_complete
|
|
123
|
+
root_cause:
|
|
124
|
+
- phase_start
|
|
125
|
+
- skill_loaded
|
|
126
|
+
- phase_complete
|
|
127
|
+
verify:
|
|
128
|
+
- phase_start
|
|
129
|
+
- agent_spawned
|
|
130
|
+
- phase_complete
|
|
131
|
+
execute:
|
|
132
|
+
- phase_start
|
|
133
|
+
- execute_start
|
|
134
|
+
- edit_allowed
|
|
135
|
+
- edit_denied
|
|
136
|
+
- execute_complete
|
|
137
|
+
- phase_complete
|
|
138
|
+
|
|
139
|
+
check:
|
|
140
|
+
description: Check verbs — readonly discover → gates → report (no execute).
|
|
141
|
+
phases:
|
|
142
|
+
- discover
|
|
143
|
+
- validate
|
|
144
|
+
- impact_analysis
|
|
145
|
+
- dependency_graph
|
|
146
|
+
- fitness_functions
|
|
147
|
+
- report
|
|
148
|
+
swarm_minimums:
|
|
149
|
+
discover: 4
|
|
150
|
+
check_swarm: 3
|
|
151
|
+
expected_events:
|
|
152
|
+
discover:
|
|
153
|
+
- command_parsed
|
|
154
|
+
- run_created
|
|
155
|
+
- graph_resolved
|
|
156
|
+
- phase_start
|
|
157
|
+
- agent_spawned
|
|
158
|
+
- phase_complete
|
|
159
|
+
validate:
|
|
160
|
+
- phase_start
|
|
161
|
+
- gate_pass
|
|
162
|
+
- phase_complete
|
|
163
|
+
report:
|
|
164
|
+
- phase_start
|
|
165
|
+
- skill_loaded
|
|
166
|
+
- phase_complete
|
|
167
|
+
readonly: true
|
|
168
|
+
notes: >-
|
|
169
|
+
Orchestrator runs check_swarm (3 explore agents) during discover.
|
|
170
|
+
Edits always denied — no execute phase.
|
|
@@ -122,6 +122,13 @@
|
|
|
122
122
|
"requirements": 0.8,
|
|
123
123
|
"scope": 0.8
|
|
124
124
|
},
|
|
125
|
+
"complexity_ref": "aaac/complexity.yaml",
|
|
126
|
+
"complexity_strategy": {
|
|
127
|
+
"optimization": "capability_per_complexity",
|
|
128
|
+
"default_verb_when_ambiguous": "update",
|
|
129
|
+
"mutating_verbs": ["create", "update", "fix"],
|
|
130
|
+
"priority": ["reuse", "extend", "modify", "create"]
|
|
131
|
+
},
|
|
125
132
|
"object_maturity": {
|
|
126
133
|
"function": "evolving",
|
|
127
134
|
"component": "evolving",
|
|
@@ -214,6 +221,8 @@
|
|
|
214
221
|
"create-form": "create-component",
|
|
215
222
|
"update-page": "update-component",
|
|
216
223
|
"review-flow": "review-workflow",
|
|
217
|
-
"update-flow": "update-workflow"
|
|
224
|
+
"update-flow": "update-workflow",
|
|
225
|
+
"module-fix": "fix-module",
|
|
226
|
+
"bug-fix": "fix-bug"
|
|
218
227
|
}
|
|
219
228
|
}
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
**Every AAAC command executes within a Run.** There is no standalone lifecycle execution or standalone logging.
|
|
4
4
|
|
|
5
|
+
**Hook enforcement:** [.cursor/hooks.json](../../hooks.json) + [enforcement.json](../enforcement.json) block code edits until `execute`. Registry: [runtime-registry.json](../runtime-registry.json).
|
|
6
|
+
|
|
5
7
|
Schema: [schema.json](schema.json)
|
|
6
8
|
|
|
7
9
|
## Create Run (dispatch step 2.5 — after graph resolve, before orchestrator)
|
|
@@ -71,6 +71,15 @@
|
|
|
71
71
|
"rollback",
|
|
72
72
|
"report"
|
|
73
73
|
],
|
|
74
|
+
"plan_complexity_fields": [
|
|
75
|
+
"requirement_map",
|
|
76
|
+
"complexity_score",
|
|
77
|
+
"complexity_breakdown",
|
|
78
|
+
"reuse",
|
|
79
|
+
"modify",
|
|
80
|
+
"create",
|
|
81
|
+
"rejected_alternatives"
|
|
82
|
+
],
|
|
74
83
|
"resume": {
|
|
75
84
|
"read_manifest": "state/runs/{run_id}/run.json",
|
|
76
85
|
"continue_from": "phase field when status running or blocked with user approval"
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** Emit .cursor/aaac/runtime-registry.json from ontology + lifecycle — SSOT for hooks */
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
|
|
7
|
+
const aaac = path.join(
|
|
8
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
9
|
+
"..",
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
const ontology = JSON.parse(
|
|
13
|
+
fs.readFileSync(path.join(aaac, "ontology.json"), "utf8"),
|
|
14
|
+
);
|
|
15
|
+
const lifecycle = JSON.parse(
|
|
16
|
+
fs.readFileSync(path.join(aaac, "lifecycle/lifecycle.json"), "utf8"),
|
|
17
|
+
);
|
|
18
|
+
const phases = JSON.parse(
|
|
19
|
+
fs.readFileSync(path.join(aaac, "lifecycle/phases.json"), "utf8"),
|
|
20
|
+
);
|
|
21
|
+
const gates = JSON.parse(
|
|
22
|
+
fs.readFileSync(path.join(aaac, "governance/gates.json"), "utf8"),
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
function composeRuntimePhases(verbDef) {
|
|
26
|
+
const work = verbDef.work_phases ?? [];
|
|
27
|
+
if (!verbDef.gate_stack) return work;
|
|
28
|
+
const gatePhases = gates.stacks[verbDef.gate_stack] ?? [];
|
|
29
|
+
const executeIdx = work.indexOf("execute");
|
|
30
|
+
if (executeIdx === -1) {
|
|
31
|
+
const reportIdx = work.indexOf("report");
|
|
32
|
+
if (reportIdx <= 0) {
|
|
33
|
+
return work.length <= 1
|
|
34
|
+
? [...work, ...gatePhases]
|
|
35
|
+
: [...work.slice(0, -1), ...gatePhases, work[work.length - 1]];
|
|
36
|
+
}
|
|
37
|
+
return [...work.slice(0, reportIdx), ...gatePhases, ...work.slice(reportIdx)];
|
|
38
|
+
}
|
|
39
|
+
return [...work.slice(0, executeIdx), ...gatePhases, ...work.slice(executeIdx)];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const gatePhases = {};
|
|
43
|
+
for (const [id, def] of Object.entries(phases.phases ?? {})) {
|
|
44
|
+
if (def.gate) gatePhases[id] = true;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const commands = {};
|
|
48
|
+
const aliases = { ...(ontology.command_aliases ?? {}) };
|
|
49
|
+
|
|
50
|
+
for (const [cmd, entry] of Object.entries(ontology.command_overrides ?? {})) {
|
|
51
|
+
if (entry.alias) continue;
|
|
52
|
+
let pending;
|
|
53
|
+
let gate_stack = null;
|
|
54
|
+
if (lifecycle.workflows?.[cmd]) {
|
|
55
|
+
pending = composeRuntimePhases(lifecycle.workflows[cmd]);
|
|
56
|
+
gate_stack = lifecycle.workflows[cmd].gate_stack ?? null;
|
|
57
|
+
} else {
|
|
58
|
+
const verb = cmd.split("-")[0];
|
|
59
|
+
const verbDef = lifecycle.verbs?.[verb];
|
|
60
|
+
if (verbDef) {
|
|
61
|
+
pending = composeRuntimePhases(verbDef);
|
|
62
|
+
gate_stack = verbDef.gate_stack ?? null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (!pending) continue;
|
|
66
|
+
commands[cmd] = {
|
|
67
|
+
verb: cmd.split("-")[0],
|
|
68
|
+
object: cmd.includes("-") ? cmd.split("-").slice(1).join("-") : null,
|
|
69
|
+
orchestrator: entry.orchestrator ?? entry.resolver ?? null,
|
|
70
|
+
resolver: entry.resolver ?? null,
|
|
71
|
+
pending,
|
|
72
|
+
gate_stack,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
for (const verb of Object.keys(ontology.verbs ?? {})) {
|
|
77
|
+
for (const object of Object.keys(ontology.objects ?? {})) {
|
|
78
|
+
const cmd = `${verb}-${object}`;
|
|
79
|
+
if (commands[cmd]) continue;
|
|
80
|
+
if (
|
|
81
|
+
(ontology.invalid_pairs ?? []).some(([v, o]) => v === verb && o === object)
|
|
82
|
+
) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const verbDef = lifecycle.verbs?.[verb];
|
|
86
|
+
if (!verbDef) continue;
|
|
87
|
+
commands[cmd] = {
|
|
88
|
+
verb,
|
|
89
|
+
object,
|
|
90
|
+
orchestrator: verb === "check" ? "verb-check" : `verb-${verb}`,
|
|
91
|
+
pending: composeRuntimePhases(verbDef),
|
|
92
|
+
gate_stack: verbDef.gate_stack ?? null,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
for (const [alias, canonical] of Object.entries(aliases)) {
|
|
98
|
+
if (commands[canonical] && !commands[alias]) {
|
|
99
|
+
commands[alias] = { ...commands[canonical], alias_of: canonical };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const out = {
|
|
104
|
+
version: 1,
|
|
105
|
+
generated_at: new Date().toISOString(),
|
|
106
|
+
aliases,
|
|
107
|
+
gate_phases: gatePhases,
|
|
108
|
+
commands,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
fs.writeFileSync(
|
|
112
|
+
path.join(aaac, "runtime-registry.json"),
|
|
113
|
+
`${JSON.stringify(out, null, 2)}\n`,
|
|
114
|
+
);
|
|
115
|
+
console.log(`Wrote runtime-registry.json (${Object.keys(commands).length} commands)`);
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Advance Run to next phase. Validates swarm counts + required artifacts.
|
|
4
|
+
* Usage: node advance-phase.mjs <run_id> <completed_phase> [--force]
|
|
5
|
+
*/
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import path from "path";
|
|
8
|
+
import {
|
|
9
|
+
loadRegistry,
|
|
10
|
+
loadEnforcement,
|
|
11
|
+
loadRunManifest,
|
|
12
|
+
runDir,
|
|
13
|
+
isoNow,
|
|
14
|
+
phaseKind,
|
|
15
|
+
isEditPhase,
|
|
16
|
+
isGatePhase,
|
|
17
|
+
writeJson,
|
|
18
|
+
saveActiveRun,
|
|
19
|
+
} from "./lib.mjs";
|
|
20
|
+
import { recordLog } from "./log.mjs";
|
|
21
|
+
|
|
22
|
+
const runId = process.argv[2];
|
|
23
|
+
const completedPhase = process.argv[3];
|
|
24
|
+
const force = process.argv.includes("--force");
|
|
25
|
+
|
|
26
|
+
if (!runId || !completedPhase) {
|
|
27
|
+
console.error("Usage: advance-phase.mjs <run_id> <completed_phase> [--force]");
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const registry = loadRegistry();
|
|
32
|
+
const enforcement = loadEnforcement();
|
|
33
|
+
const manifestPath = path.join(runDir(runId), "run.json");
|
|
34
|
+
const manifest = loadRunManifest(runId);
|
|
35
|
+
|
|
36
|
+
if (!manifest) {
|
|
37
|
+
console.error(`Run not found: ${runId}`);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (manifest.phase !== completedPhase) {
|
|
42
|
+
console.error(
|
|
43
|
+
`Phase mismatch: current=${manifest.phase} completed=${completedPhase}`,
|
|
44
|
+
);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const minAgents =
|
|
49
|
+
completedPhase === "verify" &&
|
|
50
|
+
(enforcement.fix_commands?.includes(manifest.command) || manifest.verb === "fix")
|
|
51
|
+
? enforcement.swarm_min_agents?.verify_fix
|
|
52
|
+
: enforcement.swarm_min_agents?.[completedPhase];
|
|
53
|
+
const launches = manifest.swarm?.task_launches_this_phase ?? 0;
|
|
54
|
+
if (minAgents && launches < minAgents && !force) {
|
|
55
|
+
recordLog(manifest, {
|
|
56
|
+
event: "gate_fail",
|
|
57
|
+
phase: completedPhase,
|
|
58
|
+
phase_kind: manifest.phase_kind,
|
|
59
|
+
detail: `swarm incomplete: ${launches}/${minAgents} agents`,
|
|
60
|
+
level: "warn",
|
|
61
|
+
});
|
|
62
|
+
manifest.updated_at = isoNow();
|
|
63
|
+
writeJson(manifestPath, manifest);
|
|
64
|
+
console.error(
|
|
65
|
+
`Swarm incomplete: phase ${completedPhase} requires ${minAgents} Task agents, got ${launches}. Launch parallel Task subagents first.`,
|
|
66
|
+
);
|
|
67
|
+
process.exit(2);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const requiredArtifacts = enforcement.phase_artifacts?.[completedPhase] ?? [];
|
|
71
|
+
for (const rel of requiredArtifacts) {
|
|
72
|
+
const artifactPath = path.join(runDir(runId), rel);
|
|
73
|
+
if (!fs.existsSync(artifactPath)) {
|
|
74
|
+
recordLog(manifest, {
|
|
75
|
+
event: "gate_fail",
|
|
76
|
+
phase: completedPhase,
|
|
77
|
+
phase_kind: manifest.phase_kind,
|
|
78
|
+
detail: `missing artifact: ${rel}`,
|
|
79
|
+
level: "warn",
|
|
80
|
+
});
|
|
81
|
+
manifest.updated_at = isoNow();
|
|
82
|
+
writeJson(manifestPath, manifest);
|
|
83
|
+
console.error(`Missing artifact: ${rel} (required before leaving ${completedPhase})`);
|
|
84
|
+
process.exit(2);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const now = isoNow();
|
|
89
|
+
const completedIsGate = isGatePhase(completedPhase, registry);
|
|
90
|
+
|
|
91
|
+
if (completedIsGate) {
|
|
92
|
+
recordLog(manifest, {
|
|
93
|
+
event: "gate_pass",
|
|
94
|
+
phase: completedPhase,
|
|
95
|
+
phase_kind: "gate",
|
|
96
|
+
detail: "gate phase completed",
|
|
97
|
+
level: "info",
|
|
98
|
+
});
|
|
99
|
+
manifest.gates = manifest.gates ?? { stack: null, results: {} };
|
|
100
|
+
manifest.gates.results[completedPhase] = "pass";
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (completedPhase === "execute") {
|
|
104
|
+
recordLog(manifest, {
|
|
105
|
+
event: "execute_complete",
|
|
106
|
+
phase: completedPhase,
|
|
107
|
+
phase_kind: "work",
|
|
108
|
+
detail: "execute phase completed",
|
|
109
|
+
level: "info",
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
manifest.completed.push(completedPhase);
|
|
114
|
+
recordLog(manifest, {
|
|
115
|
+
event: "phase_complete",
|
|
116
|
+
phase: completedPhase,
|
|
117
|
+
phase_kind: manifest.phase_kind,
|
|
118
|
+
detail: minAgents ? `swarm_count=${launches}` : "ok",
|
|
119
|
+
level: "info",
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const nextPhase = manifest.pending.shift() ?? null;
|
|
123
|
+
if (!nextPhase) {
|
|
124
|
+
manifest.status = "completed";
|
|
125
|
+
manifest.phase = "report";
|
|
126
|
+
manifest.enforcement.edit_allowed = false;
|
|
127
|
+
recordLog(manifest, {
|
|
128
|
+
event: "run_completed",
|
|
129
|
+
phase: "report",
|
|
130
|
+
phase_kind: "work",
|
|
131
|
+
detail: "all phases completed",
|
|
132
|
+
level: "info",
|
|
133
|
+
});
|
|
134
|
+
} else {
|
|
135
|
+
manifest.phase = nextPhase;
|
|
136
|
+
manifest.phase_kind = phaseKind(nextPhase, registry);
|
|
137
|
+
manifest.swarm = { task_launches_this_phase: 0, phase: nextPhase };
|
|
138
|
+
manifest.enforcement.edit_allowed = isEditPhase(nextPhase, enforcement);
|
|
139
|
+
|
|
140
|
+
recordLog(manifest, {
|
|
141
|
+
event: "phase_start",
|
|
142
|
+
phase: nextPhase,
|
|
143
|
+
phase_kind: manifest.phase_kind,
|
|
144
|
+
detail: "advanced",
|
|
145
|
+
level: "info",
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
if (nextPhase === "execute") {
|
|
149
|
+
recordLog(manifest, {
|
|
150
|
+
event: "execute_start",
|
|
151
|
+
phase: nextPhase,
|
|
152
|
+
phase_kind: "work",
|
|
153
|
+
detail: "edit phase unlocked",
|
|
154
|
+
level: "info",
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (isGatePhase(nextPhase, registry)) {
|
|
159
|
+
recordLog(manifest, {
|
|
160
|
+
event: "gate_blocked",
|
|
161
|
+
phase: nextPhase,
|
|
162
|
+
phase_kind: "gate",
|
|
163
|
+
detail: "awaiting gate evaluation",
|
|
164
|
+
level: "debug",
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
manifest.updated_at = now;
|
|
170
|
+
writeJson(manifestPath, manifest);
|
|
171
|
+
|
|
172
|
+
saveActiveRun(manifest.conversation_id ?? null, {
|
|
173
|
+
run_id: runId,
|
|
174
|
+
conversation_id: manifest.conversation_id ?? null,
|
|
175
|
+
command: manifest.command,
|
|
176
|
+
phase: manifest.phase,
|
|
177
|
+
status: manifest.status,
|
|
178
|
+
task_launches_this_phase: 0,
|
|
179
|
+
edit_allowed: manifest.enforcement.edit_allowed,
|
|
180
|
+
started_at: manifest.created_at,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
console.log(
|
|
184
|
+
JSON.stringify({
|
|
185
|
+
ok: true,
|
|
186
|
+
run_id: runId,
|
|
187
|
+
completed: completedPhase,
|
|
188
|
+
phase: manifest.phase,
|
|
189
|
+
status: manifest.status,
|
|
190
|
+
edit_allowed: manifest.enforcement.edit_allowed,
|
|
191
|
+
}),
|
|
192
|
+
);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** One-shot Run status. Usage: debug-run.mjs <run_id> [--json] */
|
|
3
|
+
import { loadRunManifest } from "./lib.mjs";
|
|
4
|
+
import { debugRunSummary } from "./log.mjs";
|
|
5
|
+
|
|
6
|
+
const runId = process.argv[2];
|
|
7
|
+
const asJson = process.argv.includes("--json");
|
|
8
|
+
|
|
9
|
+
if (!runId) {
|
|
10
|
+
console.error("Usage: debug-run.mjs <run_id> [--json]");
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const manifest = loadRunManifest(runId);
|
|
15
|
+
if (!manifest) {
|
|
16
|
+
console.error(`Run not found: ${runId}`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const summary = debugRunSummary(manifest);
|
|
21
|
+
|
|
22
|
+
if (asJson) {
|
|
23
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
24
|
+
process.exit(0);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
console.log(`Run: ${summary.run_id}`);
|
|
28
|
+
console.log(`Command: /${summary.command} verb=${summary.verb} status=${summary.status}`);
|
|
29
|
+
console.log(`Phase: ${summary.phase} (${summary.phase_kind}) edit_allowed=${summary.edit_allowed}`);
|
|
30
|
+
if (summary.blocked_reason) console.log(`Blocked: ${summary.blocked_reason}`);
|
|
31
|
+
console.log(`Completed: ${summary.completed.join(" → ") || "(none)"}`);
|
|
32
|
+
console.log(`Pending: ${summary.pending.join(" → ") || "(none)"}`);
|
|
33
|
+
console.log(`Swarm: phase=${summary.swarm.phase} count=${summary.swarm.task_launches_this_phase}`);
|
|
34
|
+
console.log(`Log: ${summary.log_count} entries Decisions: ${summary.decisions_count}`);
|
|
35
|
+
console.log("--- last 10 log entries ---");
|
|
36
|
+
for (const e of summary.last_log_entries) {
|
|
37
|
+
console.log(`${e.at} ${e.phase} :: ${e.event} — ${e.detail}`);
|
|
38
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** preToolUse — deny code edits outside execute phase for THIS chat only. */
|
|
3
|
+
import path from "path";
|
|
4
|
+
import {
|
|
5
|
+
loadActiveRun,
|
|
6
|
+
loadRunManifest,
|
|
7
|
+
loadEnforcement,
|
|
8
|
+
isEditPhase,
|
|
9
|
+
isArtifactPath,
|
|
10
|
+
conversationIdFromHook,
|
|
11
|
+
runDir,
|
|
12
|
+
writeJson,
|
|
13
|
+
isoNow,
|
|
14
|
+
} from "./lib.mjs";
|
|
15
|
+
import { recordLog } from "./log.mjs";
|
|
16
|
+
|
|
17
|
+
let input = "";
|
|
18
|
+
process.stdin.setEncoding("utf8");
|
|
19
|
+
process.stdin.on("data", (c) => (input += c));
|
|
20
|
+
process.stdin.on("end", () => {
|
|
21
|
+
const deny = (userMessage, agentMessage, detail) => {
|
|
22
|
+
console.log(JSON.stringify({ permission: "deny", user_message: userMessage, agent_message: agentMessage }));
|
|
23
|
+
process.exit(0);
|
|
24
|
+
};
|
|
25
|
+
const allow = () => {
|
|
26
|
+
console.log(JSON.stringify({ permission: "allow" }));
|
|
27
|
+
process.exit(0);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const persistEditEvent = (manifest, runId, event, detail) => {
|
|
31
|
+
recordLog(manifest, {
|
|
32
|
+
event,
|
|
33
|
+
phase: manifest.phase,
|
|
34
|
+
phase_kind: manifest.phase_kind,
|
|
35
|
+
detail,
|
|
36
|
+
level: "debug",
|
|
37
|
+
});
|
|
38
|
+
manifest.updated_at = isoNow();
|
|
39
|
+
writeJson(path.join(runDir(runId), "run.json"), manifest);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
let hook;
|
|
43
|
+
try {
|
|
44
|
+
hook = JSON.parse(input || "{}");
|
|
45
|
+
} catch {
|
|
46
|
+
allow();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const toolName = hook.tool_name ?? hook.toolName ?? "";
|
|
50
|
+
if (!/^(Write|StrReplace|Delete|EditNotebook|ApplyPatch)$/i.test(toolName)) allow();
|
|
51
|
+
|
|
52
|
+
const conversationId = conversationIdFromHook(hook);
|
|
53
|
+
if (!conversationId) allow();
|
|
54
|
+
|
|
55
|
+
const active = loadActiveRun(conversationId);
|
|
56
|
+
if (!active?.run_id || active.status === "completed") allow();
|
|
57
|
+
|
|
58
|
+
const manifest = loadRunManifest(active.run_id);
|
|
59
|
+
if (!manifest || manifest.status === "completed") allow();
|
|
60
|
+
if (manifest.conversation_id && manifest.conversation_id !== conversationId) allow();
|
|
61
|
+
|
|
62
|
+
const enforcement = loadEnforcement();
|
|
63
|
+
const filePath =
|
|
64
|
+
hook.tool_input?.path ?? hook.toolInput?.path ?? hook.tool_input?.file_path ?? hook.arguments?.path ?? "";
|
|
65
|
+
|
|
66
|
+
if (filePath && isArtifactPath(filePath, enforcement)) {
|
|
67
|
+
persistEditEvent(manifest, active.run_id, "edit_allowed", `artifact path: ${filePath}`);
|
|
68
|
+
allow();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (manifest.awaiting_approval || manifest.status === "blocked") {
|
|
72
|
+
persistEditEvent(manifest, active.run_id, "edit_denied", `blocked at gate: ${manifest.blocked_reason ?? "approval"}`);
|
|
73
|
+
deny(`AAAC Run ${active.run_id} blocked at gate.`, `Run blocked. Run: ${active.run_id}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (isEditPhase(manifest.phase, enforcement)) {
|
|
77
|
+
persistEditEvent(manifest, active.run_id, "edit_allowed", `${toolName} in phase ${manifest.phase}`);
|
|
78
|
+
allow();
|
|
79
|
+
}
|
|
80
|
+
if (enforcement.artifact_write_phases?.includes(manifest.phase) && filePath) {
|
|
81
|
+
persistEditEvent(manifest, active.run_id, "edit_allowed", `artifact_write phase ${manifest.phase}`);
|
|
82
|
+
allow();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
persistEditEvent(
|
|
86
|
+
manifest,
|
|
87
|
+
active.run_id,
|
|
88
|
+
"edit_denied",
|
|
89
|
+
`${toolName} blocked in phase ${manifest.phase}${filePath ? `: ${filePath}` : ""}`,
|
|
90
|
+
);
|
|
91
|
+
deny(
|
|
92
|
+
`AAAC: edits blocked in phase "${manifest.phase}" (this chat). Run: ${active.run_id}`,
|
|
93
|
+
`Cannot ${toolName} during "${manifest.phase}". Chat ${conversationId}. Advance phase first.`,
|
|
94
|
+
);
|
|
95
|
+
});
|