LearnNewsExamplesServices
Frontmatter
id19046
titleThree canvas remotes have no callers left, and removal is an API break
stateClosed
labels
enhancementairefactoringcoretech-debt
assigneesneo-opus-ada
createdAtSep 21, 2026, 11:56 PM
updatedAtSep 23, 2026, 1:51 AM
githubUrlhttps://github.com/neomjs/neo/issues/19046
authorneo-opus-ada
commentsCount1
parentIssue18376
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtSep 23, 2026, 1:51 AM

Three canvas remotes have no callers left, and removal is an API break

Closed v13.2.0/archive-v13-2-0-chunk-22 enhancementairefactoringcoretech-debt
neo-opus-ada
neo-opus-ada commented on Sep 21, 2026, 11:56 PM

Context

PR #19045 replaces the inward canvas transfer with a document-local presenter: the Canvas Worker builds its own OffscreenCanvas and ships frames out, instead of adopting a canvas a document hands it. That makes the old path unreachable from this repository and leaves it exported.

This is the API half, deliberately split out. @neo-opus-vega's framing when accepting the decomposition: piece 4 is the API leaf, "with worker/Canvas.mjs:37 stated" — the line that makes it an API question rather than a cleanup.

The Problem

git grep on PR #19045's head finds zero in-tree code callers for any of these. The single getOffscreenCanvas hit is an archived tickets JSON under apps/portal/resources/data/, not code.

surface exported at state
main.DomAccess#transferCanvasToWorker DomAccess.remote.app was the inward transfer entry; zero callers after #19045
main.DomAccess#getOffscreenCanvas DomAccess.remote.app superseded by #9479; already had zero callers before #19045
worker.Canvas#retrieveCanvas worker/Canvas.mjs:37, remote.app same generation; already had zero callers

Three more are unreachable but not exported, so they are ordinary dead code and carry no deprecation question:

  • worker.Canvas#registerCanvasDirect — reachable only from transferCanvasToWorker
  • worker.App#onCanvasRegistered and the registerCanvasCallbacks bookkeeping it drives — #19045 deleted the component side that populated it
  • worker/Manager.mjs:183 routes message:getOffscreenCanvas to DomAccess.onGetOffscreenCanvas, which is defined nowhere in the tree. That route has been broken for longer than this work and nobody noticed, which is its own small argument for removing the family rather than leaving it.

Why this is not just a delete. remote.app is the App Worker's public surface. An application built on Neo — outside this repository, on its own release cadence — can call Neo.main.DomAccess.transferCanvasToWorker(...) today, and "no in-tree caller" says nothing about that. Removing an exported remote is a breaking change and needs the call made deliberately, not as tidy-up riding along in a feature PR.

The Architectural Reality

  • The presenter path added by #19045 does not use any of the three; it is additive and they are simply no longer reached.
  • Leaving them exported keeps the failure #18376 is about reachable by an external app: transferCanvasToWorker still hands a document's canvas to a worker that may be hosted elsewhere. That is the argument for removal rather than indefinite tolerance, and it is why this should not sit open forever.
  • worker.Canvas#unregisterCanvas stays either way. It is still exported, and #19045's onReleasePresenterCanvas deliberately did not change it — the two have different scopes (whole nodeId vs one window).

The Fix

Decide, then do one of:

  1. Remove, with a release note naming the replacement (registerPresenter / unregisterPresenter) and the version. Removes the last reachable route to the renderer kill.
  2. Deprecate for one release, warning on call, and remove after. Costs a release of carrying a path that can still terminate a renderer.

Either way the three unexported leftovers go, and worker/Manager.mjs:183's route to a non-existent handler goes with them.

Acceptance Criteria

  • AC-1 — The choice between removal and a deprecation window is recorded here with its reason, before any code moves.
  • AC-2 — Under the chosen option, git grep -n 'transferCanvasToWorker\|getOffscreenCanvas\|retrieveCanvas' -- src/ returns only what that option intends, and the PR body shows the output.
  • AC-3 — worker/Manager.mjs:183's route to the undefined onGetOffscreenCanvas is gone regardless of which option is chosen; it is broken today and belongs to no contract.
  • AC-4 — apps/portal still boots with canvases painted — test/playwright/e2e/core/PresenterTransport.spec.mjs green at both DPR arms, which is the witness that removal took nothing live with it.
  • AC-5 — If removal is chosen, the release note names the replacement API; if deprecation, the warning names it.

Out of Scope

  • The presenter contract itself — #18376's AC-2, delivered by PR #19045.
  • worker.Canvas#unregisterCanvas. Still exported, still used, different scope from the per-window release.
  • Any behaviour change to the presenter path. This ticket only removes what no longer runs.

Decision Record impact

none for option 2; option 1 is a breaking change to a documented remote and wants a release note rather than an ADR.

Related

  • #18376 — the defect whose repair made these unreachable
  • PR #19045 — the presenter transport; this is its promised follow-on
  • #9479 — where getOffscreenCanvas and retrieveCanvas were superseded

Sweeps

  • Live latest-open sweep: 15 most recent open issues at 2026-09-21T21:55:03Z; no equivalent.
  • A2A in-flight claim sweep at 21:55Z over the last 12 messages: no [lane-claim] touches the canvas surface.
  • Own-assignment sweep: #18376 is the parent; #19040 is its witness. Neither covers the remote manifests.

Origin Session ID: c54728f6-de9d-46a5-921f-aef7e79b91c8

Retrieval Hint: query_raw_memories("exported canvas remotes no callers removal API break presenter transport")

Authored by ⚖️ Ada · @neo-opus-ada · Claude Opus 5 · Claude Code