@deeeed/metamask-harness 0.47.4 → 0.49.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +3 -2
  3. package/adapters/extension/lib/macos-focus.cjs +2 -2
  4. package/adapters/extension/live.sh +6 -8
  5. package/adapters/mobile/bridge-runtime/lib/bridge-errors.cjs +1 -0
  6. package/adapters/shared/ensure-runner-deps.sh +6 -0
  7. package/dist/adapters/extension/runtime-decision.js +18 -1
  8. package/dist/adapters/extension/runtime.js +5 -5
  9. package/dist/adapters/extension/surface.js +1 -0
  10. package/dist/app-lifecycle.js +3 -1
  11. package/dist/command-contract.js +2 -0
  12. package/dist/commands/call.js +1 -8
  13. package/dist/commands/device-target.js +12 -3
  14. package/dist/commands/doctor.js +7 -5
  15. package/dist/commands/fixtures.js +1 -1
  16. package/dist/commands/help.js +17 -5
  17. package/dist/commands/launch/index.js +10 -0
  18. package/dist/commands/manifest.js +3 -1
  19. package/dist/commands/mobile-device-view.js +13 -3
  20. package/dist/commands/parse-args.js +2 -0
  21. package/dist/commands/run-engine.js +29 -4
  22. package/dist/commands/run.js +1 -1
  23. package/dist/commands/runtime-launch.js +10 -1
  24. package/dist/commands/status.js +8 -4
  25. package/dist/devices.js +31 -9
  26. package/dist/execution-provenance.js +8 -7
  27. package/dist/funding-execution-context.js +62 -17
  28. package/dist/heal-bounds.js +3 -3
  29. package/dist/live-adapter-contract.js +4 -0
  30. package/dist/manifest.js +2 -13
  31. package/dist/metamask-action-validation.js +3 -2
  32. package/dist/mm-harness-cli.js +4 -2
  33. package/dist/runner.js +14 -3
  34. package/docs/RECIPES.md +135 -24
  35. package/docs/RELEASE-QA-CAPABILITY-MAP.md +3 -2
  36. package/library/actions/core/perps/_controller.mjs +24 -24
  37. package/library/actions/core/perps/assert_orders.mjs +3 -5
  38. package/library/actions/core/perps/assert_positions.mjs +3 -5
  39. package/library/actions/core/perps/close_orders.mjs +39 -19
  40. package/library/actions/core/perps/close_positions.mjs +53 -12
  41. package/library/actions/core/perps/edit_order.mjs +2 -0
  42. package/library/actions/core/perps/ensure_orders.mjs +3 -4
  43. package/library/actions/core/perps/ensure_positions.mjs +4 -5
  44. package/library/actions/core/perps/place_order.mjs +7 -14
  45. package/library/actions/core/perps/read_account.mjs +2 -0
  46. package/library/actions/core/perps/read_orders.mjs +2 -0
  47. package/library/actions/core/perps/read_positions.mjs +3 -1
  48. package/library/actions/core/perps/read_snapshot.mjs +3 -0
  49. package/library/actions/core/perps/start_state.mjs +3 -14
  50. package/library/actions/core/perps/teardown_state.mjs +3 -14
  51. package/library/actions/core/perps/update_position_tpsl.mjs +13 -19
  52. package/library/actions/core/wallet/list_accounts.mjs +3 -0
  53. package/library/actions/extension/analytics/consent.mjs +2 -0
  54. package/library/actions/extension/analytics/set_consent.mjs +2 -0
  55. package/library/actions/extension/assets/home-token-identity.mjs +31 -0
  56. package/library/actions/extension/assets/import_custom_token.mjs +63 -79
  57. package/library/actions/extension/assets/open_details.mjs +19 -7
  58. package/library/actions/extension/assets/prepare_send.mjs +1 -0
  59. package/library/actions/extension/assets/read_details.mjs +62 -29
  60. package/library/actions/extension/assets/read_visible_state.mjs +5 -1
  61. package/library/actions/extension/assets/review_send.mjs +1 -0
  62. package/library/actions/extension/assets/select_network_scope.mjs +17 -9
  63. package/library/actions/extension/assets/send.mjs +2 -0
  64. package/library/actions/extension/assets/set_custom_gas.mjs +1 -0
  65. package/library/actions/extension/assets/set_token_visibility.mjs +86 -17
  66. package/library/actions/extension/assets/verify_sorting.mjs +63 -14
  67. package/library/actions/extension/deeplink/open.mjs +1 -0
  68. package/library/actions/extension/networks/add_chainlist.mjs +2 -0
  69. package/library/actions/extension/networks/add_custom.mjs +21 -17
  70. package/library/actions/extension/networks/custom_network.mjs +10 -3
  71. package/library/actions/extension/networks/read_visible_state.mjs +2 -0
  72. package/library/actions/extension/networks/remove_custom.mjs +1 -0
  73. package/library/actions/extension/performance/_navigation-memory.mjs +37 -6
  74. package/library/actions/extension/performance/compare_idle_navigation_memory.mjs +5 -2
  75. package/library/actions/extension/performance/measure_detached_dom.mjs +5 -2
  76. package/library/actions/extension/performance/measure_navigation_memory.mjs +5 -2
  77. package/library/actions/extension/perps/assert_orders.mjs +1 -0
  78. package/library/actions/extension/perps/assert_positions.mjs +1 -0
  79. package/library/actions/extension/perps/assert_visible_consistency.mjs +11 -4
  80. package/library/actions/extension/perps/close_orders.mjs +4 -1
  81. package/library/actions/extension/perps/close_positions.mjs +1 -0
  82. package/library/actions/extension/perps/close_visible_position.mjs +160 -11
  83. package/library/actions/extension/perps/compare_provider_market.mjs +2 -0
  84. package/library/actions/extension/perps/edit_margin.mjs +3 -1
  85. package/library/actions/extension/perps/ensure_orders.mjs +1 -0
  86. package/library/actions/extension/perps/ensure_positions.mjs +1 -0
  87. package/library/actions/extension/perps/mutation-receipt.mjs +54 -3
  88. package/library/actions/extension/perps/open_balance_action.mjs +17 -7
  89. package/library/actions/extension/perps/open_position_action.mjs +1 -0
  90. package/library/actions/extension/perps/perps.mjs +550 -69
  91. package/library/actions/extension/perps/place_order.mjs +1 -0
  92. package/library/actions/extension/perps/read_eligibility.mjs +1 -0
  93. package/library/actions/extension/perps/read_funds_confirmation.mjs +10 -0
  94. package/library/actions/extension/perps/read_orders.mjs +1 -0
  95. package/library/actions/extension/perps/read_positions.mjs +1 -0
  96. package/library/actions/extension/perps/read_snapshot.mjs +4 -1
  97. package/library/actions/extension/perps/read_visible_state.mjs +12 -6
  98. package/library/actions/extension/perps/search_markets.mjs +8 -2
  99. package/library/actions/extension/perps/select_activity_filter.mjs +2 -1
  100. package/library/actions/extension/perps/select_market_filter.mjs +75 -45
  101. package/library/actions/extension/perps/set_market_favorite.mjs +7 -3
  102. package/library/actions/extension/perps/state.mjs +2 -0
  103. package/library/actions/extension/perps/update_position_tpsl.mjs +3 -1
  104. package/library/actions/extension/perps/visible-mutation-identity.mjs +6 -2
  105. package/library/actions/extension/platform/cdp.mjs +50 -142
  106. package/library/actions/extension/settings/set_basic_functionality.mjs +1 -0
  107. package/library/actions/extension/swap_bridge/quote-state.mjs +56 -0
  108. package/library/actions/extension/swap_bridge/read_visible_state.mjs +5 -1
  109. package/library/actions/extension/swap_bridge/select_assets.mjs +12 -1
  110. package/library/actions/extension/swap_bridge/set_amount.mjs +11 -8
  111. package/library/actions/extension/swap_bridge/set_max_amount.mjs +18 -18
  112. package/library/actions/extension/swap_bridge/set_slippage.mjs +1 -9
  113. package/library/actions/extension/ui/locators.mjs +1 -0
  114. package/library/actions/extension/ui/navigate.mjs +60 -14
  115. package/library/actions/extension/wallet/ensure_unlocked.mjs +2 -0
  116. package/library/actions/extension/wallet/import.mjs +2 -0
  117. package/library/actions/extension/wallet/list_accounts.mjs +1 -0
  118. package/library/actions/extension/wallet/lock.mjs +4 -1
  119. package/library/actions/extension/wallet/read_state.mjs +1 -0
  120. package/library/actions/extension/wallet/reset.mjs +2 -0
  121. package/library/actions/extension/wallet/secret-input.mjs +2 -0
  122. package/library/actions/extension/wallet/select_account.mjs +2 -0
  123. package/library/actions/extension/wallet/setup.mjs +1 -0
  124. package/library/actions/extension/wallet/state.mjs +2 -0
  125. package/library/actions/extension/wallet/visible-session.mjs +2 -0
  126. package/library/actions/mobile/analytics/consent-settings.mjs +1 -0
  127. package/library/actions/mobile/analytics/set_consent.mjs +1 -0
  128. package/library/actions/mobile/app/network-control.mjs +2 -0
  129. package/library/actions/mobile/app/network.mjs +1 -0
  130. package/library/actions/mobile/app/network_assert.mjs +1 -0
  131. package/library/actions/mobile/app/network_capture.mjs +1 -0
  132. package/library/actions/mobile/assets/import_custom_token.mjs +3 -0
  133. package/library/actions/mobile/assets/open_details.mjs +1 -0
  134. package/library/actions/mobile/assets/read_details.mjs +1 -0
  135. package/library/actions/mobile/assets/read_visible_state.mjs +4 -1
  136. package/library/actions/mobile/assets/set_token_visibility.mjs +15 -9
  137. package/library/actions/mobile/assets/verify_sorting.mjs +90 -7
  138. package/library/actions/mobile/deeplink/open.mjs +1 -0
  139. package/library/actions/mobile/networks/add_custom.mjs +1 -0
  140. package/library/actions/mobile/networks/network-management.mjs +23 -15
  141. package/library/actions/mobile/networks/read_visible_state.mjs +1 -0
  142. package/library/actions/mobile/networks/remove_custom.mjs +7 -2
  143. package/library/actions/mobile/perps/assert_orders.mjs +1 -0
  144. package/library/actions/mobile/perps/assert_positions.mjs +1 -0
  145. package/library/actions/mobile/perps/capture_performance.mjs +1 -0
  146. package/library/actions/mobile/perps/close_orders.mjs +1 -0
  147. package/library/actions/mobile/perps/close_positions.mjs +1 -0
  148. package/library/actions/mobile/perps/ensure_orders.mjs +1 -0
  149. package/library/actions/mobile/perps/ensure_positions.mjs +1 -0
  150. package/library/actions/mobile/perps/measure_homepage_visible.mjs +1 -0
  151. package/library/actions/mobile/perps/performance-capture.mjs +2 -0
  152. package/library/actions/mobile/perps/perps.mjs +8 -1
  153. package/library/actions/mobile/perps/place_order.mjs +1 -0
  154. package/library/actions/mobile/perps/prepare_local_snapshot_endpoint.mjs +2 -0
  155. package/library/actions/mobile/perps/read-visible-state-loop.mjs +3 -0
  156. package/library/actions/mobile/perps/read_orders.mjs +1 -0
  157. package/library/actions/mobile/perps/read_positions.mjs +1 -0
  158. package/library/actions/mobile/perps/read_visible_state.mjs +1 -0
  159. package/library/actions/mobile/perps/search_markets.mjs +1 -0
  160. package/library/actions/mobile/perps/set_market_favorite.mjs +17 -19
  161. package/library/actions/mobile/platform/bridge.mjs +16 -4
  162. package/library/actions/mobile/platform/native-session-name.mjs +1 -0
  163. package/library/actions/mobile/platform/native-session.mjs +28 -17
  164. package/library/actions/mobile/platform/observe-ui.mjs +4 -2
  165. package/library/actions/mobile/platform/tool-paths.mjs +1 -0
  166. package/library/actions/mobile/swap_bridge/native-session.mjs +1 -0
  167. package/library/actions/mobile/swap_bridge/read_visible_state.mjs +1 -0
  168. package/library/actions/mobile/swap_bridge/select_assets.mjs +2 -0
  169. package/library/actions/mobile/swap_bridge/set_amount.mjs +1 -0
  170. package/library/actions/mobile/swap_bridge/set_max_amount.mjs +1 -0
  171. package/library/actions/mobile/swap_bridge/set_slippage.mjs +1 -0
  172. package/library/actions/mobile/swap_bridge/submit_transaction.mjs +2 -0
  173. package/library/actions/mobile/ui/locators.mjs +1 -0
  174. package/library/actions/mobile/ui/native-navigation.mjs +2 -0
  175. package/library/actions/mobile/ui/navigate.mjs +3 -1
  176. package/library/actions/mobile/wallet/ensure_unlocked.mjs +2 -0
  177. package/library/actions/mobile/wallet/home.mjs +1 -0
  178. package/library/actions/mobile/wallet/import.mjs +2 -0
  179. package/library/actions/mobile/wallet/list_accounts.mjs +1 -0
  180. package/library/actions/mobile/wallet/lock.mjs +1 -0
  181. package/library/actions/mobile/wallet/native-ui.mjs +2 -0
  182. package/library/actions/mobile/wallet/read_state.mjs +2 -0
  183. package/library/actions/mobile/wallet/reset-helper.mjs +1 -0
  184. package/library/actions/mobile/wallet/reset.mjs +1 -0
  185. package/library/actions/mobile/wallet/select_account.mjs +2 -0
  186. package/library/actions/mobile/wallet/setup.mjs +2 -0
  187. package/library/actions/shared/analytics/assert_events.mjs +3 -0
  188. package/library/actions/shared/analytics/consent.mjs +3 -0
  189. package/library/actions/shared/analytics/read_events.mjs +2 -0
  190. package/library/actions/shared/analytics/start_capture.mjs +3 -0
  191. package/library/actions/shared/app/network-artifact.mjs +2 -0
  192. package/library/actions/shared/app/network-assert.mjs +2 -0
  193. package/library/actions/shared/assets/visible-state.mjs +2 -0
  194. package/library/actions/shared/deeplink/url.mjs +3 -0
  195. package/library/actions/shared/networks/visible-state.mjs +2 -0
  196. package/library/actions/shared/perps/visible-state.mjs +16 -3
  197. package/library/actions/shared/swap-bridge/transaction.mjs +3 -0
  198. package/library/actions/shared/swap-bridge/visible-state.mjs +2 -0
  199. package/library/actions/shared/ui/locators.mjs +2 -0
  200. package/library/actions/shared/wallet/import-source.mjs +3 -0
  201. package/library/manifests/core.action-manifest.json +34 -2
  202. package/library/manifests/extension.action-manifest.json +50 -17
  203. package/library/manifests/mobile.action-manifest.json +11 -10
  204. package/library/recipes/core/perps/smoke.recipe.json +23 -0
  205. package/library/recipes/core/perps/snapshot.recipe.json +26 -1
  206. package/library/recipes/extension/assets/release-custom-network-token-persistence.recipe.json +25 -8
  207. package/library/recipes/extension/performance/navigation-memory-suite.recipe.json +219 -0
  208. package/library/recipes/extension/performance/navigation-memory.recipe.json +61 -156
  209. package/library/recipes/extension/wallet/smoke.recipe.json +17 -8
  210. package/library/recipes/mobile/app/lifecycle-smoke.recipe.json +89 -0
  211. package/library/recipes/mobile/networks/release-custom-network-persistence.recipe.json +31 -6
  212. package/library/recipes/mobile/networks/release-multichain-visibility.recipe.json +5 -4
  213. package/library/recipes/mobile/wallet/smoke.recipe.json +17 -8
  214. package/library/recipes/{assets → shared/assets}/release-token-address-import.recipe.json +7 -0
  215. package/library/recipes/{assets → shared/assets}/release-token-details.recipe.json +2 -2
  216. package/library/recipes/shared/assets/release-token-hide-readd.recipe.json +91 -0
  217. package/library/recipes/{assets → shared/assets}/release-token-persistence.recipe.json +3 -0
  218. package/library/recipes/{assets → shared/assets}/release-token-search-import.recipe.json +4 -1
  219. package/library/recipes/{assets → shared/assets}/release-token-sorting.recipe.json +13 -2
  220. package/library/recipes/{swap-bridge → shared/swap-bridge}/release-quote.recipe.json +29 -3
  221. package/package.json +3 -3
  222. package/site/assets/help-recipes.json +220 -27
  223. package/library/recipes/assets/release-token-hide-readd.recipe.json +0 -57
  224. package/library/recipes/core/perps/clean-market-testnet.recipe.json +0 -44
  225. package/library/recipes/core/perps/order-lifecycle.recipe.json +0 -82
  226. package/library/recipes/core/perps/read-markets.recipe.json +0 -36
  227. package/library/recipes/core/perps/trading-lifecycle.recipe.json +0 -80
  228. package/library/recipes/extension/perps/release-activity-filters.recipe.json +0 -51
  229. package/library/recipes/extension/perps/release-funds-flows.recipe.json +0 -76
  230. package/library/recipes/extension/perps/release-live-limit-order.recipe.json +0 -109
  231. package/library/recipes/extension/perps/release-market-filters.recipe.json +0 -97
  232. package/library/recipes/extension/perps/release-order-entry.recipe.json +0 -158
  233. package/library/recipes/extension/perps/release-order-validation.recipe.json +0 -259
  234. package/library/recipes/extension/perps/release-visible-consistency.recipe.json +0 -47
  235. package/library/recipes/extension/perps/source-dev-snapshot-consistency.recipe.json +0 -76
  236. package/library/recipes/mobile/app/lifecycle.android-smoke.recipe.json +0 -69
  237. package/library/recipes/mobile/perps/chase-assert-running.recipe.json +0 -110
  238. package/library/recipes/mobile/perps/chase-place.recipe.json +0 -145
  239. package/library/recipes/mobile/perps/chase-terminate.recipe.json +0 -98
  240. package/library/recipes/mobile/perps/performance.recipe.json +0 -722
  241. package/library/recipes/mobile/perps/pro-order-setup.recipe.json +0 -111
  242. package/library/recipes/mobile/perps/pro-order-start-state.recipe.json +0 -53
  243. package/library/recipes/mobile/perps/scale-assert-orders.recipe.json +0 -126
  244. package/library/recipes/mobile/perps/scale-place.recipe.json +0 -186
  245. package/library/recipes/mobile/perps/twap-assert-active.recipe.json +0 -97
  246. package/library/recipes/mobile/perps/twap-place.recipe.json +0 -146
  247. package/library/recipes/mobile/runner/action-validation.recipe.json +0 -336
  248. package/library/recipes/perps/clean-market-testnet.recipe.json +0 -49
  249. package/library/recipes/perps/lifecycle.recipe.json +0 -144
  250. package/library/recipes/perps/release-live-market-order.recipe.json +0 -139
  251. package/library/recipes/perps/release-watchlist.recipe.json +0 -111
  252. package/library/recipes/swap-bridge/release-max-quote.recipe.json +0 -72
  253. /package/library/recipes/{assets → shared/assets}/release-token-list.recipe.json +0 -0
  254. /package/library/recipes/{perps → shared/perps}/release-market-details.recipe.json +0 -0
  255. /package/library/recipes/{perps → shared/perps}/release-market-search.recipe.json +0 -0
  256. /package/library/recipes/{swap-bridge → shared/swap-bridge}/release-custom-slippage.recipe.json +0 -0
  257. /package/library/recipes/{swap-bridge → shared/swap-bridge}/release-live-max-swap.recipe.json +0 -0
  258. /package/library/recipes/{swap-bridge → shared/swap-bridge}/release-live-swap.recipe.json +0 -0
  259. /package/library/recipes/{swap-bridge → shared/swap-bridge}/release-surface.recipe.json +0 -0
  260. /package/library/recipes/{wallet → shared/wallet}/import.recipe.json +0 -0
  261. /package/library/recipes/{wallet → shared/wallet}/reset-import.recipe.json +0 -0
