@ludecker/aaac 1.1.4 → 1.1.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ludecker/aaac",
3
- "version": "1.1.4",
3
+ "version": "1.1.5",
4
4
  "description": "Agentic Architecture as Code (AAAC) — installable Cursor agent framework",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -16,6 +16,7 @@ import {
16
16
  phaseKind,
17
17
  isEditPhase,
18
18
  isGatePhase,
19
+ resolveSwarmMinimum,
19
20
  writeJson,
20
21
  saveActiveRun,
21
22
  } from "./lib.mjs";
@@ -55,11 +56,7 @@ if (manifest.phase !== completedPhase) {
55
56
  process.exit(1);
56
57
  }
57
58
 
58
- const minAgents =
59
- completedPhase === "verify" &&
60
- (enforcement.fix_commands?.includes(manifest.command) || manifest.verb === "fix")
61
- ? enforcement.swarm_min_agents?.verify_fix
62
- : enforcement.swarm_min_agents?.[completedPhase];
59
+ const minAgents = resolveSwarmMinimum(completedPhase, manifest, enforcement);
63
60
  const launches = manifest.swarm?.task_launches_this_phase ?? 0;
64
61
  if (minAgents && launches < minAgents && !force) {
65
62
  recordLog(manifest, {
@@ -31,6 +31,12 @@ if (summary.blocked_reason) console.log(`Blocked: ${summary.blocked_reason}`);
31
31
  console.log(`Completed: ${summary.completed.join(" → ") || "(none)"}`);
32
32
  console.log(`Pending: ${summary.pending.join(" → ") || "(none)"}`);
33
33
  console.log(`Swarm: phase=${summary.swarm.phase} count=${summary.swarm.task_launches_this_phase}`);
34
+ if (summary.swarm.agents?.length) {
35
+ console.log(`Agents: ${summary.swarm.agents.length} recorded this phase`);
36
+ for (const a of summary.swarm.agents.slice(-5)) {
37
+ console.log(` #${a.index} ${a.subagent_type ?? "?"} ${a.description ?? ""} @ ${a.at}`);
38
+ }
39
+ }
34
40
  console.log(`Log: ${summary.log_count} entries Decisions: ${summary.decisions_count}`);
35
41
  console.log("--- last 10 log entries ---");
36
42
  for (const e of summary.last_log_entries) {
@@ -136,6 +136,23 @@ export function phaseKind(phase, registry) {
136
136
  return isGatePhase(phase, registry) ? "gate" : "work";
137
137
  }
138
138
 
139
+ /** Swarm minimum for completed phase — check verb uses check_swarm on discover. */
140
+ export function resolveSwarmMinimum(completedPhase, manifest, enforcement) {
141
+ if (
142
+ completedPhase === "verify" &&
143
+ (enforcement.fix_commands?.includes(manifest.command) || manifest.verb === "fix")
144
+ ) {
145
+ return enforcement.swarm_min_agents?.verify_fix;
146
+ }
147
+ if (completedPhase === "discover" && manifest.verb === "check") {
148
+ return (
149
+ enforcement.swarm_min_agents?.check_swarm ??
150
+ enforcement.swarm_min_agents?.discover
151
+ );
152
+ }
153
+ return enforcement.swarm_min_agents?.[completedPhase];
154
+ }
155
+
139
156
  export function promptFromHook(hook) {
140
157
  return hook?.prompt ?? hook?.text ?? hook?.content ?? "";
141
158
  }
@@ -334,7 +334,7 @@ export function debugRunSummary(manifest) {
334
334
  awaiting_approval: manifest.awaiting_approval,
335
335
  completed: manifest.completed ?? [],
336
336
  pending: manifest.pending ?? [],
337
- swarm: { phase: swarmPhase, task_launches_this_phase: swarmCount },
337
+ swarm: { phase: swarmPhase, task_launches_this_phase: swarmCount, agents: manifest.swarm?.agents ?? [] },
338
338
  edit_allowed: manifest.enforcement?.edit_allowed ?? false,
339
339
  last_log_entries: log.slice(-10),
340
340
  decisions_count: (manifest.decisions ?? []).length,
@@ -44,19 +44,40 @@ process.stdin.on("end", () => {
44
44
  if (manifest.conversation_id && manifest.conversation_id !== conversationId) allow();
45
45
 
46
46
  manifest.swarm = manifest.swarm ?? {};
47
- manifest.swarm.task_launches_this_phase = (manifest.swarm.task_launches_this_phase ?? 0) + 1;
47
+ const launchIndex = (manifest.swarm.task_launches_this_phase ?? 0) + 1;
48
+ manifest.swarm.task_launches_this_phase = launchIndex;
48
49
  manifest.swarm.phase = manifest.phase;
49
- manifest.updated_at = isoNow();
50
+
51
+ const agentEntry = {
52
+ at: isoNow(),
53
+ index: launchIndex,
54
+ phase: manifest.phase,
55
+ subagent_type: hook.subagent_type ?? hook.subagentType ?? null,
56
+ description: hook.description ?? hook.subagent_description ?? null,
57
+ model: hook.model ?? null,
58
+ readonly: hook.readonly ?? null,
59
+ };
60
+ manifest.swarm.agents = manifest.swarm.agents ?? [];
61
+ manifest.swarm.agents.push(agentEntry);
62
+
63
+ const telemetryDetail = JSON.stringify({
64
+ count: launchIndex,
65
+ subagent_type: agentEntry.subagent_type,
66
+ index: launchIndex,
67
+ });
50
68
 
51
69
  recordLog(manifest, {
52
70
  event: "agent_spawned",
53
71
  phase: manifest.phase,
54
72
  phase_kind: manifest.phase_kind,
55
- detail: `count=${manifest.swarm.task_launches_this_phase}`,
73
+ detail: telemetryDetail,
56
74
  level: "debug",
57
75
  });
58
76
 
59
77
  writeJson(path.join(runDir(active.run_id), "run.json"), manifest);
60
- saveActiveRun(conversationId, { ...active, task_launches_this_phase: manifest.swarm.task_launches_this_phase });
78
+ saveActiveRun(conversationId, {
79
+ ...active,
80
+ task_launches_this_phase: manifest.swarm.task_launches_this_phase,
81
+ });
61
82
  allow();
62
83
  });
@@ -6,6 +6,7 @@
6
6
  "verify_verbs": ["create", "update", "fix"],
7
7
  "swarm_min_agents": {
8
8
  "discover": 4,
9
+ "check_swarm": 3,
9
10
  "investigate_swarm": 7,
10
11
  "research_swarm": 6,
11
12
  "verify_fix": 3
@@ -14,6 +15,11 @@
14
15
  "investigate_swarm": ["artifacts/investigation.md"],
15
16
  "root_cause": ["artifacts/root_cause.yaml"],
16
17
  "plan": ["artifacts/plan.yaml"],
18
+ "validate": ["artifacts/validate.yaml"],
19
+ "impact_analysis": ["artifacts/impact.yaml"],
20
+ "dependency_graph": ["artifacts/dependency_graph.yaml"],
21
+ "fitness_functions": ["artifacts/fitness.yaml"],
22
+ "rollback": ["artifacts/rollback.yaml"],
17
23
  "verify": ["artifacts/verify.yaml"],
18
24
  "report": ["artifacts/report.md"]
19
25
  },
@@ -35,6 +35,11 @@
35
35
  "stack": "string | null",
36
36
  "results": {}
37
37
  },
38
+ "swarm": {
39
+ "task_launches_this_phase": 0,
40
+ "phase": "string | null",
41
+ "agents": []
42
+ },
38
43
  "created_at": "iso8601",
39
44
  "updated_at": "iso8601"
40
45
  },
@@ -16,6 +16,7 @@ import {
16
16
  phaseKind,
17
17
  isEditPhase,
18
18
  isGatePhase,
19
+ resolveSwarmMinimum,
19
20
  writeJson,
20
21
  saveActiveRun,
21
22
  } from "./lib.mjs";
@@ -55,11 +56,7 @@ if (manifest.phase !== completedPhase) {
55
56
  process.exit(1);
56
57
  }
57
58
 
58
- const minAgents =
59
- completedPhase === "verify" &&
60
- (enforcement.fix_commands?.includes(manifest.command) || manifest.verb === "fix")
61
- ? enforcement.swarm_min_agents?.verify_fix
62
- : enforcement.swarm_min_agents?.[completedPhase];
59
+ const minAgents = resolveSwarmMinimum(completedPhase, manifest, enforcement);
63
60
  const launches = manifest.swarm?.task_launches_this_phase ?? 0;
64
61
  if (minAgents && launches < minAgents && !force) {
65
62
  recordLog(manifest, {
@@ -31,6 +31,12 @@ if (summary.blocked_reason) console.log(`Blocked: ${summary.blocked_reason}`);
31
31
  console.log(`Completed: ${summary.completed.join(" → ") || "(none)"}`);
32
32
  console.log(`Pending: ${summary.pending.join(" → ") || "(none)"}`);
33
33
  console.log(`Swarm: phase=${summary.swarm.phase} count=${summary.swarm.task_launches_this_phase}`);
34
+ if (summary.swarm.agents?.length) {
35
+ console.log(`Agents: ${summary.swarm.agents.length} recorded this phase`);
36
+ for (const a of summary.swarm.agents.slice(-5)) {
37
+ console.log(` #${a.index} ${a.subagent_type ?? "?"} ${a.description ?? ""} @ ${a.at}`);
38
+ }
39
+ }
34
40
  console.log(`Log: ${summary.log_count} entries Decisions: ${summary.decisions_count}`);
35
41
  console.log("--- last 10 log entries ---");
36
42
  for (const e of summary.last_log_entries) {
@@ -136,6 +136,23 @@ export function phaseKind(phase, registry) {
136
136
  return isGatePhase(phase, registry) ? "gate" : "work";
137
137
  }
138
138
 
139
+ /** Swarm minimum for completed phase — check verb uses check_swarm on discover. */
140
+ export function resolveSwarmMinimum(completedPhase, manifest, enforcement) {
141
+ if (
142
+ completedPhase === "verify" &&
143
+ (enforcement.fix_commands?.includes(manifest.command) || manifest.verb === "fix")
144
+ ) {
145
+ return enforcement.swarm_min_agents?.verify_fix;
146
+ }
147
+ if (completedPhase === "discover" && manifest.verb === "check") {
148
+ return (
149
+ enforcement.swarm_min_agents?.check_swarm ??
150
+ enforcement.swarm_min_agents?.discover
151
+ );
152
+ }
153
+ return enforcement.swarm_min_agents?.[completedPhase];
154
+ }
155
+
139
156
  export function promptFromHook(hook) {
140
157
  return hook?.prompt ?? hook?.text ?? hook?.content ?? "";
141
158
  }
@@ -334,7 +334,7 @@ export function debugRunSummary(manifest) {
334
334
  awaiting_approval: manifest.awaiting_approval,
335
335
  completed: manifest.completed ?? [],
336
336
  pending: manifest.pending ?? [],
337
- swarm: { phase: swarmPhase, task_launches_this_phase: swarmCount },
337
+ swarm: { phase: swarmPhase, task_launches_this_phase: swarmCount, agents: manifest.swarm?.agents ?? [] },
338
338
  edit_allowed: manifest.enforcement?.edit_allowed ?? false,
339
339
  last_log_entries: log.slice(-10),
340
340
  decisions_count: (manifest.decisions ?? []).length,
@@ -44,19 +44,40 @@ process.stdin.on("end", () => {
44
44
  if (manifest.conversation_id && manifest.conversation_id !== conversationId) allow();
45
45
 
46
46
  manifest.swarm = manifest.swarm ?? {};
47
- manifest.swarm.task_launches_this_phase = (manifest.swarm.task_launches_this_phase ?? 0) + 1;
47
+ const launchIndex = (manifest.swarm.task_launches_this_phase ?? 0) + 1;
48
+ manifest.swarm.task_launches_this_phase = launchIndex;
48
49
  manifest.swarm.phase = manifest.phase;
49
- manifest.updated_at = isoNow();
50
+
51
+ const agentEntry = {
52
+ at: isoNow(),
53
+ index: launchIndex,
54
+ phase: manifest.phase,
55
+ subagent_type: hook.subagent_type ?? hook.subagentType ?? null,
56
+ description: hook.description ?? hook.subagent_description ?? null,
57
+ model: hook.model ?? null,
58
+ readonly: hook.readonly ?? null,
59
+ };
60
+ manifest.swarm.agents = manifest.swarm.agents ?? [];
61
+ manifest.swarm.agents.push(agentEntry);
62
+
63
+ const telemetryDetail = JSON.stringify({
64
+ count: launchIndex,
65
+ subagent_type: agentEntry.subagent_type,
66
+ index: launchIndex,
67
+ });
50
68
 
51
69
  recordLog(manifest, {
52
70
  event: "agent_spawned",
53
71
  phase: manifest.phase,
54
72
  phase_kind: manifest.phase_kind,
55
- detail: `count=${manifest.swarm.task_launches_this_phase}`,
73
+ detail: telemetryDetail,
56
74
  level: "debug",
57
75
  });
58
76
 
59
77
  writeJson(path.join(runDir(active.run_id), "run.json"), manifest);
60
- saveActiveRun(conversationId, { ...active, task_launches_this_phase: manifest.swarm.task_launches_this_phase });
78
+ saveActiveRun(conversationId, {
79
+ ...active,
80
+ task_launches_this_phase: manifest.swarm.task_launches_this_phase,
81
+ });
61
82
  allow();
62
83
  });
@@ -11,8 +11,8 @@ Every AAAC slash command (`/fix-module`, `/update-module`, `/write-article`, …
11
11
 
12
12
  ## Prerequisites
13
13
 
14
- 1. **Project opened in Cursor** — `.cursor/hooks.json` is installed by `init`; hooks run when the project is open
15
- 2. **Registry current** — after ontology edits: `npx @ludecker/aaac@latest generate`
14
+ 1. **Cursor Hooks enabled** — Settings → Hooks; restart Cursor after `.cursor/hooks.json` changes
15
+ 2. **Registry current** — `node .cursor/aaac/generate-graph.mjs`
16
16
 
17
17
  ## Hook behavior (automatic)
18
18
 
@@ -31,11 +31,16 @@ Every AAAC slash command (`/fix-module`, `/update-module`, `/write-article`, …
31
31
  node .cursor/aaac/scripts/run-engine/advance-phase.mjs <run_id> <phase>
32
32
  ```
33
33
  3. **Swarm minimums** (enforced by advance-phase):
34
- - `discover`: 4 Task agents
34
+ - `discover`: 4 Task agents (check verbs: `check_swarm` 3)
35
35
  - `investigate_swarm`: 7 Task agents
36
36
  - `research_swarm`: 6 Task agents
37
- 4. **Code edits only in `execute`** (hook-enforced). Before execute: artifacts only under `.cursor/aaac/state/runs/`.
38
- 5. **Complete the Run** — advance through `report`, set status completed.
37
+ 4. **Verify gate (create / update / fix):** before advancing past `verify`, run:
38
+ ```bash
39
+ node .cursor/aaac/scripts/run-engine/verify-website-build.mjs --run-id <run_id>
40
+ ```
41
+ `advance-phase.mjs verify` runs this automatically and blocks on missing static assets or failed `vite build` (catches favicon/path regressions).
42
+ 5. **Code edits only in `execute`** (hook-enforced). Before execute: artifacts only under `.cursor/aaac/state/runs/`.
43
+ 6. **Complete the Run** — advance through `report`, set status completed.
39
44
 
40
45
  ## If edit is denied
41
46
 
@@ -0,0 +1,18 @@
1
+ # Task prompt policy excerpt (mandatory)
2
+
3
+ Append this block to **every** Task sub-agent prompt the orchestrator sends.
4
+
5
+ ## Policies (mandatory)
6
+
7
+ - **Readonly** unless the agent spec explicitly allows test runs or shell commands.
8
+ - **Evidence:** every claim needs `path:line` citations the parent can spot-check.
9
+ - **SSOT:** do not invent constants, routes, or file paths — read the repo.
10
+ - **Prime directive** (master rules): clarity beats cleverness; predictability beats shortcuts; one truth beats convenience.
11
+ - **Layer boundaries:** `packages/ui` must not import `apps/website`; `packages/types` and `packages/utils` stay runtime-free.
12
+ - **Errors:** never silent — state gaps explicitly in the return block.
13
+
14
+ Full policy chain: [.cursor/policies/master-rules.md](../../policies/master-rules.md) → [docs/master_rules.md](../../../docs/master_rules.md)
15
+
16
+ ## Return shape
17
+
18
+ Follow the agent spec (`Findings`, `Evidence`, `Gaps`, `Confidence`). Do not edit files unless the spec allows it.
@@ -34,6 +34,10 @@ Launch **3** parallel `Task` subagents (`explore`, `readonly: true`) in **one me
34
34
 
35
35
  Optional **4th** agent (second wave, only if intent names external system): `discovery-boundaries.md` for integration edges.
36
36
 
37
+ ## Task prompt (mandatory)
38
+
39
+ Every Task prompt **must** include the policy excerpt from [_task-prompt-policy.md](../_task-prompt-policy.md) plus: question, scope, agent spec path, and inventory path when available.
40
+
37
41
  ## Merge
38
42
 
39
43
  Parent synthesizes one brief:
@@ -24,6 +24,10 @@ Launch **4–6** parallel `Task` subagents (`explore`, `readonly: true`) in **on
24
24
 
25
25
  Add domain-specific angles from inventory skill. Max **8** agents total; second wave ≤2 for critical gaps.
26
26
 
27
+ ## Task prompt (mandatory)
28
+
29
+ Every Task prompt **must** include the policy excerpt from [_task-prompt-policy.md](../_task-prompt-policy.md) plus: intent, domain, inventory constraints, and the linked agent spec path.
30
+
27
31
  ## Output
28
32
 
29
33
  Merged brief for `planning`: findings, evidence, gaps, confidence. Parent spot-checks `path:line` claims.
@@ -53,7 +53,7 @@ Launch **7 parallel** `Task` subagents in **one message**. `readonly: true` unle
53
53
  | 6 | [fix-runtime-evidence.md](../../../agents/fix-runtime-evidence.md) | `generalPurpose` | CI/logs/MCP evidence |
54
54
  | 7 | [fix-inventory-confirm.md](../../../agents/fix-inventory-confirm.md) | `explore` | Inventory scope + constraints |
55
55
 
56
- **Parent prompt must include:** intent, domain, inventory path, discovery brief summary, frame fields.
56
+ **Parent prompt must include:** intent, domain, inventory path, discovery brief summary, frame fields, and the policy excerpt from [_task-prompt-policy.md](../_task-prompt-policy.md).
57
57
 
58
58
  **Anti-patterns (hard fail):**
59
59
 
@@ -36,3 +36,5 @@ Pass output to [validation](../validation/SKILL.md). If any confidence below thr
36
36
  ## Agents
37
37
 
38
38
  Reuse readonly specs: [discovery-inventory.md](../../../agents/discovery-inventory.md), [discovery-ssot.md](../../../agents/discovery-ssot.md), [dependency-analysis.md](../../../agents/dependency-analysis.md) — 2–3 parallel max for lite path.
39
+
40
+ Every Task prompt **must** include the policy excerpt from [_task-prompt-policy.md](../_task-prompt-policy.md).
@@ -18,15 +18,15 @@ Phase `verify` (and `test_only` orchestrators). On **fix** paths, run fix verify
18
18
  2. Invoke [unit-test-run.md](../../../agents/unit-test-run.md) pattern for targeted vitest
19
19
  3. Fallow MCP → `check_changed` on touched files when configured
20
20
  4. `ReadLints` on edited paths
21
- 5. **App build gate (when enabled):** before advancing `verify`, run:
21
+ 5. **Website build gate (mandatory on create / update / fix):** before advancing `verify`, run:
22
22
  ```bash
23
23
  node .cursor/aaac/scripts/run-engine/verify-website-build.mjs --run-id <run_id>
24
24
  ```
25
- Reads `verify` from `.cursor/aaac/project.config.json`. Skips when `verify.enabled: false` (default on fresh install). When enabled, checks `index_html` static assets and runs the configured build command. `advance-phase.mjs verify` **blocks** until this passes and writes `artifacts/verify.yaml`.
25
+ This checks `index.html` static asset paths resolve under `apps/website/public/` (or project root for Vite dev) and runs `pnpm --filter @ludecker/website build`. `advance-phase.mjs verify` **blocks** until this passes and writes `artifacts/verify.yaml`.
26
26
 
27
27
  ## Fix verify swarm (mandatory on fix verb / fix_mode)
28
28
 
29
- After unit tests, launch **3 parallel** `Task` subagents in **one message**:
29
+ After unit tests, launch **3 parallel** `Task` subagents in **one message**. Each prompt **must** include [_task-prompt-policy.md](../_task-prompt-policy.md) and investigation artifact paths.
30
30
 
31
31
  | # | Agent spec | `subagent_type` | Role |
32
32
  |---|------------|-----------------|------|