@dailephd/my-dev-kit-lab 0.4.2 → 0.4.4
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 +28 -113
- package/benchmarks/contracts/todo-behavior.md +1 -1
- package/benchmarks/projects/task-analytics-large-mixed/README.md +2 -0
- package/benchmarks/projects/task-workflow-medium-ts/README.md +3 -1
- package/dist/src/evaluation/stageContextDeterminism/calculateStageContextDeterminism.js +52 -0
- package/dist/src/evaluation/stageContextDeterminism/canonicalizeStageContextRun.js +53 -0
- package/dist/src/evaluation/stageContextDeterminism/index.js +2 -0
- package/dist/src/evaluation/stageContextDeterminism/types.js +1 -0
- package/dist/src/evaluation/stageContextExpectations/index.js +2 -0
- package/dist/src/evaluation/stageContextExpectations/producerReadinessExpectationTypes.js +8 -0
- package/dist/src/evaluation/stageContextExpectations/readStageContextExpectationFixtureV1.js +50 -0
- package/dist/src/evaluation/stageContextExpectations/readTypes.js +1 -0
- package/dist/src/evaluation/stageContextExpectations/types.js +1 -0
- package/dist/src/evaluation/stageContextExpectations/validation.js +768 -0
- package/dist/src/evaluation/stageContextMetrics/calculateAllocationMetrics.js +96 -0
- package/dist/src/evaluation/stageContextMetrics/calculateArtifactStateMetrics.js +130 -0
- package/dist/src/evaluation/stageContextMetrics/calculateContextSizeMetrics.js +85 -0
- package/dist/src/evaluation/stageContextMetrics/calculateCriticalityMetrics.js +112 -0
- package/dist/src/evaluation/stageContextMetrics/calculateExpectationMetrics.js +103 -0
- package/dist/src/evaluation/stageContextMetrics/calculateOwnerMetrics.js +84 -0
- package/dist/src/evaluation/stageContextMetrics/calculateReadinessAgreement.js +152 -0
- package/dist/src/evaluation/stageContextMetrics/calculateResponsibilityMappingMetrics.js +36 -0
- package/dist/src/evaluation/stageContextMetrics/calculateSupplementalRawAgreement.js +50 -0
- package/dist/src/evaluation/stageContextMetrics/calculateTruncationClassification.js +44 -0
- package/dist/src/evaluation/stageContextMetrics/collectContextCapsuleEvidence.js +312 -0
- package/dist/src/evaluation/stageContextMetrics/collectRetrievalAuditEvidence.js +35 -0
- package/dist/src/evaluation/stageContextMetrics/collectWorkflowInstructionEvidence.js +137 -0
- package/dist/src/evaluation/stageContextMetrics/evaluateProducerReadinessBridge.js +127 -0
- package/dist/src/evaluation/stageContextMetrics/evaluateV043StageContextExecution.js +247 -0
- package/dist/src/evaluation/stageContextMetrics/index.js +17 -0
- package/dist/src/evaluation/stageContextMetrics/matchStageContextExpectations.js +38 -0
- package/dist/src/evaluation/stageContextMetrics/producerReadinessMetricTypes.js +1 -0
- package/dist/src/evaluation/stageContextMetrics/targetKeys.js +35 -0
- package/dist/src/evaluation/stageContextMetrics/types.js +1 -0
- package/dist/src/evaluation/stageContextSelectors/contextArtifactConsistency.js +89 -0
- package/dist/src/evaluation/stageContextSelectors/contextCapsuleSelectors.js +126 -0
- package/dist/src/evaluation/stageContextSelectors/index.js +4 -0
- package/dist/src/evaluation/stageContextSelectors/retrievalAuditRecordSelectors.js +54 -0
- package/dist/src/evaluation/stageContextSelectors/workflowInstructionPacketSelectors.js +51 -0
- package/dist/src/evaluation/targetImmutability/captureTargetSnapshot.js +208 -0
- package/dist/src/evaluation/targetImmutability/compareTargetSnapshots.js +56 -0
- package/dist/src/evaluation/targetImmutability/index.js +3 -0
- package/dist/src/evaluation/targetImmutability/types.js +1 -0
- package/dist/src/evaluation/targetImmutability/validateTargetImmutabilityConfig.js +130 -0
- package/dist/src/evaluation/upstreamArtifacts/artifactReadTypes.js +1 -0
- package/dist/src/evaluation/upstreamArtifacts/index.js +9 -0
- package/dist/src/evaluation/upstreamArtifacts/jsonTypes.js +1 -0
- package/dist/src/evaluation/upstreamArtifacts/myDevKitClassificationTypesV1.js +1 -0
- package/dist/src/evaluation/upstreamArtifacts/myDevKitContextArtifactsV1.js +1 -0
- package/dist/src/evaluation/upstreamArtifacts/myDevKitSemanticTypesV1.js +1 -0
- package/dist/src/evaluation/upstreamArtifacts/orchestratorContextReadinessResultV1.js +247 -0
- package/dist/src/evaluation/upstreamArtifacts/orchestratorWorkflowInstructionPacketV1.js +1 -0
- package/dist/src/evaluation/upstreamArtifacts/readImplementationContextPacketV1.js +9 -0
- package/dist/src/evaluation/upstreamArtifacts/readImplementationContextRetrievalReportV1.js +9 -0
- package/dist/src/evaluation/upstreamArtifacts/readJsonArtifact.js +53 -0
- package/dist/src/evaluation/upstreamArtifacts/readMyDevKitContextCapsuleV1.js +9 -0
- package/dist/src/evaluation/upstreamArtifacts/readMyDevKitRetrievalAuditRecordV1.js +9 -0
- package/dist/src/evaluation/upstreamArtifacts/readOrchestratorWorkflowInstructionPacketV1.js +9 -0
- package/dist/src/evaluation/upstreamArtifacts/readSupplementalContextTextFile.js +29 -0
- package/dist/src/evaluation/upstreamArtifacts/readTestContextPacketV1.js +9 -0
- package/dist/src/evaluation/upstreamArtifacts/readTestContextRetrievalReportV1.js +9 -0
- package/dist/src/evaluation/upstreamArtifacts/runtimeValidation.js +131 -0
- package/dist/src/evaluation/upstreamArtifacts/schemaVersion.js +32 -0
- package/dist/src/evaluation/upstreamArtifacts/supplementalContextTextParser.js +75 -0
- package/dist/src/evaluation/upstreamArtifacts/supplementalContextTypes.js +152 -0
- package/dist/src/evaluation/upstreamArtifacts/validateMyDevKitContextCapsuleV1.js +1208 -0
- package/dist/src/evaluation/upstreamArtifacts/validateMyDevKitRetrievalAuditRecordV1.js +112 -0
- package/dist/src/evaluation/upstreamArtifacts/validateOrchestratorWorkflowInstructionPacketV1.js +235 -0
- package/dist/src/evaluation/upstreamArtifacts/validateSupplementalContextDocumentV1.js +148 -0
- package/dist/src/experiments/plugins/contextStrategyComparison/executeV043StageContextStrategy.js +46 -0
- package/dist/src/experiments/plugins/contextStrategyComparison/index.js +9 -0
- package/dist/src/experiments/plugins/contextStrategyComparison/loadV043StrategyArtifacts.js +397 -0
- package/dist/src/experiments/plugins/contextStrategyComparison/plugin.js +39 -1
- package/dist/src/experiments/plugins/contextStrategyComparison/readV043FullWorkflowLibraryFixture.js +162 -0
- package/dist/src/experiments/plugins/contextStrategyComparison/resolveV043StrategyInputs.js +43 -0
- package/dist/src/experiments/plugins/contextStrategyComparison/resultMapping.js +3 -0
- package/dist/src/experiments/plugins/contextStrategyComparison/runV043StageContextStrategyWithAssurance.js +205 -0
- package/dist/src/experiments/plugins/contextStrategyComparison/v043FullWorkflowLibraryFixture.js +1 -0
- package/dist/src/experiments/plugins/contextStrategyComparison/v043RunAssuranceTypes.js +1 -0
- package/dist/src/experiments/plugins/contextStrategyComparison/v043StrategyExecutionTypes.js +1 -0
- package/dist/src/experiments/plugins/contextStrategyComparison/v043StrategyIds.js +13 -0
- package/dist/src/experiments/plugins/contextStrategyComparison/v043StrategyInputContracts.js +1 -0
- package/dist/src/experiments/plugins/contextStrategyComparison/validateV043RunAssuranceConfig.js +59 -0
- package/dist/src/experiments/plugins/contextStrategyComparison/validateV043StrategyInput.js +266 -0
- package/dist/src/report/experiments/buildContextStrategyComparisonV043Report.js +520 -0
- package/dist/src/report/experiments/buildPluginExperimentReport.js +21 -6
- package/dist/src/report/experiments/contextStrategyComparisonV043ReportModel.js +1 -0
- package/dist/src/report/experiments/index.js +4 -0
- package/dist/src/report/experiments/renderContextStrategyComparisonV043Html.js +296 -0
- package/dist/src/report/experiments/renderPluginExperimentReportHtml.js +3 -0
- package/dist/src/report/experiments/renderPluginExperimentReportText.js +292 -0
- package/dist/src/report/experiments/writePluginExperimentReports.js +3 -0
- package/dist/src/securityValidation/attackScenarios/scenarios/secretLeakageScenario.js +215 -9
- package/docs/METRICS.md +84 -10
- package/package.json +2 -2
- package/tests/fixtures/full-workflow-library/complete-v1.0.0.json +24 -0
- package/tests/fixtures/full-workflow-library/future-minor-v1.1.0-additive.json +25 -0
- package/tests/fixtures/stage-context-expectations/complete-v1.0.0.json +230 -0
- package/tests/fixtures/stage-context-expectations/future-minor-v1.1.0-additive.json +232 -0
- package/tests/fixtures/stage-context-expectations/invalid/malformed-json.txt +1 -0
- package/tests/fixtures/stage-context-expectations/invalid/non-object-root.json +1 -0
- package/tests/fixtures/stage-context-expectations/minimal-v1.0.0.json +18 -0
- package/tests/fixtures/upstream-artifacts/invalid/malformed-json.txt +1 -0
- package/tests/fixtures/upstream-artifacts/invalid/non-object-root.json +1 -0
- package/tests/fixtures/upstream-artifacts/my-dev-kit/1.10.2/context-capsule/complete-v1.0.0.json +504 -0
- package/tests/fixtures/upstream-artifacts/my-dev-kit/1.10.2/context-capsule/future-minor-v1.1.0-additive.json +955 -0
- package/tests/fixtures/upstream-artifacts/my-dev-kit/1.10.2/retrieval-audit-record/complete-v1.0.0.json +190 -0
- package/tests/fixtures/upstream-artifacts/my-dev-kit/1.10.2/retrieval-audit-record/future-minor-v1.1.0-additive.json +327 -0
- package/tests/fixtures/upstream-artifacts/my-dev-kit-1.10.2-identity/PROVENANCE.txt +13 -0
- package/tests/fixtures/upstream-artifacts/my-dev-kit-1.10.2-identity/context-capsule.json +7874 -0
- package/tests/fixtures/upstream-artifacts/my-dev-kit-1.10.2-identity/retrieval-audit-record.json +1076 -0
- package/tests/fixtures/upstream-artifacts/my-dev-kit-orchestrator/1.2.1/workflow-instruction-packet/complete-v1.0.0.json +160 -0
- package/tests/fixtures/upstream-artifacts/my-dev-kit-orchestrator/1.2.1/workflow-instruction-packet/future-minor-v1.1.0-additive.json +186 -0
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
import { readImplementationContextPacketV1, readImplementationContextRetrievalReportV1, readMyDevKitContextCapsuleV1, readMyDevKitRetrievalAuditRecordV1, readOrchestratorWorkflowInstructionPacketV1, readTestContextPacketV1, readTestContextRetrievalReportV1, validateOrchestratorContextReadinessResultV1 } from "../../../evaluation/upstreamArtifacts/index.js";
|
|
2
|
+
import { checkMyDevKitContextArtifactConsistency } from "../../../evaluation/stageContextSelectors/index.js";
|
|
3
|
+
import { readStageContextExpectationFixtureV1 } from "../../../evaluation/stageContextExpectations/index.js";
|
|
4
|
+
import { readV043FullWorkflowLibraryFixture } from "./readV043FullWorkflowLibraryFixture.js";
|
|
5
|
+
function buildFailureResult(input, issues) {
|
|
6
|
+
return { status: "failed", strategyId: input.strategyId, input, issues };
|
|
7
|
+
}
|
|
8
|
+
async function loadContextArtifactPair(role, capsulePath, auditPath, pairLabel, issues) {
|
|
9
|
+
const capsuleResult = await readMyDevKitContextCapsuleV1(capsulePath);
|
|
10
|
+
if (!capsuleResult.ok) {
|
|
11
|
+
issues.push({
|
|
12
|
+
code: "CONTEXT_CAPSULE_READ_FAILED",
|
|
13
|
+
fieldPath: `${pairLabel}.contextCapsule`,
|
|
14
|
+
message: `Failed to read ContextCapsule for "${pairLabel}": ${capsuleResult.message}`,
|
|
15
|
+
sourcePath: capsuleResult.sourcePath,
|
|
16
|
+
details: capsuleResult
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
let auditReadOk = false;
|
|
20
|
+
let auditSourcePath;
|
|
21
|
+
let auditArtifact;
|
|
22
|
+
if (auditPath !== undefined) {
|
|
23
|
+
const auditResult = await readMyDevKitRetrievalAuditRecordV1(auditPath);
|
|
24
|
+
if (!auditResult.ok) {
|
|
25
|
+
issues.push({
|
|
26
|
+
code: "RETRIEVAL_AUDIT_READ_FAILED",
|
|
27
|
+
fieldPath: `${pairLabel}.retrievalAuditRecord`,
|
|
28
|
+
message: `Failed to read RetrievalAuditRecord for "${pairLabel}": ${auditResult.message}`,
|
|
29
|
+
sourcePath: auditResult.sourcePath,
|
|
30
|
+
details: auditResult
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
auditReadOk = true;
|
|
35
|
+
auditSourcePath = auditResult.sourcePath;
|
|
36
|
+
auditArtifact = auditResult.artifact;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (!capsuleResult.ok)
|
|
40
|
+
return null;
|
|
41
|
+
const pair = {
|
|
42
|
+
role,
|
|
43
|
+
contextCapsuleSourcePath: capsuleResult.sourcePath,
|
|
44
|
+
contextCapsule: capsuleResult.artifact
|
|
45
|
+
};
|
|
46
|
+
if (auditReadOk) {
|
|
47
|
+
pair.retrievalAuditRecordSourcePath = auditSourcePath;
|
|
48
|
+
pair.retrievalAuditRecord = auditArtifact;
|
|
49
|
+
}
|
|
50
|
+
return pair;
|
|
51
|
+
}
|
|
52
|
+
function validateRole(pair, expectedRole, pairLabel, issues) {
|
|
53
|
+
if (pair.contextCapsule.request.role !== expectedRole) {
|
|
54
|
+
const fieldPath = `${pairLabel}.contextCapsule.request.role`;
|
|
55
|
+
issues.push({
|
|
56
|
+
code: "CONTEXT_ROLE_MISMATCH",
|
|
57
|
+
fieldPath,
|
|
58
|
+
message: `Expected context role "${expectedRole}" but found "${pair.contextCapsule.request.role}" at "${fieldPath}".`
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
if (pair.contextCapsule.roleContext.role !== expectedRole) {
|
|
62
|
+
const fieldPath = `${pairLabel}.contextCapsule.roleContext.role`;
|
|
63
|
+
issues.push({
|
|
64
|
+
code: "CONTEXT_ROLE_MISMATCH",
|
|
65
|
+
fieldPath,
|
|
66
|
+
message: `Expected context role "${expectedRole}" but found "${pair.contextCapsule.roleContext.role}" at "${fieldPath}".`
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
if (pair.retrievalAuditRecord) {
|
|
70
|
+
if (pair.retrievalAuditRecord.request.role !== expectedRole) {
|
|
71
|
+
const fieldPath = `${pairLabel}.retrievalAuditRecord.request.role`;
|
|
72
|
+
issues.push({
|
|
73
|
+
code: "CONTEXT_ROLE_MISMATCH",
|
|
74
|
+
fieldPath,
|
|
75
|
+
message: `Expected context role "${expectedRole}" but found "${pair.retrievalAuditRecord.request.role}" at "${fieldPath}".`
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
if (pair.retrievalAuditRecord.roleContext.role !== expectedRole) {
|
|
79
|
+
const fieldPath = `${pairLabel}.retrievalAuditRecord.roleContext.role`;
|
|
80
|
+
issues.push({
|
|
81
|
+
code: "CONTEXT_ROLE_MISMATCH",
|
|
82
|
+
fieldPath,
|
|
83
|
+
message: `Expected context role "${expectedRole}" but found "${pair.retrievalAuditRecord.roleContext.role}" at "${fieldPath}".`
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function checkPairConsistency(pair, pairLabel, issues) {
|
|
89
|
+
if (!pair.retrievalAuditRecord)
|
|
90
|
+
return;
|
|
91
|
+
const consistency = checkMyDevKitContextArtifactConsistency(pair.contextCapsule, pair.retrievalAuditRecord);
|
|
92
|
+
pair.consistency = consistency;
|
|
93
|
+
if (!consistency.consistent) {
|
|
94
|
+
for (const consistencyIssue of consistency.issues) {
|
|
95
|
+
const prefixedFieldPath = `${pairLabel}.${consistencyIssue.fieldPath}`;
|
|
96
|
+
issues.push({
|
|
97
|
+
code: "CONTEXT_ARTIFACT_INCONSISTENT",
|
|
98
|
+
fieldPath: prefixedFieldPath,
|
|
99
|
+
message: `ContextCapsule and RetrievalAuditRecord are inconsistent at "${prefixedFieldPath}".`,
|
|
100
|
+
details: consistencyIssue
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function validatePairsInOrder(entries, issues) {
|
|
106
|
+
for (const entry of entries) {
|
|
107
|
+
validateRole(entry.pair, entry.role, entry.label, issues);
|
|
108
|
+
checkPairConsistency(entry.pair, entry.label, issues);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
export async function loadV043StrategyArtifacts(input) {
|
|
112
|
+
const issues = [];
|
|
113
|
+
const expectationsResult = await readStageContextExpectationFixtureV1(input.expectationsPath);
|
|
114
|
+
if (!expectationsResult.ok) {
|
|
115
|
+
issues.push({
|
|
116
|
+
code: "EXPECTATION_READ_FAILED",
|
|
117
|
+
fieldPath: "expectations",
|
|
118
|
+
message: `Failed to read stage-context expectation fixture: ${expectationsResult.message}`,
|
|
119
|
+
sourcePath: expectationsResult.sourcePath,
|
|
120
|
+
details: expectationsResult
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
switch (input.strategyId) {
|
|
124
|
+
case "architecture-context-only": {
|
|
125
|
+
const architecture = await loadContextArtifactPair("architecture", input.architectureContextCapsulePath, input.architectureRetrievalAuditRecordPath, "architecture", issues);
|
|
126
|
+
const entries = [];
|
|
127
|
+
if (architecture)
|
|
128
|
+
entries.push({ pair: architecture, role: "architecture", label: "architecture" });
|
|
129
|
+
validatePairsInOrder(entries, issues);
|
|
130
|
+
if (issues.length > 0 || !expectationsResult.ok || !architecture) {
|
|
131
|
+
return buildFailureResult(input, issues);
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
status: "completed",
|
|
135
|
+
strategyId: input.strategyId,
|
|
136
|
+
input,
|
|
137
|
+
expectationsSourcePath: expectationsResult.sourcePath,
|
|
138
|
+
expectations: expectationsResult.fixture,
|
|
139
|
+
payload: { architecture },
|
|
140
|
+
warnings: []
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
case "architecture-plus-implementation-refresh": {
|
|
144
|
+
const architecture = await loadContextArtifactPair("architecture", input.architectureContextCapsulePath, input.architectureRetrievalAuditRecordPath, "architecture", issues);
|
|
145
|
+
const implementation = await loadContextArtifactPair("implementation", input.implementationContextCapsulePath, input.implementationRetrievalAuditRecordPath, "implementation", issues);
|
|
146
|
+
const entries = [];
|
|
147
|
+
if (architecture)
|
|
148
|
+
entries.push({ pair: architecture, role: "architecture", label: "architecture" });
|
|
149
|
+
if (implementation)
|
|
150
|
+
entries.push({ pair: implementation, role: "implementation", label: "implementation" });
|
|
151
|
+
validatePairsInOrder(entries, issues);
|
|
152
|
+
if (issues.length > 0 || !expectationsResult.ok || !architecture || !implementation) {
|
|
153
|
+
return buildFailureResult(input, issues);
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
status: "completed",
|
|
157
|
+
strategyId: input.strategyId,
|
|
158
|
+
input,
|
|
159
|
+
expectationsSourcePath: expectationsResult.sourcePath,
|
|
160
|
+
expectations: expectationsResult.fixture,
|
|
161
|
+
payload: { architecture, implementation },
|
|
162
|
+
warnings: []
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
case "architecture-plus-implementation-and-test-refresh": {
|
|
166
|
+
const architecture = await loadContextArtifactPair("architecture", input.architectureContextCapsulePath, input.architectureRetrievalAuditRecordPath, "architecture", issues);
|
|
167
|
+
const implementation = await loadContextArtifactPair("implementation", input.implementationContextCapsulePath, input.implementationRetrievalAuditRecordPath, "implementation", issues);
|
|
168
|
+
const testImplementation = await loadContextArtifactPair("test-implementation", input.testImplementationContextCapsulePath, input.testImplementationRetrievalAuditRecordPath, "testImplementation", issues);
|
|
169
|
+
const entries = [];
|
|
170
|
+
if (architecture)
|
|
171
|
+
entries.push({ pair: architecture, role: "architecture", label: "architecture" });
|
|
172
|
+
if (implementation)
|
|
173
|
+
entries.push({ pair: implementation, role: "implementation", label: "implementation" });
|
|
174
|
+
if (testImplementation) {
|
|
175
|
+
entries.push({ pair: testImplementation, role: "test-implementation", label: "testImplementation" });
|
|
176
|
+
}
|
|
177
|
+
validatePairsInOrder(entries, issues);
|
|
178
|
+
if (issues.length > 0 || !expectationsResult.ok || !architecture || !implementation || !testImplementation) {
|
|
179
|
+
return buildFailureResult(input, issues);
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
status: "completed",
|
|
183
|
+
strategyId: input.strategyId,
|
|
184
|
+
input,
|
|
185
|
+
expectationsSourcePath: expectationsResult.sourcePath,
|
|
186
|
+
expectations: expectationsResult.fixture,
|
|
187
|
+
payload: { architecture, implementation, testImplementation },
|
|
188
|
+
warnings: []
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
case "full-workflow-library": {
|
|
192
|
+
const libraryResult = await readV043FullWorkflowLibraryFixture(input.fullWorkflowLibraryFixturePath);
|
|
193
|
+
if (!libraryResult.ok) {
|
|
194
|
+
issues.push({
|
|
195
|
+
code: "FULL_WORKFLOW_LIBRARY_READ_FAILED",
|
|
196
|
+
fieldPath: "fullWorkflowLibrary",
|
|
197
|
+
message: `Failed to read full-workflow-library fixture: ${libraryResult.message}`,
|
|
198
|
+
sourcePath: libraryResult.sourcePath,
|
|
199
|
+
details: libraryResult
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
if (issues.length > 0 || !expectationsResult.ok || !libraryResult.ok) {
|
|
203
|
+
return buildFailureResult(input, issues);
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
status: "completed",
|
|
207
|
+
strategyId: input.strategyId,
|
|
208
|
+
input,
|
|
209
|
+
expectationsSourcePath: expectationsResult.sourcePath,
|
|
210
|
+
expectations: expectationsResult.fixture,
|
|
211
|
+
payload: {
|
|
212
|
+
fullWorkflowLibrarySourcePath: libraryResult.sourcePath,
|
|
213
|
+
fullWorkflowLibrary: libraryResult.fixture
|
|
214
|
+
},
|
|
215
|
+
warnings: []
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
case "bounded-workflow-instruction-packet": {
|
|
219
|
+
const packetResult = await readOrchestratorWorkflowInstructionPacketV1(input.workflowInstructionPacketPath);
|
|
220
|
+
if (!packetResult.ok) {
|
|
221
|
+
issues.push({
|
|
222
|
+
code: "WORKFLOW_PACKET_READ_FAILED",
|
|
223
|
+
fieldPath: "workflowInstructionPacket",
|
|
224
|
+
message: `Failed to read WorkflowInstructionPacket: ${packetResult.message}`,
|
|
225
|
+
sourcePath: packetResult.sourcePath,
|
|
226
|
+
details: packetResult
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
if (issues.length > 0 || !expectationsResult.ok || !packetResult.ok) {
|
|
230
|
+
return buildFailureResult(input, issues);
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
status: "completed",
|
|
234
|
+
strategyId: input.strategyId,
|
|
235
|
+
input,
|
|
236
|
+
expectationsSourcePath: expectationsResult.sourcePath,
|
|
237
|
+
expectations: expectationsResult.fixture,
|
|
238
|
+
payload: {
|
|
239
|
+
workflowInstructionPacketSourcePath: packetResult.sourcePath,
|
|
240
|
+
workflowInstructionPacket: packetResult.artifact
|
|
241
|
+
},
|
|
242
|
+
warnings: []
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
case "combined-bounded-stage-context": {
|
|
246
|
+
const pairs = [];
|
|
247
|
+
const entries = [];
|
|
248
|
+
for (let index = 0; index < input.contextArtifacts.length; index += 1) {
|
|
249
|
+
const contextArtifact = input.contextArtifacts[index];
|
|
250
|
+
const pairLabel = `contextArtifacts[${index}]`;
|
|
251
|
+
const pair = await loadContextArtifactPair(contextArtifact.role, contextArtifact.contextCapsulePath, contextArtifact.retrievalAuditRecordPath, pairLabel, issues);
|
|
252
|
+
if (pair) {
|
|
253
|
+
pairs.push(pair);
|
|
254
|
+
entries.push({ pair, role: contextArtifact.role, label: pairLabel });
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
validatePairsInOrder(entries, issues);
|
|
258
|
+
const packetResult = await readOrchestratorWorkflowInstructionPacketV1(input.workflowInstructionPacketPath);
|
|
259
|
+
if (!packetResult.ok) {
|
|
260
|
+
issues.push({
|
|
261
|
+
code: "WORKFLOW_PACKET_READ_FAILED",
|
|
262
|
+
fieldPath: "workflowInstructionPacket",
|
|
263
|
+
message: `Failed to read WorkflowInstructionPacket: ${packetResult.message}`,
|
|
264
|
+
sourcePath: packetResult.sourcePath,
|
|
265
|
+
details: packetResult
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
// v0.4.4 Batch 3: producer-readiness bridge inputs -- every one is optional (section
|
|
269
|
+
// 11.2). Only a supplied-but-unreadable source becomes a load failure; an absent
|
|
270
|
+
// source is simply left off the payload for the bridge evaluator to treat as
|
|
271
|
+
// unavailable/not-applicable.
|
|
272
|
+
let implementationContextPacketSourcePath;
|
|
273
|
+
let implementationContextPacket;
|
|
274
|
+
if (input.implementationContextPacketPath !== undefined) {
|
|
275
|
+
const result = await readImplementationContextPacketV1(input.implementationContextPacketPath);
|
|
276
|
+
if (!result.ok) {
|
|
277
|
+
issues.push({
|
|
278
|
+
code: "IMPLEMENTATION_CONTEXT_PACKET_READ_FAILED",
|
|
279
|
+
fieldPath: "implementationContextPacket",
|
|
280
|
+
message: `Failed to read implementation-context packet: ${result.message}`,
|
|
281
|
+
sourcePath: result.sourcePath,
|
|
282
|
+
details: result
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
implementationContextPacketSourcePath = result.sourcePath;
|
|
287
|
+
implementationContextPacket = result.artifact;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
let implementationContextRetrievalReportSourcePath;
|
|
291
|
+
let implementationContextRetrievalReport;
|
|
292
|
+
if (input.implementationContextRetrievalReportPath !== undefined) {
|
|
293
|
+
const result = await readImplementationContextRetrievalReportV1(input.implementationContextRetrievalReportPath);
|
|
294
|
+
if (!result.ok) {
|
|
295
|
+
issues.push({
|
|
296
|
+
code: "IMPLEMENTATION_CONTEXT_RETRIEVAL_REPORT_READ_FAILED",
|
|
297
|
+
fieldPath: "implementationContextRetrievalReport",
|
|
298
|
+
message: `Failed to read implementation-context retrieval report: ${result.message}`,
|
|
299
|
+
sourcePath: result.sourcePath,
|
|
300
|
+
details: result
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
implementationContextRetrievalReportSourcePath = result.sourcePath;
|
|
305
|
+
implementationContextRetrievalReport = result.artifact;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
let testContextPacketSourcePath;
|
|
309
|
+
let testContextPacket;
|
|
310
|
+
if (input.testContextPacketPath !== undefined) {
|
|
311
|
+
const result = await readTestContextPacketV1(input.testContextPacketPath);
|
|
312
|
+
if (!result.ok) {
|
|
313
|
+
issues.push({
|
|
314
|
+
code: "TEST_CONTEXT_PACKET_READ_FAILED",
|
|
315
|
+
fieldPath: "testContextPacket",
|
|
316
|
+
message: `Failed to read test-context packet: ${result.message}`,
|
|
317
|
+
sourcePath: result.sourcePath,
|
|
318
|
+
details: result
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
testContextPacketSourcePath = result.sourcePath;
|
|
323
|
+
testContextPacket = result.artifact;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
let testContextRetrievalReportSourcePath;
|
|
327
|
+
let testContextRetrievalReport;
|
|
328
|
+
if (input.testContextRetrievalReportPath !== undefined) {
|
|
329
|
+
const result = await readTestContextRetrievalReportV1(input.testContextRetrievalReportPath);
|
|
330
|
+
if (!result.ok) {
|
|
331
|
+
issues.push({
|
|
332
|
+
code: "TEST_CONTEXT_RETRIEVAL_REPORT_READ_FAILED",
|
|
333
|
+
fieldPath: "testContextRetrievalReport",
|
|
334
|
+
message: `Failed to read test-context retrieval report: ${result.message}`,
|
|
335
|
+
sourcePath: result.sourcePath,
|
|
336
|
+
details: result
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
testContextRetrievalReportSourcePath = result.sourcePath;
|
|
341
|
+
testContextRetrievalReport = result.artifact;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
let readiness;
|
|
345
|
+
if (input.readiness !== undefined) {
|
|
346
|
+
const readinessResult = validateOrchestratorContextReadinessResultV1(input.readiness, "programmatic-input:readiness");
|
|
347
|
+
if (!readinessResult.ok) {
|
|
348
|
+
issues.push({
|
|
349
|
+
code: "READINESS_INPUT_INVALID",
|
|
350
|
+
fieldPath: "readiness",
|
|
351
|
+
message: `Readiness programmatic input is invalid: ${readinessResult.message}`,
|
|
352
|
+
details: readinessResult
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
readiness = readinessResult.artifact;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
if (issues.length > 0 || !expectationsResult.ok || !packetResult.ok || pairs.length !== input.contextArtifacts.length) {
|
|
360
|
+
return buildFailureResult(input, issues);
|
|
361
|
+
}
|
|
362
|
+
const payload = {
|
|
363
|
+
contextArtifacts: pairs,
|
|
364
|
+
workflowInstructionPacketSourcePath: packetResult.sourcePath,
|
|
365
|
+
workflowInstructionPacket: packetResult.artifact
|
|
366
|
+
};
|
|
367
|
+
if (implementationContextPacket !== undefined) {
|
|
368
|
+
payload.implementationContextPacketSourcePath = implementationContextPacketSourcePath;
|
|
369
|
+
payload.implementationContextPacket = implementationContextPacket;
|
|
370
|
+
}
|
|
371
|
+
if (implementationContextRetrievalReport !== undefined) {
|
|
372
|
+
payload.implementationContextRetrievalReportSourcePath = implementationContextRetrievalReportSourcePath;
|
|
373
|
+
payload.implementationContextRetrievalReport = implementationContextRetrievalReport;
|
|
374
|
+
}
|
|
375
|
+
if (testContextPacket !== undefined) {
|
|
376
|
+
payload.testContextPacketSourcePath = testContextPacketSourcePath;
|
|
377
|
+
payload.testContextPacket = testContextPacket;
|
|
378
|
+
}
|
|
379
|
+
if (testContextRetrievalReport !== undefined) {
|
|
380
|
+
payload.testContextRetrievalReportSourcePath = testContextRetrievalReportSourcePath;
|
|
381
|
+
payload.testContextRetrievalReport = testContextRetrievalReport;
|
|
382
|
+
}
|
|
383
|
+
if (readiness !== undefined) {
|
|
384
|
+
payload.readiness = readiness;
|
|
385
|
+
}
|
|
386
|
+
return {
|
|
387
|
+
status: "completed",
|
|
388
|
+
strategyId: input.strategyId,
|
|
389
|
+
input,
|
|
390
|
+
expectationsSourcePath: expectationsResult.sourcePath,
|
|
391
|
+
expectations: expectationsResult.fixture,
|
|
392
|
+
payload,
|
|
393
|
+
warnings: []
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
@@ -3,6 +3,10 @@ import path from "node:path";
|
|
|
3
3
|
import { runControlledExperiment } from "../../../evaluation/runControlledExperiment.js";
|
|
4
4
|
import { defaultContextStrategyComparisonConfig, validateContextStrategyComparisonConfig, } from "./config.js";
|
|
5
5
|
import { mapLegacyArtifactsToExperimentRun, } from "./resultMapping.js";
|
|
6
|
+
import { resolveV043StrategyInputs } from "./resolveV043StrategyInputs.js";
|
|
7
|
+
import { V043_STAGE_CONTEXT_STRATEGY_IDS } from "./v043StrategyIds.js";
|
|
8
|
+
import { validateV043RunAssuranceConfig } from "./validateV043RunAssuranceConfig.js";
|
|
9
|
+
import { runV043StageContextStrategyWithAssurance } from "./runV043StageContextStrategyWithAssurance.js";
|
|
6
10
|
export const contextStrategyComparisonMetadata = {
|
|
7
11
|
id: "context-strategy-comparison",
|
|
8
12
|
name: "Context Strategy Comparison",
|
|
@@ -12,6 +16,9 @@ export const contextStrategyComparisonMetadata = {
|
|
|
12
16
|
supportedTargets: ["self", "external-local"],
|
|
13
17
|
supportedOutputs: ["json", "html", "plot", "screenshot", "artifact"],
|
|
14
18
|
};
|
|
19
|
+
function isV043StrategyId(value) {
|
|
20
|
+
return V043_STAGE_CONTEXT_STRATEGY_IDS.includes(value);
|
|
21
|
+
}
|
|
15
22
|
export const contextStrategyComparisonPlugin = {
|
|
16
23
|
metadata: contextStrategyComparisonMetadata,
|
|
17
24
|
defaultConfig: defaultContextStrategyComparisonConfig,
|
|
@@ -31,13 +38,41 @@ export const contextStrategyComparisonPlugin = {
|
|
|
31
38
|
const projectProfiles = readInputArray(context.inputs, "projectProfiles");
|
|
32
39
|
const env = readEnv(context.inputs);
|
|
33
40
|
const startedAt = context.startedAt.toISOString();
|
|
41
|
+
const selectedStrategyIds = context.config.strategies ?? defaultContextStrategyComparisonConfig.strategies ?? [];
|
|
42
|
+
const providedV043Inputs = context.config.v043StrategyInputs ?? [];
|
|
43
|
+
const resolution = resolveV043StrategyInputs(selectedStrategyIds, providedV043Inputs);
|
|
44
|
+
if (!resolution.ok) {
|
|
45
|
+
const issuesText = resolution.issues.map((issue) => `${issue.code}:${issue.strategyId}`).join(", ");
|
|
46
|
+
throw new Error(`Invalid v0.4.3 strategy-input configuration: ${issuesText}`);
|
|
47
|
+
}
|
|
48
|
+
const runAssuranceValidation = validateV043RunAssuranceConfig(context.config.v043RunAssurance);
|
|
49
|
+
if (!runAssuranceValidation.ok) {
|
|
50
|
+
const issuesText = runAssuranceValidation.issues.map((issue) => `${issue.code}:${issue.fieldPath}`).join(", ");
|
|
51
|
+
throw new Error(`Invalid v0.4.3 run-assurance configuration: ${issuesText}`);
|
|
52
|
+
}
|
|
53
|
+
const runAssuranceConfig = runAssuranceValidation.config;
|
|
54
|
+
const legacyStrategyIds = selectedStrategyIds.filter((strategyId) => !isV043StrategyId(strategyId));
|
|
55
|
+
const legacyConfig = { ...context.config, strategies: legacyStrategyIds };
|
|
34
56
|
const legacyArtifacts = await runControlledExperiment({
|
|
35
|
-
config:
|
|
57
|
+
config: legacyConfig,
|
|
36
58
|
cases,
|
|
37
59
|
projectProfiles,
|
|
38
60
|
repoRoot: context.target.targetRoot,
|
|
39
61
|
env,
|
|
40
62
|
});
|
|
63
|
+
const v043SelectedStrategyIds = selectedStrategyIds.filter(isV043StrategyId);
|
|
64
|
+
const v043StageContextExecutions = [];
|
|
65
|
+
const v043StageContextEvaluations = [];
|
|
66
|
+
const v043StageContextRunAssurance = [];
|
|
67
|
+
for (const strategyId of v043SelectedStrategyIds) {
|
|
68
|
+
const input = resolution.inputByStrategyId[strategyId];
|
|
69
|
+
if (!input)
|
|
70
|
+
continue;
|
|
71
|
+
const assurance = await runV043StageContextStrategyWithAssurance(input, runAssuranceConfig);
|
|
72
|
+
v043StageContextExecutions.push(assurance.primaryExecution);
|
|
73
|
+
v043StageContextEvaluations.push(assurance.primaryEvaluation);
|
|
74
|
+
v043StageContextRunAssurance.push(assurance);
|
|
75
|
+
}
|
|
41
76
|
const completedAt = new Date().toISOString();
|
|
42
77
|
const outDir = context.outputRoot ?? path.resolve(context.toolRoot, context.config.outDir);
|
|
43
78
|
await mkdir(outDir, { recursive: true });
|
|
@@ -48,6 +83,9 @@ export const contextStrategyComparisonPlugin = {
|
|
|
48
83
|
completedAt,
|
|
49
84
|
target: context.target,
|
|
50
85
|
legacyArtifacts,
|
|
86
|
+
v043StageContextExecutions,
|
|
87
|
+
v043StageContextEvaluations,
|
|
88
|
+
v043StageContextRunAssurance,
|
|
51
89
|
pluginResultPath,
|
|
52
90
|
});
|
|
53
91
|
await writeFile(pluginResultPath, `${JSON.stringify(redactLegacyArtifacts(result), null, 2)}\n`, "utf8");
|
package/dist/src/experiments/plugins/contextStrategyComparison/readV043FullWorkflowLibraryFixture.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const SCHEMA_VERSION_PATTERN = /^[0-9]+\.[0-9]+\.[0-9]+$/;
|
|
4
|
+
const FIXTURE_ID_PATTERN = /^FULL-WORKFLOW-LIBRARY-[A-Z0-9]+(?:-[A-Z0-9]+)*-[0-9]{3}$/;
|
|
5
|
+
const REQUIRED_NONEMPTY_ID_ARRAY_FIELDS = [
|
|
6
|
+
"workflowIds",
|
|
7
|
+
"stageIds",
|
|
8
|
+
"commandIds",
|
|
9
|
+
"ruleIds",
|
|
10
|
+
"reportContractIds"
|
|
11
|
+
];
|
|
12
|
+
const ALL_ID_ARRAY_FIELDS = [
|
|
13
|
+
...REQUIRED_NONEMPTY_ID_ARRAY_FIELDS,
|
|
14
|
+
"provenanceEvidenceIds"
|
|
15
|
+
];
|
|
16
|
+
function failure(sourcePath, code, message, extra) {
|
|
17
|
+
return { ok: false, sourcePath, code, message, ...extra };
|
|
18
|
+
}
|
|
19
|
+
function isStringArray(value) {
|
|
20
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
21
|
+
}
|
|
22
|
+
function hasDuplicates(values) {
|
|
23
|
+
return new Set(values).size !== values.length;
|
|
24
|
+
}
|
|
25
|
+
function validateFullWorkflowLibraryFixture(value, sourcePath) {
|
|
26
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
27
|
+
return failure(sourcePath, "NON_OBJECT_ROOT", "Full-workflow-library fixture must parse to a JSON object at the root.", {
|
|
28
|
+
expected: "object",
|
|
29
|
+
actual: value
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
const root = value;
|
|
33
|
+
if (root.schemaVersion === undefined) {
|
|
34
|
+
return failure(sourcePath, "MISSING_REQUIRED_FIELD", "schemaVersion is required.", { fieldPath: "schemaVersion" });
|
|
35
|
+
}
|
|
36
|
+
if (typeof root.schemaVersion !== "string") {
|
|
37
|
+
return failure(sourcePath, "INVALID_FIELD_TYPE", "schemaVersion must be a string.", { fieldPath: "schemaVersion" });
|
|
38
|
+
}
|
|
39
|
+
if (!SCHEMA_VERSION_PATTERN.test(root.schemaVersion)) {
|
|
40
|
+
return failure(sourcePath, "INVALID_SCHEMA_VERSION", `schemaVersion "${root.schemaVersion}" does not match x.y.z.`, {
|
|
41
|
+
fieldPath: "schemaVersion",
|
|
42
|
+
actual: root.schemaVersion
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
const schemaVersionValue = root.schemaVersion;
|
|
46
|
+
const schemaMajor = Number(schemaVersionValue.split(".")[0]);
|
|
47
|
+
if (schemaMajor !== 1) {
|
|
48
|
+
return failure(sourcePath, "UNSUPPORTED_SCHEMA_MAJOR", `schemaVersion major "${schemaMajor}" is not supported.`, {
|
|
49
|
+
fieldPath: "schemaVersion",
|
|
50
|
+
actual: schemaVersionValue
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if (root.fixtureId === undefined) {
|
|
54
|
+
return failure(sourcePath, "MISSING_REQUIRED_FIELD", "fixtureId is required.", { fieldPath: "fixtureId" });
|
|
55
|
+
}
|
|
56
|
+
if (typeof root.fixtureId !== "string") {
|
|
57
|
+
return failure(sourcePath, "INVALID_FIELD_TYPE", "fixtureId must be a string.", { fieldPath: "fixtureId" });
|
|
58
|
+
}
|
|
59
|
+
if (!FIXTURE_ID_PATTERN.test(root.fixtureId)) {
|
|
60
|
+
return failure(sourcePath, "INVALID_FIXTURE_ID", `fixtureId "${root.fixtureId}" does not match the required pattern.`, {
|
|
61
|
+
fieldPath: "fixtureId",
|
|
62
|
+
actual: root.fixtureId
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
for (const field of ["title", "description"]) {
|
|
66
|
+
const fieldValue = root[field];
|
|
67
|
+
if (fieldValue === undefined) {
|
|
68
|
+
return failure(sourcePath, "MISSING_REQUIRED_FIELD", `${field} is required.`, { fieldPath: field });
|
|
69
|
+
}
|
|
70
|
+
if (typeof fieldValue !== "string") {
|
|
71
|
+
return failure(sourcePath, "INVALID_FIELD_TYPE", `${field} must be a string.`, { fieldPath: field });
|
|
72
|
+
}
|
|
73
|
+
if (fieldValue.trim().length === 0) {
|
|
74
|
+
return failure(sourcePath, "INVALID_FIELD_TYPE", `${field} must be a nonempty string.`, { fieldPath: field });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
for (const field of ALL_ID_ARRAY_FIELDS) {
|
|
78
|
+
const fieldValue = root[field];
|
|
79
|
+
if (fieldValue === undefined) {
|
|
80
|
+
return failure(sourcePath, "MISSING_REQUIRED_FIELD", `${field} is required.`, { fieldPath: field });
|
|
81
|
+
}
|
|
82
|
+
if (!isStringArray(fieldValue)) {
|
|
83
|
+
return failure(sourcePath, "INVALID_FIELD_TYPE", `${field} must be an array of strings.`, { fieldPath: field });
|
|
84
|
+
}
|
|
85
|
+
if (fieldValue.some((entry) => entry.length === 0)) {
|
|
86
|
+
return failure(sourcePath, "INVALID_FIELD_TYPE", `${field} entries must be nonempty strings.`, { fieldPath: field });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
for (const field of REQUIRED_NONEMPTY_ID_ARRAY_FIELDS) {
|
|
90
|
+
const fieldValue = root[field];
|
|
91
|
+
if (fieldValue.length === 0) {
|
|
92
|
+
return failure(sourcePath, "EMPTY_REQUIRED_ID_SET", `${field} must contain at least one entry.`, { fieldPath: field });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
for (const field of ALL_ID_ARRAY_FIELDS) {
|
|
96
|
+
const fieldValue = root[field];
|
|
97
|
+
if (hasDuplicates(fieldValue)) {
|
|
98
|
+
return failure(sourcePath, "DUPLICATE_ID", `${field} contains duplicate entries.`, { fieldPath: field });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (root.rawText === undefined) {
|
|
102
|
+
return failure(sourcePath, "MISSING_REQUIRED_FIELD", "rawText is required.", { fieldPath: "rawText" });
|
|
103
|
+
}
|
|
104
|
+
if (typeof root.rawText !== "string") {
|
|
105
|
+
return failure(sourcePath, "INVALID_FIELD_TYPE", "rawText must be a string.", { fieldPath: "rawText" });
|
|
106
|
+
}
|
|
107
|
+
if (root.rawText.trim().length === 0) {
|
|
108
|
+
return failure(sourcePath, "EMPTY_RAW_TEXT", "rawText must be a nonempty string.", { fieldPath: "rawText" });
|
|
109
|
+
}
|
|
110
|
+
if (root.warnings === undefined) {
|
|
111
|
+
return failure(sourcePath, "MISSING_REQUIRED_FIELD", "warnings is required.", { fieldPath: "warnings" });
|
|
112
|
+
}
|
|
113
|
+
if (!isStringArray(root.warnings)) {
|
|
114
|
+
return failure(sourcePath, "INVALID_FIELD_TYPE", "warnings must be an array of strings.", { fieldPath: "warnings" });
|
|
115
|
+
}
|
|
116
|
+
const fixture = value;
|
|
117
|
+
return {
|
|
118
|
+
ok: true,
|
|
119
|
+
sourcePath,
|
|
120
|
+
schemaVersion: schemaVersionValue,
|
|
121
|
+
schemaMajor: 1,
|
|
122
|
+
fixture,
|
|
123
|
+
rawFixture: fixture
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
export async function readV043FullWorkflowLibraryFixture(sourcePathInput) {
|
|
127
|
+
const sourcePath = path.resolve(sourcePathInput);
|
|
128
|
+
let raw;
|
|
129
|
+
try {
|
|
130
|
+
raw = await readFile(sourcePath, "utf8");
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
const code = error.code;
|
|
134
|
+
if (code === "ENOENT") {
|
|
135
|
+
return {
|
|
136
|
+
ok: false,
|
|
137
|
+
sourcePath,
|
|
138
|
+
code: "FILE_NOT_FOUND",
|
|
139
|
+
message: `Full-workflow-library fixture was not found at "${sourcePath}".`
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
ok: false,
|
|
144
|
+
sourcePath,
|
|
145
|
+
code: "UNREADABLE_FILE",
|
|
146
|
+
message: `Full-workflow-library fixture at "${sourcePath}" could not be read: ${error.message}`
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
let parsed;
|
|
150
|
+
try {
|
|
151
|
+
parsed = JSON.parse(raw);
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
return {
|
|
155
|
+
ok: false,
|
|
156
|
+
sourcePath,
|
|
157
|
+
code: "MALFORMED_JSON",
|
|
158
|
+
message: `Full-workflow-library fixture at "${sourcePath}" is not valid JSON: ${error.message}`
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
return validateFullWorkflowLibraryFixture(parsed, sourcePath);
|
|
162
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { V043_STAGE_CONTEXT_STRATEGY_IDS } from "./v043StrategyIds.js";
|
|
2
|
+
function isV043StrategyId(value) {
|
|
3
|
+
return V043_STAGE_CONTEXT_STRATEGY_IDS.includes(value);
|
|
4
|
+
}
|
|
5
|
+
export function resolveV043StrategyInputs(selectedStrategyIds, providedInputs) {
|
|
6
|
+
const issues = [];
|
|
7
|
+
const inputByStrategyId = {};
|
|
8
|
+
const selectedV043Ids = selectedStrategyIds.filter(isV043StrategyId);
|
|
9
|
+
for (const strategyId of selectedV043Ids) {
|
|
10
|
+
const matches = providedInputs.filter((input) => input.strategyId === strategyId);
|
|
11
|
+
if (matches.length === 0) {
|
|
12
|
+
issues.push({
|
|
13
|
+
code: "MISSING_STRATEGY_INPUT",
|
|
14
|
+
strategyId,
|
|
15
|
+
message: `No strategy input was supplied for selected strategy "${strategyId}".`
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
else if (matches.length > 1) {
|
|
19
|
+
issues.push({
|
|
20
|
+
code: "DUPLICATE_STRATEGY_INPUT",
|
|
21
|
+
strategyId,
|
|
22
|
+
message: `More than one strategy input was supplied for selected strategy "${strategyId}".`
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
inputByStrategyId[strategyId] = matches[0];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const selectedV043IdSet = new Set(selectedV043Ids);
|
|
30
|
+
for (const input of providedInputs) {
|
|
31
|
+
if (!selectedV043IdSet.has(input.strategyId)) {
|
|
32
|
+
issues.push({
|
|
33
|
+
code: "INPUT_STRATEGY_NOT_SELECTED",
|
|
34
|
+
strategyId: input.strategyId,
|
|
35
|
+
message: `A strategy input was supplied for "${input.strategyId}", which was not selected.`
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (issues.length > 0) {
|
|
40
|
+
return { ok: false, issues };
|
|
41
|
+
}
|
|
42
|
+
return { ok: true, inputByStrategyId };
|
|
43
|
+
}
|