@deeeed/metamask-harness 0.47.4 → 0.48.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.
- package/CHANGELOG.md +6 -0
- package/README.md +3 -2
- package/adapters/extension/lib/macos-focus.cjs +2 -2
- package/adapters/extension/live.sh +6 -8
- package/adapters/mobile/bridge-runtime/lib/bridge-errors.cjs +1 -0
- package/adapters/shared/ensure-runner-deps.sh +6 -0
- package/dist/adapters/extension/runtime-decision.js +18 -1
- package/dist/adapters/extension/runtime.js +5 -5
- package/dist/adapters/extension/surface.js +1 -0
- package/dist/app-lifecycle.js +3 -1
- package/dist/command-contract.js +2 -0
- package/dist/commands/call.js +1 -8
- package/dist/commands/device-target.js +12 -3
- package/dist/commands/doctor.js +7 -5
- package/dist/commands/fixtures.js +1 -1
- package/dist/commands/launch/index.js +10 -0
- package/dist/commands/manifest.js +3 -1
- package/dist/commands/mobile-device-view.js +13 -3
- package/dist/commands/parse-args.js +2 -0
- package/dist/commands/run-engine.js +29 -4
- package/dist/commands/run.js +1 -1
- package/dist/commands/runtime-launch.js +10 -1
- package/dist/commands/status.js +8 -4
- package/dist/devices.js +31 -9
- package/dist/execution-provenance.js +8 -7
- package/dist/funding-execution-context.js +43 -13
- package/dist/heal-bounds.js +3 -3
- package/dist/live-adapter-contract.js +4 -0
- package/dist/manifest.js +2 -13
- package/dist/mm-harness-cli.js +2 -1
- package/dist/runner.js +14 -3
- package/docs/RECIPES.md +133 -22
- package/docs/RELEASE-QA-CAPABILITY-MAP.md +3 -2
- package/library/actions/core/perps/_controller.mjs +10 -0
- package/library/actions/core/perps/close_orders.mjs +13 -17
- package/library/actions/extension/assets/home-token-identity.mjs +30 -0
- package/library/actions/extension/assets/import_custom_token.mjs +61 -79
- package/library/actions/extension/assets/open_details.mjs +18 -7
- package/library/actions/extension/assets/read_details.mjs +61 -29
- package/library/actions/extension/assets/read_visible_state.mjs +4 -1
- package/library/actions/extension/assets/select_network_scope.mjs +16 -9
- package/library/actions/extension/assets/set_token_visibility.mjs +84 -17
- package/library/actions/extension/assets/verify_sorting.mjs +62 -14
- package/library/actions/extension/networks/add_custom.mjs +19 -17
- package/library/actions/extension/networks/custom_network.mjs +8 -3
- package/library/actions/extension/networks/read_visible_state.mjs +1 -0
- package/library/actions/extension/performance/_navigation-memory.mjs +35 -6
- package/library/actions/extension/performance/compare_idle_navigation_memory.mjs +3 -2
- package/library/actions/extension/performance/measure_detached_dom.mjs +3 -2
- package/library/actions/extension/performance/measure_navigation_memory.mjs +3 -2
- package/library/actions/extension/perps/assert_visible_consistency.mjs +9 -4
- package/library/actions/extension/perps/close_visible_position.mjs +3 -3
- package/library/actions/extension/perps/edit_margin.mjs +1 -1
- package/library/actions/extension/perps/open_balance_action.mjs +16 -7
- package/library/actions/extension/perps/perps.mjs +150 -45
- package/library/actions/extension/perps/read_funds_confirmation.mjs +8 -0
- package/library/actions/extension/perps/read_snapshot.mjs +2 -1
- package/library/actions/extension/perps/read_visible_state.mjs +10 -6
- package/library/actions/extension/perps/search_markets.mjs +7 -2
- package/library/actions/extension/perps/select_activity_filter.mjs +1 -1
- package/library/actions/extension/perps/select_market_filter.mjs +74 -45
- package/library/actions/extension/perps/set_market_favorite.mjs +6 -3
- package/library/actions/extension/perps/state.mjs +1 -0
- package/library/actions/extension/perps/update_position_tpsl.mjs +1 -1
- package/library/actions/extension/perps/visible-mutation-identity.mjs +4 -2
- package/library/actions/extension/platform/cdp.mjs +23 -142
- package/library/actions/extension/swap_bridge/quote-state.mjs +54 -0
- package/library/actions/extension/swap_bridge/read_visible_state.mjs +4 -1
- package/library/actions/extension/swap_bridge/select_assets.mjs +11 -1
- package/library/actions/extension/swap_bridge/set_amount.mjs +10 -8
- package/library/actions/extension/swap_bridge/set_max_amount.mjs +17 -18
- package/library/actions/extension/swap_bridge/set_slippage.mjs +0 -9
- package/library/actions/extension/ui/navigate.mjs +58 -14
- package/library/actions/extension/wallet/lock.mjs +2 -1
- package/library/actions/mobile/assets/import_custom_token.mjs +1 -0
- package/library/actions/mobile/assets/read_visible_state.mjs +3 -1
- package/library/actions/mobile/assets/set_token_visibility.mjs +13 -9
- package/library/actions/mobile/networks/network-management.mjs +22 -15
- package/library/actions/mobile/networks/remove_custom.mjs +6 -2
- package/library/actions/mobile/perps/perps.mjs +3 -0
- package/library/actions/mobile/perps/read-visible-state-loop.mjs +1 -0
- package/library/actions/mobile/perps/set_market_favorite.mjs +15 -19
- package/library/actions/mobile/platform/native-session.mjs +26 -17
- package/library/actions/mobile/platform/observe-ui.mjs +2 -2
- package/library/actions/mobile/ui/navigate.mjs +1 -1
- package/library/actions/shared/perps/visible-state.mjs +14 -3
- package/library/manifests/core.action-manifest.json +14 -2
- package/library/manifests/extension.action-manifest.json +49 -16
- package/library/manifests/mobile.action-manifest.json +8 -7
- package/library/recipes/core/perps/smoke.recipe.json +23 -0
- package/library/recipes/core/perps/snapshot.recipe.json +26 -1
- package/library/recipes/extension/assets/release-custom-network-token-persistence.recipe.json +25 -8
- package/library/recipes/extension/performance/navigation-memory-suite.recipe.json +219 -0
- package/library/recipes/extension/performance/navigation-memory.recipe.json +61 -156
- package/library/recipes/extension/wallet/smoke.recipe.json +17 -8
- package/library/recipes/mobile/app/lifecycle.android-smoke.recipe.json +23 -4
- package/library/recipes/mobile/networks/release-custom-network-persistence.recipe.json +31 -6
- package/library/recipes/mobile/networks/release-multichain-visibility.recipe.json +5 -4
- package/library/recipes/mobile/wallet/smoke.recipe.json +17 -8
- package/library/recipes/{assets → shared/assets}/release-token-address-import.recipe.json +7 -0
- package/library/recipes/{assets → shared/assets}/release-token-details.recipe.json +2 -2
- package/library/recipes/shared/assets/release-token-hide-readd.recipe.json +91 -0
- package/library/recipes/{assets → shared/assets}/release-token-persistence.recipe.json +3 -0
- package/library/recipes/{assets → shared/assets}/release-token-search-import.recipe.json +4 -1
- package/library/recipes/{assets → shared/assets}/release-token-sorting.recipe.json +13 -2
- package/library/recipes/{swap-bridge → shared/swap-bridge}/release-quote.recipe.json +29 -3
- package/package.json +3 -3
- package/site/assets/help-recipes.json +63 -23
- package/library/recipes/assets/release-token-hide-readd.recipe.json +0 -57
- package/library/recipes/core/perps/clean-market-testnet.recipe.json +0 -44
- package/library/recipes/core/perps/order-lifecycle.recipe.json +0 -82
- package/library/recipes/core/perps/read-markets.recipe.json +0 -36
- package/library/recipes/core/perps/trading-lifecycle.recipe.json +0 -80
- package/library/recipes/extension/perps/release-activity-filters.recipe.json +0 -51
- package/library/recipes/extension/perps/release-funds-flows.recipe.json +0 -76
- package/library/recipes/extension/perps/release-live-limit-order.recipe.json +0 -109
- package/library/recipes/extension/perps/release-market-filters.recipe.json +0 -97
- package/library/recipes/extension/perps/release-order-entry.recipe.json +0 -158
- package/library/recipes/extension/perps/release-order-validation.recipe.json +0 -259
- package/library/recipes/extension/perps/release-visible-consistency.recipe.json +0 -47
- package/library/recipes/extension/perps/source-dev-snapshot-consistency.recipe.json +0 -76
- package/library/recipes/mobile/perps/chase-assert-running.recipe.json +0 -110
- package/library/recipes/mobile/perps/chase-place.recipe.json +0 -145
- package/library/recipes/mobile/perps/chase-terminate.recipe.json +0 -98
- package/library/recipes/mobile/perps/performance.recipe.json +0 -722
- package/library/recipes/mobile/perps/pro-order-setup.recipe.json +0 -111
- package/library/recipes/mobile/perps/pro-order-start-state.recipe.json +0 -53
- package/library/recipes/mobile/perps/scale-assert-orders.recipe.json +0 -126
- package/library/recipes/mobile/perps/scale-place.recipe.json +0 -186
- package/library/recipes/mobile/perps/twap-assert-active.recipe.json +0 -97
- package/library/recipes/mobile/perps/twap-place.recipe.json +0 -146
- package/library/recipes/mobile/runner/action-validation.recipe.json +0 -336
- package/library/recipes/perps/clean-market-testnet.recipe.json +0 -49
- package/library/recipes/perps/lifecycle.recipe.json +0 -144
- package/library/recipes/perps/release-live-market-order.recipe.json +0 -139
- package/library/recipes/perps/release-watchlist.recipe.json +0 -111
- package/library/recipes/swap-bridge/release-max-quote.recipe.json +0 -72
- /package/library/recipes/{assets → shared/assets}/release-token-list.recipe.json +0 -0
- /package/library/recipes/{perps → shared/perps}/release-market-details.recipe.json +0 -0
- /package/library/recipes/{perps → shared/perps}/release-market-search.recipe.json +0 -0
- /package/library/recipes/{swap-bridge → shared/swap-bridge}/release-custom-slippage.recipe.json +0 -0
- /package/library/recipes/{swap-bridge → shared/swap-bridge}/release-live-max-swap.recipe.json +0 -0
- /package/library/recipes/{swap-bridge → shared/swap-bridge}/release-live-swap.recipe.json +0 -0
- /package/library/recipes/{swap-bridge → shared/swap-bridge}/release-surface.recipe.json +0 -0
- /package/library/recipes/{wallet → shared/wallet}/import.recipe.json +0 -0
- /package/library/recipes/{wallet → shared/wallet}/reset-import.recipe.json +0 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
|
|
3
3
|
"title": "Mobile multi-network asset visibility",
|
|
4
|
-
"description": "
|
|
4
|
+
"description": "Checks that the requested asset symbols and formatted fiat values appear together in the Mobile wallet. Symbols alone do not prove network identity or funding.",
|
|
5
5
|
"paramsSchema": {
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
8
|
"properties": {
|
|
9
|
+
"capture_path": { "type": "string", "default": "screenshots/mobile-multichain-assets.png" },
|
|
9
10
|
"symbols": {
|
|
10
11
|
"type": "array",
|
|
11
12
|
"items": { "type": "string" },
|
|
@@ -39,14 +40,14 @@
|
|
|
39
40
|
"minimum_asset_count": "{{params.minimum_asset_count}}",
|
|
40
41
|
"require_fiat": true,
|
|
41
42
|
"max_items": 100,
|
|
42
|
-
"intent": "Prove
|
|
43
|
+
"intent": "Prove the requested asset symbols and formatted fiat values appear together",
|
|
43
44
|
"next": "capture-wallet"
|
|
44
45
|
},
|
|
45
46
|
"capture-wallet": {
|
|
46
47
|
"action": "ui.screenshot",
|
|
47
|
-
"path": "
|
|
48
|
+
"path": "{{params.capture_path}}",
|
|
48
49
|
"label": "Mobile multi-network asset visibility",
|
|
49
|
-
"intent": "Preserve reviewer-visible proof of
|
|
50
|
+
"intent": "Preserve reviewer-visible proof of the requested asset rows",
|
|
50
51
|
"next": "done"
|
|
51
52
|
},
|
|
52
53
|
"done": {
|
|
@@ -2,18 +2,27 @@
|
|
|
2
2
|
"$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
|
|
3
3
|
"title": "MetaMask Mobile wallet smoke",
|
|
4
4
|
"description": "Fixture-backed proof that the Mobile wallet is reachable, unlocked, readable, and visually capturable without depending on a product feature domain.",
|
|
5
|
+
"paramsSchema": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"capture_path": { "type": "string", "default": "screenshots/wallet-smoke.png" }
|
|
10
|
+
}
|
|
11
|
+
},
|
|
5
12
|
"workflow": {
|
|
6
|
-
"entry": "
|
|
13
|
+
"entry": "fixture",
|
|
7
14
|
"nodes": {
|
|
8
|
-
"status": {
|
|
9
|
-
"action": "app.status",
|
|
10
|
-
"next": "fixture",
|
|
11
|
-
"intent": "Confirm the Mobile runtime is reachable"
|
|
12
|
-
},
|
|
13
15
|
"fixture": {
|
|
14
16
|
"action": "metamask.wallet.fixture_status",
|
|
17
|
+
"next": "require-fixture",
|
|
18
|
+
"intent": "Read the checkout's wallet fixture readiness without changing it"
|
|
19
|
+
},
|
|
20
|
+
"require-fixture": {
|
|
21
|
+
"action": "assert_output",
|
|
22
|
+
"source": "fixture",
|
|
23
|
+
"assert": { "path": "$.status", "operator": "eq", "value": "ready" },
|
|
15
24
|
"next": "unlock",
|
|
16
|
-
"intent": "
|
|
25
|
+
"intent": "Require a ready on-disk fixture before interacting with the wallet; this does not prove imported account identity"
|
|
17
26
|
},
|
|
18
27
|
"unlock": {
|
|
19
28
|
"action": "metamask.wallet.ensure_unlocked",
|
|
@@ -27,7 +36,7 @@
|
|
|
27
36
|
},
|
|
28
37
|
"screenshot": {
|
|
29
38
|
"action": "ui.screenshot",
|
|
30
|
-
"path": "
|
|
39
|
+
"path": "{{params.capture_path}}",
|
|
31
40
|
"next": "done",
|
|
32
41
|
"intent": "Record the visible wallet state as evidence"
|
|
33
42
|
},
|
|
@@ -32,6 +32,13 @@
|
|
|
32
32
|
"decimals": "{{params.decimals}}",
|
|
33
33
|
"timeout_ms": "{{params.timeout_ms}}",
|
|
34
34
|
"intent": "Import the token by address on the selected network and verify its metadata",
|
|
35
|
+
"next": "assert-imported"
|
|
36
|
+
},
|
|
37
|
+
"assert-imported": {
|
|
38
|
+
"action": "assert_output",
|
|
39
|
+
"source": "import-token",
|
|
40
|
+
"assert": { "path": "$.changed", "operator": "eq", "value": true },
|
|
41
|
+
"intent": "Require a completed import rather than reusing an already-imported token",
|
|
35
42
|
"next": "capture-imported-token"
|
|
36
43
|
},
|
|
37
44
|
"capture-imported-token": {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
|
|
3
3
|
"title": "Assets release token details",
|
|
4
|
-
"description": "
|
|
4
|
+
"description": "Open a visible token matching symbol and assert its chart and market cap through product UI. Symbol-only selection does not identify a network or token contract; use a uniquely matching row for this proof. Native reader compatibility must be validated on the tested build.",
|
|
5
5
|
"paramsSchema": {
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"action": "metamask.assets.open_details",
|
|
25
25
|
"symbol": "{{params.symbol}}",
|
|
26
26
|
"timeout_ms": "{{params.timeout_ms}}",
|
|
27
|
-
"intent": "Open the requested
|
|
27
|
+
"intent": "Open the visible token matching the requested symbol; no funding or network claim",
|
|
28
28
|
"next": "assert-details"
|
|
29
29
|
},
|
|
30
30
|
"assert-details": {
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
|
|
3
|
+
"title": "Assets release token hide and re-add",
|
|
4
|
+
"description": "Requires an enabled imported token, hides and re-enables it through Manage tokens, and restores the enabled baseline on failure. Both proof transitions must change state.",
|
|
5
|
+
"paramsSchema": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"query": { "type": "string", "default": "DAI" },
|
|
10
|
+
"asset_id": { "type": "string", "default": "", "description": "Exact ERC-20 identity on Extension; leave empty for Mobile query-only selection." },
|
|
11
|
+
"hidden_capture_path": { "type": "string", "default": "screenshots/assets-token-hidden.png" },
|
|
12
|
+
"restored_capture_path": { "type": "string", "default": "screenshots/assets-token-restored.png" },
|
|
13
|
+
"timeout_ms": { "type": "integer", "minimum": 1000, "default": 45000 }
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"workflow": {
|
|
17
|
+
"entry": "open-wallet",
|
|
18
|
+
"teardown": "baseline-known",
|
|
19
|
+
"nodes": {
|
|
20
|
+
"open-wallet": {
|
|
21
|
+
"action": "ui.navigate",
|
|
22
|
+
"page": "home",
|
|
23
|
+
"intent": "Open the all-network asset list",
|
|
24
|
+
"next": "assert-baseline"
|
|
25
|
+
},
|
|
26
|
+
"assert-baseline": {
|
|
27
|
+
"action": "metamask.assets.set_token_visibility",
|
|
28
|
+
"asset_id": "{{params.asset_id}}",
|
|
29
|
+
"query": "{{params.query}}",
|
|
30
|
+
"visible": true,
|
|
31
|
+
"require_unchanged": true,
|
|
32
|
+
"timeout_ms": "{{params.timeout_ms}}",
|
|
33
|
+
"intent": "Verify the original enabled token before authorizing any restoration",
|
|
34
|
+
"next": "hide-token"
|
|
35
|
+
},
|
|
36
|
+
"hide-token": {
|
|
37
|
+
"action": "metamask.assets.set_token_visibility",
|
|
38
|
+
"asset_id": "{{params.asset_id}}",
|
|
39
|
+
"query": "{{params.query}}",
|
|
40
|
+
"visible": false,
|
|
41
|
+
"require_change": true,
|
|
42
|
+
"timeout_ms": "{{params.timeout_ms}}",
|
|
43
|
+
"intent": "Disable the imported token and prove its row disappears",
|
|
44
|
+
"next": "capture-hidden"
|
|
45
|
+
},
|
|
46
|
+
"capture-hidden": {
|
|
47
|
+
"action": "ui.screenshot",
|
|
48
|
+
"path": "{{params.hidden_capture_path}}",
|
|
49
|
+
"label": "Token hidden",
|
|
50
|
+
"intent": "Preserve reviewer-visible hidden state",
|
|
51
|
+
"next": "readd-token"
|
|
52
|
+
},
|
|
53
|
+
"readd-token": {
|
|
54
|
+
"action": "metamask.assets.set_token_visibility",
|
|
55
|
+
"asset_id": "{{params.asset_id}}",
|
|
56
|
+
"query": "{{params.query}}",
|
|
57
|
+
"visible": true,
|
|
58
|
+
"require_change": true,
|
|
59
|
+
"timeout_ms": "{{params.timeout_ms}}",
|
|
60
|
+
"intent": "Re-enable the token and prove its metadata row returns",
|
|
61
|
+
"next": "capture-restored"
|
|
62
|
+
},
|
|
63
|
+
"capture-restored": {
|
|
64
|
+
"action": "ui.screenshot",
|
|
65
|
+
"path": "{{params.restored_capture_path}}",
|
|
66
|
+
"label": "Token restored",
|
|
67
|
+
"intent": "Preserve reviewer-visible restored state",
|
|
68
|
+
"next": "done"
|
|
69
|
+
},
|
|
70
|
+
"done": { "action": "end", "status": "pass" },
|
|
71
|
+
"baseline-known": {
|
|
72
|
+
"action": "assert_output",
|
|
73
|
+
"source": "assert-baseline",
|
|
74
|
+
"stream": "requested",
|
|
75
|
+
"match": "^true$",
|
|
76
|
+
"intent": "Refuse restoration when this run did not verify the original enabled token",
|
|
77
|
+
"next": "restore-enabled"
|
|
78
|
+
},
|
|
79
|
+
"restore-enabled": {
|
|
80
|
+
"action": "metamask.assets.set_token_visibility",
|
|
81
|
+
"asset_id": "{{params.asset_id}}",
|
|
82
|
+
"query": "{{params.query}}",
|
|
83
|
+
"visible": true,
|
|
84
|
+
"timeout_ms": "{{params.timeout_ms}}",
|
|
85
|
+
"intent": "Restore the original enabled token after success or failure without changing the proof verdict",
|
|
86
|
+
"next": "cleanup-done"
|
|
87
|
+
},
|
|
88
|
+
"cleanup-done": { "action": "end", "status": "pass" }
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
8
|
"properties": {
|
|
9
|
+
"asset_id": { "type": "string", "default": "", "description": "Optional exact ERC-20 identity on Extension; Mobile supports only empty/query-only mode." },
|
|
9
10
|
"query": { "type": "string", "default": "DAI" },
|
|
10
11
|
"timeout_ms": { "type": "integer", "minimum": 1000, "default": 45000 }
|
|
11
12
|
}
|
|
@@ -21,6 +22,7 @@
|
|
|
21
22
|
},
|
|
22
23
|
"assert-prestate": {
|
|
23
24
|
"action": "metamask.assets.set_token_visibility",
|
|
25
|
+
"asset_id": "{{params.asset_id}}",
|
|
24
26
|
"query": "{{params.query}}",
|
|
25
27
|
"visible": true,
|
|
26
28
|
"require_unchanged": true,
|
|
@@ -53,6 +55,7 @@
|
|
|
53
55
|
},
|
|
54
56
|
"assert-persisted": {
|
|
55
57
|
"action": "metamask.assets.set_token_visibility",
|
|
58
|
+
"asset_id": "{{params.asset_id}}",
|
|
56
59
|
"query": "{{params.query}}",
|
|
57
60
|
"visible": true,
|
|
58
61
|
"require_unchanged": true,
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
|
|
3
3
|
"title": "Assets release token search import",
|
|
4
|
-
"description": "
|
|
4
|
+
"description": "Requires a token that is not already enabled, imports it through live Manage tokens search, and proves it appears in the token list.",
|
|
5
5
|
"paramsSchema": {
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
8
|
"properties": {
|
|
9
9
|
"query": { "type": "string", "default": "DAI" },
|
|
10
|
+
"asset_id": { "type": "string", "default": "", "description": "Optional exact ERC-20 identity on Extension to disambiguate search results; leave empty for Mobile query-only selection." },
|
|
10
11
|
"timeout_ms": { "type": "integer", "minimum": 1000, "default": 45000 }
|
|
11
12
|
}
|
|
12
13
|
},
|
|
@@ -22,7 +23,9 @@
|
|
|
22
23
|
"import-token": {
|
|
23
24
|
"action": "metamask.assets.set_token_visibility",
|
|
24
25
|
"query": "{{params.query}}",
|
|
26
|
+
"asset_id": "{{params.asset_id}}",
|
|
25
27
|
"visible": true,
|
|
28
|
+
"require_change": true,
|
|
26
29
|
"timeout_ms": "{{params.timeout_ms}}",
|
|
27
30
|
"intent": "Find the token through the live API and enable its management toggle",
|
|
28
31
|
"next": "capture-imported-token"
|
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
"$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
|
|
3
3
|
"title": "Assets release token sorting",
|
|
4
4
|
"description": "Exercises and verifies both token-list sort modes through visible product controls.",
|
|
5
|
+
"paramsSchema": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"capture_path": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"default": "screenshots/assets-sorted.png",
|
|
12
|
+
"description": "Final sort-state capture; pass distinct paths when composing repeated checks."
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
},
|
|
5
16
|
"workflow": {
|
|
6
17
|
"entry": "open-wallet",
|
|
7
18
|
"nodes": {
|
|
@@ -18,8 +29,8 @@
|
|
|
18
29
|
},
|
|
19
30
|
"capture-sorted-list": {
|
|
20
31
|
"action": "ui.screenshot",
|
|
21
|
-
"path": "
|
|
22
|
-
"label": "
|
|
32
|
+
"path": "{{params.capture_path}}",
|
|
33
|
+
"label": "Final asset sort state",
|
|
23
34
|
"intent": "Preserve reviewer-visible sorted token list evidence",
|
|
24
35
|
"next": "done"
|
|
25
36
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
|
|
3
3
|
"title": "Swap release quote",
|
|
4
|
-
"description": "
|
|
4
|
+
"description": "Select an exact Swap pair, enter a fixed amount or use Max, and verify a reviewable destination quote without submitting. Extension quote identity requires exposed bridge quote state; an opaque build without it is not certified by UI amounts alone.",
|
|
5
5
|
"paramsSchema": {
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
@@ -14,7 +14,18 @@
|
|
|
14
14
|
"destination_query": { "type": "string", "default": "WETH" },
|
|
15
15
|
"source_asset_id": { "type": "string", "default": "" },
|
|
16
16
|
"destination_asset_id": { "type": "string", "default": "" },
|
|
17
|
+
"amount_mode": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"enum": ["fixed", "max"],
|
|
20
|
+
"default": "fixed",
|
|
21
|
+
"description": "Enter amount or use the visible Max balance. Neither branch submits a transaction."
|
|
22
|
+
},
|
|
17
23
|
"amount": { "type": "number", "minimum": 0.00000001, "default": 0.0001 },
|
|
24
|
+
"capture_path": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"default": "screenshots/swap-quote.png",
|
|
27
|
+
"description": "Use a distinct artifact path for each call when composing several quotes in one run."
|
|
28
|
+
},
|
|
18
29
|
"timeout_ms": { "type": "integer", "minimum": 1000, "default": 60000 }
|
|
19
30
|
}
|
|
20
31
|
},
|
|
@@ -40,7 +51,22 @@
|
|
|
40
51
|
"destination_asset_id": "{{params.destination_asset_id}}",
|
|
41
52
|
"timeout_ms": "{{params.timeout_ms}}",
|
|
42
53
|
"intent": "Select the exact source and destination assets",
|
|
43
|
-
"next": "
|
|
54
|
+
"next": "choose-amount"
|
|
55
|
+
},
|
|
56
|
+
"choose-amount": {
|
|
57
|
+
"action": "switch",
|
|
58
|
+
"value": "{{params.amount_mode}}",
|
|
59
|
+
"equals": "max",
|
|
60
|
+
"cases": { "match": "use-max" },
|
|
61
|
+
"default": "enter-amount",
|
|
62
|
+
"intent": "Run only the requested amount-entry method"
|
|
63
|
+
},
|
|
64
|
+
"use-max": {
|
|
65
|
+
"action": "metamask.swap_bridge.set_max_amount",
|
|
66
|
+
"wait_for_quote": true,
|
|
67
|
+
"timeout_ms": "{{params.timeout_ms}}",
|
|
68
|
+
"intent": "Use the visible available source balance and wait for its quote",
|
|
69
|
+
"next": "assert-quote"
|
|
44
70
|
},
|
|
45
71
|
"enter-amount": {
|
|
46
72
|
"action": "metamask.swap_bridge.set_amount",
|
|
@@ -62,7 +88,7 @@
|
|
|
62
88
|
},
|
|
63
89
|
"capture-quote": {
|
|
64
90
|
"action": "ui.screenshot",
|
|
65
|
-
"path": "
|
|
91
|
+
"path": "{{params.capture_path}}",
|
|
66
92
|
"label": "Swap release quote",
|
|
67
93
|
"intent": "Preserve reviewer-visible proof of the live quote",
|
|
68
94
|
"next": "done"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deeeed/metamask-harness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.48.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mm-harness": "bin/mm-harness"
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@farmslot/agent-runtime": "0.8.1",
|
|
24
|
-
"@farmslot/expo-recipe": "0.
|
|
24
|
+
"@farmslot/expo-recipe": "0.9.0",
|
|
25
25
|
"@farmslot/handoff": "^0.3.1",
|
|
26
26
|
"@farmslot/protocol": "0.21.0",
|
|
27
|
-
"@farmslot/recipe-harness": "0.
|
|
27
|
+
"@farmslot/recipe-harness": "0.16.0",
|
|
28
28
|
"agent-device": "0.19.3",
|
|
29
29
|
"commander": "^12.0.0",
|
|
30
30
|
"es-module-lexer": "2.3.1",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"topic": "recipes",
|
|
4
4
|
"title": "Recipe-backed work",
|
|
5
|
-
"summary": "
|
|
5
|
+
"summary": "The harness exposes tools to inspect, interact with, and measure MetaMask. Recipes combine them with starting conditions, branching, and assertions to produce repeatable evidence. Skills organize the task around that proof.",
|
|
6
6
|
"capabilities": "Recipe uses: functional QA, visual checks, analytics and log assertions, and performance measurements during development, PR review, or release testing.",
|
|
7
7
|
"scopeNotice": "Current coverage: mm-harness is heavily optimized for Perps. Other teams can use it by adding composable recipes for their key journeys; contact Arthur for help.",
|
|
8
8
|
"recipeProtocolVersion": "1",
|
|
@@ -12,8 +12,9 @@
|
|
|
12
12
|
"title": "Freeze the proof target",
|
|
13
13
|
"instruction": "Work inside one isolated MetaMask checkout. Name the exact product version and acceptance criteria, repair harness-owned state, then verify or launch that checkout.",
|
|
14
14
|
"details": [
|
|
15
|
-
"The tested build and current criteria are authoritative. A prior recipe pass is not current proof.",
|
|
16
|
-
"
|
|
15
|
+
"The tested build and current criteria are authoritative. A prior recipe pass is not current proof. Freeze source and installed dependencies during execution. Parallel workers need private dependency trees with matching manifests and lockfiles; installing through a shared node_modules symlink can change another run. Mobile catalog compatibility does not establish both iOS and Android runtime support; validate the selected transport.",
|
|
16
|
+
"Launching Extension preserves its wallet profile. Applying a fixture or resetting that profile is a separate explicit action. A session helper proves only its declared postcondition, not the selected account, provider, network, or funding; verify those separately before trading. Use account-aware balances: Hyperliquid unified accounts can use spot collateral even when clearinghouseState reports zero margin.",
|
|
17
|
+
"Choose state, visual, log, or performance evidence for each criterion before authoring the graph. A tool returning successfully proves only its own operation, not the task's claim."
|
|
17
18
|
],
|
|
18
19
|
"commands": [
|
|
19
20
|
"mm-harness doctor --fix",
|
|
@@ -25,13 +26,16 @@
|
|
|
25
26
|
"title": "Discover the strongest starting point",
|
|
26
27
|
"instruction": "Search the installed recipe and action catalogs, inspect exact schemas, and select the closest recipe before writing anything new.",
|
|
27
28
|
"details": [
|
|
28
|
-
"A bundled recipe, including a Core recipe, is strong composable vocabulary, not authority for every future product version.",
|
|
29
|
+
"A bundled recipe, including a Core recipe, is strong composable vocabulary, not authority for every future product version. Core actions use isolated controllers: pass account/network into each action instead of adding a no-op initialization node.",
|
|
29
30
|
"Prefer an existing recipe, then existing actions, then read-only CDP or controller inspection. Add shared vocabulary only after repeated need is proven.",
|
|
30
|
-
"Before declaring a capability unsupported, inspect both catalogs and the closest recipe's composition. A missing dedicated action may already be composed from UI actions. Read the returned schema before
|
|
31
|
+
"Before declaring a capability unsupported, inspect both catalogs and the closest recipe's composition. A missing dedicated action may already be composed from UI actions. Read the returned schema, examples, and result_cases before authoring; do not treat guessed field names as empty capabilities.",
|
|
32
|
+
"Register a team library with --library team=/path/to/library-root, the directory containing recipes/. Use discovered recipe names in call.ref; the library alias names its source, not a recipe-name prefix. Confirm --describe reports the intended source and platform variant, then plan the complete composition. Discovery alone does not prove that child actions or parameter contracts work on this adapter."
|
|
31
33
|
],
|
|
32
34
|
"commands": [
|
|
33
35
|
"mm-harness actions --json",
|
|
34
36
|
"mm-harness actions --action app.status --json",
|
|
37
|
+
"mm-harness actions --action ui.screenshot --json",
|
|
38
|
+
"mm-harness actions performance --json",
|
|
35
39
|
"mm-harness run --list --json",
|
|
36
40
|
"mm-harness run runner.smoke --describe --json"
|
|
37
41
|
]
|
|
@@ -42,18 +46,34 @@
|
|
|
42
46
|
"instruction": "Declare genuine caller choices once in paramsSchema and keep invariants required by the postcondition inside the recipe.",
|
|
43
47
|
"details": [
|
|
44
48
|
"Parameterize reusable differences such as market, account, provider, network, amount, side, or lifecycle mode.",
|
|
45
|
-
"
|
|
49
|
+
"Use {{params.name}} for declared inputs and {{outputs.nodeId.path}} for a preceding result. Whole-value templates preserve types; embedded templates become strings. Inspect the action's actual output before selecting a field.",
|
|
50
|
+
"CLI values are parsed as JSON when possible. For a numeric-looking string parameter, preserve JSON quotes, for example 'amount=\"15\"'; shell quoting 15 alone still produces a number.",
|
|
51
|
+
"Do not parameterize away safety, assertions, cleanup, or the meaning of success. Never depend on hidden wallet, account, provider, network, page, port, or device state.",
|
|
52
|
+
"A clean-market postcondition must check both sides. Keep side-specific open-state proof separate, and inspect the actual cancellation payload: filtering by side before canceling by symbol does not protect opposite-side orders. Map client receipt IDs to exchange IDs when they differ, checking account and market. Never infer ownership from any new order or widen cleanup to fix a failed assertion.",
|
|
53
|
+
"Asset symbols and test IDs may repeat across networks. Require an unambiguous observed row, or use a contract that accepts network/token identity; never infer chain, funding or ownership from a symbol alone.",
|
|
54
|
+
"For catalog changes, verify membership and lifecycle flags as well as counts. A smaller list can hide valid delisted entries still needed by callers; distinguish unavailable trading metadata from missing historical identity.",
|
|
55
|
+
"Defaults must agree across callers and children. Keep consent and environment-specific values required when no safe default exists; a missing required input is not recipe drift. The current runtime resolves referenced inputs before branching, so an unselected branch does not hide an undefined parameter. Venue-key registration needs explicit account/key-slot ownership too. Pass account and network explicitly to isolated Core actions rather than assuming a preceding node selected them."
|
|
46
56
|
],
|
|
47
57
|
"commands": []
|
|
48
58
|
},
|
|
49
59
|
{
|
|
50
60
|
"id": "compose",
|
|
51
61
|
"title": "Compose a readable graph",
|
|
52
|
-
"instruction": "Use static call references for unchanged sub-journeys.
|
|
62
|
+
"instruction": "Use static call references for unchanged sub-journeys. Separate preparation, action, assertions, and evidence; add workflow.teardown when cleanup must run after success or failure.",
|
|
53
63
|
"details": [
|
|
54
|
-
"Use idempotent ensure actions
|
|
64
|
+
"Starting conditions are ordinary nodes at the beginning of workflow, not a preconditions field. Use idempotent ensure actions and independently assert their postconditions. Reopening a route may preserve filters or search state; establish the required view explicitly before sampling, and validate composition after a different journey changes it.",
|
|
55
65
|
"Inspect existing state before creating or cleaning fixtures. Establish only prerequisites required by the claim; a history or display check may already have usable records and need no trade or cleanup. Record the selected records and verify they still satisfy the proof boundary.",
|
|
56
|
-
"
|
|
66
|
+
"Match history-query ranges to the dates of existing visible records before declaring fixtures missing. Sample only sections needed for change detection; fetch one-time corroborating history once, and count background calls as well as graph nodes.",
|
|
67
|
+
"Actions translate UI, CDP, or controller operations. Recipes compose them. Product code owns business rules. Teams own specialized journeys in their libraries; the harness retains shared capabilities and a small set of composable examples.",
|
|
68
|
+
"Put identical cross-adapter graphs in recipes/shared/<domain>/ and platform variants in recipes/<adapter>/<domain>/. Both keep the same domain.recipe name; an adapter variant takes precedence. Keep platform differences in actions when the journey is identical. Verify discovery, execution, and callers before removing duplicates.",
|
|
69
|
+
"Keep lifecycle transitions explicit. Check whether an observation tool activates or foregrounds the app; that behavior can invalidate a lifecycle claim even when the captured screen looks correct.",
|
|
70
|
+
"On source-dev Mobile, app.lifecycle launch, foreground, and restart already wait for the selected bridge and route. Use runtime_ready_timeout_ms to bound readiness and require_process_continuity for warm-resume claims. settle_ms is an explicit dwell period, not readiness proof. Unlock before a visual proof if the app protects locked-screen capture; never bypass that protection.",
|
|
71
|
+
"Measure executed nodes and elapsed time across nested calls, including setup and teardown. Include action-internal repetitions such as navigation cycles. Splitting a long graph into child files does not reduce work. Reuse a verified prerequisite within its valid scope, branch around already-satisfied setup, and keep broad coverage suites separate from the small journey a task needs.",
|
|
72
|
+
"Inspect scripts invoked by command nodes before running them. A read or stream label does not rule out signing, order placement or cleanup. Count the script's operations and verify its actual assertions; callback counts do not establish transport identity. Use a distinct output path for repeated invocations so one phase cannot overwrite another's evidence.",
|
|
73
|
+
"Merge branch endings when their resolved action inputs and assertions are identical. Preserve the scenario identity in parameters; keep distinct endings when required evidence differs. Compare the selected operation sequences before claiming fewer checks or faster execution.",
|
|
74
|
+
"Use summary.total for executed node count and summary.durationMs for engine duration. Measure CLI wall time separately when assessing speed; preparation and closeout can dominate a short recipe. The trace includes child nodes and their enclosing call; summing both durations double-counts the child work.",
|
|
75
|
+
"Keep repository builds, unit suites, and release bookkeeping outside reusable runtime journeys. Run them separately when required and report both results; a runtime pass does not imply package acceptance.",
|
|
76
|
+
"Main and teardown graphs are disjoint. A cleanup node reached only through next may be skipped on failure. Capture the baseline in a completed read before mutation, guard restoration on that output, and restore only task-owned state. A missing or ambiguous baseline is unknown, not false. A failed mutation may have changed the product without returning output. Teardown cannot guarantee provider acceptance or survive a killed process."
|
|
57
77
|
],
|
|
58
78
|
"commands": []
|
|
59
79
|
},
|
|
@@ -64,6 +84,7 @@
|
|
|
64
84
|
"details": [
|
|
65
85
|
"Plan after every structural change and preserve the first failing trace.",
|
|
66
86
|
"A plan validates structure only. Probe a reused setup boundary against the current build with a short explicit timeout before a full journey; a bundled journey timeout is not a discovery budget.",
|
|
87
|
+
"A later update cannot repair a failed initial-seed assertion. Preserve the initial sample and stop that wait instead of spending the remaining budget on a proof that cannot pass.",
|
|
67
88
|
"A CDP observation can help find the current route or selector; it is not final evidence unless the recipe records an allowed action and assertion for that signal."
|
|
68
89
|
],
|
|
69
90
|
"commands": [
|
|
@@ -74,12 +95,13 @@
|
|
|
74
95
|
{
|
|
75
96
|
"id": "branch",
|
|
76
97
|
"title": "Branch without hiding decisions",
|
|
77
|
-
"instruction": "
|
|
98
|
+
"instruction": "Branch on a declared input or a preceding observation. switch compares resolved strings with value and equals: cases.match handles equality and default handles the other path.",
|
|
78
99
|
"details": [
|
|
79
|
-
"
|
|
80
|
-
"
|
|
100
|
+
"For a runtime decision, value can use {{outputs.inspect.state}} when the preceding inspect node actually returns that string. Actions with result_cases can route directly through cases and default; the recipe supplies destinations, not the action.",
|
|
101
|
+
"Use either next or cases with default. call always uses next and a static ref; only call.params passes child inputs, and child output stays scoped under the call node.",
|
|
102
|
+
"Graphs are acyclic. Put bounded polling inside actions, not backward edges. Each success path must prove its promised outcome. end.status supports pass, fail, or unknown; an unknown result is not proof of success."
|
|
81
103
|
],
|
|
82
|
-
"commands": []
|
|
104
|
+
"commands": ["mm-harness actions --action switch --json"]
|
|
83
105
|
},
|
|
84
106
|
{
|
|
85
107
|
"id": "quality",
|
|
@@ -87,11 +109,19 @@
|
|
|
87
109
|
"instruction": "Run the complete graph once into an explicit artifact directory, then apply the built-in quality bar before assigning coverage or a verdict.",
|
|
88
110
|
"details": [
|
|
89
111
|
"Coverage: every criterion needs an executable path and assertion; manual, untestable, and environment-dependent gaps stay explicit.",
|
|
112
|
+
"Optional proofTargets names claims with id and claim; nodes link evidence with proves: [id]. Coverage labels do not replace assertions.",
|
|
90
113
|
"A blocked required runtime claim is incomplete proof. Passing static checks or closing a checklist cannot make that claim validated or fixed.",
|
|
91
114
|
"Graph: no unconditional pass, generic intent, hidden start state, or opaque node that collapses preparation, action, assertion, evidence, and teardown.",
|
|
92
|
-
"
|
|
115
|
+
"Assertion modes such as require_unchanged must fail before any corrective action. Verify the negative path leaves the inspected state untouched; an assertion must not repair the behavior it is supposed to test.",
|
|
116
|
+
"Reuse structured reads with assert_output instead of adding a new domain action for every comparison: source names the earlier node, and assert can select $.matchingCount with operator eq and the expected parameter. A status-report action can succeed while reporting missing prerequisites; assert required values before the dependent action. Checking three rows exist does not prove there are exactly three, or that they belong to this task.",
|
|
117
|
+
"Evidence: inspect the recipe resolution, summary, trace, manifest, logs, and actual visual evidence. A filename or passing node is not visual proof. A container's presence does not prove its data loaded; tab content does not prove its selection indicator. Verify the values and visible state required by the claim.",
|
|
118
|
+
"For a change claim, compare the actual before/after values for the same subject. Two captures alone prove neither an update nor freshness. Preserve unchanged observations as unproven rather than retrying to obtain a pass.",
|
|
119
|
+
"Match capture to the claim. Extension ui.capture_surface records the browser document through Chrome, including below-viewport document content but not hidden nested-scroll contents or native dialogs. Keep viewport assertions separate, inspect every image, and preserve failed captures rather than reconstructing them from HTML.",
|
|
120
|
+
"Screenshots: establish the claimed state, capture it, and inspect the original file. If a displayed preview looks incomplete, verify stored pixels or local OCR before reporting a capture defect or changing code. Preserve genuine failed captures. Performance: define a measured window, exclude setup, and compare equivalent builds, devices, and lifecycle conditions; action duration alone is not app latency.",
|
|
121
|
+
"Retain samples and declared limits when a measurement fails. For memory growth, compare an equal-duration idle control before attributing it to navigation; do not raise the budget to make the run pass.",
|
|
122
|
+
"Analytics: verify the collection destination and existing consent before capture. Changing participation or marketing consent requires operator approval and restoration of the prior settings; a recipe's hardcoded opt-in is not permission. Missing collection prerequisites are not missing product events.",
|
|
93
123
|
"Flake risk: wait on observable state instead of sleeping, keep device and runtime identity explicit, and never overwrite a prior run's artifacts.",
|
|
94
|
-
"Mutation: require an independent state assertion
|
|
124
|
+
"Mutation: require an independent state assertion and identity-bound receipt. Schedule authorized cleanup in teardown, verify its result, and report any residue. Testnet cleanup must not cancel another task's orders or close its positions."
|
|
95
125
|
],
|
|
96
126
|
"commands": [
|
|
97
127
|
"mm-harness run ./proof.recipe.json --artifacts-dir temp/recipe-proof --json",
|
|
@@ -102,18 +132,28 @@
|
|
|
102
132
|
{
|
|
103
133
|
"id": "drift",
|
|
104
134
|
"title": "Repair drift and promote reusable learning",
|
|
105
|
-
"instruction": "
|
|
135
|
+
"instruction": "Treat saved recipes as starting points, not compatibility guarantees. A failure starts diagnosis, not an automatic task stop. Preserve it, compare the current app with the task's acceptance criteria, and distinguish recipe drift from a product, harness, fixture, or live-dependency failure before repairing anything.",
|
|
106
136
|
"details": [
|
|
107
137
|
"The graph and evidence contract can be deterministic while the product, provider, account, or network outcome is not. Honest external rejection remains a valid result, not a reason to seek a green retry.",
|
|
108
|
-
"Trace the first failure through the exact action source and test the smallest causal explanation before filing or fixing a harness issue.
|
|
109
|
-
"For blocked runtime proof, retain the target identity, build/app state, and first failing command and error. Reconcile later status snapshots with that evidence; they cannot establish that an earlier device was absent.",
|
|
110
|
-
"
|
|
111
|
-
"
|
|
112
|
-
"
|
|
113
|
-
"
|
|
138
|
+
"Trace the first failure through the exact action source and test the smallest causal explanation before filing or fixing a harness issue. Verify the visible screen as well as the route name; an old route identifier may now open a different screen. If a proposed fix outgrows the confirmed cause, preserve it separately and return to the minimal fix.",
|
|
139
|
+
"For blocked runtime proof, retain the target identity, build/app state, and first failing command and error. Reconcile later status snapshots with that evidence; they cannot establish that an earlier device was absent. Cold XCTest infrastructure belongs in explicit setup: consult agent-device help prepare using the same package, device and state directory. A preparation health check does not prove native snapshot or interaction latency.",
|
|
140
|
+
"A failed device-discovery probe is unknown availability, not proof that a simulator stopped. Inspect deviceDiscoveryErrors or DEVICE_DISCOVERY_FAILED and run its Next command before changing targets or restarting a runtime.",
|
|
141
|
+
"An RPC failure is not necessarily a credential or network failure. Distinguish an HTTP rejection from JavaScript execution errors and circuit-breaker cooldowns. Check build capabilities separately from remote rollout flags; do not reset a wallet to repair an unclassified failure.",
|
|
142
|
+
"For LavaMoat execution errors, inspect the effective bundled policy as well as the calling code. A nested method grant can change receiver binding. Reproduce the mechanism in isolation, then validate the owning product correction with LavaMoat still enabled; never replace fetch in the live worker to manufacture proof.",
|
|
143
|
+
"Use LaunchDarkly for feature rollout. Do not add local flag overrides to unblock validation. Match compile-time capabilities to the intended product build; if its remote rollout excludes the feature, report that prerequisite or request targeting from its owner.",
|
|
144
|
+
"For confirmed product-version drift, copy the closest recipe into the task and retain its working nodes. Adapt only the stale selector, route, input contract, or setup condition against current source and observed UI. Keep the task's acceptance criteria; do not rebuild a working journey or relax its proof to obtain a pass.",
|
|
145
|
+
"Inspect selection before toggling; skip already-satisfied setup. Visibility and a matching locator do not prove click readiness: a menu can move between press and release. Require an uncovered, stable target within the action budget, click once, then assert the result. If it fails, inspect the actual pointer target before changing locators. No fixed sleeps or repeated clicks.",
|
|
146
|
+
"Use test values distinct from the product's defaults so resets are observable. If an intentional transition reseeds a form, assert its documented reset contract using a recorded baseline; do not claim preservation because the chosen value happened to equal the default.",
|
|
147
|
+
"A loaded form shell is not a settled quote. Recheck every required preview condition after polling expires; stopping the wait must never turn missing fields into success. Verify this with a real unavailable-preview case and retain the failure.",
|
|
148
|
+
"After changing quote inputs, bind the returned quote to those inputs. A debounced request may leave the previous positive output and enabled CTA visible briefly; neither proves the new quote is ready. Inspect the capture too, and preserve stale/loading evidence instead of refreshing invisibly.",
|
|
149
|
+
"Native trees may include controls covered by developer or system overlays. Verify the screenshot before changing a selector; move or dismiss an overlay only as explicit setup and restore it afterward. Gesture path and delta values are offsets from the resolved start, not absolute endpoints; inspect the resolved coordinates and assert the resulting state.",
|
|
150
|
+
"Reconcile any earlier side effects before repeating a step; an unknown order or transaction outcome must not be resubmitted. Probe the repaired node with call or a small recipe, re-plan, then validate the full task-local recipe and affected callers. Continue the task when the repaired path is proven; report a blocker only when no supported, authorized path remains.",
|
|
151
|
+
"Record the source recipe digest, tested product identity, repair, and fresh evidence. If the defect belongs to reusable harness code or bundled vocabulary, prepare a focused experimental-metamask-harness issue and open it when authorized. Put verified authoring lessons in version-matched harness help; keep task-specific evidence in the task and skills as thin workflow wrappers.",
|
|
152
|
+
"Promote a repair only after it works on the tested version and current main, or declare and enforce its compatibility boundary.",
|
|
153
|
+
"Authoring guide: https://farmslot.io/docs/guides/write-a-recipe/ ; canonical field and graph contract: https://farmslot.io/docs/reference/recipe-protocol-v1/"
|
|
114
154
|
],
|
|
115
155
|
"commands": []
|
|
116
156
|
}
|
|
117
157
|
],
|
|
118
|
-
"safety": "Recipe parameters never bypass mutation consent or live-funds gates. Preserve valid live financial rejections
|
|
158
|
+
"safety": "Recipe parameters never bypass mutation consent or live-funds gates. Preserve valid live financial rejections; never tune or retry merely to obtain a pass. Run required cleanup and report its actual outcome."
|
|
119
159
|
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
|
|
3
|
-
"title": "Assets release token hide and re-add",
|
|
4
|
-
"description": "Hides one imported token and re-enables it through Manage tokens, proving both home-list states.",
|
|
5
|
-
"paramsSchema": {
|
|
6
|
-
"type": "object",
|
|
7
|
-
"additionalProperties": false,
|
|
8
|
-
"properties": {
|
|
9
|
-
"query": { "type": "string", "default": "DAI" },
|
|
10
|
-
"timeout_ms": { "type": "integer", "minimum": 1000, "default": 45000 }
|
|
11
|
-
}
|
|
12
|
-
},
|
|
13
|
-
"workflow": {
|
|
14
|
-
"entry": "open-wallet",
|
|
15
|
-
"nodes": {
|
|
16
|
-
"open-wallet": {
|
|
17
|
-
"action": "ui.navigate",
|
|
18
|
-
"page": "home",
|
|
19
|
-
"intent": "Open the all-network asset list",
|
|
20
|
-
"next": "hide-token"
|
|
21
|
-
},
|
|
22
|
-
"hide-token": {
|
|
23
|
-
"action": "metamask.assets.set_token_visibility",
|
|
24
|
-
"query": "{{params.query}}",
|
|
25
|
-
"visible": false,
|
|
26
|
-
"require_change": true,
|
|
27
|
-
"timeout_ms": "{{params.timeout_ms}}",
|
|
28
|
-
"intent": "Disable the imported token and prove its row disappears",
|
|
29
|
-
"next": "capture-hidden"
|
|
30
|
-
},
|
|
31
|
-
"capture-hidden": {
|
|
32
|
-
"action": "ui.screenshot",
|
|
33
|
-
"path": "screenshots/assets-token-hidden.png",
|
|
34
|
-
"label": "Token hidden",
|
|
35
|
-
"intent": "Preserve reviewer-visible hidden state",
|
|
36
|
-
"next": "readd-token"
|
|
37
|
-
},
|
|
38
|
-
"readd-token": {
|
|
39
|
-
"action": "metamask.assets.set_token_visibility",
|
|
40
|
-
"query": "{{params.query}}",
|
|
41
|
-
"visible": true,
|
|
42
|
-
"require_change": true,
|
|
43
|
-
"timeout_ms": "{{params.timeout_ms}}",
|
|
44
|
-
"intent": "Re-enable the token and prove its metadata row returns",
|
|
45
|
-
"next": "capture-restored"
|
|
46
|
-
},
|
|
47
|
-
"capture-restored": {
|
|
48
|
-
"action": "ui.screenshot",
|
|
49
|
-
"path": "screenshots/assets-token-restored.png",
|
|
50
|
-
"label": "Token restored",
|
|
51
|
-
"intent": "Preserve reviewer-visible restored state",
|
|
52
|
-
"next": "done"
|
|
53
|
-
},
|
|
54
|
-
"done": { "action": "end", "status": "pass" }
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|