From 0cf56b64d2711947ee7ea40a5a7f47fdb13e4521 Mon Sep 17 00:00:00 2001 From: Harvey Zhao Date: Mon, 14 Sep 2026 00:42:47 +0800 Subject: [PATCH] refactor(jassub): [PKG-JASSUB-01] pin exact nightly and font provenance --- package.json | 2 +- .../artplayer-plugin-jassub/ARCHITECTURE.md | 18 +- refactor/baselines/jassub-contract.md | 12 +- .../jassub-provenance-validation.json | 103 +++++ refactor/baselines/jassub-provenance.json | 379 ++++++++++++++++++ .../2026-09-14-PKG-JASSUB-01-provenance.md | 64 +++ refactor/plan.md | 8 +- refactor/progress.md | 9 + refactor/risk-table.md | 4 +- refactor/risks.json | 16 +- refactor/scripts/jassub-provenance.test.mjs | 182 +++++++++ refactor/tasks.json | 11 +- refactor/third-party.json | 27 +- 13 files changed, 802 insertions(+), 33 deletions(-) create mode 100644 refactor/baselines/jassub-provenance-validation.json create mode 100644 refactor/baselines/jassub-provenance.json create mode 100644 refactor/changes/2026-09-14-PKG-JASSUB-01-provenance.md create mode 100644 refactor/scripts/jassub-provenance.test.mjs diff --git a/package.json b/package.json index b975ecd08..36fc168e3 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "test:vtt-thumbnail-types-package": "node refactor/scripts/vtt-thumbnail-package-types.mjs", "test:multiple-subtitles": "node --test test/multiple-subtitles-merge.test.js test/multiple-subtitles-lifecycle.test.js test/multiple-subtitles-failures.test.js test/multiple-subtitles.test.js test/multiple-subtitles-vendor.test.js refactor/scripts/multiple-subtitles-contract.test.mjs refactor/scripts/multiple-subtitles-runtime-types.test.mjs refactor/scripts/multiple-subtitles-types.test.mjs", "test:multiple-subtitles-types-package": "node refactor/scripts/multiple-subtitles-package-types.mjs", - "test:jassub": "node --test test/jassub.test.js refactor/scripts/jassub-contract.test.mjs", + "test:jassub": "node --test test/jassub.test.js refactor/scripts/jassub-contract.test.mjs refactor/scripts/jassub-provenance.test.mjs", "check:ci": "node refactor/scripts/ci-workflow.mjs", "test:ci": "node --test test/ci-summary.test.js refactor/scripts/ci-workflow.test.mjs refactor/scripts/impact.test.mjs test/package-runtime.test.js", "test:package:runtime": "node scripts/package-runtime.mjs --canonical", diff --git a/packages/artplayer-plugin-jassub/ARCHITECTURE.md b/packages/artplayer-plugin-jassub/ARCHITECTURE.md index 0eafc20b2..b48aa5089 100644 --- a/packages/artplayer-plugin-jassub/ARCHITECTURE.md +++ b/packages/artplayer-plugin-jassub/ARCHITECTURE.md @@ -17,20 +17,28 @@ resize is width/height/top/left/force. Keep the existing declarations until the type compatibility work has assessed extraction, callbacks and replacement consumers. Both option and instance expose extension indexes; they are existing compatibility boundaries. -Provenance is incomplete. The wrapper matches upstream jassub 1.8.8 apart from formatting and -the ESLint header; worker JS and default font match that archive byte-for-byte. Local WASM -files validate but differ from its build. Other font metadata includes different source and -license clues; do not infer redistribution rights from family names or embedding flags. -Task PKG-JASSUB-01 and VENDOR-04/05 remain open. Full original acquisition history is unknown. +The wrapper matches upstream jassub 1.8.8 apart from formatting and the ESLint header; +worker JS and default font match that archive byte-for-byte. Local WASM instead matches +the exact Pages nightly blobs associated with source 6b19a04ddfbad8f9bfd3237395788dd76218841b. +Its build workflow and seven submodule revisions are pinned in the separate +refactor/baselines/jassub-provenance.json supplement. All 11 demo font blobs also match +the historical Pages tree. Do not replace the binaries just to match the npm version. +The build has not been independently reproduced. Complete component/open-font notices +and six unclear font redistribution permissions remain VENDOR-04/05 gates in +PKG-JASSUB-06 / SITE-01. Source identity permits owned adapter work to proceed after 01; +it does not authorize redistribution or prove original acquisition history. Use the pinned Node/Yarn toolchain: ```sh yarn test:jassub +node refactor/scripts/jassub-provenance.test.mjs --network yarn build artplayer-plugin-jassub ``` The baseline runner verifies both actual published packages and frozen workspace inputs. +Provenance tests are offline by default; explicit --network refreshes pinned source and +asset comparisons and fails on any request error, without substituting cached evidence. Set ARTPLAYER_JASSUB_CANDIDATE=1 for current source behavior; ARTPLAYER_JASSUB_ARTIFACT can point at a main/legacy artifact. Tests use controlled DOM, Worker and SIMD detection, with actual vendor JavaScript. They do not render ASS or execute worker WASM. Real browser, failures, diff --git a/refactor/baselines/jassub-contract.md b/refactor/baselines/jassub-contract.md index 475a3a68f..52fcc1c1f 100644 --- a/refactor/baselines/jassub-contract.md +++ b/refactor/baselines/jassub-contract.md @@ -1,7 +1,7 @@ # JASSUB 历史契约与来源检查点 -PKG-JASSUB-01 仍为 doing。本记录冻结实现事实,不将尚未确认的 WASM/字体来源或设备 -验证标成完成。输入见 [发布清单](jassub-release.json)、[上游比较源](jassub-vendor.json) +PKG-JASSUB-01 的来源与契约核对已完成;下面保留首次检查事实,并以末尾来源补充 +更新结论。完整许可通知、字体分发和设备验证未完成。输入见 [发布清单](jassub-release.json)、[上游比较源](jassub-vendor.json) 和 [字体内嵌元数据](jassub-font-metadata.json)。 ## 来源与分发 @@ -73,4 +73,10 @@ art.destroy 叠加、构造失败清理、custom canvas 等边界仍归 02/03; 48 项包含七份实现的 42 个受控行为和六项发布/vendor/资源/字体/真实 ESM 检查。 所有输入均校验实际 tarball 或 Git 哈希。Worker/DOM/SIMD 为受控对象,不能计为真实 ASS 绘制、WASM 初始化、字体外观、视频时钟/seek/倍率同步或完整浏览器验收。 -下一步继续 WASM 构建及字体授权/通知来源核对;02 再补异常/资源与实际渲染失败基线。 +来源补充已确认两份 WASM 来自 Pages nightly 的精确 Git blob,并关联源码 +6b19a04ddfbad8f9bfd3237395788dd76218841b、构建工作流和七个子模块;11 份字体也 +与历史 Pages 字节一致。与 npm 1.8.8 WASM 不同的历史测试继续保留。 +见 [独立来源数据](jassub-provenance.json) 和 +[补充记录](../changes/2026-09-14-PKG-JASSUB-01-provenance.md)。原取得操作和独立 +WASM 重建未证明。01 完成不关闭 VENDOR-04/05:完整库/字体通知与六份未明确 +字体的发布处理由 06/SITE-01 继续;02 开始异常/资源与实际渲染失败基线。 diff --git a/refactor/baselines/jassub-provenance-validation.json b/refactor/baselines/jassub-provenance-validation.json new file mode 100644 index 000000000..75e79006a --- /dev/null +++ b/refactor/baselines/jassub-provenance-validation.json @@ -0,0 +1,103 @@ +{ + "task": "PKG-JASSUB-01", + "stage": "contract-and-source-identification", + "node": "v24.21.0", + "platform": "win32", + "baseline": { + "file": "refactor/baselines/jassub-provenance.json", + "sha256": "4c68b588069b69b350f0cccfa84494c33d72841341f28296b64aa03bfaf59ea2" + }, + "verifier": { + "file": "refactor/scripts/jassub-provenance.test.mjs", + "sha256": "ed6db8a93a50229143af0ebcaa59578ba17575d79068a10317a22495c3865cc5" + }, + "checks": [ + { + "name": "historical-and-provenance", + "log": "refactor/.cache/jassub01-provenance-tests-final.log", + "passed": 50, + "file": "refactor/.cache/jassub01-provenance-tests-final.log", + "sha256": "433c97790a274a79ec3d43a7c86bc9115819102fd95a39b0fa332cdaa860a9f3", + "failed": 0, + "skipped": 0 + }, + { + "name": "saved-evidence-recheck", + "log": "refactor/.cache/jassub01-provenance-cache-final.log", + "passed": 3, + "file": "refactor/.cache/jassub01-provenance-cache-final.log", + "sha256": "c19f9b37e5160f44a0474b78a963d458fc9b2db0fca54529f0b29faab7fd2260", + "failed": 0, + "skipped": 0 + }, + { + "name": "fresh-network", + "log": "refactor/.cache/jassub01-provenance-network-final.log", + "passed": 3, + "successfulRequests": 23, + "comparedAssets": 13, + "file": "refactor/.cache/jassub01-provenance-network-final.log", + "sha256": "3a25fd1b397c19731dc009b5511a397609a78e086330a0cc218b93e60b39863b", + "failed": 0, + "skipped": 0 + } + ], + "freshNetworkCompletedAt": "2026-09-13T16:38:47.774Z", + "unchangedFrozenBaselines": [ + { + "file": "refactor/baselines/jassub-release.json", + "sha256": "b97edb655e7deb5b11b4d69b69291cc8a78abb791f00ddceb3cc9b25a7113925" + }, + { + "file": "refactor/baselines/jassub-vendor.json", + "sha256": "a04f2e65a4ad250e964ecc03712848989c7eb63953dfbc08808d71768977c4b4" + }, + { + "file": "refactor/baselines/jassub-font-metadata.json", + "sha256": "6f110c76f204081daa61a20127dd974fbcc476a87882bee195348239e1b85128" + } + ], + "sourceFacts": { + "uniqueAssets": 13, + "localPathsIncludingAliases": 16, + "wasmBuildSource": "6b19a04ddfbad8f9bfd3237395788dd76218841b", + "wasmPagesCommit": "747e392d7f6fe7c38bbcdfe05c9ada46b0b62daf", + "submodules": 7, + "pinnedBuildFiles": 3, + "noticesWithExactBytes": 2 + }, + "additionalDraftChecks": { + "scope": "Subagent cache draft checks before integration; not native/browser tests.", + "logs": [ + { + "file": "refactor/.cache/jassub-provenance-offline-verification.log", + "sha256": "56accc72ee45a4eed7d53cb044b4ba2e840b1f1732a415e1036435238f323118" + }, + { + "file": "refactor/.cache/jassub-provenance-negative-git-blob.log", + "sha256": "9fcb8ee4912041582ce46656526365a77c227f14095ec5822a684571b1b9cb8e" + }, + { + "file": "refactor/.cache/jassub-provenance-negative-source-link.log", + "sha256": "b2215b821a624a1f6626e07c0a19f54fa080cd7d2bdd159752ae1732d7dbffb3" + }, + { + "file": "refactor/.cache/jassub-provenance-network-fail-closed.log", + "sha256": "ca15852342099ed174e6d52fb1f65bd7c805dfad39403c3c9784a4ea4bfa66c1" + } + ], + "outcomes": [ + "Offline saved-evidence run passed with fetch prohibited.", + "Corrupt Git blob and source-link metadata each exited 1.", + "Explicit network mode exited 1 when fetch was prohibited; no cache fallback." + ] + }, + "sourceOrApiChanges": false, + "nativeBrowserValidation": false, + "independentWasmRebuild": false, + "remaining": { + "VENDOR-04": "PKG-JASSUB-06: complete component notices and linked-library source/distribution review", + "VENDOR-05": "PKG-JASSUB-06 / SITE-01: five open-font notices and six unresolved font permissions or reviewed alternatives", + "runtime": "PKG-JASSUB-02/03/04/05/06 remain pending" + } +} diff --git a/refactor/baselines/jassub-provenance.json b/refactor/baselines/jassub-provenance.json new file mode 100644 index 000000000..1cfc181b5 --- /dev/null +++ b/refactor/baselines/jassub-provenance.json @@ -0,0 +1,379 @@ +{ + "schemaVersion": 1, + "kind": "append-only-provenance-supplement", + "capturedAt": "2026-09-13T16:32:55.277Z", + "baseline": "refactor/baselines/jassub-release.json", + "vendorComparison": "refactor/baselines/jassub-vendor.json", + "upstream": { + "repository": "https://github.com/ThaUnknown/jassub", + "api": "https://api.github.com/repos/ThaUnknown/jassub", + "localIntroduction": "f4d99eb09ecd294c676d3bb89902c2ea06fa7990", + "pagesSnapshot": { + "commit": "25d1ea7ca1f827dbed2c8ea539c9bc917ccbfab1", + "sourceCommit": "ca27d60a672612b2b8a80d0728092c2164e32a07", + "message": "Update binaries to latest nightly\n\nFrom ca27d60a672612b2b8a80d0728092c2164e32a07", + "changedPaths": [ + "jassub/assets/jassub-worker.js" + ], + "cachedCommit": "jassub01-demo-commit.json", + "cachedTree": "jassub01-historical-demo-tree.json", + "tree": "25d1ea7ca1f827dbed2c8ea539c9bc917ccbfab1" + }, + "wasmNightly": { + "commit": "747e392d7f6fe7c38bbcdfe05c9ada46b0b62daf", + "sourceCommit": "6b19a04ddfbad8f9bfd3237395788dd76218841b", + "message": "Update binaries to latest nightly\n\nFrom 6b19a04ddfbad8f9bfd3237395788dd76218841b", + "cachedCommit": "jassub01-wasm-nightly-commit.txt" + }, + "build": { + "sourceCommit": "6b19a04ddfbad8f9bfd3237395788dd76218841b", + "cachedTree": "jassub01-6b19a04ddfbad8f9bfd3237395788dd76218841b.json", + "emsdkImage": "docker.io/emscripten/emsdk:4.0.22", + "artifactName": "js", + "artifactSource": "dist", + "artifactDestination": "jassub/assets", + "evidence": [ + { + "path": "Dockerfile", + "commit": "6b19a04ddfbad8f9bfd3237395788dd76218841b", + "bytes": 734, + "sha256": "a31a538d5233eed5bc93ac4dd695397b15e05124ecf5314fbfc78e4e644ef2a3", + "gitBlob": "812ce6a20d53a0f6b9b7a25b6faaef17ea97cada", + "cachedDownload": "jassub01-source-Dockerfile.txt" + }, + { + "path": ".gitmodules", + "commit": "6b19a04ddfbad8f9bfd3237395788dd76218841b", + "bytes": 790, + "sha256": "d8d1dbf29bef736d2f6ed13fd26bed06c8de7ed5691becab072d01d2d634a62c", + "gitBlob": "d748f813634ed83be56feb1702626411b9d124d6", + "cachedDownload": "jassub01-source-gitmodules.txt" + }, + { + "path": ".github/workflows/emscripten.yml", + "commit": "6b19a04ddfbad8f9bfd3237395788dd76218841b", + "bytes": 1684, + "sha256": "ab427152acf8bfbf9059c69d9fe5b7386b9eb38ec3c3213309857ac8dc7d965a", + "gitBlob": "cdc72dd411af7f71fc6e94531a1e9706205ca977", + "cachedDownload": "jassub01-source-workflow.yml" + } + ], + "submodules": [ + { + "path": "lib/brotli", + "commit": "e61745a6b7add50d380cfd7d3883dd6c62fc2c71" + }, + { + "path": "lib/expat", + "commit": "27d5b8ba1771f916d9cfea2aac6bdac72071dc66" + }, + { + "path": "lib/fontconfig", + "commit": "d37f97223a4715176c0889e91685dc796457e25f" + }, + { + "path": "lib/freetype", + "commit": "801cd842e27c85cb1d5000f6397f382ffe295daa" + }, + { + "path": "lib/fribidi", + "commit": "247fddc3599e3fe7b1b5cc21020c9eb51e662637" + }, + { + "path": "lib/harfbuzz", + "commit": "afcae83a064843d71d47624bc162e121cc56c08b" + }, + { + "path": "lib/libass", + "commit": "695509365f152bd28720a0c0e036d46836ee9345" + } + ], + "reproduced": false, + "limitations": [ + "Docker tag, apt dependencies and npm install are not fully locked.", + "Checked-in source dist bytes differ from the generated Pages nightly artifacts." + ], + "tree": "6b19a04ddfbad8f9bfd3237395788dd76218841b" + } + }, + "assets": [ + { + "kind": "wasm", + "file": "docs/assets/jassub/jassub-worker.wasm", + "aliases": [ + "packages/artplayer-plugin-jassub/worker/jassub-worker.wasm" + ], + "bytes": 1969100, + "sha256": "73880d99ba9260ac39fc4476491cf254ca784c76f3af2fb03f3f9be8065ad3c0", + "gitBlob": "527ccdcaec1fbbed3066525651d8e8970b8373db", + "upstream": { + "commit": "25d1ea7ca1f827dbed2c8ea539c9bc917ccbfab1", + "paths": [ + "jassub/assets/jassub-worker.wasm", + "jassub/assets/js/jassub-worker.wasm" + ] + }, + "cachedDownload": "jassub01-upstream-527ccdcaec1fbbed3066525651d8e8970b8373db.bin" + }, + { + "kind": "wasm", + "file": "docs/assets/jassub/jassub-worker-modern.wasm", + "aliases": [ + "packages/artplayer-plugin-jassub/worker/jassub-worker-modern.wasm" + ], + "bytes": 2037191, + "sha256": "8a8b37463713d1f2ddbc1fbb8a35889d9565e5a6670851758b46b2216d8e513c", + "gitBlob": "6f7fcbb4b5a016054bef1c6e2137dda65e97347a", + "upstream": { + "commit": "25d1ea7ca1f827dbed2c8ea539c9bc917ccbfab1", + "paths": [ + "jassub/assets/jassub-worker-modern.wasm", + "jassub/assets/js/jassub-worker-modern.wasm" + ] + }, + "cachedDownload": "jassub01-upstream-6f7fcbb4b5a016054bef1c6e2137dda65e97347a.bin" + }, + { + "kind": "font", + "file": "docs/assets/jassub/fonts/allison-script.regular.otf", + "aliases": [], + "bytes": 107896, + "sha256": "97cd66bb6f89bdd32af2a9592f439536c85aa7553516514e2623bb1e81c65bd6", + "gitBlob": "0bfc60accfb3ac9cef6cbf488d3ad9f22574ee5b", + "upstream": { + "commit": "25d1ea7ca1f827dbed2c8ea539c9bc917ccbfab1", + "paths": [ + "fonts/allison-script.regular.otf" + ] + }, + "cachedDownload": "jassub01-upstream-0bfc60accfb3ac9cef6cbf488d3ad9f22574ee5b.bin" + }, + { + "kind": "font", + "file": "docs/assets/jassub/fonts/architext.regular.ttf", + "aliases": [], + "bytes": 30568, + "sha256": "a6b8e33ce190ee9c871590b982cff37e137b303bb73dcab55ef3ff7154ba143a", + "gitBlob": "47791099b9e86b6e9b8d5d216f9a5c27aed39b98", + "upstream": { + "commit": "25d1ea7ca1f827dbed2c8ea539c9bc917ccbfab1", + "paths": [ + "fonts/architext.regular.ttf" + ] + }, + "cachedDownload": "jassub01-upstream-47791099b9e86b6e9b8d5d216f9a5c27aed39b98.bin" + }, + { + "kind": "font", + "file": "docs/assets/jassub/fonts/arial.ttf", + "aliases": [], + "bytes": 1036584, + "sha256": "c9b76220a5be42ead4733611e417cd65c5fd8aeaa33eb56576ac378a37d130a1", + "gitBlob": "8682d94623e575a4ecc9586a35fc909dff37fb2c", + "upstream": { + "commit": "25d1ea7ca1f827dbed2c8ea539c9bc917ccbfab1", + "paths": [ + "fonts/arial.ttf" + ] + }, + "cachedDownload": "jassub01-upstream-8682d94623e575a4ecc9586a35fc909dff37fb2c.bin" + }, + { + "kind": "font", + "file": "docs/assets/jassub/fonts/Averia Sans Libre Light.ttf", + "aliases": [], + "bytes": 106684, + "sha256": "df4ca526f9a941afda1f9c5a275695beb066a531f5b008c1e28ddc2c5f6e4390", + "gitBlob": "2c15bdc1b5c88b659b6435dfb3ac32b78d32a3e3", + "upstream": { + "commit": "25d1ea7ca1f827dbed2c8ea539c9bc917ccbfab1", + "paths": [ + "fonts/Averia Sans Libre Light.ttf" + ] + }, + "cachedDownload": "jassub01-upstream-2c15bdc1b5c88b659b6435dfb3ac32b78d32a3e3.bin" + }, + { + "kind": "font", + "file": "docs/assets/jassub/fonts/Averia Serif Simple Light.ttf", + "aliases": [], + "bytes": 110612, + "sha256": "fb6398238298a7010920e1c216f7b29bc920477bc810f8bcc677ae4e6f411b4b", + "gitBlob": "6e1d0952e4dc6140cc32dbc4d9790d04b91dcced", + "upstream": { + "commit": "25d1ea7ca1f827dbed2c8ea539c9bc917ccbfab1", + "paths": [ + "fonts/Averia Serif Simple Light.ttf" + ] + }, + "cachedDownload": "jassub01-upstream-6e1d0952e4dc6140cc32dbc4d9790d04b91dcced.bin" + }, + { + "kind": "font", + "file": "docs/assets/jassub/fonts/chawp.otf", + "aliases": [], + "bytes": 122620, + "sha256": "9107241585a4328ce1438dfeabbfb1a8d0cd513dab6902be9a1a5774eab607c6", + "gitBlob": "8cceee5183cb068903a5f65c63c64deddc74e8d7", + "upstream": { + "commit": "25d1ea7ca1f827dbed2c8ea539c9bc917ccbfab1", + "paths": [ + "fonts/chawp.otf" + ] + }, + "cachedDownload": "jassub01-upstream-8cceee5183cb068903a5f65c63c64deddc74e8d7.bin" + }, + { + "kind": "font", + "file": "docs/assets/jassub/fonts/default.woff2", + "aliases": [ + "docs/assets/jassub/default.woff2" + ], + "bytes": 145972, + "sha256": "886929903707c5bb28b07cd2eed69921b3d5ef5c49c73a0dd1e187ebf6546a4c", + "gitBlob": "a562391696ad477e5a8ad28ef8b4d53c90bb0d42", + "upstream": { + "commit": "25d1ea7ca1f827dbed2c8ea539c9bc917ccbfab1", + "paths": [ + "fonts/default.woff2", + "jassub/assets/default.woff2" + ] + }, + "cachedDownload": "jassub01-upstream-a562391696ad477e5a8ad28ef8b4d53c90bb0d42.bin" + }, + { + "kind": "font", + "file": "docs/assets/jassub/fonts/FRABK.TTF", + "aliases": [], + "bytes": 152700, + "sha256": "9ad3d0e5ef31c4a9a98cb0e169e4e625286aa34c712add3e001c0100138730d4", + "gitBlob": "21c4ecfc553e9fa94ac0c01368ff657f48493ca1", + "upstream": { + "commit": "25d1ea7ca1f827dbed2c8ea539c9bc917ccbfab1", + "paths": [ + "fonts/FRABK.TTF" + ] + }, + "cachedDownload": "jassub01-upstream-21c4ecfc553e9fa94ac0c01368ff657f48493ca1.bin" + }, + { + "kind": "font", + "file": "docs/assets/jassub/fonts/Gramond.ttf", + "aliases": [], + "bytes": 173484, + "sha256": "072859788bc577d35df2e852225c52cc5670339a4859d86b5edb96b1724a2177", + "gitBlob": "980f0fe8f8c88bb45f6282b8cf8d23929fb89a23", + "upstream": { + "commit": "25d1ea7ca1f827dbed2c8ea539c9bc917ccbfab1", + "paths": [ + "fonts/Gramond.ttf" + ] + }, + "cachedDownload": "jassub01-upstream-980f0fe8f8c88bb45f6282b8cf8d23929fb89a23.bin" + }, + { + "kind": "font", + "file": "docs/assets/jassub/fonts/Lato-Regular.ttf", + "aliases": [], + "bytes": 657212, + "sha256": "6f6940be0835c3ddec9199e5fc42be4cbc61ebcfd58c623fdf719366253f1780", + "gitBlob": "adbfc467d2d0dd5cdf2b942e5d0cf3aab2b73c3a", + "upstream": { + "commit": "25d1ea7ca1f827dbed2c8ea539c9bc917ccbfab1", + "paths": [ + "fonts/Lato-Regular.ttf" + ] + }, + "cachedDownload": "jassub01-upstream-adbfc467d2d0dd5cdf2b942e5d0cf3aab2b73c3a.bin" + }, + { + "kind": "font", + "file": "docs/assets/jassub/fonts/SlatePro-Medium.otf", + "aliases": [], + "bytes": 80944, + "sha256": "56aa0fc2878bf782c10e5bba6dc45de1982a572d14ba9e10cd9d5a4414b6171e", + "gitBlob": "067b5cb5cbc05045586a4b0a84595c6d132a62a6", + "upstream": { + "commit": "25d1ea7ca1f827dbed2c8ea539c9bc917ccbfab1", + "paths": [ + "fonts/SlatePro-Medium.otf" + ] + }, + "cachedDownload": "jassub01-upstream-067b5cb5cbc05045586a4b0a84595c6d132a62a6.bin" + } + ], + "notices": [ + { + "path": "LICENSE", + "commit": "6b19a04ddfbad8f9bfd3237395788dd76218841b", + "bytes": 1145, + "sha256": "b3fbc65487e4b505288c5fca0d86d200e2dc1c7bb33a1476e625c4f0379e598c", + "gitBlob": "c046b62eeab3818fced95e6507a0cfddcf92732d", + "cachedDownload": "jassub01-license-raw.bin", + "npm": { + "version": "1.8.8", + "member": "package/LICENSE", + "bytes": 1167, + "sha256": "f300758600ecf59b736407f80f6dc8338a9879a062c6bcb16cc739d14814ec75", + "cachedDownload": "jassub01-license-npm.bin", + "comparison": "different-bytes-equal-after-crlf-to-lf" + } + }, + { + "path": "jassub/assets/COPYRIGHT", + "commit": "747e392d7f6fe7c38bbcdfe05c9ada46b0b62daf", + "bytes": 69206, + "sha256": "a921cd41f0f3d715200874319586c970072751dcadaa3237c4fd648452f819d6", + "gitBlob": "3e35c600fee96299f8a029849a3dd40c9d63bdf2", + "cachedDownload": "jassub01-copyright-raw.bin", + "npm": { + "version": "1.8.8", + "member": "package/dist/COPYRIGHT", + "bytes": 70157, + "sha256": "420f27cc7703563bb12842b10244f4a4951dc8bbcc5456050e696db96533edfb", + "cachedDownload": "jassub01-copyright-npm.bin", + "comparison": "different-bytes-equal-after-crlf-to-lf" + } + } + ], + "licensing": { + "redistributionApproval": false, + "completeNoticeAudit": false, + "componentNoticeAssembly": "pending", + "fullPinnedFreeTypeLicense": "pending", + "lgplComponent": "lib/fribidi", + "fontSourceIdentity": "all-11-font-files-byte-matched", + "openFontNoticeAssembly": [ + "default.woff2", + "Averia Sans Libre Light.ttf", + "Averia Serif Simple Light.ttf", + "Lato-Regular.ttf", + "chawp.otf" + ], + "unresolvedFontPermission": [ + "allison-script.regular.otf", + "architext.regular.ttf", + "arial.ttf", + "FRABK.TTF", + "Gramond.ttf", + "SlatePro-Medium.otf" + ], + "limitations": [ + "Public upstream hosting proves object identity, not redistribution authorization.", + "Embedded font metadata and fsType are not purchase or redistribution evidence.", + "The Arial embedded MIT text covers Hebrew layout logic only.", + "The autogenerated component COPYRIGHT contains noisy extracted text and links to the full FTL." + ] + }, + "validation": { + "offline": "Only recomputes local file hashes and checks frozen metadata. No current remote claim.", + "cachedEvidence": "Explicit --cached-evidence rechecks previously downloaded bytes and saved upstream metadata. No fresh network claim.", + "network": "Explicit --network fetches pinned upstream trees, commits and blobs; failures fail the test and never fall back to cache.", + "previousObservation": { + "downloadedAssets": 13, + "exactByteMatches": 13, + "sourceOfEvidence": "GitHub official Git blob API, base64-decoded bytes" + }, + "browserValidated": false + } +} diff --git a/refactor/changes/2026-09-14-PKG-JASSUB-01-provenance.md b/refactor/changes/2026-09-14-PKG-JASSUB-01-provenance.md new file mode 100644 index 000000000..fd04c31d9 --- /dev/null +++ b/refactor/changes/2026-09-14-PKG-JASSUB-01-provenance.md @@ -0,0 +1,64 @@ +# PKG-JASSUB-01 来源补充与契约核对完成 + +本次完成来源与契约的核对步骤,未替换 vendor、WASM、字体或公开声明。原始 +release/vendor/font 基线保持不变;新增独立 provenance 补充,后续源码迁移可以 +基于准确的第三方边界继续。完整许可通知和未明确的字体再分发条件仍为发布门槛。 + +## 从本地字节到上游源代码 + +两份 WASM 与 11 份字体均逐字节匹配 JASSUB 历史 Pages 中的 Git blob。包括包内 +WASM 两份副本、docs 默认字体副本在内,本地 16 路径均与原基线 SHA 保持一致。 +检验同时计算文件长度、SHA-256 和带 blob header 的 Git SHA-1,不使用文件名猜版本。 + +WASM 发布提交 `747e392d7f6fe7c38bbcdfe05c9ada46b0b62daf` 明确引用源码 +`6b19a04ddfbad8f9bfd3237395788dd76218841b`。该源码使用 emsdk 4.0.22、递归子模块、 +普通与 MODERN 构建,工作流将 dist artifact 放入 Pages 的 jassub/assets。七个 +子模块 revision 及三个构建文件的 Git blob/内容已固定。其 checked-in dist +与 nightly 产物不同,必须保留 Pages 的精确 blob,不能只给源码目录链接。 + +本地引入前的 Pages 快照 `25d1ea7ca1f827dbed2c8ea539c9bc917ccbfab1` 后来只更新 +worker JS,保留前述 WASM。因此“wrapper/worker 对应 npm 1.8.8、本地 WASM +却不等于 npm 1.8.8”的旧观察仍成立,新的来源链解释了差异而未覆盖旧事实。 +本次没有独立重建 WASM;Docker tag、apt 和 npm install 也未全部锁定。 + +## 通知及未决项 + +固定源码 LICENSE 与 WASM 所在 Pages 的 COPYRIGHT 都有精确 blob 与 SHA。 +它们与原 npm 成员仅换行归一化后相同,原始字节/长度不同,分别保存指纹。 +COPYRIGHT 涉及 libass、fribidi、FreeType 等组件;fribidi 的 LGPL-2.1+ 和 +FreeType 完整 FTL 等通知/源码分发要求仍需在 06 落实,不能用插件 MIT 总括。 + +11 份字体的上游文件身份已确认。default Liberation、两份 Averia、Lato、CHAWP +仍须配齐对应完整通知;Allison、Architext、Arial、Franklin Gothic、Garamond、 +Slate Pro 六份仍缺适用的再分发凭证或经过审查的替换方案。上游公开托管与字体 +嵌入标志不是这些凭证,Arial 的内嵌 MIT 文本也仅指 Hebrew layout 部分。 +这些结论不是法律许可确认或部署授权。 + +VENDOR-04/05 保持 open,保留原关闭标准,追加 PKG-JASSUB-06 / SITE-01 的明确 +责任。01 的验收是核对并登记来源/通知,06/SITE 负责交付完整通知和处理发布 +资产;此次不删除工作、不豁免发布条件,也不要求把第三方文件改写为自有 TS。 +02/03/04 继续自有 adapter 的风险测试、结构和类型迁移。 + +## 验证与维护 + +新增 `jassub-provenance.test.mjs`,默认仅检查本地固定资产与来源/许可分类, +不需要 cache 或网络;加入 `yarn test:jassub`,现有 baseline glob 也会执行。 +两个显式补充模式: + +```sh +node refactor/scripts/jassub-provenance.test.mjs --cached-evidence +node refactor/scripts/jassub-provenance.test.mjs --network +``` + +前者要求保存的来源/下载证据存在并逐项复验,不表示重新联网。后者查询固定 +commits/trees/blobs,任何请求或字节比较失败都失败,不能自动回退缓存。普通 CI +不运行这两个补充模式。无需新依赖或锁文件修改,Node/Yarn 版本保持不变。 + +具体运行结果及报告指纹见 +[本次验证](../baselines/jassub-provenance-validation.json)。子代理另用禁用 fetch +的 preload 验证离线模式,篡改 blob/source-link 均被拒绝;这些负测日志与最终 +正式文件验证分开记录。历史 48 项基线继续验证实际 vendor JavaScript、七份 +实现及发布入口;它们不是原生 Worker/WASM/字幕绘制的证据。 + +架构、契约、第三方台账和风险 owner 同步更新。回退本次补充元数据、验证器和 +脚本入口不会改变播放器行为。独立本地提交,不推送或发布。 diff --git a/refactor/plan.md b/refactor/plan.md index 30fdab382..131c17f7c 100644 --- a/refactor/plan.md +++ b/refactor/plan.md @@ -4,7 +4,7 @@ 基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 231 项,范围 22 个包及工作区/示例。 -状态:todo 65 / doing 15 / blocked 0 / done 151 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 +状态:todo 65 / doing 14 / blocked 0 / done 152 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。 @@ -229,12 +229,12 @@ | ID | 范围 / 步骤 | 前置依赖 | 交付物 | 验收条件 | 风险 | 状态 | | --- | --- | --- | --- | --- | --- | --- | -| PKG-JASSUB-01 | artplayer-plugin-jassub
核对包契约与历史用法 | BASE-05 | 选项透传、result.instance、vendor 来源、worker/WASM/font 路径 | 源码/声明/README/demo/发布包差异已登记;公开形状和版本范围冻结;接续 VENDOR-04/05,核对 wrapper/worker/WASM/font 的独立来源与通知,保持资源路径和选项透传 | H | doing | +| PKG-JASSUB-01 | artplayer-plugin-jassub
核对包契约与历史用法 | BASE-05 | 选项透传、result.instance、vendor 来源、worker/WASM/font 路径 | 源码/声明/README/demo/发布包差异已登记;公开形状和版本范围冻结;接续 VENDOR-04/05,核对 wrapper/worker/WASM/font 的独立来源与通知,保持资源路径和选项透传 | H | done | | PKG-JASSUB-02 | artplayer-plugin-jassub
建立特有行为与错误测试 | PKG-JASSUB-01, ENG-03, ENG-05 | ASS 字体、时钟/seek/倍率/resize、加载失败和销毁 | 旧版本行为可重跑,成功/失败/切源/销毁有必要断言 | H | todo | | PKG-JASSUB-03 | artplayer-plugin-jassub
整理内部职责与资源 | PKG-JASSUB-02, CORE-15, CORE-16 | 仅整理自有 adapter/销毁;保留第三方文件及来源 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | H | todo | | PKG-JASSUB-04 | artplayer-plugin-jassub
迁移自有源码和公开类型 | PKG-JASSUB-03, ENG-04, ENG-06, CORE-07 | JASSUB option/instance 的兼容类型包装,vendor JS 例外记录 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | todo | | PKG-JASSUB-05 | artplayer-plugin-jassub
验证新旧核心和组合 | PKG-JASSUB-04, CORE-22 | 真实 worker/WASM 字幕渲染与全屏、旧核心测试 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | todo | -| PKG-JASSUB-06 | artplayer-plugin-jassub
验证分发并同步文档 | PKG-JASSUB-05, ENG-07 | jassub.js、外部资源路径、许可和离线失败记录 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | H | todo | +| PKG-JASSUB-06 | artplayer-plugin-jassub
验证分发并同步文档 | PKG-JASSUB-05, ENG-07 | jassub.js、外部资源路径、许可和离线失败记录;接续 VENDOR-04/05 完整组件/字体通知、LGPL 源码分发核对及六份未明确字体的分发处置 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录;来源身份已由01冻结,完整通知和未明确字体的发布处理必须闭环,不能以来源相同替代许可结论 | H | todo | ## 5 包迁移:artplayer-plugin-danmuku-mask @@ -531,7 +531,7 @@ - PKG-MULTI-SUB-03: [记录](baselines/multiple-subtitles-resources.json) [记录](changes/2026-09-13-PKG-MULTI-SUB-03-resources.md) - PKG-MULTI-SUB-04: [记录](baselines/multiple-subtitles-runtime-types.json) [记录](changes/2026-09-13-PKG-MULTI-SUB-04-runtime-types.md) [记录](baselines/multiple-subtitles-public-types.json) [记录](changes/2026-09-13-PKG-MULTI-SUB-04-public-types.md) [记录](type-compatibility-policy.md) [记录](baselines/multiple-subtitles-approved-types.json) [记录](changes/2026-09-13-PKG-MULTI-SUB-04-approved-types.md) - PKG-MULTI-SUB-07: [记录](baselines/multiple-subtitles-timestamps.json) [记录](changes/2026-09-13-PKG-MULTI-SUB-07-timestamps.md) -- PKG-JASSUB-01: [记录](baselines/jassub-release.json) [记录](baselines/jassub-vendor.json) [记录](baselines/jassub-font-metadata.json) [记录](baselines/jassub-contract.md) [记录](baselines/jassub-contract-validation.json) [记录](changes/2026-09-13-PKG-JASSUB-01-baseline.md) +- PKG-JASSUB-01: [记录](baselines/jassub-release.json) [记录](baselines/jassub-vendor.json) [记录](baselines/jassub-font-metadata.json) [记录](baselines/jassub-contract.md) [记录](baselines/jassub-contract-validation.json) [记录](changes/2026-09-13-PKG-JASSUB-01-baseline.md) [记录](baselines/jassub-provenance.json) [记录](baselines/jassub-provenance-validation.json) [记录](changes/2026-09-14-PKG-JASSUB-01-provenance.md) - PKG-MASK-01: [记录](baselines/danmuku-mask-release.json) [记录](baselines/danmuku-mask-registry.json) [记录](baselines/danmuku-mask-contract.md) [记录](baselines/danmuku-mask-contract-validation.json) [记录](changes/2026-09-13-PKG-MASK-01-contract.md) - PKG-MASK-02: [记录](changes/2026-09-13-PKG-MASK-02-failures.md) [记录](baselines/danmuku-mask-failures-validation.json) - PKG-MASK-03: [记录](changes/2026-09-13-PKG-MASK-03-lifecycle.md) [记录](baselines/danmuku-mask-lifecycle-validation.json) diff --git a/refactor/progress.md b/refactor/progress.md index fd253ca2e..64199d837 100644 --- a/refactor/progress.md +++ b/refactor/progress.md @@ -1,5 +1,14 @@ # 进度与证据 +## PKG-JASSUB-01 来源及契约核对完成 + +两份 WASM 与 11 份字体的上游 blob 已精确匹配,nightly 对应源码、七子模块和 +构建/通知来源明确;新联网验证 23 请求成功,13 字节比较通过。本包联合 50 项 +通过,saved-evidence 三项通过。旧 npm WASM 不匹配事实保留,VENDOR-04/05 仍 +open,完整通知/未明确字体分发由 06/SITE-01 继续。 +[来源补充](changes/2026-09-14-PKG-JASSUB-01-provenance.md)和验证器已落地,生产资源 +未变;02 接着建立异常及资源基线,然后继续自有 adapter TS。此项新增一项 done。 + ## PKG-MASK-05 真实模型组合检查点(仍 doing) main/legacy 各 18 项,三个浏览器引擎和三个核心组合共 36 项通过。实际模型、PNG diff --git a/refactor/risk-table.md b/refactor/risk-table.md index 7362c825f..beeaccceb 100644 --- a/refactor/risk-table.md +++ b/refactor/risk-table.md @@ -28,8 +28,8 @@ | VENDOR-01 | resolved / 源码/产物事实 | screenfull 来源、版本与许可闭环 | CORE-12 | | VENDOR-02 | resolved / 源码/产物事实 | hint.css 来源、版本与许可闭环 | CORE-12 | | VENDOR-03 | resolved / 已复现 | webvtt-parser 来源、版本与许可闭环 | PKG-MULTI-SUB-01 | -| VENDOR-04 | open / 源码/产物事实 | jassub-code-and-workers 来源、版本与许可闭环 | PKG-JASSUB-01 | -| VENDOR-05 | open / 源码/产物事实 | jassub-font-assets 来源、版本与许可闭环 | PKG-JASSUB-01, SITE-01 | +| VENDOR-04 | open / 源码/产物事实 | jassub-code-and-workers 来源、版本与许可闭环 | PKG-JASSUB-01, PKG-JASSUB-06 | +| VENDOR-05 | open / 源码/产物事实 | jassub-font-assets 来源、版本与许可闭环 | PKG-JASSUB-01, SITE-01, PKG-JASSUB-06 | | VENDOR-06 | open / 待取证 | monaco-static-assets 来源、版本与许可闭环 | SITE-01, SITE-05 | | VENDOR-07 | open / 待取证 | vconsole 来源、版本与许可闭环 | SITE-01 | | VENDOR-08 | open / 待取证 | console-bundle 来源、版本与许可闭环 | SITE-01 | diff --git a/refactor/risks.json b/refactor/risks.json index 7adeb2ca4..807f9c7f4 100644 --- a/refactor/risks.json +++ b/refactor/risks.json @@ -538,7 +538,8 @@ "confirmation": "source-observed", "status": "open", "owners": [ - "PKG-JASSUB-01" + "PKG-JASSUB-01", + "PKG-JASSUB-06" ], "evidence": [ "refactor/third-party.json", @@ -552,7 +553,10 @@ "refactor/baselines/jassub-font-metadata.json", "refactor/baselines/jassub-contract.md", "refactor/baselines/jassub-contract-validation.json", - "refactor/changes/2026-09-13-PKG-JASSUB-01-baseline.md" + "refactor/changes/2026-09-13-PKG-JASSUB-01-baseline.md", + "refactor/baselines/jassub-provenance.json", + "refactor/baselines/jassub-provenance-validation.json", + "refactor/changes/2026-09-14-PKG-JASSUB-01-provenance.md" ], "compatibleResolution": "Keep separate from owned TS migration. Verify source/version/diff and license notices before replacement; preserve API/CSS/worker URLs and run owning package tests.", "closureCriteria": "固定上游版本/内容差异、完整组件许可与分发 notices,兼容测试通过;仅当前上游许可证名称不足以关闭。" @@ -564,7 +568,8 @@ "status": "open", "owners": [ "PKG-JASSUB-01", - "SITE-01" + "SITE-01", + "PKG-JASSUB-06" ], "evidence": [ "refactor/third-party.json", @@ -575,7 +580,10 @@ "refactor/baselines/jassub-font-metadata.json", "refactor/baselines/jassub-contract.md", "refactor/baselines/jassub-contract-validation.json", - "refactor/changes/2026-09-13-PKG-JASSUB-01-baseline.md" + "refactor/changes/2026-09-13-PKG-JASSUB-01-baseline.md", + "refactor/baselines/jassub-provenance.json", + "refactor/baselines/jassub-provenance-validation.json", + "refactor/changes/2026-09-14-PKG-JASSUB-01-provenance.md" ], "compatibleResolution": "Keep separate from owned TS migration. Verify source/version/diff and license notices before replacement; preserve API/CSS/worker URLs and run owning package tests.", "closureCriteria": "固定上游版本/内容差异、完整组件许可与分发 notices,兼容测试通过;仅当前上游许可证名称不足以关闭。" diff --git a/refactor/scripts/jassub-provenance.test.mjs b/refactor/scripts/jassub-provenance.test.mjs new file mode 100644 index 000000000..6e3a34e71 --- /dev/null +++ b/refactor/scripts/jassub-provenance.test.mjs @@ -0,0 +1,182 @@ +import assert from 'node:assert/strict' +import { Buffer } from 'node:buffer' +import { createHash } from 'node:crypto' +import fs from 'node:fs' +import path from 'node:path' +import process from 'node:process' +// eslint-disable-next-line test/no-import-node-test -- Provenance checks use the repository runner. +import test from 'node:test' + +const args = process.argv.slice(2) +for (const value of args) + assert(['--network', '--cached-evidence'].includes(value) || value.startsWith('--provenance='), `unknown provenance argument: ${value}`) +const filename = args.find(value => value.startsWith('--provenance='))?.slice('--provenance='.length) + ?? 'refactor/baselines/jassub-provenance.json' +const provenance = readJson(filename) +const cache = 'refactor/.cache' +const network = args.includes('--network') +const cachedEvidence = args.includes('--cached-evidence') + +function readJson(file) { + return JSON.parse(fs.readFileSync(file, 'utf8')) +} + +function digest(bytes, algorithm = 'sha256') { + return createHash(algorithm).update(bytes).digest('hex') +} + +function gitBlob(bytes) { + return createHash('sha1').update(`blob ${bytes.length}\0`).update(bytes).digest('hex') +} + +function verifyBytes(bytes, expected, label) { + assert.equal(bytes.length, expected.bytes, `${label}: bytes`) + assert.equal(digest(bytes), expected.sha256, `${label}: SHA-256`) + assert.equal(gitBlob(bytes), expected.gitBlob, `${label}: Git blob SHA-1`) +} + +function verifyTreeEntry(tree, file, expected) { + assert.equal(tree.truncated, false, 'an incomplete tree cannot prove path identity') + const entry = tree.tree.find(item => item.path === file) + assert(entry, `upstream tree is missing ${file}`) + assert.equal(entry.type, 'blob', file) + assert.equal(entry.sha, expected.gitBlob, file) + assert.equal(entry.size, expected.bytes, file) +} + +function verifyLineage(pagesCommit, nightlyCommit, pagesTree, sourceTree) { + const { pagesSnapshot, wasmNightly, build } = provenance.upstream + assert.equal(pagesCommit.sha, pagesSnapshot.commit) + assert.equal(pagesCommit.commit.message, pagesSnapshot.message) + assert(pagesCommit.commit.message.includes(`From ${pagesSnapshot.sourceCommit}`)) + assert.deepEqual(pagesCommit.files.map(item => item.filename), pagesSnapshot.changedPaths) + assert.equal(nightlyCommit.sha, wasmNightly.commit) + assert.equal(nightlyCommit.commit.message, wasmNightly.message) + assert(nightlyCommit.commit.message.includes(`From ${build.sourceCommit}`)) + assert.equal(wasmNightly.sourceCommit, build.sourceCommit, 'artifact source revision must match build source') + assert.equal(pagesTree.sha, pagesSnapshot.tree) + assert.equal(sourceTree.sha, build.tree) + assert.equal(sourceTree.truncated, false) + for (const item of provenance.assets) { + assert.equal(item.upstream.commit, pagesSnapshot.commit) + for (const file of item.upstream.paths) verifyTreeEntry(pagesTree, file, item) + if (item.kind === 'wasm') { + for (const file of item.upstream.paths) { + const changed = nightlyCommit.files.find(entry => entry.filename === file) + assert(changed, `nightly commit did not update ${file}`) + assert.equal(changed.sha, item.gitBlob, file) + } + } + } + for (const item of build.evidence) verifyTreeEntry(sourceTree, item.path, item) + for (const item of build.submodules) { + const entry = sourceTree.tree.find(row => row.path === item.path) + assert.equal(entry?.type, 'commit', item.path) + assert.equal(entry.sha, item.commit, item.path) + } + for (const notice of provenance.notices) { + const tree = notice.commit === build.sourceCommit ? sourceTree : pagesTree + verifyTreeEntry(tree, notice.path, notice) + } +} + +test('JASSUB provenance checks all frozen local WASM/font bytes offline without making a remote claim', () => { + assert.equal(provenance.schemaVersion, 1) + assert.equal(provenance.kind, 'append-only-provenance-supplement') + assert.equal(provenance.assets.length, 13) + const baseline = readJson(provenance.baseline) + const expected = baseline.assets.filter(item => /\.(?:wasm|ttf|otf|woff2)$/i.test(item.file)) + const files = provenance.assets.flatMap(item => [item.file, ...item.aliases]) + assert.equal(new Set(files).size, files.length) + assert.deepEqual(files.toSorted(), expected.map(item => item.file).toSorted()) + for (const item of provenance.assets) { + const original = fs.readFileSync(item.file) + for (const file of [item.file, ...item.aliases]) { + const frozen = expected.find(entry => entry.file === file) + assert.equal(item.sha256, frozen.sha256, `supplement must retain frozen ${file}`) + assert.equal(item.bytes, frozen.bytes, file) + const bytes = fs.readFileSync(file) + verifyBytes(bytes, item, file) + assert.deepEqual(bytes, original, file) + } + } + const vendor = readJson(provenance.vendorComparison) + for (const notice of provenance.notices) { + assert.equal(notice.npm.version, vendor.version) + assert.equal(notice.npm.sha256, vendor.files[notice.npm.member]) + } +}) + +test('JASSUB provenance retains explicit build and licensing limits', () => { + const { licensing, upstream, validation } = provenance + assert.equal(upstream.build.reproduced, false) + assert.equal(licensing.redistributionApproval, false) + assert.equal(licensing.completeNoticeAudit, false) + assert.equal(validation.browserValidated, false) + const fonts = provenance.assets.filter(item => item.kind === 'font').map(item => path.basename(item.file)) + const classified = [...licensing.openFontNoticeAssembly, ...licensing.unresolvedFontPermission] + assert.equal(new Set(classified).size, fonts.length) + assert.deepEqual(classified.toSorted(), fonts.toSorted()) +}) + +if (cachedEvidence) { + test('JASSUB explicitly rechecks saved downloaded bytes and lineage without refreshing network evidence', () => { + const { pagesSnapshot, wasmNightly, build } = provenance.upstream + verifyLineage( + readJson(path.join(cache, pagesSnapshot.cachedCommit)), + readJson(path.join(cache, wasmNightly.cachedCommit)), + readJson(path.join(cache, pagesSnapshot.cachedTree)), + readJson(path.join(cache, build.cachedTree)), + ) + for (const item of [...provenance.assets, ...build.evidence, ...provenance.notices]) { + const bytes = fs.readFileSync(path.join(cache, item.cachedDownload)) + verifyBytes(bytes, item, item.cachedDownload) + if (item.file) + assert.deepEqual(bytes, fs.readFileSync(item.file), item.file) + if (item.npm) { + const npm = fs.readFileSync(path.join(cache, item.npm.cachedDownload)) + assert.equal(npm.length, item.npm.bytes) + assert.equal(digest(npm), item.npm.sha256) + assert.notDeepEqual(bytes, npm) + assert.equal(bytes.toString().replaceAll('\r\n', '\n'), npm.toString().replaceAll('\r\n', '\n')) + } + } + }) +} + +if (network) { + test('JASSUB explicitly fetches immutable upstream commits, trees and bytes; unavailable evidence fails', async (context) => { + const { api, pagesSnapshot, wasmNightly, build } = provenance.upstream + assert.equal(api, 'https://api.github.com/repos/ThaUnknown/jassub') + let requests = 0 + async function getJson(suffix) { + const response = await fetch(`${api}/${suffix}`, { signal: AbortSignal.timeout(20000) }) + requests++ + assert(response.ok, `upstream HTTP ${response.status}: ${suffix}`) + return response.json() + } + const [pagesCommit, nightlyCommit, pagesTree, sourceTree, nightlyTree] = await Promise.all([ + getJson(`commits/${pagesSnapshot.commit}`), + getJson(`commits/${wasmNightly.commit}`), + getJson(`git/trees/${pagesSnapshot.commit}?recursive=1`), + getJson(`git/trees/${build.sourceCommit}?recursive=1`), + getJson(`git/trees/${wasmNightly.commit}?recursive=1`), + ]) + verifyLineage(pagesCommit, nightlyCommit, pagesTree, sourceTree) + for (const item of provenance.assets.filter(item => item.kind === 'wasm')) { + for (const file of item.upstream.paths) verifyTreeEntry(nightlyTree, file, item) + } + for (const notice of provenance.notices.filter(item => item.commit === wasmNightly.commit)) + verifyTreeEntry(nightlyTree, notice.path, notice) + for (const item of [...provenance.assets, ...build.evidence, ...provenance.notices]) { + const remote = await getJson(`git/blobs/${item.gitBlob}`) + assert.equal(remote.encoding, 'base64') + assert.equal(remote.sha, item.gitBlob) + const bytes = Buffer.from(remote.content, 'base64') + verifyBytes(bytes, item, item.file ?? item.path) + if (item.file) + assert.deepEqual(bytes, fs.readFileSync(item.file), item.file) + } + context.diagnostic(`Fresh network verification finished at ${new Date().toISOString()}: ${requests} successful requests; no cache fallback; 13 downloaded asset comparisons.`) + }) +} diff --git a/refactor/tasks.json b/refactor/tasks.json index 62ff4a535..4006817c0 100644 --- a/refactor/tasks.json +++ b/refactor/tasks.json @@ -2406,7 +2406,7 @@ "dependsOn": [ "BASE-05" ], - "status": "doing", + "status": "done", "risk": "H", "deliverable": "选项透传、result.instance、vendor 来源、worker/WASM/font 路径", "acceptance": "源码/声明/README/demo/发布包差异已登记;公开形状和版本范围冻结;接续 VENDOR-04/05,核对 wrapper/worker/WASM/font 的独立来源与通知,保持资源路径和选项透传", @@ -2416,7 +2416,10 @@ "baselines/jassub-font-metadata.json", "baselines/jassub-contract.md", "baselines/jassub-contract-validation.json", - "changes/2026-09-13-PKG-JASSUB-01-baseline.md" + "changes/2026-09-13-PKG-JASSUB-01-baseline.md", + "baselines/jassub-provenance.json", + "baselines/jassub-provenance-validation.json", + "changes/2026-09-14-PKG-JASSUB-01-provenance.md" ] }, { @@ -2504,8 +2507,8 @@ ], "status": "todo", "risk": "H", - "deliverable": "jassub.js、外部资源路径、许可和离线失败记录", - "acceptance": "tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录", + "deliverable": "jassub.js、外部资源路径、许可和离线失败记录;接续 VENDOR-04/05 完整组件/字体通知、LGPL 源码分发核对及六份未明确字体的分发处置", + "acceptance": "tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录;来源身份已由01冻结,完整通知和未明确字体的发布处理必须闭环,不能以来源相同替代许可结论", "evidence": [] }, { diff --git a/refactor/third-party.json b/refactor/third-party.json index d29e1d7d5..85ddb4fe8 100644 --- a/refactor/third-party.json +++ b/refactor/third-party.json @@ -352,15 +352,19 @@ "docs/assets/jassub/jassub-worker-modern.wasm" ], "owners": [ - "PKG-JASSUB-01" + "PKG-JASSUB-01", + "PKG-JASSUB-06" ], - "sourceStatus": "Wrapper matches npm jassub 1.8.8 after formatting/ESLint-header adaptation; package/docs worker JS matches exact bytes. WASM files validate but code/data differ from 1.8.8; exact build/source/component graph remains unresolved. See baselines/jassub-contract.md.", - "licenseStatus": "Current upstream wrapper LICENSE is MIT; this does not certify linked WASM libraries, fonts, or the exact local build.", + "sourceStatus": "Wrapper/worker JS match npm 1.8.8 as previously frozen. Both local WASM binaries exactly match Pages nightly 747e392d7f6fe7c38bbcdfe05c9ada46b0b62daf linked to source 6b19a04ddfbad8f9bfd3237395788dd76218841b, with pinned build files and seven submodules. No independent rebuild. See baselines/jassub-provenance.json.", + "licenseStatus": "Matching source LICENSE and nightly COPYRIGHT identified and byte-verified; full component notice assembly, LGPL source/distribution review and full FreeType license remain PKG-JASSUB-06 gates. VENDOR-04 remains open.", "upstreamSources": [ "https://github.com/ThaUnknown/jassub/blob/main/LICENSE", - "https://registry.npmjs.org/jassub/-/jassub-1.8.8.tgz" + "https://registry.npmjs.org/jassub/-/jassub-1.8.8.tgz", + "https://github.com/ThaUnknown/jassub/tree/25d1ea7ca1f827dbed2c8ea539c9bc917ccbfab1", + "https://github.com/ThaUnknown/jassub/tree/6b19a04ddfbad8f9bfd3237395788dd76218841b", + "https://github.com/ThaUnknown/jassub/commit/747e392d7f6fe7c38bbcdfe05c9ada46b0b62daf" ], - "upstreamReviewedAt": "2026-09-13", + "upstreamReviewedAt": "2026-09-14", "updatePolicy": "Keep separate from owned TS migration. Verify source/version/diff and license notices before replacement; preserve API/CSS/worker URLs and run owning package tests.", "fingerprints": [ { @@ -409,12 +413,15 @@ ], "owners": [ "PKG-JASSUB-01", - "SITE-01" + "SITE-01", + "PKG-JASSUB-06" ], - "sourceStatus": "All 12 font name tables and embedding flags extracted with hashes; default font matches npm jassub 1.8.8. Other embedded notices are source clues only; original acquisition and redistribution evidence remain unresolved. See baselines/jassub-font-metadata.json.", - "licenseStatus": "Font redistribution rights are unverified; do not infer licenses from root MIT or font names.", - "upstreamSources": [], - "upstreamReviewedAt": null, + "sourceStatus": "All 11 unique fonts and default alias exactly match historical upstream Pages blobs. Original acquisition/redistribution permissions are not established by that identity. See baselines/jassub-provenance.json.", + "licenseStatus": "Five open-font notice assemblies and six unresolved font permissions remain explicit in the supplement. PKG-JASSUB-06 / SITE-01 must resolve actual package/Pages redistribution scope; VENDOR-05 remains open.", + "upstreamSources": [ + "https://github.com/ThaUnknown/jassub/tree/25d1ea7ca1f827dbed2c8ea539c9bc917ccbfab1" + ], + "upstreamReviewedAt": "2026-09-14", "updatePolicy": "Keep separate from owned TS migration. Verify source/version/diff and license notices before replacement; preserve API/CSS/worker URLs and run owning package tests.", "fingerprints": [ {