@deeeed/metamask-harness 0.17.5 → 0.19.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 (169) hide show
  1. package/CHANGELOG.md +1059 -0
  2. package/README.md +91 -140
  3. package/adapters/core/inject.sh +1 -6
  4. package/adapters/extension/console-tail.mjs +55 -24
  5. package/adapters/extension/ensure-browser.sh +6 -1
  6. package/adapters/extension/inject.mjs +7 -13
  7. package/adapters/extension/launch-browser.cjs +7 -1
  8. package/adapters/extension/launch-webpack.cjs +83 -0
  9. package/adapters/extension/launch.sh +8 -11
  10. package/adapters/extension/lib/chrome-args.cjs +8 -1
  11. package/adapters/extension/lib/macos-focus.cjs +32 -0
  12. package/adapters/extension/live.sh +12 -21
  13. package/adapters/extension/reattach.sh +1 -0
  14. package/adapters/extension/seed-fixture.sh +4 -12
  15. package/adapters/extension/sidepanel-toggle.sh +4 -1
  16. package/adapters/extension/stamp-runtime-title.cjs +58 -0
  17. package/adapters/extension/start-watch.sh +17 -4
  18. package/adapters/extension/stop-viewers.sh +1 -1
  19. package/adapters/extension/sync-webpack-dist.cjs +107 -0
  20. package/adapters/extension/verify.sh +2 -2
  21. package/adapters/manifest.json +53 -37
  22. package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +16 -0
  23. package/adapters/mobile/bridge-runtime/console-forwarder.cjs +1 -1
  24. package/adapters/mobile/bridge-runtime/lib/match-bridge-target.cjs +3 -1
  25. package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +2 -3
  26. package/adapters/mobile/cleanup.sh +16 -176
  27. package/adapters/mobile/inject.sh +91 -671
  28. package/adapters/mobile/launch-console-forwarder.cjs +39 -0
  29. package/adapters/mobile/open-device.sh +45 -7
  30. package/adapters/mobile/start-console-forwarder.sh +70 -0
  31. package/adapters/mobile/start-metro.sh +0 -41
  32. package/adapters/mobile/verify.sh +34 -83
  33. package/adapters/mobile/wait-for-bridge.sh +22 -8
  34. package/adapters/shared/harness-source-fingerprint.mjs +14 -12
  35. package/adapters/shared/resolve-slot-ports-core.mjs +17 -6
  36. package/adapters/shared/resolve-slot-ports.sh +17 -5
  37. package/adapters/shared/update-check-worker.mjs +43 -0
  38. package/bin/mm-harness +21 -2
  39. package/dist/adapters/core/surface.js +4 -1
  40. package/dist/adapters/extension/console-capture.js +95 -0
  41. package/dist/adapters/extension/product-config.js +110 -0
  42. package/dist/adapters/extension/runtime-decision.js +20 -2
  43. package/dist/adapters/extension/surface.js +22 -3
  44. package/dist/adapters/harness-freshness.js +49 -0
  45. package/dist/adapters/mobile/perps-env.js +43 -3
  46. package/dist/adapters/mobile/prepare.js +39 -7
  47. package/dist/adapters/mobile/surface.js +5 -2
  48. package/dist/adapters/resolve-slot-ports.js +2 -2
  49. package/dist/adapters/slot-ports.js +13 -16
  50. package/dist/adapters.js +56 -16
  51. package/dist/checkout-lock.js +27 -2
  52. package/dist/cli-color.js +19 -0
  53. package/dist/cli-commands.js +1 -1
  54. package/dist/cli.js +7 -18
  55. package/dist/command-contract.js +462 -0
  56. package/dist/command-journal.js +263 -0
  57. package/dist/commands/call.js +252 -57
  58. package/dist/commands/check.js +10 -4
  59. package/dist/commands/completion-candidates.js +17 -22
  60. package/dist/commands/device-target.js +27 -12
  61. package/dist/commands/doctor.js +106 -25
  62. package/dist/commands/fixtures.js +142 -35
  63. package/dist/commands/last.js +60 -0
  64. package/dist/commands/launch/extension.js +40 -17
  65. package/dist/commands/launch/index.js +207 -63
  66. package/dist/commands/launch/mobile.js +2 -0
  67. package/dist/commands/list-executables.js +187 -29
  68. package/dist/commands/logs.js +8 -6
  69. package/dist/commands/manifest.js +286 -35
  70. package/dist/commands/parse-args.js +68 -4
  71. package/dist/commands/provision.js +10 -4
  72. package/dist/commands/run-engine.js +772 -264
  73. package/dist/commands/run-report.js +12 -3
  74. package/dist/commands/run.js +378 -85
  75. package/dist/commands/shared.js +76 -4
  76. package/dist/commands/status-probe.js +4 -1
  77. package/dist/commands/status.js +2 -1
  78. package/dist/commands/stop.js +7 -2
  79. package/dist/commands/update.js +16 -28
  80. package/dist/completions-cache.js +1 -1
  81. package/dist/doctor.js +57 -10
  82. package/dist/harness.js +47 -137
  83. package/dist/heal-bounds.js +7 -2
  84. package/dist/json-stream.js +57 -0
  85. package/dist/live-adapter-contract.js +138 -32
  86. package/dist/manifest.js +161 -1
  87. package/dist/mm-harness-cli.js +146 -73
  88. package/dist/paths.js +2 -5
  89. package/dist/recipe-security.js +178 -0
  90. package/dist/run-diagnostics.js +261 -0
  91. package/dist/run-recording.js +1 -1
  92. package/dist/runner.js +140 -9
  93. package/docs/CONTRIBUTING.md +136 -0
  94. package/docs/QA.md +184 -0
  95. package/docs/RECIPES.md +122 -0
  96. package/docs/SECURITY.md +88 -0
  97. package/library/README.md +7 -3
  98. package/library/actions/core/perps/_controller.mjs +43 -56
  99. package/library/actions/core/perps/assert_orders.mjs +6 -7
  100. package/library/actions/core/perps/assert_positions.mjs +6 -7
  101. package/library/actions/core/perps/close_orders.mjs +2 -0
  102. package/library/actions/core/perps/close_positions.mjs +2 -0
  103. package/library/actions/core/perps/ensure_orders.mjs +4 -2
  104. package/library/actions/core/perps/ensure_positions.mjs +4 -2
  105. package/library/actions/core/perps/place_order.mjs +7 -3
  106. package/library/actions/core/perps/read_account.mjs +2 -2
  107. package/library/actions/core/perps/read_orders.mjs +2 -1
  108. package/library/actions/core/perps/read_positions.mjs +2 -1
  109. package/library/actions/core/wallet/list_accounts.mjs +95 -0
  110. package/library/actions/extension/perps/assert_orders.mjs +2 -1
  111. package/library/actions/extension/perps/assert_positions.mjs +2 -1
  112. package/library/actions/extension/perps/perps.mjs +43 -14
  113. package/library/actions/extension/platform/cdp.mjs +1 -0
  114. package/library/actions/extension/wallet/list_accounts.mjs +41 -0
  115. package/library/actions/mobile/perps/assert_orders.mjs +2 -1
  116. package/library/actions/mobile/perps/assert_positions.mjs +2 -1
  117. package/library/actions/mobile/perps/perps.mjs +40 -12
  118. package/library/actions/mobile/wallet/list_accounts.mjs +37 -0
  119. package/library/library.json +1 -1
  120. package/library/manifests/core.action-manifest.json +1214 -396
  121. package/library/manifests/extension.action-manifest.json +1635 -728
  122. package/library/manifests/mobile.action-manifest.json +1740 -727
  123. package/library/recipes/app/lifecycle.android-smoke.mobile.recipe.json +63 -81
  124. package/library/recipes/perps/clean-market-testnet.core.recipe.json +44 -0
  125. package/library/recipes/perps/clean-market-testnet.recipe.json +49 -0
  126. package/library/recipes/perps/lifecycle.recipe.json +136 -180
  127. package/library/recipes/perps/order-lifecycle.core.recipe.json +71 -67
  128. package/library/recipes/perps/performance.background-resume.mobile.recipe.json +51 -67
  129. package/library/recipes/perps/performance.cold-start.mobile.recipe.json +51 -67
  130. package/library/recipes/perps/performance.mobile.recipe.json +37 -51
  131. package/library/recipes/perps/performance.warm-start.mobile.recipe.json +44 -59
  132. package/library/recipes/perps/read-markets.core.recipe.json +29 -31
  133. package/library/recipes/perps/smoke.core.recipe.json +29 -32
  134. package/library/recipes/perps/smoke.extension.recipe.json +41 -44
  135. package/library/recipes/perps/smoke.mobile.recipe.json +42 -44
  136. package/library/recipes/perps/trading-lifecycle.core.recipe.json +69 -65
  137. package/library/recipes/runner/action-validation.extension.recipe.json +312 -398
  138. package/library/recipes/runner/action-validation.mobile.recipe.json +316 -402
  139. package/library/recipes/runner/smoke.core.recipe.json +25 -0
  140. package/library/recipes/runner/smoke.extension.recipe.json +23 -24
  141. package/library/recipes/runner/smoke.mobile.recipe.json +23 -24
  142. package/library/recipes/wallet/smoke.extension.recipe.json +40 -0
  143. package/library/recipes/wallet/smoke.mobile.recipe.json +40 -0
  144. package/package.json +12 -5
  145. package/scripts/completions.sh +8 -11
  146. package/scripts/validate-human-outcomes.mjs +169 -0
  147. package/adapters/mobile/overlay/app/dev-tools/AgenticService/AgentStepHud.tsx.patch +0 -185
  148. package/adapters/mobile/overlay/app/dev-tools/AgenticService/AgenticService.ts.patch +0 -1662
  149. package/adapters/mobile/overlay/compat/README.md +0 -28
  150. package/adapters/mobile/overlay/compat/rn81-message-event-source.patch +0 -42
  151. package/adapters/shared/resolve-farmslot-ports-core.mjs +0 -3
  152. package/adapters/shared/resolve-farmslot-ports.mjs +0 -5
  153. package/adapters/shared/resolve-farmslot-ports.sh +0 -7
  154. package/dist/adapters/extension/harness-freshness.js +0 -39
  155. package/dist/commands/flows.js +0 -62
  156. package/docs/ADAPTER-SURFACE.md +0 -119
  157. package/docs/CHEATSHEET.md +0 -61
  158. package/docs/CLI-SPEC.md +0 -1098
  159. package/docs/CODE-MAP.md +0 -62
  160. package/docs/DEBUG-HANDOVER.md +0 -36
  161. package/docs/MENTAL-MODEL.md +0 -295
  162. package/docs/UX-PRINCIPLES.md +0 -64
  163. package/docs/architecture.md +0 -398
  164. package/docs/live-adapter-contract.md +0 -188
  165. package/docs/package-boundaries.md +0 -47
  166. package/docs/perps-flow-catalog.md +0 -235
  167. package/docs/recipe-libraries.md +0 -301
  168. package/docs/runtime-file-conventions.md +0 -36
  169. package/library/flows/perps.flows.json +0 -64
