import { createHash } from 'node:crypto' import { readFile } from 'node:fs/promises' import { resolve } from 'node:path' const root = resolve(new URL('..', import.meta.url).pathname) const report = JSON.parse(await readFile(resolve(root, 'config/freecad-property-area-inventory.json'), 'utf8')) const fail = (message) => { throw new Error(`FreeCAD PropertyArea inventory check failed: ${message}`) } if (report.schemaVersion !== 1 || report.status !== 'pass' || report.baseline?.freecadVersion !== '1.1.1' || report.baseline?.commit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d' || report.propertyType !== 'App::PropertyArea' || report.recordCount !== 1 || report.classification !== 'opaque-fcstd-proxy') fail('report boundary is invalid') const [record] = report.records ?? [] if (record?.objectTypeId !== 'Measure::MeasureArea' || record.objectAvailable !== true || record.probeStatus !== 'available' || record.propertyName !== 'Area' || record.group !== 'Measurement' || JSON.stringify(record.status) !== JSON.stringify([24, 27]) || JSON.stringify(record.normalizedStatus) !== JSON.stringify(['PropReadOnly', 'PropOutput']) || record.defaultRaw !== '0.0 mm^2') fail('native property identity or status changed') if (record.valueModel?.kind !== 'quantity' || record.valueModel.dimension !== 'length^2' || record.valueModel.defaultValue !== 0 || record.valueModel.defaultUnit !== 'mm^2' || record.valueModel.derived !== true || record.valueModel.writable !== false) fail('area value model is invalid') const [input] = record.inputs ?? [] if (input?.propertyName !== 'Elements' || input.typeId !== 'App::PropertyLinkSubList' || input.group !== 'Measurement' || JSON.stringify(input.defaultValue) !== '[]' || input.scope !== 'Global' || input.allowExternal !== true || input.cardinality !== 'one-or-more') fail('native input contract is incomplete') const [dependency] = record.dependencies ?? [] if (dependency?.sourceProperty !== 'Elements' || dependency.targetProperty !== 'Area' || dependency.relation !== 'link-sub-list' || dependency.effect !== 'immediate-recompute-on-change') fail('dependency contract is incomplete') const applicability = record.applicability if (applicability?.selectionMustBeNonEmpty !== true || applicability.everyElementMustBeValid !== true || JSON.stringify(applicability.supportedMeasureElementTypes) !== JSON.stringify(['PLANE', 'CYLINDER', 'SURFACE', 'VOLUME']) || JSON.stringify(applicability.unsupportedMeasureElementTypes) !== JSON.stringify(['INVALID', 'POINT', 'LINE', 'CURVE']) || applicability.externalDocumentElementsAllowed !== true) fail('selection applicability is incomplete') if (record.execution?.aggregation !== 'sum-area' || record.execution.resultProperty !== 'Area' || record.execution.invalidGeometryDiagnostic !== 'Cannot calculate area' || record.execution.shape !== 'not-applicable-derived-measurement') fail('execution contract is incomplete') for (const locked of [report.provenance?.runtime, ...(report.provenance?.sources ?? [])]) { const content = await readFile(resolve(root, locked?.path ?? '')) if (content.length !== locked.bytes || createHash('sha256').update(content).digest('hex') !== locked.sha256) fail(`provenance is stale for ${locked.path}`) } const sourceText = await readFile(resolve(root, '.cache/freecad/FreeCAD/src/Mod/Measure/App/MeasureArea.cpp'), 'utf8') if (!sourceText.includes('Elements.setScope(App::LinkScope::Global)') || !sourceText.includes('Elements.setAllowExternal(true)') || !sourceText.includes('App::Prop_ReadOnly | App::Prop_Output') || !sourceText.includes('Cannot calculate area')) fail('locked native source no longer exposes the recorded contract') console.log(JSON.stringify({ status: 'freecad-property-area-inventory-pass', propertyType: report.propertyType, objectTypeId: record.objectTypeId, defaultRaw: record.defaultRaw, statusNames: record.normalizedStatus, input: record.inputs[0], applicability: record.applicability, execution: record.execution }, null, 2))