zz-meridian 0.15.0 → 0.16.1

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 (488) hide show
  1. package/README.md +7 -4
  2. package/dist/adopt.js +41 -15
  3. package/dist/cli.js +7 -0
  4. package/dist/command-flags.js +38 -0
  5. package/dist/files.js +17 -4
  6. package/dist/migrations.js +294 -6
  7. package/dist/ownership.js +2 -2
  8. package/dist/rebrand.js +13 -2
  9. package/dist/session.js +4 -1
  10. package/package.json +1 -1
  11. package/payload/CHANGELOG.md +292 -0
  12. package/payload/CONTRIBUTING.md +2 -2
  13. package/payload/app/(dashboard)/(home)/README.md +129 -0
  14. package/payload/app/(dashboard)/(home)/loading.tsx +75 -0
  15. package/payload/app/(dashboard)/(home)/page.tsx +94 -0
  16. package/payload/app/(dashboard)/business/README.md +124 -0
  17. package/payload/app/(dashboard)/business/loading.tsx +81 -0
  18. package/payload/app/(dashboard)/business/page.tsx +86 -0
  19. package/payload/app/(dashboard)/customers/README.md +29 -9
  20. package/payload/app/(dashboard)/customers/[id]/README.md +98 -0
  21. package/payload/app/(dashboard)/customers/[id]/loading.tsx +34 -0
  22. package/payload/app/(dashboard)/customers/[id]/page.tsx +43 -0
  23. package/payload/app/(dashboard)/customers/loading.tsx +2 -1
  24. package/payload/app/(dashboard)/customers/page.tsx +9 -5
  25. package/payload/app/(dashboard)/engineering/README.md +136 -0
  26. package/payload/app/(dashboard)/engineering/loading.tsx +63 -0
  27. package/payload/app/(dashboard)/engineering/page.tsx +64 -0
  28. package/payload/app/(dashboard)/error.tsx +5 -5
  29. package/payload/app/(dashboard)/keys/README.md +20 -7
  30. package/payload/app/(dashboard)/keys/loading.tsx +2 -1
  31. package/payload/app/(dashboard)/keys/page.tsx +11 -10
  32. package/payload/app/(dashboard)/layout.tsx +9 -4
  33. package/payload/app/(dashboard)/members/README.md +19 -6
  34. package/payload/app/(dashboard)/members/loading.tsx +2 -1
  35. package/payload/app/(dashboard)/members/page.tsx +6 -4
  36. package/payload/app/(dashboard)/not-found.tsx +2 -1
  37. package/payload/app/(dashboard)/operations-tickets/README.md +21 -9
  38. package/payload/app/(dashboard)/operations-tickets/loading.tsx +3 -2
  39. package/payload/app/(dashboard)/operations-tickets/page.tsx +7 -7
  40. package/payload/app/(dashboard)/releases/README.md +15 -2
  41. package/payload/app/(dashboard)/releases/loading.tsx +2 -1
  42. package/payload/app/(dashboard)/releases/page.tsx +4 -2
  43. package/payload/app/(dashboard)/requests/README.md +20 -7
  44. package/payload/app/(dashboard)/requests/[id]/README.md +19 -5
  45. package/payload/app/(dashboard)/requests/[id]/actions.ts +4 -2
  46. package/payload/app/(dashboard)/requests/[id]/loading.tsx +2 -1
  47. package/payload/app/(dashboard)/requests/[id]/page.tsx +25 -12
  48. package/payload/app/(dashboard)/requests/loading.tsx +2 -1
  49. package/payload/app/(dashboard)/requests/page.tsx +6 -4
  50. package/payload/app/(dashboard)/services/README.md +113 -0
  51. package/payload/app/(dashboard)/services/loading.tsx +44 -0
  52. package/payload/app/(dashboard)/services/page.tsx +54 -0
  53. package/payload/app/(dashboard)/settings/README.md +2 -1
  54. package/payload/app/(dashboard)/settings/loading.tsx +2 -1
  55. package/payload/app/(dashboard)/settings/page.tsx +4 -2
  56. package/payload/app/(dashboard)/tests/README.md +17 -4
  57. package/payload/app/(dashboard)/tests/loading.tsx +3 -2
  58. package/payload/app/(dashboard)/tests/page.tsx +5 -3
  59. package/payload/app/(dashboard)/usage/README.md +131 -0
  60. package/payload/app/(dashboard)/usage/loading.tsx +52 -0
  61. package/payload/app/(dashboard)/usage/page.tsx +94 -0
  62. package/payload/app/api/assistant/route.ts +123 -11
  63. package/payload/app/embed/home/README.md +35 -0
  64. package/payload/app/embed/home/page.tsx +27 -0
  65. package/payload/app/embed/home/view.tsx +40 -0
  66. package/payload/app/embed/now/README.md +44 -0
  67. package/payload/app/embed/now/page.tsx +23 -0
  68. package/payload/app/embed/now/view.tsx +49 -0
  69. package/payload/app/embed/proposal/README.md +2 -2
  70. package/payload/app/embed/requests/README.md +1 -1
  71. package/payload/app/embed/requests/view.tsx +2 -2
  72. package/payload/app/layout.tsx +1 -1
  73. package/payload/app/not-found/README.md +4 -3
  74. package/payload/app/not-found.tsx +10 -4
  75. package/payload/app/sign-in/README.md +5 -5
  76. package/payload/app/sign-in/page.tsx +3 -3
  77. package/payload/app/system/(atlas)/[section]/[id]/page.tsx +2 -2
  78. package/payload/app/system/(atlas)/page.tsx +1 -1
  79. package/payload/app/system/states/loading/page.tsx +2 -2
  80. package/payload/decisions/0006-quiet-light.md +1 -1
  81. package/payload/decisions/0012-roles-and-feature-access.md +1 -1
  82. package/payload/docs/agents.md +70 -8
  83. package/payload/docs/assistant.md +26 -12
  84. package/payload/docs/atlas-scenario-coverage.md +117 -96
  85. package/payload/docs/benchmark.md +44 -24
  86. package/payload/docs/data-display.md +96 -10
  87. package/payload/docs/distribution.md +21 -17
  88. package/payload/docs/product-workspaces.md +40 -31
  89. package/payload/docs/start-a-dashboard.md +10 -9
  90. package/payload/docs/surfaces.md +40 -29
  91. package/payload/docs/voice.md +5 -4
  92. package/payload/journeys/workspaces.ts +375 -268
  93. package/payload/package.json +1 -1
  94. package/payload/scripts/ask.ts +397 -0
  95. package/payload/scripts/assistant.ts +168 -52
  96. package/payload/scripts/audit.ts +24 -11
  97. package/payload/scripts/brand.ts +4 -3
  98. package/payload/scripts/check.ts +34 -6
  99. package/payload/scripts/contrast.ts +18 -7
  100. package/payload/scripts/fake-llm.ts +81 -19
  101. package/payload/scripts/keyboard.ts +1 -1
  102. package/payload/scripts/lib/contrast-pairs.ts +10 -0
  103. package/payload/scripts/lib/data-url.ts +86 -0
  104. package/payload/scripts/lib/routes.ts +154 -0
  105. package/payload/scripts/lib/sample.ts +23 -3
  106. package/payload/scripts/navigate.ts +134 -33
  107. package/payload/scripts/perf.ts +18 -5
  108. package/payload/scripts/shot.ts +8 -1
  109. package/payload/scripts/verify.baseline.json +29 -26
  110. package/payload/scripts/verify.config.ts +37 -14
  111. package/payload/scripts/verify.ts +18 -23
  112. package/payload/skills/zz-meridian/SKILL.md +4 -2
  113. package/payload/skills/zz-meridian/references/agents.md +57 -6
  114. package/payload/skills/zz-meridian/references/cache.md +88 -22
  115. package/payload/skills/zz-meridian/references/customize.md +345 -47
  116. package/payload/skills/zz-meridian/references/existing-project.md +16 -12
  117. package/payload/skills/zz-meridian/references/live.md +11 -4
  118. package/payload/skills/zz-meridian/references/ownership.md +1 -1
  119. package/payload/skills/zz-meridian/references/standard.md +1 -1
  120. package/payload/skills/zz-meridian/references/update.md +17 -9
  121. package/payload/skills/zz-meridian/references/validation.md +40 -7
  122. package/payload/skills/zz-meridian/references/voice.md +5 -4
  123. package/payload/src/app.config.ts +45 -14
  124. package/payload/src/components/base/icons/preview.tsx +2 -2
  125. package/payload/src/components/base/shell/README.md +56 -9
  126. package/payload/src/components/base/shell/index.tsx +122 -23
  127. package/payload/src/components/base/shell/preview.tsx +31 -3
  128. package/payload/src/components/base/surface/README.md +2 -2
  129. package/payload/src/components/base/surface/preview.tsx +4 -3
  130. package/payload/src/components/base/text-roles/README.md +2 -2
  131. package/payload/src/components/base/text-roles/index.tsx +1 -1
  132. package/payload/src/components/base/text-roles/preview.tsx +2 -2
  133. package/payload/src/components/charts/bar-list/README.md +11 -4
  134. package/payload/src/components/charts/bar-list/index.tsx +13 -1
  135. package/payload/src/components/charts/bar-list/preview.tsx +23 -5
  136. package/payload/src/components/charts/column-chart/README.md +127 -17
  137. package/payload/src/components/charts/column-chart/index.tsx +299 -59
  138. package/payload/src/components/charts/column-chart/preview.tsx +185 -24
  139. package/payload/src/components/charts/composition-bar/README.md +3 -2
  140. package/payload/src/components/charts/composition-bar/index.tsx +13 -7
  141. package/payload/src/components/charts/composition-bar/preview.tsx +6 -1
  142. package/payload/src/components/charts/heatmap/README.md +70 -21
  143. package/payload/src/components/charts/heatmap/index.tsx +334 -70
  144. package/payload/src/components/charts/heatmap/preview.tsx +123 -3
  145. package/payload/src/components/charts/meridian/README.md +12 -9
  146. package/payload/src/components/charts/meridian/index.tsx +8 -2
  147. package/payload/src/components/charts/meridian/preview.tsx +3 -3
  148. package/payload/src/components/charts/readout.tsx +48 -0
  149. package/payload/src/components/charts/scale.ts +18 -4
  150. package/payload/src/components/charts/sparkline/README.md +18 -7
  151. package/payload/src/components/charts/sparkline/index.tsx +24 -12
  152. package/payload/src/components/charts/sparkline/preview.tsx +16 -5
  153. package/payload/src/components/charts/trend-chart/README.md +25 -12
  154. package/payload/src/components/charts/trend-chart/index.tsx +106 -51
  155. package/payload/src/components/charts/trend-chart/preview.tsx +33 -5
  156. package/payload/src/components/charts/uptime-bars/README.md +7 -7
  157. package/payload/src/components/charts/uptime-bars/index.tsx +17 -6
  158. package/payload/src/components/charts/uptime-bars/preview.tsx +8 -0
  159. package/payload/src/components/patterns/ask-about/README.md +6 -2
  160. package/payload/src/components/patterns/ask-about/index.tsx +2 -3
  161. package/payload/src/components/patterns/assistant/README.md +4 -2
  162. package/payload/src/components/patterns/assistant/index.tsx +44 -22
  163. package/payload/src/components/patterns/assistant/page-text.ts +21 -0
  164. package/payload/src/components/patterns/assistant/preview.tsx +1 -1
  165. package/payload/src/components/patterns/assistant/scope.tsx +28 -0
  166. package/payload/src/components/patterns/assistant/thread.ts +65 -13
  167. package/payload/src/components/patterns/data-table/index.tsx +2 -2
  168. package/payload/src/components/patterns/embed-frame/README.md +2 -2
  169. package/payload/src/components/patterns/embed-frame/preview.tsx +5 -4
  170. package/payload/src/components/patterns/export-button/README.md +2 -2
  171. package/payload/src/components/patterns/export-button/preview.tsx +6 -5
  172. package/payload/src/components/patterns/featured-metric/README.md +137 -20
  173. package/payload/src/components/patterns/featured-metric/index.tsx +138 -46
  174. package/payload/src/components/patterns/featured-metric/preview.tsx +196 -32
  175. package/payload/src/components/patterns/form-section/README.md +14 -10
  176. package/payload/src/components/patterns/form-section/index.tsx +16 -7
  177. package/payload/src/components/patterns/form-section/preview.tsx +11 -2
  178. package/payload/src/components/patterns/incident-card/README.md +31 -14
  179. package/payload/src/components/patterns/incident-card/index.tsx +29 -7
  180. package/payload/src/components/patterns/incident-card/preview.tsx +25 -7
  181. package/payload/src/components/patterns/metric-tile/README.md +78 -13
  182. package/payload/src/components/patterns/metric-tile/index.tsx +101 -24
  183. package/payload/src/components/patterns/metric-tile/preview.tsx +124 -18
  184. package/payload/src/components/patterns/page-loading/README.md +37 -6
  185. package/payload/src/components/patterns/page-loading/index.tsx +72 -4
  186. package/payload/src/components/patterns/page-loading/preview.tsx +9 -1
  187. package/payload/src/components/patterns/period-select/README.md +2 -2
  188. package/payload/src/components/patterns/proposal/README.md +1 -1
  189. package/payload/src/components/patterns/proposal/index.tsx +2 -2
  190. package/payload/src/components/patterns/rail/README.md +5 -5
  191. package/payload/src/components/patterns/rail/index.tsx +15 -3
  192. package/payload/src/components/patterns/rail/preview.tsx +4 -4
  193. package/payload/src/components/patterns/shell-tools/README.md +1 -1
  194. package/payload/src/components/patterns/shell-tools/preview.tsx +1 -1
  195. package/payload/src/components/patterns/status-list/README.md +36 -14
  196. package/payload/src/components/patterns/status-list/index.tsx +29 -2
  197. package/payload/src/components/patterns/status-list/preview.tsx +15 -3
  198. package/payload/src/components/templates/records/README.md +2 -2
  199. package/payload/src/components/templates/records/preview.tsx +2 -5
  200. package/payload/src/components/ui/banner/index.tsx +1 -1
  201. package/payload/src/components/ui/card/README.md +7 -2
  202. package/payload/src/components/ui/card/index.tsx +4 -1
  203. package/payload/src/components/ui/card/preview.tsx +12 -0
  204. package/payload/src/components/ui/copy-field/index.tsx +13 -1
  205. package/payload/src/components/ui/dialog/README.md +1 -2
  206. package/payload/src/components/ui/icon-button/preview.tsx +1 -1
  207. package/payload/src/components/ui/key-value/preview.tsx +2 -1
  208. package/payload/src/components/ui/pagination/README.md +1 -1
  209. package/payload/src/components/ui/segmented/README.md +3 -3
  210. package/payload/src/components/ui/segmented/index.tsx +2 -2
  211. package/payload/src/components/ui/select/index.tsx +5 -3
  212. package/payload/src/components/ui/skeleton/README.md +1 -1
  213. package/payload/src/components/ui/table/index.tsx +1 -1
  214. package/payload/src/components/ui/table/preview.tsx +2 -2
  215. package/payload/src/components/ui/tabs/preview.tsx +2 -2
  216. package/payload/src/data/README.md +27 -8
  217. package/payload/src/data/access.ts +50 -8
  218. package/payload/src/data/alerts.ts +21 -24
  219. package/payload/src/data/collections.ts +123 -44
  220. package/payload/src/data/engineering.ts +1 -1
  221. package/payload/src/data/features.ts +24 -20
  222. package/payload/src/data/main.ts +3 -31
  223. package/payload/src/data/member-mutations.ts +6 -5
  224. package/payload/src/data/metrics.ts +63 -23
  225. package/payload/src/data/read.ts +30 -10
  226. package/payload/src/data/refusal.ts +20 -0
  227. package/payload/src/data/requests.ts +56 -22
  228. package/payload/src/data/roles.ts +4 -4
  229. package/payload/src/data/sample.ts +9 -5
  230. package/payload/src/data/sign-in.ts +11 -0
  231. package/payload/src/data/ticket-mutations.ts +23 -10
  232. package/payload/src/data/{analytics.ts → usage.ts} +8 -9
  233. package/payload/src/lib/agent-guidance.ts +4 -3
  234. package/payload/src/lib/assistant/prompt.ts +65 -5
  235. package/payload/src/lib/assistant/respond.ts +26 -6
  236. package/payload/src/lib/assistant/scoped.ts +15 -32
  237. package/payload/src/lib/assistant/tools.ts +50 -14
  238. package/payload/src/lib/collection.ts +184 -22
  239. package/payload/src/lib/explore.ts +474 -0
  240. package/payload/src/lib/facts.ts +172 -0
  241. package/payload/src/lib/format-date.ts +35 -0
  242. package/payload/src/lib/format.ts +53 -24
  243. package/payload/src/lib/insight.ts +0 -56
  244. package/payload/src/lib/shared-context.ts +22 -7
  245. package/payload/src/styles/tokens.css +2 -2
  246. package/payload/src/system/atlas-shell.tsx +23 -8
  247. package/payload/src/system/content.ts +8 -5
  248. package/payload/src/system/fixtures/company.ts +410 -0
  249. package/payload/src/system/fixtures/sample-engineering.ts +15 -10
  250. package/payload/src/system/fixtures/sample-ops.ts +12 -16
  251. package/payload/src/system/fixtures/sample-records.ts +11 -21
  252. package/payload/src/system/fixtures/sample.ts +109 -127
  253. package/payload/src/system/hero.tsx +1 -1
  254. package/payload/src/system/page-stage.tsx +1 -1
  255. package/payload/src/system/sample-cells.tsx +13 -5
  256. package/payload/src/system/specimen.tsx +10 -2
  257. package/payload/src/system/strata.tsx +1 -1
  258. package/payload/src/system/token-view.tsx +2 -2
  259. package/payload/src/views/ask-period.tsx +15 -0
  260. package/payload/src/views/customer-context.ts +144 -0
  261. package/payload/src/views/customer-explore.ts +48 -0
  262. package/payload/src/views/customer.tsx +217 -0
  263. package/payload/src/views/customers-context.ts +75 -26
  264. package/payload/src/views/customers-explore.ts +42 -0
  265. package/payload/src/views/customers.tsx +52 -43
  266. package/payload/src/views/fact-insight.ts +56 -0
  267. package/payload/src/views/home-context.ts +262 -0
  268. package/payload/src/views/home-explore.ts +77 -0
  269. package/payload/src/views/home-parts.ts +336 -0
  270. package/payload/src/views/home.tsx +365 -0
  271. package/payload/src/views/keys-context.ts +6 -3
  272. package/payload/src/views/keys-explore.ts +27 -0
  273. package/payload/src/views/keys.tsx +24 -17
  274. package/payload/src/views/main-summary.ts +3 -33
  275. package/payload/src/views/members-context.ts +13 -6
  276. package/payload/src/views/members-explore.ts +31 -0
  277. package/payload/src/views/members.tsx +19 -31
  278. package/payload/src/views/missing-page.tsx +8 -4
  279. package/payload/src/views/no-access.tsx +1 -10
  280. package/payload/src/views/not-found-address.tsx +6 -5
  281. package/payload/src/views/not-found.tsx +9 -4
  282. package/payload/src/views/now-context.ts +291 -0
  283. package/payload/src/views/now-explore.ts +92 -0
  284. package/payload/src/views/now-parts.ts +240 -0
  285. package/payload/src/views/now.tsx +426 -0
  286. package/payload/src/views/period-actions.tsx +29 -0
  287. package/payload/src/views/releases-context.ts +1 -1
  288. package/payload/src/views/releases-explore.ts +57 -0
  289. package/payload/src/views/releases.tsx +31 -25
  290. package/payload/src/views/request-context.ts +8 -5
  291. package/payload/src/views/request-explore.ts +30 -0
  292. package/payload/src/views/request.tsx +43 -33
  293. package/payload/src/views/requests-context.ts +2 -3
  294. package/payload/src/views/requests-explore.ts +50 -0
  295. package/payload/src/views/requests.tsx +39 -36
  296. package/payload/src/views/revenue-context.ts +242 -0
  297. package/payload/src/views/revenue-explore.ts +48 -0
  298. package/payload/src/views/revenue-parts.ts +219 -0
  299. package/payload/src/views/revenue.tsx +350 -0
  300. package/payload/src/views/sample-footer.tsx +2 -2
  301. package/payload/src/views/services-context.ts +179 -0
  302. package/payload/src/views/services-explore.ts +44 -0
  303. package/payload/src/views/services.tsx +129 -0
  304. package/payload/src/views/settings.tsx +5 -5
  305. package/payload/src/views/tests-context.ts +3 -3
  306. package/payload/src/views/tests-explore.ts +46 -0
  307. package/payload/src/views/tests.tsx +32 -26
  308. package/payload/src/views/tickets-context.ts +3 -3
  309. package/payload/src/views/tickets-explore.ts +61 -0
  310. package/payload/src/views/tickets.tsx +39 -35
  311. package/payload/src/views/tools.ts +205 -54
  312. package/payload/src/views/unread.tsx +25 -0
  313. package/payload/src/views/usage-context.ts +162 -0
  314. package/payload/src/views/usage-explore.ts +66 -0
  315. package/payload/src/views/usage-parts.ts +126 -0
  316. package/payload/src/views/usage.tsx +220 -0
  317. package/payload/src/views/workspace-chrome.tsx +3 -2
  318. package/payload/tests/action-needs.test.tsx +12 -11
  319. package/payload/tests/agent-page-spec.test.ts +1 -1
  320. package/payload/tests/assistant-access.test.ts +32 -14
  321. package/payload/tests/assistant-brief.test.ts +1 -1
  322. package/payload/tests/assistant-errors.test.ts +1 -1
  323. package/payload/tests/assistant-explore-tool.test.ts +121 -0
  324. package/payload/tests/assistant-page-text.test.tsx +73 -0
  325. package/payload/tests/assistant-panel.test.tsx +1 -1
  326. package/payload/tests/assistant-prompt-truth.test.ts +89 -0
  327. package/payload/tests/assistant-proposal.test.tsx +2 -2
  328. package/payload/tests/assistant-respond.test.ts +34 -3
  329. package/payload/tests/assistant-route.test.ts +13 -0
  330. package/payload/tests/assistant-safety.test.ts +2 -2
  331. package/payload/tests/assistant-settings.test.tsx +2 -2
  332. package/payload/tests/assistant-stale-scope.test.tsx +93 -0
  333. package/payload/tests/assistant-thread-scope.test.tsx +122 -0
  334. package/payload/tests/assistant-thread.test.tsx +12 -10
  335. package/payload/tests/assistant-tools.test.ts +62 -21
  336. package/payload/tests/assistant-withheld.test.ts +73 -0
  337. package/payload/tests/atlas-layers.test.ts +2 -2
  338. package/payload/tests/atlas-shell-early-press.test.tsx +55 -0
  339. package/payload/tests/atlas-template-contracts.test.tsx +4 -3
  340. package/payload/tests/bar-list-links.test.tsx +38 -0
  341. package/payload/tests/bypasses.test.ts +8 -7
  342. package/payload/tests/card-header-title.test.tsx +24 -0
  343. package/payload/tests/chrome-navigation.test.tsx +2 -2
  344. package/payload/tests/client-imports.test.ts +85 -0
  345. package/payload/tests/collection-aggregate.test.ts +107 -0
  346. package/payload/tests/collection-null-group.test.ts +37 -0
  347. package/payload/tests/collection.test.ts +4 -13
  348. package/payload/tests/column-chart-crowded.test.tsx +93 -0
  349. package/payload/tests/column-chart-labels.test.tsx +77 -0
  350. package/payload/tests/column-chart-no-data.test.tsx +107 -0
  351. package/payload/tests/column-chart-readout.test.tsx +52 -0
  352. package/payload/tests/column-chart-running.test.tsx +49 -0
  353. package/payload/tests/column-chart-stacked.test.tsx +151 -0
  354. package/payload/tests/column-chart.test.tsx +153 -0
  355. package/payload/tests/composition-bar.test.tsx +33 -0
  356. package/payload/tests/console-chrome.test.tsx +2 -2
  357. package/payload/tests/copy-field.test.tsx +48 -0
  358. package/payload/tests/counts-and-lists.test.tsx +76 -29
  359. package/payload/tests/customer-figures.test.ts +35 -12
  360. package/payload/tests/customers-payload.test.tsx +30 -0
  361. package/payload/tests/data-model.test.ts +88 -0
  362. package/payload/tests/day-in-progress.test.ts +85 -0
  363. package/payload/tests/empty-views.test.tsx +20 -14
  364. package/payload/tests/engineering-records.test.ts +4 -4
  365. package/payload/tests/engineering-workflows.test.ts +4 -15
  366. package/payload/tests/explore-answer-length.test.ts +83 -0
  367. package/payload/tests/explore-contract.test.ts +43 -0
  368. package/payload/tests/explore-group-change.test.ts +65 -0
  369. package/payload/tests/explore-labels.test.ts +33 -0
  370. package/payload/tests/explore-money.test.ts +72 -0
  371. package/payload/tests/explore-narrowed-measure.test.ts +54 -0
  372. package/payload/tests/explore-none.test.ts +60 -0
  373. package/payload/tests/explore-percent.test.ts +62 -0
  374. package/payload/tests/explore-refers.test.ts +86 -0
  375. package/payload/tests/explore-runner.test.ts +224 -0
  376. package/payload/tests/fact-periods.test.ts +29 -0
  377. package/payload/tests/facts-config.test.ts +40 -0
  378. package/payload/tests/facts-engineering.test.ts +84 -0
  379. package/payload/tests/facts.test.ts +146 -0
  380. package/payload/tests/fake-llm.test.ts +98 -1
  381. package/payload/tests/feature-access.test.ts +14 -9
  382. package/payload/tests/feature-definitions.test.ts +2 -4
  383. package/payload/tests/featured-metric.test.tsx +178 -0
  384. package/payload/tests/form-section-actions.test.tsx +30 -0
  385. package/payload/tests/format-compact-boundary.test.ts +31 -0
  386. package/payload/tests/format-cost-axis.test.ts +39 -0
  387. package/payload/tests/format.test.ts +29 -7
  388. package/payload/tests/forms.test.tsx +20 -0
  389. package/payload/tests/heatmap-calendar-timezone.test.tsx +29 -0
  390. package/payload/tests/heatmap-calendar.test.tsx +214 -0
  391. package/payload/tests/heatmap-hours-timezone.test.tsx +30 -0
  392. package/payload/tests/heatmap-names-column.test.tsx +30 -0
  393. package/payload/tests/heatmap-scale.test.tsx +91 -0
  394. package/payload/tests/heatmap-states.test.tsx +191 -0
  395. package/payload/tests/honest-controls.test.tsx +27 -10
  396. package/payload/tests/incident-card-hyphens.test.tsx +56 -0
  397. package/payload/tests/incident-card.test.tsx +54 -0
  398. package/payload/tests/key-value.test.tsx +7 -1
  399. package/payload/tests/keys-gate.test.tsx +7 -1
  400. package/payload/tests/linked-records.test.ts +126 -0
  401. package/payload/tests/loading-routes.test.tsx +28 -5
  402. package/payload/tests/member-mutations.test.ts +8 -3
  403. package/payload/tests/member-roster-boundary.test.ts +73 -0
  404. package/payload/tests/member-row-access.test.tsx +5 -3
  405. package/payload/tests/metric-tile-compact.test.tsx +107 -0
  406. package/payload/tests/metric-tile-note.test.tsx +28 -0
  407. package/payload/tests/metric-tile.test.tsx +100 -0
  408. package/payload/tests/navigate-menu-start.test.ts +37 -0
  409. package/payload/tests/navigate-start.test.ts +9 -9
  410. package/payload/tests/no-access.test.tsx +14 -3
  411. package/payload/tests/not-found.test.ts +2 -2
  412. package/payload/tests/page-fallbacks.test.tsx +4 -4
  413. package/payload/tests/page-frame-title.test.tsx +168 -0
  414. package/payload/tests/page-gates.test.tsx +13 -7
  415. package/payload/tests/page-loading.test.tsx +76 -2
  416. package/payload/tests/pages-read-every-row.test.tsx +91 -0
  417. package/payload/tests/prose-memo.test.tsx +16 -4
  418. package/payload/tests/rail-menu-prefetch.test.tsx +46 -0
  419. package/payload/tests/rail-sign-out-thread.test.tsx +44 -0
  420. package/payload/tests/read-all-query.test.ts +43 -0
  421. package/payload/tests/read-every-row.test.ts +66 -0
  422. package/payload/tests/requests-export.test.ts +13 -0
  423. package/payload/tests/requests-query.test.ts +15 -0
  424. package/payload/tests/requests-summary-newest.test.ts +23 -0
  425. package/payload/tests/roles-context.test.ts +2 -2
  426. package/payload/tests/roles.test.ts +6 -3
  427. package/payload/tests/route-policy.test.ts +1 -1
  428. package/payload/tests/sample-company.test.ts +227 -0
  429. package/payload/tests/sample.test.ts +115 -6
  430. package/payload/tests/segmented-fit.test.tsx +22 -0
  431. package/payload/tests/settings-access.test.tsx +7 -2
  432. package/payload/tests/shared-context.test.tsx +492 -63
  433. package/payload/tests/shell-alerts.test.ts +21 -4
  434. package/payload/tests/shell-drawer-back.test.tsx +51 -0
  435. package/payload/tests/shell-early-press.test.tsx +58 -0
  436. package/payload/tests/shell-promise.test.tsx +1 -1
  437. package/payload/tests/sparkline-gaps.test.tsx +51 -0
  438. package/payload/tests/sparkline.test.tsx +20 -0
  439. package/payload/tests/status-list.test.tsx +56 -0
  440. package/payload/tests/ticket-mutations.test.ts +73 -10
  441. package/payload/tests/tickets-view.test.tsx +12 -3
  442. package/payload/tests/titles-engineering.test.ts +40 -0
  443. package/payload/tests/titles.test.ts +101 -0
  444. package/payload/tests/trend-chart-baseline.test.tsx +147 -0
  445. package/payload/tests/trend-chart-running.test.tsx +77 -0
  446. package/payload/tests/trend-chart.test.tsx +8 -4
  447. package/payload/tests/truncate.test.tsx +2 -2
  448. package/payload/tests/unsaved-leave.test.tsx +3 -3
  449. package/payload/tests/uptime-bars-end.test.tsx +25 -0
  450. package/payload/tests/uptime-no-data.test.tsx +34 -0
  451. package/payload/tests/{analytics-figures.test.ts → usage-figures.test.ts} +7 -7
  452. package/payload/tests/usage-summary.test.tsx +109 -0
  453. package/payload/tests/verify-coverage.test.ts +4 -4
  454. package/payload/tests/verify-data-url.test.ts +122 -0
  455. package/payload/tests/verify-explore-surfaces.test.ts +28 -0
  456. package/payload/tests/verify-personas.test.ts +3 -3
  457. package/payload/tests/verify-sample-surfaces.test.ts +20 -6
  458. package/payload/tests/verify-sizes.test.ts +1 -1
  459. package/payload/tests/view-tool-needs.test.ts +8 -3
  460. package/payload/tests/workspace-access.test.ts +4 -4
  461. package/payload/tests/workspace-chrome.test.tsx +10 -10
  462. package/payload/tests/workspace-data-paths.test.ts +8 -6
  463. package/payload/tests/workspace-home.test.tsx +119 -0
  464. package/payload/tests/workspaces.test.ts +15 -11
  465. package/payload/tokens/theme.light.tokens.json +1 -1
  466. package/payload/app/(dashboard)/(overview)/README.md +0 -69
  467. package/payload/app/(dashboard)/(overview)/loading.tsx +0 -60
  468. package/payload/app/(dashboard)/(overview)/page.tsx +0 -62
  469. package/payload/app/(dashboard)/analytics/README.md +0 -69
  470. package/payload/app/(dashboard)/analytics/loading.tsx +0 -24
  471. package/payload/app/(dashboard)/analytics/page.tsx +0 -61
  472. package/payload/app/(dashboard)/health/README.md +0 -68
  473. package/payload/app/(dashboard)/health/loading.tsx +0 -33
  474. package/payload/app/(dashboard)/health/page.tsx +0 -26
  475. package/payload/app/embed/health/README.md +0 -41
  476. package/payload/app/embed/health/page.tsx +0 -22
  477. package/payload/app/embed/health/view.tsx +0 -44
  478. package/payload/app/embed/overview/README.md +0 -31
  479. package/payload/app/embed/overview/page.tsx +0 -27
  480. package/payload/app/embed/overview/view.tsx +0 -59
  481. package/payload/src/views/analytics-context.ts +0 -88
  482. package/payload/src/views/analytics.tsx +0 -166
  483. package/payload/src/views/health-context.ts +0 -55
  484. package/payload/src/views/health.tsx +0 -103
  485. package/payload/src/views/overview-context.ts +0 -229
  486. package/payload/src/views/overview.tsx +0 -265
  487. package/payload/tests/analytics-summary.test.tsx +0 -80
  488. package/payload/tests/workspace-overview.test.tsx +0 -153
