diff --git a/config/compatibility-matrix.json b/config/compatibility-matrix.json index 5f3f903..4c30871 100644 --- a/config/compatibility-matrix.json +++ b/config/compatibility-matrix.json @@ -82,7 +82,7 @@ "javascript": "native/occt-history/dist/bitbybit-occt-history.js", "wasm": "native/occt-history/dist/bitbybit-occt-history.wasm", "javascriptSha256": "78f1cdd17a6a7b61ff8435c0ff8c912a43c4517e6ef49f3f741521662a903557", - "wasmSha256": "b6cb6307399b188cdf1377e9294b4af8e36b21783693101db8f985e41cff5fbe" + "wasmSha256": "b52125bd2c4999f62d435f0a34664556b7f1feb23590ddc0503fc6d3c7f1c8d6" }, "protocol": "native result and history records stay inside the provider; cross-WASM handles are forbidden; STEP text is the explicit transport boundary", "historyProtocol": { @@ -102,6 +102,7 @@ }, "freecadNamingBuild": { "status": "contract-only", + "prerequisiteStatus": "qt6-wasm-core-and-locked-mapped-name-source-probe-pass", "sdkEnvironment": "FREECAD_WASM_SDK_DIR", "requiredManifest": "manifest.json", "requiredCallbacks": ["freecadNamingAbiVersion", "freecadNamingCapabilitiesJson", "freecadNamingEvidenceJson"], @@ -109,6 +110,10 @@ "requiredSourceCommit": "0108fd4b4850cc46e625b60e53cea7a7bbe69f8d", "requiredEmscriptenVersion": "3.1.69", "buildCommand": "./npmw run build:freecad-naming-worker", + "qtCoreBuildCommand": "./npmw run build:qt6-wasm-core", + "sourceProbeBuildCommand": "./npmw run build:freecad-naming-source-probe", + "sourceProbeTestCommand": "./npmw run test:freecad-naming-source-probe", + "boundaryCheckCommand": "./npmw run check:freecad-private-naming-boundary", "checkCommand": "./npmw run check:freecad-naming-sdk", "statusBoundary": "Missing SDK or callback probe keeps EX-TSN-02=in_progress and systemExact=false" }, diff --git a/config/freecad-web-exact-parity-plan.json b/config/freecad-web-exact-parity-plan.json index c7c8479..cac6131 100644 --- a/config/freecad-web-exact-parity-plan.json +++ b/config/freecad-web-exact-parity-plan.json @@ -32,7 +32,7 @@ { "id": "EX-KER-01", "title": "Match OCCT construction, validation, tolerances and failure diagnostics", "priority": "P0", "status": "in_progress", "dependencies": ["EX-ORA-02"], "deliverables": ["Per-operation native differential", "Tolerance and placement contract", "Failure-code map"], "acceptance": ["Shape validity, topology, mass properties and diagnostics match locked FreeCAD fixtures", "Worker ownership returns to zero after every case"], "evidence": ["check:browser-occt", "check:chrome-geometry-features"], "exactBlockedBy": ["Not all FreeCAD builder flags and failure branches are represented"] }, { "id": "EX-TSN-00", "title": "Fail closed when native naming or builder evidence is absent", "priority": "P0", "status": "completed", "dependencies": [], "deliverables": ["Runtime naming evidence contract", "final-shape-only and ambiguous persistence", "ElementMap2 writer evidence guard"], "acceptance": ["Final geometry cannot mint a private FreeCAD token", "Unproven stage relations and isomorphic sources cannot become stable"], "evidence": ["check:freecad-native-naming-evidence", "check:freecad-exact-history-elementmap-gate"], "exactBlockedBy": [] }, { "id": "EX-TSN-01", "title": "Capture native stage Shape and Generated/Modified/Deleted for every builder", "priority": "P0", "status": "completed", "dependencies": ["EX-KER-01", "EX-TSN-00"], "deliverables": ["All builder-stage captures", "Multi-input and one-to-many lineage", "Local feature recompute history"], "acceptance": ["Every feature result relation has a producing native stage", "No final-result index is applied to an intermediate Shape"], "evidence": ["check:freecad-composite-history-elementmap", "check:freecad-tsn-stage-evidence"], "exactBlockedBy": [] }, - { "id": "EX-TSN-02", "title": "Expose native MappedNameRef and StringHasher decisions for every feature", "priority": "P0", "status": "in_progress", "dependencies": ["EX-TSN-01"], "deliverables": ["Native naming callback ABI", "Per-stage StringHasher evidence", "Lossless ElementMap2 token generation"], "acceptance": ["All new tokens originate in native evidence", "FreeCAD round-trip names remain byte and semantic stable"], "evidence": ["check:freecad-native-naming-evidence", "check:freecad-sketcher-partdesign-abi", "check:freecad-naming-sdk", "check:occt-history-artifact"], "exactBlockedBy": ["The shipped Worker does not link the FreeCAD private naming implementation for every builder"] }, + { "id": "EX-TSN-02", "title": "Expose native MappedNameRef and StringHasher decisions for every feature", "priority": "P0", "status": "in_progress", "dependencies": ["EX-TSN-01"], "deliverables": ["Native naming callback ABI", "Per-stage StringHasher evidence", "Lossless ElementMap2 token generation"], "acceptance": ["All new tokens originate in native evidence", "FreeCAD round-trip names remain byte and semantic stable"], "evidence": ["check:freecad-native-naming-evidence", "check:freecad-sketcher-partdesign-abi", "check:freecad-naming-sdk", "check:freecad-private-naming-boundary", "check:occt-history-artifact"], "exactBlockedBy": ["The shipped Worker does not link the FreeCAD private naming implementation for every builder"] }, { "id": "EX-TSN-03", "title": "Resolve isomorphic topology only from unique native provenance", "priority": "P0", "status": "in_progress", "dependencies": ["EX-TSN-01", "EX-TSN-02"], "deliverables": ["Symmetric Boolean corpus", "Candidate provenance graph", "Ambiguity repair lifecycle"], "acceptance": ["Unique native sources resolve deterministically", "Non-unique sources remain ambiguous across save, undo and recompute"], "evidence": ["test:topology-replay", "check:freecad-exact-history-elementmap-gate"], "exactBlockedBy": ["Symmetric cases without unique native provenance remain intentionally ambiguous"] }, { "id": "EX-TSN-04", "title": "Close topology naming across all feature edits and FCStd round-trips", "priority": "P0", "status": "pending", "dependencies": ["EX-TSN-02", "EX-TSN-03"], "deliverables": ["Cross-feature mutation matrix", "ElementMap2 history migration", "Long-chain LinkSub stability report"], "acceptance": ["Wrong bindings, unexplained relations and name drift are zero for the exhaustive corpus"], "evidence": ["check:freecad-fcstd-roundtrip", "check:freecad-exact-history-elementmap-gate"], "exactBlockedBy": ["Exhaustive cross-feature naming corpus is incomplete"] } ] diff --git a/config/occt-upstream-drift-baseline.json b/config/occt-upstream-drift-baseline.json index 191a682..dc818d7 100644 --- a/config/occt-upstream-drift-baseline.json +++ b/config/occt-upstream-drift-baseline.json @@ -18,7 +18,7 @@ "version": "8.0.0", "sourceCommit": "bb75017e915f6509a4bfa8b551f853352754c2da", "javascriptSha256": "78f1cdd17a6a7b61ff8435c0ff8c912a43c4517e6ef49f3f741521662a903557", - "wasmSha256": "b6cb6307399b188cdf1377e9294b4af8e36b21783693101db8f985e41cff5fbe" + "wasmSha256": "b52125bd2c4999f62d435f0a34664556b7f1feb23590ddc0503fc6d3c7f1c8d6" }, "golden": { "manifestScenarioCount": 100, diff --git a/config/offline-resources.json b/config/offline-resources.json index 3cde047..f8d59d5 100644 --- a/config/offline-resources.json +++ b/config/offline-resources.json @@ -139,6 +139,34 @@ "libraryPath": "build-caches/occt-history-build.tar.zst", "required": false }, + { + "id": "qt6-wasm-core-downloads", + "source": ".cache/toolchains/qt6/downloads", + "restore": ".cache/toolchains/qt6/downloads", + "libraryPath": "sources/qtbase-6.8.2-downloads.tar.zst", + "required": true + }, + { + "id": "qt6-wasm-core-source", + "source": ".cache/toolchains/qt6/src", + "restore": ".cache/toolchains/qt6/src", + "libraryPath": "sources/qtbase-6.8.2-source.tar.zst", + "required": true + }, + { + "id": "qt6-wasm-core-sdk", + "source": ".cache/toolchains/qt6/install-wasm", + "restore": ".cache/toolchains/qt6/install-wasm", + "libraryPath": "toolchains/qt6-wasm-core-6.8.2.tar.zst", + "required": true + }, + { + "id": "qt6-wasm-core-build", + "source": ".cache/toolchains/qt6/build-wasm", + "restore": ".cache/toolchains/qt6/build-wasm", + "libraryPath": "build-caches/qt6-wasm-core-build.tar.zst", + "required": false + }, { "id": "opencamlib-build", "source": ".cache/opencamlib-wasm", diff --git a/config/release-artifacts.json b/config/release-artifacts.json index 30b674a..ace35c8 100644 --- a/config/release-artifacts.json +++ b/config/release-artifacts.json @@ -344,8 +344,8 @@ }, { "path": "native/occt-history/bitbybit-occt-history.wasm", - "bytes": 23091777, - "sha256": "b6cb6307399b188cdf1377e9294b4af8e36b21783693101db8f985e41cff5fbe" + "bytes": 23091986, + "sha256": "b52125bd2c4999f62d435f0a34664556b7f1feb23590ddc0503fc6d3c7f1c8d6" }, { "path": "native/occt-history/browser-harness-worker.js", @@ -402,7 +402,7 @@ "status": "signed", "algorithm": "Ed25519", "keyId": "bitbybit-local-release-2026", - "payloadSha256": "20fb0fbc84390befc6fa22358b25cc139fb9dde8829643b5fc4ab3e804c44181", - "signature": "QyhRB17NFPFu9Nda1rU2KW+Lv5yROFM9Lo0vY9hOexecfwzb3PWw+XWHymLc1/hYjBm3Hw7dPdQ8Ou2KapWmBg==" + "payloadSha256": "c8734e374954b614941eb295623252af2094e9de7e415a46a399936d1cc391a9", + "signature": "vtnNIPTf3sCXN2F0c5lRVO7E76DXuolr+B5dZ8YQ34rWeWbJvgLqFVIqQ3IHke9nQVUhwYOR7SR2EyG3gucBAQ==" } } diff --git a/docs/continuation-status.zh-CN.md b/docs/continuation-status.zh-CN.md index df38577..e4449bc 100644 --- a/docs/continuation-status.zh-CN.md +++ b/docs/continuation-status.zh-CN.md @@ -1785,3 +1785,11 @@ Sketcher 数据模型和 FCStd codec 已覆盖 FreeCAD 1.1.1 的完整原生几 新增 `FreeCADPrivateNamingABI v1`:三项 Embind 回调分别报告 ABI version、锁定 FreeCAD 1.1.1/commit/operation 能力,并按 JSON 请求返回命名证据。Direct provider 与 Worker 都传输输入 STEP、最终 STEP/BRep、stage DAG 和 OCCT history;回包必须包含原生/歧义状态、MappedNameRef、StringHasher、ElementMap2,并通过 stage/result 上下文、16/32 MiB 上限、StringID、ElementMap2 和两者引用闭包检查。缺回调、版本/提交不符、operation 未声明或证据不合法均不生成 token,继续保留 `final-shape-only`。 `config/freecad-sketcher-partdesign-abi-contract.json` 与 `check:freecad-sketcher-partdesign-abi` 已进入默认 Sketcher 门禁,Facade 为 `190/190`,独立合同门、Facade boundary、锁定 FreeCAD 原生 FCStd 门和全量 `verify` 均通过。这里完成的是支持范围内的参数合同、FCStd 表面和私有 ABI/传输/验证层;当前随附的 `native/occt-history` 仍未链接 FreeCAD 私有 C++,所以浏览器实时命名实现仍为 `not-linked`。`EX-TSN-02` 由 `pending` 转为 `in_progress`,机器计划为 `2 completed / 37 in_progress / 13 pending / 0 blocked`;`systemExact=false` 和 promotion fail-closed 状态不变。 + +## 230. 2026-08-12 Qt6 wasm 前置条件、FreeCAD 私有源码探针与边界门 + +QtBase 6.8.2 源码包已通过 USTC 国内镜像取得,并用 Qt 发布包官方 SHA-256 `012043ce6d411e6e8a91fdc4e05e6bedcfa10fcb1347d3c33908f7fdd10dfe05` 校验。新增 `build:qt6-wasm-core`,固定 Emscripten 3.1.69,构建静态 wasm32 `Qt6Core`、bundled Pcre2 和 zlib;离线模式只使用本机缓存,缺少已校验源码包时立即失败。源码包、展开源码、可用 SDK 和可选增量构建目录均已纳入项目离线资源清单。 + +新增的 `build:freecad-naming-source-probe` 并未假装提供生产命名实现。它固定 FreeCAD 1.1.1 提交 `0108fd4b4850cc46e625b60e53cea7a7bbe69f8d`,同时校验 `IndexedName.cpp` 与 `MappedName.cpp` 的逐文件 SHA-256,再把这两个原始编译单元与 wasm Qt6Core 编译为独立探针。运行时验证 IndexedName 解析、MappedName 追加和 tag 读取;探针明确检查不导出 `freecadNamingAbiVersion`、`freecadNamingCapabilitiesJson`、`freecadNamingEvidenceJson`。这证明的是锁定基础源码与工具链可交叉编译,不包含完整 StringHasher、FreeCADApp/Part 静态库或生产 Worker bridge。 + +新增 `check:freecad-private-naming-boundary`,跨 ABI 合同、兼容性矩阵和 exact 计划校验同一状态,并接入默认 `verify`、真实 wasm lane 和离线 smoke。准确边界保持不变:随附 OCCT Worker 仍没有链接 FreeCAD 私有 C++ 命名实现,制品仍无三项私有命名回调;`shippedWorkerImplementation=not-linked`、`freecadNamingBuild.status=contract-only`、`EX-TSN-02=in_progress`、`systemExact=false`。本批只完成可离线复现的前置构建和防止状态误提升的机器门禁。 diff --git a/docs/freecad-full-parity-plan.zh-CN.md b/docs/freecad-full-parity-plan.zh-CN.md index 3d445bd..df0bb9c 100644 --- a/docs/freecad-full-parity-plan.zh-CN.md +++ b/docs/freecad-full-parity-plan.zh-CN.md @@ -393,6 +393,10 @@ PartDesign Mirrored 已形成 experimental whole-shape 垂直切片:Facade 验 ABI 输入现同时携带文档 `objectId`、FCStd 原生正整数 object tag、上一阶段命名证据、明确的 result object/tag 和阶段 DAG;object tag 会从 `Document.xml` 的 `Object.id` 导入,按稳定值写回 FCStd,并通过 SQLite schema v7、OPFS 重开和 checkpoint 往返。`build:freecad-naming-worker` 是独立的 fail-closed 构建入口,只接受锁定提交、Emscripten 3.1.69、wasm Qt/Python、FreeCADBase/FreeCADApp/Part 静态库及三回调 bridge 的哈希清单;普通 `build:occt-history` 继续是 OCCT-only。`check:occt-history-artifact` 会实际实例化 WASM 并探测三回调,当前结果为 unavailable,因此 `EX-TSN-02=in_progress` 与 `systemExact=false` 保持不变。 +Qt 前置条件已按独立、可离线复现的链路落地:`build:qt6-wasm-core` 使用 Emscripten 3.1.69 构建 QtBase 6.8.2 静态 wasm `Qt6Core`,下载使用 USTC 国内镜像,但必须先通过 Qt 发布包官方 SHA-256;离线模式缺包时直接失败,不允许回退联网。`build:freecad-naming-source-probe` 进一步锁定 FreeCAD 提交及 `IndexedName.cpp`、`MappedName.cpp` 两个原始编译单元的内容哈希,并验证它们能与 wasm Qt6Core 一起生成和执行 wasm32 探针。该探针使用最小编译适配头,不包含完整 `StringHasher`、FreeCADApp/Part 静态库或生产 bridge,也明确禁止导出三项生产命名回调,因此只消除工具链和基础源码可编译性风险,不构成 `FreeCADPrivateNamingABI` 的生产实现。 + +`check:freecad-private-naming-boundary` 同时读取 ABI 合同、兼容性矩阵和 exact 计划,固定校验 `shippedWorkerImplementation=not-linked`、`freecadNamingBuild.status=contract-only`、`EX-TSN-02=in_progress` 与 `systemExact=false`;该检查已进入默认 `verify`、真实 wasm lane 和离线 smoke。只有生产 OCCT Worker 实际链接锁定的 FreeCAD 私有静态库/bridge,且制品探针取得三项回调和完整命名证据后,才能在后续批次修改这些状态。 + Sketcher/PartDesign 的 Web 支持范围新增可执行参数合同:Sketcher 覆盖 FreeCAD 1.1.1 FCStd 表面的 9 类 geometry、19 类 constraint 和 10 类 InternalAlignment 语义;PartDesign 覆盖 Facade 已支持的 21 个 family、202 个属性槽和 72 个语义分区,并在对象创建及属性编辑时执行跨字段校验。`check:freecad-sketcher-partdesign-abi` 会从实现重算这些计数并拒绝陈旧报告;该“完整”限定于 supported Web Facade,不包含尚未实现的 FreeCAD PartDesign TypeId,也不表示 basic TypeScript solver 已求解全部圆锥曲线约束。 逐 feature 等级目前由 `config/compatibility-matrix.json` 的 `facadeCapabilities.geometry.featureLevels` 管理:Pad/Pocket/Revolution/Groove/Boolean 为有 native history 子集的 `compatible`,Fillet/Chamfer/MultiTransform 保持 `experimental`。`systemExactEvaluation.exact` 必须在所有 feature 的阶段 Shape/history、私有 token 证据和同构来源都闭合后才允许改为 true;当前值固定为 false,阻断项为私有 FreeCAD token 算法、没有原生 builder stage、以及无唯一同构来源。 diff --git a/docs/offline-development.zh-CN.md b/docs/offline-development.zh-CN.md index b106c6f..da8c7cb 100644 --- a/docs/offline-development.zh-CN.md +++ b/docs/offline-development.zh-CN.md @@ -16,6 +16,7 @@ - FreeCAD 1.1.1(含 GSL、OndselSolver、AddonManager、测试子模块)、bitbybit、OCCT 8、OpenCAMLib、CAMotics/C! 和 LinuxCNC Web 工作树; - FreeCAD headless/desktop Oracle、FreeCAD sysroot、OCCT/PlaneGCS 原生 Worker 制品和可选增量构建目录; - Playwright Chromium/Firefox/WebKit、Boost/Eigen 头文件,以及本机 `/var/cache/apt/archives` 中的 Debian 安装包池。 +- QtBase 6.8.2 源码、静态 wasm Qt6Core SDK 与可选增量构建缓存;源码包使用 Qt 官方 SHA-256 校验,国内 USTC 镜像仅作为传输源。 LinuxCNC 和 CAMotics 存在尚未合入各自上游提交的本地适配,因此保存完整工作树快照。`working_ocl/` 是上述源码的历史重复副本,不作为规范资源;构建入口实际使用的 `OpenCAMLib/`、`CAMotics/` 和 `.cache/bitbybit/bitbybit` 才进入资源库。 diff --git a/native/freecad-naming-probe/probe.cpp b/native/freecad-naming-probe/probe.cpp new file mode 100644 index 0000000..20ababd --- /dev/null +++ b/native/freecad-naming-probe/probe.cpp @@ -0,0 +1,34 @@ +#include + +#include +#include + +#include +#include + +namespace +{ +std::string freecadPrivateNamingSourceProbe() +{ + const Data::IndexedName indexed("Edge12"); + if (!indexed || indexed.getIndex() != 12 || std::string(indexed.getType()) != "Edge") { + throw std::runtime_error("FreeCAD IndexedName source probe failed"); + } + + Data::MappedName name(indexed); + name += ";:M;CUT;:H2a:7,E"; + long tag = 0; + int length = 0; + char type = 0; + const int position = name.findTagInElementName(&tag, &length, nullptr, &type); + if (position < 0 || tag != 0x2a || type != 'E') { + throw std::runtime_error("FreeCAD MappedName source probe failed"); + } + return name.toString(); +} +} // namespace + +EMSCRIPTEN_BINDINGS(freecad_private_naming_source_probe) +{ + emscripten::function("freecadPrivateNamingSourceProbe", &freecadPrivateNamingSourceProbe); +} diff --git a/native/freecad-naming-probe/shims/App/StringHasher.h b/native/freecad-naming-probe/shims/App/StringHasher.h new file mode 100644 index 0000000..aaf212f --- /dev/null +++ b/native/freecad-naming-probe/shims/App/StringHasher.h @@ -0,0 +1,27 @@ +#pragma once + +#define APP_STRING_ID_H + +#include + +namespace App +{ +class StringIDRef +{ +public: + void toBytes(QByteArray& bytes) const + { + bytes.clear(); + } + + bool operator<(const StringIDRef&) const + { + return false; + } + + bool operator==(const StringIDRef&) const + { + return true; + } +}; +} // namespace App diff --git a/native/freecad-naming-probe/shims/Base/Console.h b/native/freecad-naming-probe/shims/Base/Console.h new file mode 100644 index 0000000..db80a3a --- /dev/null +++ b/native/freecad-naming-probe/shims/Base/Console.h @@ -0,0 +1,3 @@ +#pragma once + +#define FC_LOG_LEVEL_INIT(...) diff --git a/native/freecad-naming-probe/shims/QtCore.h b/native/freecad-naming-probe/shims/QtCore.h new file mode 100644 index 0000000..221f28b --- /dev/null +++ b/native/freecad-naming-probe/shims/QtCore.h @@ -0,0 +1,3 @@ +#pragma once + +#define HAVE_Q_DISABLE_COPY_MOVE diff --git a/native/freecad-naming-probe/smoke-test.mjs b/native/freecad-naming-probe/smoke-test.mjs new file mode 100644 index 0000000..01f584d --- /dev/null +++ b/native/freecad-naming-probe/smoke-test.mjs @@ -0,0 +1,16 @@ +import createProbe from './dist/freecad-private-naming-source-probe.js' + +const probe = await createProbe() +const result = probe.freecadPrivateNamingSourceProbe() +if (result !== 'Edge12;:M;CUT;:H2a:7,E') throw new Error(`Unexpected FreeCAD private naming source probe result: ${result}`) +for (const forbidden of ['freecadNamingAbiVersion', 'freecadNamingCapabilitiesJson', 'freecadNamingEvidenceJson']) { + if (typeof probe[forbidden] === 'function') throw new Error(`Source prerequisite probe must not export production ABI callback ${forbidden}.`) +} +console.log(JSON.stringify({ + status: 'source-prerequisite-pass', + freecadVersion: '1.1.1', + sourceCommit: '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d', + linkedSources: ['App/IndexedName.cpp', 'App/MappedName.cpp'], + result, + productionWorkerLinked: false, +}, null, 2)) diff --git a/native/occt-history/README.md b/native/occt-history/README.md index 850d448..9f433e0 100644 --- a/native/occt-history/README.md +++ b/native/occt-history/README.md @@ -43,6 +43,21 @@ publishing the OCCT-only artifact. A successful artifact probe still only establishes ABI linkage; exact promotion additionally requires valid MappedNameRef, StringHasher, ElementMap2, stage history, and round-trip evidence. +The repository also carries a prerequisite-only source probe. It builds QtBase +6.8.2 `Qt6Core` for wasm, then compiles the locked FreeCAD `IndexedName.cpp` and +`MappedName.cpp` sources without exporting the production naming callbacks: + +```bash +./npmw run build:qt6-wasm-core +./npmw run build:freecad-naming-source-probe +./npmw run test:freecad-naming-source-probe +./npmw run check:freecad-private-naming-boundary +``` + +Passing this probe establishes that the private source subset and toolchain are +cross-compilable. It does not link the production OCCT Worker, does not close +EX-TSN-02, and does not change `systemExact=false`. + The versioned JSON request carries document `objectId`, stable positive `objectTag`, prior naming evidence, result object identity, result tag, stage DAG, STEP/BRep, and OCCT Generated/Modified/Deleted history. No Web fallback is diff --git a/package.json b/package.json index dd673e6..9cd035c 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "test:golden:freecad:family-failures": "node scripts/run-freecad-golden-failures.mjs --manifest=fixtures/freecad-golden/feature-families/failures/manifest.json", "check:occt-history-artifact": "node scripts/check-occt-history-browser-artifact.mjs", "check:freecad-naming-sdk": "node scripts/check-freecad-naming-sdk.mjs", + "check:freecad-private-naming-boundary": "node scripts/check-freecad-private-naming-boundary.mjs", "test:browser-occt": "node scripts/run-browser-occt-harness.mjs", "test:freecad:threeway": "node scripts/run-freecad-threeway-boolean.mjs", "check:browser-occt": "node scripts/check-occt-browser-verification.mjs", @@ -82,6 +83,10 @@ "test:freecad-cam-path-report": "node --test tests/freecadCamPathOracle.test.mjs", "build:occt-history": "bash native/occt-history/build.sh", "build:freecad-naming-worker": "FREECAD_WASM_NAMING_REQUIRED=1 bash native/occt-history/build.sh", + "build:qt6-wasm-core": "bash scripts/build-qt6-wasm-core.sh", + "build:freecad-naming-source-probe": "bash scripts/build-freecad-naming-source-probe.sh", + "test:freecad-naming-source-probe": "node native/freecad-naming-probe/smoke-test.mjs", + "test:freecad-private-naming-boundary": "node --test tests/freecadPrivateNamingBoundary.test.mjs", "test:occt-history": "node native/occt-history/smoke-test.mjs", "build:planegcs": "bash native/planegcs/build.sh", "test:planegcs": "node native/planegcs/smoke-test.mjs", @@ -269,7 +274,7 @@ "test:browser-matrix": "node scripts/run-browser-matrix.mjs", "check:browser-matrix": "node scripts/check-browser-matrix.mjs", "check:runtime": "node scripts/check-runtime.mjs", - "verify": "npm run check:runtime && npm run check:baseline && npm run check:bitbybit-history && npm run check:execution-plan && npm run check:freecad-sketcher-constraints && npm run check:freecad-inventory && npm run check:freecad-type-properties && npm run check:freecad-gui-commands && npm run check:freecad-entrypoints && npm run check:freecad-golden-fixtures && npm run check:freecad-semantic-comparator && npm run check:freecad-fcstd-roundtrip && npm run check:freecad-composite-history-elementmap && npm run check:freecad-exact-history-elementmap-gate && npm run check:freecad-native-naming-evidence && npm run check:fcstd-hardening && npm run check:freecad-naming-sdk && npm run check:occt-history-artifact && npm run check:planegcs-artifact && npm run check:browser-occt && npm run check:chrome-topology && npm run check:chrome-planegcs && npm run check:chrome-fault-injection && npm run check:chrome-opfs-migration && npm run check:chrome-persistence && npm run check:chrome-app-e2e && npm run check:chrome-partdesign-lifecycle && npm run check:pwa && npm run check:chrome-offline && npm run check:chrome-geometry-features && npm run check:chrome-native-history && npm run check:chrome-native-pad-history && npm run check:chrome-native-pocket-history && npm run check:chrome-native-loft-history && npm run check:chrome-native-pipe-history && npm run check:chrome-native-revolution-history && npm run check:chrome-native-groove-history && npm run check:chrome-native-fillet-history && npm run check:chrome-native-chamfer-history && npm run check:chrome-native-hole-history && npm run check:chrome-native-draft-history && npm run check:chrome-native-thickness-history && npm run check:chrome-native-linear-pattern-history && npm run check:chrome-native-polar-pattern-history && npm run check:chrome-native-mirrored-history && npm run check:chrome-native-multi-transform-history && npm run check:chrome-sketcher-diagnostics && npm run check:chrome-profile-validation && npm run check:chrome-sketcher-editor && npm run check:chrome-sketcher-bspline && npm run check:chrome-sketcher-stress && npm run check:chrome-part-primitives && npm run check:chrome-partdesign-transform && npm run check:chrome-partdesign-loft && npm run check:partdesign-closure && npm run check:chrome-fcstd-semantic && npm run check:chrome-fcstd-roundtrip && npm run check:fcstd-closure && npm run check:chrome-spreadsheet && npm run check:chrome-plot && npm run check:chrome-draft && npm run check:chrome-techdraw && npm run check:chrome-assembly && npm run check:chrome-mesh && npm run check:chrome-inspection && npm run check:chrome-bim && npm run check:chrome-cam && npm run check:cam-parity && npm run check:cam-pipeline && npm run check:opencamlib-wasm && npm run check:camotics-native && npm run check:camotics-gui-tpl && npm run check:camotics-wasm && npm run check:chrome-camotics-wasm && npm run check:chrome-cam-native-simulation && npm run check:chrome-cam-linuxcnc-machine && npm run check:chrome-fem && npm run check:chrome-robot && npm run check:chrome-data && npm run check:chrome-addon && npm run check:chrome-script && npm run check:chrome-surface && npm run check:chrome-qa08 && npm run check:chrome-secondary-formats && npm run check:chrome-performance && npm run check:chrome-production-document && npm run check:document-closure && npm run check:chrome-engineering && npm run check:chrome-security && npm run check:chrome-fcstd-golden && npm run check:sbom && npm run check:project-migration && npm run check:security-baseline && npm run check:dependency-audit && npm run check:freecad:threeway && npm run check:occt-upstream-drift && npm run check:facade-boundary && npm run test:golden && npm run test:spreadsheet && npm run test:plot && npm run test:draft && npm run test:techdraw && npm run test:assembly && npm run test:mesh && npm run test:inspection && npm run test:bim && npm run test:cam && npm run test:camotics-wasm && npm run test:fem && npm run test:robot && npm run test:data && npm run test:addon && npm run test:script && npm run test:surface && npm run test:locale && npm run test:secondary-formats && npm run test:performance && npm run test:production-document && npm run test:engineering && npm run test:security-preflight && npm run test:fcstd-roundtrip && npm run test:facade && npm run test:fcstd-fuzz && npm run test:geometry-input-fuzz && npm run test:sketch-solver-fuzz && npm run check:quality-closure && npm run test:planegcs && npm run test:project-migration && npm run test:sketcher-stress && npm run test:topology-replay && npm run build && npm run check:built-pwa && npm run generate:release-artifacts && npm run check:release-artifacts && npm run check:release-closure && npm run test:release-signature", + "verify": "npm run check:runtime && npm run check:baseline && npm run check:bitbybit-history && npm run check:execution-plan && npm run check:freecad-sketcher-constraints && npm run check:freecad-inventory && npm run check:freecad-type-properties && npm run check:freecad-gui-commands && npm run check:freecad-entrypoints && npm run check:freecad-golden-fixtures && npm run check:freecad-semantic-comparator && npm run check:freecad-fcstd-roundtrip && npm run check:freecad-composite-history-elementmap && npm run check:freecad-exact-history-elementmap-gate && npm run check:freecad-native-naming-evidence && npm run check:fcstd-hardening && npm run check:freecad-naming-sdk && npm run check:freecad-private-naming-boundary && npm run check:occt-history-artifact && npm run check:planegcs-artifact && npm run check:browser-occt && npm run check:chrome-topology && npm run check:chrome-planegcs && npm run check:chrome-fault-injection && npm run check:chrome-opfs-migration && npm run check:chrome-persistence && npm run check:chrome-app-e2e && npm run check:chrome-partdesign-lifecycle && npm run check:pwa && npm run check:chrome-offline && npm run check:chrome-geometry-features && npm run check:chrome-native-history && npm run check:chrome-native-pad-history && npm run check:chrome-native-pocket-history && npm run check:chrome-native-loft-history && npm run check:chrome-native-pipe-history && npm run check:chrome-native-revolution-history && npm run check:chrome-native-groove-history && npm run check:chrome-native-fillet-history && npm run check:chrome-native-chamfer-history && npm run check:chrome-native-hole-history && npm run check:chrome-native-draft-history && npm run check:chrome-native-thickness-history && npm run check:chrome-native-linear-pattern-history && npm run check:chrome-native-polar-pattern-history && npm run check:chrome-native-mirrored-history && npm run check:chrome-native-multi-transform-history && npm run check:chrome-sketcher-diagnostics && npm run check:chrome-profile-validation && npm run check:chrome-sketcher-editor && npm run check:chrome-sketcher-bspline && npm run check:chrome-sketcher-stress && npm run check:chrome-part-primitives && npm run check:chrome-partdesign-transform && npm run check:chrome-partdesign-loft && npm run check:partdesign-closure && npm run check:chrome-fcstd-semantic && npm run check:chrome-fcstd-roundtrip && npm run check:fcstd-closure && npm run check:chrome-spreadsheet && npm run check:chrome-plot && npm run check:chrome-draft && npm run check:chrome-techdraw && npm run check:chrome-assembly && npm run check:chrome-mesh && npm run check:chrome-inspection && npm run check:chrome-bim && npm run check:chrome-cam && npm run check:cam-parity && npm run check:cam-pipeline && npm run check:opencamlib-wasm && npm run check:camotics-native && npm run check:camotics-gui-tpl && npm run check:camotics-wasm && npm run check:chrome-camotics-wasm && npm run check:chrome-cam-native-simulation && npm run check:chrome-cam-linuxcnc-machine && npm run check:chrome-fem && npm run check:chrome-robot && npm run check:chrome-data && npm run check:chrome-addon && npm run check:chrome-script && npm run check:chrome-surface && npm run check:chrome-qa08 && npm run check:chrome-secondary-formats && npm run check:chrome-performance && npm run check:chrome-production-document && npm run check:document-closure && npm run check:chrome-engineering && npm run check:chrome-security && npm run check:chrome-fcstd-golden && npm run check:sbom && npm run check:project-migration && npm run check:security-baseline && npm run check:dependency-audit && npm run check:freecad:threeway && npm run check:occt-upstream-drift && npm run check:facade-boundary && npm run test:golden && npm run test:spreadsheet && npm run test:plot && npm run test:draft && npm run test:techdraw && npm run test:assembly && npm run test:mesh && npm run test:inspection && npm run test:bim && npm run test:cam && npm run test:camotics-wasm && npm run test:fem && npm run test:robot && npm run test:data && npm run test:addon && npm run test:script && npm run test:surface && npm run test:locale && npm run test:secondary-formats && npm run test:performance && npm run test:production-document && npm run test:engineering && npm run test:security-preflight && npm run test:fcstd-roundtrip && npm run test:facade && npm run test:fcstd-fuzz && npm run test:geometry-input-fuzz && npm run test:sketch-solver-fuzz && npm run check:quality-closure && npm run test:planegcs && npm run test:project-migration && npm run test:sketcher-stress && npm run test:topology-replay && npm run build && npm run check:built-pwa && npm run generate:release-artifacts && npm run check:release-artifacts && npm run check:release-closure && npm run test:release-signature", "test:chrome-native-thickness-history": "node scripts/run-chrome-native-thickness-history.mjs", "check:chrome-native-thickness-history": "node scripts/check-chrome-native-thickness-history.mjs", "test:chrome-native-linear-pattern-history": "node scripts/run-chrome-native-linear-pattern-history.mjs", diff --git a/scripts/build-freecad-naming-source-probe.sh b/scripts/build-freecad-naming-source-probe.sh new file mode 100755 index 0000000..24d75cc --- /dev/null +++ b/scripts/build-freecad-naming-source-probe.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +FREECAD_SOURCE_DIR="${FREECAD_SOURCE_DIR:-${ROOT_DIR}/.cache/freecad/FreeCAD}" +QT6_WASM_DIR="${QT6_WASM_DIR:-${ROOT_DIR}/.cache/toolchains/qt6/install-wasm}" +OFFLINE_INCLUDE_DIR="${OFFLINE_INCLUDE_DIR:-${ROOT_DIR}/.cache/offline-sysroot/include}" +DIST_DIR="${ROOT_DIR}/native/freecad-naming-probe/dist" +SHIM_DIR="${ROOT_DIR}/native/freecad-naming-probe/shims" +FREECAD_COMMIT="0108fd4b4850cc46e625b60e53cea7a7bbe69f8d" +INDEXED_NAME_SHA256="73e0e60a9d6ee06851252e2071ebdd58698f8f99733903232529b3a194164435" +MAPPED_NAME_SHA256="90173aba5f331ac9453589833f7e5fc63c5c80a35f1600dbc71211606b1c9dbc" + +if [[ ! -d "${FREECAD_SOURCE_DIR}/.git" ]] || [[ "$(git -C "${FREECAD_SOURCE_DIR}" rev-parse HEAD)" != "${FREECAD_COMMIT}" ]]; then + echo "FreeCAD private naming probe requires locked commit ${FREECAD_COMMIT}." >&2 + exit 1 +fi +printf '%s %s\n' \ + "${INDEXED_NAME_SHA256}" "${FREECAD_SOURCE_DIR}/src/App/IndexedName.cpp" \ + "${MAPPED_NAME_SHA256}" "${FREECAD_SOURCE_DIR}/src/App/MappedName.cpp" | sha256sum -c - +if [[ "$(emcc --version | sed -n '1s/.* \([0-9][0-9.]*\) .*/\1/p')" != "3.1.69" ]]; then + echo "FreeCAD private naming probe requires Emscripten 3.1.69." >&2 + exit 1 +fi +for path in \ + "${QT6_WASM_DIR}/include/QtCore/QByteArray" \ + "${QT6_WASM_DIR}/lib/libQt6Core.a" \ + "${QT6_WASM_DIR}/lib/libQt6BundledPcre2.a" \ + "${QT6_WASM_DIR}/lib/libQt6BundledZLIB.a"; do + if [[ ! -s "${path}" ]]; then + echo "Missing Qt6 wasm Core prerequisite: ${path}" >&2 + exit 1 + fi +done +if [[ ! -s "${OFFLINE_INCLUDE_DIR}/boost/signals2/signal.hpp" ]]; then + echo "Missing offline Boost headers: ${OFFLINE_INCLUDE_DIR}" >&2 + exit 1 +fi + +mkdir -p "${DIST_DIR}" +em++ \ + "${ROOT_DIR}/native/freecad-naming-probe/probe.cpp" \ + "${FREECAD_SOURCE_DIR}/src/App/IndexedName.cpp" \ + "${FREECAD_SOURCE_DIR}/src/App/MappedName.cpp" \ + -I"${SHIM_DIR}" \ + -include "${SHIM_DIR}/App/StringHasher.h" \ + -I"${FREECAD_SOURCE_DIR}/src" \ + -I"${QT6_WASM_DIR}/include" \ + -I"${QT6_WASM_DIR}/include/QtCore" \ + -I"${OFFLINE_INCLUDE_DIR}" \ + -std=c++17 -Oz -fexceptions --bind \ + -Wl,--start-group \ + "${QT6_WASM_DIR}/lib/libQt6Core.a" \ + "${QT6_WASM_DIR}/lib/libQt6BundledPcre2.a" \ + "${QT6_WASM_DIR}/lib/libQt6BundledZLIB.a" \ + -Wl,--end-group \ + -sMODULARIZE=1 -sEXPORT_ES6=1 -sENVIRONMENT=web,worker,node \ + -sALLOW_MEMORY_GROWTH=1 -sNO_EXIT_RUNTIME=1 \ + -o "${DIST_DIR}/freecad-private-naming-source-probe.js" + +sha256sum "${DIST_DIR}/freecad-private-naming-source-probe.js" "${DIST_DIR}/freecad-private-naming-source-probe.wasm" diff --git a/scripts/build-qt6-wasm-core.sh b/scripts/build-qt6-wasm-core.sh new file mode 100755 index 0000000..dacc205 --- /dev/null +++ b/scripts/build-qt6-wasm-core.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +QT_VERSION="6.8.2" +QT_ARCHIVE="qtbase-everywhere-src-${QT_VERSION}.tar.xz" +QT_ARCHIVE_SHA256="012043ce6d411e6e8a91fdc4e05e6bedcfa10fcb1347d3c33908f7fdd10dfe05" +QT_CACHE_DIR="${QT6_WASM_CACHE_DIR:-${ROOT_DIR}/.cache/toolchains/qt6}" +QT_DOWNLOAD_DIR="${QT_CACHE_DIR}/downloads" +QT_SOURCE_DIR="${QT_CACHE_DIR}/src" +QT_BUILD_DIR="${QT_CACHE_DIR}/build-wasm" +QT_INSTALL_DIR="${QT_CACHE_DIR}/install-wasm" +EMSDK_SHIM_DIR="${QT_CACHE_DIR}/emsdk-shim" +JOBS="${JOBS:-$(nproc)}" + +if ! command -v emcc >/dev/null || ! command -v em++ >/dev/null; then + echo "Emscripten emcc/em++ are required." >&2 + exit 1 +fi +if [[ "$(emcc --version | sed -n '1s/.* \([0-9][0-9.]*\) .*/\1/p')" != "3.1.69" ]]; then + echo "Qt wasm Core must use the project-locked Emscripten 3.1.69 toolchain." >&2 + exit 1 +fi + +mkdir -p "${QT_DOWNLOAD_DIR}" "${QT_CACHE_DIR}" "${EMSDK_SHIM_DIR}/upstream" +if [[ ! -f "${QT_DOWNLOAD_DIR}/${QT_ARCHIVE}" ]]; then + if [[ "${WEB_FREECAD_OFFLINE:-0}" == "1" ]]; then + echo "Missing offline Qt source archive: ${QT_DOWNLOAD_DIR}/${QT_ARCHIVE}" >&2 + exit 1 + fi + QT_ARCHIVE_PART="${QT_DOWNLOAD_DIR}/${QT_ARCHIVE}.part" + curl -fL --retry 3 --retry-delay 2 \ + "https://mirrors.ustc.edu.cn/qtproject/archive/qt/6.8/${QT_VERSION}/submodules/${QT_ARCHIVE}" \ + -o "${QT_ARCHIVE_PART}" + printf '%s %s\n' "${QT_ARCHIVE_SHA256}" "${QT_ARCHIVE_PART}" | sha256sum -c - + mv "${QT_ARCHIVE_PART}" "${QT_DOWNLOAD_DIR}/${QT_ARCHIVE}" +fi +printf '%s %s\n' "${QT_ARCHIVE_SHA256}" "${QT_DOWNLOAD_DIR}/${QT_ARCHIVE}" | sha256sum -c - + +if [[ ! -x "${QT_SOURCE_DIR}/configure" ]]; then + mkdir -p "${QT_SOURCE_DIR}" + tar -xJf "${QT_DOWNLOAD_DIR}/${QT_ARCHIVE}" -C "${QT_SOURCE_DIR}" --strip-components=1 +fi + +printf '%s\n' "EMSCRIPTEN_ROOT = emsdk_path + '/upstream/emscripten'" > "${EMSDK_SHIM_DIR}/.emscripten" +ln -sfn /usr/share/emscripten "${EMSDK_SHIM_DIR}/upstream/emscripten" +ln -sfn "$(command -v emcc)" "${EMSDK_SHIM_DIR}/emcc" +ln -sfn "$(command -v em++)" "${EMSDK_SHIM_DIR}/em++" + +mkdir -p "${QT_BUILD_DIR}" "${QT_INSTALL_DIR}" +if [[ ! -f "${QT_BUILD_DIR}/build.ninja" ]]; then + ( + cd "${QT_BUILD_DIR}" + EMSDK="${EMSDK_SHIM_DIR}" "${QT_SOURCE_DIR}/configure" \ + -platform wasm-emscripten \ + -qt-host-path /usr \ + -prefix /qt \ + -extprefix "${QT_INSTALL_DIR}" \ + -static -release -optimize-size \ + -no-gui -no-widgets -no-dbus -no-feature-network -no-feature-thread \ + -nomake examples -nomake tests -nomake benchmarks -nomake manual-tests \ + -no-pch -no-sbom \ + -- -G Ninja \ + -DCMAKE_TOOLCHAIN_FILE=/usr/share/emscripten/cmake/Modules/Platform/Emscripten.cmake \ + -DCMAKE_BUILD_TYPE=Release \ + -DQT_BUILD_TOOLS_WHEN_CROSSCOMPILING=OFF \ + -DQT_FEATURE_wasm_simd128=OFF \ + -DQT_FEATURE_wasm_exceptions=OFF \ + -DQt6HostInfo_DIR=/usr/lib/x86_64-linux-gnu/cmake/Qt6HostInfo + ) +fi + +cmake --build "${QT_BUILD_DIR}" --target Core --parallel "${JOBS}" +cmake --install "${QT_BUILD_DIR}" --component Devel +cmake --install "${QT_BUILD_DIR}/src/corelib" + +for path in \ + "${QT_BUILD_DIR}/lib/libQt6Core.a" \ + "${QT_BUILD_DIR}/lib/libQt6BundledPcre2.a" \ + "${QT_BUILD_DIR}/lib/libQt6BundledZLIB.a"; do + if [[ ! -s "${path}" ]]; then + echo "Qt wasm Core output is missing: ${path}" >&2 + exit 1 + fi + cp -f "${path}" "${QT_INSTALL_DIR}/lib/$(basename "${path}")" +done +if [[ ! -s "${QT_INSTALL_DIR}/include/QtCore/QByteArray" ]]; then + echo "Qt wasm Core headers were not installed into ${QT_INSTALL_DIR}." >&2 + exit 1 +fi + +sha256sum \ + "${QT_DOWNLOAD_DIR}/${QT_ARCHIVE}" \ + "${QT_INSTALL_DIR}/lib/libQt6Core.a" \ + "${QT_INSTALL_DIR}/lib/libQt6BundledPcre2.a" \ + "${QT_INSTALL_DIR}/lib/libQt6BundledZLIB.a" diff --git a/scripts/check-freecad-private-naming-boundary.mjs b/scripts/check-freecad-private-naming-boundary.mjs new file mode 100644 index 0000000..88074ab --- /dev/null +++ b/scripts/check-freecad-private-naming-boundary.mjs @@ -0,0 +1,31 @@ +import { readFile } from 'node:fs/promises' +import { resolve } from 'node:path' + +const root = resolve(new URL('..', import.meta.url).pathname) +const load = async (path) => JSON.parse(await readFile(resolve(root, path), 'utf8')) +const [abi, matrix, plan] = await Promise.all([ + load('config/freecad-sketcher-partdesign-abi-contract.json'), + load('config/compatibility-matrix.json'), + load('config/freecad-web-exact-parity-plan.json'), +]) +const fail = (message) => { throw new Error(`FreeCAD private naming boundary: ${message}`) } + +const tasks = plan.programs.flatMap((program) => program.tasks) +const task = tasks.find((entry) => entry.id === 'EX-TSN-02') +if (!task) fail('EX-TSN-02 is missing from the exact parity plan.') +if (abi.privateNamingAbi?.shippedWorkerImplementation !== 'not-linked') fail('the shipped ABI contract must remain not-linked until the production artifact exports verified callbacks.') +if (abi.claim?.exactFreeCadParity !== false) fail('the supported facade contract cannot claim exact FreeCAD parity.') +if (matrix.nativeOcctHistory?.freecadNamingBuild?.status !== 'contract-only') fail('the FreeCAD naming build must remain contract-only before a locked SDK and callback probe exist.') +if (task.status !== 'in_progress') fail(`EX-TSN-02 must remain in_progress, received ${String(task.status)}.`) +if (matrix.systemExactEvaluation?.exact !== false) fail('systemExact must remain false.') +const blockers = matrix.systemExactEvaluation?.blockers ?? [] +if (!blockers.includes('shipped Worker lacks the FreeCAD-linked private naming ABI implementation')) fail('systemExact blockers must retain the missing FreeCAD-linked Worker implementation.') +if (!task.exactBlockedBy?.includes('The shipped Worker does not link the FreeCAD private naming implementation for every builder')) fail('EX-TSN-02 must retain its production Worker linkage blocker.') + +console.log(JSON.stringify({ + status: 'boundary-pass', + shippedWorkerImplementation: 'not-linked', + task: { id: task.id, status: task.status }, + systemExact: matrix.systemExactEvaluation.exact, + sdkStatus: matrix.nativeOcctHistory.freecadNamingBuild.status, +}, null, 2)) diff --git a/scripts/offline-resource-lib.mjs b/scripts/offline-resource-lib.mjs index 7212845..bbe1ad9 100644 --- a/scripts/offline-resource-lib.mjs +++ b/scripts/offline-resource-lib.mjs @@ -607,7 +607,7 @@ async function smoke() { FREECAD_SOURCE_OFFLINE: '1', OCCT_SOURCE_DIR: resolve(root, '.cache/occt/occt'), } - for (const script of ['check:runtime', 'check:freecad-source', 'check:occt-history-artifact', 'test:occt-history', 'test:planegcs', 'build']) { + for (const script of ['check:runtime', 'check:freecad-source', 'check:freecad-private-naming-boundary', 'check:occt-history-artifact', 'test:occt-history', 'test:planegcs', 'build']) { print(`[offline:smoke] npm run ${script}`) await run(resolve(root, 'npmw'), ['run', script], { env: environment }) } diff --git a/scripts/run-real-verification.mjs b/scripts/run-real-verification.mjs index b04ef58..12cf7bb 100644 --- a/scripts/run-real-verification.mjs +++ b/scripts/run-real-verification.mjs @@ -81,6 +81,7 @@ const lanes = { wasm: [ ...wasmBuilds, 'check:occt-history-artifact', + 'check:freecad-private-naming-boundary', 'test:browser-occt', 'check:browser-occt', 'test:planegcs', diff --git a/tests/freecadPrivateNamingBoundary.test.mjs b/tests/freecadPrivateNamingBoundary.test.mjs new file mode 100644 index 0000000..32dcbb4 --- /dev/null +++ b/tests/freecadPrivateNamingBoundary.test.mjs @@ -0,0 +1,42 @@ +import test from 'node:test' +import assert from 'node:assert/strict' +import { readFile } from 'node:fs/promises' +import { resolve } from 'node:path' + +const root = resolve(new URL('..', import.meta.url).pathname) + +test('Qt wasm Core build pins the released QtBase source and project Emscripten baseline', async () => { + const source = await readFile(resolve(root, 'scripts/build-qt6-wasm-core.sh'), 'utf8') + assert.match(source, /QT_VERSION="6\.8\.2"/) + assert.match(source, /012043ce6d411e6e8a91fdc4e05e6bedcfa10fcb1347d3c33908f7fdd10dfe05/) + assert.match(source, /Emscripten 3\.1\.69/) + assert.match(source, /mirrors\.ustc\.edu\.cn/) + assert.match(source, /WEB_FREECAD_OFFLINE/) + assert.match(source, /cmake --install "\$\{QT_BUILD_DIR\}\/src\/corelib"/) + assert.doesNotMatch(source, /cmake --install[^\n]+\|\| true/) +}) + +test('FreeCAD source probe pins the exact private source inputs and does not export the production ABI', async () => { + const build = await readFile(resolve(root, 'scripts/build-freecad-naming-source-probe.sh'), 'utf8') + const smoke = await readFile(resolve(root, 'native/freecad-naming-probe/smoke-test.mjs'), 'utf8') + assert.match(build, /0108fd4b4850cc46e625b60e53cea7a7bbe69f8d/) + assert.match(build, /73e0e60a9d6ee06851252e2071ebdd58698f8f99733903232529b3a194164435/) + assert.match(build, /90173aba5f331ac9453589833f7e5fc63c5c80a35f1600dbc71211606b1c9dbc/) + assert.doesNotMatch(build, /public\/native\/freecad-naming-probe/) + for (const callback of ['freecadNamingAbiVersion', 'freecadNamingCapabilitiesJson', 'freecadNamingEvidenceJson']) { + assert.match(smoke, new RegExp(callback)) + } + assert.match(smoke, /productionWorkerLinked: false/) +}) + +test('private naming boundary checker cross-validates all authoritative status files', async () => { + const source = await readFile(resolve(root, 'scripts/check-freecad-private-naming-boundary.mjs'), 'utf8') + for (const path of [ + 'config/freecad-sketcher-partdesign-abi-contract.json', + 'config/compatibility-matrix.json', + 'config/freecad-web-exact-parity-plan.json', + ]) assert.match(source, new RegExp(path.replaceAll('.', '\\.'))) + assert.match(source, /shippedWorkerImplementation !== 'not-linked'/) + assert.match(source, /task\.status !== 'in_progress'/) + assert.match(source, /systemExactEvaluation\?\.exact !== false/) +}) diff --git a/tests/offlineResourceLibrary.test.mjs b/tests/offlineResourceLibrary.test.mjs index 18ebe9b..532c234 100644 --- a/tests/offlineResourceLibrary.test.mjs +++ b/tests/offlineResourceLibrary.test.mjs @@ -39,6 +39,9 @@ test('offline profile covers runtime, native sources, browsers and host recovery 'linuxcnc-worktree', 'occt-history-dist', 'planegcs-dist', + 'qt6-wasm-core-downloads', + 'qt6-wasm-core-source', + 'qt6-wasm-core-sdk', 'playwright-browsers', 'debian-package-cache', ]) assert.ok(ids.has(id), id)