Skip to content

feat(plasmic-mcp): hostless component reachability fix + screenshot renderer + test coverage#170

Merged
field123 merged 4 commits into
masterfrom
feat/mcp-gap-fixes
Mar 6, 2026
Merged

feat(plasmic-mcp): hostless component reachability fix + screenshot renderer + test coverage#170
field123 merged 4 commits into
masterfrom
feat/mcp-gap-fixes

Conversation

@field123
Copy link
Copy Markdown
Collaborator

@field123 field123 commented Mar 6, 2026

Summary

  • Hostless component reachability fix — adds isExternalRef callback to ChangeRecorder (matching Studio's StudioCtx behavior) and ensureDependencyAddresses() verification for hostless Component/PropParam/SlotParam references. Prevents fastBundle() from misclassifying dependency instances as project-internal, which caused "Unreachable instance" errors on node.add and node.update-props for hostless package components (gaps _26, _29).
  • Screenshot renderer — adds inspect.capture-screenshot action with headless Playwright rendering via dev host iframe. Converts tree-reader JSON to React elements using the dev host's own React instance.
  • WebSocket test coverage — closes gaps across 7 modules (rebase-engine, undo-manager, change-tracker, update-queue, batch-manager, cross-module integration).

All changes contained within packages/plasmic-mcp/ — zero upstream WAB modifications.

Test plan

  • 2,125 tests pass across 43 files (0 skips)
  • 23 new tests for hostless reachability (bundler address verification, isExternalRef, ChangeTracker integration, nested slots, cross-package refs)
  • 32 new tests for headless canvas screenshot renderer
  • 35 new tests for WebSocket subsystem coverage gaps
  • Integration test against real EP Plasmic server with hostless commerce components (manual verification needed)

field123 added 4 commits March 6, 2026 20:07
…rior specs

Clear completed specs (ep-studio-lockdown, websocket-live-sync,
websocket-presence) and reset IMPLEMENTATION_PLAN.md for the new task.
Add spec for fixing hostless package component reachability bugs
(gaps #26, #29) that block node.add, update-props, and slot children
on hostless components. Update PROMPT_plan.md to be generic.
…f + dependency address verification (#170)

Addresses the P0 blocker where node.add and node.update-props targeting
hostless package components could trigger "Unreachable instance" errors
from FastBundler.assertFastBundleInvariants(). The root cause: when
dependency instances (Component, PropParam) are missing from the bundler's
_uid2addr map, mkRefAndMaybeVisit() creates addresses with the project UUID
instead of the dependency package UUID, causing __ref instead of __xref
classification.

Three-part fix (all within packages/plasmic-mcp/, zero upstream changes):

1. bundler-helpers.ts (NEW): ensureDependencyAddresses() walks new Tpl
   trees after studioElementSchemaToTpl() and verifies all dependency
   instance references have correct bundler addresses. makeIsExternalRef()
   creates a callback matching Studio's StudioCtx pattern.

2. change-tracker.ts: ChangeRecorder now receives isExternalRef callback
   (auto-detected from session), telling it to skip deep MobX observation
   of dependency package instances. This matches Studio's behavior and
   prevents wasteful observation of the entire dependency tree.

3. edit-tools.ts: plasmicElementToTpl() calls ensureDependencyAddresses()
   after creating Tpl trees, logging warnings if any dependency instances
   lack proper bundler registration.

Also adds _uid2addr and _addr2inst to the FastBundler type declarations
(they are intentionally public in upstream source).

23 new tests covering: address verification for Components/Params/Slots,
nested hostless components in slots, cross-package references,
isExternalRef classification, ChangeTracker integration.

All 1,911 tests pass. Zero regressions.
…s Playwright rendering

Add headless canvas screenshot capture that navigates to the dev host's
PlasmicCanvasHost page in headless Chromium, converts the component's
TreeNode JSON to React elements using the dev host's own React instance
(window.__Sub), and calls setPlasmicRootNode() to render. Code components
registered in the dev host are resolved from __PlasmicComponentRegistry.

New files:
- headless-canvas.ts: Playwright orchestration (~200 lines)
- __tests__/headless-canvas.test.ts: 32 unit tests

Modified:
- server.ts: capture-screenshot wired into inspect tool (11 actions now)
- IMPLEMENTATION_PLAN.md: Item 2 marked complete, counts updated
…s 7 modules

Add 35 new tests covering critical gaps in the rebase engine, undo manager,
change tracker, update queue, batch manager, and cross-module integration:

- rebase-engine: undoChangesAndResolveConflicts return value usage (3 tests),
  dep pkg unbundle failure handling and continuation (2 tests)
- undo-manager: concurrent undo during save (2 tests), CRITICAL log on
  rollback failure (1 test), getStack/replaceStack rebase API (3 tests)
- change-tracker: getRecorder identity (3 tests), withRecording error
  propagation (1 test), isExternalRef session integration (2 tests)
- update-queue: enqueue+stop race condition (1 test), isProcessing flag
  (3 tests), handler error + concurrent enqueue (1 test)
- batch-manager: rollback failure during endBatch with CRITICAL log (1 test),
  replaceAccumulatedChanges for rebase (3 tests), sequential batch
  independence (2 tests)
- cross-module-integration (new file): rebase+undo stack rebuild (1 test),
  rebase+batch replace (1 test), rebase+undo+batch ordering (1 test),
  UpdateQueue+save gating (1 test), self-update echo filtering (1 test),
  batch→save→undo flow (1 test), session state during rebase (1 test)

All 1,955 unit tests pass. TypeScript compiles clean.
@field123 field123 merged commit 025aa1f into master Mar 6, 2026
9 checks passed
field123 added a commit that referenced this pull request Mar 7, 2026
…lidation (#171)

* chore(ralph): add hostless component reachability spec and clean up prior specs

Clear completed specs (ep-studio-lockdown, websocket-live-sync,
websocket-presence) and reset IMPLEMENTATION_PLAN.md for the new task.
Add spec for fixing hostless package component reachability bugs
(gaps #26, #29) that block node.add, update-props, and slot children
on hostless components. Update PROMPT_plan.md to be generic.

* feat(plasmic-mcp): hostless component reachability fix — isExternalRef + dependency address verification (#170)

Addresses the P0 blocker where node.add and node.update-props targeting
hostless package components could trigger "Unreachable instance" errors
from FastBundler.assertFastBundleInvariants(). The root cause: when
dependency instances (Component, PropParam) are missing from the bundler's
_uid2addr map, mkRefAndMaybeVisit() creates addresses with the project UUID
instead of the dependency package UUID, causing __ref instead of __xref
classification.

Three-part fix (all within packages/plasmic-mcp/, zero upstream changes):

1. bundler-helpers.ts (NEW): ensureDependencyAddresses() walks new Tpl
   trees after studioElementSchemaToTpl() and verifies all dependency
   instance references have correct bundler addresses. makeIsExternalRef()
   creates a callback matching Studio's StudioCtx pattern.

2. change-tracker.ts: ChangeRecorder now receives isExternalRef callback
   (auto-detected from session), telling it to skip deep MobX observation
   of dependency package instances. This matches Studio's behavior and
   prevents wasteful observation of the entire dependency tree.

3. edit-tools.ts: plasmicElementToTpl() calls ensureDependencyAddresses()
   after creating Tpl trees, logging warnings if any dependency instances
   lack proper bundler registration.

Also adds _uid2addr and _addr2inst to the FastBundler type declarations
(they are intentionally public in upstream source).

23 new tests covering: address verification for Components/Params/Slots,
nested hostless components in slots, cross-package references,
isExternalRef classification, ChangeTracker integration.

All 1,911 tests pass. Zero regressions.

* feat(plasmic-mcp): add inspect.capture-screenshot action with headless Playwright rendering

Add headless canvas screenshot capture that navigates to the dev host's
PlasmicCanvasHost page in headless Chromium, converts the component's
TreeNode JSON to React elements using the dev host's own React instance
(window.__Sub), and calls setPlasmicRootNode() to render. Code components
registered in the dev host are resolved from __PlasmicComponentRegistry.

New files:
- headless-canvas.ts: Playwright orchestration (~200 lines)
- __tests__/headless-canvas.test.ts: 32 unit tests

Modified:
- server.ts: capture-screenshot wired into inspect tool (11 actions now)
- IMPLEMENTATION_PLAN.md: Item 2 marked complete, counts updated

* test(plasmic-mcp): close WebSocket subsystem test coverage gaps across 7 modules

Add 35 new tests covering critical gaps in the rebase engine, undo manager,
change tracker, update queue, batch manager, and cross-module integration:

- rebase-engine: undoChangesAndResolveConflicts return value usage (3 tests),
  dep pkg unbundle failure handling and continuation (2 tests)
- undo-manager: concurrent undo during save (2 tests), CRITICAL log on
  rollback failure (1 test), getStack/replaceStack rebase API (3 tests)
- change-tracker: getRecorder identity (3 tests), withRecording error
  propagation (1 test), isExternalRef session integration (2 tests)
- update-queue: enqueue+stop race condition (1 test), isProcessing flag
  (3 tests), handler error + concurrent enqueue (1 test)
- batch-manager: rollback failure during endBatch with CRITICAL log (1 test),
  replaceAccumulatedChanges for rebase (3 tests), sequential batch
  independence (2 tests)
- cross-module-integration (new file): rebase+undo stack rebuild (1 test),
  rebase+batch replace (1 test), rebase+undo+batch ordering (1 test),
  UpdateQueue+save gating (1 test), self-update echo filtering (1 test),
  batch→save→undo flow (1 test), session state during rebase (1 test)

All 1,955 unit tests pass. TypeScript compiles clean.

* fix(plasmic-mcp): reject partial-parse expressions in validateJsExpression to prevent codegen corruption

acorn.parseExpressionAt silently succeeds on partial input (e.g. "expr:$props.imageSrc"
parses "expr" as a valid identifier, ignoring the rest). The full post-$ string was then
stored as CustomCode, producing invalid JS in codegen output. Now checks that the entire
string is consumed by comparing node.end to the input length.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant