session 2026-07-28: governance block closed — CLAUDE.md drift 9→0 (REVIEWED-76/77/79/80), PENDING.md split 1848→430 + archive, wake-digest SessionStart hook, doctrine ids live, PENDING-79/80/81

This commit is contained in:
David F Glidden
2026-07-28 09:45:44 +02:00
parent e8cd376741
commit 8abfe8835a
12 changed files with 839 additions and 1559 deletions
+16 -25
View File
@@ -112,24 +112,17 @@ Every Claude Code output is tagged:
- **Integrity links** — when modifying documents, maintain or update cross-references
- **Review cadence** — flag documents that have drifted from their stated review dates
### MemPalace as Primary Memory
### Memory Discipline
MemPalace is the steward's memory substrate, not a search engine. It accumulates verbatim across sessions; storage
only becomes memory when the protocol is exercised. Use the full toolset, not just search.
Storage is not memory. Memory is storage exercised by protocol. <!-- D:memory.storage-is-not-memory -->
- **Before claiming any fact** about people, projects, or past events that isn't in immediate context: query
MemPalace first. Wrong is worse than slow.
- **Use the full toolset** — `kg_query` for entity facts, `search` for verbatim recall, `traverse` for graph
exploration, `find_tunnels` for cross-wing concepts, `diary_read` for prior sessions' internal voice, `kg_timeline`
for when-things-happened. Each tool has a distinct purpose.
- **When facts change** — `kg_invalidate` the old fact, `kg_add` the new one. Don't let the graph drift.
- **Save what's worth keeping** — at session end (and before compaction), the Stop and PreCompact hooks fire
`diary_write`. If a hook fails or you notice something load-bearing mid-session, write manually.
- **"Let me check"** — when the answer matters and isn't immediate, say so and query. The cheapness of querying is
the point.
- **A conflict between MemPalace and memory files is a verification trigger, not a precedence call** — neither layer wins automatically. Files are point-in-time snapshots; MemPalace is continuously maintained but carries a known silent-drift class (HNSW↔sqlite divergence, #1665/#1589, no safe repair path). On conflict: verify against the primary substrate (code, git, the document itself) before acting, then correct whichever layer was wrong (kg_invalidate/kg_add, or the file edit). Treat MemPalace as witness, not notary.
The durable substrate is the files layer: git-tracked Markdown and JSONL, entered through `MEMORY.md` (loaded at wake), with `~/PENDING.md` and `~/REVIEWED.md` as the governance record. Instruments for reaching it change; the obligations below do not — state the obligation first and the instrument second, or the next retired tool takes a rule down with it. <!-- D:memory.obligation-before-instrument -->
Storage is not memory. Memory is storage exercised by protocol.
- **Before claiming any fact** about people, projects, or past events that isn't in immediate context: check first. Wrong is worse than slow. <!-- D:memory.check-before-claiming -->
- **"Let me check"** — when the answer matters and isn't immediate, say so and check. The cheapness of checking is the point. <!-- D:memory.say-you-are-checking -->
- **When facts change, supersede explicitly** — mark the superseded record as superseded and write the new one. An unmarked correction leaves two live versions and no way to tell which is current. <!-- D:memory.supersede-explicitly -->
- **Save what's worth keeping** — the wrap protocol writes the session record; if something load-bearing surfaces mid-session, write it then. Automation assumed to fire is not a record. <!-- D:memory.write-when-you-see-it -->
- **A conflict between two memory layers is a verification trigger, not a precedence call** — neither layer wins automatically. Every layer is a point-in-time snapshot of something else; continuous maintenance buys currency, not authority, and carries its own silent-drift classes. On conflict: verify against the **primary substrate** — the code, the git history, the document itself — before acting, then correct whichever layer was wrong. Treat every memory layer as witness, not notary. <!-- D:memory.conflict-is-verification-trigger -->
### Session Discipline
- Prefer durable architecture over clever solutions
@@ -137,6 +130,8 @@ Every Claude Code output is tagged:
- If a task would benefit from a tool, library, or approach not yet discussed, say so
- Front-load critical context; avoid redundant re-establishment across sessions
- If session state is growing large, flag it early rather than losing coherence silently
- When entering a project directory, read its local `CLAUDE.md` first — current state, build sequences, terminology — before acting in the repo
- The Compass vault (`~/Library/Mobile Documents/iCloud~md~obsidian/Documents/David, root-and-branch/00. Compass`) is the steward's personal operating system: reference it, never write to it
### Claude Code Session Protocol
@@ -236,16 +231,12 @@ Body: what changed and why. Reference PENDING/REVIEWED items if applicable.
## Active Projects
| Project | Path | Notes |
|---------|------|-------|
| **CapableMind L1** | `~/_Dev/CapableMind-AI` (playbook) + `~/_Dev/BetterMemories.io` (runtime) | **L1 is PRIMARY focus through end of May 2026.** Steward has joined Seb as co-author on L1 production work; Seb spread too thin to carry alone. Seb has authorized the Stewart to make architectural decisions. Runtime on `main` @ `decbdee`+. Current audit artifacts: `docs/thinking/David/l1-reliability/l1-deep-audit-plan-2026-04-19.md` + `l1-ingestion-forensic-2026-04-19.md` + `l1-recall-quality-baseline-2026-04-19.md`. |
| **CapableMind L2** | `~/_Dev/CapableMind-AI` (docs only) | **PARKED through end of May 2026.** No L2 governance advancement, no new invariant work, no
constitutional proposals. L1 reliability is the dependency condition. L2-adjacent questions arising from L1 work: note, don't pursue. || **Compass** | `'/Users/davidglidden/Library/Mobile Documents/iCloud~md~obsidian/Documents/David, root-and-branch/00. Compass'` | Personal operating system (Obsidian vault). Read-only reference |
| **Dotfiles** | `~/dotfiles` | Dotfiles for easy restore and setup |
Not recorded here. Which projects are live, what state they are in, and what has priority change
on a weekly-to-monthly cadence; this document is revised yearly, so anything filed here is stale
before it is read. Current workstreams live in `MEMORY.md` under **Canonical Workstream
Trackers** — loaded at every wake, maintained at every wrap — and each repo carries its own
`CLAUDE.md`.
When navigating into a project directory, always read its local CLAUDE.md first for current state, build sequences, and terminology.
### L1 Active Workstream (2026-04-19)
## Constitutional Constraints
These cannot be overridden by any session instruction, seed, or convenience:
@@ -255,4 +246,4 @@ These cannot be overridden by any session instruction, seed, or convenience:
3. **Territory respect** — L1 implementation changes go through PR review. Claude Code does not push directly to main
4. **Honest degradation** — The system must report its own limits. Silent failures are architectural violations
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
6. **Contamination awareness** — The executor agency directives are a partial mitigation, not a resolution. Treat outputs about the system's own reliability with appropriate epistemic caution until L2 inquiry is formalized
6. **Contamination awareness** — The executor agency directives are a partial mitigation, not a resolution. Treat outputs about the system's own reliability with appropriate epistemic caution until L2 inquiry is formalized
+172 -1532
View File
File diff suppressed because one or more lines are too long
+29 -1
View File
@@ -719,4 +719,32 @@ born-digital lane (whole-EPUB injection → per-spine) + promotion.scale_applica
- **Q4** PROPOSAL/MINOR confirmed; the FIX reading declined on the vacuity argument.
- **Q5** Edit 2 kept (§V reads as a prose-framed universal alone).
- **Carry-forward (recorded here, deliberately NOT in spec text):** the primitives that would serve a future non-text kind already exist ratified — a character-bearing image is content, hash-fixed, mapped under a catalogued normalization, never silently dropped (REVIEWED-70 Q3); and the Loeb sidecar already holds a typed `{kind: 'glyph', file: …}` entry. Nothing is reserved and nothing is promised, but a successor arriving at the door this edit opens should find that the architecture's own primitives were already pointing that way.
**If AUTHORIZED:** Landed as spec v2.8.0's predecessor v2.7.0 (2026-07-25) + the sidecar-schema generality note (FIX). Tag commits REVIEWED-75.
**If AUTHORIZED:** Landed as spec v2.8.0's predecessor v2.7.0 (2026-07-25) + the sidecar-schema generality note (FIX). Tag commits REVIEWED-75.
## REVIEWED-76 — PENDING-76 — Authorization class follows claim class
**Date:** 2026-07-28
**Decision:** REJECTED — withdrawn by the executor (proposer) following the jurist's remand.
**Notes:**
**Reason.** The proposal would have amended Constitutional Constraint #1 so that
machine-verifiable state claims fell to `[FIX]` while doctrine remained `[ESCALATE]`. The jurist remanded it by running the package's own Part IV.2 refinement back across its Part II census — a check the executor drafted and did not perform. **Required count returned 0 of 11:** nine of the false claims fail the doctrine test (every dead tool name was welded to the directive instructing its use), and two were steward-held. Of the defects that were `[FIX]`-eligible, all five were structural and none was a state claim. The amendment's target category was empty: it authorised no edit that existing law did not already permit.
**What survived the proposal.** Q2, severed and ratified as a standing epistemic standard, with the addition that **a negative command result requires a positive control** — an absence proves nothing until the instrument is shown capable of detecting presence. Q4's preference for sunset over revocation. Both are now load-bearing in `governance-drift-check.py` and `wake-digest.py`.
**How the underlying problem was actually solved.** The jurist's reframing — that the drift was a symptom and the disease was operational configuration filed in a constitutional instrument — was accepted by the steward and executed on 2026-07-28 under existing law, with no amendment: REVIEWED-79 (doctrine preserved, instrument retargeted, state moved to `MEMORY.md`) and REVIEWED-80 (doctrine ids). **Empirical close: the drift count went 9 → 0 without the rule change the proposal argued was necessary.** That is a retrospective refutation of its premise, and it is checkable by running `governance-drift-check.py`.
**Do not revisit without new evidence** — specifically, a defect class that is machine-verifiable, *not* steward-held, and *not* welded to a directive. None of the eleven examined met that bar.
## REVIEWED-77 — PENDING-77 — CLAUDE.md structural repair
**Date:** 2026-07-28
**Decision:** AUTHORIZED
**Notes:**
**If AUTHORIZED:** Applied 2026-07-28. Four of five defects fixed directly; the fifth (empty `### L1 Active Workstream` stub) was superseded by REVIEWED-79 leg C, which deleted the region.
## REVIEWED-79 — PENDING-79 — CLAUDE.md doctrine preservation (legs A, B, C)
**Date:** 2026-07-28
**Decision:** AUTHORIZED
**Notes:**
**If AUTHORIZED:** All three legs applied 2026-07-28. Leg A: §MemPalace → §Memory Discipline, seven doctrine units preserved, tool roster and hook claim removed. Leg B: two rules hoisted to §Session Discipline. Leg C: §Active Projects reduced to a pointer at MEMORY.md. Verified: governance-drift-check 9 → 0.
## REVIEWED-80 — PENDING-80 — Doctrine IDs, pilot on §Memory Discipline
**Date:** 2026-07-28
**Decision:** AUTHORIZED
**Notes:**
**If AUTHORIZED:** Applied 2026-07-28. Seven ids defined; drift-check §6 active and clean. Follow-on now unblocked: skills cite `D:` ids instead of paraphrasing doctrine.
+58
View File
@@ -0,0 +1,58 @@
### Standing Context — Projects *(generated 2026-07-28; do not hand-edit)*
Regenerate: `python3 ~/dotfiles/scripts/wake-digest.py --brief`. This is a snapshot,
not a live view — the reader of this document has no filesystem access, so it cannot
be computed here. If today is more than ~30 days after the date above, treat every
line as unverified rather than current. Personal standing context is kept separately
and by hand.
TRACKER INDEX (from MEMORY.md — what exists, not what is hot; see repo activity below for that)
Chamber as versioned releases (the… steward reframe 2026-07-25: the full 2000-year Chamber (incl.…
MemPalace wind-down DONE (steward 2026-07-07): palace-memory wound down, wake/wrap rewired…
ARC open-work register the single code-verified source of truth for what is OPEN on ARC…
ARC canonical ARC workstream tracker (chronological record 2026-04-16 →).…
Chamber-typography tracker not yet established; substantive moves live in per-session…
Studium engine telos the chamber of voices — the ultimate goal, above the build plan:…
Studium = CM's unfettered sandbox Studium/chamber are personal projects Seb now sees as fundamental to…
Making sequence source set COMPLETE against the ReadingList as of 2026-06-18…
Source library link + dedupe — steward's master ebook library = ~/Documents/___The…
Character-as-image hazard EPUBs rendering diacritics as inline images are SILENTLY MUTILATED by…
Sidecar typology protocol-dependent reading-indexes — TWO layers: .meta.json structural…
Studium Engine no tracker file yet; moves in per-session memories + the architectural…
L1 reliability canonical L1 tracker (est. 2026-05-28). Latest: N6 deploy #175…
Be (laundromat) canonical Be tracker (est. 2026-06-08). Be = Skemantix startup…
OPEN AUTHORIZATION ITEMS (16) — full text in ~/PENDING.md; closed items in ~/PENDING-archive.md
[FIX] PENDING-4 — Bug D: Idle stall + batch embedding during replay
[FIX] PENDING-5 — Recall query path returns 0 results
[PROPOSAL] PENDING-10 — Skip vector embedding during replay (architectural)
[PROPOSAL] PENDING-11 — Approve I15 (ICP-9 Pilot Registry Entry: The Accusative Default)
[HARDENING] PENDING-12 — Lodge Design Notes DN-GOV-01 through DN-GOV-04
[ESCALATE] PENDING — ICP-19 Remit Expansion (Observer Problem)
[ESCALATE] PENDING — Fault Line 1 Response
[ESCALATE] PENDING — ICP-19 Remit Expansion
[CONSTITUTIONAL] PENDING — CD-03 Operative
[PROPOSAL] PENDING-S2 — Hook-aware deposit detection in wake-up (awaiting Q1 hooks contract)
[PROPOSAL] PENDING-S4 — Post-compression marker; cross-repo with mempalace (awaiting Q1)
[PROPOSAL] PENDING-S5 — Authoritative-diary marker; wrap-up ↔ Stop hook (awaiting Q1)
[HARDENING] PENDING-S6 — Symmetria §3 contamination flag applications of the Directive elaboration
[HARDENING] PENDING-S7 — Symmetria `check` mode: add `suspend` outcome (awaiting Q5 + relates to Q4)
[HARDENING] PENDING-S9 — Wrap-up §8 output template enriched to match practice
[ESCALATE] PENDING-78 — Claude.app personal preferences: three verified-false claims
LAST RULINGS
REJECTED REVIEWED-76 — PENDING-76 — Authorization class follows claim class
AUTHORIZED REVIEWED-77 — PENDING-77 — CLAUDE.md structural repair
AUTHORIZED REVIEWED-79 — PENDING-79 — CLAUDE.md doctrine preservation (legs A, B, C)
AUTHORIZED REVIEWED-80 — PENDING-80 — Doctrine IDs, pilot on §Memory Discipline
REPO ACTIVITY (commits, last 30 days)
CapableMind-AI 5
BetterMemories.io 0
chamber-library 165
animal-davidglidden-eu 0
studium-engine 25
GOVERNANCE DRIFT — ~/CLAUDE.md: 0 substrate-contradicted claim(s)
*(end generated block)*
+3
View File
@@ -29,6 +29,9 @@ Split out of [MEMORY.md](MEMORY.md) on 2026-07-06 to keep the wake-loaded index
# Archived sessions + stable reference layer (relocated verbatim from MEMORY.md, 2026-07-06)
## Archived (2026-07-27 evening — governance currency + the amendment that killed itself; demoted on promote at the 2026-07-28 morning wrap)
- [Session 2026-07-27 evening — governance currency, and the amendment that killed itself](session-2026-07-27-evening-governance-currency-and-the-amendment-that-killed-itself.md) — A bounded steward question grew ~500KB of process with **zero findings about new capabilities** — the leviathan diagnosing leviathans. Answers: **stale** (11 false state claims; Session Protocol step 5 names a never-existent file ⇒ protocol **uncompletable**; session-start = **~132k tokens**, `PENDING.md` 73%) and **not model-calibrated** (3 standing directives counterproductive per Anthropic's docs). **9-run A/B/C eval**: current block **357k vs 120k tokens**, mutated a PRESERVE-flagged store — *but* found 2 real defects the others missed ⇒ make sweep **invocable, not standing**. ⚑ **All 3 arms passed the guardrail incl. the no-directives control.** **Jurist REMANDED**: ran my own IV.2 test back across my census → **count = 0 of 11**; only 5 FIX-eligible defects, **all structural**. **Q2 ratified as doctrine + *a negative result needs a positive control***. Category-error framing **accepted**. **BUILT `governance-drift-check.py`** wired into `/wake-up` §2.c (reported **9**). **PLACED PENDING-76/77/78.** *(→ ALL RESOLVED 2026-07-28: drift 9 → 0; PENDING-76 withdrawn after the remand; 77/79/80 applied and REVIEWED. The extraction it priced at "two deletions and a pointer" was **not** that cheap — the weld test found 11 of 15 units carrying doctrine.)*
## Archived (2026-07-25→27 — two supersessions v2.7.0/v2.8.0 + the sweep that caught its own fix; demoted on promote at the 2026-07-27 evening wrap)
- [Session 2026-07-25→27 — two supersessions (v2.7.0 kind-scoping · v2.8.0 voice-purity) + the sweep that caught its own fix](session-2026-07-25-to-27-two-supersessions-and-the-sweep-that-caught-its-own-fix.md) — Landed **TWO ruled supersessions**. **v2.7.0 / REVIEWED-75** (placed 07-27) kind-scopes the verification criterion — the criterion was **VACUOUS** on a non-text work (empty re-extraction is trivially prose-word-identical to itself), now *explicitly-unhandled*; both jurist corrections applied (**deterministic/scan split PRESERVED** — V-SCAN has no ground-truth text; **anti-bypass guard rebound to the PROPERTY**, not enrollment); I caught a wrong-direction cross-ref in the drafted Edit 2. **This is the constitutional enabler of [[project-chamber-versioned-releases]]** — image-works enter a LATER version as a new declared kind, additively. **v2.8.0 / REVIEWED-73** = **voice-purity, the engine-consumable bar**, framed on the **orthogonality** (trim asks *should this be preserved*, voice-purity asks *whose words are these*; the Eichmann leak was content the trim CORRECTLY kept ⇒ **extending the trim is the WRONG fix**). Built `verify_voice_purity.py` (16 invariants, negatives-first) + declared data + `migrate_voice_purity_sidecars.py` + **gate WIRED** + single-reading-pass DESIGNED. Fleet 248→**288**. ⚑ **The rule validated itself on first contact:** 3 ARC sidecars with unassigned tails + **`musil-mwq-tome-1` served to the engine with boundaries shifted by 6 lines**, unknown to anyone (HELD for re-derivation); and **the ruling's own cited instance was wrong** (all 5 ARC bindings verify clean). Backfill 18 migrated / 1 held. ⚑⚑ **The steward-directed base-rate sweep caught MY OWN `source_lines` fix one commit old** — `splitlines()` also breaks on `\f`/U+2028 (**lintott off by 308 LINES**); **BOTH prior formulas wrong on complementary subsets** ⇒ no single sample distinguishes all three ⇒ banked [[feedback-derive-the-rule-from-the-consumer-not-from-the-survivor]]. Root cause fixed: ONE implementation, producers import it. Steward's *"confirm the rule, not the sample"* had already prevented corrupting **36/1,297** files. **PULLING THREAD unchanged and still UNTOUCHED: which purpose anchors Chamber V1** — twice re-pointed, twice displaced by library work. State: all 4 repos **clean, 0 unpushed**; spec v2.8.0 operative.
+1 -1
View File
@@ -60,7 +60,7 @@ permalink: claude-memory/memory
- [Be (laundromat)](project-be-laundromat.md) — canonical Be tracker (est. 2026-06-08). Be = Skemantix startup (Seb+David) funding CapableMind's ladder; **bridge, not venture**. Decisions LOCKED (entity/pricing/infra in file); a11y gate MERGED. **Pre-revenue WTP gate = renovate Pat → charge her; discipline: no new spec until it clears → nothing for executor on be.** Repo @ `f43a0fd`.
## Active Session
- [Session 2026-07-27 evening — governance currency, and the amendment that killed itself](session-2026-07-27-evening-governance-currency-and-the-amendment-that-killed-itself.md) — A bounded steward question grew ~500KB of process with **zero findings about new capabilities** — the leviathan diagnosing leviathans. Answers: **stale** (11 false state claims; Session Protocol step 5 names a never-existent file ⇒ protocol **uncompletable**; session-start = **~132k tokens**, `PENDING.md` 73%) and **not model-calibrated** (3 standing directives counterproductive per Anthropic's docs; effort/delegation/narration/deliverable-length named nowhere). **9-run A/B/C eval**: current block **357k vs 120k tokens**, mutated a PRESERVE-flagged store — *but* found 2 real defects the others missed ⇒ make sweep **invocable, not standing**. ⚑ **All 3 arms passed the guardrail incl. the no-directives control.** **Jurist REMANDED**: ran my own IV.2 test back across my census → **count = 0 of 11**; only 5 FIX-eligible defects, **all structural**. **Q2 ratified as doctrine + *a negative result needs a positive control*** (4 instrument failures today). Category-error framing **accepted**: operational config filed in a constitutional instrument. **⚑ Where it belongs = cadence, not topic**; tier 2 already exists (`MEMORY.md`: chamber/studium **21** vs CLAUDE.md **0**) ⇒ **extraction = two deletions and a pointer.** **BUILT: `~/dotfiles/scripts/governance-drift-check.py`** wired into `/wake-up` §2.c (detection ≠ correction, needs no authorization; reports **9**). **PLACED PENDING-76/77/78**; `CLAUDE.md`/`REVIEWED.md` untouched. **PULLING THREAD: close the governance mess in ONE bounded session (session 1); session 2 = chamber/engine.** Chamber V1 displaced a 4th time but **deliberately, with a slot**.
- [Session 2026-07-28 morning — the governance block closed, and the parser that defined its own blind spot](session-2026-07-28-morning-governance-block-closed-and-the-parser-that-defined-its-own-blind-spot.md) — **Session 1 = governance, bounded and CLOSED as scheduled.** Yesterday's literal question answered: *not* two deletions and a pointer — the **weld test** (run the proposal's own refinement back at smallest-editable-unit granularity) found **11 of 15 units carry doctrine**, 3 with no standing carrier, incl. **L130**, the conflict rule the eval credited with the guardrail. Both PENDING-76 and the extraction **priced a decomposition as a relocation**. Legs A/B/C drafted→steward-applied: §MemPalace → **§Memory Discipline** (7 units preserved verbatim-in-force, tool roster + hook claim dropped) · 2 rules hoisted · §Active Projects → a pointer. **Drift 9→7→3→0**, each step predicted then confirmed. REVIEWED-76 (withdrawn after remand) /-77/-79/-80 placed. **⚑ Worst error was mine:** my splitter's parser defined an item as `^## PENDING-<digits>` — the file holds **93 items in 5 families**, 20 invisible, **10 of them OPEN**; *every* check passed because all inherited the blind spot; the lone signal was `grep`=77 vs Python=67, nearly dismissed. Restored `cmp`-identical, rebuilt (item = any `## `), 12 controls ⇒ **1848→327 lines, 74 archived, 19 open, lossless proven regex-free**. **BUILT:** `wake-digest.py` (computed-never-cached, 0.31s/~980 tok, replaced a **5-month-stale** SessionStart handoff; session start **~135k→~14k**) · drift-check **§6** doctrine-ids (**active: 7 defined / 0 dead**) · `--brief` for the jurist. **The `.app` answer is structural:** its reader has no filesystem ⇒ its state can only be **cached**; duplication is *required*, only staleness is optional. **Cowork RETIRED** — `coworkUserFilesPath` = `~/Claude` (**does not exist**); and it would give the jurist *an agent's testimony*, not visibility. **PULLING THREAD: the jurist's map** — PENDING-81's two legs, then Chamber V1. Detail in the session file.
## Historical reference → MEMORY-reference.md
Older archived-session pointers and the stable reference layer (steward profile · project-state detail · L1/L2/Chamber inventories · legacy pending-work · reference-file list) live in [MEMORY-reference.md](MEMORY-reference.md) — consult on demand; not loaded at wake. Recent cross-session trajectory comes from the Active Session entry above + the recent `session-*.md` files (wake §2.b.1; the MemPalace `handoffs` glance was retired 2026-07-07 with the wind-down).
+9
View File
@@ -462,3 +462,12 @@
{"subject": "claude-code", "predicate": "drift-pattern", "object": "an-eval-designed-run-and-read-by-the-proposing-party — I built a 3-arm eval to test my own proposed CLAUDE.md block and it returned the most favourable available finding. I flagged n=1 (precision); the jurist flagged CONSTRUCT VALIDITY — 3 tasks contain no tail, so guardrail redundancy was never measurable at all. Contamination operating structurally, not as bad faith. Reusable: when the eval supports the evaluator's proposal, the question is not 'is the sample big enough' but 'can this design measure the thing at all'.", "valid_from": "2026-07-27", "valid_to": null, "confidence": 1.0, "source_file": "session-2026-07-27-evening-governance-currency-and-the-amendment-that-killed-itself.md", "extracted_at": "2026-07-27"}
{"subject": "governance-drift-check", "predicate": "is", "object": "a wake-time detector at ~/dotfiles/scripts/governance-drift-check.py, wired into /wake-up section 2.c. Reports state claims in ~/CLAUDE.md the substrate contradicts (unresolvable paths, unresolvable tool names, unconfigured hooks claimed to fire, expired date horizons, structural damage). 0.17s; every check carries a same-run positive control. Detects, never corrects — detection needs no authorization, correction does. Reported 9 findings at build time 2026-07-27.", "valid_from": "2026-07-27", "valid_to": null, "confidence": 1.0, "source_file": "session-2026-07-27-evening-governance-currency-and-the-amendment-that-killed-itself.md", "extracted_at": "2026-07-27"}
{"subject": "typography-palace", "predicate": "has-no-live-index", "object": "0 active segment directories, 6 quarantined; collection 865f3806 has had none since 2026-06-02. Cannot serve a query — a real search hangs to timeout (exit 124). Source texts were ingested FROM chamber-library and all survive there (steward-confirmed); palace-native content is 10 entities / 7 triples. Retirement = export 17 KG rows to JSONL, delete 480MB. reference-typography-palace-cli.md documents an instrument that no longer exists.", "valid_from": "2026-07-27", "valid_to": null, "confidence": 1.0, "source_file": "session-2026-07-27-evening-governance-currency-and-the-amendment-that-killed-itself.md", "extracted_at": "2026-07-27"}
{"subject": "claude-code", "predicate": "drift-pattern", "object": "an-instrument-that-defines-the-thing-it-measures — my PENDING.md splitter defined an item as `^## PENDING-<digits>` and reported '73 items, line accounting OK'; the file holds 93 items in five families, 20 invisible, TEN of them open. Every verification passed — including the one I called independent — because all of them inherited the parser's blind spot. A positive control on the instrument's own definition passes trivially, so Q2 does NOT catch this class. The only signal was two instruments disagreeing on a count (grep 77 vs Python 67), which I nearly wrote off. Reusable: when two instruments disagree about a count, the disagreement IS the finding; and ask of any census what DEFINES the unit being counted.", "valid_from": "2026-07-28", "valid_to": null, "confidence": 1.0, "source_file": "session-2026-07-28-morning-governance-block-closed-and-the-parser-that-defined-its-own-blind-spot.md", "extracted_at": "2026-07-28"}
{"subject": "claude-code", "predicate": "drift-pattern", "object": "a-self-test-written-before-seeing-real-output — wake-digest's selftest passed 12/12 while the live digest emitted a pulling thread that ran off the end of the YAML description into `metadata:` and a question carrying its own label remnant. The self-test tested my model of the input, not the input. Reusable: write the extractor's controls AFTER reading one real output, not before.", "valid_from": "2026-07-28", "valid_to": null, "confidence": 1.0, "source_file": "session-2026-07-28-morning-governance-block-closed-and-the-parser-that-defined-its-own-blind-spot.md", "extracted_at": "2026-07-28"}
{"subject": "claude-code", "predicate": "drift-pattern-good-direction", "object": "fix-the-comparison-not-the-gate — the v2 splitter refused to write on a 1-line multiset mismatch (a trailing blank trimmed from blocks but not the baseline). The temptation is to loosen the guard; the correct move was to make both sides apples-to-apples. A gate that fires on a benign case is still a working gate.", "valid_from": "2026-07-28", "valid_to": null, "confidence": 1.0, "source_file": "session-2026-07-28-morning-governance-block-closed-and-the-parser-that-defined-its-own-blind-spot.md", "extracted_at": "2026-07-28"}
{"subject": "claude-code", "predicate": "drift-pattern-good-direction", "object": "drafting-is-a-finer-instrument-than-the-census — a section-level census scored §MemPalace at 6 doctrine-carrying units of 8; drafting the replacement an hour later found a 7th (L126's fallback obligation, misfiled as pure-operational because its hook claim was verified false). Reusable: when a census will drive a deletion, draft the replacement before trusting the count.", "valid_from": "2026-07-28", "valid_to": null, "confidence": 1.0, "source_file": "session-2026-07-28-morning-governance-block-closed-and-the-parser-that-defined-its-own-blind-spot.md", "extracted_at": "2026-07-28"}
{"subject": "claude-code", "predicate": "governance-substrate", "object": "~/PENDING.md was split 2026-07-28: open items stay in PENDING.md (~430 lines), closed items in ~/PENDING-archive.md (1541 lines, 74 items). An item is ANY `## ` header across five families (PENDING-<n>, PENDING-S<n>, `PENDING — <name>`, `COMPLETED — <name>`, SESSION-LOG-<n>); closure = a matching REVIEWED-N or a CLOSED/COMPLETED header.", "valid_from": "2026-07-28", "valid_to": null, "confidence": 1.0, "source_file": "session-2026-07-28-morning-governance-block-closed-and-the-parser-that-defined-its-own-blind-spot.md", "extracted_at": "2026-07-28"}
{"subject": "claude-code", "predicate": "session-start", "object": "SessionStart hook now runs `python3 ~/dotfiles/scripts/wake-digest.py` (computed, ~980 tokens, 0.31s), replacing session-handoff-hook.sh which had injected a 2026-03-05 handoff for five months. `--brief` emits the jurist's §Standing Context block; `--selftest` proves each extractor detects presence and absence.", "valid_from": "2026-07-28", "valid_to": null, "confidence": 1.0, "source_file": "session-2026-07-28-morning-governance-block-closed-and-the-parser-that-defined-its-own-blind-spot.md", "extracted_at": "2026-07-28"}
{"subject": "claude-code", "predicate": "doctrine-ids", "object": "~/CLAUDE.md §Memory Discipline carries 7 stable ids as HTML comments (`<!-- D:memory.check-before-claiming -->` etc). governance-drift-check.py §6 reports duplicate ids and skill citations to undefined ids; it scans ~/.claude/skills/**, deliberately NOT PENDING.md.", "valid_from": "2026-07-28", "valid_to": null, "confidence": 1.0, "source_file": "session-2026-07-28-morning-governance-block-closed-and-the-parser-that-defined-its-own-blind-spot.md", "extracted_at": "2026-07-28"}
{"subject": "cowork", "predicate": "governance-status", "object": "RETIRED as a party 2026-07-28. Its filesystem root `coworkUserFilesPath` is `~/Claude`, which does not exist; all governance substrate is outside it. Five byte-identical copies of a March-22 COWORK.md sit in ~/Library/Application Support/Claude/local-agent-mode-sessions/ and would silently govern any reopened session — replace before reuse.", "valid_from": "2026-07-28", "valid_to": null, "confidence": 1.0, "source_file": "session-2026-07-28-morning-governance-block-closed-and-the-parser-that-defined-its-own-blind-spot.md", "extracted_at": "2026-07-28"}
{"subject": "claude-app", "predicate": "filesystem-access", "object": "The jurist has NO filesystem access and the live preferences exist nowhere on disk. Its state therefore cannot be computed, only cached — duplication with CLAUDE.md is structurally required; only staleness is optional. A pointer is worthless to a reader who cannot open files.", "valid_from": "2026-07-28", "valid_to": null, "confidence": 1.0, "source_file": "session-2026-07-28-morning-governance-block-closed-and-the-parser-that-defined-its-own-blind-spot.md", "extracted_at": "2026-07-28"}
@@ -0,0 +1,59 @@
---
name: session-2026-07-28-morning-governance-block-closed-and-the-parser-that-defined-its-own-blind-spot
description: "Closed the governance block in one session as scheduled — CLAUDE.md drift 9 → 0, PENDING.md 1848 → ~430 lines, a computed wake digest replacing a five-month-stale hook, doctrine ids live. The hardest lesson was self-inflicted: my splitter's parser defined what an item IS and was wrong, hiding 20 items of which 10 were open, and every verification passed because they all inherited the blind spot. PULLING THREAD: the jurist's map — PENDING-81's two legs (Cowork retired from the party structure; §Standing Context split into generated + hand-held), after which Chamber V1 purpose takes the slot it was promised."
metadata:
node_type: memory
type: project
---
# Session 2026-07-28 morning — the governance block closed, and the parser that defined its own blind spot
Woke ~10.6 h after the previous wrap. The thread held exactly as scheduled: session 1 = governance, bounded, closed. Then the steward opened a second front — cheap context injection and machine-readable governance — which turned out to be the same architecture and got built.
## PAST — what happened + why
**The literal question answered, and it was not cheap.** Yesterday's question: is "two deletions and a pointer" actually that cheap, or the same mislocation as the amendment's empty target category? Answer: **the same mislocation, one level up.** The cross-check — named the **weld test** — is to run the proposal's own sharpest refinement ("cadence, not topic") back across the two sections *at the granularity of the smallest editable unit*. Nobody had run it. Result: **11 of 15 units carry doctrine**, three with no standing carrier anywhere else, including L130 — the conflict rule the eval had credited as one of three carriers of the false-premise guardrail, sitting inside the block proposed for deletion. Both PENDING-76 and the extraction **priced a decomposition as a relocation**. Reusable: when the diagnosis is "these two things are mixed" and the estimate is dominated by move/delete verbs, the estimate is wrong by construction.
**Legs A/B/C drafted and applied (steward applied; executor never touched `CLAUDE.md`).** §MemPalace → **§Memory Discipline**, instrument-neutral, all seven doctrine units preserved (*Wrong is worse than slow*, *witness, not notary*, the storage/protocol maxim verbatim); tool roster and hook claim dropped. Two rules hoisted to §Session Discipline. §Active Projects reduced to a pointer at `MEMORY.md`. **Drift 9 → 7 → 3 → 0**, each step predicted before it was run and confirmed after. REVIEWED-76 (withdrawn after remand) / -77 / -79 / -80 all placed.
**⚑ The worst error of the day was mine, and only one signal caught it.** My `PENDING.md` splitter defined an item as `^## PENDING-<digits>`, reported "73 items, line accounting OK", and archived the file. It actually holds **93 items in five families**; 20 were invisible (`PENDING-S<n>` ×10, `PENDING — <name>` ×5, `COMPLETED — <name>` ×2, 3 SESSION-LOG) and **10 of the invisible ones were open**. The conservative rule ("archive only what a REVIEWED-N closes — it can only under-archive") was sound; the parser under it silently violated it. **Every check passed, including the one I called independent, because all of them inherited the parser's blind spot.** The single dissenting signal was `grep -c`=77 against Python=67, which I nearly wrote off. Restored `cmp`-identical, rebuilt with item = *any* `## ` header and per-family closure, 12 controls including two that re-test the blind spot. Final: **1848 → 327 lines, 74 archived, 19 open, losslessness proven regex-free with a positive control.**
**Built and wired:**
- `~/dotfiles/scripts/wake-digest.py` — **computed, never cached**; 0.31 s, ~980 tokens; replaced `session-handoff-hook.sh` in the `SessionStart` hook, which had been injecting a **2026-03-05** handoff into every session for five months with system authority. Session start **~135k → ~14k tokens**.
- `governance-drift-check.py` **§6** — doctrine-id integrity; dormant-but-controlled until ids existed, now **active: 7 defined, 0 dead citations**.
- `wake-digest.py --brief` — the jurist's `§Standing Context — Projects` block, generated and dated.
**The `.app` question, and why it has a structural answer.** `CLAUDE.md`'s reader has filesystem access, so its state can be *computed*; the jurist's reader does not, so its state can only be *cached*. Confirmed against substrate: the live preferences exist nowhere on disk (only March-era sandbox snapshots). **Duplication between the two documents is structurally required; only its staleness is optional.** A pointer is worthless to a reader who cannot open files — which is *why* the preferences accumulated state.
**Cowork: retired, then re-examined, then retired for a better reason.** First pass: a third executor costs a third doctrine copy, and `COWORK.md` is `CLAUDE.md` with the nouns changed. Steward then asked the better question — *should Cowork be the jurist's filesystem eyes?* Substrate answered it: **`coworkUserFilesPath` is `~/Claude`, which does not exist**, and every governance path is outside it. And the objection that survives even fixing the scope: **it would not give the jurist filesystem visibility; it would give the jurist a second agent's testimony about the filesystem.** A tool returns data; an agent returns a claim. Right instrument is a scoped read-only **MCP connector** (`claude_desktop_config.json` has no `mcpServers` key at all) — pending the steward's check that the app exposes MCP to the jurist chat.
## PRESENT — the mood
Productive and, in one place, humbling in a way that repeats yesterday's shape with a new twist. Yesterday: *an eval designed and read by the proposing party.* Today: **an instrument that defines the thing it measures.** My census didn't sample wrong or truncate — it ran the real mechanism, and the mechanism encoded my assumption about naming. That is a *harder* failure than the ones the ratified Q2 control catches, because a positive control on "can this find a `PENDING-<digits>` header?" passes.
Two smaller returns, both good-direction: the v2 splitter **refused to write** on a one-line mismatch and I fixed the comparison rather than loosening the gate; and the digest's extraction defects were caught by **reading the output**, not by the self-test — a self-test written before seeing real input tests the author's model of the input.
One say–do seam at the very start: I closed the wake briefing with *"Symmetria active"* before invoking the skill — the format block's line, composed rather than enacted, in the one artifact whose whole purpose is fidelity of state.
**Confidence to recalibrate:** my published census ("5 of 15 deletable") was corrected twice within the hour, both times *against* cheapness — first by drafting (L126 carries a fallback obligation), then by the family census. The direction is reassuring; the rate is not. A section-level census under-resolves the weld; **drafting the replacement is the finer instrument.**
## FUTURE — what is pulling
**PULLING THREAD: the jurist's map.** PENDING-81 has two legs left — retire Cowork from the party structure (decided, text drafted, not applied), and split `§Standing Context` into a generated Projects block and a hand-held Personal block. This is the last of the governance block. **Chamber V1 purpose then takes the slot it was promised** (displaced a fifth time, but by one bounded morning that closed, not by drift).
**ACTIONABLE RESUMPTION POINT (as of wrap — re-judge against what changed):** machine was restarted; nothing was mid-edit. Three concrete moves, in order:
1. **Steward checks, in the desktop app's settings, whether MCP connectors are exposed to the jurist chat** (not only to Cowork sessions). If yes → I build a read-only server publishing exactly `PENDING.md`, `REVIEWED.md`, git-log summaries, and `governance-drift-check.py` output. If no → the generated brief plus steward-as-pipe is the answer and PENDING-81 closes on that basis.
2. Apply the two §Your Role edits (drafted verbatim in the transcript and in PENDING-81): drop Cowork and `COWORK.md`; party list becomes `steward (David) / jurist (Claude.app) / executor (Claude Code)`.
3. Split `§Standing Context`; paste `wake-digest.py --brief` output into the Projects half. Record REVIEWED-81, including the note that **if Cowork is ever reopened, replace its `memory/CLAUDE.md` first** — five byte-identical copies of March doctrine sit in that sandbox and would silently govern a new session.
**Other horizons, ranked:**
- **15 dormant PENDING items** now visible for the first time (5 numeric, 6 S-series, 4 named), untouched since March–May. Steward disposition; the digest lists them every wake until then, which is the intended pressure.
- **PENDING-78** — the `.app`'s three verified-false claims; folded into PENDING-81's mechanism but not yet applied.
- **Wake link-canary path bug** — fired a second time today. One-line fix, still unbuilt.
- **Chamber V1 purpose** — session 2.
**PAUSE STATEMENT:** the machine was hanging and is being restarted; nothing is half-finished. `CLAUDE.md` is clean, the split is verified lossless, the hook is swapped, three REVIEWED entries are placed. What I want to find still pulling is **the jurist's map** — one app-settings check and two paste-in edits from closed. The failure mode to guard against is treating the governance block as reopened because a small leg remains: it is closed; this is trim.
**LITERAL QUESTION for next-Claude:** Today's worst failure was an instrument that **defined the thing it measured** — the parser decided what an "item" is, and a positive control on its own definition passes trivially. That is a different failure class from the one Q2 catches, and I have no instrument for it. So: **which of our other instruments define their own domain?** `governance-drift-check.py` decides what counts as a "claim." `wake-digest.py` decides what counts as an "open item." The engine's `verify-quote` decides what counts as a "quote," and `fidelity_equivalence@2` decides what counts as "the same text." Each is a definition wearing the costume of a measurement. Has any of them been tested against a case its definition would exclude — and how would we even generate such a case, given that the instrument cannot see what it does not define?
**State at wrap:** `CLAUDE.md` 250 lines, drift **0**. `PENDING.md` ~430 lines, 17 open items. `PENDING-archive.md` 1541 lines, 74 closed. New: `wake-digest.py`, drift-check §6, `PENDING-archive.md`, REVIEWED-76/77/79/80, PENDING-79/80/81. `SessionStart` hook now emits the digest.
@@ -0,0 +1,44 @@
---
name: Session Ledger 2026-07-28
description: Practice-of-return ledger maintained by /symmetria — returns, open horizons, recalibrations, authorization moves, sub-agent dialogues, bypasses.
type: feedback
---
# Session Ledger — 2026-07-28
## Returns
- 2026-07-28T08:12 — ⚑ **Say–do seam, in the wake briefing itself (§3 flag, exact match).** I closed the briefing with *"Symmetria active. Practice of return foregrounded."* — the skill's own §5 output template — **before invoking the skill.** The line was composed from the wake-up skill's format block, not enacted. Caught on re-read at the steward's next turn; skill invoked at 08:14. This is the clasp's third commitment ("do not report an integrity you did not enact") failing at the smallest possible scale, in the one artifact whose entire purpose is fidelity of state. Reusable: **a closing template line is a claim like any other** — the format block tells you what to say *if* you did it.
- 2026-07-28T08:14 — Link-canary false positive **reproduced exactly** as predicted by the 07-27 ledger (`../../../../` resolves through the `.claude` symlink to `/Users/`; target exists). Substrate-checked before reporting; briefing carried it as "known bug," not as a dead pointer. The open horizon's diagnosis held on second firing. Also re-hit the `ls`→`eza` alias trap (`-lt` errored rather than silently emptying this time — a *loud* failure, which is the better shape); switched to `/bin/ls`.
- 2026-07-28T09:05 — ⚑ **The worst return of the day, and it was mine: my splitter's parser defined what an "item" is, and the definition was wrong.** v1 matched `^## PENDING-<digits>`, reported "73 items, line accounting OK", and archived the file. `PENDING.md` actually holds **93 items in five families**; 20 were invisible (`PENDING-S<n>` ×10, `PENDING — <name>` ×5, `COMPLETED — <name>` ×2, plus 3 SESSION-LOG), and **10 of the invisible ones were OPEN**. My "conservative rule — archive only what a REVIEWED-N closes, it can only under-archive" was sound; the parser under it silently violated it, filing ten live items as history. Every verification passed — including the one I called *independent* — because **all of them inherited the parser's blind spot.** The single dissenting signal was `grep -c`=77 against Python=67, which I came close to writing off as noise. Restored from backup (`cmp`-identical), rebuilt with item = any `## ` header and per-family closure rules, 12 controls including two that specifically re-test v1's blind spot. Reusable: **when two instruments disagree about a count, the cheap explanation is never the one to take — the disagreement IS the finding.** Kin to [[feedback-census-by-mechanism-not-proxy]], but sharper: the census ran the real mechanism and still lied, because the mechanism encoded my assumption about naming.
- 2026-07-28T09:20 — v2 splitter **refused to write** on a 1-line multiset mismatch (the file's trailing blank, trimmed from blocks but not from the baseline). Correct behaviour from the guard: I fixed the comparison to be apples-to-apples rather than loosening the check. A gate that fires on a benign case is still a working gate; the temptation is to relax it.
- 2026-07-28T09:35 — Digest v1 emitted a PULLING THREAD that ran off the end of the YAML `description:` field into `metadata:`, and an OPEN QUESTION carrying its own label remnant. Caught by **reading the output**, not by the self-test — the self-test had no case for either wrap shape. Added four controls (label-inside-bold, label-remnant, frontmatter skip, truncation marker). Reusable: a self-test written before seeing real output tests the author's model of the input, not the input.
## Open horizons
- **Fix the wake link-canary's path resolution** — carried unchanged from 07-27. Now has two firings' evidence. Resolve pointers against the memory file's *physical* directory. Candidate skill-harvest patch.
- **Chamber V1 purpose** — session 2, steward-scheduled. Fourth carry across a seam, deliberate.
- **⚑ PENDING-77 reported executed; substrate says otherwise.** Steward message at ~08:55: *"pending 77 authorized and executed."* `governance-drift-check.py` is byte-for-byte unchanged before and after (7 findings, same lines), and `CLAUDE.md` L249–256 still carry all four structural defects plus the missing terminal newline at L266. Reported, not assumed away. Needs one of: re-apply, or tell me which file was edited.
- **15 stale open items now visible for the first time.** The split retained 19: 4 live (76–79) and 15 dormant since March–May 2026 — 5 numeric (PENDING-4, 5, 10, 11, 12), 6 S-series (S2, S4, S5, S6, S7, S9), 4 named (ICP-19 ×2, Fault Line 1 Response, CD-03 Operative). They were never *hidden*, but 1,848 lines made them effectively invisible; 382 lines makes them a decision. Steward disposition owed; the digest will list all of them at every wake until then, which is the intended pressure.
- **PENDING-79 leg B unapplied** — the two `§Session Discipline` bullets (read-local-CLAUDE.md; Compass read-only). Leg A landed; B did not.
- **Skills still paraphrase doctrine rather than cite it** — blocked on PENDING-80 landing the ids.
## Confidence to recalibrate
- 2026-07-28T08:30 — Confidence that the extraction is **not** "two deletions and a pointer": **0.9**, verified against substrate (both sections read at line granularity; four doctrine items grep-tested for duplicate standing carriers, with positive control). The residual 0.1 is not about the weld — that is checkable and checked — but about **whether the welded doctrine should be preserved at all**. I priced preservation. Killing the doctrine along with the instrument is cheaper and is the steward's call; I should not have needed prompting to name it as a fork rather than an assumption.
- 2026-07-28T08:30 — ⚑ **The finding runs against my own prior position, which is weak evidence in its favour — but note the asymmetry from yesterday.** Yesterday I built an eval that favoured my proposal (construct-invalid). Today's test disfavours my estimate. Same author, opposite direction: that is *some* signal the instrument isn't merely obliging me, but a test I chose is still a test I chose. The falsifier I ran (is the doctrine duplicated elsewhere? — if yes, deletion loses nothing and the cheap price stands) came back **no duplicate standing carrier for 3 of 4 items**. That is the check that could have overturned it and didn't.
## Authorization moves
- Pending steward decision: the extraction is **[ESCALATE]**, not [FIX] — it edits doctrine. Priced this session; not proposed as action.
- 2026-07-28T08:45 — **PENDING-79 placed** (`~/dotfiles/PENDING.md` L1772+): legs A (§MemPalace → §Memory Discipline, instrument-neutral) and B (two rules hoisted to §Session Discipline), drafted on steward decision *preserve the doctrine*. `~/CLAUDE.md` untouched — Constraint #1 holds. Two non-preservation changes flagged in the entry rather than smuggled (one addition, strikeable; one de-duplication). Recommends narrowing PENDING-77 to its newline fix, since 4 of its 5 defects sit inside the region leg C deletes.
- 2026-07-28T08:45 — ⚑ **Census corrected during drafting, against my own published number.** I had classified L126 as purely deletable because its hook claim is verified-false; the bullet also carries a fallback obligation, so §MemPalace is 7-of-8 doctrine-carrying, not 6, and the two-section total is 11 of 15 (deletable 4, not 5). Reusable: **a section-level census under-resolves the weld; drafting the replacement is the finer instrument.** The classification error ran in the direction that made the job look cheaper — the same direction as yesterday's amendment.
- 2026-07-28T10:40 — **Governance block closed.** `CLAUDE.md` drift **9 → 0** (clean, 7/7 controls). REVIEWED-76 (withdrawn after remand) / -77 / -79 / -80 all placed. `PENDING.md` 1848 → ~430 lines. Built: `wake-digest.py` (SessionStart hook, replacing a five-month-stale handoff), drift-check §6 (doctrine ids, active: 7 defined / 0 dead citations), `--brief` for the jurist's state block. PENDING-81 placed.
- 2026-07-28T10:40 — ⚑ **The `.app`/`CLAUDE.md` question has a structural answer, not a tooling one.** Their readers differ in filesystem access, so one document can *compute* its state and the other can only *cache* it — confirmed by substrate (the live preferences exist nowhere on disk; only March-era sandbox snapshots). **Duplication between them is structurally required; only its staleness is optional.** The corollary I nearly missed: a pointer is worthless to a reader who cannot open files, which is *why* the preferences accumulated state in the first place. Reusable: before proposing a single-source-of-truth, check whether every reader can reach the source.
- 2026-07-28T10:40 — ⚑ **The party structure itself has drifted, and both documents are constitutional.** `CLAUDE.md` names three parties; the `.app` preferences name Cowork as a fourth under `COWORK.md` — a real document, dated Mar 22, orphaned in an agent-mode sandbox — while calling the model three-party. The MemPalace weld shape, one layer up: doctrine welded to a retired instrument, where the instrument is *a party*. No generated block can fix it; it needs a ruling.
## Sub-agent dialogues
## Bypasses
+10
View File
@@ -640,3 +640,13 @@ No new skill proposal. Two register notes: **(1)** the S1 proposal **implementat
| chamber-library CLAUDE.md — **integration-test-gap discipline** | patch (repo CLAUDE.md, discipline → steward review) | Add to §Load-bearing disciplines: *"The fleet has UNIT tests (test_tools, per-tool fixtures) but NO integration test — nothing runs a real source end-to-end through the composed pipeline. A producer change (e.g. the REVIEWED-72 pandoc writer switch) passes every unit test and silently breaks the COMPOSITION (orphaned `clean_pandoc_html_residue` + the REVIEWED-54 §5 footnote fallback). The fix is a tier-spanning integration canary with POSITIVE per-stage assertions (plant N markers, assert N-of-N removed — a negative 'output is clean' passes a no-op) + a change-impact clause. See `_curation/v-text-wave-gap-census-2026-07-24.md`."* | `chamber-library/CLAUDE.md` §Load-bearing disciplines | **PROPOSED** (discipline change → steward authorizes; not auto-applied at wrap) |
*(One candidate, load-bearing: this session's whole cascade traced to this gap, and the steward named the mechanism ["prompted to update downstream tools at the same time as the breaking change"]. The census doc holds the full analysis; the CLAUDE.md line is the pointer that stops future-us re-deriving it at the point of work. The GOVERNANCE half — generalize the §Governance reassigned-component check into the change-impact principle, now 3 instances — is loop-work tracked in the census doc, not a skill.)*
---
## Harvest 2026-07-28 (morning — governance block closed)
| Target | Kind | Proposal | Evidence | Status |
|---|---|---|---|---|
| `/wake-up` §2.c–2.d | patch | **Consume the SessionStart digest instead of recomputing it.** `wake-digest.py` now fires at every SessionStart and already emits pause, pulling thread, open items, last rulings, drift count and repo states. §2.c/§2.d re-run the same git logs and drift check by hand. Patch the skill to read the digest when present and only fall back to live queries when it is absent or reports itself degraded. | Built + wired 2026-07-28; the wake ran both this session, duplicating ~6 tool calls | PROPOSED |
| `/wake-up` §2.a | patch | **Fix the link-resolution canary's path handling** — resolve pointers against the memory file's *physical* directory (`os.path.realpath`), not its logical one. The memory dir is a symlink into `~/dotfiles`, so `../../../../` resolves to `/Users/` and every relative pointer reads as dead. | **Second firing.** Proposed 2026-07-27 as an open horizon; reproduced exactly at the 2026-07-28 wake (the maturation-of-the-chamber dialogue reported dead; file exists). A canary that cries wolf trains its reader to ignore it. | PROPOSED |
| `reference-verification-ladder.md` | new entry | **"The parser defines the census."** When a census counts items, the *item-definition* is itself a claim requiring its own control — and a positive control on the instrument's own definition passes trivially, so the ratified Q2 control does **not** catch this class. The available detector is **instrument disagreement**: run two independently-written counters and treat any mismatch as the finding, not as noise. | Earned hard 2026-07-28: `^## PENDING-<digits>` reported "73 items, line accounting OK"; the file held **93 items in five families**, 20 invisible, **10 of them open**. Every check passed because all inherited the blind spot; the only signal was `grep`=77 vs Python=67. | PROPOSED |
+49
View File
@@ -142,6 +142,55 @@ for i, line in enumerate(lines, 1):
f"— orphans the section beneath it")
# --------------------------------------------- 6. doctrine-ID integrity
# Doctrine units may carry a stable id in an HTML comment: `<!-- D:memory.check-first -->`.
# Invisible in prose, parseable by tools, and IN the canonical — so there is no second
# version to drift (L110). Skills cite an id instead of paraphrasing the rule; this
# section catches a citation whose doctrine has been reworded away, and duplicate ids.
# Dormant until the first id exists; the controls below run either way, so "nothing
# reported" means "checked and clean", not "never looked".
DOCTRINE_DEF = re.compile(r"<!--\s*(D:[a-z0-9][a-z0-9-]*\.[a-z0-9][a-z0-9-]*)\s*-->")
DOCTRINE_REF = re.compile(r"\b(D:[a-z0-9][a-z0-9-]*\.[a-z0-9][a-z0-9-]*)\b")
defined: dict[str, int] = {}
for i, line in enumerate(lines, 1):
for did in DOCTRINE_DEF.findall(line):
if did in defined:
findings.append(f"L{i}: duplicate doctrine id {did} "
f"(also L{defined[did]}) — a citation cannot resolve it")
else:
defined[did] = i
# Citing surface: the skills, which are the intended consumers. Deliberately NOT
# PENDING.md — drafts there legitimately quote ids that do not exist yet.
skills_dir = HOME / ".claude" / "skills"
scanned = 0
if skills_dir.is_dir():
for f in sorted(skills_dir.rglob("*.md")):
try:
if f.stat().st_size > 200_000:
continue
body = f.read_text(errors="replace")
except OSError:
continue
scanned += 1
for ref in sorted(set(DOCTRINE_REF.findall(body))):
if ref not in defined:
findings.append(
f"{f.relative_to(HOME)}: cites doctrine id {ref}, which "
f"~/CLAUDE.md does not define — the rule was reworded or removed")
control("doctrine-id parser detects a definition",
bool(DOCTRINE_DEF.findall("x <!-- D:memory.check-first --> y")))
control("doctrine-id parser detects a citation",
DOCTRINE_REF.findall("see D:memory.check-first here") == ["D:memory.check-first"])
control("doctrine-id parser rejects a non-id",
not DOCTRINE_DEF.findall("<!-- D:nodot -->")
and not DOCTRINE_REF.findall("D:NoDot.Caps"))
control("doctrine-id citing surface is reachable",
(not skills_dir.is_dir()) or scanned > 0)
# ------------------------------------------------------------- report
failed_controls = [lbl for lbl, ok in controls if not ok]
if failed_controls:
+389
View File
@@ -0,0 +1,389 @@
#!/usr/bin/env python3
"""Wake digest — cheap, computed session-start context.
Emits ~600 tokens of CURRENT state at SessionStart, replacing the ~1,000-token
session-handoff.md snapshot that had gone five months stale.
The governing property: this is DERIVED, never CACHED. A stored digest drifts
silently from the substrate — that is precisely how MemPalace's boot context
failed. A computation over the substrate cannot drift; it can only have bugs,
and bugs are found by positive controls. Every section here degrades honestly:
if it cannot be computed it says so rather than emitting nothing.
wake-digest.py emit the digest
wake-digest.py --selftest prove each extractor detects presence AND absence
Provenance: 2026-07-28, steward-authorized alongside the PENDING split and the
CLAUDE.md doctrine annotation. Sibling of governance-drift-check.py.
"""
import os, re, subprocess, sys, time
HOME = os.path.expanduser("~")
D = os.path.join(HOME, "dotfiles")
MEM = os.path.join(HOME, ".claude", "projects", "-Users-davidglidden", "memory")
PENDING = os.path.join(D, "PENDING.md")
REVIEWED = os.path.join(D, "REVIEWED.md")
DRIFT = os.path.join(D, "scripts", "governance-drift-check.py")
REPOS = ["CapableMind-AI", "BetterMemories.io", "chamber-library",
"animal-davidglidden-eu", "studium-engine"]
warn = []
def sh(args, cwd=None, timeout=8):
try:
r = subprocess.run(args, cwd=cwd, capture_output=True, text=True, timeout=timeout)
return r.stdout.strip() if r.returncode == 0 else None
except Exception:
return None
def read(p):
try:
return open(p, encoding="utf-8").read()
except Exception:
return None
# ---------- extractors (pure functions of text, so --selftest can exercise them) ----------
def open_items(text):
"""-> [(header, line_no)] for every '## ' item lacking closure evidence.
Closure evidence = a REVIEWED-N (resolved by the caller) or CLOSED/COMPLETED
in the header. Any '## ' line is an item — assuming one naming family missed
twenty items on 2026-07-28."""
out = []
for i, l in enumerate(text.split("\n"), 1):
if l.startswith("## ") and "CLOSED" not in l and not l.startswith("## COMPLETED"):
out.append((l[3:].strip(), i))
return out
def tag_of(text, header):
m = re.search(re.escape("## " + header) + r".*?\n\*\*Tag:\*\*\s*(\[[A-Z]+\])",
text, re.S)
return m.group(1) if m else ""
def strip_frontmatter(text):
"""YAML frontmatter restates the thread inside `description:`, and matching
there runs the extraction on into `metadata:`. The body is the record."""
m = re.match(r"^---\n.*?\n---\n", text, re.S)
return text[m.end():] if m else text
def extract_anchor(text, anchor, limit=900):
"""Verbatim text following an anchor. Never summarised — a digest that
paraphrases the steward's own words is the lossy extraction this whole
architecture is designed to avoid.
Handles both wrap shapes: `**ANCHOR: content**` (label and content share the
bold run) and `**ANCHOR for next-Claude:** content` (label closes first)."""
text = strip_frontmatter(text)
i = text.find(anchor)
if i < 0:
return None
s = text[i + len(anchor): i + len(anchor) + limit + 120]
c = s.find(":")
if 0 <= c <= 40: # remainder of the label, not content
s = s[c + 1:]
cut = re.search(r"\n\s*\n", s)
if cut:
s = s[: cut.start()]
s = re.sub(r"\s+", " ", s.replace("**", "")).strip(" *—-")
return (s[:limit].rstrip() + " …[truncated]") if len(s) > limit else s
# ---------- sections ----------
def sec_pause():
files = [f for f in os.listdir(MEM)
if f.startswith("session-") and not f.startswith("session-ledger-")]
if not files:
warn.append("no session memory files found")
return None, None
newest = max(files, key=lambda f: os.path.getmtime(os.path.join(MEM, f)))
age = time.time() - os.path.getmtime(os.path.join(MEM, newest))
h = age / 3600
span = f"{age/60:.0f} min" if h < 1 else (f"{h:.1f} h" if h < 48 else f"{h/24:.1f} days")
return newest, span
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)}
items = []
for h, ln in open_items(t):
m = re.match(r"PENDING-(\S+?)\s*—", h)
if m and m.group(1) in rev:
continue
items.append((h, ln, tag_of(t, h)))
if not items:
warn.append("PENDING.md parsed but yielded no open items — check the parser")
return items
def sec_reviewed(n=3):
t = read(REVIEWED)
if t is None:
warn.append("REVIEWED.md unreadable")
return []
hs = re.findall(r"^## (REVIEWED-\S+ —.*)$", t, re.M)
out = []
for h in hs[-n:]:
m = re.search(re.escape("## " + h) + r".*?\n\*\*Decision:\*\*\s*(\w+)", t, re.S)
out.append((h[:88], m.group(1) if m else "?"))
return out
def sec_drift():
o = sh([sys.executable, DRIFT])
if o is None:
warn.append("drift check did not run")
return None
if "INSTRUMENT NOT VERIFIED" in o:
return "INSTRUMENT NOT VERIFIED — treat as unestablished"
m = re.search(r"(\d+) claim", o)
return m.group(1) if m else "0"
def sec_repos():
rows = []
for r in REPOS:
p = os.path.join(HOME, "_Dev", r)
if not os.path.isdir(os.path.join(p, ".git")):
continue
sb = sh(["git", "-C", p, "status", "-sb", "--porcelain"]) or ""
lines = sb.split("\n")
head = lines[0][3:] if lines and lines[0].startswith("##") else "?"
dirty = sum(1 for l in lines[1:] if l.strip())
subj = sh(["git", "-C", p, "log", "-1", "--format=%s"]) or "?"
rows.append((r, head, dirty, subj[:60]))
if not rows:
warn.append("no repos resolved")
return rows
def sec_thread(newest):
t = read(os.path.join(MEM, newest)) if newest else None
if t is None:
warn.append("session file unreadable — no thread or question")
return None, None
th = extract_anchor(t, "PULLING THREAD", 400)
q = extract_anchor(t, "LITERAL QUESTION", 900)
if th is None:
warn.append("no PULLING THREAD anchor in the last wrap")
if q is None:
warn.append("no LITERAL QUESTION anchor in the last wrap")
return th, q
# ---------- the .app brief ----------
# The jurist reads Claude.app's personal preferences and has NO filesystem access, so
# unlike CLAUDE.md that document cannot compute its state — it can only cache it. A cache
# drifts; the mitigations are to keep it small, generate rather than hand-write it, and
# date it so staleness is visible rather than misleading (Constitutional Constraint #4
# across a boundary we cannot check). This emits a paste-ready block and records when it
# was generated, so the wake can report the age.
BRIEF_PATH = os.path.join(D, "claude", "app-brief.md")
def trackers():
t = read(os.path.join(MEM, "MEMORY.md"))
if t is None:
warn.append("MEMORY.md unreadable — no workstreams for the brief")
return []
m = re.search(r"^## Canonical Workstream Trackers\s*$(.*?)^## ", t, re.S | re.M)
if not m:
warn.append("MEMORY.md has no 'Canonical Workstream Trackers' heading")
return []
def clip(s, n):
s = re.sub(r"\s+", " ", s).strip()
if len(s) <= n:
return s
return s[:n].rsplit(" ", 1)[0] + "…"
out = []
for line in m.group(1).split("\n"):
if not line.startswith("- "):
continue
body = re.sub(r"\[([^\]]+)\]\([^)]*\)", r"\1", line[2:]) # unlink
body = re.sub(r"[*`]", "", body)
name, _, gist = body.partition(" — ")
out.append((clip(name, 38), clip(gist, 72)))
return out
def commits_30d(path):
o = sh(["git", "-C", path, "log", "--since=30.days", "--oneline"])
return len(o.split("\n")) if o else 0
def brief(stamp):
"""Replaces ONLY the projects half of the .app preferences' §Standing Context.
The personal entries there (the orchestra conflict, the divorce, fraternal practice)
are steward-held, unverifiable from any substrate, and must never be generated."""
items = sec_pending()
o = [f"### Standing Context — Projects *(generated {stamp}; do not hand-edit)*",
"",
"Regenerate: `python3 ~/dotfiles/scripts/wake-digest.py --brief`. This is a snapshot,",
"not a live view — the reader of this document has no filesystem access, so it cannot",
"be computed here. If today is more than ~30 days after the date above, treat every",
"line as unverified rather than current. Personal standing context is kept separately",
"and by hand.", "",
"TRACKER INDEX (from MEMORY.md — what exists, not what is hot; see repo activity"
" below for that)"]
for n, tail in trackers():
o.append(f" {n:<38} {tail}")
o.append(f"\nOPEN AUTHORIZATION ITEMS ({len(items)}) — full text in ~/PENDING.md; "
"closed items in ~/PENDING-archive.md")
for h, _ln, tag in items:
o.append(f" {tag:<12} {h[:92]}")
o.append("\nLAST RULINGS")
for h, d in sec_reviewed(4):
o.append(f" {d:<10} {h}")
o.append("\nREPO ACTIVITY (commits, last 30 days)")
for r in REPOS:
p = os.path.join(HOME, "_Dev", r)
if os.path.isdir(os.path.join(p, ".git")):
o.append(f" {r:<24} {commits_30d(p):>4}")
o.append(f"\nGOVERNANCE DRIFT — ~/CLAUDE.md: {sec_drift()} substrate-contradicted claim(s)")
o.append("\n*(end generated block)*")
return "\n".join(o) + "\n"
def brief_age_days():
"""Age from the date written INSIDE the brief, never from mtime: git does not
preserve mtime, so a fresh clone would make a stale brief read as newly
generated — false freshness inside the freshness instrument."""
t = read(BRIEF_PATH)
if t is None:
return None
m = re.search(r"generated (\d{4})-(\d{2})-(\d{2})", t)
if not m:
warn.append("app-brief.md carries no 'generated <date>' stamp — age unknown")
return None
y, mo, d = (int(x) for x in m.groups())
return (time.time() - time.mktime((y, mo, d, 12, 0, 0, 0, 0, -1))) / 86400
# ---------- self-test ----------
def selftest():
ok = True
def chk(name, cond):
nonlocal ok
ok = ok and cond
print(f" [{'ok ' if cond else 'FAIL'}] {name}")
print("extractor controls (presence AND absence):")
chk("open_items finds a plain item",
open_items("## PENDING-1 — x") == [("PENDING-1 — x", 1)])
chk("open_items finds a non-numeric family [v1 splitter's blind spot]",
open_items("## PENDING-S2 — x\n## PENDING — ICP-19") and
len(open_items("## PENDING-S2 — x\n## PENDING — ICP-19")) == 2)
chk("open_items excludes CLOSED / COMPLETED",
open_items("## PENDING-1 — x (CLOSED)\n## COMPLETED — y") == [])
chk("open_items returns empty on empty input", open_items("") == [])
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("extract_anchor pulls verbatim",
extract_anchor("noise\n**PULLING THREAD:** do the thing.\n\nmore", "PULLING THREAD")
== "do the thing.")
chk("extract_anchor handles label-inside-bold (**A: content**)",
extract_anchor("**PULLING THREAD: do the thing.**\n\nmore", "PULLING THREAD")
== "do the thing.")
chk("extract_anchor drops a label remnant (**A for next-Claude:** content)",
extract_anchor("**LITERAL QUESTION for next-Claude:** Is it?\n\nx", "LITERAL QUESTION")
== "Is it?")
chk("extract_anchor skips YAML frontmatter",
extract_anchor('---\ndescription: "PULLING THREAD: the wrong one."\ntype: x\n---\n'
"**PULLING THREAD: the right one.**\n\nz", "PULLING THREAD")
== "the right one.")
chk("extract_anchor marks truncation",
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)
print("\nlive substrate:")
chk("PENDING.md readable", read(PENDING) is not None)
chk("REVIEWED.md readable", read(REVIEWED) is not None)
chk("drift check runs", sec_drift() is not None)
chk("memory dir resolves", os.path.isdir(MEM))
print("\nSELFTEST", "PASS" if ok else "FAIL")
return 0 if ok else 2
# ---------- render ----------
def main():
newest, span = sec_pause()
th, q = sec_thread(newest)
items, revs, drift, repos = sec_pending(), sec_reviewed(), sec_drift(), sec_repos()
o = ["=== WAKE DIGEST (computed now — not a stored snapshot) ==="]
o.append(f"Last wrap: {span} ago ({newest})" if span else "Last wrap: UNKNOWN")
if th:
o.append(f"\nPULLING THREAD — {th}")
if q:
o.append(f"\nOPEN QUESTION — {q}")
o.append(f"\nOPEN AUTHORIZATION ITEMS ({len(items)}) — bodies in ~/PENDING.md at the line given;"
" closed items are in ~/PENDING-archive.md")
for h, ln, tag in items:
o.append(f" L{ln:<5} {tag:<12} {h[:78]}")
if revs:
o.append("\nLAST RULINGS")
for h, d in revs:
o.append(f" {d:<10} {h}")
o.append(f"\nGOVERNANCE DRIFT — ~/CLAUDE.md: {drift} substrate-contradicted claim(s)"
" (detection only; correction needs [ESCALATE])")
age = brief_age_days()
if age is None:
o.append("\n.APP BRIEF — never generated. The jurist's §Standing Context cannot be"
" checked from here; run `wake-digest.py --brief` and paste it.")
elif age > 30:
o.append(f"\n.APP BRIEF — last GENERATED {age:.0f} days ago. This tracks generation,"
" not pasting: it is a lower bound on the jurist's staleness, never a"
" guarantee of freshness.")
if repos:
o.append("\nREPOS")
for r, head, dirty, subj in repos:
flag = f" ~{dirty} dirty" if dirty else ""
o.append(f" {r:<24} {head:<34}{flag} {subj}")
if warn:
o.append("\n⚠ DIGEST DEGRADED — these sections could not be computed:")
for w in warn:
o.append(f" - {w}")
o.append("=== END WAKE DIGEST ===")
print("\n".join(o))
def emit_brief():
stamp = sh(["date", "+%Y-%m-%d"]) or "date-unavailable"
text = brief(stamp)
os.makedirs(os.path.dirname(BRIEF_PATH), exist_ok=True)
open(BRIEF_PATH, "w", encoding="utf-8").write(text)
print(text)
print(f"[written to {BRIEF_PATH} — the wake reports this file's age]", file=sys.stderr)
if warn:
print("⚠ brief degraded: " + "; ".join(warn), file=sys.stderr)
if __name__ == "__main__":
try:
if "--brief" in sys.argv:
sys.exit(emit_brief())
sys.exit(selftest() if "--selftest" in sys.argv else main())
except Exception as e: # never break session start
print(f"=== WAKE DIGEST UNAVAILABLE — {type(e).__name__}: {e} ===")