@deeeed/metamask-harness 0.17.4 → 0.18.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 (93) hide show
  1. package/CHANGELOG.md +775 -0
  2. package/README.md +65 -71
  3. package/adapters/extension/ensure-browser.sh +12 -1
  4. package/adapters/extension/inject.mjs +7 -0
  5. package/adapters/extension/launch-browser.cjs +10 -3
  6. package/adapters/extension/lib/chrome-args.cjs +31 -0
  7. package/adapters/extension/lib/macos-focus.cjs +32 -0
  8. package/adapters/extension/live.sh +10 -20
  9. package/adapters/manifest.json +8 -0
  10. package/adapters/mobile/open-device.sh +45 -7
  11. package/adapters/mobile/verify.sh +15 -3
  12. package/adapters/shared/harness-source-fingerprint.mjs +49 -0
  13. package/adapters/shared/install-repo-deps.sh +1 -5
  14. package/adapters/shared/open-debug.mjs +184 -103
  15. package/adapters/shared/resolve-slot-ports-core.mjs +23 -6
  16. package/adapters/shared/resolve-slot-ports.sh +22 -5
  17. package/bin/mm-harness +15 -3
  18. package/dist/adapters/core/surface.js +4 -1
  19. package/dist/adapters/extension/console-capture.js +3 -1
  20. package/dist/adapters/extension/harness-freshness.js +39 -0
  21. package/dist/adapters/extension/product-config.js +110 -0
  22. package/dist/adapters/extension/runtime-decision.js +20 -71
  23. package/dist/adapters/extension/surface.js +19 -1
  24. package/dist/adapters/mobile/prepare.js +17 -0
  25. package/dist/adapters/mobile/source-freshness.js +26 -41
  26. package/dist/adapters/mobile/surface.js +4 -1
  27. package/dist/adapters/resolve-slot-ports.js +2 -0
  28. package/dist/adapters/slot-ports.js +13 -32
  29. package/dist/adapters.js +50 -17
  30. package/dist/checkout-lock.js +27 -2
  31. package/dist/cli-color.js +19 -0
  32. package/dist/cli-commands.js +1 -1
  33. package/dist/cli.js +2 -3
  34. package/dist/command-contract.js +13 -3
  35. package/dist/commands/call.js +115 -29
  36. package/dist/commands/checklist.js +4 -1
  37. package/dist/commands/completion-candidates.js +20 -13
  38. package/dist/commands/debug.js +31 -38
  39. package/dist/commands/doctor.js +33 -6
  40. package/dist/commands/fixtures.js +65 -17
  41. package/dist/commands/flows.js +39 -10
  42. package/dist/commands/launch/extension.js +40 -15
  43. package/dist/commands/launch/index.js +41 -5
  44. package/dist/commands/list-executables.js +151 -29
  45. package/dist/commands/manifest.js +127 -18
  46. package/dist/commands/parse-args.js +11 -1
  47. package/dist/commands/run-engine.js +384 -56
  48. package/dist/commands/run.js +112 -17
  49. package/dist/commands/shared.js +22 -2
  50. package/dist/commands/status-probe.js +3 -0
  51. package/dist/commands/status.js +1 -0
  52. package/dist/completions-cache.js +1 -1
  53. package/dist/doctor.js +56 -6
  54. package/dist/harness.js +6 -5
  55. package/dist/heal-bounds.js +1 -1
  56. package/dist/live-adapter-contract.js +132 -12
  57. package/dist/manifest.js +161 -1
  58. package/dist/mm-harness-cli.js +13 -7
  59. package/dist/recipe-security.js +178 -0
  60. package/dist/runner.js +87 -13
  61. package/dist/runtime-context.js +8 -26
  62. package/docs/CONTRIBUTING.md +137 -0
  63. package/docs/QA.md +185 -0
  64. package/docs/RECIPES.md +161 -0
  65. package/docs/SECURITY.md +88 -0
  66. package/library/actions/core/perps/read_account.mjs +2 -2
  67. package/library/actions/core/perps/read_orders.mjs +2 -1
  68. package/library/actions/core/perps/read_positions.mjs +2 -1
  69. package/library/actions/core/wallet/list_accounts.mjs +95 -0
  70. package/library/actions/extension/platform/cdp.mjs +1 -0
  71. package/library/actions/extension/wallet/list_accounts.mjs +41 -0
  72. package/library/actions/mobile/platform/bridge.mjs +1 -5
  73. package/library/actions/mobile/wallet/list_accounts.mjs +37 -0
  74. package/library/manifests/core.action-manifest.json +61 -0
  75. package/library/manifests/extension.action-manifest.json +53 -0
  76. package/library/manifests/mobile.action-manifest.json +53 -0
  77. package/library/recipes/runner/action-validation.extension.recipe.json +8 -1
  78. package/library/recipes/runner/action-validation.mobile.recipe.json +8 -1
  79. package/package.json +10 -6
  80. package/scripts/completions.sh +7 -7
  81. package/docs/ADAPTER-SURFACE.md +0 -26
  82. package/docs/CHEATSHEET.md +0 -48
  83. package/docs/CLI-ERGONOMICS-AUDIT.md +0 -32
  84. package/docs/CLI-ERGONOMICS-HUMAN-QA.md +0 -64
  85. package/docs/CODE-MAP.md +0 -62
  86. package/docs/UX-PRINCIPLES.md +0 -66
  87. package/docs/VIDEO-DEMO-VALIDATION.md +0 -74
  88. package/docs/architecture.md +0 -88
  89. package/docs/live-adapter-contract.md +0 -190
  90. package/docs/package-boundaries.md +0 -47
  91. package/docs/perps-flow-catalog.md +0 -235
  92. package/docs/recipe-libraries.md +0 -71
  93. package/docs/runtime-file-conventions.md +0 -36
