@haystackeditor/cli 0.25.1 → 0.27.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 +39 -463
- package/dist/capture/app-config.js +30 -6
- package/dist/commands/capture-brief.js +45 -35
- package/dist/commands/capture-contract.js +4 -4
- package/dist/commands/crawl-report.js +196 -0
- package/dist/commands/db-profile-upload.js +3 -3
- package/dist/commands/feedback.js +66 -0
- package/dist/commands/init-telemetry.js +55 -9
- package/dist/commands/init.js +8 -3
- package/dist/commands/lockfile-pin.js +307 -0
- package/dist/commands/telemetry-token.js +3 -3
- package/dist/commands/tokens.js +4 -4
- package/dist/commands/verify-explore.js +3 -3
- package/dist/commands/verify-history.js +2 -2
- package/dist/commands/verify-onboarding.js +13 -16
- package/dist/commands/verify-precompute.js +3 -4
- package/dist/commands/verify.js +31 -118
- package/dist/index.js +55 -968
- package/dist/schema.js +4 -10
- package/dist/utils/haystack-api.js +8 -36
- package/package.json +1 -5
- package/schemas/feedback.v1.json +13 -0
- package/schemas/pre-verify.v2.json +240 -0
- package/schemas/verify-raw.v1.json +1132 -0
- package/schemas/verify.v2.json +655 -0
- package/dist/assets/hooks/agent-context/detect.ts +0 -316
- package/dist/assets/hooks/agent-context/format.ts +0 -100
- package/dist/assets/hooks/agent-context/index.ts +0 -41
- package/dist/assets/hooks/agent-context/parsers/claude.ts +0 -262
- package/dist/assets/hooks/agent-context/parsers/codex.ts +0 -416
- package/dist/assets/hooks/agent-context/parsers/gemini.ts +0 -155
- package/dist/assets/hooks/agent-context/parsers/opencode.ts +0 -174
- package/dist/assets/hooks/agent-context/tsconfig.json +0 -14
- package/dist/assets/hooks/agent-context/types.ts +0 -58
- package/dist/assets/hooks/llm-rules-template.md +0 -59
- package/dist/assets/hooks/package-lock.json +0 -598
- package/dist/assets/hooks/package.json +0 -12
- package/dist/assets/hooks/scripts/commit-msg.sh +0 -5
- package/dist/assets/hooks/scripts/post-commit.sh +0 -5
- package/dist/assets/hooks/scripts/pre-commit.sh +0 -175
- package/dist/assets/hooks/scripts/pre-push.sh +0 -25
- package/dist/assets/hooks/scripts/prepare-commit-msg.sh +0 -5
- package/dist/assets/hooks/truncation-checker/ast-analyzer.ts +0 -528
- package/dist/assets/hooks/truncation-checker/index.ts +0 -595
- package/dist/assets/hooks/truncation-checker/tsconfig.json +0 -13
- package/dist/assets/skills/map-cloud-verifier-universe/SKILL.md +0 -2051
- package/dist/assets/skills/map-cloud-verifier-universe/agents/openai.yaml +0 -4
- package/dist/assets/skills/map-cloud-verifier-universe/references/output-contract.md +0 -3411
- package/dist/assets/skills/map-your-system.md +0 -143
- package/dist/assets/skills/submit.md +0 -200
- package/dist/commands/ask.js +0 -20
- package/dist/commands/cloud-verifier-behaviors.js +0 -218
- package/dist/commands/cloud-verifier-data-store-census.js +0 -539
- package/dist/commands/cloud-verifier-data-store-drift.js +0 -158
- package/dist/commands/cloud-verifier-identity-census.js +0 -4060
- package/dist/commands/cloud-verifier-materialization.js +0 -704
- package/dist/commands/cloud-verifier-pascal-selector-census.js +0 -1382
- package/dist/commands/cloud-verifier-python-manifest-selector-census.js +0 -2015
- package/dist/commands/cloud-verifier-specialized-operational-census.js +0 -11432
- package/dist/commands/cloud-verifier-universe.js +0 -10178
- package/dist/commands/config.js +0 -549
- package/dist/commands/design-verify.js +0 -311
- package/dist/commands/dismiss.js +0 -159
- package/dist/commands/hooks.js +0 -226
- package/dist/commands/inbox.js +0 -137
- package/dist/commands/mcp.js +0 -201
- package/dist/commands/policy.js +0 -371
- package/dist/commands/pr-status.js +0 -207
- package/dist/commands/pr.js +0 -105
- package/dist/commands/prepare-universe-review.js +0 -1092
- package/dist/commands/production-source-deny-policy.js +0 -100
- package/dist/commands/request-review.js +0 -74
- package/dist/commands/review.js +0 -191
- package/dist/commands/rules.js +0 -98
- package/dist/commands/scaffold-provisional-universe.js +0 -806
- package/dist/commands/setup.js +0 -1170
- package/dist/commands/skills.js +0 -447
- package/dist/commands/status.js +0 -35
- package/dist/commands/submit.js +0 -745
- package/dist/commands/system-map.js +0 -228
- package/dist/commands/triage.js +0 -598
- package/dist/commands/webhooks.js +0 -241
- package/dist/states.js +0 -46
- package/dist/tools/detect.js +0 -832
- package/dist/triage/astra.js +0 -202
- package/dist/triage/prompts.js +0 -188
- package/dist/triage/runner.js +0 -200
- package/dist/triage/types.js +0 -7
- package/dist/types.js +0 -326
- package/dist/utils/action-output.js +0 -26
- package/dist/utils/analysis-api.js +0 -416
- package/dist/utils/config.js +0 -54
- package/dist/utils/design-verifier-api.js +0 -294
- package/dist/utils/design-verifier-history.js +0 -79
- package/dist/utils/design-verifier-result.js +0 -424
- package/dist/utils/github-api.js +0 -324
- package/dist/utils/pending-state.js +0 -86
- package/dist/utils/pr-ref.js +0 -56
- package/dist/utils/prompter.js +0 -328
- package/schemas/action.v1.json +0 -22
- package/schemas/ask.v1.json +0 -40
- package/schemas/inbox.v1.json +0 -27
- package/schemas/pr-status.v1.json +0 -61
- package/schemas/pr.v1.json +0 -97
- package/schemas/pr.v3.json +0 -45
- package/schemas/setup.v1.json +0 -75
- package/schemas/submit.v1.json +0 -90
- package/schemas/triage.v1.json +0 -103
- package/schemas/triage.v2.json +0 -64
|
@@ -1,704 +0,0 @@
|
|
|
1
|
-
const STABLE_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
2
|
-
const OPERATIONAL_STATUSES = new Set([
|
|
3
|
-
'declared',
|
|
4
|
-
'adapter-verified',
|
|
5
|
-
'materialization-probed',
|
|
6
|
-
]);
|
|
7
|
-
const LATENCY_BASES = new Set(['assumed', 'measured']);
|
|
8
|
-
const READINESS_STATUSES = new Set([
|
|
9
|
-
'proposal',
|
|
10
|
-
'cold-preparable',
|
|
11
|
-
'hot-path-proven',
|
|
12
|
-
'blocked',
|
|
13
|
-
]);
|
|
14
|
-
const READINESS_SCOPES = new Set(['real-production', 'synthetic-evaluation']);
|
|
15
|
-
const REFRESH_STRATEGIES = new Set([
|
|
16
|
-
'scheduled',
|
|
17
|
-
'on-source-cut-change',
|
|
18
|
-
'continuous',
|
|
19
|
-
'manual',
|
|
20
|
-
]);
|
|
21
|
-
const HOT_PATH_PROBE_SOURCE_KINDS = new Set([
|
|
22
|
-
'haystack-materialization-probe-result',
|
|
23
|
-
'provider-materialization-probe-result',
|
|
24
|
-
]);
|
|
25
|
-
const GENERATED_CASE_IDENTITY_FIELDS = [
|
|
26
|
-
'generated_case_id',
|
|
27
|
-
'sandbox_id',
|
|
28
|
-
'universe_incarnation_id',
|
|
29
|
-
];
|
|
30
|
-
const GENERATED_CASE_PARALLEL_PHASES = ['fork', 'attach', 'execute'];
|
|
31
|
-
const GENERATED_CASE_CLEANUP_ORDER = [
|
|
32
|
-
'release-all-attachments',
|
|
33
|
-
'revoke-all-leases',
|
|
34
|
-
];
|
|
35
|
-
export const MAX_GENERATED_CASE_FANOUT = 64;
|
|
36
|
-
function isRecord(value) {
|
|
37
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
38
|
-
}
|
|
39
|
-
function isNonemptyString(value) {
|
|
40
|
-
return typeof value === 'string' && value.trim().length > 0;
|
|
41
|
-
}
|
|
42
|
-
function isNonnegativeInteger(value) {
|
|
43
|
-
return Number.isInteger(value) && Number(value) >= 0;
|
|
44
|
-
}
|
|
45
|
-
function isPositiveInteger(value) {
|
|
46
|
-
return Number.isInteger(value) && Number(value) > 0;
|
|
47
|
-
}
|
|
48
|
-
function isExactStringSequence(value, expected) {
|
|
49
|
-
return (Array.isArray(value) &&
|
|
50
|
-
value.length === expected.length &&
|
|
51
|
-
value.every((entry, index) => entry === expected[index]));
|
|
52
|
-
}
|
|
53
|
-
function issue(issues, path, message, file = 'universe.yml') {
|
|
54
|
-
issues.push({ file, path, message });
|
|
55
|
-
}
|
|
56
|
-
function mappingAt(parent, key, path, issues) {
|
|
57
|
-
const value = parent[key];
|
|
58
|
-
if (!isRecord(value)) {
|
|
59
|
-
issue(issues, path, 'must be a mapping');
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
return value;
|
|
63
|
-
}
|
|
64
|
-
function recordsAt(parent, key, path, issues, required = true) {
|
|
65
|
-
const value = parent[key];
|
|
66
|
-
if (value === undefined && !required)
|
|
67
|
-
return [];
|
|
68
|
-
if (!Array.isArray(value)) {
|
|
69
|
-
issue(issues, path, 'must be a list');
|
|
70
|
-
return [];
|
|
71
|
-
}
|
|
72
|
-
const result = [];
|
|
73
|
-
value.forEach((entry, index) => {
|
|
74
|
-
if (!isRecord(entry)) {
|
|
75
|
-
issue(issues, `${path}[${index}]`, 'must be a mapping');
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
result.push(entry);
|
|
79
|
-
});
|
|
80
|
-
return result;
|
|
81
|
-
}
|
|
82
|
-
function checkId(value, path, targetIds, issues) {
|
|
83
|
-
if (typeof value !== 'string' || !STABLE_ID.test(value)) {
|
|
84
|
-
issue(issues, path, 'must be a lowercase kebab-case stable ID reference');
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
if (!targetIds.has(value)) {
|
|
88
|
-
issue(issues, path, `unknown stable ID reference "${value}"`);
|
|
89
|
-
}
|
|
90
|
-
return value;
|
|
91
|
-
}
|
|
92
|
-
function registerIds(records, path, globalIds, issues) {
|
|
93
|
-
const localIds = new Set();
|
|
94
|
-
records.forEach((record, index) => {
|
|
95
|
-
const id = record.id;
|
|
96
|
-
if (typeof id !== 'string' || !STABLE_ID.test(id)) {
|
|
97
|
-
issue(issues, `${path}[${index}].id`, 'must be a lowercase kebab-case stable ID');
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
if (globalIds.has(id)) {
|
|
101
|
-
issue(issues, `${path}[${index}].id`, `duplicate stable ID "${id}"`);
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
globalIds.add(id);
|
|
105
|
-
localIds.add(id);
|
|
106
|
-
});
|
|
107
|
-
return localIds;
|
|
108
|
-
}
|
|
109
|
-
function checkIdList(value, path, targetIds, issues, options = {}) {
|
|
110
|
-
if (!Array.isArray(value)) {
|
|
111
|
-
issue(issues, path, 'must be a list of stable ID references');
|
|
112
|
-
return [];
|
|
113
|
-
}
|
|
114
|
-
if (options.nonempty && value.length === 0) {
|
|
115
|
-
issue(issues, path, 'must be a non-empty list of stable ID references');
|
|
116
|
-
}
|
|
117
|
-
const result = [];
|
|
118
|
-
value.forEach((entry, index) => {
|
|
119
|
-
if (typeof entry !== 'string' || !STABLE_ID.test(entry)) {
|
|
120
|
-
issue(issues, `${path}[${index}]`, 'must be a lowercase kebab-case stable ID reference');
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
result.push(entry);
|
|
124
|
-
if (!targetIds.has(entry)) {
|
|
125
|
-
issue(issues, `${path}[${index}]`, `unknown stable ID reference "${entry}"`);
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
return result;
|
|
129
|
-
}
|
|
130
|
-
function checkEvidenceIdList(value, path, evidenceById, issues, nonempty) {
|
|
131
|
-
return checkIdList(value, path, new Set(evidenceById.keys()), issues, { nonempty });
|
|
132
|
-
}
|
|
133
|
-
function requireEvidenceSubjectLinks(ids, path, subjectIds, evidenceById, issues) {
|
|
134
|
-
ids.forEach((id, index) => {
|
|
135
|
-
const record = evidenceById.get(id);
|
|
136
|
-
if (!record)
|
|
137
|
-
return;
|
|
138
|
-
subjectIds.forEach((subjectId) => {
|
|
139
|
-
if (!Array.isArray(record.subject_ids) || !record.subject_ids.includes(subjectId)) {
|
|
140
|
-
issue(issues, `${path}[${index}]`, `evidence "${id}" must name "${subjectId}" in subject_ids`);
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
export function validateMaterializationCapabilityMetadata(record, path, issues) {
|
|
146
|
-
if (record.claim_kind !== 'materialization-capability')
|
|
147
|
-
return;
|
|
148
|
-
if (typeof record.operational_status !== 'string' ||
|
|
149
|
-
!OPERATIONAL_STATUSES.has(record.operational_status)) {
|
|
150
|
-
issue(issues, `${path}.operational_status`, 'must be "declared", "adapter-verified", or "materialization-probed"', 'evidence.yml');
|
|
151
|
-
}
|
|
152
|
-
if (typeof record.latency_basis !== 'string' || !LATENCY_BASES.has(record.latency_basis)) {
|
|
153
|
-
issue(issues, `${path}.latency_basis`, 'must be "assumed" or "measured"', 'evidence.yml');
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
function validateStepCapabilityEvidence(step, path, evidenceById, issues, requiredOperationalStatus) {
|
|
157
|
-
const ids = checkEvidenceIdList(step.capability_evidence_ids, `${path}.capability_evidence_ids`, evidenceById, issues, true);
|
|
158
|
-
const requiredRank = {
|
|
159
|
-
declared: 0,
|
|
160
|
-
'adapter-verified': 1,
|
|
161
|
-
'materialization-probed': 2,
|
|
162
|
-
}[requiredOperationalStatus];
|
|
163
|
-
ids.forEach((id, index) => {
|
|
164
|
-
const evidence = evidenceById.get(id);
|
|
165
|
-
if (!evidence)
|
|
166
|
-
return;
|
|
167
|
-
const evidencePath = `${path}.capability_evidence_ids[${index}]`;
|
|
168
|
-
if (evidence.claim_kind !== 'materialization-capability') {
|
|
169
|
-
issue(issues, evidencePath, `capability evidence "${id}" must use claim_kind "materialization-capability"`);
|
|
170
|
-
}
|
|
171
|
-
if (evidence.confidence !== 'proven') {
|
|
172
|
-
issue(issues, evidencePath, `capability evidence "${id}" must have confidence "proven"`);
|
|
173
|
-
}
|
|
174
|
-
if (!Array.isArray(evidence.subject_ids) || !evidence.subject_ids.includes(step.id)) {
|
|
175
|
-
issue(issues, evidencePath, `capability evidence "${id}" must name step "${String(step.id)}" in subject_ids`);
|
|
176
|
-
}
|
|
177
|
-
const actualRank = typeof evidence.operational_status === 'string'
|
|
178
|
-
? {
|
|
179
|
-
declared: 0,
|
|
180
|
-
'adapter-verified': 1,
|
|
181
|
-
'materialization-probed': 2,
|
|
182
|
-
}[evidence.operational_status]
|
|
183
|
-
: undefined;
|
|
184
|
-
if (actualRank === undefined || actualRank < requiredRank) {
|
|
185
|
-
issue(issues, evidencePath, `capability evidence "${id}" must have operational_status "${requiredOperationalStatus}" or stronger`);
|
|
186
|
-
}
|
|
187
|
-
if (step.latency_basis === 'measured' && evidence.latency_basis !== 'measured') {
|
|
188
|
-
issue(issues, evidencePath, `measured step latency requires capability evidence "${id}" with latency_basis "measured"`);
|
|
189
|
-
}
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
function computeCriticalPath(steps, stepIds, path, metric, issues) {
|
|
193
|
-
const byId = new Map();
|
|
194
|
-
steps.forEach((record, index) => {
|
|
195
|
-
if (typeof record.id === 'string' && stepIds.has(record.id)) {
|
|
196
|
-
byId.set(record.id, { record, index });
|
|
197
|
-
}
|
|
198
|
-
});
|
|
199
|
-
const state = new Map();
|
|
200
|
-
const memo = new Map();
|
|
201
|
-
let valid = true;
|
|
202
|
-
const visit = (id, trail) => {
|
|
203
|
-
const currentState = state.get(id);
|
|
204
|
-
if (currentState === 'visited')
|
|
205
|
-
return memo.get(id) ?? 0;
|
|
206
|
-
if (currentState === 'visiting') {
|
|
207
|
-
valid = false;
|
|
208
|
-
issue(issues, path, `step dependency graph contains a cycle: ${[...trail, id].join(' -> ')}`);
|
|
209
|
-
return 0;
|
|
210
|
-
}
|
|
211
|
-
const found = byId.get(id);
|
|
212
|
-
if (!found)
|
|
213
|
-
return 0;
|
|
214
|
-
state.set(id, 'visiting');
|
|
215
|
-
const dependencies = Array.isArray(found.record.depends_on_step_ids)
|
|
216
|
-
? found.record.depends_on_step_ids.filter((entry) => typeof entry === 'string')
|
|
217
|
-
: [];
|
|
218
|
-
let dependencyMax = 0;
|
|
219
|
-
dependencies.forEach((dependencyId) => {
|
|
220
|
-
if (stepIds.has(dependencyId)) {
|
|
221
|
-
dependencyMax = Math.max(dependencyMax, visit(dependencyId, [...trail, id]));
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
const own = found.record[metric];
|
|
225
|
-
if (!isNonnegativeInteger(own)) {
|
|
226
|
-
valid = false;
|
|
227
|
-
return dependencyMax;
|
|
228
|
-
}
|
|
229
|
-
const total = dependencyMax + own;
|
|
230
|
-
memo.set(id, total);
|
|
231
|
-
state.set(id, 'visited');
|
|
232
|
-
return total;
|
|
233
|
-
};
|
|
234
|
-
let critical = 0;
|
|
235
|
-
stepIds.forEach((id) => {
|
|
236
|
-
critical = Math.max(critical, visit(id, []));
|
|
237
|
-
});
|
|
238
|
-
return valid ? critical : null;
|
|
239
|
-
}
|
|
240
|
-
function validatePhase(phase, phaseName, globalIds, allSubjectIds, preparedStateIds, evidenceById, issues, requiredOperationalStatus) {
|
|
241
|
-
const path = `materialization_plan.${phaseName}`;
|
|
242
|
-
const steps = recordsAt(phase, 'steps', `${path}.steps`, issues);
|
|
243
|
-
const stepIds = registerIds(steps, `${path}.steps`, globalIds, issues);
|
|
244
|
-
const knownSubjects = new Set([...allSubjectIds, ...globalIds]);
|
|
245
|
-
const producedPreparedStateIds = new Set();
|
|
246
|
-
steps.forEach((step, index) => {
|
|
247
|
-
const stepPath = `${path}.steps[${index}]`;
|
|
248
|
-
if (!isNonemptyString(step.kind))
|
|
249
|
-
issue(issues, `${stepPath}.kind`, 'must be a non-empty string');
|
|
250
|
-
checkIdList(step.subject_ids, `${stepPath}.subject_ids`, knownSubjects, issues, {
|
|
251
|
-
nonempty: true,
|
|
252
|
-
});
|
|
253
|
-
checkIdList(step.depends_on_step_ids, `${stepPath}.depends_on_step_ids`, stepIds, issues);
|
|
254
|
-
if (typeof step.latency_basis !== 'string' || !LATENCY_BASES.has(step.latency_basis)) {
|
|
255
|
-
issue(issues, `${stepPath}.latency_basis`, 'must be "assumed" or "measured"');
|
|
256
|
-
}
|
|
257
|
-
for (const metric of ['planned_typical_ms', 'planned_high_ms']) {
|
|
258
|
-
if (!isNonnegativeInteger(step[metric])) {
|
|
259
|
-
issue(issues, `${stepPath}.${metric}`, 'must be a non-negative integer number of milliseconds');
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
if (step.expected_ms !== undefined) {
|
|
263
|
-
issue(issues, `${stepPath}.expected_ms`, 'is not an honest branch-duration label; use planned_typical_ms');
|
|
264
|
-
}
|
|
265
|
-
if (step.component_p95_ms !== undefined) {
|
|
266
|
-
issue(issues, `${stepPath}.component_p95_ms`, 'cannot be summed into a phase p95; use planned_high_ms for the planning input');
|
|
267
|
-
}
|
|
268
|
-
validateStepCapabilityEvidence(step, stepPath, evidenceById, issues, requiredOperationalStatus);
|
|
269
|
-
if (phaseName === 'cold_preparation') {
|
|
270
|
-
const produced = checkIdList(step.produces_prepared_state_ids, `${stepPath}.produces_prepared_state_ids`, preparedStateIds, issues);
|
|
271
|
-
produced.forEach((id) => producedPreparedStateIds.add(id));
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
|
-
const plannedTypicalCriticalPathMs = computeCriticalPath(steps, stepIds, `${path}.steps`, 'planned_typical_ms', issues);
|
|
275
|
-
const plannedHighCriticalPathMs = computeCriticalPath(steps, stepIds, `${path}.steps`, 'planned_high_ms', issues);
|
|
276
|
-
const emptyLegacyPlanningAlias = steps.length === 0 &&
|
|
277
|
-
phase.planned_typical_critical_path_ms === undefined &&
|
|
278
|
-
phase.reported_expected_critical_path_ms === 0;
|
|
279
|
-
const plannedTypical = emptyLegacyPlanningAlias
|
|
280
|
-
? 0
|
|
281
|
-
: phase.planned_typical_critical_path_ms;
|
|
282
|
-
if (!isNonnegativeInteger(plannedTypical)) {
|
|
283
|
-
issue(issues, `${path}.planned_typical_critical_path_ms`, 'must be a non-negative integer number of milliseconds');
|
|
284
|
-
}
|
|
285
|
-
else if (plannedTypicalCriticalPathMs !== null &&
|
|
286
|
-
plannedTypical !== plannedTypicalCriticalPathMs) {
|
|
287
|
-
issue(issues, `${path}.planned_typical_critical_path_ms`, `must equal the computed typical planning path ${plannedTypicalCriticalPathMs}; it is not a true expected makespan`);
|
|
288
|
-
}
|
|
289
|
-
const emptyLegacyHighAlias = steps.length === 0 &&
|
|
290
|
-
phase.planned_high_critical_path_ms === undefined &&
|
|
291
|
-
phase.reported_conservative_component_p95_critical_path_ms === 0;
|
|
292
|
-
const plannedHigh = emptyLegacyHighAlias ? 0 : phase.planned_high_critical_path_ms;
|
|
293
|
-
if (!isNonnegativeInteger(plannedHigh)) {
|
|
294
|
-
issue(issues, `${path}.planned_high_critical_path_ms`, 'must be a non-negative integer number of milliseconds');
|
|
295
|
-
}
|
|
296
|
-
else if (plannedHighCriticalPathMs !== null &&
|
|
297
|
-
plannedHigh !== plannedHighCriticalPathMs) {
|
|
298
|
-
issue(issues, `${path}.planned_high_critical_path_ms`, `must equal the computed high planning path ${plannedHighCriticalPathMs}; it is not an observed or statistical phase p95`);
|
|
299
|
-
}
|
|
300
|
-
if (phase.reported_expected_critical_path_ms !== undefined &&
|
|
301
|
-
!emptyLegacyPlanningAlias) {
|
|
302
|
-
issue(issues, `${path}.reported_expected_critical_path_ms`, 'can imply a true expected makespan; use planned_typical_critical_path_ms');
|
|
303
|
-
}
|
|
304
|
-
if (phase.reported_conservative_component_p95_critical_path_ms !== undefined &&
|
|
305
|
-
!emptyLegacyHighAlias) {
|
|
306
|
-
issue(issues, `${path}.reported_conservative_component_p95_critical_path_ms`, 'can imply a statistical phase p95; use planned_high_critical_path_ms');
|
|
307
|
-
}
|
|
308
|
-
if (phase.reported_p95_critical_path_ms !== undefined) {
|
|
309
|
-
issue(issues, `${path}.reported_p95_critical_path_ms`, 'is ambiguous; use planned_high_critical_path_ms or hot_launch.observed_end_to_end_p95_ms');
|
|
310
|
-
}
|
|
311
|
-
return {
|
|
312
|
-
stepIds,
|
|
313
|
-
producedPreparedStateIds,
|
|
314
|
-
plannedTypicalCriticalPathMs,
|
|
315
|
-
plannedHighCriticalPathMs,
|
|
316
|
-
steps,
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
function validateExactPreparedStateIdentity(state, path, workloadProfileIds, workloadProfilesById, compatibilityGroupIds, evidenceById, issues) {
|
|
320
|
-
const exactIdentity = mappingAt(state, 'exact_identity', `${path}.exact_identity`, issues);
|
|
321
|
-
if (exactIdentity) {
|
|
322
|
-
for (const key of [
|
|
323
|
-
'template_catalog_ref',
|
|
324
|
-
'resource_size',
|
|
325
|
-
'durable_snapshot_ref',
|
|
326
|
-
'source_cut_ref',
|
|
327
|
-
]) {
|
|
328
|
-
if (!isNonemptyString(exactIdentity[key])) {
|
|
329
|
-
issue(issues, `${path}.exact_identity.${key}`, 'must be a non-empty opaque identity field');
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
for (const key of ['artifact_digest_refs', 'config_digest_refs']) {
|
|
333
|
-
const value = exactIdentity[key];
|
|
334
|
-
if (!Array.isArray(value) || value.length === 0 || value.some((entry) => !isNonemptyString(entry))) {
|
|
335
|
-
issue(issues, `${path}.exact_identity.${key}`, 'must be a non-empty list of opaque digest references');
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
const workloadProfileId = checkId(exactIdentity.workload_profile_id, `${path}.exact_identity.workload_profile_id`, workloadProfileIds, issues);
|
|
339
|
-
checkId(exactIdentity.compatibility_group_id, `${path}.exact_identity.compatibility_group_id`, compatibilityGroupIds, issues);
|
|
340
|
-
const profile = workloadProfileId
|
|
341
|
-
? workloadProfilesById.get(workloadProfileId)
|
|
342
|
-
: undefined;
|
|
343
|
-
if (profile) {
|
|
344
|
-
for (const key of ['template_catalog_ref', 'resource_size']) {
|
|
345
|
-
if (exactIdentity[key] !== profile[key]) {
|
|
346
|
-
issue(issues, `${path}.exact_identity.${key}`, `must exactly match materialization_plan.workload_profiles entry "${workloadProfileId}"`);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
const stateEvidenceIds = checkEvidenceIdList(state.evidence_ids, `${path}.evidence_ids`, evidenceById, issues, true);
|
|
352
|
-
if (typeof state.id === 'string') {
|
|
353
|
-
requireEvidenceSubjectLinks(stateEvidenceIds, `${path}.evidence_ids`, [state.id], evidenceById, issues);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
function validateGeneratedCaseRuns(value, path, achievedGeneratedCaseFanout, issues) {
|
|
357
|
-
if (!Array.isArray(value)) {
|
|
358
|
-
issue(issues, path, 'must be a list of exact generated-case run identities', 'evidence.yml');
|
|
359
|
-
return;
|
|
360
|
-
}
|
|
361
|
-
if (isPositiveInteger(achievedGeneratedCaseFanout) &&
|
|
362
|
-
value.length !== achievedGeneratedCaseFanout) {
|
|
363
|
-
issue(issues, path, 'must contain exactly one run identity for each achieved generated case', 'evidence.yml');
|
|
364
|
-
}
|
|
365
|
-
const seenByField = new Map(GENERATED_CASE_IDENTITY_FIELDS.map((field) => [field, new Set()]));
|
|
366
|
-
value.forEach((entry, index) => {
|
|
367
|
-
const entryPath = `${path}[${index}]`;
|
|
368
|
-
if (!isRecord(entry)) {
|
|
369
|
-
issue(issues, entryPath, 'must be a mapping', 'evidence.yml');
|
|
370
|
-
return;
|
|
371
|
-
}
|
|
372
|
-
GENERATED_CASE_IDENTITY_FIELDS.forEach((field) => {
|
|
373
|
-
const id = entry[field];
|
|
374
|
-
if (typeof id !== 'string' || !STABLE_ID.test(id)) {
|
|
375
|
-
issue(issues, `${entryPath}.${field}`, 'must be a lowercase kebab-case stable ID', 'evidence.yml');
|
|
376
|
-
return;
|
|
377
|
-
}
|
|
378
|
-
const seen = seenByField.get(field);
|
|
379
|
-
if (seen?.has(id)) {
|
|
380
|
-
issue(issues, `${entryPath}.${field}`, `duplicate exact ${field} "${id}"`, 'evidence.yml');
|
|
381
|
-
}
|
|
382
|
-
seen?.add(id);
|
|
383
|
-
});
|
|
384
|
-
});
|
|
385
|
-
}
|
|
386
|
-
function validateHotPathProbeEvidence(hot, readinessScope, injection, freshUniversePerGeneratedCase, consumedPreparedStateIds, achievedGeneratedCaseFanout, evidenceById, issues) {
|
|
387
|
-
const path = 'materialization_plan.hot_launch.probe_evidence_ids';
|
|
388
|
-
const ids = checkEvidenceIdList(hot.probe_evidence_ids, path, evidenceById, issues, true);
|
|
389
|
-
const expectedEnvironmentScope = readinessScope === 'real-production'
|
|
390
|
-
? 'hosted-production'
|
|
391
|
-
: 'production-reference';
|
|
392
|
-
const requiredSubjectIds = [
|
|
393
|
-
...consumedPreparedStateIds,
|
|
394
|
-
...(typeof injection?.step_id === 'string' ? [injection.step_id] : []),
|
|
395
|
-
...(typeof freshUniversePerGeneratedCase?.step_id === 'string'
|
|
396
|
-
? [freshUniversePerGeneratedCase.step_id]
|
|
397
|
-
: []),
|
|
398
|
-
].filter((id, index, all) => all.indexOf(id) === index);
|
|
399
|
-
requireEvidenceSubjectLinks(ids, path, requiredSubjectIds, evidenceById, issues);
|
|
400
|
-
ids.forEach((id, index) => {
|
|
401
|
-
const evidence = evidenceById.get(id);
|
|
402
|
-
if (!evidence)
|
|
403
|
-
return;
|
|
404
|
-
const evidencePath = `${path}[${index}]`;
|
|
405
|
-
if (evidence.claim_kind !== 'materialization-hot-path-probe') {
|
|
406
|
-
issue(issues, evidencePath, `probe evidence "${id}" must use claim_kind "materialization-hot-path-probe"`);
|
|
407
|
-
}
|
|
408
|
-
if (evidence.confidence !== 'proven') {
|
|
409
|
-
issue(issues, evidencePath, `probe evidence "${id}" must have confidence "proven"`);
|
|
410
|
-
}
|
|
411
|
-
if (evidence.operational_status !== 'materialization-probed') {
|
|
412
|
-
issue(issues, evidencePath, `probe evidence "${id}" must have operational_status "materialization-probed"`);
|
|
413
|
-
}
|
|
414
|
-
if (evidence.latency_basis !== 'measured') {
|
|
415
|
-
issue(issues, evidencePath, `probe evidence "${id}" must have latency_basis "measured"`);
|
|
416
|
-
}
|
|
417
|
-
if (evidence.authority !== 'authoritative') {
|
|
418
|
-
issue(issues, evidencePath, `probe evidence "${id}" must be authoritative`);
|
|
419
|
-
}
|
|
420
|
-
if (evidence.environment_scope !== expectedEnvironmentScope) {
|
|
421
|
-
issue(issues, evidencePath, `probe evidence "${id}" must use environment_scope "${expectedEnvironmentScope}" for readiness scope "${String(readinessScope)}"; local or synthetic evidence cannot prove a real-production hot path`);
|
|
422
|
-
}
|
|
423
|
-
const hasTypedProbeSource = Array.isArray(evidence.sources) &&
|
|
424
|
-
evidence.sources.some((source) => isRecord(source) &&
|
|
425
|
-
isNonemptyString(source.source_ref) &&
|
|
426
|
-
typeof source.kind === 'string' &&
|
|
427
|
-
HOT_PATH_PROBE_SOURCE_KINDS.has(source.kind) &&
|
|
428
|
-
source.environment_scope === expectedEnvironmentScope);
|
|
429
|
-
if (!hasTypedProbeSource) {
|
|
430
|
-
issue(issues, evidencePath, `probe evidence "${id}" must cite a scope-compatible source_ref typed as a Haystack or provider materialization probe result; repository paths do not qualify`);
|
|
431
|
-
}
|
|
432
|
-
const results = isRecord(evidence.probe_results) ? evidence.probe_results : null;
|
|
433
|
-
if (!results) {
|
|
434
|
-
issue(issues, evidencePath, `probe evidence "${id}" must contain probe_results`);
|
|
435
|
-
return;
|
|
436
|
-
}
|
|
437
|
-
if (!isNonnegativeInteger(results.observed_end_to_end_p95_ms)) {
|
|
438
|
-
issue(issues, `${evidencePath}.probe_results.observed_end_to_end_p95_ms`, 'must be a non-negative integer number of measured milliseconds');
|
|
439
|
-
}
|
|
440
|
-
else if (results.observed_end_to_end_p95_ms !== hot.observed_end_to_end_p95_ms) {
|
|
441
|
-
issue(issues, `${evidencePath}.probe_results.observed_end_to_end_p95_ms`, 'must exactly match hot_launch.observed_end_to_end_p95_ms');
|
|
442
|
-
}
|
|
443
|
-
if (!isPositiveInteger(results.achieved_generated_case_fanout)) {
|
|
444
|
-
issue(issues, `${evidencePath}.probe_results.achieved_generated_case_fanout`, 'must be a positive integer');
|
|
445
|
-
}
|
|
446
|
-
else if (results.achieved_generated_case_fanout !== achievedGeneratedCaseFanout) {
|
|
447
|
-
issue(issues, `${evidencePath}.probe_results.achieved_generated_case_fanout`, 'must exactly match hot_launch.achieved_generated_case_fanout');
|
|
448
|
-
}
|
|
449
|
-
if (!isNonemptyString(results.verification_commit_ref)) {
|
|
450
|
-
issue(issues, `${evidencePath}.probe_results.verification_commit_ref`, 'must identify the arbitrary verification commit exercised by the probe');
|
|
451
|
-
}
|
|
452
|
-
for (const [key, expected] of [
|
|
453
|
-
['arbitrary_verification_commit_injection', injection?.supported === true],
|
|
454
|
-
[
|
|
455
|
-
'fresh_universe_per_generated_case',
|
|
456
|
-
freshUniversePerGeneratedCase?.enforced === true,
|
|
457
|
-
],
|
|
458
|
-
[
|
|
459
|
-
'parent_write_protected',
|
|
460
|
-
freshUniversePerGeneratedCase?.parent_write_protected === true,
|
|
461
|
-
],
|
|
462
|
-
['cleanup_covers_all_attempts_on_partial_failure', true],
|
|
463
|
-
['single_absolute_deadline', true],
|
|
464
|
-
]) {
|
|
465
|
-
if (results[key] !== true || expected !== true) {
|
|
466
|
-
issue(issues, `${evidencePath}.probe_results.${key}`, 'must be true and exactly match the enforced hot-launch property');
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
for (const [key, expected] of [
|
|
470
|
-
['parallel_phases', GENERATED_CASE_PARALLEL_PHASES],
|
|
471
|
-
['cleanup_order', GENERATED_CASE_CLEANUP_ORDER],
|
|
472
|
-
]) {
|
|
473
|
-
if (!isExactStringSequence(results[key], expected)) {
|
|
474
|
-
issue(issues, `${evidencePath}.probe_results.${key}`, `must be exactly [${expected.join(', ')}]`, 'evidence.yml');
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
validateGeneratedCaseRuns(results.generated_case_runs, `${evidencePath}.probe_results.generated_case_runs`, achievedGeneratedCaseFanout, issues);
|
|
478
|
-
});
|
|
479
|
-
}
|
|
480
|
-
function validateHotPathInventoryCoverage(coldSteps, hotSteps, coverageSubjects, issues) {
|
|
481
|
-
const stepSubjectIds = new Set();
|
|
482
|
-
[...coldSteps, ...hotSteps].forEach((step) => {
|
|
483
|
-
if (!Array.isArray(step.subject_ids))
|
|
484
|
-
return;
|
|
485
|
-
step.subject_ids.forEach((id) => {
|
|
486
|
-
if (typeof id === 'string')
|
|
487
|
-
stepSubjectIds.add(id);
|
|
488
|
-
});
|
|
489
|
-
});
|
|
490
|
-
coverageSubjects.forEach((subject) => {
|
|
491
|
-
if (stepSubjectIds.has(subject.id))
|
|
492
|
-
return;
|
|
493
|
-
issue(issues, subject.path, `hot-path-proven requires mapped ${subject.kind} "${subject.id}" to be named by at least one cold_preparation.steps[].subject_ids or hot_launch.steps[].subject_ids entry`);
|
|
494
|
-
});
|
|
495
|
-
}
|
|
496
|
-
export function validateMaterializationPlan(value, context, issues, required) {
|
|
497
|
-
if (value === undefined && !required)
|
|
498
|
-
return;
|
|
499
|
-
if (!isRecord(value)) {
|
|
500
|
-
issue(issues, 'materialization_plan', 'must be a mapping');
|
|
501
|
-
return;
|
|
502
|
-
}
|
|
503
|
-
const plan = value;
|
|
504
|
-
if (!isPositiveInteger(plan.verifier_total_slo_ms)) {
|
|
505
|
-
issue(issues, 'materialization_plan.verifier_total_slo_ms', 'must be a positive integer');
|
|
506
|
-
}
|
|
507
|
-
if (!isPositiveInteger(plan.target_slo_ms)) {
|
|
508
|
-
issue(issues, 'materialization_plan.target_slo_ms', 'must be a positive integer hot-launch allocation');
|
|
509
|
-
}
|
|
510
|
-
if (isPositiveInteger(plan.verifier_total_slo_ms) &&
|
|
511
|
-
isPositiveInteger(plan.target_slo_ms) &&
|
|
512
|
-
plan.target_slo_ms >= plan.verifier_total_slo_ms) {
|
|
513
|
-
issue(issues, 'materialization_plan.target_slo_ms', 'must be strictly smaller than verifier_total_slo_ms to reserve time for case generation, execution, and aggregation');
|
|
514
|
-
}
|
|
515
|
-
if (!isPositiveInteger(plan.reserved_case_work_ms)) {
|
|
516
|
-
issue(issues, 'materialization_plan.reserved_case_work_ms', 'must be a positive integer');
|
|
517
|
-
}
|
|
518
|
-
else if (isPositiveInteger(plan.verifier_total_slo_ms) &&
|
|
519
|
-
isPositiveInteger(plan.target_slo_ms) &&
|
|
520
|
-
plan.reserved_case_work_ms !== plan.verifier_total_slo_ms - plan.target_slo_ms) {
|
|
521
|
-
issue(issues, 'materialization_plan.reserved_case_work_ms', 'must equal verifier_total_slo_ms minus target_slo_ms');
|
|
522
|
-
}
|
|
523
|
-
const generatedCaseFanout = mappingAt(plan, 'generated_case_fanout', 'materialization_plan.generated_case_fanout', issues);
|
|
524
|
-
if (generatedCaseFanout) {
|
|
525
|
-
if (!isPositiveInteger(generatedCaseFanout.target_n)) {
|
|
526
|
-
issue(issues, 'materialization_plan.generated_case_fanout.target_n', 'must be a positive integer');
|
|
527
|
-
}
|
|
528
|
-
if (!isPositiveInteger(generatedCaseFanout.hard_max_n)) {
|
|
529
|
-
issue(issues, 'materialization_plan.generated_case_fanout.hard_max_n', 'must be a positive integer hard bound');
|
|
530
|
-
}
|
|
531
|
-
else {
|
|
532
|
-
if (generatedCaseFanout.hard_max_n > MAX_GENERATED_CASE_FANOUT) {
|
|
533
|
-
issue(issues, 'materialization_plan.generated_case_fanout.hard_max_n', `must be at or below the product hard ceiling ${MAX_GENERATED_CASE_FANOUT}`);
|
|
534
|
-
}
|
|
535
|
-
if (isPositiveInteger(generatedCaseFanout.target_n) &&
|
|
536
|
-
generatedCaseFanout.target_n > generatedCaseFanout.hard_max_n) {
|
|
537
|
-
issue(issues, 'materialization_plan.generated_case_fanout.target_n', 'must be at or below hard_max_n');
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
if (!isExactStringSequence(generatedCaseFanout.required_identity_fields, GENERATED_CASE_IDENTITY_FIELDS)) {
|
|
541
|
-
issue(issues, 'materialization_plan.generated_case_fanout.required_identity_fields', `must be exactly [${GENERATED_CASE_IDENTITY_FIELDS.join(', ')}]`);
|
|
542
|
-
}
|
|
543
|
-
if (!isExactStringSequence(generatedCaseFanout.parallel_phases, GENERATED_CASE_PARALLEL_PHASES)) {
|
|
544
|
-
issue(issues, 'materialization_plan.generated_case_fanout.parallel_phases', `must be exactly [${GENERATED_CASE_PARALLEL_PHASES.join(', ')}]`);
|
|
545
|
-
}
|
|
546
|
-
if (!isExactStringSequence(generatedCaseFanout.cleanup_order, GENERATED_CASE_CLEANUP_ORDER)) {
|
|
547
|
-
issue(issues, 'materialization_plan.generated_case_fanout.cleanup_order', `must be exactly [${GENERATED_CASE_CLEANUP_ORDER.join(', ')}]`);
|
|
548
|
-
}
|
|
549
|
-
if (generatedCaseFanout.cleanup_covers_all_attempts_on_partial_failure !== true) {
|
|
550
|
-
issue(issues, 'materialization_plan.generated_case_fanout.cleanup_covers_all_attempts_on_partial_failure', 'must be true');
|
|
551
|
-
}
|
|
552
|
-
if (generatedCaseFanout.single_absolute_deadline !== true) {
|
|
553
|
-
issue(issues, 'materialization_plan.generated_case_fanout.single_absolute_deadline', 'must be true');
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
const readiness = mappingAt(plan, 'readiness', 'materialization_plan.readiness', issues);
|
|
557
|
-
const readinessStatus = readiness?.status;
|
|
558
|
-
if (typeof readinessStatus !== 'string' || !READINESS_STATUSES.has(readinessStatus)) {
|
|
559
|
-
issue(issues, 'materialization_plan.readiness.status', 'must be "proposal", "cold-preparable", "hot-path-proven", or "blocked"');
|
|
560
|
-
}
|
|
561
|
-
if (typeof readiness?.scope !== 'string' || !READINESS_SCOPES.has(readiness.scope)) {
|
|
562
|
-
issue(issues, 'materialization_plan.readiness.scope', 'must be "real-production" or "synthetic-evaluation"');
|
|
563
|
-
}
|
|
564
|
-
if (readinessStatus === 'blocked' && !isNonemptyString(readiness?.reason)) {
|
|
565
|
-
issue(issues, 'materialization_plan.readiness.reason', 'must explain why readiness is blocked');
|
|
566
|
-
}
|
|
567
|
-
const baseUniverseIds = new Set(context.universeIds);
|
|
568
|
-
const workloadProfiles = recordsAt(plan, 'workload_profiles', 'materialization_plan.workload_profiles', issues, false);
|
|
569
|
-
const workloadProfileIds = registerIds(workloadProfiles, 'materialization_plan.workload_profiles', context.universeIds, issues);
|
|
570
|
-
const workloadProfilesById = new Map();
|
|
571
|
-
workloadProfiles.forEach((profile, index) => {
|
|
572
|
-
if (typeof profile.id === 'string' && workloadProfileIds.has(profile.id)) {
|
|
573
|
-
workloadProfilesById.set(profile.id, profile);
|
|
574
|
-
}
|
|
575
|
-
for (const key of ['template_catalog_ref', 'resource_size']) {
|
|
576
|
-
if (!isNonemptyString(profile[key])) {
|
|
577
|
-
issue(issues, `materialization_plan.workload_profiles[${index}].${key}`, 'must be a non-empty exact profile identity field');
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
});
|
|
581
|
-
const compatibilityGroups = recordsAt(plan, 'compatibility_groups', 'materialization_plan.compatibility_groups', issues, false);
|
|
582
|
-
const compatibilityGroupIds = registerIds(compatibilityGroups, 'materialization_plan.compatibility_groups', context.universeIds, issues);
|
|
583
|
-
compatibilityGroups.forEach((group, index) => {
|
|
584
|
-
checkIdList(group.member_subject_ids, `materialization_plan.compatibility_groups[${index}].member_subject_ids`, baseUniverseIds, issues, { nonempty: true });
|
|
585
|
-
});
|
|
586
|
-
const preparedStates = recordsAt(plan, 'prepared_states', 'materialization_plan.prepared_states', issues);
|
|
587
|
-
const preparedStateIds = registerIds(preparedStates, 'materialization_plan.prepared_states', context.universeIds, issues);
|
|
588
|
-
preparedStates.forEach((state, index) => validateExactPreparedStateIdentity(state, `materialization_plan.prepared_states[${index}]`, workloadProfileIds, workloadProfilesById, compatibilityGroupIds, context.evidenceById, issues));
|
|
589
|
-
const cold = mappingAt(plan, 'cold_preparation', 'materialization_plan.cold_preparation', issues);
|
|
590
|
-
const hot = mappingAt(plan, 'hot_launch', 'materialization_plan.hot_launch', issues);
|
|
591
|
-
if (!cold || !hot)
|
|
592
|
-
return;
|
|
593
|
-
const refresh = mappingAt(cold, 'refresh', 'materialization_plan.cold_preparation.refresh', issues);
|
|
594
|
-
if (refresh) {
|
|
595
|
-
if (typeof refresh.strategy !== 'string' || !REFRESH_STRATEGIES.has(refresh.strategy)) {
|
|
596
|
-
issue(issues, 'materialization_plan.cold_preparation.refresh.strategy', 'must be "scheduled", "on-source-cut-change", "continuous", or "manual"');
|
|
597
|
-
}
|
|
598
|
-
if (!isPositiveInteger(refresh.max_age_seconds)) {
|
|
599
|
-
issue(issues, 'materialization_plan.cold_preparation.refresh.max_age_seconds', 'must be a positive integer freshness bound');
|
|
600
|
-
}
|
|
601
|
-
checkEvidenceIdList(refresh.freshness_evidence_ids, 'materialization_plan.cold_preparation.refresh.freshness_evidence_ids', context.evidenceById, issues, readinessStatus === 'cold-preparable' || readinessStatus === 'hot-path-proven');
|
|
602
|
-
}
|
|
603
|
-
const coldRequiredStatus = readinessStatus === 'cold-preparable' || readinessStatus === 'hot-path-proven'
|
|
604
|
-
? 'adapter-verified'
|
|
605
|
-
: 'declared';
|
|
606
|
-
const coldResult = validatePhase(cold, 'cold_preparation', context.universeIds, context.universeIds, preparedStateIds, context.evidenceById, issues, coldRequiredStatus);
|
|
607
|
-
const hotRequiredStatus = readinessStatus === 'hot-path-proven'
|
|
608
|
-
? 'materialization-probed'
|
|
609
|
-
: 'declared';
|
|
610
|
-
const hotResult = validatePhase(hot, 'hot_launch', context.universeIds, context.universeIds, preparedStateIds, context.evidenceById, issues, hotRequiredStatus);
|
|
611
|
-
const consumedPreparedStateIds = checkIdList(hot.consumes_prepared_state_ids, 'materialization_plan.hot_launch.consumes_prepared_state_ids', preparedStateIds, issues, { nonempty: readinessStatus === 'hot-path-proven' });
|
|
612
|
-
if (readinessStatus === 'cold-preparable' || readinessStatus === 'hot-path-proven') {
|
|
613
|
-
if (preparedStates.length === 0) {
|
|
614
|
-
issue(issues, 'materialization_plan.prepared_states', `must be non-empty for readiness status "${readinessStatus}"`);
|
|
615
|
-
}
|
|
616
|
-
if (coldResult.steps.length === 0) {
|
|
617
|
-
issue(issues, 'materialization_plan.cold_preparation.steps', `must be non-empty for readiness status "${readinessStatus}"`);
|
|
618
|
-
}
|
|
619
|
-
preparedStateIds.forEach((id) => {
|
|
620
|
-
if (!coldResult.producedPreparedStateIds.has(id)) {
|
|
621
|
-
issue(issues, 'materialization_plan.cold_preparation.steps', `prepared state "${id}" must be produced by a cold-preparation step`);
|
|
622
|
-
}
|
|
623
|
-
});
|
|
624
|
-
}
|
|
625
|
-
consumedPreparedStateIds.forEach((id) => {
|
|
626
|
-
if (!coldResult.producedPreparedStateIds.has(id)) {
|
|
627
|
-
issue(issues, 'materialization_plan.hot_launch.consumes_prepared_state_ids', `consumed prepared state "${id}" must be produced by cold_preparation`);
|
|
628
|
-
}
|
|
629
|
-
});
|
|
630
|
-
const injection = mappingAt(hot, 'arbitrary_verification_commit_injection', 'materialization_plan.hot_launch.arbitrary_verification_commit_injection', issues);
|
|
631
|
-
if (injection) {
|
|
632
|
-
if (typeof injection.supported !== 'boolean') {
|
|
633
|
-
issue(issues, 'materialization_plan.hot_launch.arbitrary_verification_commit_injection.supported', 'must be a boolean');
|
|
634
|
-
}
|
|
635
|
-
if (injection.supported === true) {
|
|
636
|
-
checkId(injection.step_id, 'materialization_plan.hot_launch.arbitrary_verification_commit_injection.step_id', hotResult.stepIds, issues);
|
|
637
|
-
checkEvidenceIdList(injection.production_recipe_equivalence_evidence_ids, 'materialization_plan.hot_launch.arbitrary_verification_commit_injection.production_recipe_equivalence_evidence_ids', context.evidenceById, issues, true);
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
const freshUniversePerGeneratedCase = mappingAt(hot, 'fresh_universe_per_generated_case', 'materialization_plan.hot_launch.fresh_universe_per_generated_case', issues);
|
|
641
|
-
if (freshUniversePerGeneratedCase) {
|
|
642
|
-
if (typeof freshUniversePerGeneratedCase.enforced !== 'boolean') {
|
|
643
|
-
issue(issues, 'materialization_plan.hot_launch.fresh_universe_per_generated_case.enforced', 'must be a boolean');
|
|
644
|
-
}
|
|
645
|
-
if (freshUniversePerGeneratedCase.enforced === true) {
|
|
646
|
-
checkId(freshUniversePerGeneratedCase.step_id, 'materialization_plan.hot_launch.fresh_universe_per_generated_case.step_id', hotResult.stepIds, issues);
|
|
647
|
-
if (freshUniversePerGeneratedCase.parent_write_protected !== true) {
|
|
648
|
-
issue(issues, 'materialization_plan.hot_launch.fresh_universe_per_generated_case.parent_write_protected', 'must be true when generated-case universe isolation is enforced');
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
if (hot.achieved_generated_case_fanout !== undefined &&
|
|
653
|
-
!isNonnegativeInteger(hot.achieved_generated_case_fanout)) {
|
|
654
|
-
issue(issues, 'materialization_plan.hot_launch.achieved_generated_case_fanout', 'must be a non-negative integer');
|
|
655
|
-
}
|
|
656
|
-
if (hot.achieved_generated_case_fanout === undefined) {
|
|
657
|
-
issue(issues, 'materialization_plan.hot_launch.achieved_generated_case_fanout', 'must be a non-negative integer');
|
|
658
|
-
}
|
|
659
|
-
if (isPositiveInteger(generatedCaseFanout?.hard_max_n) &&
|
|
660
|
-
isNonnegativeInteger(hot.achieved_generated_case_fanout) &&
|
|
661
|
-
hot.achieved_generated_case_fanout > generatedCaseFanout.hard_max_n) {
|
|
662
|
-
issue(issues, 'materialization_plan.hot_launch.achieved_generated_case_fanout', 'must not exceed materialization_plan.generated_case_fanout.hard_max_n');
|
|
663
|
-
}
|
|
664
|
-
if (typeof hot.latency_basis !== 'string' || !LATENCY_BASES.has(hot.latency_basis)) {
|
|
665
|
-
issue(issues, 'materialization_plan.hot_launch.latency_basis', 'must be "assumed" or "measured"');
|
|
666
|
-
}
|
|
667
|
-
if (hot.observed_end_to_end_p95_ms !== undefined &&
|
|
668
|
-
!isNonnegativeInteger(hot.observed_end_to_end_p95_ms)) {
|
|
669
|
-
issue(issues, 'materialization_plan.hot_launch.observed_end_to_end_p95_ms', 'must be a non-negative integer number of measured milliseconds when present');
|
|
670
|
-
}
|
|
671
|
-
if (readinessStatus === 'hot-path-proven') {
|
|
672
|
-
validateHotPathInventoryCoverage(coldResult.steps, hotResult.steps, context.coverageSubjects, issues);
|
|
673
|
-
if (hotResult.steps.length === 0) {
|
|
674
|
-
issue(issues, 'materialization_plan.hot_launch.steps', 'must be non-empty for hot-path-proven');
|
|
675
|
-
}
|
|
676
|
-
if (injection?.supported !== true) {
|
|
677
|
-
issue(issues, 'materialization_plan.hot_launch.arbitrary_verification_commit_injection.supported', 'must be true for hot-path-proven');
|
|
678
|
-
}
|
|
679
|
-
if (freshUniversePerGeneratedCase?.enforced !== true) {
|
|
680
|
-
issue(issues, 'materialization_plan.hot_launch.fresh_universe_per_generated_case.enforced', 'must be true for hot-path-proven');
|
|
681
|
-
}
|
|
682
|
-
if (hot.latency_basis !== 'measured') {
|
|
683
|
-
issue(issues, 'materialization_plan.hot_launch.latency_basis', 'must be "measured" for hot-path-proven');
|
|
684
|
-
}
|
|
685
|
-
if (!isNonnegativeInteger(hot.observed_end_to_end_p95_ms)) {
|
|
686
|
-
issue(issues, 'materialization_plan.hot_launch.observed_end_to_end_p95_ms', 'must report a measured end-to-end p95 for hot-path-proven');
|
|
687
|
-
}
|
|
688
|
-
else if (isPositiveInteger(plan.target_slo_ms) &&
|
|
689
|
-
hot.observed_end_to_end_p95_ms > plan.target_slo_ms) {
|
|
690
|
-
issue(issues, 'materialization_plan.hot_launch.observed_end_to_end_p95_ms', 'must be at or below the hot-launch allocation materialization_plan.target_slo_ms for hot-path-proven');
|
|
691
|
-
}
|
|
692
|
-
if (isPositiveInteger(generatedCaseFanout?.target_n) &&
|
|
693
|
-
(!isPositiveInteger(hot.achieved_generated_case_fanout) ||
|
|
694
|
-
hot.achieved_generated_case_fanout < generatedCaseFanout.target_n)) {
|
|
695
|
-
issue(issues, 'materialization_plan.hot_launch.achieved_generated_case_fanout', 'must be at least materialization_plan.generated_case_fanout.target_n for hot-path-proven');
|
|
696
|
-
}
|
|
697
|
-
hotResult.steps.forEach((step, index) => {
|
|
698
|
-
if (step.latency_basis !== 'measured') {
|
|
699
|
-
issue(issues, `materialization_plan.hot_launch.steps[${index}].latency_basis`, 'must be "measured" for hot-path-proven');
|
|
700
|
-
}
|
|
701
|
-
});
|
|
702
|
-
validateHotPathProbeEvidence(hot, readiness?.scope, injection, freshUniversePerGeneratedCase, consumedPreparedStateIds, hot.achieved_generated_case_fanout, context.evidenceById, issues);
|
|
703
|
-
}
|
|
704
|
-
}
|