session 2026-08-23: the vault got instruments, a convention, and a name for what it lacks

Fool: FOOL-SEED-RULE.md §2a corrected two days before the beacon — the heading
claimed a ruling scope the recorded ruling could not reach (d6377af, pushed).

Obsidian: the paper bridge tested and working, first notebook transcription
filed under its own entry date; vault-links.py built and twice caught failing
while its selftest read green; Paradigm + Marshall read in full; the Archive
Convention and Re-encounter placed in the vault; the vault's CLAUDE.md
rewritten after fifteen months stale.

Files: session record + notebook-provenance reference memory + 6 KG lines
(3 drift-patterns, 2 preventions, 1 measured-state); MEMORY.md rotated with
the prior Active Session demoted verbatim to MEMORY-reference.md;
scripts/vault-links.py archive-path fix (substring -> path component, 3 new
controls, 26/26).

No new PENDING items — today's vault work is steward-direct and touches no
governed artifact.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JQKeKY9T9d95KpvHwwok8T
This commit is contained in:
David F Glidden
2026-08-24 00:01:58 +02:00
co-authored by Claude Opus 5
parent 9bc84a6f16
commit e3da9abad1
7 changed files with 308 additions and 8 deletions
+28 -1
View File
@@ -149,13 +149,26 @@ class Index:
return sorted({n for n in self.by_name if pref.match(n) and pref.sub("", n) == base})
ARCHIVE_DIR = "99. Archive"
def in_archive(path, vault):
"""Exact path-COMPONENT match, never a substring.
Earned 2026-08-23, hours after this file was written to avoid exactly this class:
`"99. Archive" in str(p)` also matches `99. Archives—Previous Iterations`, an ARC
folder inside 00. Compass. 78 live notes were silently excluded from every "live
vault" figure of the day (1153 counted as archive; the real archive holds 1070).
A substring is not a path."""
return ARCHIVE_DIR in path.relative_to(vault).parts
def scan(vault, include_archive=False, once_per_note=True):
idx = Index(vault)
dead, case_only, total = defaultdict(set), defaultdict(set), 0
for p in sorted(idx.vault.rglob("*.md")):
if "/.obsidian/" in str(p):
continue
if not include_archive and "99. Archive" in str(p):
if not include_archive and in_archive(p, idx.vault):
continue
text = strip_code(p.read_text(errors="replace"))
raws = [target_of(m) for m in WIKI.findall(text)]
@@ -253,6 +266,20 @@ def selftest():
chk("a vault with no broken links reports ZERO [negative control — proves the"
" scanner is not manufacturing findings]", len(dead2) == 0 and tot2 == 1, state)
shutil.rmtree(d2, ignore_errors=True)
# --- archive exclusion must match a path COMPONENT, not a substring ---
d3 = Path(tempfile.mkdtemp())
(d3 / "99. Archive").mkdir(); (d3 / "99. Archives—Previous Iterations").mkdir()
(d3 / "99. Archive" / "Old.md").write_text("x\n")
(d3 / "99. Archives—Previous Iterations" / "Live.md").write_text("x\n")
(d3 / "Plain.md").write_text("x\n")
chk("a note in '99. Archive/' IS excluded [positive control]",
in_archive(d3 / "99. Archive" / "Old.md", d3), state)
chk("a note in '99. Archives—Previous Iterations/' is NOT excluded [the 78-note"
" over-match of 2026-08-23 — a substring is not a path]",
not in_archive(d3 / "99. Archives—Previous Iterations" / "Live.md", d3), state)
chk("a note at the vault root is NOT excluded", not in_archive(d3 / "Plain.md", d3), state)
shutil.rmtree(d3, ignore_errors=True)
finally:
shutil.rmtree(d, ignore_errors=True)
print(f"\n{'SELFTEST PASS' if not state['fail'] else 'SELFTEST FAIL'}"