Private
Public Access
0
0
Files
manual_slop/docs/guide_workspace_profiles.md
T
ed 3e0c7702ad docs(workspace_profiles+app_controller): fix 3 unverified claims surfaced by re-audit
Honest report: when re-verifying the 4 commits the user asked about
(d82153c0, f973fb27, 5aa19e59, 237f5725), I found 3 docs claims I
made WITHOUT actually reading the code:

1. f973fb27 guide_workspace_profiles.md activation step 4:
   Claimed 'App._apply_panel_states'. This method does not exist.
   Actual: App._apply_workspace_profile(profile) iterates
   profile.panel_states.items() and setattr on App. See
   src/gui_2.py:844-848.

2. 237f5725 guide_app_controller.md Manager objects paragraph:
   Claimed 'App._post_init at src/gui_2.py:3995'. Actual line: 492
   (off by ~3500 lines; the file was refactored during
   startup_speedup and many earlier-line methods were deleted).

3. 237f5725 guide_app_controller.md closing paragraph:
   Claimed 'AppController.__init__ at src/app_controller.py:778-836'.
   Actual range: 778-1212 (the method body is much longer than I
   assumed; the trailing 800-1212 is locks/io_pool/warmup/manager
   wiring). Note added to explain the long range.

Fixes the wrong claims with line numbers I re-verified via AST.

The structural claims (data structure fields, line numbers of
_validate_collection_dim, _init_vector_store, _LiveGuiHandle,
etc.) WERE all verified and are correct.
2026-06-10 20:40:14 -04:00

14 KiB

Workspace Profiles (Docking Layouts)

Top | Architecture | MMA | Simulations


Overview

Workspace Profiles are named, persistent snapshots of the ImGui docking layout, window visibility, and panel state. Users can save their current layout as a profile, switch between profiles on demand, and have profiles auto-apply based on context (active project, active MMA tier, or current task).

This is essential for multi-monitor workflows and for users who frequently switch between "focused work" (one or two panels visible) and "broad context" (all panels visible across multiple monitors).

This guide covers:

  1. Architecture — Where WorkspaceProfile fits in the rendering system
  2. Data Model — The WorkspaceProfile schema
  3. WorkspaceManager — CRUD and activation
  4. Scope Inheritance — Global vs Project profiles
  5. Contextual Auto-Switch — Experimental feature for binding profiles to context
  6. Testing — Test patterns

Architecture

Workspace Profiles live at the boundary between the ImGui render loop and the persistent configuration system. The render loop reads the active profile; the WorkspaceManager updates the active profile and persists changes.

┌────────────────────────────────────────────────┐
│  ImGui Render Loop                              │
│  - Applies docking layout from active profile   │
│  - Reads window visibility / position from      │
│    active profile at startup                    │
│  - Updates active profile when user resizes     │
│    / moves / shows / hides windows              │
└─────────────────┬──────────────────────────────┘
                  │ reads / writes
                  ▼
┌────────────────────────────────────────────────┐
│  WorkspaceManager (src/workspace_manager.py)    │
│  - Profiles dict (active + saved)               │
│  - save_profile(name, scope)                    │
│  - load_profile(name)                           │
│  - delete_profile(name, scope)                  │
│  - bind_to_context(context_id, profile_name)    │
│  - auto_switch(context_id)                      │
└─────────────────┬──────────────────────────────┘
                  │ persists to
                  ▼
┌────────────────────────────────────────────────┐
│  TOML Files                                     │
│  - Global: <user_config>/workspace_profiles.toml│
│  - Project: <project_root>/workspace_profiles.toml│
└────────────────────────────────────────────────┘

Lifecycle:

  • On application startup, the last-active profile is loaded (per-project or global, depending on the loaded project).
  • On user action (resize, move, show, hide), the active profile is updated in memory.
  • On explicit "Save Profile" or application exit, the active profile is persisted to disk.
  • Profile activation (manual or auto-switch) triggers a layout re-apply.

Data Model

WorkspaceProfile (src/models.py)

A snapshot of window state at a moment in time.

class WorkspaceProfile:
    name: str
    ini_content: str                                 # ImGui ini settings string (from SaveIniSettingsToMemory)
    show_windows: Dict[str, bool] = field(default_factory=dict)   # {"mma_dashboard": True, "context_panel": True, ...}
    panel_states: Dict[str, Any] = field(default_factory=dict)    # Per-panel UI state (collapsed flags, scroll positions, etc.)
