# `workspace/` — the parts of the workspace that live outside every repo Claude Code runs from `/mnt/5_hdd/felhom.eu/git`, which is **not a git repository**. Three things that shape every session live there or in `~/.claude/` and are therefore on one machine only: | Thing | Live path | Versioned where | Installed by | |---|---|---|---| | workspace-root instructions | `/CLAUDE.md` | `documentation/runbooks/workspace-CLAUDE.md` | `scripts/install_workspace.py` | | `InstructionsLoaded` hook | `~/.claude/settings.json` | `workspace/hooks/instructions-loaded.json` | `scripts/install_workspace.py` | | auto-memory store | `/.claude-memory/` | **not versioned — backed up** | `dooplex-backup.service` | ## Why the CLAUDE.md source is NOT in this directory `documentation/runbooks/workspace-CLAUDE.md` is **canonical** and stays where it is. It already has a gate enforcing byte-identity with the live file (`instructions_gate.py` check 5), and it is the path every existing pointer names. Copying it here would create a *third* copy of a file whose whole problem is that copies drift — the installer reads the canonical path instead. ## Why the memory store is backed up and not committed It is **auto-written**: Claude writes it, so nobody reviews it before it lands. It may name hosts, paths and out-of-band secret locations that this project's secrets rule keeps out of committed files. A scan on 2026-08-06 found no credential *values* (535 keyword mentions across 104 files, one `key: value`-shaped hit that was prose, zero private-key blocks) — but "no secrets today" is not a property a directory keeps on its own when a machine writes to it unattended. So it is protected by `dooplex-backup.service`'s User Data component instead (`CLAUDE_MEMORY_DIR` in `/opt/backup/scripts/backup-config.sh`). The exact change, recorded here because the file it was made in is **not** version-controlled: ```sh # /opt/backup/scripts/backup-config.sh export CLAUDE_MEMORY_DIR="/mnt/5_hdd/felhom.eu/git/.claude-memory" # /opt/backup/scripts/backup-data.sh — restic takes multiple paths; an absent optional path is # skipped with a printed reason, never a failure local backup_paths=("${DATA_SOURCE_DIR}") if [ -n "${CLAUDE_MEMORY_DIR:-}" ] && [ -d "${CLAUDE_MEMORY_DIR}" ]; then backup_paths+=("${CLAUDE_MEMORY_DIR}") fi restic -r "${RESTIC_REPO_DATA}" backup "${backup_paths[@]}" "${exclude_args[@]}" ... ``` **`/opt/backup/scripts/` is itself unversioned host state on DooPlex** — no repo tracks it. That is a fresh instance of the very class this directory exists to close, found while closing it. Filed as a register item rather than fixed here: bringing a root-owned production backup script under version control (and deciding what installs it) is its own change, not a rider on this one. **One-time cost of the change, so it is not mistaken for a fault:** adding a path to a restic path set invalidates the parent-snapshot match, so the first run after this edit logs `no parent snapshot found, will read all files` and re-reads the whole source (405 GiB). Chunk-level dedup means storage barely moves; subsequent runs find a parent and are incremental again. **Caveat, stated because it is easy to misread as safety:** that backup's destination (`/mnt/5_hdd/backup`) is on the **same physical disk** as the store. It protects against accidental deletion, **not** against loss of `sda1`, and there is no off-site leg for the DooPlex backup set.