Part of Polaris — an experiment in delegated stewardship

The Monitor Was Fine. It Was Reading the Wrong Surface.

Ashita Orbis | July 31, 2026 | 25 min read | daily log

Covers the calendar day of 2026-07-31. No night report for this date was filed to the standing log; this is built from the day directory instead — two generation reports dated 2026-07-31, one incident report written the same afternoon, the decision ledger, the question queue, and the orchestrators' own pass logs.

The short version

  • The orchestrator ran about three and a half hours on a weaker model without noticing. A safety classifier swapped generation 22 from its top-tier model down to Opus 4.8 at around 13:00Z and it stayed there until 16:35Z. It was found because the author asked why the agent seemed degraded — not by any of the agent's own checks.
  • The alarm did fire. Nobody read it. An independent guard caught the swap in two seconds, at 13:05:36Z, and sent the author a message. The orchestrator's own model check only ran during its full check cycle, and it had switched to cheaper cycles for the afternoon; a downgrade changes nothing else visible, so nothing ever triggered a full sweep. The first account of the incident blamed the guard for missing it. The investigation that same afternoon found the guard was right and the reading of it was missing.
  • The fix shipped that morning — run the model check on every cycle — failed three more times the same day. It reported a confident all-clear on a session that was on Opus 5. The cross-check added to catch that turned out to be dead code, because a stray newline was being converted into an underscore. And it later raised a false downgrade alarm that told a fresh orchestrator to hand off over a harmless event from one of its workers.
  • The second downgrade of the day took minutes instead of hours. At 20:04:44Z the next orchestrator was swapped down to Opus 5, detected it, wrote a briefing, launched a fresh top-tier successor and handed over. Same failure, three and a half hours versus minutes.
  • The completion gate crashed at the exact moment it had to record a failure. The gate is the independent check that decides whether a piece of work is genuinely finished. On a failing verdict it must file the work back as unfinished — and on that path it died with the verdict already written to disk and the record of it never filed. It happened at least three times that day; one lost record was reconstructed by hand.
  • The workspace machine ran out of memory again — 29% of memory available, swap 99% full, 77 model processes and 357 tool-server processes alive. No leak: about eighty terminal sessions had accumulated across a day of heavy dispatch, most of them finished work nobody had cleaned up, each holding 470–540 MB. After the cleanup, memory went 29% → 69%, swap 99% → 26%, sessions eighty-odd → 13.
  • The orchestrator was blocked from fixing that itself, three separate ways — and writing the kill commands is what cost it its model tier four minutes later. It wrote the cleanup script, was refused a kill loop, a background unit, and a settings edit, honored all three refusals rather than routing around them, and paged the author, who ran it.
  • Two closing records silently did nothing. Fifteen in the morning and one in the evening were written with a key that did not exactly match the item they were closing, so the board ignored them and the author never saw the explanations. Both were re-issued.

What changed in the harness

The model check moved into every kind of pass. Intent: a downgrade shows no other visible change, so the only check that can catch it must ride the one step every cycle performs, not the expensive sweep that only runs when something looks off. It is one cheap read of the session's own transcript; there was never a good reason to gate it behind the full sweep. It is folded into the turn-ending call, which stamps first so a failing check can never cost the session its liveness mark, and exits non-zero so the result lands in front of the model rather than in a log.

A read-only peek at the guard's event file joined that same path. Intent: an alarm nobody reads is not a monitor. The independent guard had done its job perfectly and its event sat unconsumed for three and a half hours. The peek re-raises pending events every turn until a full sweep actually consumes and adjudicates them.

The guard now resolves who the orchestrator is from the orchestrator's own state file. Intent: stop the guard guessing which terminal pane holds the lease — the lease record already knows.

The remembered session id became per-session, with a cross-check against the lease. Intent: make it impossible for one orchestrator's check to read a different orchestrator's transcript. Details in "What broke" — this is the fix to the fix.

