omc-core 0.4.36 → 0.4.38

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,886 +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_FIELD_PUBLISH } from "./cloudpivot-low-code.js";
5
- import { 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
- import { activeFieldFamily, fieldFamilyMemberNamesProse } from "./families/registry.js";
13
- import { isTicket08TargetSchema } from "./ticket08-test-model-mutation.js";
14
- import { isTicket09TargetField, ticket09TargetFieldCodes, TICKET09_FIELD_FAMILY, TICKET09_FIELD_MUTATION_ROUTE } from "./ticket09-test-field-mutation.js";
15
- /**
16
- * Ticket 10 (usable-model ladder, rung 2) — AMENDED IN PLACE (Ticket 19,
17
- * numeric-logical family rung): the model-scoped publish now publishes the
18
- * run's own registered draft fixture family fields in ONE
19
- * model-level publication (the SchemaPublishedVO wire payload is
20
- * model-scoped and unchanged); the preflight, readback gates, and behavior
21
- * checks are per family field. Cleanup probes force_delete per owned
22
- * published family field. The route and capability ids are unchanged. The composite chain runs the executable
23
- * Ticket 08 model-create and Ticket 09 field-create capabilities first; the
24
- * publish package consumes the completed, behavior-validated field-create
25
- * execution report plus a fresh authoritative two-state readback (draft
26
- * present, published absent) and is itself an independently immutable,
27
- * independently dual-approved package. No evidence is inferred from the
28
- * field-create capability; form draft/publish, app publish, Environment
29
- * release, production, staging, tenant administration, Redis, restart,
30
- * deployment, and runtime CRUD stay rejected.
31
- *
32
- * Authoritative 8.6.26 contract (decompiled engine + live probe ledger):
33
- * - publish is POST /api/api/app/bizproperty/publish with the exact
34
- * `SchemaPublishedVO` DTO {bizSchemaCode, sendMessage:false}, errcode=0,
35
- * guarded by @RequireAdmin (BizPropertyController.java:1130-1167,
36
- * SchemaPublishedVO.java:9-31); the controller calls publishBizSchema and
37
- * logs EDIT_AND_PUBLISH_DATA_MODEL — a model-level data-item publication,
38
- * never an app publish, form publish, or Environment release;
39
- * - after publication the field is visible in BOTH the isPublish=false and
40
- * isPublish=true readback states (probe ledger); "published" means the
41
- * runtime-visible state, not a flag;
42
- * - the ledger warns "字段发布后表单可能仍未更新": model, field, and form are
43
- * not one transaction, so form consistency lag is recorded as an explicit
44
- * partial state rather than assumed cascade consistency;
45
- * - cleanup is stated explicitly per the spec: the owned published field is
46
- * the force_delete probe (the decompiled forceDelete passes the published
47
- * flag to removeBizPropertyByCode, BizPropertyController.java:1532-1565).
48
- * If the server accepts it, cleanup stays staged (field force_delete +
49
- * two-state exact absence readback, then the model physical delete). If
50
- * published-field deletion semantics differ, this rung's cleanup is the
51
- * model physical delete cascade plus field-level two-state absence
52
- * confirmation — written down, never silently assumed.
53
- */
54
- export const TICKET10_FIELD_PUBLISH_ROUTE = "ticket10-test-field-publish";
55
- export const TICKET10_FIELD_CLEANUP_ROUTE = "ticket10-test-field-cleanup";
56
- export const TICKET10_FIELD_PUBLISH_CAPABILITY_ID = "cloudpivot.test.field-publish-8.6.26";
57
- export const TICKET10_FIELD_CLEANUP_CAPABILITY_ID = "cloudpivot.test.field-publish-cleanup-8.6.26";
58
- export const TICKET10_FIELD_PUBLISH_KNOWLEDGE_PACK_ID = "cloudpivot-8.6-ticket10-test-field-publish";
59
- export const TICKET10_TEST_ENVIRONMENT_NAME = "test";
60
- export const TICKET10_TEST_APPLICATION_CODE = "test";
61
- export const TICKET10_REFERENCE_EVIDENCE_VERSION = "8.6.26";
62
- export const TICKET10_FIELD_PUBLISH_ENDPOINT = "/api/api/app/bizproperty/publish";
63
- export const TICKET10_FIELD_READBACK_ENDPOINT = "/api/api/app/bizproperty/list";
64
- export const TICKET10_FIELD_CLEANUP_ENDPOINT = "/api/api/app/bizproperty/force_delete";
65
- export const TICKET10_PROHIBITED_MUTATION_TERMS = Object.freeze([
66
- "app-publish", "form", "deploy", "restart", "release", "redis", "tenant-admin", "runtime-crud",
67
- "model:", "view", "folder-create", "folder-delete",
68
- ]);
69
- const TICKET10_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;
70
- const SHA256 = /^[a-f0-9]{64}$/i;
71
- /** The named `test` environment and its dedicated test application are hard preconditions. */
72
- export function assertTicket10TestFieldPublishEnvironment(settings) {
73
- if (settings.environmentName !== "test")
74
- throw new Error("Ticket 10 field publish is restricted to the named test environment; production and staging are rejected.");
75
- if (!settings.baseUrl.trim() || !/^https?:\/\/\S+$/i.test(settings.baseUrl.trim()))
76
- throw new Error("Ticket 10 field publish requires an absolute configured test environment endpoint.");
77
- if (settings.testApplicationCode !== "test")
78
- throw new Error("Ticket 10 field publish requires the dedicated test application to be test.");
79
- }
80
- export function ticket10DeliveryId(testRunId) { return `ticket10-field-publish-${testRunId}`; }
81
- export function ticket10FieldCleanupDeliveryId(testRunId) { return `ticket10-field-cleanup-${testRunId}`; }
82
- export function ticket10FieldPublishScope(targetSchemaCode, familyFieldCodesKey) { return `cloudpivot:fields:${targetSchemaCode}/${familyFieldCodesKey}`; }
83
- export function ticket10FieldCleanupScope(targetSchemaCode, targetFieldCode) { return `cloudpivot:field:${targetSchemaCode}/${targetFieldCode}`; }
84
- /** Design-time designer direct URL; the fixture stays inspectable here regardless of publication state. */
85
- export function ticket10DesignerDirectUrl(targetSchemaCode) { return `/admin/#/apps/model/${TICKET10_TEST_APPLICATION_CODE}/${targetSchemaCode}/`; }
86
- export function buildTicket10FieldCleanupContract(testRunId, targetSchemaCode, targetFieldCodes) {
87
- if (!TICKET10_UUID_V4.test(testRunId) || !isTicket08TargetSchema(targetSchemaCode) || targetFieldCodes.length !== TICKET09_FIELD_FAMILY.length || targetFieldCodes.some((code) => !isTicket09TargetField(code)) || new Set(targetFieldCodes).size !== TICKET09_FIELD_FAMILY.length)
88
- throw new Error("Ticket 10 field cleanup contract requires the exact host-owned testRunId, fixture schema, and one distinct family field code per active family member.");
89
- return Object.freeze({
90
- capabilityId: TICKET10_FIELD_CLEANUP_CAPABILITY_ID,
91
- testRunId,
92
- targetSchemaCode,
93
- targetFieldCodes,
94
- probe: { method: "POST", endpoint: TICKET10_FIELD_CLEANUP_ENDPOINT, payload: "query:bizSchemaCode+bizPropertyCode-per-family-field", evidence: "decompiled forceDelete passes the published flag to removeBizPropertyByCode (BizPropertyController.java:1532-1565); each owned published family field is the probe, disambiguated by authoritative two-state readback" },
95
- postDeleteAbsenceReadback: { method: "GET", endpoint: TICKET10_FIELD_READBACK_ENDPOINT, expected: "two-state-exact-absence" },
96
- variants: {
97
- forceDeleteSupported: "published-field-force-delete: field force_delete plus two-state exact absence readback, then the separately approved model physical delete with model absence readback",
98
- forceDeleteRejected: "model-cascade-plus-field-absence-confirmation: stop the field-level delete (retain, no retry); the separately approved model physical delete cascades, then field-level two-state exact absence confirmation closes the contract",
99
- },
100
- ownership: "field-code-schema-and-published-state-bound-to-testRunId",
101
- replayPolicy: "absent-is-no-op",
102
- uncertainPolicy: "stop-retain-report-no-retry",
103
- });
104
- }
105
- /** Deterministic, run-independent contract identities pinned by the Knowledge Pack promotion. */
106
- export function ticket10FieldPublishContractHash() { return digest(ticket10FieldPublishContract()); }
107
- export function ticket10FieldCleanupContractHash() { return digest(ticket10FieldCleanupContractTemplate()); }
108
- function ticket10FieldPublishContract() {
109
- return {
110
- capabilityId: TICKET10_FIELD_PUBLISH_CAPABILITY_ID,
111
- operationKind: "field-publish",
112
- method: "POST",
113
- endpoint: CLOUDPIVOT_8_6_26_SERIALIZED_FIELD_PUBLISH.endpoint,
114
- readbackEndpoint: CLOUDPIVOT_8_6_26_SERIALIZED_FIELD_PUBLISH.readbackEndpoint,
115
- preflight: CLOUDPIVOT_8_6_26_SERIALIZED_FIELD_PUBLISH.preflight,
116
- dto: "SchemaPublishedVO:bizSchemaCode,sendMessage",
117
- sendMessage: false,
118
- scope: "model-scoped-field-publish",
119
- permission: "@RequireAdmin",
120
- readbackStates: ["isPublish=false:present", "isPublish=true:present"],
121
- publishedFlagDecode: "published===true-in-published-state",
122
- auditSurface: "server-side operation log LogOperationActionType.EDIT_AND_PUBLISH_DATA_MODEL recorded by the publish endpoint (BizPropertyController.java:1156-1162); no separate audit read API is evidenced",
123
- formConsistency: "model/field/form-are-not-one-transaction;form-lag-recorded-as-explicit-partial-state",
124
- transportMode: "serialized-server-field-publish-8.6.26",
125
- capability: CLOUDPIVOT_8_6_26_SERIALIZED_FIELD_PUBLISH,
126
- evidence: [...CLOUDPIVOT_8_6_26_SERIALIZED_FIELD_PUBLISH.evidence],
127
- requiredPermissions: ["configuration-write"],
128
- prohibited: [...TICKET10_PROHIBITED_MUTATION_TERMS],
129
- };
130
- }
131
- function ticket10FieldCleanupContractTemplate() {
132
- return {
133
- capabilityId: TICKET10_FIELD_CLEANUP_CAPABILITY_ID,
134
- operationKind: "owned-published-field-force-delete-probe",
135
- method: "POST",
136
- endpoint: TICKET10_FIELD_CLEANUP_ENDPOINT,
137
- payload: "query:bizSchemaCode+bizPropertyCode-per-family-field",
138
- postDeleteAbsenceReadback: `${TICKET10_FIELD_READBACK_ENDPOINT}?isPublish=false+isPublish=true:two-state-exact-absence-per-family-field`,
139
- variants: ["published-field-force-delete", "model-cascade-plus-field-absence-confirmation"],
140
- ownership: "field-code-schema-and-published-state-bound-to-testRunId",
141
- replayPolicy: "absent-is-no-op",
142
- uncertainPolicy: "stop-retain-report-no-retry",
143
- };
144
- }
145
- /** Publish packages are exactly ONE model-scoped publication binding both family field codes; cleanup packages are exactly the two per-field published-field force-delete probes. */
146
- export function assertTicket10OperationsBounded(input) {
147
- const familyKey = input.targetFieldCodes.join("+");
148
- const expectedCount = input.route === TICKET10_FIELD_PUBLISH_ROUTE ? 1 : TICKET09_FIELD_FAMILY.length;
149
- if (input.operations.length !== expectedCount)
150
- throw new Error(`Ticket 10 ${input.route} packages must contain exactly ${expectedCount} bounded operation(s).`);
151
- input.operations.forEach((operation, index) => {
152
- const identity = `${operation.id} ${operation.target} ${operation.path}`.toLowerCase();
153
- const expectedPrefix = input.route === TICKET10_FIELD_PUBLISH_ROUTE ? "field-publish:" : "cleanup:published-field:";
154
- if (TICKET10_PROHIBITED_MUTATION_TERMS.some((term) => identity.includes(term)) || !operation.id.startsWith(expectedPrefix))
155
- throw new Error(`Ticket 10 operation ${operation.id} is outside the bounded operation set; prohibited families (app publish, form writes, deploy, restart, release, redis, tenant administration, runtime CRUD, model/view/folder writes) remain rejected.`);
156
- const expected = input.route === TICKET10_FIELD_PUBLISH_ROUTE
157
- ? { id: `field-publish:${input.targetSchemaCode}/${familyKey}`, target: `fields:${input.targetSchemaCode}/${familyKey}`, path: TICKET10_FIELD_PUBLISH_ENDPOINT, transportMode: "serialized-server-field-publish-8.6.26" }
158
- : { id: `cleanup:published-field:${input.targetSchemaCode}/${input.targetFieldCodes[index]}`, target: `field:${input.targetSchemaCode}/${input.targetFieldCodes[index]}`, path: TICKET10_FIELD_CLEANUP_ENDPOINT, transportMode: "cloudpivot-published-field-force-delete-8.6.26" };
159
- if (operation.id !== expected.id || operation.path !== expected.path || operation.transportMode !== expected.transportMode || operation.target !== expected.target)
160
- throw new Error(`Ticket 10 operation ${operation.id} does not match the exact target binding for ${expected.target}.`);
161
- });
162
- }
163
- /** Builds the signed, version-bound pack promoting exactly the single test field-publish capability. */
164
- export function buildTicket10FieldPublishKnowledgePack(input) {
165
- const compiler = { id: "src/ticket10-test-field-publish.ts:createTicket10FieldPublishPlanApproval", sha256: digest("omc-ticket10-compiler-v1") };
166
- const decoder = { id: "src/dsh-host-plugin.ts:createCloudPivotWriteTransport", sha256: digest("omc-ticket10-decoder-v1") };
167
- return buildKnowledgePack({
168
- packId: TICKET10_FIELD_PUBLISH_KNOWLEDGE_PACK_ID,
169
- packVersion: "1.0.0",
170
- sourceVersion: TICKET10_REFERENCE_EVIDENCE_VERSION,
171
- sourceArtifacts: [{ id: "cloudpivot-8.6.26-serialized-field-publish-capability", sha256: digest(CLOUDPIVOT_8_6_26_SERIALIZED_FIELD_PUBLISH) }],
172
- references: [
173
- { id: "yunshu-probe-ledger-field-publish-two-state-readback", sha256: digest("yunshu-probe-ledger-field-publish") },
174
- { id: TICKET10_FIELD_PUBLISH_ENDPOINT, sha256: digest(TICKET10_FIELD_PUBLISH_ENDPOINT) },
175
- { id: TICKET10_FIELD_READBACK_ENDPOINT, sha256: digest(TICKET10_FIELD_READBACK_ENDPOINT) },
176
- { id: TICKET10_FIELD_CLEANUP_ENDPOINT, sha256: digest(TICKET10_FIELD_CLEANUP_ENDPOINT) },
177
- ],
178
- compilers: [compiler],
179
- decoders: [decoder],
180
- capabilities: [{
181
- id: TICKET10_FIELD_PUBLISH_CAPABILITY_ID,
182
- status: "runtime-verified",
183
- promote: true,
184
- compilerRef: compiler.id,
185
- decoderRef: decoder.id,
186
- sourceEvidence: [...CLOUDPIVOT_8_6_26_SERIALIZED_FIELD_PUBLISH.evidence, TICKET10_FIELD_PUBLISH_ENDPOINT, TICKET10_FIELD_READBACK_ENDPOINT, "BizPropertyController.java:1130-1167", "SchemaPublishedVO.java:9-31", "BizPropertyController.java:1532-1565"],
187
- runtimeEvidence: ["yunshu-probe-ledger-field-publish-two-state-readback", "cloudpivot.field-publish.serialized-server-8.6.26"],
188
- operationPromotions: [{
189
- id: "field-publish",
190
- status: "promoted",
191
- operationContractHash: ticket10FieldPublishContractHash(),
192
- sourceRefs: [...CLOUDPIVOT_8_6_26_SERIALIZED_FIELD_PUBLISH.evidence, TICKET10_FIELD_PUBLISH_ENDPOINT, TICKET10_FIELD_READBACK_ENDPOINT, TICKET10_FIELD_CLEANUP_ENDPOINT],
193
- versionFamily: "8.6",
194
- referenceVersion: TICKET10_REFERENCE_EVIDENCE_VERSION,
195
- requiredEvidence: ["cleanup", "discovery", "permission", "readback", "test", "write"],
196
- evidence: ["cleanup", "discovery", "permission", "readback", "test", "write"],
197
- blockedReasons: [],
198
- }],
199
- }],
200
- signerKeyId: input.signerKeyId,
201
- signingKey: input.signingKey,
202
- createdAt: input.createdAt,
203
- });
204
- }
205
- /** Loads the pack against CloudPivot 8.6.26 and pins the single executable capability and contract hash. */
206
- export function loadTicket10KnowledgePack(artifact, trustedKeys) {
207
- const runtime = new KnowledgePackRuntime(trustedKeys);
208
- const load = runtime.load(artifact, TICKET10_REFERENCE_EVIDENCE_VERSION);
209
- if (!load.accepted)
210
- throw new Error(`Ticket 10 Knowledge Pack is not authoritative: ${load.blockingReasons.join(", ")}.`);
211
- const active = runtime.active();
212
- const capabilities = active.capabilities;
213
- if (capabilities.length !== 1 || capabilities[0].id !== TICKET10_FIELD_PUBLISH_CAPABILITY_ID)
214
- throw new Error("Ticket 10 Knowledge Pack must promote exactly the single test field-publish capability; evidence inference from field-create or to any other capability is rejected.");
215
- const hashes = capabilities[0].operationContractHashes;
216
- if (hashes.length !== 1 || hashes[0] !== ticket10FieldPublishContractHash())
217
- throw new Error("Ticket 10 Knowledge Pack operation contract hash does not match the canonical field-publish contract.");
218
- return { runtime, packId: active.manifest.packId, contentDigest: active.manifest.contentSha256, executableCapabilities: capabilities.map(({ id }) => id) };
219
- }
220
- /** The "+''-joined family field-code key carried by the publish operation id and scope. */
221
- export function ticket10FamilyFieldKey(evidence) { return evidence.targetFieldCodes.join("+"); }
222
- /** Host-owned composite-chain evidence capture: consumes the completed family field-create handoff and proves BOTH draft family fields are present and still unpublished. */
223
- export function captureTicket10FieldPublishEvidence(input) {
224
- assertTicket10TestFieldPublishEnvironment(input.settings);
225
- if (!TICKET10_UUID_V4.test(input.testRunId))
226
- throw new Error("Ticket 10 evidence capture requires the run's host-owned UUID v4 testRunId.");
227
- const handoff = input.handoff;
228
- const report = handoff.fieldCreateExecutionReport;
229
- const fieldCreateEvidence = handoff.fieldCreateEvidence;
230
- if (!handoff.fieldCreatePackageId || !SHA256.test(handoff.fieldCreatePackageHash) || !/[1-9a-f]/i.test(handoff.fieldCreatePackageHash) || report.route !== TICKET09_FIELD_MUTATION_ROUTE || report.status !== "completed" || report.mutationExecuted !== true || report.behavior?.status !== "validated")
231
- throw new Error("Ticket 10 field publish requires the completed, behavior-validated field-create handoff; serial writes never assume state.");
232
- if (report.testRunId !== input.testRunId || fieldCreateEvidence.testRunId !== input.testRunId)
233
- throw new Error("Ticket 10 composite chain requires the single run testRunId across the model, field, and publish packages.");
234
- const targetSchemaCode = fieldCreateEvidence.targetSchemaCode;
235
- const targetFieldCodes = ticket09TargetFieldCodes(fieldCreateEvidence);
236
- if (!isTicket08TargetSchema(targetSchemaCode) || report.targetSchemaCode !== targetSchemaCode || report.targetFields.length !== targetFieldCodes.length || report.targetFields.some((field, index) => field.targetFieldCode !== targetFieldCodes[index]))
237
- throw new Error("Ticket 10 fixture family field identity diverges from the completed field-create handoff.");
238
- const modelPresence = input.modelPresence;
239
- if (modelPresence.classification !== "present" || modelPresence.query.schemaCode !== targetSchemaCode)
240
- throw new Error("Ticket 10 field publish requires the fixture model to be present in fresh authoritative readback.");
241
- targetFieldCodes.forEach((targetFieldCode, index) => {
242
- const member = TICKET09_FIELD_FAMILY[index];
243
- const draftPresence = input.draftPresence[index];
244
- if (draftPresence.classification !== "present" || draftPresence.reason !== "exact-presence" || draftPresence.query.schemaCode !== targetSchemaCode || draftPresence.query.propertyCode !== targetFieldCode || draftPresence.query.isPublish !== "false")
245
- throw new Error(`Ticket 10 evidence capture requires exact authenticated draft presence for the ${member.propertyTypeName} handoff field.`);
246
- if (!draftPresence.field || draftPresence.field.code !== targetFieldCode || draftPresence.field.schemaCode !== targetSchemaCode || draftPresence.field.published === true)
247
- throw new Error(`Ticket 10 evidence capture requires the ${member.propertyTypeName} field to still be an unpublished draft before publication.`);
248
- const publishedAbsence = input.publishedAbsence[index];
249
- if (publishedAbsence.classification !== "absent" || publishedAbsence.reason !== "exact-absence" || publishedAbsence.query.schemaCode !== targetSchemaCode || publishedAbsence.query.propertyCode !== targetFieldCode || publishedAbsence.query.isPublish !== "true")
250
- throw new Error(`Ticket 10 evidence capture requires exact authenticated published-state absence for the ${member.propertyTypeName} handoff field; duplicate publication is rejected.`);
251
- });
252
- const authoritativeOrigin = input.discovery.authoritativeOrigin;
253
- const enterpriseId = input.discovery.enterpriseId;
254
- if (!authoritativeOrigin || !enterpriseId)
255
- throw new Error("Ticket 10 evidence capture requires authenticated environment fingerprint evidence.");
256
- const environmentFingerprint = canonicalCloudPivotEnvironmentFingerprint({ environment: TICKET10_TEST_ENVIRONMENT_NAME, authoritativeOrigin, applicationCode: TICKET10_TEST_APPLICATION_CODE, enterpriseId });
257
- if (environmentFingerprint !== fieldCreateEvidence.environmentFingerprint)
258
- throw new Error("Ticket 10 field publish is outside the field-create handoff environment fingerprint.");
259
- const evidence = Object.freeze({
260
- route: TICKET10_FIELD_PUBLISH_ROUTE,
261
- capabilityId: TICKET10_FIELD_PUBLISH_CAPABILITY_ID,
262
- environmentName: TICKET10_TEST_ENVIRONMENT_NAME,
263
- applicationCode: TICKET10_TEST_APPLICATION_CODE,
264
- folder: fieldCreateEvidence.folder,
265
- targetSchemaCode,
266
- targetFieldCodes,
267
- testRunId: input.testRunId,
268
- environmentFingerprint,
269
- baselineFingerprint: canonicalCloudPivotBaselineFingerprint({ environmentFingerprint, applicationCode: TICKET10_TEST_APPLICATION_CODE, discovery: input.discovery }),
270
- discoveryFingerprint: canonicalFormalDiscoveryFingerprint(input.discovery),
271
- handoff,
272
- modelPresence,
273
- draftPresence: Object.freeze([...input.draftPresence]),
274
- publishedAbsence: Object.freeze([...input.publishedAbsence]),
275
- cleanupContract: buildTicket10FieldCleanupContract(input.testRunId, targetSchemaCode, targetFieldCodes),
276
- designerDirectUrl: ticket10DesignerDirectUrl(targetSchemaCode),
277
- capturedAt: new Date((input.now ?? Date.now)()).toISOString(),
278
- });
279
- if ([evidence.environmentFingerprint, evidence.baselineFingerprint, evidence.discoveryFingerprint].some((value) => !SHA256.test(value)))
280
- throw new Error("Ticket 10 evidence fingerprints are not canonical.");
281
- if (SENSITIVE_SECRET_VALUE.test(stable(evidence)))
282
- throw new Error("Ticket 10 evidence contains prohibited sensitive data.");
283
- return evidence;
284
- }
285
- export const TICKET10_EVIDENCE_DEFAULT_TTL_MS = 25 * 60_000;
286
- export const TICKET10_EVIDENCE_MAX_TTL_MS = 30 * 60_000;
287
- /** Single-use, TTL-bounded evidence store; consumed exactly once by plan creation. */
288
- export class Ticket10EvidenceStore {
289
- records = new Map();
290
- now;
291
- ttlMs;
292
- lastNow;
293
- constructor(options = {}) {
294
- this.now = options.now ?? Date.now;
295
- this.ttlMs = options.ttlMs ?? TICKET10_EVIDENCE_DEFAULT_TTL_MS;
296
- if (!Number.isFinite(this.ttlMs) || this.ttlMs < 1 || this.ttlMs > TICKET10_EVIDENCE_MAX_TTL_MS)
297
- throw new Error("Ticket 10 evidence TTL must be between 1ms and its safety maximum.");
298
- }
299
- capture(evidence) {
300
- validateTicket10Evidence(evidence);
301
- const evidenceId = `ticket10-evidence-${randomUUID()}`;
302
- const expiresAt = this.readClock() + this.ttlMs;
303
- const stored = Object.freeze({ ...evidence, evidenceArtifactId: evidenceId });
304
- this.records.set(evidenceId, { evidence: stored, expiresAt, consumed: false });
305
- return { evidenceId, expiresAt, evidence: stored };
306
- }
307
- consume(evidenceId) {
308
- const record = this.records.get(evidenceId);
309
- if (!record)
310
- throw new Error("Ticket 10 evidence artifact is missing or divergent.");
311
- if (record.consumed)
312
- throw new Error("Ticket 10 evidence has already been consumed.");
313
- if (record.expiresAt <= this.readClock())
314
- throw new Error("Ticket 10 evidence is stale or expired.");
315
- record.consumed = true;
316
- return record.evidence;
317
- }
318
- resolve(evidenceId) {
319
- const record = this.records.get(evidenceId);
320
- if (!record || !record.consumed)
321
- throw new Error("Ticket 10 evidence artifact is missing or has not been consumed by planning.");
322
- if (record.expiresAt <= this.readClock())
323
- throw new Error("Ticket 10 evidence artifact is stale or expired.");
324
- return record.evidence;
325
- }
326
- readClock() {
327
- const currentTime = this.now();
328
- if (!Number.isFinite(currentTime))
329
- throw new Error("Ticket 10 evidence clock is invalid; fail closed.");
330
- if (this.lastNow !== undefined && currentTime < this.lastNow)
331
- throw new Error("Ticket 10 evidence clock moved backwards; fail closed.");
332
- this.lastNow = currentTime;
333
- return currentTime;
334
- }
335
- }
336
- /** The exact evidenced SchemaPublishedVO publish DTO: {bizSchemaCode, sendMessage:false}. */
337
- export function buildTicket10FieldPublishPayload(evidence) {
338
- validateTicket10Evidence(evidence);
339
- return { bizSchemaCode: evidence.targetSchemaCode, sendMessage: false };
340
- }
341
- export function buildTicket10ConfigurationIntent(evidence) {
342
- validateTicket10Evidence(evidence);
343
- const target = `${evidence.targetSchemaCode}/${evidence.targetFieldCodes.join("+")}`;
344
- return {
345
- intentVersion: 1,
346
- targetFamily: "8.6",
347
- environmentBinding: { application: evidence.applicationCode, model: evidence.targetSchemaCode, baselineFingerprint: evidence.baselineFingerprint },
348
- sourceRequirement: `Ticket 10 maintainer test (${activeFieldFamily().familyId} family rung): publish the run's ${fieldFamilyMemberNamesProse()} draft fixture family fields through the single model-scoped field publish (SchemaPublishedVO sendMessage:false), verified across the isPublish=false/true readback states per field, with per-type behavior validation and staged owned cleanup.`,
349
- resources: [{ id: target, kind: "field", state: "publish", properties: { schemaCode: evidence.targetSchemaCode, publishScope: "model-scoped-field-publish-family", familyFieldCodes: [...evidence.targetFieldCodes] } }],
350
- relationships: [],
351
- lifecycle: [{ resourceId: target, transitions: ["publish"], order: 1, idempotent: false, dependencyChecks: ["completed-field-create-handoff", "draft-presence-and-published-absence-preflight-per-family-field"], recoveryBoundary: `fields:${target}` }],
352
- behavior: [{ id: "published-family-field-two-state", kind: "structure", requiredCapabilityIds: [TICKET10_FIELD_PUBLISH_CAPABILITY_ID], resourceIds: [target], obligations: ["published (isPublish=true) readback carries BOTH family fields with the published flag decoded", "draft (isPublish=false) readback still carries both family fields", "form consistency lag is recorded as an explicit partial state; model/field/form are not one transaction", "designer direct URL stays inspectable"] }],
353
- acceptance: [{ id: "ticket10-publish-readback", scenario: "family-field-publish-two-state-readback-behavior", actor: "omc-maintainer-opencode", assertions: ["both family fields are present in the draft and published readback states after the single model-scoped publication", "the published flag decodes per family field from the authoritative published readback", "fixture remains inspectable at the designer direct URL"], readbackInvariants: [`${TICKET10_FIELD_READBACK_ENDPOINT}?schemaCode=<model>&isPublish=false lists both family fields`, `${TICKET10_FIELD_READBACK_ENDPOINT}?schemaCode=<model>&isPublish=true lists both family fields`], outcome: "validated" }],
354
- cleanup: [{ id: "ticket10-owned-family-field-cleanup", boundary: `ticket10:${evidence.testRunId}:published-family-field-force-delete-probe`, dependencyChecks: ["published family field ownership readback per field", "two-state exact absence readback per field"], resources: evidence.targetFieldCodes.map((fieldCode) => `field:${evidence.targetSchemaCode}/${fieldCode}`) }],
355
- implementationConstraints: { nativePreferred: true, backendRequired: false, prohibitedPaths: ["form.online-js", "business-rule", "integration", "backend.extension"], approvalExceptions: [] },
356
- evidenceRefs: [TICKET10_FIELD_PUBLISH_CAPABILITY_ID, TICKET10_FIELD_PUBLISH_ENDPOINT, TICKET10_FIELD_READBACK_ENDPOINT, TICKET10_FIELD_CLEANUP_ENDPOINT, ...CLOUDPIVOT_8_6_26_SERIALIZED_FIELD_PUBLISH.evidence],
357
- unresolved: [],
358
- };
359
- }
360
- function ticket10FieldPublishOperation(evidence) {
361
- const familyKey = evidence.targetFieldCodes.join("+");
362
- const target = `fields:${evidence.targetSchemaCode}/${familyKey}`;
363
- return {
364
- id: `field-publish:${evidence.targetSchemaCode}/${familyKey}`,
365
- method: "POST",
366
- path: TICKET10_FIELD_PUBLISH_ENDPOINT,
367
- target,
368
- payload: buildTicket10FieldPublishPayload(evidence),
369
- expectedReadback: `${target}:published`,
370
- evidence: [TICKET10_FIELD_PUBLISH_CAPABILITY_ID, ...CLOUDPIVOT_8_6_26_SERIALIZED_FIELD_PUBLISH.evidence, TICKET10_FIELD_READBACK_ENDPOINT],
371
- transportMode: "serialized-server-field-publish-8.6.26",
372
- capability: CLOUDPIVOT_8_6_26_SERIALIZED_FIELD_PUBLISH,
373
- };
374
- }
375
- function ticket10FieldCleanupOperations(evidence) {
376
- return evidence.targetFieldCodes.map((fieldCode) => {
377
- const target = `field:${evidence.targetSchemaCode}/${fieldCode}`;
378
- return {
379
- id: `cleanup:published-field:${evidence.targetSchemaCode}/${fieldCode}`,
380
- method: "POST",
381
- path: TICKET10_FIELD_CLEANUP_ENDPOINT,
382
- target,
383
- payload: { bizSchemaCode: evidence.targetSchemaCode, bizPropertyCode: fieldCode },
384
- expectedReadback: `${target}:deleted`,
385
- evidence: [TICKET10_FIELD_CLEANUP_CAPABILITY_ID, "BizPropertyController.java:1532-1565", TICKET10_FIELD_READBACK_ENDPOINT],
386
- transportMode: "cloudpivot-published-field-force-delete-8.6.26",
387
- };
388
- });
389
- }
390
- /** Creates and proposes the immutable Ticket 10 Delivery Plan Approval for the single model-scoped field-publish step. */
391
- export function createTicket10FieldPublishPlanApproval(input) {
392
- const evidence = validateTicket10Evidence(input.evidence);
393
- assertDigest(input.knowledgePackDigest, "knowledge pack digest");
394
- const operation = ticket10FieldPublishOperation(evidence);
395
- const operations = [operation];
396
- assertTicket10OperationsBounded({ route: TICKET10_FIELD_PUBLISH_ROUTE, operations, targetSchemaCode: evidence.targetSchemaCode, targetFieldCodes: evidence.targetFieldCodes });
397
- const intent = buildTicket10ConfigurationIntent(evidence);
398
- if (validateConfigurationIntent(intent).length)
399
- throw new Error("Ticket 10 Configuration Intent is invalid; planning is blocked.");
400
- const binding = {
401
- deliveryId: ticket10DeliveryId(evidence.testRunId),
402
- environment: TICKET10_TEST_ENVIRONMENT_NAME,
403
- baselineFingerprint: evidence.baselineFingerprint,
404
- environmentFingerprint: evidence.environmentFingerprint,
405
- discoveryFingerprint: evidence.discoveryFingerprint,
406
- ...(evidence.evidenceArtifactId ? { evidenceArtifactId: evidence.evidenceArtifactId } : {}),
407
- };
408
- const familyKey = evidence.targetFieldCodes.join("+");
409
- const planHash = digest({ route: TICKET10_FIELD_PUBLISH_ROUTE, capabilityId: TICKET10_FIELD_PUBLISH_CAPABILITY_ID, target: `${evidence.targetSchemaCode}/${familyKey}`, testRunId: evidence.testRunId, operation: { id: operation.id, readback: operation.expectedReadback }, familyFieldCodes: [...evidence.targetFieldCodes], fieldHandoff: { packageId: evidence.handoff.fieldCreatePackageId, packageHash: evidence.handoff.fieldCreatePackageHash }, folder: evidence.folder, fingerprints: { baseline: evidence.baselineFingerprint, environment: evidence.environmentFingerprint, discovery: evidence.discoveryFingerprint }, cleanupContractHash: ticket10FieldCleanupContractHash(), knowledgePackDigest: input.knowledgePackDigest, prohibited: [...TICKET10_PROHIBITED_MUTATION_TERMS] });
410
- const base = createDeliveryPlanApproval({
411
- binding,
412
- requirementHash: digest({ target: `${evidence.targetSchemaCode}/${familyKey}`, publish: true }),
413
- clarificationHash: digest("ticket10-test-field-publish-family-numeric-logical"),
414
- intentHash: digest(intent),
415
- decisionHash: digest(TICKET10_FIELD_PUBLISH_ROUTE),
416
- graphHash: digest([operation.id]),
417
- riskHash: digest(TICKET10_RISKS),
418
- validationPlanHash: digest("authoritative-two-state-family-field-publish-readback-and-form-lag-observation"),
419
- cleanupPlanHash: ticket10FieldCleanupContractHash(),
420
- allowedOperationScope: [ticket10FieldPublishScope(evidence.targetSchemaCode, familyKey)],
421
- planHash,
422
- applicationCode: TICKET10_TEST_APPLICATION_CODE,
423
- folderId: evidence.folder.id,
424
- folderName: evidence.folder.name,
425
- targets: [`${evidence.targetSchemaCode}/${familyKey}`],
426
- });
427
- const approval = {
428
- ...base,
429
- route: TICKET10_FIELD_PUBLISH_ROUTE,
430
- testRunId: evidence.testRunId,
431
- targetSchemaCode: evidence.targetSchemaCode,
432
- targetFieldCodes: evidence.targetFieldCodes,
433
- capabilityId: TICKET10_FIELD_PUBLISH_CAPABILITY_ID,
434
- knowledgePackDigest: input.knowledgePackDigest,
435
- cleanupContractHash: ticket10FieldCleanupContractHash(),
436
- environmentName: TICKET10_TEST_ENVIRONMENT_NAME,
437
- };
438
- const proposal = { ...approval, approvalHash: deliveryPlanApprovalHash(approval) };
439
- proposeDeliveryPlan(input.ledger, proposal);
440
- return { approval: proposal, operations, cleanupContract: evidence.cleanupContract, intent, binding };
441
- }
442
- const TICKET10_RISKS = [
443
- `Exactly one model-scoped field publish of the run's own draft fixture family fields (${fieldFamilyMemberNamesProse()}, one SchemaPublishedVO wire call) is in scope; this is a model-level data-item publication, never an app publish, form publish, or Environment release; model, view, runtime, folder, deployment, restart, release, Redis, and tenant administration remain rejected.`,
444
- "Owned published-family-field cleanup is separately approved and bounded to the fields published by this testRunId with the explicit two-variant contract (per-field force_delete with two-state absence, or model cascade plus per-field absence confirmation); the model fixture is physically deleted afterwards through the existing owned model cleanup.",
445
- ];
446
- /** Proves the fixture family fields were published by this run: exact codes, present in BOTH readback states per field, published flag decoded, dedicated application model. */
447
- export function validateTicket10FieldCleanupOwnership(input) {
448
- const evidence = validateTicket10Evidence(input.evidence);
449
- const fieldIds = [];
450
- evidence.targetFieldCodes.forEach((targetFieldCode, index) => {
451
- const member = TICKET09_FIELD_FAMILY[index];
452
- const published = input.published[index];
453
- if (published.classification !== "present" || published.reason !== "exact-presence" || published.query.schemaCode !== evidence.targetSchemaCode || published.query.propertyCode !== targetFieldCode || published.query.isPublish !== "true")
454
- throw new Error(`Ticket 10 field cleanup requires the published ${member.propertyTypeName} field to be present in the authoritative published readback.`);
455
- const field = published.field;
456
- if (!field || field.code !== targetFieldCode || field.schemaCode !== evidence.targetSchemaCode)
457
- throw new Error(`Ticket 10 field cleanup ownership readback returned the wrong ${member.propertyTypeName} field.`);
458
- if (field.published !== true)
459
- throw new Error("Ticket 10 field cleanup only owns fields whose published flag decodes from the authoritative published readback; unpublished draft semantics are the Ticket 09 rung.");
460
- const draft = input.draft[index];
461
- if (draft.classification !== "present" || draft.query.schemaCode !== evidence.targetSchemaCode || draft.query.propertyCode !== targetFieldCode || draft.query.isPublish !== "false")
462
- throw new Error(`Ticket 10 field cleanup requires two-state visibility: the published ${member.propertyTypeName} field must remain present in the draft readback as well.`);
463
- if (field.id)
464
- fieldIds.push(field.id);
465
- });
466
- return {
467
- ...(fieldIds.length ? { fieldIds } : {}),
468
- testRunId: evidence.testRunId,
469
- postPublishBaselineFingerprint: canonicalCloudPivotBaselineFingerprint({ environmentFingerprint: evidence.environmentFingerprint, applicationCode: evidence.applicationCode, discovery: input.discovery }),
470
- };
471
- }
472
- /** Creates and proposes the separately gated published-field cleanup Delivery Plan Approval bound to the owned fixture field. */
473
- export function createTicket10FieldCleanupPlanApproval(input) {
474
- const evidence = validateTicket10Evidence(input.evidence);
475
- assertDigest(input.knowledgePackDigest, "knowledge pack digest");
476
- if (input.ownership.testRunId !== evidence.testRunId || !SHA256.test(input.ownership.postPublishBaselineFingerprint))
477
- throw new Error("Ticket 10 field cleanup approval requires the exact validated ownership binding.");
478
- const operations = ticket10FieldCleanupOperations(evidence);
479
- assertTicket10OperationsBounded({ route: TICKET10_FIELD_CLEANUP_ROUTE, operations, targetSchemaCode: evidence.targetSchemaCode, targetFieldCodes: evidence.targetFieldCodes });
480
- const binding = {
481
- deliveryId: ticket10FieldCleanupDeliveryId(evidence.testRunId),
482
- environment: TICKET10_TEST_ENVIRONMENT_NAME,
483
- baselineFingerprint: input.ownership.postPublishBaselineFingerprint,
484
- environmentFingerprint: evidence.environmentFingerprint,
485
- ...(evidence.evidenceArtifactId ? { evidenceArtifactId: evidence.evidenceArtifactId } : {}),
486
- };
487
- const planHash = digest({ route: TICKET10_FIELD_CLEANUP_ROUTE, capabilityId: TICKET10_FIELD_CLEANUP_CAPABILITY_ID, target: `${evidence.targetSchemaCode}/${evidence.targetFieldCodes.join("+")}`, testRunId: evidence.testRunId, fieldIds: input.ownership.fieldIds ?? [], operations: operations.map((operation) => ({ id: operation.id, readback: operation.expectedReadback })), cleanupContractHash: ticket10FieldCleanupContractHash(), knowledgePackDigest: input.knowledgePackDigest, prohibited: [...TICKET10_PROHIBITED_MUTATION_TERMS] });
488
- const base = createDeliveryPlanApproval({
489
- binding,
490
- requirementHash: digest({ target: `${evidence.targetSchemaCode}/${evidence.targetFieldCodes.join("+")}`, cleanup: true }),
491
- clarificationHash: digest("ticket10-owned-published-family-field-cleanup"),
492
- intentHash: digest({ cleanup: true, target: `${evidence.targetSchemaCode}/${evidence.targetFieldCodes.join("+")}` }),
493
- decisionHash: digest(TICKET10_FIELD_CLEANUP_ROUTE),
494
- graphHash: digest(operations.map((operation) => operation.id)),
495
- riskHash: digest(TICKET10_CLEANUP_RISKS),
496
- validationPlanHash: digest("published-family-field-force-delete-probe-two-variant-contract"),
497
- cleanupPlanHash: ticket10FieldCleanupContractHash(),
498
- allowedOperationScope: evidence.targetFieldCodes.map((fieldCode) => ticket10FieldCleanupScope(evidence.targetSchemaCode, fieldCode)),
499
- planHash,
500
- applicationCode: TICKET10_TEST_APPLICATION_CODE,
501
- folderId: evidence.folder.id,
502
- folderName: evidence.folder.name,
503
- targets: [`${evidence.targetSchemaCode}/${evidence.targetFieldCodes.join("+")}`],
504
- });
505
- const approval = {
506
- ...base,
507
- route: TICKET10_FIELD_CLEANUP_ROUTE,
508
- testRunId: evidence.testRunId,
509
- targetSchemaCode: evidence.targetSchemaCode,
510
- targetFieldCodes: evidence.targetFieldCodes,
511
- capabilityId: TICKET10_FIELD_CLEANUP_CAPABILITY_ID,
512
- knowledgePackDigest: input.knowledgePackDigest,
513
- cleanupContractHash: ticket10FieldCleanupContractHash(),
514
- environmentName: TICKET10_TEST_ENVIRONMENT_NAME,
515
- };
516
- const proposal = { ...approval, approvalHash: deliveryPlanApprovalHash(approval) };
517
- proposeDeliveryPlan(input.ledger, proposal);
518
- return { approval: proposal, operations, cleanupContract: evidence.cleanupContract, intent: buildTicket10ConfigurationIntent(evidence), binding };
519
- }
520
- const TICKET10_CLEANUP_RISKS = [
521
- "Field cleanup probes force_delete on exactly the registered published family fields owned by this testRunId after per-field two-state ownership readback; the adopted variant (published-field force_delete, or model cascade plus field-level absence confirmation) is recorded explicitly.",
522
- "Uncertain delete outcomes stop, retain, and report the fixture; replay and unbounded deletion are rejected.",
523
- ];
524
- /** Compiles the immutable one-step field-publish package bound to the approved plan, pack digest, and pinned contract hash. */
525
- export function prepareTicket10FieldPublishPackage(input) {
526
- const evidence = validateTicket10Evidence(input.evidence);
527
- const approval = requireTicket10Approval(input.approval, TICKET10_FIELD_PUBLISH_ROUTE, TICKET10_FIELD_PUBLISH_CAPABILITY_ID);
528
- const persisted = requirePersistedApprovedPlan(input.ledger, approval);
529
- assertTicket10BindingMatchesEvidence(persisted.binding, evidence);
530
- const scope = ticket10FieldPublishScope(evidence.targetSchemaCode, evidence.targetFieldCodes.join("+"));
531
- if (!persisted.allowedOperationScope.includes(scope))
532
- throw new Error(`Ticket 10 package step is outside the approved operation scope: ${scope}.`);
533
- if (input.pinnedFieldPublishContractHash !== ticket10FieldPublishContractHash())
534
- throw new Error("Ticket 10 pinned field-publish contract hash does not match the canonical 8.6.26 capability contract.");
535
- assertDigest(input.knowledgePackDigest, "knowledge pack digest");
536
- if (input.knowledgePackDigest !== approval.knowledgePackDigest)
537
- throw new Error("Ticket 10 knowledge pack digest diverges from the approved plan binding.");
538
- const operation = ticket10FieldPublishOperation(evidence);
539
- const operations = [operation];
540
- assertTicket10OperationsBounded({ route: TICKET10_FIELD_PUBLISH_ROUTE, operations, targetSchemaCode: evidence.targetSchemaCode, targetFieldCodes: evidence.targetFieldCodes });
541
- const pkg = compileTicket10Package({ ledger: input.ledger, planApproval: persisted, operations, knowledgePackDigest: input.knowledgePackDigest, packageId: `ticket10-field-publish-${digest(persisted.approvalId).slice(0, 32)}`, recoveryPolicyHash: digest("ticket10-family-field-publish-step-only"), capabilityEvidence: [TICKET10_FIELD_PUBLISH_CAPABILITY_ID, "cloudpivot.field-publish.serialized-server-8.6.26", TICKET10_FIELD_READBACK_ENDPOINT, ...evidence.targetFieldCodes.map((fieldCode) => `familyField:${fieldCode}`), `fieldHandoff:${evidence.handoff.fieldCreatePackageId}`, `folder:${evidence.folder.id}`, `testRunId:${evidence.testRunId}`, `baselineFingerprint:${persisted.binding.baselineFingerprint}`] });
542
- return { package: pkg, operations, planApproval: persisted, dryRun: ticket10DryRun(evidence, operations), evidence };
543
- }
544
- /** Compiles the immutable one-step owned published-field cleanup package bound to the separately approved cleanup plan. */
545
- export function prepareTicket10FieldCleanupPackage(input) {
546
- const evidence = validateTicket10Evidence(input.evidence);
547
- const approval = requireTicket10Approval(input.approval, TICKET10_FIELD_CLEANUP_ROUTE, TICKET10_FIELD_CLEANUP_CAPABILITY_ID);
548
- const persisted = requirePersistedApprovedPlan(input.ledger, approval);
549
- if (persisted.binding.environmentFingerprint !== evidence.environmentFingerprint)
550
- throw new Error("Ticket 10 field cleanup approval is outside the evidence environment binding.");
551
- for (const fieldCode of evidence.targetFieldCodes) {
552
- const scope = ticket10FieldCleanupScope(evidence.targetSchemaCode, fieldCode);
553
- if (!persisted.allowedOperationScope.includes(scope))
554
- throw new Error(`Ticket 10 field cleanup step is outside the approved operation scope: ${scope}.`);
555
- }
556
- if (input.pinnedCleanupContractHash !== ticket10FieldCleanupContractHash())
557
- throw new Error("Ticket 10 pinned field cleanup contract hash does not match the canonical owned-cleanup contract.");
558
- assertDigest(input.knowledgePackDigest, "knowledge pack digest");
559
- if (input.knowledgePackDigest !== approval.knowledgePackDigest)
560
- throw new Error("Ticket 10 knowledge pack digest diverges from the approved field cleanup plan binding.");
561
- if (input.ownership.testRunId !== evidence.testRunId)
562
- throw new Error("Ticket 10 field cleanup package requires the exact validated ownership binding.");
563
- const operations = ticket10FieldCleanupOperations(evidence);
564
- assertTicket10OperationsBounded({ route: TICKET10_FIELD_CLEANUP_ROUTE, operations, targetSchemaCode: evidence.targetSchemaCode, targetFieldCodes: evidence.targetFieldCodes });
565
- const pkg = compileTicket10Package({ ledger: input.ledger, planApproval: persisted, operations, knowledgePackDigest: input.knowledgePackDigest, packageId: `ticket10-field-cleanup-${digest(persisted.approvalId).slice(0, 32)}`, recoveryPolicyHash: digest("ticket10-family-field-cleanup-steps-only"), capabilityEvidence: [TICKET10_FIELD_CLEANUP_CAPABILITY_ID, TICKET10_FIELD_CLEANUP_ENDPOINT, TICKET10_FIELD_READBACK_ENDPOINT, ...(input.ownership.fieldIds ?? []).map((fieldId) => `fieldId:${fieldId}`), `testRunId:${evidence.testRunId}`] });
566
- return { package: pkg, operations, planApproval: persisted, dryRun: ticket10DryRun(evidence, operations), evidence };
567
- }
568
- function compileTicket10Package(input) {
569
- const snapshots = input.operations.map((operation) => {
570
- const snapshotBase = { id: `snapshot:${operation.id}`, target: operation.target, identity: operation.target, recoveryBoundary: operation.target, state: "absent", baselineFingerprint: input.planApproval.binding.baselineFingerprint };
571
- return { ...snapshotBase, snapshotHash: digest(snapshotBase) };
572
- });
573
- const pkg = compileNativeOperations({
574
- operations: [...input.operations],
575
- binding: input.planApproval.binding,
576
- planApproval: input.planApproval,
577
- snapshotManifest: snapshots,
578
- releasePlanHash: digest("ticket10-no-release"),
579
- recoveryPolicyHash: input.recoveryPolicyHash,
580
- knowledgePack: { id: TICKET10_FIELD_PUBLISH_KNOWLEDGE_PACK_ID, digest: input.knowledgePackDigest },
581
- capabilityEvidence: input.capabilityEvidence,
582
- requiredPermissions: ["configuration-write"],
583
- packageId: input.packageId,
584
- ledger: input.ledger,
585
- });
586
- input.ledger.createPackage(pkg);
587
- return pkg;
588
- }
589
- /** Read-only dry-run projection; never a CloudPivot write and never an app/form publish operation. */
590
- function ticket10DryRun(evidence, operations) {
591
- const readbackOperations = [
592
- { id: `readback:fields:${evidence.targetSchemaCode}:draft`, method: "GET", path: TICKET10_FIELD_READBACK_ENDPOINT, target: `fields:${evidence.targetSchemaCode}:draft`, payload: { schemaCode: evidence.targetSchemaCode, isPublish: "false" }, expectedReadback: `fields:${evidence.targetSchemaCode}:draft:read`, evidence: ["BizPropertyController.java:161-207"] },
593
- { id: `readback:fields:${evidence.targetSchemaCode}:published`, method: "GET", path: TICKET10_FIELD_READBACK_ENDPOINT, target: `fields:${evidence.targetSchemaCode}:published`, payload: { schemaCode: evidence.targetSchemaCode, isPublish: "true" }, expectedReadback: `fields:${evidence.targetSchemaCode}:published:read`, evidence: ["BizPropertyController.java:161-207"] },
594
- ];
595
- const body = JSON.stringify({ execution: "read-only-dry-run", operations, readbackOperations, blocked: [], prohibitedActions: ["cloudpivot-write"] }, null, 2) + "\n";
596
- return { execution: "read-only-dry-run", operations, readbackOperations, blocked: [], prohibitedActions: ["cloudpivot-write"], artifact: { name: "cloudpivot-ticket10-field-publish-dry-run.json", body, sha256: digest(body), mimeType: "application/json" } };
597
- }
598
- export function formatTicket10DeliveryPlanApprovalFrame(approval) {
599
- const value = requireTicket10Approval(approval, TICKET10_FIELD_PUBLISH_ROUTE, TICKET10_FIELD_PUBLISH_CAPABILITY_ID);
600
- return appendApprovalHumanBlock(`Approve Ticket 10 field publish 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}); fixture model ${value.targetSchemaCode}; family fields ${value.targetFieldCodes.join(" + ")} (${fieldFamilyMemberNamesProse()}); testRunId ${value.testRunId}; exact scope ${value.allowedOperationScope[0]}; one model-scoped family field publish step (POST ${TICKET10_FIELD_PUBLISH_ENDPOINT} with SchemaPublishedVO sendMessage:false publishing the draft family fields in one model-level data-item publication, never an app publish, form publish, or Environment release) — with authoritative per-field two-state readback, per-type behavior validation, and separately approved staged owned cleanup.`, { capability: "ticket10-field-publish", kind: "delivery-plan", identities: [value.targetSchemaCode, ...value.targetFieldCodes], scope: [value.allowedOperationScope[0]] });
601
- }
602
- export function formatTicket10EnvironmentChangeApprovalFrame(input) {
603
- const value = requireTicket10Approval(input.approval, TICKET10_FIELD_PUBLISH_ROUTE, TICKET10_FIELD_PUBLISH_CAPABILITY_ID);
604
- return appendApprovalHumanBlock(`Approve Ticket 10 field publish Environment Change Approval for package ${input.packageId} hash ${input.packageHash}; plan approval ${value.approvalId} hash ${value.approvalHash}; environment test; folder ${value.folderId} (${value.folderName}); fixture model ${value.targetSchemaCode}; family fields ${value.targetFieldCodes.join(" + ")}; testRunId ${value.testRunId}; exact scope ${value.allowedOperationScope[0]}; exactly one model-scoped family field publish step (never an app publish, form publish, or Environment release) executed by the single Forge configuration writer lease with authoritative per-field two-state readback.`, { capability: "ticket10-field-publish", kind: "environment-change", identities: [value.targetSchemaCode, ...value.targetFieldCodes], scope: [value.allowedOperationScope[0]] });
605
- }
606
- export function formatTicket10FieldCleanupDeliveryPlanApprovalFrame(approval) {
607
- const value = requireTicket10Approval(approval, TICKET10_FIELD_CLEANUP_ROUTE, TICKET10_FIELD_CLEANUP_CAPABILITY_ID);
608
- return appendApprovalHumanBlock(`Approve Ticket 10 field 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-publish baseline fingerprint ${value.binding.baselineFingerprint}; folder ${value.folderId} (${value.folderName}); fixture model ${value.targetSchemaCode}; family fields ${value.targetFieldCodes.join(" + ")}; testRunId ${value.testRunId}; exact scopes ${value.allowedOperationScope.join("; ")}; probe force_delete on only the owned published family fields after per-field two-state ownership readback; if published-field deletion semantics differ, the stated contract is the model-cascade plus field-level absence confirmation; uncertain outcomes stop, retain, and report.`, { capability: "ticket10-field-cleanup", kind: "delivery-plan", identities: [value.targetSchemaCode, ...value.targetFieldCodes], scope: [...value.allowedOperationScope] });
609
- }
610
- export function formatTicket10FieldCleanupEnvironmentChangeApprovalFrame(input) {
611
- const value = requireTicket10Approval(input.approval, TICKET10_FIELD_CLEANUP_ROUTE, TICKET10_FIELD_CLEANUP_CAPABILITY_ID);
612
- return appendApprovalHumanBlock(`Approve Ticket 10 field cleanup Environment Change Approval for package ${input.packageId} hash ${input.packageHash}; plan approval ${value.approvalId} hash ${value.approvalHash}; environment test; fixture model ${value.targetSchemaCode}; family fields ${value.targetFieldCodes.join(" + ")}; testRunId ${value.testRunId}; one owned published-family-field force-delete probe per active family member with per-field two-state exact absence readback; a deterministic rejection falls back to the stated model-cascade contract; uncertain outcomes stop, retain, and report.`, { capability: "ticket10-field-cleanup", kind: "environment-change", identities: [value.targetSchemaCode, ...value.targetFieldCodes], scope: [...value.allowedOperationScope] });
613
- }
614
- function validateOneTicket10Field(propertyTypeName, targetFieldCode, suffix, evidence, draft, published, checks) {
615
- const add = (id, ok, detail) => checks.push({ id, status: ok ? "validated" : "blocked", detail });
616
- const publishedPresent = published.classification === "present" && published.query.schemaCode === evidence.targetSchemaCode && published.query.propertyCode === targetFieldCode && published.query.isPublish === "true" && published.field?.code === targetFieldCode && published.field?.schemaCode === evidence.targetSchemaCode;
617
- add(`published-field-present-${suffix}`, publishedPresent, publishedPresent ? `the published (isPublish=true) readback lists the ${propertyTypeName} field; publication means the runtime-visible state.` : `the published readback did not list the ${propertyTypeName} field after the publish step returned success.`);
618
- add(`published-flag-decoded-${suffix}`, published.field?.published === true, `the ${propertyTypeName} published flag decodes as ${String(published.field?.published)} from the authoritative published readback.`);
619
- const draftPresent = draft.classification === "present" && draft.query.schemaCode === evidence.targetSchemaCode && draft.query.propertyCode === targetFieldCode && draft.query.isPublish === "false" && draft.field?.code === targetFieldCode && draft.field?.schemaCode === evidence.targetSchemaCode;
620
- add(`draft-field-present-${suffix}`, draftPresent, draftPresent ? `the draft (isPublish=false) readback still lists the published ${propertyTypeName} field.` : `the draft readback no longer lists the ${propertyTypeName} field after publication.`);
621
- }
622
- /** Validates real family publication behavior beyond HTTP 2xx: per-field two-state presence plus the published flag; form lag is an explicit recorded observation. */
623
- export function validateTicket10FieldPublishBehavior(input) {
624
- const evidence = validateTicket10Evidence(input.evidence);
625
- const checks = [];
626
- evidence.targetFieldCodes.forEach((targetFieldCode, index) => {
627
- const member = TICKET09_FIELD_FAMILY[index];
628
- const suffix = member.propertyTypeName.toLowerCase();
629
- validateOneTicket10Field(member.propertyTypeName, targetFieldCode, suffix, evidence, input.draft[index], input.published[index], checks);
630
- });
631
- const add = (id, ok, detail) => checks.push({ id, status: ok ? "validated" : "blocked", detail });
632
- add("designer-direct-url", evidence.designerDirectUrl.startsWith(`/admin/#/apps/model/${TICKET10_TEST_APPLICATION_CODE}/${evidence.targetSchemaCode}/`), `designer direct URL ${evidence.designerDirectUrl} stays inspectable.`);
633
- const form = input.form;
634
- add("form-consistency-observation", true, form.observed
635
- ? `form readback observes published=${String(form.published)} existDraft=${String(form.existDraft)} after the field publish; model, field, and form are not one transaction, so any form consistency lag is recorded as this explicit partial state rather than assumed cascade consistency.`
636
- : "form readback is unavailable; recorded as an explicit partial state (model, field, and form are not one transaction) instead of assumed cascade consistency.");
637
- const blocked = checks.find((check) => check.status === "blocked");
638
- return blocked ? { status: "blocked", reason: `Ticket 10 behavior validation failed: ${blocked.detail}`, checks } : { status: "validated", checks };
639
- }
640
- /** Execution boundary: named test environment, route-bound package steps, active approvals, no replay of terminal or uncertain packages. */
641
- export function assertTicket10ExecutionBoundary(input) {
642
- if (input.environmentName !== "test")
643
- throw new Error("Ticket 10 execution is restricted to the named test environment; production and staging are rejected.");
644
- const { projection } = input;
645
- const routeStepPrefix = input.route === TICKET10_FIELD_PUBLISH_ROUTE ? "field-publish:" : "cleanup:published-field:";
646
- const expectedSteps = input.route === TICKET10_FIELD_PUBLISH_ROUTE ? 1 : TICKET09_FIELD_FAMILY.length;
647
- if (input.steps.length !== expectedSteps || input.steps.some((step) => !step.id.startsWith(routeStepPrefix)))
648
- throw new Error(`Ticket 10 execution boundary rejected package ${input.packageId}: its steps are not the ${expectedSteps} ${routeStepPrefix} operation(s) bound to the ${input.route} route.`);
649
- if (projection.status === "approved" && projection.approvals.plan === "approved" && projection.approvals.environment === "approved")
650
- return;
651
- if (projection.status === "completed")
652
- throw new Error(`Ticket 10 package ${input.packageId} is already terminal completed; replay is rejected.`);
653
- if (projection.status === "uncertain")
654
- throw new Error(`Ticket 10 package ${input.packageId} is uncertain; reconcile by authoritative readback before any replay.`);
655
- if (projection.status === "in_progress")
656
- throw new Error(`Ticket 10 package ${input.packageId} is already running; concurrent execution is rejected.`);
657
- const pending = [...projection.blockers];
658
- if (projection.approvals.plan !== "approved")
659
- pending.push("Delivery Plan Approval is required before package execution.");
660
- if (projection.approvals.environment !== "approved")
661
- pending.push("Environment Change Approval is required before mutation.");
662
- throw new Error(`Ticket 10 execution boundary rejected package ${input.packageId}: status is ${projection.status} (blocked); ${[...new Set(pending)].join("; ")}.`);
663
- }
664
- /** Executes the approved one-step model-scoped field-publish package through the single OMC Host write transport, then validates two-state behavior. */
665
- export async function executeTicket10FieldPublishPackage(input) {
666
- assertTicket10TestFieldPublishEnvironment(input.settings);
667
- if (typeof input.transport.serializedServerFieldPublish !== "function")
668
- throw new Error("Ticket 10 execution requires the OMC Host serialized field-publish transport; bypass transports are rejected.");
669
- const projection = input.ledger.project(input.prepared.package.packageId);
670
- assertTicket10ExecutionBoundary({ projection, environmentName: input.settings.environmentName, packageId: input.prepared.package.packageId, route: TICKET10_FIELD_PUBLISH_ROUTE, steps: input.prepared.package.steps });
671
- if (input.environmentApproval.packageId !== input.prepared.package.packageId || input.environmentApproval.packageHash !== input.prepared.package.packageHash)
672
- throw new Error("Ticket 10 Environment Change Approval does not match the prepared package.");
673
- const evidence = input.prepared.evidence;
674
- for (const member of TICKET09_FIELD_FAMILY) {
675
- const draftProbe = await input.readFieldEvidence(member.propertyTypeName, "false");
676
- if (draftProbe.classification !== "present" || draftProbe.field?.published === true)
677
- throw new Error(`Ticket 10 execution requires the draft ${member.propertyTypeName} family field to be present and unpublished; ${input.prepared.package.steps[0].target} diverges from the approved handoff.`);
678
- const publishedProbe = await input.readFieldEvidence(member.propertyTypeName, "true");
679
- if (publishedProbe.classification !== "absent" || publishedProbe.reason !== "exact-absence")
680
- throw new Error(`Ticket 10 execution requires exact published-state absence for the ${member.propertyTypeName} family field; ${input.prepared.package.steps[0].target} is already published or blocked.`);
681
- }
682
- const result = await runNativeConfigurationPackage({
683
- package: input.prepared.package,
684
- gate: {
685
- mode: "approved",
686
- planApproval: { status: "approved", approvalHash: input.prepared.planApproval.approvalHash },
687
- environmentApproval: { status: "approved", packageHash: input.environmentApproval.packageHash },
688
- lease: { owner: "configuration-agent", environment: TICKET10_TEST_ENVIRONMENT_NAME },
689
- baselineFingerprint: input.prepared.package.binding.baselineFingerprint,
690
- snapshots: input.prepared.package.snapshotManifest,
691
- package: input.prepared.package,
692
- },
693
- operations: input.prepared.operations,
694
- ledger: input.ledger,
695
- planApproval: input.prepared.planApproval,
696
- transport: input.transport,
697
- credentials: input.credentials,
698
- authoritativeOrigin: input.authoritativeOrigin,
699
- testApplicationCode: TICKET10_TEST_APPLICATION_CODE,
700
- }, input.signal);
701
- const mutationExecuted = result.steps.some((step) => step.status === "committed" || step.status === "uncertain");
702
- const blockers = [...result.blockers];
703
- let status = ticket10ExecutionStatus(result.status);
704
- let nextAction = result.nextAction;
705
- let behavior;
706
- const partialStates = [];
707
- if (result.status === "completed") {
708
- const form = await ticket10FormObservation(input.readFormState);
709
- const draft = await Promise.all(TICKET09_FIELD_FAMILY.map((member) => input.readFieldEvidence(member.propertyTypeName, "false")));
710
- const published = await Promise.all(TICKET09_FIELD_FAMILY.map((member) => input.readFieldEvidence(member.propertyTypeName, "true")));
711
- behavior = validateTicket10FieldPublishBehavior({ evidence, draft, published, form });
712
- if (!form.observed)
713
- partialStates.push("form-readback-unavailable: the default form state could not be observed after the model-scoped family field publish; model, field, and form are not one transaction, so this is recorded as an explicit partial state rather than assumed cascade consistency.");
714
- else if (form.published !== true)
715
- partialStates.push(`form-consistency-lag: the default form still reads published=false existDraft=${String(form.existDraft)} after the model-scoped family field publish; form draft/publish is an independent action and the lag is recorded as an explicit partial state.`);
716
- if (behavior.status !== "validated") {
717
- status = "blocked";
718
- blockers.push(behavior.reason ?? "Ticket 10 behavior validation failed.");
719
- nextAction = "reconcile-behavior-retain-and-report-resource";
720
- }
721
- }
722
- return { reportType: "omc.ticket10.test-field-publish-execution", schemaVersion: 1, packageId: result.packageId, packageHash: result.packageHash, route: TICKET10_FIELD_PUBLISH_ROUTE, testRunId: evidence.testRunId, targetSchemaCode: evidence.targetSchemaCode, targetFieldCodes: evidence.targetFieldCodes, status, mutationExecuted, steps: result.steps, blockers, nextAction, ...(behavior ? { behavior } : {}), ...(partialStates.length ? { partialStates } : {}) };
723
- }
724
- async function ticket10FormObservation(readFormState) {
725
- if (!readFormState)
726
- return { observed: false, published: null, existDraft: null };
727
- try {
728
- const form = await readFormState();
729
- return { observed: true, published: form.published === true, existDraft: form.existDraft === true };
730
- }
731
- catch {
732
- return { observed: false, published: null, existDraft: null };
733
- }
734
- }
735
- /**
736
- * Executes the separately approved owned published-field cleanup: the owned
737
- * field is the force_delete probe, disambiguated by authoritative two-state
738
- * readback. A deterministic rejection adopts the stated model-cascade variant
739
- * (stop, retain, no retry); uncertainty stops, retains, and reports.
740
- */
741
- export async function executeTicket10FieldCleanupPackage(input) {
742
- assertTicket10TestFieldPublishEnvironment(input.settings);
743
- if (typeof input.transport.publishedFieldForceDelete !== "function")
744
- throw new Error("Ticket 10 field cleanup requires the OMC Host published-field force-delete transport; bypass transports are rejected.");
745
- const projection = input.ledger.project(input.prepared.package.packageId);
746
- assertTicket10ExecutionBoundary({ projection, environmentName: input.settings.environmentName, packageId: input.prepared.package.packageId, route: TICKET10_FIELD_CLEANUP_ROUTE, steps: input.prepared.package.steps });
747
- if (input.environmentApproval.packageId !== input.prepared.package.packageId || input.environmentApproval.packageHash !== input.prepared.package.packageHash)
748
- throw new Error("Ticket 10 field cleanup Environment Change Approval does not match the prepared package.");
749
- const evidence = input.evidence;
750
- const probeMemberName = TICKET09_FIELD_FAMILY[0].propertyTypeName;
751
- const draftProbe = await input.readFieldEvidence(probeMemberName, "false");
752
- const publishedProbe = await input.readFieldEvidence(probeMemberName, "true");
753
- if (draftProbe.classification === "absent" && publishedProbe.classification === "absent") {
754
- return { reportType: "omc.ticket10.test-field-publish-execution", schemaVersion: 1, packageId: input.prepared.package.packageId, packageHash: input.prepared.package.packageHash, route: TICKET10_FIELD_CLEANUP_ROUTE, testRunId: evidence.testRunId, targetSchemaCode: evidence.targetSchemaCode, targetFieldCodes: evidence.targetFieldCodes, status: "blocked", mutationExecuted: false, steps: projection.steps, blockers: ["Ticket 10 field cleanup targets are already absent in both readback states; nothing owned remains to delete and no unbounded deletion is attempted."], nextAction: "accept", absenceReadback: [publishedProbe] };
755
- }
756
- const result = await runNativeConfigurationPackage({
757
- package: input.prepared.package,
758
- gate: {
759
- mode: "approved",
760
- planApproval: { status: "approved", approvalHash: input.prepared.planApproval.approvalHash },
761
- environmentApproval: { status: "approved", packageHash: input.environmentApproval.packageHash },
762
- lease: { owner: "configuration-agent", environment: TICKET10_TEST_ENVIRONMENT_NAME },
763
- baselineFingerprint: input.prepared.package.binding.baselineFingerprint,
764
- snapshots: input.prepared.package.snapshotManifest,
765
- package: input.prepared.package,
766
- },
767
- operations: input.prepared.operations,
768
- ledger: input.ledger,
769
- planApproval: input.prepared.planApproval,
770
- transport: input.transport,
771
- credentials: input.credentials,
772
- authoritativeOrigin: input.authoritativeOrigin,
773
- testApplicationCode: TICKET10_TEST_APPLICATION_CODE,
774
- }, input.signal);
775
- const mutationExecuted = result.steps.some((step) => step.status === "committed" || step.status === "uncertain");
776
- const blockers = [...result.blockers];
777
- let status = ticket10ExecutionStatus(result.status);
778
- let nextAction = result.nextAction;
779
- let absenceReadback;
780
- let draftAbsenceReadback;
781
- let cleanupVariant;
782
- if (result.status === "completed") {
783
- cleanupVariant = "published-field-force-delete";
784
- draftAbsenceReadback = await Promise.all(TICKET09_FIELD_FAMILY.map((member) => input.readFieldEvidence(member.propertyTypeName, "false")));
785
- absenceReadback = await Promise.all(TICKET09_FIELD_FAMILY.map((member) => input.readFieldEvidence(member.propertyTypeName, "true")));
786
- if (draftAbsenceReadback.some((readback) => readback.classification !== "absent" || readback.reason !== "exact-absence") || absenceReadback.some((readback) => readback.classification !== "absent" || readback.reason !== "exact-absence")) {
787
- status = "uncertain";
788
- blockers.push("Ticket 10 post-cleanup two-state exact absence readback did not confirm absence for every family field; the fixture fields are retained and reported.");
789
- nextAction = "reconcile";
790
- }
791
- }
792
- else if (result.status === "uncertain") {
793
- // Disambiguate by authoritative two-state readback: a deterministic
794
- // rejection (fields still present in both states, no mutation) adopts the
795
- // stated model-cascade variant; anything else stays uncertain.
796
- const draftStates = await Promise.all(TICKET09_FIELD_FAMILY.map((member) => input.readFieldEvidence(member.propertyTypeName, "false").catch(() => undefined)));
797
- const publishedStates = await Promise.all(TICKET09_FIELD_FAMILY.map((member) => input.readFieldEvidence(member.propertyTypeName, "true").catch(() => undefined)));
798
- const allPresent = draftStates.every((draft) => draft?.classification === "present") && publishedStates.every((published) => published?.classification === "present" && published.field?.published === true);
799
- if (allPresent) {
800
- cleanupVariant = "model-cascade-plus-field-absence-confirmation";
801
- status = "blocked";
802
- nextAction = "model-cascade-cleanup";
803
- blockers.push("Ticket 10 published-field force_delete was rejected by the server and the family fields remain present in both readback states; the stated cleanup contract for this rung applies: stop the field-level deletes (no retry), run the separately approved model physical delete cascade, then confirm per-field two-state absence.");
804
- return { reportType: "omc.ticket10.test-field-publish-execution", schemaVersion: 1, packageId: result.packageId, packageHash: result.packageHash, route: TICKET10_FIELD_CLEANUP_ROUTE, testRunId: evidence.testRunId, targetSchemaCode: evidence.targetSchemaCode, targetFieldCodes: evidence.targetFieldCodes, status, mutationExecuted: false, steps: result.steps, blockers, nextAction, cleanupVariant };
805
- }
806
- blockers.push("Ticket 10 published-field force_delete outcome is uncertain; the fixture family fields are retained and reported with their exact resource identities and no retry is attempted.");
807
- }
808
- return { reportType: "omc.ticket10.test-field-publish-execution", schemaVersion: 1, packageId: result.packageId, packageHash: result.packageHash, route: TICKET10_FIELD_CLEANUP_ROUTE, testRunId: evidence.testRunId, targetSchemaCode: evidence.targetSchemaCode, targetFieldCodes: evidence.targetFieldCodes, status, mutationExecuted, steps: result.steps, blockers, nextAction, ...(absenceReadback ? { absenceReadback } : {}), ...(draftAbsenceReadback ? { draftAbsenceReadback } : {}), ...(cleanupVariant ? { cleanupVariant } : {}) };
809
- }
810
- /** Closes the stated model-cascade cleanup variant: after the model physical delete, every family field must be absent in BOTH readback states. */
811
- export function confirmTicket10FieldCascadeAbsence(input) {
812
- const evidence = validateTicket10Evidence(input.evidence);
813
- evidence.targetFieldCodes.forEach((targetFieldCode, fieldIndex) => {
814
- for (const [label, readbacks] of [["draft", input.draft], ["published", input.published]]) {
815
- const readback = readbacks[fieldIndex];
816
- if (readback.classification !== "absent" || readback.reason !== "exact-absence" || readback.query.schemaCode !== evidence.targetSchemaCode || readback.query.propertyCode !== targetFieldCode)
817
- throw new Error(`Ticket 10 model-cascade cleanup contract is not closed: the ${label} (isPublish=${label === "draft" ? "false" : "true"}) readback does not confirm exact absence for the family field ${targetFieldCode} after the model physical delete.`);
818
- }
819
- });
820
- }
821
- function ticket10ExecutionStatus(status) {
822
- if (status === "completed" || status === "uncertain" || status === "partial")
823
- return status;
824
- return "blocked";
825
- }
826
- function requireTicket10Approval(approval, route, capabilityId) {
827
- const value = approval;
828
- 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 || !Array.isArray(value.targetFieldCodes) || value.targetFieldCodes.length !== TICKET09_FIELD_FAMILY.length || value.targetFieldCodes.some((code) => !isTicket09TargetField(code)) || new Set(value.targetFieldCodes).size !== TICKET09_FIELD_FAMILY.length)
829
- throw new Error(`Ticket 10 approval frame is not bound to the exact ${route} route, family targets, folder, and testRunId.`);
830
- return value;
831
- }
832
- function requirePersistedApprovedPlan(ledger, approval) {
833
- if (approval.status !== "approved")
834
- throw new Error("Ticket 10 package preparation requires the approved exact Ticket 10 Delivery Plan Approval.");
835
- const persisted = ledger.findPlanApproval(approval.approvalId);
836
- if (!persisted || persisted.status !== "approved" || persisted.approvalHash !== approval.approvalHash)
837
- throw new Error("Ticket 10 package preparation requires the exact authoritative approved Delivery Plan Approval.");
838
- return persisted;
839
- }
840
- function assertTicket10BindingMatchesEvidence(binding, evidence) {
841
- const expected = {
842
- deliveryId: ticket10DeliveryId(evidence.testRunId),
843
- environment: TICKET10_TEST_ENVIRONMENT_NAME,
844
- baselineFingerprint: evidence.baselineFingerprint,
845
- environmentFingerprint: evidence.environmentFingerprint,
846
- discoveryFingerprint: evidence.discoveryFingerprint,
847
- ...(evidence.evidenceArtifactId ? { evidenceArtifactId: evidence.evidenceArtifactId } : {}),
848
- };
849
- if (stable(binding) !== stable(expected))
850
- throw new Error("Ticket 10 plan approval binding is outside the evidence baseline, environment, discovery, or delivery binding.");
851
- }
852
- function validateTicket10Evidence(evidence) {
853
- if (!evidence || evidence.route !== TICKET10_FIELD_PUBLISH_ROUTE || evidence.capabilityId !== TICKET10_FIELD_PUBLISH_CAPABILITY_ID)
854
- throw new Error("Ticket 10 evidence is not the host-owned field publish route.");
855
- if (evidence.environmentName !== "test" || evidence.applicationCode !== "test")
856
- throw new Error("Ticket 10 evidence is outside the named test environment and dedicated test application.");
857
- if (!isTicket08TargetSchema(evidence.targetSchemaCode))
858
- throw new Error("Ticket 10 evidence fixture model is not the host-owned Ticket 08 schema.");
859
- if (!Array.isArray(evidence.targetFieldCodes) || evidence.targetFieldCodes.length !== TICKET09_FIELD_FAMILY.length || evidence.targetFieldCodes.some((code) => !isTicket09TargetField(code)) || new Set(evidence.targetFieldCodes).size !== TICKET09_FIELD_FAMILY.length)
860
- throw new Error("Ticket 10 evidence family field codes are not the distinct host-owned Ticket 09 field codes (one per active family member).");
861
- if ([evidence.environmentFingerprint, evidence.baselineFingerprint, evidence.discoveryFingerprint].some((value) => !SHA256.test(value)))
862
- throw new Error("Ticket 10 evidence fingerprints are not canonical.");
863
- if (evidence.modelPresence.classification !== "present" || evidence.modelPresence.query.schemaCode !== evidence.targetSchemaCode)
864
- throw new Error("Ticket 10 evidence does not carry the authoritative fixture model presence handoff.");
865
- evidence.targetFieldCodes.forEach((fieldCode, index) => {
866
- const draftPresence = evidence.draftPresence[index];
867
- const publishedAbsence = evidence.publishedAbsence[index];
868
- if (draftPresence?.classification !== "present" || draftPresence.query.schemaCode !== evidence.targetSchemaCode || draftPresence.query.propertyCode !== fieldCode)
869
- throw new Error("Ticket 10 evidence does not carry exact authenticated draft family field presence.");
870
- if (publishedAbsence?.classification !== "absent" || publishedAbsence.reason !== "exact-absence" || publishedAbsence.query.schemaCode !== evidence.targetSchemaCode || publishedAbsence.query.propertyCode !== fieldCode)
871
- throw new Error("Ticket 10 evidence does not carry exact authenticated published-state family field absence.");
872
- });
873
- if (!evidence.folder.id || !evidence.folder.name)
874
- throw new Error("Ticket 10 evidence folder binding is missing.");
875
- return evidence;
876
- }
877
- function assertDigest(value, label) {
878
- if (typeof value !== "string" || !SHA256.test(value) || !/[1-9a-f]/i.test(value))
879
- throw new Error(`Ticket 10 ${label} is invalid.`);
880
- }
881
- function digest(value) { return createHash("sha256").update(stable(value)).digest("hex"); }
882
- function stable(value) { return JSON.stringify(sort(value)); }
883
- function sort(value) { if (Array.isArray(value))
884
- return value.map(sort); if (value && typeof value === "object")
885
- return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => [key, sort(item)])); return value; }
886
- //# sourceMappingURL=ticket10-test-field-publish.js.map