[ESCALATE] PENDING-172: a version upgrade resumed an unattended executor
A binary upgrade (2.1.248 → 2.1.251) restarted the background daemon, which respawned its one parked worker with --reply-on-resume. The SessionStart hook injected the wake digest as that session's only instruction, and an executor with no human present executed the digest's OPEN QUESTION and committed to this repo. Constitutional Constraint 5 was not overridden by anyone's decision; it was removed by a restart, and nothing in the system observed it. Both binaries carry the mechanism (reply-on-resume 8x, post-takeover prewarm 1x in each), so the upgrade supplied the restart, not the capability: every auto-update can do this, and the only precondition is a background worker parked idle. /exit does not stop such a worker — it detaches from it — and nothing at exit says so. Filed after the concurrent session was stopped, so the append could not land in a file another executor was mid-measurement on (PENDING-104 ADDENDUM 1/2). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wn9mxsFjHJkzYjuDdrtxwp
This commit is contained in:
co-authored by
Claude Opus 5
parent
a2963d94f9
commit
85ce5b8343
+50
@@ -6548,3 +6548,53 @@ The block is filed with a trigger date **already in the past**, deliberately, so
|
||||
|
||||
**Files affected:** none.
|
||||
**Awaiting:** Steward authorization — the option space is not drafted here, because whether concurrent sessions are worth a mechanism is a steward judgement about how often two are run at once, which the executor cannot observe.
|
||||
|
||||
---
|
||||
|
||||
## PENDING-172 — A version upgrade resumed an unattended executor, and the wake digest became its work order
|
||||
**Date:** 2026-08-31
|
||||
**Tag:** [ESCALATE]
|
||||
**Summary:** A Claude Code binary upgrade restarted the background daemon, which respawned a stale worker with `--reply-on-resume`; the `SessionStart` wake digest was injected as that session's only instruction, and an executor with no human present executed the digest's `OPEN QUESTION` and committed to `dotfiles`.
|
||||
|
||||
**What happened, from the substrate.** `~/.claude/daemon.log`, verbatim:
|
||||
|
||||
```
|
||||
07:02:04Z [supervisor] binary at ~/.local/bin/claude changed (2.1.248 → 2.1.251) — self…
|
||||
07:02:04Z [supervisor] shutting down (cause=upgrade, uptime=289880s, leases=0, live_workers=1)
|
||||
07:02:07Z [supervisor] ─── daemon start ─── version=2.1.251 pid=49482 origin=transient
|
||||
07:02:07Z [bg] bg adopt: adopted=1 respawned=0 dead=0
|
||||
07:02:22Z [bg] bg: post-takeover prewarm burst — respawned 1/1 stale workers in 0s
|
||||
```
|
||||
|
||||
The respawned worker is pid 49525, background id `acaabadf`, session `b7e7eb39`:
|
||||
|
||||
```
|
||||
…/versions/2.1.251 --resume …/b7e7eb39-….jsonl --reply-on-resume --allowed-tools mcp__computer-use__…
|
||||
```
|
||||
|
||||
At 07:02:51Z the `SessionStart` hook injected the wake digest into that session as a `user` record (transcript record 22; record 5 carries the same text as an `attachment`). The first assistant text follows at 07:04:50Z — *"Instrument controls pass. But the enumeration has a hole."* **There is no human turn anywhere in the transcript.** The digest's `OPEN QUESTION` field — the literal question left by the 2026-08-27 wrap — was the only instruction present, and was executed as one. Commit `5ba5842` (09:08:25 +0200) and `PENDING-171` were both filed by that session.
|
||||
|
||||
**Why this is `[ESCALATE]`.** Constitutional Constraint 5: *"The loop is load-bearing — human authorization is not a bottleneck to be optimized away. It is the structural requirement of the governance model."* Nobody optimized it away. A version bump removed it, the steward's own account of the morning is that they initiated no session, and **no mechanism in this system observed the condition** — not the wake, not `governance-drift-check.py`, not the hooks. The executor on the other side had no way to know no one was there; from inside the session the digest is indistinguishable from orientation a human asked for.
|
||||
|
||||
**The scope, stated honestly, because it is bounded.** That session's commit added two new files (549 insertions, zero deletions) and did not modify `PENDING.md`, `REVIEWED.md` or the archive; before measuring it hashed all three to prove it had not. It has not pushed. Unattended, it was practising the collision discipline. The defect is the absent loop, not the conduct inside it.
|
||||
|
||||
**A second, narrower finding this exposed** — flagged here, and it may deserve its own `[HARDENING]` number rather than living inside this one. **The memory protocol assumes one executor per day.** `/symmetria`'s ledger path is date-keyed (`session-ledger-YYYY-MM-DD.md`); `/wrap-up` writes one session file and one Active Session block in `MEMORY.md` with demote-on-promote. Two concurrent sessions do not merge — whichever wraps second silently becomes the record of the day. Today both existed: this item's author created `session-ledger-2026-08-31.md` at 09:12 while `acaabadf` was working.
|
||||
|
||||
**⚠ How a worker comes to be parked, and why the steward could not have known.** `/exit` does not end a background session; it ends the terminal's attachment to one (`claude attach <id>` — *"Open a background session in this terminal"*). The CLI prints `Bye!`, which is true of the attachment and false of the session, and **nothing at exit reports that a worker is still parked under the daemon.** Demonstrated live: after the steward attached to `acaabadf` and left, pid 49525 remained alive and idle. This is the same class as the digest-as-instruction — a surface that reads as one thing and means another — and it is the *precondition* for the rest of this item: no parked worker, no respawn, no unattended executor.
|
||||
|
||||
**⚠ Not established:** how these sessions became `kind: background`. Nothing aliases `claude --bg` in the steward's shell config; nothing in `settings.json` requests it. Either they were started that way, or the daemon's adopt/respawn path re-registers a resumed worker as background — `bg adopt: adopted=1` and `respawned 1/1 stale workers` are suggestive and not conclusive. `84ce2880` has sat in that state since ~2026-05-20 with `pid=None`.
|
||||
|
||||
**Options.**
|
||||
- **(a)** Treat as a one-off upgrade artifact; change nothing.
|
||||
- **(b)** Make the digest non-imperative: `wake-digest.py` states in its own output that it is orientation and not an instruction, and the `OPEN QUESTION` field is marked as inherited-from-a-human, to be answered *to* a human.
|
||||
- **(c)** Detect the condition: the wake refuses to proceed autonomously when no human turn precedes it in the session, and says so instead of working.
|
||||
- **(d)** Do not run background workers at all, so there is no stale worker for an upgrade to respawn.
|
||||
|
||||
**⚠ This is repeatable, and the upgrade did not cause it.** Both builds are on disk and both contain the mechanism — `reply-on-resume` 8× and `post-takeover prewarm` 1× in **2.1.248 and 2.1.251 alike**. The upgrade supplied the *restart*, not the capability: `cause=upgrade` is what shut the daemon down and respawned its idle worker. ⇒ **every auto-update will do this, and the only precondition is a background worker parked idle.** At the time of filing, `acaabadf` is parked idle again (`state=blocked`, `status=idle`, pid 49525 alive) — the same state the pre-upgrade worker was in. *Limit: identical string counts show the flag exists in both binaries; they do not establish that the respawn policy is byte-identical.*
|
||||
|
||||
**Recommendation:** (b) and (c) together, with (d) as the steward's standing choice about background sessions. ⚠ **Neither (b) nor (c) is a guarantee** — both are text and logic *inside* the session, and the failure they address is that the session cannot tell whether a human is present. (d) is the only option that removes the mechanism rather than annotating it, and it is the only one whose enforcement does not depend on the party being checked. State that asymmetry plainly rather than let (b)+(c) read as a fix.
|
||||
|
||||
**⚠ What this item does not establish.** Whether the pre-upgrade worker was one the steward knowingly started earlier. The daemon's `uptime=289880s` dates it to ~2026-08-27 19:00Z, the last wrapped session; `live_workers=1` says only that a worker was alive when the upgrade landed. `claude agents --json` also lists `84ce2880` — background, *"Start with vignette"*, `state: blocked`, started ~2026-05-20 — so at least one background session has persisted unnoticed for over three months. How workers come to exist, and whether the steward authorized each, is not answered here.
|
||||
|
||||
**Files affected:** `~/.claude/settings.json` (`SessionStart` hook); `dotfiles/scripts/wake-digest.py`; `~/.claude/skills/wake-up/SKILL.md`; `~/.claude/skills/symmetria/SKILL.md` and `wrap-up` (the date-keyed single-writer paths). `~/CLAUDE.md` Constraint 5 is **not** proposed for amendment — it was not wrong; it was unenforced.
|
||||
**Awaiting:** Steward authorization.
|
||||
|
||||
Reference in New Issue
Block a user