Files
headquarter/openspec/changes/archive/2026-05-25-add-config-profiles/design.md
T
Alex Blank ab79080f0b refactor: consolidate loading/error states and extract instance actions hook
Frontend:
- Create reusable DataStates components (LoadingState, ErrorState, EmptyState)
- Refactor 12 pages to use shared state components instead of inline JSX
- Extract useInstanceActions hook to eliminate session action duplication
- Update dashboard and sessions pages to use shared hook

OpenSpec:
- Archive completed mobile-app-usability change (44/44 tasks)
- Archive completed add-config-profiles change (15/15 tasks)

Quality: TypeScript check passes, production build succeeds
2026-05-25 22:50:18 +02:00

110 lines
6.7 KiB
Markdown

## Context
The current system has tool configs for tool-type runtime fields and config folders for user-owned mounted files. Tool instance start currently discovers applicable tool configs and active config folders automatically, writes files/env vars into the instance directory, modifies Docker Compose, and starts the container. This creates useful building blocks but not a single user-facing launch profile that can be selected, composed, previewed, scoped to project/tool, or disabled for a launch.
The target model is a config profile: a user-owned, selectable launch configuration that owns UTF-8 text files, mount roots, plain environment variables, and runtime hints. A profile can include other profiles in an ordered graph. Launch chooses one profile or `None`; included profiles provide stacking without making the start form multi-select.
## Goals / Non-Goals
**Goals:**
- Provide one primary config abstraction for reusable launch setup.
- Allow one selected profile, or no profile, at session start.
- Allow ordered profile composition through includes with loop detection.
- Support portable, project-specific, tool-specific, and project+tool profiles through optional `project_id` and `tool_type_id` references.
- Resolve compatible defaults by specificity, falling back to the first created compatible profile.
- Store the selected profile on the instance so restart behavior is predictable.
- Replace legacy active config folder auto-mounting; compatibility with old config folder behavior is not required.
- Provide a settings editor for profile env vars, mount roots, text files, include order, defaults, and basic runtime hints.
**Non-Goals:**
- Secret storage or masking for env vars/files in v1.
- Binary file upload/editing in v1.
- Selecting multiple profiles directly at launch.
- Preserving legacy config folder semantics.
- Cross-user shared profiles.
## Decisions
### Config profiles own files directly
Profiles will own their file content instead of referencing the existing `ConfigFolder` model. Reuse comes from profile composition: a profile such as `OpenCode Kimi` can include `Git identity` and `Shell defaults`.
Alternative considered: keep `ConfigFolder` as a reusable file-bundle primitive. This adds another concept (`profile -> folder -> files`) and makes the UI harder to explain. Direct file ownership keeps the model centered on one abstraction.
### Scope is derived from optional project/tool references
Profiles do not need a separate scope enum. Scope is inferred from nullable references:
- portable: no project and no tool
- tool: tool only
- project: project only
- project+tool: both project and tool
This avoids storing redundant state and naturally supports `Headquarter OpenCode` profiles.
### Launch selects one profile, composition happens inside profiles
The start UI will expose a single config profile selector with `None` as an option. Profiles may include other profiles in ordered composition, so advanced stacking happens in the profile editor rather than the launch form.
Alternative considered: allow selecting multiple profiles at launch. This is more flexible but makes start behavior harder to understand and raises ordering questions for every launch.
### Includes use an ordered graph with cycle detection
Profile includes will be represented as ordered edges. Resolution processes included profiles in position order, then applies the selected profile itself. Later layers override earlier layers. Cycles must be rejected when saving include relationships and guarded against again during launch resolution.
### Mounts use target roots with relative UTF-8 text files
Each profile mount has a target path, mode (`ro` or `rw`), and a map/list of relative file paths to UTF-8 text content. The resolver stages each resolved mount into the instance directory and adds Docker bind mounts to the compose file.
Alternative considered: store absolute container paths on every file. Mount roots better match Docker volume behavior, simplify editing, and make merge/conflict rules clearer.
### Deterministic override rules
Resolution order is:
1. tool defaults already provided by the tool type/compose template
2. included profiles in configured order, recursively resolved
3. selected profile itself
4. start-time/runtime overrides if a future workflow exposes them
For env vars and runtime hints, later values replace earlier values. For mounts with the same target path, file maps are merged and later relative file paths win. For mount mode conflicts on the same target path, the later layer wins.
### Defaults are selected by specificity
The default selector will prefer explicit defaults by specificity:
1. project+tool
2. project
3. tool
4. global/user
5. first created compatible profile
6. none
If no explicit default exists, the first created compatible profile becomes the default launch selection. Users can still choose `None` to disable all profile config for a launch.
### Instance stores selected profile
Tool instances store the selected config profile ID, or null when `None` was selected. Restart uses the stored selection to avoid changing behavior when the user's default profile changes later.
## Risks / Trade-offs
- Existing config folder users may lose automatic mounts because legacy compatibility is explicitly out of scope. Mitigation: this is an accepted breaking change and can be handled by manually recreating profiles.
- Direct profile-owned files may duplicate content across profiles. Mitigation: include relationships provide reusable file-only profiles without adding another model.
- Plain env vars can contain secrets. Mitigation: label v1 env vars as non-secret/plain text and defer secret storage to a later change.
- Profile graph resolution can become complex. Mitigation: keep launch selection single-profile, use ordered includes, test cycle detection and override ordering thoroughly.
- Mount conflicts may surprise users. Mitigation: provide a resolved preview showing final env vars, mount targets, and overridden files before launch or in profile details.
## Migration Plan
- Add config profile tables and profile selection fields without preserving config folder behavior.
- Stop applying all active config folders during instance start.
- Apply selected config profile resolution during instance start/restart.
- Existing tool config runtime fields can remain available until replaced by profile runtime hints, but reusable file/env launch behavior moves to config profiles.
## Open Questions
- Should runtime hints initially include all existing tool config runtime fields, or only env vars/files/mounts with start command and working directory?
- Should resolved config preview be required before first implementation, or can it be shipped after CRUD/start selection?