@deeeed/metamask-harness 0.46.0 → 0.47.1

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 (42) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +1 -1
  3. package/adapters/manifest.json +12 -4
  4. package/adapters/mobile/wait-for-bridge.cjs +550 -0
  5. package/adapters/mobile/wait-for-bridge.sh +11 -148
  6. package/bin/mm-harness +7 -2
  7. package/dist/adapters/mobile/prepare.js +26 -18
  8. package/dist/adapters/mobile/runtime-decision.js +1 -0
  9. package/dist/cli-commands.js +2 -0
  10. package/dist/command-contract.js +7 -0
  11. package/dist/commands/checklist.js +1 -0
  12. package/dist/commands/help.js +76 -0
  13. package/dist/commands/parse-args.js +2 -1
  14. package/dist/commands/recipe-quality.js +1 -1
  15. package/dist/commands/tutorial.js +46 -0
  16. package/dist/mm-harness-cli.js +45 -6
  17. package/docs/CONTRIBUTING.md +2 -0
  18. package/docs/QA.md +2 -2
  19. package/library/actions/mobile/perps/perps.mjs +1 -1
  20. package/package.json +2 -2
  21. package/scripts/site-contrast.mjs +6 -2
  22. package/site/assets/help-recipes.json +113 -0
  23. package/site/architecture.html +0 -497
  24. package/site/assets/metamask-fox.svg +0 -24
  25. package/site/assets/progress.mjs +0 -323
  26. package/site/assets/style.css +0 -1066
  27. package/site/cheatsheet.html +0 -307
  28. package/site/ecosystem.html +0 -162
  29. package/site/how-it-works.html +0 -692
  30. package/site/index.html +0 -184
  31. package/site/perps-advanced-orders-qa.html +0 -96
  32. package/site/perps.html +0 -265
  33. package/site/recipes.html +0 -423
  34. package/site/reviewers.html +0 -375
  35. package/site/tutorials/index.html +0 -181
  36. package/site/tutorials/v1.html +0 -212
  37. package/site/tutorials/v2.html +0 -207
  38. package/site/tutorials/v3.html +0 -258
  39. package/site/tutorials/v4.html +0 -196
  40. package/site/tutorials/v5.html +0 -164
  41. package/site/tutorials/v6.html +0 -166
  42. package/site/tutorials/v7.html +0 -185
