@specverse/engines 6.42.3 → 6.53.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 (119) 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/services/templates/prisma/behavior-generator.js +24 -2
  34. package/dist/libs/instance-factories/services/templates/prisma/controller-generator.js +28 -20
  35. package/dist/normalise/index.d.ts +14 -0
  36. package/dist/normalise/index.d.ts.map +1 -0
  37. package/dist/normalise/index.js +14 -0
  38. package/dist/normalise/index.js.map +1 -0
  39. package/dist/normalise/load-overrides.d.ts +43 -0
  40. package/dist/normalise/load-overrides.d.ts.map +1 -0
  41. package/dist/normalise/load-overrides.js +121 -0
  42. package/dist/normalise/load-overrides.js.map +1 -0
  43. package/dist/normalise/normalise-rules.d.ts +181 -0
  44. package/dist/normalise/normalise-rules.d.ts.map +1 -0
  45. package/dist/normalise/normalise-rules.js +79 -0
  46. package/dist/normalise/normalise-rules.js.map +1 -0
  47. package/dist/normalise/rules/cluster-module-functions.d.ts +31 -0
  48. package/dist/normalise/rules/cluster-module-functions.d.ts.map +1 -0
  49. package/dist/normalise/rules/cluster-module-functions.js +238 -0
  50. package/dist/normalise/rules/cluster-module-functions.js.map +1 -0
  51. package/dist/normalise/rules/crud-into-curved.d.ts +117 -0
  52. package/dist/normalise/rules/crud-into-curved.d.ts.map +1 -0
  53. package/dist/normalise/rules/crud-into-curved.js +303 -0
  54. package/dist/normalise/rules/crud-into-curved.js.map +1 -0
  55. package/dist/normalise/rules/drop-trivial-passthrough.d.ts +92 -0
  56. package/dist/normalise/rules/drop-trivial-passthrough.d.ts.map +1 -0
  57. package/dist/normalise/rules/drop-trivial-passthrough.js +217 -0
  58. package/dist/normalise/rules/drop-trivial-passthrough.js.map +1 -0
  59. package/dist/normalise/runner.d.ts +58 -0
  60. package/dist/normalise/runner.d.ts.map +1 -0
  61. package/dist/normalise/runner.js +114 -0
  62. package/dist/normalise/runner.js.map +1 -0
  63. package/dist/parser/import-resolver/resolver.js +1 -1
  64. package/dist/parser/import-resolver/resolver.js.map +1 -1
  65. package/dist/realize/engines/typescript-engine.js +1 -1
  66. package/dist/realize/engines/typescript-engine.js.map +1 -1
  67. package/dist/realize/index.d.ts.map +1 -1
  68. package/dist/realize/index.js +142 -90
  69. package/dist/realize/index.js.map +1 -1
  70. package/dist/realize/library/library.js +1 -1
  71. package/dist/realize/library/library.js.map +1 -1
  72. package/dist/realize/library/resolver.d.ts.map +1 -1
  73. package/dist/realize/library/resolver.js +14 -1
  74. package/dist/realize/library/resolver.js.map +1 -1
  75. package/dist/realize/owner-emit-shared.d.ts +114 -0
  76. package/dist/realize/owner-emit-shared.d.ts.map +1 -0
  77. package/dist/realize/owner-emit-shared.js +227 -0
  78. package/dist/realize/owner-emit-shared.js.map +1 -0
  79. package/dist/realize/per-owner-emit.d.ts +1 -58
  80. package/dist/realize/per-owner-emit.d.ts.map +1 -1
  81. package/dist/realize/per-owner-emit.js +45 -209
  82. package/dist/realize/per-owner-emit.js.map +1 -1
  83. package/dist/realize/per-owner-runner.d.ts +1 -2
  84. package/dist/realize/per-owner-runner.d.ts.map +1 -1
  85. package/dist/realize/per-owner-runner.js +1 -1
  86. package/dist/realize/per-owner-runner.js.map +1 -1
  87. package/dist/realize/post-emit-verify/feedback-runner.d.ts +84 -0
  88. package/dist/realize/post-emit-verify/feedback-runner.d.ts.map +1 -0
  89. package/dist/realize/post-emit-verify/feedback-runner.js +177 -0
  90. package/dist/realize/post-emit-verify/feedback-runner.js.map +1 -0
  91. package/dist/realize/post-emit-verify/index.d.ts +17 -0
  92. package/dist/realize/post-emit-verify/index.d.ts.map +1 -0
  93. package/dist/realize/post-emit-verify/index.js +16 -0
  94. package/dist/realize/post-emit-verify/index.js.map +1 -0
  95. package/dist/realize/post-emit-verify/reemit.d.ts +61 -0
  96. package/dist/realize/post-emit-verify/reemit.d.ts.map +1 -0
  97. package/dist/realize/post-emit-verify/reemit.js +122 -0
  98. package/dist/realize/post-emit-verify/reemit.js.map +1 -0
  99. package/dist/realize/post-emit-verify/types.d.ts +138 -0
  100. package/dist/realize/post-emit-verify/types.d.ts.map +1 -0
  101. package/dist/realize/post-emit-verify/types.js +28 -0
  102. package/dist/realize/post-emit-verify/types.js.map +1 -0
  103. package/dist/realize/post-emit-verify/verifier-manifest.d.ts +29 -0
  104. package/dist/realize/post-emit-verify/verifier-manifest.d.ts.map +1 -0
  105. package/dist/realize/post-emit-verify/verifier-manifest.js +55 -0
  106. package/dist/realize/post-emit-verify/verifier-manifest.js.map +1 -0
  107. package/dist/realize/post-emit-verify/verifiers/tsc.d.ts +24 -0
  108. package/dist/realize/post-emit-verify/verifiers/tsc.d.ts.map +1 -0
  109. package/dist/realize/post-emit-verify/verifiers/tsc.js +148 -0
  110. package/dist/realize/post-emit-verify/verifiers/tsc.js.map +1 -0
  111. package/dist/realize/realize-rules.d.ts +113 -0
  112. package/dist/realize/realize-rules.d.ts.map +1 -0
  113. package/dist/realize/realize-rules.js +271 -0
  114. package/dist/realize/realize-rules.js.map +1 -0
  115. package/libs/instance-factories/cli/templates/commander/cli-entry-generator.ts +11 -12
  116. package/libs/instance-factories/cli/templates/commander/command-generator.ts +2 -2
  117. package/libs/instance-factories/services/templates/prisma/behavior-generator.ts +62 -2
  118. package/libs/instance-factories/services/templates/prisma/controller-generator.ts +42 -20
  119. package/package.json +9 -1
