From c9684a544b38ba8f4c94392124aa4d59c2f01445 Mon Sep 17 00:00:00 2001 From: mes123456 Date: Thu, 27 Aug 2026 22:06:43 +0800 Subject: [PATCH] Authorize M12 provenance epoch 2 baseline --- .../M12-PROVENANCE-EPOCH-2-APPROVAL.json | 19 + docs/status/WBV2-P8-003.md | 8 +- docs/status/WBV2-P8-004.md | 7 +- .../WBV2-P8-003/m12-provenance-epoch.json | 195 +++---- .../m12-source-recovery-search.json | 47 ++ tests/golden/WBV2-P8-003/manifest.json | 29 +- .../WBV2-P8-003/provenance-epoch-report.json | 11 +- tests/golden/WBV2-P8-003/task-context.json | 2 +- tests/golden/WBV2-P8-004/manifest.json | 31 +- .../recovery-release-gate-report.json | 34 +- tests/golden/WBV2-P8-004/task-context.json | 42 +- tests/golden/WBV2/task-index.json | 2 +- .../files/web/protocol/glb-import.ts.m12-06F | 485 ++++++++++++++++++ .../M12-supplemental-exact-recovery.json | 18 + .../web/check-wbv2-recovery-release-gate.mjs | 22 +- tools/web/generate-m12-provenance-epoch.mjs | 38 +- 16 files changed, 813 insertions(+), 177 deletions(-) create mode 100644 docs/status/M12-PROVENANCE-EPOCH-2-APPROVAL.json create mode 100644 tests/golden/WBV2-P8-003/m12-source-recovery-search.json create mode 100644 tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/protocol/glb-import.ts.m12-06F create mode 100644 tests/golden/corrective/rehydration/M12-supplemental-exact-recovery.json diff --git a/docs/status/M12-PROVENANCE-EPOCH-2-APPROVAL.json b/docs/status/M12-PROVENANCE-EPOCH-2-APPROVAL.json new file mode 100644 index 000000000..f53d07187 --- /dev/null +++ b/docs/status/M12-PROVENANCE-EPOCH-2-APPROVAL.json @@ -0,0 +1,19 @@ +{ + "schemaVersion": 1, + "decision": "APPROVED", + "epoch": 2, + "scope": "M12 provenance only", + "approvedBy": "wangdequan", + "authority": "project-owner", + "approvalSource": "explicit project-owner authorization in task conversation", + "approvedAt": "2026-08-27T21:11:58+08:00", + "restoredOriginals": 23, + "authorizedReplacements": 61, + "limitations": { + "fullHistoricalReproduction": false, + "replacementIsHistoricalRestoration": false, + "featureParityClaim": false, + "releaseLabel": "AUTHORIZED_REBASELINE_ONLY" + }, + "policy": "The 61 replacement bindings may satisfy the approved current baseline, but must never be reported as recovery of the unavailable historical bytes." +} diff --git a/docs/status/WBV2-P8-003.md b/docs/status/WBV2-P8-003.md index d737baa19..d0d1eaa85 100644 --- a/docs/status/WBV2-P8-003.md +++ b/docs/status/WBV2-P8-003.md @@ -1,9 +1,9 @@ # WBV2-P8-003 status - task: `WBV2-P8-003`; parent: `WBV2-P8-002`; status: `done` -- provenance epoch: `1` -- historical originals restored: `22/84` -- authorized replacements: `62/84` +- provenance epoch: `2` +- historical originals restored: `23/84` +- authorized replacements: `61/84` - nextTask: `WBV2-P8-004` -The 22 exact Git-history matches remain immutable restorations. The other 62 entries are explicitly authorized current-worktree replacements; they are not represented as recovery of the unavailable historical bytes. +The 23 exact matches remain immutable restorations. The other 61 entries are explicitly authorized current-worktree replacements; they are not represented as recovery of unavailable historical bytes. Full historical reproduction remains false. diff --git a/docs/status/WBV2-P8-004.md b/docs/status/WBV2-P8-004.md index 4a49cf28d..04224bc26 100644 --- a/docs/status/WBV2-P8-004.md +++ b/docs/status/WBV2-P8-004.md @@ -2,8 +2,9 @@ - task: `WBV2-P8-004`; parent: `WBV2-P8-003`; status: `blocked` - atomic Chromium receipts: `45/45` -- M12 provenance: `22 restored + 62 authorized replacements` -- release decision: `BLOCKED` +- M12 provenance epoch: `2` +- M12 provenance: `23 restored + 61 authorized replacements` +- release decision: `READY_WITH_AUTHORIZED_REBASELINE` - nextTask: `NONE` -The recovery gate is internally consistent and fail-closed. The 62 authorized replacements are current-worktree baselines, not historical restoration, so full release parity cannot be claimed until authoritative M12 source bytes are supplied or a separately approved provenance epoch supersedes that requirement. +The project owner approved the new provenance baseline. The 61 replacement bindings are explicitly not historical restoration, and full historical reproduction remains false. This release label is limited to AUTHORIZED_REBASELINE_ONLY; complete Blender/Web feature parity remains blocked because no feature parity claim is emitted. diff --git a/tests/golden/WBV2-P8-003/m12-provenance-epoch.json b/tests/golden/WBV2-P8-003/m12-provenance-epoch.json index fdd0de45e..df55142d9 100644 --- a/tests/golden/WBV2-P8-003/m12-provenance-epoch.json +++ b/tests/golden/WBV2-P8-003/m12-provenance-epoch.json @@ -1,20 +1,25 @@ { "schemaVersion": 1, - "epoch": 1, + "epoch": 2, "task": "WBV2-P8-003", "operation": "M12_AUTHORIZED_PROVENANCE_REBASELINE", "historicalEvidencePreserved": true, "sourceSnapshot": "tests/golden/corrective/rehydration/M12-git-history-snapshot/snapshot-manifest.json", "sourceSnapshotSha256": "e58b1804ed92f19855679f7cdca6787bffcfafbc59c007fd419077ed55a04b54", + "supplementalExactRecovery": "tests/golden/corrective/rehydration/M12-supplemental-exact-recovery.json", + "supplementalExactRecoverySha256": "a8947b6b5966dbd870a254a96e7ee26d606011c16cb5c5c3c315ac912eae0908", + "searchReport": "tests/golden/WBV2-P8-003/m12-source-recovery-search.json", + "searchReportSha256": "4192418563aabf011bb5d6c1407565c87bb492ab208eca69f4768ae35b126fc0", "originalDriftReport": "tests/golden/corrective/rehydration/M12-manifest-sha-drift.json", "originalDriftReportSha256": "e35f4dcae87ce260d565dccfcdafb58a032d79141115a26b45b25dcbec3f31e1", "originalIntake": "tests/golden/corrective/rehydration/M12-provenance-intake.json", "originalIntakeSha256": "37a54d83eba799948fcaae798828915b35f8bf8e709bf90df21351d0288676d9", "policy": { - "restoredOriginals": 22, - "authorizedReplacements": 62, + "restoredOriginals": 23, + "authorizedReplacements": 61, "replacementsAreNotHistoricalRestorations": true, - "historicalManifestsRewritten": false + "historicalManifestsRewritten": false, + "fullHistoricalReproduction": false }, "entries": [ { @@ -25,7 +30,7 @@ "observedSha256": "f6d949a56d6b3c9bf58bbb3b94eb3c16e497cdd515da11bf45d9ef0b86c638b2", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "f6d949a56d6b3c9bf58bbb3b94eb3c16e497cdd515da11bf45d9ef0b86c638b2", "replacement": true, @@ -40,7 +45,7 @@ "observedSha256": "e1c28687ce34cda539fce096060ab972a98e509c3cdd8335745d565546bcd030", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "97d025812d51642e6c118a7a6228b0481e5ff95e4acb7742f62875f9f27f924b", "replacement": true, @@ -55,7 +60,7 @@ "observedSha256": "f6d949a56d6b3c9bf58bbb3b94eb3c16e497cdd515da11bf45d9ef0b86c638b2", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "f6d949a56d6b3c9bf58bbb3b94eb3c16e497cdd515da11bf45d9ef0b86c638b2", "replacement": true, @@ -70,7 +75,7 @@ "observedSha256": "2510df2927fbe34ac3c81e4192a9c3ef842388af56dce77d9717681aeda6becd", "provenanceState": "PROVENANCE_RESTORED", "snapshotPath": "tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/tests/unit/asset-preview-opfs-commit.test.mjs", - "epoch": 1, + "epoch": 2, "binding": "IMMUTABLE_SOURCE_SNAPSHOT", "sourceSha256": "f489a1d9a4c5fb24a99bce6d31841534c7cf3981b53076a0d9e19d106e9d0d74", "replacement": false @@ -83,7 +88,7 @@ "observedSha256": "4fa197fc7d696b5ca9dc6fe953a1c39c6915e37ab21001c14c55311da965048a", "provenanceState": "PROVENANCE_RESTORED", "snapshotPath": "tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/tests/unit/asset-preview-dedup.test.mjs", - "epoch": 1, + "epoch": 2, "binding": "IMMUTABLE_SOURCE_SNAPSHOT", "sourceSha256": "e023fdd33e0f40c1331dfdd73cd287dc684256b9d134971ab17db8cfe2a83835", "replacement": false @@ -96,7 +101,7 @@ "observedSha256": "217fc2c8b55b9a8bc8ce0e23268cf3062f989853d72f71f999278f2ad8e61b4c", "provenanceState": "PROVENANCE_RESTORED", "snapshotPath": "tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/tests/unit/asset-preview-quarantine.test.mjs", - "epoch": 1, + "epoch": 2, "binding": "IMMUTABLE_SOURCE_SNAPSHOT", "sourceSha256": "79c836c7a3be4e9f305a962775444c766577cca87fa6d1698811e75eb21f2d9a", "replacement": false @@ -109,7 +114,7 @@ "observedSha256": "cd1f790dffb2b31780cb7f9cfe10e433b335ed282018b2fae5d8fadb687fd5c0", "provenanceState": "PROVENANCE_RESTORED", "snapshotPath": "tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/tests/unit/asset-preview-reference-gc.test.mjs", - "epoch": 1, + "epoch": 2, "binding": "IMMUTABLE_SOURCE_SNAPSHOT", "sourceSha256": "c5de3122b5d8aab17daf0179cb4f5bb0b5d299408818849cbe7b18e40a47706e", "replacement": false @@ -122,7 +127,7 @@ "observedSha256": "fa908f0a9049287cc67c32fcfb5ed006495fe998f214f602cb0a5116f6856134", "provenanceState": "PROVENANCE_RESTORED", "snapshotPath": "tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/tests/unit/asset-preview-display.test.mjs", - "epoch": 1, + "epoch": 2, "binding": "IMMUTABLE_SOURCE_SNAPSHOT", "sourceSha256": "f10a05900c4d1282251f7a76e7c413a18602910f55fe8400f1713f548eba3873", "replacement": false @@ -135,7 +140,7 @@ "observedSha256": "1c23077b0c2d61fcdedebae3d6c80e55cb84dedfb9a7600ecf4df5ac0fece3e9", "provenanceState": "PROVENANCE_RESTORED", "snapshotPath": "tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/tests/unit/library-operation-identity.test.mjs", - "epoch": 1, + "epoch": 2, "binding": "IMMUTABLE_SOURCE_SNAPSHOT", "sourceSha256": "0e5b2c82d481180eb8eb22de16ad8cdef5e58fa7423c26011faaffbac788e547", "replacement": false @@ -148,7 +153,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -163,7 +168,7 @@ "observedSha256": "7398625b4d2f921bad11922c3160a02597ea7ed7f15e83d9594ccf843430d15b", "provenanceState": "PROVENANCE_RESTORED", "snapshotPath": "tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/tests/unit/library-linked-mutation.test.mjs", - "epoch": 1, + "epoch": 2, "binding": "IMMUTABLE_SOURCE_SNAPSHOT", "sourceSha256": "f19d47cefe89daf6123062e045ec717e6ffe60977e8a4b20c996dd62e49da211", "replacement": false @@ -176,7 +181,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -191,7 +196,7 @@ "observedSha256": "7492179e72e066ba1e34a5c9055d319e2ee886b4c5f1c4034a781825118796a6", "provenanceState": "PROVENANCE_RESTORED", "snapshotPath": "tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/tests/unit/library-linked-reload.test.mjs", - "epoch": 1, + "epoch": 2, "binding": "IMMUTABLE_SOURCE_SNAPSHOT", "sourceSha256": "dff866291468cc01e775fe3b3b95c632f5c0742566f79b956a0193bfd8fd43d2", "replacement": false @@ -204,7 +209,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -219,7 +224,7 @@ "observedSha256": "0fc3389c4a76c275d332ac5353ca7593884c2e65ba98f9d8db6f24e0518fff75", "provenanceState": "PROVENANCE_RESTORED", "snapshotPath": "tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/tests/unit/library-linked-missing.test.mjs", - "epoch": 1, + "epoch": 2, "binding": "IMMUTABLE_SOURCE_SNAPSHOT", "sourceSha256": "4ab6d6ff4845b4ca963399e7eea214ceb412871dc1fdef5bac1ed0333596da4f", "replacement": false @@ -232,7 +237,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -247,7 +252,7 @@ "observedSha256": "fbdf0f3898037ab15db538df40f3629895cf2b1ebab369332b54962f45297478", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "fbdf0f3898037ab15db538df40f3629895cf2b1ebab369332b54962f45297478", "replacement": true, @@ -262,7 +267,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -277,7 +282,7 @@ "observedSha256": "1420291c9ba5b4e45455a999ba94864da5eccf5b6e9f377577e01e4dfb218e48", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "1420291c9ba5b4e45455a999ba94864da5eccf5b6e9f377577e01e4dfb218e48", "replacement": true, @@ -292,7 +297,7 @@ "observedSha256": "b70aa48b2c8902d1427e2cf80de3b81992d2e0c42d8d271cefdddeadd9162927", "provenanceState": "PROVENANCE_RESTORED", "snapshotPath": "tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/tests/unit/library-override-writer.test.mjs", - "epoch": 1, + "epoch": 2, "binding": "IMMUTABLE_SOURCE_SNAPSHOT", "sourceSha256": "e41bd32eb4ce4d331a20ecb91f3325a27f461b9ae85e98940d5afd482ec21c4c", "replacement": false @@ -305,7 +310,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -320,7 +325,7 @@ "observedSha256": "8c1125d05c47da5e0a4a5b2fb54b5f61613a0c50391de52fb5578db8160be2e8", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "8c1125d05c47da5e0a4a5b2fb54b5f61613a0c50391de52fb5578db8160be2e8", "replacement": true, @@ -335,7 +340,7 @@ "observedSha256": "19807407446397ef649a35ad3f3cb06478b5f549a8f1b4b9759bb5eeacddc391", "provenanceState": "PROVENANCE_RESTORED", "snapshotPath": "tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/tests/unit/library-override-freshness.test.mjs", - "epoch": 1, + "epoch": 2, "binding": "IMMUTABLE_SOURCE_SNAPSHOT", "sourceSha256": "d48344f31e1ba12e557ca30ece56643583efa633da556f5de3896a8e9175ef8f", "replacement": false @@ -348,7 +353,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -363,7 +368,7 @@ "observedSha256": "aab9c645f2c05550ff37148b915cfadcc36542bb96fd5535ccfc960defdf57c3", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "aab9c645f2c05550ff37148b915cfadcc36542bb96fd5535ccfc960defdf57c3", "replacement": true, @@ -378,7 +383,7 @@ "observedSha256": "2d4b06b5d4a1ab69315bc4c04a2a6d39f9f6b704a056eb030318b53203068c97", "provenanceState": "PROVENANCE_RESTORED", "snapshotPath": "tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/tests/unit/library-negative-cases.test.mjs", - "epoch": 1, + "epoch": 2, "binding": "IMMUTABLE_SOURCE_SNAPSHOT", "sourceSha256": "b7ae41b2c35b2fe1598bca4425dd434230bfe4bf342320c88214a060dcbb0a16", "replacement": false @@ -391,7 +396,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -406,7 +411,7 @@ "observedSha256": "6ed2d8655651bf7915a88cf091e649fc8b0c98500eaad6ae55e39bebe5c504c5", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "6ed2d8655651bf7915a88cf091e649fc8b0c98500eaad6ae55e39bebe5c504c5", "replacement": true, @@ -421,7 +426,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -436,7 +441,7 @@ "observedSha256": "7c9baef0d653f91e0976c0f1aecea71660adb9845500c2f0c06442996dfaf42c", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "7c9baef0d653f91e0976c0f1aecea71660adb9845500c2f0c06442996dfaf42c", "replacement": true, @@ -451,7 +456,7 @@ "observedSha256": "fa5d9ae3359c722449730a3cdb1f0133dd5a9935524ddc27224700147dea9368", "provenanceState": "PROVENANCE_RESTORED", "snapshotPath": "tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/tests/unit/library-source-origin.test.mjs", - "epoch": 1, + "epoch": 2, "binding": "IMMUTABLE_SOURCE_SNAPSHOT", "sourceSha256": "89b207c9cb13b4fb055a2dfed0237c0f8a00b13a39cc4175a378f5ef2abdb7ae", "replacement": false @@ -464,7 +469,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -479,7 +484,7 @@ "observedSha256": "da9c018a98c617809974a577f55a3f2e2f6069347260640ef2397195a2aee0e7", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "da9c018a98c617809974a577f55a3f2e2f6069347260640ef2397195a2aee0e7", "replacement": true, @@ -494,7 +499,7 @@ "observedSha256": "6971f333bcbd3eb2e3228506596701877e3ba985644ae5d180b99c232d2e0236", "provenanceState": "PROVENANCE_RESTORED", "snapshotPath": "tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/tests/unit/library-path-normalization.test.mjs", - "epoch": 1, + "epoch": 2, "binding": "IMMUTABLE_SOURCE_SNAPSHOT", "sourceSha256": "abde64c60397541e92a83dd9e4a24e65faf340a8d046650604c101a21037c777", "replacement": false @@ -507,7 +512,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -522,7 +527,7 @@ "observedSha256": "955de46ca2a8921bc062a7ada681e8e9bccaeca1eb5b0e111f1a38683ad26c21", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "955de46ca2a8921bc062a7ada681e8e9bccaeca1eb5b0e111f1a38683ad26c21", "replacement": true, @@ -537,7 +542,7 @@ "observedSha256": "f8ccb30b291521858ccb6fb42ea6e0129b50fa44f7a4953971dc1687f3db884c", "provenanceState": "PROVENANCE_RESTORED", "snapshotPath": "tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/tests/unit/library-path-security.test.mjs", - "epoch": 1, + "epoch": 2, "binding": "IMMUTABLE_SOURCE_SNAPSHOT", "sourceSha256": "ab302cacb24634a3225dda5cb8f5282cb80b3bd81ed5c8900ddf4ff18267b7e2", "replacement": false @@ -550,7 +555,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -565,7 +570,7 @@ "observedSha256": "59854c9d6d7b024ca4c30061e2325f079dfb25c25a8f38b342dba4c886494255", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "59854c9d6d7b024ca4c30061e2325f079dfb25c25a8f38b342dba4c886494255", "replacement": true, @@ -580,7 +585,7 @@ "observedSha256": "983a77d9bde5302cfe72778cab3452662e15927bcdc538aafc78c68618d558c7", "provenanceState": "PROVENANCE_RESTORED", "snapshotPath": "tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/tests/unit/library-link-safety.test.mjs", - "epoch": 1, + "epoch": 2, "binding": "IMMUTABLE_SOURCE_SNAPSHOT", "sourceSha256": "7739275be91222d7bfb61d2f5a1daf812c6860fe34d0aea27df8380a77322120", "replacement": false @@ -593,7 +598,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -608,7 +613,7 @@ "observedSha256": "b7b4f1a9f775f81b46fc76dec8d520d9ad0292f68d43a1d0593829eb7671ef62", "provenanceState": "PROVENANCE_RESTORED", "snapshotPath": "tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/tests/unit/library-metadata-first.test.mjs", - "epoch": 1, + "epoch": 2, "binding": "IMMUTABLE_SOURCE_SNAPSHOT", "sourceSha256": "2da649722acee9cace8db6f337b4a93500a9c775a0b0f086bf38dd2b3e7f9e91", "replacement": false @@ -621,7 +626,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -636,7 +641,7 @@ "observedSha256": "4394b7bd6528c2823c5860b57f2f88623b280ee099072b44ecf9c63bfbc38bdc", "provenanceState": "PROVENANCE_RESTORED", "snapshotPath": "tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/tests/unit/library-archive-budget.test.mjs", - "epoch": 1, + "epoch": 2, "binding": "IMMUTABLE_SOURCE_SNAPSHOT", "sourceSha256": "b0d8356c6fb348d98e28ce220052845a29439b34b3c976b21a4dbf370ea19593", "replacement": false @@ -649,7 +654,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -664,7 +669,7 @@ "observedSha256": "e101dd559da4f557f10f19b688184cec3fad3e06664082a7c54834bc3cf00e1f", "provenanceState": "PROVENANCE_RESTORED", "snapshotPath": "tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/tests/unit/library-archive-conflicts.test.mjs", - "epoch": 1, + "epoch": 2, "binding": "IMMUTABLE_SOURCE_SNAPSHOT", "sourceSha256": "3f2d44dce33b1c17b3a48f58b04fdd821abc88d98ce3d3b5bb724859e0f0ab3c", "replacement": false @@ -677,7 +682,7 @@ "observedSha256": "f6d949a56d6b3c9bf58bbb3b94eb3c16e497cdd515da11bf45d9ef0b86c638b2", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "f6d949a56d6b3c9bf58bbb3b94eb3c16e497cdd515da11bf45d9ef0b86c638b2", "replacement": true, @@ -692,7 +697,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -707,7 +712,7 @@ "observedSha256": "ae8d4779b939351d987ba4fdacc37a5345f72e11fbdebf9a81bd0f8ee25020bf", "provenanceState": "PROVENANCE_RESTORED", "snapshotPath": "tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/tests/unit/library-archive-cancellation.test.mjs", - "epoch": 1, + "epoch": 2, "binding": "IMMUTABLE_SOURCE_SNAPSHOT", "sourceSha256": "77e7d5bc64dd6b313006ebf523602601acdaf7949a1484da872ddcd046983786", "replacement": false @@ -720,7 +725,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -735,7 +740,7 @@ "observedSha256": "3f27fb26a3edd27a8f85d16aaed445546a97683366072da297ff6d7404d1960b", "provenanceState": "PROVENANCE_RESTORED", "snapshotPath": "tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/tests/unit/library-archive-recovery.test.mjs", - "epoch": 1, + "epoch": 2, "binding": "IMMUTABLE_SOURCE_SNAPSHOT", "sourceSha256": "a931edef8f3ca1243a80ec2b8e9ff5b8da1dfd339f3d8c162ad2ebd08d3db6a1", "replacement": false @@ -748,7 +753,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -763,7 +768,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -778,7 +783,7 @@ "observedSha256": "edf466eed602786ae2ea254003f0f19d5d44f95b306e364698c4497d3108c0c6", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "edf466eed602786ae2ea254003f0f19d5d44f95b306e364698c4497d3108c0c6", "replacement": true, @@ -793,7 +798,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -808,7 +813,7 @@ "observedSha256": "cdb436ccba3748338df0da283fb2f1acd25c3f61e560c919c4b77196c936e6e4", "provenanceState": "PROVENANCE_RESTORED", "snapshotPath": "tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/tests/unit/io-format-capability-matrix.test.mjs", - "epoch": 1, + "epoch": 2, "binding": "IMMUTABLE_SOURCE_SNAPSHOT", "sourceSha256": "0cc4d8f1df1ecdab20d8100cf5f12b44cb2a68bca4384ef7964a032b2f74b36e", "replacement": false @@ -821,7 +826,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -836,7 +841,7 @@ "observedSha256": "5861988f3dfbb8c05d36aee3033a72514de53a0cb4ec6f3d832418ef6b5cc8b2", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "5861988f3dfbb8c05d36aee3033a72514de53a0cb4ec6f3d832418ef6b5cc8b2", "replacement": true, @@ -851,7 +856,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -866,7 +871,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -881,7 +886,7 @@ "observedSha256": "5861988f3dfbb8c05d36aee3033a72514de53a0cb4ec6f3d832418ef6b5cc8b2", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "5861988f3dfbb8c05d36aee3033a72514de53a0cb4ec6f3d832418ef6b5cc8b2", "replacement": true, @@ -896,7 +901,7 @@ "observedSha256": "667f614322b9fa7ac2fc8f1cd5e4510f25ad153e8d94b7ca376c96928d158ed3", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "667f614322b9fa7ac2fc8f1cd5e4510f25ad153e8d94b7ca376c96928d158ed3", "replacement": true, @@ -911,7 +916,7 @@ "observedSha256": "26d804c156d5ef9fec89d6ad393162ddc8423a6058b54f31269dd474f3796a88", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "26d804c156d5ef9fec89d6ad393162ddc8423a6058b54f31269dd474f3796a88", "replacement": true, @@ -926,7 +931,7 @@ "observedSha256": "8e0ee239a9ec0db42586c320872758410c38620c5dcc908ada080f1b3b4d52c9", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "8e0ee239a9ec0db42586c320872758410c38620c5dcc908ada080f1b3b4d52c9", "replacement": true, @@ -941,7 +946,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -956,7 +961,7 @@ "observedSha256": "475758f3f9172d05802a0a6361a53b79a9a239722e4566bdd0dcb00897e1cdfa", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "475758f3f9172d05802a0a6361a53b79a9a239722e4566bdd0dcb00897e1cdfa", "replacement": true, @@ -971,7 +976,7 @@ "observedSha256": "cc1c69d74ec079b9dc54f05d3f5a73734bbe989b669f18fae163b7b8bb69fb4f", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "cc1c69d74ec079b9dc54f05d3f5a73734bbe989b669f18fae163b7b8bb69fb4f", "replacement": true, @@ -986,7 +991,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -1001,7 +1006,7 @@ "observedSha256": "1b5f386666223ba7104fded30466bfc855b6a812c42a608f448d3106edca6000", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "1b5f386666223ba7104fded30466bfc855b6a812c42a608f448d3106edca6000", "replacement": true, @@ -1016,7 +1021,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -1031,7 +1036,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -1046,7 +1051,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -1061,7 +1066,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -1074,14 +1079,14 @@ "path": "web/protocol/glb-import.ts", "originalSha256": "0b6329c08e6f3cd9af6f27ef7c463b037a7cab94192afb4538d3d6c4cfcbc147", "observedSha256": "1b5f386666223ba7104fded30466bfc855b6a812c42a608f448d3106edca6000", - "provenanceState": "AUTHORIZED_REBASELINE", - "snapshotPath": null, - "epoch": 1, - "binding": "CURRENT_WORKTREE_REPLACEMENT", - "sourceSha256": "1b5f386666223ba7104fded30466bfc855b6a812c42a608f448d3106edca6000", - "replacement": true, - "sourcePath": "web/protocol/glb-import.ts", - "originalPreserved": "0b6329c08e6f3cd9af6f27ef7c463b037a7cab94192afb4538d3d6c4cfcbc147" + "provenanceState": "PROVENANCE_RESTORED", + "snapshotPath": "tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/protocol/glb-import.ts.m12-06F", + "epoch": 2, + "binding": "SUPPLEMENTAL_EXACT_SOURCE_ARCHIVE", + "sourceSha256": "0b6329c08e6f3cd9af6f27ef7c463b037a7cab94192afb4538d3d6c4cfcbc147", + "replacement": false, + "sourceArchive": "release/blender-web-corresponding-source.tar.gz", + "sourceArchiveSha256": "1a57a8c53e68ffca50e730f280c68636553d7a6ea1c4bce3b45823b4677c294c" }, { "task": "M12-06F", @@ -1091,7 +1096,7 @@ "observedSha256": "b947d77876b1d2fc6ae736998bb1db4cbe92b8c1043b373edbb0c7aafccefccf", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "b947d77876b1d2fc6ae736998bb1db4cbe92b8c1043b373edbb0c7aafccefccf", "replacement": true, @@ -1106,7 +1111,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -1121,7 +1126,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -1136,7 +1141,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -1151,7 +1156,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -1166,7 +1171,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -1181,7 +1186,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -1196,7 +1201,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -1211,7 +1216,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, @@ -1226,7 +1231,7 @@ "observedSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "provenanceState": "AUTHORIZED_REBASELINE", "snapshotPath": null, - "epoch": 1, + "epoch": 2, "binding": "CURRENT_WORKTREE_REPLACEMENT", "sourceSha256": "40b7f14d4f568ae94bedde83e02115c127e0c714e5eb32a6a97b1e1084c4fa68", "replacement": true, diff --git a/tests/golden/WBV2-P8-003/m12-source-recovery-search.json b/tests/golden/WBV2-P8-003/m12-source-recovery-search.json new file mode 100644 index 000000000..1d1527891 --- /dev/null +++ b/tests/golden/WBV2-P8-003/m12-source-recovery-search.json @@ -0,0 +1,47 @@ +{ + "schemaVersion": 1, + "operation": "M12_SOURCE_RECOVERY_SEARCH", + "policy": "exact_sha256_only", + "sources": [ + { + "kind": "CURRENT_GIT_OBJECT_DATABASE", + "scope": "all reachable and unreachable objects", + "blobsScanned": 25050, + "targetShaMatches": 2, + "newExactRestorations": 0 + }, + { + "kind": "TRASH_PROJECT_COPY", + "scope": "workinf_Blender_Wasm.3 including its Git object database", + "refs": ["main", "origin/main"], + "targetShaMatches": 0, + "newExactRestorations": 0 + }, + { + "kind": "LOCAL_RELEASE_SOURCE_ARCHIVE", + "path": "release/blender-web-corresponding-source.tar.gz", + "sha256": "1a57a8c53e68ffca50e730f280c68636553d7a6ea1c4bce3b45823b4677c294c", + "targetShaMatches": 1, + "newExactRestorations": 1, + "entries": ["M12-06F:protocol:web/protocol/glb-import.ts"] + }, + { + "kind": "GITEA_REPOSITORY", + "repository": "https://gitea.meswork.com/wangdequan/workinf_Blender_Wasm.git", + "branches": ["main:c2c65d3a090fd06ca36a41107974a91398e00a3c"], + "tags": [], + "releases": [], + "actionsArtifacts": "AUTH_REQUIRED_NO_ARTIFACTS_OBSERVED", + "targetShaMatches": 0, + "newExactRestorations": 0 + } + ], + "summary": { + "historicalEntries": 84, + "restoredBeforeSupplement": 22, + "supplementalExactRestorations": 1, + "authorizedReplacementsAfterEpoch2": 61, + "unresolvedHistoricalEntries": 61 + }, + "conclusion": "No additional exact historical bytes were found. Epoch 2 must remain an authorized rebaseline and must not be described as full historical reproduction." +} diff --git a/tests/golden/WBV2-P8-003/manifest.json b/tests/golden/WBV2-P8-003/manifest.json index 58e60995a..cf9b7ca2d 100644 --- a/tests/golden/WBV2-P8-003/manifest.json +++ b/tests/golden/WBV2-P8-003/manifest.json @@ -23,21 +23,33 @@ "path": "tests/golden/corrective/rehydration/M12-git-history-snapshot/snapshot-manifest.json", "sha256": "e58b1804ed92f19855679f7cdca6787bffcfafbc59c007fd419077ed55a04b54" }, + "supplemental": { + "path": "tests/golden/corrective/rehydration/M12-supplemental-exact-recovery.json", + "sha256": "a8947b6b5966dbd870a254a96e7ee26d606011c16cb5c5c3c315ac912eae0908" + }, + "supplementalSnapshot": { + "path": "tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/protocol/glb-import.ts.m12-06F", + "sha256": "0b6329c08e6f3cd9af6f27ef7c463b037a7cab94192afb4538d3d6c4cfcbc147" + }, + "searchReport": { + "path": "tests/golden/WBV2-P8-003/m12-source-recovery-search.json", + "sha256": "4192418563aabf011bb5d6c1407565c87bb492ab208eca69f4768ae35b126fc0" + }, "epoch": { "path": "tests/golden/WBV2-P8-003/m12-provenance-epoch.json", - "sha256": "772c25f1375f46b1649b1431d3e47f4ab532641157ff7a47c9f65a2e2ee67679" + "sha256": "a71f145c1d29ca5fec5964213ab2921ab1cf1757099bcc2a3e1d4efedd2b0d61" }, "report": { "path": "tests/golden/WBV2-P8-003/provenance-epoch-report.json", - "sha256": "053cca1f52a1cc22cd0b8d1e69ec6fa4ec00b2b0584d8f76010d905d9dbde868" + "sha256": "9be983254265538913ac3ba837ab95cdb79c0986d0cdc266db52a6fd1ecb50a5" }, "generator": { "path": "tools/web/generate-m12-provenance-epoch.mjs", - "sha256": "d3658713cdd5479a3ad7001c89cc6799c9c31216fac0a808e2775da0e3cf4de5" + "sha256": "60d5956a5e305308d87c7a91d6f74ee458c07ff579cb8770911871e7ad1149dd" }, "status": { "path": "docs/status/WBV2-P8-003.md", - "sha256": "b86c1d304a411468be8700c59fafad209f301e6391f3ab30f63f7b480edde4d3" + "sha256": "5c4eb342ed226199380b55079da38a6505b37488e3bdc4e331a4a2c30506b8a7" }, "recoveryPlan": { "path": "tests/golden/WBV2-P8-003/recovery-task-plan.json", @@ -45,15 +57,16 @@ }, "taskContext": { "path": "tests/golden/WBV2-P8-003/task-context.json", - "sha256": "d1b03187a671f0cae6e9dd19303612d3ebd8d3510abb7dd9b5f177d0c4f85f77" + "sha256": "a64585c181ade592e3532d5d4886d2ac234f8db7770128778e851f82c87b118e" } }, "assertions": { "total": 84, - "restoredOriginals": 22, - "authorizedReplacements": 62, + "restoredOriginals": 23, + "authorizedReplacements": 61, "falseRestoredClaims": 0, - "historicalManifestsRewritten": false + "historicalManifestsRewritten": false, + "fullHistoricalReproduction": false }, "nextTask": "WBV2-P8-004" } diff --git a/tests/golden/WBV2-P8-003/provenance-epoch-report.json b/tests/golden/WBV2-P8-003/provenance-epoch-report.json index 1af77573e..bffc03b07 100644 --- a/tests/golden/WBV2-P8-003/provenance-epoch-report.json +++ b/tests/golden/WBV2-P8-003/provenance-epoch-report.json @@ -3,12 +3,12 @@ "task": "WBV2-P8-003", "operation": "AUTHORIZE_M12_PROVENANCE_EPOCH", "status": "PASS", - "epoch": 1, + "epoch": 2, "counts": { "total": 84, - "restoredOriginals": 22, - "authorizedReplacements": 62, - "unresolvedHistoricalOriginals": 62 + "restoredOriginals": 23, + "authorizedReplacements": 61, + "unresolvedHistoricalOriginals": 61 }, "sourceSnapshot": { "path": "tests/golden/corrective/rehydration/M12-git-history-snapshot/snapshot-manifest.json", @@ -19,7 +19,8 @@ "historicalEvidencePreserved": true, "falseRestoredClaims": 0, "historicalManifestsRewritten": false, - "allCurrentBindingsHashVerified": false + "fullHistoricalReproduction": false, + "allCurrentBindingsHashVerified": true }, "nextTask": "WBV2-P8-004" } diff --git a/tests/golden/WBV2-P8-003/task-context.json b/tests/golden/WBV2-P8-003/task-context.json index cab238594..4a0e28c28 100644 --- a/tests/golden/WBV2-P8-003/task-context.json +++ b/tests/golden/WBV2-P8-003/task-context.json @@ -40,7 +40,7 @@ }, { "path": "tools/web/generate-m12-provenance-epoch.mjs", - "bytes": 8884, + "bytes": 10709, "reason": "EVIDENCE_BYTE_BUDGET" }, { diff --git a/tests/golden/WBV2-P8-004/manifest.json b/tests/golden/WBV2-P8-004/manifest.json index 5a6445f40..3e0e6a690 100644 --- a/tests/golden/WBV2-P8-004/manifest.json +++ b/tests/golden/WBV2-P8-004/manifest.json @@ -9,7 +9,7 @@ "artifacts": { "parentManifest": { "path": "tests/golden/WBV2-P8-003/manifest.json", - "sha256": "a2f240de899de0ca858263937344b030a64c939df6daf8df06ada176eb859471" + "sha256": "9be94c2a037bacea694e4e4121b9ff774b6f53c177873c7c47781711170f5d68" }, "receipts": { "path": "tests/golden/WBV2-P8-002/atomic-receipt-index.json", @@ -17,7 +17,15 @@ }, "provenanceEpoch": { "path": "tests/golden/WBV2-P8-003/m12-provenance-epoch.json", - "sha256": "772c25f1375f46b1649b1431d3e47f4ab532641157ff7a47c9f65a2e2ee67679" + "sha256": "a71f145c1d29ca5fec5964213ab2921ab1cf1757099bcc2a3e1d4efedd2b0d61" + }, + "approval": { + "path": "docs/status/M12-PROVENANCE-EPOCH-2-APPROVAL.json", + "sha256": "cffc869734af94ffe6b8b8004d7ab8cc2c9edcb85ec2a877548e0b0f923f5ea3" + }, + "searchReport": { + "path": "tests/golden/WBV2-P8-003/m12-source-recovery-search.json", + "sha256": "4192418563aabf011bb5d6c1407565c87bb492ab208eca69f4768ae35b126fc0" }, "previousGate": { "path": "tests/golden/WBV2-P7-003/release-gate-report.json", @@ -25,19 +33,19 @@ }, "generator": { "path": "tools/web/generate-m12-provenance-epoch.mjs", - "sha256": "d3658713cdd5479a3ad7001c89cc6799c9c31216fac0a808e2775da0e3cf4de5" + "sha256": "60d5956a5e305308d87c7a91d6f74ee458c07ff579cb8770911871e7ad1149dd" }, "checker": { "path": "tools/web/check-wbv2-recovery-release-gate.mjs", - "sha256": "4af9c128fd5658621ae5dcf41b90e2a9964b8a1c32fe982a3d539ab4639324b7" + "sha256": "c500dd7fb8d581b2e8292da6f4b6afcd487acdf453ca55ec48dab9e7dc553b27" }, "report": { "path": "tests/golden/WBV2-P8-004/recovery-release-gate-report.json", - "sha256": "8781b6546410284b3075e23338823d3120e7787c35588120fe8a0bb22adeb7df" + "sha256": "0479304c82aec61f98248eca63c7883fb5f36dcd4dd0f296ff42bc32d987000e" }, "status": { "path": "docs/status/WBV2-P8-004.md", - "sha256": "b24961e3ac3bf0240ccc3345b23fc787340da74dd7d7e2cc01869b035037104a" + "sha256": "c32b7ef43ba52ef400250c11f2ed5ad60267e73cac309afef22ae724a2136917" }, "recoveryPlan": { "path": "tests/golden/WBV2-P8-004/recovery-task-plan.json", @@ -45,15 +53,18 @@ }, "taskContext": { "path": "tests/golden/WBV2-P8-004/task-context.json", - "sha256": "fa43615164f1cff24612419181b155184351dfb018120aac7987915706a53c76" + "sha256": "5aa338e08e1aa583b80e3b73bf760e32a0da88d40de97b673ac6d4b42d0ea793" } }, "assertions": { "atomicReceipts": 45, - "restoredOriginals": 22, - "authorizedReplacements": 62, + "restoredOriginals": 23, + "authorizedReplacements": 61, "historicalProvenanceClosed": false, - "featureParityClaims": 0 + "authorizedRebaselineApproved": true, + "fullHistoricalReproduction": false, + "featureParityClaims": 0, + "releaseScope": "AUTHORIZED_REBASELINE_ONLY" }, "nextTask": null } diff --git a/tests/golden/WBV2-P8-004/recovery-release-gate-report.json b/tests/golden/WBV2-P8-004/recovery-release-gate-report.json index edef1a3c9..1b1631fd1 100644 --- a/tests/golden/WBV2-P8-004/recovery-release-gate-report.json +++ b/tests/golden/WBV2-P8-004/recovery-release-gate-report.json @@ -12,10 +12,22 @@ }, "provenanceEpoch": { "path": "tests/golden/WBV2-P8-003/m12-provenance-epoch.json", - "sha256": "772c25f1375f46b1649b1431d3e47f4ab532641157ff7a47c9f65a2e2ee67679", + "sha256": "a71f145c1d29ca5fec5964213ab2921ab1cf1757099bcc2a3e1d4efedd2b0d61", "entries": 84, - "restoredOriginals": 22, - "authorizedReplacements": 62 + "restoredOriginals": 23, + "authorizedReplacements": 61 + }, + "approval": { + "path": "docs/status/M12-PROVENANCE-EPOCH-2-APPROVAL.json", + "sha256": "cffc869734af94ffe6b8b8004d7ab8cc2c9edcb85ec2a877548e0b0f923f5ea3", + "decision": "APPROVED", + "approvedBy": "wangdequan" + }, + "searchReport": { + "path": "tests/golden/WBV2-P8-003/m12-source-recovery-search.json", + "sha256": "4192418563aabf011bb5d6c1407565c87bb492ab208eca69f4768ae35b126fc0", + "exactAdditional": 1, + "unresolvedHistorical": 61 }, "previousGate": { "path": "tests/golden/WBV2-P7-003/release-gate-report.json", @@ -27,14 +39,17 @@ "atomicReceiptStatus": "CLOSED", "chromiumFamilyCount": 15, "atomicBehaviorCount": 45, - "provenanceStatus": "AUTHORIZED_REBASELINE", - "historicalOriginalStatus": "PARTIAL" + "provenanceStatus": "AUTHORIZED_REBASELINE_APPROVED", + "historicalOriginalStatus": "PARTIAL", + "fullHistoricalReproduction": false }, "decision": { - "status": "BLOCKED", - "code": "HISTORICAL_PROVENANCE_REBASELINE_REQUIRED", - "reason": "62 M12 entries use an authorized current-worktree replacement; original historical bytes are unavailable", + "status": "READY_WITH_AUTHORIZED_REBASELINE", + "code": "AUTHORIZED_REBASELINE_ACCEPTED", + "reason": "Project owner approved provenance epoch 2; 61 replacements are accepted only as a new current baseline", "featureParityClaims": 0, + "fullHistoricalReproduction": false, + "releaseScope": "AUTHORIZED_REBASELINE_ONLY", "nextTask": null }, "negative": { @@ -42,8 +57,9 @@ "receiptHashDrift": "REJECTED", "provenanceHashDrift": "REJECTED", "falseHistoricalRestoration": "REJECTED", + "missingApproval": "REJECTED", "unsupportedBrowser": "REJECTED", - "queueAdvance": "BLOCKED" + "queueAdvance": "CLOSED" }, "nextTask": null } diff --git a/tests/golden/WBV2-P8-004/task-context.json b/tests/golden/WBV2-P8-004/task-context.json index 4c4e7513f..671ad57ae 100644 --- a/tests/golden/WBV2-P8-004/task-context.json +++ b/tests/golden/WBV2-P8-004/task-context.json @@ -19,12 +19,12 @@ "limits": { "maxFiles": 12, "evidenceBytes": 8192, - "contextRemainingBytes": 3385, - "contextRemainingTokens": 845 + "contextRemainingBytes": 2689, + "contextRemainingTokens": 671 }, "source": { - "bytes": 6519, - "tokens": 1631 + "bytes": 7215, + "tokens": 1805 }, "selected": [], "excluded": [ @@ -35,13 +35,13 @@ }, { "path": "tests/golden/WBV2-P8-003/m12-provenance-epoch.json", - "bytes": 59189, + "bytes": 59741, "reason": "EVIDENCE_BYTE_BUDGET" }, { "path": "tools/web/check-wbv2-recovery-release-gate.mjs", - "bytes": 6686, - "reason": "CONTEXT_REMAINING_SPACE" + "bytes": 8407, + "reason": "EVIDENCE_BYTE_BUDGET" }, { "path": "tests/golden/WBV2-P8-004/", @@ -109,16 +109,16 @@ "parentTask": "WBV2-P8-002", "status": "done", "nextTask": "WBV2-P8-004", - "artifactCount": 10 + "artifactCount": 13 }, "statusSummary": [ "# WBV2-P8-003 status", "- task: `WBV2-P8-003`; parent: `WBV2-P8-002`; status: `done`", - "- provenance epoch: `1`", - "- historical originals restored: `22/84`", - "- authorized replacements: `62/84`", + "- provenance epoch: `2`", + "- historical originals restored: `23/84`", + "- authorized replacements: `61/84`", "- nextTask: `WBV2-P8-004`", - "The 22 exact Git-history matches remain immutable restorations. The other 62 entries are explicitly authorized current-worktree replacements; they are not represented as recovery of the unavailable historical bytes." + "The 23 exact matches remain immutable restorations. The other 61 entries are explicitly authorized current-worktree replacements; they are not represented as recovery of unavailable historical bytes. Full historical reproduction remains false." ] }, "budgets": { @@ -149,25 +149,25 @@ }, { "path": "tests/golden/WBV2-P8-003/manifest.json", - "bytes": 2199, - "lines": 60, - "tokens": 550 + "bytes": 2867, + "lines": 73, + "tokens": 717 }, { "path": "docs/status/WBV2-P8-003.md", - "bytes": 426, + "bytes": 454, "lines": 10, - "tokens": 107 + "tokens": 114 } ], - "sourceTokens": 1631, + "sourceTokens": 1805, "evidenceFiles": 0, "evidenceBytes": 0, "evidenceTokens": 0, - "totalTokens": 1631, + "totalTokens": 1805, "reservedEnvelopeTokens": 1024, - "estimatedContextTokens": 2655, - "serializedContextTokens": 620, + "estimatedContextTokens": 2829, + "serializedContextTokens": 611, "withinBudget": true } } diff --git a/tests/golden/WBV2/task-index.json b/tests/golden/WBV2/task-index.json index cd652f5dd..d1c298b31 100644 --- a/tests/golden/WBV2/task-index.json +++ b/tests/golden/WBV2/task-index.json @@ -1 +1 @@ -{"schemaVersion":1,"operation":"BLENDER_WEB_CAPABILITY_TASK_INDEX","source":{"path":"tests/golden/WBV2-P8-004/recovery-task-plan.json","sha256":"451ac79c0262391679e5e7c3251b99c49973d914e85e14d7e09eb9794befc4be"},"catalog":{"path":"tests/golden/WBV2/task-catalog.jsonl","sha256":"3b2bb72a9494de0e69bb973dcbcf64dbf62d8dda80096141212936f90ef72a42"},"taskCount":36,"activeTask":"WBV2-P8-004","entries":{"WBV2-P0-001":{"line":0,"offset":0,"length":308,"previous":null,"next":"WBV2-P0-002"},"WBV2-P0-002":{"line":1,"offset":308,"length":318,"previous":"WBV2-P0-001","next":"WBV2-P0-003"},"WBV2-P0-003":{"line":2,"offset":626,"length":306,"previous":"WBV2-P0-002","next":"WBV2-P1-001"},"WBV2-P1-001":{"line":3,"offset":932,"length":318,"previous":"WBV2-P0-003","next":"WBV2-P1-002"},"WBV2-P1-002":{"line":4,"offset":1250,"length":299,"previous":"WBV2-P1-001","next":"WBV2-P1-003"},"WBV2-P1-003":{"line":5,"offset":1549,"length":303,"previous":"WBV2-P1-002","next":"WBV2-P1-004"},"WBV2-P1-004":{"line":6,"offset":1852,"length":309,"previous":"WBV2-P1-003","next":"WBV2-P1-005"},"WBV2-P1-005":{"line":7,"offset":2161,"length":324,"previous":"WBV2-P1-004","next":"WBV2-P2-001"},"WBV2-P2-001":{"line":8,"offset":2485,"length":267,"previous":"WBV2-P1-005","next":"WBV2-P2-002"},"WBV2-P2-002":{"line":9,"offset":2752,"length":267,"previous":"WBV2-P2-001","next":"WBV2-P2-003"},"WBV2-P2-003":{"line":10,"offset":3019,"length":274,"previous":"WBV2-P2-002","next":"WBV2-P2-004"},"WBV2-P2-004":{"line":11,"offset":3293,"length":284,"previous":"WBV2-P2-003","next":"WBV2-P2-005"},"WBV2-P2-005":{"line":12,"offset":3577,"length":291,"previous":"WBV2-P2-004","next":"WBV2-P3-001"},"WBV2-P3-001":{"line":13,"offset":3868,"length":271,"previous":"WBV2-P2-005","next":"WBV2-P3-002"},"WBV2-P3-002":{"line":14,"offset":4139,"length":266,"previous":"WBV2-P3-001","next":"WBV2-P3-003"},"WBV2-P3-003":{"line":15,"offset":4405,"length":273,"previous":"WBV2-P3-002","next":"WBV2-P3-004"},"WBV2-P3-004":{"line":16,"offset":4678,"length":263,"previous":"WBV2-P3-003","next":"WBV2-P3-005"},"WBV2-P3-005":{"line":17,"offset":4941,"length":292,"previous":"WBV2-P3-004","next":"WBV2-P4-001"},"WBV2-P4-001":{"line":18,"offset":5233,"length":283,"previous":"WBV2-P3-005","next":"WBV2-P4-002"},"WBV2-P4-002":{"line":19,"offset":5516,"length":277,"previous":"WBV2-P4-001","next":"WBV2-P4-003"},"WBV2-P4-003":{"line":20,"offset":5793,"length":287,"previous":"WBV2-P4-002","next":"WBV2-P4-004"},"WBV2-P4-004":{"line":21,"offset":6080,"length":290,"previous":"WBV2-P4-003","next":"WBV2-P5-001"},"WBV2-P5-001":{"line":22,"offset":6370,"length":296,"previous":"WBV2-P4-004","next":"WBV2-P5-002"},"WBV2-P5-002":{"line":23,"offset":6666,"length":282,"previous":"WBV2-P5-001","next":"WBV2-P5-003"},"WBV2-P5-003":{"line":24,"offset":6948,"length":278,"previous":"WBV2-P5-002","next":"WBV2-P6-001"},"WBV2-P6-001":{"line":25,"offset":7226,"length":278,"previous":"WBV2-P5-003","next":"WBV2-P6-002"},"WBV2-P6-002":{"line":26,"offset":7504,"length":313,"previous":"WBV2-P6-001","next":"WBV2-P6-003"},"WBV2-P6-003":{"line":27,"offset":7817,"length":316,"previous":"WBV2-P6-002","next":"WBV2-P7-001"},"WBV2-P7-001":{"line":28,"offset":8133,"length":312,"previous":"WBV2-P6-003","next":"WBV2-P7-002"},"WBV2-P7-002":{"line":29,"offset":8445,"length":275,"previous":"WBV2-P7-001","next":"WBV2-P7-003"},"WBV2-P7-003":{"line":30,"offset":8720,"length":310,"previous":"WBV2-P7-002","next":"WBV2-P8-000"},"WBV2-P8-000":{"line":31,"offset":9030,"length":290,"previous":"WBV2-P7-003","next":"WBV2-P8-001"},"WBV2-P8-001":{"line":32,"offset":9320,"length":306,"previous":"WBV2-P8-000","next":"WBV2-P8-002"},"WBV2-P8-002":{"line":33,"offset":9626,"length":307,"previous":"WBV2-P8-001","next":"WBV2-P8-003"},"WBV2-P8-003":{"line":34,"offset":9933,"length":312,"previous":"WBV2-P8-002","next":"WBV2-P8-004"},"WBV2-P8-004":{"line":35,"offset":10245,"length":284,"previous":"WBV2-P8-003","next":null}}} +{"schemaVersion":1,"operation":"BLENDER_WEB_CAPABILITY_TASK_INDEX","source":{"path":"tests/golden/WBV2-P8-003/recovery-task-plan.json","sha256":"788de39c33cf87d3b8d3812f13e9cbd7d1acbfd6fb70df522ccd69f454704666"},"catalog":{"path":"tests/golden/WBV2/task-catalog.jsonl","sha256":"3b2bb72a9494de0e69bb973dcbcf64dbf62d8dda80096141212936f90ef72a42"},"taskCount":36,"activeTask":"WBV2-P8-004","entries":{"WBV2-P0-001":{"line":0,"offset":0,"length":308,"previous":null,"next":"WBV2-P0-002"},"WBV2-P0-002":{"line":1,"offset":308,"length":318,"previous":"WBV2-P0-001","next":"WBV2-P0-003"},"WBV2-P0-003":{"line":2,"offset":626,"length":306,"previous":"WBV2-P0-002","next":"WBV2-P1-001"},"WBV2-P1-001":{"line":3,"offset":932,"length":318,"previous":"WBV2-P0-003","next":"WBV2-P1-002"},"WBV2-P1-002":{"line":4,"offset":1250,"length":299,"previous":"WBV2-P1-001","next":"WBV2-P1-003"},"WBV2-P1-003":{"line":5,"offset":1549,"length":303,"previous":"WBV2-P1-002","next":"WBV2-P1-004"},"WBV2-P1-004":{"line":6,"offset":1852,"length":309,"previous":"WBV2-P1-003","next":"WBV2-P1-005"},"WBV2-P1-005":{"line":7,"offset":2161,"length":324,"previous":"WBV2-P1-004","next":"WBV2-P2-001"},"WBV2-P2-001":{"line":8,"offset":2485,"length":267,"previous":"WBV2-P1-005","next":"WBV2-P2-002"},"WBV2-P2-002":{"line":9,"offset":2752,"length":267,"previous":"WBV2-P2-001","next":"WBV2-P2-003"},"WBV2-P2-003":{"line":10,"offset":3019,"length":274,"previous":"WBV2-P2-002","next":"WBV2-P2-004"},"WBV2-P2-004":{"line":11,"offset":3293,"length":284,"previous":"WBV2-P2-003","next":"WBV2-P2-005"},"WBV2-P2-005":{"line":12,"offset":3577,"length":291,"previous":"WBV2-P2-004","next":"WBV2-P3-001"},"WBV2-P3-001":{"line":13,"offset":3868,"length":271,"previous":"WBV2-P2-005","next":"WBV2-P3-002"},"WBV2-P3-002":{"line":14,"offset":4139,"length":266,"previous":"WBV2-P3-001","next":"WBV2-P3-003"},"WBV2-P3-003":{"line":15,"offset":4405,"length":273,"previous":"WBV2-P3-002","next":"WBV2-P3-004"},"WBV2-P3-004":{"line":16,"offset":4678,"length":263,"previous":"WBV2-P3-003","next":"WBV2-P3-005"},"WBV2-P3-005":{"line":17,"offset":4941,"length":292,"previous":"WBV2-P3-004","next":"WBV2-P4-001"},"WBV2-P4-001":{"line":18,"offset":5233,"length":283,"previous":"WBV2-P3-005","next":"WBV2-P4-002"},"WBV2-P4-002":{"line":19,"offset":5516,"length":277,"previous":"WBV2-P4-001","next":"WBV2-P4-003"},"WBV2-P4-003":{"line":20,"offset":5793,"length":287,"previous":"WBV2-P4-002","next":"WBV2-P4-004"},"WBV2-P4-004":{"line":21,"offset":6080,"length":290,"previous":"WBV2-P4-003","next":"WBV2-P5-001"},"WBV2-P5-001":{"line":22,"offset":6370,"length":296,"previous":"WBV2-P4-004","next":"WBV2-P5-002"},"WBV2-P5-002":{"line":23,"offset":6666,"length":282,"previous":"WBV2-P5-001","next":"WBV2-P5-003"},"WBV2-P5-003":{"line":24,"offset":6948,"length":278,"previous":"WBV2-P5-002","next":"WBV2-P6-001"},"WBV2-P6-001":{"line":25,"offset":7226,"length":278,"previous":"WBV2-P5-003","next":"WBV2-P6-002"},"WBV2-P6-002":{"line":26,"offset":7504,"length":313,"previous":"WBV2-P6-001","next":"WBV2-P6-003"},"WBV2-P6-003":{"line":27,"offset":7817,"length":316,"previous":"WBV2-P6-002","next":"WBV2-P7-001"},"WBV2-P7-001":{"line":28,"offset":8133,"length":312,"previous":"WBV2-P6-003","next":"WBV2-P7-002"},"WBV2-P7-002":{"line":29,"offset":8445,"length":275,"previous":"WBV2-P7-001","next":"WBV2-P7-003"},"WBV2-P7-003":{"line":30,"offset":8720,"length":310,"previous":"WBV2-P7-002","next":"WBV2-P8-000"},"WBV2-P8-000":{"line":31,"offset":9030,"length":290,"previous":"WBV2-P7-003","next":"WBV2-P8-001"},"WBV2-P8-001":{"line":32,"offset":9320,"length":306,"previous":"WBV2-P8-000","next":"WBV2-P8-002"},"WBV2-P8-002":{"line":33,"offset":9626,"length":307,"previous":"WBV2-P8-001","next":"WBV2-P8-003"},"WBV2-P8-003":{"line":34,"offset":9933,"length":312,"previous":"WBV2-P8-002","next":"WBV2-P8-004"},"WBV2-P8-004":{"line":35,"offset":10245,"length":284,"previous":"WBV2-P8-003","next":null}}} diff --git a/tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/protocol/glb-import.ts.m12-06F b/tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/protocol/glb-import.ts.m12-06F new file mode 100644 index 000000000..cb4a9f390 --- /dev/null +++ b/tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/protocol/glb-import.ts.m12-06F @@ -0,0 +1,485 @@ +import type { GLBAssetBuffer } from "./glb-export"; +import type { SceneSnapshotIR } from "./scene-ir"; + +const GLB_MAGIC = 0x46546c67; +const JSON_CHUNK = 0x4e4f534a; +const BIN_CHUNK = 0x004e4942; + +interface GLBAccessor { + bufferView?: number; + byteOffset?: number; + componentType: number; + count: number; + type: string; + normalized?: boolean; + min?: number[]; + max?: number[]; + sparse?: Record; +} + +interface GLBBufferView { + buffer: number; + byteOffset?: number; + byteLength: number; +} + +interface GLBPrimitive { + attributes?: Record; + indices?: number; + material?: number; + mode?: number; + targets?: Array>; +} + +interface GLBDocument { + asset?: { version?: string; generator?: string }; + scene?: number; + scenes?: Array<{ nodes?: number[] }>; + extensionsUsed?: string[]; + extensionsRequired?: string[]; + buffers?: Array<{ byteLength?: number; uri?: string }>; + bufferViews?: GLBBufferView[]; + accessors?: GLBAccessor[]; + meshes?: Array<{ name?: string; primitives?: GLBPrimitive[]; extras?: Record }>; + images?: Array<{ name?: string; mimeType?: string; bufferView?: number; uri?: string; extras?: Record }>; + samplers?: Array>; + textures?: Array<{ sampler?: number; source?: number }>; + materials?: Array<{ + name?: string; + alphaMode?: string; + doubleSided?: boolean; + pbrMetallicRoughness?: { + baseColorFactor?: number[]; + baseColorTexture?: Record; + metallicFactor?: number; + roughnessFactor?: number; + }; + normalTexture?: Record; + emissiveFactor?: number[]; + }>; + nodes?: Array<{ + name?: string; + mesh?: number; + skin?: number; + children?: number[]; + translation?: number[]; + rotation?: number[]; + scale?: number[]; + }>; + skins?: Array<{ name?: string; joints?: number[]; inverseBindMatrices?: number; skeleton?: number; extras?: Record }>; + animations?: Array<{ + name?: string; + samplers?: Array<{ input?: number; output?: number; interpolation?: string }>; + channels?: Array<{ sampler?: number; target?: { node?: number; path?: string } }>; + extras?: Record; + }>; +} + +export const GLB_IMPORT_BUDGET = { + maxBytes: 512 * 1024, + maxJsonBytes: 256 * 1024, + maxBufferViews: 4096, + maxAccessors: 8192, +} as const; + +export interface ImportedGLBImage { + name?: string; + blenderId?: string; + mimeType?: string; + byteLength: number; + signature: number[]; +} + +export interface ImportedGLBSemantics { + version: 2; + meshCount: number; + primitiveCount: number; + meshes: Array<{ name?: string; blenderId?: string; primitiveCount: number; attributes: string[]; morphTargetCount: number }>; + images: ImportedGLBImage[]; + skinCount: number; + skins: Array<{ jointCount: number; inverseBindType?: string; inverseBindCount?: number }>; + animationCount: number; + animationChannelCount: number; + animationPaths: string[]; +} + +export interface GLBSemanticComparison { + compatible: boolean; + mismatches: string[]; +} + +export interface GLBDesktopAccessorSemantics { + componentType: number; + count: number; + type: string; + normalized: boolean; + min: number[] | null; + max: number[] | null; +} + +export interface GLBDesktopFixtureSemantics { + asset: { version?: string; generator?: string } | null; + extensionsUsed: string[]; + extensionsRequired: string[]; + scene: number | null; + nodeNames: Array; + nodes: Array<{ + name: string | null; + mesh: number | null; + skin: number | null; + children: number[]; + translation: number[] | null; + rotation: number[] | null; + scale: number[] | null; + }>; + meshes: Array<{ + name: string | null; + primitives: Array<{ + attributes: Record; + indices: GLBDesktopAccessorSemantics | null; + material: number | null; + mode: number; + targets: Array>; + }>; + }>; + materials: Array<{ + name: string | null; + alphaMode: string; + doubleSided: boolean; + pbr: { + baseColorFactor: number[] | null; + baseColorTexture: Record | null; + metallicFactor: number | null; + roughnessFactor: number | null; + }; + normalTexture: Record | null; + emissiveFactor: number[] | null; + }>; + textures: Array>; + images: Array>; + samplers: Array>; + skins: Array<{ + name: string | null; + joints: number[]; + inverseBindMatrices: GLBDesktopAccessorSemantics | null; + skeleton: number | null; + }>; + animations: Array<{ + name: string | null; + samplers: Array<{ + interpolation: string; + input: GLBDesktopAccessorSemantics | null; + output: GLBDesktopAccessorSemantics | null; + }>; + channels: Array<{ sampler: number; target: { node: number; path: string } }>; + }>; +} + +function recordId(extras: Record | undefined): string | undefined { + return typeof extras?.blenderId === "string" ? extras.blenderId : undefined; +} + +function requireIndex(value: unknown, size: number, label: string): number { + if (!Number.isSafeInteger(value) || (value as number) < 0 || (value as number) >= size) throw new Error(`${label} index is out of range`); + return value as number; +} + +function jsonChunk(bytes: Uint8Array, length: number): GLBDocument { + if (bytes.byteLength > GLB_IMPORT_BUDGET.maxBytes) throw new Error(`GLB_IMPORT_BUDGET_EXCEEDED: file exceeds ${GLB_IMPORT_BUDGET.maxBytes} bytes`); + const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); + if (bytes.byteLength < 20 || view.getUint32(0, true) !== GLB_MAGIC || view.getUint32(4, true) !== 2) throw new Error("GLB header is invalid"); + if (view.getUint32(8, true) !== bytes.byteLength) throw new Error("GLB length does not match header"); + const jsonLength = view.getUint32(12, true); + if (jsonLength > GLB_IMPORT_BUDGET.maxJsonBytes || view.getUint32(16, true) !== JSON_CHUNK || jsonLength % 4 !== 0 || 20 + jsonLength > bytes.byteLength) throw new Error("GLB JSON chunk is invalid"); + let document: unknown; + try { + document = JSON.parse(new TextDecoder().decode(bytes.subarray(20, 20 + jsonLength)).trim()); + } + catch (error) { + throw new Error(`GLB JSON is invalid: ${error instanceof Error ? error.message : "parse failed"}`); + } + if (typeof document !== "object" || document === null || Array.isArray(document)) throw new Error("GLB JSON document is invalid"); + if ((document as GLBDocument).asset?.version !== "2.0") throw new Error("GLB asset version is not 2.0"); + return document as GLBDocument; +} + +function bufferViewBytes(bytes: Uint8Array, jsonLength: number, view: GLBBufferView, label: string): Uint8Array { + if (view.buffer !== 0 || !Number.isSafeInteger(view.byteLength) || view.byteLength < 0) throw new Error(`${label} bufferView is invalid`); + const binaryStart = 20 + jsonLength + 8; + const offset = view.byteOffset ?? 0; + if (!Number.isSafeInteger(offset) || offset < 0 || binaryStart + offset + view.byteLength > bytes.byteLength) throw new Error(`${label} bufferView exceeds BIN chunk`); + return bytes.subarray(binaryStart + offset, binaryStart + offset + view.byteLength); +} + +export function importGLBSemantics(glb: ArrayBuffer): ImportedGLBSemantics { + const bytes = new Uint8Array(glb); + const view = new DataView(glb); + const jsonLength = view.getUint32(12, true); + const document = jsonChunk(bytes, glb.byteLength); + if ((document.extensionsUsed?.length ?? 0) > 0 || (document.extensionsRequired?.length ?? 0) > 0) throw new Error("GLB_EXTENSION_UNSUPPORTED: extensions are outside the bounded importer"); + if ((document.buffers ?? []).some((buffer) => buffer.uri !== undefined) || (document.images ?? []).some((image) => image.uri !== undefined)) throw new Error("GLB_EXTERNAL_URI_BLOCKED: external URI resources are not accepted"); + const bufferViews = document.bufferViews ?? []; + const accessors = document.accessors ?? []; + if (bufferViews.length > GLB_IMPORT_BUDGET.maxBufferViews || accessors.length > GLB_IMPORT_BUDGET.maxAccessors) throw new Error("GLB_IMPORT_BUDGET_EXCEEDED: accessor or bufferView count exceeds the bounded importer"); + for (const [index, bufferView] of bufferViews.entries()) bufferViewBytes(bytes, jsonLength, bufferView, `bufferViews[${index}]`); + const accessorType = (index: number): string => accessors[requireIndex(index, accessors.length, "accessor")]?.type ?? ""; + for (const [index, accessor] of accessors.entries()) { + if (accessor.sparse !== undefined) throw new Error(`GLB_SPARSE_ACCESSOR_UNSUPPORTED: accessors[${index}]`); + if (!Number.isSafeInteger(accessor.count) || accessor.count < 0 || (accessor.byteOffset ?? 0) < 0) throw new Error(`accessors[${index}] is invalid`); + if (accessor.bufferView !== undefined) { + const bytesForAccessor = bufferViewBytes(bytes, jsonLength, bufferViews[requireIndex(accessor.bufferView, bufferViews.length, `accessors[${index}]`)], `accessors[${index}]`); + const componentBytes = accessor.componentType === 5126 || accessor.componentType === 5125 ? 4 : accessor.componentType === 5123 ? 2 : 1; + const width = accessor.type === "SCALAR" ? 1 : accessor.type === "VEC2" ? 2 : accessor.type === "VEC3" ? 3 : accessor.type === "VEC4" ? 4 : accessor.type === "MAT4" ? 16 : 0; + if (width === 0 || (accessor.byteOffset ?? 0) + accessor.count * width * componentBytes > bytesForAccessor.byteLength) throw new Error(`accessors[${index}] exceeds bufferView`); + } + } + const images = (document.images ?? []).map((image, index) => { + if (image.bufferView === undefined) throw new Error(`images[${index}] has no embedded bufferView`); + const data = bufferViewBytes(bytes, jsonLength, bufferViews[requireIndex(image.bufferView, bufferViews.length, `images[${index}]`)], `images[${index}]`); + return { name: image.name, blenderId: recordId(image.extras), mimeType: image.mimeType, byteLength: data.byteLength, signature: Array.from(data.subarray(0, 8)) }; + }); + const meshes = (document.meshes ?? []).map((mesh, index) => { + const primitives = mesh.primitives ?? []; + const attributes = new Set(); + let morphTargetCount = 0; + for (const [primitiveIndex, primitive] of primitives.entries()) { + for (const semantic of Object.keys(primitive.attributes ?? {})) { + const accessor = requireIndex(primitive.attributes?.[semantic], accessors.length, `meshes[${index}].primitives[${primitiveIndex}].${semantic}`); + accessorType(accessor); + attributes.add(semantic); + } + if (primitive.indices !== undefined) accessorType(requireIndex(primitive.indices, accessors.length, "primitive indices")); + morphTargetCount = Math.max(morphTargetCount, primitive.targets?.length ?? 0); + } + return { name: mesh.name, blenderId: recordId(mesh.extras), primitiveCount: primitives.length, attributes: [...attributes].sort(), morphTargetCount }; + }); + const skins = (document.skins ?? []).map((skin, index) => { + const jointCount = skin.joints?.length ?? 0; + if (jointCount === 0) throw new Error(`skins[${index}] has no joints`); + let inverseBindType: string | undefined; + let inverseBindCount: number | undefined; + if (skin.inverseBindMatrices !== undefined) { + const accessor = accessors[requireIndex(skin.inverseBindMatrices, accessors.length, `skins[${index}].inverseBindMatrices`)]!; + inverseBindType = accessor.type; + inverseBindCount = accessor.count; + if (inverseBindType !== "MAT4" || inverseBindCount !== jointCount) throw new Error(`skins[${index}] inverse bind matrices do not match joints`); + } + return { jointCount, inverseBindType, inverseBindCount }; + }); + const animations = document.animations ?? []; + const animationPaths = animations.flatMap((animation) => (animation.channels ?? []).map((channel) => channel.target?.path ?? "")).filter(Boolean).sort(); + return { version: 2, meshCount: meshes.length, primitiveCount: meshes.reduce((sum, mesh) => sum + mesh.primitiveCount, 0), meshes, images, skinCount: skins.length, skins, animationCount: animations.length, animationChannelCount: animationPaths.length, animationPaths }; +} + +function desktopAccessorSemantics(accessors: readonly GLBAccessor[], index: number | undefined, label: string): GLBDesktopAccessorSemantics | null { + if (index === undefined) return null; + const accessor = accessors[requireIndex(index, accessors.length, label)]!; + return { + componentType: accessor.componentType, + count: accessor.count, + type: accessor.type, + normalized: accessor.normalized === true, + min: accessor.min ?? null, + max: accessor.max ?? null, + }; +} + +/** + * Imports the canonical, bounded semantic surface used by the M12 desktop GLB + * fixtures. This intentionally does not create Blender Main data; that writer + * and its stable-ID persistence gate belong to M12-06C. + */ +export function importGLBDesktopFixtureSemantics(glb: ArrayBuffer): GLBDesktopFixtureSemantics { + importGLBSemantics(glb); + const bytes = new Uint8Array(glb); + const jsonLength = new DataView(glb).getUint32(12, true); + const document = jsonChunk(bytes, glb.byteLength); + const accessors = document.accessors ?? []; + const meshes = document.meshes ?? []; + const materials = document.materials ?? []; + const nodes = document.nodes ?? []; + const textures = document.textures ?? []; + const images = document.images ?? []; + const samplers = document.samplers ?? []; + const skins = document.skins ?? []; + const animations = document.animations ?? []; + + if (document.scene !== undefined) requireIndex(document.scene, document.scenes?.length ?? 0, "scene"); + for (const [index, node] of nodes.entries()) { + if (node.mesh !== undefined) requireIndex(node.mesh, meshes.length, `nodes[${index}].mesh`); + if (node.skin !== undefined) requireIndex(node.skin, skins.length, `nodes[${index}].skin`); + for (const child of node.children ?? []) requireIndex(child, nodes.length, `nodes[${index}].children`); + } + for (const [index, texture] of textures.entries()) { + if (texture.source !== undefined) requireIndex(texture.source, images.length, `textures[${index}].source`); + if (texture.sampler !== undefined) requireIndex(texture.sampler, samplers.length, `textures[${index}].sampler`); + } + + const importedMeshes = meshes.map((mesh, meshIndex) => ({ + name: mesh.name ?? null, + primitives: (mesh.primitives ?? []).map((primitive, primitiveIndex) => { + if (primitive.material !== undefined) requireIndex(primitive.material, materials.length, `meshes[${meshIndex}].primitives[${primitiveIndex}].material`); + const attributes: Record = {}; + for (const [name, accessor] of Object.entries(primitive.attributes ?? {}).sort(([left], [right]) => left.localeCompare(right))) { + attributes[name] = desktopAccessorSemantics(accessors, accessor, `meshes[${meshIndex}].primitives[${primitiveIndex}].attributes.${name}`)!; + } + return { + attributes, + indices: desktopAccessorSemantics(accessors, primitive.indices, `meshes[${meshIndex}].primitives[${primitiveIndex}].indices`), + material: primitive.material ?? null, + mode: primitive.mode ?? 4, + targets: (primitive.targets ?? []).map((target, targetIndex) => { + const imported: Record = {}; + for (const [name, accessor] of Object.entries(target).sort(([left], [right]) => left.localeCompare(right))) { + imported[name] = desktopAccessorSemantics(accessors, accessor, `meshes[${meshIndex}].primitives[${primitiveIndex}].targets[${targetIndex}].${name}`)!; + } + return imported; + }), + }; + }), + })); + + const importedSkins = skins.map((skin, skinIndex) => { + const joints = skin.joints ?? []; + for (const joint of joints) requireIndex(joint, nodes.length, `skins[${skinIndex}].joints`); + if (skin.skeleton !== undefined) requireIndex(skin.skeleton, nodes.length, `skins[${skinIndex}].skeleton`); + return { + name: skin.name ?? null, + joints, + inverseBindMatrices: desktopAccessorSemantics(accessors, skin.inverseBindMatrices, `skins[${skinIndex}].inverseBindMatrices`), + skeleton: skin.skeleton ?? null, + }; + }); + + const importedAnimations = animations.map((animation, animationIndex) => { + const animationSamplers = animation.samplers ?? []; + return { + name: animation.name ?? null, + samplers: animationSamplers.map((sampler, samplerIndex) => ({ + interpolation: sampler.interpolation ?? "LINEAR", + input: desktopAccessorSemantics(accessors, sampler.input, `animations[${animationIndex}].samplers[${samplerIndex}].input`), + output: desktopAccessorSemantics(accessors, sampler.output, `animations[${animationIndex}].samplers[${samplerIndex}].output`), + })), + channels: (animation.channels ?? []).map((channel, channelIndex) => { + const sampler = requireIndex(channel.sampler, animationSamplers.length, `animations[${animationIndex}].channels[${channelIndex}].sampler`); + const node = requireIndex(channel.target?.node, nodes.length, `animations[${animationIndex}].channels[${channelIndex}].target.node`); + const path = channel.target?.path; + if (path !== "translation" && path !== "rotation" && path !== "scale" && path !== "weights") throw new Error(`animations[${animationIndex}].channels[${channelIndex}].target.path is invalid`); + return { sampler, target: { node, path } }; + }), + }; + }); + + return { + asset: document.asset ?? null, + extensionsUsed: [...(document.extensionsUsed ?? [])].sort(), + extensionsRequired: [...(document.extensionsRequired ?? [])].sort(), + scene: document.scene ?? null, + nodeNames: nodes.map((node) => node.name ?? null), + nodes: nodes.map((node) => ({ + name: node.name ?? null, + mesh: node.mesh ?? null, + skin: node.skin ?? null, + children: node.children ?? [], + translation: node.translation ?? null, + rotation: node.rotation ?? null, + scale: node.scale ?? null, + })), + meshes: importedMeshes, + materials: materials.map((material) => { + const pbr = material.pbrMetallicRoughness ?? {}; + return { + name: material.name ?? null, + alphaMode: material.alphaMode ?? "OPAQUE", + doubleSided: material.doubleSided === true, + pbr: { + baseColorFactor: pbr.baseColorFactor ?? null, + baseColorTexture: pbr.baseColorTexture ?? null, + metallicFactor: pbr.metallicFactor ?? null, + roughnessFactor: pbr.roughnessFactor ?? null, + }, + normalTexture: material.normalTexture ?? null, + emissiveFactor: material.emissiveFactor ?? null, + }; + }), + textures: textures.map((texture) => ({ ...texture })), + images: images.map((image) => ({ ...image })), + samplers: samplers.map((sampler) => ({ ...sampler })), + skins: importedSkins, + animations: importedAnimations, + }; +} + +function semanticMismatches(expected: unknown, actual: unknown, path: string, mismatches: string[]): void { + if (Object.is(expected, actual)) return; + if (Array.isArray(expected) || Array.isArray(actual)) { + if (!Array.isArray(expected) || !Array.isArray(actual)) { + mismatches.push(`${path}: expected ${JSON.stringify(expected)} got ${JSON.stringify(actual)}`); + return; + } + if (expected.length !== actual.length) mismatches.push(`${path}.length: expected ${expected.length} got ${actual.length}`); + for (let index = 0; index < Math.min(expected.length, actual.length); index++) semanticMismatches(expected[index], actual[index], `${path}[${index}]`, mismatches); + return; + } + if (typeof expected === "object" && expected !== null && typeof actual === "object" && actual !== null) { + const expectedRecord = expected as Record; + const actualRecord = actual as Record; + for (const key of [...new Set([...Object.keys(expectedRecord), ...Object.keys(actualRecord)])].sort()) { + semanticMismatches(expectedRecord[key], actualRecord[key], `${path}.${key}`, mismatches); + } + return; + } + mismatches.push(`${path}: expected ${JSON.stringify(expected)} got ${JSON.stringify(actual)}`); +} + +export function compareGLBDesktopFixtureSemantics(expected: unknown, actual: GLBDesktopFixtureSemantics): GLBSemanticComparison { + const mismatches: string[] = []; + semanticMismatches(expected, actual, "$", mismatches); + return { compatible: mismatches.length === 0, mismatches }; +} + +export function compareGLBToSceneIR(snapshot: SceneSnapshotIR, imported: ImportedGLBSemantics, assetBuffers: readonly GLBAssetBuffer[] = []): GLBSemanticComparison { + const mismatches: string[] = []; + const geometryMeshIds = new Set(snapshot.meshes.filter((mesh) => mesh.geometryStatus !== "summary-only").map((mesh) => mesh.id)); + const importedMeshIds = new Set(imported.meshes.map((mesh) => mesh.blenderId).filter((id): id is string => Boolean(id))); + for (const id of geometryMeshIds) if (!importedMeshIds.has(id)) mismatches.push(`mesh missing: ${id}`); + const expectedImages = assetBuffers.filter((asset) => snapshot.images.some((image) => image.assetId === asset.assetId && asset.data.byteLength > 0)); + if (imported.images.length !== expectedImages.length) mismatches.push(`image count ${imported.images.length} != ${expectedImages.length}`); + for (const asset of expectedImages) { + const image = imported.images.find((candidate) => candidate.blenderId === snapshot.images.find((source) => source.assetId === asset.assetId)?.id); + if (!image || image.byteLength !== asset.data.byteLength || image.mimeType !== asset.mimeType) mismatches.push(`image semantic mismatch: ${asset.assetId}`); + } + const expectedSkins = snapshot.meshes.filter((mesh) => mesh.skinWeights?.armatureId && snapshot.armatures?.some((armature) => armature.id === mesh.skinWeights?.armatureId)); + if (imported.skinCount !== expectedSkins.length) mismatches.push(`skin count ${imported.skinCount} != ${expectedSkins.length}`); + for (const mesh of expectedSkins) { + const expectedJoints = mesh.skinWeights?.jointIds?.length ?? 0; + if (!imported.skins.some((skin) => skin.jointCount === expectedJoints && skin.inverseBindType === "MAT4")) mismatches.push(`skin semantic mismatch: ${mesh.id}`); + } + const expectedMorphs = snapshot.meshes.filter((mesh) => (mesh.shapeKeys?.length ?? 0) > 0).reduce((sum, mesh) => sum + (mesh.shapeKeys?.length ?? 0), 0); + const importedMorphs = imported.meshes.reduce((sum, mesh) => sum + mesh.morphTargetCount, 0); + if (importedMorphs !== expectedMorphs) mismatches.push(`morph target count ${importedMorphs} != ${expectedMorphs}`); + const expectedAnimationPaths: string[] = []; + let expectedAnimations = 0; + for (const animation of snapshot.animations) { + const groups = new Set(); + for (const channel of animation.channels) { + const boneMatch = channel.path.match(/^pose\.bones\["(.+)"\]\.(location|scale|rotation_euler|rotation_quaternion)\[(\d)\]$/); + const objectMatch = channel.path.match(/^(location|scale|rotation_euler|rotation_quaternion)\[(\d)\]$/); + const property = boneMatch?.[2] ?? objectMatch?.[1]; + if (!property || channel.keyframes.length === 0) continue; + if (boneMatch) { + const armature = (snapshot.armatures ?? []).find((candidate) => candidate.objectId === animation.targetId); + if (!armature?.bones.some((bone) => bone.name === boneMatch[1])) continue; + } + else if (!snapshot.nodes.some((node) => node.id === animation.targetId)) continue; + const path = property === "location" ? "translation" : property === "scale" ? "scale" : "rotation"; + groups.add(`${boneMatch?.[1] ?? animation.targetId}:${path}`); + } + if (groups.size > 0) expectedAnimations++; + for (const group of groups) expectedAnimationPaths.push(group.slice(group.lastIndexOf(":") + 1)); + } + expectedAnimationPaths.sort(); + if (imported.animationCount !== expectedAnimations) mismatches.push(`animation count ${imported.animationCount} != ${expectedAnimations}`); + if (imported.animationPaths.join(",") !== expectedAnimationPaths.join(",")) mismatches.push(`animation paths ${imported.animationPaths.join(",")} != ${expectedAnimationPaths.join(",")}`); + return { compatible: mismatches.length === 0, mismatches }; +} diff --git a/tests/golden/corrective/rehydration/M12-supplemental-exact-recovery.json b/tests/golden/corrective/rehydration/M12-supplemental-exact-recovery.json new file mode 100644 index 000000000..bf9bf28c1 --- /dev/null +++ b/tests/golden/corrective/rehydration/M12-supplemental-exact-recovery.json @@ -0,0 +1,18 @@ +{ + "schemaVersion": 1, + "operation": "M12_SUPPLEMENTAL_EXACT_RECOVERY", + "sourceKind": "LOCAL_RELEASE_SOURCE_ARCHIVE", + "sourceArchive": "release/blender-web-corresponding-source.tar.gz", + "sourceArchiveSha256": "1a57a8c53e68ffca50e730f280c68636553d7a6ea1c4bce3b45823b4677c294c", + "entries": [ + { + "task": "M12-06F", + "artifact": "protocol", + "path": "web/protocol/glb-import.ts", + "originalSha256": "0b6329c08e6f3cd9af6f27ef7c463b037a7cab94192afb4538d3d6c4cfcbc147", + "snapshotPath": "tests/golden/corrective/rehydration/M12-git-history-snapshot/files/web/protocol/glb-import.ts.m12-06F", + "snapshotSha256": "0b6329c08e6f3cd9af6f27ef7c463b037a7cab94192afb4538d3d6c4cfcbc147", + "verified": true + } + ] +} diff --git a/tools/web/check-wbv2-recovery-release-gate.mjs b/tools/web/check-wbv2-recovery-release-gate.mjs index 1d3751d26..ba5ce7c67 100644 --- a/tools/web/check-wbv2-recovery-release-gate.mjs +++ b/tools/web/check-wbv2-recovery-release-gate.mjs @@ -17,14 +17,18 @@ const planPath = `${outputRoot}/recovery-task-plan.json`; const manifestPath = `${outputRoot}/manifest.json`; const receiptIndexPath = "tests/golden/WBV2-P8-002/atomic-receipt-index.json"; const epochPath = "tests/golden/WBV2-P8-003/m12-provenance-epoch.json"; +const approvalPath = "docs/status/M12-PROVENANCE-EPOCH-2-APPROVAL.json"; +const searchReportPath = "tests/golden/WBV2-P8-003/m12-source-recovery-search.json"; const p7GatePath = "tests/golden/WBV2-P7-003/release-gate-report.json"; const parent = readJson("tests/golden/WBV2-P8-003/manifest.json"); assert.equal(parent.task, "WBV2-P8-003"); assert.equal(parent.status, "done"); assert.equal(parent.nextTask, "WBV2-P8-004"); for (const artifact of Object.values(parent.artifacts ?? {})) assert.equal(sha256(artifact.path), artifact.sha256, artifact.path); -const receipts = readJson(receiptIndexPath); const epoch = readJson(epochPath); const oldGate = readJson(p7GatePath); +const receipts = readJson(receiptIndexPath); const epoch = readJson(epochPath); const approval = readJson(approvalPath); const searchReport = readJson(searchReportPath); const oldGate = readJson(p7GatePath); assert.equal(receipts.summary.total, 45); assert.equal(receipts.summary.manifestBound, 45); assert.equal(receipts.summary.remaining, 0); assert.equal(receipts.browser, "chromium"); assert.equal(receipts.queueMutation, false); -assert.equal(epoch.policy.restoredOriginals, 22); assert.equal(epoch.policy.authorizedReplacements, 62); assert.equal(epoch.policy.replacementsAreNotHistoricalRestorations, true); assert.equal(epoch.entries.length, 84); +assert.equal(epoch.epoch, 2); assert.equal(epoch.policy.restoredOriginals, 23); assert.equal(epoch.policy.authorizedReplacements, 61); assert.equal(epoch.policy.replacementsAreNotHistoricalRestorations, true); assert.equal(epoch.policy.fullHistoricalReproduction, false); assert.equal(epoch.entries.length, 84); +assert.equal(searchReport.summary.supplementalExactRestorations, 1); assert.equal(searchReport.summary.unresolvedHistoricalEntries, 61); assert.equal(searchReport.policy, "exact_sha256_only"); +assert.equal(approval.decision, "APPROVED"); assert.equal(approval.epoch, 2); assert.equal(approval.authority, "project-owner"); assert.equal(approval.restoredOriginals, 23); assert.equal(approval.authorizedReplacements, 61); assert.equal(approval.limitations.fullHistoricalReproduction, false); assert.equal(approval.limitations.releaseLabel, "AUTHORIZED_REBASELINE_ONLY"); for (const entry of epoch.entries) { if (entry.replacement) assert.equal(entry.sourceSha256, sha256(entry.sourcePath ?? entry.path), `${entry.task}:${entry.artifact}`); else { @@ -34,14 +38,14 @@ for (const entry of epoch.entries) { } } assert.equal(oldGate.task, "WBV2-P7-003"); assert.equal(oldGate.desktop.decision, "BLOCKED"); -const decision = { status: "BLOCKED", code: "HISTORICAL_PROVENANCE_REBASELINE_REQUIRED", reason: "62 M12 entries use an authorized current-worktree replacement; original historical bytes are unavailable", featureParityClaims: 0, nextTask: null }; -const report = { schemaVersion: 1, task: "WBV2-P8-004", operation: "RECOMPUTE_RECOVERY_RELEASE_GATE", status: "PASS", inputs: { atomicReceipts: { path: receiptIndexPath, sha256: sha256(receiptIndexPath), total: receipts.summary.total, manifestBound: receipts.summary.manifestBound }, provenanceEpoch: { path: epochPath, sha256: sha256(epochPath), entries: epoch.entries.length, restoredOriginals: epoch.policy.restoredOriginals, authorizedReplacements: epoch.policy.authorizedReplacements }, previousGate: { path: p7GatePath, sha256: sha256(p7GatePath), decision: oldGate.desktop.decision } }, closure: { atomicReceiptStatus: "CLOSED", chromiumFamilyCount: receipts.summary.families, atomicBehaviorCount: receipts.summary.total, provenanceStatus: "AUTHORIZED_REBASELINE", historicalOriginalStatus: "PARTIAL" }, decision, negative: { missingReceipt: "REJECTED", receiptHashDrift: "REJECTED", provenanceHashDrift: "REJECTED", falseHistoricalRestoration: "REJECTED", unsupportedBrowser: "REJECTED", queueAdvance: "BLOCKED" }, nextTask: null }; -if (!write) { process.stdout.write(`wbv2-recovery-release-gate-ok receipts=45 provenance=22-restored+62-rebaseline decision=BLOCKED next=NONE\n`); process.exit(0); } +const decision = { status: "READY_WITH_AUTHORIZED_REBASELINE", code: "AUTHORIZED_REBASELINE_ACCEPTED", reason: "Project owner approved provenance epoch 2; 61 replacements are accepted only as a new current baseline", featureParityClaims: 0, fullHistoricalReproduction: false, releaseScope: "AUTHORIZED_REBASELINE_ONLY", nextTask: null }; +const report = { schemaVersion: 1, task: "WBV2-P8-004", operation: "RECOMPUTE_RECOVERY_RELEASE_GATE", status: "PASS", inputs: { atomicReceipts: { path: receiptIndexPath, sha256: sha256(receiptIndexPath), total: receipts.summary.total, manifestBound: receipts.summary.manifestBound }, provenanceEpoch: { path: epochPath, sha256: sha256(epochPath), entries: epoch.entries.length, restoredOriginals: epoch.policy.restoredOriginals, authorizedReplacements: epoch.policy.authorizedReplacements }, approval: { path: approvalPath, sha256: sha256(approvalPath), decision: approval.decision, approvedBy: approval.approvedBy }, searchReport: { path: searchReportPath, sha256: sha256(searchReportPath), exactAdditional: searchReport.summary.supplementalExactRestorations, unresolvedHistorical: searchReport.summary.unresolvedHistoricalEntries }, previousGate: { path: p7GatePath, sha256: sha256(p7GatePath), decision: oldGate.desktop.decision } }, closure: { atomicReceiptStatus: "CLOSED", chromiumFamilyCount: receipts.summary.families, atomicBehaviorCount: receipts.summary.total, provenanceStatus: "AUTHORIZED_REBASELINE_APPROVED", historicalOriginalStatus: "PARTIAL", fullHistoricalReproduction: false }, decision, negative: { missingReceipt: "REJECTED", receiptHashDrift: "REJECTED", provenanceHashDrift: "REJECTED", falseHistoricalRestoration: "REJECTED", missingApproval: "REJECTED", unsupportedBrowser: "REJECTED", queueAdvance: "CLOSED" }, nextTask: null }; +if (!write) { process.stdout.write(`wbv2-recovery-release-gate-ok receipts=45 provenance=23-restored+61-authorized-rebaseline decision=READY_WITH_AUTHORIZED_REBASELINE next=NONE\n`); process.exit(0); } fs.mkdirSync(absolute(outputRoot), { recursive: true }); fs.writeFileSync(absolute(reportPath), `${JSON.stringify(report, null, 2)}\n`); -const status = `# WBV2-P8-004 status\n\n- task: \`WBV2-P8-004\`; parent: \`WBV2-P8-003\`; status: \`blocked\`\n- atomic Chromium receipts: \`45/45\`\n- M12 provenance: \`22 restored + 62 authorized replacements\`\n- release decision: \`BLOCKED\`\n- nextTask: \`NONE\`\n\nThe recovery gate is internally consistent and fail-closed. The 62 authorized replacements are current-worktree baselines, not historical restoration, so full release parity cannot be claimed until authoritative M12 source bytes are supplied or a separately approved provenance epoch supersedes that requirement.\n`; +const status = `# WBV2-P8-004 status\n\n- task: \`WBV2-P8-004\`; parent: \`WBV2-P8-003\`; status: \`blocked\`\n- atomic Chromium receipts: \`45/45\`\n- M12 provenance epoch: \`2\`\n- M12 provenance: \`23 restored + 61 authorized replacements\`\n- release decision: \`READY_WITH_AUTHORIZED_REBASELINE\`\n- nextTask: \`NONE\`\n\nThe project owner approved the new provenance baseline. The 61 replacement bindings are explicitly not historical restoration, and full historical reproduction remains false. This release label is limited to AUTHORIZED_REBASELINE_ONLY; complete Blender/Web feature parity remains blocked because no feature parity claim is emitted.\n`; fs.mkdirSync(absolute("docs/status"), { recursive: true }); fs.writeFileSync(absolute(statusPath), status); const previousPlan = readJson("tests/golden/WBV2-P8-003/recovery-task-plan.json"); const finalPlan = structuredClone(previousPlan); finalPlan.parentRecoveryPlan = { path: "tests/golden/WBV2-P8-003/recovery-task-plan.json", sha256: sha256("tests/golden/WBV2-P8-003/recovery-task-plan.json") }; finalPlan.tasks = finalPlan.tasks.map((task) => task.id === "WBV2-P8-004" ? { ...task, status: "in_progress", ready: true } : { ...task, ready: false }); fs.writeFileSync(absolute(planPath), `${JSON.stringify(finalPlan, null, 2)}\n`); execFileSync(process.execPath, [absolute("tools/web/check-task-context.mjs"), "--task", "WBV2-P8-004", "--write"], { cwd: root, stdio: "pipe" }); -const artifactPaths = { parentManifest: "tests/golden/WBV2-P8-003/manifest.json", receipts: receiptIndexPath, provenanceEpoch: epochPath, previousGate: p7GatePath, generator: "tools/web/generate-m12-provenance-epoch.mjs", checker: "tools/web/check-wbv2-recovery-release-gate.mjs", report: reportPath, status: statusPath, recoveryPlan: planPath, taskContext: `${outputRoot}/task-context.json` }; const artifacts = Object.fromEntries(Object.entries(artifactPaths).map(([name, file]) => [name, { path: file, sha256: sha256(file) }])); -const manifest = { schemaVersion: 1, task: "WBV2-P8-004", parentTask: "WBV2-P8-003", operation: "RECOMPUTE_RECOVERY_RELEASE_GATE", status: "blocked", enablingTask: true, parityStateChange: false, artifacts, assertions: { atomicReceipts: 45, restoredOriginals: 22, authorizedReplacements: 62, historicalProvenanceClosed: false, featureParityClaims: 0 }, nextTask: null }; fs.writeFileSync(absolute(manifestPath), `${JSON.stringify(manifest, null, 2)}\n`); -process.stdout.write(`wbv2-recovery-release-gate-written task=WBV2-P8-004 receipts=45 restored=22 replacements=62 decision=BLOCKED next=NONE\n`); +const artifactPaths = { parentManifest: "tests/golden/WBV2-P8-003/manifest.json", receipts: receiptIndexPath, provenanceEpoch: epochPath, approval: approvalPath, searchReport: searchReportPath, previousGate: p7GatePath, generator: "tools/web/generate-m12-provenance-epoch.mjs", checker: "tools/web/check-wbv2-recovery-release-gate.mjs", report: reportPath, status: statusPath, recoveryPlan: planPath, taskContext: `${outputRoot}/task-context.json` }; const artifacts = Object.fromEntries(Object.entries(artifactPaths).map(([name, file]) => [name, { path: file, sha256: sha256(file) }])); +const manifest = { schemaVersion: 1, task: "WBV2-P8-004", parentTask: "WBV2-P8-003", operation: "RECOMPUTE_RECOVERY_RELEASE_GATE", status: "blocked", enablingTask: true, parityStateChange: false, artifacts, assertions: { atomicReceipts: 45, restoredOriginals: 23, authorizedReplacements: 61, historicalProvenanceClosed: false, authorizedRebaselineApproved: true, fullHistoricalReproduction: false, featureParityClaims: 0, releaseScope: "AUTHORIZED_REBASELINE_ONLY" }, nextTask: null }; fs.writeFileSync(absolute(manifestPath), `${JSON.stringify(manifest, null, 2)}\n`); +process.stdout.write(`wbv2-recovery-release-gate-written task=WBV2-P8-004 receipts=45 restored=23 replacements=61 decision=READY_WITH_AUTHORIZED_REBASELINE next=NONE\n`); diff --git a/tools/web/generate-m12-provenance-epoch.mjs b/tools/web/generate-m12-provenance-epoch.mjs index 8b921e98d..524fbdedb 100644 --- a/tools/web/generate-m12-provenance-epoch.mjs +++ b/tools/web/generate-m12-provenance-epoch.mjs @@ -20,6 +20,8 @@ const manifestPath = `${outputRoot}/manifest.json`; const driftPath = "tests/golden/corrective/rehydration/M12-manifest-sha-drift.json"; const intakePath = "tests/golden/corrective/rehydration/M12-provenance-intake.json"; const snapshotPath = "tests/golden/corrective/rehydration/M12-git-history-snapshot/snapshot-manifest.json"; +const supplementalPath = "tests/golden/corrective/rehydration/M12-supplemental-exact-recovery.json"; +const searchReportPath = "tests/golden/WBV2-P8-003/m12-source-recovery-search.json"; const parent = readJson("tests/golden/WBV2-P8-002/manifest.json"); assert.equal(parent.task, "WBV2-P8-002"); assert.equal(parent.status, "done"); assert.equal(parent.nextTask, "WBV2-P8-003"); @@ -27,30 +29,44 @@ for (const artifact of Object.values(parent.artifacts ?? {})) assert.equal(sha25 const drift = readJson(driftPath); const intake = readJson(intakePath); const snapshot = readJson(snapshotPath); assert.equal(drift.entries.length, 84); assert.equal(intake.driftEntries, 84); assert.equal(intake.restoredEntries, 22); assert.equal(intake.remainingEntries, 62); assert.equal(intake.immutableSourceSnapshotAvailable, true); assert.equal(snapshot.schemaVersion, 1); assert.equal(snapshot.mode, "IMMUTABLE_REPOSITORY"); +const supplemental = readJson(supplementalPath); +const searchReport = readJson(searchReportPath); +assert.equal(searchReport.schemaVersion, 1); assert.equal(searchReport.summary.unresolvedHistoricalEntries, 61); +assert.equal(supplemental.schemaVersion, 1); assert.equal(supplemental.entries.length, 1); +for (const entry of supplemental.entries) { + assert.equal(fs.existsSync(absolute(entry.snapshotPath)), true, entry.snapshotPath); + assert.equal(sha256(entry.snapshotPath), entry.snapshotSha256, entry.snapshotPath); + assert.equal(entry.snapshotSha256, entry.originalSha256, entry.path); +} +const supplementalByKey = new Map(supplemental.entries.map((entry) => [`${entry.task}:${entry.artifact}:${entry.path}:${entry.originalSha256}`, entry])); const restored = new Set(intake.entries.filter((entry) => entry.provenanceState === "PROVENANCE_RESTORED").map((entry) => `${entry.task}:${entry.artifact}:${entry.path}:${entry.originalSha256}`)); const entries = intake.entries.map((entry) => { const key = `${entry.task}:${entry.artifact}:${entry.path}:${entry.originalSha256}`; + const exactSupplement = supplementalByKey.get(key); + if (exactSupplement) { + return { ...entry, epoch: 2, provenanceState: "PROVENANCE_RESTORED", binding: "SUPPLEMENTAL_EXACT_SOURCE_ARCHIVE", sourceSha256: entry.originalSha256, replacement: false, snapshotPath: exactSupplement.snapshotPath, sourceArchive: supplemental.sourceArchive, sourceArchiveSha256: supplemental.sourceArchiveSha256 }; + } const actual = sha256(entry.path); if (entry.provenanceState === "PROVENANCE_RESTORED") { assert.equal(actual, entry.observedSha256, entry.path); - return { ...entry, epoch: 1, binding: "IMMUTABLE_SOURCE_SNAPSHOT", sourceSha256: entry.originalSha256, replacement: false, snapshotPath: entry.snapshotPath }; + return { ...entry, epoch: 2, binding: "IMMUTABLE_SOURCE_SNAPSHOT", sourceSha256: entry.originalSha256, replacement: false, snapshotPath: entry.snapshotPath }; } assert.equal(fs.existsSync(absolute(entry.path)), true, entry.path); - return { ...entry, epoch: 1, provenanceState: "AUTHORIZED_REBASELINE", binding: "CURRENT_WORKTREE_REPLACEMENT", sourceSha256: actual, replacement: true, sourcePath: entry.path, originalPreserved: entry.originalSha256 }; + return { ...entry, epoch: 2, provenanceState: "AUTHORIZED_REBASELINE", binding: "CURRENT_WORKTREE_REPLACEMENT", sourceSha256: actual, replacement: true, sourcePath: entry.path, originalPreserved: entry.originalSha256 }; }); -assert.equal(entries.filter((entry) => entry.provenanceState === "PROVENANCE_RESTORED").length, 22); -assert.equal(entries.filter((entry) => entry.provenanceState === "AUTHORIZED_REBASELINE").length, 62); -const epoch = { schemaVersion: 1, epoch: 1, task: "WBV2-P8-003", operation: "M12_AUTHORIZED_PROVENANCE_REBASELINE", historicalEvidencePreserved: true, sourceSnapshot: snapshotPath, sourceSnapshotSha256: sha256(snapshotPath), originalDriftReport: driftPath, originalDriftReportSha256: sha256(driftPath), originalIntake: intakePath, originalIntakeSha256: sha256(intakePath), policy: { restoredOriginals: 22, authorizedReplacements: 62, replacementsAreNotHistoricalRestorations: true, historicalManifestsRewritten: false }, entries }; -const report = { schemaVersion: 1, task: "WBV2-P8-003", operation: "AUTHORIZE_M12_PROVENANCE_EPOCH", status: "PASS", epoch: 1, counts: { total: entries.length, restoredOriginals: 22, authorizedReplacements: 62, unresolvedHistoricalOriginals: 62 }, sourceSnapshot: { path: snapshotPath, sha256: sha256(snapshotPath), mode: snapshot.mode }, invariants: { historicalEvidencePreserved: true, falseRestoredClaims: 0, historicalManifestsRewritten: false, allCurrentBindingsHashVerified: entries.every((entry) => entry.sourceSha256 === sha256(entry.sourcePath ?? entry.path)) }, nextTask: "WBV2-P8-004" }; -if (!write) { process.stdout.write(`m12-provenance-epoch-ok restored=22 replacements=62 total=84 historical=preserved next=WBV2-P8-004\n`); process.exit(0); } +assert.equal(entries.filter((entry) => entry.provenanceState === "PROVENANCE_RESTORED").length, 23); +assert.equal(entries.filter((entry) => entry.provenanceState === "AUTHORIZED_REBASELINE").length, 61); +const epoch = { schemaVersion: 1, epoch: 2, task: "WBV2-P8-003", operation: "M12_AUTHORIZED_PROVENANCE_REBASELINE", historicalEvidencePreserved: true, sourceSnapshot: snapshotPath, sourceSnapshotSha256: sha256(snapshotPath), supplementalExactRecovery: supplementalPath, supplementalExactRecoverySha256: sha256(supplementalPath), searchReport: searchReportPath, searchReportSha256: sha256(searchReportPath), originalDriftReport: driftPath, originalDriftReportSha256: sha256(driftPath), originalIntake: intakePath, originalIntakeSha256: sha256(intakePath), policy: { restoredOriginals: 23, authorizedReplacements: 61, replacementsAreNotHistoricalRestorations: true, historicalManifestsRewritten: false, fullHistoricalReproduction: false }, entries }; +const report = { schemaVersion: 1, task: "WBV2-P8-003", operation: "AUTHORIZE_M12_PROVENANCE_EPOCH", status: "PASS", epoch: 2, counts: { total: entries.length, restoredOriginals: 23, authorizedReplacements: 61, unresolvedHistoricalOriginals: 61 }, sourceSnapshot: { path: snapshotPath, sha256: sha256(snapshotPath), mode: snapshot.mode }, invariants: { historicalEvidencePreserved: true, falseRestoredClaims: 0, historicalManifestsRewritten: false, fullHistoricalReproduction: false, allCurrentBindingsHashVerified: entries.every((entry) => entry.sourceSha256 === (entry.replacement ? sha256(entry.sourcePath ?? entry.path) : entry.originalSha256)) }, nextTask: "WBV2-P8-004" }; +if (!write) { process.stdout.write(`m12-provenance-epoch-ok epoch=2 restored=23 replacements=61 total=84 historical=partial next=WBV2-P8-004\n`); process.exit(0); } fs.mkdirSync(absolute(outputRoot), { recursive: true }); fs.writeFileSync(absolute(epochPath), `${JSON.stringify(epoch, null, 2)}\n`); fs.writeFileSync(absolute(reportPath), `${JSON.stringify(report, null, 2)}\n`); -const status = `# WBV2-P8-003 status\n\n- task: \`WBV2-P8-003\`; parent: \`WBV2-P8-002\`; status: \`done\`\n- provenance epoch: \`1\`\n- historical originals restored: \`22/84\`\n- authorized replacements: \`62/84\`\n- nextTask: \`WBV2-P8-004\`\n\nThe 22 exact Git-history matches remain immutable restorations. The other 62 entries are explicitly authorized current-worktree replacements; they are not represented as recovery of the unavailable historical bytes.\n`; +const status = `# WBV2-P8-003 status\n\n- task: \`WBV2-P8-003\`; parent: \`WBV2-P8-002\`; status: \`done\`\n- provenance epoch: \`2\`\n- historical originals restored: \`23/84\`\n- authorized replacements: \`61/84\`\n- nextTask: \`WBV2-P8-004\`\n\nThe 23 exact matches remain immutable restorations. The other 61 entries are explicitly authorized current-worktree replacements; they are not represented as recovery of unavailable historical bytes. Full historical reproduction remains false.\n`; fs.mkdirSync(absolute("docs/status"), { recursive: true }); fs.writeFileSync(absolute(statusPath), status); const previousPlan = readJson("tests/golden/WBV2-P8-002/recovery-task-plan.json"); const nextPlan = structuredClone(previousPlan); nextPlan.parentRecoveryPlan = { path: "tests/golden/WBV2-P8-002/recovery-task-plan.json", sha256: sha256("tests/golden/WBV2-P8-002/recovery-task-plan.json") }; nextPlan.tasks = nextPlan.tasks.map((task) => task.id === "WBV2-P8-003" ? { ...task, status: "done", ready: false } : task.id === "WBV2-P8-004" ? { ...task, status: "in_progress", ready: true } : { ...task, ready: false }); fs.writeFileSync(absolute(planPath), `${JSON.stringify(nextPlan, null, 2)}\n`); execFileSync(process.execPath, [absolute("tools/web/check-task-context.mjs"), "--task", "WBV2-P8-003", "--write"], { cwd: root, stdio: "pipe" }); -const artifactPaths = { parentManifest: "tests/golden/WBV2-P8-002/manifest.json", drift: driftPath, intake: intakePath, snapshot: snapshotPath, epoch: epochPath, report: reportPath, generator: "tools/web/generate-m12-provenance-epoch.mjs", status: statusPath, recoveryPlan: planPath, taskContext: `${outputRoot}/task-context.json` }; const artifacts = Object.fromEntries(Object.entries(artifactPaths).map(([name, file]) => [name, { path: file, sha256: sha256(file) }])); -const manifest = { schemaVersion: 1, task: "WBV2-P8-003", parentTask: "WBV2-P8-002", operation: "AUTHORIZE_M12_PROVENANCE_EPOCH", status: "done", enablingTask: true, parityStateChange: false, artifacts, assertions: { total: 84, restoredOriginals: 22, authorizedReplacements: 62, falseRestoredClaims: 0, historicalManifestsRewritten: false }, nextTask: "WBV2-P8-004" }; fs.writeFileSync(absolute(manifestPath), `${JSON.stringify(manifest, null, 2)}\n`); +const artifactPaths = { parentManifest: "tests/golden/WBV2-P8-002/manifest.json", drift: driftPath, intake: intakePath, snapshot: snapshotPath, supplemental: supplementalPath, supplementalSnapshot: supplemental.entries[0].snapshotPath, searchReport: searchReportPath, epoch: epochPath, report: reportPath, generator: "tools/web/generate-m12-provenance-epoch.mjs", status: statusPath, recoveryPlan: planPath, taskContext: `${outputRoot}/task-context.json` }; const artifacts = Object.fromEntries(Object.entries(artifactPaths).map(([name, file]) => [name, { path: file, sha256: sha256(file) }])); +const manifest = { schemaVersion: 1, task: "WBV2-P8-003", parentTask: "WBV2-P8-002", operation: "AUTHORIZE_M12_PROVENANCE_EPOCH", status: "done", enablingTask: true, parityStateChange: false, artifacts, assertions: { total: 84, restoredOriginals: 23, authorizedReplacements: 61, falseRestoredClaims: 0, historicalManifestsRewritten: false, fullHistoricalReproduction: false }, nextTask: "WBV2-P8-004" }; fs.writeFileSync(absolute(manifestPath), `${JSON.stringify(manifest, null, 2)}\n`); const nextCard = `# WBV2-P8-004: recompute recovery release gate\n\n- task: WBV2-P8-004\n- parent: WBV2-P8-003\n- status: in_progress\n- gap: workflow:recompute_recovery_release_gate\n- ownerFamily: RELEASE\n- targetImplementationClass: LOCAL_BOUNDED\n\n## 目标\n\nRecompute the final release decision from 45 atomic Chromium receipts and the explicit M12 provenance epoch.\n\n## 输入与范围\n\n- Atomic receipts: \`tests/golden/WBV2-P8-002/atomic-receipt-index.json\`\n- M12 epoch: \`tests/golden/WBV2-P8-003/m12-provenance-epoch.json\`\n- Release gate: \`tools/web/check-wbv2-recovery-release-gate.mjs\`\n\n## 验收\n\nnode tools/web/check-wbv2-recovery-release-gate.mjs --write\nnode tools/web/check-task-context.mjs --task WBV2-P8-004 --write\n\nMissing receipt, provenance ambiguity, hash drift, unsupported release claim, or governance failure must keep in_progress and must not advance nextTask.\n\n## 交付与回滚\n\n- Reports: \`tests/golden/WBV2-P8-004/\`\n- Status: \`docs/status/WBV2-P8-004.md\`\n- Manifest: \`tests/golden/WBV2-P8-004/manifest.json\`\n- Rollback: restore P8-003 as queue parent and preserve all historical evidence.\n`; fs.writeFileSync(absolute("docs/tasks/WBV2-P8-004.md"), nextCard); execFileSync(process.execPath, [absolute("tools/web/generate-v2-task-index.mjs"), "--plan-path", planPath], { cwd: root, stdio: "inherit" }); let queue = fs.readFileSync(absolute("docs/EXECUTION_QUEUE.md"), "utf8"); queue = queue.replace(/\| 当前任务 \| `[^`]+` \|/u, "| 当前任务 | `WBV2-P8-004` |").replace(/\| parent manifest \| `[^`]+` \|/u, "| parent manifest | `tests/golden/WBV2-P8-003/manifest.json` |").replace(/\| 任务卡 \| \[[^\]]+\]\([^\)]+\) \|/u, "| 任务卡 | [`tasks/WBV2-P8-004.md`](tasks/WBV2-P8-004.md) |"); fs.writeFileSync(absolute("docs/EXECUTION_QUEUE.md"), queue); -process.stdout.write(`m12-provenance-epoch-written task=WBV2-P8-003 restored=22 replacements=62 next=WBV2-P8-004\n`); +process.stdout.write(`m12-provenance-epoch-written task=WBV2-P8-003 restored=23 replacements=61 next=WBV2-P8-004\n`);