@massa-ai/cursor-plugin 1.30.0 → 1.32.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/.cursor-plugin/plugin.json +1 -1
- package/agent-profiles/balanced/massa-ai-audit-specialist.md +2 -1
- package/agent-profiles/balanced/massa-ai-test-engineer.md +2 -1
- package/agent-profiles/cheap/massa-ai-audit-specialist.md +2 -1
- package/agent-profiles/cheap/massa-ai-test-engineer.md +2 -1
- package/agent-profiles/heavy/massa-ai-audit-specialist.md +2 -1
- package/agent-profiles/heavy/massa-ai-test-engineer.md +2 -1
- package/agent-profiles/home/massa-ai-audit-specialist.md +2 -1
- package/agent-profiles/home/massa-ai-test-engineer.md +2 -1
- package/agent-profiles/work/massa-ai-audit-specialist.md +2 -1
- package/agent-profiles/work/massa-ai-test-engineer.md +2 -1
- package/agents/massa-ai-audit-specialist.md +2 -1
- package/agents/massa-ai-test-engineer.md +2 -1
- package/package.json +1 -1
- package/skills/agents/audit-specialist/SKILL.md +2 -1
- package/skills/agents/test-engineer/SKILL.md +2 -1
- package/skills/massa-ai/SKILL.md +2 -1
- package/skills/massa-ai/references/coding-guidelines.md +9 -0
- package/skills/massa-ai/references/implementation-delivery.md +1 -0
- package/skills/massa-ai/references/lessons.md +52 -0
- package/skills/massa-ai/references/spec-driven/validate.md +10 -0
- package/skills/massa-ai/scripts/lessons.ts +238 -2
- package/skills/massa-ai/workflows/architecture/architecture-fix.md +13 -0
- package/skills/massa-ai/workflows/bugs/bugs-fix.md +13 -0
- package/skills/massa-ai/workflows/code-quality/code-quality-audit.md +4 -3
- package/skills/massa-ai/workflows/code-quality/code-quality-fix.md +16 -3
- package/skills/massa-ai/workflows/debug.md +13 -0
- package/skills/massa-ai/workflows/discovery.md +236 -0
- package/skills/massa-ai/workflows/feature.md +18 -4
- package/skills/massa-ai/workflows/general.md +13 -0
- package/skills/massa-ai/workflows/implementation/implementation-fix.md +13 -0
- package/skills/massa-ai/workflows/maestro/maestro-fix.md +13 -0
- package/skills/massa-ai/workflows/mobile-figma/mobile-figma-fix.md +13 -0
- package/skills/massa-ai/workflows/pr-review.md +1 -1
- package/skills/massa-ai/workflows/refactor.md +14 -0
- package/skills/massa-ai/workflows/requirements/requirements-fix.md +13 -0
- package/skills/massa-ai/workflows/security/security-fix.md +13 -0
- package/skills/massa-ai/workflows/spec-driven.md +12 -0
- package/skills/massa-ai/workflows/tests/tests-audit.md +15 -1
- package/skills/massa-ai/workflows/tests/tests-fix.md +14 -0
|
@@ -23,6 +23,9 @@
|
|
|
23
23
|
* export Export the lessons store as JSON (round-trips with import).
|
|
24
24
|
* import Import lessons from JSON (merge by dedup key; best-effort massa-ai memory).
|
|
25
25
|
* selftest Run stdlib regressions (normalization).
|
|
26
|
+
* review Append reviewer-feedback records and derive per-category trust streaks.
|
|
27
|
+
* trust Print derived per-category trust status (advisory only, AEH-03).
|
|
28
|
+
* metrics Append quality-metric snapshots and print the derived trend verdict (AEH-05).
|
|
26
29
|
*
|
|
27
30
|
* Exit codes: 0 ok, 2 usage/validation error (e.g. missing grounding).
|
|
28
31
|
*/
|
|
@@ -43,6 +46,7 @@ const SIGNALS: Record<string, string> = {
|
|
|
43
46
|
const SIGNAL_KEYS_SORTED = Object.keys(SIGNALS).sort();
|
|
44
47
|
|
|
45
48
|
const DEFAULTS = { promote_threshold: 2, window_days: 45, quarantine_threshold: 2 };
|
|
49
|
+
const RAMP_DEFAULTS = { trust_threshold: 30 };
|
|
46
50
|
|
|
47
51
|
// massa-ai supported memory types (references/mcp-tools.md). `procedural` is a
|
|
48
52
|
// TAG, never a type. Lessons are procedural knowledge -> type `pattern`.
|
|
@@ -69,6 +73,25 @@ interface Lesson {
|
|
|
69
73
|
[key: string]: unknown;
|
|
70
74
|
}
|
|
71
75
|
|
|
76
|
+
/** Reviewer-feedback event (AEH-03). Append-only; category is a free-form kebab-case label. */
|
|
77
|
+
interface ReviewRecord {
|
|
78
|
+
category: string;
|
|
79
|
+
feedback: "none" | "minor" | "major";
|
|
80
|
+
source: string;
|
|
81
|
+
recordedAt: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Per-validation quality-metric snapshot (AEH-05). Append-only. */
|
|
85
|
+
interface MetricSnapshot {
|
|
86
|
+
feature: string;
|
|
87
|
+
result: "PASS" | "FAIL";
|
|
88
|
+
fixLoopIterations: number;
|
|
89
|
+
survivingMutants: number;
|
|
90
|
+
acsTotal: number;
|
|
91
|
+
acsCovered: number;
|
|
92
|
+
recordedAt: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
72
95
|
interface Store {
|
|
73
96
|
schema: number;
|
|
74
97
|
promote_threshold: number;
|
|
@@ -76,6 +99,13 @@ interface Store {
|
|
|
76
99
|
quarantine_threshold: number;
|
|
77
100
|
next_id: number;
|
|
78
101
|
lessons: Lesson[];
|
|
102
|
+
// Ramp-only fields (AEH-03/05) - absent on legacy stores, lazily backfilled by
|
|
103
|
+
// ensureRampFields() inside the new review/trust/metrics commands only, NEVER in
|
|
104
|
+
// load(), so every legacy command keeps reading/writing the store byte-identically
|
|
105
|
+
// to before this feature (pyts-golden protection).
|
|
106
|
+
trust_threshold?: number;
|
|
107
|
+
reviews?: ReviewRecord[];
|
|
108
|
+
metrics?: MetricSnapshot[];
|
|
79
109
|
[key: string]: unknown;
|
|
80
110
|
}
|
|
81
111
|
|
|
@@ -433,6 +463,60 @@ function find(data: Store, signal: string, text: string): Lesson | null {
|
|
|
433
463
|
return null;
|
|
434
464
|
}
|
|
435
465
|
|
|
466
|
+
// ---------------------------------------------------------------------------
|
|
467
|
+
// Trust ramp + metric-trend derivations (AEH-03, AEH-05)
|
|
468
|
+
//
|
|
469
|
+
// review/metrics records are append-only events; streak, trusted, and trend
|
|
470
|
+
// verdict are all derived at read time from the log (design Approach A) - no
|
|
471
|
+
// cached state, so demotion on a `major` record is emergent rather than a
|
|
472
|
+
// write-path invariant that needs its own tests.
|
|
473
|
+
// ---------------------------------------------------------------------------
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Lazily backfills the ramp-only fields (`trust_threshold`, `reviews`, `metrics`)
|
|
477
|
+
* onto an in-memory store. Called ONLY from the new review/trust/metrics commands -
|
|
478
|
+
* NEVER from `load()` - so every legacy command keeps reading/writing the store
|
|
479
|
+
* byte-identically to before this feature (pyts-golden protection).
|
|
480
|
+
*/
|
|
481
|
+
function ensureRampFields(data: Store): void {
|
|
482
|
+
pySetDefault(data as unknown as Record<string, unknown>, "trust_threshold", RAMP_DEFAULTS.trust_threshold);
|
|
483
|
+
pySetDefault(data as unknown as Record<string, unknown>, "reviews", []);
|
|
484
|
+
pySetDefault(data as unknown as Record<string, unknown>, "metrics", []);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/** Count of trailing none|minor records for `category`, scanning newest-first until a major. */
|
|
488
|
+
function categoryStreak(data: Store, category: string): number {
|
|
489
|
+
const records = (data.reviews ?? []).filter((r) => r.category === category);
|
|
490
|
+
let count = 0;
|
|
491
|
+
for (let i = records.length - 1; i >= 0; i--) {
|
|
492
|
+
if (records[i]!.feedback === "major") break;
|
|
493
|
+
count++;
|
|
494
|
+
}
|
|
495
|
+
return count;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function isTrusted(data: Store, category: string): boolean {
|
|
499
|
+
const threshold = data.trust_threshold ?? RAMP_DEFAULTS.trust_threshold;
|
|
500
|
+
return categoryStreak(data, category) >= threshold;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/** Scalar trend score for one snapshot - lower is better. FAIL*100 + mutants*10 + fixIters + uncoveredACs. */
|
|
504
|
+
function trendScore(s: MetricSnapshot): number {
|
|
505
|
+
return (s.result === "FAIL" ? 100 : 0) + s.survivingMutants * 10 + s.fixLoopIterations + (s.acsTotal - s.acsCovered);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/** Compares the last two snapshots' scores (lower = better). <2 snapshots -> "insufficient data". */
|
|
509
|
+
function trendVerdict(snapshots: MetricSnapshot[]): string {
|
|
510
|
+
if (snapshots.length < 2) return "insufficient data";
|
|
511
|
+
const last = snapshots[snapshots.length - 1]!;
|
|
512
|
+
const prev = snapshots[snapshots.length - 2]!;
|
|
513
|
+
const lastScore = trendScore(last);
|
|
514
|
+
const prevScore = trendScore(prev);
|
|
515
|
+
if (lastScore < prevScore) return "improving";
|
|
516
|
+
if (lastScore > prevScore) return "degrading";
|
|
517
|
+
return "stable";
|
|
518
|
+
}
|
|
519
|
+
|
|
436
520
|
// ---------------------------------------------------------------------------
|
|
437
521
|
// Commands
|
|
438
522
|
// ---------------------------------------------------------------------------
|
|
@@ -737,6 +821,108 @@ function cmdStatus(root: string): number {
|
|
|
737
821
|
return 0;
|
|
738
822
|
}
|
|
739
823
|
|
|
824
|
+
interface ReviewAddArgs {
|
|
825
|
+
category: string;
|
|
826
|
+
feedback: string;
|
|
827
|
+
source: string;
|
|
828
|
+
project: string;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
function cmdReviewAdd(root: string, args: ReviewAddArgs): number {
|
|
832
|
+
const category = (args.category || "").trim();
|
|
833
|
+
const source = (args.source || "").trim();
|
|
834
|
+
const data = load(root);
|
|
835
|
+
ensureRampFields(data);
|
|
836
|
+
const record: ReviewRecord = {
|
|
837
|
+
category,
|
|
838
|
+
feedback: args.feedback as ReviewRecord["feedback"],
|
|
839
|
+
source,
|
|
840
|
+
recordedAt: now(),
|
|
841
|
+
};
|
|
842
|
+
data.reviews!.push(record);
|
|
843
|
+
save(root, data);
|
|
844
|
+
const streak = categoryStreak(data, category);
|
|
845
|
+
console.log(`REVIEW ${category} (streak=${streak}, trusted=${isTrusted(data, category)})`);
|
|
846
|
+
return 0;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
function cmdTrustStatus(root: string, categoryFilter: string): number {
|
|
850
|
+
const data = load(root);
|
|
851
|
+
ensureRampFields(data);
|
|
852
|
+
const reviews = data.reviews ?? [];
|
|
853
|
+
let categories = Array.from(new Set(reviews.map((r) => r.category)));
|
|
854
|
+
if (categoryFilter) {
|
|
855
|
+
categories = categories.filter((c) => c === categoryFilter);
|
|
856
|
+
}
|
|
857
|
+
categories.sort(pyStringCompare);
|
|
858
|
+
if (!categories.length) {
|
|
859
|
+
console.log("(no review records)");
|
|
860
|
+
return 0;
|
|
861
|
+
}
|
|
862
|
+
const threshold = data.trust_threshold ?? RAMP_DEFAULTS.trust_threshold;
|
|
863
|
+
for (const category of categories) {
|
|
864
|
+
const total = reviews.filter((r) => r.category === category).length;
|
|
865
|
+
const streak = categoryStreak(data, category);
|
|
866
|
+
const trusted = streak >= threshold;
|
|
867
|
+
console.log(`${category}: streak=${streak}/${threshold} total=${total} trusted=${trusted ? "yes" : "no"}`);
|
|
868
|
+
}
|
|
869
|
+
return 0;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
/** Parses a required non-negative-integer flag value; prints an error naming `flagName` on failure. */
|
|
873
|
+
function parseNonNegativeInt(value: string, flagName: string): number | null {
|
|
874
|
+
const n = Number(value);
|
|
875
|
+
if (!Number.isFinite(n) || !Number.isInteger(n) || n < 0) {
|
|
876
|
+
console.error(`ERROR: ${flagName} must be a non-negative integer, got ${pyRepr(value)}`);
|
|
877
|
+
return null;
|
|
878
|
+
}
|
|
879
|
+
return n;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
function cmdMetricsAdd(root: string, raw: Record<string, string>): number {
|
|
883
|
+
const feature = (raw.feature || "").trim();
|
|
884
|
+
const result = raw.result as MetricSnapshot["result"];
|
|
885
|
+
const fixLoopIterations = parseNonNegativeInt(raw["fix-iterations"]!, "--fix-iterations");
|
|
886
|
+
if (fixLoopIterations === null) return 2;
|
|
887
|
+
const survivingMutants = parseNonNegativeInt(raw["surviving-mutants"]!, "--surviving-mutants");
|
|
888
|
+
if (survivingMutants === null) return 2;
|
|
889
|
+
const acsTotal = parseNonNegativeInt(raw["acs-total"]!, "--acs-total");
|
|
890
|
+
if (acsTotal === null) return 2;
|
|
891
|
+
const acsCovered = parseNonNegativeInt(raw["acs-covered"]!, "--acs-covered");
|
|
892
|
+
if (acsCovered === null) return 2;
|
|
893
|
+
|
|
894
|
+
const data = load(root);
|
|
895
|
+
ensureRampFields(data);
|
|
896
|
+
const snapshot: MetricSnapshot = {
|
|
897
|
+
feature,
|
|
898
|
+
result,
|
|
899
|
+
fixLoopIterations,
|
|
900
|
+
survivingMutants,
|
|
901
|
+
acsTotal,
|
|
902
|
+
acsCovered,
|
|
903
|
+
recordedAt: now(),
|
|
904
|
+
};
|
|
905
|
+
data.metrics!.push(snapshot);
|
|
906
|
+
save(root, data);
|
|
907
|
+
console.log(
|
|
908
|
+
`METRICS ${feature} (result=${result}, survivingMutants=${survivingMutants}, fixIters=${fixLoopIterations}, acs=${acsCovered}/${acsTotal})`,
|
|
909
|
+
);
|
|
910
|
+
return 0;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
function cmdMetricsTrend(root: string): number {
|
|
914
|
+
const data = load(root);
|
|
915
|
+
ensureRampFields(data);
|
|
916
|
+
const snapshots = data.metrics ?? [];
|
|
917
|
+
for (const s of snapshots) {
|
|
918
|
+
console.log(
|
|
919
|
+
`${s.feature} result=${s.result} fixIters=${s.fixLoopIterations} survivingMutants=${s.survivingMutants} acs=${s.acsCovered}/${s.acsTotal} recordedAt=${s.recordedAt}`,
|
|
920
|
+
);
|
|
921
|
+
}
|
|
922
|
+
console.log(`trend: ${trendVerdict(snapshots)}`);
|
|
923
|
+
return 0;
|
|
924
|
+
}
|
|
925
|
+
|
|
740
926
|
// ---------------------------------------------------------------------------
|
|
741
927
|
// CLI
|
|
742
928
|
// ---------------------------------------------------------------------------
|
|
@@ -744,7 +930,9 @@ function cmdStatus(root: string): number {
|
|
|
744
930
|
const PROG = "lessons.ts";
|
|
745
931
|
|
|
746
932
|
function usageError(msg: string): void {
|
|
747
|
-
process.stderr.write(
|
|
933
|
+
process.stderr.write(
|
|
934
|
+
`usage: ${PROG} [-h] [--root ROOT] {init,add,penalize,list,observe,export,import,prune,status,selftest,review,trust,metrics} ...\n${PROG}: error: ${msg}\n`,
|
|
935
|
+
);
|
|
748
936
|
}
|
|
749
937
|
|
|
750
938
|
interface FlagSpec {
|
|
@@ -894,9 +1082,57 @@ async function main(argv: string[]): Promise<number> {
|
|
|
894
1082
|
case "selftest":
|
|
895
1083
|
return selftestNorm();
|
|
896
1084
|
|
|
1085
|
+
case "review": {
|
|
1086
|
+
const sub = rest[0];
|
|
1087
|
+
if (sub === "add") {
|
|
1088
|
+
const parsed = parseFlags(rest.slice(1), [
|
|
1089
|
+
{ name: "--category", required: true },
|
|
1090
|
+
{ name: "--feedback", required: true, choices: ["none", "minor", "major"] },
|
|
1091
|
+
{ name: "--source", required: true },
|
|
1092
|
+
{ name: "--project", default: "" },
|
|
1093
|
+
]);
|
|
1094
|
+
if (!parsed) return 2;
|
|
1095
|
+
return cmdReviewAdd(absRoot, parsed as unknown as ReviewAddArgs);
|
|
1096
|
+
}
|
|
1097
|
+
usageError(`argument cmd: invalid choice: ${pyRepr(sub ?? "")} (choose from 'add')`);
|
|
1098
|
+
return 2;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
case "trust": {
|
|
1102
|
+
const sub = rest[0];
|
|
1103
|
+
if (sub === "status") {
|
|
1104
|
+
const parsed = parseFlags(rest.slice(1), [{ name: "--category", default: "" }]);
|
|
1105
|
+
if (!parsed) return 2;
|
|
1106
|
+
return cmdTrustStatus(absRoot, parsed.category!);
|
|
1107
|
+
}
|
|
1108
|
+
usageError(`argument cmd: invalid choice: ${pyRepr(sub ?? "")} (choose from 'status')`);
|
|
1109
|
+
return 2;
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
case "metrics": {
|
|
1113
|
+
const sub = rest[0];
|
|
1114
|
+
if (sub === "add") {
|
|
1115
|
+
const parsed = parseFlags(rest.slice(1), [
|
|
1116
|
+
{ name: "--feature", required: true },
|
|
1117
|
+
{ name: "--result", required: true, choices: ["PASS", "FAIL"] },
|
|
1118
|
+
{ name: "--fix-iterations", required: true },
|
|
1119
|
+
{ name: "--surviving-mutants", required: true },
|
|
1120
|
+
{ name: "--acs-total", required: true },
|
|
1121
|
+
{ name: "--acs-covered", required: true },
|
|
1122
|
+
]);
|
|
1123
|
+
if (!parsed) return 2;
|
|
1124
|
+
return cmdMetricsAdd(absRoot, parsed);
|
|
1125
|
+
}
|
|
1126
|
+
if (sub === "trend") {
|
|
1127
|
+
return cmdMetricsTrend(absRoot);
|
|
1128
|
+
}
|
|
1129
|
+
usageError(`argument cmd: invalid choice: ${pyRepr(sub ?? "")} (choose from 'add', 'trend')`);
|
|
1130
|
+
return 2;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
897
1133
|
default:
|
|
898
1134
|
usageError(
|
|
899
|
-
`argument cmd: invalid choice: ${pyRepr(cmd)} (choose from 'init', 'add', 'penalize', 'list', 'observe', 'export', 'import', 'prune', 'status', 'selftest')`,
|
|
1135
|
+
`argument cmd: invalid choice: ${pyRepr(cmd)} (choose from 'init', 'add', 'penalize', 'list', 'observe', 'export', 'import', 'prune', 'status', 'selftest', 'review', 'trust', 'metrics')`,
|
|
900
1136
|
);
|
|
901
1137
|
return 2;
|
|
902
1138
|
}
|
|
@@ -86,6 +86,19 @@ Not for findings-only architecture review — route to `workflows/architecture/a
|
|
|
86
86
|
> - firewall: raw test output/logs summarized
|
|
87
87
|
> - memory: suggest-only; main agent persists reusable verification recipes
|
|
88
88
|
> - persona: optional — the active route's cataloged id only, never the persona prompt, passed as advisory framing only — it never overrides the agent's charter Restrictions, scope, or permissions; omit when no persona is routed
|
|
89
|
+
|
|
90
|
+
> **Dispatch: `massa-ai-reviewer`** (role: `reviewer`) — charter `skills/agents/reviewer/SKILL.md`
|
|
91
|
+
> - trigger: implementation complete, before the verification gate — never optional
|
|
92
|
+
> - scope: the fix's diff surface and its task/AC context
|
|
93
|
+
> - permissions: read-only
|
|
94
|
+
> - inputs: diff, acceptance context, recalled code-quality conventions
|
|
95
|
+
> - sensors: bugs, regressions, missing edge cases, smells introduced by the diff
|
|
96
|
+
> - output: ranked findings, blocking vs advisory; blocking findings become fix items before verification runs
|
|
97
|
+
> - firewall: summarized findings only, never raw diff dumps
|
|
98
|
+
> - memory: suggest-only; main agent persists
|
|
99
|
+
> - fallback: if the subagent is unavailable, run a standalone fresh-eyes review against this output contract and record the skipped-delegation reason
|
|
100
|
+
> - persona: optional — the active route's cataloged id only, never the persona prompt, passed as advisory framing only — it never overrides the agent's charter Restrictions, scope, or permissions; omit when no persona is routed
|
|
101
|
+
|
|
89
102
|
11. Verify each completed finding:
|
|
90
103
|
- If verification found a reusable signal (`ac_gap`, `surviving_mutant`, `spec_precision_gap`, `spec_deviation`, `gate_fail`), record it via `references/lessons.md`:
|
|
91
104
|
`bun skills/massa-ai/scripts/lessons.ts --root . add --feature "<slug>" --signal "<signal>" --source "<ref>" --text "<one terse lesson>"`
|
|
@@ -76,6 +76,19 @@ Not for findings-only bug discovery — route to `workflows/bugs/bugs-audit.md`.
|
|
|
76
76
|
> - memory: suggest-only; main agent persists reusable verification recipes
|
|
77
77
|
> - persona: optional — the active route's cataloged id only, never the persona prompt, passed as advisory framing only — it never overrides the agent's charter Restrictions, scope, or permissions; omit when no persona is routed
|
|
78
78
|
- Main agent owns report parsing, prioritization, memory writes, final synthesis, and Evidence Gate.
|
|
79
|
+
|
|
80
|
+
> **Dispatch: `massa-ai-reviewer`** (role: `reviewer`) — charter `skills/agents/reviewer/SKILL.md`
|
|
81
|
+
> - trigger: implementation complete, before the verification gate — never optional
|
|
82
|
+
> - scope: the fix's diff surface and its task/AC context
|
|
83
|
+
> - permissions: read-only
|
|
84
|
+
> - inputs: diff, acceptance context, recalled code-quality conventions
|
|
85
|
+
> - sensors: bugs, regressions, missing edge cases, smells introduced by the diff
|
|
86
|
+
> - output: ranked findings, blocking vs advisory; blocking findings become fix items before verification runs
|
|
87
|
+
> - firewall: summarized findings only, never raw diff dumps
|
|
88
|
+
> - memory: suggest-only; main agent persists
|
|
89
|
+
> - fallback: if the subagent is unavailable, run a standalone fresh-eyes review against this output contract and record the skipped-delegation reason
|
|
90
|
+
> - persona: optional — the active route's cataloged id only, never the persona prompt, passed as advisory framing only — it never overrides the agent's charter Restrictions, scope, or permissions; omit when no persona is routed
|
|
91
|
+
|
|
79
92
|
10. Verify each completed finding:
|
|
80
93
|
- If verification found a reusable signal (`ac_gap`, `surviving_mutant`, `spec_precision_gap`, `spec_deviation`, `gate_fail`), record it via `references/lessons.md`:
|
|
81
94
|
`bun skills/massa-ai/scripts/lessons.ts --root . add --feature "<slug>" --signal "<signal>" --source "<ref>" --text "<one terse lesson>"`
|
|
@@ -76,6 +76,7 @@ Findings-only: do not edit code unless the user separately asks for fixes.
|
|
|
76
76
|
- Magic values: repeated strings, event names, timeouts, numeric thresholds, status codes.
|
|
77
77
|
- Generic names: `data`, `info`, `result`, `value`, `temp`, `manager`, `handler`, `helper` without useful qualification, using `references/naming-standards.md` to filter conventional short-scope or framework-required names.
|
|
78
78
|
- Long parameter lists: more than 3-4 positional parameters.
|
|
79
|
+
- File shape: flag multi-subject files (unrelated exported surfaces bundled together) and any file over ~600 lines, regardless of subject count — it crowds out working context for the rest of the task (see `references/coding-guidelines.md` "File shape for agent readers"). Do NOT flag a single-subject file for line count alone below that bound.
|
|
79
80
|
- Needlessly indirect code: pass-through wrappers, one-use abstractions, helper layers with no behavior, factories/builders that only hide one constructor call.
|
|
80
81
|
- Speculative surfaces: unused options, future-oriented hooks, extension points with one implementation, exported APIs with no evidence of use.
|
|
81
82
|
- Complexity without payoff: deep nesting, miniature state machines, or polymorphism where a direct branch or data map would preserve clarity.
|
|
@@ -84,20 +85,20 @@ Findings-only: do not edit code unless the user separately asks for fixes.
|
|
|
84
85
|
9. Investigation pass:
|
|
85
86
|
- Use summary/enriched search, symbol tools, and targeted file reads to inspect target modules, semantic hotspots, public classes, interfaces, functions, and exported API surface.
|
|
86
87
|
- Apply SOLID checks to non-test source only:
|
|
87
|
-
- Single Responsibility: flag classes/modules
|
|
88
|
+
- Single Responsibility: flag classes/modules bundling distinct concern groups, such as validation plus persistence or formatting plus dispatch, only when separating them yields an externally-findable named unit (locatable by search or grep from outside the file) or measurably reduces change risk — never on concern-count or size alone.
|
|
88
89
|
- Open/Closed: flag caller-side switches or if/else chains on type tags where adding a variant requires modifying existing files.
|
|
89
90
|
- Liskov: flag subtypes that throw where the base does not, ignore required methods, or narrow the base contract.
|
|
90
91
|
- Interface Segregation: flag interfaces that force implementors to define unused methods.
|
|
91
92
|
- Dependency Inversion: flag hardcoded `new ConcreteType()` inside class bodies where abstraction or injection would be natural.
|
|
92
93
|
- Apply Clean Code checks to test and non-test source:
|
|
93
94
|
- Magic values: meaningful bare literals should be named constants, especially repeated strings, timeouts, thresholds, and event names.
|
|
94
|
-
- Function does more than one thing:
|
|
95
|
+
- Function does more than one thing: split only when the result yields an externally-findable named unit (locatable by search or grep from outside the file) or measurably reduces change risk; never split on size or "more than one thing" alone.
|
|
95
96
|
- Unqualified generic names: flag vague names without domain or role qualification.
|
|
96
97
|
- What-comments: flag comments that restate code; keep only why comments for constraints, workarounds, or non-obvious invariants.
|
|
97
98
|
- Half-finished surfaces: flag exported TODOs, stubs, placeholder returns, and "implement later" code.
|
|
98
99
|
- Long parameter lists: flag more than 3-4 positional parameters; suggest an options object.
|
|
99
100
|
- Apply KISS/YAGNI/DRY checks:
|
|
100
|
-
- KISS: flag abstractions, layers, indirection, or control flow that raise cognitive load without clearly improving readability, correctness, or constraint handling. Call out premature generalization, deep call chains, excessive configuration, and clever patterns that obscure intent. Prefer straightforward, explicit code a new reader can follow end-to-end: inline trivial abstractions, collapse unnecessary layers, choose boring solutions unless complexity is justified (real variability, hard constraints, or measured bottlenecks).
|
|
101
|
+
- KISS: flag abstractions, layers, indirection, or control flow that raise cognitive load without clearly improving readability, correctness, or constraint handling. Call out premature generalization, deep call chains, excessive configuration, and clever patterns that obscure intent. Prefer straightforward, explicit code a new reader can follow end-to-end: inline trivial abstractions, collapse unnecessary layers, choose boring solutions unless complexity is justified (real variability, hard constraints, or measured bottlenecks). When weighing whether to split instead of inline, apply the same discoverability-or-change-risk criterion used for the split lead above.
|
|
101
102
|
- YAGNI: flag speculative features, extension points, and generic infrastructure with no concrete caller, requirement, or near-term use. Call out "just in case" hooks, over-parameterization, unused toggles, and frameworks introduced ahead of need. Prefer implementing only what current use cases demand, structured to evolve when real requirements appear. Defer generalization until duplication or constraints force it, and remove dead or unused paths aggressively.
|
|
102
103
|
- DRY: flag duplicated logic, data transformations, or domain rules repeated without a strong reason (e.g., performance isolation or explicit decoupling). Highlight copy-paste patterns, parallel conditionals, and repeated constants that raise maintenance cost or inconsistency risk. Recommend consolidation into a single source of truth when it improves clarity and reduces bugs, but avoid over-abstraction that harms readability or adds indirection for trivial reuse.
|
|
103
104
|
- Prefer delete, inline, or merge recommendations over replacement abstractions when simpler code preserves behavior.
|
|
@@ -48,9 +48,9 @@ Not for findings-only SOLID, Clean Code, KISS, YAGNI, DRY, maintainability, or o
|
|
|
48
48
|
- Standard: multi-file consolidation, shared behavior cleanup, public helper contract change, or meaningful test impact; define characterization checks first.
|
|
49
49
|
- Spec-driven: broad redesign, unclear behavior, cross-boundary migration, or user-visible behavior change; pause and route to `workflows/spec-driven.md` or ask for approval.
|
|
50
50
|
8. Apply code quality fixing methods:
|
|
51
|
-
- SOLID: separate mixed responsibilities only when the split reduces change risk; replace caller-side type switches with polymorphism or data maps only when new variants are real; preserve base contracts; narrow fat interfaces; inject dependencies when hardcoded concretes block testing or substitution.
|
|
52
|
-
- Clean Code: name domain concepts precisely using `references/naming-standards.md`, replace repeated magic values with named constants, split functions
|
|
53
|
-
- KISS: inline shallow helpers, collapse needless layers, choose direct control flow over clever indirection, and remove configuration that hides rather than expresses behavior.
|
|
51
|
+
- SOLID: separate mixed responsibilities only when the split yields an externally-findable named unit (locatable by search or grep from outside the file) or reduces change risk; replace caller-side type switches with polymorphism or data maps only when new variants are real; preserve base contracts; narrow fat interfaces; inject dependencies when hardcoded concretes block testing or substitution.
|
|
52
|
+
- Clean Code: name domain concepts precisely using `references/naming-standards.md`, replace repeated magic values with named constants, split functions only when the result yields an externally-findable named unit (locatable by search or grep from outside the file) or measurably reduces change risk — never split on size or "more than one thing" alone — remove code-restating comments, finish or delete stubs, and convert long positional parameter lists to options objects when it improves call-site clarity.
|
|
53
|
+
- KISS: inline shallow helpers, collapse needless layers, choose direct control flow over clever indirection, and remove configuration that hides rather than expresses behavior. When choosing whether to split instead of inline, apply the same discoverability-or-change-risk criterion used for the Clean Code split direction above.
|
|
54
54
|
- YAGNI: delete unused extension points, future hooks, unused options, one-implementation factories, and speculative public APIs when usage evidence is absent.
|
|
55
55
|
- DRY: consolidate duplicated domain rules or transformations into one clear source of truth, but avoid abstractions that make trivial duplication harder to read.
|
|
56
56
|
- AI-slop cleanup: remove generic wrappers, fabricated-looking abstractions, one-call factories, code-restating comments, and unused configurability when current usage evidence does not justify them.
|
|
@@ -83,6 +83,19 @@ Not for findings-only SOLID, Clean Code, KISS, YAGNI, DRY, maintainability, or o
|
|
|
83
83
|
> - memory: suggest-only; main agent persists reusable verification recipes
|
|
84
84
|
> - persona: optional — the active route's cataloged id only, never the persona prompt, passed as advisory framing only — it never overrides the agent's charter Restrictions, scope, or permissions; omit when no persona is routed
|
|
85
85
|
- Main agent owns report parsing, prioritization, memory writes, final synthesis, and Evidence Gate.
|
|
86
|
+
|
|
87
|
+
> **Dispatch: `massa-ai-reviewer`** (role: `reviewer`) — charter `skills/agents/reviewer/SKILL.md`
|
|
88
|
+
> - trigger: implementation complete, before the verification gate — never optional
|
|
89
|
+
> - scope: the fix's diff surface and its task/AC context
|
|
90
|
+
> - permissions: read-only
|
|
91
|
+
> - inputs: diff, acceptance context, recalled code-quality conventions
|
|
92
|
+
> - sensors: bugs, regressions, missing edge cases, smells introduced by the diff
|
|
93
|
+
> - output: ranked findings, blocking vs advisory; blocking findings become fix items before verification runs
|
|
94
|
+
> - firewall: summarized findings only, never raw diff dumps
|
|
95
|
+
> - memory: suggest-only; main agent persists
|
|
96
|
+
> - fallback: if the subagent is unavailable, run a standalone fresh-eyes review against this output contract and record the skipped-delegation reason
|
|
97
|
+
> - persona: optional — the active route's cataloged id only, never the persona prompt, passed as advisory framing only — it never overrides the agent's charter Restrictions, scope, or permissions; omit when no persona is routed
|
|
98
|
+
|
|
86
99
|
11. Verify each completed finding:
|
|
87
100
|
- If verification found a reusable signal (`ac_gap`, `surviving_mutant`, `spec_precision_gap`, `spec_deviation`, `gate_fail`), record it via `references/lessons.md`:
|
|
88
101
|
`bun skills/massa-ai/scripts/lessons.ts --root . add --feature "<slug>" --signal "<signal>" --source "<ref>" --text "<one terse lesson>"`
|
|
@@ -57,6 +57,19 @@ Before the first repository mutation, load `references/implementation-delivery.m
|
|
|
57
57
|
- file-integrity checks for validation assets such as tests, specs, benchmarks, fixtures, and snapshots
|
|
58
58
|
12. Fix the divergence point closest to the root cause
|
|
59
59
|
13. Add regression coverage at the correct seam, or document why no valid regression seam exists
|
|
60
|
+
|
|
61
|
+
> **Dispatch: `massa-ai-reviewer`** (role: `reviewer`) — charter `skills/agents/reviewer/SKILL.md`
|
|
62
|
+
> - trigger: implementation complete, before the verification gate — never optional
|
|
63
|
+
> - scope: the fix's diff surface and its task/AC context
|
|
64
|
+
> - permissions: read-only
|
|
65
|
+
> - inputs: diff, acceptance context, recalled code-quality conventions
|
|
66
|
+
> - sensors: bugs, regressions, missing edge cases, smells introduced by the diff
|
|
67
|
+
> - output: ranked findings, blocking vs advisory; blocking findings become fix items before verification runs
|
|
68
|
+
> - firewall: summarized findings only, never raw diff dumps
|
|
69
|
+
> - memory: suggest-only; main agent persists
|
|
70
|
+
> - fallback: if the subagent is unavailable, run a standalone fresh-eyes review against this output contract and record the skipped-delegation reason
|
|
71
|
+
> - persona: optional — the active route's cataloged id only, never the persona prompt, passed as advisory framing only — it never overrides the agent's charter Restrictions, scope, or permissions; omit when no persona is routed
|
|
72
|
+
|
|
60
73
|
14. If verification found a reusable signal (`ac_gap`, `surviving_mutant`, `spec_precision_gap`, `spec_deviation`, `gate_fail`), record it via `references/lessons.md`:
|
|
61
74
|
`bun skills/massa-ai/scripts/lessons.ts --root . add --feature "<slug>" --signal "<signal>" --source "<ref>" --text "<one terse lesson>"`
|
|
62
75
|
Rerun the original feedback loop, run the verification recipe, and remove temporary instrumentation unless intentionally retained as observability
|