# Changelog

## Unreleased

## 0.44.1 - 2026-08-27

### Fixed

- Accept retained market context as lifecycle-valid Homepage evidence during account-only switches.
- Require controller-backed Mobile unlock proof before continuing a recipe after startup or restart.

## 0.44.0 - 2026-08-25

### Fixed

- Remove `cold_disk_cache` from the `perps.performance` lifecycle enum because disk hydration is a cache source within a lifecycle, not a standalone lifecycle.
- Await promise-returning Mobile `press-test-id` and `set-input` bridge handlers so recipe nodes finish only after the in-app operation settles.
- Keep Mobile broker commands available while Hermes installs the in-app bridge after a target handoff, instead of reconnecting the inspector until actions time out.
- Honor `launch --build` when a healthy Mobile runtime is already connected; the explicit native build can no longer return a false pass without invoking the build leaf.
- Let resident Homepage and short-resume performance recipes finish from the ordered visible-content sequence without requiring a new live takeover.
- Make Mobile reload stay on the pinned device, fail before an active Android Hermes sampler can abort the app, and fail if the process exits or a replacement target never appears; reload never relaunches the app.
- Bind run evidence to the executable Harness source, build, bin, package, library, and adapter inputs plus task-local command/config helpers, and reject input drift or symlink swaps.
- Persist recovery and mutation lists in recipe packages, include a redacted run-scoped application log, reject failed recovery before retrying, and prove lifecycle continuity with native process IDs, including opaque runtimes when requested.
- Refuse stale Mobile source under every heal policy unless an authored `app.lifecycle restart` reloads it, and invalidate run evidence if recipe, library, or product provenance drifts after preparation.
- Require Homepage demand, committed UI, resolved content, and live-current frame records before the canonical Mobile Perps performance recipe can pass.
- Fail a Mobile recipe or action without implicitly restarting Metro or the app; process restarts remain explicit lifecycle actions.
- Keep one Mobile inspector connection open while React Native replays its console buffer, avoiding repeated `Runtime.enable` requests that can stall Metro after unlock.
- Allow `metamask.wallet.lock` to use the same visible Android controls on development and opaque clients.
- Keep account-selection proof attached across the temporary Hermes/CDP stall caused by the account switch itself.
- Add `metamask.perps.ensure_mode` so recipes can reach Lite or Pro through the visible mode control without inheriting prior device state.
- Preserve Homepage boundary source and content-variant metadata in performance summaries, and bind recipe manifests to the exact product checkout commit.
- Accept only structured, generation-coherent Perps live-stream records as performance proof, and keep Mobile wallet and mode probes inside one absolute action deadline.
- Keep the latest automatic Mobile HUD update in the CDP broker and retry it until the in-app bridge applies it when the pinned Hermes target returns, without restarting the app.
- Keep the Perps performance recipe's empty-account setup read-only and allow its setup-only prime capture to contain no performance records.
- Redact quoted JSON credential fields before persisting run-scoped application logs.

## 0.43.0 - 2026-08-21

### Added

- Add platform-neutral `app.performance_capture` and `app.performance_assert` actions that attribute Mobile or Extension CDP trace samples to exact recipe-node boundaries and emit JSON plus self-contained HTML evidence.
- Mobile native UI FPS requires a development client that reports `unstable_frameRecordingEnabled: true`; unpatched clients report partial or unavailable native coverage. Extension uses Chromium CDP and needs no client patch.

### Fixed

- Bound performance capture duration, discard raw samples after node attribution, correct iOS frame completion timestamps, fail closed on unresolved Extension renderers, and reset CDP collector state after trace failures.
- Scope Mobile Hermes broker ownership by checkout runtime and Metro port so a forwarder from a prior port cannot intercept bridge, HUD, or observation commands after a slot port change.
- Preserve explicit Mobile `--device` pins when slot defaults load so HUD and live-adapter child processes cannot target another device.
- Resume broker commands on the next Hermes session generation without losing the pinned logical device.
- Keep Hermes session recovery inside the caller's RPC deadline, pass only the remaining time to the recovered command, and classify broker connection or request timeouts so transient HUD updates do not abort a recipe.
- Accept React Native trace response chunks up to 16 MiB while keeping broker requests capped at 8 MiB, and never retry the non-idempotent `Tracing.end` command.
- Limit Mobile UI captures to clock and native-frame categories; keep browser timeline and JavaScript task categories on Extension.

## 0.42.0 - 2026-08-20

### Added

- Add `app.network_capture` and `app.network_assert` for redacted Mobile HTTP request windows, indexed evidence, and assertions that preserve failed-run diagnostics.
- Support the same bounded network observation contract on Extension and automatically index safe request metadata with recipe-node boundaries for live Mobile and Extension runs.
- Add `mm-harness reload` for Metro-equivalent Mobile reloads and in-place Extension CDP refreshes.

### Changed

- Route Mobile console forwarding and recipe bridge commands through the same debugger connection so Network capture can coexist with HUD and wallet/domain actions.
- Make the canonical Perps performance recipe close positions and orders outside timing for its market-only cohort instead of failing on inherited account state.

### Fixed

- Pass Expo `--clear` only for an explicit `mm-harness launch --clear-metro`, bound default Metro concurrency to four workers, and remove a stopped checkout from Watchman's root set.
- Route React Native DevTools through the harness CDP broker so debugger use, recipe actions, and app reloads no longer evict one another.

## 0.41.1 - 2026-08-19

### Fixed

- Rotate each harness-owned Metro start into a run-scoped evidence generation, bound retained archives, coalesce repetitive bundle progress, and keep bundle-error recovery state inside one launch invocation.

## 0.41.0 - 2026-08-19

### Added

- Add one parameterized, platform-neutral Mobile Perps performance recipe covering cold, cached, resume, reconnect, account-switch, and network-switch lifecycles with native visible-content proof and production loading/WebSocket evidence.

### Changed

- Replace the duplicated Android, iOS, cold, warm, and background Perps performance recipes with the single lifecycle recipe.

### Fixed

- Keep explicitly pinned iOS and Android bridge targets isolated when both platforms are active, and classify bounded CDP target failures as infrastructure failures.
- Make Mobile wallet unlock and account selection deterministic across app restarts, propagate recipe timeouts through Perps state setup, and preserve missing performance counts and offsets as unknown instead of zero.
- Keep the entire checkout-local `temp` tree outside Metro watching during canonical `yarn watch` launches, and leave the Mobile recipe HUD visible by default for performance lifecycles.

## 0.40.1 - 2026-08-16

### Fixed

- Read durable funding claims and mutation receipts through one non-symlink file descriptor, and keep filter polling delays out of page-evaluated code.

## 0.40.0 - 2026-08-16

- Remove Extension's public `metamask.perps.close_positions` and `metamask.perps.start_state` surfaces and its mutating `runner.action-validation` recipe; retain only state assertions it can prove without implicit readiness or unreceipted convergence.
- Make guarded Extension custom-network setup wait for an unobscured account-menu hit point before opening network management.

### Added

- Add explicit Extension fullscreen/sidepanel launch selection and auto/show/hide recipe HUD policy.
- Bind external-mutation runs to independently validated release, onboarding, balance-evidence, request, and preflight bytes plus the same-run visible wallet/runtime identity.
- Add a packaged Perps market-filter release recipe with a deterministic market-list start state and every nonconditional category control.
- Add official-compatible visible Extension Perps market-list/activity consistency assertions, plus source-dev-only background snapshot sampling and activity-to-history identity expectations.
- Bind every live Extension action result to the selected CDP target and current browser runtime session.

### Changed

- Canonicalize durable Extension Perps release journeys under one discovered recipe ID each, with activity and funds flows scoped to Extension and explicit official/source-development and live-mutation boundaries.
- Parameterize live values in the Extension Perps order-validation recipe and narrow existing market-details, activity-filter, and funds-preview claims to their asserted evidence.
- Require an explicit flat market for non-submitting Extension order-form recipes and make live-price movement an opt-in source-development snapshot assertion.

### Fixed

- Trust only the current Perps-only funding registry plus the named frozen G005 digest; reject duplicate or aggregate-over-cap mutation operations and budgets, mutation-capable balance graphs, root-ref dependency shadowing, and non-visible or non-Home balance observations.
- Bind trusted funding and onboarding evidence to exact emitted call graphs, successful node/action order, and reachable root PASS terminals while preserving Mobile's adapter-owned external-mutation consent boundary.
- Stop advertising shared Perps clean-market convergence on Extension; its state helpers now expose only navigation and assertion inputs and reject ignored creation or cleanup parameters during planning.
- Remove unsupported Extension send completion, Swap/Bridge submission, and generic activate-and-observe capabilities plus four non-executable Assets submit recipes; retain read-only, prepare, review, quote, and Mobile-owned live Swap planning.
- Bind funded Extension Perps margin and TP/SL updates to the exact visible position size, enforce order caps against requested leveraged exposure before receipt publication, and default funded placements to one-times leverage.
- Fail closed on visible and background Perps financial mutations without an identity-bound durable receipt, current visible funding minimum, runner-owned artifact/runtime, and exact dispatch-boundary provider/network; terminally record known non-dispatch instead of retrying the same claim.
- Require approved visible wallet method and credential provenance plus a hash-bound emitted recipe, pass summary, artifact manifest, and exact reachable root-terminal PASS in funding evidence.
- Require every funded visible position edit or close to recheck current funding, provider/network, and exact target state after receipt publication and immediately before its first pointer dispatch.
- Invalidate funded-mutation wallet identity proof after account selection, reset, lock, unlock, setup, or any state-changing generic UI action until a complete fresh visible wallet import proof succeeds.
- Keep Extension Perps state wrappers observation-only so they cannot place, close, or cancel implicitly outside a leaf action's funding authorization.
- Preserve a final Perps navigation observation poll and require Side Panel convergence to one MetaMask product surface beside the explicitly requested dapp.
- Require the canonical non-submitting Swap quote recipe to prove a positive reviewable quote instead of accepting a no-route state.
- Refuse cleanup-bound custom-network setup when the target network already exists, preserving pre-existing wallet configuration.
- Bind Android release-artifact action results to the verified native app process session and redact Mobile wallet-import identity proofs to a matched digest.
- Distinguish obscured Perps controls from harmless edge intersections in visible layout proofs and include confirmation controls on funds surfaces.
- Reject duplicate rendered market, watchlist, or activity identities during optional Extension consistency proofs.
- Read Perps funds confirmation titles from their visible wallet-initiated header and support exact amount, token, network, percentage-control, and disabled-state assertions.
- Wait for Basic Functionality consent to enable its confirmation before submitting the visible settings change.
- Reach the Extension global wallet menu through the visible Home control before locking from headerless product routes.
- Keep Extension surface transitions singular and route the sidepanel launcher's obscuration hit test through hardened raw CDP when its visible notification badge overlaps the button.
- Namespace funded Perps claims by exact request and preflight bytes, persist identity-bound receipts before live pointer dispatch, and restrict over-cap order recovery to exact cleanup-only cancellation without upgrading placement success.
- Relaunch intact Extension release artifacts without source dependency repair, and support exact receipt-bound close-only Perps cleanup with primary-card and balance reconciliation proof.
- Validate Extension Perps activity filters against their singular route query while requiring the requested filter label to remain visible.
- Make Extension `wallet.import method=ui` idempotent on an onboarded profile by visibly unlocking and selecting the exact derived account without fixture or background-state probes.

## 0.39.0 - 2026-08-14

### Added

- Add opaque Android release actions for token details, sorting, visibility, custom-token import, custom-network lifecycle, Perps search and watchlists, Swap Max, slippage, guarded submission, and wallet locking through native accessibility.
- Make shared Assets, Perps, and Swap release recipes executable on untouched Extension and Android release artifacts with parameterized visible-state assertions.

### Fixed

