@junghanacs/entwurf 0.18.2 → 0.19.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.
@@ -12,6 +12,32 @@
12
12
  "timeoutSeconds": 180,
13
13
  "signature": "[QK:FRESH-CUT-COLLISION-NO-MOVE]",
14
14
  "signatureSource": "scripts/check-fresh-cut-gate.sh"
15
+ },
16
+ {
17
+ "claim": "FRESHCUT-SYNCAUTH-TRIPWIRE-FIRES",
18
+ "title": "the retired-credential tripwire goes blind again. This replants the ACTUAL defect rather than a stand-in: under pipefail a `-q` grep exits at the first match and SIGPIPEs the awk still writing ~200KB of a 433KB run.sh, so the pipeline reports 141 and the `if` takes the CLEAN branch — the cell reported no sync_auth surface EXACTLY when one existed. It was deterministic here (100/100 measured) because the producer dwarfs the pipe buffer, which is why the self-test plants its surface near the TOP of the real run.sh: a small synthetic fixture would pass under this mutant and prove nothing",
19
+ "subject": "scripts/check-fresh-cut-gate.sh",
20
+ "find": [" awk '!/^[[:space:]]*#/' \"$1\" | grep -c 'sync_auth' || true"],
21
+ "replace": [" awk '!/^[[:space:]]*#/' \"$1\" | grep -q 'sync_auth' && echo 1 || echo 0"],
22
+ "gate": ["bash", "run.sh", "check-fresh-cut-gate"],
23
+ "timeoutSeconds": 180,
24
+ "signature": "[QK:FRESHCUT-SYNCAUTH-TRIPWIRE-FIRES]",
25
+ "signatureSource": "scripts/check-fresh-cut-gate.sh"
26
+ },
27
+ {
28
+ "claim": "FRESHCUT-PREFLIGHT-CUT-DETECTED",
29
+ "title": "the cell forbidding an install from cutting a generation by itself stops SEEING the call it forbids — the context window silently narrows to the matched line, so a fresh-cut invocation one line into preflight_v3_store() reads as absent and D7 stays green. A forbidding cell that is green on a clean tree cannot tell blindness from compliance, which is what the self-test exists to separate",
30
+ "subject": "scripts/check-fresh-cut-gate.sh",
31
+ "find": [
32
+ " grep -n 'preflight_v3_store()' -A 40 \"$1\" | grep -cE 'run_ts scripts/meta-bridge-fresh-cut\\.ts' || true"
33
+ ],
34
+ "replace": [
35
+ " grep -n 'preflight_v3_store()' -A 0 \"$1\" | grep -cE 'run_ts scripts/meta-bridge-fresh-cut\\.ts' || true"
36
+ ],
37
+ "gate": ["bash", "run.sh", "check-fresh-cut-gate"],
38
+ "timeoutSeconds": 180,
39
+ "signature": "[QK:FRESHCUT-PREFLIGHT-CUT-DETECTED]",
40
+ "signatureSource": "scripts/check-fresh-cut-gate.sh"
15
41
  }
16
42
  ]
17
43
  }
@@ -191,6 +191,30 @@
191
191
  "timeoutSeconds": 60,
192
192
  "signature": "[QK:MUX-LAUNCH-CORE-IMPORT-FREE]",
193
193
  "signatureSource": "scripts/check-mux-launch.ts"
194
+ },
195
+ {
196
+ "claim": "MUX-CLOSE-SERVER-BOUND",
197
+ "title": "the close-side predicate accepts a foreign SERVER — a restarted server hands out the same `@id`, and a close would kill whatever window now wears it",
198
+ "subject": "pi-extensions/lib/mux-placement.ts",
199
+ "find": ["\treturn origin.serverPid === now.serverPid;\n}"],
200
+ "replace": ["\treturn true;\n}"],
201
+ "gate": ["bash", "run.sh", "check-mux-placement"],
202
+ "timeoutSeconds": 120,
203
+ "signature": "[QK:MUX-CLOSE-SERVER-BOUND]",
204
+ "signatureSource": "scripts/check-mux-placement.ts"
205
+ },
206
+ {
207
+ "claim": "RESOLVE-TMUX-SESSION-IMPORT-FREE",
208
+ "title": "the session-lookup leaf reaches into the placement leaf for runTmux — the one decision leaf that must not be able to run tmux acquires exactly that power, and the injected-runner seam its whole safety argument rests on stops being the only way in",
209
+ "subject": "pi-extensions/lib/resolve-tmux-session.ts",
210
+ "find": ["export type TmuxSessionRejectReason = \"tmux-session-name-invalid\" | \"tmux-session-missing\";"],
211
+ "replace": [
212
+ "import { runTmux } from \"./mux-placement.ts\";\nvoid runTmux;\nexport type TmuxSessionRejectReason = \"tmux-session-name-invalid\" | \"tmux-session-missing\";"
213
+ ],
214
+ "gate": ["bash", "run.sh", "check-mux-launch"],
215
+ "timeoutSeconds": 120,
216
+ "signature": "[QK:RESOLVE-TMUX-SESSION-IMPORT-FREE]",
217
+ "signatureSource": "scripts/check-mux-launch.ts"
194
218
  }