Field Type Purpose
name str Unique identifier within the scope.
ini_content str ImGui ini settings string from SaveIniSettingsToMemory(). Plain string in TOML (not base64).
show_windows Dict[str, bool] Maps window name to visibility. Windows not in the dict use the default (visible).
panel_states Dict[str, Any] Per-panel UI state (collapsed flags, scroll positions, value overrides). See App._capture_workspace_profile in src/gui_2.py for the captured key set.

Serialization

The ini_content is captured via ImGui.SaveIniSettingsToMemory() as a plain string. On load, ImGui.LoadIniSettingsFromMemory(ini_data) restores the layout. The to_dict/from_dict methods on WorkspaceProfile handle the TOML round-trip via the standard models registry.

Type contract note: ini_content is str, not bytes. The previous base64-encoded-bytes design was removed in the 2026-06-05 live_gui_fragility_fixes_20260605 track. A regression that used b"" as a sentinel caused tomli_w to raise TypeError: Object of type 'bytes' is not TOML serializable. The regression test tests/test_workspace_profile_serialization.py encodes this contract. See the sentinel type contract rule in guide_testing.md and guide_gui_2.md.

The TOML representation:

[profiles.focused_work]
ini_content = "[Window][Project Manager]\nPos=8,8\nSize=400,400\n..."
show_windows = { mma_dashboard = false, context_panel = true, ai_settings = false }
panel_states = { mma_dashboard_collapsed = false, context_panel_scroll_y = 0.0 }

WorkspaceManager

from src.workspace_manager import WorkspaceManager

manager = WorkspaceManager(project_root=Path("/path/to/project"))

Methods

def load_all_profiles(self) -> Dict[str, WorkspaceProfile]:
    """Merges global and project profiles into a single dict."""

def save_profile(self, profile: WorkspaceProfile, scope: str = "project") -> None:
    """Saves a profile to the specified scope (global or project)."""

def delete_profile(self, name: str, scope: str = "project") -> None:
    """Removes a profile from the specified scope."""

def activate_profile(self, name: str) -> None:
    """Applies the profile's layout, visibility, and theme to the live GUI."""

def capture_current_as(self, name: str, scope: str = "project") -> WorkspaceProfile:
    """Captures the current GUI state as a new profile."""

def bind_to_context(self, context_id: str, profile_name: str) -> None:
    """Binds a profile to a context (active tier, project, etc.) for auto-switching."""

def auto_switch(self, context_id: str) -> bool:
    """If a profile is bound to this context, activates it. Returns True if switched."""

Profile Activation

activate_profile(name) performs:

  1. Read the profile from the loaded dict.
  2. Apply ini_content via ImGui.LoadIniSettingsFromMemory(ini_content).
  3. Apply show_windows to the live windows (hide/show as needed).
  4. Apply panel_states to the per-panel settable fields via App._apply_workspace_profile (which iterates profile.panel_states.items() and setattrs on the App — see src/gui_2.py:844-848).

Activation is immediate; the next frame renders the new layout. Theme is NOT part of the profile (themes are managed by the separate theme system; see guide_themes.md).

Capturing Current State

capture_current_as(name, scope):

  1. Read the current ImGui state via ImGui.SaveIniSettingsToMemory() (with the defer-not-catch guard _ini_capture_ready).
  2. Snapshot window visibility into show_windows.
  3. Snapshot per-panel state (collapsed flags, scroll positions, value overrides) into panel_states.
  4. Save to the specified scope.

This is the "Save Profile" action in the GUI. Theme is NOT captured (see activate_profile above).


Scope Inheritance

Scope Path
Global <user_config>/workspace_profiles.toml
Project <project_root>/workspace_profiles.toml

Merge rule: load_all_profiles() returns global first, then project entries override globals of the same name. This is the same pattern as personas and presets.

Example:

  • Global has focused_work with theme "dark".
  • Project has focused_work with theme "nerv".
  • The project version wins for this project. Other projects without the override use the global version.

When to use which:

  • Global: Layouts you want across all projects (e.g., "minimal layout for reading", "presentation layout").
  • Project: Layouts specific to a project's workflow (e.g., "Python project layout with discussion at bottom").