- Keep physical-device actions on isolated semantic sessions, bypass biometric authentication with password unlock, verify every recovery-phrase field, and reject incorrect token metadata as a product failure.
- Prove Extension token visibility changes and parse visible custom-slippage labels consistently across both clients.

## 0.38.1 - 2026-08-14

### Fixed

- Read release evidence through verified regular-file descriptors, escape generated Markdown safely, and keep UI waits out of dynamically constructed page expressions.

## 0.38.0 - 2026-08-14

### Added

- Add visible Perps release actions and recipes for market details, search, filters, watchlists, order entry, position management, funds confirmations, and activity on untouched Extension builds, plus native Android market-state evidence without a development bridge.
- Add shared Assets release actions and recipes for token lists, details, sorting, import, visibility, persistence, send review, custom gas, and network scope on Extension, plus native Android holdings evidence.
- Add shared Swap and Bridge actions and recipes for exact asset selection, quote amounts, Max, slippage, and guarded submission on Extension, with native Android selection and quote evidence.
- Add Extension custom-network lifecycle and visible-state actions plus bounded balance-stability validation, and native Android multi-network holdings evidence.
- Add a canonical release-report gate that rejects missing case IDs, incomplete evidence, non-terminal verdicts, and unfinished case inventories before writing report metadata and quality output.

### Fixed

- Recover Runway provisioning from an unresponsive iOS simulator by restarting the selected device once, waiting for a confirmed boot, and retrying the bounded installed-app probe.
- Consume Protocol 0.21.0, Recipe Harness 0.15.0, Agent Runtime 0.8.1, and Expo Recipe 0.8.1 as one compatible QA runtime set.
- Stop every Chrome process using an Extension validation profile before replacing its runtime snapshot, preventing stale pages from loading chunks from a different build.
- Prefer an unlocked Extension Home page over stale onboarding and Side Panel pages when multiple CDP targets remain open.
- Fixed opaque Android navigation completing the same native session twice after reaching its destination.
- Keep Android release actions on one device-and-app-scoped accessibility session so sequential recipe nodes do not conflict over stale leases.
- Navigate Android Swap through semantic accessibility identifiers, dismiss the keyboard before selecting exact chain-qualified assets, and scroll the token list until the requested row is visible.
- Unlock an opaque Android release with the wallet password instead of entering biometric authentication, and omit Metro launch data from native release lifecycle actions.
- Bind send and limit-order evidence to the newly submitted operation, reject selectors that native UI cannot disambiguate, and fail when Perps navigation or partial TP/SL reconciliation is not proven.
- Classify live Swap submission as an external mutation, reject ID-less release-report cases, and probe competing Extension tabs before selecting the runtime health target.
- Require explicit approval for live Perps submissions and reject release evidence that escapes its declared root or lacks regular trace leaves.
- Bind custom-token reuse to its chain and contract, reject vacuous asset sorting proof, and report capped Perps observations as truncated.
- Bind Swap confirmation to a newly created transaction, reconcile exact TP/SL values, assert disabled zero-withdraw submission, and require structured run evidence.
- Dismiss the first-run Perps tutorial through its visible Skip control before release recipes act on the Perps home screen.

## 0.37.0 - 2026-08-13

### Added

- Add archive and extracted-tree integrity checks, caching, provenance, and isolated launch for downloaded Extension release artifacts without using the checkout build or product configuration.
- Add verified local Android APK install and launch with expected version name, version code, package ID, trusted digest, explicit device selection, terminal provenance, and native-accessibility recipes that do not require a development bridge or HUD.
- Add an Extension Perps snapshot action for bounded, redacted account, market, position, open-order, history, and watchlist observations when the background test hook is available.
- Add a release QA capability map covering the current Perps, Assets, Swap and Bridge, and Networks checklists, with a prioritized parity roadmap.
- Add native Android release navigation and accessibility observation for wallet home, Perps home, and parameterized Perps markets without Metro, a development bridge, a HUD, or fixed screen coordinates.

### Fixed

- Probe Ethereum through the Extension UI provider when production builds omit test state hooks, while preserving explicit unreachable failures.
- Import recovery phrases through production LavaMoat onboarding without browser clipboard permissions, and remove the temporary browser bridge after use.
- Keep release-artifact recipe failures on the acquired binary and fail closed instead of recovering through a checkout build.
- Relaunch a stopped verified Extension artifact from its integrity-checked cache without switching builds or deleting the imported wallet profile.
- Recover a newly opened Extension home tab from Chromium's transient blocked-page shell by navigating the selected extension target once before readiness fails.
- Prefer the visible side panel after official-build onboarding opens it, instead of leaving recipes attached to the completed onboarding tab.
- Reject unattended biometric setup explicitly, handle default Mobile notification and wallet-tour choices on bridge-backed onboarding, and keep Extension-only Perps snapshot limits out of Core validation.
- Confine Extension runtime snapshots and artifact identity state to non-symlinked checkout runtime directories before replacing prior state.
- Complete opaque Mobile onboarding without Metro or a development bridge by cancelling biometric storage for password-only automation, handling notification and wallet-tour prompts with configurable defaults, and proving the imported address through native UI.
- Close each physical-device accessibility session only after its wallet proof completes, so composed reset/import/state recipes hand the device to the next node cleanly.
- Label artifact preparation as preparation instead of reporting it as a client build.

## 0.36.0 - 2026-08-13

### Added

- Add a composable Mobile and Extension wallet import recipe that either verifies an existing fixture-backed profile or imports the primary mnemonic through visible onboarding UI, with secret-safe inputs, privacy-preserving defaults, and configurable MetaMetrics and Mobile interests.
- Add `fixtures reset` plus a shared Mobile/Extension reset-import recipe. Extension resets its owned browser profile; Mobile clears only the selected app's data while preserving its installed build. The visible recipe separately proves the real user reset and import flow.

### Fixed

- Restart a harness-owned Extension watcher when its effective Infura credential differs from the current product configuration, and preflight compiled Ethereum and Linea credentials before Chrome starts, preventing a forced-red validation from poisoning later launches or opening a native HTTP authentication dialog.
- Make normal Extension launch and runtime recovery use the product's `yarn start` development build, while keeping production-like LavaMoat builds behind explicit `launch --build`.
- Require wallet profile reuse to match the requested account, wait for Mobile onboarding to reach a terminal route, and resolve the exact Mobile reset device and prewarm its bundle before relaunching after app-data reset.
- Refuse ambiguous installed Mobile app variants during reset, prefer complete environment wallet credentials without parsing a stale fixture, and reject bad Extension RPC configuration before replacing the loaded runtime snapshot.
- Validate recovery credentials, fixture structure, and platform-specific onboarding choices before destructive resets; prove Mobile reset by the visible import control; honor persisted MetaMetrics consent through Security settings; and keep file-backed wallet secrets out of bridge errors.

## 0.35.0 - 2026-08-12

### Fixed

- Make Extension and Mobile launch verification fail when their in-app EVM network client cannot complete a live RPC, with exact Infura configuration and retry guidance.
- Make Extension doctor require checkout-pinned Playwright Chromium and report the exact install command when it is missing.
- Make doctor report the exact dependency-install command when `--fix` finds stale or missing dependencies instead of implying they were repaired.
- Make the setup prompt reuse configured skill sources and require consent before cloning or editing shell startup files.
- Keep installed overlays delegated to the source runner that installed them before falling back to a potentially stale global runner.

## 0.34.4 - 2026-08-12

### Changed

- Split the site quick start into one-time setup and task-focused workflow tabs.

### Fixed

- Update the transitive `tar` dependency from 7.5.19 to 7.5.22.
- Update transitive `js-yaml`, `nanoid`, and `postcss` dependencies.

## 0.34.3 - 2026-08-12

### Added

- Add a composable headless Core Perps snapshot recipe that calls the public controller APIs with configurable Terminal endpoints.
- Add route-agnostic Extension actions and a reusable recipe for post-GC navigation slopes, detached-DOM growth, and equal-duration idle-control memory measurement.

### Fixed

- Preserve an already-foregrounded Android app while recipes toggle network state, avoiding an unnecessary launcher intent during recovery validation.

## 0.34.2 - 2026-08-11

### Added

- Add composable platform-neutral Homepage Perps position setup and background-lifecycle measurement recipes.

### Fixed

- Give Mobile foreground lifecycle actions the same bounded 60-second runtime-reacquisition window as launch and restart.
- Classify iOS foreground as a same-process resume or cold relaunch, and let reconnect recipes reject process-evicted samples immediately.
- Quiesce active Perps streams before direct-CDP performance cache removal and verify the disk keys remain absent.
- Exclude queued pre-lifecycle socket frames from lifecycle-specific Fresh DFD measurements.

## 0.34.1 - 2026-08-09

### Fixed

- Launch Mobile Metro through the product's canonical watcher while preserving slot, build, Sentry, and Segment environment settings, applying cache clears only to the selected Metro instance, and removing the config override that could corrupt cold bundles.
- Pin native Mobile builds to the exact supported Main or Flask app identity and Android device, rejecting ambiguous physical-device names instead of building or launching on the wrong target.
- Clean up timed-out Metro process groups safely and require the cold-disk-cache Perps freshness gate to report the expected lifecycle.
- Allow performance recipes to enforce a first-fresh socket-to-subscriber latency budget for a stream and subscriber throttle, optionally scoped to a lifecycle while always reporting the observed lifecycle.
- Allow Mobile wallet unlock actions to use their declared target and unlock timeouts without the live-adapter process ending first.
- Remove Android's 180-second `screenrecord` cap so `--record-video=full-run` covers long recipe runs.
- Retry empty Metro target lists, derive the Android model pin from `ADB_SERIAL`, and use pin-aware wallet status while Hermes re-registers between recipe actions.
- Keep compound Mobile actions under one debugger lease so nested bridge calls cannot race another recipe action.
- Let Android `scroll_into_view` fall back to a scrollable ancestor when the target itself is not a scroll container.
- Leave Metro's worker count at its native default unless `METRO_MAX_WORKERS` is explicitly configured.
- Bound each Mobile wallet-status probe by its polling deadline, raise the default unlock verification window to 30 seconds, and give the Android cold-no-cache recipe an explicit cold-start allowance, preventing a successful physical-device unlock from being reported as a timeout.

## 0.34.0 - 2026-08-07

### Fixed

- Record Mobile Android recipe runs with the selected device's native `screenrecord` capability when `--record-video=full-run` is requested.
- Record iOS Simulator recipe runs with `simctl recordVideo`, avoiding hidden/offscreen Simulator-window captures that produce empty videos.
- Keep pinned Android commands on their requested device while its Hermes target re-registers with Metro after a runtime rotation.
- Make Mobile account selection wait for the selected account state without disconnecting or reinitializing the Perps controller, preventing duplicate account-change reconnections and transient Perps errors.
- Resolve shutdown iOS simulators to their canonical bridge-visible name while preserving the requested UDID for native launch.
- Keep lifecycle performance recipes free from HUD bridge timeouts while the app is backgrounded or its runtime target is rotating.
- Make Android coordinate swipes and scroll-into-view use the selected device consistently, including the native fallback path.

### Added

- Add manifest-declared Mobile `app.lifecycle` and `app.network` actions with device targeting, runtime readiness, and Android offline/online recovery.
- Add reusable Perps Homepage performance capture with lifecycle, delivery-source, visible-content, and socket-pipeline validation.
- Bundle the device-proven Android/iOS Perps Homepage short-resume, reconnect, cold-cache, cold-no-cache, and Android network-recovery recipes with explicit cleanup and full-run evidence commands.
- Add wallet setup/account-selection readiness checks and source-freshness recovery evidence for Mobile recipe runs.

## 0.33.3 - 2026-08-05

### Changed

