@sublang/slc 0.9.0 → 0.10.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/README.md +43 -14
- package/dist/app.d.ts +2 -2
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +6 -4
- package/dist/app.js.map +1 -1
- package/dist/artifacts.d.ts +5 -1
- package/dist/artifacts.d.ts.map +1 -1
- package/dist/artifacts.js +19 -3
- package/dist/artifacts.js.map +1 -1
- package/dist/clarification.d.ts +32 -0
- package/dist/clarification.d.ts.map +1 -0
- package/dist/clarification.js +93 -0
- package/dist/clarification.js.map +1 -0
- package/dist/compiled-execution.js +1 -1
- package/dist/compiled-execution.js.map +1 -1
- package/dist/compiled-executor.d.ts.map +1 -1
- package/dist/compiled-executor.js +58 -22
- package/dist/compiled-executor.js.map +1 -1
- package/dist/config-file.d.ts.map +1 -1
- package/dist/config-file.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/entry-module.d.ts.map +1 -1
- package/dist/entry-module.js +16 -7
- package/dist/entry-module.js.map +1 -1
- package/dist/entry-options.d.ts +13 -0
- package/dist/entry-options.d.ts.map +1 -0
- package/dist/entry-options.js +116 -0
- package/dist/entry-options.js.map +1 -0
- package/dist/execution.d.ts +21 -4
- package/dist/execution.d.ts.map +1 -1
- package/dist/execution.js +58 -19
- package/dist/execution.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/interpreter.d.ts.map +1 -1
- package/dist/interpreter.js +20 -1
- package/dist/interpreter.js.map +1 -1
- package/dist/invocation.d.ts.map +1 -1
- package/dist/link.d.ts.map +1 -1
- package/dist/phase-runner.d.ts +1 -4
- package/dist/phase-runner.d.ts.map +1 -1
- package/dist/phase-runner.js +4 -1
- package/dist/phase-runner.js.map +1 -1
- package/dist/phase.d.ts.map +1 -1
- package/dist/phase.js.map +1 -1
- package/dist/pin-currency.js.map +1 -1
- package/dist/pin-generate.js.map +1 -1
- package/dist/pin-inputs.js.map +1 -1
- package/dist/pins.d.ts.map +1 -1
- package/dist/pins.js.map +1 -1
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/playbook-contract.d.ts.map +1 -1
- package/dist/playbook-contract.js.map +1 -1
- package/dist/playbook-ports.d.ts +9 -1
- package/dist/playbook-ports.d.ts.map +1 -1
- package/dist/playbook-ports.js +24 -6
- package/dist/playbook-ports.js.map +1 -1
- package/dist/progress.js.map +1 -1
- package/dist/resolver.js.map +1 -1
- package/dist/reviewing-agent.d.ts +2 -2
- package/dist/reviewing-agent.d.ts.map +1 -1
- package/dist/reviewing-agent.js +34 -12
- package/dist/reviewing-agent.js.map +1 -1
- package/dist/runner.d.ts +4 -2
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +361 -45
- package/dist/runner.js.map +1 -1
- package/dist/slc.config.template.yaml +1 -1
- package/dist/verify-coverage.d.ts +7 -0
- package/dist/verify-coverage.d.ts.map +1 -1
- package/dist/verify-coverage.js +805 -254
- package/dist/verify-coverage.js.map +1 -1
- package/dist/verify-source.d.ts.map +1 -1
- package/dist/verify-source.js +61 -20
- package/dist/verify-source.js.map +1 -1
- package/dist/verify-support.js.map +1 -1
- package/dist/verify-typescript.d.ts +3 -0
- package/dist/verify-typescript.d.ts.map +1 -0
- package/dist/verify-typescript.js +87 -0
- package/dist/verify-typescript.js.map +1 -0
- package/dist/verify.d.ts +28 -10
- package/dist/verify.d.ts.map +1 -1
- package/dist/verify.js +580 -126
- package/dist/verify.js.map +1 -1
- package/package.json +17 -16
package/dist/runner.js
CHANGED
|
@@ -13,11 +13,12 @@ import { mkdir, readFile, stat } from 'node:fs/promises';
|
|
|
13
13
|
import { dirname, join, relative, resolve, sep } from 'node:path';
|
|
14
14
|
import { fileURLToPath } from 'node:url';
|
|
15
15
|
import { artifactDir, planArtifacts, parseSource } from './artifacts.js';
|
|
16
|
+
import { formatClarificationReport, } from './clarification.js';
|
|
16
17
|
import { encodeLocator, invalidateBuildHistory, loadBuildHistory, recordBuild, verifiedInput, } from './build-history.js';
|
|
17
18
|
import { emitEntryModule } from './entry-module.js';
|
|
18
19
|
import { reconcileLinkObjectImportSpecifiers, unresolvableRelativeImports, } from './emitted-imports.js';
|
|
19
20
|
import { messageOf } from './errors.js';
|
|
20
|
-
import { assertSafeTarget, formatFailureReport, pathsAlias, runPhase, } from './execution.js';
|
|
21
|
+
import { assertSafeTarget, formatFailureReport, phaseProtectedPaths, pathsAlias, runPhase, watchProtectedPaths, } from './execution.js';
|
|
21
22
|
import { compareUtf8, hashBytes, isHash } from './hash.js';
|
|
22
23
|
import { parseInvocation } from './invocation.js';
|
|
23
24
|
import { unifiedLineDiff } from './line-diff.js';
|
|
@@ -30,9 +31,11 @@ import { PINS_FILE, PinError, loadPinFile, } from './pins.js';
|
|
|
30
31
|
import { loadPipeline, resolvePipeline, } from './pipeline.js';
|
|
31
32
|
import { RESERVED_SLC_PIPELINE, defaultPlaybookLinkTarget, isReservedPipeline, } from './resolver.js';
|
|
32
33
|
import { readPlaybookRuntimeDeclaration, } from './runtime-contract.js';
|
|
33
|
-
import { artifactSchemaForPlaybookProvenance, checkLinkedModuleContract, emitFsmCoverageTest, emitFsmIntrospectionTest, emitGearsFsmConformanceTest, emitPromptContractTest, findMachineConfig, loadFsmModule, loadLinkedModuleForVerification, playbookProvenanceForLinkTarget, resolveArtifactSchemaForVerification, } from './verify.js';
|
|
34
|
+
import { artifactSchemaForPlaybookProvenance, checkFsmContinuationInputs, checkFsmChildSuspension, checkFsmCoverage, checkGearsActorContract, checkGearsFsmConformance, checkGearsResultContract, checkLinkedModuleContract, emitFsmCoverageTest, FsmCoverageDeadlineError, emitFsmIntrospectionTest, emitGearsFsmConformanceTest, emitPromptContractTest, findConcurrentRoleSets, inspectGearsRoleContract, findMachineConfig, loadFsmModule, loadLinkedModuleForVerification, playbookProvenanceForLinkTarget, resolveArtifactSchemaForVerification, } from './verify.js';
|
|
34
35
|
import { VERIFIER_SUPPORT_MODULE, emitVerifierSupport, verifierSupportFiles, } from './verify-support.js';
|
|
35
36
|
import { checkSourceGearsContract } from './verify-source.js';
|
|
37
|
+
import { checkFsmTypeScript } from './verify-typescript.js';
|
|
38
|
+
import { checkEntryOptions, ENTRY_OPTIONS_CONTRACT } from './entry-options.js';
|
|
36
39
|
/**
|
|
37
40
|
* Parses argv and runs the requested pipeline, phase, or link end-to-end.
|
|
38
41
|
* Never rejects: malformed invocations, refusals, and phase failures all return
|
|
@@ -46,6 +49,24 @@ export async function runSlc(argv, deps) {
|
|
|
46
49
|
catch (error) {
|
|
47
50
|
return failure(messageOf(error));
|
|
48
51
|
}
|
|
52
|
+
const finish = (result) => {
|
|
53
|
+
if (result.clarification === undefined)
|
|
54
|
+
return result;
|
|
55
|
+
const clarification = {
|
|
56
|
+
...result.clarification,
|
|
57
|
+
sources: (invocation.kind === 'link'
|
|
58
|
+
? invocation.objects
|
|
59
|
+
: [invocation.source]).map((path) => resolve(runCwd(deps), path)),
|
|
60
|
+
};
|
|
61
|
+
return {
|
|
62
|
+
...result,
|
|
63
|
+
clarification,
|
|
64
|
+
diagnostics: [
|
|
65
|
+
...result.diagnostics,
|
|
66
|
+
formatClarificationReport(clarification),
|
|
67
|
+
],
|
|
68
|
+
};
|
|
69
|
+
};
|
|
49
70
|
try {
|
|
50
71
|
switch (invocation.kind) {
|
|
51
72
|
case 'full':
|
|
@@ -53,20 +74,20 @@ export async function runSlc(argv, deps) {
|
|
|
53
74
|
// (self-hosting-13): a bare full run becomes a full-link against the
|
|
54
75
|
// installed @sublang/playbook runtime contract module (DR-014).
|
|
55
76
|
if (invocation.pipeline === 'playbook') {
|
|
56
|
-
return await runFullLink({
|
|
77
|
+
return finish(await runFullLink({
|
|
57
78
|
...invocation,
|
|
58
79
|
kind: 'full-link',
|
|
59
80
|
linkTarget: defaultPlaybookLinkTarget(),
|
|
60
81
|
options: [],
|
|
61
|
-
}, deps);
|
|
82
|
+
}, deps));
|
|
62
83
|
}
|
|
63
|
-
return await runFull(invocation, deps);
|
|
84
|
+
return finish(await runFull(invocation, deps));
|
|
64
85
|
case 'phase':
|
|
65
|
-
return await runSinglePhase(invocation, deps);
|
|
86
|
+
return finish(await runSinglePhase(invocation, deps));
|
|
66
87
|
case 'link':
|
|
67
|
-
return await runDirectLink(invocation, deps);
|
|
88
|
+
return finish(await runDirectLink(invocation, deps));
|
|
68
89
|
case 'full-link':
|
|
69
|
-
return await runFullLink(invocation, deps);
|
|
90
|
+
return finish(await runFullLink(invocation, deps));
|
|
70
91
|
}
|
|
71
92
|
}
|
|
72
93
|
catch (error) {
|
|
@@ -101,6 +122,7 @@ async function runFull(invocation, deps) {
|
|
|
101
122
|
optimize: !invocation.noOptimize,
|
|
102
123
|
normalize: invocation.normalize || raw,
|
|
103
124
|
sourceFidelity: invocation.pipeline !== RESERVED_SLC_PIPELINE,
|
|
125
|
+
signal: deps.signal,
|
|
104
126
|
});
|
|
105
127
|
const verification = {
|
|
106
128
|
pipeline: invocation.pipeline,
|
|
@@ -139,6 +161,7 @@ async function runSinglePhase(invocation, deps) {
|
|
|
139
161
|
sourceFormat: phase.source.format,
|
|
140
162
|
ext: phase.source.ext,
|
|
141
163
|
entry: pipeline.phases[0] === phase,
|
|
164
|
+
staged: phase.pass,
|
|
142
165
|
});
|
|
143
166
|
if (raw) {
|
|
144
167
|
// A named phase cannot normalize (pipeline-37), so a raw entry source has no
|
|
@@ -153,7 +176,7 @@ async function runSinglePhase(invocation, deps) {
|
|
|
153
176
|
// `.opt` sibling unless `-o` relocates it (DR-013).
|
|
154
177
|
const target = (invocation.output === null ? null : resolve(cwd, invocation.output)) ??
|
|
155
178
|
join(artDir, `${basename}.${phase.target.format}.opt${phase.target.ext}`);
|
|
156
|
-
const step = compileStep(pipeline, phase, source, target, sourceFidelity);
|
|
179
|
+
const step = compileStep(pipeline, phase, source, target, sourceFidelity, undefined, deps.signal);
|
|
157
180
|
return executeSteps([step], pipeline, deps);
|
|
158
181
|
}
|
|
159
182
|
// Plan over the whole chain so the named phase keeps its pipeline role: a
|
|
@@ -166,7 +189,7 @@ async function runSinglePhase(invocation, deps) {
|
|
|
166
189
|
output: invocation.output === null ? undefined : resolve(cwd, invocation.output),
|
|
167
190
|
});
|
|
168
191
|
const artifact = plan[pipeline.phases.indexOf(phase)];
|
|
169
|
-
const step = compileStep(pipeline, phase, source, artifact.path, sourceFidelity);
|
|
192
|
+
const step = compileStep(pipeline, phase, source, artifact.path, sourceFidelity, undefined, deps.signal);
|
|
170
193
|
return executeSteps([step], pipeline, deps);
|
|
171
194
|
}
|
|
172
195
|
async function runDirectLink(invocation, deps) {
|
|
@@ -191,6 +214,7 @@ async function runDirectLink(invocation, deps) {
|
|
|
191
214
|
linkTarget: resolve(cwd, invocation.linkTarget),
|
|
192
215
|
options: invocation.options,
|
|
193
216
|
linked,
|
|
217
|
+
signal: deps.signal,
|
|
194
218
|
}),
|
|
195
219
|
], pipeline, deps);
|
|
196
220
|
}
|
|
@@ -220,6 +244,8 @@ async function runFullLink(invocation, deps) {
|
|
|
220
244
|
optimize: !invocation.noOptimize,
|
|
221
245
|
normalize,
|
|
222
246
|
sourceFidelity: invocation.pipeline !== RESERVED_SLC_PIPELINE,
|
|
247
|
+
signal: deps.signal,
|
|
248
|
+
linkTarget: resolve(cwd, invocation.linkTarget),
|
|
223
249
|
});
|
|
224
250
|
const linked = linkedArtifactPath({
|
|
225
251
|
kind: 'full',
|
|
@@ -228,6 +254,14 @@ async function runFullLink(invocation, deps) {
|
|
|
228
254
|
linked: link.target,
|
|
229
255
|
output: invocation.output === null ? null : resolve(cwd, invocation.output),
|
|
230
256
|
});
|
|
257
|
+
const gearsPlan = plan.find((artifact) => artifact.phase.target.format === 'gears');
|
|
258
|
+
const entryCandidate = invocation.pipeline === 'playbook' &&
|
|
259
|
+
invocation.output === null &&
|
|
260
|
+
gearsPlan !== undefined
|
|
261
|
+
? resolve(cwd, `${basename}.ts`)
|
|
262
|
+
: null;
|
|
263
|
+
const entryAliasesSource = entryCandidate !== null && (await pathsAlias(entryCandidate, source));
|
|
264
|
+
const entryPath = entryAliasesSource ? null : entryCandidate;
|
|
231
265
|
const steps = [
|
|
232
266
|
...compileSteps,
|
|
233
267
|
linkStep({
|
|
@@ -237,16 +271,12 @@ async function runFullLink(invocation, deps) {
|
|
|
237
271
|
linkTarget: resolve(cwd, invocation.linkTarget),
|
|
238
272
|
options: invocation.options,
|
|
239
273
|
linked,
|
|
274
|
+
signal: deps.signal,
|
|
275
|
+
...(entryPath !== null && gearsPlan !== undefined
|
|
276
|
+
? { entryGearsPath: gearsPlan.path }
|
|
277
|
+
: {}),
|
|
240
278
|
}),
|
|
241
279
|
];
|
|
242
|
-
const gearsPlan = plan.find((artifact) => artifact.phase.target.format === 'gears');
|
|
243
|
-
const entryCandidate = invocation.pipeline === 'playbook' &&
|
|
244
|
-
invocation.output === null &&
|
|
245
|
-
gearsPlan !== undefined
|
|
246
|
-
? resolve(cwd, `${basename}.ts`)
|
|
247
|
-
: null;
|
|
248
|
-
const entryAliasesSource = entryCandidate !== null && (await pathsAlias(entryCandidate, source));
|
|
249
|
-
const entryPath = entryAliasesSource ? null : entryCandidate;
|
|
250
280
|
const verification = {
|
|
251
281
|
pipeline: invocation.pipeline,
|
|
252
282
|
plan,
|
|
@@ -500,6 +530,9 @@ function buildCompileSteps(opts) {
|
|
|
500
530
|
if (opts.normalize) {
|
|
501
531
|
const entry = pipeline.phases[0];
|
|
502
532
|
const normalized = join(artDir, `${basename}.${entry.source.format}${entry.source.ext}`);
|
|
533
|
+
const gearsContract = entry.source.format === 'gears'
|
|
534
|
+
? () => gearsContractFindings(normalized)
|
|
535
|
+
: undefined;
|
|
503
536
|
steps.push({
|
|
504
537
|
request: {
|
|
505
538
|
kind: 'compile',
|
|
@@ -507,9 +540,15 @@ function buildCompileSteps(opts) {
|
|
|
507
540
|
source: previous,
|
|
508
541
|
target: normalized,
|
|
509
542
|
references: [phaseDefinition(pipeline, entry.name)],
|
|
543
|
+
...(gearsContract === undefined
|
|
544
|
+
? {}
|
|
545
|
+
: { mechanicalReview: gearsContract }),
|
|
510
546
|
},
|
|
511
547
|
phase: 'normalize',
|
|
512
548
|
targetExt: entry.source.ext,
|
|
549
|
+
...(gearsContract === undefined
|
|
550
|
+
? {}
|
|
551
|
+
: { compileFidelity: gearsContract }),
|
|
513
552
|
});
|
|
514
553
|
previous = normalized;
|
|
515
554
|
}
|
|
@@ -519,46 +558,92 @@ function buildCompileSteps(opts) {
|
|
|
519
558
|
? pipeline.passes.filter((pass) => pass.source.format === phase.target.format)
|
|
520
559
|
: [];
|
|
521
560
|
if (passes.length === 0) {
|
|
522
|
-
steps.push(compileStep(pipeline, phase, previous, artifact.path, opts.sourceFidelity));
|
|
561
|
+
steps.push(compileStep(pipeline, phase, previous, artifact.path, opts.sourceFidelity, opts.linkTarget, opts.signal));
|
|
523
562
|
previous = artifact.path;
|
|
524
563
|
continue;
|
|
525
564
|
}
|
|
526
565
|
const raw = join(artDir, `${basename}.${phase.target.format}.raw${phase.target.ext}`);
|
|
527
|
-
steps.push(compileStep(pipeline, phase, previous, raw, opts.sourceFidelity));
|
|
566
|
+
steps.push(compileStep(pipeline, phase, previous, raw, opts.sourceFidelity, opts.linkTarget, opts.signal));
|
|
528
567
|
previous = raw;
|
|
529
568
|
passes.forEach((pass, index) => {
|
|
530
569
|
const target = index === passes.length - 1
|
|
531
570
|
? artifact.path
|
|
532
571
|
: join(artDir, `${basename}.${phase.target.format}.opt${index + 1}${phase.target.ext}`);
|
|
533
|
-
steps.push(compileStep(pipeline, pass, previous, target, opts.sourceFidelity));
|
|
572
|
+
steps.push(compileStep(pipeline, pass, previous, target, opts.sourceFidelity, opts.linkTarget, opts.signal));
|
|
534
573
|
previous = target;
|
|
535
574
|
});
|
|
536
575
|
}
|
|
537
576
|
return steps;
|
|
538
577
|
}
|
|
539
|
-
function compileStep(pipeline, phase, source, target, sourceFidelity) {
|
|
578
|
+
function compileStep(pipeline, phase, source, target, sourceFidelity, linkTarget, signal) {
|
|
540
579
|
// Only a text-to-GEARS phase conserves authored Source fragments, and the
|
|
541
580
|
// reserved meta-pipeline compiles definitions under its own fidelity gate
|
|
542
581
|
// (DR-029, phase-execution-51, verification-23).
|
|
543
582
|
const gated = sourceFidelity &&
|
|
544
583
|
phase.source.format === 'text' &&
|
|
545
584
|
phase.target.format === 'gears';
|
|
585
|
+
const fsmConformance = phase.source.format === 'gears' && phase.target.format === 'fsm'
|
|
586
|
+
? async () => {
|
|
587
|
+
if (phase.target.ext === '.ts') {
|
|
588
|
+
const findings = await checkFsmTypeScript(target, signal);
|
|
589
|
+
if (findings.length > 0)
|
|
590
|
+
return findings;
|
|
591
|
+
}
|
|
592
|
+
return fsmConformanceFindings(source, target, linkTarget, signal);
|
|
593
|
+
}
|
|
594
|
+
: undefined;
|
|
595
|
+
const checks = [
|
|
596
|
+
...(gated ? [() => sourceFidelityFindings(source, target)] : []),
|
|
597
|
+
...(phase.target.format === 'gears'
|
|
598
|
+
? [() => gearsContractFindings(target)]
|
|
599
|
+
: []),
|
|
600
|
+
...(fsmConformance === undefined ? [] : [fsmConformance]),
|
|
601
|
+
];
|
|
602
|
+
const mechanicalReview = checks.length === 0
|
|
603
|
+
? undefined
|
|
604
|
+
: async () => (await Promise.all(checks.map((check) => check()))).flat();
|
|
546
605
|
return {
|
|
547
606
|
request: {
|
|
548
607
|
kind: 'compile',
|
|
549
608
|
definitionPath: phaseDefinition(pipeline, phase.name),
|
|
550
609
|
source,
|
|
551
610
|
target,
|
|
552
|
-
...(
|
|
553
|
-
? { mechanicalReview: () => sourceFidelityFindings(source, target) }
|
|
554
|
-
: {}),
|
|
611
|
+
...(mechanicalReview === undefined ? {} : { mechanicalReview }),
|
|
555
612
|
},
|
|
556
613
|
phase: phase.name,
|
|
557
614
|
pinKey: phase.name,
|
|
558
615
|
targetExt: phase.target.ext,
|
|
559
|
-
...(
|
|
616
|
+
...(mechanicalReview === undefined
|
|
617
|
+
? {}
|
|
618
|
+
: { compileFidelity: mechanicalReview }),
|
|
619
|
+
...(phase.source.format === 'gears'
|
|
620
|
+
? { gearsSourceContract: () => gearsContractFindings(source) }
|
|
621
|
+
: {}),
|
|
622
|
+
...(phase.source.format === 'fsm' && phase.source.ext === '.ts'
|
|
623
|
+
? { fsmSourceTypecheck: () => checkFsmTypeScript(source, signal) }
|
|
624
|
+
: {}),
|
|
625
|
+
...(phase.source.format === 'fsm'
|
|
626
|
+
? {
|
|
627
|
+
fsmSourceContinuation: () => fsmContinuationFindings(source, linkTarget, signal),
|
|
628
|
+
}
|
|
629
|
+
: {}),
|
|
560
630
|
};
|
|
561
631
|
}
|
|
632
|
+
/** Parser-only findings belong to the phase that can still edit the GEARS. */
|
|
633
|
+
async function gearsContractFindings(path) {
|
|
634
|
+
try {
|
|
635
|
+
const gears = await readFile(path, 'utf8');
|
|
636
|
+
return [
|
|
637
|
+
...checkGearsResultContract(gears),
|
|
638
|
+
...checkGearsActorContract(gears),
|
|
639
|
+
];
|
|
640
|
+
}
|
|
641
|
+
catch (error) {
|
|
642
|
+
if (error.code === 'ENOENT')
|
|
643
|
+
return [];
|
|
644
|
+
return [`GEARS contract could not be checked: ${messageOf(error)}`];
|
|
645
|
+
}
|
|
646
|
+
}
|
|
562
647
|
/**
|
|
563
648
|
* The DR-029 gate over one text-to-GEARS phase's live target: the deterministic
|
|
564
649
|
* conservation findings against the Source that phase reads (phase-execution-51,
|
|
@@ -587,6 +672,88 @@ async function sourceFidelityFindings(source, target) {
|
|
|
587
672
|
return [`Source fidelity could not be checked: ${messageOf(error)}`];
|
|
588
673
|
}
|
|
589
674
|
}
|
|
675
|
+
/** Reuses the emitted GEARS-to-FSM suite before accepting its input (DR-033). */
|
|
676
|
+
async function fsmConformanceFindings(source, target, linkTarget, signal) {
|
|
677
|
+
signal?.throwIfAborted();
|
|
678
|
+
try {
|
|
679
|
+
await stat(target);
|
|
680
|
+
}
|
|
681
|
+
catch (error) {
|
|
682
|
+
// Absence belongs to the generic target check; all other failures are
|
|
683
|
+
// actionable findings rather than permission to skip the gate.
|
|
684
|
+
if (error.code === 'ENOENT')
|
|
685
|
+
return [];
|
|
686
|
+
return [`FSM conformance could not be checked: ${messageOf(error)}`];
|
|
687
|
+
}
|
|
688
|
+
try {
|
|
689
|
+
const [gears, fsm] = await Promise.all([
|
|
690
|
+
readFile(source, 'utf8'),
|
|
691
|
+
loadFsmModule(target),
|
|
692
|
+
]);
|
|
693
|
+
const config = findMachineConfig(fsm);
|
|
694
|
+
const schema = await fsmSchema(config, linkTarget);
|
|
695
|
+
const findings = [
|
|
696
|
+
...schema.findings,
|
|
697
|
+
...checkGearsFsmConformance(gears, config, {
|
|
698
|
+
concurrentRoleSets: findConcurrentRoleSets(fsm),
|
|
699
|
+
...(schema.artifactSchema === undefined
|
|
700
|
+
? {}
|
|
701
|
+
: { artifactSchema: schema.artifactSchema }),
|
|
702
|
+
}),
|
|
703
|
+
...(schema.artifactSchema === undefined || schema.findings.length > 0
|
|
704
|
+
? []
|
|
705
|
+
: checkFsmContinuationInputs(config, schema.artifactSchema)),
|
|
706
|
+
];
|
|
707
|
+
signal?.throwIfAborted();
|
|
708
|
+
return findings.length > 0
|
|
709
|
+
? findings
|
|
710
|
+
: await checkFsmCoverage(fsm, {
|
|
711
|
+
sourceText: await readFile(target, 'utf8'),
|
|
712
|
+
signal,
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
catch (error) {
|
|
716
|
+
signal?.throwIfAborted();
|
|
717
|
+
if (error instanceof FsmCoverageDeadlineError)
|
|
718
|
+
throw error;
|
|
719
|
+
return [`FSM conformance could not be checked: ${messageOf(error)}`];
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
async function fsmSchema(config, linkTarget) {
|
|
723
|
+
const provenance = linkTarget === undefined
|
|
724
|
+
? undefined
|
|
725
|
+
: await playbookProvenanceForLinkTarget(linkTarget);
|
|
726
|
+
const runtimeDeclaration = linkTarget === undefined ||
|
|
727
|
+
artifactSchemaForPlaybookProvenance(provenance) !== undefined
|
|
728
|
+
? undefined
|
|
729
|
+
: await linkTargetRuntimeDeclaration(linkTarget);
|
|
730
|
+
return resolveArtifactSchemaForVerification({
|
|
731
|
+
requireContinuationSchema: false,
|
|
732
|
+
config,
|
|
733
|
+
...(provenance === undefined ? {} : { provenance }),
|
|
734
|
+
...(runtimeDeclaration === undefined ? {} : { runtimeDeclaration }),
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
async function fsmContinuationFindings(source, linkTarget, signal) {
|
|
738
|
+
signal?.throwIfAborted();
|
|
739
|
+
const fsm = await loadFsmModule(source);
|
|
740
|
+
const config = findMachineConfig(fsm);
|
|
741
|
+
const schema = await fsmSchema(config, linkTarget);
|
|
742
|
+
signal?.throwIfAborted();
|
|
743
|
+
const findings = [
|
|
744
|
+
...schema.findings,
|
|
745
|
+
...checkFsmChildSuspension(config),
|
|
746
|
+
...(schema.artifactSchema === undefined || schema.findings.length > 0
|
|
747
|
+
? []
|
|
748
|
+
: checkFsmContinuationInputs(config, schema.artifactSchema)),
|
|
749
|
+
];
|
|
750
|
+
return findings.length > 0
|
|
751
|
+
? findings
|
|
752
|
+
: await checkFsmCoverage(fsm, {
|
|
753
|
+
sourceText: await readFile(source, 'utf8'),
|
|
754
|
+
signal,
|
|
755
|
+
});
|
|
756
|
+
}
|
|
590
757
|
/**
|
|
591
758
|
* Builds one link step, gated by DR-030 when the link emits Playbook's
|
|
592
759
|
* `playbook` module over exactly the one FSM object the checks read
|
|
@@ -596,8 +763,20 @@ async function sourceFidelityFindings(source, target) {
|
|
|
596
763
|
function linkStep(opts) {
|
|
597
764
|
const { link } = opts;
|
|
598
765
|
const fsmObjects = opts.objects.filter((object) => object.endsWith(`.${link.source.format}${link.source.ext}`));
|
|
766
|
+
const entryRequired = async () => opts.entryGearsPath !== undefined &&
|
|
767
|
+
inspectGearsRoleContract(await readFile(opts.entryGearsPath, 'utf8'))
|
|
768
|
+
.generation === 'schema-3';
|
|
599
769
|
const gated = link.target.format === PLAYBOOK_LINKED_FORMAT && fsmObjects.length === 1
|
|
600
|
-
? () =>
|
|
770
|
+
? async () => [
|
|
771
|
+
...(await linkFidelityFindings(opts.linked, fsmObjects[0])),
|
|
772
|
+
...((await entryRequired())
|
|
773
|
+
? await checkEntryOptions({
|
|
774
|
+
linkedPath: opts.linked,
|
|
775
|
+
fsmPath: fsmObjects[0],
|
|
776
|
+
signal: opts.signal,
|
|
777
|
+
})
|
|
778
|
+
: []),
|
|
779
|
+
]
|
|
601
780
|
: undefined;
|
|
602
781
|
return {
|
|
603
782
|
request: {
|
|
@@ -609,10 +788,27 @@ function linkStep(opts) {
|
|
|
609
788
|
linked: opts.linked,
|
|
610
789
|
...(gated === undefined ? {} : { mechanicalReview: gated }),
|
|
611
790
|
},
|
|
791
|
+
...(gated !== undefined && opts.entryGearsPath !== undefined
|
|
792
|
+
? {
|
|
793
|
+
outputContract: async () => (await entryRequired()) ? ENTRY_OPTIONS_CONTRACT : undefined,
|
|
794
|
+
}
|
|
795
|
+
: {}),
|
|
612
796
|
phase: 'link',
|
|
613
797
|
pinKey: 'link',
|
|
614
798
|
targetExt: link.target.ext,
|
|
615
799
|
...(gated === undefined ? {} : { linkFidelity: gated }),
|
|
800
|
+
...(gated !== undefined &&
|
|
801
|
+
link.source.format === 'fsm' &&
|
|
802
|
+
link.source.ext === '.ts'
|
|
803
|
+
? {
|
|
804
|
+
fsmSourceTypecheck: () => checkFsmTypeScript(fsmObjects[0], opts.signal),
|
|
805
|
+
}
|
|
806
|
+
: {}),
|
|
807
|
+
...(gated !== undefined && link.source.format === 'fsm'
|
|
808
|
+
? {
|
|
809
|
+
fsmSourceContinuation: () => fsmContinuationFindings(fsmObjects[0], opts.linkTarget, opts.signal),
|
|
810
|
+
}
|
|
811
|
+
: {}),
|
|
616
812
|
};
|
|
617
813
|
}
|
|
618
814
|
/**
|
|
@@ -736,7 +932,7 @@ async function executeSteps(steps, pipeline, deps, opts = {}) {
|
|
|
736
932
|
: [
|
|
737
933
|
...diagnostics,
|
|
738
934
|
...state.accepted.map((accepted) => `accepted phase "${accepted.step.phase}" left its live target at ${accepted.target}`),
|
|
739
|
-
`resume from the last accepted target with: ${resumeCommand(state.pipelineName, steps[failedIndex], state.accepted[state.accepted.length - 1].target)}`,
|
|
935
|
+
`resume from the last accepted target with: ${resumeCommand(state.pipelineName, steps[failedIndex], state.accepted[state.accepted.length - 1].target, pipeline.passes)}`,
|
|
740
936
|
],
|
|
741
937
|
});
|
|
742
938
|
for (let index = 0; index < steps.length; index++) {
|
|
@@ -796,6 +992,71 @@ async function executeSteps(steps, pipeline, deps, opts = {}) {
|
|
|
796
992
|
target,
|
|
797
993
|
elapsedMs: Date.now() - startedAt,
|
|
798
994
|
});
|
|
995
|
+
let request = mode.mode === 'update' && step.request.kind === 'compile'
|
|
996
|
+
? {
|
|
997
|
+
...step.request,
|
|
998
|
+
update: { priorInput: mode.priorInput, diff: mode.diff },
|
|
999
|
+
}
|
|
1000
|
+
: step.request;
|
|
1001
|
+
if (request.kind === 'link' && step.outputContract !== undefined) {
|
|
1002
|
+
const outputContract = await step.outputContract();
|
|
1003
|
+
if (outputContract !== undefined)
|
|
1004
|
+
request = { ...request, outputContract };
|
|
1005
|
+
}
|
|
1006
|
+
// The consumer cannot repair its protected source. Reject these existing
|
|
1007
|
+
// parser findings before selecting or constructing any execution strategy.
|
|
1008
|
+
if (step.gearsSourceContract !== undefined) {
|
|
1009
|
+
const findings = await step.gearsSourceContract();
|
|
1010
|
+
if (findings.length > 0 && step.request.kind === 'compile') {
|
|
1011
|
+
fail();
|
|
1012
|
+
diagnostics.push(formatFailureReport({
|
|
1013
|
+
phase: step.phase,
|
|
1014
|
+
target,
|
|
1015
|
+
reasons: [
|
|
1016
|
+
`invalid GEARS source: ${step.request.source}`,
|
|
1017
|
+
...findings,
|
|
1018
|
+
],
|
|
1019
|
+
}));
|
|
1020
|
+
return stopped(index);
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
for (const [check, name] of [
|
|
1024
|
+
[step.fsmSourceTypecheck, 'TypeScript'],
|
|
1025
|
+
[step.fsmSourceContinuation, 'continuation'],
|
|
1026
|
+
]) {
|
|
1027
|
+
if (check === undefined)
|
|
1028
|
+
continue;
|
|
1029
|
+
let reasons = [];
|
|
1030
|
+
let checkProtectedPaths;
|
|
1031
|
+
try {
|
|
1032
|
+
if (name === 'continuation') {
|
|
1033
|
+
checkProtectedPaths = await watchProtectedPaths(phaseProtectedPaths(request, {
|
|
1034
|
+
definitions,
|
|
1035
|
+
protectedInputs: [...immutableInputs, ...stepDeclared.paths],
|
|
1036
|
+
}));
|
|
1037
|
+
}
|
|
1038
|
+
const findings = await check();
|
|
1039
|
+
if (findings.length > 0)
|
|
1040
|
+
reasons = ['invalid FSM source', ...findings];
|
|
1041
|
+
}
|
|
1042
|
+
catch (error) {
|
|
1043
|
+
reasons = [`FSM ${name} check could not run: ${messageOf(error)}`];
|
|
1044
|
+
}
|
|
1045
|
+
// Module imports can execute code even when the later probe fails or
|
|
1046
|
+
// throws. Their mutations outrank every checker outcome, before selection.
|
|
1047
|
+
const changes = await checkProtectedPaths?.();
|
|
1048
|
+
if (changes !== undefined && changes.length > 0)
|
|
1049
|
+
reasons = changes;
|
|
1050
|
+
if (reasons.length > 0) {
|
|
1051
|
+
fail();
|
|
1052
|
+
diagnostics.push(formatFailureReport({
|
|
1053
|
+
phase: step.phase,
|
|
1054
|
+
target,
|
|
1055
|
+
reasons,
|
|
1056
|
+
}));
|
|
1057
|
+
return stopped(index);
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
799
1060
|
// Selecting a compiled executor can throw rather than return a verdict —
|
|
800
1061
|
// notably a pinned link target whose installed engine declares no
|
|
801
1062
|
// supported contract, which the host factory rejects (phase-execution-30).
|
|
@@ -825,19 +1086,15 @@ async function executeSteps(steps, pipeline, deps, opts = {}) {
|
|
|
825
1086
|
text: `updating ${step.phase} → ${target}`,
|
|
826
1087
|
});
|
|
827
1088
|
}
|
|
828
|
-
const request = mode.mode === 'update' && step.request.kind === 'compile'
|
|
829
|
-
? {
|
|
830
|
-
...step.request,
|
|
831
|
-
update: { priorInput: mode.priorInput, diff: mode.diff },
|
|
832
|
-
}
|
|
833
|
-
: step.request;
|
|
834
1089
|
const result = await runPhase({
|
|
835
1090
|
request,
|
|
836
1091
|
phase: step.phase,
|
|
837
1092
|
targetExt: step.targetExt,
|
|
838
1093
|
executor: selection.executor,
|
|
839
1094
|
definitions,
|
|
840
|
-
|
|
1095
|
+
// Original invocation inputs remain immutable even when a later phase
|
|
1096
|
+
// reads an intermediate and asks the author to clarify that original.
|
|
1097
|
+
protectedInputs: [...immutableInputs, ...stepDeclared.paths],
|
|
841
1098
|
aliasInputs: [
|
|
842
1099
|
...immutableInputs,
|
|
843
1100
|
...declaredInputs,
|
|
@@ -851,16 +1108,45 @@ async function executeSteps(steps, pipeline, deps, opts = {}) {
|
|
|
851
1108
|
});
|
|
852
1109
|
if (!result.ok) {
|
|
853
1110
|
fail();
|
|
1111
|
+
if (result.clarification !== undefined) {
|
|
1112
|
+
return {
|
|
1113
|
+
ok: false,
|
|
1114
|
+
outputs,
|
|
1115
|
+
diagnostics,
|
|
1116
|
+
outcome: 'clarification-required',
|
|
1117
|
+
clarification: {
|
|
1118
|
+
schema: 'sublang.slc.clarification.v1',
|
|
1119
|
+
phase: step.phase,
|
|
1120
|
+
target,
|
|
1121
|
+
sources: [],
|
|
1122
|
+
questions: result.clarification,
|
|
1123
|
+
},
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
854
1126
|
diagnostics.push(formatFailureReport(result.report));
|
|
855
1127
|
return stopped(index);
|
|
856
1128
|
}
|
|
857
1129
|
diagnostics.push(...result.diagnostics);
|
|
858
|
-
//
|
|
859
|
-
//
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
1130
|
+
// Recheck even custom executors that do not consume mechanicalReview.
|
|
1131
|
+
// runPhase has already enforced the unchanged generic protection checks.
|
|
1132
|
+
if (step.compileFidelity !== undefined) {
|
|
1133
|
+
let findings;
|
|
1134
|
+
let checkProtectedPaths;
|
|
1135
|
+
try {
|
|
1136
|
+
checkProtectedPaths = await watchProtectedPaths(phaseProtectedPaths(request, {
|
|
1137
|
+
definitions,
|
|
1138
|
+
protectedInputs: [...immutableInputs, ...stepDeclared.paths],
|
|
1139
|
+
}));
|
|
1140
|
+
findings = await step.compileFidelity();
|
|
1141
|
+
}
|
|
1142
|
+
catch (error) {
|
|
1143
|
+
findings = [`mechanical review could not run: ${messageOf(error)}`];
|
|
1144
|
+
}
|
|
1145
|
+
// Coverage executes artifact guards/actions after generic acceptance.
|
|
1146
|
+
// Their protected-input mutations outrank success, findings and errors.
|
|
1147
|
+
const changes = await checkProtectedPaths?.();
|
|
1148
|
+
if (changes !== undefined && changes.length > 0)
|
|
1149
|
+
findings = changes;
|
|
864
1150
|
if (findings.length > 0) {
|
|
865
1151
|
fail();
|
|
866
1152
|
diagnostics.push(formatFailureReport({
|
|
@@ -967,10 +1253,40 @@ async function executeSteps(steps, pipeline, deps, opts = {}) {
|
|
|
967
1253
|
* the run stopped at, so a stopped link step resumes through the direct-link
|
|
968
1254
|
* form with that target as its object.
|
|
969
1255
|
*/
|
|
970
|
-
function resumeCommand(pipelineName, next, lastTarget) {
|
|
971
|
-
|
|
972
|
-
?
|
|
973
|
-
|
|
1256
|
+
function resumeCommand(pipelineName, next, lastTarget, passes) {
|
|
1257
|
+
const operands = next.request.kind === 'link'
|
|
1258
|
+
? [
|
|
1259
|
+
`${pipelineName}.link`,
|
|
1260
|
+
lastTarget,
|
|
1261
|
+
next.request.linkTarget,
|
|
1262
|
+
// The link phase's opaque options are part of the invocation
|
|
1263
|
+
// (pipeline-14); dropping them would resume a different link.
|
|
1264
|
+
...next.request.options.flatMap((option) => [
|
|
1265
|
+
'--link-option',
|
|
1266
|
+
`${option.name}=${option.value}`,
|
|
1267
|
+
]),
|
|
1268
|
+
]
|
|
1269
|
+
: [
|
|
1270
|
+
`${pipelineName}.${next.phase}`,
|
|
1271
|
+
lastTarget,
|
|
1272
|
+
// A standalone pass otherwise writes the `.opt` sibling (pipeline-33);
|
|
1273
|
+
// name the target the stopped run was writing so the resumed pass
|
|
1274
|
+
// lands where its downstream phases read.
|
|
1275
|
+
...(passes.some((pass) => pass.name === next.phase)
|
|
1276
|
+
? ['-o', stepTarget(next)]
|
|
1277
|
+
: []),
|
|
1278
|
+
];
|
|
1279
|
+
return ['slc', ...operands.map(shellOperand)].join(' ');
|
|
1280
|
+
}
|
|
1281
|
+
// An operand the shell would not read back verbatim — a path with a space, say
|
|
1282
|
+
// — must be quoted, or the reported line is a different invocation than the one
|
|
1283
|
+
// it resumes (pipeline-9, pipeline-12).
|
|
1284
|
+
const VERBATIM_OPERAND = /^[A-Za-z0-9_@%+=:,./-]+$/;
|
|
1285
|
+
/** One resume operand, POSIX-quoted only where the shell would reinterpret it. */
|
|
1286
|
+
function shellOperand(value) {
|
|
1287
|
+
return VERBATIM_OPERAND.test(value)
|
|
1288
|
+
? value
|
|
1289
|
+
: `'${value.replaceAll("'", `'\\''`)}'`;
|
|
974
1290
|
}
|
|
975
1291
|
function stepTarget(step) {
|
|
976
1292
|
return step.request.kind === 'compile'
|