@1agh/maude 1.0.11 → 1.3.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 (567) hide show
  1. package/README.md +3 -1
  2. package/apps/studio/acp/bridge.ts +18 -13
  3. package/apps/studio/acp/index.ts +43 -7
  4. package/apps/studio/acp/transcript-io.ts +394 -0
  5. package/apps/studio/acp/transcript.ts +103 -38
  6. package/apps/studio/annotations-layer.tsx +191 -116
  7. package/apps/studio/annotations-model.ts +39 -0
  8. package/apps/studio/annotations-sync.ts +50 -0
  9. package/apps/studio/api.ts +821 -55
  10. package/apps/studio/bin/_import-figma.mjs +57 -11
  11. package/apps/studio/bin/_seed-fixture.mjs +163 -0
  12. package/apps/studio/bin/annotate.mjs +3 -1
  13. package/apps/studio/bin/server-up.sh +17 -2
  14. package/apps/studio/canvas-edit.ts +391 -7
  15. package/apps/studio/canvas-imports.ts +89 -0
  16. package/apps/studio/canvas-lib.tsx +82 -18
  17. package/apps/studio/canvas-notice-message.ts +16 -0
  18. package/apps/studio/canvas-notifications.tsx +19 -0
  19. package/apps/studio/canvas-shell.tsx +57 -6
  20. package/apps/studio/client/app.jsx +943 -248
  21. package/apps/studio/client/apply-edit-request.ts +55 -0
  22. package/apps/studio/client/export-center.jsx +125 -67
  23. package/apps/studio/client/github.js +38 -1
  24. package/apps/studio/client/index-loader.ts +76 -0
  25. package/apps/studio/client/panels/CloudBar.jsx +52 -6
  26. package/apps/studio/client/panels/GitPanel.jsx +103 -0
  27. package/apps/studio/client/panels/OnboardingWizard.jsx +36 -7
  28. package/apps/studio/client/panels/RepoBranchSwitcher.jsx +92 -27
  29. package/apps/studio/client/panels/SourceConflictPanel.jsx +166 -0
  30. package/apps/studio/client/panels/SyncPanel.jsx +238 -4
  31. package/apps/studio/client/panels/TeamProjects.jsx +376 -0
  32. package/apps/studio/client/panels/file-deep-link-dialog.jsx +54 -0
  33. package/apps/studio/client/photo-knobs.jsx +3 -0
  34. package/apps/studio/client/report-bug.jsx +19 -1
  35. package/apps/studio/client/share-dialog.jsx +91 -0
  36. package/apps/studio/client/share-link.js +94 -0
  37. package/apps/studio/client/styles/3-shell-maude.css +69 -18
  38. package/apps/studio/client/styles/4-components.css +44 -1
  39. package/apps/studio/client/tree-row-menu.jsx +48 -3
  40. package/apps/studio/client/whats-new.jsx +26 -32
  41. package/apps/studio/cloud/endpoints.ts +132 -0
  42. package/apps/studio/collab/index.ts +4 -0
  43. package/apps/studio/collab/persistence.ts +137 -17
  44. package/apps/studio/collab/registry.ts +19 -21
  45. package/apps/studio/collab/room.ts +13 -2
  46. package/apps/studio/context.ts +59 -0
  47. package/apps/studio/debug-bundle.ts +13 -0
  48. package/apps/studio/dist/client.bundle.js +1911 -1879
  49. package/apps/studio/dist/runtime/.min-sizes.json +1 -0
  50. package/apps/studio/dist/runtime/sonner.js +1 -0
  51. package/apps/studio/dist/styles.css +1 -1
  52. package/apps/studio/export-dialog.tsx +54 -0
  53. package/apps/studio/exporters/degraded.ts +19 -3
  54. package/apps/studio/exporters/ghostscript.ts +194 -0
  55. package/apps/studio/exporters/pdf-fonts.ts +261 -0
  56. package/apps/studio/exporters/pdf.ts +152 -3
  57. package/apps/studio/exporters/remote.ts +36 -1
  58. package/apps/studio/fs-watch.ts +45 -1
  59. package/apps/studio/http.ts +296 -19
  60. package/apps/studio/inspect.ts +19 -0
  61. package/apps/studio/managed-projects.ts +150 -0
  62. package/apps/studio/mem.ts +45 -9
  63. package/apps/studio/notifications.tsx +292 -0
  64. package/apps/studio/runtime-bundle.ts +2 -0
  65. package/apps/studio/server.ts +69 -6
  66. package/apps/studio/sync/accepted-cold-start.ts +225 -0
  67. package/apps/studio/sync/accepted-link.ts +320 -0
  68. package/apps/studio/sync/action-stage.ts +343 -0
  69. package/apps/studio/sync/agent.ts +104 -45
  70. package/apps/studio/sync/asset-push.ts +1 -47
  71. package/apps/studio/sync/cell-file-events.ts +3 -0
  72. package/apps/studio/sync/codec.ts +192 -34
  73. package/apps/studio/sync/cold-start-apply.ts +7 -0
  74. package/apps/studio/sync/cold-start.ts +118 -22
  75. package/apps/studio/sync/comment-identity.ts +68 -0
  76. package/apps/studio/sync/connection-state.ts +15 -1
  77. package/apps/studio/sync/ctl-provider.ts +15 -2
  78. package/apps/studio/sync/document-discovery.ts +81 -0
  79. package/apps/studio/sync/file-ledger.ts +117 -0
  80. package/apps/studio/sync/file-membership.ts +10 -2
  81. package/apps/studio/sync/file-plane.ts +926 -47
  82. package/apps/studio/sync/index.ts +1786 -36
  83. package/apps/studio/sync/journal-client.ts +13 -1
  84. package/apps/studio/sync/migrate-seed.ts +150 -22
  85. package/apps/studio/sync/poke.ts +4 -2
  86. package/apps/studio/sync/presentation.ts +311 -5
  87. package/apps/studio/sync/projection.ts +820 -23
  88. package/apps/studio/sync/repeated-module.ts +73 -0
  89. package/apps/studio/sync/retry-after.ts +169 -0
  90. package/apps/studio/sync/revision-barrier.ts +129 -0
  91. package/apps/studio/sync/seed-progress.ts +218 -0
  92. package/apps/studio/sync/seed-repair.ts +46 -0
  93. package/apps/studio/sync/source-merge.ts +100 -0
  94. package/apps/studio/sync/source-ops.ts +289 -0
  95. package/apps/studio/sync/source-recovery.ts +70 -0
  96. package/apps/studio/sync/source-validation.ts +56 -0
  97. package/apps/studio/sync/status.ts +201 -6
  98. package/apps/studio/sync/transaction-client.ts +460 -0
  99. package/apps/studio/sync/transport-error.ts +111 -0
  100. package/apps/studio/sync/writer-registry.ts +236 -0
  101. package/apps/studio/text-caret.ts +35 -0
  102. package/apps/studio/undo-hud.tsx +9 -87
  103. package/apps/studio/use-canvas-media-drop.tsx +4 -39
  104. package/apps/studio/whats-new.json +173 -0
  105. package/cli/bin/maude.mjs +2 -0
  106. package/cli/commands/codex.mjs +191 -0
  107. package/cli/commands/harness.mjs +1051 -0
  108. package/cli/commands/help.mjs +13 -0
  109. package/cli/lib/design-link.mjs +190 -2
  110. package/cli/lib/harness/capabilities.mjs +302 -0
  111. package/cli/lib/harness/codex-runtime.mjs +892 -0
  112. package/cli/lib/harness/compatibility.mjs +27 -0
  113. package/cli/lib/harness/discover-claude.mjs +1393 -0
  114. package/cli/lib/harness/managed-state.mjs +1296 -0
  115. package/cli/lib/harness/model.mjs +86 -0
  116. package/cli/lib/harness/secrets.mjs +390 -0
  117. package/cli/lib/harness/targets/codex.mjs +1141 -0
  118. package/cli/lib/harness/targets/opencode.mjs +807 -0
  119. package/cli/lib/harness/transaction.mjs +1099 -0
  120. package/cli/lib/hubs-config.mjs +35 -8
  121. package/cli/templates/harness/opencode/maude-projector.ts +58 -0
  122. package/docs/harness-capability-matrix.md +125 -0
  123. package/docs/harness-environment-projection.md +95 -0
  124. package/package.json +33 -9
  125. package/plugins/design/dependencies.json +3 -3
  126. package/plugins/design/templates/_shell.html +47 -0
  127. package/plugins/flow/.claude-plugin/config.schema.json +8 -0
  128. package/plugins/flow/dependencies.json +3 -3
  129. package/apps/studio/.ai/cache/_stats.json +0 -7
  130. package/apps/studio/bin/_agent-browser-safe.test.mjs +0 -165
  131. package/apps/studio/bin/_curl-local.test.mjs +0 -280
  132. package/apps/studio/bin/_pw-launch.test.mjs +0 -90
  133. package/apps/studio/bin/_smart-frames.test.mjs +0 -195
  134. package/apps/studio/bin/_transcribe.test.mjs +0 -80
  135. package/apps/studio/bun.lock +0 -928
  136. package/apps/studio/draw/test/animate.test.ts +0 -172
  137. package/apps/studio/draw/test/brush.test.ts +0 -213
  138. package/apps/studio/draw/test/composition.test.ts +0 -141
  139. package/apps/studio/draw/test/geometry.test.ts +0 -199
  140. package/apps/studio/draw/test/gradient.test.ts +0 -167
  141. package/apps/studio/draw/test/layout.test.ts +0 -120
  142. package/apps/studio/draw/test/morph.test.ts +0 -107
  143. package/apps/studio/draw/test/optimize.test.ts +0 -40
  144. package/apps/studio/draw/test/palette.test.ts +0 -123
  145. package/apps/studio/draw/test/primitives.test.ts +0 -129
  146. package/apps/studio/draw/test/serialize-animate.test.ts +0 -142
  147. package/apps/studio/draw/test/serialize.test.ts +0 -105
  148. package/apps/studio/figma/assets.test.ts +0 -464
  149. package/apps/studio/figma/client.test.ts +0 -395
  150. package/apps/studio/figma/codegen-client.test.ts +0 -276
  151. package/apps/studio/figma/codegen-fonts.test.ts +0 -103
  152. package/apps/studio/figma/codegen-values.test.ts +0 -179
  153. package/apps/studio/figma/comments-to-strokes.test.ts +0 -194
  154. package/apps/studio/figma/fig-decode.test.ts +0 -788
  155. package/apps/studio/figma/fig-differential.test.ts +0 -182
  156. package/apps/studio/figma/fig-translator.test.ts +0 -192
  157. package/apps/studio/figma/fig-vector.test.ts +0 -113
  158. package/apps/studio/figma/from-codegen.test.ts +0 -408
  159. package/apps/studio/figma/sanitize.test.ts +0 -325
  160. package/apps/studio/figma/tailwind-map.test.ts +0 -142
  161. package/apps/studio/figma/to-artboard.test.ts +0 -808
  162. package/apps/studio/figma/to-render.test.ts +0 -180
  163. package/apps/studio/figma/to-strokes-roundtrip.test.ts +0 -152
  164. package/apps/studio/figma/to-strokes.test.ts +0 -705
  165. package/apps/studio/figma/to-tokens.test.ts +0 -321
  166. package/apps/studio/figma/url.test.ts +0 -167
  167. package/apps/studio/footage/schema.test.ts +0 -287
  168. package/apps/studio/generation/adapters/groq.test.ts +0 -80
  169. package/apps/studio/generation/audio-library.test.ts +0 -95
  170. package/apps/studio/generation/captions.test.ts +0 -78
  171. package/apps/studio/generation/elevenlabs.test.ts +0 -223
  172. package/apps/studio/generation/gemini.test.ts +0 -350
  173. package/apps/studio/generation/jobs.test.ts +0 -128
  174. package/apps/studio/generation/keys.test.ts +0 -75
  175. package/apps/studio/generation/prefs.test.ts +0 -95
  176. package/apps/studio/generation/types.test.ts +0 -123
  177. package/apps/studio/generation/whisper-models.test.ts +0 -179
  178. package/apps/studio/test/_helpers.ts +0 -108
  179. package/apps/studio/test/acp-activity-endpoint.test.ts +0 -183
  180. package/apps/studio/test/acp-activity.test.ts +0 -154
  181. package/apps/studio/test/acp-ai-activity.test.ts +0 -182
  182. package/apps/studio/test/acp-attachment-serve.test.ts +0 -74
  183. package/apps/studio/test/acp-bootstrap-brief.test.ts +0 -178
  184. package/apps/studio/test/acp-branch-guard.test.ts +0 -123
  185. package/apps/studio/test/acp-bridge-lifetime.test.ts +0 -475
  186. package/apps/studio/test/acp-bridge.test.ts +0 -545
  187. package/apps/studio/test/acp-capabilities.test.ts +0 -123
  188. package/apps/studio/test/acp-caps-bridge.test.ts +0 -279
  189. package/apps/studio/test/acp-commands.test.ts +0 -119
  190. package/apps/studio/test/acp-elicitation-bridge.test.ts +0 -480
  191. package/apps/studio/test/acp-elicitation.test.ts +0 -251
  192. package/apps/studio/test/acp-env.test.ts +0 -95
  193. package/apps/studio/test/acp-mode-banner.test.ts +0 -45
  194. package/apps/studio/test/acp-origin-gate.test.ts +0 -160
  195. package/apps/studio/test/acp-permission-prompt.test.ts +0 -251
  196. package/apps/studio/test/acp-permission.test.ts +0 -278
  197. package/apps/studio/test/acp-plugin-bootstrap.test.ts +0 -89
  198. package/apps/studio/test/acp-session-allowed-tools.test.ts +0 -235
  199. package/apps/studio/test/acp-session-plugins.test.ts +0 -209
  200. package/apps/studio/test/acp-toolgroup.test.ts +0 -76
  201. package/apps/studio/test/acp-transcript-view.test.ts +0 -71
  202. package/apps/studio/test/acp-transcript.test.ts +0 -238
  203. package/apps/studio/test/acp-usage-bridge.test.ts +0 -141
  204. package/apps/studio/test/acp-usage.test.ts +0 -143
  205. package/apps/studio/test/acp-write-gate.test.ts +0 -323
  206. package/apps/studio/test/acp-write-scope.test.ts +0 -533
  207. package/apps/studio/test/active-state.test.ts +0 -186
  208. package/apps/studio/test/activity.test.ts +0 -189
  209. package/apps/studio/test/ai-activity.test.ts +0 -113
  210. package/apps/studio/test/annotate-write.test.ts +0 -1074
  211. package/apps/studio/test/annotation-strokes-command.test.ts +0 -166
  212. package/apps/studio/test/annotations-align.test.ts +0 -88
  213. package/apps/studio/test/annotations-api.test.ts +0 -145
  214. package/apps/studio/test/annotations-bindings.test.ts +0 -262
  215. package/apps/studio/test/annotations-draw-modifiers.test.ts +0 -206
  216. package/apps/studio/test/annotations-groups.test.ts +0 -231
  217. package/apps/studio/test/annotations-layer.test.ts +0 -1011
  218. package/apps/studio/test/annotations-roundtrip.test.ts +0 -663
  219. package/apps/studio/test/annotations-snap.test.ts +0 -135
  220. package/apps/studio/test/annotations-text-format.test.ts +0 -396
  221. package/apps/studio/test/artboard-activity-overlay.test.tsx +0 -56
  222. package/apps/studio/test/artboard-guides-overlay.test.tsx +0 -152
  223. package/apps/studio/test/artboard-hug-commit.test.ts +0 -36
  224. package/apps/studio/test/artboard-kinds.test.tsx +0 -83
  225. package/apps/studio/test/artboard-selection-attrs.test.ts +0 -69
  226. package/apps/studio/test/asset-api.test.ts +0 -149
  227. package/apps/studio/test/assets-s3.test.ts +0 -249
  228. package/apps/studio/test/binary-smoke.test.ts +0 -47
  229. package/apps/studio/test/boot-self-heal.test.ts +0 -87
  230. package/apps/studio/test/browse-posture.test.tsx +0 -107
  231. package/apps/studio/test/bundle-smoke.test.ts +0 -46
  232. package/apps/studio/test/camera-reveal.test.tsx +0 -173
  233. package/apps/studio/test/canvas-artifacts.test.ts +0 -125
  234. package/apps/studio/test/canvas-build-sandbox.test.ts +0 -69
  235. package/apps/studio/test/canvas-build.test.ts +0 -141
  236. package/apps/studio/test/canvas-create-api.test.ts +0 -676
  237. package/apps/studio/test/canvas-cursors.test.ts +0 -164
  238. package/apps/studio/test/canvas-edit.test.ts +0 -470
  239. package/apps/studio/test/canvas-freshness-deps.test.ts +0 -64
  240. package/apps/studio/test/canvas-header.test.ts +0 -127
  241. package/apps/studio/test/canvas-hide-chrome.test.ts +0 -88
  242. package/apps/studio/test/canvas-hmr-runtime.test.tsx +0 -114
  243. package/apps/studio/test/canvas-lib-inline.test.ts +0 -146
  244. package/apps/studio/test/canvas-lib-motion.test.ts +0 -131
  245. package/apps/studio/test/canvas-lib-resolver.test.ts +0 -111
  246. package/apps/studio/test/canvas-list-watch.test.ts +0 -388
  247. package/apps/studio/test/canvas-media-drop.test.ts +0 -187
  248. package/apps/studio/test/canvas-meta-api.test.ts +0 -707
  249. package/apps/studio/test/canvas-move-api.test.ts +0 -587
  250. package/apps/studio/test/canvas-origin-gate.test.ts +0 -417
  251. package/apps/studio/test/canvas-pipeline.test.ts +0 -180
  252. package/apps/studio/test/canvas-rects.test.ts +0 -198
  253. package/apps/studio/test/canvas-reorder.test.ts +0 -211
  254. package/apps/studio/test/canvas-route.test.ts +0 -179
  255. package/apps/studio/test/canvas-shell-base.test.ts +0 -107
  256. package/apps/studio/test/canvas-shell-build-error.test.ts +0 -52
  257. package/apps/studio/test/canvas-url.test.ts +0 -138
  258. package/apps/studio/test/canvas-wheel-delta.test.ts +0 -42
  259. package/apps/studio/test/canvas-zoom-floor.test.ts +0 -73
  260. package/apps/studio/test/capture-core.test.ts +0 -32
  261. package/apps/studio/test/capture-determinism-shape.test.ts +0 -135
  262. package/apps/studio/test/chat-attachments.test.ts +0 -117
  263. package/apps/studio/test/chat-context.test.ts +0 -129
  264. package/apps/studio/test/chat-markdown.test.tsx +0 -58
  265. package/apps/studio/test/clip-addressing.test.ts +0 -738
  266. package/apps/studio/test/clip-ops.test.ts +0 -922
  267. package/apps/studio/test/cloud-build.test.ts +0 -95
  268. package/apps/studio/test/cloud-connect-note.test.ts +0 -135
  269. package/apps/studio/test/cloud-endpoints.test.ts +0 -590
  270. package/apps/studio/test/cloud-history-hardening.test.ts +0 -165
  271. package/apps/studio/test/cloud-history-posture.test.ts +0 -230
  272. package/apps/studio/test/cloud-managed-save-surfaces.test.ts +0 -90
  273. package/apps/studio/test/cloud-renew.test.ts +0 -205
  274. package/apps/studio/test/cloud-session-role.test.ts +0 -161
  275. package/apps/studio/test/cloud-shell-surfaces.test.ts +0 -170
  276. package/apps/studio/test/cold-start-apply.test.ts +0 -303
  277. package/apps/studio/test/collab-annotations-bridge.test.ts +0 -55
  278. package/apps/studio/test/collab-awareness-bridge.test.ts +0 -223
  279. package/apps/studio/test/collab-bridge.test.ts +0 -79
  280. package/apps/studio/test/collab-loopback.test.ts +0 -63
  281. package/apps/studio/test/collab-origin-gate.test.ts +0 -323
  282. package/apps/studio/test/collab-protocol.test.ts +0 -146
  283. package/apps/studio/test/collab-readonly-gate.test.ts +0 -194
  284. package/apps/studio/test/collab-reseed-guard.test.ts +0 -78
  285. package/apps/studio/test/collab-room.test.ts +0 -188
  286. package/apps/studio/test/collab-session-survival.test.tsx +0 -176
  287. package/apps/studio/test/collab-stress.test.ts +0 -129
  288. package/apps/studio/test/comment-mount.test.ts +0 -97
  289. package/apps/studio/test/comment-relay-origin-gate.test.ts +0 -117
  290. package/apps/studio/test/comments-api.test.ts +0 -277
  291. package/apps/studio/test/comments-fs-rebroadcast.test.ts +0 -155
  292. package/apps/studio/test/comments-overlay.test.ts +0 -117
  293. package/apps/studio/test/compile-entry.test.ts +0 -133
  294. package/apps/studio/test/component-map.test.ts +0 -48
  295. package/apps/studio/test/config-projection.test.ts +0 -104
  296. package/apps/studio/test/config-reload.test.ts +0 -230
  297. package/apps/studio/test/config-version.test.ts +0 -88
  298. package/apps/studio/test/context-resolve-tokens.test.ts +0 -97
  299. package/apps/studio/test/convert-to-absolute.test.ts +0 -333
  300. package/apps/studio/test/credential-grammar.test.ts +0 -107
  301. package/apps/studio/test/csp-canvas-shell.test.ts +0 -62
  302. package/apps/studio/test/csrf-write-guard.test.ts +0 -141
  303. package/apps/studio/test/debug-bundle.test.ts +0 -105
  304. package/apps/studio/test/design-setup-readiness.test.ts +0 -155
  305. package/apps/studio/test/detach-component.test.ts +0 -94
  306. package/apps/studio/test/dns-rebinding-guard.test.ts +0 -101
  307. package/apps/studio/test/dom-selection.test.ts +0 -198
  308. package/apps/studio/test/dynamic-text-edit.test.ts +0 -162
  309. package/apps/studio/test/edit-css-occurrence.test.ts +0 -81
  310. package/apps/studio/test/edit-persistence.test.ts +0 -91
  311. package/apps/studio/test/edit-scope-api.test.ts +0 -119
  312. package/apps/studio/test/edit-source-command.test.ts +0 -142
  313. package/apps/studio/test/edit-suppress.test.ts +0 -170
  314. package/apps/studio/test/editing-presence.test.ts +0 -103
  315. package/apps/studio/test/element-resize.test.ts +0 -136
  316. package/apps/studio/test/element-structural-api.test.ts +0 -434
  317. package/apps/studio/test/element-structural-edit.test.ts +0 -750
  318. package/apps/studio/test/ensure-browser.test.ts +0 -92
  319. package/apps/studio/test/equal-spacing-detector.test.ts +0 -257
  320. package/apps/studio/test/export-assemble.test.ts +0 -53
  321. package/apps/studio/test/export-browser-lane.test.ts +0 -126
  322. package/apps/studio/test/export-capture-fidelity.test.ts +0 -146
  323. package/apps/studio/test/export-capture-hygiene.test.ts +0 -675
  324. package/apps/studio/test/export-center.test.tsx +0 -287
  325. package/apps/studio/test/export-e2e-lanes.test.ts +0 -857
  326. package/apps/studio/test/export-format-scope-coherence.test.ts +0 -89
  327. package/apps/studio/test/export-lane.test.ts +0 -245
  328. package/apps/studio/test/export-shim-multi-capture.test.ts +0 -83
  329. package/apps/studio/test/exporters/canva.test.ts +0 -63
  330. package/apps/studio/test/exporters/degraded-propagation.test.ts +0 -123
  331. package/apps/studio/test/exporters/endpoint.test.ts +0 -121
  332. package/apps/studio/test/exporters/history.test.ts +0 -107
  333. package/apps/studio/test/exporters/html.test.ts +0 -26
  334. package/apps/studio/test/exporters/jobs.test.ts +0 -342
  335. package/apps/studio/test/exporters/pdf.test.ts +0 -85
  336. package/apps/studio/test/exporters/png.test.ts +0 -103
  337. package/apps/studio/test/exporters/pptx-deck.test.ts +0 -112
  338. package/apps/studio/test/exporters/pptx.test.ts +0 -31
  339. package/apps/studio/test/exporters/pw-launch.test.ts +0 -49
  340. package/apps/studio/test/exporters/runtime.test.ts +0 -59
  341. package/apps/studio/test/exporters/scope.test.ts +0 -292
  342. package/apps/studio/test/exporters/svg.test.ts +0 -29
  343. package/apps/studio/test/exporters/unsupported-media.test.ts +0 -123
  344. package/apps/studio/test/exporters/zip.test.ts +0 -104
  345. package/apps/studio/test/fetch-asset-gate.test.ts +0 -189
  346. package/apps/studio/test/figjam-v3-model.test.ts +0 -342
  347. package/apps/studio/test/figma-explode.test.ts +0 -438
  348. package/apps/studio/test/figma-provenance.test.ts +0 -108
  349. package/apps/studio/test/figma-routes.test.ts +0 -294
  350. package/apps/studio/test/file-lock.test.ts +0 -84
  351. package/apps/studio/test/fixtures/backgrounding-claude-auth.sh +0 -12
  352. package/apps/studio/test/fixtures/fake-claude-auth.mjs +0 -23
  353. package/apps/studio/test/fixtures/figjam-v3-groups-bindings.svg +0 -1
  354. package/apps/studio/test/fixtures/mock-acp-agent-caps.mjs +0 -158
  355. package/apps/studio/test/fixtures/mock-acp-agent-commands.mjs +0 -44
  356. package/apps/studio/test/fixtures/mock-acp-agent-elicit-flood.mjs +0 -46
  357. package/apps/studio/test/fixtures/mock-acp-agent-elicit-url.mjs +0 -42
  358. package/apps/studio/test/fixtures/mock-acp-agent-elicit.mjs +0 -63
  359. package/apps/studio/test/fixtures/mock-acp-agent-permission-flood.mjs +0 -51
  360. package/apps/studio/test/fixtures/mock-acp-agent-permission.mjs +0 -50
  361. package/apps/studio/test/fixtures/mock-acp-agent-slow.mjs +0 -45
  362. package/apps/studio/test/fixtures/mock-acp-agent-usage.mjs +0 -56
  363. package/apps/studio/test/fixtures/mock-acp-agent-wedged.mjs +0 -37
  364. package/apps/studio/test/fixtures/mock-acp-agent-write.mjs +0 -118
  365. package/apps/studio/test/fixtures/mock-acp-agent.mjs +0 -69
  366. package/apps/studio/test/fixtures/noisy-claude-auth.mjs +0 -27
  367. package/apps/studio/test/fixtures/perf-canvas.mjs +0 -201
  368. package/apps/studio/test/fixtures/phase-20-annotations.svg +0 -1
  369. package/apps/studio/test/fixtures/phase-21-annotations.svg +0 -1
  370. package/apps/studio/test/fixtures/tiny-subset.woff2 +0 -0
  371. package/apps/studio/test/fixtures/video-comp-fixture.tsx +0 -82
  372. package/apps/studio/test/fixtures/wedged-claude-auth.sh +0 -11
  373. package/apps/studio/test/footage-store.test.ts +0 -100
  374. package/apps/studio/test/fs-mkdir-api.test.ts +0 -248
  375. package/apps/studio/test/fs-watch.test.ts +0 -41
  376. package/apps/studio/test/gemma-models.test.ts +0 -245
  377. package/apps/studio/test/generate-route.test.ts +0 -106
  378. package/apps/studio/test/git-api.test.ts +0 -0
  379. package/apps/studio/test/git-branches.test.ts +0 -341
  380. package/apps/studio/test/git-cloud-posture.test.ts +0 -59
  381. package/apps/studio/test/git-grouping.test.ts +0 -106
  382. package/apps/studio/test/git-lifecycle.test.ts +0 -100
  383. package/apps/studio/test/git-log-format.test.ts +0 -95
  384. package/apps/studio/test/git-safe-rel.test.ts +0 -132
  385. package/apps/studio/test/git-system-engine.test.ts +0 -135
  386. package/apps/studio/test/git-watch.test.ts +0 -97
  387. package/apps/studio/test/github-api.test.ts +0 -577
  388. package/apps/studio/test/grid-track-handles.test.ts +0 -160
  389. package/apps/studio/test/handoff-static-frames.test.ts +0 -215
  390. package/apps/studio/test/handoff.test.ts +0 -329
  391. package/apps/studio/test/history-rollback.test.ts +0 -88
  392. package/apps/studio/test/hmr-broadcast.test.ts +0 -236
  393. package/apps/studio/test/hmr-classify.test.ts +0 -87
  394. package/apps/studio/test/hub-link.test.ts +0 -80
  395. package/apps/studio/test/identity-cache.test.ts +0 -83
  396. package/apps/studio/test/import-asset-browser.test.ts +0 -64
  397. package/apps/studio/test/import-asset-route.test.ts +0 -71
  398. package/apps/studio/test/import-asset.test.ts +0 -427
  399. package/apps/studio/test/import-brand-browser.test.ts +0 -88
  400. package/apps/studio/test/import-brand-route.test.ts +0 -120
  401. package/apps/studio/test/import-brand.test.ts +0 -206
  402. package/apps/studio/test/import-figma.test.ts +0 -667
  403. package/apps/studio/test/import-tokens.test.ts +0 -722
  404. package/apps/studio/test/index-data-canvas-ds.test.ts +0 -97
  405. package/apps/studio/test/input-router.test.ts +0 -757
  406. package/apps/studio/test/inspect-script-syntax.test.ts +0 -53
  407. package/apps/studio/test/inspect-selections.test.ts +0 -219
  408. package/apps/studio/test/knob-props-authored.test.ts +0 -87
  409. package/apps/studio/test/layers-synthetic-groups.test.ts +0 -96
  410. package/apps/studio/test/locator.test.ts +0 -214
  411. package/apps/studio/test/marquee-overlay.test.ts +0 -94
  412. package/apps/studio/test/media-commit-chain.test.ts +0 -210
  413. package/apps/studio/test/mode-toggle.test.tsx +0 -226
  414. package/apps/studio/test/motion-sample.test.ts +0 -35
  415. package/apps/studio/test/move-artboards-command.test.ts +0 -108
  416. package/apps/studio/test/participants-chrome.test.ts +0 -65
  417. package/apps/studio/test/paths.test.ts +0 -88
  418. package/apps/studio/test/pdf-print-boxes.test.ts +0 -359
  419. package/apps/studio/test/peer-selection-follows-camera.test.tsx +0 -131
  420. package/apps/studio/test/perf-harness.ts +0 -193
  421. package/apps/studio/test/phase-3.6-smoke.test.ts +0 -79
  422. package/apps/studio/test/photo-bg-remove-validation.test.ts +0 -71
  423. package/apps/studio/test/photo-canvas-bundle.test.ts +0 -79
  424. package/apps/studio/test/photo-edit-api.test.ts +0 -201
  425. package/apps/studio/test/photo-filters.test.ts +0 -149
  426. package/apps/studio/test/photo-pipeline.test.ts +0 -106
  427. package/apps/studio/test/photo-taxonomy.test.ts +0 -96
  428. package/apps/studio/test/print-marks.test.ts +0 -113
  429. package/apps/studio/test/print-units.test.ts +0 -173
  430. package/apps/studio/test/read-annotations.test.ts +0 -727
  431. package/apps/studio/test/read-only-gate.test.ts +0 -188
  432. package/apps/studio/test/read-only-role.test.ts +0 -95
  433. package/apps/studio/test/readiness.test.ts +0 -337
  434. package/apps/studio/test/remote-ahead-behind-cache.test.ts +0 -89
  435. package/apps/studio/test/reorder-api.test.ts +0 -210
  436. package/apps/studio/test/repo-concurrency.test.ts +0 -146
  437. package/apps/studio/test/repo-lock.test.ts +0 -350
  438. package/apps/studio/test/report-proxy.test.ts +0 -125
  439. package/apps/studio/test/ripple.test.ts +0 -197
  440. package/apps/studio/test/runtime-bundle-error-mapping.test.ts +0 -43
  441. package/apps/studio/test/runtime-bundle.test.ts +0 -69
  442. package/apps/studio/test/sanitize-annotation-svg.test.ts +0 -238
  443. package/apps/studio/test/scaffold-design.test.ts +0 -122
  444. package/apps/studio/test/server-lifecycle.test.ts +0 -38
  445. package/apps/studio/test/session-runtime-state.test.ts +0 -226
  446. package/apps/studio/test/session-scope.test.ts +0 -100
  447. package/apps/studio/test/shared-doc-cell-pairing.test.ts +0 -642
  448. package/apps/studio/test/shared-doc-convergence.test.ts +0 -265
  449. package/apps/studio/test/shared-doc-foundation.test.ts +0 -63
  450. package/apps/studio/test/shared-doc-migrate.test.ts +0 -369
  451. package/apps/studio/test/shared-doc-projection.test.ts +0 -238
  452. package/apps/studio/test/shell-importmap.test.ts +0 -56
  453. package/apps/studio/test/sizing-mode.test.ts +0 -102
  454. package/apps/studio/test/slash-commands.test.ts +0 -117
  455. package/apps/studio/test/snap-distance-pill.test.ts +0 -68
  456. package/apps/studio/test/spacing-handles.test.ts +0 -138
  457. package/apps/studio/test/specimen-select.test.ts +0 -88
  458. package/apps/studio/test/sync-agent.test.ts +0 -660
  459. package/apps/studio/test/sync-annotations-cold-start.test.ts +0 -302
  460. package/apps/studio/test/sync-asset-push.test.ts +0 -931
  461. package/apps/studio/test/sync-atomic-write.test.ts +0 -62
  462. package/apps/studio/test/sync-attach-incremental.test.ts +0 -527
  463. package/apps/studio/test/sync-autocommit.test.ts +0 -537
  464. package/apps/studio/test/sync-canvas-path.test.ts +0 -200
  465. package/apps/studio/test/sync-cell-write-nudge.test.ts +0 -346
  466. package/apps/studio/test/sync-codec.test.ts +0 -230
  467. package/apps/studio/test/sync-cold-seed-dedup.test.ts +0 -187
  468. package/apps/studio/test/sync-cold-start.test.ts +0 -304
  469. package/apps/studio/test/sync-connect-honesty.test.ts +0 -114
  470. package/apps/studio/test/sync-connection-state.test.ts +0 -337
  471. package/apps/studio/test/sync-ctl-channel.test.ts +0 -508
  472. package/apps/studio/test/sync-decide-file.test.ts +0 -420
  473. package/apps/studio/test/sync-doc-name.test.ts +0 -281
  474. package/apps/studio/test/sync-echo-guard.test.ts +0 -96
  475. package/apps/studio/test/sync-file-ledger.test.ts +0 -334
  476. package/apps/studio/test/sync-file-membership.test.ts +0 -347
  477. package/apps/studio/test/sync-file-plane.test.ts +0 -976
  478. package/apps/studio/test/sync-file-pull.test.ts +0 -333
  479. package/apps/studio/test/sync-fresh-link-parity.test.ts +0 -267
  480. package/apps/studio/test/sync-fs-mirror.test.ts +0 -181
  481. package/apps/studio/test/sync-hardening.test.ts +0 -586
  482. package/apps/studio/test/sync-hub-listing.test.ts +0 -46
  483. package/apps/studio/test/sync-hubs-config.test.ts +0 -133
  484. package/apps/studio/test/sync-incident-replay.test.ts +0 -211
  485. package/apps/studio/test/sync-journal.test.ts +0 -176
  486. package/apps/studio/test/sync-meta-codec.test.ts +0 -199
  487. package/apps/studio/test/sync-migrate-flat-fallback.test.ts +0 -98
  488. package/apps/studio/test/sync-move-retirement.test.ts +0 -231
  489. package/apps/studio/test/sync-panel-surface.test.ts +0 -153
  490. package/apps/studio/test/sync-path-pull.test.ts +0 -531
  491. package/apps/studio/test/sync-presentation.test.ts +0 -262
  492. package/apps/studio/test/sync-pull-budget.test.ts +0 -169
  493. package/apps/studio/test/sync-remote-docs.test.ts +0 -333
  494. package/apps/studio/test/sync-resync-routes.test.ts +0 -168
  495. package/apps/studio/test/sync-runtime.test.ts +0 -1675
  496. package/apps/studio/test/sync-seed-defers-to-hub.test.ts +0 -83
  497. package/apps/studio/test/sync-settings-routes.test.ts +0 -195
  498. package/apps/studio/test/sync-settings.test.ts +0 -151
  499. package/apps/studio/test/sync-status.test.ts +0 -261
  500. package/apps/studio/test/sync-supervisor.test.ts +0 -262
  501. package/apps/studio/test/sync-tombstone-apply.test.ts +0 -111
  502. package/apps/studio/test/sync-trash.test.ts +0 -132
  503. package/apps/studio/test/sync-two-peer-discovery.test.ts +0 -343
  504. package/apps/studio/test/sync-untrusted.test.ts +0 -103
  505. package/apps/studio/test/system-endpoint.test.ts +0 -144
  506. package/apps/studio/test/text-editability-stamp.test.ts +0 -131
  507. package/apps/studio/test/timeline-comp-target.test.ts +0 -139
  508. package/apps/studio/test/timeline-media-cache.test.ts +0 -41
  509. package/apps/studio/test/timeline-parse.test.ts +0 -184
  510. package/apps/studio/test/timeline-scale.test.ts +0 -65
  511. package/apps/studio/test/timeline-snap.test.ts +0 -85
  512. package/apps/studio/test/tool-palette-insert-anchor.test.ts +0 -86
  513. package/apps/studio/test/tool-palette.test.tsx +0 -55
  514. package/apps/studio/test/tour-overlay.test.tsx +0 -90
  515. package/apps/studio/test/trusted-request-host.test.ts +0 -66
  516. package/apps/studio/test/undo-sequence-byte-compare.test.ts +0 -211
  517. package/apps/studio/test/undo-stack.test.ts +0 -244
  518. package/apps/studio/test/use-agent-presence.test.tsx +0 -117
  519. package/apps/studio/test/use-annotation-resize.test.ts +0 -402
  520. package/apps/studio/test/use-annotation-selection.test.tsx +0 -77
  521. package/apps/studio/test/use-artboard-drag.test.ts +0 -324
  522. package/apps/studio/test/use-canvas-activity.test.tsx +0 -206
  523. package/apps/studio/test/use-collab.test.ts +0 -0
  524. package/apps/studio/test/use-cursor-modifiers.test.ts +0 -59
  525. package/apps/studio/test/use-keyboard-discipline.test.ts +0 -27
  526. package/apps/studio/test/use-selection-set.test.tsx +0 -166
  527. package/apps/studio/test/use-snap-guides.test.ts +0 -271
  528. package/apps/studio/test/use-tool-mode.test.tsx +0 -186
  529. package/apps/studio/test/use-undo-stack.test.tsx +0 -223
  530. package/apps/studio/test/video-asset.test.ts +0 -163
  531. package/apps/studio/test/video-comp-fixture.test.ts +0 -50
  532. package/apps/studio/test/video-comp.test.ts +0 -270
  533. package/apps/studio/test/video-encode-lib.test.ts +0 -63
  534. package/apps/studio/test/video-render-bridge.test.ts +0 -171
  535. package/apps/studio/test/whats-new.test.ts +0 -113
  536. package/apps/studio/test/whisper-setup.test.ts +0 -97
  537. package/apps/studio/test/workspace-containment.test.ts +0 -382
  538. package/apps/studio/test/workspace-signin.test.ts +0 -292
  539. package/apps/studio/test/ws-handshake.test.ts +0 -33
  540. package/cli/commands/cache.test.mjs +0 -166
  541. package/cli/commands/design-link.test.mjs +0 -419
  542. package/cli/commands/design.test.mjs +0 -240
  543. package/cli/commands/doctor.test.mjs +0 -237
  544. package/cli/commands/hub-workspace.test.mjs +0 -275
  545. package/cli/commands/hub.test.mjs +0 -225
  546. package/cli/commands/kg.test.mjs +0 -142
  547. package/cli/commands/preflight.test.mjs +0 -46
  548. package/cli/lib/argv.test.mjs +0 -46
  549. package/cli/lib/cache.test.mjs +0 -303
  550. package/cli/lib/cell-plan.test.mjs +0 -225
  551. package/cli/lib/config-lint.test.mjs +0 -117
  552. package/cli/lib/ddr-to-kgai.test.mjs +0 -118
  553. package/cli/lib/design-ownership.test.mjs +0 -329
  554. package/cli/lib/fetch-asset.test.mjs +0 -213
  555. package/cli/lib/figma-codegen-reachability.test.mjs +0 -104
  556. package/cli/lib/figma-import-controls.test.mjs +0 -70
  557. package/cli/lib/flow-design-integration.test.mjs +0 -165
  558. package/cli/lib/gitignore-block.test.mjs +0 -136
  559. package/cli/lib/gitignore-drift.test.mjs +0 -156
  560. package/cli/lib/hubs-config.test.mjs +0 -165
  561. package/cli/lib/pkg-root.test.mjs +0 -111
  562. package/cli/lib/plugin-cli-reachability.test.mjs +0 -57
  563. package/cli/lib/plugin-name-namespace.test.mjs +0 -71
  564. package/cli/lib/reconstruct-toolset.test.mjs +0 -194
  565. package/cli/lib/stack-detect.test.mjs +0 -121
  566. package/cli/lib/update-check.test.mjs +0 -32
  567. package/cli/lib/workspace-plan.test.mjs +0 -699
