@specverse/engines 6.41.1 → 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.
- 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/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 +142 -90
- 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-owner-emit.d.ts +13 -1
- package/dist/realize/per-owner-emit.d.ts.map +1 -1
- package/dist/realize/per-owner-emit.js +64 -1
- package/dist/realize/per-owner-emit.js.map +1 -1
- package/dist/realize/per-owner-runner.d.ts +36 -2
- package/dist/realize/per-owner-runner.d.ts.map +1 -1
- package/dist/realize/per-owner-runner.js +137 -3
- package/dist/realize/per-owner-runner.js.map +1 -1
- package/dist/realize/post-emit-verify/feedback-runner.d.ts +84 -0
- package/dist/realize/post-emit-verify/feedback-runner.d.ts.map +1 -0
- package/dist/realize/post-emit-verify/feedback-runner.js +177 -0
- package/dist/realize/post-emit-verify/feedback-runner.js.map +1 -0
- package/dist/realize/post-emit-verify/index.d.ts +17 -0
- package/dist/realize/post-emit-verify/index.d.ts.map +1 -0
- package/dist/realize/post-emit-verify/index.js +16 -0
- package/dist/realize/post-emit-verify/index.js.map +1 -0
- package/dist/realize/post-emit-verify/reemit.d.ts +61 -0
- package/dist/realize/post-emit-verify/reemit.d.ts.map +1 -0
- package/dist/realize/post-emit-verify/reemit.js +122 -0
- package/dist/realize/post-emit-verify/reemit.js.map +1 -0
- package/dist/realize/post-emit-verify/types.d.ts +138 -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 +55 -0
- package/dist/realize/post-emit-verify/verifier-manifest.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-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/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/services/templates/prisma/behavior-generator.ts +62 -2
- package/libs/instance-factories/services/templates/prisma/controller-generator.ts +42 -20
- package/package.json +9 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @specverse/engines/realize/post-emit-verify — post-emit verification
|
|
3
|
+
* + feedback runner.
|
|
4
|
+
*
|
|
5
|
+
* Generic framework for running compile / lint / typecheck verifiers
|
|
6
|
+
* on the realized output and feeding errors back to the LLM for a
|
|
7
|
+
* surgical fix pass.
|
|
8
|
+
*
|
|
9
|
+
* See `specverse-self/docs/proposals/...` (TBD) for the design rationale
|
|
10
|
+
* + empirical evidence from idle-meta 2026-05-13.
|
|
11
|
+
*/
|
|
12
|
+
export { VERIFIERS, runAllVerifiers, findVerifier, } from './verifier-manifest.js';
|
|
13
|
+
export { runPostEmitFeedback, defaultMapFileToOwner, } from './feedback-runner.js';
|
|
14
|
+
export { TSC_VERIFIER } from './verifiers/tsc.js';
|
|
15
|
+
export { buildLlmReemit, formatFeedbackPrompt, } from './reemit.js';
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/realize/post-emit-verify/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAUH,OAAO,EACL,SAAS,EACT,eAAe,EACf,YAAY,GACb,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,mBAAmB,EACnB,qBAAqB,GAMtB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EACL,cAAc,EACd,oBAAoB,GAErB,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default `Reemit` implementation — builds a feedback prompt from
|
|
3
|
+
* verifier errors + previous file content and calls the LLM to
|
|
4
|
+
* re-emit a single file.
|
|
5
|
+
*
|
|
6
|
+
* The realize-emit skill (at ~/.claude/skills/realize-emit/) is
|
|
7
|
+
* auto-loaded by claude-cli when its description matches the prompt
|
|
8
|
+
* shape — that carries all the per-owner emission conventions
|
|
9
|
+
* (typing rules, _input convention, γ-stub shape, etc.) so the
|
|
10
|
+
* feedback re-emit benefits from the same guidance as the original
|
|
11
|
+
* emission.
|
|
12
|
+
*
|
|
13
|
+
* The prompt is intentionally minimal:
|
|
14
|
+
* - Errors (verifier-formatted)
|
|
15
|
+
* - Previous file content
|
|
16
|
+
* - A request to re-emit
|
|
17
|
+
*
|
|
18
|
+
* Future iterations may augment with AVAILABLE SPEC SURFACE (cross-
|
|
19
|
+
* service signatures) when the errors are arity-shape — empirically,
|
|
20
|
+
* the skill + basic prompt suffices for most null-safety / type
|
|
21
|
+
* mismatch / unused-decl cases.
|
|
22
|
+
*/
|
|
23
|
+
import type { LanguageModel } from 'ai';
|
|
24
|
+
import type { Reemit, ReemitRequest } from './feedback-runner.js';
|
|
25
|
+
export interface BuildReemitOptions {
|
|
26
|
+
/** LLM model — same shared model used by per-owner-emit. */
|
|
27
|
+
model: LanguageModel;
|
|
28
|
+
/** Max retries per file when the LLM produces empty / unparseable
|
|
29
|
+
* output. Default 1. */
|
|
30
|
+
maxAttempts?: number;
|
|
31
|
+
/** Per-call timeout. Default 3min — feedback re-emits are typically
|
|
32
|
+
* faster than fresh emissions because the LLM has the previous
|
|
33
|
+
* content to anchor on. */
|
|
34
|
+
timeoutMs?: number;
|
|
35
|
+
/**
|
|
36
|
+
* Skills whose content should be inlined into the system prompt for
|
|
37
|
+
* non-claude-cli providers. claude-cli auto-loads skills via
|
|
38
|
+
* filesystem-based resolution; other providers (`openai-compatible`,
|
|
39
|
+
* `anthropic`, `stub`) need explicit inlining of the skill content.
|
|
40
|
+
*
|
|
41
|
+
* Defaults to `['realize-emit']` — the realize-emit skill carries the
|
|
42
|
+
* per-owner emission CONSTRAINTS / GUIDANCE / EXAMPLE that's directly
|
|
43
|
+
* relevant to the feedback re-emit task (typing conventions, _input
|
|
44
|
+
* convention, γ-stub shape).
|
|
45
|
+
*/
|
|
46
|
+
skills?: string[];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Build a `Reemit` callback that uses the provided LLM model.
|
|
50
|
+
*
|
|
51
|
+
* The returned closure can be passed straight to `runPostEmitFeedback`.
|
|
52
|
+
*/
|
|
53
|
+
export declare function buildLlmReemit(options: BuildReemitOptions): Reemit;
|
|
54
|
+
/**
|
|
55
|
+
* Format the feedback prompt body. Caller's LLM provider (e.g.
|
|
56
|
+
* claude-cli) loads the realize-emit skill as system context.
|
|
57
|
+
*
|
|
58
|
+
* Exported for tests + so callers can override the prompt shape.
|
|
59
|
+
*/
|
|
60
|
+
export declare function formatFeedbackPrompt(req: ReemitRequest): string;
|
|
61
|
+
//# sourceMappingURL=reemit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reemit.d.ts","sourceRoot":"","sources":["../../../src/realize/post-emit-verify/reemit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAGxC,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGlE,MAAM,WAAW,kBAAkB;IACjC,4DAA4D;IAC5D,KAAK,EAAE,aAAa,CAAC;IACrB;6BACyB;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;gCAE4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;;;;OAUG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,CA4ClE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAqB/D"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default `Reemit` implementation — builds a feedback prompt from
|
|
3
|
+
* verifier errors + previous file content and calls the LLM to
|
|
4
|
+
* re-emit a single file.
|
|
5
|
+
*
|
|
6
|
+
* The realize-emit skill (at ~/.claude/skills/realize-emit/) is
|
|
7
|
+
* auto-loaded by claude-cli when its description matches the prompt
|
|
8
|
+
* shape — that carries all the per-owner emission conventions
|
|
9
|
+
* (typing rules, _input convention, γ-stub shape, etc.) so the
|
|
10
|
+
* feedback re-emit benefits from the same guidance as the original
|
|
11
|
+
* emission.
|
|
12
|
+
*
|
|
13
|
+
* The prompt is intentionally minimal:
|
|
14
|
+
* - Errors (verifier-formatted)
|
|
15
|
+
* - Previous file content
|
|
16
|
+
* - A request to re-emit
|
|
17
|
+
*
|
|
18
|
+
* Future iterations may augment with AVAILABLE SPEC SURFACE (cross-
|
|
19
|
+
* service signatures) when the errors are arity-shape — empirically,
|
|
20
|
+
* the skill + basic prompt suffices for most null-safety / type
|
|
21
|
+
* mismatch / unused-decl cases.
|
|
22
|
+
*/
|
|
23
|
+
import { generateText } from 'ai';
|
|
24
|
+
import { assembleSystemWithSkills } from '../../ai/prompt-runner.js';
|
|
25
|
+
import { resolveProviderId } from '../../ai/model-resolver.js';
|
|
26
|
+
/**
|
|
27
|
+
* Build a `Reemit` callback that uses the provided LLM model.
|
|
28
|
+
*
|
|
29
|
+
* The returned closure can be passed straight to `runPostEmitFeedback`.
|
|
30
|
+
*/
|
|
31
|
+
export function buildLlmReemit(options) {
|
|
32
|
+
const maxAttempts = options.maxAttempts ?? 1;
|
|
33
|
+
const timeoutMs = options.timeoutMs ?? 3 * 60_000;
|
|
34
|
+
const skills = options.skills ?? ['realize-emit'];
|
|
35
|
+
// Provider-aware skill inlining: claude-cli auto-loads skills via the
|
|
36
|
+
// CLI's filesystem resolution; everyone else needs the skill content
|
|
37
|
+
// prepended to the system prompt. Compute once at build time — the
|
|
38
|
+
// provider can't change mid-loop.
|
|
39
|
+
const providerId = resolveProviderId();
|
|
40
|
+
const systemPrompt = assembleSystemWithSkills('', providerId, skills);
|
|
41
|
+
return async (req) => {
|
|
42
|
+
const prompt = formatFeedbackPrompt(req);
|
|
43
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
44
|
+
try {
|
|
45
|
+
const result = await Promise.race([
|
|
46
|
+
generateText({
|
|
47
|
+
model: options.model,
|
|
48
|
+
prompt,
|
|
49
|
+
// For non-claude-cli providers, `systemPrompt` carries the
|
|
50
|
+
// skill content; for claude-cli it's empty and the CLI
|
|
51
|
+
// auto-loads.
|
|
52
|
+
...(systemPrompt ? { system: systemPrompt } : {}),
|
|
53
|
+
}),
|
|
54
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('reemit timeout')), timeoutMs)),
|
|
55
|
+
]);
|
|
56
|
+
const extracted = extractTypeScriptBlock(result.text ?? '');
|
|
57
|
+
if (!extracted) {
|
|
58
|
+
if (attempt + 1 < maxAttempts)
|
|
59
|
+
continue;
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
return extracted;
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
if (attempt + 1 < maxAttempts)
|
|
66
|
+
continue;
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Format the feedback prompt body. Caller's LLM provider (e.g.
|
|
75
|
+
* claude-cli) loads the realize-emit skill as system context.
|
|
76
|
+
*
|
|
77
|
+
* Exported for tests + so callers can override the prompt shape.
|
|
78
|
+
*/
|
|
79
|
+
export function formatFeedbackPrompt(req) {
|
|
80
|
+
const errorLines = req.errors
|
|
81
|
+
.map(formatError)
|
|
82
|
+
.join('\n');
|
|
83
|
+
return [
|
|
84
|
+
`Re-emit the entire contents of \`${req.file}\`.`,
|
|
85
|
+
'',
|
|
86
|
+
`Your previous emission had the following compile / lint errors:`,
|
|
87
|
+
'',
|
|
88
|
+
errorLines,
|
|
89
|
+
'',
|
|
90
|
+
`Previous file content (re-emit this with the errors fixed):`,
|
|
91
|
+
'',
|
|
92
|
+
'```typescript',
|
|
93
|
+
req.previousContent,
|
|
94
|
+
'```',
|
|
95
|
+
'',
|
|
96
|
+
`Fix the errors and emit the ENTIRE updated file inside one \`\`\`typescript code block.`,
|
|
97
|
+
`Do not change any logic beyond what's needed to fix the errors.`,
|
|
98
|
+
].join('\n');
|
|
99
|
+
}
|
|
100
|
+
function formatError(e) {
|
|
101
|
+
const pos = e.line !== undefined
|
|
102
|
+
? `:${e.line}${e.col !== undefined ? `:${e.col}` : ''}`
|
|
103
|
+
: '';
|
|
104
|
+
return ` ${e.file}${pos} [${e.code}] ${e.message}`;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Pull the TypeScript code out of the LLM response. Matches the
|
|
108
|
+
* canonical ```typescript ... ``` block; falls back to ``` ... ``` if
|
|
109
|
+
* the LLM omitted the language tag.
|
|
110
|
+
*/
|
|
111
|
+
function extractTypeScriptBlock(text) {
|
|
112
|
+
if (!text)
|
|
113
|
+
return null;
|
|
114
|
+
const tsMatch = /```typescript\s*\n([\s\S]*?)\n```/.exec(text);
|
|
115
|
+
if (tsMatch)
|
|
116
|
+
return tsMatch[1];
|
|
117
|
+
const anyMatch = /```\s*\n([\s\S]*?)\n```/.exec(text);
|
|
118
|
+
if (anyMatch)
|
|
119
|
+
return anyMatch[1];
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=reemit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reemit.js","sourceRoot":"","sources":["../../../src/realize/post-emit-verify/reemit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAElC,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AA4B/D;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,OAA2B;IACxD,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,CAAC,CAAC;IAC7C,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,CAAC,GAAG,MAAM,CAAC;IAClD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,cAAc,CAAC,CAAC;IAElD,sEAAsE;IACtE,qEAAqE;IACrE,mEAAmE;IACnE,kCAAkC;IAClC,MAAM,UAAU,GAAG,iBAAiB,EAAE,CAAC;IACvC,MAAM,YAAY,GAAG,wBAAwB,CAAC,EAAE,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;IAEtE,OAAO,KAAK,EAAE,GAAkB,EAA0B,EAAE;QAC1D,MAAM,MAAM,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC;QAEzC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;YACvD,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;oBAChC,YAAY,CAAC;wBACX,KAAK,EAAE,OAAO,CAAC,KAAK;wBACpB,MAAM;wBACN,2DAA2D;wBAC3D,uDAAuD;wBACvD,cAAc;wBACd,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;qBAClD,CAAC;oBACF,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAC/B,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC,EAAE,SAAS,CAAC,CACjE;iBACF,CAAC,CAAC;gBAEH,MAAM,SAAS,GAAG,sBAAsB,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;gBAC5D,IAAI,CAAC,SAAS,EAAE,CAAC;oBACf,IAAI,OAAO,GAAG,CAAC,GAAG,WAAW;wBAAE,SAAS;oBACxC,OAAO,IAAI,CAAC;gBACd,CAAC;gBACD,OAAO,SAAS,CAAC;YACnB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,OAAO,GAAG,CAAC,GAAG,WAAW;oBAAE,SAAS;gBACxC,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAkB;IACrD,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM;SAC1B,GAAG,CAAC,WAAW,CAAC;SAChB,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO;QACL,oCAAoC,GAAG,CAAC,IAAI,KAAK;QACjD,EAAE;QACF,iEAAiE;QACjE,EAAE;QACF,UAAU;QACV,EAAE;QACF,6DAA6D;QAC7D,EAAE;QACF,eAAe;QACf,GAAG,CAAC,eAAe;QACnB,KAAK;QACL,EAAE;QACF,yFAAyF;QACzF,iEAAiE;KAClE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,SAAS,WAAW,CAAC,CAAc;IACjC,MAAM,GAAG,GAAG,CAAC,CAAC,IAAI,KAAK,SAAS;QAC9B,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;QACvD,CAAC,CAAC,EAAE,CAAC;IACP,OAAO,KAAK,CAAC,CAAC,IAAI,GAAG,GAAG,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;AACtD,CAAC;AAED;;;;GAIG;AACH,SAAS,sBAAsB,CAAC,IAAY;IAC1C,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,MAAM,OAAO,GAAG,mCAAmC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/D,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC,CAAC,CAAE,CAAC;IAChC,MAAM,QAAQ,GAAG,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtD,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC,CAAC,CAAE,CAAC;IAClC,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Post-emit verification framework — generic interface.
|
|
3
|
+
*
|
|
4
|
+
* SpecVerse realize emits one or more LLM-authored source files per
|
|
5
|
+
* owner (e.g. `backend/src/behaviors/<Owner>.ai.ts` for the TypeScript
|
|
6
|
+
* stack). LLM emissions sometimes carry compile errors / lint issues
|
|
7
|
+
* that don't show up at per-rule validation time (the realize-rules
|
|
8
|
+
* manifest catches forbidden APIs, missing `.js` extensions, etc., but
|
|
9
|
+
* not type mismatches or runtime semantics).
|
|
10
|
+
*
|
|
11
|
+
* A `PostEmitVerifier` runs AFTER all owners have emitted, on the
|
|
12
|
+
* fully-realized output tree. It produces structured errors that the
|
|
13
|
+
* feedback runner uses to drive a per-file re-emit pass.
|
|
14
|
+
*
|
|
15
|
+
* The interface is intentionally language-agnostic. The first
|
|
16
|
+
* implementation is `tsc` for TypeScript stacks; future verifiers can
|
|
17
|
+
* target Python (`mypy`/`ruff`), Go (`go vet`), Java (`mvn compile`),
|
|
18
|
+
* Rust (`cargo check`), etc. Each verifier knows whether it `applies`
|
|
19
|
+
* given the realized target (the manifest's stack tells you).
|
|
20
|
+
*
|
|
21
|
+
* Empirical motivation: 2026-05-13 idle-meta realize at engines 6.50.1
|
|
22
|
+
* produced 48 backend tsc errors. Two hand-crafted feedback prompts
|
|
23
|
+
* (AuthController.ai.ts + FormatNumberService.ai.ts) reduced this to
|
|
24
|
+
* 37 errors with zero regressions. Extrapolation: a full feedback
|
|
25
|
+
* sweep brings idle-meta from ~48 → ~5-10 errors per realize.
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* Single error reported by a verifier. Position-optional (some
|
|
29
|
+
* verifiers — Go vet, formatters — emit errors without precise
|
|
30
|
+
* line/col).
|
|
31
|
+
*/
|
|
32
|
+
export interface VerifyError {
|
|
33
|
+
/** Path to the source file, relative to the realized output dir. */
|
|
34
|
+
file: string;
|
|
35
|
+
line?: number;
|
|
36
|
+
col?: number;
|
|
37
|
+
/** Verifier-specific code, e.g. "TS2554", "py:E501", "go:S1000". */
|
|
38
|
+
code: string;
|
|
39
|
+
/** Human-readable description, suitable for inclusion in a feedback
|
|
40
|
+
* prompt. Verifier-specific; the framework doesn't interpret it. */
|
|
41
|
+
message: string;
|
|
42
|
+
severity: 'error' | 'warning';
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Result of running a single verifier on the realized output.
|
|
46
|
+
*/
|
|
47
|
+
export interface VerifyResult {
|
|
48
|
+
/** Verifier id (matches `PostEmitVerifier.id`). */
|
|
49
|
+
verifierId: string;
|
|
50
|
+
/** Wall-time. Useful for tuning verifier order + budget. */
|
|
51
|
+
durationMs: number;
|
|
52
|
+
/** Whether the verifier ran successfully. False indicates a tool-level
|
|
53
|
+
* failure (tsc not installed, npm install needed first, etc.) — the
|
|
54
|
+
* feedback runner SKIPS the verifier when not ok, not when errors > 0. */
|
|
55
|
+
ok: boolean;
|
|
56
|
+
errors: VerifyError[];
|
|
57
|
+
/** Non-fatal warnings from the verifier itself (separate from
|
|
58
|
+
* per-error severity), e.g. "tsc emitted 200 errors but we truncated
|
|
59
|
+
* to 100". */
|
|
60
|
+
notes: string[];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Context passed to verifiers — describes the realize target so each
|
|
64
|
+
* verifier can decide whether it `applies`.
|
|
65
|
+
*
|
|
66
|
+
* Minimal for now; extends as new verifiers need additional signal.
|
|
67
|
+
*/
|
|
68
|
+
export interface VerifyContext {
|
|
69
|
+
/** Absolute path to the realized output root (the dir that contains
|
|
70
|
+
* e.g. `backend/`, `frontend/`, `tests/`). */
|
|
71
|
+
outputDir: string;
|
|
72
|
+
/** Realize target tag, e.g. 'typescript', 'python', 'go'. Set by the
|
|
73
|
+
* realize pipeline based on the manifest's primary stack. */
|
|
74
|
+
targetLanguage?: string;
|
|
75
|
+
/** Optional: explicit subpath within outputDir to verify. tsc would
|
|
76
|
+
* typically scope to `backend/`. Defaults to outputDir itself. */
|
|
77
|
+
subpath?: string;
|
|
78
|
+
/**
|
|
79
|
+
* When true (the default), verifiers MAY run dependency installation
|
|
80
|
+
* (`npm install` / `pip install` / etc.) before running their actual
|
|
81
|
+
* checks. Required for the in-pipeline `spv realize` invocation:
|
|
82
|
+
* realize emits a scaffolded project but doesn't install deps, so
|
|
83
|
+
* tsc/mypy/etc. wouldn't otherwise be available.
|
|
84
|
+
*
|
|
85
|
+
* Set to false in test harnesses or when the caller has already
|
|
86
|
+
* installed deps. Each verifier decides whether to honour the hint —
|
|
87
|
+
* a stub-only verifier might ignore it.
|
|
88
|
+
*/
|
|
89
|
+
autoInstall?: boolean;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* A verifier runs a quality check on the realized output. Each
|
|
93
|
+
* verifier is independent — the framework collects results from
|
|
94
|
+
* applicable verifiers, then the feedback runner decides what to do.
|
|
95
|
+
*
|
|
96
|
+
* Implementations must NOT throw on tool-level failures; return
|
|
97
|
+
* `{ ok: false, errors: [], notes: ['…'] }` instead. The framework
|
|
98
|
+
* surfaces notes to the user; throws crash the realize pipeline.
|
|
99
|
+
*/
|
|
100
|
+
export interface PostEmitVerifier {
|
|
101
|
+
/** Stable identifier — kebab-case. e.g. 'tsc', 'mypy', 'go-vet'. */
|
|
102
|
+
id: string;
|
|
103
|
+
/** Human-readable name for logs and audit. */
|
|
104
|
+
name: string;
|
|
105
|
+
/**
|
|
106
|
+
* Whether this verifier runs unless explicitly disabled. Mature
|
|
107
|
+
* stable verifiers ship `enabledByDefault: true` (tsc); experimental
|
|
108
|
+
* or third-party verifiers ship `false` to require an opt-in via
|
|
109
|
+
* `SPECVERSE_REALIZE_POST_VERIFY_ENABLE=<id>,<id>` env (future).
|
|
110
|
+
*
|
|
111
|
+
* Optional for backwards compatibility — missing field is treated
|
|
112
|
+
* as `true` (the original behavior).
|
|
113
|
+
*/
|
|
114
|
+
enabledByDefault?: boolean;
|
|
115
|
+
/** Predicate: does this verifier apply to the given context? */
|
|
116
|
+
applies: (ctx: VerifyContext) => boolean;
|
|
117
|
+
/** Run the verifier. MUST NOT throw on tool failure (return ok:false). */
|
|
118
|
+
verify: (ctx: VerifyContext) => Promise<VerifyResult>;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* After all verifiers have run, the feedback runner needs to map
|
|
122
|
+
* each errored file to its owning spec entity (e.g.
|
|
123
|
+
* `backend/src/behaviors/AuthController.ai.ts` → ownerName=AuthController).
|
|
124
|
+
*
|
|
125
|
+
* Each verifier MAY contribute a mapping hint via `mapFileToOwner`,
|
|
126
|
+
* but the framework falls back to a generic regex
|
|
127
|
+
* (`behaviors/(.+)\\.ai\\.ts$` for TypeScript). Verifiers for other
|
|
128
|
+
* stacks would override the mapping.
|
|
129
|
+
*/
|
|
130
|
+
export interface OwnerMapping {
|
|
131
|
+
/** Source file path (relative to outputDir). */
|
|
132
|
+
file: string;
|
|
133
|
+
/** Inferred owner name. null if the file can't be mapped (e.g.
|
|
134
|
+
* shared infrastructure files generated by the framework, not by
|
|
135
|
+
* per-owner LLM emit — those should NOT receive feedback re-emits). */
|
|
136
|
+
ownerName: string | null;
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/realize/post-emit-verify/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;IACb;yEACqE;IACrE,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,mDAAmD;IACnD,UAAU,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,UAAU,EAAE,MAAM,CAAC;IACnB;;+EAE2E;IAC3E,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB;;mBAEe;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B;mDAC+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB;kEAC8D;IAC9D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;uEACmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,EAAE,EAAE,MAAM,CAAC;IACX,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;;OAQG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,gEAAgE;IAChE,OAAO,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC;IACzC,0EAA0E;IAC1E,MAAM,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;CACvD;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,YAAY;IAC3B,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb;;4EAEwE;IACxE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Post-emit verification framework — generic interface.
|
|
3
|
+
*
|
|
4
|
+
* SpecVerse realize emits one or more LLM-authored source files per
|
|
5
|
+
* owner (e.g. `backend/src/behaviors/<Owner>.ai.ts` for the TypeScript
|
|
6
|
+
* stack). LLM emissions sometimes carry compile errors / lint issues
|
|
7
|
+
* that don't show up at per-rule validation time (the realize-rules
|
|
8
|
+
* manifest catches forbidden APIs, missing `.js` extensions, etc., but
|
|
9
|
+
* not type mismatches or runtime semantics).
|
|
10
|
+
*
|
|
11
|
+
* A `PostEmitVerifier` runs AFTER all owners have emitted, on the
|
|
12
|
+
* fully-realized output tree. It produces structured errors that the
|
|
13
|
+
* feedback runner uses to drive a per-file re-emit pass.
|
|
14
|
+
*
|
|
15
|
+
* The interface is intentionally language-agnostic. The first
|
|
16
|
+
* implementation is `tsc` for TypeScript stacks; future verifiers can
|
|
17
|
+
* target Python (`mypy`/`ruff`), Go (`go vet`), Java (`mvn compile`),
|
|
18
|
+
* Rust (`cargo check`), etc. Each verifier knows whether it `applies`
|
|
19
|
+
* given the realized target (the manifest's stack tells you).
|
|
20
|
+
*
|
|
21
|
+
* Empirical motivation: 2026-05-13 idle-meta realize at engines 6.50.1
|
|
22
|
+
* produced 48 backend tsc errors. Two hand-crafted feedback prompts
|
|
23
|
+
* (AuthController.ai.ts + FormatNumberService.ai.ts) reduced this to
|
|
24
|
+
* 37 errors with zero regressions. Extrapolation: a full feedback
|
|
25
|
+
* sweep brings idle-meta from ~48 → ~5-10 errors per realize.
|
|
26
|
+
*/
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/realize/post-emit-verify/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Post-emit verifier manifest — single source of truth for which
|
|
3
|
+
* verifiers ship with the realize pipeline.
|
|
4
|
+
*
|
|
5
|
+
* Adding a new verifier: append to VERIFIERS, then it auto-runs on
|
|
6
|
+
* matching realize targets via `applies`.
|
|
7
|
+
*/
|
|
8
|
+
import type { PostEmitVerifier, VerifyContext, VerifyResult } from './types.js';
|
|
9
|
+
export declare const VERIFIERS: PostEmitVerifier[];
|
|
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 declare function runAllVerifiers(ctx: VerifyContext, enabledOverrides?: Set<string>): Promise<VerifyResult[]>;
|
|
28
|
+
export declare function findVerifier(id: string): PostEmitVerifier | undefined;
|
|
29
|
+
//# sourceMappingURL=verifier-manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verifier-manifest.d.ts","sourceRoot":"","sources":["../../../src/realize/post-emit-verify/verifier-manifest.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAGhF,eAAO,MAAM,SAAS,EAAE,gBAAgB,EAOvC,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,aAAa,EAClB,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAC7B,OAAO,CAAC,YAAY,EAAE,CAAC,CAqBzB;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAErE"}
|
|
@@ -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
|