--- name: Launcher 单项目解耦 overview: 在保持单一 LanMountainDesktop.Launcher 项目、单一 exe、零部署风险的前提下,按职责域增量重构:目录分层、RunAsync→Pipeline+Phase、UpdateEngine→策略类、App→纯 Avalonia+LauncherOrchestrator;执行过程中由 Agent 自主 Git 提交,每域可编译可测。 todos: - id: phase-a-diagnostics content: Phase A:Startup 诊断 + HostStartupMonitor 独立类 + AOT 启动检测竞态修复 + 测试 status: completed - id: phase-b-directory content: Phase B1:职责域目录迁移(Deployment/Update/Startup/Oobe/Plugins/Infrastructure),零逻辑变更,提交 status: completed - id: phase-b-pipeline content: Phase B2:RunAsync→LaunchPipeline+ILaunchPhase,引入 LauncherOrchestrator,删除 LauncherFlowCoordinator,提交 status: completed - id: phase-b-app-slim content: Phase B3:App.axaml.cs 精简为纯 Avalonia 初始化 + 委托 LauncherOrchestrator,提交 status: completed - id: phase-c-di content: Phase C:LauncherServiceRegistration + 轻量 MS DI,统一 CLI/GUI 装配,提交 status: completed - id: phase-d-update-split content: Phase D:UpdateEngineService→门面+策略类(Verifier/Activator/Rollback 等),提交 status: completed - id: phase-e-guardrails content: Phase E:LauncherArchitectureTests + 文档 + AOT 回归,提交 status: completed isProject: false --- # Launcher 单项目内部解耦改造计划(执行版) ## 0. 硬性约束 | 约束 | 说明 | | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | **单项目** | 仅 `[LanMountainDesktop.Launcher/LanMountainDesktop.Launcher.csproj](LanMountainDesktop.Launcher/LanMountainDesktop.Launcher.csproj)`,不新建 Launcher.* 独立程序集 | | **单 exe** | 仍只发布 `LanMountainDesktop.Launcher.exe`(AOT 单文件) | | **零部署风险** | 不改变安装包目录结构、不引入新进程、不改变 Public IPC / Coordinator IPC 拓扑与契约 | | **增量重构** | 一个职责域一域推进,每步 `dotnet build` + 相关 `dotnet test` 通过后再进下一步 | | **单进程性能** | 模块间仅 in-process 接口调用,不为解耦新增 IPC | | **未来可拆** | 各域暴露 `I`* 接口,将来若需多进程可直接复用契约 | | **Git 自主提交** | Agent 在每个职责域完成且验证通过后 **自动 commit**,无需用户手动提交(见 §8) | 外部共享库 `[LanMountainDesktop.PluginPackaging](LanMountainDesktop.PluginPackaging/)` 保留(Host + Launcher CLI 共用),不属于 Launcher 拆分。 --- ## 1. 验收标准(必须全部满足) ### 1.1 零部署风险 - Inno Setup / CI 产物仍只有:`LanMountainDesktop.Launcher.exe` + `app-{version}/` + `.launcher/` - Host 调用 Launcher 的 CLI 参数、`launch-source`、`apply-update` 路径不变 - Public IPC routes(`lanmountain.launcher.startup-progress`、`loading-state`)与 Coordinator pipe 不变 - VeloPack / 更新 apply 状态机(`.current/.partial/.destroy`)行为不变 ### 1.2 增量可验证 - 每个 Phase 结束:编译绿 + 该域新增/既有测试绿 - 允许「纯移动文件」的 PR 单独提交,行为 diff 为零 ### 1.3 测试友好 - `Startup/`、`Update/`、`Deployment/` 内类型 **无 Avalonia 依赖**,可独立单元测试 - 每个 `ILaunchPhase`、每个 Update 策略类各有对应测试类 - 保留并扩展现有 `[LauncherStartupTimeoutPolicyTests](LanMountainDesktop.Tests/LauncherStartupTimeoutPolicyTests.cs)`、`[LauncherMultiInstancePolicyTests](LanMountainDesktop.Tests/LauncherMultiInstancePolicyTests.cs)` ### 1.4 启动性能 - Pipeline 阶段为同步/异步方法调用链,不引入额外进程或网络 - DI 容器仅在进程入口构建一次;Stage/Phase 实例可复用 Singleton ### 1.5 代码结构目标 | 对象 | 当前(实测) | 目标 | | ----------------------------------- | -------------------------------------------- | --------------------------------------------------- | | `LauncherFlowCoordinator` 全 partial | ~1880 行(859+568+279+…) | **删除**;逻辑迁入 Pipeline + Phases | | `RunAsync()` 等价逻辑 | 跨 partial ~800+ 行 while/阶段混杂 | **≤80 行** 编排入口,细节在各 Phase | | `UpdateEngineService` | ~1622 行 | 门面 **≤200 行** + 6 个策略类各 **≤300 行** | | `App.axaml.cs` | ~258 行(已部分瘦身) | **≤120 行**:纯 Avalonia + 一行委托 `LauncherOrchestrator` | | `LauncherOrchestrator` | 不存在(逻辑在 Coordinator + CompositionRoot 546 行) | **≤250 行**:GUI 入口编排 | | `LauncherCompositionRoot` | ~546 行 | **≤150 行**:仅 DI 构建 + 入口分发 | --- ## 2. 目标架构 ### 2.1 核心类型关系 ```mermaid flowchart TB Program --> EntryRouter App --> LauncherOrchestrator EntryRouter --> LauncherOrchestrator LauncherOrchestrator --> LaunchPipeline LaunchPipeline --> Phase1[CleanupPhase] LaunchPipeline --> Phase2[OobeGatePhase] LaunchPipeline --> Phase3[ApplyUpdatePhase] LaunchPipeline --> Phase4[LaunchHostPhase] LaunchPipeline --> Phase5[MonitorStartupPhase] Phase3 --> IUpdateEngine Phase4 --> IDeploymentLocator Phase5 --> IHostStartupMonitor LauncherCompositionRoot --> ServiceProvider ServiceProvider --> LaunchPipeline ``` **命名约定:** - `**LauncherOrchestrator`**:GUI 生命周期内的唯一编排入口(取代 `LauncherFlowCoordinator` 对外角色) - `**LaunchPipeline**`:按序执行 `ILaunchPhase` 列表 - `**ILaunchPhase**`:原 `ILaunchPipelineStage`;每个 Phase 对应原 `RunAsync` 中一个职责段 ### 2.2 职责域目录(单项目内) ``` LanMountainDesktop.Launcher/ ├── Program.cs # CLI / GUI 路由 ├── App.axaml.cs # 纯 Avalonia(≤120 行) ├── Shell/ │ ├── LauncherOrchestrator.cs # GUI 编排入口 │ ├── LauncherCompositionRoot.cs # DI + Entry 分发 │ ├── LaunchPipeline.cs │ ├── Phases/ # ILaunchPhase 实现 │ │ ├── CleanupDeploymentsPhase.cs │ │ ├── OobeGatePhase.cs │ │ ├── ApplyPendingUpdatePhase.cs │ │ ├── LaunchHostPhase.cs │ │ └── MonitorStartupPhase.cs │ └── EntryHandlers/ # apply-update / air-app-broker / attach ├── Deployment/ ├── Update/ │ ├── IUpdateEngine.cs │ ├── UpdateEngineFacade.cs # 原 UpdateEngineService 门面 │ └── Strategies/ │ ├── PendingUpdateDetector.cs │ ├── UpdatePackageVerifier.cs │ ├── DeploymentActivator.cs │ ├── UpdateSnapshotStore.cs │ ├── RollbackStrategy.cs │ └── IncomingArtifactsCleaner.cs ├── Startup/ ├── Oobe/ ├── Ipc/ ├── AirApp/ ├── Plugins/ ├── Infrastructure/ ├── Models/ └── Views/ ``` ### 2.3 模块依赖规则 - `Deployment/`、`Update/`、`Startup/`:**禁止** `using Avalonia` - `Views/`:**禁止** 引用具体 `UpdateEngineFacade` / `DeploymentLocator`(仅接口或 Orchestrator) - 跨域:**仅通过 `I`* 接口**;Orchestrator/Pipeline 负责装配 ### 2.4 与 Host 边界(不变) | 能力 | Owner | | -------------------------- | ------------------------------ | | OOBE / Splash / 多实例 / 启动检测 | Launcher `Startup/` + `Shell/` | | 更新 apply / rollback | Launcher `Update/` | | 插件市场 / pending | Host + PluginPackaging | | 更新 download | Host → spawn Launcher apply | --- ## 3. 三大核心拆分(用户指定) ### 3.1 拆分 `LauncherFlowCoordinator`:`RunAsync` → Pipeline + Phase **现状:** 逻辑分散在 4 个 partial,等效一个 1800+ 行 God Class;`RunAsync` 内含清理、OOBE、更新、启动、IPC 监听、超时 while-loop、多实例分支。 **目标 API(单项目 `Shell/` 内):** ```csharp internal interface ILaunchPhase { string PhaseId { get; } /// null = 继续下一阶段;非 null = 管道终止并返回结果 Task ExecuteAsync(LaunchContext context, CancellationToken cancellationToken); } internal sealed class LaunchPipeline { public LaunchPipeline(IEnumerable phases) { ... } public Task RunAsync(LaunchContext context, CancellationToken ct); } ``` **Phase 映射(与原 RunAsync 步骤一一对应):** | Phase | 原 RunAsync 段 | 产出 | | ------------------------- | --------------------------------------- | ----------------------------- | | `CleanupDeploymentsPhase` | `CleanupOldDeployments` | 无 UI | | `ExistingHostProbePhase` | 多实例 / Public IPC 探测 | 可短路成功 | | `ApplyPendingUpdatePhase` | `_updateEngine.ApplyPendingUpdateAsync` | 失败仍继续 | | `OobeGatePhase` | migration + OOBE steps | UI via `ILauncherUiPresenter` | | `LaunchHostPhase` | `LaunchHostWithIpcAsync` | Process + plan | | `MonitorStartupPhase` | while-loop + IPC + timeout | 调用 `IHostStartupMonitor` | `**LauncherOrchestrator` 职责:** - 接收 `SplashWindow`、构建 `LaunchContext`(含 reporter、attempt registry、coordinator server) - 调用 `LaunchPipeline.RunAsync` - 管理 Splash/Error 窗口生命周期(委托 `ILauncherUiPresenter`) - **不含** 更新/部署/IPC 细节 **删除清单:** `LauncherFlowCoordinator.cs` 及全部 partial 文件。 --- ### 3.2 拆分 `UpdateEngineService` → 门面 + 策略类 **现状:** ~1622 行单文件,混合检测、验签、解压、激活、快照、回滚、清理。 **目标结构:** ``` Update/ ├── IUpdateEngine.cs # 对外契约(未来多进程可原样抽出) ├── UpdateEngineFacade.cs # 门面,编排策略,≤200 行 └── Strategies/ ├── IUpdateStrategy.cs # 可选:各策略统一接口 ├── PendingUpdateDetector.cs # CheckPendingUpdate ├── UpdatePackageVerifier.cs # manifest + RSA 签名 ├── UpdatePackageExtractor.cs # 解压 / 增量复用 ├── DeploymentActivator.cs # .current / .partial / .destroy ├── UpdateSnapshotStore.cs # snapshots 读写 ├── RollbackStrategy.cs # rollback CLI/GUI └── IncomingArtifactsCleaner.cs # CleanupIncomingArtifacts ``` **门面方法映射:** | 原 `UpdateEngineService` 公开方法 | 委托策略 | | ---------------------------- | ------------------------------------------------------ | | `CheckPendingUpdate()` | `PendingUpdateDetector` | | `ApplyPendingUpdateAsync()` | Detector → Verifier → Extractor → Activator → Snapshot | | `RollbackLatest()` | `RollbackStrategy` | | `CleanupIncomingArtifacts()` | `IncomingArtifactsCleaner` | | `DownloadAsync()`(若有) | 保持或拆 `UpdateDownloader` | **测试:** 每个 Strategy 独立 mock `IDeploymentLocator` / 文件系统,不启 Avalonia。 --- ### 3.3 精简 `App.axaml.cs` → 纯 Avalonia + `LauncherOrchestrator` **现状:** ~258 行,仍含 apply-update、air-app-broker、preview、coordinator attach 等分支。 **目标结构:** ```csharp // App.axaml.cs 目标形态(概念) public override void OnFrameworkInitializationCompleted() { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { var context = LauncherRuntimeContext.Current; var mode = LauncherEntryModeResolver.Resolve(context); _ = LauncherOrchestrator.RunAsync(desktop, context, mode); } base.OnFrameworkInitializationCompleted(); } ``` **从 App 迁出的逻辑 → `Shell/EntryHandlers/`:** | 现 App 分支 | 新 Handler | | ----------------- | -------------------------------------- | | `launch` + splash | `GuiLaunchEntryHandler` → Orchestrator | | `apply-update` | `ApplyUpdateEntryHandler` | | `air-app-broker` | `AirAppBrokerEntryHandler` | | debug preview | `PreviewEntryHandler` | **验收:** `App.axaml.cs` ≤120 行;不含 `new UpdateEngineService` / `new DeploymentLocator` / while-loop。 --- ## 4. 分阶段执行顺序与 Git 提交点 ```mermaid flowchart LR A[Phase A Startup] --> B1[Phase B1 目录迁移] B1 --> B2[Phase B2 Pipeline+Orchestrator] B2 --> B3[Phase B3 App 精简] B3 --> C[Phase C DI] B1 --> D[Phase D Update 策略拆分] C --> E[Phase E 守卫+文档+AOT回归] D --> E ``` ### Phase A:Startup 子系统 + AOT 生产 bug(优先) - 抽出 `Startup/HostStartupMonitor.cs`(从 partial 独立) - 修复 IPC 连接退避、成功判定统一走 `StartupSuccessTracker` - Host 侧 `DesktopVisible` 上报对齐(仅日志/时序,不改 IPC 契约) - 测试 + `**git commit**`: `fix(launcher): extract HostStartupMonitor and harden startup detection` ### Phase B1:目录迁移(零逻辑变更) - 物理移动文件到 `Deployment/`、`Update/`、`Startup/` 等,更新 namespace - `dotnet build` + test - `**git commit**`: `refactor(launcher): reorganize into responsibility folders` ### Phase B2:Pipeline + Phase + LauncherOrchestrator - 实现 `ILaunchPhase`、`LaunchPipeline`、`LauncherOrchestrator` - 逐 Phase 从 Coordinator 迁移逻辑(可先并行运行对照测试) - 删除 `LauncherFlowCoordinator*` - `**git commit**`: `refactor(launcher): replace LauncherFlowCoordinator with LaunchPipeline` ### Phase B3:App.axaml.cs 精简 - EntryHandlers 提取;App 仅 Avalonia + Orchestrator 委托 - `**git commit**`: `refactor(launcher): slim App.axaml.cs to Avalonia shell only` ### Phase C:轻量 DI - `LauncherServiceRegistration.cs` + `Microsoft.Extensions.DependencyInjection` - Program / CliHost / CompositionRoot 统一 `ServiceProvider` - `**git commit**`: `refactor(launcher): add composition-root DI wiring` ### Phase D:UpdateEngine 策略拆分(可与 B2 并行,依赖 B1) - 策略类提取 + `UpdateEngineFacade` - 删除原巨型 `UpdateEngineService.cs` - 每策略测试 - `**git commit**`: `refactor(launcher): split UpdateEngine into strategy classes` ### Phase E:守卫 + 文档 + AOT 回归 - `LauncherArchitectureTests`(命名空间依赖规则) - 更新 `[docs/LAUNCHER.md](docs/LAUNCHER.md)`、`[.trae/specs/launcher-shell-hardening/spec.md](.trae/specs/launcher-shell-hardening/spec.md)` - AOT publish 本地 smoke:launch / apply-update / 多实例 / 启动检测 - `**git commit**`: `docs(launcher): document module boundaries and add architecture tests` --- ## 5. Phase / Service 测试矩阵 | 组件 | 测试文件 | 覆盖点 | | ----------------------- | ---------------------------- | --------------------------------- | | `StartupSuccessTracker` | `StartupSuccessTrackerTests` | Foreground/Tray/Background policy | | `HostStartupMonitor` | `HostStartupMonitorTests` | 超时、IPC 延迟、ShellStatus 轮询 | | `LaunchPipeline` | `LaunchPipelineTests` | Phase 短路、失败传播 | | 各 `ILaunchPhase` | `*PhaseTests` | 单阶段 mock | | `PendingUpdateDetector` | `PendingUpdateDetectorTests` | 无 pending / corrupt | | `DeploymentActivator` | `DeploymentActivatorTests` | 标记文件状态机 | | `RollbackStrategy` | `RollbackStrategyTests` | 快照回退 | | 命名空间规则 | `LauncherArchitectureTests` | 无 Avalonia 泄漏 | --- ## 6. 明确不做 - 不新建 csproj(Launcher.Deployment 等) - 不新建 exe / Windows Service - 不改变 Public IPC / Coordinator IPC 协议 - 不把插件市场安装迁回 Launcher - 不为模块间通信引入新 IPC(仅保留现有 Host↔Launcher 契约) --- ## 7. 风险与缓解 | 风险 | 缓解 | | --------------- | ------------------------------------------------------------------ | | 大规模移动 merge 冲突 | B1 独立 commit,零逻辑变更 | | Pipeline 迁移行为回归 | 先写 Phase 级测试再迁代码;保留 `LMD_LAUNCHER_LEGACY_COORDINATOR=1` 开关一个版本(可选) | | AOT + DI | 显式注册,禁止反射扫描;`PublishAot` CI 步骤验证 | | Update 拆分遗漏路径 | CLI `update *` 与 GUI apply-update 同一 `IUpdateEngine` 门面 | --- ## 8. Git 工作流(Agent 自主提交) **原则:** 每个 Phase 验证通过后立即提交;不累积巨型 uncommitted diff。 **Commit 前检查(每个 commit 必做):** ```bash dotnet build LanMountainDesktop.slnx -c Debug dotnet test LanMountainDesktop.slnx -c Debug --filter "FullyQualifiedName~Launcher" ``` **Commit message 风格(与仓库一致):** ``` refactor(launcher): replace LauncherFlowCoordinator with LaunchPipeline Pipeline + Phase pattern; LauncherOrchestrator becomes GUI entry. No deployment or IPC contract changes. ``` **禁止:** `git push --force`、修改 git config、跳过 hooks(除非 hook 失败需修复后新 commit)。 **建议分支:** `refactor/launcher-internal-modularization`(单 long-lived 分支,按 Phase 连续 commit;或每 Phase 一个 PR 由用户决定 merge 时机)。 --- ## 9. 整体完成定义(Definition of Done) - 无 `LauncherFlowCoordinator` 源文件 - `App.axaml.cs` ≤120 行,仅 Avalonia + Orchestrator 委托 - `UpdateEngineService` 巨型文件已替换为 Facade + Strategies - 职责域目录就位,架构测试通过 - 全量 Launcher 相关测试 + AOT publish smoke 通过 - 安装包结构与 IPC 拓扑与重构前一致 - 每个 Phase 有对应 Git commit,工作区 clean