@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.
Files changed (40) hide show
  1. package/.cursor-plugin/plugin.json +1 -1
  2. package/agent-profiles/balanced/massa-ai-audit-specialist.md +2 -1
  3. package/agent-profiles/balanced/massa-ai-test-engineer.md +2 -1
  4. package/agent-profiles/cheap/massa-ai-audit-specialist.md +2 -1
  5. package/agent-profiles/cheap/massa-ai-test-engineer.md +2 -1
  6. package/agent-profiles/heavy/massa-ai-audit-specialist.md +2 -1
  7. package/agent-profiles/heavy/massa-ai-test-engineer.md +2 -1
  8. package/agent-profiles/home/massa-ai-audit-specialist.md +2 -1
  9. package/agent-profiles/home/massa-ai-test-engineer.md +2 -1
  10. package/agent-profiles/work/massa-ai-audit-specialist.md +2 -1
  11. package/agent-profiles/work/massa-ai-test-engineer.md +2 -1
  12. package/agents/massa-ai-audit-specialist.md +2 -1
  13. package/agents/massa-ai-test-engineer.md +2 -1
  14. package/package.json +1 -1
  15. package/skills/agents/audit-specialist/SKILL.md +2 -1
  16. package/skills/agents/test-engineer/SKILL.md +2 -1
  17. package/skills/massa-ai/SKILL.md +2 -1
  18. package/skills/massa-ai/references/coding-guidelines.md +9 -0
  19. package/skills/massa-ai/references/implementation-delivery.md +1 -0
  20. package/skills/massa-ai/references/lessons.md +52 -0
  21. package/skills/massa-ai/references/spec-driven/validate.md +10 -0
  22. package/skills/massa-ai/scripts/lessons.ts +238 -2
  23. package/skills/massa-ai/workflows/architecture/architecture-fix.md +13 -0
  24. package/skills/massa-ai/workflows/bugs/bugs-fix.md +13 -0
  25. package/skills/massa-ai/workflows/code-quality/code-quality-audit.md +4 -3
  26. package/skills/massa-ai/workflows/code-quality/code-quality-fix.md +16 -3
  27. package/skills/massa-ai/workflows/debug.md +13 -0
  28. package/skills/massa-ai/workflows/discovery.md +236 -0
  29. package/skills/massa-ai/workflows/feature.md +18 -4
  30. package/skills/massa-ai/workflows/general.md +13 -0
  31. package/skills/massa-ai/workflows/implementation/implementation-fix.md +13 -0
  32. package/skills/massa-ai/workflows/maestro/maestro-fix.md +13 -0
  33. package/skills/massa-ai/workflows/mobile-figma/mobile-figma-fix.md +13 -0
  34. package/skills/massa-ai/workflows/pr-review.md +1 -1
  35. package/skills/massa-ai/workflows/refactor.md +14 -0
  36. package/skills/massa-ai/workflows/requirements/requirements-fix.md +13 -0
  37. package/skills/massa-ai/workflows/security/security-fix.md +13 -0
  38. package/skills/massa-ai/workflows/spec-driven.md +12 -0
  39. package/skills/massa-ai/workflows/tests/tests-audit.md +15 -1
  40. 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(`usage: ${PROG} [-h] [--root ROOT] {init,add,penalize,list,observe,export,import,prune,status,selftest} ...\n${PROG}: error: ${msg}\n`);
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 with distinct concern groups, such as validation plus persistence or formatting plus dispatch.
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: if accurate description needs "and", recommend splitting.
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 that truly do multiple things, 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.
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