@qijenchen/design-system 0.1.0-beta.92 → 0.1.0-beta.95

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (493) hide show
  1. package/AGENTS.md +6 -208
  2. package/CLAUDE.md +2 -23
  3. package/dist/components/DataTable/data-table-filter-value-picker.js +4 -0
  4. package/dist/components/DataTable/data-table-filter-value-picker.js.map +1 -1
  5. package/dist/components/DataTable/filter-tree.js +6 -0
  6. package/dist/components/DataTable/filter-tree.js.map +1 -1
  7. package/dist/patterns/element-anatomy/item-anatomy.js +1 -1
  8. package/ds-canonical/adapters/claude/agents/README.md +58 -0
  9. package/ds-canonical/adapters/claude/agents/canonical-reviewer.md +14 -0
  10. package/ds-canonical/adapters/claude-root-instructions.md +24 -0
  11. package/ds-canonical/adapters/claude-settings-base.json +172 -0
  12. package/ds-canonical/commands/README.md +8 -13
  13. package/ds-canonical/commands/gov-status.md +5 -75
  14. package/ds-canonical/fork/AGENTS.md +116 -213
  15. package/ds-canonical/fork/CLAUDE.md +54 -0
  16. package/ds-canonical/fork/codex/agents/skills/bug-fix-rhythm/SKILL.md +207 -0
  17. package/ds-canonical/fork/codex/agents/skills/code-quality-audit/SKILL.md +71 -0
  18. package/ds-canonical/fork/codex/agents/skills/delivery-handoff/SKILL.md +244 -0
  19. package/ds-canonical/fork/codex/agents/skills/delivery-handoff/references/flow-diagram.md +180 -0
  20. package/ds-canonical/fork/codex/agents/skills/delivery-handoff/references/handoff-template.md +177 -0
  21. package/ds-canonical/fork/codex/agents/skills/delivery-handoff/references/inventory-checklist.md +196 -0
  22. package/ds-canonical/fork/codex/agents/skills/independent-review/SKILL.md +29 -19
  23. package/ds-canonical/fork/codex/agents/skills/independent-review/agents/openai.yaml +4 -0
  24. package/ds-canonical/fork/codex/agents/skills/independent-review/references/evidence-contract.md +24 -0
  25. package/ds-canonical/fork/codex/agents/skills/performance-audit/SKILL.md +114 -0
  26. package/ds-canonical/fork/codex/agents/skills/product-ui-audit/SKILL.md +244 -0
  27. package/ds-canonical/fork/codex/agents/skills/product-ui-audit/references/audit-checks.md +250 -0
  28. package/ds-canonical/fork/codex/agents/skills/product-ui-audit/references/common-misuses.md +329 -0
  29. package/ds-canonical/fork/codex/agents/skills/product-ui-audit/references/report-template.md +163 -0
  30. package/ds-canonical/fork/codex/agents/skills/propose-options/SKILL.md +190 -0
  31. package/ds-canonical/fork/codex/agents/skills/prototype/SKILL.md +246 -0
  32. package/ds-canonical/fork/codex/agents/skills/prototype/references/audit-checks.md +39 -0
  33. package/ds-canonical/fork/codex/agents/skills/prototype/references/benchmark-sources.md +94 -0
  34. package/ds-canonical/fork/codex/agents/skills/prototype/references/checkpoints.md +200 -0
  35. package/ds-canonical/fork/codex/agents/skills/prototype/references/evaluation-matrix.md +142 -0
  36. package/ds-canonical/fork/codex/agents/skills/prototype/references/ooux-template.md +198 -0
  37. package/ds-canonical/fork/codex/agents/skills/prototype/references/proposal-template.md +230 -0
  38. package/ds-canonical/fork/codex/agents/skills/scan-similar-bugs/SKILL.md +212 -0
  39. package/ds-canonical/fork/codex/agents/skills/ux-audit/SKILL.md +135 -0
  40. package/ds-canonical/fork/codex/agents/skills/visual-audit/SKILL.md +259 -0
  41. package/ds-canonical/fork/codex/agents/skills/visual-audit/output/.gitkeep +0 -0
  42. package/ds-canonical/fork/codex/agents/skills/visual-audit/references/audit-architecture.md +101 -0
  43. package/ds-canonical/fork/codex/agents/skills/visual-audit/references/visual-checklist.md +297 -0
  44. package/ds-canonical/fork/codex/agents/skills/visual-audit/references/world-class-benchmarks.md +198 -0
  45. package/ds-canonical/fork/codex/hooks.json +44 -3
  46. package/ds-canonical/fork/common/closed-tool-execution.mjs +1001 -0
  47. package/ds-canonical/fork/common/instruction.md +116 -0
  48. package/ds-canonical/fork/consumer/governance-check.mjs +3059 -0
  49. package/ds-canonical/fork/consumer/lib/closed-tool-execution.mjs +1001 -0
  50. package/ds-canonical/fork/consumer/lock.json +189 -0
  51. package/ds-canonical/fork/consumer/lock.schema.json +338 -0
  52. package/ds-canonical/fork/consumer/managed-files/0235cf6b3fd402a3717881dc205b5bf77aed7632e7d7d6f1811a40e3033bd816.blob +145 -0
  53. package/ds-canonical/fork/consumer/managed-files/145aab4b4fc470fc9ba7679b4a1d87d4f6a6d604940bd40f8d675c8af696498f.blob +47 -0
  54. package/ds-canonical/fork/consumer/managed-files/189e551431181e4a88783c02130d2787b801ef731c86e35bcde89a7313aa020e.blob +149 -0
  55. package/ds-canonical/fork/consumer/managed-files/24ad71c8613ddcf6fd23818cb3bb477a1fb6d83af4550b0bad43099813088686.blob +28 -0
  56. package/ds-canonical/fork/consumer/managed-files/2d1ef5617a5ce3c39ad9aae1c42af7d199f9270c0a0ccb490b63d62dbc6913db.blob +1001 -0
  57. package/ds-canonical/fork/consumer/managed-files/3625e02235cf166a50649a4d704cfb68fead69d0e168c481df013ebae9f6be25.blob +1412 -0
  58. package/ds-canonical/fork/consumer/managed-files/3ddedc67b464bdfe3dde9803a2d917920c96d633678b802eee638a44cd47366a.blob +8 -0
  59. package/ds-canonical/fork/consumer/managed-files/423729840acdd43c812f17c245b871716c6b60f76e4d3e94864d0e34a815bf8d.blob +348 -0
  60. package/ds-canonical/fork/consumer/managed-files/46f10bc55cdfdf834d0749eebff42ea0bb8576f361b36d2b6ba1851354c50150.blob +464 -0
  61. package/ds-canonical/fork/consumer/managed-files/475fba657eaeb8abd88f60635f3eadab27a9d0d3114ba4fffc65f43a2a0fe6e2.blob +124 -0
  62. package/ds-canonical/fork/consumer/managed-files/61c6aca6d7f04bedbf556eaa9d3043681fe48cbe0e93a8ccb1d69a7c5e23fdcf.blob +113 -0
  63. package/ds-canonical/fork/consumer/managed-files/63061e05969667005020166ea2ae7df3c96663f7a928e7dd3eebc1471022df56.blob +2398 -0
  64. package/ds-canonical/fork/consumer/managed-files/65410b66e8799a560e07b71779c186fed2a0ef50575ac44745df7a0938a9c2b4.blob +317 -0
  65. package/ds-canonical/fork/consumer/managed-files/6929a3c8bb4d69c507720b4754aeb339c6cbb8ff655e0a381d76606d64695e38.blob +393 -0
  66. package/ds-canonical/fork/consumer/managed-files/6b746628ac11fcccbe277ef6fea94cc2a7b90ea2822e83536eae80d5ee4b3fa5.blob +167 -0
  67. package/ds-canonical/fork/consumer/managed-files/76d485470d30ff014afacf7ce643763de9f4f956d9fc8128dbc1814774cf2e63.blob +671 -0
  68. package/ds-canonical/fork/consumer/managed-files/77e06f62670dfee01e9b5e8b0f8dad085ce711d75538c1fb0679d9818acaca82.blob +424 -0
  69. package/ds-canonical/fork/consumer/managed-files/7937321877e5c34c27dae36c0d507d21cd42fa3f46a8f11a284b8b4a52b6faa4.blob +84 -0
  70. package/ds-canonical/fork/consumer/managed-files/794bb911ba8ebef58ab0d44a55b9968a19d2c62bcfb310b1ee3a84166f087f21.blob +880 -0
  71. package/ds-canonical/fork/consumer/managed-files/7b17835d0ceb17cdcdcaf930540a3e5d0943a34a747ac43c12ca8cd485c11581.blob +44 -0
  72. package/ds-canonical/fork/consumer/managed-files/7eb6cc60abfdabaf6617c336058fad1454ccde08889295767bfd9b7fd153cc6e.blob +778 -0
  73. package/ds-canonical/fork/consumer/managed-files/81037efa5b3b9f4a875704005fb268acdc9233bb901f44902e4e37e1da92b8e3.blob +46 -0
  74. package/ds-canonical/fork/consumer/managed-files/884721550b56308812b20917adac7a12f36fdb4ecda6b02460a7f5a219c0c966.blob +78 -0
  75. package/ds-canonical/fork/consumer/managed-files/91f414b9462ddc1e4be1d4c6ca4ac7a7340a6eec9e98ee109cbc049d3b354b6a.blob +36 -0
  76. package/ds-canonical/fork/consumer/managed-files/92e4828cdcb96ff0d01daa16f6885f24045a8e72820665650f2a1890dc3a446c.blob +121 -0
  77. package/ds-canonical/fork/consumer/managed-files/93a89f16f6717d1e4e4e27ec20ef916e3355c7aa890ca74cd9b12db30d8a899f.blob +43 -0
  78. package/ds-canonical/fork/consumer/managed-files/974521d6b8fe796ea4c58b3c78b0c2b8a1be262b97100d77aaf4cc9c7724bcab.blob +45 -0
  79. package/ds-canonical/fork/consumer/managed-files/9791c85388d2e2e2bd564e2550fadc3351c4c960a47ce838098afcba7544eb90.blob +959 -0
  80. package/ds-canonical/fork/consumer/managed-files/a448d2222133335c30e6fda5116e26a40db3283a19d62ccc0663c30d272262a7.blob +174 -0
  81. package/ds-canonical/fork/consumer/managed-files/ab072e7f2eca51f0e18a5e18c4b333260ee5d219a12cc6df5bb2d683f0b2734d.blob +2636 -0
  82. package/ds-canonical/fork/consumer/managed-files/bc44cabad181f766611dab6a8f52d81100afadd50ac59799015e8ba7bf46d9eb.blob +596 -0
  83. package/ds-canonical/fork/consumer/managed-files/c4aa767f11714467de0038a2149cf78074baddc17fc6a9e074b6aed7544c6f3c.blob +80 -0
  84. package/ds-canonical/fork/consumer/managed-files/c893e9b734b162f4c631fef871128c16a9ab565f796a0e76d7f320f821f220f5.blob +41 -0
  85. package/ds-canonical/fork/consumer/managed-files/cd9c61f8ff24fb9bcc3663e346e2cfa419cfb84e02aae18d3dc66dd192681696.blob +9 -0
  86. package/ds-canonical/fork/consumer/managed-files/da2ad2737315d25a5300905ddefd62f1695056a8a1af155c5b34170ad9923bce.blob +38 -0
  87. package/ds-canonical/fork/consumer/managed-files/e1b11160d6c4043002d2cbff6d9a4ae551698a96137773c4244dadfac152fcb0.blob +222 -0
  88. package/ds-canonical/fork/consumer/managed-files/e26057408661597aac4a57cdf7e30e2f9d0afb1b9a2dbbb692c3805cd9028ca5.blob +250 -0
  89. package/ds-canonical/fork/consumer/managed-files/e68ae1915965d8e17d4b2441c00c2b97fe062ea308ae8535c9eebb2fc16fa245.blob +20 -0
  90. package/ds-canonical/fork/consumer/managed-files/e71c65f45c6af7ce12b6b3c0d8028f185da5b719e2050c9b2a6b243d41ae4878.blob +182 -0
  91. package/ds-canonical/fork/consumer/managed-files/e813096d69c49812e40e00be9ac8fa14d77f0ec1f97ab4c45cb096b3bedd35de.blob +3 -0
  92. package/ds-canonical/fork/consumer/managed-files/e930ebca6fd8eeb30802aac0930efae7bcc44cc0996f24de07c11ca6a081fedf.blob +277 -0
  93. package/ds-canonical/fork/consumer/managed-files/ea94e914d5c8acb84ad7660e5bda0f01b6ccc8cf96b0f6b1ce048f8938d0f5b5.blob +47 -0
  94. package/ds-canonical/fork/consumer/managed-files/f4db6e616d5229aa7e700c6a3ace022cb9d90baae74e38ca65fce617f18e0d9b.blob +110 -0
  95. package/ds-canonical/fork/consumer/managed-files/faa128c26102415dc5345e68698efbbd28a395c35ee278dceae044cf430692b2.blob +11 -0
  96. package/ds-canonical/fork/governance.lock +1552 -103
  97. package/ds-canonical/fork/hooks/_log-fire.sh +109 -12
  98. package/ds-canonical/fork/hooks/block_prototype_imports.py +148 -29
  99. package/ds-canonical/fork/hooks/check_chrome_header_avatar_canonical.sh +23 -12
  100. package/ds-canonical/fork/hooks/check_consumer_app_invariants.sh +116 -65
  101. package/ds-canonical/fork/hooks/check_consumer_code_quality.sh +140 -0
  102. package/ds-canonical/fork/hooks/check_ds_anchor_preflight.sh +84 -30
  103. package/ds-canonical/fork/hooks/check_dynamic_tailwind_class.sh +16 -6
  104. package/ds-canonical/fork/hooks/check_escape_marker_abuse.sh +65 -21
  105. package/ds-canonical/fork/hooks/check_field_family_invariants.sh +90 -24
  106. package/ds-canonical/fork/hooks/check_fork_product_quality.sh +8 -6
  107. package/ds-canonical/fork/hooks/check_item_list_gap.sh +4 -2
  108. package/ds-canonical/fork/hooks/check_layout_space_magic_numbers.sh +31 -13
  109. package/ds-canonical/fork/hooks/check_opacity_token_usage.sh +220 -95
  110. package/ds-canonical/fork/hooks/check_pattern_invariants.sh +73 -14
  111. package/ds-canonical/fork/hooks/check_sidebar_menu_button_implicit_wrap.sh +23 -12
  112. package/ds-canonical/fork/hooks/check_story_determinism.sh +1 -1
  113. package/ds-canonical/fork/hooks/check_story_invariants.sh +342 -61
  114. package/ds-canonical/fork/hooks/check_tabs_content_chrome_body_double_gap.sh +52 -23
  115. package/ds-canonical/fork/hooks/check_tailwind_wildcard_in_docs.sh +20 -13
  116. package/ds-canonical/fork/hooks/inject_deploy_url_after_push.sh +105 -22
  117. package/ds-canonical/fork/hooks/lib/_hook_integrity.sh +158 -0
  118. package/ds-canonical/fork/hooks/lib/_provider_paths.sh +162 -0
  119. package/ds-canonical/fork/launchers/approval-evidence.mjs +1131 -0
  120. package/ds-canonical/fork/launchers/authority-decision-evidence.mjs +413 -0
  121. package/ds-canonical/fork/launchers/canonical-order.mjs +8 -0
  122. package/ds-canonical/fork/launchers/fork-governance-dispatcher.sh +139 -29
  123. package/ds-canonical/fork/launchers/format-provider-hook-output.mjs +25 -0
  124. package/ds-canonical/fork/launchers/independent-review.mjs +490 -0
  125. package/ds-canonical/fork/launchers/inject_fork_governance_preamble.sh +88 -43
  126. package/ds-canonical/fork/launchers/normalize-provider-event.mjs +355 -0
  127. package/ds-canonical/fork/launchers/product-hook-dispatch-runtime.mjs +1067 -0
  128. package/ds-canonical/fork/launchers/provider-hook-normalization.mjs +1646 -0
  129. package/ds-canonical/fork/launchers/provider-hook-output-transport.mjs +525 -0
  130. package/ds-canonical/fork/launchers/settings-hooks.json +30 -53
  131. package/ds-canonical/fork/manifest.json +1881 -23
  132. package/ds-canonical/fork/providers/claude/hook-config.json +333 -0
  133. package/ds-canonical/fork/providers/claude/instruction.md +54 -0
  134. package/ds-canonical/fork/providers/claude/skills/bug-fix-rhythm/SKILL.md +207 -0
  135. package/ds-canonical/fork/providers/claude/skills/code-quality-audit/SKILL.md +71 -0
  136. package/ds-canonical/fork/providers/claude/skills/delivery-handoff/SKILL.md +244 -0
  137. package/ds-canonical/fork/providers/claude/skills/delivery-handoff/references/flow-diagram.md +180 -0
  138. package/ds-canonical/fork/providers/claude/skills/delivery-handoff/references/handoff-template.md +177 -0
  139. package/ds-canonical/fork/providers/claude/skills/delivery-handoff/references/inventory-checklist.md +196 -0
  140. package/ds-canonical/fork/providers/claude/skills/independent-review/SKILL.md +43 -0
  141. package/ds-canonical/fork/providers/claude/skills/independent-review/agents/openai.yaml +4 -0
  142. package/ds-canonical/fork/providers/claude/skills/independent-review/references/evidence-contract.md +24 -0
  143. package/ds-canonical/fork/providers/claude/skills/performance-audit/SKILL.md +114 -0
  144. package/ds-canonical/fork/providers/claude/skills/product-ui-audit/SKILL.md +244 -0
  145. package/ds-canonical/fork/providers/claude/skills/product-ui-audit/references/audit-checks.md +250 -0
  146. package/ds-canonical/fork/providers/claude/skills/product-ui-audit/references/common-misuses.md +329 -0
  147. package/ds-canonical/fork/providers/claude/skills/product-ui-audit/references/report-template.md +163 -0
  148. package/ds-canonical/fork/providers/claude/skills/propose-options/SKILL.md +190 -0
  149. package/ds-canonical/fork/providers/claude/skills/prototype/SKILL.md +246 -0
  150. package/ds-canonical/fork/providers/claude/skills/prototype/references/audit-checks.md +39 -0
  151. package/ds-canonical/fork/providers/claude/skills/prototype/references/benchmark-sources.md +94 -0
  152. package/ds-canonical/fork/providers/claude/skills/prototype/references/checkpoints.md +200 -0
  153. package/ds-canonical/fork/providers/claude/skills/prototype/references/evaluation-matrix.md +142 -0
  154. package/ds-canonical/fork/providers/claude/skills/prototype/references/ooux-template.md +198 -0
  155. package/ds-canonical/fork/providers/claude/skills/prototype/references/proposal-template.md +230 -0
  156. package/ds-canonical/fork/providers/claude/skills/scan-similar-bugs/SKILL.md +212 -0
  157. package/ds-canonical/fork/providers/claude/skills/ux-audit/SKILL.md +135 -0
  158. package/ds-canonical/fork/providers/claude/skills/visual-audit/SKILL.md +259 -0
  159. package/ds-canonical/fork/providers/claude/skills/visual-audit/output/.gitkeep +0 -0
  160. package/ds-canonical/fork/providers/claude/skills/visual-audit/references/audit-architecture.md +101 -0
  161. package/ds-canonical/fork/providers/claude/skills/visual-audit/references/visual-checklist.md +297 -0
  162. package/ds-canonical/fork/providers/claude/skills/visual-audit/references/world-class-benchmarks.md +198 -0
  163. package/ds-canonical/fork/providers/codex/hook-config.json +57 -0
  164. package/ds-canonical/fork/providers/codex/skills/bug-fix-rhythm/SKILL.md +207 -0
  165. package/ds-canonical/fork/providers/codex/skills/code-quality-audit/SKILL.md +71 -0
  166. package/ds-canonical/fork/providers/codex/skills/delivery-handoff/SKILL.md +244 -0
  167. package/ds-canonical/fork/providers/codex/skills/delivery-handoff/references/flow-diagram.md +180 -0
  168. package/ds-canonical/fork/providers/codex/skills/delivery-handoff/references/handoff-template.md +177 -0
  169. package/ds-canonical/fork/providers/codex/skills/delivery-handoff/references/inventory-checklist.md +196 -0
  170. package/ds-canonical/fork/providers/codex/skills/independent-review/SKILL.md +43 -0
  171. package/ds-canonical/fork/providers/codex/skills/independent-review/agents/openai.yaml +4 -0
  172. package/ds-canonical/fork/providers/codex/skills/independent-review/references/evidence-contract.md +24 -0
  173. package/ds-canonical/fork/providers/codex/skills/performance-audit/SKILL.md +114 -0
  174. package/ds-canonical/fork/providers/codex/skills/product-ui-audit/SKILL.md +244 -0
  175. package/ds-canonical/fork/providers/codex/skills/product-ui-audit/references/audit-checks.md +250 -0
  176. package/ds-canonical/fork/providers/codex/skills/product-ui-audit/references/common-misuses.md +329 -0
  177. package/ds-canonical/fork/providers/codex/skills/product-ui-audit/references/report-template.md +163 -0
  178. package/ds-canonical/fork/providers/codex/skills/propose-options/SKILL.md +190 -0
  179. package/ds-canonical/fork/providers/codex/skills/prototype/SKILL.md +246 -0
  180. package/ds-canonical/fork/providers/codex/skills/prototype/references/audit-checks.md +39 -0
  181. package/ds-canonical/fork/providers/codex/skills/prototype/references/benchmark-sources.md +94 -0
  182. package/ds-canonical/fork/providers/codex/skills/prototype/references/checkpoints.md +200 -0
  183. package/ds-canonical/fork/providers/codex/skills/prototype/references/evaluation-matrix.md +142 -0
  184. package/ds-canonical/fork/providers/codex/skills/prototype/references/ooux-template.md +198 -0
  185. package/ds-canonical/fork/providers/codex/skills/prototype/references/proposal-template.md +230 -0
  186. package/ds-canonical/fork/providers/codex/skills/scan-similar-bugs/SKILL.md +212 -0
  187. package/ds-canonical/fork/providers/codex/skills/ux-audit/SKILL.md +135 -0
  188. package/ds-canonical/fork/providers/codex/skills/visual-audit/SKILL.md +259 -0
  189. package/ds-canonical/fork/providers/codex/skills/visual-audit/output/.gitkeep +0 -0
  190. package/ds-canonical/fork/providers/codex/skills/visual-audit/references/audit-architecture.md +101 -0
  191. package/ds-canonical/fork/providers/codex/skills/visual-audit/references/visual-checklist.md +297 -0
  192. package/ds-canonical/fork/providers/codex/skills/visual-audit/references/world-class-benchmarks.md +198 -0
  193. package/ds-canonical/fork/references/story-baseline-registry.json +117 -0
  194. package/ds-canonical/fork/references/story-baseline-registry.schema.json +145 -0
  195. package/ds-canonical/fork/references/utility-registry.json +377 -0
  196. package/ds-canonical/fork/references/utility-registry.schema.json +373 -0
  197. package/ds-canonical/fork/review/canonical-order.mjs +8 -0
  198. package/ds-canonical/fork/review/provider-review-binding.mjs +2377 -0
  199. package/ds-canonical/fork/skills/bug-fix-rhythm/SKILL.md +19 -12
  200. package/ds-canonical/fork/skills/code-quality-audit/SKILL.md +19 -15
  201. package/ds-canonical/fork/skills/delivery-handoff/SKILL.md +17 -11
  202. package/ds-canonical/fork/skills/independent-review/SKILL.md +37 -0
  203. package/ds-canonical/fork/skills/independent-review/agents/openai.yaml +4 -0
  204. package/ds-canonical/fork/skills/independent-review/references/evidence-contract.md +24 -0
  205. package/ds-canonical/fork/skills/performance-audit/SKILL.md +11 -8
  206. package/ds-canonical/fork/skills/product-ui-audit/SKILL.md +31 -21
  207. package/ds-canonical/fork/skills/product-ui-audit/references/audit-checks.md +9 -5
  208. package/ds-canonical/fork/skills/product-ui-audit/references/report-template.md +30 -26
  209. package/ds-canonical/fork/skills/propose-options/SKILL.md +6 -4
  210. package/ds-canonical/fork/skills/prototype/SKILL.md +21 -21
  211. package/ds-canonical/fork/skills/prototype/references/audit-checks.md +7 -6
  212. package/ds-canonical/fork/skills/prototype/references/checkpoints.md +26 -17
  213. package/ds-canonical/fork/skills/prototype/references/evaluation-matrix.md +2 -1
  214. package/ds-canonical/fork/skills/prototype/references/ooux-template.md +1 -1
  215. package/ds-canonical/fork/skills/prototype/references/proposal-template.md +6 -5
  216. package/ds-canonical/fork/skills/scan-similar-bugs/SKILL.md +28 -27
  217. package/ds-canonical/fork/skills/ux-audit/SKILL.md +8 -5
  218. package/ds-canonical/fork/skills/visual-audit/SKILL.md +37 -43
  219. package/ds-canonical/fork/skills/visual-audit/references/audit-architecture.md +15 -15
  220. package/ds-canonical/fork/skills/visual-audit/references/visual-checklist.md +6 -6
  221. package/ds-canonical/fork/skills/visual-audit/references/world-class-benchmarks.md +2 -2
  222. package/ds-canonical/hooks/README.md +37 -23
  223. package/ds-canonical/hooks/_log-fire.sh +109 -12
  224. package/ds-canonical/hooks/auto_regen_ds_barrel.sh +52 -20
  225. package/ds-canonical/hooks/block_prototype_imports.py +148 -29
  226. package/ds-canonical/hooks/check_audit_post_report_validator.sh +76 -66
  227. package/ds-canonical/hooks/check_audit_sample_escape.sh +4 -4
  228. package/ds-canonical/hooks/check_benchmark_citation.sh +3 -3
  229. package/ds-canonical/hooks/check_canonical_propagation.sh +81 -22
  230. package/ds-canonical/hooks/check_chrome_header_avatar_canonical.sh +22 -11
  231. package/ds-canonical/hooks/check_codex_brief_invariants.sh +100 -56
  232. package/ds-canonical/hooks/check_codex_collab_5step.sh +31 -25
  233. package/ds-canonical/hooks/check_consumer_app_invariants.sh +116 -65
  234. package/ds-canonical/hooks/check_consumer_code_quality.sh +140 -0
  235. package/ds-canonical/hooks/check_datatable_invariants.sh +103 -25
  236. package/ds-canonical/hooks/check_dim_count_drift.sh +5 -4
  237. package/ds-canonical/hooks/check_ds_anchor_preflight.sh +83 -29
  238. package/ds-canonical/hooks/check_dynamic_tailwind_class.sh +16 -6
  239. package/ds-canonical/hooks/check_escape_marker_abuse.sh +65 -21
  240. package/ds-canonical/hooks/check_field_controls_contracts.sh +10 -8
  241. package/ds-canonical/hooks/check_field_family_invariants.sh +90 -24
  242. package/ds-canonical/hooks/check_file_size_budget.sh +32 -14
  243. package/ds-canonical/hooks/check_full_story_visual_interaction_sweep.sh +60 -21
  244. package/ds-canonical/hooks/check_item_list_gap.sh +4 -2
  245. package/ds-canonical/hooks/check_layout_space_magic_numbers.sh +31 -13
  246. package/ds-canonical/hooks/check_main_branch_workbench.sh +27 -68
  247. package/ds-canonical/hooks/check_naming_and_abstraction.sh +20 -9
  248. package/ds-canonical/hooks/check_opacity_token_usage.sh +220 -95
  249. package/ds-canonical/hooks/check_orphan_ds_css.sh +48 -18
  250. package/ds-canonical/hooks/check_overlay_open_focus_escape_probe.sh +29 -11
  251. package/ds-canonical/hooks/check_pattern_invariants.sh +73 -14
  252. package/ds-canonical/hooks/check_peoplepicker_ssot_drift.sh +4 -4
  253. package/ds-canonical/hooks/check_pixel_quantified_audit.sh +3 -2
  254. package/ds-canonical/hooks/check_plugin_fork_health.sh +151 -41
  255. package/ds-canonical/hooks/check_post_main_ssot_propagate.sh +5 -109
  256. package/ds-canonical/hooks/check_propose_discipline.sh +116 -29
  257. package/ds-canonical/hooks/check_propose_pre_grep_verify.sh +6 -4
  258. package/ds-canonical/hooks/check_propose_without_benchmark.sh +10 -6
  259. package/ds-canonical/hooks/check_select_all_canonical.sh +1 -1
  260. package/ds-canonical/hooks/check_sidebar_menu_button_implicit_wrap.sh +22 -11
  261. package/ds-canonical/hooks/check_solo_workflow.sh +117 -186
  262. package/ds-canonical/hooks/check_spec_class_drift.sh +33 -19
  263. package/ds-canonical/hooks/check_ssot_header_declaration.sh +6 -5
  264. package/ds-canonical/hooks/check_story_determinism.sh +1 -1
  265. package/ds-canonical/hooks/check_story_invariants.sh +526 -213
  266. package/ds-canonical/hooks/check_storybook_addon_packaging.sh +65 -15
  267. package/ds-canonical/hooks/check_substantive_edit_approval_preflight.sh +137 -67
  268. package/ds-canonical/hooks/check_tabs_content_chrome_body_double_gap.sh +52 -23
  269. package/ds-canonical/hooks/check_tailwind_wildcard_in_docs.sh +20 -13
  270. package/ds-canonical/hooks/check_wrapper_primitive_schema_drift.sh +15 -12
  271. package/ds-canonical/hooks/chrome_header_dispatcher.sh +75 -26
  272. package/ds-canonical/hooks/enforce_home_charter.sh +29 -8
  273. package/ds-canonical/hooks/governance_control_plane.sh +15 -0
  274. package/ds-canonical/hooks/inject_deploy_url_after_push.sh +105 -22
  275. package/ds-canonical/hooks/inject_pending_self_audit.sh +27 -24
  276. package/ds-canonical/hooks/lib/_app_shell_primary_header_consistency.sh +77 -29
  277. package/ds-canonical/hooks/lib/_approval_re.sh +5 -6
  278. package/ds-canonical/hooks/lib/_chrome_header_handcraft.sh +66 -20
  279. package/ds-canonical/hooks/lib/_code_quality.sh +9 -9
  280. package/ds-canonical/hooks/lib/_cva_default_sync.sh +11 -13
  281. package/ds-canonical/hooks/lib/_governance_coverage_check.sh +48 -21
  282. package/ds-canonical/hooks/lib/_hardcoded_strings.sh +13 -10
  283. package/ds-canonical/hooks/lib/_header_with_tabs_border.sh +68 -48
  284. package/ds-canonical/hooks/lib/_hook_integrity.sh +158 -0
  285. package/ds-canonical/hooks/lib/_layout_space_canonical.sh +5 -5
  286. package/ds-canonical/hooks/lib/_overlay_handcraft.sh +21 -23
  287. package/ds-canonical/hooks/lib/_person_data_richness.sh +5 -5
  288. package/ds-canonical/hooks/lib/_provider_paths.sh +162 -0
  289. package/ds-canonical/hooks/lib/_story_compile_drift.sh +5 -5
  290. package/ds-canonical/hooks/lib/_tab_lg_chrome_header_equal.sh +247 -27
  291. package/ds-canonical/hooks/lib/_token_hygiene.sh +39 -22
  292. package/ds-canonical/hooks/lib/approval-evidence.mjs +1131 -0
  293. package/ds-canonical/hooks/lib/audit-report-validator.mjs +118 -0
  294. package/ds-canonical/hooks/lib/provider-marker-regex.mjs +23 -0
  295. package/ds-canonical/hooks/lib/tsx-governance-analysis.mjs +410 -0
  296. package/ds-canonical/hooks/log_governance_fires.sh +45 -13
  297. package/ds-canonical/hooks/log_skill_invokes.sh +8 -7
  298. package/ds-canonical/hooks/post_edit_dispatcher.sh +106 -28
  299. package/ds-canonical/hooks/registrations.json +488 -0
  300. package/ds-canonical/hooks/registrations.schema.json +58 -0
  301. package/ds-canonical/hooks/retired/2026-06-11-prune-merge/check_propose_plain_chinese.sh +1 -1
  302. package/ds-canonical/hooks/retired/check_governance_compliance.sh +1 -1
  303. package/ds-canonical/hooks/session_start_governance_check.sh +109 -78
  304. package/ds-canonical/hooks/stop_audit_content_quality.sh +20 -0
  305. package/ds-canonical/hooks/stop_extract_canonical_rules.sh +20 -0
  306. package/ds-canonical/hooks/stop_passive_logging.sh +133 -59
  307. package/ds-canonical/hooks/stop_self_audit.sh +349 -210
  308. package/ds-canonical/hooks/tests/fixtures/transcripts/claude-transcript-jsonl-v1.jsonl +3 -0
  309. package/ds-canonical/hooks/tests/fixtures/transcripts/claude-transcript-jsonl-v2.jsonl +3 -0
  310. package/ds-canonical/hooks/tests/registration-output-contracts.mjs +151 -0
  311. package/ds-canonical/hooks/tests/run-all.sh +35 -5
  312. package/ds-canonical/hooks/tests/test_auto_regen_ds_barrel.sh +48 -4
  313. package/ds-canonical/hooks/tests/test_block_prototype_imports.sh +157 -9
  314. package/ds-canonical/hooks/tests/test_check_app_shell_primary_header_consistency.sh +265 -127
  315. package/ds-canonical/hooks/tests/test_check_audit_post_report_validator.sh +119 -12
  316. package/ds-canonical/hooks/tests/test_check_audit_sample_escape.sh +0 -0
  317. package/ds-canonical/hooks/tests/test_check_benchmark_citation.sh +0 -0
  318. package/ds-canonical/hooks/tests/test_check_canonical_propagation.sh +56 -5
  319. package/ds-canonical/hooks/tests/test_check_chrome_header_avatar_canonical.sh +6 -6
  320. package/ds-canonical/hooks/tests/test_check_chrome_header_handcraft.sh +193 -100
  321. package/ds-canonical/hooks/tests/test_check_codex_brief_invariants.sh +143 -17
  322. package/ds-canonical/hooks/tests/test_check_codex_collab_5step.sh +49 -5
  323. package/ds-canonical/hooks/tests/test_check_consumer_app_invariants.sh +128 -0
  324. package/ds-canonical/hooks/tests/test_check_consumer_app_story_title.sh +2 -2
  325. package/ds-canonical/hooks/tests/test_check_consumer_code_quality.sh +64 -0
  326. package/ds-canonical/hooks/tests/test_check_consumer_ds_primitive_misuse.sh +2 -2
  327. package/ds-canonical/hooks/tests/test_check_consumer_no_ds_catalog.sh +2 -2
  328. package/ds-canonical/hooks/tests/test_check_consumer_story_baseline.sh +2 -2
  329. package/ds-canonical/hooks/tests/test_check_data_table_size_num_to_meta_width.sh +26 -8
  330. package/ds-canonical/hooks/tests/test_check_datatable_invariants.sh +126 -12
  331. package/ds-canonical/hooks/tests/test_check_dim_count_drift.sh +13 -8
  332. package/ds-canonical/hooks/tests/test_check_ds_anchor_preflight.sh +53 -7
  333. package/ds-canonical/hooks/tests/test_check_dynamic_tailwind_class.sh +3 -3
  334. package/ds-canonical/hooks/tests/test_check_escape_marker_abuse.sh +17 -7
  335. package/ds-canonical/hooks/tests/test_check_field_family_invariants.sh +72 -16
  336. package/ds-canonical/hooks/tests/test_check_file_size_budget.sh +67 -5
  337. package/ds-canonical/hooks/tests/test_check_fork_user_plugin_install.sh +107 -4
  338. package/ds-canonical/hooks/tests/test_check_full_story_visual_interaction_sweep.sh +25 -15
  339. package/ds-canonical/hooks/tests/test_check_header_with_tabs_border.sh +177 -67
  340. package/ds-canonical/hooks/tests/test_check_item_list_gap.sh +7 -7
  341. package/ds-canonical/hooks/tests/test_check_layout_space_magic_numbers.sh +2 -2
  342. package/ds-canonical/hooks/tests/test_check_main_branch_workbench.sh +45 -110
  343. package/ds-canonical/hooks/tests/test_check_naming_and_abstraction.sh +19 -2
  344. package/ds-canonical/hooks/tests/test_check_opacity_token_usage.sh +114 -8
  345. package/ds-canonical/hooks/tests/test_check_orphan_ds_css.sh +38 -22
  346. package/ds-canonical/hooks/tests/test_check_overlay_open_focus_escape_probe.sh +2 -2
  347. package/ds-canonical/hooks/tests/test_check_pattern_invariants.sh +68 -6
  348. package/ds-canonical/hooks/tests/test_check_plugin_fork_health.sh +53 -0
  349. package/ds-canonical/hooks/tests/test_check_post_main_ssot_propagate.sh +37 -0
  350. package/ds-canonical/hooks/tests/test_check_propose_cite_required.sh +2 -2
  351. package/ds-canonical/hooks/tests/test_check_propose_discipline.sh +59 -0
  352. package/ds-canonical/hooks/tests/test_check_propose_plain_chinese.sh +7 -5
  353. package/ds-canonical/hooks/tests/test_check_propose_pre_grep_verify.sh +5 -5
  354. package/ds-canonical/hooks/tests/test_check_propose_without_benchmark.sh +45 -0
  355. package/ds-canonical/hooks/tests/test_check_sidebar_menu_button_implicit_wrap.sh +5 -5
  356. package/ds-canonical/hooks/tests/test_check_solo_workflow.sh +183 -105
  357. package/ds-canonical/hooks/tests/test_check_spec_class_drift.sh +35 -7
  358. package/ds-canonical/hooks/tests/{test_check_story_anatomy.sh.broken → test_check_story_anatomy.sh} +73 -4
  359. package/ds-canonical/hooks/tests/{test_check_story_category.sh.broken → test_check_story_category.sh} +82 -2
  360. package/ds-canonical/hooks/tests/test_check_story_determinism.sh +39 -0
  361. package/ds-canonical/hooks/tests/test_check_story_invariants.sh +559 -43
  362. package/ds-canonical/hooks/tests/{test_check_story_name_jargon.sh.broken → test_check_story_name_jargon.sh} +22 -1
  363. package/ds-canonical/hooks/tests/test_check_storybook_addon_packaging.sh +79 -0
  364. package/ds-canonical/hooks/tests/test_check_storybook_addon_preset_cjs.sh +2 -2
  365. package/ds-canonical/hooks/tests/test_check_substantive_edit_approval_preflight.sh +546 -28
  366. package/ds-canonical/hooks/tests/test_check_tab_lg_chrome_header_equal.sh +180 -98
  367. package/ds-canonical/hooks/tests/test_check_tabs_content_chrome_body_double_gap.sh +3 -3
  368. package/ds-canonical/hooks/tests/test_check_tailwind_wildcard_in_docs.sh +2 -2
  369. package/ds-canonical/hooks/tests/test_check_token_hygiene.sh +63 -15
  370. package/ds-canonical/hooks/tests/test_check_wrapper_primitive_schema_drift.sh +12 -6
  371. package/ds-canonical/hooks/tests/test_chrome_header_dispatcher.sh +125 -0
  372. package/ds-canonical/hooks/tests/test_enforce_home_charter.sh +57 -3
  373. package/ds-canonical/hooks/tests/test_governance_control_plane.sh +66 -0
  374. package/ds-canonical/hooks/tests/test_governance_coverage_check.sh +88 -0
  375. package/ds-canonical/hooks/tests/test_hook_integrity_classification.sh +217 -0
  376. package/ds-canonical/hooks/tests/test_inject_deploy_url_after_push.sh +85 -2
  377. package/ds-canonical/hooks/tests/test_inject_pending_self_audit.sh +116 -11
  378. package/ds-canonical/hooks/tests/test_log_governance_fires.sh +117 -1
  379. package/ds-canonical/hooks/tests/test_log_skill_invokes.sh +74 -5
  380. package/ds-canonical/hooks/tests/test_post_edit_dispatcher.sh +234 -77
  381. package/ds-canonical/hooks/tests/test_provider_peer_symmetry.sh +76 -0
  382. package/ds-canonical/hooks/tests/test_registration_output_contracts.sh +20 -0
  383. package/ds-canonical/hooks/tests/test_session_start_governance_check.sh +130 -18
  384. package/ds-canonical/hooks/tests/test_stop_audit_content_quality.sh +75 -0
  385. package/ds-canonical/hooks/tests/test_stop_capture_metrics.sh +101 -23
  386. package/ds-canonical/hooks/tests/test_stop_extract_canonical_rules.sh +75 -0
  387. package/ds-canonical/hooks/tests/{test_stop_governance_drift_check.sh.broken → test_stop_governance_drift_check.sh} +22 -12
  388. package/ds-canonical/hooks/tests/test_stop_passive_logging.sh +102 -1
  389. package/ds-canonical/hooks/tests/test_stop_repository_command_context.sh +38 -0
  390. package/ds-canonical/hooks/tests/test_stop_self_audit.sh +363 -9
  391. package/ds-canonical/references/README.md +8 -8
  392. package/ds-canonical/references/audit-coverage-vs-24-checklist.md +4 -4
  393. package/ds-canonical/references/build-ui-canonicals.md +3 -3
  394. package/ds-canonical/references/certified-surfaces.md +46 -15
  395. package/ds-canonical/references/cva-patterns.md +2 -2
  396. package/ds-canonical/references/failure-class-registry.json +3 -2
  397. package/ds-canonical/references/governance-audit-coverage.md +8 -8
  398. package/ds-canonical/references/naming-conventions.md +1 -1
  399. package/ds-canonical/references/preflight-gate-baseline.json +210 -3
  400. package/ds-canonical/references/principle-dim-map.json +15 -9
  401. package/ds-canonical/references/props-naming.md +1 -1
  402. package/ds-canonical/references/scenario-definition.md +33 -30
  403. package/ds-canonical/references/spec-rules.md +1 -1
  404. package/ds-canonical/references/ssot-consultation.md +3 -3
  405. package/ds-canonical/references/ssot-index.md +1 -1
  406. package/ds-canonical/references/story-baseline-registry.json +9 -6
  407. package/ds-canonical/references/story-baseline-registry.schema.json +145 -0
  408. package/ds-canonical/references/structural-token-retention.md +1 -1
  409. package/ds-canonical/references/tailwind-gotchas.md +2 -2
  410. package/ds-canonical/references/ui-dev-rules.md +1 -1
  411. package/ds-canonical/rules/README.md +14 -9
  412. package/ds-canonical/rules/meta-patterns.md +13 -13
  413. package/ds-canonical/rules/self-verify.md +12 -7
  414. package/ds-canonical/rules/spec-rules.md +1 -1
  415. package/ds-canonical/rules/ui-development.md +5 -5
  416. package/ds-canonical/skills/README.md +27 -15
  417. package/ds-canonical/skills/bug-fix-rhythm/SKILL.md +14 -7
  418. package/ds-canonical/skills/canonical-reviewer/SKILL.md +29 -0
  419. package/ds-canonical/skills/code-quality-audit/SKILL.md +11 -7
  420. package/ds-canonical/skills/codex-collab/SKILL.md +19 -238
  421. package/ds-canonical/skills/codex-collab/references/brief-template.md +37 -47
  422. package/ds-canonical/skills/codex-collab/references/transport.md +38 -57
  423. package/ds-canonical/skills/codify-corrections/SKILL.md +32 -31
  424. package/ds-canonical/skills/codify-principle/SKILL.md +33 -32
  425. package/ds-canonical/skills/component-quality-gate/SKILL.md +13 -13
  426. package/ds-canonical/skills/component-quality-gate/references/checklist.md +7 -7
  427. package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +28 -229
  428. package/ds-canonical/skills/deep-audit-cross-codex/references/phase-a-workflow.md +36 -131
  429. package/ds-canonical/skills/deep-audit-cross-codex/references/phase-b-codex-brief.md +33 -187
  430. package/ds-canonical/skills/deep-audit-cross-codex/references/skill-relationships-antipatterns-benchmarks.md +22 -24
  431. package/ds-canonical/skills/deep-audit-cross-codex/references/triage-rubric.md +15 -79
  432. package/ds-canonical/skills/deep-audit-cross-codex/references/upstream-directives-r-mapping.md +24 -23
  433. package/ds-canonical/skills/delivery-handoff/SKILL.md +14 -8
  434. package/ds-canonical/skills/design-system-audit/SKILL.md +63 -62
  435. package/ds-canonical/skills/design-system-audit/references/audit-prompts.md +86 -77
  436. package/ds-canonical/skills/design-system-audit/references/checkpoints.md +59 -50
  437. package/ds-canonical/skills/design-system-audit/references/historical-bugs.md +8 -8
  438. package/ds-canonical/skills/design-system-audit/references/principle-audit-protocol.md +37 -32
  439. package/ds-canonical/skills/design-system-audit/references/rule-placement.md +39 -38
  440. package/ds-canonical/skills/ensure-canonical/SKILL.md +36 -34
  441. package/ds-canonical/skills/governance-health/SKILL.md +26 -22
  442. package/ds-canonical/skills/governance-status/SKILL.md +30 -0
  443. package/ds-canonical/skills/independent-review/SKILL.md +37 -0
  444. package/ds-canonical/skills/independent-review/agents/openai.yaml +4 -0
  445. package/ds-canonical/skills/independent-review/references/evidence-contract.md +24 -0
  446. package/ds-canonical/skills/knowledge-prune/SKILL.md +45 -52
  447. package/ds-canonical/skills/knowledge-prune/references/phase-z-cross-repo-ssot-propagation.md +10 -7
  448. package/ds-canonical/skills/knowledge-prune/references/prune-dimensions-d5-d10.md +6 -6
  449. package/ds-canonical/skills/new-component/SKILL.md +18 -12
  450. package/ds-canonical/skills/new-component/references/new-component-checklist.md +7 -6
  451. package/ds-canonical/skills/performance-audit/SKILL.md +13 -10
  452. package/ds-canonical/skills/product-ui-audit/SKILL.md +27 -17
  453. package/ds-canonical/skills/product-ui-audit/references/audit-checks.md +7 -4
  454. package/ds-canonical/skills/product-ui-audit/references/report-template.md +30 -26
  455. package/ds-canonical/skills/propose-options/SKILL.md +1 -1
  456. package/ds-canonical/skills/prototype/SKILL.md +19 -21
  457. package/ds-canonical/skills/prototype/references/audit-checks.md +6 -5
  458. package/ds-canonical/skills/prototype/references/checkpoints.md +26 -17
  459. package/ds-canonical/skills/prototype/references/ooux-template.md +1 -1
  460. package/ds-canonical/skills/prototype/references/proposal-template.md +4 -4
  461. package/ds-canonical/skills/scan-similar-bugs/SKILL.md +22 -21
  462. package/ds-canonical/skills/story-auto-compile-migrate/SKILL.md +21 -20
  463. package/ds-canonical/skills/story-writing/SKILL.md +12 -12
  464. package/ds-canonical/skills/story-writing/references/anatomy-standard.md +1 -1
  465. package/ds-canonical/skills/story-writing/references/category-templates.md +5 -5
  466. package/ds-canonical/skills/ux-audit/SKILL.md +12 -9
  467. package/ds-canonical/skills/visual-audit/SKILL.md +41 -47
  468. package/ds-canonical/skills/visual-audit/references/audit-architecture.md +7 -7
  469. package/ds-canonical/skills/visual-audit/references/visual-checklist.md +12 -12
  470. package/ds-canonical/skills/visual-audit/references/world-class-benchmarks.md +3 -3
  471. package/ds-canonical/templates/_README.md +6 -7
  472. package/ds-canonical/templates/consumer-governance.md +78 -0
  473. package/ds-canonical/templates/dashboard-app.tsx +3 -1
  474. package/ds-canonical/templates/product-launchers/fork-governance-dispatcher.sh +154 -0
  475. package/ds-canonical/templates/product-launchers/format-provider-hook-output.mjs +25 -0
  476. package/ds-canonical/templates/product-launchers/independent-review.mjs +490 -0
  477. package/ds-canonical/templates/product-launchers/inject_fork_governance_preamble.sh +99 -0
  478. package/ds-canonical/templates/product-launchers/normalize-provider-event.mjs +355 -0
  479. package/ds-canonical/templates/product-launchers/product-hook-dispatch-runtime.mjs +1067 -0
  480. package/ds-canonical/templates/product-provider-wiring.json +80 -0
  481. package/ds-canonical/templates/product-provider-wiring.schema.json +151 -0
  482. package/llms-full.txt +6 -6
  483. package/llms.txt +2 -2
  484. package/package.json +15 -4
  485. package/src/tokens/utility-registry.json +119 -4
  486. package/src/tokens/utility-registry.schema.json +373 -0
  487. package/tools/README.md +9 -0
  488. package/tools/codemods/cli.mjs +93 -0
  489. package/tools/codemods/index.mjs +582 -0
  490. package/tools/shared/safe-filesystem.mjs +371 -0
  491. package/tools/visual-baseline/cli.mjs +97 -0
  492. package/tools/visual-baseline/index.mjs +522 -0
  493. /package/dist/{react-day-picker.css → index.css} +0 -0
