# Memory Crystal v0.4.0 Implementation Plan

**Date:** 2026-03-03
**Author:** CC-Mini
**Status:** Ready to build
**Repo:** `ldm-os/components/memory-crystal-private/`
**Onboarding design:** `ai/plans/2026-03-03--onboarding-flow.md`

---

## What We're Building

Parker designed a full onboarding flow for Memory Crystal with Crystal Core/Crystal Node architecture. This plan builds the features that flow requires.

### New CLI Commands
- `crystal role` ... show current role (Core/Node/Standalone), relay config, embedding status
- `crystal promote` ... make this device the Crystal Core
- `crystal demote` ... make this device a Crystal Node
- `crystal doctor` ... health check: 10 checks showing what's installed, what's missing, and how to fix it
- `crystal backup` ... run a backup now
- `crystal backup setup` ... install daily LaunchAgent (03:00, keeps last 7 copies)
- `crystal bridge setup` ... install + register lesa-bridge MCP server
- `crystal bridge status` ... show bridge install state

### New Files
- `src/role.ts` ... role detection, promote/demote logic
- `src/doctor.ts` ... health check engine (10 checks)
- `src/bridge.ts` ... bridge install/register detection
- `scripts/ldm-backup.sh` ... backup script (follows crystal-capture.sh pattern)

### Modified Files
- `src/cli.ts` ... all new command dispatch
- `src/ldm.ts` ... deployBackupScript(), installBackupLaunchAgent()
- `skills/memory/SKILL.md` ... full rewrite with branching onboarding flow
- `package.json` ... build entries + version bump to 0.4.0

---

## Architecture

### Role Detection (`src/role.ts`)

Auto-detection logic:
1. Check `~/.ldm/state/crystal-role.json` (override from promote/demote wins)
2. If no override: check env vars
   - `CRYSTAL_RELAY_URL` set + no local embedding provider = **node**
   - `CRYSTAL_RELAY_URL` set + local embedding provider = **core**
   - No relay URL = **standalone**

State file: `~/.ldm/state/crystal-role.json`
```json
{ "role": "core", "override": true, "agentId": "cc-mini", "setAt": "2026-03-03T..." }
```

Follows state file patterns from cc-hook.ts (stateWritePath, resolveStatePath).

### Doctor (`src/doctor.ts`)

10 checks, each returns OK/WARN/FAIL:

1. **Role** ... informational (always OK)
2. **Database** ... crystal.db exists + chunk count
3. **Embedding provider** ... configured? (optional for nodes)
4. **Capture cron** ... crystal-capture line in crontab
5. **Relay config** ... URL + token (node), relay key (core)
6. **MCP server** ... crystal-mcp registered in ~/.claude/.mcp.json
7. **Backup** ... LaunchAgent exists
8. **Bridge** ... lesa-bridge installed + registered
9. **LDM directory** ... ~/.ldm/ structure intact
10. **Private mode** ... capture enabled/disabled

Output:
```
Crystal Doctor

  [OK] Role: core (auto-detected)
  [OK] Database: 172,341 chunks
  [!!] Backup: not configured
       Fix: crystal backup setup
  [OK] Bridge: registered

2 warnings. Run suggested fixes above.
```

### Backup (`scripts/ldm-backup.sh`)

- Copies: crystal.db (sqlite3 .backup for safe copy), state/, config.json, agents/
- Timestamped dirs: `$LDM_BACKUP_DIR/YYYY-MM-DD-HHMMSS/`
- Default destination: `$LDM_BACKUP_DIR` env var or `~/.ldm/backups/`
- Retention: last N copies (default 7, `--keep N` flag)
- LaunchAgent: `ai.openclaw.ldm-backup.plist` runs daily at 03:00

New functions in ldm.ts:
- `deployBackupScript()` ... follows deployCaptureScript() pattern exactly
- `installBackupLaunchAgent()` ... plist + launchctl load

### Bridge Detection (`src/bridge.ts`)

- `isBridgeInstalled()` ... checks `which lesa`
- `isBridgeRegistered()` ... checks ~/.claude/.mcp.json for lesa-bridge
- `registerBridgeMcp()` ... `claude mcp add --scope user lesa-bridge -- lesa`

crystal init updated to detect bridge and suggest setup.

### SKILL.md Rewrite

Branching flow (designed in onboarding-flow.md):
1. Developer or end user? (fork vs npm)
2. First install or adding a device?
3. Transparency: show what gets created before doing it
4. Core recommendation for always-on machines
5. Backup offer after init
6. Relay choice: WIP.computer vs self-hosted vs none
7. Bridge: auto-installed
8. Verify with crystal doctor

---

## Execution Order

| Step | What | Files | Depends On |
|------|------|-------|------------|
| 1 | role.ts | new | nothing |
| 2 | role/promote/demote in cli.ts | modify | step 1 |
| 3 | doctor.ts | new | step 1 |
| 4 | doctor in cli.ts | modify | step 3 |
| 5 | ldm-backup.sh | new | nothing |
| 6 | backup functions in ldm.ts | modify | step 5 |
| 7 | backup in cli.ts | modify | step 6 |
| 8 | bridge.ts | new | nothing |
| 9 | bridge in cli.ts | modify | step 8 |
| 10 | update crystal init | modify | steps 6, 8 |
| 11 | SKILL.md rewrite | modify | steps 1-10 |
| 12 | package.json build + version | modify | all |
| 13 | build + test | - | step 12 |

Steps 1, 3, 5, 8 can be built in parallel (no dependencies).

---

## Testing

```bash
npm run build

# Role
node dist/cli.js role
node dist/cli.js promote && node dist/cli.js role
node dist/cli.js demote && node dist/cli.js role

# Doctor
node dist/cli.js doctor

# Backup
node dist/cli.js backup
ls ~/.ldm/backups/

# Bridge
node dist/cli.js bridge status
```