- Lead the getting-started site with what a recipe is for — proving a change inside the running app, catching its own regressions on replay, and standing as the trust layer between an agent's change and a human's review — and add copy-paste prompts for first-time setup and the shared Perps recipe library.
- Add a dedicated Perps workflow page covering library discovery, testnet-first live proof, platform recipe selection, evidence, composition, and drift classification.

### Fixed

- Raise the site's stated harness floor to 0.33+ and make first-time iOS setup pin one simulator UUID through forced Runway provisioning, install verification, launch, and fixture application.
- Recover Mobile navigation across typed Hermes target transitions after lifecycle foregrounding with at most one route-verified reissue.

## 0.33.2 - 2026-08-04

### Fixed

- Keep generated Extension account-tracker state aligned with its Mainnet-only network configuration so stale chain entries cannot break RPC polling.

## 0.33.1 - 2026-08-04

### Fixed

- Make Mobile onboarding deterministic by pinning provisioning, native builds, launch, and readiness to one simulator UDID and Metro port, while ensuring `doctor --fix` enables Perps without inventing `OVERRIDE_REMOTE_FEATURE_FLAGS`.

## 0.33.0 - 2026-08-03

### Added

- Expose standard full-surface capture and continuous UI gestures for Mobile and Extension by delegating to the shared native and browser transports.

### Changed

- Upgrade `@farmslot/agent-runtime` to 0.7.0, `@farmslot/protocol` to 0.18.0, and `@farmslot/recipe-harness` to 0.14.0, and add `@farmslot/expo-recipe` 0.7.1 with `agent-device` 0.19.3 for the native UI provider.

## 0.32.0 - 2026-08-02

### Changed

- Organize bundled platform recipe variants under `library/recipes/<adapter>/` while retaining temporary compatibility with legacy filename suffixes.

## 0.31.0 - 2026-08-02

### Added

- Add one cross-platform `metamask.deeplink.open` capability that exercises real Chrome or device URL ingress for external deeplink proofs.

## 0.30.0 - 2026-08-02

### Changed

- Upgrade the Farmslot protocol, recipe runtime, and agent runtime to their continuous-gesture-compatible releases and converge published installs on protocol 0.16.0.

## 0.29.3 - 2026-08-01

### Fixed

- Preserve immutable harness snapshots during runner dependency bootstrap by installing from the repository's pinned Yarn lock without build scripts or inherited product Yarn settings, while recognizing dependencies hoisted by `npx`.

## 0.29.2 - 2026-08-01

### Changed

- Upgrade `@farmslot/recipe-harness` to 0.11.1 so iOS lifecycle terminate and restart remain idempotent when Simulator reports that it found nothing to terminate.

## 0.29.1 - 2026-08-01

### Added

- Deploy the static getting-started site through GitHub Pages when its files change on `main`, with manual deployment restricted to the same `main` branch.
- Document the farmslot specification on the getting-started site: what it standardizes, that its schemas are published and versioned, and why one spec is what makes an engineer's run and a fleet run the same artifact. Carries the control-plane boundary — experimental, sole-maintained, and required by nothing the site teaches.

### Changed

- Define composition-first recipe maintenance and require failures to be classified against authoritative expectations before the owning product, recipe, runtime, or harness layer is changed and revalidated.
- Define the signed-deeplink canonicalization boundary, its required negative cases, and the distinction between external-ingress proof and in-app route-consumption evidence.

### Fixed

- Correct the site's setup path: `mm-harness setup-base` shipped in 0.29.0, so the bootstrap is no longer described as unpublished.

## 0.29.0 - 2026-08-01

### Added

- Add `mm-harness setup-base` to bootstrap the shared default MetaMask checkout layout with interactive and scripted selection, persisted preferences, disk checks, dependency installation, and machine-readable summaries.
- Ship a dependency-free getting-started site with an interactive walkthrough, platform-filterable command reference, architecture guide, tutorials, reviewer evidence guide, and automated browser acceptance and contrast checks.

### Changed

- Upgrade the Farmslot runtime dependencies to their structured suite-evidence releases and converge on protocol 0.15.0.

### Fixed

- Verify Extension MetaMetrics consent from the production background controller's flat state instead of relying on test-only page state hooks.
- Resolve Extension Perps network state from both flattened and nested controller stores.
- Update both supported `brace-expansion` lines to their patched releases for CVE-2026-14257.
- Build the non-test Extension proof runtime with its webpack sources and supported debug hooks, then read wallet actions from clean app state and wait for account selection to converge.
- Bind Extension runtime reuse to a per-launch nonce echoed by Chrome, with owner-only state, exact process-argument checks, and machine-global quarantine for unproven detached launches; keep validation launches on the invoking runner with checkout-contained runtime files; route Playwright-owned Extension injections through raw CDP; and require the hardened-page-safe recipe harness for page scrolling.

## 0.28.0 - 2026-07-31

### Added

- Add `actions --matrix` and adapter-aware action refusals that name missing capabilities and list the adapters that satisfy them.
- Add `ui.locators` for ranked, copy-pasteable recipe targets derived from the current Extension or Mobile `ui.visible` observation.

## 0.27.0 - 2026-07-31

### Changed

- Mobile UI actions now record bounded native-hierarchy observations, while defaulted Extension UI actions record screen and visible observations; Extension screenshots and recordings publish through symlink-safe private staging as regular `0600` files, Mobile launch and diagnostics resolve absolute device-tool paths, and iOS preparation enables application accessibility before relaunching.

### Fixed

- Serve analytics capture on both IPv4 and IPv6 loopback so Mobile clients resolving `localhost` to `::1` can reach the collector without exposing it on external interfaces.

## 0.26.5 - 2026-07-31

### Fixed

- Forward Mobile launch's resolved Metro port to every runtime leaf so stale slot context cannot start, compile, launch, or observe a second Metro.
- Propagate inline Segment configuration into detached Mobile Metro runs and invalidate the Metro cache when it changes.
- Navigate through Mobile's root settings navigator before changing analytics consent.

## 0.26.4 - 2026-07-31

### Fixed

- Forward Mobile verify's resolved Metro port into nested recipe runs so an older runtime context cannot redirect live bridge checks to another slot.

## 0.26.3 - 2026-07-31

### Fixed

- Stop the verified detached analytics collector with its owning harness runtime instead of leaving an idle process behind.

## 0.26.2 - 2026-07-31

### Fixed

- Verify Extension MetaMetrics consent through the production Redux store when test-only clean-state hooks are unavailable.

## 0.26.1 - 2026-07-31

### Fixed

- Launch an existing Extension build through the live sequencer's supported `--launch-existing-dist` flag.

## 0.26.0 - 2026-07-31

### Added

- Allow analytics event property assertions to use `$exists`, `$type`, and `$gt` predicates for dynamic runtime values.

### Fixed

- Avoid direct `globalThis.chrome` access when reading Extension Perps runtime state, so state preparation works with LavaMoat scuttling enabled.
- Add a harness-owned non-test LavaMoat build mode for production-like Extension proof, while continuing to configure runtime snapshots with the validated Infura project ID. This avoids the placeholder credentials embedded by Extension `--test` builds.
- Keep macOS awake for the lifetime of a harness-launched Extension browser so long live recipes are not invalidated by display-lock compositor suspension.
- Retry post-interaction CDP settlement in an isolated world when navigation replaces the page context.

## 0.25.1 - 2026-07-30

### Fixed

- Source-checkout dependency readiness works under restrictive caller umasks and uses an isolated npm cache instead of inheriting a broken global cache.

## 0.25.0 - 2026-07-30

### Added

- Extension and Mobile recipes can start a local analytics collector, set MetaMetrics consent through each app's native state boundary, and assert captured event cardinality and properties.
- Extension UI actions record default screen and visibility observations in recipe traces.

### Changed

- Updated to Recipe Protocol 0.14 and `@farmslot/recipe-harness` 0.10.3 for trustworthy observation, artifact, and hardened-page CDP behavior.

### Fixed

- Analytics capture rejects foreign collectors, replaces its own previous listener safely, and reverses the collector port to selected Android devices.
- Analytics captures honor the selected runtime directory and store telemetry in private, symlink-safe files.
- Analytics collector startup executes only plan-bound bundled bytes; opt-out consent defaults, typed property assertions, and long settle windows retain their declared semantics.
- Extension analytics consent setup calls the background controller over a temporary Extension port without navigating through unrelated Settings UI.
- Recipe evidence screenshots and recordings publish atomically with private permissions and reject symlink destinations.
- Explicit Extension video recording waits for a captured frame and fails the run when the recorder cannot publish a valid MP4 instead of reporting a proof without its requested evidence.
- Extension fixture seeding evaluates through raw CDP so LavaMoat scuttling cannot break live launch setup.
- Extension launch output prints a runnable `runtime-launch` recovery command, including a selected Chrome profile, instead of suggesting unsupported or incomplete `launch` flags.
- `runtime-launch` distinguishes launch failures from post-launch verification failures and points at the failing evidence.
- Source-checkout dependency bootstrap verifies every runtime dependency and its declared version before declaring the harness ready, while preserving explicit local protocol package links.
- Source checkouts run newer TypeScript sources instead of silently executing an older compiled `dist/`.

## 0.24.0 - 2026-07-30

### Added

- `metamask.perps.edit_order` drives `PerpsController.editOrder()` on the `core` adapter, verifying that a known resting target was replaced by one new order matching the requested attributes rather than trusting the acknowledgement.
- `expect_error` asserts that a `core` perps operation is REJECTED with a given typed error instead of succeeding. The comparison is exact and code-only: actions attach the controller's error code as data, and a rejection carrying no code does NOT match, so an unrelated failure whose message happens to mention the code cannot satisfy a refusal proof. Exposed only on the five actions with a real controller-refusal boundary (`place_order`, `edit_order`, `update_position_tpsl`, `close_orders`, `close_positions`); on read, assert and ensure actions every throw is argument validation or a postcondition failure, which can carry no code, so the parameter is not advertised there.
- `expect_count` on `metamask.perps.assert_orders` and `assert_positions` pins the exact number of matching items, which the default at-least-one check cannot do.
- Attached TP/SL assertions verify that one parent links to live children of the requested TP/SL family, market, side and trigger price, and record those attributes in evidence.
- Relative price and size inputs on `place_order` and `update_position_tpsl`: `take_profit_offset_pct` / `stop_loss_offset_pct` derive a price from live mid, and `take_profit_fraction` / `stop_loss_fraction` derive a size from the live position, rounded to the market's size precision. An absolute value still wins when supplied.
- `notional` on `edit_order` derives a size when the resting order cannot be read.

### Fixed

- Optional params left unset by a recipe template arrive as an empty string; the `core` perps actions treated that as a supplied value, so an order with no TP/SL could be rejected as having an invalid TP/SL size. Empty now means absent and falls through to a supplied alias.
- Batch order-cancel and position-close failures retain the failed controller result instead of being flattened, including partial failures.
- `edit_order` accepts only resting `GTC` and `ALO` replacements; `IOC` cannot satisfy its resting-order postcondition.
- `place_order` no longer discards a `time_in_force` or a stray trigger price on a non-trigger placement. Dropping a field the caller set is wrong on its own terms, and it also hid the controller's refusal of it; both are forwarded and the controller decides.
- `place_order` and `edit_order` reject a blank or unrecognised `side` instead of treating anything that is not `short` as a long, which let an unset template place a real order on an assumed side.

## 0.23.1 - 2026-07-29

### Fixed

- Mobile recipe evidence no longer triggers Metro Fast Refresh: harness-managed Metro ignores checkout-local `temp` artifacts, and screenshot scratch files stay outside the watched project.

## 0.23.0 - 2026-07-28

### Added

- Core Perps actions support trigger orders, attached and partial TP/SL, and position TP/SL updates.

### Fixed

