SLOPSHOPPER

qe-account-cell-feasibility

Zero-auth, zero-inference account-cell broker feasibility spike

newguardcommandprompttoolprocess
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · qe-account-cell-feasibility
› fix the failing auth test and add an audit log call ⏺ Read(src/auth.ts) ⎿ Read 6 lines ⏺ Update(src/auth.ts) ⎿ Added 2 lines, removed 1 line ⏺ Read(/work/app/src/auth.ts) ⎿ Denied by qe-account-cell-feasibility: QE account-cell Mod is not ready. ● Done. refresh now rejects expired claims and logs an audit event. ✻ Worked for 42s · done 4:20 PM ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts
README

Claude account-cell feasibility spike

This is a zero-auth, zero-inference, non-production fixture for docs/claude-account-cell-feasibility.md. It targets the exact Claude Code 2.1.292 binary bundled through @anthropic-ai/claude-agent-sdk@0.3.292.

The retained Mod demonstrates a broker-only model surface:

  • eight QE-owned workspace/control tools are registered under new names;
  • native Read, Write, Edit, Bash, search, agent, web, MCP, and other account-cell tools are not delegated to core;
  • every workspace operation reaches one fixed bridge executable as bounded JSON on stdin, never as model-selected account-cell argv;
  • the Mod contains no $.fs, $.http, $.model, $.tool.call, $.agent.spawn, or $.prompt.submit call;
  • subagents, unapproved Mods, unknown local commands, unknown tools, malformed broker input, and policy failures fail closed; and
  • completion and durable HumanAttention remain semantic control operations.

The fake Worker/account/Run model demonstrates workspace and symlink containment, shell namespace separation on macOS, exact pre-bound routing, native-session ownership, generation and Run-recovery fencing, logout quiescence, restart/cleanup separation, a one-way network topology, and serialized canonical fake credential mutation. The credential fixture is an explicit canary and is not a provider credential.

Validate

Using the exact retained Claude Code binary:

.pi/tmp/claude-mods-reassessment/packages/darwin/claude \
  plugin validate spikes/claude-account-cell-feasibility

workers/bun/node_modules/.bin/tsc \
  -p spikes/claude-account-cell-feasibility/tsconfig.json --noEmit

.pi/tmp/claude-mods-reassessment/packages/darwin/claude \
  plugin test spikes/claude-account-cell-feasibility

workers/bun/node_modules/.bin/tsc \
  -p spikes/claude-account-cell-feasibility/proof-tsconfig.json --noEmit

bun test ./spikes/claude-account-cell-feasibility/proof.check.ts

Expected results for the pinned fixture are 12 Mod tests and 12 fake architecture tests with no failures. claude plugin test uses mocked engine events and operations and does not sign in or contact a model.

The safe local startup sequence first sends a fixed synthetic stream-input message with shouldQuery: false, validates system/init and its zero-turn result, and only then sends /qe_account_cell_probe to the already validated Mod. With an empty disposable CLAUDE_CONFIG_DIR, --restricted, and an exact --tools list, both stages reported num_turns: 0, total_cost_usd: 0, empty modelUsage, the eight registered tools, and matching Mod/CLI session IDs while the process remained alive. Sending the slash command before proving that the Mod loaded is unsafe. The report records the exact evidence and production constraints.

Deliberate limits

This retained fixture does not itself implement ProviderAccount state, a real broker, Claude v4, a profile, authentication, or provider inference. The production generic broker, strict local protocol, cancellation/certainty, and physical two-SBX evidence now live in docs/workspace-tool-broker.md and the Worker/server implementation. Managed Mod integration and all provider behavior remain later gates; this spike stays a zero-auth research fixture.

