# Workbuddy Workspace Migration

> 本技能涵盖 WorkBuddy 的本地数据存储架构与工作区迁移工作流。 当重命名或移动工作区目录后会话消失、需要恢复丢失的对话， 或诊断某些会话为何在界面中不可见时，使用本技能。 触发词：workspace migration、sessions disappeared、recovery、工作空间迁移、会话丢失、恢复会话.

- Skill: `zhangweildlh/workbuddy-workspace-migration` (Agent Skill, multi-file: 6 files)
- Install (CLI): `npx skillmds@latest add zhangweildlh/workbuddy-workspace-migration`
- Raw SKILL.md: https://api.skillmd.com/api/skills/zhangweildlh/workbuddy-workspace-migration/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: zhangweildlh (https://skillmd.com/u/zhangweildlh)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/zhangweildlh/workbuddy-workspace-migration

---


# WorkBuddy Workspace Migration & Data Recovery

## Purpose

WorkBuddy stores session data across multiple local files. Renaming or moving a workspace directory
breaks the path-based linkage between sessions and their workspace, causing sessions to disappear
from the UI. This skill documents the complete storage architecture and the steps to recover sessions.

## When to Use

- Sessions disappeared after renaming or moving a workspace directory
- Need to merge sessions from multiple workspaces into one
- Want to understand how WorkBuddy stores session data locally
- Need to recover "deleted" (archived) sessions
- Need to physically purge soft-deleted sessions or stale workspaces to free disk space
- Want to pick specific sessions out of a "temp dump" workspace and group them into dedicated workspaces (post-hoc clustering)

## Data Storage Architecture

WorkBuddy stores data in `~/.workbuddy/` across these layers:

### 1. Session Content: `projects/{slug}/*.jsonl`

Each workspace gets a slug directory under `~/.workbuddy/projects/`. Inside, each session is a
JSON Lines file named by `conversationId`. Each line is a JSON object representing one message
(user message, AI reply, tool call, etc.).