195
219
  ]
196
220
  }
@@ -438,6 +438,87 @@
438
438
  "timeoutSeconds": 180,
439
439
  "signature": "[QK:FRESHCALL-BACKEND-SET-SURFACE-PARITY-PI]",
440
440
  "signatureSource": "test/fresh-call-surfaces.contract.test.ts"
441
+ },
442
+ {
443
+ "claim": "TMUXSESSION-LOOKUP-ENGINE",
444
+ "title": "the seat lookup goes back to the `-f` filter engine, which was measured to match EVERY session when the name contains a '}' — a fresh sibling then opens in whichever session tmux listed first",
445
+ "subject": "pi-extensions/lib/resolve-tmux-session.ts",
446
+ "find": ["\treturn [\"list-windows\", \"-t\", `=${name}`, \"-F\", \"#{session_id}\"];"],
447
+ "replace": ["\treturn [\"list-sessions\", \"-f\", `#{==:#{session_name},${name}}`, \"-F\", \"#{session_id}\"];"],
448
+ "gate": ["bash", "run.sh", "check-mux-fresh-call"],
449
+ "timeoutSeconds": 180,
450
+ "signature": "[QK:TMUXSESSION-LOOKUP-ENGINE]",
451
+ "signatureSource": "test/mux-fresh-call.test.ts"
452
+ },
453
+ {
454
+ "claim": "TMUXSESSION-NAME-GRAMMAR",
455
+ "title": "the seat name grammar is widened to 'anything non-empty' — tmux then stores a DIFFERENT name than the one requested (or splits it on '.'/':') and the seat can never be found, reported as 'missing' instead of as a shape this rail does not address",
456
+ "subject": "pi-extensions/lib/resolve-tmux-session.ts",
457
+ "find": ["const SESSION_NAME = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;"],
458
+ "replace": ["const SESSION_NAME = /^.+$/;"],
459
+ "gate": ["bash", "run.sh", "check-mux-fresh-call"],
460
+ "timeoutSeconds": 180,
461
+ "signature": "[QK:TMUXSESSION-NAME-GRAMMAR]",
462
+ "signatureSource": "test/mux-fresh-call.test.ts"
463
+ },
464
+ {
465
+ "claim": "FRESHCALL-PLACEMENT-MISSING-REJECTS",
466
+ "title": "an absent seat is read as success — tmux's exit code is ignored, so a missing session falls through to the caller's own session and the launch looks like the seat worked. Proof is in two halves: this mutant dies on the leaf's injected-runner assert plus the source-text ordering assert in the fast lane, and the behavioural oracle independent of that source is the real-tmux seat cell in check-mux-launch-tmux (refusal by name with a byte-identical list-windows -a before and after)",
467
+ "subject": "pi-extensions/lib/resolve-tmux-session.ts",
468
+ "find": ["\tif (result.status !== 0) return { ok: false, reason: \"tmux-session-missing\" };"],
469
+ "replace": [
470
+ "\tif (result.status !== 0 && result.stdout.length > 0) return { ok: false, reason: \"tmux-session-missing\" };"
471
+ ],
472
+ "gate": ["bash", "run.sh", "check-mux-fresh-call"],
473
+ "timeoutSeconds": 180,
474
+ "signature": "[QK:FRESHCALL-PLACEMENT-MISSING-REJECTS]",
475
+ "signatureSource": "test/mux-fresh-call.test.ts"
476
+ },
477
+ {
478
+ "claim": "FRESHCALL-PLACEMENT-TARGET-ARGV",
479
+ "title": "the launch argv targets the caller's own session again — a seated fresh call silently opens beside the caller instead of in the operator's project session, and the receipt still looks right",
480
+ "subject": "pi-extensions/lib/mux-fresh-call.ts",
481
+ "find": ["\t\t`${targetSessionId}:{end}`,"],
482
+ "replace": ["\t\t`$0:{end}`,"],
483
+ "gate": ["bash", "run.sh", "check-mux-fresh-call"],
484
+ "timeoutSeconds": 180,
485
+ "signature": "[QK:FRESHCALL-PLACEMENT-TARGET-ARGV]",
486
+ "signatureSource": "test/mux-fresh-call.test.ts"
487
+ },
488
+ {
489
+ "claim": "FRESHCALL-PLACEMENT-BACKEND-PARITY",
490
+ "title": "the project seat becomes a pi capability — the grammar refusal is gated on the backend, so claude-code, copilot and omp fall past the seat check into runtime resolution and answer with the wrong reason. Every seat assertion that existed when #105 landed was written against backend pi alone, so nothing would have noticed",
491
+ "subject": "pi-extensions/lib/mux-fresh-call.ts",
492
+ "find": ["\t\tif (badSeat) return { ok: false, reason: badSeat };"],
493
+ "replace": ["\t\tif (badSeat && params.backend === \"pi\") return { ok: false, reason: badSeat };"],
494
+ "gate": ["bash", "run.sh", "check-mux-fresh-call"],
495
+ "timeoutSeconds": 180,
496
+ "signature": "[QK:FRESHCALL-PLACEMENT-BACKEND-PARITY]",
497
+ "signatureSource": "test/mux-fresh-call.test.ts"
498
+ },
499
+ {
500
+ "claim": "FRESHCALL-PLACEMENT-RECEIPT-TARGET",
501
+ "title": "the receipt copies the CALLER's session id again — the window is in the seat, the receipt names the caller's session, and every later close/lookup built on that receipt addresses the wrong session. Proof is in two halves: this mutant dies on the source-text assembly assert in the fast lane, and the behavioural oracle independent of that source is the real-tmux seat cell in check-mux-launch-tmux (receipt.sessionId === seatId !== callerId, read from the server's own inventory)",
502
+ "subject": "pi-extensions/lib/mux-fresh-call.ts",
503
+ "find": ["\t\t\tsessionId: targetSessionId,"],
504
+ "replace": ["\t\t\tsessionId: placement.sessionId,"],
505
+ "gate": ["bash", "run.sh", "check-mux-fresh-call"],
506
+ "timeoutSeconds": 180,
507
+ "signature": "[QK:FRESHCALL-PLACEMENT-RECEIPT-TARGET]",
508
+ "signatureSource": "test/mux-fresh-call.test.ts"
509
+ },
510
+ {
511
+ "claim": "FRESHCALL-PLACEMENT-SURFACE-PARITY",
512
+ "title": "one surface drops the optional seat from its schema, so opening a sibling in the operator's project session works from pi and silently cannot exist from the MCP bridge",
513
+ "subject": "mcp/entwurf-bridge/src/index.ts",
514
+ "find": [
515
+ "\t\tplacement: z\n\t\t\t.object({\n\t\t\t\ttmuxSession: z\n\t\t\t\t\t.string()\n\t\t\t\t\t.describe(\n\t\t\t\t\t\t\"EXACT name of an EXISTING session on this agent's own tmux server. Nothing is created: an absent session is refused as tmux-session-missing, and a name outside [A-Za-z0-9][A-Za-z0-9_-]* as tmux-session-name-invalid.\",\n\t\t\t\t\t),\n\t\t\t})\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t\"Optional project seat: open the sibling in ONE EXISTING tmux session of this agent's own server instead of the caller's session. Nothing is ever created — an absent session is a refusal, not a new session. Independent of cwd; neither is inferred from the other. The receipt echoes the REQUESTED name and reports the resolved target session id.\",\n\t\t\t),\n"
516
+ ],
517
+ "replace": [""],
518
+ "gate": ["bash", "run.sh", "check-mux-fresh-call"],
519
+ "timeoutSeconds": 180,
520
+ "signature": "[QK:FRESHCALL-PLACEMENT-SURFACE-PARITY]",
521
+ "signatureSource": "test/fresh-call-surfaces.contract.test.ts"
441
522
  }
