@junghanacs/entwurf 0.39.0 → 0.40.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 (31) hide show
  1. package/BASELINE.md +5 -3
  2. package/CHANGELOG.md +236 -0
  3. package/DELIVERY.md +34 -0
  4. package/README.md +11 -9
  5. package/VERIFY.md +11 -1
  6. package/docs/setup-clean-host.md +42 -3
  7. package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +19 -5
  8. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-mailbox-body.js +10 -4
  9. package/mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-receive.js +123 -0
  10. package/mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-send.js +259 -0
  11. package/mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-transport.js +80 -0
  12. package/mcp/entwurf-bridge/dist/scripts/remote-pair-receive.js +65 -0
  13. package/mcp/entwurf-bridge/src/index.ts +25 -5
  14. package/mcp/entwurf-bridge/tsconfig.build.json +7 -0
  15. package/package.json +2 -2
  16. package/pi-extensions/lib/meta-mailbox-body.ts +13 -4
  17. package/pi-extensions/lib/remote-pair-receive.ts +184 -0
  18. package/pi-extensions/lib/remote-pair-send.ts +325 -0
  19. package/pi-extensions/lib/remote-pair-transport.ts +81 -0
  20. package/run.sh +44 -0
  21. package/scripts/check-gate-qualification.ts +6 -0
  22. package/scripts/check-remote-pair-installed.ts +242 -0
  23. package/scripts/lib/remote-pair-installed-audit.ts +25 -0
  24. package/scripts/mutants/control-send-receipt.json +2 -2
  25. package/scripts/mutants/remote-pair-display.json +53 -0
  26. package/scripts/mutants/remote-pair-installed-audit.json +28 -0
  27. package/scripts/mutants/remote-pair-receive.json +126 -0
  28. package/scripts/mutants/remote-pair-roundtrip.json +83 -0
  29. package/scripts/mutants/remote-pair-send.json +194 -0
  30. package/scripts/mutants/remote-pair-transport.json +66 -0
  31. package/scripts/remote-pair-receive.ts +73 -0
@@ -3497,6 +3497,12 @@ let manifestCount: number;
3497
3497
  "omp-receive": 11,
3498
3498
  "probe-ordering": 1,
3499
3499
  "release-gate": 21,
3500
+ "remote-pair-display": 3,
3501
+ "remote-pair-installed-audit": 2,
3502
+ "remote-pair-receive": 8,
3503
+ "remote-pair-roundtrip": 5,
3504
+ "remote-pair-send": 12,
3505
+ "remote-pair-transport": 4,
3500
3506
  "retired-sent-surface": 2,
3501
3507
  "resume-args": 6,
3502
3508
  "resume-launch-identity": 7,
