Profiles are approved.
The plan is docs/plans/profiles.md, and this issue tracks phase 1.
Phase 2, several accounts per agent, is #278, which is where the ask came from and where that half is tracked.
What it is
A profile is a fully isolated termic instance: its own projects, tasks, settings, agents and config, running in its own window.
The point is two windows on two monitors, live at the same time, that cannot see each other's projects.
Not a sidebar filter, and not serial switching inside one window.
This replaces the Spaces idea, which was deleted in 8f8161e.
Spaces was a filter with a window attached, and the ask was the boundary rather than the view.
Settled
- One window per profile. Strictly 1:1 in v1. Several windows sharing one profile is deferred: it needs a Rust-authoritative sync layer, and the same task mounted twice means either two WebGL terminals on one PTY or scrollback replay on every move.
- Everything is profile scoped: settings, projects, tasks, agents, config.
- Phase 1 changes nothing about agent logins. Every profile shares the one login each agent already has, so a new profile opens with its agents already signed in. No config dir is relocated in this phase.
- The CLI and MCP address the profile that owns the project. A project registered in more than one profile resolves to the most recently focused window.
Data layout
One frozen slug keys both trees, so the two halves of a profile are the same word in each:
<data>/profiles/<slug>/ for settings, projects, tasks and scratch.
~/termic/profiles/<slug>/tasks for worktrees.
The default profile is the existing root directory and nothing moves, so an install that never creates a second profile is byte for byte what it is today, and profiles.json does not exist until the first one is created.
App data deliberately stays out of ~/termic: the Seatbelt profile ends with a deny on the data dir, and nesting that inside the worktrees tree would put an allow and a deny in one subtree.
UI
- A strip above the sidebar footer carrying the accent tile and the profile name in clear, which appears only once a profile exists. Clicking it switches to a profile, or launches its window if it is closed.
- The footer loses its Add project button, which duplicates the one in the PROJECTS header.
- A New Profile wizard, not
WelcomeDialog, whose welcomed flag is global and must never replay.
- Deleting a profile asks whether to keep its worktrees. It never deletes a branch and never touches a main checkout.
Decide before any code
How Rust resolves which profile a command means.
Threading a slug through every command signature is invasive and silently forgettable.
Deriving it from the calling window (window: tauri::Window, profile-<slug> label) and then deleting data_dir() makes a forgotten call site a compile error rather than a silent write into the default profile's projects.json.
Biggest known cost
localStorage is not per profile and never will be for free: all windows share one webview origin, so everything not keyed by task UUID needs a profile:<slug>: namespace or a move to the per-profile data dir.
The plan lists the rest, including the tray merge, the deep_link_take_pending race across windows, and the multi-window perf budget.
Profiles are approved.
The plan is docs/plans/profiles.md, and this issue tracks phase 1.
Phase 2, several accounts per agent, is #278, which is where the ask came from and where that half is tracked.
What it is
A profile is a fully isolated termic instance: its own projects, tasks, settings, agents and config, running in its own window.
The point is two windows on two monitors, live at the same time, that cannot see each other's projects.
Not a sidebar filter, and not serial switching inside one window.
This replaces the Spaces idea, which was deleted in 8f8161e.
Spaces was a filter with a window attached, and the ask was the boundary rather than the view.
Settled
Data layout
One frozen slug keys both trees, so the two halves of a profile are the same word in each:
<data>/profiles/<slug>/for settings, projects, tasks and scratch.~/termic/profiles/<slug>/tasksfor worktrees.The default profile is the existing root directory and nothing moves, so an install that never creates a second profile is byte for byte what it is today, and
profiles.jsondoes not exist until the first one is created.App data deliberately stays out of
~/termic: the Seatbelt profile ends with a deny on the data dir, and nesting that inside the worktrees tree would put an allow and a deny in one subtree.UI
WelcomeDialog, whosewelcomedflag is global and must never replay.Decide before any code
How Rust resolves which profile a command means.
Threading a slug through every command signature is invasive and silently forgettable.
Deriving it from the calling window (
window: tauri::Window,profile-<slug>label) and then deletingdata_dir()makes a forgotten call site a compile error rather than a silent write into the default profile'sprojects.json.Biggest known cost
localStorageis not per profile and never will be for free: all windows share one webview origin, so everything not keyed by task UUID needs aprofile:<slug>:namespace or a move to the per-profile data dir.The plan lists the rest, including the tray merge, the
deep_link_take_pendingrace across windows, and the multi-window perf budget.