442
523
  ]
443
524
  }
@@ -158,16 +158,29 @@
158
158
  },
159
159
  {
160
160
  "claim": "OMP-DOCTOR-CARRIER-TRIMS",
161
- "title": "treating an empty or whitespace-only PI carrier as an inherited identity makes Bundle C's deliberate tmux `-e NAME=` scrub fail the deterministic floor whenever a visible fresh omp citizen is alive",
161
+ "title": "treating an empty or whitespace-only PI carrier as an inherited identity makes Bundle C's deliberate tmux `-e NAME=` scrub fail the deterministic floor whenever a visible fresh omp citizen is alive. Re-anchored when the carrier scan moved from an early-exiting `grep -q` to `grep -c` (the SIGPIPE/pipefail repair); the replanted defect is unchanged — only the line it attaches to moved",
162
162
  "subject": "scripts/omp-bridge-doctor.sh",
163
163
  "find": [
164
- " if tr '\\0' '\\n' < \"$ENVIRON\" 2>/dev/null | grep -qE '^(PI_SESSION_ID|PI_AGENT_ID)=.*[^[:space:]]'; then"
164
+ " HITS=\"$(tr '\\0' '\\n' < \"$ENVIRON\" 2>/dev/null | grep -cE '^(PI_SESSION_ID|PI_AGENT_ID)=.*[^[:space:]]' || true)\""
165
+ ],
166
+ "replace": [
167
+ " HITS=\"$(tr '\\0' '\\n' < \"$ENVIRON\" 2>/dev/null | grep -cE '^(PI_SESSION_ID|PI_AGENT_ID)=' || true)\""
165
168
  ],
166
- "replace": [" if tr '\\0' '\\n' < \"$ENVIRON\" 2>/dev/null | grep -qE '^(PI_SESSION_ID|PI_AGENT_ID)='; then"],
167
169
  "gate": ["bash", "run.sh", "smoke-omp-bridge-state"],
168
170
  "timeoutSeconds": 300,
169
171
  "signature": "[QK:OMP-DOCTOR-CARRIER-TRIMS]",
170
172
  "signatureSource": "scripts/smoke-omp-bridge-state.sh"
173
+ },
174
+ {
175
+ "claim": "OMP-DOCTOR-CARRIER-NONBLANK-IS-RED",
176
+ "title": "the doctor stops flagging a live omp process that carries a NONBLANK inherited PI_SESSION_ID/PI_AGENT_ID, so a session whose MCP children would speak under the parent pi garden id reads as clean and the doctor prints PASS. The real defect was subtler than this replant, which is why the replant is written this way: `grep -q` exited at the first match, SIGPIPEd `tr` mid-environ, and pipefail turned a MATCH into a failed pipeline — a race on how far `tr` got, misreading ~15% of runs, so a `-q` replant would SURVIVE most of the time and prove nothing. This one makes the same false-clean deterministic",
177
+ "subject": "scripts/omp-bridge-doctor.sh",
178
+ "find": [" if [ \"${HITS:-0}\" -gt 0 ]; then"],
179
+ "replace": [" if [ \"${HITS:-0}\" -gt 99 ]; then"],
180
+ "gate": ["bash", "run.sh", "smoke-omp-bridge-state"],
181
+ "timeoutSeconds": 180,
182
+ "signature": "[QK:OMP-DOCTOR-CARRIER-NONBLANK-IS-RED]",
183
+ "signatureSource": "scripts/smoke-omp-bridge-state.sh"
171
184
  }