- Core advanced-order mutations verify the submitted order fields, use executable trigger-limit defaults, honor bounded evidence polling, and classify mutation capabilities correctly.
- Core advanced-order camelCase aliases validate consistently with their documented snake_case forms.

## 0.22.0 - 2026-07-26

### Added

- Added `execution-template` as the thin public boundary for shared checklist discovery and materialization.

### Fixed

- `ui.navigate page=home` recognizes both the current bottom navigation and the selected legacy Tokens tab.
- Extension readiness recovers deterministically from transient target-list failures.
- Team-library calls retain their source provenance.

## 0.21.0 - 2026-07-24

### Changed

- Simplified action manifests to one schema-backed keyed action map; action kind,
  library identity, categories, and provenance are inferred from authoritative
  runtime inputs.
- Updated the Farmslot protocol, agent runtime, and recipe harness dependencies
  for Recipe Protocol v1 trace consistency.

## 0.20.0 - 2026-07-24

### Added

- `mm-harness checklist closeout` delegates learning capture to `@farmslot/handoff`; successful terminal marks stage automatically while sharing stays explicit.

### Fixed

- Extension readiness bounds both CDP connection setup and page calls instead of hanging.
- Extension readiness reports a suspended UI compositor instead of claiming a reachable CDP page is interactive.
- Extension hash navigation stays inside the running app so SPA routes are not reset during startup.
- `ui.navigate page=perps` discovers and activates current Perps controls, including bounded back navigation from detail screens, and verifies the destination; bundled recipes no longer embed stale product routes.
- Semantic Extension navigation honors the caller timeout while avoiding repeated control activation.
- Invalid enum recovery commands use the closest valid value instead of repeating the rejected input.
- Perps mutation plans fail early when required market, side, or amount inputs are missing.
- `check diff` honors the checkout's configured ESLint config file.
- Extension builds no longer certify dependency state; Yarn `node-modules` freshness follows the installed tree and only successful installs record its baseline.

## 0.19.1 - 2026-07-23

### Added

- Mobile `ui.press` supports `long_press=true` for real long-press gestures such as resetting a keypad value.

### Fixed

- Perps position and order assertions honor `timeout_ms` consistently on Mobile, Extension, and Core.
- Core Perps reads and assertions honor their explicit network instead of silently defaulting isolated actions to testnet.
- Mobile and Extension `ensure_orders state=open` create a resting testnet limit order when missing and recognize live buy/sell sides consistently.
- Reject invalid live-adapter process timeouts and use the safe default.

## 0.19.0 - 2026-07-22

### Added

- Discover a task recipe's adjacent `recipe-library/` without extra flags.
- Recipe execution now accepts typed root `key=value` parameters, applies defaults, and resolves nested recipes through the same adapter-aware index.
- `run --list` and `run --describe` now expose parameter defaults, adapter variants, nested recipes, sources, and shadows.
- Runs now retain exact digest-keyed dependency recipes and `recipe-resolution.json`.

### Changed

- Made bare `mm-harness fixtures` report the current fixture status and next action instead of failing with an option dump.
- Simplified CLI usage failures and `run --help`; removed duplicate `--project-root`, `--record`, and logical-adapter `--platform` spellings while retaining `--platform ios|android`.
- Direct calls teach missing/invalid values, show declared defaults, omit routine library provenance, and reject implicit Perps assertion/mutation targets.
- Perps reads without a selector consistently return all live items across Extension, Mobile, and Core.
- `run` errors lead to recipe discovery instead of dumping every flag; help separates common overrides from managed integration controls.
- Declared team and task-library actions retain source provenance through the packaged adapter path.
- Validate every bundled recipe dependency graph during `yarn check` so missing or cyclic `call.ref` values fail before runtime.
- Require Farmslot protocol `^0.11.0` and recipe-harness `^0.9.0` for composable Recipe v1 execution.
- Discovery now exposes callable actions through `call --list` and executable recipes through `run --list`.

### Fixed

- Deduplicate recipe-library output and reduce application failures to their actionable message while retaining full trace detail.
- `last` now shows the redacted command and evidence paths, including automatically resolved run/call artifact directories.
- `run --describe` reports the recipe's exact `$schema` URL instead of a fabricated nested version.
- `run --plan` rejects invalid parameters passed into nested recipes.
- Command journals redact sensitive keys inside standalone JSON arguments.
- Core Perps actions support the current Core `tsx` module export shape.
- Recipe delegation preserves upstream non-zero exit status and uses the public `mm-harness` name in recovery commands.
- Recipe discovery and repeated `--library` flags preserve every source in declared precedence order.

### Removed

- Removed redundant `--project-root`, bare `--record`, and logical-adapter uses of `run`/`call --platform`; use `--target`, `--record-video`, and `--adapter`.
- Removed flow catalogs, flow promotion, the `flows` command, and their completion surface; reusable graphs are composable Recipe v1 documents.

## 0.18.0 - 2026-07-20

### Added

- Recipe runs now preflight source provenance and action risk before runtime preparation; untrusted restricted plans require exact-digest approval.
- Local checks now follow explicitly linked Farmslot packages instead of an unrelated sibling checkout.
- 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.
- `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.
- Exact human action detail now prints one runnable `mm-harness call` and the authored recipe-node example already present in the action manifest.
- 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`).
- `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.
- 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.
- Added the minimal cross-platform `metamask.wallet.list_accounts` action for Extension, Mobile, and headless Core without expanding the default domain catalog.
- Recipe runs now choose a unique checkout-scoped artifact directory automatically, preferring an active checkout-local task directory while preserving explicit overrides.
- Added a concise capability-ladder guide covering official primitives, MetaMask defaults, team libraries, and independently proven task-local controller/CDP operations.

### Changed

- Consolidated overlapping guides, historical handovers, and generated audits into five current, command-verified documents for onboarding, recipes, security, QA, and contribution.
- Require Farmslot protocol `^0.10.0` and recipe-harness `^0.8.0` for source-trust enforcement and delegated recovery.
- Reuse the authorized execution context for the first attempt, avoiding duplicate adapter resolution while retaining the immediate pre-side-effect trust check.
- 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.
- 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`.
- Direct `call` execution preserves typed action inputs, redacts sensitive inputs/results, suppresses graph-owned HUD completion, and reports the selected capability source.
- Mobile application logs are captured separately from Metro output, and the app console collector starts only after the selected bridge target is ready.
- Removed duplicate Extension launcher names, legacy port-resolver aliases, and Mobile product-overlay copies so the package has one current execution path per capability.

### Fixed

- Extension launch now rejects missing or placeholder Infura configuration before build and prints the documented setup action instead of opening a broken wallet.
- Mobile status now reports the verified `Login` and `LockScreen` routes as locked even when app state still contains a selected account.
- 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.
- Extension launch now preserves an isolated `CHROME_USER_DATA_DIR` through the live sequencer so browser ownership and launch use the same profile.
- Isolated macOS Extension profiles now use Chromium's mock keychain, preventing credential prompts from blocking automated startup or touching the operator's login keychain.
- `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.
- Approval recovery now explains that the plan digest is bound to the same project root, artifact directory, and execution environment.
- Untrusted custom adapters reject unresolved dynamic imports with a structured trust error; trusted adapters retain direct controller and CDP loading.
- The CLI launcher no longer requires `basename` on `PATH` when resolving recovery-command paths.
- Flow delegation now preserves the upstream non-zero exit status and uses the public `mm-harness` name in recovery commands.
- Flow discovery preserves repeatable library precedence so its printed inspect and recovery commands execute unchanged without duplicating or dropping sources.
- 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.
- Extension debugger contract coverage now exercises the successful local DevTools attach handshake and window creation path instead of only the unreachable-CDP failure.
- 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.
- `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.
- `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.
- 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.
- 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.
- 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.
- 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.
- 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`.
- `flows --library <name=path>` now shows that explicit source in precedence order and forwards it exactly once instead of reporting a duplicate-library failure.
- `run --help` now teaches the automatic active-task or checkout-local artifact directory instead of claiming `--artifacts-dir` is required.
- Streaming leaves now complete cooperative signal traps before the parent exits on Ctrl+C, with a bounded kill fallback for unresponsive process trees.
- `mm-harness actions` now presents grouped, color-aware human output while preserving byte-clean JSON and piped output.
- Explicit artifact paths retain caller-relative CLI semantics; task-derived artifact directories remain checkout-scoped.
- 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.
- Checkout liveness treats `EPERM` consistently so a live-but-unsignalable process cannot have its sandbox lock removed.
- `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.
- 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.

## 0.17.5 - 2026-07-16

### Changed

- 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`.
- 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.
- 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`.
- 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.
- 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.
- `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.
- 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.
- 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.
- 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.
- 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.
- `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.
- 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`.
- 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`.

- 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.
- 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`.
- Core `check diff` now runs changed package tests through their owning Yarn workspaces instead of passing workspace paths to the root Jest configuration.

- 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.

- 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.
- 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.
- 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.
- 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.
- 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.
- 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`.
- `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.
- 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.
- 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.
- 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.
- 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.
- `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.

### Added

- 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.
- `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.
- `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.

### Fixed

- 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.
- `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.
- `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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- `run --plan` now rejects `validate.workflow.startState`, which execution ignores, and directs authors to the supported recipe-root `startState` field.
- `check diff` now uses checkout-configured oxfmt when present, falling back to Prettier, so formatting follows each repository's declared tool.
- `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.
- 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.
- 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.
- Extension `ui.wait_for` now documents its fields and accepts `visible=true|false` as a concise alias for visible/absent assertions.
- `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.
- 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.
- 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.
- 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.
- `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.
- 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.
- `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.
- 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`.

## 0.14.7 - 2026-07-10

### Changed

- `doctor --print-ready` now implies the exit-coded live probe — Farmslot `health_check` hooks need only `--print-ready`, not `--expect-live --print-ready`.
- Mobile `--print-ready` uses bridge `walletState` (unlocked + live bridge), not React Navigation route names; mobile `ready_indicator` is `OK`.
- Removed `metamask-recipe` install symlinks from adapter inject paths (no compat aliases).
- `doctor --print-ready` rejects `--json` (stdout is reserved for the Farmslot indicator line).

### Fixed

- Mobile `surface.runtimeStatus` infers Android from `ADB_SERIAL` / `ANDROID_SERIAL` when present.

## 0.14.6 - 2026-07-10

### Added

- `mm-harness doctor --print-ready` — Farmslot `health_check` mode: prints `health.ready_indicator` on stdout (`extension`/`mobile`: `OK`; `core`: `ready`).
- `doctor --expect-live` — exit-coded liveness gate without indicator output (prepare recovery and other pass/fail callers).

## 0.14.5 - 2026-07-10

### Fixed

- Extension `ensure-browser` closes disposable New Tab / blank / extensions pages after reopen, matching reattach/ensure-ready hygiene.
- 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.
- 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.
- 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.

## 0.14.4 - 2026-07-09

### Fixed

- Include the new extension viewer cleanup scripts in the adapter manifest so published packages pass adapter-surface validation.

## 0.14.3 - 2026-07-09

### Fixed

- 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`.
- Extension sidepanel launch waits longer for Chrome to expose the `sidepanel.html` CDP target, avoiding false failures after Chrome accepts the open request.
- 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.
- 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.

## 0.14.2 - 2026-07-09

### Fixed

- 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.

## 0.14.1 - 2026-07-09

### Added

- `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.
- `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.

### Fixed

- 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`.
- 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.
- 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`.
- Mobile `status` and `doctor` reuse one bridge probe for scoped and additional reachable devices, preserving the bounded probe window when the bridge is down.
- 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.
- `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`.
- 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.
- 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.
- `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.
- 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.
- 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.
- Extension readiness validates local-only CDP websocket targets and sanitizes slot title data before stamping browser tab titles.

## 0.14.0 - 2026-07-08

### Added

