Skip to content

Commit e8be958

Browse files
authored
Merge branch 'main' into fix/upstream-error-log
2 parents 2aab5ea + 0e7aa80 commit e8be958

2 files changed

Lines changed: 65 additions & 0 deletions

File tree

WORKAROUND_CATALOG.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,34 @@ An earlier reading of gregmarkowitz-gif's 2026-08-31 report on the thread framed
100100

101101
**Authoritative fix Anthropic should ship (from the thread):** the canonical tracker is #41458. Any workaround here is defensive; the upstream fix is theirs. Ship with the mechanism identified so users can distinguish "safe to leave setting high" from "safe to leave setting low" from "no configuration is safe."
102102

103+
#### Operational recipe: durable-snapshot + touch-refresh stack
104+
105+
The `tool`-depth row above ("out-of-band snapshot") is the mitigation that survives regardless of mechanism. Below is the operational shape cache-fix-team runs, with the gotchas we hit the hard way in the 2026-09-01 → 2026-09-05 window. Adapt to fit; the invariants that matter are called out.
106+
107+
**The stack (four pieces):**
108+
109+
1. **Settings:** `"cleanupPeriodDays": 30` (default) or lower in `~/.claude/settings.json`. Do not set 99999.
110+
2. **Hourly snapshot:** `rsync -a --link-dest=<prev-snap>` from `~/.claude/` to an archive dir on a filesystem CC does not walk. Not just `projects/` — the WSL2 report on CC#62272 shows `file-history/` and paste-cache siblings in the blast radius. `--link-dest` gives hardlink dedup between snapshots when they share a filesystem; on Btrfs `cp --reflink=always` is the CoW alternative.
111+
3. **Daily selective touch:** `find ~/.claude/projects/ -type f -mtime +20 -exec touch {} +`. Only bumps files near whatever mtime cliff CC uses, does NOT flatten recent mtimes.
112+
4. **Systemd-timer with `Persistent=true` for both jobs** so a missed run catches up on next boot.
113+
114+
**Non-obvious behaviors (each one cost real evidence to pin):**
115+
116+
- **Blanket touch breaks `claude --continue`.** `--continue` picks the most-recently-modified JSONL in the current project's directory. A blanket touch across every file flattens all mtimes to the touch instant, and `--continue` then picks arbitrarily among tied siblings. Selective touch (`-mtime +20`, or wherever your safety buffer lands relative to your setting) preserves the mtime ordering `--continue` needs. If you must recover a specific old session, use `claude --resume <session-id>` — it bypasses the `--continue` heuristic entirely.
117+
- **`/rename <name>` writes to two locations.** Runtime: `~/.claude/sessions/<pid>.json` (dies with the process, has `formerNames[]` for rename history). Durable: `~/.claude/projects/<project-key>/<session-id>/custom-title.json` — a `{"customTitle":"..."}` file inside the per-session subdir. `claude --resume <name>` reads the durable one at invocation time. Any snapshot that captures `~/.claude/projects/` recursively catches both cases; the selective touch above walks into the subdir and bumps `custom-title.json` too.
118+
- **Archive on a separate filesystem breaks `--link-dest` hardlinks** (Linux hardlinks can't cross filesystems). Snapshots still succeed as independent copies but you lose the dedup. Keep previous-snapshot and new-snapshot on one filesystem (or switch to `cp --reflink` if that filesystem is Btrfs).
119+
- **`find -newer` against the archive is not evidence of file freshness.** `rsync -a` preserves the source mtime, so a file the source hasn't changed in 60 days still shows a 60-day-old mtime in the archive even though the snapshot was written today. Use content hashes for reconciliation, not mtime.
120+
- **Prune inside the snapshot cron, AFTER the current snapshot lands.** `find <archive> -maxdepth 1 -type d -name 'snap-*' -mmin +$((48*60)) -exec rm -rf {} +` — but ONLY on the success path. If snapshot creation fails, the prune step MUST be skipped so a broken cron can't cascade into history loss.
121+
122+
**Restore workflow (when a loss is caught):**
123+
124+
1. Identify affected project keys: `find ~/.claude/projects/ -maxdepth 1 -type d` — subdirs with no top-level `<sid>.jsonl` are candidates.
125+
2. Rsync-back: `rsync -a <archive>/snap-<pre-loss-timestamp>/projects/-home-manager-.../ ~/.claude/projects/-home-manager-.../`. Path assumes the documented whole-`~/.claude/` snapshot shape above — the archive's `projects/` subdir mirrors `~/.claude/projects/`. Additive without `--delete`, so files created since the snapshot are preserved. Restore other sibling subtrees (`file-history/`, paste-cache) the same way: `rsync -a <archive>/snap-.../file-history/ ~/.claude/file-history/`.
126+
3. `touch` the restored files so their mtimes are current and won't age out immediately: `find ~/.claude/projects/<project-key>/ -type f -exec touch {} +`.
127+
4. Verify: `claude --resume <session-id>` — direct-session-id resume works even when `/resume`'s picker filters by cwd or session-recency.
128+
129+
If the top-level session JSONL itself is gone from every snapshot (e.g. loss predates the archive's oldest snap), the subagent tree under `~/.claude/projects/<key>/<sid>/subagents/` often survives, along with `~/.claude/history.jsonl` entries for the sessionId — enough to reconstruct a session-continuation brief without the main transcript.
130+
103131
---
104132

105133
### Cross-symptom — Post-update silent model remap (the Web Manager / April 17 pattern)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Review: PR #366 WORKAROUND_CATALOG durable snapshot recipe
2+
3+
Date: 2026-09-05
4+
Reviewed: PR #366 (`WORKAROUND_CATALOG.md`) at head `713989ce053a28e2636b1a7efafa84d367570b0d`
5+
Round: 1
6+
Label applied: `changes-requested`
7+
8+
## What Is Correct
9+
10+
- [Read] The new subsection does not reintroduce the retracted signed-int32-overflow mechanism. The existing entry still says the int32 hypothesis was retracted and the mechanism is unresolved (`WORKAROUND_CATALOG.md:80-101`), while the added recipe explicitly frames the snapshot mitigation as mechanism-independent (`WORKAROUND_CATALOG.md:103-105`).
11+
- [Read] The subagent fallback is coherent at the docs level. The daily `find ~/.claude/projects/ -type f -mtime +20 -exec touch {} +` traversal reaches nested per-session trees, and the fallback explicitly names `~/.claude/projects/<key>/<sid>/subagents/` plus `~/.claude/history.jsonl` as reconstruction sources (`WORKAROUND_CATALOG.md:111`, `WORKAROUND_CATALOG.md:129`).
12+
- [Measured] I found no client/customer/project identifiers or origin secrets in the PR diff. Command: `gh pr diff 366 --repo cnighswonger/claude-code-cache-fix | rg -n '(/home/|visits-|vsits|client|customer|project name|[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+|ssh-rsa|BEGIN .*PRIVATE|api[_-]?key|token|secret)'` exited 1 with no matches.
13+
- [Measured] `Closes #364` targets the intended open issue. `gh issue view 364 --repo cnighswonger/claude-code-cache-fix --json number,state,title` reports issue 364 as `OPEN`, titled `docs(WORKAROUND_CATALOG): add durable-snapshot + touch-refresh workaround stack for CC#41458 (session-JSONL loss)`.
14+
- [Measured] The upstream status references still match the catalog context: `gh issue view 62272 --repo anthropics/claude-code --json state,stateReason,closedAt` reports `CLOSED`, `DUPLICATE`, `2026-08-19T20:52:54Z`; `gh issue view 41458 --repo anthropics/claude-code --json state` reports `OPEN`.
15+
16+
## Blockers
17+
18+
1. [Read] The restore command omits the `projects/` path component from the archive side, so it does not match the snapshot shape documented four lines earlier. The stack says the hourly job snapshots all of `~/.claude/` to `<archive>/snap-*` (`WORKAROUND_CATALOG.md:110`), which means project directories would restore from something like `<archive>/snap-<ts>/projects/<project-key>/`. The restore step instead says `rsync -a <archive>/snap-<pre-loss-timestamp>/-home-manager-... ~/.claude/projects/-home-manager-.../` (`WORKAROUND_CATALOG.md:125`). That command only works if the archive root is already `~/.claude/projects/`, contradicting the "not just `projects/`" invariant. Fix the example to include `projects/<project-key>/` on the source side, or explicitly document that `<archive>` points at the snapshot's `projects/` subdirectory.
19+
20+
## What Needs Attention
21+
22+
- [Measured] CI was not green at review time. `gh pr view 366 --repo cnighswonger/claude-code-cache-fix --json statusCheckRollup` showed Node 18, 20, and 22 test jobs `IN_PROGRESS`; GitGuardian and Snyk were `SUCCESS`. This PR is docs-only, so I did not run `uv run pytest -q`.
23+
24+
## Bloat / Non-Functional
25+
26+
- [Measured] Proportionate. The PR touches only `WORKAROUND_CATALOG.md` with 28 additions and 0 deletions: `gh pr diff 366 --repo cnighswonger/claude-code-cache-fix | awk ...` reported `additions=28 deletions=0`.
27+
28+
## Recommendations
29+
30+
1. Replace the restore command with a placeholder form that preserves the documented whole-`~/.claude/` archive layout, for example `rsync -a <archive>/snap-<pre-loss-timestamp>/projects/<project-key>/ ~/.claude/projects/<project-key>/`.
31+
2. Keep the mechanism-independent wording as-is; it correctly avoids reviving the PR #359 int32-overflow framing.
32+
33+
## Bottom Line
34+
35+
Request changes. The new workaround section is directionally sound and avoids the retracted mechanism claim, but the restore command is load-bearing for the documented operational recipe and currently points at the wrong archive path for the snapshot shape the PR recommends.
36+
37+
— Codex, cross-LLM review, round 1

0 commit comments

Comments
 (0)