@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
|
@@ -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);
|
|
@@ -2,43 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
## Role
|
|
4
4
|
|
|
5
|
-
Run
|
|
5
|
+
Run Playwright E2E checks from domain inventory during verify.
|
|
6
6
|
|
|
7
7
|
## When
|
|
8
8
|
|
|
9
|
-
**Verify phase** for
|
|
9
|
+
**Verify phase** for `create`, `update`, or `fix` verbs when inventory lists Playwright targets.
|
|
10
10
|
|
|
11
|
-
**Report phase** for
|
|
11
|
+
**Report phase** for `check` verbs when project defines contract E2E specs.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Browser smoke runs only when `PLAYWRIGHT_BASE_URL` is set.
|
|
14
14
|
|
|
15
15
|
## Commands
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Use the command from domain inventory. Example:
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
pnpm
|
|
20
|
+
pnpm exec playwright test
|
|
21
|
+
PLAYWRIGHT_BASE_URL=http://localhost:3000 pnpm exec playwright test
|
|
21
22
|
```
|
|
22
23
|
|
|
23
|
-
Optional website smoke (dev server or deployed URL):
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
PLAYWRIGHT_BASE_URL=http://localhost:3000 pnpm --filter @ludecker/aaac test:e2e
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
Interactive debugging:
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
pnpm --filter @ludecker/aaac test:e2e:ui
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## What passes
|
|
36
|
-
|
|
37
|
-
- `runtime-registry.json` command entries match `graph.yaml` `verb_runtime` for the command verb
|
|
38
|
-
- Fix verb includes `investigate_swarm` and `root_cause` before `plan`
|
|
39
|
-
- Check verb has no `execute` or `plan` — pending matches `verb_runtime.check`
|
|
40
|
-
- Skipped browser tests when `PLAYWRIGHT_BASE_URL` is unset (CI default)
|
|
41
|
-
|
|
42
24
|
## Return
|
|
43
25
|
|
|
44
|
-
Exit code, failing spec file names,
|
|
26
|
+
Exit code, failing spec file names, browser smoke failures with route and status.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
## Role
|
|
6
6
|
|
|
7
|
-
Commit and push all pending work to
|
|
7
|
+
Commit and push all pending work to the default branch for this repository.
|
|
8
8
|
|
|
9
9
|
## Inputs (from orchestrator)
|
|
10
10
|
|
|
@@ -16,7 +16,7 @@ Commit and push all pending work to `main` for the Lüdecker monorepo.
|
|
|
16
16
|
|
|
17
17
|
Follow [ship-procedure.md § Git](../skills/shared/platform-release/ship-procedure.md).
|
|
18
18
|
|
|
19
|
-
**Repo check:** `git rev-parse --show-toplevel` must be the
|
|
19
|
+
**Repo check:** `git rev-parse --show-toplevel` must be the project root (not a subdirectory).
|
|
20
20
|
|
|
21
21
|
## Return
|
|
22
22
|
|
|
@@ -4,15 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
Run targeted tests for paths from domain inventory. Report pass/fail with test names.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Defaults
|
|
8
8
|
|
|
9
|
-
From repo root
|
|
9
|
+
From repo root, use the test command in domain inventory when listed (e.g. `pnpm test`, `npm test`, `cargo test`).
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
pnpm typecheck
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
If domain inventory lists vitest/jest targets, run those instead.
|
|
11
|
+
If no inventory entry, run the project's root `package.json` `test` or `typecheck` script when present.
|
|
16
12
|
|
|
17
13
|
## Return
|
|
18
14
|
|