{
  "v1Version": "1.45.0",
  "v2Version": "2.0.0-next.36",
  "dispositions": {
    "mechanical": "A direct substitution with equivalent behavior. Safe to apply mechanically, and required to be backed by a compiled fixture before it carries this label.",
    "redesign": "A replacement exists, but the semantics differ. Read the replacement and re-verify the behavior; do not apply it as a find-and-replace.",
    "unsupported": "No v2 equivalent. The capability must be removed or rebuilt outside SuperDoc."
  },
  "entries": [
    {
      "id": "subpath.types",
      "v1": "superdoc/types",
      "v2": "superdoc",
      "disposition": "redesign",
      "failureMode": "unresolved-path",
      "surface": "package",
      "symptom": "Module resolution fails; the subpath is absent from the v2 exports map.",
      "notes": "Not a path rename. v1 re-exported 116 names here, almost all of them ProseMirror and schema types, and none of them exist on the v2 root. Integration types such as `Config`, `DocumentMode`, `User`, and `DocumentApi` are exported from `superdoc`; types describing ProseMirror nodes, marks, commands, and transactions have no v2 equivalent because the model they described is gone.",
      "docsPath": "/document-api/mental-model"
    },
    {
      "id": "subpath.super-editor",
      "v1": "superdoc/super-editor",
      "v2": "superdoc.activeEditor",
      "disposition": "redesign",
      "failureMode": "unresolved-path",
      "surface": "package",
      "symptom": "Module resolution fails; the subpath is absent from the v2 exports map.",
      "notes": "v2 has no importable editor class. Reach the active document through the `SuperDoc` instance and its Document API facade.",
      "docsPath": "/document-api/mental-model"
    },
    {
      "id": "subpath.converter",
      "v1": "superdoc/converter",
      "v2": null,
      "disposition": "unsupported",
      "failureMode": "unresolved-path",
      "surface": "converter",
      "symptom": "Module resolution fails; the subpath is absent from the v2 exports map.",
      "notes": "v2 owns DOCX parsing and serialization internally. Load documents through `document` and produce output through `export()`.",
      "docsPath": "/editor/load-and-save-documents"
    },
    {
      "id": "subpath.docx-zipper",
      "v1": "superdoc/docx-zipper",
      "v2": null,
      "disposition": "unsupported",
      "failureMode": "unresolved-path",
      "surface": "converter",
      "symptom": "Module resolution fails; the subpath is absent from the v2 exports map.",
      "notes": "No public archive surface in v2. Use the supported load and export workflow."
    },
    {
      "id": "subpath.file-zipper",
      "v1": "superdoc/file-zipper",
      "v2": null,
      "disposition": "unsupported",
      "failureMode": "unresolved-path",
      "surface": "converter",
      "symptom": "Module resolution fails; the subpath is absent from the v2 exports map.",
      "notes": "No public archive surface in v2. Use the supported load and export workflow."
    },
    {
      "id": "subpath.headless-toolbar",
      "v1": "superdoc/headless-toolbar",
      "v2": "superdoc.ui",
      "disposition": "redesign",
      "failureMode": "unresolved-path",
      "surface": "custom-ui",
      "symptom": "Module resolution fails; the subpath is absent from the v2 exports map.",
      "notes": "`superdoc.ui` is a getter that lazily creates and owns the controller. It returns `BorrowedSuperDocUI`, which omits `destroy()` because `superdoc.destroy()` owns teardown. Call `createSuperDocUI({ superdoc })` from `superdoc/ui` only when you want an independently owned controller to dispose yourself.",
      "docsPath": "/editor/custom-ui/overview"
    },
    {
      "id": "subpath.headless-toolbar-react",
      "v1": "superdoc/headless-toolbar/react",
      "v2": "superdoc/ui/react",
      "disposition": "redesign",
      "failureMode": "unresolved-path",
      "surface": "custom-ui",
      "symptom": "Module resolution fails; the subpath is absent from the v2 exports map.",
      "notes": "Hooks and providers moved and now bind to the instance-owned controller.",
      "docsPath": "/editor/custom-ui/overview"
    },
    {
      "id": "subpath.headless-toolbar-vue",
      "v1": "superdoc/headless-toolbar/vue",
      "v2": "superdoc.ui",
      "disposition": "redesign",
      "failureMode": "unresolved-path",
      "surface": "custom-ui",
      "symptom": "Module resolution fails; the subpath is absent from the v2 exports map.",
      "notes": "v2 ships no Vue-specific bindings. Build on the framework-agnostic controller.",
      "docsPath": "/editor/custom-ui/overview"
    },
    {
      "id": "subpath-export.ui-types",
      "v1": "superdoc/ui type exports",
      "isGroup": true,
      "v1Symbols": [
        "ContextMenuContribution",
        "ContextMenuWhenInput",
        "DocumentExportInput",
        "DynamicCommandHandle",
        "PublicToolbarItemId",
        "SelectionAnchorRectOptions",
        "TextSegment",
        "ToolbarCommandHandleState",
        "UIToolbarCommandState",
        "ViewportContextAtInput",
        "ViewportEntityAtInput",
        "ViewportPositionAtInput",
        "ViewportPositionHit",
        "ZoomMode",
        "ZoomViewportMetrics"
      ],
      "v2": "superdoc/ui",
      "disposition": "redesign",
      "failureMode": "missing-export",
      "surface": "custom-ui",
      "symptom": "The import path still resolves, so only the named type fails. TypeScript reports the missing name; a plain JavaScript build sees nothing because these are type-only.",
      "notes": "v2 rebuilt `superdoc/ui` as a native controller rather than re-exporting v1, so these 15 type names did not carry over. Use the controller types v2 publishes from the same path.",
      "docsPath": "/editor/custom-ui/overview"
    },
    {
      "id": "root.Editor",
      "v1": "Editor",
      "v2": "superdoc.activeEditor",
      "disposition": "redesign",
      "failureMode": "missing-export",
      "surface": "editor-internals",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "v2 exposes no editor constructor. Mount through `SuperDoc` and read the active document via its Document API facade.",
      "docsPath": "/document-api/mental-model"
    },
    {
      "id": "root.PresentationEditor",
      "v1": "PresentationEditor",
      "v2": null,
      "disposition": "unsupported",
      "failureMode": "missing-export",
      "surface": "editor-internals",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "v2 owns rendering internally. There is no public presentation-editor surface."
    },
    {
      "id": "root.SuperEditor",
      "v1": "SuperEditor",
      "v2": "SuperDoc",
      "disposition": "redesign",
      "failureMode": "missing-export",
      "surface": "editor-internals",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "The mounting capability survives, the component does not. v1 rendered a Vue component taking `fileSource`, `documentId`, `state`, and `options` props; v2 mounts imperatively with `new SuperDoc({ selector, document })`. A React or Vue wrapper is now application code around that instance.",
      "docsPath": "/editor/quickstart"
    },
    {
      "id": "root.SuperInput",
      "v1": "SuperInput",
      "v2": null,
      "disposition": "unsupported",
      "failureMode": "missing-export",
      "surface": "editor-internals",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "v2 ships no importable single-line input component, and no v2 surface is a drop-in equivalent. Build the control in your application."
    },
    {
      "id": "root.SuperConverter",
      "v1": "SuperConverter",
      "v2": null,
      "disposition": "unsupported",
      "failureMode": "missing-export",
      "surface": "converter",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "DOCX conversion is internal to the v2 engine. Load through `document` and produce output through `export()`.",
      "docsPath": "/editor/load-and-save-documents"
    },
    {
      "id": "root.DocxZipper",
      "v1": "DocxZipper",
      "v2": null,
      "disposition": "unsupported",
      "failureMode": "missing-export",
      "surface": "converter",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "No public archive surface in v2."
    },
    {
      "id": "root.createZip",
      "v1": "createZip",
      "v2": null,
      "disposition": "unsupported",
      "failureMode": "missing-export",
      "surface": "converter",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "No public archive surface in v2."
    },
    {
      "id": "root.SuperToolbar",
      "v1": "SuperToolbar",
      "v2": "config.toolbar",
      "disposition": "redesign",
      "failureMode": "missing-export",
      "surface": "custom-ui",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "For the built-in toolbar, pass a container through `toolbar` in the config. For a custom toolbar, drive `superdoc.ui`.",
      "docsPath": "/editor/built-in-ui/configure-the-toolbar"
    },
    {
      "id": "root.Toolbar",
      "v1": "Toolbar",
      "v2": "config.toolbar",
      "disposition": "redesign",
      "failureMode": "missing-export",
      "surface": "custom-ui",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "The built-in toolbar is configured, not imported and rendered by the consumer.",
      "docsPath": "/editor/built-in-ui/configure-the-toolbar"
    },
    {
      "id": "root.ContextMenu",
      "v1": "ContextMenu",
      "v2": null,
      "disposition": "unsupported",
      "failureMode": "missing-export",
      "surface": "custom-ui",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "`getV2FeatureMatrix()` reports `shell.context-menu` as not-shipped: the v2 shell disables the legacy context menu. A `modules.contextMenu` config field exists but no v2 runtime renders it, so it is not a working replacement. Build an application-owned menu on `superdoc.ui` if you need one.",
      "docsPath": "/editor/built-in-ui/links-and-context-menus"
    },
    {
      "id": "root.SlashMenu",
      "v1": "SlashMenu",
      "v2": null,
      "disposition": "unsupported",
      "failureMode": "missing-export",
      "surface": "custom-ui",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "`modules.slashMenu` is marked deprecated in favor of `modules.contextMenu`, which is itself not-shipped. Do not migrate onto either; build an application-owned menu on `superdoc.ui`."
    },
    {
      "id": "root.AIWriter",
      "v1": "AIWriter",
      "v2": "config.modules.ai",
      "disposition": "redesign",
      "failureMode": "missing-export",
      "surface": "custom-ui",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "The built-in toolbar renders the AI writer when `modules.ai` is configured, and applies generated text through the Document API."
    },
    {
      "id": "root.Extensions",
      "v1": "Extensions",
      "v2": "defineSuperDocExtension",
      "disposition": "redesign",
      "failureMode": "missing-export",
      "surface": "extensions",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "v2 extensions receive commands, anchors, and decorations. They do not receive ProseMirror state, custom schema, or mutable DOM, so extensions that defined custom nodes or marks may have no v2 equivalent.",
      "docsPath": "/editor/custom-ui/custom-commands"
    },
    {
      "id": "root.defineNode",
      "v1": "defineNode",
      "v2": null,
      "disposition": "unsupported",
      "failureMode": "missing-export",
      "surface": "extensions",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "v2 has no custom-schema surface. Custom document nodes cannot be reproduced.",
      "docsPath": "/editor/custom-ui/custom-commands"
    },
    {
      "id": "root.defineMark",
      "v1": "defineMark",
      "v2": null,
      "disposition": "unsupported",
      "failureMode": "missing-export",
      "surface": "extensions",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "v2 has no custom-schema surface. Custom marks cannot be reproduced.",
      "docsPath": "/editor/custom-ui/custom-commands"
    },
    {
      "id": "root.getStarterExtensions",
      "v1": "getStarterExtensions",
      "v2": null,
      "disposition": "unsupported",
      "failureMode": "missing-export",
      "surface": "extensions",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "v2 loads its own document capabilities. There is no consumer-assembled extension list."
    },
    {
      "id": "root.getRichTextExtensions",
      "v1": "getRichTextExtensions",
      "v2": null,
      "disposition": "unsupported",
      "failureMode": "missing-export",
      "surface": "extensions",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "v2 loads its own document capabilities. There is no consumer-assembled extension list."
    },
    {
      "id": "root.isNodeType",
      "v1": "isNodeType",
      "v2": null,
      "disposition": "unsupported",
      "failureMode": "missing-export",
      "surface": "extensions",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "ProseMirror node predicates have no v2 equivalent. Query structure through the Document API.",
      "docsPath": "/document-api/query-content"
    },
    {
      "id": "root.assertNodeType",
      "v1": "assertNodeType",
      "v2": null,
      "disposition": "unsupported",
      "failureMode": "missing-export",
      "surface": "extensions",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "ProseMirror node predicates have no v2 equivalent. Query structure through the Document API.",
      "docsPath": "/document-api/query-content"
    },
    {
      "id": "root.isMarkType",
      "v1": "isMarkType",
      "v2": null,
      "disposition": "unsupported",
      "failureMode": "missing-export",
      "surface": "extensions",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "ProseMirror mark predicates have no v2 equivalent."
    },
    {
      "id": "root.getSchemaIntrospection",
      "v1": "getSchemaIntrospection",
      "v2": null,
      "disposition": "unsupported",
      "failureMode": "missing-export",
      "surface": "extensions",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "v2 exposes no ProseMirror schema to introspect."
    },
    {
      "id": "root.registeredHandlers",
      "v1": "registeredHandlers",
      "v2": null,
      "disposition": "unsupported",
      "failureMode": "missing-export",
      "surface": "extensions",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "Marked internal in v1 and removed in v2."
    },
    {
      "id": "root.superEditorHelpers",
      "v1": "superEditorHelpers",
      "v2": "superdoc.activeEditor.doc",
      "disposition": "redesign",
      "failureMode": "missing-export",
      "surface": "editor-internals",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "Helper bags are replaced by explicit Document API operations.",
      "docsPath": "/document-api/mental-model"
    },
    {
      "id": "root.trackChangesHelpers",
      "v1": "trackChangesHelpers",
      "v2": "superdoc.activeEditor.doc.trackChanges",
      "disposition": "redesign",
      "failureMode": "missing-export",
      "surface": "editor-internals",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "Helper bag replaced by explicit Document API operations for listing, navigating, and deciding tracked changes.",
      "docsPath": "/document-api/tracked-changes"
    },
    {
      "id": "root.fieldAnnotationHelpers",
      "v1": "fieldAnnotationHelpers",
      "v2": "superdoc.activeEditor.doc",
      "disposition": "redesign",
      "failureMode": "missing-export",
      "surface": "editor-internals",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "Field and annotation work moves to Document API operations."
    },
    {
      "id": "root.AnnotatorHelpers",
      "v1": "AnnotatorHelpers",
      "v2": "superdoc.activeEditor.doc",
      "disposition": "redesign",
      "failureMode": "missing-export",
      "surface": "editor-internals",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "Helper bag replaced by explicit Document API operations. Field and annotation work goes through `doc`, not a helper namespace."
    },
    {
      "id": "root.SectionHelpers",
      "v1": "SectionHelpers",
      "v2": "superdoc.activeEditor.doc.sections",
      "disposition": "redesign",
      "failureMode": "missing-export",
      "surface": "editor-internals",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "Verify coverage before migrating: linked-section workflows may not have full v2 equivalents."
    },
    {
      "id": "root.getMarksFromSelection",
      "v1": "getMarksFromSelection",
      "v2": "superdoc.ui",
      "disposition": "redesign",
      "failureMode": "missing-export",
      "surface": "custom-ui",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "Active formatting is published as reactive controller state rather than read from a selection.",
      "docsPath": "/editor/custom-ui/overview"
    },
    {
      "id": "root.getActiveFormatting",
      "v1": "getActiveFormatting",
      "v2": "superdoc.ui",
      "disposition": "redesign",
      "failureMode": "missing-export",
      "surface": "custom-ui",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "Active formatting is published as reactive controller state rather than read from a selection.",
      "docsPath": "/editor/custom-ui/overview"
    },
    {
      "id": "root.getAllowedImageDimensions",
      "v1": "getAllowedImageDimensions",
      "v2": null,
      "disposition": "unsupported",
      "failureMode": "missing-export",
      "surface": "editor-internals",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "No public sizing helper in v2. Compute the constraint in your application, or let the engine apply its own bounds on insert."
    },
    {
      "id": "root.CommentsPluginKey",
      "v1": "CommentsPluginKey",
      "v2": "superdoc.activeEditor.doc.comments",
      "disposition": "redesign",
      "failureMode": "missing-export",
      "surface": "editor-internals",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "v2 exposes no ProseMirror plugin keys. Use the Document API comments operations.",
      "docsPath": "/document-api/comments"
    },
    {
      "id": "root.TrackChangesBasePluginKey",
      "v1": "TrackChangesBasePluginKey",
      "v2": "superdoc.activeEditor.doc.trackChanges",
      "disposition": "redesign",
      "failureMode": "missing-export",
      "surface": "editor-internals",
      "symptom": "ESM and TypeScript reject the import. A CommonJS `require` binds `undefined` instead, so the failure surfaces later at the call site.",
      "notes": "v2 exposes no ProseMirror plugin keys.",
      "docsPath": "/document-api/tracked-changes"
    },
    {
      "id": "runtime.commands",
      "v1": "editor.commands",
      "v2": "superdoc.activeEditor.doc",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "editor-internals",
      "symptom": "A null-property error at the point of use. The message names the command, not SuperDoc, so it does not read as a migration failure.",
      "notes": "The v2 facade exposes `commands` as null rather than removing it. Optional chaining converts the throw into a silent no-op, which is harder to notice.",
      "docsPath": "/document-api/mental-model"
    },
    {
      "id": "runtime.state",
      "v1": "editor.state",
      "v2": "superdoc.activeEditor.doc",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "editor-internals",
      "symptom": "A null-property error, or `undefined` when accessed with optional chaining.",
      "notes": "There is no ProseMirror document model in v2. Positions expressed as numeric offsets have no equivalent; use addresses and targets.",
      "docsPath": "/document-api/query-content"
    },
    {
      "id": "runtime.view",
      "v1": "editor.view",
      "v2": null,
      "disposition": "unsupported",
      "failureMode": "runtime",
      "surface": "editor-internals",
      "symptom": "A null-property error, or `undefined` when accessed with optional chaining.",
      "notes": "v2 renders through its own engine and exposes no editor view or mutable DOM."
    },
    {
      "id": "runtime.documentRuntime",
      "v1": "superdoc.getDocumentRuntimeForDocument(documentId)",
      "v2": "superdoc.ui",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "custom-ui",
      "symptom": "The lookup returns `null`, and application code that treats the renderer as the ready UI surface throws an error such as `DocumentRuntime is not available`.",
      "notes": "V2 custom UI does not need to retrieve the renderer runtime. Read the instance-owned `superdoc.ui` controller after `onReady`; it publishes reactive feature state and routes UI actions without exposing the renderer object. `superdoc.ui` is a getter, not an import. The instance creates it once and owns its cleanup.",
      "docsPath": "/editor/migrate-from-v1/overview"
    },
    {
      "id": "runtime.documentRuntime.scrollToElement",
      "v1": "documentRuntime.scrollToElement(elementId)",
      "v2": "superdoc.ui.*.scrollTo / superdoc.scrollToElement",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "custom-ui",
      "symptom": "The preceding runtime lookup returns `null`, so navigation throws or is skipped even though the document is mounted.",
      "notes": "Route known entities through their feature handle: `ui.trackChanges.scrollTo(changeId)`, `ui.comments.scrollTo(commentId)`, or `ui.contentControls.scrollIntoView({ id })`. These methods resolve through the public host navigation surface and can reveal virtualized pages. Use `superdoc.scrollToElement(elementId)` when all you have is a stable block, comment, or tracked-change id. The root facade keeps the renderer object private and returns `false` when navigation cannot be routed.",
      "docsPath": "/editor/migrate-from-v1/overview"
    },
    {
      "id": "runtime.view.dispatchSelection",
      "v1": "editor.view.dispatch(tr.setSelection(...))",
      "v2": "superdoc.ui.selection.apply",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "custom-ui",
      "symptom": "A null-property error on `view`, or `undefined` under optional chaining.",
      "notes": "Setting the selection programmatically is supported. Pass a `SelectionTarget` to `selection.apply()` rather than dispatching a transaction. Read it from `selection.current().selectionTarget` or a query result; there is no ProseMirror position to hand it.",
      "docsPath": "/editor/custom-ui/selection-and-viewport"
    },
    {
      "id": "runtime.view.posAtCoords",
      "v1": "editor.view.posAtCoords",
      "v2": "superdoc.ui.viewport.entityAt",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "custom-ui",
      "symptom": "A null-property error on `view`, or `undefined` under optional chaining.",
      "notes": "Partial. `viewport.entityAt({ x, y })` resolves the public entities painted under a point (tracked changes, comments, content controls), innermost first, and returns `[]` over plain text. That covers hit-testing, which is what most `posAtCoords` callers wanted, but v2 ships no general point-to-document-position resolver, so a caller that needs an arbitrary position under the cursor has no equivalent. The legacy positional form `entityAt(x, y)` fails closed and returns `null`.",
      "docsPath": "/editor/custom-ui/selection-and-viewport"
    },
    {
      "id": "runtime.view.coordsAtPos",
      "v1": "editor.view.coordsAtPos",
      "v2": "superdoc.ui.viewport.getRect",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "custom-ui",
      "symptom": "A null-property error on `view`, or `undefined` under optional chaining.",
      "notes": "The opposite direction from `entityAt`, and a different surface. `viewport.getRect({ target })` resolves painted geometry for a public target and returns `{ found, rects, rect, reason }`. It is keyed on a target, not a numeric position, so obtain one from `selection.capture()` or a query result first.",
      "docsPath": "/editor/custom-ui/selection-and-viewport"
    },
    {
      "id": "runtime.trackChanges.navigate",
      "v1": "manual tracked-change traversal through editor.state",
      "v2": "superdoc.ui.trackChanges.navigateNext",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "custom-ui",
      "symptom": "A null-property error on `state`, or `undefined` under optional chaining.",
      "notes": "Review navigation is supported. `trackChanges.navigateNext()` moves through changes and `trackChanges.list()` reads the catalog. The catalog resolves asynchronously, so observe the slice until `status` is `ready` before consuming `items`.",
      "docsPath": "/editor/custom-ui/tracked-changes"
    },
    {
      "id": "runtime.editorExtensions",
      "v1": "editorExtensions",
      "v2": "extensions",
      "disposition": "redesign",
      "failureMode": "config-silent",
      "surface": "extensions",
      "symptom": "The config typechecks and the editor mounts, but the extensions never run. SuperDoc logs a console warning naming the field.",
      "notes": "A legacy ProseMirror concept that v2 ignores. Rebuild against `extensions` with `defineSuperDocExtension`; extensions defining custom nodes or marks may have no equivalent.",
      "docsPath": "/editor/custom-ui/custom-commands"
    },
    {
      "id": "runtime.modulesCollaboration",
      "v1": "modules.collaboration",
      "v2": "document.v2Collaboration",
      "disposition": "redesign",
      "failureMode": "config-silent",
      "surface": "collaboration",
      "symptom": "The config typechecks, then SuperDoc refuses to attach the provider and reports a terminal compatibility failure.",
      "notes": "v2 owns the provider and Y.Doc. This is a data migration, not a config change: v2 rooms use a different format and must not be pointed at an existing v1 room.",
      "docsPath": "/editor/migrate-from-v1/overview"
    },
    {
      "id": "runtime.commands.replaceWithFieldAnnotation.citation",
      "isGroup": true,
      "v1": "editor.commands.replaceWithFieldAnnotation (citation)",
      "v1Symbols": ["editor.commands.replaceWithFieldAnnotation"],
      "v2": "doc.citations.insert",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "editor-internals",
      "symptom": "A null-property error on `commands`, or a silent no-op under optional chaining.",
      "notes": "One v1 node type did two jobs and v2 splits them, so decide which this annotation was before porting it. This row is the citation branch: a marker backed by a source record is `citations.sources.insert()` followed by `citations.insert({ at, sourceIds })`, where `sourceIds` carries the canonical `sourceId` from the receipt rather than the source tag. Not a rename, and not a replacement either: v1 replaced its `from`/`to` range, while `citations.insert` resolves `at`, then prepends the field to the start of the containing paragraph and reports offset 0. The selected text is left where it was. Delete the range yourself if the citation is meant to stand in for it, and do not expect the citation to land at the offset you passed. If the annotation was application-owned data rather than a citation, use the `metadata.attach` row instead.",
      "docsPath": "/document-api/reference/citations/insert"
    },
    {
      "id": "runtime.commands.replaceWithFieldAnnotation.metadata",
      "isGroup": true,
      "v1": "editor.commands.replaceWithFieldAnnotation (application annotation)",
      "v1Symbols": ["editor.commands.replaceWithFieldAnnotation"],
      "v2": "doc.metadata.attach",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "editor-internals",
      "symptom": "A null-property error on `commands`, or a silent no-op under optional chaining.",
      "notes": "The other half of the same v1 call. An annotation carrying application-owned data anchored to a range is `metadata.attach({ id, namespace, target, payload })`, and the entries it creates are not v1 field annotations: the ids, the payloads, and the anchoring model all differ, so treat this as remodelling rather than a rename. Read them back with `metadata.list({})` and `metadata.get({ id })`. If the annotation was a citation with a source record, use the `citations.insert` row instead.",
      "docsPath": "/document-api/reference/metadata/attach"
    },
    {
      "id": "runtime.state.citationNodes",
      "v1": "editor.state.doc.descendants (citation nodes)",
      "v2": "doc.citations.list",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "editor-internals",
      "symptom": "A null-property error on `state`, or `undefined` under optional chaining.",
      "notes": "Reading citations no longer means walking a node tree. `citations.list()` returns the rows with their addresses and `sourceIds`. Item order can drive application-owned numbering, but that produces local UI labels only: it does not change the citation field result stored in the DOCX. The call is synchronous in some hosts and asynchronous in others, so shared browser and headless code should normalize with `await Promise.resolve(doc.citations.list())`.",
      "docsPath": "/document-api/reference/citations/list"
    },
    {
      "id": "runtime.helpers.fieldAnnotations",
      "v1": "editor.helpers.fieldAnnotations.getAll()",
      "v2": "doc.metadata.list",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "editor-internals",
      "symptom": "A null-property error on `helpers`, or `undefined` under optional chaining.",
      "notes": "A shape change, not a rename. `getAll()` returned v1 field annotations; `metadata.list({})` returns anchored-metadata entries whose ids, payloads, and anchoring model all differ. `metadata.list` is the discovery call and `metadata.get({ id })` reads one payload, so pair them when you need the collection. `get` returns null for an entry that disappeared between the two calls. Treat legacy field annotations as something to remodel rather than something this call returns.",
      "docsPath": "/document-api/reference/metadata/list"
    },
    {
      "id": "runtime.citations.presentation",
      "v1": "tr.setNodeMarkup(pos, undefined, { resolvedText })",
      "v2": "doc.fields.insert",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "editor-internals",
      "symptom": "A null-property error reaching the transaction. Citations inserted through the citations API persist and export, but render with no visible text.",
      "notes": "The citations API cannot do this: `CitationInsertInput` accepts only `at` and `sourceIds`, `CitationUpdateInput.patch` accepts only `sourceIds`, and `displayText` is on `CitationInfo` as a read with no writer. `citations.bibliography.rebuild` is not it either; it rebuilds the bibliography from current sources at a `BibliographyAddress` and never touches inline citation text. The one public path is a level lower: `fields.insert({ at, instruction, mode: \"raw\", updatePolicy: \"preserveCached\", cachedResultText })` writes the visible result directly. Three costs come with it. You author the field instruction and keep the source records in step yourself, so you are outside the citation model rather than extending it. `fields.rebuild` recalculates a field and discards the preserved result. And the contract records `preserveCached` returning `CAPABILITY_UNAVAILABLE` on v1-backed sessions, so confirm it on your target runtime before depending on it. There is still no way to change the visible text of a citation the citations API already inserted.",
      "docsPath": "/document-api/reference/fields/insert"
    },
    {
      "id": "runtime.dom.citationRuns",
      "v1": ".superdoc-text-run[data-pm-start] (citation pill styling)",
      "v2": "ctx.visuals.highlight",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "extensions",
      "symptom": "Nothing breaks, which is the problem. The painter still stamps `data-pm-start` on runs, so the selector keeps matching and the code keeps mutating renderer-owned DOM.",
      "notes": "This one does not fail on upgrade. `layout-engine/dom-contract` stamps `data-pm-start` and `data-pm-end` alongside the editor-neutral attributes specifically so v1 consumers keep working, and `.superdoc-text-run` is still the painted class. What you lose is the guarantee: the DOM shape is renderer-owned and carries no stability contract, so this is worth migrating before a paint change breaks it silently rather than after. The supported replacement is a painted class through `ctx.visuals.highlight(id, { className })`, or `ctx.decorations.register` underneath it. A `CitationAddress` is not itself a visual target, but converting one is mechanical rather than blocked, because both sides use the same coordinate system. `CitationAddress.anchor` is an `InlineAnchor` of `{ blockId, offset }` positions, and `Position.offset` is a 0-based offset into the block's flattened text, which is what `SuperDocTextAddress.range` expects: `{ kind: \"text\", blockId: anchor.start.blockId, range: { start: anchor.start.offset, end: anchor.end.offset } }`. The real limit is the class. `className` accepts paint properties only: background, color, text-decoration, outline, and box-shadow, explicitly not padding, margin, display, or position. Recolouring citation text works; a padded pill shape does not. Decorations are render-only and never reach the DOCX."
    },
    {
      "id": "runtime.on.fieldAnnotationClicked",
      "v1": "superdoc.on('fieldAnnotationClicked')",
      "v2": null,
      "disposition": "unsupported",
      "failureMode": "runtime",
      "surface": "custom-ui",
      "symptom": "The subscription is accepted and the handler never fires.",
      "notes": "v2 emits no per-entity click events, and nothing recovers the identity this one carried. `superdoc.ui.viewport.entityAt` is the supported point lookup, but it collects only tracked changes, comments, and content controls, so it cannot return a field annotation and is not a replacement for this event. Whether hit-testing helps at all depends on what the annotation became: content controls are returned, citations and anchored metadata are not. Decide the v2 model for the annotation first, then pick the lookup that model supports."
    },
    {
      "id": "runtime.state.selection",
      "v1": "editor.state.selection",
      "v2": "superdoc.ui.selection.current",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "custom-ui",
      "symptom": "A null-property error on `state`, or `undefined` under optional chaining.",
      "notes": "A v1 `Selection` was one live object used for both reading and preserving, and v2 splits those. To read the current selection use `selection.current()` or the observed snapshot; both describe a collapsed caret. To preserve a selection across something that moves focus, use `capture()` and then `restore()`. Do not reach for `capture()` as the general read: it returns null whenever the selection is empty, which includes a collapsed caret, so caret-dependent code migrated onto it loses the selection it was trying to keep. Either way the payload is document addresses rather than numeric positions.",
      "docsPath": "/editor/custom-ui/selection-and-viewport"
    },
    {
      "id": "runtime.dom.selectionRect",
      "v1": "document.getSelection().getRangeAt(0).getBoundingClientRect()",
      "v2": "superdoc.ui.selection.getAnchorRect",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "custom-ui",
      "symptom": "The browser selection reflects rendered DOM rather than document state, so the rectangle is wrong or the call throws on an empty range.",
      "notes": "`getAnchorRect()` resolves one rectangle from the document selection against the painted layout, and accepts `'start'`, `'end'`, or `'center'`. v1's `'union'` placement has no v2 value: use `selection.getRects()` when a bubble needs the full multi-line union."
    },
    {
      "id": "runtime.dom.commentAnchor",
      "v1": "document.querySelector('[data-comment-id=\"...\"]')",
      "v2": "superdoc.ui.viewport.getRect",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "custom-ui",
      "symptom": "Depends on the sidebar, which is the trap. With the built-in comments UI enabled the selector matches a sidebar card and positions against the wrong element; with it disabled the same selector matches nothing.",
      "notes": "The painter stamps no singular `data-comment-id` on document content, but `FloatingComments.vue` binds one on each sidebar placeholder, so an integration running the built-in comments UI gets a match that is a panel card rather than the painted anchor. Code that scrolls or positions from it silently targets the sidebar. Turn the sidebar off and the same selector returns null, so the failure changes shape with configuration rather than being absent. There is a second near miss: the painter stamps `data-comment-ids`, plural and comma-separated, on `.superdoc-comment-highlight`, and retargeting the selector there puts you back on renderer-owned DOM with no stability contract. Read the comment through `ui.comments.getById()` and resolve its target with `viewport.getRect({ target })` instead. Treat a `null` from `getById()` as \"not loaded yet\" rather than \"no such comment\": it checks the loaded snapshot, then falls back to the Document API, and returns `null` when that fallback is a pending promise, which is the normal browser case. It does not prime the read either, unlike `contentControls.getById()`, and `comments.list()` returns `[]` on the same path. Gate on the observed comments slice, and gate on `listStatus` rather than `status`: `status` folds in the live selection read, so an unrelated selection change holds it at `pending` while the list is ready. Do not cache the rectangle as identity: geometry changes on scroll, zoom, resize, and pagination, and `viewport.observe()` is the signal to measure again.",
      "docsPath": "/editor/custom-ui/selection-and-viewport"
    },
    {
      "id": "runtime.dom.trackedChangeAnchor",
      "v1": "document.querySelector('[data-track-change-id=\"...\"]').scrollIntoView()",
      "v2": "superdoc.ui.trackChanges.scrollTo",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "custom-ui",
      "symptom": "Usually a match, and a `TypeError` when not. The call as written dereferences the result, so a change that is not painted takes down whatever runs it, and the same code works in a short document and throws in a long one.",
      "notes": "This survives the upgrade, which is why it is worth migrating deliberately. The v2 painter still stamps `data-track-change-id` on painted runs, so the selector keeps resolving and nothing announces that it has become unreliable. It misses in two different ways. A change on a page that is not currently painted has no element at all, because `querySelector` sees only mounted DOM and virtualization leaves the rest of the document unrendered. And where several changes affect one marker, only one id lands in the singular attribute: the rest are in `data-track-change-ids`, comma-separated, so a secondary overlapping change is unreachable by the selector even while it is on screen. Both misses reach `.scrollIntoView()` on `null` and throw, unless the call is guarded. The attribute is renderer-owned and carries no stability contract either. `trackChanges.scrollTo(changeId)` takes the id rather than a selector, mounts the target page when it has to, resolves overlapping changes through the same catalog the review UI uses, and reports whether it succeeded.",
      "docsPath": "/editor/custom-ui/tracked-changes"
    },
    {
      "id": "runtime.dom.mutationObserver",
      "v1": "new MutationObserver(...) on the editor element",
      "v2": "superdoc.ui.viewport.observe",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "custom-ui",
      "symptom": "The observer attaches and fires on repaints that do not correspond to document changes, or never fires for layout changes that produce no mutations.",
      "notes": "Watching the rendered DOM was always a proxy for the question consumers were asking. `viewport.observe()` fires when painted geometry is invalidated, including scroll, zoom, resize, pagination, and virtualized page mounts, which a subtree MutationObserver either misses or floods on.",
      "docsPath": "/editor/custom-ui/selection-and-viewport"
    },
    {
      "id": "runtime.dom.contentControlScroll",
      "v1": "element.scrollIntoView() on a content-control element",
      "v2": "superdoc.ui.contentControls.scrollIntoView",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "custom-ui",
      "symptom": "There is no element to hold: the query that produced it matches nothing, so the call throws or is skipped.",
      "notes": "Address the control by id rather than by element: `contentControls.scrollIntoView({ id, block })` resolves it through the host, including on pages that are not currently painted. A DOM-held reference cannot survive virtualization."
    },
    {
      "id": "runtime.comments.isTrackedChange",
      "v1": "editor.comments.filter((c) => c.isTrackedChange)",
      "v2": "superdoc.ui.trackChanges.list",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "custom-ui",
      "symptom": "A null-property error on `comments`. Once ported, the more damaging failure is quiet: a document full of tracked changes reports none.",
      "notes": "v2 separates tracked changes from comments instead of tagging one list. The trap is readiness, not the split: the catalog is an async read, so a synchronous `list()` on a controller that has not settled returns an empty snapshot rather than an error. Observe the slice and consume `items` once `status` is `ready`. `total` and `authors` come from the same slice and carry the same caveat.",
      "docsPath": "/editor/custom-ui/tracked-changes"
    },
    {
      "id": "runtime.presentationEditor",
      "v1": "editor.presentationEditor",
      "isGroup": true,
      "v1Symbols": ["presentationEditor.getPages", "presentationEditor.onLayoutUpdated", "presentationEditor.element"],
      "v2": "superdoc.activeEditor.pageMetrics",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "editor-internals",
      "symptom": "A null-property error on `presentationEditor`, or `undefined` under optional chaining.",
      "notes": "Page metrics survive as a capability. `pageMetrics` exposes `getSnapshot`, `subscribe`, `setZoom`, `scrollToPage`, `revealBodyTarget`, and `pageIndexForBodyTarget`, and `ui.viewport.getHost()` replaces reading `presentationEditor.element`. The facade types `pageMetrics` as `unknown`, so consuming it means declaring the shape yourself and re-verifying it on upgrade; it is not part of the typed public surface the rest of this catalog points at."
    },
    {
      "id": "runtime.dom.pageElement",
      "v1": ".superdoc-page[data-page-index] (page element lookup)",
      "v2": "superdoc.activeEditor.pageMetrics",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "custom-ui",
      "symptom": "Usually nothing, which hides the two cases that matter. The painter still stamps `data-page-index` on `.superdoc-page`, so the selector keeps matching for mounted pages and returns null for virtualized ones.",
      "notes": "The DOM lookup survives the upgrade rather than breaking, so this migrates on correctness rather than on a build error. Page windowing means only mounted pages have elements, and a page outside the window returns null from the selector while still having metrics. Read the page from the metrics snapshot instead. The same untyped-surface caveat applies as for `presentationEditor`, and the painted DOM carries no stability contract even where it currently answers."
    },
    {
      "id": "runtime.onTransaction",
      "v1": "onTransaction",
      "v2": "defineSuperDocExtension + ctx.onMutation",
      "disposition": "redesign",
      "failureMode": "config-silent",
      "surface": "extensions",
      "symptom": "The config typechecks and the editor mounts, and the callback never fires.",
      "notes": "v2 has no transactions to hand back. Declare an extension and subscribe through its activation context: `ctx.onMutation({ origin, sourceComplete, affects }, handler)`. The filter is the point of the redesign, because a v1 handler had to inspect every transaction and decide whether it mattered."
    },
    {
      "id": "runtime.chain.insertPageBreak",
      "v1": "editor.chain().insertPageBreak().run()",
      "v2": "doc.create.paragraph + doc.format.paragraph.setFlowOptions",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "editor-internals",
      "symptom": "A null-property error on `chain`, or `undefined` under optional chaining.",
      "notes": "Not a behavioral equivalent, and the difference decides the port. v1 broke at the caret. v2 creates a paragraph at an address you supply and sets `pageBreakBefore` on it, which starts that paragraph on a new page rather than inserting a standalone break character. A mid-paragraph caret has no address of its own, so code that broke mid-paragraph must split the paragraph first or accept a block boundary. Await the creation: its returned address is the target of the formatting call.",
      "docsPath": "/document-api/reference/format/paragraph/set-flow-options"
    },
    {
      "id": "runtime.documentApi.syncReads",
      "v1": "doc.extract({}) / doc.getMarkdown({}) / doc.selection.current({}) read synchronously",
      "v2": "the same operations, awaited",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "editor-internals",
      "symptom": "The call returns a Promise where the code expected a value, so reads look empty and downstream property access yields `undefined`.",
      "notes": "The v2 browser facade can route reads and mutations through a worker even when the underlying operation is synchronous in headless Node, so the same code is sync in one host and async in the other. Await browser calls, or normalize shared browser and headless code with `await Promise.resolve(call)`, and keep author overrides or other scoped state active until an awaited mutation settles.",
      "docsPath": "/document-api/mental-model"
    },
    {
      "id": "runtime.ui.createScope",
      "v1": "ui.createScope().register(...)",
      "v2": "superdoc.ui.commands.register",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "custom-ui",
      "symptom": "A null-property error on `createScope`, or `undefined` under optional chaining.",
      "notes": "Register on the instance-owned controller: `superdoc.ui` is created once on first read and reused, and its type omits `destroy()` because the instance owns teardown. The registration result is callable and also exposes `unregister()`; keep either and call it on unmount. Command callbacks receive the public `doc`, `ui`, selection, and mode rather than ProseMirror command state, and the selection should be read from `context.selection`, which is synchronous and always present, not through the deep-partial `context.doc`.",
      "docsPath": "/editor/custom-ui/custom-commands"
    },
    {
      "id": "runtime.on.selectionUpdate",
      "v1": "editor.on('selectionUpdate')",
      "v2": "superdoc.ui.selection.subscribe",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "custom-ui",
      "symptom": "The subscription is accepted and the handler never fires.",
      "notes": "`subscribe` fires immediately and passes `{ snapshot }`; `observe` is the value-shaped alias. Both return an unsubscribe function, where v1 required a matching `off`. The payload is a public selection slice with document addresses, not a ProseMirror selection, so handlers that read positions have to be rewritten rather than rebound."
    },
    {
      "id": "runtime.trackChanges.authorColor",
      "v1": "trackedChange.authorColor",
      "v2": "superdoc.ui.trackChanges.list",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "custom-ui",
      "symptom": "Present on some rows and absent on others. Rows without it render `undefined` as a color, and nothing errors.",
      "notes": "Conditional rather than removed. The row projection passes the v2 tracked-changes facade fields through, so `list()` returns `authorColor` for rows whose host supplies one. It is not declared on `TrackChangeInfo`, so it is not part of the typed public surface and reading it means asserting the shape yourself. `trackChanges.authors` is flattened to `readonly string[]` and drops the per-author color the facade returns, so it is not the fallback either. Keep an application-owned default rather than assuming the field is there, and do not mutate the readonly slice arrays to add one.",
      "docsPath": "/editor/custom-ui/tracked-changes"
    },
    {
      "id": "runtime.view.domPaste",
      "v1": "editor.view.dom.addEventListener('paste', ...)",
      "v2": "the element your application passed as the SuperDoc host",
      "disposition": "redesign",
      "failureMode": "runtime",
      "surface": "custom-ui",
      "symptom": "A null-property error on `view`, or `undefined` under optional chaining.",
      "notes": "The capability survives as ordinary DOM work, but the element does not: v2 owns and re-paints its internal DOM, so attach the listener to the stable host element you passed to SuperDoc and remove it on unmount. A bubbling listener is enough when validation only needs `preventDefault()`. This guards browser input only; programmatic Document API mutations emit no DOM paste events."
    }
  ]
}
