@ludecker/aaac 1.1.5 → 1.2.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.
Files changed (104) hide show
  1. package/README.md +27 -12
  2. package/package.json +9 -9
  3. package/src/cli.mjs +19 -7
  4. package/src/generators/generate-commands.mjs +25 -1
  5. package/src/generators/generate-graph.mjs +9 -1
  6. package/src/lib/install.mjs +13 -1
  7. package/src/lib/sweep-project-docs.mjs +348 -0
  8. package/src/run-engine/advance-phase.mjs +23 -0
  9. package/src/run-engine/debug-run.mjs +0 -0
  10. package/src/run-engine/gate-write.mjs +13 -0
  11. package/src/run-engine/lib.mjs +153 -5
  12. package/src/run-engine/log-dump.mjs +0 -0
  13. package/src/run-engine/log-trace.mjs +0 -0
  14. package/templates/cursor/aaac/enforcement.json +96 -5
  15. package/templates/cursor/aaac/graph.project.yaml +44 -5
  16. package/templates/cursor/aaac/lifecycle/lifecycle.json +26 -0
  17. package/templates/cursor/aaac/lifecycle/phases.json +9 -1
  18. package/templates/cursor/aaac/ontology.json +1 -0
  19. package/templates/cursor/aaac/project.config.json +36 -0
  20. package/templates/cursor/aaac/scripts/remediation/auto-check-swarm-synthesis.mjs +75 -0
  21. package/templates/cursor/aaac/scripts/remediation/auto-dispatch-queue-from-health.mjs +78 -0
  22. package/templates/cursor/aaac/scripts/remediation/bootstrap-autonomous.mjs +113 -0
  23. package/templates/cursor/aaac/scripts/remediation/capture-verify-baseline.mjs +66 -0
  24. package/templates/cursor/aaac/scripts/remediation/capture-wave-snapshot.mjs +79 -0
  25. package/templates/cursor/aaac/scripts/remediation/check-swarm-raw.template.json +26 -0
  26. package/templates/cursor/aaac/scripts/remediation/classify-fallow-issues.mjs +77 -0
  27. package/templates/cursor/aaac/scripts/remediation/classify-verify-failure.mjs +176 -0
  28. package/templates/cursor/aaac/scripts/remediation/compute-satisfaction.mjs +344 -0
  29. package/templates/cursor/aaac/scripts/remediation/debt-sweep-gate.mjs +202 -0
  30. package/templates/cursor/aaac/scripts/remediation/dispatch-rules.json +44 -0
  31. package/templates/cursor/aaac/scripts/remediation/fallow-fp-rules.json +87 -0
  32. package/templates/cursor/aaac/scripts/remediation/fallow-scan.mjs +219 -0
  33. package/templates/cursor/aaac/scripts/remediation/handle-yield.mjs +240 -0
  34. package/templates/cursor/aaac/scripts/remediation/init-campaign.mjs +211 -0
  35. package/templates/cursor/aaac/scripts/remediation/lib/autonomous-mode.mjs +63 -0
  36. package/templates/cursor/aaac/scripts/remediation/lib/campaign-focus.mjs +87 -0
  37. package/templates/cursor/aaac/scripts/remediation/lib/fallow-classifier.mjs +190 -0
  38. package/templates/cursor/aaac/scripts/remediation/lib/fallow-health-targets.mjs +56 -0
  39. package/templates/cursor/aaac/scripts/remediation/lib/fallow-metrics.mjs +119 -0
  40. package/templates/cursor/aaac/scripts/remediation/lib/invoke-cursor-agent.mjs +51 -0
  41. package/templates/cursor/aaac/scripts/remediation/lib/reconcile-run-manifest.mjs +41 -0
  42. package/templates/cursor/aaac/scripts/remediation/lib/regression-analysis.mjs +55 -0
  43. package/templates/cursor/aaac/scripts/remediation/lib/remediation-config.mjs +69 -0
  44. package/templates/cursor/aaac/scripts/remediation/lib/remediation-progress.mjs +58 -0
  45. package/templates/cursor/aaac/scripts/remediation/lib/remediation-watch-loop.mjs +168 -0
  46. package/templates/cursor/aaac/scripts/remediation/lib/runner-exec.mjs +156 -0
  47. package/templates/cursor/aaac/scripts/remediation/lib/runner-state.mjs +145 -0
  48. package/templates/cursor/aaac/scripts/remediation/lib/verify-metrics.mjs +205 -0
  49. package/templates/cursor/aaac/scripts/remediation/merge-check-swarm.mjs +257 -0
  50. package/templates/cursor/aaac/scripts/remediation/plan-waves-from-queue.mjs +85 -0
  51. package/templates/cursor/aaac/scripts/remediation/prepare-check-context.mjs +148 -0
  52. package/templates/cursor/aaac/scripts/remediation/record-fallow-fp.mjs +107 -0
  53. package/templates/cursor/aaac/scripts/remediation/record-iteration-step.mjs +56 -0
  54. package/templates/cursor/aaac/scripts/remediation/remediation-cli.mjs +157 -0
  55. package/templates/cursor/aaac/scripts/remediation/remediation-cursor-watch.sh +10 -0
  56. package/templates/cursor/aaac/scripts/remediation/remediation-runner-daemon.sh +13 -0
  57. package/templates/cursor/aaac/scripts/remediation/remediation-runner.mjs +748 -0
  58. package/templates/cursor/aaac/scripts/remediation/remediation-yield-watcher.mjs +40 -0
  59. package/templates/cursor/aaac/scripts/remediation/remediator-gate.mjs +405 -0
  60. package/templates/cursor/aaac/scripts/remediation/repair-fallow-start-baseline.mjs +118 -0
  61. package/templates/cursor/aaac/scripts/remediation/runner-health-check.mjs +164 -0
  62. package/templates/cursor/aaac/scripts/remediation/satisfaction-loop-gate.mjs +286 -0
  63. package/templates/cursor/aaac/scripts/remediation/validate-campaign-complete.mjs +191 -0
  64. package/templates/cursor/aaac/scripts/remediation/verify-remediation-iteration.mjs +112 -0
  65. package/templates/cursor/aaac/scripts/run-engine/advance-phase.mjs +23 -0
  66. package/templates/cursor/aaac/scripts/run-engine/debug-run.mjs +0 -0
  67. package/templates/cursor/aaac/scripts/run-engine/gate-write.mjs +13 -0
  68. package/templates/cursor/aaac/scripts/run-engine/lib.mjs +153 -5
  69. package/templates/cursor/aaac/scripts/run-engine/log-dump.mjs +0 -0
  70. package/templates/cursor/aaac/scripts/run-engine/log-trace.mjs +0 -0
  71. package/templates/cursor/agents/doc-conformance.md +25 -0
  72. package/templates/cursor/agents/implementation-review.md +21 -0
  73. package/templates/cursor/agents/remediation-check-app-inventory.md +32 -0
  74. package/templates/cursor/agents/remediation-check-app-ssot.md +24 -0
  75. package/templates/cursor/agents/remediation-check-app-trace.md +29 -0
  76. package/templates/cursor/agents/remediation-check-architecture-boundaries.md +21 -0
  77. package/templates/cursor/agents/remediation-check-architecture-decomposition.md +25 -0
  78. package/templates/cursor/agents/remediation-check-architecture-deps.md +23 -0
  79. package/templates/cursor/agents/remediation-check-risk.md +37 -0
  80. package/templates/cursor/agents/remediation-e2e-gate.md +30 -0
  81. package/templates/cursor/agents/remediation-remediator.md +69 -0
  82. package/templates/cursor/agents/test-author.md +27 -0
  83. package/templates/cursor/commands/remediate-app.md +212 -0
  84. package/templates/cursor/hooks/aaac-before-submit.sh +0 -0
  85. package/templates/cursor/hooks/aaac-pre-tool.sh +0 -0
  86. package/templates/cursor/hooks/aaac-stop.sh +0 -0
  87. package/templates/cursor/hooks/aaac-subagent-start.sh +0 -0
  88. package/templates/cursor/rules/aaac-enforcement.mdc +10 -3
  89. package/templates/cursor/skills/shared/execution/SKILL.md +7 -3
  90. package/templates/cursor/skills/shared/governance/implementation/SKILL.md +396 -28
  91. package/templates/cursor/skills/shared/implementation-review/SKILL.md +49 -0
  92. package/templates/cursor/skills/shared/planning/SKILL.md +5 -0
  93. package/templates/cursor/skills/shared/remediation/SKILL.md +51 -0
  94. package/templates/cursor/skills/shared/remediation/babysit/SKILL.md +223 -0
  95. package/templates/cursor/skills/shared/remediation/check-swarm/SKILL.md +114 -0
  96. package/templates/cursor/skills/shared/remediation/orchestrator/SKILL.md +275 -0
  97. package/templates/cursor/skills/shared/remediation/orchestrator/contract.yaml +116 -0
  98. package/templates/cursor/skills/shared/test-authoring/SKILL.md +58 -0
  99. package/templates/cursor/skills/shared/testing/SKILL.md +6 -0
  100. package/templates/cursor/skills/shared/verbs/create/orchestrator/SKILL.md +5 -3
  101. package/templates/cursor/skills/shared/verbs/fix/orchestrator/SKILL.md +5 -3
  102. package/templates/cursor/skills/shared/verbs/update/orchestrator/SKILL.md +5 -3
  103. package/templates/cursor/skills/shared/verification/SKILL.md +5 -3
  104. package/templates/docs/agentic_architecture.md +169 -97
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Multi-layer verification gate for remediation campaigns.
4
+ *
5
+ * Modes:
6
+ * wave — fast gate after each fix wave (typecheck, vitest, go test)
7
+ * iteration — full gate (+ build + Playwright)
8
+ * debt — strict full gate (same layers as iteration; used by debt_sweep)
9
+ * strict — alias for debt
10
+ *
11
+ * Usage:
12
+ * node verify-remediation-iteration.mjs --campaign-id <id> --iteration <n> \
13
+ * --mode wave|iteration|debt [--run-id <run_id>] [--label <suffix>]
14
+ */
15
+ import fs from "fs";
16
+ import path from "path";
17
+ import { spawnSync } from "child_process";
18
+ import { fileURLToPath } from "url";
19
+ import { REPO_ROOT, isoNow, writeJson, runDir } from "../run-engine/lib.mjs";
20
+ import {
21
+ runVerifySteps,
22
+ writeVerifyLogs,
23
+ } from "./lib/verify-metrics.mjs";
24
+
25
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
26
+ const CAMPAIGNS_ROOT = path.join(REPO_ROOT, ".cursor/aaac/state/campaigns");
27
+
28
+ function parseArgs(argv) {
29
+ const out = { campaignId: null, iteration: 0, mode: "iteration", runId: null, label: null };
30
+ for (let i = 0; i < argv.length; i++) {
31
+ const a = argv[i];
32
+ if (a === "--campaign-id") out.campaignId = argv[++i];
33
+ else if (a === "--iteration") out.iteration = Number(argv[++i]);
34
+ else if (a === "--mode") out.mode = argv[++i];
35
+ else if (a === "--run-id") out.runId = argv[++i];
36
+ else if (a === "--label") out.label = argv[++i];
37
+ }
38
+ return out;
39
+ }
40
+
41
+ function appendJournal(campaignId, text) {
42
+ fs.appendFileSync(path.join(CAMPAIGNS_ROOT, campaignId, "journal.md"), text);
43
+ }
44
+
45
+ const args = parseArgs(process.argv.slice(2));
46
+ if (!args.campaignId) {
47
+ console.error("verify-remediation-iteration: --campaign-id required");
48
+ process.exit(2);
49
+ }
50
+
51
+ const verifyMode = args.mode === "strict" ? "debt" : args.mode;
52
+ const iterDir = path.join(CAMPAIGNS_ROOT, args.campaignId, "iterations", String(args.iteration));
53
+ const logDir = path.join(iterDir, "verify-logs");
54
+ fs.mkdirSync(iterDir, { recursive: true });
55
+
56
+ const stepMode = verifyMode === "wave" ? "wave" : "debt";
57
+ const report = await runVerifySteps(stepMode);
58
+ report.iteration = args.iteration;
59
+ report.campaign_id = args.campaignId;
60
+ report.label = args.label;
61
+
62
+ writeVerifyLogs(report, logDir, args.label ?? verifyMode);
63
+
64
+ const outName =
65
+ args.label != null
66
+ ? `verify-${args.label}.json`
67
+ : verifyMode === "wave"
68
+ ? "verify-wave.json"
69
+ : verifyMode === "debt"
70
+ ? "verify-debt.json"
71
+ : "verify-iteration.json";
72
+ const outPath = path.join(iterDir, outName);
73
+ writeJson(outPath, report);
74
+
75
+ if (report.status === "fail") {
76
+ const classify = spawnSync(
77
+ process.execPath,
78
+ [
79
+ path.join(__dirname, "classify-verify-failure.mjs"),
80
+ "--report",
81
+ outPath,
82
+ "--campaign-id",
83
+ args.campaignId,
84
+ "--iteration",
85
+ String(args.iteration),
86
+ ],
87
+ { encoding: "utf8" },
88
+ );
89
+ try {
90
+ const line = classify.stdout.trim().split("\n").pop();
91
+ report.failure_classification = JSON.parse(line)?.classification ?? null;
92
+ writeJson(outPath, report);
93
+ } catch {
94
+ report.failure_classification = null;
95
+ }
96
+ }
97
+
98
+ appendJournal(
99
+ args.campaignId,
100
+ `- Verify **${verifyMode}**${args.label ? ` (${args.label})` : ""} iter ${args.iteration}: **${report.status.toUpperCase()}** — total_errors=${report.metrics?.total_errors ?? 0}\n`,
101
+ );
102
+
103
+ if (args.runId) {
104
+ const artifactName = args.label
105
+ ? `verify_${args.label}_iter_${args.iteration}.json`
106
+ : `verify_${verifyMode}_iter_${args.iteration}.json`;
107
+ writeJson(path.join(runDir(args.runId), "artifacts", artifactName), report);
108
+ }
109
+
110
+ const strictPass = report.status === "pass" && (report.metrics?.total_errors ?? 0) === 0;
111
+ console.log(JSON.stringify({ ok: strictPass, report, strict_pass: strictPass }));
112
+ process.exit(strictPass ? 0 : 1);
@@ -17,6 +17,7 @@ import {
17
17
  isEditPhase,
18
18
  isGatePhase,
19
19
  resolveSwarmMinimum,
20
+ validatePhaseArtifactContent,
20
21
  writeJson,
21
22
  saveActiveRun,
22
23
  } from "./lib.mjs";
