spike(harness): probe gate behavior in pi --mode rpc + findings

Static analysis of Pi runtime (rpc-mode.js, agent-session.js, runner.js)
confirms all 4 decisive questions:
- Q1 kickoff: NO — source:"rpc" bypasses the "interactive" guard (agent-session.js:720)
- Q2 widget emission: NO — ctx.sendRaw does not exist, crashes (0 refs in core/extensions/)
- Q3 response routing: NO — rpc-mode.js returns after consuming extension_ui_response;
  pi.on("extension_ui_response") never fires (not in runner.js)
- Q4 steering: YES — steer() bypasses emitInput entirely

Decision: both Task 3 (kickoff guard) and Task 4 (emitAndWait → ctx.ui.* / Variant A)
require gate adaptation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-27 19:38:04 +02:00
co-authored by Claude Sonnet 4.6
parent c9c150c942
commit 59bd6135e2
2 changed files with 292 additions and 0 deletions
+35
View File
@@ -0,0 +1,35 @@
// rpc_probe.mjs — manual probe: drive `pi --mode rpc` and observe the gate.
// Usage (from harness/, with .env loaded + VPN up): node scripts/rpc_probe.mjs
import { spawn } from "node:child_process";
const pi = spawn("pi", ["--mode", "rpc"], { cwd: process.cwd(), env: process.env });
const send = (obj) => {
const line = JSON.stringify(obj) + "\n";
process.stdout.write(`>>> SEND ${line}`);
pi.stdin.write(line);
};
let buf = "";
pi.stdout.on("data", (chunk) => {
buf += chunk.toString("utf8");
for (let nl; (nl = buf.indexOf("\n")) !== -1; ) {
const line = buf.slice(0, nl);
buf = buf.slice(nl + 1);
if (!line.trim()) continue;
process.stdout.write(`<<< RECV ${line}\n`);
let msg;
try { msg = JSON.parse(line); } catch { continue; }
if (msg.type === "extension_ui_request") {
// Reply in BOTH the gate's expected shape and Pi's native shape; observe which one unblocks.
const id = msg.id ?? msg.ui_request?.id;
send({ type: "extension_ui_response", id, control: "freetext", text: "PROBE-ANSWER" });
}
}
});
pi.stderr.on("data", (d) => process.stdout.write(`!!! STDERR ${d}`));
pi.on("exit", (code) => console.log(`### pi exited ${code}`));
// Kick off the workflow via an RPC prompt command (NOT interactive keystrokes).
setTimeout(() => send({ type: "prompt", message: '/nuova-domanda "quante cardioversioni nel 2024"' }), 500);
setTimeout(() => { pi.stdin.end(); }, 60000);