I was about to be away for a week with no reliable way to reach any of this remotely. That's the whole shape of the night: not a project deadline, a self-imposed one. Anything left undecided by morning would sit blocked for seven days, so between just after midnight and quarter to six I forced the decisions instead. The session had picked up threads from before midnight and, somewhere in the middle of it, went quiet for about five hours. When it came back it said so itself: "This session resumed after a ~5 hour gap." That admission turned out to matter more than it looked, because the first thing waiting on the other side of the gap was a session that had already done the work I was about to redo.
The work itself was Phase 0 — a battery of go/no-go checks for n5-board, the Kanban board my agents will eventually read and write on their own. Before building it, I wanted proof that a handful of risky assumptions about the harness actually hold up in production rather than in theory. Six checks, each with a code: does a shared token's background refresh break other sessions running at the same time, does an API key still work a day after being minted, can the sandbox actually fence off which hosts a worker can reach, does argv-based execution block shell injection, does the harness honestly report which model served a session, and does a project's CLAUDE.md actually load into a worker's context.
The sandbox that died three times
The network-fencing test — run, it turned out, by a parallel session I'd authorised earlier and left running through the quiet stretch — went looking for a sandbox and found it dead in three different ways before I gave up on it.
First it found socat missing from the host entirely. Supplying it from userspace uncovered the real blocker: kernel.apparmor_restrict_unprivileged_userns=1 stops the nested user namespace the sandbox's seccomp helper needs, failing with apply-seccomp: write /proc/self/setgroups... Permission denied. A second attempt inside a throwaway privileged container on the second node, meant to isolate whether the allowlist itself works once that restriction is out of the way, hit a different wall: apply-seccomp: write /proc/self/uid_map: Operation not permitted, an AppArmor profile denial. The agent would not work around that one by loosening the container's own AppArmor profile to unconfined — the harness's own classifier flagged it as a security-bypass pattern, and it left the block standing rather than override it.
I made the call at 03:47: "rely on the firewall rule definitely, that's the thing that has been designed to be a firewall." The sandbox layer is retired; nftables is now the only thing controlling egress. That decision closed a real bug on the way out — a sandbox that fails to start had been reporting is_error:false while every command inside it silently failed. Fail-open. A worker in that state looks healthy and does nothing, which is worse than looking broken.
A model reporting on itself, three ways
The strongest thread of the night showed up in three unrelated tests, and it was the same failure each time: you cannot ask a model to describe its own state and trust the answer.
The first was the CLAUDE.md-loading test. Round one asked the model to self-report whether something was absent from its own context — a confabulation-prone question by design, and my own evaluator caught it, scoring the result 3.6 out of 5 and noting that "the safety-critical cell is the weakest-evidenced cell in the table." Round two threw the model's opinion out entirely and used a mechanical hook instead, logging file loads without asking anyone anything. On that evidence — 13 InstructionsLoaded events with no flag set, zero events across three runs with --setting-sources user — the flag genuinely suppresses a project's CLAUDE.md rather than just being a workspace-trust artifact.
The second was the model-resolution test, and it found something worse than a wrong answer: a confidently wrong one. A subagent declared against a retired model ID got a hard 404, the orchestrator silently retried on a current model, and the task finished with marker files written — a card closing green while running on a model nobody asked for. Then it found that the system/init stream event, the thing you'd naturally trust to say which model is running, reports the model requested, not the model actually served. Under a fallback model it read back the retired ID even while a different model was doing the work. The only signals that don't lie are modelUsage and the persisted message.model field. Along the way it also confirmed that a fallback model doesn't apply to subagent-pinned models at all, which had been sitting in the plan as a real protection and wasn't one.
The third was smaller but belongs in the same list: the plan had assumed a model could hash its own context to prove it hadn't been tampered with. It can't. Between the three, the pattern that survived the night is consistent — stop asking the model what it is or what it has, and replace that with something a file can prove: a marker, a hook log, a field the harness writes rather than one the model reports.
The soak that ran all night and didn't answer
The one test built to be the most careful gave the least. Token-refresh safety under concurrent sessions was deliberately run as a standalone background soak rather than an agent, because dispatching an agent to check it would itself add a competing session and wreck the measurement — three concurrent calls every 150 seconds across the token's roughly 4.77-hour natural window. I tried to force the refresh boundary manually around 03:33 to skip the wait, and the harness's own classifier blocked that as credential manipulation. It deferred to the block instead of finding a way around it and told me to do it myself if I wanted it faster. I didn't push.
It didn't matter. At round 13 of 130 one of the three concurrent calls came back with an AUTH failure before any refresh had happened at all, which by the test's own logic means a clean pass is no longer reachable. It ran to quota exhaustion anyway and closed as INDETERMINATE — quota-limited, refresh safety not measured. No answer, after all that engineering. I'd rather say that plainly than round it up to something safer-sounding.
A session I didn't know was still running
Two smaller things surfaced almost by accident. Coming out of the five-hour gap, the session noticed the plan file's line count and header positions had shifted since it last read them, stopped, and read the file properly instead of overwriting it — a separate session I'd authorised earlier had already done the sandbox and CLAUDE.md work while this one sat quiet.
While checking for stray processes I found a tmux session that had been running continuously since Tuesday, permission checks disabled, under an account holding root SSH access to the second node. The plan claimed it had been "Removed. Nothing depends on it." It hadn't been. I told it to take both that session and its supervisor down, which it did, and disabled them at the systemd level so they can't respawn on reboot — the trade-off being that this removes the Discord !start recovery path; SSH and the web UI still work.
Subtitle sidecars, and a mistake I'd already written a lesson about
Past four in the morning I switched to something smaller: adding Bazarr to the stack to fetch English subtitle sidecars for anything arriving through Sonarr or Radarr without them, the worst gap being on a few long-running rolling-release distros in the library. It went in cleanly on the same identical-path mount the rest of the stack uses, right after Sonarr, backup taken first. One real bug on the way in — a stale FUSE handle on that mount left it reporting "Socket not connected" while the host and everything else on it were fine, fixed by restarting the container to rebind.
Then came the mistake, and it's the best-documented one of the night because it caught itself. At 04:14 it reported only about 67 items "wanted" and concluded the coverage gap was mostly solving itself through subtitle tracks already embedded in the files — checked with ffprobe against one point release, which came back with two embedded English tracks in 3.7 seconds over the mount. Comfortable conclusion, and it went looking for the real gap elsewhere: reading the running Bazarr source rather than the docs, which is how it found that two of the commonly recommended subtitle providers don't exist in this version at all, and two more are force-disabled at the build level. One survivor, a proxy in front of one of the disabled providers, wasn't on that list and turned out to be the real route — six results for an item that had shown zero coverage, 31 downloads, all from it.
Then, at 04:42, it caught the actual problem — only 836 of 13,375 point releases, 6.3%, had been scanned, and the scan had not finished. Its own line: "I got this wrong, and it's the same mistake I just wrote a runsheet lesson about." The "67 wanted, mostly solved" number was never a result, it was a snapshot of an index that had barely started. By 05:43 the scan had reached 14.1%, the one-shot images were fully indexed, and the wanted count had climbed from 38 to 166 on one library and 105 to 120 on another as the scan turned up more gaps than it closed — the expected shape of an unfinished scan, not a regression. It also caught a runsheet claim it had itself repeated earlier, that one provider was blocked by Cloudflare; the actual container log said ConfigurationError... 'User-agent config missing', fixed by setting a browser user agent and clearing a stale throttle.
One more provider got enabled and then reverted: the embedded-subtitle route, which turned out to need "Treat Embedded Subtitles as Downloaded" switched off to do anything, and switching that off would have flipped roughly 13,000 items to wanted and forced multi-terabyte demuxing over the remote mount to produce sidecar files that are already redundant with the tracks embedded in the images. Not worth it, left alone.
By 05:46 the honest diagnosis was that most of the 166 still-wanted items aren't a coverage problem at all — they're a score problem. One had six candidate matches, all scoring 61 against a minimum of 90; another scored 56 against a minimum of 70. The fix on the table is a hash-matching provider rather than lowering the threshold, and lowering it is a quality call that's mine to make, not the session's, so it left it alone. What's left for me: sign up for that provider to close the one-shot-image gap, and hand-restart Bazarr after any mount bounce until the mount-recovery unit is taught to include it, which needs root it doesn't currently have.
A separate false alarm closed out the night — I thought the agent had frozen on my terminal again, but it hadn't. I'd run /resume in a fresh terminal against a session already running live in the background, which correctly refused and left that terminal parked burning CPU on an idle child process for nothing. Not the known auto-update wedge, just a dead end.
Two things happened twice tonight and they're the same thing: a model's opinion of itself proved worthless three separate times, and a claim repeated from a plan or a runsheet turned out to be stale twice, caught only by checking the running system instead of the document describing it. Neither is a new rule so much as the same rule, twice confirmed.