@kungfu-tech/buildchain 4.0.1-alpha.5 → 4.0.1-alpha.55
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 +9 -6
- package/actions/release-tail/README.md +8 -0
- package/actions/v4-release-candidate-promote/README.md +3 -0
- package/architecture/agent-change-map.md +1015 -0
- package/architecture/ci-lane-change-budget.json +256 -0
- package/architecture/decisions/0004-v4-declarative-release-tail.md +47 -0
- package/architecture/internal-capabilities.json +220 -15
- package/architecture/maintainability-debt.json +3229 -0
- package/architecture/maintainability-policy.json +489 -132
- package/architecture/release-tail-contract-inventory.json +5 -2
- package/architecture/v3-core-mechanism-inventory.json +4 -3
- package/architecture/v3-v4-capability-closure.md +59 -0
- package/architecture/v3-v4-live-capability-inventory.json +102404 -0
- package/architecture/v4-adopter-delivery-parity.json +32 -5
- package/architecture/v4-cross-platform-adopter-qualification.json +52 -0
- package/architecture/v4-delivery-warrant-shadow-fixtures.json +3 -3
- package/architecture/v4-floating-consumer-policy.json +6 -0
- package/architecture/v4-publication-rehearsal-parity.json +84 -0
- package/architecture/v4-runtime-semantic-closure.json +710 -0
- package/bin/buildchain.mjs +7 -1
- package/bin/internal/adopter-delivery-cli.mjs +70 -0
- package/bin/internal/command-registry.mjs +1 -0
- package/bin/internal/trust-release-release-handlers.mjs +5 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/archive-template.json +19 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/bootstrap-positive.json +93 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/gate-positive.json +29 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/gate-unknown-selector.json +26 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/offline-vectors.json +50 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/artifacts/product.bin +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/config/buildchain.toml +5 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/release-activation.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/signed-channel.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/evidence/qualification.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/candidate.json +5 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/release-passport.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/capsule.json +707 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/offline-vectors.json +27 -0
- package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
- package/contracts/v4-adopter-delivery-v1.schema.json +81 -0
- package/contracts/v4-floating-consumer-policy-receipt-v1.schema.json +4 -2
- package/contracts/v4-publication-qualification-v1.schema.json +51 -0
- package/contracts/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
- package/dist/site/agent-index.json +4 -0
- package/dist/site/artifact-schemas.json +8 -0
- package/dist/site/buildchain-contract.json +125 -34
- package/dist/site/buildchain-site.json +308 -33
- package/dist/site/capability-registry.json +13 -12
- package/dist/site/cli-registry.json +57 -2
- package/dist/site/controller-registry.json +41 -5
- package/dist/site/kfd-claims.json +438 -24
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +22 -7
- package/dist/site/node-api-registry.json +12843 -9314
- package/dist/site/page-registry.json +288 -21
- package/dist/site/public-surface-audit.json +281 -21
- package/dist/site/publication-authority-registry.json +66 -8
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-provenance.json +20 -0
- package/dist/site/schemas/release-tail-capabilities-v1.schema.json +199 -0
- package/dist/site/schemas/v4-adopter-delivery-v1.schema.json +81 -0
- package/dist/site/schemas/v4-publication-qualification-v1.schema.json +51 -0
- package/dist/site/schemas/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
- package/dist/site/site-manifest.json +19 -11
- package/dist/site/workflow-registry.json +195 -17
- package/docs/MAP.md +4 -2
- package/docs/cli-reference.md +70 -2
- package/docs/cli.md +8 -0
- package/docs/dev-delivery-warrant.md +34 -10
- package/docs/node-api-reference.md +524 -279
- package/docs/publish-transaction.md +11 -4
- package/docs/release-candidate.md +7 -0
- package/docs/release-tail-provider-plane.md +30 -0
- package/docs/v4-adopter-delivery.md +152 -0
- package/docs/v4-declarative-release-tail-migration.md +43 -0
- package/docs/v4-publication-rehearsal.md +130 -0
- package/package.json +24 -4
- package/packages/core/adopter-delivery-passport.js +295 -0
- package/packages/core/adopter-delivery-vectors.js +158 -0
- package/packages/core/adopter-delivery-vectors.json +77 -0
- package/packages/core/buildchain-agent-manuals.js +1 -0
- package/packages/core/buildchain-config.js +84 -0
- package/packages/core/buildchain-contract.js +31 -1
- package/packages/core/buildchain-delivery-bootstrap.js +240 -0
- package/packages/core/buildchain-delivery-infrastructure.js +164 -0
- package/packages/core/buildchain-delivery-self-dogfood.js +395 -0
- package/packages/core/buildchain-publication-authority.js +2 -0
- package/packages/core/ci-lane-change-budget.js +247 -0
- package/packages/core/dev-delivery-native-proof.js +43 -27
- package/packages/core/dev-delivery-warrant-settlement.js +239 -3
- package/packages/core/dev-delivery-warrant-state.js +46 -10
- package/packages/core/dev-delivery-warrant.js +29 -7
- package/packages/core/github-governance-authority.js +5 -4
- package/packages/core/index.js +6 -0
- package/packages/core/legacy-kfd-adopter-driver.js +167 -0
- package/packages/core/paper-scaffold-content.js +401 -0
- package/packages/core/paper.js +12 -378
- package/packages/core/publication-control-plane-audit.js +135 -111
- package/packages/core/publication-rehearsal-projection.js +104 -0
- package/packages/core/publication-rehearsal-runtime.js +190 -0
- package/packages/core/published-delivery-authority.js +266 -0
- package/packages/core/release-candidate-recovery.js +71 -15
- package/packages/core/release-candidate.js +2 -2
- package/packages/core/release-passport.js +95 -170
- package/packages/core/v4-adopter-delivery-parity.js +3 -3
- package/packages/core/v4-adopter-delivery.js +359 -0
- package/packages/core/v4-cross-platform-adopter-qualification.js +343 -0
- package/packages/core/v4-delivery-warrant-read-candidate.js +10 -3
- package/packages/core/v4-floating-consumer-evidence.js +18 -4
- package/packages/core/v4-floating-consumer-policy.js +139 -35
- package/packages/core/v4-floating-consumer-release-passport.js +154 -8
- package/packages/core/v4-publication-qualification.js +222 -0
- package/packages/core/v4-publication-rehearsal-capsule.js +468 -0
- package/packages/core/v4-publication-rehearsal-provider-bindings.js +212 -0
- package/packages/core/v4-publication-rehearsal.js +481 -0
- package/packages/core/workflow-yaml-contract.js +24 -2
- package/scripts/artifact-signing-controller-core.mjs +1 -1
- package/scripts/audit-publication-control-plane.mjs +17 -17
- package/scripts/buildchain-cli-help.mjs +13 -0
- package/scripts/check-ci-lane-change-budget.mjs +100 -0
- package/scripts/check-internal-architecture.mjs +23 -3
- package/scripts/check-inventory.mjs +24 -6
- package/scripts/check-maintainability.mjs +145 -4
- package/scripts/check-project-cut-merge-queue-admission.mjs +87 -0
- package/scripts/check-v3-v4-capability-inventory.mjs +574 -0
- package/scripts/check-v4-floating-consumer-policy-contract.mjs +30 -4
- package/scripts/check-v4-runtime-semantic-closure.mjs +266 -0
- package/scripts/dev-delivery-source-proof-replay.mjs +153 -0
- package/scripts/dev-delivery-source-proof-reuse.mjs +13 -97
- package/scripts/dev-delivery-warrant-options.mjs +27 -11
- package/scripts/dev-delivery-warrant-store.mjs +10 -6
- package/scripts/dev-delivery-warrant.mjs +50 -6
- package/scripts/dev-pr-auto-merge.mjs +25 -11
- package/scripts/dev-pr-delivery-warrant.mjs +45 -0
- package/scripts/dev-pr-prequeue-guard.mjs +0 -18
- package/scripts/dev-qualification-patrol.mjs +9 -9
- package/scripts/dispatch-artifact-signing-authority.mjs +1 -1
- package/scripts/generate-agent-change-map.mjs +118 -0
- package/scripts/generate-channel-promotion-workflow.mjs +2 -2
- package/scripts/generate-release-candidate-passport.mjs +177 -0
- package/scripts/generate-site-bundle.mjs +36 -0
- package/scripts/maintainability-domain-metrics.mjs +237 -0
- package/scripts/maintainability-governance.mjs +473 -0
- package/scripts/maintainability-metrics.mjs +28 -6
- package/scripts/project-cut-merge-queue-admission.mjs +394 -0
- package/scripts/promotion-channel-router.mjs +4 -4
- package/scripts/promotion-routing-evidence.mjs +35 -6
- package/scripts/release-candidate-anchor-provenance.mjs +99 -0
- package/scripts/release-candidate-resolver.mjs +44 -0
- package/scripts/release-line-policy.mjs +25 -15
- package/scripts/release-tail.mjs +68 -3
- package/scripts/resume-from-candidate-run.mjs +595 -65
- package/scripts/run-lifecycle-core.mjs +341 -376
- package/scripts/site-capability-metadata.mjs +21 -0
- package/scripts/stable-candidate-qualification.mjs +9 -10
- package/scripts/v3-v4-capability-catalog.mjs +439 -0
- package/scripts/v4-consumer-policy.mjs +4 -0
- package/scripts/v4-cross-platform-adopter-qualification.mjs +228 -0
- package/scripts/v4-declarative-promotion-admission.mjs +42 -0
- package/scripts/v4-publication-rehearsal-fixture.mjs +144 -0
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
observeDevDeliveryQueue,
|
|
11
11
|
qualifyDevDeliveryWarrant,
|
|
12
12
|
recoverExpiredDevDeliveryWarrant,
|
|
13
|
+
reconcileDevDeliveryTerminalEvidence,
|
|
13
14
|
selectDevDeliveryWarrant,
|
|
14
15
|
settleDevDeliveryTerminalEvent,
|
|
15
16
|
submitDevDeliveryCandidate,
|
|
@@ -52,6 +53,43 @@ function exactSha(value, label) {
|
|
|
52
53
|
return normalized;
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
function terminalSourceHead(options) {
|
|
57
|
+
return exactSha(
|
|
58
|
+
options.expectedSourceHead || options.sourceHead,
|
|
59
|
+
"sourceHead",
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function reconcileTerminalEvidenceCommand(queue, options) {
|
|
64
|
+
return reconcileDevDeliveryTerminalEvidence(
|
|
65
|
+
queue,
|
|
66
|
+
{
|
|
67
|
+
candidateId: exactRoot(options.candidateId, "candidateId"),
|
|
68
|
+
expectedPriorEvidenceRoot: exactRoot(
|
|
69
|
+
options.expectedPriorEvidenceRoot,
|
|
70
|
+
"expectedPriorEvidenceRoot",
|
|
71
|
+
),
|
|
72
|
+
integrationProof:
|
|
73
|
+
options.integrationProof ||
|
|
74
|
+
jsonFile(options.integrationProofPath, "integration proof"),
|
|
75
|
+
reason: options.reason,
|
|
76
|
+
},
|
|
77
|
+
{ now: options.now },
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function requireTerminalEvidenceCas(options) {
|
|
82
|
+
if (
|
|
83
|
+
options.command === "reconcile-terminal-evidence" &&
|
|
84
|
+
options.execute &&
|
|
85
|
+
!options.expectedOldStateRoot
|
|
86
|
+
) {
|
|
87
|
+
throw new Error(
|
|
88
|
+
"terminal evidence reconciliation execute requires expected-old CAS",
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
55
93
|
function normalizeRepository(value) {
|
|
56
94
|
const normalized = text(value);
|
|
57
95
|
const match = normalized.match(/^([^/\s]+)\/([^/\s]+)$/);
|
|
@@ -245,10 +283,7 @@ function transitionFor(command, queue, options) {
|
|
|
245
283
|
options.pullRequestNumber,
|
|
246
284
|
"pullRequestNumber",
|
|
247
285
|
),
|
|
248
|
-
sourceHead:
|
|
249
|
-
options.expectedSourceHead || options.sourceHead,
|
|
250
|
-
"sourceHead",
|
|
251
|
-
),
|
|
286
|
+
sourceHead: terminalSourceHead(options),
|
|
252
287
|
fencingToken: options.fencingToken,
|
|
253
288
|
leaseGeneration: options.leaseGeneration,
|
|
254
289
|
outcome: options.outcome,
|
|
@@ -263,6 +298,9 @@ function transitionFor(command, queue, options) {
|
|
|
263
298
|
{ now: options.now },
|
|
264
299
|
);
|
|
265
300
|
}
|
|
301
|
+
if (command === "reconcile-terminal-evidence") {
|
|
302
|
+
return reconcileTerminalEvidenceCommand(queue, options);
|
|
303
|
+
}
|
|
266
304
|
if (command === "cancel-queued") {
|
|
267
305
|
return cancelQueuedDevDeliveryCandidate(
|
|
268
306
|
queue,
|
|
@@ -295,7 +333,10 @@ async function observeQueue(loaded, options) {
|
|
|
295
333
|
const readMode = text(options.readMode || "v3").toLowerCase();
|
|
296
334
|
if (!["v3", "v4"].includes(readMode))
|
|
297
335
|
throw new Error("readMode must be v3 or v4");
|
|
298
|
-
let observation = observeDevDeliveryQueue(loaded.queue, {
|
|
336
|
+
let observation = observeDevDeliveryQueue(loaded.queue, {
|
|
337
|
+
now: options.now,
|
|
338
|
+
allowLegacyV3Readback: true,
|
|
339
|
+
});
|
|
299
340
|
let readCandidate;
|
|
300
341
|
if (readMode === "v4") {
|
|
301
342
|
const qualification =
|
|
@@ -353,6 +394,7 @@ export async function runDevDeliveryCommand(optionsInput = {}, clientInput) {
|
|
|
353
394
|
now: new Date(optionsInput.now || Date.now()).toISOString(),
|
|
354
395
|
execute: bool(optionsInput.execute, false),
|
|
355
396
|
};
|
|
397
|
+
requireTerminalEvidenceCas(options);
|
|
356
398
|
if (
|
|
357
399
|
options.command === "submit" &&
|
|
358
400
|
options.execute &&
|
|
@@ -374,6 +416,7 @@ export async function runDevDeliveryCommand(optionsInput = {}, clientInput) {
|
|
|
374
416
|
stateRef: options.stateRef,
|
|
375
417
|
protectedBase: options.branch,
|
|
376
418
|
now: options.now,
|
|
419
|
+
allowLegacyV3Readback: options.command === "observe",
|
|
377
420
|
});
|
|
378
421
|
if (
|
|
379
422
|
options.expectedOldStateRoot &&
|
|
@@ -453,7 +496,7 @@ export async function runDevDeliveryCommand(optionsInput = {}, clientInput) {
|
|
|
453
496
|
}
|
|
454
497
|
|
|
455
498
|
function usage() {
|
|
456
|
-
return "Usage:\n buildchain dev warrant <submit|select|heartbeat|qualify|recover|close|settle|cancel-queued|observe> --repository owner/repo --branch dev/vN/vN.M [--execute] [--output FILE] [--json]\n\nRead candidate:\n observe --read-mode v4 --read-qualification FILE --read-qualification-root sha256:... --read-typescript-revision SHA --read-rust-revision SHA --read-validator-version TOKEN [--read-evidence-output FILE]\n";
|
|
499
|
+
return "Usage:\n buildchain dev warrant <submit|select|heartbeat|qualify|recover|close|settle|reconcile-terminal-evidence|cancel-queued|observe> --repository owner/repo --branch dev/vN/vN.M [--execute] [--output FILE] [--json]\n\nRead candidate:\n observe --read-mode v4 --read-qualification FILE --read-qualification-root sha256:... --read-typescript-revision SHA --read-rust-revision SHA --read-validator-version TOKEN [--read-evidence-output FILE]\n";
|
|
457
500
|
}
|
|
458
501
|
|
|
459
502
|
async function main() {
|
|
@@ -472,6 +515,7 @@ async function main() {
|
|
|
472
515
|
"recover",
|
|
473
516
|
"close",
|
|
474
517
|
"settle",
|
|
518
|
+
"reconcile-terminal-evidence",
|
|
475
519
|
"cancel-queued",
|
|
476
520
|
"observe",
|
|
477
521
|
].includes(options.command)
|
|
@@ -3,7 +3,7 @@ import crypto from "node:crypto";
|
|
|
3
3
|
import { spawnSync } from "node:child_process";
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
|
-
import { admitExistingQueueEntry, createDevPrAdmissionReceipt, qualifyAtomicQueueAdmission, readDeliveryWarrantResult, runSourceQualification, runTargetedQueueAdmission, setActiveLeaseStatus, verifyCurrentDeliveryWarrant } from "./dev-pr-delivery-warrant.mjs";
|
|
6
|
+
import { admitExistingQueueEntry, createDevPrAdmissionReceipt, createPreReadinessQueueFence, qualifyAtomicQueueAdmission, readDeliveryWarrantResult, runSourceQualification, runTargetedQueueAdmission, setActiveLeaseStatus, setQueueAdmissionStatus, verifyCurrentDeliveryWarrant } from "./dev-pr-delivery-warrant.mjs";
|
|
7
7
|
import { projectCutQualification } from "./dev-pr-prequeue-guard.mjs";
|
|
8
8
|
const DEFAULT_BLOCK_LABELS = ["blocked", "do-not-merge", "work-in-progress"];
|
|
9
9
|
const DEFAULT_ALLOWED_HEAD_PREFIXES = ["feature/", "fix/", "chore/", "docs/", "ci/", "refactor/"];
|
|
@@ -197,12 +197,6 @@ function mergeableAccepted(pr, landingMode = "direct", projectCutQualified = fal
|
|
|
197
197
|
function rejectBaseMoveBeforeAtomicReplay(options, initialBaseSha, observedBaseSha) {
|
|
198
198
|
return observedBaseSha !== initialBaseSha && options.warrantMode !== "required";
|
|
199
199
|
}
|
|
200
|
-
async function setQueueAdmissionStatus(client, repository, sha, context, state) {
|
|
201
|
-
if (!context) return null;
|
|
202
|
-
await client.request("POST", `/repos/${repository.owner}/${repository.repo}/statuses/${sha}`, { body: { state, context, description: state === "success" ? "Buildchain admitted this exact PR head to the merge queue" : "Buildchain rejected merge queue admission for this exact PR head" } });
|
|
203
|
-
return { context, state, sha };
|
|
204
|
-
}
|
|
205
|
-
|
|
206
200
|
function skip(reason, details = {}) {
|
|
207
201
|
return { action: "skip", reason, ...details };
|
|
208
202
|
}
|
|
@@ -790,8 +784,10 @@ export async function runDevPrAdmission(optionsInput = {}, clientInput) {
|
|
|
790
784
|
return targetedFailure({ options, pr: {}, state: "missing", reason: "pull-request-not-found" });
|
|
791
785
|
}
|
|
792
786
|
|
|
787
|
+
const readinessFence = createPreReadinessQueueFence({ client, options, sleep: delay });
|
|
793
788
|
const reject = async (state, reason, readiness = { observed: hasReadyLabel(pr, options.readyLabel), established: false }) => {
|
|
794
789
|
const result = targetedFailure({ options, pr, state, reason, readiness });
|
|
790
|
+
await readinessFence.finish(result);
|
|
795
791
|
if (!options.dryRun) result.diagnostic = await publishAdmissionDiagnostic(client, options, result.receipt, result.receiptRoot);
|
|
796
792
|
return result;
|
|
797
793
|
};
|
|
@@ -803,6 +799,12 @@ export async function runDevPrAdmission(optionsInput = {}, clientInput) {
|
|
|
803
799
|
if (pr.draft) return reject("blocked", "draft");
|
|
804
800
|
if (!headPrefixAllowed(pr, options.allowedHeadPrefixes)) return reject("blocked", "head-prefix-not-allowed");
|
|
805
801
|
|
|
802
|
+
try {
|
|
803
|
+
await readinessFence.establish();
|
|
804
|
+
} catch (error) {
|
|
805
|
+
return reject("blocked", error.code || "pre-readiness-queue-fence-failed");
|
|
806
|
+
}
|
|
807
|
+
|
|
806
808
|
let readiness = { observed: hasReadyLabel(pr, options.readyLabel), established: false };
|
|
807
809
|
if (!readiness.observed) {
|
|
808
810
|
if (options.dryRun) return reject("rejected", "missing-ready-label", readiness);
|
|
@@ -825,7 +827,7 @@ export async function runDevPrAdmission(optionsInput = {}, clientInput) {
|
|
|
825
827
|
}
|
|
826
828
|
|
|
827
829
|
if (options.qualificationOnly) {
|
|
828
|
-
|
|
830
|
+
const result = await runSourceQualification({
|
|
829
831
|
options, pullRequest: pr, readiness, client, reject,
|
|
830
832
|
evaluate: evaluatePullRequest,
|
|
831
833
|
admissionState: admissionStateFor,
|
|
@@ -833,6 +835,7 @@ export async function runDevPrAdmission(optionsInput = {}, clientInput) {
|
|
|
833
835
|
root: contentRoot,
|
|
834
836
|
publishDiagnostic: publishAdmissionDiagnostic,
|
|
835
837
|
});
|
|
838
|
+
return readinessFence.finish(result);
|
|
836
839
|
}
|
|
837
840
|
|
|
838
841
|
const initialQueue = await client.getMergeQueueState(options.targetBranch);
|
|
@@ -844,6 +847,7 @@ export async function runDevPrAdmission(optionsInput = {}, clientInput) {
|
|
|
844
847
|
}
|
|
845
848
|
const matchingEntry = initialQueue.entries.find((entry) =>
|
|
846
849
|
entry.pullRequestNumber === pr.number && entry.pullRequestHeadSha === options.expectedHeadSha);
|
|
850
|
+
await readinessFence.qualifyExisting(matchingEntry);
|
|
847
851
|
const existing = await admitExistingQueueEntry({
|
|
848
852
|
options, pullRequest: pr, readiness, client, entry: matchingEntry, warrant,
|
|
849
853
|
createReceipt: createAdmissionReceipt,
|
|
@@ -852,7 +856,7 @@ export async function runDevPrAdmission(optionsInput = {}, clientInput) {
|
|
|
852
856
|
});
|
|
853
857
|
if (existing) return existing;
|
|
854
858
|
|
|
855
|
-
|
|
859
|
+
const result = await runTargetedQueueAdmission({
|
|
856
860
|
options, pullRequest: pr, readiness, client, warrant,
|
|
857
861
|
runController: runDevPrAutoMerge,
|
|
858
862
|
admissionState: admissionStateFor,
|
|
@@ -860,6 +864,7 @@ export async function runDevPrAdmission(optionsInput = {}, clientInput) {
|
|
|
860
864
|
root: contentRoot,
|
|
861
865
|
publishDiagnostic: publishAdmissionDiagnostic,
|
|
862
866
|
});
|
|
867
|
+
return readinessFence.finish(result);
|
|
863
868
|
}
|
|
864
869
|
|
|
865
870
|
function finalizePatrolResult(result) {
|
|
@@ -1132,6 +1137,11 @@ function cliFlag(args, name) {
|
|
|
1132
1137
|
|
|
1133
1138
|
export function cliOptions(args = [], environment = process.env) {
|
|
1134
1139
|
const targetPullRequestNumber = cliValue(args, "pull-request", environment.BUILDCHAIN_DEV_PR_EXPECTED_PR_NUMBER);
|
|
1140
|
+
const warrantMode = cliValue(
|
|
1141
|
+
args,
|
|
1142
|
+
"warrant-mode",
|
|
1143
|
+
environment.BUILDCHAIN_DEV_PR_WARRANT_MODE || environment.WARRANT_MODE,
|
|
1144
|
+
);
|
|
1135
1145
|
return {
|
|
1136
1146
|
repository: cliValue(args, "repository", environment.BUILDCHAIN_DEV_PR_REPOSITORY || environment.GITHUB_REPOSITORY),
|
|
1137
1147
|
targetBranch: cliValue(args, "branch", environment.BUILDCHAIN_DEV_PR_TARGET_BRANCH || environment.GITHUB_REF_NAME),
|
|
@@ -1141,10 +1151,14 @@ export function cliOptions(args = [], environment = process.env) {
|
|
|
1141
1151
|
blockLabels: cliValue(args, "block-labels", environment.BUILDCHAIN_DEV_PR_BLOCK_LABELS),
|
|
1142
1152
|
allowedHeadPrefixes: cliValue(args, "allowed-head-prefixes", environment.BUILDCHAIN_DEV_PR_ALLOWED_HEAD_PREFIXES),
|
|
1143
1153
|
requiredChecks: cliValue(args, "required-checks", environment.BUILDCHAIN_DEV_PR_REQUIRED_CHECKS),
|
|
1144
|
-
queueAdmissionContext: cliValue(
|
|
1154
|
+
queueAdmissionContext: cliValue(
|
|
1155
|
+
args,
|
|
1156
|
+
"queue-admission-context",
|
|
1157
|
+
environment.BUILDCHAIN_DEV_PR_QUEUE_ADMISSION_CONTEXT || (warrantMode === "required" ? "Queue admission lease" : ""),
|
|
1158
|
+
),
|
|
1145
1159
|
activeLeaseContext: cliValue(args, "active-lease-context", environment.BUILDCHAIN_DEV_PR_ACTIVE_LEASE_CONTEXT),
|
|
1146
1160
|
diagnosticContext: cliValue(args, "diagnostic-context", environment.BUILDCHAIN_DEV_PR_DIAGNOSTIC_CONTEXT),
|
|
1147
|
-
warrantMode
|
|
1161
|
+
warrantMode,
|
|
1148
1162
|
warrantResultPath: cliValue(args, "warrant-result", environment.BUILDCHAIN_DEV_PR_WARRANT_RESULT_PATH),
|
|
1149
1163
|
projectCutProofPath: cliValue(args, "project-cut-proof", environment.BUILDCHAIN_DEV_PR_PROJECT_CUT_PROOF_PATH),
|
|
1150
1164
|
sourceProofPath: cliValue(args, "source-proof", environment.BUILDCHAIN_DEV_PR_SOURCE_PROOF_PATH),
|
|
@@ -143,6 +143,7 @@ export async function readCurrentDeliveryQueueState(client, repository, targetBr
|
|
|
143
143
|
const { queue } = await store.read({
|
|
144
144
|
stateRef: defaultDevDeliveryStateRef(targetBranch),
|
|
145
145
|
protectedBase: targetBranch,
|
|
146
|
+
allowLegacyV3Readback: true,
|
|
146
147
|
});
|
|
147
148
|
return queue;
|
|
148
149
|
}
|
|
@@ -205,6 +206,50 @@ export async function setActiveLeaseStatus(client, repository, sha, context, sta
|
|
|
205
206
|
return { context, state, sha };
|
|
206
207
|
}
|
|
207
208
|
|
|
209
|
+
export async function setQueueAdmissionStatus(client, repository, sha, context, state) {
|
|
210
|
+
if (!context) return null;
|
|
211
|
+
const descriptions = {
|
|
212
|
+
pending: "Buildchain is awaiting a qualified Warrant for this exact PR head",
|
|
213
|
+
success: "Buildchain admitted this exact PR head to the merge queue",
|
|
214
|
+
failure: "Buildchain rejected merge queue admission for this exact PR head",
|
|
215
|
+
};
|
|
216
|
+
await client.request("POST", `/repos/${repository.owner}/${repository.repo}/statuses/${sha}`, {
|
|
217
|
+
body: { state, context, description: descriptions[state] || descriptions.failure },
|
|
218
|
+
});
|
|
219
|
+
return { context, state, sha };
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function createPreReadinessQueueFence({ client, options, sleep }) {
|
|
223
|
+
let status = null;
|
|
224
|
+
const write = async (state) => {
|
|
225
|
+
status = await setQueueAdmissionStatus(client, options.repository, options.expectedHeadSha, options.queueAdmissionContext, state);
|
|
226
|
+
return status;
|
|
227
|
+
};
|
|
228
|
+
return {
|
|
229
|
+
async establish() {
|
|
230
|
+
if (options.dryRun || options.warrantMode !== "required") return null;
|
|
231
|
+
if (!options.queueAdmissionContext) mismatch("queue-admission-context-required-before-readiness");
|
|
232
|
+
await write("pending");
|
|
233
|
+
for (let attempt = 1; attempt <= Math.max(1, options.pollMergeableAttempts); attempt += 1) {
|
|
234
|
+
const checks = await client.listCommitChecks(options.expectedHeadSha);
|
|
235
|
+
const readback = (checks?.statuses || []).find((entry) => entry.context === options.queueAdmissionContext);
|
|
236
|
+
if (readback?.state === "pending") return { ...status, readbackAttempts: attempt };
|
|
237
|
+
if (attempt < options.pollMergeableAttempts) await sleep(options.pollMergeableDelayMs);
|
|
238
|
+
}
|
|
239
|
+
await write("failure");
|
|
240
|
+
mismatch("pre-readiness-queue-fence-readback-mismatch");
|
|
241
|
+
},
|
|
242
|
+
async qualifyExisting(entry) {
|
|
243
|
+
if (entry && status?.state === "pending") await write("success");
|
|
244
|
+
},
|
|
245
|
+
async finish(result) {
|
|
246
|
+
const controllerFailed = result.controller?.evaluated?.some((entry) => entry.queueAdmissionStatus?.state === "failure");
|
|
247
|
+
if (!result.ok && status?.state === "pending" && !controllerFailed) await write("failure");
|
|
248
|
+
return result;
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
208
253
|
async function readBackAdmissionStatuses(client, options, expectedHeadSha, sleep) {
|
|
209
254
|
let last = { queueAdmission: null, activeLease: null };
|
|
210
255
|
const attempts = Math.max(1, options.pollMergeableAttempts);
|
|
@@ -164,15 +164,6 @@ async function qualifyProjectCut({
|
|
|
164
164
|
|
|
165
165
|
let reuseDecision = null;
|
|
166
166
|
if (currentBaseSha !== previousBaseSha) {
|
|
167
|
-
const observedPullRequestBase = String(
|
|
168
|
-
observedPullRequest.base?.sha || "",
|
|
169
|
-
).toLowerCase();
|
|
170
|
-
if (observedPullRequestBase !== currentBaseSha) {
|
|
171
|
-
mismatch("pre-enqueue-project-cut-base-stale", {
|
|
172
|
-
observedPullRequestBase,
|
|
173
|
-
currentBaseSha,
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
167
|
const proof = readOptionalJson(options.sourceProofPath);
|
|
177
168
|
if (!proof) mismatch("pre-enqueue-base-attribution-unknown");
|
|
178
169
|
if (
|
|
@@ -342,15 +333,6 @@ export async function qualifyPreEnqueueReadback({
|
|
|
342
333
|
) {
|
|
343
334
|
mismatch("head-sha-drift-after-project-cut");
|
|
344
335
|
}
|
|
345
|
-
const casPullRequestBase = String(
|
|
346
|
-
casPullRequest.base?.sha || "",
|
|
347
|
-
).toLowerCase();
|
|
348
|
-
if (casPullRequestBase && casPullRequestBase !== casBaseSha) {
|
|
349
|
-
mismatch("pre-enqueue-project-cut-base-stale", {
|
|
350
|
-
observedPullRequestBase: casPullRequestBase,
|
|
351
|
-
currentBaseSha: casBaseSha,
|
|
352
|
-
});
|
|
353
|
-
}
|
|
354
336
|
if (casPullRequest.mergeable === false) {
|
|
355
337
|
mismatch("pre-enqueue-merge-conflict-after-project-cut");
|
|
356
338
|
}
|
|
@@ -501,16 +501,16 @@ export function createGitHubDevQualificationClient({
|
|
|
501
501
|
sourceBranch,
|
|
502
502
|
{ activeOnly = false } = {},
|
|
503
503
|
) {
|
|
504
|
-
const query = sourceBranch
|
|
505
|
-
|
|
506
|
-
: ""
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
504
|
+
const query = sourceBranch ? `branch=${encodeRef(sourceBranch)}&` : "";
|
|
505
|
+
const runs = await Promise.all(
|
|
506
|
+
[...(activeOnly ? ACTIVE_STATUSES : [""])].map((status) =>
|
|
507
|
+
paged(
|
|
508
|
+
`/repos/${owner}/${repo}/actions/workflows/${encodeURIComponent(workflow)}/runs?${query}${status ? `status=${status}` : ""}`,
|
|
509
|
+
"workflow_runs",
|
|
510
|
+
),
|
|
511
|
+
),
|
|
510
512
|
);
|
|
511
|
-
return
|
|
512
|
-
? runs.filter((run) => ACTIVE_STATUSES.has(text(run.status)))
|
|
513
|
-
: runs;
|
|
513
|
+
return runs.flat();
|
|
514
514
|
},
|
|
515
515
|
async listRunJobs(runId, runAttempt) {
|
|
516
516
|
return paged(
|
|
@@ -24,7 +24,7 @@ const AUTHORITY_WORKFLOW = "artifact-signing-authority.yml";
|
|
|
24
24
|
|
|
25
25
|
export function resolveAuthorityDispatchRef(value) {
|
|
26
26
|
const ref = required(value, "authority ref");
|
|
27
|
-
return /^[0-9a-f]{40}$/u.test(ref)
|
|
27
|
+
return /^[0-9a-f]{40}$/u.test(ref) || /^v\d+(?:\.\d+)?(?:-alpha)?$/u.test(ref)
|
|
28
28
|
? DEFAULT_ARTIFACT_SIGNING_AUTHORITY_REF
|
|
29
29
|
: ref;
|
|
30
30
|
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
|
|
7
|
+
const indexPath = "architecture/internal-capabilities.json";
|
|
8
|
+
const debtPath = "architecture/maintainability-debt.json";
|
|
9
|
+
const outputPath = "architecture/agent-change-map.md";
|
|
10
|
+
|
|
11
|
+
function list(values) {
|
|
12
|
+
if (!values?.length) return "- None.";
|
|
13
|
+
return values.map((value) => `- \`${value}\``).join("\n");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function debtRouteSection(debt) {
|
|
17
|
+
if (!debt?.surfaces) return "";
|
|
18
|
+
const defaults = debt.defaults;
|
|
19
|
+
const governedPaths = Object.entries(debt.surfaces).flatMap(
|
|
20
|
+
([domain, entries]) =>
|
|
21
|
+
Object.keys(entries).map((entry) => `${domain}: \`${entry}\``),
|
|
22
|
+
);
|
|
23
|
+
return `## Governed complexity debt routes
|
|
24
|
+
|
|
25
|
+
Owner: ${defaults.owner}
|
|
26
|
+
|
|
27
|
+
Capability: \`${defaults.capability}\`
|
|
28
|
+
|
|
29
|
+
Invariant: ${defaults.invariant}
|
|
30
|
+
|
|
31
|
+
Tests: ${defaults.tests.map((entry) => `\`${entry}\``).join(", ")}
|
|
32
|
+
|
|
33
|
+
Safe change route: ${defaults.safeChangeRoute}
|
|
34
|
+
|
|
35
|
+
### Oversized surfaces
|
|
36
|
+
|
|
37
|
+
${governedPaths.map((entry) => `- ${entry}`).join("\n")}
|
|
38
|
+
|
|
39
|
+
### Current top twenty maintenance hotspots
|
|
40
|
+
|
|
41
|
+
${debt.hotspots.map((entry) => `- \`${entry}\``).join("\n")}`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function generateAgentChangeMap(index, debt = null) {
|
|
45
|
+
const capabilities = [...index.capabilities].sort((left, right) =>
|
|
46
|
+
left.id.localeCompare(right.id),
|
|
47
|
+
);
|
|
48
|
+
const sections = capabilities.map(
|
|
49
|
+
(capability) => `## ${capability.id}
|
|
50
|
+
|
|
51
|
+
Owner: ${capability.owner}
|
|
52
|
+
|
|
53
|
+
### Implementation
|
|
54
|
+
|
|
55
|
+
${list(capability.implementation)}
|
|
56
|
+
|
|
57
|
+
### Contracts
|
|
58
|
+
|
|
59
|
+
${list(capability.contracts)}
|
|
60
|
+
|
|
61
|
+
### Tests
|
|
62
|
+
|
|
63
|
+
${list(capability.tests)}
|
|
64
|
+
|
|
65
|
+
### Generated outputs
|
|
66
|
+
|
|
67
|
+
${list(capability.generatedOutputs)}
|
|
68
|
+
|
|
69
|
+
### Minimal validation
|
|
70
|
+
|
|
71
|
+
${list(capability.validationCommands)}`,
|
|
72
|
+
);
|
|
73
|
+
return `<!-- Generated by scripts/generate-agent-change-map.mjs. Do not edit directly. -->
|
|
74
|
+
# Agent Change Map
|
|
75
|
+
|
|
76
|
+
Use this index to identify the smallest implementation, contract, test, generated-output, and validation surface for a capability change. The machine-readable source of truth is \`${indexPath}\`.
|
|
77
|
+
|
|
78
|
+
${debtRouteSection(debt)}
|
|
79
|
+
|
|
80
|
+
${sections.join("\n\n")}
|
|
81
|
+
`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function run({ root = process.cwd(), mode = "check" } = {}) {
|
|
85
|
+
const index = JSON.parse(fs.readFileSync(path.join(root, indexPath), "utf8"));
|
|
86
|
+
const debt = JSON.parse(fs.readFileSync(path.join(root, debtPath), "utf8"));
|
|
87
|
+
const generated = generateAgentChangeMap(index, debt);
|
|
88
|
+
const target = path.join(root, outputPath);
|
|
89
|
+
if (mode === "write") {
|
|
90
|
+
fs.writeFileSync(target, generated);
|
|
91
|
+
return { outputPath, updated: true };
|
|
92
|
+
}
|
|
93
|
+
const current = fs.existsSync(target) ? fs.readFileSync(target, "utf8") : "";
|
|
94
|
+
if (current !== generated) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
`${outputPath} is stale; run node scripts/generate-agent-change-map.mjs --write`,
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
return { outputPath, updated: false };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (
|
|
103
|
+
process.argv[1] &&
|
|
104
|
+
import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href
|
|
105
|
+
) {
|
|
106
|
+
try {
|
|
107
|
+
const mode = process.argv.includes("--write") ? "write" : "check";
|
|
108
|
+
const report = run({ mode });
|
|
109
|
+
console.log(
|
|
110
|
+
`${report.outputPath} ${report.updated ? "generated" : "is current"}`,
|
|
111
|
+
);
|
|
112
|
+
} catch (error) {
|
|
113
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
114
|
+
process.exitCode = 1;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export { generateAgentChangeMap, run };
|
|
@@ -159,8 +159,8 @@ function validateWorkflowRoute(name, route, expectedLogicalRef) {
|
|
|
159
159
|
if (!/^\.github\/workflows\/[.a-z0-9-]+\.ya?ml$/.test(route.workflowPath || "")) {
|
|
160
160
|
throw new Error(`promotion shell ${name} workflowPath must name a reusable workflow`);
|
|
161
161
|
}
|
|
162
|
-
if (!/^(?:v[0-9]+(?:-alpha)?|train\/[A-Za-z0-9._+\/-]+|[0-9a-f]{40})$/.test(route.callRef || "")) {
|
|
163
|
-
throw new Error(`promotion shell ${name} callRef must be an official channel ref, trusted train, or exact SHA`);
|
|
162
|
+
if (!/^(?:v[0-9]+(?:-alpha)?|(?:train|alpha|release)\/[A-Za-z0-9._+\/-]+|[0-9a-f]{40})$/.test(route.callRef || "")) {
|
|
163
|
+
throw new Error(`promotion shell ${name} callRef must be an official tag, protected channel ref, trusted train, or exact SHA`);
|
|
164
164
|
}
|
|
165
165
|
if (typeof route.forwardInternalInputs !== "boolean") {
|
|
166
166
|
throw new Error(`promotion shell ${name} forwardInternalInputs must be boolean`);
|