package/docs/CODE-MAP.md DELETED
@@ -1,62 +0,0 @@
1
- # Code map — where things live and how a command flows
2
-
3
- One page for humans. The contracts live in docs/UX-PRINCIPLES.md and
4
- docs/ADAPTER-SURFACE.md; this is the geography.
5
-
6
- ## How a command flows
7
-
8
- ```
9
- bin/mm-harness bash front door: MM_HARNESS_BIN dev/prod switch,
10
- │ dist-vs-src selection (dist wins when present;
11
- │ stale-dist warning), deps ensure
12
-
13
- src/mm-harness-cli.ts commander surface: grouped colored help, per-command
14
- │ --help, retired-name teaching; delegates in-process
15
-
16
- src/cli.ts parse + dispatch ONLY — no verb logic lives here
17
-
18
- ├── src/commands/<verb>.ts one module per verb (doctor, stop, call, run,
19
- │ │ provision, fixtures, flows, logs, debug, …)
20
- │ ▼
21
- │ src/adapters/surface.ts AdapterSurface registry — per-platform behavior
22
- │ │ (ports, runtime status, dev server, log sources)
23
- │ ▼
24
- │ src/adapters/{mobile,extension,core}/ TS logic per platform
25
-
26
- └── src/harness.ts overlay lifecycle (install/verify/cleanup) — writes
27
- │ the per-checkout overlay + runner delegate
28
-
29
- adapters/ SHELL LEAVES (repo root — not src/adapters!):
30
- mobile/ extension/ single-OS-op scripts the TS layer spawns
31
- core/ shared/ (simctl/adb/tmux/metro/webpack lifecycle)
32
- ```
33
-
34
- ## The two `adapters/` directories
35
-
36
- - `adapters/` (repo root) — executable shell leaves. Thin, OS-facing, spawned by TS.
37
- - `src/adapters/` — TypeScript platform logic behind the AdapterSurface.
38
- Rule: logic/decisions in TS; a leaf only executes. A leaf converts to TS only when
39
- it starts making decisions (see backlog ruling), never as a campaign.
40
-
41
- ## Other roots
42
-
43
- - `library/` — recipe library: actions (`library/actions/<adapter>/…`, executable
44
- action modules — importing one RUNS it, see library/README.md), flows, recipes.
45
- - `tests/contract/` — hermetic bash contract tests (PATH-stubbed OS tools). The
46
- launch-leaf-flags test enforces composer↔leaf flag agreement.
47
- - `dist/` — esbuild output; the ONLY thing published runs. gitignored; in a source
48
- checkout it SHADOWS src when present (bin warns when it goes stale).
49
- - Per-checkout runtime state lives in the TARGET repo: `temp/recipe/runtime/`
50
- (context, logs, wallet fixture) and `temp/recipe/harness/<adapter>/` (overlay +
51
- runner delegate pinned to the installing bin via `.runner-source`).
52
-
53
- ## Adding things
54
-
55
- - **A verb**: module in `src/commands/`, register in `src/cli.ts` dispatch + the
56
- commander metadata in `src/mm-harness-cli.ts` (help group), contract test.
57
- - **A flag**: parse in the verb module; if it reaches a leaf, the leaf's parser
58
- must accept it (launch-leaf-flags test will fail otherwise) — or pass via env.
59
- - **A leaf**: shell under `adapters/<platform>/`, spawned via the shared spawn
60
- helpers (never raw exec), contract test with PATH stubs, errors teach escape.
61
- - **A platform behavior**: extend the AdapterSurface, implement per platform —
62
- verbs must not branch on adapter for surface-owned behavior.
@@ -1,36 +0,0 @@
1
- # Extension debugger handover
2
-
3
- ## Objective
4
-
5
- Make `mm-harness debug` open the normal Chrome DevTools console attached to the already-running extension page in the current slot. Do not claim success from a URL or JSON response alone.
6
-
7
- ## Acceptance
8
-
9
- 1. Start an existing slot runtime with `mm-harness launch --verify`.
10
- 2. Run `mm-harness debug` without relaunching the slot.
11
- 3. Confirm the visible DevTools Console is attached to the existing `chrome-extension://.../home.html#/` target.
12
- 4. Run a harmless console expression and observe its result.
13
- 5. Confirm the debugger remains connected after the extension watcher settles.
14
- 6. Repeat on two isolated extension slots.
15
-
16
- ## Failed attempts (do not repeat blindly)
17
-
18
- This investigation repeatedly reported machine success without proving the visible UI. The following approaches failed:
19
-
20
- 1. Open the target's hosted `devtoolsFrontendUrl` (`chrome-devtools-frontend.appspot.com`) with macOS `open`. This could select the user's default Chrome profile; when offline it opened an empty tab with `ERR_INTERNET_DISCONNECTED`.
21
- 2. Add `--remote-allow-origins=https://chrome-devtools-frontend.appspot.com`. This fixed one observed HTTP 403 origin rejection but did not make the visible frontend reliable.
22
- 3. Open the hosted URL through the slot profile using `open -na`. It still produced a separate disconnected DevTools tab.
23
- 4. Create a tab through the slot CDP `/json/new` endpoint using `devtools://devtools/bundled/inspector.html?...`. The tab was created, but the visible UI still reported `Debugging connection was closed`.
24
- 5. Add `devtools://devtools` to `--remote-allow-origins`. Direct CDP to the extension target still worked, but the human DevTools UI was not proven connected.
25
- 6. Testing the local checkout while the user invoked the globally installed command caused false confidence: the global command was running a different installation. The global install was temporarily overwritten with unreleased local code; do not assume the published version contains these changes.
26
-
27
- Screenshots showed the exact failure: a DevTools tab exists, but displays `Debugging connection was closed — WebSocket disconnected`. Direct CDP to the extension page succeeds, so target discovery is not the failure; the human DevTools frontend attachment/lifecycle remains unresolved.
28
-
29
- ## Constraints
30
-
31
- - Do not open an unrelated/default Chrome profile.
32
- - Do not silently fall back to another tab and report success.
33
- - Keep the extension target unchanged.
34
- - Do not modify product code or commit until the visible acceptance steps pass.
35
- - Test the actual human UI, not only `--json`, `/json/list`, or a raw CDP handshake.
36
- - Never report `opened: true` or a present `/json/list` target as proof that the visible DevTools console is attached.
@@ -1,295 +0,0 @@
1
- # mm-harness — Mental Model (review first)
2
-
3
- Read this before the code. It is the before→after map for the CLI refactor. Every
4
- "today" command below is real (from `bin/mm-recipe`, `bin/mme-recipe`,
5
- `runner/src/cli.ts`, and the `recipe-harness` skill) — nothing invented.
6
-
7
- > Skimmable overview. The detailed contract — a complete capability inventory
8
- > (every verb, with a fate), a per-verb spec, the caller audit, and the env-var
9
- > audit — is in [CLI-SPEC.md](CLI-SPEC.md).
10
-
11
- ## The model in one paragraph
12
-
13
- **`mm-harness` is built for the agent but easy for the human to use and
14
- understand.** It is the one front door for the whole recipe loop. The agent gets
15
- full depth (explicit flags, `--json`, ports, build tiers — machine-grade
16
- determinism); the human gets short verbs, positional targets, good defaults,
17
- completions, and teaching help/errors. Under it: the **engine** is
18
- `@farmslot/recipe-harness` (generic graph execution, adapters, recording, CLI);
19
- the **runner** (this repo) is the thin **MetaMask adapter** layer (manifests,
20
- `metamask.*` live adapters, orchestration); the **runtime overlay** is the set of
21
- files the tool installs into a target checkout (`install`/`verify`/`cleanup`).
22
- The runner never re-implements engine logic — it imports it. The agent
23
- composition loop is the harness's thesis made concrete: **discover** actions and
24
- browse flows (`actions --json` / `flows --json`) → **call** one action in
25
- isolation to verify it (`call <action> --json`) → **compose** a `recipe.json` →
26
- **plan** before any device is touched (`run --plan`) → **run** → review
27
- evidence (`summary.json` / `trace.json`). `run --plan` closes the loop: adapter-aware
28
- validation (action existence, platform support, fixture preconditions) exits fast
29
- with a reviewable plan artifact — wrong recipes fail before any device is touched.
30
-
31
- ## Organizing rule (Arthur)
32
-
33
- Identify the MAIN commands a human types daily, then each subcommand — always
34
- optimizing "how simple for the human," with advanced options for the agent and
35
- good defaults for easy typing. Enforced rules, visible in every table below:
36
-
37
- - **Grammar:** `mm-harness <verb> [target] [flags]`. Target is a positional
38
- (`ios` | `android` | `extension`) — the human form. The equivalent explicit
39
- flag (`--platform …`) is the agent form.
40
- - **Depth lives in FLAGS, never in extra command names.** If a daily task needs
41
- >1 flag (or >1 step) for a human, the default is wrong — a `[DEFAULT-GAP]`.
42
- - **No env-var prefixes for humans.** `VAR=x mm-harness …` is agent/CI syntax
43
- only; a human-typed value that has no flag/config home is an `[ENV-GAP]`.
44
- - **One bin, clean break.** End state has exactly one command, `mm-harness`. The
45
- `metamask-recipe` / `mm-recipe` / `mme-recipe` names and the `harness <verb>`
46
- subcommand form are REMOVED in the same coordinated wave; their capabilities
47
- live at their final `mm-harness` home (Part 2 of the spec).
48
- - **A verb's most common action is its default action.** `mm-harness flows` lists;
49
- `mm-harness flows promote` is the explicit subcommand. Never force the user to
50
- type `list` when listing is what the verb does by default.
51
- - **Direct action names are not top-level verbs (REJECTED).** `mm-harness unlock`
52
- is rejected — an open action vocabulary collides with the verb namespace; hot
53
- actions get deliberately promoted to real verbs instead. Use
54
- `mm-harness call unlock` (fuzzy short-name resolution to `metamask.wallet.unlock`
55
- when unambiguous).
56
- - **The overlay is per-checkout, git-ignored plumbing — humans never manage it.**
57
- Daily verbs (`launch`, `run`) check for the overlay at startup and
58
- auto-install it inline with a one-line notice on first run on that checkout.
59
- `install` is the explicit form for CI, agents, and deterministic setup
60
- (`--heal=off` on daily verbs disables the inline check and all healing).
61
- - **Your checkout's settings live in `agentic-runtime.json` — `doctor --fix` creates
62
- local identity/resources when absent and `doctor` explains them;
63
- you never hunt for the file.** `doctor` prints every field (slotId, extensionId,
64
- cdpPort, runtimeStart.*), its current value, its source (file / env var /
65
- default), and exactly how to change it. Arthur's rule made concrete: no
66
- knowledge lock-in about which file to edit.
67
- - **Verbs are idempotent — they make reality match intent; the recovery command is
68
- the normal command.** `launch` and `run` detect broken runtime state
69
- (Metro dead, Chrome/CDP offline, stale tabs) and heal before proceeding (controlled
70
- by `--heal`; default `auto` for `launch`, `infra-only` for `run`). The
71
- agent does not need a separate recovery verb: re-running `mm-harness launch ios`
72
- IS the recovery. `--json` reports what was healed in a `recovered:[...]` field
73
- (stable codes). `recoverable:false` + `attemptedRecoveries[]` stop agent retry
74
- loops when bounds are hit. `doctor --fix` repairs without launching when healing
75
- alone is the goal.
76
-
77
- ## Status legend
78
-
79
- | Tag | Meaning |
80
- |---|---|
81
- | **ROUTES-NOW** | Works today through `mm-harness` (this draft) |
82
- | **STUB** | `mm-harness` verb exists but exits 2 and teaches the working command; spec'd in Part 2 |
83
- | **ABSORB-LATER** | Capability folds into an `mm-harness` verb (spec'd in Part 2 with a maps-to row) |
84
- | **KEEP-INTERNAL** | Still reachable, just not a user-facing verb |
85
- | **REMOVE** | Name/bin/subcommand deleted in the refactor wave; callers updated in the same wave (needs sign-off) |
86
-
87
- ## Bin & name table (before → after)
88
-
89
- | Before | Role today | After |
90
- |---|---|---|
91
- | `bin/metamask-recipe` | public typed CLI + platform passthrough | **REMOVE** — becomes `bin/mm-harness` (same entrypoint, one name); no alias kept |
92
- | `metamask-recipe harness <verb>` | overlay lifecycle subcommand | **REMOVE** — verbs live top-level (`mm-harness install/verify/cleanup`); `live` dissolved into `launch --verify` |
93
- | `bin/mm-recipe` | mobile porcelain (Metro/sim/bridge) | **REMOVE** — capabilities become `mm-harness` verbs (Part 2); impl kept internal |
94
- | `bin/mme-recipe` | extension porcelain (Chrome/CDP) | **REMOVE** — capabilities become `mm-harness` verbs (Part 2); impl kept internal |
95
- | `recipe-harness` skill (~812 lines bash) | overlay lifecycle | **REMOVE** — thins to a caller of `mm-harness` overlay verbs |
96
- | `completions/_recipe` (zsh; completes `recipe mme-recipe mm-recipe`) | shell completion | **REMOVE** — replaced by bundled `mm-harness` completions (zsh + bash), auto-installed on setup |
97
-
98
- **`bin/mm-harness`** is the one user bin. **No per-platform binaries** —
99
- `mm-mobile-harness` / `mm-extension-harness` are rejected (recreates the
100
- three-names problem). Platform = auto-detected context; the positional target
101
- forces it; platform-specific needs are FLAGS on the same verb.
102
-
103
- ---
104
-
105
- ## BEFORE → AFTER by main command
106
-
107
- Columns: **Human form** (bare verb / positional target; zero env prefixes;
108
- defaults stated) · **Agent / advanced form** (explicit flags, `--json`) · **Status**.
109
-
110
- ### DAILY LOOP
111
-
112
- #### `launch` — start the app (Metro/build + boot) · REAL (wave 3)
113
-
114
- Auto-ensures runtime overlay before launching (installs inline with a one-line notice on first run; `--heal=off` to disable). **Idempotent + self-healing (default `--heal=auto`):** detects broken runtime state (Metro dead, Chrome/CDP offline, stale home tabs) and recovers before launching. The agent's recovery command is simply `mm-harness launch <target>` — no separate recovery verb. `--json` gains `"recovered": ["metro.restarted", ...]` (stable codes); `recoverable:false` + `attemptedRecoveries[]` stop retry loops. **`--verify`:** after launch, polls CDP/bridge until ready + runs smoke verify — implements the retired `live` sequence (zero capability lost). **Change detection:** quick launch (no `--build`) checks native/webpack build inputs; if changed → refuses with teaching error pointing at `--build`. Phase state-machine: `resolve → install → healthcheck → recover → launch → verify`.
115
-
116
- | | |
117
- |---|---|
118
- | **Human form (mobile)** | `mm-harness launch ios` · `mm-harness launch android` — target **MANDATORY**; missing → teaching error: "specify ios or android" |
119
- | **Human form (extension)** | `mm-harness launch` (no target; default `--fullscreen`; `--sidepanel` to open sidepanel mode) |
120
- | **Quick vs full build** | bare `launch` = quick relaunch (no native/webpack build); `--build` = full build first |
121
- | **Runway (mobile only)** | `--runway` runs a post-launch runway check; teaching error on extension |
122
- | **`live` replacement** | `mm-harness launch ios --verify` (install overlay → launch → poll → smoke verify — never seeds fixtures) |
123
- | **Agent / advanced** | `mm-harness launch --platform ios --device <udid> --build --verify --heal=off --json` |
124
-
125
- | Today (real) | → mm-harness | Status |
126
- |---|---|---|
127
- | `mm-recipe ios` · `android` · `start` | `launch ios\|android` (positional, mandatory) | ABSORB-LATER |
128
- | `mm-recipe up` | `launch ios --build` | ABSORB-LATER · [DEFAULT-GAP] tier hidden behind `MOBILE_PREFLIGHT_MODE=…` [ENV-GAP] |
129
- | `mm-recipe refresh` · `reload` · `relaunch` | `launch ios\|android` (quick) | ABSORB-LATER |
130
- | `mme-recipe rebuild` · `reopen` · `browser` · `launch` · `reload` · `reset` | `launch` (quick, extension) | ABSORB-LATER |
131
- | `mme-recipe up` | `launch --build` (extension) | ABSORB-LATER |
132
- | `mme-recipe watch` **then** `rebuild` | `launch --watch` (extension) | ABSORB-LATER · [DEFAULT-GAP] two commands today |
133
- | `mme-recipe build` · `refresh` · `refresh-once` | `launch` tiers (extension) | ABSORB-LATER |
134
- | `mme-recipe sidepanel [cycle\|open]` | `launch --sidepanel` (extension) | ABSORB-LATER |
135
-
136
- #### `logs` — tail Metro/webpack + app logs · REAL (wave 3)
137
-
138
- | | |
139
- |---|---|
140
- | **Human form** | `mm-harness logs` (compact, auto platform) |
141
- | **Agent / advanced** | `mm-harness logs --full --platform mobile --json` |
142
-
143
- | Today (real) | → mm-harness | Status |
144
- |---|---|---|
145
- | `mm-recipe logs` · `tail` · `logs --full` | `logs [--full]` | ABSORB-LATER · [ENV-GAP] `RECIPE_LOG_UI`/`RECIPE_LOG_EVENTS` → flags |
146
- | `mme-recipe logs` · `tail` · `tail-webpack` | `logs` | ABSORB-LATER |
147
-
148
- #### `debug` — open the debug console · REAL (wave 3)
149
-
150
- | | |
151
- |---|---|
152
- | **Human form** | `mm-harness debug` (auto platform) |
153
- | **Agent / advanced** | `mm-harness debug --worker` (extension SW) · `--dev-menu` · `--platform mobile` |
154
-
155
- | Today (real) | → mm-harness | Status |
156
- |---|---|---|
157
- | `mme-recipe debug` · `devtools` · `inspect` · `debug worker` | `debug [--worker]` | ABSORB-LATER |
158
- | `mm-recipe debug` | `debug` | ABSORB-LATER |
159
- | `mm-recipe dev-menu` · `devmenu` | `debug --dev-menu` (flag) | ABSORB-LATER · [DEFAULT-GAP] separate verb for one console concept |
160
-
161
- #### `fixtures` — initialize/sync fixture files + set up the wallet · REAL (wave 3)
162
-
163
- One canonical wallet fixture per checkout (SSOT). Fixture status is reported by `doctor` automatically. Initialize from an existing secure team/test fixture or explicitly choose a disposable public dev wallet; secrets are never typed into chat or command arguments.
164
-
165
- Subcommands: `fixtures init`, `fixtures sync`, `fixtures set`, plus Extension generate/finalize helpers.
166
-
167
- | | |
168
- |---|---|
169
- | **Human form** | `mm-harness fixtures init --from <path>` or explicit `--dev` · `fixtures sync` · `fixtures set` (password read FROM the fixture, never typed) |
170
- | **Agent / advanced** | `mm-harness fixtures set --fixture <json> --platform mobile` (`--fixture` is agent-only; humans edit the canonical file directly) |
171
-
172
- | Today (real) | → mm-harness | Status |
173
- |---|---|---|
174
- | `mm-recipe sync` · `mme-recipe sync` | `fixtures sync` | ABSORB-LATER |
175
- | `mme-recipe update` · `sync-runtime` | `fixtures sync` | ABSORB-LATER |
176
- | `mm-recipe setup-wallet` · `wallet-setup` | `fixtures set` | ABSORB-LATER · [ENV-GAP] `RECIPE_WALLET_FIXTURE` → `--fixture` (agent-only) |
177
- | `mm-recipe setup:ios` · `setup:android` | `launch ios` **then** `fixtures set` | ABSORB-LATER |
178
- | `mm-recipe unlock` | `fixtures set` (password from fixture) | ABSORB-LATER · [ENV-GAP] `MM_PASSWORD=… unlock` is the anti-pattern — read the fixture |
179
- | Extension standalone wallet setup | `fixtures set` | **[GAP]** no standalone verb today — the first-run wallet is seeded by the legacy `mme-recipe up`; end-state home is `fixtures set` (launch / `launch --verify` never seed) |
180
-
181
- ### DISCOVER (agent composition layer — `--json` is PRIMARY)
182
-
183
- The composition loop: `actions --json` → `call <action>` (try one via real engine path) → `flows --json` → compose `recipe.json` → `run --plan` (adapter-aware validate + plan, no device) → `run`.
184
-
185
- `manifest` is **RETIRED**: raw protocol dump → `actions --raw`; validation → `run --plan` / `doctor`. `validate` verb **DISSOLVED** into `run` (`run` validates first; `run --plan` = plan-only; same or richer shape, plus adapter cross-check). **DISCOVER final shape: `actions` · `call` · `flows`** — no manifest verb, no validate verb. Shell completions are sourced from this same layer: `call` completes action names, `run`/`run --plan` complete flow refs, both with a per-checkout short-TTL cache.
186
-
187
- #### `actions` · `call` · `flows` · ROUTES-NOW (call: REAL wave 2)
188
-
189
- | | |
190
- |---|---|
191
- | **Human form** | `mm-harness actions --adapter mobile` · `mm-harness call unlock` · `mm-harness flows` |
192
- | **Agent PRIMARY** | `mm-harness actions --adapter mobile --json` → `{ adapter, actions: [{ name, kind, description, fields, examples }] }` |
193
- | | `mm-harness call metamask.perps.read_positions --arg symbol=BTC --adapter core --json` → `{ action, resolvedAction, args, trace, evidence, recovered, mutations, exitCode, schemaVersion }` (same shape as `run --json`, one-node subset) |
194
- | | `mm-harness flows --json` → `{ sources, flows: [{ ref, source, file, description?, requiredParams?, shadows?, lastVerified? }] }` |
195
- | **Plan before run** | `mm-harness run recipe.json --plan --json` → `{ plan[], validation: { status, findings }, schemaVersion }` (adapter-aware; exit 5 on errors) |
196
-
197
- | Today (real) | → mm-harness | Status |
198
- |---|---|---|
199
- | `metamask-recipe actions --adapter …` | `mm-harness actions --adapter …` | ROUTES-NOW · [DEFAULT-GAP] `--adapter` required · [DISCOVERY-GAP] no `--action <name>` filter, no `--kind` filter, no keyword search |
200
- | `metamask-recipe flows list\|promote` | `mm-harness flows [\|promote]` | ROUTES-NOW · `flows` = browse the reusable flow library (compose, don't rewrite); `promote` publishes a proven flow up a tier · bare `flows` = list · [DISCOVERY-GAP] no keyword filter, no per-flow body fetch, no platform filter |
201
- | `mm-recipe`/`mme-recipe` `actions`/`doctor` (auto adapter) | same `mm-harness` verbs | ABSORB-LATER — porcelain supplies missing adapter default |
202
- | hook layer `run-action app.unlock` | `mm-harness call unlock` (fuzzy: `unlock` → `metamask.wallet.unlock`) — **one-node recipe via real engine path** | REAL (wave 2) · ambiguous → exit 2 listing candidates |
203
- | `mm-harness validate recipe.json` (old stub) | `mm-harness run recipe.json --plan` | **DISSOLVED** — stub teaching message updated to point at `run --plan`; `validate-command.ts` called internally by run engine (phase: `validate`) |
204
-
205
- ### PROVE
206
-
207
- #### `run` — validate + execute a recipe, write evidence · ROUTES-NOW
208
-
209
- **Validates first (adapter-aware):** before touching any device, `run` checks action existence, platform support, and fixture preconditions. Exit 5 on validation errors (same code for `run`, `run --plan`, `call`). `--plan` = plan-only: prints what would happen, touches NO device, exits 0/5. Phase state-machine: `resolve → install → healthcheck → recover → validate → launch → execute`. `--heal` defaults `infra-only`; healing never touches wallet state — only the explicit `fixtures set` mutates it.
210
-
211
- | | |
212
- |---|---|
213
- | **Human form** | `mm-harness run recipe.json` (adapter + artifacts auto-defaulted) |
214
- | **Plan before run** | `mm-harness run recipe.json --plan` (adapter-aware; no device; exit 0 = valid, exit 5 = errors) |
215
- | **Agent / advanced** | `mm-harness run recipe.json --adapter mobile --project-root <repo> --artifacts-dir <dir> --record-video=full-run --heal=off --json --json-stream` |
216
-
217
- | Today (real) | → mm-harness | Status |
218
- |---|---|---|
219
- | `metamask-recipe run … --adapter … --artifacts-dir …` | `mm-harness run …` | ROUTES-NOW · [DEFAULT-GAP] `--adapter` + `--artifacts-dir` mandatory today · [ENV-GAP] `RECIPE_LIBRARY_PATH` → `--library`/config |
220
- | `mm-recipe run recipe.json` · `mme-recipe run recipe.json` (auto adapter+artifacts) | `mm-harness run recipe.json` | ABSORB-LATER — porcelain already supplies the good defaults the typed CLI lacks |
221
-
222
- #### `doctor` · ROUTES-NOW
223
-
224
- Pure read-only diagnostic. Absorbs the retired `manifest` verb's validation: readiness checks include manifest well-formedness + reports manifest path + protocol version. `--fix` mode runs healing without launching (same path as `launch`/`verify` self-healing but stops short of starting the app).
225
-
226
- | | |
227
- |---|---|
228
- | **Human form** | `mm-harness doctor` |
229
- | **Agent / advanced** | `mm-harness doctor --adapter mobile --target <repo> --json` |
230
- | **Repair without launch** | `mm-harness doctor --fix [--json]` → `{ ..., "fixed": [...], "failed": [...] }` |
231
-
232
- | Today (real) | → mm-harness | Status |
233
- |---|---|---|
234
- | `metamask-recipe doctor --adapter … --target …` | `mm-harness doctor` | ROUTES-NOW · [DEFAULT-GAP] `--adapter`+`--target` required |
235
- | `mm-recipe`/`mme-recipe` `doctor` (auto adapter/target) | `mm-harness doctor` | ABSORB-LATER — porcelain supplies the missing defaults |
236
-
237
- ### RUNTIME OVERLAY
238
-
239
- #### `install` · `verify` · `cleanup` · ROUTES-NOW
240
-
241
- `live` is **REMOVED** — dissolved into `launch --verify` (same sequence: install overlay if missing → launch → CDP poll → smoke verify; zero capability lost — `launch --verify` never seeds fixtures).
242
-
243
- | | |
244
- |---|---|
245
- | **Human form** | `mm-harness install` · `verify` · `cleanup` (platform auto-detected, target = cwd) |
246
- | **`live` replacement** | `mm-harness launch [ios\|android] --verify` |
247
- | **Agent / advanced** | `mm-harness verify --platform extension --target <repo> --json -- --cdp-port <port>` |
248
-
249
- | Today (real) | → mm-harness | Status |
250
- |---|---|---|
251
- | `recipe-harness install\|verify\|cleanup\|live …` (skill) | `mm-harness install\|verify\|cleanup` · `live` → `launch --verify` | ROUTES-NOW; skill REMOVE |
252
- | `metamask-recipe harness install\|verify\|cleanup\|live …` | `mm-harness install\|verify\|cleanup` (top-level) · `live` → `launch --verify` | ROUTES-NOW; `harness` prefix REMOVE |
253
- | `mm-recipe prepare` · `mme-recipe prepare` | `install` / `launch` prep | ABSORB-LATER |
254
- | `mme-recipe ready` · `ensure-ready` | `verify` (or advanced `ensure-ready`) | ABSORB-LATER |
255
-
256
- ### ADVANCED (agent / internal — rarely typed by hand)
257
-
258
- | Today (real) | → mm-harness | Status |
259
- |---|---|---|
260
- | `metamask-recipe runtime-health` · `runtime-decision` · `runtime-launch` · `resolve-extension` · `ensure-ready` · `self-test` | logic moves INSIDE `launch`/`verify` self-healing path — no user-facing verb; `self-test` callable via `package.json` scripts only | KEEP-INTERNAL |
261
- | `metamask-recipe manifest --adapter …` | **RETIRED** — (1) validation → `doctor` readiness check; (2) raw dump → `mm-harness actions --raw`; (3) path + protocol version → `doctor` explain-my-setup | REMOVE |
262
- | `mm-recipe`/`mme-recipe` `runtime-status` · `decision` · `ports` · `status\|home\|health` · `stop` | operator/infra flags on `mm-harness` (e.g. `--json` status) | ABSORB-LATER |
263
- | `mm-recipe`/`mme-recipe` `completion\|complete` | bundled `mm-harness` completions (dynamic from DISCOVER layer) | KEEP-INTERNAL / REMOVE (old `_recipe`) |
264
- | `mm-recipe`/`mme-recipe` `interactive\|menu` | interactive shell | ABSORB-LATER |
265
- | `mm-recipe` bridge verbs: `app-status` · `route` · `navigate` · `back` · `accounts` · `select-account` · `screenshot` | recipe-driven or future flags | ABSORB-LATER |
266
-
267
- ---
268
-
269
- ## Decisions (final)
270
-
271
- The surface below is settled — the design passed a three-round external review (final verdict: SOUND). The rulings that shape day-to-day use:
272
-
273
- - **One bin, clean break.** `mm-harness` is the only command; `metamask-recipe` / `mm-recipe` / `mme-recipe` and the `harness <verb>` form are removed in the coordinated caller-sweep wave (CLI-SPEC.md Part 3).
274
- - **DISCOVER = `actions · call · flows`.** `manifest` is retired (raw dump → `actions --raw`; validation → `run --plan` / `doctor`; path + protocol version → `doctor`). `validate` is dissolved into `run` (`run` always validates first; `run --plan` = plan-only, no device).
275
- - **`call` is a one-node recipe** through the real engine path — same trace / evidence / `--json` shape as `run`; one execution path, two doors. `call` inherits `run` semantics in full (default `--heal=infra-only`).
276
- - **`live` is dissolved into `launch --verify`** (install overlay → launch → CDP poll → smoke verify; zero capability lost — `launch --verify` never seeds fixtures). No `live` verb on any surface.
277
- - **`--heal` = `off | infra-only | auto`** (+ `--yes` for non-interactive confirmation). `launch` defaults `auto`; `run` and `call` default `infra-only`; `off` is repro-preserving and also disables auto-install. Recovery bounds are ALWAYS enforced (recipe running / app-logic failure / same recovery already failed once → `recoverable:false`); no flag bypasses them. `--heal=off` is the single opt-out (it also disables auto-install).
278
- - **Wallet state is mutated ONLY by the explicit `fixtures set`.** Healing never touches wallet state — overlay / Metro / Chrome recovery is infra; wallet data is not. If recovery would require a wallet write, the verb stops with `recoverable:false` and `userAction: "run mm-harness fixtures set"`. `fixtures` is wallet DATA only and never touches the overlay.
279
- - **Overlay install notice** (not a "consent print"): stderr-only in human mode; in `--json` it appears solely as `mutations[]`, so stdout stays clean for machines.
280
- - **Exit code 5 = validation failure everywhere** (`run`, `run --plan`, `call`) — no mode-specific meanings. `run --plan` items carry `confidence: static | conditional` so a plan never claims live runtime facts. Full taxonomy + agent error contract: CLI-SPEC.md Part 5.
281
- - **Agent error contract:** structured `error:{code,message,cause,retryable,userAction}`, stable `recovered[]` / `mutations[]`, a phase state-machine, JSONL `--json-stream`, and `recoverable:false` + `attemptedRecoveries[]` to stop retry loops (CLI-SPEC.md Part 5).
282
- - **Quick-launch change detection:** before a quick launch (no `--build`), `launch` checks native/webpack build inputs; if changed → teaching error pointing at `--build`. Never runs on a stale build.
283
- - **Daily verbs auto-ensure the overlay**; humans never run `install`. `install` / `verify` / `cleanup` are the explicit top-level forms for CI/agents.
284
- - **`flows` bare = list**; `flows promote` is the explicit subcommand.
285
- - **Implementation:** `commander` (the library `@farmslot/recipe-harness` already uses) + bundled dynamic zsh/bash completions sourced from the DISCOVER layer.
286
-
287
- **Deferred to later waves (sequencing, not open questions):** the `[DEFAULT-GAP]` auto-defaults (adapter/artifacts for `run` / `doctor` / `actions`), the `[ENV-GAP]` env→flag moves (CLI-SPEC.md Part 4), the caller sweep that deletes the old names (Part 3), and the repo rename.
288
-
289
- ### Considered and rejected
290
-
291
- - **Teaching-tombstone stub bins** (`mm-harness-legacy`, etc.): Rejected. Pre-adoption burden with zero payoff; the coordinated wave (CLI-SPEC.md Part 3) updates all callers atomically. No tombstone bins needed.
292
-
293
- ## How farmslot calls mm-harness (preflight & the fleet binding)
294
-
295
- "Preflight" survives as a capability, not an exposed concept: it is what `launch` does before returning (readiness: Metro/bundle → app → health/bridge poll), with `launch --verify` as the prove-it tier and `doctor` as the read-only view. The fleet is just another agent-grade caller — farmslot prepare profiles keep their git / fixtures / deps phases, and the preflight/health hooks collapse to one call: `mm-harness launch <target> --verify --heal=auto --json`. Same-capability by construction: the machine contract (`--json` schemas, `--heal`, exit-code taxonomy, `recovered[]` / `mutations[]` / `recoverable:false`) is the one agents already use. Three bindings, one verb: the engineer types `mm-harness launch ios`; the slot's prepare hook calls the same verb with slot context; dispatch scales it across the fleet. (Full contract: CLI-SPEC.md Part 6.)
@@ -1,64 +0,0 @@
1
- # UX principles — the mm-harness interaction contract
2
-
3
- Every command surface follows these rules. They are review criteria, not aspirations:
4
- a PR that adds a command or output path is checked against each one.
5
-
6
- **Scope: the HUMAN surface.** The agent contract is exempt and inviolable: under
7
- `--json`, stdout carries exactly one machine-readable envelope — no color, no
8
- banners, no decorative lines, schema per CLI-SPEC.md. Progress/streaming exists
9
- for agents too, but only ever on stderr. When a rule below conflicts with envelope
10
- purity, the envelope wins.
11
-
12
- ## 1. Context-aware by default
13
- The CLI answers relative to the checkout it is standing in.
14
- - Adapter is auto-detected everywhere (`--adapter` overrides; a bare command never
15
- fails on a value detection can supply).
16
- - The slot context the orchestrator wrote into the checkout
17
- (`<runtime_dir>/agentic-runtime.json`) is the authoritative source for device,
18
- ports, and slot identity — naming formulas are last-resort fallbacks and never
19
- hardcode pool prefixes (they go stale on rename).
20
- - Output is scoped to the platform: an extension-only field is noise on a mobile
21
- slot; help, flag lists, and completion candidates shrink to what applies here.
22
- - Enforcement: platform-specific behavior is resolved through the adapter surface
23
- (`getAdapterSurface(adapter)`), never a per-command `if (adapter === …)` ladder —
24
- that ladder is how commands go platform-blind. See docs/ADAPTER-SURFACE.md.
25
-
26
- ## 2. Never silent, never opaque
27
- Long operations show intent immediately and progress continuously.
28
- - First line within milliseconds: what is being done, where (device/port), and
29
- where the full log lives.
30
- - Leaf output streams live to stderr — `--json` reserves stdout for the envelope,
31
- it never buys silence. Quiet stretches get heartbeats with elapsed time.
32
- - Each dev server a command starts gets its tmux log-tail window at launch time
33
- (window creation is launch-coupled; users re-create separately when they close one).
34
-
35
- ## 3. Summaries state outcomes
36
- The final line says what actually happened: target device, what the tier meant,
37
- resulting state — never a bare status word. Dev-override runs are marked
38
- (`[dev: MM_HARNESS_BIN]`) so it is always evident which bin served.
39
-
40
- ## 4. Dev and prod are explicit
41
- The global install is prod (`mm-harness`); a dev checkout runs via the documented
42
- `MM_HARNESS_BIN` switch (conventionally aliased `mmdev-harness`). The dev override
43
- announces itself in help and summaries. Completions cover both names.
44
-
45
- ## 5. Errors teach the exact escape
46
- Every failure states the next command for the caller's actual situation. A wrong
47
- hint (an escape for a different failure mode) is a bug of the same severity as
48
- the failure itself.
49
-
50
- ## 6. Lifecycle verbs are symmetric and idempotent
51
- What a command starts, a sibling verb stops (`launch`/`stop`), scoped to what this
52
- checkout owns (port-scoped, session-scoped) — never "whatever is running".
53
- Nothing-to-do is success. Tests never leak into the operator's real tmux.
54
-
55
- ## 7. Color is consistent and optional
56
- All human output styles through `cli-color.ts` (headers, ok/warn/err states,
57
- provenance, "Next:" hints); `NO_COLOR`/non-TTY degrade to plain text. No surface
58
- invents its own ANSI.
59
-
60
- ## 8. Doctor is the status surface
61
- `doctor` reports, per platform: live runtime state (deps currency, dev-server
62
- up/stale on the slot's resolved port, app/browser presence), the scoped runtime
63
- context with provenance (`[file]`/`[env]`/`[default]`), and the exact customize
64
- hint per row. Read-only unless `--fix`.