- `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.
- 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.
- 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.

### Fixed

- 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>`.
- 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.
- 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.
- 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.
- `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.
- 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.
- `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.
- `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.

## 0.13.0 - 2026-07-08

### Added

- 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.
- 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.
- `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).

### Fixed

- 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.
- 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.
- 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.
- `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.

## 0.12.0 - 2026-07-08

### Added

- 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.

### Fixed

- `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.
- 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.
- `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.
- `open-debug` anchors its CDP-eval fallback to the app checkout so the bridge lock lands where the forwarder watches.

## 0.11.0 - 2026-07-07

### Added

- 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).
- `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`.
- 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.

### Changed

- `@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).

### Fixed

- `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.
- 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.
- `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.
- 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.
- Mobile `cdp.target` required checks only pass when the responding target has `agenticPresent === true`, avoiding false positives from non-instrumented React Native targets.
- 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.
- `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.

## 0.10.0 - 2026-07-07

### Added

- **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.
- **`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`.
- **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.
- **`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.

### Fixed

- **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.

## 0.9.1 - 2026-07-07

### Fixed

- **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.
- **`--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.
- **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.
- **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.

## 0.9.0 - 2026-07-07

### Added

- **`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.
- **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.

## 0.8.0 - 2026-07-07

### Added

- **`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.
- **`--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.
- **`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.

## 0.7.5 - 2026-07-07

### Fixed

- **`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.
- **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.

### Changed

- **`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.

## 0.7.4 - 2026-07-07

### Added

- **`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.
- **`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.

### Fixed

- **`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".
- **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.
- **`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`.

### Changed

- **`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.
- **`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.

## 0.7.3

### Fixed

- **`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.
- **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.

## 0.7.2

### Added

- **`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`.

### Changed

- `@farmslot/protocol` dependency raised to `^0.7.3` so one protocol version serves both the harness and the builder.
- The packed-install contract test now executes `recipe-quality build` from the published layout, guarding the new dependency's packaging path.

## 0.7.1

### Added

- **`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.

### Fixed

- **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.
- **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.
- Removed two parsed-but-unused `launch` flags (`--yes`, `--json-stream`).

## 0.7.0

The "version people use": every remaining identified fix/improvement batched on the
0.6.x stability foundation. Built on top of the extension quick-reuse regression fix
(0.6.3, below). One themed commit per item; full contract suite green (macOS).

### Added

- **`call --list` / `run --list`** — one scope-aware listing of everything invocable
  for the detected adapter: the fuzzy short-name action universe (each action's full
  name plus its unambiguous short name — what `call` accepts) and the runnable
  library flows (what `run <flow>` accepts). No `<action>`/`<recipe>` required; same
  shape under `--json`.
- **`logs --window`** — (re)open the read-only tmux tail window for the resolved
  dev-server port without touching the process (recovers a window closed while
  Metro/webpack keeps running); teaches when there is no run-owned session or log.
- **`doctor` surfaces Screen-Recording / capture readiness** (macOS) — shells
  `capture-helper doctor` and reports pass/WARN with the grant step; capture is
  optional (absent binary → no row, never a doctor failure). Seam: `CAPTURE_HELPER_PATH`.

### Changed

- **`fixtures set` is never silent** — an upfront intent line the moment work begins,
  plus the mobile setup-wallet leaf's own progress streamed live (heartbeat) during
  the 20-40s wallet build; stdout stays a clean `--json` envelope.
- **`actions --action <name>` fuzzy-resolves like `call`** (exact full name → exact
  final segment → substring) and teaches the vocabulary on an unknown name (exit 2,
  `--json` error envelope) instead of throwing.
- **`run` auto-detects the adapter** from the target/cwd when `--adapter` is absent
  (parity with call/doctor); teaches when the repo type cannot be detected.
- **Log-tail windows follow an explicitly owned session ladder** —
  `RECIPE_TMUX_SESSION` → the checkout's `agentic-runtime.json` `session`, with no
  current-session or slot-number inference (the orchestrator names the session; the
  harness only populates windows). Stale `metro-<port>` windows whose port has no
  live listener are swept only inside that resolved session on Metro start.
- **Extension slot ports fill independently** — the resolver no longer early-returns
  once CDP is known, so a context that pins only `cdpPort` still gets `WATCHER_PORT`
  from the pool/formula.
- Orchestrator-neutral protocol-root env: `METAMASK_RUNNER_PROTOCOL_ROOT` is primary,
  `FARMSLOT_ROOT` a back-compat alias for one release.

### Fixed

- **Extension `launch` teaches `yarn install --immutable` on missing/stale deps** —
  a pre-watcher deps-freshness probe fails fast (teaching only, never auto-installs)
  instead of letting webpack crash deep on stale `node_modules`; gated so a reusable
  live runtime (reload-in-place) is never blocked.
- **Core actions run under the target checkout's own tsx** — a core perps action
  dynamically imports the checkout's controller TypeScript, so it now runs under tsx
  (resolved from the target first — the published harness ships none; a thin-installed
  slot has it) rather than bare node throwing `ERR_MODULE_NOT_FOUND`; teaches
  `yarn install` / `yarn build` when no tsx is found.
- **Repo-pinned Ruby for iOS pod/gem work** — `yarn setup` / the iOS native build
  activate the checkout's `.ruby-version` (asdf → rbenv → mise → PATH) so a cold
  checkout does not build native gems against the system Ruby; teaches the install
  when the pinned Ruby is unresolved.
- **iOS deep-link dialog pre-approval at provisioning** — the dev-client scheme
  approval is persisted right after app install, so the first headless `openurl` does
  not pause on the "Open in <App>?" dialog (the launch-time write can race).
- **Extension quick-tier webpack tail window** is regression-guarded (Metro parity).

### Internal

- Dependabot: no high/critical advisories remain (existing `resolutions` cover them).
- Boring-library swaps (pidtree, ajv) declined per the "delete hand-rolled code with a
  bug history" bar: the ps/lsof process detection is contract-tested and working, and
  manifest validation is already schema-driven via `@farmslot/protocol`.
- Orchestrator-neutral port leaf: `resolve-farmslot-ports{,-core}.mjs` / `.sh` and
  `src/adapters/resolve-farmslot-ports.ts` renamed to `resolve-slot-ports*`; the
  function `resolveFarmslotPortsByRepo`, the bash function / cli key
  `resolve_farmslot_ports_by_repo`, and the JS export renamed to the `*slot*`
  equivalents. The old filenames, bash function, cli key, and JS export are kept as
  delegating shims/aliases for ONE RELEASE (deprecation window) — the next release
  removes them. The pool-path constants the leaf reads are left as-is (orchestrator
  data location, not a callable surface).

## 0.6.3

### Fixed

- **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.

## 0.6.2 - 2026-07-06

### Added

- **`-v` / `--version`** prints the mm-harness version.

### Fixed

- **`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.
- **`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.

## 0.6.1 - 2026-07-06

Fresh-install fixes found live on a published-0.6.0 install.

### Fixed

- **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.
- **`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.

### Internal

- Reap-checkout-metros test hardened against a SIGTERM timing flake (polls for exit).

## 0.6.0 - 2026-07-06

Minor release: CLI hardening (decomposed, single-parser, dist-only) plus a
fresh-slot validation wave — every fix proven live on untouched -2 slots and
independently cross-reviewed.

### Changed

- **CLI decomposition + dist-only publish** — `src/cli.ts` is parse+dispatch only;
  handlers under `src/commands/<verb>.ts`; one commander parser; the npm tarball
  ships compiled `dist/` without `src/` and `tsx` is a devDependency (plain-node,
  smaller, faster). Source checkouts keep the tsx dev path (`MM_HARNESS_BIN`).
- `verify` auto-ensures its own overlay (no refusal on a fresh checkout); every
  engine execution resolves the slot's ports/device first (call/run/fixtures get
  the same slot isolation launch has).

### Added

- **`stop` reaps leaked Metro bundlers by checkout** (port-agnostic, path-
  boundary matched), `doctor` surfaces any orphan and teaches the reap.
- **`launch --url <dapp>`** (extension) opens the dapp in the main tab beside the
  MetaMask sidepanel; `--stop-only` profile release before seed; stable per-checkout
  Chrome profile + runtime-dist; post-open tab cleanup.
- DX: run-mode + stale-`dist` warning in `bin`; overlay-delegation notice; docs/CODE-MAP.md.

### Fixed

- `launch` targets the slot's own simulator (never the `booted` alias) and boots it;
  slot-port resolution layers rungs so a partial rung never nulls the port; provision
  resolves the simulator from the pool and boots before install.
- `call` works from published installs (dist bridge, no type-strip crash);
  `ensure_unlocked` fails teaching `fixtures set` on an un-onboarded wallet instead
  of vacuously passing (mobile + extension); `provision --json` usage errors emit the
  standard envelope; deps-not-ready teaches a command that works on a bare checkout.

## 0.5.1 - 2026-07-05

Fresh-install hotfixes found in live validation of 0.5.0.

### Fixed

- **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.
- **`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`.
- **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.

## Unreleased

### Fixed

- **`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.

### Changed

- **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).
- **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).
- **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.

## 0.5.0 - 2026-07-05

Minor release: thin Runway provisioning (harness-owned slot provisioning with a shared artifact cache) plus compiled-dist packaging.

### Added

- **`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.
- **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).

### Changed

- 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.

## 0.4.0 - 2026-07-04

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).

### Added

- **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).
- **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.
- **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.
- **`logs` is multi-source per adapter** — sources and the `--source` default come from the adapter's logSources (extension: webpack/watcher/rebuild/app).
- **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.

### Changed

- **BREAKING (agent contract)**: `doctor --json` `runtime.metro` (string) is now `runtime.devServer` `{label, status}` and is absent for core.

### Feature wave recap (0.3.6-0.3.9)

- `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.

## 0.3.9 - 2026-07-04

### Fixed

- **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).
- **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.
- **UTF-8 locale for pod-triggering spawns** — CocoaPods no longer dies on multibyte podspecs under locale-less orchestrator environments.
- **Hermetic contract tests** — tmux/xcrun/adb are stubbed; the Metro log-window opens only in a run-owned session, never an unrelated attached one.

### Added

- **`mm-harness stop`** — stop the port-scoped Metro this checkout owns and close its log-tail window; idempotent; teaching error on extension/core.
- **Extension launch streams live** with an upfront intent line (ports named); mobile and extension long operations are never silent.
- **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.
- **Colored grouped help with DEV OVERRIDE banner and SLOT line**; `mmdev-harness` completions; outcome-stating launch summaries with a `[dev]` marker.
- **docs/UX-PRINCIPLES.md** — the CLI interaction contract (human surface rules; `--json` envelope purity is exempt and inviolable).

### Fixed

- **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.
- **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.
- **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.
- **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`).
- **`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.
- **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.

### Added

- **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.
- **`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.
- **`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.
- **`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.
- **`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`.
- **`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.
- **`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".
- **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.
- **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.

### Changed

- **`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.
- **`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.
- **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.
- **`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.

## 0.3.8 - 2026-07-04

### Fixed

- **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.

## 0.3.7 - 2026-07-04

### Fixed

- **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.

## 0.3.6 - 2026-07-04

### Fixed

- **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.

## 0.3.5 - 2026-07-04

### Fixed

- **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.

### Changed

- **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`.

## 0.3.4 - 2026-07-04

### Fixed

- **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.
- **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`.

## 0.3.3 - 2026-07-04

### Fixed

- **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.
- **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.

## 0.3.2 - 2026-07-04

### Fixed

- **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.
- **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`.

## 0.3.1 - 2026-07-03

### Fixed

- **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.
- **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.
- **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.
- **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.
- **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`).

## 0.3.0 - 2026-07-03

### Changed

- 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).

### Fixed