@@ -0,0 +1,242 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * check-remote-pair-installed — #107 rail 3: the remote pair on an INSTALLED package.
4
+ *
5
+ * `./run.sh check-remote-pair-installed --tgz <absolute candidate tarball>`
6
+ *
7
+ * Why this is a scripts/ gate and not a source-adjacent vitest test: its subject is an ARTIFACT.
8
+ * It installs a caller-supplied tarball into a fresh consumer prefix with npm and then drives
9
+ * that install's own processes (its bridge `start.sh` → compiled dist, its `run.sh` → run_ts →
10
+ * compiled entry). A test beside the source can only reach the source.
11
+ *
12
+ * What it does, once, with every writable root (HOME, npm cache/userconfig, XDG, PI agent dir)
13
+ * inside one temp sandbox:
14
+ * 1. copies the given tarball into the sandbox and installs THAT copy (never repacks; the copy's
15
+ * sha256 is printed). npm reads the package's declared dependencies from the registry into the
16
+ * sandbox cache — a network read, but no model, no operator config and no ssh;
17
+ * 2. checks the installed package carries the receive entry's compiled twin and the remote libs;
18
+ * 3. runs the installed `entwurf remote-pair-receive` with wrong argv: usage on stderr, exit 2 —
19
+ * the compiled twin was reached through run_ts, not a TS crossing;
20
+ * 4. runs the existing T2 scene (pi-extensions/lib/remote-pair-roundtrip.test.ts) with its subject
21
+ * switched to this install: real installed bridge, stand-in ssh → installed run.sh → installed
22
+ * entry → production dispatch, with the same mailbox/provenance/receipt/reverse-dispatch oracles;
23
+ * 5. audits checkout invisibility: a preload resolve hook records every module each SUBJECT
24
+ * process resolves; every file must sit (by real path) inside the consumer prefix. The
25
+ * orchestrator (vitest + its fixture writer) is this checkout and is not audited;
26
+ * 6. control: the same hook on this checkout's own `run.sh remote-pair-receive` must report a
27
+ * checkout path, so the audit can see the thing it refuses.
28
+ *
29
+ * The subject switch (ENTWURF_REMOTE_PAIR_INSTALLED_PKG and the resolve-hook variables) is set
30
+ * here and only here: it changes what the T2 cells measure, so it must not be set ambiently.
31
+ *
32
+ * Mutation proof covers only the audit's acceptance primitive (scripts/lib/remote-pair-installed-audit.ts);
33
+ * the install, compiled-surface, twin and roundtrip cells are actual-runtime evidence with the
34
+ * checkout control, not mutation-qualified.
35
+ *
36
+ * Not claimed: the published npm artifact (this is whatever tarball the caller passes), real sshd,
37
+ * a real remote host, a durable host reading or replying, or the check-pack/check-pack-install floor.
38
+ */
39
+
40
+ import { spawnSync } from "node:child_process";
41
+ import { createHash } from "node:crypto";
42
+ import * as fs from "node:fs";
43
+ import * as os from "node:os";
44
+ import * as path from "node:path";
45
+ import { fileURLToPath } from "node:url";
46
+ import { filesOutsideRoot as outside } from "./lib/remote-pair-installed-audit.ts";
47
+
48
+ const LABEL = "check-remote-pair-installed";
49
+ const REPO = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
50
+
51
+ const args = process.argv.slice(2);
52
+ if (args.length !== 2 || args[0] !== "--tgz" || !path.isAbsolute(args[1])) {
53
+ console.error(`usage: ./run.sh ${LABEL} --tgz <absolute candidate tarball>`);
54
+ process.exit(2);
55
+ }
56
+ const tgz = args[1];
57
+ if (!fs.lstatSync(tgz).isFile()) {
58
+ console.error(`${LABEL}: --tgz must be a regular file (${tgz})`);
59
+ process.exit(2);
60
+ }
61
+
62
+ let passed = 0;
63
+ let failed = 0;
64
+ function ok(label: string, cond: boolean, detail = ""): void {
65
+ if (cond) {
66
+ passed++;
67
+ console.log(` ok ${label}`);
68
+ } else {
69
+ failed++;
70
+ console.log(` FAIL ${label}${detail ? `\n ${detail}` : ""}`);
71
+ }
72
+ }
73
+
74
+ const sandbox = fs.mkdtempSync(path.join(os.tmpdir(), "entwurf-remote-pair-installed-"));
75
+ const consumer = path.join(sandbox, "consumer");
76
+ const npmHome = path.join(sandbox, "npm-home");
77
+ const logs = path.join(sandbox, "resolve-logs");
78
+ const hook = path.join(sandbox, "resolve-hook.mjs");
79
+ for (const d of [consumer, npmHome, logs]) fs.mkdirSync(d, { recursive: true });
80
+
81
+ function sandboxEnv(home: string): NodeJS.ProcessEnv {
82
+ return {
83
+ PATH: process.env.PATH ?? "",
84
+ HOME: home,
85
+ XDG_CONFIG_HOME: path.join(home, ".config"),
86
+ XDG_DATA_HOME: path.join(home, ".local", "share"),
87
+ XDG_STATE_HOME: path.join(home, ".local", "state"),
88
+ XDG_CACHE_HOME: path.join(home, ".cache"),
89
+ PI_CODING_AGENT_DIR: path.join(home, ".pi", "agent"),
90
+ };
91
+ }
92
+
93
+ fs.writeFileSync(
94
+ hook,
95
+ [
96
+ 'import { appendFileSync } from "node:fs";',
97
+ 'import { registerHooks } from "node:module";',
98
+ "const log = process.env.ENTWURF_REMOTE_PAIR_RESOLVE_LOG;",
99
+ "if (log) {",
100
+ "\tregisterHooks({",
101
+ "\t\tresolve(specifier, context, next) {",
102
+ "\t\t\tconst r = next(specifier, context);",
103
+ '\t\t\tappendFileSync(log, r.url + "\\n");',
104
+ "\t\t\treturn r;",
105
+ "\t\t},",
106
+ "\t});",
107
+ "}",
108
+ "",
109
+ ].join("\n"),
110
+ );
111
+
112
+ function hookEnv(label: string): NodeJS.ProcessEnv {
113
+ return { NODE_OPTIONS: `--import=${hook}`, ENTWURF_REMOTE_PAIR_RESOLVE_LOG: path.join(logs, `${label}.log`) };
114
+ }
115
+
116
+ // The artifact is a snapshot: copy the caller's file into the sandbox once, then hash and install
117
+ // THAT copy, so what is measured is exactly what is installed. Never repacked.
118
+ const snapshot = path.join(sandbox, "artifact", path.basename(tgz));
119
+ fs.mkdirSync(path.dirname(snapshot), { recursive: true });
120
+ fs.copyFileSync(tgz, snapshot);
121
+ const sha = createHash("sha256").update(fs.readFileSync(snapshot)).digest("hex");
122
+ console.log(
123
+ `[${LABEL}] caller tarball ${tgz}\n[${LABEL}] snapshot ${snapshot}\n[${LABEL}] snapshot sha256 ${sha}\n[${LABEL}] sandbox ${sandbox}`,
124
+ );
125
+
126
+ try {
127
+ // 1. Install as-is.
128
+ fs.writeFileSync(
129
+ path.join(consumer, "package.json"),
130
+ JSON.stringify({ name: "remote-pair-consumer", private: true }),
131
+ );
132
+ const npm = spawnSync("npm", ["install", snapshot, "--no-audit", "--no-fund"], {
133
+ cwd: consumer,
134
+ encoding: "utf8",
135
+ env: {
136
+ ...sandboxEnv(npmHome),
137
+ npm_config_cache: path.join(sandbox, "npm-cache"),
138
+ npm_config_userconfig: path.join(npmHome, ".npmrc"),
139
+ },
140
+ });
141
+ ok(
142
+ "[QK:RPI-INSTALLED] npm installs the given tarball into a fresh consumer prefix",
143
+ npm.status === 0,
144
+ npm.stderr.slice(-800),
145
+ );
146
+ const pkg = path.join(consumer, "node_modules", "@junghanacs", "entwurf");
147
+ const pkgStat = fs.existsSync(pkg) ? fs.lstatSync(pkg) : null;
148
+ ok(
149
+ "[QK:RPI-INSTALLED] the package is a real directory inside the consumer (not a link to a checkout)",
150
+ pkgStat !== null && pkgStat.isDirectory() && !pkgStat.isSymbolicLink(),
151
+ );
152
+ if (failed) throw new Error("install failed; later cells would not measure the artifact");
153
+
154
+ // 2. The compiled surface is in the package.
155
+ for (const rel of [
156
+ "mcp/entwurf-bridge/dist/scripts/remote-pair-receive.js",
157
+ "mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-receive.js",
158
+ "mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-send.js",
159
+ "mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-transport.js",
160
+ ]) {
161
+ ok(`[QK:RPI-COMPILED-SURFACE] the installed package carries ${rel}`, fs.existsSync(path.join(pkg, rel)));
162
+ }
163
+
164
+ // 3. The installed bin reaches the compiled twin.
165
+ const home = path.join(sandbox, "usage-home");
166
+ const usage = spawnSync(path.join(pkg, "run.sh"), ["remote-pair-receive"], {
167
+ encoding: "utf8",
168
+ env: { ...sandboxEnv(home), ...hookEnv("installed-usage") },
169
+ });
170
+ ok(
171
+ "[QK:RPI-INSTALLED-ENTRY-TWIN] the installed `entwurf remote-pair-receive` answers wrong argv with usage on stderr, exit 2, no receipt",
172
+ usage.status === 2 && /usage: entwurf remote-pair-receive/.test(usage.stderr) && usage.stdout === "",
173
+ `status=${usage.status} stdout=${usage.stdout.slice(0, 200)} stderr=${usage.stderr.slice(0, 300)}`,
174
+ );
175
+
176
+ // 4. The T2 scene against the install.
177
+ const vitest = spawnSync(
178
+ path.join(REPO, "node_modules", ".bin", "vitest"),
179
+ ["run", "pi-extensions/lib/remote-pair-roundtrip.test.ts"],
180
+ {
181
+ cwd: REPO,
182
+ encoding: "utf8",
183
+ env: {
184
+ ...process.env,
185
+ ENTWURF_REMOTE_PAIR_INSTALLED_PKG: pkg,
186
+ ENTWURF_REMOTE_PAIR_RESOLVE_HOOK: hook,
187
+ ENTWURF_REMOTE_PAIR_RESOLVE_LOG_DIR: logs,
188
+ },
189
+ },
190
+ );
191
+ const summary = (vitest.stdout.match(/Tests\s+.*$/m) ?? [""])[0].trim();
192
+ ok(
193
+ "[QK:RPI-ROUNDTRIP-ON-INSTALL] the T2 cells (A→B, reverse dispatch, opt-in absent off, other host refuses, both socket dirs) pass against the installed bridge and entry",
194
+ vitest.status === 0 && /^Tests\s+5 passed\b/.test(summary),
195
+ `${summary}\n${vitest.stdout
196
+ .split("\n")
197
+ .filter((l) => /×|FAIL|Error/.test(l))
198
+ .slice(0, 10)
199
+ .join("\n")}`,
200
+ );
201
+
202
+ // 5. Checkout invisibility of the subject processes.
203
+ const all = fs.readdirSync(logs).map((n) => path.join(logs, n));
204
+ const bridgeLogs = all.filter((f) => path.basename(f).startsWith("bridge-"));
205
+ const entryLogs = all.filter((f) => path.basename(f).startsWith("entry-"));
206
+ const subjectLogs = [...bridgeLogs, ...entryLogs, ...all.filter((f) => path.basename(f) === "installed-usage.log")];
207
+ ok(
208
+ "[QK:RPI-AUDIT-SAW-SUBJECTS] the resolve hook recorded the installed bridge processes and the installed entry processes",
209
+ bridgeLogs.some((f) => fs.statSync(f).size > 0) && entryLogs.some((f) => fs.statSync(f).size > 0),
210
+ `bridge logs=${bridgeLogs.length} entry logs=${entryLogs.length}`,
211
+ );
212
+ const audit = outside(subjectLogs, consumer);
213
+ ok(
214
+ `[QK:RPI-CHECKOUT-INVISIBLE] every module the installed subject processes resolved (${audit.total}) sits inside the consumer prefix`,
215
+ audit.total > 0 && audit.outside.length === 0,
216
+ audit.outside.slice(0, 10).join("\n "),
217
+ );
218
+
219
+ // 6. Control: the audit sees a checkout when there is one.
220
+ const control = spawnSync(path.join(REPO, "run.sh"), ["remote-pair-receive"], {
221
+ encoding: "utf8",
222
+ env: { ...sandboxEnv(path.join(sandbox, "control-home")), ...hookEnv("control-checkout") },
223
+ });
224
+ const controlAudit = outside([path.join(logs, "control-checkout.log")], consumer);
225
+ const realRepo = fs.realpathSync(REPO);
226
+ ok(
227
+ "[QK:RPI-AUDIT-CONTROL] the same audit on this checkout's own entry reports checkout paths (the detector is live)",
228
+ control.status === 2 && controlAudit.outside.some((p) => p.startsWith(`${realRepo}${path.sep}`)),
229
+ `status=${control.status} outside=${controlAudit.outside.length}`,
230
+ );
231
+ } catch (err) {
232
+ ok(`[${LABEL}] gate ran to completion`, false, err instanceof Error ? err.message : String(err));
233
+ } finally {
234
+ // Synthetic fixture data only. Kept on failure so the resolve logs and npm output can be read.
235
+ if (failed === 0) fs.rmSync(sandbox, { recursive: true, force: true });
236
+ else console.log(`[${LABEL}] kept the sandbox for diagnosis: ${sandbox}`);
237
+ }
238
+
239
+ console.log(
240
+ `\n${LABEL}: ${passed} passed, ${failed} failed — installed artifact ${sha.slice(0, 12)}; no real ssh, no model`,
241
+ );
242
+ process.exit(failed === 0 ? 0 : 1);
@@ -0,0 +1,25 @@
1
+ /**
2
+ * The acceptance primitive of `check-remote-pair-installed`'s checkout-invisibility cell (#107 rail 3),
3
+ * as a pure function over resolve-hook logs so it can be proven on tiny synthetic files
4
+ * (test/remote-pair-installed-audit.test.ts). Each log line is one URL a subject process resolved.
5
+ * A `file:` URL counts and is judged by its REAL path; anything else (node: builtins) is ignored.
6
+ */
7
+ import * as fs from "node:fs";
8
+ import * as path from "node:path";
9
+ import { fileURLToPath } from "node:url";
10
+
11
+ /** Every file the logs name that is NOT inside `root` (both compared by real path), and how many file URLs were read. */
12
+ export function filesOutsideRoot(logFiles: readonly string[], root: string): { total: number; outside: string[] } {
13
+ const realRoot = fs.realpathSync(root);
14
+ const out = new Set<string>();
15
+ let total = 0;
16
+ for (const f of logFiles) {
17
+ for (const url of fs.readFileSync(f, "utf8").split("\n").filter(Boolean)) {
18
+ if (!url.startsWith("file:")) continue;
19
+ total++;
20
+ const real = fs.realpathSync(fileURLToPath(url));
21
+ if (real !== realRoot && !real.startsWith(`${realRoot}${path.sep}`)) out.add(real);
22
+ }
23
+ }
24
+ return { total, outside: [...out].sort() };
25
+ }
@@ -166,10 +166,10 @@
166
166
  "title": "the pi queue oracle's public coordinate is dropped from `check:vitest`, so the file that proves every claim the receipt makes about a receiver's queues is green only when somebody runs `pnpm test` by hand — the discovery door deliberately does not select `test/**`, so nothing else would notice",
