@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.
- package/dist/ai/analyse-runner.d.ts.map +1 -1
- package/dist/ai/analyse-runner.js +53 -1
- package/dist/ai/analyse-runner.js.map +1 -1
- package/dist/ai/prompt-runner.d.ts +39 -1
- package/dist/ai/prompt-runner.d.ts.map +1 -1
- package/dist/ai/prompt-runner.js +44 -3
- package/dist/ai/prompt-runner.js.map +1 -1
- package/dist/ai/providers/claude-cli.d.ts.map +1 -1
- package/dist/ai/providers/claude-cli.js +8 -1
- package/dist/ai/providers/claude-cli.js.map +1 -1
- package/dist/ai/skill-loader.d.ts +50 -0
- package/dist/ai/skill-loader.d.ts.map +1 -0
- package/dist/ai/skill-loader.js +96 -0
- package/dist/ai/skill-loader.js.map +1 -0
- package/dist/analyse-prepass/adapters/index.d.ts +2 -0
- package/dist/analyse-prepass/adapters/index.d.ts.map +1 -1
- package/dist/analyse-prepass/adapters/index.js +2 -0
- package/dist/analyse-prepass/adapters/index.js.map +1 -1
- package/dist/analyse-prepass/adapters/module-functions.d.ts +95 -0
- package/dist/analyse-prepass/adapters/module-functions.d.ts.map +1 -0
- package/dist/analyse-prepass/adapters/module-functions.js +358 -0
- package/dist/analyse-prepass/adapters/module-functions.js.map +1 -0
- package/dist/analyse-prepass/adapters/naming-convention-fks.d.ts +90 -0
- package/dist/analyse-prepass/adapters/naming-convention-fks.d.ts.map +1 -0
- package/dist/analyse-prepass/adapters/naming-convention-fks.js +181 -0
- package/dist/analyse-prepass/adapters/naming-convention-fks.js.map +1 -0
- package/dist/analyse-prepass/index.d.ts +8 -0
- package/dist/analyse-prepass/index.d.ts.map +1 -1
- package/dist/analyse-prepass/index.js +130 -0
- package/dist/analyse-prepass/index.js.map +1 -1
- package/dist/libs/instance-factories/cli/templates/commander/cli-entry-generator.js +11 -12
- package/dist/libs/instance-factories/cli/templates/commander/command-generator.js +2 -2
- package/dist/libs/instance-factories/controllers/templates/fastify/routes-generator.js +29 -10
- package/dist/libs/instance-factories/services/templates/prisma/ai-behaviors-generator.js +10 -9
- package/dist/libs/instance-factories/services/templates/prisma/behavior-generator.js +24 -2
- package/dist/libs/instance-factories/services/templates/prisma/controller-generator.js +28 -20
- package/dist/normalise/index.d.ts +14 -0
- package/dist/normalise/index.d.ts.map +1 -0
- package/dist/normalise/index.js +14 -0
- package/dist/normalise/index.js.map +1 -0
- package/dist/normalise/load-overrides.d.ts +43 -0
- package/dist/normalise/load-overrides.d.ts.map +1 -0
- package/dist/normalise/load-overrides.js +121 -0
- package/dist/normalise/load-overrides.js.map +1 -0
- package/dist/normalise/normalise-rules.d.ts +181 -0
- package/dist/normalise/normalise-rules.d.ts.map +1 -0
- package/dist/normalise/normalise-rules.js +79 -0
- package/dist/normalise/normalise-rules.js.map +1 -0
- package/dist/normalise/rules/cluster-module-functions.d.ts +31 -0
- package/dist/normalise/rules/cluster-module-functions.d.ts.map +1 -0
- package/dist/normalise/rules/cluster-module-functions.js +238 -0
- package/dist/normalise/rules/cluster-module-functions.js.map +1 -0
- package/dist/normalise/rules/crud-into-curved.d.ts +117 -0
- package/dist/normalise/rules/crud-into-curved.d.ts.map +1 -0
- package/dist/normalise/rules/crud-into-curved.js +303 -0
- package/dist/normalise/rules/crud-into-curved.js.map +1 -0
- package/dist/normalise/rules/drop-trivial-passthrough.d.ts +92 -0
- package/dist/normalise/rules/drop-trivial-passthrough.d.ts.map +1 -0
- package/dist/normalise/rules/drop-trivial-passthrough.js +217 -0
- package/dist/normalise/rules/drop-trivial-passthrough.js.map +1 -0
- package/dist/normalise/runner.d.ts +58 -0
- package/dist/normalise/runner.d.ts.map +1 -0
- package/dist/normalise/runner.js +114 -0
- package/dist/normalise/runner.js.map +1 -0
- package/dist/parser/import-resolver/resolver.js +1 -1
- package/dist/parser/import-resolver/resolver.js.map +1 -1
- package/dist/realize/engines/typescript-engine.js +1 -1
- package/dist/realize/engines/typescript-engine.js.map +1 -1
- package/dist/realize/index.d.ts.map +1 -1
- package/dist/realize/index.js +221 -88
- package/dist/realize/index.js.map +1 -1
- package/dist/realize/library/library.js +1 -1
- package/dist/realize/library/library.js.map +1 -1
- package/dist/realize/library/resolver.d.ts.map +1 -1
- package/dist/realize/library/resolver.js +14 -1
- package/dist/realize/library/resolver.js.map +1 -1
- package/dist/realize/owner-emit-shared.d.ts +114 -0
- package/dist/realize/owner-emit-shared.d.ts.map +1 -0
- package/dist/realize/owner-emit-shared.js +227 -0
- package/dist/realize/owner-emit-shared.js.map +1 -0
- package/dist/realize/per-action-recovery.d.ts +74 -0
- package/dist/realize/per-action-recovery.d.ts.map +1 -0
- package/dist/realize/per-action-recovery.js +268 -0
- package/dist/realize/per-action-recovery.js.map +1 -0
- package/dist/realize/per-owner-emit.d.ts +7 -58
- package/dist/realize/per-owner-emit.d.ts.map +1 -1
- package/dist/realize/per-owner-emit.js +67 -215
- package/dist/realize/per-owner-emit.js.map +1 -1
- package/dist/realize/per-owner-runner.d.ts +24 -4
- package/dist/realize/per-owner-runner.d.ts.map +1 -1
- package/dist/realize/per-owner-runner.js +77 -19
- package/dist/realize/per-owner-runner.js.map +1 -1
- package/dist/realize/post-emit-verify/diagnostics.d.ts +107 -0
- package/dist/realize/post-emit-verify/diagnostics.d.ts.map +1 -0
- package/dist/realize/post-emit-verify/diagnostics.js +148 -0
- package/dist/realize/post-emit-verify/diagnostics.js.map +1 -0
- package/dist/realize/post-emit-verify/feedback-runner.d.ts +123 -0
- package/dist/realize/post-emit-verify/feedback-runner.d.ts.map +1 -0
- package/dist/realize/post-emit-verify/feedback-runner.js +232 -0
- package/dist/realize/post-emit-verify/feedback-runner.js.map +1 -0
- package/dist/realize/post-emit-verify/index.d.ts +19 -0
- package/dist/realize/post-emit-verify/index.d.ts.map +1 -0
- package/dist/realize/post-emit-verify/index.js +18 -0
- package/dist/realize/post-emit-verify/index.js.map +1 -0
- package/dist/realize/post-emit-verify/reemit.d.ts +82 -0
- package/dist/realize/post-emit-verify/reemit.d.ts.map +1 -0
- package/dist/realize/post-emit-verify/reemit.js +124 -0
- package/dist/realize/post-emit-verify/reemit.js.map +1 -0
- package/dist/realize/post-emit-verify/types.d.ts +187 -0
- package/dist/realize/post-emit-verify/types.d.ts.map +1 -0
- package/dist/realize/post-emit-verify/types.js +28 -0
- package/dist/realize/post-emit-verify/types.js.map +1 -0
- package/dist/realize/post-emit-verify/verifier-manifest.d.ts +29 -0
- package/dist/realize/post-emit-verify/verifier-manifest.d.ts.map +1 -0
- package/dist/realize/post-emit-verify/verifier-manifest.js +57 -0
- package/dist/realize/post-emit-verify/verifier-manifest.js.map +1 -0
- package/dist/realize/post-emit-verify/verifiers/stub-completeness.d.ts +85 -0
- package/dist/realize/post-emit-verify/verifiers/stub-completeness.d.ts.map +1 -0
- package/dist/realize/post-emit-verify/verifiers/stub-completeness.js +298 -0
- package/dist/realize/post-emit-verify/verifiers/stub-completeness.js.map +1 -0
- package/dist/realize/post-emit-verify/verifiers/tsc.d.ts +24 -0
- package/dist/realize/post-emit-verify/verifiers/tsc.d.ts.map +1 -0
- package/dist/realize/post-emit-verify/verifiers/tsc.js +148 -0
- package/dist/realize/post-emit-verify/verifiers/tsc.js.map +1 -0
- package/dist/realize/realize-context-snapshot.d.ts +70 -0
- package/dist/realize/realize-context-snapshot.d.ts.map +1 -0
- package/dist/realize/realize-context-snapshot.js +96 -0
- package/dist/realize/realize-context-snapshot.js.map +1 -0
- package/dist/realize/realize-rules.d.ts +113 -0
- package/dist/realize/realize-rules.d.ts.map +1 -0
- package/dist/realize/realize-rules.js +271 -0
- package/dist/realize/realize-rules.js.map +1 -0
- package/dist/realize/structural-validator.d.ts +36 -2
- package/dist/realize/structural-validator.d.ts.map +1 -1
- package/dist/realize/structural-validator.js +50 -7
- package/dist/realize/structural-validator.js.map +1 -1
- package/libs/instance-factories/cli/templates/commander/cli-entry-generator.ts +11 -12
- package/libs/instance-factories/cli/templates/commander/command-generator.ts +2 -2
- package/libs/instance-factories/controllers/templates/fastify/routes-generator.ts +49 -15
- package/libs/instance-factories/services/templates/prisma/ai-behaviors-generator.ts +19 -3
- package/libs/instance-factories/services/templates/prisma/behavior-generator.ts +62 -2
- package/libs/instance-factories/services/templates/prisma/controller-generator.ts +47 -20
- package/package.json +9 -1
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-owner realize-context snapshot.
|
|
3
|
+
*
|
|
4
|
+
* Phase 3 of 2026-05-13-VERIFIER-DIAGNOSTIC-TREATMENT-SPLIT.
|
|
5
|
+
*
|
|
6
|
+
* The per-owner emit prompt has TARGET RUNTIME, [PRE-BAKED] step bullets,
|
|
7
|
+
* cross-service operation signatures, available capabilities, library
|
|
8
|
+
* whitelist — everything the LLM needs to emit a real action body. The
|
|
9
|
+
* post-emit feedback runner's `buildLlmReemit` historically had NONE of
|
|
10
|
+
* this; the reemit prompt provided only the previous bad output + the
|
|
11
|
+
* verifier errors.
|
|
12
|
+
*
|
|
13
|
+
* Empirical consequence: sonnet still bridged the gap via inference;
|
|
14
|
+
* smaller models (Ollama qwen3-coder:30b, MarrBox) drifted further from
|
|
15
|
+
* the realize-emit skill's contract, introducing cross-file tsc
|
|
16
|
+
* regressions when asked to fix STUB002 stubs (measured 2026-05-13 at
|
|
17
|
+
* engines 6.54.0 on idle-meta).
|
|
18
|
+
*
|
|
19
|
+
* Phase 3 closes the gap by SNAPSHOTTING the per-owner context to disk
|
|
20
|
+
* at emit time and READING it at reemit time. The snapshot lives at
|
|
21
|
+
* `<outputDir>/.realize-context/<OwnerName>.md` and contains a compact
|
|
22
|
+
* markdown blob — directly embeddable in the reemit user prompt.
|
|
23
|
+
*
|
|
24
|
+
* Decoupling: the snapshot writer (called from per-owner-runner)
|
|
25
|
+
* doesn't know about reemit; the snapshot loader (called from reemit
|
|
26
|
+
* factory) doesn't know about per-owner-emit. They share only the
|
|
27
|
+
* file layout convention.
|
|
28
|
+
*/
|
|
29
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
30
|
+
import { dirname, join } from 'path';
|
|
31
|
+
const CONTEXT_DIR_NAME = '.realize-context';
|
|
32
|
+
/**
|
|
33
|
+
* Render the context as a compact markdown blob suitable for direct
|
|
34
|
+
* embedding in the reemit prompt body. Mirrors the section headings
|
|
35
|
+
* used in the per-owner-emit prompt template so the LLM sees the same
|
|
36
|
+
* structural cues.
|
|
37
|
+
*/
|
|
38
|
+
export function formatContextSnapshot(ctx) {
|
|
39
|
+
const lines = [];
|
|
40
|
+
lines.push(`# Realize context for owner: ${ctx.ownerName}`);
|
|
41
|
+
lines.push('');
|
|
42
|
+
lines.push(`## TARGET RUNTIME`);
|
|
43
|
+
lines.push(ctx.targetRuntime);
|
|
44
|
+
lines.push('');
|
|
45
|
+
lines.push(`## AVAILABLE SPEC SURFACE`);
|
|
46
|
+
lines.push(ctx.availableSpecSurface);
|
|
47
|
+
lines.push('');
|
|
48
|
+
lines.push(`## AVAILABLE CAPABILITIES`);
|
|
49
|
+
lines.push(ctx.availableCapabilities);
|
|
50
|
+
lines.push('');
|
|
51
|
+
lines.push(`## AVAILABLE CLIENTS`);
|
|
52
|
+
lines.push(ctx.availableClients);
|
|
53
|
+
lines.push('');
|
|
54
|
+
lines.push(`## ACTIONS`);
|
|
55
|
+
lines.push(ctx.perActionScaffolding);
|
|
56
|
+
return lines.join('\n');
|
|
57
|
+
}
|
|
58
|
+
/** Resolve the per-owner snapshot path. Owner names are
|
|
59
|
+
* always TypeScript identifiers, so no path-sanitization concerns. */
|
|
60
|
+
function snapshotPath(outputDir, ownerName) {
|
|
61
|
+
return join(outputDir, CONTEXT_DIR_NAME, `${ownerName}.md`);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Persist a context snapshot to disk. Best-effort — caller should
|
|
65
|
+
* never crash on snapshot failure (the reemit prompt degrades to the
|
|
66
|
+
* pre-Phase-3 behavior cleanly).
|
|
67
|
+
*/
|
|
68
|
+
export function writeContextSnapshot(outputDir, ctx) {
|
|
69
|
+
try {
|
|
70
|
+
const path = snapshotPath(outputDir, ctx.ownerName);
|
|
71
|
+
const dir = dirname(path);
|
|
72
|
+
if (!existsSync(dir))
|
|
73
|
+
mkdirSync(dir, { recursive: true });
|
|
74
|
+
writeFileSync(path, formatContextSnapshot(ctx));
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// best-effort; reemit falls back to the Phase 1/2 prompt shape
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Load a previously-written snapshot. Returns the raw markdown blob,
|
|
82
|
+
* or `null` if no snapshot exists for the owner. Callers (the reemit
|
|
83
|
+
* factory) include the blob in the prompt body when present.
|
|
84
|
+
*/
|
|
85
|
+
export function loadContextSnapshot(outputDir, ownerName) {
|
|
86
|
+
try {
|
|
87
|
+
const path = snapshotPath(outputDir, ownerName);
|
|
88
|
+
if (!existsSync(path))
|
|
89
|
+
return null;
|
|
90
|
+
return readFileSync(path, 'utf8');
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=realize-context-snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realize-context-snapshot.js","sourceRoot":"","sources":["../../src/realize/realize-context-snapshot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AACxE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAErC,MAAM,gBAAgB,GAAG,kBAAkB,CAAC;AAyB5C;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAwB;IAC5D,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,gCAAgC,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC;IAC5D,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAChC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAC9B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;IACxC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IACrC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;IACxC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IACtC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACnC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IACjC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACzB,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IACrC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;uEACuE;AACvE,SAAS,YAAY,CAAC,SAAiB,EAAE,SAAiB;IACxD,OAAO,IAAI,CAAC,SAAS,EAAE,gBAAgB,EAAE,GAAG,SAAS,KAAK,CAAC,CAAC;AAC9D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAClC,SAAiB,EACjB,GAAwB;IAExB,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,YAAY,CAAC,SAAS,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC;QACpD,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC1B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1D,aAAa,CAAC,IAAI,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAC,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,+DAA+D;IACjE,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,SAAiB,EACjB,SAAiB;IAEjB,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,YAAY,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAChD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QACnC,OAAO,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -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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"}
|
|
@@ -39,9 +39,17 @@ export interface PrebakedSnippet {
|
|
|
39
39
|
/** The pre-baked TypeScript snippet. The matcher's `.call` string. */
|
|
40
40
|
snippet: string;
|
|
41
41
|
}
|
|
42
|
-
/** Outcome of the structural-preservation check.
|
|
42
|
+
/** Outcome of the structural-preservation check.
|
|
43
|
+
*
|
|
44
|
+
* `ok: true` with a non-empty `warnings` array means the LLM dropped
|
|
45
|
+
* SOME pre-baked snippets but stayed at or under the drop-fraction
|
|
46
|
+
* threshold — the output is accepted (no γ-fallback) but the dropped
|
|
47
|
+
* snippets are surfaced so the caller can log them. `ok: false` means
|
|
48
|
+
* the drop fraction exceeded the threshold: the LLM substantially
|
|
49
|
+
* ignored the scaffolding and the owner should γ-fall-back. */
|
|
43
50
|
export type StructuralValidationResult = {
|
|
44
51
|
ok: true;
|
|
52
|
+
warnings?: MissingSnippet[];
|
|
45
53
|
} | {
|
|
46
54
|
ok: false;
|
|
47
55
|
missing: MissingSnippet[];
|
|
@@ -56,6 +64,27 @@ export interface MissingSnippet {
|
|
|
56
64
|
/** The closest match found in the LLM body, or null when no near-match. */
|
|
57
65
|
closestMatch: string | null;
|
|
58
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Default drop-fraction threshold: an owner may drop up to (and
|
|
69
|
+
* including) HALF of its pre-baked snippets and still be accepted
|
|
70
|
+
* (with the drops surfaced as warnings). Above half → γ-fallback.
|
|
71
|
+
*
|
|
72
|
+
* Rationale (2026-05-14, Phase 5 first target of
|
|
73
|
+
* `2026-05-13-VERIFIER-DIAGNOSTIC-TREATMENT-SPLIT.md`): pre-fix the
|
|
74
|
+
* validator rejected an ENTIRE owner if it dropped a SINGLE snippet.
|
|
75
|
+
* For sonnet this rarely fires; for openai-compatible models (Ollama
|
|
76
|
+
* qwen3-coder:30b, MarrBox) it fired on ~13/36 owners in the idle-meta
|
|
77
|
+
* trial — every paraphrased snippet nuked the whole owner to γ-stub.
|
|
78
|
+
* The post-emit feedback loop then recovered those owners anyway via
|
|
79
|
+
* context-complete reemit (which does NOT run this validator), so the
|
|
80
|
+
* strictness bought an extra LLM round-trip and STILL lost determinism.
|
|
81
|
+
* A threshold keeps the gross-failure guard ("the LLM basically
|
|
82
|
+
* ignored the scaffolding") while letting a minority of paraphrases
|
|
83
|
+
* through — they ride the normal tsc + stub-completeness gates instead.
|
|
84
|
+
*
|
|
85
|
+
* Overridable via `SPECVERSE_REALIZE_STRUCTURAL_DROP_THRESHOLD`.
|
|
86
|
+
*/
|
|
87
|
+
export declare const DEFAULT_STRUCTURAL_DROP_THRESHOLD = 0.5;
|
|
59
88
|
/**
|
|
60
89
|
* Validate that every pre-baked snippet appears in the LLM-emitted body.
|
|
61
90
|
*
|
|
@@ -64,8 +93,13 @@ export interface MissingSnippet {
|
|
|
64
93
|
* step number, step text, and the closest near-match found in the body
|
|
65
94
|
* (or `null` if there's no token-overlap candidate).
|
|
66
95
|
*
|
|
96
|
+
* `dropThreshold` is the fraction of countable snippets that may be
|
|
97
|
+
* dropped before the result flips to `ok: false`. At or below the
|
|
98
|
+
* threshold, the result is `ok: true` with the dropped snippets in
|
|
99
|
+
* `warnings`. Above it, `ok: false` with the full `missing` list.
|
|
100
|
+
*
|
|
67
101
|
* Empty `prebakedSnippets` is a vacuous pass — when no convention
|
|
68
102
|
* matched, there's nothing to preserve.
|
|
69
103
|
*/
|
|
70
|
-
export declare function validateLlmOutputPreservesConventions(llmBody: string, prebakedSnippets: PrebakedSnippet[]): StructuralValidationResult;
|
|
104
|
+
export declare function validateLlmOutputPreservesConventions(llmBody: string, prebakedSnippets: PrebakedSnippet[], dropThreshold?: number): StructuralValidationResult;
|
|
71
105
|
//# sourceMappingURL=structural-validator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"structural-validator.d.ts","sourceRoot":"","sources":["../../src/realize/structural-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,2EAA2E;AAC3E,MAAM,WAAW,eAAe;IAC9B,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,QAAQ,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED
|
|
1
|
+
{"version":3,"file":"structural-validator.d.ts","sourceRoot":"","sources":["../../src/realize/structural-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,2EAA2E;AAC3E,MAAM,WAAW,eAAe;IAC9B,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,QAAQ,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;gEAOgE;AAChE,MAAM,MAAM,0BAA0B,GAClC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAA;CAAE,GACzC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,cAAc,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7D,kCAAkC;AAClC,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAuBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,iCAAiC,MAAM,CAAC;AAErD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qCAAqC,CACnD,OAAO,EAAE,MAAM,EACf,gBAAgB,EAAE,eAAe,EAAE,EACnC,aAAa,GAAE,MAA0C,GACxD,0BAA0B,CA8D5B"}
|