172
185
  ]
173
186
  }
@@ -266,7 +266,17 @@ if [ -d /proc ]; then
266
266
  case "$pid" in *[!0-9]*) continue ;; esac
267
267
  ENVIRON="/proc/$pid/environ"
268
268
  [ -r "$ENVIRON" ] || continue
269
- if tr '\0' '\n' < "$ENVIRON" 2>/dev/null | grep -qE '^(PI_SESSION_ID|PI_AGENT_ID)=.*[^[:space:]]'; then
269
+ # `grep -c`, never `grep -q`. This file runs under `set -o pipefail` (line 26), and a
270
+ # `-q` grep EXITS AT THE FIRST MATCH, closing the pipe while `tr` is still writing the
271
+ # rest of an ~12KB environ. `tr` dies of SIGPIPE (141), pipefail promotes that to the
272
+ # pipeline's status, and the `if` takes the CLEAN branch — so a genuinely contaminated
273
+ # process was reported as clean, and the doctor printed PASS. It is a RACE on how far
274
+ # `tr` got, which is why it read as flakiness: measured on this host, 6 misses in 40
275
+ # reads of one live fixture carrying PI_SESSION_ID=foreign-garden. `-c` consumes all of
276
+ # stdin, so there is no early close and nothing to race. The same lesson is already
277
+ # written at smoke-omp-mcp-state.sh:91 — it had not reached this reader.
278
+ HITS="$(tr '\0' '\n' < "$ENVIRON" 2>/dev/null | grep -cE '^(PI_SESSION_ID|PI_AGENT_ID)=.*[^[:space:]]' || true)"
279
+ if [ "${HITS:-0}" -gt 0 ]; then
270
280
  CONTAMINATED="$CONTAMINATED $pid"