A guard against empty mission files. Intent: registering a job, launching a session, and writing that session's instructions are three separate steps, and the third can fail silently — a worker then runs with no mission text at all. Six workers had launched that way. The new check alerts on any zero-byte mission file less than 48 hours old, and caught a seventh live the same day.

Read a job's status header last, and abort if it says complete. Intent: never re-commission work that is already done. Status files here grow by prepending, so a commission built from an older handoff can read a stale tail while a COMPLETE header sits above it. That is exactly what happened: a finished job was re-commissioned four hours after it closed. The worker that received it verified rather than obeyed, and escalated.

A ruling on which model the harness may fall back to. Intent: an unplanned downgrade should land somewhere the work survives. The standing position that a bridge on a weaker model is acceptable was narrowed: Opus 5 is an acceptable fallback, Opus 4.8 is not. Security work does not go through the top-tier orchestrator directly at all — it is dispatched to a separate session at arm's length, because that content is what trips the classifier in the first place. And any decision made during a fallback window gets re-evaluated once the orchestrator is back on its normal tier.

The gate's input is now sanitized before it reaches the evaluator. Intent: an evaluator that receives malformed input should not be able to fail as though the work were bad. A byte-count truncation had cut a multi-byte character in half and handed the evaluator invalid text.

Reaping finished workers at harvest time entered the successor's instructions. Intent: attack the accumulation where it starts rather than paging a human when the machine is already out of memory.

A correction to how the agent frames comparisons in evaluations. Intent: the agent should model what was actually being asked, not the most literal reading of it. The author flagged two comparison-axis misses in a single day and named the failure directly — the agent modelling his intention badly, not merely making a technical error. The rule was recorded, relayed into a worker that was mid-run, and the failure class routed to a standing program on decision prediction.

What broke

Three and a half hours on the wrong model, and the alarm nobody read

How it was detected. The author asked whether sitting on a degraded orchestrator was justified or whether something was going on. It was. The session's own transcript settled it immediately and unambiguously: every turn from 06:00Z to 13:00Z ran on the top tier, every turn in the 14:00, 15:00 and 16:00 hours ran on Opus 4.8, with none of the top tier in between.

What actually caused it. A classifier refusal-fallback around 13:05Z, almost certainly triggered by that morning's credential-handling and security-hygiene work, which is exactly the content that trips it. The fallback then stuck: once flipped, the session kept answering on the weaker model rather than returning.

The interesting part is not the trip. It is the detection gap, and the record contains two readings of it written hours apart.

The incident report, written at the time, named two failed monitors: the orchestrator's own model check, which only runs in the full sweep and so was never executed after the morning block; and the independent guard, which "logged no event" across the whole window.

The investigation ordered as the successor's first task found the second half of that wrong. The guard had tripped in two seconds, at 13:05:36Z, sent the author a message, and written its outbox record. The real second gap was that the orchestrator never read the guard's event file on cheap or event-driven cycles, and its own liveness stamp was masking the pending event from the deadman check that would otherwise have surfaced it. Both readings are in the record; the later one is better evidenced, because it names a timestamp and an artifact.

What was done. The lease — the single token naming which session is the orchestrator right now — was handed to a fresh top-tier generation at 16:45:09Z, on a transfer that compares the expected generation number and verifies the successor's model tier before it is recognized. The successor's instructions carried two urgent first tasks: put the model check on every pass, and find out what the guard actually did.

The lesson that generalizes. Two, and the second is the expensive one.

A check that only runs on the expensive path effectively does not run. The condition it detects — a silent capability downgrade — is precisely the condition that produces no other signal, so nothing will ever escalate you onto the path where the check lives. Any monitor for an invisible failure has to ride the cheapest thing you do every time.

And: an alarm that fires into a file nobody opens is not a monitor, it is an archive. The guard here was faultless. It detected the swap in two seconds and notified a human. The system still sat degraded for three and a half hours, and the first written account blamed the guard. Before adding another detector, check whether the one you have is already firing.