Contextual Auto-Switch (Experimental)

The bind_to_context(context_id, profile_name) and auto_switch(context_id) methods enable automatic profile switching based on context.

Context IDs

Context Format Example
MMA Tier tier:<tier_name> tier:tier3-worker
Project project:<project_name> project:my_app
User-defined <custom> code_review, documentation

Activation

auto_switch(context_id) is called on:

  • MMA tier transitions (e.g., when moving from Tier 1 to Tier 2)
  • Project load/unload
  • User-defined triggers (via the GUI's "Apply Profile to Context" action)

If a profile is bound to the context, it's activated. Otherwise, the current profile remains.

Configuration

[workspace]
auto_switch_profiles = false  # Master toggle; default off

When auto_switch_profiles = false (the default), auto_switch is a no-op. Set to true to enable.

Current Status (as of 2026-06-02)

The bind_to_context and auto_switch methods exist on WorkspaceManager. The MMA's ConductorEngine does not yet call auto_switch on tier transitions. This is planned integration work (see guide_mma.md#workspace-profile-auto-switching-roadmap).


GUI Integration

Profile Selector

A dropdown in the View menu lists all available profiles. Selecting one activates it.

Save Profile

A "Save Current as Profile..." action in the View menu prompts for a name and scope (Global / Project), then calls capture_current_as().

Delete Profile

A "Delete Profile" action removes the active profile (with confirmation).

Bind to Context (Experimental)

A "Bind Profile to Context..." action lets the user specify a context ID and bind the current profile. This is gated behind the auto_switch_profiles config flag.


Configuration

[workspace]
auto_switch_profiles = false
last_active_profile = "default"  # Set automatically on activation

Per-project overrides go in manual_slop.toml under [workspace].


Testing

Unit Tests

  • tests/test_workspace_manager.py — CRUD, scope merging, profile serialization
  • tests/test_workspace_profiles_sim.py — End-to-end via live_gui: save, switch, verify layout

Test Pattern

def test_profile_save_and_load(tmp_path, live_gui):
    # Set up a project
    # ... (live_gui fixture handles this)
    
    # Capture the current state as a profile
    client.capture_current_as("test_profile", scope="project")
    
    # Modify the layout (hide a panel)
    client.hide_panel("mma_dashboard")
    
    # Activate the saved profile
    client.activate_profile("test_profile")
    
    # Verify the panel is visible again
    state = client.get_window_state("mma_dashboard")
    assert state["visible"] == True

Layout Stability

The ini_content is the raw ImGui ini settings string from SaveIniSettingsToMemory(). It's machine-generated text (not human-readable, but parseable). Tests typically use a "save, modify, restore" pattern rather than asserting specific layout details. The panel_states dict, by contrast, is human-readable and testable.


Limitations

  1. Theme is Not Captured: Themes are managed by the separate theme system (see guide_themes.md) and are global. A profile captures docking layout, window visibility, and panel state — but not the active theme. Switching profiles does not change the theme.

  2. No Profile Variants per Monitor: A single profile applies to all connected monitors. Multi-monitor users with different layouts per monitor must use ImGui's native multi-viewport support, not profiles.

  3. No Profile Composition: A profile cannot inherit from another. Duplication is the only way to share settings.

  4. ImGui-Specific: Profiles are tightly coupled to ImGui's docking format. Migrating to a different GUI framework would require a profile format migration.

  5. No Undo for Profile Activation: Activating a profile changes the layout immediately. If the user doesn't like it, they must switch to another profile manually.

  6. Contextual Auto-Switch Is Off by Default: Requires explicit opt-in via auto_switch_profiles = true. Even when on, the MMA integration is not yet implemented.


Future Work

  • MMA IntegrationConductorEngine calls auto_switch on tier transitions. See guide_mma.md#workspace-profile-auto-switching-roadmap.
  • Per-Monitor Layouts — Capture and restore layouts per connected monitor.
  • Profile Composition — Allow a profile to inherit from a base profile.
  • Animated Transitions — Smoothly animate the layout change rather than snapping.
  • Profile Templates — Pre-built profiles for common workflows.
  • Versioned Profiles — Allow multiple versions of a profile, switchable via the GUI.

See guide_mma.md for the broader workspace profile integration roadmap.