@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.
- package/README.md +3 -1
- package/apps/studio/acp/bridge.ts +18 -13
- package/apps/studio/acp/index.ts +43 -7
- package/apps/studio/acp/transcript-io.ts +394 -0
- package/apps/studio/acp/transcript.ts +103 -38
- package/apps/studio/annotations-layer.tsx +191 -116
- package/apps/studio/annotations-model.ts +39 -0
- package/apps/studio/annotations-sync.ts +50 -0
- package/apps/studio/api.ts +821 -55
- package/apps/studio/bin/_import-figma.mjs +57 -11
- package/apps/studio/bin/_seed-fixture.mjs +163 -0
- package/apps/studio/bin/annotate.mjs +3 -1
- package/apps/studio/bin/server-up.sh +17 -2
- package/apps/studio/canvas-edit.ts +391 -7
- package/apps/studio/canvas-imports.ts +89 -0
- package/apps/studio/canvas-lib.tsx +82 -18
- package/apps/studio/canvas-notice-message.ts +16 -0
- package/apps/studio/canvas-notifications.tsx +19 -0
- package/apps/studio/canvas-shell.tsx +57 -6
- package/apps/studio/client/app.jsx +943 -248
- package/apps/studio/client/apply-edit-request.ts +55 -0
- package/apps/studio/client/export-center.jsx +125 -67
- package/apps/studio/client/github.js +38 -1
- package/apps/studio/client/index-loader.ts +76 -0
- package/apps/studio/client/panels/CloudBar.jsx +52 -6
- package/apps/studio/client/panels/GitPanel.jsx +103 -0
- package/apps/studio/client/panels/OnboardingWizard.jsx +36 -7
- package/apps/studio/client/panels/RepoBranchSwitcher.jsx +92 -27
- package/apps/studio/client/panels/SourceConflictPanel.jsx +166 -0
- package/apps/studio/client/panels/SyncPanel.jsx +238 -4
- package/apps/studio/client/panels/TeamProjects.jsx +376 -0
- package/apps/studio/client/panels/file-deep-link-dialog.jsx +54 -0
- package/apps/studio/client/photo-knobs.jsx +3 -0
- package/apps/studio/client/report-bug.jsx +19 -1
- package/apps/studio/client/share-dialog.jsx +91 -0
- package/apps/studio/client/share-link.js +94 -0
- package/apps/studio/client/styles/3-shell-maude.css +69 -18
- package/apps/studio/client/styles/4-components.css +44 -1
- package/apps/studio/client/tree-row-menu.jsx +48 -3
- package/apps/studio/client/whats-new.jsx +26 -32
- package/apps/studio/cloud/endpoints.ts +132 -0
- package/apps/studio/collab/index.ts +4 -0
- package/apps/studio/collab/persistence.ts +137 -17
- package/apps/studio/collab/registry.ts +19 -21
- package/apps/studio/collab/room.ts +13 -2
- package/apps/studio/context.ts +59 -0
- package/apps/studio/debug-bundle.ts +13 -0
- package/apps/studio/dist/client.bundle.js +1911 -1879
- package/apps/studio/dist/runtime/.min-sizes.json +1 -0
- package/apps/studio/dist/runtime/sonner.js +1 -0
- package/apps/studio/dist/styles.css +1 -1
- package/apps/studio/export-dialog.tsx +54 -0
- package/apps/studio/exporters/degraded.ts +19 -3
- package/apps/studio/exporters/ghostscript.ts +194 -0
- package/apps/studio/exporters/pdf-fonts.ts +261 -0
- package/apps/studio/exporters/pdf.ts +152 -3
- package/apps/studio/exporters/remote.ts +36 -1
- package/apps/studio/fs-watch.ts +45 -1
- package/apps/studio/http.ts +296 -19
- package/apps/studio/inspect.ts +19 -0
- package/apps/studio/managed-projects.ts +150 -0
- package/apps/studio/mem.ts +45 -9
- package/apps/studio/notifications.tsx +292 -0
- package/apps/studio/runtime-bundle.ts +2 -0
- package/apps/studio/server.ts +69 -6
- package/apps/studio/sync/accepted-cold-start.ts +225 -0
- package/apps/studio/sync/accepted-link.ts +320 -0
- package/apps/studio/sync/action-stage.ts +343 -0
- package/apps/studio/sync/agent.ts +104 -45
- package/apps/studio/sync/asset-push.ts +1 -47
- package/apps/studio/sync/cell-file-events.ts +3 -0
- package/apps/studio/sync/codec.ts +192 -34
- package/apps/studio/sync/cold-start-apply.ts +7 -0
- package/apps/studio/sync/cold-start.ts +118 -22
- package/apps/studio/sync/comment-identity.ts +68 -0
- package/apps/studio/sync/connection-state.ts +15 -1
- package/apps/studio/sync/ctl-provider.ts +15 -2
- package/apps/studio/sync/document-discovery.ts +81 -0
- package/apps/studio/sync/file-ledger.ts +117 -0
- package/apps/studio/sync/file-membership.ts +10 -2
- package/apps/studio/sync/file-plane.ts +926 -47
- package/apps/studio/sync/index.ts +1786 -36
- package/apps/studio/sync/journal-client.ts +13 -1
- package/apps/studio/sync/migrate-seed.ts +150 -22
- package/apps/studio/sync/poke.ts +4 -2
- package/apps/studio/sync/presentation.ts +311 -5
- package/apps/studio/sync/projection.ts +820 -23
- package/apps/studio/sync/repeated-module.ts +73 -0
- package/apps/studio/sync/retry-after.ts +169 -0
- package/apps/studio/sync/revision-barrier.ts +129 -0
- package/apps/studio/sync/seed-progress.ts +218 -0
- package/apps/studio/sync/seed-repair.ts +46 -0
- package/apps/studio/sync/source-merge.ts +100 -0
- package/apps/studio/sync/source-ops.ts +289 -0
- package/apps/studio/sync/source-recovery.ts +70 -0
- package/apps/studio/sync/source-validation.ts +56 -0
- package/apps/studio/sync/status.ts +201 -6
- package/apps/studio/sync/transaction-client.ts +460 -0
- package/apps/studio/sync/transport-error.ts +111 -0
- package/apps/studio/sync/writer-registry.ts +236 -0
- package/apps/studio/text-caret.ts +35 -0
- package/apps/studio/undo-hud.tsx +9 -87
- package/apps/studio/use-canvas-media-drop.tsx +4 -39
- package/apps/studio/whats-new.json +173 -0
- package/cli/bin/maude.mjs +2 -0
- package/cli/commands/codex.mjs +191 -0
- package/cli/commands/harness.mjs +1051 -0
- package/cli/commands/help.mjs +13 -0
- package/cli/lib/design-link.mjs +190 -2
- package/cli/lib/harness/capabilities.mjs +302 -0
- package/cli/lib/harness/codex-runtime.mjs +892 -0
- package/cli/lib/harness/compatibility.mjs +27 -0
- package/cli/lib/harness/discover-claude.mjs +1393 -0
- package/cli/lib/harness/managed-state.mjs +1296 -0
- package/cli/lib/harness/model.mjs +86 -0
- package/cli/lib/harness/secrets.mjs +390 -0
- package/cli/lib/harness/targets/codex.mjs +1141 -0
- package/cli/lib/harness/targets/opencode.mjs +807 -0
- package/cli/lib/harness/transaction.mjs +1099 -0
- package/cli/lib/hubs-config.mjs +35 -8
- package/cli/templates/harness/opencode/maude-projector.ts +58 -0
- package/docs/harness-capability-matrix.md +125 -0
- package/docs/harness-environment-projection.md +95 -0
- package/package.json +33 -9
- package/plugins/design/dependencies.json +3 -3
- package/plugins/design/templates/_shell.html +47 -0
- package/plugins/flow/.claude-plugin/config.schema.json +8 -0
- package/plugins/flow/dependencies.json +3 -3
- package/apps/studio/.ai/cache/_stats.json +0 -7
- package/apps/studio/bin/_agent-browser-safe.test.mjs +0 -165
- package/apps/studio/bin/_curl-local.test.mjs +0 -280
- package/apps/studio/bin/_pw-launch.test.mjs +0 -90
- package/apps/studio/bin/_smart-frames.test.mjs +0 -195
- package/apps/studio/bin/_transcribe.test.mjs +0 -80
- package/apps/studio/bun.lock +0 -928
- package/apps/studio/draw/test/animate.test.ts +0 -172
- package/apps/studio/draw/test/brush.test.ts +0 -213
- package/apps/studio/draw/test/composition.test.ts +0 -141
- package/apps/studio/draw/test/geometry.test.ts +0 -199
- package/apps/studio/draw/test/gradient.test.ts +0 -167
- package/apps/studio/draw/test/layout.test.ts +0 -120
- package/apps/studio/draw/test/morph.test.ts +0 -107
- package/apps/studio/draw/test/optimize.test.ts +0 -40
- package/apps/studio/draw/test/palette.test.ts +0 -123
- package/apps/studio/draw/test/primitives.test.ts +0 -129
- package/apps/studio/draw/test/serialize-animate.test.ts +0 -142
- package/apps/studio/draw/test/serialize.test.ts +0 -105
- package/apps/studio/figma/assets.test.ts +0 -464
- package/apps/studio/figma/client.test.ts +0 -395
- package/apps/studio/figma/codegen-client.test.ts +0 -276
- package/apps/studio/figma/codegen-fonts.test.ts +0 -103
- package/apps/studio/figma/codegen-values.test.ts +0 -179
- package/apps/studio/figma/comments-to-strokes.test.ts +0 -194
- package/apps/studio/figma/fig-decode.test.ts +0 -788
- package/apps/studio/figma/fig-differential.test.ts +0 -182
- package/apps/studio/figma/fig-translator.test.ts +0 -192
- package/apps/studio/figma/fig-vector.test.ts +0 -113
- package/apps/studio/figma/from-codegen.test.ts +0 -408
- package/apps/studio/figma/sanitize.test.ts +0 -325
- package/apps/studio/figma/tailwind-map.test.ts +0 -142
- package/apps/studio/figma/to-artboard.test.ts +0 -808
- package/apps/studio/figma/to-render.test.ts +0 -180
- package/apps/studio/figma/to-strokes-roundtrip.test.ts +0 -152
- package/apps/studio/figma/to-strokes.test.ts +0 -705
- package/apps/studio/figma/to-tokens.test.ts +0 -321
- package/apps/studio/figma/url.test.ts +0 -167
- package/apps/studio/footage/schema.test.ts +0 -287
- package/apps/studio/generation/adapters/groq.test.ts +0 -80
- package/apps/studio/generation/audio-library.test.ts +0 -95
- package/apps/studio/generation/captions.test.ts +0 -78
- package/apps/studio/generation/elevenlabs.test.ts +0 -223
- package/apps/studio/generation/gemini.test.ts +0 -350
- package/apps/studio/generation/jobs.test.ts +0 -128
- package/apps/studio/generation/keys.test.ts +0 -75
- package/apps/studio/generation/prefs.test.ts +0 -95
- package/apps/studio/generation/types.test.ts +0 -123
- package/apps/studio/generation/whisper-models.test.ts +0 -179
- package/apps/studio/test/_helpers.ts +0 -108
- package/apps/studio/test/acp-activity-endpoint.test.ts +0 -183
- package/apps/studio/test/acp-activity.test.ts +0 -154
- package/apps/studio/test/acp-ai-activity.test.ts +0 -182
- package/apps/studio/test/acp-attachment-serve.test.ts +0 -74
- package/apps/studio/test/acp-bootstrap-brief.test.ts +0 -178
- package/apps/studio/test/acp-branch-guard.test.ts +0 -123
- package/apps/studio/test/acp-bridge-lifetime.test.ts +0 -475
- package/apps/studio/test/acp-bridge.test.ts +0 -545
- package/apps/studio/test/acp-capabilities.test.ts +0 -123
- package/apps/studio/test/acp-caps-bridge.test.ts +0 -279
- package/apps/studio/test/acp-commands.test.ts +0 -119
- package/apps/studio/test/acp-elicitation-bridge.test.ts +0 -480
- package/apps/studio/test/acp-elicitation.test.ts +0 -251
- package/apps/studio/test/acp-env.test.ts +0 -95
- package/apps/studio/test/acp-mode-banner.test.ts +0 -45
- package/apps/studio/test/acp-origin-gate.test.ts +0 -160
- package/apps/studio/test/acp-permission-prompt.test.ts +0 -251
- package/apps/studio/test/acp-permission.test.ts +0 -278
- package/apps/studio/test/acp-plugin-bootstrap.test.ts +0 -89
- package/apps/studio/test/acp-session-allowed-tools.test.ts +0 -235
- package/apps/studio/test/acp-session-plugins.test.ts +0 -209
- package/apps/studio/test/acp-toolgroup.test.ts +0 -76
- package/apps/studio/test/acp-transcript-view.test.ts +0 -71
- package/apps/studio/test/acp-transcript.test.ts +0 -238
- package/apps/studio/test/acp-usage-bridge.test.ts +0 -141
- package/apps/studio/test/acp-usage.test.ts +0 -143
- package/apps/studio/test/acp-write-gate.test.ts +0 -323
- package/apps/studio/test/acp-write-scope.test.ts +0 -533
- package/apps/studio/test/active-state.test.ts +0 -186
- package/apps/studio/test/activity.test.ts +0 -189
- package/apps/studio/test/ai-activity.test.ts +0 -113
- package/apps/studio/test/annotate-write.test.ts +0 -1074
- package/apps/studio/test/annotation-strokes-command.test.ts +0 -166
- package/apps/studio/test/annotations-align.test.ts +0 -88
- package/apps/studio/test/annotations-api.test.ts +0 -145
- package/apps/studio/test/annotations-bindings.test.ts +0 -262
- package/apps/studio/test/annotations-draw-modifiers.test.ts +0 -206
- package/apps/studio/test/annotations-groups.test.ts +0 -231
- package/apps/studio/test/annotations-layer.test.ts +0 -1011
- package/apps/studio/test/annotations-roundtrip.test.ts +0 -663
- package/apps/studio/test/annotations-snap.test.ts +0 -135
- package/apps/studio/test/annotations-text-format.test.ts +0 -396
- package/apps/studio/test/artboard-activity-overlay.test.tsx +0 -56
- package/apps/studio/test/artboard-guides-overlay.test.tsx +0 -152
- package/apps/studio/test/artboard-hug-commit.test.ts +0 -36
- package/apps/studio/test/artboard-kinds.test.tsx +0 -83
- package/apps/studio/test/artboard-selection-attrs.test.ts +0 -69
- package/apps/studio/test/asset-api.test.ts +0 -149
- package/apps/studio/test/assets-s3.test.ts +0 -249
- package/apps/studio/test/binary-smoke.test.ts +0 -47
- package/apps/studio/test/boot-self-heal.test.ts +0 -87
- package/apps/studio/test/browse-posture.test.tsx +0 -107
- package/apps/studio/test/bundle-smoke.test.ts +0 -46
- package/apps/studio/test/camera-reveal.test.tsx +0 -173
- package/apps/studio/test/canvas-artifacts.test.ts +0 -125
- package/apps/studio/test/canvas-build-sandbox.test.ts +0 -69
- package/apps/studio/test/canvas-build.test.ts +0 -141
- package/apps/studio/test/canvas-create-api.test.ts +0 -676
- package/apps/studio/test/canvas-cursors.test.ts +0 -164
- package/apps/studio/test/canvas-edit.test.ts +0 -470
- package/apps/studio/test/canvas-freshness-deps.test.ts +0 -64
- package/apps/studio/test/canvas-header.test.ts +0 -127
- package/apps/studio/test/canvas-hide-chrome.test.ts +0 -88
- package/apps/studio/test/canvas-hmr-runtime.test.tsx +0 -114
- package/apps/studio/test/canvas-lib-inline.test.ts +0 -146
- package/apps/studio/test/canvas-lib-motion.test.ts +0 -131
- package/apps/studio/test/canvas-lib-resolver.test.ts +0 -111
- package/apps/studio/test/canvas-list-watch.test.ts +0 -388
- package/apps/studio/test/canvas-media-drop.test.ts +0 -187
- package/apps/studio/test/canvas-meta-api.test.ts +0 -707
- package/apps/studio/test/canvas-move-api.test.ts +0 -587
- package/apps/studio/test/canvas-origin-gate.test.ts +0 -417
- package/apps/studio/test/canvas-pipeline.test.ts +0 -180
- package/apps/studio/test/canvas-rects.test.ts +0 -198
- package/apps/studio/test/canvas-reorder.test.ts +0 -211
- package/apps/studio/test/canvas-route.test.ts +0 -179
- package/apps/studio/test/canvas-shell-base.test.ts +0 -107
- package/apps/studio/test/canvas-shell-build-error.test.ts +0 -52
- package/apps/studio/test/canvas-url.test.ts +0 -138
- package/apps/studio/test/canvas-wheel-delta.test.ts +0 -42
- package/apps/studio/test/canvas-zoom-floor.test.ts +0 -73
- package/apps/studio/test/capture-core.test.ts +0 -32
- package/apps/studio/test/capture-determinism-shape.test.ts +0 -135
- package/apps/studio/test/chat-attachments.test.ts +0 -117
- package/apps/studio/test/chat-context.test.ts +0 -129
- package/apps/studio/test/chat-markdown.test.tsx +0 -58
- package/apps/studio/test/clip-addressing.test.ts +0 -738
- package/apps/studio/test/clip-ops.test.ts +0 -922
- package/apps/studio/test/cloud-build.test.ts +0 -95
- package/apps/studio/test/cloud-connect-note.test.ts +0 -135
- package/apps/studio/test/cloud-endpoints.test.ts +0 -590
- package/apps/studio/test/cloud-history-hardening.test.ts +0 -165
- package/apps/studio/test/cloud-history-posture.test.ts +0 -230
- package/apps/studio/test/cloud-managed-save-surfaces.test.ts +0 -90
- package/apps/studio/test/cloud-renew.test.ts +0 -205
- package/apps/studio/test/cloud-session-role.test.ts +0 -161
- package/apps/studio/test/cloud-shell-surfaces.test.ts +0 -170
- package/apps/studio/test/cold-start-apply.test.ts +0 -303
- package/apps/studio/test/collab-annotations-bridge.test.ts +0 -55
- package/apps/studio/test/collab-awareness-bridge.test.ts +0 -223
- package/apps/studio/test/collab-bridge.test.ts +0 -79
- package/apps/studio/test/collab-loopback.test.ts +0 -63
- package/apps/studio/test/collab-origin-gate.test.ts +0 -323
- package/apps/studio/test/collab-protocol.test.ts +0 -146
- package/apps/studio/test/collab-readonly-gate.test.ts +0 -194
- package/apps/studio/test/collab-reseed-guard.test.ts +0 -78
- package/apps/studio/test/collab-room.test.ts +0 -188
- package/apps/studio/test/collab-session-survival.test.tsx +0 -176
- package/apps/studio/test/collab-stress.test.ts +0 -129
- package/apps/studio/test/comment-mount.test.ts +0 -97
- package/apps/studio/test/comment-relay-origin-gate.test.ts +0 -117
- package/apps/studio/test/comments-api.test.ts +0 -277
- package/apps/studio/test/comments-fs-rebroadcast.test.ts +0 -155
- package/apps/studio/test/comments-overlay.test.ts +0 -117
- package/apps/studio/test/compile-entry.test.ts +0 -133
- package/apps/studio/test/component-map.test.ts +0 -48
- package/apps/studio/test/config-projection.test.ts +0 -104
- package/apps/studio/test/config-reload.test.ts +0 -230
- package/apps/studio/test/config-version.test.ts +0 -88
- package/apps/studio/test/context-resolve-tokens.test.ts +0 -97
- package/apps/studio/test/convert-to-absolute.test.ts +0 -333
- package/apps/studio/test/credential-grammar.test.ts +0 -107
- package/apps/studio/test/csp-canvas-shell.test.ts +0 -62
- package/apps/studio/test/csrf-write-guard.test.ts +0 -141
- package/apps/studio/test/debug-bundle.test.ts +0 -105
- package/apps/studio/test/design-setup-readiness.test.ts +0 -155
- package/apps/studio/test/detach-component.test.ts +0 -94
- package/apps/studio/test/dns-rebinding-guard.test.ts +0 -101
- package/apps/studio/test/dom-selection.test.ts +0 -198
- package/apps/studio/test/dynamic-text-edit.test.ts +0 -162
- package/apps/studio/test/edit-css-occurrence.test.ts +0 -81
- package/apps/studio/test/edit-persistence.test.ts +0 -91
- package/apps/studio/test/edit-scope-api.test.ts +0 -119
- package/apps/studio/test/edit-source-command.test.ts +0 -142
- package/apps/studio/test/edit-suppress.test.ts +0 -170
- package/apps/studio/test/editing-presence.test.ts +0 -103
- package/apps/studio/test/element-resize.test.ts +0 -136
- package/apps/studio/test/element-structural-api.test.ts +0 -434
- package/apps/studio/test/element-structural-edit.test.ts +0 -750
- package/apps/studio/test/ensure-browser.test.ts +0 -92
- package/apps/studio/test/equal-spacing-detector.test.ts +0 -257
- package/apps/studio/test/export-assemble.test.ts +0 -53
- package/apps/studio/test/export-browser-lane.test.ts +0 -126
- package/apps/studio/test/export-capture-fidelity.test.ts +0 -146
- package/apps/studio/test/export-capture-hygiene.test.ts +0 -675
- package/apps/studio/test/export-center.test.tsx +0 -287
- package/apps/studio/test/export-e2e-lanes.test.ts +0 -857
- package/apps/studio/test/export-format-scope-coherence.test.ts +0 -89
- package/apps/studio/test/export-lane.test.ts +0 -245
- package/apps/studio/test/export-shim-multi-capture.test.ts +0 -83
- package/apps/studio/test/exporters/canva.test.ts +0 -63
- package/apps/studio/test/exporters/degraded-propagation.test.ts +0 -123
- package/apps/studio/test/exporters/endpoint.test.ts +0 -121
- package/apps/studio/test/exporters/history.test.ts +0 -107
- package/apps/studio/test/exporters/html.test.ts +0 -26
- package/apps/studio/test/exporters/jobs.test.ts +0 -342
- package/apps/studio/test/exporters/pdf.test.ts +0 -85
- package/apps/studio/test/exporters/png.test.ts +0 -103
- package/apps/studio/test/exporters/pptx-deck.test.ts +0 -112
- package/apps/studio/test/exporters/pptx.test.ts +0 -31
- package/apps/studio/test/exporters/pw-launch.test.ts +0 -49
- package/apps/studio/test/exporters/runtime.test.ts +0 -59
- package/apps/studio/test/exporters/scope.test.ts +0 -292
- package/apps/studio/test/exporters/svg.test.ts +0 -29
- package/apps/studio/test/exporters/unsupported-media.test.ts +0 -123
- package/apps/studio/test/exporters/zip.test.ts +0 -104
- package/apps/studio/test/fetch-asset-gate.test.ts +0 -189
- package/apps/studio/test/figjam-v3-model.test.ts +0 -342
- package/apps/studio/test/figma-explode.test.ts +0 -438
- package/apps/studio/test/figma-provenance.test.ts +0 -108
- package/apps/studio/test/figma-routes.test.ts +0 -294
- package/apps/studio/test/file-lock.test.ts +0 -84
- package/apps/studio/test/fixtures/backgrounding-claude-auth.sh +0 -12
- package/apps/studio/test/fixtures/fake-claude-auth.mjs +0 -23
- package/apps/studio/test/fixtures/figjam-v3-groups-bindings.svg +0 -1
- package/apps/studio/test/fixtures/mock-acp-agent-caps.mjs +0 -158
- package/apps/studio/test/fixtures/mock-acp-agent-commands.mjs +0 -44
- package/apps/studio/test/fixtures/mock-acp-agent-elicit-flood.mjs +0 -46
- package/apps/studio/test/fixtures/mock-acp-agent-elicit-url.mjs +0 -42
- package/apps/studio/test/fixtures/mock-acp-agent-elicit.mjs +0 -63
- package/apps/studio/test/fixtures/mock-acp-agent-permission-flood.mjs +0 -51
- package/apps/studio/test/fixtures/mock-acp-agent-permission.mjs +0 -50
- package/apps/studio/test/fixtures/mock-acp-agent-slow.mjs +0 -45
- package/apps/studio/test/fixtures/mock-acp-agent-usage.mjs +0 -56
- package/apps/studio/test/fixtures/mock-acp-agent-wedged.mjs +0 -37
- package/apps/studio/test/fixtures/mock-acp-agent-write.mjs +0 -118
- package/apps/studio/test/fixtures/mock-acp-agent.mjs +0 -69
- package/apps/studio/test/fixtures/noisy-claude-auth.mjs +0 -27
- package/apps/studio/test/fixtures/perf-canvas.mjs +0 -201
- package/apps/studio/test/fixtures/phase-20-annotations.svg +0 -1
- package/apps/studio/test/fixtures/phase-21-annotations.svg +0 -1
- package/apps/studio/test/fixtures/tiny-subset.woff2 +0 -0
- package/apps/studio/test/fixtures/video-comp-fixture.tsx +0 -82
- package/apps/studio/test/fixtures/wedged-claude-auth.sh +0 -11
- package/apps/studio/test/footage-store.test.ts +0 -100
- package/apps/studio/test/fs-mkdir-api.test.ts +0 -248
- package/apps/studio/test/fs-watch.test.ts +0 -41
- package/apps/studio/test/gemma-models.test.ts +0 -245
- package/apps/studio/test/generate-route.test.ts +0 -106
- package/apps/studio/test/git-api.test.ts +0 -0
- package/apps/studio/test/git-branches.test.ts +0 -341
- package/apps/studio/test/git-cloud-posture.test.ts +0 -59
- package/apps/studio/test/git-grouping.test.ts +0 -106
- package/apps/studio/test/git-lifecycle.test.ts +0 -100
- package/apps/studio/test/git-log-format.test.ts +0 -95
- package/apps/studio/test/git-safe-rel.test.ts +0 -132
- package/apps/studio/test/git-system-engine.test.ts +0 -135
- package/apps/studio/test/git-watch.test.ts +0 -97
- package/apps/studio/test/github-api.test.ts +0 -577
- package/apps/studio/test/grid-track-handles.test.ts +0 -160
- package/apps/studio/test/handoff-static-frames.test.ts +0 -215
- package/apps/studio/test/handoff.test.ts +0 -329
- package/apps/studio/test/history-rollback.test.ts +0 -88
- package/apps/studio/test/hmr-broadcast.test.ts +0 -236
- package/apps/studio/test/hmr-classify.test.ts +0 -87
- package/apps/studio/test/hub-link.test.ts +0 -80
- package/apps/studio/test/identity-cache.test.ts +0 -83
- package/apps/studio/test/import-asset-browser.test.ts +0 -64
- package/apps/studio/test/import-asset-route.test.ts +0 -71
- package/apps/studio/test/import-asset.test.ts +0 -427
- package/apps/studio/test/import-brand-browser.test.ts +0 -88
- package/apps/studio/test/import-brand-route.test.ts +0 -120
- package/apps/studio/test/import-brand.test.ts +0 -206
- package/apps/studio/test/import-figma.test.ts +0 -667
- package/apps/studio/test/import-tokens.test.ts +0 -722
- package/apps/studio/test/index-data-canvas-ds.test.ts +0 -97
- package/apps/studio/test/input-router.test.ts +0 -757
- package/apps/studio/test/inspect-script-syntax.test.ts +0 -53
- package/apps/studio/test/inspect-selections.test.ts +0 -219
- package/apps/studio/test/knob-props-authored.test.ts +0 -87
- package/apps/studio/test/layers-synthetic-groups.test.ts +0 -96
- package/apps/studio/test/locator.test.ts +0 -214
- package/apps/studio/test/marquee-overlay.test.ts +0 -94
- package/apps/studio/test/media-commit-chain.test.ts +0 -210
- package/apps/studio/test/mode-toggle.test.tsx +0 -226
- package/apps/studio/test/motion-sample.test.ts +0 -35
- package/apps/studio/test/move-artboards-command.test.ts +0 -108
- package/apps/studio/test/participants-chrome.test.ts +0 -65
- package/apps/studio/test/paths.test.ts +0 -88
- package/apps/studio/test/pdf-print-boxes.test.ts +0 -359
- package/apps/studio/test/peer-selection-follows-camera.test.tsx +0 -131
- package/apps/studio/test/perf-harness.ts +0 -193
- package/apps/studio/test/phase-3.6-smoke.test.ts +0 -79
- package/apps/studio/test/photo-bg-remove-validation.test.ts +0 -71
- package/apps/studio/test/photo-canvas-bundle.test.ts +0 -79
- package/apps/studio/test/photo-edit-api.test.ts +0 -201
- package/apps/studio/test/photo-filters.test.ts +0 -149
- package/apps/studio/test/photo-pipeline.test.ts +0 -106
- package/apps/studio/test/photo-taxonomy.test.ts +0 -96
- package/apps/studio/test/print-marks.test.ts +0 -113
- package/apps/studio/test/print-units.test.ts +0 -173
- package/apps/studio/test/read-annotations.test.ts +0 -727
- package/apps/studio/test/read-only-gate.test.ts +0 -188
- package/apps/studio/test/read-only-role.test.ts +0 -95
- package/apps/studio/test/readiness.test.ts +0 -337
- package/apps/studio/test/remote-ahead-behind-cache.test.ts +0 -89
- package/apps/studio/test/reorder-api.test.ts +0 -210
- package/apps/studio/test/repo-concurrency.test.ts +0 -146
- package/apps/studio/test/repo-lock.test.ts +0 -350
- package/apps/studio/test/report-proxy.test.ts +0 -125
- package/apps/studio/test/ripple.test.ts +0 -197
- package/apps/studio/test/runtime-bundle-error-mapping.test.ts +0 -43
- package/apps/studio/test/runtime-bundle.test.ts +0 -69
- package/apps/studio/test/sanitize-annotation-svg.test.ts +0 -238
- package/apps/studio/test/scaffold-design.test.ts +0 -122
- package/apps/studio/test/server-lifecycle.test.ts +0 -38
- package/apps/studio/test/session-runtime-state.test.ts +0 -226
- package/apps/studio/test/session-scope.test.ts +0 -100
- package/apps/studio/test/shared-doc-cell-pairing.test.ts +0 -642
- package/apps/studio/test/shared-doc-convergence.test.ts +0 -265
- package/apps/studio/test/shared-doc-foundation.test.ts +0 -63
- package/apps/studio/test/shared-doc-migrate.test.ts +0 -369
- package/apps/studio/test/shared-doc-projection.test.ts +0 -238
- package/apps/studio/test/shell-importmap.test.ts +0 -56
- package/apps/studio/test/sizing-mode.test.ts +0 -102
- package/apps/studio/test/slash-commands.test.ts +0 -117
- package/apps/studio/test/snap-distance-pill.test.ts +0 -68
- package/apps/studio/test/spacing-handles.test.ts +0 -138
- package/apps/studio/test/specimen-select.test.ts +0 -88
- package/apps/studio/test/sync-agent.test.ts +0 -660
- package/apps/studio/test/sync-annotations-cold-start.test.ts +0 -302
- package/apps/studio/test/sync-asset-push.test.ts +0 -931
- package/apps/studio/test/sync-atomic-write.test.ts +0 -62
- package/apps/studio/test/sync-attach-incremental.test.ts +0 -527
- package/apps/studio/test/sync-autocommit.test.ts +0 -537
- package/apps/studio/test/sync-canvas-path.test.ts +0 -200
- package/apps/studio/test/sync-cell-write-nudge.test.ts +0 -346
- package/apps/studio/test/sync-codec.test.ts +0 -230
- package/apps/studio/test/sync-cold-seed-dedup.test.ts +0 -187
- package/apps/studio/test/sync-cold-start.test.ts +0 -304
- package/apps/studio/test/sync-connect-honesty.test.ts +0 -114
- package/apps/studio/test/sync-connection-state.test.ts +0 -337
- package/apps/studio/test/sync-ctl-channel.test.ts +0 -508
- package/apps/studio/test/sync-decide-file.test.ts +0 -420
- package/apps/studio/test/sync-doc-name.test.ts +0 -281
- package/apps/studio/test/sync-echo-guard.test.ts +0 -96
- package/apps/studio/test/sync-file-ledger.test.ts +0 -334
- package/apps/studio/test/sync-file-membership.test.ts +0 -347
- package/apps/studio/test/sync-file-plane.test.ts +0 -976
- package/apps/studio/test/sync-file-pull.test.ts +0 -333
- package/apps/studio/test/sync-fresh-link-parity.test.ts +0 -267
- package/apps/studio/test/sync-fs-mirror.test.ts +0 -181
- package/apps/studio/test/sync-hardening.test.ts +0 -586
- package/apps/studio/test/sync-hub-listing.test.ts +0 -46
- package/apps/studio/test/sync-hubs-config.test.ts +0 -133
- package/apps/studio/test/sync-incident-replay.test.ts +0 -211
- package/apps/studio/test/sync-journal.test.ts +0 -176
- package/apps/studio/test/sync-meta-codec.test.ts +0 -199
- package/apps/studio/test/sync-migrate-flat-fallback.test.ts +0 -98
- package/apps/studio/test/sync-move-retirement.test.ts +0 -231
- package/apps/studio/test/sync-panel-surface.test.ts +0 -153
- package/apps/studio/test/sync-path-pull.test.ts +0 -531
- package/apps/studio/test/sync-presentation.test.ts +0 -262
- package/apps/studio/test/sync-pull-budget.test.ts +0 -169
- package/apps/studio/test/sync-remote-docs.test.ts +0 -333
- package/apps/studio/test/sync-resync-routes.test.ts +0 -168
- package/apps/studio/test/sync-runtime.test.ts +0 -1675
- package/apps/studio/test/sync-seed-defers-to-hub.test.ts +0 -83
- package/apps/studio/test/sync-settings-routes.test.ts +0 -195
- package/apps/studio/test/sync-settings.test.ts +0 -151
- package/apps/studio/test/sync-status.test.ts +0 -261
- package/apps/studio/test/sync-supervisor.test.ts +0 -262
- package/apps/studio/test/sync-tombstone-apply.test.ts +0 -111
- package/apps/studio/test/sync-trash.test.ts +0 -132
- package/apps/studio/test/sync-two-peer-discovery.test.ts +0 -343
- package/apps/studio/test/sync-untrusted.test.ts +0 -103
- package/apps/studio/test/system-endpoint.test.ts +0 -144
- package/apps/studio/test/text-editability-stamp.test.ts +0 -131
- package/apps/studio/test/timeline-comp-target.test.ts +0 -139
- package/apps/studio/test/timeline-media-cache.test.ts +0 -41
- package/apps/studio/test/timeline-parse.test.ts +0 -184
- package/apps/studio/test/timeline-scale.test.ts +0 -65
- package/apps/studio/test/timeline-snap.test.ts +0 -85
- package/apps/studio/test/tool-palette-insert-anchor.test.ts +0 -86
- package/apps/studio/test/tool-palette.test.tsx +0 -55
- package/apps/studio/test/tour-overlay.test.tsx +0 -90
- package/apps/studio/test/trusted-request-host.test.ts +0 -66
- package/apps/studio/test/undo-sequence-byte-compare.test.ts +0 -211
- package/apps/studio/test/undo-stack.test.ts +0 -244
- package/apps/studio/test/use-agent-presence.test.tsx +0 -117
- package/apps/studio/test/use-annotation-resize.test.ts +0 -402
- package/apps/studio/test/use-annotation-selection.test.tsx +0 -77
- package/apps/studio/test/use-artboard-drag.test.ts +0 -324
- package/apps/studio/test/use-canvas-activity.test.tsx +0 -206
- package/apps/studio/test/use-collab.test.ts +0 -0
- package/apps/studio/test/use-cursor-modifiers.test.ts +0 -59
- package/apps/studio/test/use-keyboard-discipline.test.ts +0 -27
- package/apps/studio/test/use-selection-set.test.tsx +0 -166
- package/apps/studio/test/use-snap-guides.test.ts +0 -271
- package/apps/studio/test/use-tool-mode.test.tsx +0 -186
- package/apps/studio/test/use-undo-stack.test.tsx +0 -223
- package/apps/studio/test/video-asset.test.ts +0 -163
- package/apps/studio/test/video-comp-fixture.test.ts +0 -50
- package/apps/studio/test/video-comp.test.ts +0 -270
- package/apps/studio/test/video-encode-lib.test.ts +0 -63
- package/apps/studio/test/video-render-bridge.test.ts +0 -171
- package/apps/studio/test/whats-new.test.ts +0 -113
- package/apps/studio/test/whisper-setup.test.ts +0 -97
- package/apps/studio/test/workspace-containment.test.ts +0 -382
- package/apps/studio/test/workspace-signin.test.ts +0 -292
- package/apps/studio/test/ws-handshake.test.ts +0 -33
- package/cli/commands/cache.test.mjs +0 -166
- package/cli/commands/design-link.test.mjs +0 -419
- package/cli/commands/design.test.mjs +0 -240
- package/cli/commands/doctor.test.mjs +0 -237
- package/cli/commands/hub-workspace.test.mjs +0 -275
- package/cli/commands/hub.test.mjs +0 -225
- package/cli/commands/kg.test.mjs +0 -142
- package/cli/commands/preflight.test.mjs +0 -46
- package/cli/lib/argv.test.mjs +0 -46
- package/cli/lib/cache.test.mjs +0 -303
- package/cli/lib/cell-plan.test.mjs +0 -225
- package/cli/lib/config-lint.test.mjs +0 -117
- package/cli/lib/ddr-to-kgai.test.mjs +0 -118
- package/cli/lib/design-ownership.test.mjs +0 -329
- package/cli/lib/fetch-asset.test.mjs +0 -213
- package/cli/lib/figma-codegen-reachability.test.mjs +0 -104
- package/cli/lib/figma-import-controls.test.mjs +0 -70
- package/cli/lib/flow-design-integration.test.mjs +0 -165
- package/cli/lib/gitignore-block.test.mjs +0 -136
- package/cli/lib/gitignore-drift.test.mjs +0 -156
- package/cli/lib/hubs-config.test.mjs +0 -165
- package/cli/lib/pkg-root.test.mjs +0 -111
- package/cli/lib/plugin-cli-reachability.test.mjs +0 -57
- package/cli/lib/plugin-name-namespace.test.mjs +0 -71
- package/cli/lib/reconstruct-toolset.test.mjs +0 -194
- package/cli/lib/stack-detect.test.mjs +0 -121
- package/cli/lib/update-check.test.mjs +0 -32
- package/cli/lib/workspace-plan.test.mjs +0 -699
|
@@ -10,6 +10,7 @@ import type { Api } from '../api.ts';
|
|
|
10
10
|
import type { Context } from '../context.ts';
|
|
11
11
|
// From the LEAF, never from `sync/codec.ts` — codec imports `Y_TYPES` from this
|
|
12
12
|
// file, so reaching for it here would close a cycle (see sync/limits.ts).
|
|
13
|
+
import { commentKey } from '../sync/comment-identity.ts';
|
|
13
14
|
import { MAX_ANNOTATIONS_BYTES, MAX_COMMENTS_BYTES, withinByteCap } from '../sync/limits.ts';
|
|
14
15
|
import { ensureStateDir, type RoomCallbacks } from './room.ts';
|
|
15
16
|
|
|
@@ -82,15 +83,91 @@ export function createPersistence(deps: PersistenceDeps): RoomCallbacks {
|
|
|
82
83
|
const { ctx, api, fileForSlug } = deps;
|
|
83
84
|
const stateDir = ensureStateDir(ctx.paths.designRoot);
|
|
84
85
|
|
|
85
|
-
// Per-slug
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
|
|
86
|
+
// Per-slug: every comment identity this doc has EVER carried (issue #111).
|
|
87
|
+
//
|
|
88
|
+
// This subsumes the old `seenComments` latch — "have we ever projected a
|
|
89
|
+
// NON-empty array for this canvas", which gated the delete-all write so a
|
|
90
|
+
// never-populated doc could not clobber a non-empty local file with `[]`
|
|
91
|
+
// (DDR-064's receiving-peer gap). Same question, better evidence.
|
|
92
|
+
//
|
|
93
|
+
// It also answers the question the projection could not ask at all before,
|
|
94
|
+
// and the one this issue is about: is the doc AHEAD of the file (a real
|
|
95
|
+
// change, write it) or BEHIND it (a write the doc has not caught up with —
|
|
96
|
+
// writing would erase it)?
|
|
97
|
+
//
|
|
98
|
+
// Identity settles it without a wire-format change. An id on disk that the
|
|
99
|
+
// doc has held and dropped is a genuine delete and must materialize. An id on
|
|
100
|
+
// disk the doc has NEVER held is something the doc has not seen yet, and
|
|
101
|
+
// dropping it is silent data loss — the reporter's "add a second comment and
|
|
102
|
+
// it disappears after a few seconds". `Room.flush()` fires on ANY doc update
|
|
103
|
+
// and every hub update re-arms it, so on a linked project a flush lands in
|
|
104
|
+
// that window constantly; on a local one it essentially never does, which is
|
|
105
|
+
// exactly the scoping the reporter gave ("connected to a custom hub").
|
|
106
|
+
const docSeenComments = new Map<string, Set<string>>();
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Ceiling on identities remembered per canvas — DDR-054 §2d applied to a set
|
|
110
|
+
* rather than a byte length.
|
|
111
|
+
*
|
|
112
|
+
* The doc is populated by the hub, so what lands in this set is peer-supplied
|
|
113
|
+
* and the set only ever grows: without a bound, a hostile hub streaming
|
|
114
|
+
* distinct comment ids is unbounded memory on every linked peer. Eviction is
|
|
115
|
+
* oldest-first (JS sets keep insertion order), and it degrades in the SAFE
|
|
116
|
+
* direction — an evicted id reads as "never seen", so the projection defers
|
|
117
|
+
* the write instead of deleting. The cost is that a delete of a very old
|
|
118
|
+
* comment on a canvas that has carried more than this many distinct comments
|
|
119
|
+
* may not materialize, which is a far better failure than losing one.
|
|
120
|
+
*/
|
|
121
|
+
const MAX_SEEN_COMMENT_IDS = 10_000;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Docs already wired to their slug's identity set.
|
|
125
|
+
*
|
|
126
|
+
* WEAK on purpose: a room dropped when its last browser leaves destroys its
|
|
127
|
+
* doc, and a strong reference here would keep every doc of every canvas the
|
|
128
|
+
* user ever opened alive for the process's lifetime. The observer closes over
|
|
129
|
+
* the SET, not the other way round, so nothing else pins the doc either.
|
|
130
|
+
*/
|
|
131
|
+
const observedCommentDocs = new WeakSet<Y.Doc>();
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* The identity set for `slug`, observing `doc` so it records an id the moment
|
|
135
|
+
* it ENTERS the array.
|
|
136
|
+
*
|
|
137
|
+
* Sampling at flush time alone is not enough, and the gap is a real delete
|
|
138
|
+
* bug rather than a theoretical one: an id that arrives and is deleted again
|
|
139
|
+
* between two flushes (a local add, then a remote delete inside the same
|
|
140
|
+
* 800 ms debounce — every doc update re-arms the timer, so this is the common
|
|
141
|
+
* shape, not a rare one) would never be recorded, the guard below would read
|
|
142
|
+
* the surviving disk copy as "never seen", and the delete could never
|
|
143
|
+
* materialize. The observer only ever ADDS, which is what makes it a record
|
|
144
|
+
* of what the doc has held rather than of what it holds.
|
|
145
|
+
*
|
|
146
|
+
* The set is per SLUG and the wiring is per DOC: a slug's room can be rebuilt
|
|
147
|
+
* on a fresh doc, and the ids carry over because this peer did see them.
|
|
148
|
+
*/
|
|
149
|
+
function commentIdsSeenBy(slug: string, doc: Y.Doc): Set<string> {
|
|
150
|
+
let ids = docSeenComments.get(slug);
|
|
151
|
+
if (!ids) {
|
|
152
|
+
ids = new Set<string>();
|
|
153
|
+
docSeenComments.set(slug, ids);
|
|
154
|
+
}
|
|
155
|
+
if (observedCommentDocs.has(doc)) return ids;
|
|
156
|
+
observedCommentDocs.add(doc);
|
|
157
|
+
|
|
158
|
+
const seen = ids;
|
|
159
|
+
const arr = doc.getArray<unknown>(Y_TYPES.comments);
|
|
160
|
+
const absorb = () => {
|
|
161
|
+
for (const c of arr.toArray()) seen.add(commentKey(c));
|
|
162
|
+
for (const stale of seen) {
|
|
163
|
+
if (seen.size <= MAX_SEEN_COMMENT_IDS) break;
|
|
164
|
+
seen.delete(stale); // oldest first — insertion order
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
absorb();
|
|
168
|
+
arr.observe(absorb);
|
|
169
|
+
return ids;
|
|
170
|
+
}
|
|
94
171
|
|
|
95
172
|
function ydocBinPath(slug: string): string {
|
|
96
173
|
return path.join(stateDir, `${slug}.ydoc.bin`);
|
|
@@ -108,6 +185,13 @@ export function createPersistence(deps: PersistenceDeps): RoomCallbacks {
|
|
|
108
185
|
}
|
|
109
186
|
|
|
110
187
|
async function seed(slug: string, doc: Y.Doc): Promise<void> {
|
|
188
|
+
// Start recording comment identities at ROOM MOUNT, before anything can
|
|
189
|
+
// gate out (issue #111). `persistJson` would otherwise be the first thing
|
|
190
|
+
// to observe this doc, which is one flush too late: an id added and deleted
|
|
191
|
+
// before that flush would never be recorded, and its delete could never
|
|
192
|
+
// materialize. This is the earliest callback that sees the doc.
|
|
193
|
+
commentIdsSeenBy(slug, doc);
|
|
194
|
+
|
|
111
195
|
// Phase 9.2 (DDR-064) — under sharedDoc the migrate-seed + hub provider own
|
|
112
196
|
// initial population for a pinned slug; a local file-seed here would push
|
|
113
197
|
// fresh Y.Array items that DUPLICATE the hub's canonical items on merge
|
|
@@ -136,7 +220,22 @@ export function createPersistence(deps: PersistenceDeps): RoomCallbacks {
|
|
|
136
220
|
doc.transact(() => {
|
|
137
221
|
if (comments.length > 0) {
|
|
138
222
|
const arr = doc.getArray<unknown>(Y_TYPES.comments);
|
|
139
|
-
|
|
223
|
+
// Push only what the array does not already hold (issue #112). This was
|
|
224
|
+
// an unconditional `arr.push(comments)`, which concatenates whenever the
|
|
225
|
+
// doc is NOT empty at seed time — the DDR-064 Risk 1 window that
|
|
226
|
+
// `shouldSeed`/`isPinned` closes only when the pin already exists, i.e.
|
|
227
|
+
// not when a room is mounted a beat before the hub provider attaches.
|
|
228
|
+
// The identity rule is `commentKey`, shared with the codec's diff and
|
|
229
|
+
// the cold-start union; the leaf import is deliberate — this file owns
|
|
230
|
+
// `Y_TYPES`, so it can never import `sync/codec.ts` back (see above).
|
|
231
|
+
const present = new Set(arr.toArray().map(commentKey));
|
|
232
|
+
const missing = comments.filter((c) => {
|
|
233
|
+
const k = commentKey(c);
|
|
234
|
+
if (present.has(k)) return false;
|
|
235
|
+
present.add(k);
|
|
236
|
+
return true;
|
|
237
|
+
});
|
|
238
|
+
if (missing.length > 0) arr.push(missing);
|
|
140
239
|
}
|
|
141
240
|
if (svg && typeof svg === 'string') {
|
|
142
241
|
const map = doc.getMap<string>(Y_TYPES.annotations);
|
|
@@ -151,14 +250,31 @@ export function createPersistence(deps: PersistenceDeps): RoomCallbacks {
|
|
|
151
250
|
|
|
152
251
|
// Comments — Y.Array projection back to JSON. Write whenever the doc holds
|
|
153
252
|
// comments, OR when it just emptied after previously holding some (so a
|
|
154
|
-
// delete-all materializes on EVERY peer's disk, not just the originator's)
|
|
155
|
-
//
|
|
156
|
-
//
|
|
253
|
+
// delete-all materializes on EVERY peer's disk, not just the originator's),
|
|
254
|
+
// and never from a doc that was never populated at all (cold start before
|
|
255
|
+
// seed/migrate completes), which would clobber a non-empty local file.
|
|
256
|
+
//
|
|
257
|
+
// `everSeen` is that latch as well as the freshness oracle below — one
|
|
258
|
+
// notion of "this doc has held comments" instead of two that can disagree.
|
|
259
|
+
// It replaces a per-slug flag sampled HERE, which could not see a comment
|
|
260
|
+
// that arrived and was deleted again between two flushes: the flag stayed
|
|
261
|
+
// unset, so the delete-all read as "never populated" and never reached
|
|
262
|
+
// disk. Both readings need the same fact, and only the observer has it.
|
|
157
263
|
const arr = doc.getArray(Y_TYPES.comments);
|
|
158
264
|
const list = arr.toArray() as Parameters<Api['saveCommentsForFile']>[1];
|
|
159
|
-
|
|
160
|
-
if (list.length > 0 ||
|
|
161
|
-
|
|
265
|
+
const everSeen = commentIdsSeenBy(slug, doc);
|
|
266
|
+
if (list.length > 0 || everSeen.size > 0) {
|
|
267
|
+
// Issue #111 — refuse to project a doc that is BEHIND the file. An id on
|
|
268
|
+
// disk this doc has never carried is a write still in flight towards it
|
|
269
|
+
// (a mutation whose file→doc import has not landed, an external edit to
|
|
270
|
+
// `_comments/<slug>.json`); overwriting it deletes a real comment with no
|
|
271
|
+
// error and no recovery. Deferring is safe and self-clearing: whatever
|
|
272
|
+
// brings that id into the doc is itself a doc update, which re-arms the
|
|
273
|
+
// flush, and the next pass writes the merged state. A delete still
|
|
274
|
+
// materializes — its id IS in `everSeen`, so the write proceeds.
|
|
275
|
+
const onDisk = await api.loadCommentsForFile(file);
|
|
276
|
+
const behind = onDisk.some((c) => !everSeen.has(commentKey(c)));
|
|
277
|
+
if (!behind && withinCap(slug, 'comments', JSON.stringify(list), MAX_COMMENTS_BYTES)) {
|
|
162
278
|
await api.saveCommentsForFile(file, list);
|
|
163
279
|
}
|
|
164
280
|
}
|
|
@@ -168,7 +284,11 @@ export function createPersistence(deps: PersistenceDeps): RoomCallbacks {
|
|
|
168
284
|
const svg = map.get('svg');
|
|
169
285
|
if (typeof svg === 'string' && svg) {
|
|
170
286
|
if (withinCap(slug, 'annotations', svg, MAX_ANNOTATIONS_BYTES)) {
|
|
171
|
-
|
|
287
|
+
// Projection must never re-enter onAnnotationsChanged: an old flush
|
|
288
|
+
// finishing after a new edit otherwise republishes the old SVG and
|
|
289
|
+
// rolls back every peer. The API checks freshness after async IO and
|
|
290
|
+
// before its atomic rename; a later doc update schedules a new flush.
|
|
291
|
+
await api.projectAnnotations(file, svg, () => map.get('svg') === svg);
|
|
172
292
|
}
|
|
173
293
|
}
|
|
174
294
|
}
|
|
@@ -8,17 +8,13 @@
|
|
|
8
8
|
|
|
9
9
|
import type { Awareness } from 'y-protocols/awareness';
|
|
10
10
|
|
|
11
|
+
import { ANNOTATION_WRITE_ID, validAnnotationWriteId } from '../annotations-sync.ts';
|
|
12
|
+
import { applyCommentsToDoc } from '../sync/codec.ts';
|
|
11
13
|
import { bridgeAwareness } from './awareness-bridge.ts';
|
|
12
14
|
import { Y_TYPES } from './persistence.ts';
|
|
13
15
|
import type { Room, RoomCallbacks } from './room.ts';
|
|
14
16
|
import { createRoom } from './room.ts';
|
|
15
17
|
|
|
16
|
-
/** Structural equality via canonical JSON — used by the syncRoomFrom* no-op
|
|
17
|
-
* guards. Comment lists are small; stringify is cheap + dependency-free. */
|
|
18
|
-
function jsonEqual(a: unknown, b: unknown): boolean {
|
|
19
|
-
return JSON.stringify(a) === JSON.stringify(b);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
18
|
export interface Registry {
|
|
23
19
|
/** Get-or-create. Reuses an existing room for the same slug. */
|
|
24
20
|
get(slug: string): Room;
|
|
@@ -57,7 +53,7 @@ export interface Registry {
|
|
|
57
53
|
* post-write SVG; the room replaces `Y.Map.svg` so collab peers see the
|
|
58
54
|
* updated stroke set without waiting for a cold-open re-seed.
|
|
59
55
|
*/
|
|
60
|
-
syncRoomFromAnnotations(slug: string, svg: string): void;
|
|
56
|
+
syncRoomFromAnnotations(slug: string, svg: string, writeId?: string): void;
|
|
61
57
|
/**
|
|
62
58
|
* Phase 30 — project agent editing-presence onto a slug's room awareness so
|
|
63
59
|
* it crosses the hub (the loopback `ai-activity` bus event does not). `null`
|
|
@@ -186,27 +182,29 @@ export function createRegistry(callbacks: RoomCallbacks): Registry {
|
|
|
186
182
|
function syncRoomFromComments(slug: string, comments: readonly unknown[]): void {
|
|
187
183
|
const room = rooms.get(slug);
|
|
188
184
|
if (!room) return;
|
|
189
|
-
|
|
190
|
-
//
|
|
191
|
-
//
|
|
192
|
-
//
|
|
193
|
-
//
|
|
194
|
-
//
|
|
195
|
-
//
|
|
196
|
-
|
|
197
|
-
room.doc
|
|
198
|
-
if (arr.length > 0) arr.delete(0, arr.length);
|
|
199
|
-
if (comments.length > 0) arr.push(comments as unknown[]);
|
|
200
|
-
}, 'inspector-write');
|
|
185
|
+
// Through the codec's identity-keyed diff, NOT a local delete-all + push
|
|
186
|
+
// (issue #112). This bridge fires after EVERY comment mutation, so it was
|
|
187
|
+
// one of the three writers that could collide with an in-flight hub update
|
|
188
|
+
// and leave the array holding both runs. The codec keeps the no-op guard
|
|
189
|
+
// this function used to carry inline — an unchanged list emits no
|
|
190
|
+
// transaction, so re-seeding the live room from a disk change (sync-agent
|
|
191
|
+
// or design:edit write — see createCollab's fs hook) still can't spin the
|
|
192
|
+
// 800 ms persist storm the old equality short-circuit existed to prevent.
|
|
193
|
+
applyCommentsToDoc(room.doc, comments as unknown[], 'inspector-write');
|
|
201
194
|
}
|
|
202
195
|
|
|
203
|
-
function syncRoomFromAnnotations(slug: string, svg: string): void {
|
|
196
|
+
function syncRoomFromAnnotations(slug: string, svg: string, writeId?: string): void {
|
|
204
197
|
const room = rooms.get(slug);
|
|
205
198
|
if (!room) return;
|
|
206
199
|
const map = room.doc.getMap<string>(Y_TYPES.annotations);
|
|
207
|
-
|
|
200
|
+
// Identical disk notifications remain no-ops. A new UI operation may
|
|
201
|
+
// deliberately restore identical content while an author has pending work.
|
|
202
|
+
const id = validAnnotationWriteId(writeId) ? writeId : undefined;
|
|
203
|
+
if (map.get('svg') === svg && (!id || map.get(ANNOTATION_WRITE_ID) === id)) return;
|
|
208
204
|
room.doc.transact(() => {
|
|
209
205
|
map.set('svg', svg);
|
|
206
|
+
if (id) map.set(ANNOTATION_WRITE_ID, id);
|
|
207
|
+
else map.delete(ANNOTATION_WRITE_ID);
|
|
210
208
|
}, 'inspector-write');
|
|
211
209
|
}
|
|
212
210
|
|
|
@@ -77,6 +77,15 @@ export interface RoomCallbacks {
|
|
|
77
77
|
* the seeded state.
|
|
78
78
|
*/
|
|
79
79
|
seed(slug: string, doc: Y.Doc): Promise<void> | void;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Accepted revisions (DDR-241 §7): while true, this room's document is a
|
|
83
|
+
* replica of the project's accepted state — no browser connection may write
|
|
84
|
+
* a persistent lane into it (the hub would drop it and the replica would
|
|
85
|
+
* diverge). Comments and annotations go through the API as proposals.
|
|
86
|
+
* Awareness is unaffected. Optional; absent means false.
|
|
87
|
+
*/
|
|
88
|
+
acceptedMode?: () => boolean;
|
|
80
89
|
}
|
|
81
90
|
|
|
82
91
|
export interface Room {
|
|
@@ -216,6 +225,8 @@ export function createRoom(slug: string, callbacks: RoomCallbacks): Room {
|
|
|
216
225
|
*/
|
|
217
226
|
function refusedLanes(conn: RoomConn, roots: ReadonlySet<string>): string[] {
|
|
218
227
|
const refused: string[] = [];
|
|
228
|
+
// Accepted revisions: every persistent lane is the hub's to change.
|
|
229
|
+
if (callbacks.acceptedMode?.()) return [...roots];
|
|
219
230
|
for (const r of roots) {
|
|
220
231
|
if (conn.realm === 'canvas' && (isBodyLane(r) || r === '<unresolved>')) {
|
|
221
232
|
refused.push(r);
|
|
@@ -255,7 +266,7 @@ export function createRoom(slug: string, callbacks: RoomCallbacks): Room {
|
|
|
255
266
|
gateRefusalCount += 1;
|
|
256
267
|
discardMirror();
|
|
257
268
|
console.warn(
|
|
258
|
-
`[collab/${slug}] origin gate REFUSED a ${conn.readOnly ? 'read-only' : 'canvas-realm'} ` +
|
|
269
|
+
`[collab/${slug}] origin gate REFUSED a ${callbacks.acceptedMode?.() ? 'accepted-replica' : conn.readOnly ? 'read-only' : 'canvas-realm'} ` +
|
|
259
270
|
`sync frame (lanes: ${refused.join(', ') || 'unclassifiable'}). ` +
|
|
260
271
|
(conn.readOnly
|
|
261
272
|
? 'A viewer-role socket may only write comments — role matrix, cloud collab lane.'
|
|
@@ -351,7 +362,7 @@ export function createRoom(slug: string, callbacks: RoomCallbacks): Room {
|
|
|
351
362
|
}
|
|
352
363
|
|
|
353
364
|
function receive(conn: RoomConn, payload: Uint8Array): void {
|
|
354
|
-
if (conn.realm === 'canvas' || conn.readOnly === true) {
|
|
365
|
+
if (conn.realm === 'canvas' || conn.readOnly === true || callbacks.acceptedMode?.()) {
|
|
355
366
|
receiveGated(conn, payload);
|
|
356
367
|
return;
|
|
357
368
|
}
|
package/apps/studio/context.ts
CHANGED
|
@@ -259,6 +259,65 @@ export interface Context {
|
|
|
259
259
|
current?(): {
|
|
260
260
|
cancelAssetSweep(): boolean;
|
|
261
261
|
retireForMove?(fromSlug: string, toRel: string): Promise<boolean>;
|
|
262
|
+
/** Accepted-revisions mode (DDR-241) — see SyncRuntime.proposeLane. */
|
|
263
|
+
proposeLane?(
|
|
264
|
+
slug: string,
|
|
265
|
+
lane: 'comments' | 'annotations' | 'meta',
|
|
266
|
+
text: string,
|
|
267
|
+
opts?: { baseText?: string; writeId?: string }
|
|
268
|
+
): Promise<{ status: 'accepted' | 'rejected'; code?: string }> | null;
|
|
269
|
+
/** Accepted-revisions mode (DDR-241) — see SyncRuntime.proposeFolder. */
|
|
270
|
+
proposeFolder?(
|
|
271
|
+
op:
|
|
272
|
+
| { op: 'dir.create'; path: string }
|
|
273
|
+
| { op: 'dir.delete'; path: string }
|
|
274
|
+
| { op: 'dir.move'; from: string; to: string }
|
|
275
|
+
): Promise<{ status: 'accepted' | 'rejected'; code?: string; queued?: boolean }> | null;
|
|
276
|
+
acceptedMode?(): boolean;
|
|
277
|
+
acceptedHistory?(q: {
|
|
278
|
+
limit?: number;
|
|
279
|
+
before?: number | null;
|
|
280
|
+
path?: string | null;
|
|
281
|
+
}): Promise<unknown[] | null>;
|
|
282
|
+
acceptedVersion?(repoRel: string, revision: number): Promise<string | null>;
|
|
283
|
+
acceptedRestore?(
|
|
284
|
+
repoRel: string,
|
|
285
|
+
revision: number
|
|
286
|
+
): Promise<{ status: 'accepted' | 'rejected'; code?: string; queued?: boolean } | null>;
|
|
287
|
+
acceptedUndo?(
|
|
288
|
+
actionId: string,
|
|
289
|
+
redo?: boolean
|
|
290
|
+
): Promise<{
|
|
291
|
+
status: 'accepted' | 'rejected';
|
|
292
|
+
code?: string;
|
|
293
|
+
queued?: boolean;
|
|
294
|
+
actionId?: string;
|
|
295
|
+
} | null>;
|
|
296
|
+
acceptedActionForContent?(repoRel: string, content: string): string | null;
|
|
297
|
+
prepareOffline?(): Promise<{
|
|
298
|
+
complete: boolean;
|
|
299
|
+
pulled: number;
|
|
300
|
+
passes: number;
|
|
301
|
+
failed: number;
|
|
302
|
+
} | null>;
|
|
303
|
+
conflictVersions?(
|
|
304
|
+
repoRel: string
|
|
305
|
+
): { slug: string; mine: string | null; theirs: string } | null;
|
|
306
|
+
resolveConflict?(
|
|
307
|
+
repoRel: string,
|
|
308
|
+
choice: 'mine' | 'theirs'
|
|
309
|
+
): Promise<{ status: 'accepted' | 'rejected' | 'taken'; code?: string } | null>;
|
|
310
|
+
/** T16 — AI action boundaries (see sync/action-stage.ts). */
|
|
311
|
+
beginAiAction?(key: string, label: string): void;
|
|
312
|
+
endAiAction?(
|
|
313
|
+
key: string,
|
|
314
|
+
outcome: 'done' | 'failed'
|
|
315
|
+
): Promise<{ status: 'accepted' | 'rejected'; code?: string } | null>;
|
|
316
|
+
resolveAiAction?(choice: 'publish' | 'discard'): Promise<{
|
|
317
|
+
status: 'accepted' | 'rejected' | 'discarded';
|
|
318
|
+
code?: string;
|
|
319
|
+
canvases?: number;
|
|
320
|
+
} | null>;
|
|
262
321
|
} | null;
|
|
263
322
|
};
|
|
264
323
|
}
|
|
@@ -120,6 +120,13 @@ export function scrub(text: string, opts: ScrubOptions = {}): string {
|
|
|
120
120
|
export interface DebugBundle {
|
|
121
121
|
app: { maudeVersion: string; platform: string; arch: string };
|
|
122
122
|
context: { projectName: string | null; activeCanvas: string | null };
|
|
123
|
+
/** Server process memory at report time (#119). A runaway-memory report used
|
|
124
|
+
* to arrive as a number the user eyeballed in Activity Monitor, with nothing
|
|
125
|
+
* in the bundle to confirm or refute it — and the log ring was silent
|
|
126
|
+
* because the heap watch was measuring the wrong counter. `rss` is the same
|
|
127
|
+
* number the user sees, so a future report of this class carries its own
|
|
128
|
+
* evidence. Bytes; process-level, never per-request. */
|
|
129
|
+
process: { rssBytes: number; heapUsedBytes: number; uptimeSeconds: number };
|
|
123
130
|
logs: { serverLogTail: string };
|
|
124
131
|
}
|
|
125
132
|
|
|
@@ -137,6 +144,7 @@ export function buildDebugBundle(args: {
|
|
|
137
144
|
tailLines?: number;
|
|
138
145
|
}): DebugBundle {
|
|
139
146
|
const scrubOpts: ScrubOptions = { repoRoot: args.repoRoot };
|
|
147
|
+
const memory = process.memoryUsage();
|
|
140
148
|
return {
|
|
141
149
|
app: {
|
|
142
150
|
maudeVersion: args.maudeVersion,
|
|
@@ -147,6 +155,11 @@ export function buildDebugBundle(args: {
|
|
|
147
155
|
projectName: args.projectName,
|
|
148
156
|
activeCanvas: args.activeCanvas ? scrub(args.activeCanvas, scrubOpts) : null,
|
|
149
157
|
},
|
|
158
|
+
process: {
|
|
159
|
+
rssBytes: memory.rss,
|
|
160
|
+
heapUsedBytes: memory.heapUsed,
|
|
161
|
+
uptimeSeconds: Math.round(process.uptime()),
|
|
162
|
+
},
|
|
150
163
|
logs: {
|
|
151
164
|
serverLogTail: logRingLines(args.tailLines ?? 200)
|
|
152
165
|
.map((l) => scrub(l, scrubOpts))
|