@deeeed/metamask-harness 0.92.19 → 0.92.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/README.md +7 -5
- package/dist/cli-commands.js +2 -1
- package/dist/command-contract.js +35 -3
- package/dist/commands/pr-body.js +29 -0
- package/dist/mm-harness-cli.js +3 -3
- package/docs/CONTRIBUTING.md +1 -1
- package/docs/cli-contract.md +2 -1
- package/package.json +9 -9
- package/dist/commands/runtime-health.js +0 -19
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.92.21 - 2026-10-17
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Restore `mm-harness pr-body render` as a thin command over the shared Farmslot renderer.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- Pin the `@farmslot` packages to the release set with `@farmslot/recipe-cli` 0.23.0 (protocol 0.54.0, recipe-runner 0.39.0, adapter-sdk 0.18.0, agent-runtime 0.29.0, adapter-web 0.10.14, adapter-node 0.1.34, adapter-rn 0.22.23).
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- `pr-body render` takes the Validation Recipe from the selected attempt's captured `recipe.json` (or the task's single retained package), not the editable `artifacts/recipe.json`, and names the run, runner, runner revision and recipe digest above its log.
|
|
18
|
+
- `pr-body render` refuses a selected attempt that fails artifact validation or lacks its recipe or report, and refuses two candidate packages when no attempt is selected; it writes nothing in either case.
|
|
19
|
+
- `pr-body render --out` refuses to overwrite the authored description, recipe, workflow, attempt selection or retained evidence.
|
|
20
|
+
- `pr-body render` refuses symlinked or oversized (over 4 MiB) inputs, converts CRLF in fenced logs to LF, and creates new output files owner-only without following a symlink.
|
|
21
|
+
|
|
22
|
+
## 0.92.20 - 2026-10-17
|
|
23
|
+
|
|
24
|
+
### Removed
|
|
25
|
+
|
|
26
|
+
- Retire the unused `runtime-health` hidden command and the `health` and `home` aliases of `status`. Extension health checks keep running inside `ensure-ready`, `runtime-decision` and launch verification.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- Document that hosted tasks resolve the Recipe Cook and Perps checklist roots from each node's pool `env`.
|
|
31
|
+
|
|
5
32
|
## 0.92.19 - 2026-10-17
|
|
6
33
|
|
|
7
34
|
### Fixed
|
package/README.md
CHANGED
|
@@ -400,16 +400,16 @@ check for upstream updates. Keep the selected library unchanged during a run.
|
|
|
400
400
|
```bash
|
|
401
401
|
# Write artifacts/pr-description.md in the repository PR template shape (prose
|
|
402
402
|
# only), then render the publishable body.
|
|
403
|
-
|
|
404
|
-
--out temp/tasks/dev/TAT-1/artifacts/pr-body.md \
|
|
405
|
-
--command 'farmslot recipe run artifacts/recipe.json'
|
|
403
|
+
mm-harness pr-body render temp/tasks/dev/TAT-1 --command 'mm-harness run artifacts/recipe.json'
|
|
406
404
|
```
|
|
407
405
|
|
|
408
|
-
|
|
406
|
+
`pr-body render` writes `artifacts/pr-body.md`: the description with the "Validation
|
|
409
407
|
Recipe" and "Validation Logs" sections rendered from `artifacts/recipe.json` and
|
|
410
408
|
`artifacts/recipe-run/report.md` (and "Recipe Workflow" from `artifacts/workflow.mmd`
|
|
411
409
|
when it exists), replacing any pasted by hand. The worker never pastes artifacts
|
|
412
|
-
into markdown, so fences cannot break.
|
|
410
|
+
into markdown, so fences cannot break. When a passing run recorded the task's
|
|
411
|
+
selected attempt, both sections come from that attempt's validated package; a package
|
|
412
|
+
that fails validation is refused, and nothing is written.
|
|
413
413
|
|
|
414
414
|
## Review a change
|
|
415
415
|
|
|
@@ -473,6 +473,8 @@ Farmslot selects that same package through `recipe.provider.root.env`, configure
|
|
|
473
473
|
|
|
474
474
|
Explicitly invoke `mms-recipe-dev`, `mms-recipe-fix-bug`, `mms-recipe-qa` or the team's static review skill. `mms-recipe-cook` selects or resumes their shared Markdown checklist. Matching descriptions or changed paths never activates these workflows.
|
|
475
475
|
|
|
476
|
+
Hosted tasks select that checklist on the execution node, not the gateway. The Core, Mobile and Extension packs root their Recipe Cook checklists at `CONSENSYS_RECIPE_COOK_ROOT` and their Perps checklists at `PERPS_RECIPE_LIBRARY_ROOT`. Set them in every node's pool `env`, not only the gateway's. A node missing the root for the selected checklist refuses the task as source unavailable.
|
|
477
|
+
|
|
476
478
|
Static Perps review uses a generated source checklist without installing or launching the app harness. `recipe-qa` freezes a PR, range or daily change window and requires runtime smoke plus selected recipe proof. Official release validation uses its named release profile and proof lane.
|
|
477
479
|
|
|
478
480
|
TASK.md holds the request; CHECKLIST.md is the sole step list. The frozen terminal contract and task-local mark validate completion. Publication, retained sessions and workspace cleanup belong to the caller. Farmslot handles them through its control plane; direct invocation returns the outcome to the engineer. See the [task-directory guide](site/task-dir.html) and [QA tutorial](site/tutorials/v5.html).
|
package/dist/cli-commands.js
CHANGED
|
@@ -11,10 +11,11 @@ const SPEC = {
|
|
|
11
11
|
{ name: "help", desc: "Load version-matched recipe guidance (help review composes the review guide)", args: ["review"], flags: ["--json", "--adapter", "--target", "--domain"] },
|
|
12
12
|
{ name: "review", desc: "Materialize a review checklist composed from the base review and a team library", args: ["checklist"], flags: ["--domain", "--since", "--base", "--out", "--adapter", "--target", "--json"] },
|
|
13
13
|
{ name: "domain", desc: "Which team library owns the change (declared value, else owned-paths.json)", flags: ["--domain", "--base", "--adapter", "--target", "--json"] },
|
|
14
|
+
{ name: "pr-body", desc: "Render the publishable PR body: pr-description.md plus the recipe and run sections built from artifacts", args: ["render", "<task-dir>"], flags: ["--command", "--out", "--json"] },
|
|
14
15
|
{ name: "config", desc: "Per-engineer locations and optional advisor settings", args: ["list", "path", "get", "set", "unset"], flags: ["--json"] },
|
|
15
16
|
{ name: "tutorial", desc: "Open the visual recipe tutorial", flags: ["--json", "--no-open"] },
|
|
16
17
|
{ name: "setup-base", desc: "Bootstrap numbered product checkouts", flags: ["--dir", "--counts", "--only", "--dry-run", "--force", "--json", "--show-config", "--reset-config", "--skip-harness-update"] },
|
|
17
|
-
{ name: "status",
|
|
18
|
+
{ name: "status", desc: "Home status + next commands", flags: ["--json", "--fast", "--watch", "--task"] },
|
|
18
19
|
{ name: "ports", desc: "Slot ports and runtime paths", flags: ["--json"] },
|
|
19
20
|
{ name: "up", desc: "Decide + run minimum work to reach ready", flags: ["--json", "--dry-run"] },
|
|
20
21
|
{ name: "sync", desc: "Refresh harness + canonicalize wallet fixture", flags: ["--json"] },
|
package/dist/command-contract.js
CHANGED
|
@@ -175,6 +175,40 @@ Example:
|
|
|
175
175
|
})
|
|
176
176
|
}
|
|
177
177
|
},
|
|
178
|
+
{
|
|
179
|
+
name: "pr-body",
|
|
180
|
+
summary: "Render the publishable PR body: the authored description plus the recipe and run log sections built from artifacts.",
|
|
181
|
+
example: "mm-harness pr-body render temp/tasks/dev/TAT-1",
|
|
182
|
+
helpText: `mm-harness pr-body render <task-dir> [flags]
|
|
183
|
+
|
|
184
|
+
Reads <task-dir>/artifacts/pr-description.md (the PR body the worker wrote in
|
|
185
|
+
the repository PR template shape) and inserts the machine sections rendered
|
|
186
|
+
from artifacts: "Validation Recipe" from artifacts/recipe.json, "Validation
|
|
187
|
+
Logs" from artifacts/recipe-run/report.md, and "Recipe Workflow" from
|
|
188
|
+
artifacts/workflow.mmd when it exists. Any such section the worker pasted by
|
|
189
|
+
hand is replaced. The sections go before the first checklist section, else at
|
|
190
|
+
the end. Fences are always longer than any backtick run in the content and
|
|
191
|
+
close on their own line. Writes <task-dir>/artifacts/pr-body.md.
|
|
192
|
+
|
|
193
|
+
When a passing run recorded the task's selected attempt, both sections come
|
|
194
|
+
from that attempt's package, which must pass validation or nothing is written.
|
|
195
|
+
|
|
196
|
+
--command <text> Recipe invocation to show above the run log
|
|
197
|
+
--out <file> Write elsewhere than artifacts/pr-body.md (never over an
|
|
198
|
+
authored input or retained evidence)
|
|
199
|
+
--json Machine-readable result
|
|
200
|
+
|
|
201
|
+
Example:
|
|
202
|
+
mm-harness pr-body render temp/tasks/dev/TAT-1 --json`,
|
|
203
|
+
contract: {
|
|
204
|
+
options: options(HELP, JSON, {
|
|
205
|
+
"--command": value(),
|
|
206
|
+
"--out": value()
|
|
207
|
+
}),
|
|
208
|
+
positionals: [{ label: "action", choices: ["render"] }, { label: "task-dir" }],
|
|
209
|
+
minimumPositionals: 2
|
|
210
|
+
}
|
|
211
|
+
},
|
|
178
212
|
{
|
|
179
213
|
name: "config",
|
|
180
214
|
summary: "Per-engineer locations and optional recipe advisor settings.",
|
|
@@ -258,13 +292,12 @@ Example:
|
|
|
258
292
|
},
|
|
259
293
|
{
|
|
260
294
|
name: "status",
|
|
261
|
-
aliases: ["health", "home"],
|
|
262
295
|
summary: "Home dashboard for a checkout: detected adapter, next command, and (mobile) connected devices with the selected target.",
|
|
263
296
|
example: "mm-harness status",
|
|
264
297
|
helpText: `mm-harness status [flags]
|
|
265
298
|
|
|
266
299
|
Home dashboard for the current checkout \u2014 the detected adapter, the next command
|
|
267
|
-
to run, and (mobile only) the connected devices with live app state.
|
|
300
|
+
to run, and (mobile only) the connected devices with live app state.
|
|
268
301
|
|
|
269
302
|
--adapter <{adapters}> Target adapter (auto-detected inside a checkout)
|
|
270
303
|
--target <path> Checkout path (default: cwd)
|
|
@@ -1409,7 +1442,6 @@ Example:
|
|
|
1409
1442
|
];
|
|
1410
1443
|
const HIDDEN_COMMANDS = [
|
|
1411
1444
|
"completion-candidates",
|
|
1412
|
-
"runtime-health",
|
|
1413
1445
|
"runtime-decision",
|
|
1414
1446
|
"resolve-extension",
|
|
1415
1447
|
"ensure-ready",
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { optionFlag, optionString, parseArgs, renderRetainedArtifactsMarkdown, usageError } from "@farmslot/recipe-cli/harness";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
const USAGE = "Usage: mm-harness pr-body render <task-dir> [--command <text>] [--out <file>] [--json]";
|
|
5
|
+
async function handlePrBody(argv) {
|
|
6
|
+
const parsed = parseArgs(argv);
|
|
7
|
+
const [action, taskArg] = parsed.positional;
|
|
8
|
+
if (action !== "render" || !taskArg) throw usageError(USAGE);
|
|
9
|
+
const taskDir = path.resolve(taskArg);
|
|
10
|
+
if (!fs.existsSync(path.join(taskDir, "artifacts"))) {
|
|
11
|
+
throw usageError(`${taskDir} has no artifacts/ directory; pass the task directory mm-harness task init wrote.`);
|
|
12
|
+
}
|
|
13
|
+
const command = optionString(parsed.options, "command");
|
|
14
|
+
const out = path.resolve(optionString(parsed.options, "out") ?? path.join(taskDir, "artifacts", "pr-body.md"));
|
|
15
|
+
const { result } = await renderRetainedArtifactsMarkdown({ directory: taskDir, command, out, format: "markdown" });
|
|
16
|
+
const { sections = [], recipe, run, workflow } = result;
|
|
17
|
+
if (optionFlag(parsed.options, "json")) {
|
|
18
|
+
process.stdout.write(`${JSON.stringify({ command: "pr-body", subcommand: "render", status: "ok", taskDir, out, sections, recipe, run, workflow }, null, 2)}
|
|
19
|
+
`);
|
|
20
|
+
} else {
|
|
21
|
+
process.stdout.write(`pr body: ${out}
|
|
22
|
+
${sections.join("\n ")}
|
|
23
|
+
`);
|
|
24
|
+
}
|
|
25
|
+
return 0;
|
|
26
|
+
}
|
|
27
|
+
export {
|
|
28
|
+
handlePrBody
|
|
29
|
+
};
|
package/dist/mm-harness-cli.js
CHANGED
|
@@ -48,13 +48,13 @@ import { handleHelp } from "./commands/help.js";
|
|
|
48
48
|
import { handleLogs } from "./commands/logs.js";
|
|
49
49
|
import { overlayCommandOptions } from "./commands/overlay.js";
|
|
50
50
|
import { handlePin } from "./commands/pin.js";
|
|
51
|
+
import { handlePrBody } from "./commands/pr-body.js";
|
|
51
52
|
import { handlePrepare } from "./commands/prepare.js";
|
|
52
53
|
import { handleProvision } from "./commands/provision.js";
|
|
53
54
|
import { handleRecipeQuality } from "./commands/recipe-quality.js";
|
|
54
55
|
import { handleResolveExtension } from "./commands/resolve-extension.js";
|
|
55
56
|
import { handleReview } from "./commands/review.js";
|
|
56
57
|
import { handleRuntimeDecision } from "./commands/runtime-decision.js";
|
|
57
|
-
import { handleRuntimeHealth } from "./commands/runtime-health.js";
|
|
58
58
|
import { handleRuntimeLaunch } from "./commands/runtime-launch.js";
|
|
59
59
|
import { handleSelfTest } from "./commands/self-test.js";
|
|
60
60
|
import { handleSetupBase } from "./commands/setup-base.js";
|
|
@@ -139,6 +139,7 @@ const PUBLIC_RUN = {
|
|
|
139
139
|
help: { run: (args) => handleHelp(args, { packageRoot, harnessVersion: hostVersion() }), nudge: false },
|
|
140
140
|
review: { run: handleReview },
|
|
141
141
|
domain: { run: handleDomain },
|
|
142
|
+
"pr-body": { run: handlePrBody },
|
|
142
143
|
config: { run: handleConfig },
|
|
143
144
|
tutorial: { run: (args) => handleTutorial(args, { harnessVersion: hostVersion() }), exit: "now", nudge: false },
|
|
144
145
|
"setup-base": { run: handleSetupBase, raw: true, exit: "now", nudge: false },
|
|
@@ -173,7 +174,6 @@ const PUBLIC_RUN = {
|
|
|
173
174
|
const HIDDEN_RUN = {
|
|
174
175
|
// Shell completion answers for the checkout's adapter, so it resolves one.
|
|
175
176
|
"completion-candidates": { run: handleCompletionCandidates, nudge: false, context: "adapter" },
|
|
176
|
-
"runtime-health": { run: (args) => handleRuntimeHealth(parseArgs(args)) },
|
|
177
177
|
"runtime-decision": { run: (args) => handleRuntimeDecision(parseArgs(args, { recordIsFlag: true })) },
|
|
178
178
|
"resolve-extension": { run: (args) => handleResolveExtension(parseArgs(args)) },
|
|
179
179
|
"ensure-ready": { run: (args) => handleEnsureReady(parseArgs(args)) },
|
|
@@ -198,7 +198,7 @@ const HELP_GROUPS = [
|
|
|
198
198
|
{
|
|
199
199
|
title: "PROVE",
|
|
200
200
|
blurb: "run recipes and inspect readiness",
|
|
201
|
-
commands: ["run", "last", "doctor", "check", "checklist", "review", "recipe-quality"]
|
|
201
|
+
commands: ["run", "last", "doctor", "check", "checklist", "review", "pr-body", "recipe-quality"]
|
|
202
202
|
},
|
|
203
203
|
{
|
|
204
204
|
title: "RUNTIME OVERLAY",
|
package/docs/CONTRIBUTING.md
CHANGED
|
@@ -92,7 +92,7 @@ command-local platform ladders for surface-owned behavior:
|
|
|
92
92
|
`prepare`, `debug`, `reload` and `runtime-decision` read their own members.
|
|
93
93
|
|
|
94
94
|
A few comparisons remain where a command exists for one platform
|
|
95
|
-
(`
|
|
95
|
+
(`resolve-extension`, `ensure-ready`, `runtime-launch`) or
|
|
96
96
|
code compares a platform's own data (`review/knowledge.ts`, the Core jest
|
|
97
97
|
grouping in `check.ts`, the Mobile release-artifact state file).
|
|
98
98
|
|
package/docs/cli-contract.md
CHANGED
|
@@ -62,7 +62,8 @@ Callers scanned (2026-10-02):
|
|
|
62
62
|
| Command | Caller | Consumes | G: |
|
|
63
63
|
|---|---|---|---|
|
|
64
64
|
| `checklist mark <taskDir> {start\|<n>\|complete [--mark-last]\|blocked --reason <s>\|no-change --reason <s>\|--help}` | `{{TASK_DIR}}/mark` shim (`mark_cmd`) | exit code; `SIGNAL.json`, `CHECKLIST.md` | `task/mark-*` |
|
|
65
|
-
| `
|
|
65
|
+
| `pr-body render <taskDir> [--out <file>] --json` (cwd = task dir) | MetaMask engineers and skills; the gateway `pr-body-render.ts` when a pack's `pr_body_cmd` names it | on failure `JSON.parse(stdout).error`, else stderr; then `--out`, else `artifacts/pr-body.md` | `task/pr-body-render-*` |
|
|
66
|
+
| `farmslot recipe artifacts <taskDir> --format markdown --out <file> --json` | farm pack hooks (`vars.pr_body_cmd`) and PR-description skills on the farm | successful exit, structured `error.message`, explicit `out`; no implicit output file | shared recipe-cli artifact contracts; retained Markdown baselines |
|
|
66
67
|
| `review checklist --out <file> [--domain <d>] [--since <sha>]` | domain fixtures, review templates | the written file | `task/review-checklist` |
|
|
67
68
|
| `check diff …`, `recipe-quality build …` | templates | exit code | not covered: run repo lint/test tooling |
|
|
68
69
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deeeed/metamask-harness",
|
|
3
|
-
"version": "0.92.
|
|
3
|
+
"version": "0.92.21",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mm-harness": "bin/mm-harness"
|
|
@@ -21,15 +21,15 @@
|
|
|
21
21
|
"check:syntax": "find . -name '*.mjs' -print0 | xargs -0 -n1 node --check"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@farmslot/adapter-node": "0.1.
|
|
25
|
-
"@farmslot/adapter-rn": "0.22.
|
|
26
|
-
"@farmslot/adapter-sdk": "0.
|
|
27
|
-
"@farmslot/adapter-web": "0.10.
|
|
28
|
-
"@farmslot/agent-runtime": "0.
|
|
24
|
+
"@farmslot/adapter-node": "0.1.34",
|
|
25
|
+
"@farmslot/adapter-rn": "0.22.23",
|
|
26
|
+
"@farmslot/adapter-sdk": "0.18.0",
|
|
27
|
+
"@farmslot/adapter-web": "0.10.14",
|
|
28
|
+
"@farmslot/agent-runtime": "0.29.0",
|
|
29
29
|
"@farmslot/handoff": "^0.3.1",
|
|
30
|
-
"@farmslot/protocol": "0.
|
|
31
|
-
"@farmslot/recipe-cli": "0.
|
|
32
|
-
"@farmslot/recipe-runner": "0.
|
|
30
|
+
"@farmslot/protocol": "0.54.0",
|
|
31
|
+
"@farmslot/recipe-cli": "0.23.0",
|
|
32
|
+
"@farmslot/recipe-runner": "0.39.0",
|
|
33
33
|
"agent-device": "0.19.3",
|
|
34
34
|
"commander": "^12.0.0",
|
|
35
35
|
"esbuild": "0.28.1",
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { adapterOption, optionFlag, optionString, parsePort, targetPath } from "@farmslot/recipe-cli/harness";
|
|
2
|
-
async function handleRuntimeHealth({ options }) {
|
|
3
|
-
const adapter = adapterOption(options);
|
|
4
|
-
if (adapter !== "extension") throw new Error("runtime-health currently applies to the extension adapter.");
|
|
5
|
-
const target = targetPath(options);
|
|
6
|
-
const cdpPort = parsePort(
|
|
7
|
-
optionString(options, "cdpPort") ?? process.env.CDP_PORT ?? process.env.RECIPE_CDP_PORT,
|
|
8
|
-
"runtime-health requires --cdp-port <port>."
|
|
9
|
-
);
|
|
10
|
-
const { checkExtensionRuntimeHealth, formatHealthFailure } = await import("../adapters/extension/runtime.js");
|
|
11
|
-
const report = await checkExtensionRuntimeHealth(target, cdpPort);
|
|
12
|
-
if (optionFlag(options, "json")) console.log(JSON.stringify(report, null, 2));
|
|
13
|
-
else if (report.status === "PASS") console.log(`PASS extension runtime cdp=${cdpPort} target=${report.targetUrl}`);
|
|
14
|
-
else console.error(formatHealthFailure(report, target));
|
|
15
|
-
return report.status === "PASS" ? 0 : 1;
|
|
16
|
-
}
|
|
17
|
-
export {
|
|
18
|
-
handleRuntimeHealth
|
|
19
|
-
};
|