From 4dc38e69b9b7e7e769a9ce37ecd00d4439910eb2 Mon Sep 17 00:00:00 2001 From: David F Glidden Date: Sun, 2 Aug 2026 11:59:47 +0200 Subject: [PATCH] [FIX] wake-digest: resolve rulings by subject, not by number sec_pending() suppressed any PENDING-N whose number appeared as a REVIEWED-N, never checking the ruling was about that item. The two sequences have drifted: REVIEWED-84 rules on PENDING-87. So PENDING-84 was invisible at wake on the very morning the steward's pulling thread pointed at it, and closing it later produced no visible count change because it had never been counted. Found 2026-08-01 at wake, surfaced not fixed; register row 179. Resolution now reads the PENDING each ruling names in its header. Rulings that name none (REVIEWED-78, -81, -82) suppress nothing. Extracted as ruled_pendings() so it is testable at all -- the logic was buried in a file-reading routine with no way to exercise it. Five self-tests added, including the 2026-08-01 bug as a regression control and an empty-input positive control. Bounded-change proof against the live files: 18 -> 19 visible. Three items surfaced that were falsely hidden (PENDING-78, -81, -82); two stopped being shown that are genuinely ruled (PENDING-87 by REVIEWED-84, PENDING-88 by REVIEWED-85 -- no REVIEWED-87 or -88 exists, so number-matching had never suppressed them). That proof also caught an overclaim in this fix's own docstring, which asserted the correction could only ever surface more items and never fewer. It corrects in both directions. Comment repaired to say what the code does; the false version would have been a comment promising behaviour the code does not deliver, in a file whose purpose is to stop governance hiding from the steward. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01WuMjg3ipEVa3n8CoSzoyvc --- scripts/wake-digest.py | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/scripts/wake-digest.py b/scripts/wake-digest.py index 3fb9e88..490fa40 100755 --- a/scripts/wake-digest.py +++ b/scripts/wake-digest.py @@ -132,17 +132,44 @@ def sec_pause(): return newest, span +def ruled_pendings(reviewed_text): + """ + The set of PENDING ids that rulings actually DISPOSE OF. + + Resolved by the PENDING each ruling NAMES in its header, never by the REVIEWED + number. Those numbering sequences have drifted apart: REVIEWED-84 rules on + PENDING-87. Matching on the number alone suppressed PENDING-84 from the wake + digest on the very morning the steward's pulling thread pointed at it, and + closing that item later produced no visible change because it had never been + counted. Measured at the time: 9 suppressed, 8 correctly, 1 falsely. + + A ruling whose header names no PENDING (REVIEWED-78, -81, -82 …) suppresses + nothing. + + The correction runs in BOTH directions, and an earlier draft of this docstring + claimed otherwise — that it could only ever surface more, never fewer. That was + an overclaim, caught by the change proof rather than by reading. Measured against + the live files at the time of the fix: 3 items surfaced that had been falsely + hidden (PENDING-78, -81, -82 — like-numbered rulings exist, concerning other + matters), and 2 stopped being shown that were genuinely ruled (PENDING-87 by + REVIEWED-84, PENDING-88 by REVIEWED-85 — no REVIEWED-87 or -88 exists, so the + number-match had never suppressed them). Net 18 → 19 visible. Number-matching + was wrong in both directions; only subject-matching is right in either. + """ + return set(re.findall(r"^## REVIEWED-\S+\s*—\s*PENDING-(\S+?)\s*—", reviewed_text, re.M)) + + def sec_pending(): t = read(PENDING) if t is None: warn.append("PENDING.md unreadable") return [] rt = read(REVIEWED) or "" - rev = {m for m in re.findall(r"^## REVIEWED-(\S+)", rt, re.M)} + ruled = ruled_pendings(rt) items = [] for h, ln in open_items(t): m = re.match(r"PENDING-(\S+?)\s*—", h) - if m and m.group(1) in rev: + if m and m.group(1) in ruled: continue items.append((h, ln, tag_of(t, h))) if not items: @@ -452,6 +479,16 @@ def selftest(): chk("tag_of reads a tag", tag_of("## PENDING-9 — t\n**Date:** d\n**Tag:** [ESCALATE]\n", "PENDING-9 — t") == "[ESCALATE]") chk("tag_of returns '' when absent", tag_of("## PENDING-9 — t\n", "PENDING-9 — t") == "") + chk("ruled_pendings resolves by the NAMED pending, not the REVIEWED number", + ruled_pendings("## REVIEWED-84 — PENDING-87 — Order attestation") == {"87"}) + chk("ruled_pendings does NOT suppress the like-numbered item [the 2026-08-01 bug]", + "84" not in ruled_pendings("## REVIEWED-84 — PENDING-87 — Order attestation")) + chk("ruled_pendings ignores a ruling that names no PENDING", + ruled_pendings("## REVIEWED-82 — Read-only MCP server: eyes on the substrate") == set()) + chk("ruled_pendings handles non-numeric families", + ruled_pendings("## REVIEWED-9 — PENDING-S2 — hook-aware deposit") == {"S2"}) + chk("ruled_pendings returns empty on empty input [positive control]", + ruled_pendings("") == set()) chk("extract_anchor pulls verbatim", extract_anchor("noise\n**PULLING THREAD:** do the thing.\n\nmore", "PULLING THREAD") == "do the thing.")