@@ -1,3 +1,5 @@
1
+ // Normalize platform Perps observations and enforce the requested screen, content and count claims.
2
+
1
3
  const SURFACES = new Set([
2
4
  'auto',
3
5
  'home',
@@ -182,7 +184,7 @@ function visibleFeatures(items) {
182
184
  const short = has(/(?:short-cta-button|direction-short|market-details-short-button)$/u);
183
185
  return {
184
186
  activity: has(/^(?:transaction-card-|perps-(?:recent-activity|activity))/u),
185
- balance: has(/^(?:perps-balance-dropdown|perps-market-(?:balance-value|available-balance-text))/u),
187
+ balance: has(/^(?:perps-balance-dropdown|perps-balance-actions-available-value|perps-market-(?:balance-value|available-balance-text))/u),
186
188
  chart: has(/^(?:chart_surface|tv_chart_container)$/u) ||
187
189
  has(/perps-(?:candlestick-chart|market-detail-chart|market-details-fullscreen-chart|pro-market-chart)/u),
188
190
  'long-short': long && short,
@@ -190,7 +192,7 @@ function visibleFeatures(items) {
190
192
  'order-entry': has(/perps-(?:order-entry|pro-order-form)/u),
191
193
  price: has(/perps-(?:market-detail-price|market-header-price|pro-market-price)$/u),
192
194
  'price-change': has(/perps-(?:market-detail-change|market-header-price-change|pro-market-price-change)$/u),
193
- stats: has(/perps-(?:stats-section|market-details-market-summary|market-detail-oracle-price|pro-market-stats)/u),
195
+ stats: has(/perps-(?:stats-section|market-detail-oracle-price|pro-market-stats)/u),
194
196
  };
195
197
  }
196
198
 
@@ -199,6 +201,16 @@ export function normalizeVisiblePerpsState(raw, options, platform) {
199
201
  .map(normalizedItem)
200
202
  .filter(Boolean);
201
203
  const unique = Array.from(new Map(items.map((item) => [item.testId, item])).values());
204
+ const pairs = items.filter((item) => item.visible &&
205
+ /^(?:perps-market-detail-pair|perps-(?:pro-)?market-header-subtitle)$/u.test(item.testId));
206
+ const favoriteControls = items.filter((item) => item.visible &&
207
+ /^(?:perps-market-detail-favorite-button|perps-(?:pro-)?market-header-favorite-button)$/u.test(item.testId));
208
+ const requestedMarket = options.market?.replace(/^XYZ-/u, 'XYZ:');
209
+ const favoriteLabel = favoriteControls.length === 1 ? favoriteControls[0].label ?? '' : '';
210
+ const favorite = requestedMarket && pairs.length === 1 &&
211
+ pairs[0].label?.toUpperCase().startsWith(`${requestedMarket}-`) &&
212
+ /^(?:add to|remove from) (?:favorites|watchlist)$/iu.test(favoriteLabel)
213
+ ? /^remove from /iu.test(favoriteLabel) : null;
202
214
  const testIds = new Set(unique.map((item) => item.testId));
203
215
  const route = text(raw.route) ?? '';
204
216
  const surface = classifySurface(route, testIds);
@@ -216,10 +228,11 @@ export function normalizeVisiblePerpsState(raw, options, platform) {
216
228
  source: 'visible-ui',
217
229
  platform,
218
230
  surface,
231
+ favorite,
219
232
  ...(route ? { route } : {}),
220
233
  ...(text(raw.title) ? { title: text(raw.title) } : {}),
221
234
  balance: compactEntries(
222
- selected(unique, (item) => /^(?:perps-balance-dropdown|perps-market-balance-value)$/u.test(item.testId), 1),
235
+ selected(unique, (item) => /^(?:perps-balance-dropdown|perps-balance-actions-available-value|perps-market-balance-value)$/u.test(item.testId), 1),
223
236
  1,
224
237
  )[0] ?? null,
225
238
  markets: marketItems,
@@ -1,3 +1,6 @@
1
+ // Validate cross-platform swap asset selections and positive amounts, including decimal normalization.
2
+ // Platform actions perform the actual form changes and submission.
3
+
1
4
  function requiredText(value, name) {
2
5
  const resolved = String(value ?? '').trim();
3
6
  if (!resolved) throw new Error(`metamask.swap_bridge ${name} is required.`);
@@ -1,3 +1,5 @@
1
+ // Normalize observed swap controls and enforce token, quote, settings and slippage requirements.
2
+
1
3
  function text(value) {
2
4
  if (typeof value !== 'string') return undefined;
3
5
  const normalized = value.replace(/\s+/gu, ' ').trim();
@@ -1,3 +1,5 @@
1
+ // Rank locator suggestions from observed interactive elements and enforce observation visibility.
2
+
1
3
  const LOCATOR_LIMIT = 20;
2
4
  const INTERACTIVE_ROLE_PARTS = [
3
5
  'button',
@@ -1,3 +1,6 @@
1
+ // Resolve wallet-import credentials from explicit environment or fixture sources.
2
+ // Validate method/source combinations before platform onboarding runs.
3
+
1
4
  import { readFile } from 'node:fs/promises';
2
5
  import path from 'node:path';
3
6
 
@@ -189,7 +189,7 @@
189
189
  }
190
190
  },
191
191
  "assert_output": {
192
- "description": "Assert stdout or stderr captured from a command node output.",
192
+ "description": "Assert a previous node's stdout/stderr with contains or match, or compare a structured output field using assert.path and an operator such as eq or length_eq.",
193
193
  "examples": [
194
194
  {
195
195
  "action": "assert_output",
@@ -1161,6 +1161,21 @@
1161
1161
  "schema": {
1162
1162
  "type": "object",
1163
1163
  "properties": {
1164
+ "expected_positions": {
1165
+ "type": "array",
1166
+ "maxItems": 1,
1167
+ "items": {
1168
+ "type": "object",
1169
+ "required": ["coin", "size", "entryPrice"],
1170
+ "properties": {
1171
+ "coin": { "type": "string" },
1172
+ "size": { "type": ["string", "number"] },
1173
+ "entryPrice": { "type": ["string", "number"] }
1174
+ },
1175
+ "additionalProperties": true
1176
+ },
1177
+ "description": "Optional seed receipt guard. Require the selected live market, signed size and entry price to match before dispatch; close at most that quantity in its reducing direction. Empty requires no selected position. Identical netted replacements cannot be distinguished."
1178
+ },
1164
1179
  "market": {
1165
1180
  "type": "string",
1166
1181
  "description": "Single market symbol, e.g. BTC or ETH. Alias: symbol."
@@ -1565,10 +1580,19 @@
1565
1580
  ]
1566
1581
  },
1567
1582
  "metamask.perps.close_orders": {
1568
- "description": "core Default to testnet for Perps mutations; mainnet is read-only unless explicitly requested. Cancel selected live Perps open orders. Use selector/mode params to cancel matching symbols or all orders.",
1583
+ "description": "Core Perps cancellation selects current orders by market/side or mode=all, then dispatches and verifies those exact order IDs. Missing or duplicate selected IDs fail before dispatch. Requires authorized signing and task-owned selection; defaults to testnet.",
1569
1584
  "schema": {
1570
1585
  "type": "object",
1571
1586
  "properties": {
1587
+ "expected_order_ids": {
1588
+ "type": "array",
1589
+ "items": { "type": "string" },
1590
+ "description": "Optional exact receipt set. Refuse a changed selected order set before dispatch and cancel only these IDs. Empty requires no selected open orders."
1591
+ },
1592
+ "account": {
1593
+ "type": "string",
1594
+ "description": "Wallet fixture account name used for signing; defaults to dev1."
1595
+ },
1572
1596
  "market": {
1573
1597
  "type": "string",
1574
1598
  "description": "Single market symbol, e.g. BTC or ETH. Alias: symbol."
@@ -1687,6 +1711,10 @@
1687
1711
  "schema": {
1688
1712
  "type": "object",
1689
1713
  "properties": {
1714
+ "account": {
1715
+ "type": "string",
1716
+ "description": "Wallet fixture account name, defaulting to dev1. Without a fixture, an EVM address permits read-only assertions."
1717
+ },
1690
1718
  "market": {
1691
1719
  "type": "string",
1692
1720
  "description": "Single market symbol, e.g. BTC or ETH. Alias: symbol."
@@ -1890,6 +1918,10 @@
1890
1918
  "schema": {
1891
1919
  "type": "object",
1892
1920
  "properties": {
1921
+ "account": {
1922
+ "type": "string",
1923
+ "description": "Wallet fixture account name used for convergence and assertions; defaults to dev1."
1924
+ },
1893
1925
  "market": {
1894
1926
  "type": "string",
1895
1927
  "description": "Single market symbol, e.g. BTC or ETH. Alias: symbol."
@@ -189,7 +189,7 @@
189
189
  }
190
190
  },
191
191
  "assert_output": {
192
- "description": "Assert stdout or stderr captured from a command node output.",
192
+ "description": "Assert a previous node's stdout/stderr with contains or match, or compare a structured output field using assert.path and an operator such as eq or length_eq.",
193
193
  "examples": [
194
194
  {
195
195
  "action": "assert_output",
@@ -593,7 +593,7 @@
593
593
  "string",
594
594
  "number"
595
595
  ],
596
- "description": "Text or a typed numeric recipe parameter; numbers are entered as their decimal text."
596
+ "description": "Text or a typed numeric recipe parameter; numbers are entered as their decimal text. Use an empty string to clear the field."
597
597
  },
598
598
  "text": {
599
599
  "type": "string"
@@ -750,7 +750,7 @@
750
750
  ]
751
751
  },
752
752
  "ui.pan": {
753
- "description": "Pan through a continuous pointer path from a test-id target or screen coordinates through the shared browser UI transport.",
753
+ "description": "Pan from a test-id target or screen coordinates through the shared browser UI transport. path and delta are offsets from the resolved start, not absolute endpoints.",
754
754
  "schema": {
755
755
  "type": "object",
756
756
  "properties": {
@@ -848,7 +848,7 @@
848
848
  ]
849
849
  },
850
850
  "ui.drag": {
851
- "description": "Drag a test-id target or screen coordinate through the shared browser UI transport.",
851
+ "description": "Drag a test-id target or screen coordinate through the shared browser UI transport. path and delta are offsets from the resolved start, not absolute endpoints.",
852
852
  "schema": {
853
853
  "type": "object",
854
854
  "properties": {
@@ -1062,7 +1062,7 @@
1062
1062
  ]
1063
1063
  },
1064
1064
  "ui.screenshot": {
1065
- "description": "Capture registered visual evidence through capture-helper, bounded Chrome-native capture, or an explicitly labeled computed-style DOM raster fallback. Always read the resulting PNG; artifact metadata records the provider.",
1065
+ "description": "Capture the current native browser window on macOS, with bounded Chrome viewport capture as fallback. Always inspect the resulting PNG; artifact metadata records the provider. Failed real capture does not produce a reconstructed DOM image.",
1066
1066
  "examples": [
1067
1067
  {
1068
1068
  "action": "ui.screenshot",
@@ -1091,7 +1091,7 @@
1091
1091
  }
1092
1092
  },
1093
1093
  "ui.capture_surface": {
1094
- "description": "Capture the complete current browser page as one registered PNG while leaving ui.screenshot viewport-only.",
1094
+ "description": "Capture the complete current browser document through Chrome as one registered PNG. Includes document content below the viewport, not browser chrome or the hidden contents of nested scroll areas. Keep viewport assertions separate.",
1095
1095
  "schema": {
1096
1096
  "type": "object",
1097
1097
  "properties": {
@@ -1968,7 +1968,7 @@
1968
1968
  ]
1969
1969
  },
1970
1970
  "metamask.assets.open_details": {
1971
- "description": "Open one visible Extension asset by symbol.",
1971
+ "description": "Open the uniquely visible Extension asset matching a symbol; refuse ambiguous rows without clicking. Symbol alone does not prove funding or network identity.",
1972
1972
  "schema": {
1973
1973
  "type": "object",
1974
1974
  "properties": {
@@ -1990,7 +1990,7 @@
1990
1990
  {
1991
1991
  "action": "metamask.assets.open_details",
1992
1992
  "symbol": "ETH",
1993
- "intent": "Open the funded ETH details",
1993
+ "intent": "Open ETH details only when one matching row is visible",
1994
1994
  "next": "done"
1995
1995
  }
1996
1996
  ],
@@ -1999,7 +1999,7 @@
1999
1999
  ]
2000
2000
  },
2001
2001
  "metamask.assets.read_details": {
2002
- "description": "Read and assert the current Extension asset details page through visible UI.",
2002
+ "description": "Observe asset details until requested chart and market data are ready within the polling budget. A required chart needs visible price labels and an unmuted price, not just a canvas. Never refreshes the page.",
2003
2003
  "schema": {
2004
2004
  "type": "object",
2005
2005
  "properties": {
@@ -2037,7 +2037,7 @@
2037
2037
  ]
2038
2038
  },
2039
2039
  "metamask.assets.verify_sorting": {
2040
- "description": "Select both visible Extension asset sort modes, assert their row order, and restore declining balance.",
2040
+ "description": "Verify both Extension asset sort modes and restore the observed initial preference after success or failure. Already-selected modes skip redundant selection; row-order observation is bounded.",
2041
2041
  "schema": {
2042
2042
  "type": "object",
2043
2043
  "properties": {},
@@ -2055,10 +2055,14 @@
2055
2055
  ]
2056
2056
  },
2057
2057
  "metamask.assets.set_token_visibility": {
2058
- "description": "Enable or disable one searched Extension token through Manage tokens and verify the home list state.",
2058
+ "description": "Enable or disable one uniquely matched Extension token through Manage tokens and verify the home list state. Ambiguous search results fail before toggling; require_unchanged verifies existing visibility without repairing it.",
2059
2059
  "schema": {
2060
2060
  "type": "object",
2061
2061
  "properties": {
2062
+ "asset_id": {
2063
+ "type": "string",
2064
+ "description": "Optional ERC-20 CAIP asset ID binding the exact management toggle to a unique Home details route. Enabling waits for import completion and verifies Home identity afterward. Hiding requires visible=false, require_change=true and require_unchanged=false; verifies Home identity before hiding and Home absence after Back. Already-hidden exact checks are unsupported; empty uses query-only matching."
2065
+ },
2062
2066
  "query": {
2063
2067
  "type": "string"
2064
2068
  },
@@ -2531,7 +2535,7 @@
2531
2535
  ]
2532
2536
  },
2533
2537
  "metamask.swap_bridge.read_visible_state": {
2534
- "description": "Read and assert the visible Swap surface from an untouched Extension runtime without product background hooks.",
2538
+ "description": "Read the visible Swap surface without mutation. require_quote additionally needs exposed bridge quote state to verify current input/quote identity; surface-only reads do not require those hooks.",
2535
2539
  "schema": {
2536
2540
  "type": "object",
2537
2541
  "properties": {
@@ -2632,7 +2636,7 @@
2632
2636
  ]
2633
2637
  },
2634
2638
  "metamask.swap_bridge.set_amount": {
2635
- "description": "Enter a positive Swap amount through visible UI and wait for a quote or explicit no-route state.",
2639
+ "description": "Enter a positive Swap amount through visible UI. Quote waiting requires exposed bridge quote state to bind the current token-unit amount and assets to the quote; no implicit refresh or submission.",
2636
2640
  "schema": {
2637
2641
  "type": "object",
2638
2642
  "properties": {
@@ -2700,7 +2704,7 @@
2700
2704
  ]
2701
2705
  },
2702
2706
  "metamask.swap_bridge.set_max_amount": {
2703
- "description": "Use the visible Swap Max control and wait for the derived live quote.",
2707
+ "description": "Use the visible Swap Max control; quote waiting requires exposed bridge quote state and a reviewable quote matching current inputs. Never refresh or submit implicitly.",
2704
2708
  "schema": {
2705
2709
  "type": "object",
2706
2710
  "properties": {
@@ -2729,7 +2733,7 @@
2729
2733
  ]
2730
2734
  },
2731
2735
  "metamask.perps.read_visible_state": {
2732
- "description": "Read and assert bounded visible Perps UI state from an untouched Extension runtime without product background hooks.",
2736
+ "description": "Read and assert bounded visible Perps UI state without product background hooks. favorite is boolean only when the requested market pair and one favorite control are both visible; otherwise null, never an assumed false.",
2733
2737
  "schema": {
2734
2738
  "type": "object",
2735
2739
  "properties": {
@@ -2996,6 +3000,10 @@
2996
3000
  "schema": {
2997
3001
  "type": "object",
2998
3002
  "properties": {
3003
+ "placement_node": {
3004
+ "type": "string",
3005
+ "description": "Exact placement receipt node relative to this recipe call, such as submit or setup/place. Supports cleanup after a failed submission; never selects the newest receipt."
3006
+ },
2999
3007
  "market": {
3000
3008
  "type": "string"
3001
3009
  },
@@ -3163,7 +3171,8 @@
3163
3171
  },
3164
3172
  "require_unchanged": {
3165
3173
  "type": "boolean",
3166
- "default": false
3174
+ "default": false,
3175
+ "description": "Assert the requested favorite state without clicking; a mismatch fails before any favorite change."
3167
3176
  }
3168
3177
  },
3169
3178
  "required": [
@@ -3809,6 +3818,11 @@
3809
3818
  "schema": {
3810
3819
  "type": "object",
3811
3820
  "properties": {
3821
+ "network": {
3822
+ "type": "string",
3823
+ "enum": ["testnet", "mainnet"],
3824
+ "description": "Require the currently selected Perps network before reading; never switches it."
3825
+ },
3812
3826
  "market": {
3813
3827
  "type": "string",
3814
3828
  "description": "Single market symbol, e.g. BTC or ETH. Alias: symbol."
@@ -3906,6 +3920,11 @@
3906
3920
  "schema": {
3907
3921
  "type": "object",
3908
3922
  "properties": {
3923
+ "network": {
3924
+ "type": "string",
3925
+ "enum": ["testnet", "mainnet"],
3926
+ "description": "Require the currently selected Perps network before reading; never switches it."
3927
+ },
3909
3928
  "market": {
3910
3929
  "type": "string",
3911
3930
  "description": "Single market symbol, e.g. BTC or ETH. Alias: symbol."
@@ -4003,6 +4022,10 @@
4003
4022
  "schema": {
4004
4023
  "type": "object",
4005
4024
  "properties": {
4025
+ "placement_node": {
4026
+ "type": "string",
4027
+ "description": "Exact visible-form placement receipt node relative to this recipe call. Selects receipt-owned UI cancellation even when a controller bridge exists."
4028
+ },
4006
4029
  "market": {
4007
4030
  "type": "string",
4008
4031
  "description": "Single market symbol, e.g. BTC or ETH. Alias: symbol."
@@ -4165,6 +4188,11 @@
4165
4188
  "schema": {
4166
4189
  "type": "object",
4167
4190
  "properties": {
4191
+ "from_current_form": {
4192
+ "type": "boolean",
4193
+ "default": false,
4194
+ "description": "Submit the already configured visible form once without navigation, field edits, or controller placement. Requires an empty position/order baseline, verified provider/network, matching market/direction/amount/leverage, and trusted funding. Prepared limit sizing currently requires 1x leverage. Preserves the current protection settings and observes the product's resulting route."
4195
+ },
4168
4196
  "market": {
4169
4197
  "type": "string",
4170
4198
  "description": "Single market symbol, e.g. BTC or ETH. Alias: symbol."
@@ -4818,10 +4846,15 @@
4818
4846
  ]
4819
4847
  },
4820
4848
  "metamask.perps.teardown_state": {
4821
- "description": "Observe and assert the requested final Extension Perps order and position state, then optionally navigate. This action never closes or cancels financial state.",
4849
+ "description": "Verify an optional expected network, assert the requested final Extension Perps order and position state, then optionally navigate. This action never switches networks, closes positions or cancels orders.",
4822
4850
  "schema": {
4823
4851
  "type": "object",
4824
4852
  "properties": {
4853
+ "network": {
4854
+ "type": "string",
4855
+ "enum": ["testnet", "mainnet"],
4856
+ "description": "Expected current Perps network; mismatch fails before state assertions or navigation, without switching it."
4857
+ },
4825
4858
  "market": {
4826
4859
  "type": "string"
4827
4860
  },
@@ -178,7 +178,7 @@
178
178
  }
179
179
  },
180
180
  "assert_output": {
181
- "description": "Assert stdout or stderr captured from a command node output.",
181
+ "description": "Assert a previous node's stdout/stderr with contains or match, or compare a structured output field using assert.path and an operator such as eq or length_eq.",
182
182
  "examples": [
183
183
  {
184
184
  "action": "assert_output",
@@ -699,7 +699,7 @@
699
699
  ]
700
700
  },
701
701
  "ui.pan": {
702
- "description": "Pan from a test-id target or screen coordinates through the shared native UI provider.",
702
+ "description": "Pan from a test-id target or screen coordinates through the shared native UI provider. path and delta are offsets from the resolved start, not absolute endpoints.",
703
703
  "schema": {
704
704
  "type": "object",
705
705
  "properties": {
@@ -754,7 +754,7 @@
754
754
  ]
755
755
  },
756
756
  "ui.drag": {
757
- "description": "Drag a test-id target or screen coordinate through the shared native UI provider.",
757
+ "description": "Drag a test-id target or screen coordinate through the shared native UI provider. path and delta are offsets from the resolved start, not absolute endpoints.",
758
758
  "schema": {
759
759
  "type": "object",
760
760
  "properties": {
@@ -1814,7 +1814,7 @@
1814
1814
  "execution_capabilities": ["host-read-export"]
1815
1815
  },
1816
1816
  "metamask.assets.verify_sorting": {
1817
- "description": "Select alphabetical and declining-fiat token sorting and assert the visible native row order.",
1817
+ "description": "Verify native name order under MetaMask's SES collation and declining-fiat order, then restore the original sort preference.",
1818
1818
  "schema": {
1819
1819
  "type": "object",
1820
1820
  "properties": {
@@ -1830,10 +1830,11 @@
1830
1830
  "execution_capabilities": ["app-mutation"]
1831
1831
  },
1832
1832
  "metamask.assets.set_token_visibility": {
1833
- "description": "Import or remove one Mobile token through visible native controls and verify the resulting state.",
1833
+ "description": "Import or remove one uniquely matched Mobile token through native controls. Require an observed matching label for import state and reject duplicate matching rows before selection or removal.",
1834
1834
  "schema": {
1835
1835
  "type": "object",
1836
1836
  "properties": {
1837
+ "asset_id": { "type": "string", "enum": [""], "description": "Exact asset-ID corroboration is not implemented on Mobile; only the omitted/empty query-only mode is supported." },
1837
1838
  "query": { "type": "string" },
1838
1839
  "visible": { "type": "boolean" },
1839
1840
  "require_unchanged": { "type": "boolean", "default": false },
@@ -2087,7 +2088,7 @@
2087
2088
  "execution_capabilities": ["app-mutation"]
2088
2089
  },
2089
2090
  "metamask.perps.read_visible_state": {
2090
- "description": "Read and assert bounded visible Perps UI state from an opaque Mobile runtime through native accessibility.",
2091
+ "description": "Read and assert bounded visible Perps UI state through native accessibility. favorite is boolean only when the requested market pair and one favorite control are both observed as visible; otherwise null, never an assumed false.",
2091
2092
  "schema": {
2092
2093
  "type": "object",
2093
2094
  "properties": {
@@ -2208,7 +2209,7 @@
2208
2209
  "properties": {
2209
2210
  "market": { "type": "string" },
2210
2211
  "favorite": { "type": "boolean" },
2211
- "require_unchanged": { "type": "boolean", "default": false },
2212
+ "require_unchanged": { "type": "boolean", "default": false, "description": "Assert the requested favorite state without clicking; a mismatch fails before any favorite change." },
2212
2213
  "timeout_ms": { "type": "integer", "minimum": 1000, "default": 30000 }
2213
2214
  },
2214
2215
  "required": ["market", "favorite"],
@@ -3282,7 +3283,7 @@
3282
3283
  "execution_capabilities": ["app-mutation"]
3283
3284
  },
3284
3285
  "metamask.perps.clear_performance_caches": {
3285
- "description": "mobile Clear only persisted Perps market/user performance caches in the development app before a cold-no-cache validation run.",
3286
+ "description": "Mobile source-dev cold-start preparation: clear Perps disk and controller market/user caches and disconnect its marketData, positions, orders and account channels. Preserve wallet/exchange state; follow with an explicit restart before proof, never use as recovery inside a proof action.",
3286
3287
  "schema": {
3287
3288
  "type": "object",
3288
3289
  "properties": {},
@@ -3298,7 +3299,7 @@
3298
3299
  "execution_capabilities": ["app-mutation"]
3299
3300
  },
3300
3301
  "metamask.perps.ensure_mode": {
3301
- "description": "Ensure the viewport-visible Perps market-detail root is Lite or Pro. The action presses the active mode control at most once and fails closed if that press opens another chooser or does not converge directly.",
3302
+ "description": "Ensure the viewport-visible Perps market-detail root is Lite or Pro. Press the active mode control at most once and select the requested first-entry choice if shown; fail if the requested detail root does not appear before the deadline.",
3302
3303
  "schema": {
3303
3304
  "type": "object",
3304
3305
  "properties": {
@@ -3340,7 +3341,7 @@
3340
3341
  },
3341
3342
  "account": {
3342
3343
  "type": "string",
3343
- "description": "Optional exact account name. Omit it to keep the currently selected account."
3344
+ "description": "Optional exact account name or EVM address. Prefer an address when the current product does not expose account names. Omit it to keep the currently selected account."
3344
3345
  },
3345
3346
  "provider": {
3346
3347
  "type": "string",
@@ -2,6 +2,23 @@
2
2
  "$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
3
3
  "title": "MetaMask core Perps smoke",
4
4
  "description": "Non-mutating Perps domain smoke for the headless core adapter: instantiates the Perps controller and reads live positions, orders, and account state.",
5
+ "paramsSchema": {
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "account": {
10
+ "type": "string",
11
+ "default": "",
12
+ "description": "Fixture account name, defaulting to dev1. Without a fixture, supply an EVM address for read-only inspection."
13
+ },
14
+ "network": {
15
+ "type": "string",
16
+ "enum": ["testnet", "mainnet"],
17
+ "default": "testnet",
18
+ "description": "Network for every isolated controller read."
19
+ }
20
+ }
21
+ },
5
22
  "workflow": {
6
23
  "entry": "status",
7
24
  "nodes": {
@@ -12,18 +29,24 @@
12
29
  },
13
30
  "read-positions": {
14
31
  "action": "metamask.perps.read_positions",
32
+ "account": "{{params.account}}",
33
+ "network": "{{params.network}}",
15
34
  "mode": "all",
16
35
  "next": "read-orders",
17
36
  "intent": "Read live Perps positions from the headless controller"
18
37
  },
19
38
  "read-orders": {
20
39
  "action": "metamask.perps.read_orders",
40
+ "account": "{{params.account}}",
41
+ "network": "{{params.network}}",
21
42
  "mode": "all",
22
43
  "next": "read-account",
23
44
  "intent": "Read live Perps open orders from the headless controller"
24
45
  },
25
46
  "read-account": {
26
47
  "action": "metamask.perps.read_account",
48
+ "account": "{{params.account}}",
49
+ "network": "{{params.network}}",
27
50
  "next": "done",
28
51
  "intent": "Read live Perps account state from the headless controller"
29
52
  },
@@ -2,6 +2,29 @@
2
2
  "$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
3
3
  "title": "MetaMask core Perps snapshots",
4
4
  "description": "Reads global market and account snapshots through the public PerpsController API used by wallet clients. Configure CORE_PERPS_TERMINAL_GLOBAL_SNAPSHOT_URL to exercise a Terminal schema-v2 endpoint.",
5
+ "paramsSchema": {
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "account": {
10
+ "type": "string",
11
+ "default": "",
12
+ "description": "Fixture account name, defaulting to dev1. Without a fixture, supply an EVM address for read-only inspection."
13
+ },
14
+ "network": {
15
+ "type": "string",
16
+ "enum": ["testnet", "mainnet"],
17
+ "default": "testnet",
18
+ "description": "Perps network for this isolated snapshot read."
19
+ },
20
+ "scope": {
21
+ "type": "string",
22
+ "enum": ["markets", "account", "all"],
23
+ "default": "all",
24
+ "description": "Read market data, account state, or both through the public controller API."
25
+ }
26
+ }
27
+ },
5
28
  "workflow": {
6
29
  "entry": "status",
7
30
  "nodes": {
@@ -12,7 +35,9 @@
12
35
  },
13
36
  "read_snapshot": {
14
37
  "action": "metamask.perps.read_snapshot",
15
- "scope": "all",
38
+ "scope": "{{params.scope}}",
39
+ "account": "{{params.account}}",
40
+ "network": "{{params.network}}",
16
41
  "next": "done",
17
42
  "intent": "Read coherent market and account snapshots through public controller methods"
18
43
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
3
3
  "title": "Assets release custom-network token persistence",
4
- "description": "Adds PulseChain and PLSX, proves both survive lock/unlock, then removes the custom network.",
4
+ "description": "Adds a previously absent custom network and token, proves persistence through lock/unlock, then removes the network this run confirmed adding. PulseChain and PLSX are the defaults.",
5
5
  "paramsSchema": {
6
6
  "type": "object",
7
7
  "additionalProperties": false,
@@ -14,11 +14,14 @@
14
14
  "token_address": { "type": "string", "default": "0x95B303987A60C71504D99Aa1b13B4DA07b0790ab" },
15
15
  "token_symbol": { "type": "string", "default": "PLSX" },
16
16
  "token_decimals": { "type": "integer", "minimum": 0, "default": 18 },
17
+ "before_capture_path": { "type": "string", "default": "screenshots/custom-network-token-before-lock.png" },
18
+ "after_capture_path": { "type": "string", "default": "screenshots/custom-network-token-after-unlock.png" },
17
19
  "timeout_ms": { "type": "integer", "minimum": 1000, "default": 60000 }
18
20
  }
19
21
  },
20
22
  "workflow": {
21
23
  "entry": "open-wallet",
24
+ "teardown": "require-owned-network",
22
25
  "nodes": {
23
26
  "open-wallet": {
24
27
  "action": "ui.navigate",
@@ -51,9 +54,9 @@
51
54
  },
52
55
  "capture-before": {
53
56
  "action": "ui.screenshot",
54
- "path": "screenshots/custom-network-token-before-lock.png",
57
+ "path": "{{params.before_capture_path}}",
55
58
  "label": "Custom network token before lock",
56
- "intent": "Preserve pre-lock PulseChain and PLSX evidence",
59
+ "intent": "Preserve pre-lock {{params.network}} and {{params.token_symbol}} evidence",
57
60
  "next": "lock-wallet"
58
61
  },
59
62
  "lock-wallet": {
@@ -88,9 +91,22 @@
88
91
  },
89
92
  "capture-after": {
90
93
  "action": "ui.screenshot",
91
- "path": "screenshots/custom-network-token-after-unlock.png",
94
+ "path": "{{params.after_capture_path}}",
92
95
  "label": "Custom network token after unlock",
93
- "intent": "Preserve post-unlock PulseChain and PLSX evidence",
96
+ "intent": "Preserve post-unlock {{params.network}} and {{params.token_symbol}} evidence",
97
+ "next": "done"
98
+ },
99
+ "require-owned-network": {
100
+ "action": "assert_output",
101
+ "source": "add-network",
102
+ "stream": "added",
103
+ "match": "^true$",
104
+ "intent": "Refuse deletion without completed evidence that this run added the network",
105
+ "next": "unlock-cleanup"
106
+ },
107
+ "unlock-cleanup": {
108
+ "action": "metamask.wallet.ensure_unlocked",
109
+ "intent": "Reach the confirmed task-owned network settings even if the lock-cycle proof failed",
94
110
  "next": "remove-network"
95
111
  },
96
112
  "remove-network": {
@@ -98,10 +114,11 @@
98
114
  "network": "{{params.network}}",
99
115
  "chain_id": "{{params.chain_id}}",
100
116
  "timeout_ms": "{{params.timeout_ms}}",
101
- "intent": "Remove the custom network and its token during cleanup",
102
- "next": "done"
117
+ "intent": "Remove only the custom network this run confirmed adding",
118
+ "next": "cleanup-done"
103
119
  },
104
- "done": { "action": "end", "status": "pass" }
120
+ "done": { "action": "end", "status": "pass" },
121
+ "cleanup-done": { "action": "end", "status": "pass" }
105
122
  }
106
123
  }
107
124
  }