[FIX] A home for steward-facing maps; memory pointers made portable and self-diagnosing

Two loose ends closed before the Harrison re-gate, both surfaced by the steward.

Memory pointers. Six links in MEMORY.md / MEMORY-reference.md pointed at files
that all existed, via hand-counted relative depths that resolved from neither of
the memory dir's two addresses (it is ~/dotfiles/claude/memory, symlinked from
~/.claude/projects/…). The wake canary detected this correctly FOUR times over
two days and the banked remedy was to change the canary's path resolution — i.e.
to silence a true positive. The defect was never the pointers: the alarm emitted
one undifferentiated word, MISSING, so every firing had to be re-diagnosed by
hand and the cheapest re-diagnosis is always "known bug". wake-digest.py now
reports four outcomes (ok / mis-authored / dead / non-portable), hands back the
exact replacement, and carries a regression control replaying this bug's shape.
Pointers are home-anchored (~/…), not absolute — steward's correction; absolute
hardcodes this machine into the repo whose purpose is surviving a machine change.

Maps. With the noise gone, one genuine dead pointer surfaced:
arc-current-state-2026-05-07.md, a live ARC dashboard the steward read to orient.
It lived only on the Desktop and went with a tidy-up. A census found four more in
the same condition, zero copies anywhere — including the Making-Sequence
architecture and reading list, load-bearing for current corpus work. The cause is
structural: code, session records and memories are durable; the one artifact
class addressed to the steward had no home. All five now live in maps/ and are
symlinked back to their exact Desktop paths (Desktop view unchanged), moved under
a checksum gate with a positive control. wake-digest.py reports stray Desktop
maps; it never moves them — the Desktop is the steward's.

How to verify:
  python3 scripts/wake-digest.py --selftest     # 28 controls, PASS
  python3 scripts/wake-digest.py | grep -A3 'MEMORY POINTERS'
  cd ~/Desktop && shasum -a 256 *.md            # reads through the symlinks

