1% ---------------------------------------------------------------------------2% Section 6 — Laboratory infrastructure: Claude Code calling Claude Code3% (Agent H; describes the built system and the Phase 0 measurements, no result)4% ---------------------------------------------------------------------------5\section{Laboratory infrastructure: Claude Code calling Claude Code}\label{sec:infrastructure}67The mandate requires that the research laboratory be built, not described: a central controller must launch and supervise independent Claude Code sessions with isolated workspaces, bounded permissions, task-specific prompts and resource limits, and must log, hash and reset everything it touches. This section describes the system as built, the environment it was measured in, and the places where the environment constrained the design. Everything stated about the environment was measured during Phase~0 of the project on the day the laboratory was assembled; nothing is copied from historical inventories.89\subsection{Controller and executors}\label{sec:infrastructure-architecture}1011The laboratory has a controller--executor architecture. The \emph{controller} is a laptop session that holds the repository, the hidden oracles, the experiment database and the ledger; it draws the assignment, composes workspaces, launches sessions over SSH, streams and stores their events, enforces the wall limit, collects and hashes deliverables, runs the deterministic evaluator and the blind judges, and renders the dashboard. It never executes a worker itself. The \emph{executors} are four Mac Studio nodes of the authorised MacLustr cluster on which Claude Code is installed; each runs up to three concurrent worker sessions in separate per-run directories, hosts the synthetic enterprise portal for the runs that need it, and holds no oracle, no rubric and no evaluator code. Figure~\ref{fig:architecture} shows the architecture; Table~\ref{tab:roles} gives the correspondence between the mandate's nine roles and the processes that implement them.1213\begin{figure}[htbp]14\centering15\IfFileExists{figures/fig01_architecture.pdf}{%16\includegraphics[width=\textwidth]{fig01_architecture}%17}{%18\fbox{\parbox{0.92\textwidth}{\centering\small\vspace{1.2em}19\textbf{Figure placeholder.} Controller (laptop: assignment, workspace composition, launcher, event store, ledger, deterministic evaluator, blind judges, dashboard; hidden oracles outside the repository) $\longrightarrow$ SSH $\longrightarrow$ four executor nodes (per-run workspaces, \texttt{claude --bare} sessions under arm-specific flags, loopback portal for A3/A4, LibreOffice headless, Playwright Chromium) $\longrightarrow$ collection, hashing, reset. The figure file \texttt{fig01\_architecture.pdf} is produced by the figure script of the reproducibility package.\vspace{1.2em}}}%20}21\caption{Laboratory architecture: controller, executors and the separation of hidden material}22\label{fig:architecture}23\end{figure}2425\begin{table}[htbp]26\centering27\caption{Role separation: the mandate's roles and the processes that implement them}28\label{tab:roles}29\small30\begin{tabularx}{\textwidth}{@{}l >{\raggedright\arraybackslash}X l@{}}31\toprule32Role & Process & Sees the oracle? \\33\midrule34A Director & the controller session and \texttt{protocol/} & yes \\35B Scenario generator & one fresh sub-session per scenario, writing bundle and oracle & writes it \\36C Scenario validator & one fresh sub-session per scenario; solves from the public bundle first, then compares & after solving \\37D Worker & \texttt{claude --bare} on an executor node, in a fresh workspace & never \\38E Deterministic evaluator & \texttt{evaluate.py} of the hidden oracle, run on the controller & yes \\39F Blind evaluator & \texttt{claude -p --json-schema} judges on the controller; rubric and deliverables only & rubric only \\40G Statistician & the \texttt{analysis/} scripts, on validated data & validated data \\41H Writer & sub-sessions writing \texttt{manuscript/} from the \texttt{analysis/} outputs & no \\42I Auditor & a fresh sub-session checking claims against records at the end & all records \\43\bottomrule44\end{tabularx}45\end{table}4647The separation is operational rather than nominal. Generator and validator are distinct sessions with no shared conversation; the worker is a separate operating-system process on a separate machine, started in a mode that ignores the user's settings, hooks and instruction files, so that nothing from the controller's own configuration reaches it; the deterministic evaluator is code that the worker never sees; the judges receive a rendered package and a rubric; and the writer of the present section had access to the protocol, the code and the benchmark, but to no run.4849\subsection{The measured environment}\label{sec:infrastructure-environment}5051Table~\ref{tab:nodes} summarises the Phase~0 survey of the executor nodes. The controller runs macOS~27.0 with Claude Code 2.1.316; the four executors run macOS~26.4 to~26.6 with Claude Code 2.1.314, a single version on all nodes, which is pinned by recording the client version reported in the system event of every session. Each node has a project Python environment, LibreOffice~26.2 for headless conversion and recalculation, and Playwright's headless Chromium, all installed or verified during the survey. Other cluster nodes that answered over SSH did not have Claude Code installed and were not used; a few did not answer within the timeout.5253\begin{table}[htbp]54\centering55\caption{Executor nodes (Phase~0 survey, measured live)}56\label{tab:nodes}57\small58\begin{tabular}{@{}llllllll@{}}59\toprule60Node & macOS & Cores / RAM & Free disk & Claude Code & Python & LibreOffice & System Events \\61\midrule62M3U96a & 26.6 & 28 / 96 GB & 238 GB & 2.1.314 & 3.14.5 & yes & answers \\63M3U96b & 26.4 & 28 / 96 GB & 380 GB & 2.1.314 & 3.14.4 & 26.2.3 & times out \\64M4M64a & 26.5.1 & 16 / 64 GB & 540 GB & 2.1.314 & 3.14.4 & 26.2.3 & answers \\65M4M64b & 26.5.1 & 16 / 64 GB & 54 GB & 2.1.314 & 3.14.4 & yes & times out \\66\bottomrule67\end{tabular}68\begin{tablenotes}69\footnotesize70\item ``System Events'': whether \texttt{osascript} reaches System Events from a non-console SSH session. Finder and TextEdit time out on all four nodes (Section~\ref{sec:infrastructure-macos}). All nodes have a console user logged in, the project Python environment, \texttt{uv} and headless Chromium.71\end{tablenotes}72\end{table}7374Model access was verified by a headless single-turn probe of each candidate model from an executor node, with the API key read from an environment file held outside the repository with restricted permissions and never logged. The probe recorded, from the \texttt{total\_cost\_usd} field of the client's result event, the marginal cost of one trivial turn: about 0.0002~dollars for \texttt{claude-haiku-5-5}, 0.004 for \texttt{claude-sonnet-5-5}, 0.008 for \texttt{claude-opus-5-5} and 0.020 for \texttt{claude-fable-5-1}. These are infrastructure measurements, not experimental results; their only use was to confirm that costs are read from the client rather than from an assumed price table and to inform the choice of the primary model and its sensitivities (amendment A-004).7576\subsection{The launcher}\label{sec:infrastructure-launcher}7778A worker session is a single command assembled by the launcher from the arm's configuration and the run's identifiers: the client is started in bare mode, with the prompt read from a file in the workspace, streaming JSON events with verbose output, the model fixed, the tool set restricted and pre-approved, permission prompts disabled, the budget and turn caps set, the arm's documentation appended to the system prompt from a file, the browser integration disabled, slash commands disabled, session persistence disabled, the arm's denied command families listed, and a session identifier generated by the controller. The command runs on the node through SSH inside a shell line that changes to the workspace, sets the path, sets an environment variable that marks the process as a worker, disables non-essential client traffic and the auto-updater, loads the API key from the node's environment file, and replaces the shell with the client; standard input is closed. The controller reads the event stream line by line, appends every line unmodified to an append-only raw file, normalises it into the event store, and parses the final result event, from which it takes the cost, the number of turns and the termination subtype.7980Four properties of the launcher are design requirements rather than conveniences. Bare mode means that the worker runs without the user's settings, hooks and instruction files, so that the controller's own cluster instructions never reach a worker. The tool restriction is literal: a worker started without the shell tool has no shell, as verified by a probe in which the model reported only file tools and could not run a trivial command; a denied command family is denied inside the shell, as verified by a probe in which an AppleScript invocation was refused and a plain command ran. No Agent or Task tool is ever given to a worker, so a worker cannot spawn sub-agents, and the launcher refuses to start if the worker-marker variable is already set in its own environment, which prevents a nested controller. The wall limit is enforced by the controller: when the elapsed time exceeds the limit, the remote process is killed by its unique session identifier, first gently and then forcibly, and the run is marked as timed out; a run that ends by the client's own budget or turn cap is recognised from the result subtype.8182\subsection{Workspace isolation, portal, office and browser adapters}\label{sec:infrastructure-workspace}8384Each run receives a workspace composed on the controller and pushed to its node. The workspace contains the public bundle with the generator script removed and the metadata reduced to what a worker legitimately needs, the public completion-record schema, the prompt and system files, for the portal arms a portal description with the loopback URL and the credentials, and for arm A4 the public self-check tool. A manifest of SHA-256 hashes of every file in the workspace, excluding the output directory, is written before the session starts. After the session, the output directory is copied back and hashed file by file, the inputs are copied back and compared with the manifest, any change to an input is recorded as a critical violation, the portal is stopped, and the workspace on the node is deleted. The copy of the output directory is retried with backoff on an SSH or transfer failure and verified by comparing the number of files collected with the number listed on the node; a persistent failure is loud and marks the run as an infrastructure error rather than as a run that produced no deliverables. This check was added after the audit of the pilot found three runs whose deliverables had been lost silently at collection under twelve concurrent sessions (amendment A-010); the three runs were reset and rerun under the same identifiers, and their first attempts are preserved in the infrastructure-stability statistics (Section~\ref{sec:reliability}). Successive runs on the same node therefore never see one another's files, and the reset is a deletion rather than a cleanup.8586The \emph{synthetic enterprise portal} is a small web application started on the node for each run of arms A3 and A4, bound to the loopback interface on a free port and driven by the scenario's portal description. It serves a login page, a document library whose download links point to files in the inputs, record tables as HTML and as JSON, and forms whose submissions are appended to a file in the output directory, where they are collected and evaluated with the other deliverables. It holds no information absent from the inputs, by specification and by construction, since its documents are served from the input directory. The \emph{office adapter} is LibreOffice in headless mode, used by the worker to convert documents and to recalculate workbook formulas so that cached values are present when a file is opened; the \emph{browser adapter} is Playwright's headless Chromium in the project Python environment, which the worker can drive to log in to the portal, browse the library, download documents and submit forms; the portal is also reachable with command-line HTTP tools. External network access is forbidden by policy and the portal listens on loopback only; the orchestrator gives the worker no credential other than the API key in its process environment.8788\subsection{The macOS layer: what works headlessly and what does not}\label{sec:infrastructure-macos}8990The mandate's arm A2 is a ``macOS agent'' with AppleScript and approved GUI automation. Phase~0 measured what is actually available to a process started over SSH on a node whose console user is logged in. The AppleScript interpreter runs on all nodes and reaches System Events---process lists, the frontmost application, file-system automation---on two of the four nodes; on the other two, every Apple Event times out. Finder and TextEdit time out on all four nodes: the request is blocked by the automation consent prompt that macOS presents on the console and that nobody can answer for a non-console process, which is the behaviour of the Transparency, Consent and Control framework for background sessions. The consequence is recorded as amendment A-003 and reflected in Table~\ref{tab:arms}: arm A2 is implemented as the shell plus the macOS automation layer that works headlessly---\texttt{osascript} with AppleScript and JavaScript for Automation, \texttt{do shell script}, System Events on the nodes that answer---and the native document utilities for conversion, images, metadata, previews and printing to PDF. Control of graphical applications such as TextEdit, Numbers or Pages is not available and is recorded as an infrastructure limitation; GUI-dependent runs are pinned to the two nodes where System Events answers. The mandate's warning that ordinary containers do not provide reliable macOS GUI isolation was, in this sense, confirmed from the other direction: on real machines, GUI automation is not available to a headless experiment at all without a dedicated console session, which the present version of the laboratory does not have.9192\subsection{The A4 self-repair protocol and bounded recovery}\label{sec:infrastructure-recovery}9394Arm A4 adds three elements to arm A3. The worker is instructed to keep a checkpoint file in the output directory updated after every major step---what is done, what remains, where intermediate files are---and, before finishing, to run the public self-check and fix every problem it reports. The self-check is a short program copied into the workspace; it verifies that every required deliverable exists and is non-empty, that office files are valid archives, that a workbook required to contain formulas does contain them and shows no error values when cached values are read, that a document has a minimum number of words, that JSON deliverables parse, and that the completion record has the required fields and a valid status. It knows nothing about the correct answers.9596Recovery is controller-side and bounded. After the first session ends and its deliverables are collected, the controller runs the same self-check locally on the collected output. If it fails, or if the session ended by timeout, budget exhaustion or error, and the emergency stop is not set, the controller writes a recovery prompt into the workspace---stating that the previous session ended, quoting the self-check's problems, instructing the worker to read its checkpoint and the current output, finish the remaining deliverables, fix the reported problems, run the self-check and rewrite the completion record---and resumes the same session identifier with the same flags, the remaining budget and the remaining wall time. At most one recovery is granted per run. The recovery's cost is computed as the increment over the first session when the resumed client reports a cumulative figure, is added to the run's cost and recorded in the ledger under its own label, and its wall time is added to the run's total. The deliverables are collected again, and the final self-check status is recorded with the first; the two statuses are the data from which the Autonomous Recovery Index of Section~\ref{sec:theory-wdf} is estimated. All of a recovery's cost and time are attributed to the parent run, as the mandate requires for any sub-process within a treatment.9798\subsection{Logging, hashing, ledger, dashboard and emergency shutdown}\label{sec:infrastructure-logging}99100Every session's raw event stream is written unmodified to an append-only file in the run's directory, with a separate file for the recovery stream; the normalised events are stored in a SQLite database on the controller together with the run table, the artefact table of paths, hashes and sizes, the receipts, the judgments, the metrics and the ledger. The ledger records every charge---worker, recovery, each judge---against the run that caused it, and its total is the quantity compared with the study envelope before each dispatch. The deterministic receipt carries the hash of the oracle and evaluator that produced it; the workspace manifest carries the hashes of what the worker saw; the collection record carries the hashes of what it produced. The normalised run and judgment tables are exported to Parquet for the analysis, and the raw files are never rewritten.101102A private dashboard is rendered from the database as a static page that refreshes itself: progress by arm and status, the ledger against the envelope, validated outcomes by arm, node utilisation, a list of errors and a table of runs with their status, node, result subtype, wall time, cost, tool calls, deterministic completion, violations, quality, acceptance and recovery flags. It is served on the controller's loopback interface only. Emergency shutdown is a single command that kills every bare-mode client and every portal process on every executor node and sets the stop event that prevents the scheduler from dispatching further runs or granting further recoveries; a stale-run detector lists runs that have been in a running state for longer than a threshold so that they can be killed and reset individually.103104\subsection{Scheduling and concurrency}\label{sec:infrastructure-scheduling}105106The scheduler takes the assigned runs in the order of the seeded permutation, checks the ledger against the envelope, and dispatches each to the least-loaded node that is eligible for the arm and has a free slot, up to a global limit of concurrent sessions. The pilot's concurrency, three sessions per node and twelve in total, was chosen from the experience of an earlier study in the same series, which ran two sessions per node without incident on shorter runs, and from the longer expected duration of office and portal work; it is a judgement, not a measurement, and the infrastructure-stability statistics of the pilot are the test of it. Each dispatched run executes in its own thread on the controller: prepare, launch, collect, evaluate and judge, then release the node slot. A run that fails for an infrastructure reason before any inference occurs is marked as such with the exception recorded, its workspace is reset, and it is re-queued under the same identifier according to the exclusion rules of Section~\ref{sec:design-exclusions}.107