diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..29e83e1
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+working_Web_FreeCAD_Bitbybit.zip filter=lfs diff=lfs merge=lfs -text
diff --git a/chrome-property-linklisthidden-harness.html b/chrome-property-linklisthidden-harness.html
new file mode 100644
index 0000000..ee78222
--- /dev/null
+++ b/chrome-property-linklisthidden-harness.html
@@ -0,0 +1,5 @@
+
+
+
Chrome PropertyLinkListHidden harness
+ Initializing PropertyLinkListHidden harness.
+
diff --git a/config/chrome-property-linklisthidden-verification.json b/config/chrome-property-linklisthidden-verification.json
new file mode 100644
index 0000000..13692c6
--- /dev/null
+++ b/config/chrome-property-linklisthidden-verification.json
@@ -0,0 +1,85 @@
+{
+ "schemaVersion": 1,
+ "status": "pass",
+ "browserId": "chrome",
+ "userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/150.0.0.0 Safari/537.36",
+ "crossOriginIsolated": true,
+ "ui": {
+ "visibleLabelInput": true,
+ "hiddenControlCountBefore": 0,
+ "hiddenControlCountAfter": 0,
+ "hiddenTextPresentBefore": false,
+ "hiddenTextPresentAfter": false,
+ "hiddenStatus": true,
+ "beforeValue": [
+ "ExportA",
+ "ExportB"
+ ],
+ "afterValue": [
+ "ExportB",
+ "ExportA",
+ "ExportB"
+ ],
+ "documentVersion": 2,
+ "dirty": true,
+ "objectState": "touched",
+ "dependencyCount": 0,
+ "errors": []
+ },
+ "persistence": {
+ "mode": "sqlite-opfs",
+ "sqliteWasm": true,
+ "opfs": true,
+ "savedMode": "sqlite-opfs",
+ "loadedValue": [
+ "ExportB",
+ "ExportA",
+ "ExportB"
+ ],
+ "loadedType": "App::PropertyLinkListHidden",
+ "loadedDependencyCount": 0,
+ "checkpointVersion": 2,
+ "recoveryIntegrity": "ok",
+ "fcstdElement": "LinkList",
+ "fcstdValue": [
+ "ExportB",
+ "ExportA",
+ "ExportB"
+ ],
+ "fcstdNativeStatus": 67108864
+ },
+ "resource": {
+ "byteLength": 44,
+ "roundTrip": true,
+ "released": true,
+ "markerRemoved": true
+ },
+ "worker": {
+ "constructed": [
+ {
+ "name": "bitbybit-persistence",
+ "url": "http://127.0.0.1:37495/src/facade/persistenceWorker.ts?worker_file&type=module"
+ }
+ ],
+ "requestTypes": [
+ "initialize",
+ "save-document",
+ "load-document",
+ "load-checkpoint",
+ "recovery-report",
+ "put-resource",
+ "get-resource",
+ "release-resource",
+ "get-resource",
+ "dispose"
+ ],
+ "terminated": [
+ "bitbybit-persistence"
+ ]
+ },
+ "release": {
+ "shapeCount": 0,
+ "kernelReferenceCount": 0,
+ "workerTerminated": true
+ }
+}
diff --git a/config/freecad-active-work-queue.json b/config/freecad-active-work-queue.json
index ff64292..6b5ab3f 100644
--- a/config/freecad-active-work-queue.json
+++ b/config/freecad-active-work-queue.json
@@ -24,12 +24,12 @@
],
"followUpProgress": "config/freecad-follow-up-task-progress.json"
},
- "nextTask": "PROP-app-propertyintegerset-A",
+ "nextTask": "PROP-app-propertylinksubhidden-D",
"summary": {
"tasks": 1756,
- "completed": 586,
+ "completed": 607,
"inProgress": 1,
- "pending": 1169
+ "pending": 1148
},
"milestones": [
{
@@ -8037,7 +8037,7 @@
{
"id": "PROP-app-propertyintegerset-A",
"title": "Lock native inventory and preconditions: App::PropertyIntegerSet",
- "status": "in_progress",
+ "status": "completed",
"dependencies": [
"PROP-app-propertyheatflux-I"
],
@@ -8053,7 +8053,7 @@
{
"id": "PROP-app-propertyintegerset-B",
"title": "Capture native success evidence: App::PropertyIntegerSet",
- "status": "pending",
+ "status": "completed",
"dependencies": [
"PROP-app-propertyintegerset-A"
],
@@ -8069,7 +8069,7 @@
{
"id": "PROP-app-propertyintegerset-C",
"title": "Capture native failure and cancel evidence: App::PropertyIntegerSet",
- "status": "pending",
+ "status": "completed",
"dependencies": [
"PROP-app-propertyintegerset-B"
],
@@ -8085,7 +8085,7 @@
{
"id": "PROP-app-propertyintegerset-D",
"title": "Capture parameter mutation and restore evidence: App::PropertyIntegerSet",
- "status": "pending",
+ "status": "completed",
"dependencies": [
"PROP-app-propertyintegerset-C"
],
@@ -8101,7 +8101,7 @@
{
"id": "PROP-app-propertyintegerset-E",
"title": "Implement the Facade capability: App::PropertyIntegerSet",
- "status": "pending",
+ "status": "completed",
"dependencies": [
"PROP-app-propertyintegerset-D"
],
@@ -8117,7 +8117,7 @@
{
"id": "PROP-app-propertyintegerset-F",
"title": "Close transaction and recovery behavior: App::PropertyIntegerSet",
- "status": "pending",
+ "status": "completed",
"dependencies": [
"PROP-app-propertyintegerset-E"
],
@@ -8133,7 +8133,7 @@
{
"id": "PROP-app-propertyintegerset-G",
"title": "Close FCStd save, reopen and resave: App::PropertyIntegerSet",
- "status": "pending",
+ "status": "completed",
"dependencies": [
"PROP-app-propertyintegerset-F"
],
@@ -8149,7 +8149,7 @@
{
"id": "PROP-app-propertyintegerset-H",
"title": "Replay in a real browser: App::PropertyIntegerSet",
- "status": "pending",
+ "status": "completed",
"dependencies": [
"PROP-app-propertyintegerset-G"
],
@@ -8165,7 +8165,7 @@
{
"id": "PROP-app-propertyintegerset-I",
"title": "Promote capability and synchronize blockers: App::PropertyIntegerSet",
- "status": "pending",
+ "status": "completed",
"dependencies": [
"PROP-app-propertyintegerset-H"
],
@@ -8182,7 +8182,7 @@
{
"id": "PROP-app-propertylinklisthidden-A",
"title": "Lock native inventory and preconditions: App::PropertyLinkListHidden",
- "status": "pending",
+ "status": "completed",
"dependencies": [
"PROP-app-propertyintegerset-I"
],
@@ -8198,7 +8198,7 @@
{
"id": "PROP-app-propertylinklisthidden-B",
"title": "Capture native success evidence: App::PropertyLinkListHidden",
- "status": "pending",
+ "status": "completed",
"dependencies": [
"PROP-app-propertylinklisthidden-A"
],
@@ -8214,7 +8214,7 @@
{
"id": "PROP-app-propertylinklisthidden-C",
"title": "Capture native failure and cancel evidence: App::PropertyLinkListHidden",
- "status": "pending",
+ "status": "completed",
"dependencies": [
"PROP-app-propertylinklisthidden-B"
],
@@ -8230,7 +8230,7 @@
{
"id": "PROP-app-propertylinklisthidden-D",
"title": "Capture parameter mutation and restore evidence: App::PropertyLinkListHidden",
- "status": "pending",
+ "status": "completed",
"dependencies": [
"PROP-app-propertylinklisthidden-C"
],
@@ -8246,7 +8246,7 @@
{
"id": "PROP-app-propertylinklisthidden-E",
"title": "Implement the Facade capability: App::PropertyLinkListHidden",
- "status": "pending",
+ "status": "completed",
"dependencies": [
"PROP-app-propertylinklisthidden-D"
],
@@ -8262,7 +8262,7 @@
{
"id": "PROP-app-propertylinklisthidden-F",
"title": "Close transaction and recovery behavior: App::PropertyLinkListHidden",
- "status": "pending",
+ "status": "completed",
"dependencies": [
"PROP-app-propertylinklisthidden-E"
],
@@ -8278,7 +8278,7 @@
{
"id": "PROP-app-propertylinklisthidden-G",
"title": "Close FCStd save, reopen and resave: App::PropertyLinkListHidden",
- "status": "pending",
+ "status": "completed",
"dependencies": [
"PROP-app-propertylinklisthidden-F"
],
@@ -8294,7 +8294,7 @@
{
"id": "PROP-app-propertylinklisthidden-H",
"title": "Replay in a real browser: App::PropertyLinkListHidden",
- "status": "pending",
+ "status": "completed",
"dependencies": [
"PROP-app-propertylinklisthidden-G"
],
@@ -8310,7 +8310,7 @@
{
"id": "PROP-app-propertylinklisthidden-I",
"title": "Promote capability and synchronize blockers: App::PropertyLinkListHidden",
- "status": "pending",
+ "status": "completed",
"dependencies": [
"PROP-app-propertylinklisthidden-H"
],
@@ -8327,7 +8327,7 @@
{
"id": "PROP-app-propertylinksubhidden-A",
"title": "Lock native inventory and preconditions: App::PropertyLinkSubHidden",
- "status": "pending",
+ "status": "completed",
"dependencies": [
"PROP-app-propertylinklisthidden-I"
],
@@ -8343,7 +8343,7 @@
{
"id": "PROP-app-propertylinksubhidden-B",
"title": "Capture native success evidence: App::PropertyLinkSubHidden",
- "status": "pending",
+ "status": "completed",
"dependencies": [
"PROP-app-propertylinksubhidden-A"
],
@@ -8359,7 +8359,7 @@
{
"id": "PROP-app-propertylinksubhidden-C",
"title": "Capture native failure and cancel evidence: App::PropertyLinkSubHidden",
- "status": "pending",
+ "status": "completed",
"dependencies": [
"PROP-app-propertylinksubhidden-B"
],
@@ -8375,7 +8375,7 @@
{
"id": "PROP-app-propertylinksubhidden-D",
"title": "Capture parameter mutation and restore evidence: App::PropertyLinkSubHidden",
- "status": "pending",
+ "status": "in_progress",
"dependencies": [
"PROP-app-propertylinksubhidden-C"
],
diff --git a/config/freecad-follow-up-task-progress.json b/config/freecad-follow-up-task-progress.json
index 35cacfc..de31392 100644
--- a/config/freecad-follow-up-task-progress.json
+++ b/config/freecad-follow-up-task-progress.json
@@ -777,6 +777,58 @@
{
"id": "PROP-app-propertyintegerset-H",
"evidence": ["config/chrome-property-integerset-verification.json", "check:chrome-property-integerset"]
+ },
+ {
+ "id": "PROP-app-propertyintegerset-I",
+ "evidence": ["config/freecad-property-integerset-promotion.json", "check:freecad-property-integerset-promotion"]
+ },
+ {
+ "id": "PROP-app-propertylinklisthidden-A",
+ "evidence": ["config/freecad-property-linklisthidden-inventory.json", "check:freecad-property-linklisthidden-inventory"]
+ },
+ {
+ "id": "PROP-app-propertylinklisthidden-B",
+ "evidence": ["config/freecad-property-linklisthidden-success.json", "check:freecad-property-linklisthidden-success"]
+ },
+ {
+ "id": "PROP-app-propertylinklisthidden-C",
+ "evidence": ["config/freecad-property-linklisthidden-failure.json", "check:freecad-property-linklisthidden-failure"]
+ },
+ {
+ "id": "PROP-app-propertylinklisthidden-D",
+ "evidence": ["config/freecad-property-linklisthidden-mutation.json", "check:freecad-property-linklisthidden-mutation"]
+ },
+ {
+ "id": "PROP-app-propertylinklisthidden-E",
+ "evidence": ["src/facade/types.ts", "src/facade/fcstd.ts", "src/facade/mockFacade.ts", "src/App.tsx", "tests/propertyLinkListHiddenFacade.test.ts", "test:freecad-property-linklisthidden-facade"]
+ },
+ {
+ "id": "PROP-app-propertylinklisthidden-F",
+ "evidence": ["tests/propertyLinkListHiddenTransaction.test.ts", "test:freecad-property-linklisthidden-transaction"]
+ },
+ {
+ "id": "PROP-app-propertylinklisthidden-G",
+ "evidence": ["config/freecad-property-linklisthidden-roundtrip.json", "check:freecad-property-linklisthidden-roundtrip"]
+ },
+ {
+ "id": "PROP-app-propertylinklisthidden-H",
+ "evidence": ["config/chrome-property-linklisthidden-verification.json", "check:chrome-property-linklisthidden"]
+ },
+ {
+ "id": "PROP-app-propertylinklisthidden-I",
+ "evidence": ["config/freecad-property-linklisthidden-promotion.json", "check:freecad-property-linklisthidden-promotion"]
+ },
+ {
+ "id": "PROP-app-propertylinksubhidden-A",
+ "evidence": ["config/freecad-property-linksubhidden-inventory.json", "check:freecad-property-linksubhidden-inventory"]
+ },
+ {
+ "id": "PROP-app-propertylinksubhidden-B",
+ "evidence": ["config/freecad-property-linksubhidden-success.json", "check:freecad-property-linksubhidden-success"]
+ },
+ {
+ "id": "PROP-app-propertylinksubhidden-C",
+ "evidence": ["config/freecad-property-linksubhidden-failure.json", "check:freecad-property-linksubhidden-failure"]
}
]
}
diff --git a/config/freecad-native-property-semantics.json b/config/freecad-native-property-semantics.json
index e15dd73..f76e7d2 100644
--- a/config/freecad-native-property-semantics.json
+++ b/config/freecad-native-property-semantics.json
@@ -37,16 +37,16 @@
},
"supportSummary": {
"native-editable-codec": {
- "typeCount": 34,
- "recordCount": 4370
+ "typeCount": 35,
+ "recordCount": 4372
},
"native-specialized-codec": {
"typeCount": 5,
"recordCount": 683
},
"opaque-fcstd-proxy": {
- "typeCount": 46,
- "recordCount": 457
+ "typeCount": 45,
+ "recordCount": 455
}
},
"types": [
@@ -327,7 +327,7 @@
},
{
"typeId": "App::PropertyLinkListHidden",
- "support": "opaque-fcstd-proxy",
+ "support": "native-editable-codec",
"recordCount": 2,
"objectTypeCount": 2,
"statusNames": [
@@ -902,10 +902,10 @@
},
"harness": {
"path": "scripts/run-freecad-native-property-semantics.mjs",
- "bytes": 6710,
- "sha256": "7362b5e31dfcfba144c78f525cf5e1c94ab687e2a57dd34118d4428e79867804"
+ "bytes": 6741,
+ "sha256": "7f97a99a107ac51ddbd96d1b7034e5ad365e04fbd8ad6e49bc6db08b50cb3025"
},
"exactPromotionReady": false,
- "exactBlocker": "46 runtime property types and 457 records remain opaque-only; complete native document and property semantics",
- "generatedAt": "2026-08-17T13:03:27.855Z"
+ "exactBlocker": "45 runtime property types and 455 records remain opaque-only; complete native document and property semantics",
+ "generatedAt": "2026-08-17T20:51:42.339Z"
}
diff --git a/config/freecad-property-acceleration-promotion.json b/config/freecad-property-acceleration-promotion.json
index bcc82d6..2e51a1b 100644
--- a/config/freecad-property-acceleration-promotion.json
+++ b/config/freecad-property-acceleration-promotion.json
@@ -54,13 +54,13 @@
"zeroUnknownDrift": true
},
"exactBlockerSync": {
- "nativeEditableTypes": 34,
- "nativeEditableRecords": 4370,
- "opaqueTypes": 46,
- "opaqueRecords": 457,
+ "nativeEditableTypes": 35,
+ "nativeEditableRecords": 4372,
+ "opaqueTypes": 45,
+ "opaqueRecords": 455,
"exactPromotionReady": false,
- "exactBlocker": "46 runtime property types and 457 records remain opaque-only; complete native document and property semantics"
+ "exactBlocker": "45 runtime property types and 455 records remain opaque-only; complete native document and property semantics"
},
"systemExact": false,
- "generatedAt": "2026-08-17T13:03:42.988Z"
+ "generatedAt": "2026-08-17T20:51:52.484Z"
}
diff --git a/config/freecad-property-area-promotion.json b/config/freecad-property-area-promotion.json
index 7b8f095..f5decff 100644
--- a/config/freecad-property-area-promotion.json
+++ b/config/freecad-property-area-promotion.json
@@ -59,13 +59,13 @@
"zeroUnknownDrift": true
},
"exactBlockerSync": {
- "nativeEditableTypes": 34,
- "nativeEditableRecords": 4370,
- "opaqueTypes": 46,
- "opaqueRecords": 457,
+ "nativeEditableTypes": 35,
+ "nativeEditableRecords": 4372,
+ "opaqueTypes": 45,
+ "opaqueRecords": 455,
"exactPromotionReady": false,
- "exactBlocker": "46 runtime property types and 457 records remain opaque-only; complete native document and property semantics"
+ "exactBlocker": "45 runtime property types and 455 records remain opaque-only; complete native document and property semantics"
},
"systemExact": false,
- "generatedAt": "2026-08-17T13:03:43.029Z"
+ "generatedAt": "2026-08-17T20:51:52.476Z"
}
diff --git a/config/freecad-property-boollist-promotion.json b/config/freecad-property-boollist-promotion.json
index 7ac6764..eb43ef7 100644
--- a/config/freecad-property-boollist-promotion.json
+++ b/config/freecad-property-boollist-promotion.json
@@ -68,13 +68,13 @@
"zeroUnknownDrift": true
},
"exactBlockerSync": {
- "nativeEditableTypes": 34,
- "nativeEditableRecords": 4370,
- "opaqueTypes": 46,
- "opaqueRecords": 457,
+ "nativeEditableTypes": 35,
+ "nativeEditableRecords": 4372,
+ "opaqueTypes": 45,
+ "opaqueRecords": 455,
"exactPromotionReady": false,
- "exactBlocker": "46 runtime property types and 457 records remain opaque-only; complete native document and property semantics"
+ "exactBlocker": "45 runtime property types and 455 records remain opaque-only; complete native document and property semantics"
},
"systemExact": false,
- "generatedAt": "2026-08-17T13:03:43.058Z"
+ "generatedAt": "2026-08-17T20:51:52.509Z"
}
diff --git a/config/freecad-property-color-promotion.json b/config/freecad-property-color-promotion.json
index 14c2b35..c6fb397 100644
--- a/config/freecad-property-color-promotion.json
+++ b/config/freecad-property-color-promotion.json
@@ -68,13 +68,13 @@
"zeroUnknownDrift": true
},
"exactBlockerSync": {
- "nativeEditableTypes": 34,
- "nativeEditableRecords": 4370,
- "opaqueTypes": 46,
- "opaqueRecords": 457,
+ "nativeEditableTypes": 35,
+ "nativeEditableRecords": 4372,
+ "opaqueTypes": 45,
+ "opaqueRecords": 455,
"exactPromotionReady": false,
- "exactBlocker": "46 runtime property types and 457 records remain opaque-only; complete native document and property semantics"
+ "exactBlocker": "45 runtime property types and 455 records remain opaque-only; complete native document and property semantics"
},
"systemExact": false,
- "generatedAt": "2026-08-17T13:03:43.033Z"
+ "generatedAt": "2026-08-17T20:51:52.509Z"
}
diff --git a/config/freecad-property-colorlist-promotion.json b/config/freecad-property-colorlist-promotion.json
index 5b9b80d..2ee1a78 100644
--- a/config/freecad-property-colorlist-promotion.json
+++ b/config/freecad-property-colorlist-promotion.json
@@ -85,13 +85,13 @@
"zeroUnknownDrift": true
},
"exactBlockerSync": {
- "nativeEditableTypes": 34,
- "nativeEditableRecords": 4370,
- "opaqueTypes": 46,
- "opaqueRecords": 457,
+ "nativeEditableTypes": 35,
+ "nativeEditableRecords": 4372,
+ "opaqueTypes": 45,
+ "opaqueRecords": 455,
"exactPromotionReady": false,
- "exactBlocker": "46 runtime property types and 457 records remain opaque-only; complete native document and property semantics"
+ "exactBlocker": "45 runtime property types and 455 records remain opaque-only; complete native document and property semantics"
},
"systemExact": false,
- "generatedAt": "2026-08-17T13:03:42.988Z"
+ "generatedAt": "2026-08-17T20:51:52.481Z"
}
diff --git a/config/freecad-property-direction-promotion.json b/config/freecad-property-direction-promotion.json
index 589a65f..bc8558d 100644
--- a/config/freecad-property-direction-promotion.json
+++ b/config/freecad-property-direction-promotion.json
@@ -65,13 +65,13 @@
"zeroUnknownDrift": true
},
"exactBlockerSync": {
- "nativeEditableTypes": 34,
- "nativeEditableRecords": 4370,
- "opaqueTypes": 46,
- "opaqueRecords": 457,
+ "nativeEditableTypes": 35,
+ "nativeEditableRecords": 4372,
+ "opaqueTypes": 45,
+ "opaqueRecords": 455,
"exactPromotionReady": false,
- "exactBlocker": "46 runtime property types and 457 records remain opaque-only; complete native document and property semantics"
+ "exactBlocker": "45 runtime property types and 455 records remain opaque-only; complete native document and property semantics"
},
"systemExact": false,
- "generatedAt": "2026-08-17T13:03:43.033Z"
+ "generatedAt": "2026-08-17T20:51:52.546Z"
}
diff --git a/config/freecad-property-file-promotion.json b/config/freecad-property-file-promotion.json
index b081e5b..f1f58b1 100644
--- a/config/freecad-property-file-promotion.json
+++ b/config/freecad-property-file-promotion.json
@@ -59,13 +59,13 @@
"zeroUnknownDrift": true
},
"exactBlockerSync": {
- "nativeEditableTypes": 34,
- "nativeEditableRecords": 4370,
- "opaqueTypes": 46,
- "opaqueRecords": 457,
+ "nativeEditableTypes": 35,
+ "nativeEditableRecords": 4372,
+ "opaqueTypes": 45,
+ "opaqueRecords": 455,
"exactPromotionReady": false,
- "exactBlocker": "46 runtime property types and 457 records remain opaque-only; complete native document and property semantics"
+ "exactBlocker": "45 runtime property types and 455 records remain opaque-only; complete native document and property semantics"
},
"systemExact": false,
- "generatedAt": "2026-08-17T13:03:43.095Z"
+ "generatedAt": "2026-08-17T20:51:52.501Z"
}
diff --git a/config/freecad-property-fileincluded-promotion.json b/config/freecad-property-fileincluded-promotion.json
index 485e6d2..1d08c44 100644
--- a/config/freecad-property-fileincluded-promotion.json
+++ b/config/freecad-property-fileincluded-promotion.json
@@ -64,13 +64,13 @@
"zeroUnknownDrift": true
},
"exactBlockerSync": {
- "nativeEditableTypes": 34,
- "nativeEditableRecords": 4370,
- "opaqueTypes": 46,
- "opaqueRecords": 457,
+ "nativeEditableTypes": 35,
+ "nativeEditableRecords": 4372,
+ "opaqueTypes": 45,
+ "opaqueRecords": 455,
"exactPromotionReady": false,
- "exactBlocker": "46 runtime property types and 457 records remain opaque-only; complete native document and property semantics"
+ "exactBlocker": "45 runtime property types and 455 records remain opaque-only; complete native document and property semantics"
},
"systemExact": false,
- "generatedAt": "2026-08-17T13:03:43.103Z"
+ "generatedAt": "2026-08-17T20:51:52.483Z"
}
diff --git a/config/freecad-property-font-promotion.json b/config/freecad-property-font-promotion.json
index 42e8b18..db0d872 100644
--- a/config/freecad-property-font-promotion.json
+++ b/config/freecad-property-font-promotion.json
@@ -60,13 +60,13 @@
"zeroUnknownDrift": true
},
"exactBlockerSync": {
- "nativeEditableTypes": 34,
- "nativeEditableRecords": 4370,
- "opaqueTypes": 46,
- "opaqueRecords": 457,
+ "nativeEditableTypes": 35,
+ "nativeEditableRecords": 4372,
+ "opaqueTypes": 45,
+ "opaqueRecords": 455,
"exactPromotionReady": false,
- "exactBlocker": "46 runtime property types and 457 records remain opaque-only; complete native document and property semantics"
+ "exactBlocker": "45 runtime property types and 455 records remain opaque-only; complete native document and property semantics"
},
"systemExact": false,
- "generatedAt": "2026-08-17T13:03:43.084Z"
+ "generatedAt": "2026-08-17T20:51:52.528Z"
}
diff --git a/config/freecad-property-force-promotion.json b/config/freecad-property-force-promotion.json
index 14d2bf9..3980ed5 100644
--- a/config/freecad-property-force-promotion.json
+++ b/config/freecad-property-force-promotion.json
@@ -67,13 +67,13 @@
"zeroUnknownDrift": true
},
"exactBlockerSync": {
- "nativeEditableTypes": 34,
- "nativeEditableRecords": 4370,
- "opaqueTypes": 46,
- "opaqueRecords": 457,
+ "nativeEditableTypes": 35,
+ "nativeEditableRecords": 4372,
+ "opaqueTypes": 45,
+ "opaqueRecords": 455,
"exactPromotionReady": false,
- "exactBlocker": "46 runtime property types and 457 records remain opaque-only; complete native document and property semantics"
+ "exactBlocker": "45 runtime property types and 455 records remain opaque-only; complete native document and property semantics"
},
"systemExact": false,
- "generatedAt": "2026-08-17T13:03:43.093Z"
+ "generatedAt": "2026-08-17T20:51:52.542Z"
}
diff --git a/config/freecad-property-heatflux-promotion.json b/config/freecad-property-heatflux-promotion.json
index ace9636..3acd2bc 100644
--- a/config/freecad-property-heatflux-promotion.json
+++ b/config/freecad-property-heatflux-promotion.json
@@ -66,13 +66,13 @@
"zeroUnknownDrift": true
},
"exactBlockerSync": {
- "nativeEditableTypes": 34,
- "nativeEditableRecords": 4370,
- "opaqueTypes": 46,
- "opaqueRecords": 457,
+ "nativeEditableTypes": 35,
+ "nativeEditableRecords": 4372,
+ "opaqueTypes": 45,
+ "opaqueRecords": 455,
"exactPromotionReady": false,
- "exactBlocker": "46 runtime property types and 457 records remain opaque-only; complete native document and property semantics"
+ "exactBlocker": "45 runtime property types and 455 records remain opaque-only; complete native document and property semantics"
},
"systemExact": false,
- "generatedAt": "2026-08-17T13:03:43.078Z"
+ "generatedAt": "2026-08-17T20:51:52.566Z"
}
diff --git a/config/freecad-property-integerset-promotion.json b/config/freecad-property-integerset-promotion.json
index 393dc5c..3390905 100644
--- a/config/freecad-property-integerset-promotion.json
+++ b/config/freecad-property-integerset-promotion.json
@@ -68,13 +68,13 @@
"zeroUnknownDrift": true
},
"exactBlockerSync": {
- "nativeEditableTypes": 34,
- "nativeEditableRecords": 4370,
- "opaqueTypes": 46,
- "opaqueRecords": 457,
+ "nativeEditableTypes": 35,
+ "nativeEditableRecords": 4372,
+ "opaqueTypes": 45,
+ "opaqueRecords": 455,
"exactPromotionReady": false,
- "exactBlocker": "46 runtime property types and 457 records remain opaque-only; complete native document and property semantics"
+ "exactBlocker": "45 runtime property types and 455 records remain opaque-only; complete native document and property semantics"
},
"systemExact": false,
- "generatedAt": "2026-08-17T13:03:43.129Z"
+ "generatedAt": "2026-08-17T20:51:52.579Z"
}
diff --git a/config/freecad-property-linklisthidden-failure.json b/config/freecad-property-linklisthidden-failure.json
new file mode 100644
index 0000000..1135ce7
--- /dev/null
+++ b/config/freecad-property-linklisthidden-failure.json
@@ -0,0 +1,928 @@
+{
+ "baselineId": "freecad-1.1.1-property-linklisthidden-failure",
+ "cancellationBoundary": {
+ "classification": "synchronous-property-setter",
+ "reason": "no-native-cancel-hook",
+ "replacement": "abort-active-document-transaction",
+ "supported": false
+ },
+ "documentIntegrity": {
+ "finalObjects": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "foreignDocumentObjects": [
+ {
+ "name": "ForeignExport",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "initialObjects": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "objectsPreserved": true
+ },
+ "editorReadOnly": {
+ "after": {
+ "editorMode": [
+ "ReadOnly",
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShapeNull": true,
+ "ownerState": [
+ "Touched"
+ ],
+ "propertyStatus": [
+ "ReadOnly",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Touched",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapeHashes": {
+ "ExportA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ExportB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": [
+ "ExportB"
+ ]
+ },
+ "before": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShapeNull": true,
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapeHashes": {
+ "ExportA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ExportB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": [
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "classification": "editor-read-only-python-setter-accepted",
+ "editorMode": [
+ "ReadOnly",
+ "Hidden"
+ ],
+ "exception": null,
+ "pythonBypassesEditorReadOnly": true,
+ "restoredEditorMode": [
+ "Hidden"
+ ]
+ },
+ "failures": [
+ {
+ "after": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShapeNull": true,
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapeHashes": {
+ "ExportA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ExportB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": [
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "before": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShapeNull": true,
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapeHashes": {
+ "ExportA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ExportB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": [
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "documentPreserved": true,
+ "exception": {
+ "message": "Type must be App.DocumentObject or None, not str",
+ "type": "TypeError"
+ },
+ "id": "string-scalar",
+ "requested": "'ExportA'",
+ "valuePreserved": true
+ },
+ {
+ "after": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShapeNull": true,
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapeHashes": {
+ "ExportA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ExportB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": [
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "before": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShapeNull": true,
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapeHashes": {
+ "ExportA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ExportB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": [
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "documentPreserved": true,
+ "exception": {
+ "message": "Type must be App.DocumentObject or None, not str",
+ "type": "TypeError"
+ },
+ "id": "string-item",
+ "requested": "['ExportA']",
+ "valuePreserved": true
+ },
+ {
+ "after": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShapeNull": true,
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapeHashes": {
+ "ExportA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ExportB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": [
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "before": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShapeNull": true,
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapeHashes": {
+ "ExportA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ExportB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": [
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "documentPreserved": true,
+ "exception": {
+ "message": "Type must be App.DocumentObject or None, not int",
+ "type": "TypeError"
+ },
+ "id": "integer-scalar",
+ "requested": "7",
+ "valuePreserved": true
+ },
+ {
+ "after": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShapeNull": true,
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapeHashes": {
+ "ExportA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ExportB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": [
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "before": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShapeNull": true,
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapeHashes": {
+ "ExportA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ExportB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": [
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "documentPreserved": true,
+ "exception": {
+ "message": "Type must be App.DocumentObject or None, not int",
+ "type": "TypeError"
+ },
+ "id": "integer-item",
+ "requested": "[7]",
+ "valuePreserved": true
+ },
+ {
+ "after": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShapeNull": true,
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapeHashes": {
+ "ExportA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ExportB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": [
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "before": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShapeNull": true,
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapeHashes": {
+ "ExportA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ExportB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": [
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "documentPreserved": true,
+ "exception": {
+ "message": "PropertyLinkList does not support external object",
+ "type": "ValueError"
+ },
+ "id": "cross-document",
+ "requested": "[]",
+ "valuePreserved": true
+ }
+ ],
+ "freecadVersion": "1.1.1",
+ "gitCommit": "0108fd4b4850cc46e625b60e53cea7a7bbe69f8d",
+ "immutable": {
+ "after": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShapeNull": true,
+ "ownerState": [
+ "Touched"
+ ],
+ "propertyStatus": [
+ "Immutable",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Touched",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapeHashes": {
+ "ExportA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ExportB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": [
+ "ExportB"
+ ]
+ },
+ "before": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShapeNull": true,
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "Immutable",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapeHashes": {
+ "ExportA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ExportB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": [
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "classification": "immutable-python-setter-accepted",
+ "exception": null,
+ "pythonBypassesImmutable": true,
+ "restoredStatus": [
+ "26"
+ ]
+ },
+ "initial": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShapeNull": true,
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapeHashes": {
+ "ExportA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ExportB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": [
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "object": {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ "property": {
+ "name": "Exports",
+ "typeId": "App::PropertyLinkListHidden"
+ },
+ "propertyType": "App::PropertyLinkListHidden",
+ "schemaVersion": 1,
+ "status": "pass",
+ "transaction": {
+ "activeAfterAbort": {
+ "id": 0,
+ "name": ""
+ },
+ "activeAfterEdit": {
+ "id": 1,
+ "name": "property-linklisthidden-cancel"
+ },
+ "afterAbort": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShapeNull": true,
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapeHashes": {
+ "ExportA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ExportB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": [
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "afterAbortBeforeRecompute": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShapeNull": true,
+ "ownerState": [
+ "Touched"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Touched",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapeHashes": {
+ "ExportA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ExportB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": [
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "before": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShapeNull": true,
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapeHashes": {
+ "ExportA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ExportB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": [
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "edited": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShapeNull": true,
+ "ownerState": [
+ "Touched"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Touched",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapeHashes": {
+ "ExportA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ExportB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": [
+ "ExportB",
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "pendingAfterAbort": false,
+ "pendingAfterEdit": true,
+ "recomputeAfterAbort": true,
+ "restored": true,
+ "undoMode": 1
+ }
+}
diff --git a/config/freecad-property-linklisthidden-inventory.json b/config/freecad-property-linklisthidden-inventory.json
new file mode 100644
index 0000000..a0f1d9e
--- /dev/null
+++ b/config/freecad-property-linklisthidden-inventory.json
@@ -0,0 +1,148 @@
+{
+ "schemaVersion": 1,
+ "status": "pass",
+ "baseline": {
+ "freecadVersion": "1.1.1",
+ "commit": "0108fd4b4850cc46e625b60e53cea7a7bbe69f8d"
+ },
+ "propertyType": "App::PropertyLinkListHidden",
+ "recordCount": 2,
+ "objectTypeCount": 2,
+ "writableRecordCount": 2,
+ "records": [
+ {
+ "objectTypeId": "Sketcher::SketchObject",
+ "objectAvailable": true,
+ "probeStatus": "available",
+ "propertyName": "Exports",
+ "group": "Sketch",
+ "statusRaw": [
+ 26
+ ],
+ "statusNames": [
+ "PropHidden"
+ ],
+ "defaultRaw": [],
+ "valueModel": {
+ "kind": "hidden-document-object-list",
+ "inheritedFrom": "App::PropertyLinkList",
+ "elementType": "same-document App::DocumentObject reference",
+ "orderPreserved": true,
+ "duplicatesPreserved": true,
+ "writable": true
+ },
+ "inputs": {
+ "nativeCppSetValues": "attached same-document DocumentObject list",
+ "emptyListClears": true,
+ "singleNullListClears": true,
+ "detachedObjectRejected": true,
+ "crossDocumentRejectedWithoutLinkAllowExternal": true
+ },
+ "dependencies": {
+ "linkScope": "Hidden",
+ "createsBacklinks": false,
+ "includedByDefaultGetLinks": false,
+ "includedByGetLinksAll": true,
+ "defaultDagDependency": false
+ },
+ "applicability": {
+ "requiredObjectTypeId": "Sketcher::SketchObject",
+ "role": "Sketch exported child geometry lookup by object name",
+ "propertyWriteRequiresShape": false,
+ "source": "Document.supportedTypes runtime inventory and SketchObject::getSubObject"
+ }
+ },
+ {
+ "objectTypeId": "Sketcher::SketchObjectPython",
+ "objectAvailable": true,
+ "probeStatus": "available",
+ "propertyName": "Exports",
+ "group": "Sketch",
+ "statusRaw": [
+ 26
+ ],
+ "statusNames": [
+ "PropHidden"
+ ],
+ "defaultRaw": [],
+ "valueModel": {
+ "kind": "hidden-document-object-list",
+ "inheritedFrom": "App::PropertyLinkList",
+ "elementType": "same-document App::DocumentObject reference",
+ "orderPreserved": true,
+ "duplicatesPreserved": true,
+ "writable": true
+ },
+ "inputs": {
+ "nativeCppSetValues": "attached same-document DocumentObject list",
+ "emptyListClears": true,
+ "singleNullListClears": true,
+ "detachedObjectRejected": true,
+ "crossDocumentRejectedWithoutLinkAllowExternal": true
+ },
+ "dependencies": {
+ "linkScope": "Hidden",
+ "createsBacklinks": false,
+ "includedByDefaultGetLinks": false,
+ "includedByGetLinksAll": true,
+ "defaultDagDependency": false
+ },
+ "applicability": {
+ "requiredObjectTypeId": "Sketcher::SketchObjectPython",
+ "role": "Sketch exported child geometry lookup by object name",
+ "propertyWriteRequiresShape": false,
+ "source": "Document.supportedTypes runtime inventory and SketchObject::getSubObject"
+ }
+ }
+ ],
+ "typeContract": {
+ "inheritedFrom": "App::PropertyLinkList",
+ "linkScope": "Hidden",
+ "propertyStatus": "PropHidden",
+ "propertyStatusPurpose": "hide the Exports property from the ordinary editor surface",
+ "linkScopePurpose": "exclude Exports from ordinary backlink and dependency traversal",
+ "xmlElement": "LinkList",
+ "xmlChildElement": "Link",
+ "legacyRestoreType": "App::PropertyLinkList"
+ },
+ "provenance": {
+ "runtime": {
+ "path": ".cache/freecad/reference-desktop.json",
+ "bytes": 2298709,
+ "sha256": "082400ec7379e8efc2af16709032d45890d7092277967b714acb05761fd1c2a7"
+ },
+ "sources": [
+ {
+ "path": ".cache/freecad/FreeCAD/src/App/PropertyLinks.h",
+ "bytes": 58046,
+ "sha256": "03176b14948d43fcae3ff290711a913ae4fa43f3ba286d66ceb610e074555d1c"
+ },
+ {
+ "path": ".cache/freecad/FreeCAD/src/App/PropertyLinks.cpp",
+ "bytes": 202779,
+ "sha256": "b4221dd89d2b0b668dc8a3c2852ba53bc67bcb721830e3a6011ea53c1a32edb0"
+ },
+ {
+ "path": ".cache/freecad/FreeCAD/src/Mod/Sketcher/App/SketchObject.h",
+ "bytes": 52959,
+ "sha256": "a21018ddcfc2bda32f15eeadc5c63015677b87cd441e829beae575323fd0fea0"
+ },
+ {
+ "path": ".cache/freecad/FreeCAD/src/Mod/Sketcher/App/SketchObject.cpp",
+ "bytes": 467084,
+ "sha256": "1762c9bb9d7fc54ff171be638094c5f4a50b8d2bcefe3af5cff6010d2478ef9a"
+ }
+ ]
+ },
+ "classification": "opaque-fcstd-proxy",
+ "nextPhases": [
+ "B",
+ "C",
+ "D",
+ "E",
+ "F",
+ "G",
+ "H",
+ "I"
+ ]
+}
diff --git a/config/freecad-property-linklisthidden-mutation.json b/config/freecad-property-linklisthidden-mutation.json
new file mode 100644
index 0000000..ad8de46
--- /dev/null
+++ b/config/freecad-property-linklisthidden-mutation.json
@@ -0,0 +1,663 @@
+{
+ "baselineId": "freecad-1.1.1-property-linklisthidden-mutation",
+ "caseCount": 2,
+ "cases": [
+ {
+ "before": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": "Sketch",
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShape": {
+ "brepSha256": null,
+ "edges": 0,
+ "faces": 0,
+ "isNull": true,
+ "solids": 0,
+ "vertices": 0
+ },
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapes": {
+ "ExportA": {
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ExportB": {
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": [
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "editRecomputeResult": true,
+ "edited": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": "Sketch",
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShape": {
+ "brepSha256": null,
+ "edges": 0,
+ "faces": 0,
+ "isNull": true,
+ "solids": 0,
+ "vertices": 0
+ },
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapes": {
+ "ExportA": {
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ExportB": {
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": [
+ "ExportB",
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "objectName": "SketchProbe",
+ "objectTypeId": "Sketcher::SketchObject",
+ "propertyName": "Exports",
+ "restoreRecomputeResult": true,
+ "restored": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": "Sketch",
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShape": {
+ "brepSha256": null,
+ "edges": 0,
+ "faces": 0,
+ "isNull": true,
+ "solids": 0,
+ "vertices": 0
+ },
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapes": {
+ "ExportA": {
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ExportB": {
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": [
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "semanticStateRestored": true,
+ "touchedAfterEdit": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": "Sketch",
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShape": {
+ "brepSha256": null,
+ "edges": 0,
+ "faces": 0,
+ "isNull": true,
+ "solids": 0,
+ "vertices": 0
+ },
+ "ownerState": [
+ "Touched"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Touched",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapes": {
+ "ExportA": {
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ExportB": {
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": [
+ "ExportB",
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "touchedAfterRestore": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": "Sketch",
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShape": {
+ "brepSha256": null,
+ "edges": 0,
+ "faces": 0,
+ "isNull": true,
+ "solids": 0,
+ "vertices": 0
+ },
+ "ownerState": [
+ "Touched"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Touched",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapes": {
+ "ExportA": {
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ExportB": {
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": [
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "valueRestored": true
+ },
+ {
+ "before": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": "Sketch",
+ "objectSet": [
+ {
+ "name": "SketchPythonProbe",
+ "typeId": "Sketcher::SketchObjectPython"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShape": {
+ "brepSha256": null,
+ "edges": 0,
+ "faces": 0,
+ "isNull": true,
+ "solids": 0,
+ "vertices": 0
+ },
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapes": {
+ "ExportA": {
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ExportB": {
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": [
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "editRecomputeResult": true,
+ "edited": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": "Sketch",
+ "objectSet": [
+ {
+ "name": "SketchPythonProbe",
+ "typeId": "Sketcher::SketchObjectPython"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShape": {
+ "brepSha256": null,
+ "edges": 0,
+ "faces": 0,
+ "isNull": true,
+ "solids": 0,
+ "vertices": 0
+ },
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapes": {
+ "ExportA": {
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ExportB": {
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": [
+ "ExportB",
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "objectName": "SketchPythonProbe",
+ "objectTypeId": "Sketcher::SketchObjectPython",
+ "propertyName": "Exports",
+ "restoreRecomputeResult": true,
+ "restored": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": "Sketch",
+ "objectSet": [
+ {
+ "name": "SketchPythonProbe",
+ "typeId": "Sketcher::SketchObjectPython"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShape": {
+ "brepSha256": null,
+ "edges": 0,
+ "faces": 0,
+ "isNull": true,
+ "solids": 0,
+ "vertices": 0
+ },
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapes": {
+ "ExportA": {
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ExportB": {
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": [
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "semanticStateRestored": true,
+ "touchedAfterEdit": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": "Sketch",
+ "objectSet": [
+ {
+ "name": "SketchPythonProbe",
+ "typeId": "Sketcher::SketchObjectPython"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShape": {
+ "brepSha256": null,
+ "edges": 0,
+ "faces": 0,
+ "isNull": true,
+ "solids": 0,
+ "vertices": 0
+ },
+ "ownerState": [
+ "Touched"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Touched",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapes": {
+ "ExportA": {
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ExportB": {
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": [
+ "ExportB",
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "touchedAfterRestore": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": "Sketch",
+ "objectSet": [
+ {
+ "name": "SketchPythonProbe",
+ "typeId": "Sketcher::SketchObjectPython"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerShape": {
+ "brepSha256": null,
+ "edges": 0,
+ "faces": 0,
+ "isNull": true,
+ "solids": 0,
+ "vertices": 0
+ },
+ "ownerState": [
+ "Touched"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "statusString": "Touched",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapes": {
+ "ExportA": {
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ExportB": {
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": [
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "valueRestored": true
+ }
+ ],
+ "freecadVersion": "1.1.1",
+ "gitCommit": "0108fd4b4850cc46e625b60e53cea7a7bbe69f8d",
+ "propertyType": "App::PropertyLinkListHidden",
+ "schemaVersion": 1,
+ "status": "pass"
+}
diff --git a/config/freecad-property-linklisthidden-promotion.json b/config/freecad-property-linklisthidden-promotion.json
new file mode 100644
index 0000000..2a7ea8d
--- /dev/null
+++ b/config/freecad-property-linklisthidden-promotion.json
@@ -0,0 +1,84 @@
+{
+ "schemaVersion": 1,
+ "status": "pass",
+ "taskId": "PROP-app-propertylinklisthidden-I",
+ "baseline": {
+ "freecadVersion": "1.1.1",
+ "commit": "0108fd4b4850cc46e625b60e53cea7a7bbe69f8d"
+ },
+ "propertyType": "App::PropertyLinkListHidden",
+ "recordCount": 2,
+ "phaseEvidence": {
+ "A": [
+ "config/freecad-property-linklisthidden-inventory.json",
+ "check:freecad-property-linklisthidden-inventory"
+ ],
+ "B": [
+ "config/freecad-property-linklisthidden-success.json",
+ "check:freecad-property-linklisthidden-success"
+ ],
+ "C": [
+ "config/freecad-property-linklisthidden-failure.json",
+ "check:freecad-property-linklisthidden-failure"
+ ],
+ "D": [
+ "config/freecad-property-linklisthidden-mutation.json",
+ "check:freecad-property-linklisthidden-mutation"
+ ],
+ "E": [
+ "src/facade/types.ts",
+ "src/facade/fcstd.ts",
+ "src/facade/mockFacade.ts",
+ "src/App.tsx",
+ "tests/propertyLinkListHiddenFacade.test.ts",
+ "test:freecad-property-linklisthidden-facade"
+ ],
+ "F": [
+ "tests/propertyLinkListHiddenTransaction.test.ts",
+ "test:freecad-property-linklisthidden-transaction"
+ ],
+ "G": [
+ "config/freecad-property-linklisthidden-roundtrip.json",
+ "check:freecad-property-linklisthidden-roundtrip"
+ ],
+ "H": [
+ "config/chrome-property-linklisthidden-verification.json",
+ "check:chrome-property-linklisthidden"
+ ]
+ },
+ "promotion": {
+ "from": "opaque-fcstd-proxy",
+ "to": "native-editable-codec",
+ "facadeValueModel": "ordered-duplicate-preserving-same-document-object-name-array",
+ "writableRecords": 2,
+ "objectTypeCount": 2,
+ "propertyStatus": "PropHidden",
+ "linkScope": "Hidden",
+ "runtimeDependencyEdges": 0,
+ "fcstdElement": "LinkList",
+ "nativeRoundTripValue": [
+ "ExportB",
+ "ExportA",
+ "ExportB"
+ ],
+ "browserRoundTripValue": [
+ "ExportB",
+ "ExportA",
+ "ExportB"
+ ],
+ "hiddenUiFiltered": true,
+ "hiddenLinksPreserved": true,
+ "nativeDependencyMetadataNormalized": true,
+ "zeroUnknownDrift": true
+ },
+ "exactBlockerSync": {
+ "nativeEditableTypes": 35,
+ "nativeEditableRecords": 4372,
+ "opaqueTypes": 45,
+ "opaqueRecords": 455,
+ "exactPromotionReady": false,
+ "exactBlocker": "45 runtime property types and 455 records remain opaque-only; complete native document and property semantics"
+ },
+ "systemExact": false,
+ "generatedAt": "2026-08-17T20:51:52.575Z"
+}
diff --git a/config/freecad-property-linklisthidden-roundtrip.json b/config/freecad-property-linklisthidden-roundtrip.json
new file mode 100644
index 0000000..0ecc9db
--- /dev/null
+++ b/config/freecad-property-linklisthidden-roundtrip.json
@@ -0,0 +1,375 @@
+{
+ "schemaVersion": 1,
+ "status": "pass",
+ "baselineId": "freecad-1.1.1-property-linklisthidden-roundtrip",
+ "freecadVersion": "1.1.1",
+ "gitCommit": "0108fd4b4850cc46e625b60e53cea7a7bbe69f8d",
+ "propertyType": "App::PropertyLinkListHidden",
+ "archives": {
+ "nativeInitial": {
+ "path": ".cache/freecad/property-linklisthidden-roundtrip/native-initial.FCStd",
+ "bytes": 3839,
+ "sha256": "3f9a950c0acadae61581844b35436f0ff0a340a9c1dc358e6659b90092737936"
+ },
+ "webEdited": {
+ "path": ".cache/freecad/property-linklisthidden-roundtrip/web-edited.FCStd",
+ "bytes": 3712,
+ "sha256": "63bbffbee9b7d7e4c9356697251da61cf300f721c8be12027917ab0466a256cc"
+ },
+ "nativeResaved": {
+ "path": ".cache/freecad/property-linklisthidden-roundtrip/native-resaved.FCStd",
+ "bytes": 3851,
+ "sha256": "02329f1ba06f8eaba4e9ec38f632f1cbcbaa342cfe37fca14851f18b74909e41"
+ }
+ },
+ "nativeInitial": {
+ "object": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": "Sketch",
+ "name": "SketchProbe",
+ "outList": [],
+ "propertyName": "Exports",
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "shape": {
+ "area": 0,
+ "brepSha256": null,
+ "edges": 0,
+ "faces": 0,
+ "isNull": true,
+ "shapeType": "Null",
+ "solids": 0,
+ "valid": false,
+ "vertices": 0,
+ "volume": 0
+ },
+ "state": [
+ "Up-to-date"
+ ],
+ "statusString": "Valid",
+ "typeId": "Sketcher::SketchObject",
+ "value": [
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "targets": {
+ "ExportA": {
+ "inList": [],
+ "shape": {
+ "area": 52,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "shapeType": "Solid",
+ "solids": 1,
+ "valid": true,
+ "vertices": 8,
+ "volume": 24
+ },
+ "typeId": "Part::Feature"
+ },
+ "ExportB": {
+ "inList": [],
+ "shape": {
+ "area": 61.261056745,
+ "brepSha256": "82bf07000178ed1f34895ff3e2510f838cd26a6247e2e395fff73feb2117960c",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "shapeType": "Solid",
+ "solids": 1,
+ "valid": true,
+ "vertices": 2,
+ "volume": 35.342917353
+ },
+ "typeId": "Part::Feature"
+ }
+ }
+ },
+ "web": {
+ "before": {
+ "name": "Exports",
+ "typeId": "App::PropertyLinkListHidden",
+ "element": "LinkList",
+ "value": "",
+ "nativeStatus": 67108864,
+ "statusNames": [
+ "PropHidden"
+ ],
+ "links": [
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "after": {
+ "name": "Exports",
+ "typeId": "App::PropertyLinkListHidden",
+ "element": "LinkList",
+ "value": "",
+ "nativeStatus": 67108864,
+ "statusNames": [
+ "PropHidden"
+ ],
+ "links": [
+ "ExportB",
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "targetMarkedTouched": true,
+ "initialDependencyTargets": [
+ "ExportA",
+ "ExportB"
+ ],
+ "webDependencyTargets": [
+ "ExportA",
+ "ExportB"
+ ],
+ "hiddenDependencyMetadataPreserved": true,
+ "webOutputMatches": true,
+ "objectSetPreserved": true,
+ "semanticObjectsPreserved": true,
+ "opaquePathsPreserved": 4,
+ "opaqueEntriesPreserved": true
+ },
+ "nativeAfter": {
+ "reopened": {
+ "object": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": "Sketch",
+ "name": "SketchProbe",
+ "outList": [],
+ "propertyName": "Exports",
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "shape": {
+ "area": 0,
+ "brepSha256": null,
+ "edges": 0,
+ "faces": 0,
+ "isNull": true,
+ "shapeType": "Null",
+ "solids": 0,
+ "valid": false,
+ "vertices": 0,
+ "volume": 0
+ },
+ "state": [
+ "Up-to-date"
+ ],
+ "statusString": "Valid",
+ "typeId": "Sketcher::SketchObject",
+ "value": [
+ "ExportB",
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "targets": {
+ "ExportA": {
+ "inList": [],
+ "shape": {
+ "area": 52,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "shapeType": "Solid",
+ "solids": 1,
+ "valid": true,
+ "vertices": 8,
+ "volume": 24
+ },
+ "typeId": "Part::Feature"
+ },
+ "ExportB": {
+ "inList": [],
+ "shape": {
+ "area": 61.261056745,
+ "brepSha256": "82bf07000178ed1f34895ff3e2510f838cd26a6247e2e395fff73feb2117960c",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "shapeType": "Solid",
+ "solids": 1,
+ "valid": true,
+ "vertices": 2,
+ "volume": 35.342917353
+ },
+ "typeId": "Part::Feature"
+ }
+ }
+ },
+ "resaved": {
+ "object": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": "Sketch",
+ "name": "SketchProbe",
+ "outList": [],
+ "propertyName": "Exports",
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "shape": {
+ "area": 0,
+ "brepSha256": null,
+ "edges": 0,
+ "faces": 0,
+ "isNull": true,
+ "shapeType": "Null",
+ "solids": 0,
+ "valid": false,
+ "vertices": 0,
+ "volume": 0
+ },
+ "state": [
+ "Up-to-date"
+ ],
+ "statusString": "Valid",
+ "typeId": "Sketcher::SketchObject",
+ "value": [
+ "ExportB",
+ "ExportA",
+ "ExportB"
+ ]
+ },
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "targets": {
+ "ExportA": {
+ "inList": [],
+ "shape": {
+ "area": 52,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "shapeType": "Solid",
+ "solids": 1,
+ "valid": true,
+ "vertices": 8,
+ "volume": 24
+ },
+ "typeId": "Part::Feature"
+ },
+ "ExportB": {
+ "inList": [],
+ "shape": {
+ "area": 61.261056745,
+ "brepSha256": "82bf07000178ed1f34895ff3e2510f838cd26a6247e2e395fff73feb2117960c",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "shapeType": "Solid",
+ "solids": 1,
+ "valid": true,
+ "vertices": 2,
+ "volume": 35.342917353
+ },
+ "typeId": "Part::Feature"
+ }
+ }
+ }
+ },
+ "classification": {
+ "requestedValue": [
+ "ExportB",
+ "ExportA",
+ "ExportB"
+ ],
+ "webValue": [
+ "ExportB",
+ "ExportA",
+ "ExportB"
+ ],
+ "reopenedValue": [
+ "ExportB",
+ "ExportA",
+ "ExportB"
+ ],
+ "resavedValue": [
+ "ExportB",
+ "ExportA",
+ "ExportB"
+ ],
+ "nativeOutputMatches": true,
+ "hiddenLinksPreserved": true,
+ "nativeResavedDependencyTargets": [
+ "ExportB",
+ "ExportA",
+ "ExportB"
+ ],
+ "nativeDependencyMetadataNormalized": true,
+ "targetShapeStructurePreserved": true,
+ "ownerShapePreserved": true,
+ "nativeTargetBrepHashes": [
+ {
+ "ExportA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ExportB": "82bf07000178ed1f34895ff3e2510f838cd26a6247e2e395fff73feb2117960c"
+ },
+ {
+ "ExportA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ExportB": "82bf07000178ed1f34895ff3e2510f838cd26a6247e2e395fff73feb2117960c"
+ },
+ {
+ "ExportA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ExportB": "82bf07000178ed1f34895ff3e2510f838cd26a6247e2e395fff73feb2117960c"
+ }
+ ],
+ "unknownSemanticDrift": false,
+ "zeroUnknownDrift": true
+ }
+}
diff --git a/config/freecad-property-linklisthidden-success.json b/config/freecad-property-linklisthidden-success.json
new file mode 100644
index 0000000..bd11114
--- /dev/null
+++ b/config/freecad-property-linklisthidden-success.json
@@ -0,0 +1,691 @@
+{
+ "baselineId": "freecad-1.1.1-property-linklisthidden-success",
+ "bindingBoundary": {
+ "oracleDefault": [],
+ "safeFirstRead": "assign-empty-list-before-read"
+ },
+ "caseCount": 2,
+ "cases": [
+ {
+ "group": "Sketch",
+ "mode": "direct-native-property-setter",
+ "objectName": "SketchProbe",
+ "objectTypeId": "Sketcher::SketchObject",
+ "phases": {
+ "duplicates": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": "Sketch",
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "recomputeResult": true,
+ "shape": {
+ "applicable": true,
+ "brepSha256": null,
+ "edges": 0,
+ "faces": 0,
+ "isNull": true,
+ "solids": 0,
+ "vertices": 0
+ },
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapes": {
+ "ExportA": {
+ "applicable": true,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ExportB": {
+ "applicable": true,
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": [
+ "ExportA",
+ "ExportB",
+ "ExportA"
+ ]
+ },
+ "explicitEmpty": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": "Sketch",
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "recomputeResult": true,
+ "shape": {
+ "applicable": true,
+ "brepSha256": null,
+ "edges": 0,
+ "faces": 0,
+ "isNull": true,
+ "solids": 0,
+ "vertices": 0
+ },
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapes": {
+ "ExportA": {
+ "applicable": true,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ExportB": {
+ "applicable": true,
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": []
+ },
+ "ordered": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": "Sketch",
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "recomputeResult": true,
+ "shape": {
+ "applicable": true,
+ "brepSha256": null,
+ "edges": 0,
+ "faces": 0,
+ "isNull": true,
+ "solids": 0,
+ "vertices": 0
+ },
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapes": {
+ "ExportA": {
+ "applicable": true,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ExportB": {
+ "applicable": true,
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": [
+ "ExportB",
+ "ExportA"
+ ]
+ },
+ "restored": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": "Sketch",
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "recomputeResult": true,
+ "shape": {
+ "applicable": true,
+ "brepSha256": null,
+ "edges": 0,
+ "faces": 0,
+ "isNull": true,
+ "solids": 0,
+ "vertices": 0
+ },
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapes": {
+ "ExportA": {
+ "applicable": true,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ExportB": {
+ "applicable": true,
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": []
+ },
+ "single": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": "Sketch",
+ "objectSet": [
+ {
+ "name": "SketchProbe",
+ "typeId": "Sketcher::SketchObject"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "recomputeResult": true,
+ "shape": {
+ "applicable": true,
+ "brepSha256": null,
+ "edges": 0,
+ "faces": 0,
+ "isNull": true,
+ "solids": 0,
+ "vertices": 0
+ },
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapes": {
+ "ExportA": {
+ "applicable": true,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ExportB": {
+ "applicable": true,
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": [
+ "ExportA"
+ ]
+ }
+ },
+ "propertyName": "Exports"
+ },
+ {
+ "group": "Sketch",
+ "mode": "direct-native-property-setter",
+ "objectName": "SketchPythonProbe",
+ "objectTypeId": "Sketcher::SketchObjectPython",
+ "phases": {
+ "duplicates": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": "Sketch",
+ "objectSet": [
+ {
+ "name": "SketchPythonProbe",
+ "typeId": "Sketcher::SketchObjectPython"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "recomputeResult": true,
+ "shape": {
+ "applicable": true,
+ "brepSha256": null,
+ "edges": 0,
+ "faces": 0,
+ "isNull": true,
+ "solids": 0,
+ "vertices": 0
+ },
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapes": {
+ "ExportA": {
+ "applicable": true,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ExportB": {
+ "applicable": true,
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": [
+ "ExportA",
+ "ExportB",
+ "ExportA"
+ ]
+ },
+ "explicitEmpty": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": "Sketch",
+ "objectSet": [
+ {
+ "name": "SketchPythonProbe",
+ "typeId": "Sketcher::SketchObjectPython"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "recomputeResult": true,
+ "shape": {
+ "applicable": true,
+ "brepSha256": null,
+ "edges": 0,
+ "faces": 0,
+ "isNull": true,
+ "solids": 0,
+ "vertices": 0
+ },
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapes": {
+ "ExportA": {
+ "applicable": true,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ExportB": {
+ "applicable": true,
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": []
+ },
+ "ordered": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": "Sketch",
+ "objectSet": [
+ {
+ "name": "SketchPythonProbe",
+ "typeId": "Sketcher::SketchObjectPython"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "recomputeResult": true,
+ "shape": {
+ "applicable": true,
+ "brepSha256": null,
+ "edges": 0,
+ "faces": 0,
+ "isNull": true,
+ "solids": 0,
+ "vertices": 0
+ },
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapes": {
+ "ExportA": {
+ "applicable": true,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ExportB": {
+ "applicable": true,
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": [
+ "ExportB",
+ "ExportA"
+ ]
+ },
+ "restored": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": "Sketch",
+ "objectSet": [
+ {
+ "name": "SketchPythonProbe",
+ "typeId": "Sketcher::SketchObjectPython"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "recomputeResult": true,
+ "shape": {
+ "applicable": true,
+ "brepSha256": null,
+ "edges": 0,
+ "faces": 0,
+ "isNull": true,
+ "solids": 0,
+ "vertices": 0
+ },
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapes": {
+ "ExportA": {
+ "applicable": true,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ExportB": {
+ "applicable": true,
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": []
+ },
+ "single": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": "Sketch",
+ "objectSet": [
+ {
+ "name": "SketchPythonProbe",
+ "typeId": "Sketcher::SketchObjectPython"
+ },
+ {
+ "name": "ExportA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ExportB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkListHidden",
+ "recomputeResult": true,
+ "shape": {
+ "applicable": true,
+ "brepSha256": null,
+ "edges": 0,
+ "faces": 0,
+ "isNull": true,
+ "solids": 0,
+ "vertices": 0
+ },
+ "statusString": "Valid",
+ "targetInLists": {
+ "ExportA": [],
+ "ExportB": []
+ },
+ "targetShapes": {
+ "ExportA": {
+ "applicable": true,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ExportB": {
+ "applicable": true,
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": [
+ "ExportA"
+ ]
+ }
+ },
+ "propertyName": "Exports"
+ }
+ ],
+ "freecadVersion": "1.1.1",
+ "gitCommit": "0108fd4b4850cc46e625b60e53cea7a7bbe69f8d",
+ "propertyType": "App::PropertyLinkListHidden",
+ "schemaVersion": 1,
+ "status": "pass"
+}
diff --git a/config/freecad-property-linksubhidden-failure.json b/config/freecad-property-linksubhidden-failure.json
new file mode 100644
index 0000000..511e917
--- /dev/null
+++ b/config/freecad-property-linksubhidden-failure.json
@@ -0,0 +1,1100 @@
+{
+ "baselineId": "freecad-1.1.1-property-linksubhidden-failure",
+ "cancellationBoundary": {
+ "classification": "synchronous-property-setter",
+ "reason": "no-native-cancel-hook",
+ "replacement": "abort-active-document-transaction",
+ "supported": false
+ },
+ "case": {
+ "baseline": {
+ "object": "ColorSourceA",
+ "subElements": [
+ "Face1",
+ "Face3"
+ ]
+ },
+ "crossDocument": {
+ "accepted": false,
+ "after": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "LinkGroupProbe",
+ "typeId": "App::LinkGroup"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapeHashes": {
+ "ColorSourceA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ColorSourceB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": [
+ "Face1",
+ "Face3"
+ ]
+ }
+ },
+ "before": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "LinkGroupProbe",
+ "typeId": "App::LinkGroup"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapeHashes": {
+ "ColorSourceA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ColorSourceB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": [
+ "Face1",
+ "Face3"
+ ]
+ }
+ },
+ "documentPreserved": true,
+ "error": {
+ "message": "PropertyLinkSub does not support external object",
+ "type": "ValueError"
+ },
+ "valuePreserved": true
+ },
+ "documentIntegrity": {
+ "finalObjects": [
+ {
+ "name": "LinkGroupProbe",
+ "typeId": "App::LinkGroup"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "foreignDocumentObjects": [
+ {
+ "name": "ExternalSource",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "initialObjects": [
+ {
+ "name": "LinkGroupProbe",
+ "typeId": "App::LinkGroup"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "objectsPreserved": true
+ },
+ "failures": [
+ {
+ "accepted": false,
+ "after": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "LinkGroupProbe",
+ "typeId": "App::LinkGroup"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapeHashes": {
+ "ColorSourceA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ColorSourceB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": [
+ "Face1",
+ "Face3"
+ ]
+ }
+ },
+ "before": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "LinkGroupProbe",
+ "typeId": "App::LinkGroup"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapeHashes": {
+ "ColorSourceA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ColorSourceB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": [
+ "Face1",
+ "Face3"
+ ]
+ }
+ },
+ "documentPreserved": true,
+ "error": {
+ "message": "type must be 'DocumentObject', 'NoneType' or ('DocumentObject',['String',]) not str",
+ "type": "TypeError"
+ },
+ "name": "scalar",
+ "requested": "'ColorSourceA'",
+ "valuePreserved": true
+ },
+ {
+ "accepted": true,
+ "after": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "LinkGroupProbe",
+ "typeId": "App::LinkGroup"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Touched"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "statusString": "Touched",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapeHashes": {
+ "ColorSourceA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ColorSourceB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": [
+ "Face1"
+ ]
+ }
+ },
+ "before": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "LinkGroupProbe",
+ "typeId": "App::LinkGroup"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapeHashes": {
+ "ColorSourceA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ColorSourceB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": [
+ "Face1",
+ "Face3"
+ ]
+ }
+ },
+ "documentPreserved": false,
+ "error": null,
+ "name": "wrongSubList",
+ "requested": "(, 'Face1')",
+ "valuePreserved": false
+ },
+ {
+ "accepted": true,
+ "after": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "LinkGroupProbe",
+ "typeId": "App::LinkGroup"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Touched"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "statusString": "Touched",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapeHashes": {
+ "ColorSourceA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ColorSourceB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": [
+ "MissingFace"
+ ]
+ }
+ },
+ "before": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "LinkGroupProbe",
+ "typeId": "App::LinkGroup"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapeHashes": {
+ "ColorSourceA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ColorSourceB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": [
+ "Face1",
+ "Face3"
+ ]
+ }
+ },
+ "documentPreserved": false,
+ "error": null,
+ "name": "unknownTuple",
+ "requested": "(, ['MissingFace'])",
+ "valuePreserved": false
+ }
+ ],
+ "immutable": {
+ "accepted": false,
+ "after": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "LinkGroupProbe",
+ "typeId": "App::LinkGroup"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "Immutable",
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapeHashes": {
+ "ColorSourceA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ColorSourceB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": [
+ "Face1",
+ "Face3"
+ ]
+ }
+ },
+ "before": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "LinkGroupProbe",
+ "typeId": "App::LinkGroup"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "Immutable",
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapeHashes": {
+ "ColorSourceA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ColorSourceB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": [
+ "Face1",
+ "Face3"
+ ]
+ }
+ },
+ "error": {
+ "message": "Object attribute 'ColoredElements' is read-only",
+ "type": "AttributeError"
+ },
+ "pythonBypassesImmutable": false,
+ "status": [
+ "Immutable",
+ "LockDynamic",
+ "26"
+ ]
+ },
+ "initial": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "LinkGroupProbe",
+ "typeId": "App::LinkGroup"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapeHashes": {
+ "ColorSourceA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ColorSourceB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": [
+ "Face1",
+ "Face3"
+ ]
+ }
+ },
+ "objectName": "LinkGroupProbe",
+ "objectTypeId": "App::LinkGroup",
+ "propertyName": "ColoredElements",
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "readOnly": {
+ "accepted": true,
+ "after": {
+ "editorMode": [
+ "ReadOnly",
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "LinkGroupProbe",
+ "typeId": "App::LinkGroup"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Touched"
+ ],
+ "propertyStatus": [
+ "ReadOnly",
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "statusString": "Touched",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapeHashes": {
+ "ColorSourceA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ColorSourceB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": {
+ "object": "ColorSourceB",
+ "subElements": [
+ "Face2"
+ ]
+ }
+ },
+ "before": {
+ "editorMode": [
+ "ReadOnly",
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "LinkGroupProbe",
+ "typeId": "App::LinkGroup"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "ReadOnly",
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapeHashes": {
+ "ColorSourceA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ColorSourceB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": [
+ "Face1",
+ "Face3"
+ ]
+ }
+ },
+ "error": null,
+ "pythonBypassesEditorReadOnly": true,
+ "status": [
+ "ReadOnly",
+ "LockDynamic",
+ "26"
+ ]
+ },
+ "transaction": {
+ "activeAfterAbort": {
+ "id": 0,
+ "name": ""
+ },
+ "activeAfterEdit": {
+ "id": 1,
+ "name": "property-linksubhidden-cancel"
+ },
+ "afterAbort": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "LinkGroupProbe",
+ "typeId": "App::LinkGroup"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapeHashes": {
+ "ColorSourceA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ColorSourceB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": [
+ "Face1",
+ "Face3"
+ ]
+ }
+ },
+ "afterAbortBeforeRecompute": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "LinkGroupProbe",
+ "typeId": "App::LinkGroup"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Touched"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "statusString": "Touched",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapeHashes": {
+ "ColorSourceA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ColorSourceB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": [
+ "Face1",
+ "Face3"
+ ]
+ }
+ },
+ "before": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "LinkGroupProbe",
+ "typeId": "App::LinkGroup"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapeHashes": {
+ "ColorSourceA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ColorSourceB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": [
+ "Face1",
+ "Face3"
+ ]
+ }
+ },
+ "edited": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "LinkGroupProbe",
+ "typeId": "App::LinkGroup"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Touched"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "statusString": "Touched",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapeHashes": {
+ "ColorSourceA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ColorSourceB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": {
+ "object": "ColorSourceB",
+ "subElements": [
+ "Face5"
+ ]
+ }
+ },
+ "pendingAfterAbort": false,
+ "pendingAfterEdit": true,
+ "recomputeAfterAbort": true,
+ "restored": true,
+ "undoMode": 1
+ }
+ },
+ "caseCount": 1,
+ "disabled": {
+ "classification": "editor-read-only-python-mutable-until-immutable",
+ "immutable": {
+ "accepted": false,
+ "after": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "LinkGroupProbe",
+ "typeId": "App::LinkGroup"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "Immutable",
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapeHashes": {
+ "ColorSourceA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ColorSourceB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": [
+ "Face1",
+ "Face3"
+ ]
+ }
+ },
+ "before": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "LinkGroupProbe",
+ "typeId": "App::LinkGroup"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "Immutable",
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapeHashes": {
+ "ColorSourceA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ColorSourceB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": [
+ "Face1",
+ "Face3"
+ ]
+ }
+ },
+ "error": {
+ "message": "Object attribute 'ColoredElements' is read-only",
+ "type": "AttributeError"
+ },
+ "pythonBypassesImmutable": false,
+ "status": [
+ "Immutable",
+ "LockDynamic",
+ "26"
+ ]
+ },
+ "immutableSetter": "rejected",
+ "readOnly": {
+ "accepted": true,
+ "after": {
+ "editorMode": [
+ "ReadOnly",
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "LinkGroupProbe",
+ "typeId": "App::LinkGroup"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Touched"
+ ],
+ "propertyStatus": [
+ "ReadOnly",
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "statusString": "Touched",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapeHashes": {
+ "ColorSourceA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ColorSourceB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": {
+ "object": "ColorSourceB",
+ "subElements": [
+ "Face2"
+ ]
+ }
+ },
+ "before": {
+ "editorMode": [
+ "ReadOnly",
+ "Hidden"
+ ],
+ "objectSet": [
+ {
+ "name": "LinkGroupProbe",
+ "typeId": "App::LinkGroup"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "ReadOnly",
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapeHashes": {
+ "ColorSourceA": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "ColorSourceB": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851"
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": [
+ "Face1",
+ "Face3"
+ ]
+ }
+ },
+ "error": null,
+ "pythonBypassesEditorReadOnly": true,
+ "status": [
+ "ReadOnly",
+ "LockDynamic",
+ "26"
+ ]
+ },
+ "readOnlySetter": "accepted"
+ },
+ "freecadVersion": "1.1.1",
+ "gitCommit": "0108fd4b4850cc46e625b60e53cea7a7bbe69f8d",
+ "propertyType": "App::PropertyLinkSubHidden",
+ "schemaVersion": 1,
+ "status": "pass"
+}
diff --git a/config/freecad-property-linksubhidden-inventory.json b/config/freecad-property-linksubhidden-inventory.json
new file mode 100644
index 0000000..28e1fca
--- /dev/null
+++ b/config/freecad-property-linksubhidden-inventory.json
@@ -0,0 +1,246 @@
+{
+ "schemaVersion": 1,
+ "status": "pass",
+ "baseline": {
+ "freecadVersion": "1.1.1",
+ "commit": "0108fd4b4850cc46e625b60e53cea7a7bbe69f8d"
+ },
+ "propertyType": "App::PropertyLinkSubHidden",
+ "recordCount": 4,
+ "objectTypeCount": 4,
+ "writableRecordCount": 4,
+ "records": [
+ {
+ "objectTypeId": "App::Link",
+ "objectAvailable": true,
+ "probeStatus": "available",
+ "propertyName": "ColoredElements",
+ "group": " Link",
+ "statusRaw": [
+ "LockDynamic",
+ 26
+ ],
+ "statusNames": [
+ "LockDynamic",
+ "PropHidden"
+ ],
+ "defaultRaw": null,
+ "valueModel": {
+ "kind": "hidden-document-object-sub-element-reference",
+ "inheritedFrom": "App::PropertyLinkSub",
+ "elementType": "same-document App::DocumentObject plus ordered sub-element names",
+ "orderPreserved": true,
+ "duplicatesPreserved": false,
+ "writable": true
+ },
+ "inputs": {
+ "nativeCppSetValues": "attached same-document DocumentObject plus sub-element string list",
+ "emptySubListMeansWholeObject": true,
+ "nullObjectClears": true,
+ "detachedObjectRejected": true,
+ "crossDocumentRejectedWithoutLinkAllowExternal": true,
+ "invalidSubElementAcceptedAsReference": true
+ },
+ "dependencies": {
+ "linkScope": "Hidden",
+ "createsBacklinks": false,
+ "includedByDefaultGetLinks": false,
+ "includedByGetLinksAll": true,
+ "defaultDagDependency": false
+ },
+ "applicability": {
+ "requiredObjectTypeId": "App::Link",
+ "role": "Link colored element override lookup",
+ "propertyWriteRequiresShape": false,
+ "source": "Link extension and Draft colored element implementation"
+ }
+ },
+ {
+ "objectTypeId": "App::LinkGroup",
+ "objectAvailable": true,
+ "probeStatus": "available",
+ "propertyName": "ColoredElements",
+ "group": " Link",
+ "statusRaw": [
+ "LockDynamic",
+ 26
+ ],
+ "statusNames": [
+ "LockDynamic",
+ "PropHidden"
+ ],
+ "defaultRaw": null,
+ "valueModel": {
+ "kind": "hidden-document-object-sub-element-reference",
+ "inheritedFrom": "App::PropertyLinkSub",
+ "elementType": "same-document App::DocumentObject plus ordered sub-element names",
+ "orderPreserved": true,
+ "duplicatesPreserved": false,
+ "writable": true
+ },
+ "inputs": {
+ "nativeCppSetValues": "attached same-document DocumentObject plus sub-element string list",
+ "emptySubListMeansWholeObject": true,
+ "nullObjectClears": true,
+ "detachedObjectRejected": true,
+ "crossDocumentRejectedWithoutLinkAllowExternal": true,
+ "invalidSubElementAcceptedAsReference": true
+ },
+ "dependencies": {
+ "linkScope": "Hidden",
+ "createsBacklinks": false,
+ "includedByDefaultGetLinks": false,
+ "includedByGetLinksAll": true,
+ "defaultDagDependency": false
+ },
+ "applicability": {
+ "requiredObjectTypeId": "App::LinkGroup",
+ "role": "Link colored element override lookup",
+ "propertyWriteRequiresShape": false,
+ "source": "Link extension and Draft colored element implementation"
+ }
+ },
+ {
+ "objectTypeId": "App::LinkGroupPython",
+ "objectAvailable": true,
+ "probeStatus": "available",
+ "propertyName": "ColoredElements",
+ "group": " Link",
+ "statusRaw": [
+ "LockDynamic",
+ 26
+ ],
+ "statusNames": [
+ "LockDynamic",
+ "PropHidden"
+ ],
+ "defaultRaw": null,
+ "valueModel": {
+ "kind": "hidden-document-object-sub-element-reference",
+ "inheritedFrom": "App::PropertyLinkSub",
+ "elementType": "same-document App::DocumentObject plus ordered sub-element names",
+ "orderPreserved": true,
+ "duplicatesPreserved": false,
+ "writable": true
+ },
+ "inputs": {
+ "nativeCppSetValues": "attached same-document DocumentObject plus sub-element string list",
+ "emptySubListMeansWholeObject": true,
+ "nullObjectClears": true,
+ "detachedObjectRejected": true,
+ "crossDocumentRejectedWithoutLinkAllowExternal": true,
+ "invalidSubElementAcceptedAsReference": true
+ },
+ "dependencies": {
+ "linkScope": "Hidden",
+ "createsBacklinks": false,
+ "includedByDefaultGetLinks": false,
+ "includedByGetLinksAll": true,
+ "defaultDagDependency": false
+ },
+ "applicability": {
+ "requiredObjectTypeId": "App::LinkGroupPython",
+ "role": "Link colored element override lookup",
+ "propertyWriteRequiresShape": false,
+ "source": "Link extension and Draft colored element implementation"
+ }
+ },
+ {
+ "objectTypeId": "App::LinkPython",
+ "objectAvailable": true,
+ "probeStatus": "available",
+ "propertyName": "ColoredElements",
+ "group": " Link",
+ "statusRaw": [
+ "LockDynamic",
+ 26
+ ],
+ "statusNames": [
+ "LockDynamic",
+ "PropHidden"
+ ],
+ "defaultRaw": null,
+ "valueModel": {
+ "kind": "hidden-document-object-sub-element-reference",
+ "inheritedFrom": "App::PropertyLinkSub",
+ "elementType": "same-document App::DocumentObject plus ordered sub-element names",
+ "orderPreserved": true,
+ "duplicatesPreserved": false,
+ "writable": true
+ },
+ "inputs": {
+ "nativeCppSetValues": "attached same-document DocumentObject plus sub-element string list",
+ "emptySubListMeansWholeObject": true,
+ "nullObjectClears": true,
+ "detachedObjectRejected": true,
+ "crossDocumentRejectedWithoutLinkAllowExternal": true,
+ "invalidSubElementAcceptedAsReference": true
+ },
+ "dependencies": {
+ "linkScope": "Hidden",
+ "createsBacklinks": false,
+ "includedByDefaultGetLinks": false,
+ "includedByGetLinksAll": true,
+ "defaultDagDependency": false
+ },
+ "applicability": {
+ "requiredObjectTypeId": "App::LinkPython",
+ "role": "Link colored element override lookup",
+ "propertyWriteRequiresShape": false,
+ "source": "Link extension and Draft colored element implementation"
+ }
+ }
+ ],
+ "typeContract": {
+ "inheritedFrom": "App::PropertyLinkSub",
+ "linkScope": "Hidden",
+ "propertyStatus": "PropHidden",
+ "dynamicStatus": "LockDynamic",
+ "propertyStatusPurpose": "hide the ColoredElements property from the ordinary editor surface",
+ "linkScopePurpose": "exclude ColoredElements from ordinary backlink and dependency traversal",
+ "xmlElement": "LinkSub",
+ "xmlChildElement": "Sub",
+ "legacyRestoreType": "App::PropertyLinkSub",
+ "emptySubListMeans": "whole-object-reference"
+ },
+ "provenance": {
+ "runtime": {
+ "path": ".cache/freecad/reference-desktop.json",
+ "bytes": 2298709,
+ "sha256": "082400ec7379e8efc2af16709032d45890d7092277967b714acb05761fd1c2a7"
+ },
+ "sources": [
+ {
+ "path": ".cache/freecad/FreeCAD/src/App/PropertyLinks.h",
+ "bytes": 58046,
+ "sha256": "03176b14948d43fcae3ff290711a913ae4fa43f3ba286d66ceb610e074555d1c"
+ },
+ {
+ "path": ".cache/freecad/FreeCAD/src/App/PropertyLinks.cpp",
+ "bytes": 202779,
+ "sha256": "b4221dd89d2b0b668dc8a3c2852ba53bc67bcb721830e3a6011ea53c1a32edb0"
+ },
+ {
+ "path": ".cache/freecad/FreeCAD/src/App/Link.h",
+ "bytes": 40417,
+ "sha256": "57df13bd156ad7f3a1e23a74ae4040fa159bbc3d38c4a49b087d0c92260721e6"
+ },
+ {
+ "path": ".cache/freecad/FreeCAD/src/Mod/Draft/draftobjects/draftlink.py",
+ "bytes": 11576,
+ "sha256": "2b781a0b18dc89a3e07dc576b508b8c331ad33bf64fc9dca53240bf943c05e57"
+ }
+ ]
+ },
+ "classification": "opaque-fcstd-proxy",
+ "nextPhases": [
+ "B",
+ "C",
+ "D",
+ "E",
+ "F",
+ "G",
+ "H",
+ "I"
+ ]
+}
diff --git a/config/freecad-property-linksubhidden-success.json b/config/freecad-property-linksubhidden-success.json
new file mode 100644
index 0000000..c69356d
--- /dev/null
+++ b/config/freecad-property-linksubhidden-success.json
@@ -0,0 +1,1108 @@
+{
+ "baselineId": "freecad-1.1.1-property-linksubhidden-success",
+ "bindingBoundary": {
+ "emptySubListMeans": "whole-object-reference",
+ "oracleDefault": null,
+ "safeFirstRead": "assign-null-object-before-read"
+ },
+ "caseCount": 4,
+ "cases": [
+ {
+ "group": " Link",
+ "mode": "direct-native-property-setter",
+ "objectName": "LinkProbe",
+ "objectTypeId": "App::Link",
+ "phases": {
+ "duplicates": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": " Link",
+ "objectSet": [
+ {
+ "name": "LinkProbe",
+ "typeId": "App::Link"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [
+ "ColorSourceA"
+ ],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "recomputeResult": true,
+ "shape": {
+ "applicable": false,
+ "reason": "host-has-no-shape-property"
+ },
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [
+ "LinkProbe"
+ ],
+ "ColorSourceB": []
+ },
+ "targetShapes": {
+ "ColorSourceA": {
+ "applicable": true,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ColorSourceB": {
+ "applicable": true,
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": [
+ "Face6",
+ "Face1",
+ "Face6"
+ ]
+ }
+ },
+ "explicitNull": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": " Link",
+ "objectSet": [
+ {
+ "name": "LinkProbe",
+ "typeId": "App::Link"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [
+ "ColorSourceA"
+ ],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "recomputeResult": true,
+ "shape": {
+ "applicable": false,
+ "reason": "host-has-no-shape-property"
+ },
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [
+ "LinkProbe"
+ ],
+ "ColorSourceB": []
+ },
+ "targetShapes": {
+ "ColorSourceA": {
+ "applicable": true,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ColorSourceB": {
+ "applicable": true,
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": null
+ },
+ "ordered": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": " Link",
+ "objectSet": [
+ {
+ "name": "LinkProbe",
+ "typeId": "App::Link"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [
+ "ColorSourceA"
+ ],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "recomputeResult": true,
+ "shape": {
+ "applicable": false,
+ "reason": "host-has-no-shape-property"
+ },
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [
+ "LinkProbe"
+ ],
+ "ColorSourceB": []
+ },
+ "targetShapes": {
+ "ColorSourceA": {
+ "applicable": true,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ColorSourceB": {
+ "applicable": true,
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": [
+ "Face1",
+ "Face3"
+ ]
+ }
+ },
+ "wholeObject": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": " Link",
+ "objectSet": [
+ {
+ "name": "LinkProbe",
+ "typeId": "App::Link"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [
+ "ColorSourceA"
+ ],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "recomputeResult": true,
+ "shape": {
+ "applicable": false,
+ "reason": "host-has-no-shape-property"
+ },
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [
+ "LinkProbe"
+ ],
+ "ColorSourceB": []
+ },
+ "targetShapes": {
+ "ColorSourceA": {
+ "applicable": true,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ColorSourceB": {
+ "applicable": true,
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": []
+ }
+ }
+ },
+ "propertyName": "ColoredElements"
+ },
+ {
+ "group": " Link",
+ "mode": "direct-native-property-setter",
+ "objectName": "LinkGroupProbe",
+ "objectTypeId": "App::LinkGroup",
+ "phases": {
+ "duplicates": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": " Link",
+ "objectSet": [
+ {
+ "name": "LinkGroupProbe",
+ "typeId": "App::LinkGroup"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "recomputeResult": true,
+ "shape": {
+ "applicable": false,
+ "reason": "host-has-no-shape-property"
+ },
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapes": {
+ "ColorSourceA": {
+ "applicable": true,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ColorSourceB": {
+ "applicable": true,
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": [
+ "Face6",
+ "Face1",
+ "Face6"
+ ]
+ }
+ },
+ "explicitNull": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": " Link",
+ "objectSet": [
+ {
+ "name": "LinkGroupProbe",
+ "typeId": "App::LinkGroup"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "recomputeResult": true,
+ "shape": {
+ "applicable": false,
+ "reason": "host-has-no-shape-property"
+ },
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapes": {
+ "ColorSourceA": {
+ "applicable": true,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ColorSourceB": {
+ "applicable": true,
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": null
+ },
+ "ordered": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": " Link",
+ "objectSet": [
+ {
+ "name": "LinkGroupProbe",
+ "typeId": "App::LinkGroup"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "recomputeResult": true,
+ "shape": {
+ "applicable": false,
+ "reason": "host-has-no-shape-property"
+ },
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapes": {
+ "ColorSourceA": {
+ "applicable": true,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ColorSourceB": {
+ "applicable": true,
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": [
+ "Face1",
+ "Face3"
+ ]
+ }
+ },
+ "wholeObject": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": " Link",
+ "objectSet": [
+ {
+ "name": "LinkGroupProbe",
+ "typeId": "App::LinkGroup"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "recomputeResult": true,
+ "shape": {
+ "applicable": false,
+ "reason": "host-has-no-shape-property"
+ },
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapes": {
+ "ColorSourceA": {
+ "applicable": true,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ColorSourceB": {
+ "applicable": true,
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": []
+ }
+ }
+ },
+ "propertyName": "ColoredElements"
+ },
+ {
+ "group": " Link",
+ "mode": "direct-native-property-setter",
+ "objectName": "LinkGroupPythonProbe",
+ "objectTypeId": "App::LinkGroupPython",
+ "phases": {
+ "duplicates": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": " Link",
+ "objectSet": [
+ {
+ "name": "LinkGroupPythonProbe",
+ "typeId": "App::LinkGroupPython"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "recomputeResult": true,
+ "shape": {
+ "applicable": false,
+ "reason": "host-has-no-shape-property"
+ },
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapes": {
+ "ColorSourceA": {
+ "applicable": true,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ColorSourceB": {
+ "applicable": true,
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": [
+ "Face6",
+ "Face1",
+ "Face6"
+ ]
+ }
+ },
+ "explicitNull": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": " Link",
+ "objectSet": [
+ {
+ "name": "LinkGroupPythonProbe",
+ "typeId": "App::LinkGroupPython"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "recomputeResult": true,
+ "shape": {
+ "applicable": false,
+ "reason": "host-has-no-shape-property"
+ },
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapes": {
+ "ColorSourceA": {
+ "applicable": true,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ColorSourceB": {
+ "applicable": true,
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": null
+ },
+ "ordered": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": " Link",
+ "objectSet": [
+ {
+ "name": "LinkGroupPythonProbe",
+ "typeId": "App::LinkGroupPython"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "recomputeResult": true,
+ "shape": {
+ "applicable": false,
+ "reason": "host-has-no-shape-property"
+ },
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapes": {
+ "ColorSourceA": {
+ "applicable": true,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ColorSourceB": {
+ "applicable": true,
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": [
+ "Face1",
+ "Face3"
+ ]
+ }
+ },
+ "wholeObject": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": " Link",
+ "objectSet": [
+ {
+ "name": "LinkGroupPythonProbe",
+ "typeId": "App::LinkGroupPython"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "recomputeResult": true,
+ "shape": {
+ "applicable": false,
+ "reason": "host-has-no-shape-property"
+ },
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [],
+ "ColorSourceB": []
+ },
+ "targetShapes": {
+ "ColorSourceA": {
+ "applicable": true,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ColorSourceB": {
+ "applicable": true,
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": []
+ }
+ }
+ },
+ "propertyName": "ColoredElements"
+ },
+ {
+ "group": " Link",
+ "mode": "direct-native-property-setter",
+ "objectName": "LinkPythonProbe",
+ "objectTypeId": "App::LinkPython",
+ "phases": {
+ "duplicates": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": " Link",
+ "objectSet": [
+ {
+ "name": "LinkPythonProbe",
+ "typeId": "App::LinkPython"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [
+ "ColorSourceA"
+ ],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "recomputeResult": true,
+ "shape": {
+ "applicable": false,
+ "reason": "host-has-no-shape-property"
+ },
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [
+ "LinkPythonProbe"
+ ],
+ "ColorSourceB": []
+ },
+ "targetShapes": {
+ "ColorSourceA": {
+ "applicable": true,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ColorSourceB": {
+ "applicable": true,
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": [
+ "Face6",
+ "Face1",
+ "Face6"
+ ]
+ }
+ },
+ "explicitNull": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": " Link",
+ "objectSet": [
+ {
+ "name": "LinkPythonProbe",
+ "typeId": "App::LinkPython"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [
+ "ColorSourceA"
+ ],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "recomputeResult": true,
+ "shape": {
+ "applicable": false,
+ "reason": "host-has-no-shape-property"
+ },
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [
+ "LinkPythonProbe"
+ ],
+ "ColorSourceB": []
+ },
+ "targetShapes": {
+ "ColorSourceA": {
+ "applicable": true,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ColorSourceB": {
+ "applicable": true,
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": null
+ },
+ "ordered": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": " Link",
+ "objectSet": [
+ {
+ "name": "LinkPythonProbe",
+ "typeId": "App::LinkPython"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [
+ "ColorSourceA"
+ ],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "recomputeResult": true,
+ "shape": {
+ "applicable": false,
+ "reason": "host-has-no-shape-property"
+ },
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [
+ "LinkPythonProbe"
+ ],
+ "ColorSourceB": []
+ },
+ "targetShapes": {
+ "ColorSourceA": {
+ "applicable": true,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ColorSourceB": {
+ "applicable": true,
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": [
+ "Face1",
+ "Face3"
+ ]
+ }
+ },
+ "wholeObject": {
+ "editorMode": [
+ "Hidden"
+ ],
+ "group": " Link",
+ "objectSet": [
+ {
+ "name": "LinkPythonProbe",
+ "typeId": "App::LinkPython"
+ },
+ {
+ "name": "ColorSourceA",
+ "typeId": "Part::Feature"
+ },
+ {
+ "name": "ColorSourceB",
+ "typeId": "Part::Feature"
+ }
+ ],
+ "ownerOutList": [
+ "ColorSourceA"
+ ],
+ "ownerState": [
+ "Up-to-date"
+ ],
+ "propertyStatus": [
+ "LockDynamic",
+ "26"
+ ],
+ "propertyTypeId": "App::PropertyLinkSubHidden",
+ "recomputeResult": true,
+ "shape": {
+ "applicable": false,
+ "reason": "host-has-no-shape-property"
+ },
+ "statusString": "Valid",
+ "targetInLists": {
+ "ColorSourceA": [
+ "LinkPythonProbe"
+ ],
+ "ColorSourceB": []
+ },
+ "targetShapes": {
+ "ColorSourceA": {
+ "applicable": true,
+ "brepSha256": "8c42068f05129bb15610da52f3fef1132d8f4459ae9662549de0c14dbd6ee44b",
+ "edges": 12,
+ "faces": 6,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 8
+ },
+ "ColorSourceB": {
+ "applicable": true,
+ "brepSha256": "c3786ce59c730f9b18585c41062b02591e323238119986649a96ef34eb439851",
+ "edges": 3,
+ "faces": 3,
+ "isNull": false,
+ "solids": 1,
+ "vertices": 2
+ }
+ },
+ "value": {
+ "object": "ColorSourceA",
+ "subElements": []
+ }
+ }
+ },
+ "propertyName": "ColoredElements"
+ }
+ ],
+ "freecadVersion": "1.1.1",
+ "gitCommit": "0108fd4b4850cc46e625b60e53cea7a7bbe69f8d",
+ "propertyType": "App::PropertyLinkSubHidden",
+ "schemaVersion": 1,
+ "status": "pass"
+}
diff --git a/config/freecad-web-exact-parity-plan.json b/config/freecad-web-exact-parity-plan.json
index b363b2b..ec03688 100644
--- a/config/freecad-web-exact-parity-plan.json
+++ b/config/freecad-web-exact-parity-plan.json
@@ -41,7 +41,7 @@
"id": "EX03",
"title": "Application document, property and recompute semantics",
"tasks": [
- { "id": "EX-DOC-01", "title": "Implement every native property type, flag, editor and serialization rule", "priority": "P0", "status": "in_progress", "dependencies": ["EX-ORA-01"], "deliverables": ["Complete Property schema", "Status and editor metadata", "Binary and XML codecs"], "acceptance": ["Every runtime property fixture round-trips without type coercion or unknown loss"], "evidence": ["check:freecad-type-properties", "check:freecad-native-property-semantics", "check:freecad-property-status", "check:freecad-fcstd-roundtrip"], "exactBlockedBy": ["Of 85 runtime property types and 5510 records, 46 types/457 records remain opaque-only; all 15 observed status classes now have typed Facade behavior, including LockDynamic dynamic mutation and PartialTrigger partial-document observer transactions, but the remaining codecs, editors and complete document observer semantics are not closed"] },
+ { "id": "EX-DOC-01", "title": "Implement every native property type, flag, editor and serialization rule", "priority": "P0", "status": "in_progress", "dependencies": ["EX-ORA-01"], "deliverables": ["Complete Property schema", "Status and editor metadata", "Binary and XML codecs"], "acceptance": ["Every runtime property fixture round-trips without type coercion or unknown loss"], "evidence": ["check:freecad-type-properties", "check:freecad-native-property-semantics", "check:freecad-property-status", "check:freecad-fcstd-roundtrip"], "exactBlockedBy": ["Of 85 runtime property types and 5510 records, 45 types/455 records remain opaque-only; all 15 observed status classes now have typed Facade behavior, including LockDynamic dynamic mutation and PartialTrigger partial-document observer transactions, but the remaining codecs, editors and complete document observer semantics are not closed"] },
{ "id": "EX-DOC-02", "title": "Match transactions, DAG recompute, partial failure and document observers", "priority": "P0", "status": "in_progress", "dependencies": ["EX-DOC-01", "EX-TSN-04"], "deliverables": ["Transaction and observer event oracle", "Partial recompute scheduler", "Failure recovery contract"], "acceptance": ["Undo, redo, abort, stale and cyclic dependency behavior matches FreeCAD"], "evidence": ["check:document-closure", "test:facade"], "exactBlockedBy": ["Observer ordering and every partial-recompute branch are not yet oracle-backed"] },
{ "id": "EX-DOC-03", "title": "Complete expressions, links, extensions, groups and App object lifecycle", "priority": "P0", "status": "in_progress", "dependencies": ["EX-DOC-01"], "deliverables": ["Expression dependency grammar", "Link and extension lifecycle", "Group and clone semantics"], "acceptance": ["Copy, clone, delete, relink and expression edits survive native round-trip"], "evidence": ["check:fcstd-closure", "check:chrome-production-document"], "exactBlockedBy": ["FeaturePython-backed extensions remain non-executable"] },
{ "id": "EX-DOC-04", "title": "Complete multi-document, external link, merge and recovery workflows", "priority": "P1", "status": "pending", "dependencies": ["EX-DOC-02", "EX-DOC-03"], "deliverables": ["Cross-document Link corpus", "Merge and conflict semantics", "Crash and autosave recovery"], "acceptance": ["Open, close, relink, merge and recovery match the native document graph"], "evidence": ["check:chrome-persistence", "check:project-migration"], "exactBlockedBy": ["Current application is project-first and does not implement every desktop multi-document workflow"] },
@@ -63,7 +63,7 @@
"id": "EX05",
"title": "FCStd and all import/export entrypoints",
"tasks": [
- { "id": "EX-FC-01", "title": "Round-trip every native FCStd object, property, extension and resource", "priority": "P0", "status": "in_progress", "dependencies": ["EX-DOC-04", "EX-TSN-04"], "deliverables": ["Complete native archive model", "Unknown object preservation", "FreeCAD-Web-FreeCAD mutation corpus"], "acceptance": ["No supported object, property, GUI state or resource drifts after mutation and resave"], "evidence": ["check:freecad-native-property-semantics", "check:freecad-isomorphic-provenance", "check:fcstd-closure", "check:chrome-fcstd-roundtrip"], "exactBlockedBy": ["FCStd supports 35 native property codecs plus opaque preservation for 50 runtime types; exhaustive native mutation and resave remains open"] },
+ { "id": "EX-FC-01", "title": "Round-trip every native FCStd object, property, extension and resource", "priority": "P0", "status": "in_progress", "dependencies": ["EX-DOC-04", "EX-TSN-04"], "deliverables": ["Complete native archive model", "Unknown object preservation", "FreeCAD-Web-FreeCAD mutation corpus"], "acceptance": ["No supported object, property, GUI state or resource drifts after mutation and resave"], "evidence": ["check:freecad-native-property-semantics", "check:freecad-isomorphic-provenance", "check:fcstd-closure", "check:chrome-fcstd-roundtrip"], "exactBlockedBy": ["FCStd supports 36 native property codecs plus opaque preservation for 49 runtime types; exhaustive native mutation and resave remains open"] },
{ "id": "EX-FMT-01", "title": "Close BREP, STEP and IGES import/export semantics", "priority": "P0", "status": "in_progress", "dependencies": ["EX-KER-01"], "deliverables": ["Assembly/color/name import", "Unit and tolerance handling", "Deterministic exports"], "acceptance": ["Native and Web topology, metadata and diagnostics match for each format"] , "evidence": ["check:chrome-secondary-formats", "check:browser-occt"], "exactBlockedBy": ["Advanced STEP/IGES metadata and assembly cases are incomplete"] },
{ "id": "EX-FMT-02", "title": "Close STL, OBJ, PLY, DXF, SVG and PDF round-trips", "priority": "P1", "status": "in_progress", "dependencies": ["EX-MESH-01", "EX-DRAFT-01", "EX-TD-01"], "deliverables": ["Mesh and 2D format codecs", "Style/layer metadata", "Import-export failure corpus"], "acceptance": ["All supported entities and metadata round-trip or fail with a registered difference"] , "evidence": ["check:chrome-secondary-formats", "check:document-closure"], "exactBlockedBy": ["Several formats remain read-only or omit advanced entity metadata"] },
{ "id": "EX-FMT-03", "title": "Close IFC, CSV and G-code native object semantics", "priority": "P1", "status": "in_progress", "dependencies": ["EX-BIM-01", "EX-SS-01", "EX-CAM-01"], "deliverables": ["IFC schema and property sets", "Spreadsheet CSV dialect parity", "Path/G-code object round-trip"], "acceptance": ["Import, edit, export and native reopen preserve the declared object graph"] , "evidence": ["check:chrome-bim", "check:document-closure", "check:cam-parity"], "exactBlockedBy": ["IFC and G-code entrypoint inventory still records unsupported desktop semantics"] },
diff --git a/docs/freecad-active-work-status.generated.zh-CN.md b/docs/freecad-active-work-status.generated.zh-CN.md
index d788b90..f164daa 100644
--- a/docs/freecad-active-work-status.generated.zh-CN.md
+++ b/docs/freecad-active-work-status.generated.zh-CN.md
@@ -3,10 +3,10 @@
生成日期:2026-08-15。基线:FreeCAD 1.1.1 / `0108fd4b4850cc46e625b60e53cea7a7bbe69f8d`。
-- 唯一活动任务:`PROP-app-propertyintegerset-A`
-- 微任务:1756 total / 586 completed / 1 in_progress / 1169 pending
+- 唯一活动任务:`PROP-app-propertylinksubhidden-D`
+- 微任务:1756 total / 607 completed / 1 in_progress / 1148 pending
- 有序操作对:361/361 classified / 0 unknown
-- Property:38/85 native types / 47 opaque types
+- Property:40/85 native types / 45 opaque types
- exact 任务:5/52 completed
- 模块能力:0 exact / 26 compatible / 5 proxy / 3 development
@@ -16,7 +16,7 @@
| `ORA-GUI-WORKFLOWS` | 7 | 7 | 0 | 0 | `-` |
| `TSN-RECOVERY-DRIFT` | 13 | 13 | 0 | 0 | `-` |
| `TSN-ORDERED-PAIRS` | 361 | 361 | 0 | 0 | `-` |
-| `PROPERTY-CODECS` | 522 | 99 | 1 | 422 | `PROP-app-propertyintegerset-A` |
+| `PROPERTY-CODECS` | 522 | 120 | 1 | 401 | `PROP-app-propertylinksubhidden-D` |
| `DOCUMENT-SEMANTICS` | 90 | 0 | 0 | 90 | `-` |
| `CORE-MODELING` | 171 | 0 | 0 | 171 | `-` |
| `FCSTD-AND-FORMATS` | 72 | 0 | 0 | 72 | `-` |
diff --git a/package.json b/package.json
index ca0272b..0affe9a 100644
--- a/package.json
+++ b/package.json
@@ -188,6 +188,28 @@
"check:chrome-property-integerset": "node scripts/check-chrome-property-integerset.mjs",
"generate:freecad-property-integerset-promotion": "node scripts/generate-freecad-property-integerset-promotion.mjs",
"check:freecad-property-integerset-promotion": "node scripts/check-freecad-property-integerset-promotion.mjs",
+ "generate:freecad-property-linklisthidden-inventory": "node scripts/generate-freecad-property-linklisthidden-inventory.mjs",
+ "check:freecad-property-linklisthidden-inventory": "node scripts/check-freecad-property-linklisthidden-inventory.mjs",
+ "probe:freecad-property-linklisthidden-success": "node scripts/run-freecad-property-linklisthidden-success.mjs",
+ "check:freecad-property-linklisthidden-success": "node scripts/check-freecad-property-linklisthidden-success.mjs",
+ "probe:freecad-property-linklisthidden-failure": "node scripts/run-freecad-property-linklisthidden-failure.mjs",
+ "check:freecad-property-linklisthidden-failure": "node scripts/check-freecad-property-linklisthidden-failure.mjs",
+ "probe:freecad-property-linklisthidden-mutation": "node scripts/run-freecad-property-linklisthidden-mutation.mjs",
+ "check:freecad-property-linklisthidden-mutation": "node scripts/check-freecad-property-linklisthidden-mutation.mjs",
+ "test:freecad-property-linklisthidden-facade": "tsx --test tests/propertyLinkListHiddenFacade.test.ts",
+ "test:freecad-property-linklisthidden-transaction": "tsx --test tests/propertyLinkListHiddenTransaction.test.ts",
+ "probe:freecad-property-linklisthidden-roundtrip": "tsx scripts/run-freecad-property-linklisthidden-roundtrip.ts",
+ "check:freecad-property-linklisthidden-roundtrip": "node scripts/check-freecad-property-linklisthidden-roundtrip.mjs",
+ "test:chrome-property-linklisthidden": "node scripts/run-chrome-property-linklisthidden.mjs",
+ "check:chrome-property-linklisthidden": "node scripts/check-chrome-property-linklisthidden.mjs",
+ "generate:freecad-property-linklisthidden-promotion": "node scripts/generate-freecad-property-linklisthidden-promotion.mjs",
+ "check:freecad-property-linklisthidden-promotion": "node scripts/check-freecad-property-linklisthidden-promotion.mjs",
+ "generate:freecad-property-linksubhidden-inventory": "node scripts/generate-freecad-property-linksubhidden-inventory.mjs",
+ "check:freecad-property-linksubhidden-inventory": "node scripts/check-freecad-property-linksubhidden-inventory.mjs",
+ "probe:freecad-property-linksubhidden-success": "node scripts/run-freecad-property-linksubhidden-success.mjs",
+ "check:freecad-property-linksubhidden-success": "node scripts/check-freecad-property-linksubhidden-success.mjs",
+ "probe:freecad-property-linksubhidden-failure": "node scripts/run-freecad-property-linksubhidden-failure.mjs",
+ "check:freecad-property-linksubhidden-failure": "node scripts/check-freecad-property-linksubhidden-failure.mjs",
"probe:freecad-property-file-success": "node scripts/run-freecad-property-file-success.mjs",
"check:freecad-property-file-success": "node scripts/check-freecad-property-file-success.mjs",
"probe:freecad-property-file-failure": "node scripts/run-freecad-property-file-failure.mjs",
diff --git a/scripts/check-chrome-property-linklisthidden.mjs b/scripts/check-chrome-property-linklisthidden.mjs
new file mode 100644
index 0000000..6360c2c
--- /dev/null
+++ b/scripts/check-chrome-property-linklisthidden.mjs
@@ -0,0 +1,16 @@
+import { isDeepStrictEqual } from 'node:util'
+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/chrome-property-linklisthidden-verification.json'), 'utf8'))
+const fail = (message) => { throw new Error(`Chrome PropertyLinkListHidden check failed: ${message}`) }
+if (report.schemaVersion !== 1 || report.status !== 'pass' || report.browserId !== 'chrome' || !/Chrome\//.test(report.userAgent || '') || report.crossOriginIsolated !== true) fail('browser boundary is invalid')
+const requiredWorkerRequests = ['initialize', 'save-document', 'load-document', 'load-checkpoint', 'recovery-report', 'put-resource', 'get-resource', 'release-resource', 'dispose']
+if (!report.worker?.constructed?.some(({ name }) => name === 'bitbybit-persistence') || !requiredWorkerRequests.every((type) => report.worker.requestTypes?.includes(type)) || report.worker.terminated?.includes('bitbybit-persistence') !== true) fail('persistence Worker lifecycle is incomplete')
+const initial = ['ExportA', 'ExportB']
+const target = ['ExportB', 'ExportA', 'ExportB']
+if (report.ui?.visibleLabelInput !== true || report.ui.hiddenControlCountBefore !== 0 || report.ui.hiddenControlCountAfter !== 0 || report.ui.hiddenTextPresentBefore !== false || report.ui.hiddenTextPresentAfter !== false || report.ui.hiddenStatus !== true || !isDeepStrictEqual(report.ui.beforeValue, initial) || !isDeepStrictEqual(report.ui.afterValue, target) || report.ui.documentVersion !== 2 || report.ui.dirty !== true || report.ui.objectState !== 'touched' || report.ui.dependencyCount !== 0 || report.ui.errors?.length !== 0) fail('production hidden-property UI state is incomplete')
+if (report.persistence?.mode !== 'sqlite-opfs' || report.persistence.sqliteWasm !== true || report.persistence.opfs !== true || report.persistence.savedMode !== 'sqlite-opfs' || !isDeepStrictEqual(report.persistence.loadedValue, target) || report.persistence.loadedType !== 'App::PropertyLinkListHidden' || report.persistence.loadedDependencyCount !== 0 || report.persistence.checkpointVersion !== 2 || report.persistence.recoveryIntegrity !== 'ok' || report.persistence.fcstdElement !== 'LinkList' || !isDeepStrictEqual(report.persistence.fcstdValue, target) || report.persistence.fcstdNativeStatus !== 67108864) fail('OPFS/FCStd persistence evidence is incomplete')
+if (!(report.resource?.byteLength > 0) || report.resource.roundTrip !== true || report.resource.released !== true || report.resource.markerRemoved !== true || report.release?.shapeCount !== 0 || report.release.kernelReferenceCount !== 0 || report.release.workerTerminated !== true) fail('resource release evidence is incomplete')
+console.log(JSON.stringify({ status: 'chrome-property-linklisthidden-pass', ui: report.ui, persistence: report.persistence, workerRequests: report.worker.requestTypes, resource: report.resource, release: report.release }, null, 2))
diff --git a/scripts/check-facade-boundary.mjs b/scripts/check-facade-boundary.mjs
index bf075dc..906df87 100644
--- a/scripts/check-facade-boundary.mjs
+++ b/scripts/check-facade-boundary.mjs
@@ -23,6 +23,7 @@ const allowWorkerMessaging = new Set([
'chromePropertyForceHarness.tsx',
'chromePropertyHeatFluxHarness.tsx',
'chromePropertyIntegerSetHarness.tsx',
+ 'chromePropertyLinkListHiddenHarness.tsx',
'facade/camPipeline.ts',
])
diff --git a/scripts/check-freecad-native-property-semantics.mjs b/scripts/check-freecad-native-property-semantics.mjs
index 044f70d..e32c509 100644
--- a/scripts/check-freecad-native-property-semantics.mjs
+++ b/scripts/check-freecad-native-property-semantics.mjs
@@ -5,10 +5,10 @@ import { resolve } from 'node:path'
const root = resolve(new URL('..', import.meta.url).pathname)
const report = JSON.parse(await readFile(resolve(root, 'config/freecad-native-property-semantics.json'), 'utf8'))
const fail = (message) => { throw new Error(`FreeCAD native property semantics check failed: ${message}`) }
-if (report.schemaVersion !== 1 || report.status !== 'pass' || report.baseline?.freecadVersion !== '1.1.1' || report.baseline?.commit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d' || report.exactPromotionReady !== false || report.exactBlocker !== '46 runtime property types and 457 records remain opaque-only; complete native document and property semantics') fail('report boundary is invalid')
+if (report.schemaVersion !== 1 || report.status !== 'pass' || report.baseline?.freecadVersion !== '1.1.1' || report.baseline?.commit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d' || report.exactPromotionReady !== false || report.exactBlocker !== '45 runtime property types and 455 records remain opaque-only; complete native document and property semantics') fail('report boundary is invalid')
if (report.runtime?.registeredObjectTypes !== 352 || report.runtime.instantiableObjectTypes !== 348 || report.runtime.unavailableObjectTypes !== 4 || report.runtime.propertyRecords !== 5510 || report.runtime.propertyTypes !== 85 || report.runtime.unavailableObjects?.length !== 4) fail('runtime inventory counts changed')
const support = report.supportSummary
-if (support?.['native-editable-codec']?.typeCount !== 34 || support['native-editable-codec'].recordCount !== 4370 || support?.['native-specialized-codec']?.typeCount !== 5 || support['native-specialized-codec'].recordCount !== 683 || support?.['opaque-fcstd-proxy']?.typeCount !== 46 || support['opaque-fcstd-proxy'].recordCount !== 457) fail('property support partition is stale')
+if (support?.['native-editable-codec']?.typeCount !== 35 || support['native-editable-codec'].recordCount !== 4372 || support?.['native-specialized-codec']?.typeCount !== 5 || support['native-specialized-codec'].recordCount !== 683 || support?.['opaque-fcstd-proxy']?.typeCount !== 45 || support['opaque-fcstd-proxy'].recordCount !== 455) fail('property support partition is stale')
if (report.types?.length !== 85 || new Set(report.types.map(({ typeId }) => typeId)).size !== 85 || report.types.reduce((total, entry) => total + entry.recordCount, 0) !== 5510) fail('per-TypeId inventory is invalid')
const acceleration = report.types.find(({ typeId }) => typeId === 'App::PropertyAcceleration')
if (acceleration?.support !== 'native-editable-codec' || acceleration.recordCount !== 1 || acceleration.objectTypeCount !== 1) fail('App::PropertyAcceleration promotion is missing')
@@ -34,6 +34,8 @@ const heatFlux = report.types.find(({ typeId }) => typeId === 'App::PropertyHeat
if (heatFlux?.support !== 'native-editable-codec' || heatFlux.recordCount !== 1 || heatFlux.objectTypeCount !== 1 || heatFlux.statusNames?.length !== 0) fail('App::PropertyHeatFlux promotion is missing')
const integerSet = report.types.find(({ typeId }) => typeId === 'App::PropertyIntegerSet')
if (integerSet?.support !== 'native-editable-codec' || integerSet.recordCount !== 2 || integerSet.objectTypeCount !== 2 || integerSet.statusNames?.length !== 0) fail('App::PropertyIntegerSet promotion is missing')
+const linkListHidden = report.types.find(({ typeId }) => typeId === 'App::PropertyLinkListHidden')
+if (linkListHidden?.support !== 'native-editable-codec' || linkListHidden.recordCount !== 2 || linkListHidden.objectTypeCount !== 2 || linkListHidden.statusNames?.join(',') !== 'PropHidden') fail('App::PropertyLinkListHidden promotion is missing')
if (report.propertyStatus?.unknownNumericBits?.length !== 0 || report.propertyStatus.proxyOnlyRecordCount !== 0 || report.propertyStatus.proxyOnly?.length !== 0 || report.propertyStatus.facadeNative?.join(',') !== report.propertyStatus.observed?.join(',')) fail('property status representation coverage is stale')
if (report.propertyStatus.behaviorNative?.join(',') !== 'Hidden,Immutable,LockDynamic,NoModify,Ordered,Output,PartialTrigger,PropHidden,PropNoPersist,PropNoRecompute,PropOutput,PropReadOnly,PropTransient,ReadOnly,Transient' || report.propertyStatus.preservedOnly?.length !== 0 || report.propertyStatus.preservedOnlyRecordCount !== 0) fail('property status behavior boundary is stale')
for (const locked of [report.source, report.harness]) {
@@ -41,4 +43,4 @@ for (const locked of [report.source, report.harness]) {
const [content, bytes] = await Promise.all([readFile(path), stat(path).then(({ size }) => size)])
if (bytes !== locked.bytes || createHash('sha256').update(content).digest('hex') !== locked.sha256) fail(`report is stale for ${locked.path}`)
}
-console.log(JSON.stringify({ status: 'freecad-native-property-semantics-pass', propertyTypes: 85, propertyRecords: 5510, nativeCodecRecords: 5053, opaqueProxyRecords: 457, promotedTypes: [acceleration.typeId, area.typeId, boolList.typeId, color.typeId, colorList.typeId, direction.typeId, file.typeId, fileIncluded.typeId, font.typeId, force.typeId, heatFlux.typeId, integerSet.typeId], representedStatusRecords: 5510, preservedOnlyStatusRecords: 0, exactPromotionReady: false, exactBlocker: report.exactBlocker }, null, 2))
+console.log(JSON.stringify({ status: 'freecad-native-property-semantics-pass', propertyTypes: 85, propertyRecords: 5510, nativeCodecRecords: 5055, opaqueProxyRecords: 455, promotedTypes: [acceleration.typeId, area.typeId, boolList.typeId, color.typeId, colorList.typeId, direction.typeId, file.typeId, fileIncluded.typeId, font.typeId, force.typeId, heatFlux.typeId, integerSet.typeId, linkListHidden.typeId], representedStatusRecords: 5510, preservedOnlyStatusRecords: 0, exactPromotionReady: false, exactBlocker: report.exactBlocker }, null, 2))
diff --git a/scripts/check-freecad-property-acceleration-promotion.mjs b/scripts/check-freecad-property-acceleration-promotion.mjs
index eb86b0c..ce22760 100644
--- a/scripts/check-freecad-property-acceleration-promotion.mjs
+++ b/scripts/check-freecad-property-acceleration-promotion.mjs
@@ -21,7 +21,7 @@ for (const phase of requiredPhases) {
const propertyType = semantics.types?.find(({ typeId }) => typeId === report.propertyType)
if (report.promotion?.from !== 'opaque-fcstd-proxy' || report.promotion.to !== 'native-editable-codec' || propertyType?.support !== report.promotion.to || report.promotion.facadeValueModel !== 'finite-number-mm/s^2' || report.promotion.fcstdElement !== 'Float' || report.promotion.nativeRoundTripValue !== 500 || report.promotion.browserRoundTripValue !== 500 || report.promotion.zeroUnknownDrift !== true) fail('capability promotion is incomplete')
const sync = report.exactBlockerSync
-if (sync?.nativeEditableTypes !== 34 || sync.nativeEditableRecords !== 4370 || sync.opaqueTypes !== 46 || sync.opaqueRecords !== 457 || sync.exactPromotionReady !== false || sync.exactBlocker !== '46 runtime property types and 457 records remain opaque-only; complete native document and property semantics') fail('exact blocker synchronization is stale')
+if (sync?.nativeEditableTypes !== 35 || sync.nativeEditableRecords !== 4372 || sync.opaqueTypes !== 45 || sync.opaqueRecords !== 455 || sync.exactPromotionReady !== false || sync.exactBlocker !== '45 runtime property types and 455 records remain opaque-only; complete native document and property semantics') fail('exact blocker synchronization is stale')
if (JSON.stringify(sync) !== JSON.stringify({ nativeEditableTypes: semantics.supportSummary['native-editable-codec'].typeCount, nativeEditableRecords: semantics.supportSummary['native-editable-codec'].recordCount, opaqueTypes: semantics.supportSummary['opaque-fcstd-proxy'].typeCount, opaqueRecords: semantics.supportSummary['opaque-fcstd-proxy'].recordCount, exactPromotionReady: semantics.exactPromotionReady, exactBlocker: semantics.exactBlocker })) fail('promotion report diverges from global property semantics')
if (roundTrip.classification?.zeroUnknownDrift !== true || chrome.status !== 'pass' || chrome.persistence?.fcstdElement !== 'Float' || chrome.persistence.loadedValue !== 500 || chrome.resource?.released !== true || chrome.release?.workerTerminated !== true) fail('G/H closure evidence regressed')
console.log(JSON.stringify({ status: 'freecad-property-acceleration-promotion-pass', propertyType: report.propertyType, promotion: report.promotion, completedPhases: requiredPhases, exactBlockerSync: sync, systemExact: report.systemExact }, null, 2))
diff --git a/scripts/check-freecad-property-area-promotion.mjs b/scripts/check-freecad-property-area-promotion.mjs
index abe3e6a..70498b2 100644
--- a/scripts/check-freecad-property-area-promotion.mjs
+++ b/scripts/check-freecad-property-area-promotion.mjs
@@ -21,7 +21,7 @@ for (const phase of requiredPhases) {
const propertyType = semantics.types?.find(({ typeId }) => typeId === report.propertyType)
if (report.promotion?.from !== 'opaque-fcstd-proxy' || report.promotion.to !== 'native-editable-codec' || propertyType?.support !== report.promotion.to || propertyType.statusNames?.join(',') !== 'PropOutput,PropReadOnly' || report.promotion.facadeValueModel !== 'read-only-derived-mm^2-with-link-sub-list-input' || report.promotion.inputType !== 'App::PropertyLinkSubList' || report.promotion.fcstdAreaElement !== 'Float' || report.promotion.fcstdElementsElement !== 'LinkSubList' || report.promotion.nativeRoundTripArea !== 20 || report.promotion.browserRoundTripArea !== 0 || report.promotion.browserRoundTripElementCount !== 0 || report.promotion.sourceGeometryPreserved !== true || report.promotion.zeroUnknownDrift !== true) fail('capability promotion is incomplete')
const sync = report.exactBlockerSync
-if (sync?.nativeEditableTypes !== 34 || sync.nativeEditableRecords !== 4370 || sync.opaqueTypes !== 46 || sync.opaqueRecords !== 457 || sync.exactPromotionReady !== false || sync.exactBlocker !== '46 runtime property types and 457 records remain opaque-only; complete native document and property semantics') fail('exact blocker synchronization is stale')
+if (sync?.nativeEditableTypes !== 35 || sync.nativeEditableRecords !== 4372 || sync.opaqueTypes !== 45 || sync.opaqueRecords !== 455 || sync.exactPromotionReady !== false || sync.exactBlocker !== '45 runtime property types and 455 records remain opaque-only; complete native document and property semantics') fail('exact blocker synchronization is stale')
if (JSON.stringify(sync) !== JSON.stringify({ nativeEditableTypes: semantics.supportSummary['native-editable-codec'].typeCount, nativeEditableRecords: semantics.supportSummary['native-editable-codec'].recordCount, opaqueTypes: semantics.supportSummary['opaque-fcstd-proxy'].typeCount, opaqueRecords: semantics.supportSummary['opaque-fcstd-proxy'].recordCount, exactPromotionReady: semantics.exactPromotionReady, exactBlocker: semantics.exactBlocker })) fail('promotion report diverges from global property semantics')
if (roundTrip.classification?.zeroUnknownDrift !== true || roundTrip.classification.sourceGeometryPreserved !== true || chrome.status !== 'pass' || chrome.ui?.areaReadOnly !== true || chrome.persistence?.fcstdAreaElement !== 'Float' || chrome.persistence.fcstdElementsElement !== 'LinkSubList' || chrome.persistence.loadedArea !== 0 || chrome.persistence.loadedElementCount !== 0 || chrome.resource?.released !== true || chrome.release?.workerTerminated !== true) fail('G/H closure evidence regressed')
console.log(JSON.stringify({ status: 'freecad-property-area-promotion-pass', propertyType: report.propertyType, promotion: report.promotion, completedPhases: requiredPhases, exactBlockerSync: sync, systemExact: report.systemExact }, null, 2))
diff --git a/scripts/check-freecad-property-boollist-promotion.mjs b/scripts/check-freecad-property-boollist-promotion.mjs
index b21ca46..9cf7a29 100644
--- a/scripts/check-freecad-property-boollist-promotion.mjs
+++ b/scripts/check-freecad-property-boollist-promotion.mjs
@@ -23,7 +23,7 @@ const target = [true, false, true, false]
const propertyType = semantics.types?.find(({ typeId }) => typeId === report.propertyType)
if (report.promotion?.from !== 'opaque-fcstd-proxy' || report.promotion.to !== 'native-editable-codec' || propertyType?.support !== report.promotion.to || propertyType.statusNames?.join(',') !== 'Hidden,Immutable,LockDynamic' || report.promotion.facadeValueModel !== 'variable-boolean-array' || report.promotion.writableRecords !== 3 || report.promotion.immutableHiddenRecords !== 4 || report.promotion.fcstdElement !== 'BoolList' || !isDeepStrictEqual(report.promotion.nativeRoundTripValue, target) || !isDeepStrictEqual(report.promotion.browserRoundTripValue, target) || report.promotion.zeroUnknownDrift !== true) fail('capability promotion is incomplete')
const sync = report.exactBlockerSync
-if (sync?.nativeEditableTypes !== 34 || sync.nativeEditableRecords !== 4370 || sync.opaqueTypes !== 46 || sync.opaqueRecords !== 457 || sync.exactPromotionReady !== false || sync.exactBlocker !== '46 runtime property types and 457 records remain opaque-only; complete native document and property semantics') fail('exact blocker synchronization is stale')
+if (sync?.nativeEditableTypes !== 35 || sync.nativeEditableRecords !== 4372 || sync.opaqueTypes !== 45 || sync.opaqueRecords !== 455 || sync.exactPromotionReady !== false || sync.exactBlocker !== '45 runtime property types and 455 records remain opaque-only; complete native document and property semantics') fail('exact blocker synchronization is stale')
if (!isDeepStrictEqual(sync, { nativeEditableTypes: semantics.supportSummary['native-editable-codec'].typeCount, nativeEditableRecords: semantics.supportSummary['native-editable-codec'].recordCount, opaqueTypes: semantics.supportSummary['opaque-fcstd-proxy'].typeCount, opaqueRecords: semantics.supportSummary['opaque-fcstd-proxy'].recordCount, exactPromotionReady: semantics.exactPromotionReady, exactBlocker: semantics.exactBlocker })) fail('promotion report diverges from global property semantics')
if (roundTrip.classification?.zeroUnknownDrift !== true || !isDeepStrictEqual(roundTrip.classification.resavedValue, target) || chrome.status !== 'pass' || chrome.persistence?.fcstdElement !== 'BoolList' || !isDeepStrictEqual(chrome.persistence.loadedValue, target) || chrome.resource?.released !== true || chrome.release?.workerTerminated !== true) fail('G/H closure evidence regressed')
console.log(JSON.stringify({ status: 'freecad-property-boollist-promotion-pass', propertyType: report.propertyType, promotion: report.promotion, completedPhases: requiredPhases, exactBlockerSync: sync, systemExact: report.systemExact }, null, 2))
diff --git a/scripts/check-freecad-property-color-promotion.mjs b/scripts/check-freecad-property-color-promotion.mjs
index a72fbcf..9a2c5c0 100644
--- a/scripts/check-freecad-property-color-promotion.mjs
+++ b/scripts/check-freecad-property-color-promotion.mjs
@@ -24,7 +24,7 @@ const approximatelyTarget = (value) => Array.isArray(value) && value.length ===
const propertyType = semantics.types?.find(({ typeId }) => typeId === report.propertyType)
if (report.promotion?.from !== 'opaque-fcstd-proxy' || report.promotion.to !== 'native-editable-codec' || propertyType?.support !== report.promotion.to || propertyType.statusNames?.length !== 0 || report.promotion.facadeValueModel !== 'four-finite-rgba-channels-with-8-bit-fcstd-packing' || report.promotion.writableRecords !== 8 || report.promotion.fcstdElement !== 'PropertyColor' || report.promotion.fcstdPackedValue !== '862362060' || !approximatelyTarget(report.promotion.nativeRoundTripValue) || !isDeepStrictEqual(report.promotion.browserRoundTripValue, target) || report.promotion.zeroUnknownDrift !== true) fail('capability promotion is incomplete')
const sync = report.exactBlockerSync
-if (sync?.nativeEditableTypes !== 34 || sync.nativeEditableRecords !== 4370 || sync.opaqueTypes !== 46 || sync.opaqueRecords !== 457 || sync.exactPromotionReady !== false || sync.exactBlocker !== '46 runtime property types and 457 records remain opaque-only; complete native document and property semantics') fail('exact blocker synchronization is stale')
+if (sync?.nativeEditableTypes !== 35 || sync.nativeEditableRecords !== 4372 || sync.opaqueTypes !== 45 || sync.opaqueRecords !== 455 || sync.exactPromotionReady !== false || sync.exactBlocker !== '45 runtime property types and 455 records remain opaque-only; complete native document and property semantics') fail('exact blocker synchronization is stale')
if (!isDeepStrictEqual(sync, { nativeEditableTypes: semantics.supportSummary['native-editable-codec'].typeCount, nativeEditableRecords: semantics.supportSummary['native-editable-codec'].recordCount, opaqueTypes: semantics.supportSummary['opaque-fcstd-proxy'].typeCount, opaqueRecords: semantics.supportSummary['opaque-fcstd-proxy'].recordCount, exactPromotionReady: semantics.exactPromotionReady, exactBlocker: semantics.exactBlocker })) fail('promotion report diverges from global property semantics')
if (roundTrip.classification?.zeroUnknownDrift !== true || !approximatelyTarget(roundTrip.classification.resavedValue) || chrome.status !== 'pass' || chrome.persistence?.fcstdElement !== 'PropertyColor' || chrome.persistence.fcstdValue !== '862362060' || !isDeepStrictEqual(chrome.persistence.loadedValue, target) || chrome.resource?.released !== true || chrome.release?.workerTerminated !== true) fail('G/H closure evidence regressed')
console.log(JSON.stringify({ status: 'freecad-property-color-promotion-pass', propertyType: report.propertyType, promotion: report.promotion, completedPhases: requiredPhases, exactBlockerSync: sync, systemExact: report.systemExact }, null, 2))
diff --git a/scripts/check-freecad-property-colorlist-promotion.mjs b/scripts/check-freecad-property-colorlist-promotion.mjs
index d6e40a0..868fa15 100644
--- a/scripts/check-freecad-property-colorlist-promotion.mjs
+++ b/scripts/check-freecad-property-colorlist-promotion.mjs
@@ -25,7 +25,7 @@ const approximately = (value, target, tolerance = 1e-7) => Array.isArray(value)
const propertyType = semantics.types?.find(({ typeId }) => typeId === report.propertyType)
if (report.promotion?.from !== 'opaque-fcstd-proxy' || report.promotion.to !== 'native-editable-codec' || propertyType?.support !== report.promotion.to || propertyType.statusNames?.length !== 0 || report.promotion.facadeValueModel !== 'ordered-variable-rgba-array-with-external-8-bit-fcstd-packing' || report.promotion.writableRecords !== 2 || report.promotion.fcstdElement !== 'ColorList' || report.promotion.fcstdResourcePath !== 'ColourList' || report.promotion.fcstdResourceBytes !== 12 || !approximately(report.promotion.nativeRoundTripValue, nativeTarget) || !isDeepStrictEqual(report.promotion.browserRoundTripValue, browserTarget) || report.promotion.zeroUnknownDrift !== true) fail('capability promotion is incomplete')
const sync = report.exactBlockerSync
-if (sync?.nativeEditableTypes !== 34 || sync.nativeEditableRecords !== 4370 || sync.opaqueTypes !== 46 || sync.opaqueRecords !== 457 || sync.exactPromotionReady !== false || sync.exactBlocker !== '46 runtime property types and 457 records remain opaque-only; complete native document and property semantics') fail('exact blocker synchronization is stale')
+if (sync?.nativeEditableTypes !== 35 || sync.nativeEditableRecords !== 4372 || sync.opaqueTypes !== 45 || sync.opaqueRecords !== 455 || sync.exactPromotionReady !== false || sync.exactBlocker !== '45 runtime property types and 455 records remain opaque-only; complete native document and property semantics') fail('exact blocker synchronization is stale')
if (!isDeepStrictEqual(sync, { nativeEditableTypes: semantics.supportSummary['native-editable-codec'].typeCount, nativeEditableRecords: semantics.supportSummary['native-editable-codec'].recordCount, opaqueTypes: semantics.supportSummary['opaque-fcstd-proxy'].typeCount, opaqueRecords: semantics.supportSummary['opaque-fcstd-proxy'].recordCount, exactPromotionReady: semantics.exactPromotionReady, exactBlocker: semantics.exactBlocker })) fail('promotion report diverges from global property semantics')
if (roundTrip.classification?.zeroUnknownDrift !== true || !approximately(roundTrip.classification.resavedValue, nativeTarget) || chrome.status !== 'pass' || chrome.persistence?.fcstdElement !== 'ColorList' || chrome.persistence.fcstdResourcePath !== 'ColourList' || chrome.persistence.fcstdResourceBytes !== 12 || !isDeepStrictEqual(chrome.persistence.loadedValue, browserTarget) || chrome.resource?.released !== true || chrome.release?.workerTerminated !== true) fail('G/H closure evidence regressed')
console.log(JSON.stringify({ status: 'freecad-property-colorlist-promotion-pass', propertyType: report.propertyType, promotion: report.promotion, completedPhases: requiredPhases, exactBlockerSync: sync, systemExact: report.systemExact }, null, 2))
diff --git a/scripts/check-freecad-property-direction-promotion.mjs b/scripts/check-freecad-property-direction-promotion.mjs
index 6265c00..c0a8633 100644
--- a/scripts/check-freecad-property-direction-promotion.mjs
+++ b/scripts/check-freecad-property-direction-promotion.mjs
@@ -24,7 +24,7 @@ const vectorEquals = (left, right) => !!left && (['x', 'y', 'z'].every((axis) =>
const propertyType = semantics.types?.find(({ typeId }) => typeId === report.propertyType)
if (report.promotion?.from !== 'opaque-fcstd-proxy' || report.promotion.to !== 'native-editable-codec' || propertyType?.support !== report.promotion.to || propertyType.recordCount !== 2 || propertyType.objectTypeCount !== 2 || report.promotion.facadeValueModel !== 'three-finite-vector-components-with-native-propertyvector-fcstd-payload' || report.promotion.writableRecords !== 2 || report.promotion.fcstdElement !== 'PropertyVector' || !vectorEquals(report.promotion.nativeRoundTripValue, target) || !vectorEquals(report.promotion.browserRoundTripValue, target) || report.promotion.zeroUnknownDrift !== true) fail('capability promotion is incomplete')
const sync = report.exactBlockerSync
-if (sync?.nativeEditableTypes !== 34 || sync.nativeEditableRecords !== 4370 || sync.opaqueTypes !== 46 || sync.opaqueRecords !== 457 || sync.exactPromotionReady !== false || sync.exactBlocker !== '46 runtime property types and 457 records remain opaque-only; complete native document and property semantics') fail('exact blocker synchronization is stale')
+if (sync?.nativeEditableTypes !== 35 || sync.nativeEditableRecords !== 4372 || sync.opaqueTypes !== 45 || sync.opaqueRecords !== 455 || sync.exactPromotionReady !== false || sync.exactBlocker !== '45 runtime property types and 455 records remain opaque-only; complete native document and property semantics') fail('exact blocker synchronization is stale')
if (!isDeepStrictEqual(sync, { nativeEditableTypes: semantics.supportSummary['native-editable-codec'].typeCount, nativeEditableRecords: semantics.supportSummary['native-editable-codec'].recordCount, opaqueTypes: semantics.supportSummary['opaque-fcstd-proxy'].typeCount, opaqueRecords: semantics.supportSummary['opaque-fcstd-proxy'].recordCount, exactPromotionReady: semantics.exactPromotionReady, exactBlocker: semantics.exactBlocker })) fail('promotion report diverges from global property semantics')
if (roundTrip.classification?.zeroUnknownDrift !== true || !vectorEquals(roundTrip.classification.resavedValue, target) || chrome.status !== 'pass' || chrome.persistence?.fcstdElement !== 'PropertyVector' || chrome.persistence.fcstdValue !== JSON.stringify(target) || !vectorEquals(chrome.persistence.loadedValue, target) || chrome.resource?.released !== true || chrome.release?.workerTerminated !== true) fail('G/H closure evidence regressed')
console.log(JSON.stringify({ status: 'freecad-property-direction-promotion-pass', propertyType: report.propertyType, promotion: report.promotion, completedPhases: requiredPhases, exactBlockerSync: sync, systemExact: report.systemExact }, null, 2))
diff --git a/scripts/check-freecad-property-file-promotion.mjs b/scripts/check-freecad-property-file-promotion.mjs
index 1690e44..38d06b5 100644
--- a/scripts/check-freecad-property-file-promotion.mjs
+++ b/scripts/check-freecad-property-file-promotion.mjs
@@ -24,7 +24,7 @@ const browserTarget = 'project-files/chrome-updated.obj'
const propertyType = semantics.types?.find(({ typeId }) => typeId === report.propertyType)
if (report.promotion?.from !== 'opaque-fcstd-proxy' || report.promotion.to !== 'native-editable-codec' || propertyType?.support !== report.promotion.to || propertyType.recordCount !== 16 || propertyType.objectTypeCount !== 12 || propertyType.statusNames?.length !== 0 || report.promotion.facadeValueModel !== 'safe-project-relative-path-string-with-native-string-fcstd-payload' || report.promotion.writableRecords !== 16 || report.promotion.objectTypeCount !== 12 || report.promotion.fcstdElement !== 'String' || report.promotion.nativeRoundTripValue !== nativeTarget || report.promotion.browserRoundTripValue !== browserTarget || report.promotion.externalResourceBoundary !== 'project-relative-path; asset bytes remain external to FCStd' || report.promotion.zeroUnknownDrift !== true) fail('capability promotion is incomplete')
const sync = report.exactBlockerSync
-if (sync?.nativeEditableTypes !== 34 || sync.nativeEditableRecords !== 4370 || sync.opaqueTypes !== 46 || sync.opaqueRecords !== 457 || sync.exactPromotionReady !== false || sync.exactBlocker !== '46 runtime property types and 457 records remain opaque-only; complete native document and property semantics') fail('exact blocker synchronization is stale')
+if (sync?.nativeEditableTypes !== 35 || sync.nativeEditableRecords !== 4372 || sync.opaqueTypes !== 45 || sync.opaqueRecords !== 455 || sync.exactPromotionReady !== false || sync.exactBlocker !== '45 runtime property types and 455 records remain opaque-only; complete native document and property semantics') fail('exact blocker synchronization is stale')
if (!isDeepStrictEqual(sync, { nativeEditableTypes: semantics.supportSummary['native-editable-codec'].typeCount, nativeEditableRecords: semantics.supportSummary['native-editable-codec'].recordCount, opaqueTypes: semantics.supportSummary['opaque-fcstd-proxy'].typeCount, opaqueRecords: semantics.supportSummary['opaque-fcstd-proxy'].recordCount, exactPromotionReady: semantics.exactPromotionReady, exactBlocker: semantics.exactBlocker })) fail('promotion report diverges from global property semantics')
if (roundTrip.classification?.zeroUnknownDrift !== true || roundTrip.classification.resavedValue !== nativeTarget || roundTrip.classification.externalResourceBoundary !== 'project-relative-path; asset bytes remain external to FCStd' || chrome.status !== 'pass' || chrome.persistence?.fcstdElement !== 'String' || chrome.persistence.fcstdValue !== browserTarget || chrome.persistence.loadedValue !== browserTarget || chrome.resource?.released !== true || chrome.resource.markerRemoved !== true || chrome.release?.workerTerminated !== true) fail('G/H closure evidence regressed')
console.log(JSON.stringify({ status: 'freecad-property-file-promotion-pass', propertyType: report.propertyType, promotion: report.promotion, completedPhases: requiredPhases, exactBlockerSync: sync, systemExact: report.systemExact }, null, 2))
diff --git a/scripts/check-freecad-property-fileincluded-promotion.mjs b/scripts/check-freecad-property-fileincluded-promotion.mjs
index bbdbbb6..3ea88de 100644
--- a/scripts/check-freecad-property-fileincluded-promotion.mjs
+++ b/scripts/check-freecad-property-fileincluded-promotion.mjs
@@ -23,7 +23,7 @@ const nativeTarget = '09f4e4853de4277e25871d26f2e953c6ea9c13a73c7a01bdfcb5209d17
const propertyType = semantics.types?.find(({ typeId }) => typeId === report.propertyType)
if (report.promotion?.from !== 'opaque-fcstd-proxy' || report.promotion.to !== 'native-editable-codec' || propertyType?.support !== report.promotion.to || propertyType.recordCount !== 20 || propertyType.objectTypeCount !== 15 || propertyType.statusNames?.join(',') !== 'PropOutput,ReadOnly' || report.promotion.facadeValueModel !== 'project-resource-identity-and-authoritative-bytes-with-native-fileincluded-fcstd-resource' || report.promotion.writableRecords !== 11 || report.promotion.readOnlyRecords !== 8 || report.promotion.outputRecords !== 1 || report.promotion.objectTypeCount !== 15 || report.promotion.fcstdElement !== 'FileIncluded' || report.promotion.nativeArchiveName !== 'payload.bin' || report.promotion.nativeRoundTripSha256 !== nativeTarget || report.promotion.browserArchiveName !== 'chrome-resource.bin' || report.promotion.browserRoundTripBytes !== 16 || report.promotion.externalResourceBoundary !== 'embedded FCStd ZIP entry restored to a read-only document transient file' || report.promotion.zeroUnknownDrift !== true) fail('capability promotion is incomplete')
const sync = report.exactBlockerSync
-if (sync?.nativeEditableTypes !== 34 || sync.nativeEditableRecords !== 4370 || sync.opaqueTypes !== 46 || sync.opaqueRecords !== 457 || sync.exactPromotionReady !== false || sync.exactBlocker !== '46 runtime property types and 457 records remain opaque-only; complete native document and property semantics') fail('exact blocker synchronization is stale')
+if (sync?.nativeEditableTypes !== 35 || sync.nativeEditableRecords !== 4372 || sync.opaqueTypes !== 45 || sync.opaqueRecords !== 455 || sync.exactPromotionReady !== false || sync.exactBlocker !== '45 runtime property types and 455 records remain opaque-only; complete native document and property semantics') fail('exact blocker synchronization is stale')
if (!isDeepStrictEqual(sync, { nativeEditableTypes: semantics.supportSummary['native-editable-codec'].typeCount, nativeEditableRecords: semantics.supportSummary['native-editable-codec'].recordCount, opaqueTypes: semantics.supportSummary['opaque-fcstd-proxy'].typeCount, opaqueRecords: semantics.supportSummary['opaque-fcstd-proxy'].recordCount, exactPromotionReady: semantics.exactPromotionReady, exactBlocker: semantics.exactBlocker })) fail('promotion report diverges from global property semantics')
if (roundTrip.classification?.zeroUnknownDrift !== true || roundTrip.classification.resavedSha256 !== nativeTarget || roundTrip.classification.resavedResourceMatches !== true || roundTrip.classification.externalResourceBoundary !== 'embedded FCStd ZIP entry restored to a read-only document transient file' || chrome.status !== 'pass' || chrome.persistence?.fcstdElement !== 'FileIncluded' || chrome.persistence.fcstdResourcePath !== 'chrome-resource.bin' || chrome.persistence.fcstdResourceBytes !== 16 || chrome.persistence.fcstdResourceMatches !== true || chrome.resource?.documentRoundTrip !== true || chrome.resource.independentReleased !== true || chrome.resource.markerRemoved !== true || chrome.release?.workerTerminated !== true) fail('G/H closure evidence regressed')
console.log(JSON.stringify({ status: 'freecad-property-fileincluded-promotion-pass', propertyType: report.propertyType, promotion: report.promotion, completedPhases: requiredPhases, exactBlockerSync: sync, systemExact: report.systemExact }, null, 2))
diff --git a/scripts/check-freecad-property-font-promotion.mjs b/scripts/check-freecad-property-font-promotion.mjs
index 070090c..611721f 100644
--- a/scripts/check-freecad-property-font-promotion.mjs
+++ b/scripts/check-freecad-property-font-promotion.mjs
@@ -23,7 +23,7 @@ const target = 'Noto Sans CJK SC'
const propertyType = semantics.types?.find(({ typeId }) => typeId === report.propertyType)
if (report.promotion?.from !== 'opaque-fcstd-proxy' || report.promotion.to !== 'native-editable-codec' || propertyType?.support !== report.promotion.to || propertyType.recordCount !== 2 || propertyType.objectTypeCount !== 2 || propertyType.statusNames?.length !== 0 || report.promotion.facadeValueModel !== 'bounded-utf8-font-family-string-with-native-string-fcstd-payload' || report.promotion.writableRecords !== 2 || report.promotion.objectTypeCount !== 2 || report.promotion.fcstdElement !== 'String' || report.promotion.defaultValue !== 'osifont' || report.promotion.nativeRoundTripValue !== target || report.promotion.browserRoundTripValue !== target || report.promotion.fontResourceBoundary !== 'font family name persists in FCStd; font files and availability remain presentation-only' || report.promotion.zeroUnknownDrift !== true) fail('capability promotion is incomplete')
const sync = report.exactBlockerSync
-if (sync?.nativeEditableTypes !== 34 || sync.nativeEditableRecords !== 4370 || sync.opaqueTypes !== 46 || sync.opaqueRecords !== 457 || sync.exactPromotionReady !== false || sync.exactBlocker !== '46 runtime property types and 457 records remain opaque-only; complete native document and property semantics') fail('exact blocker synchronization is stale')
+if (sync?.nativeEditableTypes !== 35 || sync.nativeEditableRecords !== 4372 || sync.opaqueTypes !== 45 || sync.opaqueRecords !== 455 || sync.exactPromotionReady !== false || sync.exactBlocker !== '45 runtime property types and 455 records remain opaque-only; complete native document and property semantics') fail('exact blocker synchronization is stale')
if (!isDeepStrictEqual(sync, { nativeEditableTypes: semantics.supportSummary['native-editable-codec'].typeCount, nativeEditableRecords: semantics.supportSummary['native-editable-codec'].recordCount, opaqueTypes: semantics.supportSummary['opaque-fcstd-proxy'].typeCount, opaqueRecords: semantics.supportSummary['opaque-fcstd-proxy'].recordCount, exactPromotionReady: semantics.exactPromotionReady, exactBlocker: semantics.exactBlocker })) fail('promotion report diverges from global property semantics')
if (roundTrip.classification?.zeroUnknownDrift !== true || roundTrip.classification.resavedValue !== target || roundTrip.classification.fontResourceBoundary !== 'font family name persists in FCStd; font files and availability remain presentation-only' || chrome.status !== 'pass' || chrome.persistence?.fcstdElement !== 'String' || chrome.persistence.fcstdValue !== target || chrome.persistence.loadedValue !== target || chrome.resource?.released !== true || chrome.resource.markerRemoved !== true || chrome.release?.workerTerminated !== true) fail('G/H closure evidence regressed')
console.log(JSON.stringify({ status: 'freecad-property-font-promotion-pass', propertyType: report.propertyType, promotion: report.promotion, completedPhases: requiredPhases, exactBlockerSync: sync, systemExact: report.systemExact }, null, 2))
diff --git a/scripts/check-freecad-property-force-promotion.mjs b/scripts/check-freecad-property-force-promotion.mjs
index 5a9e62b..10b9836 100644
--- a/scripts/check-freecad-property-force-promotion.mjs
+++ b/scripts/check-freecad-property-force-promotion.mjs
@@ -23,7 +23,7 @@ const promotion = report.promotion
if (promotion?.from !== 'opaque-fcstd-proxy' || promotion.to !== 'native-editable-codec' || propertyType?.support !== promotion.to || propertyType.recordCount !== 4 || propertyType.objectTypeCount !== 2 || propertyType.statusNames?.join(',') !== 'PropOutput') fail('capability identity is incomplete')
if (promotion.facadeValueModel !== 'finite-number-internal-mm-kg-per-s2-with-registered-force-units' || promotion.internalUnit !== 'mm*kg/s^2' || promotion.displayUnits?.join(',') !== 'N,kN,lbf' || promotion.newtonScale !== 1000 || promotion.writableRecords !== 1 || promotion.outputRecords !== 3 || promotion.fcstdElement !== 'Float' || promotion.nativeRoundTripValue !== 500000 || promotion.browserRoundTripValue !== 500000 || promotion.outputPropertiesPreserved !== true || promotion.zeroUnknownDrift !== true) fail('capability promotion is incomplete')
const sync = report.exactBlockerSync
-if (sync?.nativeEditableTypes !== 34 || sync.nativeEditableRecords !== 4370 || sync.opaqueTypes !== 46 || sync.opaqueRecords !== 457 || sync.exactPromotionReady !== false || sync.exactBlocker !== '46 runtime property types and 457 records remain opaque-only; complete native document and property semantics') fail('exact blocker synchronization is stale')
+if (sync?.nativeEditableTypes !== 35 || sync.nativeEditableRecords !== 4372 || sync.opaqueTypes !== 45 || sync.opaqueRecords !== 455 || sync.exactPromotionReady !== false || sync.exactBlocker !== '45 runtime property types and 455 records remain opaque-only; complete native document and property semantics') fail('exact blocker synchronization is stale')
if (JSON.stringify(sync) !== JSON.stringify({ nativeEditableTypes: semantics.supportSummary['native-editable-codec'].typeCount, nativeEditableRecords: semantics.supportSummary['native-editable-codec'].recordCount, opaqueTypes: semantics.supportSummary['opaque-fcstd-proxy'].typeCount, opaqueRecords: semantics.supportSummary['opaque-fcstd-proxy'].recordCount, exactPromotionReady: semantics.exactPromotionReady, exactBlocker: semantics.exactBlocker })) fail('promotion report diverges from global property semantics')
if (roundTrip.classification?.zeroUnknownDrift !== true || roundTrip.classification.outputPropertiesPreserved !== true || chrome.status !== 'pass' || chrome.persistence?.fcstdElement !== 'Float' || chrome.persistence.loadedValue !== 500000 || chrome.resource?.released !== true || chrome.release?.workerTerminated !== true) fail('G/H closure evidence regressed')
console.log(JSON.stringify({ status: 'freecad-property-force-promotion-pass', propertyType: report.propertyType, promotion, completedPhases: requiredPhases, exactBlockerSync: sync, systemExact: report.systemExact }, null, 2))
diff --git a/scripts/check-freecad-property-heatflux-promotion.mjs b/scripts/check-freecad-property-heatflux-promotion.mjs
index 6c4b5f2..20c8f49 100644
--- a/scripts/check-freecad-property-heatflux-promotion.mjs
+++ b/scripts/check-freecad-property-heatflux-promotion.mjs
@@ -23,7 +23,7 @@ const promotion = report.promotion
if (promotion?.from !== 'opaque-fcstd-proxy' || promotion.to !== 'native-editable-codec' || propertyType?.support !== promotion.to || propertyType.recordCount !== 1 || propertyType.objectTypeCount !== 1 || propertyType.statusNames?.length !== 0) fail('capability identity is incomplete')
if (promotion.facadeValueModel !== 'finite-number-internal-kg-per-s3-with-registered-heat-flux-units' || promotion.internalUnit !== 'kg/s^3' || promotion.displayUnits?.join(',') !== 'W/m^2,kW/m^2,W/mm^2' || promotion.wattsPerSquareMeterScale !== 1 || promotion.writableRecords !== 1 || promotion.outputRecords !== 0 || promotion.fcstdElement !== 'Float' || promotion.nativeRoundTripValue !== 500 || promotion.browserRoundTripValue !== 500 || promotion.zeroUnknownDrift !== true) fail('capability promotion is incomplete')
const sync = report.exactBlockerSync
-if (sync?.nativeEditableTypes !== 34 || sync.nativeEditableRecords !== 4370 || sync.opaqueTypes !== 46 || sync.opaqueRecords !== 457 || sync.exactPromotionReady !== false || sync.exactBlocker !== '46 runtime property types and 457 records remain opaque-only; complete native document and property semantics') fail('exact blocker synchronization is stale')
+if (sync?.nativeEditableTypes !== 35 || sync.nativeEditableRecords !== 4372 || sync.opaqueTypes !== 45 || sync.opaqueRecords !== 455 || sync.exactPromotionReady !== false || sync.exactBlocker !== '45 runtime property types and 455 records remain opaque-only; complete native document and property semantics') fail('exact blocker synchronization is stale')
if (JSON.stringify(sync) !== JSON.stringify({ nativeEditableTypes: semantics.supportSummary['native-editable-codec'].typeCount, nativeEditableRecords: semantics.supportSummary['native-editable-codec'].recordCount, opaqueTypes: semantics.supportSummary['opaque-fcstd-proxy'].typeCount, opaqueRecords: semantics.supportSummary['opaque-fcstd-proxy'].recordCount, exactPromotionReady: semantics.exactPromotionReady, exactBlocker: semantics.exactBlocker })) fail('promotion report diverges from global property semantics')
if (roundTrip.classification?.zeroUnknownDrift !== true || roundTrip.classification.nativeValueMatches !== true || chrome.status !== 'pass' || chrome.persistence?.fcstdElement !== 'Float' || chrome.persistence.loadedValue !== 500 || chrome.resource?.released !== true || chrome.release?.workerTerminated !== true) fail('G/H closure evidence regressed')
console.log(JSON.stringify({ status: 'freecad-property-heatflux-promotion-pass', propertyType: report.propertyType, promotion, completedPhases: requiredPhases, exactBlockerSync: sync, systemExact: report.systemExact }, null, 2))
diff --git a/scripts/check-freecad-property-integerset-promotion.mjs b/scripts/check-freecad-property-integerset-promotion.mjs
index 036eb74..848e4a2 100644
--- a/scripts/check-freecad-property-integerset-promotion.mjs
+++ b/scripts/check-freecad-property-integerset-promotion.mjs
@@ -15,7 +15,7 @@ const propertyType = semantics.types?.find(({ typeId }) => typeId === report.pro
const promotion = report.promotion
if (promotion?.from !== 'opaque-fcstd-proxy' || promotion.to !== 'native-editable-codec' || propertyType?.support !== promotion.to || propertyType.recordCount !== 2 || propertyType.objectTypeCount !== 2 || propertyType.statusNames?.length !== 0 || promotion.facadeValueModel !== 'sorted-unique-safe-integer-array' || promotion.writableRecords !== 2 || promotion.nativeEmptyBindingBoundary?.neverAssignedEmptyRead !== 'native-process-termination' || promotion.fcstdElement !== 'IntegerSet' || !isDeepStrictEqual(promotion.nativeRoundTripValue, target) || !isDeepStrictEqual(promotion.browserRoundTripValue, target) || promotion.zeroUnknownDrift !== true) fail('capability promotion is incomplete')
const sync = report.exactBlockerSync
-if (sync?.nativeEditableTypes !== 34 || sync.nativeEditableRecords !== 4370 || sync.opaqueTypes !== 46 || sync.opaqueRecords !== 457 || sync.exactPromotionReady !== false || sync.exactBlocker !== '46 runtime property types and 457 records remain opaque-only; complete native document and property semantics') fail('exact blocker synchronization is stale')
+if (sync?.nativeEditableTypes !== 35 || sync.nativeEditableRecords !== 4372 || sync.opaqueTypes !== 45 || sync.opaqueRecords !== 455 || sync.exactPromotionReady !== false || sync.exactBlocker !== '45 runtime property types and 455 records remain opaque-only; complete native document and property semantics') fail('exact blocker synchronization is stale')
if (!isDeepStrictEqual(sync, { nativeEditableTypes: semantics.supportSummary['native-editable-codec'].typeCount, nativeEditableRecords: semantics.supportSummary['native-editable-codec'].recordCount, opaqueTypes: semantics.supportSummary['opaque-fcstd-proxy'].typeCount, opaqueRecords: semantics.supportSummary['opaque-fcstd-proxy'].recordCount, exactPromotionReady: semantics.exactPromotionReady, exactBlocker: semantics.exactBlocker })) fail('promotion report diverges from global property semantics')
if (roundTrip.classification?.zeroUnknownDrift !== true || !isDeepStrictEqual(roundTrip.classification.resavedValue, target) || chrome.status !== 'pass' || chrome.persistence?.fcstdElement !== 'IntegerSet' || !isDeepStrictEqual(chrome.persistence.loadedValue, target) || chrome.resource?.released !== true || chrome.release?.workerTerminated !== true) fail('G/H closure evidence regressed')
console.log(JSON.stringify({ status: 'freecad-property-integerset-promotion-pass', propertyType: report.propertyType, promotion, completedPhases: requiredPhases, exactBlockerSync: sync, systemExact: report.systemExact }, null, 2))
diff --git a/scripts/check-freecad-property-linklisthidden-failure.mjs b/scripts/check-freecad-property-linklisthidden-failure.mjs
new file mode 100644
index 0000000..8768af6
--- /dev/null
+++ b/scripts/check-freecad-property-linklisthidden-failure.mjs
@@ -0,0 +1,48 @@
+import { isDeepStrictEqual } from 'node:util'
+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-linklisthidden-failure.json'), 'utf8'))
+const fail = (message) => { throw new Error(`FreeCAD PropertyLinkListHidden failure check failed: ${message}`) }
+if (report.schemaVersion !== 1 || report.status !== 'pass' || report.baselineId !== 'freecad-1.1.1-property-linklisthidden-failure' || report.freecadVersion !== '1.1.1' || report.gitCommit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d' || report.propertyType !== 'App::PropertyLinkListHidden') fail('baseline is invalid')
+if (report.object?.name !== 'SketchProbe' || report.object.typeId !== 'Sketcher::SketchObject' || report.property?.name !== 'Exports' || report.property.typeId !== 'App::PropertyLinkListHidden') fail('fixture identity changed')
+const expectedObjects = [
+ { name: 'SketchProbe', typeId: 'Sketcher::SketchObject' },
+ { name: 'ExportA', typeId: 'Part::Feature' },
+ { name: 'ExportB', typeId: 'Part::Feature' },
+]
+const initial = report.initial
+if (!isDeepStrictEqual(initial?.value, ['ExportA', 'ExportB']) || initial.propertyTypeId !== 'App::PropertyLinkListHidden' || !isDeepStrictEqual(initial.propertyStatus, ['26']) || !isDeepStrictEqual(initial.editorMode, ['Hidden']) || !isDeepStrictEqual(initial.ownerState, ['Up-to-date']) || initial.statusString !== 'Valid' || initial.ownerShapeNull !== true || !isDeepStrictEqual(initial.ownerOutList, []) || !isDeepStrictEqual(initial.targetInLists, { ExportA: [], ExportB: [] }) || typeof initial.targetShapeHashes?.ExportA !== 'string' || typeof initial.targetShapeHashes?.ExportB !== 'string' || !isDeepStrictEqual(initial.objectSet, expectedObjects)) fail('initial hidden-link state is invalid')
+const assertBoundary = (snapshot, label) => {
+ if (snapshot?.propertyTypeId !== 'App::PropertyLinkListHidden' || snapshot.ownerShapeNull !== true || !isDeepStrictEqual(snapshot.ownerOutList, []) || !isDeepStrictEqual(snapshot.targetInLists, { ExportA: [], ExportB: [] }) || !isDeepStrictEqual(snapshot.targetShapeHashes, initial.targetShapeHashes) || !isDeepStrictEqual(snapshot.objectSet, expectedObjects)) fail(`${label} polluted hidden dependencies, Shapes, or objects`)
+}
+const expectedFailures = new Map([
+ ['string-scalar', ['TypeError', 'Type must be App.DocumentObject or None, not str']],
+ ['string-item', ['TypeError', 'Type must be App.DocumentObject or None, not str']],
+ ['integer-scalar', ['TypeError', 'Type must be App.DocumentObject or None, not int']],
+ ['integer-item', ['TypeError', 'Type must be App.DocumentObject or None, not int']],
+ ['cross-document', ['ValueError', 'PropertyLinkList does not support external object']],
+])
+if (report.failures?.length !== expectedFailures.size) fail('failure case count changed')
+for (const entry of report.failures) {
+ const expected = expectedFailures.get(entry.id)
+ if (!expected || entry.exception?.type !== expected[0] || entry.exception.message !== expected[1] || entry.valuePreserved !== true || entry.documentPreserved !== true || !isDeepStrictEqual(entry.before, initial) || !isDeepStrictEqual(entry.after, initial)) fail(`${entry.id} did not reject atomically`)
+}
+
+const editor = report.editorReadOnly
+if (editor?.classification !== 'editor-read-only-python-setter-accepted' || !isDeepStrictEqual(editor.editorMode, ['ReadOnly', 'Hidden']) || editor.exception !== null || editor.pythonBypassesEditorReadOnly !== true || !isDeepStrictEqual(editor.before.value, ['ExportA', 'ExportB']) || !isDeepStrictEqual(editor.after.value, ['ExportB']) || !isDeepStrictEqual(editor.after.propertyStatus, ['ReadOnly', '26']) || !isDeepStrictEqual(editor.after.editorMode, ['ReadOnly', 'Hidden']) || !isDeepStrictEqual(editor.after.ownerState, ['Touched']) || !isDeepStrictEqual(editor.restoredEditorMode, ['Hidden'])) fail('ReadOnly Python acceptance boundary changed')
+assertBoundary(editor.before, 'ReadOnly before')
+assertBoundary(editor.after, 'ReadOnly after')
+const immutable = report.immutable
+if (immutable?.classification !== 'immutable-python-setter-accepted' || immutable.exception !== null || immutable.pythonBypassesImmutable !== true || !isDeepStrictEqual(immutable.before.value, ['ExportA', 'ExportB']) || !isDeepStrictEqual(immutable.after.value, ['ExportB']) || !isDeepStrictEqual(immutable.before.propertyStatus, ['Immutable', '26']) || !isDeepStrictEqual(immutable.after.propertyStatus, ['Immutable', '26']) || !isDeepStrictEqual(immutable.after.ownerState, ['Touched']) || !isDeepStrictEqual(immutable.restoredStatus, ['26'])) fail('Immutable Python acceptance boundary changed')
+assertBoundary(immutable.before, 'Immutable before')
+assertBoundary(immutable.after, 'Immutable after')
+
+const transaction = report.transaction
+if (transaction?.undoMode !== 1 || transaction.pendingAfterEdit !== true || transaction.pendingAfterAbort !== false || transaction.activeAfterEdit?.name !== 'property-linklisthidden-cancel' || !(transaction.activeAfterEdit.id > 0) || transaction.activeAfterAbort?.name !== '' || transaction.activeAfterAbort.id !== 0 || !isDeepStrictEqual(transaction.before?.value, ['ExportA', 'ExportB']) || !isDeepStrictEqual(transaction.edited?.value, ['ExportB', 'ExportA', 'ExportB']) || !isDeepStrictEqual(transaction.afterAbortBeforeRecompute?.value, ['ExportA', 'ExportB']) || !isDeepStrictEqual(transaction.afterAbortBeforeRecompute.ownerState, ['Touched']) || transaction.recomputeAfterAbort !== true || !isDeepStrictEqual(transaction.afterAbort?.ownerState, ['Up-to-date']) || transaction.afterAbort.statusString !== 'Valid' || transaction.restored !== true || !isDeepStrictEqual(transaction.before, transaction.afterAbort)) fail('transaction abort and recompute recovery changed')
+for (const [name, snapshot] of Object.entries({ before: transaction.before, edited: transaction.edited, afterAbortBeforeRecompute: transaction.afterAbortBeforeRecompute, afterAbort: transaction.afterAbort })) assertBoundary(snapshot, `transaction ${name}`)
+if (report.cancellationBoundary?.supported !== false || report.cancellationBoundary.classification !== 'synchronous-property-setter' || report.cancellationBoundary.reason !== 'no-native-cancel-hook' || report.cancellationBoundary.replacement !== 'abort-active-document-transaction') fail('cancellation boundary is incomplete')
+if (report.documentIntegrity?.objectsPreserved !== true || !isDeepStrictEqual(report.documentIntegrity.initialObjects, expectedObjects) || !isDeepStrictEqual(report.documentIntegrity.finalObjects, expectedObjects) || !isDeepStrictEqual(report.documentIntegrity.foreignDocumentObjects, [{ name: 'ForeignExport', typeId: 'Part::Feature' }])) fail('failure cases polluted either document')
+
+console.log(JSON.stringify({ status: 'freecad-property-linklisthidden-failure-pass', failures: report.failures.map(({ id, exception }) => ({ id, exception })), readOnlyPythonAccepted: editor.pythonBypassesEditorReadOnly, immutablePythonAccepted: immutable.pythonBypassesImmutable, transactionRestored: transaction.restored, cancellationBoundary: report.cancellationBoundary }, null, 2))
diff --git a/scripts/check-freecad-property-linklisthidden-inventory.mjs b/scripts/check-freecad-property-linklisthidden-inventory.mjs
new file mode 100644
index 0000000..70b8783
--- /dev/null
+++ b/scripts/check-freecad-property-linklisthidden-inventory.mjs
@@ -0,0 +1,50 @@
+import { createHash } from 'node:crypto'
+import { isDeepStrictEqual } from 'node:util'
+import { readFile } from 'node:fs/promises'
+import { resolve } from 'node:path'
+
+const root = resolve(new URL('..', import.meta.url).pathname)
+const runtimePath = '.cache/freecad/reference-desktop.json'
+const reportPath = 'config/freecad-property-linklisthidden-inventory.json'
+const [runtimeContent, reportContent] = await Promise.all([readFile(resolve(root, runtimePath)), readFile(resolve(root, reportPath))])
+const runtime = JSON.parse(runtimeContent)
+const report = JSON.parse(reportContent)
+const fail = (message) => { throw new Error(`FreeCAD PropertyLinkListHidden inventory check failed: ${message}`) }
+if (report.schemaVersion !== 1 || report.status !== 'pass' || report.propertyType !== 'App::PropertyLinkListHidden' || report.classification !== 'opaque-fcstd-proxy' || report.baseline?.freecadVersion !== '1.1.1' || report.baseline?.commit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d') fail('report boundary is invalid')
+if (report.recordCount !== 2 || report.objectTypeCount !== 2 || report.writableRecordCount !== 2 || report.records?.length !== 2) fail('runtime record or host count changed')
+const typeContract = report.typeContract
+if (typeContract?.inheritedFrom !== 'App::PropertyLinkList' || typeContract.linkScope !== 'Hidden' || typeContract.propertyStatus !== 'PropHidden' || typeContract.xmlElement !== 'LinkList' || typeContract.xmlChildElement !== 'Link' || typeContract.legacyRestoreType !== 'App::PropertyLinkList') fail('type contract is incomplete')
+if (report.provenance?.runtime?.path !== runtimePath || report.provenance.runtime.bytes !== runtimeContent.length || report.provenance.runtime.sha256 !== createHash('sha256').update(runtimeContent).digest('hex')) fail('runtime provenance is stale')
+for (const locked of 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(`source provenance is stale for ${locked.path}`)
+}
+
+const nativeMatches = []
+for (const objectType of runtime.runtimeObjects?.types ?? []) for (const property of objectType.properties ?? []) {
+ if (property.typeId === 'App::PropertyLinkListHidden') nativeMatches.push({ objectTypeId: objectType.typeId, objectAvailable: objectType.available === true, probeStatus: objectType.probeStatus, propertyName: property.name, group: property.group, statusRaw: property.status, defaultRaw: property.default })
+}
+const identity = ({ objectTypeId, objectAvailable, probeStatus, propertyName, group, statusRaw, defaultRaw }) => ({ objectTypeId, objectAvailable, probeStatus, propertyName, group, statusRaw, defaultRaw })
+const sorted = (records) => records.map(identity).sort((left, right) => `${left.objectTypeId}.${left.propertyName}`.localeCompare(`${right.objectTypeId}.${right.propertyName}`))
+if (!isDeepStrictEqual(sorted(nativeMatches), sorted(report.records))) fail('report diverges from the locked runtime oracle')
+const expectedKeys = ['Sketcher::SketchObject.Exports', 'Sketcher::SketchObjectPython.Exports']
+if (!isDeepStrictEqual(report.records.map(({ objectTypeId, propertyName }) => `${objectTypeId}.${propertyName}`), expectedKeys)) fail('native host/property identities changed')
+for (const record of report.records) {
+ const model = record.valueModel
+ const inputs = record.inputs
+ const dependencies = record.dependencies
+ if (record.objectAvailable !== true || record.probeStatus !== 'available' || record.group !== 'Sketch' || !isDeepStrictEqual(record.statusRaw, [26]) || !isDeepStrictEqual(record.statusNames, ['PropHidden']) || !isDeepStrictEqual(record.defaultRaw, [])) fail(`native metadata changed for ${record.objectTypeId}.Exports`)
+ if (model?.kind !== 'hidden-document-object-list' || model.inheritedFrom !== 'App::PropertyLinkList' || model.elementType !== 'same-document App::DocumentObject reference' || model.orderPreserved !== true || model.duplicatesPreserved !== true || model.writable !== true) fail(`value model changed for ${record.objectTypeId}.Exports`)
+ if (inputs?.nativeCppSetValues !== 'attached same-document DocumentObject list' || inputs.emptyListClears !== true || inputs.singleNullListClears !== true || inputs.detachedObjectRejected !== true || inputs.crossDocumentRejectedWithoutLinkAllowExternal !== true) fail(`input preconditions changed for ${record.objectTypeId}.Exports`)
+ if (dependencies?.linkScope !== 'Hidden' || dependencies.createsBacklinks !== false || dependencies.includedByDefaultGetLinks !== false || dependencies.includedByGetLinksAll !== true || dependencies.defaultDagDependency !== false) fail(`hidden dependency scope changed for ${record.objectTypeId}.Exports`)
+ if (record.applicability?.requiredObjectTypeId !== record.objectTypeId || record.applicability.role !== 'Sketch exported child geometry lookup by object name' || record.applicability.propertyWriteRequiresShape !== false) fail(`applicability changed for ${record.objectTypeId}.Exports`)
+}
+
+const sources = Object.fromEntries(await Promise.all(report.provenance.sources.map(async ({ path }) => [path, await readFile(resolve(root, path), 'utf8')])))
+const linksHeader = sources['.cache/freecad/FreeCAD/src/App/PropertyLinks.h']
+const linksSource = sources['.cache/freecad/FreeCAD/src/App/PropertyLinks.cpp']
+const sketchHeader = sources['.cache/freecad/FreeCAD/src/Mod/Sketcher/App/SketchObject.h']
+const sketchSource = sources['.cache/freecad/FreeCAD/src/Mod/Sketcher/App/SketchObject.cpp']
+if (!linksHeader.includes('class AppExport PropertyLinkListHidden: public PropertyLinkList') || !linksHeader.includes('_pcScope = LinkScope::Hidden;') || !linksSource.includes('TYPESYSTEM_SOURCE(App::PropertyLinkListHidden, App::PropertyLinkList)') || !linksSource.includes('if (all || _pcScope != LinkScope::Hidden)') || !linksSource.includes(' objectTypeId), propertyStatus: typeContract.propertyStatus, linkScope: typeContract.linkScope, classification: report.classification }, null, 2))
diff --git a/scripts/check-freecad-property-linklisthidden-mutation.mjs b/scripts/check-freecad-property-linklisthidden-mutation.mjs
new file mode 100644
index 0000000..907304a
--- /dev/null
+++ b/scripts/check-freecad-property-linklisthidden-mutation.mjs
@@ -0,0 +1,30 @@
+import { isDeepStrictEqual } from 'node:util'
+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-linklisthidden-mutation.json'), 'utf8'))
+const fail = (message) => { throw new Error(`FreeCAD PropertyLinkListHidden mutation check failed: ${message}`) }
+if (report.schemaVersion !== 1 || report.status !== 'pass' || report.baselineId !== 'freecad-1.1.1-property-linklisthidden-mutation' || report.freecadVersion !== '1.1.1' || report.gitCommit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d' || report.propertyType !== 'App::PropertyLinkListHidden' || report.caseCount !== 2 || report.cases?.length !== 2) fail('baseline is invalid')
+const expectedHosts = new Map([
+ ['Sketcher::SketchObject', 'SketchProbe'],
+ ['Sketcher::SketchObjectPython', 'SketchPythonProbe'],
+])
+const expectedObjects = (ownerName, ownerTypeId) => [
+ { name: ownerName, typeId: ownerTypeId },
+ { name: 'ExportA', typeId: 'Part::Feature' },
+ { name: 'ExportB', typeId: 'Part::Feature' },
+]
+for (const entry of report.cases) {
+ const ownerName = expectedHosts.get(entry.objectTypeId)
+ if (!ownerName || entry.objectName !== ownerName || entry.propertyName !== 'Exports' || entry.editRecomputeResult !== true || entry.restoreRecomputeResult !== true || entry.valueRestored !== true || entry.semanticStateRestored !== true) fail(`${entry.objectTypeId} mutation identity or recovery changed`)
+ if (!isDeepStrictEqual(entry.before?.value, ['ExportA', 'ExportB']) || !isDeepStrictEqual(entry.touchedAfterEdit?.value, ['ExportB', 'ExportA', 'ExportB']) || !isDeepStrictEqual(entry.edited?.value, ['ExportB', 'ExportA', 'ExportB']) || !isDeepStrictEqual(entry.touchedAfterRestore?.value, ['ExportA', 'ExportB']) || !isDeepStrictEqual(entry.restored?.value, ['ExportA', 'ExportB'])) fail(`${entry.objectTypeId} value mutation changed`)
+ if (!isDeepStrictEqual(entry.before.ownerState, ['Up-to-date']) || entry.before.statusString !== 'Valid' || !isDeepStrictEqual(entry.touchedAfterEdit.ownerState, ['Touched']) || entry.touchedAfterEdit.statusString !== 'Touched' || !isDeepStrictEqual(entry.edited.ownerState, ['Up-to-date']) || entry.edited.statusString !== 'Valid' || !isDeepStrictEqual(entry.touchedAfterRestore.ownerState, ['Touched']) || entry.touchedAfterRestore.statusString !== 'Touched' || !isDeepStrictEqual(entry.restored.ownerState, ['Up-to-date']) || entry.restored.statusString !== 'Valid') fail(`${entry.objectTypeId} state transition changed`)
+ const baselineTargets = entry.before.targetShapes
+ for (const [phase, snapshot] of Object.entries({ before: entry.before, touchedAfterEdit: entry.touchedAfterEdit, edited: entry.edited, touchedAfterRestore: entry.touchedAfterRestore, restored: entry.restored })) {
+ if (snapshot.propertyTypeId !== 'App::PropertyLinkListHidden' || snapshot.group !== 'Sketch' || !isDeepStrictEqual(snapshot.propertyStatus, ['26']) || !isDeepStrictEqual(snapshot.editorMode, ['Hidden']) || snapshot.ownerShape?.isNull !== true || snapshot.ownerShape.solids !== 0 || snapshot.ownerShape.faces !== 0 || snapshot.ownerShape.edges !== 0 || snapshot.ownerShape.vertices !== 0 || snapshot.ownerShape.brepSha256 !== null) fail(`${entry.objectTypeId} ${phase} metadata or host Shape changed`)
+ if (!isDeepStrictEqual(snapshot.ownerOutList, []) || !isDeepStrictEqual(snapshot.targetInLists, { ExportA: [], ExportB: [] }) || !isDeepStrictEqual(snapshot.targetShapes, baselineTargets) || !isDeepStrictEqual(snapshot.objectSet, expectedObjects(ownerName, entry.objectTypeId))) fail(`${entry.objectTypeId} ${phase} leaked dependencies or changed targets/objects`)
+ }
+ if (baselineTargets?.ExportA?.solids !== 1 || baselineTargets.ExportA.faces !== 6 || baselineTargets.ExportA.edges !== 12 || baselineTargets.ExportA.vertices !== 8 || baselineTargets.ExportB?.solids !== 1 || baselineTargets.ExportB.faces !== 3 || baselineTargets.ExportB.edges !== 3 || baselineTargets.ExportB.vertices !== 2) fail(`${entry.objectTypeId} target topology changed`)
+}
+console.log(JSON.stringify({ status: 'freecad-property-linklisthidden-mutation-pass', cases: report.cases.map(({ objectTypeId, before, edited, restored, semanticStateRestored }) => ({ objectTypeId, before: before.value, edited: edited.value, restored: restored.value, semanticStateRestored })) }, null, 2))
diff --git a/scripts/check-freecad-property-linklisthidden-promotion.mjs b/scripts/check-freecad-property-linklisthidden-promotion.mjs
new file mode 100644
index 0000000..53cd9d3
--- /dev/null
+++ b/scripts/check-freecad-property-linklisthidden-promotion.mjs
@@ -0,0 +1,28 @@
+import { isDeepStrictEqual } from 'node:util'
+import { readFile } from 'node:fs/promises'
+import { resolve } from 'node:path'
+
+const root = resolve(new URL('..', import.meta.url).pathname)
+const load = (path) => readFile(resolve(root, path), 'utf8').then(JSON.parse)
+const [report, semantics, progress, roundTrip, chrome] = await Promise.all([
+ load('config/freecad-property-linklisthidden-promotion.json'),
+ load('config/freecad-native-property-semantics.json'),
+ load('config/freecad-follow-up-task-progress.json'),
+ load('config/freecad-property-linklisthidden-roundtrip.json'),
+ load('config/chrome-property-linklisthidden-verification.json'),
+])
+const fail = (message) => { throw new Error(`FreeCAD PropertyLinkListHidden promotion check failed: ${message}`) }
+if (report.schemaVersion !== 1 || report.status !== 'pass' || report.taskId !== 'PROP-app-propertylinklisthidden-I' || report.propertyType !== 'App::PropertyLinkListHidden' || report.recordCount !== 2 || report.baseline?.freecadVersion !== '1.1.1' || report.baseline?.commit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d' || report.systemExact !== false) fail('promotion report boundary is invalid')
+const requiredPhases = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
+const completed = new Map(progress.completedTasks?.map((entry) => [entry.id, entry]) ?? [])
+for (const phase of requiredPhases) { const taskId = `PROP-app-propertylinklisthidden-${phase}`; if (!completed.has(taskId) || !Array.isArray(report.phaseEvidence?.[phase]) || report.phaseEvidence[phase].length === 0 || !isDeepStrictEqual(report.phaseEvidence[phase], completed.get(taskId).evidence)) fail(`phase ${phase} evidence is incomplete`) }
+const initial = ['ExportA', 'ExportB']
+const target = ['ExportB', 'ExportA', 'ExportB']
+const propertyType = semantics.types?.find(({ typeId }) => typeId === report.propertyType)
+const promotion = report.promotion
+if (promotion?.from !== 'opaque-fcstd-proxy' || promotion.to !== 'native-editable-codec' || propertyType?.support !== promotion.to || propertyType.recordCount !== 2 || propertyType.objectTypeCount !== 2 || propertyType.statusNames?.join(',') !== 'PropHidden' || promotion.facadeValueModel !== 'ordered-duplicate-preserving-same-document-object-name-array' || promotion.writableRecords !== 2 || promotion.objectTypeCount !== 2 || promotion.propertyStatus !== 'PropHidden' || promotion.linkScope !== 'Hidden' || promotion.runtimeDependencyEdges !== 0 || promotion.fcstdElement !== 'LinkList' || !isDeepStrictEqual(promotion.nativeRoundTripValue, target) || !isDeepStrictEqual(promotion.browserRoundTripValue, target) || promotion.hiddenUiFiltered !== true || promotion.hiddenLinksPreserved !== true || promotion.nativeDependencyMetadataNormalized !== true || promotion.zeroUnknownDrift !== true) fail('capability promotion is incomplete')
+const sync = report.exactBlockerSync
+if (sync?.nativeEditableTypes !== 35 || sync.nativeEditableRecords !== 4372 || sync.opaqueTypes !== 45 || sync.opaqueRecords !== 455 || sync.exactPromotionReady !== false || sync.exactBlocker !== '45 runtime property types and 455 records remain opaque-only; complete native document and property semantics') fail('exact blocker synchronization is stale')
+if (!isDeepStrictEqual(sync, { nativeEditableTypes: semantics.supportSummary['native-editable-codec'].typeCount, nativeEditableRecords: semantics.supportSummary['native-editable-codec'].recordCount, opaqueTypes: semantics.supportSummary['opaque-fcstd-proxy'].typeCount, opaqueRecords: semantics.supportSummary['opaque-fcstd-proxy'].recordCount, exactPromotionReady: semantics.exactPromotionReady, exactBlocker: semantics.exactBlocker })) fail('promotion report diverges from global property semantics')
+if (roundTrip.classification?.zeroUnknownDrift !== true || !isDeepStrictEqual(roundTrip.classification.resavedValue, target) || roundTrip.classification.hiddenLinksPreserved !== true || roundTrip.classification.nativeDependencyMetadataNormalized !== true || chrome.status !== 'pass' || !isDeepStrictEqual(chrome.persistence?.loadedValue, target) || chrome.persistence?.fcstdElement !== 'LinkList' || chrome.ui?.hiddenControlCountBefore !== 0 || chrome.ui?.hiddenControlCountAfter !== 0 || chrome.resource?.released !== true || chrome.release?.workerTerminated !== true) fail('G/H closure evidence regressed')
+console.log(JSON.stringify({ status: 'freecad-property-linklisthidden-promotion-pass', propertyType: report.propertyType, promotion, completedPhases: requiredPhases, exactBlockerSync: sync, systemExact: report.systemExact }, null, 2))
diff --git a/scripts/check-freecad-property-linklisthidden-roundtrip.mjs b/scripts/check-freecad-property-linklisthidden-roundtrip.mjs
new file mode 100644
index 0000000..118e5a4
--- /dev/null
+++ b/scripts/check-freecad-property-linklisthidden-roundtrip.mjs
@@ -0,0 +1,26 @@
+import { createHash } from 'node:crypto'
+import { isDeepStrictEqual } from 'node:util'
+import { readFile, stat } 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-linklisthidden-roundtrip.json'), 'utf8'))
+const fail = (message) => { throw new Error(`FreeCAD PropertyLinkListHidden roundtrip check failed: ${message}`) }
+if (report.schemaVersion !== 1 || report.status !== 'pass' || report.baselineId !== 'freecad-1.1.1-property-linklisthidden-roundtrip' || report.freecadVersion !== '1.1.1' || report.gitCommit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d' || report.propertyType !== 'App::PropertyLinkListHidden') fail('baseline is invalid')
+const initial = ['ExportA', 'ExportB']
+const target = ['ExportB', 'ExportA', 'ExportB']
+const objectSet = [{ name: 'SketchProbe', typeId: 'Sketcher::SketchObject' }, { name: 'ExportA', typeId: 'Part::Feature' }, { name: 'ExportB', typeId: 'Part::Feature' }]
+for (const [phase, snapshot, value] of [['nativeInitial', report.nativeInitial, initial], ['nativeReopened', report.nativeAfter?.reopened, target], ['nativeResaved', report.nativeAfter?.resaved, target]]) {
+ if (!isDeepStrictEqual(snapshot?.objectSet, objectSet) || snapshot.object?.name !== 'SketchProbe' || snapshot.object.typeId !== 'Sketcher::SketchObject' || snapshot.object.propertyName !== 'Exports' || snapshot.object.propertyTypeId !== 'App::PropertyLinkListHidden' || snapshot.object.group !== 'Sketch' || !isDeepStrictEqual(snapshot.object.value, value) || !isDeepStrictEqual(snapshot.object.propertyStatus, ['26']) || !isDeepStrictEqual(snapshot.object.editorMode, ['Hidden']) || !isDeepStrictEqual(snapshot.object.state, ['Up-to-date']) || snapshot.object.statusString !== 'Valid' || !isDeepStrictEqual(snapshot.object.outList, [])) fail(`${phase} native hidden-link semantics changed`)
+ const ownerShape = snapshot.object.shape
+ if (ownerShape?.isNull !== true || ownerShape.valid !== false || ownerShape.shapeType !== 'Null' || ownerShape.solids !== 0 || ownerShape.faces !== 0 || ownerShape.edges !== 0 || ownerShape.vertices !== 0 || ownerShape.brepSha256 !== null) fail(`${phase} owner Shape boundary changed`)
+ const box = snapshot.targets?.ExportA
+ const cylinder = snapshot.targets?.ExportB
+ if (box?.typeId !== 'Part::Feature' || !isDeepStrictEqual(box.inList, []) || box.shape?.isNull !== false || box.shape.valid !== true || box.shape.shapeType !== 'Solid' || box.shape.solids !== 1 || box.shape.faces !== 6 || box.shape.edges !== 12 || box.shape.vertices !== 8 || box.shape.area !== 52 || box.shape.volume !== 24 || !/^[0-9a-f]{64}$/.test(box.shape.brepSha256)) fail(`${phase} box target changed`)
+ if (cylinder?.typeId !== 'Part::Feature' || !isDeepStrictEqual(cylinder.inList, []) || cylinder.shape?.isNull !== false || cylinder.shape.valid !== true || cylinder.shape.shapeType !== 'Solid' || cylinder.shape.solids !== 1 || cylinder.shape.faces !== 3 || cylinder.shape.edges !== 3 || cylinder.shape.vertices !== 2 || cylinder.shape.area !== 61.261056745 || cylinder.shape.volume !== 35.342917353 || !/^[0-9a-f]{64}$/.test(cylinder.shape.brepSha256)) fail(`${phase} cylinder target changed`)
+}
+if (report.web.before?.typeId !== 'App::PropertyLinkListHidden' || report.web.before.element !== 'LinkList' || report.web.after?.typeId !== 'App::PropertyLinkListHidden' || report.web.after.element !== 'LinkList' || report.web.targetMarkedTouched !== true || !isDeepStrictEqual(report.web.initialDependencyTargets, initial) || !isDeepStrictEqual(report.web.webDependencyTargets, initial) || report.web.hiddenDependencyMetadataPreserved !== true || report.web.webOutputMatches !== true || report.web.objectSetPreserved !== true || report.web.semanticObjectsPreserved !== true || report.web.opaqueEntriesPreserved !== true || !Number.isSafeInteger(report.web.opaquePathsPreserved) || report.web.opaquePathsPreserved < 1) fail('Web edit did not preserve the native archive boundary')
+if (!isDeepStrictEqual(report.classification?.requestedValue, target) || !isDeepStrictEqual(report.classification.webValue, target) || !isDeepStrictEqual(report.classification.reopenedValue, target) || !isDeepStrictEqual(report.classification.resavedValue, target) || report.classification.nativeOutputMatches !== true || report.classification.hiddenLinksPreserved !== true || !isDeepStrictEqual(report.classification.nativeResavedDependencyTargets, target) || report.classification.nativeDependencyMetadataNormalized !== true || report.classification.targetShapeStructurePreserved !== true || report.classification.ownerShapePreserved !== true || report.classification.unknownSemanticDrift !== false || report.classification.zeroUnknownDrift !== true || report.classification.nativeTargetBrepHashes?.length !== 3) fail('roundtrip classification is incomplete')
+for (const hashes of report.classification.nativeTargetBrepHashes) if (!/^[0-9a-f]{64}$/.test(hashes.ExportA) || !/^[0-9a-f]{64}$/.test(hashes.ExportB)) fail('native target BREP evidence is incomplete')
+for (const archive of Object.values(report.archives ?? {})) { const path = resolve(root, archive.path ?? ''); const content = await readFile(path); if ((await stat(path)).size !== archive.bytes || createHash('sha256').update(content).digest('hex') !== archive.sha256) fail(`archive is stale: ${archive.path}`) }
+console.log(JSON.stringify({ status: 'freecad-property-linklisthidden-roundtrip-pass', values: { nativeInitial: initial, webEdited: report.classification.webValue, nativeReopened: report.classification.reopenedValue, nativeResaved: report.classification.resavedValue }, hiddenLinksPreserved: report.classification.hiddenLinksPreserved, targetShapeStructurePreserved: report.classification.targetShapeStructurePreserved, opaquePathsPreserved: report.web.opaquePathsPreserved, zeroUnknownDrift: report.classification.zeroUnknownDrift }, null, 2))
diff --git a/scripts/check-freecad-property-linklisthidden-success.mjs b/scripts/check-freecad-property-linklisthidden-success.mjs
new file mode 100644
index 0000000..753394c
--- /dev/null
+++ b/scripts/check-freecad-property-linklisthidden-success.mjs
@@ -0,0 +1,41 @@
+import { isDeepStrictEqual } from 'node:util'
+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-linklisthidden-success.json'), 'utf8'))
+const fail = (message) => { throw new Error(`FreeCAD PropertyLinkListHidden success check failed: ${message}`) }
+if (report.schemaVersion !== 1 || report.status !== 'pass' || report.baselineId !== 'freecad-1.1.1-property-linklisthidden-success' || report.freecadVersion !== '1.1.1' || report.gitCommit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d' || report.propertyType !== 'App::PropertyLinkListHidden') fail('baseline is invalid')
+if (report.caseCount !== 2 || report.cases?.length !== 2 || !isDeepStrictEqual(report.bindingBoundary?.oracleDefault, []) || report.bindingBoundary.safeFirstRead !== 'assign-empty-list-before-read') fail('case inventory or first-read boundary changed')
+const expectedHosts = new Map([
+ ['Sketcher::SketchObject', 'SketchProbe'],
+ ['Sketcher::SketchObjectPython', 'SketchPythonProbe'],
+])
+const expectedValues = {
+ explicitEmpty: [],
+ single: ['ExportA'],
+ ordered: ['ExportB', 'ExportA'],
+ duplicates: ['ExportA', 'ExportB', 'ExportA'],
+ restored: [],
+}
+const expectedObjects = (ownerName, ownerTypeId) => [
+ { name: ownerName, typeId: ownerTypeId },
+ { name: 'ExportA', typeId: 'Part::Feature' },
+ { name: 'ExportB', typeId: 'Part::Feature' },
+]
+for (const entry of report.cases) {
+ const objectName = expectedHosts.get(entry.objectTypeId)
+ if (!objectName || entry.objectName !== objectName || entry.propertyName !== 'Exports' || entry.group !== 'Sketch' || entry.mode !== 'direct-native-property-setter') fail(`${entry.objectTypeId} identity changed`)
+ const baselineTargets = entry.phases?.explicitEmpty?.targetShapes
+ if (baselineTargets?.ExportA?.isNull !== false || baselineTargets.ExportA.solids !== 1 || baselineTargets.ExportA.faces !== 6 || baselineTargets.ExportA.edges !== 12 || baselineTargets.ExportA.vertices !== 8 || typeof baselineTargets.ExportA.brepSha256 !== 'string') fail(`${entry.objectTypeId} box target is invalid`)
+ if (baselineTargets?.ExportB?.isNull !== false || baselineTargets.ExportB.solids !== 1 || baselineTargets.ExportB.faces !== 3 || baselineTargets.ExportB.edges !== 3 || baselineTargets.ExportB.vertices !== 2 || typeof baselineTargets.ExportB.brepSha256 !== 'string') fail(`${entry.objectTypeId} cylinder target is invalid`)
+ for (const [phase, expectedValue] of Object.entries(expectedValues)) {
+ const snapshot = entry.phases?.[phase]
+ if (!isDeepStrictEqual(snapshot?.value, expectedValue) || snapshot.propertyTypeId !== 'App::PropertyLinkListHidden' || snapshot.group !== 'Sketch' || !isDeepStrictEqual(snapshot.propertyStatus, ['26']) || !isDeepStrictEqual(snapshot.editorMode, ['Hidden'])) fail(`${entry.objectTypeId} ${phase} property semantics changed`)
+ if (!isDeepStrictEqual(snapshot.ownerState, ['Up-to-date']) || snapshot.statusString !== 'Valid' || snapshot.recomputeResult !== true || snapshot.shape?.applicable !== true || snapshot.shape.isNull !== true || snapshot.shape.solids !== 0 || snapshot.shape.faces !== 0 || snapshot.shape.edges !== 0 || snapshot.shape.vertices !== 0 || snapshot.shape.brepSha256 !== null) fail(`${entry.objectTypeId} ${phase} host state changed`)
+ if (!isDeepStrictEqual(snapshot.ownerOutList, []) || !isDeepStrictEqual(snapshot.targetInLists, { ExportA: [], ExportB: [] })) fail(`${entry.objectTypeId} ${phase} leaked a hidden dependency or backlink`)
+ if (!isDeepStrictEqual(snapshot.targetShapes, baselineTargets)) fail(`${entry.objectTypeId} ${phase} changed a target Shape`)
+ if (!isDeepStrictEqual(snapshot.objectSet, expectedObjects(objectName, entry.objectTypeId))) fail(`${entry.objectTypeId} ${phase} changed the document object set`)
+ }
+}
+console.log(JSON.stringify({ status: 'freecad-property-linklisthidden-success-pass', cases: report.caseCount, phases: Object.keys(expectedValues), orderedValue: expectedValues.ordered, duplicateValue: expectedValues.duplicates, hiddenDependency: { ownerOutList: [], targetInLists: { ExportA: [], ExportB: [] } } }, null, 2))
diff --git a/scripts/check-freecad-property-linksubhidden-failure.mjs b/scripts/check-freecad-property-linksubhidden-failure.mjs
new file mode 100644
index 0000000..85f85fe
--- /dev/null
+++ b/scripts/check-freecad-property-linksubhidden-failure.mjs
@@ -0,0 +1,57 @@
+import { isDeepStrictEqual } from 'node:util'
+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-linksubhidden-failure.json'), 'utf8'))
+const fail = (message) => { throw new Error(`FreeCAD PropertyLinkSubHidden failure check failed: ${message}`) }
+if (report.schemaVersion !== 1 || report.status !== 'pass' || report.baselineId !== 'freecad-1.1.1-property-linksubhidden-failure' || report.freecadVersion !== '1.1.1' || report.gitCommit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d' || report.propertyType !== 'App::PropertyLinkSubHidden' || report.caseCount !== 1) fail('baseline is invalid')
+
+const entry = report.case
+const expectedObjects = [
+ { name: 'LinkGroupProbe', typeId: 'App::LinkGroup' },
+ { name: 'ColorSourceA', typeId: 'Part::Feature' },
+ { name: 'ColorSourceB', typeId: 'Part::Feature' },
+]
+if (entry?.objectTypeId !== 'App::LinkGroup' || entry.objectName !== 'LinkGroupProbe' || entry.propertyName !== 'ColoredElements' || entry.propertyTypeId !== 'App::PropertyLinkSubHidden' || !isDeepStrictEqual(entry.baseline, { object: 'ColorSourceA', subElements: ['Face1', 'Face3'] })) fail('native case identity changed')
+
+const initial = entry.initial
+if (!isDeepStrictEqual(initial?.value, entry.baseline) || initial.propertyTypeId !== 'App::PropertyLinkSubHidden' || !isDeepStrictEqual(initial.propertyStatus, ['LockDynamic', '26']) || !isDeepStrictEqual(initial.editorMode, ['Hidden']) || !isDeepStrictEqual(initial.ownerState, ['Up-to-date']) || initial.statusString !== 'Valid' || !isDeepStrictEqual(initial.ownerOutList, []) || !isDeepStrictEqual(initial.targetInLists, { ColorSourceA: [], ColorSourceB: [] }) || !isDeepStrictEqual(initial.objectSet, expectedObjects) || typeof initial.targetShapeHashes?.ColorSourceA !== 'string' || typeof initial.targetShapeHashes?.ColorSourceB !== 'string') fail('initial hidden sub-link state is invalid')
+
+const assertBoundary = (snapshot, label) => {
+ if (snapshot?.propertyTypeId !== 'App::PropertyLinkSubHidden' || !isDeepStrictEqual(snapshot.ownerOutList, []) || !isDeepStrictEqual(snapshot.targetInLists, { ColorSourceA: [], ColorSourceB: [] }) || !isDeepStrictEqual(snapshot.targetShapeHashes, initial.targetShapeHashes) || !isDeepStrictEqual(snapshot.objectSet, expectedObjects)) fail(`${label} polluted hidden dependencies, Shapes, or objects`)
+}
+
+const failureNames = new Set(entry.failures?.map(({ name }) => name))
+if (!['scalar', 'wrongSubList', 'unknownTuple'].every((name) => failureNames.has(name)) || entry.failures.length !== 3) fail('invalid input case inventory changed')
+const scalar = entry.failures.find(({ name }) => name === 'scalar')
+if (scalar?.accepted !== false || scalar.error?.type !== 'TypeError' || !/DocumentObject|NoneType/.test(scalar.error.message) || scalar.valuePreserved !== true || scalar.documentPreserved !== true || !isDeepStrictEqual(scalar.before, initial) || !isDeepStrictEqual(scalar.after, initial)) fail('scalar input was not rejected atomically')
+const wrongSubList = entry.failures.find(({ name }) => name === 'wrongSubList')
+if (wrongSubList?.accepted !== true || wrongSubList.error !== null || wrongSubList.valuePreserved !== false || !isDeepStrictEqual(wrongSubList.after.value, { object: 'ColorSourceA', subElements: ['Face1'] }) || wrongSubList.after.ownerState?.join(',') !== 'Touched') fail('tuple shorthand boundary changed')
+const unknownTuple = entry.failures.find(({ name }) => name === 'unknownTuple')
+if (unknownTuple?.accepted !== true || unknownTuple.error !== null || unknownTuple.valuePreserved !== false || !isDeepStrictEqual(unknownTuple.after.value, { object: 'ColorSourceA', subElements: ['MissingFace'] }) || unknownTuple.after.ownerState?.join(',') !== 'Touched') fail('unknown sub-element reference boundary changed')
+for (const [label, snapshot] of [['scalar before', scalar.before], ['scalar after', scalar.after], ['wrongSubList before', wrongSubList.before], ['wrongSubList after', wrongSubList.after], ['unknownTuple before', unknownTuple.before], ['unknownTuple after', unknownTuple.after]]) assertBoundary(snapshot, label)
+
+if (entry.crossDocument?.accepted !== false || entry.crossDocument.error?.type !== 'ValueError' || entry.crossDocument.error.message !== 'PropertyLinkSub does not support external object' || entry.crossDocument.valuePreserved !== true || entry.crossDocument.documentPreserved !== true || !isDeepStrictEqual(entry.crossDocument.before, initial) || !isDeepStrictEqual(entry.crossDocument.after, initial)) fail('cross-document input boundary changed')
+assertBoundary(entry.crossDocument.before, 'cross-document before')
+assertBoundary(entry.crossDocument.after, 'cross-document after')
+
+const editor = entry.readOnly
+if (editor?.accepted !== true || editor.error !== null || editor.pythonBypassesEditorReadOnly !== true || !isDeepStrictEqual(editor.before.value, entry.baseline) || !isDeepStrictEqual(editor.after.value, { object: 'ColorSourceB', subElements: ['Face2'] }) || !isDeepStrictEqual(editor.after.propertyStatus, ['ReadOnly', 'LockDynamic', '26']) || !isDeepStrictEqual(editor.after.editorMode, ['ReadOnly', 'Hidden']) || !isDeepStrictEqual(editor.after.ownerState, ['Touched'])) fail('ReadOnly Python acceptance boundary changed')
+assertBoundary(editor.before, 'ReadOnly before')
+assertBoundary(editor.after, 'ReadOnly after')
+
+const immutable = entry.immutable
+if (immutable?.accepted !== false || immutable.error?.type !== 'AttributeError' || immutable.error.message !== "Object attribute 'ColoredElements' is read-only" || immutable.pythonBypassesImmutable !== false || !isDeepStrictEqual(immutable.before.value, entry.baseline) || !isDeepStrictEqual(immutable.after.value, entry.baseline) || !isDeepStrictEqual(immutable.before.propertyStatus, ['Immutable', 'LockDynamic', '26']) || !isDeepStrictEqual(immutable.after.propertyStatus, ['Immutable', 'LockDynamic', '26']) || !isDeepStrictEqual(immutable.after.ownerState, ['Up-to-date'])) fail('Immutable Python rejection boundary changed')
+assertBoundary(immutable.before, 'Immutable before')
+assertBoundary(immutable.after, 'Immutable after')
+
+const transaction = entry.transaction
+if (transaction?.undoMode !== 1 || transaction.pendingAfterEdit !== true || transaction.pendingAfterAbort !== false || transaction.activeAfterEdit?.name !== 'property-linksubhidden-cancel' || !(transaction.activeAfterEdit.id > 0) || transaction.activeAfterAbort?.name !== '' || transaction.activeAfterAbort.id !== 0 || !isDeepStrictEqual(transaction.before?.value, entry.baseline) || !isDeepStrictEqual(transaction.edited?.value, { object: 'ColorSourceB', subElements: ['Face5'] }) || !isDeepStrictEqual(transaction.afterAbortBeforeRecompute?.value, entry.baseline) || !isDeepStrictEqual(transaction.afterAbortBeforeRecompute.ownerState, ['Touched']) || transaction.recomputeAfterAbort !== true || !isDeepStrictEqual(transaction.afterAbort?.ownerState, ['Up-to-date']) || transaction.afterAbort.statusString !== 'Valid' || transaction.restored !== true) fail('transaction abort and recompute recovery changed')
+for (const [name, snapshot] of Object.entries({ before: transaction.before, edited: transaction.edited, afterAbortBeforeRecompute: transaction.afterAbortBeforeRecompute, afterAbort: transaction.afterAbort })) assertBoundary(snapshot, `transaction ${name}`)
+
+if (report.disabled?.classification !== 'editor-read-only-python-mutable-until-immutable' || report.disabled.readOnlySetter !== 'accepted' || report.disabled.immutableSetter !== 'rejected') fail('disabled state boundary is incomplete')
+if (report.cancellationBoundary?.supported !== false || report.cancellationBoundary.classification !== 'synchronous-property-setter' || report.cancellationBoundary.reason !== 'no-native-cancel-hook' || report.cancellationBoundary.replacement !== 'abort-active-document-transaction') fail('cancellation boundary is incomplete')
+if (entry.documentIntegrity?.objectsPreserved !== true || !isDeepStrictEqual(entry.documentIntegrity.initialObjects, expectedObjects) || !isDeepStrictEqual(entry.documentIntegrity.finalObjects, expectedObjects) || !isDeepStrictEqual(entry.documentIntegrity.foreignDocumentObjects, [{ name: 'ExternalSource', typeId: 'Part::Feature' }])) fail('failure cases polluted either document')
+
+console.log(JSON.stringify({ status: 'freecad-property-linksubhidden-failure-pass', invalidCases: [...failureNames], crossDocument: entry.crossDocument, readOnlyPythonAccepted: editor.pythonBypassesEditorReadOnly, immutableRejected: !immutable.pythonBypassesImmutable, transactionRestored: transaction.restored, cancellationBoundary: report.cancellationBoundary }, null, 2))
diff --git a/scripts/check-freecad-property-linksubhidden-inventory.mjs b/scripts/check-freecad-property-linksubhidden-inventory.mjs
new file mode 100644
index 0000000..3f44de2
--- /dev/null
+++ b/scripts/check-freecad-property-linksubhidden-inventory.mjs
@@ -0,0 +1,36 @@
+import { createHash } from 'node:crypto'
+import { isDeepStrictEqual } from 'node:util'
+import { readFile } from 'node:fs/promises'
+import { resolve } from 'node:path'
+
+const root = resolve(new URL('..', import.meta.url).pathname)
+const runtimePath = '.cache/freecad/reference-desktop.json'
+const [runtimeContent, reportContent] = await Promise.all([readFile(resolve(root, runtimePath)), readFile(resolve(root, 'config/freecad-property-linksubhidden-inventory.json'))])
+const runtime = JSON.parse(runtimeContent)
+const report = JSON.parse(reportContent)
+const fail = (message) => { throw new Error(`FreeCAD PropertyLinkSubHidden inventory check failed: ${message}`) }
+if (report.schemaVersion !== 1 || report.status !== 'pass' || report.propertyType !== 'App::PropertyLinkSubHidden' || report.classification !== 'opaque-fcstd-proxy' || report.baseline?.freecadVersion !== '1.1.1' || report.baseline?.commit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d') fail('report boundary is invalid')
+if (report.recordCount !== 4 || report.objectTypeCount !== 4 || report.writableRecordCount !== 4 || report.records?.length !== 4) fail('runtime record or host count changed')
+const contract = report.typeContract
+if (contract?.inheritedFrom !== 'App::PropertyLinkSub' || contract.linkScope !== 'Hidden' || contract.propertyStatus !== 'PropHidden' || contract.dynamicStatus !== 'LockDynamic' || contract.xmlElement !== 'LinkSub' || contract.xmlChildElement !== 'Sub' || contract.legacyRestoreType !== 'App::PropertyLinkSub' || contract.emptySubListMeans !== 'whole-object-reference') fail('type contract is incomplete')
+if (report.provenance?.runtime?.path !== runtimePath || report.provenance.runtime.bytes !== runtimeContent.length || report.provenance.runtime.sha256 !== createHash('sha256').update(runtimeContent).digest('hex')) fail('runtime provenance is stale')
+for (const locked of 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(`source provenance is stale for ${locked.path}`) }
+const nativeMatches = []
+for (const objectType of runtime.runtimeObjects?.types ?? []) for (const property of objectType.properties ?? []) if (property.typeId === 'App::PropertyLinkSubHidden') nativeMatches.push({ objectTypeId: objectType.typeId, objectAvailable: objectType.available === true, probeStatus: objectType.probeStatus, propertyName: property.name, group: property.group, statusRaw: property.status, defaultRaw: property.default })
+const identity = ({ objectTypeId, objectAvailable, probeStatus, propertyName, group, statusRaw, defaultRaw }) => ({ objectTypeId, objectAvailable, probeStatus, propertyName, group, statusRaw, defaultRaw })
+const sorted = (records) => records.map(identity).sort((left, right) => `${left.objectTypeId}.${left.propertyName}`.localeCompare(`${right.objectTypeId}.${right.propertyName}`))
+if (!isDeepStrictEqual(sorted(nativeMatches), sorted(report.records))) fail('report diverges from the locked runtime oracle')
+const expectedKeys = ['App::Link.ColoredElements', 'App::LinkGroup.ColoredElements', 'App::LinkGroupPython.ColoredElements', 'App::LinkPython.ColoredElements']
+if (!isDeepStrictEqual(report.records.map(({ objectTypeId, propertyName }) => `${objectTypeId}.${propertyName}`), expectedKeys)) fail('native host/property identities changed')
+for (const record of report.records) {
+ if (record.objectAvailable !== true || record.probeStatus !== 'available' || record.group !== ' Link' || !isDeepStrictEqual(record.statusRaw, ['LockDynamic', 26]) || !isDeepStrictEqual(record.statusNames, ['LockDynamic', 'PropHidden']) || record.defaultRaw !== null) fail(`native metadata changed for ${record.objectTypeId}.ColoredElements`)
+ const model = record.valueModel; const inputs = record.inputs; const dependencies = record.dependencies
+ if (model?.kind !== 'hidden-document-object-sub-element-reference' || model.inheritedFrom !== 'App::PropertyLinkSub' || model.elementType !== 'same-document App::DocumentObject plus ordered sub-element names' || model.orderPreserved !== true || model.duplicatesPreserved !== false || model.writable !== true) fail(`value model changed for ${record.objectTypeId}.ColoredElements`)
+ if (inputs?.nativeCppSetValues !== 'attached same-document DocumentObject plus sub-element string list' || inputs.emptySubListMeansWholeObject !== true || inputs.nullObjectClears !== true || inputs.detachedObjectRejected !== true || inputs.crossDocumentRejectedWithoutLinkAllowExternal !== true || inputs.invalidSubElementAcceptedAsReference !== true) fail(`input preconditions changed for ${record.objectTypeId}.ColoredElements`)
+ if (dependencies?.linkScope !== 'Hidden' || dependencies.createsBacklinks !== false || dependencies.includedByDefaultGetLinks !== false || dependencies.includedByGetLinksAll !== true || dependencies.defaultDagDependency !== false) fail(`hidden dependency scope changed for ${record.objectTypeId}.ColoredElements`)
+ if (record.applicability?.requiredObjectTypeId !== record.objectTypeId || record.applicability.role !== 'Link colored element override lookup' || record.applicability.propertyWriteRequiresShape !== false) fail(`applicability changed for ${record.objectTypeId}.ColoredElements`)
+}
+const sources = Object.fromEntries(await Promise.all(report.provenance.sources.map(async ({ path }) => [path, await readFile(resolve(root, path), 'utf8')])))
+const linksHeader = sources['.cache/freecad/FreeCAD/src/App/PropertyLinks.h']; const linksSource = sources['.cache/freecad/FreeCAD/src/App/PropertyLinks.cpp']; const linkHeader = sources['.cache/freecad/FreeCAD/src/App/Link.h']; const draftSource = sources['.cache/freecad/FreeCAD/src/Mod/Draft/draftobjects/draftlink.py']
+if (!linksHeader.includes('class AppExport PropertyLinkSubHidden: public PropertyLinkSub') || !linksHeader.includes('_pcScope = LinkScope::Hidden;') || !linksSource.includes('TYPESYSTEM_SOURCE(App::PropertyLinkSubHidden, App::PropertyLinkSub)') || !linksSource.includes('if (all || _pcScope != LinkScope::Hidden)') || !linksSource.includes(' objectTypeId), propertyStatus: contract.propertyStatus, dynamicStatus: contract.dynamicStatus, linkScope: contract.linkScope, classification: report.classification }, null, 2))
diff --git a/scripts/check-freecad-property-linksubhidden-success.mjs b/scripts/check-freecad-property-linksubhidden-success.mjs
new file mode 100644
index 0000000..279f8b1
--- /dev/null
+++ b/scripts/check-freecad-property-linksubhidden-success.mjs
@@ -0,0 +1,29 @@
+import { isDeepStrictEqual } from 'node:util'
+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-linksubhidden-success.json'), 'utf8'))
+const fail = (message) => { throw new Error(`FreeCAD PropertyLinkSubHidden success check failed: ${message}`) }
+if (report.schemaVersion !== 1 || report.status !== 'pass' || report.baselineId !== 'freecad-1.1.1-property-linksubhidden-success' || report.freecadVersion !== '1.1.1' || report.gitCommit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d' || report.propertyType !== 'App::PropertyLinkSubHidden') fail('baseline is invalid')
+if (report.caseCount !== 4 || report.cases?.length !== 4 || report.bindingBoundary?.oracleDefault !== null || report.bindingBoundary.safeFirstRead !== 'assign-null-object-before-read' || report.bindingBoundary.emptySubListMeans !== 'whole-object-reference') fail('case inventory or first-read boundary changed')
+const expectedHosts = new Map([['App::Link', { name: 'LinkProbe', ordinaryOutList: ['ColorSourceA'], targetInLists: { ColorSourceA: ['LinkProbe'], ColorSourceB: [] } }], ['App::LinkGroup', { name: 'LinkGroupProbe', ordinaryOutList: [], targetInLists: { ColorSourceA: [], ColorSourceB: [] } }], ['App::LinkGroupPython', { name: 'LinkGroupPythonProbe', ordinaryOutList: [], targetInLists: { ColorSourceA: [], ColorSourceB: [] } }], ['App::LinkPython', { name: 'LinkPythonProbe', ordinaryOutList: ['ColorSourceA'], targetInLists: { ColorSourceA: ['LinkPythonProbe'], ColorSourceB: [] } }]])
+const expectedValues = { ordered: { object: 'ColorSourceA', subElements: ['Face1', 'Face3'] }, duplicates: { object: 'ColorSourceA', subElements: ['Face6', 'Face1', 'Face6'] }, wholeObject: { object: 'ColorSourceA', subElements: [] }, explicitNull: null }
+const expectedObjects = (ownerName, ownerTypeId) => [{ name: ownerName, typeId: ownerTypeId }, { name: 'ColorSourceA', typeId: 'Part::Feature' }, { name: 'ColorSourceB', typeId: 'Part::Feature' }]
+for (const entry of report.cases) {
+ const host = expectedHosts.get(entry.objectTypeId)
+ const objectName = host?.name
+ if (!objectName || entry.objectName !== objectName || entry.propertyName !== 'ColoredElements' || entry.group !== ' Link' || entry.mode !== 'direct-native-property-setter') fail(`${entry.objectTypeId} identity changed`)
+ const baselineTargets = entry.phases?.ordered?.targetShapes
+ if (baselineTargets?.ColorSourceA?.isNull !== false || baselineTargets.ColorSourceA.solids !== 1 || baselineTargets.ColorSourceA.faces !== 6 || baselineTargets.ColorSourceA.edges !== 12 || baselineTargets.ColorSourceA.vertices !== 8 || typeof baselineTargets.ColorSourceA.brepSha256 !== 'string') fail(`${entry.objectTypeId} box target is invalid`)
+ if (baselineTargets?.ColorSourceB?.isNull !== false || baselineTargets.ColorSourceB.solids !== 1 || baselineTargets.ColorSourceB.faces !== 3 || baselineTargets.ColorSourceB.edges !== 3 || baselineTargets.ColorSourceB.vertices !== 2 || typeof baselineTargets.ColorSourceB.brepSha256 !== 'string') fail(`${entry.objectTypeId} cylinder target is invalid`)
+ for (const [phase, expectedValue] of Object.entries(expectedValues)) {
+ const snapshot = entry.phases?.[phase]
+ if (!isDeepStrictEqual(snapshot?.value, expectedValue) || snapshot.propertyTypeId !== 'App::PropertyLinkSubHidden' || snapshot.group !== ' Link' || !isDeepStrictEqual(snapshot.propertyStatus, ['LockDynamic', '26']) || !isDeepStrictEqual(snapshot.editorMode, ['Hidden'])) fail(`${entry.objectTypeId} ${phase} property semantics changed`)
+ if (!isDeepStrictEqual(snapshot.ownerState, ['Up-to-date']) || snapshot.statusString !== 'Valid' || snapshot.recomputeResult !== true || snapshot.shape?.applicable !== false) fail(`${entry.objectTypeId} ${phase} host state changed`)
+ if (!isDeepStrictEqual(snapshot.ownerOutList, host.ordinaryOutList) || !isDeepStrictEqual(snapshot.targetInLists, host.targetInLists)) fail(`${entry.objectTypeId} ${phase} leaked a hidden dependency or backlink`)
+ if (!isDeepStrictEqual(snapshot.targetShapes, baselineTargets)) fail(`${entry.objectTypeId} ${phase} changed a target Shape`)
+ if (!isDeepStrictEqual(snapshot.objectSet, expectedObjects(objectName, entry.objectTypeId))) fail(`${entry.objectTypeId} ${phase} changed the document object set`)
+ }
+}
+console.log(JSON.stringify({ status: 'freecad-property-linksubhidden-success-pass', cases: report.caseCount, phases: Object.keys(expectedValues), orderedValue: expectedValues.ordered, duplicateValue: expectedValues.duplicates, wholeObjectValue: expectedValues.wholeObject, hiddenDependency: { ownerOutList: [], targetInLists: { ColorSourceA: [], ColorSourceB: [] } } }, null, 2))
diff --git a/scripts/freecad-property-linklisthidden-failure.py b/scripts/freecad-property-linklisthidden-failure.py
new file mode 100644
index 0000000..4bd0474
--- /dev/null
+++ b/scripts/freecad-property-linklisthidden-failure.py
@@ -0,0 +1,185 @@
+import hashlib
+import json
+import os
+
+import FreeCAD as App
+import Part
+
+
+FREECAD_COMMIT = "0108fd4b4850cc46e625b60e53cea7a7bbe69f8d"
+OUTPUT_PATH = os.environ.get("FREECAD_PROPERTY_LINKLISTHIDDEN_FAILURE_OUTPUT", "")
+
+
+def shape_hash(obj):
+ shape = obj.Shape
+ if shape.isNull():
+ return None
+ return hashlib.sha256(shape.exportBrepToString().encode("utf-8")).hexdigest()
+
+
+def object_set(document):
+ return [{"name": obj.Name, "typeId": obj.TypeId} for obj in document.Objects]
+
+
+def snapshot(document, owner, targets):
+ return {
+ "value": [candidate.Name for candidate in owner.Exports],
+ "propertyTypeId": owner.getTypeIdOfProperty("Exports"),
+ "propertyStatus": [str(item) for item in owner.getPropertyStatus("Exports")],
+ "editorMode": [str(item) for item in owner.getEditorMode("Exports")],
+ "ownerState": [str(item) for item in owner.State],
+ "statusString": str(owner.getStatusString()),
+ "ownerShapeNull": bool(owner.Shape.isNull()),
+ "ownerOutList": [candidate.Name for candidate in owner.OutList],
+ "targetInLists": {target.Name: [candidate.Name for candidate in target.InList] for target in targets},
+ "targetShapeHashes": {target.Name: shape_hash(target) for target in targets},
+ "objectSet": object_set(document),
+ }
+
+
+def exception_snapshot(callback):
+ try:
+ callback()
+ except Exception as error:
+ return {"type": type(error).__name__, "message": str(error)}
+ return None
+
+
+def active_transaction_snapshot():
+ active = App.getActiveTransaction()
+ return {"name": str(active[0]), "id": int(active[1])} if active else {"name": "", "id": 0}
+
+
+def run():
+ document = App.newDocument("PropertyLinkListHiddenFailure")
+ foreign_document = App.newDocument("PropertyLinkListHiddenForeign")
+ try:
+ owner = document.addObject("Sketcher::SketchObject", "SketchProbe")
+ first = document.addObject("Part::Feature", "ExportA")
+ first.Shape = Part.makeBox(2.0, 3.0, 4.0)
+ second = document.addObject("Part::Feature", "ExportB")
+ second.Shape = Part.makeCylinder(1.5, 5.0)
+ targets = [first, second]
+ foreign = foreign_document.addObject("Part::Feature", "ForeignExport")
+ foreign.Shape = Part.makeBox(1.0, 1.0, 1.0)
+ owner.Exports = [first, second]
+ document.recompute()
+ document.UndoMode = 1
+ initial = snapshot(document, owner, targets)
+ failures = []
+ cases = [
+ ("string-scalar", "ExportA"),
+ ("string-item", ["ExportA"]),
+ ("integer-scalar", 7),
+ ("integer-item", [7]),
+ ("cross-document", [foreign]),
+ ]
+ for case_id, requested in cases:
+ before = snapshot(document, owner, targets)
+ exception = exception_snapshot(lambda value=requested: setattr(owner, "Exports", value))
+ after = snapshot(document, owner, targets)
+ failures.append({
+ "id": case_id,
+ "requested": repr(requested),
+ "exception": exception,
+ "before": before,
+ "after": after,
+ "valuePreserved": before["value"] == after["value"],
+ "documentPreserved": before == after,
+ })
+
+ editor_before = snapshot(document, owner, targets)
+ owner.setEditorMode("Exports", ["ReadOnly"])
+ editor_mode = [str(item) for item in owner.getEditorMode("Exports")]
+ editor_exception = exception_snapshot(lambda: setattr(owner, "Exports", [second]))
+ editor_after = snapshot(document, owner, targets)
+ owner.setEditorMode("Exports", 0)
+ editor_restored = [str(item) for item in owner.getEditorMode("Exports")]
+
+ owner.Exports = [first, second]
+ document.recompute()
+ owner.setPropertyStatus("Exports", "Immutable")
+ immutable_before = snapshot(document, owner, targets)
+ immutable_exception = exception_snapshot(lambda: setattr(owner, "Exports", [second]))
+ immutable_after = snapshot(document, owner, targets)
+ owner.setPropertyStatus("Exports", "-Immutable")
+ immutable_restored_status = [str(item) for item in owner.getPropertyStatus("Exports")]
+
+ owner.Exports = [first, second]
+ document.recompute()
+ document.openTransaction("property-linklisthidden-cancel")
+ transaction_before = snapshot(document, owner, targets)
+ owner.Exports = [second, first, second]
+ transaction_edited = snapshot(document, owner, targets)
+ pending_after_edit = bool(document.HasPendingTransaction)
+ active_after_edit = active_transaction_snapshot()
+ document.abortTransaction()
+ transaction_after_abort_before_recompute = snapshot(document, owner, targets)
+ recompute_after_abort = bool(document.recompute())
+ transaction_after = snapshot(document, owner, targets)
+ return {
+ "schemaVersion": 1,
+ "status": "pass",
+ "baselineId": "freecad-1.1.1-property-linklisthidden-failure",
+ "freecadVersion": ".".join(str(value) for value in App.Version()[:3]),
+ "gitCommit": FREECAD_COMMIT,
+ "propertyType": "App::PropertyLinkListHidden",
+ "object": {"name": owner.Name, "typeId": owner.TypeId},
+ "property": {"name": "Exports", "typeId": owner.getTypeIdOfProperty("Exports")},
+ "initial": initial,
+ "failures": failures,
+ "editorReadOnly": {
+ "classification": "editor-read-only-python-setter-accepted",
+ "before": editor_before,
+ "editorMode": editor_mode,
+ "exception": editor_exception,
+ "after": editor_after,
+ "pythonBypassesEditorReadOnly": editor_exception is None,
+ "restoredEditorMode": editor_restored,
+ },
+ "immutable": {
+ "classification": "immutable-python-setter-accepted",
+ "before": immutable_before,
+ "exception": immutable_exception,
+ "after": immutable_after,
+ "pythonBypassesImmutable": immutable_exception is None,
+ "restoredStatus": immutable_restored_status,
+ },
+ "transaction": {
+ "before": transaction_before,
+ "edited": transaction_edited,
+ "afterAbortBeforeRecompute": transaction_after_abort_before_recompute,
+ "afterAbort": transaction_after,
+ "recomputeAfterAbort": recompute_after_abort,
+ "undoMode": int(document.UndoMode),
+ "pendingAfterEdit": pending_after_edit,
+ "pendingAfterAbort": bool(document.HasPendingTransaction),
+ "activeAfterEdit": active_after_edit,
+ "activeAfterAbort": active_transaction_snapshot(),
+ "restored": transaction_before == transaction_after,
+ },
+ "cancellationBoundary": {
+ "supported": False,
+ "classification": "synchronous-property-setter",
+ "reason": "no-native-cancel-hook",
+ "replacement": "abort-active-document-transaction",
+ },
+ "documentIntegrity": {
+ "initialObjects": initial["objectSet"],
+ "finalObjects": object_set(document),
+ "objectsPreserved": initial["objectSet"] == object_set(document),
+ "foreignDocumentObjects": object_set(foreign_document),
+ },
+ }
+ finally:
+ App.closeDocument(foreign_document.Name)
+ App.closeDocument(document.Name)
+
+
+report = run()
+if not OUTPUT_PATH:
+ raise RuntimeError("FREECAD_PROPERTY_LINKLISTHIDDEN_FAILURE_OUTPUT is required")
+with open(OUTPUT_PATH, "w", encoding="utf-8") as handle:
+ json.dump(report, handle, indent=2, sort_keys=True)
+ handle.write("\n")
+print("FREECAD_PROPERTY_LINKLISTHIDDEN_FAILURE_RESULT=" + json.dumps({"status": report["status"], "failureCount": len(report["failures"])}, sort_keys=True))
diff --git a/scripts/freecad-property-linklisthidden-mutation.py b/scripts/freecad-property-linklisthidden-mutation.py
new file mode 100644
index 0000000..d847760
--- /dev/null
+++ b/scripts/freecad-property-linklisthidden-mutation.py
@@ -0,0 +1,103 @@
+import hashlib
+import json
+import os
+
+import FreeCAD as App
+import Part
+
+
+FREECAD_COMMIT = "0108fd4b4850cc46e625b60e53cea7a7bbe69f8d"
+OUTPUT_PATH = os.environ.get("FREECAD_PROPERTY_LINKLISTHIDDEN_MUTATION_OUTPUT", "")
+
+
+def shape_snapshot(obj):
+ shape = obj.Shape
+ if shape.isNull():
+ return {"isNull": True, "solids": 0, "faces": 0, "edges": 0, "vertices": 0, "brepSha256": None}
+ return {
+ "isNull": False,
+ "solids": len(shape.Solids),
+ "faces": len(shape.Faces),
+ "edges": len(shape.Edges),
+ "vertices": len(shape.Vertexes),
+ "brepSha256": hashlib.sha256(shape.exportBrepToString().encode("utf-8")).hexdigest(),
+ }
+
+
+def snapshot(document, owner, targets):
+ return {
+ "value": [candidate.Name for candidate in owner.Exports],
+ "propertyTypeId": owner.getTypeIdOfProperty("Exports"),
+ "group": owner.getGroupOfProperty("Exports"),
+ "propertyStatus": [str(item) for item in owner.getPropertyStatus("Exports")],
+ "editorMode": [str(item) for item in owner.getEditorMode("Exports")],
+ "ownerState": [str(item) for item in owner.State],
+ "statusString": str(owner.getStatusString()),
+ "ownerShape": shape_snapshot(owner),
+ "ownerOutList": [candidate.Name for candidate in owner.OutList],
+ "targetInLists": {target.Name: [candidate.Name for candidate in target.InList] for target in targets},
+ "targetShapes": {target.Name: shape_snapshot(target) for target in targets},
+ "objectSet": [{"name": candidate.Name, "typeId": candidate.TypeId} for candidate in document.Objects],
+ }
+
+
+def run_case(object_type_id, object_name):
+ document = App.newDocument("PropertyLinkListHiddenMutation" + object_name)
+ try:
+ owner = document.addObject(object_type_id, object_name)
+ first = document.addObject("Part::Feature", "ExportA")
+ first.Shape = Part.makeBox(2.0, 3.0, 4.0)
+ second = document.addObject("Part::Feature", "ExportB")
+ second.Shape = Part.makeCylinder(1.5, 5.0)
+ targets = [first, second]
+ owner.Exports = [first, second]
+ document.recompute()
+ before = snapshot(document, owner, targets)
+
+ owner.Exports = [second, first, second]
+ touched_after_edit = snapshot(document, owner, targets)
+ edit_recompute_result = bool(document.recompute())
+ edited = snapshot(document, owner, targets)
+
+ owner.Exports = [first, second]
+ touched_after_restore = snapshot(document, owner, targets)
+ restore_recompute_result = bool(document.recompute())
+ restored = snapshot(document, owner, targets)
+ return {
+ "objectTypeId": object_type_id,
+ "objectName": object_name,
+ "propertyName": "Exports",
+ "before": before,
+ "touchedAfterEdit": touched_after_edit,
+ "edited": edited,
+ "touchedAfterRestore": touched_after_restore,
+ "restored": restored,
+ "editRecomputeResult": edit_recompute_result,
+ "restoreRecomputeResult": restore_recompute_result,
+ "valueRestored": before["value"] == restored["value"],
+ "semanticStateRestored": before == restored,
+ }
+ finally:
+ App.closeDocument(document.Name)
+
+
+cases = [
+ run_case("Sketcher::SketchObject", "SketchProbe"),
+ run_case("Sketcher::SketchObjectPython", "SketchPythonProbe"),
+]
+report = {
+ "schemaVersion": 1,
+ "status": "pass",
+ "baselineId": "freecad-1.1.1-property-linklisthidden-mutation",
+ "freecadVersion": ".".join(str(value) for value in App.Version()[:3]),
+ "gitCommit": FREECAD_COMMIT,
+ "propertyType": "App::PropertyLinkListHidden",
+ "caseCount": len(cases),
+ "cases": cases,
+}
+if not OUTPUT_PATH:
+ raise RuntimeError("FREECAD_PROPERTY_LINKLISTHIDDEN_MUTATION_OUTPUT is required")
+with open(OUTPUT_PATH, "w", encoding="utf-8") as handle:
+ json.dump(report, handle, indent=2, sort_keys=True)
+ handle.write("\n")
+print("FREECAD_PROPERTY_LINKLISTHIDDEN_MUTATION_RESULT=" + json.dumps({"status": report["status"], "caseCount": report["caseCount"]}, sort_keys=True))
diff --git a/scripts/freecad-property-linklisthidden-roundtrip.py b/scripts/freecad-property-linklisthidden-roundtrip.py
new file mode 100644
index 0000000..c75d114
--- /dev/null
+++ b/scripts/freecad-property-linklisthidden-roundtrip.py
@@ -0,0 +1,108 @@
+import hashlib
+import json
+import os
+
+import FreeCAD as App
+import Part
+
+
+FREECAD_COMMIT = "0108fd4b4850cc46e625b60e53cea7a7bbe69f8d"
+
+
+def shape_snapshot(obj):
+ shape = obj.Shape
+ if shape.isNull():
+ return {"isNull": True, "valid": False, "shapeType": "Null", "solids": 0, "faces": 0, "edges": 0, "vertices": 0, "area": 0.0, "volume": 0.0, "brepSha256": None}
+ return {
+ "isNull": False,
+ "valid": bool(shape.isValid()),
+ "shapeType": shape.ShapeType,
+ "solids": len(shape.Solids),
+ "faces": len(shape.Faces),
+ "edges": len(shape.Edges),
+ "vertices": len(shape.Vertexes),
+ "area": round(float(shape.Area), 9),
+ "volume": round(float(shape.Volume), 9),
+ "brepSha256": hashlib.sha256(shape.exportBrepToString().encode("utf-8")).hexdigest(),
+ }
+
+
+def object_snapshot(document):
+ owner = document.getObject("SketchProbe")
+ targets = [document.getObject("ExportA"), document.getObject("ExportB")]
+ return {
+ "objectSet": [{"name": item.Name, "typeId": item.TypeId} for item in document.Objects],
+ "object": {
+ "name": owner.Name,
+ "typeId": owner.TypeId,
+ "propertyName": "Exports",
+ "propertyTypeId": owner.getTypeIdOfProperty("Exports"),
+ "group": owner.getGroupOfProperty("Exports"),
+ "value": [item.Name for item in owner.Exports],
+ "propertyStatus": [str(item) for item in owner.getPropertyStatus("Exports")],
+ "editorMode": [str(item) for item in owner.getEditorMode("Exports")],
+ "state": [str(item) for item in owner.State],
+ "statusString": str(owner.getStatusString()),
+ "shape": shape_snapshot(owner),
+ "outList": [item.Name for item in owner.OutList],
+ },
+ "targets": {
+ target.Name: {
+ "typeId": target.TypeId,
+ "inList": [item.Name for item in target.InList],
+ "shape": shape_snapshot(target),
+ }
+ for target in targets
+ },
+ }
+
+
+def create_native(path):
+ document = App.newDocument("PropertyLinkListHiddenRoundtrip")
+ try:
+ owner = document.addObject("Sketcher::SketchObject", "SketchProbe")
+ first = document.addObject("Part::Feature", "ExportA")
+ first.Shape = Part.makeBox(2.0, 3.0, 4.0)
+ second = document.addObject("Part::Feature", "ExportB")
+ second.Shape = Part.makeCylinder(1.5, 5.0)
+ owner.Exports = [first, second]
+ document.recompute()
+ document.saveAs(path)
+ finally:
+ App.closeDocument(document.Name)
+ reopened = App.openDocument(path)
+ try:
+ reopened.recompute()
+ return object_snapshot(reopened)
+ finally:
+ App.closeDocument(reopened.Name)
+
+
+def verify_native(path, resaved_path):
+ document = App.openDocument(path)
+ try:
+ document.recompute()
+ reopened = object_snapshot(document)
+ document.saveAs(resaved_path)
+ finally:
+ App.closeDocument(document.Name)
+ resaved_document = App.openDocument(resaved_path)
+ try:
+ resaved_document.recompute()
+ resaved = object_snapshot(resaved_document)
+ finally:
+ App.closeDocument(resaved_document.Name)
+ return {"reopened": reopened, "resaved": resaved}
+
+
+mode = os.environ.get("FREECAD_PROPERTY_LINKLISTHIDDEN_ROUNDTRIP_MODE", "")
+path = os.environ.get("FREECAD_PROPERTY_LINKLISTHIDDEN_ROUNDTRIP_PATH", "")
+resaved_path = os.environ.get("FREECAD_PROPERTY_LINKLISTHIDDEN_ROUNDTRIP_RESAVED_PATH", "")
+if mode == "create":
+ result = create_native(path)
+elif mode == "verify":
+ result = verify_native(path, resaved_path)
+else:
+ raise RuntimeError("FREECAD_PROPERTY_LINKLISTHIDDEN_ROUNDTRIP_MODE must be create or verify")
+
+print("FREECAD_PROPERTY_LINKLISTHIDDEN_ROUNDTRIP_RESULT=" + json.dumps({"schemaVersion": 1, "status": "pass", "baselineId": "freecad-1.1.1-property-linklisthidden-roundtrip", "freecadVersion": ".".join(str(value) for value in App.Version()[:3]), "gitCommit": FREECAD_COMMIT, "mode": mode, "result": result}, sort_keys=True))
diff --git a/scripts/freecad-property-linklisthidden-success.py b/scripts/freecad-property-linklisthidden-success.py
new file mode 100644
index 0000000..16d9cc8
--- /dev/null
+++ b/scripts/freecad-property-linklisthidden-success.py
@@ -0,0 +1,114 @@
+import hashlib
+import json
+import os
+
+import FreeCAD as App
+import Part
+
+
+FREECAD_COMMIT = "0108fd4b4850cc46e625b60e53cea7a7bbe69f8d"
+OUTPUT_PATH = os.environ.get("FREECAD_PROPERTY_LINKLISTHIDDEN_SUCCESS_OUTPUT", "")
+
+
+def shape_snapshot(obj):
+ if "Shape" not in obj.PropertiesList:
+ return {"applicable": False, "reason": "host-has-no-shape-property"}
+ shape = obj.Shape
+ if shape.isNull():
+ return {
+ "applicable": True,
+ "isNull": True,
+ "solids": 0,
+ "faces": 0,
+ "edges": 0,
+ "vertices": 0,
+ "brepSha256": None,
+ }
+ brep = shape.exportBrepToString()
+ return {
+ "applicable": True,
+ "isNull": False,
+ "solids": len(shape.Solids),
+ "faces": len(shape.Faces),
+ "edges": len(shape.Edges),
+ "vertices": len(shape.Vertexes),
+ "brepSha256": hashlib.sha256(brep.encode("utf-8")).hexdigest(),
+ }
+
+
+def snapshot(document, owner, targets):
+ recompute_result = bool(document.recompute())
+ return {
+ "value": [candidate.Name for candidate in owner.Exports],
+ "propertyTypeId": owner.getTypeIdOfProperty("Exports"),
+ "group": owner.getGroupOfProperty("Exports"),
+ "propertyStatus": [str(item) for item in owner.getPropertyStatus("Exports")],
+ "editorMode": [str(item) for item in owner.getEditorMode("Exports")],
+ "ownerState": [str(item) for item in owner.State],
+ "statusString": str(owner.getStatusString()),
+ "recomputeResult": recompute_result,
+ "shape": shape_snapshot(owner),
+ "ownerOutList": [candidate.Name for candidate in owner.OutList],
+ "targetInLists": {target.Name: [candidate.Name for candidate in target.InList] for target in targets},
+ "targetShapes": {target.Name: shape_snapshot(target) for target in targets},
+ "objectSet": [{"name": candidate.Name, "typeId": candidate.TypeId} for candidate in document.Objects],
+ }
+
+
+def run_case(object_type_id, object_name):
+ document = App.newDocument("PropertyLinkListHidden" + object_name)
+ try:
+ owner = document.addObject(object_type_id, object_name)
+ first = document.addObject("Part::Feature", "ExportA")
+ first.Shape = Part.makeBox(2.0, 3.0, 4.0)
+ second = document.addObject("Part::Feature", "ExportB")
+ second.Shape = Part.makeCylinder(1.5, 5.0)
+ targets = [first, second]
+
+ owner.Exports = []
+ phases = {"explicitEmpty": snapshot(document, owner, targets)}
+ assignments = {
+ "single": [first],
+ "ordered": [second, first],
+ "duplicates": [first, second, first],
+ "restored": [],
+ }
+ for phase, value in assignments.items():
+ owner.Exports = value
+ phases[phase] = snapshot(document, owner, targets)
+ return {
+ "objectTypeId": object_type_id,
+ "objectName": object_name,
+ "propertyName": "Exports",
+ "group": "Sketch",
+ "mode": "direct-native-property-setter",
+ "phases": phases,
+ }
+ finally:
+ App.closeDocument(document.Name)
+
+
+cases = [
+ run_case("Sketcher::SketchObject", "SketchProbe"),
+ run_case("Sketcher::SketchObjectPython", "SketchPythonProbe"),
+]
+report = {
+ "schemaVersion": 1,
+ "status": "pass",
+ "baselineId": "freecad-1.1.1-property-linklisthidden-success",
+ "freecadVersion": ".".join(str(value) for value in App.Version()[:3]),
+ "gitCommit": FREECAD_COMMIT,
+ "propertyType": "App::PropertyLinkListHidden",
+ "caseCount": len(cases),
+ "bindingBoundary": {
+ "oracleDefault": [],
+ "safeFirstRead": "assign-empty-list-before-read",
+ },
+ "cases": cases,
+}
+if not OUTPUT_PATH:
+ raise RuntimeError("FREECAD_PROPERTY_LINKLISTHIDDEN_SUCCESS_OUTPUT is required")
+with open(OUTPUT_PATH, "w", encoding="utf-8") as handle:
+ json.dump(report, handle, indent=2, sort_keys=True)
+ handle.write("\n")
+print("FREECAD_PROPERTY_LINKLISTHIDDEN_SUCCESS_RESULT=" + json.dumps({"status": report["status"], "caseCount": report["caseCount"]}, sort_keys=True))
diff --git a/scripts/freecad-property-linksubhidden-failure.py b/scripts/freecad-property-linksubhidden-failure.py
new file mode 100644
index 0000000..9d33455
--- /dev/null
+++ b/scripts/freecad-property-linksubhidden-failure.py
@@ -0,0 +1,219 @@
+import hashlib
+import json
+import os
+
+import FreeCAD as App
+import Part
+
+
+FREECAD_COMMIT = "0108fd4b4850cc46e625b60e53cea7a7bbe69f8d"
+OUTPUT_PATH = os.environ.get("FREECAD_PROPERTY_LINKSUBHIDDEN_FAILURE_OUTPUT", "")
+
+
+def value_of(owner):
+ value = owner.ColoredElements
+ if value is None:
+ return None
+ return {"object": value[0].Name if value[0] else None, "subElements": list(value[1])}
+
+
+def shape_hash(obj):
+ shape = obj.Shape
+ if shape.isNull():
+ return None
+ return hashlib.sha256(shape.exportBrepToString().encode("utf-8")).hexdigest()
+
+
+def object_set(document):
+ return [{"name": obj.Name, "typeId": obj.TypeId} for obj in document.Objects]
+
+
+def snapshot(document, owner, targets):
+ return {
+ "value": value_of(owner),
+ "propertyTypeId": owner.getTypeIdOfProperty("ColoredElements"),
+ "propertyStatus": [str(item) for item in owner.getPropertyStatus("ColoredElements")],
+ "editorMode": [str(item) for item in owner.getEditorMode("ColoredElements")],
+ "ownerState": [str(item) for item in owner.State],
+ "statusString": str(owner.getStatusString()),
+ "ownerOutList": [candidate.Name for candidate in owner.OutList],
+ "targetInLists": {target.Name: [candidate.Name for candidate in target.InList] for target in targets},
+ "targetShapeHashes": {target.Name: shape_hash(target) for target in targets},
+ "objectSet": object_set(document),
+ }
+
+
+def exception_snapshot(callback):
+ try:
+ callback()
+ except Exception as error:
+ return {"type": type(error).__name__, "message": str(error)}
+ return None
+
+
+def active_transaction_snapshot():
+ active = App.getActiveTransaction()
+ return {"name": str(active[0]), "id": int(active[1])} if active else {"name": "", "id": 0}
+
+
+def run_case():
+ document = App.newDocument("PropertyLinkSubHiddenFailure")
+ external = App.newDocument("PropertyLinkSubHiddenExternal")
+ try:
+ owner = document.addObject("App::LinkGroup", "LinkGroupProbe")
+ first = document.addObject("Part::Feature", "ColorSourceA")
+ first.Shape = Part.makeBox(2.0, 3.0, 4.0)
+ second = document.addObject("Part::Feature", "ColorSourceB")
+ second.Shape = Part.makeCylinder(1.5, 5.0)
+ other = external.addObject("Part::Feature", "ExternalSource")
+ other.Shape = Part.makeBox(1.0, 1.0, 1.0)
+ targets = [first, second]
+
+ owner.ColoredElements = (first, ["Face1", "Face3"])
+ document.recompute()
+ document.UndoMode = 1
+ initial = snapshot(document, owner, targets)
+
+ failures = []
+ for name, candidate in [("scalar", "ColorSourceA"), ("wrongSubList", (first, "Face1")), ("unknownTuple", (first, ["MissingFace"]))]:
+ before = snapshot(document, owner, targets)
+ exception = exception_snapshot(lambda value=candidate: setattr(owner, "ColoredElements", value))
+ after = snapshot(document, owner, targets)
+ failures.append({
+ "name": name,
+ "requested": repr(candidate),
+ "accepted": exception is None,
+ "error": exception,
+ "before": before,
+ "after": after,
+ "valuePreserved": before["value"] == after["value"],
+ "documentPreserved": before == after,
+ })
+ owner.ColoredElements = (first, ["Face1", "Face3"])
+ document.recompute()
+
+ owner.ColoredElements = (first, ["Face1", "Face3"])
+ document.recompute()
+ before = snapshot(document, owner, targets)
+ exception = exception_snapshot(lambda: setattr(owner, "ColoredElements", (other, ["Face1"])))
+ after = snapshot(document, owner, targets)
+ cross_document = {
+ "accepted": exception is None,
+ "error": exception,
+ "before": before,
+ "after": after,
+ "valuePreserved": before["value"] == after["value"],
+ "documentPreserved": before == after,
+ }
+
+ owner.ColoredElements = (first, ["Face1", "Face3"])
+ document.recompute()
+ owner.setPropertyStatus("ColoredElements", "ReadOnly")
+ read_only_before = snapshot(document, owner, targets)
+ read_only_exception = exception_snapshot(lambda: setattr(owner, "ColoredElements", (second, ["Face2"])))
+ read_only_after = snapshot(document, owner, targets)
+ read_only = {
+ "accepted": read_only_exception is None,
+ "error": read_only_exception,
+ "before": read_only_before,
+ "after": read_only_after,
+ "status": [str(item) for item in owner.getPropertyStatus("ColoredElements")],
+ "pythonBypassesEditorReadOnly": read_only_exception is None,
+ }
+ owner.setPropertyStatus("ColoredElements", "-ReadOnly")
+
+ owner.ColoredElements = (first, ["Face1", "Face3"])
+ document.recompute()
+ owner.setPropertyStatus("ColoredElements", "Immutable")
+ immutable_before = snapshot(document, owner, targets)
+ immutable_exception = exception_snapshot(lambda: setattr(owner, "ColoredElements", (first, ["Face4"])))
+ immutable_after = snapshot(document, owner, targets)
+ immutable = {
+ "accepted": immutable_exception is None,
+ "error": immutable_exception,
+ "before": immutable_before,
+ "after": immutable_after,
+ "status": [str(item) for item in owner.getPropertyStatus("ColoredElements")],
+ "pythonBypassesImmutable": immutable_exception is None,
+ }
+ owner.setPropertyStatus("ColoredElements", "-Immutable")
+
+ owner.ColoredElements = (first, ["Face1", "Face3"])
+ document.recompute()
+ document.openTransaction("property-linksubhidden-cancel")
+ transaction_before = snapshot(document, owner, targets)
+ owner.ColoredElements = (second, ["Face5"])
+ transaction_edited = snapshot(document, owner, targets)
+ pending_after_edit = bool(document.HasPendingTransaction)
+ active_after_edit = active_transaction_snapshot()
+ document.abortTransaction()
+ transaction_after_abort_before_recompute = snapshot(document, owner, targets)
+ recompute_after_abort = bool(document.recompute())
+ transaction_after = snapshot(document, owner, targets)
+
+ return {
+ "objectTypeId": "App::LinkGroup",
+ "objectName": "LinkGroupProbe",
+ "propertyName": "ColoredElements",
+ "propertyTypeId": owner.getTypeIdOfProperty("ColoredElements"),
+ "baseline": initial["value"],
+ "initial": initial,
+ "failures": failures,
+ "crossDocument": cross_document,
+ "readOnly": read_only,
+ "immutable": immutable,
+ "transaction": {
+ "before": transaction_before,
+ "edited": transaction_edited,
+ "afterAbortBeforeRecompute": transaction_after_abort_before_recompute,
+ "afterAbort": transaction_after,
+ "recomputeAfterAbort": recompute_after_abort,
+ "undoMode": int(document.UndoMode),
+ "pendingAfterEdit": pending_after_edit,
+ "pendingAfterAbort": bool(document.HasPendingTransaction),
+ "activeAfterEdit": active_after_edit,
+ "activeAfterAbort": active_transaction_snapshot(),
+ "restored": transaction_before == transaction_after,
+ },
+ "documentIntegrity": {
+ "initialObjects": initial["objectSet"],
+ "finalObjects": object_set(document),
+ "objectsPreserved": initial["objectSet"] == object_set(document),
+ "foreignDocumentObjects": object_set(external),
+ },
+ }
+ finally:
+ App.closeDocument(external.Name)
+ App.closeDocument(document.Name)
+
+
+case = run_case()
+report = {
+ "schemaVersion": 1,
+ "status": "pass",
+ "baselineId": "freecad-1.1.1-property-linksubhidden-failure",
+ "freecadVersion": ".".join(str(value) for value in App.Version()[:3]),
+ "gitCommit": FREECAD_COMMIT,
+ "propertyType": "App::PropertyLinkSubHidden",
+ "caseCount": 1,
+ "case": case,
+ "disabled": {
+ "classification": "editor-read-only-python-mutable-until-immutable",
+ "readOnlySetter": "accepted",
+ "immutableSetter": "rejected",
+ "readOnly": case["readOnly"],
+ "immutable": case["immutable"],
+ },
+ "cancellationBoundary": {
+ "supported": False,
+ "classification": "synchronous-property-setter",
+ "reason": "no-native-cancel-hook",
+ "replacement": "abort-active-document-transaction",
+ },
+}
+if not OUTPUT_PATH:
+ raise RuntimeError("FREECAD_PROPERTY_LINKSUBHIDDEN_FAILURE_OUTPUT is required")
+with open(OUTPUT_PATH, "w", encoding="utf-8") as handle:
+ json.dump(report, handle, indent=2, sort_keys=True)
+ handle.write("\n")
+print("FREECAD_PROPERTY_LINKSUBHIDDEN_FAILURE_RESULT=" + json.dumps({"status": report["status"], "caseCount": report["caseCount"]}, sort_keys=True))
diff --git a/scripts/freecad-property-linksubhidden-success.py b/scripts/freecad-property-linksubhidden-success.py
new file mode 100644
index 0000000..e480c7a
--- /dev/null
+++ b/scripts/freecad-property-linksubhidden-success.py
@@ -0,0 +1,85 @@
+import hashlib
+import json
+import os
+
+import FreeCAD as App
+import Part
+
+FREECAD_COMMIT = "0108fd4b4850cc46e625b60e53cea7a7bbe69f8d"
+OUTPUT_PATH = os.environ.get("FREECAD_PROPERTY_LINKSUBHIDDEN_SUCCESS_OUTPUT", "")
+
+
+def shape_snapshot(obj):
+ if "Shape" not in obj.PropertiesList:
+ return {"applicable": False, "reason": "host-has-no-shape-property"}
+ shape = obj.Shape
+ if shape.isNull():
+ return {"applicable": True, "isNull": True, "solids": 0, "faces": 0, "edges": 0, "vertices": 0, "brepSha256": None}
+ return {"applicable": True, "isNull": False, "solids": len(shape.Solids), "faces": len(shape.Faces), "edges": len(shape.Edges), "vertices": len(shape.Vertexes), "brepSha256": hashlib.sha256(shape.exportBrepToString().encode("utf-8")).hexdigest()}
+
+
+def colored_value(owner):
+ value = owner.ColoredElements
+ if value is None:
+ return None
+ target, subs = value
+ return {"object": target.Name if target is not None else None, "subElements": list(subs)}
+
+
+def snapshot(document, owner, targets):
+ recompute_result = bool(document.recompute())
+ return {
+ "value": colored_value(owner),
+ "propertyTypeId": owner.getTypeIdOfProperty("ColoredElements"),
+ "group": owner.getGroupOfProperty("ColoredElements"),
+ "propertyStatus": [str(item) for item in owner.getPropertyStatus("ColoredElements")],
+ "editorMode": [str(item) for item in owner.getEditorMode("ColoredElements")],
+ "ownerState": [str(item) for item in owner.State],
+ "statusString": str(owner.getStatusString()),
+ "recomputeResult": recompute_result,
+ "shape": shape_snapshot(owner),
+ "ownerOutList": [candidate.Name for candidate in owner.OutList],
+ "targetInLists": {target.Name: [candidate.Name for candidate in target.InList] for target in targets},
+ "targetShapes": {target.Name: shape_snapshot(target) for target in targets},
+ "objectSet": [{"name": candidate.Name, "typeId": candidate.TypeId} for candidate in document.Objects],
+ }
+
+
+def run_case(object_type_id, object_name):
+ document = App.newDocument("PropertyLinkSubHidden" + object_name)
+ try:
+ owner = document.addObject(object_type_id, object_name)
+ first = document.addObject("Part::Feature", "ColorSourceA")
+ first.Shape = Part.makeBox(2.0, 3.0, 4.0)
+ second = document.addObject("Part::Feature", "ColorSourceB")
+ second.Shape = Part.makeCylinder(1.5, 5.0)
+ targets = [first, second]
+ if "LinkedObject" in owner.PropertiesList:
+ owner.LinkedObject = first
+ phases = {}
+ owner.ColoredElements = (first, ["Face1", "Face3"])
+ phases["ordered"] = snapshot(document, owner, targets)
+ owner.ColoredElements = (first, ["Face6", "Face1", "Face6"])
+ phases["duplicates"] = snapshot(document, owner, targets)
+ owner.ColoredElements = (first, [])
+ phases["wholeObject"] = snapshot(document, owner, targets)
+ owner.ColoredElements = None
+ phases["explicitNull"] = snapshot(document, owner, targets)
+ return {"objectTypeId": object_type_id, "objectName": object_name, "propertyName": "ColoredElements", "group": " Link", "mode": "direct-native-property-setter", "phases": phases}
+ finally:
+ App.closeDocument(document.Name)
+
+
+cases = [
+ run_case("App::Link", "LinkProbe"),
+ run_case("App::LinkGroup", "LinkGroupProbe"),
+ run_case("App::LinkGroupPython", "LinkGroupPythonProbe"),
+ run_case("App::LinkPython", "LinkPythonProbe"),
+]
+report = {"schemaVersion": 1, "status": "pass", "baselineId": "freecad-1.1.1-property-linksubhidden-success", "freecadVersion": ".".join(str(value) for value in App.Version()[:3]), "gitCommit": FREECAD_COMMIT, "propertyType": "App::PropertyLinkSubHidden", "caseCount": len(cases), "bindingBoundary": {"oracleDefault": None, "safeFirstRead": "assign-null-object-before-read", "emptySubListMeans": "whole-object-reference"}, "cases": cases}
+if not OUTPUT_PATH:
+ raise RuntimeError("FREECAD_PROPERTY_LINKSUBHIDDEN_SUCCESS_OUTPUT is required")
+with open(OUTPUT_PATH, "w", encoding="utf-8") as handle:
+ json.dump(report, handle, indent=2, sort_keys=True)
+ handle.write("\n")
+print("FREECAD_PROPERTY_LINKSUBHIDDEN_SUCCESS_RESULT=" + json.dumps({"status": report["status"], "caseCount": report["caseCount"]}, sort_keys=True))
diff --git a/scripts/generate-freecad-property-acceleration-promotion.mjs b/scripts/generate-freecad-property-acceleration-promotion.mjs
index d435b86..f93625f 100644
--- a/scripts/generate-freecad-property-acceleration-promotion.mjs
+++ b/scripts/generate-freecad-property-acceleration-promotion.mjs
@@ -24,7 +24,7 @@ const propertyType = semantics.types?.find(({ typeId }) => typeId === 'App::Prop
if (propertyType?.support !== 'native-editable-codec' || propertyType.recordCount !== 1) fail('global property semantics did not promote App::PropertyAcceleration')
const opaque = semantics.supportSummary?.['opaque-fcstd-proxy']
const editable = semantics.supportSummary?.['native-editable-codec']
-if (opaque?.typeCount !== 46 || opaque.recordCount !== 457 || editable?.typeCount !== 34 || editable.recordCount !== 4370) fail('global property support summary is not synchronized')
+if (opaque?.typeCount !== 45 || opaque.recordCount !== 455 || editable?.typeCount !== 35 || editable.recordCount !== 4372) fail('global property support summary is not synchronized')
if (roundTrip.classification?.zeroUnknownDrift !== true || chrome.persistence?.loadedType !== 'App::PropertyAcceleration' || chrome.persistence.loadedValue !== 500 || chrome.release?.workerTerminated !== true) fail('round-trip or browser closure evidence is incomplete')
const report = {
diff --git a/scripts/generate-freecad-property-area-promotion.mjs b/scripts/generate-freecad-property-area-promotion.mjs
index e38d656..21891af 100644
--- a/scripts/generate-freecad-property-area-promotion.mjs
+++ b/scripts/generate-freecad-property-area-promotion.mjs
@@ -24,7 +24,7 @@ const propertyType = semantics.types?.find(({ typeId }) => typeId === 'App::Prop
if (propertyType?.support !== 'native-editable-codec' || propertyType.recordCount !== 1 || propertyType.statusNames?.join(',') !== 'PropOutput,PropReadOnly') fail('global property semantics did not promote App::PropertyArea with its native status')
const opaque = semantics.supportSummary?.['opaque-fcstd-proxy']
const editable = semantics.supportSummary?.['native-editable-codec']
-if (opaque?.typeCount !== 46 || opaque.recordCount !== 457 || editable?.typeCount !== 34 || editable.recordCount !== 4370) fail('global property support summary is not synchronized')
+if (opaque?.typeCount !== 45 || opaque.recordCount !== 455 || editable?.typeCount !== 35 || editable.recordCount !== 4372) fail('global property support summary is not synchronized')
if (roundTrip.classification?.zeroUnknownDrift !== true || chrome.ui?.areaReadOnly !== true || chrome.persistence?.loadedAreaType !== 'App::PropertyArea' || chrome.persistence.loadedElementsType !== 'App::PropertyLinkSubList' || chrome.release?.workerTerminated !== true) fail('round-trip or browser closure evidence is incomplete')
const report = {
diff --git a/scripts/generate-freecad-property-boollist-promotion.mjs b/scripts/generate-freecad-property-boollist-promotion.mjs
index a9732c4..20323c0 100644
--- a/scripts/generate-freecad-property-boollist-promotion.mjs
+++ b/scripts/generate-freecad-property-boollist-promotion.mjs
@@ -24,7 +24,7 @@ const propertyType = semantics.types?.find(({ typeId }) => typeId === 'App::Prop
if (propertyType?.support !== 'native-editable-codec' || propertyType.recordCount !== 7 || propertyType.objectTypeCount !== 7 || propertyType.statusNames?.join(',') !== 'Hidden,Immutable,LockDynamic') fail('global property semantics did not promote App::PropertyBoolList with its native status')
const opaque = semantics.supportSummary?.['opaque-fcstd-proxy']
const editable = semantics.supportSummary?.['native-editable-codec']
-if (opaque?.typeCount !== 46 || opaque.recordCount !== 457 || editable?.typeCount !== 34 || editable.recordCount !== 4370) fail('global property support summary is not synchronized')
+if (opaque?.typeCount !== 45 || opaque.recordCount !== 455 || editable?.typeCount !== 35 || editable.recordCount !== 4372) fail('global property support summary is not synchronized')
if (roundTrip.classification?.zeroUnknownDrift !== true || chrome.persistence?.loadedType !== 'App::PropertyBoolList' || chrome.persistence.fcstdElement !== 'BoolList' || chrome.release?.workerTerminated !== true) fail('round-trip or browser closure evidence is incomplete')
const report = {
diff --git a/scripts/generate-freecad-property-color-promotion.mjs b/scripts/generate-freecad-property-color-promotion.mjs
index c80fe0e..7caa85b 100644
--- a/scripts/generate-freecad-property-color-promotion.mjs
+++ b/scripts/generate-freecad-property-color-promotion.mjs
@@ -24,7 +24,7 @@ const propertyType = semantics.types?.find(({ typeId }) => typeId === 'App::Prop
if (propertyType?.support !== 'native-editable-codec' || propertyType.recordCount !== 8 || propertyType.objectTypeCount !== 8 || propertyType.statusNames?.length !== 0) fail('global property semantics did not promote App::PropertyColor')
const opaque = semantics.supportSummary?.['opaque-fcstd-proxy']
const editable = semantics.supportSummary?.['native-editable-codec']
-if (opaque?.typeCount !== 46 || opaque.recordCount !== 457 || editable?.typeCount !== 34 || editable.recordCount !== 4370) fail('global property support summary is not synchronized')
+if (opaque?.typeCount !== 45 || opaque.recordCount !== 455 || editable?.typeCount !== 35 || editable.recordCount !== 4372) fail('global property support summary is not synchronized')
if (roundTrip.classification?.zeroUnknownDrift !== true || chrome.persistence?.loadedType !== 'App::PropertyColor' || chrome.persistence.fcstdElement !== 'PropertyColor' || chrome.persistence.fcstdValue !== '862362060' || chrome.release?.workerTerminated !== true) fail('round-trip or browser closure evidence is incomplete')
const report = {
diff --git a/scripts/generate-freecad-property-colorlist-promotion.mjs b/scripts/generate-freecad-property-colorlist-promotion.mjs
index fddac0d..549ff76 100644
--- a/scripts/generate-freecad-property-colorlist-promotion.mjs
+++ b/scripts/generate-freecad-property-colorlist-promotion.mjs
@@ -24,7 +24,7 @@ const propertyType = semantics.types?.find(({ typeId }) => typeId === 'App::Prop
if (propertyType?.support !== 'native-editable-codec' || propertyType.recordCount !== 2 || propertyType.objectTypeCount !== 2 || propertyType.statusNames?.length !== 0) fail('global property semantics did not promote App::PropertyColorList')
const opaque = semantics.supportSummary?.['opaque-fcstd-proxy']
const editable = semantics.supportSummary?.['native-editable-codec']
-if (opaque?.typeCount !== 46 || opaque.recordCount !== 457 || editable?.typeCount !== 34 || editable.recordCount !== 4370) fail('global property support summary is not synchronized')
+if (opaque?.typeCount !== 45 || opaque.recordCount !== 455 || editable?.typeCount !== 35 || editable.recordCount !== 4372) fail('global property support summary is not synchronized')
if (roundTrip.classification?.zeroUnknownDrift !== true || chrome.persistence?.loadedType !== 'App::PropertyColorList' || chrome.persistence.fcstdElement !== 'ColorList' || chrome.persistence.fcstdResourcePath !== 'ColourList' || chrome.persistence.fcstdResourceBytes !== 12 || chrome.release?.workerTerminated !== true) fail('round-trip or browser closure evidence is incomplete')
const report = {
diff --git a/scripts/generate-freecad-property-direction-promotion.mjs b/scripts/generate-freecad-property-direction-promotion.mjs
index badd62a..0f5b735 100644
--- a/scripts/generate-freecad-property-direction-promotion.mjs
+++ b/scripts/generate-freecad-property-direction-promotion.mjs
@@ -24,7 +24,7 @@ const propertyType = semantics.types?.find(({ typeId }) => typeId === 'App::Prop
if (propertyType?.support !== 'native-editable-codec' || propertyType.recordCount !== 2 || propertyType.objectTypeCount !== 2 || propertyType.statusNames?.length !== 0) fail('global property semantics did not promote App::PropertyDirection')
const opaque = semantics.supportSummary?.['opaque-fcstd-proxy']
const editable = semantics.supportSummary?.['native-editable-codec']
-if (opaque?.typeCount !== 46 || opaque.recordCount !== 457 || editable?.typeCount !== 34 || editable.recordCount !== 4370) fail('global property support summary is not synchronized')
+if (opaque?.typeCount !== 45 || opaque.recordCount !== 455 || editable?.typeCount !== 35 || editable.recordCount !== 4372) fail('global property support summary is not synchronized')
if (roundTrip.classification?.zeroUnknownDrift !== true || chrome.persistence?.loadedType !== 'App::PropertyDirection' || chrome.persistence.fcstdElement !== 'PropertyVector' || chrome.persistence.fcstdValue !== JSON.stringify({ x: 0.25, y: -0.5, z: 1.5 }) || chrome.release?.workerTerminated !== true) fail('round-trip or browser closure evidence is incomplete')
const report = {
diff --git a/scripts/generate-freecad-property-file-promotion.mjs b/scripts/generate-freecad-property-file-promotion.mjs
index 20d5b99..0b6257b 100644
--- a/scripts/generate-freecad-property-file-promotion.mjs
+++ b/scripts/generate-freecad-property-file-promotion.mjs
@@ -24,7 +24,7 @@ const propertyType = semantics.types?.find(({ typeId }) => typeId === 'App::Prop
if (propertyType?.support !== 'native-editable-codec' || propertyType.recordCount !== 16 || propertyType.objectTypeCount !== 12 || propertyType.statusNames?.length !== 0) fail('global property semantics did not promote App::PropertyFile')
const opaque = semantics.supportSummary?.['opaque-fcstd-proxy']
const editable = semantics.supportSummary?.['native-editable-codec']
-if (opaque?.typeCount !== 46 || opaque.recordCount !== 457 || editable?.typeCount !== 34 || editable.recordCount !== 4370) fail('global property support summary is not synchronized')
+if (opaque?.typeCount !== 45 || opaque.recordCount !== 455 || editable?.typeCount !== 35 || editable.recordCount !== 4372) fail('global property support summary is not synchronized')
if (roundTrip.classification?.zeroUnknownDrift !== true || roundTrip.classification.externalResourceBoundary !== 'project-relative-path; asset bytes remain external to FCStd' || chrome.persistence?.loadedType !== 'App::PropertyFile' || chrome.persistence.fcstdElement !== 'String' || chrome.persistence.fcstdValue !== 'project-files/chrome-updated.obj' || chrome.resource?.released !== true || chrome.release?.workerTerminated !== true) fail('round-trip or browser closure evidence is incomplete')
const report = {
diff --git a/scripts/generate-freecad-property-fileincluded-promotion.mjs b/scripts/generate-freecad-property-fileincluded-promotion.mjs
index eacf6df..117963a 100644
--- a/scripts/generate-freecad-property-fileincluded-promotion.mjs
+++ b/scripts/generate-freecad-property-fileincluded-promotion.mjs
@@ -24,7 +24,7 @@ const propertyType = semantics.types?.find(({ typeId }) => typeId === 'App::Prop
if (propertyType?.support !== 'native-editable-codec' || propertyType.recordCount !== 20 || propertyType.objectTypeCount !== 15 || propertyType.statusNames?.join(',') !== 'PropOutput,ReadOnly') fail('global property semantics did not promote App::PropertyFileIncluded')
const opaque = semantics.supportSummary?.['opaque-fcstd-proxy']
const editable = semantics.supportSummary?.['native-editable-codec']
-if (opaque?.typeCount !== 46 || opaque.recordCount !== 457 || editable?.typeCount !== 34 || editable.recordCount !== 4370) fail('global property support summary is not synchronized')
+if (opaque?.typeCount !== 45 || opaque.recordCount !== 455 || editable?.typeCount !== 35 || editable.recordCount !== 4372) fail('global property support summary is not synchronized')
if (roundTrip.classification?.zeroUnknownDrift !== true || roundTrip.classification.archiveName !== 'payload.bin' || roundTrip.classification.resavedResourceMatches !== true || chrome.persistence?.loadedType !== 'App::PropertyFileIncluded' || chrome.persistence.fcstdElement !== 'FileIncluded' || chrome.persistence.fcstdResourceMatches !== true || chrome.resource?.documentRoundTrip !== true || chrome.resource.independentReleased !== true || chrome.release?.workerTerminated !== true) fail('round-trip or browser closure evidence is incomplete')
const report = {
diff --git a/scripts/generate-freecad-property-font-promotion.mjs b/scripts/generate-freecad-property-font-promotion.mjs
index b3e07c0..38ef06e 100644
--- a/scripts/generate-freecad-property-font-promotion.mjs
+++ b/scripts/generate-freecad-property-font-promotion.mjs
@@ -24,7 +24,7 @@ const propertyType = semantics.types?.find(({ typeId }) => typeId === 'App::Prop
if (propertyType?.support !== 'native-editable-codec' || propertyType.recordCount !== 2 || propertyType.objectTypeCount !== 2 || propertyType.statusNames?.length !== 0) fail('global property semantics did not promote App::PropertyFont')
const opaque = semantics.supportSummary?.['opaque-fcstd-proxy']
const editable = semantics.supportSummary?.['native-editable-codec']
-if (opaque?.typeCount !== 46 || opaque.recordCount !== 457 || editable?.typeCount !== 34 || editable.recordCount !== 4370) fail('global property support summary is not synchronized')
+if (opaque?.typeCount !== 45 || opaque.recordCount !== 455 || editable?.typeCount !== 35 || editable.recordCount !== 4372) fail('global property support summary is not synchronized')
if (roundTrip.classification?.zeroUnknownDrift !== true || roundTrip.classification.fontResourceBoundary !== 'font family name persists in FCStd; font files and availability remain presentation-only' || chrome.persistence?.loadedType !== 'App::PropertyFont' || chrome.persistence.fcstdElement !== 'String' || chrome.persistence.fcstdValue !== 'Noto Sans CJK SC' || chrome.resource?.released !== true || chrome.release?.workerTerminated !== true) fail('round-trip or browser closure evidence is incomplete')
const report = {
diff --git a/scripts/generate-freecad-property-force-promotion.mjs b/scripts/generate-freecad-property-force-promotion.mjs
index 01ed3d0..35df541 100644
--- a/scripts/generate-freecad-property-force-promotion.mjs
+++ b/scripts/generate-freecad-property-force-promotion.mjs
@@ -24,7 +24,7 @@ const propertyType = semantics.types?.find(({ typeId }) => typeId === 'App::Prop
if (propertyType?.support !== 'native-editable-codec' || propertyType.recordCount !== 4 || propertyType.objectTypeCount !== 2 || propertyType.statusNames?.join(',') !== 'PropOutput') fail('global property semantics did not promote App::PropertyForce')
const opaque = semantics.supportSummary?.['opaque-fcstd-proxy']
const editable = semantics.supportSummary?.['native-editable-codec']
-if (opaque?.typeCount !== 46 || opaque.recordCount !== 457 || editable?.typeCount !== 34 || editable.recordCount !== 4370) fail('global property support summary is not synchronized')
+if (opaque?.typeCount !== 45 || opaque.recordCount !== 455 || editable?.typeCount !== 35 || editable.recordCount !== 4372) fail('global property support summary is not synchronized')
if (roundTrip.classification?.zeroUnknownDrift !== true || roundTrip.classification.outputPropertiesPreserved !== true || chrome.persistence?.loadedType !== 'App::PropertyForce' || chrome.persistence.loadedValue !== 500000 || chrome.release?.workerTerminated !== true) fail('round-trip or browser closure evidence is incomplete')
const report = {
diff --git a/scripts/generate-freecad-property-heatflux-promotion.mjs b/scripts/generate-freecad-property-heatflux-promotion.mjs
index 335822f..89d9876 100644
--- a/scripts/generate-freecad-property-heatflux-promotion.mjs
+++ b/scripts/generate-freecad-property-heatflux-promotion.mjs
@@ -24,7 +24,7 @@ const propertyType = semantics.types?.find(({ typeId }) => typeId === 'App::Prop
if (propertyType?.support !== 'native-editable-codec' || propertyType.recordCount !== 1 || propertyType.objectTypeCount !== 1 || propertyType.statusNames?.length !== 0) fail('global property semantics did not promote App::PropertyHeatFlux')
const opaque = semantics.supportSummary?.['opaque-fcstd-proxy']
const editable = semantics.supportSummary?.['native-editable-codec']
-if (opaque?.typeCount !== 46 || opaque.recordCount !== 457 || editable?.typeCount !== 34 || editable.recordCount !== 4370) fail('global property support summary is not synchronized')
+if (opaque?.typeCount !== 45 || opaque.recordCount !== 455 || editable?.typeCount !== 35 || editable.recordCount !== 4372) fail('global property support summary is not synchronized')
if (roundTrip.classification?.zeroUnknownDrift !== true || roundTrip.classification.nativeValueMatches !== true || chrome.persistence?.loadedType !== 'App::PropertyHeatFlux' || chrome.persistence.loadedValue !== 500 || chrome.release?.workerTerminated !== true) fail('round-trip or browser closure evidence is incomplete')
const report = {
diff --git a/scripts/generate-freecad-property-integerset-promotion.mjs b/scripts/generate-freecad-property-integerset-promotion.mjs
index b9cff9e..38ee398 100644
--- a/scripts/generate-freecad-property-integerset-promotion.mjs
+++ b/scripts/generate-freecad-property-integerset-promotion.mjs
@@ -17,7 +17,7 @@ const propertyType = semantics.types?.find(({ typeId }) => typeId === 'App::Prop
if (propertyType?.support !== 'native-editable-codec' || propertyType.recordCount !== 2 || propertyType.objectTypeCount !== 2 || propertyType.statusNames?.length !== 0) fail('global property semantics did not promote App::PropertyIntegerSet')
const opaque = semantics.supportSummary?.['opaque-fcstd-proxy']
const editable = semantics.supportSummary?.['native-editable-codec']
-if (opaque?.typeCount !== 46 || opaque.recordCount !== 457 || editable?.typeCount !== 34 || editable.recordCount !== 4370) fail('global property support summary is not synchronized')
+if (opaque?.typeCount !== 45 || opaque.recordCount !== 455 || editable?.typeCount !== 35 || editable.recordCount !== 4372) fail('global property support summary is not synchronized')
if (roundTrip.classification?.zeroUnknownDrift !== true || chrome.persistence?.loadedType !== 'App::PropertyIntegerSet' || chrome.persistence.fcstdElement !== 'IntegerSet' || chrome.release?.workerTerminated !== true) fail('round-trip or browser closure evidence is incomplete')
const report = {
schemaVersion: 1, status: 'pass', taskId: 'PROP-app-propertyintegerset-I', baseline: semantics.baseline, propertyType: 'App::PropertyIntegerSet', recordCount: 2,
diff --git a/scripts/generate-freecad-property-linklisthidden-inventory.mjs b/scripts/generate-freecad-property-linklisthidden-inventory.mjs
new file mode 100644
index 0000000..3e03033
--- /dev/null
+++ b/scripts/generate-freecad-property-linklisthidden-inventory.mjs
@@ -0,0 +1,96 @@
+import { createHash } from 'node:crypto'
+import { readFile, writeFile } from 'node:fs/promises'
+import { resolve } from 'node:path'
+
+const root = resolve(new URL('..', import.meta.url).pathname)
+const runtimePath = '.cache/freecad/reference-desktop.json'
+const sourcePaths = [
+ '.cache/freecad/FreeCAD/src/App/PropertyLinks.h',
+ '.cache/freecad/FreeCAD/src/App/PropertyLinks.cpp',
+ '.cache/freecad/FreeCAD/src/Mod/Sketcher/App/SketchObject.h',
+ '.cache/freecad/FreeCAD/src/Mod/Sketcher/App/SketchObject.cpp',
+]
+const outputPath = resolve(root, 'config/freecad-property-linklisthidden-inventory.json')
+const [runtimeContent, ...sourceContents] = await Promise.all([
+ readFile(resolve(root, runtimePath)),
+ ...sourcePaths.map((path) => readFile(resolve(root, path))),
+])
+const runtime = JSON.parse(runtimeContent)
+const fail = (message) => { throw new Error(`FreeCAD PropertyLinkListHidden inventory generation failed: ${message}`) }
+if (runtime.schemaVersion !== 1 || runtime.freecadVersion !== '1.1.1' || runtime.gitCommit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d') fail('desktop oracle is not the locked FreeCAD baseline')
+
+const records = []
+for (const objectType of runtime.runtimeObjects?.types ?? []) {
+ for (const property of objectType.properties ?? []) {
+ if (property.typeId !== 'App::PropertyLinkListHidden') continue
+ records.push({
+ objectTypeId: objectType.typeId,
+ objectAvailable: objectType.available === true,
+ probeStatus: objectType.probeStatus,
+ propertyName: property.name,
+ group: property.group,
+ statusRaw: property.status,
+ statusNames: property.status.map((status) => status === 26 ? 'PropHidden' : `UnknownBit${status}`),
+ defaultRaw: property.default,
+ valueModel: {
+ kind: 'hidden-document-object-list',
+ inheritedFrom: 'App::PropertyLinkList',
+ elementType: 'same-document App::DocumentObject reference',
+ orderPreserved: true,
+ duplicatesPreserved: true,
+ writable: !property.status.some((status) => status === 'ReadOnly' || status === 'Immutable' || status === 24),
+ },
+ inputs: {
+ nativeCppSetValues: 'attached same-document DocumentObject list',
+ emptyListClears: true,
+ singleNullListClears: true,
+ detachedObjectRejected: true,
+ crossDocumentRejectedWithoutLinkAllowExternal: true,
+ },
+ dependencies: {
+ linkScope: 'Hidden',
+ createsBacklinks: false,
+ includedByDefaultGetLinks: false,
+ includedByGetLinksAll: true,
+ defaultDagDependency: false,
+ },
+ applicability: {
+ requiredObjectTypeId: objectType.typeId,
+ role: 'Sketch exported child geometry lookup by object name',
+ propertyWriteRequiresShape: false,
+ source: 'Document.supportedTypes runtime inventory and SketchObject::getSubObject',
+ },
+ })
+ }
+}
+records.sort((left, right) => `${left.objectTypeId}.${left.propertyName}`.localeCompare(`${right.objectTypeId}.${right.propertyName}`))
+if (records.length !== 2 || new Set(records.map(({ objectTypeId }) => objectTypeId)).size !== 2) fail(`expected two native App::PropertyLinkListHidden records, found ${records.length}`)
+
+const report = {
+ schemaVersion: 1,
+ status: 'pass',
+ baseline: { freecadVersion: runtime.freecadVersion, commit: runtime.gitCommit },
+ propertyType: 'App::PropertyLinkListHidden',
+ recordCount: records.length,
+ objectTypeCount: new Set(records.map(({ objectTypeId }) => objectTypeId)).size,
+ writableRecordCount: records.filter(({ valueModel }) => valueModel.writable).length,
+ records,
+ typeContract: {
+ inheritedFrom: 'App::PropertyLinkList',
+ linkScope: 'Hidden',
+ propertyStatus: 'PropHidden',
+ propertyStatusPurpose: 'hide the Exports property from the ordinary editor surface',
+ linkScopePurpose: 'exclude Exports from ordinary backlink and dependency traversal',
+ xmlElement: 'LinkList',
+ xmlChildElement: 'Link',
+ legacyRestoreType: 'App::PropertyLinkList',
+ },
+ provenance: {
+ runtime: { path: runtimePath, bytes: runtimeContent.length, sha256: createHash('sha256').update(runtimeContent).digest('hex') },
+ sources: sourcePaths.map((path, index) => ({ path, bytes: sourceContents[index].length, sha256: createHash('sha256').update(sourceContents[index]).digest('hex') })),
+ },
+ classification: 'opaque-fcstd-proxy',
+ nextPhases: ['B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'],
+}
+await writeFile(outputPath, `${JSON.stringify(report, null, 2)}\n`)
+console.log(JSON.stringify({ status: 'freecad-property-linklisthidden-inventory-generated', output: 'config/freecad-property-linklisthidden-inventory.json', propertyType: report.propertyType, recordCount: report.recordCount, writableRecordCount: report.writableRecordCount }, null, 2))
diff --git a/scripts/generate-freecad-property-linklisthidden-promotion.mjs b/scripts/generate-freecad-property-linklisthidden-promotion.mjs
new file mode 100644
index 0000000..7077bad
--- /dev/null
+++ b/scripts/generate-freecad-property-linklisthidden-promotion.mjs
@@ -0,0 +1,66 @@
+import { readFile, writeFile } from 'node:fs/promises'
+import { resolve } from 'node:path'
+
+const root = resolve(new URL('..', import.meta.url).pathname)
+const load = (path) => readFile(resolve(root, path), 'utf8').then(JSON.parse)
+const outputPath = resolve(root, 'config/freecad-property-linklisthidden-promotion.json')
+const [inventory, success, failure, mutation, roundTrip, chrome, semantics, progress] = await Promise.all([
+ load('config/freecad-property-linklisthidden-inventory.json'),
+ load('config/freecad-property-linklisthidden-success.json'),
+ load('config/freecad-property-linklisthidden-failure.json'),
+ load('config/freecad-property-linklisthidden-mutation.json'),
+ load('config/freecad-property-linklisthidden-roundtrip.json'),
+ load('config/chrome-property-linklisthidden-verification.json'),
+ load('config/freecad-native-property-semantics.json'),
+ load('config/freecad-follow-up-task-progress.json'),
+])
+const fail = (message) => { throw new Error(`FreeCAD PropertyLinkListHidden promotion generation failed: ${message}`) }
+const requiredCompletedPhases = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
+const completed = new Map(progress.completedTasks?.map((entry) => [entry.id, entry]) ?? [])
+for (const phase of requiredCompletedPhases) if (!completed.has(`PROP-app-propertylinklisthidden-${phase}`)) fail(`phase ${phase} is not completed in the evidence ledger`)
+if (inventory.status !== 'pass' || inventory.propertyType !== 'App::PropertyLinkListHidden' || inventory.recordCount !== 2 || inventory.objectTypeCount !== 2 || inventory.writableRecordCount !== 2) fail('inventory evidence is invalid')
+for (const [name, artifact] of Object.entries({ success, failure, mutation, roundTrip, chrome })) if (artifact.status !== 'pass') fail(`${name} evidence is not passing`)
+const propertyType = semantics.types?.find(({ typeId }) => typeId === 'App::PropertyLinkListHidden')
+if (propertyType?.support !== 'native-editable-codec' || propertyType.recordCount !== 2 || propertyType.objectTypeCount !== 2 || propertyType.statusNames?.join(',') !== 'PropHidden') fail('global property semantics did not promote App::PropertyLinkListHidden')
+const opaque = semantics.supportSummary?.['opaque-fcstd-proxy']
+const editable = semantics.supportSummary?.['native-editable-codec']
+if (opaque?.typeCount !== 45 || opaque.recordCount !== 455 || editable?.typeCount !== 35 || editable.recordCount !== 4372) fail('global property support summary is not synchronized')
+if (roundTrip.classification?.zeroUnknownDrift !== true || roundTrip.classification.hiddenLinksPreserved !== true || roundTrip.classification.nativeDependencyMetadataNormalized !== true || chrome.persistence?.loadedType !== 'App::PropertyLinkListHidden' || chrome.persistence.fcstdElement !== 'LinkList' || chrome.ui?.hiddenControlCountAfter !== 0 || chrome.release?.workerTerminated !== true) fail('round-trip or browser closure evidence is incomplete')
+const report = {
+ schemaVersion: 1,
+ status: 'pass',
+ taskId: 'PROP-app-propertylinklisthidden-I',
+ baseline: semantics.baseline,
+ propertyType: 'App::PropertyLinkListHidden',
+ recordCount: 2,
+ phaseEvidence: Object.fromEntries(requiredCompletedPhases.map((phase) => [phase, completed.get(`PROP-app-propertylinklisthidden-${phase}`).evidence])),
+ promotion: {
+ from: inventory.classification,
+ to: propertyType.support,
+ facadeValueModel: 'ordered-duplicate-preserving-same-document-object-name-array',
+ writableRecords: 2,
+ objectTypeCount: 2,
+ propertyStatus: 'PropHidden',
+ linkScope: 'Hidden',
+ runtimeDependencyEdges: 0,
+ fcstdElement: chrome.persistence.fcstdElement,
+ nativeRoundTripValue: roundTrip.classification.resavedValue,
+ browserRoundTripValue: chrome.persistence.loadedValue,
+ hiddenUiFiltered: chrome.ui.hiddenControlCountAfter === 0 && chrome.ui.hiddenTextPresentAfter === false,
+ hiddenLinksPreserved: roundTrip.classification.hiddenLinksPreserved,
+ nativeDependencyMetadataNormalized: roundTrip.classification.nativeDependencyMetadataNormalized,
+ zeroUnknownDrift: roundTrip.classification.zeroUnknownDrift,
+ },
+ exactBlockerSync: {
+ nativeEditableTypes: editable.typeCount,
+ nativeEditableRecords: editable.recordCount,
+ opaqueTypes: opaque.typeCount,
+ opaqueRecords: opaque.recordCount,
+ exactPromotionReady: semantics.exactPromotionReady,
+ exactBlocker: semantics.exactBlocker,
+ },
+ systemExact: false,
+ generatedAt: new Date().toISOString(),
+}
+await writeFile(outputPath, `${JSON.stringify(report, null, 2)}\n`)
+console.log(JSON.stringify({ status: 'freecad-property-linklisthidden-promotion-generated', output: 'config/freecad-property-linklisthidden-promotion.json', promotion: report.promotion, exactBlockerSync: report.exactBlockerSync }, null, 2))
diff --git a/scripts/generate-freecad-property-linksubhidden-inventory.mjs b/scripts/generate-freecad-property-linksubhidden-inventory.mjs
new file mode 100644
index 0000000..c5468e4
--- /dev/null
+++ b/scripts/generate-freecad-property-linksubhidden-inventory.mjs
@@ -0,0 +1,47 @@
+import { createHash } from 'node:crypto'
+import { readFile, writeFile } from 'node:fs/promises'
+import { resolve } from 'node:path'
+
+const root = resolve(new URL('..', import.meta.url).pathname)
+const runtimePath = '.cache/freecad/reference-desktop.json'
+const sourcePaths = [
+ '.cache/freecad/FreeCAD/src/App/PropertyLinks.h',
+ '.cache/freecad/FreeCAD/src/App/PropertyLinks.cpp',
+ '.cache/freecad/FreeCAD/src/App/Link.h',
+ '.cache/freecad/FreeCAD/src/Mod/Draft/draftobjects/draftlink.py',
+]
+const outputPath = resolve(root, 'config/freecad-property-linksubhidden-inventory.json')
+const [runtimeContent, ...sourceContents] = await Promise.all([readFile(resolve(root, runtimePath)), ...sourcePaths.map((path) => readFile(resolve(root, path)))])
+const runtime = JSON.parse(runtimeContent)
+const fail = (message) => { throw new Error(`FreeCAD PropertyLinkSubHidden inventory generation failed: ${message}`) }
+if (runtime.schemaVersion !== 1 || runtime.freecadVersion !== '1.1.1' || runtime.gitCommit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d') fail('desktop oracle is not the locked FreeCAD baseline')
+
+const records = []
+for (const objectType of runtime.runtimeObjects?.types ?? []) for (const property of objectType.properties ?? []) {
+ if (property.typeId !== 'App::PropertyLinkSubHidden') continue
+ records.push({
+ objectTypeId: objectType.typeId,
+ objectAvailable: objectType.available === true,
+ probeStatus: objectType.probeStatus,
+ propertyName: property.name,
+ group: property.group,
+ statusRaw: property.status,
+ statusNames: property.status.map((status) => status === 26 ? 'PropHidden' : status),
+ defaultRaw: property.default,
+ valueModel: { kind: 'hidden-document-object-sub-element-reference', inheritedFrom: 'App::PropertyLinkSub', elementType: 'same-document App::DocumentObject plus ordered sub-element names', orderPreserved: true, duplicatesPreserved: false, writable: true },
+ inputs: { nativeCppSetValues: 'attached same-document DocumentObject plus sub-element string list', emptySubListMeansWholeObject: true, nullObjectClears: true, detachedObjectRejected: true, crossDocumentRejectedWithoutLinkAllowExternal: true, invalidSubElementAcceptedAsReference: true },
+ dependencies: { linkScope: 'Hidden', createsBacklinks: false, includedByDefaultGetLinks: false, includedByGetLinksAll: true, defaultDagDependency: false },
+ applicability: { requiredObjectTypeId: objectType.typeId, role: 'Link colored element override lookup', propertyWriteRequiresShape: false, source: 'Link extension and Draft colored element implementation' },
+ })
+}
+records.sort((left, right) => `${left.objectTypeId}.${left.propertyName}`.localeCompare(`${right.objectTypeId}.${right.propertyName}`))
+if (records.length !== 4 || new Set(records.map(({ objectTypeId }) => objectTypeId)).size !== 4) fail(`expected four native App::PropertyLinkSubHidden records, found ${records.length}`)
+
+const report = {
+ schemaVersion: 1, status: 'pass', baseline: { freecadVersion: runtime.freecadVersion, commit: runtime.gitCommit }, propertyType: 'App::PropertyLinkSubHidden', recordCount: records.length, objectTypeCount: new Set(records.map(({ objectTypeId }) => objectTypeId)).size, writableRecordCount: records.filter(({ valueModel }) => valueModel.writable).length, records,
+ typeContract: { inheritedFrom: 'App::PropertyLinkSub', linkScope: 'Hidden', propertyStatus: 'PropHidden', dynamicStatus: 'LockDynamic', propertyStatusPurpose: 'hide the ColoredElements property from the ordinary editor surface', linkScopePurpose: 'exclude ColoredElements from ordinary backlink and dependency traversal', xmlElement: 'LinkSub', xmlChildElement: 'Sub', legacyRestoreType: 'App::PropertyLinkSub', emptySubListMeans: 'whole-object-reference' },
+ provenance: { runtime: { path: runtimePath, bytes: runtimeContent.length, sha256: createHash('sha256').update(runtimeContent).digest('hex') }, sources: sourcePaths.map((path, index) => ({ path, bytes: sourceContents[index].length, sha256: createHash('sha256').update(sourceContents[index]).digest('hex') })) },
+ classification: 'opaque-fcstd-proxy', nextPhases: ['B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'],
+}
+await writeFile(outputPath, `${JSON.stringify(report, null, 2)}\n`)
+console.log(JSON.stringify({ status: 'freecad-property-linksubhidden-inventory-generated', output: 'config/freecad-property-linksubhidden-inventory.json', propertyType: report.propertyType, recordCount: report.recordCount, writableRecordCount: report.writableRecordCount }, null, 2))
diff --git a/scripts/run-chrome-property-linklisthidden.mjs b/scripts/run-chrome-property-linklisthidden.mjs
new file mode 100644
index 0000000..2975660
--- /dev/null
+++ b/scripts/run-chrome-property-linklisthidden.mjs
@@ -0,0 +1,51 @@
+import { createServer } from 'node:http'
+import { existsSync } from 'node:fs'
+import { writeFile } from 'node:fs/promises'
+import { resolve } from 'node:path'
+import { spawn } from 'node:child_process'
+import { createChromeProfile, removeChromeProfile } from './chrome-profile.mjs'
+
+const root = resolve(new URL('..', import.meta.url).pathname)
+const reportPath = resolve(root, 'config/chrome-property-linklisthidden-verification.json')
+const waitForPort = (child) => new Promise((resolvePort, reject) => {
+ let output = ''
+ const timer = setTimeout(() => reject(new Error(`Vite did not announce a port: ${output.slice(-2000)}`)), 30_000)
+ const onData = (chunk) => { output += String(chunk); const match = output.match(/http:\/\/127\.0\.0\.1:(\d+)/); if (match) { clearTimeout(timer); resolvePort(Number(match[1])) } }
+ child.stdout.on('data', onData); child.stderr.on('data', onData)
+})
+
+const vite = spawn('./nodew', ['node_modules/vite/bin/vite.js', '--host', '127.0.0.1'], { cwd: root, stdio: ['ignore', 'pipe', 'pipe'] })
+let vitePort
+try { vitePort = await waitForPort(vite) } catch (error) { vite.kill('SIGTERM'); throw error }
+let resolveReport
+const reportPromise = new Promise((resolveValue) => { resolveReport = resolveValue })
+const server = createServer(async (request, response) => {
+ response.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
+ response.setHeader('Cross-Origin-Embedder-Policy', 'require-corp')
+ response.setHeader('Cross-Origin-Resource-Policy', 'same-origin')
+ if (request.method === 'POST' && request.url === '/__chrome-property-linklisthidden-report') {
+ let body = ''
+ request.setEncoding('utf8'); request.on('data', (chunk) => { body += String(chunk) })
+ request.on('end', async () => { const report = JSON.parse(body); await writeFile(reportPath, `${JSON.stringify(report, null, 2)}\n`); resolveReport(report); response.writeHead(204); response.end() })
+ return
+ }
+ try {
+ const upstream = await fetch(`http://127.0.0.1:${vitePort}${request.url}`, { headers: { ...request.headers } })
+ response.writeHead(upstream.status, Object.fromEntries(upstream.headers.entries())); response.end(Buffer.from(await upstream.arrayBuffer()))
+ } catch (error) { response.writeHead(502); response.end(String(error)) }
+})
+await new Promise((resolveServer) => server.listen(0, '127.0.0.1', resolveServer))
+const port = server.address().port
+const executable = process.env.CHROME_BIN || '/home/mes123456/.local/bin/google-chrome'
+if (!existsSync(executable)) throw new Error(`Chrome executable is missing: ${executable}`)
+const chromeProfile = await createChromeProfile('property-linklisthidden')
+const chrome = spawn(executable, ['--headless=new', '--no-sandbox', '--disable-gpu', '--disable-dev-shm-usage', '--noerrdialogs', '--no-first-run', `--user-data-dir=${chromeProfile}`, `http://127.0.0.1:${port}/chrome-property-linklisthidden-harness.html`], { cwd: root, stdio: ['ignore', 'ignore', 'pipe'] })
+let chromeStderr = ''
+chrome.stderr.on('data', (chunk) => { chromeStderr += String(chunk) })
+let timeoutId
+const timeoutReport = new Promise((resolveTimeout) => { timeoutId = setTimeout(() => resolveTimeout({ status: 'timeout', browserId: 'chrome' }), 120_000) })
+const report = await Promise.race([reportPromise, timeoutReport])
+clearTimeout(timeoutId); chrome.kill('SIGTERM'); vite.kill('SIGTERM'); server.closeAllConnections?.(); server.close()
+await removeChromeProfile(chromeProfile)
+console.log(JSON.stringify(report, null, 2))
+if (report.status !== 'pass') { if (chromeStderr) console.error(chromeStderr.slice(-3000)); process.exit(1) }
diff --git a/scripts/run-freecad-native-property-semantics.mjs b/scripts/run-freecad-native-property-semantics.mjs
index bab5415..9dbeb29 100644
--- a/scripts/run-freecad-native-property-semantics.mjs
+++ b/scripts/run-freecad-native-property-semantics.mjs
@@ -18,7 +18,7 @@ const editableTypes = new Set([
'App::PropertyAcceleration', 'App::PropertyAngle', 'App::PropertyArea', 'App::PropertyBool', 'App::PropertyBoolList', 'App::PropertyColor', 'App::PropertyColorList', 'App::PropertyDistance', 'App::PropertyEnumeration', 'App::PropertyForce', 'App::PropertyHeatFlux',
'App::PropertyFloat', 'App::PropertyFloatConstraint', 'App::PropertyFloatList', 'App::PropertyInteger', 'App::PropertyIntegerConstraint', 'App::PropertyIntegerSet',
'App::PropertyIntegerList', 'App::PropertyLength', 'App::PropertyLink', 'App::PropertyLinkHidden', 'App::PropertyLinkList',
- 'App::PropertyLinkSub', 'App::PropertyLinkSubList', 'App::PropertyPlacement', 'App::PropertyString',
+ 'App::PropertyLinkSub', 'App::PropertyLinkSubList', 'App::PropertyLinkListHidden', 'App::PropertyPlacement', 'App::PropertyString',
'App::PropertyPrecision', 'App::PropertyQuantityConstraint', 'App::PropertyStringList', 'App::PropertyVector', 'App::PropertyDirection', 'App::PropertyFile', 'App::PropertyFileIncluded', 'App::PropertyFont',
])
const specializedTypes = new Set([
diff --git a/scripts/run-freecad-property-linklisthidden-failure.mjs b/scripts/run-freecad-property-linklisthidden-failure.mjs
new file mode 100644
index 0000000..78f3b93
--- /dev/null
+++ b/scripts/run-freecad-property-linklisthidden-failure.mjs
@@ -0,0 +1,27 @@
+import { existsSync } from 'node:fs'
+import { readFile } from 'node:fs/promises'
+import { spawnSync } from 'node:child_process'
+import { resolve } from 'node:path'
+
+const root = resolve(new URL('..', import.meta.url).pathname)
+const executable = process.env.FREECAD_CMD || resolve(root, '.cache/freecad/install-desktop/bin/FreeCADCmd')
+const sysroot = resolve(root, '.cache/freecad/sysroot')
+const scriptPath = resolve(root, 'scripts/freecad-property-linklisthidden-failure.py')
+const outputPath = resolve(root, 'config/freecad-property-linklisthidden-failure.json')
+if (!existsSync(executable)) throw new Error(`FreeCAD PropertyLinkListHidden executable is missing: ${executable}`)
+const execution = spawnSync(executable, ['--python-path', resolve(sysroot, 'usr/lib/python3/dist-packages'), scriptPath], {
+ cwd: root,
+ encoding: 'utf8',
+ timeout: 120_000,
+ maxBuffer: 32 * 1024 * 1024,
+ env: {
+ ...process.env,
+ FREECAD_PROPERTY_LINKLISTHIDDEN_FAILURE_OUTPUT: outputPath,
+ PYTHONPATH: `${resolve(sysroot, 'usr/lib/python3/dist-packages')}${process.env.PYTHONPATH ? `:${process.env.PYTHONPATH}` : ''}`,
+ LD_LIBRARY_PATH: `${resolve(sysroot, 'usr/lib/x86_64-linux-gnu')}${process.env.LD_LIBRARY_PATH ? `:${process.env.LD_LIBRARY_PATH}` : ''}`,
+ },
+})
+const output = `${execution.stdout || ''}\n${execution.stderr || ''}`
+if (execution.error || execution.status !== 0 || !output.includes('FREECAD_PROPERTY_LINKLISTHIDDEN_FAILURE_RESULT=')) throw new Error(`FreeCAD PropertyLinkListHidden failure probe failed with status ${execution.status}: ${execution.error?.message || output.trim()}`)
+const report = JSON.parse(await readFile(outputPath, 'utf8'))
+console.log(JSON.stringify({ status: report.status, output: 'config/freecad-property-linklisthidden-failure.json', failureCount: report.failures?.length, failures: report.failures?.map(({ id, exception }) => ({ id, exception })) }, null, 2))
diff --git a/scripts/run-freecad-property-linklisthidden-mutation.mjs b/scripts/run-freecad-property-linklisthidden-mutation.mjs
new file mode 100644
index 0000000..60b0520
--- /dev/null
+++ b/scripts/run-freecad-property-linklisthidden-mutation.mjs
@@ -0,0 +1,27 @@
+import { existsSync } from 'node:fs'
+import { readFile } from 'node:fs/promises'
+import { spawnSync } from 'node:child_process'
+import { resolve } from 'node:path'
+
+const root = resolve(new URL('..', import.meta.url).pathname)
+const executable = process.env.FREECAD_CMD || resolve(root, '.cache/freecad/install-desktop/bin/FreeCADCmd')
+const sysroot = resolve(root, '.cache/freecad/sysroot')
+const scriptPath = resolve(root, 'scripts/freecad-property-linklisthidden-mutation.py')
+const outputPath = resolve(root, 'config/freecad-property-linklisthidden-mutation.json')
+if (!existsSync(executable)) throw new Error(`FreeCAD PropertyLinkListHidden executable is missing: ${executable}`)
+const execution = spawnSync(executable, ['--python-path', resolve(sysroot, 'usr/lib/python3/dist-packages'), scriptPath], {
+ cwd: root,
+ encoding: 'utf8',
+ timeout: 120_000,
+ maxBuffer: 32 * 1024 * 1024,
+ env: {
+ ...process.env,
+ FREECAD_PROPERTY_LINKLISTHIDDEN_MUTATION_OUTPUT: outputPath,
+ PYTHONPATH: `${resolve(sysroot, 'usr/lib/python3/dist-packages')}${process.env.PYTHONPATH ? `:${process.env.PYTHONPATH}` : ''}`,
+ LD_LIBRARY_PATH: `${resolve(sysroot, 'usr/lib/x86_64-linux-gnu')}${process.env.LD_LIBRARY_PATH ? `:${process.env.LD_LIBRARY_PATH}` : ''}`,
+ },
+})
+const output = `${execution.stdout || ''}\n${execution.stderr || ''}`
+if (execution.error || execution.status !== 0 || !output.includes('FREECAD_PROPERTY_LINKLISTHIDDEN_MUTATION_RESULT=')) throw new Error(`FreeCAD PropertyLinkListHidden mutation probe failed with status ${execution.status}: ${execution.error?.message || output.trim()}`)
+const report = JSON.parse(await readFile(outputPath, 'utf8'))
+console.log(JSON.stringify({ status: report.status, output: 'config/freecad-property-linklisthidden-mutation.json', caseCount: report.caseCount, restored: report.cases?.map(({ objectTypeId, semanticStateRestored }) => ({ objectTypeId, semanticStateRestored })) }, null, 2))
diff --git a/scripts/run-freecad-property-linklisthidden-roundtrip.ts b/scripts/run-freecad-property-linklisthidden-roundtrip.ts
new file mode 100644
index 0000000..2b844c7
--- /dev/null
+++ b/scripts/run-freecad-property-linklisthidden-roundtrip.ts
@@ -0,0 +1,108 @@
+import { createHash } from 'node:crypto'
+import { existsSync } from 'node:fs'
+import { mkdir, readFile, writeFile } from 'node:fs/promises'
+import { spawnSync } from 'node:child_process'
+import { resolve } from 'node:path'
+import { isDeepStrictEqual } from 'node:util'
+import { unzipSync } from 'fflate'
+import { createWebCadFacade } from '../src/facade/mockFacade'
+import { decodeFcstdPropertyValue } from '../src/facade/fcstd'
+
+const root = resolve(new URL('..', import.meta.url).pathname)
+const executable = process.env.FREECAD_CMD || resolve(root, '.cache/freecad/install-desktop/bin/FreeCADCmd')
+const sysroot = resolve(root, '.cache/freecad/sysroot')
+const scriptPath = resolve(root, 'scripts/freecad-property-linklisthidden-roundtrip.py')
+const outputDirectory = resolve(root, '.cache/freecad/property-linklisthidden-roundtrip')
+const nativePath = resolve(outputDirectory, 'native-initial.FCStd')
+const webPath = resolve(outputDirectory, 'web-edited.FCStd')
+const resavedPath = resolve(outputDirectory, 'native-resaved.FCStd')
+const reportPath = resolve(root, 'config/freecad-property-linklisthidden-roundtrip.json')
+if (!existsSync(executable)) throw new Error(`FreeCAD PropertyLinkListHidden roundtrip executable is missing: ${executable}`)
+await mkdir(outputDirectory, { recursive: true })
+
+const runNative = (mode: 'create' | 'verify', path: string, resaved = '') => {
+ const execution = spawnSync(executable, ['--python-path', resolve(sysroot, 'usr/lib/python3/dist-packages'), scriptPath], {
+ cwd: root,
+ encoding: 'utf8',
+ timeout: 120_000,
+ maxBuffer: 32 * 1024 * 1024,
+ env: {
+ ...process.env,
+ FREECAD_PROPERTY_LINKLISTHIDDEN_ROUNDTRIP_MODE: mode,
+ FREECAD_PROPERTY_LINKLISTHIDDEN_ROUNDTRIP_PATH: path,
+ FREECAD_PROPERTY_LINKLISTHIDDEN_ROUNDTRIP_RESAVED_PATH: resaved,
+ PYTHONPATH: `${resolve(sysroot, 'usr/lib/python3/dist-packages')}${process.env.PYTHONPATH ? `:${process.env.PYTHONPATH}` : ''}`,
+ LD_LIBRARY_PATH: `${resolve(sysroot, 'usr/lib/x86_64-linux-gnu')}${process.env.LD_LIBRARY_PATH ? `:${process.env.LD_LIBRARY_PATH}` : ''}`,
+ },
+ })
+ const output = `${execution.stdout || ''}\n${execution.stderr || ''}`
+ const marker = 'FREECAD_PROPERTY_LINKLISTHIDDEN_ROUNDTRIP_RESULT='
+ const line = output.split(/\r?\n/).find((candidate) => candidate.includes(marker))
+ if (execution.error || execution.status !== 0 || !line) throw new Error(`FreeCAD PropertyLinkListHidden ${mode} probe failed with status ${execution.status}: ${execution.error?.message || output.trim()}`)
+ return JSON.parse(line.slice(line.indexOf(marker) + marker.length))
+}
+
+const initial = ['ExportA', 'ExportB']
+const target = ['ExportB', 'ExportA', 'ExportB']
+const native = runNative('create', nativePath)
+const nativeBytes = new Uint8Array(await readFile(nativePath))
+const facade = createWebCadFacade({ runtimeMode: 'mock' })
+const inspectedBefore = facade.project.fcstd.inspect(nativeBytes)
+const property = (inspection: typeof inspectedBefore) => inspection.objects.find((object) => object.name === 'SketchProbe')?.properties.find((candidate) => candidate.name === 'Exports')
+const editedBytes = facade.project.fcstd.rewriteLinkListHidden(nativeBytes, { objectName: 'SketchProbe', propertyName: 'Exports', value: target, expectedValue: initial })
+const inspectedAfter = facade.project.fcstd.inspect(editedBytes)
+await facade.project.dispose()
+await writeFile(webPath, editedBytes)
+
+const initialFiles = unzipSync(nativeBytes)
+const editedFiles = unzipSync(editedBytes)
+const opaquePaths = Object.keys(initialFiles).filter((path) => path.toLowerCase() !== 'document.xml')
+const opaqueEntriesPreserved = opaquePaths.every((path) => Buffer.from(initialFiles[path]).equals(Buffer.from(editedFiles[path] ?? new Uint8Array())))
+const withoutEditedProperty = (inspection: typeof inspectedBefore) => inspection.objects.map((object) => ({ ...object, properties: object.name === 'SketchProbe' ? object.properties.filter((candidate) => candidate.name !== 'Exports') : object.properties }))
+const semanticObjectsPreserved = isDeepStrictEqual(withoutEditedProperty(inspectedBefore), withoutEditedProperty(inspectedAfter))
+const editedDocumentXml = new TextDecoder().decode(editedFiles['Document.xml'])
+const initialDocumentXml = new TextDecoder().decode(initialFiles['Document.xml'])
+const dependencyTargets = (xml: string, objectName: string): string[] => {
+ const escapedName = objectName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+ const match = xml.match(new RegExp(`]*Name="${escapedName}")[^>]*>([\\s\\S]*?)<\\/ObjectDeps>`))
+ return match ? [...match[1].matchAll(//g)].map((entry) => entry[1]) : []
+}
+const initialDependencyTargets = dependencyTargets(initialDocumentXml, 'SketchProbe')
+const webDependencyTargets = dependencyTargets(editedDocumentXml, 'SketchProbe')
+const targetMarkedTouched = /