@deeeed/metamask-harness 0.33.2 → 0.33.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 CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.33.3 - 2026-08-05
6
+
7
+ ### Changed
8
+
9
+ - Lead the getting-started site with what a recipe is for — proving a change inside the running app, catching its own regressions on replay, and standing as the trust layer between an agent's change and a human's review — and add copy-paste prompts for first-time setup and the shared Perps recipe library.
10
+ - Add a dedicated Perps workflow page covering library discovery, testnet-first live proof, platform recipe selection, evidence, composition, and drift classification.
11
+
12
+ ### Fixed
13
+
14
+ - Raise the site's stated harness floor to 0.33+ and make first-time iOS setup pin one simulator UUID through forced Runway provisioning, install verification, launch, and fixture application.
15
+ - Recover Mobile navigation across typed Hermes target transitions after lifecycle foregrounding with at most one route-verified reissue.
16
+
5
17
  ## 0.33.2 - 2026-08-04
6
18
 
7
19
  ### Fixed
@@ -442,34 +442,188 @@ export async function evalSync(input, expression) {
442
442
  return parseMaybeJson(await bridgeCommand(input, ['eval', expression]));
443
443
  }
444
444
 
445
+ const TARGET_TRANSITION_CODES = new Set([
446
+ BRIDGE_ERROR_CODES.NO_TARGET,
447
+ BRIDGE_ERROR_CODES.CDP_TIMEOUT,
448
+ BRIDGE_ERROR_CODES.WS_CLOSED,
449
+ ]);
450
+
451
+ function isTargetTransition(error) {
452
+ return TARGET_TRANSITION_CODES.has(error?.code);
453
+ }
454
+
455
+ function valueContains(actual, expected) {
456
+ if (Array.isArray(expected)) {
457
+ return Array.isArray(actual) &&
458
+ actual.length === expected.length &&
459
+ expected.every((value, index) => valueContains(actual[index], value));
460
+ }
461
+ if (expected && typeof expected === 'object') {
462
+ return actual && typeof actual === 'object' &&
463
+ Object.entries(expected).every(([key, value]) => valueContains(actual[key], value));
464
+ }
465
+ return Object.is(actual, expected);
466
+ }
467
+
468
+ function routeMatches(route, expectedRoute, expectedParams) {
469
+ return routeName(route) === expectedRoute &&
470
+ (expectedParams === undefined || valueContains(route?.params ?? {}, expectedParams));
471
+ }
472
+
473
+ function routeTransitionProven(previousRoute, currentRoute, expectedRoute, expectedParams) {
474
+ if (!previousRoute || !routeMatches(currentRoute, expectedRoute, expectedParams)) return false;
475
+ if (!routeMatches(previousRoute, expectedRoute, expectedParams)) return true;
476
+ return Boolean(
477
+ previousRoute.key &&
478
+ currentRoute?.key &&
479
+ previousRoute.key !== currentRoute.key,
480
+ );
481
+ }
482
+
483
+ async function waitForNextRouteProbe(deadline) {
484
+ const remaining = deadline - Date.now();
485
+ if (remaining > 0) await sleep(Math.min(250, remaining));
486
+ }
487
+
445
488
  export async function navigate(input, route, params = {}, expectedRoute) {
446
- const navigation = await bridgeCommand(input, ['navigate', route, JSON.stringify(params)]);
447
- const verifiedRoute = String(
448
- expectedRoute ??
449
- (navigation && typeof navigation === 'object' && navigation.navigated
450
- ? navigation.navigated
451
- : route),
489
+ const timeoutMs = Number(input.node?.navigation_timeout_ms ?? 15000);
490
+ const requestedRoute = String(expectedRoute ?? route);
491
+ const requestedParams = requestedRoute === String(route) ? params : undefined;
492
+ const transitionCodes = [];
493
+ let lastTransitionError = null;
494
+ let navigateAttempts = 0;
495
+ let previousRoute = null;
496
+ let recoveryDeadline = null;
497
+ let recoveryPending = false;
498
+
499
+ try {
500
+ previousRoute = await bridgeCommand(input, ['get-route']);
501
+ } catch (error) {
502
+ if (!isTargetTransition(error)) throw error;
503
+ }
504
+
505
+ while (navigateAttempts < 2 || recoveryPending) {
506
+ if (recoveryPending) {
507
+ const deadline = recoveryDeadline ?? Date.now() + timeoutMs;
508
+ let currentRoute = null;
509
+ while (Date.now() < deadline) {
510
+ try {
511
+ currentRoute = await bridgeCommand(input, ['get-route']);
512
+ if (currentRoute !== null) break;
513
+ } catch (error) {
514
+ if (!isTargetTransition(error)) throw error;
515
+ lastTransitionError = error;
516
+ transitionCodes.push(error.code);
517
+ }
518
+ await waitForNextRouteProbe(deadline);
519
+ }
520
+ if (routeTransitionProven(
521
+ previousRoute,
522
+ currentRoute,
523
+ requestedRoute,
524
+ requestedParams,
525
+ )) {
526
+ return {
527
+ navigated: route,
528
+ params,
529
+ previousRoute,
530
+ currentRoute,
531
+ deviceName: null,
532
+ platform: null,
533
+ verifiedRoute: requestedRoute,
534
+ bridgeRecovery: {
535
+ codes: [...new Set(transitionCodes)],
536
+ navigateAttempts,
537
+ },
538
+ };
539
+ }
540
+ if (!previousRoute && routeMatches(currentRoute, requestedRoute, requestedParams)) {
541
+ break;
542
+ }
543
+ recoveryPending = false;
544
+ if (Date.now() >= deadline || navigateAttempts >= 2) break;
545
+ previousRoute = currentRoute;
546
+ }
547
+
548
+ navigateAttempts += 1;
549
+ try {
550
+ const navigation = await bridgeCommand(
551
+ input,
552
+ ['navigate', route, JSON.stringify(params)],
553
+ );
554
+ const verifiedRoute = String(
555
+ expectedRoute ??
556
+ (navigation && typeof navigation === 'object' && navigation.navigated
557
+ ? navigation.navigated
558
+ : route),
559
+ );
560
+ const verifiedParams = verifiedRoute === String(route) ? params : undefined;
561
+ const currentRoute = await waitForRoute(
562
+ input,
563
+ verifiedRoute,
564
+ timeoutMs,
565
+ verifiedParams,
566
+ );
567
+ return {
568
+ ...navigation,
569
+ currentRoute,
570
+ verifiedRoute,
571
+ ...(transitionCodes.length > 0
572
+ ? {
573
+ bridgeRecovery: {
574
+ codes: [...new Set(transitionCodes)],
575
+ navigateAttempts,
576
+ },
577
+ }
578
+ : {}),
579
+ };
580
+ } catch (error) {
581
+ if (!isTargetTransition(error)) throw error;
582
+ lastTransitionError = error;
583
+ transitionCodes.push(error.code);
584
+ recoveryDeadline = Date.now() + timeoutMs;
585
+ recoveryPending = true;
586
+ await waitForNextRouteProbe(recoveryDeadline);
587
+ }
588
+ }
589
+
590
+ throw lastTransitionError ?? new Error(
591
+ `Timed out navigating Mobile to route '${requestedRoute}' after ${timeoutMs}ms.`,
452
592
  );
453
- const currentRoute = await waitForRoute(input, verifiedRoute, Number(input.node?.navigation_timeout_ms ?? 15000));
454
- return { ...navigation, currentRoute, verifiedRoute };
455
593
  }
456
594
 
457
595
  function routeName(route) {
458
596
  return route && typeof route === 'object' ? String(route.name ?? '') : '';
459
597
  }
460
598
 
461
- export async function waitForRoute(input, expectedRoute, timeoutMs = 15000) {
599
+ export async function waitForRoute(
600
+ input,
601
+ expectedRoute,
602
+ timeoutMs = 15000,
603
+ expectedParams,
604
+ ) {
462
605
  const expected = String(expectedRoute);
463
606
  const deadline = Date.now() + timeoutMs;
464
607
  // lastRoute is null when bridgeCommand returns null (transient: route not yet
465
608
  // settled mid-navigation). Null means "not ready yet" — keep polling.
466
609
  let lastRoute = null;
610
+ let lastTransitionError = null;
467
611
  let pollCount = 0;
612
+ const transitionCodes = [];
468
613
  while (Date.now() < deadline) {
469
- lastRoute = await bridgeCommand(input, ['get-route']);
470
- if (routeName(lastRoute) === expected) return lastRoute;
614
+ try {
615
+ lastRoute = await bridgeCommand(input, ['get-route']);
616
+ } catch (error) {
617
+ if (!isTargetTransition(error)) throw error;
618
+ lastTransitionError = error;
619
+ transitionCodes.push(error.code);
620
+ pollCount += 1;
621
+ await waitForNextRouteProbe(deadline);
622
+ continue;
623
+ }
624
+ if (routeMatches(lastRoute, expected, expectedParams)) return lastRoute;
471
625
  pollCount += 1;
472
- await sleep(250);
626
+ await waitForNextRouteProbe(deadline);
473
627
  }
474
628
  const target = resolveMobileTarget(input);
475
629
  const deviceHint = [
@@ -483,13 +637,18 @@ export async function waitForRoute(input, expectedRoute, timeoutMs = 15000) {
483
637
  const lastReply = lastRoute === null
484
638
  ? 'empty/undefined (route transiently unavailable — bridge not yet settled)'
485
639
  : JSON.stringify(lastRoute);
486
- throw new Error(
640
+ const timeoutError = new Error(
487
641
  `Timed out waiting for Mobile route '${expected}' after ${timeoutMs}ms (${pollCount} polls).\n` +
488
642
  ` Expected route: ${expected}\n` +
643
+ ` Expected params: ${expectedParams === undefined ? 'any' : JSON.stringify(expectedParams)}\n` +
489
644
  ` Last parsed route: ${JSON.stringify(lastRoute)}\n` +
490
645
  ` Last bridge reply: ${lastReply}\n` +
646
+ ` Bridge transitions: ${transitionCodes.length > 0 ? [...new Set(transitionCodes)].join(', ') : 'none'}\n` +
491
647
  ` Device: ${deviceHint}`,
492
648
  );
649
+ throw lastTransitionError
650
+ ? coded(timeoutError, lastTransitionError.code)
651
+ : timeoutError;
493
652
  }
494
653
 
495
654
  export async function simulatorScreenshot(input, relPath) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deeeed/metamask-harness",
3
- "version": "0.33.2",
3
+ "version": "0.33.3",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mm-harness": "bin/mm-harness"
@@ -342,7 +342,9 @@ async function checkBehavior(browserEndpoint, port, pages) {
342
342
  },
343
343
  },
344
344
  });
345
- const button = document.querySelector('.prompt .copy');
345
+ // The onboarding prompt is the hero call to action; it was a collapsible
346
+ // before, so accept either shape.
347
+ const button = document.querySelector('.cmd-hero .copy, .prompt .copy');
346
348
  if (!button) throw new Error('copy button was not initialized');
347
349
  button.click();
348
350
  await new Promise((resolve) => setTimeout(resolve, 25));
@@ -390,6 +392,17 @@ async function checkBehavior(browserEndpoint, port, pages) {
390
392
 
391
393
  for (const relative of pages) {
392
394
  await withPage(browserEndpoint, `${base}${relative}`, async (page) => {
395
+ const copyControls = await page.evaluate(String.raw`
396
+ (() => ({
397
+ blocks: document.querySelectorAll('.cmd').length,
398
+ buttons: document.querySelectorAll('.cmd > .copy').length,
399
+ }))()
400
+ `);
401
+ if (copyControls.blocks !== copyControls.buttons) {
402
+ throw new Error(
403
+ `${relative} initialized ${copyControls.buttons} copy buttons for ${copyControls.blocks} command blocks`,
404
+ );
405
+ }
393
406
  const broken = await page.evaluate(String.raw`
394
407
  (async () => {
395
408
  const links = [...document.querySelectorAll('a[href]')]
@@ -413,6 +426,26 @@ async function checkBehavior(browserEndpoint, port, pages) {
413
426
  if (broken.length) {
414
427
  throw new Error(`${relative} has broken internal links: ${broken.join(', ')}`);
415
428
  }
429
+ for (const width of [834, 390]) {
430
+ await page.send('Emulation.setDeviceMetricsOverride', {
431
+ width,
432
+ height: 1000,
433
+ deviceScaleFactor: 1,
434
+ mobile: false,
435
+ });
436
+ const layout = await page.evaluate(String.raw`
437
+ (() => {
438
+ const header = document.querySelector('.topbar')?.getBoundingClientRect();
439
+ const nav = document.querySelector('.nav')?.getBoundingClientRect();
440
+ return {
441
+ navOverflow: Boolean(header && nav && nav.bottom > header.bottom + 1),
442
+ };
443
+ })()
444
+ `);
445
+ if (layout.navOverflow) {
446
+ throw new Error(`${relative} navigation escapes its header at ${width}px`);
447
+ }
448
+ }
416
449
  });
417
450
  }
418
451
  }
@@ -19,6 +19,7 @@
19
19
  <nav class="nav" aria-label="Main">
20
20
  <a href="index.html">Start Here</a>
21
21
  <a href="recipes.html">Recipes</a>
22
+ <a href="perps.html">Perps</a>
22
23
  <a href="cheatsheet.html">Cheatsheet</a>
23
24
  <a href="architecture.html" aria-current="page">Architecture</a>
24
25
  <a href="tutorials/index.html">Tutorials</a>
@@ -177,6 +178,12 @@
177
178
  these contracts. Evidence returns in the same shape either way, so a fleet result is reviewable by
178
179
  whoever wrote the recipe. Parity is structural: one spec, both sides.
179
180
  </p>
181
+ <p>
182
+ That is what makes the trust layer scale. A proof written once becomes a permanent guard: it is
183
+ replayed by whoever changes that code next, it stays readable as evidence no matter who or what ran
184
+ it, and the bug it was written for cannot quietly return. Review does not get weaker as volume
185
+ grows.
186
+ </p>
180
187
  <p>The seam shows in the CLI's own help — the flags a control plane supplies:</p>
181
188
  <div class="out">managed trust boundary (not needed for normal runs; Farmslot supplies it):
182
189
  --source-trust, --source-kind, --source-name, --source-digest,
@@ -196,7 +203,10 @@
196
203
 
197
204
  <section class="wrap">
198
205
  <h2 id="loop">The improvement loop</h2>
199
- <p>The stack is arranged so each run can leave the next one better informed.</p>
206
+ <p>
207
+ A recipe guards the regression it was written for. This is how a team catches the ones nobody has
208
+ written down yet — each run leaving the next one better informed.
209
+ </p>
200
210
 
201
211
  <div class="doctrine">
202
212
  <p>
@@ -465,7 +475,7 @@
465
475
  <footer class="footer">
466
476
  <div class="wrap">
467
477
  <p>Internal getting-started guide for the MetaMask agentic coding workflow. Not official MetaMask product documentation.</p>
468
- <p>Verified against mm-harness 0.26+.</p>
478
+ <p>Verified against mm-harness 0.33+.</p>
469
479
  </div>
470
480
  </footer>
471
481
 
@@ -136,9 +136,18 @@ main { padding-bottom: 6rem; }
136
136
 
137
137
  .brand-name { font-family: var(--mono); font-size: 0.95rem; }
138
138
 
139
- .nav { display: flex; gap: 0.3rem; margin-left: auto; flex-wrap: wrap; }
139
+ .nav {
140
+ display: flex;
141
+ gap: 0.3rem;
142
+ margin-left: auto;
143
+ min-width: 0;
144
+ overflow-x: auto;
145
+ flex-wrap: nowrap;
146
+ scrollbar-width: thin;
147
+ }
140
148
 
141
149
  .nav a {
150
+ flex: none;
142
151
  color: var(--text-dim);
143
152
  font-size: 0.885rem;
144
153
  font-weight: 500;
@@ -333,6 +342,16 @@ main { padding-bottom: 6rem; }
333
342
 
334
343
  @media (hover: none) { .copy { opacity: 1; } }
335
344
 
345
+ /* The one-prompt block is a call to action: its copy control never hides, and
346
+ the prompt scrolls rather than pushing the page down. */
347
+ .cmd-hero .copy { opacity: 1; }
348
+ .cmd-hero pre {
349
+ white-space: pre-wrap;
350
+ word-break: break-word;
351
+ max-height: 22rem;
352
+ overflow-y: auto;
353
+ }
354
+
336
355
  /* Expected-output block */
337
356
  .out {
338
357
  background: var(--bg-raised);
@@ -19,6 +19,7 @@
19
19
  <nav class="nav" aria-label="Main">
20
20
  <a href="index.html">Start Here</a>
21
21
  <a href="recipes.html">Recipes</a>
22
+ <a href="perps.html">Perps</a>
22
23
  <a href="cheatsheet.html" aria-current="page">Cheatsheet</a>
23
24
  <a href="architecture.html">Architecture</a>
24
25
  <a href="tutorials/index.html">Tutorials</a>
@@ -37,8 +38,8 @@
37
38
  same command. Filter by what you actually work on.
38
39
  </p>
39
40
  <div class="hero-meta">
40
- <span>mm-harness 0.26+</span>
41
- <span>one bin, 20 commands</span>
41
+ <span>mm-harness 0.33+</span>
42
+ <span>one bin, 21 commands</span>
42
43
  <span>--json on everything</span>
43
44
  </div>
44
45
  </section>
@@ -146,15 +147,15 @@
146
147
  <tr data-platforms="all"><td>A recipe with parameters</td><td><code>mm-harness run perps.clean-market-testnet market=BTC</code></td></tr>
147
148
  <tr data-platforms="all"><td>A recipe file on disk</td><td><code>mm-harness run ./my-recipe.json</code></td></tr>
148
149
  <tr data-platforms="all"><td>Add a team recipe library</td><td><code>mm-harness run &lt;recipe&gt; --library perps=/path/to/library</code></td></tr>
149
- <tr data-platforms="mobile"><td>Record video of the whole run</td><td><code>mm-harness run &lt;recipe&gt; --record-video=full-run</code></td></tr>
150
+ <tr data-platforms="extension mobile"><td>Record video of the whole run (Extension or iOS)</td><td><code>mm-harness run &lt;recipe&gt; --record-video=full-run</code></td></tr>
150
151
  <tr data-platforms="all"><td>Streaming progress for an agent</td><td><code>mm-harness run &lt;recipe&gt; --json-stream</code></td></tr>
151
152
  <tr data-platforms="all"><td>What did I last run, and how did it go</td><td><code>mm-harness last</code></td></tr>
152
153
  </tbody>
153
154
  </table>
154
155
  </div>
155
156
  <p class="step-why" style="font-size:.92rem">
156
- iOS carries video; Extension capture is screenshots only. That asymmetry is real — do not promise
157
- a reviewer a video of an extension run.
157
+ Extension and iOS can record full-run video through capture-helper. Android replay video is not
158
+ implemented yet; use screenshot evidence there.
158
159
  </p>
159
160
  </div>
160
161
 
@@ -296,7 +297,7 @@
296
297
  <footer class="footer">
297
298
  <div class="wrap-wide">
298
299
  <p>Internal getting-started guide for the MetaMask agentic coding workflow. Not official MetaMask product documentation.</p>
299
- <p>Verified against mm-harness 0.26+.</p>
300
+ <p>Verified against mm-harness 0.33+.</p>
300
301
  </div>
301
302
  </footer>
302
303
 
package/site/index.html CHANGED
@@ -19,6 +19,7 @@
19
19
  <nav class="nav" aria-label="Main">
20
20
  <a href="index.html" aria-current="page">Start Here</a>
21
21
  <a href="recipes.html">Recipes</a>
22
+ <a href="perps.html">Perps</a>
22
23
  <a href="cheatsheet.html">Cheatsheet</a>
23
24
  <a href="architecture.html">Architecture</a>
24
25
  <a href="tutorials/index.html">Tutorials</a>
@@ -39,30 +40,118 @@
39
40
 
40
41
  <main>
41
42
  <section class="wrap hero">
42
- <span class="eyebrow">Agentic coding at MetaMask</span>
43
+ <span class="eyebrow">A proposed agentic workflow at MetaMask</span>
43
44
  <h1>Drive the wallet. Prove what happened.</h1>
44
45
  <p class="lede">
45
46
  <code>mm-harness</code> launches a real MetaMask build — Extension, Mobile, or Core — drives it
46
47
  through typed actions, and writes evidence a reviewer can check without taking your word for it.
47
- Eight steps from an empty terminal to a green run you can open.
48
+ Paste one prompt and let your agent do it, or walk the eight steps yourself.
48
49
  </p>
49
50
  <div class="hero-meta">
50
- <span>requires mm-harness 0.26+</span>
51
+ <span>requires mm-harness 0.33+</span>
51
52
  <span>~20 minutes</span>
52
53
  <span>progress saves in this browser</span>
53
54
  </div>
55
+ <p style="margin-top:.9rem;opacity:.7;font-size:.9rem;max-width:60ch">
56
+ A proposal for how agents can code at MetaMask — offered for teams to try, not an official standard.
57
+ </p>
54
58
  </section>
55
59
 
56
60
  <section class="wrap">
57
- <h2 id="model">Two words</h2>
61
+ <div class="agent-card">
62
+ <h2 style="margin-top:0">Just want to try it? Paste this into your agent.</h2>
63
+ <p>
64
+ Start your agent in or near a MetaMask checkout. This installs the harness, proves control of a
65
+ real build, runs a recipe, and re-runs it — end to end, without reading the rest of this page.
66
+ </p>
67
+ <div class="cmd cmd-hero"><pre><code>Task: set me up with the MetaMask recipe workflow and prove it works end to end.
68
+ Follow these steps in order. When a command fails, its message names the exact
69
+ next command — run that instead of improvising.
70
+
71
+ 1. Install the harness: `npm i -g @deeeed/metamask-harness@latest`, then
72
+ `mm-harness --version` to confirm it resolved.
73
+ 2. Checkout: if I started you inside a MetaMask product checkout
74
+ (metamask-extension, metamask-mobile, or core), use it. If not, run
75
+ `mm-harness setup-base --dry-run` to show me the layout it would create,
76
+ and ask me before running it for real.
77
+ 3. From inside the checkout, run `mm-harness doctor` and fix every finding by
78
+ following its own instructions until doctor passes. `mm-harness doctor --fix`
79
+ repairs harness-owned runtime state; it will not invent credentials or pick
80
+ a wallet fixture for me.
81
+ 4. Run `mm-harness fixtures`. If no fixture exists, run
82
+ `mm-harness fixtures init --dev`; this creates a disposable public test
83
+ wallet that must never receive real funds.
84
+ 5. Launch and wait for readiness:
85
+ - Extension: `mm-harness launch --verify`.
86
+ - Android: `mm-harness launch android --verify`; if more than one device is
87
+ listed, choose one serial and repeat it with `--device &lt;serial&gt;`.
88
+ - iOS: run `xcrun simctl list devices available`, choose ONE available iPhone
89
+ UUID (ask me if the choice is ambiguous), and use that literal UUID in all
90
+ three commands below — never substitute the simulator name:
91
+ `mm-harness provision runway ios --device '&lt;UUID&gt;' --force`
92
+ `xcrun simctl get_app_container '&lt;UUID&gt;' io.metamask.MetaMask app`
93
+ `mm-harness launch ios --device '&lt;UUID&gt;' --verify`
94
+ - Core: `mm-harness verify`; it has no app to launch.
95
+ `--preflight-mode` is an internal adapter flag, not a public launch flag.
96
+ 6. On Extension run `mm-harness fixtures set`. On Mobile include the same
97
+ `--device &lt;UUID-or-serial&gt;` used for launch. Wait for it to succeed.
98
+ 7. Discover before acting: run `mm-harness actions`. That list is the ONLY set
99
+ of capabilities you may use, now and in every later session. Never invent an
100
+ action or a flag. If you need something not listed, stop and tell me.
101
+ 8. Prove control with two calls. On Extension or Mobile, run
102
+ `mm-harness call read_state` and one listed visible UI action. On Core, run
103
+ `mm-harness call read_positions mode=all` and
104
+ `mm-harness call command cmd="echo core-ready"`. Show me both outputs.
105
+ 9. Prove a whole task: pick a smoke recipe from `mm-harness run --list`, then
106
+ `mm-harness run &lt;recipe&gt; --artifacts-dir ./first-recipe-artifacts`.
107
+ Show me the status from summary.json, the per-node results from trace.json,
108
+ and the artifact paths.
109
+ 10. Without starting a new session, run that same recipe again and confirm it
110
+ still passes. Checking your own work in the context you did it in is the
111
+ point; from here on, replay it after every change you make.
112
+ 11. Tell me in three sentences: what a recipe proves, where the evidence lives,
113
+ and which command lists what I can do next.
114
+
115
+ Not optional: evidence comes only from executed actions — never fabricate a
116
+ result or edit state to manufacture one. If the harness reports a capability as
117
+ unsupported, stop and tell me rather than working around it. If a step fails
118
+ twice after following its error's instructions, stop and show me the exact
119
+ error.</code></pre></div>
120
+ <p style="margin-bottom:0">
121
+ Prefer to go step by step? <a href="#steps">Every piece is below.</a>
122
+ </p>
123
+ </div>
124
+ </section>
125
+
126
+ <section class="wrap">
127
+ <h2 id="why">Why a recipe</h2>
58
128
  <p>
59
- An <strong>action</strong> is one typed operation: unlock the wallet, press a button, read the
60
- account state. A <strong>recipe</strong> is a JSON graph of actions that proves a task. Run one
61
- against a real app and the harness records what happened — a per-step trace, a verdict,
62
- screenshots. The steps below follow <strong>discover → drive → prove</strong>, which is also the
63
- order you will work in from here on.
129
+ <strong>Proof.</strong> The agent drives a real build, asserts what should be true, and leaves a
130
+ trace: which actions ran, what they returned, how many times. On-device evidence, not a claim in a
131
+ pull request description.
132
+ </p>
133
+ <p>
134
+ <strong>Checked before you ever see it.</strong> Replaying a recipe needs no fresh session, no
135
+ human, and no wait for CI. The agent does it mid-task, in the same context it made the change in —
136
+ so what arrives at your review has already been verified by the thing that wrote it.
137
+ </p>
138
+ <p>
139
+ <strong>Prevents regressions.</strong> The recipe outlives the change it was written for. Every
140
+ later change has to pass it too, so the same bug cannot quietly come back: a one-time check becomes
141
+ a permanent guard.
142
+ </p>
143
+ <p>
144
+ <strong>Trust.</strong> Proven, self-checked, and still proving — so review reads evidence instead
145
+ of extending credit, and every gate stays yours to steer.
64
146
  </p>
147
+ </section>
148
+
149
+ <section class="wrap">
150
+ <h2 id="model">Two words</h2>
65
151
  <p>
152
+ An <strong>action</strong> is one typed operation: unlock the wallet, press a button, read the
153
+ account state. A <strong>recipe</strong> is a JSON graph of actions. The steps below follow
154
+ <strong>discover → drive → prove</strong>, which is also the order you will work in from here on.
66
155
  <code>mm-harness</code> implements the farmslot specification —
67
156
  <a href="architecture.html#spec">see Architecture</a>.
68
157
  </p>
@@ -78,64 +167,8 @@
78
167
  </section>
79
168
 
80
169
  <section class="wrap">
81
- <div class="agent-card">
82
- <h2 style="margin-top:0">Or hand it to your agent</h2>
83
- <p>
84
- Start your agent in (or near) a MetaMask checkout and paste the prompt below. It walks the same
85
- eight steps, following each error's own instructions rather than improvising.
86
- </p>
87
- <p>
88
- Do them by hand once anyway — you will be reviewing this workflow's output later, and a bundle
89
- you produced yourself is much easier to trust.
90
- </p>
91
- <details class="prompt">
92
- <summary>Show the copyable prompt</summary>
93
- <div class="prompt-body">
94
- <div class="cmd"><pre><code>Task: set me up with the MetaMask recipe workflow and prove it works end-to-end.
95
- Follow these steps exactly. Where a tool prints an error, its message contains the
96
- exact next command — follow that instead of improvising.
97
-
98
- 1. Install or update the harness: `npm i -g @deeeed/metamask-harness@latest`,
99
- then `mm-harness --version` to confirm it resolved.
100
- 2. Repo: use the product checkout I started you in (metamask-extension,
101
- metamask-mobile, or core). If I started you outside one, ask me which ONE
102
- product to clone and clone just that — do not clone multiples.
103
- 3. Run `mm-harness doctor` from the checkout and fix everything it reports by
104
- following each finding's own instructions until doctor passes. `mm-harness
105
- doctor --fix` repairs harness-owned runtime state; it will not invent
106
- credentials or choose a wallet fixture for me.
107
- 4. Launch the app under harness control: `mm-harness launch` (add `ios` or
108
- `android` for mobile; core is headless and has nothing to launch — use
109
- `mm-harness verify` there). Wait for the runtime to be ready.
110
- 5. Discovery before anything else: run `mm-harness actions`. That list is the
111
- ONLY set of capabilities you may use, now and in every future session.
112
- Never invent an action or a flag. If you need something that is not listed,
113
- stop and tell me.
114
- 6. Prove basic control with two direct calls. On Extension or Mobile, run
115
- `mm-harness call read_state` and one listed visible UI action. On Core, run
116
- `mm-harness call read_positions mode=all` and
117
- `mm-harness call command cmd="echo core-ready"`. Show me both outputs.
118
- 7. Run one library recipe end-to-end. Pick a smoke recipe from
119
- `mm-harness run --list`, then:
120
- `mm-harness run &lt;recipe&gt; --artifacts-dir ./first-recipe-artifacts`
121
- When it finishes, show me: the status from summary.json, the per-node
122
- results from trace.json, and the artifact paths.
123
- 8. Close by telling me, in three sentences: what a recipe is, where the
124
- evidence lives, and which command lists what I can do next.
125
-
126
- Rules that are not optional: evidence comes only from executed actions — never
127
- fabricate a result or edit state to manufacture one; if the harness says a
128
- capability is unsupported, stop and report it rather than working around it;
129
- if any step fails twice after following its error's instructions, stop and show
130
- me the exact error.</code></pre></div>
131
- </div>
132
- </details>
133
- </div>
134
- </section>
135
-
136
- <section class="wrap">
137
- <h2 id="steps">The walkthrough</h2>
138
- <p>Checkboxes persist in this browser, so you can close the tab and come back.</p>
170
+ <h2 id="steps">Prefer to go step by step?</h2>
171
+ <p>Here is each piece on its own. Checkboxes persist in this browser, so you can close the tab and come back.</p>
139
172
 
140
173
  <ol class="steps">
141
174
 
@@ -172,7 +205,7 @@ me the exact error.</code></pre></div>
172
205
  <span class="p">$ </span>mm-harness --version</code></pre></div>
173
206
 
174
207
  <div class="out-label">Expected</div>
175
- <div class="out">0.26.x <span class="dim">— any 0.26 or newer</span></div>
208
+ <div class="out">0.33.x <span class="dim">— any 0.33 or newer</span></div>
176
209
 
177
210
  <p class="step-why">Everything from here runs inside a product checkout:</p>
178
211
  <div class="cmd"><pre><code><span class="p">$ </span>cd ~/dev/metamask/metamask-extension <span class="dim"># or metamask-mobile, or core</span></code></pre></div>
@@ -186,7 +219,7 @@ me the exact error.</code></pre></div>
186
219
  directory to your shell profile.
187
220
  </p>
188
221
  <p>
189
- <strong>Older than 0.26</strong> — a stale global install is the most common source of
222
+ <strong>Older than 0.33</strong> — a stale global install is the most common source of
190
223
  "the docs don't match my terminal". Run <code>mm-harness update</code>, or
191
224
  <code>update --check</code> to look without installing.
192
225
  </p>
@@ -208,7 +241,7 @@ me the exact error.</code></pre></div>
208
241
 
209
242
  <div class="out-label">Expected — a checkout that needs one repair</div>
210
243
  <div class="out"><span class="ok">pass</span> extension bridge present manifest=…/extension.action-manifest.json
211
- harness: @deeeed/metamask-harness@0.26.x
244
+ harness: @deeeed/metamask-harness@0.33.x
212
245
  runtime: decision=install (deps-missing) deps=missing webpack=down
213
246
  Dependencies are not installed (no yarn install-state markers).
214
247
  runtime-context: temp/recipe/runtime/agentic-runtime.json (<span class="bad">absent</span> — run mm-harness doctor --fix)
@@ -264,13 +297,16 @@ capture: <span class="ok">pass</span> (screenshots: capture-helper → cdp; vide
264
297
  </div>
265
298
  <p class="step-why">
266
299
  Starts the app and its dev server, installing the runtime overlay first if missing. Platform is
267
- auto-detected; mobile needs an explicit target because the harness will not pick a simulator for
268
- you.
300
+ auto-detected. On iOS, pin one simulator UUID through provisioning and launch so duplicate device
301
+ names cannot select different simulators.
269
302
  </p>
270
303
 
271
- <div class="cmd"><pre><code><span class="p">$ </span>mm-harness launch <span class="dim"># extension — fullscreen by default</span>
272
- <span class="p">$ </span>mm-harness launch ios <span class="dim"># mobile target is mandatory</span>
273
- <span class="p">$ </span>mm-harness launch android</code></pre></div>
304
+ <div class="cmd"><pre><code><span class="p">$ </span>mm-harness launch --verify <span class="dim"># extension — fullscreen by default</span>
305
+ <span class="p">$ </span>xcrun simctl list devices available <span class="dim"># choose one iPhone UUID</span>
306
+ <span class="p">$ </span>mm-harness provision runway ios --device '&lt;UUID&gt;' --force
307
+ <span class="p">$ </span>xcrun simctl get_app_container '&lt;UUID&gt;' io.metamask.MetaMask app
308
+ <span class="p">$ </span>mm-harness launch ios --device '&lt;UUID&gt;' --verify
309
+ <span class="p">$ </span>mm-harness launch android --verify</code></pre></div>
274
310
 
275
311
  <p class="step-why">
276
312
  Quick relaunch is the default and reuses a healthy runtime. <code>--build</code> is the escape
@@ -290,7 +326,13 @@ capture: <span class="ok">pass</span> (screenshots: capture-helper → cdp; vide
290
326
  <div class="fail-body">
291
327
  <p>
292
328
  <strong>Mobile: more than one device</strong> — the harness lists what it found rather than
293
- guessing. Re-run with <code>--device &lt;udid|serial|name&gt;</code>.
329
+ guessing. Re-run with <code>--device &lt;udid|serial&gt;</code>. Prefer the iOS UUID over a name,
330
+ because Xcode can contain several simulators with the same display name.
331
+ </p>
332
+ <p>
333
+ <strong>iOS app missing after provisioning</strong> — repeat the provision command with
334
+ <code>--force</code> and the same UUID. Do not use <code>--preflight-mode</code>; it is an
335
+ internal adapter flag, not a public <code>mm-harness launch</code> option.
294
336
  </p>
295
337
  <p>
296
338
  <strong>Stuck</strong> — <code>mm-harness logs</code> tails the dev server and app logs;
@@ -638,7 +680,7 @@ artifacts (6):
638
680
  <footer class="footer">
639
681
  <div class="wrap">
640
682
  <p>Internal getting-started guide for the MetaMask agentic coding workflow. Not official MetaMask product documentation.</p>
641
- <p>Verified against mm-harness 0.26+. If a command here disagrees with your terminal, your terminal is right: run <code>mm-harness update</code>, then trust <code>--help</code>.</p>
683
+ <p>Verified against mm-harness 0.33+. If a command here disagrees with your terminal, your terminal is right: run <code>mm-harness update</code>, then trust <code>--help</code>.</p>
642
684
  </div>
643
685
  </footer>
644
686
 
@@ -0,0 +1,195 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Perps recipes — MetaMask recipe workflow</title>
7
+ <meta name="description" content="Install the shared MetaMask Perps recipe library and run live, testnet-first proof across Extension, Mobile, and Core.">
8
+ <link rel="stylesheet" href="assets/style.css">
9
+ </head>
10
+ <body>
11
+ <a class="skip" href="#setup">Skip to setup</a>
12
+
13
+ <header class="topbar">
14
+ <div class="wrap topbar-inner">
15
+ <a class="brand" href="index.html">
16
+ <span class="brand-mark" aria-hidden="true"></span>
17
+ <span class="brand-name">mm-harness</span>
18
+ </a>
19
+ <nav class="nav" aria-label="Main">
20
+ <a href="index.html">Start Here</a>
21
+ <a href="recipes.html">Recipes</a>
22
+ <a href="perps.html" aria-current="page">Perps</a>
23
+ <a href="cheatsheet.html">Cheatsheet</a>
24
+ <a href="architecture.html">Architecture</a>
25
+ <a href="tutorials/index.html">Tutorials</a>
26
+ <a href="reviewers.html">For Reviewers</a>
27
+ </nav>
28
+ </div>
29
+ </header>
30
+
31
+ <main>
32
+ <section class="wrap hero" style="padding-bottom:1rem">
33
+ <span class="eyebrow">Shared MetaMask recipe library</span>
34
+ <h1>Prove Perps changes in the real product</h1>
35
+ <p class="lede">
36
+ The private Perps library gives Extension, Mobile, and Core one set of small, parameterized
37
+ recipes for live UI journeys, controller state, analytics, performance, and testnet mutations.
38
+ </p>
39
+ <div class="hero-meta">
40
+ <span>library name: perps</span>
41
+ <span>defaults to testnet</span>
42
+ <span>requires repository access</span>
43
+ </div>
44
+ </section>
45
+
46
+ <section class="wrap">
47
+ <div class="doctrine">
48
+ <p>
49
+ <span class="k">The harness supplies the execution standard; the Perps library supplies the domain proof.</span>
50
+ Keep the shared library small: compose or repair the closest recipe before creating another one.
51
+ </p>
52
+ </div>
53
+
54
+ <h2>What lives where</h2>
55
+ <div class="table-scroll">
56
+ <table>
57
+ <thead><tr><th>Layer</th><th>Owns</th><th>Does not own</th></tr></thead>
58
+ <tbody>
59
+ <tr>
60
+ <td><code>mm-harness</code></td>
61
+ <td>Launch, typed actions, graph execution, screenshots, video, traces, and artifact contracts.</td>
62
+ <td>Ticket-specific Perps scenarios.</td>
63
+ </tr>
64
+ <tr>
65
+ <td><code>perps</code> library</td>
66
+ <td>Reusable Perps setup, journeys, assertions, cleanup, and platform variants.</td>
67
+ <td>Wallet credentials, generated evidence, or product source.</td>
68
+ </tr>
69
+ <tr>
70
+ <td>Product checkout</td>
71
+ <td>The real Extension, Mobile, or Core code and runtime being proved.</td>
72
+ <td>A substitute test implementation.</td>
73
+ </tr>
74
+ </tbody>
75
+ </table>
76
+ </div>
77
+ </section>
78
+
79
+ <section class="wrap">
80
+ <h2 id="setup">Install and discover it</h2>
81
+ <p>
82
+ Run these commands from the MetaMask product checkout you want to exercise. The repository is
83
+ private; a clone failure means your GitHub account needs access.
84
+ </p>
85
+
86
+ <div class="cmd"><pre><code><span class="p">$ </span>npm i -g @deeeed/metamask-harness@latest
87
+ <span class="p">$ </span>git clone git@github.com:MetaMask/experimental-metamask-recipe-perps.git \
88
+ "$HOME/shared-library/metamask-recipe-perps"
89
+ <span class="p">$ </span>mm-harness run --list \
90
+ --library "perps=$HOME/shared-library/metamask-recipe-perps"</code></pre></div>
91
+
92
+ <p>
93
+ If the checkout already exists and is clean, update it with <code>git pull --ff-only</code>.
94
+ Preserve and report a dirty checkout instead of overwriting it. Keep <code>--library</code> on each
95
+ command so separate tool shells do not lose an exported variable.
96
+ </p>
97
+
98
+ <div class="note blue">
99
+ <span class="note-title">Discovery is adapter-aware</span>
100
+ <p>
101
+ The list shows only recipes runnable in the current product checkout. A recipe marked
102
+ <code>[perps]</code> came from the shared library; <code>[metamask]</code> means it ships with the
103
+ harness. Read the selected platform variant with <code>--describe</code> before running it.
104
+ </p>
105
+ </div>
106
+ </section>
107
+
108
+ <section class="wrap">
109
+ <h2>Your first live proof</h2>
110
+ <p>
111
+ Complete <a href="index.html#steps">Start Here</a> first so the app, device, and wallet fixture are
112
+ ready. Then use a read-only recipe as the first proof.
113
+ </p>
114
+
115
+ <h3>Extension or Mobile</h3>
116
+ <div class="cmd"><pre><code><span class="p">$ </span>mm-harness run perps.open-market --describe \
117
+ --library "perps=$HOME/shared-library/metamask-recipe-perps"
118
+ <span class="p">$ </span>mm-harness run perps.open-market network=testnet market=BTC --plan \
119
+ --library "perps=$HOME/shared-library/metamask-recipe-perps"
120
+ <span class="p">$ </span>mm-harness run perps.open-market network=testnet market=BTC \
121
+ --record-video=full-run --artifacts-dir ./perps-open-market-evidence \
122
+ --library "perps=$HOME/shared-library/metamask-recipe-perps"</code></pre></div>
123
+
124
+ <h3>Core</h3>
125
+ <div class="cmd"><pre><code><span class="p">$ </span>mm-harness run perps.snapshot-market --describe \
126
+ --library "perps=$HOME/shared-library/metamask-recipe-perps"
127
+ <span class="p">$ </span>mm-harness run perps.snapshot-market network=testnet market=BTC \
128
+ --artifacts-dir ./perps-market-evidence \
129
+ --library "perps=$HOME/shared-library/metamask-recipe-perps"</code></pre></div>
130
+
131
+ <p>
132
+ A passing run writes <code>summary.json</code>, per-node results in <code>trace.json</code>, and an
133
+ artifact manifest beside screenshots or video. Visual review surfaces can render those same
134
+ recipe-derived artifacts without changing the proof graph.
135
+ </p>
136
+ </section>
137
+
138
+ <section class="wrap">
139
+ <h2>Choose the recipe that owns the claim</h2>
140
+ <div class="table-scroll">
141
+ <table>
142
+ <thead><tr><th>Claim</th><th>Recipe</th><th>Platforms</th></tr></thead>
143
+ <tbody>
144
+ <tr><td>Reach a live market detail screen</td><td><code>perps.open-market</code></td><td>Mobile, Extension</td></tr>
145
+ <tr><td>Read positions and orders</td><td><code>perps.snapshot-market</code></td><td>Mobile, Extension, Core</td></tr>
146
+ <tr><td>Place, assert, and clean up an order</td><td><code>perps.prove-order</code></td><td>Mobile, Extension, Core</td></tr>
147
+ <tr><td>Manage or close a position</td><td><code>perps.manage-position</code></td><td>Mobile, Extension, Core</td></tr>
148
+ <tr><td>Prove real MetaMetrics emissions</td><td><code>perps.analytics-lifecycle</code></td><td>Mobile, Extension</td></tr>
149
+ <tr><td>Measure the open-market journey</td><td><code>perps.performance-open-market</code></td><td>Mobile, Extension</td></tr>
150
+ <tr><td>Exercise edit-order contracts</td><td><code>perps.prove-edit-order</code></td><td>Core</td></tr>
151
+ </tbody>
152
+ </table>
153
+ </div>
154
+
155
+ <div class="note">
156
+ <span class="note-title">Mutation boundary</span>
157
+ <p>
158
+ Always run <code>--describe</code> and <code>--plan</code> before a state-changing recipe. The
159
+ library defaults to testnet and cleans up testnet state. Never turn an onboarding proof into a
160
+ mainnet mutation; mainnet operations require the recipe's explicit real-funds confirmation.
161
+ </p>
162
+ </div>
163
+ </section>
164
+
165
+ <section class="wrap">
166
+ <h2>Maintain the proof, not a pile of scenarios</h2>
167
+ <ol>
168
+ <li>Parameterize stable choices such as market, side, order type, and network.</li>
169
+ <li>Compose existing setup, assertion, and cleanup recipes before adding a new top-level recipe.</li>
170
+ <li>Add a platform variant only when the products genuinely differ.</li>
171
+ <li>
172
+ If a locator or screen assertion fails, classify it first. Intentional product change may mean
173
+ small recipe drift; otherwise preserve the failure and fix the product, runtime, or harness layer
174
+ that owns it.
175
+ </li>
176
+ <li>Re-run every caller after a shared node changes and attach the fresh evidence.</li>
177
+ </ol>
178
+
179
+ <div class="btn-row">
180
+ <a class="btn btn-primary" href="tutorials/v3.html">Install skills and the library →</a>
181
+ <a class="btn btn-ghost" href="recipes.html">How recipes prove claims</a>
182
+ <a class="btn btn-ghost" href="reviewers.html">How to review evidence</a>
183
+ </div>
184
+ </section>
185
+ </main>
186
+
187
+ <footer class="footer">
188
+ <div class="wrap">
189
+ <p>Internal getting-started guide for the MetaMask agentic coding workflow. Not official MetaMask product documentation.</p>
190
+ <p>Verified against mm-harness 0.33+ and the shared MetaMask Perps recipe library.</p>
191
+ </div>
192
+ </footer>
193
+ <script type="module" src="assets/progress.mjs"></script>
194
+ </body>
195
+ </html>
package/site/recipes.html CHANGED
@@ -19,6 +19,7 @@
19
19
  <nav class="nav" aria-label="Main">
20
20
  <a href="index.html">Start Here</a>
21
21
  <a href="recipes.html" aria-current="page">Recipes</a>
22
+ <a href="perps.html">Perps</a>
22
23
  <a href="cheatsheet.html">Cheatsheet</a>
23
24
  <a href="architecture.html">Architecture</a>
24
25
  <a href="tutorials/index.html">Tutorials</a>
@@ -48,6 +49,27 @@
48
49
  </div>
49
50
 
50
51
  <p>Every clause there does a job against a specific failure mode. Here they are, one at a time.</p>
52
+
53
+ <h2 id="why">What it is for</h2>
54
+ <p>
55
+ <strong>Proof.</strong> Actions drive a real build and assertions check what should be true —
56
+ counted, where a count is what distinguishes working from nearly working. What lands is on-device
57
+ evidence, not a claim in a pull request description.
58
+ </p>
59
+ <p>
60
+ <strong>Self-checking, in the same context.</strong> The agent does not need a fresh session, a
61
+ reviewer, or a CI round-trip to know whether its change held. It replays the recipe mid-task, where
62
+ it is already working, and reads the verdict itself — the inner loop, closed before handoff.
63
+ </p>
64
+ <p>
65
+ <strong>Prevents regressions.</strong> The outer loop is the same recipe replayed against every
66
+ later change. Written once, it keeps refusing the bug it was written for, which is why a recipe is
67
+ worth authoring rather than checking by hand: the check survives the task.
68
+ </p>
69
+ <p>
70
+ <strong>Trust.</strong> Proven, self-checked, and still proving. Review reads evidence rather than
71
+ extending credit, and every gate stays yours to steer.
72
+ </p>
51
73
  </section>
52
74
 
53
75
  <section class="wrap">
@@ -387,7 +409,7 @@
387
409
  <footer class="footer">
388
410
  <div class="wrap">
389
411
  <p>Internal getting-started guide for the MetaMask agentic coding workflow. Not official MetaMask product documentation.</p>
390
- <p>Verified against mm-harness 0.26+. The annotated recipe is a bundled library recipe, abridged.</p>
412
+ <p>Verified against mm-harness 0.33+. The annotated recipe is a bundled library recipe, abridged.</p>
391
413
  </div>
392
414
  </footer>
393
415
 
@@ -19,6 +19,7 @@
19
19
  <nav class="nav" aria-label="Main">
20
20
  <a href="index.html">Start Here</a>
21
21
  <a href="recipes.html">Recipes</a>
22
+ <a href="perps.html">Perps</a>
22
23
  <a href="cheatsheet.html">Cheatsheet</a>
23
24
  <a href="architecture.html">Architecture</a>
24
25
  <a href="tutorials/index.html">Tutorials</a>
@@ -365,7 +366,7 @@ Nodes: 6/6 passed
365
366
  <footer class="footer">
366
367
  <div class="wrap">
367
368
  <p>Internal getting-started guide for the MetaMask agentic coding workflow. Not official MetaMask product documentation.</p>
368
- <p>Verified against mm-harness 0.26+. Example bundle is a real run with identifying values redacted.</p>
369
+ <p>Verified against mm-harness 0.33+. Example bundle is a real run with identifying values redacted.</p>
369
370
  </div>
370
371
  </footer>
371
372
 
@@ -19,6 +19,7 @@
19
19
  <nav class="nav" aria-label="Main">
20
20
  <a href="../index.html">Start Here</a>
21
21
  <a href="../recipes.html">Recipes</a>
22
+ <a href="../perps.html">Perps</a>
22
23
  <a href="../cheatsheet.html">Cheatsheet</a>
23
24
  <a href="../architecture.html">Architecture</a>
24
25
  <a href="index.html" aria-current="page">Tutorials</a>
@@ -171,7 +172,7 @@
171
172
  <footer class="footer">
172
173
  <div class="wrap-wide">
173
174
  <p>Internal getting-started guide for the MetaMask agentic coding workflow. Not official MetaMask product documentation.</p>
174
- <p>Verified against mm-harness 0.26+.</p>
175
+ <p>Verified against mm-harness 0.33+.</p>
175
176
  </div>
176
177
  </footer>
177
178
 
@@ -19,6 +19,7 @@
19
19
  <nav class="nav" aria-label="Main">
20
20
  <a href="../index.html">Start Here</a>
21
21
  <a href="../recipes.html">Recipes</a>
22
+ <a href="../perps.html">Perps</a>
22
23
  <a href="../cheatsheet.html">Cheatsheet</a>
23
24
  <a href="../architecture.html">Architecture</a>
24
25
  <a href="index.html" aria-current="page">Tutorials</a>
@@ -103,7 +104,7 @@
103
104
  <p class="step-why">Install globally, confirm it resolved, then work from inside a product checkout.</p>
104
105
  <div class="cmd"><pre><code><span class="p">$ </span>npm i -g @deeeed/metamask-harness@latest
105
106
  <span class="p">$ </span>mm-harness --version</code></pre></div>
106
- <div class="out">0.26.x <span class="dim">— any 0.26 or newer</span></div>
107
+ <div class="out">0.33.x <span class="dim">— any 0.33 or newer</span></div>
107
108
  </li>
108
109
 
109
110
  <li class="step" data-step="doctor">
@@ -202,7 +203,7 @@
202
203
  <footer class="footer">
203
204
  <div class="wrap">
204
205
  <p>Internal getting-started guide for the MetaMask agentic coding workflow. Not official MetaMask product documentation.</p>
205
- <p>Verified against mm-harness 0.26+.</p>
206
+ <p>Verified against mm-harness 0.33+.</p>
206
207
  </div>
207
208
  </footer>
208
209
 
@@ -19,6 +19,7 @@
19
19
  <nav class="nav" aria-label="Main">
20
20
  <a href="../index.html">Start Here</a>
21
21
  <a href="../recipes.html">Recipes</a>
22
+ <a href="../perps.html">Perps</a>
22
23
  <a href="../cheatsheet.html">Cheatsheet</a>
23
24
  <a href="../architecture.html">Architecture</a>
24
25
  <a href="index.html" aria-current="page">Tutorials</a>
@@ -77,7 +78,7 @@
77
78
  <tr><td><code>0:40</code></td><td><code>recipe.json</code> — the graph that executed. Each node's <code>intent</code> is the argument being made.</td></tr>
78
79
  <tr><td><code>1:40</code></td><td><code>trace.json</code> — per-node verdicts, timings, outputs. Counts come from here.</td></tr>
79
80
  <tr><td><code>2:50</code></td><td><code>summary.json</code> — the verdict, the totals, which libraries were in scope.</td></tr>
80
- <tr><td><code>3:30</code></td><td>Screenshots, and on Mobile the run video. Extension captures stills only.</td></tr>
81
+ <tr><td><code>3:30</code></td><td>Screenshots, plus full-run video on Extension and iOS.</td></tr>
81
82
  <tr><td><code>4:20</code></td><td><code>diagnostics.json</code> — application warnings, quarantined from the verdict.</td></tr>
82
83
  <tr><td><code>5:10</code></td><td>The two rules that make the whole thing worth reading.</td></tr>
83
84
  </tbody>
@@ -106,9 +107,9 @@
106
107
  <div class="note blue">
107
108
  <span class="note-title">Capture is not symmetric</span>
108
109
  <p>
109
- Mobile records video of a full run; Extension captures screenshots only. Do not promise a reviewer
110
- a video of an Extension run. <code>mm-harness doctor</code> reports what the current checkout can
111
- capture on its <code>capture:</code> line.
110
+ Extension and iOS record full-run video through capture-helper; Android replay video is not
111
+ implemented yet. <code>mm-harness doctor</code> reports what the current checkout can capture on its
112
+ <code>capture:</code> line.
112
113
  </p>
113
114
  </div>
114
115
  </section>
@@ -198,7 +199,7 @@
198
199
  <footer class="footer">
199
200
  <div class="wrap">
200
201
  <p>Internal getting-started guide for the MetaMask agentic coding workflow. Not official MetaMask product documentation.</p>
201
- <p>Verified against mm-harness 0.26+.</p>
202
+ <p>Verified against mm-harness 0.33+.</p>
202
203
  </div>
203
204
  </footer>
204
205
 
@@ -19,6 +19,7 @@
19
19
  <nav class="nav" aria-label="Main">
20
20
  <a href="../index.html">Start Here</a>
21
21
  <a href="../recipes.html">Recipes</a>
22
+ <a href="../perps.html">Perps</a>
22
23
  <a href="../cheatsheet.html">Cheatsheet</a>
23
24
  <a href="../architecture.html">Architecture</a>
24
25
  <a href="index.html" aria-current="page">Tutorials</a>
@@ -84,6 +85,46 @@
84
85
  <section class="wrap">
85
86
  <h2 id="steps">Set it up</h2>
86
87
 
88
+ <div class="agent-card">
89
+ <h3 style="margin-top:0">Paste this into your agent</h3>
90
+ <div class="cmd cmd-hero"><pre><code>Task: install the latest recipe skills and shared MetaMask Perps recipe library,
91
+ then prove one real recipe runs end to end.
92
+
93
+ 1. Work from the MetaMask product checkout I started you in. Run
94
+ `npm i -g @deeeed/metamask-harness@latest` and `mm-harness --version`.
95
+ 2. Install the latest Consensys recipe skills into this checkout:
96
+ `yarn skills --maturity experimental --include
97
+ agentic/recipe-cook,agentic/recipe-quality --save`
98
+ This command updates the skills sources before syncing. Show me that
99
+ recipe-cook and recipe-quality were installed.
100
+ 3. Use this exact library directory:
101
+ `$HOME/shared-library/metamask-recipe-perps`.
102
+ If it does not exist, clone
103
+ `git@github.com:MetaMask/experimental-metamask-recipe-perps.git` there.
104
+ If it exists and is clean, update it with `git pull --ff-only`; if it is
105
+ dirty, preserve it and report that instead of changing its files.
106
+ If cloning reports 404 or permission denied, stop and tell me that repository
107
+ access is missing; do not search for a substitute repository.
108
+ 4. Run `mm-harness run --list --library
109
+ "perps=$HOME/shared-library/metamask-recipe-perps"` and show me the recipes
110
+ tagged `[perps]`. Use this explicit `--library` on every later command; do not
111
+ assume an `export` survives between tool shells.
112
+ 5. Run `mm-harness status`. If the app is not ready, follow its public `Next:`
113
+ command while preserving the same device UUID or serial used during setup.
114
+ 6. Choose only a recipe actually printed by step 4. Prefer
115
+ `perps.open-market` on Extension/Mobile or `perps.snapshot-market` on Core.
116
+ If that shared recipe has no variant for this checkout, use the listed bundled
117
+ `wallet.smoke` (Extension/Mobile) or `runner.smoke` (Core) recipe.
118
+ 7. Run the chosen recipe with `--describe`, then `--plan`, then for real with
119
+ `--artifacts-dir ./first-team-recipe-artifacts`. Include
120
+ `--library "perps=$HOME/shared-library/metamask-recipe-perps"` each time.
121
+ For a Perps recipe use `network=testnet market=BTC`. Never place an order or
122
+ use mainnet in this setup proof.
123
+ 8. Show me summary.json status, every trace.json node result, and all artifact
124
+ paths. Then confirm the checkout is ready for a bug-fix task using the
125
+ installed recipe-cook skill.</code></pre></div>
126
+ </div>
127
+
87
128
  <ol class="steps">
88
129
  <li class="step" data-step="skills">
89
130
  <div class="step-head">
@@ -91,7 +132,7 @@
91
132
  <h3 class="step-title" id="t3-skills">Install the skills into your checkout</h3>
92
133
  </div>
93
134
  <p class="step-why">Run this from the product checkout you work in.</p>
94
- <div class="cmd"><pre><code><span class="p">$ </span>yarn skills --include agentic/recipe-cook,agentic/recipe-quality --save</code></pre></div>
135
+ <div class="cmd"><pre><code><span class="p">$ </span>yarn skills --maturity experimental --include agentic/recipe-cook,agentic/recipe-quality --save</code></pre></div>
95
136
  <p class="step-why">
96
137
  The agentic skills carry <code>experimental</code> maturity, so they are visible only when
97
138
  experimental skills are enabled. <code>recipe-cook</code> is the one you want first — it owns
@@ -205,7 +246,7 @@ Inspect: mm-harness run &lt;recipe&gt; --describe
205
246
  <footer class="footer">
206
247
  <div class="wrap">
207
248
  <p>Internal getting-started guide for the MetaMask agentic coding workflow. Not official MetaMask product documentation.</p>
208
- <p>Verified against mm-harness 0.26+.</p>
249
+ <p>Verified against mm-harness 0.33+.</p>
209
250
  </div>
210
251
  </footer>
211
252
 
@@ -19,6 +19,7 @@
19
19
  <nav class="nav" aria-label="Main">
20
20
  <a href="../index.html">Start Here</a>
21
21
  <a href="../recipes.html">Recipes</a>
22
+ <a href="../perps.html">Perps</a>
22
23
  <a href="../cheatsheet.html">Cheatsheet</a>
23
24
  <a href="../architecture.html">Architecture</a>
24
25
  <a href="index.html" aria-current="page">Tutorials</a>
@@ -186,7 +187,7 @@
186
187
  <footer class="footer">
187
188
  <div class="wrap">
188
189
  <p>Internal getting-started guide for the MetaMask agentic coding workflow. Not official MetaMask product documentation.</p>
189
- <p>Verified against mm-harness 0.26+.</p>
190
+ <p>Verified against mm-harness 0.33+.</p>
190
191
  </div>
191
192
  </footer>
192
193
 
@@ -19,6 +19,7 @@
19
19
  <nav class="nav" aria-label="Main">
20
20
  <a href="../index.html">Start Here</a>
21
21
  <a href="../recipes.html">Recipes</a>
22
+ <a href="../perps.html">Perps</a>
22
23
  <a href="../cheatsheet.html">Cheatsheet</a>
23
24
  <a href="../architecture.html">Architecture</a>
24
25
  <a href="index.html" aria-current="page">Tutorials</a>
@@ -154,7 +155,7 @@
154
155
  <footer class="footer">
155
156
  <div class="wrap">
156
157
  <p>Internal getting-started guide for the MetaMask agentic coding workflow. Not official MetaMask product documentation.</p>
157
- <p>Verified against mm-harness 0.26+.</p>
158
+ <p>Verified against mm-harness 0.33+.</p>
158
159
  </div>
159
160
  </footer>
160
161
 
@@ -19,6 +19,7 @@
19
19
  <nav class="nav" aria-label="Main">
20
20
  <a href="../index.html">Start Here</a>
21
21
  <a href="../recipes.html">Recipes</a>
22
+ <a href="../perps.html">Perps</a>
22
23
  <a href="../cheatsheet.html">Cheatsheet</a>
23
24
  <a href="../architecture.html">Architecture</a>
24
25
  <a href="index.html" aria-current="page">Tutorials</a>
@@ -156,7 +157,7 @@
156
157
  <footer class="footer">
157
158
  <div class="wrap">
158
159
  <p>Internal getting-started guide for the MetaMask agentic coding workflow. Not official MetaMask product documentation.</p>
159
- <p>Verified against mm-harness 0.26+.</p>
160
+ <p>Verified against mm-harness 0.33+.</p>
160
161
  </div>
161
162
  </footer>
162
163
 
@@ -19,6 +19,7 @@
19
19
  <nav class="nav" aria-label="Main">
20
20
  <a href="../index.html">Start Here</a>
21
21
  <a href="../recipes.html">Recipes</a>
22
+ <a href="../perps.html">Perps</a>
22
23
  <a href="../cheatsheet.html">Cheatsheet</a>
23
24
  <a href="../architecture.html">Architecture</a>
24
25
  <a href="index.html" aria-current="page">Tutorials</a>
@@ -175,7 +176,7 @@
175
176
  <footer class="footer">
176
177
  <div class="wrap">
177
178
  <p>Internal getting-started guide for the MetaMask agentic coding workflow. Not official MetaMask product documentation.</p>
178
- <p>Verified against mm-harness 0.26+.</p>
179
+ <p>Verified against mm-harness 0.33+.</p>
179
180
  </div>
180
181
  </footer>
181
182