@deeeed/metamask-harness 0.2.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 (158) hide show
  1. package/CHANGELOG.md +161 -0
  2. package/README.md +140 -0
  3. package/bin/mm-harness +99 -0
  4. package/docs/CHEATSHEET.md +61 -0
  5. package/docs/CLI-SPEC.md +915 -0
  6. package/docs/MENTAL-MODEL.md +295 -0
  7. package/docs/architecture.md +367 -0
  8. package/docs/extension-runtime-commands.md +60 -0
  9. package/docs/harness-cli.md +43 -0
  10. package/docs/live-adapter-contract.md +188 -0
  11. package/docs/package-boundaries.md +47 -0
  12. package/docs/perps-flow-catalog.md +235 -0
  13. package/docs/recipe-libraries.md +95 -0
  14. package/docs/runtime-file-conventions.md +36 -0
  15. package/library/actions/core/perps/_controller.mjs +727 -0
  16. package/library/actions/core/perps/assert_orders.mjs +53 -0
  17. package/library/actions/core/perps/assert_positions.mjs +52 -0
  18. package/library/actions/core/perps/close_orders.mjs +97 -0
  19. package/library/actions/core/perps/close_positions.mjs +118 -0
  20. package/library/actions/core/perps/ensure_orders.mjs +40 -0
  21. package/library/actions/core/perps/ensure_positions.mjs +37 -0
  22. package/library/actions/core/perps/place_order.mjs +201 -0
  23. package/library/actions/core/perps/read_account.mjs +30 -0
  24. package/library/actions/core/perps/read_orders.mjs +27 -0
  25. package/library/actions/core/perps/read_positions.mjs +27 -0
  26. package/library/actions/core/perps/start_state.mjs +92 -0
  27. package/library/actions/core/perps/teardown_state.mjs +86 -0
  28. package/library/actions/extension/perps/assert_orders.mjs +11 -0
  29. package/library/actions/extension/perps/assert_positions.mjs +11 -0
  30. package/library/actions/extension/perps/close_orders.mjs +8 -0
  31. package/library/actions/extension/perps/close_positions.mjs +8 -0
  32. package/library/actions/extension/perps/ensure_orders.mjs +4 -0
  33. package/library/actions/extension/perps/ensure_positions.mjs +4 -0
  34. package/library/actions/extension/perps/perps.mjs +730 -0
  35. package/library/actions/extension/perps/place_order.mjs +7 -0
  36. package/library/actions/extension/perps/read_orders.mjs +4 -0
  37. package/library/actions/extension/perps/read_positions.mjs +3 -0
  38. package/library/actions/extension/platform/cdp.mjs +541 -0
  39. package/library/actions/extension/ui/navigate.mjs +44 -0
  40. package/library/actions/extension/wallet/ensure_unlocked.mjs +36 -0
  41. package/library/actions/extension/wallet/read_state.mjs +27 -0
  42. package/library/actions/extension/wallet/select_account.mjs +48 -0
  43. package/library/actions/extension/wallet/setup.mjs +35 -0
  44. package/library/actions/mobile/app-overlay/app/dev-tools/AgenticService/AgentStepHud.tsx.patch +185 -0
  45. package/library/actions/mobile/app-overlay/app/dev-tools/AgenticService/AgenticService.ts.patch +1662 -0
  46. package/library/actions/mobile/bridge-runtime/cdp-bridge.cjs +686 -0
  47. package/library/actions/mobile/bridge-runtime/lib/cdp-eval.cjs +110 -0
  48. package/library/actions/mobile/bridge-runtime/lib/config.cjs +39 -0
  49. package/library/actions/mobile/bridge-runtime/lib/issue-capture.cjs +446 -0
  50. package/library/actions/mobile/bridge-runtime/lib/target-discovery.cjs +204 -0
  51. package/library/actions/mobile/bridge-runtime/lib/ws-client.cjs +108 -0
  52. package/library/actions/mobile/bridge-runtime/setup-wallet.sh +442 -0
  53. package/library/actions/mobile/perps/assert_orders.mjs +11 -0
  54. package/library/actions/mobile/perps/assert_positions.mjs +11 -0
  55. package/library/actions/mobile/perps/close_orders.mjs +8 -0
  56. package/library/actions/mobile/perps/close_positions.mjs +8 -0
  57. package/library/actions/mobile/perps/ensure_orders.mjs +4 -0
  58. package/library/actions/mobile/perps/ensure_positions.mjs +4 -0
  59. package/library/actions/mobile/perps/perps.mjs +709 -0
  60. package/library/actions/mobile/perps/place_order.mjs +7 -0
  61. package/library/actions/mobile/perps/read_orders.mjs +4 -0
  62. package/library/actions/mobile/perps/read_positions.mjs +3 -0
  63. package/library/actions/mobile/platform/bridge.mjs +283 -0
  64. package/library/actions/mobile/ui/navigate.mjs +38 -0
  65. package/library/actions/mobile/wallet/ensure_unlocked.mjs +107 -0
  66. package/library/actions/mobile/wallet/home.mjs +35 -0
  67. package/library/actions/mobile/wallet/read_state.mjs +40 -0
  68. package/library/actions/mobile/wallet/select_account.mjs +48 -0
  69. package/library/actions/mobile/wallet/setup.mjs +220 -0
  70. package/library/flows/perps.flows.json +64 -0
  71. package/library/library.json +7 -0
  72. package/library/manifests/core.action-manifest.json +1282 -0
  73. package/library/manifests/extension.action-manifest.json +1749 -0
  74. package/library/manifests/mobile.action-manifest.json +1753 -0
  75. package/library/recipes/action-validation.extension.recipe.json +417 -0
  76. package/library/recipes/action-validation.mobile.recipe.json +422 -0
  77. package/library/recipes/order-lifecycle.core.recipe.json +78 -0
  78. package/library/recipes/perps-lifecycle.recipe.json +194 -0
  79. package/library/recipes/read-markets.core.recipe.json +38 -0
  80. package/library/recipes/smoke.extension.recipe.json +31 -0
  81. package/library/recipes/smoke.mobile.recipe.json +31 -0
  82. package/library/recipes/trading-lifecycle.core.recipe.json +76 -0
  83. package/orchestration/compat-overlays/README.md +19 -0
  84. package/orchestration/compat-overlays/mobile/README.md +13 -0
  85. package/orchestration/compat-overlays/mobile/rn81-message-event-source.patch +42 -0
  86. package/orchestration/core/cleanup.sh +37 -0
  87. package/orchestration/core/inject.sh +154 -0
  88. package/orchestration/doctor.mjs +72 -0
  89. package/orchestration/extension/cleanup.mjs +60 -0
  90. package/orchestration/extension/console-tail.mjs +228 -0
  91. package/orchestration/extension/ensure-browser.sh +416 -0
  92. package/orchestration/extension/ensure-ready.ts +185 -0
  93. package/orchestration/extension/extension-id.ts +107 -0
  94. package/orchestration/extension/inject.mjs +266 -0
  95. package/orchestration/extension/launch-browser.cjs +216 -0
  96. package/orchestration/extension/launch.sh +175 -0
  97. package/orchestration/extension/live.sh +320 -0
  98. package/orchestration/extension/pin-remote-flags.cjs +45 -0
  99. package/orchestration/extension/readiness.mjs +414 -0
  100. package/orchestration/extension/refresh-build.sh +190 -0
  101. package/orchestration/extension/runtime-decision.ts +445 -0
  102. package/orchestration/extension/runtime.ts +407 -0
  103. package/orchestration/extension/seed-fixture.sh +177 -0
  104. package/orchestration/extension/sidepanel-toggle.sh +291 -0
  105. package/orchestration/extension/snapshot-dist.sh +84 -0
  106. package/orchestration/extension/start-watch.sh +339 -0
  107. package/orchestration/extension/wallet-fixture-state.cjs +1086 -0
  108. package/orchestration/lib/activate-repo-node.sh +144 -0
  109. package/orchestration/lib/cli-color.mjs +84 -0
  110. package/orchestration/lib/cli-commands.mjs +243 -0
  111. package/orchestration/lib/cli-home.mjs +354 -0
  112. package/orchestration/lib/cli-ux.sh +252 -0
  113. package/orchestration/lib/cli-version.mjs +123 -0
  114. package/orchestration/lib/ensure-runner-deps.sh +56 -0
  115. package/orchestration/lib/harness-path.sh +55 -0
  116. package/orchestration/lib/hash-helpers.sh +44 -0
  117. package/orchestration/lib/json-field.sh +23 -0
  118. package/orchestration/lib/log-tui.mjs +304 -0
  119. package/orchestration/lib/open-debug.mjs +317 -0
  120. package/orchestration/lib/path-defaults.json +4 -0
  121. package/orchestration/lib/progress.mjs +107 -0
  122. package/orchestration/lib/recipe-paths.mjs +26 -0
  123. package/orchestration/lib/resolve-farmslot-ports.sh +144 -0
  124. package/orchestration/manifest.json +358 -0
  125. package/orchestration/mobile/cleanup.sh +192 -0
  126. package/orchestration/mobile/deps-markers.ts +21 -0
  127. package/orchestration/mobile/inject.sh +681 -0
  128. package/orchestration/mobile/launch.sh +137 -0
  129. package/orchestration/mobile/live.sh +125 -0
  130. package/orchestration/mobile/runtime-decision.ts +292 -0
  131. package/orchestration/porcelain/metamask-recipe +99 -0
  132. package/orchestration/porcelain/mm-recipe +1591 -0
  133. package/orchestration/porcelain/mme-recipe +1181 -0
  134. package/package.json +59 -0
  135. package/runner/extension/verify.sh +511 -0
  136. package/runner/mobile/verify.sh +501 -0
  137. package/runner/src/adapters.ts +601 -0
  138. package/runner/src/cli.ts +1820 -0
  139. package/runner/src/commands/debug.ts +44 -0
  140. package/runner/src/commands/fixtures.ts +99 -0
  141. package/runner/src/commands/launch.ts +397 -0
  142. package/runner/src/commands/logs.ts +60 -0
  143. package/runner/src/commands/shared.ts +138 -0
  144. package/runner/src/completions-cache.ts +86 -0
  145. package/runner/src/doctor.ts +203 -0
  146. package/runner/src/harness.ts +516 -0
  147. package/runner/src/heal-bounds.ts +179 -0
  148. package/runner/src/index.ts +6 -0
  149. package/runner/src/live-adapter-contract.ts +274 -0
  150. package/runner/src/manifest.ts +47 -0
  151. package/runner/src/mm-harness-cli.ts +488 -0
  152. package/runner/src/paths.ts +198 -0
  153. package/runner/src/recording-target.ts +147 -0
  154. package/runner/src/run-recording.ts +329 -0
  155. package/runner/src/runner.ts +108 -0
  156. package/runner/src/types.ts +57 -0
  157. package/scripts/completions.sh +125 -0
  158. package/scripts/install-completions.sh +62 -0
