@1agh/maude 1.0.9 → 1.2.0

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 (506) hide show
  1. package/apps/studio/acp/bridge.ts +18 -13
  2. package/apps/studio/acp/probe.ts +268 -16
  3. package/apps/studio/acp/transcript-io.ts +394 -0
  4. package/apps/studio/acp/transcript.ts +103 -38
  5. package/apps/studio/api.ts +139 -26
  6. package/apps/studio/bin/_import-figma.mjs +57 -11
  7. package/apps/studio/bin/_seed-fixture.mjs +163 -0
  8. package/apps/studio/canvas-imports.ts +89 -0
  9. package/apps/studio/client/app.jsx +257 -7
  10. package/apps/studio/client/export-center.jsx +15 -7
  11. package/apps/studio/client/panels/ReadinessList.jsx +7 -1
  12. package/apps/studio/client/panels/SyncPanel.jsx +125 -4
  13. package/apps/studio/client/report-bug.jsx +19 -1
  14. package/apps/studio/client/styles/3-shell-maude.css +34 -0
  15. package/apps/studio/client/styles/4-components.css +26 -0
  16. package/apps/studio/collab/persistence.ts +132 -16
  17. package/apps/studio/collab/registry.ts +10 -18
  18. package/apps/studio/debug-bundle.ts +13 -0
  19. package/apps/studio/dist/client.bundle.js +1128 -1128
  20. package/apps/studio/dist/styles.css +1 -1
  21. package/apps/studio/export-dialog.tsx +54 -0
  22. package/apps/studio/exporters/degraded.ts +19 -3
  23. package/apps/studio/exporters/ghostscript.ts +194 -0
  24. package/apps/studio/exporters/pdf-fonts.ts +261 -0
  25. package/apps/studio/exporters/pdf.ts +152 -3
  26. package/apps/studio/exporters/remote.ts +36 -1
  27. package/apps/studio/fs-watch.ts +45 -1
  28. package/apps/studio/http.ts +7 -1
  29. package/apps/studio/mem.ts +45 -9
  30. package/apps/studio/readiness.ts +42 -7
  31. package/apps/studio/server.ts +11 -4
  32. package/apps/studio/sync/agent.ts +136 -29
  33. package/apps/studio/sync/asset-push.ts +1 -47
  34. package/apps/studio/sync/codec.ts +122 -31
  35. package/apps/studio/sync/cold-start-apply.ts +7 -0
  36. package/apps/studio/sync/cold-start.ts +118 -22
  37. package/apps/studio/sync/comment-identity.ts +68 -0
  38. package/apps/studio/sync/connection-state.ts +15 -1
  39. package/apps/studio/sync/file-ledger.ts +117 -0
  40. package/apps/studio/sync/file-plane.ts +623 -27
  41. package/apps/studio/sync/index.ts +711 -20
  42. package/apps/studio/sync/journal-client.ts +13 -1
  43. package/apps/studio/sync/migrate-seed.ts +40 -3
  44. package/apps/studio/sync/presentation.ts +26 -3
  45. package/apps/studio/sync/retry-after.ts +169 -0
  46. package/apps/studio/sync/seed-progress.ts +218 -0
  47. package/apps/studio/sync/status.ts +122 -5
  48. package/apps/studio/sync/transport-error.ts +111 -0
  49. package/apps/studio/whats-new.json +91 -0
  50. package/cli/bin/maude.mjs +2 -0
  51. package/cli/commands/codex.mjs +191 -0
  52. package/cli/commands/harness.mjs +1051 -0
  53. package/cli/commands/help.mjs +13 -0
  54. package/cli/lib/design-link.mjs +190 -2
  55. package/cli/lib/harness/capabilities.mjs +302 -0
  56. package/cli/lib/harness/codex-runtime.mjs +889 -0
  57. package/cli/lib/harness/compatibility.mjs +27 -0
  58. package/cli/lib/harness/discover-claude.mjs +1393 -0
  59. package/cli/lib/harness/managed-state.mjs +1296 -0
  60. package/cli/lib/harness/model.mjs +86 -0
  61. package/cli/lib/harness/secrets.mjs +390 -0
  62. package/cli/lib/harness/targets/codex.mjs +1141 -0
  63. package/cli/lib/harness/targets/opencode.mjs +807 -0
  64. package/cli/lib/harness/transaction.mjs +1099 -0
  65. package/cli/lib/hubs-config.mjs +35 -8
  66. package/cli/templates/harness/opencode/maude-projector.ts +58 -0
  67. package/docs/harness-capability-matrix.md +125 -0
  68. package/docs/harness-environment-projection.md +95 -0
  69. package/package.json +32 -9
  70. package/plugins/flow/.claude-plugin/config.schema.json +8 -0
  71. package/apps/studio/.ai/cache/_stats.json +0 -7
  72. package/apps/studio/bin/_agent-browser-safe.test.mjs +0 -165
  73. package/apps/studio/bin/_curl-local.test.mjs +0 -280
  74. package/apps/studio/bin/_pw-launch.test.mjs +0 -90
  75. package/apps/studio/bin/_smart-frames.test.mjs +0 -195
  76. package/apps/studio/bin/_transcribe.test.mjs +0 -80
  77. package/apps/studio/bun.lock +0 -928
  78. package/apps/studio/draw/test/animate.test.ts +0 -172
  79. package/apps/studio/draw/test/brush.test.ts +0 -213
  80. package/apps/studio/draw/test/composition.test.ts +0 -141
  81. package/apps/studio/draw/test/geometry.test.ts +0 -199
  82. package/apps/studio/draw/test/gradient.test.ts +0 -167
  83. package/apps/studio/draw/test/layout.test.ts +0 -120
  84. package/apps/studio/draw/test/morph.test.ts +0 -107
  85. package/apps/studio/draw/test/optimize.test.ts +0 -40
  86. package/apps/studio/draw/test/palette.test.ts +0 -123
  87. package/apps/studio/draw/test/primitives.test.ts +0 -129
  88. package/apps/studio/draw/test/serialize-animate.test.ts +0 -142
  89. package/apps/studio/draw/test/serialize.test.ts +0 -105
  90. package/apps/studio/figma/assets.test.ts +0 -464
  91. package/apps/studio/figma/client.test.ts +0 -395
  92. package/apps/studio/figma/codegen-client.test.ts +0 -276
  93. package/apps/studio/figma/codegen-fonts.test.ts +0 -103
  94. package/apps/studio/figma/codegen-values.test.ts +0 -179
  95. package/apps/studio/figma/comments-to-strokes.test.ts +0 -194
  96. package/apps/studio/figma/fig-decode.test.ts +0 -788
  97. package/apps/studio/figma/fig-differential.test.ts +0 -182
  98. package/apps/studio/figma/fig-translator.test.ts +0 -192
  99. package/apps/studio/figma/fig-vector.test.ts +0 -113
  100. package/apps/studio/figma/from-codegen.test.ts +0 -408
  101. package/apps/studio/figma/sanitize.test.ts +0 -325
  102. package/apps/studio/figma/tailwind-map.test.ts +0 -142
  103. package/apps/studio/figma/to-artboard.test.ts +0 -808
  104. package/apps/studio/figma/to-render.test.ts +0 -180
  105. package/apps/studio/figma/to-strokes-roundtrip.test.ts +0 -152
  106. package/apps/studio/figma/to-strokes.test.ts +0 -705
  107. package/apps/studio/figma/to-tokens.test.ts +0 -321
  108. package/apps/studio/figma/url.test.ts +0 -167
  109. package/apps/studio/footage/schema.test.ts +0 -287
  110. package/apps/studio/generation/adapters/groq.test.ts +0 -80
  111. package/apps/studio/generation/audio-library.test.ts +0 -95
  112. package/apps/studio/generation/captions.test.ts +0 -78
  113. package/apps/studio/generation/elevenlabs.test.ts +0 -223
  114. package/apps/studio/generation/gemini.test.ts +0 -350
  115. package/apps/studio/generation/jobs.test.ts +0 -128
  116. package/apps/studio/generation/keys.test.ts +0 -75
  117. package/apps/studio/generation/prefs.test.ts +0 -95
  118. package/apps/studio/generation/types.test.ts +0 -123
  119. package/apps/studio/generation/whisper-models.test.ts +0 -179
  120. package/apps/studio/test/_helpers.ts +0 -108
  121. package/apps/studio/test/acp-activity-endpoint.test.ts +0 -183
  122. package/apps/studio/test/acp-activity.test.ts +0 -154
  123. package/apps/studio/test/acp-ai-activity.test.ts +0 -182
  124. package/apps/studio/test/acp-attachment-serve.test.ts +0 -74
  125. package/apps/studio/test/acp-bootstrap-brief.test.ts +0 -178
  126. package/apps/studio/test/acp-branch-guard.test.ts +0 -123
  127. package/apps/studio/test/acp-bridge-lifetime.test.ts +0 -475
  128. package/apps/studio/test/acp-bridge.test.ts +0 -301
  129. package/apps/studio/test/acp-capabilities.test.ts +0 -123
  130. package/apps/studio/test/acp-caps-bridge.test.ts +0 -279
  131. package/apps/studio/test/acp-commands.test.ts +0 -119
  132. package/apps/studio/test/acp-elicitation-bridge.test.ts +0 -480
  133. package/apps/studio/test/acp-elicitation.test.ts +0 -251
  134. package/apps/studio/test/acp-env.test.ts +0 -95
  135. package/apps/studio/test/acp-mode-banner.test.ts +0 -45
  136. package/apps/studio/test/acp-origin-gate.test.ts +0 -160
  137. package/apps/studio/test/acp-permission-prompt.test.ts +0 -251
  138. package/apps/studio/test/acp-permission.test.ts +0 -278
  139. package/apps/studio/test/acp-plugin-bootstrap.test.ts +0 -89
  140. package/apps/studio/test/acp-session-allowed-tools.test.ts +0 -235
  141. package/apps/studio/test/acp-session-plugins.test.ts +0 -209
  142. package/apps/studio/test/acp-toolgroup.test.ts +0 -76
  143. package/apps/studio/test/acp-transcript-view.test.ts +0 -71
  144. package/apps/studio/test/acp-transcript.test.ts +0 -238
  145. package/apps/studio/test/acp-usage-bridge.test.ts +0 -141
  146. package/apps/studio/test/acp-usage.test.ts +0 -143
  147. package/apps/studio/test/acp-write-gate.test.ts +0 -323
  148. package/apps/studio/test/acp-write-scope.test.ts +0 -533
  149. package/apps/studio/test/active-state.test.ts +0 -186
  150. package/apps/studio/test/activity.test.ts +0 -189
  151. package/apps/studio/test/ai-activity.test.ts +0 -113
  152. package/apps/studio/test/annotate-write.test.ts +0 -1074
  153. package/apps/studio/test/annotation-strokes-command.test.ts +0 -166
  154. package/apps/studio/test/annotations-align.test.ts +0 -88
  155. package/apps/studio/test/annotations-api.test.ts +0 -145
  156. package/apps/studio/test/annotations-bindings.test.ts +0 -262
  157. package/apps/studio/test/annotations-draw-modifiers.test.ts +0 -206
  158. package/apps/studio/test/annotations-groups.test.ts +0 -231
  159. package/apps/studio/test/annotations-layer.test.ts +0 -1011
  160. package/apps/studio/test/annotations-roundtrip.test.ts +0 -663
  161. package/apps/studio/test/annotations-snap.test.ts +0 -135
  162. package/apps/studio/test/annotations-text-format.test.ts +0 -396
  163. package/apps/studio/test/artboard-activity-overlay.test.tsx +0 -56
  164. package/apps/studio/test/artboard-guides-overlay.test.tsx +0 -152
  165. package/apps/studio/test/artboard-hug-commit.test.ts +0 -36
  166. package/apps/studio/test/artboard-kinds.test.tsx +0 -83
  167. package/apps/studio/test/artboard-selection-attrs.test.ts +0 -69
  168. package/apps/studio/test/asset-api.test.ts +0 -149
  169. package/apps/studio/test/assets-s3.test.ts +0 -249
  170. package/apps/studio/test/binary-smoke.test.ts +0 -47
  171. package/apps/studio/test/boot-self-heal.test.ts +0 -87
  172. package/apps/studio/test/browse-posture.test.tsx +0 -107
  173. package/apps/studio/test/bundle-smoke.test.ts +0 -46
  174. package/apps/studio/test/camera-reveal.test.tsx +0 -173
  175. package/apps/studio/test/canvas-artifacts.test.ts +0 -125
  176. package/apps/studio/test/canvas-build-sandbox.test.ts +0 -69
  177. package/apps/studio/test/canvas-build.test.ts +0 -141
  178. package/apps/studio/test/canvas-create-api.test.ts +0 -676
  179. package/apps/studio/test/canvas-cursors.test.ts +0 -164
  180. package/apps/studio/test/canvas-edit.test.ts +0 -470
  181. package/apps/studio/test/canvas-freshness-deps.test.ts +0 -64
  182. package/apps/studio/test/canvas-header.test.ts +0 -127
  183. package/apps/studio/test/canvas-hide-chrome.test.ts +0 -88
  184. package/apps/studio/test/canvas-hmr-runtime.test.tsx +0 -114
  185. package/apps/studio/test/canvas-lib-inline.test.ts +0 -146
  186. package/apps/studio/test/canvas-lib-motion.test.ts +0 -131
  187. package/apps/studio/test/canvas-lib-resolver.test.ts +0 -111
  188. package/apps/studio/test/canvas-list-watch.test.ts +0 -388
  189. package/apps/studio/test/canvas-media-drop.test.ts +0 -187
  190. package/apps/studio/test/canvas-meta-api.test.ts +0 -707
  191. package/apps/studio/test/canvas-move-api.test.ts +0 -587
  192. package/apps/studio/test/canvas-origin-gate.test.ts +0 -417
  193. package/apps/studio/test/canvas-pipeline.test.ts +0 -180
  194. package/apps/studio/test/canvas-rects.test.ts +0 -198
  195. package/apps/studio/test/canvas-reorder.test.ts +0 -211
  196. package/apps/studio/test/canvas-route.test.ts +0 -179
  197. package/apps/studio/test/canvas-shell-base.test.ts +0 -107
  198. package/apps/studio/test/canvas-shell-build-error.test.ts +0 -52
  199. package/apps/studio/test/canvas-url.test.ts +0 -138
  200. package/apps/studio/test/canvas-wheel-delta.test.ts +0 -42
  201. package/apps/studio/test/canvas-zoom-floor.test.ts +0 -73
  202. package/apps/studio/test/capture-core.test.ts +0 -32
  203. package/apps/studio/test/capture-determinism-shape.test.ts +0 -135
  204. package/apps/studio/test/chat-attachments.test.ts +0 -117
  205. package/apps/studio/test/chat-context.test.ts +0 -129
  206. package/apps/studio/test/chat-markdown.test.tsx +0 -58
  207. package/apps/studio/test/clip-addressing.test.ts +0 -738
  208. package/apps/studio/test/clip-ops.test.ts +0 -922
  209. package/apps/studio/test/cloud-build.test.ts +0 -95
  210. package/apps/studio/test/cloud-connect-note.test.ts +0 -135
  211. package/apps/studio/test/cloud-endpoints.test.ts +0 -590
  212. package/apps/studio/test/cloud-history-hardening.test.ts +0 -165
  213. package/apps/studio/test/cloud-history-posture.test.ts +0 -230
  214. package/apps/studio/test/cloud-managed-save-surfaces.test.ts +0 -90
  215. package/apps/studio/test/cloud-renew.test.ts +0 -205
  216. package/apps/studio/test/cloud-session-role.test.ts +0 -161
  217. package/apps/studio/test/cloud-shell-surfaces.test.ts +0 -170
  218. package/apps/studio/test/cold-start-apply.test.ts +0 -303
  219. package/apps/studio/test/collab-annotations-bridge.test.ts +0 -55
  220. package/apps/studio/test/collab-awareness-bridge.test.ts +0 -223
  221. package/apps/studio/test/collab-bridge.test.ts +0 -79
  222. package/apps/studio/test/collab-loopback.test.ts +0 -63
  223. package/apps/studio/test/collab-origin-gate.test.ts +0 -323
  224. package/apps/studio/test/collab-protocol.test.ts +0 -146
  225. package/apps/studio/test/collab-readonly-gate.test.ts +0 -194
  226. package/apps/studio/test/collab-reseed-guard.test.ts +0 -78
  227. package/apps/studio/test/collab-room.test.ts +0 -188
  228. package/apps/studio/test/collab-session-survival.test.tsx +0 -176
  229. package/apps/studio/test/collab-stress.test.ts +0 -129
  230. package/apps/studio/test/comment-mount.test.ts +0 -97
  231. package/apps/studio/test/comment-relay-origin-gate.test.ts +0 -117
  232. package/apps/studio/test/comments-api.test.ts +0 -277
  233. package/apps/studio/test/comments-fs-rebroadcast.test.ts +0 -155
  234. package/apps/studio/test/comments-overlay.test.ts +0 -117
  235. package/apps/studio/test/compile-entry.test.ts +0 -133
  236. package/apps/studio/test/component-map.test.ts +0 -48
  237. package/apps/studio/test/config-projection.test.ts +0 -104
  238. package/apps/studio/test/config-reload.test.ts +0 -230
  239. package/apps/studio/test/config-version.test.ts +0 -88
  240. package/apps/studio/test/context-resolve-tokens.test.ts +0 -97
  241. package/apps/studio/test/convert-to-absolute.test.ts +0 -333
  242. package/apps/studio/test/credential-grammar.test.ts +0 -107
  243. package/apps/studio/test/csp-canvas-shell.test.ts +0 -62
  244. package/apps/studio/test/csrf-write-guard.test.ts +0 -141
  245. package/apps/studio/test/debug-bundle.test.ts +0 -105
  246. package/apps/studio/test/design-setup-readiness.test.ts +0 -155
  247. package/apps/studio/test/detach-component.test.ts +0 -94
  248. package/apps/studio/test/dns-rebinding-guard.test.ts +0 -101
  249. package/apps/studio/test/dom-selection.test.ts +0 -198
  250. package/apps/studio/test/dynamic-text-edit.test.ts +0 -162
  251. package/apps/studio/test/edit-css-occurrence.test.ts +0 -81
  252. package/apps/studio/test/edit-persistence.test.ts +0 -91
  253. package/apps/studio/test/edit-scope-api.test.ts +0 -119
  254. package/apps/studio/test/edit-source-command.test.ts +0 -142
  255. package/apps/studio/test/edit-suppress.test.ts +0 -170
  256. package/apps/studio/test/editing-presence.test.ts +0 -103
  257. package/apps/studio/test/element-resize.test.ts +0 -136
  258. package/apps/studio/test/element-structural-api.test.ts +0 -434
  259. package/apps/studio/test/element-structural-edit.test.ts +0 -750
  260. package/apps/studio/test/ensure-browser.test.ts +0 -92
  261. package/apps/studio/test/equal-spacing-detector.test.ts +0 -257
  262. package/apps/studio/test/export-assemble.test.ts +0 -53
  263. package/apps/studio/test/export-browser-lane.test.ts +0 -126
  264. package/apps/studio/test/export-capture-fidelity.test.ts +0 -146
  265. package/apps/studio/test/export-capture-hygiene.test.ts +0 -675
  266. package/apps/studio/test/export-center.test.tsx +0 -287
  267. package/apps/studio/test/export-e2e-lanes.test.ts +0 -857
  268. package/apps/studio/test/export-format-scope-coherence.test.ts +0 -89
  269. package/apps/studio/test/export-lane.test.ts +0 -245
  270. package/apps/studio/test/export-shim-multi-capture.test.ts +0 -83
  271. package/apps/studio/test/exporters/canva.test.ts +0 -63
  272. package/apps/studio/test/exporters/degraded-propagation.test.ts +0 -123
  273. package/apps/studio/test/exporters/endpoint.test.ts +0 -121
  274. package/apps/studio/test/exporters/history.test.ts +0 -107
  275. package/apps/studio/test/exporters/html.test.ts +0 -26
  276. package/apps/studio/test/exporters/jobs.test.ts +0 -342
  277. package/apps/studio/test/exporters/pdf.test.ts +0 -85
  278. package/apps/studio/test/exporters/png.test.ts +0 -103
  279. package/apps/studio/test/exporters/pptx-deck.test.ts +0 -112
  280. package/apps/studio/test/exporters/pptx.test.ts +0 -31
  281. package/apps/studio/test/exporters/pw-launch.test.ts +0 -49
  282. package/apps/studio/test/exporters/runtime.test.ts +0 -59
  283. package/apps/studio/test/exporters/scope.test.ts +0 -292
  284. package/apps/studio/test/exporters/svg.test.ts +0 -29
  285. package/apps/studio/test/exporters/unsupported-media.test.ts +0 -123
  286. package/apps/studio/test/exporters/zip.test.ts +0 -104
  287. package/apps/studio/test/fetch-asset-gate.test.ts +0 -189
  288. package/apps/studio/test/figjam-v3-model.test.ts +0 -342
  289. package/apps/studio/test/figma-explode.test.ts +0 -438
  290. package/apps/studio/test/figma-provenance.test.ts +0 -108
  291. package/apps/studio/test/figma-routes.test.ts +0 -294
  292. package/apps/studio/test/file-lock.test.ts +0 -84
  293. package/apps/studio/test/fixtures/fake-claude-auth.mjs +0 -13
  294. package/apps/studio/test/fixtures/figjam-v3-groups-bindings.svg +0 -1
  295. package/apps/studio/test/fixtures/mock-acp-agent-caps.mjs +0 -158
  296. package/apps/studio/test/fixtures/mock-acp-agent-commands.mjs +0 -44
  297. package/apps/studio/test/fixtures/mock-acp-agent-elicit-flood.mjs +0 -46
  298. package/apps/studio/test/fixtures/mock-acp-agent-elicit-url.mjs +0 -42
  299. package/apps/studio/test/fixtures/mock-acp-agent-elicit.mjs +0 -63
  300. package/apps/studio/test/fixtures/mock-acp-agent-permission-flood.mjs +0 -51
  301. package/apps/studio/test/fixtures/mock-acp-agent-permission.mjs +0 -50
  302. package/apps/studio/test/fixtures/mock-acp-agent-slow.mjs +0 -45
  303. package/apps/studio/test/fixtures/mock-acp-agent-usage.mjs +0 -56
  304. package/apps/studio/test/fixtures/mock-acp-agent-wedged.mjs +0 -37
  305. package/apps/studio/test/fixtures/mock-acp-agent-write.mjs +0 -118
  306. package/apps/studio/test/fixtures/mock-acp-agent.mjs +0 -69
  307. package/apps/studio/test/fixtures/perf-canvas.mjs +0 -201
  308. package/apps/studio/test/fixtures/phase-20-annotations.svg +0 -1
  309. package/apps/studio/test/fixtures/phase-21-annotations.svg +0 -1
  310. package/apps/studio/test/fixtures/tiny-subset.woff2 +0 -0
  311. package/apps/studio/test/fixtures/video-comp-fixture.tsx +0 -82
  312. package/apps/studio/test/footage-store.test.ts +0 -100
  313. package/apps/studio/test/fs-mkdir-api.test.ts +0 -248
  314. package/apps/studio/test/fs-watch.test.ts +0 -41
  315. package/apps/studio/test/gemma-models.test.ts +0 -245
  316. package/apps/studio/test/generate-route.test.ts +0 -106
  317. package/apps/studio/test/git-api.test.ts +0 -0
  318. package/apps/studio/test/git-branches.test.ts +0 -341
  319. package/apps/studio/test/git-cloud-posture.test.ts +0 -59
  320. package/apps/studio/test/git-grouping.test.ts +0 -106
  321. package/apps/studio/test/git-lifecycle.test.ts +0 -100
  322. package/apps/studio/test/git-log-format.test.ts +0 -95
  323. package/apps/studio/test/git-safe-rel.test.ts +0 -132
  324. package/apps/studio/test/git-system-engine.test.ts +0 -135
  325. package/apps/studio/test/git-watch.test.ts +0 -97
  326. package/apps/studio/test/github-api.test.ts +0 -577
  327. package/apps/studio/test/grid-track-handles.test.ts +0 -160
  328. package/apps/studio/test/handoff-static-frames.test.ts +0 -215
  329. package/apps/studio/test/handoff.test.ts +0 -329
  330. package/apps/studio/test/history-rollback.test.ts +0 -88
  331. package/apps/studio/test/hmr-broadcast.test.ts +0 -236
  332. package/apps/studio/test/hmr-classify.test.ts +0 -87
  333. package/apps/studio/test/hub-link.test.ts +0 -80
  334. package/apps/studio/test/identity-cache.test.ts +0 -83
  335. package/apps/studio/test/import-asset-browser.test.ts +0 -64
  336. package/apps/studio/test/import-asset-route.test.ts +0 -71
  337. package/apps/studio/test/import-asset.test.ts +0 -427
  338. package/apps/studio/test/import-brand-browser.test.ts +0 -88
  339. package/apps/studio/test/import-brand-route.test.ts +0 -120
  340. package/apps/studio/test/import-brand.test.ts +0 -206
  341. package/apps/studio/test/import-figma.test.ts +0 -667
  342. package/apps/studio/test/import-tokens.test.ts +0 -722
  343. package/apps/studio/test/index-data-canvas-ds.test.ts +0 -97
  344. package/apps/studio/test/input-router.test.ts +0 -757
  345. package/apps/studio/test/inspect-script-syntax.test.ts +0 -53
  346. package/apps/studio/test/inspect-selections.test.ts +0 -219
  347. package/apps/studio/test/knob-props-authored.test.ts +0 -87
  348. package/apps/studio/test/layers-synthetic-groups.test.ts +0 -96
  349. package/apps/studio/test/locator.test.ts +0 -214
  350. package/apps/studio/test/marquee-overlay.test.ts +0 -94
  351. package/apps/studio/test/media-commit-chain.test.ts +0 -210
  352. package/apps/studio/test/mode-toggle.test.tsx +0 -226
  353. package/apps/studio/test/motion-sample.test.ts +0 -35
  354. package/apps/studio/test/move-artboards-command.test.ts +0 -108
  355. package/apps/studio/test/participants-chrome.test.ts +0 -65
  356. package/apps/studio/test/paths.test.ts +0 -88
  357. package/apps/studio/test/pdf-print-boxes.test.ts +0 -359
  358. package/apps/studio/test/peer-selection-follows-camera.test.tsx +0 -131
  359. package/apps/studio/test/perf-harness.ts +0 -193
  360. package/apps/studio/test/phase-3.6-smoke.test.ts +0 -79
  361. package/apps/studio/test/photo-bg-remove-validation.test.ts +0 -71
  362. package/apps/studio/test/photo-canvas-bundle.test.ts +0 -79
  363. package/apps/studio/test/photo-edit-api.test.ts +0 -201
  364. package/apps/studio/test/photo-filters.test.ts +0 -149
  365. package/apps/studio/test/photo-pipeline.test.ts +0 -106
  366. package/apps/studio/test/photo-taxonomy.test.ts +0 -96
  367. package/apps/studio/test/print-marks.test.ts +0 -113
  368. package/apps/studio/test/print-units.test.ts +0 -173
  369. package/apps/studio/test/read-annotations.test.ts +0 -727
  370. package/apps/studio/test/read-only-gate.test.ts +0 -188
  371. package/apps/studio/test/read-only-role.test.ts +0 -95
  372. package/apps/studio/test/readiness.test.ts +0 -226
  373. package/apps/studio/test/remote-ahead-behind-cache.test.ts +0 -89
  374. package/apps/studio/test/reorder-api.test.ts +0 -210
  375. package/apps/studio/test/repo-concurrency.test.ts +0 -146
  376. package/apps/studio/test/repo-lock.test.ts +0 -350
  377. package/apps/studio/test/report-proxy.test.ts +0 -125
  378. package/apps/studio/test/ripple.test.ts +0 -197
  379. package/apps/studio/test/runtime-bundle-error-mapping.test.ts +0 -43
  380. package/apps/studio/test/runtime-bundle.test.ts +0 -69
  381. package/apps/studio/test/sanitize-annotation-svg.test.ts +0 -238
  382. package/apps/studio/test/scaffold-design.test.ts +0 -122
  383. package/apps/studio/test/server-lifecycle.test.ts +0 -38
  384. package/apps/studio/test/session-runtime-state.test.ts +0 -226
  385. package/apps/studio/test/session-scope.test.ts +0 -100
  386. package/apps/studio/test/shared-doc-cell-pairing.test.ts +0 -642
  387. package/apps/studio/test/shared-doc-convergence.test.ts +0 -265
  388. package/apps/studio/test/shared-doc-foundation.test.ts +0 -63
  389. package/apps/studio/test/shared-doc-migrate.test.ts +0 -369
  390. package/apps/studio/test/shared-doc-projection.test.ts +0 -238
  391. package/apps/studio/test/shell-importmap.test.ts +0 -56
  392. package/apps/studio/test/sizing-mode.test.ts +0 -102
  393. package/apps/studio/test/slash-commands.test.ts +0 -117
  394. package/apps/studio/test/snap-distance-pill.test.ts +0 -68
  395. package/apps/studio/test/spacing-handles.test.ts +0 -138
  396. package/apps/studio/test/specimen-select.test.ts +0 -88
  397. package/apps/studio/test/sync-agent.test.ts +0 -660
  398. package/apps/studio/test/sync-annotations-cold-start.test.ts +0 -302
  399. package/apps/studio/test/sync-asset-push.test.ts +0 -931
  400. package/apps/studio/test/sync-atomic-write.test.ts +0 -62
  401. package/apps/studio/test/sync-attach-incremental.test.ts +0 -527
  402. package/apps/studio/test/sync-autocommit.test.ts +0 -537
  403. package/apps/studio/test/sync-canvas-path.test.ts +0 -200
  404. package/apps/studio/test/sync-cell-write-nudge.test.ts +0 -346
  405. package/apps/studio/test/sync-codec.test.ts +0 -230
  406. package/apps/studio/test/sync-cold-seed-dedup.test.ts +0 -187
  407. package/apps/studio/test/sync-cold-start.test.ts +0 -304
  408. package/apps/studio/test/sync-connect-honesty.test.ts +0 -114
  409. package/apps/studio/test/sync-connection-state.test.ts +0 -337
  410. package/apps/studio/test/sync-ctl-channel.test.ts +0 -508
  411. package/apps/studio/test/sync-decide-file.test.ts +0 -420
  412. package/apps/studio/test/sync-doc-name.test.ts +0 -281
  413. package/apps/studio/test/sync-echo-guard.test.ts +0 -96
  414. package/apps/studio/test/sync-file-ledger.test.ts +0 -334
  415. package/apps/studio/test/sync-file-membership.test.ts +0 -347
  416. package/apps/studio/test/sync-file-plane.test.ts +0 -976
  417. package/apps/studio/test/sync-file-pull.test.ts +0 -333
  418. package/apps/studio/test/sync-fresh-link-parity.test.ts +0 -267
  419. package/apps/studio/test/sync-fs-mirror.test.ts +0 -181
  420. package/apps/studio/test/sync-hardening.test.ts +0 -586
  421. package/apps/studio/test/sync-hub-listing.test.ts +0 -46
  422. package/apps/studio/test/sync-hubs-config.test.ts +0 -133
  423. package/apps/studio/test/sync-incident-replay.test.ts +0 -211
  424. package/apps/studio/test/sync-journal.test.ts +0 -176
  425. package/apps/studio/test/sync-meta-codec.test.ts +0 -199
  426. package/apps/studio/test/sync-migrate-flat-fallback.test.ts +0 -98
  427. package/apps/studio/test/sync-move-retirement.test.ts +0 -231
  428. package/apps/studio/test/sync-panel-surface.test.ts +0 -153
  429. package/apps/studio/test/sync-path-pull.test.ts +0 -531
  430. package/apps/studio/test/sync-presentation.test.ts +0 -262
  431. package/apps/studio/test/sync-pull-budget.test.ts +0 -169
  432. package/apps/studio/test/sync-remote-docs.test.ts +0 -333
  433. package/apps/studio/test/sync-resync-routes.test.ts +0 -168
  434. package/apps/studio/test/sync-runtime.test.ts +0 -1675
  435. package/apps/studio/test/sync-seed-defers-to-hub.test.ts +0 -83
  436. package/apps/studio/test/sync-settings-routes.test.ts +0 -195
  437. package/apps/studio/test/sync-settings.test.ts +0 -151
  438. package/apps/studio/test/sync-status.test.ts +0 -261
  439. package/apps/studio/test/sync-supervisor.test.ts +0 -262
  440. package/apps/studio/test/sync-tombstone-apply.test.ts +0 -111
  441. package/apps/studio/test/sync-trash.test.ts +0 -132
  442. package/apps/studio/test/sync-two-peer-discovery.test.ts +0 -343
  443. package/apps/studio/test/sync-untrusted.test.ts +0 -103
  444. package/apps/studio/test/system-endpoint.test.ts +0 -144
  445. package/apps/studio/test/text-editability-stamp.test.ts +0 -131
  446. package/apps/studio/test/timeline-comp-target.test.ts +0 -139
  447. package/apps/studio/test/timeline-media-cache.test.ts +0 -41
  448. package/apps/studio/test/timeline-parse.test.ts +0 -184
  449. package/apps/studio/test/timeline-scale.test.ts +0 -65
  450. package/apps/studio/test/timeline-snap.test.ts +0 -85
  451. package/apps/studio/test/tool-palette-insert-anchor.test.ts +0 -86
  452. package/apps/studio/test/tool-palette.test.tsx +0 -55
  453. package/apps/studio/test/tour-overlay.test.tsx +0 -90
  454. package/apps/studio/test/trusted-request-host.test.ts +0 -66
  455. package/apps/studio/test/undo-sequence-byte-compare.test.ts +0 -211
  456. package/apps/studio/test/undo-stack.test.ts +0 -244
  457. package/apps/studio/test/use-agent-presence.test.tsx +0 -117
  458. package/apps/studio/test/use-annotation-resize.test.ts +0 -402
  459. package/apps/studio/test/use-annotation-selection.test.tsx +0 -77
  460. package/apps/studio/test/use-artboard-drag.test.ts +0 -324
  461. package/apps/studio/test/use-canvas-activity.test.tsx +0 -206
  462. package/apps/studio/test/use-collab.test.ts +0 -0
  463. package/apps/studio/test/use-cursor-modifiers.test.ts +0 -59
  464. package/apps/studio/test/use-keyboard-discipline.test.ts +0 -27
  465. package/apps/studio/test/use-selection-set.test.tsx +0 -166
  466. package/apps/studio/test/use-snap-guides.test.ts +0 -271
  467. package/apps/studio/test/use-tool-mode.test.tsx +0 -186
  468. package/apps/studio/test/use-undo-stack.test.tsx +0 -223
  469. package/apps/studio/test/video-asset.test.ts +0 -163
  470. package/apps/studio/test/video-comp-fixture.test.ts +0 -50
  471. package/apps/studio/test/video-comp.test.ts +0 -270
  472. package/apps/studio/test/video-encode-lib.test.ts +0 -63
  473. package/apps/studio/test/video-render-bridge.test.ts +0 -171
  474. package/apps/studio/test/whats-new.test.ts +0 -113
  475. package/apps/studio/test/whisper-setup.test.ts +0 -97
  476. package/apps/studio/test/workspace-containment.test.ts +0 -382
  477. package/apps/studio/test/workspace-signin.test.ts +0 -292
  478. package/apps/studio/test/ws-handshake.test.ts +0 -33
  479. package/cli/commands/cache.test.mjs +0 -166
  480. package/cli/commands/design-link.test.mjs +0 -419
  481. package/cli/commands/design.test.mjs +0 -240
  482. package/cli/commands/doctor.test.mjs +0 -237
  483. package/cli/commands/hub-workspace.test.mjs +0 -275
  484. package/cli/commands/hub.test.mjs +0 -225
  485. package/cli/commands/kg.test.mjs +0 -142
  486. package/cli/commands/preflight.test.mjs +0 -46
  487. package/cli/lib/argv.test.mjs +0 -46
  488. package/cli/lib/cache.test.mjs +0 -303
  489. package/cli/lib/cell-plan.test.mjs +0 -225
  490. package/cli/lib/config-lint.test.mjs +0 -117
  491. package/cli/lib/ddr-to-kgai.test.mjs +0 -118
  492. package/cli/lib/design-ownership.test.mjs +0 -329
  493. package/cli/lib/fetch-asset.test.mjs +0 -213
  494. package/cli/lib/figma-codegen-reachability.test.mjs +0 -104
  495. package/cli/lib/figma-import-controls.test.mjs +0 -70
  496. package/cli/lib/flow-design-integration.test.mjs +0 -165
  497. package/cli/lib/gitignore-block.test.mjs +0 -136
  498. package/cli/lib/gitignore-drift.test.mjs +0 -156
  499. package/cli/lib/hubs-config.test.mjs +0 -165
  500. package/cli/lib/pkg-root.test.mjs +0 -111
  501. package/cli/lib/plugin-cli-reachability.test.mjs +0 -57
  502. package/cli/lib/plugin-name-namespace.test.mjs +0 -71
  503. package/cli/lib/reconstruct-toolset.test.mjs +0 -194
  504. package/cli/lib/stack-detect.test.mjs +0 -121
  505. package/cli/lib/update-check.test.mjs +0 -32
  506. package/cli/lib/workspace-plan.test.mjs +0 -699
