@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
|
@@ -0,0 +1,266 @@
|
|
|
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
|
+
export const V4_RUNTIME_SEMANTIC_CLOSURE_CONTRACT =
|
|
8
|
+
"kungfu-buildchain-v4-runtime-semantic-closure";
|
|
9
|
+
export const V4_RUNTIME_SEMANTIC_CLOSURE_PATH =
|
|
10
|
+
"architecture/v4-runtime-semantic-closure.json";
|
|
11
|
+
export const REQUIRED_RUNTIME_EVIDENCE = Object.freeze([
|
|
12
|
+
"positive",
|
|
13
|
+
"negative",
|
|
14
|
+
"failure",
|
|
15
|
+
"recovery",
|
|
16
|
+
"idempotence",
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
function loadJson(root, relativePath) {
|
|
20
|
+
return JSON.parse(fs.readFileSync(path.join(root, relativePath), "utf8"));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function sorted(values) {
|
|
24
|
+
return [...values].sort((left, right) => left.localeCompare(right));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function sameMembers(left, right) {
|
|
28
|
+
return JSON.stringify(sorted(left)) === JSON.stringify(sorted(right));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function publicSurfaceIds(root) {
|
|
32
|
+
const packageJson = loadJson(root, "package.json");
|
|
33
|
+
const cli = loadJson(root, "dist/site/cli-registry.json");
|
|
34
|
+
const workflows = loadJson(root, "dist/site/workflow-registry.json");
|
|
35
|
+
return new Set([
|
|
36
|
+
...Object.keys(packageJson.exports || {}).map((id) => `export:${id}`),
|
|
37
|
+
...(cli.commands || []).map((entry) => `cli:${entry.id}`),
|
|
38
|
+
...(workflows.workflows || []).map((entry) => `workflow:${entry.id}`),
|
|
39
|
+
...(workflows.actions || []).map((entry) => `action:${entry.id}`),
|
|
40
|
+
]);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function validateEvidence(root, mechanism, capability, issues) {
|
|
44
|
+
const sourceTests = new Set(mechanism.testPaths || []);
|
|
45
|
+
for (const dimension of REQUIRED_RUNTIME_EVIDENCE) {
|
|
46
|
+
const entries = capability.evidence?.[dimension];
|
|
47
|
+
if (!Array.isArray(entries) || entries.length === 0) {
|
|
48
|
+
issues.push(`${capability.id}: ${dimension} evidence is empty`);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
for (const entry of entries) {
|
|
52
|
+
if (!entry?.path || !entry?.name) {
|
|
53
|
+
issues.push(`${capability.id}: ${dimension} evidence is incomplete`);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (!sourceTests.has(entry.path))
|
|
57
|
+
issues.push(
|
|
58
|
+
`${capability.id}: ${dimension} evidence is outside the reverse-scanned test set: ${entry.path}`,
|
|
59
|
+
);
|
|
60
|
+
const absolute = path.join(root, entry.path);
|
|
61
|
+
if (!fs.existsSync(absolute)) {
|
|
62
|
+
issues.push(
|
|
63
|
+
`${capability.id}: evidence path is missing: ${entry.path}`,
|
|
64
|
+
);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const source = fs.readFileSync(absolute, "utf8");
|
|
68
|
+
if (!source.includes(`test(${JSON.stringify(entry.name)}`))
|
|
69
|
+
issues.push(
|
|
70
|
+
`${capability.id}: evidence test is missing from ${entry.path}: ${entry.name}`,
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function validateResidualClosure(mechanism, capability, issues) {
|
|
77
|
+
const sourceResiduals = mechanism.unresolved || [];
|
|
78
|
+
const closed = capability.resolvedResiduals || [];
|
|
79
|
+
if (
|
|
80
|
+
!sameMembers(
|
|
81
|
+
sourceResiduals,
|
|
82
|
+
closed.map((entry) => entry?.source || ""),
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
issues.push(
|
|
86
|
+
`${capability.id}: resolved residual identities do not match the reverse scan`,
|
|
87
|
+
);
|
|
88
|
+
for (const entry of closed) {
|
|
89
|
+
if (!String(entry?.resolution || "").trim())
|
|
90
|
+
issues.push(`${capability.id}: residual resolution is empty`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function validateImplementation(root, mechanism, capability, issues) {
|
|
95
|
+
if (
|
|
96
|
+
!Array.isArray(capability.implementation) ||
|
|
97
|
+
capability.implementation.length === 0
|
|
98
|
+
) {
|
|
99
|
+
issues.push(`${capability.id}: implementation route is empty`);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const reverseScanned = new Set(mechanism.sourcePaths || []);
|
|
103
|
+
let boundToReverseScan = false;
|
|
104
|
+
for (const relativePath of capability.implementation) {
|
|
105
|
+
if (!fs.existsSync(path.join(root, relativePath)))
|
|
106
|
+
issues.push(
|
|
107
|
+
`${capability.id}: implementation path is missing: ${relativePath}`,
|
|
108
|
+
);
|
|
109
|
+
if (reverseScanned.has(relativePath)) boundToReverseScan = true;
|
|
110
|
+
}
|
|
111
|
+
if (!boundToReverseScan)
|
|
112
|
+
issues.push(
|
|
113
|
+
`${capability.id}: implementation is not bound to a reverse-scanned v3 mechanism route`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function validateAuthority(capability, stateMachine, issues) {
|
|
118
|
+
for (const field of [
|
|
119
|
+
"exactHeadFence",
|
|
120
|
+
"immutableRootFence",
|
|
121
|
+
"providerReadbackBeforeRetry",
|
|
122
|
+
"idempotentReplay",
|
|
123
|
+
]) {
|
|
124
|
+
if (capability.invariants?.[field] !== true)
|
|
125
|
+
issues.push(`${capability.id}: ${field} must remain true`);
|
|
126
|
+
}
|
|
127
|
+
if (capability.routeKind !== "v4-native")
|
|
128
|
+
issues.push(`${capability.id}: routeKind must be v4-native`);
|
|
129
|
+
if (capability.legacyFallbackAllowed !== false)
|
|
130
|
+
issues.push(`${capability.id}: legacy fallback must remain disabled`);
|
|
131
|
+
if (capability.providerDecisionAuthorityAllowed !== false)
|
|
132
|
+
issues.push(
|
|
133
|
+
`${capability.id}: provider decision authority must remain disabled`,
|
|
134
|
+
);
|
|
135
|
+
if (stateMachine) {
|
|
136
|
+
if (capability.stateAuthority !== "single-writer-state-machine")
|
|
137
|
+
issues.push(
|
|
138
|
+
`${capability.id}: state-machine authority classification drifted`,
|
|
139
|
+
);
|
|
140
|
+
if (
|
|
141
|
+
stateMachine.writer?.authoritative !== true ||
|
|
142
|
+
stateMachine.writer?.secondWriterBudget !== 0
|
|
143
|
+
)
|
|
144
|
+
issues.push(`${capability.id}: v4 state machine is not single-writer`);
|
|
145
|
+
if (
|
|
146
|
+
!Array.isArray(stateMachine.recovery) ||
|
|
147
|
+
stateMachine.recovery.length === 0
|
|
148
|
+
)
|
|
149
|
+
issues.push(`${capability.id}: v4 state machine recovery is empty`);
|
|
150
|
+
} else if (capability.stateAuthority !== "bounded-protocol") {
|
|
151
|
+
issues.push(
|
|
152
|
+
`${capability.id}: bounded protocol authority classification drifted`,
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function validateRuntimeSemanticClosure({
|
|
158
|
+
root = process.cwd(),
|
|
159
|
+
manifest = loadJson(root, V4_RUNTIME_SEMANTIC_CLOSURE_PATH),
|
|
160
|
+
v3Inventory = loadJson(root, "architecture/v3-core-mechanism-inventory.json"),
|
|
161
|
+
v4Manifest = loadJson(
|
|
162
|
+
root,
|
|
163
|
+
"architecture/v4-capability-state-machine-manifest.json",
|
|
164
|
+
),
|
|
165
|
+
} = {}) {
|
|
166
|
+
const issues = [];
|
|
167
|
+
if (
|
|
168
|
+
manifest?.schemaVersion !== 1 ||
|
|
169
|
+
manifest?.contract !== V4_RUNTIME_SEMANTIC_CLOSURE_CONTRACT
|
|
170
|
+
)
|
|
171
|
+
issues.push(
|
|
172
|
+
"runtime semantic closure contract or schemaVersion is invalid",
|
|
173
|
+
);
|
|
174
|
+
if (!sameMembers(manifest?.requiredEvidence || [], REQUIRED_RUNTIME_EVIDENCE))
|
|
175
|
+
issues.push("runtime semantic closure evidence dimensions drifted");
|
|
176
|
+
|
|
177
|
+
const mechanisms = new Map(
|
|
178
|
+
(v3Inventory.mechanisms || []).map((entry) => [entry.id, entry]),
|
|
179
|
+
);
|
|
180
|
+
const v4Capabilities = new Map(
|
|
181
|
+
(v4Manifest.capabilities || []).map((entry) => [entry.id, entry]),
|
|
182
|
+
);
|
|
183
|
+
const stateMachines = new Map(
|
|
184
|
+
(v4Manifest.stateMachines || []).map((entry) => [entry.id, entry]),
|
|
185
|
+
);
|
|
186
|
+
const capabilities = Array.isArray(manifest?.capabilities)
|
|
187
|
+
? manifest.capabilities
|
|
188
|
+
: [];
|
|
189
|
+
if (
|
|
190
|
+
!sameMembers(
|
|
191
|
+
capabilities.map((entry) => entry.id),
|
|
192
|
+
mechanisms.keys(),
|
|
193
|
+
)
|
|
194
|
+
)
|
|
195
|
+
issues.push(
|
|
196
|
+
"runtime semantic closure does not cover every v3 mechanism exactly once",
|
|
197
|
+
);
|
|
198
|
+
if (
|
|
199
|
+
!sameMembers(
|
|
200
|
+
capabilities.map((entry) => entry.id),
|
|
201
|
+
v4Capabilities.keys(),
|
|
202
|
+
)
|
|
203
|
+
)
|
|
204
|
+
issues.push(
|
|
205
|
+
"runtime semantic closure does not match the v4 capability manifest",
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
const surfaces = publicSurfaceIds(root);
|
|
209
|
+
const ids = new Set();
|
|
210
|
+
for (const capability of capabilities) {
|
|
211
|
+
if (!capability?.id || ids.has(capability.id)) {
|
|
212
|
+
issues.push(
|
|
213
|
+
`runtime semantic capability id is missing or duplicated: ${capability?.id || "<empty>"}`,
|
|
214
|
+
);
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
ids.add(capability.id);
|
|
218
|
+
const mechanism = mechanisms.get(capability.id);
|
|
219
|
+
const v4Capability = v4Capabilities.get(capability.id);
|
|
220
|
+
if (!mechanism || !v4Capability) continue;
|
|
221
|
+
if (capability.sourceMechanismId !== capability.id)
|
|
222
|
+
issues.push(`${capability.id}: source mechanism identity drifted`);
|
|
223
|
+
validateImplementation(root, mechanism, capability, issues);
|
|
224
|
+
validateResidualClosure(mechanism, capability, issues);
|
|
225
|
+
validateEvidence(root, mechanism, capability, issues);
|
|
226
|
+
validateAuthority(capability, stateMachines.get(capability.id), issues);
|
|
227
|
+
for (const surface of v4Capability.surfaces || []) {
|
|
228
|
+
if (!surfaces.has(surface))
|
|
229
|
+
issues.push(
|
|
230
|
+
`${capability.id}: executable v4 surface is absent: ${surface}`,
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (issues.length)
|
|
236
|
+
throw new Error(
|
|
237
|
+
`v4 runtime semantic closure failed:\n- ${issues.join("\n- ")}`,
|
|
238
|
+
);
|
|
239
|
+
return {
|
|
240
|
+
capabilities: capabilities.length,
|
|
241
|
+
evidenceDimensions: capabilities.length * REQUIRED_RUNTIME_EVIDENCE.length,
|
|
242
|
+
residualsClosed: capabilities.reduce(
|
|
243
|
+
(sum, entry) => sum + entry.resolvedResiduals.length,
|
|
244
|
+
0,
|
|
245
|
+
),
|
|
246
|
+
legacyFallbacks: 0,
|
|
247
|
+
providerDecisionAuthorities: 0,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (
|
|
252
|
+
process.argv[1] &&
|
|
253
|
+
import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href
|
|
254
|
+
) {
|
|
255
|
+
try {
|
|
256
|
+
const report = validateRuntimeSemanticClosure();
|
|
257
|
+
console.log(
|
|
258
|
+
`v4 runtime semantic closure passed: ${report.capabilities} capabilities, ` +
|
|
259
|
+
`${report.evidenceDimensions} evidence dimensions, ${report.residualsClosed} reverse-scan residuals closed, ` +
|
|
260
|
+
"0 legacy fallbacks, 0 provider decision authorities",
|
|
261
|
+
);
|
|
262
|
+
} catch (error) {
|
|
263
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
264
|
+
process.exitCode = 1;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
|
|
5
|
+
const SHA = /^[0-9a-f]{40}$/u;
|
|
6
|
+
|
|
7
|
+
function exactSha(value, label) {
|
|
8
|
+
const normalized = String(value || "")
|
|
9
|
+
.trim()
|
|
10
|
+
.toLowerCase();
|
|
11
|
+
if (!SHA.test(normalized))
|
|
12
|
+
throw new Error(`${label} must be an exact 40-hex Git SHA`);
|
|
13
|
+
return normalized;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function git(args, { cwd, encoding = "utf8" }) {
|
|
17
|
+
const result = spawnSync("git", args, {
|
|
18
|
+
cwd,
|
|
19
|
+
encoding,
|
|
20
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
21
|
+
});
|
|
22
|
+
if (result.status !== 0)
|
|
23
|
+
throw new Error(
|
|
24
|
+
`git ${args.join(" ")} failed: ${String(result.stderr || "").trim()}`,
|
|
25
|
+
);
|
|
26
|
+
return encoding === null ? result.stdout : String(result.stdout || "").trim();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function patchRoot(base, head, cwd) {
|
|
30
|
+
const patch = git(
|
|
31
|
+
[
|
|
32
|
+
"diff",
|
|
33
|
+
"--binary",
|
|
34
|
+
"--full-index",
|
|
35
|
+
"--no-ext-diff",
|
|
36
|
+
`${base}..${head}`,
|
|
37
|
+
],
|
|
38
|
+
{ cwd, encoding: null },
|
|
39
|
+
);
|
|
40
|
+
return `sha256:${crypto.createHash("sha256").update(patch).digest("hex")}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function linearRange(base, head, cwd) {
|
|
44
|
+
try {
|
|
45
|
+
git(["merge-base", "--is-ancestor", base, head], { cwd });
|
|
46
|
+
} catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
const commits = git(
|
|
50
|
+
["rev-list", "--reverse", "--topo-order", `${base}..${head}`],
|
|
51
|
+
{ cwd },
|
|
52
|
+
)
|
|
53
|
+
.split(/\s+/u)
|
|
54
|
+
.filter(Boolean);
|
|
55
|
+
if (commits.length === 0) return null;
|
|
56
|
+
let expectedParent = base;
|
|
57
|
+
const trees = [];
|
|
58
|
+
const patchRoots = [];
|
|
59
|
+
for (const rangeCommit of commits) {
|
|
60
|
+
const [observedCommit, ...parents] = git(
|
|
61
|
+
["rev-list", "--parents", "-n", "1", rangeCommit],
|
|
62
|
+
{ cwd },
|
|
63
|
+
).split(/\s+/u);
|
|
64
|
+
if (
|
|
65
|
+
observedCommit !== rangeCommit ||
|
|
66
|
+
parents.length !== 1 ||
|
|
67
|
+
parents[0] !== expectedParent
|
|
68
|
+
) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
trees.push(
|
|
72
|
+
exactSha(
|
|
73
|
+
git(["rev-parse", `${rangeCommit}^{tree}`], { cwd }),
|
|
74
|
+
"replayedCommitTree",
|
|
75
|
+
),
|
|
76
|
+
);
|
|
77
|
+
patchRoots.push(patchRoot(expectedParent, rangeCommit, cwd));
|
|
78
|
+
expectedParent = rangeCommit;
|
|
79
|
+
}
|
|
80
|
+
return { commits, trees, patchRoots };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function exactMergeGroupBinding(input = {}) {
|
|
84
|
+
const cwd = path.resolve(input.cwd || process.cwd());
|
|
85
|
+
const mergeGroupHead = exactSha(input.mergeGroupHead, "mergeGroupHead");
|
|
86
|
+
const expectedTree = exactSha(input.mergeGroupTree, "mergeGroupTree");
|
|
87
|
+
const qualifiedBase = exactSha(input.qualifiedBase, "qualifiedBase");
|
|
88
|
+
const currentBase = exactSha(input.currentBase, "currentBase");
|
|
89
|
+
const sourceHead = exactSha(input.sourceHead, "sourceHead");
|
|
90
|
+
const sourcePatchRoot = String(input.sourcePatchRoot || "").trim();
|
|
91
|
+
const actualTree = exactSha(
|
|
92
|
+
git(["rev-parse", `${mergeGroupHead}^{tree}`], { cwd }),
|
|
93
|
+
"mergeGroupTree",
|
|
94
|
+
);
|
|
95
|
+
if (actualTree !== expectedTree)
|
|
96
|
+
return { ok: false, reason: "merge-group-tree-mismatch" };
|
|
97
|
+
try {
|
|
98
|
+
git(["merge-base", "--is-ancestor", qualifiedBase, currentBase], { cwd });
|
|
99
|
+
} catch {
|
|
100
|
+
return {
|
|
101
|
+
ok: false,
|
|
102
|
+
reason: "current-base-not-descendant-of-qualified-base",
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
if (!/^sha256:[0-9a-f]{64}$/u.test(sourcePatchRoot))
|
|
106
|
+
throw new Error("sourcePatchRoot must be an exact SHA-256 root");
|
|
107
|
+
if (patchRoot(currentBase, mergeGroupHead, cwd) !== sourcePatchRoot)
|
|
108
|
+
return { ok: false, reason: "merge-group-source-patch-mismatch" };
|
|
109
|
+
|
|
110
|
+
const [commit, ...parents] = git(
|
|
111
|
+
["rev-list", "--parents", "-n", "1", mergeGroupHead],
|
|
112
|
+
{ cwd },
|
|
113
|
+
).split(/\s+/u);
|
|
114
|
+
if (commit !== mergeGroupHead)
|
|
115
|
+
return { ok: false, reason: "merge-group-parent-mismatch" };
|
|
116
|
+
if (
|
|
117
|
+
parents.length === 2 &&
|
|
118
|
+
parents[0] === currentBase &&
|
|
119
|
+
parents[1] === sourceHead
|
|
120
|
+
) {
|
|
121
|
+
return {
|
|
122
|
+
ok: true,
|
|
123
|
+
mergeGroupHead,
|
|
124
|
+
mergeGroupTree: actualTree,
|
|
125
|
+
parents,
|
|
126
|
+
compositionMode: "two-parent-merge",
|
|
127
|
+
replayedCommitTrees: [],
|
|
128
|
+
replayedCommitPatchRoots: [],
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const sourceRange = linearRange(qualifiedBase, sourceHead, cwd);
|
|
133
|
+
const mergeGroupRange = linearRange(currentBase, mergeGroupHead, cwd);
|
|
134
|
+
if (
|
|
135
|
+
!sourceRange ||
|
|
136
|
+
!mergeGroupRange ||
|
|
137
|
+
sourceRange.commits.length !== mergeGroupRange.commits.length ||
|
|
138
|
+
sourceRange.patchRoots.some(
|
|
139
|
+
(root, index) => root !== mergeGroupRange.patchRoots[index],
|
|
140
|
+
)
|
|
141
|
+
) {
|
|
142
|
+
return { ok: false, reason: "merge-group-parent-mismatch" };
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
ok: true,
|
|
146
|
+
mergeGroupHead,
|
|
147
|
+
mergeGroupTree: actualTree,
|
|
148
|
+
parents,
|
|
149
|
+
compositionMode: "linear-replay",
|
|
150
|
+
replayedCommitTrees: mergeGroupRange.trees,
|
|
151
|
+
replayedCommitPatchRoots: mergeGroupRange.patchRoots,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
verifySourceQualificationProof,
|
|
12
12
|
} from "../packages/core/dev-delivery-warrant.js";
|
|
13
13
|
import { validateControllerReceipt } from "../packages/core/controller-evidence.js";
|
|
14
|
+
import { exactMergeGroupBinding } from "./dev-delivery-source-proof-replay.mjs";
|
|
14
15
|
|
|
15
16
|
const REUSE_DECISION_SCHEMA =
|
|
16
17
|
"kungfu.buildchain.source-qualification-reuse-decision/v1";
|
|
@@ -113,98 +114,6 @@ function gitPathRoot(ref, paths, label, cwd) {
|
|
|
113
114
|
});
|
|
114
115
|
}
|
|
115
116
|
|
|
116
|
-
function exactMergeGroupBinding(input = {}) {
|
|
117
|
-
const cwd = path.resolve(input.cwd || process.cwd());
|
|
118
|
-
const mergeGroupHead = exactSha(input.mergeGroupHead, "mergeGroupHead");
|
|
119
|
-
const expectedTree = exactSha(input.mergeGroupTree, "mergeGroupTree");
|
|
120
|
-
const currentBase = exactSha(input.currentBase, "currentBase");
|
|
121
|
-
const sourceHead = exactSha(input.sourceHead, "sourceHead");
|
|
122
|
-
const actualTree = exactSha(
|
|
123
|
-
git(["rev-parse", `${mergeGroupHead}^{tree}`], { cwd }),
|
|
124
|
-
"mergeGroupTree",
|
|
125
|
-
);
|
|
126
|
-
if (actualTree !== expectedTree)
|
|
127
|
-
return { ok: false, reason: "merge-group-tree-mismatch" };
|
|
128
|
-
const [commit, ...parents] = git(
|
|
129
|
-
["rev-list", "--parents", "-n", "1", mergeGroupHead],
|
|
130
|
-
{ cwd },
|
|
131
|
-
).split(/\s+/u);
|
|
132
|
-
if (commit !== mergeGroupHead) {
|
|
133
|
-
return { ok: false, reason: "merge-group-parent-mismatch" };
|
|
134
|
-
}
|
|
135
|
-
if (
|
|
136
|
-
parents.length === 2 &&
|
|
137
|
-
parents[0] === currentBase &&
|
|
138
|
-
parents[1] === sourceHead
|
|
139
|
-
) {
|
|
140
|
-
return {
|
|
141
|
-
ok: true,
|
|
142
|
-
mergeGroupHead,
|
|
143
|
-
mergeGroupTree: actualTree,
|
|
144
|
-
parents,
|
|
145
|
-
compositionMode: "two-parent-merge",
|
|
146
|
-
replayedCommitTrees: [],
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const linearRange = (head) => {
|
|
151
|
-
try {
|
|
152
|
-
git(["merge-base", "--is-ancestor", currentBase, head], { cwd });
|
|
153
|
-
} catch {
|
|
154
|
-
return null;
|
|
155
|
-
}
|
|
156
|
-
const commits = git(
|
|
157
|
-
["rev-list", "--reverse", "--topo-order", `${currentBase}..${head}`],
|
|
158
|
-
{ cwd },
|
|
159
|
-
)
|
|
160
|
-
.split(/\s+/u)
|
|
161
|
-
.filter(Boolean);
|
|
162
|
-
if (commits.length === 0) return null;
|
|
163
|
-
let expectedParent = currentBase;
|
|
164
|
-
const trees = [];
|
|
165
|
-
for (const rangeCommit of commits) {
|
|
166
|
-
const [observedCommit, ...rangeParents] = git(
|
|
167
|
-
["rev-list", "--parents", "-n", "1", rangeCommit],
|
|
168
|
-
{ cwd },
|
|
169
|
-
).split(/\s+/u);
|
|
170
|
-
if (
|
|
171
|
-
observedCommit !== rangeCommit ||
|
|
172
|
-
rangeParents.length !== 1 ||
|
|
173
|
-
rangeParents[0] !== expectedParent
|
|
174
|
-
) {
|
|
175
|
-
return null;
|
|
176
|
-
}
|
|
177
|
-
trees.push(
|
|
178
|
-
exactSha(
|
|
179
|
-
git(["rev-parse", `${rangeCommit}^{tree}`], { cwd }),
|
|
180
|
-
"replayedCommitTree",
|
|
181
|
-
),
|
|
182
|
-
);
|
|
183
|
-
expectedParent = rangeCommit;
|
|
184
|
-
}
|
|
185
|
-
return trees;
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
const sourceTrees = linearRange(sourceHead);
|
|
189
|
-
const mergeGroupTrees = linearRange(mergeGroupHead);
|
|
190
|
-
if (
|
|
191
|
-
!sourceTrees ||
|
|
192
|
-
!mergeGroupTrees ||
|
|
193
|
-
sourceTrees.length !== mergeGroupTrees.length ||
|
|
194
|
-
sourceTrees.some((tree, index) => tree !== mergeGroupTrees[index])
|
|
195
|
-
) {
|
|
196
|
-
return { ok: false, reason: "merge-group-parent-mismatch" };
|
|
197
|
-
}
|
|
198
|
-
return {
|
|
199
|
-
ok: true,
|
|
200
|
-
mergeGroupHead,
|
|
201
|
-
mergeGroupTree: actualTree,
|
|
202
|
-
parents,
|
|
203
|
-
compositionMode: "linear-replay",
|
|
204
|
-
replayedCommitTrees: mergeGroupTrees,
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
|
|
208
117
|
export function sourceQualificationPredicates(input = {}) {
|
|
209
118
|
const cwd = path.resolve(input.cwd || process.cwd());
|
|
210
119
|
const repository = required(input.repository, "repository");
|
|
@@ -397,7 +306,8 @@ export function materializeReuseLifecycleEvidence({
|
|
|
397
306
|
decisionRoot: decision.decisionRoot,
|
|
398
307
|
sourceWorkflowRunId: decision.sourceWorkflowRunId,
|
|
399
308
|
sourceHead: decision.sourceHead,
|
|
400
|
-
qualifiedBase: decision.
|
|
309
|
+
qualifiedBase: decision.qualifiedBase,
|
|
310
|
+
currentBase: decision.currentBase,
|
|
401
311
|
mergeGroupHead: decision.mergeGroupHead,
|
|
402
312
|
mergeGroupTree: decision.mergeGroupTree,
|
|
403
313
|
},
|
|
@@ -446,7 +356,7 @@ export function verifySourceQualificationReuse(input = {}) {
|
|
|
446
356
|
const verification = verifySourceQualificationProof(proof, {
|
|
447
357
|
repository: required(input.repository, "repository"),
|
|
448
358
|
protectedBase: required(input.protectedBase, "protectedBase"),
|
|
449
|
-
qualifiedBase: exactSha(
|
|
359
|
+
qualifiedBase: exactSha(proof.qualifiedBase, "qualifiedBase"),
|
|
450
360
|
sourceHead: exactSha(input.sourceHead, "sourceHead"),
|
|
451
361
|
sourceWorkflowRunId: Number(input.sourceWorkflowRunId),
|
|
452
362
|
controllerReceiptRoot: receipt.digest,
|
|
@@ -462,7 +372,7 @@ export function verifySourceQualificationReuse(input = {}) {
|
|
|
462
372
|
}
|
|
463
373
|
const current = sourceQualificationPredicates({
|
|
464
374
|
...input,
|
|
465
|
-
qualifiedBase:
|
|
375
|
+
qualifiedBase: proof.qualifiedBase,
|
|
466
376
|
});
|
|
467
377
|
const classification = classifyDevDeliveryDelta({
|
|
468
378
|
proof,
|
|
@@ -476,7 +386,11 @@ export function verifySourceQualificationReuse(input = {}) {
|
|
|
476
386
|
reason: classification.reason,
|
|
477
387
|
});
|
|
478
388
|
}
|
|
479
|
-
const mergeGroup = exactMergeGroupBinding(
|
|
389
|
+
const mergeGroup = exactMergeGroupBinding({
|
|
390
|
+
...input,
|
|
391
|
+
qualifiedBase: proof.qualifiedBase,
|
|
392
|
+
sourcePatchRoot: proof.sourcePatchRoot,
|
|
393
|
+
});
|
|
480
394
|
if (!mergeGroup.ok) {
|
|
481
395
|
return rootedDecision({
|
|
482
396
|
schema: REUSE_DECISION_SCHEMA,
|
|
@@ -493,12 +407,14 @@ export function verifySourceQualificationReuse(input = {}) {
|
|
|
493
407
|
sourceProofRoot: proof.proofRoot,
|
|
494
408
|
sourceWorkflowRunId: proof.sourceWorkflowRunId,
|
|
495
409
|
sourceHead: proof.sourceHead,
|
|
496
|
-
|
|
410
|
+
qualifiedBase: proof.qualifiedBase,
|
|
411
|
+
currentBase: exactSha(input.currentBase, "currentBase"),
|
|
497
412
|
mergeGroupHead: mergeGroup.mergeGroupHead,
|
|
498
413
|
mergeGroupTree: mergeGroup.mergeGroupTree,
|
|
499
414
|
mergeGroupParents: mergeGroup.parents,
|
|
500
415
|
mergeGroupCompositionMode: mergeGroup.compositionMode,
|
|
501
416
|
mergeGroupReplayTrees: mergeGroup.replayedCommitTrees,
|
|
417
|
+
mergeGroupReplayPatchRoots: mergeGroup.replayedCommitPatchRoots,
|
|
502
418
|
predicateRoots: {
|
|
503
419
|
sourceIdentityRoot: proof.sourceIdentityRoot,
|
|
504
420
|
sourcePatchRoot: proof.sourcePatchRoot,
|
|
@@ -7,6 +7,32 @@ export function hasFlag(args, name) {
|
|
|
7
7
|
return args.includes(`--${name}`);
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
function terminalEvidenceOptions(rest, environment) {
|
|
11
|
+
return {
|
|
12
|
+
outcome: flag(rest, "outcome", environment.BUILDCHAIN_DEV_DELIVERY_OUTCOME),
|
|
13
|
+
eventAction: flag(
|
|
14
|
+
rest,
|
|
15
|
+
"event-action",
|
|
16
|
+
environment.BUILDCHAIN_DEV_DELIVERY_EVENT_ACTION,
|
|
17
|
+
),
|
|
18
|
+
evidenceRoot: flag(
|
|
19
|
+
rest,
|
|
20
|
+
"evidence-root",
|
|
21
|
+
environment.BUILDCHAIN_DEV_DELIVERY_EVIDENCE_ROOT,
|
|
22
|
+
),
|
|
23
|
+
expectedPriorEvidenceRoot: flag(
|
|
24
|
+
rest,
|
|
25
|
+
"expected-prior-evidence-root",
|
|
26
|
+
environment.BUILDCHAIN_DEV_DELIVERY_EXPECTED_PRIOR_EVIDENCE_ROOT,
|
|
27
|
+
),
|
|
28
|
+
integrationProofPath: flag(
|
|
29
|
+
rest,
|
|
30
|
+
"integration-proof",
|
|
31
|
+
environment.BUILDCHAIN_DEV_DELIVERY_INTEGRATION_PROOF,
|
|
32
|
+
),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
10
36
|
export function devDeliveryCliOptions(args = [], environment = process.env) {
|
|
11
37
|
const [command = "", ...rest] = args;
|
|
12
38
|
return {
|
|
@@ -196,17 +222,7 @@ export function devDeliveryCliOptions(args = [], environment = process.env) {
|
|
|
196
222
|
"lease-seconds",
|
|
197
223
|
environment.BUILDCHAIN_DEV_DELIVERY_LEASE_SECONDS,
|
|
198
224
|
),
|
|
199
|
-
|
|
200
|
-
eventAction: flag(
|
|
201
|
-
rest,
|
|
202
|
-
"event-action",
|
|
203
|
-
environment.BUILDCHAIN_DEV_DELIVERY_EVENT_ACTION,
|
|
204
|
-
),
|
|
205
|
-
evidenceRoot: flag(
|
|
206
|
-
rest,
|
|
207
|
-
"evidence-root",
|
|
208
|
-
environment.BUILDCHAIN_DEV_DELIVERY_EVIDENCE_ROOT,
|
|
209
|
-
),
|
|
225
|
+
...terminalEvidenceOptions(rest, environment),
|
|
210
226
|
transferRoot: flag(rest, "transfer-root"),
|
|
211
227
|
finalizerBoundaryRoot: flag(rest, "finalizer-boundary-root"),
|
|
212
228
|
nativeJobId: flag(rest, "native-job-id"),
|
|
@@ -46,7 +46,7 @@ function decodeBlob(blob) {
|
|
|
46
46
|
).toString("utf8");
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
function validateStoredState(queue) {
|
|
49
|
+
function validateStoredState(queue, { allowLegacyV3Readback = false } = {}) {
|
|
50
50
|
const body = structuredClone(queue || {});
|
|
51
51
|
const embeddedStateRoot = body.stateRoot;
|
|
52
52
|
delete body.stateRoot;
|
|
@@ -59,7 +59,7 @@ function validateStoredState(queue) {
|
|
|
59
59
|
) {
|
|
60
60
|
normalizeDevDeliveryAuthorityState(queue);
|
|
61
61
|
} else {
|
|
62
|
-
normalizeDevDeliveryQueue(queue);
|
|
62
|
+
normalizeDevDeliveryQueue(queue, { allowLegacyV3Readback });
|
|
63
63
|
}
|
|
64
64
|
return queue;
|
|
65
65
|
}
|
|
@@ -108,7 +108,7 @@ export class GitHubDevDeliveryStore {
|
|
|
108
108
|
return data;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
async read({ stateRef, protectedBase, now }) {
|
|
111
|
+
async read({ stateRef, protectedBase, now, allowLegacyV3Readback = false }) {
|
|
112
112
|
let ref;
|
|
113
113
|
try {
|
|
114
114
|
ref = await this.request(
|
|
@@ -128,11 +128,13 @@ export class GitHubDevDeliveryStore {
|
|
|
128
128
|
};
|
|
129
129
|
}
|
|
130
130
|
const commitSha = exactSha(ref?.object?.sha, "state ref commit");
|
|
131
|
-
const readback = await this.readCommit(commitSha
|
|
131
|
+
const readback = await this.readCommit(commitSha, {
|
|
132
|
+
allowLegacyV3Readback,
|
|
133
|
+
});
|
|
132
134
|
return { exists: true, ...readback };
|
|
133
135
|
}
|
|
134
136
|
|
|
135
|
-
async readCommit(commitShaInput) {
|
|
137
|
+
async readCommit(commitShaInput, { allowLegacyV3Readback = false } = {}) {
|
|
136
138
|
const commitSha = exactSha(commitShaInput, "state commit");
|
|
137
139
|
const commit = await this.request(
|
|
138
140
|
"GET",
|
|
@@ -152,7 +154,9 @@ export class GitHubDevDeliveryStore {
|
|
|
152
154
|
`/repos/${this.repository.fullName}/git/blobs/${entry.sha}`,
|
|
153
155
|
);
|
|
154
156
|
const bytes = decodeBlob(blob);
|
|
155
|
-
const queue = validateStoredState(JSON.parse(bytes)
|
|
157
|
+
const queue = validateStoredState(JSON.parse(bytes), {
|
|
158
|
+
allowLegacyV3Readback,
|
|
159
|
+
});
|
|
156
160
|
return { commitSha, queue, bytes };
|
|
157
161
|
}
|
|
158
162
|
|