@@ -0,0 +1,194 @@
1
+ {
2
+ "schema_version": 1,
3
+ "title": "MetaMask Perps lifecycle smoke",
4
+ "description": "Reusable Recipe v1 smoke that proves a deterministic Perps prestate, opens a small ETH testnet position, verifies it, closes it, and tears down to a clean state.",
5
+ "validate": {
6
+ "workflow": {
7
+ "entry": "status",
8
+ "nodes": {
9
+ "status": {
10
+ "action": "app.status",
11
+ "intent": "Read runner compatibility status",
12
+ "detail": "Confirm the selected MetaMask adapter is connected before trading.",
13
+ "flow": "setup",
14
+ "next": "fixture"
15
+ },
16
+ "fixture": {
17
+ "action": "metamask.wallet.fixture_status",
18
+ "intent": "Verify wallet fixture availability",
19
+ "detail": "Check the preseeded wallet fixture required for deterministic Perps validation.",
20
+ "flow": "setup",
21
+ "next": "wallet-setup"
22
+ },
23
+ "wallet-setup": {
24
+ "action": "metamask.wallet.setup",
25
+ "intent": "Ensure wallet fixture setup is ready",
26
+ "detail": "Confirm onboarding and fixture state before controlling the app.",
27
+ "flow": "wallet",
28
+ "next": "wallet-unlock"
29
+ },
30
+ "wallet-unlock": {
31
+ "action": "metamask.wallet.ensure_unlocked",
32
+ "intent": "Ensure the wallet is unlocked",
33
+ "detail": "Unlock only if the app is currently locked.",
34
+ "flow": "wallet",
35
+ "next": "select-account"
36
+ },
37
+ "select-account": {
38
+ "action": "metamask.wallet.select_account",
39
+ "address": "0x8dc623e964475d4d669da601fd15ea9125469003",
40
+ "intent": "Select the validation account",
41
+ "detail": "Use the deterministic testnet account shared by mobile and extension.",
42
+ "flow": "wallet",
43
+ "next": "wallet-read-state"
44
+ },
45
+ "wallet-read-state": {
46
+ "action": "metamask.wallet.read_state",
47
+ "intent": "Capture wallet state proof",
48
+ "detail": "Record selected account/network state before the Perps lifecycle.",
49
+ "flow": "wallet",
50
+ "next": "navigate-perps"
51
+ },
52
+ "navigate-perps": {
53
+ "action": "ui.navigate",
54
+ "intent": "Navigate to Perps with raw platform routing",
55
+ "detail": "Mobile consumes route=PerpsMarketListView; extension consumes hash=#/?tab=perps.",
56
+ "flow": "perps setup",
57
+ "next": "navigate-market",
58
+ "route": "PerpsMarketListView",
59
+ "hash": "#/?tab=perps"
60
+ },
61
+ "navigate-market": {
62
+ "action": "ui.navigate",
63
+ "intent": "Open the ETH Perps market with raw platform routing",
64
+ "detail": "Mobile consumes route=params; extension consumes hash=#/perps/market/ETH.",
65
+ "flow": "perps setup",
66
+ "next": "start-state",
67
+ "route": "PerpsMarketDetails",
68
+ "params": {
69
+ "market": {
70
+ "symbol": "ETH"
71
+ }
72
+ },
73
+ "hash": "#/perps/market/ETH"
74
+ },
75
+ "start-state": {
76
+ "action": "metamask.perps.start_state",
77
+ "profile": "clean_market_testnet",
78
+ "market": "ETH",
79
+ "positions": {
80
+ "state": "none",
81
+ "mode": "matching"
82
+ },
83
+ "orders": {
84
+ "state": "none",
85
+ "mode": "matching"
86
+ },
87
+ "page": "market",
88
+ "intent": "Prepare clean ETH testnet Perps state",
89
+ "detail": "Close existing ETH testnet positions and orders before the smoke lifecycle starts.",
90
+ "flow": "perps setup",
91
+ "next": "assert-clean-position",
92
+ "tutorial": {
93
+ "completed": true
94
+ }
95
+ },
96
+ "assert-clean-position": {
97
+ "action": "metamask.perps.assert_positions",
98
+ "market": "ETH",
99
+ "state": "none",
100
+ "intent": "Confirm ETH position state is clean",
101
+ "detail": "Assert lifecycle starts with no ETH Perps position.",
102
+ "flow": "perps setup",
103
+ "next": "assert-clean-orders"
104
+ },
105
+ "assert-clean-orders": {
106
+ "action": "metamask.perps.assert_orders",
107
+ "market": "ETH",
108
+ "state": "none",
109
+ "intent": "Confirm ETH order state is clean",
110
+ "detail": "Assert lifecycle starts with no ETH Perps orders.",
111
+ "flow": "perps setup",
112
+ "next": "place-order"
113
+ },
114
+ "place-order": {
115
+ "action": "metamask.perps.place_order",
116
+ "market": "ETH",
117
+ "side": "long",
118
+ "amount": "11",
119
+ "leverage": 3,
120
+ "intent": "Open a small ETH 3x long position",
121
+ "detail": "Place an approximately $11 real testnet market order through the Perps controller.",
122
+ "flow": "perps lifecycle",
123
+ "next": "assert-open"
124
+ },
125
+ "assert-open": {
126
+ "action": "metamask.perps.assert_positions",
127
+ "market": "ETH",
128
+ "state": "open",
129
+ "intent": "Confirm the ETH position opened",
130
+ "detail": "Verify the lifecycle created a real position before cleanup.",
131
+ "flow": "perps lifecycle",
132
+ "next": "read-orders"
133
+ },
134
+ "read-orders": {
135
+ "action": "metamask.perps.read_orders",
136
+ "market": "ETH",
137
+ "intent": "Read ETH orders before cleanup",
138
+ "detail": "Collect order state proof before lifecycle teardown.",
139
+ "flow": "perps lifecycle",
140
+ "next": "close-orders"
141
+ },
142
+ "close-orders": {
143
+ "action": "metamask.perps.close_orders",
144
+ "market": "ETH",
145
+ "intent": "Close ETH orders from the lifecycle",
146
+ "detail": "Cancel any open ETH orders selected by the recipe parameters.",
147
+ "flow": "perps teardown",
148
+ "next": "close-position"
149
+ },
150
+ "close-position": {
151
+ "action": "metamask.perps.close_positions",
152
+ "market": "ETH",
153
+ "intent": "Close the ETH lifecycle position",
154
+ "detail": "Close the position created by this smoke and wait for clean state.",
155
+ "flow": "perps teardown",
156
+ "next": "assert-closed"
157
+ },
158
+ "assert-closed": {
159
+ "action": "metamask.perps.assert_positions",
160
+ "market": "ETH",
161
+ "state": "none",
162
+ "intent": "Confirm ETH position cleanup completed",
163
+ "detail": "Assert no ETH position remains after lifecycle cleanup.",
164
+ "flow": "perps teardown",
165
+ "next": "teardown-state"
166
+ },
167
+ "teardown-state": {
168
+ "action": "metamask.perps.teardown_state",
169
+ "market": "ETH",
170
+ "positions": {
171
+ "state": "none",
172
+ "mode": "matching"
173
+ },
174
+ "orders": {
175
+ "state": "none",
176
+ "mode": "matching"
177
+ },
178
+ "page": "home",
179
+ "intent": "Return Perps to globally clean home state",
180
+ "detail": "Close remaining ETH positions and orders so the lifecycle leaves its selected market clean.",
181
+ "flow": "perps teardown",
182
+ "next": "done"
183
+ },
184
+ "done": {
185
+ "action": "end",
186
+ "status": "pass",
187
+ "intent": "Finish Perps lifecycle smoke",
188
+ "detail": "The recipe opened and closed a real ETH testnet position.",
189
+ "flow": "complete"
190
+ }
191
+ }
192
+ }
193
+ }
194
+ }
@@ -0,0 +1,38 @@
1
+ {
2
+ "schema_version": 1,
3
+ "title": "MetaMask core headless Perps read",
4
+ "description": "Proves the headless `core` adapter instantiates @metamask/perps-controller against a resolved MetaMask/core checkout and reads live HyperLiquid testnet Perps state (positions, open orders, account state) over HTTP — no CDP, bridge, or UI.",
5
+ "validate": {
6
+ "workflow": {
7
+ "entry": "status",
8
+ "nodes": {
9
+ "status": {
10
+ "action": "app.status",
11
+ "next": "read_positions",
12
+ "intent": "Resolve the core checkout and report headless compatibility mode"
13
+ },
14
+ "read_positions": {
15
+ "action": "metamask.perps.read_positions",
16
+ "mode": "all",
17
+ "next": "read_orders",
18
+ "intent": "Read live Perps positions from the headless controller"
19
+ },
20
+ "read_orders": {
21
+ "action": "metamask.perps.read_orders",
22
+ "mode": "all",
23
+ "next": "read_account",
24
+ "intent": "Read live Perps open orders from the headless controller"
25
+ },
26
+ "read_account": {
27
+ "action": "metamask.perps.read_account",
28
+ "next": "done",
29
+ "intent": "Read live Perps account state from the headless controller"
30
+ },
31
+ "done": {
32
+ "action": "end",
33
+ "status": "pass"
34
+ }
35
+ }
36
+ }
37
+ }
38
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "schema_version": 1,
3
+ "title": "MetaMask Extension v1 runner smoke",
4
+ "description": "Proves the MetaMask Extension runner executes manifest-declared actions and writes a valid artifact package.",
5
+ "validate": {
6
+ "workflow": {
7
+ "entry": "status",
8
+ "nodes": {
9
+ "status": {
10
+ "action": "app.status",
11
+ "next": "fixture",
12
+ "intent": "Perform status for the recipe"
13
+ },
14
+ "fixture": {
15
+ "action": "metamask.wallet.fixture_status",
16
+ "next": "cdp",
17
+ "intent": "Perform fixture for the recipe"
18
+ },
19
+ "cdp": {
20
+ "action": "cdp.target",
21
+ "next": "done",
22
+ "intent": "Perform cdp for the recipe"
23
+ },
24
+ "done": {
25
+ "action": "end",
26
+ "status": "pass"
27
+ }
28
+ }
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "schema_version": 1,
3
+ "title": "MetaMask Mobile v1 runner smoke",
4
+ "description": "Proves the MetaMask Mobile runner executes manifest-declared actions and writes a valid artifact package.",
5
+ "validate": {
6
+ "workflow": {
7
+ "entry": "status",
8
+ "nodes": {
9
+ "status": {
10
+ "action": "app.status",
11
+ "next": "fixture",
12
+ "intent": "Perform status for the recipe"
13
+ },
14
+ "fixture": {
15
+ "action": "metamask.wallet.fixture_status",
16
+ "next": "cdp",
17
+ "intent": "Perform fixture for the recipe"
18
+ },
19
+ "cdp": {
20
+ "action": "cdp.target",
21
+ "next": "done",
22
+ "intent": "Perform cdp for the recipe"
23
+ },
24
+ "done": {
25
+ "action": "end",
26
+ "status": "pass"
27
+ }
28
+ }
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,76 @@
1
+ {
2
+ "schema_version": 1,
3
+ "title": "MetaMask core headless Perps trading lifecycle",
4
+ "description": "Proves the headless `core` adapter places and closes a REAL Perps order on HyperLiquid testnet through the full signing path: a default viem signer backs the perps controller's wallet adapter, the active HyperLiquid testnet provider is initialized, and a small BTC long is opened then closed. Leaves the account FLAT. No CDP, bridge, or UI.",
5
+ "validate": {
6
+ "workflow": {
7
+ "entry": "status",
8
+ "nodes": {
9
+ "status": {
10
+ "action": "app.status",
11
+ "next": "start_flat",
12
+ "intent": "Resolve the core checkout and report headless compatibility mode"
13
+ },
14
+ "start_flat": {
15
+ "action": "metamask.perps.start_state",
16
+ "market": "BTC",
17
+ "positions": { "state": "none" },
18
+ "orders": { "state": "none" },
19
+ "network": "testnet",
20
+ "timeout_ms": 45000,
21
+ "next": "place_order",
22
+ "intent": "Converge BTC testnet baseline to flat (no positions, no orders) before the proof window"
23
+ },
24
+ "place_order": {
25
+ "action": "metamask.perps.place_order",
26
+ "market": "BTC",
27
+ "side": "long",
28
+ "notional": "11",
29
+ "leverage": 2,
30
+ "network": "testnet",
31
+ "timeout_ms": 45000,
32
+ "next": "assert_open",
33
+ "intent": "Place a small BTC long market order via the headless controller (real signed order to HL testnet)"
34
+ },
35
+ "assert_open": {
36
+ "action": "metamask.perps.assert_positions",
37
+ "market": "BTC",
38
+ "state": "open",
39
+ "timeout_ms": 30000,
40
+ "next": "close_position",
41
+ "intent": "Assert the BTC position opened"
42
+ },
43
+ "close_position": {
44
+ "action": "metamask.perps.close_positions",
45
+ "market": "BTC",
46
+ "network": "testnet",
47
+ "timeout_ms": 45000,
48
+ "next": "assert_flat",
49
+ "intent": "Close the BTC position via the headless controller (real signed close to HL testnet)"
50
+ },
51
+ "assert_flat": {
52
+ "action": "metamask.perps.assert_positions",
53
+ "market": "BTC",
54
+ "state": "none",
55
+ "timeout_ms": 30000,
56
+ "next": "teardown_flat",
57
+ "intent": "Assert the BTC position closed — account is flat"
58
+ },
59
+ "teardown_flat": {
60
+ "action": "metamask.perps.teardown_state",
61
+ "market": "BTC",
62
+ "positions": { "state": "none" },
63
+ "orders": { "state": "none" },
64
+ "network": "testnet",
65
+ "timeout_ms": 45000,
66
+ "next": "done",
67
+ "intent": "Restore BTC testnet baseline to flat after the proof (leave the shared account clean)"
68
+ },
69
+ "done": {
70
+ "action": "end",
71
+ "status": "pass"
72
+ }
73
+ }
74
+ }
75
+ }
76
+ }
@@ -0,0 +1,19 @@
1
+ # Compatibility overlays (historical-commit replay)
2
+
3
+ NOT dead code. These overlays exist for replay/eval runs against OLD product
4
+ commits — e.g. re-running a historical task on a fixed baseline to validate
5
+ prompt or harness changes. A "zero current references" scan will always come
6
+ up empty here by design: the feature's purpose is non-current checkouts.
7
+
8
+ - Purpose: make historical product checkouts bootable/bridgeable under the
9
+ current local toolchain so Recipe v1 evals can replay against them.
10
+ - When it applies: mobile checkouts predating React Native 0.81 polyfill
11
+ fixes (see mobile/README.md for the per-patch details).
12
+ - How it is applied: MANUALLY (no automatic apply logic in this repo).
13
+ The operator or the `/recipe-harness` skill applies the patch reversibly
14
+ before a historical rebuild (`git apply <patch>` in the product checkout,
15
+ `git apply -R` to remove) and records the overlay path in validation
16
+ evidence. `orchestration/mobile/inject.sh` does NOT apply these.
17
+
18
+ Listed in orchestration/manifest.json (kind: compat-overlay) so the doctor
19
+ verifies the surface and this directory is never flagged as unused again.
@@ -0,0 +1,13 @@
1
+ # Mobile historical compatibility overlays
2
+
3
+ These patch files document temporary source overlays used by Recipe v1 historical
4
+ injection evals when an older MetaMask Mobile checkout cannot boot under the
5
+ current local Xcode/Hermes toolchain.
6
+
7
+ They are intentionally kept in the external runner repository, not Farmslot root
8
+ scripts, so a `/recipe-harness` skill can apply the same reversible overlay before
9
+ a historical rebuild and record the overlay path in validation evidence.
10
+
11
+ - `rn81-message-event-source.patch`: adds a read-only `MessageEvent.prototype.source`
12
+ getter for React Native 0.81 historical checkouts whose bundled polyfill omits
13
+ it while CDP websocket clients expect browser-compatible `MessageEvent` shape.
@@ -0,0 +1,42 @@
1
+ compat-overlay: rn81-message-event-source (historical-checkout replay support)
2
+
3
+ What: patches metamask-mobile shim.js to add a read-only
4
+ MessageEvent.prototype.source getter.
5
+ Why: the harness CDP bridge expects browser-compatible MessageEvent shape;
6
+ pre-RN-0.81 checkouts ship a polyfill that omits .source, so the bridge
7
+ cannot attach when replaying tasks on those historical commits.
8
+ When: only when replaying/evaluating against mobile checkouts older than the
9
+ RN 0.81 upgrade (pre-RN-0.81 checkouts). Never needed on current main.
10
+ How: applied MANUALLY in the historical product checkout —
11
+ git apply <this file> (and `git apply -R` to remove)
12
+ No automatic apply logic exists in this repo. See ../README.md.
13
+
14
+ diff --git a/shim.js b/shim.js
15
+ --- a/shim.js
16
+ +++ b/shim.js
17
+ @@
18
+ if (typeof global.MessageEvent === 'undefined') {
19
+ global.MessageEvent =
20
+ require('react-native/src/private/webapis/html/events/MessageEvent').default;
21
+ }
22
+
23
+ +// Historical runner compatibility: React Native 0.81's MessageEvent polyfill
24
+ +// can omit the browser `source` getter expected by CDP websocket clients under
25
+ +// newer Xcode/Hermes toolchains. Keep the getter read-only and null, matching
26
+ +// browser MessageEvent semantics for non-window messages.
27
+ +if (
28
+ + global.MessageEvent?.prototype &&
29
+ + !Object.getOwnPropertyDescriptor(global.MessageEvent.prototype, 'source')
30
+ +) {
31
+ + Object.defineProperty(global.MessageEvent.prototype, 'source', {
32
+ + configurable: true,
33
+ + enumerable: true,
34
+ + get() {
35
+ + return null;
36
+ + },
37
+ + });
38
+ +}
39
+ +
40
+ class AbortError extends Error {
41
+ constructor(message) {
42
+ super(message);
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env bash
2
+ # cleanup.sh — remove the headless `core` adapter overlay.
3
+ #
4
+ # Inputs: --target <metamask-core> (default $PWD); env RECIPE_HARNESS_ROOT.
5
+ # Outputs: removes <harness>/core. Exit 0 — cleaned (idempotent); 2 — bad args.
6
+ # Never touches: product files (core install patches nothing).
7
+ #
8
+ # Core injects nothing into the product checkout, so cleanup only removes the
9
+ # ignored overlay directory temp/recipe/harness/core. There are no backups to
10
+ # restore and no patched product files. Idempotent: a missing overlay is a
11
+ # success, not an error.
12
+ set -euo pipefail
13
+
14
+ TARGET="$PWD"
15
+ while [ "$#" -gt 0 ]; do
16
+ case "$1" in
17
+ --target) TARGET="$2"; shift 2 ;;
18
+ -h|--help) echo "Usage: cleanup.sh (core) [--target <metamask-core>]"; exit 0 ;;
19
+ *) echo "Unknown arg: $1" >&2; exit 2 ;;
20
+ esac
21
+ done
22
+
23
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
24
+ TARGET="$(cd "$TARGET" && pwd)"
25
+ # shellcheck disable=SC1091
26
+ for _hp in "$SCRIPT_DIR/../lib/harness-path.sh"; do
27
+ [ -f "$_hp" ] && { . "$_hp"; break; }
28
+ done
29
+ unset _hp
30
+ if ! command -v harness_root >/dev/null 2>&1; then
31
+ echo "mm-harness: shared lib orchestration/lib/harness-path.sh not found; reinstall the runner." >&2
32
+ exit 1
33
+ fi
34
+ HARNESS_DIR="$(harness_dir "$TARGET" core)"
35
+
36
+ rm -rf "$HARNESS_DIR"
37
+ echo "Cleaned core recipe harness from $TARGET"
@@ -0,0 +1,154 @@
1
+ #!/usr/bin/env bash
2
+ # inject.sh — headless `core` adapter install.
3
+ #
4
+ # Inputs: --target <metamask-core> (default $PWD); env RECIPE_HARNESS_ROOT,
5
+ # METAMASK_RUNNER_PROTOCOL_ROOT > FARMSLOT_ROOT > <runner>/.farmslot-root.
6
+ # Outputs: <harness>/core/{manifest.json,action-manifest.json,runner/}.
7
+ # Exit 0 — installed; 1 — symlink refusal/install failure; 2 — bad args.
8
+ # Never touches: product checkout source (overlay metadata only).
9
+ #
10
+ # Core is a headless adapter: it instantiates @metamask/perps-controller against
11
+ # a resolved MetaMask/core checkout and reads/writes HyperLiquid testnet over
12
+ # HTTP. There is no app, CDP target, bridge, or UI. Unlike the mobile/extension
13
+ # installs this writes NOTHING into the product checkout source: it only creates
14
+ # the ignored harness overlay under temp/recipe/harness/core with a delegate
15
+ # wrapper, the action manifests, recipe snapshots, and install metadata.
16
+ set -euo pipefail
17
+
18
+ TARGET="$PWD"
19
+ while [ "$#" -gt 0 ]; do
20
+ case "$1" in
21
+ --target) TARGET="$2"; shift 2 ;;
22
+ -h|--help) echo "Usage: inject.sh (core) [--target <metamask-core>]"; exit 0 ;;
23
+ *) echo "Unknown arg: $1" >&2; exit 2 ;;
24
+ esac
25
+ done
26
+
27
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
28
+ RUNNER_DIR="$(cd "${SCRIPT_DIR}/../.." && pwd -P)"
29
+ # shellcheck disable=SC1091
30
+ for _hp in "$SCRIPT_DIR/../lib/harness-path.sh"; do
31
+ [ -f "$_hp" ] && { . "$_hp"; break; }
32
+ done
33
+ unset _hp
34
+ if ! command -v harness_root >/dev/null 2>&1; then
35
+ echo "mm-harness: shared lib orchestration/lib/harness-path.sh not found; reinstall the runner." >&2
36
+ exit 1
37
+ fi
38
+ TARGET="$(cd "$TARGET" && pwd)"
39
+ METAMASK_RUNNER_DIR="$RUNNER_DIR"
40
+ METAMASK_RUNNER_SOURCE_KIND="runner-self"
41
+ METAMASK_RUNNER_REVISION="$(git -C "$RUNNER_DIR" rev-parse HEAD 2>/dev/null || echo unknown)"
42
+ METAMASK_RUNNER_PROTOCOL_ROOT="${METAMASK_RUNNER_PROTOCOL_ROOT:-${METAMASK_RUNNER_FARMSLOT_ROOT:-${FARMSLOT_ROOT:-}}}"
43
+ if [ -z "$METAMASK_RUNNER_PROTOCOL_ROOT" ] && [ -f "$RUNNER_DIR/.farmslot-root" ]; then
44
+ METAMASK_RUNNER_PROTOCOL_ROOT="$(cat "$RUNNER_DIR/.farmslot-root")"
45
+ fi
46
+ export METAMASK_RUNNER_DIR METAMASK_RUNNER_SOURCE_KIND METAMASK_RUNNER_REVISION METAMASK_RUNNER_PROTOCOL_ROOT
47
+ HARNESS_ROOT="$(harness_root)"
48
+ HARNESS_REL="$HARNESS_ROOT/core"
49
+ HARNESS_DIR="$(harness_dir "$TARGET" core)"
50
+
51
+ refuse_symlink_destination() {
52
+ local rel="$1"
53
+ local path_so_far="$TARGET"
54
+ IFS='/' read -r -a parts <<< "$rel"
55
+ for part in "${parts[@]}"; do
56
+ [ -n "$part" ] || continue
57
+ path_so_far="$path_so_far/$part"
58
+ if [ -L "$path_so_far" ]; then
59
+ echo "Refusing core recipe harness install: $rel contains symlink component $path_so_far." >&2
60
+ return 1
61
+ fi
62
+ done
63
+ }
64
+
65
+ install_v1_runner_assets() {
66
+ # refuse_symlink_destination walks every path component, so the deepest paths
67
+ # also guard their parents ($HARNESS_REL covers the root segments).
68
+ refuse_symlink_destination "$HARNESS_REL"
69
+ refuse_symlink_destination "$HARNESS_REL/runner"
70
+ refuse_symlink_destination "$HARNESS_REL/action-manifest.json"
71
+ mkdir -p "$HARNESS_DIR"
72
+ rm -rf "$HARNESS_DIR/runner"
73
+ mkdir -p "$HARNESS_DIR/runner/bin" "$HARNESS_DIR/runner/manifests" "$HARNESS_DIR/runner/recipes"
74
+
75
+ # Install a harness-owned delegate that execs the reviewed external runner
76
+ # source recorded in manifest.json. The delegate keeps the overlay lightweight
77
+ # and never owns the runtime itself. Core is headless: no app, no .js.env, no
78
+ # simulator detection — the delegate only forwards FARMSLOT_ROOT (when set) and
79
+ # execs the real runner. Emit shell-safe lines: %q-quote the interpolated paths
80
+ # so a protocol/runner path containing a space — or $()/backtick/quote — cannot
81
+ # break the generated wrapper or inject at runtime.
82
+ local runner_protocol_root_q runner_exec_q
83
+ runner_protocol_root_q="$(printf '%q' "$METAMASK_RUNNER_PROTOCOL_ROOT")"
84
+ runner_exec_q="$(printf '%q' "$METAMASK_RUNNER_DIR/orchestration/porcelain/metamask-recipe")"
85
+ {
86
+ printf '%s\n' '#!/usr/bin/env bash'
87
+ printf '%s\n' 'set -euo pipefail'
88
+ if [ -n "$METAMASK_RUNNER_PROTOCOL_ROOT" ]; then
89
+ printf 'export FARMSLOT_ROOT=${FARMSLOT_ROOT:-%s}\n' "$runner_protocol_root_q"
90
+ fi
91
+ printf 'exec %s "$@"\n' "$runner_exec_q"
92
+ } > "$HARNESS_DIR/runner/bin/metamask-recipe"
93
+ chmod +x "$HARNESS_DIR/runner/bin/metamask-recipe"
94
+ if [ -n "$METAMASK_RUNNER_PROTOCOL_ROOT" ]; then
95
+ printf '%s\n' "$METAMASK_RUNNER_PROTOCOL_ROOT" > "$HARNESS_DIR/runner/.farmslot-root"
96
+ fi
97
+ printf '%s\n' "$METAMASK_RUNNER_DIR" > "$HARNESS_DIR/runner/.runner-source"
98
+ # resolve-runner-source.sh requires mobile + extension manifests to consider a
99
+ # runner valid, so the installed delegate must carry them alongside core's.
100
+ cp "$METAMASK_RUNNER_DIR/library/manifests/core.action-manifest.json" "$HARNESS_DIR/action-manifest.json"
101
+ cp "$METAMASK_RUNNER_DIR/library/manifests/core.action-manifest.json" "$HARNESS_DIR/runner/manifests/core.action-manifest.json"
102
+ cp "$METAMASK_RUNNER_DIR/library/manifests/mobile.action-manifest.json" "$HARNESS_DIR/runner/manifests/mobile.action-manifest.json"
103
+ cp "$METAMASK_RUNNER_DIR/library/manifests/extension.action-manifest.json" "$HARNESS_DIR/runner/manifests/extension.action-manifest.json"
104
+ if [ -d "$METAMASK_RUNNER_DIR/library/recipes" ]; then
105
+ rsync -a --delete "$METAMASK_RUNNER_DIR/library/recipes/" "$HARNESS_DIR/runner/recipes/"
106
+ fi
107
+ # Canonical recipe-library snapshot: flows/ + library.json so the installed
108
+ # harness mirrors the runner's library layout.
109
+ if [ -d "$METAMASK_RUNNER_DIR/library/flows" ]; then
110
+ mkdir -p "$HARNESS_DIR/runner/flows"
111
+ rsync -a --delete "$METAMASK_RUNNER_DIR/library/flows/" "$HARNESS_DIR/runner/flows/"
112
+ fi
113
+ if [ -f "$METAMASK_RUNNER_DIR/library/library.json" ]; then
114
+ cp "$METAMASK_RUNNER_DIR/library/library.json" "$HARNESS_DIR/runner/library.json"
115
+ fi
116
+ if [ ! -x "$HARNESS_DIR/runner/bin/metamask-recipe" ]; then
117
+ echo "Refusing core recipe harness install: failed to make runner executable." >&2
118
+ return 1
119
+ fi
120
+ }
121
+
122
+ install_v1_runner_assets
123
+
124
+ ENSURE_DEPS="$SCRIPT_DIR/../lib/ensure-runner-deps.sh"
125
+ if [ -f "$ENSURE_DEPS" ]; then
126
+ bash "$ENSURE_DEPS" "$RUNNER_DIR"
127
+ fi
128
+
129
+ SOURCE_REV="$(git -C "$RUNNER_DIR" rev-parse HEAD 2>/dev/null || echo unknown)"
130
+ CLEANUP_COMMAND="RECIPE_HARNESS_ROOT=$HARNESS_ROOT $(printf '%q' "$SCRIPT_DIR/cleanup.sh") --target $(printf '%q' "$TARGET")"
131
+ node -e '
132
+ const fs = require("fs");
133
+ const [runnerDir, runnerRevision, runnerSourceKind, target, manifestPath, harnessRel, cleanupCommand] = process.argv.slice(1);
134
+ const m = {
135
+ adapter: "core",
136
+ installedAt: new Date().toISOString(),
137
+ source: {
138
+ runnerDir,
139
+ runnerRevision,
140
+ runnerSourceKind,
141
+ },
142
+ target,
143
+ protocolVersion: "v1",
144
+ actionManifestPath: harnessRel + "/action-manifest.json",
145
+ runnerEntrypoint: harnessRel + "/runner/bin/metamask-recipe",
146
+ runtimeHelpers: {},
147
+ installedPaths: [harnessRel + "/runner"],
148
+ patchedFiles: [],
149
+ cleanupCommand,
150
+ };
151
+ fs.writeFileSync(manifestPath, JSON.stringify(m, null, 2) + "\n");
152
+ ' "$METAMASK_RUNNER_DIR" "$METAMASK_RUNNER_REVISION" "$METAMASK_RUNNER_SOURCE_KIND" "$TARGET" "$HARNESS_DIR/manifest.json" "$HARNESS_REL" "$CLEANUP_COMMAND"
153
+
154
+ echo "Installed core recipe harness: $HARNESS_DIR/manifest.json"