@specverse/engines 6.42.3 → 6.60.1

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 (143) hide show
  1. package/dist/ai/analyse-runner.d.ts.map +1 -1
  2. package/dist/ai/analyse-runner.js +53 -1
  3. package/dist/ai/analyse-runner.js.map +1 -1
  4. package/dist/ai/prompt-runner.d.ts +39 -1
  5. package/dist/ai/prompt-runner.d.ts.map +1 -1
  6. package/dist/ai/prompt-runner.js +44 -3
  7. package/dist/ai/prompt-runner.js.map +1 -1
  8. package/dist/ai/providers/claude-cli.d.ts.map +1 -1
  9. package/dist/ai/providers/claude-cli.js +8 -1
  10. package/dist/ai/providers/claude-cli.js.map +1 -1
  11. package/dist/ai/skill-loader.d.ts +50 -0
  12. package/dist/ai/skill-loader.d.ts.map +1 -0
  13. package/dist/ai/skill-loader.js +96 -0
  14. package/dist/ai/skill-loader.js.map +1 -0
  15. package/dist/analyse-prepass/adapters/index.d.ts +2 -0
  16. package/dist/analyse-prepass/adapters/index.d.ts.map +1 -1
  17. package/dist/analyse-prepass/adapters/index.js +2 -0
  18. package/dist/analyse-prepass/adapters/index.js.map +1 -1
  19. package/dist/analyse-prepass/adapters/module-functions.d.ts +95 -0
  20. package/dist/analyse-prepass/adapters/module-functions.d.ts.map +1 -0
  21. package/dist/analyse-prepass/adapters/module-functions.js +358 -0
  22. package/dist/analyse-prepass/adapters/module-functions.js.map +1 -0
  23. package/dist/analyse-prepass/adapters/naming-convention-fks.d.ts +90 -0
  24. package/dist/analyse-prepass/adapters/naming-convention-fks.d.ts.map +1 -0
  25. package/dist/analyse-prepass/adapters/naming-convention-fks.js +181 -0
  26. package/dist/analyse-prepass/adapters/naming-convention-fks.js.map +1 -0
  27. package/dist/analyse-prepass/index.d.ts +8 -0
  28. package/dist/analyse-prepass/index.d.ts.map +1 -1
  29. package/dist/analyse-prepass/index.js +130 -0
  30. package/dist/analyse-prepass/index.js.map +1 -1
  31. package/dist/libs/instance-factories/cli/templates/commander/cli-entry-generator.js +11 -12
  32. package/dist/libs/instance-factories/cli/templates/commander/command-generator.js +2 -2
  33. package/dist/libs/instance-factories/controllers/templates/fastify/routes-generator.js +29 -10
  34. package/dist/libs/instance-factories/services/templates/prisma/ai-behaviors-generator.js +10 -9
  35. package/dist/libs/instance-factories/services/templates/prisma/behavior-generator.js +24 -2
  36. package/dist/libs/instance-factories/services/templates/prisma/controller-generator.js +28 -20
  37. package/dist/normalise/index.d.ts +14 -0
  38. package/dist/normalise/index.d.ts.map +1 -0
  39. package/dist/normalise/index.js +14 -0
  40. package/dist/normalise/index.js.map +1 -0
  41. package/dist/normalise/load-overrides.d.ts +43 -0
  42. package/dist/normalise/load-overrides.d.ts.map +1 -0
  43. package/dist/normalise/load-overrides.js +121 -0
  44. package/dist/normalise/load-overrides.js.map +1 -0
  45. package/dist/normalise/normalise-rules.d.ts +181 -0
  46. package/dist/normalise/normalise-rules.d.ts.map +1 -0
  47. package/dist/normalise/normalise-rules.js +79 -0
  48. package/dist/normalise/normalise-rules.js.map +1 -0
  49. package/dist/normalise/rules/cluster-module-functions.d.ts +31 -0
  50. package/dist/normalise/rules/cluster-module-functions.d.ts.map +1 -0
  51. package/dist/normalise/rules/cluster-module-functions.js +238 -0
  52. package/dist/normalise/rules/cluster-module-functions.js.map +1 -0
  53. package/dist/normalise/rules/crud-into-curved.d.ts +117 -0
  54. package/dist/normalise/rules/crud-into-curved.d.ts.map +1 -0
  55. package/dist/normalise/rules/crud-into-curved.js +303 -0
  56. package/dist/normalise/rules/crud-into-curved.js.map +1 -0
  57. package/dist/normalise/rules/drop-trivial-passthrough.d.ts +92 -0
  58. package/dist/normalise/rules/drop-trivial-passthrough.d.ts.map +1 -0
  59. package/dist/normalise/rules/drop-trivial-passthrough.js +217 -0
  60. package/dist/normalise/rules/drop-trivial-passthrough.js.map +1 -0
  61. package/dist/normalise/runner.d.ts +58 -0
  62. package/dist/normalise/runner.d.ts.map +1 -0
  63. package/dist/normalise/runner.js +114 -0
  64. package/dist/normalise/runner.js.map +1 -0
  65. package/dist/parser/import-resolver/resolver.js +1 -1
  66. package/dist/parser/import-resolver/resolver.js.map +1 -1
  67. package/dist/realize/engines/typescript-engine.js +1 -1
  68. package/dist/realize/engines/typescript-engine.js.map +1 -1
  69. package/dist/realize/index.d.ts.map +1 -1
  70. package/dist/realize/index.js +221 -88
  71. package/dist/realize/index.js.map +1 -1
  72. package/dist/realize/library/library.js +1 -1
  73. package/dist/realize/library/library.js.map +1 -1
  74. package/dist/realize/library/resolver.d.ts.map +1 -1
  75. package/dist/realize/library/resolver.js +14 -1
  76. package/dist/realize/library/resolver.js.map +1 -1
  77. package/dist/realize/owner-emit-shared.d.ts +114 -0
  78. package/dist/realize/owner-emit-shared.d.ts.map +1 -0
  79. package/dist/realize/owner-emit-shared.js +227 -0
  80. package/dist/realize/owner-emit-shared.js.map +1 -0
  81. package/dist/realize/per-action-recovery.d.ts +74 -0
  82. package/dist/realize/per-action-recovery.d.ts.map +1 -0
  83. package/dist/realize/per-action-recovery.js +268 -0
  84. package/dist/realize/per-action-recovery.js.map +1 -0
  85. package/dist/realize/per-owner-emit.d.ts +7 -58
  86. package/dist/realize/per-owner-emit.d.ts.map +1 -1
  87. package/dist/realize/per-owner-emit.js +67 -215
  88. package/dist/realize/per-owner-emit.js.map +1 -1
  89. package/dist/realize/per-owner-runner.d.ts +24 -4
  90. package/dist/realize/per-owner-runner.d.ts.map +1 -1
  91. package/dist/realize/per-owner-runner.js +77 -19
  92. package/dist/realize/per-owner-runner.js.map +1 -1
  93. package/dist/realize/post-emit-verify/diagnostics.d.ts +107 -0
  94. package/dist/realize/post-emit-verify/diagnostics.d.ts.map +1 -0
  95. package/dist/realize/post-emit-verify/diagnostics.js +148 -0
  96. package/dist/realize/post-emit-verify/diagnostics.js.map +1 -0
  97. package/dist/realize/post-emit-verify/feedback-runner.d.ts +123 -0
  98. package/dist/realize/post-emit-verify/feedback-runner.d.ts.map +1 -0
  99. package/dist/realize/post-emit-verify/feedback-runner.js +232 -0
  100. package/dist/realize/post-emit-verify/feedback-runner.js.map +1 -0
  101. package/dist/realize/post-emit-verify/index.d.ts +19 -0
  102. package/dist/realize/post-emit-verify/index.d.ts.map +1 -0
  103. package/dist/realize/post-emit-verify/index.js +18 -0
  104. package/dist/realize/post-emit-verify/index.js.map +1 -0
  105. package/dist/realize/post-emit-verify/reemit.d.ts +82 -0
  106. package/dist/realize/post-emit-verify/reemit.d.ts.map +1 -0
  107. package/dist/realize/post-emit-verify/reemit.js +124 -0
  108. package/dist/realize/post-emit-verify/reemit.js.map +1 -0
  109. package/dist/realize/post-emit-verify/types.d.ts +187 -0
  110. package/dist/realize/post-emit-verify/types.d.ts.map +1 -0
  111. package/dist/realize/post-emit-verify/types.js +28 -0
  112. package/dist/realize/post-emit-verify/types.js.map +1 -0
  113. package/dist/realize/post-emit-verify/verifier-manifest.d.ts +29 -0
  114. package/dist/realize/post-emit-verify/verifier-manifest.d.ts.map +1 -0
  115. package/dist/realize/post-emit-verify/verifier-manifest.js +57 -0
  116. package/dist/realize/post-emit-verify/verifier-manifest.js.map +1 -0
  117. package/dist/realize/post-emit-verify/verifiers/stub-completeness.d.ts +85 -0
  118. package/dist/realize/post-emit-verify/verifiers/stub-completeness.d.ts.map +1 -0
  119. package/dist/realize/post-emit-verify/verifiers/stub-completeness.js +298 -0
  120. package/dist/realize/post-emit-verify/verifiers/stub-completeness.js.map +1 -0
  121. package/dist/realize/post-emit-verify/verifiers/tsc.d.ts +24 -0
  122. package/dist/realize/post-emit-verify/verifiers/tsc.d.ts.map +1 -0
  123. package/dist/realize/post-emit-verify/verifiers/tsc.js +148 -0
  124. package/dist/realize/post-emit-verify/verifiers/tsc.js.map +1 -0
  125. package/dist/realize/realize-context-snapshot.d.ts +70 -0
  126. package/dist/realize/realize-context-snapshot.d.ts.map +1 -0
  127. package/dist/realize/realize-context-snapshot.js +96 -0
  128. package/dist/realize/realize-context-snapshot.js.map +1 -0
  129. package/dist/realize/realize-rules.d.ts +113 -0
  130. package/dist/realize/realize-rules.d.ts.map +1 -0
  131. package/dist/realize/realize-rules.js +271 -0
  132. package/dist/realize/realize-rules.js.map +1 -0
  133. package/dist/realize/structural-validator.d.ts +36 -2
  134. package/dist/realize/structural-validator.d.ts.map +1 -1
  135. package/dist/realize/structural-validator.js +50 -7
  136. package/dist/realize/structural-validator.js.map +1 -1
  137. package/libs/instance-factories/cli/templates/commander/cli-entry-generator.ts +11 -12
  138. package/libs/instance-factories/cli/templates/commander/command-generator.ts +2 -2
  139. package/libs/instance-factories/controllers/templates/fastify/routes-generator.ts +49 -15
  140. package/libs/instance-factories/services/templates/prisma/ai-behaviors-generator.ts +19 -3
  141. package/libs/instance-factories/services/templates/prisma/behavior-generator.ts +62 -2
  142. package/libs/instance-factories/services/templates/prisma/controller-generator.ts +47 -20
  143. package/package.json +9 -1
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Normalise rule manifest.
3
+ *
4
+ * Single source of truth for every deterministic spec-reshape rule
5
+ * applied between analyse (faithful extraction) and inference. Mirrors
6
+ * the realize-rules manifest pattern (see `engines/src/realize/realize-rules.ts`):
7
+ * one declaration per rule, three consumers (runner, per-rule corpus,
8
+ * project-override loader).
9
+ *
10
+ * Phase 1a (engines 6.49.x) of the 2026-05-13-NORMALISE-PHASE proposal —
11
+ * INFRASTRUCTURE only. The RULES array is empty in this phase; the
12
+ * runner, manifest interface, and tests land first. Real rules
13
+ * (`cluster-module-functions`, `crud-into-curved`,
14
+ * `drop-trivial-passthrough`) land in Phase 2+.
15
+ *
16
+ * Why in engines/src/normalise/ (a new subsystem):
17
+ * Normalise is its own phase between analyse and inference, with its
18
+ * own pluggable rule set. Putting it under realize/ would conflate
19
+ * two different rule-classes (one for post-LLM-emit validation, one
20
+ * for pre-realize spec reshape). Following the entity-module
21
+ * colocation principle (R12), normalise gets its own home.
22
+ */
23
+ import { CLUSTER_MODULE_FUNCTIONS_RULE } from './rules/cluster-module-functions.js';
24
+ import { CRUD_INTO_CURVED_RULE } from './rules/crud-into-curved.js';
25
+ import { DROP_TRIVIAL_PASSTHROUGH_RULE } from './rules/drop-trivial-passthrough.js';
26
+ /**
27
+ * The canonical rule manifest. Append rules as they land. Order in
28
+ * the array is informational only — the runner sorts by `ordering`.
29
+ *
30
+ * Phase 2 of `2026-05-13-NORMALISE-PHASE.md` §5:
31
+ * - `cluster-module-functions` (ordering 0): strip project-prefix
32
+ * from services synthesised by the module-functions adapter.
33
+ *
34
+ * Remaining rules (Phase 3-4 in §5):
35
+ * - `crud-into-curved`: collapse Service ops 1:1 with CURVED to entity
36
+ * - `drop-trivial-passthrough`: drop single-delegate-call ops
37
+ */
38
+ export const RULES = [
39
+ CLUSTER_MODULE_FUNCTIONS_RULE,
40
+ CRUD_INTO_CURVED_RULE,
41
+ DROP_TRIVIAL_PASSTHROUGH_RULE,
42
+ ];
43
+ /**
44
+ * Filter the manifest to rules applicable in `ctx`. Applies the
45
+ * standard inclusion logic in one place:
46
+ *
47
+ * 1. `disabledRules` in ctx wins — always skip.
48
+ * 2. `enabledByDefault: true` AND not in disabledRules → include.
49
+ * 3. `enabledByDefault: false` AND id is in enabledRules → include.
50
+ * 4. `appliesWhen` (when present) must return true.
51
+ *
52
+ * Returned rules are sorted by `ordering` ascending.
53
+ */
54
+ export function rulesApplicableTo(ctx) {
55
+ const disabled = ctx.disabledRules ?? new Set();
56
+ const enabled = ctx.enabledRules ?? new Set();
57
+ return RULES
58
+ .filter((r) => {
59
+ if (disabled.has(r.id))
60
+ return false;
61
+ const enabledByConfig = r.enabledByDefault || enabled.has(r.id);
62
+ if (!enabledByConfig)
63
+ return false;
64
+ if (r.appliesWhen && !r.appliesWhen(ctx))
65
+ return false;
66
+ return true;
67
+ })
68
+ .slice()
69
+ .sort((a, b) => a.ordering - b.ordering);
70
+ }
71
+ /**
72
+ * Find a rule by id. Returns undefined when no rule with that id is
73
+ * registered. Used by tests, project-override loaders, audit-trail
74
+ * emission.
75
+ */
76
+ export function getRule(id) {
77
+ return RULES.find((r) => r.id === id);
78
+ }
79
+ //# sourceMappingURL=normalise-rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalise-rules.js","sourceRoot":"","sources":["../../src/normalise/normalise-rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,6BAA6B,EAAE,MAAM,qCAAqC,CAAC;AACpF,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,6BAA6B,EAAE,MAAM,qCAAqC,CAAC;AAoIpF;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,KAAK,GAAoB;IACpC,6BAA6B;IAC7B,qBAAqB;IACrB,6BAA6B;CAC9B,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAqB;IACrD,MAAM,QAAQ,GAAG,GAAG,CAAC,aAAa,IAAI,IAAI,GAAG,EAAU,CAAC;IACxD,MAAM,OAAO,GAAG,GAAG,CAAC,YAAY,IAAI,IAAI,GAAG,EAAU,CAAC;IACtD,OAAO,KAAK;SACT,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QACZ,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YAAE,OAAO,KAAK,CAAC;QACrC,MAAM,eAAe,GAAG,CAAC,CAAC,gBAAgB,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAChE,IAAI,CAAC,eAAe;YAAE,OAAO,KAAK,CAAC;QACnC,IAAI,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QACvD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;SACD,KAAK,EAAE;SACP,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;AAC7C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,OAAO,CAAC,EAAU;IAChC,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AACxC,CAAC"}
@@ -0,0 +1,31 @@
1
+ import type { NormaliseRule } from '../normalise-rules.js';
2
+ interface ClusterMatchData {
3
+ componentName: string;
4
+ oldServiceName: string;
5
+ newServiceName: string;
6
+ prefix: string;
7
+ /** Map from old operation key to new operation key. */
8
+ opRenames: Map<string, string>;
9
+ }
10
+ /**
11
+ * Find the longest lowercase prefix shared by `names`, ending strictly
12
+ * before the first uppercase letter (camelCase boundary). Returns the
13
+ * empty string when no such prefix is found.
14
+ */
15
+ declare function findCamelPrefix(names: string[]): string;
16
+ declare function stripCamelPrefix(name: string, prefix: string): string;
17
+ declare function stripPascalPrefix(serviceName: string, prefix: string): string | null;
18
+ /**
19
+ * Detect whether the service with `serviceName` is a cluster-cleanup
20
+ * candidate. See file-header doc for the two detection signals.
21
+ */
22
+ declare function detectClusterMatch(componentName: string, serviceName: string, serviceBody: Record<string, unknown>): ClusterMatchData | null;
23
+ export declare const CLUSTER_MODULE_FUNCTIONS_RULE: NormaliseRule;
24
+ export declare const __internal: {
25
+ findCamelPrefix: typeof findCamelPrefix;
26
+ stripCamelPrefix: typeof stripCamelPrefix;
27
+ stripPascalPrefix: typeof stripPascalPrefix;
28
+ detectClusterMatch: typeof detectClusterMatch;
29
+ };
30
+ export {};
31
+ //# sourceMappingURL=cluster-module-functions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cluster-module-functions.d.ts","sourceRoot":"","sources":["../../../src/normalise/rules/cluster-module-functions.ts"],"names":[],"mappings":"AA6DA,OAAO,KAAK,EACV,aAAa,EAId,MAAM,uBAAuB,CAAC;AAE/B,UAAU,gBAAgB;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED;;;;GAIG;AACH,iBAAS,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAYhD;AAED,iBAAS,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAK9D;AAED,iBAAS,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQ7E;AAED;;;GAGG;AACH,iBAAS,kBAAkB,CACzB,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,gBAAgB,GAAG,IAAI,CAwDzB;AAkCD,eAAO,MAAM,6BAA6B,EAAE,aAuC3C,CAAC;AAGF,eAAO,MAAM,UAAU;;;;;CAKtB,CAAC"}
@@ -0,0 +1,238 @@
1
+ /**
2
+ * Normalise rule: `cluster-module-functions`.
3
+ *
4
+ * Phase 2 of the 2026-05-13-NORMALISE-PHASE proposal. Cleans up
5
+ * project-prefix noise on services synthesised by the module-functions
6
+ * adapter (Phase 0 prepass change).
7
+ *
8
+ * Trigger pattern: PowerSync / Drizzle / Supabase-queries codebases
9
+ * tend to prefix module functions with a project tag:
10
+ *
11
+ * export async function smcCreateRound(...)
12
+ * export async function smcGetRound(...)
13
+ * export async function smcUpdateRoundStatus(...)
14
+ *
15
+ * The prepass synthesises one service per file. After Phase 0 the
16
+ * faithful spec looks like:
17
+ *
18
+ * SmcRoundsService:
19
+ * operations:
20
+ * smcCreateRound: { ... }
21
+ * smcGetRound: { ... }
22
+ * smcUpdateRoundStatus: { ... }
23
+ *
24
+ * After this rule fires:
25
+ *
26
+ * RoundsService:
27
+ * operations:
28
+ * createRound: { ... }
29
+ * getRound: { ... }
30
+ * updateRoundStatus: { ... }
31
+ *
32
+ * The `Smc` / `smc` project prefix appears on both the service name
33
+ * (PascalCase) and operation names (camelCase). Stripping it from both
34
+ * removes noise while preserving the rest of the naming so the LLM-
35
+ * authored bodies still reference the right entities.
36
+ *
37
+ * What this rule does NOT do (deferred to later rules):
38
+ * - Rename service from `RoundsService` to `RoundService` based on
39
+ * detected target entity (would be `cluster-module-functions`'s
40
+ * dominant-entity inference, per proposal §5 — separate rule)
41
+ * - Collapse `RoundsService.getRound` → `Round.retrieve` CURVED
42
+ * (that's `crud-into-curved`, Rule 2)
43
+ * - Drop trivial passthrough ops (Rule 3)
44
+ *
45
+ * Conservative invariants:
46
+ * - Prefix must be 2–3 lowercase letters ending just before an
47
+ * uppercase letter (camelCase boundary). The 3-char ceiling is the
48
+ * load-bearing false-positive defence: real project tags are short
49
+ * non-words (`smc`, `db`, `api`, `iam`); English verbs that share
50
+ * a camelCase head (`save`, `claim`, `format`, `calculate`,
51
+ * `invalidate`) are 4+ characters and stay untouched.
52
+ * - At least ONE operation must share the prefix (no service-name-
53
+ * only renames — `SaveGameService` doesn't get stripped to
54
+ * `GameService` just because `Save` appears at the start of the
55
+ * service name; that's a meaningful word, not a project tag).
56
+ * - Skip when stripping would produce a name collision.
57
+ * - Skip when stripping would produce an empty / invalid name.
58
+ * - Skip when ctx.source === 'hand-authored'.
59
+ */
60
+ const MAX_PREFIX_LENGTH = 3;
61
+ /**
62
+ * Find the longest lowercase prefix shared by `names`, ending strictly
63
+ * before the first uppercase letter (camelCase boundary). Returns the
64
+ * empty string when no such prefix is found.
65
+ */
66
+ function findCamelPrefix(names) {
67
+ if (names.length === 0)
68
+ return '';
69
+ const reference = names[0];
70
+ let candidate = '';
71
+ for (let i = 0; i < reference.length; i++) {
72
+ const c = reference[i];
73
+ if (c >= 'A' && c <= 'Z')
74
+ break;
75
+ if (!(c >= 'a' && c <= 'z'))
76
+ break;
77
+ if (!names.every((n) => n[i] === c))
78
+ break;
79
+ candidate += c;
80
+ }
81
+ return candidate;
82
+ }
83
+ function stripCamelPrefix(name, prefix) {
84
+ if (!name.startsWith(prefix))
85
+ return name;
86
+ const rest = name.slice(prefix.length);
87
+ if (rest.length === 0)
88
+ return name;
89
+ return rest[0].toLowerCase() + rest.slice(1);
90
+ }
91
+ function stripPascalPrefix(serviceName, prefix) {
92
+ if (prefix.length === 0)
93
+ return null;
94
+ const pascalPrefix = prefix[0].toUpperCase() + prefix.slice(1).toLowerCase();
95
+ if (!serviceName.startsWith(pascalPrefix))
96
+ return null;
97
+ const rest = serviceName.slice(pascalPrefix.length);
98
+ if (rest.length === 0)
99
+ return null;
100
+ if (!(rest[0] >= 'A' && rest[0] <= 'Z'))
101
+ return null;
102
+ return rest;
103
+ }
104
+ /**
105
+ * Detect whether the service with `serviceName` is a cluster-cleanup
106
+ * candidate. See file-header doc for the two detection signals.
107
+ */
108
+ function detectClusterMatch(componentName, serviceName, serviceBody) {
109
+ const ops = serviceBody.operations;
110
+ if (!ops || typeof ops !== 'object')
111
+ return null;
112
+ const opNames = Object.keys(ops);
113
+ if (opNames.length === 0)
114
+ return null;
115
+ // Signal A — multi-operation common prefix.
116
+ let prefix = '';
117
+ if (opNames.length >= 2) {
118
+ prefix = findCamelPrefix(opNames);
119
+ }
120
+ // Signal B — service-name prefix matched by ≥1 op's camel prefix.
121
+ if ((prefix.length < 2 || prefix.length > MAX_PREFIX_LENGTH) && opNames.length >= 1) {
122
+ for (const op of opNames) {
123
+ const opPrefix = findCamelPrefix([op]);
124
+ if (opPrefix.length < 2 || opPrefix.length > MAX_PREFIX_LENGTH)
125
+ continue;
126
+ if (stripPascalPrefix(serviceName, opPrefix) !== null) {
127
+ prefix = opPrefix;
128
+ break;
129
+ }
130
+ }
131
+ }
132
+ // Length cap: project tags are short. Anything longer is an English
133
+ // verb like `save` / `claim` / `calculate` that we mustn't strip.
134
+ if (prefix.length < 2 || prefix.length > MAX_PREFIX_LENGTH)
135
+ return null;
136
+ // Compute the renames + check for collisions.
137
+ const opRenames = new Map();
138
+ const newNamesSeen = new Set();
139
+ for (const op of opNames) {
140
+ const newName = stripCamelPrefix(op, prefix);
141
+ if (newName === op)
142
+ continue;
143
+ if (newName.length === 0)
144
+ return null;
145
+ if (newNamesSeen.has(newName) || opNames.includes(newName))
146
+ return null;
147
+ opRenames.set(op, newName);
148
+ newNamesSeen.add(newName);
149
+ }
150
+ // Activity requirement: at least one operation must share the prefix.
151
+ // Without this, a service whose NAME happens to begin with a short
152
+ // lowercase word (`SaveGameService`, `IdpFooService`) would get
153
+ // renamed even when no operation evidence supports the prefix being
154
+ // a project tag.
155
+ if (opRenames.size === 0)
156
+ return null;
157
+ const newServiceName = stripPascalPrefix(serviceName, prefix);
158
+ return {
159
+ componentName,
160
+ oldServiceName: serviceName,
161
+ newServiceName: newServiceName ?? serviceName,
162
+ prefix,
163
+ opRenames,
164
+ };
165
+ }
166
+ function applyClusterMatch(spec, data) {
167
+ const components = spec.components;
168
+ const component = components?.[data.componentName];
169
+ const services = component?.services;
170
+ if (!services)
171
+ return spec;
172
+ const serviceBody = services[data.oldServiceName];
173
+ if (!serviceBody)
174
+ return spec;
175
+ // Rename operations preserving insertion order.
176
+ const oldOps = serviceBody.operations;
177
+ const renamedOps = {};
178
+ for (const [oldKey, value] of Object.entries(oldOps)) {
179
+ const newKey = data.opRenames.get(oldKey) ?? oldKey;
180
+ renamedOps[newKey] = value;
181
+ }
182
+ serviceBody.operations = renamedOps;
183
+ // Rename service key if changed. We need to preserve insertion order
184
+ // here too — rebuild the services object with the renamed key in the
185
+ // original slot.
186
+ if (data.newServiceName !== data.oldServiceName) {
187
+ const rebuilt = {};
188
+ for (const [k, v] of Object.entries(services)) {
189
+ if (k === data.oldServiceName)
190
+ rebuilt[data.newServiceName] = v;
191
+ else
192
+ rebuilt[k] = v;
193
+ }
194
+ component.services = rebuilt;
195
+ }
196
+ return spec;
197
+ }
198
+ export const CLUSTER_MODULE_FUNCTIONS_RULE = {
199
+ id: 'cluster-module-functions',
200
+ summary: 'Strip project-prefix from synthesised module-function services + their operations (e.g. SmcRoundsService.smcGetRound → RoundsService.getRound).',
201
+ ordering: 0,
202
+ enabledByDefault: true,
203
+ appliesWhen: (ctx) => ctx.source !== 'hand-authored',
204
+ detect: (spec) => {
205
+ const matches = [];
206
+ const components = spec.components;
207
+ if (!components || typeof components !== 'object')
208
+ return matches;
209
+ for (const [componentName, componentBody] of Object.entries(components)) {
210
+ const services = componentBody?.services;
211
+ if (!services || typeof services !== 'object')
212
+ continue;
213
+ for (const [serviceName, serviceBody] of Object.entries(services)) {
214
+ if (!serviceBody || typeof serviceBody !== 'object')
215
+ continue;
216
+ const data = detectClusterMatch(componentName, serviceName, serviceBody);
217
+ if (!data)
218
+ continue;
219
+ matches.push({
220
+ description: `${componentName}.${serviceName}: strip prefix "${data.prefix}" — ${data.opRenames.size} operation(s) renamed${data.newServiceName !== serviceName ? `, service → ${data.newServiceName}` : ''}`,
221
+ data,
222
+ });
223
+ }
224
+ }
225
+ return matches;
226
+ },
227
+ transform: (spec, match) => {
228
+ return applyClusterMatch(spec, match.data);
229
+ },
230
+ };
231
+ // Internal helpers exported for unit testing.
232
+ export const __internal = {
233
+ findCamelPrefix,
234
+ stripCamelPrefix,
235
+ stripPascalPrefix,
236
+ detectClusterMatch,
237
+ };
238
+ //# sourceMappingURL=cluster-module-functions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cluster-module-functions.js","sourceRoot":"","sources":["../../../src/normalise/rules/cluster-module-functions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Normalise rule: `crud-into-curved`.
3
+ *
4
+ * Phase 3 of `2026-05-13-NORMALISE-PHASE.md` §5 Rule 2. Lifts Service
5
+ * operations that match the canonical CRUD shape onto their target
6
+ * entity's Controller `cured:` block. This eliminates redundant service
7
+ * shells for plain CRUD wrappers — SpecVerse generates
8
+ * `cured.retrieve/create/update/delete` from a controller's cured
9
+ * declaration, so an explicit `Service.getClub` that just does
10
+ * "look up by id" is duplicating convention.
11
+ *
12
+ * Schema note: CURVED operations live on Controllers, NOT on Models.
13
+ * If the target entity has no Controller declared, the rule synthesises
14
+ * a `<Entity>Controller` (with `model: <Entity>`) in the same component
15
+ * as the Model. The cured op lands there.
16
+ *
17
+ * Before (post-Rule-1 / cluster-module-functions):
18
+ *
19
+ * components:
20
+ * App:
21
+ * models:
22
+ * Club:
23
+ * attributes: [...]
24
+ * services:
25
+ * ClubsService:
26
+ * operations:
27
+ * getClub:
28
+ * steps: [Look up Club by id, Return Club or null]
29
+ * requires: [id is provided]
30
+ * getClubPositions: # NOT lifted (filtered query)
31
+ * steps: [...]
32
+ *
33
+ * After:
34
+ *
35
+ * components:
36
+ * App:
37
+ * models:
38
+ * Club:
39
+ * attributes: [...]
40
+ * controllers:
41
+ * ClubController: # synthesised by this rule
42
+ * model: Club
43
+ * cured:
44
+ * retrieve:
45
+ * steps: [Look up Club by id, Return Club or null]
46
+ * requires: [id is provided]
47
+ * services:
48
+ * ClubsService:
49
+ * operations:
50
+ * getClubPositions: { ... } # remains
51
+ *
52
+ * If the service becomes empty after lifting, it is dropped entirely.
53
+ *
54
+ * Conservative matching:
55
+ * - Op name must be EXACTLY `<verb><EntityName>` — no trailing suffix
56
+ * (`getRound` ✓, `getRoundsForUser` ✗, `getRoundStatus` ✗)
57
+ * - Target Entity name must resolve UNIQUELY to ONE model across all
58
+ * components (the cured block goes to whichever component the model
59
+ * lives in — cross-component lifts are fine since the cured: lives
60
+ * ON the model, not at the service site)
61
+ * - Skip when the entity name is declared in multiple components (rare,
62
+ * but ambiguous — caller can rename the duplicate)
63
+ * - Target model's `cured.<curvedOp>` must NOT already be declared
64
+ * (don't overwrite spec-author intent)
65
+ * - Verb patterns recognised:
66
+ * get / retrieve / find → retrieve
67
+ * create / add → create (Phase 3: only `create` to stay safe)
68
+ * update → update
69
+ * delete / remove → delete (Phase 3: only `delete` to stay safe)
70
+ *
71
+ * NOT in Phase 3 scope:
72
+ * - retrieve_many (list / getAll / search — naming varies too widely)
73
+ * - Evolve transitions (would need lifecycle state-machine signal)
74
+ * - Cross-component lifts (target model in a different component)
75
+ * - Multi-arg create variants (createClubWithPositions)
76
+ */
77
+ import type { NormaliseRule, NormaliseSpec } from '../normalise-rules.js';
78
+ interface CrudMatchData {
79
+ /** Component hosting the source Service. */
80
+ sourceComponentName: string;
81
+ /** Component hosting the target Model (may differ from source). */
82
+ targetComponentName: string;
83
+ modelName: string;
84
+ serviceName: string;
85
+ operationName: string;
86
+ curvedOp: 'retrieve' | 'create' | 'update' | 'delete';
87
+ }
88
+ /**
89
+ * Parse an operation name into (verb, entityName) if it matches the
90
+ * exact `<verb><PascalCaseEntity>` shape. Returns null otherwise.
91
+ *
92
+ * The entity portion must be PascalCase, contain no further uppercase
93
+ * boundaries after the leading letter (i.e. single PascalCase word),
94
+ * AND must end at the end of the string (no trailing qualifier like
95
+ * `getRoundStatus` or `getClubsForUser`).
96
+ */
97
+ declare function parseCrudOpName(opName: string): {
98
+ verb: string;
99
+ curved: CrudMatchData['curvedOp'];
100
+ entity: string;
101
+ } | null;
102
+ /**
103
+ * Build a model-name → component-name index across all components. Used
104
+ * by the cross-component target lookup. When an entity name is declared
105
+ * in multiple components, it's recorded as ambiguous (the rule then
106
+ * skips matches against that name).
107
+ */
108
+ declare function buildModelIndex(spec: NormaliseSpec): Map<string, string | 'ambiguous'>;
109
+ declare function detectMatchesInComponent(spec: NormaliseSpec, modelIndex: Map<string, string | 'ambiguous'>, componentName: string, componentBody: Record<string, unknown>): CrudMatchData[];
110
+ export declare const CRUD_INTO_CURVED_RULE: NormaliseRule;
111
+ export declare const __internal: {
112
+ parseCrudOpName: typeof parseCrudOpName;
113
+ buildModelIndex: typeof buildModelIndex;
114
+ detectMatchesInComponent: typeof detectMatchesInComponent;
115
+ };
116
+ export {};
117
+ //# sourceMappingURL=crud-into-curved.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crud-into-curved.d.ts","sourceRoot":"","sources":["../../../src/normalise/rules/crud-into-curved.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2EG;AAEH,OAAO,KAAK,EACV,aAAa,EAGb,aAAa,EACd,MAAM,uBAAuB,CAAC;AAE/B,UAAU,aAAa;IACrB,4CAA4C;IAC5C,mBAAmB,EAAE,MAAM,CAAC;IAC5B,mEAAmE;IACnE,mBAAmB,EAAE,MAAM,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;CACvD;AAYD;;;;;;;;GAQG;AACH,iBAAS,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAoBnH;AAED;;;;;GAKG;AACH,iBAAS,eAAe,CAAC,IAAI,EAAE,aAAa,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAAC,CAgB/E;AAED,iBAAS,wBAAwB,CAC/B,IAAI,EAAE,aAAa,EACnB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAAC,EAC7C,aAAa,EAAE,MAAM,EACrB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACrC,aAAa,EAAE,CAuCjB;AAmFD,eAAO,MAAM,qBAAqB,EAAE,aAmCnC,CAAC;AAEF,eAAO,MAAM,UAAU;;;;CAItB,CAAC"}