@ludecker/aaac 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/package.json +1 -1
  2. package/src/generators/generate-commands.mjs +17 -9
  3. package/src/run-engine/advance-phase.mjs +152 -1
  4. package/src/run-engine/capability-evidence.mjs +460 -0
  5. package/src/run-engine/gate-write.mjs +14 -2
  6. package/src/run-engine/init-run.mjs +92 -1
  7. package/src/run-engine/lib.mjs +38 -0
  8. package/src/run-engine/record-task.mjs +7 -1
  9. package/src/run-engine/stop-check.mjs +7 -1
  10. package/src/run-engine/verify-website-build.mjs +185 -0
  11. package/templates/cursor/aaac/capabilities/promotion-rules.json +64 -0
  12. package/templates/cursor/aaac/capabilities/registry.json +11 -11
  13. package/templates/cursor/aaac/dispatch.md +2 -2
  14. package/templates/cursor/aaac/enforcement.json +6 -3
  15. package/templates/cursor/aaac/governance/gates.json +3 -1
  16. package/templates/cursor/aaac/graph.project.yaml +4 -204
  17. package/templates/cursor/aaac/layers.md +3 -0
  18. package/templates/cursor/aaac/observability/telemetry.yaml +3 -0
  19. package/templates/cursor/aaac/ontology.md +17 -32
  20. package/templates/cursor/aaac/project.config.json +4 -1
  21. package/templates/cursor/aaac/run/schema.json +5 -1
  22. package/templates/cursor/aaac/scripts/run-engine/advance-phase.mjs +152 -1
  23. package/templates/cursor/aaac/scripts/run-engine/capability-evidence.mjs +460 -0
  24. package/templates/cursor/aaac/scripts/run-engine/gate-write.mjs +14 -2
  25. package/templates/cursor/aaac/scripts/run-engine/init-run.mjs +92 -1
  26. package/templates/cursor/aaac/scripts/run-engine/lib.mjs +38 -0
  27. package/templates/cursor/aaac/scripts/run-engine/record-task.mjs +7 -1
  28. package/templates/cursor/aaac/scripts/run-engine/stop-check.mjs +7 -1
  29. package/templates/cursor/aaac/scripts/run-engine/verify-website-build.mjs +185 -0
  30. package/templates/cursor/aaac/state/capability-stats.json +5 -0
  31. package/templates/cursor/agents/playwright-check-run.md +8 -26
  32. package/templates/cursor/agents/release-git.md +2 -2
  33. package/templates/cursor/agents/unit-test-run.md +3 -7
  34. package/templates/cursor/skills/shared/governance/implementation/SKILL.md +25 -396
  35. package/templates/cursor/skills/shared/platform-release/SKILL.md +22 -19
  36. package/templates/cursor/skills/shared/platform-release/orchestrator/contract.yaml +27 -7
  37. package/templates/cursor/skills/shared/testing/SKILL.md +5 -0
  38. package/templates/cursor/skills/shared/verbs/check/orchestrator/SKILL.md +1 -1
  39. package/templates/cursor/skills/shared/verification/SKILL.md +2 -1
  40. package/templates/docs/agentic_architecture.md +163 -60
