@tiangong-lca/cli 0.0.28 → 0.0.30
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 +90 -2
- package/dist/src/cli.js +553 -4
- package/dist/src/cli.js.map +1 -1
- package/dist/src/lib/dataset-command.js +11 -0
- package/dist/src/lib/dataset-command.js.map +1 -1
- package/dist/src/lib/dataset-maintenance-contract.js.map +1 -1
- package/dist/src/lib/dataset-maintenance-flow-identity-approval-claim.js +175 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-approval-claim.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-capture.js +511 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-capture.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-command.js +26 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-command.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-contract.js +784 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-contract.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-execution-contract.js +1317 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-execution-contract.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-freeze.js +342 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-freeze.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-plan.js +900 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-plan.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-recovery.js +688 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-recovery.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-run.js +1369 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-run.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-seal.js +144 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-seal.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-verify.js +377 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-verify.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-wire.js +178 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-wire.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-remote.js +156 -10
- package/dist/src/lib/dataset-maintenance-remote.js.map +1 -1
- package/dist/src/lib/dataset-save-draft-run.js +667 -0
- package/dist/src/lib/dataset-save-draft-run.js.map +1 -1
- package/dist/src/lib/http.js.map +1 -1
- package/dist/src/lib/lca-release.js +683 -0
- package/dist/src/lib/lca-release.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,1317 @@
|
|
|
1
|
+
import { assertCurrentFlowIdentityAuthority, parseFlowIdentityPlan, } from './dataset-maintenance-flow-identity-contract.js';
|
|
2
|
+
import { isJsonObject, sha256Json, sha256Text, } from './dataset-maintenance-contract.js';
|
|
3
|
+
import { flowIdentityRestrictedSha256 } from './dataset-maintenance-flow-identity-wire.js';
|
|
4
|
+
import { CliError } from './errors.js';
|
|
5
|
+
const HASH_PATTERN = /^[a-f0-9]{64}$/u;
|
|
6
|
+
const UUID_PATTERN = /^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/u;
|
|
7
|
+
function fail(message, code = 'DATASET_FLOW_IDENTITY_EXECUTION_CONTRACT_INVALID') {
|
|
8
|
+
throw new CliError(message, { code, exitCode: 2 });
|
|
9
|
+
}
|
|
10
|
+
function token(value, label) {
|
|
11
|
+
if (typeof value !== 'string' || !value.trim())
|
|
12
|
+
fail(`${label} must be a non-empty string.`);
|
|
13
|
+
return value.trim();
|
|
14
|
+
}
|
|
15
|
+
function hash(value, label) {
|
|
16
|
+
const normalized = token(value, label);
|
|
17
|
+
if (!HASH_PATTERN.test(normalized))
|
|
18
|
+
fail(`${label} must be a lowercase SHA-256.`);
|
|
19
|
+
return normalized;
|
|
20
|
+
}
|
|
21
|
+
function uuid(value, label) {
|
|
22
|
+
const normalized = token(value, label);
|
|
23
|
+
if (!UUID_PATTERN.test(normalized))
|
|
24
|
+
fail(`${label} must be a canonical lowercase UUID.`);
|
|
25
|
+
return normalized;
|
|
26
|
+
}
|
|
27
|
+
function instant(value, label) {
|
|
28
|
+
const normalized = token(value, label);
|
|
29
|
+
if (!Number.isFinite(Date.parse(normalized)))
|
|
30
|
+
fail(`${label} must be an RFC3339 timestamp.`);
|
|
31
|
+
return normalized;
|
|
32
|
+
}
|
|
33
|
+
function integer(value, label, minimum = 0) {
|
|
34
|
+
if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < minimum) {
|
|
35
|
+
fail(`${label} must be an integer >= ${minimum}.`);
|
|
36
|
+
}
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
function boolean(value, label) {
|
|
40
|
+
if (typeof value !== 'boolean')
|
|
41
|
+
fail(`${label} must be boolean.`);
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
function deterministicUuidFromSha256(digest) {
|
|
45
|
+
const chars = digest.slice(0, 32).split('');
|
|
46
|
+
chars[12] = '5';
|
|
47
|
+
chars[16] = ((Number.parseInt(chars[16], 16) & 0x3) | 0x8).toString(16);
|
|
48
|
+
const hex = chars.join('');
|
|
49
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
50
|
+
}
|
|
51
|
+
export function computeFlowIdentityFreezeSha256(freeze) {
|
|
52
|
+
return sha256Json({ ...freeze, freeze_sha256: '' });
|
|
53
|
+
}
|
|
54
|
+
export function computeFlowIdentityApprovalIdentitySha256(approval) {
|
|
55
|
+
return sha256Json({ ...approval, execution_approval_identity_sha256: '' });
|
|
56
|
+
}
|
|
57
|
+
export function parseFlowIdentityFreeze(value, plan) {
|
|
58
|
+
if (!isJsonObject(value) || !isJsonObject(value.actor))
|
|
59
|
+
fail('Flow identity freeze is invalid.');
|
|
60
|
+
const freeze = value;
|
|
61
|
+
if (freeze.schema_version !== 'dataset-flow-identity-freeze.v2' ||
|
|
62
|
+
freeze.environment !== 'production' ||
|
|
63
|
+
plan.environment !== 'production' ||
|
|
64
|
+
freeze.project_ref !== plan.project_ref ||
|
|
65
|
+
freeze.actor.user_id !== plan.account.user_id ||
|
|
66
|
+
freeze.actor.email !== plan.account.email ||
|
|
67
|
+
freeze.plan_sha256 !== plan.plan_sha256 ||
|
|
68
|
+
freeze.operation_id !== plan.operation_id ||
|
|
69
|
+
freeze.capture_artifact_sha256 !== plan.capture_artifact_sha256 ||
|
|
70
|
+
freeze.receipt_id !== plan.receipt_id ||
|
|
71
|
+
freeze.receipt_proof_sha256 !== plan.receipt_proof_sha256 ||
|
|
72
|
+
freeze.capture_request_sha256 !== plan.capture_request_sha256 ||
|
|
73
|
+
freeze.source_guard_set_sha256 !== plan.source_guard_set_sha256 ||
|
|
74
|
+
freeze.support_guard_set_sha256 !== plan.support_guard_set_sha256 ||
|
|
75
|
+
freeze.target_guard_set_sha256 !== plan.target_guard_set_sha256 ||
|
|
76
|
+
freeze.mapping_guard_set_sha256 !== plan.mapping_guard_set_sha256 ||
|
|
77
|
+
freeze.process_intent_set_sha256 !== plan.process_intent_set_sha256 ||
|
|
78
|
+
freeze.receipt_protected_closure_sha256 !== plan.receipt_protected_closure_sha256 ||
|
|
79
|
+
freeze.capture_whole_scope_proof_sha256 !== plan.capture_whole_scope_proof_sha256 ||
|
|
80
|
+
freeze.source_universe_artifact_sha256 !== plan.source_universe_artifact_sha256 ||
|
|
81
|
+
freeze.support_snapshot_artifact_sha256 !== plan.support_snapshot_artifact_sha256 ||
|
|
82
|
+
freeze.mapping_artifact_sha256 !== plan.mapping_artifact_sha256 ||
|
|
83
|
+
freeze.process_manifest_artifact_sha256 !== plan.process_manifest_artifact_sha256 ||
|
|
84
|
+
freeze.protected_closure_artifact_sha256 !== plan.protected_closure_artifact_sha256 ||
|
|
85
|
+
freeze.policy_approval_text_sha256 !== plan.compatibility_policy.approval_text_sha256 ||
|
|
86
|
+
!UUID_PATTERN.test(freeze.receipt_id) ||
|
|
87
|
+
![
|
|
88
|
+
freeze.receipt_proof_sha256,
|
|
89
|
+
freeze.capture_request_sha256,
|
|
90
|
+
freeze.source_guard_set_sha256,
|
|
91
|
+
freeze.support_guard_set_sha256,
|
|
92
|
+
freeze.target_guard_set_sha256,
|
|
93
|
+
freeze.mapping_guard_set_sha256,
|
|
94
|
+
freeze.process_intent_set_sha256,
|
|
95
|
+
freeze.receipt_protected_closure_sha256,
|
|
96
|
+
freeze.capture_whole_scope_proof_sha256,
|
|
97
|
+
].every((digest) => HASH_PATTERN.test(digest)) ||
|
|
98
|
+
!HASH_PATTERN.test(freeze.toolchain_evidence_sha256) ||
|
|
99
|
+
!Number.isFinite(Date.parse(freeze.generated_at_utc)) ||
|
|
100
|
+
freeze.freeze_sha256 !== computeFlowIdentityFreezeSha256(freeze)) {
|
|
101
|
+
fail('Flow identity freeze does not exactly bind the immutable production plan.');
|
|
102
|
+
}
|
|
103
|
+
return freeze;
|
|
104
|
+
}
|
|
105
|
+
export function parseFlowIdentityApproval(value, plan, freeze) {
|
|
106
|
+
if (!isJsonObject(value) || !isJsonObject(value.actor))
|
|
107
|
+
fail('Flow identity approval is invalid.');
|
|
108
|
+
const approval = value;
|
|
109
|
+
assertCurrentFlowIdentityAuthority({ oracleSha256: approval.policy_approval_text_sha256 });
|
|
110
|
+
if (approval.schema_version !== 'dataset-flow-identity-execution-approval.v2' ||
|
|
111
|
+
approval.actor.user_id !== plan.account.user_id ||
|
|
112
|
+
approval.actor.email !== plan.account.email ||
|
|
113
|
+
approval.plan_sha256 !== plan.plan_sha256 ||
|
|
114
|
+
approval.freeze_sha256 !== freeze.freeze_sha256 ||
|
|
115
|
+
approval.toolchain_evidence_sha256 !== freeze.toolchain_evidence_sha256 ||
|
|
116
|
+
approval.policy_approval_text_sha256 !== plan.compatibility_policy.approval_text_sha256 ||
|
|
117
|
+
!HASH_PATTERN.test(approval.policy_approval_text_sha256) ||
|
|
118
|
+
!HASH_PATTERN.test(approval.execution_approval_request_sha256) ||
|
|
119
|
+
!HASH_PATTERN.test(approval.execution_approval_text_sha256) ||
|
|
120
|
+
new Set([
|
|
121
|
+
approval.policy_approval_text_sha256,
|
|
122
|
+
approval.execution_approval_request_sha256,
|
|
123
|
+
approval.execution_approval_text_sha256,
|
|
124
|
+
approval.execution_approval_identity_sha256,
|
|
125
|
+
]).size !== 4 ||
|
|
126
|
+
!Number.isFinite(Date.parse(approval.approved_at_utc)) ||
|
|
127
|
+
Date.parse(approval.approved_at_utc) < Date.parse(freeze.generated_at_utc) ||
|
|
128
|
+
approval.execution_approval_identity_sha256 !==
|
|
129
|
+
computeFlowIdentityApprovalIdentitySha256(approval)) {
|
|
130
|
+
fail('Flow identity approval does not exactly bind the plan/freeze/account/toolchain.');
|
|
131
|
+
}
|
|
132
|
+
return approval;
|
|
133
|
+
}
|
|
134
|
+
export function buildFlowIdentityExecutionIdentity(options) {
|
|
135
|
+
const body = {
|
|
136
|
+
environment: 'production',
|
|
137
|
+
project_ref: options.plan.project_ref,
|
|
138
|
+
actor: options.plan.account,
|
|
139
|
+
target_visibility: 'owner_draft',
|
|
140
|
+
operation_id: options.plan.operation_id,
|
|
141
|
+
plan_sha256: options.plan.plan_sha256,
|
|
142
|
+
freeze_sha256: options.freeze.freeze_sha256,
|
|
143
|
+
receipt_id: options.plan.receipt_id,
|
|
144
|
+
receipt_proof_sha256: options.plan.receipt_proof_sha256,
|
|
145
|
+
policy_approval_text_sha256: options.approval.policy_approval_text_sha256,
|
|
146
|
+
execution_approval_request_sha256: options.approval.execution_approval_request_sha256,
|
|
147
|
+
execution_approval_text_sha256: options.approval.execution_approval_text_sha256,
|
|
148
|
+
execution_approval_identity_sha256: options.approval.execution_approval_identity_sha256,
|
|
149
|
+
toolchain_evidence_sha256: options.freeze.toolchain_evidence_sha256,
|
|
150
|
+
};
|
|
151
|
+
const identitySha256 = flowIdentityRestrictedSha256(body);
|
|
152
|
+
return {
|
|
153
|
+
request_id: deterministicUuidFromSha256(sha256Text(`dataset-flow-identity-scope.v2\u0000${identitySha256}`)),
|
|
154
|
+
identity_sha256: identitySha256,
|
|
155
|
+
...body,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
export function buildFlowIdentityScopePreflightRequest(options) {
|
|
159
|
+
const request = {
|
|
160
|
+
schema_version: 'dataset-flow-identity-scope-preflight.v2',
|
|
161
|
+
request_id: options.identity.request_id,
|
|
162
|
+
receipt_id: options.identity.receipt_id,
|
|
163
|
+
receipt_proof_sha256: options.identity.receipt_proof_sha256,
|
|
164
|
+
environment: options.identity.environment,
|
|
165
|
+
project_ref: options.identity.project_ref,
|
|
166
|
+
actor: options.identity.actor,
|
|
167
|
+
target_visibility: options.identity.target_visibility,
|
|
168
|
+
operation_id: options.identity.operation_id,
|
|
169
|
+
plan_sha256: options.identity.plan_sha256,
|
|
170
|
+
freeze_sha256: options.identity.freeze_sha256,
|
|
171
|
+
policy_approval_text_sha256: options.identity.policy_approval_text_sha256,
|
|
172
|
+
execution_approval_request_sha256: options.identity.execution_approval_request_sha256,
|
|
173
|
+
execution_approval_text_sha256: options.identity.execution_approval_text_sha256,
|
|
174
|
+
execution_approval_identity_sha256: options.identity.execution_approval_identity_sha256,
|
|
175
|
+
toolchain_evidence_sha256: options.identity.toolchain_evidence_sha256,
|
|
176
|
+
user_state_claim: 'authenticated_actor_state_100_plus_own_state_0',
|
|
177
|
+
approval_reusable: false,
|
|
178
|
+
maximum_wrapper_invocations: 1,
|
|
179
|
+
maximum_cli_apply_spawns: 1,
|
|
180
|
+
maximum_process_posts: options.plan.processes.length,
|
|
181
|
+
maximum_finalize_posts: 1,
|
|
182
|
+
automatic_retry: false,
|
|
183
|
+
};
|
|
184
|
+
return request;
|
|
185
|
+
}
|
|
186
|
+
export function buildFlowIdentityScopeLookupRequest(options) {
|
|
187
|
+
return {
|
|
188
|
+
schema_version: 'dataset-flow-identity-scope-lookup.v1',
|
|
189
|
+
request_id: options.identity.request_id,
|
|
190
|
+
receipt_id: options.identity.receipt_id,
|
|
191
|
+
receipt_proof_sha256: options.identity.receipt_proof_sha256,
|
|
192
|
+
environment: options.identity.environment,
|
|
193
|
+
project_ref: options.identity.project_ref,
|
|
194
|
+
actor: options.identity.actor,
|
|
195
|
+
target_visibility: options.identity.target_visibility,
|
|
196
|
+
user_state_claim: 'authenticated_actor_state_100_plus_own_state_0',
|
|
197
|
+
operation_id: options.identity.operation_id,
|
|
198
|
+
plan_sha256: options.identity.plan_sha256,
|
|
199
|
+
freeze_sha256: options.identity.freeze_sha256,
|
|
200
|
+
policy_approval_text_sha256: options.identity.policy_approval_text_sha256,
|
|
201
|
+
execution_approval_request_sha256: options.identity.execution_approval_request_sha256,
|
|
202
|
+
execution_approval_text_sha256: options.identity.execution_approval_text_sha256,
|
|
203
|
+
execution_approval_identity_sha256: options.identity.execution_approval_identity_sha256,
|
|
204
|
+
toolchain_evidence_sha256: options.identity.toolchain_evidence_sha256,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function parseFlowIdentityExecutionPermit(value, options) {
|
|
208
|
+
if (!isJsonObject(value))
|
|
209
|
+
fail(`${options.label} execution permit is invalid.`);
|
|
210
|
+
assertExactKeys(value, ['schema_version', 'invocation_id', 'generation', 'token'], `${options.label} execution permit`);
|
|
211
|
+
if (value.schema_version !== 'dataset-flow-identity-execution-permit.v1' ||
|
|
212
|
+
integer(value.generation, 'execution permit generation') !== options.expectedGeneration ||
|
|
213
|
+
(options.expectedInvocationId !== undefined &&
|
|
214
|
+
value.invocation_id !== options.expectedInvocationId)) {
|
|
215
|
+
fail(`${options.label} execution permit does not bind the expected live wrapper generation.`);
|
|
216
|
+
}
|
|
217
|
+
uuid(value.invocation_id, 'execution permit invocation_id');
|
|
218
|
+
hash(value.token, 'execution permit token');
|
|
219
|
+
return value;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Removes the memory-only permit before a response enters a proof parser or
|
|
223
|
+
* durable artifact writer. Exact proof parsers therefore cannot accidentally
|
|
224
|
+
* accept or persist the bearer token.
|
|
225
|
+
*/
|
|
226
|
+
export function splitFlowIdentityPermitResponse(options) {
|
|
227
|
+
if (!isJsonObject(options.value))
|
|
228
|
+
fail(`${options.label} response is invalid.`);
|
|
229
|
+
if (!Object.hasOwn(options.value, 'execution_permit')) {
|
|
230
|
+
fail(`${options.label} response omitted the execution permit envelope.`);
|
|
231
|
+
}
|
|
232
|
+
const proof = { ...options.value };
|
|
233
|
+
const rawPermit = proof.execution_permit;
|
|
234
|
+
delete proof.execution_permit;
|
|
235
|
+
if (rawPermit === null) {
|
|
236
|
+
if (options.permitRequired) {
|
|
237
|
+
fail(`${options.label} response did not provide a fresh write-capable permit.`);
|
|
238
|
+
}
|
|
239
|
+
return { proof, executionPermit: null };
|
|
240
|
+
}
|
|
241
|
+
if (options.permitForbidden === true) {
|
|
242
|
+
fail(`${options.label} replay unexpectedly contained a write-capable execution permit.`);
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
proof,
|
|
246
|
+
executionPermit: parseFlowIdentityExecutionPermit(rawPermit, {
|
|
247
|
+
expectedGeneration: options.expectedGeneration,
|
|
248
|
+
expectedInvocationId: options.expectedInvocationId,
|
|
249
|
+
label: options.label,
|
|
250
|
+
}),
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
export function computeFlowIdentityProcessRequestSha256(request) {
|
|
254
|
+
const body = { ...request };
|
|
255
|
+
delete body.process_request_sha256;
|
|
256
|
+
return flowIdentityRestrictedSha256(body);
|
|
257
|
+
}
|
|
258
|
+
export function buildFlowIdentityProcessRequest(options) {
|
|
259
|
+
const body = {
|
|
260
|
+
schema_version: 'dataset-flow-identity-process-rewrite.v2',
|
|
261
|
+
request_id: deterministicUuidFromSha256(sha256Text(`dataset-flow-identity-process.v2\u0000${options.scopeProofSha256}\u0000${options.ordinal}\u0000${options.processIntentProofSha256}`)),
|
|
262
|
+
scope_proof_sha256: hash(options.scopeProofSha256, 'scope_proof_sha256'),
|
|
263
|
+
ordinal: integer(options.ordinal, 'ordinal', 1),
|
|
264
|
+
process_intent_proof_sha256: hash(options.processIntentProofSha256, 'process_intent_proof_sha256'),
|
|
265
|
+
process_request_sha256: '',
|
|
266
|
+
};
|
|
267
|
+
body.process_request_sha256 = computeFlowIdentityProcessRequestSha256(body);
|
|
268
|
+
return body;
|
|
269
|
+
}
|
|
270
|
+
export function buildFlowIdentityFinalizeRequest(options) {
|
|
271
|
+
const completed = options.status.processes.filter((process) => process.status === 'completed');
|
|
272
|
+
if (completed.length !== options.plan.processes.length ||
|
|
273
|
+
completed.some((process) => !process.audit_id)) {
|
|
274
|
+
fail('Cannot finalize without an exact completed database process ledger.');
|
|
275
|
+
}
|
|
276
|
+
const expected = {
|
|
277
|
+
process_count: options.plan.processes.length,
|
|
278
|
+
rewrite_count: options.plan.summary.rewrites,
|
|
279
|
+
completed_process_count: completed.length,
|
|
280
|
+
};
|
|
281
|
+
return {
|
|
282
|
+
schema_version: 'dataset-flow-identity-scope-finalize.v2',
|
|
283
|
+
request_id: deterministicUuidFromSha256(sha256Text(`dataset-flow-identity-finalize.v2\u0000${options.scopeProofSha256}\u0000${options.status.whole_scope_proof_sha256}`)),
|
|
284
|
+
scope_proof_sha256: hash(options.scopeProofSha256, 'scope_proof_sha256'),
|
|
285
|
+
expected,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
function requireScopeCounts(value, plan) {
|
|
289
|
+
if (integer(value.process_count, 'process_count') !== plan.processes.length ||
|
|
290
|
+
integer(value.mapping_count, 'mapping_count') !== plan.mappings.length ||
|
|
291
|
+
integer(value.rewrite_count, 'rewrite_count') !== plan.summary.rewrites) {
|
|
292
|
+
fail('Flow identity scope proof counts do not match the plan.');
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
export function parseFlowIdentityScopePreflightProof(value, plan) {
|
|
296
|
+
if (!isJsonObject(value))
|
|
297
|
+
fail('Flow identity scope preflight proof is invalid.');
|
|
298
|
+
assertExactKeys(value, [
|
|
299
|
+
'ok',
|
|
300
|
+
'command',
|
|
301
|
+
'schema_version',
|
|
302
|
+
'receipt_id',
|
|
303
|
+
'receipt_proof_sha256',
|
|
304
|
+
'scope_id',
|
|
305
|
+
'operation_id',
|
|
306
|
+
'plan_sha256',
|
|
307
|
+
'scope_proof_sha256',
|
|
308
|
+
'status',
|
|
309
|
+
'process_count',
|
|
310
|
+
'mapping_count',
|
|
311
|
+
'mapping_guard_set_sha256',
|
|
312
|
+
'process_intent_set_sha256',
|
|
313
|
+
'support_snapshot_count',
|
|
314
|
+
'source_universe_count',
|
|
315
|
+
'rewrite_count',
|
|
316
|
+
'next_ordinal',
|
|
317
|
+
'audit_id',
|
|
318
|
+
'replay',
|
|
319
|
+
], 'scope preflight result');
|
|
320
|
+
requireScopeCounts(value, plan);
|
|
321
|
+
if (value.ok !== true ||
|
|
322
|
+
value.command !== 'cmd_dataset_flow_identity_scope_preflight_guarded' ||
|
|
323
|
+
value.schema_version !== 'dataset-flow-identity-scope-preflight-result.v2' ||
|
|
324
|
+
value.receipt_id !== plan.receipt_id ||
|
|
325
|
+
value.receipt_proof_sha256 !== plan.receipt_proof_sha256 ||
|
|
326
|
+
value.operation_id !== plan.operation_id ||
|
|
327
|
+
value.plan_sha256 !== plan.plan_sha256 ||
|
|
328
|
+
value.mapping_guard_set_sha256 !== plan.mapping_guard_set_sha256 ||
|
|
329
|
+
value.process_intent_set_sha256 !== plan.process_intent_set_sha256 ||
|
|
330
|
+
!['sealed', 'running', 'primary_complete', 'derivatives_pending', 'completed'].includes(String(value.status)) ||
|
|
331
|
+
typeof value.replay !== 'boolean') {
|
|
332
|
+
fail('Flow identity scope preflight proof does not bind the plan.');
|
|
333
|
+
}
|
|
334
|
+
uuid(value.scope_id, 'scope_id');
|
|
335
|
+
hash(value.scope_proof_sha256, 'scope_proof_sha256');
|
|
336
|
+
integer(value.support_snapshot_count, 'support_snapshot_count');
|
|
337
|
+
if (value.support_snapshot_count !== plan.support_snapshots.length ||
|
|
338
|
+
value.source_universe_count !== 305) {
|
|
339
|
+
fail('Flow identity scope support/source counts do not match the plan receipt.');
|
|
340
|
+
}
|
|
341
|
+
integer(value.next_ordinal, 'next_ordinal', 1);
|
|
342
|
+
token(value.audit_id, 'audit_id');
|
|
343
|
+
return value;
|
|
344
|
+
}
|
|
345
|
+
export function parseFlowIdentityScopeLookupProof(value, plan, identity) {
|
|
346
|
+
if (!isJsonObject(value))
|
|
347
|
+
fail('Flow identity scope lookup proof is invalid.');
|
|
348
|
+
assertExactKeys(value, [
|
|
349
|
+
'ok',
|
|
350
|
+
'command',
|
|
351
|
+
'schema_version',
|
|
352
|
+
'read_only',
|
|
353
|
+
'scope_id',
|
|
354
|
+
'receipt_id',
|
|
355
|
+
'receipt_proof_sha256',
|
|
356
|
+
'mapping_guard_set_sha256',
|
|
357
|
+
'process_intent_set_sha256',
|
|
358
|
+
'operation_id',
|
|
359
|
+
'plan_sha256',
|
|
360
|
+
'scope_proof_sha256',
|
|
361
|
+
'status',
|
|
362
|
+
'process_count',
|
|
363
|
+
'mapping_count',
|
|
364
|
+
'support_snapshot_count',
|
|
365
|
+
'source_universe_count',
|
|
366
|
+
'rewrite_count',
|
|
367
|
+
'next_ordinal',
|
|
368
|
+
'audit_id',
|
|
369
|
+
'whole_scope_proof_sha256',
|
|
370
|
+
'execution_permit',
|
|
371
|
+
], 'scope lookup result');
|
|
372
|
+
requireScopeCounts(value, plan);
|
|
373
|
+
if (value.ok !== true ||
|
|
374
|
+
value.command !== 'cmd_dataset_flow_identity_scope_lookup' ||
|
|
375
|
+
value.schema_version !== 'dataset-flow-identity-scope-lookup-result.v1' ||
|
|
376
|
+
value.read_only !== true ||
|
|
377
|
+
value.execution_permit !== null ||
|
|
378
|
+
value.receipt_id !== identity.receipt_id ||
|
|
379
|
+
value.receipt_proof_sha256 !== identity.receipt_proof_sha256 ||
|
|
380
|
+
value.mapping_guard_set_sha256 !== plan.mapping_guard_set_sha256 ||
|
|
381
|
+
value.process_intent_set_sha256 !== plan.process_intent_set_sha256 ||
|
|
382
|
+
value.operation_id !== identity.operation_id ||
|
|
383
|
+
value.plan_sha256 !== identity.plan_sha256 ||
|
|
384
|
+
!['sealed', 'running', 'primary_complete', 'derivatives_pending', 'completed'].includes(String(value.status)) ||
|
|
385
|
+
value.support_snapshot_count !== plan.support_snapshots.length ||
|
|
386
|
+
value.source_universe_count !== 305) {
|
|
387
|
+
fail('Flow identity scope lookup proof does not bind the immutable execution.');
|
|
388
|
+
}
|
|
389
|
+
uuid(value.scope_id, 'lookup scope_id');
|
|
390
|
+
hash(value.scope_proof_sha256, 'lookup scope_proof_sha256');
|
|
391
|
+
integer(value.next_ordinal, 'lookup next_ordinal', 1);
|
|
392
|
+
token(value.audit_id, 'lookup audit_id');
|
|
393
|
+
hash(value.whole_scope_proof_sha256, 'lookup whole_scope_proof_sha256');
|
|
394
|
+
return value;
|
|
395
|
+
}
|
|
396
|
+
export function parseFlowIdentityProcessProof(options) {
|
|
397
|
+
if (!isJsonObject(options.value))
|
|
398
|
+
fail('Flow identity process proof is invalid.');
|
|
399
|
+
if (!Number.isSafeInteger(options.processCount) ||
|
|
400
|
+
options.processCount < options.process.ordinal) {
|
|
401
|
+
fail('Flow identity process count cannot bind the sealed ordinal.');
|
|
402
|
+
}
|
|
403
|
+
const value = options.value;
|
|
404
|
+
const completedProcessCount = value.completed_process_count;
|
|
405
|
+
assertExactKeys(value, [
|
|
406
|
+
'ok',
|
|
407
|
+
'command',
|
|
408
|
+
'schema_version',
|
|
409
|
+
'scope_id',
|
|
410
|
+
'receipt_id',
|
|
411
|
+
'receipt_proof_sha256',
|
|
412
|
+
'mapping_guard_set_sha256',
|
|
413
|
+
'process_intent_set_sha256',
|
|
414
|
+
'invocation_id',
|
|
415
|
+
'permit_generation_before',
|
|
416
|
+
'ordinal',
|
|
417
|
+
'process_id',
|
|
418
|
+
'process_version',
|
|
419
|
+
'process_request_sha256',
|
|
420
|
+
'process_intent_proof_sha256',
|
|
421
|
+
'desired_payload_sha256',
|
|
422
|
+
'desired_exchange_set_sha256',
|
|
423
|
+
'completed_process_count',
|
|
424
|
+
'next_ordinal',
|
|
425
|
+
'primary_complete',
|
|
426
|
+
'before_payload_sha256',
|
|
427
|
+
'before_exchange_set_sha256',
|
|
428
|
+
'after_payload_sha256',
|
|
429
|
+
'after_exchange_set_sha256',
|
|
430
|
+
'rewrite_count',
|
|
431
|
+
'audit_id',
|
|
432
|
+
'derivative_batch_id',
|
|
433
|
+
'status',
|
|
434
|
+
'replay',
|
|
435
|
+
], 'process rewrite result');
|
|
436
|
+
if (value.ok !== true ||
|
|
437
|
+
value.command !== 'cmd_dataset_flow_identity_process_rewrite_guarded' ||
|
|
438
|
+
value.schema_version !== 'dataset-flow-identity-process-rewrite-result.v2' ||
|
|
439
|
+
value.scope_id !== options.scopeId ||
|
|
440
|
+
value.receipt_id !== options.receiptId ||
|
|
441
|
+
value.receipt_proof_sha256 !== options.receiptProofSha256 ||
|
|
442
|
+
value.mapping_guard_set_sha256 !== options.mappingGuardSetSha256 ||
|
|
443
|
+
value.process_intent_set_sha256 !== options.processIntentSetSha256 ||
|
|
444
|
+
!UUID_PATTERN.test(String(value.invocation_id)) ||
|
|
445
|
+
!Number.isSafeInteger(value.permit_generation_before) ||
|
|
446
|
+
Number(value.permit_generation_before) < 0 ||
|
|
447
|
+
(options.expectedInvocationId !== undefined &&
|
|
448
|
+
value.invocation_id !== options.expectedInvocationId) ||
|
|
449
|
+
(options.expectedPermitGenerationBefore !== undefined &&
|
|
450
|
+
value.permit_generation_before !== options.expectedPermitGenerationBefore) ||
|
|
451
|
+
value.ordinal !== options.process.ordinal ||
|
|
452
|
+
value.process_id !== options.process.id ||
|
|
453
|
+
value.process_version !== options.process.version ||
|
|
454
|
+
value.process_request_sha256 !== options.requestSha256 ||
|
|
455
|
+
value.process_intent_proof_sha256 !== options.processIntentProofSha256 ||
|
|
456
|
+
value.desired_payload_sha256 !== options.process.desired_payload_sha256 ||
|
|
457
|
+
value.desired_exchange_set_sha256 !== options.process.desired_exchange_set_sha256 ||
|
|
458
|
+
!Number.isSafeInteger(completedProcessCount) ||
|
|
459
|
+
Number(completedProcessCount) < options.process.ordinal ||
|
|
460
|
+
Number(completedProcessCount) > options.processCount ||
|
|
461
|
+
(value.replay === false && completedProcessCount !== options.process.ordinal) ||
|
|
462
|
+
typeof value.primary_complete !== 'boolean' ||
|
|
463
|
+
(value.primary_complete
|
|
464
|
+
? completedProcessCount !== options.processCount || value.next_ordinal !== null
|
|
465
|
+
: value.next_ordinal !== Number(completedProcessCount) + 1 ||
|
|
466
|
+
Number(value.next_ordinal) > options.processCount) ||
|
|
467
|
+
!HASH_PATTERN.test(String(value.before_payload_sha256)) ||
|
|
468
|
+
!HASH_PATTERN.test(String(value.before_exchange_set_sha256)) ||
|
|
469
|
+
!HASH_PATTERN.test(String(value.after_payload_sha256)) ||
|
|
470
|
+
!HASH_PATTERN.test(String(value.after_exchange_set_sha256)) ||
|
|
471
|
+
value.after_payload_sha256 !== value.desired_payload_sha256 ||
|
|
472
|
+
value.after_exchange_set_sha256 !== value.desired_exchange_set_sha256 ||
|
|
473
|
+
value.rewrite_count !== options.process.rewrite_count ||
|
|
474
|
+
value.status !== 'completed' ||
|
|
475
|
+
!UUID_PATTERN.test(String(value.derivative_batch_id)) ||
|
|
476
|
+
typeof value.replay !== 'boolean') {
|
|
477
|
+
fail('Flow identity process proof does not bind the sealed process template.');
|
|
478
|
+
}
|
|
479
|
+
token(value.audit_id, 'audit_id');
|
|
480
|
+
return value;
|
|
481
|
+
}
|
|
482
|
+
function parseScopeProcess(value, expected) {
|
|
483
|
+
if (!isJsonObject(value))
|
|
484
|
+
fail('Flow identity scope process ledger entry is invalid.');
|
|
485
|
+
assertExactKeys(value, [
|
|
486
|
+
'ordinal',
|
|
487
|
+
'id',
|
|
488
|
+
'version',
|
|
489
|
+
'status',
|
|
490
|
+
'process_request_sha256',
|
|
491
|
+
'process_intent_proof_sha256',
|
|
492
|
+
'desired_payload_sha256',
|
|
493
|
+
'desired_exchange_set_sha256',
|
|
494
|
+
'rewrite_count',
|
|
495
|
+
'audit_id',
|
|
496
|
+
'before_payload_sha256',
|
|
497
|
+
'before_exchange_set_sha256',
|
|
498
|
+
'after_payload_sha256',
|
|
499
|
+
'after_exchange_set_sha256',
|
|
500
|
+
'derivative_batch_id',
|
|
501
|
+
'derivative_request_id',
|
|
502
|
+
'derivative_status',
|
|
503
|
+
'causal_terminal_proof',
|
|
504
|
+
'completed_at',
|
|
505
|
+
'last_error',
|
|
506
|
+
], 'scope process ledger entry');
|
|
507
|
+
if (value.ordinal !== expected.ordinal ||
|
|
508
|
+
value.id !== expected.id ||
|
|
509
|
+
value.version !== expected.version ||
|
|
510
|
+
value.rewrite_count !== expected.rewrite_count ||
|
|
511
|
+
value.desired_payload_sha256 !== expected.desired_payload_sha256 ||
|
|
512
|
+
value.desired_exchange_set_sha256 !== expected.desired_exchange_set_sha256 ||
|
|
513
|
+
!HASH_PATTERN.test(String(value.before_payload_sha256)) ||
|
|
514
|
+
!HASH_PATTERN.test(String(value.before_exchange_set_sha256)) ||
|
|
515
|
+
!HASH_PATTERN.test(String(value.process_intent_proof_sha256)) ||
|
|
516
|
+
!['pending', 'completed', 'failed'].includes(String(value.status))) {
|
|
517
|
+
fail('Flow identity scope process ledger does not match the sealed manifest.');
|
|
518
|
+
}
|
|
519
|
+
const completed = value.status === 'completed';
|
|
520
|
+
const pending = value.status === 'pending';
|
|
521
|
+
const missingOriginalDerivative = value.derivative_request_id === null && value.derivative_status === 'missing';
|
|
522
|
+
const presentOriginalDerivative = UUID_PATTERN.test(String(value.derivative_request_id)) &&
|
|
523
|
+
typeof value.derivative_status === 'string' &&
|
|
524
|
+
Boolean(value.derivative_status.trim()) &&
|
|
525
|
+
value.derivative_status !== 'missing';
|
|
526
|
+
if (completed &&
|
|
527
|
+
(!HASH_PATTERN.test(String(value.process_request_sha256)) ||
|
|
528
|
+
!token(value.audit_id, 'audit_id') ||
|
|
529
|
+
!HASH_PATTERN.test(String(value.after_payload_sha256)) ||
|
|
530
|
+
!HASH_PATTERN.test(String(value.after_exchange_set_sha256)) ||
|
|
531
|
+
value.after_payload_sha256 !== expected.desired_payload_sha256 ||
|
|
532
|
+
value.after_exchange_set_sha256 !== expected.desired_exchange_set_sha256 ||
|
|
533
|
+
!UUID_PATTERN.test(String(value.derivative_batch_id)) ||
|
|
534
|
+
(!missingOriginalDerivative && !presentOriginalDerivative) ||
|
|
535
|
+
!value.completed_at ||
|
|
536
|
+
!Number.isFinite(Date.parse(String(value.completed_at))))) {
|
|
537
|
+
fail('Completed flow identity process ledger proof is incomplete.');
|
|
538
|
+
}
|
|
539
|
+
if (pending &&
|
|
540
|
+
(value.process_request_sha256 !== null ||
|
|
541
|
+
value.audit_id !== null ||
|
|
542
|
+
value.after_payload_sha256 !== null ||
|
|
543
|
+
value.after_exchange_set_sha256 !== null ||
|
|
544
|
+
value.derivative_batch_id !== null ||
|
|
545
|
+
value.derivative_request_id !== null ||
|
|
546
|
+
value.derivative_status !== null ||
|
|
547
|
+
value.completed_at !== null)) {
|
|
548
|
+
fail('Pending flow identity process ledger unexpectedly contains completion proof.');
|
|
549
|
+
}
|
|
550
|
+
if (value.causal_terminal_proof !== false) {
|
|
551
|
+
fail('Scope status must not substitute a child status bit for terminal causal proof.');
|
|
552
|
+
}
|
|
553
|
+
return value;
|
|
554
|
+
}
|
|
555
|
+
function parseCompensationTarget(entry, plan, scopeId, source) {
|
|
556
|
+
if (!isJsonObject(entry))
|
|
557
|
+
fail('Derivative compensation target is invalid.');
|
|
558
|
+
const ordinal = integer(entry.ordinal, 'compensation ordinal', 1);
|
|
559
|
+
const process = plan.processes[ordinal - 1];
|
|
560
|
+
const reason = `FLOW_IDENTITY_SCOPE_COMPENSATION:${scopeId}:${ordinal}`;
|
|
561
|
+
if (!process ||
|
|
562
|
+
entry.table !== 'processes' ||
|
|
563
|
+
entry.id !== process.id ||
|
|
564
|
+
entry.version !== process.version ||
|
|
565
|
+
!UUID_PATTERN.test(String(entry.original_batch_id)) ||
|
|
566
|
+
!['failed', 'stale', 'missing'].includes(String(entry.original_status)) ||
|
|
567
|
+
typeof entry.original_code !== 'string' ||
|
|
568
|
+
!entry.original_code.trim() ||
|
|
569
|
+
!HASH_PATTERN.test(String(entry.desired_payload_sha256)) ||
|
|
570
|
+
!HASH_PATTERN.test(String(entry.current_json_ordered_sha256)) ||
|
|
571
|
+
!HASH_PATTERN.test(String(entry.current_snapshot_sha256)) ||
|
|
572
|
+
!Number.isFinite(Date.parse(String(entry.current_modified_at))) ||
|
|
573
|
+
!Array.isArray(entry.components) ||
|
|
574
|
+
entry.components.length !== 2 ||
|
|
575
|
+
entry.components[0] !== 'extracted_md' ||
|
|
576
|
+
entry.components[1] !== 'embedding_ft' ||
|
|
577
|
+
entry.reason_code !== reason ||
|
|
578
|
+
entry.operation_id_prefix !== `${reason}:` ||
|
|
579
|
+
entry.requires_new_plan_freeze_approval !== true ||
|
|
580
|
+
entry.automatic_retry !== false ||
|
|
581
|
+
(entry.latest_compensation_request_id !== null &&
|
|
582
|
+
!UUID_PATTERN.test(String(entry.latest_compensation_request_id))) ||
|
|
583
|
+
(entry.latest_compensation_status !== null &&
|
|
584
|
+
(typeof entry.latest_compensation_status !== 'string' ||
|
|
585
|
+
!entry.latest_compensation_status.trim())) ||
|
|
586
|
+
(entry.latest_compensation_plan_sha256 !== null &&
|
|
587
|
+
!HASH_PATTERN.test(String(entry.latest_compensation_plan_sha256)))) {
|
|
588
|
+
fail('Derivative compensation target does not bind the sealed process/current snapshot.');
|
|
589
|
+
}
|
|
590
|
+
const missingOriginalRequest = entry.original_status === 'missing' &&
|
|
591
|
+
entry.original_code === 'DERIVATIVE_BATCH_CHILD_MISSING' &&
|
|
592
|
+
entry.original_request_id === null;
|
|
593
|
+
const hasOriginalRequestId = Object.prototype.hasOwnProperty.call(entry, 'original_request_id');
|
|
594
|
+
const hasOriginalError = Object.prototype.hasOwnProperty.call(entry, 'original_error');
|
|
595
|
+
if ((source === 'scope_read' &&
|
|
596
|
+
(!hasOriginalRequestId ||
|
|
597
|
+
!hasOriginalError ||
|
|
598
|
+
(!missingOriginalRequest &&
|
|
599
|
+
(!['failed', 'stale'].includes(String(entry.original_status)) ||
|
|
600
|
+
!UUID_PATTERN.test(String(entry.original_request_id)))))) ||
|
|
601
|
+
(source !== 'scope_read' && (hasOriginalRequestId || hasOriginalError)) ||
|
|
602
|
+
(entry.original_status === 'missing' &&
|
|
603
|
+
entry.original_code !== 'DERIVATIVE_BATCH_CHILD_MISSING') ||
|
|
604
|
+
(entry.original_status !== 'missing' && entry.original_request_id === null)) {
|
|
605
|
+
fail('Derivative compensation target provenance does not match its RPC response.');
|
|
606
|
+
}
|
|
607
|
+
return entry;
|
|
608
|
+
}
|
|
609
|
+
function parseCompensationEnvelope(value, plan, scopeId, source) {
|
|
610
|
+
if (value.compensation_required === undefined)
|
|
611
|
+
return [];
|
|
612
|
+
if (typeof value.compensation_required !== 'boolean') {
|
|
613
|
+
fail('Flow identity compensation_required must be boolean.');
|
|
614
|
+
}
|
|
615
|
+
if (value.compensation_required === false) {
|
|
616
|
+
if (value.automatic_retry !== false ||
|
|
617
|
+
(value.compensation_targets !== undefined &&
|
|
618
|
+
(!Array.isArray(value.compensation_targets) || value.compensation_targets.length !== 0))) {
|
|
619
|
+
fail('Non-required compensation envelope is malformed.');
|
|
620
|
+
}
|
|
621
|
+
return [];
|
|
622
|
+
}
|
|
623
|
+
if ((source === 'scope_read'
|
|
624
|
+
? !['derivatives_pending', 'completed'].includes(String(value.status))
|
|
625
|
+
: value.status !== 'derivatives_pending') ||
|
|
626
|
+
value.code !== 'FLOW_IDENTITY_DERIVATIVE_COMPENSATION_REQUIRED' ||
|
|
627
|
+
value.automatic_retry !== false ||
|
|
628
|
+
!Array.isArray(value.compensation_targets) ||
|
|
629
|
+
value.compensation_targets.length === 0) {
|
|
630
|
+
fail('Required derivative compensation envelope is malformed.');
|
|
631
|
+
}
|
|
632
|
+
return value.compensation_targets.map((entry) => parseCompensationTarget(entry, plan, scopeId, source));
|
|
633
|
+
}
|
|
634
|
+
function assertExactKeys(value, expected, label) {
|
|
635
|
+
const actual = Object.keys(value).sort();
|
|
636
|
+
const wanted = [...expected].sort();
|
|
637
|
+
if (actual.length !== wanted.length || actual.some((key, index) => key !== wanted[index])) {
|
|
638
|
+
fail(`${label} keys do not match the database contract.`);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
function parseDerivativeResidue(value, label) {
|
|
642
|
+
if (!isJsonObject(value))
|
|
643
|
+
fail(`${label} must be an object.`);
|
|
644
|
+
assertExactKeys(value, ['http_requests', 'embedding_jobs', 'pending_jobs', 'failure_rows', 'other_active_fences'], label);
|
|
645
|
+
return {
|
|
646
|
+
http_requests: integer(value.http_requests, `${label}.http_requests`),
|
|
647
|
+
embedding_jobs: integer(value.embedding_jobs, `${label}.embedding_jobs`),
|
|
648
|
+
pending_jobs: integer(value.pending_jobs, `${label}.pending_jobs`),
|
|
649
|
+
failure_rows: integer(value.failure_rows, `${label}.failure_rows`),
|
|
650
|
+
other_active_fences: integer(value.other_active_fences, `${label}.other_active_fences`),
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
function parseDerivativeTarget(options) {
|
|
654
|
+
const label = `derivative_set_proof.targets[${options.index}]`;
|
|
655
|
+
if (!isJsonObject(options.value))
|
|
656
|
+
fail(`${label} must be an object.`);
|
|
657
|
+
const value = options.value;
|
|
658
|
+
assertExactKeys(value, [
|
|
659
|
+
'ordinal',
|
|
660
|
+
'id',
|
|
661
|
+
'version',
|
|
662
|
+
'original_batch_id',
|
|
663
|
+
'effective_reference_id',
|
|
664
|
+
'effective_reference_kind',
|
|
665
|
+
'status',
|
|
666
|
+
'request_status',
|
|
667
|
+
'phase',
|
|
668
|
+
'lineage_ok',
|
|
669
|
+
'proposals_committed',
|
|
670
|
+
'terminal_audit_present',
|
|
671
|
+
'residue',
|
|
672
|
+
'current_snapshot_sha256',
|
|
673
|
+
'current_json_ordered_sha256',
|
|
674
|
+
'causal_terminal_proof',
|
|
675
|
+
], label);
|
|
676
|
+
const ordinal = integer(value.ordinal, `${label}.ordinal`, 1);
|
|
677
|
+
const expected = options.plan.processes[ordinal - 1];
|
|
678
|
+
const requestStatus = token(value.request_status, `${label}.request_status`);
|
|
679
|
+
const requestIsTerminal = ['completed', 'stale', 'failed', 'missing'].includes(requestStatus);
|
|
680
|
+
const causalTerminalProof = boolean(value.causal_terminal_proof, `${label}.causal_terminal_proof`);
|
|
681
|
+
const expectedStatus = causalTerminalProof
|
|
682
|
+
? 'completed'
|
|
683
|
+
: requestIsTerminal
|
|
684
|
+
? 'failed'
|
|
685
|
+
: 'pending';
|
|
686
|
+
const effectiveReferenceKind = token(value.effective_reference_kind, `${label}.effective_reference_kind`);
|
|
687
|
+
const phase = token(value.phase, `${label}.phase`);
|
|
688
|
+
const missingOriginalDerivative = options.process.derivative_request_id === null &&
|
|
689
|
+
options.process.derivative_status === 'missing' &&
|
|
690
|
+
value.effective_reference_id === null &&
|
|
691
|
+
effectiveReferenceKind === 'protected_batch' &&
|
|
692
|
+
requestStatus === 'missing' &&
|
|
693
|
+
phase === 'missing' &&
|
|
694
|
+
value.status === 'failed' &&
|
|
695
|
+
value.lineage_ok === false &&
|
|
696
|
+
value.proposals_committed === false &&
|
|
697
|
+
value.terminal_audit_present === false &&
|
|
698
|
+
causalTerminalProof === false;
|
|
699
|
+
if (ordinal !== options.index + 1 ||
|
|
700
|
+
!expected ||
|
|
701
|
+
options.process.ordinal !== ordinal ||
|
|
702
|
+
options.process.status !== 'completed' ||
|
|
703
|
+
value.id !== expected.id ||
|
|
704
|
+
value.version !== expected.version ||
|
|
705
|
+
value.original_batch_id !== options.process.derivative_batch_id ||
|
|
706
|
+
!UUID_PATTERN.test(String(value.original_batch_id)) ||
|
|
707
|
+
(!missingOriginalDerivative && !UUID_PATTERN.test(String(value.effective_reference_id))) ||
|
|
708
|
+
!['protected_batch', 'separate_compensation'].includes(effectiveReferenceKind) ||
|
|
709
|
+
![
|
|
710
|
+
'queued',
|
|
711
|
+
'dispatching',
|
|
712
|
+
'markdown_pending',
|
|
713
|
+
'embedding_pending',
|
|
714
|
+
'completed',
|
|
715
|
+
'stale',
|
|
716
|
+
'failed',
|
|
717
|
+
'missing',
|
|
718
|
+
].includes(requestStatus) ||
|
|
719
|
+
((requestStatus === 'missing' ||
|
|
720
|
+
phase === 'missing' ||
|
|
721
|
+
value.effective_reference_id === null) &&
|
|
722
|
+
!missingOriginalDerivative) ||
|
|
723
|
+
value.status !== expectedStatus ||
|
|
724
|
+
(causalTerminalProof && requestStatus !== 'completed') ||
|
|
725
|
+
(effectiveReferenceKind === 'protected_batch' &&
|
|
726
|
+
value.effective_reference_id !== options.process.derivative_request_id) ||
|
|
727
|
+
(effectiveReferenceKind === 'separate_compensation' &&
|
|
728
|
+
value.effective_reference_id === options.process.derivative_request_id) ||
|
|
729
|
+
!HASH_PATTERN.test(String(value.current_json_ordered_sha256)) ||
|
|
730
|
+
!HASH_PATTERN.test(String(value.current_snapshot_sha256))) {
|
|
731
|
+
fail(`${label} does not bind the ordered process/derivative ledger.`);
|
|
732
|
+
}
|
|
733
|
+
return {
|
|
734
|
+
ordinal,
|
|
735
|
+
id: expected.id,
|
|
736
|
+
version: expected.version,
|
|
737
|
+
original_batch_id: String(value.original_batch_id),
|
|
738
|
+
effective_reference_id: value.effective_reference_id === null ? null : String(value.effective_reference_id),
|
|
739
|
+
effective_reference_kind: effectiveReferenceKind,
|
|
740
|
+
status: expectedStatus,
|
|
741
|
+
request_status: requestStatus,
|
|
742
|
+
phase,
|
|
743
|
+
lineage_ok: boolean(value.lineage_ok, `${label}.lineage_ok`),
|
|
744
|
+
proposals_committed: boolean(value.proposals_committed, `${label}.proposals_committed`),
|
|
745
|
+
terminal_audit_present: boolean(value.terminal_audit_present, `${label}.terminal_audit_present`),
|
|
746
|
+
residue: parseDerivativeResidue(value.residue, `${label}.residue`),
|
|
747
|
+
current_snapshot_sha256: String(value.current_snapshot_sha256),
|
|
748
|
+
current_json_ordered_sha256: String(value.current_json_ordered_sha256),
|
|
749
|
+
causal_terminal_proof: causalTerminalProof,
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
export function parseFlowIdentityDerivativeSetProof(options) {
|
|
753
|
+
if (!isJsonObject(options.value))
|
|
754
|
+
fail('Flow identity derivative set proof is invalid.');
|
|
755
|
+
const value = options.value;
|
|
756
|
+
assertExactKeys(value, [
|
|
757
|
+
'ok',
|
|
758
|
+
'schema_version',
|
|
759
|
+
'scope_id',
|
|
760
|
+
'status',
|
|
761
|
+
'target_count',
|
|
762
|
+
'completed_count',
|
|
763
|
+
'pending_count',
|
|
764
|
+
'failed_count',
|
|
765
|
+
'causal_terminal_proof',
|
|
766
|
+
'targets',
|
|
767
|
+
'compensation_targets',
|
|
768
|
+
'proof_sha256',
|
|
769
|
+
], 'derivative_set_proof');
|
|
770
|
+
if (!Array.isArray(value.targets) || !Array.isArray(value.compensation_targets)) {
|
|
771
|
+
fail('Flow identity derivative set proof arrays are invalid.');
|
|
772
|
+
}
|
|
773
|
+
const completedProcesses = options.processes.filter((entry) => entry.status === 'completed');
|
|
774
|
+
const targets = value.targets.map((entry, index) => {
|
|
775
|
+
const process = completedProcesses[index];
|
|
776
|
+
if (!process)
|
|
777
|
+
fail('Derivative set proof contains a foreign target.');
|
|
778
|
+
return parseDerivativeTarget({ value: entry, index, plan: options.plan, process });
|
|
779
|
+
});
|
|
780
|
+
const targetCount = integer(value.target_count, 'derivative_set_proof.target_count');
|
|
781
|
+
const completedCount = integer(value.completed_count, 'derivative_set_proof.completed_count');
|
|
782
|
+
const pendingCount = integer(value.pending_count, 'derivative_set_proof.pending_count');
|
|
783
|
+
const failedCount = integer(value.failed_count, 'derivative_set_proof.failed_count');
|
|
784
|
+
const causalTerminalProof = boolean(value.causal_terminal_proof, 'derivative_set_proof.causal_terminal_proof');
|
|
785
|
+
const expectedStatus = targetCount === 0
|
|
786
|
+
? 'failed'
|
|
787
|
+
: failedCount > 0
|
|
788
|
+
? 'compensation_required'
|
|
789
|
+
: pendingCount > 0
|
|
790
|
+
? 'pending'
|
|
791
|
+
: 'completed';
|
|
792
|
+
const compensationTargets = value.compensation_targets.map((entry) => parseCompensationTarget(entry, options.plan, options.scopeId, 'derivative_set'));
|
|
793
|
+
const failedOrdinals = targets
|
|
794
|
+
.filter((entry) => entry.status === 'failed')
|
|
795
|
+
.map((entry) => entry.ordinal);
|
|
796
|
+
if (value.schema_version !== 'dataset-flow-identity-derivative-set-proof.v1' ||
|
|
797
|
+
value.scope_id !== options.scopeId ||
|
|
798
|
+
targetCount !== targets.length ||
|
|
799
|
+
targetCount !== completedProcesses.length ||
|
|
800
|
+
completedCount !== targets.filter((entry) => entry.status === 'completed').length ||
|
|
801
|
+
pendingCount !== targets.filter((entry) => entry.status === 'pending').length ||
|
|
802
|
+
failedCount !== failedOrdinals.length ||
|
|
803
|
+
completedCount + pendingCount + failedCount !== targetCount ||
|
|
804
|
+
compensationTargets.length !== failedCount ||
|
|
805
|
+
compensationTargets.some((entry, index) => entry.ordinal !== failedOrdinals[index]) ||
|
|
806
|
+
compensationTargets.some((entry) => {
|
|
807
|
+
const process = options.processes[entry.ordinal - 1];
|
|
808
|
+
const target = targets[entry.ordinal - 1];
|
|
809
|
+
const processMissingOriginal = process?.derivative_request_id === null && process.derivative_status === 'missing';
|
|
810
|
+
return ((entry.original_status === 'missing') !== processMissingOriginal ||
|
|
811
|
+
(target?.request_status === 'missing' && entry.original_status !== 'missing'));
|
|
812
|
+
}) ||
|
|
813
|
+
value.status !== expectedStatus ||
|
|
814
|
+
value.ok !== (targetCount > 0 && failedCount === 0) ||
|
|
815
|
+
causalTerminalProof !== (targetCount > 0 && completedCount === targetCount) ||
|
|
816
|
+
!HASH_PATTERN.test(String(value.proof_sha256))) {
|
|
817
|
+
fail('Flow identity derivative set proof counts/status do not bind its ordered targets.');
|
|
818
|
+
}
|
|
819
|
+
return {
|
|
820
|
+
ok: Boolean(value.ok),
|
|
821
|
+
schema_version: 'dataset-flow-identity-derivative-set-proof.v1',
|
|
822
|
+
scope_id: options.scopeId,
|
|
823
|
+
status: expectedStatus,
|
|
824
|
+
target_count: targetCount,
|
|
825
|
+
completed_count: completedCount,
|
|
826
|
+
pending_count: pendingCount,
|
|
827
|
+
failed_count: failedCount,
|
|
828
|
+
causal_terminal_proof: causalTerminalProof,
|
|
829
|
+
targets,
|
|
830
|
+
compensation_targets: compensationTargets,
|
|
831
|
+
proof_sha256: String(value.proof_sha256),
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
export function parseFlowIdentityWholeScopeProof(options) {
|
|
835
|
+
if (!isJsonObject(options.value))
|
|
836
|
+
fail('Flow identity whole-scope proof is invalid.');
|
|
837
|
+
const value = options.value;
|
|
838
|
+
assertExactKeys(value, [
|
|
839
|
+
'schema_version',
|
|
840
|
+
'scope_id',
|
|
841
|
+
'receipt_id',
|
|
842
|
+
'primary_current',
|
|
843
|
+
'audit_current',
|
|
844
|
+
'source_guards_current',
|
|
845
|
+
'support_guards_current',
|
|
846
|
+
'target_guards_current',
|
|
847
|
+
'approved_reference_residue_count',
|
|
848
|
+
'protected_closure_current',
|
|
849
|
+
'occurrence_closure_current',
|
|
850
|
+
'derivatives_current',
|
|
851
|
+
'primary_closure_sha256',
|
|
852
|
+
'source_guard_set_sha256',
|
|
853
|
+
'support_guard_set_sha256',
|
|
854
|
+
'target_guard_set_sha256',
|
|
855
|
+
'protected_closure_sha256',
|
|
856
|
+
'derivative_proof_set_sha256',
|
|
857
|
+
'causal_terminal_proof',
|
|
858
|
+
'proof_sha256',
|
|
859
|
+
], 'whole_scope_proof');
|
|
860
|
+
const proof = {
|
|
861
|
+
schema_version: 'dataset-flow-identity-whole-scope-proof.v2',
|
|
862
|
+
scope_id: uuid(value.scope_id, 'whole_scope_proof.scope_id'),
|
|
863
|
+
receipt_id: uuid(value.receipt_id, 'whole_scope_proof.receipt_id'),
|
|
864
|
+
primary_current: boolean(value.primary_current, 'whole_scope_proof.primary_current'),
|
|
865
|
+
audit_current: boolean(value.audit_current, 'whole_scope_proof.audit_current'),
|
|
866
|
+
source_guards_current: boolean(value.source_guards_current, 'whole_scope_proof.source_guards_current'),
|
|
867
|
+
support_guards_current: boolean(value.support_guards_current, 'whole_scope_proof.support_guards_current'),
|
|
868
|
+
target_guards_current: boolean(value.target_guards_current, 'whole_scope_proof.target_guards_current'),
|
|
869
|
+
approved_reference_residue_count: integer(value.approved_reference_residue_count, 'whole_scope_proof.approved_reference_residue_count'),
|
|
870
|
+
protected_closure_current: boolean(value.protected_closure_current, 'whole_scope_proof.protected_closure_current'),
|
|
871
|
+
occurrence_closure_current: boolean(value.occurrence_closure_current, 'whole_scope_proof.occurrence_closure_current'),
|
|
872
|
+
derivatives_current: boolean(value.derivatives_current, 'whole_scope_proof.derivatives_current'),
|
|
873
|
+
primary_closure_sha256: hash(value.primary_closure_sha256, 'whole_scope_proof.primary_closure_sha256'),
|
|
874
|
+
source_guard_set_sha256: hash(value.source_guard_set_sha256, 'whole_scope_proof.source_guard_set_sha256'),
|
|
875
|
+
support_guard_set_sha256: hash(value.support_guard_set_sha256, 'whole_scope_proof.support_guard_set_sha256'),
|
|
876
|
+
target_guard_set_sha256: hash(value.target_guard_set_sha256, 'whole_scope_proof.target_guard_set_sha256'),
|
|
877
|
+
protected_closure_sha256: hash(value.protected_closure_sha256, 'whole_scope_proof.protected_closure_sha256'),
|
|
878
|
+
derivative_proof_set_sha256: hash(value.derivative_proof_set_sha256, 'whole_scope_proof.derivative_proof_set_sha256'),
|
|
879
|
+
causal_terminal_proof: boolean(value.causal_terminal_proof, 'whole_scope_proof.causal_terminal_proof'),
|
|
880
|
+
proof_sha256: hash(value.proof_sha256, 'whole_scope_proof.proof_sha256'),
|
|
881
|
+
};
|
|
882
|
+
if (value.schema_version !== proof.schema_version ||
|
|
883
|
+
proof.scope_id !== options.scopeId ||
|
|
884
|
+
proof.receipt_id !== options.receiptId) {
|
|
885
|
+
fail('Flow identity whole-scope proof does not bind the actor scope receipt.');
|
|
886
|
+
}
|
|
887
|
+
return proof;
|
|
888
|
+
}
|
|
889
|
+
export function parseFlowIdentityScopeStatus(value, plan, scopeId, scopeProofSha256) {
|
|
890
|
+
if (!isJsonObject(value) || !Array.isArray(value.processes)) {
|
|
891
|
+
fail('Flow identity scope status is invalid.');
|
|
892
|
+
}
|
|
893
|
+
const scopeStatusKeys = [
|
|
894
|
+
'ok',
|
|
895
|
+
'command',
|
|
896
|
+
'schema_version',
|
|
897
|
+
'scope_id',
|
|
898
|
+
'receipt_id',
|
|
899
|
+
'receipt_proof_sha256',
|
|
900
|
+
'mapping_guard_set_sha256',
|
|
901
|
+
'process_intent_set_sha256',
|
|
902
|
+
'operation_id',
|
|
903
|
+
'plan_sha256',
|
|
904
|
+
'scope_proof_sha256',
|
|
905
|
+
'status',
|
|
906
|
+
'process_count',
|
|
907
|
+
'completed_process_count',
|
|
908
|
+
'pending_process_count',
|
|
909
|
+
'failed_process_count',
|
|
910
|
+
'next_ordinal',
|
|
911
|
+
'rewrite_count',
|
|
912
|
+
'completed_rewrite_count',
|
|
913
|
+
'primary_complete',
|
|
914
|
+
'cancellable',
|
|
915
|
+
'strict_continuation_required',
|
|
916
|
+
'primary_current',
|
|
917
|
+
'live_guard_current',
|
|
918
|
+
'derivatives_current',
|
|
919
|
+
'derivative_pending_count',
|
|
920
|
+
'derivative_failed_count',
|
|
921
|
+
'derivative_set_proof',
|
|
922
|
+
'derivative_proof_set_sha256',
|
|
923
|
+
'compensation_required',
|
|
924
|
+
'automatic_retry',
|
|
925
|
+
'compensation_targets',
|
|
926
|
+
'protected_closure_current',
|
|
927
|
+
'protected_closure_proof',
|
|
928
|
+
'processes',
|
|
929
|
+
'terminal_proof_sha256',
|
|
930
|
+
'completed_at',
|
|
931
|
+
'whole_scope_proof',
|
|
932
|
+
'whole_scope_proof_sha256',
|
|
933
|
+
...(value.code === undefined ? [] : ['code']),
|
|
934
|
+
];
|
|
935
|
+
assertExactKeys(value, scopeStatusKeys, 'scope status result');
|
|
936
|
+
const processes = value.processes.map((entry, index) => {
|
|
937
|
+
const expected = plan.processes[index];
|
|
938
|
+
if (!expected)
|
|
939
|
+
fail('Scope status contains a foreign process ledger entry.');
|
|
940
|
+
return parseScopeProcess(entry, expected);
|
|
941
|
+
});
|
|
942
|
+
const derivativeSetProof = parseFlowIdentityDerivativeSetProof({
|
|
943
|
+
value: value.derivative_set_proof,
|
|
944
|
+
plan,
|
|
945
|
+
scopeId,
|
|
946
|
+
processes,
|
|
947
|
+
});
|
|
948
|
+
const wholeScopeProof = parseFlowIdentityWholeScopeProof({
|
|
949
|
+
value: value.whole_scope_proof,
|
|
950
|
+
scopeId,
|
|
951
|
+
receiptId: plan.receipt_id,
|
|
952
|
+
});
|
|
953
|
+
const status = String(value.status);
|
|
954
|
+
const liveDrift = status === 'live_drift';
|
|
955
|
+
if (integer(value.process_count, 'process_count') !== plan.processes.length ||
|
|
956
|
+
integer(value.rewrite_count, 'rewrite_count') !== plan.summary.rewrites ||
|
|
957
|
+
integer(value.pending_process_count, 'pending_process_count') !==
|
|
958
|
+
processes.filter((entry) => entry.status === 'pending').length ||
|
|
959
|
+
integer(value.failed_process_count, 'failed_process_count') !==
|
|
960
|
+
processes.filter((entry) => entry.status === 'failed').length ||
|
|
961
|
+
integer(value.completed_rewrite_count, 'completed_rewrite_count') !==
|
|
962
|
+
processes
|
|
963
|
+
.filter((entry) => entry.status === 'completed')
|
|
964
|
+
.reduce((sum, entry) => sum + entry.rewrite_count, 0)) {
|
|
965
|
+
fail('Flow identity scope status counts do not match the plan.');
|
|
966
|
+
}
|
|
967
|
+
if (value.ok !==
|
|
968
|
+
(!['failed', 'live_drift'].includes(status) && value.compensation_required !== true) ||
|
|
969
|
+
value.command !== 'cmd_dataset_flow_identity_scope_read' ||
|
|
970
|
+
value.schema_version !== 'dataset-flow-identity-scope-status.v2' ||
|
|
971
|
+
value.scope_id !== scopeId ||
|
|
972
|
+
value.receipt_id !== plan.receipt_id ||
|
|
973
|
+
value.receipt_proof_sha256 !== plan.receipt_proof_sha256 ||
|
|
974
|
+
value.mapping_guard_set_sha256 !== plan.mapping_guard_set_sha256 ||
|
|
975
|
+
value.process_intent_set_sha256 !== plan.process_intent_set_sha256 ||
|
|
976
|
+
value.operation_id !== plan.operation_id ||
|
|
977
|
+
value.plan_sha256 !== plan.plan_sha256 ||
|
|
978
|
+
value.scope_proof_sha256 !== scopeProofSha256 ||
|
|
979
|
+
![
|
|
980
|
+
'sealed',
|
|
981
|
+
'running',
|
|
982
|
+
'primary_complete',
|
|
983
|
+
'derivatives_pending',
|
|
984
|
+
'completed',
|
|
985
|
+
'live_drift',
|
|
986
|
+
'failed',
|
|
987
|
+
].includes(String(value.status)) ||
|
|
988
|
+
processes.length !== plan.processes.length ||
|
|
989
|
+
integer(value.completed_process_count, 'completed_process_count') !==
|
|
990
|
+
processes.filter((entry) => entry.status === 'completed').length ||
|
|
991
|
+
integer(value.next_ordinal, 'next_ordinal', 1) !==
|
|
992
|
+
Math.min(processes.find((entry) => entry.status === 'pending')?.ordinal ?? processes.length + 1, processes.length + 1) ||
|
|
993
|
+
typeof value.primary_complete !== 'boolean' ||
|
|
994
|
+
value.primary_complete !== processes.every((entry) => entry.status === 'completed') ||
|
|
995
|
+
typeof value.cancellable !== 'boolean' ||
|
|
996
|
+
value.cancellable !==
|
|
997
|
+
(processes.every((entry) => entry.status === 'pending') &&
|
|
998
|
+
!['completed', 'failed', 'live_drift'].includes(status)) ||
|
|
999
|
+
typeof value.strict_continuation_required !== 'boolean' ||
|
|
1000
|
+
value.strict_continuation_required !==
|
|
1001
|
+
(processes.some((entry) => entry.status === 'completed') &&
|
|
1002
|
+
processes.some((entry) => entry.status === 'pending')) ||
|
|
1003
|
+
typeof value.primary_current !== 'boolean' ||
|
|
1004
|
+
value.primary_current !== wholeScopeProof.primary_current ||
|
|
1005
|
+
typeof value.live_guard_current !== 'boolean' ||
|
|
1006
|
+
value.live_guard_current !==
|
|
1007
|
+
(wholeScopeProof.audit_current &&
|
|
1008
|
+
wholeScopeProof.source_guards_current &&
|
|
1009
|
+
wholeScopeProof.support_guards_current &&
|
|
1010
|
+
wholeScopeProof.target_guards_current &&
|
|
1011
|
+
wholeScopeProof.protected_closure_current &&
|
|
1012
|
+
wholeScopeProof.occurrence_closure_current) ||
|
|
1013
|
+
typeof value.derivatives_current !== 'boolean' ||
|
|
1014
|
+
(!liveDrift && value.derivatives_current !== derivativeSetProof.causal_terminal_proof) ||
|
|
1015
|
+
value.derivatives_current !== wholeScopeProof.derivatives_current ||
|
|
1016
|
+
integer(value.derivative_pending_count, 'derivative_pending_count') !==
|
|
1017
|
+
derivativeSetProof.pending_count ||
|
|
1018
|
+
integer(value.derivative_failed_count, 'derivative_failed_count') !==
|
|
1019
|
+
derivativeSetProof.failed_count ||
|
|
1020
|
+
value.derivative_proof_set_sha256 !== derivativeSetProof.proof_sha256 ||
|
|
1021
|
+
wholeScopeProof.derivative_proof_set_sha256 !== derivativeSetProof.proof_sha256 ||
|
|
1022
|
+
typeof value.protected_closure_current !== 'boolean' ||
|
|
1023
|
+
value.protected_closure_current !== wholeScopeProof.protected_closure_current ||
|
|
1024
|
+
!isJsonObject(value.protected_closure_proof) ||
|
|
1025
|
+
value.whole_scope_proof_sha256 !== wholeScopeProof.proof_sha256 ||
|
|
1026
|
+
value.automatic_retry !== false ||
|
|
1027
|
+
typeof value.compensation_required !== 'boolean' ||
|
|
1028
|
+
(!liveDrift && value.compensation_required !== derivativeSetProof.failed_count > 0) ||
|
|
1029
|
+
!Array.isArray(value.compensation_targets)) {
|
|
1030
|
+
fail('Flow identity scope status does not match the sealed plan/progress ledger.');
|
|
1031
|
+
}
|
|
1032
|
+
if ((status === 'completed' &&
|
|
1033
|
+
(!wholeScopeProof.primary_current ||
|
|
1034
|
+
!wholeScopeProof.audit_current ||
|
|
1035
|
+
!wholeScopeProof.source_guards_current ||
|
|
1036
|
+
!wholeScopeProof.support_guards_current ||
|
|
1037
|
+
!wholeScopeProof.target_guards_current ||
|
|
1038
|
+
wholeScopeProof.approved_reference_residue_count !== 0 ||
|
|
1039
|
+
!wholeScopeProof.protected_closure_current ||
|
|
1040
|
+
!wholeScopeProof.occurrence_closure_current ||
|
|
1041
|
+
!wholeScopeProof.derivatives_current ||
|
|
1042
|
+
!wholeScopeProof.causal_terminal_proof)) ||
|
|
1043
|
+
(liveDrift &&
|
|
1044
|
+
(value.ok !== false ||
|
|
1045
|
+
!['FLOW_IDENTITY_PRIMARY_OR_GUARD_DRIFT', 'FLOW_IDENTITY_SCOPE_TERMINAL_CONFLICT'].includes(String(value.code)) ||
|
|
1046
|
+
(value.primary_current === true && value.live_guard_current === true) ||
|
|
1047
|
+
value.derivatives_current !== false ||
|
|
1048
|
+
value.compensation_required !== false ||
|
|
1049
|
+
!Array.isArray(value.compensation_targets) ||
|
|
1050
|
+
value.compensation_targets.length !== 0))) {
|
|
1051
|
+
fail('Flow identity completed/live-drift status contradicts the dynamic whole-scope proof.');
|
|
1052
|
+
}
|
|
1053
|
+
if (value.status === 'completed'
|
|
1054
|
+
? !HASH_PATTERN.test(String(value.terminal_proof_sha256))
|
|
1055
|
+
: value.terminal_proof_sha256 !== null) {
|
|
1056
|
+
fail('Flow identity scope terminal proof does not match its status.');
|
|
1057
|
+
}
|
|
1058
|
+
if (status === 'completed'
|
|
1059
|
+
? !Number.isFinite(Date.parse(String(value.completed_at)))
|
|
1060
|
+
: value.completed_at !== null) {
|
|
1061
|
+
fail('Flow identity scope completion timestamp does not match its status.');
|
|
1062
|
+
}
|
|
1063
|
+
const compensationTargets = liveDrift
|
|
1064
|
+
? []
|
|
1065
|
+
: parseCompensationEnvelope(value, plan, scopeId, 'scope_read');
|
|
1066
|
+
if (!liveDrift &&
|
|
1067
|
+
(compensationTargets.length !== derivativeSetProof.compensation_targets.length ||
|
|
1068
|
+
compensationTargets.some((entry, index) => {
|
|
1069
|
+
const derivativeEntry = derivativeSetProof.compensation_targets[index];
|
|
1070
|
+
return (!derivativeEntry ||
|
|
1071
|
+
entry.ordinal !== derivativeEntry.ordinal ||
|
|
1072
|
+
entry.id !== derivativeEntry.id ||
|
|
1073
|
+
entry.version !== derivativeEntry.version ||
|
|
1074
|
+
entry.original_batch_id !== derivativeEntry.original_batch_id ||
|
|
1075
|
+
entry.original_status !== derivativeEntry.original_status ||
|
|
1076
|
+
entry.original_code !== derivativeEntry.original_code ||
|
|
1077
|
+
entry.desired_payload_sha256 !== derivativeEntry.desired_payload_sha256 ||
|
|
1078
|
+
entry.current_json_ordered_sha256 !== derivativeEntry.current_json_ordered_sha256 ||
|
|
1079
|
+
entry.current_snapshot_sha256 !== derivativeEntry.current_snapshot_sha256 ||
|
|
1080
|
+
entry.latest_compensation_request_id !== derivativeEntry.latest_compensation_request_id ||
|
|
1081
|
+
entry.latest_compensation_status !== derivativeEntry.latest_compensation_status ||
|
|
1082
|
+
entry.latest_compensation_plan_sha256 !== derivativeEntry.latest_compensation_plan_sha256);
|
|
1083
|
+
}))) {
|
|
1084
|
+
fail('Scope compensation convenience fields do not match the dynamic derivative proof.');
|
|
1085
|
+
}
|
|
1086
|
+
return {
|
|
1087
|
+
...value,
|
|
1088
|
+
processes,
|
|
1089
|
+
derivative_set_proof: derivativeSetProof,
|
|
1090
|
+
whole_scope_proof: wholeScopeProof,
|
|
1091
|
+
compensation_targets: compensationTargets,
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
export function flowIdentityScopeHasCurrentDerivativeClosure(status) {
|
|
1095
|
+
return Boolean(status.status === 'completed' &&
|
|
1096
|
+
typeof status.terminal_proof_sha256 === 'string' &&
|
|
1097
|
+
HASH_PATTERN.test(status.terminal_proof_sha256) &&
|
|
1098
|
+
flowIdentityScopeIsReadyToFinalize(status));
|
|
1099
|
+
}
|
|
1100
|
+
export function flowIdentityScopeIsReadyToFinalize(status) {
|
|
1101
|
+
const proof = status.derivative_set_proof;
|
|
1102
|
+
const whole = status.whole_scope_proof;
|
|
1103
|
+
return Boolean(['primary_complete', 'completed'].includes(status.status) &&
|
|
1104
|
+
status.primary_complete &&
|
|
1105
|
+
status.primary_current &&
|
|
1106
|
+
status.live_guard_current &&
|
|
1107
|
+
status.derivatives_current &&
|
|
1108
|
+
status.protected_closure_current &&
|
|
1109
|
+
status.derivative_pending_count === 0 &&
|
|
1110
|
+
status.derivative_failed_count === 0 &&
|
|
1111
|
+
status.compensation_required === false &&
|
|
1112
|
+
status.compensation_targets?.length === 0 &&
|
|
1113
|
+
proof.ok &&
|
|
1114
|
+
proof.status === 'completed' &&
|
|
1115
|
+
proof.target_count === status.process_count &&
|
|
1116
|
+
proof.completed_count === status.process_count &&
|
|
1117
|
+
proof.pending_count === 0 &&
|
|
1118
|
+
proof.failed_count === 0 &&
|
|
1119
|
+
proof.causal_terminal_proof &&
|
|
1120
|
+
proof.targets.length === status.process_count &&
|
|
1121
|
+
proof.compensation_targets.length === 0 &&
|
|
1122
|
+
HASH_PATTERN.test(proof.proof_sha256) &&
|
|
1123
|
+
whole.primary_current &&
|
|
1124
|
+
whole.audit_current &&
|
|
1125
|
+
whole.source_guards_current &&
|
|
1126
|
+
whole.support_guards_current &&
|
|
1127
|
+
whole.target_guards_current &&
|
|
1128
|
+
whole.approved_reference_residue_count === 0 &&
|
|
1129
|
+
whole.protected_closure_current &&
|
|
1130
|
+
whole.occurrence_closure_current &&
|
|
1131
|
+
whole.derivatives_current &&
|
|
1132
|
+
whole.causal_terminal_proof &&
|
|
1133
|
+
whole.proof_sha256 === status.whole_scope_proof_sha256 &&
|
|
1134
|
+
proof.targets.every((target, index) => {
|
|
1135
|
+
const process = status.processes[index];
|
|
1136
|
+
return (process?.status === 'completed' &&
|
|
1137
|
+
target.ordinal === index + 1 &&
|
|
1138
|
+
target.current_json_ordered_sha256 === process.desired_payload_sha256 &&
|
|
1139
|
+
target.status === 'completed' &&
|
|
1140
|
+
target.request_status === 'completed' &&
|
|
1141
|
+
target.phase === 'completed' &&
|
|
1142
|
+
target.lineage_ok &&
|
|
1143
|
+
target.proposals_committed &&
|
|
1144
|
+
target.terminal_audit_present &&
|
|
1145
|
+
HASH_PATTERN.test(target.current_snapshot_sha256) &&
|
|
1146
|
+
Object.values(target.residue).every((count) => count === 0) &&
|
|
1147
|
+
target.causal_terminal_proof);
|
|
1148
|
+
}));
|
|
1149
|
+
}
|
|
1150
|
+
export function parseFlowIdentityFinalizeProof(options) {
|
|
1151
|
+
if (!isJsonObject(options.value) || !isJsonObject(options.request.expected)) {
|
|
1152
|
+
fail('Flow identity finalize proof is invalid.');
|
|
1153
|
+
}
|
|
1154
|
+
const value = options.value;
|
|
1155
|
+
const expected = options.request.expected;
|
|
1156
|
+
const completed = value.status === 'completed';
|
|
1157
|
+
const failed = value.status === 'failed';
|
|
1158
|
+
const liveDrift = value.status === 'live_drift';
|
|
1159
|
+
const compensationRequired = value.compensation_required === true;
|
|
1160
|
+
const derivativesPending = value.status === 'derivatives_pending';
|
|
1161
|
+
const wholeScopeProof = parseFlowIdentityWholeScopeProof({
|
|
1162
|
+
value: value.whole_scope_proof,
|
|
1163
|
+
scopeId: options.scopeId,
|
|
1164
|
+
receiptId: options.plan.receipt_id,
|
|
1165
|
+
});
|
|
1166
|
+
const finalizeKeys = [
|
|
1167
|
+
'ok',
|
|
1168
|
+
'command',
|
|
1169
|
+
'schema_version',
|
|
1170
|
+
'scope_id',
|
|
1171
|
+
'receipt_id',
|
|
1172
|
+
'receipt_proof_sha256',
|
|
1173
|
+
'mapping_guard_set_sha256',
|
|
1174
|
+
'process_intent_set_sha256',
|
|
1175
|
+
'operation_id',
|
|
1176
|
+
'plan_sha256',
|
|
1177
|
+
'scope_proof_sha256',
|
|
1178
|
+
'invocation_id',
|
|
1179
|
+
'permit_generation_before',
|
|
1180
|
+
'status',
|
|
1181
|
+
'process_count',
|
|
1182
|
+
'completed_process_count',
|
|
1183
|
+
'rewrite_count',
|
|
1184
|
+
'primary_closure_sha256',
|
|
1185
|
+
'protected_closure_sha256',
|
|
1186
|
+
'derivative_target_set_sha256',
|
|
1187
|
+
'derivative_proof_set_sha256',
|
|
1188
|
+
'primary_current',
|
|
1189
|
+
'live_guard_current',
|
|
1190
|
+
'derivatives_current',
|
|
1191
|
+
'terminal_proof_sha256',
|
|
1192
|
+
'whole_scope_proof',
|
|
1193
|
+
'whole_scope_proof_sha256',
|
|
1194
|
+
'audit_id',
|
|
1195
|
+
'replay',
|
|
1196
|
+
...(completed
|
|
1197
|
+
? []
|
|
1198
|
+
: ['code', 'compensation_required', 'automatic_retry', 'compensation_targets']),
|
|
1199
|
+
];
|
|
1200
|
+
assertExactKeys(value, finalizeKeys, `${String(value.status)} finalize result`);
|
|
1201
|
+
if (value.ok !== (!failed && !liveDrift && !compensationRequired) ||
|
|
1202
|
+
value.command !== 'cmd_dataset_flow_identity_scope_finalize_guarded' ||
|
|
1203
|
+
value.schema_version !== 'dataset-flow-identity-scope-finalize-result.v2' ||
|
|
1204
|
+
value.scope_id !== options.scopeId ||
|
|
1205
|
+
value.receipt_id !== options.plan.receipt_id ||
|
|
1206
|
+
value.receipt_proof_sha256 !== options.plan.receipt_proof_sha256 ||
|
|
1207
|
+
value.mapping_guard_set_sha256 !== options.plan.mapping_guard_set_sha256 ||
|
|
1208
|
+
value.process_intent_set_sha256 !== options.plan.process_intent_set_sha256 ||
|
|
1209
|
+
value.operation_id !== options.plan.operation_id ||
|
|
1210
|
+
value.plan_sha256 !== options.plan.plan_sha256 ||
|
|
1211
|
+
value.scope_proof_sha256 !== options.scopeProofSha256 ||
|
|
1212
|
+
!UUID_PATTERN.test(String(value.invocation_id)) ||
|
|
1213
|
+
!Number.isSafeInteger(value.permit_generation_before) ||
|
|
1214
|
+
Number(value.permit_generation_before) < 0 ||
|
|
1215
|
+
(options.expectedInvocationId !== undefined &&
|
|
1216
|
+
value.invocation_id !== options.expectedInvocationId) ||
|
|
1217
|
+
(options.expectedPermitGenerationBefore !== undefined &&
|
|
1218
|
+
value.permit_generation_before !== options.expectedPermitGenerationBefore) ||
|
|
1219
|
+
!['derivatives_pending', 'completed', 'live_drift', 'failed'].includes(String(value.status)) ||
|
|
1220
|
+
value.process_count !== expected.process_count ||
|
|
1221
|
+
value.rewrite_count !== expected.rewrite_count ||
|
|
1222
|
+
value.completed_process_count !== expected.completed_process_count ||
|
|
1223
|
+
!HASH_PATTERN.test(String(value.primary_closure_sha256)) ||
|
|
1224
|
+
!HASH_PATTERN.test(String(value.protected_closure_sha256)) ||
|
|
1225
|
+
!HASH_PATTERN.test(String(value.derivative_target_set_sha256)) ||
|
|
1226
|
+
!HASH_PATTERN.test(String(value.derivative_proof_set_sha256)) ||
|
|
1227
|
+
value.primary_closure_sha256 !== wholeScopeProof.primary_closure_sha256 ||
|
|
1228
|
+
value.protected_closure_sha256 !== wholeScopeProof.protected_closure_sha256 ||
|
|
1229
|
+
value.derivative_proof_set_sha256 !== wholeScopeProof.derivative_proof_set_sha256 ||
|
|
1230
|
+
typeof value.primary_current !== 'boolean' ||
|
|
1231
|
+
value.primary_current !== wholeScopeProof.primary_current ||
|
|
1232
|
+
typeof value.live_guard_current !== 'boolean' ||
|
|
1233
|
+
value.live_guard_current !==
|
|
1234
|
+
(wholeScopeProof.audit_current &&
|
|
1235
|
+
wholeScopeProof.source_guards_current &&
|
|
1236
|
+
wholeScopeProof.support_guards_current &&
|
|
1237
|
+
wholeScopeProof.target_guards_current &&
|
|
1238
|
+
wholeScopeProof.protected_closure_current &&
|
|
1239
|
+
wholeScopeProof.occurrence_closure_current) ||
|
|
1240
|
+
typeof value.derivatives_current !== 'boolean' ||
|
|
1241
|
+
value.derivatives_current !== wholeScopeProof.derivatives_current ||
|
|
1242
|
+
value.whole_scope_proof_sha256 !== wholeScopeProof.proof_sha256 ||
|
|
1243
|
+
(derivativesPending &&
|
|
1244
|
+
(typeof value.compensation_required !== 'boolean' ||
|
|
1245
|
+
!Array.isArray(value.compensation_targets) ||
|
|
1246
|
+
value.automatic_retry !== false ||
|
|
1247
|
+
![
|
|
1248
|
+
'FLOW_IDENTITY_DERIVATIVES_PENDING',
|
|
1249
|
+
'FLOW_IDENTITY_DERIVATIVE_COMPENSATION_REQUIRED',
|
|
1250
|
+
].includes(String(value.code)))) ||
|
|
1251
|
+
(completed
|
|
1252
|
+
? !HASH_PATTERN.test(String(value.terminal_proof_sha256))
|
|
1253
|
+
: value.terminal_proof_sha256 !== null) ||
|
|
1254
|
+
(completed
|
|
1255
|
+
? typeof value.audit_id !== 'string' || !value.audit_id.trim()
|
|
1256
|
+
: value.audit_id !== null &&
|
|
1257
|
+
(typeof value.audit_id !== 'string' || !value.audit_id.trim())) ||
|
|
1258
|
+
typeof value.replay !== 'boolean') {
|
|
1259
|
+
fail('Flow identity finalize proof does not match the exact expected closure.');
|
|
1260
|
+
}
|
|
1261
|
+
if (completed &&
|
|
1262
|
+
(!value.primary_current ||
|
|
1263
|
+
!value.live_guard_current ||
|
|
1264
|
+
!value.derivatives_current ||
|
|
1265
|
+
!wholeScopeProof.audit_current ||
|
|
1266
|
+
wholeScopeProof.approved_reference_residue_count !== 0 ||
|
|
1267
|
+
!wholeScopeProof.causal_terminal_proof)) {
|
|
1268
|
+
fail('Completed finalize replay is not dynamically current across the whole scope.');
|
|
1269
|
+
}
|
|
1270
|
+
if (liveDrift &&
|
|
1271
|
+
(value.ok !== false ||
|
|
1272
|
+
value.code !== 'FLOW_IDENTITY_PRIMARY_OR_GUARD_DRIFT' ||
|
|
1273
|
+
(value.primary_current === true && value.live_guard_current === true) ||
|
|
1274
|
+
value.derivatives_current !== false ||
|
|
1275
|
+
value.compensation_required !== false ||
|
|
1276
|
+
!Array.isArray(value.compensation_targets) ||
|
|
1277
|
+
value.compensation_targets.length !== 0)) {
|
|
1278
|
+
fail('Live-drift finalize response must be a non-compensable dynamic downgrade.');
|
|
1279
|
+
}
|
|
1280
|
+
if (failed &&
|
|
1281
|
+
(value.code !== 'FLOW_IDENTITY_FINALIZE_FAILED' ||
|
|
1282
|
+
value.compensation_required !== false ||
|
|
1283
|
+
value.automatic_retry !== false ||
|
|
1284
|
+
!Array.isArray(value.compensation_targets) ||
|
|
1285
|
+
value.compensation_targets.length !== 0)) {
|
|
1286
|
+
fail('Failed finalize response must be the exact non-compensable v2 envelope.');
|
|
1287
|
+
}
|
|
1288
|
+
parseCompensationEnvelope(value, options.plan, options.scopeId, 'finalize');
|
|
1289
|
+
return {
|
|
1290
|
+
...value,
|
|
1291
|
+
whole_scope_proof: wholeScopeProof,
|
|
1292
|
+
};
|
|
1293
|
+
}
|
|
1294
|
+
export function prepareFlowIdentityExecution(options) {
|
|
1295
|
+
const plan = parseFlowIdentityPlan(options.plan);
|
|
1296
|
+
const freeze = parseFlowIdentityFreeze(options.freeze, plan);
|
|
1297
|
+
const approval = parseFlowIdentityApproval(options.approval, plan, freeze);
|
|
1298
|
+
const identity = buildFlowIdentityExecutionIdentity({ plan, freeze, approval });
|
|
1299
|
+
return {
|
|
1300
|
+
plan,
|
|
1301
|
+
freeze,
|
|
1302
|
+
approval,
|
|
1303
|
+
identity,
|
|
1304
|
+
preflightRequest: buildFlowIdentityScopePreflightRequest({ plan, identity }),
|
|
1305
|
+
};
|
|
1306
|
+
}
|
|
1307
|
+
export const __testInternals = {
|
|
1308
|
+
deterministicUuidFromSha256,
|
|
1309
|
+
hash,
|
|
1310
|
+
instant,
|
|
1311
|
+
integer,
|
|
1312
|
+
parseScopeProcess,
|
|
1313
|
+
parseCompensationEnvelope,
|
|
1314
|
+
token,
|
|
1315
|
+
uuid,
|
|
1316
|
+
};
|
|
1317
|
+
//# sourceMappingURL=dataset-maintenance-flow-identity-execution-contract.js.map
|