omc-core 0.4.35 → 0.4.37

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.
@@ -1,901 +0,0 @@
1
- import { createHash, randomUUID } from "node:crypto";
2
- import { canonicalCloudPivotBaselineFingerprint, canonicalCloudPivotEnvironmentFingerprint } from "./cloudpivot-fingerprints.js";
3
- import { canonicalFormalDiscoveryFingerprint } from "./formal-capability-discovery.js";
4
- import { CLOUDPIVOT_8_6_26_SERIALIZED_MODEL_CREATE } from "./cloudpivot-low-code.js";
5
- import { buildListModelPayload, buildNativeConfigurationDryRun, buildPhysicalCleanupOperation, compileNativeOperations, runNativeConfigurationPackage } from "./cloudpivot-native-configuration.js";
6
- import { buildKnowledgePack } from "./knowledge-pack-builder.js";
7
- import { KnowledgePackRuntime } from "./knowledge-pack-runtime.js";
8
- import { createDeliveryPlanApproval, deliveryPlanApprovalHash, proposeDeliveryPlan } from "./two-stage-change-package.js";
9
- import { validateConfigurationIntent } from "./configuration-intent.js";
10
- import { SENSITIVE_SECRET_VALUE } from "./delivery-graph.js";
11
- import { appendApprovalHumanBlock } from "./approval-frame-readability.js";
12
- /**
13
- * Ticket 08 (MVP series) promotes exactly one independently bounded CloudPivot
14
- * `test` configuration mutation: a unique test-only LIST model create with
15
- * authoritative readback, real behavior validation, and separately approved
16
- * owned cleanup. Evidence from this capability is never inferred for any other
17
- * capability; production, staging, tenant administration, Redis, restart,
18
- * deployment, and release stay rejected.
19
- */
20
- export const TICKET08_TEST_MUTATION_ROUTE = "ticket08-test-model-mutation";
21
- export const TICKET08_TEST_MODEL_CLEANUP_ROUTE = "ticket08-test-model-cleanup";
22
- export const TICKET08_TEST_MODEL_CREATE_CAPABILITY_ID = "cloudpivot.test.model-create-8.6.26";
23
- export const TICKET08_TEST_MODEL_CLEANUP_CAPABILITY_ID = "cloudpivot.test.model-cleanup-8.6.26";
24
- export const TICKET08_TEST_MUTATION_KNOWLEDGE_PACK_ID = "cloudpivot-8.6-ticket08-test-model-mutation";
25
- export const TICKET08_TEST_ENVIRONMENT_NAME = "test";
26
- export const TICKET08_TEST_APPLICATION_CODE = "test";
27
- export const TICKET08_REFERENCE_EVIDENCE_VERSION = "8.6.26";
28
- export const TICKET08_MODEL_CREATE_ENDPOINT = "/api/api/app/bizmodels/create";
29
- export const TICKET08_MODEL_READBACK_ENDPOINT = "/api/api/app/bizmodels/get_all";
30
- export const TICKET08_MODEL_CLEANUP_ENDPOINT = "/api/api/app/apppackage/physically_delete";
31
- export const TICKET08_PROHIBITED_MUTATION_TERMS = Object.freeze([
32
- "publish", "deploy", "restart", "release", "redis", "tenant-admin", "runtime-crud",
33
- "field-create", "form-create", "view-create", "folder-create", "folder-delete",
34
- ]);
35
- const TICKET08_UUID_V4 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
36
- const SHA256 = /^[a-f0-9]{64}$/i;
37
- /** The named `test` environment and its dedicated test application are hard preconditions. */
38
- export function assertTicket08TestMutationEnvironment(settings) {
39
- if (settings.environmentName !== "test")
40
- throw new Error("Ticket 08 test mutation is restricted to the named test environment; production and staging are rejected.");
41
- if (!settings.baseUrl.trim() || !/^https?:\/\/\S+$/i.test(settings.baseUrl.trim()))
42
- throw new Error("Ticket 08 test mutation requires an absolute configured test environment endpoint.");
43
- if (settings.testApplicationCode !== "test")
44
- throw new Error("Ticket 08 test mutation requires the dedicated test application to be test.");
45
- }
46
- /**
47
- * CloudPivot 8.6.26 enforces bizSchemaCode `^[A-Za-z][A-Za-z0-9_]*$` with a
48
- * 26-character maximum (live authoritative errcode 10005 evidence). The
49
- * host-owned fixture code is therefore `omc_t08_` + 16 lowercase hex digits
50
- * (24 characters, unique per run; exact-absence preflight still guards
51
- * collisions).
52
- */
53
- export const TICKET08_TARGET_SCHEMA_PATTERN = /^omc_t08_[0-9a-f]{16}$/;
54
- const TICKET08_SCHEMA_CODE_PREFIX = "omc_t08_";
55
- export function ticket08TargetSchemaCode() {
56
- return `${TICKET08_SCHEMA_CODE_PREFIX}${randomUUID().replaceAll("-", "").slice(0, 16)}`;
57
- }
58
- export function isTicket08TargetSchema(targetSchemaCode) {
59
- return TICKET08_TARGET_SCHEMA_PATTERN.test(targetSchemaCode);
60
- }
61
- export function assertTicket08TargetSchema(targetSchemaCode) {
62
- if (!isTicket08TargetSchema(targetSchemaCode))
63
- throw new Error("Ticket 08 targetSchemaCode must be a host-scoped omc_t08_<16-hex> schema within the CloudPivot 8.6.26 26-character code contract.");
64
- return targetSchemaCode;
65
- }
66
- export function ticket08DeliveryId(testRunId) { return `ticket08-${testRunId}`; }
67
- export function ticket08CleanupDeliveryId(testRunId) { return `ticket08-cleanup-${testRunId}`; }
68
- export function ticket08ModelScope(targetSchemaCode) { return `cloudpivot:model:${targetSchemaCode}`; }
69
- export function ticket08ResourceName(targetSchemaCode) { return assertTicket08TargetSchema(targetSchemaCode); }
70
- export function buildTicket08CleanupContract(testRunId, targetSchemaCode) {
71
- if (!TICKET08_UUID_V4.test(testRunId) || !isTicket08TargetSchema(targetSchemaCode))
72
- throw new Error("Ticket 08 cleanup contract requires the exact host-owned testRunId and server-legal target schema binding.");
73
- return Object.freeze({
74
- capabilityId: TICKET08_TEST_MODEL_CLEANUP_CAPABILITY_ID,
75
- testRunId,
76
- targetSchemaCode,
77
- operation: { method: "POST", endpoint: TICKET08_MODEL_CLEANUP_ENDPOINT, payload: "AppDeleteParamVO[1]" },
78
- dependencyCheck: { endpoint: "/api/api/app/bizmodels/exits_bizschema", expected: "errTypes-empty" },
79
- taskHistory: { endpoint: "/api/api/app/apppackage/delete/history", expected: "taskStatus-END" },
80
- postDeleteAbsenceReadback: { method: "GET", endpoint: TICKET08_MODEL_READBACK_ENDPOINT, expected: "exact-absence" },
81
- ownership: "model-id-folder-and-schema-prefix-bound-to-testRunId",
82
- replayPolicy: "absent-is-no-op",
83
- uncertainPolicy: "stop-retain-report-no-retry",
84
- });
85
- }
86
- /** Deterministic, run-independent contract identities pinned by the Knowledge Pack promotion. */
87
- export function ticket08ModelCreateContractHash() { return digest(ticket08ModelCreateContract()); }
88
- export function ticket08CleanupContractHash() { return digest(ticket08CleanupContractTemplate()); }
89
- function ticket08ModelCreateContract() {
90
- return {
91
- capabilityId: TICKET08_TEST_MODEL_CREATE_CAPABILITY_ID,
92
- operationKind: "model-create",
93
- method: "POST",
94
- endpoint: CLOUDPIVOT_8_6_26_SERIALIZED_MODEL_CREATE.endpoint,
95
- readbackEndpoint: CLOUDPIVOT_8_6_26_SERIALIZED_MODEL_CREATE.readbackEndpoint,
96
- preflight: CLOUDPIVOT_8_6_26_SERIALIZED_MODEL_CREATE.preflight,
97
- modelType: CLOUDPIVOT_8_6_26_SERIALIZED_MODEL_CREATE.modelType,
98
- transportMode: "serialized-server-model-create-8.6.26",
99
- schemaCodeContract: { pattern: "^[A-Za-z][A-Za-z0-9_]*$", maxLength: 26, evidence: "live-authoritative-errcode-10005" },
100
- capability: CLOUDPIVOT_8_6_26_SERIALIZED_MODEL_CREATE,
101
- evidence: [...CLOUDPIVOT_8_6_26_SERIALIZED_MODEL_CREATE.evidence],
102
- requiredPermissions: ["configuration-write"],
103
- prohibited: [...TICKET08_PROHIBITED_MUTATION_TERMS],
104
- };
105
- }
106
- function ticket08CleanupContractTemplate() {
107
- return {
108
- capabilityId: TICKET08_TEST_MODEL_CLEANUP_CAPABILITY_ID,
109
- operationKind: "owned-model-physical-delete",
110
- method: "POST",
111
- endpoint: TICKET08_MODEL_CLEANUP_ENDPOINT,
112
- dependencyCheck: "/api/api/app/bizmodels/exits_bizschema:errTypes-empty",
113
- taskHistory: "/api/api/app/apppackage/delete/history:taskStatus-END",
114
- postDeleteAbsenceReadback: `${TICKET08_MODEL_READBACK_ENDPOINT}:exact-absence`,
115
- ownership: "model-id-folder-and-schema-prefix-bound-to-testRunId",
116
- replayPolicy: "absent-is-no-op",
117
- uncertainPolicy: "stop-retain-report-no-retry",
118
- };
119
- }
120
- /** Package operations must be exactly one bounded create (or cleanup) step for the bound target. */
121
- export function assertTicket08OperationsBounded(input) {
122
- if (input.operations.length !== 1)
123
- throw new Error("Ticket 08 packages must contain exactly one bounded operation.");
124
- const operation = input.operations[0];
125
- const identity = `${operation.id} ${operation.target} ${operation.path}`.toLowerCase();
126
- const expectedPrefix = input.route === TICKET08_TEST_MUTATION_ROUTE ? "model:" : "cleanup:model:";
127
- if (TICKET08_PROHIBITED_MUTATION_TERMS.some((term) => identity.includes(term)) || !operation.id.startsWith(expectedPrefix))
128
- throw new Error(`Ticket 08 operation ${operation.id} is outside the bounded operation set; prohibited families (publish, deploy, restart, release, redis, tenant administration, runtime CRUD, field/form/view writes) remain rejected.`);
129
- const expected = input.route === TICKET08_TEST_MUTATION_ROUTE
130
- ? { id: `model:${input.targetSchemaCode}`, path: TICKET08_MODEL_CREATE_ENDPOINT, transportMode: "serialized-server-model-create-8.6.26" }
131
- : { id: `cleanup:model:${input.targetSchemaCode}`, path: TICKET08_MODEL_CLEANUP_ENDPOINT, transportMode: "cloudpivot-model-physical-delete-8.6.26" };
132
- if (operation.id !== expected.id || operation.path !== expected.path || operation.transportMode !== expected.transportMode || operation.target !== `model:${input.targetSchemaCode}`)
133
- throw new Error(`Ticket 08 operation ${operation.id} does not match the exact target binding for ${input.targetSchemaCode}.`);
134
- }
135
- /** Builds the signed, version-bound pack promoting exactly the single test model-create capability. */
136
- export function buildTicket08TestMutationKnowledgePack(input) {
137
- const compiler = { id: "src/ticket08-test-model-mutation.ts:createTicket08TestMutationPlanApproval", sha256: digest("omc-ticket08-compiler-v1") };
138
- const decoder = { id: "src/dsh-host-plugin.ts:createCloudPivotWriteTransport", sha256: digest("omc-ticket08-decoder-v1") };
139
- return buildKnowledgePack({
140
- packId: TICKET08_TEST_MUTATION_KNOWLEDGE_PACK_ID,
141
- packVersion: "1.0.0",
142
- sourceVersion: TICKET08_REFERENCE_EVIDENCE_VERSION,
143
- sourceArtifacts: [{ id: "cloudpivot-8.6.26-serialized-model-create-capability", sha256: digest(CLOUDPIVOT_8_6_26_SERIALIZED_MODEL_CREATE) }],
144
- references: [
145
- { id: "ticket02-live-test-model-create-readback-cleanup-evidence", sha256: digest("ticket02-live-acceptance") },
146
- { id: TICKET08_MODEL_CREATE_ENDPOINT, sha256: digest(TICKET08_MODEL_CREATE_ENDPOINT) },
147
- { id: TICKET08_MODEL_READBACK_ENDPOINT, sha256: digest(TICKET08_MODEL_READBACK_ENDPOINT) },
148
- { id: TICKET08_MODEL_CLEANUP_ENDPOINT, sha256: digest(TICKET08_MODEL_CLEANUP_ENDPOINT) },
149
- ],
150
- compilers: [compiler],
151
- decoders: [decoder],
152
- capabilities: [{
153
- id: TICKET08_TEST_MODEL_CREATE_CAPABILITY_ID,
154
- status: "runtime-verified",
155
- promote: true,
156
- compilerRef: compiler.id,
157
- decoderRef: decoder.id,
158
- sourceEvidence: [...CLOUDPIVOT_8_6_26_SERIALIZED_MODEL_CREATE.evidence, TICKET08_MODEL_CREATE_ENDPOINT, TICKET08_MODEL_READBACK_ENDPOINT, "AppIntegrationFacadeImpl.java:1273-1302"],
159
- runtimeEvidence: ["ticket02-live-test-model-create-readback-physical-delete-cycle", "cloudpivot.model-create.serialized-server-8.6.26"],
160
- operationPromotions: [{
161
- id: "model-create",
162
- status: "promoted",
163
- operationContractHash: ticket08ModelCreateContractHash(),
164
- sourceRefs: [...CLOUDPIVOT_8_6_26_SERIALIZED_MODEL_CREATE.evidence, TICKET08_MODEL_CREATE_ENDPOINT, TICKET08_MODEL_READBACK_ENDPOINT, TICKET08_MODEL_CLEANUP_ENDPOINT],
165
- versionFamily: "8.6",
166
- referenceVersion: TICKET08_REFERENCE_EVIDENCE_VERSION,
167
- requiredEvidence: ["cleanup", "discovery", "permission", "readback", "test", "write"],
168
- evidence: ["cleanup", "discovery", "permission", "readback", "test", "write"],
169
- blockedReasons: [],
170
- }],
171
- }],
172
- signerKeyId: input.signerKeyId,
173
- signingKey: input.signingKey,
174
- createdAt: input.createdAt,
175
- });
176
- }
177
- /** Loads the pack against CloudPivot 8.6.26 and pins the single executable capability and contract hash. */
178
- export function loadTicket08KnowledgePack(artifact, trustedKeys) {
179
- const runtime = new KnowledgePackRuntime(trustedKeys);
180
- const load = runtime.load(artifact, TICKET08_REFERENCE_EVIDENCE_VERSION);
181
- if (!load.accepted)
182
- throw new Error(`Ticket 08 Knowledge Pack is not authoritative: ${load.blockingReasons.join(", ")}.`);
183
- const active = runtime.active();
184
- const capabilities = active.capabilities;
185
- if (capabilities.length !== 1 || capabilities[0].id !== TICKET08_TEST_MODEL_CREATE_CAPABILITY_ID)
186
- throw new Error("Ticket 08 Knowledge Pack must promote exactly the single test model-create capability; evidence inference to other capabilities is rejected.");
187
- const hashes = capabilities[0].operationContractHashes;
188
- if (hashes.length !== 1 || hashes[0] !== ticket08ModelCreateContractHash())
189
- throw new Error("Ticket 08 Knowledge Pack operation contract hash does not match the canonical model-create contract.");
190
- return { runtime, packId: active.manifest.packId, contentDigest: active.manifest.contentSha256, executableCapabilities: capabilities.map(({ id }) => id) };
191
- }
192
- /** Host-owned evidence capture: exact absence, dedicated application, existing non-default folder, canonical fingerprints. */
193
- export function captureTicket08TestMutationEvidence(input) {
194
- assertTicket08TestMutationEnvironment(input.settings);
195
- if (!TICKET08_UUID_V4.test(input.testRunId))
196
- throw new Error("Ticket 08 evidence capture requires a host-owned UUID v4 testRunId.");
197
- const discovery = input.discovery;
198
- if (!discovery.applications.some((application) => application.code === TICKET08_TEST_APPLICATION_CODE))
199
- throw new Error("Ticket 08 evidence capture requires the dedicated test application to be present in authoritative discovery.");
200
- const isDefaultFolder = (folder) => [folder.code, folder.name].some((value) => value?.trim().toLowerCase() === "default" || value?.trim() === "默认分组");
201
- const candidates = discovery.folders.filter((folder) => folder.applicationCode === TICKET08_TEST_APPLICATION_CODE);
202
- const nonDefault = candidates.filter((folder) => !isDefaultFolder(folder));
203
- // An explicit folderId must name a real non-default folder of the
204
- // application; the default folder is only a fallback when the dedicated
205
- // test application has no non-default folder at all. Ownership stays
206
- // provable in both cases through the prefix, folder binding, and readback.
207
- const folder = input.folderId
208
- ? (() => { const match = nonDefault.find((candidate) => candidate.id === input.folderId); if (!match)
209
- throw new Error("Ticket 08 evidence capture folderId must match an existing non-default folder of the dedicated test application."); return match; })()
210
- : nonDefault.length === 1 ? nonDefault[0] : nonDefault.length === 0 ? candidates.find(isDefaultFolder) : undefined;
211
- if (!folder)
212
- throw new Error("Ticket 08 evidence capture requires exactly one existing non-default folder in the dedicated test application, or an explicit matching folderId.");
213
- const targetSchemaCode = assertTicket08TargetSchema(input.targetSchemaCode);
214
- const modelEvidence = input.modelEvidence;
215
- if (modelEvidence.classification !== "absent" || modelEvidence.reason !== "exact-absence" || modelEvidence.query.schemaCode !== targetSchemaCode || modelEvidence.url !== `${TICKET08_MODEL_READBACK_ENDPOINT}?schemaCode=${encodeURIComponent(targetSchemaCode)}`)
216
- throw new Error("Ticket 08 evidence capture requires exact authenticated absence evidence for the host-derived target model.");
217
- if (discovery.models.some((model) => model.applicationCode === TICKET08_TEST_APPLICATION_CODE && model.schemaCode === targetSchemaCode))
218
- throw new Error("Ticket 08 target model is already present in authoritative discovery.");
219
- const authoritativeOrigin = discovery.authoritativeOrigin;
220
- const enterpriseId = discovery.enterpriseId;
221
- if (!authoritativeOrigin || !enterpriseId)
222
- throw new Error("Ticket 08 evidence capture requires authenticated environment fingerprint evidence.");
223
- const environmentFingerprint = canonicalCloudPivotEnvironmentFingerprint({ environment: TICKET08_TEST_ENVIRONMENT_NAME, authoritativeOrigin, applicationCode: TICKET08_TEST_APPLICATION_CODE, enterpriseId });
224
- const evidence = Object.freeze({
225
- route: TICKET08_TEST_MUTATION_ROUTE,
226
- capabilityId: TICKET08_TEST_MODEL_CREATE_CAPABILITY_ID,
227
- environmentName: TICKET08_TEST_ENVIRONMENT_NAME,
228
- applicationCode: TICKET08_TEST_APPLICATION_CODE,
229
- folder: Object.freeze({ id: folder.id, name: folder.name }),
230
- targetSchemaCode,
231
- testRunId: input.testRunId,
232
- resourceName: ticket08ResourceName(targetSchemaCode),
233
- environmentFingerprint,
234
- baselineFingerprint: canonicalCloudPivotBaselineFingerprint({ environmentFingerprint, applicationCode: TICKET08_TEST_APPLICATION_CODE, discovery }),
235
- discoveryFingerprint: canonicalFormalDiscoveryFingerprint(discovery),
236
- modelEvidence,
237
- cleanupContract: buildTicket08CleanupContract(input.testRunId, targetSchemaCode),
238
- capturedAt: new Date((input.now ?? Date.now)()).toISOString(),
239
- });
240
- if ([evidence.environmentFingerprint, evidence.baselineFingerprint, evidence.discoveryFingerprint].some((value) => !SHA256.test(value)))
241
- throw new Error("Ticket 08 evidence fingerprints are not canonical.");
242
- if (SENSITIVE_SECRET_VALUE.test(stable(evidence)))
243
- throw new Error("Ticket 08 evidence contains prohibited sensitive data.");
244
- return evidence;
245
- }
246
- export const TICKET08_EVIDENCE_DEFAULT_TTL_MS = 25 * 60_000;
247
- export const TICKET08_EVIDENCE_MAX_TTL_MS = 30 * 60_000;
248
- /** Single-use, TTL-bounded evidence store; consumed exactly once by plan creation. */
249
- export class Ticket08EvidenceStore {
250
- records = new Map();
251
- now;
252
- ttlMs;
253
- lastNow;
254
- constructor(options = {}) {
255
- this.now = options.now ?? Date.now;
256
- this.ttlMs = options.ttlMs ?? TICKET08_EVIDENCE_DEFAULT_TTL_MS;
257
- if (!Number.isFinite(this.ttlMs) || this.ttlMs < 1 || this.ttlMs > TICKET08_EVIDENCE_MAX_TTL_MS)
258
- throw new Error("Ticket 08 evidence TTL must be between 1ms and its safety maximum.");
259
- }
260
- capture(evidence) {
261
- validateTicket08Evidence(evidence);
262
- const evidenceId = `ticket08-evidence-${randomUUID()}`;
263
- const expiresAt = this.readClock() + this.ttlMs;
264
- const stored = Object.freeze({ ...evidence, evidenceArtifactId: evidenceId });
265
- this.records.set(evidenceId, { evidence: stored, expiresAt, consumed: false });
266
- return { evidenceId, expiresAt, evidence: stored };
267
- }
268
- consume(evidenceId) {
269
- const record = this.records.get(evidenceId);
270
- if (!record)
271
- throw new Error("Ticket 08 evidence artifact is missing or divergent.");
272
- if (record.consumed)
273
- throw new Error("Ticket 08 evidence has already been consumed.");
274
- if (record.expiresAt <= this.readClock())
275
- throw new Error("Ticket 08 evidence is stale or expired.");
276
- record.consumed = true;
277
- return record.evidence;
278
- }
279
- resolve(evidenceId) {
280
- const record = this.records.get(evidenceId);
281
- if (!record || !record.consumed)
282
- throw new Error("Ticket 08 evidence artifact is missing or has not been consumed by planning.");
283
- if (record.expiresAt <= this.readClock())
284
- throw new Error("Ticket 08 evidence artifact is stale or expired.");
285
- return record.evidence;
286
- }
287
- readClock() {
288
- const currentTime = this.now();
289
- if (!Number.isFinite(currentTime))
290
- throw new Error("Ticket 08 evidence clock is invalid; fail closed.");
291
- if (this.lastNow !== undefined && currentTime < this.lastNow)
292
- throw new Error("Ticket 08 evidence clock moved backwards; fail closed.");
293
- this.lastNow = currentTime;
294
- return currentTime;
295
- }
296
- }
297
- export function buildTicket08ConfigurationIntent(evidence) {
298
- validateTicket08Evidence(evidence);
299
- return {
300
- intentVersion: 1,
301
- targetFamily: "8.6",
302
- environmentBinding: { application: evidence.applicationCode, model: evidence.targetSchemaCode, baselineFingerprint: evidence.baselineFingerprint },
303
- sourceRequirement: "Ticket 08 maintainer test: create one unique test-only LIST model with authoritative readback, behavior validation, and owned cleanup.",
304
- resources: [{ id: evidence.targetSchemaCode, kind: "model", state: "create", properties: { modelType: "LIST", folderId: evidence.folder.id, name: evidence.resourceName } }],
305
- relationships: [],
306
- lifecycle: [{ resourceId: evidence.targetSchemaCode, transitions: ["create"], order: 1, idempotent: false, dependencyChecks: ["exact-absence-preflight"], recoveryBoundary: `model:${evidence.targetSchemaCode}` }],
307
- behavior: [{ id: "created-model-structure", kind: "structure", requiredCapabilityIds: [TICKET08_TEST_MODEL_CREATE_CAPABILITY_ID], resourceIds: [evidence.targetSchemaCode], obligations: ["authoritative get_all readback as LIST", "directory projection under the bound folder", "default form binding to the created model"] }],
308
- acceptance: [{ id: "ticket08-create-readback", scenario: "create-readback-behavior", actor: "omc-maintainer-opencode", assertions: ["model is present with exact schemaCode and modelType LIST", "model appears in discovery under the bound non-default folder", "default form binding targets the created model"], readbackInvariants: [`${TICKET08_MODEL_READBACK_ENDPOINT}?schemaCode=<target> returns the exact LIST model`, "discovery directory binds the model to the bound folder with a default form"], outcome: "validated" }],
309
- cleanup: [{ id: "ticket08-owned-cleanup", boundary: `ticket08:${evidence.testRunId}:model-physical-delete`, dependencyChecks: [`${TICKET08_MODEL_CLEANUP_ENDPOINT} dependency preflight errTypes empty`, "delete history taskStatus END"], resources: [`model:${evidence.targetSchemaCode}`] }],
310
- implementationConstraints: { nativePreferred: true, backendRequired: false, prohibitedPaths: ["form.online-js", "business-rule", "integration", "backend.extension"], approvalExceptions: [] },
311
- evidenceRefs: [TICKET08_TEST_MODEL_CREATE_CAPABILITY_ID, TICKET08_MODEL_CREATE_ENDPOINT, TICKET08_MODEL_READBACK_ENDPOINT, TICKET08_MODEL_CLEANUP_ENDPOINT, ...CLOUDPIVOT_8_6_26_SERIALIZED_MODEL_CREATE.evidence],
312
- unresolved: [],
313
- };
314
- }
315
- function ticket08ModelCreateOperation(evidence) {
316
- return {
317
- id: `model:${evidence.targetSchemaCode}`,
318
- method: "POST",
319
- path: TICKET08_MODEL_CREATE_ENDPOINT,
320
- target: `model:${evidence.targetSchemaCode}`,
321
- payload: buildListModelPayload({ appCode: evidence.applicationCode, parentId: evidence.folder.id, name: evidence.resourceName, schemaCode: evidence.targetSchemaCode }),
322
- expectedReadback: `model:${evidence.targetSchemaCode}:LIST`,
323
- evidence: [TICKET08_TEST_MODEL_CREATE_CAPABILITY_ID, ...CLOUDPIVOT_8_6_26_SERIALIZED_MODEL_CREATE.evidence, TICKET08_MODEL_READBACK_ENDPOINT],
324
- transportMode: "serialized-server-model-create-8.6.26",
325
- capability: CLOUDPIVOT_8_6_26_SERIALIZED_MODEL_CREATE,
326
- };
327
- }
328
- /** Creates and proposes the immutable Ticket 08 Delivery Plan Approval for the single create step. */
329
- export function createTicket08TestMutationPlanApproval(input) {
330
- const evidence = validateTicket08Evidence(input.evidence);
331
- assertDigest(input.knowledgePackDigest, "knowledge pack digest");
332
- const operation = ticket08ModelCreateOperation(evidence);
333
- assertTicket08OperationsBounded({ route: TICKET08_TEST_MUTATION_ROUTE, operations: [operation], targetSchemaCode: evidence.targetSchemaCode });
334
- const intent = buildTicket08ConfigurationIntent(evidence);
335
- if (validateConfigurationIntent(intent).length)
336
- throw new Error("Ticket 08 Configuration Intent is invalid; planning is blocked.");
337
- const binding = {
338
- deliveryId: ticket08DeliveryId(evidence.testRunId),
339
- environment: TICKET08_TEST_ENVIRONMENT_NAME,
340
- baselineFingerprint: evidence.baselineFingerprint,
341
- environmentFingerprint: evidence.environmentFingerprint,
342
- discoveryFingerprint: evidence.discoveryFingerprint,
343
- ...(evidence.evidenceArtifactId ? { evidenceArtifactId: evidence.evidenceArtifactId } : {}),
344
- };
345
- const planHash = digest({ route: TICKET08_TEST_MUTATION_ROUTE, capabilityId: TICKET08_TEST_MODEL_CREATE_CAPABILITY_ID, target: evidence.targetSchemaCode, testRunId: evidence.testRunId, operation: { id: operation.id, readback: operation.expectedReadback }, folder: evidence.folder, fingerprints: { baseline: evidence.baselineFingerprint, environment: evidence.environmentFingerprint, discovery: evidence.discoveryFingerprint }, cleanupContractHash: ticket08CleanupContractHash(), knowledgePackDigest: input.knowledgePackDigest, prohibited: [...TICKET08_PROHIBITED_MUTATION_TERMS] });
346
- const base = createDeliveryPlanApproval({
347
- binding,
348
- requirementHash: digest({ target: evidence.targetSchemaCode }),
349
- clarificationHash: digest("ticket08-test-model-mutation"),
350
- intentHash: digest(intent),
351
- decisionHash: digest(TICKET08_TEST_MUTATION_ROUTE),
352
- graphHash: digest([operation.id]),
353
- riskHash: digest(TICKET08_RISKS),
354
- validationPlanHash: digest("authoritative-model-get-readback-and-default-form-behavior"),
355
- cleanupPlanHash: ticket08CleanupContractHash(),
356
- allowedOperationScope: [ticket08ModelScope(evidence.targetSchemaCode)],
357
- planHash,
358
- applicationCode: TICKET08_TEST_APPLICATION_CODE,
359
- folderId: evidence.folder.id,
360
- folderName: evidence.folder.name,
361
- targets: [evidence.targetSchemaCode],
362
- });
363
- const approval = {
364
- ...base,
365
- route: TICKET08_TEST_MUTATION_ROUTE,
366
- testRunId: evidence.testRunId,
367
- targetSchemaCode: evidence.targetSchemaCode,
368
- capabilityId: TICKET08_TEST_MODEL_CREATE_CAPABILITY_ID,
369
- knowledgePackDigest: input.knowledgePackDigest,
370
- cleanupContractHash: ticket08CleanupContractHash(),
371
- environmentName: TICKET08_TEST_ENVIRONMENT_NAME,
372
- };
373
- const proposal = { ...approval, approvalHash: deliveryPlanApprovalHash(approval) };
374
- proposeDeliveryPlan(input.ledger, proposal);
375
- return { approval: proposal, operation, cleanupContract: evidence.cleanupContract, intent, binding };
376
- }
377
- const TICKET08_RISKS = [
378
- "Exactly one unique test-only LIST model create is in scope; field, form, view, runtime, folder, publish, deployment, restart, release, Redis, and tenant administration remain rejected.",
379
- "Owned cleanup is separately approved and bounded to resources created by this testRunId with authoritative absence readback.",
380
- ];
381
- /** Proves the fixture was created by this run: schema prefix, dedicated application, bound folder, present readback. */
382
- export function validateTicket08CleanupOwnership(input) {
383
- const evidence = validateTicket08Evidence(input.evidence);
384
- assertTicket08TargetSchema(evidence.targetSchemaCode);
385
- const entry = input.designTime.directory.models.find((candidate) => candidate.schemaCode === evidence.targetSchemaCode);
386
- if (!entry || entry.appCode !== evidence.applicationCode)
387
- throw new Error(`Ticket 08 cleanup target ${evidence.targetSchemaCode} is not present in the authoritative design-time directory under the dedicated test application.`);
388
- if (input.modelEvidence.classification !== "present" || input.modelEvidence.query.schemaCode !== evidence.targetSchemaCode)
389
- throw new Error("Ticket 08 cleanup requires the created model to be present in authoritative readback.");
390
- if (entry.modelType !== "LIST")
391
- throw new Error("Ticket 08 cleanup ownership requires the created LIST model semantics.");
392
- if (entry.parentId !== evidence.folder.id)
393
- throw new Error("Ticket 08 cleanup ownership folder binding diverges from the evidence folder.");
394
- if (typeof entry.id !== "string" || !entry.id)
395
- throw new Error("Ticket 08 cleanup ownership requires the authoritative model id.");
396
- return {
397
- modelId: entry.id,
398
- folderId: entry.parentId ?? evidence.folder.id,
399
- testRunId: evidence.testRunId,
400
- postCreateBaselineFingerprint: canonicalCloudPivotBaselineFingerprint({ environmentFingerprint: evidence.environmentFingerprint, applicationCode: evidence.applicationCode, discovery: input.discovery }),
401
- };
402
- }
403
- /** Creates and proposes the separately gated cleanup Delivery Plan Approval bound to the owned fixture. */
404
- export function createTicket08CleanupPlanApproval(input) {
405
- const evidence = validateTicket08Evidence(input.evidence);
406
- assertDigest(input.knowledgePackDigest, "knowledge pack digest");
407
- if (input.ownership.testRunId !== evidence.testRunId || input.ownership.folderId !== evidence.folder.id || !input.ownership.modelId || !SHA256.test(input.ownership.postCreateBaselineFingerprint))
408
- throw new Error("Ticket 08 cleanup approval requires the exact validated ownership binding.");
409
- const operation = buildPhysicalCleanupOperation({ kind: "model", schemaCode: evidence.targetSchemaCode, id: input.ownership.modelId });
410
- assertTicket08OperationsBounded({ route: TICKET08_TEST_MODEL_CLEANUP_ROUTE, operations: [operation], targetSchemaCode: evidence.targetSchemaCode });
411
- const binding = {
412
- deliveryId: ticket08CleanupDeliveryId(evidence.testRunId),
413
- environment: TICKET08_TEST_ENVIRONMENT_NAME,
414
- baselineFingerprint: input.ownership.postCreateBaselineFingerprint,
415
- environmentFingerprint: evidence.environmentFingerprint,
416
- ...(evidence.evidenceArtifactId ? { evidenceArtifactId: evidence.evidenceArtifactId } : {}),
417
- };
418
- const planHash = digest({ route: TICKET08_TEST_MODEL_CLEANUP_ROUTE, capabilityId: TICKET08_TEST_MODEL_CLEANUP_CAPABILITY_ID, target: evidence.targetSchemaCode, testRunId: evidence.testRunId, modelId: input.ownership.modelId, operation: { id: operation.id, readback: operation.expectedReadback }, cleanupContractHash: ticket08CleanupContractHash(), knowledgePackDigest: input.knowledgePackDigest, prohibited: [...TICKET08_PROHIBITED_MUTATION_TERMS] });
419
- const base = createDeliveryPlanApproval({
420
- binding,
421
- requirementHash: digest({ target: evidence.targetSchemaCode, cleanup: true }),
422
- clarificationHash: digest("ticket08-owned-cleanup"),
423
- intentHash: digest({ cleanup: true, target: evidence.targetSchemaCode, modelId: input.ownership.modelId }),
424
- decisionHash: digest(TICKET08_TEST_MODEL_CLEANUP_ROUTE),
425
- graphHash: digest([operation.id]),
426
- riskHash: digest(TICKET08_CLEANUP_RISKS),
427
- validationPlanHash: digest("physical-delete-history-end-and-exact-absence"),
428
- cleanupPlanHash: ticket08CleanupContractHash(),
429
- allowedOperationScope: [ticket08ModelScope(evidence.targetSchemaCode)],
430
- planHash,
431
- applicationCode: TICKET08_TEST_APPLICATION_CODE,
432
- folderId: evidence.folder.id,
433
- folderName: evidence.folder.name,
434
- targets: [evidence.targetSchemaCode],
435
- });
436
- const approval = {
437
- ...base,
438
- route: TICKET08_TEST_MODEL_CLEANUP_ROUTE,
439
- testRunId: evidence.testRunId,
440
- targetSchemaCode: evidence.targetSchemaCode,
441
- capabilityId: TICKET08_TEST_MODEL_CLEANUP_CAPABILITY_ID,
442
- knowledgePackDigest: input.knowledgePackDigest,
443
- cleanupContractHash: ticket08CleanupContractHash(),
444
- environmentName: TICKET08_TEST_ENVIRONMENT_NAME,
445
- };
446
- const proposal = { ...approval, approvalHash: deliveryPlanApprovalHash(approval) };
447
- proposeDeliveryPlan(input.ledger, proposal);
448
- return { approval: proposal, operation, cleanupContract: evidence.cleanupContract, intent: buildTicket08ConfigurationIntent(evidence), binding };
449
- }
450
- const TICKET08_CLEANUP_RISKS = [
451
- "Cleanup physically deletes exactly the one model created by this testRunId after ownership and dependency preflight.",
452
- "Uncertain delete outcomes stop, retain, and report the fixture; replay and unbounded deletion are rejected.",
453
- ];
454
- /**
455
- * Maintainer orphan cleanup: prior Ticket 08 runs whose create committed but
456
- * whose cleanup could not complete left host-owned fixtures. They are provably
457
- * ours (omc_t08_ prefix, dedicated test application, non-default folder, LIST,
458
- * present schema, design-time directory entry) and are invisible to every
459
- * published UI view. Adoption is bounded to exactly this shape and still
460
- * requires both DSH-native approvals and exact absence readback.
461
- */
462
- export const TICKET08_ORPHAN_CLEANUP_CAPABILITY_ID = "cloudpivot.test.orphan-cleanup-8.6.26";
463
- export function captureTicket08OrphanCleanupEvidence(input) {
464
- assertTicket08TestMutationEnvironment(input.settings);
465
- const targetSchemaCode = assertTicket08TargetSchema(input.modelEvidence.query.schemaCode);
466
- if (input.modelEvidence.classification !== "present")
467
- throw new Error(`Ticket 08 orphan cleanup requires the fixture to be present in authoritative readback; ${targetSchemaCode} is ${input.modelEvidence.classification}.`);
468
- const entry = input.designTime.directory.models.find((candidate) => candidate.schemaCode === targetSchemaCode);
469
- if (!entry || entry.appCode !== TICKET08_TEST_APPLICATION_CODE || entry.modelType !== "LIST")
470
- throw new Error(`Ticket 08 orphan cleanup requires a design-time directory entry binding ${targetSchemaCode} to the dedicated test application as LIST.`);
471
- if (entry.parentId !== input.folder.id || input.designTime.directory.folderId !== input.folder.id)
472
- throw new Error("Ticket 08 orphan cleanup folder binding diverges between the directory entry and the adopted folder.");
473
- if (typeof entry.id !== "string" || !entry.id)
474
- throw new Error("Ticket 08 orphan cleanup requires the design-time directory node id.");
475
- if (!input.folder.name)
476
- throw new Error("Ticket 08 orphan cleanup requires the adopted folder of the dedicated test application.");
477
- const environmentFingerprint = canonicalCloudPivotEnvironmentFingerprint({ environment: TICKET08_TEST_ENVIRONMENT_NAME, authoritativeOrigin: input.authoritativeOrigin, applicationCode: TICKET08_TEST_APPLICATION_CODE, enterpriseId: input.enterpriseId });
478
- // The canonical discovery baseline keeps the executor mutation boundary
479
- // comparable at orphan execution time.
480
- const baselineFingerprint = canonicalCloudPivotBaselineFingerprint({ environmentFingerprint, applicationCode: TICKET08_TEST_APPLICATION_CODE, discovery: input.discovery });
481
- const evidence = Object.freeze({
482
- route: TICKET08_TEST_MODEL_CLEANUP_ROUTE,
483
- capabilityId: TICKET08_ORPHAN_CLEANUP_CAPABILITY_ID,
484
- environmentName: TICKET08_TEST_ENVIRONMENT_NAME,
485
- applicationCode: TICKET08_TEST_APPLICATION_CODE,
486
- folder: Object.freeze({ id: input.folder.id, name: input.folder.name }),
487
- targetSchemaCode,
488
- nodeModelId: entry.id,
489
- environmentFingerprint,
490
- baselineFingerprint,
491
- modelEvidence: input.modelEvidence,
492
- capturedAt: new Date((input.now ?? Date.now)()).toISOString(),
493
- });
494
- if (!SHA256.test(evidence.environmentFingerprint) || !SHA256.test(evidence.baselineFingerprint))
495
- throw new Error("Ticket 08 orphan evidence fingerprints are not canonical.");
496
- if (SENSITIVE_SECRET_VALUE.test(stable(evidence)))
497
- throw new Error("Ticket 08 orphan evidence contains prohibited sensitive data.");
498
- return evidence;
499
- }
500
- const TICKET08_ORPHAN_CLEANUP_RISKS = [
501
- "Orphan cleanup physically deletes exactly one previously retained host-owned omc_t08 fixture after adoption ownership and dependency preflight.",
502
- "Uncertain delete outcomes stop, retain, and report the fixture; replay and unbounded deletion are rejected.",
503
- ];
504
- export function createTicket08OrphanCleanupPlanApproval(input) {
505
- const evidence = input.evidence;
506
- assertDigest(input.knowledgePackDigest, "knowledge pack digest");
507
- assertTicket08TargetSchema(evidence.targetSchemaCode);
508
- const operation = buildPhysicalCleanupOperation({ kind: "model", schemaCode: evidence.targetSchemaCode, id: evidence.nodeModelId });
509
- assertTicket08OperationsBounded({ route: TICKET08_TEST_MODEL_CLEANUP_ROUTE, operations: [operation], targetSchemaCode: evidence.targetSchemaCode });
510
- const binding = {
511
- deliveryId: `ticket08-orphan-cleanup-${evidence.targetSchemaCode}`,
512
- environment: TICKET08_TEST_ENVIRONMENT_NAME,
513
- baselineFingerprint: evidence.baselineFingerprint,
514
- environmentFingerprint: evidence.environmentFingerprint,
515
- };
516
- const planHash = digest({ route: TICKET08_TEST_MODEL_CLEANUP_ROUTE, capabilityId: TICKET08_ORPHAN_CLEANUP_CAPABILITY_ID, target: evidence.targetSchemaCode, nodeModelId: evidence.nodeModelId, operation: { id: operation.id, readback: operation.expectedReadback }, cleanupContractHash: ticket08CleanupContractHash(), knowledgePackDigest: input.knowledgePackDigest, prohibited: [...TICKET08_PROHIBITED_MUTATION_TERMS] });
517
- const base = createDeliveryPlanApproval({
518
- binding,
519
- requirementHash: digest({ orphanCleanup: true, target: evidence.targetSchemaCode }),
520
- clarificationHash: digest("ticket08-orphan-cleanup"),
521
- intentHash: digest({ orphanCleanup: true, target: evidence.targetSchemaCode, nodeModelId: evidence.nodeModelId }),
522
- decisionHash: digest("ticket08-orphan-cleanup"),
523
- graphHash: digest([operation.id]),
524
- riskHash: digest(TICKET08_ORPHAN_CLEANUP_RISKS),
525
- validationPlanHash: digest("physical-delete-history-end-and-exact-absence"),
526
- cleanupPlanHash: ticket08CleanupContractHash(),
527
- allowedOperationScope: [ticket08ModelScope(evidence.targetSchemaCode)],
528
- planHash,
529
- applicationCode: TICKET08_TEST_APPLICATION_CODE,
530
- folderId: evidence.folder.id,
531
- folderName: evidence.folder.name,
532
- targets: [evidence.targetSchemaCode],
533
- });
534
- const approval = {
535
- ...base,
536
- route: TICKET08_TEST_MODEL_CLEANUP_ROUTE,
537
- testRunId: `orphan-${evidence.targetSchemaCode}`,
538
- targetSchemaCode: evidence.targetSchemaCode,
539
- capabilityId: TICKET08_ORPHAN_CLEANUP_CAPABILITY_ID,
540
- knowledgePackDigest: input.knowledgePackDigest,
541
- cleanupContractHash: ticket08CleanupContractHash(),
542
- environmentName: TICKET08_TEST_ENVIRONMENT_NAME,
543
- };
544
- const proposal = { ...approval, approvalHash: deliveryPlanApprovalHash(approval) };
545
- proposeDeliveryPlan(input.ledger, proposal);
546
- return { approval: proposal, operation };
547
- }
548
- export function prepareTicket08OrphanCleanupPackage(input) {
549
- const evidence = input.evidence;
550
- const value = input.approval;
551
- if (value.route !== TICKET08_TEST_MODEL_CLEANUP_ROUTE || value.capabilityId !== TICKET08_ORPHAN_CLEANUP_CAPABILITY_ID || value.environmentName !== "test" || value.applicationCode !== "test" || value.targets?.length !== 1 || value.targets?.[0] !== evidence.targetSchemaCode)
552
- throw new Error("Ticket 08 orphan package preparation requires the exact proposed orphan cleanup approval.");
553
- const persisted = input.ledger.findPlanApproval(value.approvalId);
554
- if (!persisted || persisted.status !== "approved" || persisted.approvalHash !== value.approvalHash)
555
- throw new Error("Ticket 08 orphan package preparation requires the approved orphan cleanup Delivery Plan Approval.");
556
- if (!persisted.allowedOperationScope.includes(ticket08ModelScope(evidence.targetSchemaCode)))
557
- throw new Error(`Ticket 08 orphan step is outside the approved operation scope: ${ticket08ModelScope(evidence.targetSchemaCode)}.`);
558
- if (input.knowledgePackDigest !== value.knowledgePackDigest)
559
- throw new Error("Ticket 08 knowledge pack digest diverges from the orphan cleanup approval binding.");
560
- const operation = buildPhysicalCleanupOperation({ kind: "model", schemaCode: evidence.targetSchemaCode, id: evidence.nodeModelId });
561
- assertTicket08OperationsBounded({ route: TICKET08_TEST_MODEL_CLEANUP_ROUTE, operations: [operation], targetSchemaCode: evidence.targetSchemaCode });
562
- const snapshotBase = { id: `snapshot:${operation.id}`, target: operation.target, identity: operation.target, recoveryBoundary: operation.target, state: "absent", baselineFingerprint: persisted.binding.baselineFingerprint };
563
- const snapshot = { ...snapshotBase, snapshotHash: digest(snapshotBase) };
564
- const pkg = compileNativeOperations({
565
- operations: [operation],
566
- binding: persisted.binding,
567
- planApproval: persisted,
568
- snapshotManifest: [snapshot],
569
- releasePlanHash: digest("ticket08-no-release"),
570
- recoveryPolicyHash: digest("ticket08-orphan-cleanup-step-only"),
571
- knowledgePack: { id: TICKET08_TEST_MUTATION_KNOWLEDGE_PACK_ID, digest: input.knowledgePackDigest },
572
- capabilityEvidence: [TICKET08_ORPHAN_CLEANUP_CAPABILITY_ID, TICKET08_MODEL_CLEANUP_ENDPOINT, "/api/api/app/apppackage/delete/history", `nodeModelId:${evidence.nodeModelId}`, `adoptedFixture:${evidence.targetSchemaCode}`],
573
- requiredPermissions: ["configuration-write"],
574
- packageId: `ticket08-orphan-cleanup-${digest(persisted.approvalId).slice(0, 32)}`,
575
- ledger: input.ledger,
576
- });
577
- input.ledger.createPackage(pkg);
578
- return { package: pkg, operations: [operation], planApproval: persisted, evidence };
579
- }
580
- export async function executeTicket08OrphanCleanupPackage(input) {
581
- assertTicket08TestMutationEnvironment(input.settings);
582
- if (typeof input.transport.physicalDeleteModel !== "function")
583
- throw new Error("Ticket 08 orphan cleanup requires the OMC Host physical model delete transport; bypass transports are rejected.");
584
- const projection = input.ledger.project(input.prepared.package.packageId);
585
- assertTicket08ExecutionBoundary({ projection, environmentName: input.settings.environmentName, packageId: input.prepared.package.packageId, route: TICKET08_TEST_MODEL_CLEANUP_ROUTE, steps: input.prepared.package.steps });
586
- if (input.environmentApproval.packageId !== input.prepared.package.packageId || input.environmentApproval.packageHash !== input.prepared.package.packageHash)
587
- throw new Error("Ticket 08 orphan cleanup Environment Change Approval does not match the prepared package.");
588
- const probe = await input.readModelEvidence();
589
- if (probe.classification === "absent") {
590
- return { reportType: "omc.ticket08.test-mutation-execution", schemaVersion: 1, packageId: input.prepared.package.packageId, packageHash: input.prepared.package.packageHash, route: TICKET08_TEST_MODEL_CLEANUP_ROUTE, testRunId: `orphan-${input.evidence.targetSchemaCode}`, targetSchemaCode: input.evidence.targetSchemaCode, status: "blocked", mutationExecuted: false, steps: projection.steps, blockers: ["Ticket 08 orphan cleanup target is already absent; no unbounded deletion is attempted."], nextAction: "accept", absenceReadback: probe };
591
- }
592
- const result = await runNativeConfigurationPackage({
593
- package: input.prepared.package,
594
- gate: {
595
- mode: "approved",
596
- planApproval: { status: "approved", approvalHash: input.prepared.planApproval.approvalHash },
597
- environmentApproval: { status: "approved", packageHash: input.environmentApproval.packageHash },
598
- lease: { owner: "configuration-agent", environment: TICKET08_TEST_ENVIRONMENT_NAME },
599
- baselineFingerprint: input.prepared.package.binding.baselineFingerprint,
600
- snapshots: input.prepared.package.snapshotManifest,
601
- package: input.prepared.package,
602
- },
603
- operations: input.prepared.operations,
604
- ledger: input.ledger,
605
- planApproval: input.prepared.planApproval,
606
- transport: input.transport,
607
- credentials: input.credentials,
608
- authoritativeOrigin: input.authoritativeOrigin,
609
- testApplicationCode: TICKET08_TEST_APPLICATION_CODE,
610
- }, input.signal);
611
- const mutationExecuted = result.steps.some((step) => step.status === "committed" || step.status === "uncertain");
612
- const blockers = [...result.blockers];
613
- let status = ticket08ExecutionStatus(result.status);
614
- let nextAction = result.nextAction;
615
- let absenceReadback;
616
- if (result.status === "completed") {
617
- absenceReadback = await input.readModelEvidence();
618
- if (absenceReadback.classification !== "absent" || absenceReadback.reason !== "exact-absence") {
619
- status = "uncertain";
620
- blockers.push("Ticket 08 orphan post-cleanup exact absence readback did not confirm absence; the fixture is retained and reported.");
621
- nextAction = "reconcile";
622
- }
623
- }
624
- return { reportType: "omc.ticket08.test-mutation-execution", schemaVersion: 1, packageId: result.packageId, packageHash: result.packageHash, route: TICKET08_TEST_MODEL_CLEANUP_ROUTE, testRunId: `orphan-${input.evidence.targetSchemaCode}`, targetSchemaCode: input.evidence.targetSchemaCode, status, mutationExecuted, steps: result.steps, blockers, nextAction, ...(absenceReadback ? { absenceReadback } : {}) };
625
- }
626
- export function formatTicket08OrphanCleanupDeliveryPlanApprovalFrame(approval) {
627
- const value = approval;
628
- if (value.route !== TICKET08_TEST_MODEL_CLEANUP_ROUTE || value.capabilityId !== TICKET08_ORPHAN_CLEANUP_CAPABILITY_ID)
629
- throw new Error("Approval frame is not the Ticket 08 orphan cleanup Delivery Plan Approval.");
630
- return appendApprovalHumanBlock(`Approve Ticket 08 orphan cleanup Delivery Plan Approval ${value.approvalId} hash ${value.approvalHash}; plan hash ${value.planHash}; capability ${value.capabilityId}; environment test; application test; environment fingerprint ${value.binding.environmentFingerprint}; adopted baseline fingerprint ${value.binding.baselineFingerprint}; folder ${value.folderId} (${value.folderName}); adopted fixture ${value.targetSchemaCode}; exact scope ${value.allowedOperationScope[0]}; physically delete exactly this retained host-owned omc_t08 fixture after adoption ownership and dependency preflight.`, { capability: "ticket08-orphan-cleanup", kind: "delivery-plan", identities: [value.targetSchemaCode], scope: [value.allowedOperationScope[0]] });
631
- }
632
- export function formatTicket08OrphanCleanupEnvironmentChangeApprovalFrame(input) {
633
- const value = input.approval;
634
- if (value.route !== TICKET08_TEST_MODEL_CLEANUP_ROUTE || value.capabilityId !== TICKET08_ORPHAN_CLEANUP_CAPABILITY_ID)
635
- throw new Error("Approval frame is not the Ticket 08 orphan cleanup Environment Change Approval.");
636
- return appendApprovalHumanBlock(`Approve Ticket 08 orphan cleanup Environment Change Approval for package ${input.packageId} hash ${input.packageHash}; plan approval ${value.approvalId} hash ${value.approvalHash}; environment test; adopted fixture ${value.targetSchemaCode}; exactly one owned physical model delete with task-history END and exact absence readback; uncertain outcomes stop, retain, and report.`, { capability: "ticket08-orphan-cleanup", kind: "environment-change", identities: [value.targetSchemaCode], scope: [value.allowedOperationScope[0]] });
637
- }
638
- /** Compiles the immutable one-step create package bound to the approved plan, pack digest, and pinned contract hash. */
639
- export function prepareTicket08TestMutationPackage(input) {
640
- const evidence = validateTicket08Evidence(input.evidence);
641
- const approval = requireTicket08Approval(input.approval, TICKET08_TEST_MUTATION_ROUTE, TICKET08_TEST_MODEL_CREATE_CAPABILITY_ID);
642
- const persisted = requirePersistedApprovedPlan(input.ledger, approval);
643
- assertTicket08BindingMatchesEvidence(persisted.binding, evidence);
644
- if (!persisted.allowedOperationScope.includes(ticket08ModelScope(evidence.targetSchemaCode)))
645
- throw new Error(`Ticket 08 package step is outside the approved operation scope: ${ticket08ModelScope(evidence.targetSchemaCode)}.`);
646
- if (input.pinnedModelCreateContractHash !== ticket08ModelCreateContractHash())
647
- throw new Error("Ticket 08 pinned model-create contract hash does not match the canonical 8.6.26 capability contract.");
648
- assertDigest(input.knowledgePackDigest, "knowledge pack digest");
649
- if (input.knowledgePackDigest !== approval.knowledgePackDigest)
650
- throw new Error("Ticket 08 knowledge pack digest diverges from the approved plan binding.");
651
- const operation = ticket08ModelCreateOperation(evidence);
652
- assertTicket08OperationsBounded({ route: TICKET08_TEST_MUTATION_ROUTE, operations: [operation], targetSchemaCode: evidence.targetSchemaCode });
653
- const pkg = compileTicket08Package({ ledger: input.ledger, planApproval: persisted, operation, knowledgePackDigest: input.knowledgePackDigest, packageId: `ticket08-${digest(persisted.approvalId).slice(0, 32)}`, recoveryPolicyHash: digest("ticket08-create-step-only"), capabilityEvidence: [TICKET08_TEST_MODEL_CREATE_CAPABILITY_ID, "cloudpivot.model-create.serialized-server-8.6.26", TICKET08_MODEL_READBACK_ENDPOINT, `folder:${evidence.folder.id}`, `testRunId:${evidence.testRunId}`, `baselineFingerprint:${persisted.binding.baselineFingerprint}`] });
654
- const dryRun = buildNativeConfigurationDryRun({ appCode: evidence.applicationCode, models: [{ name: evidence.resourceName, schemaCode: evidence.targetSchemaCode, parentId: evidence.folder.id }], fields: [], forms: [] });
655
- return { package: pkg, operations: [operation], planApproval: persisted, dryRun, evidence };
656
- }
657
- /** Compiles the immutable one-step owned-cleanup package bound to the separately approved cleanup plan. */
658
- export function prepareTicket08CleanupPackage(input) {
659
- const evidence = validateTicket08Evidence(input.evidence);
660
- const approval = requireTicket08Approval(input.approval, TICKET08_TEST_MODEL_CLEANUP_ROUTE, TICKET08_TEST_MODEL_CLEANUP_CAPABILITY_ID);
661
- const persisted = requirePersistedApprovedPlan(input.ledger, approval);
662
- if (persisted.binding.environmentFingerprint !== evidence.environmentFingerprint)
663
- throw new Error("Ticket 08 cleanup approval is outside the evidence environment binding.");
664
- if (!persisted.allowedOperationScope.includes(ticket08ModelScope(evidence.targetSchemaCode)))
665
- throw new Error(`Ticket 08 cleanup step is outside the approved operation scope: ${ticket08ModelScope(evidence.targetSchemaCode)}.`);
666
- if (input.pinnedCleanupContractHash !== ticket08CleanupContractHash())
667
- throw new Error("Ticket 08 pinned cleanup contract hash does not match the canonical owned-cleanup contract.");
668
- assertDigest(input.knowledgePackDigest, "knowledge pack digest");
669
- if (input.knowledgePackDigest !== approval.knowledgePackDigest)
670
- throw new Error("Ticket 08 knowledge pack digest diverges from the approved cleanup plan binding.");
671
- if (input.ownership.testRunId !== evidence.testRunId || !input.ownership.modelId)
672
- throw new Error("Ticket 08 cleanup package requires the exact validated ownership binding.");
673
- const operation = buildPhysicalCleanupOperation({ kind: "model", schemaCode: evidence.targetSchemaCode, id: input.ownership.modelId });
674
- assertTicket08OperationsBounded({ route: TICKET08_TEST_MODEL_CLEANUP_ROUTE, operations: [operation], targetSchemaCode: evidence.targetSchemaCode });
675
- const pkg = compileTicket08Package({ ledger: input.ledger, planApproval: persisted, operation, knowledgePackDigest: input.knowledgePackDigest, packageId: `ticket08-cleanup-${digest(persisted.approvalId).slice(0, 32)}`, recoveryPolicyHash: digest("ticket08-cleanup-step-only"), capabilityEvidence: [TICKET08_TEST_MODEL_CLEANUP_CAPABILITY_ID, TICKET08_MODEL_CLEANUP_ENDPOINT, "/api/api/app/apppackage/delete/history", `modelId:${input.ownership.modelId}`, `testRunId:${evidence.testRunId}`] });
676
- const dryRun = buildNativeConfigurationDryRun({ appCode: evidence.applicationCode, models: [], fields: [], forms: [] });
677
- return { package: pkg, operations: [operation], planApproval: persisted, dryRun, evidence };
678
- }
679
- function compileTicket08Package(input) {
680
- const snapshotBase = { id: `snapshot:${input.operation.id}`, target: input.operation.target, identity: input.operation.target, recoveryBoundary: input.operation.target, state: "absent", baselineFingerprint: input.planApproval.binding.baselineFingerprint };
681
- const snapshot = { ...snapshotBase, snapshotHash: digest(snapshotBase) };
682
- const pkg = compileNativeOperations({
683
- operations: [input.operation],
684
- binding: input.planApproval.binding,
685
- planApproval: input.planApproval,
686
- snapshotManifest: [snapshot],
687
- releasePlanHash: digest("ticket08-no-release"),
688
- recoveryPolicyHash: input.recoveryPolicyHash,
689
- knowledgePack: { id: TICKET08_TEST_MUTATION_KNOWLEDGE_PACK_ID, digest: input.knowledgePackDigest },
690
- capabilityEvidence: input.capabilityEvidence,
691
- requiredPermissions: ["configuration-write"],
692
- packageId: input.packageId,
693
- ledger: input.ledger,
694
- });
695
- input.ledger.createPackage(pkg);
696
- return pkg;
697
- }
698
- export function formatTicket08DeliveryPlanApprovalFrame(approval) {
699
- const value = requireTicket08Approval(approval, TICKET08_TEST_MUTATION_ROUTE, TICKET08_TEST_MODEL_CREATE_CAPABILITY_ID);
700
- return appendApprovalHumanBlock(`Approve Ticket 08 Delivery Plan Approval ${value.approvalId} hash ${value.approvalHash}; plan hash ${value.planHash}; capability ${value.capabilityId}; environment test; application test; environment fingerprint ${value.binding.environmentFingerprint}; baseline fingerprint ${value.binding.baselineFingerprint}; folder ${value.folderId} (${value.folderName}); target ${value.targetSchemaCode}; testRunId ${value.testRunId}; exact scope ${value.allowedOperationScope[0]}; one serialized LIST model-create step with authoritative readback, behavior validation, and separately approved owned cleanup.`, { capability: "ticket08-model-create", kind: "delivery-plan", identities: [value.targetSchemaCode], scope: [value.allowedOperationScope[0]] });
701
- }
702
- export function formatTicket08EnvironmentChangeApprovalFrame(input) {
703
- const value = requireTicket08Approval(input.approval, TICKET08_TEST_MUTATION_ROUTE, TICKET08_TEST_MODEL_CREATE_CAPABILITY_ID);
704
- return appendApprovalHumanBlock(`Approve Ticket 08 Environment Change Approval for package ${input.packageId} hash ${input.packageHash}; plan approval ${value.approvalId} hash ${value.approvalHash}; environment test; folder ${value.folderId} (${value.folderName}); target ${value.targetSchemaCode}; testRunId ${value.testRunId}; exact scope ${value.allowedOperationScope[0]}; exactly one serialized LIST model-create step executed by the single Forge configuration writer lease with authoritative readback.`, { capability: "ticket08-model-create", kind: "environment-change", identities: [value.targetSchemaCode], scope: [value.allowedOperationScope[0]] });
705
- }
706
- export function formatTicket08CleanupDeliveryPlanApprovalFrame(approval) {
707
- const value = requireTicket08Approval(approval, TICKET08_TEST_MODEL_CLEANUP_ROUTE, TICKET08_TEST_MODEL_CLEANUP_CAPABILITY_ID);
708
- return appendApprovalHumanBlock(`Approve Ticket 08 cleanup Delivery Plan Approval ${value.approvalId} hash ${value.approvalHash}; plan hash ${value.planHash}; capability ${value.capabilityId}; environment test; application test; environment fingerprint ${value.binding.environmentFingerprint}; post-create baseline fingerprint ${value.binding.baselineFingerprint}; folder ${value.folderId} (${value.folderName}); target ${value.targetSchemaCode}; testRunId ${value.testRunId}; exact scope ${value.allowedOperationScope[0]}; physically delete only the owned fixture after ownership and dependency preflight.`, { capability: "ticket08-model-cleanup", kind: "delivery-plan", identities: [value.targetSchemaCode], scope: [value.allowedOperationScope[0]] });
709
- }
710
- export function formatTicket08CleanupEnvironmentChangeApprovalFrame(input) {
711
- const value = requireTicket08Approval(input.approval, TICKET08_TEST_MODEL_CLEANUP_ROUTE, TICKET08_TEST_MODEL_CLEANUP_CAPABILITY_ID);
712
- return appendApprovalHumanBlock(`Approve Ticket 08 cleanup Environment Change Approval for package ${input.packageId} hash ${input.packageHash}; plan approval ${value.approvalId} hash ${value.approvalHash}; environment test; target ${value.targetSchemaCode}; testRunId ${value.testRunId}; exactly one owned physical model delete with task-history END and exact absence readback; uncertain outcomes stop, retain, and report.`, { capability: "ticket08-model-cleanup", kind: "environment-change", identities: [value.targetSchemaCode], scope: [value.allowedOperationScope[0]] });
713
- }
714
- export function validateTicket08ModelBehavior(input) {
715
- const evidence = validateTicket08Evidence(input.evidence);
716
- const checks = [];
717
- const add = (id, ok, detail) => checks.push({ id, status: ok ? "validated" : "blocked", detail });
718
- const present = input.modelEvidence.classification === "present" && input.modelEvidence.query.schemaCode === evidence.targetSchemaCode;
719
- add("authoritative-model-present", present, present ? "get_all reports the created model as present." : "authoritative model readback did not report the created model as present.");
720
- const directory = input.designTime.directory;
721
- const entry = directory.models.find((candidate) => candidate.schemaCode === evidence.targetSchemaCode);
722
- add("design-time-directory", Boolean(entry), entry ? "the design-time folder directory lists the created model." : `the design-time directory of folder ${directory.folderId} did not list the created model.`);
723
- add("dedicated-application", entry?.appCode === evidence.applicationCode, `directory entry binds the model to application ${entry?.appCode ?? "<missing>"}.`);
724
- add("list-semantics", entry?.modelType === "LIST", `model type is ${entry?.modelType ?? "<missing>"}.`);
725
- add("folder-binding", entry?.parentId === evidence.folder.id, `model folder is ${entry?.parentId ?? "<missing>"}.`);
726
- const form = input.designTime.defaultForm;
727
- add("default-form-binding", form?.sheetCode === evidence.targetSchemaCode && (form?.bindingModelCode ?? evidence.targetSchemaCode) === evidence.targetSchemaCode, form ? "the auto-created default form binds the created model." : "default form binding did not bind the created model.");
728
- const blocked = checks.find((check) => check.status === "blocked");
729
- return blocked ? { status: "blocked", reason: `Ticket 08 behavior validation failed: ${blocked.detail}`, checks } : { status: "validated", checks };
730
- }
731
- /** Execution boundary: named test environment, route-bound package steps, active approvals, no replay of terminal or uncertain packages. */
732
- export function assertTicket08ExecutionBoundary(input) {
733
- if (input.environmentName !== "test")
734
- throw new Error("Ticket 08 execution is restricted to the named test environment; production and staging are rejected.");
735
- const { projection } = input;
736
- const routeStepPrefix = input.route === TICKET08_TEST_MUTATION_ROUTE ? "model:" : "cleanup:model:";
737
- if (input.steps.length !== 1 || !input.steps[0].id.startsWith(routeStepPrefix))
738
- throw new Error(`Ticket 08 execution boundary rejected package ${input.packageId}: its steps are not the single ${routeStepPrefix} operation bound to the ${input.route} route.`);
739
- if (projection.status === "approved" && projection.approvals.plan === "approved" && projection.approvals.environment === "approved")
740
- return;
741
- if (projection.status === "completed")
742
- throw new Error(`Ticket 08 package ${input.packageId} is already terminal completed; replay is rejected.`);
743
- if (projection.status === "uncertain")
744
- throw new Error(`Ticket 08 package ${input.packageId} is uncertain; reconcile by authoritative readback before any replay.`);
745
- if (projection.status === "in_progress")
746
- throw new Error(`Ticket 08 package ${input.packageId} is already running; concurrent execution is rejected.`);
747
- const pending = [...projection.blockers];
748
- if (projection.approvals.plan !== "approved")
749
- pending.push("Delivery Plan Approval is required before package execution.");
750
- if (projection.approvals.environment !== "approved")
751
- pending.push("Environment Change Approval is required before mutation.");
752
- throw new Error(`Ticket 08 execution boundary rejected package ${input.packageId}: status is ${projection.status} (blocked); ${[...new Set(pending)].join("; ")}.`);
753
- }
754
- /** Executes the approved one-step create package through the single OMC Host write transport, then validates behavior. */
755
- export async function executeTicket08TestMutationPackage(input) {
756
- assertTicket08TestMutationEnvironment(input.settings);
757
- if (typeof input.transport.serializedServerModelCreate !== "function")
758
- throw new Error("Ticket 08 execution requires the OMC Host serialized model-create transport; bypass transports are rejected.");
759
- const projection = input.ledger.project(input.prepared.package.packageId);
760
- assertTicket08ExecutionBoundary({ projection, environmentName: input.settings.environmentName, packageId: input.prepared.package.packageId, route: TICKET08_TEST_MUTATION_ROUTE, steps: input.prepared.package.steps });
761
- if (input.environmentApproval.packageId !== input.prepared.package.packageId || input.environmentApproval.packageHash !== input.prepared.package.packageHash)
762
- throw new Error("Ticket 08 Environment Change Approval does not match the prepared package.");
763
- const probe = await input.readModelEvidence();
764
- if (probe.classification !== "absent" || probe.reason !== "exact-absence")
765
- throw new Error(`Ticket 08 execution requires exact target absence; ${input.prepared.package.steps[0].target} is already present or blocked.`);
766
- const result = await runNativeConfigurationPackage({
767
- package: input.prepared.package,
768
- gate: {
769
- mode: "approved",
770
- planApproval: { status: "approved", approvalHash: input.prepared.planApproval.approvalHash },
771
- environmentApproval: { status: "approved", packageHash: input.environmentApproval.packageHash },
772
- lease: { owner: "configuration-agent", environment: TICKET08_TEST_ENVIRONMENT_NAME },
773
- baselineFingerprint: input.prepared.package.binding.baselineFingerprint,
774
- snapshots: input.prepared.package.snapshotManifest,
775
- package: input.prepared.package,
776
- },
777
- operations: input.prepared.operations,
778
- ledger: input.ledger,
779
- planApproval: input.prepared.planApproval,
780
- transport: input.transport,
781
- credentials: input.credentials,
782
- authoritativeOrigin: input.authoritativeOrigin,
783
- testApplicationCode: TICKET08_TEST_APPLICATION_CODE,
784
- }, input.signal);
785
- const mutationExecuted = result.steps.some((step) => step.status === "committed" || step.status === "uncertain");
786
- const blockers = [...result.blockers];
787
- let status = ticket08ExecutionStatus(result.status);
788
- let nextAction = result.nextAction;
789
- let behavior;
790
- if (result.status === "completed") {
791
- behavior = validateTicket08ModelBehavior({ evidence: input.prepared.evidence, designTime: await input.readDesignTimeEvidence(), modelEvidence: await input.readModelEvidence() });
792
- if (behavior.status !== "validated") {
793
- status = "blocked";
794
- blockers.push(behavior.reason ?? "Ticket 08 behavior validation failed.");
795
- nextAction = "reconcile-behavior-retain-and-report-resource";
796
- }
797
- }
798
- return { reportType: "omc.ticket08.test-mutation-execution", schemaVersion: 1, packageId: result.packageId, packageHash: result.packageHash, route: TICKET08_TEST_MUTATION_ROUTE, testRunId: input.prepared.evidence.testRunId, targetSchemaCode: input.prepared.evidence.targetSchemaCode, status, mutationExecuted, steps: result.steps, blockers, nextAction, ...(behavior ? { behavior } : {}) };
799
- }
800
- /** Executes the separately approved owned cleanup with exact absence readback; uncertainty stops and retains. */
801
- export async function executeTicket08CleanupPackage(input) {
802
- assertTicket08TestMutationEnvironment(input.settings);
803
- if (typeof input.transport.physicalDeleteModel !== "function")
804
- throw new Error("Ticket 08 cleanup requires the OMC Host physical model delete transport; bypass transports are rejected.");
805
- const projection = input.ledger.project(input.prepared.package.packageId);
806
- assertTicket08ExecutionBoundary({ projection, environmentName: input.settings.environmentName, packageId: input.prepared.package.packageId, route: TICKET08_TEST_MODEL_CLEANUP_ROUTE, steps: input.prepared.package.steps });
807
- if (input.environmentApproval.packageId !== input.prepared.package.packageId || input.environmentApproval.packageHash !== input.prepared.package.packageHash)
808
- throw new Error("Ticket 08 cleanup Environment Change Approval does not match the prepared package.");
809
- const probe = await input.readModelEvidence();
810
- if (probe.classification === "absent") {
811
- return { reportType: "omc.ticket08.test-mutation-execution", schemaVersion: 1, packageId: input.prepared.package.packageId, packageHash: input.prepared.package.packageHash, route: TICKET08_TEST_MODEL_CLEANUP_ROUTE, testRunId: input.evidence.testRunId, targetSchemaCode: input.evidence.targetSchemaCode, status: "blocked", mutationExecuted: false, steps: projection.steps, blockers: ["Ticket 08 cleanup target is already absent; nothing owned remains to delete and no unbounded deletion is attempted."], nextAction: "accept", absenceReadback: probe };
812
- }
813
- const result = await runNativeConfigurationPackage({
814
- package: input.prepared.package,
815
- gate: {
816
- mode: "approved",
817
- planApproval: { status: "approved", approvalHash: input.prepared.planApproval.approvalHash },
818
- environmentApproval: { status: "approved", packageHash: input.environmentApproval.packageHash },
819
- lease: { owner: "configuration-agent", environment: TICKET08_TEST_ENVIRONMENT_NAME },
820
- baselineFingerprint: input.prepared.package.binding.baselineFingerprint,
821
- snapshots: input.prepared.package.snapshotManifest,
822
- package: input.prepared.package,
823
- },
824
- operations: input.prepared.operations,
825
- ledger: input.ledger,
826
- planApproval: input.prepared.planApproval,
827
- transport: input.transport,
828
- credentials: input.credentials,
829
- authoritativeOrigin: input.authoritativeOrigin,
830
- testApplicationCode: TICKET08_TEST_APPLICATION_CODE,
831
- }, input.signal);
832
- const mutationExecuted = result.steps.some((step) => step.status === "committed" || step.status === "uncertain");
833
- const blockers = [...result.blockers];
834
- let status = ticket08ExecutionStatus(result.status);
835
- let nextAction = result.nextAction;
836
- let absenceReadback;
837
- if (result.status === "completed") {
838
- absenceReadback = await input.readModelEvidence();
839
- if (absenceReadback.classification !== "absent" || absenceReadback.reason !== "exact-absence") {
840
- status = "uncertain";
841
- blockers.push("Ticket 08 post-cleanup exact absence readback did not confirm absence; the fixture is retained and reported.");
842
- nextAction = "reconcile";
843
- }
844
- }
845
- return { reportType: "omc.ticket08.test-mutation-execution", schemaVersion: 1, packageId: result.packageId, packageHash: result.packageHash, route: TICKET08_TEST_MODEL_CLEANUP_ROUTE, testRunId: input.evidence.testRunId, targetSchemaCode: input.evidence.targetSchemaCode, status, mutationExecuted, steps: result.steps, blockers, nextAction, ...(absenceReadback ? { absenceReadback } : {}) };
846
- }
847
- function ticket08ExecutionStatus(status) {
848
- if (status === "completed" || status === "uncertain" || status === "partial")
849
- return status;
850
- return "blocked";
851
- }
852
- function requireTicket08Approval(approval, route, capabilityId) {
853
- const value = approval;
854
- if (value.route !== route || value.capabilityId !== capabilityId || value.environmentName !== "test" || value.applicationCode !== "test" || value.targets?.length !== 1 || !value.folderId || !value.folderName || !value.testRunId || !value.targetSchemaCode)
855
- throw new Error(`Ticket 08 approval frame is not bound to the exact ${route} route, target, folder, and testRunId.`);
856
- return value;
857
- }
858
- function requirePersistedApprovedPlan(ledger, approval) {
859
- if (approval.status !== "approved")
860
- throw new Error("Ticket 08 package preparation requires the approved exact Ticket 08 Delivery Plan Approval.");
861
- const persisted = ledger.findPlanApproval(approval.approvalId);
862
- if (!persisted || persisted.status !== "approved" || persisted.approvalHash !== approval.approvalHash)
863
- throw new Error("Ticket 08 package preparation requires the exact authoritative approved Delivery Plan Approval.");
864
- return persisted;
865
- }
866
- function assertTicket08BindingMatchesEvidence(binding, evidence) {
867
- const expected = {
868
- deliveryId: ticket08DeliveryId(evidence.testRunId),
869
- environment: TICKET08_TEST_ENVIRONMENT_NAME,
870
- baselineFingerprint: evidence.baselineFingerprint,
871
- environmentFingerprint: evidence.environmentFingerprint,
872
- discoveryFingerprint: evidence.discoveryFingerprint,
873
- ...(evidence.evidenceArtifactId ? { evidenceArtifactId: evidence.evidenceArtifactId } : {}),
874
- };
875
- if (stable(binding) !== stable(expected))
876
- throw new Error("Ticket 08 plan approval binding is outside the evidence baseline, environment, discovery, or delivery binding.");
877
- }
878
- function validateTicket08Evidence(evidence) {
879
- if (!evidence || evidence.route !== TICKET08_TEST_MUTATION_ROUTE || evidence.capabilityId !== TICKET08_TEST_MODEL_CREATE_CAPABILITY_ID)
880
- throw new Error("Ticket 08 evidence is not the host-owned test mutation route.");
881
- if (evidence.environmentName !== "test" || evidence.applicationCode !== "test")
882
- throw new Error("Ticket 08 evidence is outside the named test environment and dedicated test application.");
883
- assertTicket08TargetSchema(evidence.targetSchemaCode);
884
- if ([evidence.environmentFingerprint, evidence.baselineFingerprint, evidence.discoveryFingerprint].some((value) => !SHA256.test(value)))
885
- throw new Error("Ticket 08 evidence fingerprints are not canonical.");
886
- if (evidence.modelEvidence.classification !== "absent" || evidence.modelEvidence.reason !== "exact-absence" || evidence.modelEvidence.query.schemaCode !== evidence.targetSchemaCode)
887
- throw new Error("Ticket 08 evidence does not carry exact authenticated absence.");
888
- if (!evidence.folder.id || !evidence.folder.name)
889
- throw new Error("Ticket 08 evidence folder binding is missing.");
890
- return evidence;
891
- }
892
- function assertDigest(value, label) {
893
- if (typeof value !== "string" || !SHA256.test(value) || !/[1-9a-f]/i.test(value))
894
- throw new Error(`Ticket 08 ${label} is invalid.`);
895
- }
896
- function digest(value) { return createHash("sha256").update(stable(value)).digest("hex"); }
897
- function stable(value) { return JSON.stringify(sort(value)); }
898
- function sort(value) { if (Array.isArray(value))
899
- return value.map(sort); if (value && typeof value === "object")
900
- return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => [key, sort(item)])); return value; }
901
- //# sourceMappingURL=ticket08-test-model-mutation.js.map