Files
galaxy/specs/APP-3575/TECH.md
T

7.4 KiB

Finalize TOML Schema for Tab Configs — Tech Spec

Linear: APP-3575

Problem

The recursive [layout] / [[layout.panes]] TOML format becomes unreadable at depth 3+. We need to replace it with a flat [[panes]] array where nodes reference children by string ID, add tab color support, and ship a bundled Oz skill for generating configs.

Relevant code

  • app/src/tab_configs/tab_config.rsTabConfig, TabConfigPaneNode, render_tab_config, resolve_pane_tree
  • app/src/tab_configs/tab_config_tests.rs — existing tests for parsing and rendering
  • app/src/tab_configs/mod.rs — public exports
  • app/src/workspace/view.rs:4702-4780open_tab_config_with_params, open_tab_config, create_and_open_new_tab_config
  • app/src/launch_configs/launch_config.rsPaneTemplateType, SplitDirection, CommandTemplate (the output types tab configs produce)
  • app/src/pane_group/mod.rs:1181-1333pane_tree_from_template (consumes PaneTemplateType to create panes)
  • app/src/user_config/native.rs:228-232load_tab_configs
  • app/src/user_config/util.rs:167-180parse_tab_config_dir_entry (TOML parsing)
  • app/resources/tab_configs/new_tab_config_template.toml — default template
  • resources/bundled/skills/tab-configs/SKILL.md — bundled Oz skill
  • app/src/ai/skills/skill_manager.rs — bundled skill loading from resources/bundled/skills/

Current state

Tab configs are TOML files in ~/.warp/tab_configs/. They previously used a recursive TabConfigLayout struct with nested panes: Vec<TabConfigLayout> for child splits, which became impractical at depth 3+.

There is no color field on TabConfig — launch configs support color via TabTemplate.color but tab configs don't.

There is no bundled skill for generating tab configs.

Proposed changes

1. New struct: TabConfigPaneNode

A flat node in the [[panes]] array. Distinguished as split vs leaf by the presence of split + children. No type field — all leaves are terminal panes (a type discriminator will be added when non-terminal pane types are supported).

Fields: id, pane_type (serde-renamed to type), split, children, is_focused, cwd, commands, worktree_name_autogenerated.

worktree_name_autogenerated (bool, default false): when true, the param-fill UI auto-generates the worktree branch name instead of showing a free-text input. The app infers "is this a worktree config?" by scanning commands for git worktree — no separate worktree flag is needed.

1b. New enum: TabConfigPaneType

Terminal / Agent / Cloud — maps to PaneMode at render time.

1c. New enum: PaneMode (in launch_config.rs)

Terminal (default) / Agent / Cloud — added as a field on PaneTemplateType::PaneTemplate with #[serde(default)] so launch config deserialization is unaffected.

2. Updated TabConfig

Add two fields:

  • color: Option<AnsiColorIdentifier> — tab color, applied after tab creation (matching the launch config pattern in open_launch_config_window).
  • panes: Vec<TabConfigPaneNode> — flat pane list. The first entry is the root of the pane tree.

Remove the old layout: TabConfigLayout field and the TabConfigLayout struct entirely. The legacy [layout] format is no longer supported.

3. resolve_pane_tree function

Converts the flat panes list into a PaneTemplateType tree:

  1. Index all nodes by ID. Reject duplicate IDs.
  2. Root = first entry.
  3. Recursively resolve: split nodes look up children by ID; leaf nodes produce PaneTemplate { cwd, commands, is_focused }.
  4. Focus handling: if any pane has explicit is_focused = true, use it. Otherwise auto-focus the first leaf (matching legacy render_layout behavior).
  5. On error (missing refs, <2 children), return Err and the caller falls back to a single empty terminal.

4. Updated render_tab_config

Always calls resolve_pane_tree. On error (empty panes, missing refs, missing type on a leaf, etc.), falls back to a single empty terminal pane with a logged warning. Leaf resolution maps TabConfigPaneType to PaneMode and sets it on the produced PaneTemplate.

5. Updated open_tab_config_with_params in workspace view

After add_tab_with_pane_layout, apply tab_config.color to the new tab's selected_color — the same pattern launch configs use at workspace/view.rs:2386.

6. Updated template

app/resources/tab_configs/new_tab_config_template.toml uses the new flat format. Active (uncommented) content is a single-pane terminal with commands = []. Commented examples show two-pane split, 2x2 grid, and parameterized configs.

7. Bundled Oz skill

resources/bundled/skills/tab-configs/SKILL.md contains the full schema reference, examples, validation rules, and common natural-language-to-layout mappings. The build script (script/prepare_bundled_resources) copies it into the app bundle automatically.

End-to-end flow

  1. User creates/edits a .toml file in ~/.warp/tab_configs/.
  2. Filesystem watcher in WarpConfig detects the change and calls load_tab_configs.
  3. parse_tab_config_dir_entry calls toml::from_str::<TabConfig>(). The panes field deserializes from [[panes]] entries.
  4. User selects the config from the + menu → open_tab_config is called.
  5. If params exist, the param-fill modal opens. Otherwise open_tab_config_with_params is called directly.
  6. render_tab_config builds param contexts, calls resolve_pane_tree, returns (Option<String>, PaneTemplateType).
  7. Workspace calls add_tab_with_pane_layout(PanesLayout::Template(pane_template), ...).
  8. PaneGroup::pane_tree_from_template recursively creates panes from the PaneTemplateType tree. For PaneMode::Agent, it creates a terminal session then enters agent mode. For PaneMode::Cloud, it creates an ambient agent pane via create_ambient_agent_terminal.
  9. If tab_config.color is set, the tab's selected_color is updated.

Risks and mitigations

  • Flat pane list validation: Invalid configs (missing refs, cycles) are caught at render time with descriptive error messages logged and a fallback to a single terminal pane. The user's tab config file is never modified.
  • Skill generating invalid TOML: The skill embeds validation rules and examples. The schema is simple enough (terminal-only, no type field) that generation errors are unlikely.

Testing and validation

  • Unit tests in tab_config_tests.rs:
    • Parse single flat pane.
    • Parse and render flat two-pane split.
    • Parse and render flat 2x2 grid.
    • Explicit is_focused honored.
    • Auto-focus first leaf when no explicit focus.
    • Invalid flat pane tree (missing child ref) returns error.
    • Duplicate IDs rejected.
    • Tab color deserialized correctly.
  • Manual: build and run Warp, create tab configs, verify pane layouts open correctly.
  • Manual: invoke the tab-configs skill, confirm it generates a working config.

Follow-ups

  • Add non-terminal pane types (notebook, code, settings, etc.) with a type field discriminator.
  • Consider cycle detection in validation (currently not implemented — the recursive resolver will stack overflow on cycles, but configs are small enough that this is not a practical concern).
  • Consider orphan pane warnings (panes not referenced by any children and not the root).
  • Investigate whether the Oz skill should auto-open the created config in the user's editor.