- Extension `launch` opens the app directly (composes `live.sh`) instead of a retired command.
- Mobile `cleanup` resilient to a stale runner-source path.
- Core run teaches the correct next step when no account is set.
- Resolved CodeQL alerts (insecure temp dir, file-access-to-http).

## 0.2.0 - 2026-07-03

### Added

- **`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.
- **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.
- **`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.

### Changed

- **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.
- **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.)
- **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.
- **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`.
- **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).

### Fixed

- **`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.
- **`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.
- **`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.
- **`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.
- **`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.
- **`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.
- **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`.
- **`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.
- **`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).
- **`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.
- **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.
- **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.
- **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.

### Changed

- **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`.

### Added

- **`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`.
- **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.
- **`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`.
- **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.
- **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`.
- **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.
  - **`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`.
  - **`logs`** — composes the porcelain `logs` (+ `--full`, `--source metro|app`); teaching exit 2 when nothing is running (no `metro.log`/`webpack.log`), pointing at `launch`.
  - **`debug`** — extension `debug` (`--worker` → `debug worker`), mobile `debug` (`--dev-menu` → `dev-menu`); cross-platform flag misuse teaches (exit 2).
  - **`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.
- **`--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.
- **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.
- **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.
- **`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`.
- **`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`).
- **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).
- **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`.
- **`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`.
- **`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`.
- **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.
- **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.

## 0.1.9 - 2026-07-01

### Fixed

- **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`.

## 0.1.8 - 2026-06-30

### Fixed

- **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.
- **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).
- **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.
- **`recipe build` semantics**: `mme-recipe build` runs a full rebuild when no watcher is active; `refresh` stays fast-path only.
- **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`).

### Changed

- **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.
- **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`.
- **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).
- **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.

## 0.1.7 - 2026-06-28

### Fixed

- **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.
- **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.

## 0.1.6 - 2026-06-27

### Added

- **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).

### Fixed

- **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.

## 0.1.5 - 2026-06-26

### Added

- **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).
- **`orchestration/mobile/deps-markers.ts`**: MetaMask Mobile product/native marker tables consumed by harness `deps-readiness`.
- **`mm-recipe decision`**: expose mobile runtime-decision for dry inspection.
- **CI** `.github/workflows/check.yml`: `yarn check`, contract tests (`tests/contract/run.sh`), and shell syntax on every PR/push to `main`.
- **Contract tests** `tests/contract/mobile-runtime-decision.test.sh`, `tests/contract/mobile-mm-recipe-up-install.test.sh`.

### Changed

- **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`.
- **Extension runtime-decision**: `depsCheck` / `recordDepsBaseline` import from `@farmslot/recipe-harness/runtime/deps-readiness` (no local copy).
- **`mm-recipe up`**: runs runtime-decision first (`install` → `yarn setup`, `launch` → Metro + dev client, `ready` → bridge wait). `prewarm_bundle` failures now propagate.
- **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.
- **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.

### Fixed

- **`runtime-decision --record-baseline`**: `--record` on `run` remains the video alias; baseline recording uses `--record-baseline` (or `--record` only on `runtime-decision`).
- **`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.
- **`yarn setup` failures** now propagate from `run_mobile_install`.
- **Stale Metro unresolved errors**: ignore `Unable to resolve` lines superseded by a later successful bundle (`lastOk > lastErr`).
- **Android `rebuild-native`**: `launch_android` honors `MOBILE_PREFLIGHT_MODE` via `ensure_android_app_for_mode` (parity with iOS).
- **Stale native-module log lines**: ignore Nitro HybridObject errors superseded by a later successful bundle.
- **`mm-recipe up --json`**: emit a single final decision JSON document after the invocation completes (suppress interim install/launch reports).
- **Unresolved-module log scoping**: only cite `Unable to resolve` lines after the last successful bundle when classifying current failures.
- **`moduleExistsInNodeModules`**: require `package.json` in the resolved package root (ignore empty `node_modules` dirs).
- **Persistent bundle errors**: repeated identical Metro bundle failures surface `blocked` / `bundle-error-persistent` instead of relaunch loops.
- **`PLATFORM` inference**: `yarn setup` defaults from Android adb env / `RECIPE_HARNESS_PLATFORM`, not hardcoded `ios`.
- **Optional native markers**: when `PLATFORM` is `ios` or `android`, check `ios/Podfile.lock` or `android/gradle.properties` for partial deps.

### Removed

- **`orchestration/lib/deps-readiness.ts`**: moved upstream to `@farmslot/recipe-harness/runtime/deps-readiness` (0.3.0).

### Documentation

- **README**, **CLAUDE.md**, **AGENTS.md**: runtime-readiness ownership boundary (harness vs MetaMask adapter) and changelog/CI expectations for agents.

## 0.1.4 - 2026-06-24

- 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.
- 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.
- Register `activate-repo-node` and `pin-remote-flags` in `orchestration/manifest.json` so `doctor.mjs` tracks the full extension surface.

## 0.1.3 - 2026-06-13

- 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`.
- Improve handling of stale Metro listeners and session management.
- Clarify runner/runtime boundaries and centralize runtime helpers in the runner.
- 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.

## 0.1.2 - 2026-06-10

- Declare `ui.key_press` for Mobile and Extension recipe manifests so trusted keyboard input recipes validate against the runner action manifest.

## 0.1.1 - 2026-06-06

- Harden harness setup so fallback installs are more reliable when the local skill installer is unavailable.
- Configure npm scope/cache settings for reproducible package installs and publishes.
- Prepare pilot npm distribution as `@deeeed/metamask-recipe-runner`; intended to migrate to org ownership if ADR-58 is accepted.
- Add `mm-recipe` and `mme-recipe` human-friendly wrappers for Mobile and Extension recipe control.
- Keep `metamask-recipe` as the single package bin; `mm-recipe` and `mme-recipe` are repo/local convenience wrappers.
- Improve Extension Perps order placement by resolving market price from background market data, stream cache, or visible UI before submitting.

## 0.17.4 - 2026-07-15

### Fixed

- Removed file replacement races from Extension title stamping and incremental webpack-log reads.
- Validated passive registry versions before caching them and documented the intentional persistence boundary for untrusted application-console transcripts.

## 0.17.3 - 2026-07-15

### Added

- Added a real-checkout human-outcome gate covering live readiness, runtime verification, debugger target resolution, smoke evidence, and unchanged tracked product source across Extension, Mobile, and Core.
- Added generic Core runner smoke and fixture-backed wallet smoke recipes so the reusable examples are not tied to Perps.
- Added nine-slot isolation coverage for platform ports, runtime files, logs, and headless Core behavior.

### Changed

- `doctor` now reports CDP or simulator/ADB screenshot fallback when `capture-helper` is absent or unhealthy, while identifying video as unavailable separately.
- Action, flow, and recipe discovery/documentation now use one concise scope model; the default recipe-library and adapter-surface references were reduced to current behavior.
- Fixture discovery now uses one canonical checkout path instead of legacy fallback locations.

### Fixed

- Mobile launch now waits for two consecutive platform-matched agentic bridge snapshots, preventing a transient startup route from being reported ready before an immediate verify.
- `verify --json` keeps stdout to one machine-readable JSON document even when the platform harness must first be installed.
- Mobile install metadata safely preserves checkout paths containing shell-significant characters.
- Cleanup discovery no longer advertises a removed compatibility flag, and Core recovery guidance now points to the canonical wallet fixture.
- Mobile console forwarding now uses canonical checkout paths, readiness rejects impossible one-poll stability checks, and Extension debug handles malformed WebSocket frames without crashing.
- Extension watch refreshes remove stale runtime files, delay file replacement until transfer completion, allow slower console attachment, and stop the detached webpack process group.

## 0.17.2 - 2026-07-15

### Fixed

- Passive update discovery now runs in a detached, once-daily registry probe, so fast commands populate the cache without adding latency or contaminating machine output.

## 0.17.1 - 2026-07-15

### Fixed

- Extension `debug` now uses Chrome's native DevTools command for the active slot target and opens directly on Console; `debug --worker` targets the service worker without creating a frontend browser tab.
- Extension webpack watchers now launch in their own process group, so they remain alive for incremental rebuilds after the initial `launch --build` command exits.
- Successful Extension watch rebuilds now update the isolated loaded runtime in place, so a normal refresh of the current fullscreen or sidepanel UI shows source edits without another harness build or relaunch.
- Extension slot titles are stamped into the isolated runtime after snapshots and successful rebuilds, preserving the slot identifier across an operator's normal browser refresh without changing product source.
- Extension sidepanel opening no longer waits for the harness-owned user-gesture button to pass visual actionability checks; occluded wallet loading surfaces cannot block the trusted click.
- Human recipe results now print an absolute-path artifact inventory and expand run-scoped application diagnostics inline; single-action calls show the same `CLEAN`/`REVIEW` diagnostics instead of requiring another query.
- Extension console collection now starts after every successful public launch path, including quick reattach, and persists as one checkout-scoped process until `stop`. MetaMask UI/service-worker events and dapp-page events are routed to separate `extension-console.log` and `dapp-console.log` files; bare `logs` defaults to MetaMask while `--source dapp` and `--source webpack` remain explicit.
- Mobile CDP-forwarded app events now write to `app-console.log`, so `logs --source app` and run diagnostics are no longer mixed with Metro bundler output; warm and cold launches start a process-group-detached collector only after bridge readiness so capture remains live after the launch command exits.
- Extension `debug` now opens Chrome's local DevTools frontend inside the exact slot-owned profile, targets the MetaMask extension page rather than the dapp, and verifies the debugging connection before reporting success.
- Mobile fixture application now fails immediately when the checkout fixture is absent, rejects the misplaced `fixtures set --dev` flag with the correct initialization command, and never prints a success message after failure.

### Changed

- Current documentation now teaches the auto-detected workflow and verified human outcomes only: launch, incremental refresh, target-specific debugging/logs, fixture setup, capability discovery, and proof evidence.
- Mobile fixture setup now requires the current multichain account-tree path, and Core signing requires the canonical checkout wallet fixture.

### Removed

- Removed compatibility-only runtime-start fields, hidden `live` execution, removed-command handlers, undomained recipe aliases, flat/non-ESM action lookup, old port-resolver filenames, duplicate Extension launcher naming, obsolete fixture search locations, and the alternate Mobile product-source bridge. Mobile install now requires the current product-owned bridge and writes metadata only.
- Removed the historical CLI migration/spec documents; current behavior is documented in the README, cheatsheet, architecture, and human QA checklist.

## 0.17.0 - 2026-07-14

### Added

- Command-specific JSON failures now preserve a structured `userAction` across device targeting, action calls, recipe validation/execution, checks, and dev-server stop. Successful install/verify/cleanup, provision, and stop transitions add `next` only when one truthful follow-up exists; discovery and terminal evidence results remain quiet.
- `run` and `launch` now implement the documented `--json-stream` contract: line-flushed JSONL phase/progress events, per-node recipe status, compact evidence paths, and one terminal `complete` event. Stream mode keeps stdout machine-clean and takes precedence when combined with `--json`; existing `--json` output is unchanged.
- Interactive Mobile launch, Runway provisioning, and recipe execution now print contextual intent before their first potentially slow readiness or validation probe. The new lines are suppressed for `--json` and non-TTY stdout.
- `last --json` reads an atomic, mode-`0600`, per-checkout resumability journal for significant runtime/proof commands, including redacted args, verdict, evidence paths, and timestamps. Interrupted commands remain `running`; discovery commands do not overwrite the record.
- `actions <query>` now searches action names, categories, fields, and descriptions with typo tolerance, while single-action detail includes compact related-action names.

### Changed