@@ -132,6 +133,28 @@ for (const rel of requiredArtifacts) {
132
133
  }
133
134
  }
134
135
 
136
+ if (!force) {
137
+ const contentGate = validatePhaseArtifactContent(
138
+ runId,
139
+ completedPhase,
140
+ manifest,
141
+ enforcement,
142
+ );
143
+ if (!contentGate.ok) {
144
+ recordLog(manifest, {
145
+ event: "gate_fail",
146
+ phase: completedPhase,
147
+ phase_kind: manifest.phase_kind,
148
+ detail: contentGate.reason,
149
+ level: "warn",
150
+ });
151
+ manifest.updated_at = isoNow();
152
+ writeJson(manifestPath, manifest);
153
+ console.error(contentGate.reason);
154
+ process.exit(2);
155
+ }
156
+ }
157
+
135
158
  const now = isoNow();
136
159
  const completedIsGate = isGatePhase(completedPhase, registry);
137
160
 
@@ -7,6 +7,7 @@ import {
7
7
  loadEnforcement,
8
8
  isEditPhase,
9
9
  isArtifactPath,
10
+ isPathAllowedForPhase,
10
11
  conversationIdFromHook,
11
12
  runDir,
12
13
  writeJson,
@@ -86,6 +87,18 @@ process.stdin.on("end", () => {
86
87
  }
87
88
 
88
89
  if (isEditPhase(manifest.phase, enforcement)) {
90
+ if (filePath && !isPathAllowedForPhase(filePath, manifest.phase, enforcement)) {
91
+ persistEditEvent(
92
+ manifest,
93
+ active.run_id,
94
+ "edit_denied",
95
+ `${toolName} path not allowed in phase ${manifest.phase}: ${filePath}`,
96
+ );
97
+ deny(
98
+ `AAAC: ${manifest.phase} phase cannot edit this path. Run: ${active.run_id}`,
99
+ `Phase "${manifest.phase}" scope violation${filePath ? `: ${filePath}` : ""}. Use test_execute for tests; execute for prod code only.`,
100
+ );
101
+ }
89
102
  persistEditEvent(manifest, active.run_id, "edit_allowed", `${toolName} in phase ${manifest.phase}`);
90
103
  allow();
91
104
  }
@@ -123,6 +123,28 @@ export function isEditPhase(phase, enforcement) {
123
123
  return enforcement.edit_phases.includes(phase);
124
124
  }
125
125
 
126
+ /** Test/spec file paths — used for writer vs tester phase scoping. */
127
+ export function isTestPath(filePath) {
128
+ if (!filePath) return false;
129
+ const normalized = filePath.replace(/\\/g, "/");
130
+ return (
131
+ /\.(test|spec)\.(mjs|cjs|js|ts|tsx)$/.test(normalized) ||
132
+ /(?:^|\/)__tests__(?:\/|$)/.test(normalized) ||
133
+ /(?:^|\/)tests\/(?:unit|integration|e2e|fixtures)\//.test(normalized)
134
+ );
135
+ }
136
+
137
+ /** Phase-scoped edit rules from enforcement.phase_edit_scopes (v3+). */
138
+ export function isPathAllowedForPhase(filePath, phase, enforcement) {
139
+ if (!filePath) return true;
140
+ const scopes = enforcement.phase_edit_scopes?.[phase];
141
+ if (!scopes) return true;
142
+ const isTest = isTestPath(filePath);
143
+ if (scopes.deny_test_paths && isTest) return false;
144
+ if (scopes.test_paths_only && !isTest) return false;
145
+ return true;
146
+ }
147
+
126
148
  export function isArtifactPath(filePath, enforcement) {
127
149
  const normalized = filePath.replace(/\\/g, "/");
128
150
  const prefixes = [
@@ -138,11 +160,22 @@ export function phaseKind(phase, registry) {
138
160
 
139
161
  /** Swarm minimum for completed phase — check verb uses check_swarm on discover. */
140
162
  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;
163
+ const mutating = enforcement.mutating_verbs ?? ["create", "update", "fix"];
164
+ const isMutating =
165
+ mutating.includes(manifest.verb) ||
166
+ enforcement.fix_commands?.includes(manifest.command);
167
+
168
+ if (completedPhase === "verify" && isMutating) {
169
+ return (
170
+ enforcement.swarm_min_agents?.verify ??
171
+ enforcement.swarm_min_agents?.verify_fix
172
+ );
173
+ }
174
+ if (completedPhase === "test_execute" && isMutating) {
175
+ return enforcement.swarm_min_agents?.test_execute;
176
+ }
177
+ if (completedPhase === "review_swarm" && isMutating) {
178
+ return enforcement.swarm_min_agents?.review_swarm;
146
179
  }
147
180
  if (completedPhase === "discover" && manifest.verb === "check") {
148
181
  return (
@@ -189,3 +222,118 @@ export function clearActiveRun(conversationId) {
189
222
  // already cleared
190
223
  }
191
224
  }
225
+
226
+ export function isMutatingVerb(manifest, enforcement) {
227
+ const mutating = enforcement.mutating_verbs ?? ["create", "update", "fix"];
228
+ return (
229
+ mutating.includes(manifest.verb) ||
230
+ (enforcement.fix_commands ?? []).includes(manifest.command)
231
+ );
232
+ }
233
+
234
+ /** List items under a YAML field (lines starting with `-` before next top-level key). */
235
+ export function readYamlListField(content, fieldName) {
236
+ if (!content) return [];
237
+ const lines = content.split("\n");
238
+ const start = lines.findIndex((line) => line.startsWith(`${fieldName}:`));
239
+ if (start < 0) return [];
240
+
241
+ const inline = lines[start].slice(`${fieldName}:`.length).trim();
242
+ if (inline === "[]") return [];
243
+ if (inline && !inline.startsWith("-")) return [inline];
244
+
245
+ const items = [];
246
+ for (let i = start + 1; i < lines.length; i += 1) {
247
+ const line = lines[i];
248
+ if (/^\S/.test(line) && line.trim()) break;
249
+ const itemMatch = line.match(/^\s+-\s+(.*)$/);
250
+ if (itemMatch) items.push(itemMatch[1].trim());
251
+ }
252
+ return items;
253
+ }
254
+
255
+ export function readYamlScalarField(content, fieldName) {
256
+ if (!content) return null;
257
+ const match = content.match(new RegExp(`^${fieldName}:\\s*(.+)$`, "m"));
258
+ if (!match) return null;
259
+ return match[1].trim().replace(/^["']|["']$/g, "");
260
+ }
261
+
262
+ export function hasYamlField(content, fieldName) {
263
+ if (!content) return false;
264
+ return new RegExp(`^${fieldName}:`, "m").test(content);
265
+ }
266
+
267
+ export function planRequiresTests(planContent) {
268
+ if (!planContent) return false;
269
+ if (hasYamlField(planContent, "tests_to_add")) {
270
+ return readYamlListField(planContent, "tests_to_add").length > 0;
271
+ }
272
+ return /^\s*create:[\s\S]*?^\s+-\s+path:.*\/lib\//m.test(planContent);
273
+ }
274
+
275
+ export function validatePhaseArtifactContent(runId, completedPhase, manifest, enforcement) {
276
+ if (!isMutatingVerb(manifest, enforcement)) {
277
+ return { ok: true };
278
+ }
279
+
280
+ const planPath = path.join(runDir(runId), "artifacts/plan.yaml");
281
+ const planContent = fs.existsSync(planPath)
282
+ ? fs.readFileSync(planPath, "utf8")
283
+ : "";
284
+
285
+ if (completedPhase === "plan") {
286
+ if (!hasYamlField(planContent, "tests_to_add")) {
287
+ return {
288
+ ok: false,
289
+ reason:
290
+ "plan.yaml must include tests_to_add (behaviors to cover, or tests_to_add: [] when no tests are needed)",
291
+ };
292
+ }
293
+ return { ok: true };
294
+ }
295
+
296
+ if (completedPhase === "test_execute") {
297
+ const testPlanPath = path.join(runDir(runId), "artifacts/test_plan.yaml");
298
+ const testPlanContent = fs.existsSync(testPlanPath)
299
+ ? fs.readFileSync(testPlanPath, "utf8")
300
+ : "";
301
+
302
+ const filesWritten = readYamlListField(testPlanContent, "files_written");
303
+ const skippedReason = readYamlScalarField(testPlanContent, "skipped_reason");
304
+ const testsRequired = planRequiresTests(planContent);
305
+
306
+ if (/status:\s*deferred/i.test(testPlanContent) && filesWritten.length === 0) {
307
+ return {
308
+ ok: false,
309
+ reason:
310
+ "test_plan.yaml cannot defer tests — author test files in test_execute (files_written required)",
311
+ };
312
+ }
313
+
314
+ if (testsRequired && filesWritten.length === 0) {
315
+ return {
316
+ ok: false,
317
+ reason:
318
+ "plan.yaml tests_to_add requires non-empty test_plan.files_written — launch test-author Task in test_execute",
319
+ };
320
+ }
321
+
322
+ if (
323
+ hasYamlField(planContent, "tests_to_add") &&
324
+ /tests_to_add:\s*\[\]/m.test(planContent) &&
325
+ filesWritten.length === 0 &&
326
+ !skippedReason
327
+ ) {
328
+ return {
329
+ ok: false,
330
+ reason:
331
+ "tests_to_add is empty — test_plan.yaml must include skipped_reason explaining why no tests were authored",
332
+ };
333
+ }
334
+
335
+ return { ok: true };
336
+ }
337
+
338
+ return { ok: true };
339
+ }
@@ -0,0 +1,25 @@
1
+ # Agent: doc-conformance
2
+
3
+ **Readonly.**
4
+
5
+ ## Role
6
+
7
+ Compare implementation diff against supporting docs and policies — not layer boundaries (see boundary-review).
8
+
9
+ ## Sources (read before judging)
10
+
11
+ - [docs/master_rules.md](../../docs/master_rules.md)
12
+ - [docs/architecture.md](../../docs/architecture.md) when present
13
+ - Domain inventory under `.cursor/domains/<slug>/update/inventory/` when available
14
+ - [.cursor/policies/](../../.cursor/policies/)
15
+
16
+ ## Check
17
+
18
+ - SSOT violations (duplicated constants, mirrored state)
19
+ - Undocumented exceptions to master rules
20
+ - Plan `requirement_map` entries satisfied in code
21
+ - Missing validation at boundaries when plan promised schemas
22
+
23
+ ## Return
24
+
25
+ Findings, Evidence (`path:line`), Severity (critical | suggestion), Confidence.
@@ -0,0 +1,21 @@
1
+ # Agent: implementation-review
2
+
3
+ **Readonly.**
4
+
5
+ ## Role
6
+
7
+ Independent post-execute review of the diff — **not** the agent that wrote the code. Spot-check that the change matches plan and does not introduce obvious defects.
8
+
9
+ ## Check
10
+
11
+ - Plan `paths_to_touch` vs actual diff scope
12
+ - No drive-by refactors outside plan
13
+ - Error paths logged, not swallowed
14
+ - Async flows use explicit state machines where plan required
15
+ - Size budgets not violated on touched files (flag if file grew past 80% budget)
16
+
17
+ ## Return
18
+
19
+ Findings, Evidence (`path:line`), Severity (critical | suggestion), Confidence.
20
+
21
+ **Blocking:** any **critical** finding must be fixed before `report` on mutating verbs.
@@ -0,0 +1,32 @@
1
+ # Agent: remediation-check-app-inventory
2
+
3
+ **Readonly.** Mirrors `/check-app` discover phase for Fallow remediation.
4
+
5
+ ## Role
6
+
7
+ Map Fallow `unused_files` and `review` inventory to **live app surfaces**: Vite entry points, workers (`src/workers/**`), hooks (`*Worker*`), overlay renderer barrels, lazy routes, Playwright-critical imports.
8
+
9
+ ## Inputs (mandatory)
10
+
11
+ - `iterations/{n}/check-context.json`
12
+ - `iterations/{n}/fallow-scan.json`
13
+ - `frontend/.fallowrc.json` (`dynamicallyLoaded`)
14
+
15
+ ## Commands (run as needed)
16
+
17
+ ```bash
18
+ cd frontend && fallow list --entry-points --format json --quiet 2>/dev/null || true
19
+ cd frontend && fallow dead-code --format json --quiet --trace-file <path> 2>/dev/null || true
20
+ ```
21
+
22
+ ## Return
23
+
24
+ Structured JSON block (see [check-swarm SKILL](../skills/shared/remediation/check-swarm/SKILL.md)) plus:
25
+
26
+ - **Answer** — are flagged unused files actually unreachable from app runtime?
27
+ - **protected_paths** — paths waves must never delete
28
+ - **false_positives** — with `reason` + `evidence` (`path:line`)
29
+
30
+ ## Confidence
31
+
32
+ high | medium | low
@@ -0,0 +1,24 @@
1
+ # Agent: remediation-check-app-ssot
2
+
3
+ **Readonly.** Mirrors `/check-app` SSOT trace for Fallow remediation.
4
+
5
+ ## Role
6
+
7
+ For each Fallow `review` / `true_positive` export: who owns the symbol? Is it consumed via barrel re-export, dynamic import, worker postMessage, or external package API?
8
+
9
+ ## Inputs (mandatory)
10
+
11
+ - `iterations/{n}/check-context.json` — `fallow.inventory`
12
+ - `fallow-false-positives.json` (campaign registry)
13
+
14
+ ## Method
15
+
16
+ 1. Grep importers for top `review` exports
17
+ 2. Check barrel `index.ts` re-export chains
18
+ 3. Flag provider interface methods (`name`, `createInvoice`) as **protected** not dead
19
+
20
+ ## Return
21
+
22
+ JSON block with `false_positives`, `do_not_delete`, `safe_to_fix`, `findings`, `gaps`.
23
+
24
+ Set `command_mirror: "check-app"`.
@@ -0,0 +1,29 @@
1
+ # Agent: remediation-check-app-trace
2
+
3
+ **Readonly.** Mirrors `/check-app` capability trace for Fallow remediation.
4
+
5
+ ## Role
6
+
7
+ Run Fallow trace CLI for every item in `check-context.fallow.top_review_for_trace`. Confirm whether static unused = actually unreachable.
8
+
9
+ ## Commands (mandatory for each review item)
10
+
11
+ ```bash
12
+ cd frontend && fallow dead-code --format json --quiet --trace-file <path> 2>/dev/null || true
13
+ cd frontend && fallow dead-code --format json --quiet --trace <path>:<export> 2>/dev/null || true
14
+ ```
15
+
16
+ ## Classification rules
17
+
18
+ | Trace result | Classification |
19
+ |--------------|----------------|
20
+ | Entry-point or dynamically loaded | `false_positive` |
21
+ | Re-export chain to live entry | `false_positive` |
22
+ | Zero importers, not entry | `true_positive` or `safe_to_fix` |
23
+ | Ambiguous (test-only import) | `review` |
24
+
25
+ ## Return
26
+
27
+ JSON block with per-path trace summary in `findings`. Populate `false_positives` for confirmed runtime paths.
28
+
29
+ Set `command_mirror: "check-app"`, `agent_id: "remediation-check-app-trace"`.
@@ -0,0 +1,21 @@
1
+ # Agent: remediation-check-architecture-boundaries
2
+
3
+ **Readonly.** Mirrors `/check-architecture` boundary review for remediation waves.
4
+
5
+ ## Role
6
+
7
+ Evaluate whether proposed Fallow deletions or dupes extractions would cross layer boundaries (UI→fetch, domain→infrastructure, worker↔main SSOT violations).
8
+
9
+ ## Inputs
10
+
11
+ - `check-context.json`
12
+ - `docs/architecture.md` (if present)
13
+ - Fallow `boundary_violations` from dead-code scan
14
+
15
+ ## Return
16
+
17
+ JSON block: `command_mirror: "check-architecture"`. List boundary risks in `findings`. Add blast-radius paths to `protected_paths` / `do_not_delete`.
18
+
19
+ ## Severity
20
+
21
+ critical = deletion would break boundary; suggestion = refactor-only
@@ -0,0 +1,25 @@
1
+ # Agent: remediation-check-architecture-decomposition
2
+
3
+ **Readonly.** Mirrors `/check-architecture` system decomposition for dupes remediation.
4
+
5
+ ## Role
6
+
7
+ Classify dupes clone families (operations/, workers/, e2e specs) into:
8
+
9
+ - **extract-shared** — safe consolidation target
10
+ - **main-worker mirror** — do not delete one side; extract to shared module first
11
+ - **test-only dupes** — low risk extract
12
+ - **intentional parallel** — mark protected (e.g. provider adapters)
13
+
14
+ ## Inputs
15
+
16
+ - `check-context.dupes_top_groups`
17
+ - `fallow-dupes.json` clone_groups
18
+
19
+ ## Return
20
+
21
+ JSON block: `command_mirror: "check-architecture"`. Dupes safe targets in `safe_to_fix`. Mirrored paths in `protected_paths`.
22
+
23
+ ## Anti-pattern
24
+
25
+ Never recommend deleting `src/lib/**` because worker has a copy — recommend shared extract wave instead.
@@ -0,0 +1,23 @@
1
+ # Agent: remediation-check-architecture-deps
2
+
3
+ **Readonly.** Mirrors `/check-architecture` dependency analysis for remediation.
4
+
5
+ ## Role
6
+
7
+ For top `true_positive` file deletions and large dupes groups: compute fan-in, import cycles, and downstream test breakage risk.
8
+
9
+ ## Inputs
10
+
11
+ - `check-context.json` — `dupes_top_groups`, `fallow.inventory.true_positive`
12
+ - `fallow dead-code` circular_dependencies list
13
+
14
+ ## Commands
15
+
16
+ ```bash
17
+ cd frontend && fallow dead-code --format json --quiet --trace-file <path> 2>/dev/null || true
18
+ cd frontend && fallow dupes --format json --quiet --trace <path>:<line> 2>/dev/null || true
19
+ ```
20
+
21
+ ## Return
22
+
23
+ JSON block: `command_mirror: "check-architecture"`. High fan-in paths → `protected_paths`. Isolated leaves → `safe_to_fix`.
@@ -0,0 +1,37 @@
1
+ # Agent: remediation-check-risk
2
+
3
+ **Readonly.** Remediation guard — consolidates FP traps before fix waves.
4
+
5
+ ## Role
6
+
7
+ Final pass on all Fallow layers (dead-code, dupes, health). Confirm or reject classifications from other swarm agents. **This agent owns the FP registry update.**
8
+
9
+ ## Mandatory actions
10
+
11
+ 1. Read `check-context.json` + `fallow-classification.json`
12
+ 2. Cross-check other agents' `false_positives` proposals
13
+ 3. Write batch file for merge script OR confirm parent will run:
14
+
15
+ ```bash
16
+ node .cursor/aaac/scripts/remediation/record-fallow-fp.mjs \
17
+ --campaign-id <id> --from-json iterations/<n>/check-swarm-fp-batch.json
18
+ ```
19
+
20
+ ## Known FP patterns (always verify)
21
+
22
+ | Pattern | Reason |
23
+ |---------|--------|
24
+ | `src/hooks/*Worker*.ts` | worker_hook_runtime |
25
+ | `src/workers/**` | dynamically_loaded |
26
+ | `src/overlays/renderers/*/index.ts` | overlay_renderer_barrel |
27
+ | `LayoutSaveQueue.enqueue/cancel` | framework lifecycle |
28
+ | `AtlosPaymentProvider.name/createInvoice` | provider interface |
29
+ | `src/operations/categories/**` dupes | boilerplate — extract, don't delete ops |
30
+
31
+ ## Return
32
+
33
+ JSON block with complete `false_positives[]`, `protected_paths[]`, `do_not_delete[]`. Set `agent_id: "remediation-check-risk"`.
34
+
35
+ ## Rule
36
+
37
+ When uncertain → `review` + `protected_paths`, never `true_positive` delete.
@@ -0,0 +1,30 @@
1
+ # Agent: remediation-e2e-gate
2
+
3
+ ## Role
4
+
5
+ Run the full iteration verification gate for a remediation campaign and return structured pass/fail.
6
+
7
+ ## Steps
8
+
9
+ 1. Confirm `SE100_BASE_URL` (default `http://localhost:5173`) is reachable
10
+ 2. Run:
11
+ ```bash
12
+ node .cursor/aaac/scripts/remediation/verify-remediation-iteration.mjs \
13
+ --campaign-id <campaign_id> --iteration <n> --mode iteration --run-id <run_id>
14
+ ```
15
+ 3. Read output JSON — report each layer status
16
+
17
+ ## Return
18
+
19
+ ```yaml
20
+ status: pass | fail
21
+ layers:
22
+ typecheck: pass | fail
23
+ vitest: pass | fail
24
+ go_test: pass | fail | skipped
25
+ build: pass | fail
26
+ playwright: pass | fail
27
+ artifact_path: .cursor/aaac/state/campaigns/{id}/iterations/{n}/verify-iteration.json
28
+ ```
29
+
30
+ On fail: include `stderr_tail` excerpts and whether rollback is recommended.