@specverse/engines 6.32.0 → 6.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai/analyse-runner.d.ts.map +1 -1
- package/dist/ai/analyse-runner.js +8 -1
- package/dist/ai/analyse-runner.js.map +1 -1
- package/dist/ai/behaviours-runner.d.ts.map +1 -1
- package/dist/ai/behaviours-runner.js +35 -7
- package/dist/ai/behaviours-runner.js.map +1 -1
- package/dist/ai/create-runner.d.ts.map +1 -1
- package/dist/ai/create-runner.js +6 -0
- package/dist/ai/create-runner.js.map +1 -1
- package/dist/ai/deployment-emitter.d.ts +3 -0
- package/dist/ai/deployment-emitter.d.ts.map +1 -1
- package/dist/ai/deployment-emitter.js +145 -0
- package/dist/ai/deployment-emitter.js.map +1 -1
- package/dist/ai/manifest-emitter.d.ts +35 -10
- package/dist/ai/manifest-emitter.d.ts.map +1 -1
- package/dist/ai/manifest-emitter.js +140 -54
- package/dist/ai/manifest-emitter.js.map +1 -1
- package/dist/ai/skeleton-emitter.d.ts +1 -1
- package/dist/ai/skeleton-emitter.d.ts.map +1 -1
- package/dist/ai/skeleton-emitter.js +152 -14
- package/dist/ai/skeleton-emitter.js.map +1 -1
- package/dist/analyse-prepass/imports-graph.d.ts +407 -0
- package/dist/analyse-prepass/imports-graph.d.ts.map +1 -0
- package/dist/analyse-prepass/imports-graph.js +1200 -0
- package/dist/analyse-prepass/imports-graph.js.map +1 -0
- package/dist/analyse-prepass/index.d.ts +33 -0
- package/dist/analyse-prepass/index.d.ts.map +1 -1
- package/dist/analyse-prepass/index.js +35 -0
- package/dist/analyse-prepass/index.js.map +1 -1
- package/dist/inference/logical/generators/view-generator.d.ts +10 -0
- package/dist/inference/logical/generators/view-generator.d.ts.map +1 -1
- package/dist/inference/logical/generators/view-generator.js +20 -0
- package/dist/inference/logical/generators/view-generator.js.map +1 -1
- package/dist/libs/instance-factories/orms/templates/prisma/schema-generator.js +66 -4
- package/dist/parser/unified-parser.d.ts.map +1 -1
- package/dist/parser/unified-parser.js +103 -0
- package/dist/parser/unified-parser.js.map +1 -1
- package/dist/realize/index.d.ts.map +1 -1
- package/dist/realize/index.js +73 -148
- package/dist/realize/index.js.map +1 -1
- package/dist/realize/per-action-emitter.d.ts +235 -0
- package/dist/realize/per-action-emitter.d.ts.map +1 -0
- package/dist/realize/per-action-emitter.js +229 -0
- package/dist/realize/per-action-emitter.js.map +1 -0
- package/dist/realize/per-action-llm-emit.d.ts +87 -0
- package/dist/realize/per-action-llm-emit.d.ts.map +1 -0
- package/dist/realize/per-action-llm-emit.js +427 -0
- package/dist/realize/per-action-llm-emit.js.map +1 -0
- package/dist/realize/per-action-runner.d.ts +127 -0
- package/dist/realize/per-action-runner.d.ts.map +1 -0
- package/dist/realize/per-action-runner.js +269 -0
- package/dist/realize/per-action-runner.js.map +1 -0
- package/dist/realize/structural-validator.d.ts +71 -0
- package/dist/realize/structural-validator.d.ts.map +1 -0
- package/dist/realize/structural-validator.js +167 -0
- package/dist/realize/structural-validator.js.map +1 -0
- package/libs/instance-factories/orms/templates/prisma/__tests__/schema-generator.test.ts +416 -0
- package/libs/instance-factories/orms/templates/prisma/schema-generator.ts +182 -5
- package/package.json +3 -3
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-action body emitter — Phase 1 scaffolding for the Realize L3
|
|
3
|
+
* per-action redesign (proposal: 2026-05-07-REALIZE-L3-PER-ACTION).
|
|
4
|
+
*
|
|
5
|
+
* Reframes the unit of work for action body emission from per-step
|
|
6
|
+
* (one helper per spec step bullet, glued by step1Result → step2Result
|
|
7
|
+
* envelopes) to per-action (one coherent method body per action, with
|
|
8
|
+
* step bullets as spec input rather than code-output structure).
|
|
9
|
+
*
|
|
10
|
+
* Three branches, in priority order:
|
|
11
|
+
*
|
|
12
|
+
* 1. **Full-convention coverage** — the L2 convention matcher returns
|
|
13
|
+
* a non-AI-fallback `MatchResult` for every step in the action.
|
|
14
|
+
* The matched per-step TS snippets are assembled deterministically
|
|
15
|
+
* into the method body (no LLM call).
|
|
16
|
+
*
|
|
17
|
+
* 2. **LLM-driven emission** — at least one step lacks convention
|
|
18
|
+
* coverage AND a real LLM is available. We delegate to the
|
|
19
|
+
* microcall-orchestrator hook with the convention-matched scaffolding
|
|
20
|
+
* as pre-baked context (DR3-A: pre-baked TS shown to LLM as
|
|
21
|
+
* scaffolding, structural validator post-emission). Phase 1 stubs
|
|
22
|
+
* the prompt name (`realize-action.prompt.yaml`) — that asset lands
|
|
23
|
+
* in Phase 2; here we just thread the call site.
|
|
24
|
+
*
|
|
25
|
+
* 3. **Stub-mode body** (DR4-γ) — when neither full-convention nor LLM
|
|
26
|
+
* is available, emit a body that:
|
|
27
|
+
* a. Throws `Error("<action>: not implemented")` at function entry
|
|
28
|
+
* so the failure is unambiguous at runtime.
|
|
29
|
+
* b. Preserves the convention-matched scaffolding as a code-block
|
|
30
|
+
* comment beneath the throw, with explicit "Gaps:" entries
|
|
31
|
+
* enumerating the steps the matcher couldn't cover.
|
|
32
|
+
* Captures stub-mode review value without misleading partial execution.
|
|
33
|
+
*
|
|
34
|
+
* Phase 1 scope:
|
|
35
|
+
* - Just the orchestrator + emitter scaffolding. The realize-time
|
|
36
|
+
* wiring (replacing the per-step `.ai.ts` emission path), the
|
|
37
|
+
* `realize-action.prompt.yaml` asset, and the actual LLM call
|
|
38
|
+
* plumbing all land in subsequent phases (3, 2, and 3 respectively).
|
|
39
|
+
* - Out of scope: `.ai.ts` deletion (DR2: keep the filename, repurpose
|
|
40
|
+
* content shape — Phase 3 work).
|
|
41
|
+
*/
|
|
42
|
+
import type { runPrompt as runPromptFn } from '../ai/prompt-runner.js';
|
|
43
|
+
/** Mirror of `SharedStepContext` (libs/_shared/step-matching.ts:33). */
|
|
44
|
+
export interface SharedStepContext {
|
|
45
|
+
modelName: string;
|
|
46
|
+
serviceName: string;
|
|
47
|
+
operationName: string;
|
|
48
|
+
stepNum: number;
|
|
49
|
+
parameterNames?: string[];
|
|
50
|
+
declaredVars?: Set<string>;
|
|
51
|
+
resultName?: string;
|
|
52
|
+
}
|
|
53
|
+
/** Mirror of `SharedConvention` (libs/_shared/step-matching.ts:43). */
|
|
54
|
+
export interface SharedConvention<C extends SharedStepContext = SharedStepContext> {
|
|
55
|
+
name: string;
|
|
56
|
+
pattern: RegExp;
|
|
57
|
+
generateCall: (match: RegExpMatchArray, ctx: C) => string;
|
|
58
|
+
generateMethod?: (match: RegExpMatchArray, ctx: C) => string;
|
|
59
|
+
}
|
|
60
|
+
/** Mirror of `MatchResult` (libs/_shared/step-matching.ts:54). */
|
|
61
|
+
export interface MatchResult {
|
|
62
|
+
matched: boolean;
|
|
63
|
+
call: string;
|
|
64
|
+
helperMethod?: string;
|
|
65
|
+
functionName?: string;
|
|
66
|
+
inputs?: string[];
|
|
67
|
+
resultVar?: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Signature of the shared step matcher
|
|
71
|
+
* (`engines/libs/instance-factories/services/templates/_shared/step-matching.ts`).
|
|
72
|
+
* Re-declared here so the emitter doesn't have to static-import the
|
|
73
|
+
* libs path (which is outside `rootDir: 'src'`).
|
|
74
|
+
*/
|
|
75
|
+
export type MatcherFn = <C extends SharedStepContext>(step: string, ctx: C, conventions: ReadonlyArray<SharedConvention<C>>, aiArgsExpr: (inputs: string[], paramNames: string[]) => string) => MatchResult;
|
|
76
|
+
/**
|
|
77
|
+
* The unit of work the emitter is asked to fill. A spec-derived view of
|
|
78
|
+
* an action / operation. Mirrors the AST `ExecutablePropertiesSpec`
|
|
79
|
+
* shape but kept local + plain so callers in different phases of the
|
|
80
|
+
* realize pipeline can construct it without coupling to AST internals.
|
|
81
|
+
*/
|
|
82
|
+
export interface ActionSpec {
|
|
83
|
+
/** Action / operation name as it appears in the spec (e.g. `processOrder`). */
|
|
84
|
+
name: string;
|
|
85
|
+
/** Owning component / class — for prompt context + identity. */
|
|
86
|
+
ownerName: string;
|
|
87
|
+
/** Whether this owner is a controller, service, or model.behaviors host. */
|
|
88
|
+
ownerKind: 'controller' | 'service' | 'model';
|
|
89
|
+
/** The spec step bullets — what the LLM (or matcher) is filling in. */
|
|
90
|
+
steps: string[];
|
|
91
|
+
/** Action-level metadata, threaded into both the matcher and the LLM. */
|
|
92
|
+
description?: string;
|
|
93
|
+
parameters?: Record<string, string>;
|
|
94
|
+
returns?: string;
|
|
95
|
+
requires?: string[];
|
|
96
|
+
ensures?: string[];
|
|
97
|
+
publishes?: string[];
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Per-action emission context. Carries everything the emitter needs to
|
|
101
|
+
* dispatch a step against the L2 matcher and / or hand off to the LLM.
|
|
102
|
+
*
|
|
103
|
+
* Phase 1 keeps this minimal + additive — Phases 2/3 will add fields
|
|
104
|
+
* (cross-action sibling context, processContextByMethodKey, manifest
|
|
105
|
+
* resolved factory, …) without breaking existing call sites.
|
|
106
|
+
*/
|
|
107
|
+
export interface RealizeContext {
|
|
108
|
+
/** Component name the owning entity lives in. */
|
|
109
|
+
componentName: string;
|
|
110
|
+
/** Spec model name powering this controller / service (e.g. `OrderProcessor`). */
|
|
111
|
+
modelName: string;
|
|
112
|
+
/**
|
|
113
|
+
* The shared matcher walker (`matchAgainstConventions`) lives under
|
|
114
|
+
* `engines/libs/instance-factories/services/templates/_shared/` —
|
|
115
|
+
* outside `rootDir: 'src'`. Phase 1 keeps the emitter decoupled from
|
|
116
|
+
* the libs path by accepting the matcher as an injected callable.
|
|
117
|
+
* The realize-time wire-in (Phase 3) will dynamic-import it the same
|
|
118
|
+
* way `realize/index.ts` does for the per-step matcher; tests inject
|
|
119
|
+
* a lightweight stand-in.
|
|
120
|
+
*
|
|
121
|
+
* Optional in Phase 1: when omitted, every step is treated as
|
|
122
|
+
* "unmatched", exercising the LLM / stub branches uniformly.
|
|
123
|
+
*/
|
|
124
|
+
matcher?: MatcherFn;
|
|
125
|
+
/**
|
|
126
|
+
* The convention list to walk. The shared matcher
|
|
127
|
+
* (`matchAgainstConventions`) is library-agnostic but the conventions
|
|
128
|
+
* array carries the ORM / driver-specific patterns. Caller passes the
|
|
129
|
+
* resolved factory's conventions (prisma / mongodb-native /
|
|
130
|
+
* postgres-native / future).
|
|
131
|
+
*/
|
|
132
|
+
conventions?: ReadonlyArray<SharedConvention<SharedStepContext>>;
|
|
133
|
+
/**
|
|
134
|
+
* When the matcher leaves a step unmatched it computes an AI-fallback
|
|
135
|
+
* call site shape. The expression for that call's input object varies
|
|
136
|
+
* per-ORM (prisma uses bare `{ a, b, c }`; pg-native uses
|
|
137
|
+
* `{ args: { a, b, c } }`). Caller passes its ORM's expression. Phase
|
|
138
|
+
* 1 default keeps things ORM-neutral.
|
|
139
|
+
*/
|
|
140
|
+
aiArgsExpr?: (inputs: string[], paramNames: string[]) => string;
|
|
141
|
+
/**
|
|
142
|
+
* Hook for the per-action LLM emission. Phase 1 threads this through
|
|
143
|
+
* but does NOT wire into a real prompt (the `realize-action.prompt.yaml`
|
|
144
|
+
* asset is a Phase 2 deliverable). Tests inject a stub; production
|
|
145
|
+
* Phase 3 will inject a microcall-orchestrator-backed implementation.
|
|
146
|
+
*
|
|
147
|
+
* Returning `null` is equivalent to "LLM unavailable" → falls through
|
|
148
|
+
* to the stub-mode γ branch.
|
|
149
|
+
*/
|
|
150
|
+
llmEmit?: LlmEmitFn;
|
|
151
|
+
/**
|
|
152
|
+
* Override for "is the LLM available?". Default: respects
|
|
153
|
+
* `SPECVERSE_AI_PROVIDER` — `stub` → false, anything else → true.
|
|
154
|
+
* Tests use this to deterministically force the stub-γ branch.
|
|
155
|
+
*/
|
|
156
|
+
canCallLLM?: () => boolean;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* The LLM emission hook. Phase 1 is a thin pass-through to a microcall-
|
|
160
|
+
* orchestrator-style handler; Phase 3 will replace this with a wrapper
|
|
161
|
+
* around `runActionMicrocalls` (or a direct `runPrompt` call) using the
|
|
162
|
+
* `realize-action.prompt.yaml` asset.
|
|
163
|
+
*/
|
|
164
|
+
export type LlmEmitFn = (request: LlmEmitRequest) => Promise<string | null>;
|
|
165
|
+
/**
|
|
166
|
+
* Inputs handed to the LLM emission hook. Carries the action's full
|
|
167
|
+
* spec body plus the per-step scaffolding produced by the L2 matcher
|
|
168
|
+
* (DR3-A pre-baked TS shown to LLM).
|
|
169
|
+
*/
|
|
170
|
+
export interface LlmEmitRequest {
|
|
171
|
+
action: ActionSpec;
|
|
172
|
+
context: RealizeContext;
|
|
173
|
+
/** Matcher output for each step, in spec order. `null` means unmatched. */
|
|
174
|
+
perStepMatches: Array<MatchResult | null>;
|
|
175
|
+
/**
|
|
176
|
+
* The pre-baked TS scaffolding (matched steps' `.call` strings joined).
|
|
177
|
+
* Phase 2's prompt feeds this to the LLM verbatim alongside the spec
|
|
178
|
+
* step bullets.
|
|
179
|
+
*/
|
|
180
|
+
scaffoldingSnippet: string;
|
|
181
|
+
/**
|
|
182
|
+
* Phase 1 placeholder. The prompt name the orchestrator should use.
|
|
183
|
+
* The asset itself doesn't exist yet (Phase 2 deliverable); Phase 3
|
|
184
|
+
* implementations should pin this to `realize-action.prompt.yaml`.
|
|
185
|
+
*/
|
|
186
|
+
promptName: string;
|
|
187
|
+
/**
|
|
188
|
+
* Optional thread-through to the underlying `runPrompt`. Lets tests
|
|
189
|
+
* assert the call shape without standing up an LLM provider.
|
|
190
|
+
*/
|
|
191
|
+
runPromptOverride?: typeof runPromptFn;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* When every step is convention-matched, assemble the method body
|
|
195
|
+
* deterministically by joining the matcher's `.call` strings.
|
|
196
|
+
*
|
|
197
|
+
* The call strings come pre-indented (4 spaces — enough for "inside a
|
|
198
|
+
* method body") and trailing-newline-free. We wrap them with a
|
|
199
|
+
* try-block + a `return` at the end where possible (last step's
|
|
200
|
+
* resultVar). Phase 3 will refine the assembly (typed signatures,
|
|
201
|
+
* error-flow shapes) once the realize-time wiring decides on a single
|
|
202
|
+
* controller / service template path; Phase 1 keeps it minimal so the
|
|
203
|
+
* emitter's contract is stable.
|
|
204
|
+
*/
|
|
205
|
+
export declare function assembleFromConventions(action: ActionSpec, perStepMatches: MatchResult[]): string;
|
|
206
|
+
/**
|
|
207
|
+
* Emit the γ-shaped stub body (DR4 resolution). The function body opens
|
|
208
|
+
* with a `throw new Error("<action>: not implemented")` so the failure
|
|
209
|
+
* surfaces unambiguously at runtime, and the convention-matched
|
|
210
|
+
* scaffolding is preserved as a code-block comment beneath the throw
|
|
211
|
+
* for reviewer context.
|
|
212
|
+
*
|
|
213
|
+
* Format:
|
|
214
|
+
* throw new Error("processOrder: not implemented");
|
|
215
|
+
* /* Convention-matched scaffolding (preserved for review):
|
|
216
|
+
* * <pre-baked TS for matched steps>
|
|
217
|
+
* * Gaps:
|
|
218
|
+
* * - <step text for unmatched steps>
|
|
219
|
+
* *\/
|
|
220
|
+
*/
|
|
221
|
+
export declare function emitStubGammaBody(action: ActionSpec, perStepMatches: Array<MatchResult | null>): string;
|
|
222
|
+
/**
|
|
223
|
+
* Emit the body for a single action.
|
|
224
|
+
*
|
|
225
|
+
* Branch order:
|
|
226
|
+
* 1. Full-convention coverage → assembled deterministically.
|
|
227
|
+
* 2. Mixed coverage + LLM available + `llmEmit` returns text → LLM result.
|
|
228
|
+
* 3. Otherwise (stub mode, no LLM, or `llmEmit` returned null) → γ stub.
|
|
229
|
+
*
|
|
230
|
+
* The signature returns just the body (not the wrapping method
|
|
231
|
+
* declaration / params / return type); Phase 3 will integrate this
|
|
232
|
+
* with the controller / service template emitters.
|
|
233
|
+
*/
|
|
234
|
+
export declare function emitActionBody(action: ActionSpec, context: RealizeContext): Promise<string>;
|
|
235
|
+
//# sourceMappingURL=per-action-emitter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"per-action-emitter.d.ts","sourceRoot":"","sources":["../../src/realize/per-action-emitter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-action body emitter — Phase 1 scaffolding for the Realize L3
|
|
3
|
+
* per-action redesign (proposal: 2026-05-07-REALIZE-L3-PER-ACTION).
|
|
4
|
+
*
|
|
5
|
+
* Reframes the unit of work for action body emission from per-step
|
|
6
|
+
* (one helper per spec step bullet, glued by step1Result → step2Result
|
|
7
|
+
* envelopes) to per-action (one coherent method body per action, with
|
|
8
|
+
* step bullets as spec input rather than code-output structure).
|
|
9
|
+
*
|
|
10
|
+
* Three branches, in priority order:
|
|
11
|
+
*
|
|
12
|
+
* 1. **Full-convention coverage** — the L2 convention matcher returns
|
|
13
|
+
* a non-AI-fallback `MatchResult` for every step in the action.
|
|
14
|
+
* The matched per-step TS snippets are assembled deterministically
|
|
15
|
+
* into the method body (no LLM call).
|
|
16
|
+
*
|
|
17
|
+
* 2. **LLM-driven emission** — at least one step lacks convention
|
|
18
|
+
* coverage AND a real LLM is available. We delegate to the
|
|
19
|
+
* microcall-orchestrator hook with the convention-matched scaffolding
|
|
20
|
+
* as pre-baked context (DR3-A: pre-baked TS shown to LLM as
|
|
21
|
+
* scaffolding, structural validator post-emission). Phase 1 stubs
|
|
22
|
+
* the prompt name (`realize-action.prompt.yaml`) — that asset lands
|
|
23
|
+
* in Phase 2; here we just thread the call site.
|
|
24
|
+
*
|
|
25
|
+
* 3. **Stub-mode body** (DR4-γ) — when neither full-convention nor LLM
|
|
26
|
+
* is available, emit a body that:
|
|
27
|
+
* a. Throws `Error("<action>: not implemented")` at function entry
|
|
28
|
+
* so the failure is unambiguous at runtime.
|
|
29
|
+
* b. Preserves the convention-matched scaffolding as a code-block
|
|
30
|
+
* comment beneath the throw, with explicit "Gaps:" entries
|
|
31
|
+
* enumerating the steps the matcher couldn't cover.
|
|
32
|
+
* Captures stub-mode review value without misleading partial execution.
|
|
33
|
+
*
|
|
34
|
+
* Phase 1 scope:
|
|
35
|
+
* - Just the orchestrator + emitter scaffolding. The realize-time
|
|
36
|
+
* wiring (replacing the per-step `.ai.ts` emission path), the
|
|
37
|
+
* `realize-action.prompt.yaml` asset, and the actual LLM call
|
|
38
|
+
* plumbing all land in subsequent phases (3, 2, and 3 respectively).
|
|
39
|
+
* - Out of scope: `.ai.ts` deletion (DR2: keep the filename, repurpose
|
|
40
|
+
* content shape — Phase 3 work).
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* Default LLM-availability check. Mirrors `model-resolver.resolveProviderId`'s
|
|
44
|
+
* logic without importing it (avoids dragging the AI subpath into
|
|
45
|
+
* realize for a cheap predicate). When a real implementation lands in
|
|
46
|
+
* Phase 3, it will likely just call `resolveProviderId() !== 'stub'`.
|
|
47
|
+
*/
|
|
48
|
+
function defaultCanCallLLM() {
|
|
49
|
+
const explicit = process.env.SPECVERSE_AI_PROVIDER;
|
|
50
|
+
if (explicit === 'stub')
|
|
51
|
+
return false;
|
|
52
|
+
if (process.env.MCP_SERVER === '1')
|
|
53
|
+
return false;
|
|
54
|
+
// If unset, we can't be sure — be conservative (false) so realize
|
|
55
|
+
// doesn't accidentally fire LLM calls in environments that aren't
|
|
56
|
+
// configured for AI. Tests inject `canCallLLM` explicitly.
|
|
57
|
+
return Boolean(explicit) || Boolean(process.env.ANTHROPIC_API_KEY);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Run every step in the action through the L2 convention matcher.
|
|
61
|
+
* Returns `MatchResult | null` per step in spec order (`null` when the
|
|
62
|
+
* action has no steps to dispatch).
|
|
63
|
+
*
|
|
64
|
+
* The matcher walks conventions in order; first match wins. Unmatched
|
|
65
|
+
* steps compute the AI-fallback shape (`functionName + inputs + resultVar`)
|
|
66
|
+
* deterministically — they're still part of `MatchResult`, just with
|
|
67
|
+
* `matched: false`. We rely on that flag to detect coverage.
|
|
68
|
+
*/
|
|
69
|
+
function matchSteps(action, context) {
|
|
70
|
+
if (!action.steps || action.steps.length === 0)
|
|
71
|
+
return [];
|
|
72
|
+
if (!context.matcher || !context.conventions) {
|
|
73
|
+
// No matcher / no conventions → every step is "unmatched" for
|
|
74
|
+
// emitter purposes. We still return MatchResult shapes so the LLM /
|
|
75
|
+
// stub branches see the same per-step structure as a real ORM
|
|
76
|
+
// dispatch.
|
|
77
|
+
return action.steps.map((_step, i) => ({
|
|
78
|
+
matched: false,
|
|
79
|
+
call: ` // Step ${i + 1}: ${action.steps[i]} [unmatched]`,
|
|
80
|
+
functionName: undefined,
|
|
81
|
+
inputs: undefined,
|
|
82
|
+
resultVar: undefined,
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
const aiArgsExpr = context.aiArgsExpr ??
|
|
86
|
+
((inputs) => (inputs.length > 0 ? `{ ${inputs.join(', ')} }` : '{}'));
|
|
87
|
+
const declaredVars = new Set();
|
|
88
|
+
const parameterNames = action.parameters ? Object.keys(action.parameters) : [];
|
|
89
|
+
const results = [];
|
|
90
|
+
for (let i = 0; i < action.steps.length; i++) {
|
|
91
|
+
const step = action.steps[i];
|
|
92
|
+
const ctx = {
|
|
93
|
+
modelName: context.modelName,
|
|
94
|
+
serviceName: action.ownerName,
|
|
95
|
+
operationName: action.name,
|
|
96
|
+
stepNum: i + 1,
|
|
97
|
+
parameterNames,
|
|
98
|
+
declaredVars,
|
|
99
|
+
};
|
|
100
|
+
const result = context.matcher(step, ctx, context.conventions, aiArgsExpr);
|
|
101
|
+
results.push(result);
|
|
102
|
+
}
|
|
103
|
+
return results;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* When every step is convention-matched, assemble the method body
|
|
107
|
+
* deterministically by joining the matcher's `.call` strings.
|
|
108
|
+
*
|
|
109
|
+
* The call strings come pre-indented (4 spaces — enough for "inside a
|
|
110
|
+
* method body") and trailing-newline-free. We wrap them with a
|
|
111
|
+
* try-block + a `return` at the end where possible (last step's
|
|
112
|
+
* resultVar). Phase 3 will refine the assembly (typed signatures,
|
|
113
|
+
* error-flow shapes) once the realize-time wiring decides on a single
|
|
114
|
+
* controller / service template path; Phase 1 keeps it minimal so the
|
|
115
|
+
* emitter's contract is stable.
|
|
116
|
+
*/
|
|
117
|
+
export function assembleFromConventions(action, perStepMatches) {
|
|
118
|
+
const lines = [];
|
|
119
|
+
for (const m of perStepMatches) {
|
|
120
|
+
// m.call already carries indentation. Strip a trailing newline if any.
|
|
121
|
+
lines.push(m.call.replace(/\n$/, ''));
|
|
122
|
+
}
|
|
123
|
+
// Best-effort return statement: when the last step produced a
|
|
124
|
+
// resultVar, return it. The matcher's `.call` for matched conventions
|
|
125
|
+
// doesn't include a return; we add one here so the body is
|
|
126
|
+
// syntactically complete. Unmatched fallbacks still set resultVar
|
|
127
|
+
// but we'd never reach this assembler with an unmatched step.
|
|
128
|
+
const last = perStepMatches[perStepMatches.length - 1];
|
|
129
|
+
if (last?.resultVar) {
|
|
130
|
+
lines.push(` return ${last.resultVar};`);
|
|
131
|
+
}
|
|
132
|
+
return lines.join('\n');
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Emit the γ-shaped stub body (DR4 resolution). The function body opens
|
|
136
|
+
* with a `throw new Error("<action>: not implemented")` so the failure
|
|
137
|
+
* surfaces unambiguously at runtime, and the convention-matched
|
|
138
|
+
* scaffolding is preserved as a code-block comment beneath the throw
|
|
139
|
+
* for reviewer context.
|
|
140
|
+
*
|
|
141
|
+
* Format:
|
|
142
|
+
* throw new Error("processOrder: not implemented");
|
|
143
|
+
* /* Convention-matched scaffolding (preserved for review):
|
|
144
|
+
* * <pre-baked TS for matched steps>
|
|
145
|
+
* * Gaps:
|
|
146
|
+
* * - <step text for unmatched steps>
|
|
147
|
+
* *\/
|
|
148
|
+
*/
|
|
149
|
+
export function emitStubGammaBody(action, perStepMatches) {
|
|
150
|
+
const matchedLines = [];
|
|
151
|
+
const gaps = [];
|
|
152
|
+
for (let i = 0; i < perStepMatches.length; i++) {
|
|
153
|
+
const result = perStepMatches[i];
|
|
154
|
+
const step = action.steps[i] ?? '';
|
|
155
|
+
if (result?.matched) {
|
|
156
|
+
// Indent for readability inside the comment block.
|
|
157
|
+
const callLines = result.call.split('\n').map((ln) => ' * ' + ln.trimStart());
|
|
158
|
+
matchedLines.push(...callLines);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
gaps.push(` * - ${step}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const lines = [];
|
|
165
|
+
lines.push(` throw new Error(${JSON.stringify(`${action.name}: not implemented`)});`);
|
|
166
|
+
lines.push(' /* Convention-matched scaffolding (preserved for review):');
|
|
167
|
+
if (matchedLines.length > 0) {
|
|
168
|
+
lines.push(...matchedLines);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
lines.push(' * (no steps matched any convention)');
|
|
172
|
+
}
|
|
173
|
+
if (gaps.length > 0) {
|
|
174
|
+
lines.push(' * Gaps:');
|
|
175
|
+
lines.push(...gaps);
|
|
176
|
+
}
|
|
177
|
+
lines.push(' */');
|
|
178
|
+
return lines.join('\n');
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Emit the body for a single action.
|
|
182
|
+
*
|
|
183
|
+
* Branch order:
|
|
184
|
+
* 1. Full-convention coverage → assembled deterministically.
|
|
185
|
+
* 2. Mixed coverage + LLM available + `llmEmit` returns text → LLM result.
|
|
186
|
+
* 3. Otherwise (stub mode, no LLM, or `llmEmit` returned null) → γ stub.
|
|
187
|
+
*
|
|
188
|
+
* The signature returns just the body (not the wrapping method
|
|
189
|
+
* declaration / params / return type); Phase 3 will integrate this
|
|
190
|
+
* with the controller / service template emitters.
|
|
191
|
+
*/
|
|
192
|
+
export async function emitActionBody(action, context) {
|
|
193
|
+
const perStepMatches = matchSteps(action, context);
|
|
194
|
+
const allMatched = perStepMatches.length > 0 &&
|
|
195
|
+
perStepMatches.every((m) => m !== null && m.matched === true);
|
|
196
|
+
// Branch 1 — every step has a convention match.
|
|
197
|
+
if (allMatched) {
|
|
198
|
+
return assembleFromConventions(action, perStepMatches);
|
|
199
|
+
}
|
|
200
|
+
// Branch 2 — at least one gap. Try the LLM if available + hook present.
|
|
201
|
+
const canCall = (context.canCallLLM ?? defaultCanCallLLM)();
|
|
202
|
+
if (canCall && context.llmEmit) {
|
|
203
|
+
const matched = perStepMatches.filter((m) => m !== null && m.matched === true);
|
|
204
|
+
const scaffoldingSnippet = matched.map((m) => m.call.replace(/\n$/, '')).join('\n');
|
|
205
|
+
try {
|
|
206
|
+
const llmBody = await context.llmEmit({
|
|
207
|
+
action,
|
|
208
|
+
context,
|
|
209
|
+
perStepMatches,
|
|
210
|
+
scaffoldingSnippet,
|
|
211
|
+
// PLACEHOLDER (Phase 1) — the prompt asset doesn't exist yet.
|
|
212
|
+
// Phase 2 ships `realize-action.prompt.yaml` and Phase 3 wires
|
|
213
|
+
// this constant up to the actual call site.
|
|
214
|
+
promptName: 'realize-action.prompt.yaml',
|
|
215
|
+
});
|
|
216
|
+
if (llmBody && llmBody.trim().length > 0) {
|
|
217
|
+
return llmBody;
|
|
218
|
+
}
|
|
219
|
+
// Fall through to stub when LLM returned null / empty.
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
// Any LLM error → stub. Phase 3 will add structured logging +
|
|
223
|
+
// surgical-retry semantics here; Phase 1 keeps the contract simple.
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// Branch 3 — γ stub.
|
|
227
|
+
return emitStubGammaBody(action, perStepMatches);
|
|
228
|
+
}
|
|
229
|
+
//# sourceMappingURL=per-action-emitter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"per-action-emitter.js","sourceRoot":"","sources":["../../src/realize/per-action-emitter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-action LLM emit hook — Phase 3 of the L3 redesign.
|
|
3
|
+
*
|
|
4
|
+
* Builds an `LlmEmitFn` (the dependency-injection seam exposed by
|
|
5
|
+
* `per-action-emitter.ts`) that loads `realize-action.prompt.yaml`,
|
|
6
|
+
* calls `runPrompt` with the assembled per-action context, runs the
|
|
7
|
+
* structural validator over the LLM output, and surgically retries
|
|
8
|
+
* once on parse failure (DR3-aligned).
|
|
9
|
+
*
|
|
10
|
+
* Three quality gates run on every LLM emission:
|
|
11
|
+
* 1. Markdown extraction — pull the ```typescript fenced block out of
|
|
12
|
+
* the LLM response. Empty / missing block → null (caller falls
|
|
13
|
+
* through to γ stub).
|
|
14
|
+
* 2. Structural preservation (DR3) — every pre-baked snippet must
|
|
15
|
+
* appear verbatim in the body. A rewritten snippet is a loud
|
|
16
|
+
* failure; we throw with a diff so the user knows the LLM
|
|
17
|
+
* misbehaved.
|
|
18
|
+
* 3. esbuild parse check — the body must be syntactically valid TS.
|
|
19
|
+
* On parse failure we run ONE surgical retry with the parse error
|
|
20
|
+
* fed back as a userPrefix; if it fails again we return null and
|
|
21
|
+
* the caller emits the γ stub.
|
|
22
|
+
*
|
|
23
|
+
* The realize-action.prompt.yaml asset (Phase 2 deliverable) lives in
|
|
24
|
+
* `@specverse/assets/prompts/core/standard/default/realize-action.prompt.yaml`
|
|
25
|
+
* once that package is republished. Until then, we fall back to a
|
|
26
|
+
* filesystem path under specverse-self/assets/ so Phase 3 doesn't have
|
|
27
|
+
* to wait on a registry roundtrip.
|
|
28
|
+
*/
|
|
29
|
+
import { runPrompt as runPromptFn, type PromptYaml } from '../ai/prompt-runner.js';
|
|
30
|
+
import type { LlmEmitFn } from './per-action-emitter.js';
|
|
31
|
+
/** Options for {@link createRealizeActionLlmEmit}. */
|
|
32
|
+
export interface CreateRealizeActionLlmEmitOptions {
|
|
33
|
+
/** Override `runPrompt` for tests. */
|
|
34
|
+
runPromptOverride?: typeof runPromptFn;
|
|
35
|
+
/** Override `loadPrompt` for tests; default is loadPrompt('realize-action'). */
|
|
36
|
+
loadPromptOverride?: () => PromptYaml;
|
|
37
|
+
/** Override the parse-check for tests. Must return null on success
|
|
38
|
+
* and an error message string on failure. */
|
|
39
|
+
parseCheckOverride?: (code: string) => Promise<string | null>;
|
|
40
|
+
/** Override the model passed to runPrompt (rare). */
|
|
41
|
+
model?: any;
|
|
42
|
+
/** Per-call timeout. Default 5min — same as microcall-orchestrator. */
|
|
43
|
+
timeoutMs?: number;
|
|
44
|
+
/** Disable the surgical retry (tests). */
|
|
45
|
+
disableSurgicalRetry?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Available clients string passed into the prompt's `availableClients`
|
|
48
|
+
* variable. Caller (the realize runner) builds this from the
|
|
49
|
+
* component's `import:` block + the action's `uses:`. When omitted,
|
|
50
|
+
* the prompt receives a "(none)" placeholder.
|
|
51
|
+
*/
|
|
52
|
+
availableClients?: string;
|
|
53
|
+
/** Sibling-action signatures string. Same convention. */
|
|
54
|
+
siblingActions?: string;
|
|
55
|
+
/** Cross-method process context string. Same convention. */
|
|
56
|
+
processContext?: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Build an {@link LlmEmitFn} for use in per-action realize emission.
|
|
60
|
+
*
|
|
61
|
+
* The returned function:
|
|
62
|
+
* 1. Builds the prompt input from the per-action emitter's request.
|
|
63
|
+
* 2. Calls runPrompt({ operation: 'realize-action', ... }).
|
|
64
|
+
* 3. Extracts the ```typescript block from the response.
|
|
65
|
+
* 4. Runs the structural validator (DR3); throws on rewrite.
|
|
66
|
+
* 5. Runs an esbuild parse check; surgical-retries once on failure.
|
|
67
|
+
* 6. Returns the body string, or null when all attempts fail (caller
|
|
68
|
+
* falls through to γ stub).
|
|
69
|
+
*/
|
|
70
|
+
export declare function createRealizeActionLlmEmit(opts?: CreateRealizeActionLlmEmitOptions): LlmEmitFn;
|
|
71
|
+
/**
|
|
72
|
+
* Filesystem fallback for realize-action.prompt.yaml when the installed
|
|
73
|
+
* @specverse/assets package predates Phase 2's prompt addition.
|
|
74
|
+
*
|
|
75
|
+
* Searches a small fixed set of candidate paths relative to the engines
|
|
76
|
+
* package's installed location and the worktree layout used in
|
|
77
|
+
* specverse-self repo. Returns null when none are found; runPrompt then
|
|
78
|
+
* surfaces the canonical "prompt file not found" error.
|
|
79
|
+
*/
|
|
80
|
+
export declare function loadRealizeActionPromptFromFallback(): PromptYaml | null;
|
|
81
|
+
/**
|
|
82
|
+
* Pull the first ```typescript fenced block out of an LLM response.
|
|
83
|
+
* Tolerates plain ``` (no language tag) when the body starts with
|
|
84
|
+
* recognisably-TS content. Returns null if no block is found.
|
|
85
|
+
*/
|
|
86
|
+
export declare function extractTypescriptBlock(text: string): string | null;
|
|
87
|
+
//# sourceMappingURL=per-action-llm-emit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"per-action-llm-emit.d.ts","sourceRoot":"","sources":["../../src/realize/per-action-llm-emit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAOH,OAAO,EAEL,SAAS,IAAI,WAAW,EAGxB,KAAK,UAAU,EAGhB,MAAM,wBAAwB,CAAC;AAKhC,OAAO,KAAK,EACV,SAAS,EAKV,MAAM,yBAAyB,CAAC;AAIjC,sDAAsD;AACtD,MAAM,WAAW,iCAAiC;IAChD,sCAAsC;IACtC,iBAAiB,CAAC,EAAE,OAAO,WAAW,CAAC;IACvC,gFAAgF;IAChF,kBAAkB,CAAC,EAAE,MAAM,UAAU,CAAC;IACtC;kDAC8C;IAC9C,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC9D,qDAAqD;IACrD,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,yDAAyD;IACzD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,4DAA4D;IAC5D,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,GAAE,iCAAsC,GAC3C,SAAS,CAuFX;AA4ED;;;;;;;;GAQG;AACH,wBAAgB,mCAAmC,IAAI,UAAU,GAAG,IAAI,CAqDvE;AAsID;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAalE"}
|