167
167
  "subject": "package.json",
168
168
  "find": [
169
- "\"check:vitest\": \"./run.sh check-mux-fresh-call && ./run.sh check-acp-sdk-surface && ./run.sh check-pi-queue-oracle && ./run.sh check-tests-beside-behavior\""
169
+ "\"check:vitest\": \"./run.sh check-mux-fresh-call && ./run.sh check-acp-sdk-surface && ./run.sh check-pi-queue-oracle && ./run.sh check-tests-beside-behavior && ./run.sh check-remote-pair-installed-audit\""
170
170
  ],
171
171
  "replace": [
172
- "\"check:vitest\": \"./run.sh check-mux-fresh-call && ./run.sh check-acp-sdk-surface && ./run.sh check-tests-beside-behavior\""
172
+ "\"check:vitest\": \"./run.sh check-mux-fresh-call && ./run.sh check-acp-sdk-surface && ./run.sh check-tests-beside-behavior && ./run.sh check-remote-pair-installed-audit\""
173
173
  ],
174
174
  "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
175
175
  "timeoutSeconds": 600,
@@ -0,0 +1,53 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "lane": "remote-pair-display",
4
+ "mutants": [
5
+ {
6
+ "claim": "REMOTE-DISPLAY-LOCAL-UNCHANGED",
7
+ "title": "the remote branch is taken for every non-replyable sender, so an ordinary external or meta-session header turns into a remote line naming a route that does not exist",
8
+ "subject": "pi-extensions/lib/meta-mailbox-body.ts",
9
+ "find": ["\tconst remote = !replyable ? sender.remote : undefined;"],
10
+ "replace": [
11
+ "\tconst remote = !replyable ? (sender.remote ?? { via: \"?\", replyRoute: \"none\" as const }) : undefined;"
12
+ ],
13
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
14
+ "timeoutSeconds": 600,
15
+ "signature": "[QK:REMOTE-DISPLAY-LOCAL-UNCHANGED]",
16
+ "signatureSource": "pi-extensions/lib/meta-mailbox-body.test.ts",
17
+ "build": {
18
+ "argv": ["bash", "scripts/build-bridge.sh"],
19
+ "output": "mcp/entwurf-bridge/dist"
20
+ }
21
+ },
22
+ {
23
+ "claim": "REMOTE-DISPLAY-SESSIONLINE",
24
+ "title": "a remote sender with a declared route back is told to reply via entwurf_v2 as if that route had been probed",
25
+ "subject": "pi-extensions/lib/meta-mailbox-body.ts",
26
+ "find": ["\"reply route declared, not probed\""],
27
+ "replace": ["\"reply via entwurf_v2 to this sessionId\""],
28
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
29
+ "timeoutSeconds": 600,
30
+ "signature": "[QK:REMOTE-DISPLAY-SESSIONLINE]",
31
+ "signatureSource": "pi-extensions/lib/meta-mailbox-body.test.ts",
32
+ "build": {
33
+ "argv": ["bash", "scripts/build-bridge.sh"],
34
+ "output": "mcp/entwurf-bridge/dist"
35
+ }
36
+ },
37
+ {
38
+ "claim": "REMOTE-DISPLAY-SENDER-HOST-CWD",
39
+ "title": "a remote sender's cwd is abbreviated against this host's HOME, so a path on the other machine reads as a local one",
40
+ "subject": "pi-extensions/lib/meta-mailbox-body.ts",
41
+ "find": ["\tconst where = remote ? `${sender.cwd} (sender-host path)` : abbreviateHome(sender.cwd);"],
42
+ "replace": ["\tconst where = abbreviateHome(sender.cwd);"],
43
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
44
+ "timeoutSeconds": 600,
45
+ "signature": "[QK:REMOTE-DISPLAY-SENDER-HOST-CWD]",
46
+ "signatureSource": "pi-extensions/lib/meta-mailbox-body.test.ts",
47
+ "build": {
48
+ "argv": ["bash", "scripts/build-bridge.sh"],
49
+ "output": "mcp/entwurf-bridge/dist"
50
+ }
51
+ }
52
+ ]
53
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "lane": "remote-pair-installed-audit",
4
+ "mutants": [
5
+ {
6
+ "claim": "RPI-AUDIT-REAL-PATH",
7
+ "title": "the installed-closure audit judges a resolved file by its link path, so an installed file that is a symlink into a checkout passes as inside the consumer",
8
+ "subject": "scripts/lib/remote-pair-installed-audit.ts",
9
+ "find": ["\t\t\tconst real = fs.realpathSync(fileURLToPath(url));"],
10
+ "replace": ["\t\t\tconst real = fileURLToPath(url);"],
11
+ "gate": ["bash", "run.sh", "check-remote-pair-installed-audit"],
12
+ "timeoutSeconds": 120,
13
+ "signature": "[QK:RPI-AUDIT-REAL-PATH]",
14
+ "signatureSource": "test/remote-pair-installed-audit.test.ts"
15
+ },
16
+ {
17
+ "claim": "RPI-AUDIT-ROOT-BOUNDARY",
18
+ "title": "the installed-closure audit treats any path that merely starts with the consumer prefix as inside it, so a sibling directory such as <prefix>-x passes",
19
+ "subject": "scripts/lib/remote-pair-installed-audit.ts",
20
+ "find": ["\t\t\tif (real !== realRoot && !real.startsWith(`${realRoot}${path.sep}`)) out.add(real);"],
21
+ "replace": ["\t\t\tif (real !== realRoot && !real.startsWith(realRoot)) out.add(real);"],
22
+ "gate": ["bash", "run.sh", "check-remote-pair-installed-audit"],
23
+ "timeoutSeconds": 120,
24
+ "signature": "[QK:RPI-AUDIT-ROOT-BOUNDARY]",
25
+ "signatureSource": "test/remote-pair-installed-audit.test.ts"
26
+ }
27
+ ]
28
+ }
@@ -0,0 +1,126 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "lane": "remote-pair-receive",
4
+ "mutants": [
5
+ {
6
+ "claim": "REMOTE-PAIR-FROM-OUTSIDE-NO-MUTATION",
7
+ "title": "the receiving entry stops checking the claimed sender against the declared pair, so any gid the SSH account writes is dispatched into the declared target's mailbox",
8
+ "subject": "pi-extensions/lib/remote-pair-receive.ts",
9
+ "find": ["\tif (claim.from !== decl.remoteGardenId) {"],
10
+ "replace": ["\tif (false) {"],
11
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
12
+ "timeoutSeconds": 600,
13
+ "signature": "[QK:REMOTE-PAIR-FROM-OUTSIDE-NO-MUTATION]",
14
+ "signatureSource": "pi-extensions/lib/remote-pair-receive.test.ts",
15
+ "build": {
16
+ "argv": ["bash", "scripts/build-bridge.sh"],
17
+ "output": "mcp/entwurf-bridge/dist"
18
+ }
19
+ },
20
+ {
21
+ "claim": "REMOTE-PAIR-TARGET-OUTSIDE-NO-MUTATION",
22
+ "title": "the receiving entry stops checking the claimed target against the declared pair, so a declared remote can reach every deliverable citizen on this host",
23
+ "subject": "pi-extensions/lib/remote-pair-receive.ts",
24
+ "find": ["\tif (claim.target !== decl.localGardenId) {"],
25
+ "replace": ["\tif (false) {"],
26
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
27
+ "timeoutSeconds": 600,
28
+ "signature": "[QK:REMOTE-PAIR-TARGET-OUTSIDE-NO-MUTATION]",
29
+ "signatureSource": "pi-extensions/lib/remote-pair-receive.test.ts",
30
+ "build": {
31
+ "argv": ["bash", "scripts/build-bridge.sh"],
32
+ "output": "mcp/entwurf-bridge/dist"
33
+ }
34
+ },
35
+ {
36
+ "claim": "REMOTE-PAIR-IGNORES-AMBIENT-SENDER",
37
+ "title": "the receiving entry lets an ambient PI_SESSION_ID in the SSH environment stand in for the sender, so the header names whoever's carrier leaked instead of the claim",
38
+ "subject": "pi-extensions/lib/remote-pair-receive.ts",
39
+ "find": ["\t\tsessionId: claim.from,"],
40
+ "replace": ["\t\tsessionId: process.env.PI_SESSION_ID ?? claim.from,"],
41
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
42
+ "timeoutSeconds": 600,
43
+ "signature": "[QK:REMOTE-PAIR-IGNORES-AMBIENT-SENDER]",
44
+ "signatureSource": "pi-extensions/lib/remote-pair-receive.test.ts",
45
+ "build": {
46
+ "argv": ["bash", "scripts/build-bridge.sh"],
47
+ "output": "mcp/entwurf-bridge/dist"
48
+ }
49
+ },
50
+ {
51
+ "claim": "REMOTE-PAIR-MATCH-DISPATCHES-ONCE",
52
+ "title": "the receiving entry marks a remote sender replyable, so the body tells the receiver to reply via entwurf_v2 as if a live local reply route had been probed — none was",
53
+ "subject": "pi-extensions/lib/remote-pair-receive.ts",
54
+ "find": ["\t\treplyable: false,"],
55
+ "replace": ["\t\treplyable: true,"],
56
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
57
+ "timeoutSeconds": 600,
58
+ "signature": "[QK:REMOTE-PAIR-MATCH-DISPATCHES-ONCE]",
59
+ "signatureSource": "pi-extensions/lib/remote-pair-receive.test.ts",
60
+ "build": {
61
+ "argv": ["bash", "scripts/build-bridge.sh"],
62
+ "output": "mcp/entwurf-bridge/dist"
63
+ }
64
+ },
65
+ {
66
+ "claim": "REMOTE-PAIR-CLAIM-EXACT-SHAPE",
67
+ "title": "the claim parser accepts extra or missing fields, so a sender-side field the receiver never reads can ride in and a partial claim is dispatched",
68
+ "subject": "pi-extensions/lib/remote-pair-receive.ts",
69
+ "find": ["\tif (got.length !== want.length || got.some((k, i) => k !== want[i])) {"],
70
+ "replace": ["\tif (false) {"],
71
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
72
+ "timeoutSeconds": 600,
73
+ "signature": "[QK:REMOTE-PAIR-CLAIM-EXACT-SHAPE]",
74
+ "signatureSource": "pi-extensions/lib/remote-pair-receive.test.ts",
75
+ "build": {
76
+ "argv": ["bash", "scripts/build-bridge.sh"],
77
+ "output": "mcp/entwurf-bridge/dist"
78
+ }
79
+ },
80
+ {
81
+ "claim": "REMOTE-PAIR-ENTRY-REFUSES-OUTSIDE",
82
+ "title": "the entry reports a pair refusal as exit 0, so the sending host cannot tell a refused claim from a dispatched one without parsing prose",
83
+ "subject": "scripts/remote-pair-receive.ts",
84
+ "find": ["emit(receipt, receipt.kind === \"rejected\" ? 3 : receipt.isError ? 4 : 0);"],
85
+ "replace": ["emit(receipt, receipt.kind === \"rejected\" ? 0 : receipt.isError ? 4 : 0);"],
86
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
87
+ "timeoutSeconds": 600,
88
+ "signature": "[QK:REMOTE-PAIR-ENTRY-REFUSES-OUTSIDE]",
89
+ "signatureSource": "pi-extensions/lib/remote-pair-receive.test.ts",
90
+ "build": {
91
+ "argv": ["bash", "scripts/build-bridge.sh"],
92
+ "output": "mcp/entwurf-bridge/dist"
93
+ }
94
+ },
95
+ {
96
+ "claim": "REMOTE-PAIR-ENTRY-USAGE",
97
+ "title": "the entry accepts any --pair it is given, so the sender picks which declaration scopes the claim on the receiving host",
98
+ "subject": "scripts/remote-pair-receive.ts",
99
+ "find": ["\tif (!path.isAbsolute(pairFile) || path.normalize(pairFile) !== own) {"],
100
+ "replace": ["\tif (false) {"],
101
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
102
+ "timeoutSeconds": 600,
103
+ "signature": "[QK:REMOTE-PAIR-ENTRY-USAGE]",
104
+ "signatureSource": "pi-extensions/lib/remote-pair-receive.test.ts",
105
+ "build": {
106
+ "argv": ["bash", "scripts/build-bridge.sh"],
107
+ "output": "mcp/entwurf-bridge/dist"
108
+ }
109
+ },
110
+ {
111
+ "claim": "REMOTE-PAIR-ENTRY-REPLY-ROUTE-DECLARED",
112
+ "title": "the entry reports no route back for a claim its own opt-in pairs with, so the receiver is told it cannot answer a sender it has declared",
113
+ "subject": "scripts/remote-pair-receive.ts",
114
+ "find": ["\treplyRoute: \"declared\","],
115
+ "replace": ["\treplyRoute: \"none\","],
116
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
117
+ "timeoutSeconds": 600,
118
+ "signature": "[QK:REMOTE-PAIR-ENTRY-REPLY-ROUTE-DECLARED]",
119
+ "signatureSource": "pi-extensions/lib/remote-pair-receive.test.ts",
120
+ "build": {
121
+ "argv": ["bash", "scripts/build-bridge.sh"],
122
+ "output": "mcp/entwurf-bridge/dist"
123
+ }
124
+ }
125
+ ]
126
+ }
@@ -0,0 +1,83 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "lane": "remote-pair-roundtrip",
4
+ "mutants": [
5
+ {
6
+ "claim": "REMOTE-ROUNDTRIP-A-TO-B",
7
+ "title": "the bridge never reads the opt-in, so a declared remote target falls through to the local decider as bad-target",
8
+ "subject": "mcp/entwurf-bridge/src/index.ts",
9
+ "find": ["\t\t\t\t\troute: readRemotePairOptIn(remotePairOptInPath()),"],
10
+ "replace": ["\t\t\t\t\troute: null,"],
11
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
12
+ "timeoutSeconds": 600,
13
+ "signature": "[QK:REMOTE-ROUNDTRIP-A-TO-B]",
14
+ "signatureSource": "pi-extensions/lib/remote-pair-roundtrip.test.ts",
15
+ "build": {
16
+ "argv": ["bash", "scripts/build-bridge.sh"],
17
+ "output": "mcp/entwurf-bridge/dist"
18
+ }
19
+ },
20
+ {
21
+ "claim": "REMOTE-ROUNDTRIP-REVERSE-DISPATCH",
22
+ "title": "the bridge's transport never reaches the other host and always answers exit 1, so every declared remote send in either direction becomes indeterminate",
23
+ "subject": "mcp/entwurf-bridge/src/index.ts",
24
+ "find": ["\t\t\t\t\ttransport: processTransport,"],
25
+ "replace": [
26
+ "\t\t\t\t\ttransport: { run: async () => ({ exitCode: 1, signal: null, timedOut: false, stdout: \"\", stderr: \"\" }) },"
27
+ ],
28
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
29
+ "timeoutSeconds": 600,
30
+ "signature": "[QK:REMOTE-ROUNDTRIP-REVERSE-DISPATCH]",
31
+ "signatureSource": "pi-extensions/lib/remote-pair-roundtrip.test.ts",
32
+ "build": {
33
+ "argv": ["bash", "scripts/build-bridge.sh"],
34
+ "output": "mcp/entwurf-bridge/dist"
35
+ }
36
+ },
37
+ {
38
+ "claim": "REMOTE-ROUNDTRIP-OPT-IN-ABSENT-OFF",
39
+ "title": "the bridge replaces the local surface on the remote path's behalf, so a host with no opt-in stops running the existing local decider",
40
+ "subject": "mcp/entwurf-bridge/src/index.ts",
41
+ "find": ["\t\t\t\t\trunLocal: (params, opts) => runAndRenderEntwurfV2FromSurface(params, opts),"],
42
+ "replace": ["\t\t\t\t\trunLocal: async () => ({ isError: false, text: \"local skipped\" }),"],
43
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
44
+ "timeoutSeconds": 600,
45
+ "signature": "[QK:REMOTE-ROUNDTRIP-OPT-IN-ABSENT-OFF]",
46
+ "signatureSource": "pi-extensions/lib/remote-pair-roundtrip.test.ts",
47
+ "build": {
48
+ "argv": ["bash", "scripts/build-bridge.sh"],
49
+ "output": "mcp/entwurf-bridge/dist"
50
+ }
51
+ },
52
+ {
53
+ "claim": "REMOTE-ROUNDTRIP-OTHER-HOST-REFUSES",
54
+ "title": "the sender renders the other host's pair refusal without naming it, so A cannot tell B refused the pair from any other failure",
55
+ "subject": "pi-extensions/lib/remote-pair-send.ts",
56
+ "find": ["refused by the other host's pair: ${o.reason} (${o.detail})"],
57
+ "replace": ["failed"],
58
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
59
+ "timeoutSeconds": 600,
60
+ "signature": "[QK:REMOTE-ROUNDTRIP-OTHER-HOST-REFUSES]",
61
+ "signatureSource": "pi-extensions/lib/remote-pair-roundtrip.test.ts",
62
+ "build": {
63
+ "argv": ["bash", "scripts/build-bridge.sh"],
64
+ "output": "mcp/entwurf-bridge/dist"
65
+ }
66
+ },
67
+ {
68
+ "claim": "REMOTE-ROUNDTRIP-LOCAL-TRACE-BOTH-SOCKET-DIRS",
69
+ "title": "the bridge checks only its own ENTWURF_DIR, so a live local socket in the decider's canonical dir is bypassed and the gid is sent away",
70
+ "subject": "mcp/entwurf-bridge/src/index.ts",
71
+ "find": ["\t\t\t\t\t\tcontrolSocketDirs: [CONTROL_SOCKET_DIR, ENTWURF_DIR],"],
72
+ "replace": ["\t\t\t\t\t\tcontrolSocketDirs: [ENTWURF_DIR],"],
73
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
74
+ "timeoutSeconds": 600,
75
+ "signature": "[QK:REMOTE-ROUNDTRIP-LOCAL-TRACE-BOTH-SOCKET-DIRS]",
76
+ "signatureSource": "pi-extensions/lib/remote-pair-roundtrip.test.ts",
77
+ "build": {
78
+ "argv": ["bash", "scripts/build-bridge.sh"],
79
+ "output": "mcp/entwurf-bridge/dist"
80
+ }
81
+ }
82
+ ]
83
+ }