Slug naming: path `D:\work\临时` becomes `d-work-临时` (lowercase, `:\` replaced with `-`).

The JSONL records contain a `cwd` field per line — if this doesn't match the workspace path,
the UI may not display the session.

### 2. Session Metadata & Workspaces: `workbuddy.db` (SQLite 3.x)

**`sessions` table**:
- `id` — conversation UUID
- `cwd` — workspace path
- `title` — session title
- `status` — session status
- `deleted_at` — **controls visibility**: `IS NULL` = visible, `IS NOT NULL` = hidden (archived)
- `is_playground` — **`1` = auto-created (never saved to workspace), `0` = explicitly saved to workspace**. Playground sessions are filtered out of workspace task lists in the UI.
- `user_id`, `mode`, `permission_mode`, `project_id`, etc.

**`workspaces` table**:
- `path` — workspace directory path
- `last_opened` — timestamp (ms)

The UI enumerates workspaces from this table. If a workspace is not registered here,
sessions bound to it will not appear even if all other data is correct. Migrating
a workspace must add the new path and remove the old one.

### 3. Session Mapping: `app/sessions.json`

Lightweight JSON cache mapping `conversationId` to `workDir`.

### 4. Other Storage

- `file-history/{conversationId}/` — versioned file snapshots per session
- `artifact-index/{conversationId}.json` — artifact summaries
- `blobs/` — uploaded images/files
- `app/session/IndexedDB/` — Electron IndexedDB (LevelDB), may contain session state

## Visibility Control

The definitive flag for whether a session appears in the UI is `workbuddy.db.sessions.deleted_at`:

```
deleted_at IS NULL     → visible in task list
deleted_at IS NOT NULL → hidden (archived/deleted)
```

Archiving a session through the UI sets `deleted_at` to the current timestamp. It does NOT delete
the JSONL file or any other data — it's a soft delete. To physically reclaim disk space, use the
purge script (see "Session Purge" section below).

## One-Click Migration (Recommended)

When the user says "migrate workspace from X to Y", run the bundled script:

```bash
uv run --project D:/Tools/Assembly/python/myenv python ~/.workbuddy/skills/workbuddy-workspace-migration/scripts/migrate.py "<old_dir>" "<new_dir>"
```

The script handles all steps automatically:
1. Copies workspace files from old to new directory (uses `copytree` with `dirs_exist_ok=True` — safe while WorkBuddy is running, won't overwrite existing files that differ)
2. **Moves** JSONL files from old project slug to new slug (avoids duplicate entries)
3. Updates `cwd` in every JSONL record (case-insensitive matching)
4. Updates `workbuddy.db`:
   - `sessions.cwd` → new path
   - `sessions.deleted_at` → NULL (un-archive)
   - `sessions.is_playground` → 0 (convert auto-created sessions to normal)
5. Updates `workspaces` table: registers new workspace (uses `last_opened_at` column), removes old one
6. Updates `app/sessions.json` if entries exist
7. **Cleans up empty old slug directory** (if all JSONL files moved successfully)

**Bug fixes (2026-09-02)**:
- Fixed `workspaces` table column name: `last_opened` → `last_opened_at` (matches actual DB schema)
- Added empty slug directory cleanup after JSONL move to prevent orphan directories

If the old directory no longer exists (already moved manually), use `--no-copy`:

```bash
uv run --project D:/Tools/Assembly/python/myenv python ~/.workbuddy/skills/workbuddy-workspace-migration/scripts/migrate.py "<old_dir>" "<new_dir>" --no-copy
```

Preview changes without applying:

```bash
uv run --project D:/Tools/Assembly/python/myenv python ~/.workbuddy/skills/workbuddy-workspace-migration/scripts/migrate.py "<old_dir>" "<new_dir>" --dry-run
```

**After running**: tell the user to restart WorkBuddy for sessions to appear.

**Multi-user**: By default, both scripts only operate on the current user's sessions (auto-detected
via `sessions.json` → `userId`). Use `--no-user-filter` to bypass, or `--user-id` to specify a different user.

## Session & Workspace Purge (Physical Delete)

> ### ⚠️ CRITICAL — `--no-clean-orphans` 铁律（已验证）
> WorkBuddy 的"删除"本质是**软删除**：仅置 `deleted_at` 隐藏会话，JSONL / file-history / artifact 仍占磁盘。
> **自加固版本起**：会话级与工作区级清理**默认即带 `--no-clean-orphans`**，脚本不再自动运行孤儿清理，**默认绝不触碰任何工作区目录**。仅当显式运行 `--clean-orphans`（孤儿清理模式）时，脚本才可能 `rmtree` 工作区目录；届时强制先列出待删工作区**绝对路径**并经 `--yes`/交互确认授权后方可执行（见下方 🔐 授权协议）。
> **后果实证（加固前旧行为）**：一次会话级清理遗漏该参数，导致 7 个工作区目录（含用户自有项目文件）被整棵移入 `D:\$Recycle.Bin`。
> **铁律**：凡是只想清理会话、**保留工作区目录**的会话级清理，命令**必须带 `--no-clean-orphans`**（现已成为默认，脚本完全不触碰工作区目录，无需额外授权）。任何会**删除工作区目录**的操作（孤儿清理、`--purge-workspace`）都**必须**先列出待删工作区**绝对路径**、并**经用户明确授权**后方可执行，绝不依赖自动孤儿清理。

WorkBuddy's "delete" is a **soft delete** — it only sets `deleted_at` to hide the session.
All JSONL files, file history, and artifact data remain on disk, consuming space.
Similarly, workspaces with no active sessions remain registered in the database
and may have stale directories and session data still on disk.

Use the bundled `purge.py` script for three levels of cleanup:

### Session-Level Purge

Delete individual soft-deleted sessions. **自加固版本起，会话级清理默认即等价于携带 `--no-clean-orphans`**：清理后**不**自动运行孤儿清理，**绝不触碰任何工作区目录**——仅删除 `~/.workbuddy/` 下的 JSONL / file-history / artifact / DB 行 / sessions.json 条目（见 `physically_delete_session`）。历史教训：加固前版本默认会跑孤儿清理，曾因遗漏该参数导致 7 个工作区目录被整棵误删。故显式携带 `--no-clean-orphans` 仍是好习惯（跨版本保险）。仅显式 `--clean-orphans` 才会触发孤儿清理，且届时强制列出待删工作区绝对路径并经授权（见下方 🔐 授权协议）。

**List all soft-deleted sessions (default, safe)**:

```bash
uv run --project D:/Tools/Assembly/python/myenv python ~/.workbuddy/skills/workbuddy-workspace-migration/scripts/purge.py
```

**Purge ALL soft-deleted sessions**:

```bash
uv run --project D:/Tools/Assembly/python/myenv python ~/.workbuddy/skills/workbuddy-workspace-migration/scripts/purge.py --all
```

**Purge by filters**:

```bash
# By ID prefix
uv run --project D:/Tools/Assembly/python/myenv python ~/.workbuddy/skills/workbuddy-workspace-migration/scripts/purge.py --ids 794f328e 0b2cc7e2

# Older than N days
uv run --project D:/Tools/Assembly/python/myenv python ~/.workbuddy/skills/workbuddy-workspace-migration/scripts/purge.py --older-than 30

# From a specific workspace
uv run --project D:/Tools/Assembly/python/myenv python ~/.workbuddy/skills/workbuddy-workspace-migration/scripts/purge.py --workspace "D:\\work\\temp"

# Larger than N KB
uv run --project D:/Tools/Assembly/python/myenv python ~/.workbuddy/skills/workbuddy-workspace-migration/scripts/purge.py --min-size 100

# Combine + dry run
uv run --project D:/Tools/Assembly/python/myenv python ~/.workbuddy/skills/workbuddy-workspace-migration/scripts/purge.py --older-than 30 --dry-run
```

### Workspace-Level Purge

Delete an entire workspace — all its sessions, disk files, and DB records.

**List all workspaces with active/inactive status (safe, first step)**:

```bash
uv run --project D:/Tools/Assembly/python/myenv python ~/.workbuddy/skills/workbuddy-workspace-migration/scripts/purge.py --list-workspaces
```

Outputs a table showing: last opened date, active/total/deleted session counts, disk usage.
Workspaces with **0 active sessions** are marked with `*` — these are cleanup candidates.

**Purge a specific inactive workspace**:

```bash
uv run --project D:/Tools/Assembly/python/myenv python ~/.workbuddy/skills/workbuddy-workspace-migration/scripts/purge.py --purge-workspace "c:\\Users\\User\\WorkBuddy\\Claw"
```

This permanently deletes:
- All sessions belonging to that workspace (even soft-deleted ones)
- The `projects/{slug}/` directory (all JSONL files)
- The workspace directory on disk
- The workspace entry from `workspaces` table
- All session entries from `sessions` table
- All matching entries from `sessions.json`
- All `file-history/` and `artifact-index/` for those sessions

**Purge ALL inactive workspaces at once (DANGER — confirm before running)**:

```bash
# Preview first
uv run --project D:/Tools/Assembly/python/myenv python ~/.workbuddy/skills/workbuddy-workspace-migration/scripts/purge.py --purge-workspace --all-inactive --dry-run

# Execute
uv run --project D:/Tools/Assembly/python/myenv python ~/.workbuddy/skills/workbuddy-workspace-migration/scripts/purge.py --purge-workspace --all-inactive
```

### Orphan Directory Cleanup

> ⚠️ **危险提示**：`--clean-orphans` 不仅会删空 `projects/{slug}/` 目录和过期 DB 条目，还会 `shutil.rmtree` 任何"磁盘存在且 0 会话"的**工作区目录本身**（含其中的用户项目文件）。**绝不**把它当作"无害清理"。会话级清理请改用 `--no-clean-orphans` 跳过它。

> 🔐 **授权协议（必须）**：孤儿目录清理与自动孤儿清理是脚本中**唯一会 `rmtree` 工作区目录**的代码路径。任何会触及工作区目录/文件的操作，都**必须**先列出将被删除的**工作区目录绝对路径**（如 `D:\Documents\AI_Work_Temp\XXX`），且**只有得到用户明确授权后**才能执行。标准流程：`--clean-orphans --dry-run` 先列出路径 → 用户核对无误并**显式确认**（或加 `--yes`/`-y`）→ 再实际执行。会话级清理（带 `--no-clean-orphans`）完全不触碰工作区目录，故无需此授权。

After session purging or workspace migration, empty directories and stale DB entries
may remain on disk. These are NOT reachable through workspace-level purge (they're not
in the `workspaces` table or have no sessions).

**Preview orphans (safe)**:

```bash
uv run --project D:/Tools/Assembly/python/myenv python ~/.workbuddy/skills/workbuddy-workspace-migration/scripts/purge.py --clean-orphans --dry-run
```

**Clean all orphans**:

```bash
uv run --project D:/Tools/Assembly/python/myenv python ~/.workbuddy/skills/workbuddy-workspace-migration/scripts/purge.py --clean-orphans
```

What gets cleaned:
- **Empty `projects/{slug}/` directories** — slug dirs with no remaining `.jsonl` files (and any leftover non-JSONL files)
- **Workspace directories with 0 sessions for ANY user** — workspace dirs that no longer have sessions (regardless of user_id)
- **Stale `workspaces` table entries** — records pointing to directories that no longer exist on disk and have 0 sessions

Safety: workspace directories are only deleted if NO user (any user_id) has sessions referencing them.
This prevents accidentally deleting other accounts' workspace data.

### What Gets Deleted

| Mode | Scope | Includes |
|------|-------|----------|
| Session purge | One session | JSONL, file-history, artifact-index, DB row, sessions.json entry |
| Workspace purge | Entire workspace | All sessions above + workspace directory + projects/ slug dir + workspaces table |

Note: `blobs/` files are NOT deleted (they may be shared across sessions).

### Multi-User Safety (Critical)

Multiple WorkBuddy accounts on the same machine share the same `~/.workbuddy/` directory.
Without user filtering, purge/migrate would operate on ALL users' sessions — potentially
deleting or breaking data belonging to other accounts.

**Both scripts use `sessions.user_id` filtering by default.** The current user's ID is
auto-detected from `sessions.json` (most recent entry's `userId`) with fallback to the
most recently active session in `workbuddy.db`.

```bash
# Default: auto-detect current user (safe)
uv run --project D:/Tools/Assembly/python/myenv python purge.py --all

# Manual: specify user_id
uv run --project D:/Tools/Assembly/python/myenv python purge.py --all --user-id 28bfa73c-3367-4b19-a753-ccae8ac3a4ef

# Dangerous: operate on ALL users (use with caution!)
uv run --project D:/Tools/Assembly/python/myenv python purge.py --all --no-user-filter
```

The same `--user-id` and `--no-user-filter` options apply to `migrate.py` as well.

### After Purge

The script automatically runs `VACUUM` on `workbuddy.db` to reclaim space.

**自加固版本起，孤儿清理默认不自动运行**（会话级/工作区级清理等价于默认 `--no-clean-orphans`）。只有显式 `--clean-orphans` 才会执行孤儿清理；届时 `clean_orphans` 会先列出每个待删工作区目录的**绝对路径**，并经 `--yes`/交互确认授权后，才 `shutil.rmtree` 该目录（**包含其中的用户项目文件**）。因此：
- 只想清会话、保留工作区目录 → **永远带 `--no-clean-orphans`**。
- 确实要连目录一起删 → 用 `--purge-workspace`（语义明确、可控），不要依赖自动孤儿清理。

Restart WorkBuddy to ensure the UI reflects the changes.

### Output Formats

- **Default**: human-readable table with size totals
- **`--json`**: machine-readable JSON array (for programmatic use)
- **`--dry-run`**: preview mode, no files touched

## Verified Operational Playbook (最平凡流程 · 已验证)

基于一次完整会话（盘点 → 会话级清理 → 误删 → 回收站恢复 → 查询 → 方案沉淀）实证总结。核心目标：**只清会话数据，绝不触碰工作区目录中的用户文件**。

### 本机工作区根目录（权威）

- `D:\Documents\AI_Work_Temp` —— 默认工作空间存储路径（绝大多数工作区在此）
- `D:\Documents\AI_MCP-Skill-CLI` —— 第二个工作区根（如 `AI_MCP-Skill-CLI\ref-material-writing`、`AI_MCP-Skill-CLI\we-mp-rss` 等子目录）

会话数据物理分离于两处：

| 位置 | 内容 | 处置 |
|---|---|---|
| `~/.workbuddy/projects/{slug}/*.jsonl` | 会话内容 | 会话级清理删除 |
| `~/.workbuddy/workbuddy.db`（sessions 表 `cwd` 匹配） | 会话元数据 | 会话级清理删除 |
| `~/.workbuddy/app/sessions.json` | 缓存映射 | 会话级清理删除 |
| `<工作区>/.workbuddy/` | 该工作区 WorkBuddy 元数据（memory/artifact） | 显式删除 |
| `<工作区>/`（根目录其他文件） | **用户项目文件** | **一律不动** |

### "最平凡流程"5 步（会话级清理标准动作）

1. **预览**：`purge.py --workspace "<路径>" --no-clean-orphans --dry-run`，确认输出只含 `~/.workbuddy` 条目、无任何 `D:\Documents\AI_Work_Temp` 目录操作。
2. **执行清理**：去掉 `--dry-run` 运行同上命令，物理删除 JSONL / file-history / artifact / DB 行 / sessions.json 条目。
3. **删工作区内 WorkBuddy 元数据**：额外 `rm -rf "<工作区>/.workbuddy"`（确认该目录确为 WorkBuddy 元数据、不含用户文件；本机需沙箱外执行，见下）。
4. **校验**：工作区目录 `D:\Documents\AI_Work_Temp\XXX` 完好、用户文件无缺；`~/.workbuddy/projects/{slug}` 该会话条目已清零。
5. **收尾**：重启 WorkBuddy 使 UI 同步。

### 本机执行约束（已验证）

- **Python 调用**：一律 `uv run --project D:/Tools/Assembly/python/myenv python <脚本>`，**禁用裸 `python`/`pip`**（`python3` 亦不可用）。
- **Git Bash 路径**：脚本路径与工作区路径写 **Windows 原生盘符**（`C:/Users/15794/...` 或 `C:\Users\...`），**勿用 `/c/Users/...`**——否则 uv 拼成相对路径导致 `No such file or directory`。
- **safe-delete 沙箱**：WorkBuddy 注入 `sitecustomize.py` 拦截删除 API，沙箱内删除被拒/改移回收站。真实删除需 `dangerouslyDisableSandbox: true` 并 `unset CODEBUDDY_SAFE_DELETE_*`。第 3 步删 `.workbuddy` 目录建议沙箱外执行。
- **BULK_GUARD**：单回合累计删文件 > 50 触发 `SystemExit(1)` 中断。批量清理应分批或沙箱外 + unset 安全删除变量。
- **多卷回收站**：`C:\$Recycle.Bin` 存 C 盘数据，`D:\$Recycle.Bin` 存 D 盘数据，恢复误删时按盘分别处理。

### 复用口诀

> 会话清理两处删（`~/.workbuddy` + 工作区 `.workbuddy`），工作区目录永不动；`--no-clean-orphans` 已为默认（显式携带更保险），先 `--dry-run` 再动手。

## Recycle Bin Recovery (误删工作区目录的恢复 · 已验证)

若遗漏 `--no-clean-orphans` 已把工作区目录整棵移入回收站，按此恢复（本次已验证：38 目录成功还原 / 0 失败）：

1. **定位**：遍历 `D:\$Recycle.Bin\<SID>\` 下所有 `$Ixxxx` 文件，解析原始路径。
2. **解析 `$I`**（关键，勿错）：
   ```python
   def parse_i(ipath):
       data = open(ipath, 'rb').read()
       idx = data.find(b'D\x00:\x00\\\x00')   # 直接搜 UTF-16-LE 的 "D:\"
       if idx == -1:
           return ""
       return data[idx:].decode('utf-16-le', errors='ignore').rstrip('\x00').rstrip(' ')
   ```
   **切勿**用 `data.find(b'\x00\x00')` 找终止符——会错误匹配"末 ASCII 字符低字节 + 终止符首字节"组成的伪 `00 00`，吞掉路径最后一个字符（如 `Claw`→`Cla`）。
3. **筛选**：仅恢复 `D:\Documents\AI_Work_Temp\<单段>\` 与 `D:\Documents\AI_MCP-Skill-CLI\`（及其下一级）中 `$Rxxxx` **为目录**的项；排除散落文件（`.md/.txt/.json/.log`）、`Deepseek-pp` 内部子文件、系统临时（`D:\System\UserTemp\*`）。
4. **还原**：`os.rename($Rxxxx, 原始路径)`，父目录优先（避免子项重复），目标已存在则跳过。
5. **校验**：恢复后 `os.path.exists(原始路径)` 验证；交叉核对 `workbuddy.db`。注意非常规位置（如 `GitExtensions-Git图形界面` 实际在 `D:\Tools\`，不在 `AI_Work_Temp`，始终完好）。

> 经验：今后任何 `purge.py` 会话级清理务必加 `--no-clean-orphans`；误删进回收站可还原，但真实物理删除（绕过沙箱）不可逆，须二次确认。

## Session Organization (Post-Hoc Clustering)

A common real-world workflow: you dump everything into a single "temp" workspace as you go,
and only later realize that some sessions are related and deserve their own dedicated workspace.

Use `organize.py` to pick specific sessions out of a source workspace and move them to a
target workspace (new or existing), without touching the rest.

**List sessions in a workspace**:

```bash
uv run --project D:/Tools/Assembly/python/myenv python ~/.workbuddy/skills/workbuddy-workspace-migration/scripts/organize.py "D:\\work\\temp"
```

Output:
```
  #  Updated       Size  Title
[0]  06-25 17:14   30KB  是不是太能弄
[1]  06-25 05:26   874KB 中金对这个AI Agent在企业应用中的发展很看好，但通过主要是要加从场景侧
[2]  06-25 03:16   3MB   n8n 和dify，对比
...

To pick sessions:
  organize.py "D:\work\temp" --pick 0,2,5 --to "D:\work\new" --dry-run
```

**Pick by index and move**:

```bash
# Preview first
uv run --project D:/Tools/Assembly/python/myenv python organize.py "D:\\work\\temp" --pick 0,2,5 --to "D:\\work\\infra" --dry-run

# Execute
uv run --project D:/Tools/Assembly/python/myenv python organize.py "D:\\work\\temp" --pick 0,2,5 --to "D:\\work\\infra"
```

**Pick by ID prefix** (useful when you have IDs from somewhere else):

```bash
uv run --project D:/Tools/Assembly/python/myenv python organize.py "D:\\work\\temp" --pick-ids 794f328e 0b2cc7e2 --to "D:\\work\\infra"
```

**What happens under the hood** (same four-layer handling as migrate.py, but per session):

1. JSONL file moved from `projects/{src_slug}/` to `projects/{dst_slug}/`
2. `cwd` field inside JSONL records updated (case-insensitive match)
3. `workbuddy.db` sessions row: `cwd` updated, `deleted_at` cleared, `is_playground` set to 0
4. `app/sessions.json` entry updated
5. Target workspace auto-registered in `workspaces` table if missing
6. `file-history/` and `artifact-index/` left in place (keyed by session ID, no path ref)

After moving, if the source workspace is now empty and you want to clean it up:
- 若源工作区**仍有用户项目文件**，**不要**运行 `--clean-orphans`（它会 `shutil.rmtree` 整个工作区目录）。只手动删除 `~/.workbuddy/projects/{src_slug}/` 即可。
- 仅当源是纯临时工作区、无任何你要在意的用户文件时，才运行 `purge.py --clean-orphans` 清空 slug 目录与过期 DB 条目。

## Manual Migration Procedure (Fallback)

If the script cannot be used, follow these steps manually:

### Step 1: Identify sessions

Query `workbuddy.db`:

```sql
SELECT id, cwd, title, deleted_at FROM sessions WHERE cwd LIKE '%old%';
```

### Step 2: Determine slugs

Path `D:\work\临时` → slug `d-work-临时` (lowercase, `:\`→`-`).

### Step 3: Copy JSONL files

Copy `~/.workbuddy/projects/{old-slug}/*.jsonl` to `~/.workbuddy/projects/{new-slug}/`.

### Step 4: Update cwd in JSONL

Each line has a `cwd` field. **CRITICAL**: JSONL may have different casing than `workbuddy.db`
(e.g. `c:\Users\...` vs `C:\Users\...`). Match case-insensitively.

### Step 5: Update workbuddy.db

```sql
UPDATE sessions SET cwd = 'D:\\new\\path' WHERE cwd LIKE 'D:\\old\\path';
UPDATE sessions SET deleted_at = NULL WHERE cwd = 'D:\\new\\path' AND deleted_at IS NOT NULL;
```

### Step 6: Update sessions.json

Update `workDir` field for matching `conversationId` entries.

### Step 7: Restart WorkBuddy

## Diagnostic Queries

List all sessions for current workspace:
```sql
SELECT id, cwd, title, deleted_at FROM sessions WHERE cwd = 'D:\\work\\临时';
```

Count sessions per workspace:
```sql
SELECT cwd, COUNT(*) FROM sessions GROUP BY cwd;
```

List registered workspaces:
```sql
SELECT path, last_opened FROM workspaces;
```

Find playground (auto-created) sessions:
```sql
SELECT id, cwd, title FROM sessions WHERE is_playground = 1;
```

Find which JSONL files exist vs. which sessions are in the database:
```bash
ls ~/.workbuddy/projects/d-work-临时/
uv run --project D:/Tools/Assembly/python/myenv python -c "import sqlite3; c=sqlite3.connect('~/.workbuddy/workbuddy.db').cursor(); c.execute(\"SELECT id FROM sessions WHERE cwd='D:\\\\work\\\\临时'\"); print([row[0] for row in c.fetchall()])"
```

## Important Notes

- `workbuddy.db` is SQLite 3.x, confirmed by `file` command and hex header `SQLite format 3\0`
- Chinese characters in paths are stored natively (UTF-8), both in SQLite and JSONL
- Do NOT delete `workbuddy.db` or `sessions.json` — they are the primary data sources
- Most session content is local (JSONL), not server-stored
- Two accounts on the same machine share the same `~/.workbuddy/projects/` directory structure
- Auto-created workspaces (no explicit "Save to Workspace") have `is_playground=1` and are NOT registered in the `workspaces` table — migration must fix both
- The `workspaces` table is the authoritative source for UI workspace enumeration; a missing entry means the workspace won't appear in the sidebar