package/README.md CHANGED
@@ -39,10 +39,12 @@ Brand flags: `--name "Acme Ops"`, `--hex '#2E6BE4'` (or `--accent indigo|cobalt|
39
39
  in a page's kicker ("Production"); it does not touch the navigation workspaces.
40
40
 
41
41
  **adopt** copies Meridian's tokens, styles, components, gates and scripts into `src/`, `tokens/` and `scripts/`;
42
- merges the dependencies and scripts it needs into your `package.json`; replaces your global stylesheet (yours is kept
42
+ merges the dependencies and scripts it needs into your `package.json`; adds the `@meridian/*` alias to your
43
+ `tsconfig.json` (yours is kept as `tsconfig.before.json` when it carries comments or trailing commas, which JSON
44
+ cannot hold); replaces your global stylesheet (yours is kept
43
45
  beside it as `*.before.css`); brands it; adds Meridian's managed block to your `AGENTS.md` between two markers, keeping your own text there byte
44
46
  for byte; writes an empty `docs/brief.md` (your product, users, data, decisions and glossary) if you have none; installs the skill into
45
- `.agents/skills/` (Codex) and `.claude/skills/` (Claude Code); records every copied file in `.meridian/manifest.json`;
47
+ `.agents/skills/` (Codex) and `.claude/skills/` (Claude Code); records every Meridian file it copied in `.meridian/manifest.json`;
46
48
  installs and type checks. Your routes, your data layer and your own components are not touched. Meridian's files
47
49
  import each other by relative path, so a `components/ui/button` of your own is never confused with Meridian's; your
48
50
  code imports Meridian as `@meridian/…`.
@@ -61,7 +63,8 @@ writes nothing; run it first.
61
63
  deleted or kept (`.meridian/keep.json`) is never overwritten: an edited or deleted one is staged as base/ours/new copies
62
64
  for you to merge.
63
65
  - It adds the dependencies and scripts this release needs to `package.json` and updates Meridian's managed block in
64
- `AGENTS.md`; a version or script you chose yourself becomes a migration to resolve, never a silent change.
66
+ `AGENTS.md`; an older version of the same major is raised, and a newer version, an older major or a script you
67
+ chose yourself becomes a migration to resolve, never a silent change.
65
68
  - It reports a migration for each interface the release changed where one of your own files still has the old shape,
66
69
  such as `cache-components-config` or `nav-needs`. `cli/src/migrations.ts` holds every one; resolve them as the skill's
67
70
  `references/update.md` says.
@@ -83,7 +86,7 @@ Requires Node 22.18 or newer. The default `pnpm verify` runs without Google Chro
83
86
 
84
87
  It has no dependencies and no install scripts: running it executes only its own code, and only when you call it. It
85
88
  sends nothing anywhere; the only network access is your package manager's install, which you can skip with
86
- `--no-install`. Every release is built and published by GitHub Actions with npm provenance, so the registry shows the
89
+ `--no-install`, and `update` reading releases from npm. Every release is built and published by GitHub Actions with npm provenance, so the registry shows the
87
90
  commit and workflow each version came from.
88
91
 
89
92
  ## Privacy and feedback
package/dist/adopt.js CHANGED
@@ -8,7 +8,7 @@
8
8
  */
9
9
  import fs from 'node:fs';
10
10
  import path from 'node:path';
11
- import { VERSION, brandArgs, effectiveBrand, inside, installSkill, packageManager, payloadBytes, payloadFiles, readJsonc, readPayload, relativeImports, run, runTool, sha256, writeIn, writeManifest, } from './files.js';
11
+ import { VERSION, brandArgs, effectiveBrand, inside, installSkill, jsoncExtras, packageManager, payloadBytes, payloadFiles, readJsonc, readPayload, relativeImports, run, runTool, sha256, writeIn, writeManifest, } from './files.js';
12
12
  import { BRIEF_TEMPLATE, managedBlock, upsertManagedBlock } from './context.js';
13
13
  import { FIXED, adoptSetOf, managedPaths } from './ownership.js';
14
14
  /** The template's dependencies a dashboard built from these files needs. */
@@ -96,6 +96,8 @@ function verifyConfig() {
96
96
  return readPayload('scripts/verify.config.ts').replace(/const config: VerifyConfig = \{[\s\S]*?\n\};/, `const config: VerifyConfig = {
97
97
  // One detail page per state worth seeing (a normal record, a failed one, a missing one), with ids from your data.
98
98
  detailRoutes: [],
99
+ // What a route a persona may not open draws: the product's choice in src/app.config.ts.
100
+ refusal,
99
101
  };`);
100
102
  }
101
103
  /** The template's stylesheet, with its paths made relative to where the project keeps its own. */
@@ -121,6 +123,39 @@ export default defineConfig({
121
123
  });
122
124
  `;
123
125
  }
126
+ /**
127
+ * What `adopt` writes for a team's `tsconfig.json`, and what it must say about it: the file Meridian's code needs (its
128
+ * scripts import with `.ts` extensions, its code is ES2022, `@meridian/*` reaches its files), the team's own kept beside
129
+ * it when it carries what `JSON.stringify` cannot hold, and what their `@/*` alias stands for. A function rather than a
130
+ * block inside `adopt` so a test can hold it to those three: a test that ran the whole `adopt` would need the template
131
+ * the payload carries, which a checkout has only after a build, and it would be testing the copy, not this decision.
132
+ */
133
+ export function tsconfigWrites(tsPath) {
134
+ const writes = new Map();
135
+ const notes = [];
136
+ const ts = readJsonc(tsPath);
137
+ ts.compilerOptions ??= {};
138
+ ts.compilerOptions.allowImportingTsExtensions = true;
139
+ ts.compilerOptions.noEmit = true;
140
+ if (!/^es20(2[2-9]|[3-9]\d)|^esnext$/i.test(ts.compilerOptions.target ?? '')) {
141
+ notes.push(`raised tsconfig target from ${ts.compilerOptions.target ?? 'its default'} to ES2022`);
142
+ ts.compilerOptions.target = 'ES2022';
143
+ }
144
+ ts.compilerOptions.paths ??= {};
145
+ ts.compilerOptions.paths['@meridian/*'] = ['./src/*'];
146
+ const at = ts.compilerOptions.paths['@/*']?.[0];
147
+ const atAlias = typeof at === 'string' ? at.replace(/\/\*$/, '').replace(/^\.\//, '') || '.' : null;
148
+ // A tsconfig may carry comments and trailing commas, which `next dev` keeps (`comment-json`) and `JSON.stringify`
149
+ // cannot hold. What the team wrote is kept beside the rewritten file, as the stylesheet and the postcss config are,
150
+ // rather than dropped in silence — the note says so, and there is no backup to explain when there was nothing to lose.
151
+ const was = fs.readFileSync(tsPath, 'utf8');
152
+ writes.set('tsconfig.json', JSON.stringify(ts, null, 2) + '\n');
153
+ if (jsoncExtras(was)) {
154
+ writes.set('tsconfig.before.json', was);
155
+ notes.push('tsconfig.json carried comments or trailing commas, which the rewritten file cannot: yours is tsconfig.before.json');
156
+ }
157
+ return { writes, notes, atAlias };
158
+ }
124
159
  export function adopt(o) {
125
160
  const { root } = o;
126
161
  const say = (s) => console.log(s);
@@ -228,20 +263,11 @@ Rename or move them, then run adopt again.`);
228
263
  const tsPath = path.join(root, 'tsconfig.json');
229
264
  let atAlias = null;
230
265
  if (fs.existsSync(tsPath)) {
231
- const ts = readJsonc(tsPath);
232
- ts.compilerOptions ??= {};
233
- ts.compilerOptions.allowImportingTsExtensions = true;
234
- ts.compilerOptions.noEmit = true;
235
- if (!/^es20(2[2-9]|[3-9]\d)|^esnext$/i.test(ts.compilerOptions.target ?? '')) {
236
- notes.push(`raised tsconfig target from ${ts.compilerOptions.target ?? 'its default'} to ES2022`);
237
- ts.compilerOptions.target = 'ES2022';
238
- }
239
- ts.compilerOptions.paths ??= {};
240
- ts.compilerOptions.paths['@meridian/*'] = ['./src/*'];
241
- const at = ts.compilerOptions.paths['@/*']?.[0];
242
- if (typeof at === 'string')
243
- atAlias = at.replace(/\/\*$/, '').replace(/^\.\//, '') || '.';
244
- writes.set('tsconfig.json', JSON.stringify(ts, null, 2) + '\n');
266
+ const ts = tsconfigWrites(tsPath);
267
+ for (const [f, text] of ts.writes)
268
+ writes.set(f, text);
269
+ notes.push(...ts.notes);
270
+ atAlias = ts.atAlias;
245
271
  }
246
272
  else {
247
273
  return fail('no tsconfig.json: Meridian is TypeScript. Add one (next dev writes it), then run adopt again.');
package/dist/cli.js CHANGED
@@ -7,6 +7,7 @@ import os from 'node:os';
7
7
  import path from 'node:path';
8
8
  import { parseArgs } from 'node:util';
9
9
  import { adopt } from './adopt.js';
10
+ import { misapplied } from './command-flags.js';
10
11
  import { create } from './create.js';
11
12
  import { BRAND_FLAGS, VERSION, installSkill } from './files.js';
12
13
  import { brandCommand } from './rebrand.js';
@@ -75,6 +76,12 @@ function main() {
75
76
  console.log(HELP);
76
77
  return command || values.help ? 0 : 1;
77
78
  }
79
+ // A flag a command does not read is refused, never ignored: `adopt --dry-run` wrote the project rather than previewing it.
80
+ const wrong = misapplied(command, flags);
81
+ if (wrong) {
82
+ console.error(`zz-meridian ${command}: --${wrong.flag} belongs to ${wrong.belongsTo}`);
83
+ return 1;
84
+ }
78
85
  if (command === 'adopt')
79
86
  return adopt({ root: process.cwd(), brand, allowDirty: Boolean(values['allow-dirty']), install });
80
87
  if (command === 'create') {
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Which flags each command reads. A flag that names a mode or a value belongs to the command that acts on it: anywhere
3
+ * else it is refused by name rather than accepted and ignored, so `adopt --dry-run` never modifies a project while the
4
+ * person waits for a preview.
5
+ */
6
+ import { BRAND_FLAGS } from './files.js';
7
+ /** Every command reads these. */
8
+ const EVERYWHERE = ['help', 'version'];
9
+ /** The brand flags, read by the commands that write a brand. */
10
+ const BRAND = BRAND_FLAGS;
11
+ /** What each command reads, beyond the two above. */
12
+ const ACCEPTS = {
13
+ adopt: [...BRAND, 'allow-dirty', 'no-install'],
14
+ create: [...BRAND, 'no-install'],
15
+ update: ['allow-dirty', 'no-install', 'dry-run', 'resume', 'finalize', 'abort', 'verify', 'verbose'],
16
+ brand: [...BRAND, 'allow-dirty'],
17
+ skill: ['global'],
18
+ };
19
+ /** The commands that read one flag, in the CLI's own order, for the sentence that sends the person to the right one. */
20
+ function commandsReading(flag) {
21
+ return Object.keys(ACCEPTS).filter((c) => ACCEPTS[c].includes(flag));
22
+ }
23
+ /**
24
+ * The first flag a command does not read, with the command it belongs to; null when every flag is the command's own.
25
+ * `update` is the one command with no spelling of its own, so a flag only it reads is named as its own.
26
+ */
27
+ export function misapplied(command, flags) {
28
+ const accepted = ACCEPTS[command];
29
+ if (!accepted)
30
+ return null;
31
+ for (const [flag, value] of Object.entries(flags)) {
32
+ if (value === undefined || EVERYWHERE.includes(flag) || accepted.includes(flag))
33
+ continue;
34
+ const others = commandsReading(flag);
35
+ return { flag, belongsTo: others.length ? `zz-meridian ${others.join(' or ')}` : 'no command' };
36
+ }
37
+ return null;
38
+ }
package/dist/files.js CHANGED
@@ -82,10 +82,10 @@ export function runTool(root, entry, args, quiet = true) {
82
82
  return spawnSync(process.execPath, [path.join(root, 'node_modules', entry), ...args], { cwd: root, encoding: 'utf8', stdio: quiet ? 'pipe' : 'inherit' });
83
83
  }
84
84
  export const hasCommand = (cmd) => spawnSync(cmd, ['--version'], { stdio: 'ignore', shell: process.platform === 'win32' }).status === 0;
85
- /** JSON with the comments and trailing commas a tsconfig may carry. */
86
- export function readJsonc(file) {
87
- const text = fs.readFileSync(file, 'utf8');
85
+ /** A JSONC document's text with the comments dropped, and whether anything had to be dropped to read it. */
86
+ function strippedJsonc(text) {
88
87
  let out = '';
88
+ let dropped = false;
89
89
  for (let i = 0, str = false; i < text.length; i++) {
90
90
  const c = text[i];
91
91
  if (str) {
@@ -102,12 +102,14 @@ export function readJsonc(file) {
102
102
  continue;
103
103
  }
104
104
  if (c === '/' && text[i + 1] === '/') {
105
+ dropped = true;
105
106
  while (i < text.length && text[i] !== '\n')
106
107
  i++;
107
108
  out += '\n';
108
109
  continue;
109
110
  }
110
111
  if (c === '/' && text[i + 1] === '*') {
112
+ dropped = true;
111
113
  i += 2;
112
114
  while (i < text.length && !(text[i] === '*' && text[i + 1] === '/'))
113
115
  i++;
@@ -116,8 +118,19 @@ export function readJsonc(file) {
116
118
  }
117
119
  out += c;
118
120
  }
119
- return JSON.parse(out.replace(/,(\s*[}\]])/g, '$1'));
121
+ const json = out.replace(/,(\s*[}\]])/g, '$1');
122
+ return { json, dropped: dropped || json.length !== out.length };
120
123
  }
124
+ /** JSON with the comments and trailing commas a tsconfig may carry. */
125
+ export function readJsonc(file) {
126
+ return JSON.parse(strippedJsonc(fs.readFileSync(file, 'utf8')).json);
127
+ }
128
+ /**
129
+ * Whether a project's file carries what plain JSON cannot hold: a comment, or a trailing comma. `next dev` writes a
130
+ * tsconfig through `comment-json`, which keeps both, so a tool that re-serialises one drops what the team's own
131
+ * toolchain preserves — and this is the question to ask before doing it.
132
+ */
133
+ export const jsoncExtras = (text) => strippedJsonc(text).dropped;
121
134
  /**
122
135
  * Meridian's own `@/` imports, made relative to the file. An adopting project may map `@/` somewhere else, or already
123
136
  * have a `components/ui/button`; a relative import can never resolve to the team's file instead of Meridian's.
@@ -100,6 +100,134 @@ function callArity(src, fn) {
100
100
  }
101
101
  return out;
102
102
  }
103
+ /** Where an import of the team's file `rel` points, as a project path with no extension; null for a package. */
104
+ const resolvedImport = (rel, spec) => {
105
+ const bare = spec.replace(/\.[mc]?[jt]sx?$/, '');
106
+ if (bare.startsWith('@/'))
107
+ return `src/${bare.slice(2)}`;
108
+ return bare.startsWith('.') ? path.posix.join(path.posix.dirname(rel), bare) : null;
109
+ };
110
+ /** Each import of a file of the team's: the module it resolves to, the names it takes and its namespace, if it has one. */
111
+ function importsOf(rel, src) {
112
+ const out = [];
113
+ for (const m of src.matchAll(/^\s*import\s+(?:type\s+)?([^'"]*?)\s+from\s+(['"])([^'"]+)\2/gm)) {
114
+ const from = resolvedImport(rel, m[3]);
115
+ if (from === null)
116
+ continue;
117
+ const names = (/\{([^}]*)\}/.exec(m[1])?.[1] ?? '').split(',').map((s) => s.trim().replace(/^type\s+/, '').split(/\s+as\s+/)[0]).filter(Boolean);
118
+ out.push({ from, names, namespace: /\*\s*as\s+([\w$]+)/.exec(m[1])?.[1] ?? null });
119
+ }
120
+ return out;
121
+ }
122
+ /**
123
+ * The index just past the string, template or comment that starts at `i`, or `i` when none does. A regular expression is
124
+ * not told from a division here: a view tool's object holds none whose brackets or quotes do not balance.
125
+ */
126
+ function skipLiteral(src, i) {
127
+ const c = src[i], next = src[i + 1];
128
+ if (c === '/' && next === '/') {
129
+ const e = src.indexOf('\n', i);
130
+ return e < 0 ? src.length : e;
131
+ }
132
+ if (c === '/' && next === '*') {
133
+ const e = src.indexOf('*/', i + 2);
134
+ return e < 0 ? src.length : e + 2;
135
+ }
136
+ if (c === "'" || c === '"') {
137
+ let j = i + 1;
138
+ while (j < src.length && src[j] !== c && src[j] !== '\n')
139
+ j += src[j] === '\\' ? 2 : 1;
140
+ return j + 1;
141
+ }
142
+ if (c === '`') {
143
+ let j = i + 1;
144
+ while (j < src.length && src[j] !== '`') {
145
+ if (src[j] === '\\')
146
+ j += 2;
147
+ else if (src[j] === '$' && src[j + 1] === '{')
148
+ j = closeOf(src, j + 1) + 1;
149
+ else
150
+ j++;
151
+ }
152
+ return j + 1;
153
+ }
154
+ return i;
155
+ }
156
+ /** The index of the bracket that closes the `{`, `(` or `[` at `open`, passing over strings and comments. */
157
+ function closeOf(src, open) {
158
+ let depth = 0;
159
+ for (let i = open; i < src.length;) {
160
+ const past = skipLiteral(src, i);
161
+ if (past !== i) {
162
+ i = past;
163
+ continue;
164
+ }
165
+ const c = src[i];
166
+ if ('{(['.includes(c))
167
+ depth++;
168
+ else if ('})]'.includes(c) && --depth === 0)
169
+ return i;
170
+ i++;
171
+ }
172
+ return src.length;
173
+ }
174
+ /**
175
+ * Each `defineViewTool({ … })` call of a file, as the keys of its object's own level, each with its value when that is a
176
+ * string and null otherwise; a key inside a nested object, call or function body, such as an input's `page`, is not
177
+ * the tool's. An object that spreads another has the key `...`, since what it spreads cannot be read here.
178
+ */
179
+ function viewToolCalls(src) {
180
+ const out = [];
181
+ const call = /defineViewTool\s*(?:<[^>]*>)?\s*\(\s*\{/y;
182
+ const key = /(?:async\s+)?([A-Za-z_$][\w$]*)(?:\s*:\s*(['"])((?:\\.|(?!\2)[^\\\n])*)\2)?/y;
183
+ for (let i = 0; i < src.length;) {
184
+ const past = skipLiteral(src, i);
185
+ if (past !== i) {
186
+ i = past;
187
+ continue;
188
+ }
189
+ call.lastIndex = i;
190
+ const m = call.exec(src);
191
+ if (!m || /[\w$.]/.test(src[i - 1] ?? '')) {
192
+ i++;
193
+ continue;
194
+ }
195
+ const open = i + m[0].length - 1;
196
+ const close = closeOf(src, open);
197
+ const keys = new Map();
198
+ let prev = '{';
199
+ for (let k = open + 1; k < close;) {
200
+ const lit = skipLiteral(src, k);
201
+ if (lit !== k) {
202
+ k = lit;
203
+ continue;
204
+ }
205
+ const c = src[k];
206
+ if (/\s/.test(c)) {
207
+ k++;
208
+ continue;
209
+ }
210
+ if (prev === '{' || prev === ',') {
211
+ if (src.startsWith('...', k))
212
+ keys.set('...', null);
213
+ key.lastIndex = k;
214
+ const found = key.exec(src);
215
+ if (found && !keys.has(found[1]))
216
+ keys.set(found[1], found[3] ?? null);
217
+ }
218
+ if ('{(['.includes(c)) {
219
+ k = closeOf(src, k) + 1;
220
+ prev = '}';
221
+ continue;
222
+ }
223
+ prev = c;
224
+ k++;
225
+ }
226
+ out.push(keys);
227
+ i = close + 1;
228
+ }
229
+ return out;
230
+ }
103
231
  // ── The registry ─────────────────────────────────────────────────────────────────────────────────────
104
232
  const GATE_AND_BUILD = ['gate', 'build'];
105
233
  export const RELEASE_MIGRATIONS = [
@@ -254,7 +382,166 @@ export const RELEASE_MIGRATIONS = [
254
382
  instructions: 'The gate asked this of pages under `app/(dashboard)/` only; from 0.15.0 it asks it under whatever route group sits at the top of the app folder. Read "A page spec" in references/customize.md. Under `## Agents` in each page\'s README.md listed here, add `### What the agent reads`: what the page\'s shared context tells each agent, with a real line of it, and what the MCP App and the console assistant do with it. A copy of the rule kept in `scripts/check.local.ts` for the project\'s own group can be deleted.',
255
383
  checks: ['gate'],
256
384
  },
385
+ {
386
+ id: 'refusal-declared',
387
+ since: '0.16.0',
388
+ applies: refusalUndeclared,
389
+ summary: 'The product says what an address a person may not open draws: `refusal` in `src/app.config.ts`.',
390
+ instructions: 'Read "Navigation: `src/app.config.ts`" in references/customize.md. Add `export const refusal: \'explain\' | \'absent\' = \'explain\';` to `src/app.config.ts`, beside `facts`: `explain` keeps the no-access page a refused page draws today, and `absent` answers as an address that names nothing. A project that keeps the sample\'s access seam brings `src/data/access.ts` and `src/data/refusal.ts` over from the release (`gate` reads `refusal`, with `gatedMetadata` and `withheldEntries` beside it, and `gatedLoading` reads it alone), titles each gated page with `export function generateMetadata() { return gatedMetadata(\'<feature>\', { title: \'<its title>\' }); }` in place of a static `metadata`, passes its loading state\'s `name` and `title` through `gatedLoading`, and adds `refusal,` to the config object of `scripts/verify.config.ts`, imported from `../src/app.config.ts`, so `verify --as` expects the page the product chose.',
391
+ checks: GATE_AND_BUILD,
392
+ },
393
+ {
394
+ id: 'managed-exports-removed',
395
+ since: '0.16.0',
396
+ applies: removedExportReaders,
397
+ summary: 'A file of the team\'s imports an export the release removed from a managed module: `deviations`, `largest`, `runs` or `weekendRatio` from `src/lib/insight.ts`, or `queryInput` from `src/lib/collection.ts`.',
398
+ instructions: 'The update brought `src/lib/insight.ts` and `src/lib/collection.ts` without them, so each file listed here fails the type check. A file of the sample\'s comes over from the release: the Overview context is Home\'s, `src/views/home-context.ts` (see `sample-pages-renamed`); the assistant reads through `view_<name>` and `explore_<view>` and has no `query_<name>` tools, so bring `src/lib/assistant/tools.ts` and `src/lib/assistant/respond.ts` over and pass `readable`, every collection the person may read, from the assistant route to `respond` beside `collections` (read "Prepared queries" in references/agents.md); and a shipped test that imports one, such as `tests/shared-context.test.tsx` or `tests/collection.test.ts`, comes over as the release has it. A file of your own that still needs one of the functions writes it in that file, from its 0.15.0 source.',
399
+ checks: GATE_AND_BUILD,
400
+ },
401
+ {
402
+ id: 'view-tool-page-explore',
403
+ since: '0.16.0',
404
+ applies: (root) => matching(root, (_, src) => viewToolCalls(src).some((k) => !k.has('...') && !(k.has('page') && k.has('explore')))),
405
+ summary: 'Every view tool names the page it renders and its prepared query: `page` and `explore` on `defineViewTool`.',
406
+ instructions: 'Read "Prepared queries" in references/agents.md. Add to each `defineViewTool` call in the files listed here `page`, the console route of the page the view renders (a rail destination\'s `href` in `nav`, or a detail page\'s route with its segment, such as `/customers/[id]`), and `explore`, its prepared query (`PreparedQuery` in `src/lib/explore.ts`), declared beside the view as the sample\'s are in `src/views/<view>-explore.ts`. `ViewTool` in `src/lib/shared-context.ts` requires both, so a tool without them fails the type check. A project that kept the sample\'s view tools brings `src/views/tools.ts` and the `src/views/*-explore.ts` files over from the release. Once a tool names its page, the gate asks that page\'s specification what the agent does there (`agent-does-here-section`).',
407
+ checks: GATE_AND_BUILD,
408
+ },
409
+ {
410
+ id: 'agent-does-here-section',
411
+ since: '0.16.0',
412
+ applies: viewToolPagesWithoutAgentDoes,
413
+ summary: 'The specification of a page a view tool renders says what the agent does there.',
414
+ instructions: 'Read "Prepared queries" in references/agents.md. Under `## Agents` in the README.md of each page a view tool\'s `page` names, add `### What the agent does here`: a line `Protagonist: <the card\'s name>.`, then at least three questions a person on the page asks that its context does not answer alone, each `- "<question>" — <what the prepared query is asked>`, and mark that card on the page with `data-protagonist="<name>"`. `scripts/check.ts` fails the gate on such a page without the part. While a view tool in `src/views/tools.ts` names no page yet (`view-tool-page-explore`), every console page specification without the part is listed: one that no tool comes to name needs nothing.',
415
+ checks: ['gate'],
416
+ },
417
+ {
418
+ id: 'sample-pages-renamed',
419
+ since: '0.16.0',
420
+ applies: renamedSampleFiles,
421
+ summary: 'The project holds the sample\'s pages and views under their 0.15.0 names: Overview is Home, Analytics is Usage at `/usage` and Operations is Services at `/services`.',
422
+ instructions: 'The release renamed them with no redirect, and an update writes none of the team\'s pages, views or tests. Each file listed is one of them or names one: a page or a view, the view tools, `src/app.config.ts`\'s nav and the feature ids it names, the routes of `scripts/verify.config.ts`, or a shipped test that expects them. A project that kept the sample\'s pages as they were brings the release\'s over: `app/(dashboard)/` and `app/embed/`, `src/views/`, `src/data/`, `src/lib/assistant/` beside `prompt.ts`, `src/app.config.ts` with its own `app` kept, and each shipped test as the release has it; a file listed here that the release no longer has is deleted. A project that made the pages its own keeps its own names, makes the changes this release\'s other migrations list in them, and reads "not applicable" for the assistant walk-through\'s steps on Home and Now (`scripts/lib/sample.ts`).',
423
+ checks: GATE_AND_BUILD,
424
+ },
257
425
  ];
426
+ /** The team's files that import an export 0.16.0 removed from a managed module, by name or through a namespace. */
427
+ function removedExportReaders(root) {
428
+ const removed = { 'src/lib/insight': ['deviations', 'largest', 'runs', 'weekendRatio'], 'src/lib/collection': ['queryInput'] };
429
+ return matching(root, (rel, src) => importsOf(rel, src).some(({ from, names, namespace }) => {
430
+ const gone = removed[from] ?? [];
431
+ return names.some((n) => gone.includes(n)) || (namespace !== null && gone.some((g) => new RegExp(`(?<![\\w$.])${namespace.replace(/\$/g, '\\$')}\\.${g}\\b`).test(src)));
432
+ }));
433
+ }
434
+ /**
435
+ * The specifications of the pages the view tools of `src/views/tools.ts` render, as the gate reads them from 0.16.0,
436
+ * that lack "What the agent does here". While a tool names no page, the gate cannot tell its page, and neither can this:
437
+ * every console page specification without the part is listed then.
438
+ */
439
+ function viewToolPagesWithoutAgentDoes(root) {
440
+ const tools = text(root, 'src/views/tools.ts');
441
+ if (tools === null)
442
+ return null;
443
+ const calls = viewToolCalls(tools).filter((k) => !k.has('...'));
444
+ if (calls.length === 0)
445
+ return null;
446
+ const pages = new Set(calls.map((k) => k.get('page')).filter((p) => typeof p === 'string'));
447
+ const unnamed = calls.some((k) => !k.has('page'));
448
+ const app = fs.existsSync(path.join(root, 'app')) ? 'app' : 'src/app';
449
+ const specs = [];
450
+ walk(root, app, specs, /^README\.md$/);
451
+ const hits = specs.filter((rel) => {
452
+ const rest = rel.slice(app.length + 1);
453
+ if (!/^\([^/)]+\)\//.test(rest))
454
+ return false;
455
+ const route = `/${path.posix.dirname(rest).split('/').filter((s) => !/^\(.*\)$/.test(s)).join('/')}`;
456
+ return (unnamed || pages.has(route)) && !/^## Agents$(?:\n(?!## ).*)*\n### What the agent does here$/m.test(text(root, rel) ?? '');
457
+ });
458
+ return hits.length > 0 ? hits : null;
459
+ }
460
+ /**
461
+ * The sample's pages and views under the names 0.16.0 replaced, and every file of the team's that names one: an import
462
+ * of one of their modules, a feature id or view tool of theirs, the walk-through's surfaces as they were, and the
463
+ * verify config's routes for them. Only a project that still holds one of the sample's old files is read, so a page of
464
+ * a product's own at the same address, or a feature of its own of the same name, is never taken for the sample's. The
465
+ * names are put together from their parts: the repository holds them out of everything but its history.
466
+ */
467
+ function renamedSampleFiles(root) {
468
+ const old = ['overview', 'analytics', 'health'];
469
+ const modules = new Set([...old.flatMap((n) => [`src/views/${n}`, `src/views/${n}-context`]), `src/data/${old[1]}`]);
470
+ const group = `(${old[0]})`;
471
+ const named = new RegExp(`\\b(?:FEATURES\\.|view_)(?:${old.join('|')})\\b`);
472
+ const isOld = (rel) => modules.has(rel.replace(/\.[mc]?[jt]sx?$/, '')) || (/^(?:src\/)?app\//.test(rel) && rel.split('/').includes(group));
473
+ const sources = teamSources(root);
474
+ if (!sources.some((f) => isOld(f.rel)))
475
+ return null;
476
+ const seeds = sources.filter(({ rel, src }) => isOld(rel)
477
+ || importsOf(rel, src).some((i) => modules.has(i.from))
478
+ || named.test(src)
479
+ || (/\bsampleSurfaces\b/.test(src) && new RegExp(`\\b${old[0]}\\s*:|\\.${old[0]}\\b`).test(src))).map((f) => f.rel);
480
+ // A page or an embed view of the sample's goes with its folder: its loading state and its specification name nothing
481
+ // of the old, and are as much the sample's. A file that imports one of the old files, such as a test that renders the
482
+ // page, names it too; a file that only names one, such as the configuration, is not itself the old.
483
+ const oldFiles = new Set(sources.map((f) => f.rel).filter(isOld));
484
+ const pages = sources.filter(({ rel, src }) => /^(?:src\/)?app\//.test(rel) && /^(?:page|view)\.tsx?$/.test(base(rel)) && (isOld(rel) || importsOf(rel, src).some((i) => modules.has(i.from))));
485
+ for (const { rel } of pages) {
486
+ const dir = path.posix.dirname(rel);
487
+ try {
488
+ for (const e of fs.readdirSync(path.join(root, dir), { withFileTypes: true }))
489
+ if (e.isFile())
490
+ oldFiles.add(`${dir}/${e.name}`);
491
+ }
492
+ catch { /* gone since it was read */ }
493
+ }
494
+ const oldPaths = new Set([...oldFiles].map((r) => r.replace(/\.[mc]?[jt]sx?$/, '')));
495
+ const found = new Set([...seeds, ...oldFiles]);
496
+ for (const { rel, src } of sources)
497
+ if (importsOf(rel, src).some((i) => oldPaths.has(i.from)))
498
+ found.add(rel);
499
+ const hits = [...found].sort();
500
+ const verifyConfig = 'scripts/verify.config.ts';
501
+ const routes = new RegExp(`'/(?:${old[1]}|${old[2]})'|title:\\s*'(?:Overview|Analytics|Operations)'`);
502
+ if (routes.test(text(root, verifyConfig) ?? ''))
503
+ hits.push(verifyConfig);
504
+ return hits;
505
+ }
506
+ /** The project's configuration, when it does not yet say what an address a person may not open draws (0.16.0). */
507
+ function refusalUndeclared(root) {
508
+ const rel = 'src/app.config.ts';
509
+ const body = text(root, rel);
510
+ if (body === null)
511
+ return null;
512
+ return /^export const refusal\b/m.test(body) ? null : [rel];
513
+ }
514
+ /** The nav item objects of a nav array, by brace: an object naming an `href` of its own, however many lines it is over. Braces inside a quoted value are not braces. */
515
+ function navItemObjects(nav) {
516
+ const found = [];
517
+ const open = [];
518
+ let quote = null;
519
+ for (let i = 0; i < nav.length; i++) {
520
+ const ch = nav[i];
521
+ if (quote) {
522
+ if (ch === '\\')
523
+ i++;
524
+ else if (ch === quote)
525
+ quote = null;
526
+ continue;
527
+ }
528
+ if (ch === "'" || ch === '"' || ch === '`')
529
+ quote = ch;
530
+ else if (ch === '{')
531
+ open.push(i);
532
+ else if (ch === '}') {
533
+ const start = open.pop();
534
+ if (start === undefined)
535
+ continue;
536
+ const inner = nav.slice(start + 1, i);
537
+ // What is left once the objects inside it are taken out: a group's `href`s are all in its items, so a group has
538
+ // none of its own left and is not an item.
539
+ if (/\bhref\s*:/.test(inner.replace(/\{[^{}]*\}/g, '')))
540
+ found.push(inner);
541
+ }
542
+ }
543
+ return found;
544
+ }
258
545
  /** The project's own nav, with any item still missing the `needs` that 0.13.0 requires of it. */
259
546
  function navItemsWithoutNeeds(root) {
260
547
  const rel = 'src/app.config.ts';
@@ -264,16 +551,17 @@ function navItemsWithoutNeeds(root) {
264
551
  const nav = /export const nav\b[^=]*=\s*\[([\s\S]*?)\n\];/.exec(body)?.[1];
265
552
  if (nav === undefined)
266
553
  return null;
267
- // One item per line, as `adopt` writes them and as a hand-kept nav keeps them, so the item is the line naming its
268
- // `href`: a group's opening line — every project groups its nav — names no `href` and carries no `needs` of its own,
269
- // so counting it as an item would report a change no edit can settle. Anything the file does beyond that is the
270
- // team's own shape, and none of it is read.
271
- const items = nav.split('\n').filter((l) => /\bhref\s*:/.test(l));
554
+ // The item is the object naming its `href`, read by its braces rather than by its line: a team that spreads an item
555
+ // over its keys put the `needs` two lines below the `href`, and a line-based read reported the migration for a file
556
+ // with none of the old shape left in it. A group is not a destination and carries no `needs`: its own `href`s belong
557
+ // to the items inside it, so an object whose braces hold only other objects is not read. Anything the file does
558
+ // beyond that is the team's own shape, and none of it is read.
559
+ const items = navItemObjects(nav);
272
560
  // A project of 0.12.3's vintage also declares `NavItem` itself, without the property: an item that names a `needs` its
273
561
  // own type does not have is the type error the gate prints next, so the change is not done until the type carries it.
274
562
  const declaredType = /export type NavItem[^;]*\};/.test(body);
275
563
  const typeHasNeeds = /export type NavItem[^;]*\bneeds\s*:/.test(body);
276
- return items.some((l) => !/\bneeds\s*:/.test(l)) || (declaredType && !typeHasNeeds) ? [rel] : null;
564
+ return items.some((item) => !/\bneeds\s*:/.test(item)) || (declaredType && !typeHasNeeds) ? [rel] : null;
277
565
  }
278
566
  /**
279
567
  * The page specifications under a route group of the project's own, at the top of its app folder, that lack the part the
package/dist/ownership.js CHANGED
@@ -1,14 +1,14 @@
1
1
  const HASH = /^sha256-[0-9a-f]{64}$/;
2
2
  const SEMVER = /^\d+\.\d+\.\d+$/;
3
3
  /** The library modules Meridian's components and gates import; the rest of src/lib belongs to the template's pages. */
4
- export const LIB = ['cn', 'format', 'format-date', 'period', 'color', 'host', 'preferences', 'csv', 'safe-markdown', 'logo', 'collection', 'live', 'shared-context', 'agent-guidance', 'insight'].map((n) => `src/lib/${n}.ts`);
4
+ export const LIB = ['cn', 'format', 'format-date', 'period', 'color', 'host', 'preferences', 'csv', 'safe-markdown', 'logo', 'collection', 'explore', 'live', 'shared-context', 'agent-guidance', 'insight', 'facts'].map((n) => `src/lib/${n}.ts`);
5
5
  /** The single files adopt copies besides the folders and the library: all of them must be in a payload that is whole. */
6
6
  export const FIXED = [...LIB, 'src/lib/assistant/prompt.ts', 'src/views/console-chrome.tsx', 'tests/setup.ts'];
7
7
  /** The one rule, a plain-English line per clause. `references/ownership.md` renders it, so the guidance cannot drift. */
8
8
  export const OWNERSHIP = [
9
9
  'Managed: the payload files under `tokens/`, `src/styles/` and `src/components/`, except each component\'s `README.md` and `preview.tsx`.',
10
10
  'Managed: the distributed scripts under `scripts/`, except `scripts/verify.config.ts`, the first-load baseline `optional:scripts/verify.baseline.json` and a team-local `optional:scripts/check.local.ts`.',
11
- 'Managed: the library helpers `src/lib/cn.ts`, `src/lib/format.ts`, `src/lib/format-date.ts`, `src/lib/period.ts`, `src/lib/color.ts`, `src/lib/host.ts`, `src/lib/preferences.ts`, `src/lib/csv.ts`, `src/lib/safe-markdown.ts`, `src/lib/logo.ts`, `src/lib/collection.ts`, `src/lib/live.ts`, `src/lib/shared-context.ts`, `src/lib/agent-guidance.ts` and `src/lib/insight.ts`, and the assistant prompt `src/lib/assistant/prompt.ts`.',
11
+ 'Managed: the library helpers `src/lib/cn.ts`, `src/lib/format.ts`, `src/lib/format-date.ts`, `src/lib/period.ts`, `src/lib/color.ts`, `src/lib/host.ts`, `src/lib/preferences.ts`, `src/lib/csv.ts`, `src/lib/safe-markdown.ts`, `src/lib/logo.ts`, `src/lib/collection.ts`, `src/lib/explore.ts`, `src/lib/live.ts`, `src/lib/shared-context.ts`, `src/lib/agent-guidance.ts`, `src/lib/insight.ts` and `src/lib/facts.ts`, and the assistant prompt `src/lib/assistant/prompt.ts`.',
12
12
  'Managed: `src/views/console-chrome.tsx` and `tests/setup.ts`.',
13
13
  'Managed: `optional:scripts/package.json`, only when the adopt or create that set the project up generated it for this project shape.',
14
14
  'Managed: both installed skill trees, `optional:.agents/skills/zz-meridian/` and `optional:.claude/skills/zz-meridian/`, taken from the release\'s skill payload.',
package/dist/rebrand.js CHANGED
@@ -165,8 +165,19 @@ function attempt(ctx, change, allowDirty) {
165
165
  }
166
166
  /** The command: the real context, which replays the running release and runs its branding script in scratch. */
167
167
  export function brandCommand({ root, flags, allowDirty }) {
168
- const scratch = fs.mkdtempSync(path.join(os.tmpdir(), 'zz-meridian-brand-'));
169
168
  const log = (line) => (line.startsWith('zz-meridian brand:') ? console.error(line) : console.log(line));
169
+ // The flags are read before anything is copied, and a value the branding script refuses is refused here with the
170
+ // command's own sentence: `adopt` and `create` read the same flags through `effectiveBrand` and answer its Error
171
+ // (`adopt.ts:153`, `create.ts:19`), where `brand` let it out as a stack trace.
172
+ let brand;
173
+ try {
174
+ brand = effectiveBrand(flags);
175
+ }
176
+ catch (e) {
177
+ log(`zz-meridian brand: ${e.message}`);
178
+ return 1;
179
+ }
180
+ const scratch = fs.mkdtempSync(path.join(os.tmpdir(), 'zz-meridian-brand-'));
170
181
  const replays = new Map();
171
182
  let n = 0;
172
183
  try {
@@ -204,7 +215,7 @@ export function brandCommand({ root, flags, allowDirty }) {
204
215
  return fs.readFileSync(path.join(copy, APP_CONFIG), 'utf8');
205
216
  },
206
217
  };
207
- return rebrand(ctx, effectiveBrand(flags), { allowDirty });
218
+ return rebrand(ctx, brand, { allowDirty });
208
219
  }
209
220
  finally {
210
221
  fs.rmSync(scratch, { recursive: true, force: true });
package/dist/session.js CHANGED
@@ -265,8 +265,11 @@ function install(ctx, session, j, pm) {
265
265
  const { root } = ctx;
266
266
  const lock = lockfileFor(root, pm);
267
267
  const prior = j.operations.find((o) => o.path === lock && o.disposition === 'team-preserved' && o.applied);
268
- const before = prior ? prior.base : probe(root, lock).side;
269
268
  const backup = path.join(session, 'backup', lock);
269
+ // The preimage is what the lockfile was before this update's install, and the first run wrote it here before running
270
+ // one. A resumed run that probed the project instead takes whatever a killed install left behind as "ours", and an
271
+ // abort would then put a half-written lockfile back in place of the team's.
272
+ const before = prior ? prior.base : fs.existsSync(backup) ? sideOf(sha(fs.readFileSync(backup))) : probe(root, lock).side;
270
273
  if (before.exists && !fs.existsSync(backup))
271
274
  writeAtomic(backup, fs.readFileSync(inside(root, lock)));
272
275
  const t0 = performance.now();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zz-meridian",
3
- "version": "0.15.0",
3
+ "version": "0.16.1",
4
4
  "description": "Bring ZZ Meridian, a dashboard design system, into a Next.js project, or start a new dashboard on it. Copies the files in; nothing depends on this package at runtime.",
5
5
  "license": "MIT",
6
6
  "type": "module",