package/CHANGELOG.md CHANGED
@@ -2,6 +2,781 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ### Added
6
+
7
+ - Recipe runs now preflight source provenance and action risk before runtime preparation; untrusted restricted plans require exact-digest approval.
8
+ - Local checks now follow explicitly linked Farmslot packages instead of an unrelated sibling checkout.
9
+ - Added fast Vitest/V8 unit coverage for TypeScript CLI contracts, redaction, discovery, targeting, readiness, diagnostics, and bounded recovery; the normal `yarn check` gate now runs the unit suite.
10
+ - `mm-harness flows describe <ref>` now exposes the upstream resolved flow contract: source/path precedence, parameter schema/defaults, complete definition, shadowing, and an authored call node or clearly labeled template.
11
+ - Exact human action detail now prints one runnable `mm-harness call` and the authored recipe-node example already present in the action manifest.
12
+ - Doctor human and JSON output now identify the active mm-harness version, executable, package source, and install kind (`local-link`, `source-checkout`, `project-install`, or `global-install`).
13
+ - `mm-harness run <recipe> --describe` now shows a runnable recipe's title, description, source, adapter, declared recipe-owned inputs, composed actions/flows, and a copyable `--plan` command in human and JSON output.
14
+ - Action and flow discovery now identifies the providing library and source path, and declared team/task-library actions execute through the same adapter path as packaged actions.
15
+ - Added the minimal cross-platform `metamask.wallet.list_accounts` action for Extension, Mobile, and headless Core without expanding the default domain catalog.
16
+ - Recipe runs now choose a unique checkout-scoped artifact directory automatically, preferring an active checkout-local task directory while preserving explicit overrides.
17
+ - Added a concise capability-ladder guide covering official primitives, MetaMask defaults, team libraries, and independently proven task-local controller/CDP operations.
18
+
19
+ ### Changed
20
+
21
+ - Consolidated overlapping guides, historical handovers, and generated audits into five current, command-verified documents for onboarding, recipes, security, QA, and contribution.
22
+ - Require Farmslot protocol `^0.10.0` and recipe-harness `^0.8.0` for source-trust enforcement and delegated recovery.
23
+ - Reuse the authorized execution context for the first attempt, avoiding duplicate adapter resolution while retaining the immediate pre-side-effect trust check.
24
+ - Harness-owned progress, success, failure, warning, and `Next:` lines now share one semantic color formatter across launch, install/verify, fixtures, and other shared progress emitters; raw child logs remain untouched and machine output remains byte-clean.
25
+ - Discovery is command-scoped: `call --list` shows callable actions, while `run --list` and `run` completion show only complete recipes that `run` actually accepts; reusable flows remain under `mm-harness flows`.
26
+ - Direct `call` execution preserves typed action inputs, redacts sensitive inputs/results, suppresses graph-owned HUD completion, and reports the selected capability source.
27
+ - Mobile application logs are captured separately from Metro output, and the app console collector starts only after the selected bridge target is ready.
28
+ - Removed duplicate Extension launcher names, legacy port-resolver aliases, and Mobile product-overlay copies so the package has one current execution path per capability.
29
+
30
+ ### Fixed
31
+
32
+ - Extension launch now rejects missing or placeholder Infura configuration before build and prints the documented setup action instead of opening a broken wallet.
33
+ - Mobile status now reports the verified `Login` and `LockScreen` routes as locked even when app state still contains a selected account.
34
+ - Extension launch now stops before build when the checkout's Playwright Chromium is missing and prints its exact install command, instead of opening an incompatible system Chrome profile.
35
+ - Extension launch now preserves an isolated `CHROME_USER_DATA_DIR` through the live sequencer so browser ownership and launch use the same profile.
36
+ - Isolated macOS Extension profiles now use Chromium's mock keychain, preventing credential prompts from blocking automated startup or touching the operator's login keychain.
37
+ - `doctor` and `doctor --fix` now report runtime readiness and one exact platform-owned recovery command after configuration repair, instead of implying an unprepared checkout is finished while dependencies remain missing.
38
+ - Approval recovery now explains that the plan digest is bound to the same project root, artifact directory, and execution environment.
39
+ - Untrusted custom adapters reject unresolved dynamic imports with a structured trust error; trusted adapters retain direct controller and CDP loading.
40
+ - The CLI launcher no longer requires `basename` on `PATH` when resolving recovery-command paths.
41
+ - Flow delegation now preserves the upstream non-zero exit status and uses the public `mm-harness` name in recovery commands.
42
+ - Flow discovery preserves repeatable library precedence so its printed inspect and recovery commands execute unchanged without duplicating or dropping sources.
43
+ - Extension console capture now bounds its CDP readiness probe at three seconds, so a listening but stalled debug endpoint cannot freeze launch or recipe diagnostics.
44
+ - Extension debugger contract coverage now exercises the successful local DevTools attach handshake and window creation path instead of only the unreachable-CDP failure.
45
+ - Recipe completion caches are adapter-scoped, configured-library failures degrade to empty completion, and every nested recipe name shown by `run --list` resolves back to the same file.
46
+ - `fixtures set` on Extension now resets and seeds the slot-owned profile from the selected wallet fixture using the existing build, then validates account parity; missing builds and failed seeding return typed recovery instead of a wallet-adapter stack.
47
+ - `launch ios` now opens Simulator.app in the background when the selected simulator is booted but its GUI is absent, while leaving an already-open Simulator untouched unless `MM_HARNESS_FOCUS_SIMULATOR=1` is explicitly requested.
48
+ - Extension runtime freshness now normalizes only the intentional slot-title prefix in `home.html` and `sidepanel.html`, so slot identification no longer makes a freshly seeded runtime report itself stale while other HTML changes remain detectable.
49
+ - Extension browser launch now restores the exact previously frontmost macOS application process after a background profile launch, preventing Chrome for Testing from taking keyboard focus while retaining the explicit `MM_HARNESS_FOCUS_BROWSER=1` opt-in.
50
+ - Stale checkout locks are reclaimed through a unique atomic rename claim and owner revalidation, so a concurrent contender cannot blindly delete a freshly acquired lock.
51
+ - Direct calls now keep default evidence in a stable checkout-scoped directory derived from the exact call and always remove the temporary synthesized recipe containing raw action inputs.
52
+ - Mobile `launch --verify` now bounds the brief bridge-ready/device-discovery race, so a newly booted simulator is not falsely rejected as `DEVICE_NOT_FOUND`.
53
+ - `flows --library <name=path>` now shows that explicit source in precedence order and forwards it exactly once instead of reporting a duplicate-library failure.
54
+ - `run --help` now teaches the automatic active-task or checkout-local artifact directory instead of claiming `--artifacts-dir` is required.
55
+ - Streaming leaves now complete cooperative signal traps before the parent exits on Ctrl+C, with a bounded kill fallback for unresponsive process trees.
56
+ - `mm-harness actions` now presents grouped, color-aware human output while preserving byte-clean JSON and piped output.
57
+ - Explicit artifact paths retain caller-relative CLI semantics; task-derived artifact directories remain checkout-scoped.
58
+ - External runtime-context overrides are honored, shell/TypeScript port resolution preserves pre-set values, Android screenshots never pass display names as ADB serials, and stop recognizes all owned Chrome profile variants.
59
+ - Checkout liveness treats `EPERM` consistently so a live-but-unsignalable process cannot have its sandbox lock removed.
60
+ - `mm-harness call` now behaves as a direct action probe: it prints redacted normalized inputs and the result without painting automatic recipe progress/completion over the action’s visible outcome. Full `run` executions retain their normal HUD lifecycle.
61
+ - Direct calls preserve every supplied action parameter (including `intent`) and reject recipe-owned `action`/`next` fields; discovery and action help no longer present those structural fields as user parameters.
62
+
63
+ ## 0.17.5 - 2026-07-16
64
+
65
+ ### Changed
66
+
67
+ - Mobile proof preflight now waits for the requested platform's bridge target after an automatic source-change restart; a bridge transition fails with a typed `MOBILE_BRIDGE_NOT_READY` result instead of letting the recipe race into `NO_TARGET`.
68
+ - Extension proof recovery now gives the checkout-owned runtime-dist synchronizer a short bounded handoff window after reattach, avoiding a false stale-runtime failure while still surfacing a real unhealthy build.
69
+ - Extension `status` and `doctor` now report the webpack dev server as `up` only when a target-owned watcher is alive; historical successful build logs no longer imply liveness after `stop`.
70
+ - Mobile overlay freshness no longer reports `pass` when every checkable runner overlay file is absent from the target; missing or drifted files are classified before the empty-overlay success case.
71
+ - Mobile live verification now compares AgenticService/HUD freshness against the canonical `app/dev-tools/AgenticService` runner and product paths instead of skipping the check through the obsolete `app/core/AgenticService` layout.
72
+ - `check diff` now rejects only newly added ESLint directive comments before running repository tools, without treating prose or string literals containing `eslint-disable` as suppressions; pre-existing suppressions remain untouched.
73
+ - Mobile `run`/`call` now fingerprints bundle-affecting source and environment state against the last successful app reload; default infra healing performs one `app.lifecycle restart` before proof when the loaded source is missing or stale, while `--heal off` stops with the exact recovery command.
74
+ - Mobile `metamask.perps.start_state` now restores the fixture-backed unlocked wallet before navigation, so required post-edit app restarts do not race a locked/initializing runtime.
75
+ - Mobile `ui.wait_for` discovery now exposes its `test_id`, `expected`, text, and timeout schema plus an `expected=absent` example; shared recipe examples use the same supported `expected` field instead of the ignored `visibility` spelling.
76
+ - Mobile fast-preflight dependency failures now present numbered standalone vs orchestrated recovery choices, so interactive agents can relay a portable one-character choice instead of inventing an option from an unnumbered `Next:` hint.
77
+ - `checklist mark` now blocks a `mm-harness check diff` gate until that task's own `artifacts/validation-summary.json` records a real diff-check PASS, preventing stale or failed validation from being marked complete.
78
+ - Changed-file ESLint now respects the repository's configured warning severity instead of failing unchanged warnings via `--max-warnings=0`; real failures tell agents at the harness boundary to use existing patterns/tokens rather than suppressing or hiding violations, weakening configuration, or bypassing `check diff`.
79
+ - Mobile Perps `start_state` now enters the requested Perps route before reading orders or positions, initializing the provider client on a freshly launched wallet instead of timing out with `CLIENT_NOT_INITIALIZED`.
80
+
81
+ - Updated the compatible Farmslot runtime set to protocol 0.8, recipe-harness 0.5, and agent-runtime 0.2, making passive `ui.screen`/`ui.visible` observation support available to adapters that declare those observers without a local Farmslot checkout.
82
+ - Checklist terminal signals are monotonic through `mm-harness`: a late numeric or `start` mark can no longer regress a completed or blocked task back to `running`.
83
+ - Core `check diff` now runs changed package tests through their owning Yarn workspaces instead of passing workspace paths to the root Jest configuration.
84
+
85
+ - Mobile iOS launch no longer opens or activates Simulator.app during normal automation; set `MM_HARNESS_FOCUS_SIMULATOR=1` only when an operator explicitly wants the Simulator UI foregrounded.
86
+
87
+ - Public Extension `stop` now tears down the checkout-owned watcher, Chrome/CDP profile processes, viewer tails/windows, and stale runtime PID/lock markers; explicit `--port` overrides only the watcher port and no longer hides the runtime-context CDP owner.
88
+ - Tmux log viewers now require an explicit `RECIPE_TMUX_SESSION` or checkout `agentic-runtime.json` session; merely running from another tmux session never creates, sweeps, or marker-closes windows there, while stale markers are discarded and file logs remain available.
89
+ - Extension `run`/`call --heal auto` now rebuild or reload before proof when source, `dist/chrome`, and the loaded runtime snapshot are out of sync; healing-off runs stop before execution with the exact `launch --build --verify` recovery command.
90
+ - Extension slots now launch with `--remote-allow-origins=http://127.0.0.1:<cdpPort>` (scoped to the slot's own debug origin, never `*`) so the local DevTools frontend can attach; without it Chrome 403s every browser DevTools WebSocket on the debug port. The debug-port flag trio (address + port + allow-origins) is declared once in a shared `chrome-args.cjs` and consumed by both the fresh launcher and the reopen path, so the origin allow-list can never drift away from the port.
91
+ - Extension `launch` now refreshes a slot's injected harness overlay from the runner whenever they diverge: `inject` stamps a source fingerprint into the slot manifest and each launch re-injects on mismatch (or when never installed). A runner change — e.g. a launcher flag — reaches every slot on the next launch instead of silently running a stale injected copy.
92
+ - Extension launch, readiness, reattach, actions, and screenshots preserve the operator's keyboard focus by default while retaining a headed, isolated CDP browser; macOS app launch uses Launch Services in the background, and explicit window focus remains available with `MM_HARNESS_FOCUS_BROWSER=1`.
93
+ - `mm-harness check diff --fix` now repairs only changed files with the checkout's direct ESLint and oxfmt/Prettier binaries, then runs the normal bounded validation, so agents do not need to guess repository lint scripts.
94
+ - Recipe-run infra healing now performs the adapter's runtime recovery before retrying instead of only recording a recovery label and immediately rerunning the recipe.
95
+ - Extension `doctor --fix` now enables the checkout-local Perps build flag when `.metamaskrc` has it disabled, so packaged Perps actions cannot launch against a build that omits their product surface.
96
+ - Extension `ui.screenshot` now falls back from a failed capture-helper window snapshot to bounded Chrome-native capture, then to an explicitly labeled computed-style DOM raster when the headed Chrome screenshot domain is unavailable.
97
+ - Mobile `ui.screenshot` now validates simctl/adb output as PNG evidence before registration and records the native provider, command mode, selected device, and MIME type in artifact provenance.
98
+ - `checklist mark ... complete` now teaches the exact compact quality input, canonical builder command, and coverage artifact when a recipe activates those terminal requirements, instead of forcing agents to reverse-engineer the artifact validator.
99
+
100
+ ### Added
101
+ - Checkout-scoped operation locking prevents concurrent launch, fixture mutation, and recipe execution in one repository while allowing other checkouts/slots to run independently; stale locks from exited processes are reclaimed.
102
+ - `doctor --fix` creates an ignored, checkout-scoped `agentic-runtime.json` when absent, with stable claimed local ports for ordinary standalone clones while preserving explicit or Farmslot-provided slot resources.
103
+ - `fixtures init --from <path>|--dev` initializes the canonical checkout wallet fixture explicitly; `--dev` is a disposable public test wallet and is never suitable for real funds.
104
+
105
+ ### Fixed
106
+ - Extension `debug` now opens a working, offline DevTools console attached to the running slot's extension page: it launches the locally-served bundled DevTools frontend in its own window inside the slot's Chromium instead of Chrome's hosted `chrome-devtools-frontend.appspot.com` URL, which Chrome's WebSocket origin check rejected (the console showed "Debugging connection was closed"). A slot launched before this change fails a pre-open origin probe and reports the exact relaunch command instead of opening a dead console.
107
+ - `doctor --fix` now schema-validates, quarantines, and recreates malformed, semantically invalid, or cross-adapter-contaminated checkout runtime context without touching product data; wallet-fixture recovery commands use `--force` only for invalid or incomplete existing fixtures, while a missing fixture retains the safe unforced initialization command.
108
+ - `logs --source app` now resolves the adapter-owned application console (`extension-console.log` on Extension and the CDP-forwarded `metro.log` on Mobile); an absent console returns `APP_LOG_UNAVAILABLE` instead of falling back to a dev-server log or reporting a usage error.
109
+ - Doctor JSON now retains runtime probe failures, marks every diagnostic check as required or optional, derives its status from required checks, and reports runtime readiness separately.
110
+ - Debug JSON now preserves the leaf target, method, endpoint, and detail; `--no-open` reaches the leaf, and Mobile reports its debug endpoint without invoking it.
111
+ - Checklist diff gates and terminal-signal preservation now share agent-runtime's numeric step semantics, preventing exponent-form step tokens from bypassing a required PASS or regressing a terminal task.
112
+ - Extension Perps config repair and fixture initialization now stage validated, mode-correct regular files before atomic publication, preserve existing config modes, refuse no-force overwrites, do not follow destination symlinks, and reject runtime-ancestor symlinks.
113
+ - Mobile Metro startup now reaps an existing same-port console forwarder even when macOS `ps` pads a short PID, while retaining command-and-port ownership checks.
114
+ - Extension and Core dependency readiness now requires Yarn's node-modules install state when the checkout selects `nodeLinker: node-modules`, preventing a leftover `.yarn/install-state.gz` from reporting removed dependencies as current.
115
+ - Non-tmux Mobile Metro launch now uses a repository-shipped detached Node launcher instead of a same-process-group `nohup` child, so Metro survives the launching terminal/process-group boundary while retaining exact checkout PID, port, env, log, and public-stop ownership.
116
+ - Mobile launch now fingerprints `.js.env`, `.env`, and `.env.local` per runtime directory; a missing or changed successful-bundle baseline forces exactly one cache-cleared Metro restart, while failed starts and mid-launch env changes cannot bless stale transforms.
117
+ - Mobile `fixtures set` now bounds wallet application at 120 seconds, uses short bridge-readiness probes against the resolved slot Metro port, terminates only its owned child process group on timeout, releases the checkout lock, and leaves runtime relaunch to the explicit recovery command.
118
+ - Mobile `doctor --fix` now adds `MM_PERPS_ENABLED=true` and `OVERRIDE_REMOTE_FEATURE_FLAGS=true` to `.js.env` only when each key is absent, while preserving explicit repository or Farmslot values such as `false`; read-only doctor reports both effective local settings.
119
+ - Extension `metamask.perps.start_state` now rejects `page: false` during planning because the action must enter Perps before controller convergence; teardown retains `false` as its skip-navigation option.
120
+ - `run --plan` now rejects `validate.workflow.startState`, which execution ignores, and directs authors to the supported recipe-root `startState` field.
121
+ - `check diff` now uses checkout-configured oxfmt when present, falling back to Prettier, so formatting follows each repository's declared tool.
122
+ - `check diff` no longer passes ESLint 9's `--no-warn-ignored` flag to repositories on ESLint 8, so changed-file lint runs instead of failing during CLI argument parsing.
123
+ - Extension fixture seeding now fails immediately with the visible MetaMask fatal-startup detail instead of waiting through unlock/setup polling when the product cannot initialize.
124
+ - Extension harness fixtures now initialize Perps on testnet; start-state enters the current `#/perps-home` surface before controller convergence, reads the persisted controller network without requiring an initialized Perps request channel, and proves a requested toggle with a bounded poll.
125
+ - Extension `ui.wait_for` now documents its fields and accepts `visible=true|false` as a concise alias for visible/absent assertions.
126
+ - `mm-harness call` now parses JSON-like numbers, booleans, arrays, and objects from its documented `key=value` syntax, so typed action fields do not arrive as invalid strings.
127
+ - Extension launch auto-healing now repairs missing or stale dependencies with the checkout's pinned Node version instead of teaching agents to guess a host tool manager.
128
+ - Extension `metamask.perps.ensure_positions state=open` now preserves the position-selection result across the CDP metadata wrapper instead of treating the wrapped result as an array.
129
+ - Checkout locks are reentrant for nested `mm-harness` subprocesses carrying the owning operation's token, so `launch --verify` cannot block its own runner smoke while unrelated processes remain excluded.
130
+ - `doctor --fix` now reports top-level `status: "fail"` and exits nonzero when any repair remains unresolved, so missing fixtures and other sandbox blockers cannot look ready to agents.
131
+ - Fixture sync now fails with an actionable initialization command when no usable source exists, and Core accepts the same canonical signing fixture consumed by its Perps controller actions.
132
+ - `launch --verify` now performs the selected quick or `--build` launch before verification instead of bypassing launch through the legacy live path; `--build --verify` no longer drops the build request.
133
+ - Extension `verify` and `live` infer the same checkout-scoped CDP port as `status`, `doctor`, and `launch --verify` via the shared `formatExtensionSlotPorts` ladder (runtime context → pool → slot-suffix formula), so standalone verify passes on healthy prepared slots without `--cdp-port`.
134
+
135
+ ## 0.14.7 - 2026-07-10
136
+
137
+ ### Changed
138
+ - `doctor --print-ready` now implies the exit-coded live probe — Farmslot `health_check` hooks need only `--print-ready`, not `--expect-live --print-ready`.
139
+ - Mobile `--print-ready` uses bridge `walletState` (unlocked + live bridge), not React Navigation route names; mobile `ready_indicator` is `OK`.
140
+ - Removed `metamask-recipe` install symlinks from adapter inject paths (no compat aliases).
141
+ - `doctor --print-ready` rejects `--json` (stdout is reserved for the Farmslot indicator line).
142
+
143
+ ### Fixed
144
+ - Mobile `surface.runtimeStatus` infers Android from `ADB_SERIAL` / `ANDROID_SERIAL` when present.
145
+
146
+ ## 0.14.6 - 2026-07-10
147
+
148
+ ### Added
149
+ - `mm-harness doctor --print-ready` — Farmslot `health_check` mode: prints `health.ready_indicator` on stdout (`extension`/`mobile`: `OK`; `core`: `ready`).
150
+ - `doctor --expect-live` — exit-coded liveness gate without indicator output (prepare recovery and other pass/fail callers).
151
+
152
+ ## 0.14.5 - 2026-07-10
153
+
154
+ ### Fixed
155
+ - Extension `ensure-browser` closes disposable New Tab / blank / extensions pages after reopen, matching reattach/ensure-ready hygiene.
156
+ - Extension slot browser titles are stamped through one shared helper (`adapters/extension/lib/slot-title.cjs`) with a persistent `MutationObserver`, and re-applied after `ensureExtensionReady` open/prune/reopen so relaunch and recipe paths keep `<slot-id> — MetaMask` instead of a bare MetaMask window.
157
+ - Extension slot-title contract test is hermetic (local CDP/WebSocket stub + fake DOM); Playwright and CDP stamps share one function source so they cannot drift.
158
+ - Extension inject copies `scripts/lib/slot-title.cjs` with the other installed helpers so reattach/readiness/reopen do not `MODULE_NOT_FOUND` on injected checkouts.
159
+
160
+ ## 0.14.4 - 2026-07-09
161
+
162
+ ### Fixed
163
+ - Include the new extension viewer cleanup scripts in the adapter manifest so published packages pass adapter-surface validation.
164
+
165
+ ## 0.14.3 - 2026-07-09
166
+
167
+ ### Fixed
168
+ - Extension `status` and `doctor` now accept a sidepanel-only live runtime, matching `run`/`call` health checks instead of requiring a fullscreen `home.html` tab after `launch --sidepanel`.
169
+ - Extension sidepanel launch waits longer for Chrome to expose the `sidepanel.html` CDP target, avoiding false failures after Chrome accepts the open request.
170
+ - Extension tmux viewers are opened only in the run-owned tmux session and are cleaned by `mm-harness stop`, preventing stale `webpack-*`/`console-*` windows from leaking across slots.
171
+ - Extension clean relaunch recognizes prior same-checkout Chrome instances loaded from both timestamped harness snapshots and `temp/recipe/runtime/runtime-dist`, while still refusing foreign CDP owners.
172
+
173
+ ## 0.14.2 - 2026-07-09
174
+
175
+ ### Fixed
176
+ - Require `@farmslot/recipe-harness` `^0.4.3` so runtime dependency readiness trusts install markers newer than an old recorded baseline and stops repeatedly prompting managed slots to reinstall fresh dependencies.
177
+
178
+ ## 0.14.1 - 2026-07-09
179
+
180
+ ### Added
181
+ - `mm-harness check diff` runs bounded repo-local validation for the active git diff, writing `validation-summary.json`/`.md` plus per-check logs. The fast profile runs changed-file ESLint, Prettier, and changed test files; `--profile full` also runs the repo typecheck when available. The default base follows the active PR base or repo remote HEAD before falling back to common branch names.
182
+ - `mm-harness run` writes a concise human report (`report.md`) and prints a colored step summary for every adapter, so successful Core/Mobile/Extension recipe runs are scannable without opening raw trace files.
183
+
184
+ ### Fixed
185
+ - Mobile slot targeting now refuses a platform launch/run/call when `--platform ios|android` conflicts with the configured default target, preventing an iOS-pinned slot from silently stealing a shared Android device; `status` and `doctor` scope device lists to the configured target by default and `status` reports other reachable bridges as additional targets that can be driven with `--device`.
186
+ - Mobile `status` bases `Next:` on the configured default target only, avoids assigning an additional Android bridge to a same-platform selected device, and reports stale Android reverse ports when the selected device is not connected to the slot Metro.
187
+ - Mobile `doctor` now reports the same additional reachable targets as `status`, so diagnostics and the home view agree on which devices can be driven with `--device`.
188
+ - Mobile `status` and `doctor` reuse one bridge probe for scoped and additional reachable devices, preserving the bounded probe window when the bridge is down.
189
+ - Mobile slot env resolution now preserves a configured Android Metro device name when an ADB serial is also present, instead of overwriting it with the serial.
190
+ - `mm-harness run <action-name>` now teaches `mm-harness call <action-name>` when the argument matches an action instead of a recipe, before requiring `--artifacts-dir`.
191
+ - Core Perps recipes now fail fast with a targeted `yarn install --immutable` teaching step when the target checkout is missing controller dependencies, including Perps actions reached through called library flows.
192
+ - Extension quick reattach refreshes the loaded runtime-dist in place and preserves an explicitly requested dapp/start URL instead of closing it during fullscreen cleanup.
193
+ - `mm-harness check diff` ignores its own artifact directory when that directory lives inside the target checkout, so repeated runs do not validate generated check artifacts.
194
+ - Mobile launch treats missing slot simulators or missing dev clients as provisioning failures, not Metro-healable failures, and prints the matching `mm-harness provision runway ios` recovery command without retrying the wrong layer.
195
+ - Mobile Runway provisioning recreates a deleted slot simulator by resolving the latest available iOS runtime and iPhone simulator type from `simctl`, so accidental simulator deletion is one-command repairable.
196
+ - Extension readiness validates local-only CDP websocket targets and sanitizes slot title data before stamping browser tab titles.
197
+
198
+ ## 0.14.0 - 2026-07-08
199
+
200
+ ### Added
201
+ - `mm-harness call` accepts action fields as direct `key=value` shorthand in addition to repeated `--arg k=v`, so commands like `mm-harness call ui.navigate page=perps` now match the examples agents naturally write.
202
+ - Packaged recipes are grouped by domain under `library/recipes/<domain>/` and still resolve by dotted names (`perps.smoke`, `runner.smoke`) with legacy aliases for the former flat names.
203
+ - Packaged `perps.smoke` recipes for mobile, extension, and core provide an explicit non-mutating Perps domain smoke. The existing bare `smoke` alias remains a runner/transport smoke for compatibility.
204
+
205
+ ### Fixed
206
+ - The top-level help SLOT banner now labels the slot dev-server port by adapter, so extension checkouts show `webpack :<port>` instead of the mobile-only `metro :<port>`.
207
+ - Hidden internal runtime commands remain executable through the installed `mm-harness` bin, fixing Extension verify warnings where `runtime-decision` returned `{}` because the wrapper blocked its own shipped helper.
208
+ - Extension launch/readiness now stamps the live home tab title with the Farmslot slot id after MetaMask has settled, so headed browser windows identify their owning slot.
209
+ - Extension quick launch now reattaches to a live slot browser when CDP belongs to this checkout's Chrome profile and dist is fresh, even if the webpack watcher is not currently listening; foreign CDP profiles still force the clean path.
210
+ - `status` now hydrates slot runtime context before device enumeration, so a Farmslot checkout pinned to `IOS_SIMULATOR=mm-1` reports only the selected slot simulator instead of every booted simulator/device on the machine. Live status also updates `Next:` to `mm-harness logs` when the selected bridge is reachable.
211
+ - The tmux Metro window opened by `launch ios|android` now streams the Metro output it writes to `metro.log` instead of opening a blank server window.
212
+ - `log-tui watch` now matches success/failure patterns only against log content emitted after the watcher starts, so stale markers already present in long-lived Metro/webpack logs cannot false-pass readiness checks.
213
+ - `start-metro` now reaps any existing mobile console-forwarder attached to the same Metro port before spawning this slot's forwarder, even when the old process was launched from another checkout or package install path.
214
+
215
+ ## 0.13.0 - 2026-07-08
216
+
217
+ ### Added
218
+ - Typed cdp-bridge failure codes (`NO_TARGET`, `CDP_TIMEOUT`, `WS_CLOSED`, `METRO_UNREACHABLE`): the bridge classifies a failure at its source and reports it three ways a caller can recover — an `ERROR[<CODE>]:` stderr marker, a code-specific exit status (10–13), and a `Next:` teaching line for the caller's actual situation. `bridge.mjs` attaches the code to the error it throws, and `app.hud` lifecycle-skip classification (`isMobileHudLifecycleSkip`) branches on the code first, keeping the substring needles only as a fallback for output from a bridge that predates the codes. The needle fallback is now case-insensitive, closing a gap where target-discovery's `Pinned Android device …` (capital A) missed a lowercase needle.
219
+ - Console-forwarder now expands object/array console arguments in `metro.log` from the Hermes-supplied inline `preview` (e.g. `{ symbol: "BTC", px: 42 }` instead of the literal `Object`), bounded one level deep and rendered synchronously so the stream never blocks; objects without a preview degrade to their description.
220
+ - `mm-harness call <action> --help` now renders the named action's own field schema (name, type, required, description, example — from the action manifest) above the generic call flags, instead of printing only the generic call help. Fuzzy short-name resolves like `call` (an ambiguous prefix shows every match); an unresolvable name falls back to the generic help plus a pointer to `mm-harness actions`. `run <recipe> --help` is unchanged (recipe-specific help is a separate, larger surface — recipes are not in the action manifest).
221
+
222
+ ### Fixed
223
+ - Runner construction no longer fails with `Manifest action app.lifecycle has no registered adapter` when a manifest declaring `app.lifecycle` is used with a non-mobile adapter (e.g. `call --adapter core --action-manifest library/manifests/mobile.action-manifest.json`): lifecycle adapter registration is manifest-driven instead of gated on the mobile adapter. Executing the action outside a mobile run still fails explicitly at target resolution.
224
+ - Mobile run teardown always clears any HUD step left painted on-device, so a failed run no longer strands a FAIL banner for the next run. Best-effort and bounded: a down/transitioning bridge simply has nothing to clear and never masks the run's real outcome.
225
+ - start-metro quick-launch now applies the dev Sentry DSN remap (`MM_SENTRY_DSN` defaults to `MM_SENTRY_DSN_DEV` when unset) at both launch paths. The quick-launch runs `expo start` directly and bypasses `scripts/build.sh`, which normally performs this remap, so without it Sentry never initialized in quick-launched dev clients.
226
+ - `launch ios|android` no longer claims `app + bridge ready` when the app is not actually up for the requested platform. On a dual-platform slot (one iOS simulator + one Android device sharing a Metro), a "ready" verdict could be reached from a shared successful bundle plus a cached OS process, and an answering bridge target belonging to the OTHER platform satisfied the claim — so `launch android` printed ✓ while nothing ran on the Android device. The quick-relaunch path now confirms a bridge target bound to the REQUESTED platform before claiming ready: the platform is passed explicitly to `wait-for-bridge` (`--platform ios|android`), so a slot-injected `IOS_SIMULATOR`/`ADB_SERIAL` for the other platform can no longer decide the match. The boundary-safe platform/device matcher is now a single shared module (`bridge-runtime/lib/match-bridge-target.cjs`) used by both `wait-for-bridge` and the confirm. If no matched target answers it launches the app for real, and if the platform-matched target still cannot be brought up it fails loudly. `wait-for-bridge` timeouts now name what was requested vs what answered.
227
+
228
+ ## 0.12.0 - 2026-07-08
229
+
230
+ ### Added
231
+ - Mobile console-forwarder: restores the device console (incl. DevLogger) in `metro.log` on RN 0.79+/Bridgeless via persistent CDP streaming (`Runtime.consoleAPICalled`) — one session per device JS-runtime page (shared target-discovery ranking, never the native C++ page), buffer replay + persisted last-seen cursor (timestamp + same-stamp texts) deduping across reconnects/app reloads, auto-started by `start-metro`; opt out with `METAMASK_RECIPE_CONSOLE_FORWARD=0`. Grep `[console:` for device lines. Zero-dependency transport (built-in Node 22+ WebSocket, like the CDP bridge client). Yields to interactive React Native DevTools: a session evicted by another debugger with no bridge lock present backs off for 5 minutes instead of evicting the human every discovery tick. Idles at the slow poll cadence while Metro is down; flushes pending lines and dedupe state synchronously on SIGTERM/SIGINT.
232
+
233
+ ### Fixed
234
+ - `app.hud` no longer aborts recipes on `CDP message timeout` / `WebSocket closed` during dev-client lifecycle transitions (0.11.0 regression that failed every warm-start run at `foreground-app`/`ensure-unlocked`). Skips are loud: the reason is written to stderr and recorded in the action result, so a masked real failure remains visible.
235
+ - cdp-bridge takes a runtime lock (`cdp-bridge.lock`) so the console-forwarder yields the single debugger slot during bridge commands — prevents mutual eviction and lost `Runtime.evaluate` replies. The forwarder honors the lock while the pid inside it is alive (`kill -0`), so long commands (wallet setup runs `CDP_TIMEOUT=120000`) are never evicted mid-flight while a crashed/killed bridge releases immediately; the bridge releases on SIGTERM/SIGINT, only unlinks a lock it still owns (last-writer-wins under overlap), and takes no lock for `--help`/unknown commands.
236
+ - `stop-metro` and mobile `cleanup` stop the console-forwarder (recorded pid + checkout-scoped sweep), so no orphan forwarder outlives Metro or fights a later one for the debugger slot.
237
+ - `open-debug` anchors its CDP-eval fallback to the app checkout so the bridge lock lands where the forwarder watches.
238
+
239
+ ## 0.11.0 - 2026-07-07
240
+
241
+ ### Added
242
+ - Mobile recipes can now use the standard outer `app.lifecycle` action for deterministic performance start states without rebuilding (Android background/foreground/terminate/restart, iOS simulator launch/terminate/restart through the shared Farmslot lifecycle adapter).
243
+ - `fixtures set --device <serial|udid|name>` now pins mobile fixture sync to the requested device using the same targeting rules as `run`, `call`, and `doctor`.
244
+ - New lifecycle-controlled measured flows in the packaged library: `app.lifecycle.android-smoke`, `perps.performance.warm-start`, `perps.performance.background-resume`, `perps.performance.cold-start`; performance recipes now keep lifecycle/unlock in `setup[]`/`startState` so measured nodes carry only CUF timings.
245
+
246
+ ### Changed
247
+ - `@farmslot/recipe-harness` dependency raised to `^0.4.0` (ships the `app.lifecycle` adapter — the `FARMSLOT_ROOT` local-source fallback is no longer needed at runtime).
248
+
249
+ ### Fixed
250
+ - `launch ios --device <udid|name>` again accepts a shutdown simulator target: launch preserves the requested simulator identity and lets `open-device.sh` boot it instead of requiring it to appear in the booted-device list first.
251
+ - iOS UDID pins are exported as both `SIM_UDID` and simulator name where available, so downstream simulator tooling can use the stable name while preserving the exact pin.
252
+ - `metamask.wallet.ensure_unlocked` now waits for the pinned agentic bridge target, tolerates transient status drops during the unlocked stability check, and avoids the old full password-unlock fallback for brief bridge gaps.
253
+ - Automatic `app.hud` updates are best-effort only while the mobile bridge target is down during app lifecycle transitions; ordinary HUD bridge failures now fail loudly.
254
+ - Mobile `cdp.target` required checks only pass when the responding target has `agenticPresent === true`, avoiding false positives from non-instrumented React Native targets.
255
+ - Mobile Metro startup is hardened around tmux/PID ownership: stale PID files and dead tmux windows are cleaned before reuse, and Metro survives the launching shell.
256
+ - `wait-for-bridge` now matches the selected Android target with the same boundary-safe device-name rules as device targeting and waits for the selected target, not just any target on the Metro port.
257
+
258
+ ## 0.10.0 - 2026-07-07
259
+
260
+ ### Added
261
+ - **Canonical `perps.performance` measured flow** (`library/recipes/perps/performance.mobile.recipe.json`): unlock → open the Perps market list → read live state → open a market detail — one node per user-visible step with stable node names, so the per-node `duration`s in `trace.json` are the timings you monitor. Run pinned: `mm-harness run perps.performance --device <serial> --heal off`. Device-proven end-to-end on a physical Pixel.
262
+ - **`run <name>` resolves personal/team recipe libraries.** Previously only the packaged library was probed by name; custom recipes ran by path. Sources resolve in library-precedence order (personal > team shadow the packaged canonical — a same-named personal recipe wins), path-shaped args never probe libraries, and a miss teaches which sources were searched. Zero-flag default: `$FARMSLOT_HOME/recipe-library`.
263
+ - **Custom-library walkthrough** ("Your own measured flow" in `docs/recipe-libraries.md`): a peer engineer scaffolds a personal library, copies the canonical flow, retargets the nodes to their journey, and runs it by name. Every step is executed by the `perps.performance-recipe` contract test, so the doc cannot drift from reality.
264
+ - **`call` declared on mobile and extension manifests** (was core-only) with self-discovery metadata — personal `flows/` segments are now usable from mobile/extension recipes. Honest limitation documented: flows carry no adapter dimension in the protocol yet, so a cross-adapter `call` fails at live-run rather than plan time.
265
+
266
+ ### Fixed
267
+ - **A `--device` pin wins target selection end-to-end on dual-platform slots.** Three independently sufficient holes let a pinned android run drive the iOS simulator (observed live): the discovery simulator filter ran before the android pin and the slot's ambient `IOS_SIMULATOR` captured the candidate set; pins were only enforced when more than one candidate existed (a single WRONG candidate was silently accepted); and the wallet actions' status-entry selectors checked the ambient simulator identity first while never matching serials against Metro device names. Fixed at every layer with live-repro contract cases; an unmatchable pin fails fast listing the Metro candidates.
268
+
269
+ ## 0.9.1 - 2026-07-07
270
+
271
+ ### Fixed
272
+ - **Metro survives the launching shell.** `start-metro.sh` spawned Metro without SIGHUP protection, so closing the launching tmux window/shell killed it, leaving a stale `metro.pid` and hanging every subsequent bridge/fixtures call. Metro now starts under `nohup` (argv spawn, no `bash -c` string interpolation), and a stale `metro.pid` naming a dead process is detected and cleaned before start.
273
+ - **`--device <adb serial>` reaches CDP target selection.** The runner's ambiguity gate resolved the serial, but target discovery compared it against Metro's `deviceName` ("Pixel 6 - 16 - API 36") — never a serial — so with an iOS simulator also attached, a recipe pinned to the physical Android device could silently drive the simulator. The serial is now mapped to the Metro identity via `adb -s <serial> shell getprop ro.product.model` with model-prefix matching (scoped to non-simulator targets); an unmatchable or ambiguous pin (two same-model devices) fails fast listing every Metro `/json/list` candidate instead of silently picking one. `--device <serial>` remains the only thing users pass.
274
+ - **Bridge commands always emit valid JSON.** `get-route` printed the literal string `undefined` when the route was transiently unavailable mid-navigation, so `bridgeCommand()` threw on parse and `waitForRoute()` aborted instead of polling. `get-route`/`navigate`/`go-back` now normalise a missing route to `null`, `bridgeCommand()` treats `''`/`undefined` stdout as not-settled-yet only for transient-legitimate commands, and `waitForRoute()` polls through `null` until timeout — the timeout error carries the expected route, last parsed route, last bridge reply and the device pin.
275
+ - **Wallet setup env propagation.** `bridgeEnv()` became async with the serial mapping; the wallet setup action now awaits it instead of spreading a Promise, which would have handed `setup-wallet.sh` an almost-empty environment.
276
+
277
+ ## 0.9.0 - 2026-07-07
278
+
279
+ ### Added
280
+ - **`status` auto-probes live app state per device** — the `yarn a:status` successor. Static info prints instantly; within a strict 2s window each device line is enriched with `screen=` (active route via the `__AGENTIC__` bridge, Route objects normalised), `wallet=locked|unlocked|onboarding`, `account=<label> (0x1234…abcd)` and `fixture=READY|missing`. `--fast` skips all probes (guaranteed-instant for scripts); `--json` carries the enriched `devices[]` in one envelope. All RN targets attached to the checkout's Metro are probed and matched per device (exact id → exact name → platform-uniqueness → leftover 1:1), so android + ios side by side on one Metro both report. Honest degradation: `no-bridge` (nothing matched/answering) vs `bridge-absent` (target attached but the installed build predates `__AGENTIC__`, rendered with a rebuild hint) — surfacing pre-bridge builds required the target discovery probe to become three-state (agentic/responsive/unreachable) instead of silently dropping them.
281
+ - **Every action now carries self-discovery metadata.** `action_metadata` (description + example recipe node, derived from the real handler implementations) covers all official and custom actions across the mobile, extension and core manifests — previously `app.status`, `cdp.target` and all 16 `metamask.wallet.*`/`metamask.perps.*` actions were bare names to agents. A new contract guard enforces coverage (including example structure: `node.action` must match), and locks mobile ↔ extension action-set parity with an explicit platform-only allowlist.
282
+
283
+ ## 0.8.0 - 2026-07-07
284
+
285
+ ### Added
286
+ - **`run <name>` — the recipe positional resolves packaged-library recipe names.** An existing file path (absolute or cwd-relative) wins; otherwise the arg is a library recipe name, adapter-scoped first (`run smoke` on mobile → `library/recipes/runner/smoke.mobile.recipe.json`), then adapterless (`run perps.lifecycle` → `library/recipes/perps/lifecycle.recipe.json`), then the exact library filename. A miss teaches `RECIPE_NOT_FOUND` naming the library recipes available for the adapter — no more `$LIB`-style path prefixes in docs or demo scripts. A directory shadowing a library name never wins, and path-shaped args (containing a separator) never fall through to the library.
287
+ - **`--device <udid|serial|name>` — first-class mobile device targeting on `run`, `call`, and `doctor`.** Resolves against connected devices (adb + booted simulators; exact id first, then exact name with an ambiguity teaching error) and sets the same env `launch --device` does. Mobile `run`/`call` **without** `--device` while more than one targetable device is connected (across android + ios; android state `device`, iOS `Booted`) fail fast listing the devices and the `--device` hint, so a recipe never lands on the wrong device silently. `run --plan`/`--list` are exempt; extension/core teach. Errors honor `--json` with structured envelopes.
288
+ - **`status` gains `devices[]`** (additive): `{platform, id, name, state, selected}` for connected android devices and booted iOS simulators on mobile checkouts — the `yarn a:status` successor; `selected` reflects the current env pinning.
289
+
290
+ ## 0.7.5 - 2026-07-07
291
+
292
+ ### Fixed
293
+ - **`launch --verify` converges duplicate extension home tabs instead of failing prepare forever.** A second bare `home.html` tab can appear beside the operator's slot-titled one outside the harness's control (the extension opens its own tab after the build-freeze reload), and the live verify health gate requires exactly one home tab — but nothing in launch/verify ever collapsed duplicates (`ensureExtensionReady` was only reachable via the retired `ensure-ready` verb), so every preflight retry recounted two tabs and failed. `launch --verify` (extension, with a CDP port) now runs the convergence before the live verify phase.
294
+ - **Convergence keeps the operator's tab.** With multiple home tabs, `ensureExtensionReady` now prunes the extras keeping the preferred tab — attached (another client is driving it) over custom slot title (`<slot-id> — MetaMask`, how operators tell which agent owns a browser) over first listed — instead of closing all and reopening fresh, which discarded the slot title. The deterministic close-all + fresh-open remains as a one-shot fallback when the kept tab fails the health check, and a failed re-listing during the fallback reports `cdp-unreachable` instead of a stale tab count.
295
+
296
+ ### Changed
297
+ - **`EnsureReadyAction` gains `pruned`** — extras were closed keeping the preferred tab; `reopened` now specifically means the pruned keeper failed the health check and one fresh tab was reopened.
298
+
299
+ ## 0.7.4 - 2026-07-07
300
+
301
+ ### Added
302
+ - **`fixtures generate`** — renders the extension wallet `fixture-state.json` from a wallet fixture by fronting `adapters/extension/wallet-fixture-state.cjs generate` (no reimplementation). Extension-only (mobile/core teach); requires `--fixture <wallet-fixture.json>` and `--out <fixture-state.json>`, with a `--json` machine envelope and teaching escapes on missing inputs or a non-extension adapter. This is the public surface farm packs call instead of reaching into the internal leaf, so the Extension pack's `setup/generate-fixture.cjs` resolver can be replaced by a one-line `mm-harness fixtures generate` hook.
303
+ - **`fixtures finalize`** — seeds account labels/selection into the **live** extension over CDP and validates import parity, by fronting `adapters/extension/wallet-fixture-state.cjs seed-cdp` (no reimplementation). Extension-only, post-launch (mobile/core teach); requires `--fixture`, `--state`, `--cdp-port`, `--extension-dir` (optional `--extension-id-file` / `--out`), with a pure `--json` envelope (leaf progress on stderr), the shared `LEAF_MISSING` pre-check, and teaching escapes on missing flags or a non-extension adapter. This is the public surface for the Extension pack's `setup/preflight.sh` account-label finalization, retiring its last `--resolve-script` reach-in into the internal leaf.
304
+
305
+ ### Fixed
306
+ - **`fixtures generate` teaches on a missing leaf instead of leaking a node error.** When the `wallet-fixture-state.cjs` leaf is absent (a corrupt packed install), `generate` now pre-checks the leaf on disk and fails with a one-sentence teaching error plus a `Next:` reinstall command (and a clean `--json` `LEAF_MISSING` envelope), instead of surfacing node's opaque "Cannot find module".
307
+ - **Extension readiness no longer spawns a duplicate home tab on a transient CDP hiccup.** `ensureExtensionReady` treated any `/json/list` failure as an empty tab set, so a list that transiently failed mid-prepare (unlock → home rerender) looked like "no home tab" and opened a second one next to the healthy tab — the following health check then found two targets and failed prepare. `/json/list` now distinguishes "CDP answered: N targets" from "CDP unreachable" (retried a few times), returns `reasonCode: 'cdp-unreachable'` without touching any tab when the state is unknown, confirms a zero count with a second listing before opening, and converges over a few passes so a wrong action is corrected within the call instead of tripping the health check.
308
+ - **`readiness` no longer opens a duplicate home tab when the existing one is attached.** `findPageTarget` required `webSocketDebuggerUrl`, but Chrome omits that field for any target another client is attached to — during prepare the fixture finalizer / smoke recipe / CDP evals attach to the healthy home tab, so it listed without a wsUrl, looked absent, and `readiness` opened a second `home.html` that then tripped the exactly-one-home health gate. Page existence is now decided by URL in any attach state; the websocket only gates UI inspection (a present-but-attached tab is reported `pagePresent: true`, `pageInspected: false` and left alone instead of duplicated). URL matching also tolerates the `home.html#/` router rewrite and query suffixes. New additive report fields: `pagePresent`, `pageInspected`.
309
+
310
+ ### Changed
311
+ - **`install --adapter core` is documented and contract-locked as the public core-runner install surface for farm packs.** It already fronts `adapters/core/inject.sh` and produces the runner delegate at `<harness>/core/runner/bin/mm-harness`; a new CLI-level contract test (`tests/contract/core-install-cli.test.sh`) pins the happy path, `--json` envelope purity, and the adapter-detection teaching escape. Packs call `mm-harness install --adapter core --target <repo>` (inheriting `RECIPE_HARNESS_ROOT` / `FARMSLOT_ROOT`) instead of cloning a runner checkout and invoking the internal leaf directly — no separate top-level command is minted.
312
+ - **`EnsureReadyResult` gains a machine-readable `action` field** (`none` | `opened` | `reopened` | `skipped`) stating the tab decision the call took, so a consumer distinguishes "opened one because a zero was confirmed" from "touched nothing because CDP state was unknown" straight from the JSON envelope.
313
+
314
+ ## 0.7.3
315
+
316
+ ### Fixed
317
+ - **`provision runway` gates its install-skip on artifact identity.** The baseline now records the installed artifact identity (run id, branch, digest) at install time and a skip preserves it; provisioning skips ONLY when the recorded identity matches the requested (or probe-resolved) run — a different or unknown installed app is reinstalled from the cache instead of being silently kept. Previously any installed MetaMask.app suppressed the install, so a stale binary could be served against a newer JS bundle.
318
+ - **Provision decisions are machine-readable**: the --json envelope carries `reason` (`identity-match` | `identity-mismatch` | `unknown-identity` | `fresh-install` | `forced`) and a skip envelope includes the matched artifact identity.
319
+
320
+ ## 0.7.2
321
+
322
+ ### Added
323
+ - **`recipe-quality build`** — the single worker surface for producing `recipe-quality.json`: fronts @farmslot/agent-runtime's canonical builder (no reimplementation), validates against @farmslot/protocol `RecipeQualityArtifact` before writing (invalid input writes nothing, exit 5 with a teaching escape naming the invalid field). New production dependency: `@farmslot/agent-runtime`.
324
+
325
+ ### Changed
326
+ - `@farmslot/protocol` dependency raised to `^0.7.3` so one protocol version serves both the harness and the builder.
327
+ - The packed-install contract test now executes `recipe-quality build` from the published layout, guarding the new dependency's packaging path.
328
+
329
+ ## 0.7.1
330
+
331
+ ### Added
332
+ - **`doctor --expect-live`** — exit-coded runtime liveness for hooks and scripts: exit 0 only when the adapter runtime decision is `ready` (extension: watcher + CDP; mobile: metro + bridge; core: deps), non-zero with a teaching escape otherwise. Fails closed on every degraded path; reuses the doctor runtime probe, no duplicated checks.
333
+
334
+ ### Fixed
335
+ - **Per-command `--help` documents every implemented flag** (`call/run --list`, `--library`, `--action-manifest`, `logs --window`, `actions --action`, `launch --url`, `doctor --runtime-dir`, `stop --adapter`) and tab-completion matches — the CLI self-teaches its own surface. A contract test guards help/implementation parity.
336
+ - **Injected runner shims resolve at run time** instead of a path baked at inject time: `MM_HARNESS_BIN` → global `mm-harness` on PATH → the recorded install-time path as last resort → teaching escape. A slot copied to or shared with another machine no longer points at a missing absolute path.
337
+ - Removed two parsed-but-unused `launch` flags (`--yes`, `--json-stream`).
338
+
339
+ ## 0.7.0
340
+
341
+ The "version people use": every remaining identified fix/improvement batched on the
342
+ 0.6.x stability foundation. Built on top of the extension quick-reuse regression fix
343
+ (0.6.3, below). One themed commit per item; full contract suite green (macOS).
344
+
345
+ ### Added
346
+ - **`call --list` / `run --list`** — one scope-aware listing of everything invocable
347
+ for the detected adapter: the fuzzy short-name action universe (each action's full
348
+ name plus its unambiguous short name — what `call` accepts) and the runnable
349
+ library flows (what `run <flow>` accepts). No `<action>`/`<recipe>` required; same
350
+ shape under `--json`.
351
+ - **`logs --window`** — (re)open the read-only tmux tail window for the resolved
352
+ dev-server port without touching the process (recovers a window closed while
353
+ Metro/webpack keeps running); teaches when there is no run-owned session or log.
354
+ - **`doctor` surfaces Screen-Recording / capture readiness** (macOS) — shells
355
+ `capture-helper doctor` and reports pass/WARN with the grant step; capture is
356
+ optional (absent binary → no row, never a doctor failure). Seam: `CAPTURE_HELPER_PATH`.
357
+
358
+ ### Changed
359
+ - **`fixtures set` is never silent** — an upfront intent line the moment work begins,
360
+ plus the mobile setup-wallet leaf's own progress streamed live (heartbeat) during
361
+ the 20-40s wallet build; stdout stays a clean `--json` envelope.
362
+ - **`actions --action <name>` fuzzy-resolves like `call`** (exact full name → exact
363
+ final segment → substring) and teaches the vocabulary on an unknown name (exit 2,
364
+ `--json` error envelope) instead of throwing.
365
+ - **`run` auto-detects the adapter** from the target/cwd when `--adapter` is absent
366
+ (parity with call/doctor); teaches when the repo type cannot be detected.
367
+ - **Log-tail windows follow an explicitly owned session ladder** —
368
+ `RECIPE_TMUX_SESSION` → the checkout's `agentic-runtime.json` `session`, with no
369
+ current-session or slot-number inference (the orchestrator names the session; the
370
+ harness only populates windows). Stale `metro-<port>` windows whose port has no
371
+ live listener are swept only inside that resolved session on Metro start.
372
+ - **Extension slot ports fill independently** — the resolver no longer early-returns
373
+ once CDP is known, so a context that pins only `cdpPort` still gets `WATCHER_PORT`
374
+ from the pool/formula.
375
+ - Orchestrator-neutral protocol-root env: `METAMASK_RUNNER_PROTOCOL_ROOT` is primary,
376
+ `FARMSLOT_ROOT` a back-compat alias for one release.
377
+
378
+ ### Fixed
379
+ - **Extension `launch` teaches `yarn install --immutable` on missing/stale deps** —
380
+ a pre-watcher deps-freshness probe fails fast (teaching only, never auto-installs)
381
+ instead of letting webpack crash deep on stale `node_modules`; gated so a reusable
382
+ live runtime (reload-in-place) is never blocked.
383
+ - **Core actions run under the target checkout's own tsx** — a core perps action
384
+ dynamically imports the checkout's controller TypeScript, so it now runs under tsx
385
+ (resolved from the target first — the published harness ships none; a thin-installed
386
+ slot has it) rather than bare node throwing `ERR_MODULE_NOT_FOUND`; teaches
387
+ `yarn install` / `yarn build` when no tsx is found.
388
+ - **Repo-pinned Ruby for iOS pod/gem work** — `yarn setup` / the iOS native build
389
+ activate the checkout's `.ruby-version` (asdf → rbenv → mise → PATH) so a cold
390
+ checkout does not build native gems against the system Ruby; teaches the install
391
+ when the pinned Ruby is unresolved.
392
+ - **iOS deep-link dialog pre-approval at provisioning** — the dev-client scheme
393
+ approval is persisted right after app install, so the first headless `openurl` does
394
+ not pause on the "Open in <App>?" dialog (the launch-time write can race).
395
+ - **Extension quick-tier webpack tail window** is regression-guarded (Metro parity).
396
+
397
+ ### Internal
398
+ - Dependabot: no high/critical advisories remain (existing `resolutions` cover them).
399
+ - Boring-library swaps (pidtree, ajv) declined per the "delete hand-rolled code with a
400
+ bug history" bar: the ps/lsof process detection is contract-tested and working, and
401
+ manifest validation is already schema-driven via `@farmslot/protocol`.
402
+ - Orchestrator-neutral port leaf: `resolve-farmslot-ports{,-core}.mjs` / `.sh` and
403
+ `src/adapters/resolve-farmslot-ports.ts` renamed to `resolve-slot-ports*`; the
404
+ function `resolveFarmslotPortsByRepo`, the bash function / cli key
405
+ `resolve_farmslot_ports_by_repo`, and the JS export renamed to the `*slot*`
406
+ equivalents. The old filenames, bash function, cli key, and JS export are kept as
407
+ delegating shims/aliases for ONE RELEASE (deprecation window) — the next release
408
+ removes them. The pool-path constants the leaf reads are left as-is (orchestrator
409
+ data location, not a callable surface).
410
+
411
+ ## 0.6.3
412
+
413
+ ### Fixed
414
+ - **Extension `launch` reuses a healthy live runtime instead of always clean-building** — quick `launch` / `launch --sidepanel` on an extension slot now probes for a reusable runtime (webpack watcher LISTENing on `WATCHER_PORT` + CDP `/json/version` reachable on `CDP_PORT` + dist not stale). When all hold it REATTACHES: re-snapshots `dist/chrome` into the loaded runtime-dist and reloads the extension in place over CDP (service worker + pages), re-asserts the side panel, and applies `--url` — against the existing slot Chrome profile, with no watcher stop, webpack cache clear, or Chrome kill/relaunch. Reuse is best-effort: a failed probe (watcher down, CDP unreachable, stale dist) falls through to the clean path, and `--build` is the explicit escape that always clean-builds + relaunches a fresh runtime (no probe) to force-recover a broken slot.
415
+
416
+ ## 0.6.2 - 2026-07-06
417
+
418
+ ### Added
419
+ - **`-v` / `--version`** prints the mm-harness version.
420
+
421
+ ### Fixed
422
+ - **`logs` now streams live and shows the file path** — it captured output via spawnSync (never shown for a `tail -F` that doesn't exit), so `logs`/`logs --full` looked dead. New spawnInherit hands the terminal to the follow; the resolved log path + `tail -f`/`less +F` hints print upfront so you can page it yourself. Honors the test override seam.
423
+ - **`debug` resolves the checkout's own port** (slot context → pool → formula) like launch/stop/doctor — bare `mm-harness debug` no longer 'fetch failed' against the wrong port.
424
+
425
+ ## 0.6.1 - 2026-07-06
426
+
427
+ Fresh-install fixes found live on a published-0.6.0 install.
428
+
429
+ ### Fixed
430
+ - **Mobile `launch` no longer dies on empty `METAMASK_BUILD_TYPE`** — the quick-launch runs `expo start` directly (bypassing scripts/build.sh, which passes the build type as an arg); the fixture `.js.env` shipped `METAMASK_BUILD_TYPE=""`, which Metro's transform rejects (`Invalid METAMASK_BUILD_TYPE`). start-metro now defaults to the main dev client when unset, so already-installed slots launch without re-syncing fixtures.
431
+ - **`doctor` no longer false-flags orphan Metros** — the detector matched any argv containing "metro" (caught the `tail -F metro.log` viewer) and reported the live, valid bundler as leaked. Now it matches only `expo start` bundlers and excludes the one serving the current port.
432
+
433
+ ### Internal
434
+ - Reap-checkout-metros test hardened against a SIGTERM timing flake (polls for exit).
435
+
436
+ ## 0.6.0 - 2026-07-06
437
+
438
+ Minor release: CLI hardening (decomposed, single-parser, dist-only) plus a
439
+ fresh-slot validation wave — every fix proven live on untouched -2 slots and
440
+ independently cross-reviewed.
441
+
442
+ ### Changed
443
+ - **CLI decomposition + dist-only publish** — `src/cli.ts` is parse+dispatch only;
444
+ handlers under `src/commands/<verb>.ts`; one commander parser; the npm tarball
445
+ ships compiled `dist/` without `src/` and `tsx` is a devDependency (plain-node,
446
+ smaller, faster). Source checkouts keep the tsx dev path (`MM_HARNESS_BIN`).
447
+ - `verify` auto-ensures its own overlay (no refusal on a fresh checkout); every
448
+ engine execution resolves the slot's ports/device first (call/run/fixtures get
449
+ the same slot isolation launch has).
450
+
451
+ ### Added
452
+ - **`stop` reaps leaked Metro bundlers by checkout** (port-agnostic, path-
453
+ boundary matched), `doctor` surfaces any orphan and teaches the reap.
454
+ - **`launch --url <dapp>`** (extension) opens the dapp in the main tab beside the
455
+ MetaMask sidepanel; `--stop-only` profile release before seed; stable per-checkout
456
+ Chrome profile + runtime-dist; post-open tab cleanup.
457
+ - DX: run-mode + stale-`dist` warning in `bin`; overlay-delegation notice; docs/CODE-MAP.md.
458
+
459
+ ### Fixed
460
+ - `launch` targets the slot's own simulator (never the `booted` alias) and boots it;
461
+ slot-port resolution layers rungs so a partial rung never nulls the port; provision
462
+ resolves the simulator from the pool and boots before install.
463
+ - `call` works from published installs (dist bridge, no type-strip crash);
464
+ `ensure_unlocked` fails teaching `fixtures set` on an un-onboarded wallet instead
465
+ of vacuously passing (mobile + extension); `provision --json` usage errors emit the
466
+ standard envelope; deps-not-ready teaches a command that works on a bare checkout.
467
+
468
+ ## 0.5.1 - 2026-07-05
469
+
470
+ Fresh-install hotfixes found in live validation of 0.5.0.
471
+
472
+ ### Fixed
473
+ - **First launch on a provisioned slot targets the slot's own simulator** — the mobile resolution ladder now reads the `runway-provision.json` baseline (simulator name/udid, watcher port, slot id) when `agentic-runtime.json` doesn't exist yet, and boots that simulator. Previously a provisioned-but-unprepared slot degraded to the simctl `booted` alias, missed the installed dev client, and guessed the wrong Metro port.
474
+ - **`call` works from published installs** — library actions load harness helpers through a dist-preferring bridge instead of importing `src/index.ts` (node refuses to type-strip `.ts` under `node_modules`). The bridge is deliberately narrow (paths + run-recording) to avoid an import cycle through `adapters.ts`.
475
+ - **deps-not-ready guidance matches the caller's state** — on a checkout without node_modules the taught command now leads with `yarn install --immutable` (yarn cannot run scripts at all on a bare checkout), and always ends with the launch re-run.
476
+
477
+ ## Unreleased
478
+
479
+ ### Fixed
480
+ - **`provision --json` adapter-detection failures emit the standard error envelope** — usage errors now match `doctor`/`launch` (envelope on stdout with `error.userAction`; stderr stays human-only). Previously `--json` callers got empty stdout.
481
+
482
+ ### Changed
483
+ - **CLI decomposition** — `src/cli.ts` is parse+dispatch only; handlers live under `src/commands/<verb>.ts`; `provision` routes through `src/commands/provision.ts` (not overlay harness dispatch); launch composition splits under `src/commands/launch/`; commander owns per-command `--help` (no hand-rolled interception).
484
+ - **Published tarball is dist-only** — `files` no longer ships `src/`; `tsx` is a devDependency; `bin/mm-harness` and overlay leaves resolve `dist/*.js` (teaching error if a published install lacks `dist/`). Library actions keep importing `library/actions/harness-exports.mjs` (dist-preferring narrow bridge from 0.5.1); package exports remain for external consumers (`dist/index.js` published; `src/index.ts` when `--conditions=development` and dist is absent).
485
+ - **resolve-farmslot-ports is TypeScript-first** — pool/context/formula port resolution lives in self-contained `adapters/shared/resolve-farmslot-ports-core.mjs` (works from runner and injected overlay copies); `src/adapters/resolve-farmslot-ports.ts` re-exports it for `slot-ports.ts`. `resolve-farmslot-ports.mjs` is the node leaf; `resolve-farmslot-ports.sh` remains a thin bash-compat wrapper for sourced callers.
486
+
487
+ ## 0.5.0 - 2026-07-05
488
+ Minor release: thin Runway provisioning (harness-owned slot provisioning with a shared artifact cache) plus compiled-dist packaging.
489
+ ### Added
490
+ - **`mm-harness provision runway`** — provisions a mobile slot with exactly: resolve the latest Runway (expo-dev-build) artifact, download it ONCE into a shared machine cache (`~/.cache/metamask-harness/runway/<runId>/`, sha256+size integrity, corrupt entries re-downloaded, never a slot failure), create the simulator if missing, install the dev client on it. Zero yarn/deps/Metro — those stay dispatch-time (lazy, presence-authoritative readiness unchanged). `install --runway` is the overlay-path equivalent (covered by its own contract test). Baseline recorded so doctor reports provisioned-but-deps-pending as a normal state.
491
+ - **Compiled `dist/` for published installs** — the npm tarball ships esbuild-compiled JS; `npm i -g` installs run the dist entry (~0.21s cold vs ~0.37s tsx). Source checkouts without `dist/` keep the tsx dev path; `bin/mm-harness` prefers dist when present. Packaging contract test (pack → global install → dist entry runs).
492
+ ### Changed
493
+ - Runway provisioning callers (farm installer, mobile-farm `runway` prepare profile) delegate to the harness; the profile no longer runs a deps phase and a contract test enforces that.
494
+
495
+ ## 0.4.0 - 2026-07-04
496
+
497
+ Minor release: the adapter-surface architecture plus the day's feature wave (0.3.6-0.3.9 shipped fixes as they were validated live; this cut marks the surface additions honestly).
498
+
499
+ ### Added
500
+ - **AdapterSurface** — one per-platform surface behind a common interface (`src/adapters/surface.ts`: resolveSlotPorts, runtimeStatus, devServer, logSources, hints); doctor/launch/stop/logs/debug/fixtures are surface-backed, so no verb hand-rolls platform branching. Contract: `docs/ADAPTER-SURFACE.md` (never-branch rule, add-a-platform checklist, migration table).
501
+ - **Platform-adaptive `stop` on every adapter** — extension: kills this checkout's webpack watcher (pid file + orphan scan, slot-scoped) and closes its tail window; mobile: port-scoped Metro; core: headless teaching error. Idempotent, outcome-stating summaries.
502
+ - **Doctor reports live runtime state on every adapter** — mobile Metro, extension webpack (via the extension readiness decision), core deps-only — one uniform shape, adapter-scoped colored context rows.
503
+ - **`logs` is multi-source per adapter** — sources and the `--source` default come from the adapter's logSources (extension: webpack/watcher/rebuild/app).
504
+ - **Context-aware examples and tier-visible flows** — `call` usage/help examples use the detected adapter's real manifest actions; `flows` prints a precedence legend decoding each source into personal > team > canonical.
505
+
506
+ ### Changed
507
+ - **BREAKING (agent contract)**: `doctor --json` `runtime.metro` (string) is now `runtime.devServer` `{label, status}` and is absent for core.
508
+
509
+ ### Feature wave recap (0.3.6-0.3.9)
510
+ - `mm-harness stop`; live-streamed mobile AND extension launches with upfront intent lines; presence-authoritative readiness (no setup-on-every-launch) with the dev-client running probe; slot-context-first device/port resolution; UTF-8 locale for pod spawns; DEV OVERRIDE banner + SLOT line + colored help; `mmdev-harness` completions; hermetic tmux-stubbed contract suite (now gating CI) + ESLint in `npm run check`; `docs/UX-PRINCIPLES.md` interaction contract.
511
+
512
+ ## 0.3.9 - 2026-07-04
513
+
514
+ ### Fixed
515
+ - **Readiness is presence-authoritative** — a no-baseline mtime-only stale verdict no longer re-runs `yarn setup` inside every launch (warning instead; fingerprint-verified drift still installs), the deps baseline is recorded after any successful setup, and the `ready` decision probes that the dev client is actually running on the target device before doing nothing (`launch` launches).
516
+ - **Slot-context-first resolution** — device/ports resolve from the checkout's `agentic-runtime.json` before pool/formula fallbacks, for mobile (simulator, Metro port) and extension (CDP + watcher ports, overwriting stale inherited env); naming formulas no longer hardcode pool prefixes.
517
+ - **UTF-8 locale for pod-triggering spawns** — CocoaPods no longer dies on multibyte podspecs under locale-less orchestrator environments.
518
+ - **Hermetic contract tests** — tmux/xcrun/adb are stubbed; the Metro log-window opens only in a run-owned session, never an unrelated attached one.
519
+
520
+ ### Added
521
+ - **`mm-harness stop`** — stop the port-scoped Metro this checkout owns and close its log-tail window; idempotent; teaching error on extension/core.
522
+ - **Extension launch streams live** with an upfront intent line (ports named); mobile and extension long operations are never silent.
523
+ - **Doctor reports live runtime state per platform** (deps currency, Metro on the slot's resolved port, dev-client presence) with adapter-scoped, colored runtime-context rows.
524
+ - **Colored grouped help with DEV OVERRIDE banner and SLOT line**; `mmdev-harness` completions; outcome-stating launch summaries with a `[dev]` marker.
525
+ - **docs/UX-PRINCIPLES.md** — the CLI interaction contract (human surface rules; `--json` envelope purity is exempt and inviolable).
526
+
527
+ ### Fixed
528
+ - **Mobile preflight no longer re-runs `yarn setup` on every launch** — the deps readiness decision escalated an mtime hint to `stale` and ran the FULL `yarn setup` (submodules + inpage bridge + jetify + pods) inside the launch phase on every invocation in orchestrated (farmslot) contexts. `depsCheck` reports `stale` for a no-baseline checkout whenever a manifest is newer than the install markers; the orchestrator's git phase refreshes tracked-file mtimes on every sync while its deps phase leaves `node_modules` untouched, so "manifest newer than markers" is normal and not proof of drift — yet it duplicated the orchestrator's deps work and turned a fast preflight into 15+ minutes (the behavior that also looked like a hang before 0.3.8's streaming). **Presence is now authoritative:** without a recorded baseline an mtime-only `stale` is trusted as `current` (with a one-line stderr warning), so a churned-but-present checkout launches with zero `yarn` in both tiers. Genuine drift is still caught by the recorded-baseline fingerprint. `mm-harness launch` (quick tier / no `--build`) also dropped `preflightMode` before deciding, so the fast contract never took effect; it is threaded through now.
529
+ - **Fast tier never installs; genuine gaps teach the orchestrator** — in the fast tier (quick launch / orchestrated), deps that genuinely need work (absent markers, an absent required package, a fingerprint drift, or Metro unable to resolve a module) return a teaching block naming the orchestrator deps/prepare phase (plus the standalone `yarn setup:expo` / `launch --build` remedies) instead of an implicit setup — deps are the orchestrator's contract. The standalone/full tier still installs.
530
+ - **Deps baseline recorded the instant a setup leaf succeeds** — `prepareMobile` records the deps fingerprint baseline immediately after any successful `yarn-setup`, unconditionally (previously only a later `record: true` re-decide did, gated on the original decision being `install` and on `RECIPE_UP_INSTALL_ATTEMPTED`). Genuine-drift detection is fingerprint-based and self-sustaining after one good install.
531
+ - **CocoaPods no longer crashes on non-UTF-8 podspecs under a locale-less env** — pod-triggering spawns (`yarn setup` / native `yarn start:*`) now pin a UTF-8 `LANG`/`LC_ALL` when the caller has none. A gateway/tmux parent env without a locale made Ruby parse podspecs as US-ASCII and die on the first multibyte byte (`Invalid react-native-mmkv.podspec: "\xE2" on US-ASCII`).
532
+ - **`launch` no longer reports a dead app as ready** — the mobile readiness decision treated a healthy Metro + successful bundle as `ready` without checking the target device, so a dev client that had crashed or was never opened produced `✓ ready` while nothing was running. The decision now probes the device (`xcrun simctl spawn … launchctl list` / `adb shell ps -A`, fail-closed) and returns `launch`/`app-not-running` (relaunch the client) unless the app is actually alive.
533
+ - **Metro log-tail window no longer leaks into an unrelated tmux session** — running `start-metro` without an explicit session previously fell back to whichever tmux client was current or last attached. The viewer now opens a window only from `RECIPE_TMUX_SESSION` or the checkout runtime context and otherwise skips it; Metro still runs detached to the log. Extracted to `adapters/mobile/lib/tmux-viewer.sh` so the rule is unit-tested; contract tests stub tmux hermetically so no suite run can create a real window.
534
+
535
+ ### Added
536
+ - **Adapter surface — one seam for per-platform behavior** — `src/adapters/surface.ts` defines `AdapterSurface` + a `getAdapterSurface(adapter)` registry that owns the behavior commands used to hand-roll with `if (adapter === …)` ladders (slot-port resolution, read-only runtime status, dev-server describe/stop, log sources, and platform-phrased Next: hints), with thin `mobile`/`extension`/`core` implementations that delegate to the existing readiness/port plumbing (re-homed to `src/adapters/slot-ports.ts`). `doctor`, `launch`, `stop`, `logs`, `debug`, and `fixtures` now resolve platform behavior through the surface instead of branching. This closes the platform-blind class of bug (a platform handled in one command but forgotten in another). Documented in `docs/ADAPTER-SURFACE.md`; enforces `docs/UX-PRINCIPLES.md` principle 1.
537
+ - **`doctor` reports a runtime section for every adapter** — the live runtime readiness line (decision + reasons, deps currency, dev server) now renders for extension (webpack watcher via `decideExtensionReadiness`) and core (deps-presence-only), not just mobile; the render is one uniform shape sourced from the surface.
538
+ - **`mm-harness stop` is adaptive per platform** — mobile stops its port-scoped Metro and closes the log-tail window; extension stops the checkout's webpack watcher (pid file + orphan ps-scan, slot-scoped) and closes its tail window; core teaches the headless path. All idempotent (nothing-to-stop is success), with a uniform summary and `--json` envelope.
539
+ - **`logs` resolves dev-server log sources through the surface** — extension now recognizes the webpack, watcher, and rebuild logs (ordered), not only `webpack.log`; each platform's "nothing running" teaches its own launch hint. `--source` default and valid set come from the adapter's own log sources (extension defaults to `webpack`, not `metro`), and core teaches with platform-neutral wording (no Metro vocabulary). `--source <label>` now prefers that dev-server log.
540
+ - **`call` with no action gives a context-aware example** — the usage error shows the DETECTED adapter and a REAL action from its manifest (a wallet action where one exists, else the universal `command`) plus the scoped `mm-harness actions --adapter <detected>` discovery hint. The static `call --help` example now uses real actions (`ensure_unlocked` / `command`) and points at `mm-harness actions`, instead of a hardcoded `call unlock --adapter extension` naming a non-existent `metamask.wallet.unlock`.
541
+ - **`flows` surfaces the resolution tier per source** — `flows` prints a precedence legend mapping each resolved library source to its tier (`personal` / `team` / `canonical`), derived from the actual resolution, so the engine's `source=<name>` per flow is decodable and LOCAL libraries read as first-class; the tier is also named in the recipe-library-unavailable teaching error.
542
+ - **`stop --help` matches the adaptive behavior** — the help text now describes mobile Metro / extension webpack watcher / core headless, instead of the stale "Metro dev server … extension/core: teaching error".
543
+ - **Slot-context device/port detection** — `resolve-farmslot-ports.sh` reads `agentic-runtime.json` (the mapping the orchestrator's prepare wrote into the checkout) first, so device/port resolution survives pool renames that break any naming formula.
544
+ - **CLI help surface** — grouped/colored `mm-harness` help, a `DEV OVERRIDE ACTIVE` banner when `MM_HARNESS_BIN` is set, and a `SLOT` line when the checkout is a prepared slot; `mmdev-harness` completion alias.
545
+
546
+ ### Changed
547
+ - **`doctor --json` runtime dev-server shape is normalized** — the mobile-only `runtime.metro` string is replaced by a platform-labelled `runtime.devServer: { label, status }` (`metro` for mobile, `webpack` for extension; absent for headless core), so agents read one shape across platforms.
548
+ - **`yarn check` runs ESLint** — flat-config ESLint (typescript-eslint recommended on `src/**/*.ts`, `@eslint/js` recommended on the `.mjs`/`.cjs` library) runs after typecheck and comment-hygiene. Dev-only (not in the published `files`); the baseline is zero-warning by config scoping.
549
+ - **Deps install uses a scoped `setup:expo` variant when native isn't wanted** — `adapters/mobile/yarn-setup.sh` gains `--mode full|expo`. `expo` runs `yarn setup:expo --no-build-ios --no-build-android` (JS deps + prebuild, no native — native builds come from `open-device.sh`, never doubled); the full `yarn setup` runs only for a native build (`launch --build`). This is also the standalone Metro-only remedy taught by the fast-tier deps block.
550
+ - **`mm-harness runtime-decision --adapter mobile` accepts `--preflight-mode`** — surfaces the fast (presence, blocks on a genuine gap) vs full (installs) decision for inspection and callers.
551
+
552
+ ## 0.3.8 - 2026-07-04
553
+
554
+ ### Fixed
555
+ - **Mobile launch streams progress in `--json` mode** — mobile prepare leaves now run through `spawnScriptStreaming`, which tees the child's stdout+stderr to the parent's stderr live while stdout stays reserved for the JSON envelope. A multi-minute `mm-harness launch --json` is no longer silent (previously `spawnSync` buffered leaf output and suppressed it entirely under `--json`). Contract test asserts leaf progress reaches stderr, the envelope stays clean, and the streaming conversion preserves the exit-code contract.
556
+
557
+ ## 0.3.7 - 2026-07-04
558
+
559
+ ### Fixed
560
+ - **Overlay install manifest no longer advertises the retired `launch.sh`/`live.sh`** — mobile launch/live orchestration is in-process now (`mm-harness launch`), and `install_v1_runner_assets` deletes `scripts/launch.sh` and `scripts/live.sh` on both install paths. The product-owned/metadata-only manifest already advertised `verify` only, but the overlay manifest's `runtimeHelpers` still listed `launch: scripts/launch.sh` and `live: scripts/live.sh` — paths the same install had just removed, a manifest that pointed at nonexistent files. The overlay manifest now advertises `verify` only, matching the product-owned path. A new manifest-honesty contract check in `tests/contract/mobile-inject.test.sh` asserts every advertised `runtimeHelpers` path exists on disk and that `launch`/`live` are not advertised, for both the product-owned and a full overlay install, so this cannot recur in either mode.
561
+
562
+ ## 0.3.6 - 2026-07-04
563
+
564
+ ### Fixed
565
+ - **Install manifest carries target checkout identity (`targetRevision`)** — `adapters/mobile/inject.sh` stamps the target repo's `HEAD` (resolved once, up front, via `git rev-parse --verify HEAD`) and an explicit `installMode` into the mobile install manifest on both install paths (product-owned/metadata-only and overlay). Consumers (the farm recipe hook) can now gate their fast path on manifest identity matching the current checkout instead of manifest existence, closing the stale-manifest hole where a gitignored manifest survives a branch switch to a bridge-less checkout. Install now fails with teaching guidance when the target `HEAD` cannot be resolved (not-a-repo, unborn branch), instead of stamping an unusable `targetRevision`. Contract tests cover the identity stamp on both paths and the unborn-HEAD refusal.
566
+
567
+ ## 0.3.5 - 2026-07-04
568
+
569
+ ### Fixed
570
+ - **IMP-24b: shell leaves run even when packed without the exec bit** — `yarn pack` (used by `yarn npm publish`) strips the executable bit off shipped `*.sh` leaves to `644`, even though git stores `100755` and `npm pack` preserves `755`. The 0.3.4 fix (git `100755` + an `npm pack` tarball test) was therefore insufficient: the published `yarn`-packed tarball still shipped `644` leaves and a fresh `npm i -g` hit `EACCES` on the first leaf. `spawnScript` now invokes shell leaves through `bash <leaf>` instead of exec'ing them directly, so the leaf's file mode is irrelevant (node leaves were already interpreter-invoked via `process.execPath`). Env overlays (`FORCE_COLOR`/`RECIPE_RUNTIME_DIR`), arg passing, cwd, JSON envelope, and exit codes are unchanged. New contract test `tests/contract/leaf-644-runs.test.sh` forces the device leaves to mode `644` and drives them through the real `mm-harness` spawn path, asserting they still run — the regression the `npm pack` tarball test could not catch.
571
+
572
+ ### Changed
573
+ - **Publish with `npm publish`, not `yarn npm publish`** — `npm publish` preserves the git `100755` mode on shipped `*.sh` leaves (defense-in-depth on top of the interpreter invocation above). Documented in `AGENTS.md`; git exec bits remain `100755`.
574
+
575
+ ## 0.3.4 - 2026-07-04
576
+
577
+ ### Fixed
578
+ - **IMP-24: published tarball stripped exec bits from shell leaves** — the harness execs `adapters/**/*.sh` leaves directly, but six shipped leaves (`adapters/mobile/lib/metro-listener.sh`, `adapters/shared/activate-repo-node.sh`, `cli-ux.sh`, `harness-path.sh`, `hash-helpers.sh`, `resolve-farmslot-ports.sh`) were committed without the git exec bit (mode `100644`), so npm packed them `644`. A fresh `npm i -g` then hit `EACCES` on the first leaf. All shipped `*.sh` are now stored `100755` in git, so the published tarball packs them executable. Added contract test `tests/contract/packaging-exec-bits.test.sh` — it runs `npm pack` to produce the actual `.tgz`, then asserts via `tar tzvf` that every shipped `*.sh` entry has owner-execute set; fails with the offending path(s) if any is `644`, so this cannot silently regress.
579
+ - **IMP-24: spawn failures are surfaced, not swallowed** — `spawnScript` returned on `result.error` (a leaf that is missing/`ENOENT` or not executable/`EACCES`) before its human-mode stderr forward, so a leaf that could not start produced a silent exit-1 (surfaced upstream as `MOBILE_PREPARE_FAILED`). The `result.error` branch now always writes a teaching diagnostic (`leaf could not start: <leaf> (<errno>)` + a `Next: reinstall mm-harness` hint) to stderr before returning. Covered by `tests/contract/spawn-error-surfaced.test.sh`.
580
+
581
+ ## 0.3.3 - 2026-07-04
582
+
583
+ ### Fixed
584
+ - **IMP-23: per-run mobile runtime dir (`RECIPE_RUNTIME_DIR`)** — every on-disk mobile runtime file (`metro.log`, `metro.pid`, `metro.tmux`, `bridge-status.log`, `wallet-fixture.json`) now resolves from the shared runtime-dir resolver that honors `RECIPE_RUNTIME_DIR` instead of the hard-coded `<target>/temp/recipe/runtime`. `RECIPE_RUNTIME_DIR` must be a non-empty relative path under the target checkout — absolute values are rejected at validation. Shell (`recipe_runtime_dir` in `harness-path.sh`, used by `start-metro.sh`, `wait-for-bridge.sh`, `prewarm-bundle.sh`, and `bridge-runtime/setup-wallet.sh`) and TS (`recipeRuntimePath` in `runtime-decision.ts`) resolve the same location, so a run pointed at an isolated subdir (e.g. `temp/recipe/runtime-8081`) writes and reads nothing under the default. Two harness jobs sharing one checkout each set `RECIPE_RUNTIME_DIR` to a distinct relative subdir for full runtime isolation.
585
+ - **IMP-23: port-scoped Metro guard** — the Metro detect/kill/restart helpers are extracted into `adapters/mobile/lib/metro-listener.sh` with an explicit invariant: discovery, inspection, and signalling key off the managed watcher-port only (`lsof -iTCP:<port> -sTCP:LISTEN`). A Metro listening on any other port is never selected, never has its cmdline read, and is never signalled, so a run managing one port cannot detect or kill a concurrent Metro on another port in the same checkout.
586
+
587
+ ## 0.3.2 - 2026-07-04
588
+
589
+ ### Fixed
590
+ - **IMP-21: FORCE_COLOR-safe pod install** — the mobile pod-triggering leaves (`yarn-setup.sh` running `yarn setup`, and `open-device.sh` running the native `yarn start:ios|android` build) now run with `FORCE_COLOR=0` / `NO_COLOR=1`. VisionCamera's podspec probes `node --print require.resolve('react-native-worklets-core')` and treats any output other than the exact string `undefined` as "found"; an inherited `FORCE_COLOR` made node emit a colorized `undefined`, enabling FrameProcessors and hard-failing on the missing worklets pod. Setup/native builds are now reliable from a `FORCE_COLOR` shell.
591
+ - **IMP-22: `--watcher-port` beats `.js.env WATCHER_PORT`** — `verify.sh` port resolution now honors an explicit `WATCHER_PORT` in the process env (which carries `mm-harness launch --watcher-port N`) over the target's `.js.env`. Precedence is flag > process env > `.js.env` > `8081` default, so a run can be pointed at an alternate Metro/CDP port without editing a shared slot's `.js.env`.
592
+
593
+ ## 0.3.1 - 2026-07-03
594
+
595
+ ### Fixed
596
+ - **Customization compose-by-ref**: recipes with `{action:"call",ref:"<lib>.<flow>"}` + `--library <lib>=<dir>` now pass pre-run schema validation AND execute at runtime. `validateRecipeAdapterAware` threads `externalFlowIds` into both `validateRecipeDocument` and `validateRecipeWithManifest`; library resolution also runs at `run` time.
597
+ - **IMP-14: stale metro.log guard** — if `metro.log` is older than the install markers (`node_modules/.yarn-state.yml`, `.yarn/install-state.gz`), it is treated as absent so a stale log cannot drive a false native-module-mismatch decision at launch.
598
+ - **IMP-15: stale overlay shim auto-repair (two modes)** — `ensureOverlay` detects (1) a `.runner-source` pointing to a nonexistent runner dir, and (2) a `.runner-source` whose dir exists but whose `bin/mm-harness` was removed. Both re-install automatically before launch.
599
+ - **IMP-17: deps-staleness fallback (two-layer)** — (1) mtime comparison when no baseline exists: manifest files newer than install markers → `install`; (2) required-dep absence: a top-level `package.json` dep absent from `node_modules` is caught at pre-flight as `deps-partial` → `install`, before Metro discovers it during a long bundle run.
600
+ - **IMP-18: iOS sim scheme-approval pre-write** — `open-device.sh` writes `com.apple.launchservices.schemeapproval` via `xcrun simctl spawn … defaults write` before the first `openurl`, eliminating the "Open in \<App\>?" dialog hang. Scheme derives from `DEV_CLIENT_SCHEME` (overridable via `IOS_DEV_CLIENT_SCHEME`).
601
+
602
+ ## 0.3.0 - 2026-07-03
603
+
604
+ ### Changed
605
+ - Porcelain layer eliminated: the legacy `mm-recipe`/`mme-recipe`/`metamask-recipe` bins are gone; launch/logs/debug/fixtures compose the adapter scripts directly in TypeScript. Repository reorganized into `src/` (all TS), `adapters/` (per-platform runtime glue), `library/` (recipe/action/flow vocabulary).
606
+
607
+ ### Fixed
608
+ - Extension `launch` opens the app directly (composes `live.sh`) instead of a retired command.
609
+ - Mobile `cleanup` resilient to a stale runner-source path.
610
+ - Core run teaches the correct next step when no account is set.
611
+ - Resolved CodeQL alerts (insecure temp dir, file-access-to-http).
612
+
613
+ ## 0.2.0 - 2026-07-03
614
+
615
+ ### Added
616
+
617
+ - **`mm-harness update`**: checks the npm registry `dist-tags.latest` for `@deeeed/metamask-harness` and, when newer, runs `npm i -g @deeeed/metamask-harness@latest` and prints `old → new`; already-current exits 0. `--check` reports only (exit 0 up-to-date, exit 1 update available); `--json` carries `{ current, latest, updateAvailable }`. Every failure path (no npm, registry unreachable, permission-denied global dir) exits 3 with the exact escape.
618
+ - **Passive update nudge**: on invocation (throttled to once/day via a cache file, bounded fetch, skipped in CI and when `MM_HARNESS_NO_UPDATE_CHECK=1`), a single `stderr` line announces a newer published version — never on stdout, never blocking, never auto-updating.
619
+ - **`MM_HARNESS_BIN` dev/prod switch**: the entrypoint honors `MM_HARNESS_BIN` as the single override point — when set, the invocation is handed to that dev checkout's `bin/mm-harness`; unset resolves the installed/global bin (prod). A self-path guard prevents an exec loop, and a non-executable value teaches its fix. Documented in `--help` and README.
620
+
621
+ ### Changed
622
+
623
+ - **Teaching errors are type-enforced**: `usageOut` now requires a `userAction` escape argument, so a teaching error without a reachable next step no longer compiles. The repo-type detection failure teaches BOTH escapes (`cd into a checkout / pass --target <path>` and `--adapter <mobile|extension|core>`) consistently across `doctor`, `launch`, `verify`, and the overlay path.
624
+ - **Docs realigned to the shipped single-bin surface**: `README.md`, `docs/harness-cli.md` (reduced to a pointer at `CLI-SPEC.md`), `docs/recipe-libraries.md`, `docs/extension-runtime-commands.md`, `docs/package-boundaries.md`, and `docs/live-adapter-contract.md` no longer teach the retired `metamask-recipe`/`mm-recipe`/`mme-recipe` names or "planned/stub" daily-loop claims — every command example is `mm-harness`. (`docs/MENTAL-MODEL.md` and `docs/CLI-SPEC.md` retain the old names by design: they are the before→after migration contract.)
625
+ - **Published package trimmed to runtime needs**: the `files` field drops dev-only helpers (`scripts/check.mjs`, `scripts/quality/`, `scripts/link-local-farmslot.mjs`, `scripts/validate-action-e2e-artifacts.mjs`) and repo-dev docs (`AGENTS.md`, `CLAUDE.md`), keeping `bin`, `runner`, `orchestration`, `library`, the runtime completion scripts, `docs`, `README.md`, and `CHANGELOG.md`. The `files` field now also negates `scripts/README.md` (dev-only) so the published tarball no longer ships it alongside the two runtime completion scripts.
626
+ - **Comment-hygiene guard extended**: `scripts/quality/comment-hygiene.mjs` now also fails on presentation vocabulary (`verb`/`daily`) in comments and object-property keys (previously only filenames and declarations), enforced with word boundaries so `verbatim`/`verbose` and user-facing help titles like `DAILY LOOP` stay exempt. Source comments and the `verb: 'rebuild'` progress field were reworded/renamed to `command`.
627
+ - **Single-bin public surface (wave 4)**: `mm-harness` is now the only npm bin. The three legacy bin files (`mm-recipe`, `mme-recipe`, `metamask-recipe`) are internalized to `orchestration/porcelain/` — they remain fully functional as implementation details (exec'd by `mm-harness launch/logs/debug/fixtures` and the inject-script delegates) but are no longer installed into `$PATH` as separate executables. Package renamed from `@deeeed/metamask-recipe-runner` to `@deeeed/metamask-harness` (effective on next publish). The `harness` top-level subcommand form is removed from the CLI; overlay verbs (`install`/`verify`/`cleanup`) are addressed directly (`mm-harness install`, `mm-harness verify`, `mm-harness cleanup`). Shell completions updated to register `mm-harness`. The `completions/_recipe` zsh completion file is removed (dynamic `mm-harness` completions shipped in wave 5).
628
+
629
+ ### Fixed
630
+
631
+ - **`flows --json --target <dir>` no longer errors**: the bare-`flows` default-to-`list` check inspected every token, so a flag value (the `--target` argument) was mistaken for a subcommand and the args were mangled (`unknown option '--json'`). It now inspects only the first positional token against the engine's real subcommand set (`list`/`promote`) and drops the runner-level `--target` (flows resolution is adapter-global), so `flows`, `flows --json`, `flows --json --target <dir>`, and `flows promote --help` all behave.
632
+ - **`run --json` / `call --json` emit parseable JSON on stdout**: the engine's library-resolution log (`Recipe libraries: …`, `logger.info` → stdout) polluted stdout ahead of the JSON payload. In `--json` mode the engine logger is now routed to stderr, so stdout is machine-only for both success and failure runs.
633
+ - **`run --json` (non-plan) now honors the validates-first contract and the mm-harness envelope**: (1) it pre-validates adapter-aware through the SAME static path `run --plan` uses, so an invalid recipe exits 5 with a structured `RECIPE_VALIDATION_FAILED` error on parseable stdout (was exit 1 with the engine's validation text mixed into output) — and, because pre-validation reads no libraries, the `Recipe libraries: …` log never reaches stdout on the validation-failure path; (2) success now emits the envelope (`schemaVersion`, `command`, `adapter`, `status`, `exitCode`, `recovered[]`, `mutations[]`) with the engine result nested under `result`, matching `launch`/`verify`/`doctor` (was the raw engine result for `core` / a flattened result for device adapters). The recipe-running bound (exit 4) still fires before the recipe is read.
634
+ - **`flows` help no longer advertises `--adapter`**: flow resolution is adapter-global, and the engine rejects `--adapter` on `flows` (`unknown option`). `flows --help` dropped the flag from its accepted-options list rather than presenting a flag that errors.
635
+ - **`doctor --fix` is implemented**: the parser now knows `--fix` is boolean (parses with `--json`/`--adapter` in any order). `--fix` runs the shared self-healing steps WITHOUT launching the app — auto-ensures the runtime overlay if missing (same bounds; never a fixture reseed) — and reports `fixed[]` / `failed[]` (`--json` adds both arrays). Exit 0 = all healed, 1 = partial/failed. Read-only `doctor` is unchanged.
636
+ - **`mm-harness` help text matches shipped behavior**: `doctor`'s summary and `doctor --help` now list `--fix` (with a one-line description + `mm-harness doctor --fix --json` example) instead of saying "`--fix` coming"; `flows`'s summary and `flows --help` now describe the current multi-source resolution (`personal > team > canonical`, highest tier wins, `--library <name=path>`) instead of the stale "requires @farmslot/recipe-harness >= 0.3.3 — pending publish" note.
637
+ - **App-logic failures surface the original output verbatim**: `launch`, `run`, and `call` now carry the original failure text in `error.originalError` (`--json`) and in human output; the classification note accompanies it, never replaces it. When a recovery bound fires, `error.code` names the exact bound (`APP_LOGIC_FAILURE`, `WALLET_STATE_REQUIRED`, `RECIPE_RUNNING`, `SAME_RECOVERY_TWICE`) instead of a generic `LAUNCH_FAILED`.
638
+ - **`launch --sidepanel` selects the display mode**: it composes `mme-recipe sidepanel open` after the base rebuild (grounded in `orchestration/extension/sidepanel-toggle.sh`), so `--sidepanel` and `--fullscreen` compose different porcelain calls and the mode reaches orchestration instead of being metadata-only.
639
+ - **`run`/`call` route failures through the shared heal bounds**: app-logic failures surface verbatim with no healing (exit 1); infra failures get one bounded recovery re-run, then the same-recovery-twice bound refuses a loop (exit 4).
640
+ - **`call <short-name>` resolves a name that uniquely matches a substring of an action's final segment**: the flagship `mm-harness call unlock` example returned `ACTION_UNKNOWN` because fuzzy resolution only matched exact trailing segments — `unlock` never matched `metamask.wallet.ensure_unlocked`. Resolution is now tiered (exact full name → exact final segment → unique substring of a final segment); an exact match always beats a substring, a unique substring resolves, and an ambiguous fragment still exits 2 listing candidates.
641
+ - **Extension launch no longer dies before the browser on a fresh checkout**: `orchestration/porcelain/mme-recipe` `rebuild` truncated `<runtime-dir>/rebuild.log` without creating the directory, so a checkout without the runtime dir failed with "No such file or directory" under `set -euo pipefail`. The log write now `mkdir -p`s its parent, matching the other log writers in the file.
642
+ - **Mobile launch no longer crashes or loops on a bare checkout**: `mm-recipe ios|android` on a checkout with no slot suffix left `WATCHER_PORT` unbound, crashing at Metro startup under `set -u`; `init_context` now defaults it to Metro's well-known `8081` (farm dispatch still sets its slot-derived port). Launching without installed dependencies now fails fast with a teaching error ("dependencies not installed — run yarn install / yarn setup first") instead of a Metro-restart retry loop, and the launch path honors a failed Metro start rather than hanging in bundle prewarm.
643
+ - **Standalone extension launch cannot attach to or kill a browser it did not launch (SAFETY)**: `orchestration/extension/launch-browser.cjs` previously terminated whatever process held the requested `--cdp-port` — including a personal Chrome — and could target a shared/default profile. It now (a) refuses a CDP port unless every listener provably loads our `--user-data-dir` (else a teaching error, never a kill/attach), and (b) refuses a shared (`~/.chrome-farmslot`) or OS-default browser profile, so standalone launches stay on a per-checkout isolated user-data-dir. Farm dispatch's runtime-context profile/port injection is unaffected.
644
+
645
+ ### Changed
646
+
647
+ - **CLI commands organized by domain**: the `launch`/`logs`/`debug`/`fixtures` handlers moved to `runner/src/commands/*.ts` and the shared healing/bounds policy to `runner/src/heal-bounds.ts` (was `runner/src/daily-verbs.ts`). Added a `Code quality rules` section to `AGENTS.md` and a `scripts/quality/comment-hygiene.mjs` guard (wired into `yarn check`) that fails on development-history comments and presentation-vocabulary identifiers in `runner/src`.
648
+
649
+ ### Added
650
+
651
+ - **`doctor --json` reports the runtime-context section**: `doctor` now emits a `runtimeContext` object (and a readable section in human mode) so the slot's runtime context is inspectable without hunting for files. It reads `agentic-runtime.json` (`RECIPE_RUNTIME_CONTEXT` override, else `<target>/temp/recipe/runtime/agentic-runtime.json`) via the harness's own reader — no duplicate parser — and lists every field (`slotId`, `extensionId`, `cdpPort`, `runtimeStart.approved`/`.command`/`.readyUrl`) with its current `value`, `source` (`env` override > `file` > `default`), the `envVar` that overrides it, and a one-line `customize` hint. When the file is absent the section is still present with `fileExists:false` and the path where it WOULD live (written by farmslot prepare/dispatch), and every field falls back to `env`/`default`. Grounded in `runner/src/doctor.ts` `runtimeContextSummary()`, contract-tested in `tests/contract/mm-harness-final-review.test.sh`.
652
+ - **Bundled dynamic shell completions — SHIPPED (refactor wave 5)**: `mm-harness` ships zsh + bash tab-completion in the npm package (`scripts/completions.sh`), installed via `mm-harness completions install` (delegates to `scripts/install-completions.sh`, which prints the `source` line to add to `~/.zshrc` / `~/.bashrc`; the farm installer companion can auto-source it later — not touched here). Completion is **dynamic**: commands, the `launch` positional (`ios|android`), `fixtures` (`sync|set`), and per-command flags are static from the surface, while **action names for `call`** come live from `mm-harness completion-candidates actions` and **flow refs for `run`** from `mm-harness completion-candidates flows` (both cache-backed per-checkout, silent static fallback on miss — never blocks the shell). The rewritten `scripts/completions.sh` targets `mm-harness` (not the retired `recipe`/`mm-recipe`/`mme-recipe` names); the legacy `completions/_recipe` was already removed in wave 4a.
653
+ - **`completion-candidates flows` — REAL (refactor wave 5)**: now enumerates the resolved recipe-library flows via `loadRecipeLibraries` on `@farmslot/recipe-harness >= 0.3.3` (was degraded to empty in wave 2). Flow-ref candidates are adapter-global, so they resolve even outside a checkout / without `--adapter`.
654
+ - **Env-gap flags — SHIPPED (refactor wave 5, CLI-SPEC Part 4)**: every remaining env var a human was told to type now has a flag with resolution order **flag > env > config/default** (the flag sets the env var the porcelain reads, so it wins; an absent flag leaves the agent/CI env untouched). `launch --device` (`IOS_SIMULATOR` for ios · `ADB_SERIAL`/`ANDROID_SERIAL`/`ANDROID_DEVICE` for android), `launch --cdp-port` (`CDP_PORT`/`RECIPE_CDP_PORT`), `launch --watcher-port` (`WATCHER_PORT`/`METRO_PORT`/`RECIPE_WATCHER_PORT`) — ports are numeric (teaching exit 2 otherwise); `logs --events` (`RECIPE_LOG_EVENTS`); `fixtures set --fixture` now resolves flag > `RECIPE_WALLET_FIXTURE` > canonical default on both adapters. Each is documented in `--help`. Precedence is contract-tested.
655
+ - **Contract coverage (wave 5)**: `tests/contract/mm-harness-wave5.test.sh` proves the completion scripts are syntactically valid (`bash -n` / `zsh -n`), target `mm-harness` + source the dynamic candidates, that `completion-candidates` return real data (the `call unl<TAB>` demo: candidates output contains `unlock`; flow refs contain `perps.clean_market_testnet`), and the env-gap flag precedence (flag beats env) for `launch --device`/`--cdp-port`/`--watcher-port`, `logs --events`, and `fixtures set --fixture`.
656
+ - **DAILY LOOP verbs — REAL (refactor wave 3)**: `launch`, `logs`, `debug`, and `fixtures sync|set` are no longer teaching stubs — they are real commands (`runner/src/commands/`). The typed layer owns POLICY, HEALING, TEACHING, and the `--json` contract; it does NOT rewrite orchestration — each verb COMPOSES the same porcelain paths `mm-recipe` / `mme-recipe` use today (grounded in their case arms), and `launch --verify` composes the overlay `live` sequence via `handleHarness`. Composition seams `MM_HARNESS_MM_RECIPE_BIN` / `MM_HARNESS_MME_RECIPE_BIN` / `MM_HARNESS_INSTALL_BIN` make every path contract-testable without a device.
657
+ - **`launch [ios|android]`** — flagship. Mobile: target MANDATORY (missing → teaching exit 2); extension: no target (`--fullscreen` default, `--sidepanel`); core: teaching error (headless). Tiers: default quick relaunch (`mm-recipe <ios|android>` / `mme-recipe rebuild`), `--build` (`mm-recipe up` / `mme-recipe build`), `--watch` (extension webpack watcher), `--verify` (absorbs the retired `live` — overlay ensure → launch → CDP/bridge poll → smoke verify). `--runway` is mobile-only (teaching error elsewhere). Quick-launch **change detection**: native inputs changed vs the recorded build baseline (git diff on `ios/`,`android/`,`package.json`,`yarn.lock` / webpack config) → refuse quick with a teaching error pointing at `--build`.
658
+ - **`logs`** — composes the porcelain `logs` (+ `--full`, `--source metro|app`); teaching exit 2 when nothing is running (no `metro.log`/`webpack.log`), pointing at `launch`.
659
+ - **`debug`** — extension `debug` (`--worker` → `debug worker`), mobile `debug` (`--dev-menu` → `dev-menu`); cross-platform flag misuse teaches (exit 2).
660
+ - **`fixtures sync|set`** — wallet DATA only. `sync` composes the porcelain `sync`; `set` composes `mm-recipe setup-wallet <fixture>` (mobile) or reuses `call`'s one-node engine path `metamask.wallet.setup` (extension GAP); the password is read FROM the fixture, and `set` always prints the fixture edit path. `--fixture <path>` agent override.
661
+ - **`--heal=off|infra-only|auto` (refactor wave 3)**: implemented on `launch` (default `auto`) and `run`/`call` (default `infra-only`). Auto-ensures the runtime overlay when missing (inline install, first-install notice on stderr in human mode / `mutations[]` in `--json`; `--heal=off` disables auto-install). `launch` runtime healing recovers infra transport failures (Metro/CDP) by re-running the composed launch **once** — bounds are ALWAYS enforced regardless of `--heal`: refuse when a recipe is running (`recipe.lock`/`MM_HARNESS_RECIPE_RUNNING`), when the same recovery already failed once this invocation, and — the WALLET RULE — healing NEVER touches fixtures: a recovery that would need a seeded wallet stops with `recoverable:false` + `userAction: "run mm-harness fixtures set"`. `--json` gains `recovered[]` (stable codes: `metro.restarted`/`chrome.reopened`…), `mutations[]`, `phase`, and `recoverable`/`attemptedRecoveries[]` on failure.
662
+ - **Exit-code taxonomy — complete (wave 3)**: `3` (infra failure not auto-healed — `--heal=off` or a bound hit) and `4` (bounded / not-retryable — same recovery failed, recipe running, wallet write needed) now land on `launch` alongside `0`/`1`/`2`/`5`, per CLI-SPEC Part 5 §5.6.
663
+ - **Contract coverage (wave 3)**: `tests/contract/mm-harness-wave3.test.sh` proves launch grammar (mobile-target mandatory, core refusal, `--runway` on extension → exit 2), tier selection incl. change-detection refusal, heal bounds (same-recovery-twice, wallet-rule with `recoverable:false`, recipe-running, `--heal=off` → exit 3), auto-ensure notice + `mutations[]`, and logs/debug/fixtures happy + teaching paths — all via the stub-recorder seams. `tests/contract/mm-harness-taxonomy.test.sh` updated: the DAILY LOOP verbs are now REAL, not planned stubs.
664
+ - **`mm-harness call <action>` — REAL (refactor wave 2)**: `call` executes ONE action from the adapter vocabulary AS a single-node recipe through the **real engine path** — the same `runRecipe` path `run` uses, so it writes the same `summary.json` / `trace.json` / `artifact-manifest.json` evidence (one execution path, two doors: `call` = one node, `run` = a graph). Fuzzy short-name resolution (`unlock` → `metamask.wallet.unlock`; ambiguous → exit 2 listing candidates; unknown → exit 2 pointing at `mm-harness actions`), repeatable `--arg k=v` mapped to the action's node fields, adapter auto-detect from `--target`/cwd, and a `--json` shape (`{ command, adapter, action, resolvedAction, args, status, summaryPath, tracePath, artifactManifestPath, exitCode }`). Always-validates its one-node recipe adapter-aware first (exit 5 on validation failure). `handleCall` in `runner/src/cli.ts`; promoted from teaching stub to a real DISCOVER verb in `mm-harness --help`.
665
+ - **`mm-harness run --plan` — REAL (refactor wave 2)**: plan-only mode validates the recipe adapter-aware (`validateRecipeDocument` schema + `validateRecipeWithManifest` action existence/platform vs the adapter manifest) and prints the plan **without touching any device or writing artifacts**. `--json` = `{ status, adapter, recipe, findings[], plan[] }`; each `plan[]` item carries `confidence: static|conditional` (static = schema/action-existence/manifest/fixture-FILE/resolved-path checks; conditional = runtime intent like "would auto-ensure the overlay / launch the app / execute N nodes"). Exit 0 = plan valid · 5 = validation failure · 2 = recipe missing/unparseable. Shared helper `validateRecipeAdapterAware` (reused by `call`). Grounded on the pinned `@farmslot/protocol`; recipe library `call.ref` external-flow-id resolution + the `run` execute path's always-validate extend when `@farmslot/recipe-harness >= 0.3.3` publishes (tagged in code + `docs/CLI-SPEC.md`).
666
+ - **Exit-code taxonomy (started, wave 2)**: `run` / `run --plan` / `call` follow the documented taxonomy (CLI-SPEC Part 5 §5.6): `0` ok · `1` recipe/runtime fail · `2` usage/bad args · `5` validation failure (`3`/`4` infra/bounded land with the wave-3 healing path).
667
+ - **Dynamic completions cache skeleton (wave 2)**: `runner/src/completions-cache.ts` defines the per-checkout cache file (`{runtimeDir}/.completion-cache.json`: `version`, `updatedAt`, `candidates`), a short TTL, and invalidation on `mm-harness install`. A hidden `mm-harness completion-candidates <actions|flows>` command (sourced by the wave-5 shell completion scripts) returns cache-backed manifest action names for `actions`; `flows` degrades to empty until `@farmslot/recipe-harness >= 0.3.3` exposes the recipe-library API. New contract coverage in `tests/contract/mm-harness-wave2.test.sh`.
668
+ - **`mm-harness` single front-door CLI (refactor wave 1)**: `bin/mm-harness` is now a real bin (same node/tsx resolution as `bin/metamask-recipe`, registered in `package.json`) backed by a **commander**-based entry (`runner/src/mm-harness-cli.ts`) — the same CLI library `@farmslot/recipe-harness` uses. It presents only the validated end-state surface with grouped `--help` (DAILY LOOP / DISCOVER / PROVE / RUNTIME OVERLAY), one purpose line + one copy-pasteable example per verb. Real verbs route to the existing dispatch in `runner/src/cli.ts` (no logic forked): `actions` (+ new `--raw` flag that dumps the raw action manifest, replacing the retired `manifest` verb), `flows` (bare = list; `flows promote`), `run`, `doctor`, and top-level `install`/`verify`/`cleanup` (byte-identical to `metamask-recipe harness <verb>`, reusing `runner/src/harness.ts`). Planned verbs (`launch`, `logs`, `debug`, `fixtures sync|set`) were honest teaching stubs at this point (promoted to real in wave 3); `call <action>` was a stub (promoted to real in wave 2). Retired names (`manifest`, `live`, `runtime-*`, `self-test`) are not on this surface; they stay reachable via the untouched legacy bins until the caller-sweep wave. `cli.ts` gains an exported, guarded `main` so the commander front reuses it in-process. New contract coverage in `tests/contract/mm-harness-taxonomy.test.sh` (grouped help, stub teaching, overlay dispatch parity, `actions --raw`, `flows` routing parity). Full contract: `docs/MENTAL-MODEL.md` + `docs/CLI-SPEC.md`.
669
+ - **`metamask-recipe harness <install|verify|cleanup|live>` command**: the recipe-harness lifecycle now lives in the runner as typed, tested TypeScript (`runner/src/harness.ts`) instead of the skill's ~670 lines of bash. It resolves the runner root (the runner itself), auto-detects the adapter from `--target` when `--platform`/`--adapter` is omitted, and dispatches to the correct orchestration file per subcommand/platform with identical behavior to the skill: `install`→`orchestration/<platform>/inject.{sh,mjs}`, `cleanup`→`inject`'s cleanup counterpart (preferring an installed overlay's `.runner-source`), `verify`/`live` for mobile/extension→installed `scripts/<action>.sh` else `orchestration|runner|scripts/<platform>/<action>.sh`, and `core verify`→the installed core delegate's `doctor`. Preserves the extension runtime-context env hydration (`agentic-runtime.json` → `--cdp-port`/`--prepare-cmd` and `RECIPE_*` exports) and forwards remaining args verbatim (use `--` for passthrough). Human-friendly by default: `--help` teaches every verb with a copy-pasteable example, the platform is auto-detected from the checkout (flags are overrides only, so bare `metamask-recipe harness verify` works inside a repo), progress and `✓`/`✗` status print to stderr while the orchestration script's stdout/artifacts stay byte-identical to the skill path, failures print the cause plus the exact next command, and `--json` emits a single machine-readable summary for agents. Lets the recipe-harness skill thin to a caller. New contract test `tests/contract/harness-cli.test.sh`.
670
+ - **Canonical recipe library**: `library/library.json` + `library/flows/perps.flows.json` seed the runner's repo-native flow source with `perps.clean_market_testnet` (wallet unlock + Perps start-state convergence as one `call`-able flow). `metamask-recipe run` resolves flows from ordered library sources — developer `--library` entries or `RECIPE_LIBRARY_PATH`, then the personal library at `<farmslot home>/recipe-library`, then this runner's `library/` last, so personal/team flows win by default and shadowing is reported in run output and `summary.json`. New `metamask-recipe flows list|promote` delegates to the harness flows CLI with the same source chain. Requires a `@farmslot/recipe-harness` with recipe-library support; older harnesses keep working (run ignores libraries, `flows`/`--library` explain the upgrade). Installed harnesses snapshot `runner/flows/` + `runner/library.json`; `scripts/check.mjs` validates committed flow catalogs against the action manifests.
671
+ - **Architecture TL;DR**: `docs/architecture.md` gains a top-of-doc summary answering the four recurring questions — scope, runner-owns-logic/skills-stay-thin responsibility split (with the in-flight `recipe-harness` thinning as the concrete example), the bash/ts/mjs/cjs language policy, and why this stays one package. No behavior change.
672
+
673
+ ## 0.1.9 - 2026-07-01
674
+
675
+ ### Fixed
676
+
677
+ - **Node 24 type-stripping under `node_modules`**: `bin/metamask-recipe` detected Node source-TypeScript support by running a probe `.ts` in `/tmp`, which passes on Node 24. But Node refuses type-stripping for files under `node_modules` (`ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`), so when the runner is installed as a dependency the probe was a false positive and `node runner/src/cli.ts` failed (broke extension preflight). The check now skips the node-source path when `RUNNER_DIR` is under `node_modules`, falling through to `dist/cli.js` or `tsx`.
678
+
679
+ ## 0.1.8 - 2026-06-30
680
+
681
+ ### Fixed
682
+
683
+ - **Runner dependency bootstrap**: new `orchestration/lib/ensure-runner-deps.sh` installs `@farmslot/recipe-harness` when a cloned runner checkout has no `node_modules` (uses `npm install`, not `yarn`, so parent product `.yarnrc.yml` settings such as `approvedGitRepositories` do not block install). Invoked from `bin/metamask-recipe`, all adapter `inject` paths, and optional local `link-local-farmslot` when `FARMSLOT_ROOT` is set.
684
+ - **Extension Node pin in tmux/nohup**: `activate-repo-node.sh` now prefers `.tool-versions`, tries `.nvmrc` major.minor candidates (e.g. `24.13` → `24.13.0`), and is installed into the harness overlay. `start-watch.sh` / `refresh-build.sh` refuse to run `yarn start` when Node cannot be pinned (prevents Homebrew Node 26 from breaking webpack).
685
+ - **Farmslot CDP port alignment**: new `resolve-farmslot-ports.sh` reads pool JSON by repo path so `mme-recipe` / `recipe sync` use the same `cdp_port` as slot-view shortcuts (e.g. mme-6 → 7666). `mme-recipe sync` rewrites `agentic-runtime.json` after harness refresh.
686
+ - **`recipe build` semantics**: `mme-recipe build` runs a full rebuild when no watcher is active; `refresh` stays fast-path only.
687
+ - **Runtime-launch CLI**: removed log-regex failure classification from `cli.ts`; failures point at `launch.log` / summary artifacts instead of mislabeling (e.g. webpack errors as `wallet_fixture_not_ready`).
688
+
689
+ ### Changed
690
+
691
+ - **Mobile AgenticService path**: overlay and inject/cleanup/verify paths now target `app/dev-tools/AgenticService` (was `app/core/AgenticService`), matching metamask-mobile's dev-tools relocation. Navigation/App patches use dynamic `import('../../dev-tools/...')` and `dev-tools/AgenticService/AgentStepHud` imports.
692
+ - **Step HUD bus decoupling**: `emitStepHud` / `registerStepHudCallback` live in `AgentStepHud.tsx.patch` so `AgenticService` no longer exports the HUD registry; the bridge stays off the static import graph for release dead-code elimination. `AgenticService.install` is guarded with `if (!__DEV__) return`.
693
+ - **Mobile inject idempotency**: skip copying overlay files when checkout content already matches (`cmp` before `cp`); Navigation/App patches no-op when already present (avoids spurious git diffs on re-install).
694
+ - **Mobile harness upgrade path**: re-inject upgrades legacy `app/core/AgenticService` Navigation/App imports to `app/dev-tools/AgenticService` when the old harness patch is already present.
695
+
696
+ ## 0.1.7 - 2026-06-28
697
+
698
+ ### Fixed
699
+
700
+ - **Orchestration doctor drift**: list `extension/console-tail` in `orchestration/manifest.json`. The live console streamer added in 0.1.6 was unlisted, so `orchestration/doctor.mjs` (and the `orchestration-doctor` contract test) failed the unlisted-script drift guard.
701
+ - **Mobile step HUD hidden behind native modals**: the injected `AgentStepHud` overlay (`library/actions/mobile/app-overlay/.../AgentStepHud.tsx.patch`) now wraps its view in `react-native-screens` `FullWindowOverlay`, so the dev step HUD paints in a UIWindow above native-stack modal screens (perps close-position / TPSL) instead of being occluded. Kept byte-identical to metamask-mobile `main`; a contract-test drift-guard (`tests/contract/mobile-inject.test.sh`) now asserts the overlay retains the `FullWindowOverlay` wrap.
702
+
703
+ ## 0.1.6 - 2026-06-27
704
+
705
+ ### Added
706
+
707
+ - **Live extension console tab** (`orchestration/extension/console-tail.mjs`): standalone CDP streamer that tails the running extension's service worker **and** UI-page console (`Runtime.consoleAPICalled` / `exceptionThrown` / `Log.entryAdded`), re-attaching across MV3 service-worker churn. Auto-opened as a `console-<cdp>` tmux window on the interactive bringup (`mme-recipe up` / `rebuild`) and the live/proof flow (`extension/launch.sh`); mirrored to `temp/recipe/runtime/extension-console.log`. Silently skipped when there is no tmux session or CDP (headless/CI).
708
+
709
+ ### Fixed
710
+
711
+ - **Blank dev-server tmux windows**: Metro (`bin/mm-recipe`), webpack `recipe watch` (`bin/mme-recipe`), and the harness webpack build (`orchestration/extension/start-watch.sh`) hosted the bundler in the tmux window with stdout/stderr redirected to a log file, leaving the pane empty. The bundler now runs detached and the window is a read-only `tail -F` viewer of the same log. Process/pid stop lifecycle is preserved: `mm-recipe stop` closes the Metro viewer (via a `metro.tmux` marker) and `mme-recipe stop` closes the webpack viewer. The console log is truncated per run (parity with the Metro/webpack logs), and a dead/non-debuggable CDP target is backed off instead of re-attached every poll.
712
+
713
+ ## 0.1.5 - 2026-06-26
714
+
715
+ ### Added
716
+
717
+ - **Mobile runtime-decision** (`orchestration/mobile/runtime-decision.ts`): deps + Metro bundle gate for `mm-recipe up`, parity with extension `runtime-decision`. Decisions: `install` (missing/stale deps or unresolved modules), `launch` (Metro down, cold bundle, or stale log with deps satisfied), `ready` (deps current + successful bundle + Metro up).
718
+ - **`orchestration/mobile/deps-markers.ts`**: MetaMask Mobile product/native marker tables consumed by harness `deps-readiness`.
719
+ - **`mm-recipe decision`**: expose mobile runtime-decision for dry inspection.
720
+ - **CI** `.github/workflows/check.yml`: `yarn check`, contract tests (`tests/contract/run.sh`), and shell syntax on every PR/push to `main`.
721
+ - **Contract tests** `tests/contract/mobile-runtime-decision.test.sh`, `tests/contract/mobile-mm-recipe-up-install.test.sh`.
722
+
723
+ ### Changed
724
+
725
+ - **Runtime-readiness ownership**: generic deps/log/metro primitives import from `@farmslot/recipe-harness/runtime/*` (**^0.3.0**). MetaMask keeps adapter-specific probes (`deps-markers`, Nitro staleness, webpack/CDP) and shell launch only. Publish harness before bumping the dependency — CI installs from npm, not `FARMSLOT_ROOT`.
726
+ - **Extension runtime-decision**: `depsCheck` / `recordDepsBaseline` import from `@farmslot/recipe-harness/runtime/deps-readiness` (no local copy).
727
+ - **`mm-recipe up`**: runs runtime-decision first (`install` → `yarn setup`, `launch` → Metro + dev client, `ready` → bridge wait). `prewarm_bundle` failures now propagate.
728
+ - **Stale Metro log recovery**: when cited unresolved modules are already in `node_modules`, relaunch with cleared Metro cache instead of re-running `yarn setup` in a loop. Stop Metro after `yarn setup`; honor `MOBILE_METRO_CLEAR` even when Metro is already listening.
729
+ - **Native module staleness**: detect `[runtime not ready]` Nitro HybridObject registration failures in `metro.log` (e.g. `NitroFetch` missing from the installed dev client) and run `recipe up` with `--preflight-mode rebuild-native` instead of fast relaunch.
730
+
731
+ ### Fixed
732
+
733
+ - **`runtime-decision --record-baseline`**: `--record` on `run` remains the video alias; baseline recording uses `--record-baseline` (or `--record` only on `runtime-decision`).
734
+ - **`mm-recipe up` / `mme-recipe up` install loop**: one `yarn setup` / `yarn install` per invocation; fail if decision stays `install`. Record deps baseline after successful install and after bridge-ready.
735
+ - **`yarn setup` failures** now propagate from `run_mobile_install`.
736
+ - **Stale Metro unresolved errors**: ignore `Unable to resolve` lines superseded by a later successful bundle (`lastOk > lastErr`).
737
+ - **Android `rebuild-native`**: `launch_android` honors `MOBILE_PREFLIGHT_MODE` via `ensure_android_app_for_mode` (parity with iOS).
738
+ - **Stale native-module log lines**: ignore Nitro HybridObject errors superseded by a later successful bundle.
739
+ - **`mm-recipe up --json`**: emit a single final decision JSON document after the invocation completes (suppress interim install/launch reports).
740
+ - **Unresolved-module log scoping**: only cite `Unable to resolve` lines after the last successful bundle when classifying current failures.
741
+ - **`moduleExistsInNodeModules`**: require `package.json` in the resolved package root (ignore empty `node_modules` dirs).
742
+ - **Persistent bundle errors**: repeated identical Metro bundle failures surface `blocked` / `bundle-error-persistent` instead of relaunch loops.
743
+ - **`PLATFORM` inference**: `yarn setup` defaults from Android adb env / `RECIPE_HARNESS_PLATFORM`, not hardcoded `ios`.
744
+ - **Optional native markers**: when `PLATFORM` is `ios` or `android`, check `ios/Podfile.lock` or `android/gradle.properties` for partial deps.
745
+
746
+ ### Removed
747
+
748
+ - **`orchestration/lib/deps-readiness.ts`**: moved upstream to `@farmslot/recipe-harness/runtime/deps-readiness` (0.3.0).
749
+
750
+ ### Documentation
751
+
752
+ - **README**, **CLAUDE.md**, **AGENTS.md**: runtime-readiness ownership boundary (harness vs MetaMask adapter) and changelog/CI expectations for agents.
753
+
754
+ ## 0.1.4 - 2026-06-24
755
+
756
+ - Add `orchestration/lib/activate-repo-node.sh`: manager-agnostic Node pin from `.nvmrc` / `.tool-versions` (asdf, fnm, mise, nvm, then matching PATH) for non-interactive webpack spawns.
757
+ - Wire `start-watch.sh` and `refresh-build.sh` to activate the repo-declared Node before `yarn start`, including tmux windows where global PATH can shadow version managers.
758
+ - Register `activate-repo-node` and `pin-remote-flags` in `orchestration/manifest.json` so `doctor.mjs` tracks the full extension surface.
759
+
760
+ ## 0.1.3 - 2026-06-13
761
+
762
+ - Add the headless `core` adapter for MetaMask core e2e (HyperLiquid perps testnet, gated mainnet support): `core` action manifest, live adapter, core recipes, and recipe-harness install/cleanup scripts. Mainnet writes require both `node.network: "mainnet"` and `CORE_PERPS_ALLOW_MAINNET_WRITES=1`.
763
+ - Improve handling of stale Metro listeners and session management.
764
+ - Clarify runner/runtime boundaries and centralize runtime helpers in the runner.
765
+ - Note: the 0.1.2 npm artifact was packed before the core adapter landed and lacks `manifests/core.action-manifest.json`; 0.1.3 republishes current main.
766
+
767
+ ## 0.1.2 - 2026-06-10
768
+
769
+ - Declare `ui.key_press` for Mobile and Extension recipe manifests so trusted keyboard input recipes validate against the runner action manifest.
770
+
771
+ ## 0.1.1 - 2026-06-06
772
+
773
+ - Harden harness setup so fallback installs are more reliable when the local skill installer is unavailable.
774
+ - Configure npm scope/cache settings for reproducible package installs and publishes.
775
+ - Prepare pilot npm distribution as `@deeeed/metamask-recipe-runner`; intended to migrate to org ownership if ADR-58 is accepted.
776
+ - Add `mm-recipe` and `mme-recipe` human-friendly wrappers for Mobile and Extension recipe control.
777
+ - Keep `metamask-recipe` as the single package bin; `mm-recipe` and `mme-recipe` are repo/local convenience wrappers.
778
+ - Improve Extension Perps order placement by resolving market price from background market data, stream cache, or visible UI before submitting.
779
+
5
780
  ## 0.17.4 - 2026-07-15
6
781
 
7
782
  ### Fixed