@@ -0,0 +1,55 @@
1
+ import { TSC_VERIFIER } from './verifiers/tsc.js';
2
+ export const VERIFIERS = [
3
+ TSC_VERIFIER,
4
+ // Future:
5
+ // PY_MYPY_VERIFIER — `mypy --strict src/`
6
+ // GO_VET_VERIFIER — `go vet ./...`
7
+ // JAVA_MVN_VERIFIER — `mvn -q compile`
8
+ // RUST_CARGO_CHECK_VERIFIER — `cargo check`
9
+ ];
10
+ /**
11
+ * Run all applicable verifiers against the realized output. Verifiers
12
+ * run in series (most are heavyweight enough that parallel doesn't
13
+ * help much, and series gives predictable log output). Each verifier's
14
+ * tool-level failures are isolated — one failure doesn't stop the
15
+ * others.
16
+ *
17
+ * Verifier filtering order:
18
+ * 1. `enabledByDefault === false` AND id NOT in `enabledOverrides` → SKIP
19
+ * 2. `applies(ctx)` returns false → SKIP
20
+ * 3. Otherwise → run
21
+ *
22
+ * Missing `enabledByDefault` field is treated as `true` (backwards-
23
+ * compatible default). The `enabledOverrides` parameter lets callers
24
+ * opt-in to experimental verifiers at run-time without changing the
25
+ * manifest.
26
+ */
27
+ export async function runAllVerifiers(ctx, enabledOverrides) {
28
+ const out = [];
29
+ for (const v of VERIFIERS) {
30
+ const enabledByDefault = v.enabledByDefault !== false;
31
+ const explicitlyEnabled = enabledOverrides?.has(v.id) ?? false;
32
+ if (!enabledByDefault && !explicitlyEnabled)
33
+ continue;
34
+ if (!v.applies(ctx))
35
+ continue;
36
+ try {
37
+ const result = await v.verify(ctx);
38
+ out.push(result);
39
+ }
40
+ catch (err) {
41
+ out.push({
42
+ verifierId: v.id,
43
+ durationMs: 0,
44
+ ok: false,
45
+ errors: [],
46
+ notes: [`Verifier "${v.id}" threw: ${err?.message ?? String(err)}`],
47
+ });
48
+ }
49
+ }
50
+ return out;
51
+ }
52
+ export function findVerifier(id) {
53
+ return VERIFIERS.find((v) => v.id === id);
54
+ }
55
+ //# sourceMappingURL=verifier-manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verifier-manifest.js","sourceRoot":"","sources":["../../../src/realize/post-emit-verify/verifier-manifest.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,CAAC,MAAM,SAAS,GAAuB;IAC3C,YAAY;IACZ,UAAU;IACV,6CAA6C;IAC7C,uCAAuC;IACvC,yCAAyC;IACzC,8CAA8C;CAC/C,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,GAAkB,EAClB,gBAA8B;IAE9B,MAAM,GAAG,GAAmB,EAAE,CAAC;IAC/B,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1B,MAAM,gBAAgB,GAAG,CAAC,CAAC,gBAAgB,KAAK,KAAK,CAAC;QACtD,MAAM,iBAAiB,GAAG,gBAAgB,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC;QAC/D,IAAI,CAAC,gBAAgB,IAAI,CAAC,iBAAiB;YAAE,SAAS;QACtD,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC;YAAE,SAAS;QAC9B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACnC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACnB,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,GAAG,CAAC,IAAI,CAAC;gBACP,UAAU,EAAE,CAAC,CAAC,EAAE;gBAChB,UAAU,EAAE,CAAC;gBACb,EAAE,EAAE,KAAK;gBACT,MAAM,EAAE,EAAE;gBACV,KAAK,EAAE,CAAC,aAAa,CAAC,CAAC,EAAE,YAAY,GAAG,EAAE,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;aACpE,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,EAAU;IACrC,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * TypeScript verifier — runs `tsc --noEmit` on the realized backend
3
+ * tree, parses error output, and returns structured `VerifyError`s.
4
+ *
5
+ * The first concrete implementation of the `PostEmitVerifier` interface.
6
+ * Used for any TypeScript realize target (the default stack today).
7
+ *
8
+ * Design notes:
9
+ * - We rely on the realized project's own `node_modules/.bin/tsc`
10
+ * and `tsconfig.json` (installed by the realize-emit step).
11
+ * The framework doesn't bundle a tsc — the user's project owns
12
+ * its own TypeScript version.
13
+ * - `applies` checks for the presence of `tsconfig.json` in
14
+ * `<outputDir>/<subpath>/`. Future stacks (e.g. a Java target)
15
+ * would have their own verifier with their own `applies`.
16
+ * - Output parsing matches the standard tsc message format:
17
+ * `path/to/file.ts(line,col): error TSXXXX: Message text.`
18
+ * - tsc tool-failure (binary missing, install needed) → ok=false
19
+ * + notes explain. The feedback runner skips the verifier; the
20
+ * realize pipeline does NOT crash.
21
+ */
22
+ import type { PostEmitVerifier } from '../types.js';
23
+ export declare const TSC_VERIFIER: PostEmitVerifier;
24
+ //# sourceMappingURL=tsc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tsc.d.ts","sourceRoot":"","sources":["../../../../src/realize/post-emit-verify/verifiers/tsc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAKH,OAAO,KAAK,EACV,gBAAgB,EAIjB,MAAM,aAAa,CAAC;AAcrB,eAAO,MAAM,YAAY,EAAE,gBA8H1B,CAAC"}
@@ -0,0 +1,148 @@
1
+ /**
2
+ * TypeScript verifier — runs `tsc --noEmit` on the realized backend
3
+ * tree, parses error output, and returns structured `VerifyError`s.
4
+ *
5
+ * The first concrete implementation of the `PostEmitVerifier` interface.
6
+ * Used for any TypeScript realize target (the default stack today).
7
+ *
8
+ * Design notes:
9
+ * - We rely on the realized project's own `node_modules/.bin/tsc`
10
+ * and `tsconfig.json` (installed by the realize-emit step).
11
+ * The framework doesn't bundle a tsc — the user's project owns
12
+ * its own TypeScript version.
13
+ * - `applies` checks for the presence of `tsconfig.json` in
14
+ * `<outputDir>/<subpath>/`. Future stacks (e.g. a Java target)
15
+ * would have their own verifier with their own `applies`.
16
+ * - Output parsing matches the standard tsc message format:
17
+ * `path/to/file.ts(line,col): error TSXXXX: Message text.`
18
+ * - tsc tool-failure (binary missing, install needed) → ok=false
19
+ * + notes explain. The feedback runner skips the verifier; the
20
+ * realize pipeline does NOT crash.
21
+ */
22
+ import { existsSync } from 'fs';
23
+ import { join, relative } from 'path';
24
+ import { spawnSync } from 'child_process';
25
+ const TSC_LINE_RE = /^(.+?)\((\d+),(\d+)\):\s+(error|warning)\s+(TS\d+):\s+(.+)$/;
26
+ /**
27
+ * Resolve the directory tsc should run in. Defaults to
28
+ * `<outputDir>/backend` (the canonical SpecVerse TypeScript target).
29
+ * Caller can override via `ctx.subpath`.
30
+ */
31
+ function resolveSubpath(ctx) {
32
+ if (ctx.subpath)
33
+ return join(ctx.outputDir, ctx.subpath);
34
+ return join(ctx.outputDir, 'backend');
35
+ }
36
+ export const TSC_VERIFIER = {
37
+ id: 'tsc',
38
+ name: 'TypeScript Compiler',
39
+ enabledByDefault: true,
40
+ applies: (ctx) => {
41
+ if (ctx.targetLanguage && ctx.targetLanguage !== 'typescript')
42
+ return false;
43
+ const dir = resolveSubpath(ctx);
44
+ return existsSync(join(dir, 'tsconfig.json'));
45
+ },
46
+ verify: async (ctx) => {
47
+ const dir = resolveSubpath(ctx);
48
+ const start = Date.now();
49
+ const notes = [];
50
+ // Auto-install deps if needed. The realize pipeline emits a
51
+ // scaffolded project tree (package.json + tsconfig.json) but does
52
+ // not run `npm install`, so node_modules/.bin/tsc is missing on
53
+ // first verify. Without auto-install the framework would silently
54
+ // no-op default-on (after the engines 6.52 flip). Run `npm install`
55
+ // once when:
56
+ // - `package.json` exists
57
+ // - `node_modules/.bin/tsc` is missing
58
+ // - `ctx.autoInstall !== false` (defaults to true)
59
+ // Bounded by a 5-min timeout — pathological dep trees fail loudly
60
+ // rather than hang the realize pipeline.
61
+ const localTsc = join(dir, 'node_modules', '.bin', 'tsc');
62
+ const pkgJson = join(dir, 'package.json');
63
+ const autoInstall = ctx.autoInstall !== false;
64
+ if (!existsSync(localTsc) && existsSync(pkgJson) && autoInstall) {
65
+ const installResult = spawnSync('npm', ['install', '--no-audit', '--no-fund', '--silent'], {
66
+ cwd: dir,
67
+ encoding: 'utf8',
68
+ maxBuffer: 50 * 1024 * 1024,
69
+ timeout: 5 * 60_000,
70
+ });
71
+ if (installResult.error || installResult.status !== 0) {
72
+ return {
73
+ verifierId: 'tsc',
74
+ durationMs: Date.now() - start,
75
+ ok: false,
76
+ errors: [],
77
+ notes: [
78
+ `tsc verifier auto-install failed in ${dir}: ${installResult.error?.message ?? `npm exit ${installResult.status}`}. Hint: run \`npm install\` manually before realize, or set ctx.autoInstall=false.`,
79
+ ],
80
+ };
81
+ }
82
+ notes.push(`auto-installed deps in ${dir} for tsc verifier`);
83
+ }
84
+ // Locate the project-local tsc. Fall back to PATH `npx tsc` only if
85
+ // node_modules/.bin/tsc isn't installed yet.
86
+ let cmd;
87
+ let args;
88
+ if (existsSync(localTsc)) {
89
+ cmd = localTsc;
90
+ args = ['--noEmit'];
91
+ }
92
+ else {
93
+ cmd = 'npx';
94
+ args = ['--no-install', 'tsc', '--noEmit'];
95
+ }
96
+ const result = spawnSync(cmd, args, {
97
+ cwd: dir,
98
+ encoding: 'utf8',
99
+ maxBuffer: 50 * 1024 * 1024, // 50MB — error output on a heavy
100
+ // realize can be megabytes.
101
+ });
102
+ const durationMs = Date.now() - start;
103
+ if (result.error) {
104
+ return {
105
+ verifierId: 'tsc',
106
+ durationMs,
107
+ ok: false,
108
+ errors: [],
109
+ notes: [
110
+ ...notes,
111
+ `tsc could not be invoked: ${result.error.message}. ` +
112
+ `Hint: run \`npm install\` in ${dir} before realizing, or ensure ` +
113
+ `tsc is on PATH.`,
114
+ ],
115
+ };
116
+ }
117
+ const combined = (result.stdout ?? '') + (result.stderr ?? '');
118
+ const errors = [];
119
+ // tsc reports file paths relative to its CWD (the subpath). Convert
120
+ // those to paths relative to outputDir so the feedback runner's
121
+ // `join(outputDir, file)` resolves correctly.
122
+ const subpathRel = relative(ctx.outputDir, dir);
123
+ const toOutputRel = (file) => subpathRel === '' ? file : `${subpathRel}/${file}`;
124
+ for (const line of combined.split('\n')) {
125
+ const m = TSC_LINE_RE.exec(line.trimEnd());
126
+ if (!m)
127
+ continue;
128
+ errors.push({
129
+ file: toOutputRel(m[1]),
130
+ line: parseInt(m[2], 10),
131
+ col: parseInt(m[3], 10),
132
+ severity: m[4],
133
+ code: m[5],
134
+ message: m[6],
135
+ });
136
+ }
137
+ return {
138
+ verifierId: 'tsc',
139
+ durationMs,
140
+ // ok: true even when errors > 0 — that's the EXPECTED path.
141
+ // ok: false reserved for tool-level failures (binary missing, etc.).
142
+ ok: true,
143
+ errors,
144
+ notes,
145
+ };
146
+ },
147
+ };
148
+ //# sourceMappingURL=tsc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tsc.js","sourceRoot":"","sources":["../../../../src/realize/post-emit-verify/verifiers/tsc.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Realize LLM-emit rule manifest.
3
+ *
4
+ * Single source of truth for every post-emit constraint enforced on
5
+ * per-owner LLM output. Replaces the prior model where each rule was
6
+ * authored in THREE places (a regex+correction in `per-owner-emit.ts`,
7
+ * a prose-statement in `realize-owner.prompt.yaml`, and an ad-hoc unit
8
+ * test fixture). That model produced drift — prompt and validator
9
+ * statements got out of sync, fixture coverage was uneven, and adding
10
+ * a rule meant editing three locations consistently.
11
+ *
12
+ * The manifest below declares each rule once. Three consumers read it:
13
+ *
14
+ * 1. Validator pipeline (per-owner-emit.ts) — calls `detect(...)` to
15
+ * find violations, calls `correction(...)` to build the surgical-
16
+ * retry prefix, sorts rules by `ordering` to control which fires
17
+ * first when multiple violations co-occur.
18
+ *
19
+ * 2. Prompt builder — reads `promptStatement` to render the
20
+ * CONSTRAINTS section of `realize-owner.prompt.yaml` (Phase 2 of
21
+ * the 2026-05-12-VALIDATOR-MANIFEST-AND-TEST-CORPUS proposal).
22
+ *
23
+ * 3. Test corpus (`engines/src/realize/__tests__/llm-output-fixtures/`)
24
+ * — each fixture is annotated with the rule IDs it should trigger;
25
+ * the pipeline harness asserts the manifest's ordering produces
26
+ * exactly those fires.
27
+ *
28
+ * Adding a new rule is one place: append a new entry to RULES.
29
+ *
30
+ * Why in engines/src/realize/ rather than entities/src/core/realize/:
31
+ * For the moment, rules are realize-internal — they're regex scans
32
+ * over LLM-emitted TypeScript, not the kind of bundle-able content
33
+ * that R12 entity modules manage. If realize rules ever need to be
34
+ * extended by user specs (e.g. project-specific forbidden imports),
35
+ * migration to the entity-module pattern would be the next step.
36
+ */
37
+ /** Context the rule pipeline carries through the per-owner emit. */
38
+ export interface RuleContext {
39
+ /** Owner identifier — e.g. 'AuthController', 'GameStateService'. */
40
+ ownerName: string;
41
+ /** Target runtime declaration — e.g. 'Node.js (backend)'. Some rules
42
+ * apply only on specific runtimes (see `appliesWhen`). */
43
+ targetRuntime?: string;
44
+ }
45
+ /** One violation found by a rule. Free-form string for the symbol /
46
+ * path / shape that triggered the rule (e.g. 'localStorage',
47
+ * '../services/AuthService'). Multiple violations can be returned
48
+ * from one detect() call. */
49
+ export type RuleViolation = string;
50
+ /**
51
+ * A single realize-emit rule. Each field has one purpose:
52
+ *
53
+ * - `id`: stable identifier for tests and logs. Kebab-case.
54
+ * - `summary`: one-line human description shown in stderr when
55
+ * `SPECVERSE_VERBOSE=1`.
56
+ * - `promptStatement`: human-language form of the rule for the
57
+ * prompt's CONSTRAINTS section. Phrased as an instruction to the
58
+ * LLM, NOT as a developer-facing comment.
59
+ * - `detect`: scan the LLM-emitted file for violations. Returns the
60
+ * list of distinct violations (cap to 8 for readability). Empty
61
+ * array = no violations.
62
+ * - `correction`: build the surgical-retry user-prefix string when
63
+ * violations are found. The LLM will see this prepended to the
64
+ * prompt on its second attempt.
65
+ * - `ordering`: pipeline position. Lower numbers fire first. Used to
66
+ * resolve which violation gets reported when multiple co-occur.
67
+ * Rough conventions:
68
+ * 0-9: structural pre-checks (parse, fence extraction)
69
+ * 10-19: runtime-API and environment-level constraints
70
+ * 20-29: import-shape constraints (extensions, scoping)
71
+ * 30-39: symbol-resolution constraints (sibling .ai, etc.)
72
+ * 40+: typing / shape constraints (Phase 3+)
73
+ * - `appliesWhen` (optional): predicate for runtime-conditional
74
+ * rules. e.g. 'no-browser-apis' applies only on Node target;
75
+ * 'no-node-builtins' applies only on Browser target.
76
+ */
77
+ export interface RealizeRule {
78
+ id: string;
79
+ summary: string;
80
+ promptStatement: string;
81
+ detect: (fileContents: string, ctx: RuleContext) => RuleViolation[];
82
+ correction: (violations: RuleViolation[], ctx: RuleContext) => string;
83
+ ordering: number;
84
+ appliesWhen?: (ctx: RuleContext) => boolean;
85
+ }
86
+ /**
87
+ * Map a target-runtime label to its forbidden-API symbol set. Exported
88
+ * so the prompt builder can render the TARGET RUNTIME section using
89
+ * the same source-of-truth the validator enforces.
90
+ *
91
+ * Targets supported:
92
+ * - "Node.js (backend)" (default) — browser globals are forbidden
93
+ * - "Browser (DOM)" — Node builtins are forbidden
94
+ * - Unknown / unset — treat as Node.js backend
95
+ */
96
+ export declare function forbiddenApisForRuntime(targetRuntime?: string): string[];
97
+ /**
98
+ * The full rule set, in pipeline order. Sorted by `ordering` field.
99
+ * The exported list is read by:
100
+ * - per-owner-emit.ts (validator loop)
101
+ * - prompt-builder (CONSTRAINTS section of realize-owner.prompt.yaml)
102
+ * - test corpus (fixture-to-rule expected mapping)
103
+ */
104
+ export declare const RULES: ReadonlyArray<RealizeRule>;
105
+ /** Helper: get a single rule by id. Throws if the id doesn't exist
106
+ * (catches typos in test fixtures + downstream consumers). */
107
+ export declare function getRule(id: string): RealizeRule;
108
+ /** Helper: filter RULES to the ones that apply for a given context. */
109
+ export declare function rulesApplicableTo(ctx: RuleContext): RealizeRule[];
110
+ /** Helper: render the CONSTRAINTS section of the prompt by joining
111
+ * each rule's promptStatement. */
112
+ export declare function renderConstraintsForPrompt(ctx: RuleContext): string;
113
+ //# sourceMappingURL=realize-rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realize-rules.d.ts","sourceRoot":"","sources":["../../src/realize/realize-rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAIH,oEAAoE;AACpE,MAAM,WAAW,WAAW;IAC1B,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB;+DAC2D;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;8BAG8B;AAC9B,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC;AAEnC;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,KAAK,aAAa,EAAE,CAAC;IACpE,UAAU,EAAE,CAAC,UAAU,EAAE,aAAa,EAAE,EAAE,GAAG,EAAE,WAAW,KAAK,MAAM,CAAC;IACtE,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,WAAW,KAAK,OAAO,CAAC;CAC7C;AAqBD;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAIxE;AA2KD;;;;;;GAMG;AACH,eAAO,MAAM,KAAK,EAAE,aAAa,CAAC,WAAW,CAKJ,CAAC;AAE1C;+DAC+D;AAC/D,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,CAO/C;AAED,uEAAuE;AACvE,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,WAAW,GAAG,WAAW,EAAE,CAEjE;AAED;mCACmC;AACnC,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,CAMnE"}
@@ -0,0 +1,271 @@
1
+ /**
2
+ * Realize LLM-emit rule manifest.
3
+ *
4
+ * Single source of truth for every post-emit constraint enforced on
5
+ * per-owner LLM output. Replaces the prior model where each rule was
6
+ * authored in THREE places (a regex+correction in `per-owner-emit.ts`,
7
+ * a prose-statement in `realize-owner.prompt.yaml`, and an ad-hoc unit
8
+ * test fixture). That model produced drift — prompt and validator
9
+ * statements got out of sync, fixture coverage was uneven, and adding
10
+ * a rule meant editing three locations consistently.
11
+ *
12
+ * The manifest below declares each rule once. Three consumers read it:
13
+ *
14
+ * 1. Validator pipeline (per-owner-emit.ts) — calls `detect(...)` to
15
+ * find violations, calls `correction(...)` to build the surgical-
16
+ * retry prefix, sorts rules by `ordering` to control which fires
17
+ * first when multiple violations co-occur.
18
+ *
19
+ * 2. Prompt builder — reads `promptStatement` to render the
20
+ * CONSTRAINTS section of `realize-owner.prompt.yaml` (Phase 2 of
21
+ * the 2026-05-12-VALIDATOR-MANIFEST-AND-TEST-CORPUS proposal).
22
+ *
23
+ * 3. Test corpus (`engines/src/realize/__tests__/llm-output-fixtures/`)
24
+ * — each fixture is annotated with the rule IDs it should trigger;
25
+ * the pipeline harness asserts the manifest's ordering produces
26
+ * exactly those fires.
27
+ *
28
+ * Adding a new rule is one place: append a new entry to RULES.
29
+ *
30
+ * Why in engines/src/realize/ rather than entities/src/core/realize/:
31
+ * For the moment, rules are realize-internal — they're regex scans
32
+ * over LLM-emitted TypeScript, not the kind of bundle-able content
33
+ * that R12 entity modules manage. If realize rules ever need to be
34
+ * extended by user specs (e.g. project-specific forbidden imports),
35
+ * migration to the entity-module pattern would be the next step.
36
+ */
37
+ // ─── Forbidden runtime APIs ─────────────────────────────────────────
38
+ /** Forbidden-API symbol set for Node.js target. */
39
+ const NODE_FORBIDDEN = [
40
+ 'localStorage',
41
+ 'sessionStorage',
42
+ 'window.',
43
+ 'document.',
44
+ 'navigator.',
45
+ 'location.',
46
+ 'history.',
47
+ 'alert(',
48
+ 'prompt(',
49
+ 'confirm(',
50
+ ];
51
+ /** Forbidden-API symbol set for Browser target. */
52
+ const BROWSER_FORBIDDEN = ['process.env', 'require(', 'Buffer', '__dirname', '__filename', 'node:'];
53
+ /**
54
+ * Map a target-runtime label to its forbidden-API symbol set. Exported
55
+ * so the prompt builder can render the TARGET RUNTIME section using
56
+ * the same source-of-truth the validator enforces.
57
+ *
58
+ * Targets supported:
59
+ * - "Node.js (backend)" (default) — browser globals are forbidden
60
+ * - "Browser (DOM)" — Node builtins are forbidden
61
+ * - Unknown / unset — treat as Node.js backend
62
+ */
63
+ export function forbiddenApisForRuntime(targetRuntime) {
64
+ const t = (targetRuntime ?? 'Node.js (backend)').toLowerCase();
65
+ if (t.includes('browser') && !t.includes('backend'))
66
+ return BROWSER_FORBIDDEN;
67
+ return NODE_FORBIDDEN;
68
+ }
69
+ // ─── Rules ───────────────────────────────────────────────────────────
70
+ const RULE_NO_FORBIDDEN_RUNTIME_APIS = {
71
+ id: 'no-forbidden-runtime-apis',
72
+ summary: 'No APIs from foreign runtimes',
73
+ promptStatement: 'Do NOT use APIs that don\'t exist in the target runtime. The ' +
74
+ 'forbidden symbols for this run are listed in the TARGET RUNTIME ' +
75
+ 'section above. If a spec step references a forbidden API ' +
76
+ '(e.g. "save to localStorage" with a Node.js target), emit the ' +
77
+ 'γ-stub form for THAT action explaining the spec/runtime ' +
78
+ 'mismatch — do NOT implement the forbidden API.',
79
+ detect(fileContents, ctx) {
80
+ const forbidden = forbiddenApisForRuntime(ctx.targetRuntime);
81
+ const violations = new Set();
82
+ for (const symbol of forbidden) {
83
+ if (fileContents.includes(symbol))
84
+ violations.add(symbol);
85
+ }
86
+ return Array.from(violations).slice(0, 8);
87
+ },
88
+ correction(violations, ctx) {
89
+ const runtime = ctx.targetRuntime ?? 'Node.js (backend)';
90
+ return [
91
+ `Your previous emission used runtime APIs that are NOT available in ${runtime}:`,
92
+ '',
93
+ ...violations.map((v) => ` - ${v}`),
94
+ '',
95
+ `Re-emit the entire file WITHOUT those symbols. If a specific action's`,
96
+ `step text references runtime-incompatible behaviour, use the γ-stub`,
97
+ `form for THAT action explaining the spec/runtime mismatch. Other`,
98
+ `actions in the file should still be implemented fully. Preserve`,
99
+ `all [PRE-BAKED] snippets verbatim.`,
100
+ '',
101
+ ].join('\n');
102
+ },
103
+ ordering: 10,
104
+ };
105
+ const RULE_JS_EXTENSIONS_ON_RELATIVE_IMPORTS = {
106
+ id: 'js-ext-on-relative-imports',
107
+ summary: 'Relative imports require explicit `.js` (or .ts/.json/...) extension',
108
+ promptStatement: 'Relative imports MUST end with `.js` (or `.ts`/`.json`/`.mjs`/`.mts`). ' +
109
+ 'The project uses `moduleResolution: NodeNext` which requires explicit ' +
110
+ 'extensions on relative paths. WRONG: `from \'../services/AuthService\'` ' +
111
+ '→ CORRECT: `from \'../services/AuthService.js\'`. Bare-package imports ' +
112
+ '(`@prisma/client`, `jsonwebtoken`) do NOT need an extension.',
113
+ detect(fileContents) {
114
+ const violations = new Set();
115
+ const re = /from\s+['"](\.\.?\/[^'"]*?)['"]/g;
116
+ let m;
117
+ while ((m = re.exec(fileContents)) !== null) {
118
+ const specifier = m[1];
119
+ if (!/\.(m?[jt]sx?|json)$/.test(specifier)) {
120
+ violations.add(specifier);
121
+ }
122
+ }
123
+ return Array.from(violations).slice(0, 8);
124
+ },
125
+ correction(violations) {
126
+ return [
127
+ `Your previous emission used relative imports WITHOUT the required`,
128
+ `\`.js\` extension. NodeNext module resolution requires explicit`,
129
+ `extensions on relative paths:`,
130
+ '',
131
+ ...violations.map((v) => ` - '${v}' → '${v}.js'`),
132
+ '',
133
+ `Re-emit the entire file with \`.js\` appended to every relative-path`,
134
+ `import. Bare-package imports do NOT need an extension. Preserve all`,
135
+ `[PRE-BAKED] snippets verbatim.`,
136
+ '',
137
+ ].join('\n');
138
+ },
139
+ ordering: 20,
140
+ };
141
+ const RULE_NO_SIBLING_CLASS_PATH_IMPORTS = {
142
+ id: 'no-sibling-class-path-imports',
143
+ summary: 'Sibling-class imports (`./<Capital>.js`) must use the parent-directory ' +
144
+ 'path (`../<dir>/<Capital>.js`) because realize emits behaviors/ and ' +
145
+ 'services/ as separate directories',
146
+ promptStatement: 'Sibling-class imports MUST use `../<dir>/<Name>.js`, NOT `./<Name>.js`. ' +
147
+ 'The realize pipeline emits this file into `backend/src/behaviors/`; ' +
148
+ 'service classes live at `backend/src/services/<Name>.ts` and ' +
149
+ 'controller classes at `backend/src/controllers/<Name>.ts`. WRONG: ' +
150
+ '`from \'./GameStateService.js\'` → CORRECT: ' +
151
+ '`from \'../services/GameStateService.js\'`. (Sibling .ai imports are ' +
152
+ 'separately forbidden — see the next rule. Subdirectory imports like ' +
153
+ '`./types/Foo.js` are fine; only TOP-LEVEL `./<Capital>.js` is wrong.)',
154
+ detect(fileContents) {
155
+ const violations = new Set();
156
+ // Match `from './<Capital><rest>.js'` with no subdirectory slash inside
157
+ // and where the bare name (rest) does NOT end in `.ai` (the sibling-ai
158
+ // rule handles those).
159
+ const re = /from\s+['"](\.\/[A-Z][^'"\/]*)\.js['"]/g;
160
+ let m;
161
+ while ((m = re.exec(fileContents)) !== null) {
162
+ const specifier = m[1];
163
+ // Skip `.ai` siblings — caught by `no-sibling-ai-imports` instead.
164
+ if (specifier.endsWith('.ai'))
165
+ continue;
166
+ violations.add(specifier);
167
+ }
168
+ return Array.from(violations).slice(0, 8);
169
+ },
170
+ correction(violations) {
171
+ return [
172
+ `Your previous emission imported sibling-class modules with the wrong path:`,
173
+ '',
174
+ ...violations.map((v) => ` - '${v}.js' → '../services/${v.slice(2)}.js' (or '../controllers/${v.slice(2)}.js' if it's a controller class)`),
175
+ '',
176
+ `The realize pipeline organises generated code by kind:`,
177
+ ` backend/src/behaviors/<Name>.ai.ts ← this file`,
178
+ ` backend/src/services/<Name>.ts ← service classes`,
179
+ ` backend/src/controllers/<Name>.ts ← controller classes`,
180
+ ``,
181
+ `So a sibling-class import from THIS file is one directory up + into`,
182
+ `the service-or-controller dir. NOT \`./<Name>.js\`.`,
183
+ '',
184
+ `Re-emit the entire file with the imports corrected. Preserve all`,
185
+ `[PRE-BAKED] snippets verbatim.`,
186
+ '',
187
+ ].join('\n');
188
+ },
189
+ ordering: 25,
190
+ };
191
+ const RULE_NO_SIBLING_AI_IMPORTS = {
192
+ id: 'no-sibling-ai-imports',
193
+ summary: 'Do not import sibling `.ai.ts` files',
194
+ promptStatement: 'Do NOT import sibling `.ai.ts` files. Each `<Owner>.ai.ts` is ' +
195
+ 'self-contained — those modules export per-action async functions, ' +
196
+ 'not the entity types or service instances you might want to use. ' +
197
+ 'For cross-service calls, import from the service / controller class ' +
198
+ 'file instead (e.g. `import { AuthService } from \'../services/AuthService.js\'`). ' +
199
+ 'If a step requires logic from a sibling .ai, emit a γ-stub for THAT ' +
200
+ 'specific action explaining the cross-owner gap.',
201
+ detect(fileContents) {
202
+ const violations = new Set();
203
+ const re = /from\s+['"](\.\.?\/[^'"]+\.ai)(?:\.js)?['"]/g;
204
+ let m;
205
+ while ((m = re.exec(fileContents)) !== null) {
206
+ violations.add(m[1]);
207
+ }
208
+ return Array.from(violations).slice(0, 8);
209
+ },
210
+ correction(violations) {
211
+ return [
212
+ `Your previous emission imported sibling .ai.ts modules, which is forbidden:`,
213
+ '',
214
+ ...violations.map((v) => ` - ${v}`),
215
+ '',
216
+ `Each <Owner>.ai.ts is self-contained — those modules export per-action`,
217
+ `functions, NOT the entity types or service instances you can use here.`,
218
+ '',
219
+ `Re-emit the entire file WITHOUT importing any sibling .ai modules.`,
220
+ `For cross-service calls, import from the service / controller class file`,
221
+ `instead (e.g. \`import { AuthService } from '../services/AuthService.js'\`).`,
222
+ `If a step requires logic that lives in a sibling .ai, emit the γ-stub for`,
223
+ `THAT specific action explaining the cross-owner gap. Preserve all`,
224
+ `[PRE-BAKED] snippets verbatim.`,
225
+ '',
226
+ ].join('\n');
227
+ },
228
+ ordering: 30,
229
+ };
230
+ /**
231
+ * The full rule set, in pipeline order. Sorted by `ordering` field.
232
+ * The exported list is read by:
233
+ * - per-owner-emit.ts (validator loop)
234
+ * - prompt-builder (CONSTRAINTS section of realize-owner.prompt.yaml)
235
+ * - test corpus (fixture-to-rule expected mapping)
236
+ */
237
+ export const RULES = [
238
+ RULE_NO_FORBIDDEN_RUNTIME_APIS,
239
+ RULE_JS_EXTENSIONS_ON_RELATIVE_IMPORTS,
240
+ RULE_NO_SIBLING_CLASS_PATH_IMPORTS,
241
+ RULE_NO_SIBLING_AI_IMPORTS,
242
+ ].sort((a, b) => a.ordering - b.ordering);
243
+ /** Helper: get a single rule by id. Throws if the id doesn't exist
244
+ * (catches typos in test fixtures + downstream consumers). */
245
+ export function getRule(id) {
246
+ const rule = RULES.find((r) => r.id === id);
247
+ if (!rule) {
248
+ const known = RULES.map((r) => r.id).join(', ');
249
+ throw new Error(`Unknown realize rule id: '${id}'. Known: ${known}.`);
250
+ }
251
+ return rule;
252
+ }
253
+ /** Helper: filter RULES to the ones that apply for a given context. */
254
+ export function rulesApplicableTo(ctx) {
255
+ return RULES.filter((r) => !r.appliesWhen || r.appliesWhen(ctx));
256
+ }
257
+ /** Helper: render the CONSTRAINTS section of the prompt by joining
258
+ * each rule's promptStatement. */
259
+ export function renderConstraintsForPrompt(ctx) {
260
+ const applicable = rulesApplicableTo(ctx);
261
+ if (applicable.length === 0)
262
+ return '(no constraints declared)';
263
+ return applicable
264
+ .map((rule, i) => `${i + 1}. ${rule.promptStatement}`)
265
+ .join('\n\n');
266
+ }
267
+ // Note: the LanguageModelV3 import is currently unused but kept for
268
+ // future rules that may need to consult the model object (e.g. for
269
+ // model-specific constraints). Remove if no such rules emerge.
270
+ void null;
271
+ //# sourceMappingURL=realize-rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realize-rules.js","sourceRoot":"","sources":["../../src/realize/realize-rules.ts"],"names":[],"mappings":"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"}