@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.
- package/package.json +1 -1
- package/src/generators/generate-commands.mjs +17 -9
- package/src/run-engine/advance-phase.mjs +152 -1
- package/src/run-engine/capability-evidence.mjs +460 -0
- package/src/run-engine/gate-write.mjs +14 -2
- package/src/run-engine/init-run.mjs +92 -1
- package/src/run-engine/lib.mjs +38 -0
- package/src/run-engine/record-task.mjs +7 -1
- package/src/run-engine/stop-check.mjs +7 -1
- package/src/run-engine/verify-website-build.mjs +185 -0
- package/templates/cursor/aaac/capabilities/promotion-rules.json +64 -0
- package/templates/cursor/aaac/capabilities/registry.json +11 -11
- package/templates/cursor/aaac/dispatch.md +2 -2
- package/templates/cursor/aaac/enforcement.json +6 -3
- package/templates/cursor/aaac/governance/gates.json +3 -1
- package/templates/cursor/aaac/graph.project.yaml +4 -204
- package/templates/cursor/aaac/layers.md +3 -0
- package/templates/cursor/aaac/observability/telemetry.yaml +3 -0
- package/templates/cursor/aaac/ontology.md +17 -32
- package/templates/cursor/aaac/project.config.json +4 -1
- package/templates/cursor/aaac/run/schema.json +5 -1
- package/templates/cursor/aaac/scripts/run-engine/advance-phase.mjs +152 -1
- package/templates/cursor/aaac/scripts/run-engine/capability-evidence.mjs +460 -0
- package/templates/cursor/aaac/scripts/run-engine/gate-write.mjs +14 -2
- package/templates/cursor/aaac/scripts/run-engine/init-run.mjs +92 -1
- package/templates/cursor/aaac/scripts/run-engine/lib.mjs +38 -0
- package/templates/cursor/aaac/scripts/run-engine/record-task.mjs +7 -1
- package/templates/cursor/aaac/scripts/run-engine/stop-check.mjs +7 -1
- package/templates/cursor/aaac/scripts/run-engine/verify-website-build.mjs +185 -0
- package/templates/cursor/aaac/state/capability-stats.json +5 -0
- package/templates/cursor/agents/playwright-check-run.md +8 -26
- package/templates/cursor/agents/release-git.md +2 -2
- package/templates/cursor/agents/unit-test-run.md +3 -7
- package/templates/cursor/skills/shared/governance/implementation/SKILL.md +25 -396
- package/templates/cursor/skills/shared/platform-release/SKILL.md +22 -19
- package/templates/cursor/skills/shared/platform-release/orchestrator/contract.yaml +27 -7
- package/templates/cursor/skills/shared/testing/SKILL.md +5 -0
- package/templates/cursor/skills/shared/verbs/check/orchestrator/SKILL.md +1 -1
- package/templates/cursor/skills/shared/verification/SKILL.md +2 -1
- 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 (
|
|
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 (
|
|
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],
|
package/src/run-engine/lib.mjs
CHANGED
|
@@ -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 (
|
|
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 (
|
|
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": "
|
|
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": "
|
|
11
|
+
"description": "Editorial readability, navigation clarity",
|
|
12
12
|
"providers": [
|
|
13
|
-
{ "id": "
|
|
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
|
|
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
|
|
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": "
|
|
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,
|
|
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) —
|
|
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":
|
|
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",
|