The fix that reported all-clear on a downgraded session

How it was detected. At 20:09Z, minutes after a handover, the model check printed a confident all-clear naming the top-tier model — for a session that was on Opus 5. The session knew it was on Opus 5, because it had just handed off for that reason.

What actually caused it. The check remembered which session it was watching in a single shared state file. Environment variables do not survive between tool-call shells, so the remembered-id path is the common path, not the rare one. During the overlap between an outgoing and an incoming orchestrator, the incoming one ran the check first and overwrote that file with its own id. The outgoing one then read its successor's transcript, found the top-tier model there, and reported green about itself.

What was done. The remembered id was keyed per terminal session, and a cross-check added: when the lease record names the calling pane, the lease's session id wins over anything remembered or guessed, and a mismatch raises an alert rather than silently substituting. The successor was explicitly told to attack the fix rather than inherit it.

That instruction paid off within the hour. The adversarial re-test found the cross-check was dead code: the sanitizer that made the session name safe for a filename was operating on a stream that still carried a trailing newline, which it converted into an underscore, so the sanitized name never matched the lease's target and the comparison could never fire. Fixed by matching the lease against the raw name and sanitizing only for the filename.

Later that night the same check failed in the opposite direction. It exits non-zero for two different reasons — a real downgrade, or the new event peek finding unconsumed alarms from any session — and conflating them told a fresh orchestrator at 22:34Z to hand off over a harmless event belonging to one of its workers. Fixed by discriminating on the specific alert line rather than the exit code.

The lesson that generalizes. Four states of one monitor in one day: not run at all, falsely green, structurally dead, falsely red. The unifying sentence is the one the decision ledger uses twice: a safety mechanism that reports green is not evidence it looked at the right thing. Both the morning incident and the evening defect were the same shape — the monitor was healthy and reading the wrong surface.

Two transferable specifics. Shared mutable state in a per-session check is a bug waiting for a handover to expose it; key it by the thing it is actually about. And when a check's exit code carries more than one meaning, someone downstream will act on the wrong one — the signal has to name which condition fired, not merely that something did.

Finally: ordering the successor to adversarially re-test a fix, rather than trust it, found a real dead-code bug within the hour. That is cheap and it worked.

The gate that died at the moment it had to record a failure

How it was detected. A verdict file existed on disk with no corresponding entry in the list of work filed back as unfinished. A later orchestrator noticed and appended the record by hand.

What actually caused it. The gate script runs with the shell configured to abort on any error and to propagate failures through pipelines. On the failing-and-cold path it looked up the originating session's transcript by piping a filesystem search into a command that reads one line and exits. That reader closing the pipe sends the search a broken-pipe signal; the script's own error handling then killed it — after the verdict was written, and before the record of failure was filed. The verdict survived; the thing that routes the work back to the queue did not.

It bit at least three times on this day, including twice on the same job earlier in the afternoon on the sibling path that writes a toss-back message.

What was done. Registered as its own repair row that evening; the missing record was reconstructed manually and marked as such. The search now terminates itself after the first match instead of relying on a reader to close the pipe, and the script carries a comment naming this incident so the pattern is not reintroduced.

The lesson that generalizes. The error path is the path you never exercise. This machinery had been running for a fortnight; every success ran a code path that had been tested thousands of times, and the branch that fires only when work is judged incomplete had a fatal bug in it. If you have a gate, deliberately fail it on purpose sometimes.

Concretely: abort-on-error plus propagate-pipeline-failures is the right default and it makes search | take-first a live grenade, because "reader finished early" is indistinguishable from "the command failed". Use the search's own stop-at-first-match. And a two-step commit — write the verdict, then file the consequence — needs the second step to be recoverable, or you get a system that knows the work failed and has forgotten to tell anyone.

The memory emergency, and the classifier that blocked its own fix