271
281
  fi
272
282
  done
@@ -2,7 +2,9 @@
2
2
  * smoke-mux-fresh-call-live — the ONE axis a deterministic gate cannot reach: a real window, a
3
3
  * real runtime, a real first turn, and a real callback arriving on a real inbound surface.
4
4
  *
5
- * OUT of `pnpm check`. Needs `LIVE=1`. Costs two model turns (one pi, one Claude Code).
5
+ * OUT of `pnpm check`. Needs `LIVE=1`. Costs FOUR model turns (three pi, one Claude Code): two
6
+ * open in the caller's own session, and #105 added two more in a second SEAT session on the same
7
+ * private server so the seat x cwd matrix is read on real panes rather than argued about.
6
8
  *
7
9
  * ── Isolate the WRITES, keep the runtimes real ──
8
10
  *
@@ -59,6 +61,17 @@ const REAL_CONTROL_DIR = path.join(REAL_HOME, ".pi", "entwurf-control");
59
61
  // so the real-HOME Claude cell can be given exact operator-env parity (issue #67).
60
62
  const ORIGINAL_XDG = snapshotOriginalXdg(process.env);
61
63
 
64
+ // The #105 seat fixture. A second session on the SAME private server, its own session_path, and
65
+ // two distinct requested cwds — three directories that must never be confusable with each other.
66
+ const SEAT_SESSION = "seat";
67
+ const SEAT_HOME = (root: string): string => path.join(root, "seat-home");
68
+ const CROSS_REPO_A = (root: string): string => path.join(root, "cross-repo-a");
69
+ const CROSS_REPO_B = (root: string): string => path.join(root, "cross-repo-b");
70
+ /** tmux fills `#{pane_current_path}` with the SERVER's cwd at creation time and only becomes
71
+ * true 0.2–0.6s later (measured ×2, #105 R1) — so acceptance reads it after a delay, never from
72
+ * the launch receipt. */
73
+ const PANE_CWD_SETTLE_SECONDS = "3";
74
+
62
75
  let passed = 0;
