@qijenchen/design-system 0.1.0-beta.13 → 0.1.0-beta.131

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 (1751) hide show
  1. package/AGENTS.md +8 -0
  2. package/CLAUDE.md +3 -199
  3. package/README.md +6 -4
  4. package/cli-init.mjs +30 -2
  5. package/dist/components/Accordion/accordion.d.ts +3 -3
  6. package/dist/components/Accordion/accordion.d.ts.map +1 -1
  7. package/dist/components/Accordion/accordion.js +6 -3
  8. package/dist/components/Accordion/accordion.js.map +1 -1
  9. package/dist/components/Accordion/index.d.ts +2 -0
  10. package/dist/components/Accordion/index.d.ts.map +1 -0
  11. package/dist/components/Accordion/index.js +9 -0
  12. package/dist/components/Accordion/index.js.map +1 -0
  13. package/dist/components/AccountMenu/account-menu.d.ts +93 -0
  14. package/dist/components/AccountMenu/account-menu.d.ts.map +1 -0
  15. package/dist/components/AccountMenu/account-menu.js +73 -0
  16. package/dist/components/AccountMenu/account-menu.js.map +1 -0
  17. package/dist/components/AccountMenu/index.d.ts +2 -0
  18. package/dist/components/AccountMenu/index.d.ts.map +1 -0
  19. package/dist/components/AccountMenu/index.js +6 -0
  20. package/dist/components/AccountMenu/index.js.map +1 -0
  21. package/dist/components/AgentPanel/agent-fab.d.ts +24 -0
  22. package/dist/components/AgentPanel/agent-fab.d.ts.map +1 -0
  23. package/dist/components/AgentPanel/agent-logo.d.ts +75 -0
  24. package/dist/components/AgentPanel/agent-logo.d.ts.map +1 -0
  25. package/dist/components/AgentPanel/agent-logo.js +371 -0
  26. package/dist/components/AgentPanel/agent-logo.js.map +1 -0
  27. package/dist/components/AgentPanel/agent-panel.d.ts +183 -0
  28. package/dist/components/AgentPanel/agent-panel.d.ts.map +1 -0
  29. package/dist/components/AgentPanel/agent-panel.js +900 -0
  30. package/dist/components/AgentPanel/agent-panel.js.map +1 -0
  31. package/dist/components/AgentPanel/index.d.ts +2 -0
  32. package/dist/components/AgentPanel/index.d.ts.map +1 -0
  33. package/dist/components/AgentPanel/index.js +16 -0
  34. package/dist/components/AgentPanel/index.js.map +1 -0
  35. package/dist/components/Alert/alert.d.ts +7 -2
  36. package/dist/components/Alert/alert.d.ts.map +1 -1
  37. package/dist/components/Alert/alert.js +12 -8
  38. package/dist/components/Alert/alert.js.map +1 -1
  39. package/dist/components/Alert/index.d.ts +2 -0
  40. package/dist/components/Alert/index.d.ts.map +1 -0
  41. package/dist/components/Alert/index.js +7 -0
  42. package/dist/components/Alert/index.js.map +1 -0
  43. package/dist/components/AppShell/_demo-helpers.d.ts +3 -1
  44. package/dist/components/AppShell/_demo-helpers.d.ts.map +1 -1
  45. package/dist/components/AppShell/app-shell.d.ts +19 -4
  46. package/dist/components/AppShell/app-shell.d.ts.map +1 -1
  47. package/dist/components/AppShell/app-shell.js +48 -11
  48. package/dist/components/AppShell/app-shell.js.map +1 -1
  49. package/dist/components/AppShell/index.d.ts +2 -0
  50. package/dist/components/AppShell/index.d.ts.map +1 -0
  51. package/dist/components/AppShell/index.js +8 -0
  52. package/dist/components/AppShell/index.js.map +1 -0
  53. package/dist/components/AspectRatio/aspect-ratio.d.ts +3 -2
  54. package/dist/components/AspectRatio/aspect-ratio.d.ts.map +1 -1
  55. package/dist/components/AspectRatio/aspect-ratio.js +4 -2
  56. package/dist/components/AspectRatio/aspect-ratio.js.map +1 -1
  57. package/dist/components/AspectRatio/index.d.ts +2 -0
  58. package/dist/components/AspectRatio/index.d.ts.map +1 -0
  59. package/dist/components/AspectRatio/index.js +6 -0
  60. package/dist/components/AspectRatio/index.js.map +1 -0
  61. package/dist/components/Avatar/avatar.d.ts +14 -7
  62. package/dist/components/Avatar/avatar.d.ts.map +1 -1
  63. package/dist/components/Avatar/avatar.js +24 -30
  64. package/dist/components/Avatar/avatar.js.map +1 -1
  65. package/dist/components/Avatar/index.d.ts +2 -0
  66. package/dist/components/Avatar/index.d.ts.map +1 -0
  67. package/dist/components/Avatar/index.js +6 -0
  68. package/dist/components/Avatar/index.js.map +1 -0
  69. package/dist/components/Badge/badge.d.ts +21 -9
  70. package/dist/components/Badge/badge.d.ts.map +1 -1
  71. package/dist/components/Badge/badge.js +9 -6
  72. package/dist/components/Badge/badge.js.map +1 -1
  73. package/dist/components/Badge/index.d.ts +2 -0
  74. package/dist/components/Badge/index.d.ts.map +1 -0
  75. package/dist/components/Badge/index.js +7 -0
  76. package/dist/components/Badge/index.js.map +1 -0
  77. package/dist/components/Breadcrumb/breadcrumb.d.ts +16 -3
  78. package/dist/components/Breadcrumb/breadcrumb.d.ts.map +1 -1
  79. package/dist/components/Breadcrumb/breadcrumb.js +21 -53
  80. package/dist/components/Breadcrumb/breadcrumb.js.map +1 -1
  81. package/dist/components/Breadcrumb/index.d.ts +2 -0
  82. package/dist/components/Breadcrumb/index.d.ts.map +1 -0
  83. package/dist/components/Breadcrumb/index.js +12 -0
  84. package/dist/components/Breadcrumb/index.js.map +1 -0
  85. package/dist/components/BulkActionBar/bulk-action-bar.d.ts +7 -4
  86. package/dist/components/BulkActionBar/bulk-action-bar.d.ts.map +1 -1
  87. package/dist/components/BulkActionBar/bulk-action-bar.js +8 -8
  88. package/dist/components/BulkActionBar/bulk-action-bar.js.map +1 -1
  89. package/dist/components/BulkActionBar/index.d.ts +2 -0
  90. package/dist/components/BulkActionBar/index.d.ts.map +1 -0
  91. package/dist/components/BulkActionBar/index.js +7 -0
  92. package/dist/components/BulkActionBar/index.js.map +1 -0
  93. package/dist/components/Button/button-group.d.ts +31 -5
  94. package/dist/components/Button/button-group.d.ts.map +1 -1
  95. package/dist/components/Button/button-group.js +31 -14
  96. package/dist/components/Button/button-group.js.map +1 -1
  97. package/dist/components/Button/button.d.ts +15 -14
  98. package/dist/components/Button/button.d.ts.map +1 -1
  99. package/dist/components/Button/button.js +94 -52
  100. package/dist/components/Button/button.js.map +1 -1
  101. package/dist/components/Button/index.d.ts +3 -0
  102. package/dist/components/Button/index.d.ts.map +1 -0
  103. package/dist/components/Button/index.js +11 -0
  104. package/dist/components/Button/index.js.map +1 -0
  105. package/dist/components/Calendar/calendar.d.ts +22 -19
  106. package/dist/components/Calendar/calendar.d.ts.map +1 -1
  107. package/dist/components/Calendar/calendar.js +180 -135
  108. package/dist/components/Calendar/calendar.js.map +1 -1
  109. package/dist/components/Calendar/index.d.ts +2 -0
  110. package/dist/components/Calendar/index.d.ts.map +1 -0
  111. package/dist/components/Calendar/index.js +6 -0
  112. package/dist/components/Calendar/index.js.map +1 -0
  113. package/dist/components/Carousel/carousel.d.ts +15 -9
  114. package/dist/components/Carousel/carousel.d.ts.map +1 -1
  115. package/dist/components/Carousel/carousel.js +91 -50
  116. package/dist/components/Carousel/carousel.js.map +1 -1
  117. package/dist/components/Carousel/index.d.ts +2 -0
  118. package/dist/components/Carousel/index.d.ts.map +1 -0
  119. package/dist/components/Carousel/index.js +11 -0
  120. package/dist/components/Carousel/index.js.map +1 -0
  121. package/dist/components/Chart/chart.d.ts +2 -2
  122. package/dist/components/Chart/chart.d.ts.map +1 -1
  123. package/dist/components/Chart/chart.js +5 -2
  124. package/dist/components/Chart/chart.js.map +1 -1
  125. package/dist/components/Chart/index.d.ts +2 -0
  126. package/dist/components/Chart/index.d.ts.map +1 -0
  127. package/dist/components/Chart/index.js +11 -0
  128. package/dist/components/Chart/index.js.map +1 -0
  129. package/dist/components/Checkbox/checkbox-group.d.ts +2 -1
  130. package/dist/components/Checkbox/checkbox-group.d.ts.map +1 -1
  131. package/dist/components/Checkbox/checkbox-group.js +21 -14
  132. package/dist/components/Checkbox/checkbox-group.js.map +1 -1
  133. package/dist/components/Checkbox/checkbox.d.ts +20 -11
  134. package/dist/components/Checkbox/checkbox.d.ts.map +1 -1
  135. package/dist/components/Checkbox/checkbox.js +117 -18
  136. package/dist/components/Checkbox/checkbox.js.map +1 -1
  137. package/dist/components/Checkbox/index.d.ts +3 -0
  138. package/dist/components/Checkbox/index.d.ts.map +1 -0
  139. package/dist/components/Checkbox/index.js +10 -0
  140. package/dist/components/Checkbox/index.js.map +1 -0
  141. package/dist/components/Chip/chip.d.ts +30 -11
  142. package/dist/components/Chip/chip.d.ts.map +1 -1
  143. package/dist/components/Chip/chip.js +83 -33
  144. package/dist/components/Chip/chip.js.map +1 -1
  145. package/dist/components/Chip/index.d.ts +2 -0
  146. package/dist/components/Chip/index.d.ts.map +1 -0
  147. package/dist/components/Chip/index.js +8 -0
  148. package/dist/components/Chip/index.js.map +1 -0
  149. package/dist/components/CircularProgress/circular-progress.d.ts +2 -3
  150. package/dist/components/CircularProgress/circular-progress.d.ts.map +1 -1
  151. package/dist/components/CircularProgress/circular-progress.js +5 -4
  152. package/dist/components/CircularProgress/circular-progress.js.map +1 -1
  153. package/dist/components/CircularProgress/index.d.ts +2 -0
  154. package/dist/components/CircularProgress/index.d.ts.map +1 -0
  155. package/dist/components/CircularProgress/index.js +6 -0
  156. package/dist/components/CircularProgress/index.js.map +1 -0
  157. package/dist/components/Coachmark/coachmark-story-helpers.d.ts +22 -0
  158. package/dist/components/Coachmark/coachmark-story-helpers.d.ts.map +1 -0
  159. package/dist/components/Coachmark/coachmark.d.ts +15 -6
  160. package/dist/components/Coachmark/coachmark.d.ts.map +1 -1
  161. package/dist/components/Coachmark/coachmark.js +12 -4
  162. package/dist/components/Coachmark/coachmark.js.map +1 -1
  163. package/dist/components/Coachmark/index.d.ts +2 -0
  164. package/dist/components/Coachmark/index.d.ts.map +1 -0
  165. package/dist/components/Coachmark/index.js +6 -0
  166. package/dist/components/Coachmark/index.js.map +1 -0
  167. package/dist/components/Combobox/combobox.d.ts +39 -15
  168. package/dist/components/Combobox/combobox.d.ts.map +1 -1
  169. package/dist/components/Combobox/combobox.js +169 -65
  170. package/dist/components/Combobox/combobox.js.map +1 -1
  171. package/dist/components/Combobox/index.d.ts +2 -0
  172. package/dist/components/Combobox/index.d.ts.map +1 -0
  173. package/dist/components/Combobox/index.js +6 -0
  174. package/dist/components/Combobox/index.js.map +1 -0
  175. package/dist/components/Command/command.d.ts +7 -3
  176. package/dist/components/Command/command.d.ts.map +1 -1
  177. package/dist/components/Command/command.js +23 -10
  178. package/dist/components/Command/command.js.map +1 -1
  179. package/dist/components/Command/index.d.ts +2 -0
  180. package/dist/components/Command/index.d.ts.map +1 -0
  181. package/dist/components/Command/index.js +14 -0
  182. package/dist/components/Command/index.js.map +1 -0
  183. package/dist/components/DataTable/active-editor-controller.d.ts +4 -3
  184. package/dist/components/DataTable/active-editor-controller.d.ts.map +1 -1
  185. package/dist/components/DataTable/cell-registry.d.ts +5 -5
  186. package/dist/components/DataTable/cell-registry.d.ts.map +1 -1
  187. package/dist/components/DataTable/cell-registry.js +89 -95
  188. package/dist/components/DataTable/cell-registry.js.map +1 -1
  189. package/dist/components/DataTable/column-types.d.ts +11 -21
  190. package/dist/components/DataTable/column-types.d.ts.map +1 -1
  191. package/dist/components/DataTable/column-types.js.map +1 -1
  192. package/dist/components/DataTable/data-table-column-visibility-panel.d.ts +4 -3
  193. package/dist/components/DataTable/data-table-column-visibility-panel.d.ts.map +1 -1
  194. package/dist/components/DataTable/data-table-column-visibility-panel.js +193 -0
  195. package/dist/components/DataTable/data-table-column-visibility-panel.js.map +1 -0
  196. package/dist/components/DataTable/data-table-filter-group.d.ts +51 -0
  197. package/dist/components/DataTable/data-table-filter-group.d.ts.map +1 -0
  198. package/dist/components/DataTable/data-table-filter-group.js +159 -0
  199. package/dist/components/DataTable/data-table-filter-group.js.map +1 -0
  200. package/dist/components/DataTable/data-table-filter-labels.d.ts +44 -0
  201. package/dist/components/DataTable/data-table-filter-labels.d.ts.map +1 -0
  202. package/dist/components/DataTable/data-table-filter-labels.js +55 -0
  203. package/dist/components/DataTable/data-table-filter-labels.js.map +1 -0
  204. package/dist/components/DataTable/data-table-filter-panel.d.ts +12 -3
  205. package/dist/components/DataTable/data-table-filter-panel.d.ts.map +1 -1
  206. package/dist/components/DataTable/data-table-filter-panel.js +316 -0
  207. package/dist/components/DataTable/data-table-filter-panel.js.map +1 -0
  208. package/dist/components/DataTable/data-table-filter-value-picker.d.ts +32 -0
  209. package/dist/components/DataTable/data-table-filter-value-picker.d.ts.map +1 -0
  210. package/dist/components/DataTable/data-table-filter-value-picker.js +205 -0
  211. package/dist/components/DataTable/data-table-filter-value-picker.js.map +1 -0
  212. package/dist/components/DataTable/data-table-interaction-layer.d.ts +17 -20
  213. package/dist/components/DataTable/data-table-interaction-layer.d.ts.map +1 -1
  214. package/dist/components/DataTable/data-table-interaction-layer.js +1 -3
  215. package/dist/components/DataTable/data-table-interaction-layer.js.map +1 -1
  216. package/dist/components/DataTable/data-table-sort-manager.d.ts +5 -5
  217. package/dist/components/DataTable/data-table-sort-manager.d.ts.map +1 -1
  218. package/dist/components/DataTable/data-table-sort-manager.js +150 -0
  219. package/dist/components/DataTable/data-table-sort-manager.js.map +1 -0
  220. package/dist/components/DataTable/data-table.d.ts +66 -15
  221. package/dist/components/DataTable/data-table.d.ts.map +1 -1
  222. package/dist/components/DataTable/data-table.js +367 -185
  223. package/dist/components/DataTable/data-table.js.map +1 -1
  224. package/dist/components/DataTable/filter-operators.d.ts +1 -1
  225. package/dist/components/DataTable/filter-operators.js +140 -0
  226. package/dist/components/DataTable/filter-operators.js.map +1 -0
  227. package/dist/components/DataTable/filter-tree.d.ts +6 -1
  228. package/dist/components/DataTable/filter-tree.d.ts.map +1 -1
  229. package/dist/components/DataTable/filter-tree.js +318 -0
  230. package/dist/components/DataTable/filter-tree.js.map +1 -0
  231. package/dist/components/DataTable/index.d.ts +5 -0
  232. package/dist/components/DataTable/index.d.ts.map +1 -0
  233. package/dist/components/DataTable/index.js +21 -0
  234. package/dist/components/DataTable/index.js.map +1 -0
  235. package/dist/components/DataTable/lib/column-meta.js +25 -0
  236. package/dist/components/DataTable/lib/column-meta.js.map +1 -0
  237. package/dist/components/DateGrid/date-grid.d.ts +23 -19
  238. package/dist/components/DateGrid/date-grid.d.ts.map +1 -1
  239. package/dist/components/DateGrid/date-grid.js +60 -12
  240. package/dist/components/DateGrid/date-grid.js.map +1 -1
  241. package/dist/components/DateGrid/index.d.ts +2 -0
  242. package/dist/components/DateGrid/index.d.ts.map +1 -0
  243. package/dist/components/DateGrid/index.js +6 -0
  244. package/dist/components/DateGrid/index.js.map +1 -0
  245. package/dist/components/DatePicker/date-picker.d.ts +12 -12
  246. package/dist/components/DatePicker/date-picker.d.ts.map +1 -1
  247. package/dist/components/DatePicker/date-picker.js +418 -309
  248. package/dist/components/DatePicker/date-picker.js.map +1 -1
  249. package/dist/components/DatePicker/index.d.ts +2 -0
  250. package/dist/components/DatePicker/index.d.ts.map +1 -0
  251. package/dist/components/DatePicker/index.js +8 -0
  252. package/dist/components/DatePicker/index.js.map +1 -0
  253. package/dist/components/DescriptionList/description-list.d.ts +6 -6
  254. package/dist/components/DescriptionList/description-list.d.ts.map +1 -1
  255. package/dist/components/DescriptionList/description-list.js +11 -10
  256. package/dist/components/DescriptionList/description-list.js.map +1 -1
  257. package/dist/components/DescriptionList/index.d.ts +2 -0
  258. package/dist/components/DescriptionList/index.d.ts.map +1 -0
  259. package/dist/components/DescriptionList/index.js +7 -0
  260. package/dist/components/DescriptionList/index.js.map +1 -0
  261. package/dist/components/Dialog/dialog.d.ts +22 -5
  262. package/dist/components/Dialog/dialog.d.ts.map +1 -1
  263. package/dist/components/Dialog/dialog.js +44 -23
  264. package/dist/components/Dialog/dialog.js.map +1 -1
  265. package/dist/components/Dialog/index.d.ts +2 -0
  266. package/dist/components/Dialog/index.d.ts.map +1 -0
  267. package/dist/components/Dialog/index.js +16 -0
  268. package/dist/components/Dialog/index.js.map +1 -0
  269. package/dist/components/DropdownMenu/dropdown-menu.d.ts +19 -10
  270. package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
  271. package/dist/components/DropdownMenu/dropdown-menu.js +79 -28
  272. package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
  273. package/dist/components/DropdownMenu/index.d.ts +2 -0
  274. package/dist/components/DropdownMenu/index.d.ts.map +1 -0
  275. package/dist/components/DropdownMenu/index.js +21 -0
  276. package/dist/components/DropdownMenu/index.js.map +1 -0
  277. package/dist/components/Empty/empty.d.ts +14 -6
  278. package/dist/components/Empty/empty.d.ts.map +1 -1
  279. package/dist/components/Empty/empty.js +7 -7
  280. package/dist/components/Empty/empty.js.map +1 -1
  281. package/dist/components/Empty/index.d.ts +2 -0
  282. package/dist/components/Empty/index.d.ts.map +1 -0
  283. package/dist/components/Empty/index.js +6 -0
  284. package/dist/components/Empty/index.js.map +1 -0
  285. package/dist/components/Field/field-context.d.ts +111 -4
  286. package/dist/components/Field/field-context.d.ts.map +1 -1
  287. package/dist/components/Field/field-context.js +70 -0
  288. package/dist/components/Field/field-context.js.map +1 -1
  289. package/dist/components/Field/field-edit-keys.d.ts +21 -0
  290. package/dist/components/Field/field-edit-keys.d.ts.map +1 -0
  291. package/dist/components/Field/field-edit-keys.js +19 -0
  292. package/dist/components/Field/field-edit-keys.js.map +1 -0
  293. package/dist/components/Field/field-types.d.ts +22 -3
  294. package/dist/components/Field/field-types.d.ts.map +1 -1
  295. package/dist/components/Field/field-types.js.map +1 -1
  296. package/dist/components/Field/field-wrapper.d.ts +24 -6
  297. package/dist/components/Field/field-wrapper.d.ts.map +1 -1
  298. package/dist/components/Field/field-wrapper.js +98 -74
  299. package/dist/components/Field/field-wrapper.js.map +1 -1
  300. package/dist/components/Field/field.d.ts +7 -4
  301. package/dist/components/Field/field.d.ts.map +1 -1
  302. package/dist/components/Field/field.js +39 -6
  303. package/dist/components/Field/field.js.map +1 -1
  304. package/dist/components/Field/index.d.ts +3 -0
  305. package/dist/components/Field/index.d.ts.map +1 -0
  306. package/dist/components/Field/index.js +14 -0
  307. package/dist/components/Field/index.js.map +1 -0
  308. package/dist/components/Field/use-form-validation.d.ts +92 -0
  309. package/dist/components/Field/use-form-validation.d.ts.map +1 -0
  310. package/dist/components/Field/use-form-validation.js +137 -0
  311. package/dist/components/Field/use-form-validation.js.map +1 -0
  312. package/dist/components/FieldControlGroup/field-control-group.d.ts +8 -11
  313. package/dist/components/FieldControlGroup/field-control-group.d.ts.map +1 -1
  314. package/dist/components/FieldControlGroup/field-control-group.js +2 -2
  315. package/dist/components/FieldControlGroup/field-control-group.js.map +1 -1
  316. package/dist/components/FieldControlGroup/index.d.ts +2 -0
  317. package/dist/components/FieldControlGroup/index.d.ts.map +1 -0
  318. package/dist/components/FieldControlGroup/index.js +6 -0
  319. package/dist/components/FieldControlGroup/index.js.map +1 -0
  320. package/dist/components/FileItem/file-item.d.ts +14 -2
  321. package/dist/components/FileItem/file-item.d.ts.map +1 -1
  322. package/dist/components/FileItem/file-item.js +45 -16
  323. package/dist/components/FileItem/file-item.js.map +1 -1
  324. package/dist/components/FileItem/index.d.ts +2 -0
  325. package/dist/components/FileItem/index.d.ts.map +1 -0
  326. package/dist/components/FileItem/index.js +6 -0
  327. package/dist/components/FileItem/index.js.map +1 -0
  328. package/dist/components/FileUpload/file-upload.d.ts +39 -19
  329. package/dist/components/FileUpload/file-upload.d.ts.map +1 -1
  330. package/dist/components/FileUpload/file-upload.js +102 -32
  331. package/dist/components/FileUpload/file-upload.js.map +1 -1
  332. package/dist/components/FileUpload/index.d.ts +2 -0
  333. package/dist/components/FileUpload/index.d.ts.map +1 -0
  334. package/dist/components/FileUpload/index.js +6 -0
  335. package/dist/components/FileUpload/index.js.map +1 -0
  336. package/dist/components/FileViewer/can-render-image.d.ts +6 -0
  337. package/dist/components/FileViewer/can-render-image.d.ts.map +1 -0
  338. package/dist/components/FileViewer/can-render-image.js +11 -0
  339. package/dist/components/FileViewer/can-render-image.js.map +1 -0
  340. package/dist/components/FileViewer/file-viewer.d.ts +4 -4
  341. package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
  342. package/dist/components/FileViewer/file-viewer.js +88 -50
  343. package/dist/components/FileViewer/file-viewer.js.map +1 -1
  344. package/dist/components/FileViewer/image-renderer.d.ts +0 -5
  345. package/dist/components/FileViewer/image-renderer.d.ts.map +1 -1
  346. package/dist/components/FileViewer/image-renderer.js +23 -10
  347. package/dist/components/FileViewer/image-renderer.js.map +1 -1
  348. package/dist/components/FileViewer/index.d.ts +2 -0
  349. package/dist/components/FileViewer/index.d.ts.map +1 -0
  350. package/dist/components/FileViewer/index.js +7 -0
  351. package/dist/components/FileViewer/index.js.map +1 -0
  352. package/dist/components/HoverCard/hover-card.d.ts +9 -5
  353. package/dist/components/HoverCard/hover-card.d.ts.map +1 -1
  354. package/dist/components/HoverCard/hover-card.js +12 -5
  355. package/dist/components/HoverCard/hover-card.js.map +1 -1
  356. package/dist/components/HoverCard/index.d.ts +2 -0
  357. package/dist/components/HoverCard/index.d.ts.map +1 -0
  358. package/dist/components/HoverCard/index.js +8 -0
  359. package/dist/components/HoverCard/index.js.map +1 -0
  360. package/dist/components/InlineEdit/index.d.ts +2 -0
  361. package/dist/components/InlineEdit/index.d.ts.map +1 -0
  362. package/dist/components/InlineEdit/index.js +6 -0
  363. package/dist/components/InlineEdit/index.js.map +1 -0
  364. package/dist/components/InlineEdit/inline-edit.d.ts +86 -0
  365. package/dist/components/InlineEdit/inline-edit.d.ts.map +1 -0
  366. package/dist/components/InlineEdit/inline-edit.js +254 -0
  367. package/dist/components/InlineEdit/inline-edit.js.map +1 -0
  368. package/dist/components/Input/index.d.ts +2 -0
  369. package/dist/components/Input/index.d.ts.map +1 -0
  370. package/dist/components/Input/index.js +6 -0
  371. package/dist/components/Input/index.js.map +1 -0
  372. package/dist/components/Input/input.d.ts +31 -10
  373. package/dist/components/Input/input.d.ts.map +1 -1
  374. package/dist/components/Input/input.js +67 -30
  375. package/dist/components/Input/input.js.map +1 -1
  376. package/dist/components/LinkInput/index.d.ts +2 -0
  377. package/dist/components/LinkInput/index.d.ts.map +1 -0
  378. package/dist/components/LinkInput/index.js +6 -0
  379. package/dist/components/LinkInput/index.js.map +1 -0
  380. package/dist/components/LinkInput/link-input.d.ts +9 -9
  381. package/dist/components/LinkInput/link-input.d.ts.map +1 -1
  382. package/dist/components/LinkInput/link-input.js +40 -37
  383. package/dist/components/LinkInput/link-input.js.map +1 -1
  384. package/dist/components/Menu/index.d.ts +2 -0
  385. package/dist/components/Menu/index.d.ts.map +1 -0
  386. package/dist/components/Menu/index.js +9 -0
  387. package/dist/components/Menu/index.js.map +1 -0
  388. package/dist/components/Menu/menu-item.d.ts +20 -4
  389. package/dist/components/Menu/menu-item.d.ts.map +1 -1
  390. package/dist/components/Menu/menu-item.js +37 -9
  391. package/dist/components/Menu/menu-item.js.map +1 -1
  392. package/dist/components/Notice/index.d.ts +2 -0
  393. package/dist/components/Notice/index.d.ts.map +1 -0
  394. package/dist/components/Notice/index.js +8 -0
  395. package/dist/components/Notice/index.js.map +1 -0
  396. package/dist/components/Notice/notice.d.ts +35 -7
  397. package/dist/components/Notice/notice.d.ts.map +1 -1
  398. package/dist/components/Notice/notice.js +34 -10
  399. package/dist/components/Notice/notice.js.map +1 -1
  400. package/dist/components/NumberInput/index.d.ts +2 -0
  401. package/dist/components/NumberInput/index.d.ts.map +1 -0
  402. package/dist/components/NumberInput/index.js +7 -0
  403. package/dist/components/NumberInput/index.js.map +1 -0
  404. package/dist/components/NumberInput/number-input.d.ts +5 -5
  405. package/dist/components/NumberInput/number-input.d.ts.map +1 -1
  406. package/dist/components/NumberInput/number-input.js +40 -18
  407. package/dist/components/NumberInput/number-input.js.map +1 -1
  408. package/dist/components/OverflowIndicator/index.d.ts +2 -0
  409. package/dist/components/OverflowIndicator/index.d.ts.map +1 -0
  410. package/dist/components/OverflowIndicator/index.js +6 -0
  411. package/dist/components/OverflowIndicator/index.js.map +1 -0
  412. package/dist/components/OverflowIndicator/overflow-indicator.d.ts +6 -2
  413. package/dist/components/OverflowIndicator/overflow-indicator.d.ts.map +1 -1
  414. package/dist/components/OverflowIndicator/overflow-indicator.js +11 -7
  415. package/dist/components/OverflowIndicator/overflow-indicator.js.map +1 -1
  416. package/dist/components/Pagination/index.d.ts +2 -0
  417. package/dist/components/Pagination/index.d.ts.map +1 -0
  418. package/dist/components/Pagination/index.js +6 -0
  419. package/dist/components/Pagination/index.js.map +1 -0
  420. package/dist/components/Pagination/pagination.d.ts +49 -0
  421. package/dist/components/Pagination/pagination.d.ts.map +1 -0
  422. package/dist/components/Pagination/pagination.js +182 -0
  423. package/dist/components/Pagination/pagination.js.map +1 -0
  424. package/dist/components/PeoplePicker/avatar-stack-overflow.d.ts +1 -1
  425. package/dist/components/PeoplePicker/avatar-stack-overflow.js.map +1 -1
  426. package/dist/components/PeoplePicker/index.d.ts +2 -0
  427. package/dist/components/PeoplePicker/index.d.ts.map +1 -0
  428. package/dist/components/PeoplePicker/index.js +15 -0
  429. package/dist/components/PeoplePicker/index.js.map +1 -0
  430. package/dist/components/PeoplePicker/people-picker-helpers.d.ts.map +1 -1
  431. package/dist/components/PeoplePicker/people-picker-helpers.js +2 -2
  432. package/dist/components/PeoplePicker/people-picker-helpers.js.map +1 -1
  433. package/dist/components/PeoplePicker/people-picker.d.ts +26 -10
  434. package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
  435. package/dist/components/PeoplePicker/people-picker.js +97 -65
  436. package/dist/components/PeoplePicker/people-picker.js.map +1 -1
  437. package/dist/components/PeoplePicker/person-display.d.ts +19 -11
  438. package/dist/components/PeoplePicker/person-display.d.ts.map +1 -1
  439. package/dist/components/PeoplePicker/person-display.js +39 -23
  440. package/dist/components/PeoplePicker/person-display.js.map +1 -1
  441. package/dist/components/Popover/index.d.ts +2 -0
  442. package/dist/components/Popover/index.d.ts.map +1 -0
  443. package/dist/components/Popover/index.js +14 -0
  444. package/dist/components/Popover/index.js.map +1 -0
  445. package/dist/components/Popover/popover.d.ts +2 -2
  446. package/dist/components/Popover/popover.d.ts.map +1 -1
  447. package/dist/components/Popover/popover.js +72 -42
  448. package/dist/components/Popover/popover.js.map +1 -1
  449. package/dist/components/ProfileCard/index.d.ts +2 -0
  450. package/dist/components/ProfileCard/index.d.ts.map +1 -0
  451. package/dist/components/ProfileCard/index.js +8 -0
  452. package/dist/components/ProfileCard/index.js.map +1 -0
  453. package/dist/components/{NameCard/name-card.d.ts → ProfileCard/profile-card.d.ts} +34 -20
  454. package/dist/components/ProfileCard/profile-card.d.ts.map +1 -0
  455. package/dist/components/{NameCard/name-card.js → ProfileCard/profile-card.js} +27 -16
  456. package/dist/components/ProfileCard/profile-card.js.map +1 -0
  457. package/dist/components/ProgressBar/index.d.ts +2 -0
  458. package/dist/components/ProgressBar/index.d.ts.map +1 -0
  459. package/dist/components/ProgressBar/index.js +6 -0
  460. package/dist/components/ProgressBar/index.js.map +1 -0
  461. package/dist/components/ProgressBar/progress-bar.d.ts +2 -14
  462. package/dist/components/ProgressBar/progress-bar.d.ts.map +1 -1
  463. package/dist/components/ProgressBar/progress-bar.js +5 -9
  464. package/dist/components/ProgressBar/progress-bar.js.map +1 -1
  465. package/dist/components/RadioGroup/index.d.ts +2 -0
  466. package/dist/components/RadioGroup/index.d.ts.map +1 -0
  467. package/dist/components/RadioGroup/index.js +8 -0
  468. package/dist/components/RadioGroup/index.js.map +1 -0
  469. package/dist/components/RadioGroup/radio-group.d.ts +18 -12
  470. package/dist/components/RadioGroup/radio-group.d.ts.map +1 -1
  471. package/dist/components/RadioGroup/radio-group.js +131 -40
  472. package/dist/components/RadioGroup/radio-group.js.map +1 -1
  473. package/dist/components/Rating/index.d.ts +2 -0
  474. package/dist/components/Rating/index.d.ts.map +1 -0
  475. package/dist/components/Rating/index.js +6 -0
  476. package/dist/components/Rating/index.js.map +1 -0
  477. package/dist/components/Rating/rating.d.ts +6 -2
  478. package/dist/components/Rating/rating.d.ts.map +1 -1
  479. package/dist/components/Rating/rating.js +17 -10
  480. package/dist/components/Rating/rating.js.map +1 -1
  481. package/dist/components/ScrollArea/index.d.ts +2 -0
  482. package/dist/components/ScrollArea/index.d.ts.map +1 -0
  483. package/dist/components/ScrollArea/index.js +7 -0
  484. package/dist/components/ScrollArea/index.js.map +1 -0
  485. package/dist/components/ScrollArea/scroll-area.d.ts +16 -4
  486. package/dist/components/ScrollArea/scroll-area.d.ts.map +1 -1
  487. package/dist/components/ScrollArea/scroll-area.js +9 -6
  488. package/dist/components/ScrollArea/scroll-area.js.map +1 -1
  489. package/dist/components/SegmentedControl/index.d.ts +2 -0
  490. package/dist/components/SegmentedControl/index.d.ts.map +1 -0
  491. package/dist/components/SegmentedControl/index.js +9 -0
  492. package/dist/components/SegmentedControl/index.js.map +1 -0
  493. package/dist/components/SegmentedControl/segmented-control.d.ts +8 -7
  494. package/dist/components/SegmentedControl/segmented-control.d.ts.map +1 -1
  495. package/dist/components/SegmentedControl/segmented-control.js +31 -20
  496. package/dist/components/SegmentedControl/segmented-control.js.map +1 -1
  497. package/dist/components/Select/index.d.ts +2 -0
  498. package/dist/components/Select/index.d.ts.map +1 -0
  499. package/dist/components/Select/index.js +6 -0
  500. package/dist/components/Select/index.js.map +1 -0
  501. package/dist/components/Select/select.d.ts +70 -13
  502. package/dist/components/Select/select.d.ts.map +1 -1
  503. package/dist/components/Select/select.js +271 -83
  504. package/dist/components/Select/select.js.map +1 -1
  505. package/dist/components/SelectMenu/index.d.ts +2 -0
  506. package/dist/components/SelectMenu/index.d.ts.map +1 -0
  507. package/dist/components/SelectMenu/index.js +8 -0
  508. package/dist/components/SelectMenu/index.js.map +1 -0
  509. package/dist/components/SelectMenu/select-menu.d.ts +61 -10
  510. package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
  511. package/dist/components/SelectMenu/select-menu.js +216 -93
  512. package/dist/components/SelectMenu/select-menu.js.map +1 -1
  513. package/dist/components/SelectionControl/boolean-value.d.ts +11 -0
  514. package/dist/components/SelectionControl/boolean-value.d.ts.map +1 -0
  515. package/dist/components/SelectionControl/boolean-value.js +23 -0
  516. package/dist/components/SelectionControl/boolean-value.js.map +1 -0
  517. package/dist/components/SelectionControl/index.d.ts +3 -0
  518. package/dist/components/SelectionControl/index.d.ts.map +1 -0
  519. package/dist/components/SelectionControl/index.js +9 -0
  520. package/dist/components/SelectionControl/index.js.map +1 -0
  521. package/dist/components/SelectionControl/selection-item.d.ts +5 -1
  522. package/dist/components/SelectionControl/selection-item.d.ts.map +1 -1
  523. package/dist/components/SelectionControl/selection-item.js +3 -1
  524. package/dist/components/SelectionControl/selection-item.js.map +1 -1
  525. package/dist/components/Separator/index.d.ts +2 -0
  526. package/dist/components/Separator/index.d.ts.map +1 -0
  527. package/dist/components/Separator/index.js +6 -0
  528. package/dist/components/Separator/index.js.map +1 -0
  529. package/dist/components/Separator/separator.d.ts +2 -2
  530. package/dist/components/Separator/separator.js +4 -3
  531. package/dist/components/Separator/separator.js.map +1 -1
  532. package/dist/components/Sheet/index.d.ts +2 -0
  533. package/dist/components/Sheet/index.d.ts.map +1 -0
  534. package/dist/components/Sheet/index.js +18 -0
  535. package/dist/components/Sheet/index.js.map +1 -0
  536. package/dist/components/Sheet/sheet.d.ts +15 -8
  537. package/dist/components/Sheet/sheet.d.ts.map +1 -1
  538. package/dist/components/Sheet/sheet.js +9 -5
  539. package/dist/components/Sheet/sheet.js.map +1 -1
  540. package/dist/components/Sidebar/index.d.ts +2 -0
  541. package/dist/components/Sidebar/index.d.ts.map +1 -0
  542. package/dist/components/Sidebar/index.js +24 -0
  543. package/dist/components/Sidebar/index.js.map +1 -0
  544. package/dist/components/Sidebar/sidebar.d.ts +17 -11
  545. package/dist/components/Sidebar/sidebar.d.ts.map +1 -1
  546. package/dist/components/Sidebar/sidebar.js +78 -35
  547. package/dist/components/Sidebar/sidebar.js.map +1 -1
  548. package/dist/components/Skeleton/index.d.ts +2 -0
  549. package/dist/components/Skeleton/index.d.ts.map +1 -0
  550. package/dist/components/Skeleton/index.js +6 -0
  551. package/dist/components/Skeleton/index.js.map +1 -0
  552. package/dist/components/Skeleton/skeleton.d.ts +2 -2
  553. package/dist/components/Skeleton/skeleton.js +4 -3
  554. package/dist/components/Skeleton/skeleton.js.map +1 -1
  555. package/dist/components/Slider/index.d.ts +2 -0
  556. package/dist/components/Slider/index.d.ts.map +1 -0
  557. package/dist/components/Slider/index.js +7 -0
  558. package/dist/components/Slider/index.js.map +1 -0
  559. package/dist/components/Slider/slider.d.ts +7 -7
  560. package/dist/components/Slider/slider.d.ts.map +1 -1
  561. package/dist/components/Slider/slider.js +39 -17
  562. package/dist/components/Slider/slider.js.map +1 -1
  563. package/dist/components/Steps/index.d.ts +2 -0
  564. package/dist/components/Steps/index.d.ts.map +1 -0
  565. package/dist/components/Steps/index.js +12 -0
  566. package/dist/components/Steps/index.js.map +1 -0
  567. package/dist/components/Steps/steps.d.ts +8 -6
  568. package/dist/components/Steps/steps.d.ts.map +1 -1
  569. package/dist/components/Steps/steps.js +74 -32
  570. package/dist/components/Steps/steps.js.map +1 -1
  571. package/dist/components/Switch/index.d.ts +2 -0
  572. package/dist/components/Switch/index.d.ts.map +1 -0
  573. package/dist/components/Switch/index.js +7 -0
  574. package/dist/components/Switch/index.js.map +1 -0
  575. package/dist/components/Switch/switch.d.ts +20 -18
  576. package/dist/components/Switch/switch.d.ts.map +1 -1
  577. package/dist/components/Switch/switch.js +133 -26
  578. package/dist/components/Switch/switch.js.map +1 -1
  579. package/dist/components/Tabs/index.d.ts +2 -0
  580. package/dist/components/Tabs/index.d.ts.map +1 -0
  581. package/dist/components/Tabs/index.js +10 -0
  582. package/dist/components/Tabs/index.js.map +1 -0
  583. package/dist/components/Tabs/tabs-inline-action-geometry.d.ts +31 -0
  584. package/dist/components/Tabs/tabs-inline-action-geometry.d.ts.map +1 -0
  585. package/dist/components/Tabs/tabs-inline-action-geometry.js +43 -0
  586. package/dist/components/Tabs/tabs-inline-action-geometry.js.map +1 -0
  587. package/dist/components/Tabs/tabs.d.ts +14 -11
  588. package/dist/components/Tabs/tabs.d.ts.map +1 -1
  589. package/dist/components/Tabs/tabs.js +184 -69
  590. package/dist/components/Tabs/tabs.js.map +1 -1
  591. package/dist/components/Tag/index.d.ts +2 -0
  592. package/dist/components/Tag/index.d.ts.map +1 -0
  593. package/dist/components/Tag/index.js +7 -0
  594. package/dist/components/Tag/index.js.map +1 -0
  595. package/dist/components/Tag/tag.d.ts +39 -23
  596. package/dist/components/Tag/tag.d.ts.map +1 -1
  597. package/dist/components/Tag/tag.js +71 -63
  598. package/dist/components/Tag/tag.js.map +1 -1
  599. package/dist/components/Textarea/index.d.ts +2 -0
  600. package/dist/components/Textarea/index.d.ts.map +1 -0
  601. package/dist/components/Textarea/index.js +7 -0
  602. package/dist/components/Textarea/index.js.map +1 -0
  603. package/dist/components/Textarea/textarea.d.ts +24 -17
  604. package/dist/components/Textarea/textarea.d.ts.map +1 -1
  605. package/dist/components/Textarea/textarea.js +110 -72
  606. package/dist/components/Textarea/textarea.js.map +1 -1
  607. package/dist/components/TimePicker/index.d.ts +2 -0
  608. package/dist/components/TimePicker/index.d.ts.map +1 -0
  609. package/dist/components/TimePicker/index.js +6 -0
  610. package/dist/components/TimePicker/index.js.map +1 -0
  611. package/dist/components/TimePicker/time-columns.d.ts.map +1 -1
  612. package/dist/components/TimePicker/time-columns.js +44 -36
  613. package/dist/components/TimePicker/time-columns.js.map +1 -1
  614. package/dist/components/TimePicker/time-picker.d.ts +8 -7
  615. package/dist/components/TimePicker/time-picker.d.ts.map +1 -1
  616. package/dist/components/TimePicker/time-picker.js +71 -53
  617. package/dist/components/TimePicker/time-picker.js.map +1 -1
  618. package/dist/components/Toast/index.d.ts +2 -0
  619. package/dist/components/Toast/index.d.ts.map +1 -0
  620. package/dist/components/Toast/index.js +7 -0
  621. package/dist/components/Toast/index.js.map +1 -0
  622. package/dist/components/Toast/toast.d.ts +2 -2
  623. package/dist/components/Toast/toast.d.ts.map +1 -1
  624. package/dist/components/Toast/toast.js +82 -20
  625. package/dist/components/Toast/toast.js.map +1 -1
  626. package/dist/components/Tooltip/index.d.ts +2 -0
  627. package/dist/components/Tooltip/index.d.ts.map +1 -0
  628. package/dist/components/Tooltip/index.js +9 -0
  629. package/dist/components/Tooltip/index.js.map +1 -0
  630. package/dist/components/Tooltip/tooltip.d.ts +2 -2
  631. package/dist/components/Tooltip/tooltip.d.ts.map +1 -1
  632. package/dist/components/Tooltip/tooltip.js +57 -25
  633. package/dist/components/Tooltip/tooltip.js.map +1 -1
  634. package/dist/components/TreeView/index.d.ts +2 -0
  635. package/dist/components/TreeView/index.d.ts.map +1 -0
  636. package/dist/components/TreeView/index.js +8 -0
  637. package/dist/components/TreeView/index.js.map +1 -0
  638. package/dist/components/TreeView/tree-view.d.ts +71 -14
  639. package/dist/components/TreeView/tree-view.d.ts.map +1 -1
  640. package/dist/components/TreeView/tree-view.js +313 -34
  641. package/dist/components/TreeView/tree-view.js.map +1 -1
  642. package/dist/hooks/use-controllable.d.ts.map +1 -1
  643. package/dist/hooks/use-controllable.js +4 -2
  644. package/dist/hooks/use-controllable.js.map +1 -1
  645. package/dist/hooks/use-is-touch-device.d.ts.map +1 -1
  646. package/dist/hooks/use-is-touch-device.js +3 -1
  647. package/dist/hooks/use-is-touch-device.js.map +1 -1
  648. package/dist/hooks/use-overflow-items.d.ts +10 -6
  649. package/dist/hooks/use-overflow-items.d.ts.map +1 -1
  650. package/dist/hooks/use-overflow-items.js.map +1 -1
  651. package/dist/hooks/use-truncated.d.ts +27 -0
  652. package/dist/hooks/use-truncated.d.ts.map +1 -0
  653. package/dist/hooks/use-truncated.js +57 -0
  654. package/dist/hooks/use-truncated.js.map +1 -0
  655. package/dist/{react-day-picker.css → index.css} +59 -0
  656. package/dist/index.d.ts +121 -67
  657. package/dist/index.d.ts.map +1 -1
  658. package/dist/index.js +143 -160
  659. package/dist/index.js.map +1 -1
  660. package/dist/lib/drag-visual.d.ts +2 -2
  661. package/dist/lib/drag-visual.js.map +1 -1
  662. package/dist/lib/utils.d.ts +1 -0
  663. package/dist/lib/utils.d.ts.map +1 -1
  664. package/dist/lib/utils.js +24 -2
  665. package/dist/lib/utils.js.map +1 -1
  666. package/dist/patterns/element-anatomy/index.d.ts +3 -0
  667. package/dist/patterns/element-anatomy/index.d.ts.map +1 -0
  668. package/dist/patterns/element-anatomy/index.js +22 -0
  669. package/dist/patterns/element-anatomy/index.js.map +1 -0
  670. package/dist/patterns/element-anatomy/item-anatomy.d.ts +32 -16
  671. package/dist/patterns/element-anatomy/item-anatomy.d.ts.map +1 -1
  672. package/dist/patterns/element-anatomy/item-anatomy.js +83 -35
  673. package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
  674. package/dist/patterns/element-anatomy/truncated-text.d.ts +27 -0
  675. package/dist/patterns/element-anatomy/truncated-text.d.ts.map +1 -0
  676. package/dist/patterns/element-anatomy/truncated-text.js +16 -0
  677. package/dist/patterns/element-anatomy/truncated-text.js.map +1 -0
  678. package/dist/patterns/header-canonical/chrome-header.d.ts +20 -4
  679. package/dist/patterns/header-canonical/chrome-header.d.ts.map +1 -1
  680. package/dist/patterns/header-canonical/chrome-header.js +4 -2
  681. package/dist/patterns/header-canonical/chrome-header.js.map +1 -1
  682. package/dist/patterns/header-canonical/index.d.ts +2 -0
  683. package/dist/patterns/header-canonical/index.d.ts.map +1 -0
  684. package/dist/patterns/header-canonical/index.js +6 -0
  685. package/dist/patterns/header-canonical/index.js.map +1 -0
  686. package/dist/patterns/horizontal-overflow/horizontal-overflow.d.ts +1 -1
  687. package/dist/patterns/horizontal-overflow/horizontal-overflow.js.map +1 -1
  688. package/dist/patterns/horizontal-overflow/index.d.ts +2 -0
  689. package/dist/patterns/horizontal-overflow/index.d.ts.map +1 -0
  690. package/dist/patterns/horizontal-overflow/index.js +14 -0
  691. package/dist/patterns/horizontal-overflow/index.js.map +1 -0
  692. package/dist/patterns/overlay-surface/index.d.ts +2 -0
  693. package/dist/patterns/overlay-surface/index.d.ts.map +1 -0
  694. package/dist/patterns/overlay-surface/index.js +8 -0
  695. package/dist/patterns/overlay-surface/index.js.map +1 -0
  696. package/dist/patterns/overlay-surface/overlay-surface.d.ts +12 -2
  697. package/dist/patterns/overlay-surface/overlay-surface.d.ts.map +1 -1
  698. package/dist/patterns/overlay-surface/overlay-surface.js +6 -2
  699. package/dist/patterns/overlay-surface/overlay-surface.js.map +1 -1
  700. package/dist/patterns/resize-handle/index.d.ts +2 -0
  701. package/dist/patterns/resize-handle/index.d.ts.map +1 -0
  702. package/dist/patterns/resize-handle/index.js +5 -0
  703. package/dist/patterns/resize-handle/index.js.map +1 -0
  704. package/dist/patterns/resize-handle/resize-handle.d.ts +9 -11
  705. package/dist/patterns/resize-handle/resize-handle.d.ts.map +1 -1
  706. package/dist/patterns/resize-handle/resize-handle.js +3 -6
  707. package/dist/patterns/resize-handle/resize-handle.js.map +1 -1
  708. package/dist/story-governance/category-matrix.d.ts +35 -0
  709. package/dist/story-governance/category-matrix.d.ts.map +1 -0
  710. package/dist/tokens/categorical-color.d.ts +60 -0
  711. package/dist/tokens/categorical-color.d.ts.map +1 -0
  712. package/dist/tokens/categorical-color.js +125 -0
  713. package/dist/tokens/categorical-color.js.map +1 -0
  714. package/dist/tokens/motion/motion.d.ts +8 -7
  715. package/dist/tokens/motion/motion.d.ts.map +1 -1
  716. package/dist/tokens/motion/motion.js +6 -6
  717. package/dist/tokens/motion/motion.js.map +1 -1
  718. package/dist/tokens/motion/overlay-motion.d.ts +19 -0
  719. package/dist/tokens/motion/overlay-motion.d.ts.map +1 -0
  720. package/dist/tokens/motion/overlay-motion.js +9 -0
  721. package/dist/tokens/motion/overlay-motion.js.map +1 -0
  722. package/dist/tokens/uiSize/icon-size.d.ts +3 -4
  723. package/dist/tokens/uiSize/icon-size.d.ts.map +1 -1
  724. package/dist/tokens/uiSize/icon-size.js +5 -0
  725. package/dist/tokens/uiSize/icon-size.js.map +1 -0
  726. package/ds-canonical/adapters/claude/agents/README.md +58 -0
  727. package/ds-canonical/adapters/claude/agents/canonical-reviewer.md +14 -0
  728. package/ds-canonical/adapters/claude-root-instructions.md +24 -0
  729. package/ds-canonical/adapters/claude-settings-base.json +169 -0
  730. package/ds-canonical/commands/README.md +9 -14
  731. package/ds-canonical/fork/AGENTS.md +110 -0
  732. package/ds-canonical/fork/CLAUDE.md +54 -0
  733. package/ds-canonical/fork/codex/agents/skills/bug-fix-rhythm/SKILL.md +207 -0
  734. package/ds-canonical/fork/codex/agents/skills/code-quality-audit/SKILL.md +71 -0
  735. package/ds-canonical/fork/codex/agents/skills/delivery-handoff/SKILL.md +244 -0
  736. package/ds-canonical/fork/codex/agents/skills/delivery-handoff/references/flow-diagram.md +180 -0
  737. package/ds-canonical/fork/codex/agents/skills/delivery-handoff/references/handoff-template.md +177 -0
  738. package/ds-canonical/fork/codex/agents/skills/delivery-handoff/references/inventory-checklist.md +196 -0
  739. package/ds-canonical/fork/codex/agents/skills/independent-review/SKILL.md +43 -0
  740. package/ds-canonical/fork/codex/agents/skills/independent-review/agents/openai.yaml +4 -0
  741. package/ds-canonical/fork/codex/agents/skills/independent-review/references/evidence-contract.md +23 -0
  742. package/ds-canonical/fork/codex/agents/skills/performance-audit/SKILL.md +114 -0
  743. package/ds-canonical/fork/codex/agents/skills/product-ui-audit/SKILL.md +252 -0
  744. package/ds-canonical/fork/codex/agents/skills/product-ui-audit/references/audit-checks.md +250 -0
  745. package/ds-canonical/fork/codex/agents/skills/product-ui-audit/references/common-misuses.md +329 -0
  746. package/ds-canonical/fork/codex/agents/skills/product-ui-audit/references/report-template.md +163 -0
  747. package/ds-canonical/fork/codex/agents/skills/propose-options/SKILL.md +190 -0
  748. package/ds-canonical/fork/codex/agents/skills/prototype/SKILL.md +246 -0
  749. package/ds-canonical/fork/codex/agents/skills/prototype/references/audit-checks.md +39 -0
  750. package/ds-canonical/fork/codex/agents/skills/prototype/references/benchmark-sources.md +94 -0
  751. package/ds-canonical/fork/codex/agents/skills/prototype/references/checkpoints.md +200 -0
  752. package/ds-canonical/fork/codex/agents/skills/prototype/references/evaluation-matrix.md +142 -0
  753. package/ds-canonical/fork/codex/agents/skills/prototype/references/ooux-template.md +198 -0
  754. package/ds-canonical/fork/codex/agents/skills/prototype/references/proposal-template.md +230 -0
  755. package/ds-canonical/fork/codex/agents/skills/scan-similar-bugs/SKILL.md +203 -0
  756. package/ds-canonical/fork/codex/agents/skills/ux-audit/SKILL.md +135 -0
  757. package/ds-canonical/fork/codex/agents/skills/visual-audit/SKILL.md +258 -0
  758. package/ds-canonical/fork/codex/agents/skills/visual-audit/output/.gitkeep +0 -0
  759. package/ds-canonical/fork/codex/agents/skills/visual-audit/references/audit-architecture.md +101 -0
  760. package/ds-canonical/fork/codex/agents/skills/visual-audit/references/visual-checklist.md +297 -0
  761. package/ds-canonical/fork/codex/agents/skills/visual-audit/references/world-class-benchmarks.md +198 -0
  762. package/ds-canonical/fork/codex/hooks.json +57 -0
  763. package/ds-canonical/fork/common/closed-tool-execution.mjs +1005 -0
  764. package/ds-canonical/fork/common/instruction.md +110 -0
  765. package/ds-canonical/fork/consumer/governance-check.mjs +3163 -0
  766. package/ds-canonical/fork/consumer/lib/closed-tool-execution.mjs +1005 -0
  767. package/ds-canonical/fork/consumer/lock.json +189 -0
  768. package/ds-canonical/fork/consumer/lock.schema.json +338 -0
  769. package/ds-canonical/fork/consumer/managed-files/0235cf6b3fd402a3717881dc205b5bf77aed7632e7d7d6f1811a40e3033bd816.blob +145 -0
  770. package/ds-canonical/fork/consumer/managed-files/145aab4b4fc470fc9ba7679b4a1d87d4f6a6d604940bd40f8d675c8af696498f.blob +53 -0
  771. package/ds-canonical/fork/consumer/managed-files/189e551431181e4a88783c02130d2787b801ef731c86e35bcde89a7313aa020e.blob +149 -0
  772. package/ds-canonical/fork/consumer/managed-files/24ad71c8613ddcf6fd23818cb3bb477a1fb6d83af4550b0bad43099813088686.blob +28 -0
  773. package/ds-canonical/fork/consumer/managed-files/2d1ef5617a5ce3c39ad9aae1c42af7d199f9270c0a0ccb490b63d62dbc6913db.blob +1005 -0
  774. package/ds-canonical/fork/consumer/managed-files/3625e02235cf166a50649a4d704cfb68fead69d0e168c481df013ebae9f6be25.blob +1412 -0
  775. package/ds-canonical/fork/consumer/managed-files/3ddedc67b464bdfe3dde9803a2d917920c96d633678b802eee638a44cd47366a.blob +8 -0
  776. package/ds-canonical/fork/consumer/managed-files/423729840acdd43c812f17c245b871716c6b60f76e4d3e94864d0e34a815bf8d.blob +348 -0
  777. package/ds-canonical/fork/consumer/managed-files/46f10bc55cdfdf834d0749eebff42ea0bb8576f361b36d2b6ba1851354c50150.blob +549 -0
  778. package/ds-canonical/fork/consumer/managed-files/475fba657eaeb8abd88f60635f3eadab27a9d0d3114ba4fffc65f43a2a0fe6e2.blob +124 -0
  779. package/ds-canonical/fork/consumer/managed-files/5aeda1df235815f40dd755951f2b868d4e02035653d4536a8dde209cdc75b493.blob +268 -0
  780. package/ds-canonical/fork/consumer/managed-files/61c6aca6d7f04bedbf556eaa9d3043681fe48cbe0e93a8ccb1d69a7c5e23fdcf.blob +113 -0
  781. package/ds-canonical/fork/consumer/managed-files/63061e05969667005020166ea2ae7df3c96663f7a928e7dd3eebc1471022df56.blob +2454 -0
  782. package/ds-canonical/fork/consumer/managed-files/65410b66e8799a560e07b71779c186fed2a0ef50575ac44745df7a0938a9c2b4.blob +321 -0
  783. package/ds-canonical/fork/consumer/managed-files/6b746628ac11fcccbe277ef6fea94cc2a7b90ea2822e83536eae80d5ee4b3fa5.blob +167 -0
  784. package/ds-canonical/fork/consumer/managed-files/76d485470d30ff014afacf7ce643763de9f4f956d9fc8128dbc1814774cf2e63.blob +671 -0
  785. package/ds-canonical/fork/consumer/managed-files/7937321877e5c34c27dae36c0d507d21cd42fa3f46a8f11a284b8b4a52b6faa4.blob +84 -0
  786. package/ds-canonical/fork/consumer/managed-files/794bb911ba8ebef58ab0d44a55b9968a19d2c62bcfb310b1ee3a84166f087f21.blob +924 -0
  787. package/ds-canonical/fork/consumer/managed-files/7ab15a8aeca39485d7b9b57234bbc4a69e0b9e70b4a851dfe10f6447127cee78.blob +51 -0
  788. package/ds-canonical/fork/consumer/managed-files/7b17835d0ceb17cdcdcaf930540a3e5d0943a34a747ac43c12ca8cd485c11581.blob +44 -0
  789. package/ds-canonical/fork/consumer/managed-files/7eb6cc60abfdabaf6617c336058fad1454ccde08889295767bfd9b7fd153cc6e.blob +900 -0
  790. package/ds-canonical/fork/consumer/managed-files/81037efa5b3b9f4a875704005fb268acdc9233bb901f44902e4e37e1da92b8e3.blob +46 -0
  791. package/ds-canonical/fork/consumer/managed-files/884721550b56308812b20917adac7a12f36fdb4ecda6b02460a7f5a219c0c966.blob +82 -0
  792. package/ds-canonical/fork/consumer/managed-files/91f414b9462ddc1e4be1d4c6ca4ac7a7340a6eec9e98ee109cbc049d3b354b6a.blob +36 -0
  793. package/ds-canonical/fork/consumer/managed-files/92e4828cdcb96ff0d01daa16f6885f24045a8e72820665650f2a1890dc3a446c.blob +121 -0
  794. package/ds-canonical/fork/consumer/managed-files/93a89f16f6717d1e4e4e27ec20ef916e3355c7aa890ca74cd9b12db30d8a899f.blob +36 -0
  795. package/ds-canonical/fork/consumer/managed-files/974521d6b8fe796ea4c58b3c78b0c2b8a1be262b97100d77aaf4cc9c7724bcab.blob +45 -0
  796. package/ds-canonical/fork/consumer/managed-files/9791c85388d2e2e2bd564e2550fadc3351c4c960a47ce838098afcba7544eb90.blob +973 -0
  797. package/ds-canonical/fork/consumer/managed-files/a448d2222133335c30e6fda5116e26a40db3283a19d62ccc0663c30d272262a7.blob +174 -0
  798. package/ds-canonical/fork/consumer/managed-files/ab072e7f2eca51f0e18a5e18c4b333260ee5d219a12cc6df5bb2d683f0b2734d.blob +2639 -0
  799. package/ds-canonical/fork/consumer/managed-files/bc44cabad181f766611dab6a8f52d81100afadd50ac59799015e8ba7bf46d9eb.blob +620 -0
  800. package/ds-canonical/fork/consumer/managed-files/c4aa767f11714467de0038a2149cf78074baddc17fc6a9e074b6aed7544c6f3c.blob +80 -0
  801. package/ds-canonical/fork/consumer/managed-files/c893e9b734b162f4c631fef871128c16a9ab565f796a0e76d7f320f821f220f5.blob +41 -0
  802. package/ds-canonical/fork/consumer/managed-files/cd9c61f8ff24fb9bcc3663e346e2cfa419cfb84e02aae18d3dc66dd192681696.blob +9 -0
  803. package/ds-canonical/fork/consumer/managed-files/da2ad2737315d25a5300905ddefd62f1695056a8a1af155c5b34170ad9923bce.blob +38 -0
  804. package/ds-canonical/fork/consumer/managed-files/e26057408661597aac4a57cdf7e30e2f9d0afb1b9a2dbbb692c3805cd9028ca5.blob +250 -0
  805. package/ds-canonical/fork/consumer/managed-files/e68ae1915965d8e17d4b2441c00c2b97fe062ea308ae8535c9eebb2fc16fa245.blob +20 -0
  806. package/ds-canonical/fork/consumer/managed-files/e71c65f45c6af7ce12b6b3c0d8028f185da5b719e2050c9b2a6b243d41ae4878.blob +182 -0
  807. package/ds-canonical/fork/consumer/managed-files/e813096d69c49812e40e00be9ac8fa14d77f0ec1f97ab4c45cb096b3bedd35de.blob +3 -0
  808. package/ds-canonical/fork/consumer/managed-files/e930ebca6fd8eeb30802aac0930efae7bcc44cc0996f24de07c11ca6a081fedf.blob +277 -0
  809. package/ds-canonical/fork/consumer/managed-files/ea94e914d5c8acb84ad7660e5bda0f01b6ccc8cf96b0f6b1ce048f8938d0f5b5.blob +47 -0
  810. package/ds-canonical/fork/consumer/managed-files/f4db6e616d5229aa7e700c6a3ace022cb9d90baae74e38ca65fce617f18e0d9b.blob +110 -0
  811. package/ds-canonical/fork/consumer/managed-files/faa128c26102415dc5345e68698efbbd28a395c35ee278dceae044cf430692b2.blob +11 -0
  812. package/ds-canonical/fork/governance.lock +1696 -0
  813. package/ds-canonical/fork/hooks/_log-fire.sh +143 -0
  814. package/ds-canonical/fork/hooks/block_prototype_imports.py +241 -0
  815. package/ds-canonical/fork/hooks/check_chrome_header_avatar_canonical.sh +106 -0
  816. package/ds-canonical/fork/hooks/check_consumer_app_invariants.sh +731 -0
  817. package/ds-canonical/fork/hooks/check_consumer_code_quality.sh +173 -0
  818. package/ds-canonical/fork/hooks/check_ds_anchor_preflight.sh +219 -0
  819. package/ds-canonical/fork/hooks/check_dynamic_tailwind_class.sh +57 -0
  820. package/ds-canonical/fork/hooks/check_escape_marker_abuse.sh +213 -0
  821. package/ds-canonical/fork/hooks/check_field_family_invariants.sh +376 -0
  822. package/ds-canonical/fork/hooks/check_fork_product_quality.sh +38 -0
  823. package/ds-canonical/fork/hooks/check_item_list_gap.sh +56 -0
  824. package/ds-canonical/fork/hooks/check_layout_space_magic_numbers.sh +160 -0
  825. package/ds-canonical/fork/hooks/check_opacity_token_usage.sh +319 -0
  826. package/ds-canonical/fork/hooks/check_pattern_invariants.sh +339 -0
  827. package/ds-canonical/fork/hooks/check_sidebar_menu_button_implicit_wrap.sh +99 -0
  828. package/ds-canonical/fork/hooks/check_story_determinism.sh +39 -0
  829. package/ds-canonical/fork/hooks/check_story_invariants.sh +400 -0
  830. package/ds-canonical/fork/hooks/check_tabs_content_chrome_body_double_gap.sh +241 -0
  831. package/ds-canonical/fork/hooks/check_tailwind_wildcard_in_docs.sh +86 -0
  832. package/ds-canonical/fork/hooks/inject_deploy_url_after_push.sh +333 -0
  833. package/ds-canonical/fork/hooks/lib/_hook_integrity.sh +158 -0
  834. package/ds-canonical/fork/hooks/lib/_micro_geometry.sh +291 -0
  835. package/ds-canonical/fork/hooks/lib/_provider_paths.sh +162 -0
  836. package/ds-canonical/fork/launchers/approval-evidence.mjs +1321 -0
  837. package/ds-canonical/fork/launchers/authority-decision-evidence.mjs +413 -0
  838. package/ds-canonical/fork/launchers/canonical-order.mjs +8 -0
  839. package/ds-canonical/fork/launchers/fork-governance-dispatcher.sh +154 -0
  840. package/ds-canonical/fork/launchers/format-provider-hook-output.mjs +25 -0
  841. package/ds-canonical/fork/launchers/independent-review.mjs +490 -0
  842. package/ds-canonical/fork/launchers/inject_fork_governance_preamble.sh +99 -0
  843. package/ds-canonical/fork/launchers/normalize-provider-event.mjs +355 -0
  844. package/ds-canonical/fork/launchers/product-hook-dispatch-runtime.mjs +1067 -0
  845. package/ds-canonical/fork/launchers/provider-hook-normalization.mjs +1640 -0
  846. package/ds-canonical/fork/launchers/provider-hook-output-transport.mjs +540 -0
  847. package/ds-canonical/fork/launchers/settings-hooks.json +35 -0
  848. package/ds-canonical/fork/manifest.json +1984 -0
  849. package/ds-canonical/fork/providers/claude/hook-config.json +327 -0
  850. package/ds-canonical/fork/providers/claude/instruction.md +54 -0
  851. package/ds-canonical/fork/providers/claude/skills/bug-fix-rhythm/SKILL.md +207 -0
  852. package/ds-canonical/fork/providers/claude/skills/code-quality-audit/SKILL.md +71 -0
  853. package/ds-canonical/fork/providers/claude/skills/delivery-handoff/SKILL.md +244 -0
  854. package/ds-canonical/fork/providers/claude/skills/delivery-handoff/references/flow-diagram.md +180 -0
  855. package/ds-canonical/fork/providers/claude/skills/delivery-handoff/references/handoff-template.md +177 -0
  856. package/ds-canonical/fork/providers/claude/skills/delivery-handoff/references/inventory-checklist.md +196 -0
  857. package/ds-canonical/fork/providers/claude/skills/independent-review/SKILL.md +43 -0
  858. package/ds-canonical/fork/providers/claude/skills/independent-review/agents/openai.yaml +4 -0
  859. package/ds-canonical/fork/providers/claude/skills/independent-review/references/evidence-contract.md +23 -0
  860. package/ds-canonical/fork/providers/claude/skills/performance-audit/SKILL.md +114 -0
  861. package/ds-canonical/fork/providers/claude/skills/product-ui-audit/SKILL.md +252 -0
  862. package/ds-canonical/fork/providers/claude/skills/product-ui-audit/references/audit-checks.md +250 -0
  863. package/ds-canonical/fork/providers/claude/skills/product-ui-audit/references/common-misuses.md +329 -0
  864. package/ds-canonical/fork/providers/claude/skills/product-ui-audit/references/report-template.md +163 -0
  865. package/ds-canonical/fork/providers/claude/skills/propose-options/SKILL.md +190 -0
  866. package/ds-canonical/fork/providers/claude/skills/prototype/SKILL.md +246 -0
  867. package/ds-canonical/fork/providers/claude/skills/prototype/references/audit-checks.md +39 -0
  868. package/ds-canonical/fork/providers/claude/skills/prototype/references/benchmark-sources.md +94 -0
  869. package/ds-canonical/fork/providers/claude/skills/prototype/references/checkpoints.md +200 -0
  870. package/ds-canonical/fork/providers/claude/skills/prototype/references/evaluation-matrix.md +142 -0
  871. package/ds-canonical/fork/providers/claude/skills/prototype/references/ooux-template.md +198 -0
  872. package/ds-canonical/fork/providers/claude/skills/prototype/references/proposal-template.md +230 -0
  873. package/ds-canonical/fork/providers/claude/skills/scan-similar-bugs/SKILL.md +203 -0
  874. package/ds-canonical/fork/providers/claude/skills/ux-audit/SKILL.md +135 -0
  875. package/ds-canonical/fork/providers/claude/skills/visual-audit/SKILL.md +258 -0
  876. package/ds-canonical/fork/providers/claude/skills/visual-audit/output/.gitkeep +0 -0
  877. package/ds-canonical/fork/providers/claude/skills/visual-audit/references/audit-architecture.md +101 -0
  878. package/ds-canonical/fork/providers/claude/skills/visual-audit/references/visual-checklist.md +297 -0
  879. package/ds-canonical/fork/providers/claude/skills/visual-audit/references/world-class-benchmarks.md +198 -0
  880. package/ds-canonical/fork/providers/codex/hook-config.json +57 -0
  881. package/ds-canonical/fork/providers/codex/skills/bug-fix-rhythm/SKILL.md +207 -0
  882. package/ds-canonical/fork/providers/codex/skills/code-quality-audit/SKILL.md +71 -0
  883. package/ds-canonical/fork/providers/codex/skills/delivery-handoff/SKILL.md +244 -0
  884. package/ds-canonical/fork/providers/codex/skills/delivery-handoff/references/flow-diagram.md +180 -0
  885. package/ds-canonical/fork/providers/codex/skills/delivery-handoff/references/handoff-template.md +177 -0
  886. package/ds-canonical/fork/providers/codex/skills/delivery-handoff/references/inventory-checklist.md +196 -0
  887. package/ds-canonical/fork/providers/codex/skills/independent-review/SKILL.md +43 -0
  888. package/ds-canonical/fork/providers/codex/skills/independent-review/agents/openai.yaml +4 -0
  889. package/ds-canonical/fork/providers/codex/skills/independent-review/references/evidence-contract.md +23 -0
  890. package/ds-canonical/fork/providers/codex/skills/performance-audit/SKILL.md +114 -0
  891. package/ds-canonical/fork/providers/codex/skills/product-ui-audit/SKILL.md +252 -0
  892. package/ds-canonical/fork/providers/codex/skills/product-ui-audit/references/audit-checks.md +250 -0
  893. package/ds-canonical/fork/providers/codex/skills/product-ui-audit/references/common-misuses.md +329 -0
  894. package/ds-canonical/fork/providers/codex/skills/product-ui-audit/references/report-template.md +163 -0
  895. package/ds-canonical/fork/providers/codex/skills/propose-options/SKILL.md +190 -0
  896. package/ds-canonical/fork/providers/codex/skills/prototype/SKILL.md +246 -0
  897. package/ds-canonical/fork/providers/codex/skills/prototype/references/audit-checks.md +39 -0
  898. package/ds-canonical/fork/providers/codex/skills/prototype/references/benchmark-sources.md +94 -0
  899. package/ds-canonical/fork/providers/codex/skills/prototype/references/checkpoints.md +200 -0
  900. package/ds-canonical/fork/providers/codex/skills/prototype/references/evaluation-matrix.md +142 -0
  901. package/ds-canonical/fork/providers/codex/skills/prototype/references/ooux-template.md +198 -0
  902. package/ds-canonical/fork/providers/codex/skills/prototype/references/proposal-template.md +230 -0
  903. package/ds-canonical/fork/providers/codex/skills/scan-similar-bugs/SKILL.md +203 -0
  904. package/ds-canonical/fork/providers/codex/skills/ux-audit/SKILL.md +135 -0
  905. package/ds-canonical/fork/providers/codex/skills/visual-audit/SKILL.md +258 -0
  906. package/ds-canonical/fork/providers/codex/skills/visual-audit/output/.gitkeep +0 -0
  907. package/ds-canonical/fork/providers/codex/skills/visual-audit/references/audit-architecture.md +101 -0
  908. package/ds-canonical/fork/providers/codex/skills/visual-audit/references/visual-checklist.md +297 -0
  909. package/ds-canonical/fork/providers/codex/skills/visual-audit/references/world-class-benchmarks.md +198 -0
  910. package/ds-canonical/fork/references/story-baseline-registry.json +123 -0
  911. package/ds-canonical/fork/references/story-baseline-registry.schema.json +145 -0
  912. package/ds-canonical/fork/references/utility-registry.json +426 -0
  913. package/ds-canonical/fork/references/utility-registry.schema.json +409 -0
  914. package/ds-canonical/fork/review/canonical-order.mjs +8 -0
  915. package/ds-canonical/fork/review/provider-review-binding.mjs +2377 -0
  916. package/ds-canonical/fork/skills/bug-fix-rhythm/SKILL.md +196 -0
  917. package/ds-canonical/fork/skills/code-quality-audit/SKILL.md +69 -0
  918. package/ds-canonical/fork/skills/delivery-handoff/SKILL.md +242 -0
  919. package/ds-canonical/fork/skills/delivery-handoff/references/flow-diagram.md +180 -0
  920. package/ds-canonical/fork/skills/delivery-handoff/references/handoff-template.md +177 -0
  921. package/ds-canonical/fork/skills/delivery-handoff/references/inventory-checklist.md +196 -0
  922. package/ds-canonical/fork/skills/independent-review/SKILL.md +37 -0
  923. package/ds-canonical/fork/skills/independent-review/agents/openai.yaml +4 -0
  924. package/ds-canonical/fork/skills/independent-review/references/evidence-contract.md +23 -0
  925. package/ds-canonical/fork/skills/performance-audit/SKILL.md +112 -0
  926. package/ds-canonical/fork/skills/product-ui-audit/SKILL.md +250 -0
  927. package/ds-canonical/fork/skills/product-ui-audit/references/audit-checks.md +250 -0
  928. package/ds-canonical/fork/skills/product-ui-audit/references/common-misuses.md +329 -0
  929. package/ds-canonical/fork/skills/product-ui-audit/references/report-template.md +163 -0
  930. package/ds-canonical/fork/skills/propose-options/SKILL.md +179 -0
  931. package/ds-canonical/fork/skills/prototype/SKILL.md +244 -0
  932. package/ds-canonical/fork/skills/prototype/references/audit-checks.md +39 -0
  933. package/ds-canonical/fork/skills/prototype/references/benchmark-sources.md +94 -0
  934. package/ds-canonical/fork/skills/prototype/references/checkpoints.md +200 -0
  935. package/ds-canonical/fork/skills/prototype/references/evaluation-matrix.md +142 -0
  936. package/ds-canonical/fork/skills/prototype/references/ooux-template.md +198 -0
  937. package/ds-canonical/fork/skills/prototype/references/proposal-template.md +230 -0
  938. package/ds-canonical/fork/skills/scan-similar-bugs/SKILL.md +201 -0
  939. package/ds-canonical/fork/skills/ux-audit/SKILL.md +133 -0
  940. package/ds-canonical/fork/skills/visual-audit/SKILL.md +248 -0
  941. package/ds-canonical/fork/skills/visual-audit/output/.gitkeep +0 -0
  942. package/ds-canonical/fork/skills/visual-audit/references/audit-architecture.md +101 -0
  943. package/ds-canonical/fork/skills/visual-audit/references/visual-checklist.md +297 -0
  944. package/ds-canonical/fork/skills/visual-audit/references/world-class-benchmarks.md +198 -0
  945. package/ds-canonical/hooks/README.md +39 -25
  946. package/ds-canonical/hooks/_log-fire.sh +111 -12
  947. package/ds-canonical/hooks/auto_regen_ds_barrel.sh +90 -0
  948. package/ds-canonical/hooks/block_prototype_imports.py +156 -26
  949. package/ds-canonical/hooks/check_audit_post_report_validator.sh +328 -32
  950. package/ds-canonical/hooks/check_audit_sample_escape.sh +10 -7
  951. package/ds-canonical/hooks/check_benchmark_citation.sh +41 -8
  952. package/ds-canonical/hooks/check_canonical_propagation.sh +85 -24
  953. package/ds-canonical/hooks/check_chrome_header_avatar_canonical.sh +106 -0
  954. package/ds-canonical/hooks/check_codex_brief_invariants.sh +138 -29
  955. package/ds-canonical/hooks/check_codex_collab_5step.sh +31 -25
  956. package/ds-canonical/hooks/check_consumer_app_invariants.sh +731 -0
  957. package/ds-canonical/hooks/check_consumer_code_quality.sh +173 -0
  958. package/ds-canonical/hooks/check_datatable_invariants.sh +173 -29
  959. package/ds-canonical/hooks/check_dim_count_drift.sh +8 -7
  960. package/ds-canonical/hooks/check_ds_anchor_preflight.sh +219 -0
  961. package/ds-canonical/hooks/check_dynamic_tailwind_class.sh +57 -0
  962. package/ds-canonical/hooks/check_escape_marker_abuse.sh +213 -0
  963. package/ds-canonical/hooks/check_field_controls_contracts.sh +37 -8
  964. package/ds-canonical/hooks/check_field_family_invariants.sh +195 -24
  965. package/ds-canonical/hooks/check_file_size_budget.sh +32 -14
  966. package/ds-canonical/hooks/check_full_story_visual_interaction_sweep.sh +111 -0
  967. package/ds-canonical/hooks/check_item_list_gap.sh +56 -0
  968. package/ds-canonical/hooks/check_layout_space_magic_numbers.sh +160 -0
  969. package/ds-canonical/hooks/check_main_branch_workbench.sh +27 -68
  970. package/ds-canonical/hooks/check_naming_and_abstraction.sh +21 -10
  971. package/ds-canonical/hooks/check_opacity_token_usage.sh +238 -68
  972. package/ds-canonical/hooks/check_orphan_ds_css.sh +96 -0
  973. package/ds-canonical/hooks/check_overlay_open_focus_escape_probe.sh +110 -0
  974. package/ds-canonical/hooks/check_pattern_invariants.sh +160 -15
  975. package/ds-canonical/hooks/check_peoplepicker_ssot_drift.sh +4 -4
  976. package/ds-canonical/hooks/check_pixel_quantified_audit.sh +6 -4
  977. package/ds-canonical/hooks/check_plugin_fork_health.sh +282 -0
  978. package/ds-canonical/hooks/check_propose_discipline.sh +255 -0
  979. package/ds-canonical/hooks/check_propose_pre_grep_verify.sh +6 -4
  980. package/ds-canonical/hooks/check_propose_without_benchmark.sh +71 -0
  981. package/ds-canonical/hooks/check_select_all_canonical.sh +1 -1
  982. package/ds-canonical/hooks/check_sidebar_menu_button_implicit_wrap.sh +99 -0
  983. package/ds-canonical/hooks/check_solo_workflow.sh +62 -168
  984. package/ds-canonical/hooks/check_spec_class_drift.sh +33 -19
  985. package/ds-canonical/hooks/check_ssot_header_declaration.sh +56 -0
  986. package/ds-canonical/hooks/check_story_determinism.sh +39 -0
  987. package/ds-canonical/hooks/check_story_invariants.sh +722 -222
  988. package/ds-canonical/hooks/check_storybook_addon_packaging.sh +201 -0
  989. package/ds-canonical/hooks/check_substantive_edit_approval_preflight.sh +143 -64
  990. package/ds-canonical/hooks/check_tabs_content_chrome_body_double_gap.sh +241 -0
  991. package/ds-canonical/hooks/check_tailwind_wildcard_in_docs.sh +86 -0
  992. package/ds-canonical/hooks/check_wrapper_primitive_schema_drift.sh +15 -12
  993. package/ds-canonical/hooks/chrome_header_dispatcher.sh +105 -0
  994. package/ds-canonical/hooks/enforce_home_charter.sh +31 -10
  995. package/ds-canonical/hooks/governance_control_plane.sh +15 -0
  996. package/ds-canonical/hooks/inject_deploy_url_after_push.sh +333 -0
  997. package/ds-canonical/hooks/inject_pending_self_audit.sh +68 -21
  998. package/ds-canonical/hooks/lib/_app_shell_primary_header_consistency.sh +148 -0
  999. package/ds-canonical/hooks/lib/_chrome_header_handcraft.sh +121 -0
  1000. package/ds-canonical/hooks/lib/_code_quality.sh +9 -9
  1001. package/ds-canonical/hooks/lib/_cva_default_sync.sh +11 -13
  1002. package/ds-canonical/hooks/lib/_governance_coverage_check.sh +48 -21
  1003. package/ds-canonical/hooks/lib/_hardcoded_strings.sh +18 -10
  1004. package/ds-canonical/hooks/lib/_header_with_tabs_border.sh +107 -0
  1005. package/ds-canonical/hooks/lib/_hook_integrity.sh +158 -0
  1006. package/ds-canonical/hooks/lib/_layout_space_canonical.sh +5 -5
  1007. package/ds-canonical/hooks/lib/_micro_geometry.sh +291 -0
  1008. package/ds-canonical/hooks/lib/_overlay_handcraft.sh +42 -23
  1009. package/ds-canonical/hooks/lib/_person_data_richness.sh +5 -5
  1010. package/ds-canonical/hooks/lib/_provider_paths.sh +162 -0
  1011. package/ds-canonical/hooks/lib/_story_compile_drift.sh +5 -5
  1012. package/ds-canonical/hooks/lib/_tab_lg_chrome_header_equal.sh +290 -0
  1013. package/ds-canonical/hooks/lib/_token_hygiene.sh +55 -20
  1014. package/ds-canonical/hooks/lib/approval-evidence.mjs +1321 -0
  1015. package/ds-canonical/hooks/lib/audit-report-validator.mjs +122 -0
  1016. package/ds-canonical/hooks/lib/provider-marker-regex.mjs +23 -0
  1017. package/ds-canonical/hooks/lib/tsx-governance-analysis.mjs +410 -0
  1018. package/ds-canonical/hooks/log_governance_fires.sh +45 -13
  1019. package/ds-canonical/hooks/log_skill_invokes.sh +8 -7
  1020. package/ds-canonical/hooks/post_edit_dispatcher.sh +106 -28
  1021. package/ds-canonical/hooks/registrations.json +488 -0
  1022. package/ds-canonical/hooks/registrations.schema.json +58 -0
  1023. package/ds-canonical/hooks/retired/2026-06-11-prune-merge/check_addon_subdir_ship.sh +76 -0
  1024. package/ds-canonical/hooks/retired/2026-06-11-prune-merge/check_consumer_app_story_title.sh +81 -0
  1025. package/ds-canonical/hooks/retired/2026-06-11-prune-merge/check_consumer_ds_primitive_misuse.sh +120 -0
  1026. package/ds-canonical/hooks/retired/2026-06-11-prune-merge/check_consumer_no_ds_catalog.sh +101 -0
  1027. package/ds-canonical/hooks/retired/2026-06-11-prune-merge/check_consumer_story_baseline.sh +76 -0
  1028. package/ds-canonical/hooks/retired/2026-06-11-prune-merge/check_data_table_size_num_to_meta_width.sh +68 -0
  1029. package/ds-canonical/hooks/retired/2026-06-11-prune-merge/check_fork_user_plugin_install.sh +79 -0
  1030. package/ds-canonical/hooks/retired/2026-06-11-prune-merge/check_plugin_freshness.sh +68 -0
  1031. package/ds-canonical/hooks/retired/2026-06-11-prune-merge/check_propose_cite_required.sh +88 -0
  1032. package/ds-canonical/hooks/{check_propose_plain_chinese.sh → retired/2026-06-11-prune-merge/check_propose_plain_chinese.sh} +2 -2
  1033. package/ds-canonical/hooks/retired/2026-06-11-prune-merge/check_storybook_addon_preset_cjs.sh +87 -0
  1034. package/ds-canonical/hooks/{lib/_approval_re.sh → retired/_approval_re.sh.retired-2026-08-18} +6 -7
  1035. package/ds-canonical/hooks/retired/check_container_breathing.sh +1 -1
  1036. package/ds-canonical/hooks/retired/check_governance_compliance.sh +1 -1
  1037. package/ds-canonical/hooks/session_start_governance_check.sh +157 -69
  1038. package/ds-canonical/hooks/stop_audit_content_quality.sh +20 -0
  1039. package/ds-canonical/hooks/stop_extract_canonical_rules.sh +20 -0
  1040. package/ds-canonical/hooks/stop_passive_logging.sh +133 -59
  1041. package/ds-canonical/hooks/stop_self_audit.sh +472 -178
  1042. package/ds-canonical/hooks/tests/fixtures/transcripts/claude-transcript-jsonl-v1.jsonl +3 -0
  1043. package/ds-canonical/hooks/tests/fixtures/transcripts/claude-transcript-jsonl-v2.jsonl +3 -0
  1044. package/ds-canonical/hooks/tests/registration-output-contracts.mjs +151 -0
  1045. package/ds-canonical/hooks/tests/run-all.sh +35 -5
  1046. package/ds-canonical/hooks/tests/test_auto_regen_ds_barrel.sh +224 -0
  1047. package/ds-canonical/hooks/tests/test_block_prototype_imports.sh +181 -9
  1048. package/ds-canonical/hooks/tests/test_check_addon_subdir_ship.sh +171 -0
  1049. package/ds-canonical/hooks/tests/test_check_app_shell_primary_header_consistency.sh +275 -81
  1050. package/ds-canonical/hooks/tests/test_check_audit_post_report_validator.sh +292 -14
  1051. package/ds-canonical/hooks/tests/test_check_audit_sample_escape.sh +0 -0
  1052. package/ds-canonical/hooks/tests/test_check_benchmark_citation.sh +41 -9
  1053. package/ds-canonical/hooks/tests/test_check_canonical_propagation.sh +56 -5
  1054. package/ds-canonical/hooks/tests/test_check_chrome_header_avatar_canonical.sh +198 -0
  1055. package/ds-canonical/hooks/tests/test_check_chrome_header_handcraft.sh +197 -81
  1056. package/ds-canonical/hooks/tests/test_check_codex_brief_invariants.sh +375 -0
  1057. package/ds-canonical/hooks/tests/test_check_codex_collab_5step.sh +49 -5
  1058. package/ds-canonical/hooks/tests/test_check_consumer_app_invariants.sh +140 -0
  1059. package/ds-canonical/hooks/tests/test_check_consumer_app_story_title.sh +164 -0
  1060. package/ds-canonical/hooks/tests/test_check_consumer_code_quality.sh +86 -0
  1061. package/ds-canonical/hooks/tests/test_check_consumer_ds_primitive_misuse.sh +417 -0
  1062. package/ds-canonical/hooks/tests/test_check_consumer_no_ds_catalog.sh +214 -0
  1063. package/ds-canonical/hooks/tests/test_check_consumer_story_baseline.sh +209 -0
  1064. package/ds-canonical/hooks/tests/test_check_data_table_size_num_to_meta_width.sh +71 -0
  1065. package/ds-canonical/hooks/tests/test_check_datatable_invariants.sh +126 -12
  1066. package/ds-canonical/hooks/tests/test_check_dim_count_drift.sh +13 -8
  1067. package/ds-canonical/hooks/tests/test_check_ds_anchor_preflight.sh +150 -0
  1068. package/ds-canonical/hooks/tests/test_check_dynamic_tailwind_class.sh +76 -0
  1069. package/ds-canonical/hooks/tests/test_check_escape_marker_abuse.sh +278 -0
  1070. package/ds-canonical/hooks/tests/test_check_field_family_invariants.sh +108 -18
  1071. package/ds-canonical/hooks/tests/test_check_file_size_budget.sh +67 -5
  1072. package/ds-canonical/hooks/tests/test_check_fork_user_plugin_install.sh +151 -0
  1073. package/ds-canonical/hooks/tests/test_check_full_story_visual_interaction_sweep.sh +200 -0
  1074. package/ds-canonical/hooks/tests/test_check_header_with_tabs_border.sh +178 -68
  1075. package/ds-canonical/hooks/tests/test_check_item_list_gap.sh +44 -0
  1076. package/ds-canonical/hooks/tests/test_check_layout_space_magic_numbers.sh +264 -0
  1077. package/ds-canonical/hooks/tests/test_check_main_branch_workbench.sh +45 -110
  1078. package/ds-canonical/hooks/tests/test_check_naming_and_abstraction.sh +19 -2
  1079. package/ds-canonical/hooks/tests/test_check_opacity_token_usage.sh +151 -6
  1080. package/ds-canonical/hooks/tests/test_check_orphan_ds_css.sh +228 -0
  1081. package/ds-canonical/hooks/tests/test_check_overlay_open_focus_escape_probe.sh +191 -0
  1082. package/ds-canonical/hooks/tests/test_check_pattern_invariants.sh +103 -7
  1083. package/ds-canonical/hooks/tests/test_check_plugin_fork_health.sh +132 -0
  1084. package/ds-canonical/hooks/tests/test_check_propose_cite_required.sh +181 -0
  1085. package/ds-canonical/hooks/tests/test_check_propose_discipline.sh +69 -0
  1086. package/ds-canonical/hooks/tests/test_check_propose_plain_chinese.sh +10 -7
  1087. package/ds-canonical/hooks/tests/test_check_propose_pre_grep_verify.sh +5 -5
  1088. package/ds-canonical/hooks/tests/test_check_propose_without_benchmark.sh +100 -0
  1089. package/ds-canonical/hooks/tests/test_check_sidebar_menu_button_implicit_wrap.sh +224 -0
  1090. package/ds-canonical/hooks/tests/test_check_solo_workflow.sh +86 -105
  1091. package/ds-canonical/hooks/tests/test_check_spec_class_drift.sh +35 -7
  1092. package/ds-canonical/hooks/tests/test_check_ssot_header_declaration.sh +72 -0
  1093. package/ds-canonical/hooks/tests/{test_check_story_anatomy.sh.broken → test_check_story_anatomy.sh} +73 -4
  1094. package/ds-canonical/hooks/tests/test_check_story_category.sh +411 -0
  1095. package/ds-canonical/hooks/tests/test_check_story_determinism.sh +39 -0
  1096. package/ds-canonical/hooks/tests/test_check_story_invariants.sh +734 -43
  1097. package/ds-canonical/hooks/tests/{test_check_story_name_jargon.sh.broken → test_check_story_name_jargon.sh} +22 -1
  1098. package/ds-canonical/hooks/tests/test_check_storybook_addon_packaging.sh +89 -0
  1099. package/ds-canonical/hooks/tests/test_check_storybook_addon_preset_cjs.sh +200 -0
  1100. package/ds-canonical/hooks/tests/test_check_substantive_edit_approval_preflight.sh +672 -28
  1101. package/ds-canonical/hooks/tests/test_check_tab_lg_chrome_header_equal.sh +182 -97
  1102. package/ds-canonical/hooks/tests/test_check_tabs_content_chrome_body_double_gap.sh +76 -0
  1103. package/ds-canonical/hooks/tests/test_check_tailwind_wildcard_in_docs.sh +182 -0
  1104. package/ds-canonical/hooks/tests/test_check_token_hygiene.sh +63 -15
  1105. package/ds-canonical/hooks/tests/test_check_wrapper_primitive_schema_drift.sh +12 -6
  1106. package/ds-canonical/hooks/tests/test_chrome_header_dispatcher.sh +125 -0
  1107. package/ds-canonical/hooks/tests/test_enforce_home_charter.sh +58 -4
  1108. package/ds-canonical/hooks/tests/test_governance_control_plane.sh +66 -0
  1109. package/ds-canonical/hooks/tests/test_governance_coverage_check.sh +88 -0
  1110. package/ds-canonical/hooks/tests/test_hook_integrity_classification.sh +217 -0
  1111. package/ds-canonical/hooks/tests/test_inject_deploy_url_after_push.sh +276 -0
  1112. package/ds-canonical/hooks/tests/test_inject_pending_self_audit.sh +116 -11
  1113. package/ds-canonical/hooks/tests/test_log_governance_fires.sh +117 -1
  1114. package/ds-canonical/hooks/tests/test_log_skill_invokes.sh +74 -5
  1115. package/ds-canonical/hooks/tests/test_post_edit_dispatcher.sh +234 -77
  1116. package/ds-canonical/hooks/tests/test_provider_peer_symmetry.sh +76 -0
  1117. package/ds-canonical/hooks/tests/test_registration_output_contracts.sh +20 -0
  1118. package/ds-canonical/hooks/tests/test_session_start_governance_check.sh +145 -22
  1119. package/ds-canonical/hooks/tests/test_stop_audit_content_quality.sh +75 -0
  1120. package/ds-canonical/hooks/tests/test_stop_capture_metrics.sh +101 -23
  1121. package/ds-canonical/hooks/tests/test_stop_extract_canonical_rules.sh +75 -0
  1122. package/ds-canonical/hooks/tests/{test_stop_governance_drift_check.sh.broken → test_stop_governance_drift_check.sh} +22 -12
  1123. package/ds-canonical/hooks/tests/test_stop_passive_logging.sh +102 -1
  1124. package/ds-canonical/hooks/tests/test_stop_repository_command_context.sh +38 -0
  1125. package/ds-canonical/hooks/tests/test_stop_self_audit.sh +427 -13
  1126. package/ds-canonical/references/README.md +19 -11
  1127. package/ds-canonical/references/build-ui-canonicals.md +23 -6
  1128. package/ds-canonical/references/certified-surfaces.md +46 -0
  1129. package/ds-canonical/references/composition-fidelity.md +47 -0
  1130. package/ds-canonical/references/cva-patterns.md +2 -2
  1131. package/ds-canonical/references/drag-canonical.md +67 -311
  1132. package/ds-canonical/references/failure-class-registry.json +160 -0
  1133. package/ds-canonical/references/governance-audit-coverage.md +34 -0
  1134. package/ds-canonical/references/item-anatomy-recipe.md +3 -3
  1135. package/ds-canonical/references/naming-conventions.md +2 -1
  1136. package/ds-canonical/references/principle-dim-map.json +136 -9
  1137. package/ds-canonical/references/props-naming.md +18 -3
  1138. package/ds-canonical/references/repository-hygiene-policy.json +143 -0
  1139. package/ds-canonical/references/repository-hygiene.md +106 -0
  1140. package/ds-canonical/references/runtime-evidence-retention.md +88 -0
  1141. package/ds-canonical/references/scenario-definition.md +146 -0
  1142. package/ds-canonical/references/spec-rules.md +1 -1
  1143. package/ds-canonical/references/ssot-consultation.md +5 -5
  1144. package/ds-canonical/references/ssot-index.md +11 -10
  1145. package/ds-canonical/references/story-baseline-registry.json +55 -11
  1146. package/ds-canonical/references/story-baseline-registry.schema.json +145 -0
  1147. package/ds-canonical/references/structural-token-retention.md +7 -10
  1148. package/ds-canonical/references/tailwind-gotchas.md +4 -4
  1149. package/ds-canonical/references/ui-dev-rules.md +22 -1
  1150. package/ds-canonical/rules/README.md +15 -10
  1151. package/ds-canonical/rules/meta-patterns.md +30 -30
  1152. package/ds-canonical/rules/self-verify.md +22 -12
  1153. package/ds-canonical/rules/spec-rules.md +2 -2
  1154. package/ds-canonical/rules/story-rules.md +36 -9
  1155. package/ds-canonical/rules/ui-development.md +23 -15
  1156. package/ds-canonical/skills/README.md +29 -17
  1157. package/ds-canonical/skills/bug-fix-rhythm/SKILL.md +26 -13
  1158. package/ds-canonical/skills/canonical-reviewer/SKILL.md +29 -0
  1159. package/ds-canonical/skills/code-quality-audit/SKILL.md +15 -11
  1160. package/ds-canonical/skills/codex-collab/SKILL.md +22 -235
  1161. package/ds-canonical/skills/codex-collab/references/brief-template.md +37 -43
  1162. package/ds-canonical/skills/codex-collab/references/transport.md +38 -57
  1163. package/ds-canonical/skills/codify-corrections/SKILL.md +37 -36
  1164. package/ds-canonical/skills/codify-principle/SKILL.md +35 -34
  1165. package/ds-canonical/skills/component-quality-gate/SKILL.md +17 -17
  1166. package/ds-canonical/skills/component-quality-gate/references/checklist.md +8 -8
  1167. package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +47 -224
  1168. package/ds-canonical/skills/deep-audit-cross-codex/references/phase-a-workflow.md +36 -102
  1169. package/ds-canonical/skills/deep-audit-cross-codex/references/phase-b-codex-brief.md +33 -147
  1170. package/ds-canonical/skills/deep-audit-cross-codex/references/skill-relationships-antipatterns-benchmarks.md +28 -0
  1171. package/ds-canonical/skills/deep-audit-cross-codex/references/triage-rubric.md +15 -80
  1172. package/ds-canonical/skills/deep-audit-cross-codex/references/upstream-directives-r-mapping.md +34 -0
  1173. package/ds-canonical/skills/delivery-handoff/SKILL.md +19 -8
  1174. package/ds-canonical/skills/design-system-audit/SKILL.md +130 -76
  1175. package/ds-canonical/skills/design-system-audit/references/audit-prompts.md +207 -105
  1176. package/ds-canonical/skills/design-system-audit/references/checkpoints.md +59 -50
  1177. package/ds-canonical/skills/design-system-audit/references/historical-bugs.md +33 -9
  1178. package/ds-canonical/skills/design-system-audit/references/principle-audit-protocol.md +51 -32
  1179. package/ds-canonical/skills/design-system-audit/references/rule-placement.md +57 -53
  1180. package/ds-canonical/skills/ensure-canonical/SKILL.md +37 -35
  1181. package/ds-canonical/skills/governance-health/SKILL.md +31 -25
  1182. package/ds-canonical/skills/governance-status/SKILL.md +30 -0
  1183. package/ds-canonical/skills/independent-review/SKILL.md +37 -0
  1184. package/ds-canonical/skills/independent-review/agents/openai.yaml +4 -0
  1185. package/ds-canonical/skills/independent-review/references/evidence-contract.md +23 -0
  1186. package/ds-canonical/skills/knowledge-prune/SKILL.md +57 -114
  1187. package/ds-canonical/skills/knowledge-prune/references/phase-z-cross-repo-ssot-propagation.md +15 -0
  1188. package/ds-canonical/skills/knowledge-prune/references/prune-dimensions-d5-d10.md +57 -0
  1189. package/ds-canonical/skills/knowledge-prune/references/world-class-prune-alignment.md +7 -0
  1190. package/ds-canonical/skills/new-component/SKILL.md +33 -25
  1191. package/ds-canonical/skills/new-component/references/new-component-checklist.md +12 -11
  1192. package/ds-canonical/skills/performance-audit/SKILL.md +15 -12
  1193. package/ds-canonical/skills/product-ui-audit/SKILL.md +52 -34
  1194. package/ds-canonical/skills/product-ui-audit/references/audit-checks.md +10 -7
  1195. package/ds-canonical/skills/product-ui-audit/references/report-template.md +30 -26
  1196. package/ds-canonical/skills/propose-options/SKILL.md +23 -23
  1197. package/ds-canonical/skills/prototype/SKILL.md +19 -21
  1198. package/ds-canonical/skills/prototype/references/audit-checks.md +6 -5
  1199. package/ds-canonical/skills/prototype/references/checkpoints.md +26 -17
  1200. package/ds-canonical/skills/prototype/references/evaluation-matrix.md +2 -2
  1201. package/ds-canonical/skills/prototype/references/ooux-template.md +1 -1
  1202. package/ds-canonical/skills/prototype/references/proposal-template.md +5 -5
  1203. package/ds-canonical/skills/scan-similar-bugs/SKILL.md +29 -28
  1204. package/ds-canonical/skills/story-auto-compile-migrate/SKILL.md +26 -25
  1205. package/ds-canonical/skills/story-writing/SKILL.md +25 -14
  1206. package/ds-canonical/skills/story-writing/references/anatomy-standard.md +2 -2
  1207. package/ds-canonical/skills/story-writing/references/category-templates.md +15 -15
  1208. package/ds-canonical/skills/ux-audit/SKILL.md +13 -10
  1209. package/ds-canonical/skills/visual-audit/SKILL.md +47 -46
  1210. package/ds-canonical/skills/visual-audit/references/audit-architecture.md +7 -7
  1211. package/ds-canonical/skills/visual-audit/references/visual-checklist.md +14 -14
  1212. package/ds-canonical/skills/visual-audit/references/world-class-benchmarks.md +3 -3
  1213. package/ds-canonical/templates/_README.md +22 -0
  1214. package/ds-canonical/templates/consumer-governance.md +82 -0
  1215. package/ds-canonical/templates/dashboard-app.tsx +186 -0
  1216. package/ds-canonical/templates/product-launchers/fork-governance-dispatcher.sh +154 -0
  1217. package/ds-canonical/templates/product-launchers/format-provider-hook-output.mjs +25 -0
  1218. package/ds-canonical/templates/product-launchers/independent-review.mjs +490 -0
  1219. package/ds-canonical/templates/product-launchers/inject_fork_governance_preamble.sh +99 -0
  1220. package/ds-canonical/templates/product-launchers/normalize-provider-event.mjs +355 -0
  1221. package/ds-canonical/templates/product-launchers/product-hook-dispatch-runtime.mjs +1067 -0
  1222. package/ds-canonical/templates/product-provider-wiring.json +82 -0
  1223. package/ds-canonical/templates/product-provider-wiring.schema.json +153 -0
  1224. package/ds-story-manifest.json +1791 -0
  1225. package/llms-full.txt +450 -0
  1226. package/llms.txt +74 -0
  1227. package/package.json +43 -10
  1228. package/src/README.md +1 -1
  1229. package/src/components/Accordion/accordion.anatomy.stories.tsx +496 -0
  1230. package/src/components/Accordion/accordion.principles.stories.tsx +365 -0
  1231. package/src/components/Accordion/accordion.spec.md +31 -5
  1232. package/src/components/Accordion/accordion.stories.tsx +192 -0
  1233. package/src/components/Accordion/accordion.tsx +13 -5
  1234. package/src/components/Accordion/index.ts +5 -0
  1235. package/src/components/AccountMenu/account-menu.anatomy.stories.tsx +117 -0
  1236. package/src/components/AccountMenu/account-menu.principles.stories.tsx +99 -0
  1237. package/src/components/AccountMenu/account-menu.spec.md +127 -0
  1238. package/src/components/AccountMenu/account-menu.stories.tsx +65 -0
  1239. package/src/components/AccountMenu/account-menu.tsx +194 -0
  1240. package/src/components/AccountMenu/index.ts +5 -0
  1241. package/src/components/AgentPanel/agent-fab.tsx +126 -0
  1242. package/src/components/AgentPanel/agent-logo.tsx +491 -0
  1243. package/src/components/AgentPanel/agent-panel.anatomy.stories.tsx +127 -0
  1244. package/src/components/AgentPanel/agent-panel.css +59 -0
  1245. package/src/components/AgentPanel/agent-panel.principles.stories.tsx +57 -0
  1246. package/src/components/AgentPanel/agent-panel.spec.md +319 -0
  1247. package/src/components/AgentPanel/agent-panel.stories.tsx +360 -0
  1248. package/src/components/AgentPanel/agent-panel.tsx +1241 -0
  1249. package/src/components/AgentPanel/index.ts +5 -0
  1250. package/src/components/Alert/alert.anatomy.stories.tsx +241 -0
  1251. package/src/components/Alert/alert.principles.stories.tsx +214 -0
  1252. package/src/components/Alert/alert.spec.md +28 -26
  1253. package/src/components/Alert/alert.stories.tsx +136 -0
  1254. package/src/components/Alert/alert.tsx +19 -8
  1255. package/src/components/Alert/index.ts +5 -0
  1256. package/src/components/AppShell/_demo-helpers.tsx +107 -28
  1257. package/src/components/AppShell/app-shell.anatomy.stories.tsx +185 -0
  1258. package/src/components/AppShell/app-shell.principles.stories.tsx +140 -0
  1259. package/src/components/AppShell/app-shell.spec.md +84 -48
  1260. package/src/components/AppShell/app-shell.stories.tsx +445 -0
  1261. package/src/components/AppShell/app-shell.tsx +77 -24
  1262. package/src/components/AppShell/index.ts +5 -0
  1263. package/src/components/AspectRatio/aspect-ratio.anatomy.stories.tsx +208 -0
  1264. package/src/components/AspectRatio/aspect-ratio.principles.stories.tsx +303 -0
  1265. package/src/components/AspectRatio/aspect-ratio.spec.md +24 -12
  1266. package/src/components/AspectRatio/aspect-ratio.stories.tsx +91 -0
  1267. package/src/components/AspectRatio/aspect-ratio.tsx +6 -3
  1268. package/src/components/AspectRatio/index.ts +5 -0
  1269. package/src/components/Avatar/avatar.anatomy.stories.tsx +705 -0
  1270. package/src/components/Avatar/avatar.principles.stories.tsx +249 -0
  1271. package/src/components/Avatar/avatar.spec.md +66 -103
  1272. package/src/components/Avatar/avatar.stories.tsx +156 -0
  1273. package/src/components/Avatar/avatar.tsx +85 -64
  1274. package/src/components/Avatar/index.ts +5 -0
  1275. package/src/components/Badge/badge.anatomy.stories.tsx +591 -0
  1276. package/src/components/Badge/badge.principles.stories.tsx +391 -0
  1277. package/src/components/Badge/badge.spec.md +43 -23
  1278. package/src/components/Badge/badge.stories.tsx +82 -0
  1279. package/src/components/Badge/badge.tsx +38 -14
  1280. package/src/components/Badge/index.ts +5 -0
  1281. package/src/components/Breadcrumb/breadcrumb.anatomy.stories.tsx +462 -0
  1282. package/src/components/Breadcrumb/breadcrumb.principles.stories.tsx +353 -0
  1283. package/src/components/Breadcrumb/breadcrumb.spec.md +55 -43
  1284. package/src/components/Breadcrumb/breadcrumb.stories.tsx +381 -0
  1285. package/src/components/Breadcrumb/breadcrumb.tsx +43 -86
  1286. package/src/components/Breadcrumb/index.ts +5 -0
  1287. package/src/components/BulkActionBar/bulk-action-bar.anatomy.stories.tsx +225 -0
  1288. package/src/components/BulkActionBar/bulk-action-bar.principles.stories.tsx +255 -0
  1289. package/src/components/BulkActionBar/bulk-action-bar.spec.md +64 -36
  1290. package/src/components/BulkActionBar/bulk-action-bar.stories.tsx +124 -0
  1291. package/src/components/BulkActionBar/bulk-action-bar.tsx +26 -18
  1292. package/src/components/BulkActionBar/index.ts +5 -0
  1293. package/src/components/Button/button-group.tsx +64 -21
  1294. package/src/components/Button/button.anatomy.stories.tsx +729 -0
  1295. package/src/components/Button/button.principles.stories.tsx +369 -0
  1296. package/src/components/Button/button.spec.md +42 -39
  1297. package/src/components/Button/button.stories.tsx +368 -0
  1298. package/src/components/Button/button.tsx +109 -55
  1299. package/src/components/Button/index.ts +6 -0
  1300. package/src/components/Calendar/calendar.anatomy.stories.tsx +237 -0
  1301. package/src/components/Calendar/calendar.principles.stories.tsx +152 -0
  1302. package/src/components/Calendar/calendar.spec.md +80 -55
  1303. package/src/components/Calendar/calendar.stories.tsx +108 -0
  1304. package/src/components/Calendar/calendar.tsx +200 -134
  1305. package/src/components/Calendar/index.ts +5 -0
  1306. package/src/components/Carousel/carousel.anatomy.stories.tsx +517 -0
  1307. package/src/components/Carousel/carousel.principles.stories.tsx +334 -0
  1308. package/src/components/Carousel/carousel.spec.md +51 -15
  1309. package/src/components/Carousel/carousel.stories.tsx +125 -0
  1310. package/src/components/Carousel/carousel.tsx +139 -56
  1311. package/src/components/Carousel/index.ts +5 -0
  1312. package/src/components/Chart/chart.anatomy.stories.tsx +310 -0
  1313. package/src/components/Chart/chart.principles.stories.tsx +437 -0
  1314. package/src/components/Chart/chart.spec.md +24 -5
  1315. package/src/components/Chart/chart.stories.tsx +263 -0
  1316. package/src/components/Chart/chart.tsx +10 -3
  1317. package/src/components/Chart/index.ts +5 -0
  1318. package/src/components/Checkbox/checkbox-group.tsx +28 -16
  1319. package/src/components/Checkbox/checkbox.anatomy.stories.tsx +614 -0
  1320. package/src/components/Checkbox/checkbox.principles.stories.tsx +216 -0
  1321. package/src/components/Checkbox/checkbox.spec.md +65 -42
  1322. package/src/components/Checkbox/checkbox.stories.tsx +119 -0
  1323. package/src/components/Checkbox/checkbox.tsx +189 -32
  1324. package/src/components/Checkbox/index.ts +6 -0
  1325. package/src/components/Chip/chip.anatomy.stories.tsx +442 -0
  1326. package/src/components/Chip/chip.principles.stories.tsx +228 -0
  1327. package/src/components/Chip/chip.spec.md +52 -40
  1328. package/src/components/Chip/chip.stories.tsx +111 -0
  1329. package/src/components/Chip/chip.tsx +130 -45
  1330. package/src/components/Chip/index.ts +5 -0
  1331. package/src/components/CircularProgress/circular-progress.anatomy.stories.tsx +177 -0
  1332. package/src/components/CircularProgress/circular-progress.principles.stories.tsx +265 -0
  1333. package/src/components/CircularProgress/circular-progress.spec.md +32 -32
  1334. package/src/components/CircularProgress/circular-progress.stories.tsx +149 -0
  1335. package/src/components/CircularProgress/circular-progress.tsx +5 -6
  1336. package/src/components/CircularProgress/index.ts +5 -0
  1337. package/src/components/Coachmark/coachmark-story-helpers.tsx +39 -0
  1338. package/src/components/Coachmark/coachmark.anatomy.stories.tsx +356 -0
  1339. package/src/components/Coachmark/coachmark.principles.stories.tsx +356 -0
  1340. package/src/components/Coachmark/coachmark.spec.md +36 -29
  1341. package/src/components/Coachmark/coachmark.stories.tsx +220 -0
  1342. package/src/components/Coachmark/coachmark.tsx +36 -10
  1343. package/src/components/Coachmark/index.ts +5 -0
  1344. package/src/components/Combobox/combobox.anatomy.stories.tsx +926 -0
  1345. package/src/components/Combobox/combobox.principles.stories.tsx +238 -0
  1346. package/src/components/Combobox/combobox.spec.md +45 -16
  1347. package/src/components/Combobox/combobox.stories.tsx +194 -0
  1348. package/src/components/Combobox/combobox.tsx +278 -97
  1349. package/src/components/Combobox/index.ts +5 -0
  1350. package/src/components/Command/command.anatomy.stories.tsx +107 -0
  1351. package/src/components/Command/command.principles.stories.tsx +86 -0
  1352. package/src/components/Command/command.spec.md +33 -23
  1353. package/src/components/Command/command.stories.tsx +312 -0
  1354. package/src/components/Command/command.tsx +28 -12
  1355. package/src/components/Command/index.ts +5 -0
  1356. package/src/components/DataTable/active-editor-controller.ts +4 -3
  1357. package/src/components/DataTable/cell-registry.tsx +161 -120
  1358. package/src/components/DataTable/column-types.ts +14 -18
  1359. package/src/components/DataTable/data-table-column-visibility-panel.tsx +28 -12
  1360. package/src/components/DataTable/data-table-filter-group.tsx +248 -0
  1361. package/src/components/DataTable/data-table-filter-labels.ts +100 -0
  1362. package/src/components/DataTable/data-table-filter-panel.tsx +85 -448
  1363. package/src/components/DataTable/data-table-filter-value-picker.tsx +254 -0
  1364. package/src/components/DataTable/data-table-interaction-layer.tsx +26 -29
  1365. package/src/components/DataTable/data-table-sort-manager.tsx +19 -11
  1366. package/src/components/DataTable/data-table.anatomy.stories.tsx +522 -0
  1367. package/src/components/DataTable/data-table.css +34 -8
  1368. package/src/components/DataTable/data-table.principles.stories.tsx +270 -0
  1369. package/src/components/DataTable/data-table.spec.md +186 -172
  1370. package/src/components/DataTable/data-table.stories.tsx +2311 -0
  1371. package/src/components/DataTable/data-table.tsx +678 -278
  1372. package/src/components/DataTable/filter-operators.spec.md +269 -0
  1373. package/src/components/DataTable/filter-operators.ts +1 -1
  1374. package/src/components/DataTable/filter-tree.ts +194 -44
  1375. package/src/components/DataTable/index.ts +8 -0
  1376. package/src/components/DateGrid/date-grid.anatomy.stories.tsx +587 -0
  1377. package/src/components/DateGrid/date-grid.principles.stories.tsx +344 -0
  1378. package/src/components/DateGrid/date-grid.spec.md +46 -41
  1379. package/src/components/DateGrid/date-grid.stories.tsx +204 -0
  1380. package/src/components/DateGrid/date-grid.tsx +89 -28
  1381. package/src/components/DateGrid/index.ts +5 -0
  1382. package/src/components/DatePicker/date-picker.anatomy.stories.tsx +946 -0
  1383. package/src/components/DatePicker/date-picker.principles.stories.tsx +205 -0
  1384. package/src/components/DatePicker/date-picker.spec.md +61 -57
  1385. package/src/components/DatePicker/date-picker.stories.tsx +364 -0
  1386. package/src/components/DatePicker/date-picker.tsx +382 -198
  1387. package/src/components/DatePicker/index.ts +5 -0
  1388. package/src/components/DescriptionList/description-list.anatomy.stories.tsx +254 -0
  1389. package/src/components/DescriptionList/description-list.principles.stories.tsx +309 -0
  1390. package/src/components/DescriptionList/description-list.spec.md +35 -30
  1391. package/src/components/DescriptionList/description-list.stories.tsx +113 -0
  1392. package/src/components/DescriptionList/description-list.tsx +16 -14
  1393. package/src/components/DescriptionList/index.ts +5 -0
  1394. package/src/components/Dialog/dialog.anatomy.stories.tsx +485 -0
  1395. package/src/components/Dialog/dialog.principles.stories.tsx +261 -0
  1396. package/src/components/Dialog/dialog.spec.md +58 -32
  1397. package/src/components/Dialog/dialog.stories.tsx +548 -0
  1398. package/src/components/Dialog/dialog.tsx +81 -36
  1399. package/src/components/Dialog/index.ts +5 -0
  1400. package/src/components/DropdownMenu/dropdown-menu.anatomy.stories.tsx +971 -0
  1401. package/src/components/DropdownMenu/dropdown-menu.principles.stories.tsx +341 -0
  1402. package/src/components/DropdownMenu/dropdown-menu.spec.md +40 -22
  1403. package/src/components/DropdownMenu/dropdown-menu.stories.tsx +276 -0
  1404. package/src/components/DropdownMenu/dropdown-menu.tsx +153 -40
  1405. package/src/components/DropdownMenu/index.ts +5 -0
  1406. package/src/components/Empty/empty.anatomy.stories.tsx +275 -0
  1407. package/src/components/Empty/empty.principles.stories.tsx +314 -0
  1408. package/src/components/Empty/empty.spec.md +42 -42
  1409. package/src/components/Empty/empty.stories.tsx +69 -0
  1410. package/src/components/Empty/empty.tsx +23 -11
  1411. package/src/components/Empty/index.ts +5 -0
  1412. package/src/components/Field/field-context.ts +196 -4
  1413. package/src/components/Field/field-controls.spec.md +161 -54
  1414. package/src/components/Field/field-edit-keys.ts +66 -0
  1415. package/src/components/Field/field-types.ts +71 -20
  1416. package/src/components/Field/field-wrapper.tsx +139 -89
  1417. package/src/components/Field/field.anatomy.stories.tsx +547 -0
  1418. package/src/components/Field/field.principles.stories.tsx +255 -0
  1419. package/src/components/Field/field.spec.md +76 -34
  1420. package/src/components/Field/field.stories.tsx +751 -0
  1421. package/src/components/Field/field.tsx +65 -12
  1422. package/src/components/Field/form-validation.spec.md +60 -34
  1423. package/src/components/Field/index.ts +6 -0
  1424. package/src/components/Field/use-form-validation.ts +270 -0
  1425. package/src/components/FieldControlGroup/field-control-group.anatomy.stories.tsx +119 -0
  1426. package/src/components/FieldControlGroup/field-control-group.principles.stories.tsx +121 -0
  1427. package/src/components/FieldControlGroup/field-control-group.spec.md +41 -19
  1428. package/src/components/FieldControlGroup/field-control-group.stories.tsx +162 -0
  1429. package/src/components/FieldControlGroup/field-control-group.tsx +13 -15
  1430. package/src/components/FieldControlGroup/index.ts +5 -0
  1431. package/src/components/FileItem/file-item.anatomy.stories.tsx +346 -0
  1432. package/src/components/FileItem/file-item.principles.stories.tsx +245 -0
  1433. package/src/components/FileItem/file-item.spec.md +133 -104
  1434. package/src/components/FileItem/file-item.stories.tsx +289 -0
  1435. package/src/components/FileItem/file-item.tsx +85 -39
  1436. package/src/components/FileItem/index.ts +5 -0
  1437. package/src/components/FileUpload/file-upload.anatomy.stories.tsx +446 -0
  1438. package/src/components/FileUpload/file-upload.principles.stories.tsx +312 -0
  1439. package/src/components/FileUpload/file-upload.spec.md +66 -24
  1440. package/src/components/FileUpload/file-upload.stories.tsx +219 -0
  1441. package/src/components/FileUpload/file-upload.tsx +138 -43
  1442. package/src/components/FileUpload/index.ts +5 -0
  1443. package/src/components/FileViewer/can-render-image.ts +15 -0
  1444. package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +1130 -0
  1445. package/src/components/FileViewer/file-viewer.principles.stories.tsx +636 -0
  1446. package/src/components/FileViewer/file-viewer.spec.md +70 -58
  1447. package/src/components/FileViewer/file-viewer.stories.tsx +417 -0
  1448. package/src/components/FileViewer/file-viewer.tsx +157 -72
  1449. package/src/components/FileViewer/image-renderer.tsx +38 -15
  1450. package/src/components/FileViewer/index.ts +5 -0
  1451. package/src/components/HoverCard/hover-card.anatomy.stories.tsx +151 -0
  1452. package/src/components/HoverCard/hover-card.principles.stories.tsx +211 -0
  1453. package/src/components/HoverCard/hover-card.spec.md +38 -28
  1454. package/src/components/HoverCard/hover-card.stories.tsx +304 -0
  1455. package/src/components/HoverCard/hover-card.tsx +23 -6
  1456. package/src/components/HoverCard/index.ts +5 -0
  1457. package/src/components/InlineEdit/index.ts +5 -0
  1458. package/src/components/InlineEdit/inline-edit.anatomy.stories.tsx +250 -0
  1459. package/src/components/InlineEdit/inline-edit.principles.stories.tsx +142 -0
  1460. package/src/components/InlineEdit/inline-edit.spec.md +186 -0
  1461. package/src/components/InlineEdit/inline-edit.stories.tsx +261 -0
  1462. package/src/components/InlineEdit/inline-edit.tsx +455 -0
  1463. package/src/components/Input/index.ts +5 -0
  1464. package/src/components/Input/input.anatomy.stories.tsx +780 -0
  1465. package/src/components/Input/input.principles.stories.tsx +260 -0
  1466. package/src/components/Input/input.spec.md +34 -26
  1467. package/src/components/Input/input.stories.tsx +150 -0
  1468. package/src/components/Input/input.tsx +102 -45
  1469. package/src/components/LinkInput/index.ts +5 -0
  1470. package/src/components/LinkInput/link-input.anatomy.stories.tsx +748 -0
  1471. package/src/components/LinkInput/link-input.principles.stories.tsx +182 -0
  1472. package/src/components/LinkInput/link-input.spec.md +25 -7
  1473. package/src/components/LinkInput/link-input.stories.tsx +133 -0
  1474. package/src/components/LinkInput/link-input.tsx +89 -66
  1475. package/src/components/Menu/index.ts +5 -0
  1476. package/src/components/Menu/menu-item.anatomy.stories.tsx +780 -0
  1477. package/src/components/Menu/menu-item.principles.stories.tsx +81 -0
  1478. package/src/components/Menu/menu-item.spec.md +55 -32
  1479. package/src/components/Menu/menu-item.stories.tsx +242 -0
  1480. package/src/components/Menu/menu-item.tsx +71 -12
  1481. package/src/components/Notice/index.ts +5 -0
  1482. package/src/components/Notice/notice.anatomy.stories.tsx +614 -0
  1483. package/src/components/Notice/notice.principles.stories.tsx +102 -0
  1484. package/src/components/Notice/notice.spec.md +39 -18
  1485. package/src/components/Notice/notice.stories.tsx +257 -0
  1486. package/src/components/Notice/notice.tsx +62 -16
  1487. package/src/components/NumberInput/index.ts +5 -0
  1488. package/src/components/NumberInput/number-input.anatomy.stories.tsx +822 -0
  1489. package/src/components/NumberInput/number-input.principles.stories.tsx +206 -0
  1490. package/src/components/NumberInput/number-input.spec.md +35 -16
  1491. package/src/components/NumberInput/number-input.stories.tsx +167 -0
  1492. package/src/components/NumberInput/number-input.tsx +65 -31
  1493. package/src/components/OverflowIndicator/index.ts +5 -0
  1494. package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +550 -0
  1495. package/src/components/OverflowIndicator/overflow-indicator.principles.stories.tsx +76 -0
  1496. package/src/components/OverflowIndicator/overflow-indicator.spec.md +36 -26
  1497. package/src/components/OverflowIndicator/overflow-indicator.stories.tsx +222 -0
  1498. package/src/components/OverflowIndicator/overflow-indicator.tsx +22 -7
  1499. package/src/components/Pagination/index.ts +5 -0
  1500. package/src/components/Pagination/pagination.anatomy.stories.tsx +144 -0
  1501. package/src/components/Pagination/pagination.principles.stories.tsx +81 -0
  1502. package/src/components/Pagination/pagination.spec.md +150 -0
  1503. package/src/components/Pagination/pagination.stories.tsx +97 -0
  1504. package/src/components/Pagination/pagination.tsx +263 -0
  1505. package/src/components/PeoplePicker/avatar-stack-overflow.ts +1 -1
  1506. package/src/components/PeoplePicker/index.ts +5 -0
  1507. package/src/components/PeoplePicker/people-picker-helpers.ts +5 -5
  1508. package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +437 -0
  1509. package/src/components/PeoplePicker/people-picker.principles.stories.tsx +196 -0
  1510. package/src/components/PeoplePicker/people-picker.spec.md +97 -40
  1511. package/src/components/PeoplePicker/people-picker.stories.tsx +245 -0
  1512. package/src/components/PeoplePicker/people-picker.tsx +225 -108
  1513. package/src/components/PeoplePicker/person-display.tsx +83 -47
  1514. package/src/components/Popover/index.ts +5 -0
  1515. package/src/components/Popover/popover.anatomy.stories.tsx +282 -0
  1516. package/src/components/Popover/popover.principles.stories.tsx +316 -0
  1517. package/src/components/Popover/popover.spec.md +41 -31
  1518. package/src/components/Popover/popover.stories.tsx +120 -0
  1519. package/src/components/Popover/popover.tsx +106 -47
  1520. package/src/components/ProfileCard/index.ts +5 -0
  1521. package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +493 -0
  1522. package/src/components/ProfileCard/profile-card.principles.stories.tsx +225 -0
  1523. package/src/components/ProfileCard/profile-card.spec.md +189 -0
  1524. package/src/components/ProfileCard/profile-card.stories.tsx +57 -0
  1525. package/src/components/{NameCard/name-card.tsx → ProfileCard/profile-card.tsx} +66 -39
  1526. package/src/components/ProgressBar/index.ts +5 -0
  1527. package/src/components/ProgressBar/progress-bar.anatomy.stories.tsx +441 -0
  1528. package/src/components/ProgressBar/progress-bar.principles.stories.tsx +326 -0
  1529. package/src/components/ProgressBar/progress-bar.spec.md +25 -21
  1530. package/src/components/ProgressBar/progress-bar.stories.tsx +133 -0
  1531. package/src/components/ProgressBar/progress-bar.tsx +6 -23
  1532. package/src/components/README.md +5 -4
  1533. package/src/components/RadioGroup/index.ts +5 -0
  1534. package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +672 -0
  1535. package/src/components/RadioGroup/radio-group.principles.stories.tsx +163 -0
  1536. package/src/components/RadioGroup/radio-group.spec.md +15 -9
  1537. package/src/components/RadioGroup/radio-group.stories.tsx +204 -0
  1538. package/src/components/RadioGroup/radio-group.tsx +219 -67
  1539. package/src/components/Rating/index.ts +5 -0
  1540. package/src/components/Rating/rating.anatomy.stories.tsx +498 -0
  1541. package/src/components/Rating/rating.principles.stories.tsx +296 -0
  1542. package/src/components/Rating/rating.spec.md +41 -29
  1543. package/src/components/Rating/rating.stories.tsx +119 -0
  1544. package/src/components/Rating/rating.tsx +37 -18
  1545. package/src/components/ScrollArea/index.ts +5 -0
  1546. package/src/components/ScrollArea/scroll-area.anatomy.stories.tsx +451 -0
  1547. package/src/components/ScrollArea/scroll-area.principles.stories.tsx +331 -0
  1548. package/src/components/ScrollArea/scroll-area.spec.md +24 -22
  1549. package/src/components/ScrollArea/scroll-area.stories.tsx +247 -0
  1550. package/src/components/ScrollArea/scroll-area.tsx +24 -7
  1551. package/src/components/SegmentedControl/index.ts +5 -0
  1552. package/src/components/SegmentedControl/segmented-control.anatomy.stories.tsx +295 -0
  1553. package/src/components/SegmentedControl/segmented-control.principles.stories.tsx +284 -0
  1554. package/src/components/SegmentedControl/segmented-control.spec.md +38 -39
  1555. package/src/components/SegmentedControl/segmented-control.stories.tsx +37 -0
  1556. package/src/components/SegmentedControl/segmented-control.tsx +44 -24
  1557. package/src/components/Select/index.ts +5 -0
  1558. package/src/components/Select/select.anatomy.stories.tsx +834 -0
  1559. package/src/components/Select/select.principles.stories.tsx +312 -0
  1560. package/src/components/Select/select.spec.md +99 -39
  1561. package/src/components/Select/select.stories.tsx +273 -0
  1562. package/src/components/Select/select.tsx +389 -97
  1563. package/src/components/SelectMenu/index.ts +5 -0
  1564. package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +830 -0
  1565. package/src/components/SelectMenu/select-menu.principles.stories.tsx +106 -0
  1566. package/src/components/SelectMenu/select-menu.spec.md +51 -32
  1567. package/src/components/SelectMenu/select-menu.stories.tsx +143 -0
  1568. package/src/components/SelectMenu/select-menu.tsx +276 -33
  1569. package/src/components/SelectionControl/boolean-value.tsx +39 -0
  1570. package/src/components/SelectionControl/index.ts +6 -0
  1571. package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +580 -0
  1572. package/src/components/SelectionControl/selection-item.principles.stories.tsx +77 -0
  1573. package/src/components/SelectionControl/selection-item.spec.md +30 -16
  1574. package/src/components/SelectionControl/selection-item.stories.tsx +137 -0
  1575. package/src/components/SelectionControl/selection-item.tsx +8 -2
  1576. package/src/components/Separator/index.ts +5 -0
  1577. package/src/components/Separator/separator.anatomy.stories.tsx +125 -0
  1578. package/src/components/Separator/separator.principles.stories.tsx +141 -0
  1579. package/src/components/Separator/separator.spec.md +15 -9
  1580. package/src/components/Separator/separator.stories.tsx +106 -0
  1581. package/src/components/Separator/separator.tsx +2 -2
  1582. package/src/components/Sheet/index.ts +5 -0
  1583. package/src/components/Sheet/sheet.anatomy.stories.tsx +269 -0
  1584. package/src/components/Sheet/sheet.principles.stories.tsx +353 -0
  1585. package/src/components/Sheet/sheet.spec.md +16 -17
  1586. package/src/components/Sheet/sheet.stories.tsx +172 -0
  1587. package/src/components/Sheet/sheet.tsx +24 -14
  1588. package/src/components/Sidebar/index.ts +5 -0
  1589. package/src/components/Sidebar/sidebar.anatomy.stories.tsx +776 -0
  1590. package/src/components/Sidebar/sidebar.principles.stories.tsx +507 -0
  1591. package/src/components/Sidebar/sidebar.spec.md +65 -62
  1592. package/src/components/Sidebar/sidebar.stories.tsx +535 -0
  1593. package/src/components/Sidebar/sidebar.tsx +134 -57
  1594. package/src/components/Skeleton/index.ts +5 -0
  1595. package/src/components/Skeleton/skeleton.anatomy.stories.tsx +145 -0
  1596. package/src/components/Skeleton/skeleton.principles.stories.tsx +234 -0
  1597. package/src/components/Skeleton/skeleton.spec.md +15 -14
  1598. package/src/components/Skeleton/skeleton.stories.tsx +134 -0
  1599. package/src/components/Skeleton/skeleton.tsx +2 -2
  1600. package/src/components/Slider/index.ts +5 -0
  1601. package/src/components/Slider/slider.anatomy.stories.tsx +237 -0
  1602. package/src/components/Slider/slider.principles.stories.tsx +250 -0
  1603. package/src/components/Slider/slider.spec.md +66 -118
  1604. package/src/components/Slider/slider.stories.tsx +115 -0
  1605. package/src/components/Slider/slider.tsx +83 -26
  1606. package/src/components/Steps/index.ts +5 -0
  1607. package/src/components/Steps/steps.anatomy.stories.tsx +448 -0
  1608. package/src/components/Steps/steps.principles.stories.tsx +300 -0
  1609. package/src/components/Steps/steps.spec.md +91 -74
  1610. package/src/components/Steps/steps.stories.tsx +306 -0
  1611. package/src/components/Steps/steps.tsx +99 -37
  1612. package/src/components/Switch/index.ts +5 -0
  1613. package/src/components/Switch/switch.anatomy.stories.tsx +227 -0
  1614. package/src/components/Switch/switch.principles.stories.tsx +177 -0
  1615. package/src/components/Switch/switch.spec.md +53 -24
  1616. package/src/components/Switch/switch.stories.tsx +95 -0
  1617. package/src/components/Switch/switch.tsx +176 -43
  1618. package/src/components/Tabs/index.ts +5 -0
  1619. package/src/components/Tabs/tabs-inline-action-geometry.ts +94 -0
  1620. package/src/components/Tabs/tabs.anatomy.stories.tsx +424 -0
  1621. package/src/components/Tabs/tabs.principles.stories.tsx +291 -0
  1622. package/src/components/Tabs/tabs.spec.md +58 -69
  1623. package/src/components/Tabs/tabs.stories.tsx +257 -0
  1624. package/src/components/Tabs/tabs.tsx +262 -82
  1625. package/src/components/Tag/index.ts +5 -0
  1626. package/src/components/Tag/tag.anatomy.stories.tsx +667 -0
  1627. package/src/components/Tag/tag.principles.stories.tsx +270 -0
  1628. package/src/components/Tag/tag.spec.md +112 -100
  1629. package/src/components/Tag/tag.stories.tsx +88 -0
  1630. package/src/components/Tag/tag.tsx +120 -80
  1631. package/src/components/Textarea/index.ts +5 -0
  1632. package/src/components/Textarea/textarea.anatomy.stories.tsx +310 -0
  1633. package/src/components/Textarea/textarea.principles.stories.tsx +192 -0
  1634. package/src/components/Textarea/textarea.spec.md +35 -16
  1635. package/src/components/Textarea/textarea.stories.tsx +57 -0
  1636. package/src/components/Textarea/textarea.tsx +154 -83
  1637. package/src/components/TimePicker/index.ts +5 -0
  1638. package/src/components/TimePicker/time-columns.tsx +15 -3
  1639. package/src/components/TimePicker/time-picker.anatomy.stories.tsx +283 -0
  1640. package/src/components/TimePicker/time-picker.principles.stories.tsx +133 -0
  1641. package/src/components/TimePicker/time-picker.spec.md +58 -52
  1642. package/src/components/TimePicker/time-picker.stories.tsx +193 -0
  1643. package/src/components/TimePicker/time-picker.tsx +119 -60
  1644. package/src/components/Toast/index.ts +5 -0
  1645. package/src/components/Toast/toast.anatomy.stories.tsx +328 -0
  1646. package/src/components/Toast/toast.principles.stories.tsx +216 -0
  1647. package/src/components/Toast/toast.spec.md +26 -16
  1648. package/src/components/Toast/toast.stories.tsx +241 -0
  1649. package/src/components/Toast/toast.tsx +101 -23
  1650. package/src/components/Tooltip/index.ts +5 -0
  1651. package/src/components/Tooltip/tooltip.anatomy.stories.tsx +422 -0
  1652. package/src/components/Tooltip/tooltip.principles.stories.tsx +204 -0
  1653. package/src/components/Tooltip/tooltip.spec.md +37 -23
  1654. package/src/components/Tooltip/tooltip.stories.tsx +82 -0
  1655. package/src/components/Tooltip/tooltip.tsx +58 -12
  1656. package/src/components/TreeView/index.ts +5 -0
  1657. package/src/components/TreeView/tree-view.anatomy.stories.tsx +436 -0
  1658. package/src/components/TreeView/tree-view.principles.stories.tsx +223 -0
  1659. package/src/components/TreeView/tree-view.spec.md +103 -54
  1660. package/src/components/TreeView/tree-view.stories.tsx +365 -0
  1661. package/src/components/TreeView/tree-view.tsx +501 -58
  1662. package/src/hooks/use-controllable.ts +7 -2
  1663. package/src/hooks/use-is-touch-device.ts +8 -1
  1664. package/src/hooks/use-overflow-items.ts +10 -6
  1665. package/src/hooks/use-truncated.ts +111 -0
  1666. package/src/index.ts +651 -71
  1667. package/src/lib/drag-visual.ts +2 -2
  1668. package/src/lib/utils.ts +21 -1
  1669. package/src/patterns/README.md +20 -16
  1670. package/src/patterns/action-bar/action-bar.spec.md +63 -45
  1671. package/src/patterns/action-bar/action-bar.stories.tsx +619 -0
  1672. package/src/patterns/element-anatomy/element-anatomy.spec.md +17 -13
  1673. package/src/patterns/element-anatomy/index.ts +6 -0
  1674. package/src/patterns/element-anatomy/inline-action.spec.md +33 -22
  1675. package/src/patterns/element-anatomy/item-anatomy.spec.md +108 -79
  1676. package/src/patterns/element-anatomy/item-anatomy.stories.tsx +1648 -0
  1677. package/src/patterns/element-anatomy/item-anatomy.tsx +147 -56
  1678. package/src/patterns/element-anatomy/truncated-text.spec.md +68 -0
  1679. package/src/patterns/element-anatomy/truncated-text.tsx +61 -0
  1680. package/src/patterns/header-canonical/chrome-header.tsx +26 -6
  1681. package/src/patterns/header-canonical/header-canonical.css +3 -4
  1682. package/src/patterns/header-canonical/header-canonical.spec.md +96 -42
  1683. package/src/patterns/header-canonical/header-canonical.stories.tsx +257 -0
  1684. package/src/patterns/header-canonical/index.ts +5 -0
  1685. package/src/patterns/horizontal-overflow/horizontal-overflow.spec.md +36 -15
  1686. package/src/patterns/horizontal-overflow/horizontal-overflow.stories.tsx +139 -0
  1687. package/src/patterns/horizontal-overflow/horizontal-overflow.tsx +1 -1
  1688. package/src/patterns/horizontal-overflow/index.ts +5 -0
  1689. package/src/patterns/overlay-surface/index.ts +5 -0
  1690. package/src/patterns/overlay-surface/overlay-surface.spec.md +94 -58
  1691. package/src/patterns/overlay-surface/overlay-surface.stories.tsx +132 -0
  1692. package/src/patterns/overlay-surface/overlay-surface.tsx +37 -18
  1693. package/src/patterns/resize-handle/index.ts +5 -0
  1694. package/src/patterns/resize-handle/resize-handle.spec.md +22 -13
  1695. package/src/patterns/resize-handle/resize-handle.stories.tsx +73 -0
  1696. package/src/patterns/resize-handle/resize-handle.tsx +13 -18
  1697. package/src/story-governance/category-matrix.json +132 -0
  1698. package/src/story-governance/category-matrix.ts +55 -0
  1699. package/src/styles/base.css +99 -0
  1700. package/src/styles/tokens.css +14 -0
  1701. package/src/tokens/README.md +10 -5
  1702. package/src/tokens/categorical-color.ts +164 -0
  1703. package/src/tokens/color/color.spec.md +95 -72
  1704. package/src/tokens/color/color.stories.tsx +403 -0
  1705. package/src/tokens/color/primitives.css +23 -7
  1706. package/src/tokens/color/semantic.css +75 -21
  1707. package/src/tokens/density/density.spec.md +33 -30
  1708. package/src/tokens/density/density.stories.tsx +254 -0
  1709. package/src/tokens/elevation/elevation.spec.md +11 -14
  1710. package/src/tokens/elevation/elevation.stories.tsx +99 -0
  1711. package/src/tokens/layoutSpace/layoutSpace.css +1 -1
  1712. package/src/tokens/layoutSpace/layoutSpace.spec.md +44 -31
  1713. package/src/tokens/layoutSpace/layoutSpace.stories.tsx +76 -0
  1714. package/src/tokens/motion/motion.css +49 -20
  1715. package/src/tokens/motion/motion.spec.md +45 -21
  1716. package/src/tokens/motion/motion.stories.tsx +196 -0
  1717. package/src/tokens/motion/motion.ts +8 -7
  1718. package/src/tokens/motion/overlay-motion.ts +26 -0
  1719. package/src/tokens/opacity/opacity.spec.md +15 -20
  1720. package/src/tokens/opacity/opacity.stories.tsx +66 -0
  1721. package/src/tokens/orphan-tokens.spec.md +22 -11
  1722. package/src/tokens/radius/radius.spec.md +14 -11
  1723. package/src/tokens/radius/radius.stories.tsx +87 -0
  1724. package/src/tokens/token-system.spec.md +241 -0
  1725. package/src/tokens/typography/typography.css +1 -1
  1726. package/src/tokens/typography/typography.spec.md +12 -8
  1727. package/src/tokens/typography/typography.stories.tsx +134 -0
  1728. package/src/tokens/uiSize/icon-size.ts +3 -4
  1729. package/src/tokens/uiSize/uiSize.css +60 -1
  1730. package/src/tokens/uiSize/uiSize.spec.md +61 -32
  1731. package/src/tokens/uiSize/uiSize.stories.tsx +106 -0
  1732. package/src/tokens/utility-registry.json +426 -0
  1733. package/src/tokens/utility-registry.schema.json +409 -0
  1734. package/tools/README.md +9 -0
  1735. package/tools/codemods/cli.mjs +93 -0
  1736. package/tools/codemods/index.mjs +582 -0
  1737. package/tools/shared/safe-filesystem.mjs +371 -0
  1738. package/tools/visual-baseline/cli.mjs +97 -0
  1739. package/tools/visual-baseline/index.mjs +522 -0
  1740. package/dist/components/NameCard/name-card.d.ts.map +0 -1
  1741. package/dist/components/NameCard/name-card.js.map +0 -1
  1742. package/ds-canonical/commands/gov-status.md +0 -79
  1743. package/ds-canonical/hooks/check_app_shell_primary_header_consistency.sh +0 -68
  1744. package/ds-canonical/hooks/check_chrome_header_handcraft.sh +0 -70
  1745. package/ds-canonical/hooks/check_header_with_tabs_border.sh +0 -87
  1746. package/ds-canonical/hooks/check_tab_lg_chrome_header_equal.sh +0 -66
  1747. package/ds-canonical/hooks/tests/KNOWN-BROKEN.md +0 -15
  1748. package/ds-canonical/hooks/tests/test_check_story_category.sh.broken +0 -187
  1749. package/ds-canonical/references/audit-coverage-vs-24-checklist.md +0 -74
  1750. package/ds-canonical/skills/design-system-audit/references/spec-template.md +0 -66
  1751. package/src/components/NameCard/name-card.spec.md +0 -171
@@ -0,0 +1,3163 @@
1
+ #!/usr/bin/env node
2
+ // Provider-neutral consumer hard gate. Read-only by construction: no install, repair, refresh,
3
+ // registry lookup, or generated-file write. Native Claude/Codex hooks are optional accelerators;
4
+ // this check binds all provider views to the same immutable release BOM and npm payload.
5
+
6
+ import { createHash } from 'node:crypto'
7
+ import { spawnSync } from 'node:child_process'
8
+ import {
9
+ chmodSync,
10
+ closeSync,
11
+ constants as fsConstants,
12
+ existsSync,
13
+ fstatSync,
14
+ fsyncSync,
15
+ lstatSync,
16
+ mkdirSync,
17
+ mkdtempSync,
18
+ openSync,
19
+ readFileSync,
20
+ readdirSync,
21
+ readlinkSync,
22
+ realpathSync,
23
+ rmSync,
24
+ writeFileSync,
25
+ } from 'node:fs'
26
+ import { dirname, resolve, join, relative, isAbsolute, sep } from 'node:path'
27
+ import { fileURLToPath, pathToFileURL } from 'node:url'
28
+ import { TextDecoder } from 'node:util'
29
+ import {
30
+ assertClosedGitLocalConfiguration,
31
+ materializeClosedHookToolProfile,
32
+ runClosedGit,
33
+ } from './lib/closed-tool-execution.mjs'
34
+ // This checker is also shipped as the standalone `consumer/governance-check.mjs` corpus entry,
35
+ // where repository managed files have not necessarily been materialized yet. Keep the exact
36
+ // comparator dependency-free at this bootstrap boundary.
37
+ const compareUtf8Bytes = (left, right) => Buffer.compare(
38
+ Buffer.from(String(left), 'utf8'),
39
+ Buffer.from(String(right), 'utf8'),
40
+ )
41
+ const strictUtf8 = new TextDecoder('utf-8', { fatal: true })
42
+ const maxReplaySourceBytes = 16 * 1024 * 1024
43
+ const maxReplayChildOutputBytes = 1024 * 1024
44
+ const replayChildTimeoutMs = 30000
45
+ const replayChildMaxAttempts = 2
46
+
47
+ const args = process.argv.slice(2)
48
+ const valueOf = (flag) => {
49
+ const i = args.indexOf(flag)
50
+ return i >= 0 ? args[i + 1] : undefined
51
+ }
52
+ const repo = resolve(valueOf('--repo') || process.cwd())
53
+ const asJson = args.includes('--json')
54
+ const canonicalOutputRoots = new Set()
55
+ const canonicalReplayExcludedRoots = new Set()
56
+ const canonicalReplayExcludedFiles = new Set()
57
+ const discoveryTraversalExcludedRoots = new Set()
58
+ const exactSemver = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z][0-9A-Za-z.-]*)?(?:\+[0-9A-Za-z][0-9A-Za-z.-]*)?$/
59
+ const stableVersionAtLeast = (value, minimum) => {
60
+ const parse = version => typeof version === 'string'
61
+ ? version.match(/^(\d+)\.(\d+)\.(\d+)(?:\+[0-9A-Za-z.-]+)?$/)?.slice(1, 4).map(Number)
62
+ : null
63
+ const observed = parse(value)
64
+ const required = parse(minimum)
65
+ if (!observed || !required) return false
66
+ for (let index = 0; index < 3; index += 1) {
67
+ if (observed[index] !== required[index]) return observed[index] > required[index]
68
+ }
69
+ return true
70
+ }
71
+ const pathsOverlap = (left, right) => left === right || left.startsWith(`${right}/`) || right.startsWith(`${left}/`)
72
+ const canonicalManagedRepositoryPath = (value) => (
73
+ typeof value === 'string'
74
+ && value.length > 0
75
+ && value === value.normalize('NFC')
76
+ && !value.includes('\\')
77
+ && !value.includes('\0')
78
+ && !isAbsolute(value)
79
+ && value.split('/').every((segment) => segment && segment !== '.' && segment !== '..')
80
+ && value.split('/')[0] !== '.git'
81
+ )
82
+ const portablePathKey = (value) => String(value).split('/')
83
+ .map((segment) => segment.normalize('NFC').toLocaleLowerCase('en-US').normalize('NFC'))
84
+ .join('/')
85
+ const diagnostics = []
86
+ const evidence = {}
87
+ const add = (ruleId, severity, message, detail = undefined) => diagnostics.push({ ruleId, severity, message, ...(detail ? { evidence: detail } : {}) })
88
+ const sha256 = (content) => createHash('sha256').update(content).digest('hex')
89
+ const fileSha = (path) => sha256(readFileSync(path))
90
+ const sameStableFileIdentity = (left, right) => (
91
+ left.dev === right.dev
92
+ && left.ino === right.ino
93
+ && left.mode === right.mode
94
+ && left.nlink === right.nlink
95
+ && left.size === right.size
96
+ && left.mtimeNs === right.mtimeNs
97
+ && left.ctimeNs === right.ctimeNs
98
+ )
99
+ const captureStableRegularFile = (path, maxBytes, label) => {
100
+ let descriptor
101
+ try {
102
+ const beforePath = lstatSync(path, { bigint: true })
103
+ if (
104
+ !beforePath.isFile()
105
+ || beforePath.isSymbolicLink()
106
+ || beforePath.nlink !== 1n
107
+ || beforePath.size > BigInt(maxBytes)
108
+ || realpathSync(path) !== resolve(path)
109
+ ) throw new Error(`${label} is not one bounded single-link regular file`)
110
+ descriptor = openSync(
111
+ path,
112
+ fsConstants.O_RDONLY
113
+ | (fsConstants.O_CLOEXEC ?? 0)
114
+ | (fsConstants.O_NOFOLLOW ?? 0),
115
+ )
116
+ const before = fstatSync(descriptor, { bigint: true })
117
+ if (!sameStableFileIdentity(beforePath, before)) throw new Error(`${label} changed before open`)
118
+ const bytes = readFileSync(descriptor)
119
+ const after = fstatSync(descriptor, { bigint: true })
120
+ const afterPath = lstatSync(path, { bigint: true })
121
+ if (
122
+ !sameStableFileIdentity(before, after)
123
+ || !sameStableFileIdentity(after, afterPath)
124
+ || BigInt(bytes.length) !== before.size
125
+ ) throw new Error(`${label} changed while captured`)
126
+ return Object.freeze({ bytes, info: after, sha256: sha256(bytes) })
127
+ } finally {
128
+ if (descriptor !== undefined) closeSync(descriptor)
129
+ }
130
+ }
131
+ const pathEntryExists = (path) => {
132
+ try { lstatSync(path); return true }
133
+ catch (error) {
134
+ if (error?.code === 'ENOENT') return false
135
+ throw error
136
+ }
137
+ }
138
+ const permissionDenied = (error) => error?.code === 'EPERM' || error?.code === 'EACCES'
139
+ // Local provider sandboxes legitimately deny credential-class paths (for example ./.env.*) that
140
+ // the repository may track as inert templates (.env.example). A denied metadata/content read is
141
+ // evidence about the runtime, not about the repository: record it as a deterministic WARNING and
142
+ // a distinct inventory row so a degraded local attestation can never collide with the fully
143
+ // readable CI hard-gate attestation, instead of crashing the whole check.
144
+ const sandboxDeniedPaths = new Set()
145
+ const noteSandboxDenied = (path) => {
146
+ const rel = relative(repo, resolve(path)).replaceAll('\\', '/')
147
+ if (!sandboxDeniedPaths.has(rel)) {
148
+ sandboxDeniedPaths.add(rel)
149
+ add('GOV-SANDBOX-001', 'WARNING', `path is unreadable in this sandboxed runtime; full verification defers to the unsandboxed CI hard gate:${rel}`)
150
+ }
151
+ return rel
152
+ }
153
+ const lstatIfReadable = (path) => {
154
+ try { return lstatSync(path) }
155
+ catch (error) {
156
+ if (!permissionDenied(error)) throw error
157
+ noteSandboxDenied(path)
158
+ return null
159
+ }
160
+ }
161
+ const readdirIfReadable = (path, options) => {
162
+ try { return readdirSync(path, options) }
163
+ catch (error) {
164
+ if (!permissionDenied(error)) throw error
165
+ noteSandboxDenied(path)
166
+ return null
167
+ }
168
+ }
169
+ const unsafePathReports = new Set()
170
+ const repositoryRootSafe = pathEntryExists(repo) && !lstatSync(repo).isSymbolicLink() && lstatSync(repo).isDirectory()
171
+ if (pathEntryExists(join(repo, 'npm-shrinkwrap.json'))) {
172
+ add(
173
+ 'GOV-LOCK-001',
174
+ 'BLOCKER',
175
+ 'root npm-shrinkwrap.json is forbidden; authenticated package-lock.json is the sole dependency lock authority',
176
+ )
177
+ }
178
+ const repositoryPathIsSafe = (path) => {
179
+ if (!repositoryRootSafe) return false
180
+ const absolute = resolve(path)
181
+ const rel = relative(repo, absolute)
182
+ if (rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {
183
+ if (!unsafePathReports.has(absolute)) add('GOV-SYMLINK-001', 'BLOCKER', `governance path escapes repository:${absolute}`)
184
+ unsafePathReports.add(absolute)
185
+ return false
186
+ }
187
+ let cursor = repo
188
+ for (const part of rel.split(sep).filter(Boolean)) {
189
+ cursor = join(cursor, part)
190
+ if (!pathEntryExists(cursor)) return true
191
+ if (lstatSync(cursor).isSymbolicLink()) {
192
+ if (!unsafePathReports.has(cursor)) add('GOV-SYMLINK-001', 'BLOCKER', `governance path contains symlink component:${relative(repo, cursor)}`)
193
+ unsafePathReports.add(cursor)
194
+ return false
195
+ }
196
+ }
197
+ return true
198
+ }
199
+ if (!repositoryRootSafe) {
200
+ add('GOV-SYMLINK-001', 'BLOCKER', 'repository root must be a real non-symlink directory')
201
+ }
202
+ let closedToolProfile = null
203
+ if (repositoryRootSafe) {
204
+ try {
205
+ closedToolProfile = materializeClosedHookToolProfile({ repoRoot: repo })
206
+ closedToolProfile.verify()
207
+ } catch (error) {
208
+ add('GOV-RUNTIME-001', 'BLOCKER', 'closed governance tool profile is unavailable', error.message)
209
+ }
210
+ }
211
+ let closedRepositoryGitReady = false
212
+ if (repositoryRootSafe) {
213
+ try {
214
+ const topLevel = runClosedGit(['rev-parse', '--show-toplevel'], {
215
+ cwd: repo,
216
+ maxOutputBytes: 1024 * 1024,
217
+ timeoutMs: 3000,
218
+ })
219
+ if (topLevel.status === 0 && typeof topLevel.stdout === 'string' && topLevel.stdout.trim()) {
220
+ const observedTopLevel = realpathSync(resolve(topLevel.stdout.trim()))
221
+ if (observedTopLevel !== repo) {
222
+ add('GOV-INVENTORY-001', 'BLOCKER', 'consumer repository must be the exact closed Git top-level')
223
+ } else {
224
+ assertClosedGitLocalConfiguration(repo)
225
+ closedRepositoryGitReady = true
226
+ }
227
+ }
228
+ } catch (error) {
229
+ add('GOV-INVENTORY-001', 'BLOCKER', 'consumer repository Git configuration is unsafe', error.message)
230
+ }
231
+ }
232
+ let externalGovernanceRoot = null
233
+ const externalGovernancePathIsSafe = (path) => {
234
+ if (!externalGovernanceRoot) return false
235
+ const absolute = resolve(path)
236
+ const rel = relative(externalGovernanceRoot, absolute)
237
+ if (rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel)) return false
238
+ let cursor = externalGovernanceRoot
239
+ for (const part of rel.split(sep).filter(Boolean)) {
240
+ cursor = join(cursor, part)
241
+ if (!pathEntryExists(cursor)) return true
242
+ if (lstatSync(cursor).isSymbolicLink()) return false
243
+ }
244
+ return true
245
+ }
246
+ const governancePathIsSafe = path => {
247
+ const absolute = resolve(path)
248
+ const rel = relative(repo, absolute)
249
+ const contained = rel === '' || (rel !== '..' && !rel.startsWith(`..${sep}`) && !isAbsolute(rel))
250
+ return contained ? repositoryPathIsSafe(absolute) : externalGovernancePathIsSafe(absolute)
251
+ }
252
+ const isRegularFile = (path) => {
253
+ if (!governancePathIsSafe(path) || !pathEntryExists(path)) return false
254
+ const info = lstatSync(path)
255
+ return info.isFile() && !info.isSymbolicLink() && info.nlink === 1
256
+ }
257
+ const readJson = (path, ruleId) => {
258
+ if (!isRegularFile(path)) { add(ruleId, 'BLOCKER', `${relative(repo, path)} missing or not a regular non-symlink file`); return null }
259
+ try { return JSON.parse(readFileSync(path, 'utf8')) }
260
+ catch (error) { add(ruleId, 'BLOCKER', `${relative(repo, path)} invalid JSON`, error.message); return null }
261
+ }
262
+ const treeInventory = (root, { frozenCanonicalSnapshot = false } = {}) => {
263
+ if (!existsSync(root)) return null
264
+ const rows = []
265
+ const visit = (absolute, rel = '') => {
266
+ const stat = lstatSync(absolute)
267
+ const observedMode = stat.mode & 0o777
268
+ let projectedMode = observedMode
269
+ if (frozenCanonicalSnapshot) {
270
+ if (stat.isDirectory() && observedMode === 0o500) projectedMode = 0o755
271
+ else if (stat.isFile() && observedMode === 0o400) projectedMode = 0o644
272
+ else if (stat.isFile() && observedMode === 0o500) projectedMode = 0o755
273
+ else projectedMode = -1
274
+ }
275
+ const mode = projectedMode < 0
276
+ ? `invalid-frozen-${observedMode.toString(8).padStart(4, '0')}`
277
+ : projectedMode.toString(8).padStart(4, '0')
278
+ if (stat.isSymbolicLink()) { rows.push(`link:${rel}:${mode}:${readlinkSync(absolute)}`); return }
279
+ if (stat.isDirectory()) {
280
+ rows.push(`dir:${rel || '.'}:${mode}`)
281
+ for (const name of readdirSync(absolute).sort()) visit(join(absolute, name), rel ? `${rel}/${name}` : name)
282
+ return
283
+ }
284
+ if (stat.isFile()) rows.push(`file:${rel}:${mode}:links=${stat.nlink}:${fileSha(absolute)}`)
285
+ else rows.push(`unsupported:${rel}:${mode}`)
286
+ }
287
+ visit(root)
288
+ return rows
289
+ }
290
+ const assertTreeEqual = (ruleId, label, actual, canonical) => {
291
+ if (!governancePathIsSafe(actual) || !governancePathIsSafe(canonical)) {
292
+ add(ruleId, 'BLOCKER', `${label} contains an unsafe symlink path component`)
293
+ return false
294
+ }
295
+ const isFrozenCanonicalPath = path => entrypointIsPrivateSnapshot
296
+ && (path === forkRoot || path.startsWith(`${forkRoot}${sep}`))
297
+ const actualRows = treeInventory(actual, {
298
+ frozenCanonicalSnapshot: isFrozenCanonicalPath(actual),
299
+ })
300
+ const canonicalRows = treeInventory(canonical, {
301
+ frozenCanonicalSnapshot: isFrozenCanonicalPath(canonical),
302
+ })
303
+ if (!actualRows || !canonicalRows || JSON.stringify(actualRows) !== JSON.stringify(canonicalRows)) {
304
+ add(ruleId, 'BLOCKER', `${label} is not a recursive path/type/mode/hash match of the installed canonical projection`)
305
+ return false
306
+ }
307
+ return true
308
+ }
309
+ const collectSourceFiles = (root) => {
310
+ const files = []
311
+ if (!repositoryRootSafe) return files
312
+ // Provider/generated control surfaces contain rule examples and are hash-verified separately.
313
+ // Everything else in the consumer repository is part of the exhaustive hooks-off replay.
314
+ const ignoredFiles = new Set(['scripts/governance-check.mjs'])
315
+ const visit = (dir) => {
316
+ if (!pathEntryExists(dir)) return
317
+ const entries = readdirIfReadable(dir, { withFileTypes: true })
318
+ if (!entries) return
319
+ for (const entry of entries) {
320
+ const absolute = join(dir, entry.name)
321
+ const rel = relative(root, absolute).replaceAll('\\', '/')
322
+ const info = lstatIfReadable(absolute)
323
+ if (!info) {
324
+ // A denied replay-class source would silently skip hook replay: that stays fail-closed.
325
+ if (/\.(?:[cm]?[jt]sx?|css|mdx?|html|json|sh|py)$/.test(entry.name)) {
326
+ add('GOV-CONTENT-002', 'BLOCKER', `replay-class source is unreadable in this runtime and cannot be attested:${rel}`)
327
+ }
328
+ continue
329
+ }
330
+ if (info.isSymbolicLink()) {
331
+ add('GOV-SYMLINK-001', 'BLOCKER', `repository symlink is outside the exhaustive replay contract: ${rel}`)
332
+ continue
333
+ }
334
+ if (info.isDirectory() && canonicalReplayExcludedRoots.has(resolve(absolute))) continue
335
+ if (info.isDirectory()) visit(absolute)
336
+ else if (
337
+ info.isFile()
338
+ && !canonicalReplayExcludedFiles.has(resolve(absolute))
339
+ && /\.(?:[cm]?[jt]sx?|css|mdx?|html|json|sh|py)$/.test(entry.name)
340
+ && !ignoredFiles.has(rel)
341
+ ) {
342
+ if (info.nlink !== 1) add('GOV-ALIAS-001', 'BLOCKER', `repository source file has a hard-link alias:${rel}`)
343
+ else files.push(absolute)
344
+ } else if (!info.isFile()) add('GOV-SPECIAL-001', 'BLOCKER', `repository contains an unsupported filesystem entry:${rel}`)
345
+ }
346
+ }
347
+ visit(root)
348
+ return files.sort()
349
+ }
350
+ const repositoryInventory = (root) => {
351
+ const rows = []
352
+ if (!repositoryRootSafe) return { basis: 'unsafe-repository-root', rows }
353
+ const tracked = closedRepositoryGitReady
354
+ ? runClosedGit(['ls-files', '-z', '--cached'], {
355
+ cwd: resolve(root),
356
+ maxOutputBytes: 32 * 1024 * 1024,
357
+ output: 'buffer',
358
+ timeoutMs: 5000,
359
+ })
360
+ : null
361
+ if (tracked?.status === 0) {
362
+ for (const rel of tracked.stdout.toString('utf8').split('\0').filter(Boolean).sort()) {
363
+ const absolute = join(root, rel)
364
+ let info
365
+ try { info = lstatSync(absolute) }
366
+ catch (error) {
367
+ if (error?.code === 'ENOENT') {
368
+ add('GOV-INVENTORY-001', 'BLOCKER', `tracked repository path missing:${rel}`)
369
+ } else if (permissionDenied(error)) {
370
+ // Tracked but sandbox-denied (for example .env.example under the canonical ./.env.*
371
+ // credential deny): present per the Git index, so this is never GOV-INVENTORY-001.
372
+ noteSandboxDenied(absolute)
373
+ rows.push(`${rel}:sandbox-unreadable`)
374
+ } else throw error
375
+ continue
376
+ }
377
+ if (info.isSymbolicLink()) {
378
+ add('GOV-SYMLINK-001', 'BLOCKER', `tracked symlink cannot be governance-attested:${rel}`)
379
+ rows.push(`${rel}:symlink:${readlinkSync(absolute)}`)
380
+ continue
381
+ }
382
+ if (!info.isFile()) {
383
+ add('GOV-SPECIAL-001', 'BLOCKER', `tracked repository path is not a regular file:${rel}`)
384
+ continue
385
+ }
386
+ if (info.nlink !== 1) {
387
+ add('GOV-ALIAS-001', 'BLOCKER', `tracked repository file has a hard-link alias:${rel}`)
388
+ continue
389
+ }
390
+ try {
391
+ rows.push(`${rel}:${(info.mode & 0o777).toString(8).padStart(4, '0')}:${fileSha(absolute)}`)
392
+ } catch (error) {
393
+ if (!permissionDenied(error)) throw error
394
+ noteSandboxDenied(absolute)
395
+ rows.push(`${rel}:sandbox-unreadable`)
396
+ }
397
+ }
398
+ return { basis: 'git-index', rows }
399
+ }
400
+ const visit = (dir) => {
401
+ const entries = readdirIfReadable(dir, { withFileTypes: true })
402
+ if (!entries) return
403
+ for (const entry of entries) {
404
+ const absolute = join(dir, entry.name)
405
+ const rel = relative(root, absolute).replaceAll('\\', '/')
406
+ const info = lstatIfReadable(absolute)
407
+ if (!info) {
408
+ rows.push(`${rel}:sandbox-unreadable`)
409
+ continue
410
+ }
411
+ if (info.isSymbolicLink()) {
412
+ add('GOV-SYMLINK-001', 'BLOCKER', `filesystem symlink cannot be governance-attested:${rel}`)
413
+ rows.push(`${rel}:symlink:${readlinkSync(absolute)}`)
414
+ continue
415
+ }
416
+ if (info.isDirectory() && canonicalOutputRoots.has(resolve(absolute))) continue
417
+ if (info.isDirectory()) visit(absolute)
418
+ else if (info.isFile()) {
419
+ if (info.nlink !== 1) {
420
+ add('GOV-ALIAS-001', 'BLOCKER', `filesystem repository file has a hard-link alias:${rel}`)
421
+ continue
422
+ }
423
+ try {
424
+ rows.push(`${rel}:${(info.mode & 0o777).toString(8).padStart(4, '0')}:${fileSha(absolute)}`)
425
+ } catch (error) {
426
+ if (!permissionDenied(error)) throw error
427
+ noteSandboxDenied(absolute)
428
+ rows.push(`${rel}:sandbox-unreadable`)
429
+ }
430
+ } else add('GOV-SPECIAL-001', 'BLOCKER', `filesystem repository contains an unsupported entry:${rel}`)
431
+ }
432
+ }
433
+ visit(root)
434
+ rows.sort()
435
+ return { basis: 'filesystem-clean-room', rows }
436
+ }
437
+ const commandAvailable = (command) => Boolean(closedToolProfile?.executables?.[command])
438
+
439
+ const pkgPath = join(repo, 'package.json')
440
+ const lockPath = join(repo, 'package-lock.json')
441
+ const bomPath = join(repo, 'governance/lock.json')
442
+ const pkg = readJson(pkgPath, 'GOV-PACKAGE-001')
443
+ const packageLock = readJson(lockPath, 'GOV-PACKAGE-LOCK-001')
444
+ const bom = readJson(bomPath, 'GOV-BOM-001')
445
+ const bomManagedFileNames = Object.keys(bom?.payload?.managedFiles || {}).sort()
446
+ const bomManagedPortableKeys = bomManagedFileNames.map(portablePathKey)
447
+ if (
448
+ bomManagedFileNames.some((path) => !canonicalManagedRepositoryPath(path))
449
+ || new Set(bomManagedPortableKeys).size !== bomManagedPortableKeys.length
450
+ ) {
451
+ add(
452
+ 'GOV-MANAGED-FILE-002',
453
+ 'BLOCKER',
454
+ 'immutable BOM managed-file paths must be canonical NFC repository paths and portable-case unique',
455
+ )
456
+ }
457
+
458
+ // Scan exclusions are absolute, machine-derived canonical roots rather than basename matches.
459
+ // Otherwise source-controlled paths such as apps/template/src/build/evil.ts or a nested
460
+ // node_modules/.codex/config.toml could evade both replay and discovery merely by choosing a
461
+ // generated-looking directory name. The only workspace output roots trusted here are direct
462
+ // children of the protected apps/* workspaces used by the base-owned consumer source harness.
463
+ const canonicalWorkspacePatterns = ['apps/*']
464
+ if (JSON.stringify(pkg?.workspaces) !== JSON.stringify(canonicalWorkspacePatterns)) {
465
+ add('GOV-CONTENT-001', 'BLOCKER', `consumer workspaces must equal the protected roots:${canonicalWorkspacePatterns.join(',')}`)
466
+ }
467
+ const canonicalScanRoots = [repo]
468
+ const appsRoot = join(repo, 'apps')
469
+ if (!repositoryPathIsSafe(appsRoot) || !pathEntryExists(appsRoot) || !lstatSync(appsRoot).isDirectory()) {
470
+ add('GOV-CONTENT-001', 'BLOCKER', 'canonical apps/* workspace parent is missing or unsafe')
471
+ } else {
472
+ for (const name of readdirSync(appsRoot).sort()) {
473
+ const workspace = join(appsRoot, name)
474
+ const info = lstatSync(workspace)
475
+ if (info.isSymbolicLink() || (!info.isDirectory() && !info.isFile())) {
476
+ add('GOV-CONTENT-001', 'BLOCKER', `canonical workspace parent contains an unsafe entry:apps/${name}`)
477
+ continue
478
+ }
479
+ if (info.isFile()) continue
480
+ if (!isRegularFile(join(workspace, 'package.json'))) {
481
+ add('GOV-CONTENT-001', 'BLOCKER', `canonical workspace directory has no regular package.json:apps/${name}`)
482
+ continue
483
+ }
484
+ canonicalScanRoots.push(workspace)
485
+ }
486
+ }
487
+ const generatedOutputNames = ['node_modules', 'dist', 'build', 'coverage', 'storybook-static', '.next', '.turbo', '.cache', '.vite']
488
+ canonicalOutputRoots.add(resolve(repo, '.git'))
489
+ discoveryTraversalExcludedRoots.add(resolve(repo, '.git'))
490
+ for (const scanRoot of canonicalScanRoots) {
491
+ for (const name of generatedOutputNames) {
492
+ const output = resolve(scanRoot, name)
493
+ canonicalOutputRoots.add(output)
494
+ if (name === 'node_modules') discoveryTraversalExcludedRoots.add(output)
495
+ }
496
+ }
497
+ for (const path of canonicalOutputRoots) canonicalReplayExcludedRoots.add(path)
498
+ // Provider control surfaces are excluded only after their signed manifest records have been
499
+ // closed-shape validated below. Keeping a fixed provider-name list here would silently scan a
500
+ // future adapter as product source (or silently trust an unregistered adapter root).
501
+ canonicalReplayExcludedFiles.add(resolve(repo, 'governance/lock.json'))
502
+ canonicalReplayExcludedFiles.add(resolve(repo, 'governance/lock.schema.json'))
503
+
504
+ if (bom) {
505
+ if (bom.schemaVersion !== 1 || bom.role !== 'template-consumer' || bom.generatedBy !== 'scripts/build-fork-governance.mjs') {
506
+ add('GOV-BOM-002', 'BLOCKER', 'governance lock identity/schema is not canonical')
507
+ }
508
+ evidence.bomSha256 = fileSha(bomPath)
509
+ }
510
+
511
+ const deps = { ...(pkg?.dependencies || {}), ...(pkg?.devDependencies || {}) }
512
+ for (const [name, lockKey] of [['@qijenchen/design-system', 'designSystem'], ['@qijenchen/storybook-config', 'storybookConfig']]) {
513
+ const declared = deps[name]
514
+ const expected = bom?.release?.[lockKey]
515
+ if (!exactSemver.test(declared || '')) add('GOV-VERSION-001', 'BLOCKER', `${name} must be exact semver; got ${declared || 'missing'}`)
516
+ if (expected && declared !== expected) add('GOV-VERSION-002', 'BLOCKER', `${name} declaration ${declared} != governance lock ${expected}`)
517
+ const lockEntry = packageLock?.packages?.[`node_modules/${name}`]
518
+ const locked = lockEntry?.version
519
+ if (declared && locked !== declared) add('GOV-PACKAGE-LOCK-002', 'BLOCKER', `${name} package-lock version ${locked || 'missing'} != ${declared}`)
520
+ const unscoped = name.split('/').at(-1)
521
+ const canonicalTarball = declared ? `https://registry.npmjs.org/${name}/-/${unscoped}-${declared}.tgz` : null
522
+ if (declared && lockEntry?.resolved !== canonicalTarball) add('GOV-SUPPLY-001', 'BLOCKER', `${name} resolved URL is not the canonical exact npm registry tarball`)
523
+ if (!/^sha512-[A-Za-z0-9+/]+={0,2}$/.test(lockEntry?.integrity || '')) add('GOV-SUPPLY-001', 'BLOCKER', `${name} package-lock entry lacks npm sha512 integrity`)
524
+ }
525
+
526
+ for (const [name, command] of Object.entries(bom?.payload?.requiredScripts || {})) {
527
+ if (pkg?.scripts?.[name] !== command) add('GOV-SCRIPT-001', 'BLOCKER', `required package script ${name} drifted from immutable governance BOM`)
528
+ }
529
+ for (const [file, digest] of Object.entries(bom?.payload?.managedFiles || {})) {
530
+ const absolute = join(repo, file)
531
+ if (!isRegularFile(absolute) || fileSha(absolute) !== digest) add('GOV-MANAGED-FILE-001', 'BLOCKER', `${file} drifted from immutable governance BOM or is not a regular file`)
532
+ }
533
+
534
+ const dsRoot = join(repo, 'node_modules/@qijenchen/design-system')
535
+ const sbRoot = join(repo, 'node_modules/@qijenchen/storybook-config')
536
+ const utilityRegistryPath = join(dsRoot, 'src/tokens/utility-registry.json')
537
+ const dsPkg = readJson(join(dsRoot, 'package.json'), 'GOV-DEPS-001')
538
+ const sbPkg = readJson(join(sbRoot, 'package.json'), 'GOV-DEPS-001')
539
+ if (dsPkg?.version !== deps['@qijenchen/design-system']) add('GOV-DEPS-002', 'BLOCKER', `installed design-system ${dsPkg?.version || 'missing'} != declaration ${deps['@qijenchen/design-system'] || 'missing'}`)
540
+ if (sbPkg?.version !== deps['@qijenchen/storybook-config']) add('GOV-DEPS-002', 'BLOCKER', `installed storybook-config ${sbPkg?.version || 'missing'} != declaration ${deps['@qijenchen/storybook-config'] || 'missing'}`)
541
+
542
+ const installedForkRoot = join(dsRoot, 'ds-canonical/fork')
543
+ const entrypoint = realpathSync(fileURLToPath(import.meta.url))
544
+ const entrypointForkRoot = resolve(dirname(entrypoint), '..')
545
+ const entrypointForkRelative = relative(repo, entrypointForkRoot)
546
+ const entrypointIsPrivateSnapshot = (
547
+ entrypoint === join(entrypointForkRoot, 'consumer/governance-check.mjs')
548
+ && entrypointForkRelative !== ''
549
+ && (entrypointForkRelative === '..' || entrypointForkRelative.startsWith(`..${sep}`))
550
+ && pathEntryExists(entrypointForkRoot)
551
+ && lstatSync(entrypointForkRoot).isDirectory()
552
+ && !lstatSync(entrypointForkRoot).isSymbolicLink()
553
+ && realpathSync(entrypointForkRoot) === entrypointForkRoot
554
+ )
555
+ const forkRoot = entrypointIsPrivateSnapshot ? entrypointForkRoot : installedForkRoot
556
+ if (entrypointIsPrivateSnapshot) externalGovernanceRoot = forkRoot
557
+ const corpusLockPath = join(forkRoot, 'governance.lock')
558
+ const corpusLock = readJson(corpusLockPath, 'GOV-CORPUS-001')
559
+ const manifest = readJson(join(forkRoot, 'manifest.json'), 'GOV-CORPUS-001')
560
+ const closedKeys = (value, keys) => Boolean(
561
+ value
562
+ && typeof value === 'object'
563
+ && !Array.isArray(value)
564
+ && JSON.stringify(Object.keys(value).sort()) === JSON.stringify([...keys].sort())
565
+ )
566
+ // Structural/tamper mirror only. Canonical selection and certification semantics remain owned by
567
+ // packages/governance/src/provider-review-binding.mjs. This standalone shipped checker cannot
568
+ // import a repository-relative module before its corpus lock has been authenticated.
569
+ const stableProjectionValue = (value) => {
570
+ if (Array.isArray(value)) return value.map(stableProjectionValue)
571
+ if (value && typeof value === 'object') {
572
+ return Object.fromEntries(
573
+ Object.keys(value).sort(compareUtf8Bytes).map((key) => [key, stableProjectionValue(value[key])]),
574
+ )
575
+ }
576
+ return value
577
+ }
578
+ const validReviewInvocation = (value) => (
579
+ (closedKeys(value, ['strategy']) && value.strategy === 'main-context')
580
+ || (
581
+ closedKeys(value, ['agent', 'context', 'strategy'])
582
+ && value.strategy === 'native-context-fork'
583
+ && value.context === 'fork'
584
+ && /^[a-z][a-z0-9-]*$/.test(value.agent || '')
585
+ )
586
+ )
587
+ const validIndependentReviewProjection = (value) => {
588
+ if (
589
+ !closedKeys(value, [
590
+ 'schemaVersion', 'kind', 'skill', 'repositoryRole', 'certificationContract',
591
+ 'bindings', 'providerCompatibility', 'certifications', 'projectionDigest',
592
+ ])
593
+ || value.schemaVersion !== 2
594
+ || value.kind !== 'provider-neutral-independent-review-projection'
595
+ || value.skill !== 'independent-review'
596
+ || value.repositoryRole !== 'product-consumer'
597
+ || value.certificationContract !== 'exact-provider-runtime-surface-role-target-v1'
598
+ || !Array.isArray(value.bindings)
599
+ || value.bindings.length === 0
600
+ || !Array.isArray(value.providerCompatibility)
601
+ || value.providerCompatibility.length === 0
602
+ || !Array.isArray(value.certifications)
603
+ || !/^[a-f0-9]{64}$/.test(value.projectionDigest || '')
604
+ ) return false
605
+ const bindingIds = []
606
+ for (const binding of value.bindings) {
607
+ if (
608
+ !closedKeys(binding, [
609
+ 'selfProviderId', 'selectionPolicy', 'reviewClass', 'requiredAssuranceTier',
610
+ 'requiredReasoningTier', 'requiredComputeTier', 'transport', 'invocation',
611
+ 'certificationContract', 'bindingDigest',
612
+ ])
613
+ || !/^[a-z][a-z0-9-]*$/.test(binding.selfProviderId || '')
614
+ || !/^[a-z][a-z0-9-]*-v[1-9][0-9]*$/.test(binding.selectionPolicy || '')
615
+ || binding.reviewClass !== 'tier-0-governance'
616
+ || binding.requiredAssuranceTier !== 'maximum'
617
+ || binding.requiredReasoningTier !== 'maximum'
618
+ || binding.requiredComputeTier !== 'maximum'
619
+ || binding.transport !== 'content-addressed-model-broker-exchange-v1'
620
+ || !validReviewInvocation(binding.invocation)
621
+ || binding.certificationContract !== 'exact-provider-runtime-surface-role-target-v1'
622
+ || !/^[a-f0-9]{64}$/.test(binding.bindingDigest || '')
623
+ ) return false
624
+ bindingIds.push(binding.selfProviderId)
625
+ }
626
+ const compatibilityProviderIds = []
627
+ const compatibilityRuntimeIds = []
628
+ for (const compatibility of value.providerCompatibility) {
629
+ if (
630
+ !closedKeys(compatibility, ['compatibilityProviderId', 'providerId'])
631
+ || !/^[a-z][a-z0-9-]*$/.test(compatibility.providerId || '')
632
+ || !/^[a-z][a-z0-9-]*$/.test(compatibility.compatibilityProviderId || '')
633
+ ) return false
634
+ compatibilityProviderIds.push(compatibility.providerId)
635
+ compatibilityRuntimeIds.push(compatibility.compatibilityProviderId)
636
+ }
637
+ if (
638
+ JSON.stringify(bindingIds) !== JSON.stringify([...new Set(bindingIds)].sort(compareUtf8Bytes))
639
+ || JSON.stringify(compatibilityProviderIds) !== JSON.stringify([...new Set(compatibilityProviderIds)].sort(compareUtf8Bytes))
640
+ || new Set(compatibilityRuntimeIds).size !== compatibilityRuntimeIds.length
641
+ || bindingIds.some((providerId) => !compatibilityProviderIds.includes(providerId))
642
+ ) return false
643
+ const expectedDigest = sha256(JSON.stringify(stableProjectionValue({
644
+ skill: value.skill,
645
+ repositoryRole: value.repositoryRole,
646
+ bindings: value.bindings,
647
+ providerCompatibility: value.providerCompatibility,
648
+ certifications: value.certifications,
649
+ })))
650
+ return value.projectionDigest === expectedDigest
651
+ }
652
+ const expectedManifestKeys = [
653
+ '_generated', 'schemaVersion', 'kind', 'providerRegistrySchemaVersion', 'materializers', 'discoveryPolicy',
654
+ 'providerLifecycle', 'providerAdapters', 'authorityDecision', 'independentReview', 'launchers', 'hooks', 'ciReplay', 'skills', 'commands',
655
+ 'agents', 'providerSurfaces', 'codex', 'consumer',
656
+ ]
657
+ if (
658
+ !closedKeys(manifest, expectedManifestKeys)
659
+ || manifest?.schemaVersion !== 2
660
+ || manifest?.kind !== 'provider-neutral-fork-manifest'
661
+ || manifest?.providerRegistrySchemaVersion !== 9
662
+ ) add('GOV-CORPUS-001', 'BLOCKER', 'installed fork manifest is open-shaped, unknown-versioned, or has the wrong identity')
663
+ const authorityDecision = manifest?.authorityDecision
664
+ const authorityDecisionArtifactPaths = [
665
+ authorityDecision?.classifierSource,
666
+ authorityDecision?.receiptContractSource,
667
+ authorityDecision?.runner,
668
+ ]
669
+ if (
670
+ !closedKeys(authorityDecision, [
671
+ 'schemaVersion', 'policySource', 'classifierSource', 'receiptContractSource', 'runner',
672
+ 'prepareArgv', 'verifyArgv', 'certificationClaim',
673
+ ])
674
+ || authorityDecision.schemaVersion !== 1
675
+ || authorityDecision.policySource !== 'AGENTS.md#canonical-decision-authority'
676
+ || authorityDecision.classifierSource !== 'launchers/approval-evidence.mjs'
677
+ || authorityDecision.receiptContractSource !== 'launchers/authority-decision-evidence.mjs'
678
+ || authorityDecision.runner !== 'launchers/product-hook-dispatch-runtime.mjs'
679
+ || JSON.stringify(authorityDecision.prepareArgv) !== JSON.stringify(['--authority-decision', 'prepare'])
680
+ || JSON.stringify(authorityDecision.verifyArgv) !== JSON.stringify(['--authority-decision', 'verify'])
681
+ || authorityDecision.certificationClaim !== 'structural-only-not-runtime-certified'
682
+ ) add('GOV-CORPUS-001', 'BLOCKER', 'installed authority-decision projection is open-shaped, incomplete, tampered, or has the wrong identity')
683
+ const independentReview = manifest?.independentReview
684
+ if (!validIndependentReviewProjection(independentReview)) {
685
+ add('GOV-CORPUS-001', 'BLOCKER', 'installed independent-review projection is open-shaped, incomplete, tampered, or has the wrong identity')
686
+ }
687
+ const expectedConsumerKeys = [
688
+ 'claudeMd', 'commonInstruction', 'compatibilityCategories', 'executionRuntime', 'governanceCheck', 'governanceLock',
689
+ 'governanceLockSchema', 'launcherDestination', 'managedFiles', 'materialization',
690
+ ]
691
+ if (!closedKeys(manifest?.consumer, expectedConsumerKeys)) {
692
+ add('GOV-CORPUS-001', 'BLOCKER', 'installed fork manifest consumer record is open-shaped or incomplete')
693
+ }
694
+ if (manifest?.consumer?.claudeMd !== null && typeof manifest?.consumer?.claudeMd !== 'string') {
695
+ add('GOV-CORPUS-001', 'BLOCKER', 'legacy Claude instruction compatibility alias must be a path or null')
696
+ }
697
+ const commonInstruction = manifest?.consumer?.commonInstruction
698
+ if (
699
+ !closedKeys(commonInstruction, ['artifact', 'destination', 'materializerId', 'schemaVersion'])
700
+ || commonInstruction.schemaVersion !== 1
701
+ || commonInstruction.artifact !== 'common/instruction.md'
702
+ || typeof commonInstruction.destination !== 'string'
703
+ || !commonInstruction.destination
704
+ || typeof commonInstruction.materializerId !== 'string'
705
+ || !commonInstruction.materializerId
706
+ ) add('GOV-ADAPTER-003', 'BLOCKER', 'installed common instruction record is not the closed consumer authority')
707
+ if (manifest?.codex !== null && (
708
+ !closedKeys(manifest?.codex, [
709
+ 'agentsMd', 'agentsSkills', 'compatibilityOnly', 'hookArtifact', 'hookDestination', 'hooksJson',
710
+ 'instructionDestination', 'skillArtifactRoot', 'skillDestination',
711
+ ])
712
+ || manifest.codex.compatibilityOnly !== true
713
+ || typeof manifest.codex.agentsMd !== 'string'
714
+ || typeof manifest.codex.instructionDestination !== 'string'
715
+ || !Array.isArray(manifest.codex.agentsSkills)
716
+ || !Array.isArray(manifest.codex.hooksJson)
717
+ || (manifest.codex.hookArtifact !== null && typeof manifest.codex.hookArtifact !== 'string')
718
+ || (manifest.codex.hookDestination !== null && typeof manifest.codex.hookDestination !== 'string')
719
+ || typeof manifest.codex.skillArtifactRoot !== 'string'
720
+ || typeof manifest.codex.skillDestination !== 'string'
721
+ )) {
722
+ add('GOV-CORPUS-001', 'BLOCKER', 'legacy Codex compatibility record must be a closed active-surface projection or null')
723
+ }
724
+ const providerMaterializers = manifest?.materializers
725
+ const materializerGroups = ['instructionViews', 'hookBaseTemplateContracts', 'hookViews', 'skillViews', 'treeViews']
726
+ let providerMaterializersValid = closedKeys(providerMaterializers, materializerGroups)
727
+ && materializerGroups.every((group) => Object.keys(providerMaterializers?.[group] || {}).length > 0)
728
+ if (providerMaterializersValid) {
729
+ for (const [id, materializer] of Object.entries(providerMaterializers.instructionViews)) {
730
+ const expectedOwnership = materializer?.engine === 'shared-instruction-v1'
731
+ ? 'common-authority-consumer'
732
+ : materializer?.engine === 'markdown-relative-at-import-v1'
733
+ ? 'provider-generated-projection'
734
+ : null
735
+ if (
736
+ !/^[a-z][a-z0-9-]*-v[1-9][0-9]*$/.test(id)
737
+ || !closedKeys(materializer, ['engine', 'ownershipPolicy', 'supplementPolicy'])
738
+ || !expectedOwnership
739
+ || materializer.ownershipPolicy !== expectedOwnership
740
+ || !['forbidden', 'optional', 'required'].includes(materializer.supplementPolicy)
741
+ || (materializer.engine === 'shared-instruction-v1' && materializer.supplementPolicy !== 'forbidden')
742
+ ) providerMaterializersValid = false
743
+ }
744
+ for (const [id, contract] of Object.entries(providerMaterializers.hookBaseTemplateContracts)) {
745
+ const valid = contract?.engine === 'claude-permission-policy-v1'
746
+ ? closedKeys(contract, ['engine', 'schema', 'source']) && typeof contract.source === 'string' && typeof contract.schema === 'string'
747
+ : contract?.engine === 'static-json-object-v1' && closedKeys(contract, ['engine'])
748
+ if (!/^[a-z][a-z0-9-]*-v[1-9][0-9]*$/.test(id) || !valid) providerMaterializersValid = false
749
+ }
750
+ for (const [id, materializer] of Object.entries(providerMaterializers.hookViews)) {
751
+ const idValid = /^[a-z][a-z0-9-]*-v[1-9][0-9]*$/.test(id)
752
+ const shapeValid = materializer?.engine === 'json-hook-map-v1'
753
+ ? closedKeys(materializer, ['baseTemplateContract', 'descriptionField', 'engine', 'hooksField', 'mergeStrategy'])
754
+ && typeof materializer.hooksField === 'string' && materializer.hooksField.length > 0
755
+ : materializer?.engine === 'json-hook-event-list-v1'
756
+ ? closedKeys(materializer, ['baseTemplateContract', 'descriptionField', 'engine', 'eventField', 'eventsField', 'groupsField', 'handlerArgvField', 'handlersField', 'matcherField', 'mergeStrategy'])
757
+ && ['eventField', 'eventsField', 'groupsField', 'handlerArgvField', 'handlersField', 'matcherField'].every((field) => typeof materializer[field] === 'string' && materializer[field].length > 0)
758
+ : false
759
+ const descriptionValid = materializer?.engine === 'json-hook-event-list-v1'
760
+ ? typeof materializer.descriptionField === 'string' && materializer.descriptionField.length > 0
761
+ : materializer?.descriptionField === null || (typeof materializer?.descriptionField === 'string' && materializer.descriptionField.length > 0)
762
+ const commonValid = descriptionValid
763
+ && ['merge-hook-map-into-base-v1', 'replace-document-v1'].includes(materializer?.mergeStrategy)
764
+ && (materializer.mergeStrategy === 'merge-hook-map-into-base-v1'
765
+ ? typeof materializer.baseTemplateContract === 'string' && Boolean(providerMaterializers.hookBaseTemplateContracts[materializer.baseTemplateContract])
766
+ : materializer.baseTemplateContract === null)
767
+ if (!idValid || !shapeValid || !commonValid) providerMaterializersValid = false
768
+ }
769
+ for (const [id, materializer] of Object.entries(providerMaterializers.skillViews)) {
770
+ if (
771
+ !/^[a-z][a-z0-9-]*-v[1-9][0-9]*$/.test(id)
772
+ || !closedKeys(materializer, ['engine', 'entryFile'])
773
+ || materializer.engine !== 'skill-directory-v1'
774
+ || typeof materializer.entryFile !== 'string'
775
+ || !/^[A-Za-z0-9._-]+\.md$/.test(materializer.entryFile)
776
+ ) providerMaterializersValid = false
777
+ }
778
+ for (const [id, materializer] of Object.entries(providerMaterializers.treeViews)) {
779
+ if (
780
+ !/^[a-z][a-z0-9-]*-v[1-9][0-9]*$/.test(id)
781
+ || !closedKeys(materializer, ['engine', 'transformPolicies'])
782
+ || materializer.engine !== 'closed-tree-copy-v1'
783
+ || JSON.stringify(materializer.transformPolicies) !== JSON.stringify(['byte-exact'])
784
+ ) providerMaterializersValid = false
785
+ }
786
+ }
787
+ const commonAuthorityMaterializerIds = Object.entries(providerMaterializers?.instructionViews || {})
788
+ .filter(([, materializer]) => (
789
+ materializer?.engine === 'shared-instruction-v1'
790
+ && materializer?.ownershipPolicy === 'common-authority-consumer'
791
+ ))
792
+ .map(([id]) => id)
793
+ if (commonAuthorityMaterializerIds.length !== 1) providerMaterializersValid = false
794
+ if (!providerMaterializersValid) add('GOV-PROVIDER-001', 'BLOCKER', 'installed provider materializer catalog is missing, open-shaped, or uses an unsupported engine')
795
+ const resolveSurfaceMaterializer = (group, id, label) => {
796
+ const materializer = typeof id === 'string' ? providerMaterializers?.[group]?.[id] : null
797
+ if (!materializer) add('GOV-PROVIDER-002', 'BLOCKER', `${label} references an unknown ${group} materializer:${id || '<missing>'}`)
798
+ return materializer || null
799
+ }
800
+ const discoveryPolicy = manifest?.discoveryPolicy
801
+ const discoveryPolicyKeys = [
802
+ 'schemaVersion', 'providerRootNames', 'instructionNames', 'instructionOverrideNames', 'configPaths', 'pluginRootNames',
803
+ ].sort()
804
+ const discoveryArrayKeys = discoveryPolicyKeys.filter((key) => key !== 'schemaVersion')
805
+ const discoveryPolicyShapeValid = Boolean(
806
+ discoveryPolicy
807
+ && typeof discoveryPolicy === 'object'
808
+ && !Array.isArray(discoveryPolicy)
809
+ && discoveryPolicy.schemaVersion === 1
810
+ && JSON.stringify(Object.keys(discoveryPolicy).sort()) === JSON.stringify(discoveryPolicyKeys)
811
+ )
812
+ if (!discoveryPolicyShapeValid) {
813
+ add('GOV-PROVIDER-001', 'BLOCKER', 'installed manifest discoveryPolicy is missing, unknown-versioned, or open-shaped')
814
+ }
815
+ const discoveryInventory = Object.fromEntries(discoveryArrayKeys.map((key) => {
816
+ const values = discoveryPolicyShapeValid && Array.isArray(discoveryPolicy[key]) ? discoveryPolicy[key] : []
817
+ const expected = [...new Set(values.filter((value) => typeof value === 'string'))].sort()
818
+ if (!values.length || JSON.stringify(values) !== JSON.stringify(expected)) {
819
+ add('GOV-PROVIDER-001', 'BLOCKER', `installed discoveryPolicy.${key} must be a nonempty sorted unique string array`)
820
+ }
821
+ return [key, expected]
822
+ }))
823
+ const discoveryRootPattern = /^\.(?!git(?:hub)?$)[A-Za-z0-9][A-Za-z0-9_-]*$/
824
+ const discoveryInstructionPattern = /^[A-Za-z0-9][A-Za-z0-9_.@-]*\.md$/
825
+ const discoveryConfigPattern = /^\.(?:[A-Za-z0-9][A-Za-z0-9_.@-]*|[A-Za-z0-9][A-Za-z0-9_-]*\/[A-Za-z0-9_.@/-]+)$/
826
+ for (const root of [...discoveryInventory.providerRootNames, ...discoveryInventory.pluginRootNames]) {
827
+ if (!discoveryRootPattern.test(root)) add('GOV-PROVIDER-001', 'BLOCKER', `installed discovery policy has an unsafe provider/plugin root:${root}`)
828
+ }
829
+ for (const name of [...discoveryInventory.instructionNames, ...discoveryInventory.instructionOverrideNames]) {
830
+ if (!discoveryInstructionPattern.test(name)) add('GOV-PROVIDER-001', 'BLOCKER', `installed discovery policy has an unsafe instruction name:${name}`)
831
+ }
832
+ for (const path of discoveryInventory.configPaths) {
833
+ if (!discoveryConfigPattern.test(path) || path.split('/').some((segment) => !segment || segment === '.' || segment === '..')) {
834
+ add('GOV-PROVIDER-001', 'BLOCKER', `installed discovery policy has an unsafe config path:${path}`)
835
+ }
836
+ }
837
+ if (discoveryPolicyShapeValid && bom?.payload?.discoveryPolicySha256 !== sha256(JSON.stringify(discoveryPolicy))) {
838
+ add('GOV-BOM-004', 'BLOCKER', 'installed discovery policy digest differs from the immutable governance BOM')
839
+ }
840
+ const providerLifecycle = manifest?.providerLifecycle
841
+ const lifecycleSha256 = /^[a-f0-9]{64}$/
842
+ const lifecycleProviderId = /^[a-z][a-z0-9-]*$/
843
+ const lifecyclePathKey = (value) => String(value).split('/')
844
+ .map((segment) => segment.normalize('NFC').toLocaleLowerCase('en-US').normalize('NFC'))
845
+ .join('/')
846
+ const lifecyclePathsOverlap = (left, right) => {
847
+ const leftKey = lifecyclePathKey(left)
848
+ const rightKey = lifecyclePathKey(right)
849
+ return leftKey === rightKey || leftKey.startsWith(`${rightKey}/`) || rightKey.startsWith(`${leftKey}/`)
850
+ }
851
+ const lifecyclePathValid = (value) => (
852
+ typeof value === 'string'
853
+ && value.length > 0
854
+ && !value.includes('\\')
855
+ && !value.includes('\0')
856
+ && !isAbsolute(value)
857
+ && value.split('/').every((segment) => segment && segment !== '.' && segment !== '..')
858
+ && !['.git', '.github'].includes(lifecyclePathKey(value.split('/')[0]))
859
+ )
860
+ const lifecycleSemverParts = (value) => {
861
+ const match = String(value || '').match(/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/)
862
+ return match ? { core: match.slice(1, 4).map(Number), pre: match[4]?.split('.') ?? null } : null
863
+ }
864
+ const compareLifecycleSemver = (leftValue, rightValue) => {
865
+ const left = lifecycleSemverParts(leftValue)
866
+ const right = lifecycleSemverParts(rightValue)
867
+ if (!left || !right) return null
868
+ for (let index = 0; index < 3; index += 1) if (left.core[index] !== right.core[index]) return left.core[index] - right.core[index]
869
+ if (left.pre === null || right.pre === null) return left.pre === right.pre ? 0 : left.pre === null ? 1 : -1
870
+ for (let index = 0; index < Math.max(left.pre.length, right.pre.length); index += 1) {
871
+ if (left.pre[index] === undefined || right.pre[index] === undefined) return left.pre[index] === undefined ? -1 : 1
872
+ if (left.pre[index] === right.pre[index]) continue
873
+ const leftNumeric = /^\d+$/.test(left.pre[index])
874
+ const rightNumeric = /^\d+$/.test(right.pre[index])
875
+ if (leftNumeric && rightNumeric) return Number(left.pre[index]) - Number(right.pre[index])
876
+ if (leftNumeric !== rightNumeric) return leftNumeric ? -1 : 1
877
+ return compareUtf8Bytes(left.pre[index], right.pre[index])
878
+ }
879
+ return 0
880
+ }
881
+ const lifecycleProblem = (message) => add('GOV-LIFECYCLE-001', 'BLOCKER', message)
882
+ const lifecycleDiscoveryReservesPath = (discovery, value) => {
883
+ const key = lifecyclePathKey(value)
884
+ const roots = [...(discovery?.providerRootNames || []), ...(discovery?.pluginRootNames || [])]
885
+ .map(lifecyclePathKey)
886
+ if (roots.some((root) => key === root || key.startsWith(`${root}/`))) return true
887
+ return [
888
+ ...(discovery?.instructionNames || []),
889
+ ...(discovery?.instructionOverrideNames || []),
890
+ ...(discovery?.configPaths || []),
891
+ ].some((entry) => lifecyclePathKey(entry) === key)
892
+ }
893
+ const validateLifecycleDiscovery = (value, label) => {
894
+ let valid = closedKeys(value, discoveryArrayKeys)
895
+ if (!valid) {
896
+ lifecycleProblem(`${label} discovery is open-shaped`)
897
+ return false
898
+ }
899
+ for (const key of discoveryArrayKeys) {
900
+ const entries = value[key]
901
+ const pattern = key === 'configPaths'
902
+ ? discoveryConfigPattern
903
+ : ['instructionNames', 'instructionOverrideNames'].includes(key)
904
+ ? discoveryInstructionPattern
905
+ : discoveryRootPattern
906
+ const canonical = Array.isArray(entries)
907
+ ? [...new Set(entries.filter((entry) => typeof entry === 'string'))].sort()
908
+ : []
909
+ const portableEntries = Array.isArray(entries) ? entries.map(lifecyclePathKey) : []
910
+ if (
911
+ !Array.isArray(entries)
912
+ || JSON.stringify(entries) !== JSON.stringify(canonical)
913
+ || portableEntries.length !== new Set(portableEntries).size
914
+ || entries.some((entry) => !pattern.test(entry))
915
+ ) {
916
+ lifecycleProblem(`${label} discovery ${key} must be a safe sorted unique string array`)
917
+ valid = false
918
+ continue
919
+ }
920
+ if (entries.some((entry) => !discoveryInventory[key].includes(entry))) {
921
+ lifecycleProblem(`${label} discovery ${key} exceeds the signed discovery policy`)
922
+ valid = false
923
+ }
924
+ }
925
+ return valid
926
+ }
927
+ const validateLifecycleSurface = (surface, label, retired = false) => {
928
+ const keys = retired ? ['kind', 'path', 'sha256'] : ['kind', 'path']
929
+ const valid = closedKeys(surface, keys)
930
+ && ['file', 'tree'].includes(surface.kind)
931
+ && lifecyclePathValid(surface.path)
932
+ && (!retired || lifecycleSha256.test(surface.sha256 || ''))
933
+ if (!valid) lifecycleProblem(`${label} is open-shaped or invalid`)
934
+ return valid
935
+ }
936
+ const validateSnapshotRetirement = (retirement, label) => {
937
+ const transfersPresent = Object.hasOwn(retirement || {}, 'transfers')
938
+ const keys = [
939
+ 'discovery', 'id', 'reasonCode', 'replacementProviderId', 'retiredInVersion', 'surfaces',
940
+ ...(transfersPresent ? ['transfers'] : []),
941
+ ]
942
+ let valid = closedKeys(retirement, keys)
943
+ && lifecycleProviderId.test(retirement.id || '')
944
+ && (retirement.replacementProviderId === null || lifecycleProviderId.test(retirement.replacementProviderId || ''))
945
+ && retirement.replacementProviderId !== retirement.id
946
+ && exactSemver.test(retirement.retiredInVersion || '')
947
+ && /^[A-Z][A-Z0-9_]*$/.test(retirement.reasonCode || '')
948
+ && Array.isArray(retirement.surfaces)
949
+ && (!transfersPresent || (Array.isArray(retirement.transfers) && retirement.transfers.length > 0))
950
+ if (!valid) {
951
+ lifecycleProblem(`${label} is open-shaped or invalid`)
952
+ return false
953
+ }
954
+ valid = validateLifecycleDiscovery(retirement.discovery, `${label}.${retirement.id}`) && valid
955
+ const transfers = retirement.transfers || []
956
+ for (const [index, surface] of retirement.surfaces.entries()) {
957
+ valid = validateLifecycleSurface(surface, `${label}.surfaces[${index}]`, true) && valid
958
+ }
959
+ for (const [index, surface] of transfers.entries()) {
960
+ valid = validateLifecycleSurface(surface, `${label}.transfers[${index}]`) && valid
961
+ }
962
+ if (!retirement.surfaces.length && !transfers.length) {
963
+ lifecycleProblem(`${label} must delete or transfer at least one surface`)
964
+ valid = false
965
+ }
966
+ if (retirement.replacementProviderId === null && transfers.length) {
967
+ lifecycleProblem(`${label} cannot transfer surfaces without a replacement provider`)
968
+ valid = false
969
+ }
970
+ const sortedRetired = [...retirement.surfaces].sort((left, right) => compareUtf8Bytes(left?.path, right?.path))
971
+ const sortedTransfers = [...transfers].sort((left, right) => compareUtf8Bytes(left?.path, right?.path))
972
+ if (JSON.stringify(retirement.surfaces) !== JSON.stringify(sortedRetired) || JSON.stringify(transfers) !== JSON.stringify(sortedTransfers)) {
973
+ lifecycleProblem(`${label} deletion/transfer surfaces must be sorted by path`)
974
+ valid = false
975
+ }
976
+ const partition = [...retirement.surfaces, ...transfers]
977
+ if (partition.length !== new Set(partition.map((surface) => lifecyclePathKey(surface?.path))).size) {
978
+ lifecycleProblem(`${label} deletion/transfer partition contains duplicate paths`)
979
+ valid = false
980
+ }
981
+ return valid
982
+ }
983
+ const validateLifecycleSnapshot = (snapshot, label) => {
984
+ let valid = closedKeys(snapshot, ['previousSnapshotSha256', 'providers', 'releaseVersion', 'retiredProviders'])
985
+ && exactSemver.test(snapshot?.releaseVersion || '')
986
+ && (snapshot?.previousSnapshotSha256 === null || lifecycleSha256.test(snapshot?.previousSnapshotSha256 || ''))
987
+ && Array.isArray(snapshot?.providers)
988
+ && Array.isArray(snapshot?.retiredProviders)
989
+ if (!valid) {
990
+ lifecycleProblem(`${label} is missing, open-shaped, or invalid`)
991
+ return false
992
+ }
993
+ const providerIds = []
994
+ const globalClaims = []
995
+ for (const [providerIndex, provider] of snapshot.providers.entries()) {
996
+ const providerLabel = `${label}.providers[${providerIndex}]`
997
+ let providerValid = closedKeys(provider, ['discovery', 'id', 'surfaces'])
998
+ && lifecycleProviderId.test(provider?.id || '')
999
+ && Array.isArray(provider?.surfaces)
1000
+ && provider.surfaces.length > 0
1001
+ if (!providerValid) {
1002
+ lifecycleProblem(`${providerLabel} is open-shaped or invalid`)
1003
+ valid = false
1004
+ continue
1005
+ }
1006
+ providerValid = validateLifecycleDiscovery(provider.discovery, `${providerLabel}.${provider.id}`) && providerValid
1007
+ for (const [surfaceIndex, surface] of provider.surfaces.entries()) {
1008
+ providerValid = validateLifecycleSurface(surface, `${providerLabel}.surfaces[${surfaceIndex}]`) && providerValid
1009
+ if (surface?.path) globalClaims.push({ ...surface, providerId: provider.id })
1010
+ }
1011
+ const sortedSurfaces = [...provider.surfaces].sort((left, right) => compareUtf8Bytes(left?.path, right?.path))
1012
+ if (JSON.stringify(provider.surfaces) !== JSON.stringify(sortedSurfaces)) {
1013
+ lifecycleProblem(`${providerLabel}.surfaces must be sorted by path`)
1014
+ providerValid = false
1015
+ }
1016
+ for (let left = 0; left < provider.surfaces.length; left += 1) for (let right = left + 1; right < provider.surfaces.length; right += 1) {
1017
+ if (lifecyclePathsOverlap(provider.surfaces[left]?.path, provider.surfaces[right]?.path)) {
1018
+ lifecycleProblem(`${providerLabel} has overlapping managed surfaces`)
1019
+ providerValid = false
1020
+ }
1021
+ }
1022
+ providerIds.push(provider.id)
1023
+ valid = providerValid && valid
1024
+ }
1025
+ if (JSON.stringify(providerIds) !== JSON.stringify([...new Set(providerIds)].sort())) {
1026
+ lifecycleProblem(`${label} provider ids must be sorted and unique`)
1027
+ valid = false
1028
+ }
1029
+ for (let left = 0; left < globalClaims.length; left += 1) for (let right = left + 1; right < globalClaims.length; right += 1) {
1030
+ if (globalClaims[left].providerId !== globalClaims[right].providerId && lifecyclePathsOverlap(globalClaims[left].path, globalClaims[right].path)) {
1031
+ lifecycleProblem(`${label} has cross-provider managed surface overlap`)
1032
+ valid = false
1033
+ }
1034
+ }
1035
+ for (const [index, retirement] of snapshot.retiredProviders.entries()) {
1036
+ valid = validateSnapshotRetirement(retirement, `${label}.retiredProviders[${index}]`) && valid
1037
+ }
1038
+ const retiredIds = snapshot.retiredProviders.map((retirement) => retirement?.id)
1039
+ if (
1040
+ JSON.stringify(retiredIds) !== JSON.stringify([...new Set(retiredIds)].sort())
1041
+ || providerIds.some((providerId) => retiredIds.includes(providerId))
1042
+ ) {
1043
+ lifecycleProblem(`${label} retired provider ids must be sorted, unique, and inactive`)
1044
+ valid = false
1045
+ }
1046
+ return valid
1047
+ }
1048
+ const lifecycleShapeValid = Boolean(
1049
+ closedKeys(providerLifecycle, [
1050
+ 'schemaVersion', 'releaseVersion', 'immutableHead', 'immutableHeadSnapshot', 'ledgerSha256', 'previousSnapshotSha256',
1051
+ 'currentSnapshot', 'currentSnapshotSha256', 'currentProviderInventorySha256', 'retiredProviders',
1052
+ ])
1053
+ && providerLifecycle.schemaVersion === 4
1054
+ && providerLifecycle.releaseVersion === bom?.release?.designSystem
1055
+ && closedKeys(providerLifecycle.immutableHead, ['releaseVersion', 'snapshotSha256', 'providerInventorySha256'])
1056
+ && exactSemver.test(providerLifecycle.immutableHead.releaseVersion || '')
1057
+ && lifecycleSha256.test(providerLifecycle.immutableHead.snapshotSha256 || '')
1058
+ && lifecycleSha256.test(providerLifecycle.immutableHead.providerInventorySha256 || '')
1059
+ && lifecycleSha256.test(providerLifecycle.ledgerSha256 || '')
1060
+ && lifecycleSha256.test(providerLifecycle.currentSnapshotSha256 || '')
1061
+ && lifecycleSha256.test(providerLifecycle.currentProviderInventorySha256 || '')
1062
+ && (providerLifecycle.previousSnapshotSha256 === null || lifecycleSha256.test(providerLifecycle.previousSnapshotSha256 || ''))
1063
+ && Array.isArray(providerLifecycle.retiredProviders)
1064
+ )
1065
+ if (!lifecycleShapeValid) lifecycleProblem('installed provider lifecycle is missing, open-shaped, or unknown-versioned')
1066
+ const currentSnapshotValid = validateLifecycleSnapshot(providerLifecycle?.currentSnapshot, 'installed current provider snapshot')
1067
+ const immutableHeadSnapshotValid = validateLifecycleSnapshot(providerLifecycle?.immutableHeadSnapshot, 'installed immutable-head provider snapshot')
1068
+ const currentSnapshot = currentSnapshotValid ? providerLifecycle.currentSnapshot : null
1069
+ const immutableHeadSnapshot = immutableHeadSnapshotValid ? providerLifecycle.immutableHeadSnapshot : null
1070
+ if (lifecycleShapeValid && currentSnapshot && immutableHeadSnapshot) {
1071
+ const currentSnapshotSha256 = sha256(JSON.stringify(currentSnapshot))
1072
+ const currentProviderInventorySha256 = sha256(JSON.stringify(currentSnapshot.providers))
1073
+ const immutableSnapshotSha256 = sha256(JSON.stringify(immutableHeadSnapshot))
1074
+ const immutableProviderInventorySha256 = sha256(JSON.stringify(immutableHeadSnapshot.providers))
1075
+ if (
1076
+ currentSnapshotSha256 !== providerLifecycle.currentSnapshotSha256
1077
+ || currentProviderInventorySha256 !== providerLifecycle.currentProviderInventorySha256
1078
+ ) lifecycleProblem('installed current provider snapshot body does not match its authenticated hashes')
1079
+ if (
1080
+ currentSnapshot.releaseVersion !== providerLifecycle.releaseVersion
1081
+ || currentSnapshot.previousSnapshotSha256 !== providerLifecycle.previousSnapshotSha256
1082
+ ) lifecycleProblem('installed current provider snapshot release/previous pointer differs from its lifecycle envelope')
1083
+ if (
1084
+ immutableSnapshotSha256 !== providerLifecycle.immutableHead.snapshotSha256
1085
+ || immutableProviderInventorySha256 !== providerLifecycle.immutableHead.providerInventorySha256
1086
+ || immutableHeadSnapshot.releaseVersion !== providerLifecycle.immutableHead.releaseVersion
1087
+ ) lifecycleProblem('installed immutable-head snapshot body does not match its authenticated head')
1088
+
1089
+ const projectedRetirements = providerLifecycle.retiredProviders.map((retirement) => {
1090
+ if (!retirement || typeof retirement !== 'object' || Array.isArray(retirement)) return retirement
1091
+ const {
1092
+ priorProviderSha256: _priorProviderSha256,
1093
+ priorReleaseVersion: _priorReleaseVersion,
1094
+ priorSnapshotSha256: _priorSnapshotSha256,
1095
+ ...projection
1096
+ } = retirement
1097
+ return projection
1098
+ })
1099
+ if (JSON.stringify(projectedRetirements) !== JSON.stringify(currentSnapshot.retiredProviders)) {
1100
+ lifecycleProblem('installed authenticated retirement proofs do not exactly project the current snapshot retirement ledger')
1101
+ }
1102
+
1103
+ if (currentSnapshot.previousSnapshotSha256 === null) {
1104
+ if (
1105
+ JSON.stringify(currentSnapshot) !== JSON.stringify(immutableHeadSnapshot)
1106
+ || currentSnapshotSha256 !== immutableSnapshotSha256
1107
+ || currentSnapshot.retiredProviders.length !== 0
1108
+ ) lifecycleProblem('provider lifecycle genesis must use one identical current/immutable snapshot without retirements')
1109
+ } else {
1110
+ if (currentSnapshot.previousSnapshotSha256 !== immutableSnapshotSha256) {
1111
+ lifecycleProblem('current provider snapshot does not bind the embedded immutable-head snapshot')
1112
+ }
1113
+ if ((compareLifecycleSemver(currentSnapshot.releaseVersion, immutableHeadSnapshot.releaseVersion) ?? 0) <= 0) {
1114
+ lifecycleProblem('current provider snapshot release must increase after the immutable-head release')
1115
+ }
1116
+ const priorRetirements = immutableHeadSnapshot.retiredProviders
1117
+ if (
1118
+ currentSnapshot.retiredProviders.length < priorRetirements.length
1119
+ || JSON.stringify(currentSnapshot.retiredProviders.slice(0, priorRetirements.length)) !== JSON.stringify(priorRetirements)
1120
+ ) lifecycleProblem('provider snapshot retirement history is not append-only')
1121
+ const previousById = new Map(immutableHeadSnapshot.providers.map((provider) => [provider.id, provider]))
1122
+ const currentById = new Map(currentSnapshot.providers.map((provider) => [provider.id, provider]))
1123
+ for (const [providerId, previousProvider] of previousById) {
1124
+ const nextProvider = currentById.get(providerId)
1125
+ if (!nextProvider) continue
1126
+ const nextSurfaceKinds = new Map(nextProvider.surfaces.map((surface) => [surface.path, surface.kind]))
1127
+ if (previousProvider.surfaces.some((surface) => nextSurfaceKinds.get(surface.path) !== surface.kind)) {
1128
+ lifecycleProblem(`${currentSnapshot.releaseVersion}:${providerId} removed or changed a managed surface without retirement`)
1129
+ }
1130
+ const previousSurfacePaths = new Set(previousProvider.surfaces.map((surface) => lifecyclePathKey(surface.path)))
1131
+ if (nextProvider.surfaces.some((surface) => (
1132
+ !previousSurfacePaths.has(lifecyclePathKey(surface.path))
1133
+ && !lifecycleDiscoveryReservesPath(previousProvider.discovery, surface.path)
1134
+ ))) lifecycleProblem(`${currentSnapshot.releaseVersion}:${providerId} added a managed surface outside its immutable discovery authority`)
1135
+ for (const key of discoveryArrayKeys) {
1136
+ const nextDiscovery = new Set(nextProvider.discovery[key].map(lifecyclePathKey))
1137
+ if (previousProvider.discovery[key].some((entry) => !nextDiscovery.has(lifecyclePathKey(entry)))) {
1138
+ lifecycleProblem(`${currentSnapshot.releaseVersion}:${providerId} removed immutable discovery authority ${key}`)
1139
+ }
1140
+ const previousDiscovery = new Set(previousProvider.discovery[key].map(lifecyclePathKey))
1141
+ if (nextProvider.discovery[key].some((entry) => (
1142
+ !previousDiscovery.has(lifecyclePathKey(entry))
1143
+ && !lifecycleDiscoveryReservesPath(previousProvider.discovery, entry)
1144
+ ))) lifecycleProblem(`${currentSnapshot.releaseVersion}:${providerId} added discovery authority outside its immutable namespace ${key}`)
1145
+ }
1146
+ }
1147
+ const removedProviderIds = [...previousById.keys()].filter((providerId) => !currentById.has(providerId)).sort()
1148
+ const appendedRetirements = currentSnapshot.retiredProviders.slice(priorRetirements.length)
1149
+ if (JSON.stringify(appendedRetirements.map((retirement) => retirement.id).sort()) !== JSON.stringify(removedProviderIds)) {
1150
+ lifecycleProblem('provider removals do not exactly match appended snapshot retirements')
1151
+ }
1152
+ const authenticatedById = new Map(providerLifecycle.retiredProviders.map((retirement) => [retirement?.id, retirement]))
1153
+ const currentActiveClaims = currentSnapshot.providers.flatMap((provider) => provider.surfaces.map((surface) => ({
1154
+ ...surface,
1155
+ providerId: provider.id,
1156
+ })))
1157
+ for (const retirement of appendedRetirements) {
1158
+ const priorProvider = previousById.get(retirement.id)
1159
+ const authenticated = authenticatedById.get(retirement.id)
1160
+ const partition = [
1161
+ ...retirement.surfaces.map(({ path, kind }) => ({ path, kind })),
1162
+ ...(retirement.transfers || []).map(({ path, kind }) => ({ path, kind })),
1163
+ ].sort((left, right) => compareUtf8Bytes(left.path, right.path))
1164
+ if (
1165
+ !priorProvider
1166
+ || JSON.stringify(retirement.discovery) !== JSON.stringify(priorProvider.discovery)
1167
+ || JSON.stringify(partition) !== JSON.stringify(priorProvider.surfaces)
1168
+ || retirement.retiredInVersion !== currentSnapshot.releaseVersion
1169
+ ) lifecycleProblem(`${retirement.id}:retirement does not exactly project its immutable prior provider`)
1170
+ if (
1171
+ !authenticated
1172
+ || authenticated.priorReleaseVersion !== immutableHeadSnapshot.releaseVersion
1173
+ || authenticated.priorSnapshotSha256 !== immutableSnapshotSha256
1174
+ || !priorProvider
1175
+ || authenticated.priorProviderSha256 !== sha256(JSON.stringify(priorProvider))
1176
+ ) lifecycleProblem(`${retirement.id}:authenticated retirement origin does not bind the embedded immutable head`)
1177
+ const replacement = retirement.replacementProviderId === null
1178
+ ? null
1179
+ : currentById.get(retirement.replacementProviderId)
1180
+ if (retirement.replacementProviderId !== null && (!replacement || replacement.id === retirement.id)) {
1181
+ lifecycleProblem(`${retirement.id}:replacement provider is not a distinct active current provider`)
1182
+ }
1183
+ for (const surface of retirement.surfaces) {
1184
+ if (currentActiveClaims.some((candidate) => lifecyclePathsOverlap(candidate.path, surface.path))) {
1185
+ lifecycleProblem(`${retirement.id}:deletion tombstone remains owned by an active provider`)
1186
+ }
1187
+ }
1188
+ for (const surface of retirement.transfers || []) {
1189
+ if (!replacement?.surfaces.some((candidate) => candidate.path === surface.path && candidate.kind === surface.kind)) {
1190
+ lifecycleProblem(`${retirement.id}:transferred surface is not exactly owned by its replacement provider`)
1191
+ }
1192
+ if (currentActiveClaims.some((candidate) => candidate.providerId !== replacement?.id && lifecyclePathsOverlap(candidate.path, surface.path))) {
1193
+ lifecycleProblem(`${retirement.id}:transferred surface is also owned by a non-replacement provider`)
1194
+ }
1195
+ }
1196
+ }
1197
+ }
1198
+ }
1199
+ if (lifecycleShapeValid && bom?.payload?.providerLifecycleSha256 !== sha256(JSON.stringify(providerLifecycle))) {
1200
+ add('GOV-BOM-004', 'BLOCKER', 'installed provider lifecycle digest differs from the immutable governance BOM')
1201
+ }
1202
+ const shippedManagedFiles = manifest?.consumer?.managedFiles || {}
1203
+ const expectedManagedNames = bomManagedFileNames
1204
+ if (JSON.stringify(Object.keys(shippedManagedFiles).sort()) !== JSON.stringify(expectedManagedNames)) {
1205
+ add('GOV-MANAGED-FILE-002', 'BLOCKER', 'installed managed-file body map does not exactly match the immutable BOM')
1206
+ }
1207
+ for (const [destination, digest] of Object.entries(bom?.payload?.managedFiles || {})) {
1208
+ const artifact = shippedManagedFiles[destination]
1209
+ const absolute = typeof artifact === 'string' ? resolve(forkRoot, artifact) : ''
1210
+ const artifactRelative = absolute ? relative(forkRoot, absolute).replaceAll('\\', '/') : ''
1211
+ if (
1212
+ !artifact
1213
+ || artifactRelative.startsWith('../')
1214
+ || !artifactRelative.startsWith('consumer/managed-files/')
1215
+ || !isRegularFile(absolute)
1216
+ || fileSha(absolute) !== digest
1217
+ ) add('GOV-MANAGED-FILE-002', 'BLOCKER', `installed managed body missing or unbound: ${destination}`)
1218
+ }
1219
+ const shippedBomPath = join(forkRoot, manifest?.consumer?.governanceLock || 'consumer/lock.json')
1220
+ const shippedBomSchemaPath = join(forkRoot, manifest?.consumer?.governanceLockSchema || 'consumer/lock.schema.json')
1221
+ const committedBomSchemaPath = join(repo, 'governance/lock.schema.json')
1222
+ if (!existsSync(shippedBomPath) || !existsSync(bomPath) || fileSha(shippedBomPath) !== fileSha(bomPath)) {
1223
+ add('GOV-BOM-003', 'BLOCKER', 'committed governance lock is not byte-identical to the installed immutable release lock')
1224
+ }
1225
+ if (!existsSync(shippedBomSchemaPath) || !existsSync(committedBomSchemaPath) || fileSha(shippedBomSchemaPath) !== fileSha(committedBomSchemaPath)) {
1226
+ add('GOV-BOM-003', 'BLOCKER', 'committed governance lock schema is not byte-identical to the installed immutable schema')
1227
+ }
1228
+ const expectedBomKeys = ['$schema', 'authority', 'generatedBy', 'payload', 'providers', 'release', 'role', 'schemaVersion', 'upgradeProtocol', 'upgradeTrust']
1229
+ const expectedPayloadKeys = [
1230
+ 'commonInstructionSha256', 'mergeSurfaceDigests', 'providerArtifactDigests',
1231
+ 'discoveryPolicySha256', 'providerLifecycleSha256', 'providerInventorySha256', 'providerMaterializersSha256',
1232
+ 'executionRuntimeSha256',
1233
+ 'forkCorpusLockSha256', 'governanceCheckSha256', 'managedFiles', 'requiredScripts',
1234
+ 'sharedSkillCount', 'sharedSkillsSha256', 'utilityRegistrySha256',
1235
+ ].sort()
1236
+ if (
1237
+ !bom || JSON.stringify(Object.keys(bom).sort()) !== JSON.stringify(expectedBomKeys.sort())
1238
+ || JSON.stringify(Object.keys(bom?.payload || {}).sort()) !== JSON.stringify(expectedPayloadKeys)
1239
+ ) add('GOV-BOM-004', 'BLOCKER', 'governance lock does not satisfy the closed canonical shape')
1240
+ if (!isRegularFile(utilityRegistryPath) || bom?.payload?.utilityRegistrySha256 !== fileSha(utilityRegistryPath)) {
1241
+ add('GOV-UTILITY-REGISTRY-001', 'BLOCKER', 'installed canonical utility registry is missing, aliased, or differs from the immutable governance BOM')
1242
+ }
1243
+ const executionRuntime = manifest?.consumer?.executionRuntime
1244
+ const expectedExecutionRuntimeKeys = [
1245
+ 'engine', 'exactNpmVersion', 'minimumNodeVersion', 'nativeWindowsPolicy', 'requiredExecutables', 'requiredPathClasses', 'schemaVersion',
1246
+ 'supportedHostPlatforms', 'windowsCompatibilityEnvironments',
1247
+ ].sort()
1248
+ const executionRuntimeValid = Boolean(
1249
+ closedKeys(executionRuntime, expectedExecutionRuntimeKeys)
1250
+ && executionRuntime.schemaVersion === 2
1251
+ && executionRuntime.engine === 'node-posix-toolchain-v2'
1252
+ && /^\d+\.\d+\.\d+$/.test(executionRuntime.minimumNodeVersion || '')
1253
+ && /^\d+\.\d+\.\d+$/.test(executionRuntime.exactNpmVersion || '')
1254
+ && executionRuntime.nativeWindowsPolicy === 'unsupported-fail-closed'
1255
+ && JSON.stringify(executionRuntime.supportedHostPlatforms) === JSON.stringify(['darwin', 'linux'])
1256
+ && JSON.stringify(executionRuntime.windowsCompatibilityEnvironments) === JSON.stringify(['devcontainer-linux', 'wsl2-linux'])
1257
+ && JSON.stringify(executionRuntime.requiredExecutables) === JSON.stringify(['bash', 'git', 'jq', 'node', 'perl', 'python3'])
1258
+ && JSON.stringify(executionRuntime.requiredPathClasses) === JSON.stringify(['plain', 'space-containing'])
1259
+ )
1260
+ if (!executionRuntimeValid) add('GOV-RUNTIME-001', 'BLOCKER', 'installed consumer execution runtime contract is missing, open-shaped, or unsupported')
1261
+ if (executionRuntimeValid && bom?.payload?.executionRuntimeSha256 !== sha256(JSON.stringify(executionRuntime))) {
1262
+ add('GOV-BOM-004', 'BLOCKER', 'installed execution runtime digest differs from the immutable governance BOM')
1263
+ }
1264
+ if (executionRuntimeValid && !executionRuntime.supportedHostPlatforms.includes(process.platform)) {
1265
+ const compatibility = process.platform === 'win32'
1266
+ ? `; use one of the declared Linux execution environments: ${executionRuntime.windowsCompatibilityEnvironments.join(', ')}`
1267
+ : ''
1268
+ add('GOV-RUNTIME-002', 'BLOCKER', `native host platform is unsupported:${process.platform}${compatibility}`)
1269
+ }
1270
+ if (executionRuntimeValid && !stableVersionAtLeast(process.versions.node, executionRuntime.minimumNodeVersion)) {
1271
+ add('GOV-RUNTIME-002', 'BLOCKER', `Node.js ${executionRuntime.minimumNodeVersion} or newer is required; received ${process.versions.node}`)
1272
+ }
1273
+ if (executionRuntimeValid) {
1274
+ const requiredNodeRange = `>=${executionRuntime.minimumNodeVersion}`
1275
+ const lockedRoot = packageLock?.packages?.['']
1276
+ const lockedNpm = packageLock?.packages?.['node_modules/npm']
1277
+ if (
1278
+ pkg?.engines?.node !== requiredNodeRange
1279
+ || pkg?.devDependencies?.npm !== executionRuntime.exactNpmVersion
1280
+ || packageLock?.lockfileVersion !== 3
1281
+ || lockedRoot?.engines?.node !== requiredNodeRange
1282
+ || lockedRoot?.devDependencies?.npm !== executionRuntime.exactNpmVersion
1283
+ || lockedNpm?.version !== executionRuntime.exactNpmVersion
1284
+ || lockedNpm?.resolved !== `https://registry.npmjs.org/npm/-/npm-${executionRuntime.exactNpmVersion}.tgz`
1285
+ || !/^sha512-[A-Za-z0-9+/]+={0,2}$/.test(lockedNpm?.integrity || '')
1286
+ ) add('GOV-RUNTIME-001', 'BLOCKER', 'package engines and exact npm lock must equal the installed execution runtime contract')
1287
+ }
1288
+ const expectedUpgradeTrust = {
1289
+ schemaVersion: 1,
1290
+ repository: 'ajenchen/design-system',
1291
+ workflow: '.github/workflows/release.yml',
1292
+ workflowRef: 'refs/heads/main',
1293
+ provenancePredicate: 'https://slsa.dev/provenance/v1',
1294
+ builderId: 'https://github.com/actions/runner/github-hosted',
1295
+ immutableRelease: true,
1296
+ releaseBomAsset: 'release-bom.json',
1297
+ upgradePackages: ['@qijenchen/design-system', '@qijenchen/storybook-config'],
1298
+ releasePackages: ['@qijenchen/design-system', '@qijenchen/governance', '@qijenchen/storybook-config'],
1299
+ }
1300
+ if (JSON.stringify(bom?.upgradeTrust) !== JSON.stringify(expectedUpgradeTrust)) {
1301
+ add('GOV-BOM-004', 'BLOCKER', 'governance lock upgradeTrust is not the closed repository/workflow/release/package policy')
1302
+ }
1303
+ const expectedUpgradeProtocolKeys = [
1304
+ 'bootstrapEvidenceContract', 'candidateCodeExecutionAllowed', 'controlPlaneUpdateEvidenceContract',
1305
+ 'controlPlaneUpdateMode', 'controlPlaneUpdatePlanner', 'implementationSha256', 'legacyEntryMode',
1306
+ 'ordinaryUpgradeMode', 'physicalPowerLossDurabilityClaimed', 'protectedBaseVerifier',
1307
+ 'protocolId', 'schemaSha256', 'schemaVersion', 'specificationSha256',
1308
+ ]
1309
+ const upgradeProtocolShapeValid = Boolean(
1310
+ closedKeys(bom?.upgradeProtocol, expectedUpgradeProtocolKeys)
1311
+ && bom.upgradeProtocol.schemaVersion === 2
1312
+ && bom.upgradeProtocol.protocolId === 'protected-base-reconstruction-v2'
1313
+ && bom.upgradeProtocol.legacyEntryMode === 'one-time-reviewed-full-snapshot-pr'
1314
+ && bom.upgradeProtocol.bootstrapEvidenceContract === 'consumer-governance-bootstrap-readback-v1'
1315
+ && bom.upgradeProtocol.controlPlaneUpdateMode === 'recurring-reviewed-full-snapshot-pr'
1316
+ && bom.upgradeProtocol.controlPlaneUpdateEvidenceContract === 'consumer-governance-control-plane-update-readback-v1'
1317
+ && bom.upgradeProtocol.controlPlaneUpdatePlanner === 'infra/governance/bin/consumerctl.mjs'
1318
+ && bom.upgradeProtocol.ordinaryUpgradeMode === 'protected-base-disposable-reconstruction'
1319
+ && bom.upgradeProtocol.protectedBaseVerifier === 'scripts/verify-upgrade-evidence.mjs'
1320
+ && bom.upgradeProtocol.candidateCodeExecutionAllowed === false
1321
+ && bom.upgradeProtocol.physicalPowerLossDurabilityClaimed === false
1322
+ && /^[a-f0-9]{64}$/.test(bom.upgradeProtocol.specificationSha256 || '')
1323
+ && /^[a-f0-9]{64}$/.test(bom.upgradeProtocol.schemaSha256 || '')
1324
+ && /^[a-f0-9]{64}$/.test(bom.upgradeProtocol.implementationSha256 || '')
1325
+ )
1326
+ if (!upgradeProtocolShapeValid) {
1327
+ add('GOV-BOM-004', 'BLOCKER', 'governance lock upgradeProtocol is not the closed protected-base reconstruction v2 specification/schema/implementation binding')
1328
+ }
1329
+ if (corpusLock && bom?.payload?.forkCorpusLockSha256 !== fileSha(corpusLockPath)) {
1330
+ add('GOV-CORPUS-002', 'BLOCKER', 'installed fork corpus lock digest does not match governance BOM')
1331
+ }
1332
+ const corpusLockShapeValid = Boolean(
1333
+ closedKeys(corpusLock, ['schemaVersion', 'kind', 'hashAlgorithm', '_purpose', 'entries'])
1334
+ && corpusLock.schemaVersion === 1
1335
+ && corpusLock.kind === 'fork-governance-corpus-lock'
1336
+ && corpusLock.hashAlgorithm === 'sha256'
1337
+ && Array.isArray(corpusLock.entries)
1338
+ )
1339
+ if (!corpusLockShapeValid) add('GOV-CORPUS-001', 'BLOCKER', 'installed fork corpus lock is open-shaped, unknown-versioned, or has the wrong identity')
1340
+ const corpusEntryFiles = []
1341
+ for (const entry of corpusLock?.entries || []) {
1342
+ if (
1343
+ !closedKeys(entry, ['schemaVersion', 'file', 'sha256', 'source', 'classification', 'destination'])
1344
+ || entry.schemaVersion !== 1
1345
+ || typeof entry.file !== 'string'
1346
+ || entry.file.includes('\\')
1347
+ || entry.file.split('/').some((segment) => !segment || segment === '.' || segment === '..')
1348
+ || !/^[a-f0-9]{64}$/.test(entry.sha256 || '')
1349
+ || ![null, 'SHIP_AS_IS', 'SHIP_REWRITTEN', 'REPLACE', 'HELPER'].includes(entry.classification)
1350
+ || (entry.source !== null && typeof entry.source !== 'string')
1351
+ || (entry.destination !== null && typeof entry.destination !== 'string')
1352
+ ) {
1353
+ add('GOV-CORPUS-001', 'BLOCKER', `installed corpus entry is open-shaped or invalid:${entry?.file || '<unknown>'}`)
1354
+ continue
1355
+ }
1356
+ corpusEntryFiles.push(entry.file)
1357
+ const file = join(forkRoot, entry.file)
1358
+ if (!isRegularFile(file)) add('GOV-CORPUS-003', 'BLOCKER', `corpus entry missing or not a regular file: ${entry.file}`)
1359
+ else if (fileSha(file) !== entry.sha256) add('GOV-CORPUS-003', 'BLOCKER', `corpus entry tampered: ${entry.file}`)
1360
+ }
1361
+ if (JSON.stringify(corpusEntryFiles) !== JSON.stringify([...new Set(corpusEntryFiles)].sort(compareUtf8Bytes))) {
1362
+ add('GOV-CORPUS-001', 'BLOCKER', 'installed corpus entries must be unique and sorted by file')
1363
+ }
1364
+ if (!corpusEntryFiles.includes('review/provider-review-binding.mjs')) {
1365
+ add('GOV-CORPUS-001', 'BLOCKER', 'installed corpus lacks the locked exact provider review resolver')
1366
+ }
1367
+ for (const artifactPath of authorityDecisionArtifactPaths) {
1368
+ const launcherName = typeof artifactPath === 'string' && artifactPath.startsWith('launchers/')
1369
+ ? artifactPath.slice('launchers/'.length)
1370
+ : null
1371
+ if (
1372
+ !launcherName
1373
+ || !Array.isArray(manifest?.launchers)
1374
+ || !manifest.launchers.includes(launcherName)
1375
+ || !corpusEntryFiles.includes(artifactPath)
1376
+ ) add('GOV-CORPUS-001', 'BLOCKER', `installed authority-decision artifact is absent from the authenticated launcher corpus:${artifactPath || '<missing>'}`)
1377
+ }
1378
+
1379
+ // Required CI executes this checker from the exact installed payload. The committed copy remains a
1380
+ // reviewed mirror, but can never become the authority by editing itself in the same pull request.
1381
+ const runningChecker = fileURLToPath(import.meta.url)
1382
+ const shippedChecker = join(forkRoot, manifest?.consumer?.governanceCheck || 'consumer/governance-check.mjs')
1383
+ const committedChecker = join(repo, 'scripts/governance-check.mjs')
1384
+ if (!isRegularFile(shippedChecker) || fileSha(runningChecker) !== fileSha(shippedChecker)) add('GOV-CHECKER-001', 'BLOCKER', 'executed checker is not the exact installed regular-file governance payload')
1385
+ if (!isRegularFile(committedChecker) || !isRegularFile(shippedChecker) || fileSha(committedChecker) !== fileSha(shippedChecker)) add('GOV-CHECKER-001', 'BLOCKER', 'committed governance checker drifted from exact installed payload')
1386
+ if (bom?.payload?.governanceCheckSha256 && isRegularFile(shippedChecker) && bom.payload.governanceCheckSha256 !== fileSha(shippedChecker)) add('GOV-CHECKER-001', 'BLOCKER', 'governance checker digest != immutable consumer BOM')
1387
+
1388
+ const providerSurfaces = manifest?.providerSurfaces
1389
+ if (!providerSurfaces || typeof providerSurfaces !== 'object' || Array.isArray(providerSurfaces)) {
1390
+ add('GOV-PROVIDER-001', 'BLOCKER', 'installed manifest has no provider-neutral providerSurfaces inventory')
1391
+ }
1392
+ if (providerSurfaces && bom?.payload?.providerInventorySha256 !== sha256(JSON.stringify(providerSurfaces))) {
1393
+ add('GOV-BOM-004', 'BLOCKER', 'installed provider surface inventory digest differs from the immutable governance BOM')
1394
+ }
1395
+ if (providerMaterializersValid && bom?.payload?.providerMaterializersSha256 !== sha256(JSON.stringify(providerMaterializers))) {
1396
+ add('GOV-BOM-004', 'BLOCKER', 'installed provider materializer catalog digest differs from the immutable governance BOM')
1397
+ }
1398
+ const providerAdapters = manifest?.providerAdapters
1399
+ if (!providerAdapters || typeof providerAdapters !== 'object' || Array.isArray(providerAdapters)) {
1400
+ add('GOV-PROVIDER-001', 'BLOCKER', 'installed manifest has no provider adapter inventory')
1401
+ }
1402
+ const expectedAdapterKeys = ['schemaVersion', 'capabilities', 'environmentMap', 'normalization', 'blockingExitCode', 'contextOutputTransport', 'stopDecisionTransport']
1403
+ for (const [providerId, adapter] of Object.entries(providerAdapters || {})) {
1404
+ if (
1405
+ !closedKeys(adapter, expectedAdapterKeys)
1406
+ || adapter.schemaVersion !== 1
1407
+ || !Number.isInteger(adapter.blockingExitCode)
1408
+ || adapter.blockingExitCode < 1
1409
+ || adapter.blockingExitCode > 255
1410
+ || !closedKeys(adapter.capabilities, ['nativeHooks', 'hookEnforcement', 'hardGate'])
1411
+ || adapter.capabilities.hardGate !== 'provider-neutral-ci-required'
1412
+ || !['native-feedback-plus-ci', 'ci-only'].includes(adapter.capabilities.hookEnforcement)
1413
+ ) add('GOV-PROVIDER-001', 'BLOCKER', `${providerId} provider adapter record is open-shaped or unknown-versioned`)
1414
+ }
1415
+ const manifestPath = (root, value, label) => {
1416
+ if (typeof value !== 'string' || !value || value.includes('\0') || isAbsolute(value)) {
1417
+ add('GOV-PROVIDER-002', 'BLOCKER', `${label} is not a repository-relative path`)
1418
+ return null
1419
+ }
1420
+ const absolute = resolve(root, value)
1421
+ const rel = relative(root, absolute)
1422
+ if (rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {
1423
+ add('GOV-PROVIDER-002', 'BLOCKER', `${label} escapes its declared root:${value}`)
1424
+ return null
1425
+ }
1426
+ return absolute
1427
+ }
1428
+ const commonInstructionMaterializer = resolveSurfaceMaterializer(
1429
+ 'instructionViews',
1430
+ commonInstruction?.materializerId,
1431
+ 'consumer.commonInstruction.materializerId',
1432
+ )
1433
+ if (
1434
+ commonInstructionMaterializer?.engine !== 'shared-instruction-v1'
1435
+ || commonInstructionMaterializer?.ownershipPolicy !== 'common-authority-consumer'
1436
+ || commonInstruction?.materializerId !== commonAuthorityMaterializerIds[0]
1437
+ ) add('GOV-ADAPTER-003', 'BLOCKER', 'common instruction materializer is not the registered common-authority consumer')
1438
+ const commonInstructionArtifact = manifestPath(forkRoot, commonInstruction?.artifact, 'common instruction artifact')
1439
+ const commonInstructionDestination = manifestPath(repo, commonInstruction?.destination, 'common instruction destination')
1440
+ if (
1441
+ !commonInstructionArtifact
1442
+ || !commonInstructionDestination
1443
+ || !isRegularFile(commonInstructionArtifact)
1444
+ || !isRegularFile(commonInstructionDestination)
1445
+ || fileSha(commonInstructionArtifact) !== fileSha(commonInstructionDestination)
1446
+ || bom?.payload?.commonInstructionSha256 !== fileSha(commonInstructionArtifact)
1447
+ ) add('GOV-ADAPTER-003', 'BLOCKER', 'common instruction is not the exact locked consumer projection')
1448
+ // Provider-native discovery roots are closed surfaces. Exact hook files and recursively verified
1449
+ // skill/launcher trees are the only leaves allowed below each generated dot-directory; otherwise a
1450
+ // sibling settings.local.json/command/skill can become an unsigned executable policy source even
1451
+ // when every canonical destination still matches its digest.
1452
+ const providerRootPlans = new Map()
1453
+ const generatedProviderDestinations = new Set()
1454
+ if (commonInstructionDestination) {
1455
+ generatedProviderDestinations.add(resolve(commonInstructionDestination))
1456
+ canonicalReplayExcludedFiles.add(resolve(commonInstructionDestination))
1457
+ }
1458
+ const providerRootFor = (absolute) => {
1459
+ const rel = relative(repo, absolute)
1460
+ if (!rel || rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel)) return null
1461
+ const first = rel.split(sep)[0]
1462
+ return first.startsWith('.') && first !== '.' && first !== '..' ? join(repo, first) : null
1463
+ }
1464
+ const registerProviderPath = (absolute, kind) => {
1465
+ if (!absolute) return
1466
+ const root = providerRootFor(absolute)
1467
+ if (!root) return
1468
+ const plan = providerRootPlans.get(root) || { files: new Set(), trees: new Set() }
1469
+ plan[kind === 'tree' ? 'trees' : 'files'].add(resolve(absolute))
1470
+ providerRootPlans.set(root, plan)
1471
+ }
1472
+ const registerProviderRootOnly = (absolute) => {
1473
+ const root = providerRootFor(absolute)
1474
+ if (root && !providerRootPlans.has(root)) providerRootPlans.set(root, { files: new Set(), trees: new Set() })
1475
+ }
1476
+ for (const root of [...discoveryInventory.providerRootNames, ...discoveryInventory.pluginRootNames]) {
1477
+ if (discoveryRootPattern.test(root)) registerProviderRootOnly(join(repo, root))
1478
+ }
1479
+ const surfaceHookConfigs = new Map()
1480
+ const installedSurfaceHookConfigs = new Map()
1481
+ const surfaceHookMaterializers = new Map()
1482
+ const surfaceSkillMaterializers = new Map()
1483
+ const surfaceNativeEventBindings = new Map()
1484
+ const generatedSurfaces = []
1485
+ const managedSurfacesByProvider = new Map()
1486
+ const expectedSurfaceKeys = [
1487
+ 'schemaVersion', 'generated', 'capabilities', 'instructionArtifact', 'instructionDestination',
1488
+ 'instructionMaterializerId', 'hookArtifact', 'hookDestination', 'hookMaterializerId', 'skillArtifactRoot', 'skillDestination',
1489
+ 'skillEntryFile', 'skillMaterializerId', 'skills', 'trees', 'managedSurfaces', 'nativeHookCoverage', 'environmentMap', 'exclusions',
1490
+ ]
1491
+ for (const [providerId, surface] of Object.entries(providerSurfaces || {}).sort(([a], [b]) => compareUtf8Bytes(a, b))) {
1492
+ if (
1493
+ !closedKeys(surface, expectedSurfaceKeys)
1494
+ || surface.schemaVersion !== 3
1495
+ || !closedKeys(surface.capabilities, ['nativeHooks', 'hookEnforcement', 'hardGate'])
1496
+ || surface.capabilities.hardGate !== 'provider-neutral-ci-required'
1497
+ || !Array.isArray(surface.skills)
1498
+ || !Array.isArray(surface.trees)
1499
+ || !Array.isArray(surface.managedSurfaces)
1500
+ || !Array.isArray(surface.exclusions)
1501
+ ) {
1502
+ add('GOV-PROVIDER-002', 'BLOCKER', `${providerId} provider surface record is open-shaped or unknown-versioned`)
1503
+ continue
1504
+ }
1505
+ if (!surface?.generated) {
1506
+ if (surface.managedSurfaces.length) add('GOV-PROVIDER-002', 'BLOCKER', `${providerId} disabled surface claims managed destinations`)
1507
+ if (surface.trees.length) add('GOV-PROVIDER-002', 'BLOCKER', `${providerId} disabled surface claims product managed trees`)
1508
+ if (!Array.isArray(surface?.exclusions) || !surface.exclusions.length) add('GOV-PROVIDER-002', 'BLOCKER', `${providerId} disabled surface has no machine-readable exclusion`)
1509
+ if (surface.instructionMaterializerId !== null || surface.hookMaterializerId !== null || surface.skillMaterializerId !== null || surface.skillEntryFile !== null) {
1510
+ add('GOV-PROVIDER-002', 'BLOCKER', `${providerId} disabled surface claims provider materializers`)
1511
+ }
1512
+ if (surface.nativeHookCoverage !== null) add('GOV-PROVIDER-002', 'BLOCKER', `${providerId} disabled surface claims native hook coverage`)
1513
+ continue
1514
+ }
1515
+ const nativeHooks = surface.capabilities.nativeHooks === true
1516
+ const nativeHookCoverage = surface.nativeHookCoverage
1517
+ const coverageCanonicalEvents = nativeHookCoverage?.canonicalEvents
1518
+ const coverageProjectedEvents = nativeHookCoverage?.projectedEvents
1519
+ const coverageProjectedNativeEvents = nativeHookCoverage?.projectedNativeEvents
1520
+ const coverageExcludedEvents = nativeHookCoverage?.excludedEvents
1521
+ const sortedUniqueStrings = (values) => Array.isArray(values)
1522
+ && values.every((value) => typeof value === 'string' && value.length > 0)
1523
+ && JSON.stringify(values) === JSON.stringify([...new Set(values)].sort())
1524
+ const coverageExcludedValid = Array.isArray(coverageExcludedEvents)
1525
+ && coverageExcludedEvents.every((record) => (
1526
+ closedKeys(record, ['authoritativeFallback', 'event', 'reasonCode'])
1527
+ && typeof record.event === 'string'
1528
+ && /^[A-Z][A-Z0-9_]*$/.test(record.reasonCode || '')
1529
+ && record.authoritativeFallback === 'immutable-governance-check-and-protected-ci'
1530
+ ))
1531
+ && JSON.stringify(coverageExcludedEvents.map((record) => record.event))
1532
+ === JSON.stringify(coverageExcludedEvents.map((record) => record.event).sort())
1533
+ const excludedCoverageEvents = coverageExcludedValid ? coverageExcludedEvents.map((record) => record.event) : []
1534
+ const coverageProjectedNativeValid = Array.isArray(coverageProjectedNativeEvents)
1535
+ && coverageProjectedNativeEvents.every((record) => (
1536
+ closedKeys(record, ['canonicalEvent', 'nativeEvent'])
1537
+ && typeof record.canonicalEvent === 'string'
1538
+ && record.canonicalEvent.length > 0
1539
+ && typeof record.nativeEvent === 'string'
1540
+ && /^[A-Za-z][A-Za-z0-9_.:@/-]*$/.test(record.nativeEvent)
1541
+ ))
1542
+ && JSON.stringify(coverageProjectedNativeEvents.map((record) => record.canonicalEvent))
1543
+ === JSON.stringify(coverageProjectedNativeEvents.map((record) => record.canonicalEvent).sort())
1544
+ const projectedNativeCanonicalEvents = coverageProjectedNativeValid ? coverageProjectedNativeEvents.map((record) => record.canonicalEvent) : []
1545
+ const projectedNativeEvents = coverageProjectedNativeValid ? coverageProjectedNativeEvents.map((record) => record.nativeEvent) : []
1546
+ if (
1547
+ !closedKeys(nativeHookCoverage, ['canonicalEvents', 'excludedEvents', 'projectedEvents', 'projectedNativeEvents', 'schemaVersion'])
1548
+ || nativeHookCoverage.schemaVersion !== 2
1549
+ || !sortedUniqueStrings(coverageCanonicalEvents)
1550
+ || !coverageCanonicalEvents.length
1551
+ || !sortedUniqueStrings(coverageProjectedEvents)
1552
+ || !coverageExcludedValid
1553
+ || !coverageProjectedNativeValid
1554
+ || JSON.stringify(projectedNativeCanonicalEvents) !== JSON.stringify(coverageProjectedEvents)
1555
+ || new Set(projectedNativeEvents).size !== projectedNativeEvents.length
1556
+ || new Set(excludedCoverageEvents).size !== excludedCoverageEvents.length
1557
+ || coverageProjectedEvents.some((event) => excludedCoverageEvents.includes(event))
1558
+ || JSON.stringify([...coverageProjectedEvents, ...excludedCoverageEvents].sort()) !== JSON.stringify(coverageCanonicalEvents)
1559
+ || (!nativeHooks && (coverageProjectedEvents.length > 0 || coverageExcludedEvents.some((record) => record.reasonCode !== 'NATIVE_HOOK_API_UNAVAILABLE')))
1560
+ || (nativeHooks && coverageExcludedEvents.some((record) => record.reasonCode !== 'EVENT_NOT_SUPPORTED'))
1561
+ ) add('GOV-PROVIDER-002', 'BLOCKER', `${providerId} native hook event coverage is open, incomplete, or lacks its authoritative fallback`)
1562
+ surfaceNativeEventBindings.set(providerId, coverageProjectedNativeValid ? coverageProjectedNativeEvents : [])
1563
+ const instructionMaterializer = resolveSurfaceMaterializer('instructionViews', surface.instructionMaterializerId, `${providerId}.instructionMaterializerId`)
1564
+ const hookMaterializer = nativeHooks
1565
+ ? resolveSurfaceMaterializer('hookViews', surface.hookMaterializerId, `${providerId}.hookMaterializerId`)
1566
+ : null
1567
+ const skillMaterializer = resolveSurfaceMaterializer('skillViews', surface.skillMaterializerId, `${providerId}.skillMaterializerId`)
1568
+ surfaceHookMaterializers.set(providerId, hookMaterializer)
1569
+ surfaceSkillMaterializers.set(providerId, skillMaterializer)
1570
+ if (skillMaterializer && surface.skillEntryFile !== skillMaterializer.entryFile) {
1571
+ add('GOV-PROVIDER-002', 'BLOCKER', `${providerId} skill entry file differs from its registered materializer`)
1572
+ }
1573
+ const canonicalManagedSurfaces = [...surface.managedSurfaces].sort((left, right) => compareUtf8Bytes(left?.path, right?.path))
1574
+ if (!surface.managedSurfaces.length || JSON.stringify(surface.managedSurfaces) !== JSON.stringify(canonicalManagedSurfaces)) {
1575
+ add('GOV-PROVIDER-002', 'BLOCKER', `${providerId} managed surfaces must be non-empty and sorted`)
1576
+ }
1577
+ const managedSurfaceKeys = new Set()
1578
+ const ownedManagedSurfaces = []
1579
+ for (const managed of surface.managedSurfaces) {
1580
+ const absolute = closedKeys(managed, ['path', 'kind']) && ['file', 'tree'].includes(managed.kind)
1581
+ ? manifestPath(repo, managed.path, `${providerId} managed surface`)
1582
+ : null
1583
+ if (!absolute || managedSurfaceKeys.has(managed.path)) {
1584
+ add('GOV-PROVIDER-002', 'BLOCKER', `${providerId} managed surface is invalid or duplicated:${managed?.path || '<unknown>'}`)
1585
+ continue
1586
+ }
1587
+ managedSurfaceKeys.add(managed.path)
1588
+ ownedManagedSurfaces.push({ path: managed.path, kind: managed.kind })
1589
+ registerProviderPath(absolute, managed.kind)
1590
+ if (managed.kind === 'tree') canonicalReplayExcludedRoots.add(resolve(absolute))
1591
+ else canonicalReplayExcludedFiles.add(resolve(absolute))
1592
+ }
1593
+ managedSurfacesByProvider.set(providerId, ownedManagedSurfaces)
1594
+ generatedSurfaces.push([providerId, surface])
1595
+ if (instructionMaterializer?.engine === 'shared-instruction-v1') {
1596
+ if (
1597
+ surface.instructionArtifact !== commonInstruction?.artifact
1598
+ || surface.instructionDestination !== commonInstruction?.destination
1599
+ || surface.instructionMaterializerId !== commonInstruction?.materializerId
1600
+ || ownedManagedSurfaces.some((managed) => commonInstruction?.destination && pathsOverlap(managed.path, commonInstruction.destination))
1601
+ ) add('GOV-PROVIDER-002', 'BLOCKER', `${providerId} shared instruction does not reference the common non-provider authority`)
1602
+ } else if (!ownedManagedSurfaces.some((managed) => managed.path === surface.instructionDestination && managed.kind === 'file')) {
1603
+ add('GOV-PROVIDER-002', 'BLOCKER', `${providerId} provider instruction projection is absent from its managed surfaces`)
1604
+ }
1605
+ const instructionArtifact = manifestPath(forkRoot, surface.instructionArtifact, `${providerId} instruction artifact`)
1606
+ const instructionDestination = manifestPath(repo, surface.instructionDestination, `${providerId} instruction destination`)
1607
+ if (instructionDestination) {
1608
+ generatedProviderDestinations.add(resolve(instructionDestination))
1609
+ registerProviderPath(instructionDestination, 'file')
1610
+ }
1611
+ if (!instructionArtifact || !instructionDestination || !isRegularFile(instructionArtifact) || !isRegularFile(instructionDestination) || fileSha(instructionArtifact) !== fileSha(instructionDestination)) {
1612
+ add('GOV-ADAPTER-002', 'BLOCKER', `${providerId} instruction is not the exact installed regular-file projection`)
1613
+ }
1614
+
1615
+ if (surface.capabilities.hookEnforcement !== (nativeHooks ? 'native-feedback-plus-ci' : 'ci-only')) {
1616
+ add('GOV-PROVIDER-002', 'BLOCKER', `${providerId} hook capability does not match its provider-neutral enforcement mode`)
1617
+ }
1618
+ const hookArtifact = nativeHooks ? manifestPath(forkRoot, surface.hookArtifact, `${providerId} hook artifact`) : null
1619
+ const hookDestination = nativeHooks ? manifestPath(repo, surface.hookDestination, `${providerId} hook destination`) : null
1620
+ if (nativeHooks && hookDestination) {
1621
+ generatedProviderDestinations.add(resolve(hookDestination))
1622
+ registerProviderPath(hookDestination, 'file')
1623
+ }
1624
+ if (!nativeHooks && (surface.hookArtifact !== null || surface.hookDestination !== null || surface.hookMaterializerId !== null)) {
1625
+ add('GOV-PROVIDER-002', 'BLOCKER', `${providerId} ci-only provider declares a native hook surface`)
1626
+ }
1627
+ if (!nativeHooks && !surface.exclusions.some((item) => item?.scope === 'native-hooks')) {
1628
+ add('GOV-PROVIDER-002', 'BLOCKER', `${providerId} ci-only provider has no explicit native-hooks exclusion`)
1629
+ }
1630
+ const canonicalHookConfig = nativeHooks && hookArtifact ? readJson(hookArtifact, 'GOV-ADAPTER-001') : null
1631
+ const installedHookConfig = nativeHooks && hookDestination ? readJson(hookDestination, 'GOV-ADAPTER-001') : null
1632
+ surfaceHookConfigs.set(providerId, canonicalHookConfig)
1633
+ installedSurfaceHookConfigs.set(providerId, installedHookConfig)
1634
+ if (nativeHooks && hookMaterializer?.mergeStrategy !== 'merge-hook-map-into-base-v1') {
1635
+ if (!hookArtifact || !hookDestination || !isRegularFile(hookArtifact) || !isRegularFile(hookDestination) || fileSha(hookArtifact) !== fileSha(hookDestination)) {
1636
+ add('GOV-ADAPTER-005', 'BLOCKER', `${providerId} hook config is not the exact installed canonical projection`)
1637
+ }
1638
+ const expectedConfigKeys = hookMaterializer?.engine === 'json-hook-map-v1'
1639
+ ? [hookMaterializer.hooksField, ...(hookMaterializer.descriptionField === null ? [] : [hookMaterializer.descriptionField])]
1640
+ : hookMaterializer?.engine === 'json-hook-event-list-v1'
1641
+ ? [hookMaterializer.eventsField, hookMaterializer.descriptionField]
1642
+ : []
1643
+ if (installedHookConfig && (!expectedConfigKeys.length || !closedKeys(installedHookConfig, expectedConfigKeys))) {
1644
+ add('GOV-ADAPTER-004', 'BLOCKER', `${providerId} hook config does not satisfy its registered closed materializer shape`)
1645
+ }
1646
+ }
1647
+
1648
+ const skillDestinationRoot = manifestPath(repo, surface.skillDestination, `${providerId} skill destination`)
1649
+ if (skillDestinationRoot) {
1650
+ generatedProviderDestinations.add(resolve(skillDestinationRoot))
1651
+ registerProviderPath(skillDestinationRoot, 'tree')
1652
+ }
1653
+ for (const name of surface.skills || []) {
1654
+ const source = manifestPath(forkRoot, `${surface.skillArtifactRoot}/${name}`, `${providerId} skill artifact ${name}`)
1655
+ const destination = manifestPath(repo, `${surface.skillDestination}/${name}`, `${providerId} skill destination ${name}`)
1656
+ if (!source || !destination) continue
1657
+ if (!isRegularFile(join(source, surface.skillEntryFile || ''))) {
1658
+ add('GOV-SKILL-003', 'BLOCKER', `${providerId} managed skill ${name} lacks its registered entry file`)
1659
+ }
1660
+ assertTreeEqual('GOV-SKILL-003', `${providerId} managed skill ${name}`, destination, source)
1661
+ }
1662
+ const validTrees = []
1663
+ for (const [index, tree] of surface.trees.entries()) {
1664
+ const label = `${providerId} product managed tree[${index}]`
1665
+ if (
1666
+ !closedKeys(tree, ['artifactRoot', 'destination', 'materializerId', 'name', 'schemaVersion', 'transformPolicy'])
1667
+ || tree.schemaVersion !== 1
1668
+ || !/^[a-z][a-z0-9-]*$/.test(tree.name || '')
1669
+ || tree.artifactRoot !== `providers/${providerId}/trees/${tree.name}`
1670
+ || typeof tree.destination !== 'string'
1671
+ || !tree.destination
1672
+ || tree.destination.includes('\\')
1673
+ || tree.destination.includes('\0')
1674
+ || isAbsolute(tree.destination)
1675
+ || tree.destination.split('/').some((segment) => !segment || segment === '.' || segment === '..')
1676
+ ) {
1677
+ add('GOV-PROVIDER-002', 'BLOCKER', `${label} record is open-shaped or invalid`)
1678
+ continue
1679
+ }
1680
+ const materializer = resolveSurfaceMaterializer('treeViews', tree.materializerId, `${label}.materializerId`)
1681
+ if (
1682
+ materializer?.engine !== 'closed-tree-copy-v1'
1683
+ || tree.transformPolicy !== 'byte-exact'
1684
+ || !materializer?.transformPolicies?.includes(tree.transformPolicy)
1685
+ ) {
1686
+ add('GOV-PROVIDER-002', 'BLOCKER', `${label} has an unsupported deterministic materializer`)
1687
+ continue
1688
+ }
1689
+ const source = manifestPath(forkRoot, tree.artifactRoot, `${label} artifact`)
1690
+ const destination = manifestPath(repo, tree.destination, `${label} destination`)
1691
+ if (!source || !destination) continue
1692
+ generatedProviderDestinations.add(resolve(destination))
1693
+ registerProviderPath(destination, 'tree')
1694
+ const lockedFiles = (corpusLock?.entries || [])
1695
+ .map((entry) => entry?.file)
1696
+ .filter((file) => typeof file === 'string' && file.startsWith(`${tree.artifactRoot}/`))
1697
+ .sort()
1698
+ const actualFiles = []
1699
+ const visitArtifact = (directory) => {
1700
+ if (!governancePathIsSafe(directory) || !pathEntryExists(directory)) return
1701
+ for (const name of readdirSync(directory).sort()) {
1702
+ const absolute = join(directory, name)
1703
+ const info = lstatSync(absolute)
1704
+ if (info.isDirectory() && !info.isSymbolicLink()) visitArtifact(absolute)
1705
+ else if (info.isFile() && !info.isSymbolicLink() && info.nlink === 1) actualFiles.push(relative(forkRoot, absolute).replaceAll('\\', '/'))
1706
+ else add('GOV-ADAPTER-005', 'BLOCKER', `${label} artifact contains an unsafe filesystem entry`)
1707
+ }
1708
+ }
1709
+ if (!pathEntryExists(source) || !lstatSync(source).isDirectory() || lstatSync(source).isSymbolicLink()) {
1710
+ add('GOV-ADAPTER-005', 'BLOCKER', `${label} artifact is missing or not a real directory`)
1711
+ } else visitArtifact(source)
1712
+ if (!actualFiles.length || JSON.stringify(actualFiles) !== JSON.stringify(lockedFiles)) {
1713
+ add('GOV-CORPUS-003', 'BLOCKER', `${label} artifact does not exactly match its corpus-lock inventory`)
1714
+ }
1715
+ assertTreeEqual('GOV-ADAPTER-005', label, destination, source)
1716
+ validTrees.push(tree)
1717
+ }
1718
+ if (
1719
+ JSON.stringify(surface.trees) !== JSON.stringify([...surface.trees].sort((left, right) => compareUtf8Bytes(left?.name, right?.name)))
1720
+ || new Set(surface.trees.map((tree) => tree?.name)).size !== surface.trees.length
1721
+ || new Set(surface.trees.map((tree) => tree?.destination)).size !== surface.trees.length
1722
+ ) add('GOV-PROVIDER-002', 'BLOCKER', `${providerId} product managed trees must be sorted and unique by name/destination`)
1723
+ const expectedManagedTrees = [surface.skillDestination, ...validTrees.map((tree) => tree.destination)].sort()
1724
+ const actualManagedTrees = ownedManagedSurfaces.filter((managed) => managed.kind === 'tree').map((managed) => managed.path).sort()
1725
+ if (JSON.stringify(actualManagedTrees) !== JSON.stringify(expectedManagedTrees)) {
1726
+ add('GOV-PROVIDER-002', 'BLOCKER', `${providerId} managed tree ownership does not exactly match skill plus product-tree artifacts`)
1727
+ }
1728
+ }
1729
+ if (manifest?.codex != null) {
1730
+ const compatibility = manifest.codex
1731
+ const matches = generatedSurfaces.filter(([, surface]) => (
1732
+ surface.instructionArtifact === compatibility.agentsMd
1733
+ && surface.instructionDestination === compatibility.instructionDestination
1734
+ && surface.hookDestination === compatibility.hookDestination
1735
+ && surface.skillDestination === compatibility.skillDestination
1736
+ && JSON.stringify(surface.skills) === JSON.stringify(compatibility.agentsSkills)
1737
+ ))
1738
+ if (matches.length !== 1) {
1739
+ add('GOV-CORPUS-001', 'BLOCKER', 'legacy Codex compatibility metadata does not bind exactly one active provider surface')
1740
+ } else {
1741
+ const [, sourceSurface] = matches[0]
1742
+ const compatibilityHook = compatibility.hookArtifact
1743
+ ? manifestPath(forkRoot, compatibility.hookArtifact, 'legacy Codex hook alias')
1744
+ : null
1745
+ const sourceHook = sourceSurface.hookArtifact
1746
+ ? manifestPath(forkRoot, sourceSurface.hookArtifact, 'legacy Codex hook source')
1747
+ : null
1748
+ if ((compatibilityHook || sourceHook) && (
1749
+ !compatibilityHook
1750
+ || !sourceHook
1751
+ || !isRegularFile(compatibilityHook)
1752
+ || !isRegularFile(sourceHook)
1753
+ || fileSha(compatibilityHook) !== fileSha(sourceHook)
1754
+ )) add('GOV-CORPUS-003', 'BLOCKER', 'legacy Codex hook alias differs from its active provider artifact')
1755
+ for (const name of compatibility.agentsSkills) {
1756
+ const alias = manifestPath(forkRoot, `${compatibility.skillArtifactRoot}/${name}`, `legacy Codex skill alias ${name}`)
1757
+ const source = manifestPath(forkRoot, `${sourceSurface.skillArtifactRoot}/${name}`, `legacy Codex skill source ${name}`)
1758
+ if (alias && source) assertTreeEqual('GOV-CORPUS-003', `legacy Codex skill alias ${name}`, alias, source)
1759
+ }
1760
+ }
1761
+ }
1762
+ const surfaceProviderIds = Object.keys(providerSurfaces || {}).sort()
1763
+ const bomProviderIds = Object.keys(bom?.providers || {}).sort()
1764
+ if (JSON.stringify(surfaceProviderIds) !== JSON.stringify(bomProviderIds)) {
1765
+ add('GOV-PROVIDER-002', 'BLOCKER', 'providerSurfaces inventory does not exactly match the immutable BOM provider inventory')
1766
+ }
1767
+ if (JSON.stringify(surfaceProviderIds) !== JSON.stringify(Object.keys(providerAdapters || {}).sort())) {
1768
+ add('GOV-PROVIDER-002', 'BLOCKER', 'providerSurfaces inventory does not exactly match the provider adapter inventory')
1769
+ }
1770
+ const providerArtifactDigests = bom?.payload?.providerArtifactDigests
1771
+ if (
1772
+ !providerArtifactDigests
1773
+ || typeof providerArtifactDigests !== 'object'
1774
+ || Array.isArray(providerArtifactDigests)
1775
+ || JSON.stringify(Object.keys(providerArtifactDigests).sort()) !== JSON.stringify(surfaceProviderIds)
1776
+ ) add('GOV-BOM-004', 'BLOCKER', 'provider artifact digest map does not exactly match the signed provider inventory')
1777
+ const lockedTreeDigest = (prefix) => sha256((corpusLock?.entries || [])
1778
+ .filter((entry) => typeof entry?.file === 'string' && entry.file.startsWith(`${prefix}/`))
1779
+ .map((entry) => `${entry.file}:${entry.sha256}`)
1780
+ .join('\n') + '\n')
1781
+ for (const [providerId, surface] of Object.entries(providerSurfaces || {})) {
1782
+ const binding = providerArtifactDigests?.[providerId]
1783
+ if (
1784
+ !closedKeys(binding, ['hookSha256', 'instructionSha256', 'skillsSha256', 'treeDigests'])
1785
+ || !closedKeys(binding?.treeDigests, (surface.trees || []).map((tree) => tree.name))
1786
+ ) {
1787
+ add('GOV-BOM-004', 'BLOCKER', `${providerId} provider artifact digest binding is open-shaped or missing`)
1788
+ continue
1789
+ }
1790
+ const expected = {
1791
+ instructionSha256: surface.instructionArtifact && isRegularFile(join(forkRoot, surface.instructionArtifact))
1792
+ ? fileSha(join(forkRoot, surface.instructionArtifact))
1793
+ : null,
1794
+ hookSha256: surface.hookArtifact && isRegularFile(join(forkRoot, surface.hookArtifact))
1795
+ ? fileSha(join(forkRoot, surface.hookArtifact))
1796
+ : null,
1797
+ skillsSha256: surface.skillArtifactRoot ? lockedTreeDigest(surface.skillArtifactRoot) : null,
1798
+ treeDigests: Object.fromEntries((surface.trees || []).map((tree) => [tree.name, lockedTreeDigest(tree.artifactRoot)])),
1799
+ }
1800
+ if (JSON.stringify(binding) !== JSON.stringify(expected)) {
1801
+ add('GOV-BOM-004', 'BLOCKER', `${providerId} provider artifact digests differ from the installed locked corpus`)
1802
+ }
1803
+ }
1804
+ const retiredProviderIds = []
1805
+ const retiredSurfacePaths = []
1806
+ const activeOwnedSurfaces = [...managedSurfacesByProvider].flatMap(([providerId, surfaces]) => (
1807
+ surfaces.map((surface) => ({ ...surface, providerId }))
1808
+ ))
1809
+ for (const retired of providerLifecycle?.retiredProviders || []) {
1810
+ const discovery = retired?.discovery
1811
+ const surfaces = retired?.surfaces
1812
+ const transfers = retired?.transfers || []
1813
+ if (
1814
+ !closedKeys(retired, [
1815
+ 'id', 'replacementProviderId', 'retiredInVersion', 'reasonCode', 'discovery', 'surfaces',
1816
+ 'priorReleaseVersion', 'priorSnapshotSha256', 'priorProviderSha256',
1817
+ ...(Object.hasOwn(retired || {}, 'transfers') ? ['transfers'] : []),
1818
+ ])
1819
+ || !/^[a-z][a-z0-9-]*$/.test(retired.id || '')
1820
+ || !(retired.replacementProviderId === null || /^[a-z][a-z0-9-]*$/.test(retired.replacementProviderId || ''))
1821
+ || !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z][0-9A-Za-z.-]*)?(?:\+[0-9A-Za-z][0-9A-Za-z.-]*)?$/.test(retired.retiredInVersion || '')
1822
+ || !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z][0-9A-Za-z.-]*)?(?:\+[0-9A-Za-z][0-9A-Za-z.-]*)?$/.test(retired.priorReleaseVersion || '')
1823
+ || !/^[a-f0-9]{64}$/.test(retired.priorSnapshotSha256 || '')
1824
+ || !/^[a-f0-9]{64}$/.test(retired.priorProviderSha256 || '')
1825
+ || (compareLifecycleSemver(retired.retiredInVersion, retired.priorReleaseVersion) ?? 0) <= 0
1826
+ || (compareLifecycleSemver(retired.retiredInVersion, providerLifecycle?.releaseVersion) ?? 1) > 0
1827
+ || !/^[A-Z][A-Z0-9_]*$/.test(retired.reasonCode || '')
1828
+ || !closedKeys(discovery, discoveryArrayKeys)
1829
+ || !Array.isArray(surfaces)
1830
+ || (Object.hasOwn(retired || {}, 'transfers') && (!Array.isArray(transfers) || !transfers.length))
1831
+ || (!surfaces?.length && !transfers.length)
1832
+ ) {
1833
+ add('GOV-LIFECYCLE-001', 'BLOCKER', `retired provider record is open-shaped or invalid:${retired?.id || '<unknown>'}`)
1834
+ continue
1835
+ }
1836
+ retiredProviderIds.push(retired.id)
1837
+ const priorSurfaces = [
1838
+ ...retired.surfaces.map((surface) => ({ path: surface?.path, kind: surface?.kind })),
1839
+ ...transfers.map((surface) => ({ path: surface?.path, kind: surface?.kind })),
1840
+ ].sort((left, right) => compareUtf8Bytes(left.path, right.path))
1841
+ if (sha256(JSON.stringify({
1842
+ id: retired.id,
1843
+ discovery: retired.discovery,
1844
+ surfaces: priorSurfaces,
1845
+ })) !== retired.priorProviderSha256) {
1846
+ add('GOV-LIFECYCLE-001', 'BLOCKER', `${retired.id} retirement proof does not bind its previous managed provider inventory`)
1847
+ }
1848
+ if (surfaceProviderIds.includes(retired.id)) add('GOV-LIFECYCLE-001', 'BLOCKER', `retired provider remains active:${retired.id}`)
1849
+ const replacementSurfaces = retired.replacementProviderId === null
1850
+ ? null
1851
+ : managedSurfacesByProvider.get(retired.replacementProviderId)
1852
+ if (retired.replacementProviderId !== null && (!replacementSurfaces || retired.replacementProviderId === retired.id)) {
1853
+ add('GOV-LIFECYCLE-001', 'BLOCKER', `replacementProviderId must name a distinct active current provider:${retired.id}:${retired.replacementProviderId}`)
1854
+ }
1855
+ if (retired.replacementProviderId === null && transfers.length) {
1856
+ add('GOV-LIFECYCLE-001', 'BLOCKER', `${retired.id} null replacementProviderId cannot transfer surfaces`)
1857
+ }
1858
+ for (const key of discoveryArrayKeys) {
1859
+ const values = discovery[key]
1860
+ if (!Array.isArray(values) || JSON.stringify(values) !== JSON.stringify([...new Set(values || [])].sort())) {
1861
+ add('GOV-LIFECYCLE-001', 'BLOCKER', `${retired.id} retired discovery ${key} must be sorted and unique`)
1862
+ continue
1863
+ }
1864
+ if (values.some((value) => !discoveryInventory[key].includes(value))) {
1865
+ add('GOV-LIFECYCLE-001', 'BLOCKER', `${retired.id} retired discovery ${key} is not reserved by the signed discovery policy`)
1866
+ }
1867
+ }
1868
+ const canonicalSurfaces = [...surfaces].sort((left, right) => compareUtf8Bytes(left?.path, right?.path))
1869
+ if (JSON.stringify(surfaces) !== JSON.stringify(canonicalSurfaces)) {
1870
+ add('GOV-LIFECYCLE-001', 'BLOCKER', `${retired.id} retired surfaces must be sorted by path`)
1871
+ }
1872
+ const canonicalTransfers = [...transfers].sort((left, right) => compareUtf8Bytes(left?.path, right?.path))
1873
+ if (JSON.stringify(transfers) !== JSON.stringify(canonicalTransfers)) {
1874
+ add('GOV-LIFECYCLE-001', 'BLOCKER', `${retired.id} transferred surfaces must be sorted by path`)
1875
+ }
1876
+ const partitionPaths = [...surfaces, ...transfers].map((surface) => surface?.path)
1877
+ if (partitionPaths.length !== new Set(partitionPaths).size) {
1878
+ add('GOV-LIFECYCLE-001', 'BLOCKER', `${retired.id} deletion/transfer partition contains a duplicate surface path`)
1879
+ }
1880
+ for (const surface of surfaces) {
1881
+ if (!closedKeys(surface, ['path', 'kind', 'sha256']) || !['file', 'tree'].includes(surface.kind) || !/^[a-f0-9]{64}$/.test(surface.sha256 || '')) {
1882
+ add('GOV-LIFECYCLE-001', 'BLOCKER', `${retired.id} retired surface is open-shaped or invalid`)
1883
+ continue
1884
+ }
1885
+ const destination = manifestPath(repo, surface.path, `${retired.id} retired surface`)
1886
+ if (!destination) continue
1887
+ if (['README.md', 'CONTRIBUTING.md', 'SECURITY.md', 'CODE_OF_CONDUCT.md', 'CHANGELOG.md', 'LICENSE.md'].includes(surface.path)) {
1888
+ add('GOV-LIFECYCLE-001', 'BLOCKER', `${retired.id} tombstone targets a protected product document:${surface.path}`)
1889
+ }
1890
+ if (commonInstruction?.destination && pathsOverlap(surface.path, commonInstruction.destination)) {
1891
+ add('GOV-LIFECYCLE-001', 'BLOCKER', `${retired.id} provider tombstone targets the common instruction authority:${surface.path}`)
1892
+ }
1893
+ retiredSurfacePaths.push(surface.path)
1894
+ if (activeOwnedSurfaces.some((candidate) => pathsOverlap(candidate.path, surface.path))) {
1895
+ add('GOV-LIFECYCLE-001', 'BLOCKER', `${retired.id} deletion tombstone remains owned by an active provider:${surface.path}`)
1896
+ }
1897
+ if (pathEntryExists(destination)) add('GOV-LIFECYCLE-001', 'BLOCKER', `retired provider surface has not converged to absent:${surface.path}`)
1898
+ }
1899
+ for (const surface of transfers) {
1900
+ if (!closedKeys(surface, ['path', 'kind']) || !['file', 'tree'].includes(surface.kind)) {
1901
+ add('GOV-LIFECYCLE-001', 'BLOCKER', `${retired.id} transferred surface is open-shaped or invalid`)
1902
+ continue
1903
+ }
1904
+ const destination = manifestPath(repo, surface.path, `${retired.id} transferred surface`)
1905
+ if (!destination) continue
1906
+ if (['README.md', 'CONTRIBUTING.md', 'SECURITY.md', 'CODE_OF_CONDUCT.md', 'CHANGELOG.md', 'LICENSE.md'].includes(surface.path)) {
1907
+ add('GOV-LIFECYCLE-001', 'BLOCKER', `${retired.id} transfer targets a protected product document:${surface.path}`)
1908
+ }
1909
+ if (!replacementSurfaces?.some((candidate) => candidate.path === surface.path && candidate.kind === surface.kind)) {
1910
+ add('GOV-LIFECYCLE-001', 'BLOCKER', `${retired.id} transfer is not exactly owned by replacement ${retired.replacementProviderId}:${surface.path}`)
1911
+ }
1912
+ if (activeOwnedSurfaces.some((candidate) => candidate.providerId !== retired.replacementProviderId && pathsOverlap(candidate.path, surface.path))) {
1913
+ add('GOV-LIFECYCLE-001', 'BLOCKER', `${retired.id} transfer is also owned by a non-replacement provider:${surface.path}`)
1914
+ }
1915
+ }
1916
+ }
1917
+ if (
1918
+ JSON.stringify(retiredProviderIds) !== JSON.stringify([...new Set(retiredProviderIds)].sort())
1919
+ || retiredSurfacePaths.length !== new Set(retiredSurfacePaths).size
1920
+ ) add('GOV-LIFECYCLE-001', 'BLOCKER', 'retired provider ids and tombstone paths must be globally unique and sorted')
1921
+ for (const [providerId, surface] of Object.entries(providerSurfaces || {})) {
1922
+ const declaration = bom?.providers?.[providerId]
1923
+ if (
1924
+ !closedKeys(declaration, ['schemaVersion', 'generated', 'instruction', 'hooks', 'skills', 'capabilities', 'exclusions'])
1925
+ || declaration.schemaVersion !== 1
1926
+ || !closedKeys(declaration.capabilities, ['nativeHooks', 'hookEnforcement', 'hardGate'])
1927
+ ) {
1928
+ add('GOV-PROVIDER-002', 'BLOCKER', `${providerId} immutable BOM provider record is open-shaped or unknown-versioned`)
1929
+ continue
1930
+ }
1931
+ if (declaration.generated !== Boolean(surface?.generated)) {
1932
+ add('GOV-PROVIDER-002', 'BLOCKER', `${providerId} generated/disabled state differs from the immutable BOM`)
1933
+ continue
1934
+ }
1935
+ if (JSON.stringify(declaration.capabilities) !== JSON.stringify(surface.capabilities)) {
1936
+ add('GOV-PROVIDER-002', 'BLOCKER', `${providerId} provider capability differs from the immutable BOM`)
1937
+ }
1938
+ const instructionName = typeof declaration.instruction === 'string' ? declaration.instruction.replaceAll('\\', '/').split('/').at(-1) : null
1939
+ if (!instructionName || !discoveryInventory.instructionNames.includes(instructionName)) {
1940
+ add('GOV-PROVIDER-002', 'BLOCKER', `${providerId} instruction is not reserved by the signed discovery policy`)
1941
+ }
1942
+ if (typeof declaration.hooks === 'string' && !discoveryInventory.configPaths.includes(declaration.hooks)) {
1943
+ add('GOV-PROVIDER-002', 'BLOCKER', `${providerId} hook config is not reserved by the signed discovery policy`)
1944
+ }
1945
+ for (const [kind, value] of [['instruction', declaration.instruction], ['hooks', declaration.hooks], ['skills', declaration.skills]]) {
1946
+ if (typeof value !== 'string') continue
1947
+ const root = value.replaceAll('\\', '/').split('/')[0]
1948
+ if (root.startsWith('.') && !discoveryInventory.providerRootNames.includes(root)) {
1949
+ add('GOV-PROVIDER-002', 'BLOCKER', `${providerId} ${kind} root ${root} is not reserved by the signed discovery policy`)
1950
+ }
1951
+ }
1952
+ if (surface.generated) {
1953
+ if (
1954
+ declaration.instruction !== surface.instructionDestination
1955
+ || declaration.hooks !== surface.hookDestination
1956
+ || declaration.skills !== surface.skillDestination
1957
+ ) add('GOV-PROVIDER-002', 'BLOCKER', `${providerId} destination binding differs from the immutable BOM`)
1958
+ continue
1959
+ }
1960
+ if (JSON.stringify(declaration.exclusions || []) !== JSON.stringify(surface.exclusions || [])) {
1961
+ add('GOV-PROVIDER-002', 'BLOCKER', `${providerId} disabled-provider exclusions differ from the immutable BOM`)
1962
+ }
1963
+ for (const [kind, value] of [['instruction', declaration.instruction], ['hook', declaration.hooks], ['skill', declaration.skills]]) {
1964
+ if (typeof value !== 'string' || !value) continue
1965
+ const destination = manifestPath(repo, value, `${providerId} disabled ${kind} destination`)
1966
+ if (!destination || generatedProviderDestinations.has(resolve(destination))) continue
1967
+ registerProviderRootOnly(destination)
1968
+ if (pathEntryExists(destination)) {
1969
+ add('GOV-EXTENSION-001', 'BLOCKER', `${providerId} is disabled but its undeclared ${kind} surface exists at ${value}`)
1970
+ }
1971
+ }
1972
+ }
1973
+ if (!generatedSurfaces.length) add('GOV-PROVIDER-001', 'BLOCKER', 'providerSurfaces has no generated provider adapter')
1974
+
1975
+ const rawManagedLaunchers = manifest?.launchers || manifest?.codex?.hooksJson || []
1976
+ const managedLaunchers = Array.isArray(rawManagedLaunchers)
1977
+ ? rawManagedLaunchers.filter((name) => typeof name === 'string' && /^[A-Za-z0-9._-]+\.(?:sh|mjs|py)$/.test(name))
1978
+ : []
1979
+ if (
1980
+ JSON.stringify(rawManagedLaunchers) !== JSON.stringify([...new Set(managedLaunchers)].sort())
1981
+ || managedLaunchers.length === 0
1982
+ ) add('GOV-CORPUS-001', 'BLOCKER', 'installed manifest launcher inventory must be non-empty, safe, unique, and sorted')
1983
+ // Claude commands and subagent definitions are package-owned discovery leaves too. Keep the
1984
+ // directory closed even while today's classified sets are empty: a later release may add an exact
1985
+ // file without requiring the checker to open the entire directory to consumer-owned content.
1986
+ const materialization = manifest?.consumer?.materialization
1987
+ const materializationExactPaths = materialization?.exactPaths
1988
+ const materializationPathPrefixes = materialization?.pathPrefixes
1989
+ if (
1990
+ !closedKeys(materialization, ['exactPaths', 'pathPrefixes'])
1991
+ || !Array.isArray(materializationExactPaths)
1992
+ || !Array.isArray(materializationPathPrefixes)
1993
+ ) add('GOV-CORPUS-001', 'BLOCKER', 'installed manifest has no closed consumer materialization inventory')
1994
+ const canonicalMaterializationPath = (value, prefix) => {
1995
+ if (
1996
+ typeof value !== 'string'
1997
+ || !value
1998
+ || value.includes('\\')
1999
+ || value.includes('\0')
2000
+ || isAbsolute(value)
2001
+ || (prefix ? !value.endsWith('/') : value.endsWith('/'))
2002
+ ) return null
2003
+ const path = prefix ? value.slice(0, -1) : value
2004
+ const segments = path.split('/')
2005
+ if (segments.some((segment) => !segment || segment === '.' || segment === '..') || segments[0] === '.git') return null
2006
+ return value
2007
+ }
2008
+ const materializationInventory = (values, prefix, label) => {
2009
+ const valid = (values || []).map((value) => canonicalMaterializationPath(value, prefix)).filter(Boolean)
2010
+ const canonical = [...new Set(valid)].sort()
2011
+ if (JSON.stringify(values || []) !== JSON.stringify(canonical)) {
2012
+ add('GOV-CORPUS-001', 'BLOCKER', `consumer materialization ${label} must be safe, unique, and sorted`)
2013
+ }
2014
+ return canonical
2015
+ }
2016
+ const exactMaterializationPaths = materializationInventory(materializationExactPaths, false, 'exactPaths')
2017
+ const prefixMaterializationPaths = materializationInventory(materializationPathPrefixes, true, 'pathPrefixes')
2018
+ const exactMaterializationSet = new Set(exactMaterializationPaths)
2019
+ const expectedProviderPrefixes = generatedSurfaces.flatMap(([, surface]) => [
2020
+ ...(surface.skills || []).map((name) => `${surface.skillDestination}/${name}/`),
2021
+ ...(surface.trees || []).map((tree) => `${tree.destination}/`),
2022
+ ]).sort()
2023
+ if (JSON.stringify(prefixMaterializationPaths) !== JSON.stringify(expectedProviderPrefixes)) {
2024
+ add('GOV-CORPUS-001', 'BLOCKER', 'consumer path-prefix materialization inventory does not exactly match provider skill and product-tree surfaces')
2025
+ }
2026
+ if (
2027
+ !commonInstruction?.destination
2028
+ || exactMaterializationPaths.filter((path) => path === commonInstruction.destination).length !== 1
2029
+ || !(corpusLock?.entries || []).some((entry) => entry?.file === commonInstruction.artifact)
2030
+ ) add('GOV-CORPUS-001', 'BLOCKER', 'common instruction must have exactly one locked artifact and one consumer destination')
2031
+ const materializationAllows = (relativePath, directory) => {
2032
+ if (exactMaterializationSet.has(relativePath) || prefixMaterializationPaths.some((prefix) => relativePath.startsWith(prefix))) return true
2033
+ if (!directory) return false
2034
+ const descendantPrefix = `${relativePath}/`
2035
+ return exactMaterializationPaths.some((path) => path.startsWith(descendantPrefix))
2036
+ || prefixMaterializationPaths.some((path) => path.startsWith(descendantPrefix))
2037
+ }
2038
+ const compatibilityCategories = manifest?.consumer?.compatibilityCategories
2039
+ const compatibilityFileSurfacesByProvider = new Map()
2040
+ if (!Array.isArray(compatibilityCategories)) {
2041
+ add('GOV-CORPUS-001', 'BLOCKER', 'installed compatibility category inventory must be an array')
2042
+ } else {
2043
+ const categoryNames = compatibilityCategories.map((category) => category?.name)
2044
+ if (JSON.stringify(categoryNames) !== JSON.stringify([...new Set(categoryNames)].sort())) {
2045
+ add('GOV-CORPUS-001', 'BLOCKER', 'installed compatibility categories must be sorted and unique by name')
2046
+ }
2047
+ for (const category of compatibilityCategories) {
2048
+ if (
2049
+ !closedKeys(category, ['artifactRoot', 'destinationRoot', 'entries', 'name', 'providerId', 'schemaVersion'])
2050
+ || category.schemaVersion !== 1
2051
+ || !['agents', 'commands'].includes(category.name)
2052
+ || !/^[a-z][a-z0-9-]*$/.test(category.providerId || '')
2053
+ || !Array.isArray(category.entries)
2054
+ ) {
2055
+ add('GOV-CORPUS-001', 'BLOCKER', 'installed compatibility category record is open-shaped or invalid')
2056
+ continue
2057
+ }
2058
+ const artifactRoot = canonicalMaterializationPath(category.artifactRoot, false)
2059
+ const destinationRoot = canonicalMaterializationPath(category.destinationRoot, false)
2060
+ const entries = category.entries.filter((name) => /^[A-Za-z0-9][A-Za-z0-9_.@-]*\.md$/.test(name))
2061
+ if (
2062
+ !artifactRoot
2063
+ || !destinationRoot
2064
+ || JSON.stringify(category.entries) !== JSON.stringify([...new Set(entries)].sort())
2065
+ || JSON.stringify(manifest?.[category.name]) !== JSON.stringify(category.entries)
2066
+ ) {
2067
+ add('GOV-CORPUS-001', 'BLOCKER', `compatibility category ${category.name} has an unsafe or divergent inventory`)
2068
+ continue
2069
+ }
2070
+ const ownerSurfaces = managedSurfacesByProvider.get(category.providerId)
2071
+ if (!ownerSurfaces) add('GOV-PROVIDER-002', 'BLOCKER', `compatibility category ${category.name} has no active provider owner`)
2072
+ for (const name of entries) {
2073
+ const artifactPath = `${artifactRoot}/${name}`
2074
+ const destinationPath = `${destinationRoot}/${name}`
2075
+ const providerFiles = compatibilityFileSurfacesByProvider.get(category.providerId) || []
2076
+ providerFiles.push({ path: destinationPath, kind: 'file' })
2077
+ compatibilityFileSurfacesByProvider.set(category.providerId, providerFiles)
2078
+ if (!(corpusLock?.entries || []).some((entry) => entry?.file === artifactPath)) {
2079
+ add('GOV-CORPUS-003', 'BLOCKER', `compatibility category artifact is absent from corpus lock:${artifactPath}`)
2080
+ }
2081
+ if (!exactMaterializationSet.has(destinationPath)) {
2082
+ add('GOV-CORPUS-001', 'BLOCKER', `compatibility category destination is absent from exact materialization:${destinationPath}`)
2083
+ }
2084
+ if (!ownerSurfaces?.some((surface) => surface.path === destinationPath && surface.kind === 'file')) {
2085
+ add('GOV-PROVIDER-002', 'BLOCKER', `compatibility category destination is absent from provider lifecycle ownership:${destinationPath}`)
2086
+ }
2087
+ const artifact = manifestPath(forkRoot, artifactPath, `compatibility category ${category.name} artifact ${name}`)
2088
+ const destination = manifestPath(repo, destinationPath, `compatibility category ${category.name} destination ${name}`)
2089
+ if (!artifact || !destination) continue
2090
+ generatedProviderDestinations.add(resolve(destination))
2091
+ registerProviderPath(destination, 'file')
2092
+ assertTreeEqual('GOV-ADAPTER-005', `compatibility category ${category.name} ${name}`, destination, artifact)
2093
+ }
2094
+ }
2095
+ for (const name of ['agents', 'commands']) if (
2096
+ !compatibilityCategories.some((category) => category.name === name)
2097
+ && (manifest?.[name] || []).length
2098
+ ) add('GOV-CORPUS-001', 'BLOCKER', `legacy ${name} inventory has no active compatibility category owner`)
2099
+ }
2100
+ const activeLifecycleProviderIds = generatedSurfaces.map(([providerId]) => providerId).sort()
2101
+ const currentSnapshotProviderIds = currentSnapshot?.providers?.map((provider) => provider.id) || []
2102
+ if (currentSnapshot && JSON.stringify(currentSnapshotProviderIds) !== JSON.stringify(activeLifecycleProviderIds)) {
2103
+ lifecycleProblem('current provider snapshot ids do not exactly match active generated provider surfaces')
2104
+ }
2105
+ const currentSnapshotProvidersById = new Map((currentSnapshot?.providers || []).map((provider) => [provider.id, provider]))
2106
+ for (const [providerId, surface] of generatedSurfaces) {
2107
+ const currentProvider = currentSnapshotProvidersById.get(providerId)
2108
+ const actualManagedSurfaces = managedSurfacesByProvider.get(providerId) || []
2109
+ if (!currentProvider || JSON.stringify(currentProvider.surfaces) !== JSON.stringify(actualManagedSurfaces)) {
2110
+ lifecycleProblem(`${providerId} current snapshot managed surfaces do not exactly match the installed provider surface inventory`)
2111
+ }
2112
+
2113
+ const instructionEngine = providerMaterializers?.instructionViews?.[surface.instructionMaterializerId]?.engine
2114
+ const expectedManagedFiles = [
2115
+ ...(instructionEngine === 'shared-instruction-v1' ? [] : [{ path: surface.instructionDestination, kind: 'file' }]),
2116
+ ...(typeof surface.hookDestination === 'string' ? [{ path: surface.hookDestination, kind: 'file' }] : []),
2117
+ ...(compatibilityFileSurfacesByProvider.get(providerId) || []),
2118
+ ].sort((left, right) => compareUtf8Bytes(left.path, right.path))
2119
+ const actualManagedFiles = actualManagedSurfaces
2120
+ .filter((managed) => managed.kind === 'file')
2121
+ .sort((left, right) => compareUtf8Bytes(left.path, right.path))
2122
+ if (JSON.stringify(actualManagedFiles) !== JSON.stringify(expectedManagedFiles)) {
2123
+ add(
2124
+ 'GOV-PROVIDER-002',
2125
+ 'BLOCKER',
2126
+ `${providerId} managed file ownership must exactly equal its non-shared instruction, hook config, and compatibility-category leaves`,
2127
+ )
2128
+ }
2129
+ }
2130
+ const mergeSurfaces = generatedSurfaces.filter(([providerId]) => surfaceHookMaterializers.get(providerId)?.mergeStrategy === 'merge-hook-map-into-base-v1')
2131
+ const mergePolicy = readJson(manifestPath(forkRoot, 'launchers/settings-hooks.json', 'provider hook merge policy'), 'GOV-ADAPTER-001')
2132
+ const mergeProviderIds = mergeSurfaces.map(([providerId]) => providerId).sort()
2133
+ const mergePolicyProviderIds = Object.keys(mergePolicy?.surfaces || {}).sort()
2134
+ const mergeDigestProviderIds = Object.keys(bom?.payload?.mergeSurfaceDigests || {}).sort()
2135
+ if (
2136
+ !closedKeys(mergePolicy, ['_generated', 'kind', 'schemaVersion', 'sourceDigest', 'surfaces'])
2137
+ || mergePolicy?._generated !== 'build-fork-governance.mjs'
2138
+ || mergePolicy?.kind !== 'provider-hook-merge-policies'
2139
+ || mergePolicy?.schemaVersion !== 1
2140
+ || !/^[a-f0-9]{64}$/.test(mergePolicy?.sourceDigest || '')
2141
+ || JSON.stringify(mergePolicyProviderIds) !== JSON.stringify(mergeProviderIds)
2142
+ || JSON.stringify(mergeDigestProviderIds) !== JSON.stringify(mergeProviderIds)
2143
+ ) add('GOV-ADAPTER-001', 'BLOCKER', 'provider hook merge policy/BOM inventory does not exactly match every merge-capable provider')
2144
+ for (const [providerId, surface] of mergeSurfaces) {
2145
+ const materializer = surfaceHookMaterializers.get(providerId)
2146
+ const baseTemplate = providerMaterializers?.hookBaseTemplateContracts?.[materializer?.baseTemplateContract]
2147
+ const policy = mergePolicy?.surfaces?.[providerId]
2148
+ if (
2149
+ materializer?.engine !== 'json-hook-map-v1'
2150
+ || !closedKeys(policy, [
2151
+ 'baseTemplateContract', 'hookArtifact', 'hookDestination', 'hookMaterializerId', 'mergeStrategy',
2152
+ 'permissionPolicy', 'retiredHookRegistrations', 'schemaVersion',
2153
+ ])
2154
+ || policy.schemaVersion !== 1
2155
+ || policy.hookArtifact !== surface.hookArtifact
2156
+ || policy.hookDestination !== surface.hookDestination
2157
+ || policy.hookMaterializerId !== surface.hookMaterializerId
2158
+ || policy.mergeStrategy !== materializer.mergeStrategy
2159
+ || policy.baseTemplateContract !== materializer.baseTemplateContract
2160
+ || !baseTemplate
2161
+ ) {
2162
+ add('GOV-ADAPTER-004', 'BLOCKER', `${providerId} template-backed hook materializer is unsupported by the closed merge runtime`)
2163
+ continue
2164
+ }
2165
+ const settings = installedSurfaceHookConfigs.get(providerId)
2166
+ const canonicalConfig = surfaceHookConfigs.get(providerId)
2167
+ const hookArtifact = manifestPath(forkRoot, surface.hookArtifact, `${providerId} canonical hook artifact`)
2168
+ if (!hookArtifact || bom?.payload?.mergeSurfaceDigests?.[providerId] !== fileSha(hookArtifact)) {
2169
+ add('GOV-ADAPTER-003', 'BLOCKER', `${providerId} canonical merge surface digest != immutable governance BOM`)
2170
+ }
2171
+ if (JSON.stringify(settings?.[materializer.hooksField] || {}) !== JSON.stringify(canonicalConfig?.[materializer.hooksField] || {})) {
2172
+ add('GOV-EXTENSION-001', 'BLOCKER', `${providerId} native hook wiring contains an undeclared executable extension; product policy belongs in governance/overlay.md`)
2173
+ }
2174
+ if (!Array.isArray(policy.retiredHookRegistrations)) {
2175
+ add('GOV-ADAPTER-004', 'BLOCKER', `${providerId} retired hook registration policy is invalid`)
2176
+ } else {
2177
+ const ids = []
2178
+ const paths = []
2179
+ for (const record of policy.retiredHookRegistrations) {
2180
+ if (
2181
+ !closedKeys(record, ['id', 'path', 'provider', 'reasonCode'])
2182
+ || record.provider !== providerId
2183
+ || !/^[a-z][a-z0-9-]*$/.test(record.id || '')
2184
+ || !/^[A-Z][A-Z0-9_]*$/.test(record.reasonCode || '')
2185
+ || !canonicalMaterializationPath(record.path, false)
2186
+ ) add('GOV-ADAPTER-004', 'BLOCKER', `${providerId} retired hook registration policy is invalid`)
2187
+ ids.push(record.id)
2188
+ paths.push(record.path)
2189
+ if (JSON.stringify(settings || {}).includes(JSON.stringify(record.path).slice(1, -1))) {
2190
+ add('GOV-ADAPTER-005', 'BLOCKER', `${providerId} settings still reference retired hook registration ${record.id}`)
2191
+ }
2192
+ }
2193
+ if (JSON.stringify(ids) !== JSON.stringify([...ids].sort()) || new Set(paths).size !== paths.length) {
2194
+ add('GOV-ADAPTER-004', 'BLOCKER', `${providerId} retired hook registrations must be sorted and path-unique`)
2195
+ }
2196
+ }
2197
+ const specializedBaseFields = baseTemplate.engine === 'claude-permission-policy-v1'
2198
+ ? ['permissions']
2199
+ : []
2200
+ for (const [key, value] of Object.entries(canonicalConfig || {})) {
2201
+ if ([materializer.hooksField, ...specializedBaseFields].includes(key)) continue
2202
+ if (JSON.stringify(settings?.[key]) !== JSON.stringify(value)) {
2203
+ add('GOV-ADAPTER-005', 'BLOCKER', `${providerId} base-template field ${key} differs from its canonical projection`)
2204
+ }
2205
+ }
2206
+ if (baseTemplate.engine === 'claude-permission-policy-v1') {
2207
+ const permissionPolicy = policy.permissionPolicy
2208
+ const canonicalPermissions = canonicalConfig?.permissions
2209
+ const installedPermissions = settings?.permissions
2210
+ if (
2211
+ !closedKeys(permissionPolicy, ['retiredPermissionAllow', 'sha256', 'source'])
2212
+ || permissionPolicy.source !== baseTemplate.source
2213
+ || !/^[a-f0-9]{64}$/.test(permissionPolicy.sha256 || '')
2214
+ || !Array.isArray(permissionPolicy.retiredPermissionAllow)
2215
+ || !permissionPolicy.retiredPermissionAllow.length
2216
+ || permissionPolicy.retiredPermissionAllow.some((rule) => typeof rule !== 'string' || !/^Bash\(.+\)$/.test(rule))
2217
+ || !canonicalPermissions
2218
+ || !installedPermissions
2219
+ || Object.hasOwn(canonicalConfig, 'disableAutoMode')
2220
+ || Object.hasOwn(settings || {}, 'disableAutoMode')
2221
+ || canonicalPermissions.defaultMode !== installedPermissions.defaultMode
2222
+ || canonicalPermissions.disableBypassPermissionsMode !== installedPermissions.disableBypassPermissionsMode
2223
+ || !Array.isArray(installedPermissions.allow)
2224
+ || !Array.isArray(installedPermissions.ask)
2225
+ || !Array.isArray(installedPermissions.deny)
2226
+ || installedPermissions.allow.some((rule) => (
2227
+ typeof rule !== 'string'
2228
+ || installedPermissions.ask.includes(rule)
2229
+ || installedPermissions.deny.includes(rule)
2230
+ ))
2231
+ || installedPermissions.ask.some((rule) => typeof rule !== 'string')
2232
+ || installedPermissions.deny.some((rule) => typeof rule !== 'string')
2233
+ || (canonicalPermissions.allow || []).some((rule) => !installedPermissions.allow.includes(rule))
2234
+ || (canonicalPermissions.ask || []).some((rule) => !installedPermissions.ask.includes(rule))
2235
+ || (canonicalPermissions.deny || []).some((rule) => !installedPermissions.deny.includes(rule))
2236
+ || permissionPolicy.retiredPermissionAllow.some((rule) => installedPermissions.allow.includes(rule))
2237
+ || Object.hasOwn(installedPermissions, 'disableAutoMode')
2238
+ || Object.hasOwn(settings || {}, 'defaultMode')
2239
+ ) add('GOV-ADAPTER-005', 'BLOCKER', `${providerId} permission base was not safely materialized from its registered contract`)
2240
+ } else if (policy.permissionPolicy !== null) {
2241
+ add('GOV-ADAPTER-004', 'BLOCKER', `${providerId} non-permission base template claims a permission policy`)
2242
+ }
2243
+ }
2244
+
2245
+ const canonicalEventSurfaces = generatedSurfaces
2246
+ .map(([providerId, surface]) => {
2247
+ const config = surfaceHookConfigs.get(providerId)
2248
+ const materializer = surfaceHookMaterializers.get(providerId)
2249
+ const rawNativeEvents = !surface.capabilities.nativeHooks
2250
+ ? []
2251
+ : materializer?.engine === 'json-hook-map-v1'
2252
+ ? Object.keys(config?.[materializer.hooksField] || {})
2253
+ : materializer?.engine === 'json-hook-event-list-v1'
2254
+ ? (config?.[materializer.eventsField] || []).map((record) => record?.[materializer.eventField]).filter((event) => typeof event === 'string')
2255
+ : []
2256
+ const bindings = surfaceNativeEventBindings.get(providerId) || []
2257
+ const canonicalEvents = bindings.map((record) => record.canonicalEvent)
2258
+ return [providerId, canonicalEvents, [...new Set(rawNativeEvents)].sort(), rawNativeEvents.length !== new Set(rawNativeEvents).size, surface.nativeHookCoverage]
2259
+ })
2260
+ const canonicalEventsByProvider = new Map(canonicalEventSurfaces.map(([providerId, events]) => [providerId, events]))
2261
+ const nativeEventsByProvider = new Map(canonicalEventSurfaces.map(([providerId, , nativeEvents]) => [providerId, nativeEvents]))
2262
+ const productEventBaseline = canonicalEventSurfaces[0]?.[4]?.canonicalEvents || []
2263
+ for (const [providerId, events, nativeEvents, duplicateNativeEvents, coverage] of canonicalEventSurfaces) {
2264
+ if (JSON.stringify(coverage?.canonicalEvents || []) !== JSON.stringify(productEventBaseline)) {
2265
+ add('GOV-PROVIDER-001', 'BLOCKER', `${providerId} product hook event authority differs from the signed provider baseline`)
2266
+ }
2267
+ if (JSON.stringify(events) !== JSON.stringify(coverage?.projectedEvents || [])) {
2268
+ add('GOV-PROVIDER-001', 'BLOCKER', `${providerId} projected native hook events differ from its machine-readable coverage record`)
2269
+ }
2270
+ if (duplicateNativeEvents || JSON.stringify(nativeEvents) !== JSON.stringify((coverage?.projectedNativeEvents || []).map((record) => record.nativeEvent).sort())) {
2271
+ add('GOV-PROVIDER-001', 'BLOCKER', `${providerId} native hook configuration event identifiers differ from its canonical-to-native bindings`)
2272
+ }
2273
+ }
2274
+
2275
+ const launcherDestination = manifest?.consumer?.launcherDestination
2276
+ const launcherRootName = typeof launcherDestination === 'string' ? launcherDestination.replaceAll('\\', '/').split('/')[0] : null
2277
+ if (
2278
+ typeof launcherDestination !== 'string'
2279
+ || !launcherDestination
2280
+ || discoveryInventory.providerRootNames.includes(launcherRootName)
2281
+ || discoveryInventory.pluginRootNames.includes(launcherRootName)
2282
+ ) add('GOV-ADAPTER-005', 'BLOCKER', 'shared launcher destination must be provider-neutral and outside every provider discovery root')
2283
+ const committedLauncherRoot = manifestPath(repo, launcherDestination, 'shared launcher destination')
2284
+ if (committedLauncherRoot) canonicalReplayExcludedRoots.add(resolve(committedLauncherRoot))
2285
+ const launcherLifecycleOwners = generatedSurfaces.filter(([, surface]) => (surface.managedSurfaces || []).some((managed) => (
2286
+ managed.path === launcherDestination
2287
+ || managed.path.startsWith(`${launcherDestination}/`)
2288
+ || launcherDestination?.startsWith(`${managed.path}/`)
2289
+ )))
2290
+ if (launcherLifecycleOwners.length) {
2291
+ add('GOV-ADAPTER-005', 'BLOCKER', `shared launcher substrate is incorrectly owned by provider lifecycle:${launcherLifecycleOwners.map(([id]) => id).join(',')}`)
2292
+ }
2293
+ const expectedLauncherDestinations = managedLaunchers.map((launcher) => `${launcherDestination}/${launcher}`).sort()
2294
+ const actualLauncherDestinations = exactMaterializationPaths
2295
+ .filter((path) => managedLaunchers.some((launcher) => path.endsWith(`/${launcher}`)))
2296
+ .sort()
2297
+ if (JSON.stringify(actualLauncherDestinations) !== JSON.stringify(expectedLauncherDestinations)) {
2298
+ add('GOV-CORPUS-001', 'BLOCKER', 'shared launchers must have exactly one provider-neutral materialization destination')
2299
+ }
2300
+ for (const launcher of managedLaunchers) {
2301
+ const committed = committedLauncherRoot ? join(committedLauncherRoot, launcher) : null
2302
+ const shipped = join(forkRoot, 'launchers', launcher)
2303
+ if (!committed || !isRegularFile(committed) || !isRegularFile(shipped) || fileSha(committed) !== fileSha(shipped)) add('GOV-ADAPTER-005', 'BLOCKER', `shared thin adapter drift: ${launcher}`)
2304
+ if (!exactMaterializationSet.has(`${launcherDestination}/${launcher}`)) {
2305
+ add('GOV-CORPUS-001', 'BLOCKER', `shared launcher is absent from consumer materialization inventory:${launcher}`)
2306
+ }
2307
+ }
2308
+ if (!committedLauncherRoot || !repositoryPathIsSafe(committedLauncherRoot) || !pathEntryExists(committedLauncherRoot) || !lstatSync(committedLauncherRoot).isDirectory()) {
2309
+ add('GOV-EXTENSION-001', 'BLOCKER', 'provider-neutral shared launcher directory is missing or unsafe')
2310
+ } else {
2311
+ const actualLaunchers = readdirSync(committedLauncherRoot).sort()
2312
+ const expectedLaunchers = [...managedLaunchers].sort()
2313
+ if (JSON.stringify(actualLaunchers) !== JSON.stringify(expectedLaunchers)) {
2314
+ add('GOV-EXTENSION-001', 'BLOCKER', 'undeclared files are forbidden in the shared launcher directory; executable extensions must be promoted through the provider-neutral package SSOT')
2315
+ }
2316
+ }
2317
+
2318
+ const managedSharedSkills = manifest?.skills || []
2319
+ for (const [providerId, surface] of generatedSurfaces) {
2320
+ const skillRoot = manifestPath(repo, surface.skillDestination, `${providerId} skill destination`)
2321
+ if (!skillRoot || !repositoryPathIsSafe(skillRoot) || !pathEntryExists(skillRoot) || !lstatSync(skillRoot).isDirectory()) {
2322
+ add('GOV-SKILL-004', 'BLOCKER', `${providerId} skill directory is missing or unsafe`)
2323
+ continue
2324
+ }
2325
+ const actualEntries = readdirSync(skillRoot).sort()
2326
+ const expectedEntries = [...new Set(surface.skills || [])].sort()
2327
+ if (JSON.stringify(actualEntries) !== JSON.stringify(expectedEntries)) {
2328
+ add('GOV-SKILL-004', 'BLOCKER', `${providerId} skill directory contains undeclared executable content; extensions must be promoted through the provider-neutral package SSOT`)
2329
+ }
2330
+ }
2331
+ if (bom?.payload?.sharedSkillCount !== managedSharedSkills.length) add('GOV-SKILL-003', 'BLOCKER', `managed shared skill count ${managedSharedSkills.length} != BOM ${bom?.payload?.sharedSkillCount}`)
2332
+ const lockedSharedSkills = (corpusLock?.entries || []).filter((entry) => entry.file.startsWith('skills/')).map((entry) => `${entry.file}:${entry.sha256}`).sort()
2333
+ if (bom?.payload?.sharedSkillsSha256 !== sha256(lockedSharedSkills.join('\n') + '\n')) add('GOV-SKILL-003', 'BLOCKER', 'shared skill aggregate digest != governance BOM')
2334
+
2335
+ // Provider runtimes create local, git-ignored state directly below their discovery root:
2336
+ // Claude Code's .cc-writes write journal and the governance dispatcher's telemetry logs
2337
+ // (template/ds-product-template/.gitignore already declares .claude/logs/ as runtime noise;
2338
+ // both are empirically present in live consumer repos). They are not instruction/config/skill/
2339
+ // command/agent/hook discovery surfaces, and every executable/discovery class above stays
2340
+ // digest-closed, so skipping these exact direct children of the Claude root cannot open an
2341
+ // unsigned policy or execution surface. Symlink and plain-file impostors still fail below.
2342
+ const claudeRuntimeStateNames = new Set(['.cc-writes', 'logs'])
2343
+ for (const [providerRoot, plan] of providerRootPlans) {
2344
+ if (!pathEntryExists(providerRoot)) continue
2345
+ if (!repositoryPathIsSafe(providerRoot) || lstatSync(providerRoot).isSymbolicLink() || !lstatSync(providerRoot).isDirectory()) {
2346
+ add('GOV-EXTENSION-001', 'BLOCKER', `provider-native root must be a real directory:${relative(repo, providerRoot)}`)
2347
+ continue
2348
+ }
2349
+ const providerRootIsClaude = relative(repo, providerRoot) === '.claude'
2350
+ const leaves = [...plan.files, ...plan.trees]
2351
+ const visit = (directory) => {
2352
+ for (const name of readdirSync(directory).sort()) {
2353
+ const absolute = resolve(directory, name)
2354
+ const info = lstatSync(absolute)
2355
+ const rel = relative(repo, absolute).replaceAll('\\', '/')
2356
+ if (info.isSymbolicLink()) {
2357
+ add('GOV-EXTENSION-001', 'BLOCKER', `provider-native surface contains an undeclared symlink:${rel}`)
2358
+ continue
2359
+ }
2360
+ if (providerRootIsClaude && directory === providerRoot && info.isDirectory() && claudeRuntimeStateNames.has(name)) continue
2361
+ if (plan.files.has(absolute)) {
2362
+ if (!info.isFile()) add('GOV-EXTENSION-001', 'BLOCKER', `provider-native managed file has the wrong type:${rel}`)
2363
+ continue
2364
+ }
2365
+ if (plan.trees.has(absolute)) {
2366
+ if (!info.isDirectory()) add('GOV-EXTENSION-001', 'BLOCKER', `provider-native managed tree has the wrong type:${rel}`)
2367
+ else visit(absolute)
2368
+ continue
2369
+ }
2370
+ const isManagedAncestor = leaves.some((leaf) => leaf.startsWith(`${absolute}${sep}`))
2371
+ if (isManagedAncestor && info.isDirectory()) visit(absolute)
2372
+ else if (materializationAllows(rel, info.isDirectory())) {
2373
+ if (info.isDirectory()) visit(absolute)
2374
+ else if (!info.isFile() || info.nlink !== 1) add('GOV-EXTENSION-001', 'BLOCKER', `provider-native materialization is not one regular file:${rel}`)
2375
+ } else add('GOV-EXTENSION-001', 'BLOCKER', `undeclared provider-native executable/discovery surface:${rel}`)
2376
+ }
2377
+ }
2378
+ visit(providerRoot)
2379
+ }
2380
+
2381
+ // Repository-level provider discovery is also closed. Codex and Claude can discover instruction,
2382
+ // config and MCP files below the repository root, so hashing only the root projections would still
2383
+ // leave a second policy/tool SSOT in an application subdirectory. Provider roots already verified
2384
+ // above (including authenticated skill trees) are skipped; generated/build dependency trees are
2385
+ // non-source and intentionally excluded from this discovery walk.
2386
+ const managedProviderRoots = new Set([...providerRootPlans.keys()].map((path) => resolve(path)))
2387
+ const reservedProviderRootNames = new Set([
2388
+ ...discoveryInventory.providerRootNames,
2389
+ ...discoveryInventory.pluginRootNames,
2390
+ ])
2391
+ const canonicalInstructionDestinations = new Set(generatedSurfaces
2392
+ .map(([, surface]) => surface?.instructionDestination)
2393
+ .filter((path) => typeof path === 'string' && path)
2394
+ .map((path) => resolve(repo, path)))
2395
+ const reservedInstructionNames = new Set(discoveryInventory.instructionNames)
2396
+ const overrideInstructionNames = new Set(discoveryInventory.instructionOverrideNames)
2397
+ const reservedConfigPaths = [...discoveryInventory.configPaths]
2398
+ const canonicalConfigDestinations = new Set(generatedSurfaces
2399
+ .map(([, surface]) => surface?.hookDestination)
2400
+ .filter((path) => typeof path === 'string' && path)
2401
+ .map((path) => resolve(repo, path)))
2402
+ const matchedDiscoveryConfig = (rel, name) => reservedConfigPaths.find((path) => path.includes('/')
2403
+ ? rel === path || rel.endsWith(`/${path}`)
2404
+ : name === path)
2405
+ const reservedDiscoveryLeaf = (rel, name) => (
2406
+ reservedInstructionNames.has(name)
2407
+ || overrideInstructionNames.has(name)
2408
+ || Boolean(matchedDiscoveryConfig(rel, name))
2409
+ )
2410
+ const visitDiscoverySurfaces = (directory) => {
2411
+ const names = readdirIfReadable(directory)
2412
+ if (!names) return
2413
+ for (const name of names.sort()) {
2414
+ const absolute = resolve(directory, name)
2415
+ const rel = relative(repo, absolute).replaceAll('\\', '/')
2416
+ const info = lstatIfReadable(absolute)
2417
+ if (!info) {
2418
+ // Reserved provider discovery surfaces must stay verifiable; only non-reserved names degrade.
2419
+ if (reservedProviderRootNames.has(name) || reservedDiscoveryLeaf(rel, name)) {
2420
+ add('GOV-EXTENSION-001', 'BLOCKER', `provider discovery surface is unreadable and cannot be verified:${rel}`)
2421
+ }
2422
+ continue
2423
+ }
2424
+ if (info.isSymbolicLink()) {
2425
+ if (reservedProviderRootNames.has(name) || reservedDiscoveryLeaf(rel, name)) {
2426
+ add('GOV-SYMLINK-001', 'BLOCKER', `provider discovery surface is a symbolic-link alias:${rel}`)
2427
+ }
2428
+ continue
2429
+ }
2430
+ if (info.isDirectory()) {
2431
+ if (managedProviderRoots.has(absolute) || discoveryTraversalExcludedRoots.has(absolute)) continue
2432
+ if (reservedProviderRootNames.has(name)) {
2433
+ add('GOV-EXTENSION-001', 'BLOCKER', `nested provider-native discovery root is undeclared:${rel}`)
2434
+ continue
2435
+ }
2436
+ visitDiscoverySurfaces(absolute)
2437
+ continue
2438
+ }
2439
+ if (!info.isFile()) {
2440
+ if (reservedDiscoveryLeaf(rel, name)) add('GOV-SPECIAL-001', 'BLOCKER', `provider discovery surface is not a regular file:${rel}`)
2441
+ continue
2442
+ }
2443
+ if (info.nlink !== 1 && reservedDiscoveryLeaf(rel, name)) {
2444
+ add('GOV-ALIAS-001', 'BLOCKER', `provider discovery surface has a hard-link alias:${rel}`)
2445
+ continue
2446
+ }
2447
+ const matchedConfig = matchedDiscoveryConfig(rel, name)
2448
+ if (matchedConfig && !canonicalConfigDestinations.has(absolute)) {
2449
+ add('GOV-EXTENSION-001', 'BLOCKER', `provider config/MCP discovery surface is undeclared:${rel} (policy:${matchedConfig})`)
2450
+ continue
2451
+ }
2452
+ if (overrideInstructionNames.has(name)) {
2453
+ add('GOV-EXTENSION-001', 'BLOCKER', `provider instruction override/local file is undeclared:${rel}`)
2454
+ continue
2455
+ }
2456
+ if (reservedInstructionNames.has(name) && !canonicalInstructionDestinations.has(absolute)) {
2457
+ add('GOV-EXTENSION-001', 'BLOCKER', `nested provider instruction is undeclared:${rel}`)
2458
+ }
2459
+ }
2460
+ }
2461
+ if (repositoryRootSafe) visitDiscoverySurfaces(repo)
2462
+
2463
+ const governanceRoot = join(repo, 'governance')
2464
+ if (!repositoryPathIsSafe(governanceRoot) || !pathEntryExists(governanceRoot) || lstatSync(governanceRoot).isSymbolicLink() || !lstatSync(governanceRoot).isDirectory()) {
2465
+ add('GOV-EXTENSION-001', 'BLOCKER', 'governance must be a real directory containing only immutable locks, authenticated managed data, the declared launcher substrate, and optional overlay.md')
2466
+ } else {
2467
+ // Governance data files are allowed only when the immutable consumer BOM declares their exact
2468
+ // repository path and digest. Deriving both files and parent directories from that authenticated
2469
+ // authority lets a release add non-executable data (for example trust registries and schemas)
2470
+ // without turning governance/ into an open local policy or executable extension home.
2471
+ // Filesystem execute bits are not a data/code boundary: `node file.mjs` and `bash file.sh`
2472
+ // execute mode-0644 files. Dynamic direct leaves therefore use a closed lowercase JSON data
2473
+ // class. Fixed Markdown/lock/overlay homes retain their separate authority below.
2474
+ const requiredManagedGuide = 'governance/consumer-governance.md'
2475
+ if (!expectedManagedNames.includes(requiredManagedGuide)) {
2476
+ add('GOV-MANAGED-FILE-002', 'BLOCKER', `${requiredManagedGuide} must remain bound to the immutable managed-file BOM`)
2477
+ }
2478
+ const fixedGovernanceEntryNames = new Set([
2479
+ 'consumer-governance.md', 'lock.json', 'lock.schema.json', 'overlay.md',
2480
+ ])
2481
+ const directGovernanceDataName = /^[a-z0-9][a-z0-9._-]*\.json$/
2482
+ const authenticatedManagedGovernanceEntries = []
2483
+ const authenticatedNestedGovernanceFiles = new Set()
2484
+ const authenticatedNestedGovernanceDirectories = new Set()
2485
+ const governanceDirectoryName = /^[a-z0-9][a-z0-9._-]*$/
2486
+ for (const path of expectedManagedNames) {
2487
+ const segments = path.split('/')
2488
+ if (segments.length < 2 || segments[0] !== 'governance') continue
2489
+ const name = segments.at(-1)
2490
+ if (segments.length === 2 && name === 'consumer-governance.md') continue
2491
+ if (fixedGovernanceEntryNames.has(name) || !directGovernanceDataName.test(name)) {
2492
+ add(
2493
+ 'GOV-EXTENSION-001',
2494
+ 'BLOCKER',
2495
+ `BOM-managed governance data must use a non-reserved canonical lowercase .json name:${path}`,
2496
+ )
2497
+ continue
2498
+ }
2499
+ if (segments.length === 2) {
2500
+ authenticatedManagedGovernanceEntries.push(name)
2501
+ continue
2502
+ }
2503
+ const directorySegments = segments.slice(1, -1)
2504
+ if (directorySegments.some((segment) => !governanceDirectoryName.test(segment))) {
2505
+ add('GOV-EXTENSION-001', 'BLOCKER', `BOM-managed governance data has an unsafe directory path:${path}`)
2506
+ continue
2507
+ }
2508
+ authenticatedNestedGovernanceFiles.add(segments.slice(1).join('/'))
2509
+ for (let index = 1; index <= directorySegments.length; index += 1) {
2510
+ authenticatedNestedGovernanceDirectories.add(directorySegments.slice(0, index).join('/'))
2511
+ }
2512
+ }
2513
+ const allowedGovernanceEntries = new Set([
2514
+ ...fixedGovernanceEntryNames,
2515
+ ...authenticatedManagedGovernanceEntries,
2516
+ ...[...authenticatedNestedGovernanceDirectories].filter((path) => !path.includes('/')),
2517
+ ])
2518
+ const launcherSegments = typeof launcherDestination === 'string' ? launcherDestination.replaceAll('\\', '/').split('/') : []
2519
+ if (launcherSegments[0] === 'governance' && launcherSegments.length > 1) allowedGovernanceEntries.add(launcherSegments[1])
2520
+ const unexpected = readdirSync(governanceRoot).filter((name) => !allowedGovernanceEntries.has(name)).sort()
2521
+ if (unexpected.length) add('GOV-EXTENSION-001', 'BLOCKER', `governance contains undeclared executable/policy homes:${unexpected.join(',')}`)
2522
+ for (const name of authenticatedManagedGovernanceEntries) {
2523
+ const managedData = join(governanceRoot, name)
2524
+ if (!isRegularFile(managedData) || (lstatSync(managedData).mode & 0o111) !== 0) {
2525
+ add('GOV-EXTENSION-001', 'BLOCKER', `managed governance data must be one regular non-executable file:governance/${name}`)
2526
+ }
2527
+ }
2528
+ const nestedGovernanceFiles = [...authenticatedNestedGovernanceFiles].sort()
2529
+ const nestedGovernanceDirectories = [...authenticatedNestedGovernanceDirectories].sort()
2530
+ if (launcherSegments[0] === 'governance' && nestedGovernanceDirectories.some((path) => (
2531
+ path === launcherSegments[1] || path.startsWith(`${launcherSegments[1]}/`)
2532
+ ))) {
2533
+ add('GOV-EXTENSION-001', 'BLOCKER', 'BOM-managed governance data overlaps the declared executable launcher substrate')
2534
+ }
2535
+ for (const directory of nestedGovernanceDirectories) {
2536
+ const absolute = join(governanceRoot, directory)
2537
+ if (!repositoryPathIsSafe(absolute) || !pathEntryExists(absolute) || lstatSync(absolute).isSymbolicLink() || !lstatSync(absolute).isDirectory()) {
2538
+ add('GOV-EXTENSION-001', 'BLOCKER', `managed governance data directory must be one real directory:governance/${directory}`)
2539
+ continue
2540
+ }
2541
+ const expectedChildren = new Set()
2542
+ for (const childDirectory of nestedGovernanceDirectories) {
2543
+ const parts = childDirectory.split('/')
2544
+ if (parts.slice(0, -1).join('/') === directory) expectedChildren.add(parts.at(-1))
2545
+ }
2546
+ for (const childFile of nestedGovernanceFiles) {
2547
+ const parts = childFile.split('/')
2548
+ if (parts.slice(0, -1).join('/') === directory) expectedChildren.add(parts.at(-1))
2549
+ }
2550
+ const actualChildren = readdirSync(absolute).sort()
2551
+ const expected = [...expectedChildren].sort()
2552
+ if (JSON.stringify(actualChildren) !== JSON.stringify(expected)) {
2553
+ add('GOV-EXTENSION-001', 'BLOCKER', `managed governance data directory differs from its exact BOM-derived inventory:governance/${directory}`)
2554
+ }
2555
+ }
2556
+ for (const file of nestedGovernanceFiles) {
2557
+ const managedData = join(governanceRoot, file)
2558
+ if (!isRegularFile(managedData) || (lstatSync(managedData).mode & 0o111) !== 0) {
2559
+ add('GOV-EXTENSION-001', 'BLOCKER', `managed governance data must be one regular non-executable file:governance/${file}`)
2560
+ }
2561
+ }
2562
+ const overlay = join(governanceRoot, 'overlay.md')
2563
+ if (pathEntryExists(overlay) && (!isRegularFile(overlay) || (lstatSync(overlay).mode & 0o111) !== 0)) {
2564
+ add('GOV-EXTENSION-001', 'BLOCKER', 'governance/overlay.md must be one regular non-executable Markdown file')
2565
+ }
2566
+ const consumerGuide = join(governanceRoot, 'consumer-governance.md')
2567
+ if (!isRegularFile(consumerGuide) || (lstatSync(consumerGuide).mode & 0o111) !== 0) {
2568
+ add('GOV-EXTENSION-001', 'BLOCKER', 'governance/consumer-governance.md must be the managed regular non-executable Markdown guide')
2569
+ }
2570
+ }
2571
+
2572
+ // Replay every enforceable shared hook directly over every consumer-controlled source/config/doc.
2573
+ // Direct invocation avoids dispatcher event fan-out/timeouts and means hook crashes are blockers,
2574
+ // while preserving one predicate implementation (the exact immutable npm corpus) as the SSOT.
2575
+ const sourceFiles = collectSourceFiles(repo)
2576
+ const replayHooks = Array.isArray(manifest?.ciReplay) ? manifest.ciReplay : []
2577
+ let replayExecutionCount = 0
2578
+ const replayHookKeys = [
2579
+ 'event',
2580
+ 'file',
2581
+ 'sourceHook',
2582
+ 'interpreter',
2583
+ 'matcher',
2584
+ 'outputMode',
2585
+ 'inputContract',
2586
+ 'transcriptRequirement',
2587
+ ]
2588
+ const nativeHookKeys = [
2589
+ 'file',
2590
+ 'sourceHook',
2591
+ 'bucket',
2592
+ 'matcher',
2593
+ 'runtime',
2594
+ 'outputMode',
2595
+ 'inputContract',
2596
+ 'transcriptRequirement',
2597
+ ]
2598
+ const nativeHookEvents = new Set(['PreToolUse', 'PostToolUse', 'SessionStart', 'Stop', 'UserPromptSubmit'])
2599
+ const replayEvents = new Set(['PreToolUse', 'PostToolUse'])
2600
+ const replayInterpreters = new Set(['bash', 'node', 'python3'])
2601
+ const replayOutputModes = new Set([
2602
+ 'diagnostic',
2603
+ 'blocking',
2604
+ 'governance-context',
2605
+ 'governance-decision',
2606
+ 'governance-context-or-block',
2607
+ ])
2608
+ const replayInputContracts = new Set(['event-v1', 'proposed-text-v1'])
2609
+ const replayTranscriptRequirements = new Set(['none', 'optional', 'stable-required'])
2610
+ const replayWriteTools = ['Write', 'Edit', 'MultiEdit']
2611
+ const replayToolByHook = new Map()
2612
+ const replayHookIdentities = new Set()
2613
+ const nativeHookByIdentity = new Map()
2614
+ const expectedReplayIdentities = new Set()
2615
+ if (!manifest?.hooks || typeof manifest.hooks !== 'object' || Array.isArray(manifest.hooks)) {
2616
+ add('GOV-CONTENT-002', 'BLOCKER', 'installed native hook manifest is missing or invalid')
2617
+ } else {
2618
+ for (const [event, hooks] of Object.entries(manifest.hooks)) {
2619
+ if (!nativeHookEvents.has(event) || !Array.isArray(hooks)) {
2620
+ add('GOV-CONTENT-002', 'BLOCKER', `installed native hook event contract is invalid:${event}`)
2621
+ continue
2622
+ }
2623
+ for (const [index, hook] of hooks.entries()) {
2624
+ const matcherTools = typeof hook?.matcher === 'string'
2625
+ ? hook.matcher.split('|').filter(Boolean)
2626
+ : []
2627
+ const identity = `${event}\0${hook?.file || ''}\0${hook?.sourceHook || ''}\0${hook?.matcher || ''}`
2628
+ const valid = (
2629
+ closedKeys(hook, nativeHookKeys)
2630
+ && canonicalManagedRepositoryPath(hook.file)
2631
+ && hook.file.startsWith('hooks/')
2632
+ && typeof hook.sourceHook === 'string'
2633
+ && hook.sourceHook.length > 0
2634
+ && ['SHIP_AS_IS', 'SHIP_REWRITTEN', 'REPLACE'].includes(hook.bucket)
2635
+ && typeof hook.matcher === 'string'
2636
+ && matcherTools.every((tool) => /^[A-Za-z][A-Za-z0-9]*$/.test(tool))
2637
+ && replayInterpreters.has(hook.runtime)
2638
+ && replayOutputModes.has(hook.outputMode)
2639
+ && replayInputContracts.has(hook.inputContract)
2640
+ && replayTranscriptRequirements.has(hook.transcriptRequirement)
2641
+ && (
2642
+ hook.inputContract !== 'proposed-text-v1'
2643
+ || (event === 'PreToolUse' && replayWriteTools.some((tool) => matcherTools.includes(tool)))
2644
+ )
2645
+ && !nativeHookByIdentity.has(identity)
2646
+ )
2647
+ if (!valid) {
2648
+ add('GOV-CONTENT-002', 'BLOCKER', `installed native hook contract is invalid or duplicated:${event}:${index}`)
2649
+ continue
2650
+ }
2651
+ nativeHookByIdentity.set(identity, hook)
2652
+ if (hook.bucket !== 'REPLACE' && hook.sourceHook !== 'inject_deploy_url_after_push.sh') {
2653
+ expectedReplayIdentities.add(identity)
2654
+ }
2655
+ }
2656
+ }
2657
+ }
2658
+ for (const [index, hook] of replayHooks.entries()) {
2659
+ const matcherTools = typeof hook?.matcher === 'string'
2660
+ ? hook.matcher.split('|').filter(Boolean)
2661
+ : []
2662
+ const replayTool = replayWriteTools.find((tool) => matcherTools.includes(tool))
2663
+ const identity = `${hook?.event || ''}\0${hook?.file || ''}\0${hook?.sourceHook || ''}\0${hook?.matcher || ''}`
2664
+ const nativeHook = nativeHookByIdentity.get(identity)
2665
+ const valid = (
2666
+ closedKeys(hook, replayHookKeys)
2667
+ && replayEvents.has(hook.event)
2668
+ && canonicalManagedRepositoryPath(hook.file)
2669
+ && hook.file.startsWith('hooks/')
2670
+ && typeof hook.sourceHook === 'string'
2671
+ && hook.sourceHook.length > 0
2672
+ && replayInterpreters.has(hook.interpreter)
2673
+ && matcherTools.length > 0
2674
+ && matcherTools.every((tool) => /^[A-Za-z][A-Za-z0-9]*$/.test(tool))
2675
+ && Boolean(replayTool)
2676
+ && replayOutputModes.has(hook.outputMode)
2677
+ && replayInputContracts.has(hook.inputContract)
2678
+ && replayTranscriptRequirements.has(hook.transcriptRequirement)
2679
+ && (hook.inputContract !== 'proposed-text-v1' || hook.event === 'PreToolUse')
2680
+ && nativeHook?.runtime === hook.interpreter
2681
+ && nativeHook?.outputMode === hook.outputMode
2682
+ && nativeHook?.inputContract === hook.inputContract
2683
+ && nativeHook?.transcriptRequirement === hook.transcriptRequirement
2684
+ && expectedReplayIdentities.has(identity)
2685
+ && !replayHookIdentities.has(identity)
2686
+ )
2687
+ if (!valid) {
2688
+ add('GOV-CONTENT-002', 'BLOCKER', `installed CI replay hook contract is invalid or duplicated at index ${index}`)
2689
+ continue
2690
+ }
2691
+ replayHookIdentities.add(identity)
2692
+ replayToolByHook.set(hook, replayTool)
2693
+ }
2694
+ if (
2695
+ JSON.stringify([...replayHookIdentities].sort())
2696
+ !== JSON.stringify([...expectedReplayIdentities].sort())
2697
+ ) {
2698
+ add('GOV-CONTENT-002', 'BLOCKER', 'installed CI replay inventory is not an exact projection of eligible native hook contracts')
2699
+ }
2700
+ const requiredTools = new Set(executionRuntimeValid ? executionRuntime.requiredExecutables : [])
2701
+ for (const hook of replayHooks) {
2702
+ if (replayToolByHook.has(hook)) requiredTools.add(hook.interpreter)
2703
+ }
2704
+ const missingTools = [...requiredTools].filter((tool) => !commandAvailable(tool)).sort()
2705
+ if (!replayHooks.length) add('GOV-CONTENT-001', 'BLOCKER', 'installed manifest has no fail-closed CI replay hooks')
2706
+ if (missingTools.length) add('GOV-CONTENT-002', 'BLOCKER', `required governance replay tools missing: ${missingTools.join(', ')}`)
2707
+ const replayBaseEnvironment = closedToolProfile
2708
+ ? Object.freeze({
2709
+ HOME: closedToolProfile.homeDirectory,
2710
+ LANG: 'C',
2711
+ LC_ALL: 'C',
2712
+ NO_COLOR: '1',
2713
+ PATH: closedToolProfile.executablePath,
2714
+ PYTHONDONTWRITEBYTECODE: '1',
2715
+ PYTHONNOUSERSITE: '1',
2716
+ TMPDIR: closedToolProfile.tempDirectory,
2717
+ XDG_CONFIG_HOME: closedToolProfile.homeDirectory,
2718
+ })
2719
+ : Object.freeze({})
2720
+
2721
+ const materializeVerifiedReplayCorpus = () => {
2722
+ if (!closedToolProfile) throw new Error('closed replay tool profile is unavailable')
2723
+ const snapshotRoot = realpathSync(mkdtempSync(join(closedToolProfile.tempDirectory, 'verified-replay-corpus-')))
2724
+ const snapshotForkRoot = join(snapshotRoot, 'fork')
2725
+ chmodSync(snapshotRoot, 0o700)
2726
+ mkdirSync(snapshotForkRoot, { mode: 0o700 })
2727
+ const captured = new Map()
2728
+ let totalBytes = 0
2729
+ const captureAndWrite = (path, expectedDigest = null) => {
2730
+ const source = join(forkRoot, ...path.split('/'))
2731
+ const record = captureStableRegularFile(source, maxReplaySourceBytes, `replay corpus ${path}`)
2732
+ if (expectedDigest !== null && record.sha256 !== expectedDigest) {
2733
+ throw new Error(`replay corpus digest differs from its authenticated lock:${path}`)
2734
+ }
2735
+ totalBytes += record.bytes.length
2736
+ if (totalBytes > 256 * 1024 * 1024) throw new Error('replay corpus exceeds the closed 256 MiB snapshot budget')
2737
+ const destination = join(snapshotForkRoot, ...path.split('/'))
2738
+ mkdirSync(dirname(destination), { recursive: true, mode: 0o700 })
2739
+ let descriptor
2740
+ try {
2741
+ descriptor = openSync(destination, 'wx', 0o400)
2742
+ writeFileSync(descriptor, record.bytes)
2743
+ fsyncSync(descriptor)
2744
+ } finally {
2745
+ if (descriptor !== undefined) closeSync(descriptor)
2746
+ }
2747
+ const written = captureStableRegularFile(destination, maxReplaySourceBytes, `private replay corpus ${path}`)
2748
+ if (written.sha256 !== record.sha256) throw new Error(`private replay corpus write changed authenticated bytes:${path}`)
2749
+ captured.set(path, record)
2750
+ }
2751
+ const thawAndRemove = () => {
2752
+ if (!existsSync(snapshotRoot)) return
2753
+ const thaw = directory => {
2754
+ chmodSync(directory, 0o700)
2755
+ for (const name of readdirSync(directory)) {
2756
+ const path = join(directory, name)
2757
+ const info = lstatSync(path)
2758
+ if (info.isDirectory() && !info.isSymbolicLink()) thaw(path)
2759
+ }
2760
+ }
2761
+ thaw(snapshotRoot)
2762
+ rmSync(snapshotRoot, { recursive: true, force: true })
2763
+ }
2764
+ try {
2765
+ for (const entry of corpusLock.entries) captureAndWrite(entry.file, entry.sha256)
2766
+ for (const special of ['governance.lock', 'consumer/lock.json', 'consumer/lock.schema.json']) {
2767
+ if (!captured.has(special)) captureAndWrite(special)
2768
+ }
2769
+ const directories = []
2770
+ const freeze = directory => {
2771
+ directories.push(directory)
2772
+ for (const name of readdirSync(directory)) {
2773
+ const path = join(directory, name)
2774
+ if (lstatSync(path).isDirectory()) freeze(path)
2775
+ }
2776
+ }
2777
+ freeze(snapshotRoot)
2778
+ for (const directory of directories.reverse()) chmodSync(directory, 0o500)
2779
+ return {
2780
+ forkRoot: snapshotForkRoot,
2781
+ outputTransport: join(snapshotForkRoot, 'launchers/provider-hook-output-transport.mjs'),
2782
+ has(path) {
2783
+ return captured.has(path)
2784
+ },
2785
+ verify() {
2786
+ for (const [path, expected] of captured) {
2787
+ const current = captureStableRegularFile(
2788
+ join(forkRoot, ...path.split('/')),
2789
+ maxReplaySourceBytes,
2790
+ `live replay corpus CAS ${path}`,
2791
+ )
2792
+ if (
2793
+ current.sha256 !== expected.sha256
2794
+ || !sameStableFileIdentity(current.info, expected.info)
2795
+ ) throw new Error(`live replay corpus changed after authenticated snapshot:${path}`)
2796
+ }
2797
+ return true
2798
+ },
2799
+ cleanup: thawAndRemove,
2800
+ corpusLockSha256: captured.get('governance.lock')?.sha256 || null,
2801
+ }
2802
+ } catch (error) {
2803
+ thawAndRemove()
2804
+ throw error
2805
+ }
2806
+ }
2807
+
2808
+ let interpretProviderHookChildResult = null
2809
+ const replayIntegrity = (detail) => ({ kind: 'integrity', detail })
2810
+ const interpretReplayResult = ({ hook, status, stdout, stderr }) => {
2811
+ if (status === 70) {
2812
+ return replayIntegrity(
2813
+ stderr.trim() || stdout.trim() || 'child reported the reserved integrity exit code:70',
2814
+ )
2815
+ }
2816
+ if (!Number.isInteger(status) || ![0, 2].includes(status)) {
2817
+ return replayIntegrity(`unsupported child exit code:${status ?? '<missing>'}`)
2818
+ }
2819
+ if (stderr.includes('GOVERNANCE_INTEGRITY:')) {
2820
+ return replayIntegrity('child output contains the reserved governance integrity marker')
2821
+ }
2822
+ const hasStdout = stdout.length > 0
2823
+ const hasStderr = stderr.length > 0
2824
+ if (hasStdout && hasStderr) return replayIntegrity('child mixed stdout and stderr channels')
2825
+
2826
+ if (hook.outputMode === 'diagnostic') {
2827
+ if (status !== 0) return replayIntegrity('diagnostic hook returned a blocking exit')
2828
+ if (hasStdout) return replayIntegrity('diagnostic hook wrote forbidden success stdout')
2829
+ return hasStderr
2830
+ ? { kind: 'content-violation', detail: stderr.trim() }
2831
+ : { kind: 'clean' }
2832
+ }
2833
+ try {
2834
+ const interpreted = interpretProviderHookChildResult({
2835
+ status,
2836
+ outputMode: hook.outputMode,
2837
+ event: hook.event,
2838
+ stdout,
2839
+ stderr,
2840
+ })
2841
+ if (interpreted.kind === 'clean') return interpreted
2842
+ if (interpreted.kind === 'policy-block') {
2843
+ return { kind: 'content-violation', detail: interpreted.reason }
2844
+ }
2845
+ if (interpreted.kind === 'governance-context') {
2846
+ return { kind: 'content-violation', detail: interpreted.message }
2847
+ }
2848
+ if (interpreted.kind === 'governance-decision') {
2849
+ return { kind: 'content-violation', detail: interpreted.reason }
2850
+ }
2851
+ return replayIntegrity(`strict child-result interpreter returned an unknown result:${interpreted.kind || '<missing>'}`)
2852
+ } catch (error) {
2853
+ return replayIntegrity(error?.message || 'strict child-result interpretation failed')
2854
+ }
2855
+ }
2856
+ // A repository that already failed a closed structural/authenticity rule cannot become compliant
2857
+ // through content replay. Avoid executing the expensive hook matrix in that terminal state while
2858
+ // preserving the complete replay for every otherwise-eligible repository and every content-only
2859
+ // violation. The report remains a hard failure and records zero hook executions, so this is an
2860
+ // explicit fail-closed short circuit rather than a hidden PASS-path optimization.
2861
+ const replaySkippedForPreexistingBlocker = diagnostics.some((diagnostic) => diagnostic.severity === 'BLOCKER')
2862
+ let verifiedReplayCorpus = null
2863
+ const replaySourceCaptures = new Map()
2864
+ const replaySourceChangeDiagnostics = new Set()
2865
+ const addReplaySourceChange = (file, message, evidence = undefined) => {
2866
+ const repositoryFile = relative(repo, file).replaceAll('\\', '/')
2867
+ if (replaySourceChangeDiagnostics.has(repositoryFile)) return
2868
+ replaySourceChangeDiagnostics.add(repositoryFile)
2869
+ add('GOV-CONTENT-002', 'BLOCKER', `${repositoryFile} ${message}`, evidence)
2870
+ }
2871
+ const verifyReplaySourceCaptures = () => {
2872
+ for (const [file, replayed] of replaySourceCaptures) {
2873
+ const repositoryFile = relative(repo, file).replaceAll('\\', '/')
2874
+ try {
2875
+ const current = captureStableRegularFile(
2876
+ file,
2877
+ maxReplaySourceBytes,
2878
+ `final replay source CAS ${repositoryFile}`,
2879
+ )
2880
+ if (
2881
+ current.sha256 !== replayed.sha256
2882
+ || !sameStableFileIdentity(current.info, replayed.info)
2883
+ ) addReplaySourceChange(file, 'changed after its authenticated replay')
2884
+ } catch (error) {
2885
+ addReplaySourceChange(file, 'cannot be stably attested after replay', error.message)
2886
+ }
2887
+ }
2888
+ }
2889
+ if (replayHooks.length && !missingTools.length && !replaySkippedForPreexistingBlocker) {
2890
+ try {
2891
+ verifiedReplayCorpus = materializeVerifiedReplayCorpus()
2892
+ ;({ interpretProviderHookChildResult } = await import(pathToFileURL(verifiedReplayCorpus.outputTransport).href))
2893
+ if (typeof interpretProviderHookChildResult !== 'function') {
2894
+ throw new Error('strict child-result interpreter export is missing')
2895
+ }
2896
+ } catch (error) {
2897
+ verifiedReplayCorpus?.cleanup()
2898
+ verifiedReplayCorpus = null
2899
+ add('GOV-CONTENT-002', 'BLOCKER', 'verified immutable CI replay corpus cannot be materialized or loaded', error.message)
2900
+ }
2901
+ }
2902
+ if (
2903
+ replayHooks.length
2904
+ && !missingTools.length
2905
+ && !replaySkippedForPreexistingBlocker
2906
+ && verifiedReplayCorpus
2907
+ && typeof interpretProviderHookChildResult === 'function'
2908
+ ) {
2909
+ outer: for (const file of sourceFiles) {
2910
+ let body
2911
+ try {
2912
+ const captured = captureStableRegularFile(file, maxReplaySourceBytes, `replay source ${relative(repo, file)}`)
2913
+ replaySourceCaptures.set(file, captured)
2914
+ body = strictUtf8.decode(captured.bytes)
2915
+ } catch (error) {
2916
+ add('GOV-CONTENT-002', 'BLOCKER', `${relative(repo, file)} cannot be replayed as bounded strict UTF-8`, error.message)
2917
+ continue
2918
+ }
2919
+ for (const hook of replayHooks) {
2920
+ const hookPath = join(verifiedReplayCorpus.forkRoot, hook.file || '')
2921
+ const replayTool = replayToolByHook.get(hook)
2922
+ if (!replayTool) {
2923
+ add('GOV-CONTENT-002', 'BLOCKER', `CI replay hook has no validated write contract: ${hook.file || 'unnamed'}`)
2924
+ break outer
2925
+ }
2926
+ if (!verifiedReplayCorpus.has(hook.file || '')) {
2927
+ add('GOV-CONTENT-002', 'BLOCKER', `CI replay hook missing or unsafe: ${hook.file || 'unnamed'}`)
2928
+ break outer
2929
+ }
2930
+ // The shared hook corpus has a provider-neutral canonical payload contract. Native adapters
2931
+ // translate provider tool names to this contract; CI deliberately replays the canonical form.
2932
+ const absoluteFile = resolve(file)
2933
+ const repositoryFile = relative(repo, file).replaceAll('\\', '/')
2934
+ const proposedState = hook.inputContract === 'proposed-text-v1'
2935
+ ? { schemaVersion: 1, path: repositoryFile, kind: 'text', content: body }
2936
+ : null
2937
+ const materializedFile = {
2938
+ file_path: absoluteFile,
2939
+ path: absoluteFile,
2940
+ content: body,
2941
+ new_string: body,
2942
+ ...(proposedState ? { governance_write_state: proposedState } : {}),
2943
+ }
2944
+ const payload = JSON.stringify({
2945
+ hook_event_name: hook.event,
2946
+ event: hook.event,
2947
+ tool_name: replayTool,
2948
+ tool: 'write_file',
2949
+ operation: 'write',
2950
+ tool_input: {
2951
+ ...materializedFile,
2952
+ edits: [materializedFile],
2953
+ changed_paths: [absoluteFile],
2954
+ },
2955
+ ...(proposedState ? { governance_write_state: proposedState } : {}),
2956
+ changed_paths: [absoluteFile],
2957
+ })
2958
+ const replayRuntime = closedToolProfile?.executables?.[hook.interpreter]
2959
+ if (!replayRuntime) {
2960
+ add('GOV-CONTENT-002', 'BLOCKER', `CI replay runtime is unavailable:${hook.interpreter}`)
2961
+ break outer
2962
+ }
2963
+ const replayOptions = {
2964
+ cwd: repo,
2965
+ input: Buffer.from(payload),
2966
+ encoding: null,
2967
+ timeout: replayChildTimeoutMs,
2968
+ maxBuffer: maxReplayChildOutputBytes,
2969
+ killSignal: 'SIGKILL',
2970
+ env: {
2971
+ ...replayBaseEnvironment,
2972
+ CLAUDE_PROJECT_DIR: repo,
2973
+ GOVERNANCE_PROJECT_DIR: repo,
2974
+ // Direct hooks-off CI replay must use the same already-verified immutable corpus as the
2975
+ // native product runner. Never inherit or rediscover a mutable project/package root.
2976
+ GOVERNANCE_CORPUS_ROOT: verifiedReplayCorpus.forkRoot,
2977
+ GOVERNANCE_PROVIDER: 'ci-replay',
2978
+ GOVERNANCE_SELF_PROVIDER: 'ci-replay',
2979
+ GOVERNANCE_READ_ONLY: '1',
2980
+ GOVERNANCE_TELEMETRY_OPT_IN: '0',
2981
+ GOVERNANCE_STRICT_REPLAY: '1',
2982
+ GOVERNANCE_STATIC_REPLAY: '1',
2983
+ GOVERNANCE_RUNTIME_ROOT: '',
2984
+ GOVERNANCE_STATE_DIR: '',
2985
+ GOVERNANCE_TRANSCRIPT_PATH: '',
2986
+ GOVERNANCE_TRANSCRIPT_TRUST: 'unavailable',
2987
+ GOVERNANCE_WRITE_STATE_TRUST: proposedState ? 'runner-v1' : 'unavailable',
2988
+ },
2989
+ }
2990
+ let replay
2991
+ let replayAttempt = 0
2992
+ do {
2993
+ replayAttempt += 1
2994
+ closedToolProfile.verify()
2995
+ replay = spawnSync(replayRuntime, [hookPath], replayOptions)
2996
+ closedToolProfile.verify()
2997
+ } while (
2998
+ replay.error?.code === 'ETIMEDOUT'
2999
+ && replayAttempt < replayChildMaxAttempts
3000
+ )
3001
+ replayExecutionCount += 1
3002
+ if (replay.error || replay.status === null || replay.signal) {
3003
+ const cause = [
3004
+ `file=${repositoryFile}`,
3005
+ `interpreter=${hook.interpreter}`,
3006
+ `attempt=${replayAttempt}/${replayChildMaxAttempts}`,
3007
+ `timeoutMs=${replayChildTimeoutMs}`,
3008
+ `error=${replay.error?.code || 'none'}`,
3009
+ `signal=${replay.signal || 'none'}`,
3010
+ `executionIndex=${replayExecutionCount}`,
3011
+ ].join(';')
3012
+ add(
3013
+ 'GOV-CONTENT-002',
3014
+ 'BLOCKER',
3015
+ `CI replay child execution failed for ${hook.sourceHook || hook.file}`,
3016
+ cause,
3017
+ )
3018
+ break outer
3019
+ }
3020
+ let stdout
3021
+ let stderr
3022
+ try {
3023
+ stdout = strictUtf8.decode(replay.stdout || Buffer.alloc(0))
3024
+ stderr = strictUtf8.decode(replay.stderr || Buffer.alloc(0))
3025
+ } catch {
3026
+ add('GOV-CONTENT-002', 'BLOCKER', `CI replay child emitted invalid UTF-8 for ${hook.sourceHook || hook.file}`)
3027
+ break outer
3028
+ }
3029
+ const interpreted = interpretReplayResult({
3030
+ hook,
3031
+ status: replay.status,
3032
+ stdout,
3033
+ stderr,
3034
+ })
3035
+ const detail = String(interpreted.detail || '').split(/\r?\n/).slice(0, 8).join(' | ')
3036
+ if (interpreted.kind === 'integrity') {
3037
+ add(
3038
+ 'GOV-CONTENT-002',
3039
+ 'BLOCKER',
3040
+ `CI replay integrity failure from ${hook.sourceHook || hook.file} (${hook.event})`,
3041
+ detail,
3042
+ )
3043
+ break outer
3044
+ }
3045
+ if (interpreted.kind === 'content-violation') {
3046
+ const ruleId = hook.outputMode === 'diagnostic' ? 'GOV-CONTENT-003' : 'GOV-CONTENT-001'
3047
+ add(ruleId, 'BLOCKER', `${relative(repo, file)} violates ${hook.sourceHook || hook.file} (${hook.event})`, detail)
3048
+ break
3049
+ }
3050
+ }
3051
+ }
3052
+ }
3053
+ if (verifiedReplayCorpus) {
3054
+ try {
3055
+ verifiedReplayCorpus.verify()
3056
+ } catch (error) {
3057
+ add('GOV-CONTENT-002', 'BLOCKER', 'live replay corpus failed final compare-and-swap verification', error.message)
3058
+ }
3059
+ }
3060
+
3061
+ evidence.providers = Object.fromEntries(generatedSurfaces.map(([providerId, surface]) => [providerId, {
3062
+ events: canonicalEventsByProvider.get(providerId) || [],
3063
+ nativeEvents: nativeEventsByProvider.get(providerId) || [],
3064
+ skills: (surface.skills || []).length,
3065
+ instruction: surface.instructionDestination,
3066
+ hooks: surface.hookDestination,
3067
+ hookEnforcement: surface.capabilities.hookEnforcement,
3068
+ }]))
3069
+ evidence.release = bom?.release || null
3070
+ evidence.checkerSha256 = isRegularFile(shippedChecker) ? fileSha(shippedChecker) : null
3071
+ evidence.corpusLockSha256 = verifiedReplayCorpus?.corpusLockSha256
3072
+ || (existsSync(corpusLockPath) ? fileSha(corpusLockPath) : null)
3073
+ const sourceInventoryRows = sourceFiles.map((file) => {
3074
+ const repositoryFile = relative(repo, file).replaceAll('\\', '/')
3075
+ try {
3076
+ const current = captureStableRegularFile(file, maxReplaySourceBytes, `source inventory ${repositoryFile}`)
3077
+ const replayed = replaySourceCaptures.get(file)
3078
+ if (
3079
+ replayed
3080
+ && (
3081
+ current.sha256 !== replayed.sha256
3082
+ || !sameStableFileIdentity(current.info, replayed.info)
3083
+ )
3084
+ ) {
3085
+ addReplaySourceChange(file, 'changed after its authenticated replay')
3086
+ return `${repositoryFile}:${replayed.sha256}`
3087
+ }
3088
+ return `${repositoryFile}:${current.sha256}`
3089
+ } catch (error) {
3090
+ addReplaySourceChange(file, 'cannot be stably attested after replay', error.message)
3091
+ return `${repositoryFile}:unavailable`
3092
+ }
3093
+ })
3094
+ evidence.sourceInventory = {
3095
+ files: sourceInventoryRows.length,
3096
+ sha256: sha256(sourceInventoryRows.join('\n') + '\n'),
3097
+ }
3098
+ const repositoryRows = repositoryInventory(repo)
3099
+ evidence.repositoryInventory = {
3100
+ basis: repositoryRows.basis,
3101
+ files: repositoryRows.rows.length,
3102
+ sha256: sha256(repositoryRows.rows.join('\n') + '\n'),
3103
+ }
3104
+ const gitIdentity = closedRepositoryGitReady
3105
+ ? runClosedGit(['rev-parse', 'HEAD', 'HEAD^{tree}'], {
3106
+ cwd: repo,
3107
+ timeoutMs: 3000,
3108
+ })
3109
+ : null
3110
+ const [gitHead = null, gitTree = null] = gitIdentity?.status === 0
3111
+ ? gitIdentity.stdout.trim().split(/\r?\n/)
3112
+ : []
3113
+ evidence.git = {
3114
+ head: /^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(gitHead || '') ? gitHead : null,
3115
+ tree: /^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(gitTree || '') ? gitTree : null,
3116
+ }
3117
+ evidence.staticReplay = {
3118
+ sourceFiles: sourceFiles.length,
3119
+ hookExecutions: replayExecutionCount,
3120
+ skippedForPreexistingBlocker: replaySkippedForPreexistingBlocker,
3121
+ hooks: replayHooks.map((hook) => `${hook.event}:${hook.sourceHook || hook.file}`),
3122
+ requiredTools: [...requiredTools].sort(),
3123
+ nativeHooks: false,
3124
+ }
3125
+ evidence.sandboxDeniedPaths = [...sandboxDeniedPaths].sort()
3126
+ verifyReplaySourceCaptures()
3127
+ if (verifiedReplayCorpus) {
3128
+ try {
3129
+ verifiedReplayCorpus.verify()
3130
+ } catch (error) {
3131
+ add('GOV-CONTENT-002', 'BLOCKER', 'live replay corpus changed before final attestation', error.message)
3132
+ } finally {
3133
+ verifiedReplayCorpus.cleanup()
3134
+ }
3135
+ }
3136
+ const ok = !diagnostics.some((diagnostic) => diagnostic.severity === 'BLOCKER')
3137
+ // Content-address the complete compliance input, not merely the package version. Two products at
3138
+ // the same release therefore cannot share an attestation digest unless their source snapshot,
3139
+ // checker/BOM/corpus, provider projections, and replay plan are all identical.
3140
+ const attestationDigest = sha256(JSON.stringify({
3141
+ role: bom?.role || null,
3142
+ ok,
3143
+ bomSha256: evidence.bomSha256 || null,
3144
+ checkerSha256: evidence.checkerSha256,
3145
+ corpusLockSha256: evidence.corpusLockSha256,
3146
+ release: evidence.release,
3147
+ providers: evidence.providers,
3148
+ sourceInventory: evidence.sourceInventory,
3149
+ repositoryInventory: evidence.repositoryInventory,
3150
+ git: evidence.git,
3151
+ staticReplay: evidence.staticReplay,
3152
+ sandboxDeniedPaths: evidence.sandboxDeniedPaths,
3153
+ blockerRuleIds: diagnostics.filter((item) => item.severity === 'BLOCKER').map((item) => item.ruleId).sort(),
3154
+ }))
3155
+ const report = { schemaVersion: 1, ok, role: bom?.role || null, hooksRequired: false, attestationDigest, evidence, diagnostics }
3156
+
3157
+ if (asJson) process.stdout.write(JSON.stringify(report, null, 2) + '\n')
3158
+ else {
3159
+ console.log(`${ok ? '✅' : '❌'} provider-neutral governance check: ${ok ? 'PASS' : 'FAIL'} (${diagnostics.length} diagnostics)`)
3160
+ console.log(` attestationDigest: ${attestationDigest}`)
3161
+ for (const item of diagnostics) console.log(` ${item.severity} ${item.ruleId}: ${item.message}${item.evidence ? ` (${item.evidence})` : ''}`)
3162
+ }
3163
+ process.exit(ok ? 0 : 1)