@@ -1,375 +0,0 @@
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>For Reviewers — how to read an evidence bundle</title>
7
- <meta name="description" content="An annotated walkthrough of a real mm-harness evidence bundle: summary.json, trace.json, diagnostics, and what an honest degraded capture looks like.">
8
- <link rel="icon" href="assets/metamask-fox.svg" type="image/svg+xml">
9
- <link rel="stylesheet" href="assets/style.css?v=4">
10
- </head>
11
- <body data-progress-page="reviewers">
12
- <a class="skip" href="#summary">Skip to the walkthrough</a>
13
-
14
- <header class="topbar">
15
- <div class="wrap topbar-inner">
16
- <a class="brand" href="index.html">
17
- <img class="brand-mark" src="assets/metamask-fox.svg" alt="" width="22" height="22">
18
- <span class="brand-name">recipes</span>
19
- </a>
20
- <nav class="nav" aria-label="Main">
21
- <a class="nav-cta" href="index.html#prompt">Quick start</a>
22
- <a href="how-it-works.html">How it works</a>
23
- <a href="perps.html">Team / Perps</a>
24
- <a href="cheatsheet.html">Cheatsheet</a>
25
- <a href="architecture.html">Architecture</a>
26
- <a href="tutorials/index.html">Tutorials</a>
27
- </nav>
28
- </div>
29
- </header>
30
-
31
- <main>
32
- <section class="wrap hero" style="padding-bottom:1rem">
33
- <span class="eyebrow">For reviewers</span>
34
- <h1>How to read an evidence bundle</h1>
35
- <p class="lede">
36
- Someone attached a run to a pull request and claimed it proves something. You have five minutes.
37
- Here is where to look, in what order, and what would make you distrust it.
38
- </p>
39
- <div class="hero-meta">
40
- <span>real run, values redacted</span>
41
- <span>6 nodes, 6.3s</span>
42
- <span>read in 5 minutes</span>
43
- </div>
44
- </section>
45
-
46
- <section class="wrap">
47
- <div class="note plain">
48
- <span class="note-title">About this example</span>
49
- <p>
50
- Everything below is an actual run of the bundled <code>wallet.smoke</code> recipe on a real
51
- Extension checkout. Paths, addresses, account identifiers, and internal package names are
52
- redacted; structure, verdicts, and timings are untouched — including a wrinkle in the screenshot
53
- step that is the most instructive part of the bundle.
54
- </p>
55
- </div>
56
-
57
- <h2 id="order">Read it in this order</h2>
58
- <div class="grid grid-3">
59
- <div class="card">
60
- <h3>1 · report.md</h3>
61
- <p>The story in one page. Skim this first. If it does not match the PR description, stop here and ask.</p>
62
- </div>
63
- <div class="card">
64
- <h3>2 · summary.json</h3>
65
- <p>The verdict and the counts. Is it green, how many nodes, and what content was in scope.</p>
66
- </div>
67
- <div class="card">
68
- <h3>3 · trace.json</h3>
69
- <p>What actually happened per step. Every number you cite comes from here, never from prose.</p>
70
- </div>
71
- </div>
72
- </section>
73
-
74
- <section class="wrap">
75
- <h2 id="report">The report</h2>
76
- <p>Written for humans. Six steps, each with its action, duration, and a one-line result.</p>
77
-
78
- <div class="out"># MetaMask Recipe Run
79
-
80
- Status: <span class="ok">pass</span>
81
- Duration: 6.3s
82
- Nodes: 6/6 passed
83
-
84
- ## Steps
85
- - <span class="ok">PASS</span> status (app.status, 9ms): platform=extension
86
- - <span class="ok">PASS</span> fixture (metamask.wallet.fixture_status, 17ms): path=temp/recipe/runtime/wallet-fixture.json
87
- - <span class="ok">PASS</span> unlock (metamask.wallet.ensure_unlocked, 100ms): proof=extension-unlocked-state
88
- - <span class="ok">PASS</span> state (metamask.wallet.read_state, 94ms): proof=extension-wallet-state
89
- - <span class="ok">PASS</span> screenshot (ui.screenshot, 6.0s): path=screenshots/wallet-smoke.png
90
- - <span class="ok">PASS</span> done (end, 0ms)</div>
91
-
92
- <p>
93
- Five steps take 220ms between them; the screenshot takes six seconds. The trace explains it, and the
94
- explanation is a reason to trust this bundle rather than doubt it.
95
- </p>
96
- </section>
97
-
98
- <section class="wrap">
99
- <h2 id="summary">summary.json — the verdict</h2>
100
-
101
- <div class="annot">
102
- <div class="out">{
103
- "status": <span class="ok">"pass"</span>,
104
- "total": 6,
105
- "passed": 6,
106
- "failed": 0,
107
- "startedAt": "2026-07-22T15:23:56.367Z",
108
- "endedAt": "2026-07-22T15:24:02.638Z",
109
- "durationMs": 6271,
110
- "runner": {
111
- "source": "&lt;redacted checkout path&gt;",
112
- "git_ref": "62c93ba5130…"
113
- },
114
- "recipeLibraries": {
115
- "sources": [
116
- {
117
- "name": "metamask",
118
- "root": "&lt;redacted&gt;/library",
119
- "recipeCount": 6,
120
- "provenance": {
121
- "kind": "bundled",
122
- "trust": <span class="ok">"trusted"</span>,
123
- "name": "metamask"
124
- }
125
- }
126
- ],
127
- "shadowed": []
128
- },
129
- "sideFindings": {
130
- "status": <span class="ok">"clean"</span>,
131
- "nonBlocking": true,
132
- "counts": { "total": 0, "warning": 0,
133
- "error": 0, "exception": 0 },
134
- "diagnosticsPath": "diagnostics.json"
135
- }
136
- }</div>
137
-
138
- <div class="annot-notes">
139
- <div class="annot-note">
140
- <b>status</b>
141
- <p>The verdict for the whole run. <code>pass</code> only if every node passed; there is no partial credit here.</p>
142
- </div>
143
- <div class="annot-note">
144
- <b>total / passed / failed</b>
145
- <p>Node counts. Check these against the claim in the PR. "It proves six things" should mean <code>total: 6</code>, and if it does not, ask why.</p>
146
- </div>
147
- <div class="annot-note">
148
- <b>git_ref</b>
149
- <p>The exact commit of the tooling that produced this. Two bundles that disagree are often two different refs — this is the first thing to compare.</p>
150
- </div>
151
- <div class="annot-note">
152
- <b>recipeLibraries.provenance</b>
153
- <p>Where the recipe came from and how much it is trusted. <code>kind: bundled</code> means it shipped with the harness. A recipe from an untrusted source is labelled as such rather than silently executed as equal.</p>
154
- </div>
155
- <div class="annot-note">
156
- <b>shadowed</b>
157
- <p>Recipes that were overridden by a higher-priority library. If this is non-empty, the recipe that ran may not be the one you were reading. Check it.</p>
158
- </div>
159
- <div class="annot-note">
160
- <b>sideFindings</b>
161
- <p>App warnings and errors, kept <em>out</em> of the verdict (<code>nonBlocking: true</code>). Noise from an unrelated subsystem cannot fail someone's proof, and a clean verdict cannot hide it either. When this is not <code>clean</code>, open <code>diagnostics.json</code>.</p>
162
- </div>
163
- </div>
164
- </div>
165
- </section>
166
-
167
- <section class="wrap">
168
- <h2 id="trace">trace.json — what actually happened</h2>
169
- <p>
170
- One entry per node, in execution order, each with the intent it declared, its timing, its verdict,
171
- and its full output. This is the authoritative record.
172
- </p>
173
-
174
- <div class="annot">
175
- <div class="out">{
176
- "nodeId": "unlock",
177
- "action": "metamask.wallet.ensure_unlocked",
178
- "intent": "Ensure the fixture-backed wallet is unlocked",
179
- "startedAt": "2026-07-22T15:23:56.409Z",
180
- "endedAt": "2026-07-22T15:23:56.509Z",
181
- "durationMs": 100,
182
- "ok": <span class="ok">true</span>,
183
- "next": "state",
184
- "output": {
185
- "unlocked": true,
186
- "alreadyUnlocked": true,
187
- "proofPath": "extension-unlocked-state",
188
- "cdpPort": 9500,
189
- "targetUrl": "chrome-extension://&lt;redacted&gt;/home.html#/",
190
- "liveAdapter": "&lt;redacted&gt;/library/actions/
191
- extension/wallet/ensure_unlocked.mjs"
192
- }
193
- }</div>
194
-
195
- <div class="annot-notes">
196
- <div class="annot-note">
197
- <b>intent</b>
198
- <p>Written by the author, in the recipe, before the run. Read them top to bottom and you have the argument the bundle makes. If they do not add up to the claim, the bundle does not support it — however green.</p>
199
- </div>
200
- <div class="annot-note">
201
- <b>ok</b>
202
- <p>The per-node verdict. To triage a failed run, find the first <code>"ok": false</code>; everything after it is consequence, not cause.</p>
203
- </div>
204
- <div class="annot-note">
205
- <b>alreadyUnlocked</b>
206
- <p>This step did not <em>do</em> anything; it confirmed a state that already held. A legitimate pass — but if a PR claims "proves unlocking works", this node did not prove it.</p>
207
- </div>
208
- <div class="annot-note">
209
- <b>liveAdapter</b>
210
- <p>The file that executed — a real adapter on disk, not a stub. A missing adapter raises rather than returning a plausible object, which is why a green node means a real one ran.</p>
211
- </div>
212
- <div class="annot-note">
213
- <b>next</b>
214
- <p>The edge taken out of this node. Following <code>next</code> from the entry point reconstructs the path actually taken, which matters when a recipe branches.</p>
215
- </div>
216
- </div>
217
- </div>
218
- </section>
219
-
220
- <section class="wrap">
221
- <h2 id="degraded">The most instructive node in this bundle</h2>
222
- <p>
223
- Here is the six-second screenshot. It passed. Read what it recorded about <em>how</em> it passed:
224
- </p>
225
-
226
- <div class="out">{
227
- "nodeId": "screenshot",
228
- "action": "ui.screenshot",
229
- "intent": "Record the visible wallet state as evidence",
230
- "durationMs": 5996,
231
- "ok": <span class="ok">true</span>,
232
- "output": {
233
- "captured": true,
234
- "path": "screenshots/wallet-smoke.png",
235
- "artifact": {
236
- "type": "screenshot",
237
- "metadata": {
238
- "provider": <span class="hint">"dom-raster"</span>,
239
- "mode": "computed-style-viewport",
240
- "fallbackFrom": <span class="hint">"capture-helper+cdp"</span>,
241
- "captureHelperError": <span class="bad">"capture-helper snapshot failed for pid &lt;redacted&gt;: could not create image from window"</span>,
242
- "cdpError": <span class="bad">"Chrome Page.captureScreenshot timed out after 5000ms."</span>
243
- }
244
- }
245
- }
246
- }</div>
247
-
248
- <div class="doctrine">
249
- <p>
250
- <span class="k">The node passed, and still wrote down both failures.</span> The preferred capture
251
- path failed, the fallback timed out after five seconds, and a DOM raster — weaker evidence —
252
- produced the PNG. All of it is in the metadata. Nothing forced it to tell you.
253
- </p>
254
- </div>
255
-
256
- <p>
257
- This is what to check when deciding whether to trust a bundle at all: a system that quietly upgraded
258
- "degraded capture" to "screenshot captured" would produce cleaner-looking and worse evidence. From
259
- this node specifically:
260
- </p>
261
- <ul>
262
- <li><strong>The PNG is real but weaker than usual.</strong> A computed-style DOM raster is not a true screen capture. If the claim depends on pixel-accurate rendering, this image does not carry it.</li>
263
- <li><strong>The six seconds were a timeout, not slow work.</strong> Timing anomalies in a bundle usually have an explanation in the metadata. Look before assuming.</li>
264
- <li><strong>Always open the image.</strong> The trace tells you a PNG was registered and how; only your eyes tell you it shows what the author says it shows.</li>
265
- </ul>
266
- </section>
267
-
268
- <section class="wrap">
269
- <h2 id="diagnostics">diagnostics.json — the quarantine</h2>
270
- <p>
271
- Application warnings and errors captured during the run, kept structurally separate from the
272
- verdict so that neither can contaminate the other.
273
- </p>
274
-
275
- <div class="out">{
276
- "scope": "recipe-run-application",
277
- "status": <span class="ok">"clean"</span>,
278
- "nonBlocking": true,
279
- "note": "No application warnings or errors were emitted during the recipe run.",
280
- "source": {
281
- "label": "extension",
282
- "path": "&lt;redacted&gt;/temp/recipe/runtime/extension-console.log",
283
- "startOffset": 16337832,
284
- "endOffset": 16338586,
285
- "bytesRead": 754,
286
- "truncated": false
287
- },
288
- "counts": { "total": 0, "warning": 0, "error": 0, "exception": 0 },
289
- "findings": []
290
- }</div>
291
-
292
- <p>
293
- The offsets are the honest part: the log is bracketed to the run window — 754 bytes — so "clean" is
294
- a claim about <em>this run</em>, not that the log file is empty. <code>truncated: false</code>
295
- confirms nothing was dropped from that window.
296
- </p>
297
- </section>
298
-
299
- <section class="wrap">
300
- <h2 id="checklist">A reviewer's checklist</h2>
301
- <div class="table-scroll">
302
- <table>
303
- <thead><tr><th>Check</th><th>Where</th><th>What should worry you</th></tr></thead>
304
- <tbody>
305
- <tr>
306
- <td>Does the run match the claim?</td>
307
- <td>node <code>intent</code>s in <code>trace.json</code></td>
308
- <td>Intents that do not add up to what the PR says was proven.</td>
309
- </tr>
310
- <tr>
311
- <td>Are the counts real?</td>
312
- <td><code>summary.json</code>, not the description</td>
313
- <td>A number in prose that no field supports.</td>
314
- </tr>
315
- <tr>
316
- <td>Did the recipe you read actually run?</td>
317
- <td><code>recipe.json</code>, <code>shadowed</code></td>
318
- <td>A non-empty <code>shadowed</code> list.</td>
319
- </tr>
320
- <tr>
321
- <td>Where did the recipe come from?</td>
322
- <td><code>recipeLibraries.provenance</code></td>
323
- <td>A trust level the change does not warrant.</td>
324
- </tr>
325
- <tr>
326
- <td>Did any step merely observe an existing state?</td>
327
- <td>node outputs, e.g. <code>alreadyUnlocked</code></td>
328
- <td>A confirmation being sold as a demonstration.</td>
329
- </tr>
330
- <tr>
331
- <td>How was the visual evidence obtained?</td>
332
- <td>artifact <code>metadata.provider</code></td>
333
- <td><code>fallbackFrom</code> present and the claim depends on rendering.</td>
334
- </tr>
335
- <tr>
336
- <td>Was the app healthy while this ran?</td>
337
- <td><code>diagnostics.json</code></td>
338
- <td>Errors in the window that the author never mentioned.</td>
339
- </tr>
340
- <tr>
341
- <td>Does the screenshot show what they say?</td>
342
- <td><code>screenshots/</code></td>
343
- <td>Not opening it. Open it.</td>
344
- </tr>
345
- </tbody>
346
- </table>
347
- </div>
348
-
349
- <div class="note">
350
- <span class="note-title">The one rule</span>
351
- <p>
352
- Numbers come from <code>trace.json</code> — not the summary paragraph, not the PR description, not
353
- what an agent wrote about its own work. A claim with no field behind it is not evidence yet, and
354
- "which node shows that?" is a reasonable review comment.
355
- </p>
356
- </div>
357
-
358
- <hr class="sep">
359
- <div class="btn-row">
360
- <a class="btn btn-primary" href="how-it-works.html#steps">Produce your own bundle →</a>
361
- <a class="btn btn-ghost" href="architecture.html">How the pieces fit</a>
362
- </div>
363
- </section>
364
- </main>
365
-
366
- <footer class="footer">
367
- <div class="wrap">
368
- <p>Internal getting-started guide for proving MetaMask changes with recipes. Not official MetaMask product documentation.</p>
369
- <p>Example bundle is a real run with identifying values redacted. Sample output moves between releases; trust your terminal over this page.</p>
370
- </div>
371
- </footer>
372
-
373
- <script type="module" src="assets/progress.mjs?v=4"></script>
374
- </body>
375
- </html>
@@ -1,181 +0,0 @@
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>Tutorials — the series</title>
7
- <meta name="description" content="Seven tutorials taking a new engineer from an empty terminal to pull requests that carry proof.">
8
- <link rel="icon" href="../assets/metamask-fox.svg" type="image/svg+xml">
9
- <link rel="stylesheet" href="../assets/style.css?v=4">
10
- </head>
11
- <body data-progress-page="tutorials">
12
- <a class="skip" href="#series">Skip to the series</a>
13
-
14
- <header class="topbar">
15
- <div class="wrap-wide topbar-inner">
16
- <a class="brand" href="../index.html">
17
- <img class="brand-mark" src="../assets/metamask-fox.svg" alt="" width="22" height="22">
18
- <span class="brand-name">recipes</span>
19
- </a>
20
- <nav class="nav" aria-label="Main">
21
- <a class="nav-cta" href="../index.html#prompt">Quick start</a>
22
- <a href="../how-it-works.html">How it works</a>
23
- <a href="../perps.html">Team / Perps</a>
24
- <a href="../cheatsheet.html">Cheatsheet</a>
25
- <a href="../architecture.html">Architecture</a>
26
- <a href="index.html" aria-current="page">Tutorials</a>
27
- </nav>
28
- </div>
29
- </header>
30
-
31
- <main>
32
- <section class="wrap-wide hero" style="padding-bottom:1.5rem">
33
- <span class="eyebrow">The series</span>
34
- <h1>Seven tutorials</h1>
35
- <p class="lede">
36
- From an empty terminal to pull requests that carry proof. Each covers one flow and ends with one
37
- artifact.
38
- </p>
39
- <div class="hero-meta">
40
- <span>7 pages, readable now</span>
41
- <span>videos in production</span>
42
- </div>
43
- </section>
44
-
45
- <section class="wrap-wide">
46
- <div class="note plain">
47
- <span class="note-title">The page is the tutorial</span>
48
- <p>
49
- Every page below is complete and usable today. The video is its visual companion, slotted in as
50
- each is recorded — the page doubles as the recording script, which is why the two cannot drift.
51
- </p>
52
- </div>
53
-
54
- <h2 id="series">The series</h2>
55
- <div class="grid grid-3" style="margin-top:1.5rem">
56
-
57
- <a class="vid" href="v1.html">
58
- <div class="vid-thumb"><span class="vid-id">V1</span></div>
59
- <div class="vid-body">
60
- <h3>Getting started with recipes</h3>
61
- <p>Install, doctor, launch, discover, drive, prove. The whole arc, and the shortest path into everything else.</p>
62
- <div class="vid-foot">
63
- <span class="badge dur">5 min</span>
64
- <span class="badge live">page ready</span>
65
- <span class="badge soon">video soon</span>
66
- </div>
67
- </div>
68
- </a>
69
-
70
- <a class="vid" href="v2.html">
71
- <div class="vid-thumb"><span class="vid-id">V2</span></div>
72
- <div class="vid-body">
73
- <h3>Reading the evidence</h3>
74
- <p>One finished run, file by file, and the two rules that make a green verdict worth believing.</p>
75
- <div class="vid-foot">
76
- <span class="badge dur">6 min</span>
77
- <span class="badge live">page ready</span>
78
- <span class="badge soon">video soon</span>
79
- </div>
80
- </div>
81
- </a>
82
-
83
- <a class="vid" href="v3.html">
84
- <div class="vid-thumb"><span class="vid-id">V3</span></div>
85
- <div class="vid-body">
86
- <h3>Skills and your team's library</h3>
87
- <p>Install the skills, declare your library, prove discovery sees it. Worked through with the perps library.</p>
88
- <div class="vid-foot">
89
- <span class="badge dur">5 min</span>
90
- <span class="badge live">page ready</span>
91
- <span class="badge soon">video soon</span>
92
- </div>
93
- </div>
94
- </a>
95
-
96
- <a class="vid" href="v4.html">
97
- <div class="vid-thumb"><span class="vid-id">V4</span></div>
98
- <div class="vid-body">
99
- <h3>Cook a recipe from an acceptance criterion</h3>
100
- <p>One criterion in, a rerunnable proof out. Discovery sets the boundary; you pick the template.</p>
101
- <div class="vid-foot">
102
- <span class="badge dur">8–10 min</span>
103
- <span class="badge live">page ready</span>
104
- <span class="badge soon">video soon</span>
105
- </div>
106
- </div>
107
- </a>
108
-
109
- <a class="vid" href="v5.html">
110
- <div class="vid-thumb"><span class="vid-id">V5</span></div>
111
- <div class="vid-body">
112
- <h3>PR QA review from one link</h3>
113
- <p>One pull request URL in, a per-criterion verdict table out. Nothing gets posted without you.</p>
114
- <div class="vid-foot">
115
- <span class="badge dur">8–10 min</span>
116
- <span class="badge live">page ready</span>
117
- <span class="badge soon">skill not merged</span>
118
- </div>
119
- </div>
120
- </a>
121
-
122
- <a class="vid" href="v6.html">
123
- <div class="vid-thumb"><span class="vid-id">V6</span></div>
124
- <div class="vid-body">
125
- <h3>Release validation</h3>
126
- <p>One team's changes on a frozen release candidate, reported with four verdicts including NOT RUN.</p>
127
- <div class="vid-foot">
128
- <span class="badge dur">6 min</span>
129
- <span class="badge live">page ready</span>
130
- <span class="badge soon">skill not merged</span>
131
- </div>
132
- </div>
133
- </a>
134
-
135
- <a class="vid" href="v7.html">
136
- <div class="vid-thumb"><span class="vid-id">V7</span></div>
137
- <div class="vid-body">
138
- <h3>A ticket end to end</h3>
139
- <p>Ordinary work, start to finish: implement, cook the proof, open a pull request a reviewer can check.</p>
140
- <div class="vid-foot">
141
- <span class="badge dur">10 min</span>
142
- <span class="badge live">page ready</span>
143
- <span class="badge soon">video soon</span>
144
- </div>
145
- </div>
146
- </a>
147
-
148
- </div>
149
- </section>
150
-
151
- <section class="wrap-wide">
152
- <hr class="sep">
153
- <h2>Series rules</h2>
154
- <div class="grid grid-2">
155
- <div class="card">
156
- <h3>One flow per tutorial</h3>
157
- <p>Setup is never mixed with usage. Each ends with the artifact it produced and one command to try yourself.</p>
158
- </div>
159
- <div class="card">
160
- <h3>Nothing is staged</h3>
161
- <p>Real repos, real evidence. Where a run takes minutes the recording cuts to the result; it never fakes one. Failures stay in — watching someone follow an error to its fix is the most useful thing here.</p>
162
- </div>
163
- </div>
164
-
165
- <div class="btn-row">
166
- <a class="btn btn-primary" href="v1.html">Start with V1 →</a>
167
- <a class="btn btn-ghost" href="../how-it-works.html#steps">Or the full walkthrough</a>
168
- </div>
169
- </section>
170
- </main>
171
-
172
- <footer class="footer">
173
- <div class="wrap-wide">
174
- <p>Internal getting-started guide for proving MetaMask changes with recipes. Not official MetaMask product documentation.</p>
175
- <p>Sample output moves between releases; trust your terminal over this page.</p>
176
- </div>
177
- </footer>
178
-
179
- <script type="module" src="../assets/progress.mjs?v=4"></script>
180
- </body>
181
- </html>