[FIX] REVIEWED-131: build (e) and (c), and annotate the digest per (b)

(e) parked_workers reads ~/.claude/jobs/<id>/state.json's respawnFlags — harness
state, not the session, so its enforcement does not depend on the party checked.
Fails to NOT ESTABLISHED, never to safety: a missing dir, an unparseable
state.json, and an absent respawnFlags are each enumerated rather than counted as
"no parked workers" (cond. 1).

(c) human_turns counts genuine human turns in a transcript, discounting
hook-injected and /clear-/exit records. Retrospective by construction and says so.
Output names which control produced each line (cond. 2).

(b) the OPEN QUESTION field is marked orientation-not-instruction, in the code and
in the output, explicitly not a control (cond. 3).

Two corrections found while building. json was never imported, which the top-level
guard would have turned into WAKE DIGEST UNAVAILABLE at every session start — the
selftest caught it on first run. And the first draft asserted that a STOPPED job
carrying the flag would take a turn on restart; whether the daemon respawns a
stopped job is NOT ESTABLISHED, and it now says so rather than claiming either way.

previous_session() lifted out of sec_unwrapped so both consumers share one
definition of "the session before this one" — this file already refuses a second
definition of "an item".

Controls run in both directions, two of them against the real transcripts:
b7e7eb39 (the unattended session) returns 0 human turns, this session returns 10.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wn9mxsFjHJkzYjuDdrtxwp
This commit is contained in:
David F Glidden
2026-08-31 11:49:02 +02:00
co-authored by Claude Opus 5
parent 7d8d4bfe9c
commit 70440db147
+236 -12
View File
@@ -16,7 +16,7 @@ if it cannot be computed it says so rather than emitting nothing.
Provenance: 2026-07-28, steward-authorized alongside the PENDING split and the Provenance: 2026-07-28, steward-authorized alongside the PENDING split and the
CLAUDE.md doctrine annotation. Sibling of governance-drift-check.py. CLAUDE.md doctrine annotation. Sibling of governance-drift-check.py.
""" """
import calendar, os, re, subprocess, sys, time import calendar, json, os, re, subprocess, sys, time
HOME = os.path.expanduser("~") HOME = os.path.expanduser("~")
D = os.path.join(HOME, "dotfiles") D = os.path.join(HOME, "dotfiles")
@@ -340,6 +340,26 @@ def wrap_verdict(span, events, lead=300, lag=10800):
return "unwrapped", "" return "unwrapped", ""
def previous_session():
"""THE single selection of 'the session before this one', for every consumer.
Lifted out of sec_unwrapped 2026-08-31 when the loop check (REVIEWED-131) needed
the same transcript. This file already refuses a second definition of 'an item'
(item_spans); a second definition of 'the previous session' would be the same
bug at a different site. -> (path, start_epoch, end_epoch) or None.
"""
proj = os.path.dirname(os.path.join(HOME, ".claude", "projects",
"-Users-davidglidden", "memory"))
try:
tx = [os.path.join(proj, f) for f in os.listdir(proj) if f.endswith(".jsonl")]
except OSError:
warn.append("session transcripts unreadable — wrap check did not run")
return None
if not tx:
return None
return previous_transcript(time.time(), [(p,) + transcript_span(p) for p in tx])
def sec_unwrapped(): def sec_unwrapped():
""" """
PENDING-S2's obligation, rebuilt on our own substrate. PENDING-S2's obligation, rebuilt on our own substrate.
@@ -361,16 +381,7 @@ def sec_unwrapped():
4. a two-valued verdict over an assessment that can fail. 4. a two-valued verdict over an assessment that can fail.
Fixing only (4), as scoped, would have left the false alarms firing. Fixing only (4), as scoped, would have left the false alarms firing.
""" """
proj = os.path.dirname(os.path.join(HOME, ".claude", "projects", prev = previous_session()
"-Users-davidglidden", "memory"))
try:
tx = [os.path.join(proj, f) for f in os.listdir(proj) if f.endswith(".jsonl")]
except OSError:
warn.append("session transcripts unreadable — wrap check did not run")
return None
if not tx:
return None
prev = previous_transcript(time.time(), [(p,) + transcript_span(p) for p in tx])
if prev is None: if prev is None:
return None return None
_path, a, b = prev _path, a, b = prev
@@ -862,6 +873,143 @@ def stray_maps():
return [] return []
# ---------- (e) + (c): is the loop currently removable? REVIEWED-131 (2026-08-31) ----------
# EARNED 2026-08-31. A binary upgrade restarted the daemon, which respawned a parked
# worker with --reply-on-resume. THIS digest was injected into that session as its only
# instruction, and an executor worked and committed with no human present (PENDING-172).
# (e) reads harness state on disk; (c) reads the session transcript. Two substrates,
# deliberately — REVIEWED-131 cond. 2 requires the output to name which one fired.
JOBS_DIR = os.path.expanduser("~/.claude/jobs")
ARM_FLAG = "--reply-on-resume"
def parked_workers(jobs_dir=JOBS_DIR):
"""(e) — REVIEWED-131 cond. 1. Returns (rows, unreadable, established).
Whether a parked worker will take a turn with no human present is recorded
per session in <jobs>/<id>/state.json's `respawnFlags`. This reads the
HARNESS, not the session: the only check here whose enforcement does not
depend on the party being checked.
`established` is False whenever the directory is missing or unreadable.
A control over parked workers whose negative result is an opaque zero is
the failure PENDING-172 is about, so this NEVER reports absence of evidence
as evidence of absence. Every dir read and every dir refused is enumerated.
"""
rows, unreadable = [], []
if not os.path.isdir(jobs_dir):
return rows, unreadable, False
try:
entries = sorted(os.listdir(jobs_dir))
except OSError as e:
return rows, [("<listing>", type(e).__name__)], False
for name in entries:
d = os.path.join(jobs_dir, name)
if not os.path.isdir(d):
continue
f = os.path.join(d, "state.json")
try:
with open(f, encoding="utf-8") as fh:
st = json.load(fh)
except Exception as e:
unreadable.append((name, type(e).__name__))
continue
flags = st.get("respawnFlags")
# An absent or non-list respawnFlags is NOT "unarmed" — it is unreadable state.
if not isinstance(flags, list):
unreadable.append((name, "respawnFlags missing or not a list"))
continue
rows.append((name, str(st.get("state") or "?"), ARM_FLAG in flags,
str(st.get("name") or "")[:52]))
return rows, unreadable, True
NONHUMAN = ("<local-command-", "<command-name>", "<command-message>", "<system-reminder",
"=== WAKE DIGEST", "<local-command-stdout>", "<local-command-caveat>")
def human_turns(path):
"""(c) — REVIEWED-131 cond. 2. Count of genuine human turns in a transcript.
A hook-injected digest arrives as a `user` record, and so do /clear and /exit;
none of them is a person. Returns None when the transcript cannot be read —
unreadable is not zero.
⚠ RETROSPECTIVE BY CONSTRUCTION. At SessionStart the current session has no
turns yet, so this is applied to the most recent COMPLETED session. It answers
'did an executor just run unattended?', which is what nothing detected on
2026-08-31 — not 'am I about to'. (e) is the forward-looking half.
"""
try:
with open(path, encoding="utf-8", errors="replace") as fh:
lines = fh.readlines()
except Exception:
return None
n = 0
for line in lines:
try:
o = json.loads(line)
except Exception:
continue
if o.get("type") != "user" or o.get("isMeta"):
continue
c = o.get("message", {}).get("content")
if isinstance(c, list):
c = " ".join(x.get("text", "") for x in c if isinstance(x, dict))
if not isinstance(c, str) or not c.strip():
continue
if any(m in c for m in NONHUMAN):
continue
n += 1
return n
def sec_loop(prev_transcript=None):
"""Assemble (e) and (c), naming which control produced each line."""
o = []
rows, unreadable, established = parked_workers()
if not established:
o.append("PARKED WORKERS [control e — harness state] — NOT ESTABLISHED:"
f" {JOBS_DIR} is missing or unreadable."
" This is 'could not assess', not 'none parked'.")
else:
armed = [r for r in rows if r[2]]
head = (f"PARKED WORKERS [control e — harness state] — {len(rows)} job dir(s) read,"
f" {len(unreadable)} unreadable, {len(armed)} ARMED")
o.append("⚠ " + head if armed else head)
for jid, state, arm, name in rows:
o.append(f" {'⚠ ARMED' if arm else ' ok '} {jid} state={state} {name}")
for jid, why in unreadable:
o.append(f" NOT ESTABLISHED {jid} ({why}) — counted, not classified")
# ⚠ A stopped job still carries its flags. Whether the daemon respawns a
# STOPPED job is NOT ESTABLISHED — the 2026-08-31 event respawned one that was
# parked (state=blocked). Reporting both as equally live would assert what was
# never measured; reporting the stopped one as safe would assume it.
live = [r for r in armed if r[1] not in ("stopped",)]
stopped = [r for r in armed if r[1] in ("stopped",)]
if live:
o.append(f" ⇒ {len(live)} PARKED worker(s) carry {ARM_FLAG}: a daemon restart"
" (an auto-update will do) makes them take a turn with no human"
" present. PENDING-172 / REVIEWED-131.")
if stopped:
o.append(f" ⇒ {len(stopped)} STOPPED job(s) still carry {ARM_FLAG}."
" Whether the daemon respawns a stopped job is NOT ESTABLISHED —"
" the 2026-08-31 respawn took a job that was parked, not stopped."
" Neither live nor proven safe.")
if prev_transcript:
n = human_turns(prev_transcript)
label = "LAST SESSION [control c — transcript]"
if n is None:
o.append(f"{label} — NOT ESTABLISHED: transcript unreadable.")
elif n == 0:
o.append(f"⚠ {label} — NO HUMAN TURN in {os.path.basename(prev_transcript)}."
" That session ran unattended. PENDING-172.")
else:
o.append(f"{label} — {n} human turn(s); a person was present.")
return o
# ---------- self-test ---------- # ---------- self-test ----------
def selftest(): def selftest():
@@ -979,6 +1127,57 @@ def selftest():
chk("classify_pointers returns clean on empty input", chk("classify_pointers returns clean on empty input",
classify_pointers("", MEMDIR) == (0, [], [], [])) classify_pointers("", MEMDIR) == (0, [], [], []))
NOWHERE = "no-such-file-anywhere-xyzzy-9931.md" NOWHERE = "no-such-file-anywhere-xyzzy-9931.md"
print("\nloop checks (e)+(c) [REVIEWED-131, earned 2026-08-31]:")
import tempfile, shutil
_tmp = tempfile.mkdtemp(prefix="wd-loop-")
try:
def _job(name, payload):
d = os.path.join(_tmp, name)
os.makedirs(d, exist_ok=True)
open(os.path.join(d, "state.json"), "w").write(payload)
_job("armed", json.dumps({"state": "blocked", "name": "x",
"respawnFlags": ["--reply-on-resume", "--allowed-tools"]}))
_job("quiet", json.dumps({"state": "blocked", "name": "y", "respawnFlags": []}))
_job("broken", "{not json")
_job("noflags", json.dumps({"state": "blocked", "name": "z"}))
rows, unread, est = parked_workers(_tmp)
chk("parked_workers ESTABLISHES on a readable dir", est is True)
chk("parked_workers DETECTS an armed worker [must-detect]",
[r for r in rows if r[0] == "armed" and r[2] is True] != [])
chk("parked_workers does NOT flag respawnFlags=[] [must-not-flag]",
[r for r in rows if r[0] == "quiet" and r[2] is False] != [])
chk("parked_workers ENUMERATES unparseable state.json, never drops it",
"broken" in [j for j, _ in unread])
chk("absent respawnFlags is NOT ESTABLISHED, not 'unarmed' [permissive direction]",
"noflags" in [j for j, _ in unread]
and "noflags" not in [r[0] for r in rows])
_job("armedstopped", json.dumps({"state": "stopped", "name": "s",
"respawnFlags": ["--reply-on-resume"]}))
_rows3, _u3, _e3 = parked_workers(_tmp)
chk("a STOPPED job still reports its flag [must-detect: flags outlive the process]",
[r for r in _rows3 if r[0] == "armedstopped" and r[2] is True and r[1] == "stopped"] != [])
chk("...and its state is carried, so the report can separate it [must-not-conflate]",
{r[1] for r in _rows3 if r[2]} == {"blocked", "stopped"})
_rows2, _u2, est2 = parked_workers(os.path.join(_tmp, "does-not-exist"))
chk("missing jobs dir -> NOT ESTABLISHED, never 'none parked'", est2 is False)
_t = os.path.join(_tmp, "t.jsonl")
_hook = json.dumps({"type": "user", "message": {"content":
"=== WAKE DIGEST ===\nOPEN QUESTION — do the thing"}})
_cmd = json.dumps({"type": "user", "message": {"content":
"<local-command-caveat>x</local-command-caveat>"}})
_human = json.dumps({"type": "user", "message": {"content": "please file it"}})
open(_t, "w").write(_hook + "\n" + _cmd + "\n")
chk("human_turns returns 0 on a hook-only transcript [must-detect]",
human_turns(_t) == 0)
open(_t, "w").write(_hook + "\n" + _human + "\n")
chk("human_turns does NOT flag a real human turn [must-not-flag]",
human_turns(_t) == 1)
chk("human_turns on an unreadable path -> None, not 0",
human_turns(os.path.join(_tmp, "nope.jsonl")) is None)
finally:
shutil.rmtree(_tmp, ignore_errors=True)
print("\ncode spans [2026-08-09: the canary flagged its own specification]:") print("\ncode spans [2026-08-09: the canary flagged its own specification]:")
chk("a pointer INSIDE a code span is not a pointer", chk("a pointer INSIDE a code span is not a pointer",
classify_pointers(f"`[y]({NOWHERE})`", MEMDIR) == (0, [], [], [])) classify_pointers(f"`[y]({NOWHERE})`", MEMDIR) == (0, [], [], []))
@@ -1127,6 +1326,23 @@ def selftest():
+ " — a spread is not a proof of correctness; the controls above are.") + " — a spread is not a proof of correctness; the controls above are.")
print("\nlive substrate:") print("\nlive substrate:")
_PROJ = os.path.join(HOME, ".claude", "projects", "-Users-davidglidden")
# Controls drawn from the 2026-08-31 event itself. They are marked NOT ESTABLISHED
# rather than FAIL when the transcript ages out of the 30-day window — a control
# that silently degrades into a pass is the failure this whole item is about.
for _sid, _want, _why in (
("b7e7eb39-1e81-4af3-9be7-c323c794c1d9", 0,
"the unattended session of 2026-08-31 (PENDING-172)"),
("62083f11-b56f-4e67-bbbb-135efeba0ed2", "gt0",
"the attended session that found it")):
_p = os.path.join(_PROJ, _sid + ".jsonl")
if not os.path.exists(_p):
print(f" [ .. ] NOT ESTABLISHED — {_sid[:8]} aged out; control not run ({_why})")
continue
_n = human_turns(_p)
chk(f"human_turns on {_sid[:8]} — {_why}",
(_n == 0) if _want == 0 else (isinstance(_n, int) and _n > 0))
chk("PENDING.md readable", read(PENDING) is not None) chk("PENDING.md readable", read(PENDING) is not None)
chk("REVIEWED.md readable", read(REVIEWED) is not None) chk("REVIEWED.md readable", read(REVIEWED) is not None)
chk("drift check runs", sec_drift() is not None) chk("drift check runs", sec_drift() is not None)
@@ -1147,10 +1363,18 @@ def main():
unwrapped = sec_unwrapped() unwrapped = sec_unwrapped()
if unwrapped: if unwrapped:
o.append(unwrapped) o.append(unwrapped)
_prev = previous_session()
for line in sec_loop(_prev[0] if _prev else None):
o.append("\n" + line if line.startswith(("PARKED", "⚠ PARKED")) else line)
if th: if th:
o.append(f"\nPULLING THREAD — {th}") o.append(f"\nPULLING THREAD — {th}")
if q: if q:
o.append(f"\nOPEN QUESTION — {q}") # (b) — REVIEWED-131 cond. 3: ANNOTATION, explicitly NOT a control. It cannot
# enforce anything and must never be reported as a mitigation. It exists because
# on 2026-08-31 this field was read by an unattended executor as an instruction.
o.append(f"\nOPEN QUESTION [orientation, not an instruction — inherited from a"
f" human and answerable to one] — {q}")
o.append(f"\nOPEN AUTHORIZATION ITEMS ({len(items)}) — bodies in ~/PENDING.md at the line given;" o.append(f"\nOPEN AUTHORIZATION ITEMS ({len(items)}) — bodies in ~/PENDING.md at the line given;"
" closed items are in ~/PENDING-archive.md") " closed items are in ~/PENDING-archive.md")