@@ -0,0 +1,959 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Protected-base verifier for credential-free consumer upgrades.
4
+ //
5
+ // The certifier job may execute the authenticated incoming checker, but its receipt is never a
6
+ // writer trust root. Before a writer token exists, this protected-base implementation installs the
7
+ // exact incoming release with scripts disabled in a disposable copy of the protected base, verifies
8
+ // npm/SLSA/immutable-Release provenance, validates both fork corpora and their lifecycle transition,
9
+ // and materializes the expected tree with the protected-base materializer. Only a byte-identical
10
+ // patch/tree/operation receipt may then be applied to the writer checkout.
11
+
12
+ import { createHash } from 'node:crypto'
13
+ import { spawnSync } from 'node:child_process'
14
+ import {
15
+ chmodSync,
16
+ existsSync,
17
+ lstatSync,
18
+ mkdirSync,
19
+ mkdtempSync,
20
+ readFileSync,
21
+ readdirSync,
22
+ realpathSync,
23
+ rmSync,
24
+ writeFileSync,
25
+ } from 'node:fs'
26
+ import { tmpdir } from 'node:os'
27
+ import { dirname, isAbsolute, join, resolve, sep } from 'node:path'
28
+ import { fileURLToPath } from 'node:url'
29
+ import {
30
+ assertNoSymlinkPath,
31
+ authorizeDisposableProviderRefreshTransaction,
32
+ canonicalRepositoryRoot,
33
+ refreshLaunchers,
34
+ validateInstalledForkCorpus,
35
+ validateInstalledProviderTransition,
36
+ } from './refresh-fork-launchers.mjs'
37
+ import {
38
+ validateUpgradeTrustPolicy,
39
+ verifyUpgradeProvenance,
40
+ } from './verify-upgrade-provenance.mjs'
41
+ import {
42
+ assertNoRootNpmShrinkwrap,
43
+ runVerifiedHighVulnerabilityAudit,
44
+ } from './lib/governance-dependency-bootstrap.mjs'
45
+ import {
46
+ assertVerifiedExactNpmRuntimeCapability,
47
+ prepareVerifiedExactNpmRuntime,
48
+ resolveExactNpmRuntimeContract,
49
+ } from './lib/verified-exact-npm-runtime.mjs'
50
+ import {
51
+ REVIEWED_CONTROL_PLANE_UPDATE_ROUTE,
52
+ packageScriptsAreIdentical,
53
+ requiresReviewedControlPlaneUpdate,
54
+ } from './lib/consumer-control-plane-policy.mjs'
55
+ import {
56
+ assertClosedGitLocalConfiguration,
57
+ runClosedGit,
58
+ } from './lib/closed-tool-execution.mjs'
59
+ import { compareUtf8Bytes } from './lib/provider-lifecycle.mjs'
60
+
61
+ const RECEIPT_KEYS = Object.freeze([
62
+ 'schemaVersion', 'baseSha', 'version', 'patchSha256', 'treeSha256', 'paths', 'operations',
63
+ 'previousCorpus', 'incomingCorpus', 'provenance', 'authoritySha256', 'toolchain',
64
+ ])
65
+ const CORPUS_EVIDENCE_KEYS = Object.freeze([
66
+ 'releaseVersion', 'consumerBomSha256', 'forkCorpusLockSha256', 'manifestSha256', 'providerLifecycleSha256',
67
+ ])
68
+ const PROVENANCE_KEYS = Object.freeze([
69
+ 'repository', 'workflow', 'tag', 'tagObject', 'gitCommit', 'gitTree',
70
+ 'bomSha256', 'releaseAssetDigest', 'releaseSetSha256',
71
+ 'finalizationReceiptSha256', 'releaseTrustEvidenceSha256', 'packages',
72
+ ])
73
+ const TOOLCHAIN_KEYS = Object.freeze(['node', 'npm'])
74
+ const OPERATION_KEYS = Object.freeze(['path', 'status'])
75
+ const AUTHORITY_KEYS = Object.freeze([
76
+ 'schemaVersion', 'previousCorpus', 'incomingCorpus', 'provenance', 'toolchain',
77
+ 'previousMaterialization', 'incomingMaterialization', 'retirementDeletes',
78
+ ])
79
+ const MATERIALIZATION_KEYS = Object.freeze(['exactPaths', 'pathPrefixes'])
80
+ const SHA256 = /^[a-f0-9]{64}$/
81
+ const GIT_OBJECT = /^[a-f0-9]{40}(?:[a-f0-9]{24})?$/
82
+ const VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z][0-9A-Za-z.-]*)?(?:\+[0-9A-Za-z][0-9A-Za-z.-]*)?$/
83
+ const NPM_REGISTRY = 'https://registry.npmjs.org/'
84
+
85
+ const invariant = (condition, message) => { if (!condition) throw new Error(message) }
86
+ const sha256 = value => createHash('sha256').update(value).digest('hex')
87
+ const exactKeys = (value, keys, label) => {
88
+ invariant(value && typeof value === 'object' && !Array.isArray(value), `${label} must be an object`)
89
+ const actual = Object.keys(value).sort()
90
+ const expected = [...keys].sort()
91
+ invariant(actual.length === expected.length && actual.every((key, index) => key === expected[index]), `${label} has an open or incomplete shape`)
92
+ return value
93
+ }
94
+
95
+ function regularFile(path, label) {
96
+ const stat = lstatSync(path)
97
+ invariant(stat.isFile() && !stat.isSymbolicLink() && stat.nlink === 1, `${label} must be one regular unaliased file`)
98
+ return path
99
+ }
100
+
101
+ function run(command, args, { cwd, input = undefined, env = process.env, encoding = 'utf8', label = command } = {}) {
102
+ const result = spawnSync(command, args, {
103
+ cwd,
104
+ input,
105
+ env,
106
+ encoding,
107
+ stdio: ['pipe', 'pipe', 'pipe'],
108
+ maxBuffer: 64 * 1024 * 1024,
109
+ })
110
+ if (result.error) throw result.error
111
+ if (result.status !== 0) {
112
+ const stdout = Buffer.isBuffer(result.stdout) ? result.stdout.toString('utf8') : String(result.stdout || '')
113
+ const stderr = Buffer.isBuffer(result.stderr) ? result.stderr.toString('utf8') : String(result.stderr || '')
114
+ throw new Error(`${label} failed(exit ${result.status}):${stderr.trim() || stdout.trim() || '<no output>'}`)
115
+ }
116
+ return result.stdout
117
+ }
118
+
119
+ function git(repository, args, options = {}) {
120
+ const {
121
+ encoding = 'utf8',
122
+ gitIdentity,
123
+ input,
124
+ } = options
125
+ const cwd = resolve(repository)
126
+ if (args[0] !== 'init') assertClosedGitLocalConfiguration(cwd)
127
+ const result = runClosedGit(args, {
128
+ cwd,
129
+ gitIdentity,
130
+ input,
131
+ maxOutputBytes: 64 * 1024 * 1024,
132
+ output: encoding === null ? 'buffer' : 'capture',
133
+ timeoutMs: 30_000,
134
+ })
135
+ if (result.error || result.signal !== null || result.status !== 0) {
136
+ const stdout = Buffer.isBuffer(result.stdout) ? result.stdout.toString('utf8') : String(result.stdout || '')
137
+ const stderr = Buffer.isBuffer(result.stderr) ? result.stderr.toString('utf8') : String(result.stderr || '')
138
+ throw new Error(`git ${args.join(' ')} failed(exit ${result.status}):${stderr.trim() || stdout.trim() || result.error?.message || '<no output>'}`)
139
+ }
140
+ return result.stdout
141
+ }
142
+
143
+ function normalizedPath(path) {
144
+ invariant(typeof path === 'string' && path.length > 0 && path.length <= 512, 'GOV-UPGRADE-PATH-005: receipt path is invalid')
145
+ invariant(!isAbsolute(path) && !path.includes('\\') && !path.includes('\0') && !path.includes('\n') && !path.includes('\r'), `GOV-UPGRADE-PATH-005: unsafe receipt path: ${JSON.stringify(path)}`)
146
+ const normalized = path.split('/').filter(part => part !== '').join('/')
147
+ invariant(normalized === path && path.split('/').every(part => part !== '.' && part !== '..'), `GOV-UPGRADE-PATH-005: non-canonical receipt path: ${path}`)
148
+ invariant(path !== '.git' && !path.startsWith('.git/'), `GOV-UPGRADE-PATH-005: git metadata path is forbidden: ${path}`)
149
+ return path
150
+ }
151
+
152
+ function canonicalCorpusEvidence(value, label) {
153
+ exactKeys(value, CORPUS_EVIDENCE_KEYS, label)
154
+ invariant(VERSION.test(value.releaseVersion || ''), `${label}.releaseVersion is invalid`)
155
+ for (const key of CORPUS_EVIDENCE_KEYS.filter(key => key !== 'releaseVersion')) {
156
+ invariant(SHA256.test(value[key] || ''), `${label}.${key} is invalid`)
157
+ }
158
+ return Object.fromEntries(CORPUS_EVIDENCE_KEYS.map(key => [key, value[key]]))
159
+ }
160
+
161
+ function canonicalPackages(packages, label) {
162
+ invariant(Array.isArray(packages) && packages.length === 2, `${label} must contain the exact upgrade package pair`)
163
+ const normalized = packages.map((item, index) => {
164
+ exactKeys(item, ['name', 'version', 'integrity'], `${label}[${index}]`)
165
+ invariant(['@qijenchen/design-system', '@qijenchen/storybook-config'].includes(item.name), `${label}[${index}].name is invalid`)
166
+ invariant(VERSION.test(item.version || '') && typeof item.integrity === 'string' && /^sha512-[A-Za-z0-9+/]+={0,2}$/.test(item.integrity), `${label}[${index}] identity is invalid`)
167
+ return { name: item.name, version: item.version, integrity: item.integrity }
168
+ })
169
+ invariant(JSON.stringify(normalized.map(item => item.name)) === JSON.stringify(['@qijenchen/design-system', '@qijenchen/storybook-config']), `${label} order/set is invalid`)
170
+ return normalized
171
+ }
172
+
173
+ function canonicalProvenance(value, label = 'upgrade provenance') {
174
+ exactKeys(value, PROVENANCE_KEYS, label)
175
+ invariant(value.repository === 'ajenchen/design-system', `${label}.repository is invalid`)
176
+ invariant(value.workflow === '.github/workflows/release.yml', `${label}.workflow is invalid`)
177
+ const packages = canonicalPackages(value.packages, `${label}.packages`)
178
+ invariant(packages.every(item => item.version === packages[0].version), `${label}.packages do not bind one exact release version`)
179
+ invariant(typeof value.tag === 'string' && value.tag === `v${packages[0].version}`, `${label}.tag is invalid`)
180
+ invariant(GIT_OBJECT.test(value.tagObject || ''), `${label}.tagObject is invalid`)
181
+ invariant(GIT_OBJECT.test(value.gitCommit || ''), `${label}.gitCommit is invalid`)
182
+ invariant(GIT_OBJECT.test(value.gitTree || ''), `${label}.gitTree is invalid`)
183
+ invariant(SHA256.test(value.bomSha256 || '') && value.releaseAssetDigest === `sha256:${value.bomSha256}`, `${label} immutable Release BOM binding is invalid`)
184
+ for (const field of ['releaseSetSha256', 'finalizationReceiptSha256', 'releaseTrustEvidenceSha256']) {
185
+ invariant(SHA256.test(value[field] || ''), `${label}.${field} is invalid`)
186
+ }
187
+ return {
188
+ repository: value.repository,
189
+ workflow: value.workflow,
190
+ tag: value.tag,
191
+ tagObject: value.tagObject,
192
+ gitCommit: value.gitCommit,
193
+ gitTree: value.gitTree,
194
+ bomSha256: value.bomSha256,
195
+ releaseAssetDigest: value.releaseAssetDigest,
196
+ releaseSetSha256: value.releaseSetSha256,
197
+ finalizationReceiptSha256: value.finalizationReceiptSha256,
198
+ releaseTrustEvidenceSha256: value.releaseTrustEvidenceSha256,
199
+ packages,
200
+ }
201
+ }
202
+
203
+ function canonicalToolchain(value, label = 'upgrade toolchain') {
204
+ exactKeys(value, TOOLCHAIN_KEYS, label)
205
+ invariant(/^v\d+\.\d+\.\d+$/.test(value.node || ''), `${label}.node is invalid`)
206
+ invariant(/^\d+\.\d+\.\d+$/.test(value.npm || ''), `${label}.npm is invalid`)
207
+ return { node: value.node, npm: value.npm }
208
+ }
209
+
210
+ function canonicalMaterialization(value, label) {
211
+ exactKeys(value, MATERIALIZATION_KEYS, label)
212
+ invariant(Array.isArray(value.exactPaths) && Array.isArray(value.pathPrefixes), `${label} is invalid`)
213
+ const exactPaths = value.exactPaths.map(normalizedPath)
214
+ const pathPrefixes = value.pathPrefixes.map(prefix => {
215
+ invariant(typeof prefix === 'string' && prefix.endsWith('/'), `${label} prefix must end in slash`)
216
+ return `${normalizedPath(prefix.slice(0, -1))}/`
217
+ })
218
+ invariant(JSON.stringify(exactPaths) === JSON.stringify([...new Set(exactPaths)].sort()), `${label}.exactPaths must be sorted and unique`)
219
+ invariant(JSON.stringify(pathPrefixes) === JSON.stringify([...new Set(pathPrefixes)].sort()), `${label}.pathPrefixes must be sorted and unique`)
220
+ for (const exact of exactPaths) for (const prefix of pathPrefixes) {
221
+ invariant(!exact.startsWith(prefix), `${label} exact path overlaps a tree prefix:${exact}:${prefix}`)
222
+ }
223
+ for (let left = 0; left < pathPrefixes.length; left += 1) for (let right = left + 1; right < pathPrefixes.length; right += 1) {
224
+ invariant(!pathPrefixes[right].startsWith(pathPrefixes[left]), `${label} tree prefixes overlap:${pathPrefixes[left]}:${pathPrefixes[right]}`)
225
+ }
226
+ return { exactPaths, pathPrefixes }
227
+ }
228
+
229
+ function canonicalOperations(operations, label = 'upgrade operations') {
230
+ invariant(Array.isArray(operations) && operations.length > 0, `${label} must be a non-empty array`)
231
+ const normalized = operations.map((item, index) => {
232
+ exactKeys(item, OPERATION_KEYS, `${label}[${index}]`)
233
+ invariant(['A', 'M', 'D'].includes(item.status), `${label}[${index}].status is unsupported`)
234
+ return { path: normalizedPath(item.path), status: item.status }
235
+ })
236
+ invariant(new Set(normalized.map(item => item.path)).size === normalized.length, `${label} contains duplicate paths`)
237
+ invariant(JSON.stringify(normalized) === JSON.stringify([...normalized].sort((left, right) => compareUtf8Bytes(left.path, right.path))), `${label} must be sorted by path`)
238
+ return normalized
239
+ }
240
+
241
+ function policyAllows(policy, path) {
242
+ return policy.exactPaths.includes(path) || policy.pathPrefixes.some(prefix => path.startsWith(prefix))
243
+ }
244
+
245
+ function retirementAllows(retirements, path) {
246
+ return retirements.some(surface => surface.kind === 'file' ? surface.path === path : path === surface.path || path.startsWith(`${surface.path}/`))
247
+ }
248
+
249
+ export function requiresReviewedBootstrap(path) {
250
+ const normalized = normalizedPath(path)
251
+ return requiresReviewedControlPlaneUpdate(normalized)
252
+ }
253
+
254
+ export function validateUpgradeOperations(operations, authority) {
255
+ exactKeys(authority, AUTHORITY_KEYS, 'upgrade mutation authority')
256
+ invariant(authority.schemaVersion === 1, 'upgrade mutation authority schema is unsupported')
257
+ const previous = canonicalMaterialization(authority.previousMaterialization, 'previous materialization')
258
+ const incoming = canonicalMaterialization(authority.incomingMaterialization, 'incoming materialization')
259
+ invariant(Array.isArray(authority.retirementDeletes), 'upgrade retirement deletion authority is invalid')
260
+ const retirements = authority.retirementDeletes.map((surface, index) => {
261
+ exactKeys(surface, ['kind', 'path', 'sha256'], `retirement deletion[${index}]`)
262
+ invariant(['file', 'tree'].includes(surface.kind) && SHA256.test(surface.sha256 || ''), `retirement deletion[${index}] is invalid`)
263
+ return { kind: surface.kind, path: normalizedPath(surface.path), sha256: surface.sha256 }
264
+ })
265
+ invariant(JSON.stringify(retirements) === JSON.stringify([...retirements].sort((left, right) => compareUtf8Bytes(left.path, right.path))), 'retirement deletions must be sorted')
266
+ const normalized = canonicalOperations(operations)
267
+ for (const operation of normalized) {
268
+ invariant(
269
+ !requiresReviewedBootstrap(operation.path),
270
+ `GOV-UPGRADE-BOOTSTRAP-001: ordinary upgrade cannot change protected control-plane path; ${REVIEWED_CONTROL_PLANE_UPDATE_ROUTE}: ${operation.path}`,
271
+ )
272
+ const before = policyAllows(previous, operation.path)
273
+ const after = policyAllows(incoming, operation.path)
274
+ const retired = retirementAllows(retirements, operation.path)
275
+ if (operation.status === 'A') invariant(after, `GOV-UPGRADE-PATH-007: added path is outside incoming authority: ${operation.path}`)
276
+ else if (operation.status === 'M') invariant(before && after, `GOV-UPGRADE-PATH-007: modified path is not retained governance authority: ${operation.path}`)
277
+ else invariant((before && after) || retired, `GOV-UPGRADE-PATH-007: deleted path has no retained authority or current retirement tombstone: ${operation.path}`)
278
+ }
279
+ return normalized
280
+ }
281
+
282
+ export function buildUpgradeAuthority({ previousCorpus, incomingCorpus, provenance, toolchain, providerTransition = null }) {
283
+ const transition = providerTransition || validateInstalledProviderTransition(previousCorpus, incomingCorpus)
284
+ invariant(transition.mode === 'upgrade', 'upgrade authority requires one monotonic lifecycle transition')
285
+ const retirementDeletes = transition.retirements
286
+ .flatMap(retirement => retirement.surfaces.map(surface => ({ kind: surface.kind, path: normalizedPath(surface.path), sha256: surface.sha256 })))
287
+ .sort((left, right) => compareUtf8Bytes(left.path, right.path))
288
+ invariant(new Set(retirementDeletes.map(item => item.path)).size === retirementDeletes.length, 'upgrade authority retirement paths are duplicated')
289
+ return {
290
+ schemaVersion: 1,
291
+ previousCorpus: canonicalCorpusEvidence(previousCorpus.evidence, 'previous corpus evidence'),
292
+ incomingCorpus: canonicalCorpusEvidence(incomingCorpus.evidence, 'incoming corpus evidence'),
293
+ provenance: canonicalProvenance(provenance),
294
+ toolchain: canonicalToolchain(toolchain),
295
+ previousMaterialization: canonicalMaterialization(previousCorpus.manifest.consumer.materialization, 'previous materialization'),
296
+ incomingMaterialization: canonicalMaterialization(incomingCorpus.manifest.consumer.materialization, 'incoming materialization'),
297
+ retirementDeletes,
298
+ }
299
+ }
300
+
301
+ export function upgradeAuthoritySha256(authority) {
302
+ exactKeys(authority, AUTHORITY_KEYS, 'upgrade mutation authority')
303
+ return sha256(JSON.stringify(authority))
304
+ }
305
+
306
+ export function validateUpgradeReceipt({
307
+ receipt,
308
+ patchBytes,
309
+ expectedBaseSha,
310
+ expectedVersion,
311
+ expectedPatchSha256 = null,
312
+ expectedTreeSha = null,
313
+ expectedAuthoritySha256 = null,
314
+ authority = null,
315
+ }) {
316
+ exactKeys(receipt, RECEIPT_KEYS, 'GOV-UPGRADE-EVIDENCE-001: receipt')
317
+ invariant(receipt.schemaVersion === 2, 'GOV-UPGRADE-EVIDENCE-001: unsupported receipt version')
318
+ invariant(GIT_OBJECT.test(expectedBaseSha || '') && receipt.baseSha === expectedBaseSha, 'GOV-UPGRADE-EVIDENCE-002: receipt base SHA mismatch')
319
+ invariant(VERSION.test(expectedVersion || '') && receipt.version === expectedVersion, 'GOV-UPGRADE-EVIDENCE-003: receipt version mismatch')
320
+ invariant(SHA256.test(receipt.patchSha256 || '') && sha256(patchBytes) === receipt.patchSha256, 'GOV-UPGRADE-EVIDENCE-004: patch digest mismatch')
321
+ if (expectedPatchSha256 !== null) invariant(SHA256.test(expectedPatchSha256) && receipt.patchSha256 === expectedPatchSha256, 'GOV-UPGRADE-EVIDENCE-004: receipt differs from certifier patch output')
322
+ invariant(GIT_OBJECT.test(receipt.treeSha256 || ''), 'GOV-UPGRADE-EVIDENCE-005: tree identity is invalid')
323
+ if (expectedTreeSha !== null) invariant(GIT_OBJECT.test(expectedTreeSha) && receipt.treeSha256 === expectedTreeSha, 'GOV-UPGRADE-EVIDENCE-005: receipt differs from certifier tree output')
324
+ const operations = canonicalOperations(receipt.operations, 'receipt operations')
325
+ const paths = receipt.paths.map(normalizedPath)
326
+ invariant(JSON.stringify(paths) === JSON.stringify(operations.map(item => item.path)), 'GOV-UPGRADE-PATH-005: receipt paths differ from its closed operations')
327
+ canonicalCorpusEvidence(receipt.previousCorpus, 'receipt previous corpus')
328
+ canonicalCorpusEvidence(receipt.incomingCorpus, 'receipt incoming corpus')
329
+ canonicalProvenance(receipt.provenance, 'receipt provenance')
330
+ canonicalToolchain(receipt.toolchain, 'receipt toolchain')
331
+ invariant(SHA256.test(receipt.authoritySha256 || ''), 'receipt authority digest is invalid')
332
+ if (expectedAuthoritySha256 !== null) invariant(SHA256.test(expectedAuthoritySha256) && receipt.authoritySha256 === expectedAuthoritySha256, 'receipt differs from certifier authority output')
333
+ if (authority !== null) {
334
+ const digest = upgradeAuthoritySha256(authority)
335
+ invariant(receipt.authoritySha256 === digest, 'receipt authority differs from protected-base reconstruction')
336
+ invariant(JSON.stringify(receipt.previousCorpus) === JSON.stringify(authority.previousCorpus), 'receipt previous corpus differs from protected-base reconstruction')
337
+ invariant(JSON.stringify(receipt.incomingCorpus) === JSON.stringify(authority.incomingCorpus), 'receipt incoming corpus differs from protected-base reconstruction')
338
+ invariant(JSON.stringify(receipt.provenance) === JSON.stringify(authority.provenance), 'receipt provenance differs from protected-base reconstruction')
339
+ invariant(JSON.stringify(receipt.toolchain) === JSON.stringify(authority.toolchain), 'receipt toolchain differs from protected-base reconstruction')
340
+ validateUpgradeOperations(operations, authority)
341
+ }
342
+ return { ...receipt, paths, operations }
343
+ }
344
+
345
+ export function stagedUpgradeOperations(repository) {
346
+ const raw = git(repository, ['diff', '--cached', '--name-status', '-z', '--no-renames'], { encoding: null })
347
+ const tokens = raw.toString('utf8').split('\0')
348
+ if (tokens.at(-1) === '') tokens.pop()
349
+ invariant(tokens.length > 0 && tokens.length % 2 === 0, 'GOV-UPGRADE-PATH-003: staged name-status inventory is malformed or empty')
350
+ const operations = []
351
+ for (let index = 0; index < tokens.length; index += 2) {
352
+ const status = tokens[index]
353
+ invariant(['A', 'M', 'D'].includes(status), `GOV-UPGRADE-PATH-004: staged path has unsupported type/status:${status}`)
354
+ operations.push({ path: normalizedPath(tokens[index + 1]), status })
355
+ }
356
+ return canonicalOperations(operations)
357
+ }
358
+
359
+ function stagedPatch(repository) {
360
+ return git(repository, ['diff', '--cached', '--binary', '--full-index', '--no-ext-diff', '--no-textconv', '--no-renames'], { encoding: null })
361
+ }
362
+
363
+ function assertNoChangedSymlinkOrSubmodule(repository) {
364
+ const summary = git(repository, ['diff', '--cached', '--summary', '--no-renames'])
365
+ invariant(!/(?:create|mode change) mode (?:120000|160000)/.test(summary), 'GOV-UPGRADE-PATH-004: evidence contains a symlink/submodule mutation')
366
+ const raw = git(repository, ['diff', '--cached', '--raw', '-z', '--no-renames'], { encoding: null }).toString('utf8')
367
+ invariant(!/(?:^|\s)(?:120000|160000)\s/.test(raw), 'GOV-UPGRADE-PATH-004: evidence raw diff contains a symlink/submodule')
368
+ }
369
+
370
+ function stagedTree(repository) {
371
+ return git(repository, ['write-tree']).trim()
372
+ }
373
+
374
+ function assertProtectedBase(repository, expectedBaseSha) {
375
+ invariant(git(repository, ['rev-parse', 'HEAD']).trim() === expectedBaseSha, 'GOV-UPGRADE-SOURCE-003: checkout does not match the protected base')
376
+ invariant(git(repository, ['diff', '--cached', '--name-only', '-z'], { encoding: null }).length === 0, 'GOV-UPGRADE-SOURCE-004: protected base index is dirty')
377
+ const status = git(repository, ['status', '--porcelain=v1', '-z', '--untracked-files=all', '--ignore-submodules=none', '--no-renames'], { encoding: null })
378
+ invariant(status.length === 0, 'GOV-UPGRADE-SOURCE-004: protected base worktree or untracked inventory is dirty')
379
+ }
380
+
381
+ export function assertLiveProtectedMain(repository, expectedBaseSha, remote = 'origin') {
382
+ invariant(typeof remote === 'string' && /^[A-Za-z0-9._-]+$/.test(remote), 'GOV-UPGRADE-SOURCE-006: live-main remote is invalid')
383
+ const remoteUrls = git(repository, [
384
+ 'config',
385
+ '--local',
386
+ '--no-includes',
387
+ '--get-all',
388
+ `remote.${remote}.url`,
389
+ ]).trim().split(/\r?\n/).filter(Boolean)
390
+ invariant(remoteUrls.length === 1, 'GOV-UPGRADE-SOURCE-006: live-main remote URL must resolve exactly once')
391
+ let parsedRemote
392
+ try {
393
+ parsedRemote = new URL(remoteUrls[0])
394
+ } catch {
395
+ throw new Error('GOV-UPGRADE-SOURCE-006: live-main remote URL is invalid')
396
+ }
397
+ invariant(
398
+ parsedRemote.protocol === 'https:'
399
+ && parsedRemote.hostname.toLowerCase() === 'github.com'
400
+ && !parsedRemote.username
401
+ && !parsedRemote.password
402
+ && !parsedRemote.port
403
+ && !parsedRemote.search
404
+ && !parsedRemote.hash
405
+ && /^\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+(?:\.git)?$/.test(parsedRemote.pathname),
406
+ 'GOV-UPGRADE-SOURCE-006: live-main remote must be one credential-free canonical GitHub HTTPS URL',
407
+ )
408
+ const canonicalRemote = `https://github.com/${parsedRemote.pathname.replace(/^\/+/, '').replace(/\.git$/, '')}.git`
409
+ const output = git(repository, ['ls-remote', '--exit-code', canonicalRemote, 'refs/heads/main']).trim()
410
+ const lines = output.split(/\r?\n/).filter(Boolean)
411
+ invariant(lines.length === 1, 'GOV-UPGRADE-SOURCE-006: live protected main did not resolve exactly once')
412
+ const [sha, ref, ...extra] = lines[0].split(/\s+/)
413
+ invariant(extra.length === 0 && ref === 'refs/heads/main' && GIT_OBJECT.test(sha || ''), 'GOV-UPGRADE-SOURCE-006: live protected main response is malformed')
414
+ invariant(sha === expectedBaseSha, `GOV-UPGRADE-SOURCE-006: protected main moved after reconstruction:${expectedBaseSha}:${sha}`)
415
+ return sha
416
+ }
417
+
418
+ export function assertProtectedPackageScripts(repository, expectedBaseSha, incomingRepository = repository) {
419
+ const base = JSON.parse(git(repository, ['show', `${expectedBaseSha}:package.json`]))
420
+ const incoming = JSON.parse(readFileSync(regularFile(join(incomingRepository, 'package.json'), 'upgrade package manifest'), 'utf8'))
421
+ invariant(
422
+ packageScriptsAreIdentical(base, incoming, {
423
+ previousLabel: 'protected-base package manifest',
424
+ incomingLabel: 'incoming package manifest',
425
+ }),
426
+ `GOV-UPGRADE-BOOTSTRAP-002: ordinary upgrade cannot change package scripts executed by protected workflows; ${REVIEWED_CONTROL_PLANE_UPDATE_ROUTE}`,
427
+ )
428
+ }
429
+
430
+ function readEvidence(evidenceDirectory) {
431
+ const evidence = realpathSync(evidenceDirectory)
432
+ const entries = readdirSync(evidence).sort()
433
+ invariant(JSON.stringify(entries) === JSON.stringify(['receipt.json', 'upgrade.patch']), 'GOV-UPGRADE-EVIDENCE-006: evidence inventory must contain exactly receipt.json and upgrade.patch')
434
+ const receiptPath = regularFile(join(evidence, 'receipt.json'), 'receipt')
435
+ const patchPath = regularFile(join(evidence, 'upgrade.patch'), 'patch')
436
+ return {
437
+ evidence,
438
+ receipt: JSON.parse(readFileSync(receiptPath, 'utf8')),
439
+ patchBytes: readFileSync(patchPath),
440
+ }
441
+ }
442
+
443
+ function isolatedNpmEnvironment(root) {
444
+ const configRoot = join(root, '.npm-config')
445
+ const cacheRoot = join(root, '.npm-cache')
446
+ const homeRoot = join(root, '.npm-home')
447
+ const temporaryRoot = join(root, '.npm-tmp')
448
+ mkdirSync(configRoot, { mode: 0o700 })
449
+ mkdirSync(cacheRoot, { mode: 0o700 })
450
+ mkdirSync(homeRoot, { mode: 0o700 })
451
+ mkdirSync(temporaryRoot, { mode: 0o700 })
452
+ const user = join(configRoot, 'user.npmrc')
453
+ const global = join(configRoot, 'global.npmrc')
454
+ const config = [
455
+ `registry=${NPM_REGISTRY}`,
456
+ 'provenance=true',
457
+ 'ignore-scripts=true',
458
+ 'strict-ssl=true',
459
+ 'fund=false',
460
+ 'update-notifier=false',
461
+ 'color=false',
462
+ 'progress=false',
463
+ '',
464
+ ].join('\n')
465
+ writeFileSync(user, config, { flag: 'wx', mode: 0o600 })
466
+ writeFileSync(global, config, { flag: 'wx', mode: 0o600 })
467
+ chmodSync(user, 0o600)
468
+ chmodSync(global, 0o600)
469
+ return Object.freeze({
470
+ HOME: homeRoot,
471
+ LANG: 'C',
472
+ LC_ALL: 'C',
473
+ LOGNAME: 'governance',
474
+ NO_COLOR: '1',
475
+ PATH: process.platform === 'win32'
476
+ ? String(process.env.SystemRoot || 'C:\\Windows') + '\\System32'
477
+ : '/usr/bin:/bin',
478
+ TMPDIR: temporaryRoot,
479
+ TZ: 'UTC',
480
+ USER: 'governance',
481
+ NPM_CONFIG_CACHE: cacheRoot,
482
+ NPM_CONFIG_COLOR: 'false',
483
+ NPM_CONFIG_FUND: 'false',
484
+ NPM_CONFIG_USERCONFIG: user,
485
+ NPM_CONFIG_GLOBALCONFIG: global,
486
+ NPM_CONFIG_REGISTRY: NPM_REGISTRY,
487
+ NPM_CONFIG_IGNORE_SCRIPTS: 'true',
488
+ NPM_CONFIG_PROGRESS: 'false',
489
+ NPM_CONFIG_PROVENANCE: 'true',
490
+ NPM_CONFIG_STRICT_SSL: 'true',
491
+ NPM_CONFIG_UPDATE_NOTIFIER: 'false',
492
+ ...(process.platform === 'darwin' ? { __CF_USER_TEXT_ENCODING: '0x1F5:0x0:0x0' } : {}),
493
+ })
494
+ }
495
+
496
+ function prepareSandboxFromProtectedBase(repository, sandbox, expectedBaseSha) {
497
+ mkdirSync(sandbox, { recursive: true, mode: 0o700 })
498
+ git(repository, ['checkout-index', '--all', '--force', `--prefix=${sandbox}${sep}`])
499
+ git(sandbox, ['init', '--quiet'])
500
+ mkdirSync(join(sandbox, '.git', 'info'), { recursive: true })
501
+ writeFileSync(join(sandbox, '.git', 'info', 'exclude'), 'node_modules/\n.governance-upgrade-journal.json\n')
502
+ git(sandbox, ['config', 'core.autocrlf', 'false'])
503
+ git(sandbox, ['config', 'core.filemode', 'true'])
504
+ git(sandbox, ['add', '-A', '--', '.'])
505
+ const baseTree = stagedTree(sandbox)
506
+ const expectedTree = git(repository, ['rev-parse', `${expectedBaseSha}^{tree}`]).trim()
507
+ invariant(baseTree === expectedTree, 'GOV-UPGRADE-SOURCE-005: disposable protected-base tree differs from the source commit')
508
+ const commit = git(sandbox, ['commit-tree', baseTree, '-m', 'protected base'], {
509
+ gitIdentity: {
510
+ authorName: 'Governance verifier',
511
+ authorEmail: 'governance-verifier@example.invalid',
512
+ committerName: 'Governance verifier',
513
+ committerEmail: 'governance-verifier@example.invalid',
514
+ authorDate: '2000-01-01T00:00:00Z',
515
+ committerDate: '2000-01-01T00:00:00Z',
516
+ },
517
+ }).trim()
518
+ git(sandbox, ['symbolic-ref', 'HEAD', 'refs/heads/protected-base'])
519
+ git(sandbox, ['update-ref', 'refs/heads/protected-base', commit])
520
+ git(sandbox, ['reset', '--quiet', '--mixed', commit])
521
+ }
522
+
523
+ function exactInstalledUpgradePackages(repository, version, policy) {
524
+ const lock = JSON.parse(readFileSync(regularFile(join(repository, 'package-lock.json'), 'reconstructed package lock'), 'utf8'))
525
+ invariant(lock.lockfileVersion === 3 && lock.packages && typeof lock.packages === 'object' && !Array.isArray(lock.packages), 'reconstructed npm lock must use closed lockfileVersion 3')
526
+ return policy.upgradePackages.map(name => {
527
+ const entry = lock.packages[`node_modules/${name}`]
528
+ const expectedTarball = `${NPM_REGISTRY}${name}/-/${name.split('/').at(-1)}-${version}.tgz`
529
+ invariant(entry?.version === version && entry?.resolved === expectedTarball, `${name}: reconstructed lock is not the exact canonical npm artifact`)
530
+ invariant(typeof entry.integrity === 'string', `${name}: reconstructed lock lacks integrity`)
531
+ return { name, version: entry.version, integrity: entry.integrity }
532
+ })
533
+ }
534
+
535
+ function temporaryRefreshCapability(projectDir, verifiedCorpus, providerTransition) {
536
+ return authorizeDisposableProviderRefreshTransaction({
537
+ projectDir,
538
+ verifiedCorpus,
539
+ providerTransition,
540
+ })
541
+ }
542
+
543
+ export async function reconstructExpectedUpgrade({
544
+ repository,
545
+ expectedBaseSha,
546
+ expectedVersion,
547
+ releaseLookup = undefined,
548
+ captureMaterialized = undefined,
549
+ runtimeFactory = prepareVerifiedExactNpmRuntime,
550
+ }) {
551
+ const repo = canonicalRepositoryRoot(repository)
552
+ assertNoRootNpmShrinkwrap(repo, { errorPrefix: 'GOV-UPGRADE-LOCK-001' })
553
+ assertProtectedBase(repo, expectedBaseSha)
554
+ const expectedNpmArtifact = resolveExactNpmRuntimeContract(repo)
555
+ const trustPolicy = validateUpgradeTrustPolicy(JSON.parse(readFileSync(regularFile(join(repo, 'governance/lock.json'), 'protected-base governance lock'), 'utf8')).upgradeTrust)
556
+ const temporaryRoot = realpathSync(mkdtempSync(join(tmpdir(), 'governance-upgrade-reconstruct-')))
557
+ const sandbox = join(temporaryRoot, 'repository')
558
+ let npmRuntime = null
559
+ try {
560
+ prepareSandboxFromProtectedBase(repo, sandbox, expectedBaseSha)
561
+ assertNoRootNpmShrinkwrap(sandbox, { errorPrefix: 'GOV-UPGRADE-LOCK-001' })
562
+ const npmEnv = isolatedNpmEnvironment(temporaryRoot)
563
+ invariant(typeof runtimeFactory === 'function', 'verified exact npm runtime factory must be a function')
564
+ npmRuntime = await runtimeFactory({
565
+ repositoryRoot: repo,
566
+ parentDirectory: temporaryRoot,
567
+ env: npmEnv,
568
+ })
569
+ try {
570
+ assertVerifiedExactNpmRuntimeCapability(npmRuntime, expectedNpmArtifact)
571
+ } catch {
572
+ invariant(false, 'verified exact npm runtime factory returned an invalid overlay-bound capability')
573
+ }
574
+ const { cli, toolchain } = npmRuntime
575
+ // Reconstruct the previous corpus from the protected commit and its committed lock. The host
576
+ // checkout's ignored node_modules (including npm itself) is intentionally never an authority.
577
+ run(process.execPath, [
578
+ cli, 'ci', '--legacy-peer-deps', '--ignore-scripts', '--no-audit', '--no-fund',
579
+ `--registry=${NPM_REGISTRY}`,
580
+ ], { cwd: sandbox, env: npmEnv, label: 'verified exact npm protected-base reconstruction' })
581
+ const previousCorpus = validateInstalledForkCorpus(sandbox)
582
+ run(process.execPath, [
583
+ cli, 'install',
584
+ `@qijenchen/design-system@${expectedVersion}`,
585
+ `@qijenchen/storybook-config@${expectedVersion}`,
586
+ '--save-exact', '--legacy-peer-deps', '--ignore-scripts', '--no-audit', '--no-fund',
587
+ `--registry=${NPM_REGISTRY}`,
588
+ ], { cwd: sandbox, env: npmEnv, label: 'protected-base exact npm install in disposable reconstruction' })
589
+ const installedOverlayReceipt = npmRuntime.applyInstalledSecurityOverlay(sandbox)
590
+ const signatureJson = run(process.execPath, [
591
+ cli, 'audit', 'signatures', '--json', `--registry=${NPM_REGISTRY}`,
592
+ ], { cwd: sandbox, env: npmEnv, label: 'protected-base npm signature audit' })
593
+ let auditReport
594
+ try { auditReport = JSON.parse(signatureJson) } catch { throw new Error('protected-base npm signature audit did not produce closed JSON') }
595
+ runVerifiedHighVulnerabilityAudit(process.execPath, [
596
+ cli, 'audit', '--audit-level=high', '--json', `--registry=${NPM_REGISTRY}`,
597
+ ], {
598
+ root: sandbox,
599
+ environment: npmEnv,
600
+ npmRuntime,
601
+ installedOverlayReceipt,
602
+ errorPrefix: 'GOV-UPGRADE-DEPENDENCY-001',
603
+ timeoutMs: 30 * 60 * 1_000,
604
+ })
605
+ const packages = exactInstalledUpgradePackages(sandbox, expectedVersion, trustPolicy)
606
+ const provenance = await verifyUpgradeProvenance({
607
+ auditReport,
608
+ packages,
609
+ version: expectedVersion,
610
+ policy: trustPolicy,
611
+ // Consumer 升級走 ordinary 車道:tag→commit→tree + BOM + npm SLSA provenance;
612
+ // GitHub-verified 簽章與 finalizer evidence 屬 opt-in 高保證 finalizer(RELEASE_HIGH_ASSURANCE)。
613
+ ordinaryRelease: true,
614
+ ...(releaseLookup ? { releaseLookup } : {}),
615
+ })
616
+ const incomingCorpus = validateInstalledForkCorpus(sandbox)
617
+ invariant(incomingCorpus.evidence.releaseVersion === expectedVersion, 'reconstructed incoming corpus release differs from requested exact version')
618
+ const providerTransition = validateInstalledProviderTransition(previousCorpus, incomingCorpus)
619
+ const transactionCapability = temporaryRefreshCapability(sandbox, incomingCorpus, providerTransition)
620
+ const refresh = refreshLaunchers(sandbox, {
621
+ dryRun: false,
622
+ verifiedCorpus: incomingCorpus,
623
+ providerTransition,
624
+ transactionCapability,
625
+ })
626
+ invariant(!refresh?.error && !refresh?.skipped, `protected-base materializer did not converge:${refresh?.error || refresh?.skipped}`)
627
+ assertProtectedPackageScripts(repo, expectedBaseSha, sandbox)
628
+ git(sandbox, ['add', '-A', '--', '.'])
629
+ assertNoChangedSymlinkOrSubmodule(sandbox)
630
+ const operations = stagedUpgradeOperations(sandbox)
631
+ const authority = buildUpgradeAuthority({ previousCorpus, incomingCorpus, provenance, toolchain, providerTransition })
632
+ validateUpgradeOperations(operations, authority)
633
+ if (captureMaterialized !== undefined) {
634
+ invariant(typeof captureMaterialized === 'function', 'protected-base materialization capture must be a function')
635
+ await captureMaterialized({
636
+ sandbox,
637
+ previousCorpus,
638
+ incomingCorpus,
639
+ providerTransition,
640
+ provenance,
641
+ toolchain,
642
+ authority,
643
+ operations,
644
+ })
645
+ }
646
+ return {
647
+ authority,
648
+ authoritySha256: upgradeAuthoritySha256(authority),
649
+ operations,
650
+ paths: operations.map(item => item.path),
651
+ patchBytes: stagedPatch(sandbox),
652
+ treeSha256: stagedTree(sandbox),
653
+ }
654
+ } finally {
655
+ try { npmRuntime?.cleanup?.() } finally { rmSync(temporaryRoot, { recursive: true, force: true }) }
656
+ }
657
+ }
658
+
659
+ function verifyStagedTree({ repository, receipt, patchBytes, authority = null }) {
660
+ assertNoChangedSymlinkOrSubmodule(repository)
661
+ const operations = stagedUpgradeOperations(repository)
662
+ invariant(JSON.stringify(operations) === JSON.stringify(receipt.operations), 'GOV-UPGRADE-PATH-003: applied operations differ from certified receipt')
663
+ if (authority) validateUpgradeOperations(operations, authority)
664
+ invariant(stagedTree(repository) === receipt.treeSha256, 'GOV-UPGRADE-TREE-002: independently applied tree differs from receipt')
665
+ invariant(stagedPatch(repository).equals(patchBytes), 'GOV-UPGRADE-EVIDENCE-007: independently applied patch bytes differ from receipt artifact')
666
+ }
667
+
668
+ function applyVerifiedPatchTransaction({
669
+ repository,
670
+ expectedBaseSha,
671
+ receipt,
672
+ patchBytes,
673
+ authority = null,
674
+ }) {
675
+ let applied = false
676
+ try {
677
+ git(repository, ['apply', '--binary', '--index', '-'], { input: patchBytes, encoding: null })
678
+ applied = true
679
+ verifyStagedTree({ repository, receipt, patchBytes, authority })
680
+ } catch (error) {
681
+ if (!applied) throw error
682
+ try {
683
+ git(repository, ['apply', '--reverse', '--binary', '--index', '-'], { input: patchBytes, encoding: null })
684
+ assertProtectedBase(repository, expectedBaseSha)
685
+ } catch (rollbackError) {
686
+ throw new AggregateError(
687
+ [error, rollbackError],
688
+ `GOV-UPGRADE-ROLLBACK-001: applied evidence failed post-verification and rollback did not restore the protected base; verification=${error.message}; rollback=${rollbackError.message}`,
689
+ )
690
+ }
691
+ throw error
692
+ }
693
+ }
694
+
695
+ export function upgradeVerificationSha256(receipt, patchBytes) {
696
+ exactKeys(receipt, RECEIPT_KEYS, 'verified upgrade receipt')
697
+ invariant(Buffer.isBuffer(patchBytes), 'verified upgrade patch must be bytes')
698
+ const canonicalReceipt = Object.fromEntries(RECEIPT_KEYS.map(key => [key, receipt[key]]))
699
+ return sha256(Buffer.concat([
700
+ Buffer.from('protected-base-upgrade-verification-v1\0'),
701
+ Buffer.from(JSON.stringify(canonicalReceipt)),
702
+ Buffer.from('\0'),
703
+ patchBytes,
704
+ ]))
705
+ }
706
+
707
+ export async function verifyAndApplyUpgradeEvidence({
708
+ repository,
709
+ evidenceDirectory,
710
+ expectedBaseSha,
711
+ expectedVersion,
712
+ expectedPatchSha256,
713
+ expectedTreeSha,
714
+ expectedAuthoritySha256,
715
+ reconstruct = reconstructExpectedUpgrade,
716
+ verificationOnly = false,
717
+ liveMainRemote = null,
718
+ liveMainVerifier = assertLiveProtectedMain,
719
+ }) {
720
+ invariant(typeof liveMainVerifier === 'function', 'GOV-UPGRADE-SOURCE-006: live-main verifier must be callable')
721
+ const repo = canonicalRepositoryRoot(repository)
722
+ assertProtectedBase(repo, expectedBaseSha)
723
+ const { receipt: rawReceipt, patchBytes } = readEvidence(evidenceDirectory)
724
+ const basic = validateUpgradeReceipt({
725
+ receipt: rawReceipt,
726
+ patchBytes,
727
+ expectedBaseSha,
728
+ expectedVersion,
729
+ expectedPatchSha256,
730
+ expectedTreeSha,
731
+ expectedAuthoritySha256,
732
+ })
733
+ const reconstructed = await reconstruct({ repository: repo, expectedBaseSha, expectedVersion })
734
+ invariant(reconstructed.treeSha256 === basic.treeSha256, 'GOV-UPGRADE-TREE-003: certifier tree differs from protected-base reconstruction')
735
+ invariant(reconstructed.patchBytes.equals(patchBytes), 'GOV-UPGRADE-EVIDENCE-007: certifier patch differs from protected-base reconstruction')
736
+ invariant(JSON.stringify(reconstructed.operations) === JSON.stringify(basic.operations), 'GOV-UPGRADE-PATH-003: certifier operations differ from protected-base reconstruction')
737
+ const receipt = validateUpgradeReceipt({
738
+ receipt: basic,
739
+ patchBytes,
740
+ expectedBaseSha,
741
+ expectedVersion,
742
+ expectedPatchSha256,
743
+ expectedTreeSha,
744
+ expectedAuthoritySha256,
745
+ authority: reconstructed.authority,
746
+ })
747
+ // Reconstruction can take many minutes. Re-prove both the local checkout and the live default
748
+ // branch after it completes and before applying bytes or handing anything to a writer job.
749
+ assertProtectedBase(repo, expectedBaseSha)
750
+ if (liveMainRemote !== null) liveMainVerifier(repo, expectedBaseSha, liveMainRemote)
751
+ const verificationSha256 = upgradeVerificationSha256(receipt, patchBytes)
752
+ if (!verificationOnly) {
753
+ applyVerifiedPatchTransaction({
754
+ repository: repo,
755
+ expectedBaseSha,
756
+ receipt,
757
+ patchBytes,
758
+ authority: reconstructed.authority,
759
+ })
760
+ }
761
+ return { ...receipt, verificationSha256 }
762
+ }
763
+
764
+ export function applyPreverifiedUpgradeEvidence({
765
+ repository,
766
+ evidenceDirectory,
767
+ expectedBaseSha,
768
+ expectedVersion,
769
+ expectedPatchSha256,
770
+ expectedTreeSha,
771
+ expectedAuthoritySha256,
772
+ expectedVerificationSha256,
773
+ liveMainRemote,
774
+ liveMainVerifier = assertLiveProtectedMain,
775
+ }) {
776
+ const repo = canonicalRepositoryRoot(repository)
777
+ assertProtectedBase(repo, expectedBaseSha)
778
+ invariant(SHA256.test(expectedVerificationSha256 || ''), 'GOV-UPGRADE-EVIDENCE-008: trusted verification digest is invalid')
779
+ invariant(typeof liveMainRemote === 'string', 'GOV-UPGRADE-SOURCE-006: apply mode requires a live-main remote')
780
+ invariant(typeof liveMainVerifier === 'function', 'GOV-UPGRADE-SOURCE-006: live-main verifier must be callable')
781
+ const { receipt: rawReceipt, patchBytes } = readEvidence(evidenceDirectory)
782
+ const receipt = validateUpgradeReceipt({
783
+ receipt: rawReceipt,
784
+ patchBytes,
785
+ expectedBaseSha,
786
+ expectedVersion,
787
+ expectedPatchSha256,
788
+ expectedTreeSha,
789
+ expectedAuthoritySha256,
790
+ })
791
+ invariant(
792
+ upgradeVerificationSha256(receipt, patchBytes) === expectedVerificationSha256,
793
+ 'GOV-UPGRADE-EVIDENCE-008: evidence differs from the protected-base verification job',
794
+ )
795
+ assertProtectedBase(repo, expectedBaseSha)
796
+ liveMainVerifier(repo, expectedBaseSha, liveMainRemote)
797
+ applyVerifiedPatchTransaction({
798
+ repository: repo,
799
+ expectedBaseSha,
800
+ receipt,
801
+ patchBytes,
802
+ })
803
+ return { ...receipt, verificationSha256: expectedVerificationSha256 }
804
+ }
805
+
806
+ function syncReportIdentity(report, expectedVersion) {
807
+ invariant(report && report.ok === true && report.mode === 'apply' && report.mutationAuthorized === true, 'certifier sync-all report is not a successful mutation receipt')
808
+ invariant(report.target?.designSystem === expectedVersion && report.target?.storybook === expectedVersion, 'certifier sync-all report target differs from requested release')
809
+ return {
810
+ provenance: canonicalProvenance(report.provenance, 'certifier sync-all provenance'),
811
+ toolchain: canonicalToolchain(report.toolchain, 'certifier sync-all toolchain'),
812
+ }
813
+ }
814
+
815
+ export function certifyStagedUpgradeEvidence({
816
+ repository,
817
+ evidenceDirectory,
818
+ previousRoot,
819
+ syncReportPath,
820
+ expectedBaseSha,
821
+ expectedVersion,
822
+ }) {
823
+ const repo = canonicalRepositoryRoot(repository)
824
+ invariant(git(repo, ['rev-parse', 'HEAD']).trim() === expectedBaseSha, 'certifier checkout does not match protected base')
825
+ const previousCorpus = validateInstalledForkCorpus(previousRoot)
826
+ const incomingCorpus = validateInstalledForkCorpus(repo)
827
+ invariant(incomingCorpus.evidence.releaseVersion === expectedVersion, 'certifier incoming corpus release differs from requested exact version')
828
+ const identity = syncReportIdentity(JSON.parse(readFileSync(regularFile(syncReportPath, 'sync-all report'), 'utf8')), expectedVersion)
829
+ const providerTransition = validateInstalledProviderTransition(previousCorpus, incomingCorpus)
830
+ const authority = buildUpgradeAuthority({ previousCorpus, incomingCorpus, providerTransition, ...identity })
831
+ assertProtectedPackageScripts(repo, expectedBaseSha)
832
+ const operations = stagedUpgradeOperations(repo)
833
+ validateUpgradeOperations(operations, authority)
834
+ assertNoChangedSymlinkOrSubmodule(repo)
835
+ const patchBytes = stagedPatch(repo)
836
+ invariant(patchBytes.length > 0, 'certifier produced an empty upgrade patch')
837
+ const receipt = {
838
+ schemaVersion: 2,
839
+ baseSha: expectedBaseSha,
840
+ version: expectedVersion,
841
+ patchSha256: sha256(patchBytes),
842
+ treeSha256: stagedTree(repo),
843
+ paths: operations.map(item => item.path),
844
+ operations,
845
+ previousCorpus: authority.previousCorpus,
846
+ incomingCorpus: authority.incomingCorpus,
847
+ provenance: authority.provenance,
848
+ authoritySha256: upgradeAuthoritySha256(authority),
849
+ toolchain: authority.toolchain,
850
+ }
851
+ validateUpgradeReceipt({ receipt, patchBytes, expectedBaseSha, expectedVersion, authority })
852
+ const output = resolve(evidenceDirectory)
853
+ invariant(!existsSync(output) || readdirSync(output).length === 0, 'certifier evidence output must be absent or empty')
854
+ mkdirSync(output, { recursive: true, mode: 0o700 })
855
+ assertNoSymlinkPath(dirname(output), output, 'certifier evidence directory', { allowMissing: false })
856
+ writeFileSync(join(output, 'upgrade.patch'), patchBytes, { flag: 'wx', mode: 0o600 })
857
+ writeFileSync(join(output, 'receipt.json'), `${JSON.stringify(receipt, null, 2)}\n`, { flag: 'wx', mode: 0o600 })
858
+ return receipt
859
+ }
860
+
861
+ function parseCli(args) {
862
+ const flags = new Set(['--certify-staged', '--verify-only', '--apply-preverified'])
863
+ const values = new Set([
864
+ '--repo', '--evidence', '--previous-root', '--sync-report', '--base-sha', '--version',
865
+ '--expected-patch-sha256', '--expected-tree-sha', '--expected-authority-sha256',
866
+ '--expected-verification-sha256', '--live-main-remote',
867
+ ])
868
+ const parsed = new Map()
869
+ for (let index = 0; index < args.length; index += 1) {
870
+ const token = args[index]
871
+ invariant(flags.has(token) || values.has(token), `unknown or positional option:${token}`)
872
+ invariant(!parsed.has(token), `duplicate option:${token}`)
873
+ if (flags.has(token)) parsed.set(token, true)
874
+ else {
875
+ const value = args[index + 1]
876
+ invariant(value && !value.startsWith('--'), `missing ${token}`)
877
+ parsed.set(token, value)
878
+ index += 1
879
+ }
880
+ }
881
+ const modes = ['--certify-staged', '--verify-only', '--apply-preverified'].filter(flag => parsed.has(flag))
882
+ invariant(modes.length === 1, 'exactly one evidence mode is required')
883
+ const required = parsed.has('--certify-staged')
884
+ ? ['--repo', '--evidence', '--previous-root', '--sync-report', '--base-sha', '--version']
885
+ : [
886
+ '--repo', '--evidence', '--base-sha', '--version', '--expected-patch-sha256',
887
+ '--expected-tree-sha', '--expected-authority-sha256', '--live-main-remote',
888
+ ...(parsed.has('--apply-preverified') ? ['--expected-verification-sha256'] : []),
889
+ ]
890
+ for (const flag of required) invariant(parsed.has(flag), `missing ${flag}`)
891
+ if (!parsed.has('--certify-staged')) {
892
+ invariant(!parsed.has('--previous-root') && !parsed.has('--sync-report'), 'writer mode may not accept certifier-only inputs')
893
+ } else {
894
+ invariant(
895
+ !parsed.has('--expected-patch-sha256') && !parsed.has('--expected-tree-sha')
896
+ && !parsed.has('--expected-authority-sha256') && !parsed.has('--expected-verification-sha256')
897
+ && !parsed.has('--live-main-remote'),
898
+ 'certifier mode may not accept writer output bindings',
899
+ )
900
+ }
901
+ if (parsed.has('--verify-only')) invariant(!parsed.has('--expected-verification-sha256'), 'verification mode may not accept a preverified digest')
902
+ return parsed
903
+ }
904
+
905
+ async function main() {
906
+ const args = parseCli(process.argv.slice(2))
907
+ const common = {
908
+ repository: resolve(args.get('--repo')),
909
+ evidenceDirectory: resolve(args.get('--evidence')),
910
+ expectedBaseSha: args.get('--base-sha'),
911
+ expectedVersion: args.get('--version').replace(/^v/, ''),
912
+ }
913
+ const expected = {
914
+ expectedPatchSha256: args.get('--expected-patch-sha256'),
915
+ expectedTreeSha: args.get('--expected-tree-sha'),
916
+ expectedAuthoritySha256: args.get('--expected-authority-sha256'),
917
+ }
918
+ const result = args.has('--certify-staged')
919
+ ? certifyStagedUpgradeEvidence({
920
+ ...common,
921
+ previousRoot: resolve(args.get('--previous-root')),
922
+ syncReportPath: resolve(args.get('--sync-report')),
923
+ })
924
+ : args.has('--verify-only')
925
+ ? await verifyAndApplyUpgradeEvidence({
926
+ ...common,
927
+ ...expected,
928
+ verificationOnly: true,
929
+ liveMainRemote: args.get('--live-main-remote'),
930
+ })
931
+ : applyPreverifiedUpgradeEvidence({
932
+ ...common,
933
+ ...expected,
934
+ expectedVerificationSha256: args.get('--expected-verification-sha256'),
935
+ liveMainRemote: args.get('--live-main-remote'),
936
+ })
937
+ if (process.env.GITHUB_OUTPUT) {
938
+ const output = regularFile(process.env.GITHUB_OUTPUT, 'GitHub step output')
939
+ const lines = [
940
+ `base_sha=${result.baseSha}`,
941
+ `version=${result.version}`,
942
+ `patch_sha256=${result.patchSha256}`,
943
+ `tree_sha256=${result.treeSha256}`,
944
+ `authority_sha256=${result.authoritySha256}`,
945
+ ...(result.verificationSha256 ? [`verification_sha256=${result.verificationSha256}`] : []),
946
+ ]
947
+ writeFileSync(output, `${lines.join('\n')}\n`, { flag: 'a' })
948
+ }
949
+ const action = args.has('--certify-staged')
950
+ ? 'upgrade evidence certified'
951
+ : args.has('--verify-only')
952
+ ? 'upgrade evidence independently reconstructed and verified'
953
+ : 'preverified upgrade evidence applied'
954
+ console.log(`${action} (${result.paths.length} paths)`)
955
+ }
956
+
957
+ if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
958
+ Promise.resolve(main()).catch(error => { console.error(error.message); process.exit(1) })
959
+ }