0.0.7
Added
-
dusk:consolenow surfacesdebugPrintoutput even withoutfluttersdk_telescope.DuskPlugin.install()now chains adebugPrintoverride that records every call into a bounded in-package ring buffer (cap 50, newest-first).ext.dusk.consolemerges this buffer with the existing telescoperecentLogsReaderoutput using the same merge+dedup pattern asext.dusk.exceptions, sodebugPrint(...)/print(...)calls appear indusk:consoleresults regardless of whether telescope is installed. The telescope reader indirection is preserved: when telescope is wired it augments withLogger.root.onRecordentries and any other watchers it ships. Directdart:developerlog()calls that bypassdebugPrintare not captured by the in-package path; they require telescope'sLogWatcher. Capture scope is documented indoc/commands/index.mdunder "Console and exceptions". -
Opt-in
verifyflag ondusk:tap/dusk_tap/ext.dusk.tap. Whenverify: true, the tap handler captures a cheap TARGET-scoped signal before and after the pointer (the nearest enclosing route name plus a hash of the target element's own semantics subtree: label, value, enabled/checked flags) and adds achanged: true|falsefield to the response reporting whether the tap produced an observable effect on the target. The signal is deliberately target-scoped, not a global route/whole-tree hash, so a counter button whose own label increments reportschanged: truewhile unrelated background churn elsewhere in the tree does not. Default off (verify: false) keeps the response shape byte-identical to before: nochangedkey. Thedusk:tapCLI gains a--verifyflag and thedusk_tapMCP descriptor gains averifyboolean property (a parameter addition to the existingdusk:tap/dusk_tap, not a new command or tool). -
Optional
sincefilter ondusk:exceptions/dusk_exceptions/ext.dusk.exceptions. Passsince: "<iso8601>"(e.g.2024-01-01T10:00:00.000Z) to receive only exceptions whosetimeis strictly after that timestamp. Agents can record the current time before an action, then calldusk:exceptions --since=<time>afterwards to see only new exceptions raised by that action, eliminating false positives from cumulative history. Default behavior (nosince) is unchanged: the full cumulative list is returned. Unparseablesincevalues are silently treated as absent. Thedusk:exceptionsCLI gains a--sinceflag and thedusk_exceptionsMCP descriptor gains asincestring property (a parameter addition to the existingdusk:exceptions/dusk_exceptions, not a new command or tool). -
dusk:fill/dusk_fill/ext.dusk.fill— one-call text-field fill. Resolves a text-field ref (e<N>/q<N>), then focuses, clears, types, and settles in a single round-trip, replacing the manual focus + clear + type + settle dance every agent re-discovers. Composes the existing GATEDext.dusk.focus,ext.dusk.clear, andext.dusk.typehandlers verbatim (so the 6-check actionability gate, IME focus,onChanged/validator firing, and post-action snapshot semantics are reused, never re-implemented). Retries the whole resolve + focus + clear + type sequence ONCE when the ref goes stale mid-fill (a transiently-missingq<N>re-walks the now-settled tree on the second pass); a second stale outcome surfaces a typedstaleenvelope so the agent re-snaps or re-finds. Returns{ref, text, filled: true}plus an optional post-fill snapshot. This is a NEW command (CLI 32 -> 34) and a NEW MCP tool (31 -> 33) backed by a NEWext.dusk.fillextension (28 -> 30). -
dusk:reset_overlays/dusk_reset_overlays/ext.dusk.reset_overlays— one-call overlay reset. Returns the app to a known clean screen via three escalating, idempotent layers: (1) pop everyPopupRoute(reusingdismissAllModals, never touching the page stack); (2) anEscapekey press for overlays driven by the dismiss shortcut that are notPopupRoutes; (3) a Cancel/Dismiss/Close/OK/Done labelled tap for modal barriers that need an explicit affordance. Each layer is a no-op when the prior already cleared the overlays, so the command is safe to call speculatively between flows. Returns{popped: N, escaped: bool, dismissTapped: bool}. This is a NEW command (CLI 32 -> 34) and a NEW MCP tool (31 -> 33) backed by a NEWext.dusk.reset_overlaysextension (28 -> 30). -
untilconfirmation ondusk:tap/dusk_tap/ext.dusk.tap. Whenuntil: "<text>"is set, after the tap settles the handler polls the live element tree (reusing thedusk:wait_forpoll loop) for aTextwhose data equals the expected string, up tountilTimeoutMs(default 3000), and adds anuntilMatched: true|falsefield reporting whether it appeared. Confirms a navigation / state change produced the expected text in one call, replacing a separatedusk_wait_forround-trip. Default off (nountil) keeps the response shape unchanged. Thedusk:tapCLI gains a--untilflag and thedusk_tapMCP descriptor gainsuntil/untilTimeoutMsproperties (a param addition, not a new tool). -
"Driving real apps: gotchas for agents" doc page (
doc/getting-started/driving-real-apps-gotchas.md). Captures the hard-won lessons from a long real-app E2E session, each now partly or fully addressed by D1-D7: refs go stale on rebuild (re-snap, or prefer aq<N>fromdusk:find); text fields may snapshot nested (usedusk:fill, or note thetypeable: truemarker on the collapsed outer node);dusk:consolecapturesdebugPrintin-package now and is enriched by telescope;dusk:exceptionsis cumulative (use--since);restartpreserves the CDP port; overlays may needdusk:reset_overlays. Linked fromllms.txt.
Fixed
dusk:dismiss_modalsnow dismisses modals on ALL NavigatorState instances, not just the first. The previous implementation walked the element tree with a first-match guard and poppedPopupRouteentries one-at-a-time with anendOfFrameawait between each pop.showDialogdefaults touseRootNavigator: true(root navigator) andshowModalBottomSheetdefaults touseRootNavigator: false(nearest navigator); when these are different navigators, the first-match walk left one modal open. The fix collects everyNavigatorStatein a full DFS walk, then callspopUntil((r) => r is! PopupRoute)on each navigator innermost-first, counting everyPopupRoutedismissed. Thepoppedreturn value is the additive sum across all navigators. The per-popendOfFrameawait is removed, which also unblocks unit tests that previously hung the flutter_test fake-clock harness when real modal routes were open.
Changed
- Pointer verbs now dispatch at the element's LIVE rect, not the cached snapshot rect. Every pointer verb (
tap,hover,dragstart + end endpoints,dblclick,right_click,triple_click) re-resolves the target's current bounding rect via the newdispatchRectOf(entry)helper immediately after the actionability gate passes and dispatches at that live center, falling back to the cachedentry.rect.centeronly when the live rect is null (sliver / detached / synthetic). A host that rebuilt the target into a shifted slot between snapshot and action retains the sameElement/RenderObjectidentity, so the live rect is valid. This fixes the false-success class wheredusk:tapreported success whileonTapnever fired because the pointer landed on the target's stale gate-time position. The helper is purely additive to the FROZEN actionability gate: it runs after the gate passes and before dispatch, touching neither the gate order nor any failure-reason substring. dusk:snapcollapses nestedtextboxnodes and marks the survivortypeable: true. A windWInputwraps asSemantics(textField:true) > MergeSemantics > TextField; becauseRenderEditableunconditionally owns its owntextFieldSemantics node (flutter#26336) andMergeSemanticscannot absorb it (flutter#160281), the tree carried TWO nestedtextboxnodes and minted twoeNrefs. Agents naturally targeted the inner leaf, wheredusk:typethrew-32000. The snapshot walk now suppresses anytextboxnode whose render object is a render-tree DESCENDANT of an enclosingtextboxnode's render object, emitting a single ref for the outer typeable node so existing scripts keep resolving. The surviving textbox line gains an additivetypeable: truesub-line. Collapse is by render-object CONTAINMENT only, never label/value equality, so two sibling fields sharing a label stay two distinct refs. Thetextboxrole string is unchanged;eNminting stays snapshot-only. The source-side fix lives in wind (W1); this is the defensive dusk-side collapse.- Bumped
fluttersdk_artisanto^0.0.8. Picks up the substraterestartfix that preserves--cdp-portacross the stop/start cycle (sodusk:resize/dusk:devicekeep working afterfsa restart) and the published-config import-path fix in the plugin installer.