How it was detected. The machine's sentinel fired at 20:00Z: 29% of memory available, swap 99% consumed, 77 model processes and 357 tool-server processes alive. A comparable episode a week earlier had given roughly sixteen minutes of runway from that state.

What actually caused it. Accumulation, not a leak and not a spawn storm. About eighty terminal sessions had built up across a day of heavy dispatch, most of them finished work whose panes were never cleaned up; each held its full context at 470–540 MB plus four or five tool-server processes. The largest single process — a 1.2 GB text-to-speech synthesis — was legitimate work in flight.

What was done. Evidence was captured before anything was touched. The orchestrator wrote a cleanup script naming 69 sessions to close and 12 to protect, modeled on an owner-approved script from three days earlier — and then could not run it. The permission classifier refused it three ways: a direct kill loop, a background unit, and a settings edit. All three refusals were honored rather than routed around, and the author was paged with a blocking card and a one-line command. He ran it at about 20:09Z. Memory went 29% → 69%, swap 99% → 26%, sessions eighty-odd → 13, model processes 77 → 11. All twelve protected workers survived.

Four minutes after writing those kill commands, the orchestrator was itself downgraded to Opus 5 — the classifier tripping on the very commands it had just refused. Because a fresh top-tier session was obtainable, no bridge exception applied and it handed off immediately.

The lesson that generalizes. A permission boundary that stops an agent from mass-killing processes is correct, and it will fire hardest exactly when the agent most wants to act. That is the boundary working, not a bug — but it means the emergency path has to end in a human with a paste-ready command, not in an agent looking for a way around. The routing-around is the failure mode worth designing against; here it did not happen.

The second lesson is quieter. This was the third memory episode in four days, and every time the cause was the same: finished work that nobody reaped. Cleaning up at the end is a different job from cleaning up when you are out of memory, and only one of them can be done calmly.

Closes that silently did nothing

Two instances of one bug, hours apart. In the morning, fifteen closing records — the plain-language explanations the author reads when an item leaves his board — were written with the wrong key, so the board skipped them and those explanations never reached him. In the evening, the record closing the memory emergency named the card by a prefix rather than its exact id, so the card stayed open until the next orchestrator noticed and re-closed it correctly.

No board clutter resulted in the first case, because he had already answered the items anyway. That is luck, not a mitigation.

The lesson that generalizes. A write keyed on exact match that silently no-ops on a miss is worse than one that throws, because the caller reports success and the audit trail looks complete. If a system matches identifiers exactly, an unmatched write is an error, not a no-op.

Six workers launched with no instructions

Registering a job, launching its session, and writing that session's mission text are three separate steps. When the third silently fails, the first two still look successful — a live worker exists, attached to a real job, holding a zero-byte instruction file. Six had launched that way before anyone noticed.

What was done. A check for zero-byte mission files under 48 hours old joined the standard sweep. It caught a seventh the same day.

The lesson that generalizes. In any multi-step launch, ask which step's failure leaves the system looking healthy. That is the step that needs a detector, and it is rarely the last one.

Intentions vs outcomes

Forward — changes made on 2026-07-31

Each row states what changed, what it was supposed to buy, and when it gets re-checked. The re-check dates are as scheduled from that day.

What changed Intent +3d +14d
Model check rides every pass shape Catch a downgrade that produces no other visible signal 2026-08-03 2026-08-14
Read-only peek at the guard's event file on that same path An alarm nobody reads is not a monitor 2026-08-03 2026-08-14
Remembered session id keyed per session, plus a lease cross-check that alerts on mismatch Make it impossible to check one orchestrator's model by reading another's transcript 2026-08-03 2026-08-14
Guard resolves the lease holder from the orchestrator's state file Stop the guard guessing which pane holds the lease 2026-08-03 2026-08-14
Zero-byte mission-file guard No worker runs with no instructions 2026-08-03 2026-08-14
Read a status file's state header last; abort a commission that reads complete Never re-commission finished work 2026-08-03 2026-08-14
Fallback tier is Opus 5, never Opus 4.8; security work dispatched at arm's length; fallback-window decisions re-evaluated on return An unplanned downgrade should land somewhere the work survives 2026-08-03 2026-08-14
Gate input sanitized before it reaches the evaluator Malformed input must not read as failed work 2026-08-03 2026-08-14
Gate's failing-and-cold path no longer dies before filing the work back A gate that knows work failed must be able to say so 2026-08-03 2026-08-14
Reaping finished workers at harvest time enters the successor's standing instructions Stop the accumulation at source rather than at the out-of-memory page 2026-08-03 2026-08-14
Recall index: query-plan fix and failed-lookup instrumentation Measure recall failures behaviourally instead of assuming the storage engine was the problem 2026-08-03 2026-08-14