@@ -1,188 +0,0 @@
1
- // Cloud Phase 25 C2 — the local half of the read-only gate.
2
- //
3
- // When the linked hub's credential carries the `viewer` role (vouched at
4
- // workspace sign-in, stored in hubs.json), the LOCAL dev-server refuses
5
- // project-mutating writes with a legible 403 — so a viewer's clone can never
6
- // silently diverge from the cell (which refuses the same writes at sync,
7
- // Phase 25 C1). Writes are default-denied with a short allowlist
8
- // (READ_ONLY_ALLOWED_WRITES in http.ts): per-user runtime state (camera,
9
- // ui-prefs) plus the two role-granted verbs the cell itself allows (export,
10
- // session management).
11
-
12
- import { afterAll, describe, expect, test } from 'bun:test';
13
- import { readFileSync, writeFileSync } from 'node:fs';
14
- import { join } from 'node:path';
15
- import type { Subprocess } from 'bun';
16
-
17
- import { bootServer, killProc, makeSandbox, nextPort } from './_helpers.ts';
18
-
19
- const HUB_URL = 'http://hub.invalid:9';
20
-
21
- /** Sandbox linked to a hub whose credential vouches `role`. */
22
- function makeLinkedSandbox(role?: string) {
23
- const { root, designRoot } = makeSandbox();
24
- const cfgPath = join(designRoot, 'config.json');
25
- const cfg = JSON.parse(readFileSync(cfgPath, 'utf8'));
26
- cfg.linkedHub = { url: HUB_URL, linkedAt: Date.now() };
27
- writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));
28
- // The hubs.json credential store the role is read from (isHubReadOnly).
29
- const hubsPath = join(root, 'hubs.json');
30
- writeFileSync(
31
- hubsPath,
32
- JSON.stringify({
33
- hubs: { [HUB_URL]: { token: 't', linkedAt: Date.now(), ...(role ? { role } : {}) } },
34
- })
35
- );
36
- return { root, designRoot, hubsPath };
37
- }
38
-
39
- const procs: Subprocess[] = [];
40
- afterAll(async () => {
41
- for (const p of procs) await killProc(p);
42
- });
43
-
44
- async function boot(role?: string) {
45
- const { root, hubsPath } = makeLinkedSandbox(role);
46
- const port = nextPort();
47
- const proc = await bootServer(root, port, { HUBS_CONFIG_PATH: hubsPath });
48
- procs.push(proc);
49
- return { base: `http://localhost:${port}` };
50
- }
51
-
52
- describe('read-only gate — viewer role refuses project writes locally', () => {
53
- test('viewer: writes 403 read-only, per-user + role-granted writes pass, /_config says so', async () => {
54
- const { base } = await boot('viewer');
55
-
56
- // /_config tells the client at boot.
57
- const cfg = (await (await fetch(`${base}/_config`)).json()) as { readOnly?: boolean };
58
- expect(cfg.readOnly).toBe(true);
59
-
60
- // Project-mutating writes: refused with the legible read-only shape.
61
- const editText = await fetch(`${base}/_api/edit-text`, {
62
- method: 'POST',
63
- headers: { 'content-type': 'application/json' },
64
- body: JSON.stringify({ file: 'ui/fixture.html', text: 'x' }),
65
- });
66
- expect(editText.status).toBe(403);
67
- const body = (await editText.json()) as { error?: string; message?: string };
68
- expect(body.error).toBe('read-only');
69
- expect(body.message).toContain('viewer');
70
-
71
- // Canvas create + delete-element + git commit: same refusal.
72
- for (const [path, method] of [
73
- ['/_api/canvas', 'POST'],
74
- ['/_api/delete-element', 'POST'],
75
- ['/_api/git/commit', 'POST'],
76
- ['/_api/fs-mkdir', 'POST'],
77
- ['/_api/annotations', 'PUT'],
78
- ['/_api/acp/chat', 'POST'],
79
- // Cloud Phase 27 D2 — both USED to be on READ_ONLY_ALLOWED_WRITES, on the
80
- // reasoning that "viewing another branch is not changing one". Both
81
- // rewrite the working TREE, and the tree is shared: a viewer switching
82
- // branches replaces the files under whoever is mid-edit. The cell's proxy
83
- // refuses them too, but this is the gate the hub-linked desktop and the
84
- // self-host path consult, where no manifest runs at all.
85
- ['/_api/git/checkout', 'POST'],
86
- ['/_api/git/pull', 'POST'],
87
- ] as const) {
88
- const r = await fetch(base + path, { method, body: '{}' });
89
- expect(`${path} ${r.status}`).toBe(`${path} 403`);
90
- }
91
-
92
- // canvas-meta: the LAYOUT lane is refused…
93
- const layout = await fetch(`${base}/_api/canvas-meta`, {
94
- method: 'PATCH',
95
- headers: { 'content-type': 'application/json' },
96
- body: JSON.stringify({
97
- file: '.design/ui/fixture.html',
98
- patch: { layout: { artboards: [] } },
99
- }),
100
- });
101
- expect(layout.status).toBe(403);
102
- // …the VIEWPORT lane (per-user camera) is not.
103
- const viewport = await fetch(`${base}/_api/canvas-meta`, {
104
- method: 'PATCH',
105
- headers: { 'content-type': 'application/json' },
106
- body: JSON.stringify({
107
- file: '.design/ui/fixture.html',
108
- patch: { viewport: { x: 0, y: 0, zoom: 1 } },
109
- }),
110
- });
111
- expect(viewport.status).not.toBe(403);
112
-
113
- // Per-user runtime state stays writable (DDR-115 taxonomy).
114
- const camera = await fetch(`${base}/_canvas-state`, {
115
- method: 'POST',
116
- headers: { 'content-type': 'application/json' },
117
- body: JSON.stringify({ file: '.design/ui/fixture.html', viewport: { x: 1, y: 2, zoom: 1 } }),
118
- });
119
- expect(camera.status).toBe(204);
120
-
121
- // Role-granted verb: export reaches its handler (may 400 on shape, never 403).
122
- const exp = await fetch(`${base}/_api/export`, { method: 'POST', body: '{}' });
123
- expect(exp.status).not.toBe(403);
124
-
125
- // Reads are untouched.
126
- expect((await fetch(`${base}/_index-data`)).status).toBe(200);
127
- expect((await fetch(`${base}/_comments-all`)).status).toBe(200);
128
- }, 30000);
129
-
130
- test('member (no role on the credential): writes are NOT gated', async () => {
131
- const { base } = await boot(undefined);
132
- const cfg = (await (await fetch(`${base}/_config`)).json()) as { readOnly?: boolean };
133
- expect(cfg.readOnly).toBe(false);
134
- // A write reaches its handler (400 on bad shape — not the 403 refusal).
135
- const r = await fetch(`${base}/_api/edit-text`, { method: 'POST', body: '{}' });
136
- expect(r.status).not.toBe(403);
137
- }, 30000);
138
- });
139
-
140
- // ---------------------------------------------------------------------------
141
- // Cloud Phase 27 — the one write a viewer holds.
142
- //
143
- // The role matrix has said `viewer.comment === true` since Phase 25 C4 and the
144
- // People page promises it in those words. This list did not have it, so a
145
- // viewer's comment was allowed by the authority (the cell's proxy) and then
146
- // refused by the defence-in-depth layer behind it. Found by commenting as a
147
- // viewer against a real cell, not by reading either file.
148
-
149
- describe('a viewer may comment — the matrix says so, and so must this gate', () => {
150
- test('comment + reply pass; annotate still does not', async () => {
151
- const { base } = await boot('viewer');
152
-
153
- const comment = await fetch(`${base}/_comments`, {
154
- method: 'POST',
155
- headers: { 'content-type': 'application/json' },
156
- body: JSON.stringify({ file: 'ui/Sample.tsx', text: 'looks off to me' }),
157
- });
158
- expect(comment.status).not.toBe(403);
159
-
160
- const reply = await fetch(`${base}/_api/comments/abc123/reply`, {
161
- method: 'POST',
162
- headers: { 'content-type': 'application/json' },
163
- body: JSON.stringify({ text: 'agreed' }),
164
- });
165
- expect(reply.status).not.toBe(403);
166
-
167
- // An annotation is drawn ON the design and versioned with it — the matrix
168
- // files it with `edit`, and that distinction has to survive this change.
169
- const annotate = await fetch(`${base}/_api/annotations`, {
170
- method: 'PUT',
171
- headers: { 'content-type': 'application/json' },
172
- body: JSON.stringify({ file: 'ui/Sample.tsx', svg: '<svg/>' }),
173
- });
174
- expect(annotate.status).toBe(403);
175
- });
176
-
177
- test('the reply pattern does not open the whole comment namespace', async () => {
178
- const { base } = await boot('viewer');
179
- for (const path of [
180
- '/_api/comments/abc/delete',
181
- '/_api/comments/abc/reply/../../edit-text',
182
- '/_api/comments//reply',
183
- ]) {
184
- const res = await fetch(`${base}${path}`, { method: 'POST' });
185
- expect(res.status).not.toBe(200);
186
- }
187
- });
188
- });
@@ -1,95 +0,0 @@
1
- // The client learns it is a viewer at BOOT — Cloud Phase 25 C2.
2
- //
3
- // The alternative is learning it from the first refused write, which means
4
- // drawing somebody an editor and then taking it away. So the role the
5
- // workspace vouched for at sign-in is persisted with the credential and
6
- // resolved before the UI paints.
7
- //
8
- // This flag decides what the UI OFFERS. It is never what stops a write: the
9
- // cell enforces that (Phase 25 C1 — a read-only peer token, Hocuspocus
10
- // dropping SyncStep2/Update, one HTTP gate), and it holds whatever a patched
11
- // client believes about itself.
12
-
13
- import { describe, expect, test } from 'bun:test';
14
- import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
15
- import { tmpdir } from 'node:os';
16
- import { join } from 'node:path';
17
-
18
- import { saveHubCredential } from '../sync/hub-link.ts';
19
- import { signInToWorkspace } from '../sync/workspace-signin.ts';
20
-
21
- function withHome<T>(fn: (home: string) => T): T {
22
- const home = mkdtempSync(join(tmpdir(), 'maude-ro-'));
23
- const prevXdg = process.env.XDG_CONFIG_HOME;
24
- process.env.XDG_CONFIG_HOME = home;
25
- try {
26
- return fn(home);
27
- } finally {
28
- if (prevXdg === undefined) delete process.env.XDG_CONFIG_HOME;
29
- else process.env.XDG_CONFIG_HOME = prevXdg;
30
- rmSync(home, { recursive: true, force: true });
31
- }
32
- }
33
-
34
- describe('the vouched role is persisted with the credential', () => {
35
- test('a viewer is recorded as one', () => {
36
- withHome((home) => {
37
- saveHubCredential('https://p.cloud.maude.sh', 'mau_tok', 'viewer');
38
- const cfg = JSON.parse(readFileSync(join(home, 'maude', 'hubs.json'), 'utf8'));
39
- expect(cfg.hubs['https://p.cloud.maude.sh'].role).toBe('viewer');
40
- });
41
- });
42
-
43
- test('a credential written without a role stays write-capable', () => {
44
- // The upgrade promise: nobody is demoted by shipping this.
45
- withHome((home) => {
46
- saveHubCredential('https://p.cloud.maude.sh', 'mau_tok');
47
- const cfg = JSON.parse(readFileSync(join(home, 'maude', 'hubs.json'), 'utf8'));
48
- expect(cfg.hubs['https://p.cloud.maude.sh'].role).toBeUndefined();
49
- });
50
- });
51
- });
52
-
53
- describe('sign-in carries the role through to the credential', () => {
54
- const okHub = (role: string) =>
55
- (async (url: string) => {
56
- const u = String(url);
57
- if (u.endsWith('/health')) return Response.json({ ok: true, version: '1.0.0' });
58
- return Response.json({ token: 'mau_minted', user: { email: 'v@example.com', role } });
59
- }) as unknown as typeof fetch;
60
-
61
- test('a viewer sign-in saves the role, and reports it back', async () => {
62
- const saved: Array<[string, string, string | undefined]> = [];
63
- const res = await signInToWorkspace(
64
- { url: 'https://p.cloud.maude.sh', email: 'v@example.com', password: 'x'.repeat(12) },
65
- {
66
- save: (u, t, r) => saved.push([u, t, r]),
67
- fetchImpl: okHub('viewer'),
68
- }
69
- );
70
- expect(res.status).toBe(200);
71
- expect(saved[0][2]).toBe('viewer');
72
- expect((res.json as { user: { role: string } }).user.role).toBe('viewer');
73
- });
74
-
75
- test('a hub that names no role is treated as member, never as viewer', async () => {
76
- // Failing OPEN on the UI flag is correct: the cell is what refuses a
77
- // write, so a wrong guess here costs a redundant affordance, never
78
- // access. Guessing "viewer" instead would lock out every self-hoster
79
- // whose hub predates this field.
80
- const saved: Array<[string, string, string | undefined]> = [];
81
- const res = await signInToWorkspace(
82
- { url: 'https://hub.example.com', email: 'm@example.com', password: 'x'.repeat(12) },
83
- {
84
- save: (u, t, r) => saved.push([u, t, r]),
85
- fetchImpl: (async (url: string) => {
86
- const u = String(url);
87
- if (u.endsWith('/health')) return Response.json({ ok: true, version: '1.0.0' });
88
- return Response.json({ token: 'mau_minted', user: { email: 'm@example.com' } });
89
- }) as unknown as typeof fetch,
90
- }
91
- );
92
- expect(res.status).toBe(200);
93
- expect(saved[0][2]).toBe('member');
94
- });
95
- });
@@ -1,226 +0,0 @@
1
- // Readiness probe (DDR-128) — plugin-registry scan + PATH resolution. The
2
- // login-shell fallback's full correctness is proven on the packaged `.app`
3
- // (native-verification ceiling); here we cover the registry-scan branches, the
4
- // no-throw contracts, and the report shape. The probe is async (the fallback shells
5
- // out off the event loop — DDR-128 hardening), so everything awaits.
6
-
7
- import { describe, expect, test } from 'bun:test';
8
- import { existsSync, mkdirSync, mkdtempSync, writeFileSync } from 'node:fs';
9
- import { tmpdir } from 'node:os';
10
- import { join } from 'node:path';
11
-
12
- import { probeReadiness, resolveOnPath } from '../readiness.ts';
13
-
14
- const MARKET_OK = {
15
- maude: { source: { source: 'github', repo: '1aGh/maude' }, installLocation: '/x' },
16
- };
17
- const INSTALLED_BOTH = {
18
- version: 2,
19
- plugins: {
20
- 'design@maude': [{ scope: 'user', version: '0.29.0' }],
21
- 'flow@maude': [{ scope: 'user', version: '0.29.0' }],
22
- },
23
- };
24
- const INSTALLED_DESIGN_ONLY = { version: 2, plugins: { 'design@maude': [{ scope: 'user' }] } };
25
- // `/flow` is no longer part of the chat (2026-07-03) — the gate tracks `design`
26
- // alone, so a registry with flow-but-not-design is the "design missing" case.
27
- const INSTALLED_FLOW_ONLY = { version: 2, plugins: { 'flow@maude': [{ scope: 'user' }] } };
28
-
29
- function fixtureClaudeDir(opts: { markets?: unknown; installed?: unknown }): string {
30
- const dir = mkdtempSync(join(tmpdir(), 'maude-readiness-'));
31
- const pluginsDir = join(dir, 'plugins');
32
- mkdirSync(pluginsDir, { recursive: true });
33
- if (opts.markets !== undefined)
34
- writeFileSync(join(pluginsDir, 'known_marketplaces.json'), JSON.stringify(opts.markets));
35
- if (opts.installed !== undefined)
36
- writeFileSync(join(pluginsDir, 'installed_plugins.json'), JSON.stringify(opts.installed));
37
- return dir;
38
- }
39
-
40
- async function withClaudeDir<T>(dir: string, fn: () => Promise<T> | T): Promise<T> {
41
- const saved = process.env.CLAUDE_CONFIG_DIR;
42
- process.env.CLAUDE_CONFIG_DIR = dir;
43
- try {
44
- return await fn();
45
- } finally {
46
- if (saved === undefined) delete process.env.CLAUDE_CONFIG_DIR;
47
- else process.env.CLAUDE_CONFIG_DIR = saved;
48
- }
49
- }
50
-
51
- /** Plugins row under the given ~/.claude fixture. `nativeBootstrap:false` sets
52
- * MAUDE_NO_PLUGIN_BOOTSTRAP to force the web / manual (DDR-128) path — needed to
53
- * isolate the registry scan, since this suite runs in the dev tree where the
54
- * bundled plugin dirs resolve and DDR-143 auto-load would otherwise mask it. */
55
- const pluginsItem = async (dir: string, opts: { nativeBootstrap?: boolean } = {}) => {
56
- const savedOptOut = process.env.MAUDE_NO_PLUGIN_BOOTSTRAP;
57
- if (opts.nativeBootstrap === false) process.env.MAUDE_NO_PLUGIN_BOOTSTRAP = '1';
58
- try {
59
- return (await withClaudeDir(dir, () => probeReadiness())).items.find(
60
- (i) => i.id === 'plugins'
61
- )!;
62
- } finally {
63
- if (savedOptOut === undefined) delete process.env.MAUDE_NO_PLUGIN_BOOTSTRAP;
64
- else process.env.MAUDE_NO_PLUGIN_BOOTSTRAP = savedOptOut;
65
- }
66
- };
67
-
68
- // Registry-scan branches under the WEB / manual path (auto-bootstrap opted out) —
69
- // the pure DDR-128 detect-and-guide behavior, still what a `maude design serve`
70
- // user sees.
71
- describe('readiness — plugin registry scan (web / manual path)', () => {
72
- test('design@maude + marketplace present → plugins item is present, no remediation', async () => {
73
- const item = await pluginsItem(
74
- fixtureClaudeDir({ markets: MARKET_OK, installed: INSTALLED_DESIGN_ONLY }),
75
- { nativeBootstrap: false }
76
- );
77
- expect(item.status).toBe('present');
78
- expect(item.remediation).toBeUndefined();
79
- });
80
-
81
- test('design@maude NOT installed → missing, names the absent design@maude + offers the fix', async () => {
82
- // flow-only registry: `/flow` is irrelevant to the chat now, so this reads as
83
- // "design missing" — the gate tracks design alone.
84
- const item = await pluginsItem(
85
- fixtureClaudeDir({ markets: MARKET_OK, installed: INSTALLED_FLOW_ONLY }),
86
- { nativeBootstrap: false }
87
- );
88
- expect(item.status).toBe('missing');
89
- expect(item.detail).toContain('design@maude');
90
- expect(item.remediation).toContain('/plugin install');
91
- });
92
-
93
- test('registry absent → unknown (Claude Code internal contract; never throws)', async () => {
94
- const item = await pluginsItem(fixtureClaudeDir({}), { nativeBootstrap: false });
95
- expect(item.status).toBe('unknown');
96
- });
97
-
98
- test('plugin presence is the gate — a foreign-repo marketplace does not change a design-installed verdict', async () => {
99
- const item = await pluginsItem(
100
- fixtureClaudeDir({
101
- markets: { other: { source: { repo: 'someone/else' } } },
102
- installed: INSTALLED_BOTH,
103
- }),
104
- { nativeBootstrap: false }
105
- );
106
- expect(item.status).toBe('present');
107
- });
108
- });
109
-
110
- // DDR-143 / DDR-168 — on the native/desktop path the ACP session ALWAYS loads
111
- // the bundled plugins, so the row is satisfied even against a pristine
112
- // registry, AND even when a marketplace copy is also installed at the user
113
- // level (DDR-168 removed the old scan-gated no-op — the bundled copy wins
114
- // regardless of disk state). This suite runs in the dev tree, so the bundled
115
- // plugin dirs resolve → native context is on by default (no opt-out).
116
- describe('readiness — plugin auto-bootstrap (native, DDR-143/DDR-168)', () => {
117
- test('pristine registry → present + "Bundled with this app" detail, no manual remediation', async () => {
118
- const item = await pluginsItem(fixtureClaudeDir({}));
119
- expect(item.status).toBe('present');
120
- expect(item.detail).toContain('Bundled with this app');
121
- expect(item.remediation).toBeUndefined();
122
- });
123
-
124
- test('design NOT installed (flow-only registry) → still present (design is bundled, no red wall)', async () => {
125
- const item = await pluginsItem(
126
- fixtureClaudeDir({ markets: MARKET_OK, installed: INSTALLED_FLOW_ONLY })
127
- );
128
- expect(item.status).toBe('present');
129
- expect(item.detail).toContain('Bundled with this app');
130
- expect(item.remediation).toBeUndefined();
131
- });
132
-
133
- test('design ALSO installed on disk → present, STILL reads as bundled (DDR-168 — no more "is installed" framing)', async () => {
134
- const item = await pluginsItem(
135
- fixtureClaudeDir({ markets: MARKET_OK, installed: INSTALLED_DESIGN_ONLY })
136
- );
137
- expect(item.status).toBe('present');
138
- expect(item.detail).toContain('Bundled with this app');
139
- });
140
- });
141
-
142
- describe('readiness — resolveOnPath', () => {
143
- test('finds a binary that is on PATH', async () => {
144
- const hit = await resolveOnPath('sh');
145
- expect(hit).not.toBeNull();
146
- expect(existsSync(hit!)).toBe(true);
147
- });
148
-
149
- test('returns null for a bogus binary without throwing', async () => {
150
- expect(await resolveOnPath('maude-definitely-not-real-xyz')).toBeNull();
151
- });
152
-
153
- test('login-shell fallback recovers a binary hidden from the app PATH', async () => {
154
- const saved = process.env.PATH;
155
- try {
156
- // Simulate the truncated/empty GUI env so Bun.which misses and the fallback fires.
157
- process.env.PATH = '';
158
- const hit = await resolveOnPath('sh');
159
- // When a login shell is usable it must recover an absolute, real path. In a
160
- // sandbox with no usable login shell the fallback returns null — we don't fail
161
- // on that, but a non-null result must be a real absolute path.
162
- if (hit !== null) {
163
- expect(hit.startsWith('/')).toBe(true);
164
- expect(existsSync(hit)).toBe(true);
165
- }
166
- } finally {
167
- if (saved === undefined) delete process.env.PATH;
168
- else process.env.PATH = saved;
169
- }
170
- });
171
- });
172
-
173
- describe('readiness — report shape', () => {
174
- test('always returns the five items with stable ids and a boolean ready', async () => {
175
- const report = await probeReadiness();
176
- expect(report.items.map((i) => i.id)).toEqual([
177
- 'claude',
178
- 'maude',
179
- 'plugins',
180
- 'agent-browser',
181
- 'adapter',
182
- ]);
183
- expect(typeof report.ready).toBe('boolean');
184
- });
185
-
186
- test('screenshot engine: required + present (bundled) on native → never blocks ready', async () => {
187
- // Native (this dev tree): agent-browser is bundled, so the row is first-class
188
- // (required) but PRESENT — it can't turn `ready` false.
189
- const ab = (await probeReadiness()).items.find((i) => i.id === 'agent-browser')!;
190
- expect(ab.required).toBe(true);
191
- expect(ab.status).toBe('present');
192
- });
193
-
194
- test('screenshot engine: optional on the web/opt-out path', async () => {
195
- const saved = process.env.MAUDE_NO_PLUGIN_BOOTSTRAP;
196
- process.env.MAUDE_NO_PLUGIN_BOOTSTRAP = '1'; // force native=false
197
- try {
198
- const ab = (await probeReadiness()).items.find((i) => i.id === 'agent-browser')!;
199
- expect(ab.required).toBe(false);
200
- } finally {
201
- if (saved === undefined) delete process.env.MAUDE_NO_PLUGIN_BOOTSTRAP;
202
- else process.env.MAUDE_NO_PLUGIN_BOOTSTRAP = saved;
203
- }
204
- });
205
-
206
- test('adapter row tracks the real chat gate (resolveAdapterEntry) and is required', async () => {
207
- const saved = process.env.MAUDE_ACP_ADAPTER_ENTRY;
208
- try {
209
- // Point the override at a real file → the bridge is "bundled".
210
- process.env.MAUDE_ACP_ADAPTER_ENTRY = import.meta.path;
211
- const present = (await probeReadiness()).items.find((i) => i.id === 'adapter')!;
212
- expect(present.required).toBe(true);
213
- expect(present.status).toBe('present');
214
-
215
- // Point it at a missing path → the row goes missing with remediation, the
216
- // honest signal the v0.31–0.32 builds lacked (all other rows green, chat dead).
217
- process.env.MAUDE_ACP_ADAPTER_ENTRY = join(tmpdir(), 'no-such-acp-adapter-xyz');
218
- const missing = (await probeReadiness()).items.find((i) => i.id === 'adapter')!;
219
- expect(missing.status).toBe('missing');
220
- expect(missing.remediation).toBeDefined();
221
- } finally {
222
- if (saved === undefined) delete process.env.MAUDE_ACP_ADAPTER_ENTRY;
223
- else process.env.MAUDE_ACP_ADAPTER_ENTRY = saved;
224
- }
225
- });
226
- });
@@ -1,89 +0,0 @@
1
- // DDR-132 — remote ahead/behind probe: in-memory TTL cache + in-flight dedupe.
2
- //
3
- // Verifies the cache WRAPPER (TTL-serve / invalidate / in-flight coalesce) fully
4
- // offline, with no network and no git spawn. Trick: the probe's DDR-131 transport
5
- // gate classifies a bare local-path remote as `unsafe`, so `remoteAheadBehind`
6
- // returns a FRESH `{ahead:0,behind:0}` object on every *uncached* evaluation
7
- // without touching the network. Object IDENTITY (`Object.is`) therefore tells a
8
- // cache-serve (same reference) apart from a re-run (new reference) — a stronger
9
- // signal than value equality, and one the security hardening can't invalidate.
10
- //
11
- // (The real fetch/count path needs a reachable github.com remote and is covered
12
- // by the manual native dogfood — same offline ceiling DDR-131 records for
13
- // `gitFetchRemote`. This file owns the wrapper semantics only.)
14
- //
15
- // This file deliberately does NOT set MAUDE_USE_SYSTEM_GIT (it would leak into the
16
- // shared bun-test process and flip other files' iso-engine expectations).
17
-
18
- import { afterEach, beforeEach, expect, test } from 'bun:test';
19
- import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
20
- import { tmpdir } from 'node:os';
21
- import { join } from 'node:path';
22
- import { invalidateRemoteProbe, remoteAheadBehind } from '../git/service.ts';
23
-
24
- function git(cwd: string, args: string[]): void {
25
- const p = Bun.spawnSync(['git', ...args], {
26
- cwd,
27
- env: { ...process.env, GIT_CONFIG_GLOBAL: '/dev/null', GIT_CONFIG_SYSTEM: '/dev/null' },
28
- });
29
- if (p.exitCode !== 0) throw new Error(`git ${args.join(' ')}: ${p.stderr.toString()}`);
30
- }
31
-
32
- let work: string;
33
-
34
- beforeEach(() => {
35
- work = mkdtempSync(join(tmpdir(), 'maude-ab-work-'));
36
- git(work, ['init', '-q', '-b', 'main']);
37
- git(work, ['config', 'user.email', 't@t.dev']);
38
- git(work, ['config', 'user.name', 'Tester']);
39
- writeFileSync(join(work, 'a.txt'), 'hello\n');
40
- git(work, ['add', '.']);
41
- git(work, ['commit', '-q', '-m', 'init']);
42
- // A bare local path → DDR-131 transport gate classifies it `unsafe` → the probe
43
- // returns {0,0} with no spawn. (Any value is fine; we assert on identity.)
44
- git(work, ['remote', 'add', 'origin', '/var/empty/maude-test-remote']);
45
- });
46
-
47
- afterEach(() => {
48
- invalidateRemoteProbe(work);
49
- rmSync(work, { recursive: true, force: true });
50
- });
51
-
52
- test('a second call within the TTL serves the cached object (no re-run)', async () => {
53
- const a = await remoteAheadBehind(work, undefined);
54
- expect(a).toEqual({ ahead: 0, behind: 0 });
55
- const b = await remoteAheadBehind(work, undefined);
56
- expect(Object.is(a, b)).toBe(true); // same reference ⇒ served from cache
57
- });
58
-
59
- test('invalidateRemoteProbe forces a fresh evaluation (new object)', async () => {
60
- const a = await remoteAheadBehind(work, undefined);
61
- invalidateRemoteProbe(work);
62
- const c = await remoteAheadBehind(work, undefined);
63
- expect(c).toEqual({ ahead: 0, behind: 0 });
64
- expect(Object.is(a, c)).toBe(false); // new reference ⇒ re-ran after invalidation
65
- });
66
-
67
- test('SECURITY (DDR-133 F1): the unattended probe never fetches a non-github HTTP remote', async () => {
68
- // Repoint origin to a non-github HTTPS host (a poisoned `.git/config` riding a clone).
69
- // With system git auto-preferred (DDR-133), the host-allowlist must gate this probe
70
- // BEFORE the engine branch — otherwise an http transport reached `git fetch` against
71
- // ANY host on the unattended 45s poll (SSRF / presence beacon). The guard returns
72
- // {0,0} with NO spawn; a regression would attempt the fetch and reject/hang here.
73
- git(work, ['remote', 'set-url', 'origin', 'https://evil.example.invalid/repo.git']);
74
- invalidateRemoteProbe(work);
75
- const r = await remoteAheadBehind(work, 'tok_must_never_be_sent');
76
- expect(r).toEqual({ ahead: 0, behind: 0 });
77
- });
78
-
79
- test('concurrent callers coalesce onto one shared in-flight result', async () => {
80
- const [a, b, c] = await Promise.all([
81
- remoteAheadBehind(work, undefined),
82
- remoteAheadBehind(work, undefined),
83
- remoteAheadBehind(work, undefined),
84
- ]);
85
- // One shared in-flight promise (or the just-written cache) ⇒ identical reference,
86
- // never three independent evaluations.
87
- expect(Object.is(a, b)).toBe(true);
88
- expect(Object.is(b, c)).toBe(true);
89
- });