@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.
- package/README.md +27 -12
- package/package.json +9 -9
- package/src/cli.mjs +19 -7
- package/src/generators/generate-commands.mjs +25 -1
- package/src/generators/generate-graph.mjs +9 -1
- package/src/lib/install.mjs +13 -1
- package/src/lib/sweep-project-docs.mjs +348 -0
- package/src/run-engine/advance-phase.mjs +23 -0
- package/src/run-engine/debug-run.mjs +0 -0
- package/src/run-engine/gate-write.mjs +13 -0
- package/src/run-engine/lib.mjs +153 -5
- package/src/run-engine/log-dump.mjs +0 -0
- package/src/run-engine/log-trace.mjs +0 -0
- package/templates/cursor/aaac/enforcement.json +96 -5
- package/templates/cursor/aaac/graph.project.yaml +44 -5
- package/templates/cursor/aaac/lifecycle/lifecycle.json +26 -0
- package/templates/cursor/aaac/lifecycle/phases.json +9 -1
- package/templates/cursor/aaac/ontology.json +1 -0
- package/templates/cursor/aaac/project.config.json +36 -0
- package/templates/cursor/aaac/scripts/remediation/auto-check-swarm-synthesis.mjs +75 -0
- package/templates/cursor/aaac/scripts/remediation/auto-dispatch-queue-from-health.mjs +78 -0
- package/templates/cursor/aaac/scripts/remediation/bootstrap-autonomous.mjs +113 -0
- package/templates/cursor/aaac/scripts/remediation/capture-verify-baseline.mjs +66 -0
- package/templates/cursor/aaac/scripts/remediation/capture-wave-snapshot.mjs +79 -0
- package/templates/cursor/aaac/scripts/remediation/check-swarm-raw.template.json +26 -0
- package/templates/cursor/aaac/scripts/remediation/classify-fallow-issues.mjs +77 -0
- package/templates/cursor/aaac/scripts/remediation/classify-verify-failure.mjs +176 -0
- package/templates/cursor/aaac/scripts/remediation/compute-satisfaction.mjs +344 -0
- package/templates/cursor/aaac/scripts/remediation/debt-sweep-gate.mjs +202 -0
- package/templates/cursor/aaac/scripts/remediation/dispatch-rules.json +44 -0
- package/templates/cursor/aaac/scripts/remediation/fallow-fp-rules.json +87 -0
- package/templates/cursor/aaac/scripts/remediation/fallow-scan.mjs +219 -0
- package/templates/cursor/aaac/scripts/remediation/handle-yield.mjs +240 -0
- package/templates/cursor/aaac/scripts/remediation/init-campaign.mjs +211 -0
- package/templates/cursor/aaac/scripts/remediation/lib/autonomous-mode.mjs +63 -0
- package/templates/cursor/aaac/scripts/remediation/lib/campaign-focus.mjs +87 -0
- package/templates/cursor/aaac/scripts/remediation/lib/fallow-classifier.mjs +190 -0
- package/templates/cursor/aaac/scripts/remediation/lib/fallow-health-targets.mjs +56 -0
- package/templates/cursor/aaac/scripts/remediation/lib/fallow-metrics.mjs +119 -0
- package/templates/cursor/aaac/scripts/remediation/lib/invoke-cursor-agent.mjs +51 -0
- package/templates/cursor/aaac/scripts/remediation/lib/reconcile-run-manifest.mjs +41 -0
- package/templates/cursor/aaac/scripts/remediation/lib/regression-analysis.mjs +55 -0
- package/templates/cursor/aaac/scripts/remediation/lib/remediation-config.mjs +69 -0
- package/templates/cursor/aaac/scripts/remediation/lib/remediation-progress.mjs +58 -0
- package/templates/cursor/aaac/scripts/remediation/lib/remediation-watch-loop.mjs +168 -0
- package/templates/cursor/aaac/scripts/remediation/lib/runner-exec.mjs +156 -0
- package/templates/cursor/aaac/scripts/remediation/lib/runner-state.mjs +145 -0
- package/templates/cursor/aaac/scripts/remediation/lib/verify-metrics.mjs +205 -0
- package/templates/cursor/aaac/scripts/remediation/merge-check-swarm.mjs +257 -0
- package/templates/cursor/aaac/scripts/remediation/plan-waves-from-queue.mjs +85 -0
- package/templates/cursor/aaac/scripts/remediation/prepare-check-context.mjs +148 -0
- package/templates/cursor/aaac/scripts/remediation/record-fallow-fp.mjs +107 -0
- package/templates/cursor/aaac/scripts/remediation/record-iteration-step.mjs +56 -0
- package/templates/cursor/aaac/scripts/remediation/remediation-cli.mjs +157 -0
- package/templates/cursor/aaac/scripts/remediation/remediation-cursor-watch.sh +10 -0
- package/templates/cursor/aaac/scripts/remediation/remediation-runner-daemon.sh +13 -0
- package/templates/cursor/aaac/scripts/remediation/remediation-runner.mjs +748 -0
- package/templates/cursor/aaac/scripts/remediation/remediation-yield-watcher.mjs +40 -0
- package/templates/cursor/aaac/scripts/remediation/remediator-gate.mjs +405 -0
- package/templates/cursor/aaac/scripts/remediation/repair-fallow-start-baseline.mjs +118 -0
- package/templates/cursor/aaac/scripts/remediation/runner-health-check.mjs +164 -0
- package/templates/cursor/aaac/scripts/remediation/satisfaction-loop-gate.mjs +286 -0
- package/templates/cursor/aaac/scripts/remediation/validate-campaign-complete.mjs +191 -0
- package/templates/cursor/aaac/scripts/remediation/verify-remediation-iteration.mjs +112 -0
- package/templates/cursor/aaac/scripts/run-engine/advance-phase.mjs +23 -0
- package/templates/cursor/aaac/scripts/run-engine/debug-run.mjs +0 -0
- package/templates/cursor/aaac/scripts/run-engine/gate-write.mjs +13 -0
- package/templates/cursor/aaac/scripts/run-engine/lib.mjs +153 -5
- package/templates/cursor/aaac/scripts/run-engine/log-dump.mjs +0 -0
- package/templates/cursor/aaac/scripts/run-engine/log-trace.mjs +0 -0
- package/templates/cursor/agents/doc-conformance.md +25 -0
- package/templates/cursor/agents/implementation-review.md +21 -0
- package/templates/cursor/agents/remediation-check-app-inventory.md +32 -0
- package/templates/cursor/agents/remediation-check-app-ssot.md +24 -0
- package/templates/cursor/agents/remediation-check-app-trace.md +29 -0
- package/templates/cursor/agents/remediation-check-architecture-boundaries.md +21 -0
- package/templates/cursor/agents/remediation-check-architecture-decomposition.md +25 -0
- package/templates/cursor/agents/remediation-check-architecture-deps.md +23 -0
- package/templates/cursor/agents/remediation-check-risk.md +37 -0
- package/templates/cursor/agents/remediation-e2e-gate.md +30 -0
- package/templates/cursor/agents/remediation-remediator.md +69 -0
- package/templates/cursor/agents/test-author.md +27 -0
- package/templates/cursor/commands/remediate-app.md +212 -0
- package/templates/cursor/hooks/aaac-before-submit.sh +0 -0
- package/templates/cursor/hooks/aaac-pre-tool.sh +0 -0
- package/templates/cursor/hooks/aaac-stop.sh +0 -0
- package/templates/cursor/hooks/aaac-subagent-start.sh +0 -0
- package/templates/cursor/rules/aaac-enforcement.mdc +10 -3
- package/templates/cursor/skills/shared/execution/SKILL.md +7 -3
- package/templates/cursor/skills/shared/governance/implementation/SKILL.md +396 -28
- package/templates/cursor/skills/shared/implementation-review/SKILL.md +49 -0
- package/templates/cursor/skills/shared/planning/SKILL.md +5 -0
- package/templates/cursor/skills/shared/remediation/SKILL.md +51 -0
- package/templates/cursor/skills/shared/remediation/babysit/SKILL.md +223 -0
- package/templates/cursor/skills/shared/remediation/check-swarm/SKILL.md +114 -0
- package/templates/cursor/skills/shared/remediation/orchestrator/SKILL.md +275 -0
- package/templates/cursor/skills/shared/remediation/orchestrator/contract.yaml +116 -0
- package/templates/cursor/skills/shared/test-authoring/SKILL.md +58 -0
- package/templates/cursor/skills/shared/testing/SKILL.md +6 -0
- package/templates/cursor/skills/shared/verbs/create/orchestrator/SKILL.md +5 -3
- package/templates/cursor/skills/shared/verbs/fix/orchestrator/SKILL.md +5 -3
- package/templates/cursor/skills/shared/verbs/update/orchestrator/SKILL.md +5 -3
- package/templates/cursor/skills/shared/verification/SKILL.md +5 -3
- package/templates/docs/agentic_architecture.md +169 -97
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Compute satisfaction score and remediation rate for a campaign iteration.
|
|
4
|
+
* Fallow components: dead-code (actionable), dupes (clone_groups), health (score).
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* node compute-satisfaction.mjs --campaign-id <id> --iteration <n>
|
|
8
|
+
*/
|
|
9
|
+
import fs from "fs";
|
|
10
|
+
import path from "path";
|
|
11
|
+
import { spawnSync } from "child_process";
|
|
12
|
+
import { fileURLToPath } from "url";
|
|
13
|
+
import { REPO_ROOT, isoNow, readJson, writeJson } from "../run-engine/lib.mjs";
|
|
14
|
+
import { resolveActionableBaseline } from "./lib/fallow-classifier.mjs";
|
|
15
|
+
import {
|
|
16
|
+
improvementRate,
|
|
17
|
+
readStartBaseline,
|
|
18
|
+
reductionRate,
|
|
19
|
+
summarizeDeadCode,
|
|
20
|
+
summarizeDupes,
|
|
21
|
+
summarizeHealth,
|
|
22
|
+
} from "./lib/fallow-metrics.mjs";
|
|
23
|
+
|
|
24
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
25
|
+
const CAMPAIGNS_ROOT = path.join(REPO_ROOT, ".cursor/aaac/state/campaigns");
|
|
26
|
+
|
|
27
|
+
const WEIGHTS = {
|
|
28
|
+
fallow_dead_code: 0.25,
|
|
29
|
+
fallow_dupes: 0.1,
|
|
30
|
+
fallow_health: 0.05,
|
|
31
|
+
structural_clean: 0.15,
|
|
32
|
+
unit_tests: 0.15,
|
|
33
|
+
build: 0.1,
|
|
34
|
+
e2e: 0.2,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/** @deprecated — use sum of effective fallow weights */
|
|
38
|
+
const LEGACY_FALLOW_WEIGHT =
|
|
39
|
+
WEIGHTS.fallow_dead_code + WEIGHTS.fallow_dupes + WEIGHTS.fallow_health;
|
|
40
|
+
|
|
41
|
+
function parseArgs(argv) {
|
|
42
|
+
const out = { campaignId: null, iteration: 0 };
|
|
43
|
+
for (let i = 0; i < argv.length; i++) {
|
|
44
|
+
if (argv[i] === "--campaign-id") out.campaignId = argv[++i];
|
|
45
|
+
else if (argv[i] === "--iteration") out.iteration = Number(argv[++i]);
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function scoreComponent(pass, partial = 0) {
|
|
51
|
+
if (pass === true) return 1;
|
|
52
|
+
if (pass === false) return 0;
|
|
53
|
+
return partial;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function resolveFallowStartBaseline(campaignDir, campaign) {
|
|
57
|
+
const startPath = path.join(campaignDir, "fallow-start-baseline.json");
|
|
58
|
+
const start = readJson(startPath, null);
|
|
59
|
+
if (start?.fallow_total_issues != null) {
|
|
60
|
+
return { issues: start.fallow_total_issues, source: "fallow-start-baseline.json" };
|
|
61
|
+
}
|
|
62
|
+
if (campaign?.baseline?.immutable && campaign.baseline.fallow_total_issues != null) {
|
|
63
|
+
return { issues: campaign.baseline.fallow_total_issues, source: "campaign.baseline" };
|
|
64
|
+
}
|
|
65
|
+
if (campaign?.baseline?.fallow_total_issues != null) {
|
|
66
|
+
return { issues: campaign.baseline.fallow_total_issues, source: "campaign.baseline.legacy" };
|
|
67
|
+
}
|
|
68
|
+
return { issues: null, source: "none" };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function loadLayerSummary(iterDir, file, summarize) {
|
|
72
|
+
const fullPath = path.join(iterDir, file);
|
|
73
|
+
if (!fs.existsSync(fullPath)) return null;
|
|
74
|
+
const payload = readJson(fullPath, {});
|
|
75
|
+
if (payload._remediation?.summary) return payload._remediation.summary;
|
|
76
|
+
return summarize(payload);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function normalizeWeights(weights, skipKeys = []) {
|
|
80
|
+
const active = { ...weights };
|
|
81
|
+
for (const key of skipKeys) delete active[key];
|
|
82
|
+
const sum = Object.values(active).reduce((a, b) => a + b, 0);
|
|
83
|
+
if (sum <= 0) return weights;
|
|
84
|
+
const normalized = {};
|
|
85
|
+
for (const [key, value] of Object.entries(active)) {
|
|
86
|
+
normalized[key] = value / sum;
|
|
87
|
+
}
|
|
88
|
+
return normalized;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function resolveVerify(iterDir) {
|
|
92
|
+
const debt = readJson(path.join(iterDir, "verify-debt.json"), null);
|
|
93
|
+
if (debt) return { verify: debt, source: "verify-debt.json" };
|
|
94
|
+
const iteration = readJson(path.join(iterDir, "verify-iteration.json"), null);
|
|
95
|
+
if (iteration) return { verify: iteration, source: "verify-iteration.json" };
|
|
96
|
+
return { verify: {}, source: "missing" };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function loadClassification(iterDir, campaignId, iteration) {
|
|
100
|
+
const classPath = path.join(iterDir, "fallow-classification.json");
|
|
101
|
+
if (fs.existsSync(classPath)) {
|
|
102
|
+
return readJson(classPath, null);
|
|
103
|
+
}
|
|
104
|
+
spawnSync(
|
|
105
|
+
process.execPath,
|
|
106
|
+
[
|
|
107
|
+
path.join(__dirname, "classify-fallow-issues.mjs"),
|
|
108
|
+
"--campaign-id",
|
|
109
|
+
campaignId,
|
|
110
|
+
"--iteration",
|
|
111
|
+
String(iteration),
|
|
112
|
+
],
|
|
113
|
+
{ encoding: "utf8" },
|
|
114
|
+
);
|
|
115
|
+
return readJson(classPath, null);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const args = parseArgs(process.argv.slice(2));
|
|
119
|
+
if (!args.campaignId) {
|
|
120
|
+
console.error("compute-satisfaction: --campaign-id required");
|
|
121
|
+
process.exit(2);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const campaignDir = path.join(CAMPAIGNS_ROOT, args.campaignId);
|
|
125
|
+
const campaign = readJson(path.join(campaignDir, "campaign.json"));
|
|
126
|
+
const iterDir = path.join(campaignDir, "iterations", String(args.iteration));
|
|
127
|
+
|
|
128
|
+
const deadSummary = loadLayerSummary(iterDir, "fallow-scan.json", summarizeDeadCode) ?? {};
|
|
129
|
+
const dupesSummary = loadLayerSummary(iterDir, "fallow-dupes.json", summarizeDupes);
|
|
130
|
+
const healthSummary = loadLayerSummary(iterDir, "fallow-health.json", summarizeHealth);
|
|
131
|
+
|
|
132
|
+
const classification = loadClassification(iterDir, args.campaignId, args.iteration);
|
|
133
|
+
const { verify, source: verifySource } = resolveVerify(iterDir);
|
|
134
|
+
|
|
135
|
+
const rawTotalIssues = deadSummary.total_issues ?? 0;
|
|
136
|
+
const actionableTotal =
|
|
137
|
+
classification?.summary?.actionable_total ?? rawTotalIssues;
|
|
138
|
+
const falsePositiveTotal = classification?.summary?.false_positive_total ?? 0;
|
|
139
|
+
const reviewTotal = classification?.summary?.review_total ?? 0;
|
|
140
|
+
|
|
141
|
+
const { issues: rawBaselineIssues, source: baselineSource } = resolveFallowStartBaseline(
|
|
142
|
+
campaignDir,
|
|
143
|
+
campaign,
|
|
144
|
+
);
|
|
145
|
+
const actionableBaseline = resolveActionableBaseline(campaignDir);
|
|
146
|
+
const startActionable =
|
|
147
|
+
actionableBaseline?.actionable_total ??
|
|
148
|
+
(rawBaselineIssues != null && classification
|
|
149
|
+
? rawBaselineIssues - (actionableBaseline?.false_positive_total ?? 0)
|
|
150
|
+
: rawBaselineIssues) ??
|
|
151
|
+
actionableTotal;
|
|
152
|
+
|
|
153
|
+
const effectiveBaseline = startActionable ?? actionableTotal;
|
|
154
|
+
|
|
155
|
+
const dupesBaseline = readStartBaseline(campaignDir, "fallow-start-dupes-baseline.json", "clone_groups");
|
|
156
|
+
const healthBaseline = readStartBaseline(
|
|
157
|
+
campaignDir,
|
|
158
|
+
"fallow-start-health-baseline.json",
|
|
159
|
+
"health_score",
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
const currentCloneGroups = dupesSummary?.clone_groups ?? null;
|
|
163
|
+
const currentHealthScore = healthSummary?.health_score ?? null;
|
|
164
|
+
|
|
165
|
+
const dupesStart =
|
|
166
|
+
dupesBaseline.value ??
|
|
167
|
+
readJson(path.join(iterDir, "iteration-baseline.json"), null)?.fallow_dupes_clone_groups ??
|
|
168
|
+
currentCloneGroups;
|
|
169
|
+
|
|
170
|
+
const healthStart =
|
|
171
|
+
healthBaseline.value ??
|
|
172
|
+
readJson(path.join(iterDir, "iteration-baseline.json"), null)?.fallow_health_score ??
|
|
173
|
+
currentHealthScore;
|
|
174
|
+
|
|
175
|
+
const skipWeightKeys = [];
|
|
176
|
+
if (dupesSummary == null || dupesStart == null || currentCloneGroups == null) {
|
|
177
|
+
skipWeightKeys.push("fallow_dupes");
|
|
178
|
+
}
|
|
179
|
+
if (healthSummary == null || healthStart == null || currentHealthScore == null) {
|
|
180
|
+
skipWeightKeys.push("fallow_health");
|
|
181
|
+
}
|
|
182
|
+
const effectiveWeights = normalizeWeights(WEIGHTS, skipWeightKeys);
|
|
183
|
+
const effectiveFallowWeight =
|
|
184
|
+
(effectiveWeights.fallow_dead_code ?? 0) +
|
|
185
|
+
(effectiveWeights.fallow_dupes ?? 0) +
|
|
186
|
+
(effectiveWeights.fallow_health ?? 0);
|
|
187
|
+
|
|
188
|
+
const prevEntry = readJson(path.join(campaignDir, "satisfaction-history.yaml"), { entries: [] });
|
|
189
|
+
const prev = prevEntry.entries.filter((e) => e.iteration < args.iteration).pop();
|
|
190
|
+
|
|
191
|
+
const iterBaseline = readJson(path.join(iterDir, "iteration-baseline.json"), null);
|
|
192
|
+
const prevActionable = prev?.fallow_actionable_total ?? prev?.fallow_total_issues;
|
|
193
|
+
const iterationStartActionable =
|
|
194
|
+
iterBaseline?.fallow_actionable_total ??
|
|
195
|
+
iterBaseline?.fallow_total_issues ??
|
|
196
|
+
prevActionable ??
|
|
197
|
+
effectiveBaseline;
|
|
198
|
+
|
|
199
|
+
const deadCodeRate =
|
|
200
|
+
effectiveBaseline > 0
|
|
201
|
+
? Math.max(0, (effectiveBaseline - actionableTotal) / effectiveBaseline)
|
|
202
|
+
: 1;
|
|
203
|
+
|
|
204
|
+
const dupesRate =
|
|
205
|
+
dupesSummary != null && dupesStart != null && currentCloneGroups != null
|
|
206
|
+
? (reductionRate(dupesStart, currentCloneGroups) ?? 0)
|
|
207
|
+
: null;
|
|
208
|
+
const healthRate =
|
|
209
|
+
healthSummary != null && healthStart != null && currentHealthScore != null
|
|
210
|
+
? (improvementRate(healthStart, currentHealthScore) ?? 0)
|
|
211
|
+
: null;
|
|
212
|
+
|
|
213
|
+
const fallowCompositeRate =
|
|
214
|
+
effectiveFallowWeight > 0
|
|
215
|
+
? ((deadCodeRate * (effectiveWeights.fallow_dead_code ?? 0)) +
|
|
216
|
+
(dupesRate ?? 0) * (effectiveWeights.fallow_dupes ?? 0) +
|
|
217
|
+
(healthRate ?? 0) * (effectiveWeights.fallow_health ?? 0)) /
|
|
218
|
+
effectiveFallowWeight
|
|
219
|
+
: deadCodeRate;
|
|
220
|
+
|
|
221
|
+
const iterationRate =
|
|
222
|
+
iterationStartActionable > 0
|
|
223
|
+
? Math.max(0, (iterationStartActionable - actionableTotal) / iterationStartActionable)
|
|
224
|
+
: deadCodeRate;
|
|
225
|
+
|
|
226
|
+
const structuralClean =
|
|
227
|
+
(deadSummary.unresolved_imports ?? 0) === 0 && (deadSummary.circular_dependencies ?? 0) === 0;
|
|
228
|
+
|
|
229
|
+
const vitestPass = verify.vitest?.status === "pass";
|
|
230
|
+
const typecheckPass = verify.typecheck?.status === "pass";
|
|
231
|
+
const buildPass = verify.build?.status === "pass";
|
|
232
|
+
const goTestPass = verify.go_test?.status === "pass" || verify.go_test?.status === "skipped";
|
|
233
|
+
const e2ePass = verify.playwright?.status === "pass";
|
|
234
|
+
|
|
235
|
+
const components = {
|
|
236
|
+
fallow_dead_code: scoreComponent(null, deadCodeRate),
|
|
237
|
+
fallow_dupes: dupesRate == null ? null : scoreComponent(null, dupesRate),
|
|
238
|
+
fallow_health: healthRate == null ? null : scoreComponent(null, healthRate),
|
|
239
|
+
fallow_remediation: scoreComponent(null, fallowCompositeRate),
|
|
240
|
+
structural_clean: scoreComponent(structuralClean),
|
|
241
|
+
unit_tests: scoreComponent(vitestPass && typecheckPass),
|
|
242
|
+
build: scoreComponent(buildPass),
|
|
243
|
+
e2e: scoreComponent(e2ePass),
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
let score = 0;
|
|
247
|
+
for (const [key, weight] of Object.entries(effectiveWeights)) {
|
|
248
|
+
score += (components[key] ?? 0) * weight * 100;
|
|
249
|
+
}
|
|
250
|
+
score = Math.round(score * 10) / 10;
|
|
251
|
+
|
|
252
|
+
const entry = {
|
|
253
|
+
iteration: args.iteration,
|
|
254
|
+
at: isoNow(),
|
|
255
|
+
score,
|
|
256
|
+
rate: Math.round(fallowCompositeRate * 1000) / 1000,
|
|
257
|
+
dead_code_rate: Math.round(deadCodeRate * 1000) / 1000,
|
|
258
|
+
dupes_rate: dupesRate == null ? null : Math.round(dupesRate * 1000) / 1000,
|
|
259
|
+
health_rate: healthRate == null ? null : Math.round(healthRate * 1000) / 1000,
|
|
260
|
+
iteration_rate: Math.round(iterationRate * 1000) / 1000,
|
|
261
|
+
fallow_raw_total: rawTotalIssues,
|
|
262
|
+
fallow_actionable_total: actionableTotal,
|
|
263
|
+
fallow_false_positive_total: falsePositiveTotal,
|
|
264
|
+
fallow_review_total: reviewTotal,
|
|
265
|
+
fallow_dupes_clone_groups: currentCloneGroups,
|
|
266
|
+
fallow_dupes_duplication_percentage: dupesSummary?.duplication_percentage ?? null,
|
|
267
|
+
fallow_health_score: currentHealthScore,
|
|
268
|
+
fallow_health_functions_above_threshold: healthSummary?.functions_above_threshold ?? null,
|
|
269
|
+
fallow_start_baseline: effectiveBaseline,
|
|
270
|
+
fallow_start_baseline_raw: rawBaselineIssues,
|
|
271
|
+
fallow_dupes_start_baseline: dupesStart,
|
|
272
|
+
fallow_health_start_baseline: healthStart,
|
|
273
|
+
fallow_baseline_source: baselineSource,
|
|
274
|
+
fallow_dupes_baseline_source: dupesBaseline.source,
|
|
275
|
+
fallow_health_baseline_source: healthBaseline.source,
|
|
276
|
+
fallow_scoring_mode: "actionable_dead_code_plus_dupes_health",
|
|
277
|
+
delta_vs_baseline: actionableTotal - effectiveBaseline,
|
|
278
|
+
delta_vs_baseline_raw: rawTotalIssues - (rawBaselineIssues ?? rawTotalIssues),
|
|
279
|
+
delta_dupes_vs_baseline: currentCloneGroups - (dupesStart ?? currentCloneGroups),
|
|
280
|
+
delta_health_vs_baseline:
|
|
281
|
+
currentHealthScore != null && healthStart != null ? currentHealthScore - healthStart : null,
|
|
282
|
+
delta_vs_iteration_start: actionableTotal - iterationStartActionable,
|
|
283
|
+
delta_vs_previous: prev
|
|
284
|
+
? actionableTotal - (prev.fallow_actionable_total ?? prev.fallow_total_issues)
|
|
285
|
+
: null,
|
|
286
|
+
e2e_pass: e2ePass,
|
|
287
|
+
vitest_pass: vitestPass,
|
|
288
|
+
typecheck_pass: typecheckPass,
|
|
289
|
+
build_pass: buildPass,
|
|
290
|
+
go_test_pass: goTestPass,
|
|
291
|
+
weights_applied: effectiveWeights,
|
|
292
|
+
weights_skipped_layers: skipWeightKeys,
|
|
293
|
+
components,
|
|
294
|
+
classification_path: path.join(iterDir, "fallow-classification.json"),
|
|
295
|
+
fallow_dupes_path: path.join(iterDir, "fallow-dupes.json"),
|
|
296
|
+
fallow_health_path: path.join(iterDir, "fallow-health.json"),
|
|
297
|
+
verify_path: path.join(
|
|
298
|
+
iterDir,
|
|
299
|
+
verifySource === "verify-debt.json" ? "verify-debt.json" : "verify-iteration.json",
|
|
300
|
+
),
|
|
301
|
+
verify_source: verifySource,
|
|
302
|
+
/** @deprecated use fallow_actionable_total */
|
|
303
|
+
fallow_total_issues: actionableTotal,
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
writeJson(path.join(iterDir, "satisfaction.json"), entry);
|
|
307
|
+
|
|
308
|
+
const history = readJson(path.join(campaignDir, "satisfaction-history.yaml"), { entries: [] });
|
|
309
|
+
history.entries = history.entries.filter((e) => e.iteration !== args.iteration);
|
|
310
|
+
history.entries.push(entry);
|
|
311
|
+
history.entries.sort((a, b) => a.iteration - b.iteration);
|
|
312
|
+
writeJson(path.join(campaignDir, "satisfaction-history.yaml"), history);
|
|
313
|
+
|
|
314
|
+
if (campaign) {
|
|
315
|
+
campaign.current = {
|
|
316
|
+
fallow_total_issues: rawTotalIssues,
|
|
317
|
+
fallow_actionable_total: actionableTotal,
|
|
318
|
+
fallow_false_positive_total: falsePositiveTotal,
|
|
319
|
+
fallow_dupes_clone_groups: currentCloneGroups,
|
|
320
|
+
fallow_health_score: currentHealthScore,
|
|
321
|
+
satisfaction_score: score,
|
|
322
|
+
satisfaction_rate: entry.rate,
|
|
323
|
+
e2e_pass: e2ePass,
|
|
324
|
+
verify_status: verify.status ?? null,
|
|
325
|
+
};
|
|
326
|
+
campaign.updated_at = isoNow();
|
|
327
|
+
const threshold = campaign.config?.satisfaction_threshold ?? 85;
|
|
328
|
+
if (
|
|
329
|
+
score >= threshold &&
|
|
330
|
+
e2ePass &&
|
|
331
|
+
vitestPass &&
|
|
332
|
+
typecheckPass &&
|
|
333
|
+
buildPass &&
|
|
334
|
+
(verify.metrics?.total_errors ?? 0) === 0
|
|
335
|
+
) {
|
|
336
|
+
campaign.status = "satisfied";
|
|
337
|
+
}
|
|
338
|
+
writeJson(path.join(campaignDir, "campaign.json"), campaign);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const journalLine = `- **Iteration ${args.iteration}** — satisfaction **${score}/100** (fallow composite ${(entry.rate * 100).toFixed(1)}%: dead-code ${(deadCodeRate * 100).toFixed(1)}%${dupesRate != null ? `, dupes ${(dupesRate * 100).toFixed(1)}%` : ""}${healthRate != null ? `, health ${(healthRate * 100).toFixed(1)}%` : ""}), fallow raw=${rawTotalIssues} actionable=${actionableTotal} (FP=${falsePositiveTotal})${currentCloneGroups != null ? `, dupes=${currentCloneGroups} groups` : ""}${currentHealthScore != null ? `, health=${currentHealthScore}` : ""}, E2E ${e2ePass ? "PASS" : "FAIL"}\n`;
|
|
342
|
+
fs.appendFileSync(path.join(campaignDir, "journal.md"), `\n### ${entry.at}\n${journalLine}`);
|
|
343
|
+
|
|
344
|
+
console.log(JSON.stringify({ ok: true, satisfaction: entry, satisfied: campaign?.status === "satisfied" }));
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Strict debt sweep — mandatory phase after cleanup waves.
|
|
4
|
+
* Loops remediator-gate --mode debt until all layers pass or max rounds exhausted.
|
|
5
|
+
*
|
|
6
|
+
* Exit codes:
|
|
7
|
+
* 0 — debt sweep complete (strict pass)
|
|
8
|
+
* 1 — blocked (max rounds exhausted)
|
|
9
|
+
* 3 — remediate required (agent must fix and re-run with --round N --attempt M+1)
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* node debt-sweep-gate.mjs --campaign-id <id> --iteration <n> \
|
|
13
|
+
* [--run-id <run_id>] [--round <n>] [--attempt <n>]
|
|
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, readJson, writeJson, runDir } from "../run-engine/lib.mjs";
|
|
20
|
+
|
|
21
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
22
|
+
const CAMPAIGNS_ROOT = path.join(REPO_ROOT, ".cursor/aaac/state/campaigns");
|
|
23
|
+
const REMEDIATOR_GATE = path.join(__dirname, "remediator-gate.mjs");
|
|
24
|
+
|
|
25
|
+
function parseArgs(argv) {
|
|
26
|
+
const out = { campaignId: null, iteration: 0, runId: null, round: 1, attempt: 1 };
|
|
27
|
+
for (let i = 0; i < argv.length; i++) {
|
|
28
|
+
const a = argv[i];
|
|
29
|
+
if (a === "--campaign-id") out.campaignId = argv[++i];
|
|
30
|
+
else if (a === "--iteration") out.iteration = Number(argv[++i]);
|
|
31
|
+
else if (a === "--run-id") out.runId = argv[++i];
|
|
32
|
+
else if (a === "--round") out.round = Number(argv[++i]);
|
|
33
|
+
else if (a === "--attempt") out.attempt = Number(argv[++i]);
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function campaignDir(id) {
|
|
39
|
+
return path.join(CAMPAIGNS_ROOT, id);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function appendJournal(campaignId, text) {
|
|
43
|
+
fs.appendFileSync(path.join(campaignDir(campaignId), "journal.md"), text);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const args = parseArgs(process.argv.slice(2));
|
|
47
|
+
if (!args.campaignId) {
|
|
48
|
+
console.error("debt-sweep-gate: --campaign-id required");
|
|
49
|
+
process.exit(2);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const campaignPath = path.join(campaignDir(args.campaignId), "campaign.json");
|
|
53
|
+
const campaign = readJson(campaignPath, {});
|
|
54
|
+
const maxRounds = campaign?.config?.max_debt_sweep_rounds ?? 10;
|
|
55
|
+
const iterDir = path.join(campaignDir(args.campaignId), "iterations", String(args.iteration));
|
|
56
|
+
fs.mkdirSync(iterDir, { recursive: true });
|
|
57
|
+
|
|
58
|
+
const statePath = path.join(iterDir, "debt-sweep-state.json");
|
|
59
|
+
let sweepState = readJson(statePath, {
|
|
60
|
+
status: "running",
|
|
61
|
+
round: args.round,
|
|
62
|
+
attempts_by_round: {},
|
|
63
|
+
started_at: isoNow(),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const gateArgs = [
|
|
67
|
+
REMEDIATOR_GATE,
|
|
68
|
+
"--campaign-id",
|
|
69
|
+
args.campaignId,
|
|
70
|
+
"--iteration",
|
|
71
|
+
String(args.iteration),
|
|
72
|
+
"--mode",
|
|
73
|
+
"debt",
|
|
74
|
+
"--attempt",
|
|
75
|
+
String(args.attempt),
|
|
76
|
+
];
|
|
77
|
+
if (args.runId) gateArgs.push("--run-id", args.runId);
|
|
78
|
+
|
|
79
|
+
const result = spawnSync(process.execPath, gateArgs, { encoding: "utf8" });
|
|
80
|
+
let payload = null;
|
|
81
|
+
try {
|
|
82
|
+
payload = JSON.parse(result.stdout.trim().split("\n").pop());
|
|
83
|
+
} catch {
|
|
84
|
+
console.error("debt-sweep-gate: failed to parse remediator-gate output");
|
|
85
|
+
process.exit(2);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const roundKey = String(args.round);
|
|
89
|
+
sweepState.attempts_by_round[roundKey] = sweepState.attempts_by_round[roundKey] ?? [];
|
|
90
|
+
sweepState.attempts_by_round[roundKey].push({
|
|
91
|
+
attempt: args.attempt,
|
|
92
|
+
at: isoNow(),
|
|
93
|
+
exit_code: result.status,
|
|
94
|
+
action: payload.action,
|
|
95
|
+
});
|
|
96
|
+
sweepState.round = args.round;
|
|
97
|
+
sweepState.updated_at = isoNow();
|
|
98
|
+
|
|
99
|
+
if (result.status === 0 && (payload.action === "promote" || payload.action === "promote_wave")) {
|
|
100
|
+
sweepState.status = "complete";
|
|
101
|
+
sweepState.completed_at = isoNow();
|
|
102
|
+
writeJson(statePath, sweepState);
|
|
103
|
+
|
|
104
|
+
if (campaign) {
|
|
105
|
+
campaign.debt_sweep = {
|
|
106
|
+
status: "complete",
|
|
107
|
+
iteration: args.iteration,
|
|
108
|
+
round: args.round,
|
|
109
|
+
completed_at: isoNow(),
|
|
110
|
+
};
|
|
111
|
+
campaign.updated_at = isoNow();
|
|
112
|
+
writeJson(campaignPath, campaign);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
appendJournal(args.campaignId, `- Debt sweep **COMPLETE** iter ${args.iteration} round ${args.round}\n`);
|
|
116
|
+
|
|
117
|
+
const output = {
|
|
118
|
+
action: "debt_sweep_complete",
|
|
119
|
+
status: "pass",
|
|
120
|
+
round: args.round,
|
|
121
|
+
attempt: args.attempt,
|
|
122
|
+
sweep_state_path: statePath,
|
|
123
|
+
verify_path: payload.verify_path,
|
|
124
|
+
};
|
|
125
|
+
if (args.runId) {
|
|
126
|
+
writeJson(path.join(runDir(args.runId), "artifacts", "debt_sweep.json"), output);
|
|
127
|
+
}
|
|
128
|
+
console.log(JSON.stringify(output));
|
|
129
|
+
process.exit(0);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (result.status === 3) {
|
|
133
|
+
writeJson(statePath, sweepState);
|
|
134
|
+
const output = {
|
|
135
|
+
...payload,
|
|
136
|
+
debt_sweep_round: args.round,
|
|
137
|
+
max_debt_sweep_rounds: maxRounds,
|
|
138
|
+
campaign_must_continue: true,
|
|
139
|
+
orchestrator_must_not_stop: true,
|
|
140
|
+
retry_command: `node .cursor/aaac/scripts/remediation/debt-sweep-gate.mjs --campaign-id ${args.campaignId} --iteration ${args.iteration} --round ${args.round} --attempt ${args.attempt + 1}${args.runId ? ` --run-id ${args.runId}` : ""}`,
|
|
141
|
+
};
|
|
142
|
+
appendJournal(
|
|
143
|
+
args.campaignId,
|
|
144
|
+
`- Debt sweep round ${args.round} attempt ${args.attempt}: **remediate** — continue loop\n`,
|
|
145
|
+
);
|
|
146
|
+
if (args.runId) {
|
|
147
|
+
writeJson(path.join(runDir(args.runId), "artifacts", "debt_sweep_handoff.json"), output);
|
|
148
|
+
}
|
|
149
|
+
console.log(JSON.stringify(output));
|
|
150
|
+
process.exit(3);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (result.status === 1) {
|
|
154
|
+
if (args.round < maxRounds) {
|
|
155
|
+
sweepState.status = "running";
|
|
156
|
+
writeJson(statePath, sweepState);
|
|
157
|
+
const nextRound = args.round + 1;
|
|
158
|
+
const output = {
|
|
159
|
+
action: "debt_sweep_next_round",
|
|
160
|
+
status: "fail",
|
|
161
|
+
round: args.round,
|
|
162
|
+
next_round: nextRound,
|
|
163
|
+
max_debt_sweep_rounds: maxRounds,
|
|
164
|
+
campaign_must_continue: true,
|
|
165
|
+
message: `Round ${args.round} blocked — starting round ${nextRound}`,
|
|
166
|
+
retry_command: `node .cursor/aaac/scripts/remediation/debt-sweep-gate.mjs --campaign-id ${args.campaignId} --iteration ${args.iteration} --round ${nextRound} --attempt 1${args.runId ? ` --run-id ${args.runId}` : ""}`,
|
|
167
|
+
};
|
|
168
|
+
appendJournal(args.campaignId, `- Debt sweep round ${args.round} blocked — advancing to round ${nextRound}\n`);
|
|
169
|
+
console.log(JSON.stringify(output));
|
|
170
|
+
process.exit(3);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
sweepState.status = "blocked";
|
|
174
|
+
sweepState.blocked_at = isoNow();
|
|
175
|
+
writeJson(statePath, sweepState);
|
|
176
|
+
|
|
177
|
+
if (campaign) {
|
|
178
|
+
campaign.debt_sweep = { status: "blocked", iteration: args.iteration, round: args.round };
|
|
179
|
+
campaign.status = "blocked";
|
|
180
|
+
campaign.updated_at = isoNow();
|
|
181
|
+
writeJson(campaignPath, campaign);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const output = {
|
|
185
|
+
action: "debt_sweep_blocked",
|
|
186
|
+
status: "fail",
|
|
187
|
+
reason: "max_debt_sweep_rounds_exhausted",
|
|
188
|
+
round: args.round,
|
|
189
|
+
max_debt_sweep_rounds: maxRounds,
|
|
190
|
+
campaign_must_continue: false,
|
|
191
|
+
payload,
|
|
192
|
+
};
|
|
193
|
+
appendJournal(args.campaignId, `- Debt sweep **BLOCKED** after ${maxRounds} rounds\n`);
|
|
194
|
+
if (args.runId) {
|
|
195
|
+
writeJson(path.join(runDir(args.runId), "artifacts", "debt_sweep.json"), output);
|
|
196
|
+
}
|
|
197
|
+
console.log(JSON.stringify(output));
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
console.error("debt-sweep-gate: unexpected remediator exit", result.status);
|
|
202
|
+
process.exit(2);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"description": "Validator failure → remediator command dispatch (SSOT for /remediate-app)",
|
|
4
|
+
"defaults": {
|
|
5
|
+
"max_remediator_attempts_per_wave": 3,
|
|
6
|
+
"max_remediator_attempts_per_iteration": 3,
|
|
7
|
+
"max_remediator_attempts_per_debt_round": 3,
|
|
8
|
+
"max_debt_sweep_rounds": 10,
|
|
9
|
+
"wave_gate_mode": "regression"
|
|
10
|
+
},
|
|
11
|
+
"layers": {
|
|
12
|
+
"typecheck": {
|
|
13
|
+
"command": "fix-module",
|
|
14
|
+
"level": "code",
|
|
15
|
+
"intent_template": "TypeScript type-check failed during remediation campaign {campaign_id} iter {iteration} wave {wave_index} attempt {attempt}. Fix compiler errors without widening types unsafely. Output:\n{evidence}"
|
|
16
|
+
},
|
|
17
|
+
"vitest": {
|
|
18
|
+
"command": "fix-module",
|
|
19
|
+
"level": "test",
|
|
20
|
+
"intent_template": "Unit tests failed during remediation campaign {campaign_id} iter {iteration} wave {wave_index} attempt {attempt}. Fix production code or test expectations as appropriate. Output:\n{evidence}"
|
|
21
|
+
},
|
|
22
|
+
"go_test": {
|
|
23
|
+
"command": "fix-module",
|
|
24
|
+
"level": "code",
|
|
25
|
+
"intent_template": "Go tests failed during remediation campaign {campaign_id} iter {iteration} wave {wave_index} attempt {attempt}. Fix failing packages. Output:\n{evidence}"
|
|
26
|
+
},
|
|
27
|
+
"build": {
|
|
28
|
+
"command": "fix-module",
|
|
29
|
+
"level": "code",
|
|
30
|
+
"intent_template": "Production build failed during remediation campaign {campaign_id} iter {iteration} attempt {attempt}. Fix build errors and broken imports. Output:\n{evidence}"
|
|
31
|
+
},
|
|
32
|
+
"playwright": {
|
|
33
|
+
"command": "fix-bug",
|
|
34
|
+
"level": "code",
|
|
35
|
+
"intent_template": "Playwright E2E failed during campaign {campaign_id} iter {iteration} attempt {attempt}. Fix runtime/module/navigation regressions. Output:\n{evidence}"
|
|
36
|
+
},
|
|
37
|
+
"playwright_infra": {
|
|
38
|
+
"command": null,
|
|
39
|
+
"level": "infrastructure",
|
|
40
|
+
"handoff": "Start dev server (see project.config.json remediation.verify.dev_server) then re-run remediator-gate"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"evidence_priority": ["typecheck", "build", "vitest", "go_test", "playwright"]
|
|
44
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"description": "SSOT rules for classifying Fallow findings before remediation scoring and wave planning",
|
|
4
|
+
"sources": {
|
|
5
|
+
"fallowrc": "frontend/.fallowrc.json",
|
|
6
|
+
"campaign_registry": "fallow-false-positives.yaml"
|
|
7
|
+
},
|
|
8
|
+
"path_globs": [
|
|
9
|
+
{
|
|
10
|
+
"id": "fallowrc_dynamically_loaded",
|
|
11
|
+
"glob": "from_fallowrc_dynamicallyLoaded",
|
|
12
|
+
"classification": "false_positive",
|
|
13
|
+
"reason": "dynamically_loaded_entry_point"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": "worker_hooks",
|
|
17
|
+
"glob": "src/hooks/*Worker*.ts",
|
|
18
|
+
"classification": "false_positive",
|
|
19
|
+
"reason": "worker_hook_runtime"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"id": "overlay_renderer_barrels",
|
|
23
|
+
"glob": "src/overlays/renderers/*/index.ts",
|
|
24
|
+
"classification": "false_positive",
|
|
25
|
+
"reason": "overlay_renderer_barrel"
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"path_regex": [
|
|
29
|
+
{
|
|
30
|
+
"id": "overlay_integration",
|
|
31
|
+
"pattern": "^src/overlays/integration/",
|
|
32
|
+
"classification": "review",
|
|
33
|
+
"reason": "overlay_integration_runtime"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": "lazy_route",
|
|
37
|
+
"pattern": "\\.lazy\\.(tsx?|jsx?)$",
|
|
38
|
+
"classification": "review",
|
|
39
|
+
"reason": "lazy_route_entry"
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
"issue_heuristics": {
|
|
43
|
+
"unused_file": [
|
|
44
|
+
{
|
|
45
|
+
"id": "runtime_visibility_note",
|
|
46
|
+
"action_note_contains": "runtime functionality not visible to static analysis",
|
|
47
|
+
"classification": "review",
|
|
48
|
+
"reason": "runtime_visibility_risk"
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
"unused_export": [
|
|
52
|
+
{
|
|
53
|
+
"id": "barrel_index_reexport",
|
|
54
|
+
"path_suffix": "/index.ts",
|
|
55
|
+
"is_re_export": true,
|
|
56
|
+
"classification": "review",
|
|
57
|
+
"reason": "barrel_reexport_facade"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"id": "barrel_index_any",
|
|
61
|
+
"path_suffix": "/index.ts",
|
|
62
|
+
"classification": "review",
|
|
63
|
+
"reason": "barrel_public_api"
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
"unused_types": [
|
|
67
|
+
{
|
|
68
|
+
"id": "type_in_barrel",
|
|
69
|
+
"path_suffix": "/index.ts",
|
|
70
|
+
"classification": "review",
|
|
71
|
+
"reason": "barrel_type_facade"
|
|
72
|
+
}
|
|
73
|
+
],
|
|
74
|
+
"unused_class_members": [
|
|
75
|
+
{
|
|
76
|
+
"id": "ws_shared_base_public_api",
|
|
77
|
+
"path_suffix": "websocketService.shared.ts",
|
|
78
|
+
"classification": "false_positive",
|
|
79
|
+
"reason": "base_class_public_api_inherited_by_subclasses"
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
},
|
|
83
|
+
"scoring": {
|
|
84
|
+
"actionable_classifications": ["true_positive", "review"],
|
|
85
|
+
"excluded_from_satisfaction": ["false_positive"]
|
|
86
|
+
}
|
|
87
|
+
}
|