63
76
  function ok(label: string, cond: boolean): void {
64
77
  if (!cond) throw new Error(`${LABEL}: FAILED — ${label}`);
@@ -89,6 +102,13 @@ function tmux(socket: string, args: string[], env: NodeJS.ProcessEnv): { status:
89
102
  return { status: r.status, stdout: (r.stdout ?? "").trim() };
90
103
  }
91
104
 
105
+ /** The session the caller's own anchor pane sits in — tmux's answer, never a name we assembled. */
106
+ function inspectedSessionId(socket: string, env: NodeJS.ProcessEnv): string {
107
+ return tmux(socket, ["display-message", "-p", "-t", String(env.TMUX_PANE), "#{session_id}"], env).stdout.split(
108
+ "\n",
109
+ )[0];
110
+ }
111
+
92
112
  function pidIsAlive(pid: number): boolean {
93
113
  try {
94
114
  process.kill(pid, 0);
@@ -150,6 +170,10 @@ async function main(): Promise<void> {
150
170
  };
151
171
  for (const dir of Object.values(fenced)) fs.mkdirSync(dir, { recursive: true });
152
172
  fs.mkdirSync(scratch, { recursive: true });
173
+ // The #105 seat cells need three directories that are all DIFFERENT from each other and from
174
+ // the scratch cwd, so "the pane landed here" can only be true for one reason. `seatHome` is
175
+ // the second session's own `session_path` — the candidate the cwd must NOT come from.
176
+ for (const dir of [SEAT_HOME(root), CROSS_REPO_A(root), CROSS_REPO_B(root)]) fs.mkdirSync(dir, { recursive: true });
153
177
  // A runtime dir is a private surface by convention and tmux checks it.
154
178
  fs.chmodSync(fenced.XDG_RUNTIME_DIR, 0o700);
155
179
  for (const [k, v] of Object.entries(fenced)) process.env[k] = v;
@@ -160,7 +184,7 @@ async function main(): Promise<void> {
160
184
  // Import the meta layer only AFTER the redirects — a module that resolved its roots at import
161
185
  // time would have captured the operator's.
162
186
  const meta = await import("../pi-extensions/lib/meta-session.ts");
163
- const { freshCall } = await import("../pi-extensions/lib/mux-fresh-call.ts");
187
+ const { freshCall, renderFreshCall } = await import("../pi-extensions/lib/mux-fresh-call.ts");
164
188
 
165
189
  const siblingGids = new Set<string>();
166
190
  const siblingPids = new Set<number>();
@@ -223,6 +247,11 @@ async function main(): Promise<void> {
223
247
  // onboarding instead of exercising the configured runtime. Separate servers preserve
224
248
  // those backend-native environments without adding an env carrier to the product.
225
249
  const nonces = new Map<string, string>();
250
+ /** Every launched cell's pane, with the directory the pane is REQUIRED to have settled in
251
+ * — read after a delay, which is the only honest way to observe it (see the constant). */
252
+ const cwdCells: Array<{ label: string; socket: string; env: NodeJS.ProcessEnv; paneId: string; expect: string }> =
253
+ [];
254
+ const servers = new Map<string, { socket: string; inherited: NodeJS.ProcessEnv }>();
226
255
  // Intentional: pi + claude-code only. Copilot clause-7 LIVE is operator-metered
227
256
  // and is not a release MUST — do not add `copilot` to this loop (VERIFY.md).
228
257
  for (const backend of ["pi", "claude-code"] as const) {
@@ -248,12 +277,17 @@ async function main(): Promise<void> {
248
277
  const anchorPane = tmux(socket, ["display-message", "-p", "-t", "fixture:anchor", "#{pane_id}"], env).stdout;
249
278
  const inherited: NodeJS.ProcessEnv = { ...env, TMUX: `${socket},0,0`, TMUX_PANE: anchorPane };
250
279
  ok(`${backend}: tmux server is a private socket, never the operator's`, String(inherited.TMUX).startsWith(root));
280
+ servers.set(backend, { socket, inherited });
251
281
 
282
+ // The caller-session half of the #105 cwd matrix: pi omits the cwd, claude-code names
283
+ // one. Both open in the CALLER's own session, which is the pre-#105 behaviour.
284
+ const requestedCwd = backend === "claude-code" ? CROSS_REPO_A(root) : undefined;
252
285
  const result = freshCall(
253
286
  {
254
287
  backend,
255
288
  model: LIVE_MODEL[backend],
256
289
  task: "Reply with the single word ACK and then stop. Do not read files.",
290
+ cwd: requestedCwd,
257
291
  callerGardenId: callerGid,
258
292
  },
259
293
  inherited,
@@ -262,6 +296,13 @@ async function main(): Promise<void> {
262
296
  if (!result.ok) return;
263
297
  nonces.set(backend, result.receipt.nonce);
264
298
  siblingPids.add(Number(result.receipt.panePid));
299
+ cwdCells.push({
300
+ label: `${backend} @caller-session cwd=${requestedCwd === undefined ? "(omitted)" : "requested"}`,
301
+ socket,
302
+ env: inherited,
303
+ paneId: result.receipt.paneId,
304
+ expect: requestedCwd ?? scratch,
305
+ });
265
306
  ok(
266
307
  `${backend}: the receipt carries tmux coordinates, requested model and nonce, and nothing about delivery`,
267
308
  Boolean(
@@ -271,6 +312,113 @@ async function main(): Promise<void> {
271
312
  result.receipt.nonce,
272
313
  ) && !("gardenId" in result.receipt),
273
314
  );
315
+ ok(
316
+ `${backend}: with no seat requested the window is in the CALLER's own session and the receipt names no seat`,
317
+ result.receipt.sessionId === inspectedSessionId(socket, inherited) && result.receipt.tmuxSession === undefined,
318
+ );
319
+ }
320
+
321
+ // ── #105 acceptance: the optional project seat ───────────────────────────
322
+ // One extra session on the SAME private pi server (R6: a second session is one line and
323
+ // costs no extra server). Its own `-c` gives it a session_path that is neither the
324
+ // caller's cwd nor either requested cwd, so the cwd matrix below can only pass for one
325
+ // reason.
326
+ const pi = servers.get("pi");
327
+ if (!pi) throw new Error(`${LABEL}: the pi fixture server was not captured`);
328
+ const callerSessionId = inspectedSessionId(pi.socket, pi.inherited);
329
+ if (
330
+ tmux(pi.socket, ["new-session", "-d", "-s", SEAT_SESSION, "-c", SEAT_HOME(root), "-n", "anchor"], pi.inherited)
331
+ .status !== 0
332
+ ) {
333
+ throw new Error(`${LABEL}: could not create the second fixture session ${SEAT_SESSION}`);
334
+ }
335
+ const seatSessionId = tmux(
336
+ pi.socket,
337
+ ["list-windows", "-t", `=${SEAT_SESSION}`, "-F", "#{session_id}"],
338
+ pi.inherited,
339
+ ).stdout.split("\n")[0];
340
+ ok(
341
+ "seat: the fixture now holds two sessions on ONE server, and the seat is not the caller's",
342
+ /^\$[0-9]+$/.test(seatSessionId) && seatSessionId !== callerSessionId,
343
+ );
344
+
345
+ // Acceptance 2 FIRST, because it is the one that must leave nothing behind. Both refusals
346
+ // are read against a byte-identical session/window inventory.
347
+ const inventory = (): string =>
348
+ tmux(pi.socket, ["list-windows", "-a", "-F", "#{session_id}|#{window_id}"], pi.inherited).stdout;
349
+ const beforeRefusals = inventory();
350
+ for (const [label, seat, reason] of [
351
+ ["absent", "nosuchseat", "tmux-session-missing"],
352
+ ["unresolvable", "no.such.seat", "tmux-session-name-invalid"],
353
+ ] as const) {
354
+ const refused = freshCall(
355
+ {
356
+ backend: "pi",
357
+ model: LIVE_MODEL.pi,
358
+ task: "This task must never run.",
359
+ placement: { tmuxSession: seat },
360
+ callerGardenId: callerGid,
361
+ },
362
+ pi.inherited,
363
+ );
364
+ ok(
365
+ `seat: an ${label} seat is refused as ${reason}, and NOTHING was created`,
366
+ !refused.ok && refused.reason === reason && inventory() === beforeRefusals,
367
+ );
368
+ const rendered = renderFreshCall(refused);
369
+ ok(
370
+ `seat: the ${label} refusal says so in the operator's words`,
371
+ rendered.isError && rendered.text.includes("No window was opened"),
372
+ );
373
+ }
374
+
375
+ // Acceptance 1 + the other half of the cwd matrix: two seated pi siblings, one with the
376
+ // cwd omitted and one with a requested cwd.
377
+ for (const [label, requestedCwd] of [
378
+ ["seat cwd=(omitted)", undefined],
379
+ ["seat cwd=requested", CROSS_REPO_B(root)],
380
+ ] as const) {
381
+ const callerWindowsBefore = tmux(
382
+ pi.socket,
383
+ ["list-windows", "-t", callerSessionId, "-F", "#{window_id}"],
384
+ pi.inherited,
385
+ ).stdout;
386
+ const seated = freshCall(
387
+ {
388
+ backend: "pi",
389
+ model: LIVE_MODEL.pi,
390
+ task: "Reply with the single word ACK and then stop. Do not read files.",
391
+ cwd: requestedCwd,
392
+ placement: { tmuxSession: SEAT_SESSION },
393
+ callerGardenId: callerGid,
394
+ },
395
+ pi.inherited,
396
+ );
397
+ ok(`${label}: launch receipt is ok`, seated.ok);
398
+ if (!seated.ok) return;
399
+ nonces.set(label, seated.receipt.nonce);
400
+ siblingPids.add(Number(seated.receipt.panePid));
401
+ cwdCells.push({
402
+ label: `pi @${label}`,
403
+ socket: pi.socket,
404
+ env: pi.inherited,
405
+ paneId: seated.receipt.paneId,
406
+ expect: requestedCwd ?? scratch,
407
+ });
408
+ ok(
409
+ `${label}: the receipt echoes the REQUESTED seat name and reports the RESOLVED target session, not the caller's`,
410
+ seated.receipt.tmuxSession === SEAT_SESSION &&
411
+ seated.receipt.sessionId === seatSessionId &&
412
+ seated.receipt.sessionId !== callerSessionId,
413
+ );
414
+ ok(
415
+ `${label}: the window really landed in the seat, and the caller's session is byte-identical`,
416
+ tmux(pi.socket, ["list-windows", "-t", seatSessionId, "-F", "#{window_id}"], pi.inherited).stdout.includes(
417
+ seated.receipt.windowId,
418
+ ) &&
419
+ tmux(pi.socket, ["list-windows", "-t", callerSessionId, "-F", "#{window_id}"], pi.inherited).stdout ===
420
+ callerWindowsBefore,
421
+ );
274
422
  }
275
423
 
276
424
  // The sender envelope rides INSIDE the mailbox body (` session: <gid> (…)`) — that line
@@ -295,9 +443,33 @@ async function main(): Promise<void> {
295
443
  if (sender) siblingGids.add(sender);
296
444
  }
297
445
  ok(
298
- "correlation: the two siblings reported DIFFERENT garden ids — the envelope identifies each one, not the launcher",
446
+ "correlation: every sibling reported a DIFFERENT garden id — the envelope identifies each one, not the launcher",
299
447
  siblingGids.size === arrived.size && !siblingGids.has(callerGid),
300
448
  );
449
+
450
+ // ── #105 acceptance 3: the seat and the cwd are ORTHOGONAL ───────────────
451
+ // Read AFTER a delay and from the stable %pane, never from the launch receipt: at
452
+ // creation time `#{pane_current_path}` reports the tmux SERVER's cwd in every cell
453
+ // (measured ×2), which is why the receipt reports the REQUESTED cwd and this is the
454
+ // acceptance that reports the observed one. The 2×2 is {cwd omitted, cwd requested} ×
455
+ // {caller session, seat}, and the seat's own `session_path` is a fourth directory that
456
+ // no cell may ever land in.
457
+ spawnSync("sleep", [PANE_CWD_SETTLE_SECONDS]);
458
+ for (const cell of cwdCells) {
459
+ const observed = tmux(
460
+ cell.socket,
461
+ ["display-message", "-p", "-t", cell.paneId, "#{pane_current_path}"],
462
+ cell.env,
463
+ ).stdout;
464
+ ok(
465
+ `cwd x seat: ${cell.label} settled in ${cell.expect} — never the target session's path, never the other cell's`,
466
+ observed === cell.expect && observed !== SEAT_HOME(root),
467
+ );
468
+ }
469
+ ok(
470
+ "cwd x seat: all four cells ran — {cwd omitted, cwd requested} x {caller session, seat}",
471
+ cwdCells.length === 4 && new Set(cwdCells.map((c) => c.expect)).size === 3,
472
+ );
301
473
  ok(
302
474
  "fence: those sibling records live in the FIXTURE store",
303
475
  [...siblingGids].every(
@@ -22,7 +22,10 @@ wait_carrier() {
22
22
  local pid="$1" key="$2" i=0
23
23
  while [ "$i" -lt 50 ]; do
24
24
  if kill -0 "$pid" 2>/dev/null && [ -r "/proc/$pid/environ" ]; then
25
- if tr '\0' '\n' < "/proc/$pid/environ" 2>/dev/null | grep -q "^${key}"; then
25
+ # `-c` not `-q`, for the reason in omp-bridge-doctor.sh's carrier scan: under
26
+ # pipefail an early-exiting grep SIGPIPEs `tr` and the pipeline reports failure even
27
+ # on a match. Here that only costs a retry, but the hazard is the same one.
28
+ if [ "$(tr '\0' '\n' < "/proc/$pid/environ" 2>/dev/null | grep -cE "^${key}" || true)" -gt 0 ]; then
26
29
  return 0
27
30
  fi
28
31
  fi
@@ -103,7 +106,7 @@ CARRIER_PID=$!
103
106
  wait_carrier "$CARRIER_PID" "PI_SESSION_ID=foreign-garden"
104
107
  if OUT="$(ENTWURF_OMP_CARRIER_PIDS="$CARRIER_PID" "$RUN" doctor-omp-bridge 2>&1)"; then
105
108
  printf '%s\n' "$OUT" >&2
106
- die "doctor accepted the live omp fixture with nonblank inherited PI carriers"
109
+ die "[QK:OMP-DOCTOR-CARRIER-NONBLANK-IS-RED] doctor accepted the live omp fixture with nonblank inherited PI carriers"
107
110
  fi
108
111
  printf '%s\n' "$OUT" | grep -q "live omp process(es).* $CARRIER_PID" || die "doctor red without naming the nonblank-carrier fixture"
109
112
  ok "a nonblank inherited PI carrier on a live omp process remains red"