@@ -1,642 +0,0 @@
1
- // Desktop ↔ cloud live pairing (variant C2) — the invariant suite.
2
- //
3
- // This is the gate the feature is allowed through, and it is written against
4
- // the two decisions it threads rather than against its own implementation:
5
- //
6
- // DDR-209 a cell does not dial out, and the hub is the sole committer;
7
- // DDR-064 one shared Y.Doc per canvas, disk is a projection of it.
8
- //
9
- // So the tests here are not "does the code do what the code does". They are:
10
- // can a cell be made to talk to anything but itself (no); can a second
11
- // committer appear (no); do the browser's doc and the desktop's doc become one
12
- // (yes); does a first connect between two non-empty sides duplicate anything
13
- // (no); does one edit produce one reload (yes).
14
- //
15
- // The convergence LAWS themselves live in `shared-doc-convergence.test.ts` and
16
- // are not repeated — this suite covers the cell TOPOLOGY on top of them.
17
-
18
- import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
19
- import { execFileSync } from 'node:child_process';
20
- import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
21
- import { tmpdir } from 'node:os';
22
- import { join } from 'node:path';
23
- import { Awareness } from 'y-protocols/awareness';
24
- import * as Y from 'yjs';
25
-
26
- import { Y_TYPES } from '../collab/persistence.ts';
27
- import { createRegistry } from '../collab/registry.ts';
28
- import type { RoomCallbacks } from '../collab/room.ts';
29
- import type { Context, DevServerConfig } from '../context.ts';
30
- import { createBus } from '../context.ts';
31
- import { resolveCellPairing } from '../sync/cell-pairing.ts';
32
- import { applyCommentsToDoc, Y_SYNC_TYPES } from '../sync/codec.ts';
33
- import {
34
- admitCanvases,
35
- type CanvasDescriptor,
36
- createSyncRuntime,
37
- DEFAULT_MAX_PINNED_ROOMS,
38
- type SyncProvider,
39
- } from '../sync/index.ts';
40
- import { migrateSeed } from '../sync/migrate-seed.ts';
41
-
42
- const LOOPBACK = 'http://127.0.0.1:1234';
43
-
44
- /** The full, valid pairing environment. Tests mutate ONE key at a time from
45
- * here, so a test that passes for the wrong reason is hard to write. */
46
- function pairingEnv(over: Record<string, string | undefined> = {}) {
47
- return {
48
- MAUDE_WORKSPACE_MODE: '1',
49
- MAUDE_CELL_PAIRING: '1',
50
- MAUDE_LOOPBACK_SYNC_URL: LOOPBACK,
51
- MAUDE_LOOPBACK_SYNC_TOKEN: 'cell-token',
52
- MAUDE_SYNC_NO_AUTOCOMMIT: '1',
53
- MAUDE_SHARED_DOC: '1',
54
- ...over,
55
- };
56
- }
57
-
58
- let dir: string;
59
- const SAVED_ENV_KEYS = [
60
- 'MAUDE_WORKSPACE_MODE',
61
- 'MAUDE_CELL_PAIRING',
62
- 'MAUDE_LOOPBACK_SYNC_URL',
63
- 'MAUDE_LOOPBACK_SYNC_TOKEN',
64
- 'MAUDE_SYNC_NO_AUTOCOMMIT',
65
- 'MAUDE_SHARED_DOC',
66
- 'MAUDE_MAX_PINNED_ROOMS',
67
- 'HUBS_CONFIG_PATH',
68
- ] as const;
69
- let saved: Record<string, string | undefined>;
70
-
71
- beforeEach(() => {
72
- dir = mkdtempSync(join(tmpdir(), 'cell-pairing-'));
73
- saved = {};
74
- for (const k of SAVED_ENV_KEYS) saved[k] = process.env[k];
75
- // A cell has no `~/.config/maude/hubs.json`; point the reader at a path that
76
- // does not exist so a stray person-credential can never satisfy a test.
77
- process.env.HUBS_CONFIG_PATH = join(dir, 'absent-hubs.json');
78
- });
79
-
80
- afterEach(() => {
81
- for (const k of SAVED_ENV_KEYS) {
82
- // Assigning `undefined` stringifies to "undefined"; delete is the restore.
83
- if (saved[k] === undefined) delete process.env[k];
84
- else process.env[k] = saved[k];
85
- }
86
- rmSync(dir, { recursive: true, force: true });
87
- });
88
-
89
- /** Install a pairing environment for the duration of one test. */
90
- function setEnv(env: Record<string, string | undefined>): void {
91
- for (const k of SAVED_ENV_KEYS) delete process.env[k];
92
- process.env.HUBS_CONFIG_PATH = join(dir, 'absent-hubs.json');
93
- for (const [k, v] of Object.entries(env)) {
94
- if (v !== undefined) process.env[k] = v;
95
- }
96
- }
97
-
98
- function makeCtx(linkedHub?: DevServerConfig['linkedHub'], repoRoot = dir): Context {
99
- const designRoot = join(repoRoot, 'design');
100
- mkdirSync(join(designRoot, 'ui'), { recursive: true });
101
- mkdirSync(join(designRoot, '_comments'), { recursive: true });
102
- return {
103
- // The Lock-2 sandbox, so `.tsx` bodies are admitted to sync (DDR-060).
104
- canvasOrigin: 'http://canvas.localhost',
105
- sharedDoc: true,
106
- cfg: {
107
- name: 'test',
108
- projectLabel: null,
109
- designRoot: 'design',
110
- canvasGroups: [{ label: 'Canvases', path: 'ui' }],
111
- rootClass: 'app',
112
- themeDefault: 'dark',
113
- tokensCssRel: 'system/colors.css',
114
- teamAccentDefault: null,
115
- handoffTargets: [],
116
- newCanvasDir: 'ui',
117
- newComponentDir: 'ui/components',
118
- linkedHub,
119
- _source: 'defaults',
120
- },
121
- projectLabel: 'test',
122
- paths: {
123
- repoRoot,
124
- designRel: 'design',
125
- designRoot,
126
- serverInfoFile: join(designRoot, '_server.json'),
127
- activeFile: join(designRoot, '_active.json'),
128
- commentsDir: join(designRoot, '_comments'),
129
- canvasStateDir: join(designRoot, '_canvas-state'),
130
- historyDir: join(designRoot, '_history'),
131
- tokensUrlRel: 'design/system/colors.css',
132
- systemDirRel: 'system',
133
- },
134
- bus: createBus(),
135
- } as Context;
136
- }
137
-
138
- function noopCallbacks(): RoomCallbacks {
139
- return { async seed() {}, async persistJson() {}, async persistBinary() {} };
140
- }
141
-
142
- /**
143
- * The cell topology in one object: the BROWSER's room doc (the registry's), the
144
- * DESKTOP's doc (`peerOf`, the other end of the hub), and a record of every URL
145
- * and token the runtime actually dialled.
146
- */
147
- function cellHarness() {
148
- const registry = createRegistry(noopCallbacks());
149
- const peers = new Map<string, Y.Doc>();
150
- const dialled: Array<{ url: string; token: string; documentName: string }> = [];
151
- const TRANSPORT = Symbol('hub-transport');
152
-
153
- const factory = (args: {
154
- url: string;
155
- token: string;
156
- documentName: string;
157
- document?: Y.Doc;
158
- }): SyncProvider => {
159
- dialled.push({ url: args.url, token: args.token, documentName: args.documentName });
160
- const ownsLocal = !args.document;
161
- const local = args.document ?? new Y.Doc();
162
- // The desktop peer, on the far side of the hub's Hocuspocus document.
163
- const desktop = new Y.Doc();
164
- local.on('update', (u: Uint8Array, origin: unknown) => {
165
- if (origin === TRANSPORT) return;
166
- Y.applyUpdate(desktop, u, TRANSPORT);
167
- });
168
- desktop.on('update', (u: Uint8Array, origin: unknown) => {
169
- if (origin === TRANSPORT) return;
170
- Y.applyUpdate(local, u, TRANSPORT);
171
- });
172
- peers.set(args.documentName, desktop);
173
- return {
174
- document: local,
175
- awareness: new Awareness(local),
176
- async onceSynced() {},
177
- destroy() {
178
- if (ownsLocal) local.destroy();
179
- desktop.destroy();
180
- },
181
- };
182
- };
183
-
184
- return {
185
- registry,
186
- factory,
187
- dialled,
188
- /** The doc the DESKTOP app holds for this canvas. */
189
- desktopDoc: (documentName: string) => {
190
- const d = peers.get(documentName);
191
- if (!d) throw new Error(`no provider for ${documentName}`);
192
- return d;
193
- },
194
- /** The doc the BROWSER's collab room holds for this canvas. */
195
- browserDoc: (slug: string) => registry.getDoc(slug),
196
- };
197
- }
198
-
199
- /* ------------------------------------------------------- Task 1: the guard */
200
-
201
- describe('Task 1 — the workspace-mode guard opens only for loopback + commit-disabled', () => {
202
- test('the full, valid environment resolves a pairing', () => {
203
- const { pairing, refusal } = resolveCellPairing(pairingEnv());
204
- expect(refusal).toBeUndefined();
205
- expect(pairing).toEqual({ url: LOOPBACK, token: 'cell-token' });
206
- });
207
-
208
- test.each([
209
- ['localhost', 'http://localhost:1234'],
210
- ['127.0.0.1', 'http://127.0.0.1:9999'],
211
- ['[::1]', 'http://[::1]:1234'],
212
- ])('accepts the loopback host %s', (_label, url) => {
213
- expect(resolveCellPairing(pairingEnv({ MAUDE_LOOPBACK_SYNC_URL: url })).pairing).not.toBeNull();
214
- });
215
-
216
- test.each([
217
- ['a public hub', 'https://hub.example.com'],
218
- ['a LAN address that merely looks local', 'http://192.168.1.10:1234'],
219
- ['a host whose NAME contains localhost', 'https://localhost.evil.example.com'],
220
- ['a private-range address', 'http://10.0.0.5:1234'],
221
- ['not a URL at all', 'hub.example.com'],
222
- ])('refuses %s — a cell syncs to itself or to nothing', (_label, url) => {
223
- const { pairing, refusal, detail } = resolveCellPairing(
224
- pairingEnv({ MAUDE_LOOPBACK_SYNC_URL: url })
225
- );
226
- expect(pairing).toBeNull();
227
- expect(refusal).toBe('not-loopback');
228
- // The operator asked for pairing, so the refusal is never silent.
229
- expect(detail).toBeTruthy();
230
- });
231
-
232
- test('refuses when autocommit is not explicitly disabled', () => {
233
- // Absent — the default. "I forgot to set it" must not read as "it is off".
234
- expect(resolveCellPairing(pairingEnv({ MAUDE_SYNC_NO_AUTOCOMMIT: undefined })).refusal).toBe(
235
- 'autocommit-enabled'
236
- );
237
- // And explicitly on.
238
- expect(resolveCellPairing(pairingEnv({ MAUDE_SYNC_NO_AUTOCOMMIT: '0' })).refusal).toBe(
239
- 'autocommit-enabled'
240
- );
241
- });
242
-
243
- test('refuses when shared-doc is explicitly off — pairing IS the single-doc model', () => {
244
- // Increment 7 flipped the default ON, so an UNSET flag now pairs; only the
245
- // explicit opt-out (the rollback flip) refuses.
246
- expect(resolveCellPairing(pairingEnv({ MAUDE_SHARED_DOC: undefined })).pairing).not.toBeNull();
247
- expect(resolveCellPairing(pairingEnv({ MAUDE_SHARED_DOC: '0' })).refusal).toBe(
248
- 'shared-doc-off'
249
- );
250
- });
251
-
252
- test('refuses when the token is missing', () => {
253
- expect(resolveCellPairing(pairingEnv({ MAUDE_LOOPBACK_SYNC_TOKEN: '' })).refusal).toBe(
254
- 'no-token'
255
- );
256
- });
257
-
258
- test('is inert outside workspace mode and when not requested', () => {
259
- expect(resolveCellPairing(pairingEnv({ MAUDE_WORKSPACE_MODE: '0' })).refusal).toBe(
260
- 'not-workspace-mode'
261
- );
262
- expect(resolveCellPairing(pairingEnv({ MAUDE_CELL_PAIRING: undefined })).refusal).toBe(
263
- 'not-requested'
264
- );
265
- // A refusal that was never asked for gets no `detail` — nothing to say.
266
- expect(
267
- resolveCellPairing(pairingEnv({ MAUDE_CELL_PAIRING: undefined })).detail
268
- ).toBeUndefined();
269
- });
270
- });
271
-
272
- describe('Task 1 — createSyncRuntime honours the guard', () => {
273
- test('workspace + loopback + autocommit-off ⇒ the runtime starts', async () => {
274
- setEnv(pairingEnv());
275
- const ctx = makeCtx();
276
- writeFileSync(join(ctx.paths.designRoot, 'ui', 'screen.tsx'), 'export default () => null;');
277
-
278
- const h = cellHarness();
279
- const runtime = createSyncRuntime(ctx, {
280
- providerFactory: h.factory,
281
- registry: h.registry,
282
- });
283
- expect(runtime).not.toBeNull();
284
- await runtime?.start();
285
- expect(runtime?.size()).toBe(1);
286
- await runtime?.stop();
287
- });
288
-
289
- test('workspace + a non-loopback linkedHub in the TENANT config ⇒ still null', () => {
290
- // No pairing env at all — just workspace mode and a tenant config pointing
291
- // at somebody else's hub. This is the DDR-209 case, unchanged.
292
- setEnv({ MAUDE_WORKSPACE_MODE: '1' });
293
- const ctx = makeCtx({ url: 'https://someone-elses-hub.example.com', linkedAt: 1 });
294
- expect(createSyncRuntime(ctx)).toBeNull();
295
- });
296
-
297
- test('workspace + pairing on + autocommit NOT disabled ⇒ still null', () => {
298
- setEnv(pairingEnv({ MAUDE_SYNC_NO_AUTOCOMMIT: undefined }));
299
- const ctx = makeCtx();
300
- expect(createSyncRuntime(ctx)).toBeNull();
301
- });
302
-
303
- test('the tenant config cannot steer the socket — only the workspace id crosses', async () => {
304
- setEnv(pairingEnv());
305
- // A hostile / merely stale committed config: a hub URL we must ignore, and
306
- // a workspace id we MUST honour (it decides the document name the desktop
307
- // peer is on — see cell-pairing.ts).
308
- const ctx = makeCtx({
309
- url: 'https://attacker.example.com',
310
- linkedAt: 1,
311
- workspaceId: 'acme-web',
312
- });
313
- writeFileSync(join(ctx.paths.designRoot, 'ui', 'screen.tsx'), 'export default () => null;');
314
- // Namespacing is what turns workspaceId into the wire name.
315
- execFileSync('git', ['init', '-q'], { cwd: dir });
316
-
317
- const h = cellHarness();
318
- const runtime = createSyncRuntime(ctx, {
319
- providerFactory: h.factory,
320
- registry: h.registry,
321
- });
322
- await runtime?.start();
323
-
324
- expect(h.dialled).toHaveLength(1);
325
- expect(h.dialled[0]?.url).toBe(LOOPBACK);
326
- expect(h.dialled[0]?.token).toBe('cell-token');
327
- // The tenant's workspace id reached the WIRE NAME — without it the browser
328
- // and the desktop would sit on two differently-named documents and this
329
- // whole feature would silently do nothing.
330
- expect(h.dialled[0]?.documentName).toContain('acme-web');
331
-
332
- await runtime?.stop();
333
- });
334
- });
335
-
336
- /* ------------------------------------------ Task 3: the hub is sole committer */
337
-
338
- describe('Task 3 — no second committer appears inside the cell', () => {
339
- test('a full edit cycle under pairing leaves git untouched', async () => {
340
- setEnv(pairingEnv());
341
- // A REAL repo, so an autocommit that existed would leave a real commit.
342
- const repo = mkdtempSync(join(tmpdir(), 'cell-pairing-repo-'));
343
- try {
344
- execFileSync('git', ['init', '-q'], { cwd: repo });
345
- execFileSync('git', ['config', 'user.email', 't@t.test'], { cwd: repo });
346
- execFileSync('git', ['config', 'user.name', 'T'], { cwd: repo });
347
-
348
- const ctx = makeCtx(undefined, repo);
349
- writeFileSync(join(ctx.paths.designRoot, 'ui', 'screen.tsx'), 'export default () => null;');
350
- execFileSync('git', ['add', '-A'], { cwd: repo });
351
- execFileSync('git', ['commit', '-qm', 'seed'], { cwd: repo });
352
- const before = execFileSync('git', ['rev-list', '--count', 'HEAD'], {
353
- cwd: repo,
354
- encoding: 'utf8',
355
- }).trim();
356
-
357
- const h = cellHarness();
358
- const runtime = createSyncRuntime(ctx, {
359
- providerFactory: h.factory,
360
- registry: h.registry,
361
- });
362
- await runtime?.start();
363
-
364
- // The desktop peer edits the body — the exact traffic that, on the hub
365
- // side, triggers `afterStoreDocument` and the ONE legitimate commit.
366
- h.desktopDoc('ui-screen').getText(Y_SYNC_TYPES.html).insert(0, 'export const x = 1;\n');
367
- // stop() is where an autocommit would flush its quiescence window.
368
- await runtime?.stop();
369
-
370
- const after = execFileSync('git', ['rev-list', '--count', 'HEAD'], {
371
- cwd: repo,
372
- encoding: 'utf8',
373
- }).trim();
374
- expect(after).toBe(before);
375
- // Not merely uncommitted — never staged either.
376
- const staged = execFileSync('git', ['diff', '--cached', '--name-only'], {
377
- cwd: repo,
378
- encoding: 'utf8',
379
- }).trim();
380
- expect(staged).toBe('');
381
- } finally {
382
- rmSync(repo, { recursive: true, force: true });
383
- }
384
- });
385
-
386
- test('pairing never writes .claudeignore into the tenant repo', async () => {
387
- setEnv(pairingEnv());
388
- const ctx = makeCtx();
389
- writeFileSync(join(ctx.paths.designRoot, 'ui', 'screen.tsx'), 'export default () => null;');
390
-
391
- const h = cellHarness();
392
- const runtime = createSyncRuntime(ctx, {
393
- providerFactory: h.factory,
394
- registry: h.registry,
395
- });
396
- await runtime?.start();
397
- // The marker's audience (Claude reading this checkout) does not exist in a
398
- // cell, and the hub would commit and mirror the file to the tenant's repo.
399
- expect(existsSync(join(ctx.paths.repoRoot, '.claudeignore'))).toBe(false);
400
- await runtime?.stop();
401
- });
402
- });
403
-
404
- /* --------------------------------- Task 5 + the headline: the two ends meet */
405
-
406
- describe('presence and edits cross both ways', () => {
407
- test('the browser room doc and the desktop doc are ONE doc', async () => {
408
- setEnv(pairingEnv());
409
- const ctx = makeCtx();
410
- writeFileSync(join(ctx.paths.designRoot, 'ui', 'screen.tsx'), 'export default () => null;');
411
-
412
- const h = cellHarness();
413
- const runtime = createSyncRuntime(ctx, {
414
- providerFactory: h.factory,
415
- registry: h.registry,
416
- });
417
- await runtime?.start();
418
-
419
- // Browser → desktop.
420
- h.browserDoc('ui-screen')
421
- .getArray(Y_TYPES.comments)
422
- .push([{ id: 'from-browser', text: 'hi' }]);
423
- expect(h.desktopDoc('ui-screen').getArray(Y_TYPES.comments).toArray()).toEqual([
424
- { id: 'from-browser', text: 'hi' },
425
- ]);
426
-
427
- // Desktop → browser.
428
- h.desktopDoc('ui-screen').getMap(Y_TYPES.annotations).set('svg', '<svg><rect/></svg>');
429
- expect(h.browserDoc('ui-screen').getMap(Y_TYPES.annotations).get('svg')).toBe(
430
- '<svg><rect/></svg>'
431
- );
432
-
433
- await runtime?.stop();
434
- });
435
-
436
- test('the hub-synced awareness is bridged to the room, so cursors relay', async () => {
437
- setEnv(pairingEnv());
438
- const ctx = makeCtx();
439
- writeFileSync(join(ctx.paths.designRoot, 'ui', 'screen.tsx'), 'export default () => null;');
440
-
441
- const h = cellHarness();
442
- let attaches = 0;
443
- const runtime = createSyncRuntime(ctx, {
444
- providerFactory: h.factory,
445
- registry: {
446
- getDoc: (s) => h.registry.getDoc(s),
447
- pin: (s) => h.registry.pin(s),
448
- unpin: (s) => h.registry.unpin(s),
449
- attachHubAwareness: (s, a) => {
450
- attaches++;
451
- return h.registry.attachHubAwareness(s, a);
452
- },
453
- },
454
- });
455
- await runtime?.start();
456
- // Presence crosses for free BECAUSE the awareness is the provider's — this
457
- // is the bridge, and without it a cursor stops at the process boundary.
458
- expect(attaches).toBe(1);
459
- await runtime?.stop();
460
- });
461
- });
462
-
463
- describe('Task 5 — first connect between two non-empty sides', () => {
464
- test('desktop state and browser files converge without duplicating comments', async () => {
465
- // The cell's disk holds what the BROWSER has been writing; the doc holds
466
- // what the DESKTOP synced up. Both non-empty — the case a naive
467
- // `applyUpdate` merge turns into every comment appearing twice.
468
- const designRoot = join(dir, 'design');
469
- mkdirSync(join(designRoot, 'ui'), { recursive: true });
470
- mkdirSync(join(designRoot, '_comments'), { recursive: true });
471
- const paths = {
472
- html: join(designRoot, 'ui', 'screen.tsx'),
473
- comments: join(designRoot, '_comments', 'ui-screen.json'),
474
- annotations: join(designRoot, 'ui-screen.annotations.svg'),
475
- };
476
- writeFileSync(paths.html, 'export const BROWSER = 1;\n');
477
- writeFileSync(
478
- paths.comments,
479
- JSON.stringify([
480
- { id: 'shared-1', text: 'both sides have this' },
481
- { id: 'browser-only', text: 'only on disk' },
482
- ])
483
- );
484
-
485
- const doc = new Y.Doc();
486
- doc.getText(Y_SYNC_TYPES.html).insert(0, 'export const DESKTOP = 1;\n');
487
- applyCommentsToDoc(
488
- doc,
489
- [
490
- { id: 'shared-1', text: 'both sides have this' },
491
- { id: 'desktop-only', text: 'only in the doc' },
492
- ],
493
- Symbol('seed')
494
- );
495
-
496
- const result = await migrateSeed({ slug: 'ui-screen', doc, paths });
497
-
498
- // ONE authoritative body — never a concatenation of the two.
499
- const body = doc.getText(Y_SYNC_TYPES.html).toString();
500
- expect([result]).not.toContain('empty');
501
- expect(body === 'export const BROWSER = 1;\n' || body === 'export const DESKTOP = 1;\n').toBe(
502
- true
503
- );
504
-
505
- // Comments are id-unioned, so nothing is lost AND nothing is doubled.
506
- const ids = (doc.getArray(Y_TYPES.comments).toArray() as Array<{ id: string }>).map(
507
- (c) => c.id
508
- );
509
- expect([...ids].sort()).toEqual(['browser-only', 'desktop-only', 'shared-1']);
510
- expect(ids).toHaveLength(new Set(ids).size);
511
- });
512
-
513
- test('re-running the seed is a no-op — a restarting cell cannot accumulate', async () => {
514
- const designRoot = join(dir, 'design');
515
- mkdirSync(join(designRoot, '_comments'), { recursive: true });
516
- mkdirSync(join(designRoot, 'ui'), { recursive: true });
517
- const paths = {
518
- html: join(designRoot, 'ui', 'screen.tsx'),
519
- comments: join(designRoot, '_comments', 'ui-screen.json'),
520
- annotations: join(designRoot, 'ui-screen.annotations.svg'),
521
- };
522
- writeFileSync(paths.html, 'export const A = 1;\n');
523
- writeFileSync(paths.comments, JSON.stringify([{ id: 'c1', text: 'one' }]));
524
-
525
- const doc = new Y.Doc();
526
- await migrateSeed({ slug: 'ui-screen', doc, paths });
527
- const first = doc.getArray(Y_TYPES.comments).toArray();
528
- for (let i = 0; i < 3; i++) await migrateSeed({ slug: 'ui-screen', doc, paths });
529
- expect(doc.getArray(Y_TYPES.comments).toArray()).toEqual(first);
530
- });
531
- });
532
-
533
- /* ------------------------------------- Task 6: exactly one reload per edit */
534
-
535
- describe('Task 6 — a doc-originated edit produces exactly one reload signal', () => {
536
- test('a projection write announces itself, once per file', async () => {
537
- setEnv(pairingEnv());
538
- const ctx = makeCtx();
539
- writeFileSync(join(ctx.paths.designRoot, 'ui', 'screen.tsx'), 'export const A = 1;\n');
540
-
541
- const seen: string[] = [];
542
- ctx.bus.on('fs:any', (rel: string) => seen.push(rel));
543
-
544
- const h = cellHarness();
545
- const runtime = createSyncRuntime(ctx, {
546
- providerFactory: h.factory,
547
- registry: h.registry,
548
- });
549
- await runtime?.start();
550
- seen.length = 0; // ignore the cold-start materialize
551
-
552
- // A desktop peer edits the body. The container's fs.watch would miss the
553
- // atomic write that follows, so without the announcement the browser's
554
- // canvas iframe never reloads — the bug this hook exists for.
555
- h.desktopDoc('ui-screen').getText(Y_SYNC_TYPES.html).insert(0, '// edited\n');
556
- await Bun.sleep(1400); // projection debounce (800 ms) + announce delay (250 ms)
557
-
558
- const bodyEvents = seen.filter((r) => r === 'ui/screen.tsx');
559
- expect(bodyEvents).toHaveLength(1);
560
- // And the file really did land — the signal is not announcing a phantom.
561
- expect(readFileSync(join(ctx.paths.designRoot, 'ui', 'screen.tsx'), 'utf8')).toContain(
562
- '// edited'
563
- );
564
-
565
- await runtime?.stop();
566
- }, 10_000);
567
-
568
- test('the announcement does not loop back into the doc', async () => {
569
- setEnv(pairingEnv());
570
- const ctx = makeCtx();
571
- writeFileSync(join(ctx.paths.designRoot, 'ui', 'screen.tsx'), 'export const A = 1;\n');
572
-
573
- const h = cellHarness();
574
- const runtime = createSyncRuntime(ctx, {
575
- providerFactory: h.factory,
576
- registry: h.registry,
577
- });
578
- await runtime?.start();
579
-
580
- const doc = h.browserDoc('ui-screen');
581
- let updates = 0;
582
- doc.on('update', () => updates++);
583
-
584
- h.desktopDoc('ui-screen').getText(Y_SYNC_TYPES.html).insert(0, '// edited\n');
585
- const afterEdit = updates;
586
- // Long enough for the write, the announcement, the fs read and any import
587
- // it might have produced. The echo guard is what makes this terminate.
588
- await Bun.sleep(1600);
589
- expect(updates).toBe(afterEdit);
590
-
591
- await runtime?.stop();
592
- }, 10_000);
593
- });
594
-
595
- /* --------------------------- DDR-064 pre-cutover checklist (A4 / A6 gates) */
596
-
597
- describe('DDR-064 pre-cutover — slug collisions and the pinned-room ceiling', () => {
598
- const canvas = (slug: string, html: string): CanvasDescriptor => ({
599
- slug,
600
- html,
601
- comments: `/c/${slug}.json`,
602
- annotations: `/a/${slug}.svg`,
603
- meta: html.replace(/\.tsx$/, '.meta.json'),
604
- css: html.replace(/\.tsx$/, '.css'),
605
- });
606
-
607
- test('A4 — two files that flatten to one slug BOTH refuse to sync', () => {
608
- // `ui/a/b.tsx` and `ui/a-b.tsx` both flatten to `ui-a-b`. One document for
609
- // two files means each receives the other's body as a remote change and
610
- // writes it over itself, forever.
611
- const admitted = admitCanvases(
612
- [
613
- canvas('ui-a-b', '/d/ui/a/b.tsx'),
614
- canvas('ui-a-b', '/d/ui/a-b.tsx'),
615
- canvas('ui-ok', '/d/ui/ok.tsx'),
616
- ],
617
- true
618
- );
619
- expect(admitted.map((c) => c.slug)).toEqual(['ui-ok']);
620
- });
621
-
622
- test('A4 — a clean set passes through untouched', () => {
623
- const set = [canvas('a', '/d/ui/a.tsx'), canvas('b', '/d/ui/b.tsx')];
624
- expect(admitCanvases(set, true)).toEqual(set);
625
- });
626
-
627
- test('A6 — the pinned-room ceiling bounds the shared-doc set', () => {
628
- process.env.MAUDE_MAX_PINNED_ROOMS = '2';
629
- const set = [
630
- canvas('a', '/d/ui/a.tsx'),
631
- canvas('b', '/d/ui/b.tsx'),
632
- canvas('c', '/d/ui/c.tsx'),
633
- ];
634
- expect(admitCanvases(set, true)).toHaveLength(2);
635
- // Flag OFF is unpinned, so the ceiling does not apply to it.
636
- expect(admitCanvases(set, false)).toHaveLength(3);
637
- });
638
-
639
- test('A6 — the default ceiling is far above any real project', () => {
640
- expect(DEFAULT_MAX_PINNED_ROOMS).toBeGreaterThanOrEqual(500);
641
- });
642
- });