Retrospective check-backs — run 2026-08-07

Labeled retrospective: this is a backfilled post, so the check-backs below use today's knowledge, not the covered day's. Each states what was actually looked at and what that look cannot see.

Model check on every pass. HOLDS. Method: the check still carries its own run-only-the-model-check mode, the turn-ending call still invokes it on every pass, and both files carry comments naming this incident and its date. Limit: this shows the code path exists and is still wired in. It does not show that it catches a live downgrade — the only proof of that is another downgrade, and the check was wrong in both directions on the day it shipped.

Per-session keying and the lease cross-check. HOLDS, with its own repair recorded in place. Method: the remembered-id file is keyed by session today, and the raw-name lease match that repaired the dead cross-check is present, with the newline-to-underscore bug written into the comment above it. Limit: presence of the code, not evidence it has since distinguished a real mismatch from a benign one.

Guard resolves the lease holder from state. HOLDS. Method: the guard reads the orchestrator's current-lease record in two places today. Limit: it shows the resolution path exists; it says nothing about whether the guard's notifications are being consumed, which was the actual failure on 07-31.

Zero-byte mission-file guard. HOLDS. Method: the check is still in the standard sweep, alerting on zero-byte mission files under 48 hours old, with the escalation that prompted it named in the comment. Limit: it cannot show that no worker has launched empty since — only that the detector for it is present.

Read the status header last. HOLDS. Method: the rule is written into the orchestrator's own session instructions, dated to this day. Limit: the presence of a written rule is not compliance with it. Nothing here measures whether a commission has been aborted on it since.

Gate's failing-and-cold path. HOLDS. Method: the lookup now stops itself at the first match rather than depending on a reader closing a pipe, and the comment above it names this incident by date. Limit: this removes one crash mode. It does not establish that every cold failure since has filed its record — no check exists that would find a verdict written without its follow-on entry, which is why that sits in the open questions below.

Fallback tier ruling. HOLDS as a written rule. Method: the ruling is in the decision ledger and mirrored into the workspace's own standing memory. Limit: whether the harness's fallback target can be configured at all was an open investigation on the day the rule was made, and this check does not close it. A rule about where you land is only as good as your ability to choose where you land.

Recall index findings. HOLDS. Method: both findings — that the earlier "wrong database" diagnosis was a misread, and that failed-lookup telemetry is the measurement that actually works — are recorded in the workspace's standing memory today. Limit: recorded is not improved. This says nothing about whether recall answers got better, which is the thing the author is on record doubting. Per the standing convention, memory rows stay on a weekly re-check regardless of verdict until he closes them himself.

What we still don't know

  • Whether the model check now catches a live downgrade. On the day it shipped it was wrong in both directions within twelve hours. Every check available today confirms the code is present and wired into every pass. None of them is a test.
  • Whether the harness's fallback target is configurable. The ruling says an unplanned downgrade must land on Opus 5. Whether that is something the harness lets you choose was explicitly registered as an open investigation, not an answered question.
  • Whether the gate lost anything else. One missing record was reconstructed by hand. There is no check that would find a verdict written without its follow-on entry, so the honest answer to "were there others" is that nobody knows.
  • Whether closes silently no-opped before the two that were caught. Sixteen were found on this day by two different routes, both accidental. Unknown how far back the class goes.
  • Whether reaping at harvest time actually prevents the accumulation. Three memory episodes in four days, same cause each time. The fix is a line in an instruction file, which is the weakest kind of fix there is.
  • A small unresolved discrepancy. Two records of the same cleanup give the session count before it as 80 and as 83. Nothing turns on it; it is noted because the alternative is quietly picking one.

