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.
- package/dist/childtable-sheet201-contract.js +1 -1
- package/dist/field-probe-runner-contract.js +1 -1
- package/dist/field-probe-schema.js +19 -0
- package/dist/form-scaffold-contract.js +126 -0
- package/dist/formula-controls-contract.js +1 -1
- package/package.json +1 -1
- package/dist/master-plan-approval.js +0 -443
- package/dist/ticket08-test-model-mutation.js +0 -901
- package/dist/ticket09-test-field-mutation.js +0 -1026
- package/dist/ticket10-test-field-publish.js +0 -886
- package/dist/ticket11-test-form-draft.js +0 -1014
- package/dist/ticket12-test-form-publish.js +0 -917
- package/dist/ticket20-test-data-rule.js +0 -780
|
@@ -1,1026 +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_CREATE } 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, fieldFamilyMemberCountWord, fieldFamilyMemberFullProse, fieldFamilyMemberNamesProse, fieldFamilyMembers } from "./families/registry.js";
|
|
13
|
-
import { isTicket08TargetSchema, TICKET08_TEST_MUTATION_ROUTE } from "./ticket08-test-model-mutation.js";
|
|
14
|
-
/**
|
|
15
|
-
* Ticket 09 (usable-model ladder, rung 1) — AMENDED IN PLACE (Ticket 19
|
|
16
|
-
* numeric-logical → Ticket 21, field-type ladder rung 03, scaffolding
|
|
17
|
-
* precedent): the governed test field-create capability now covers the
|
|
18
|
-
* active family. One chain run creates every typed draft field on the run's
|
|
19
|
-
* own fixture LIST model — each with its registered create-DTO shape,
|
|
20
|
-
* selector cardinality/identity contract, two-state readback
|
|
21
|
-
* delta, and per-type behavior checks. The fixture model is created in-run
|
|
22
|
-
* through the existing executable Ticket 08 model-create capability; the
|
|
23
|
-
* field package consumes the authoritative model readback handoff and is
|
|
24
|
-
* itself an independently immutable, independently dual-approved package.
|
|
25
|
-
* Evidence from this capability is never inferred to any other field type
|
|
26
|
-
* or capability; field publish, form draft/publish, app publish, production,
|
|
27
|
-
* staging, tenant administration, Redis, restart, deployment, and release
|
|
28
|
-
* stay rejected.
|
|
29
|
-
*
|
|
30
|
-
* Authoritative 8.6.26 contract (decompiled engine + live probe ledger +
|
|
31
|
-
* type survey matrix §2.3/§5):
|
|
32
|
-
* - create DTO is `BizPropertyModel`: the shared baseline
|
|
33
|
-
* name/name_i18n/code/schemaCode/propertyType/propertyEmpty/defaultValue
|
|
34
|
-
* plus serialized `options` from each registered family member with NO
|
|
35
|
-
* propertyLength where the type matrix has no enum default length
|
|
36
|
-
* (BizPropertyModel.java:21-40, BizPropertyController.java:849-914,
|
|
37
|
-
* write-mapping.js:136-178);
|
|
38
|
-
* - `propertyType` serializes as the BizPropertyType @JsonValue index;
|
|
39
|
-
* - field readback is two-state: bizproperty/list?isPublish=false (draft) and
|
|
40
|
-
* isPublish=true (published); a newly created field appears only in the
|
|
41
|
-
* draft state (probe ledger: field create succeeded, draft-only readback);
|
|
42
|
-
* - readback options come back FILTERED (bizFormFacade.filterOptions) — the
|
|
43
|
-
* per-type readback delta (filtered options round-trip, scalar selector
|
|
44
|
-
* identity defaultValue wire shape) is recorded verbatim by the rung's
|
|
45
|
-
* behavior probe so the live run pins
|
|
46
|
-
* the matrix's open slots (BizPropertyController.java:457-486);
|
|
47
|
-
* - draft field deletion is the synchronous force_delete with query params
|
|
48
|
-
* bizSchemaCode/bizPropertyCode (BizPropertyController.java:1532-1560);
|
|
49
|
-
* unpublished fields delete directly (controller:1484-1530);
|
|
50
|
-
* - field and schema codes share the server encoding contract: letter first,
|
|
51
|
-
* letters/digits/underscore only, pinned here to the conservative 26
|
|
52
|
-
* character limit (live errcode 10005 evidence).
|
|
53
|
-
*/
|
|
54
|
-
export const TICKET09_FIELD_MUTATION_ROUTE = "ticket09-test-field-mutation";
|
|
55
|
-
export const TICKET09_FIELD_CLEANUP_ROUTE = "ticket09-test-field-cleanup";
|
|
56
|
-
export const TICKET09_FIELD_CREATE_CAPABILITY_ID = "cloudpivot.test.field-create-8.6.26";
|
|
57
|
-
export const TICKET09_FIELD_CLEANUP_CAPABILITY_ID = "cloudpivot.test.field-cleanup-8.6.26";
|
|
58
|
-
export const TICKET09_FIELD_MUTATION_KNOWLEDGE_PACK_ID = "cloudpivot-8.6-ticket09-test-field-mutation";
|
|
59
|
-
export const TICKET09_TEST_ENVIRONMENT_NAME = "test";
|
|
60
|
-
export const TICKET09_TEST_APPLICATION_CODE = "test";
|
|
61
|
-
export const TICKET09_REFERENCE_EVIDENCE_VERSION = "8.6.26";
|
|
62
|
-
export const TICKET09_FIELD_CREATE_ENDPOINT = "/api/api/app/bizproperty/create";
|
|
63
|
-
export const TICKET09_FIELD_READBACK_ENDPOINT = "/api/api/app/bizproperty/list";
|
|
64
|
-
export const TICKET09_FIELD_CLEANUP_ENDPOINT = "/api/api/app/bizproperty/force_delete";
|
|
65
|
-
/**
|
|
66
|
-
* Deep-frozen family descriptor pinned to the capability's familyMembers.
|
|
67
|
-
* Ticket 03b (family registry refactor): single source is the ACTIVE
|
|
68
|
-
* registration of `src/families/registry.ts` (the self-contained family
|
|
69
|
-
* module `src/families/<family>.ts`) — this export is the ticket-09
|
|
70
|
-
* projection of it, byte-identical to the previously inline table. Ticket
|
|
71
|
-
* 25: N-capable — the projection carries every active family member in
|
|
72
|
-
* governed order.
|
|
73
|
-
*/
|
|
74
|
-
export const TICKET09_FIELD_FAMILY = Object.freeze(fieldFamilyMembers().map((member) => Object.freeze({
|
|
75
|
-
familyPosition: member.familyPosition,
|
|
76
|
-
propertyTypeName: member.propertyTypeName,
|
|
77
|
-
propertyType: member.propertyType,
|
|
78
|
-
controlType: member.controlType,
|
|
79
|
-
propertyLength: member.propertyLength,
|
|
80
|
-
fieldOptions: member.fieldOptions,
|
|
81
|
-
resolveFieldOptions: member.resolveFieldOptions,
|
|
82
|
-
})));
|
|
83
|
-
export function ticket09FamilyMember(propertyTypeName) {
|
|
84
|
-
const member = TICKET09_FIELD_FAMILY.find((candidate) => candidate.propertyTypeName === propertyTypeName);
|
|
85
|
-
if (!member)
|
|
86
|
-
throw new Error(`Ticket 09 family has no member ${propertyTypeName}.`);
|
|
87
|
-
return member;
|
|
88
|
-
}
|
|
89
|
-
export const TICKET09_PROHIBITED_MUTATION_TERMS = Object.freeze([
|
|
90
|
-
"publish", "deploy", "restart", "release", "redis", "tenant-admin", "runtime-crud",
|
|
91
|
-
"model:", "form", "view-create", "folder-create", "folder-delete",
|
|
92
|
-
]);
|
|
93
|
-
const TICKET09_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;
|
|
94
|
-
const SHA256 = /^[a-f0-9]{64}$/i;
|
|
95
|
-
/** The named `test` environment and its dedicated test application are hard preconditions. */
|
|
96
|
-
export function assertTicket09TestFieldMutationEnvironment(settings) {
|
|
97
|
-
if (settings.environmentName !== "test")
|
|
98
|
-
throw new Error("Ticket 09 field mutation is restricted to the named test environment; production and staging are rejected.");
|
|
99
|
-
if (!settings.baseUrl.trim() || !/^https?:\/\/\S+$/i.test(settings.baseUrl.trim()))
|
|
100
|
-
throw new Error("Ticket 09 field mutation requires an absolute configured test environment endpoint.");
|
|
101
|
-
if (settings.testApplicationCode !== "test")
|
|
102
|
-
throw new Error("Ticket 09 field mutation requires the dedicated test application to be test.");
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* The field code obeys the same server encoding contract as the model schema
|
|
106
|
-
* code: letter first, letters/digits/underscore only, at most 26 characters.
|
|
107
|
-
* The host-owned fixture field code is `omc_t09_` + 16 lowercase hex digits
|
|
108
|
-
* (24 characters, unique per run; exact-absence preflight still guards
|
|
109
|
-
* collisions).
|
|
110
|
-
*/
|
|
111
|
-
export const TICKET09_TARGET_FIELD_PATTERN = /^omc_t09_[0-9a-f]{16}$/;
|
|
112
|
-
const TICKET09_FIELD_CODE_PREFIX = "omc_t09_";
|
|
113
|
-
export function ticket09TargetFieldCode() {
|
|
114
|
-
return `${TICKET09_FIELD_CODE_PREFIX}${randomUUID().replaceAll("-", "").slice(0, 16)}`;
|
|
115
|
-
}
|
|
116
|
-
export function isTicket09TargetField(fieldCode) {
|
|
117
|
-
return TICKET09_TARGET_FIELD_PATTERN.test(fieldCode);
|
|
118
|
-
}
|
|
119
|
-
export function assertTicket09TargetField(fieldCode) {
|
|
120
|
-
if (!isTicket09TargetField(fieldCode))
|
|
121
|
-
throw new Error("Ticket 09 targetFieldCode must be a host-scoped omc_t09_<16-hex> field code within the CloudPivot 8.6.26 26-character code contract.");
|
|
122
|
-
return fieldCode;
|
|
123
|
-
}
|
|
124
|
-
export function ticket09DeliveryId(testRunId) { return `ticket09-field-${testRunId}`; }
|
|
125
|
-
export function ticket09FieldCleanupDeliveryId(testRunId) { return `ticket09-field-cleanup-${testRunId}`; }
|
|
126
|
-
export function ticket09FieldScope(targetSchemaCode, targetFieldCode) { return `cloudpivot:field:${targetSchemaCode}/${targetFieldCode}`; }
|
|
127
|
-
/** The family-scoped scope key binding every family field code (the ACTIVE family's member set). */
|
|
128
|
-
export function ticket09FamilyScope(targetSchemaCode, targetFieldCodes) { return `cloudpivot:field:${targetSchemaCode}/${targetFieldCodes.join("+")}`; }
|
|
129
|
-
export function ticket09FieldName(targetFieldCode) { return assertTicket09TargetField(targetFieldCode); }
|
|
130
|
-
/**
|
|
131
|
-
* Per-type default values (Ticket 03b: the derivation lives in the family
|
|
132
|
-
* module — `src/families/<family>.ts` — through the registry; the member
|
|
133
|
-
* identity is the ACTIVE family's registered data).
|
|
134
|
-
*/
|
|
135
|
-
function ticket09RegisteredMember(propertyTypeName) {
|
|
136
|
-
const member = fieldFamilyMembers().find((candidate) => candidate.propertyTypeName === propertyTypeName);
|
|
137
|
-
if (!member)
|
|
138
|
-
throw new Error(`The active family has no ${propertyTypeName} member.`);
|
|
139
|
-
return member;
|
|
140
|
-
}
|
|
141
|
-
export function ticket09FieldDefaultValue(member, testRunId) {
|
|
142
|
-
return ticket09RegisteredMember(member.propertyTypeName).defaultValue(testRunId);
|
|
143
|
-
}
|
|
144
|
-
/** Design-time designer direct URL; unpublished fixtures stay inspectable here without any publish. */
|
|
145
|
-
export function ticket09DesignerDirectUrl(targetSchemaCode) { return `/admin/#/apps/model/${TICKET09_TEST_APPLICATION_CODE}/${targetSchemaCode}/`; }
|
|
146
|
-
export function decodeTicket09FieldListEvidence(input) {
|
|
147
|
-
const url = `${TICKET09_FIELD_READBACK_ENDPOINT}?schemaCode=${encodeURIComponent(input.schemaCode)}&isPublish=${input.isPublish}`;
|
|
148
|
-
const query = { schemaCode: input.schemaCode, propertyCode: input.propertyCode, isPublish: input.isPublish };
|
|
149
|
-
const envelope = input.body;
|
|
150
|
-
if (!envelope || typeof envelope !== "object" || envelope.errcode !== 0 || !Array.isArray(envelope.data))
|
|
151
|
-
throw new Error("Ticket 09 field list readback did not decode the authoritative envelope.");
|
|
152
|
-
// The row must bind both the field code and the queried model schema; a
|
|
153
|
-
// same-coded field from another model never satisfies this evidence.
|
|
154
|
-
const row = envelope.data.find((candidate) => typeof candidate === "object" && candidate !== null && candidate.code === input.propertyCode && candidate.schemaCode === input.schemaCode);
|
|
155
|
-
if (!row)
|
|
156
|
-
return Object.freeze({ classification: "absent", reason: "exact-absence", query, url });
|
|
157
|
-
const expectedMember = fieldFamilyMembers().find((member) => member.propertyType === input.expectedPropertyType);
|
|
158
|
-
if (row.propertyType !== input.expectedPropertyType && row.propertyType !== String(input.expectedPropertyType))
|
|
159
|
-
throw new Error(`Ticket 09 field list readback returned a field outside the expected ${expectedMember?.propertyTypeName ?? String(input.expectedPropertyType)} contract.`);
|
|
160
|
-
const decodedPropertyType = Number(row.propertyType);
|
|
161
|
-
const decodedMember = fieldFamilyMembers().find((member) => member.propertyType === decodedPropertyType);
|
|
162
|
-
const field = Object.freeze({
|
|
163
|
-
...(typeof row.id === "string" && row.id ? { id: row.id } : {}),
|
|
164
|
-
code: input.propertyCode,
|
|
165
|
-
schemaCode: row.schemaCode,
|
|
166
|
-
propertyType: decodedPropertyType,
|
|
167
|
-
...(decodedMember ? { propertyTypeName: decodedMember.propertyTypeName } : {}),
|
|
168
|
-
propertyEmpty: typeof row.propertyEmpty === "boolean" ? row.propertyEmpty : true,
|
|
169
|
-
defaultValue: typeof row.defaultValue === "string" ? row.defaultValue : null,
|
|
170
|
-
rawDefaultValue: row.defaultValue === undefined ? null : row.defaultValue,
|
|
171
|
-
propertyLength: typeof row.propertyLength === "number" ? row.propertyLength : null,
|
|
172
|
-
published: row.published === true,
|
|
173
|
-
options: typeof row.options === "string" ? row.options : null,
|
|
174
|
-
topLevelFormat: typeof row.format === "string" ? row.format : null,
|
|
175
|
-
selectionJson: row.selectionJson === undefined ? null : row.selectionJson,
|
|
176
|
-
});
|
|
177
|
-
return Object.freeze({ classification: "present", reason: "exact-presence", query, url, field });
|
|
178
|
-
}
|
|
179
|
-
export function buildTicket09FieldCleanupContract(testRunId, targetSchemaCode, targetFieldCodes) {
|
|
180
|
-
if (!TICKET09_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)
|
|
181
|
-
throw new Error("Ticket 09 field cleanup contract requires the exact host-owned testRunId, fixture schema, and the distinct server-legal family field codes (one per active family member).");
|
|
182
|
-
return Object.freeze({
|
|
183
|
-
capabilityId: TICKET09_FIELD_CLEANUP_CAPABILITY_ID,
|
|
184
|
-
testRunId,
|
|
185
|
-
targetSchemaCode,
|
|
186
|
-
targetFieldCodes,
|
|
187
|
-
operation: { method: "POST", endpoint: TICKET09_FIELD_CLEANUP_ENDPOINT, payload: "query:bizSchemaCode+bizPropertyCode-per-family-field" },
|
|
188
|
-
postDeleteAbsenceReadback: { method: "GET", endpoint: TICKET09_FIELD_READBACK_ENDPOINT, expected: "draft-exact-absence-per-family-field" },
|
|
189
|
-
ownership: "field-codes-schema-and-draft-state-bound-to-testRunId",
|
|
190
|
-
modelCascade: "ticket08-owned-model-physical-delete-cascades-draft-fields",
|
|
191
|
-
replayPolicy: "absent-is-no-op",
|
|
192
|
-
uncertainPolicy: "stop-retain-report-no-retry",
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
/** Deterministic, run-independent contract identities pinned by the Knowledge Pack promotion. */
|
|
196
|
-
export function ticket09FieldCreateContractHash() { return digest(ticket09FieldCreateContract()); }
|
|
197
|
-
export function ticket09FieldCleanupContractHash() { return digest(ticket09FieldCleanupContractTemplate()); }
|
|
198
|
-
function ticket09FieldCreateContract() {
|
|
199
|
-
// Ticket 03b: the family fragment of the contract is synthesized over
|
|
200
|
-
// the ACTIVE registry registration (src/families/<family>.ts) — adding
|
|
201
|
-
// or amending a family recomputes this contract hash deterministically.
|
|
202
|
-
const family = activeFieldFamily();
|
|
203
|
-
const members = fieldFamilyMembers();
|
|
204
|
-
return {
|
|
205
|
-
capabilityId: TICKET09_FIELD_CREATE_CAPABILITY_ID,
|
|
206
|
-
operationKind: "field-create-family",
|
|
207
|
-
method: "POST",
|
|
208
|
-
endpoint: CLOUDPIVOT_8_6_26_SERIALIZED_FIELD_CREATE.endpoint,
|
|
209
|
-
readbackEndpoint: CLOUDPIVOT_8_6_26_SERIALIZED_FIELD_CREATE.readbackEndpoint,
|
|
210
|
-
preflight: CLOUDPIVOT_8_6_26_SERIALIZED_FIELD_CREATE.preflight,
|
|
211
|
-
dto: `BizPropertyModel-family:baseline(name,name_i18n,code,schemaCode,propertyType,propertyEmpty,defaultValue)+per-type(${members.map((member) => `${member.propertyTypeName}:${member.dtoIncrement}`).join(";")};${family.propertyLengthContractWording})`,
|
|
212
|
-
family: members.map((member) => ({ propertyTypeName: member.propertyTypeName, propertyType: member.propertyType, propertyLength: member.propertyLength, options: member.fieldOptions })),
|
|
213
|
-
propertyEmpty: false,
|
|
214
|
-
readbackStates: ["isPublish=false:present", "isPublish=true:absent"],
|
|
215
|
-
readbackDeltas: Object.fromEntries(members.map((member) => [member.propertyTypeName, member.readbackDelta])),
|
|
216
|
-
transportMode: "serialized-server-field-create-8.6.26",
|
|
217
|
-
fieldCodeContract: { pattern: "^[A-Za-z][A-Za-z0-9_]*$", maxLength: 26, evidence: "live-authoritative-errcode-10005" },
|
|
218
|
-
capability: CLOUDPIVOT_8_6_26_SERIALIZED_FIELD_CREATE,
|
|
219
|
-
evidence: [...CLOUDPIVOT_8_6_26_SERIALIZED_FIELD_CREATE.evidence],
|
|
220
|
-
requiredPermissions: ["configuration-write"],
|
|
221
|
-
prohibited: [...TICKET09_PROHIBITED_MUTATION_TERMS],
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
function ticket09FieldCleanupContractTemplate() {
|
|
225
|
-
return {
|
|
226
|
-
capabilityId: TICKET09_FIELD_CLEANUP_CAPABILITY_ID,
|
|
227
|
-
operationKind: "owned-draft-family-field-force-delete",
|
|
228
|
-
method: "POST",
|
|
229
|
-
endpoint: TICKET09_FIELD_CLEANUP_ENDPOINT,
|
|
230
|
-
payload: "query:bizSchemaCode+bizPropertyCode-per-family-field",
|
|
231
|
-
postDeleteAbsenceReadback: `${TICKET09_FIELD_READBACK_ENDPOINT}?isPublish=false:draft-exact-absence-per-family-field`,
|
|
232
|
-
ownership: "field-codes-schema-and-draft-state-bound-to-testRunId",
|
|
233
|
-
modelCascade: "ticket08-owned-model-physical-delete-cascades-draft-fields",
|
|
234
|
-
replayPolicy: "absent-is-no-op",
|
|
235
|
-
uncertainPolicy: "stop-retain-report-no-retry",
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
/** Package operations are exactly one bounded field operation per active family member, in registry order. */
|
|
239
|
-
export function assertTicket09OperationsBounded(input) {
|
|
240
|
-
const expectedCount = TICKET09_FIELD_FAMILY.length;
|
|
241
|
-
if (input.operations.length !== expectedCount)
|
|
242
|
-
throw new Error(`Ticket 09 ${input.route} packages must contain exactly the ${expectedCount} bounded family operations.`);
|
|
243
|
-
const expectedPrefix = input.route === TICKET09_FIELD_MUTATION_ROUTE ? "field:" : "cleanup:field:";
|
|
244
|
-
input.operations.forEach((operation, index) => {
|
|
245
|
-
const identity = `${operation.id} ${operation.target} ${operation.path}`.toLowerCase();
|
|
246
|
-
if (TICKET09_PROHIBITED_MUTATION_TERMS.some((term) => identity.includes(term)) || !operation.id.startsWith(expectedPrefix))
|
|
247
|
-
throw new Error(`Ticket 09 operation ${operation.id} is outside the bounded operation set; prohibited families (publish, deploy, restart, release, redis, tenant administration, runtime CRUD, model/form/view/folder writes) remain rejected.`);
|
|
248
|
-
const expectedFieldCode = input.targetFieldCodes[index];
|
|
249
|
-
const target = `field:${input.targetSchemaCode}/${expectedFieldCode}`;
|
|
250
|
-
const expected = input.route === TICKET09_FIELD_MUTATION_ROUTE
|
|
251
|
-
? { id: target, path: TICKET09_FIELD_CREATE_ENDPOINT, transportMode: "serialized-server-field-create-8.6.26" }
|
|
252
|
-
: { id: `cleanup:${target}`, path: TICKET09_FIELD_CLEANUP_ENDPOINT, transportMode: "cloudpivot-field-force-delete-8.6.26" };
|
|
253
|
-
if (operation.id !== expected.id || operation.path !== expected.path || operation.transportMode !== expected.transportMode || operation.target !== target)
|
|
254
|
-
throw new Error(`Ticket 09 operation ${operation.id} does not match the exact family target binding for ${target}.`);
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
/** Builds the signed, version-bound pack promoting exactly the single test field-create capability. */
|
|
258
|
-
export function buildTicket09FieldMutationKnowledgePack(input) {
|
|
259
|
-
const compiler = { id: "src/ticket09-test-field-mutation.ts:createTicket09FieldCreatePlanApproval", sha256: digest("omc-ticket09-compiler-v1") };
|
|
260
|
-
const decoder = { id: "src/dsh-host-plugin.ts:createCloudPivotWriteTransport", sha256: digest("omc-ticket09-decoder-v1") };
|
|
261
|
-
return buildKnowledgePack({
|
|
262
|
-
packId: TICKET09_FIELD_MUTATION_KNOWLEDGE_PACK_ID,
|
|
263
|
-
packVersion: "1.0.0",
|
|
264
|
-
sourceVersion: TICKET09_REFERENCE_EVIDENCE_VERSION,
|
|
265
|
-
sourceArtifacts: [{ id: "cloudpivot-8.6.26-serialized-field-create-capability", sha256: digest(CLOUDPIVOT_8_6_26_SERIALIZED_FIELD_CREATE) }],
|
|
266
|
-
references: [
|
|
267
|
-
{ id: "yunshu-probe-ledger-field-create-draft-only-readback", sha256: digest("yunshu-probe-ledger-field-create") },
|
|
268
|
-
{ id: TICKET09_FIELD_CREATE_ENDPOINT, sha256: digest(TICKET09_FIELD_CREATE_ENDPOINT) },
|
|
269
|
-
{ id: TICKET09_FIELD_READBACK_ENDPOINT, sha256: digest(TICKET09_FIELD_READBACK_ENDPOINT) },
|
|
270
|
-
{ id: TICKET09_FIELD_CLEANUP_ENDPOINT, sha256: digest(TICKET09_FIELD_CLEANUP_ENDPOINT) },
|
|
271
|
-
],
|
|
272
|
-
compilers: [compiler],
|
|
273
|
-
decoders: [decoder],
|
|
274
|
-
capabilities: [{
|
|
275
|
-
id: TICKET09_FIELD_CREATE_CAPABILITY_ID,
|
|
276
|
-
status: "runtime-verified",
|
|
277
|
-
promote: true,
|
|
278
|
-
compilerRef: compiler.id,
|
|
279
|
-
decoderRef: decoder.id,
|
|
280
|
-
sourceEvidence: [...CLOUDPIVOT_8_6_26_SERIALIZED_FIELD_CREATE.evidence, TICKET09_FIELD_CREATE_ENDPOINT, TICKET09_FIELD_READBACK_ENDPOINT, "BizPropertyController.java:161-207", "BizPropertyController.java:1484-1560"],
|
|
281
|
-
runtimeEvidence: ["yunshu-probe-ledger-field-create-draft-only-readback", "cloudpivot.field-create.serialized-server-8.6.26"],
|
|
282
|
-
operationPromotions: [{
|
|
283
|
-
id: "field-create",
|
|
284
|
-
status: "promoted",
|
|
285
|
-
operationContractHash: ticket09FieldCreateContractHash(),
|
|
286
|
-
sourceRefs: [...CLOUDPIVOT_8_6_26_SERIALIZED_FIELD_CREATE.evidence, TICKET09_FIELD_CREATE_ENDPOINT, TICKET09_FIELD_READBACK_ENDPOINT, TICKET09_FIELD_CLEANUP_ENDPOINT],
|
|
287
|
-
versionFamily: "8.6",
|
|
288
|
-
referenceVersion: TICKET09_REFERENCE_EVIDENCE_VERSION,
|
|
289
|
-
requiredEvidence: ["cleanup", "discovery", "permission", "readback", "test", "write"],
|
|
290
|
-
evidence: ["cleanup", "discovery", "permission", "readback", "test", "write"],
|
|
291
|
-
blockedReasons: [],
|
|
292
|
-
}],
|
|
293
|
-
}],
|
|
294
|
-
signerKeyId: input.signerKeyId,
|
|
295
|
-
signingKey: input.signingKey,
|
|
296
|
-
createdAt: input.createdAt,
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
/** Loads the pack against CloudPivot 8.6.26 and pins the single executable capability and contract hash. */
|
|
300
|
-
export function loadTicket09KnowledgePack(artifact, trustedKeys) {
|
|
301
|
-
const runtime = new KnowledgePackRuntime(trustedKeys);
|
|
302
|
-
const load = runtime.load(artifact, TICKET09_REFERENCE_EVIDENCE_VERSION);
|
|
303
|
-
if (!load.accepted)
|
|
304
|
-
throw new Error(`Ticket 09 Knowledge Pack is not authoritative: ${load.blockingReasons.join(", ")}.`);
|
|
305
|
-
const active = runtime.active();
|
|
306
|
-
const capabilities = active.capabilities;
|
|
307
|
-
if (capabilities.length !== 1 || capabilities[0].id !== TICKET09_FIELD_CREATE_CAPABILITY_ID)
|
|
308
|
-
throw new Error("Ticket 09 Knowledge Pack must promote exactly the single test field-create capability; evidence inference to other field types or capabilities is rejected.");
|
|
309
|
-
const hashes = capabilities[0].operationContractHashes;
|
|
310
|
-
if (hashes.length !== 1 || hashes[0] !== ticket09FieldCreateContractHash())
|
|
311
|
-
throw new Error("Ticket 09 Knowledge Pack operation contract hash does not match the canonical field-create contract.");
|
|
312
|
-
return { runtime, packId: active.manifest.packId, contentDigest: active.manifest.contentSha256, executableCapabilities: capabilities.map(({ id }) => id) };
|
|
313
|
-
}
|
|
314
|
-
/** The family field codes in contract order (one per active family member). */
|
|
315
|
-
export function ticket09TargetFieldCodes(evidence) {
|
|
316
|
-
return evidence.targetFields.map((field) => field.targetFieldCode);
|
|
317
|
-
}
|
|
318
|
-
/** Host-owned composite-chain evidence capture: consumes the completed model handoff and proves model presence plus exact draft absence for BOTH family fields. */
|
|
319
|
-
export function captureTicket09FieldCreateEvidence(input) {
|
|
320
|
-
assertTicket09TestFieldMutationEnvironment(input.settings);
|
|
321
|
-
if (!TICKET09_UUID_V4.test(input.testRunId))
|
|
322
|
-
throw new Error("Ticket 09 evidence capture requires the run's host-owned UUID v4 testRunId.");
|
|
323
|
-
const handoff = input.handoff;
|
|
324
|
-
const report = handoff.modelExecutionReport;
|
|
325
|
-
if (!handoff.modelPackageId || !SHA256.test(handoff.modelPackageHash) || report.route !== TICKET08_TEST_MUTATION_ROUTE || report.status !== "completed" || report.mutationExecuted !== true)
|
|
326
|
-
throw new Error("Ticket 09 field create requires the completed authoritative model-create handoff; serial writes never assume state.");
|
|
327
|
-
const modelEvidence = handoff.modelEvidence;
|
|
328
|
-
if (report.testRunId !== input.testRunId || modelEvidence.testRunId !== input.testRunId || handoff.ownership.testRunId !== input.testRunId)
|
|
329
|
-
throw new Error("Ticket 09 composite chain requires the single run testRunId across the model and field packages.");
|
|
330
|
-
const targetSchemaCode = modelEvidence.targetSchemaCode;
|
|
331
|
-
if (!isTicket08TargetSchema(targetSchemaCode) || report.targetSchemaCode !== targetSchemaCode)
|
|
332
|
-
throw new Error("Ticket 09 fixture model identity diverges from the completed model-create handoff.");
|
|
333
|
-
if (input.targetFieldCodes.length !== TICKET09_FIELD_FAMILY.length || input.targetFieldCodes.some((code) => !isTicket09TargetField(code)) || new Set(input.targetFieldCodes).size !== TICKET09_FIELD_FAMILY.length)
|
|
334
|
-
throw new Error("Ticket 09 evidence capture requires one distinct host-owned family field code per active family member.");
|
|
335
|
-
const modelPresence = input.modelPresence;
|
|
336
|
-
if (modelPresence.classification !== "present" || modelPresence.query.schemaCode !== targetSchemaCode)
|
|
337
|
-
throw new Error("Ticket 09 field create requires the fixture model to be present in fresh authoritative readback.");
|
|
338
|
-
const entry = input.designTime.directory.models.find((candidate) => candidate.schemaCode === targetSchemaCode);
|
|
339
|
-
if (!entry || entry.appCode !== TICKET09_TEST_APPLICATION_CODE || entry.modelType !== "LIST" || entry.parentId !== modelEvidence.folder.id || entry.parentId !== handoff.ownership.folderId || typeof entry.id !== "string" || !entry.id)
|
|
340
|
-
throw new Error("Ticket 09 field create requires the fixture model design-time directory binding (id, LIST, dedicated application, evidence folder).");
|
|
341
|
-
const targetFields = input.targetFieldCodes.map((targetFieldCode, index) => {
|
|
342
|
-
const member = TICKET09_FIELD_FAMILY[index];
|
|
343
|
-
const fieldEvidence = input.fieldEvidence[index];
|
|
344
|
-
if (fieldEvidence.classification !== "absent" || fieldEvidence.reason !== "exact-absence" || fieldEvidence.query.schemaCode !== targetSchemaCode || fieldEvidence.query.propertyCode !== targetFieldCode || fieldEvidence.query.isPublish !== "false")
|
|
345
|
-
throw new Error(`Ticket 09 evidence capture requires exact authenticated draft absence evidence for the host-derived ${member.propertyTypeName} family field.`);
|
|
346
|
-
const plan = Object.freeze({
|
|
347
|
-
familyPosition: member.familyPosition,
|
|
348
|
-
propertyTypeName: member.propertyTypeName,
|
|
349
|
-
propertyType: member.propertyType,
|
|
350
|
-
controlType: member.controlType,
|
|
351
|
-
targetFieldCode,
|
|
352
|
-
fieldName: ticket09FieldName(targetFieldCode),
|
|
353
|
-
fieldDefaultValue: ticket09FieldDefaultValue(member, input.testRunId),
|
|
354
|
-
propertyLength: member.propertyLength,
|
|
355
|
-
fieldOptions: member.resolveFieldOptions
|
|
356
|
-
? member.resolveFieldOptions({ schemaCode: targetSchemaCode, memberFieldCodes: input.targetFieldCodes, testRunId: input.testRunId })
|
|
357
|
-
: member.fieldOptions,
|
|
358
|
-
fieldEvidence,
|
|
359
|
-
});
|
|
360
|
-
return plan;
|
|
361
|
-
});
|
|
362
|
-
const authoritativeOrigin = input.discovery.authoritativeOrigin;
|
|
363
|
-
const enterpriseId = input.discovery.enterpriseId;
|
|
364
|
-
if (!authoritativeOrigin || !enterpriseId)
|
|
365
|
-
throw new Error("Ticket 09 evidence capture requires authenticated environment fingerprint evidence.");
|
|
366
|
-
const environmentFingerprint = canonicalCloudPivotEnvironmentFingerprint({ environment: TICKET09_TEST_ENVIRONMENT_NAME, authoritativeOrigin, applicationCode: TICKET09_TEST_APPLICATION_CODE, enterpriseId });
|
|
367
|
-
if (environmentFingerprint !== modelEvidence.environmentFingerprint)
|
|
368
|
-
throw new Error("Ticket 09 field create is outside the model handoff environment fingerprint.");
|
|
369
|
-
const evidence = Object.freeze({
|
|
370
|
-
route: TICKET09_FIELD_MUTATION_ROUTE,
|
|
371
|
-
capabilityId: TICKET09_FIELD_CREATE_CAPABILITY_ID,
|
|
372
|
-
environmentName: TICKET09_TEST_ENVIRONMENT_NAME,
|
|
373
|
-
applicationCode: TICKET09_TEST_APPLICATION_CODE,
|
|
374
|
-
folder: modelEvidence.folder,
|
|
375
|
-
targetSchemaCode,
|
|
376
|
-
targetFields,
|
|
377
|
-
testRunId: input.testRunId,
|
|
378
|
-
environmentFingerprint,
|
|
379
|
-
baselineFingerprint: canonicalCloudPivotBaselineFingerprint({ environmentFingerprint, applicationCode: TICKET09_TEST_APPLICATION_CODE, discovery: input.discovery }),
|
|
380
|
-
discoveryFingerprint: canonicalFormalDiscoveryFingerprint(input.discovery),
|
|
381
|
-
handoff,
|
|
382
|
-
modelPresence,
|
|
383
|
-
cleanupContract: buildTicket09FieldCleanupContract(input.testRunId, targetSchemaCode, targetFields.map((field) => field.targetFieldCode)),
|
|
384
|
-
designerDirectUrl: ticket09DesignerDirectUrl(targetSchemaCode),
|
|
385
|
-
capturedAt: new Date((input.now ?? Date.now)()).toISOString(),
|
|
386
|
-
});
|
|
387
|
-
if ([evidence.environmentFingerprint, evidence.baselineFingerprint, evidence.discoveryFingerprint].some((value) => !SHA256.test(value)))
|
|
388
|
-
throw new Error("Ticket 09 evidence fingerprints are not canonical.");
|
|
389
|
-
if (SENSITIVE_SECRET_VALUE.test(stable(evidence)))
|
|
390
|
-
throw new Error("Ticket 09 evidence contains prohibited sensitive data.");
|
|
391
|
-
return evidence;
|
|
392
|
-
}
|
|
393
|
-
export const TICKET09_EVIDENCE_DEFAULT_TTL_MS = 25 * 60_000;
|
|
394
|
-
export const TICKET09_EVIDENCE_MAX_TTL_MS = 30 * 60_000;
|
|
395
|
-
/** Single-use, TTL-bounded evidence store; consumed exactly once by plan creation. */
|
|
396
|
-
export class Ticket09EvidenceStore {
|
|
397
|
-
records = new Map();
|
|
398
|
-
now;
|
|
399
|
-
ttlMs;
|
|
400
|
-
lastNow;
|
|
401
|
-
constructor(options = {}) {
|
|
402
|
-
this.now = options.now ?? Date.now;
|
|
403
|
-
this.ttlMs = options.ttlMs ?? TICKET09_EVIDENCE_DEFAULT_TTL_MS;
|
|
404
|
-
if (!Number.isFinite(this.ttlMs) || this.ttlMs < 1 || this.ttlMs > TICKET09_EVIDENCE_MAX_TTL_MS)
|
|
405
|
-
throw new Error("Ticket 09 evidence TTL must be between 1ms and its safety maximum.");
|
|
406
|
-
}
|
|
407
|
-
capture(evidence) {
|
|
408
|
-
validateTicket09Evidence(evidence);
|
|
409
|
-
const evidenceId = `ticket09-evidence-${randomUUID()}`;
|
|
410
|
-
const expiresAt = this.readClock() + this.ttlMs;
|
|
411
|
-
const stored = Object.freeze({ ...evidence, evidenceArtifactId: evidenceId });
|
|
412
|
-
this.records.set(evidenceId, { evidence: stored, expiresAt, consumed: false });
|
|
413
|
-
return { evidenceId, expiresAt, evidence: stored };
|
|
414
|
-
}
|
|
415
|
-
consume(evidenceId) {
|
|
416
|
-
const record = this.records.get(evidenceId);
|
|
417
|
-
if (!record)
|
|
418
|
-
throw new Error("Ticket 09 evidence artifact is missing or divergent.");
|
|
419
|
-
if (record.consumed)
|
|
420
|
-
throw new Error("Ticket 09 evidence has already been consumed.");
|
|
421
|
-
if (record.expiresAt <= this.readClock())
|
|
422
|
-
throw new Error("Ticket 09 evidence is stale or expired.");
|
|
423
|
-
record.consumed = true;
|
|
424
|
-
return record.evidence;
|
|
425
|
-
}
|
|
426
|
-
resolve(evidenceId) {
|
|
427
|
-
const record = this.records.get(evidenceId);
|
|
428
|
-
if (!record || !record.consumed)
|
|
429
|
-
throw new Error("Ticket 09 evidence artifact is missing or has not been consumed by planning.");
|
|
430
|
-
if (record.expiresAt <= this.readClock())
|
|
431
|
-
throw new Error("Ticket 09 evidence artifact is stale or expired.");
|
|
432
|
-
return record.evidence;
|
|
433
|
-
}
|
|
434
|
-
readClock() {
|
|
435
|
-
const currentTime = this.now();
|
|
436
|
-
if (!Number.isFinite(currentTime))
|
|
437
|
-
throw new Error("Ticket 09 evidence clock is invalid; fail closed.");
|
|
438
|
-
if (this.lastNow !== undefined && currentTime < this.lastNow)
|
|
439
|
-
throw new Error("Ticket 09 evidence clock moved backwards; fail closed.");
|
|
440
|
-
this.lastNow = currentTime;
|
|
441
|
-
return currentTime;
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
/** The exact evidenced per-type BizPropertyModel create DTOs: shared baseline + per-type propertyLength/options (matrix §2.3), required (propertyEmpty false) plus per-type default value. */
|
|
445
|
-
export function buildTicket09FieldCreatePayloads(evidence) {
|
|
446
|
-
validateTicket09Evidence(evidence);
|
|
447
|
-
return evidence.targetFields.map((field) => ({
|
|
448
|
-
name: field.fieldName,
|
|
449
|
-
name_i18n: JSON.stringify({ "zh-CN": field.fieldName }),
|
|
450
|
-
code: field.targetFieldCode,
|
|
451
|
-
schemaCode: evidence.targetSchemaCode,
|
|
452
|
-
propertyType: field.propertyType,
|
|
453
|
-
...(field.propertyLength === null ? {} : { propertyLength: field.propertyLength }),
|
|
454
|
-
propertyEmpty: false,
|
|
455
|
-
defaultValue: field.fieldDefaultValue,
|
|
456
|
-
...(field.propertyType === 9 || field.propertyType === 11 ? relationPayloadFields(field.fieldOptions) : {}),
|
|
457
|
-
options: field.fieldOptions,
|
|
458
|
-
}));
|
|
459
|
-
}
|
|
460
|
-
function relationPayloadFields(optionsJson) {
|
|
461
|
-
const options = JSON.parse(optionsJson);
|
|
462
|
-
if (typeof options.schemaCode !== "string" || typeof options.displayField !== "string" || typeof options.relativePropertyMatchCode !== "string")
|
|
463
|
-
throw new Error("Relevance relation options must carry the concrete schema, display, and match fields.");
|
|
464
|
-
return { relativeCode: options.schemaCode, relativePropertyCode: options.displayField, relativePropertyMatchCode: options.relativePropertyMatchCode };
|
|
465
|
-
}
|
|
466
|
-
export function buildTicket09ConfigurationIntent(evidence) {
|
|
467
|
-
validateTicket09Evidence(evidence);
|
|
468
|
-
const target = `${evidence.targetSchemaCode}/${ticket09TargetFieldCodes(evidence).join("+")}`;
|
|
469
|
-
return {
|
|
470
|
-
intentVersion: 1,
|
|
471
|
-
targetFamily: "8.6",
|
|
472
|
-
environmentBinding: { application: evidence.applicationCode, model: evidence.targetSchemaCode, baselineFingerprint: evidence.baselineFingerprint },
|
|
473
|
-
sourceRequirement: `Ticket 09 maintainer test (${activeFieldFamily().familyId} family rung): create ${fieldFamilyMemberCountWord()} unique draft typed fields — ${fieldFamilyMemberFullProse()} — on the run's fixture LIST model, with authoritative two-state readback, per-type behavior validation and wire-shape probe evidence, and staged owned cleanup.`,
|
|
474
|
-
resources: evidence.targetFields.map((field) => ({ id: `${evidence.targetSchemaCode}/${field.targetFieldCode}`, kind: "field", state: "create", properties: { propertyType: field.propertyTypeName, propertyEmpty: false, defaultValue: field.fieldDefaultValue, ...(field.propertyLength === null ? {} : { propertyLength: field.propertyLength }), options: JSON.parse(field.fieldOptions), schemaCode: evidence.targetSchemaCode, name: field.fieldName } })),
|
|
475
|
-
relationships: [],
|
|
476
|
-
lifecycle: evidence.targetFields.map((field, index) => ({ resourceId: `${evidence.targetSchemaCode}/${field.targetFieldCode}`, transitions: ["create"], order: index + 1, idempotent: false, dependencyChecks: ["completed-model-handoff", "draft-exact-absence-preflight"], recoveryBoundary: `field:${evidence.targetSchemaCode}/${field.targetFieldCode}` })),
|
|
477
|
-
behavior: [{ id: "created-family-field-structure", kind: "structure", requiredCapabilityIds: [TICKET09_FIELD_CREATE_CAPABILITY_ID], resourceIds: evidence.targetFields.map((field) => `${evidence.targetSchemaCode}/${field.targetFieldCode}`), obligations: ["authoritative draft (isPublish=false) readback carries EVERY active family field", "published (isPublish=true) readback carries neither field", "required and per-type default-value configuration decode from authoritative readback", "per-type options round-trip decodes with the readback wire shapes recorded verbatim (each member's registered readback delta, scalar-empty defaultValue identity shape for selector fields, top-level format synthesis where the member carries it)", "designer direct URL stays inspectable without publish"] }],
|
|
478
|
-
acceptance: [{ id: "ticket09-create-readback", scenario: "family-field-create-readback-behavior", actor: "omc-maintainer-opencode", assertions: ["every family field is present in the draft readback only", "propertyEmpty false and each type's defaultValue decode from authoritative readback", "each type's options round-trip and scalar identity wire shape decode with the readback wire shapes recorded verbatim", "fixture remains inspectable at the designer direct URL"], readbackInvariants: [`${TICKET09_FIELD_READBACK_ENDPOINT}?schemaCode=<model>&isPublish=false lists every exact family field`, `${TICKET09_FIELD_READBACK_ENDPOINT}?schemaCode=<model>&isPublish=true lists neither field`], outcome: "validated" }],
|
|
479
|
-
cleanup: [{ id: "ticket09-owned-family-field-cleanup", boundary: `ticket09:${evidence.testRunId}:family-field-force-delete`, dependencyChecks: ["draft family field ownership readback", "draft exact absence readback per family field"], resources: evidence.targetFields.map((field) => `field:${evidence.targetSchemaCode}/${field.targetFieldCode}`) }],
|
|
480
|
-
implementationConstraints: { nativePreferred: true, backendRequired: false, prohibitedPaths: ["form.online-js", "business-rule", "integration", "backend.extension"], approvalExceptions: [] },
|
|
481
|
-
evidenceRefs: [TICKET09_FIELD_CREATE_CAPABILITY_ID, TICKET09_FIELD_CREATE_ENDPOINT, TICKET09_FIELD_READBACK_ENDPOINT, TICKET09_FIELD_CLEANUP_ENDPOINT, ...CLOUDPIVOT_8_6_26_SERIALIZED_FIELD_CREATE.evidence],
|
|
482
|
-
unresolved: [],
|
|
483
|
-
};
|
|
484
|
-
}
|
|
485
|
-
function ticket09FieldCreateOperations(evidence) {
|
|
486
|
-
const payloads = buildTicket09FieldCreatePayloads(evidence);
|
|
487
|
-
return evidence.targetFields.map((field, index) => {
|
|
488
|
-
const target = `field:${evidence.targetSchemaCode}/${field.targetFieldCode}`;
|
|
489
|
-
return {
|
|
490
|
-
id: target,
|
|
491
|
-
method: "POST",
|
|
492
|
-
path: TICKET09_FIELD_CREATE_ENDPOINT,
|
|
493
|
-
target,
|
|
494
|
-
payload: payloads[index],
|
|
495
|
-
expectedReadback: `${target}:draft`,
|
|
496
|
-
evidence: [TICKET09_FIELD_CREATE_CAPABILITY_ID, ...CLOUDPIVOT_8_6_26_SERIALIZED_FIELD_CREATE.evidence, TICKET09_FIELD_READBACK_ENDPOINT],
|
|
497
|
-
transportMode: "serialized-server-field-create-8.6.26",
|
|
498
|
-
capability: CLOUDPIVOT_8_6_26_SERIALIZED_FIELD_CREATE,
|
|
499
|
-
};
|
|
500
|
-
});
|
|
501
|
-
}
|
|
502
|
-
function ticket09FieldCleanupOperations(evidence) {
|
|
503
|
-
return evidence.targetFields.map((field) => {
|
|
504
|
-
const target = `field:${evidence.targetSchemaCode}/${field.targetFieldCode}`;
|
|
505
|
-
return {
|
|
506
|
-
id: `cleanup:${target}`,
|
|
507
|
-
method: "POST",
|
|
508
|
-
path: TICKET09_FIELD_CLEANUP_ENDPOINT,
|
|
509
|
-
target,
|
|
510
|
-
payload: { bizSchemaCode: evidence.targetSchemaCode, bizPropertyCode: field.targetFieldCode },
|
|
511
|
-
expectedReadback: `${target}:deleted`,
|
|
512
|
-
evidence: [TICKET09_FIELD_CLEANUP_CAPABILITY_ID, "BizPropertyController.java:1484-1560", TICKET09_FIELD_READBACK_ENDPOINT],
|
|
513
|
-
transportMode: "cloudpivot-field-force-delete-8.6.26",
|
|
514
|
-
};
|
|
515
|
-
});
|
|
516
|
-
}
|
|
517
|
-
/** Creates and proposes the immutable Ticket 09 Delivery Plan Approval for the two-step family field-create package. */
|
|
518
|
-
export function createTicket09FieldCreatePlanApproval(input) {
|
|
519
|
-
const evidence = validateTicket09Evidence(input.evidence);
|
|
520
|
-
assertDigest(input.knowledgePackDigest, "knowledge pack digest");
|
|
521
|
-
const operations = ticket09FieldCreateOperations(evidence);
|
|
522
|
-
const targetFieldCodes = ticket09TargetFieldCodes(evidence);
|
|
523
|
-
assertTicket09OperationsBounded({ route: TICKET09_FIELD_MUTATION_ROUTE, operations, targetSchemaCode: evidence.targetSchemaCode, targetFieldCodes });
|
|
524
|
-
const intent = buildTicket09ConfigurationIntent(evidence);
|
|
525
|
-
if (validateConfigurationIntent(intent).length)
|
|
526
|
-
throw new Error("Ticket 09 Configuration Intent is invalid; planning is blocked.");
|
|
527
|
-
const binding = {
|
|
528
|
-
deliveryId: ticket09DeliveryId(evidence.testRunId),
|
|
529
|
-
environment: TICKET09_TEST_ENVIRONMENT_NAME,
|
|
530
|
-
baselineFingerprint: evidence.baselineFingerprint,
|
|
531
|
-
environmentFingerprint: evidence.environmentFingerprint,
|
|
532
|
-
discoveryFingerprint: evidence.discoveryFingerprint,
|
|
533
|
-
...(evidence.evidenceArtifactId ? { evidenceArtifactId: evidence.evidenceArtifactId } : {}),
|
|
534
|
-
};
|
|
535
|
-
const planHash = digest({ route: TICKET09_FIELD_MUTATION_ROUTE, capabilityId: TICKET09_FIELD_CREATE_CAPABILITY_ID, target: `${evidence.targetSchemaCode}/${targetFieldCodes.join("+")}`, testRunId: evidence.testRunId, operations: operations.map((operation) => ({ id: operation.id, readback: operation.expectedReadback })), modelHandoff: { packageId: evidence.handoff.modelPackageId, packageHash: evidence.handoff.modelPackageHash }, folder: evidence.folder, fingerprints: { baseline: evidence.baselineFingerprint, environment: evidence.environmentFingerprint, discovery: evidence.discoveryFingerprint }, cleanupContractHash: ticket09FieldCleanupContractHash(), knowledgePackDigest: input.knowledgePackDigest, prohibited: [...TICKET09_PROHIBITED_MUTATION_TERMS] });
|
|
536
|
-
const base = createDeliveryPlanApproval({
|
|
537
|
-
binding,
|
|
538
|
-
requirementHash: digest({ target: `${evidence.targetSchemaCode}/${targetFieldCodes.join("+")}` }),
|
|
539
|
-
clarificationHash: digest("ticket09-test-field-mutation-family-date-time"),
|
|
540
|
-
intentHash: digest(intent),
|
|
541
|
-
decisionHash: digest(TICKET09_FIELD_MUTATION_ROUTE),
|
|
542
|
-
graphHash: digest(operations.map((operation) => operation.id)),
|
|
543
|
-
riskHash: digest(TICKET09_RISKS),
|
|
544
|
-
validationPlanHash: digest("authoritative-two-state-family-field-readback-and-per-type-required-default-behavior"),
|
|
545
|
-
cleanupPlanHash: ticket09FieldCleanupContractHash(),
|
|
546
|
-
allowedOperationScope: targetFieldCodes.map((fieldCode) => ticket09FieldScope(evidence.targetSchemaCode, fieldCode)),
|
|
547
|
-
planHash,
|
|
548
|
-
applicationCode: TICKET09_TEST_APPLICATION_CODE,
|
|
549
|
-
folderId: evidence.folder.id,
|
|
550
|
-
folderName: evidence.folder.name,
|
|
551
|
-
targets: [`${evidence.targetSchemaCode}/${targetFieldCodes.join("+")}`],
|
|
552
|
-
});
|
|
553
|
-
const approval = {
|
|
554
|
-
...base,
|
|
555
|
-
route: TICKET09_FIELD_MUTATION_ROUTE,
|
|
556
|
-
testRunId: evidence.testRunId,
|
|
557
|
-
targetSchemaCode: evidence.targetSchemaCode,
|
|
558
|
-
targetFieldCodes,
|
|
559
|
-
capabilityId: TICKET09_FIELD_CREATE_CAPABILITY_ID,
|
|
560
|
-
knowledgePackDigest: input.knowledgePackDigest,
|
|
561
|
-
cleanupContractHash: ticket09FieldCleanupContractHash(),
|
|
562
|
-
environmentName: TICKET09_TEST_ENVIRONMENT_NAME,
|
|
563
|
-
};
|
|
564
|
-
const proposal = { ...approval, approvalHash: deliveryPlanApprovalHash(approval) };
|
|
565
|
-
proposeDeliveryPlan(input.ledger, proposal);
|
|
566
|
-
return { approval: proposal, operations, cleanupContract: evidence.cleanupContract, intent, binding };
|
|
567
|
-
}
|
|
568
|
-
const TICKET09_RISKS = [
|
|
569
|
-
`Exactly ${fieldFamilyMemberCountWord()} unique draft typed field creates (${fieldFamilyMemberNamesProse()}) on the run's own fixture LIST model are in scope; field publish, form draft/publish, app publish, model, view, runtime, folder, deployment, restart, release, Redis, and tenant administration remain rejected.`,
|
|
570
|
-
"Owned family field cleanup is separately approved and bounded to the fields created by this testRunId with draft exact absence readback per field; the model fixture is physically deleted afterwards through the existing owned model cleanup.",
|
|
571
|
-
];
|
|
572
|
-
/** Proves the fixture family fields were created by this run: exact codes, draft state, typed semantics, dedicated application model, present readback. */
|
|
573
|
-
export function validateTicket09FieldCleanupOwnership(input) {
|
|
574
|
-
const evidence = validateTicket09Evidence(input.evidence);
|
|
575
|
-
const fieldIds = [];
|
|
576
|
-
input.fieldEvidence.forEach((fieldEvidence, index) => {
|
|
577
|
-
const expected = evidence.targetFields[index];
|
|
578
|
-
if (fieldEvidence.classification !== "present" || fieldEvidence.reason !== "exact-presence" || fieldEvidence.query.schemaCode !== evidence.targetSchemaCode || fieldEvidence.query.propertyCode !== expected.targetFieldCode || fieldEvidence.query.isPublish !== "false")
|
|
579
|
-
throw new Error(`Ticket 09 field cleanup requires the created ${expected.propertyTypeName} field to be present in the authoritative draft readback.`);
|
|
580
|
-
const field = fieldEvidence.field;
|
|
581
|
-
if (!field || field.code !== expected.targetFieldCode || field.schemaCode !== evidence.targetSchemaCode)
|
|
582
|
-
throw new Error(`Ticket 09 field cleanup ownership readback returned the wrong ${expected.propertyTypeName} field.`);
|
|
583
|
-
if (field.published === true)
|
|
584
|
-
throw new Error("Ticket 09 field cleanup only owns unpublished draft fields; published-field semantics are a separate rung.");
|
|
585
|
-
if (field.propertyType !== expected.propertyType)
|
|
586
|
-
throw new Error(`Ticket 09 field cleanup ownership requires the created ${expected.propertyTypeName} field semantics.`);
|
|
587
|
-
if (field.id)
|
|
588
|
-
fieldIds.push(field.id);
|
|
589
|
-
});
|
|
590
|
-
return {
|
|
591
|
-
...(fieldIds.length ? { fieldIds } : {}),
|
|
592
|
-
testRunId: evidence.testRunId,
|
|
593
|
-
postCreateBaselineFingerprint: canonicalCloudPivotBaselineFingerprint({ environmentFingerprint: evidence.environmentFingerprint, applicationCode: evidence.applicationCode, discovery: input.discovery }),
|
|
594
|
-
};
|
|
595
|
-
}
|
|
596
|
-
/** Creates and proposes the separately gated field cleanup Delivery Plan Approval bound to the owned fixture field. */
|
|
597
|
-
export function createTicket09FieldCleanupPlanApproval(input) {
|
|
598
|
-
const evidence = validateTicket09Evidence(input.evidence);
|
|
599
|
-
assertDigest(input.knowledgePackDigest, "knowledge pack digest");
|
|
600
|
-
if (input.ownership.testRunId !== evidence.testRunId || !SHA256.test(input.ownership.postCreateBaselineFingerprint))
|
|
601
|
-
throw new Error("Ticket 09 field cleanup approval requires the exact validated ownership binding.");
|
|
602
|
-
const operations = ticket09FieldCleanupOperations(evidence);
|
|
603
|
-
const targetFieldCodes = ticket09TargetFieldCodes(evidence);
|
|
604
|
-
assertTicket09OperationsBounded({ route: TICKET09_FIELD_CLEANUP_ROUTE, operations, targetSchemaCode: evidence.targetSchemaCode, targetFieldCodes });
|
|
605
|
-
const binding = {
|
|
606
|
-
deliveryId: ticket09FieldCleanupDeliveryId(evidence.testRunId),
|
|
607
|
-
environment: TICKET09_TEST_ENVIRONMENT_NAME,
|
|
608
|
-
baselineFingerprint: input.ownership.postCreateBaselineFingerprint,
|
|
609
|
-
environmentFingerprint: evidence.environmentFingerprint,
|
|
610
|
-
...(evidence.evidenceArtifactId ? { evidenceArtifactId: evidence.evidenceArtifactId } : {}),
|
|
611
|
-
};
|
|
612
|
-
const planHash = digest({ route: TICKET09_FIELD_CLEANUP_ROUTE, capabilityId: TICKET09_FIELD_CLEANUP_CAPABILITY_ID, target: `${evidence.targetSchemaCode}/${targetFieldCodes.join("+")}`, testRunId: evidence.testRunId, fieldIds: input.ownership.fieldIds ?? [], operations: operations.map((operation) => ({ id: operation.id, readback: operation.expectedReadback })), cleanupContractHash: ticket09FieldCleanupContractHash(), knowledgePackDigest: input.knowledgePackDigest, prohibited: [...TICKET09_PROHIBITED_MUTATION_TERMS] });
|
|
613
|
-
const base = createDeliveryPlanApproval({
|
|
614
|
-
binding,
|
|
615
|
-
requirementHash: digest({ target: `${evidence.targetSchemaCode}/${targetFieldCodes.join("+")}`, cleanup: true }),
|
|
616
|
-
clarificationHash: digest("ticket09-owned-family-field-cleanup"),
|
|
617
|
-
intentHash: digest({ cleanup: true, target: `${evidence.targetSchemaCode}/${targetFieldCodes.join("+")}` }),
|
|
618
|
-
decisionHash: digest(TICKET09_FIELD_CLEANUP_ROUTE),
|
|
619
|
-
graphHash: digest(operations.map((operation) => operation.id)),
|
|
620
|
-
riskHash: digest(TICKET09_CLEANUP_RISKS),
|
|
621
|
-
validationPlanHash: digest("draft-family-field-force-delete-and-exact-absence"),
|
|
622
|
-
cleanupPlanHash: ticket09FieldCleanupContractHash(),
|
|
623
|
-
allowedOperationScope: targetFieldCodes.map((fieldCode) => ticket09FieldScope(evidence.targetSchemaCode, fieldCode)),
|
|
624
|
-
planHash,
|
|
625
|
-
applicationCode: TICKET09_TEST_APPLICATION_CODE,
|
|
626
|
-
folderId: evidence.folder.id,
|
|
627
|
-
folderName: evidence.folder.name,
|
|
628
|
-
targets: [`${evidence.targetSchemaCode}/${targetFieldCodes.join("+")}`],
|
|
629
|
-
});
|
|
630
|
-
const approval = {
|
|
631
|
-
...base,
|
|
632
|
-
route: TICKET09_FIELD_CLEANUP_ROUTE,
|
|
633
|
-
testRunId: evidence.testRunId,
|
|
634
|
-
targetSchemaCode: evidence.targetSchemaCode,
|
|
635
|
-
targetFieldCodes,
|
|
636
|
-
capabilityId: TICKET09_FIELD_CLEANUP_CAPABILITY_ID,
|
|
637
|
-
knowledgePackDigest: input.knowledgePackDigest,
|
|
638
|
-
cleanupContractHash: ticket09FieldCleanupContractHash(),
|
|
639
|
-
environmentName: TICKET09_TEST_ENVIRONMENT_NAME,
|
|
640
|
-
};
|
|
641
|
-
const proposal = { ...approval, approvalHash: deliveryPlanApprovalHash(approval) };
|
|
642
|
-
proposeDeliveryPlan(input.ledger, proposal);
|
|
643
|
-
return { approval: proposal, operations, cleanupContract: evidence.cleanupContract, intent: buildTicket09ConfigurationIntent(evidence), binding };
|
|
644
|
-
}
|
|
645
|
-
const TICKET09_CLEANUP_RISKS = [
|
|
646
|
-
`Field cleanup force-deletes exactly the ${fieldFamilyMemberCountWord()} draft family fields (${fieldFamilyMemberNamesProse()}) created by this testRunId after per-field ownership readback.`,
|
|
647
|
-
"Uncertain delete outcomes stop, retain, and report the fixture; replay and unbounded deletion are rejected.",
|
|
648
|
-
];
|
|
649
|
-
/** Compiles the immutable two-step family field-create package bound to the approved plan, pack digest, and pinned contract hash. */
|
|
650
|
-
export function prepareTicket09FieldCreatePackage(input) {
|
|
651
|
-
const evidence = validateTicket09Evidence(input.evidence);
|
|
652
|
-
const approval = requireTicket09Approval(input.approval, TICKET09_FIELD_MUTATION_ROUTE, TICKET09_FIELD_CREATE_CAPABILITY_ID);
|
|
653
|
-
const persisted = requirePersistedApprovedPlan(input.ledger, approval);
|
|
654
|
-
assertTicket09BindingMatchesEvidence(persisted.binding, evidence);
|
|
655
|
-
const targetFieldCodes = ticket09TargetFieldCodes(evidence);
|
|
656
|
-
for (const fieldCode of targetFieldCodes) {
|
|
657
|
-
const scope = ticket09FieldScope(evidence.targetSchemaCode, fieldCode);
|
|
658
|
-
if (!persisted.allowedOperationScope.includes(scope))
|
|
659
|
-
throw new Error(`Ticket 09 package step is outside the approved operation scope: ${scope}.`);
|
|
660
|
-
}
|
|
661
|
-
if (input.pinnedFieldCreateContractHash !== ticket09FieldCreateContractHash())
|
|
662
|
-
throw new Error("Ticket 09 pinned field-create contract hash does not match the canonical 8.6.26 capability contract.");
|
|
663
|
-
assertDigest(input.knowledgePackDigest, "knowledge pack digest");
|
|
664
|
-
if (input.knowledgePackDigest !== approval.knowledgePackDigest)
|
|
665
|
-
throw new Error("Ticket 09 knowledge pack digest diverges from the approved plan binding.");
|
|
666
|
-
const operations = ticket09FieldCreateOperations(evidence);
|
|
667
|
-
assertTicket09OperationsBounded({ route: TICKET09_FIELD_MUTATION_ROUTE, operations, targetSchemaCode: evidence.targetSchemaCode, targetFieldCodes });
|
|
668
|
-
const pkg = compileTicket09Package({ ledger: input.ledger, planApproval: persisted, operations, knowledgePackDigest: input.knowledgePackDigest, packageId: `ticket09-field-${digest(persisted.approvalId).slice(0, 32)}`, recoveryPolicyHash: digest("ticket09-family-field-create-steps-only"), capabilityEvidence: [TICKET09_FIELD_CREATE_CAPABILITY_ID, "cloudpivot.field-create.serialized-server-8.6.26", TICKET09_FIELD_READBACK_ENDPOINT, ...targetFieldCodes.map((fieldCode) => `familyField:${fieldCode}`), `modelHandoff:${evidence.handoff.modelPackageId}`, `folder:${evidence.folder.id}`, `testRunId:${evidence.testRunId}`, `baselineFingerprint:${persisted.binding.baselineFingerprint}`] });
|
|
669
|
-
return { package: pkg, operations, planApproval: persisted, dryRun: ticket09DryRun(evidence, operations), evidence };
|
|
670
|
-
}
|
|
671
|
-
/** Compiles the immutable two-step owned family field-cleanup package bound to the separately approved cleanup plan. */
|
|
672
|
-
export function prepareTicket09FieldCleanupPackage(input) {
|
|
673
|
-
const evidence = validateTicket09Evidence(input.evidence);
|
|
674
|
-
const approval = requireTicket09Approval(input.approval, TICKET09_FIELD_CLEANUP_ROUTE, TICKET09_FIELD_CLEANUP_CAPABILITY_ID);
|
|
675
|
-
const persisted = requirePersistedApprovedPlan(input.ledger, approval);
|
|
676
|
-
if (persisted.binding.environmentFingerprint !== evidence.environmentFingerprint)
|
|
677
|
-
throw new Error("Ticket 09 field cleanup approval is outside the evidence environment binding.");
|
|
678
|
-
const targetFieldCodes = ticket09TargetFieldCodes(evidence);
|
|
679
|
-
for (const fieldCode of targetFieldCodes) {
|
|
680
|
-
const scope = ticket09FieldScope(evidence.targetSchemaCode, fieldCode);
|
|
681
|
-
if (!persisted.allowedOperationScope.includes(scope))
|
|
682
|
-
throw new Error(`Ticket 09 field cleanup step is outside the approved operation scope: ${scope}.`);
|
|
683
|
-
}
|
|
684
|
-
if (input.pinnedCleanupContractHash !== ticket09FieldCleanupContractHash())
|
|
685
|
-
throw new Error("Ticket 09 pinned field cleanup contract hash does not match the canonical owned-cleanup contract.");
|
|
686
|
-
assertDigest(input.knowledgePackDigest, "knowledge pack digest");
|
|
687
|
-
if (input.knowledgePackDigest !== approval.knowledgePackDigest)
|
|
688
|
-
throw new Error("Ticket 09 knowledge pack digest diverges from the approved field cleanup plan binding.");
|
|
689
|
-
if (input.ownership.testRunId !== evidence.testRunId)
|
|
690
|
-
throw new Error("Ticket 09 field cleanup package requires the exact validated ownership binding.");
|
|
691
|
-
const operations = ticket09FieldCleanupOperations(evidence);
|
|
692
|
-
assertTicket09OperationsBounded({ route: TICKET09_FIELD_CLEANUP_ROUTE, operations, targetSchemaCode: evidence.targetSchemaCode, targetFieldCodes });
|
|
693
|
-
const pkg = compileTicket09Package({ ledger: input.ledger, planApproval: persisted, operations, knowledgePackDigest: input.knowledgePackDigest, packageId: `ticket09-field-cleanup-${digest(persisted.approvalId).slice(0, 32)}`, recoveryPolicyHash: digest("ticket09-family-field-cleanup-steps-only"), capabilityEvidence: [TICKET09_FIELD_CLEANUP_CAPABILITY_ID, TICKET09_FIELD_CLEANUP_ENDPOINT, TICKET09_FIELD_READBACK_ENDPOINT, ...(input.ownership.fieldIds ?? []).map((fieldId) => `fieldId:${fieldId}`), `testRunId:${evidence.testRunId}`] });
|
|
694
|
-
return { package: pkg, operations, planApproval: persisted, dryRun: ticket09DryRun(evidence, operations), evidence };
|
|
695
|
-
}
|
|
696
|
-
function compileTicket09Package(input) {
|
|
697
|
-
const snapshots = input.operations.map((operation) => {
|
|
698
|
-
const snapshotBase = { id: `snapshot:${operation.id}`, target: operation.target, identity: operation.target, recoveryBoundary: operation.target, state: "absent", baselineFingerprint: input.planApproval.binding.baselineFingerprint };
|
|
699
|
-
return { ...snapshotBase, snapshotHash: digest(snapshotBase) };
|
|
700
|
-
});
|
|
701
|
-
const pkg = compileNativeOperations({
|
|
702
|
-
operations: [...input.operations],
|
|
703
|
-
binding: input.planApproval.binding,
|
|
704
|
-
planApproval: input.planApproval,
|
|
705
|
-
snapshotManifest: snapshots,
|
|
706
|
-
releasePlanHash: digest("ticket09-no-release"),
|
|
707
|
-
recoveryPolicyHash: input.recoveryPolicyHash,
|
|
708
|
-
knowledgePack: { id: TICKET09_FIELD_MUTATION_KNOWLEDGE_PACK_ID, digest: input.knowledgePackDigest },
|
|
709
|
-
capabilityEvidence: input.capabilityEvidence,
|
|
710
|
-
requiredPermissions: ["configuration-write"],
|
|
711
|
-
packageId: input.packageId,
|
|
712
|
-
ledger: input.ledger,
|
|
713
|
-
});
|
|
714
|
-
input.ledger.createPackage(pkg);
|
|
715
|
-
return pkg;
|
|
716
|
-
}
|
|
717
|
-
/** Read-only dry-run projection; never a CloudPivot write and never a publish operation. */
|
|
718
|
-
function ticket09DryRun(evidence, operations) {
|
|
719
|
-
const readbackOperations = [
|
|
720
|
-
{ id: `readback:fields:${evidence.targetSchemaCode}:draft`, method: "GET", path: TICKET09_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"] },
|
|
721
|
-
{ id: `readback:fields:${evidence.targetSchemaCode}:published`, method: "GET", path: TICKET09_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"] },
|
|
722
|
-
];
|
|
723
|
-
const body = JSON.stringify({ execution: "read-only-dry-run", operations, readbackOperations, blocked: [], prohibitedActions: ["cloudpivot-write"] }, null, 2) + "\n";
|
|
724
|
-
return { execution: "read-only-dry-run", operations, readbackOperations, blocked: [], prohibitedActions: ["cloudpivot-write"], artifact: { name: "cloudpivot-ticket09-field-dry-run.json", body, sha256: digest(body), mimeType: "application/json" } };
|
|
725
|
-
}
|
|
726
|
-
export function formatTicket09DeliveryPlanApprovalFrame(approval) {
|
|
727
|
-
const value = requireTicket09Approval(approval, TICKET09_FIELD_MUTATION_ROUTE, TICKET09_FIELD_CREATE_CAPABILITY_ID);
|
|
728
|
-
return appendApprovalHumanBlock(`Approve Ticket 09 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 scopes ${value.allowedOperationScope.join("; ")}; ${fieldFamilyMemberCountWord()} serialized draft typed field-create steps (${fieldFamilyMemberFullProse()}) with required configuration, authoritative two-state readback, per-type behavior validation, and separately approved staged owned cleanup.`, { capability: "ticket09-field-create", kind: "delivery-plan", identities: [value.targetSchemaCode, ...value.targetFieldCodes], scope: [...value.allowedOperationScope] });
|
|
729
|
-
}
|
|
730
|
-
export function formatTicket09EnvironmentChangeApprovalFrame(input) {
|
|
731
|
-
const value = requireTicket09Approval(input.approval, TICKET09_FIELD_MUTATION_ROUTE, TICKET09_FIELD_CREATE_CAPABILITY_ID);
|
|
732
|
-
return appendApprovalHumanBlock(`Approve Ticket 09 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 scopes ${value.allowedOperationScope.join("; ")}; ${fieldFamilyMemberCountWord()} serialized draft typed field-create steps executed by the single Forge configuration writer lease with authoritative readback.`, { capability: "ticket09-field-create", kind: "environment-change", identities: [value.targetSchemaCode, ...value.targetFieldCodes], scope: [...value.allowedOperationScope] });
|
|
733
|
-
}
|
|
734
|
-
export function formatTicket09FieldCleanupDeliveryPlanApprovalFrame(approval) {
|
|
735
|
-
const value = requireTicket09Approval(approval, TICKET09_FIELD_CLEANUP_ROUTE, TICKET09_FIELD_CLEANUP_CAPABILITY_ID);
|
|
736
|
-
return appendApprovalHumanBlock(`Approve Ticket 09 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-create 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("; ")}; force-delete only the owned draft family fields after per-field ownership readback.`, { capability: "ticket09-field-cleanup", kind: "delivery-plan", identities: [value.targetSchemaCode, ...value.targetFieldCodes], scope: [...value.allowedOperationScope] });
|
|
737
|
-
}
|
|
738
|
-
export function formatTicket09FieldCleanupEnvironmentChangeApprovalFrame(input) {
|
|
739
|
-
const value = requireTicket09Approval(input.approval, TICKET09_FIELD_CLEANUP_ROUTE, TICKET09_FIELD_CLEANUP_CAPABILITY_ID);
|
|
740
|
-
return appendApprovalHumanBlock(`Approve Ticket 09 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}; exactly ${fieldFamilyMemberCountWord()} owned draft field force-deletes with draft exact absence readback per field; uncertain outcomes stop, retain, and report.`, { capability: "ticket09-field-cleanup", kind: "environment-change", identities: [value.targetSchemaCode, ...value.targetFieldCodes], scope: [...value.allowedOperationScope] });
|
|
741
|
-
}
|
|
742
|
-
/** Decodes the readback options member defensively: the engine returns a JSON string (filtered). */
|
|
743
|
-
function decodeReadbackOptions(options) {
|
|
744
|
-
if (options === null || options === "")
|
|
745
|
-
return null;
|
|
746
|
-
try {
|
|
747
|
-
return JSON.parse(options);
|
|
748
|
-
}
|
|
749
|
-
catch {
|
|
750
|
-
return options;
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
/** Validates real family field configuration behavior beyond HTTP 2xx: per-field draft-only presence, typed decode, required/default configuration, and per-type options contract — recording the wire shapes verbatim as the probe evidence. */
|
|
754
|
-
export function validateTicket09FieldBehavior(input) {
|
|
755
|
-
const evidence = validateTicket09Evidence(input.evidence);
|
|
756
|
-
const checks = [];
|
|
757
|
-
const observations = [];
|
|
758
|
-
const add = (id, ok, detail) => checks.push({ id, status: ok ? "validated" : "blocked", detail });
|
|
759
|
-
// Ticket 03b: the per-type behavior-check definitions (expected options
|
|
760
|
-
// format, the top-level format synthesis delta, the probe record) are
|
|
761
|
-
// the ACTIVE family's registered member descriptors.
|
|
762
|
-
const members = fieldFamilyMembers();
|
|
763
|
-
input.evidence.targetFields.forEach((expected, index) => {
|
|
764
|
-
const member = members[index];
|
|
765
|
-
const draft = input.draft[index];
|
|
766
|
-
const published = input.published[index];
|
|
767
|
-
const suffix = expected.propertyTypeName.toLowerCase();
|
|
768
|
-
const expectedFormat = member.readbackOptionsFormat;
|
|
769
|
-
const draftPresent = draft.classification === "present" && draft.query.schemaCode === evidence.targetSchemaCode && draft.query.propertyCode === expected.targetFieldCode && draft.query.isPublish === "false" && draft.field?.code === expected.targetFieldCode && draft.field?.schemaCode === evidence.targetSchemaCode;
|
|
770
|
-
add(`draft-field-present-${suffix}`, draftPresent, draftPresent ? `the draft (isPublish=false) readback lists the created ${expected.propertyTypeName} field.` : `the draft readback did not list the created ${expected.propertyTypeName} field.`);
|
|
771
|
-
const publishedAbsent = published.classification === "absent" && published.query.schemaCode === evidence.targetSchemaCode && published.query.propertyCode === expected.targetFieldCode && published.query.isPublish === "true";
|
|
772
|
-
add(`published-field-absent-${suffix}`, publishedAbsent, publishedAbsent ? `the published (isPublish=true) readback does not list the draft ${expected.propertyTypeName} field.` : `the draft ${expected.propertyTypeName} field appeared in the published readback state.`);
|
|
773
|
-
const field = draft.field;
|
|
774
|
-
add(`property-type-${suffix}`, field?.propertyType === expected.propertyType, `propertyType decodes as ${field?.propertyType ?? "<missing>"}; expected ${expected.propertyType} (${expected.propertyTypeName}).`);
|
|
775
|
-
add(`required-configuration-${suffix}`, field?.propertyEmpty === false, `propertyEmpty decodes as ${String(field?.propertyEmpty)}; required configuration was not preserved on the ${expected.propertyTypeName} field.`);
|
|
776
|
-
// The defaultValue gate requires the exact pinned string shape on the
|
|
777
|
-
// wire and records the exact wire type for the matrix's open slots.
|
|
778
|
-
// OQ-5 (type-matrix §12): for selector fields the authoritative FIELD
|
|
779
|
-
// readback defaultValue is a SCALAR (the org empty-string default); an
|
|
780
|
-
// object/{id,name}/[{id,name}] payload would decode as a blocked
|
|
781
|
-
// non-scalar wire shape here and on the create-DTO transport seam.
|
|
782
|
-
const wire = field?.rawDefaultValue;
|
|
783
|
-
const defaultValueOk = wire === expected.fieldDefaultValue;
|
|
784
|
-
add(`default-value-configuration-${suffix}`, defaultValueOk, `defaultValue decodes on the wire as ${JSON.stringify(wire ?? null)}; expected the ${expected.propertyTypeName} scalar shape ${JSON.stringify(expected.fieldDefaultValue)}.`);
|
|
785
|
-
add(`identity-wire-shape-${suffix}`, wire === null || typeof wire === "string", `the ${expected.propertyTypeName} field defaultValue wire shape decodes as ${wire === null ? "null" : typeof wire} (a scalar string); an object/array identity payload is rejected by the governed create-DTO and readback decode contracts.`);
|
|
786
|
-
const decodedOptions = decodeReadbackOptions(field?.options ?? null);
|
|
787
|
-
// The per-member readback options contract is the ACTIVE family module's
|
|
788
|
-
// registered delta: when the member carries a format key it must survive
|
|
789
|
-
// the filtered readback (the top-level format synthesis depends on
|
|
790
|
-
// options.format surviving, BizPropertyController.java:473-486); when the
|
|
791
|
-
// member's delta is format-free the readback options may legitimately be
|
|
792
|
-
// absent/null or carry no format key (matrix §2.4/§3.3) — the verbatim
|
|
793
|
-
// round-trip is what the observation records either way.
|
|
794
|
-
const optionsObject = decodedOptions && typeof decodedOptions === "object" && !Array.isArray(decodedOptions) ? decodedOptions : null;
|
|
795
|
-
const formatOk = expectedFormat !== null
|
|
796
|
-
? optionsObject !== null && optionsObject.format === expectedFormat
|
|
797
|
-
: optionsObject === null || !("format" in optionsObject);
|
|
798
|
-
add(`options-configuration-${suffix}`, formatOk, formatOk ? `the ${expected.propertyTypeName} options round-trip decodes as ${JSON.stringify(decodedOptions)}${expectedFormat === null ? "" : `; the format key survives the filtered readback as "${expectedFormat}"`}.` : `the ${expected.propertyTypeName} options round-trip decodes as ${JSON.stringify(decodedOptions)}${expectedFormat === null ? " (the member's readback delta carries no format key, so the decoded options must not carry one)" : `; the format key must survive the filtered readback as "${expectedFormat}"`}.`);
|
|
799
|
-
if (member.fieldOptions.includes('"optionsSet"')) {
|
|
800
|
-
const selectionJsonOk = (field?.selectionJson ?? null) === null;
|
|
801
|
-
add(`selection-json-null-${suffix}`, selectionJsonOk, selectionJsonOk ? `the ${expected.propertyTypeName} readback keeps selectionJson explicitly null; custom choices remain under options.optionsSet.` : `the ${expected.propertyTypeName} readback did not carry an explicit null selectionJson; OQ-2 requires null.`);
|
|
802
|
-
}
|
|
803
|
-
if (member.topLevelFormatSynthesis && member.topLevelFormatSynthesisDetail) {
|
|
804
|
-
// DATE's readback delta: the controller synthesizes the row's
|
|
805
|
-
// TOP-LEVEL `format` from options.format (473-486) — pinned statically
|
|
806
|
-
// and gated; the observation records the verbatim member.
|
|
807
|
-
const topLevelFormatOk = field?.topLevelFormat === expectedFormat;
|
|
808
|
-
// A synthesis member always pins its options format (family module contract).
|
|
809
|
-
add(`top-level-format-synthesis-${suffix}`, topLevelFormatOk, member.topLevelFormatSynthesisDetail(field?.topLevelFormat ?? null, member.readbackOptionsFormat));
|
|
810
|
-
}
|
|
811
|
-
observations.push({
|
|
812
|
-
propertyTypeName: expected.propertyTypeName,
|
|
813
|
-
propertyType: expected.propertyType,
|
|
814
|
-
fieldCode: expected.targetFieldCode,
|
|
815
|
-
defaultValueWireType: wire === null ? "null" : typeof wire === "string" ? "string" : typeof wire === "number" ? "number" : typeof wire === "boolean" ? "boolean" : "other",
|
|
816
|
-
defaultValueWireValue: wire ?? null,
|
|
817
|
-
optionsRoundTrip: decodedOptions,
|
|
818
|
-
topLevelFormat: field?.topLevelFormat ?? null,
|
|
819
|
-
});
|
|
820
|
-
});
|
|
821
|
-
add("designer-direct-url", evidence.designerDirectUrl.startsWith(`/admin/#/apps/model/${TICKET09_TEST_APPLICATION_CODE}/${evidence.targetSchemaCode}/`), `designer direct URL ${evidence.designerDirectUrl} stays inspectable without publish.`);
|
|
822
|
-
const blocked = checks.find((check) => check.status === "blocked");
|
|
823
|
-
return blocked
|
|
824
|
-
? { status: "blocked", reason: `Ticket 09 behavior validation failed: ${blocked.detail}`, checks, typeMatrixObservations: observations }
|
|
825
|
-
: { status: "validated", checks, typeMatrixObservations: observations };
|
|
826
|
-
}
|
|
827
|
-
/** Execution boundary: named test environment, route-bound package steps, active approvals, no replay of terminal or uncertain packages. */
|
|
828
|
-
export function assertTicket09ExecutionBoundary(input) {
|
|
829
|
-
if (input.environmentName !== "test")
|
|
830
|
-
throw new Error("Ticket 09 execution is restricted to the named test environment; production and staging are rejected.");
|
|
831
|
-
const { projection } = input;
|
|
832
|
-
const routeStepPrefix = input.route === TICKET09_FIELD_MUTATION_ROUTE ? "field:" : "cleanup:field:";
|
|
833
|
-
if (input.steps.length !== TICKET09_FIELD_FAMILY.length || input.steps.some((step) => !step.id.startsWith(routeStepPrefix)))
|
|
834
|
-
throw new Error(`Ticket 09 execution boundary rejected package ${input.packageId}: its steps are not the ${TICKET09_FIELD_FAMILY.length} ${routeStepPrefix} family operations bound to the ${input.route} route.`);
|
|
835
|
-
if (projection.status === "approved" && projection.approvals.plan === "approved" && projection.approvals.environment === "approved")
|
|
836
|
-
return;
|
|
837
|
-
if (projection.status === "completed")
|
|
838
|
-
throw new Error(`Ticket 09 package ${input.packageId} is already terminal completed; replay is rejected.`);
|
|
839
|
-
if (projection.status === "uncertain")
|
|
840
|
-
throw new Error(`Ticket 09 package ${input.packageId} is uncertain; reconcile by authoritative readback before any replay.`);
|
|
841
|
-
if (projection.status === "in_progress")
|
|
842
|
-
throw new Error(`Ticket 09 package ${input.packageId} is already running; concurrent execution is rejected.`);
|
|
843
|
-
const pending = [...projection.blockers];
|
|
844
|
-
if (projection.approvals.plan !== "approved")
|
|
845
|
-
pending.push("Delivery Plan Approval is required before package execution.");
|
|
846
|
-
if (projection.approvals.environment !== "approved")
|
|
847
|
-
pending.push("Environment Change Approval is required before mutation.");
|
|
848
|
-
throw new Error(`Ticket 09 execution boundary rejected package ${input.packageId}: status is ${projection.status} (blocked); ${[...new Set(pending)].join("; ")}.`);
|
|
849
|
-
}
|
|
850
|
-
function ticket09ReportTargetFields(evidence) {
|
|
851
|
-
return evidence.targetFields.map((field) => ({ propertyTypeName: field.propertyTypeName, propertyType: field.propertyType, targetFieldCode: field.targetFieldCode, fieldDefaultValue: field.fieldDefaultValue, fieldOptions: field.fieldOptions }));
|
|
852
|
-
}
|
|
853
|
-
/** Executes the approved two-step family field-create package through the single OMC Host write transport, then validates per-type behavior and records the wire-shape probe. */
|
|
854
|
-
export async function executeTicket09FieldCreatePackage(input) {
|
|
855
|
-
assertTicket09TestFieldMutationEnvironment(input.settings);
|
|
856
|
-
if (typeof input.transport.serializedServerFieldCreate !== "function")
|
|
857
|
-
throw new Error("Ticket 09 execution requires the OMC Host serialized field-create transport; bypass transports are rejected.");
|
|
858
|
-
const projection = input.ledger.project(input.prepared.package.packageId);
|
|
859
|
-
assertTicket09ExecutionBoundary({ projection, environmentName: input.settings.environmentName, packageId: input.prepared.package.packageId, route: TICKET09_FIELD_MUTATION_ROUTE, steps: input.prepared.package.steps });
|
|
860
|
-
if (input.environmentApproval.packageId !== input.prepared.package.packageId || input.environmentApproval.packageHash !== input.prepared.package.packageHash)
|
|
861
|
-
throw new Error("Ticket 09 Environment Change Approval does not match the prepared package.");
|
|
862
|
-
const evidence = input.prepared.evidence;
|
|
863
|
-
for (const field of evidence.targetFields) {
|
|
864
|
-
const probe = await input.readFieldEvidence(field.propertyTypeName, "false");
|
|
865
|
-
if (probe.classification !== "absent" || probe.reason !== "exact-absence")
|
|
866
|
-
throw new Error(`Ticket 09 execution requires exact draft target absence for the ${field.propertyTypeName} family field ${field.targetFieldCode}; it is already present or blocked.`);
|
|
867
|
-
}
|
|
868
|
-
const result = await runNativeConfigurationPackage({
|
|
869
|
-
package: input.prepared.package,
|
|
870
|
-
gate: {
|
|
871
|
-
mode: "approved",
|
|
872
|
-
planApproval: { status: "approved", approvalHash: input.prepared.planApproval.approvalHash },
|
|
873
|
-
environmentApproval: { status: "approved", packageHash: input.environmentApproval.packageHash },
|
|
874
|
-
lease: { owner: "configuration-agent", environment: TICKET09_TEST_ENVIRONMENT_NAME },
|
|
875
|
-
baselineFingerprint: input.prepared.package.binding.baselineFingerprint,
|
|
876
|
-
snapshots: input.prepared.package.snapshotManifest,
|
|
877
|
-
package: input.prepared.package,
|
|
878
|
-
},
|
|
879
|
-
operations: input.prepared.operations,
|
|
880
|
-
ledger: input.ledger,
|
|
881
|
-
planApproval: input.prepared.planApproval,
|
|
882
|
-
transport: input.transport,
|
|
883
|
-
credentials: input.credentials,
|
|
884
|
-
authoritativeOrigin: input.authoritativeOrigin,
|
|
885
|
-
testApplicationCode: TICKET09_TEST_APPLICATION_CODE,
|
|
886
|
-
}, input.signal);
|
|
887
|
-
const mutationExecuted = result.steps.some((step) => step.status === "committed" || step.status === "uncertain");
|
|
888
|
-
const blockers = [...result.blockers];
|
|
889
|
-
let status = ticket09ExecutionStatus(result.status);
|
|
890
|
-
let nextAction = result.nextAction;
|
|
891
|
-
let behavior;
|
|
892
|
-
let typeMatrixObservations;
|
|
893
|
-
if (result.status === "completed") {
|
|
894
|
-
const draft = await Promise.all(evidence.targetFields.map((field) => input.readFieldEvidence(field.propertyTypeName, "false")));
|
|
895
|
-
const published = await Promise.all(evidence.targetFields.map((field) => input.readFieldEvidence(field.propertyTypeName, "true")));
|
|
896
|
-
const validation = validateTicket09FieldBehavior({ evidence, draft, published });
|
|
897
|
-
behavior = validation;
|
|
898
|
-
typeMatrixObservations = validation.typeMatrixObservations;
|
|
899
|
-
if (validation.status !== "validated") {
|
|
900
|
-
status = "blocked";
|
|
901
|
-
blockers.push(validation.reason ?? "Ticket 09 behavior validation failed.");
|
|
902
|
-
nextAction = "reconcile-behavior-retain-and-report-resource";
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
return { reportType: "omc.ticket09.test-field-mutation-execution", schemaVersion: 1, packageId: result.packageId, packageHash: result.packageHash, route: TICKET09_FIELD_MUTATION_ROUTE, testRunId: evidence.testRunId, targetSchemaCode: evidence.targetSchemaCode, targetFields: ticket09ReportTargetFields(evidence), status, mutationExecuted, steps: result.steps, blockers, nextAction, ...(behavior ? { behavior } : {}), ...(typeMatrixObservations ? { typeMatrixObservations } : {}) };
|
|
906
|
-
}
|
|
907
|
-
/** Executes the separately approved owned family field cleanup with per-field draft exact absence readback; uncertainty stops and retains. */
|
|
908
|
-
export async function executeTicket09FieldCleanupPackage(input) {
|
|
909
|
-
assertTicket09TestFieldMutationEnvironment(input.settings);
|
|
910
|
-
if (typeof input.transport.fieldForceDelete !== "function")
|
|
911
|
-
throw new Error("Ticket 09 field cleanup requires the OMC Host draft field force-delete transport; bypass transports are rejected.");
|
|
912
|
-
const projection = input.ledger.project(input.prepared.package.packageId);
|
|
913
|
-
assertTicket09ExecutionBoundary({ projection, environmentName: input.settings.environmentName, packageId: input.prepared.package.packageId, route: TICKET09_FIELD_CLEANUP_ROUTE, steps: input.prepared.package.steps });
|
|
914
|
-
if (input.environmentApproval.packageId !== input.prepared.package.packageId || input.environmentApproval.packageHash !== input.prepared.package.packageHash)
|
|
915
|
-
throw new Error("Ticket 09 field cleanup Environment Change Approval does not match the prepared package.");
|
|
916
|
-
const evidence = input.evidence;
|
|
917
|
-
const probes = await Promise.all(evidence.targetFields.map((field) => input.readFieldEvidence(field.propertyTypeName, "false")));
|
|
918
|
-
if (probes.every((probe) => probe.classification === "absent")) {
|
|
919
|
-
return { reportType: "omc.ticket09.test-field-mutation-execution", schemaVersion: 1, packageId: input.prepared.package.packageId, packageHash: input.prepared.package.packageHash, route: TICKET09_FIELD_CLEANUP_ROUTE, testRunId: evidence.testRunId, targetSchemaCode: evidence.targetSchemaCode, targetFields: ticket09ReportTargetFields(evidence), status: "blocked", mutationExecuted: false, steps: projection.steps, blockers: ["Ticket 09 field cleanup targets are already absent; nothing owned remains to delete and no unbounded deletion is attempted."], nextAction: "accept", absenceReadback: probes };
|
|
920
|
-
}
|
|
921
|
-
const result = await runNativeConfigurationPackage({
|
|
922
|
-
package: input.prepared.package,
|
|
923
|
-
gate: {
|
|
924
|
-
mode: "approved",
|
|
925
|
-
planApproval: { status: "approved", approvalHash: input.prepared.planApproval.approvalHash },
|
|
926
|
-
environmentApproval: { status: "approved", packageHash: input.environmentApproval.packageHash },
|
|
927
|
-
lease: { owner: "configuration-agent", environment: TICKET09_TEST_ENVIRONMENT_NAME },
|
|
928
|
-
baselineFingerprint: input.prepared.package.binding.baselineFingerprint,
|
|
929
|
-
snapshots: input.prepared.package.snapshotManifest,
|
|
930
|
-
package: input.prepared.package,
|
|
931
|
-
},
|
|
932
|
-
operations: input.prepared.operations,
|
|
933
|
-
ledger: input.ledger,
|
|
934
|
-
planApproval: input.prepared.planApproval,
|
|
935
|
-
transport: input.transport,
|
|
936
|
-
credentials: input.credentials,
|
|
937
|
-
authoritativeOrigin: input.authoritativeOrigin,
|
|
938
|
-
testApplicationCode: TICKET09_TEST_APPLICATION_CODE,
|
|
939
|
-
}, input.signal);
|
|
940
|
-
const mutationExecuted = result.steps.some((step) => step.status === "committed" || step.status === "uncertain");
|
|
941
|
-
const blockers = [...result.blockers];
|
|
942
|
-
let status = ticket09ExecutionStatus(result.status);
|
|
943
|
-
let nextAction = result.nextAction;
|
|
944
|
-
let absenceReadback;
|
|
945
|
-
if (result.status === "completed") {
|
|
946
|
-
absenceReadback = await Promise.all(evidence.targetFields.map((field) => input.readFieldEvidence(field.propertyTypeName, "false")));
|
|
947
|
-
if (absenceReadback.some((readback) => readback.classification !== "absent" || readback.reason !== "exact-absence")) {
|
|
948
|
-
status = "uncertain";
|
|
949
|
-
blockers.push("Ticket 09 post-cleanup draft exact absence readback did not confirm absence for every family field; the fixture fields are retained and reported.");
|
|
950
|
-
nextAction = "reconcile";
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
return { reportType: "omc.ticket09.test-field-mutation-execution", schemaVersion: 1, packageId: result.packageId, packageHash: result.packageHash, route: TICKET09_FIELD_CLEANUP_ROUTE, testRunId: evidence.testRunId, targetSchemaCode: evidence.targetSchemaCode, targetFields: ticket09ReportTargetFields(evidence), status, mutationExecuted, steps: result.steps, blockers, nextAction, ...(absenceReadback ? { absenceReadback } : {}) };
|
|
954
|
-
}
|
|
955
|
-
function ticket09ExecutionStatus(status) {
|
|
956
|
-
if (status === "completed" || status === "uncertain" || status === "partial")
|
|
957
|
-
return status;
|
|
958
|
-
return "blocked";
|
|
959
|
-
}
|
|
960
|
-
function requireTicket09Approval(approval, route, capabilityId) {
|
|
961
|
-
const value = approval;
|
|
962
|
-
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)
|
|
963
|
-
throw new Error(`Ticket 09 approval frame is not bound to the exact ${route} route, family targets, folder, and testRunId.`);
|
|
964
|
-
return value;
|
|
965
|
-
}
|
|
966
|
-
function requirePersistedApprovedPlan(ledger, approval) {
|
|
967
|
-
if (approval.status !== "approved")
|
|
968
|
-
throw new Error("Ticket 09 package preparation requires the approved exact Ticket 09 Delivery Plan Approval.");
|
|
969
|
-
const persisted = ledger.findPlanApproval(approval.approvalId);
|
|
970
|
-
if (!persisted || persisted.status !== "approved" || persisted.approvalHash !== approval.approvalHash)
|
|
971
|
-
throw new Error("Ticket 09 package preparation requires the exact authoritative approved Delivery Plan Approval.");
|
|
972
|
-
return persisted;
|
|
973
|
-
}
|
|
974
|
-
function assertTicket09BindingMatchesEvidence(binding, evidence) {
|
|
975
|
-
const expected = {
|
|
976
|
-
deliveryId: ticket09DeliveryId(evidence.testRunId),
|
|
977
|
-
environment: TICKET09_TEST_ENVIRONMENT_NAME,
|
|
978
|
-
baselineFingerprint: evidence.baselineFingerprint,
|
|
979
|
-
environmentFingerprint: evidence.environmentFingerprint,
|
|
980
|
-
discoveryFingerprint: evidence.discoveryFingerprint,
|
|
981
|
-
...(evidence.evidenceArtifactId ? { evidenceArtifactId: evidence.evidenceArtifactId } : {}),
|
|
982
|
-
};
|
|
983
|
-
if (stable(binding) !== stable(expected))
|
|
984
|
-
throw new Error("Ticket 09 plan approval binding is outside the evidence baseline, environment, discovery, or delivery binding.");
|
|
985
|
-
}
|
|
986
|
-
function validateTicket09Evidence(evidence) {
|
|
987
|
-
if (!evidence || evidence.route !== TICKET09_FIELD_MUTATION_ROUTE || evidence.capabilityId !== TICKET09_FIELD_CREATE_CAPABILITY_ID)
|
|
988
|
-
throw new Error("Ticket 09 evidence is not the host-owned field mutation route.");
|
|
989
|
-
if (evidence.environmentName !== "test" || evidence.applicationCode !== "test")
|
|
990
|
-
throw new Error("Ticket 09 evidence is outside the named test environment and dedicated test application.");
|
|
991
|
-
if (!isTicket08TargetSchema(evidence.targetSchemaCode))
|
|
992
|
-
throw new Error("Ticket 09 evidence fixture model is not the host-owned Ticket 08 schema.");
|
|
993
|
-
if (!Array.isArray(evidence.targetFields) || evidence.targetFields.length !== TICKET09_FIELD_FAMILY.length)
|
|
994
|
-
throw new Error("Ticket 09 evidence must carry the exact date-time family enumeration.");
|
|
995
|
-
evidence.targetFields.forEach((field, index) => {
|
|
996
|
-
const member = TICKET09_FIELD_FAMILY[index];
|
|
997
|
-
const expectedOptions = member.resolveFieldOptions
|
|
998
|
-
? member.resolveFieldOptions({ schemaCode: evidence.targetSchemaCode, memberFieldCodes: evidence.targetFields.map((candidate) => candidate.targetFieldCode), testRunId: evidence.testRunId })
|
|
999
|
-
: member.fieldOptions;
|
|
1000
|
-
if (field.propertyTypeName !== member.propertyTypeName || field.propertyType !== member.propertyType || field.controlType !== member.controlType || field.propertyLength !== member.propertyLength || field.fieldOptions !== expectedOptions || !isTicket09TargetField(field.targetFieldCode) || field.fieldName !== field.targetFieldCode)
|
|
1001
|
-
throw new Error(`Ticket 09 evidence family member ${index + 1} diverges from the pinned ${member.propertyTypeName} contract.`);
|
|
1002
|
-
if (field.fieldDefaultValue !== ticket09FieldDefaultValue(member, evidence.testRunId))
|
|
1003
|
-
throw new Error(`Ticket 09 evidence family member ${index + 1} carries a divergent ${member.propertyTypeName} default value.`);
|
|
1004
|
-
if (field.fieldEvidence.classification !== "absent" || field.fieldEvidence.reason !== "exact-absence" || field.fieldEvidence.query.schemaCode !== evidence.targetSchemaCode || field.fieldEvidence.query.propertyCode !== field.targetFieldCode)
|
|
1005
|
-
throw new Error(`Ticket 09 evidence does not carry exact authenticated draft absence for the ${member.propertyTypeName} family field.`);
|
|
1006
|
-
});
|
|
1007
|
-
if (new Set(evidence.targetFields.map((field) => field.targetFieldCode)).size !== TICKET09_FIELD_FAMILY.length)
|
|
1008
|
-
throw new Error("Ticket 09 evidence family field codes must be distinct.");
|
|
1009
|
-
if ([evidence.environmentFingerprint, evidence.baselineFingerprint, evidence.discoveryFingerprint].some((value) => !SHA256.test(value)))
|
|
1010
|
-
throw new Error("Ticket 09 evidence fingerprints are not canonical.");
|
|
1011
|
-
if (evidence.modelPresence.classification !== "present" || evidence.modelPresence.query.schemaCode !== evidence.targetSchemaCode)
|
|
1012
|
-
throw new Error("Ticket 09 evidence does not carry the authoritative fixture model presence handoff.");
|
|
1013
|
-
if (!evidence.folder.id || !evidence.folder.name)
|
|
1014
|
-
throw new Error("Ticket 09 evidence folder binding is missing.");
|
|
1015
|
-
return evidence;
|
|
1016
|
-
}
|
|
1017
|
-
function assertDigest(value, label) {
|
|
1018
|
-
if (typeof value !== "string" || !SHA256.test(value) || !/[1-9a-f]/i.test(value))
|
|
1019
|
-
throw new Error(`Ticket 09 ${label} is invalid.`);
|
|
1020
|
-
}
|
|
1021
|
-
function digest(value) { return createHash("sha256").update(stable(value)).digest("hex"); }
|
|
1022
|
-
function stable(value) { return JSON.stringify(sort(value)); }
|
|
1023
|
-
function sort(value) { if (Array.isArray(value))
|
|
1024
|
-
return value.map(sort); if (value && typeof value === "object")
|
|
1025
|
-
return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => [key, sort(item)])); return value; }
|
|
1026
|
-
//# sourceMappingURL=ticket09-test-field-mutation.js.map
|