@deeeed/metamask-harness 0.17.4 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/CHANGELOG.md +775 -0
  2. package/README.md +65 -71
  3. package/adapters/extension/ensure-browser.sh +12 -1
  4. package/adapters/extension/inject.mjs +7 -0
  5. package/adapters/extension/launch-browser.cjs +10 -3
  6. package/adapters/extension/lib/chrome-args.cjs +31 -0
  7. package/adapters/extension/lib/macos-focus.cjs +32 -0
  8. package/adapters/extension/live.sh +10 -20
  9. package/adapters/manifest.json +8 -0
  10. package/adapters/mobile/open-device.sh +45 -7
  11. package/adapters/mobile/verify.sh +15 -3
  12. package/adapters/shared/harness-source-fingerprint.mjs +49 -0
  13. package/adapters/shared/install-repo-deps.sh +1 -5
  14. package/adapters/shared/open-debug.mjs +184 -103
  15. package/adapters/shared/resolve-slot-ports-core.mjs +23 -6
  16. package/adapters/shared/resolve-slot-ports.sh +22 -5
  17. package/bin/mm-harness +15 -3
  18. package/dist/adapters/core/surface.js +4 -1
  19. package/dist/adapters/extension/console-capture.js +3 -1
  20. package/dist/adapters/extension/harness-freshness.js +39 -0
  21. package/dist/adapters/extension/product-config.js +110 -0
  22. package/dist/adapters/extension/runtime-decision.js +20 -71
  23. package/dist/adapters/extension/surface.js +19 -1
  24. package/dist/adapters/mobile/prepare.js +17 -0
  25. package/dist/adapters/mobile/source-freshness.js +26 -41
  26. package/dist/adapters/mobile/surface.js +4 -1
  27. package/dist/adapters/resolve-slot-ports.js +2 -0
  28. package/dist/adapters/slot-ports.js +13 -32
  29. package/dist/adapters.js +50 -17
  30. package/dist/checkout-lock.js +27 -2
  31. package/dist/cli-color.js +19 -0
  32. package/dist/cli-commands.js +1 -1
  33. package/dist/cli.js +2 -3
  34. package/dist/command-contract.js +13 -3
  35. package/dist/commands/call.js +115 -29
  36. package/dist/commands/checklist.js +4 -1
  37. package/dist/commands/completion-candidates.js +20 -13
  38. package/dist/commands/debug.js +31 -38
  39. package/dist/commands/doctor.js +33 -6
  40. package/dist/commands/fixtures.js +65 -17
  41. package/dist/commands/flows.js +39 -10
  42. package/dist/commands/launch/extension.js +40 -15
  43. package/dist/commands/launch/index.js +41 -5
  44. package/dist/commands/list-executables.js +151 -29
  45. package/dist/commands/manifest.js +127 -18
  46. package/dist/commands/parse-args.js +11 -1
  47. package/dist/commands/run-engine.js +384 -56
  48. package/dist/commands/run.js +112 -17
  49. package/dist/commands/shared.js +22 -2
  50. package/dist/commands/status-probe.js +3 -0
  51. package/dist/commands/status.js +1 -0
  52. package/dist/completions-cache.js +1 -1
  53. package/dist/doctor.js +56 -6
  54. package/dist/harness.js +6 -5
  55. package/dist/heal-bounds.js +1 -1
  56. package/dist/live-adapter-contract.js +132 -12
  57. package/dist/manifest.js +161 -1
  58. package/dist/mm-harness-cli.js +13 -7
  59. package/dist/recipe-security.js +178 -0
  60. package/dist/runner.js +87 -13
  61. package/dist/runtime-context.js +8 -26
  62. package/docs/CONTRIBUTING.md +137 -0
  63. package/docs/QA.md +185 -0
  64. package/docs/RECIPES.md +161 -0
  65. package/docs/SECURITY.md +88 -0
  66. package/library/actions/core/perps/read_account.mjs +2 -2
  67. package/library/actions/core/perps/read_orders.mjs +2 -1
  68. package/library/actions/core/perps/read_positions.mjs +2 -1
  69. package/library/actions/core/wallet/list_accounts.mjs +95 -0
  70. package/library/actions/extension/platform/cdp.mjs +1 -0
  71. package/library/actions/extension/wallet/list_accounts.mjs +41 -0
  72. package/library/actions/mobile/platform/bridge.mjs +1 -5
  73. package/library/actions/mobile/wallet/list_accounts.mjs +37 -0
  74. package/library/manifests/core.action-manifest.json +61 -0
  75. package/library/manifests/extension.action-manifest.json +53 -0
  76. package/library/manifests/mobile.action-manifest.json +53 -0
  77. package/library/recipes/runner/action-validation.extension.recipe.json +8 -1
  78. package/library/recipes/runner/action-validation.mobile.recipe.json +8 -1
  79. package/package.json +10 -6
  80. package/scripts/completions.sh +7 -7
  81. package/docs/ADAPTER-SURFACE.md +0 -26
  82. package/docs/CHEATSHEET.md +0 -48
  83. package/docs/CLI-ERGONOMICS-AUDIT.md +0 -32
  84. package/docs/CLI-ERGONOMICS-HUMAN-QA.md +0 -64
  85. package/docs/CODE-MAP.md +0 -62
  86. package/docs/UX-PRINCIPLES.md +0 -66
  87. package/docs/VIDEO-DEMO-VALIDATION.md +0 -74
  88. package/docs/architecture.md +0 -88
  89. package/docs/live-adapter-contract.md +0 -190
  90. package/docs/package-boundaries.md +0 -47
  91. package/docs/perps-flow-catalog.md +0 -235
  92. package/docs/recipe-libraries.md +0 -71
  93. package/docs/runtime-file-conventions.md +0 -36
