⓪ First boot after upgrade — auto-migrate banner
Idempotent boot-time migration: each existing workspace becomes a project with the same folder. Sessions re-linked automatically.
workspaces/ wick/files/ sandbox/files/ docs/ (custom path)
projects/ <id1>/files/ (was wick) <id2>/files/ (was sandbox) <id3>/ (was docs; custom)
sessions/<sid>/meta.json - workspace: "wick" + project_id: <id1>
① Unscoped landing (default)
Projects section above. Recent shows all chats across projects, each row tagged with its project. New session = no project unless picked.
New session
Pick a project (or skip for unscoped). Session is created when you send the first message.
Pick a project to auto-prefill provider + preset. Folder follows the project.
② Scoped to project — defaults auto-prefilled
Klik project di sidebar → swap ke scoped. Provider + preset auto-prefill dari project defaults. Folder implicit (1 folder per project).
New session in 📁 Wick Backend
Provider + preset inherited from project. Override anything per-session.
Teal dropdowns = inherited from project. Click any to override.
③ Move chat to project — 1-level menu
Right-click row → submenu list projects. Atomic meta.project_id update. Folder of target project becomes the new cwd context.
Move semantics
- Read
sessions/<sid>/meta.json - Write
meta.project_id = newPid(atomic viastorage.WriteJSON) - Registry:
upsertSessiondgn meta baru
Session ID + path stable → workflows / channels / agent spawn yg pegang sid gak break. Filesystem unchanged. Agent next-spawn pakai folder dari new project.
Cwd shift caveat
Pindah session ke project lain = cwd berubah saat next agent spawn. Lama (terminated) subprocess gak ke-touch; live subprocess perlu kill + respawn buat pickup cwd baru.
Drag-and-drop alternative
Drag chat row dari Recent ke project node di sidebar = same move op. Discoverable via right-click; power-user via drag.
④ Project settings — single folder + defaults
Folder is part of project identity — single field (managed or custom path). Defaults bundled. Pinned sessions managed inline.
12 chats · created 2026-05-20
Folder
Absolute path to an existing folder. Wick will use this as the agent cwd.
Wick creates and owns the folder at projects/<id>/files/. Useful for scratch sessions.
Defaults
📌 Pinned sessions
Project meta preview
# projects/01J.../meta.json id: "01J..." name: "Wick Backend" icon: "📁" custom_path: "/d/code/work/wick" defaults: preset: "engineer" provider: "claude/claude" pinned_sessions: ["01J..."]
Folder change semantics
- Managed → custom: managed
files/kept on disk (orphaned backup; user-deletable manual) - Custom → managed: new managed dir created; custom path untouched (wick never owned it)
- Custom A → custom B: meta-only change; no folder touched
- Live sessions: cwd shifts at next spawn; running subprocess unaffected until restart
⑤ Storage layout (cheat sheet)
Project = single folder. Managed = nested. Custom = referenced by absolute path only.
<BaseDir>/ presets/ unchanged workflows/ unchanged sessions/ <sid>/ meta.json ← + project_id (replaces workspace) conversation.jsonl ... projects/ ← NEW (replaces workspaces/) <project-id>/ meta.json ← defaults, pinned, folder ref files/ ← managed cwd (only if custom_path empty)
sessions/<sid>/meta.jsonprojects/<pid>/meta.json
- Registry in-memory cache (project + session maps)
Per-project sessions.jsonl dropped — in-memory filter via registry.sessions by Meta.ProjectID cukup utk v1 (sessions < 10k).
⑥ Migration detail (workspaces/ → projects/)
Idempotent boot-time scan. 1:1 mapping — each workspace becomes a project with the same folder.
for each workspaces/<name>/: pid = uuid.new() project.Create({ id: pid, name: name, # display = old workspace name description: ws.description, custom_path: ws.custom_path, defaults: { preset: ws.default_preset, provider: ws.default_provider, }, tags: ws.tags, }) if ws.custom_path == "": # managed → move folder (atomic same-FS rename) os.Rename( "workspaces/<name>/files", "projects/<pid>/files" ) # else: custom path unchanged, only referenced from meta for each sessions/<sid>/ where meta.workspace != "": pid = workspaceToProject[meta.workspace] meta.project_id = pid meta.workspace = "" # drop legacy field session.SaveMeta(sid, meta)
Idempotency: skip if projects/ already non-empty. Legacy workspaces/ dir kept defensively; cleanup deferred to v1.1.