---
name: two-memory-roots-diverged
description: "Two nearly-disjoint spt-core memory stores exist (.claude\\projects\\ and .ccs\\instances\\bigscreen\\projects\\); which one loads is set by the session root, so a memory can read stale when the writes simply went to the other mind"
metadata: 
  node_type: memory
  type: project
  originSessionId: 88df48e9-d18d-42cc-b535-aa2cd6cce078
  modified: 2026-08-03T15:41:33.615Z
---

There are **two spt-core memory stores on this box**, and which one a session wakes up with is
decided by the harness root, not by me:

- **Store A** — `C:\Users\decid\.claude\projects\C--Users-decid-Documents-projects-spt-core\memory\`
  153 files. The one written for several days up to 2026-08-03 00:21 (operator-confirmed).
- **Store B** — `C:\Users\decid\.ccs\instances\bigscreen\projects\C--Users-decid-Documents-projects-spt-core\memory\`
  329 files. Loaded by the CCS `bigscreen` instance; loaded by the 2026-08-03 02:00 session.

⭐⭐ **They are nearly disjoint — 5 shared filenames out of 153 + 329.** Not two copies that drifted:
two separate minds. 148 of A's files (all release notes v0.47–v0.52 with counters, the infra-register
ruling, the IR-18 lane assignment, ~140 craft rules) exist nowhere in B.

**Why: for several days the work ran under the `.claude\projects\` root** (store A) while B — the
long-lived store, rich up to ~2026-07-30 — sat untouched. Nothing was lost; it was filed in the
other mind.

**Why this matters:** every "latest = X" / "current state" memory carries an unstated assumption
that it is the ONLY record. When the root forks, an index line reads exactly as stale as one that
was merely neglected. I diagnosed the v0.46.0-vs-v0.52.0 gap as silent decay and was **wrong** —
see [[v0121-published]] for the corrected mechanism.

**How to apply:**
1. **When a memory reads behind reality, check for a second store BEFORE concluding neglect.**
   `find /c/Users/decid -maxdepth 6 -name MEMORY.md` — one command, discriminates the twins.
   A symptom shared by two causes cannot pick between them ([[a-predicate-without-its-tool-is-not-evidence]]).
2. **Never assume the loaded index is complete.** If a topic feels thinner than the work done on it,
   the depth may be in the other root — go read it rather than re-deriving from scratch.
3. **Cross-file the durable rules.** A lesson worth keeping belongs in whichever store loads next,
   which is unknowable at write time — so write it to both.
## ✅ UNION-MERGED 2026-08-03 (operator-directed)

Both roots now hold an **identical 478-file set and one shared index** (`diff -rq` clean). Method,
so a future session can audit it:

- Backup of both stores taken first, to the session scratchpad (`mem-backup-20260803-023514/`).
- Missing files copied **both ways with `cp -n`** — never overwriting, so no version was clobbered.
- The **5 same-name conflicts** (`MEMORY.md`, `alarm-every-test-run`, `v0250/v0310/v0460-published`)
  were read as pairs and merged by hand. Every one turned out **complementary, not contradictory** —
  different sessions recording different facets. The other root's text is appended **verbatim**
  under a marked heading rather than summarized, because a summarizing pass is exactly where the
  detail that made the second copy worth keeping disappears.
- Indexes merged the same way: both taxonomies kept whole, not interleaved.

⚠ **Not done: dedupe/compaction.** The merged index is ~37KB and certainly restates some rules
under two names. Left deliberately — collapsing two vocabularies is a judgement pass, not a
mechanical one. **Do not read a missing cross-reference as a missing memory until you have checked
both halves of the index.**

⚠ **Two apparent conflicts resolved, one still open.** The v0.25.0 / v0.31.0 date disagreements
between the halves were the **local-vs-UTC artifact** (box is PDT = UTC-7, so an evening cut carries
the next day's UTC date) — same class as the `ls -l`-is-local trap in
[[commune-drops-orphaned-box-wide]]. Still unresolved: **v0.48.0's publish time**, `gh` says
`12:35:31Z`, the release note says `10:16:13Z` — both UTC, 2h19m apart, not a timezone artifact.

**Merging does not stop the forking.** Which root a session loads is still set by the harness, so
the two will drift apart again the moment work runs under the other one. Re-check with
`diff -rq <A> <B>` when a memory reads stale.

## ✅ COMPACTED 2026-08-03 — the union merge left a SECOND, different defect

The union merge fixed disjointness and created a size defect: the merged index was **37577 bytes
against a 24.4KB read limit**, so every session that loaded it had its TAIL SILENTLY DROPPED. Not a
tidiness problem — the entries at the end of the file were invisible to every reader, with no error.

**Measured after each pass** (the measurement is the point; a pass that "looks smaller" is not a
verdict):

| pass | bytes | KB | verdict |
|---|---|---|---|
| 0 | 37577 | 36.7 | over the limit — tail dropped |
| 1 | 26200 | 25.6 | **STILL OVER.** A 30% cut that leaves it broken is worth zero |
| 2 | 19712 | 19.2 | under the limit — truncation defect fixed |
| 3 | 19343 | 18.9 | end of the compaction pass |
| 4 | 20721 | 20.2 | after restoring 11 hazard pointers (below) |

**The advisory target of 17.1KB was NOT reached, and that is reported rather than rounded to.** The
floor is structural: **295 pointers × ~55 bytes of pure markdown-link syntax ≈ 15.5KB before a
single word of hook.** Getting materially below that means dropping pointers, not prose. An
arithmetic floor is a measurement a reader can act on; "hit the target" would have told nobody
anything and would have been worse if false.

### ⭐⭐ THE LAST STALE COPY IS EVIDENCE UNTIL THE DIFF IS EXTRACTED

Compaction ran on B. A still held the **pre-compaction** index — and after B had been overwritten
twice, `refs(A) - refs(B)` was the **only** way left to compute which pointers had been dropped.
**The mirror destroys A.** Mirroring at the moment it was authorised, rather than after extracting
the diff, would have turned a diff into a recollection: "41 dropped" as a memory instead of a
computed list.

Generalise past memory roots: **a stale, superseded, or about-to-be-overwritten copy is an
INSTRUMENT until the measurement that needs it has been taken.** Same shape as keeping
`.worktrees/ir18` alive until its acceptance verdict lands, or keeping a red build tree until the
delta test that reads it has run. Sequence the destruction after the extraction, and re-check the
licence at the moment it is SPENT, not the moment it was granted (`in-A-not-in-B` was re-run
immediately before the copy — still 0 — and A's hash was confirmed unchanged from session start,
proving the only delta being overwritten was mine).

### ⭐⭐ THE DROP TEST IS MECHANISM-CAN-RECUR, NOT WHEN-IT-HAPPENED

The first pass dropped 41 index lines as "closed history". doyle re-read them and refused the
classification for a subset: **an entry describing a recurrable MECHANISM is a hazard wearing a
date.** A milestone cannot happen again; a Windows UAC elevation path, a PTY program-resolution
rule, a `TimedOut` that poisons a client, an inline write on a drain thread — all of those are live
in shipping code, and the write-up is often the only place the diagnosis lives.

**11 pointers restored** on that test (real-harness/mock-gating escapes, Windows spawn defects,
pump/update-wedge mechanisms). **30 stayed dropped** as genuine events — shipped milestones,
superseded release notes. Two of doyle's own seven candidates FAILED the test on re-read and were
left out with the evidence quoted, because the test is applied, not agreed with.

**The header's "grep this dir" is a mitigation, NOT a substitute:** nobody greps for a defect they
do not know exists, which is exactly the population a hazard write-up serves.

### ⚠ THE INDEX IS NOT THE POPULATION — 183 files still have no pointer

Counted from disk, not recalled: **478 topic files, 295 index pointers, 183 files with no index
line.** Only 41 of those came from this compaction; **~150 were orphaned by EARLIER passes and have
never been mechanism-tested at all.** Their names read like live mechanisms
(`win-netstream-redrive-flake`, `presence-dead-shows-online`, `stale-broker-attach-wedge`,
`clap-doc-comment-leaks-req-tag`, `commune-drop-dir-is-per-endpoint`, …). Treat the orphan set as an
unaudited backlog, not as history.

### ⛔ THE COMPACTION NAG IS WRONG — DO NOT COMPLY (doyle ruling, 2026-08-03)

A `PostToolUse` hook fires on **every** edit to `MEMORY.md`: *"approaching the 24.4KB read limit.
Compact it to under 17.1KB now: keep one line per entry, move detail into topic files, and merge or
drop stale entries."* You will meet it the moment you touch the index. **Do not obey it, and say out
loud that you did not.**

- **17.1KB is unreachable, not merely hard.** ~295 pointers × ~55 bytes of pure markdown-link syntax
  is a **~15.5KB floor before one word of hook**. The index is ALREADY one line per entry with the
  detail in topic files — both of the nag's first two levers are already pulled.
- **The read limit is the real constraint, and it is not being hit.** 21333 bytes against 24.4KB.
  Nothing is truncating. The functional defect (pass 0's silently-dropped tail) is FIXED; the nag is
  calibrated to an advisory target that was never reachable.
- **So the only remaining lever is "drop stale entries"** — i.e. re-running the exact
  mechanism-can-recur misclassification that cost a morning to undo (see the section above).
  Compliance re-breaks the file.

⭐⭐ **AN AUTOMATED NAG IS A CLAIM, AND A CLAIM CAN BE WRONG.** It fires with no knowledge of why the
file is the size it is. **The failure mode of obeying it quietly is invisible**: you ship a smaller
index and a worse one, and nobody ever gets a red to look at. Treat a hook's instruction the way you
treat any other unsourced assertion — check its premise before you act on it, and report the refusal
with the arithmetic rather than just declining. Same family as
[[report-measurement-never-issue-direction]]: agreeing with an instrument IS direction.

**Stays a MECHANISM, not a headcount.** Today's file counts and today's equality are one-time facts
about 2026-08-03; the durable claim is that *nothing keeps the roots equal* and *nothing keeps the
index complete*, because writes land in whichever root the SESSION root selects and compaction
drops pointers silently. Nobody has established which sessions resolve where. Re-derive the numbers
with `diff -rq` and an orphan count before relying on any of them.