@@ -0,0 +1,137 @@
1
+ # Contributing
2
+
3
+ Keep `mm-harness` thin: put each change in the layer that owns it and validate
4
+ both the machine contract and the visible human result.
5
+
6
+ ## Ownership
7
+
8
+ ```text
9
+ bin/mm-harness
10
+ -> src/ typed CLI and product decisions
11
+ -> @farmslot/recipe-harness generic execution, UI transports, evidence
12
+ -> @farmslot/protocol schemas
13
+ -> adapters/ focused host/browser/device leaves
14
+ -> library/ MetaMask actions, flows, recipes
15
+ -> MetaMask checkout product under test
16
+ ```
17
+
18
+ | Layer | Owns |
19
+ |---|---|
20
+ | `@farmslot/protocol` | recipe and evidence schemas |
21
+ | `@farmslot/recipe-harness` | generic execution, recovery, traces, artifacts, `ui.*` |
22
+ | `mm-harness` | MetaMask runtime control, diagnostics, durable domain actions |
23
+ | skills/checklists | task workflow and proof expectations |
24
+ | product checkout | app behavior and explicit debug hooks |
25
+
26
+ Generic bootstrap, trust, receipts, and recovery belong upstream. MetaMask
27
+ platform behavior and risk classification belong here. Ticket assertions stay
28
+ task-local.
29
+
30
+ ## Repository map
31
+
32
+ | Path | Responsibility |
33
+ |---|---|
34
+ | `bin/mm-harness` | only public executable |
35
+ | `src/commands/` | command behavior |
36
+ | `src/adapters/` | typed platform decisions and `AdapterSurface` |
37
+ | `adapters/<platform>/` | focused shell/Node host operations |
38
+ | `adapters/<platform>/inject*` | assemble the checkout overlay from shipped adapter/library files |
39
+ | `library/actions/` | executable MetaMask action adapters |
40
+ | `library/flows/` | reusable parameterized behavior |
41
+ | `library/recipes/` | reusable proofs |
42
+ | `library/manifests/` | declared capability surface |
43
+ | `scripts/` | build/validation tooling and shipped completion/human-QA helpers |
44
+
45
+ All TypeScript lives under `src/`. Use `.mjs` for direct no-build Node leaves,
46
+ `.cjs` only for required CommonJS contexts, and shell only for host/device
47
+ commands. Decisions belong in TypeScript; a leaf performs one operation and
48
+ exits.
49
+
50
+ ## Adapter surface
51
+
52
+ Commands resolve shared platform behavior through
53
+ `src/adapters/surface.ts`. Extend `AdapterSurface` only when all platforms need
54
+ the concept, implement all three adapters, and let Core report headless/N/A
55
+ explicitly. Do not add command-local platform ladders for surface-owned behavior.
56
+
57
+ Each checkout owns:
58
+
59
+ ```text
60
+ temp/recipe/runtime/ ports, processes, fixture, logs, runtime identity
61
+ temp/recipe/harness/ installed runtime overlay
62
+ ```
63
+
64
+ Profiles, devices, ports, watchers, and evidence paths must resolve from that
65
+ checkout so slots remain isolated.
66
+
67
+ ## Actions and adapters
68
+
69
+ Official `ui.*` behavior comes from the generic runtime. MetaMask-specific
70
+ actions are manifest-declared modules under:
71
+
72
+ ```text
73
+ library/actions/<platform>/<domain>/<name>.mjs
74
+ library/actions/shared/<domain>/<name>.mjs
75
+ ```
76
+
77
+ Configured libraries use the same structure. An adapter receives a JSON input
78
+ path, writes JSON to its `outputPath` or stdout, returns redacted trace-safe
79
+ output, and performs only the declared operation. It must not execute another
80
+ recipe graph.
81
+
82
+ The input path is argv 1 and `METAMASK_RECIPE_ADAPTER_INPUT`. Its document is:
83
+
84
+ ```json
85
+ {
86
+ "schemaVersion": 1,
87
+ "platform": "mobile|extension|core",
88
+ "action": "metamask.example.action",
89
+ "node": {},
90
+ "context": {
91
+ "nodeId": "step-id",
92
+ "projectRoot": "/path/to/checkout",
93
+ "artifactsDir": "/path/to/run/artifacts"
94
+ },
95
+ "outputPath": "/temporary/output.json"
96
+ }
97
+ ```
98
+
99
+ Write JSON to `outputPath` (also `METAMASK_RECIPE_ADAPTER_OUTPUT`) or stdout.
100
+ Write evidence under `context.artifactsDir` and return relative artifact paths.
101
+ `METAMASK_RECIPE_LIVE_ADAPTER_DIR` is the explicit task-local implementation
102
+ root; configured libraries are the durable sharing mechanism.
103
+
104
+ Add a bundled action only when it is reusable, typed, stable, reduces inference
105
+ or risk, and has a real postcondition. Otherwise use a team library or task-local
106
+ recipe. Follow [Recipes](RECIPES.md) and [Security](SECURITY.md).
107
+
108
+ ## CLI contract
109
+
110
+ - Human mode is contextual, colored only in a TTY, and teaches one exact next
111
+ action on failure.
112
+ - `--json` stdout is one stable document with no decoration.
113
+ - Long work acknowledges intent before blocking and streams child output.
114
+ - Lifecycle commands are checkout-scoped and idempotent.
115
+ - `doctor` is read-only; `doctor --fix` repairs without launching.
116
+ - No command may fabricate platform support or readiness.
117
+
118
+ When adding a command, register its behavior and human metadata, then add a
119
+ contract test. When a flag reaches a host leaf, both the typed composer and leaf
120
+ parser must accept it. Before renaming or removing a public flag, injected file,
121
+ or wrapper, verify real callers and retain compatibility only when one exists.
122
+
123
+ ## Change gates
124
+
125
+ ```bash
126
+ yarn test:unit
127
+ yarn test:coverage
128
+ yarn check
129
+ bash tests/contract/run.sh
130
+ ```
131
+
132
+ Runtime changes also require real affected-platform QA and a packed installation
133
+ test. Update `CHANGELOG.md` under `Unreleased` for user-visible behavior.
134
+
135
+ Docs describe only current behavior. History belongs in the changelog and Git;
136
+ do not add handovers, audits, speculative roadmaps, or duplicate command guides
137
+ under `docs/`.
package/docs/QA.md ADDED
@@ -0,0 +1,185 @@
1
+ # QA
2
+
3
+ Validate the installed candidate as a human would use it. Use disposable
4
+ profiles and an unfunded fixture; preserve existing product source and active
5
+ processes.
6
+
7
+ ## Candidate and provenance
8
+
9
+ ```bash
10
+ export CANDIDATE=/path/to/mm-harness-candidate.tgz
11
+ export QA_HOME="$(mktemp -d "$HOME/mmh-qa.XXXXXX")"
12
+ export QA_PREFIX="$QA_HOME/npm"
13
+ HOME="$QA_HOME" npm install -g --prefix "$QA_PREFIX" "$CANDIDATE"
14
+ export PATH="$QA_PREFIX/bin:$PATH"
15
+ mm-harness --version
16
+ ```
17
+
18
+ - [ ] The executable and dependencies resolve inside the isolated prefix.
19
+ - [ ] No dependency is a symlink or resolves through a local Farmslot checkout.
20
+ - [ ] Record the tarball SHA-256 and product SHAs.
21
+ - [ ] Product trees start and finish without tracked changes.
22
+
23
+ ## Common
24
+
25
+ Run inside each Extension, Mobile, and Core checkout:
26
+
27
+ ```bash
28
+ mm-harness doctor
29
+ mm-harness doctor --json
30
+ mm-harness status
31
+ mm-harness actions
32
+ mm-harness flows
33
+ mm-harness run --list
34
+ mm-harness run runner.smoke --describe
35
+ mm-harness run runner.smoke --plan
36
+ mm-harness run runner.smoke
37
+ mm-harness last --json
38
+ ```
39
+
40
+ - [ ] Product, checkout, slot, ports, and next action are correct.
41
+ - [ ] Human output is concise and evidence paths are absolute.
42
+ - [ ] JSON output is one parseable document without color or prose on stdout.
43
+ - [ ] `run` chooses a checkout-local artifact directory; an explicit
44
+ `--artifacts-dir` still wins.
45
+ - [ ] Reports, trace, diagnostics, executed recipe, and manifest exist and agree.
46
+ - [ ] `last --json` reconstructs the run without re-executing it.
47
+
48
+ Recovery checks:
49
+
50
+ ```bash
51
+ mm-harness actions --wat
52
+ mm-harness run missing.recipe --describe
53
+ mm-harness doctor --fix
54
+ mm-harness verify
55
+ ```
56
+
57
+ - [ ] Bad input fails before runtime work with a stable code and one exact next
58
+ action.
59
+ - [ ] `doctor --fix` is idempotent and does not choose a fixture or launch.
60
+ - [ ] With `capture-helper` absent, Extension reports CDP screenshots and Mobile
61
+ reports `simctl`/ADB screenshots; video is honestly unavailable.
62
+
63
+ Run the bounded multi-platform preflight when all three checkouts are available:
64
+
65
+ ```bash
66
+ yarn qa:human \
67
+ --extension /path/to/metamask-extension \
68
+ --mobile /path/to/metamask-mobile \
69
+ --core /path/to/metamask-core \
70
+ --artifacts-dir temp/human-qa
71
+ ```
72
+
73
+ It supplements the visible checks below; it does not replace them.
74
+
75
+ ## Extension
76
+
77
+ Fresh MetaMask Extension needs its normal product configuration and
78
+ checkout-pinned Chromium. Follow the product files; do not invent values:
79
+
80
+ ```bash
81
+ cd /path/to/metamask-extension
82
+ cp .metamaskrc.dist .metamaskrc
83
+ ${EDITOR:-vi} .metamaskrc
84
+ yarn playwright install chromium
85
+ mm-harness launch --verify
86
+ mm-harness fixtures set
87
+ mm-harness run wallet.smoke
88
+ mm-harness logs --source extension
89
+ mm-harness logs --source dapp
90
+ mm-harness logs --source webpack
91
+ mm-harness debug
92
+ ```
93
+
94
+ - [ ] Missing/placeholder Infura configuration returns
95
+ `EXTENSION_PRODUCT_CONFIG_REQUIRED` before build or browser startup.
96
+ - [ ] Missing Chromium returns `EXTENSION_BROWSER_REQUIRED` without using system
97
+ Chrome.
98
+ - [ ] Launch uses the checkout-owned profile, opens one MetaMask surface, avoids
99
+ keychain prompts, and preserves keyboard focus by default.
100
+ - [ ] A second launch reuses the watcher/profile without duplicate tabs.
101
+ - [ ] Two source edits rebuild incrementally; refresh shows each change; revert
102
+ restores a clean tree.
103
+ - [ ] Log sources are distinct.
104
+ - [ ] `debug` opens connected DevTools for the active Extension surface;
105
+ `location.href` returns its `chrome-extension://` URL and remains connected.
106
+
107
+ ## Mobile
108
+
109
+ ```bash
110
+ cd /path/to/metamask-mobile
111
+ mm-harness provision runway ios
112
+ mm-harness launch ios --verify
113
+ mm-harness fixtures set
114
+ mm-harness call metamask.wallet.ensure_unlocked
115
+ mm-harness run wallet.smoke
116
+ mm-harness logs --source app
117
+ mm-harness logs --source metro
118
+ mm-harness debug
119
+ ```
120
+
121
+ - [ ] Simulator opens only when absent and does not steal focus by default.
122
+ - [ ] A second launch reuses the running simulator and Metro.
123
+ - [ ] Fixture setup and unlock show the intended redacted account without
124
+ mnemonic/private-key output.
125
+ - [ ] Status matches verified product routes: `Login`/`LockScreen` are locked;
126
+ `WalletView` is unlocked.
127
+ - [ ] App and Metro logs are separate.
128
+ - [ ] A JS edit rebuilds through Metro and appears after reload without a native
129
+ rebuild; revert restores a clean tree.
130
+
131
+ When Android/ADB is available, repeat with:
132
+
133
+ ```bash
134
+ mm-harness launch android --device <serial> --verify
135
+ ```
136
+
137
+ ## Core
138
+
139
+ ```bash
140
+ cd /path/to/metamask-core
141
+ mm-harness doctor
142
+ mm-harness call metamask.wallet.list_accounts scope=selected
143
+ mm-harness run runner.smoke
144
+ mm-harness logs
145
+ mm-harness debug
146
+ ```
147
+
148
+ - [ ] Doctor reports a headless controller surface and no capture requirement.
149
+ - [ ] Account output is fixture-backed and redacted.
150
+ - [ ] Smoke writes the complete evidence package without launching an app.
151
+ - [ ] Logs/debug fail with truthful headless teaching errors.
152
+
153
+ ## Trust and evidence
154
+
155
+ - [ ] An untrusted restricted plan fails with `RECIPE_TRUST_REQUIRED` and no
156
+ side effect.
157
+ - [ ] A wrong/stale digest fails with `RECIPE_APPROVAL_MISMATCH`.
158
+ - [ ] Only the unchanged, reviewed plan executes with its exact approval digest.
159
+ - [ ] Human output, JSON, reports, traces, diagnostics, and screenshots contain
160
+ no mnemonic, private key, seed, bearer token, or API credential.
161
+ - [ ] Diagnostics contain only events observed in the proof window.
162
+ - [ ] Screenshot provenance identifies capture-helper, CDP, simctl, or ADB
163
+ accurately.
164
+
165
+ ## Automated and release gates
166
+
167
+ ```bash
168
+ yarn check
169
+ bash tests/contract/run.sh
170
+ ```
171
+
172
+ Before release:
173
+
174
+ - [ ] Focused unit tests and full contract suite pass.
175
+ - [ ] Packed isolated installation passes the critical Extension, Mobile, and
176
+ Core scenarios.
177
+ - [ ] Independent review approves the exact final diff.
178
+ - [ ] Known limits are explicit: Extension requires product Infura setup;
179
+ Mobile requires its normal dev-client/device setup; video requires optional
180
+ `capture-helper`; the harness never invents funded fixtures.
181
+ - [ ] Existing Farmslot slots remain compatible.
182
+
183
+ When a slot manager is available, repeat `runner.smoke` in one existing managed
184
+ checkout per product and verify its profile/device, ports, processes, and fixture
185
+ remain isolated.
@@ -0,0 +1,161 @@
1
+ # Recipes
2
+
3
+ Use the smallest durable layer that proves the task:
4
+
5
+ 1. bundled actions;
6
+ 2. a bundled or team flow;
7
+ 3. a task-local recipe;
8
+ 4. a trusted task-local action or direct controller/CDP operation when no
9
+ stable action exists.
10
+
11
+ Search before writing code:
12
+
13
+ ```bash
14
+ mm-harness actions <intent>
15
+ mm-harness actions --action <name>
16
+ mm-harness flows
17
+ mm-harness flows describe <ref> --json
18
+ mm-harness run --list
19
+ mm-harness run <name> --describe
20
+ ```
21
+
22
+ Discovery reports the winning source, fields, examples, and shadowed sources.
23
+ Use `--json` when an agent needs exact metadata.
24
+
25
+ ## Compose a proof
26
+
27
+ An action performs one operation. A flow packages reusable product setup or
28
+ behavior. A recipe owns the task's acceptance criteria and evidence.
29
+
30
+ Minimal recipe:
31
+
32
+ ```json
33
+ {
34
+ "schema_version": 1,
35
+ "title": "Wallet readiness proof",
36
+ "description": "Proves the running wallet is reachable and unlocked.",
37
+ "validate": {
38
+ "workflow": {
39
+ "entry": "unlock",
40
+ "nodes": {
41
+ "unlock": {
42
+ "action": "metamask.wallet.ensure_unlocked",
43
+ "intent": "Ensure the fixture-backed wallet is unlocked",
44
+ "next": "done"
45
+ },
46
+ "done": { "action": "end", "status": "pass" }
47
+ }
48
+ }
49
+ }
50
+ }
51
+ ```
52
+
53
+ Validate without side effects, then run:
54
+
55
+ ```bash
56
+ mm-harness run recipe.json --plan
57
+ mm-harness run recipe.json
58
+ ```
59
+
60
+ To reuse an existing Extension build on a specific CDP runtime:
61
+
62
+ ```bash
63
+ mm-harness run recipe.json --cdp-port 9500 --launch-existing-dist
64
+ ```
65
+
66
+ Rules:
67
+
68
+ - Use real product paths; never mutate DOM, Redux, controller, or persisted
69
+ state to fabricate proof.
70
+ - Setup may use supported APIs, but a read/assert or visible UI postcondition
71
+ must prove the result.
72
+ - Use `ui.*` for human-visible behavior. Keep ticket copy, styling, and layout
73
+ claims in the task recipe and screenshots.
74
+ - Parameterize repeated behavior instead of multiplying action names.
75
+ - Every `ensure_*` flow must prove a postcondition.
76
+ - Keep secrets out of recipes, libraries, and evidence.
77
+
78
+ The Recipe Protocol v1 schema is authoritative:
79
+ <https://farmslot.io/docs/reference/recipe-protocol-v1>.
80
+
81
+ ## Share reusable behavior
82
+
83
+ A library may contain manifests, action implementations, flows, and complete
84
+ recipes:
85
+
86
+ ```text
87
+ team-recipes/
88
+ library.json
89
+ manifests/extension.action-manifest.json
90
+ actions/extension/wallet/ensure_ready.mjs
91
+ flows/wallet.flows.json
92
+ recipes/onboarding/smoke.extension.recipe.json
93
+ ```
94
+
95
+ Minimal `library.json`:
96
+
97
+ ```json
98
+ {
99
+ "kind": "recipe-library",
100
+ "schema_version": 1,
101
+ "name": "wallet-team",
102
+ "owner": "wallet-team"
103
+ }
104
+ ```
105
+
106
+ Enable it:
107
+
108
+ ```bash
109
+ export RECIPE_LIBRARY_PATH="wallet=$HOME/shared-library/wallet-team"
110
+ mm-harness flows
111
+ mm-harness run --list
112
+ mm-harness run onboarding.smoke --describe
113
+ mm-harness run onboarding.smoke --plan
114
+ ```
115
+
116
+ Use `--library wallet=/path/to/library` for one command. Without an explicit
117
+ library, the personal library is `$FARMSLOT_HOME/recipe-library` (default
118
+ `~/.farmslot/recipe-library`). Resolution is task/local configuration first,
119
+ then bundled MetaMask; runs snapshot resolved definitions so shadowing is
120
+ reviewable.
121
+
122
+ Promote a repeated flow only after it has a useful description, typed params,
123
+ and successful proof:
124
+
125
+ ```bash
126
+ mm-harness flows promote --from recipe.json --flow wallet.ensure_ready --run artifacts
127
+ ```
128
+
129
+ ## Direct product access
130
+
131
+ The bundled catalog is intentionally small. A task may use a real controller or
132
+ CDP path when that is clearer than adding a permanent wrapper.
133
+
134
+ Before doing so, validate the path in current product source and record:
135
+
136
+ - the controller/API and supported parameters;
137
+ - whether it reads or mutates state;
138
+ - required preconditions;
139
+ - the independent postcondition;
140
+ - trace redaction.
141
+
142
+ Do not infer route or controller names from memory. A direct mutation still
143
+ requires a separate read/assert or visible UI proof. If the operation repeats
144
+ across tasks, move it to a reviewed team library; promote it into the bundled
145
+ catalog only when it is broadly reusable and stable across supported products.
146
+
147
+ ## Cross-platform expectations
148
+
149
+ Mobile and Extension use the same wallet and Perps vocabulary where both expose
150
+ the behavior. Core is headless: it reports fixture-backed controller inputs and
151
+ does not pretend to have a selected UI account or app route.
152
+
153
+ Inspect the current capability instead of assuming parity:
154
+
155
+ ```bash
156
+ mm-harness actions --category wallet --json
157
+ mm-harness actions --category perps --json
158
+ mm-harness actions --action metamask.wallet.list_accounts --json
159
+ ```
160
+
161
+ See [Security](SECURITY.md) before enabling custom executable actions.
@@ -0,0 +1,88 @@
1
+ # Security
2
+
3
+ `mm-harness` executes product code, recipes, and optional custom adapters with
4
+ the current user's authority. It is a proof tool, not an OS sandbox.
5
+
6
+ ## Trust boundary
7
+
8
+ - Bundled actions and recipes are trusted package content.
9
+ - A configured library cannot declare itself trusted.
10
+ - Untrusted plans that request restricted capabilities stop before runtime
11
+ preparation or side effects with `RECIPE_TRUST_REQUIRED`.
12
+ - The failure reports blocked nodes, capabilities, source provenance, and one
13
+ exact approval command.
14
+ - Approval is bound to the resolved plan, implementation bundle, project root,
15
+ artifact directory, and execution environment.
16
+ - Changed code or inputs produce `RECIPE_APPROVAL_MISMATCH`; an old digest does
17
+ not authorize a new plan.
18
+
19
+ Never construct an approval from memory. Inspect the plan and use the exact
20
+ `userAction` emitted by the failed run. Review custom libraries like application
21
+ code because approved code retains the user's filesystem, process, and network
22
+ permissions.
23
+
24
+ ## Product-state integrity
25
+
26
+ Proof must come from supported UI, controller, or API paths. Never write DOM,
27
+ React/Redux/MobX state, controller internals, or storage merely to manufacture
28
+ the claimed result.
29
+
30
+ Fixture setup is allowed before the proof window when it is explicit in the
31
+ trace. Mutating setup must finish with a real read/assert or visible UI
32
+ postcondition.
33
+
34
+ ## Wallet fixtures
35
+
36
+ Each checkout has one canonical fixture:
37
+
38
+ ```text
39
+ temp/recipe/runtime/wallet-fixture.json
40
+ ```
41
+
42
+ Initialize it explicitly from an approved fixture or choose the disposable
43
+ `--dev` option shown in the [README](../README.md). Never fund a dev fixture.
44
+
45
+ - Never commit a fixture containing a mnemonic or private key.
46
+ - Never print, paste, or record mnemonic/private-key material.
47
+ - Use funded fixtures only in the intended isolated environment.
48
+ - `fixtures set` reads the password from the fixture; do not put it on the
49
+ command line.
50
+ - Human and JSON outputs may identify redacted accounts but must not expose
51
+ secret material.
52
+
53
+ ## Evidence and journals
54
+
55
+ Recipe output may include application logs, screenshots, traces, and custom
56
+ action results. Before sharing it:
57
+
58
+ - verify the artifact manifest contains only intended files;
59
+ - search human output, JSON, traces, diagnostics, and reports for credentials;
60
+ - confirm screenshots use the declared provider and show no sensitive UI;
61
+ - keep task-local artifacts out of source control unless intentionally reviewed.
62
+
63
+ `mm-harness last --json` stores a redacted resumability record. It is not a
64
+ secret store.
65
+
66
+ ## Runtime isolation
67
+
68
+ Extension uses a checkout-owned Chrome profile and CDP port. Mobile resolves a
69
+ checkout-owned simulator/device and Metro port. Core is headless. Parallel
70
+ checkouts must not share profiles, runtime directories, process markers, or
71
+ evidence directories.
72
+
73
+ On macOS, Extension Chromium uses a mock keychain so an isolated test profile
74
+ does not touch the operator's login keychain. Product configuration and wallet
75
+ fixtures remain user-owned; the harness detects missing prerequisites but never
76
+ invents them.
77
+
78
+ ## Release checks
79
+
80
+ Security-sensitive changes require:
81
+
82
+ 1. trust-boundary tests proving rejection occurs before side effects;
83
+ 2. exact-digest success and stale-digest rejection;
84
+ 3. redaction tests for arguments, results, journals, and evidence;
85
+ 4. packed-install provenance with no local links;
86
+ 5. independent review of the exact diff.
87
+
88
+ Use [QA](QA.md) for the runnable checklist.
@@ -1,4 +1,4 @@
1
- import { getCoreController, runAdapter } from './_controller.mjs';
1
+ import { getCoreController, isDirectRun, runAdapter } from './_controller.mjs';
2
2
 
3
3
  function redactAccount(account) {
4
4
  return {
@@ -27,4 +27,4 @@ export async function readAccount(input) {
27
27
  };
28
28
  }
29
29
 
30
- runAdapter(readAccount);
30
+ if (isDirectRun(import.meta.url)) runAdapter(readAccount);
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  getCoreController,
3
+ isDirectRun,
3
4
  redactOrder,
4
5
  runAdapter,
5
6
  selectedItems,
@@ -24,4 +25,4 @@ export async function readOrders(input) {
24
25
  };
25
26
  }
26
27
 
27
- runAdapter(readOrders);
28
+ if (isDirectRun(import.meta.url)) runAdapter(readOrders);
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  getCoreController,
3
+ isDirectRun,
3
4
  redactPosition,
4
5
  runAdapter,
5
6
  selectedItems,
@@ -24,4 +25,4 @@ export async function readPositions(input) {
24
25
  };
25
26
  }
26
27
 
27
- runAdapter(readPositions);
28
+ if (isDirectRun(import.meta.url)) runAdapter(readPositions);