What was not changed: ~/CLAUDE.md and REVIEWED.md untouched (Constraint #1). No
Desktop file was deleted or renamed. MEMORY-reference.md's May entry is marked
superseded, not rewritten.

Known limitation: maps/ has no successor for the ARC map's FUNCTION — the
open-work register carries the content, but nothing exists that the steward can
open and orient by. Named in the entry rather than quietly closed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WuMjg3ipEVa3n8CoSzoyvc
This commit is contained in:
David F Glidden
2026-07-28 14:10:03 +02:00
co-authored by Claude Opus 5
parent 9339abf412
commit 19bddd5ecb
11 changed files with 2158 additions and 8 deletions
+179
View File
@@ -294,6 +294,134 @@ def brief_age_days():
return (time.time() - time.mktime((y, mo, d, 12, 0, 0, 0, 0, -1))) / 86400
# ---------- memory pointers ----------
#
# Provenance: 2026-07-28. Earned expensively. Six pointers in MEMORY.md /
# MEMORY-reference.md pointed at files that all EXIST via paths that never
# resolved (two at depth 4, four at depth 5 — hand-counted, never checked).
# The wake canary detected this and FIRED FOUR TIMES over two days. Each firing
# was re-diagnosed by hand and dismissed as "known canary bug", and the banked
# remedy was to change the canary's path resolution — i.e. to make the
# instrument agree with the broken data and stop reporting a TRUE POSITIVE.
#
# The defect was never the pointers; it was that the alarm said only MISSING.
# An undifferentiated alarm has to be re-diagnosed on every firing, and the
# cheapest re-diagnosis is always "known bug". So this reports three outcomes,
# never one, and hands back the exact replacement string:
#
# OK resolves from the file's real location
# MIS-AUTHORED target exists, path as written does not reach it -> fix given
# DEAD no file of that name anywhere -> a decision
#
# Relative pointers are resolved against the file's REAL directory, because the
# memory dir has two live addresses (~/.claude/projects/…/memory is a symlink to
# ~/dotfiles/claude/memory) and a relative escape is only ever valid from one of
# them. That ambiguity is structural: any pointer LEAVING the directory must be
# home-anchored (`~/…`) to be correct via both routes.
#
# Home-anchored, NOT absolute — steward's correction, same session. `/Users/
# davidglidden/…` resolves today and hardcodes this machine into a dotfiles repo
# whose whole purpose is surviving a machine change. `expanduser` is already the
# idiom three lines into this file, and `~/_Dev/…` is already the idiom in the
# memory files' own prose (17 uses). Absolute still VALIDATES here — it is not
# wrong, only unportable — but it is counted and reported so it cannot re-enter
# silently.
NONPORTABLE_RE = re.compile(r"^/(Users|home)/")
MEM_INDEXES = ["MEMORY.md", "MEMORY-reference.md"]
POINTER_RE = re.compile(r"\]\(([^)\s]+\.md)\)")
def find_basename(name):
"""Bounded search for a file of this basename. None => genuinely dead."""
local = os.path.join(os.path.realpath(MEM), name)
if os.path.exists(local):
return local
hit = sh(["find", os.path.join(HOME, "_Dev"), "-maxdepth", "6",
"-name", name, "-not", "-path", "*/node_modules/*",
"-not", "-path", "*/.git/*"], timeout=8)
return hit.splitlines()[0] if hit else None
def resolve(raw, base):
"""Home-anchored, absolute, or relative-to-the-file's-real-directory."""
if raw.startswith("~"):
return os.path.expanduser(raw)
if os.path.isabs(raw):
return raw
return os.path.join(base, raw)
def classify_pointers(text, base):
"""(n_checked, mis_authored, dead, nonportable) — outcomes, never one word."""
n, mis, dead, nonport = 0, [], [], []
for m in POINTER_RE.finditer(text):
raw = m.group(1)
if raw.startswith(("http://", "https://", "mailto:")):
continue
n += 1
line = text.count("\n", 0, m.start()) + 1
if NONPORTABLE_RE.match(raw):
nonport.append((line, raw))
if os.path.exists(resolve(raw, base)):
continue
found = find_basename(os.path.basename(raw))
if found:
home = os.path.realpath(HOME)
real = os.path.realpath(found)
fix = "~" + real[len(home):] if real.startswith(home + os.sep) else real
mis.append((line, raw, fix))
else:
dead.append((line, raw, None))
return n, mis, dead, nonport
def sec_pointers():
total, mis, dead, nonport = 0, [], [], []
for name in MEM_INDEXES:
path = os.path.realpath(os.path.join(MEM, name))
text = read(path)
if text is None:
warn.append(f"memory pointers: {name} unreadable")
continue
n, m_, d_, np_ = classify_pointers(text, os.path.dirname(path))
total += n
mis += [(name,) + t for t in m_]
dead += [(name,) + t for t in d_]
nonport += [(name,) + t for t in np_]
return total, mis, dead, nonport
# ---------- stray maps ----------
#
# A "map" is an artifact the steward reads directly to orient (dashboard, corpus
# index, reading list). Provenance 2026-07-28: arc-current-state-2026-05-07.md
# was exactly that, lived only on the Desktop, and vanished with a tidy-up; a
# census then found four more in the same condition, zero copies anywhere. Maps
# now live in ~/dotfiles/maps and are symlinked back to the Desktop. This reports
# any Desktop .md that is NOT such a symlink — it never moves anything, because
# the Desktop is the steward's. See maps/README.md.
DESKTOP = os.path.join(HOME, "Desktop")
MAPS_DIR = os.path.join(D, "maps")
def is_homed(path, maps_root):
"""True iff path is a symlink resolving inside maps_root."""
if not os.path.islink(path):
return False
return os.path.realpath(path).startswith(os.path.realpath(maps_root) + os.sep)
def stray_maps():
try:
return [n for n in sorted(os.listdir(DESKTOP))
if n.endswith(".md") and not is_homed(os.path.join(DESKTOP, n), MAPS_DIR)]
except Exception:
warn.append("stray-map check: Desktop unreadable")
return []
# ---------- self-test ----------
def selftest():
@@ -341,6 +469,39 @@ def selftest():
extract_anchor("**A:** " + "w " * 200, "A", limit=50).endswith("…[truncated]"))
chk("extract_anchor returns None when the anchor is absent",
extract_anchor("no anchor here", "PULLING THREAD") is None)
MEMDIR = os.path.realpath(MEM)
TOUCH = "~/_Dev/studium-engine/docs/the-chamber-touchstone.md"
chk("classify_pointers accepts an in-directory relative pointer",
classify_pointers("see [x](MEMORY.md)", MEMDIR) == (1, [], [], []))
chk("classify_pointers accepts a HOME-ANCHORED pointer [the chosen form]",
classify_pointers(f"[t]({TOUCH})", MEMDIR) == (1, [], [], []))
chk("classify_pointers calls a real-target/bad-path pointer MIS-AUTHORED"
" [the 4-firing bug, as a regression control]",
(lambda r: r[0] == 1 and len(r[1]) == 1 and not r[2])(
classify_pointers(
"[t](../../../../_Dev/studium-engine/docs/the-chamber-touchstone.md)",
MEMDIR)))
chk("...and the fix it hands back is home-anchored, not machine-absolute",
classify_pointers("[t](../../../../_Dev/studium-engine/docs/"
"the-chamber-touchstone.md)", MEMDIR)[1][0][2] == TOUCH)
chk("classify_pointers calls a nowhere-target pointer DEAD [must NOT collapse"
" into mis-authored]",
(lambda r: r[0] == 1 and not r[1] and len(r[2]) == 1)(
classify_pointers("[x](no-such-file-anywhere-xyzzy-9931.md)", MEMDIR)))
chk("classify_pointers flags a machine-absolute pointer NON-PORTABLE even though"
" it resolves [prevents silent re-entry]",
(lambda r: len(r[3]) == 1 and not r[1] and not r[2])(
classify_pointers(f"[t]({os.path.expanduser(TOUCH)})", MEMDIR)))
chk("classify_pointers ignores http links",
classify_pointers("[x](https://a.md)", MEMDIR) == (0, [], [], []))
chk("classify_pointers returns clean on empty input",
classify_pointers("", MEMDIR) == (0, [], [], []))
chk("is_homed TRUE for a Desktop map symlinked into maps/ [live substrate]",
is_homed(os.path.join(DESKTOP, "corpus-index-2026-07-26.md"), MAPS_DIR))
chk("is_homed FALSE for a regular file [negative control — must not pass everything]",
not is_homed(os.path.join(MAPS_DIR, "README.md"), MAPS_DIR))
chk("is_homed FALSE for a symlink pointing OUTSIDE maps/",
not is_homed(os.path.join(HOME, ".claude"), MAPS_DIR))
print("\nlive substrate:")
chk("PENDING.md readable", read(PENDING) is not None)
chk("REVIEWED.md readable", read(REVIEWED) is not None)
@@ -377,6 +538,24 @@ def main():
o.append(f"\nGOVERNANCE DRIFT — ~/CLAUDE.md: {drift} substrate-contradicted claim(s)"
" (detection only; correction needs [ESCALATE])")
ptot, pmis, pdead, pnp = sec_pointers()
o.append(f"\nMEMORY POINTERS — {ptot} checked · {len(pmis)} mis-authored · {len(pdead)} dead"
+ (f" · {len(pnp)} non-portable" if pnp else ""))
for f, ln, raw, fix in pmis:
o.append(f" MIS-AUTHORED {f}:{ln} {raw}")
o.append(f" → target exists; replace with: {fix}")
for f, ln, raw, _ in pdead:
o.append(f" DEAD {f}:{ln} {raw} (no file of that name in ~/_Dev or the memory dir)")
for f, ln, raw in pnp:
o.append(f" NON-PORTABLE {f}:{ln} {raw} (resolves, but hardcodes this machine)")
strays = stray_maps()
if strays:
o.append(f"\nSTRAY MAPS — {len(strays)} Desktop .md with no tracked copy in"
" ~/dotfiles/maps/ (one tidy-up from gone; see maps/README.md)")
for s in strays:
o.append(f" {s}")
age = brief_age_days()
if age is None:
o.append("\n.APP BRIEF — never generated. The jurist's §Standing Context cannot be"