- Action categories now group protocol primitives into useful `assertion`, `control`, `evidence`, and `runtime` domains instead of exposing one category per unnamespaced action.
- Public commands now reject unknown commands/options, missing values and positionals, invalid enums, and excess positionals before dispatch. Human errors include valid command-local options, a closest match when available, and a runnable example; `--json` returns stable `CLI_*` codes with `error.message` and `error.userAction`.
- `docs/CLI-ERGONOMICS-AUDIT.md` is generated from the public command contract so every command remains covered by the shared bad-flag and JSON usage-error policy as the surface evolves.

## 0.16.0 - 2026-07-14

### Added

- Mobile and Extension recipe runs now write bounded, redacted, non-blocking side findings to `diagnostics.json`: Mobile combines its in-app issue buffer with run-scoped log bytes, while Extension owns a CDP console subscription for the run. Core remains N/A because it is headless.
- `actions --categories` now lists compact category counts, and `actions --category <name>` returns a bounded action/schema view so agents can discover capabilities without loading the complete adapter catalog.

## 0.15.2 - 2026-07-13

### Fixed

- Mobile `ui.press text=<visible text>` now invokes the app bridge's text-content press path, while `test_id`, `testID`, and `selector` continue to use exact testID matching; ambiguous target combinations fail before bridge execution.

## 0.15.1 - 2026-07-13

### Fixed

- `doctor --fix` recovery `nextActions` (numbered fixture choices, overlay install, runtime-context retry) now embed the resolved `--adapter` and absolute `--target`, shell-quoted, so each command works verbatim from any cwd. They are also prefixed with the resolved `bin/mm-harness` executable path, so they work in task-local installs where `mm-harness` is not on PATH.

## 0.15.0 - 2026-07-13

### Changed

- 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`.
- 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.
- 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.
- `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.
- 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.
- 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.
- 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.
- 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.
- `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.
- 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`.
- 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`.

- 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.
- 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`.
- Core `check diff` now runs changed package tests through their owning Yarn workspaces instead of passing workspace paths to the root Jest configuration.

- 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.

- 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.
- 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.
- 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.
- 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`.
- `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.
- 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.
- 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.
- 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.
- 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.
- `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.

### Added

- 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.
- `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.
- `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.

### Fixed

- Missing checkout targets now return the structured usage envelope before lock acquisition in `run` and `doctor`; recovery bounds distinguish a consumed successful repair, adapter helpers preserve explicit runtime/device pins, checklist-only runtime resolution is lazy, and fixture finalization is time-bounded.
- Checkout contention now uses one bounded JSON contract across `call`, `launch`, `doctor --fix`, `fixtures`, and `run`: exit 4 with `SANDBOX_BUSY` and an actionable lock path.
- Extension loaded-snapshot checks now use size/mtime comparison instead of full checksums, fall back to an in-process comparison when `rsync` is unavailable, and report comparison failures without forcing a misleading relaunch.
- Mobile Perps environment repair now publishes `.js.env` atomically without following destination symlinks, and Mobile source preflight returns an actionable `MOBILE_SOURCE_UNAVAILABLE` error for non-Git targets.
- Mobile no longer translates the Extension-only `visible=true` wait alias into an unsupported enum; `call --arg` accepts quoted JSON strings for numeric-looking string values.
- Timed streaming leaves now settle as soon as a SIGTERM-terminated direct child exits, reap SIGTERM-ignoring descendants, and forward Ctrl+C/SIGTERM to detached child trees before the harness exits.
- `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.
- `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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- `run --plan` now rejects `validate.workflow.startState`, which execution ignores, and directs authors to the supported recipe-root `startState` field.
- `check diff` now uses checkout-configured oxfmt when present, falling back to Prettier, so formatting follows each repository's declared tool.
- `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.
- 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.
- 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.
- Extension `ui.wait_for` now documents its fields and accepts `visible=true|false` as a concise alias for visible/absent assertions.
- `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.
- 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.
- 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.
- 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.
- `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.
- 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.
- `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.
- 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`.

## 0.14.7 - 2026-07-10

### Changed

- `doctor --print-ready` now implies the exit-coded live probe — Farmslot `health_check` hooks need only `--print-ready`, not `--expect-live --print-ready`.
- Mobile `--print-ready` uses bridge `walletState` (unlocked + live bridge), not React Navigation route names; mobile `ready_indicator` is `OK`.
- Removed `metamask-recipe` install symlinks from adapter inject paths (no compat aliases).
- `doctor --print-ready` rejects `--json` (stdout is reserved for the Farmslot indicator line).

### Fixed

- Mobile `surface.runtimeStatus` infers Android from `ADB_SERIAL` / `ANDROID_SERIAL` when present.

## 0.14.6 - 2026-07-10

### Added

- `mm-harness doctor --print-ready` — Farmslot `health_check` mode: prints `health.ready_indicator` on stdout (`extension`/`mobile`: `OK`; `core`: `ready`).
- `doctor --expect-live` — exit-coded liveness gate without indicator output (prepare recovery and other pass/fail callers).

## 0.14.5 - 2026-07-10

### Fixed

- Extension `ensure-browser` closes disposable New Tab / blank / extensions pages after reopen, matching reattach/ensure-ready hygiene.
- 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.
- 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.
- 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.

## 0.14.4 - 2026-07-09

### Fixed

- Include the new extension viewer cleanup scripts in the adapter manifest so published packages pass adapter-surface validation.

## 0.14.3 - 2026-07-09

### Fixed

- 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`.
- Extension sidepanel launch waits longer for Chrome to expose the `sidepanel.html` CDP target, avoiding false failures after Chrome accepts the open request.
- 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.
- 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.

## 0.14.2 - 2026-07-09

### Fixed

- 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.

## 0.14.1 - 2026-07-09

### Added

- `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.
- `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.

### Fixed

- 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`.
- 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.
- 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`.
- Mobile `status` and `doctor` reuse one bridge probe for scoped and additional reachable devices, preserving the bounded probe window when the bridge is down.
- 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.
- `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`.
- 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.
- 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.
- `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.
- 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.
- 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.
- Extension readiness validates local-only CDP websocket targets and sanitizes slot title data before stamping browser tab titles.

## 0.14.0 - 2026-07-08

### Added

- `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.
- 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.
- 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.

### Fixed

- 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>`.
- 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.
- 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.
- 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.
- `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.
- 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.
- `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.
- `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.

## 0.13.0 - 2026-07-08

### Added

- 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.
- 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.
- `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).

### Fixed

- 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.
- 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.
- 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.
- `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.

## 0.12.0 - 2026-07-08

### Added

- 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.

### Fixed

- `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.
- 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.
- `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.
- `open-debug` anchors its CDP-eval fallback to the app checkout so the bridge lock lands where the forwarder watches.

## 0.11.0 - 2026-07-07

### Added

- 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).
- `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`.
- 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.

### Changed

- `@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).

### Fixed

- `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.
- 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.
- `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.
- 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.
- Mobile `cdp.target` required checks only pass when the responding target has `agenticPresent === true`, avoiding false positives from non-instrumented React Native targets.
- 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.
- `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.

## 0.10.0 - 2026-07-07

### Added

- **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.
- **`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`.
- **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.
- **`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.

### Fixed

- **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.

## 0.9.1 - 2026-07-07

### Fixed

- **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.
- **`--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.
- **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.
- **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.

## 0.9.0 - 2026-07-07

### Added

- **`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.
- **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.

## 0.8.0 - 2026-07-07

### Added

- **`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.
- **`--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.
- **`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.

## 0.7.5 - 2026-07-07

### Fixed

- **`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.
- **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.

### Changed

- **`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.

## 0.7.4 - 2026-07-07

### Added

- **`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.
- **`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.

### Fixed

- **`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".
- **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.
- **`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`.

### Changed

- **`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.
- **`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.

## 0.7.3

### Fixed

- **`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.
- **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.

## 0.7.2

### Added

- **`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`.

### Changed

- `@farmslot/protocol` dependency raised to `^0.7.3` so one protocol version serves both the harness and the builder.
- The packed-install contract test now executes `recipe-quality build` from the published layout, guarding the new dependency's packaging path.

## 0.7.1

### Added

- **`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.

### Fixed

- **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.
- **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.
- Removed two parsed-but-unused `launch` flags (`--yes`, `--json-stream`).

## 0.7.0

The "version people use": every remaining identified fix/improvement batched on the
0.6.x stability foundation. Built on top of the extension quick-reuse regression fix
(0.6.3, below). One themed commit per item; full contract suite green (macOS).

### Added

- **`call --list` / `run --list`** — one scope-aware listing of everything invocable
  for the detected adapter: the fuzzy short-name action universe (each action's full
  name plus its unambiguous short name — what `call` accepts) and the runnable
  library flows (what `run <flow>` accepts). No `<action>`/`<recipe>` required; same
  shape under `--json`.
- **`logs --window`** — (re)open the read-only tmux tail window for the resolved
  dev-server port without touching the process (recovers a window closed while
  Metro/webpack keeps running); teaches when there is no run-owned session or log.
- **`doctor` surfaces Screen-Recording / capture readiness** (macOS) — shells
  `capture-helper doctor` and reports pass/WARN with the grant step; capture is
  optional (absent binary → no row, never a doctor failure). Seam: `CAPTURE_HELPER_PATH`.

### Changed

- **`fixtures set` is never silent** — an upfront intent line the moment work begins,
  plus the mobile setup-wallet leaf's own progress streamed live (heartbeat) during
  the 20-40s wallet build; stdout stays a clean `--json` envelope.
- **`actions --action <name>` fuzzy-resolves like `call`** (exact full name → exact
  final segment → substring) and teaches the vocabulary on an unknown name (exit 2,
  `--json` error envelope) instead of throwing.
- **`run` auto-detects the adapter** from the target/cwd when `--adapter` is absent
  (parity with call/doctor); teaches when the repo type cannot be detected.
- **Log-tail windows follow an explicitly owned session ladder** —
  `RECIPE_TMUX_SESSION` → the checkout's `agentic-runtime.json` `session`, with no
  current-session or slot-number inference (the orchestrator names the session; the
  harness only populates windows). Stale `metro-<port>` windows whose port has no
  live listener are swept only inside that resolved session on Metro start.
- **Extension slot ports fill independently** — the resolver no longer early-returns
  once CDP is known, so a context that pins only `cdpPort` still gets `WATCHER_PORT`
  from the pool/formula.
- Orchestrator-neutral protocol-root env: `METAMASK_RUNNER_PROTOCOL_ROOT` is primary,
  `FARMSLOT_ROOT` a back-compat alias for one release.

### Fixed

- **Extension `launch` teaches `yarn install --immutable` on missing/stale deps** —
  a pre-watcher deps-freshness probe fails fast (teaching only, never auto-installs)
  instead of letting webpack crash deep on stale `node_modules`; gated so a reusable
  live runtime (reload-in-place) is never blocked.
- **Core actions run under the target checkout's own tsx** — a core perps action
  dynamically imports the checkout's controller TypeScript, so it now runs under tsx
  (resolved from the target first — the published harness ships none; a thin-installed
  slot has it) rather than bare node throwing `ERR_MODULE_NOT_FOUND`; teaches
  `yarn install` / `yarn build` when no tsx is found.
- **Repo-pinned Ruby for iOS pod/gem work** — `yarn setup` / the iOS native build
  activate the checkout's `.ruby-version` (asdf → rbenv → mise → PATH) so a cold
  checkout does not build native gems against the system Ruby; teaches the install
  when the pinned Ruby is unresolved.
- **iOS deep-link dialog pre-approval at provisioning** — the dev-client scheme
  approval is persisted right after app install, so the first headless `openurl` does
  not pause on the "Open in <App>?" dialog (the launch-time write can race).
- **Extension quick-tier webpack tail window** is regression-guarded (Metro parity).

### Internal

- Dependabot: no high/critical advisories remain (existing `resolutions` cover them).
- Boring-library swaps (pidtree, ajv) declined per the "delete hand-rolled code with a
  bug history" bar: the ps/lsof process detection is contract-tested and working, and
  manifest validation is already schema-driven via `@farmslot/protocol`.