Source 1 files
hooks/register.ts 533 lines
1import type {
2	EngineInterface,
3	Next,
4	PluginRegisterInput,
5	Register,
6	TurnStepInput,
7} from "claude-code";
8
9const PROTOCOL = "qe_claude_account_cell_feasibility_v1";
10const BRIDGE = "/opt/qe/bin/qe-account-cell-bridge";
11const MAX_BRIDGE_OUTPUT = 65_536;
12const MAX_OPERATION_INPUT = 1_048_576;
13
14const PREFIX = "mcp__qe-account-cell-feasibility__";
15const TOOLS = {
16	read: `${PREFIX}qe_workspace_read`,
17	list: `${PREFIX}qe_workspace_list`,
18	write: `${PREFIX}qe_workspace_write`,
19	edit: `${PREFIX}qe_workspace_edit`,
20	search: `${PREFIX}qe_workspace_search`,
21	shell: `${PREFIX}qe_run_shell`,
22	complete: `${PREFIX}qe_complete_step`,
23	attention: `${PREFIX}qe_request_human_assistance_and_wait`,
24} as const;
25const ALLOWED_TOOLS = new Set<string>(Object.values(TOOLS));
26
27type JsonObject = Record<string, unknown>;
28
29function object(value: unknown, label: string): JsonObject {
30	if (typeof value !== "object" || value === null || Array.isArray(value))
31		throw new Error(`${label} must be an object.`);
32	return value as JsonObject;
33}
34
35function boundedString(value: unknown, label: string, maximum: number): string {
36	if (typeof value !== "string" || value.length === 0 || value.length > maximum)
37		throw new Error(`${label} is invalid.`);
38	return value;
39}
40
41function optionalInteger(
42	value: unknown,
43	label: string,
44	minimum: number,
45	maximum: number,
46): number | undefined {
47	if (value === undefined) return undefined;
48	if (
49		typeof value !== "number" ||
50		!Number.isSafeInteger(value) ||
51		value < minimum ||
52		value > maximum
53	)
54		throw new Error(`${label} is invalid.`);
55	return value;
56}
57
58function workspacePath(value: unknown, label: string): string {
59	const path = boundedString(value, label, 4_096);
60	if (
61		path.startsWith("/") ||
62		path.includes("\\") ||
63		path.includes("\0") ||
64		path.split("/").some((segment) => segment === "..")
65	)
66		throw new Error(`${label} must remain relative to the Run workspace.`);
67	return path;
68}
69
70function boundedJson(value: unknown, label: string, maximum: number): string {
71	const encoded = JSON.stringify(value);
72	if (encoded === undefined || encoded.length > maximum)
73		throw new Error(`${label} is invalid or too large.`);
74	return encoded;
75}
76
77function parseBridgeOutput(text: string): JsonObject {
78	if (text.length > MAX_BRIDGE_OUTPUT)
79		throw new Error("QE broker output exceeded the spike limit.");
80	return object(JSON.parse(text), "QE broker output");
81}
82
83function parseEffort(value: string): NonNullable<TurnStepInput["effort"]> {
84	switch (value) {
85		case "low":
86		case "medium":
87		case "high":
88		case "xhigh":
89		case "max":
90			return value;
91		default:
92			throw new Error("QE_CLAUDE_EFFORT is invalid.");
93	}
94}
95
96async function runBridge(
97	$: EngineInterface,
98	operation: JsonObject,
99): Promise<JsonObject> {
100	const input = boundedJson(
101		operation,
102		"QE broker operation",
103		MAX_OPERATION_INPUT,
104	);
105	const execution = await $.process.run([BRIDGE, "bound-operation"], {
106		stdin: input,
107		timeoutMs: 600_000,
108	});
109	if (
110		execution.exitCode !== 0 ||
111		execution.isStdoutTruncated ||
112		execution.isStderrTruncated
113	)
114		throw new Error(`QE broker failed with exit code ${execution.exitCode}.`);
115	return parseBridgeOutput(execution.stdout);
116}
117
118async function waitForHuman(
119	$: EngineInterface,
120	operation: JsonObject,
121): Promise<JsonObject> {
122	const input = boundedJson(
123		operation,
124		"QE HumanAttention operation",
125		MAX_OPERATION_INPUT,
126	);
127	const stream = $.process.spawn({
128		argv: [BRIDGE, "bound-human-attention-wait"],
129		input,
130	});
131	let stdout = "";
132	let stderr = "";
133	let item = await stream.next();
134	while (item.done !== true) {
135		if (item.value.stream === "stdout") stdout += item.value.text;
136		else stderr += item.value.text;
137		if (stdout.length + stderr.length > MAX_BRIDGE_OUTPUT) {
138			await stream.return({ code: null, signal: "SIGTERM" });
139			throw new Error("QE HumanAttention output exceeded the spike limit.");
140		}
141		item = await stream.next();
142	}
143	if (item.value.code !== 0 || item.value.signal !== null)
144		throw new Error("QE HumanAttention bridge did not exit successfully.");
145	return parseBridgeOutput(stdout);
146}
147
148async function checkPlugin(
149	_$: EngineInterface,
150	event: PluginRegisterInput,
151	next: Next<"plugin.register">,
152) {
153	if (event.tier === "user")
154		return {
155			refuse:
156				"QE account-cell policy permits only immutable organization and built-in Mods.",
157		} as const;
158	return next(event);
159}
160
161function brokerOperation(tool: string, event: JsonObject): JsonObject {
162	switch (tool) {
163		case TOOLS.read:
164			return {
165				type: "workspace_read",
166				path: workspacePath(event.path, "path"),
167				...(optionalInteger(event.offset, "offset", 0, 10_000_000) === undefined
168					? {}
169					: { offset: event.offset }),
170				...(optionalInteger(event.limit, "limit", 1, 100_000) === undefined
171					? {}
172					: { limit: event.limit }),
173			};
174		case TOOLS.list:
175			return {
176				type: "workspace_list",
177				path: workspacePath(event.path ?? ".", "path"),
178			};
179		case TOOLS.write:
180			return {
181				type: "workspace_write",
182				path: workspacePath(event.path, "path"),
183				content: boundedString(event.content, "content", 262_144),
184			};
185		case TOOLS.edit:
186			return {
187				type: "workspace_edit",
188				path: workspacePath(event.path, "path"),
189				oldText: boundedString(event.oldText, "oldText", 262_144),
190				newText:
191					typeof event.newText === "string" && event.newText.length <= 262_144
192						? event.newText
193						: (() => {
194								throw new Error("newText is invalid.");
195							})(),
196				replaceAll: event.replaceAll === true,
197			};
198		case TOOLS.search:
199			return {
200				type: "workspace_search",
201				query: boundedString(event.query, "query", 4_096),
202				path: workspacePath(event.path ?? ".", "path"),
203				...(optionalInteger(event.maxResults, "maxResults", 1, 1_000) ===
204				undefined
205					? {}
206					: { maxResults: event.maxResults }),
207			};
208		case TOOLS.shell:
209			return {
210				type: "run_shell",
211				command: boundedString(event.command, "command", 32_768),
212				...(optionalInteger(event.timeoutMs, "timeoutMs", 1, 600_000) ===
213				undefined
214					? {}
215					: { timeoutMs: event.timeoutMs }),
216			};
217		case TOOLS.complete:
218			return {
219				type: "complete_step",
220				outputs: object(event.outputs, "outputs"),
221			};
222		default:
223			throw new Error("The tool is not a synchronous QE broker operation.");
224	}
225}
226
227export const register: Register = (on) => {
228	let ready = false;
229	let expectedModel = "";
230	let expectedEffort: NonNullable<TurnStepInput["effort"]> | "" = "";
231	let nativeSessionId = "";
232
233	on("plugin.register", checkPlugin).catch(async (_$, event, next) => {
234		if (event.tier === "user")
235			return {
236				refuse: "QE failed closed while checking an unapproved Mod.",
237			};
238		return next(event);
239	});
240
241	on("session.start", async ($, event, next) => {
242		ready = false;
243		const protocol = await $.env.get("QE_ACCOUNT_CELL_MOD_PROTOCOL");
244		expectedModel = (await $.env.get("QE_CLAUDE_MODEL")) ?? "";
245		expectedEffort = parseEffort((await $.env.get("QE_CLAUDE_EFFORT")) ?? "");
246		nativeSessionId = await $.session.id();
247		if (
248			protocol !== PROTOCOL ||
249			expectedModel.length === 0 ||
250			nativeSessionId.length === 0
251		)
252			throw new Error("QE account-cell Mod environment is incomplete.");
253
254		await $.tool.register({
255			name: "qe_workspace_read",
256			description:
257				"Read a UTF-8 file from the pre-bound Quest Engineering Run workspace. Paths are relative to that workspace; this tool cannot address the Claude account cell.",
258			inputSchema: {
259				type: "object",
260				properties: {
261					path: { type: "string", minLength: 1, maxLength: 4_096 },
262					offset: { type: "integer", minimum: 0, maximum: 10_000_000 },
263					limit: { type: "integer", minimum: 1, maximum: 100_000 },
264				},
265				required: ["path"],
266				additionalProperties: false,
267			},
268		});
269		await $.tool.register({
270			name: "qe_workspace_list",
271			description:
272				"List a directory in the pre-bound Quest Engineering Run workspace. The account-cell filesystem is not in this tool's namespace.",
273			inputSchema: {
274				type: "object",
275				properties: {
276					path: { type: "string", minLength: 1, maxLength: 4_096 },
277				},
278				additionalProperties: false,
279			},
280		});
281		await $.tool.register({
282			name: "qe_workspace_write",
283			description:
284				"Write a UTF-8 file in the pre-bound Quest Engineering Run workspace.",
285			inputSchema: {
286				type: "object",
287				properties: {
288					path: { type: "string", minLength: 1, maxLength: 4_096 },
289					content: { type: "string", minLength: 1, maxLength: 262_144 },
290				},
291				required: ["path", "content"],
292				additionalProperties: false,
293			},
294		});
295		await $.tool.register({
296			name: "qe_workspace_edit",
297			description:
298				"Replace exact text in a file in the pre-bound Quest Engineering Run workspace.",
299			inputSchema: {
300				type: "object",
301				properties: {
302					path: { type: "string", minLength: 1, maxLength: 4_096 },
303					oldText: { type: "string", minLength: 1, maxLength: 262_144 },
304					newText: { type: "string", maxLength: 262_144 },
305					replaceAll: { type: "boolean" },
306				},
307				required: ["path", "oldText", "newText"],
308				additionalProperties: false,
309			},
310		});
311		await $.tool.register({
312			name: "qe_workspace_search",
313			description:
314				"Search only the pre-bound Quest Engineering Run workspace and return bounded matches.",
315			inputSchema: {
316				type: "object",
317				properties: {
318					query: { type: "string", minLength: 1, maxLength: 4_096 },
319					path: { type: "string", minLength: 1, maxLength: 4_096 },
320					maxResults: { type: "integer", minimum: 1, maximum: 1_000 },
321				},
322				required: ["query"],
323				additionalProperties: false,
324			},
325		});
326		await $.tool.register({
327			name: "qe_run_shell",
328			description:
329				"Execute a command only inside the pre-bound credential-free Quest Engineering Run SBX. No command executes in the Claude account cell.",
330			inputSchema: {
331				type: "object",
332				properties: {
333					command: { type: "string", minLength: 1, maxLength: 32_768 },
334					timeoutMs: { type: "integer", minimum: 1, maximum: 600_000 },
335				},
336				required: ["command"],
337				additionalProperties: false,
338			},
339		});
340		await $.tool.register({
341			name: "qe_complete_step",
342			description:
343				"Submit the declared structured outputs to the pre-bound Quest Engineering completion authority.",
344			inputSchema: {
345				type: "object",
346				properties: { outputs: { type: "object" } },
347				required: ["outputs"],
348				additionalProperties: false,
349			},
350		});
351		await $.tool.register({
352			name: "qe_request_human_assistance_and_wait",
353			description:
354				"Create a durable Quest Engineering HumanAttention request and wait for its exact response without opening a local account-cell prompt.",
355			inputSchema: {
356				type: "object",
357				properties: {
358					category: {
359						type: "string",
360						enum: [
361							"needs_input",
362							"needs_permission",
363							"needs_authentication",
364							"needs_confirmation",
365							"blocked_external",
366							"interactive_prompt",
367							"unknown_interactive_block",
368						],
369					},
370					message: { type: "string", minLength: 1, maxLength: 16_384 },
371					interaction: {
372						type: "string",
373						enum: [
374							"confirmation",
375							"text",
376							"choice",
377							"multiline_response",
378							"conversational_intervention",
379						],
380					},
381					responseSchema: { type: "object" },
382				},
383				required: ["category", "message"],
384				additionalProperties: false,
385			},
386		});
387		await $.command.register({
388			name: "qe_account_cell_probe",
389			description: "Report local QE Mod readiness without a model request.",
390		});
391		ready = true;
392		return next(event);
393	});
394
395	on("command.run", { command: "qe_account_cell_probe" }, async () => ({
396		text: JSON.stringify({
397			protocol: PROTOCOL,
398			ready,
399			nativeSessionId,
400			tools: [...ALLOWED_TOOLS].sort(),
401		}),
402	}));
403
404	on("command.run", () => ({
405		text: "QE account-cell policy refused this local command.",
406	})).catch(() => ({
407		text: "QE account-cell command policy failed closed.",
408	}));
409
410	on("agent.offer", () => ({ isOffered: false })).catch(() => ({
411		isOffered: false,
412	}));
413
414	on("agent.spawn", () => ({
415		deny: "QE account-cell policy disables Claude subagents.",
416	})).catch(() => ({
417		deny: "QE account-cell subagent policy failed closed.",
418	}));
419
420	on("tool.call", async ($, event) => {
421		if (!ready) return { deny: "QE account-cell Mod is not ready." };
422		if (event.agentId)
423			return { deny: "QE account-cell policy denies subagent tool calls." };
424		if (!ALLOWED_TOOLS.has(event.tool))
425			return {
426				deny: `QE account-cell policy did not authorize ${event.tool}.`,
427			};
428
429		const input = object(event, "tool input");
430		if (event.tool === TOOLS.attention) {
431			const category = boundedString(input.category, "category", 64);
432			const message = boundedString(input.message, "message", 16_384);
433			const interaction =
434				input.interaction === undefined
435					? undefined
436					: boundedString(input.interaction, "interaction", 64);
437			const responseSchema =
438				input.responseSchema === undefined
439					? undefined
440					: object(input.responseSchema, "responseSchema");
441			if (responseSchema !== undefined)
442				boundedJson(responseSchema, "responseSchema", 16_384);
443			const result = await waitForHuman($, {
444				type: "request_human_assistance_and_wait",
445				category,
446				message,
447				...(interaction === undefined ? {} : { interaction }),
448				...(responseSchema === undefined ? {} : { responseSchema }),
449			});
450			return { result: JSON.stringify(result) };
451		}
452
453		const result = await runBridge($, brokerOperation(event.tool, input));
454		return { result: JSON.stringify(result) };
455	}).catch((_$, event, next) => ({
456		deny: `QE account-cell tool mediation failed closed for ${event.tool}: ${next.error.kind}.`,
457	}));
458
459	on("tool.check", async (_$, event, next) => {
460		if (!ready || event.agentId || !ALLOWED_TOOLS.has(event.tool))
461			return {
462				decision: "deny",
463				reason: "QE account-cell exact tool policy denied this call.",
464			};
465		const lower = await next(event);
466		if (lower.decision === "deny") return lower;
467		if (event.ceiling === "ask" || event.ceiling === "deny")
468			return {
469				decision: event.ceiling,
470				reason: "QE preserved the managed policy ceiling.",
471			};
472		return {
473			decision: "allow",
474			reason: "QE allowed an exact pre-bound broker tool.",
475		};
476	}).catch(() => ({
477		decision: "deny",
478		reason: "QE account-cell permission mediation failed closed.",
479	}));
480
481	on("prompt.submit", async (_$, event, next) => {
482		if (!ready)
483			return {
484				drop: "QE account-cell Mod is not ready; refusing the prompt.",
485			};
486		return next({
487			...event,
488			context: [
489				...(event.context ?? []),
490				"Quest Engineering account-cell contract: every filesystem, search, and shell operation must use the QE broker tools. No local account-cell coding tool exists. Use qe_complete_step for completion and qe_request_human_assistance_and_wait for human input.",
491			],
492		});
493	}).catch(() => ({
494		drop: "QE account-cell prompt mediation failed closed.",
495	}));
496
497	on("turn.step", async function* (_$, event, next) {
498		if (
499			!ready ||
500			event.agentId !== undefined ||
501			event.model !== expectedModel ||
502			event.effort !== expectedEffort
503		)
504			return {
505				turnId: event.turnId,
506				index: event.index,
507				answer: "",
508				toolUses: [],
509				stopReason: "refusal",
510				usage: null,
511			};
512		const chunks = [];
513		const downstream = next(event);
514		let item = await downstream.next();
515		while (item.done !== true) {
516			chunks.push(item.value);
517			item = await downstream.next();
518		}
519		if ((item.value.serverToolUses?.length ?? 0) > 0)
520			return {
521				turnId: event.turnId,
522				index: event.index,
523				answer: "",
524				toolUses: [],
525				serverToolUses: item.value.serverToolUses,
526				stopReason: "refusal",
527				usage: item.value.usage,
528			};
529		for (const chunk of chunks) yield chunk;
530		return item.value;
531	});
532};
533