@deeeed/metamask-harness 0.34.1 → 0.34.3
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 +24 -0
- package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +222 -0
- package/adapters/mobile/bridge-runtime/console-forwarder.cjs +22 -1
- package/adapters/mobile/bridge-runtime/lib/ws-client.cjs +18 -0
- package/adapters/mobile/open-device.sh +6 -4
- package/dist/adapters/mobile/prepare.js +1 -4
- package/dist/adapters.js +139 -8
- package/dist/commands/launch/mobile.js +16 -2
- package/dist/live-adapter-contract.js +6 -6
- package/dist/recipe-security.js +3 -0
- package/dist/runner.js +53 -8
- package/dist/runtime-context.js +1 -0
- package/library/actions/core/perps/_controller.mjs +75 -23
- package/library/actions/core/perps/read_account.mjs +6 -12
- package/library/actions/core/perps/read_snapshot.mjs +66 -0
- package/library/actions/extension/performance/_navigation-memory.mjs +372 -0
- package/library/actions/extension/performance/compare_idle_navigation_memory.mjs +18 -0
- package/library/actions/extension/performance/measure_detached_dom.mjs +18 -0
- package/library/actions/extension/performance/measure_navigation_memory.mjs +18 -0
- package/library/actions/mobile/app/network-control.mjs +24 -12
- package/library/actions/mobile/perps/measure_homepage_visible.mjs +4 -0
- package/library/actions/mobile/perps/performance-capture.mjs +506 -120
- package/library/actions/mobile/perps/perps.mjs +221 -38
- package/library/actions/mobile/perps/prepare_local_snapshot_endpoint.mjs +58 -0
- package/library/actions/mobile/platform/bridge.mjs +38 -5
- package/library/actions/mobile/wallet/ensure_unlocked.mjs +5 -1
- package/library/manifests/core.action-manifest.json +55 -0
- package/library/manifests/extension.action-manifest.json +100 -0
- package/library/manifests/mobile.action-manifest.json +226 -512
- package/library/recipes/core/perps/snapshot.recipe.json +25 -0
- package/library/recipes/extension/performance/navigation-memory.recipe.json +175 -0
- package/library/recipes/mobile/perps/performance.homepage.cold-position-sample.recipe.json +120 -0
- package/library/recipes/mobile/perps/performance.homepage.ios-background-reconnect.recipe.json +7 -6
- package/library/recipes/mobile/perps/performance.homepage.ios-cold-no-cache.recipe.json +5 -5
- package/package.json +1 -1
- package/scripts/site-contrast.mjs +39 -3
- package/site/architecture.html +22 -16
- package/site/assets/progress.mjs +1 -1
- package/site/assets/style.css +113 -1
- package/site/cheatsheet.html +14 -12
- package/site/how-it-works.html +693 -0
- package/site/index.html +70 -640
- package/site/perps.html +7 -6
- package/site/recipes.html +23 -17
- package/site/reviewers.html +7 -6
- package/site/tutorials/index.html +7 -6
- package/site/tutorials/v1.html +12 -11
- package/site/tutorials/v2.html +7 -6
- package/site/tutorials/v3.html +15 -11
- package/site/tutorials/v4.html +6 -5
- package/site/tutorials/v5.html +6 -5
- package/site/tutorials/v6.html +6 -5
- package/site/tutorials/v7.html +6 -5
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
|
|
3
|
+
"title": "MetaMask core Perps snapshots",
|
|
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
|
+
"workflow": {
|
|
6
|
+
"entry": "status",
|
|
7
|
+
"nodes": {
|
|
8
|
+
"status": {
|
|
9
|
+
"action": "app.status",
|
|
10
|
+
"next": "read_snapshot",
|
|
11
|
+
"intent": "Resolve the Core checkout and report headless compatibility"
|
|
12
|
+
},
|
|
13
|
+
"read_snapshot": {
|
|
14
|
+
"action": "metamask.perps.read_snapshot",
|
|
15
|
+
"scope": "all",
|
|
16
|
+
"next": "done",
|
|
17
|
+
"intent": "Read coherent market and account snapshots through public controller methods"
|
|
18
|
+
},
|
|
19
|
+
"done": {
|
|
20
|
+
"action": "end",
|
|
21
|
+
"status": "pass"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
|
|
3
|
+
"title": "Extension navigation retained-memory audit",
|
|
4
|
+
"description": "Measures post-GC retained nodes and listeners across eight representative Extension screens, verifies detached DOM does not grow, and separates navigation-attributable listener growth from an equal-duration idle control.",
|
|
5
|
+
"proofTargets": [
|
|
6
|
+
{
|
|
7
|
+
"id": "screen-slopes",
|
|
8
|
+
"claim": "Eight representative Extension screens remain within separate post-GC retained-node and listener growth bounds during navigation churn."
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"id": "detached-dom",
|
|
12
|
+
"claim": "Detached DOM retained after forced garbage collection does not grow beyond the declared bound."
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"id": "idle-control",
|
|
16
|
+
"claim": "Navigation-attributable listener growth remains within its declared bound after subtracting an equal-duration idle control."
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"workflow": {
|
|
20
|
+
"entry": "require-cdp",
|
|
21
|
+
"nodes": {
|
|
22
|
+
"require-cdp": {
|
|
23
|
+
"action": "cdp.target",
|
|
24
|
+
"required": true,
|
|
25
|
+
"require_reachable": true,
|
|
26
|
+
"intent": "Confirm the live Extension CDP target is reachable before measuring retained memory.",
|
|
27
|
+
"next": "ensure-unlocked"
|
|
28
|
+
},
|
|
29
|
+
"ensure-unlocked": {
|
|
30
|
+
"action": "metamask.wallet.ensure_unlocked",
|
|
31
|
+
"intent": "Ensure the representative screens are reachable from an unlocked wallet.",
|
|
32
|
+
"next": "market-detail"
|
|
33
|
+
},
|
|
34
|
+
"market-detail": {
|
|
35
|
+
"action": "metamask.performance.measure_navigation_memory",
|
|
36
|
+
"screen_hash": "#/perps/market/ETH",
|
|
37
|
+
"screen_test_id": "perps-market-detail-page",
|
|
38
|
+
"return_hash": "#/",
|
|
39
|
+
"cycles": 8,
|
|
40
|
+
"timeout_ms": 400000,
|
|
41
|
+
"max_nodes_per_cycle": 2,
|
|
42
|
+
"max_listeners_per_cycle": 4,
|
|
43
|
+
"intent": "Measure the post-GC navigation slope for Perps market detail.",
|
|
44
|
+
"proves": ["screen-slopes"],
|
|
45
|
+
"next": "order-entry"
|
|
46
|
+
},
|
|
47
|
+
"order-entry": {
|
|
48
|
+
"action": "metamask.performance.measure_navigation_memory",
|
|
49
|
+
"screen_hash": "#/perps/trade/ETH?direction=long&mode=new",
|
|
50
|
+
"screen_test_id": "perps-order-entry-page",
|
|
51
|
+
"return_hash": "#/",
|
|
52
|
+
"cycles": 6,
|
|
53
|
+
"timeout_ms": 400000,
|
|
54
|
+
"max_nodes_per_cycle": 2,
|
|
55
|
+
"max_listeners_per_cycle": 4,
|
|
56
|
+
"intent": "Measure the post-GC navigation slope for Perps order entry.",
|
|
57
|
+
"proves": ["screen-slopes"],
|
|
58
|
+
"next": "perps-home"
|
|
59
|
+
},
|
|
60
|
+
"perps-home": {
|
|
61
|
+
"action": "metamask.performance.measure_navigation_memory",
|
|
62
|
+
"screen_hash": "#/perps-home",
|
|
63
|
+
"screen_test_id": "perps-home-page",
|
|
64
|
+
"return_hash": "#/",
|
|
65
|
+
"cycles": 6,
|
|
66
|
+
"timeout_ms": 400000,
|
|
67
|
+
"max_nodes_per_cycle": 2,
|
|
68
|
+
"max_listeners_per_cycle": 4,
|
|
69
|
+
"intent": "Measure the post-GC navigation slope for Perps home.",
|
|
70
|
+
"proves": ["screen-slopes"],
|
|
71
|
+
"next": "market-list"
|
|
72
|
+
},
|
|
73
|
+
"market-list": {
|
|
74
|
+
"action": "metamask.performance.measure_navigation_memory",
|
|
75
|
+
"screen_hash": "#/perps/market-list",
|
|
76
|
+
"screen_test_id": "market-list-view",
|
|
77
|
+
"return_hash": "#/",
|
|
78
|
+
"cycles": 6,
|
|
79
|
+
"timeout_ms": 400000,
|
|
80
|
+
"max_nodes_per_cycle": 2,
|
|
81
|
+
"max_listeners_per_cycle": 4,
|
|
82
|
+
"intent": "Measure the post-GC navigation slope for the Perps market list.",
|
|
83
|
+
"proves": ["screen-slopes"],
|
|
84
|
+
"next": "perps-activity"
|
|
85
|
+
},
|
|
86
|
+
"perps-activity": {
|
|
87
|
+
"action": "metamask.performance.measure_navigation_memory",
|
|
88
|
+
"screen_hash": "#/perps/activity",
|
|
89
|
+
"screen_test_id": "perps-activity-page",
|
|
90
|
+
"return_hash": "#/",
|
|
91
|
+
"cycles": 6,
|
|
92
|
+
"timeout_ms": 400000,
|
|
93
|
+
"max_nodes_per_cycle": 2,
|
|
94
|
+
"max_listeners_per_cycle": 4,
|
|
95
|
+
"intent": "Measure the post-GC navigation slope for Perps activity.",
|
|
96
|
+
"proves": ["screen-slopes"],
|
|
97
|
+
"next": "asset-details"
|
|
98
|
+
},
|
|
99
|
+
"asset-details": {
|
|
100
|
+
"action": "metamask.performance.measure_navigation_memory",
|
|
101
|
+
"screen_hash": "#/asset/0x1/",
|
|
102
|
+
"screen_test_id": "asset-price-chart",
|
|
103
|
+
"return_hash": "#/",
|
|
104
|
+
"cycles": 6,
|
|
105
|
+
"timeout_ms": 400000,
|
|
106
|
+
"max_nodes_per_cycle": 2,
|
|
107
|
+
"max_listeners_per_cycle": 4,
|
|
108
|
+
"intent": "Measure the post-GC navigation slope for asset details.",
|
|
109
|
+
"proves": ["screen-slopes"],
|
|
110
|
+
"next": "swaps-bridge"
|
|
111
|
+
},
|
|
112
|
+
"swaps-bridge": {
|
|
113
|
+
"action": "metamask.performance.measure_navigation_memory",
|
|
114
|
+
"screen_hash": "#/cross-chain/swaps/prepare-bridge-page",
|
|
115
|
+
"screen_test_id": "bridge-source-button",
|
|
116
|
+
"return_hash": "#/",
|
|
117
|
+
"cycles": 6,
|
|
118
|
+
"timeout_ms": 400000,
|
|
119
|
+
"max_nodes_per_cycle": 2,
|
|
120
|
+
"max_listeners_per_cycle": 4,
|
|
121
|
+
"intent": "Measure the post-GC navigation slope for Swaps and Bridge.",
|
|
122
|
+
"proves": ["screen-slopes"],
|
|
123
|
+
"next": "settings-control"
|
|
124
|
+
},
|
|
125
|
+
"settings-control": {
|
|
126
|
+
"action": "metamask.performance.measure_navigation_memory",
|
|
127
|
+
"screen_hash": "#/settings",
|
|
128
|
+
"screen_test_id": "settings-tab-bar-grouped",
|
|
129
|
+
"return_hash": "#/",
|
|
130
|
+
"cycles": 6,
|
|
131
|
+
"timeout_ms": 400000,
|
|
132
|
+
"max_nodes_per_cycle": 2,
|
|
133
|
+
"max_listeners_per_cycle": 4,
|
|
134
|
+
"intent": "Measure a non-Perps control screen with the same retained-memory gate.",
|
|
135
|
+
"proves": ["screen-slopes"],
|
|
136
|
+
"next": "detached-market-detail"
|
|
137
|
+
},
|
|
138
|
+
"detached-market-detail": {
|
|
139
|
+
"action": "metamask.performance.measure_detached_dom",
|
|
140
|
+
"screen_hash": "#/perps/market/ETH",
|
|
141
|
+
"screen_test_id": "perps-market-detail-page",
|
|
142
|
+
"return_hash": "#/",
|
|
143
|
+
"cycles": 5,
|
|
144
|
+
"timeout_ms": 400000,
|
|
145
|
+
"max_detached_growth": 50,
|
|
146
|
+
"intent": "Compare retained detached DOM before and after market-detail navigation churn.",
|
|
147
|
+
"proves": ["detached-dom"],
|
|
148
|
+
"next": "idle-control-market-detail"
|
|
149
|
+
},
|
|
150
|
+
"idle-control-market-detail": {
|
|
151
|
+
"action": "metamask.performance.compare_idle_navigation_memory",
|
|
152
|
+
"screen_hash": "#/perps/market/ETH",
|
|
153
|
+
"screen_test_id": "perps-market-detail-page",
|
|
154
|
+
"return_hash": "#/",
|
|
155
|
+
"rounds": 8,
|
|
156
|
+
"round_duration_ms": 10000,
|
|
157
|
+
"timeout_ms": 400000,
|
|
158
|
+
"max_attributable_listeners_per_cycle": 2,
|
|
159
|
+
"intent": "Subtract equal-duration idle growth from the market-detail navigation slope.",
|
|
160
|
+
"proves": ["idle-control"],
|
|
161
|
+
"next": "return-home"
|
|
162
|
+
},
|
|
163
|
+
"return-home": {
|
|
164
|
+
"action": "ui.navigate",
|
|
165
|
+
"page": "home",
|
|
166
|
+
"intent": "Leave the Extension on a known screen after the audit.",
|
|
167
|
+
"next": "done"
|
|
168
|
+
},
|
|
169
|
+
"done": {
|
|
170
|
+
"action": "end",
|
|
171
|
+
"status": "pass"
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
|
|
3
|
+
"title": "Homepage Perps cold no-cache position sample",
|
|
4
|
+
"description": "Clears only persisted Perps performance caches, reproduces a process-cold Homepage position, and measures production-like Perps timing from the first usable Wallet Home frame with the device performance clock. Metro, splash, bridge readiness, unlock, and recipe orchestration are excluded from the Perps metric.",
|
|
5
|
+
"paramsSchema": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"account": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "Existing fixture account that owns the position."
|
|
12
|
+
},
|
|
13
|
+
"market": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"default": "BTC",
|
|
16
|
+
"description": "Exact visible position market symbol."
|
|
17
|
+
},
|
|
18
|
+
"sample_id": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"default": "sample",
|
|
21
|
+
"description": "Artifact-local sample identifier."
|
|
22
|
+
},
|
|
23
|
+
"snapshot_port": {
|
|
24
|
+
"type": "number",
|
|
25
|
+
"default": 3000,
|
|
26
|
+
"description": "Host port for the explicitly configured local Terminal global snapshot endpoint."
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"required": ["account"]
|
|
30
|
+
},
|
|
31
|
+
"workflow": {
|
|
32
|
+
"entry": "ensure-wallet",
|
|
33
|
+
"nodes": {
|
|
34
|
+
"ensure-wallet": {
|
|
35
|
+
"action": "metamask.wallet.ensure_unlocked",
|
|
36
|
+
"target_timeout_ms": 90000,
|
|
37
|
+
"unlock_timeout_ms": 180000,
|
|
38
|
+
"intent": "Reuse the existing unlocked fixture wallet without replacing it",
|
|
39
|
+
"next": "select-account"
|
|
40
|
+
},
|
|
41
|
+
"select-account": {
|
|
42
|
+
"action": "metamask.wallet.select_account",
|
|
43
|
+
"name": "{{params.account}}",
|
|
44
|
+
"cdp_timeout_ms": 30000,
|
|
45
|
+
"intent": "Pin the device-owned fixture account before the measured cold restart",
|
|
46
|
+
"next": "assert-position"
|
|
47
|
+
},
|
|
48
|
+
"assert-position": {
|
|
49
|
+
"action": "metamask.perps.assert_positions",
|
|
50
|
+
"market": "{{params.market}}",
|
|
51
|
+
"state": "open",
|
|
52
|
+
"timeout_ms": 30000,
|
|
53
|
+
"intent": "Prove the real position exists before process termination",
|
|
54
|
+
"next": "open-wallet-home"
|
|
55
|
+
},
|
|
56
|
+
"open-wallet-home": {
|
|
57
|
+
"action": "ui.navigate",
|
|
58
|
+
"page": "home",
|
|
59
|
+
"timeout_ms": 30000,
|
|
60
|
+
"intent": "Persist Wallet Home as the route restored by the cold launch",
|
|
61
|
+
"next": "clear-perps-caches"
|
|
62
|
+
},
|
|
63
|
+
"clear-perps-caches": {
|
|
64
|
+
"action": "metamask.perps.clear_performance_caches",
|
|
65
|
+
"intent": "Remove only persisted Perps market and user snapshots before the measured cold launch",
|
|
66
|
+
"next": "prepare-local-snapshot-endpoint"
|
|
67
|
+
},
|
|
68
|
+
"prepare-local-snapshot-endpoint": {
|
|
69
|
+
"action": "metamask.perps.prepare_local_snapshot_endpoint",
|
|
70
|
+
"port": "{{params.snapshot_port}}",
|
|
71
|
+
"intent": "Make the configured local Terminal v2 endpoint reachable on physical Android without an external command node",
|
|
72
|
+
"next": "cold-restart"
|
|
73
|
+
},
|
|
74
|
+
"cold-restart": {
|
|
75
|
+
"action": "app.lifecycle",
|
|
76
|
+
"command": "restart",
|
|
77
|
+
"settle_ms": 0,
|
|
78
|
+
"runtime_ready_timeout_ms": 90000,
|
|
79
|
+
"timeout_ms": 120000,
|
|
80
|
+
"intent": "Create a process-cold app state without rebuilding or resetting wallet state; this node is setup and is not part of the Perps metric",
|
|
81
|
+
"next": "measure-visible-perps"
|
|
82
|
+
},
|
|
83
|
+
"measure-visible-perps": {
|
|
84
|
+
"action": "metamask.perps.measure_homepage_visible",
|
|
85
|
+
"target_timeout_ms": 90000,
|
|
86
|
+
"unlock_timeout_ms": 180000,
|
|
87
|
+
"scroll_test_id": "wallet-screen",
|
|
88
|
+
"from_test_id": "wallet-screen",
|
|
89
|
+
"offset": 600,
|
|
90
|
+
"poll_interval_ms": 1000,
|
|
91
|
+
"visible_event_stage": "first_visible_recorded",
|
|
92
|
+
"visible_event_grace_ms": 1500,
|
|
93
|
+
"animated": true,
|
|
94
|
+
"target_test_id": "homepage-section-title-perps",
|
|
95
|
+
"required_present_test_id": "perps-position-row-{{params.market}}",
|
|
96
|
+
"timeout_ms": 30000,
|
|
97
|
+
"intent": "Confirm the restored wallet is usable and immediately measure the direct-CDP transition to visible Perps position content without returning control to the recipe runner",
|
|
98
|
+
"next": "wait-position"
|
|
99
|
+
},
|
|
100
|
+
"wait-position": {
|
|
101
|
+
"action": "ui.wait_for",
|
|
102
|
+
"test_id": "perps-position-row-{{params.market}}",
|
|
103
|
+
"expected": "present",
|
|
104
|
+
"timeout_ms": 60000,
|
|
105
|
+
"intent": "Prove the real market position row remains rendered after the measured Perps transition",
|
|
106
|
+
"next": "capture-position"
|
|
107
|
+
},
|
|
108
|
+
"capture-position": {
|
|
109
|
+
"action": "ui.screenshot",
|
|
110
|
+
"path": "screenshots/cold-position-{{params.sample_id}}.png",
|
|
111
|
+
"intent": "Preserve native proof that meaningful Perps content is visibly rendered after the fixed viewport demand",
|
|
112
|
+
"next": "done"
|
|
113
|
+
},
|
|
114
|
+
"done": {
|
|
115
|
+
"action": "end",
|
|
116
|
+
"status": "pass"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
package/library/recipes/mobile/perps/performance.homepage.ios-background-reconnect.recipe.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
|
|
3
3
|
"title": "iOS Homepage Perps long-background reconnect proof",
|
|
4
|
-
"description": "Keeps the existing
|
|
4
|
+
"description": "Keeps the existing simulator wallet and position, backgrounds longer than the connection grace period, then validates resident-to-fresh position content at the visible Homepage endpoint.",
|
|
5
5
|
"workflow": {
|
|
6
6
|
"entry": "ensure-wallet",
|
|
7
7
|
"nodes": {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"wait-perps-before": {
|
|
21
21
|
"action": "ui.wait_for",
|
|
22
|
-
"test_id": "homepage-
|
|
22
|
+
"test_id": "homepage-perps-positions",
|
|
23
23
|
"expected": "present",
|
|
24
24
|
"intent": "Require the Perps section before backgrounding",
|
|
25
25
|
"timeout_ms": 30000,
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"show-perps-before": {
|
|
29
29
|
"action": "ui.scroll",
|
|
30
|
-
"test_id": "homepage-
|
|
30
|
+
"test_id": "homepage-perps-positions",
|
|
31
31
|
"scroll_into_view": true,
|
|
32
32
|
"animated": true,
|
|
33
33
|
"settle": true,
|
|
@@ -53,12 +53,13 @@
|
|
|
53
53
|
"command": "foreground",
|
|
54
54
|
"settle_ms": 10000,
|
|
55
55
|
"runtime_ready_timeout_ms": 60000,
|
|
56
|
+
"require_process_continuity": true,
|
|
56
57
|
"intent": "Restore the same process and require a reconnect",
|
|
57
58
|
"next": "wait-perps-after"
|
|
58
59
|
},
|
|
59
60
|
"wait-perps-after": {
|
|
60
61
|
"action": "ui.wait_for",
|
|
61
|
-
"test_id": "homepage-
|
|
62
|
+
"test_id": "homepage-perps-positions",
|
|
62
63
|
"expected": "present",
|
|
63
64
|
"intent": "Confirm the Perps section remains available after reconnect",
|
|
64
65
|
"timeout_ms": 30000,
|
|
@@ -66,7 +67,7 @@
|
|
|
66
67
|
},
|
|
67
68
|
"show-perps-after": {
|
|
68
69
|
"action": "ui.scroll",
|
|
69
|
-
"test_id": "homepage-
|
|
70
|
+
"test_id": "homepage-perps-positions",
|
|
70
71
|
"scroll_into_view": true,
|
|
71
72
|
"animated": true,
|
|
72
73
|
"settle": true,
|
|
@@ -97,7 +98,7 @@
|
|
|
97
98
|
],
|
|
98
99
|
"required_lifecycles": ["background_reconnect"],
|
|
99
100
|
"required_sources": ["resident_state", "fresh_socket"],
|
|
100
|
-
"required_fresh_content_variants": ["
|
|
101
|
+
"required_fresh_content_variants": ["positions"],
|
|
101
102
|
"intent": "Require the long-background visible reconnect cohort",
|
|
102
103
|
"next": "done"
|
|
103
104
|
},
|
|
@@ -43,15 +43,15 @@
|
|
|
43
43
|
},
|
|
44
44
|
"wait-perps-section": {
|
|
45
45
|
"action": "ui.wait_for",
|
|
46
|
-
"test_id": "homepage-
|
|
46
|
+
"test_id": "homepage-perps-positions",
|
|
47
47
|
"expected": "present",
|
|
48
|
-
"intent": "Require the Perps
|
|
48
|
+
"intent": "Require the selected account's Perps position after cold launch",
|
|
49
49
|
"timeout_ms": 30000,
|
|
50
50
|
"next": "show-perps-section"
|
|
51
51
|
},
|
|
52
52
|
"show-perps-section": {
|
|
53
53
|
"action": "ui.scroll",
|
|
54
|
-
"test_id": "homepage-
|
|
54
|
+
"test_id": "homepage-perps-positions",
|
|
55
55
|
"scroll_into_view": true,
|
|
56
56
|
"animated": true,
|
|
57
57
|
"settle": true,
|
|
@@ -82,8 +82,8 @@
|
|
|
82
82
|
],
|
|
83
83
|
"required_lifecycles": ["cold_no_cache"],
|
|
84
84
|
"required_sources": ["fresh_socket"],
|
|
85
|
-
"required_fresh_content_variants": ["
|
|
86
|
-
"intent": "Require cold-no-cache lifecycle and fresh visible
|
|
85
|
+
"required_fresh_content_variants": ["positions"],
|
|
86
|
+
"intent": "Require cold-no-cache lifecycle and the fresh visible position",
|
|
87
87
|
"next": "done"
|
|
88
88
|
},
|
|
89
89
|
"done": {
|
package/package.json
CHANGED
|
@@ -351,11 +351,33 @@ async function checkBehavior(browserEndpoint, port, pages) {
|
|
|
351
351
|
return window.__siteCopied;
|
|
352
352
|
})()
|
|
353
353
|
`);
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
354
|
+
// Both halves of the onboarding have to survive a copy: set up the harness
|
|
355
|
+
// and the skills, then drive real work through one.
|
|
356
|
+
const expected = ['npm i -g @deeeed/metamask-harness@latest', 'yarn skills', '/mms-recipe-cook'];
|
|
357
|
+
const missing = expected.filter((line) => !copied?.includes(line));
|
|
358
|
+
if (missing.length) {
|
|
359
|
+
throw new Error(`copy prompt omitted ${missing.map((m) => `"${m}"`).join(', ')}`);
|
|
357
360
|
}
|
|
358
361
|
|
|
362
|
+
// The landing page is the lobby: one prompt, one way onward, nothing else.
|
|
363
|
+
const lobby = await page.evaluate(String.raw`
|
|
364
|
+
(() => ({
|
|
365
|
+
nav: document.querySelectorAll('.nav a').length,
|
|
366
|
+
steps: document.querySelectorAll('.step[data-step]').length,
|
|
367
|
+
depth: document.querySelector('a[href="how-it-works.html"]') !== null,
|
|
368
|
+
anchor: document.getElementById('prompt') !== null,
|
|
369
|
+
}))()
|
|
370
|
+
`);
|
|
371
|
+
if (lobby.nav || lobby.steps || !lobby.depth || !lobby.anchor) {
|
|
372
|
+
throw new Error(
|
|
373
|
+
`landing page is not a lobby: ${lobby.nav} nav links, ${lobby.steps} steps, ` +
|
|
374
|
+
`depth link ${lobby.depth}, prompt anchor ${lobby.anchor}`,
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
// The walkthrough and its saved progress live one click deeper.
|
|
380
|
+
await withPage(browserEndpoint, `${base}how-it-works.html`, async (page) => {
|
|
359
381
|
const progress = await page.evaluate(String.raw`
|
|
360
382
|
(() => {
|
|
361
383
|
const box = document.querySelector('.step-check');
|
|
@@ -426,6 +448,20 @@ async function checkBehavior(browserEndpoint, port, pages) {
|
|
|
426
448
|
if (broken.length) {
|
|
427
449
|
throw new Error(`${relative} has broken internal links: ${broken.join(', ')}`);
|
|
428
450
|
}
|
|
451
|
+
// Every page past the lobby owes the reader one click back to the prompt.
|
|
452
|
+
if (relative !== 'index.html') {
|
|
453
|
+
const back = await page.evaluate(String.raw`
|
|
454
|
+
(() => {
|
|
455
|
+
const link = document.querySelector('.nav a.nav-cta');
|
|
456
|
+
if (!link) return null;
|
|
457
|
+
const url = new URL(link.getAttribute('href'), location.href);
|
|
458
|
+
return url.pathname + url.hash;
|
|
459
|
+
})()
|
|
460
|
+
`);
|
|
461
|
+
if (back !== '/index.html#prompt') {
|
|
462
|
+
throw new Error(`${relative} has no nav link back to the setup prompt (found ${back})`);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
429
465
|
for (const width of [834, 390]) {
|
|
430
466
|
await page.send('Emulation.setDeviceMetricsOverride', {
|
|
431
467
|
width,
|
package/site/architecture.html
CHANGED
|
@@ -14,11 +14,12 @@
|
|
|
14
14
|
<div class="wrap topbar-inner">
|
|
15
15
|
<a class="brand" href="index.html">
|
|
16
16
|
<span class="brand-mark" aria-hidden="true"></span>
|
|
17
|
-
<span class="brand-name">
|
|
17
|
+
<span class="brand-name">recipes</span>
|
|
18
18
|
</a>
|
|
19
19
|
<nav class="nav" aria-label="Main">
|
|
20
|
-
<a href="index.html">
|
|
21
|
-
<a href="
|
|
20
|
+
<a class="nav-cta" href="index.html#prompt">Quick start</a>
|
|
21
|
+
<a href="how-it-works.html">How it works</a>
|
|
22
|
+
<a href="recipes.html">Recipe anatomy</a>
|
|
22
23
|
<a href="perps.html">Perps</a>
|
|
23
24
|
<a href="cheatsheet.html">Cheatsheet</a>
|
|
24
25
|
<a href="architecture.html" aria-current="page">Architecture</a>
|
|
@@ -30,11 +31,12 @@
|
|
|
30
31
|
|
|
31
32
|
<main>
|
|
32
33
|
<section class="wrap hero" style="padding-bottom:1rem">
|
|
33
|
-
<span class="eyebrow">The stack</span>
|
|
34
|
+
<span class="eyebrow">The stack behind a recipe</span>
|
|
34
35
|
<h1>How the pieces fit together</h1>
|
|
35
36
|
<p class="lede">
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
The recipe is the unit of trust; the rest of the stack runs it. Four moving parts, two of which
|
|
38
|
+
people routinely confuse. What each layer is, where it lives, which workflow you are in, and the
|
|
39
|
+
mistakes that cost newcomers their first afternoon.
|
|
38
40
|
</p>
|
|
39
41
|
</section>
|
|
40
42
|
|
|
@@ -45,8 +47,9 @@
|
|
|
45
47
|
<span class="k">A recipe proves a task with actions.</span> Recipes are JSON graphs of typed
|
|
46
48
|
actions; <code>mm-harness</code> executes them against a real app and produces an evidence bundle
|
|
47
49
|
a reviewer can trust. <strong>Skills</strong> teach an agent a workflow — author a recipe, review
|
|
48
|
-
a PR, validate a release
|
|
49
|
-
|
|
50
|
+
a PR, validate a release — and are the layer you work through; they call the harness, which runs
|
|
51
|
+
the recipe. <strong>Recipe libraries</strong> hold each team's recipes and domain actions.
|
|
52
|
+
Compose in that order, against the product repo under test.
|
|
50
53
|
</p>
|
|
51
54
|
</div>
|
|
52
55
|
</section>
|
|
@@ -67,9 +70,12 @@
|
|
|
67
70
|
release — turning "figure it out" into a procedure with gates you can steer.
|
|
68
71
|
</p>
|
|
69
72
|
<p>
|
|
70
|
-
Skills
|
|
71
|
-
|
|
72
|
-
|
|
73
|
+
Skills come from the public <code>MetaMask/skills</code> repo plus the internal
|
|
74
|
+
<code>Consensys/skills</code> overlay, which needs access to that org; private skills override
|
|
75
|
+
public ones on a name collision. Clone each, point <code>METAMASK_SKILLS_DIR</code> and
|
|
76
|
+
<code>CONSENSYS_SKILLS_DIR</code> at them, then install per checkout with
|
|
77
|
+
<code>yarn skills</code>. <strong>You do not need skills to run a recipe</strong>, only to have
|
|
78
|
+
an agent follow a proven workflow instead of improvising one.
|
|
73
79
|
<a href="tutorials/v3.html">V3 walks the install.</a>
|
|
74
80
|
</p>
|
|
75
81
|
</div>
|
|
@@ -129,8 +135,8 @@
|
|
|
129
135
|
</p>
|
|
130
136
|
<p style="margin-bottom:0">
|
|
131
137
|
They differ, and the harness says so rather than pretending otherwise: Core is headless with
|
|
132
|
-
nothing to launch and no UI actions;
|
|
133
|
-
|
|
138
|
+
nothing to launch and no UI actions; Extension and iOS can record full-run video; Android
|
|
139
|
+
replay video is not implemented yet, so use screenshot evidence there.
|
|
134
140
|
</p>
|
|
135
141
|
</div>
|
|
136
142
|
</div>
|
|
@@ -466,7 +472,7 @@
|
|
|
466
472
|
|
|
467
473
|
<hr class="sep">
|
|
468
474
|
<div class="btn-row">
|
|
469
|
-
<a class="btn btn-primary" href="
|
|
475
|
+
<a class="btn btn-primary" href="how-it-works.html#steps">Do the walkthrough →</a>
|
|
470
476
|
<a class="btn btn-ghost" href="reviewers.html">Read an evidence bundle</a>
|
|
471
477
|
</div>
|
|
472
478
|
</section>
|
|
@@ -474,8 +480,8 @@
|
|
|
474
480
|
|
|
475
481
|
<footer class="footer">
|
|
476
482
|
<div class="wrap">
|
|
477
|
-
<p>Internal getting-started guide for
|
|
478
|
-
<p>
|
|
483
|
+
<p>Internal getting-started guide for proving MetaMask changes with recipes. Not official MetaMask product documentation.</p>
|
|
484
|
+
<p>Sample output moves between releases; trust your terminal over this page.</p>
|
|
479
485
|
</div>
|
|
480
486
|
</footer>
|
|
481
487
|
|
package/site/assets/progress.mjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* buttons, platform filters, and the layer diagram. No dependencies, no
|
|
4
4
|
* network, no tracking. Progress lives in localStorage under
|
|
5
5
|
* `mmh.progress.<page>`, where <page> comes from body[data-progress-page].
|
|
6
|
-
* Pages that share a namespace share their state (
|
|
6
|
+
* Pages that share a namespace share their state (the How it works walkthrough and the V1
|
|
7
7
|
* tutorial are deliberately the same checklist).
|
|
8
8
|
*/
|
|
9
9
|
(function () {
|