@@ -53,10 +53,22 @@ process.stdin.on("end", () => {
53
53
  if (!conversationId) allow();
54
54
 
55
55
  const active = loadActiveRun(conversationId);
56
- if (!active?.run_id || active.status === "completed") allow();
56
+ if (
57
+ !active?.run_id ||
58
+ active.status === "completed" ||
59
+ active.status === "cancelled"
60
+ ) {
61
+ allow();
62
+ }
57
63
 
58
64
  const manifest = loadRunManifest(active.run_id);
59
- if (!manifest || manifest.status === "completed") allow();
65
+ if (
66
+ !manifest ||
67
+ manifest.status === "completed" ||
68
+ manifest.status === "cancelled"
69
+ ) {
70
+ allow();
71
+ }
60
72
  if (manifest.conversation_id && manifest.conversation_id !== conversationId) allow();
61
73
 
62
74
  const enforcement = loadEnforcement();
@@ -11,10 +11,20 @@ import {
11
11
  phaseKind,
12
12
  writeJson,
13
13
  saveActiveRun,
14
+ loadActiveRun,
15
+ loadRunManifest,
16
+ clearActiveRun,
17
+ cancelRunManifest,
18
+ isUserStopIntent,
14
19
  conversationIdFromHook,
15
20
  promptFromHook,
16
21
  } from "./lib.mjs";
17
22
  import { recordLog, recordDecision } from "./log.mjs";
23
+ import {
24
+ resolveCapabilitiesWithRuntime,
25
+ evaluateCapabilityRuntimePolicy,
26
+ loadObjectMaturity,
27
+ } from "./capability-evidence.mjs";
18
28
 
19
29
  async function readStdin() {
20
30
  return new Promise((resolve) => {
@@ -36,6 +46,42 @@ try {
36
46
 
37
47
  const prompt = process.argv[2] ?? promptFromHook(hook);
38
48
  const conversationId = conversationIdFromHook(hook);
49
+
50
+ if (isUserStopIntent(prompt) && conversationId) {
51
+ const active = loadActiveRun(conversationId);
52
+ let cancelledRunId = null;
53
+ if (active?.run_id) {
54
+ const existing = loadRunManifest(active.run_id);
55
+ if (
56
+ existing &&
57
+ existing.status !== "completed" &&
58
+ existing.status !== "cancelled"
59
+ ) {
60
+ cancelRunManifest(existing, prompt.trim());
61
+ recordLog(existing, {
62
+ event: "run_cancelled",
63
+ phase: existing.phase,
64
+ phase_kind: existing.phase_kind,
65
+ detail: `user stop: ${prompt.trim()}`,
66
+ level: "info",
67
+ });
68
+ recordDecision(existing, {
69
+ phase: existing.phase ?? "dispatch",
70
+ decision: "user_stop",
71
+ reason: "User requested stop",
72
+ evidence: prompt.trim(),
73
+ });
74
+ writeJson(`${runDir(active.run_id)}/run.json`, existing);
75
+ cancelledRunId = active.run_id;
76
+ }
77
+ clearActiveRun(conversationId);
78
+ }
79
+ console.log(
80
+ JSON.stringify({ ok: true, aaac: false, cancelled: cancelledRunId }),
81
+ );
82
+ process.exit(0);
83
+ }
84
+
39
85
  const parsed = parseAaacPrompt(prompt);
40
86
 
41
87
  if (!parsed) {
@@ -64,6 +110,14 @@ const runId = `run_${date}_${slugify(parsed.command + (parsed.domain ? `-${parse
64
110
  const entry = registry.commands[parsed.command];
65
111
  fs.mkdirSync(runDir(runId), { recursive: true });
66
112
 
113
+ const runObject = entry.object ?? null;
114
+ const runVerb = entry.verb ?? parsed.command.split("-")[0];
115
+ const objectMaturity = loadObjectMaturity(runObject);
116
+ const capabilitiesResolved = resolveCapabilitiesWithRuntime(runObject, runVerb);
117
+ const capabilityRuntimePolicy = evaluateCapabilityRuntimePolicy(capabilitiesResolved, {
118
+ object_maturity: objectMaturity,
119
+ });
120
+
67
121
  const manifest = {
68
122
  run_id: runId,
69
123
  conversation_id: conversationId,
@@ -84,7 +138,9 @@ const manifest = {
84
138
  artifacts: {},
85
139
  checkpoints: [],
86
140
  log: [],
87
- capabilities_resolved: {},
141
+ capabilities_resolved: capabilitiesResolved,
142
+ capability_runtime: capabilityRuntimePolicy,
143
+ capability_runtime_approved: false,
88
144
  confidence: { architecture: null, requirements: null, scope: null },
89
145
  gates: { stack: entry.gate_stack ?? null, results: {} },
90
146
  swarm: { task_launches_this_phase: 0, phase: pending[0] },
@@ -131,6 +187,41 @@ recordDecision(manifest, {
131
187
  evidence: parsed.raw,
132
188
  });
133
189
 
190
+ for (const [capabilityId, resolution] of Object.entries(manifest.capabilities_resolved)) {
191
+ recordLog(manifest, {
192
+ event: "capability_resolved",
193
+ phase: "dispatch",
194
+ phase_kind: "work",
195
+ detail: `${capabilityId}:${(resolution.providers ?? []).map((p) => p.id).join(",")} state=${resolution.runtime?.state ?? "experimental"}`,
196
+ level: "debug",
197
+ });
198
+ }
199
+
200
+ recordLog(manifest, {
201
+ event: "capability_runtime_evaluated",
202
+ phase: "dispatch",
203
+ phase_kind: "work",
204
+ detail: `action=${capabilityRuntimePolicy.action} maturity=${objectMaturity}`,
205
+ level: "info",
206
+ });
207
+
208
+ if (capabilityRuntimePolicy.action === "warn") {
209
+ recordLog(manifest, {
210
+ event: "capability_runtime_warn",
211
+ phase: "dispatch",
212
+ phase_kind: "work",
213
+ detail: capabilityRuntimePolicy.reasons.join("; "),
214
+ level: "warn",
215
+ });
216
+ }
217
+
218
+ recordDecision(manifest, {
219
+ phase: "dispatch",
220
+ decision: "capability_runtime",
221
+ reason: capabilityRuntimePolicy.action,
222
+ evidence: capabilityRuntimePolicy.reasons.join("; ") || "allow",
223
+ });
224
+
134
225
  recordLog(manifest, {
135
226
  event: "phase_start",
136
227
  phase: pending[0],
@@ -5,6 +5,7 @@ import { fileURLToPath } from "url";
5
5
 
6
6
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
7
  export const CURSOR_ROOT = path.resolve(__dirname, "../../..");
8
+ export const REPO_ROOT = path.resolve(CURSOR_ROOT, "..");
8
9
  export const AAAC_ROOT = path.join(CURSOR_ROOT, "aaac");
9
10
  export const STATE_ROOT = path.join(AAAC_ROOT, "state");
10
11
  export const RUNS_ROOT = path.join(STATE_ROOT, "runs");
@@ -12,6 +13,10 @@ export const ACTIVE_RUN_PATH = path.join(STATE_ROOT, "active-run.json");
12
13
  export const ACTIVE_RUNS_DIR = path.join(STATE_ROOT, "active-runs");
13
14
  export const REGISTRY_PATH = path.join(AAAC_ROOT, "runtime-registry.json");
14
15
  export const ENFORCEMENT_PATH = path.join(AAAC_ROOT, "enforcement.json");
16
+ export const ONTOLOGY_PATH = path.join(AAAC_ROOT, "ontology.json");
17
+ export const CAPABILITY_REGISTRY_PATH = path.join(AAAC_ROOT, "capabilities", "registry.json");
18
+ export const PROMOTION_RULES_PATH = path.join(AAAC_ROOT, "capabilities", "promotion-rules.json");
19
+ export const CAPABILITY_STATS_PATH = path.join(STATE_ROOT, "capability-stats.json");
15
20
 
16
21
  export function readJson(filePath, fallback = null) {
17
22
  try {
@@ -134,3 +139,36 @@ export function phaseKind(phase, registry) {
134
139
  export function promptFromHook(hook) {
135
140
  return hook?.prompt ?? hook?.text ?? hook?.content ?? "";
136
141
  }
142
+
143
+ /** User explicitly asked to halt the current Run (short prompts only). */
144
+ export function isUserStopIntent(text) {
145
+ if (!text || typeof text !== "string") return false;
146
+ const trimmed = text.trim();
147
+ if (trimmed.length > 60) return false;
148
+ return (
149
+ /^(stop|cancel|abort)([.!?]*)$/i.test(trimmed) ||
150
+ /^(please\s+)?(stop|cancel|abort)([.!?]*)$/i.test(trimmed) ||
151
+ /^(stop|cancel|abort)\s+(the\s+)?run([.!?]*)$/i.test(trimmed)
152
+ );
153
+ }
154
+
155
+ export function cancelRunManifest(manifest, evidence = "user_stop") {
156
+ manifest.status = "cancelled";
157
+ manifest.awaiting_approval = false;
158
+ manifest.blocked_reason = null;
159
+ manifest.updated_at = isoNow();
160
+ if (manifest.enforcement) {
161
+ manifest.enforcement.edit_allowed = true;
162
+ }
163
+ return manifest;
164
+ }
165
+
166
+ export function clearActiveRun(conversationId) {
167
+ if (!conversationId) return;
168
+ const filePath = activeRunPath(conversationId);
169
+ try {
170
+ fs.unlinkSync(filePath);
171
+ } catch {
172
+ // already cleared
173
+ }
174
+ }
@@ -34,7 +34,13 @@ process.stdin.on("end", () => {
34
34
  if (!active?.run_id) allow();
35
35
 
36
36
  const manifest = loadRunManifest(active.run_id);
37
- if (!manifest || manifest.status === "completed") allow();
37
+ if (
38
+ !manifest ||
39
+ manifest.status === "completed" ||
40
+ manifest.status === "cancelled"
41
+ ) {
42
+ allow();
43
+ }
38
44
  if (manifest.conversation_id && manifest.conversation_id !== conversationId) allow();
39
45
 
40
46
  manifest.swarm = manifest.swarm ?? {};
@@ -28,7 +28,13 @@ process.stdin.on("end", () => {
28
28
  if (!active?.run_id) process.exit(0);
29
29
 
30
30
  const manifest = loadRunManifest(active.run_id);
31
- if (!manifest || manifest.status === "completed") process.exit(0);
31
+ if (
32
+ !manifest ||
33
+ manifest.status === "completed" ||
34
+ manifest.status === "cancelled"
35
+ ) {
36
+ process.exit(0);
37
+ }
32
38
 
33
39
  const remaining = [manifest.phase, ...(manifest.pending ?? [])].filter(Boolean);
34
40
 
@@ -0,0 +1,185 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Verify app static assets + production build (project overlay).
4
+ * Skips when `.cursor/aaac/project.config.json` has `verify.enabled: false`.
5
+ *
6
+ * Usage:
7
+ * node verify-website-build.mjs [--run-id <run_id>] [--skip-build]
8
+ */
9
+ import fs from "fs";
10
+ import path from "path";
11
+ import { spawnSync } from "child_process";
12
+ import { runDir, isoNow, writeJson, readJson } from "./lib.mjs";
13
+
14
+ const PROJECT_ROOT = process.cwd();
15
+
16
+ const args = process.argv.slice(2);
17
+ const runIdIdx = args.indexOf("--run-id");
18
+ const runId = runIdIdx >= 0 ? args[runIdIdx + 1] : null;
19
+ const skipBuild = args.includes("--skip-build");
20
+
21
+ function loadVerifyConfig() {
22
+ const configPath = path.join(PROJECT_ROOT, ".cursor/aaac/project.config.json");
23
+ const config = readJson(configPath, { verify: { enabled: false } });
24
+ const verify = config.verify ?? { enabled: false };
25
+ if (!verify.enabled) {
26
+ return { enabled: false };
27
+ }
28
+ const appRootRel = verify.app_root ?? "apps/website";
29
+ const indexRel =
30
+ verify.index_html ?? path.join(appRootRel, "index.html").replace(/\\/g, "/");
31
+ const appRoot = path.join(PROJECT_ROOT, appRootRel);
32
+ const indexHtml = path.join(PROJECT_ROOT, indexRel);
33
+ const build = verify.build ?? { command: "pnpm", args: ["run", "build"] };
34
+ return { enabled: true, appRoot, indexHtml, build, appRootRel };
35
+ }
36
+
37
+ const verifyConfig = loadVerifyConfig();
38
+
39
+ const results = {
40
+ status: "pass",
41
+ checked_at: isoNow(),
42
+ static_assets: { status: "pass", missing: [] },
43
+ build: {
44
+ status: skipBuild ? "skipped" : "pending",
45
+ command: null,
46
+ },
47
+ verify_config: verifyConfig.enabled ? "enabled" : "disabled",
48
+ };
49
+
50
+ function fail(section, detail) {
51
+ results.status = "fail";
52
+ if (section === "static_assets") {
53
+ results.static_assets.status = "fail";
54
+ results.static_assets.missing.push(detail);
55
+ } else if (section === "build") {
56
+ results.build.status = "fail";
57
+ results.build.detail = detail;
58
+ }
59
+ console.error(`[verify-website-build] FAIL ${section}: ${detail}`);
60
+ }
61
+
62
+ function resolveRootAsset(assetPath, websiteRoot, appRootRel) {
63
+ const rel = assetPath.replace(/^\//, "");
64
+ const candidates = [
65
+ path.join(websiteRoot, "public", rel),
66
+ path.join(websiteRoot, rel),
67
+ ];
68
+ for (const candidate of candidates) {
69
+ if (fs.existsSync(candidate)) {
70
+ return candidate;
71
+ }
72
+ }
73
+ return null;
74
+ }
75
+
76
+ function checkStaticAssets(indexHtml, websiteRoot, appRootRel) {
77
+ if (!fs.existsSync(indexHtml)) {
78
+ fail("static_assets", `missing index.html at ${indexHtml}`);
79
+ return;
80
+ }
81
+
82
+ const html = fs.readFileSync(indexHtml, "utf8");
83
+ const rootRefs = [
84
+ ...html.matchAll(/\b(?:href|src)="(\/[^"#?]+)"/g),
85
+ ].map((match) => match[1]);
86
+
87
+ const seen = new Set();
88
+ for (const ref of rootRefs) {
89
+ if (seen.has(ref) || ref.startsWith("//")) continue;
90
+ seen.add(ref);
91
+
92
+ const resolved = resolveRootAsset(ref, websiteRoot, appRootRel);
93
+ if (!resolved) {
94
+ fail(
95
+ "static_assets",
96
+ `${ref} not found under ${appRootRel}/public/ or ${appRootRel}/`,
97
+ );
98
+ }
99
+ }
100
+ }
101
+
102
+ function runBuild(build) {
103
+ if (skipBuild) return;
104
+
105
+ const command = build.command ?? "pnpm";
106
+ const buildArgs = build.args ?? ["run", "build"];
107
+ results.build.command = [command, ...buildArgs].join(" ");
108
+
109
+ const proc = spawnSync(command, buildArgs, {
110
+ cwd: build.cwd ? path.join(PROJECT_ROOT, build.cwd) : PROJECT_ROOT,
111
+ encoding: "utf8",
112
+ env: { ...process.env, CI: "1" },
113
+ });
114
+
115
+ if (proc.status !== 0) {
116
+ const detail = [proc.stderr, proc.stdout].filter(Boolean).join("\n").trim();
117
+ results.build.status = "fail";
118
+ results.build.exit_code = proc.status ?? 1;
119
+ fail("build", detail || `exit ${proc.status}`);
120
+ return;
121
+ }
122
+
123
+ results.build.status = "pass";
124
+ results.build.exit_code = 0;
125
+ }
126
+
127
+ function writeArtifact() {
128
+ if (!runId) return;
129
+
130
+ const artifactDir = path.join(runDir(runId), "artifacts");
131
+ fs.mkdirSync(artifactDir, { recursive: true });
132
+
133
+ const yaml = [
134
+ `status: ${results.status}`,
135
+ `checked_at: ${results.checked_at}`,
136
+ `verify_config: ${results.verify_config}`,
137
+ "static_assets:",
138
+ ` status: ${results.static_assets.status}`,
139
+ ` missing: ${JSON.stringify(results.static_assets.missing)}`,
140
+ "build:",
141
+ ` status: ${results.build.status}`,
142
+ results.build.command ? ` command: ${JSON.stringify(results.build.command)}` : null,
143
+ results.build.exit_code != null ? ` exit_code: ${results.build.exit_code}` : null,
144
+ results.build.detail ? ` detail: ${JSON.stringify(results.build.detail)}` : null,
145
+ ]
146
+ .filter(Boolean)
147
+ .join("\n");
148
+
149
+ fs.writeFileSync(path.join(artifactDir, "verify.yaml"), `${yaml}\n`);
150
+
151
+ const manifestPath = path.join(runDir(runId), "run.json");
152
+ try {
153
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
154
+ manifest.artifacts = manifest.artifacts ?? {};
155
+ manifest.artifacts.verify = results;
156
+ manifest.updated_at = isoNow();
157
+ writeJson(manifestPath, manifest);
158
+ } catch {
159
+ // run.json may not exist in standalone invocations
160
+ }
161
+ }
162
+
163
+ if (!verifyConfig.enabled) {
164
+ results.static_assets.status = "skipped";
165
+ results.build.status = "skipped";
166
+ results.build.command = null;
167
+ writeArtifact();
168
+ console.log(JSON.stringify({ ok: true, skipped: true, reason: "verify.disabled", ...results }));
169
+ process.exit(0);
170
+ }
171
+
172
+ results.build.command = skipBuild
173
+ ? null
174
+ : [verifyConfig.build.command, ...(verifyConfig.build.args ?? [])].join(" ");
175
+
176
+ checkStaticAssets(
177
+ verifyConfig.indexHtml,
178
+ verifyConfig.appRoot,
179
+ verifyConfig.appRootRel,
180
+ );
181
+ runBuild(verifyConfig.build);
182
+ writeArtifact();
183
+
184
+ console.log(JSON.stringify({ ok: results.status === "pass", ...results }));
185
+ process.exit(results.status === "pass" ? 0 : 1);
@@ -0,0 +1,64 @@
1
+ {
2
+ "version": 1,
3
+ "description": "Evidence-driven capability lifecycle promotion thresholds. State belongs to capability, not provider.",
4
+ "states": ["experimental", "validated", "trusted", "canonical", "deprecated"],
5
+ "default_state": "experimental",
6
+ "thresholds": {
7
+ "validated": {
8
+ "min_invocations": 10
9
+ },
10
+ "trusted": {
11
+ "min_invocations": 25,
12
+ "min_success_rate": 0.8,
13
+ "max_rollback_rate": 0.05,
14
+ "max_gate_failure_rate": 0.2
15
+ },
16
+ "canonical": {
17
+ "min_invocations": 100,
18
+ "min_success_rate": 0.95,
19
+ "max_rollback_rate": 0.01,
20
+ "max_gate_failure_rate": 0.1,
21
+ "manual_approval": true
22
+ }
23
+ },
24
+ "demotion": {
25
+ "from_trusted": {
26
+ "min_invocations": 20,
27
+ "min_success_rate_below": 0.7
28
+ },
29
+ "to_deprecated": {
30
+ "manual_only": true
31
+ }
32
+ },
33
+ "fitness_scoring": {
34
+ "pass": 100,
35
+ "warning": 75,
36
+ "fail": 0
37
+ },
38
+ "runtime": {
39
+ "by_state": {
40
+ "experimental": {
41
+ "warn": true,
42
+ "require_approval_on": ["critical", "protected"]
43
+ },
44
+ "validated": {},
45
+ "trusted": {},
46
+ "canonical": {},
47
+ "deprecated": {
48
+ "block_execute": true
49
+ }
50
+ },
51
+ "evidence_triggers": [
52
+ {
53
+ "min_invocations": 5,
54
+ "min_success_rate_below": 0.5,
55
+ "action": "require_approval"
56
+ },
57
+ {
58
+ "min_invocations": 10,
59
+ "min_avg_fitness_below": 60,
60
+ "action": "require_approval"
61
+ }
62
+ ]
63
+ }
64
+ }
@@ -2,15 +2,15 @@
2
2
  "version": 2,
3
3
  "capabilities": {
4
4
  "ui-design": {
5
- "description": "Design tokens, component CSS, presentational UI",
5
+ "description": "Presentational components, token-based styling",
6
6
  "providers": [
7
7
  { "id": "component", "type": "skill", "path": "skills/shared/component" }
8
8
  ]
9
9
  },
10
10
  "ux-design": {
11
- "description": "User flows, readability, navigation clarity",
11
+ "description": "Editorial readability, navigation clarity",
12
12
  "providers": [
13
- { "id": "workflow", "type": "skill", "path": "skills/shared/workflow" }
13
+ { "id": "documentation", "type": "skill", "path": "skills/shared/documentation" }
14
14
  ]
15
15
  },
16
16
  "api-design": {
@@ -20,22 +20,21 @@
20
20
  ]
21
21
  },
22
22
  "database-design": {
23
- "description": "Schema, migrations, persistence contracts",
23
+ "description": "Schema, migrations, persistence boundaries",
24
24
  "providers": [
25
- { "id": "schema", "type": "skill", "path": "skills/shared/schema" },
26
- { "id": "migration", "type": "skill", "path": "skills/shared/migration" }
25
+ { "id": "schema", "type": "skill", "path": "skills/shared/schema" }
27
26
  ]
28
27
  },
29
28
  "security": {
30
- "description": "Auth, secrets, access control extend with project skills",
29
+ "description": "Auth, secrets, access control at boundaries",
31
30
  "providers": [
32
31
  { "id": "architecture", "type": "skill", "path": "skills/shared/architecture" }
33
32
  ]
34
33
  },
35
34
  "infrastructure": {
36
- "description": "Deploy, hosting, environment",
35
+ "description": "Deploy, hosting, environment configuration",
37
36
  "providers": [
38
- { "id": "platform-release", "type": "skill", "path": "skills/shared/platform-release" }
37
+ { "id": "integration", "type": "skill", "path": "skills/shared/integration" }
39
38
  ]
40
39
  },
41
40
  "layer-boundaries": {
@@ -75,7 +74,7 @@
75
74
  ]
76
75
  },
77
76
  "integration-model": {
78
- "description": "API routes, webhooks, MCP adapters",
77
+ "description": "API routes, webhooks, external adapters",
79
78
  "providers": [
80
79
  { "id": "integration", "type": "skill", "path": "skills/shared/integration" }
81
80
  ]
@@ -101,6 +100,7 @@
101
100
  },
102
101
  "resolution": {
103
102
  "graph_skill_keys": "providers where type=skill → id maps to graph skills key",
104
- "run_record": "all providers including type=mcp recorded on Run.capabilities_resolved and decisions"
103
+ "run_record": "all providers including type=mcp recorded on Run.capabilities_resolved and decisions",
104
+ "lifecycle": "cross-run state in state/capability-stats.json; promotion thresholds in promotion-rules.json; updated by capability-evidence.mjs after each completed Run"
105
105
  }
106
106
  }
@@ -51,7 +51,7 @@ Read [graph.yaml](graph.yaml) and [ontology.json](ontology.json).
51
51
  - **Lifecycle (work):** [lifecycle/lifecycle.json](lifecycle/lifecycle.json) `verbs.*.work_phases`
52
52
  - **Gates (approval):** [governance/gates.json](governance/gates.json) — composed into runtime per `verb_runtime` in graph
53
53
  - **Maturity:** read `object_maturity.<object>` and apply `maturity_rules.<level>` (may require extra gate phases)
54
- - **Capabilities:** resolve `object_capabilities.<object>` via [capabilities/registry.json](capabilities/registry.json) — record all providers (skill + mcp) on Run
54
+ - **Capabilities:** resolve `object_capabilities.<object>` via [capabilities/registry.json](capabilities/registry.json) — `init-run.mjs` records providers on `Run.capabilities_resolved`; on completion `capability-evidence.mjs` aggregates evidence into [state/capability-stats.json](state/capability-stats.json) and evaluates [capabilities/promotion-rules.json](capabilities/promotion-rules.json)
55
55
  - **Dependencies:** [dependencies.yaml](dependencies.yaml)
56
56
  - **Fitness:** [fitness-functions.yaml](fitness-functions.yaml) — includes `minimal_complexity` for create/update/fix
57
57
  - **Complexity:** [complexity.yaml](complexity.yaml) + [minimal-complexity.md](../policies/minimal-complexity.md) for create/update/fix
@@ -144,7 +144,7 @@ Do **not** proceed until user approves in chat. On approval: log decision, set `
144
144
  1. **discover** — 4–6 parallel Task agents per [discovery/SKILL.md](../skills/shared/discovery/SKILL.md)
145
145
  2. **investigate_swarm** — 7 parallel Task agents per investigation Mode A — **one message**
146
146
  3. **root_cause** — artifact required; confidence ≥ 0.7 before plan
147
- 4. **verify** — fix verify swarm (3 parallel) per [testing/SKILL.md](../skills/shared/testing/SKILL.md); fail if `repro_status: not_fixed`
147
+ 4. **verify** — fix verify swarm (3 parallel) per [testing/SKILL.md](../skills/shared/testing/SKILL.md); **website build gate** (`verify-website-build.mjs`) must pass for create/update/fix; fail if `repro_status: not_fixed`
148
148
 
149
149
  Skipping swarms because the issue "looks simple" is a **contract violation** for `fix-module` / `fix-bug` / `fix_mode`.
150
150
 
@@ -1,8 +1,9 @@
1
1
  {
2
- "version": 1,
2
+ "version": 2,
3
3
  "description": "AAAC runtime enforcement — SSOT for hooks and run engine",
4
4
  "edit_phases": ["execute", "sync_inventory", "persist", "write"],
5
- "artifact_write_phases": ["plan", "report"],
5
+ "artifact_write_phases": ["plan", "report", "verify"],
6
+ "verify_verbs": ["create", "update", "fix"],
6
7
  "swarm_min_agents": {
7
8
  "discover": 4,
8
9
  "investigate_swarm": 7,
@@ -13,10 +14,12 @@
13
14
  "investigate_swarm": ["artifacts/investigation.md"],
14
15
  "root_cause": ["artifacts/root_cause.yaml"],
15
16
  "plan": ["artifacts/plan.yaml"],
17
+ "verify": ["artifacts/verify.yaml"],
16
18
  "report": ["artifacts/report.md"]
17
19
  },
18
20
  "allowed_path_prefixes": {
19
- "run_artifacts": [".cursor/aaac/state/runs/", "aaac/state/runs/"]
21
+ "run_artifacts": [".cursor/aaac/state/runs/", "aaac/state/runs/"],
22
+ "write_article": [".cursor/write-article-runs/"]
20
23
  },
21
24
  "fix_commands": ["fix-module", "fix-bug", "module-fix", "bug-fix"]
22
25
  }
@@ -29,7 +29,9 @@
29
29
  "plan missing requirement_map or unjustified create",
30
30
  "impact proceed false",
31
31
  "rollback unverified",
32
- "user intent contains requires approval"
32
+ "user intent contains requires approval",
33
+ "capability runtime require_approval",
34
+ "capability state deprecated"
33
35
  ],
34
36
  "run_fields": {
35
37
  "status": "blocked",