@kungfu-tech/buildchain 3.0.7-alpha.0 → 3.0.7
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/actions/promote-buildchain-ref/README.md +10 -0
- package/contracts/auditable-demo-scenario-v1.schema.json +1 -1
- package/contracts/engineering-housekeeper-v1.schema.json +143 -0
- package/contracts/fixtures/engineering-housekeeper-v1/cases.json +68 -0
- package/dist/site/buildchain-contract.json +24 -24
- package/dist/site/buildchain-site.json +91 -30
- package/dist/site/capability-registry.json +3 -3
- package/dist/site/controller-registry.json +6 -2
- package/dist/site/kfd-claims.json +122 -11
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +8 -7
- package/dist/site/node-api-registry.json +683 -105
- package/dist/site/page-registry.json +80 -19
- package/dist/site/public-surface-audit.json +98 -7
- package/dist/site/publication-authority-registry.json +81 -1
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-provenance.json +2 -0
- package/dist/site/site-manifest.json +11 -11
- package/dist/site/workflow-registry.json +119 -2
- package/docs/MAP.md +1 -0
- package/docs/auditable-demo.md +2 -2
- package/docs/dev-delivery-warrant.md +49 -4
- package/docs/engineering-housekeeper.md +138 -0
- package/docs/lifecycle-protocol.md +4 -2
- package/docs/node-api-reference.md +277 -212
- package/docs/release-governance.md +17 -2
- package/docs/release-tail-provider-plane.md +1 -1
- package/docs/reusable-build-surface.md +11 -0
- package/package.json +4 -1
- package/packages/core/artifact-signing.js +61 -0
- package/packages/core/buildchain-config.js +66 -6
- package/packages/core/buildchain-publication-authority.js +4 -0
- package/packages/core/controller-evidence.js +2 -1
- package/packages/core/dev-delivery-warrant-cancellation.js +1 -0
- package/packages/core/dev-delivery-warrant-shadow.js +502 -0
- package/packages/core/dev-delivery-warrant.js +15 -6
- package/packages/core/diagnostics.js +8 -3
- package/packages/core/engineering-housekeeper-github-client.js +222 -0
- package/packages/core/engineering-housekeeper-github.js +501 -0
- package/packages/core/engineering-housekeeper.js +259 -0
- package/packages/core/index.js +3 -0
- package/packages/core/kfd-gate.js +45 -15
- package/packages/core/publication-rehearsal-runtime.js +13 -1
- package/packages/core/release-passport.js +130 -20
- package/scripts/assemble-self-publication-admission.mjs +1 -1
- package/scripts/audit-publication-control-plane.mjs +1 -1
- package/scripts/auditable-demo-bundle-verification.mjs +2 -3
- package/scripts/auditable-demo-platform.mjs +2 -2
- package/scripts/auditable-demo-renditions.mjs +1 -1
- package/scripts/auditable-demo.mjs +2 -2
- package/scripts/build-contract-core.mjs +8 -3
- package/scripts/build-standalone-binary.mjs +14 -3
- package/scripts/check-inventory.mjs +3 -1
- package/scripts/dev-delivery-warrant.mjs +31 -4
- package/scripts/dev-pr-auto-merge.mjs +30 -4
- package/scripts/dev-pr-delivery-warrant.mjs +50 -0
- package/scripts/engineering-housekeeper-workflow.mjs +394 -0
- package/scripts/generate-site-bundle.mjs +23 -4
- package/scripts/inspect-artifact-signing-requests.mjs +6 -0
- package/scripts/materialize-self-release-candidate-version.mjs +6 -0
- package/scripts/publication-commit-evidence.mjs +69 -23
- package/scripts/release-candidate-resolver.mjs +16 -10
- package/scripts/resume-from-candidate-run.mjs +123 -9
- package/scripts/seal-artifact-signing-requests.mjs +6 -0
- package/scripts/site-capability-metadata.mjs +2 -0
- package/scripts/web-surface-core.mjs +8 -2
- package/scripts/workflow-call-contract.mjs +1 -1
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
import {
|
|
2
|
+
devDeliveryClone as clone,
|
|
3
|
+
devDeliveryContentRoot,
|
|
4
|
+
devDeliveryExactRoot as exactRoot,
|
|
5
|
+
devDeliveryExactSha as exactSha,
|
|
6
|
+
devDeliveryPositiveInteger as positiveInteger,
|
|
7
|
+
devDeliveryText as text,
|
|
8
|
+
devDeliveryTimestamp as timestamp,
|
|
9
|
+
} from "./dev-delivery-common.js";
|
|
10
|
+
import {
|
|
11
|
+
normalizeDevDeliveryQueue,
|
|
12
|
+
rankDevDeliveryCandidates,
|
|
13
|
+
} from "./dev-delivery-warrant.js";
|
|
14
|
+
|
|
15
|
+
export const DEV_DELIVERY_WARRANT_SHADOW_OBSERVATION_SCHEMA =
|
|
16
|
+
"kungfu.buildchain.dev-delivery-warrant-shadow-observation/v1";
|
|
17
|
+
export const DEV_DELIVERY_WARRANT_SHADOW_PLAN_SCHEMA =
|
|
18
|
+
"kungfu.buildchain.dev-delivery-warrant-shadow-plan/v1";
|
|
19
|
+
export const DEV_DELIVERY_WARRANT_SHADOW_QUALIFICATION_SCHEMA =
|
|
20
|
+
"kungfu.buildchain.dev-delivery-warrant-shadow-qualification/v1";
|
|
21
|
+
|
|
22
|
+
const EVIDENCE_STATES = Object.freeze({
|
|
23
|
+
projectCut: "qualified",
|
|
24
|
+
approval: "approved",
|
|
25
|
+
requiredChecks: "passed",
|
|
26
|
+
status: "ready",
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
function nonNegativeInteger(value, label, fallback = 0) {
|
|
30
|
+
const parsed = Number(value ?? fallback);
|
|
31
|
+
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
32
|
+
throw new Error(`${label} must be a non-negative integer`);
|
|
33
|
+
}
|
|
34
|
+
return parsed;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function boolean(value, label) {
|
|
38
|
+
if (typeof value !== "boolean") throw new Error(`${label} must be boolean`);
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function evidenceBinding(input, label) {
|
|
43
|
+
const binding = input || {};
|
|
44
|
+
return {
|
|
45
|
+
candidateId: exactRoot(binding.candidateId, `${label}.candidateId`),
|
|
46
|
+
sourceHead: exactSha(binding.sourceHead, `${label}.sourceHead`),
|
|
47
|
+
baseHead: exactSha(binding.baseHead, `${label}.baseHead`),
|
|
48
|
+
root: exactRoot(binding.root, `${label}.root`),
|
|
49
|
+
state: text(binding.state),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function laneBinding(input) {
|
|
54
|
+
const binding = input || {};
|
|
55
|
+
return {
|
|
56
|
+
candidateId: exactRoot(binding.candidateId, "binding.candidateId"),
|
|
57
|
+
sourceHead: exactSha(binding.sourceHead, "binding.sourceHead"),
|
|
58
|
+
baseHead: exactSha(binding.baseHead, "binding.baseHead"),
|
|
59
|
+
queueGeneration: nonNegativeInteger(
|
|
60
|
+
binding.queueGeneration,
|
|
61
|
+
"binding.queueGeneration",
|
|
62
|
+
),
|
|
63
|
+
queueStateRoot: exactRoot(binding.queueStateRoot, "binding.queueStateRoot"),
|
|
64
|
+
projectedBaseRoot: exactRoot(
|
|
65
|
+
binding.projectedBaseRoot,
|
|
66
|
+
"binding.projectedBaseRoot",
|
|
67
|
+
),
|
|
68
|
+
warrantFencingToken: binding.warrantFencingToken
|
|
69
|
+
? exactRoot(binding.warrantFencingToken, "binding.warrantFencingToken")
|
|
70
|
+
: null,
|
|
71
|
+
warrantGeneration:
|
|
72
|
+
binding.warrantGeneration == null
|
|
73
|
+
? null
|
|
74
|
+
: positiveInteger(
|
|
75
|
+
binding.warrantGeneration,
|
|
76
|
+
"binding.warrantGeneration",
|
|
77
|
+
),
|
|
78
|
+
projectCut: evidenceBinding(binding.projectCut, "binding.projectCut"),
|
|
79
|
+
approval: evidenceBinding(binding.approval, "binding.approval"),
|
|
80
|
+
requiredChecks: evidenceBinding(
|
|
81
|
+
binding.requiredChecks,
|
|
82
|
+
"binding.requiredChecks",
|
|
83
|
+
),
|
|
84
|
+
status: evidenceBinding(binding.status, "binding.status"),
|
|
85
|
+
lease: {
|
|
86
|
+
...evidenceBinding(binding.lease, "binding.lease"),
|
|
87
|
+
state: text(binding.lease?.state),
|
|
88
|
+
},
|
|
89
|
+
conflictKeys: [
|
|
90
|
+
...new Set((binding.conflictKeys || []).map((entry) => text(entry))),
|
|
91
|
+
]
|
|
92
|
+
.filter(Boolean)
|
|
93
|
+
.sort(),
|
|
94
|
+
expectedEligible:
|
|
95
|
+
binding.expectedEligible == null
|
|
96
|
+
? null
|
|
97
|
+
: boolean(binding.expectedEligible, "binding.expectedEligible"),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function normalizeObservation(input) {
|
|
102
|
+
const observation = clone(input || {});
|
|
103
|
+
if (observation.schema !== DEV_DELIVERY_WARRANT_SHADOW_OBSERVATION_SCHEMA) {
|
|
104
|
+
throw new Error(
|
|
105
|
+
`shadow observation must use ${DEV_DELIVERY_WARRANT_SHADOW_OBSERVATION_SCHEMA}`,
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
const queue = normalizeDevDeliveryQueue(observation.queue);
|
|
109
|
+
const normalized = {
|
|
110
|
+
schema: observation.schema,
|
|
111
|
+
observationId: text(observation.observationId),
|
|
112
|
+
kind: text(observation.kind || "replay"),
|
|
113
|
+
observedAt: timestamp(observation.observedAt, "observation.observedAt"),
|
|
114
|
+
protectedBaseHead: exactSha(
|
|
115
|
+
observation.protectedBaseHead,
|
|
116
|
+
"observation.protectedBaseHead",
|
|
117
|
+
),
|
|
118
|
+
projectedBaseRoot: exactRoot(
|
|
119
|
+
observation.projectedBaseRoot,
|
|
120
|
+
"observation.projectedBaseRoot",
|
|
121
|
+
),
|
|
122
|
+
queue,
|
|
123
|
+
nativeQueue: {
|
|
124
|
+
occupied: boolean(
|
|
125
|
+
observation.nativeQueue?.occupied,
|
|
126
|
+
"observation.nativeQueue.occupied",
|
|
127
|
+
),
|
|
128
|
+
entryCount: nonNegativeInteger(
|
|
129
|
+
observation.nativeQueue?.entryCount,
|
|
130
|
+
"observation.nativeQueue.entryCount",
|
|
131
|
+
),
|
|
132
|
+
root: exactRoot(
|
|
133
|
+
observation.nativeQueue?.root,
|
|
134
|
+
"observation.nativeQueue.root",
|
|
135
|
+
),
|
|
136
|
+
},
|
|
137
|
+
candidateBindings: (observation.candidateBindings || []).map(laneBinding),
|
|
138
|
+
metrics: {
|
|
139
|
+
baselineQueueWaitSeconds: nonNegativeInteger(
|
|
140
|
+
observation.metrics?.baselineQueueWaitSeconds,
|
|
141
|
+
"observation.metrics.baselineQueueWaitSeconds",
|
|
142
|
+
),
|
|
143
|
+
shadowQueueWaitSeconds: nonNegativeInteger(
|
|
144
|
+
observation.metrics?.shadowQueueWaitSeconds,
|
|
145
|
+
"observation.metrics.shadowQueueWaitSeconds",
|
|
146
|
+
),
|
|
147
|
+
additionalCheckSeconds: nonNegativeInteger(
|
|
148
|
+
observation.metrics?.additionalCheckSeconds,
|
|
149
|
+
"observation.metrics.additionalCheckSeconds",
|
|
150
|
+
),
|
|
151
|
+
additionalRunnerSeconds: nonNegativeInteger(
|
|
152
|
+
observation.metrics?.additionalRunnerSeconds,
|
|
153
|
+
"observation.metrics.additionalRunnerSeconds",
|
|
154
|
+
),
|
|
155
|
+
ambiguous: boolean(
|
|
156
|
+
observation.metrics?.ambiguous ?? false,
|
|
157
|
+
"observation.metrics.ambiguous",
|
|
158
|
+
),
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
if (!normalized.observationId) {
|
|
162
|
+
throw new Error("observation.observationId is required");
|
|
163
|
+
}
|
|
164
|
+
const bindingIds = normalized.candidateBindings.map(
|
|
165
|
+
(binding) => binding.candidateId,
|
|
166
|
+
);
|
|
167
|
+
if (new Set(bindingIds).size !== bindingIds.length) {
|
|
168
|
+
throw new Error("shadow observation contains duplicate candidate bindings");
|
|
169
|
+
}
|
|
170
|
+
return normalized;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function productionOrder(queue, now) {
|
|
174
|
+
const active = queue.activeWarrant
|
|
175
|
+
? queue.candidates.find(
|
|
176
|
+
(candidate) =>
|
|
177
|
+
candidate.candidateId === queue.activeWarrant.candidateId,
|
|
178
|
+
)
|
|
179
|
+
: null;
|
|
180
|
+
const queued = rankDevDeliveryCandidates(queue, { now }).map(
|
|
181
|
+
(entry) => entry.candidate,
|
|
182
|
+
);
|
|
183
|
+
return active ? [active, ...queued] : queued;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function bindingReasons({ binding, candidate, observation }) {
|
|
187
|
+
const reasons = [];
|
|
188
|
+
const expect = (condition, reason) => {
|
|
189
|
+
if (!condition) reasons.push(reason);
|
|
190
|
+
};
|
|
191
|
+
expect(
|
|
192
|
+
binding.candidateId === candidate.candidateId,
|
|
193
|
+
"candidate-id-mismatch",
|
|
194
|
+
);
|
|
195
|
+
expect(binding.sourceHead === candidate.sourceHead, "stale-source-head");
|
|
196
|
+
expect(binding.baseHead === observation.protectedBaseHead, "stale-base-head");
|
|
197
|
+
expect(
|
|
198
|
+
binding.queueGeneration === observation.queue.generation,
|
|
199
|
+
"stale-queue-generation",
|
|
200
|
+
);
|
|
201
|
+
expect(
|
|
202
|
+
binding.queueStateRoot === observation.queue.stateRoot,
|
|
203
|
+
"stale-queue-state-root",
|
|
204
|
+
);
|
|
205
|
+
expect(
|
|
206
|
+
binding.projectedBaseRoot === observation.projectedBaseRoot,
|
|
207
|
+
"incompatible-projected-base",
|
|
208
|
+
);
|
|
209
|
+
for (const [name, acceptedState] of Object.entries(EVIDENCE_STATES)) {
|
|
210
|
+
const evidence = binding[name];
|
|
211
|
+
expect(
|
|
212
|
+
evidence.candidateId === candidate.candidateId,
|
|
213
|
+
`${name}-candidate-mismatch`,
|
|
214
|
+
);
|
|
215
|
+
expect(
|
|
216
|
+
evidence.sourceHead === candidate.sourceHead,
|
|
217
|
+
`${name}-head-mismatch`,
|
|
218
|
+
);
|
|
219
|
+
expect(
|
|
220
|
+
evidence.baseHead === observation.protectedBaseHead,
|
|
221
|
+
`${name}-base-mismatch`,
|
|
222
|
+
);
|
|
223
|
+
expect(evidence.state === acceptedState, `${name}-not-${acceptedState}`);
|
|
224
|
+
}
|
|
225
|
+
expect(
|
|
226
|
+
binding.lease.candidateId === candidate.candidateId,
|
|
227
|
+
"lease-candidate-mismatch",
|
|
228
|
+
);
|
|
229
|
+
expect(
|
|
230
|
+
binding.lease.sourceHead === candidate.sourceHead,
|
|
231
|
+
"lease-head-mismatch",
|
|
232
|
+
);
|
|
233
|
+
expect(
|
|
234
|
+
binding.lease.baseHead === observation.protectedBaseHead,
|
|
235
|
+
"lease-base-mismatch",
|
|
236
|
+
);
|
|
237
|
+
const active = observation.queue.activeWarrant;
|
|
238
|
+
if (active?.candidateId === candidate.candidateId) {
|
|
239
|
+
expect(binding.lease.state === "active", "active-lease-not-bound");
|
|
240
|
+
expect(
|
|
241
|
+
binding.warrantFencingToken === active.fencingToken,
|
|
242
|
+
"stale-warrant-fence",
|
|
243
|
+
);
|
|
244
|
+
expect(
|
|
245
|
+
binding.warrantGeneration === active.generation,
|
|
246
|
+
"stale-warrant-generation",
|
|
247
|
+
);
|
|
248
|
+
} else {
|
|
249
|
+
expect(
|
|
250
|
+
binding.lease.state === "available",
|
|
251
|
+
"queued-lane-lease-unavailable",
|
|
252
|
+
);
|
|
253
|
+
expect(binding.warrantFencingToken === null, "queued-lane-has-live-fence");
|
|
254
|
+
expect(
|
|
255
|
+
binding.warrantGeneration === null,
|
|
256
|
+
"queued-lane-has-live-generation",
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
return reasons;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function aliasedLaneIndexes(lanes) {
|
|
263
|
+
const indexes = new Set();
|
|
264
|
+
const roots = new Map();
|
|
265
|
+
const fields = [
|
|
266
|
+
"projectCut",
|
|
267
|
+
"approval",
|
|
268
|
+
"requiredChecks",
|
|
269
|
+
"status",
|
|
270
|
+
"lease",
|
|
271
|
+
];
|
|
272
|
+
lanes.forEach((lane, index) => {
|
|
273
|
+
if (!lane.binding) return;
|
|
274
|
+
for (const field of fields) {
|
|
275
|
+
const key = `${field}:${lane.binding[field].root}`;
|
|
276
|
+
const prior = roots.get(key);
|
|
277
|
+
if (prior != null) {
|
|
278
|
+
indexes.add(prior);
|
|
279
|
+
indexes.add(index);
|
|
280
|
+
} else roots.set(key, index);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
return indexes;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function conflictingLaneIndexes(lanes) {
|
|
287
|
+
const indexes = new Set();
|
|
288
|
+
for (let left = 0; left < lanes.length; left += 1) {
|
|
289
|
+
for (let right = left + 1; right < lanes.length; right += 1) {
|
|
290
|
+
if (!lanes[left].binding || !lanes[right].binding) continue;
|
|
291
|
+
const rightKeys = new Set(lanes[right].binding.conflictKeys);
|
|
292
|
+
if (lanes[left].binding.conflictKeys.some((key) => rightKeys.has(key))) {
|
|
293
|
+
indexes.add(left);
|
|
294
|
+
indexes.add(right);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return indexes;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export function planDevDeliveryWarrantShadow(
|
|
302
|
+
input,
|
|
303
|
+
{ maxConcurrency = 2 } = {},
|
|
304
|
+
) {
|
|
305
|
+
const requested = positiveInteger(maxConcurrency, "maxConcurrency");
|
|
306
|
+
if (requested > 2) throw new Error("shadow maxConcurrency cannot exceed 2");
|
|
307
|
+
const observation = normalizeObservation(input);
|
|
308
|
+
const order = productionOrder(observation.queue, observation.observedAt);
|
|
309
|
+
const candidates = order.slice(0, requested);
|
|
310
|
+
const bindings = new Map(
|
|
311
|
+
observation.candidateBindings.map((binding) => [
|
|
312
|
+
binding.candidateId,
|
|
313
|
+
binding,
|
|
314
|
+
]),
|
|
315
|
+
);
|
|
316
|
+
const lanes = candidates.map((candidate) => {
|
|
317
|
+
const binding = bindings.get(candidate.candidateId);
|
|
318
|
+
const reasonCodes = binding
|
|
319
|
+
? bindingReasons({ binding, candidate, observation })
|
|
320
|
+
: ["candidate-binding-missing"];
|
|
321
|
+
return { candidate, binding, reasonCodes };
|
|
322
|
+
});
|
|
323
|
+
if (observation.nativeQueue.occupied) {
|
|
324
|
+
for (const lane of lanes) lane.reasonCodes.push("native-queue-occupied");
|
|
325
|
+
}
|
|
326
|
+
for (const index of aliasedLaneIndexes(lanes)) {
|
|
327
|
+
lanes[index].reasonCodes.push("cross-lane-binding-alias");
|
|
328
|
+
}
|
|
329
|
+
for (const index of conflictingLaneIndexes(lanes)) {
|
|
330
|
+
lanes[index].reasonCodes.push("cross-lane-conflict");
|
|
331
|
+
}
|
|
332
|
+
const plannedLanes = lanes.map((lane) => {
|
|
333
|
+
const reasonCodes = [...new Set(lane.reasonCodes)].sort();
|
|
334
|
+
const laneBody = {
|
|
335
|
+
schema: "kungfu.buildchain.dev-delivery-warrant-shadow-lane/v1",
|
|
336
|
+
candidateId: lane.candidate.candidateId,
|
|
337
|
+
pullRequestNumber: lane.candidate.pullRequestNumber,
|
|
338
|
+
sourceHead: lane.candidate.sourceHead,
|
|
339
|
+
baseHead: observation.protectedBaseHead,
|
|
340
|
+
queueStateRoot: observation.queue.stateRoot,
|
|
341
|
+
bindingRoot: lane.binding ? devDeliveryContentRoot(lane.binding) : null,
|
|
342
|
+
accepted: reasonCodes.length === 0,
|
|
343
|
+
reasonCodes,
|
|
344
|
+
effects: [],
|
|
345
|
+
productionAuthority: false,
|
|
346
|
+
};
|
|
347
|
+
return {
|
|
348
|
+
...laneBody,
|
|
349
|
+
laneId: devDeliveryContentRoot({
|
|
350
|
+
schema: laneBody.schema,
|
|
351
|
+
candidateId: laneBody.candidateId,
|
|
352
|
+
sourceHead: laneBody.sourceHead,
|
|
353
|
+
baseHead: laneBody.baseHead,
|
|
354
|
+
queueStateRoot: laneBody.queueStateRoot,
|
|
355
|
+
}),
|
|
356
|
+
};
|
|
357
|
+
});
|
|
358
|
+
const productionCandidateId = order[0]?.candidateId || null;
|
|
359
|
+
const body = {
|
|
360
|
+
schema: DEV_DELIVERY_WARRANT_SHADOW_PLAN_SCHEMA,
|
|
361
|
+
observationId: observation.observationId,
|
|
362
|
+
observationRoot: devDeliveryContentRoot(observation),
|
|
363
|
+
observedAt: observation.observedAt,
|
|
364
|
+
maxConcurrency: requested,
|
|
365
|
+
productionCandidateId,
|
|
366
|
+
singleFlightParity: {
|
|
367
|
+
evaluated: requested === 1,
|
|
368
|
+
candidateIdentityMatches:
|
|
369
|
+
requested === 1 &&
|
|
370
|
+
(plannedLanes[0]?.candidateId || null) === productionCandidateId,
|
|
371
|
+
},
|
|
372
|
+
lanes: plannedLanes,
|
|
373
|
+
acceptedLaneCount: plannedLanes.filter((lane) => lane.accepted).length,
|
|
374
|
+
rejectedLaneCount: plannedLanes.filter((lane) => !lane.accepted).length,
|
|
375
|
+
deferredCandidateIds: order
|
|
376
|
+
.slice(requested)
|
|
377
|
+
.map((candidate) => candidate.candidateId),
|
|
378
|
+
metrics: observation.metrics,
|
|
379
|
+
decision:
|
|
380
|
+
plannedLanes.some((lane) => lane.accepted) &&
|
|
381
|
+
!observation.nativeQueue.occupied
|
|
382
|
+
? "qualified-shadow-only"
|
|
383
|
+
: "hold",
|
|
384
|
+
effects: [],
|
|
385
|
+
mutationAllowed: false,
|
|
386
|
+
productionAuthority: "unchanged-single-flight",
|
|
387
|
+
rolloutAuthorized: false,
|
|
388
|
+
};
|
|
389
|
+
return { ...body, planRoot: devDeliveryContentRoot(body) };
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function thresholds(input = {}) {
|
|
393
|
+
return {
|
|
394
|
+
minObservationCount: positiveInteger(
|
|
395
|
+
input.minObservationCount,
|
|
396
|
+
"thresholds.minObservationCount",
|
|
397
|
+
8,
|
|
398
|
+
),
|
|
399
|
+
minEligibleOverlapCount: positiveInteger(
|
|
400
|
+
input.minEligibleOverlapCount,
|
|
401
|
+
"thresholds.minEligibleOverlapCount",
|
|
402
|
+
2,
|
|
403
|
+
),
|
|
404
|
+
minProjectedQueueWaitBenefitSeconds: nonNegativeInteger(
|
|
405
|
+
input.minProjectedQueueWaitBenefitSeconds,
|
|
406
|
+
"thresholds.minProjectedQueueWaitBenefitSeconds",
|
|
407
|
+
300,
|
|
408
|
+
),
|
|
409
|
+
maxAdditionalRunnerSeconds: nonNegativeInteger(
|
|
410
|
+
input.maxAdditionalRunnerSeconds,
|
|
411
|
+
"thresholds.maxAdditionalRunnerSeconds",
|
|
412
|
+
7200,
|
|
413
|
+
),
|
|
414
|
+
maxAmbiguityCount: nonNegativeInteger(
|
|
415
|
+
input.maxAmbiguityCount,
|
|
416
|
+
"thresholds.maxAmbiguityCount",
|
|
417
|
+
0,
|
|
418
|
+
),
|
|
419
|
+
maxFalsePositiveCount: nonNegativeInteger(
|
|
420
|
+
input.maxFalsePositiveCount,
|
|
421
|
+
"thresholds.maxFalsePositiveCount",
|
|
422
|
+
0,
|
|
423
|
+
),
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
export function qualifyDevDeliveryWarrantShadow(input = {}) {
|
|
428
|
+
const observations = Array.isArray(input.observations)
|
|
429
|
+
? input.observations
|
|
430
|
+
: [];
|
|
431
|
+
const policy = thresholds(input.thresholds);
|
|
432
|
+
const plans = observations.map((observation) =>
|
|
433
|
+
planDevDeliveryWarrantShadow(observation, { maxConcurrency: 2 }),
|
|
434
|
+
);
|
|
435
|
+
const metrics = plans.reduce(
|
|
436
|
+
(summary, plan) => {
|
|
437
|
+
if (plan.lanes.filter((lane) => lane.accepted).length === 2) {
|
|
438
|
+
summary.eligibleOverlapCount += 1;
|
|
439
|
+
}
|
|
440
|
+
summary.projectedQueueWaitBenefitSeconds += Math.max(
|
|
441
|
+
0,
|
|
442
|
+
plan.metrics.baselineQueueWaitSeconds -
|
|
443
|
+
plan.metrics.shadowQueueWaitSeconds,
|
|
444
|
+
);
|
|
445
|
+
summary.additionalCheckSeconds += plan.metrics.additionalCheckSeconds;
|
|
446
|
+
summary.additionalRunnerSeconds += plan.metrics.additionalRunnerSeconds;
|
|
447
|
+
if (plan.metrics.ambiguous) summary.ambiguityCount += 1;
|
|
448
|
+
const observation = normalizeObservation(
|
|
449
|
+
observations[summary.observationCount],
|
|
450
|
+
);
|
|
451
|
+
const expected = new Map(
|
|
452
|
+
observation.candidateBindings.map((binding) => [
|
|
453
|
+
binding.candidateId,
|
|
454
|
+
binding.expectedEligible,
|
|
455
|
+
]),
|
|
456
|
+
);
|
|
457
|
+
summary.falsePositiveCount += plan.lanes.filter(
|
|
458
|
+
(lane) => lane.accepted && expected.get(lane.candidateId) === false,
|
|
459
|
+
).length;
|
|
460
|
+
summary.observationCount += 1;
|
|
461
|
+
return summary;
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
observationCount: 0,
|
|
465
|
+
eligibleOverlapCount: 0,
|
|
466
|
+
projectedQueueWaitBenefitSeconds: 0,
|
|
467
|
+
additionalCheckSeconds: 0,
|
|
468
|
+
additionalRunnerSeconds: 0,
|
|
469
|
+
ambiguityCount: 0,
|
|
470
|
+
falsePositiveCount: 0,
|
|
471
|
+
},
|
|
472
|
+
);
|
|
473
|
+
const checks = {
|
|
474
|
+
observationCount: metrics.observationCount >= policy.minObservationCount,
|
|
475
|
+
eligibleOverlap:
|
|
476
|
+
metrics.eligibleOverlapCount >= policy.minEligibleOverlapCount,
|
|
477
|
+
projectedQueueWaitBenefit:
|
|
478
|
+
metrics.projectedQueueWaitBenefitSeconds >=
|
|
479
|
+
policy.minProjectedQueueWaitBenefitSeconds,
|
|
480
|
+
runnerCost:
|
|
481
|
+
metrics.additionalRunnerSeconds <= policy.maxAdditionalRunnerSeconds,
|
|
482
|
+
ambiguity: metrics.ambiguityCount <= policy.maxAmbiguityCount,
|
|
483
|
+
falsePositives: metrics.falsePositiveCount <= policy.maxFalsePositiveCount,
|
|
484
|
+
};
|
|
485
|
+
const decision = Object.values(checks).every(Boolean) ? "proceed" : "hold";
|
|
486
|
+
const body = {
|
|
487
|
+
schema: DEV_DELIVERY_WARRANT_SHADOW_QUALIFICATION_SCHEMA,
|
|
488
|
+
decision,
|
|
489
|
+
reasonCodes: Object.entries(checks)
|
|
490
|
+
.filter(([, passed]) => !passed)
|
|
491
|
+
.map(([name]) => `threshold-${name}-not-met`),
|
|
492
|
+
thresholds: policy,
|
|
493
|
+
metrics,
|
|
494
|
+
checks,
|
|
495
|
+
planRoots: plans.map((plan) => plan.planRoot),
|
|
496
|
+
effects: [],
|
|
497
|
+
mutationAllowed: false,
|
|
498
|
+
productionAuthority: "unchanged-single-flight",
|
|
499
|
+
rolloutAuthorized: false,
|
|
500
|
+
};
|
|
501
|
+
return { ...body, qualificationRoot: devDeliveryContentRoot(body), plans };
|
|
502
|
+
}
|
|
@@ -69,6 +69,7 @@ function normalizeCandidate(input, expected) {
|
|
|
69
69
|
closureRoot: exactRoot(input.closureRoot, "closureRoot"),
|
|
70
70
|
dependencyRoot: exactRoot(input.dependencyRoot, "dependencyRoot"),
|
|
71
71
|
toolchainRoot: exactRoot(input.toolchainRoot, "toolchainRoot"),
|
|
72
|
+
...(Object.hasOwn(input, "sourceWorkflowRunId") ? { sourceWorkflowRunId: nonNegativeInteger(input.sourceWorkflowRunId, "candidate sourceWorkflowRunId", 0) } : {}),
|
|
72
73
|
priority: priority(input.priority),
|
|
73
74
|
enqueuedAt: timestamp(input.enqueuedAt, "candidate enqueuedAt"),
|
|
74
75
|
updatedAt: timestamp(input.updatedAt || input.enqueuedAt, "candidate updatedAt"),
|
|
@@ -178,6 +179,7 @@ function submissionReceipt({ before, after, candidate, action, now }) {
|
|
|
178
179
|
closureRoot: candidate.closureRoot,
|
|
179
180
|
dependencyRoot: candidate.dependencyRoot,
|
|
180
181
|
toolchainRoot: candidate.toolchainRoot,
|
|
182
|
+
sourceWorkflowRunId: candidate.sourceWorkflowRunId,
|
|
181
183
|
deliveryClass: candidate.deliveryClass,
|
|
182
184
|
priority: candidate.priority,
|
|
183
185
|
retainedEnqueuedAt: candidate.enqueuedAt,
|
|
@@ -223,7 +225,12 @@ export function submitDevDeliveryCandidate(queueInput, input, { now = new Date()
|
|
|
223
225
|
selected = existing;
|
|
224
226
|
} else {
|
|
225
227
|
if (before.activeWarrant?.candidateId === existing.candidateId) {
|
|
226
|
-
|
|
228
|
+
if (existing.sourceHead !== attemptedCandidate.sourceHead) {
|
|
229
|
+
throw new Error("selected candidate sourceHead cannot change before terminal Warrant closeout");
|
|
230
|
+
}
|
|
231
|
+
action = "active-warrant-retained-noop";
|
|
232
|
+
selected = existing;
|
|
233
|
+
return { candidate: selected, action };
|
|
227
234
|
}
|
|
228
235
|
const headChanged = existing.sourceHead !== attemptedCandidate.sourceHead;
|
|
229
236
|
existing.sourceHead = attemptedCandidate.sourceHead;
|
|
@@ -309,7 +316,7 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
|
|
|
309
316
|
candidateId: queue.activeWarrant.candidateId,
|
|
310
317
|
fencingToken: queue.activeWarrant.fencingToken,
|
|
311
318
|
leaseGeneration: queue.activeWarrant.generation,
|
|
312
|
-
expectedOldStateRoot: queue.stateRoot,
|
|
319
|
+
expectedOldStateRoot: recoveryReceipt?.expectedOldStateRoot || queue.stateRoot,
|
|
313
320
|
nextStateRoot: queue.stateRoot,
|
|
314
321
|
nextAction: "Continue the active delivery attempt; later candidates remain visibly queued.",
|
|
315
322
|
};
|
|
@@ -363,6 +370,7 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
|
|
|
363
370
|
closureRoot: candidate.closureRoot,
|
|
364
371
|
dependencyRoot: candidate.dependencyRoot,
|
|
365
372
|
toolchainRoot: candidate.toolchainRoot,
|
|
373
|
+
sourceWorkflowRunId: candidate.sourceWorkflowRunId,
|
|
366
374
|
deliveryClass: candidate.deliveryClass,
|
|
367
375
|
generation: next.fencingCounter,
|
|
368
376
|
expectedOldStateRoot: before.stateRoot,
|
|
@@ -397,6 +405,7 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
|
|
|
397
405
|
closureRoot: transaction.result.candidate.closureRoot,
|
|
398
406
|
dependencyRoot: transaction.result.candidate.dependencyRoot,
|
|
399
407
|
toolchainRoot: transaction.result.candidate.toolchainRoot,
|
|
408
|
+
sourceWorkflowRunId: transaction.result.candidate.sourceWorkflowRunId,
|
|
400
409
|
deliveryClass: transaction.result.candidate.deliveryClass,
|
|
401
410
|
queueAgeSeconds: selected.priority.ageSeconds,
|
|
402
411
|
basePriority: selected.priority.basePriority,
|
|
@@ -404,7 +413,7 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
|
|
|
404
413
|
effectivePriority: selected.priority.score,
|
|
405
414
|
fencingToken: transaction.result.warrant.fencingToken,
|
|
406
415
|
leaseGeneration: transaction.result.warrant.generation,
|
|
407
|
-
expectedOldStateRoot: transaction.expectedOldStateRoot,
|
|
416
|
+
expectedOldStateRoot: recoveryReceipt?.expectedOldStateRoot || transaction.expectedOldStateRoot,
|
|
408
417
|
nextStateRoot: transaction.after.stateRoot,
|
|
409
418
|
nextAction: transaction.result.warrant.nextAction,
|
|
410
419
|
};
|
|
@@ -417,12 +426,12 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
|
|
|
417
426
|
};
|
|
418
427
|
}
|
|
419
428
|
|
|
420
|
-
function assertWarrantMutation(queue, warrant, now) {
|
|
429
|
+
function assertWarrantMutation(queue, warrant, now, { allowExpired = false } = {}) {
|
|
421
430
|
if (!queue.activeWarrant) throw new Error("no active Delivery Warrant");
|
|
422
431
|
if (text(warrant?.fencingToken) !== queue.activeWarrant.fencingToken) throw new Error("stale fencing token");
|
|
423
432
|
if (Number(warrant?.generation) !== queue.activeWarrant.generation) throw new Error("stale lease generation");
|
|
424
433
|
if (text(warrant?.candidateId) !== queue.activeWarrant.candidateId) throw new Error("Warrant candidate mismatch");
|
|
425
|
-
if (Date.parse(queue.activeWarrant.expiresAt) <= Date.parse(now)) throw new Error("Delivery Warrant lease expired");
|
|
434
|
+
if (!allowExpired && Date.parse(queue.activeWarrant.expiresAt) <= Date.parse(now)) throw new Error("Delivery Warrant lease expired");
|
|
426
435
|
}
|
|
427
436
|
|
|
428
437
|
export function heartbeatDevDeliveryWarrant(queueInput, warrant, { now = new Date().toISOString(), leaseSeconds } = {}) {
|
|
@@ -517,7 +526,7 @@ export function closeDevDeliveryWarrant(queueInput, warrant, { outcome, evidence
|
|
|
517
526
|
const transaction = transition(
|
|
518
527
|
queueInput,
|
|
519
528
|
(queue, before) => {
|
|
520
|
-
assertWarrantMutation(before, warrant, currentTime);
|
|
529
|
+
assertWarrantMutation(before, warrant, currentTime, { allowExpired: true });
|
|
521
530
|
const active = clone(queue.activeWarrant);
|
|
522
531
|
const candidate = queue.candidates.find((entry) => entry.candidateId === active.candidateId);
|
|
523
532
|
candidate.status = normalizedOutcome;
|
|
@@ -84,10 +84,14 @@ function fileStats(cwd, entries = []) {
|
|
|
84
84
|
});
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
const TOOL_VERSION_PROBE_CWD = path.parse(process.execPath).root;
|
|
88
|
+
|
|
89
|
+
function commandVersion(command, args = ["--version"]) {
|
|
88
90
|
try {
|
|
89
91
|
return execFileSync(command, args, {
|
|
90
|
-
|
|
92
|
+
// Package managers may walk every parent even for --version. Keep this
|
|
93
|
+
// bounded instead of scanning an arbitrary consumer or shared temp root.
|
|
94
|
+
cwd: TOOL_VERSION_PROBE_CWD,
|
|
91
95
|
encoding: "utf8",
|
|
92
96
|
stdio: ["ignore", "pipe", "ignore"],
|
|
93
97
|
timeout: 5000,
|
|
@@ -493,7 +497,8 @@ export function collectRunnerDiagnostics() {
|
|
|
493
497
|
}
|
|
494
498
|
|
|
495
499
|
export function collectToolDiagnostics({ cwd = process.cwd(), tools = ["node", "pnpm", "npm", "git", "cmake", "ninja", "ccache", "sccache"] } = {}) {
|
|
496
|
-
|
|
500
|
+
void cwd;
|
|
501
|
+
return Object.fromEntries(tools.map((tool) => [tool, { version: commandVersion(tool) }]));
|
|
497
502
|
}
|
|
498
503
|
|
|
499
504
|
function parseJsonDiagnostics(value = "") {
|