- Orchestrator-neutral port leaf: `resolve-farmslot-ports{,-core}.mjs` / `.sh` and
  `src/adapters/resolve-farmslot-ports.ts` renamed to `resolve-slot-ports*`; the
  function `resolveFarmslotPortsByRepo`, the bash function / cli key
  `resolve_farmslot_ports_by_repo`, and the JS export renamed to the `*slot*`
  equivalents. The old filenames, bash function, cli key, and JS export are kept as
  delegating shims/aliases for ONE RELEASE (deprecation window) — the next release
  removes them. The pool-path constants the leaf reads are left as-is (orchestrator
  data location, not a callable surface).

## 0.6.3

### Fixed

- **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.

## 0.6.2 - 2026-07-06

### Added

- **`-v` / `--version`** prints the mm-harness version.

### Fixed

- **`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.
- **`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.

## 0.6.1 - 2026-07-06

Fresh-install fixes found live on a published-0.6.0 install.

### Fixed

- **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.
- **`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.

### Internal

- Reap-checkout-metros test hardened against a SIGTERM timing flake (polls for exit).

## 0.6.0 - 2026-07-06

Minor release: CLI hardening (decomposed, single-parser, dist-only) plus a
fresh-slot validation wave — every fix proven live on untouched -2 slots and
independently cross-reviewed.

### Changed

- **CLI decomposition + dist-only publish** — `src/cli.ts` is parse+dispatch only;
  handlers under `src/commands/<verb>.ts`; one commander parser; the npm tarball
  ships compiled `dist/` without `src/` and `tsx` is a devDependency (plain-node,
  smaller, faster). Source checkouts keep the tsx dev path (`MM_HARNESS_BIN`).
- `verify` auto-ensures its own overlay (no refusal on a fresh checkout); every
  engine execution resolves the slot's ports/device first (call/run/fixtures get
  the same slot isolation launch has).

### Added

- **`stop` reaps leaked Metro bundlers by checkout** (port-agnostic, path-
  boundary matched), `doctor` surfaces any orphan and teaches the reap.
- **`launch --url <dapp>`** (extension) opens the dapp in the main tab beside the
  MetaMask sidepanel; `--stop-only` profile release before seed; stable per-checkout
  Chrome profile + runtime-dist; post-open tab cleanup.
- DX: run-mode + stale-`dist` warning in `bin`; overlay-delegation notice; docs/CODE-MAP.md.

### Fixed

- `launch` targets the slot's own simulator (never the `booted` alias) and boots it;
  slot-port resolution layers rungs so a partial rung never nulls the port; provision
  resolves the simulator from the pool and boots before install.
- `call` works from published installs (dist bridge, no type-strip crash);
  `ensure_unlocked` fails teaching `fixtures set` on an un-onboarded wallet instead
  of vacuously passing (mobile + extension); `provision --json` usage errors emit the
  standard envelope; deps-not-ready teaches a command that works on a bare checkout.

## 0.5.1 - 2026-07-05

Fresh-install hotfixes found in live validation of 0.5.0.

### Fixed

- **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.
- **`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`.
- **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.

## Unreleased

### Fixed

- **`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.

### Changed

- **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).
- **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).
- **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.

## 0.5.0 - 2026-07-05

Minor release: thin Runway provisioning (harness-owned slot provisioning with a shared artifact cache) plus compiled-dist packaging.

### Added

- **`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.
- **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).

### Changed

- 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.

## 0.4.0 - 2026-07-04

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).

### Added

- **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).
- **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.
- **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.
- **`logs` is multi-source per adapter** — sources and the `--source` default come from the adapter's logSources (extension: webpack/watcher/rebuild/app).
- **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.

### Changed

- **BREAKING (agent contract)**: `doctor --json` `runtime.metro` (string) is now `runtime.devServer` `{label, status}` and is absent for core.

### Feature wave recap (0.3.6-0.3.9)

- `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.

## 0.3.9 - 2026-07-04

### Fixed

- **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).
- **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.
- **UTF-8 locale for pod-triggering spawns** — CocoaPods no longer dies on multibyte podspecs under locale-less orchestrator environments.
- **Hermetic contract tests** — tmux/xcrun/adb are stubbed; the Metro log-window opens only in a run-owned session, never an unrelated attached one.

### Added

- **`mm-harness stop`** — stop the port-scoped Metro this checkout owns and close its log-tail window; idempotent; teaching error on extension/core.
- **Extension launch streams live** with an upfront intent line (ports named); mobile and extension long operations are never silent.
- **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.
- **Colored grouped help with DEV OVERRIDE banner and SLOT line**; `mmdev-harness` completions; outcome-stating launch summaries with a `[dev]` marker.
- **docs/UX-PRINCIPLES.md** — the CLI interaction contract (human surface rules; `--json` envelope purity is exempt and inviolable).

### Fixed

- **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.
- **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.
- **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.
- **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`).
- **`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.
- **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.

### Added

- **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.
- **`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.
- **`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.
- **`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.
- **`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`.
- **`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.
- **`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".
- **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.
- **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.

### Changed

- **`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.
- **`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.
- **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.
- **`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.

## 0.3.8 - 2026-07-04

### Fixed

- **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.

## 0.3.7 - 2026-07-04

### Fixed

- **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.

## 0.3.6 - 2026-07-04

### Fixed

- **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.

## 0.3.5 - 2026-07-04

### Fixed

- **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.

### Changed

- **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`.

## 0.3.4 - 2026-07-04

### Fixed

- **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.
- **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`.

## 0.3.3 - 2026-07-04

### Fixed

- **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.
- **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.

## 0.3.2 - 2026-07-04

### Fixed

- **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.
- **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`.

## 0.3.1 - 2026-07-03

### Fixed

- **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.
- **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.
- **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.
- **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.
- **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`).

## 0.3.0 - 2026-07-03

### Changed

- 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).

### Fixed

- Extension `launch` opens the app directly (composes `live.sh`) instead of a retired command.
- Mobile `cleanup` resilient to a stale runner-source path.
- Core run teaches the correct next step when no account is set.
- Resolved CodeQL alerts (insecure temp dir, file-access-to-http).

## 0.2.0 - 2026-07-03

### Added

- **`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.
- **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.
- **`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.

### Changed

- **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.
- **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.)
- **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.
- **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`.
- **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).

### Fixed

- **`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.
- **`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.
- **`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.
- **`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.
- **`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.
- **`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.
- **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`.
- **`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.
- **`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).
- **`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.
- **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.
- **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.
- **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.

### Changed

- **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`.

### Added

- **`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`.
- **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.
- **`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`.
- **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.
- **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`.
- **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.
  - **`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`.
  - **`logs`** — composes the porcelain `logs` (+ `--full`, `--source metro|app`); teaching exit 2 when nothing is running (no `metro.log`/`webpack.log`), pointing at `launch`.
  - **`debug`** — extension `debug` (`--worker` → `debug worker`), mobile `debug` (`--dev-menu` → `dev-menu`); cross-platform flag misuse teaches (exit 2).
  - **`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.
- **`--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.
- **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.
- **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.
- **`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`.
- **`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`).
- **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).
- **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`.
- **`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`.
- **`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`.
- **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.
- **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.

## 0.1.9 - 2026-07-01

### Fixed

- **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`.

## 0.1.8 - 2026-06-30

### Fixed

- **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.
- **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).
- **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.
- **`recipe build` semantics**: `mme-recipe build` runs a full rebuild when no watcher is active; `refresh` stays fast-path only.
- **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`).

### Changed

- **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.
- **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`.
- **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).
- **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.

## 0.1.7 - 2026-06-28

### Fixed

- **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.
- **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.

## 0.1.6 - 2026-06-27

### Added

- **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).

### Fixed

- **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.

## 0.1.5 - 2026-06-26

### Added

- **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).
- **`orchestration/mobile/deps-markers.ts`**: MetaMask Mobile product/native marker tables consumed by harness `deps-readiness`.
- **`mm-recipe decision`**: expose mobile runtime-decision for dry inspection.
- **CI** `.github/workflows/check.yml`: `yarn check`, contract tests (`tests/contract/run.sh`), and shell syntax on every PR/push to `main`.
- **Contract tests** `tests/contract/mobile-runtime-decision.test.sh`, `tests/contract/mobile-mm-recipe-up-install.test.sh`.

### Changed

- **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`.
- **Extension runtime-decision**: `depsCheck` / `recordDepsBaseline` import from `@farmslot/recipe-harness/runtime/deps-readiness` (no local copy).
- **`mm-recipe up`**: runs runtime-decision first (`install` → `yarn setup`, `launch` → Metro + dev client, `ready` → bridge wait). `prewarm_bundle` failures now propagate.
- **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.
- **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.

### Fixed

- **`runtime-decision --record-baseline`**: `--record` on `run` remains the video alias; baseline recording uses `--record-baseline` (or `--record` only on `runtime-decision`).
- **`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.
- **`yarn setup` failures** now propagate from `run_mobile_install`.
- **Stale Metro unresolved errors**: ignore `Unable to resolve` lines superseded by a later successful bundle (`lastOk > lastErr`).
- **Android `rebuild-native`**: `launch_android` honors `MOBILE_PREFLIGHT_MODE` via `ensure_android_app_for_mode` (parity with iOS).
- **Stale native-module log lines**: ignore Nitro HybridObject errors superseded by a later successful bundle.
- **`mm-recipe up --json`**: emit a single final decision JSON document after the invocation completes (suppress interim install/launch reports).
- **Unresolved-module log scoping**: only cite `Unable to resolve` lines after the last successful bundle when classifying current failures.
- **`moduleExistsInNodeModules`**: require `package.json` in the resolved package root (ignore empty `node_modules` dirs).
- **Persistent bundle errors**: repeated identical Metro bundle failures surface `blocked` / `bundle-error-persistent` instead of relaunch loops.
- **`PLATFORM` inference**: `yarn setup` defaults from Android adb env / `RECIPE_HARNESS_PLATFORM`, not hardcoded `ios`.
- **Optional native markers**: when `PLATFORM` is `ios` or `android`, check `ios/Podfile.lock` or `android/gradle.properties` for partial deps.

### Removed

- **`orchestration/lib/deps-readiness.ts`**: moved upstream to `@farmslot/recipe-harness/runtime/deps-readiness` (0.3.0).

### Documentation

- **README**, **CLAUDE.md**, **AGENTS.md**: runtime-readiness ownership boundary (harness vs MetaMask adapter) and changelog/CI expectations for agents.

## 0.1.4 - 2026-06-24

- 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.
- 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.
- Register `activate-repo-node` and `pin-remote-flags` in `orchestration/manifest.json` so `doctor.mjs` tracks the full extension surface.

## 0.1.3 - 2026-06-13

- 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`.
- Improve handling of stale Metro listeners and session management.
- Clarify runner/runtime boundaries and centralize runtime helpers in the runner.
- 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.

## 0.1.2 - 2026-06-10

- Declare `ui.key_press` for Mobile and Extension recipe manifests so trusted keyboard input recipes validate against the runner action manifest.

## 0.1.1 - 2026-06-06

- Harden harness setup so fallback installs are more reliable when the local skill installer is unavailable.
- Configure npm scope/cache settings for reproducible package installs and publishes.
- Prepare pilot npm distribution as `@deeeed/metamask-recipe-runner`; intended to migrate to org ownership if ADR-58 is accepted.
- Add `mm-recipe` and `mme-recipe` human-friendly wrappers for Mobile and Extension recipe control.
- Keep `metamask-recipe` as the single package bin; `mm-recipe` and `mme-recipe` are repo/local convenience wrappers.
- Improve Extension Perps order placement by resolving market price from background market data, stream cache, or visible UI before submitting.