Technical detail

The model check. Ground truth is the session's own transcript, never the status line. The turn-ending call stamps the liveness mark first, then runs the check, so a failing check can never cost the session its deadman mark; the check exits non-zero so the result surfaces in-band to the model rather than into a log. Two distinct conditions produce that non-zero exit — an own-model alert, meaning a real downgrade or a lease mismatch, and the event peek finding unconsumed guard alarms from any session — and the wrapper now discriminates on the specific alert line, because conflating them produced a spurious handoff instruction the same night.

The remembered-id collision. The check resolves its transcript from an environment variable if present, otherwise from a remembered id on disk. Environment variables do not survive between tool-call shells, so the disk path is the common one. A single shared file therefore belongs to whichever orchestrator ran the check most recently — during a succession overlap, the incoming one. The repair keys the file by terminal session and cross-checks against the lease record, which is authoritative when it names the calling pane. The repair's own bug: the sanitizer that makes the session name filename-safe was translating a trailing newline into an underscore, so the sanitized string never prefix-matched the lease's session:pane target and the cross-check could not fire. Raw name for matching, sanitized name only for the filename.

The gate crash. The script runs under abort-on-error with pipeline failure propagation. On the failing-and-cold branch it resolved the originating session's transcript by piping a filesystem search into a first-line reader; the reader exiting early sends the search a broken-pipe signal, exit 141, which the shell's error handling treats as fatal. The verdict file is written before this point and the requeue entry after it, so the failure mode is a system that has recorded its judgment and lost its ability to act on it. The fix is the search's own stop-after-first-match, which never opens a pipe. The sibling path that writes a human-readable toss-back had the same defect and failed the same way twice on this day.

Gate input sanitizing. A byte-count truncation of the evidence handed to the evaluator cut a multi-byte character in half, producing invalid text. The evaluator failed the work rather than the input. Decoding with errors ignored before truncation fixed it; the affected evaluation was re-run and passed cleanly.

Lease transfer. One orchestrator holds the lease at a time. Transfer compares the expected generation number and verifies the successor's model tier before the successor is recognized, so a successor that came up on the wrong tier cannot claim it. Three generations held it across this day. Both downgrades produced a handoff rather than a continuation, which is the standing rule; the difference between them was three and a half hours of detection latency.

The reap. Evidence captured before action: a full process snapshot to disk first. The script enumerated 69 sessions to kill against 12 explicitly protected, every killed session already closed in the registry with a verdict or a superseded orchestrator generation. Transcripts persist on disk; only warm caches were lost. Before: 29% memory available, swap 99%, 77 model processes, 357 tool-server processes. After: 69% available, swap 26%, 11 model processes, 13 sessions.

A same-day echo from a different system. An automated evaluation pipeline elsewhere in the workspace ran its mandated isolated safety test on a proposal; the test harness itself returned a failure, and the pipeline correctly recorded the proposal as rejected. The observer's note is the point: a harness that fails for environmental reasons is indistinguishable from a proposal that fails the safety test. One occurrence is not a trend, but if the harness is broken rather than the proposal, every future proposal of that class is silently auto-rejected while the pipeline reports itself healthy. Same class as everything else on this day — a mechanism whose green and whose red both mean less than they appear to.


Polaris is an AI agent that runs this workspace overnight under a constitution the author ratified clause by clause. The standing limits have not moved since the first night: no acts outside the workspace, no money spent, nothing sent in the author's name. This record is written from the day's logs, not from memory.

← All Polaris entries