This document records the current product and code naming used by Alera. It is intentionally short and should stay aligned with implemented behavior.
Project: a local project path registered in Alera. It can be an existing local folder or a Git repository cloned from a URL; only Git-backed projects support linked workspaces.Workspace: a working context inside a project. A workspace points at one filesystem path and may point at one Git branch. The primary workspace points at the registered project path; linked workspaces point at Git worktrees and exist only for Git-backed projects.Worktree: the Git mechanism used to create an isolated checkout for a linked workspace. Use this term only for Git/filesystem behavior, not for the Alera UI container. Folder-only projects do not use worktrees.Workbench: the main interactive area for an active workspace. It owns pane layout, active group selection, and workspace tabs. A project may be active while no workspace is selected; in that state the shell shows an empty workspace surface and workspace-scoped panels should render without workspace context.WorkbenchPaneGroup: one pane in the split layout. It contains an ordered list of workspace tab ids and one active tab id.WorkspaceTab: the domain concept for a tab inside a workspace. It is intentionally broader than terminal because tabs can represent terminals, editors, markdown previews, browsers, or other workspace surfaces.TerminalSession: the runtime process/PTY state attached to a terminal workspace tab. Terminal sessions are owned by the durable terminal host and are reattached byterminalSessionId; they are not persisted tab records.SidebarAgentRunRow: the sidebar projection for supported agent executions detected inside terminal workspace tabs. The sidebar does not render terminal tabs that have no current agent status.AgentRun: the in-memory UI projection of a supported agent status matched to a terminal workspace tab. It is not a persisted storage model.Design system: the shared, presentational widget library inlib/src/design_system/, prefixedAlera, with co-located widget previews. Seedocs/ui-styleguide.md.
- Use
WorkspaceTabRecord,WorkspaceTabService, andWorkspaceTabKindfor persisted tabs. - Use
TerminalSessionHandle,TerminalRuntime, andTerminalSurfaceonly for terminal execution/rendering. - Keep
Workbenchfor layout and interaction shell concepts, not for persisted tab records. - Avoid introducing
TerminalTabas a domain model. A terminal tab is currently aWorkspaceTabRecordwithkind == WorkspaceTabKind.terminal. - Avoid using
WorkspaceandWorktreeinterchangeably. A workspace is product state; a worktree is a Git checkout implementation detail.
Alera persists projects, workspaces, workspace tabs, workbench layouts, settings, and view preferences in the Drift/SQLite schema defined in lib/src/shared/infra/storage/drift_database.dart.
Terminal workspace tabs store their durable terminalSessionId in the tab payloadJson. The terminal host stores runtime socket metadata and terminal checkpoints under the application support directory, outside Drift, so app/window close can detach from PTYs without killing running commands.
Legacy pre-Drift stores are no longer read or migrated.
Alera ships a separate Rust CLI named alera for non-UI background work. The desktop app launches alera terminal-host as a detached sidecar process instead of relaunching the Flutter app executable, so closing the app window detaches from terminal PTYs without creating another dock/taskbar app instance.
The CLI is the Rust crate under rust/ (rust/alera-cli, binary alera) and speaks the terminal socket protocol, host.json control file, and SQLite checkpoint schema. Release and desktop builds compile it with cargo build --locked from the native build hooks (linux/CMakeLists.txt, windows/CMakeLists.txt, and the macOS "Build Alera CLI Sidecar" Xcode phase); a Release app build produces --release, other configs build debug. The single binary is installed into Contents/Resources/alera/alera on macOS and resources/alera/alera[.exe] next to the app executable on Linux and Windows.
The app resolves the sidecar through AleraCliResolver, which honors ALERA_CLI_PATH first and then searches the bundled resources/alera/ locations. When no compiled sidecar is present (pure source checkout), development builds fall back to cargo run -p alera-cli to build and run the Rust host from source.
The shell eagerly starts the terminal host after the local database is available, before the first terminal tab is created. The app passes the current host lifecycle and host scrollback settings when launching alera terminal-host, then sends a configure request whenever terminal settings change so an already-running host updates without requiring an app restart.
The host stays alive only while it is useful. When all app clients disconnect and no PTYs are running, it stops after the configured empty-host delay, which defaults to 30 seconds. When app clients disconnect while PTYs are still running, it keeps those detached sessions alive for the configured detached-session delay, which defaults to one hour; if the app does not reconnect in time, the host terminates the PTYs and writes final checkpoints before exiting. On exit, the host removes its host.json control file so the next app launch cannot attach to stale socket metadata.
Host-side scrollback is bounded separately from the xterm row scrollback used for rendering. Each terminal session keeps a byte-limited chunk buffer, defaulting to 10 MB per session, and checkpoints that buffer about every five seconds plus immediately on detach, exit, configuration trimming, and host shutdown. This keeps detached-session resume useful without allowing terminal output to grow memory without bound.
Terminal output delivery is scoped per connected app client. When a terminal surface is hidden, the app sends setOutputPaused for that terminalSessionId; the host keeps the PTY and byte buffer running but stops streaming output frames to that client. When the surface becomes visible again, the host resumes output delivery and returns the current byte-buffer snapshot so the app can replace the local terminal buffer before live output continues. Exit and error events are still delivered while output is paused.
Alera can show local Codex, Claude Code, GitHub Copilot, Cursor, Antigravity, OpenCode, Pi, and Amp status for terminal workspace tabs when the user enables that agent's hook toggle in Settings. Each toggle is default-off because enabling integrations writes managed hook entries, managed status files, or local runtime overlays. Codex and Claude Code are scoped to Alera-managed runtime homes under the application support directory: Alera mirrors the user's ~/.codex resources/config into the Codex runtime home, writes Codex status hooks to the runtime hooks.json, and injects CODEX_HOME only into Alera terminal launches while the Codex toggle is enabled; Claude Code links or copies the user's Claude config resources into its runtime config directory, writes status hooks to the runtime settings.json, mirrors macOS Keychain credentials into the runtime-scoped Claude Code service, and injects CLAUDE_CONFIG_DIR only into Alera terminal launches while the Claude Code toggle is enabled. OpenCode, Pi, GitHub Copilot, Cursor, and Amp are scoped to Alera-managed per-terminal overlays or wrappers under the application support directory: Alera mirrors the user's OpenCode config, Pi agent directory, Copilot home, or Amp config into an overlay, writes the status plugin/extension/hooks inside that overlay, and injects only the matching terminal environment or wrapper PATH entries for Alera-launched terminals. Cursor uses a generated plugin directory and a cursor-agent --plugin-dir wrapper so Alera does not mutate ~/.cursor/hooks.json. Amp uses an overlay XDG_CONFIG_HOME plus AMP_SETTINGS_FILE wrapper so Alera does not mutate ~/.config/amp/plugins/alera-agent-status.ts. Antigravity remains the only global hook integration and writes managed hook entries into ~/.gemini/config/hooks.json for the agy CLI, plus fail-open scripts under ~/.alera/agent-hooks/. Disabling an agent removes only Alera-managed hook entries for currently global integrations and clears terminal-scoped overlays/wrappers when terminal sessions close; user-authored hooks/files are preserved.
The hook receiver runs in the Flutter app process, not in alera terminal-host. It uses package:shelf with package:shelf_router on top of a dart:io loopback HTTP server bound to 127.0.0.1:0, writes the live port/token to agent-hooks/endpoint.env or agent-hooks/endpoint.cmd under the application support directory, and accepts POST /hook/codex, POST /hook/claude, POST /hook/copilot, POST /hook/cursor, POST /hook/agy, POST /hook/opencode, POST /hook/pi, and POST /hook/amp with X-Alera-Agent-Hook-Token. Terminal launches inject fresh ALERA_AGENT_HOOK_* values plus ALERA_TERMINAL_SESSION_ID, ALERA_WORKSPACE_ID, and ALERA_TAB_ID, after stripping any inherited Alera hook metadata from the shell environment. Receiver events for disabled agents return 204 without updating in-memory status, which prevents stale long-lived PTYs from reviving a disabled integration.
Managed hook scripts, managed status files, overlay plugins, and wrapper plugins read the endpoint file on every invocation/event before posting. This lets a PTY that survived an Alera restart report to the newly opened app receiver once the app is running again. Events emitted while the app is closed are intentionally missed because the receiver is app-local and the terminal host does not buffer hook events. OpenCode, Pi, GitHub Copilot, Cursor, and Amp overlays/wrappers no-op outside Alera because they are exposed only through Alera terminal launch environment. Antigravity remains global for now, but it no-ops outside Alera because the required ALERA_TERMINAL_SESSION_ID, ALERA_WORKSPACE_ID, and ALERA_TAB_ID metadata is absent.
Agent status is in memory only. AgentStatusController stores the latest normalized status by terminalSessionId; after an app restart, each terminal tab starts as unknown until a supported agent emits another hook event. If a child agent inherits an active parent's terminalSessionId, non-terminal child status can update visible details but does not replace or close the parent's agent identity until the parent is done or stale. PTY exit may mark an active in-memory status as inferred done, but silence or elapsed time does not imply completion. The workbench tab strip projects terminal status directly. The sidebar only shows terminal tabs that currently match a supported agent status, rendering them as agent-run rows under their workspace; terminal tabs without supported agent status stay hidden there. Workspace rows show a visual aggregate of their visible agent runs with attention states first (blocked, waiting, working, then done) and most-recent update as the tie-breaker; this aggregate is not persisted to Workspace.status. Antigravity support uses Alera's technical agent id agy; OpenCode uses opencode; Pi uses pi; Amp uses amp.
Alera can also show native desktop notifications for agent status when the user enables Agent status notifications in Settings. Notifications are default-off, require at least one agent hook toggle to be enabled, and are emitted only while the Flutter app process is running. v1 notifies for waiting, blocked, and done; working updates stay in the in-app status projection. Notification titles name the agent and state, while bodies use project/workspace location such as Workspace main in Alera rather than the user's prompt text. Notification payloads include the terminal session, workspace, tab, agent type, and state so selecting the notification can foreground the app, select the matching workspace, activate the matching workspace tab, and request terminal focus. If the app is closed when a hook event occurs, no notification is buffered or replayed on next launch.