ADR 0090: Launch profiles and multiple debug startup configurations (VS-style)¶
Status: Accepted · Implemented
Date: 2026-04-23
Related ADRs¶
| ADR | Role |
|---|---|
| 0002 | unified debug layer for human and agent |
| 0028 | user settings — settings.toml, %LocalAppData%\CascadeIDE\, secrets separate |
| 0029 | TOML-first config; holistic settings UI deferred; point UI is canon facade |
| 0093 | embedded launch URL on MFD (extends profiles and launchBrowser) |
Outside ADR¶
| Document | Role |
|---|---|
| MCP-PROTOCOL.md | debug_launch and related tools |
| MsBuildDebugTargetResolver.cs | MSBuild debug target resolver |
Summary¶
- Launch profiles and multiple debug startup configurations (as in VS).
- Storage, MCP, migration from
startup-project.json. - Optional URL on MFD — 0093.
Context¶
Today one solution has at most one explicit startup project: path to .csproj in .cascade-ide/startup-project.json as single StartupProjectRelativePath. F5 and interactive debug_launch resolve one target; MSBuild configuration for debug is effectively Debug in the resolver.
In Visual Studio and SDK projects the familiar model is several named profiles — different projects, configurations (Debug/Release/…), command-line args, working directory, environment variables. Developer switches “current profile” and presses F5 without resetting startup project each time.
Without this Cascade IDE stays closer to “one start per solution”, weak for monorepos, multiple executables in one .sln, and “run API / console / test host” without a file dialog.
Web (ASP.NET Core) is not “later”: typical portal/SPA-host solutions (including product EDW.Portal, scope portal in operational memory) depend on Properties/launchSettings.json: Kestrel URLs, ASPNETCORE_ENVIRONMENT, sometimes launchBrowser, profiles (http / https / IIS). Without mapping these fields into DAP launch (process env + host args if needed) F5 in CIDE will not match familiar dotnet run / VS. Console exe is a subset; web is mandatory v1 horizon per this ADR.
Decision (direction)¶
Introduce a launch profile catalog in the workspace zone, with currently selected profile, and run debug (DAP launch) through the active profile, not sole StartupProjectRelativePath.
Minimal profile model (v1)¶
Per profile, at minimum:
- Id — stable name within solution (display name may match or localize separately).
- Project — path to
.csproj/.fsprojrelative to solution root (same base asBreakpointsFileService.GetWorkspaceRoot(solutionPath)). - MSBuild configuration — e.g.
Debug/Release(string; defaultDebugfor debug). - Program arguments — optional, string list or quoted string rules (refine at implementation).
-
Working directory — optional; if empty — inherit from IdeDapDebugSession (
ResolveLaunchWorkingDirectorylogic exists). -
Process environment variables — for web at minimum merge what profile supplies (often
ASPNETCORE_ENVIRONMENT=Development). Implementation: env in DAPlaunch(extend IdeDapDebugSession if missing), no silent drop.
ASP.NET Core / web (v1, not deferred)¶
Profile must express what is already in launchSettings.json for commandName: Project (Kestrel):
applicationUrl— one or several bindings (;as in SDK template or URL array in TOML); on launch set in environment asdotnetdoes (typicallyASPNETCORE_URLSor host-aligned — refine withdotnet/hosts).launchBrowser— optional; iftrue, open URL after start (CIDE wrapper if added; else mark deferred/v2 explicitly).- Separate profiles like
http/https— multiple named entries in TOML, 1:1 import fromlaunchSettings.profileswhere possible.
IIS Express (commandName: IISExpress) and full VS IIS parity — may be separate phase; ADR minimum: Kestrel + Project — web v1 baseline; IIS — best effort or separate ticket after baseline.
Import: reading web project Properties/launchSettings.json, TOML in .cascade-ide must preserve URL and env semantics so portal-style scenarios do not break vs dotnet run --launch-profile ….
Storage¶
- Canonical format: TOML, not JSON — e.g.
.cascade-ide/launch-profiles.toml(plus schema version key, e.g.version = 1). Same philosophy as user/canonical IDE config (0028, 0029): readability, comments, one style withsettings/workspacebeside repo. - One file per open solution (path to
.sln/ standalone.csproj— as for breakpoints and startup): profile list and active profile (active_profileor equivalent), no second file to desync. - JSON (
Properties/launchSettings.json) remains external de-facto SDK/VS standard — not duplicated as primary canon in.cascade-ide; import/export into TOML model.
Migration: if only startup-project.json exists, on first read build one default profile (name like Default / from project name), set active, write launch-profiles.toml, keep old JSON for strangler until explicit removal.
UI¶
- Visible current profile selector (toolbar or explorer strip / debug banner) — keyboard-first per 0013.
- Commands: manage profiles (add/delete/duplicate) — MFD or modal by volume (0074 overflow policy).
- F5 / “start debugging” uses active profile without
.dlldialog when resolve succeeds (0002 parity).
Agent parity (MCP / IdeCommands) — v1 contract¶
Debug launch contract at IdeCommands.DebugLaunch and MCP-PROTOCOL:
- Command:
debug_launch. - Mode A (explicit target):
workspace_path+target_path(as today, backward compatible). - Mode B (profile):
profile_name(optional) + open workspace/solution context. - if
profile_nameomitted, useactive_profile; - if profile not found — explicit contract error (no silent random startup fallback).
- Additional:
netcoredbg_path,program_args. - If both
target_pathandprofile_name—target_pathwins (explicit agent path stronger than profile).
Human and agent share F5/Launch meaning: “launch by active profile”; direct target_path kept for point scenarios.
.NET alignment¶
- Optional import from
Properties/launchSettings.json(after v1) — less friction fordotnet/VS projects. Canon remains TOML in.cascade-ide; semantic compatibility with standardlaunchSettings, not bitwise file identity. - Optional export to
launchSettings-like form for other tools — not mandatory.
Build resolve¶
- MsBuildDebugTargetResolver: pass configuration from profile (
-p:Configuration=...), not only constantDebug, when active profile is source of truth.
Consequences¶
- Refactor
MainWindowViewModel/StartupProjectStoretoward “set + active” model; old APIs — strangler. - Tests: TOML parse, migration from single
startup-project.json, import weblaunchSettings, F5 scenarios: console + Kestrel with valid URL. - User Guide — product layer, not mandatory ADR volume (see README).
Rejected / deferred alternatives¶
- Only read
launchSettings.jsonwithout own file — rejected: worse for IDE MVP parity (monorepos, paths relative to solution, stable agent contract in.cascade-idebeside breakpoints). - Multiple parallel debug sessions in one IDE — out of scope (one DAP session unless separately fixed).
Rollout status¶
- Storage canon:
.cascade-ide/launch-profiles.toml, migration fromstartup-project.json,MsBuildDebugTargetResolverwith profile configuration, DAPlaunchwithenvand optional cwd,debug_launchwithprofile_nameand explicit contract errors; testsLaunchProfilesStoreTests, docsIdeCommands/MCP-PROTOCOL.md.
Implementation checklist (decision → code)¶
- Introduce
.cascade-ide/launch-profiles.toml(profiles +active_profile) and migration fromstartup-project.json. - Update debug resolve:
MsBuildDebugTargetResolvergetsConfigurationfrom profile. - Update
debug_launchpipeline:profile_name, clear errors (profile_not_found,active_profile_missing,profile_target_unresolved), compatibility withworkspace_path + target_path. - Sync docs: XML-doc
IdeCommands.DebugLaunch,docs/MCP-PROTOCOL.md, ADR index links if needed. - Test plan: console profiles Debug/Release with different args; ASP.NET Core import
applicationUrl/env and launch by profile; negative: missing profile and unresolvable target give explicit errors.