103 lines
6.1 KiB
JavaScript
103 lines
6.1 KiB
JavaScript
const sha256 = (value) => typeof value === 'string' && /^[0-9a-f]{64}$/.test(value)
|
|
const canonical = (value) => Array.isArray(value)
|
|
? value.map(canonical)
|
|
: value && typeof value === 'object'
|
|
? Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => [key, canonical(entry)]))
|
|
: value
|
|
|
|
export const productionDriftDecisions = [
|
|
{
|
|
taskId: 'TSN-PROD-DRIFT-000',
|
|
operation: 'cut',
|
|
classification: 'allowed_evolution',
|
|
reasonCode: 'native-downstream-topology-stabilizes-after-edit-restore',
|
|
rationale: 'FreeCAD deterministically evolves the downstream Fillet, Mirrored, and LinearPattern semantic topology after a Pocket Length edit/restore while restoring their geometry; the evolved topology and mapped names remain stable through save, reopen, and resave.',
|
|
},
|
|
{
|
|
taskId: 'TSN-PROD-DRIFT-001',
|
|
operation: 'rotate',
|
|
classification: 'allowed_evolution',
|
|
reasonCode: 'native-downstream-topology-stabilizes-after-edit-restore',
|
|
rationale: 'FreeCAD deterministically evolves the downstream Mirrored and LinearPattern semantic topology after a Rotate Angle edit/restore while restoring their geometry; the evolved topology and mapped names remain stable through save, reopen, and resave.',
|
|
},
|
|
]
|
|
|
|
export const same = (left, right) => JSON.stringify(canonical(left)) === JSON.stringify(canonical(right))
|
|
|
|
const stageFingerprint = (driftReplay, ordinal, phase) => {
|
|
const mutation = driftReplay.mutation
|
|
const persistence = driftReplay.persistence?.[phase]?.[ordinal]
|
|
if (!persistence) throw new Error(`${driftReplay.operation}/${ordinal} lacks ${phase} persistence evidence.`)
|
|
return {
|
|
geometrySignature: persistence.geometrySignature,
|
|
semanticTopologyDigest: persistence.semanticTopologyDigest,
|
|
mappedNames: persistence.mappedNames,
|
|
mappedNameDigest: persistence.mappedNameDigest,
|
|
}
|
|
}
|
|
|
|
export const buildProductionDriftSnapshot = (report, operation) => {
|
|
const driftReplay = report?.driftReplays?.find((entry) => entry.operation === operation)
|
|
if (!driftReplay) throw new Error(`${operation} has no isolated production drift replay.`)
|
|
const mutation = driftReplay.mutation
|
|
const ordinals = mutation.restoreTopologyDriftOrdinals
|
|
if (!Array.isArray(ordinals) || ordinals.length === 0) throw new Error(`${operation} has no recovered production topology drift.`)
|
|
const stages = ordinals.map((ordinal) => ({
|
|
ordinal,
|
|
operation: driftReplay.persistence.initial[ordinal].operation,
|
|
name: driftReplay.persistence.initial[ordinal].name,
|
|
before: {
|
|
geometrySignature: mutation.beforeGeometrySignatures[ordinal],
|
|
semanticTopologyDigest: mutation.beforeTopologyDigests[ordinal],
|
|
},
|
|
edited: {
|
|
geometrySignature: mutation.editedGeometrySignatures[ordinal],
|
|
semanticTopologyDigest: mutation.editedTopologyDigests[ordinal],
|
|
},
|
|
restored: {
|
|
geometrySignature: mutation.restoredGeometrySignatures[ordinal],
|
|
semanticTopologyDigest: mutation.restoredTopologyDigests[ordinal],
|
|
},
|
|
persistence: Object.fromEntries(['initial', 'reopened', 'resaved'].map((phase) => [phase, stageFingerprint(driftReplay, ordinal, phase)])),
|
|
}))
|
|
return {
|
|
operation,
|
|
contract: {
|
|
featureName: mutation.featureName,
|
|
typeId: mutation.typeId,
|
|
propertyPath: mutation.propertyPath,
|
|
},
|
|
values: {
|
|
before: mutation.beforeParameter,
|
|
edited: mutation.editedParameter,
|
|
restored: mutation.restoredParameter,
|
|
},
|
|
driftOrdinals: ordinals,
|
|
stages,
|
|
checks: {
|
|
parameterRestored: mutation.parameterChanged === true && same(mutation.beforeParameter, mutation.restoredParameter),
|
|
geometryRestored: mutation.restoredGeometrically === true && mutation.restoredExactly === true && same(mutation.beforeGeometrySignatures, mutation.restoredGeometrySignatures),
|
|
topologyEvolved: stages.every(({ before, restored }) => before.semanticTopologyDigest !== restored.semanticTopologyDigest),
|
|
restoredMatchesInitial: driftReplay.checks?.restoredMatchesInitial === true && stages.every(({ restored, persistence }) => same(restored.geometrySignature, persistence.initial.geometrySignature) && restored.semanticTopologyDigest === persistence.initial.semanticTopologyDigest),
|
|
persistenceStable: driftReplay.checks?.geometryStable === true && driftReplay.checks?.topologyStable === true && driftReplay.checks?.mappedNamesStable === true && stages.every(({ persistence }) => ['reopened', 'resaved'].every((phase) => same(persistence.initial, persistence[phase]))),
|
|
},
|
|
}
|
|
}
|
|
|
|
export const validateProductionDriftSnapshot = (snapshot) => {
|
|
if (!snapshot || typeof snapshot.operation !== 'string' || !snapshot.operation) throw new Error('production drift snapshot has no operation identity.')
|
|
if (!Array.isArray(snapshot.driftOrdinals) || snapshot.driftOrdinals.length === 0 || snapshot.stages?.length !== snapshot.driftOrdinals.length) throw new Error(`${snapshot.operation} has an invalid drift-stage set.`)
|
|
if (!same(snapshot.stages.map(({ ordinal }) => ordinal), snapshot.driftOrdinals)) throw new Error(`${snapshot.operation} drift-stage identities are inconsistent.`)
|
|
for (const stage of snapshot.stages) {
|
|
if (!Number.isInteger(stage.ordinal) || !stage.operation || !stage.name) throw new Error(`${snapshot.operation} has an invalid stage identity.`)
|
|
for (const phase of ['before', 'edited', 'restored']) {
|
|
if (!Array.isArray(stage[phase]?.geometrySignature) || !sha256(stage[phase]?.semanticTopologyDigest)) throw new Error(`${snapshot.operation}/${stage.name}/${phase} has an invalid fingerprint.`)
|
|
}
|
|
for (const phase of ['initial', 'reopened', 'resaved']) {
|
|
const value = stage.persistence?.[phase]
|
|
if (!Array.isArray(value?.geometrySignature) || !value?.mappedNames || typeof value.mappedNames !== 'object' || !sha256(value?.semanticTopologyDigest) || !sha256(value?.mappedNameDigest)) throw new Error(`${snapshot.operation}/${stage.name}/${phase} has invalid persistence evidence.`)
|
|
}
|
|
}
|
|
if (!Object.values(snapshot.checks ?? {}).every((value) => value === true)) throw new Error(`${snapshot.operation} does not satisfy the allowed-evolution evidence contract.`)
|
|
}
|