@deeeed/metamask-harness 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +161 -0
- package/README.md +140 -0
- package/bin/mm-harness +99 -0
- package/docs/CHEATSHEET.md +61 -0
- package/docs/CLI-SPEC.md +915 -0
- package/docs/MENTAL-MODEL.md +295 -0
- package/docs/architecture.md +367 -0
- package/docs/extension-runtime-commands.md +60 -0
- package/docs/harness-cli.md +43 -0
- package/docs/live-adapter-contract.md +188 -0
- package/docs/package-boundaries.md +47 -0
- package/docs/perps-flow-catalog.md +235 -0
- package/docs/recipe-libraries.md +95 -0
- package/docs/runtime-file-conventions.md +36 -0
- package/library/actions/core/perps/_controller.mjs +727 -0
- package/library/actions/core/perps/assert_orders.mjs +53 -0
- package/library/actions/core/perps/assert_positions.mjs +52 -0
- package/library/actions/core/perps/close_orders.mjs +97 -0
- package/library/actions/core/perps/close_positions.mjs +118 -0
- package/library/actions/core/perps/ensure_orders.mjs +40 -0
- package/library/actions/core/perps/ensure_positions.mjs +37 -0
- package/library/actions/core/perps/place_order.mjs +201 -0
- package/library/actions/core/perps/read_account.mjs +30 -0
- package/library/actions/core/perps/read_orders.mjs +27 -0
- package/library/actions/core/perps/read_positions.mjs +27 -0
- package/library/actions/core/perps/start_state.mjs +92 -0
- package/library/actions/core/perps/teardown_state.mjs +86 -0
- package/library/actions/extension/perps/assert_orders.mjs +11 -0
- package/library/actions/extension/perps/assert_positions.mjs +11 -0
- package/library/actions/extension/perps/close_orders.mjs +8 -0
- package/library/actions/extension/perps/close_positions.mjs +8 -0
- package/library/actions/extension/perps/ensure_orders.mjs +4 -0
- package/library/actions/extension/perps/ensure_positions.mjs +4 -0
- package/library/actions/extension/perps/perps.mjs +730 -0
- package/library/actions/extension/perps/place_order.mjs +7 -0
- package/library/actions/extension/perps/read_orders.mjs +4 -0
- package/library/actions/extension/perps/read_positions.mjs +3 -0
- package/library/actions/extension/platform/cdp.mjs +541 -0
- package/library/actions/extension/ui/navigate.mjs +44 -0
- package/library/actions/extension/wallet/ensure_unlocked.mjs +36 -0
- package/library/actions/extension/wallet/read_state.mjs +27 -0
- package/library/actions/extension/wallet/select_account.mjs +48 -0
- package/library/actions/extension/wallet/setup.mjs +35 -0
- package/library/actions/mobile/app-overlay/app/dev-tools/AgenticService/AgentStepHud.tsx.patch +185 -0
- package/library/actions/mobile/app-overlay/app/dev-tools/AgenticService/AgenticService.ts.patch +1662 -0
- package/library/actions/mobile/bridge-runtime/cdp-bridge.cjs +686 -0
- package/library/actions/mobile/bridge-runtime/lib/cdp-eval.cjs +110 -0
- package/library/actions/mobile/bridge-runtime/lib/config.cjs +39 -0
- package/library/actions/mobile/bridge-runtime/lib/issue-capture.cjs +446 -0
- package/library/actions/mobile/bridge-runtime/lib/target-discovery.cjs +204 -0
- package/library/actions/mobile/bridge-runtime/lib/ws-client.cjs +108 -0
- package/library/actions/mobile/bridge-runtime/setup-wallet.sh +442 -0
- package/library/actions/mobile/perps/assert_orders.mjs +11 -0
- package/library/actions/mobile/perps/assert_positions.mjs +11 -0
- package/library/actions/mobile/perps/close_orders.mjs +8 -0
- package/library/actions/mobile/perps/close_positions.mjs +8 -0
- package/library/actions/mobile/perps/ensure_orders.mjs +4 -0
- package/library/actions/mobile/perps/ensure_positions.mjs +4 -0
- package/library/actions/mobile/perps/perps.mjs +709 -0
- package/library/actions/mobile/perps/place_order.mjs +7 -0
- package/library/actions/mobile/perps/read_orders.mjs +4 -0
- package/library/actions/mobile/perps/read_positions.mjs +3 -0
- package/library/actions/mobile/platform/bridge.mjs +283 -0
- package/library/actions/mobile/ui/navigate.mjs +38 -0
- package/library/actions/mobile/wallet/ensure_unlocked.mjs +107 -0
- package/library/actions/mobile/wallet/home.mjs +35 -0
- package/library/actions/mobile/wallet/read_state.mjs +40 -0
- package/library/actions/mobile/wallet/select_account.mjs +48 -0
- package/library/actions/mobile/wallet/setup.mjs +220 -0
- package/library/flows/perps.flows.json +64 -0
- package/library/library.json +7 -0
- package/library/manifests/core.action-manifest.json +1282 -0
- package/library/manifests/extension.action-manifest.json +1749 -0
- package/library/manifests/mobile.action-manifest.json +1753 -0
- package/library/recipes/action-validation.extension.recipe.json +417 -0
- package/library/recipes/action-validation.mobile.recipe.json +422 -0
- package/library/recipes/order-lifecycle.core.recipe.json +78 -0
- package/library/recipes/perps-lifecycle.recipe.json +194 -0
- package/library/recipes/read-markets.core.recipe.json +38 -0
- package/library/recipes/smoke.extension.recipe.json +31 -0
- package/library/recipes/smoke.mobile.recipe.json +31 -0
- package/library/recipes/trading-lifecycle.core.recipe.json +76 -0
- package/orchestration/compat-overlays/README.md +19 -0
- package/orchestration/compat-overlays/mobile/README.md +13 -0
- package/orchestration/compat-overlays/mobile/rn81-message-event-source.patch +42 -0
- package/orchestration/core/cleanup.sh +37 -0
- package/orchestration/core/inject.sh +154 -0
- package/orchestration/doctor.mjs +72 -0
- package/orchestration/extension/cleanup.mjs +60 -0
- package/orchestration/extension/console-tail.mjs +228 -0
- package/orchestration/extension/ensure-browser.sh +416 -0
- package/orchestration/extension/ensure-ready.ts +185 -0
- package/orchestration/extension/extension-id.ts +107 -0
- package/orchestration/extension/inject.mjs +266 -0
- package/orchestration/extension/launch-browser.cjs +216 -0
- package/orchestration/extension/launch.sh +175 -0
- package/orchestration/extension/live.sh +320 -0
- package/orchestration/extension/pin-remote-flags.cjs +45 -0
- package/orchestration/extension/readiness.mjs +414 -0
- package/orchestration/extension/refresh-build.sh +190 -0
- package/orchestration/extension/runtime-decision.ts +445 -0
- package/orchestration/extension/runtime.ts +407 -0
- package/orchestration/extension/seed-fixture.sh +177 -0
- package/orchestration/extension/sidepanel-toggle.sh +291 -0
- package/orchestration/extension/snapshot-dist.sh +84 -0
- package/orchestration/extension/start-watch.sh +339 -0
- package/orchestration/extension/wallet-fixture-state.cjs +1086 -0
- package/orchestration/lib/activate-repo-node.sh +144 -0
- package/orchestration/lib/cli-color.mjs +84 -0
- package/orchestration/lib/cli-commands.mjs +243 -0
- package/orchestration/lib/cli-home.mjs +354 -0
- package/orchestration/lib/cli-ux.sh +252 -0
- package/orchestration/lib/cli-version.mjs +123 -0
- package/orchestration/lib/ensure-runner-deps.sh +56 -0
- package/orchestration/lib/harness-path.sh +55 -0
- package/orchestration/lib/hash-helpers.sh +44 -0
- package/orchestration/lib/json-field.sh +23 -0
- package/orchestration/lib/log-tui.mjs +304 -0
- package/orchestration/lib/open-debug.mjs +317 -0
- package/orchestration/lib/path-defaults.json +4 -0
- package/orchestration/lib/progress.mjs +107 -0
- package/orchestration/lib/recipe-paths.mjs +26 -0
- package/orchestration/lib/resolve-farmslot-ports.sh +144 -0
- package/orchestration/manifest.json +358 -0
- package/orchestration/mobile/cleanup.sh +192 -0
- package/orchestration/mobile/deps-markers.ts +21 -0
- package/orchestration/mobile/inject.sh +681 -0
- package/orchestration/mobile/launch.sh +137 -0
- package/orchestration/mobile/live.sh +125 -0
- package/orchestration/mobile/runtime-decision.ts +292 -0
- package/orchestration/porcelain/metamask-recipe +99 -0
- package/orchestration/porcelain/mm-recipe +1591 -0
- package/orchestration/porcelain/mme-recipe +1181 -0
- package/package.json +59 -0
- package/runner/extension/verify.sh +511 -0
- package/runner/mobile/verify.sh +501 -0
- package/runner/src/adapters.ts +601 -0
- package/runner/src/cli.ts +1820 -0
- package/runner/src/commands/debug.ts +44 -0
- package/runner/src/commands/fixtures.ts +99 -0
- package/runner/src/commands/launch.ts +397 -0
- package/runner/src/commands/logs.ts +60 -0
- package/runner/src/commands/shared.ts +138 -0
- package/runner/src/completions-cache.ts +86 -0
- package/runner/src/doctor.ts +203 -0
- package/runner/src/harness.ts +516 -0
- package/runner/src/heal-bounds.ts +179 -0
- package/runner/src/index.ts +6 -0
- package/runner/src/live-adapter-contract.ts +274 -0
- package/runner/src/manifest.ts +47 -0
- package/runner/src/mm-harness-cli.ts +488 -0
- package/runner/src/paths.ts +198 -0
- package/runner/src/recording-target.ts +147 -0
- package/runner/src/run-recording.ts +329 -0
- package/runner/src/runner.ts +108 -0
- package/runner/src/types.ts +57 -0
- package/scripts/completions.sh +125 -0
- package/scripts/install-completions.sh +62 -0
package/docs/CLI-SPEC.md
ADDED
|
@@ -0,0 +1,915 @@
|
|
|
1
|
+
# mm-harness — CLI Specification (spec-first)
|
|
2
|
+
|
|
3
|
+
The detailed contract beneath [MENTAL-MODEL.md](MENTAL-MODEL.md) (skim that first
|
|
4
|
+
for the before→after at a glance). This document defines every capability: Part 1
|
|
5
|
+
inventories everything today; Part 2 specs each end-state verb in full; Part 3
|
|
6
|
+
audits every caller; Part 4 audits every env var. Each verb is fully specified here
|
|
7
|
+
independent of its implementation status.
|
|
8
|
+
|
|
9
|
+
**`mm-harness` is built for the agent but easy for the human to use and
|
|
10
|
+
understand.** The agent gets full depth (explicit flags, `--json`, ports, build
|
|
11
|
+
tiers — machine-grade determinism); the human gets short verbs, positional
|
|
12
|
+
targets, good defaults, completions, and teaching help/errors. Every table's
|
|
13
|
+
human-form vs agent-form split is the direct expression of that sentence.
|
|
14
|
+
|
|
15
|
+
Naming: **"harness" is the tool** (`mm-harness`); the engine is
|
|
16
|
+
`@farmslot/recipe-harness`; the injected files are the **runtime overlay**.
|
|
17
|
+
|
|
18
|
+
**Clean break (no aliases).** End state has exactly one command, `mm-harness`.
|
|
19
|
+
The `metamask-recipe` / `mm-recipe` / `mme-recipe` names and the `harness <verb>`
|
|
20
|
+
subcommand form are REMOVED in the same coordinated wave (Part 3). The draft code
|
|
21
|
+
keeps the old bins working so the tree still builds/tests; the surface documented
|
|
22
|
+
here is end-state only.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
# Part 1 — Complete inventory of existing capabilities
|
|
27
|
+
|
|
28
|
+
**Why this exists (Arthur):** to *guarantee no capability is lost* in the
|
|
29
|
+
refactor. **Acceptance rule:** every row MUST have a fate, and the fates are
|
|
30
|
+
exhaustive —
|
|
31
|
+
|
|
32
|
+
- **ROUTES-NOW** — capability works via `mm-harness` today (this draft),
|
|
33
|
+
- **STUB** — spec'd here, coming (exits 2 with a teaching message today),
|
|
34
|
+
- **ABSORB-LATER** — capability folds into an `mm-harness` verb (spec'd in Part 2 with a maps-to row),
|
|
35
|
+
- **KEEP-INTERNAL** — still reachable, just not a user-facing verb,
|
|
36
|
+
- **REMOVE** — name/bin/subcommand deleted in the wave; **requires a one-line justification** (needs Arthur's sign-off).
|
|
37
|
+
|
|
38
|
+
**No row may be unmapped.** An unmapped row means the refactor is not allowed to
|
|
39
|
+
proceed. Source of truth: the bins' `case` statements + the naming audit in
|
|
40
|
+
[harness-cli.md](harness-cli.md). Every row is real.
|
|
41
|
+
|
|
42
|
+
## Surface A — `metamask-recipe` typed CLI (`runner/src/cli.ts`)
|
|
43
|
+
|
|
44
|
+
Name `metamask-recipe` → **REMOVE** (becomes `mm-harness`; no alias). Capability rows:
|
|
45
|
+
|
|
46
|
+
| Verb | What it does today | Platforms | Class | Fate |
|
|
47
|
+
|---|---|---|---|---|
|
|
48
|
+
| `manifest` | Print/validate an action manifest | mobile/extension/core | ADVANCED | **REMOVE** — 3 functions rehome: (1) well-formedness validation → `doctor` readiness check; (2) raw protocol dump → `actions --raw` flag; (3) manifest path + protocol version → `doctor` explain-my-setup section. "action manifest" stays as the FILE name in docs; only manifest EDITORS needed this verb (maintainer/CI audience, served by `doctor` + `--raw`). |
|
|
49
|
+
| `actions` | Describe declared actions | all | DISCOVER | ROUTES-NOW |
|
|
50
|
+
| `doctor` | Readiness check (no launch) | all | PROVE | ROUTES-NOW |
|
|
51
|
+
| `runtime-health` | Probe extension CDP runtime health | extension | advanced | **KEEP-INTERNAL** — becomes internal function inside `launch`/`verify` self-healing path |
|
|
52
|
+
| `runtime-decision` | Decide readiness/rebuild need | extension/mobile | advanced | **KEEP-INTERNAL** — becomes internal function inside `launch` self-healing path |
|
|
53
|
+
| `runtime-launch` | Launch extension runtime via live.sh | extension | advanced | **KEEP-INTERNAL** — becomes internal function inside `launch` self-healing path |
|
|
54
|
+
| `resolve-extension` | Resolve the extension id | extension | advanced | **KEEP-INTERNAL** — internal plumbing; called inside `launch`/`launch --verify` |
|
|
55
|
+
| `ensure-ready` | Ensure extension ready (close home tabs) | extension | advanced | **KEEP-INTERNAL** — becomes internal function inside `launch`/`verify` self-healing path |
|
|
56
|
+
| `run` | Run a recipe, write evidence | all | PROVE | ROUTES-NOW |
|
|
57
|
+
| `self-test` | Package-wiring smoke (no device) | mobile+extension | advanced | **KEEP-INTERNAL** — CI smoke test; callable via `package.json` scripts; hidden from `--help` |
|
|
58
|
+
| `flows` | List/promote library flows | all | DISCOVER | ROUTES-NOW |
|
|
59
|
+
| `harness install\|verify\|cleanup\|live` | Runtime overlay lifecycle | all (core: no live) | OVERLAY | ROUTES-NOW (`harness` prefix → top-level); `live` **REMOVE** — dissolved into `launch --verify` |
|
|
60
|
+
| `mobile <...>` | Passthrough to `mm-recipe` | mobile | passthrough | ABSORB-LATER; passthrough REMOVE |
|
|
61
|
+
| `extension <...>` | Passthrough to `mme-recipe` | extension | passthrough | ABSORB-LATER; passthrough REMOVE |
|
|
62
|
+
|
|
63
|
+
## Surface B — `mm-harness` verbs (this draft)
|
|
64
|
+
|
|
65
|
+
| Verb | What it does today | Platforms | Class | Fate |
|
|
66
|
+
|---|---|---|---|---|
|
|
67
|
+
| `install\|verify\|cleanup` (top-level) | Overlay lifecycle (final home; `live` dissolved → `launch --verify`) | all | OVERLAY | ROUTES-NOW |
|
|
68
|
+
| `live` | Overlay install + launch + live-verify | all (core: n/a) | OVERLAY | **REMOVE** — dissolved into `launch --verify` (same sequence: install overlay if missing → launch → CDP poll → smoke verify; zero capability lost — `launch --verify` never seeds fixtures) |
|
|
69
|
+
| `launch` | Quick relaunch / `--build` / `--verify`; auto-ensure overlay + heal | mobile/extension (core: teaching error) | DAILY LOOP | STUB → **REAL (wave 3)** |
|
|
70
|
+
| `logs` | Tail Metro/webpack logs (composes porcelain `logs`) | mobile/extension | DAILY LOOP | STUB → **REAL (wave 3)** |
|
|
71
|
+
| `debug` | Open DevTools / RN dev menu (composes porcelain `debug`/`dev-menu`) | mobile/extension | DAILY LOOP | STUB → **REAL (wave 3)** |
|
|
72
|
+
| `fixtures` | `sync` files / `set` the wallet (wallet DATA only) | mobile/extension | DAILY LOOP | STUB → **REAL (wave 3)** |
|
|
73
|
+
| `validate` | Dissolved into `run` (`run` validates first; `run --plan` = plan-only, no device) — `run --plan` **REAL as of wave 2** | all | DISCOVER | **REMOVE** — zero capability lost; rehomed into `run` |
|
|
74
|
+
| `call` | Ad-hoc single-action execution via the real engine path (one-node recipe) — **REAL as of wave 2** (was STUB in wave 1) | all | DISCOVER | STUB → **REAL (wave 2)** |
|
|
75
|
+
|
|
76
|
+
## Surface C — `bin/mm-recipe` porcelain (mobile) — name REMOVE
|
|
77
|
+
|
|
78
|
+
| Verb | What it does today | Class | Fate |
|
|
79
|
+
|---|---|---|---|
|
|
80
|
+
| `interactive\|menu` | Interactive TUI menu | setup | ABSORB-LATER |
|
|
81
|
+
| `ports` | Show slot ports/paths | advanced | ABSORB-LATER |
|
|
82
|
+
| `up` | Decision-aware full lifecycle to ready | daily | ABSORB-LATER |
|
|
83
|
+
| `decision` | Print readiness decision | advanced | ABSORB-LATER |
|
|
84
|
+
| `sync` | Sync runtime overlay + fixtures | daily | ABSORB-LATER |
|
|
85
|
+
| `completion\|complete` | Shell completion script | tooling | KEEP-INTERNAL |
|
|
86
|
+
| `prepare` | Prepare runtime (install/build) | setup | ABSORB-LATER |
|
|
87
|
+
| `runtime-status` | Runtime status JSON | advanced | ABSORB-LATER |
|
|
88
|
+
| `ios\|start` | Launch iOS dev client | daily | ABSORB-LATER |
|
|
89
|
+
| `android` | Launch Android dev client | daily | ABSORB-LATER |
|
|
90
|
+
| `setup:ios` | Launch iOS + apply wallet fixture | daily | ABSORB-LATER |
|
|
91
|
+
| `setup:android` | Launch Android + apply wallet fixture | daily | ABSORB-LATER |
|
|
92
|
+
| `refresh\|reload\|relaunch` | Reload running app | daily | ABSORB-LATER |
|
|
93
|
+
| `logs\|tail` | Tail Metro log (`--full` = raw) | daily | ABSORB-LATER |
|
|
94
|
+
| `status\|home` | Status dashboard + next commands | daily | ABSORB-LATER |
|
|
95
|
+
| `app-status\|bridge-status` | Bridge status via `__AGENTIC__` | advanced | ABSORB-LATER |
|
|
96
|
+
| `route\|get-route` | Current route from bridge | advanced | ABSORB-LATER |
|
|
97
|
+
| `navigate` | Navigate to a route via bridge | advanced | ABSORB-LATER |
|
|
98
|
+
| `back\|go-back` | Go back via bridge | advanced | ABSORB-LATER |
|
|
99
|
+
| `unlock` | Unlock wallet with fixture password | daily | ABSORB-LATER |
|
|
100
|
+
| `setup-wallet\|wallet-setup` | Apply wallet fixture (SRP/pw/accounts) | daily | ABSORB-LATER |
|
|
101
|
+
| `accounts\|list-accounts` | List accounts via bridge | advanced | ABSORB-LATER |
|
|
102
|
+
| `select-account\|switch-account` | Switch account via bridge | advanced | ABSORB-LATER |
|
|
103
|
+
| `screenshot` | Capture app screenshot | advanced | ABSORB-LATER |
|
|
104
|
+
| `debug` | Open RN DevTools / Hermes inspector | daily | ABSORB-LATER |
|
|
105
|
+
| `dev-menu\|devmenu\|menu` | Open RN developer menu | daily | ABSORB-LATER |
|
|
106
|
+
| `stop` | Stop Metro | setup | ABSORB-LATER |
|
|
107
|
+
| `actions` | Actions (delegates to typed CLI) | PROVE | ABSORB-LATER |
|
|
108
|
+
| `doctor` | Doctor (delegates to typed CLI) | PROVE | ABSORB-LATER |
|
|
109
|
+
| `run` | Run a recipe (auto adapter+artifacts) | PROVE | ABSORB-LATER |
|
|
110
|
+
|
|
111
|
+
## Surface D — `bin/mme-recipe` porcelain (extension) — name REMOVE
|
|
112
|
+
|
|
113
|
+
| Verb | What it does today | Class | Fate |
|
|
114
|
+
|---|---|---|---|
|
|
115
|
+
| `interactive\|menu` | Interactive TUI menu | setup | ABSORB-LATER |
|
|
116
|
+
| `sync` | Sync runtime overlay | daily | ABSORB-LATER |
|
|
117
|
+
| `completion\|complete` | Shell completion script | tooling | KEEP-INTERNAL |
|
|
118
|
+
| `prepare` | Prepare runtime (install/build) | setup | ABSORB-LATER |
|
|
119
|
+
| `status\|health\|home` | Status dashboard | daily | ABSORB-LATER |
|
|
120
|
+
| `ports` | Show slot ports/paths | advanced | ABSORB-LATER |
|
|
121
|
+
| `up` | Full build + launch + fixture | daily | ABSORB-LATER |
|
|
122
|
+
| `runtime-status` | Runtime status JSON | advanced | ABSORB-LATER |
|
|
123
|
+
| `decision\|decide` | Print readiness decision | advanced | ABSORB-LATER |
|
|
124
|
+
| `update\|sync-runtime` | Hot-swap runtime overlay | daily | ABSORB-LATER |
|
|
125
|
+
| `ready\|ensure-ready` | Wait for/ensure runtime ready | daily | ABSORB-LATER |
|
|
126
|
+
| `watch\|start-watch\|watcher` | Start webpack watcher (background) | daily | ABSORB-LATER |
|
|
127
|
+
| `stop\|stop-watch\|stop-watcher` | Stop watcher | setup | ABSORB-LATER |
|
|
128
|
+
| `logs\|tail\|tail-webpack` | Tail webpack log | daily | ABSORB-LATER |
|
|
129
|
+
| `rebuild\|reset\|reopen\|reload\|browser\|launch\|full-launch\|runtime-launch` | Quick relaunch (snapshot dist + reopen Chrome) | daily | ABSORB-LATER |
|
|
130
|
+
| `refresh` | Incremental refresh | daily | ABSORB-LATER |
|
|
131
|
+
| `build` | Incremental rebuild | daily | ABSORB-LATER |
|
|
132
|
+
| `refresh:once\|refresh-once\|build:once\|build-once` | One-shot rebuild | daily | ABSORB-LATER |
|
|
133
|
+
| `debug\|devtools\|inspect` | Open Chrome DevTools (`worker` = SW) | daily | ABSORB-LATER |
|
|
134
|
+
| `sidepanel` | Toggle popup ↔ sidebar via CDP | daily | ABSORB-LATER |
|
|
135
|
+
| `actions` | Actions (delegates to typed CLI) | PROVE | ABSORB-LATER |
|
|
136
|
+
| `doctor` | Doctor (delegates to typed CLI) | PROVE | ABSORB-LATER |
|
|
137
|
+
| `run` | Run a recipe (auto adapter+artifacts) | PROVE | ABSORB-LATER |
|
|
138
|
+
|
|
139
|
+
## Surface E — `recipe-harness` skill
|
|
140
|
+
|
|
141
|
+
| Verb | What it does today | Class | Fate |
|
|
142
|
+
|---|---|---|---|
|
|
143
|
+
| `install\|verify\|cleanup\|live` | ~812 lines bash: resolve runner source + dispatch overlay lifecycle | OVERLAY | **REMOVE** — superseded by `mm-harness install/verify/cleanup` + `launch --verify`; skill thins to a caller |
|
|
144
|
+
|
|
145
|
+
## Surface F — Orchestration entry points (invoked by the overlay verbs)
|
|
146
|
+
|
|
147
|
+
| Entry | What it does today | Class | Fate |
|
|
148
|
+
|---|---|---|---|
|
|
149
|
+
| `{mobile,core}/inject.sh`, `extension/inject.mjs` | Install overlay into `temp/recipe/harness/<adapter>/` | OVERLAY impl | KEEP-INTERNAL (via `mm-harness install`) |
|
|
150
|
+
| `{mobile,core}/cleanup.sh`, `extension/cleanup.mjs` | Remove overlay | OVERLAY impl | KEEP-INTERNAL (via `mm-harness cleanup`) |
|
|
151
|
+
| `{mobile,extension}/live.sh` | Launch + live-verify | OVERLAY impl | KEEP-INTERNAL (via `mm-harness launch --verify`) |
|
|
152
|
+
| `runner/{mobile,extension}/verify.sh` (+ installed `scripts/verify.sh`) | Verify overlay/runtime health | OVERLAY impl | KEEP-INTERNAL (via `mm-harness verify`) |
|
|
153
|
+
| `orchestration/doctor.mjs` | Orchestration manifest doctor | advanced impl | KEEP-INTERNAL |
|
|
154
|
+
|
|
155
|
+
## Surface G — Shell completion
|
|
156
|
+
|
|
157
|
+
| Artifact | What it does today | Fate |
|
|
158
|
+
|---|---|---|
|
|
159
|
+
| `completions/_recipe` (zsh `#compdef recipe mme-recipe mm-recipe`) | Completes the three legacy names + adapters/recipes/flags | **REMOVE** — replaced by bundled `mm-harness` completions (zsh + bash), auto-installed on setup |
|
|
160
|
+
|
|
161
|
+
## Completeness check
|
|
162
|
+
|
|
163
|
+
- **Total rows:** 81 (A:14 · B:7 · C:30 · D:23 · E:1 · F:5 · G:1)
|
|
164
|
+
- ROUTES-NOW: 6 · STUB: 5 · ABSORB-LATER: 53 · KEEP-INTERNAL: 13 · REMOVE: 4 (total 82 rows — live split into own row in Surface B)
|
|
165
|
+
- 6 + 5 + 53 + 13 + 4 = 81 rows of capability (live row is REMOVE, not a net-new capability)
|
|
166
|
+
- **Unmapped: 0** ✅ (REMOVE rows: `manifest` A; `live` B dissolved → `launch --verify`; `validate` B dissolved → `run`/`run --plan`; Surface E; Surface G; ADVANCED KEEP-INTERNAL; name-level REMOVE in bin table + Part 3)
|
|
167
|
+
- **Class breakdown:** DAILY LOOP: 4 STUB → **REAL (wave 3)** · DISCOVER: 2 ROUTES-NOW + 1 STUB→REAL (`call`, wave 2) + 1 REMOVE (`validate` dissolved) · PROVE: 3 ROUTES-NOW · OVERLAY: 3 ROUTES-NOW + 1 REMOVE (`live` dissolved → `launch --verify`) · ADVANCED: 6 KEEP-INTERNAL + 1 REMOVE (`manifest`) · passthrough/tooling: rest
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
# Part 2 — Per-verb capability spec (end state)
|
|
172
|
+
|
|
173
|
+
Grammar: `mm-harness <verb> [target] [flags]`. OPTIONS tables carry an **ENV**
|
|
174
|
+
column — resolution order is **flag > env > config/fixture > built-in default**.
|
|
175
|
+
The human form never needs an env prefix (`VAR=x mm-harness …` is agent/CI only).
|
|
176
|
+
Audience: **human** (daily, ≤1 flag), **agent** (depth), **both**.
|
|
177
|
+
|
|
178
|
+
> Scope note: `evidence` / `quality` / `review` verbs from the wider DESIGN-mm-harness
|
|
179
|
+
> map are **not** in this repo yet and out of scope; evidence is produced by `run`.
|
|
180
|
+
|
|
181
|
+
## `launch` (REAL as of wave 3 → main DAILY LOOP verb)
|
|
182
|
+
|
|
183
|
+
> **Implemented (wave 3):** `handleLaunch` in `runner/src/commands/launch.ts`. The typed
|
|
184
|
+
> layer owns grammar (mobile-target mandatory, core/extension refusals), tier
|
|
185
|
+
> selection, quick-launch change detection, overlay auto-ensure, healing policy +
|
|
186
|
+
> bounds, teaching errors, and the `--json` contract. It does NOT rewrite
|
|
187
|
+
> orchestration: it COMPOSES the same porcelain paths (grounded in bin/mm-recipe +
|
|
188
|
+
> bin/mme-recipe case arms) and, for `--verify`, the overlay `live` sequence via
|
|
189
|
+
> `handleHarness`. Composition seams `MM_HARNESS_MM_RECIPE_BIN` /
|
|
190
|
+
> `MM_HARNESS_MME_RECIPE_BIN` / `MM_HARNESS_INSTALL_BIN` make every path
|
|
191
|
+
> contract-testable without a device (`tests/contract/mm-harness-wave3.test.sh`).
|
|
192
|
+
> Real-device boot + `--verify`/`--runway`/`--sidepanel` toggle land in the Tier B
|
|
193
|
+
> on-device checklist.
|
|
194
|
+
|
|
195
|
+
**Synopsis:**
|
|
196
|
+
- Mobile: `mm-harness launch <ios|android> [flags]` — **target is MANDATORY**; missing target → teaching error: "specify ios or android"
|
|
197
|
+
- Extension: `mm-harness launch [flags]` — target omitted; display mode via `--sidepanel` | `--fullscreen` (default `--fullscreen`)
|
|
198
|
+
|
|
199
|
+
Quick relaunch by default. `--build` triggers a full native/webpack build first.
|
|
200
|
+
|
|
201
|
+
**Per-platform behavior:**
|
|
202
|
+
|
|
203
|
+
| Platform | Behavior |
|
|
204
|
+
|---|---|
|
|
205
|
+
| extension | Snapshot `dist/chrome`, open Chrome for Testing over CDP (never seeds the wallet fixture — first-run wallet is `fixtures set`). `--build` webpack-builds first; `--watch` starts the persistent watcher; `--sidepanel` opens sidepanel mode (default `--fullscreen`). |
|
|
206
|
+
| mobile (ios\|android) | Start/reuse Metro, prewarm bundle, open iOS or Android dev client. `--build` = decision-aware native rebuild; default quick tier never triggers a native build. `--runway` runs a runway check after launch (mobile only; teaching error on extension). |
|
|
207
|
+
| core | n/a — headless. Exit 2: "core is headless; use `mm-harness verify`". |
|
|
208
|
+
|
|
209
|
+
**Options:**
|
|
210
|
+
|
|
211
|
+
| Flag | Type | Default | ENV (agent) | Audience | Description |
|
|
212
|
+
|---|---|---|---|---|---|
|
|
213
|
+
| `<ios\|android>` positional | ios\|android | **required on mobile** | — | human | Target platform — mandatory; teaching error if omitted |
|
|
214
|
+
| `--platform <p>` | mobile\|extension\|ios\|android | = positional / auto | `PLATFORM` | agent | Explicit platform (agent form of the positional) |
|
|
215
|
+
| `--device <udid\|name>` | string | first booted sim | `IOS_SIMULATOR` / `ADB_SERIAL` | both | Target simulator/device |
|
|
216
|
+
| `--build` | bool | false (quick) | `MOBILE_PREFLIGHT_MODE` | both | Full native/webpack build tier (replaces quick relaunch) |
|
|
217
|
+
| `--watch` | bool | false | — | agent | Persistent webpack watcher then relaunch (extension only) |
|
|
218
|
+
| `--verify` | bool | false | — | both | After launch: poll CDP/bridge until ready + run smoke verify (implements the retired `live` sequence: install overlay if missing → launch → poll → verify — never seeds fixtures; a first-run that needs a seeded wallet errors and points at `fixtures set`) |
|
|
219
|
+
| `--sidepanel` | bool | false | — | human | Extension: open in sidepanel mode |
|
|
220
|
+
| `--fullscreen` | bool | true | — | human | Extension: open fullscreen (default; explicit override of --sidepanel) |
|
|
221
|
+
| `--runway` | bool | false | — | both | Post-launch runway check (mobile only; teaching error on extension: "runway is mobile-only") |
|
|
222
|
+
| `--cdp-port <port>` | number | slot/context port | `CDP_PORT`/`RECIPE_CDP_PORT` | agent | Extension CDP port |
|
|
223
|
+
| `--heal` | off\|infra-only\|auto | auto | — | both | Healing policy (see Part 5 §5.7). `auto` = auto-ensure overlay + heal transport (Metro/Chrome/CDP/tabs) silently; NEVER touches wallet state. `infra-only` = heal transport only. `off` = repro-preserving, no healing, also disables auto-install (the only opt-out). Bounds are ALWAYS enforced (recipe running / app-logic failure / same recovery already failed once → `recoverable:false` in `--json`); no flag bypasses them. `--yes` confirms prompts non-interactively. |
|
|
224
|
+
| `--json` | bool | false | — | agent | Machine-readable summary; includes `recovered[]` (stable codes), `mutations[]`, `phase`, `schemaVersion` |
|
|
225
|
+
| `--json-stream` | bool | false | — | agent | JSONL event stream on stdout (one event per line; see Part 5 §5.5) |
|
|
226
|
+
|
|
227
|
+
**Auto-ensure overlay:** `launch` checks for the runtime overlay before launching (phase: `install`). If missing: installs it inline, then proceeds. **First-install overlay notice:** on the very first install for this checkout, prints `installed mm-harness overlay v<version> → <path>` to **stderr** in human mode; in `--json` it appears solely as `mutations[]` (stdout stays clean for machine output). Use `--heal=off` to disable all auto-install and healing (repro-preserving; use when `mm-harness install` runs explicitly upstream in CI).
|
|
228
|
+
|
|
229
|
+
**Self-healing (idempotent, controlled by `--heal`):** `launch` detects broken runtime state and recovers — the agent's recovery command is simply re-running `mm-harness launch <target>`. It does NOT silently ignore a bad state; it makes reality match intent:
|
|
230
|
+
|
|
231
|
+
| Platform | Detected broken state | Recovery action (internalized) |
|
|
232
|
+
|---|---|---|
|
|
233
|
+
| extension | Chrome closed / CDP offline / stale home tabs | reopen Chrome → CDP poll → close stale tabs (was: `runtime-launch` + `ensure-ready` + `runtime-health`) — never touches the wallet fixture |
|
|
234
|
+
| mobile | Metro dead / dev client disconnected / bridge offline | Restart Metro → prewarm bundle → relaunch dev client (was: `runtime-decision` + `runtime-launch`) |
|
|
235
|
+
|
|
236
|
+
Recovery is silent in human mode. With `--json`: `"recovered": ["metro.restarted", "tabs.cleared"]` (stable codes — see Part 5 §5.2). `recoverable:false` + `attemptedRecoveries` stop agent retry loops when bounds are hit. `--heal=off` disables all recovery (fail fast, exact broken state preserved).
|
|
237
|
+
|
|
238
|
+
**Quick-launch change detection:** Before a quick launch (no `--build`), `launch` checks whether native build inputs (iOS Podfile.lock, Gradle wrapper, `package.json` native deps) or webpack config have changed since the last build using the farm layer's native-diff heuristic. If changed, refuses quick launch with a teaching error: `✗ native build inputs changed since last build — run: mm-harness launch <target> --build`. Never silently runs on a stale build.
|
|
239
|
+
|
|
240
|
+
**Exit codes:** 0 launched · 1 launch failed · 2 bad args / core / missing mobile target · 3 infra failure not auto-healed (healing `off` or a bound was hit) · 4 bounded/not-retryable (see Part 5 §5.6).
|
|
241
|
+
**Failure (teaching):** `✗ launch <platform> failed\n <cause>\n Next: <exact command>`.
|
|
242
|
+
**Missing mobile target:** `✗ mm-harness launch: target is required for mobile.\n Run: mm-harness launch ios or mm-harness launch android`.
|
|
243
|
+
**Outputs:** running app + Metro/watcher; launch summary/logs under the slot runtime dir; `--json` status object.
|
|
244
|
+
**Maps-to:** C:`ios\|start`,`android`,`up`,`refresh…`,`setup:ios/android`(launch half); D:`rebuild…`,`up`,`watch`+`rebuild`,`build`,`refresh*`,`sidepanel`.
|
|
245
|
+
|
|
246
|
+
## `logs` (REAL as of wave 3 → main DAILY LOOP verb)
|
|
247
|
+
|
|
248
|
+
> **Implemented (wave 3):** `handleLogs` in `runner/src/commands/logs.ts` — composes
|
|
249
|
+
> the porcelain `logs` (+ `--full`). Teaching error (exit 2) when nothing is
|
|
250
|
+
> running (no `metro.log`/`webpack.log` for the checkout), pointing at `launch`.
|
|
251
|
+
|
|
252
|
+
**Synopsis:** `mm-harness logs [flags]` — tail Metro/webpack + app logs.
|
|
253
|
+
|
|
254
|
+
| Flag | Type | Default | ENV (agent) | Audience | Description |
|
|
255
|
+
|---|---|---|---|---|---|
|
|
256
|
+
| `--full` | bool | false (compact) | `RECIPE_LOG_UI` | both | Raw log tail |
|
|
257
|
+
| `--events <n>` | number | 10 | `RECIPE_LOG_EVENTS` | agent | Compact event count |
|
|
258
|
+
| `--platform <p>` | mobile\|extension | auto | `PLATFORM` | agent | Force platform |
|
|
259
|
+
| `--json` | bool | false | — | agent | Structured events |
|
|
260
|
+
|
|
261
|
+
**Exit:** 0 · 2 bad args. **Maps-to:** C:`logs\|tail`; D:`logs\|tail\|tail-webpack`.
|
|
262
|
+
|
|
263
|
+
## `debug` (REAL as of wave 3 → main DAILY LOOP verb)
|
|
264
|
+
|
|
265
|
+
> **Implemented (wave 3):** `handleDebug` in `runner/src/commands/debug.ts` — extension
|
|
266
|
+
> composes `debug` (`--worker` → `debug worker`); mobile composes `debug`
|
|
267
|
+
> (`--dev-menu` → `dev-menu`). Cross-platform flag misuse teaches (exit 2).
|
|
268
|
+
|
|
269
|
+
**Synopsis:** `mm-harness debug [flags]` — open the debug console.
|
|
270
|
+
|
|
271
|
+
| Flag | Type | Default | ENV | Audience | Description |
|
|
272
|
+
|---|---|---|---|---|---|
|
|
273
|
+
| `--worker` | bool | false | — | both | Extension service-worker DevTools |
|
|
274
|
+
| `--dev-menu` | bool | false | — | both | Mobile RN developer menu (folds `dev-menu` into a flag) |
|
|
275
|
+
| `--platform <p>` | mobile\|extension | auto | `PLATFORM` | agent | Force platform |
|
|
276
|
+
|
|
277
|
+
**Exit:** 0 · 2 bad args. **Maps-to:** C:`debug`,`dev-menu…`; D:`debug\|devtools\|inspect`(+`worker`).
|
|
278
|
+
|
|
279
|
+
## `fixtures` (REAL as of wave 3 → main DAILY LOOP verb, with subcommands)
|
|
280
|
+
|
|
281
|
+
> **Implemented (wave 3):** `handleFixtures` in `runner/src/commands/fixtures.ts`.
|
|
282
|
+
> `sync` composes the porcelain `sync` (both adapters). `set` composes the porcelain
|
|
283
|
+
> `setup-wallet <fixture>` on mobile; on the extension (documented GAP — no standalone
|
|
284
|
+
> porcelain arm) it reuses `call`'s one-node engine path (`metamask.wallet.setup`,
|
|
285
|
+
> `runOneNode` in `runner/src/cli.ts`). `set` always prints the fixture edit path;
|
|
286
|
+
> the password is read FROM the fixture, never typed. On-device seed is Tier B.
|
|
287
|
+
|
|
288
|
+
**One canonical wallet fixture per checkout** — same SSOT model as the farm installer. There is no fixture selection, listing, or bare status verb. **Fixture status is reported by `doctor` automatically** (grounded: `doctor.ts` `fixtureSummary()` already returns `{ status: 'missing'|'ready'|'incomplete'|'invalid', path, accountCount, hasPassword }` and is included in `createDoctorReport()` at line 125). The human edits `wallet-fixture.json` directly; `fixtures set` always teaches where.
|
|
289
|
+
|
|
290
|
+
**Synopsis:** `mm-harness fixtures <sync|set> [flags]` — subcommand required.
|
|
291
|
+
|
|
292
|
+
| Subcommand | Behavior |
|
|
293
|
+
|---|---|
|
|
294
|
+
| `sync` | Refresh **wallet fixture files** on the target (wallet DATA only — overlay refresh is `--heal` / `doctor --fix` territory, NOT `fixtures`; they are separate concerns) |
|
|
295
|
+
| `set` | Apply THE canonical fixture (SRP/password/accounts) to the running slot — password read FROM the fixture, never typed; always prints "Want different accounts? Edit: <absolute-path>/wallet-fixture.json" |
|
|
296
|
+
|
|
297
|
+
| Flag | Type | Default | ENV (agent) | Audience | Description |
|
|
298
|
+
|---|---|---|---|---|---|
|
|
299
|
+
| `--fixture <json>` | path | slot's `wallet-fixture.json` | `RECIPE_WALLET_FIXTURE` | **agent only** | Override fixture path; human form = edit the canonical file directly |
|
|
300
|
+
| `--platform <p>` | mobile\|extension | auto | `PLATFORM` | agent | Force platform |
|
|
301
|
+
|
|
302
|
+
**Exit:** 0 · 1 apply failed · 2 bad args.
|
|
303
|
+
**`set` teaching output:** always ends with: `Wallet fixture applied. Want different accounts? Edit: <absolute-path>/wallet-fixture.json`.
|
|
304
|
+
**Maps-to:** `sync` ← C:`sync`, D:`sync`,`update\|sync-runtime`; `set` ← C:`setup-wallet\|wallet-setup`,`unlock`,`setup:ios/android`(wallet half).
|
|
305
|
+
**Extension `set`:** [GAP] no standalone path today — the first-run wallet is seeded by the legacy `mme-recipe up`; the end-state home is `fixtures set` (launch / `launch --verify` never seed).
|
|
306
|
+
|
|
307
|
+
## `run` (ROUTES-NOW → main PROVE verb)
|
|
308
|
+
|
|
309
|
+
**Synopsis:** `mm-harness run <recipe.json> [flags]` — validate (adapter-aware) + execute a recipe, write evidence.
|
|
310
|
+
|
|
311
|
+
| Flag | Type | Default | ENV (agent) | Audience | Description |
|
|
312
|
+
|---|---|---|---|---|---|
|
|
313
|
+
| `--adapter <p>` | mobile\|extension\|core | auto-detect (target) | `RECIPE_HARNESS_PLATFORM` | both | Target adapter · [DEFAULT-GAP] required today |
|
|
314
|
+
| `--artifacts-dir <dir>` | path | slot artifacts dir | — | both | Evidence output · [DEFAULT-GAP] required today · **confirmed** in `cli.ts:415`, `adapters.ts:253` |
|
|
315
|
+
| `--project-root`/`--target <repo>` | path | `$PWD` | — | both | Checkout under test |
|
|
316
|
+
| `--plan` | bool | false | — | both | **REAL as of wave 2.** Plan-only mode: validate adapter-aware (schema + action existence/platform vs the adapter manifest) and print what would happen — touches NO device, writes NO artifacts. Exit 0 = plan valid; exit 5 = plan has errors (exit 2 = recipe missing/unparseable). `--json` = `{ status, adapter, recipe, findings[], plan[] }`; each `plan[]` item carries `confidence: static\|conditional`. |
|
|
317
|
+
| `--action-manifest <path>` | path | bundled | — | agent | Override manifest |
|
|
318
|
+
| `--library <name=path[:…]>` | string | personal/team then canonical | `RECIPE_LIBRARY_PATH` | agent | Recipe library sources |
|
|
319
|
+
| `--cdp-port <port>` | number | context | `CDP_PORT`/`RECIPE_CDP_PORT` | agent | Extension CDP port |
|
|
320
|
+
| `--slot <id>` | string | context | `FARMSLOT_SLOT_ID`/`SLOT_ID` | agent | Slot id (fleet) · **confirmed** in `cli.ts:51,819` (sets both env vars) |
|
|
321
|
+
| `--launch-existing-dist` | bool | false | — | agent | Reuse existing dist (extension) |
|
|
322
|
+
| `--record-video[=full-run]` | enum | off | — | agent | Record full-run video · **confirmed** (`parseRecordVideoMode` in `cli.ts:249`; `proof-window` reserved future mode; extension + iOS confirmed; Android not yet implemented) |
|
|
323
|
+
| `--heal` | off\|infra-only\|auto | infra-only | — | both | Healing policy (see Part 5 §5.7). Default `infra-only` — heals overlay/runtime transport but NEVER touches wallet state. Use `off` for repro-preserving behavior. Bounds are always enforced. If the failure requires a wallet write → the verb stops with `recoverable:false` exit 4 and `userAction: "run mm-harness fixtures set"`. |
|
|
324
|
+
| `--json` | bool | false | — | agent | Machine-readable result; includes `recovered[]` (stable codes), `mutations[]`, `plan[]` (with `--plan`), `schemaVersion` |
|
|
325
|
+
| `--json-stream` | bool | false | — | agent | JSONL event stream on stdout (one event per line; see Part 5 §5.5) |
|
|
326
|
+
|
|
327
|
+
**Pacing/slowMo:** No step-level pacing or slowMo flag exists on `run`. All CDP settle delays are hardcoded within orchestration scripts. `--settle-ms` exists only in `orchestration/extension/sidepanel-toggle.sh` (internal sidepanel CDP settle, not exposed on `run`). **[GAP — pacing option candidate]**: a `--step-delay-ms` or `--settle-ms` flag on `run` would let agents/CI slow down recipe execution for flaky-step debugging; not implemented today.
|
|
328
|
+
|
|
329
|
+
**Validates first (adapter-aware):** Before touching any device, `run` validates the recipe: action existence in the adapter's manifest, platform support for each action, and fixture preconditions. Validation errors exit 5 with a structured error list. `--plan` stops here — prints the plan and exits, no device touched. Without `--plan`, validation failures are fatal before any execution begins.
|
|
330
|
+
|
|
331
|
+
> **Wave-2 status:** adapter-aware validation (`validateRecipeDocument` +
|
|
332
|
+
> `validateRecipeWithManifest`, shared helper `validateRecipeAdapterAware`) is
|
|
333
|
+
> wired for **`run --plan`** and **`call`** at the pinned deps (`@farmslot/protocol`
|
|
334
|
+
> `validateRecipeWithManifest` is available today). Two extensions land when
|
|
335
|
+
> `@farmslot/recipe-harness >= 0.3.3` publishes: (1) `externalFlowIds` for recipes
|
|
336
|
+
> that reference library flows by `call.ref` (needs `resolveRecipeLibrarySources`),
|
|
337
|
+
> and (2) making the `run` **execute** path always-validate with those external ids.
|
|
338
|
+
> Until then, self-contained recipes (all in-repo recipes + every one-node `call`
|
|
339
|
+
> recipe) validate fully; the execute path relies on the engine's own manifest
|
|
340
|
+
> validation. Tagged in code (`runner/src/cli.ts` `validateRecipeAdapterAware`).
|
|
341
|
+
|
|
342
|
+
**Phase state-machine:** `resolve → install → healthcheck → recover → validate → launch → execute` (phase reported in `--json` as `phase`; `--json-stream` emits one event per phase transition).
|
|
343
|
+
|
|
344
|
+
**Auto-ensure overlay:** `run` checks for the runtime overlay before executing (phase: `install`). If missing: installs it inline with a one-line notice, then proceeds. **First-install overlay notice:** on the very first install for this checkout, prints `installed mm-harness overlay v<version> → <path>` to stderr in human mode; in `--json` it appears solely as `mutations[]`. Use `--heal=off` to disable all auto-install and healing.
|
|
345
|
+
|
|
346
|
+
**Self-healing (idempotent, controlled by `--heal`, default `infra-only`):** Before executing the recipe, `run` checks runtime health and heals broken infra state (Metro dead → restart; CDP offline → reopen Chrome) — but NEVER touches wallet state in `infra-only` mode. The agent re-runs `mm-harness run recipe.json` to recover from any mid-session failure — no separate recovery verb needed. `--json` gains `"recovered": ["metro.restarted", ...]` (stable codes; see Part 5 §5.2). `recoverable:false` + `attemptedRecoveries[]` stop agent retry loops when bounds are hit. `--heal=off` disables all recovery (fail fast, exact broken state preserved).
|
|
347
|
+
|
|
348
|
+
**Exit:** 0 pass · 1 recipe fail/error · 2 bad args · 3 infra failure not auto-healed (healing `off` / bound hit) · 4 bounded/not-retryable · 5 validation failure (adapter-aware validation found errors; same code for `run`, `run --plan`, and `call`).
|
|
349
|
+
**Outputs:** `summary.json`, `trace.json`, `artifact-manifest.json`, screenshots/logs under `--artifacts-dir`.
|
|
350
|
+
**Maps-to:** A:`run` (ROUTES-NOW); C/D:`run` (ABSORB-LATER — supply the auto adapter+artifacts defaults the typed CLI lacks).
|
|
351
|
+
|
|
352
|
+
## `doctor` (ROUTES-NOW → PROVE inspect verb)
|
|
353
|
+
|
|
354
|
+
Readiness check for a checkout without launching the app. Doctor is the single place to understand the full slot context — **no hunting for files**. It also absorbs the retired `manifest` verb's validation function: the readiness checks include manifest well-formedness (schema valid, no unknown action references) and report the manifest path + protocol version in the explain-my-setup section. It includes three grounded sub-sections:
|
|
355
|
+
|
|
356
|
+
**1. Fixture section** — grounded: `runner/src/doctor.ts` `fixtureSummary()` (line 69) returns `{ status: 'missing'|'ready'|'incomplete'|'invalid', path, accountCount, hasPassword }`, included in `createDoctorReport()` at line 125.
|
|
357
|
+
|
|
358
|
+
**2. Runtime-context section (spec addition)** — reads `agentic-runtime.json` from `{target}/{runtimeDir}/agentic-runtime.json` (override: `RECIPE_RUNTIME_CONTEXT` env; grounded in `runner/src/harness.ts:206-244`) and prints every field with its current value, source, and how to customize:
|
|
359
|
+
|
|
360
|
+
| Field | Env var set by harness | Flag override | How to customize |
|
|
361
|
+
|---|---|---|---|
|
|
362
|
+
| `slotId` | `RECIPE_SLOT_ID` | `--slot` | farmslot dispatch writes it |
|
|
363
|
+
| `extensionId` | `RECIPE_HARNESS_EXTENSION_ID` | — | auto-resolved; edit file to pin |
|
|
364
|
+
| `cdpPort` | `RECIPE_CDP_PORT` / `CDP_PORT` | `--cdp-port` | edit file or pass flag |
|
|
365
|
+
| `runtimeStart.approved` | `RECIPE_RUNTIME_START_APPROVED` | — | edit file (`true`/`false`) |
|
|
366
|
+
| `runtimeStart.command` | — | — | edit file |
|
|
367
|
+
| `runtimeStart.readyUrl` | `RECIPE_RUNTIME_READY_URL` | — | edit file |
|
|
368
|
+
|
|
369
|
+
**`--json` output shape** (end-state; includes both sections):
|
|
370
|
+
```json
|
|
371
|
+
{
|
|
372
|
+
"status": "pass",
|
|
373
|
+
"adapter": "extension",
|
|
374
|
+
"checks": [{ "id": "manifest", ... }],
|
|
375
|
+
"fixture": { "status": "ready", "path": "temp/recipe/.../wallet-fixture.json", "accountCount": 1, "hasPassword": true },
|
|
376
|
+
"runtimeContext": {
|
|
377
|
+
"file": "temp/recipe/.../agentic-runtime.json",
|
|
378
|
+
"fileExists": true,
|
|
379
|
+
"envOverride": null,
|
|
380
|
+
"fields": {
|
|
381
|
+
"slotId": { "value": "slot-1", "source": "file", "envVar": "RECIPE_SLOT_ID", "customize": "farmslot dispatch writes this" },
|
|
382
|
+
"extensionId": { "value": "abc123", "source": "file", "envVar": "RECIPE_HARNESS_EXTENSION_ID", "customize": "auto-resolved; edit file to pin" },
|
|
383
|
+
"cdpPort": { "value": "6663", "source": "file", "envVar": "CDP_PORT", "customize": "edit file or pass --cdp-port" },
|
|
384
|
+
"runtimeStart.approved": { "value": "true", "source": "file", "envVar": "RECIPE_RUNTIME_START_APPROVED", "customize": "edit file (true/false)" },
|
|
385
|
+
"runtimeStart.command": { "value": null, "source": "default", "envVar": null, "customize": "edit file" },
|
|
386
|
+
"runtimeStart.readyUrl": { "value": null, "source": "default", "envVar": "RECIPE_RUNTIME_READY_URL", "customize": "edit file" }
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
| Flag | Type | Default | ENV | Audience | Description |
|
|
393
|
+
|---|---|---|---|---|---|
|
|
394
|
+
| `--adapter <p>` | mobile\|extension\|core | auto-detect | `RECIPE_HARNESS_PLATFORM` | both | Target · [DEFAULT-GAP] required today |
|
|
395
|
+
| `--target <repo>` | path | `$PWD` | — | both | Checkout to probe |
|
|
396
|
+
| `--action-manifest <path>` | path | bundled | — | agent | Override manifest (the file; `manifest` verb is retired) |
|
|
397
|
+
| `--fix` | bool | false | — | both | Run healing steps WITHOUT launching — repair overlay, runtime context, and validate manifest; no app started. Exit 0 = all healed; 1 = partial/failed. `--json` adds `"fixed": [...]` and `"failed": [...]` arrays. |
|
|
398
|
+
| `--json` | bool | false | — | agent | Machine-readable (includes fixture + runtimeContext sections; with `--fix`: adds fixed/failed arrays) |
|
|
399
|
+
|
|
400
|
+
**`--fix` framing:** `doctor --fix` runs the same healing checks as `launch`/`verify`'s self-healing path but stops short of starting the app. Use it when the agent detects a broken state and wants to repair before the next `run` — or when CI needs to repair without a launch side-effect. `doctor` without `--fix` is always pure read-only.
|
|
401
|
+
|
|
402
|
+
**Exit:** 0/1.
|
|
403
|
+
**Maps-to:** A:`doctor` (ROUTES-NOW); C/D:`doctor` (ABSORB-LATER); retired `manifest` validation → absorbed here.
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
407
|
+
## DISCOVER — agent composition input
|
|
408
|
+
|
|
409
|
+
> **Decided (Arthur):** DISCOVER is a distinct group, not a sub-PROVE. The audience
|
|
410
|
+
> moment differs — composing before running — and `do` (try one action) makes the
|
|
411
|
+
> group's value self-evident in `--help`.
|
|
412
|
+
|
|
413
|
+
> **General rule:** a verb's most common action is its default. `mm-harness flows`
|
|
414
|
+
> lists flows; `mm-harness flows promote` is the explicit subcommand. Never force
|
|
415
|
+
> the user to type `list` when listing is what the verb does.
|
|
416
|
+
|
|
417
|
+
The DISCOVER verbs are the agent's composition layer. The loop is:
|
|
418
|
+
`actions --json` (vocabulary) → `call <action>` (try one action via real engine path) → `flows --json` (reusable flows) →
|
|
419
|
+
compose `recipe.json` → `run --plan` (adapter-aware plan + validate, no device) → `run` (execute).
|
|
420
|
+
`--json` is the **PRIMARY** form for every DISCOVER verb — the human form is the
|
|
421
|
+
pretty-printed fallback. `manifest` is **RETIRED**: raw protocol dump rehomes to
|
|
422
|
+
`actions --raw` (same JSON as `manifest --json` today); validation moves into
|
|
423
|
+
`run --plan` / `doctor`. `validate` verb **DISSOLVED** into `run` (`run` validates first; `run --plan` = plan-only). **DISCOVER final shape: `actions` · `call` · `flows`.**
|
|
424
|
+
|
|
425
|
+
### `actions` (ROUTES-NOW → DISCOVER verb)
|
|
426
|
+
|
|
427
|
+
**Synopsis:** `mm-harness actions --adapter <p> [--json] [--action <name>] [--kind official|custom]`
|
|
428
|
+
|
|
429
|
+
**PRIMARY (agent):** `mm-harness actions --adapter mobile --json`
|
|
430
|
+
|
|
431
|
+
**`--json` output shape** (grounded — `actions --adapter core --json` confirmed):
|
|
432
|
+
```json
|
|
433
|
+
{
|
|
434
|
+
"adapter": "core",
|
|
435
|
+
"actions": [
|
|
436
|
+
{
|
|
437
|
+
"name": "metamask.perps.read_positions",
|
|
438
|
+
"kind": "custom",
|
|
439
|
+
"description": "Read live Perps positions...",
|
|
440
|
+
"fields": ["account", "action", "market", "markets", "mode", "selector", "side", "symbol", "symbols", "timeout_ms"],
|
|
441
|
+
"examples": [{ "node": { "action": "metamask.perps.read_positions", "account": "...", "symbol": "BTC" } }]
|
|
442
|
+
}
|
|
443
|
+
]
|
|
444
|
+
}
|
|
445
|
+
```
|
|
446
|
+
`kind` is `"official"` (engine built-ins) or `"custom"` (MetaMask adapter actions). `fields` lists every accepted parameter name. `examples[].node` is a copy-pasteable recipe node.
|
|
447
|
+
|
|
448
|
+
**Human form:** `mm-harness actions --adapter mobile` — one line per action: `<name> (<kind>) <description> fields=<f1,f2,...>`.
|
|
449
|
+
|
|
450
|
+
| Flag | Type | Default | ENV | Audience | Description |
|
|
451
|
+
|---|---|---|---|---|---|
|
|
452
|
+
| `--adapter <p>` | mobile\|extension\|core | auto-detect | `RECIPE_HARNESS_PLATFORM` | both | Target adapter · [DEFAULT-GAP] required today |
|
|
453
|
+
| `--action <name>` | string | all | — | agent | Filter to one action; full schema + all examples |
|
|
454
|
+
| `--kind <k>` | official\|custom | all | — | agent | Filter by action kind |
|
|
455
|
+
| `--action-manifest <path>` | path | bundled | — | agent | Override manifest |
|
|
456
|
+
| `--raw` | bool | false | — | agent | Dump the underlying action manifest JSON (protocol version, registry version, all entries in raw registry format — same output as `manifest --json` today; replaces the retired `manifest` verb) |
|
|
457
|
+
| `--json` | bool | false | — | **agent PRIMARY** | Full schema + fields + examples per action |
|
|
458
|
+
|
|
459
|
+
**[DISCOVERY-GAP]:** No `--action <name>` single-action filter today (all or nothing). No `--kind` filter. No keyword search across descriptions or fields.
|
|
460
|
+
**Exit:** 0 / non-zero on engine error.
|
|
461
|
+
**Maps-to:** A:`actions` (ROUTES-NOW); C/D:`actions` (ABSORB-LATER).
|
|
462
|
+
|
|
463
|
+
### `call <action>` (REAL as of wave 2 → DISCOVER verb; one-node recipe via real engine path)
|
|
464
|
+
|
|
465
|
+
> **Implemented (wave 2):** `handleCall` in `runner/src/cli.ts` synthesizes a
|
|
466
|
+
> single-node recipe and hands it to `runRecipe` — the SAME path `run` uses — so
|
|
467
|
+
> `call` writes the same `summary.json` / `trace.json` / `artifact-manifest.json`
|
|
468
|
+
> evidence. Fuzzy resolution, `--arg k=v` mapping, adapter-aware validation
|
|
469
|
+
> (exit 5), and the `--json` shape are covered by `tests/contract/mm-harness-wave2.test.sh`.
|
|
470
|
+
|
|
471
|
+
> Direct top-level actions (`mm-harness unlock`) are **REJECTED**: an open action
|
|
472
|
+
> vocabulary would collide with the verb namespace; hot actions get deliberately
|
|
473
|
+
> promoted to real verbs instead.
|
|
474
|
+
|
|
475
|
+
**Synopsis:** `mm-harness call <action> [--arg k=v …] [--adapter <p>] [--json]`
|
|
476
|
+
|
|
477
|
+
**What it does:** Execute ONE action from the adapter vocabulary as a single-node recipe through the **real engine path** — same trace/evidence shape as `run`, same phase state-machine, same `--json` contract. This is NOT a shortcut; it is one door into the same execution engine. The distinction: `call` = one node, `run` = a graph. Grounded in the hook layer's existing `run-action` concept (e.g., `run-action app.unlock`).
|
|
478
|
+
|
|
479
|
+
**One execution path, two doors:** `call <action>` internally synthesizes a single-node recipe `{"nodes":[{"action":"<resolved>","args":{...}}]}` and hands it to the run engine. The result (`--json`) carries the same `trace[]`, `evidence`, `recovered[]`, `mutations[]`, and `schemaVersion` fields as `run --json`.
|
|
480
|
+
|
|
481
|
+
**Fuzzy short-name resolution:** an unambiguous short name resolves to the full action name (`unlock` → `metamask.wallet.unlock`). If ambiguous, exit 2 and list candidates: `✗ "unlock" is ambiguous: metamask.wallet.unlock, metamask.session.unlock — use the full name`.
|
|
482
|
+
|
|
483
|
+
**Human example:** `mm-harness call unlock` → resolves unambiguously to `metamask.wallet.unlock`
|
|
484
|
+
|
|
485
|
+
**Agent example:** `mm-harness call metamask.perps.read_positions --arg symbol=BTC --arg side=long --adapter core --json`
|
|
486
|
+
|
|
487
|
+
| Flag | Type | Default | ENV | Audience | Description |
|
|
488
|
+
|---|---|---|---|---|---|
|
|
489
|
+
| `<action>` positional | string | required | — | both | Full action name or unambiguous short name (fuzzy resolved from adapter vocabulary) |
|
|
490
|
+
| `--arg <k=v>` | string (repeatable) | — | — | both | Action field value; repeatable: `--arg symbol=BTC --arg side=long` |
|
|
491
|
+
| `--adapter <p>` | mobile\|extension\|core | auto-detect | `RECIPE_HARNESS_PLATFORM` | both | Target adapter |
|
|
492
|
+
| `--json` | bool | false | — | **agent PRIMARY** | `{ action, resolvedAction, args, trace, evidence, recovered, mutations, exitCode, schemaVersion }` — same shape as `run --json` (one-node subset) |
|
|
493
|
+
|
|
494
|
+
**Exit:** 0 success · 1 action failure · 2 bad args / ambiguous / unknown action · 5 validation failure (the one-node recipe failed adapter-aware validation).
|
|
495
|
+
**Maps-to:** hook layer `run-action` (grounded); `handleCall` wired in `runner/src/cli.ts` (wave 2).
|
|
496
|
+
|
|
497
|
+
### `flows` / `flows promote` (ROUTES-NOW → DISCOVER verb)
|
|
498
|
+
|
|
499
|
+
**What `flows` is:** the **reusable flow library** — pre-built, verified building blocks composed into recipes for efficiency and reuse. A recipe is mostly library-flow references plus a few unique steps; it is not rewritten from scratch each time. `flows promote` publishes a proven flow up a tier (personal → team → canonical), capturing it as a durable artifact. This is the composition story: discover what the library covers, compose from it, only author the delta.
|
|
500
|
+
|
|
501
|
+
**Synopsis:** `mm-harness flows [--library <name=path>] [--json]` (bare = list; most common action is the default)
|
|
502
|
+
|
|
503
|
+
**PRIMARY (agent):** `mm-harness flows --json`
|
|
504
|
+
|
|
505
|
+
**`--json` output shape** (grounded — `flows-command.ts` `flowsListDocument()`):
|
|
506
|
+
```json
|
|
507
|
+
{
|
|
508
|
+
"sources": [
|
|
509
|
+
{ "name": "personal", "root": "/home/user/.farmslot/recipe-library" },
|
|
510
|
+
{ "name": "team", "root": "/path/to/project/recipe-library" }
|
|
511
|
+
],
|
|
512
|
+
"flows": [
|
|
513
|
+
{
|
|
514
|
+
"ref": "perps/open-long",
|
|
515
|
+
"source": "personal",
|
|
516
|
+
"file": "perps/open-long.json",
|
|
517
|
+
"description": "Open a long position on HL testnet",
|
|
518
|
+
"requiredParams": ["symbol", "amount"],
|
|
519
|
+
"shadows": ["team/perps/open-long"],
|
|
520
|
+
"lastVerified": "2025-06-14T10:00:00Z"
|
|
521
|
+
}
|
|
522
|
+
]
|
|
523
|
+
}
|
|
524
|
+
```
|
|
525
|
+
`source` is the library name whose copy wins (personal > domain > canonical). `shadows` lists lower-precedence entries that this flow overrides. `lastVerified` stamps from `flows promote --run <artifacts>`.
|
|
526
|
+
|
|
527
|
+
**Human form:** `mm-harness flows` (bare = list, no subcommand needed) — text table: `<ref> source=<s> file=<f> [lastVerified=<t>] [shadows=<s,...>]`; description + `params:` line if present.
|
|
528
|
+
|
|
529
|
+
| Flag | Type | Default | ENV | Audience | Description |
|
|
530
|
+
|---|---|---|---|---|---|
|
|
531
|
+
| `--library <name=path>` | string (repeatable) | personal/team then canonical | `RECIPE_LIBRARY_PATH` | agent | Library sources, precedence first |
|
|
532
|
+
| `--json` | bool | false | — | **agent PRIMARY** | Full catalog with source, shadows, lastVerified |
|
|
533
|
+
|
|
534
|
+
**[DISCOVERY-GAP]:**
|
|
535
|
+
- No `--filter <keyword>` to search across refs/descriptions — cannot efficiently find flows covering a given screen or user story **[DISCOVERY-GAP]**
|
|
536
|
+
- No `--platform <p>` filter — platform-specific flows not discoverable without scanning all refs **[DISCOVERY-GAP]**
|
|
537
|
+
- No `--flow <ref>` to fetch a single flow's full body before composing — agent must read the file directly **[DISCOVERY-GAP]**
|
|
538
|
+
- `shadows` is present in JSON but no human-readable "what would override what" summary **[DISCOVERY-GAP]**
|
|
539
|
+
- Domain/coverage metadata absent — no way to ask "which flows cover the swap screen?" without scanning **[DISCOVERY-GAP]**
|
|
540
|
+
|
|
541
|
+
**Exit:** 0 / 1 (no sources found) / non-zero on engine error.
|
|
542
|
+
**Maps-to:** A:`flows` (ROUTES-NOW).
|
|
543
|
+
|
|
544
|
+
### `validate` — DISSOLVED into `run`
|
|
545
|
+
|
|
546
|
+
**`mm-harness validate` is REMOVED as a standalone verb.** Zero capability lost.
|
|
547
|
+
|
|
548
|
+
**Rehome:** `run` now validates first (adapter-aware: action existence, platform support, fixture preconditions) on every invocation. For plan-only validation without touching a device, use:
|
|
549
|
+
|
|
550
|
+
```
|
|
551
|
+
mm-harness run recipe.json --plan [--json]
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
`--plan` exits 0 if the plan is valid (exit 5 on errors). `--json` includes `plan[]` array + `validation` object with findings — same or richer than the former `validate --json` shape, plus adapter cross-check (the former [DISCOVERY-GAP] is gone: `run --plan --adapter <p>` validates action names against the specific adapter's manifest).
|
|
555
|
+
|
|
556
|
+
**Implementation note:** `@farmslot/recipe-harness` `validate-command.ts` is called internally by the `run` phase state-machine (phase: `validate`). No external wiring of `handleValidate` is needed; the capability is subsumed. The old stub teaching message for `mm-harness validate` should be updated to point at `mm-harness run <recipe> --plan`.
|
|
557
|
+
|
|
558
|
+
## Runtime overlay: `install` · `verify` · `cleanup` (ROUTES-NOW → OVERLAY verbs)
|
|
559
|
+
|
|
560
|
+
**Synopsis:** `mm-harness install|verify|cleanup [target] [flags]` — manage the
|
|
561
|
+
per-checkout runtime overlay. Final home is top-level (no `harness` prefix). `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).
|
|
562
|
+
|
|
563
|
+
> **Auto-ensure note:** `launch` and `run` auto-install the overlay if
|
|
564
|
+
> missing (with a one-line notice; `--heal=off` to disable). `install` is
|
|
565
|
+
> the explicit form for CI, agents, and deterministic setup where the install step
|
|
566
|
+
> must be auditable and separate. Human daily users need never call `install`
|
|
567
|
+
> explicitly — daily verbs handle it.
|
|
568
|
+
|
|
569
|
+
> **Self-healing note:** `verify` heals the overlay if missing (re-installs before probing health) and reports runtime state (Metro/Chrome/CDP) but does not restart it — use `launch` for that, or `doctor --fix` to repair without launching. `--json` gains `"recovered": [...]` when healing occurs.
|
|
570
|
+
|
|
571
|
+
**Per-platform behavior:**
|
|
572
|
+
|
|
573
|
+
| Verb | extension | mobile | core |
|
|
574
|
+
|---|---|---|---|
|
|
575
|
+
| `install` | `node inject.mjs` overlay → `temp/recipe/harness/extension/`; writes `.runner-source` | `inject.sh` overlay → `.../mobile/` | `inject.sh` overlay → `.../core/` |
|
|
576
|
+
| `verify` | overlay/CDP/bridge health (prefers installed `scripts/verify.sh`) | Metro + JS bridge health | delegates to installed `mm-harness doctor --adapter core` |
|
|
577
|
+
| `cleanup` | remove overlay (respects `.runner-source`) | remove overlay | remove overlay |
|
|
578
|
+
|
|
579
|
+
**Options:**
|
|
580
|
+
|
|
581
|
+
| Flag | Type | Default | ENV (agent) | Audience | Description |
|
|
582
|
+
|---|---|---|---|---|---|
|
|
583
|
+
| `[target]` positional | mobile\|extension\|core | auto-detected | — | human | Force adapter without a flag |
|
|
584
|
+
| `--platform`/`--adapter <p>` | mobile\|extension\|core | = positional / auto (git remote / markers) | `RECIPE_HARNESS_PLATFORM` | agent | Explicit adapter; a later `--platform ios\|android` is forwarded to the script |
|
|
585
|
+
| `--target <repo>` | path | `$PWD` | — | both | Checkout to operate on |
|
|
586
|
+
| `--json` | bool | false | — | agent | One JSON summary on stdout (child output → stderr) |
|
|
587
|
+
| `-- <args>` | passthrough | — | — | agent | Forwarded verbatim (e.g. `-- --cdp-port 6665`) |
|
|
588
|
+
|
|
589
|
+
**Exit:** 0 pass · 1 orchestration error / missing delegate · 2 bad args / unsupported (`live` on core).
|
|
590
|
+
**Failure (teaching):**
|
|
591
|
+
```
|
|
592
|
+
✗ verify core failed
|
|
593
|
+
core recipe harness not installed: missing delegate <path>.
|
|
594
|
+
Next: mm-harness install --platform core --target <repo>
|
|
595
|
+
```
|
|
596
|
+
**JSON:** `{ schemaVersion, command:"harness", action, adapter, target, autoDetected, status, exitCode }`.
|
|
597
|
+
**Maps-to:** A:`harness <verb>` (ROUTES-NOW); E: skill (REMOVE); F: orchestration entry points (KEEP-INTERNAL); C/D:`prepare`,`ready\|ensure-ready` (ABSORB-LATER).
|
|
598
|
+
|
|
599
|
+
## Advanced (collapsed → KEEP-INTERNAL)
|
|
600
|
+
|
|
601
|
+
**All six ADVANCED verbs are removed from the CLI surface and `--help`** — their logic
|
|
602
|
+
moves inside the self-healing daily verbs and internal plumbing. No user-facing verb
|
|
603
|
+
remains in this group.
|
|
604
|
+
|
|
605
|
+
| Verb | Fate | Rehomed inside |
|
|
606
|
+
|---|---|---|
|
|
607
|
+
| `runtime-health` | KEEP-INTERNAL | `launch`/`verify` self-healing — extension CDP health probe |
|
|
608
|
+
| `runtime-decision` | KEEP-INTERNAL | `launch` self-healing — quick vs full build decision; mobile bridge health |
|
|
609
|
+
| `runtime-launch` | KEEP-INTERNAL | `launch` self-healing — extension runtime launch sequence (snapshot dist → Chrome → CDP poll; never touches the wallet fixture) |
|
|
610
|
+
| `resolve-extension` | KEEP-INTERNAL | `launch`/`launch --verify` internal plumbing — extension id resolution at attach time |
|
|
611
|
+
| `ensure-ready` | KEEP-INTERNAL | `launch`/`verify` self-healing — stale tab cleanup + CDP readiness wait |
|
|
612
|
+
| `self-test` | KEEP-INTERNAL | CI smoke test; callable via `package.json` scripts (`yarn self-test`); not in `--help` |
|
|
613
|
+
|
|
614
|
+
`manifest` is **REMOVED** (see Surface A fate mapping): (1) validation → `doctor` readiness check; (2) raw dump → `actions --raw`; (3) path + protocol version → `doctor` explain-my-setup. The raw JSON shape previously returned by `manifest --json` is now `mm-harness actions --raw`.
|
|
615
|
+
|
|
616
|
+
Porcelain infra (`ports`, `runtime-status`, `decision`, `status`, `stop`) folds into
|
|
617
|
+
`mm-harness` status/`--json` flags (ABSORB-LATER).
|
|
618
|
+
|
|
619
|
+
## Implementation notes (spec-only; draft code stays hand-rolled)
|
|
620
|
+
|
|
621
|
+
- **CLI library:** the refactor implementation adopts **`commander`** — the same
|
|
622
|
+
library `@farmslot/recipe-harness` uses for its CLI (confirmed:
|
|
623
|
+
`packages/recipe-harness/package.json` deps `"commander": "^12.0.0"`, bin
|
|
624
|
+
`bin/farmslot-recipe.mjs`). Use it for grouped help, positionals, subcommands,
|
|
625
|
+
and completion generation, replacing the hand-rolled parser in `runner/src/cli.ts`.
|
|
626
|
+
- **Positional grammar:** `mm-harness <verb> [target] [flags]`; target is the
|
|
627
|
+
human form, `--platform` the agent form, wired via commander positionals.
|
|
628
|
+
- **Completions (dynamic, sourced from DISCOVER layer — SHIPPED):** `mm-harness`
|
|
629
|
+
ships zsh + bash completions in the npm package (`scripts/completions.sh`) with an
|
|
630
|
+
install command (`mm-harness completions install` → `scripts/install-completions.sh`,
|
|
631
|
+
prints the `source` line to add to `~/.zshrc` / `~/.bashrc`; the farm installer
|
|
632
|
+
companion can auto-source it later — precedent: farmslot's `compdef` flow).
|
|
633
|
+
Completions are **dynamic**, not static:
|
|
634
|
+
- **Commands + targets + flags:** static (from the mm-harness surface): commands,
|
|
635
|
+
the `launch` positional (`ios|android`), `fixtures` (`sync|set`), and per-command flags.
|
|
636
|
+
- **Action names for `call`:** completed live from
|
|
637
|
+
`mm-harness completion-candidates actions` (adapter manifest, cache-backed).
|
|
638
|
+
- **Flow refs for `run`:** completed live from `mm-harness completion-candidates flows`
|
|
639
|
+
(recipe library) alongside recipe-file completion. **REAL** on
|
|
640
|
+
`@farmslot/recipe-harness >= 0.3.3` — `completion-candidates flows` enumerates the
|
|
641
|
+
resolved library flows (`loadRecipeLibraries`) instead of degrading to empty.
|
|
642
|
+
- **Cache:** per-checkout cache file (`{runtimeDir}/.completion-cache.json`),
|
|
643
|
+
invalidated on `mm-harness install` (overlay change). TTL: short (60 s fallback),
|
|
644
|
+
refreshed lazily on first tab-complete after invalidation. Cache miss = silent
|
|
645
|
+
static fallback; never blocks the shell.
|
|
646
|
+
- **Principle:** DISCOVER feeds the agent (compose), the shell (complete), and the
|
|
647
|
+
docs (cheatsheet) — one source of truth for what actions and flows exist.
|
|
648
|
+
Replaces `completions/_recipe` (removed).
|
|
649
|
+
- **Resolution order everywhere:** flag > env > config/fixture > built-in default.
|
|
650
|
+
|
|
651
|
+
---
|
|
652
|
+
|
|
653
|
+
# Part 3 — Caller audit (0 unmapped)
|
|
654
|
+
|
|
655
|
+
Every place that invokes a removed name today → what it calls after the wave.
|
|
656
|
+
An unlisted caller = the wave can't ship. Grounded in `grep` over this repo +
|
|
657
|
+
the sibling `farmslot` repo.
|
|
658
|
+
|
|
659
|
+
## In-repo (metamask-recipe-runner)
|
|
660
|
+
|
|
661
|
+
| Caller (path) | Invokes today | After |
|
|
662
|
+
|---|---|---|
|
|
663
|
+
| `orchestration/{core,mobile}/inject.sh`, `orchestration/extension/inject.mjs` | Writes delegate `runner/bin/metamask-recipe` into the installed overlay | Write `runner/bin/mm-harness` delegate |
|
|
664
|
+
| `orchestration/mobile/{launch,live}.sh`, `orchestration/extension/{live,launch,ensure-browser}.sh` | `RUNNER_BIN=…/runner/bin/metamask-recipe`; human hints "Run metamask-recipe … prepare / runtime-launch" | `mm-harness` bin path + updated hint text |
|
|
665
|
+
| `orchestration/extension/runtime.ts` (`runtime-launch` help block) | Prints `bin/metamask-recipe run … --adapter extension …` | `mm-harness run …` |
|
|
666
|
+
| `orchestration/extension/live.sh` (`display_args`, `easyCommand`) | resolved — prints `mm-harness launch --verify …` | done |
|
|
667
|
+
| `orchestration/mobile/live.sh` (`easyCommand`, echo) | resolved — prints `mm-harness launch <target> --verify` | done |
|
|
668
|
+
| `orchestration/{core,mobile,extension,lib}/*` error prefixes `metamask-recipe: …` | cosmetic name in messages | `mm-harness: …` |
|
|
669
|
+
| `completions/_recipe` | zsh completion for `recipe`/`mm-recipe`/`mme-recipe` | Removed; replaced by bundled `mm-harness` completions |
|
|
670
|
+
| `bin/metamask-recipe` `mobile`/`extension` passthrough | execs `bin/mm-recipe` / `bin/mme-recipe` | Absorbed — `mm-harness` verbs; passthrough removed |
|
|
671
|
+
| `package.json` `bin`, `scripts` (`self-test`, `manifest:*`) | `bin/metamask-recipe …` | `bin/mm-harness …`; `manifest:*` scripts → `mm-harness actions --raw` (protocol dump) or removed if CI-only |
|
|
672
|
+
|
|
673
|
+
## Sibling repo (farmslot) — updated in the same wave
|
|
674
|
+
|
|
675
|
+
| Caller (path) | Invokes today | After |
|
|
676
|
+
|---|---|---|
|
|
677
|
+
| `projects/metamask-extension-farm/project.json`, `projects/metamask-core-farm/project.json` | farm hooks / commands calling the runner bin + skill | `mm-harness` verbs |
|
|
678
|
+
| `projects/metamask-extension-farm/setup/*.sh` (`launch-browser.sh`, `health-check.sh`, `ensure-runtime-ready.sh`, `preflight.sh`) | `metamask-recipe` / `mme-recipe` invocations | `mm-harness` verbs |
|
|
679
|
+
| `projects/metamask-extension-farm/setup/generate-fixture.cjs` | fixture generation referencing the runner | `mm-harness fixtures …` where applicable |
|
|
680
|
+
| `projects/metamask-core-farm/scripts/check-core-farm.mjs`, `fixtures/README.md`, `AGENTS.md` | `metamask-recipe` verbs | `mm-harness` verbs |
|
|
681
|
+
| `projects/*/templates/worker/*.md`, `fixtures/**/*.md`, `learnings/LEARNINGS.md`, architecture docs | documented `metamask-recipe`/`mm-recipe`/`mme-recipe` commands | rewritten to `mm-harness` |
|
|
682
|
+
| farm project-shortcut hooks (the slot-bound "same verb name" bindings) | wrap runner/skill invocations | wrap `mm-harness` (one verb vocabulary, three bindings) |
|
|
683
|
+
|
|
684
|
+
**Completeness:** every grep hit above is mapped; **0 unmapped**. Any new caller
|
|
685
|
+
discovered before the wave must be added here first.
|
|
686
|
+
|
|
687
|
+
---
|
|
688
|
+
|
|
689
|
+
# Part 4 — Env-var audit (0 unmapped)
|
|
690
|
+
|
|
691
|
+
Env vars a human is told to type today become flags with config/fixture defaults.
|
|
692
|
+
`[ENV-GAP]` = a human types it today; end state = flag + stored default, zero
|
|
693
|
+
prefix. Agent/CI may still set the env (resolution: flag > env > config > default).
|
|
694
|
+
|
|
695
|
+
| Env var (today) | Human-typed? | End-state flag | Config/fixture default | Tag |
|
|
696
|
+
|---|---|---|---|---|
|
|
697
|
+
| `MOBILE_PREFLIGHT_MODE` / `RECIPE_HARNESS_MOBILE_PREFLIGHT_MODE` | yes | `launch --build` (tier) | quick tier default | [ENV-GAP] |
|
|
698
|
+
| `MM_PASSWORD` (unlock) | yes | none — `fixtures set` reads the fixture | wallet fixture password | [ENV-GAP] — the canonical anti-pattern |
|
|
699
|
+
| `RECIPE_WALLET_FIXTURE` | yes | `fixtures set --fixture` | slot `wallet-fixture.json` | [ENV-GAP] |
|
|
700
|
+
| `RECIPE_LIBRARY_PATH` | yes | `run/flows --library` | personal/team then canonical | [ENV-GAP] |
|
|
701
|
+
| `IOS_SIMULATOR` / `ADB_SERIAL` / `ANDROID_SERIAL` / `ANDROID_DEVICE` | yes | `launch --device` | first booted sim / slot config | [ENV-GAP] |
|
|
702
|
+
| `CDP_PORT` / `RECIPE_CDP_PORT` | yes | `--cdp-port` | slot/runtime context | [ENV-GAP] (agent flag ok) |
|
|
703
|
+
| `WATCHER_PORT` / `METRO_PORT` / `RECIPE_WATCHER_PORT` | yes | `--watcher-port` | slot/runtime context | [ENV-GAP] |
|
|
704
|
+
| `RECIPE_LOG_UI` / `RECIPE_LOG_EVENTS` | yes | `logs --full` / `--events` | compact, 10 | [ENV-GAP] |
|
|
705
|
+
| `RECIPE_HARNESS_ROOT` / `RECIPE_RUNTIME_DIR` | rarely | `--harness-root` (agent) | `path-defaults.json` | KEEP-INTERNAL |
|
|
706
|
+
| `FARMSLOT_SLOT_ID` / `RECIPE_SLOT_ID` / `SLOT_ID` | fleet only | `--slot` | dispatch context | KEEP-INTERNAL (agent/fleet) |
|
|
707
|
+
| `PLATFORM` / `RECIPE_HARNESS_PLATFORM` | agent | `--platform` / positional target | auto-detect | KEEP-INTERNAL (agent) |
|
|
708
|
+
| `MOBILE_METRO_CLEAR` / `MOBILE_BUNDLE_PREWARM*` / `METRO_MAX_WORKERS` / `RECIPE_UP_INSTALL_ATTEMPTED` / `METAMASK_RECIPE_*` | internal | internal knobs of `launch` | built-in | KEEP-INTERNAL |
|
|
709
|
+
|
|
710
|
+
**Completeness:** every human-typed env prefix in the current surface is mapped to
|
|
711
|
+
a flag + default (`[ENV-GAP]`); internal/agent-only vars are KEEP-INTERNAL;
|
|
712
|
+
**0 unmapped**.
|
|
713
|
+
|
|
714
|
+
**Implemented:** the `[ENV-GAP]` flags exist with resolution order **flag > env >
|
|
715
|
+
config/default** (the flag sets the env var the porcelain reads, so it wins; an
|
|
716
|
+
absent flag leaves the agent/CI env untouched):
|
|
717
|
+
- `launch --device` (`IOS_SIMULATOR` for ios · `ADB_SERIAL`/`ANDROID_SERIAL`/`ANDROID_DEVICE` for android)
|
|
718
|
+
- `launch --cdp-port` (`CDP_PORT`/`RECIPE_CDP_PORT`) · `launch --watcher-port` (`WATCHER_PORT`/`METRO_PORT`/`RECIPE_WATCHER_PORT`) — numeric, teaching error otherwise
|
|
719
|
+
- `launch --build` covers `MOBILE_PREFLIGHT_MODE` (tier) · `logs --full`/`--events` cover `RECIPE_LOG_UI`/`RECIPE_LOG_EVENTS`
|
|
720
|
+
- `fixtures set --fixture` (`RECIPE_WALLET_FIXTURE`) · `run`/`flows --library` (`RECIPE_LIBRARY_PATH`)
|
|
721
|
+
- `MM_PASSWORD` stays flag-less by design — `fixtures set` reads the password FROM the fixture.
|
|
722
|
+
|
|
723
|
+
Precedence is contract-tested in `tests/contract/mm-harness-wave5.test.sh`.
|
|
724
|
+
|
|
725
|
+
---
|
|
726
|
+
|
|
727
|
+
# Part 5 — Agent Error Contract
|
|
728
|
+
|
|
729
|
+
Agents (orchestrators, CI, LLM tool-callers) need machine-grade signals to decide: retry, escalate, or abort. This section defines the complete contract: structured errors, stable codes, exit taxonomy, event stream, and healing policy. All `--json` output from every verb conforms to this contract.
|
|
730
|
+
|
|
731
|
+
## §5.1 Structured error object
|
|
732
|
+
|
|
733
|
+
Every failure in `--json` mode produces an `error` field at the top level:
|
|
734
|
+
|
|
735
|
+
```json
|
|
736
|
+
{
|
|
737
|
+
"error": {
|
|
738
|
+
"code": "METRO_START_FAILED",
|
|
739
|
+
"message": "Metro bundler failed to start within 60s",
|
|
740
|
+
"cause": "port 8081 already in use by pid 12345",
|
|
741
|
+
"retryable": true,
|
|
742
|
+
"userAction": "Free port 8081 (kill pid 12345), then re-run: mm-harness launch ios"
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
| Field | Type | Description |
|
|
748
|
+
|---|---|---|
|
|
749
|
+
| `code` | string (UPPER_SNAKE) | Stable machine code — never changes; safe to `switch` on in agent code |
|
|
750
|
+
| `message` | string | Human-readable summary |
|
|
751
|
+
| `cause` | string \| null | Root cause detail (OS error, process output, etc.) |
|
|
752
|
+
| `retryable` | boolean | `true` = same command may succeed on retry; `false` = agent must change inputs or escalate |
|
|
753
|
+
| `userAction` | string \| null | Exact next command or manual step |
|
|
754
|
+
|
|
755
|
+
## §5.2 Stable recovery codes (`recovered[]`)
|
|
756
|
+
|
|
757
|
+
`recovered[]` in `--json` output lists what was healed. These are stable enum values — not prose, safe to match in agent code:
|
|
758
|
+
|
|
759
|
+
| Code | What happened |
|
|
760
|
+
|---|---|
|
|
761
|
+
| `overlay.reinstalled` | Runtime overlay was missing or corrupted; reinstalled inline |
|
|
762
|
+
| `metro.restarted` | Metro bundler was dead; restarted and bundle prewarmed |
|
|
763
|
+
| `chrome.reopened` | Chrome was closed or crashed; reopened with correct profile/flags |
|
|
764
|
+
| `cdp.reconnected` | CDP websocket was stale/disconnected; reconnected and verified |
|
|
765
|
+
| `tabs.cleared` | Stale home/onboarding tabs were open; closed before proceeding |
|
|
766
|
+
|
|
767
|
+
All recovery codes are **infra-only**. Healing never touches wallet state: a missing/invalid fixture is not "recovered" — the verb stops with `recoverable:false` and `userAction: "run mm-harness fixtures set"` (the only path that mutates wallet data).
|
|
768
|
+
|
|
769
|
+
## §5.3 `mutations[]` — side-effect manifest
|
|
770
|
+
|
|
771
|
+
Every `--json` response includes `mutations[]` listing all side effects the command produced on the system. Agent can diff pre/post state or verify cleanup:
|
|
772
|
+
|
|
773
|
+
```json
|
|
774
|
+
{
|
|
775
|
+
"mutations": [
|
|
776
|
+
{ "type": "file", "path": "/path/to/temp/recipe/harness/extension/inject.js", "action": "created" },
|
|
777
|
+
{ "type": "process", "name": "metro", "pid": 45678, "action": "started" },
|
|
778
|
+
{ "type": "port", "port": 8081, "action": "bound" },
|
|
779
|
+
{ "type": "fixture", "path": "/path/to/wallet-fixture.json", "action": "seeded" },
|
|
780
|
+
{ "type": "tab", "url": "chrome-extension://abc/home.html", "action": "closed" }
|
|
781
|
+
]
|
|
782
|
+
}
|
|
783
|
+
```
|
|
784
|
+
|
|
785
|
+
| `type` | `action` values | When emitted |
|
|
786
|
+
|---|---|---|
|
|
787
|
+
| `file` | `created` / `deleted` / `modified` | Overlay install/cleanup; fixture write |
|
|
788
|
+
| `process` | `started` / `killed` | Metro start/stop; Chrome open/close |
|
|
789
|
+
| `port` | `bound` / `freed` | Metro/CDP port lifecycle |
|
|
790
|
+
| `fixture` | `seeded` / `cleared` | Wallet fixture apply/reset |
|
|
791
|
+
| `tab` | `opened` / `closed` | Chrome tab lifecycle (extension CDP) |
|
|
792
|
+
|
|
793
|
+
## §5.4 Phase state-machine
|
|
794
|
+
|
|
795
|
+
Every `--json` response includes a `phase` field reflecting the last completed phase. `--json-stream` emits one event per transition. Agents can detect exactly where a failure occurred.
|
|
796
|
+
|
|
797
|
+
**`run` phases:**
|
|
798
|
+
```
|
|
799
|
+
resolve → install → healthcheck → recover → validate → launch → execute
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
**`launch` phases:**
|
|
803
|
+
```
|
|
804
|
+
resolve → install → healthcheck → recover → launch → verify
|
|
805
|
+
```
|
|
806
|
+
|
|
807
|
+
| Phase | What happens |
|
|
808
|
+
|---|---|
|
|
809
|
+
| `resolve` | Recipe/target/adapter resolved; library sources loaded |
|
|
810
|
+
| `install` | Overlay auto-ensure check; install if missing (overlay install notice to stderr on first install) |
|
|
811
|
+
| `healthcheck` | Probe runtime state (Metro/CDP/bridge) |
|
|
812
|
+
| `recover` | Apply `--heal` policy; execute recovery actions; emit `recovered[]` |
|
|
813
|
+
| `validate` | Adapter-aware recipe validation (action existence, platform support, fixture preconditions). `run --plan` exits here. |
|
|
814
|
+
| `launch` | Start/attach to app (Metro prewarm, Chrome open, CDP attach) |
|
|
815
|
+
| `execute` | Run recipe nodes; collect trace/evidence (`run` only) |
|
|
816
|
+
| `verify` | Smoke verify after launch (`launch --verify` only) |
|
|
817
|
+
|
|
818
|
+
## §5.5 JSONL event stream (`--json-stream`)
|
|
819
|
+
|
|
820
|
+
`--json-stream` emits one JSON object per line on stdout as the command progresses. Parseable with `jq -R 'fromjson?'`. Schema is versioned via `schemaVersion`.
|
|
821
|
+
|
|
822
|
+
```jsonl
|
|
823
|
+
{"schemaVersion":1,"event":"phase","phase":"resolve","ts":"2026-07-02T10:00:00.000Z"}
|
|
824
|
+
{"schemaVersion":1,"event":"phase","phase":"install","ts":"2026-07-02T10:00:00.100Z"}
|
|
825
|
+
{"schemaVersion":1,"event":"mutation","mutation":{"type":"file","path":"/...","action":"created"},"ts":"..."}
|
|
826
|
+
{"schemaVersion":1,"event":"phase","phase":"validate","ts":"..."}
|
|
827
|
+
{"schemaVersion":1,"event":"phase","phase":"execute","ts":"..."}
|
|
828
|
+
{"schemaVersion":1,"event":"node","index":0,"action":"metamask.wallet.unlock","status":"running","ts":"..."}
|
|
829
|
+
{"schemaVersion":1,"event":"node","index":0,"action":"metamask.wallet.unlock","status":"passed","ts":"..."}
|
|
830
|
+
{"schemaVersion":1,"event":"complete","status":"pass","exitCode":0,"recovered":[],"mutations":[...],"ts":"..."}
|
|
831
|
+
```
|
|
832
|
+
|
|
833
|
+
Event types: `phase` · `node` · `mutation` · `recovery` · `error` · `complete`. The terminal `complete` event always appears (even on failure) so agents have a clean sentinel.
|
|
834
|
+
|
|
835
|
+
## §5.6 Exit code taxonomy
|
|
836
|
+
|
|
837
|
+
Consistent across all verbs. Agents must not retry on exit 2 or 4.
|
|
838
|
+
|
|
839
|
+
| Code | Meaning | Agent action |
|
|
840
|
+
|---|---|---|
|
|
841
|
+
| 0 | Success | Proceed |
|
|
842
|
+
| 1 | Recipe/operation failed (app-logic: assertion, action error, fixture mismatch) | Diagnose; do NOT auto-retry blindly |
|
|
843
|
+
| 2 | Bad args / unsupported platform / missing required input | Fix command; do NOT retry |
|
|
844
|
+
| 3 | Infra failure not auto-healed (healing `off`, or a recovery bound was hit) | Re-run with default healing; escalate if still 3 |
|
|
845
|
+
| 4 | Bounded / not-retryable (same recovery already failed once; failure needs a wallet fixture write — run `fixtures set`; recipe running) | Escalate; do NOT retry |
|
|
846
|
+
| 5 | Validation failure (`run`, `run --plan`, `call`): adapter-aware validation found errors | Fix recipe; do NOT retry |
|
|
847
|
+
|
|
848
|
+
**Bound-specific `error.code` values.** When a recovery bound fires, `error.code` names the exact bound (not a generic `LAUNCH_FAILED`) so agents branch on the cause. Every bound also carries `error.originalError` — the original failure output verbatim — so the real cause is never replaced by the classification note. Shared by `launch`, `run`, and `call`:
|
|
849
|
+
|
|
850
|
+
| `error.code` | Fires when | Exit |
|
|
851
|
+
|---|---|---|
|
|
852
|
+
| `APP_LOGIC_FAILURE` | Failure is app-logic (assertion, wrong route) — healing cannot help; surfaced verbatim | 1 |
|
|
853
|
+
| `WALLET_STATE_REQUIRED` | Recovery would need a seeded wallet — healing never touches fixtures (`userAction: "run mm-harness fixtures set"`) | 4 |
|
|
854
|
+
| `RECIPE_RUNNING` | A recipe is executing — recovery refused to avoid corrupting mid-run state | 4 |
|
|
855
|
+
| `SAME_RECOVERY_TWICE` | The same recovery already failed once this invocation — refused to avoid a loop | 4 |
|
|
856
|
+
|
|
857
|
+
**Stopping agent retry loops:** when `recoverable:false` appears in `--json` alongside `attemptedRecoveries[]`, the agent MUST NOT retry. The harness already attempted recovery and it failed or hit a bound.
|
|
858
|
+
|
|
859
|
+
```json
|
|
860
|
+
{
|
|
861
|
+
"recoverable": false,
|
|
862
|
+
"attemptedRecoveries": ["metro.restarted"],
|
|
863
|
+
"error": { "code": "SAME_RECOVERY_TWICE", "retryable": false, "originalError": "...", ... }
|
|
864
|
+
}
|
|
865
|
+
```
|
|
866
|
+
|
|
867
|
+
## §5.7 Healing policy (`--heal`)
|
|
868
|
+
|
|
869
|
+
Controls what the harness is allowed to repair autonomously. Defaults differ by verb: `launch` defaults `auto`; `run` and `call` default `infra-only`.
|
|
870
|
+
|
|
871
|
+
| Value | Behavior |
|
|
872
|
+
|---|---|
|
|
873
|
+
| `off` | No healing, no auto-install. Fail fast with a teaching error; exact broken state preserved (repro-preserving). |
|
|
874
|
+
| `infra-only` | Heal overlay/runtime transport (Metro, Chrome, CDP, tabs) only. NEVER touches wallet state. `run`/`call` default. If a wallet write is needed → `recoverable:false` exit 4, `userAction: "run mm-harness fixtures set"`. |
|
|
875
|
+
| `auto` | Auto-ensure the overlay + heal transport silently. Still NEVER touches wallet state (that is `fixtures set`, explicit only). `launch` default. |
|
|
876
|
+
|
|
877
|
+
`--yes` confirms any prompt non-interactively (CI/agent safe). The three `--heal` values above are the ONLY opt-out mechanism: healing bounds are ALWAYS enforced and no flag bypasses them, and wallet state is mutated only by the explicit `fixtures set`.
|
|
878
|
+
|
|
879
|
+
**Recovery bounds (always enforced regardless of `--heal`):**
|
|
880
|
+
- Recipe currently executing → refuse all recovery (would corrupt mid-run state)
|
|
881
|
+
- Failure is app-logic (assertion failure, wrong route) → refuse infra recovery (wrong tool)
|
|
882
|
+
- Same recovery already failed once in this invocation → refuse (avoid infinite loops)
|
|
883
|
+
|
|
884
|
+
---
|
|
885
|
+
|
|
886
|
+
## Considered and rejected
|
|
887
|
+
|
|
888
|
+
**Teaching-tombstone stub bins (`mm-harness-legacy`, `metamask-recipe-stub`, etc.):** Rejected. Installing stub binaries that print "this command moved to mm-harness" before any user has adopted the new surface creates pre-adoption debt with zero payoff. The coordinated wave (Part 3) updates all callers atomically. No tombstone bins needed; no user is mid-flight on the old surface at wave time.
|
|
889
|
+
|
|
890
|
+
---
|
|
891
|
+
|
|
892
|
+
# Part 6 — How farmslot calls mm-harness (preflight & the fleet binding)
|
|
893
|
+
|
|
894
|
+
"Preflight" survives as a **capability, not an exposed concept**: it is what `launch`
|
|
895
|
+
does before returning (readiness: Metro/bundle → app → health/bridge poll), with
|
|
896
|
+
`launch --verify` as the prove-it tier and `doctor` as the read-only view.
|
|
897
|
+
`FARMSLOT_MOBILE_PREFLIGHT_MODE` is an `[ENV-GAP]` row that dies (Part 4).
|
|
898
|
+
|
|
899
|
+
The fleet is just another agent-grade caller:
|
|
900
|
+
|
|
901
|
+
- farmslot prepare profiles keep their phases (git / fixtures / deps = farm-level
|
|
902
|
+
checkout management, NOT harness work); the preflight/health hooks become one
|
|
903
|
+
call: `mm-harness launch <target> --verify --heal=auto --json`.
|
|
904
|
+
- The farm's `preflight.sh` / `runway-preflight.sh` step-2 logic thins to that call
|
|
905
|
+
(already Part-3 caller-audit rows; updated in the caller sweep wave). The Runway
|
|
906
|
+
artifact install remains a farm vehicle invoked before the harness call.
|
|
907
|
+
- **Same-capability guarantee is by construction:** the machine contract (`--json`
|
|
908
|
+
schemas, `--heal`, exit-code taxonomy, `recovered[]` / `mutations[]` /
|
|
909
|
+
`recoverable:false`) is the same one agents use — gateway slot-tracking /
|
|
910
|
+
gate-summary consume structured output instead of scraping bash logs, strictly
|
|
911
|
+
richer than today.
|
|
912
|
+
- **Three bindings, one verb:** the engineer types `mm-harness launch ios`; the
|
|
913
|
+
slot's prepare hook calls the same verb with slot context; dispatch scales it
|
|
914
|
+
across the fleet.
|
|
915
|
+
|