Skip to content

feat: web screenshot CDP fallback (#13) + non-fatal FlutterError capture (#14) + artisan 0.0.7#15

Merged
anilcancakir merged 13 commits into
masterfrom
feat/dusk-web-screenshot-exceptions
Jun 9, 2026
Merged

feat: web screenshot CDP fallback (#13) + non-fatal FlutterError capture (#14) + artisan 0.0.7#15
anilcancakir merged 13 commits into
masterfrom
feat/dusk-web-screenshot-exceptions

Conversation

@anilcancakir

Copy link
Copy Markdown
Contributor

Summary

Resolves two agent-QA gaps and bumps the artisan dependency.

  • dusk:screenshot times out on Flutter web (ext.dusk.screenshot 0/1 responses) #13 (bug): dusk:screenshot timed out on Flutter web because the in-isolate OffsetLayer.toImage() never completes under CanvasKit+DWDS. When ~/.artisan/state.json carries a cdpPort and no --ref/--rect is supplied, the command now captures the full viewport over CDP (Page.enable + Page.captureScreenshot, fromSurface: true) and writes the decoded bytes directly. Native targets and web+ref/rect keep using ext.dusk.screenshot unchanged.
  • dusk:exceptions misses non-fatal FlutterErrors (RenderFlex overflow) #14 (enhancement): dusk:exceptions missed non-fatal FlutterErrors (RenderFlex overflow). DuskPlugin.install() now chains a FlutterError.onError handler into a bounded in-package buffer (cap 50, dedup by message+stackHead), and ext.dusk.exceptions merges that buffer with the telescope reader. dusk:snap additionally annotates currently-overflowing nodes with a live overflow: true sub-line.
  • Dependency: fluttersdk_artisan ^0.0.6 -> ^0.0.7 (non-breaking; 0.0.7 hardens start --cdp-port, issue #25).

Verification

  • dart format / dart analyze: clean across lib, test, bin.
  • flutter test --exclude-tags=integration: 724 pass.
  • Coverage: 80.73% (>= 80% floor).
  • Frozen contracts intact: dusk_screenshot/ext.dusk.screenshot, dusk_exceptions, DuskSnapshotEnricher, DuskPlugin.install() signature, command/MCP counts (32/31/28).

Follow-ups (non-blocking, from code-review + oracle)

  • Bound the overflow-ancestor walk in ext_snapshot.dart at the nearest distinct-SemanticsNode render parent so a high-up overflow does not flag every descendant.
  • Harden cdpPort as int cast and add a result['data'] null-check in the CDP screenshot path.

Closes #13
Closes #14

0.0.7 hardens the start --cdp-port web flow (issue #25) that the upcoming dusk:screenshot CDP fallback relies on. Non-breaking: all consumed artisan surfaces (CommandBoot, ArtisanCommand, ArtisanContext.callExtension, McpToolDescriptor, registerExtensionIdempotent, StateFile) are signature-identical to 0.0.6.
DuskPlugin.install() now installs a FlutterError.onError chain (installErrorCapture) that records non-fatal presentations (incl. RenderFlex overflow) into a bounded ring buffer, preserving the prior handler. Backs dusk:exceptions surfacing layout errors without telescope (issue #14). install()/uninstall guarded independently of _installCount; prior handler re-captured per cycle for flutter_test safety.
…t on web

When state.json has cdpPort and no ref/rect, dusk:screenshot now captures over Chrome DevTools Protocol (Page.enable + Page.captureScreenshot) instead of the in-isolate ext.dusk.screenshot, which hangs under CanvasKit/DWDS (issue #13). Native path unchanged.
ext.dusk.snap now emits an additive 'overflow: true' line on interactive nodes inside a currently-overflowing render ancestor, via a live RenderObject.toStringShort() check (no retained state). Surfaces RenderFlex overflow per-ref for the agent QA loop (issue #14).
ext.dusk.exceptions now returns the union of the in-package non-fatal capture buffer and the telescope-wired recentExceptionsReader, deduped by (type, message, stackHead) and clipped to limit. Non-fatal FlutterErrors (incl. overflow) surface even without telescope (issue #14).
…, artisan 0.0.7

CHANGELOG [Unreleased] Added (3 features) + Changed (artisan ^0.0.7 caret note); README and ARCHITECTURE updated for the web screenshot path and in-package error capture. Counts unchanged (32 CLI / 31 MCP / 28 ext.dusk.*).
…descriptions

Descriptor description text only; names, extensionMethod values, and tool counts unchanged.
Copilot AI review requested due to automatic review settings June 9, 2026 11:23

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses two QA gaps for Flutter web/diagnostics workflows in fluttersdk_dusk and bumps the fluttersdk_artisan dependency, with accompanying tests and documentation updates.

Changes:

  • Add a Flutter web fallback for dusk:screenshot that uses Chrome DevTools Protocol (Page.captureScreenshot) when a cdpPort is available and no region capture is requested.
  • Capture non-fatal FlutterErrors (including RenderFlex overflow) and surface them via dusk:exceptions, plus annotate snapshot YAML with a live overflow: true hint.
  • Bump fluttersdk_artisan to ^0.0.7 and update docs/tests to cover the new behavior.

Reviewed changes

Copilot reviewed 17 out of 20 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
test/src/extensions/ext_snapshot_dispatcher_test.dart Adds widget tests asserting the new overflow: true snapshot annotation behavior.
test/src/extensions/ext_exceptions_test.dart Adds tests for merging the in-package FlutterError buffer with the telescope exceptions reader.
test/src/dusk_error_capture_test.dart Introduces tests for installing/uninstalling the FlutterError capture hook and buffer behavior (dedupe/cap).
test/src/commands/dusk_screenshot_command_test.dart Adds tests for the new CDP screenshot path, error handling, and fallthrough behavior.
README.md Updates the overview text to mention the new web screenshot and error capture behavior.
pubspec.yaml Bumps fluttersdk_artisan dependency to ^0.0.7.
llms.txt Updates the tool summary list to include overflow and screenshot routing notes.
lib/src/extensions/ext_snapshot.dart Adds the live overflow ancestor check and emits overflow: true under affected interactive nodes.
lib/src/dusk_plugin.dart Installs the FlutterError capture hook during DuskPlugin.install().
lib/src/dusk_artisan_provider.dart Updates MCP tool descriptions (snap/screenshot/exceptions) to reflect new behaviors.
lib/src/commands/dusk_screenshot_command.dart Implements the CDP full-viewport screenshot fallback and keeps native/region capture on ext.dusk.screenshot.
lib/dusk.dart Exposes recentCapturedExceptions from the public barrel.
example/pubspec.lock Updates resolved dependency versions (notably artisan) for the example app.
doc/mcp/tool-reference.md Updates MCP tool reference text for exceptions/screenshot/snap.
doc/commands/dusk-snap.md Documents the new overflow: true snapshot annotation.
doc/commands/dusk-screenshot.md Documents the CDP fallback behavior for the CLI dusk:screenshot command on web.
CHANGELOG.md Adds unreleased entries describing the new screenshot fallback, error capture, overflow annotation, and artisan bump.
ARCHITECTURE.md Updates architecture notes to include the error capture module and web screenshot behavior.

Comment thread lib/src/commands/dusk_screenshot_command.dart
Comment thread lib/src/commands/dusk_screenshot_command.dart Outdated
Comment thread doc/mcp/tool-reference.md Outdated
Comment thread doc/mcp/tool-reference.md
Comment thread lib/src/dusk_artisan_provider.dart Outdated
Comment thread lib/src/dusk_artisan_provider.dart Outdated
Comment thread doc/mcp/tool-reference.md Outdated
Comment thread llms.txt
Comment thread README.md Outdated
Read cdpPort defensively (int/num/numeric-String -> int?, else null) so a corrupt or cross-version state file falls back to the native VM-extension path instead of throwing on a force-cast. Validate Page.captureScreenshot result['data'] is a String before decoding, returning a clear error on a malformed CDP response. Adds regression tests for both. Addresses Copilot review on PR #15.
The CDP Page.captureScreenshot fallback lives in the CLI dusk:screenshot command; the dusk_screenshot MCP tool dispatches ext.dusk.screenshot in-isolate and can still hang on web. Docs/descriptor/llms.txt/README updated to state this accurately and point web users to the CLI. Also fixes the documented defaults (jpeg/70, not png/80) and the return shape ({format, base64, width, height}, not {format, bytes}). Addresses Copilot review on PR #15.
@anilcancakir

Copy link
Copy Markdown
Contributor Author

Addressed all 9 Copilot review comments in 10fa56c (code) and b02999f (docs). Full suite green (726 tests, +2 regression), analyze + format clean.

Code

  • dusk_screenshot_command.dart:70 (cdpPort as int): now parsed via _readCdpPort (int / num / numeric-String -> int?, else null), so a corrupt or cross-version state file falls back to the native VM-extension path instead of throwing. Regression test (g) covers a non-numeric cdpPort.
  • dusk_screenshot_command.dart:120 (result['data'] as String): validate data is String before decoding; a malformed CDP response now returns a clear no image data error (exit 1). Regression test (h) covers it.

Docs accuracy (the important one): MCP vs CLI dispatch
You were right that the CDP fallback only applies to the CLI dusk:screenshot --output=<path> command. The MCP dusk_screenshot tool has extensionMethod: ext.dusk.screenshot and mcp_server._dispatch routes non-artisan: tools straight to vmClient.callServiceExtension in-isolate, so MCP web screenshots can still hang. Changing that would mean flipping the FROZEN extensionMethod to a substrate route (off-limits) or an artisan-side dispatch change, so for this PR the fix is accurate docs + a clear pointer to the CLI:

  • dusk_artisan_provider.dart description, doc/mcp/tool-reference.md (Dispatch line + body), llms.txt, and README.md now state the MCP tool dispatches in-isolate and that the CDP fallback is CLI-only.

Defaults / return shape

  • Fixed the documented defaults to match ext.dusk.screenshot (format jpeg, quality 70; were png/80) in the descriptor format/quality props and the tool-reference input table.
  • Fixed the tool-reference return shape to { format, base64, width, height } (was { format, bytes }).

Follow-up (not in this PR): making the MCP dusk_screenshot tool work on web would need a substrate-routed variant or an artisan-side _dispatch special-case that returns base64 inline. Happy to spin that up as a separate coordinated change if wanted.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 17 out of 20 changed files in this pull request and generated 2 comments.

Comment thread lib/src/commands/dusk_screenshot_command.dart
Comment thread lib/src/commands/dusk_screenshot_command.dart
…shot

dusk:screenshot captures the full app frame; cdpPort set -> CDP, else native ext.dusk.screenshot. The command never declared or forwarded --ref/--rect, so the defensive read was dead routing. Region capture stays deferred (plan D2). Removes the two obsolete ref/rect-fallthrough tests. Addresses Copilot review on PR #15.
…nces

The command does not expose region capture, so the descriptor, tool-reference, command doc, ARCHITECTURE, and CHANGELOG no longer reference --ref/--rect on dusk:screenshot. Addresses Copilot review on PR #15.
@anilcancakir

Copy link
Copy Markdown
Contributor Author

Addressed the 2 new Copilot comments in b7b221a (code) and dc71411 (docs). Suite green (724 tests), analyze + format clean.

Both comments flagged the same real inconsistency: dusk:screenshot read ref/rect for routing and the docs referenced --ref/--rect, but configure() never declared them and _handleNativePath() never forwarded them, so region capture was neither accepted nor honored (half-wired).

Resolution: I made the command honest rather than adding a new capability. Region (ref/rect) capture was deliberately deferred in the plan, and the original command never exposed it either, so:

  • Removed the dead ref/rect routing from handle(); it is now simply cdpPort set -> CDP full-viewport, else native ext.dusk.screenshot. The command captures the full app frame.
  • Removed the two obsolete ref/rect-fallthrough tests.
  • Removed every --ref/--rect reference from the descriptor, doc/commands/dusk-screenshot.md (intro, synopsis, routing table), doc/mcp/tool-reference.md, ARCHITECTURE.md, and the CHANGELOG entry.

If you'd prefer to actually expose region capture instead (declare --ref/--rect, forward to ext.dusk.screenshot, add them to the MCP inputSchema), that is a small follow-up since the extension already supports it; it was scoped out here per the plan's deferral.

…nshot-exceptions

# Conflicts:
#	example/pubspec.lock
@anilcancakir anilcancakir merged commit 4037b53 into master Jun 9, 2026
1 check passed
@anilcancakir anilcancakir deleted the feat/dusk-web-screenshot-exceptions branch June 9, 2026 12:44
@anilcancakir anilcancakir mentioned this pull request Jun 9, 2026
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.

dusk:exceptions misses non-fatal FlutterErrors (RenderFlex overflow) dusk:screenshot times out on Flutter web (ext.dusk.screenshot 0/1 responses)

2 participants