---
phase: 04-release
plan: 02
type: execute
wave: 2
depends_on:
  - 04-01
files_modified:
  - Cargo.toml
  - .cargo/config.toml
  - .github/workflows/release.yml
  - src/cli.rs
  - src/main.rs
autonomous: true
requirements:
  - PLAT-01
  - PLAT-02
  - PLAT-03
  - PLAT-04
tags:
  - rust
  - release
  - ci
  - cargo-dist

# 用户决策覆盖记录（2026-06-13 已授权）
# - D-04: Windows 目标 x86_64-pc-windows-gnu → x86_64-pc-windows-msvc（已在 04-CONTEXT.md L33 更新；理由：ring#1363 + mio#1632 阻断 windows-gnu 从 Linux 交叉编译）
# - D-05: 交叉编译方案 cross 工具链 → apt 安装 gcc-aarch64-linux-gnu + .cargo/config.toml linker（已在 04-CONTEXT.md L37 更新；理由：cargo-dist 不调用 cross，apt + linker 方案更直接；Windows 在 windows-2022 原生构建无需交叉编译；功能等价）
# - D-07: PLAT-04 placeholder 实现方式 todo!() → eprintln! + std::process::exit(1)（CONTEXT.md L43-47 D-07 明确为 placeholder；用户体验更友好且功能等价于规定的 placeholder 行为；2026-06-13 由 revision 阶段补录授权）
context_overrides:
  - decision: D-04
    original: "x86_64-pc-windows-gnu"
    updated: "x86_64-pc-windows-msvc"
    authorized_on: "2026-06-13"
    source: ".planning/phases/04-release/04-CONTEXT.md L33"
  - decision: D-05
    original: "cross 工具链 Docker-based 交叉编译"
    updated: "apt 安装 gcc-aarch64-linux-gnu + .cargo/config.toml linker（Windows 原生构建无需交叉编译）"
    authorized_on: "2026-06-13"
    source: ".planning/phases/04-release/04-CONTEXT.md L37"
  - decision: D-07
    original: "PLAT-04 placeholder 使用 todo!() + 文档注释"
    updated: "PLAT-04 placeholder 使用 eprintln! + std::process::exit(1)（用户体验更友好的明确错误信息）"
    authorized_on: "2026-06-13"
    source: ".planning/phases/04-release/04-CONTEXT.md L43-47（D-07 placeholder 定义）+ 04-RESEARCH.md Pattern 5"
    rationale: "用户体验提升：避免 panic 路径；功能等价（依然不实际执行 setup.exe，依然 exit 1 表示未完成）；与 CONTEXT.md D-07 的 placeholder 性质一致——CONTEXT.md 原文 'todo!()' 仅是初始示意，本 plan 的替代方案产出相同对外契约（CLI 入口存在 + 退出码 1 + 不实际安装）"

must_haves:
  truths:
    - "cargo-dist 0.32.0 已安装到本地（cargo install cargo-dist --version 0.32.0 --locked 完成）"
    - "cargo-dist 配置已写入 Cargo.toml [workspace.metadata.dist]，targets 含三平台（x86_64-unknown-linux-gnu / aarch64-unknown-linux-gnu / x86_64-pc-windows-msvc）"
    - ".github/workflows/release.yml 存在，由 cargo dist init 生成，on.push.tags 含 v*/x.y.z 触发规则"
    - "release.yml 中 build-local-artifacts job 的 matrix 是动态生成（fromJson(needs.plan.outputs.*)），而非静态写死"
    - "release.yml 中 Windows MSVC 构建步骤含 NASM 安装（ring crate 依赖）"
    - ".cargo/config.toml 存在并配置 aarch64-unknown-linux-gnu 的 linker"
    - "cargo dist plan 命令成功输出三平台的构建矩阵（不报 metadata 缺失错误，无 deferred fallback）"
    - "dm-installer install-windows --config x.toml 可解析（PLAT-04 placeholder），不 panic，stderr 含未实现提示并 exit 1"
    - "cargo test 全绿（含 PLAT-04 placeholder 的 CLI 解析测试）"
    - "现有 update-versions.yml 工作流保持不变"
  artifacts:
    - path: "Cargo.toml"
      provides: "[workspace.metadata.dist] 块 + [profile.dist]"
      contains: "[workspace.metadata.dist]"
    - path: ".cargo/config.toml"
      provides: "aarch64 交叉编译 linker 配置"
      contains: "aarch64-linux-gnu-gcc"
    - path: ".github/workflows/release.yml"
      provides: "v* tag 触发的 release CI（含 NASM 安装 + 动态 matrix）"
      contains: "tags:"
    - path: "src/cli.rs"
      provides: "InstallWindows 子命令 + InstallWindowsArgs 结构体（PLAT-04 placeholder）"
      contains: "InstallWindows"
    - path: "src/main.rs"
      provides: "Commands::InstallWindows match 分支（eprintln + exit 1）"
      contains: "Commands::InstallWindows"
  key_links:
    - from: "Cargo.toml [workspace.metadata.dist]"
      to: ".github/workflows/release.yml"
      via: "cargo dist init 读 Cargo.toml 配置生成 release.yml"
      pattern: "cargo-dist-version"
    - from: ".github/workflows/release.yml build-local-artifacts"
      to: "NASM (chocolatey) for Windows"
      via: "choco install nasm"
      pattern: "choco install nasm"
    - from: ".cargo/config.toml aarch64 target"
      to: "Cargo.toml [workspace.metadata.dist.dependencies.apt] gcc-aarch64-linux-gnu"
      via: "CI 中 apt 安装 gcc-aarch64-linux-gnu，cargo 用 linker 设置进行链接"
      pattern: "aarch64-linux-gnu-gcc"
    - from: "src/cli.rs Commands enum"
      to: "src/main.rs match 分支"
      via: "新 InstallWindows variant 必须在 main.rs match 中处理"
      pattern: "Commands::InstallWindows"
    - from: ".github/workflows/release.yml plan job"
      to: "build-local-artifacts matrix"
      via: "fromJson(needs.plan.outputs.val) 动态展开三平台 matrix"
      pattern: "fromJson\\(needs\\.plan\\.outputs"
---

<objective>
**As a** 项目用户（开发者/DBA）, **I want to** 通过 `cargo dist plan` 验证三平台（Linux x86_64 / Linux aarch64 / Windows）的构建矩阵已就绪，并通过 `dm-installer install-windows` 看到 Windows 目标机 placeholder 提示, **so that** 后续 push v0.1.0 tag 时 GitHub Actions 可自动构建三平台二进制，且 PLAT-04 的 CLI 入口已存在等待真实实现。

Purpose:
- 这是 Phase 4 发布流水线的核心切片：cargo-dist init 完成后，整个 release CI 链路从 tag push → 三平台构建 → GitHub Release 已配置完毕（实际执行验证留 Plan 03）
- PLAT-04 placeholder 让 CLI 表面已 cover 四个平台需求；真实 setup.exe /q /XML 集成在后续 spike

Output: 完整的 cargo-dist 配置（Cargo.toml）+ aarch64 linker 配置（.cargo/config.toml）+ 含 NASM 步骤的 release.yml + PLAT-04 CLI placeholder（cli.rs + main.rs）+ 5 个新增测试。

**重要：用户决策覆盖记录（已于 2026-06-13 获得用户授权）**

本 plan 执行的三项决策覆盖均已在 04-CONTEXT.md 的 Decisions 块中正式更新（D-04/D-05）或在本 frontmatter context_overrides 中授权记录（D-07）。此处复述以确保 executor 与审查者知晓：

1. **D-04 覆盖**（04-CONTEXT.md L33 更新；2026-06-13 授权）：
   - 原决策：Windows 目标 `x86_64-pc-windows-gnu`
   - 更新后：Windows 目标 `x86_64-pc-windows-msvc`
   - 理由：RESEARCH.md（HIGH 置信度，ring#1363 + mio#1632）确认 windows-gnu 存在 ring/tokio 双重不兼容，在 Linux runner 上无法交叉编译；改用 msvc + windows-2022 原生构建（russh CI 已验证可行，仅需 NASM）
   - 功能等价：仍为 Windows x86_64 二进制，用户体验无差异

2. **D-05 覆盖**（04-CONTEXT.md L37 更新；2026-06-13 授权）：
   - 原决策：使用 `cross` 工具链 Docker-based 交叉编译
   - 更新后：使用 apt 安装 `gcc-aarch64-linux-gnu` + `.cargo/config.toml` linker 配置进行交叉编译；Windows 目标在 windows-2022 runner 原生构建，无需交叉编译
   - 理由：cargo-dist 不调用 cross，使用 apt 工具链方案更直接；功能等价

3. **D-07 覆盖**（04-CONTEXT.md L43-47 D-07 placeholder 定义；2026-06-13 由 revision 阶段补录授权）：
   - 原决策：PLAT-04 placeholder 使用 `todo!()` + 文档注释
   - 更新后：PLAT-04 placeholder 使用 `eprintln!` + `std::process::exit(1)`
   - 理由：用户体验提升——明确的错误信息（"PLAT-04 spike 待完成"）替代 panic backtrace；功能等价——依然不实际执行 setup.exe，依然以 exit code 1 表示未完成；与 CONTEXT.md D-07 的 placeholder 性质一致（CLI 入口已存在，真实 setup.exe /q /XML 集成留 spike）；CONTEXT.md 原文 `todo!()` 仅是初始示意（CONTEXT.md L45），本 plan 的替代方案产出相同对外契约
   - 来源依据：04-RESEARCH.md Pattern 5（CLI Placeholder）明确推荐 eprintln + exit 而非 todo!()

**executor 必须**：
- 在 Cargo.toml 的 targets 数组中使用 `x86_64-pc-windows-msvc`（不要使用 gnu）
- 不要在 release.yml 中使用 cross 容器；用 apt + linker config 方案
- 在 main.rs 中使用 eprintln + exit(1)，不使用 todo!() 或 unimplemented!()
- 在 04-02-SUMMARY.md 中记录这三项覆盖及其授权时间戳
</objective>

<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/04-release/04-CONTEXT.md
@.planning/phases/04-release/04-RESEARCH.md
@.planning/phases/04-release/04-PATTERNS.md
@.planning/phases/04-release/04-VALIDATION.md
@.planning/phases/04-release/04-01-SUMMARY.md
@Cargo.toml
@src/cli.rs
@src/main.rs
@.github/workflows/update-versions.yml

<interfaces>
<!-- 关键 API 与 CLI 结构，executor 直接照用 -->

src/cli.rs 现有结构（lines 17-30）：
```rust
#[derive(Subcommand)]
pub enum Commands {
    Install(InstallArgs),
    Validate(ValidateArgs),
    Cluster(ClusterArgs),
    Completions { shell: clap_complete::Shell },
}

// 现有 InstallArgs / ValidateArgs / ClusterArgs 用 #[derive(clap::Args)] + #[arg(long)] 模式
```

src/main.rs 现有 match 分支（lines 26-39）：
```rust
match &cli_args.command {
    cli::Commands::Install(args) => install::run(args).await,
    cli::Commands::Validate(args) => config::validate::run(args),
    cli::Commands::Cluster(args) => match &args.command {
        cli::ClusterSubcommand::Deploy(deploy_args) => cluster::run(deploy_args).await,
    },
    cli::Commands::Completions { shell } => { /* ... */ },
}
```

cargo-dist 0.32.0 期望的 Cargo.toml 配置块（PATTERNS.md 已给完整内容）：
```toml
[workspace.metadata.dist]
cargo-dist-version = "0.32.0"
ci = "github"
installers = ["shell", "powershell"]
targets = [
    "x86_64-unknown-linux-gnu",
    "aarch64-unknown-linux-gnu",
    "x86_64-pc-windows-msvc",
]

[workspace.metadata.dist.github-custom-runners]
aarch64-unknown-linux-gnu = "ubuntu-22.04"

[workspace.metadata.dist.dependencies.apt]
gcc-aarch64-linux-gnu = { version = "*", targets = ["aarch64-unknown-linux-gnu"] }

[profile.dist]
inherits = "release"
lto = "thin"
```

`.cargo/config.toml` 完整内容：
```toml
[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"
```

cargo-dist 0.32.0 默认生成的 release.yml 中 build-local-artifacts job 的 matrix 模式（动态展开）：
```yaml
build-local-artifacts:
  needs:
    - plan
  strategy:
    fail-fast: false
    matrix:
      include: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }}
  runs-on: ${{ matrix.runner }}
```
此模式确保 matrix 来自 plan job 的输出（动态），而非在 yml 中静态写死三个 target triple。

clap 子命令命名注意事项：clap derive 自动把 `InstallWindows` enum variant 映射为 kebab-case `install-windows` 子命令；不要使用 `Install_Windows` 或加 `#[command(name=...)]` 改名。
</interfaces>
</context>

<tasks>

<task type="auto" tdd="true">
  <name>Task 1: 添加 PLAT-04 InstallWindows placeholder CLI + main.rs 分支（D-07 授权方案）</name>
  <files>src/cli.rs, src/main.rs</files>
  <read_first>
    - src/cli.rs（完整文件：Commands enum 第 17-30 行、InstallArgs 第 33-54 行、ClusterDeployArgs 第 79-84 行作为参考；tests 模块 第 86-215 行作为测试风格参考）
    - src/main.rs（match 分支结构 第 26-39 行）
    - .planning/phases/04-release/04-PATTERNS.md（"src/cli.rs — 新增 InstallWindows placeholder 子命令" + "src/main.rs — 新增 InstallWindows match 分支" 小节）
    - .planning/phases/04-release/04-RESEARCH.md（Pattern 5：CLI Placeholder；指出 eprintln! + exit(1) 比 todo!() 更友好）
    - .planning/phases/04-release/04-CONTEXT.md（D-07：PLAT-04 placeholder 接受 TOML 配置参数；本 plan frontmatter context_overrides 已记录 D-07 的实现方式调整为 eprintln+exit）
    - 本 plan frontmatter context_overrides 中 D-07 条目（确认 eprintln+exit 方案已正式授权）
  </read_first>
  <behavior>
    - Test 1 (PLAT-04 解析): test_install_windows_placeholder_parses — `["dm-installer", "install-windows"]` 解析为 `Commands::InstallWindows(args)` 且 `args.config.is_none()`
    - Test 2 (PLAT-04 config): test_install_windows_with_config — `["dm-installer", "install-windows", "--config", "/etc/dm.toml"]` 解析后 `args.config == Some(PathBuf::from("/etc/dm.toml"))`
    - Test 3 (PLAT-04 kebab-case): test_install_windows_subcommand_is_kebab_case — 验证 clap 接受 `install-windows` 子命令名（间接由 Test 1 覆盖；可省略）
    - Test 4 (main.rs 行为，可选 integration test): 由于 main.rs 的 eprintln + exit(1) 难以单元测试，本 plan 不强制 main.rs 行为测试；CLI 层解析测试已 cover 用户可见入口
  </behavior>
  <action>
    **D-07 实现方式说明（已授权）**：本 plan frontmatter context_overrides 中 D-07 条目已记录：CONTEXT.md L45 原文示例使用 `todo!()`，本 plan 采用 `eprintln! + std::process::exit(1)` 等价方案（功能等价、对外契约一致、用户体验更友好）。executor 严格按以下 action 实现，不要回退到 todo!()。

    **src/cli.rs 修改：**
    1. 在 Commands enum（第 17-30 行）中，紧接 `Cluster(ClusterArgs),` 之后、`Completions { ... }` 之前，新增一个 variant：
       ```
       /// 在 Windows 目标机上安装达梦（placeholder — PLAT-04 spike 待完成）
       InstallWindows(InstallWindowsArgs),
       ```
    2. 在文件末尾（tests 模块之前，紧接 ClusterDeployArgs struct 之后），新增结构体：
       ```
       /// install-windows 子命令参数（PLAT-04 placeholder）
       #[derive(clap::Args)]
       pub struct InstallWindowsArgs {
           /// TOML 配置文件路径（可选；未提供时使用内置默认参数）
           #[arg(long)]
           pub config: Option<PathBuf>,
       }
       ```
    3. 在 `#[cfg(test)] mod tests` 末尾追加上面 behavior 块的 Test 1 + Test 2 两个测试函数。命名严格匹配 acceptance_criteria。

    **src/main.rs 修改（D-07 授权方案：eprintln + exit(1)）：**
    在 match 块（第 26-39 行）中，紧接 `cli::Commands::Cluster(args) => match &args.command { ... }` 之后、`cli::Commands::Completions { shell } => { ... }` 之前，新增分支：
    ```
    cli::Commands::InstallWindows(_args) => {
        // PLAT-04 spike: setup.exe /q /XML <path> 集成待完成
        // DM Windows 安装包 URL 需从 eco.dameng.com 单独验证
        // 实现方式说明：D-07 授权采用 eprintln + exit(1)，等价于 CONTEXT.md L45 示意的 todo!() placeholder
        // 优势：避免 panic backtrace；用户看到明确的中文错误信息
        eprintln!("[WARN] Windows 目标机安装尚未实现（PLAT-04 spike 待完成）");
        eprintln!("请参考: https://eco.dameng.com/ 手动获取 Windows 安装包");
        std::process::exit(1);
    }
    ```

    **不要**使用 `todo!()` 或 `unimplemented!()`（RESEARCH.md Pattern 5 已明确 eprintln + exit 为推荐方式；D-07 授权方案）。
    **不要**实现真实的 setup.exe 调用逻辑——这是 PLAT-04 placeholder，留 spike。
    **不要**修改既有的 Install/Validate/Cluster/Completions 分支。
  </action>
  <verify>
    <automated>cargo test --lib cli::tests::test_install_windows 2>&1 | tail -20 && cargo build --release 2>&1 | tail -5</automated>
  </verify>
  <acceptance_criteria>
    - 源断言：`grep -n "InstallWindows(InstallWindowsArgs)" src/cli.rs` 返回一行（位于 Commands enum 内）
    - 源断言：`grep -n "pub struct InstallWindowsArgs" src/cli.rs` 返回一行
    - 源断言：`grep -n "cli::Commands::InstallWindows" src/main.rs` 返回一行（位于 match 块中）
    - 源断言：`grep -c "todo!()" src/main.rs` == 0 且 `grep -c "unimplemented!()" src/main.rs` == 0（不使用 panic 路径；D-07 授权方案）
    - 源断言：`grep -n "eprintln!" src/main.rs | grep "PLAT-04"` 返回至少一行
    - 行为断言：`cargo test --lib cli::tests::test_install_windows_placeholder_parses` 退出码 0
    - 行为断言：`cargo test --lib cli::tests::test_install_windows_with_config` 退出码 0
    - 行为断言：`cargo build --release` 退出码 0（编译无 warning 死代码）
    - 行为断言：`cargo run --release -- install-windows 2>&1 | grep "PLAT-04"` 命中（main.rs 分支真实执行）
    - 行为断言：`cargo run --release -- install-windows; echo "exit=$?"` 输出含 `exit=1`
  </acceptance_criteria>
  <done>
    src/cli.rs 含 InstallWindows variant + InstallWindowsArgs；src/main.rs 含对应 match 分支（按 D-07 授权方案使用 eprintln + exit 1）；2 个新 CLI 解析测试 pass；cargo build --release 通过；运行 `cargo run -- install-windows` 输出未实现提示并退出码 1。
  </done>
</task>

<task type="auto">
  <name>Task 2: 强制安装 cargo-dist + 运行 cargo dist init/plan + 写入 cargo-dist 配置 + 创建 .cargo/config.toml + 补 NASM 步骤到生成的 release.yml</name>
  <files>Cargo.toml, .cargo/config.toml, .github/workflows/release.yml</files>
  <read_first>
    - Cargo.toml（含 Plan 01 添加的 description/license/repository；本任务在文件末尾追加 dist 配置）
    - .github/workflows/update-versions.yml（确认现有 CI 工作流不被破坏；release.yml 新建独立文件）
    - .planning/phases/04-release/04-PATTERNS.md（Cargo.toml + .cargo/config.toml + release.yml 三个文件的完整模板）
    - .planning/phases/04-release/04-RESEARCH.md（Pattern 1 cargo-dist 配置、Pattern 2 aarch64 linker、Pattern 3 NASM 步骤、Pitfall 5 NASM 必须装）
    - .planning/phases/04-release/04-CONTEXT.md（D-04/D-05 已于 2026-06-13 更新；D-04 = x86_64-pc-windows-msvc；D-05 = apt + linker 方案；本任务严格按更新后内容执行）
  </read_first>
  <action>
    **执行前提（已获用户授权的 CONTEXT 更新）：**
    - D-04（更新后）：Windows 目标使用 `x86_64-pc-windows-msvc`（不要使用 windows-gnu）
    - D-05（更新后）：使用 apt 安装 `gcc-aarch64-linux-gnu` + `.cargo/config.toml` linker 配置（不要使用 cross 容器）
    这两项已于 2026-06-13 在 04-CONTEXT.md 中正式更新，本 plan frontmatter 的 context_overrides 字段亦明确记录。

    **步骤 1：强制安装 cargo-dist（不允许跳过）**
    无论本地是否已安装 cargo-dist，先运行下面的检测+安装序列：
    ```bash
    if ! command -v cargo-dist >/dev/null 2>&1 && ! cargo dist --version >/dev/null 2>&1; then
        cargo install cargo-dist --version 0.32.0 --locked
    fi
    cargo dist --version  # 必须输出 0.32.0；否则 abort
    ```
    若安装失败（网络不可达 / crates.io 不通），任务**立即终止**并报错给用户——不允许进入"手动写入兜底"绕过 verify。Plan 02 的存在前提就是 cargo-dist 可运行；如果环境约束不满足，应升级为 blocker 而非 silently degrade。

    **步骤 2：运行 cargo dist init（首选方式）**
    安装成功后运行：
    ```bash
    cargo dist init --yes --hosting=github --installer=shell --installer=powershell --ci=github
    ```
    若 `--yes` 因交互式问题失败，改用手动键入或编辑生成文件。

    **步骤 3：手动校正/确保 Cargo.toml 配置最终状态**
    无论 cargo dist init 是否成功，最终 Cargo.toml 末尾（[dev-dependencies] 块之后）必须有以下三个块（**注意：targets 列表中 Windows 目标使用 msvc，符合更新后的 D-04**）：

    ```toml
    [workspace.metadata.dist]
    cargo-dist-version = "0.32.0"
    ci = "github"
    installers = ["shell", "powershell"]
    targets = [
        "x86_64-unknown-linux-gnu",
        "aarch64-unknown-linux-gnu",
        "x86_64-pc-windows-msvc",
    ]

    [workspace.metadata.dist.github-custom-runners]
    aarch64-unknown-linux-gnu = "ubuntu-22.04"

    [workspace.metadata.dist.dependencies.apt]
    gcc-aarch64-linux-gnu = { version = "*", targets = ["aarch64-unknown-linux-gnu"] }

    [profile.dist]
    inherits = "release"
    lto = "thin"
    ```

    若 cargo dist init 生成了不同结构（例如分到独立 dist-workspace.toml 或 targets 列表含 windows-gnu），手动调整为上述形式。

    **步骤 4：创建 .cargo/config.toml**
    在仓库根目录新建 `.cargo/` 目录，并创建 `.cargo/config.toml`，内容严格如下（符合更新后的 D-05：apt + linker 方案）：
    ```toml
    # aarch64 交叉编译链接器配置（D-05 更新后方案：apt + linker）
    # 需要 CI 安装 apt 包：gcc-aarch64-linux-gnu
    # 对应 [workspace.metadata.dist.dependencies.apt] 中的 gcc-aarch64-linux-gnu
    [target.aarch64-unknown-linux-gnu]
    linker = "aarch64-linux-gnu-gcc"
    ```

    **步骤 5：生成 .github/workflows/release.yml + 追加 NASM 步骤**
    优先方案：若 cargo dist init 已成功生成 release.yml，使用其输出；不要修改 plan/host/announce 等 job 结构。
    兜底方案：若 release.yml 未被自动生成，从 cargo-dist 0.32.0 官方模板手工创建；最小必需结构：
    - `name: Release`
    - `on.push.tags: ['**[0-9]+.[0-9]+.[0-9]+*']`（D-02 锁定）
    - 至少含 `plan` job（runs-on: ubuntu-22.04，输出 plan-dist-manifest.json）
    - 至少含 `build-local-artifacts` job（runs-on: 由 matrix.runner 决定，matrix 从 plan 输出读取）
    - **必须**保留 cargo-dist 默认的**动态 matrix** 模式（不允许手写静态 matrix）：
      ```yaml
      build-local-artifacts:
        needs:
          - plan
        strategy:
          fail-fast: false
          matrix:
            include: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }}
        runs-on: ${{ matrix.runner }}
      ```
      理由：plan job 已读取 Cargo.toml [workspace.metadata.dist].targets 并生成 artifacts_matrix；静态写死三个 triple 会与 Cargo.toml 配置漂移，未来加 target 必须同时改两处。
    - **必须**在 build-local-artifacts job 的 steps 中、在 cargo dist build 步骤之前插入 NASM 安装步骤：
      ```yaml
      - name: Install NASM (Windows)
        if: runner.os == 'Windows'
        run: choco install nasm -y && echo "C:\Program Files\NASM" >> $env:GITHUB_PATH
        shell: pwsh
      ```
    - 若 cargo dist init 生成 release.yml 但未含 NASM 步骤，本任务**必须**手动追加此 step（位置：build-local-artifacts job 的 steps 列表中，在 dist build 步骤之前）
    - 若 cargo dist init 生成的 release.yml 中 matrix 已是 `fromJson(needs.plan.outputs.*)` 形式，保留原样；不要改成静态

    **步骤 6：强制运行 cargo dist plan 验证（不允许 fallback）**
    执行：
    ```bash
    cargo dist plan 2>&1 | tail -30
    ```
    要求：
    - 退出码 == 0
    - 输出至少包含三个 target triple：`x86_64-unknown-linux-gnu`、`aarch64-unknown-linux-gnu`、`x86_64-pc-windows-msvc`
    - 不报 "Missing required Cargo.toml fields" / "unsupported target" 等错误

    若 cargo dist plan 失败：**本任务未完成**——修复配置后重试，不允许把验证延期到 Plan 03 或 CI。

    **不要修改** `.github/workflows/update-versions.yml`。
    **不要触碰** [package].version 字段（保持 "0.1.0"，D-09 锁定）。
  </action>
  <verify>
    <automated>cargo build --release 2>&1 | tail -5 && cargo dist --version && cargo dist plan 2>&1 | tail -20</automated>
  </verify>
  <acceptance_criteria>
    - 源断言：`grep -c '^\[workspace.metadata.dist\]' Cargo.toml` == 1
    - 源断言：`grep -c 'cargo-dist-version = "0.32.0"' Cargo.toml` == 1
    - 源断言：`grep -c '"x86_64-pc-windows-msvc"' Cargo.toml` == 1
    - 源断言：`grep -c '"x86_64-pc-windows-gnu"' Cargo.toml` == 0（D-04 更新后：使用 msvc 而非 gnu）
    - 源断言：`grep -c '"aarch64-unknown-linux-gnu"' Cargo.toml` ≥ 2（targets 数组 + dependencies.apt 中 targets）
    - 源断言：`grep -c '^\[profile.dist\]' Cargo.toml` == 1
    - 源断言：`test -f .cargo/config.toml && grep -c 'aarch64-linux-gnu-gcc' .cargo/config.toml` == 1
    - 源断言：`test -f .github/workflows/release.yml`（exit code 0）
    - 源断言：`grep -c 'tags:' .github/workflows/release.yml` ≥ 1（含 tag 触发）
    - 源断言：`grep -ic 'nasm' .github/workflows/release.yml` ≥ 1（含 NASM 安装）
    - 源断言（新增——动态 matrix）：`grep -c 'fromJson(needs.plan.outputs' .github/workflows/release.yml` ≥ 1（matrix 来自 plan job 输出，非静态写死）
    - 源断言：`test -f .github/workflows/update-versions.yml`（未被删除）
    - 行为断言：`cargo dist --version` 退出码 0 且输出含 "0.32"（cargo-dist 已安装）
    - 行为断言：`cargo build --release` 退出码 0（Cargo.toml dist 块不影响普通构建）
    - 行为断言：`cargo test --lib` 退出码 0（既有测试不受 Cargo.toml 修改影响）
    - 行为断言（强制，无 fallback）：`cargo dist plan 2>&1` 退出码 0 且输出含 `x86_64-pc-windows-msvc`、`aarch64-unknown-linux-gnu`、`x86_64-unknown-linux-gnu` 三个 triple
  </acceptance_criteria>
  <done>
    cargo-dist 0.32.0 已安装；Cargo.toml 含完整 [workspace.metadata.dist] 配置（targets 三平台、aarch64 apt 依赖、profile.dist）；.cargo/config.toml 含 aarch64 linker；.github/workflows/release.yml 存在并含 tag 触发 + NASM 步骤 + 动态 matrix（fromJson(needs.plan.outputs.*)）；cargo build --release 通过；cargo test 全绿；cargo dist plan 成功输出三平台构建矩阵。
  </done>
</task>

</tasks>

<threat_model>
## Trust Boundaries

| Boundary | Description |
|----------|-------------|
| GitHub Actions runner → crates.io | CI 中 cargo 下载依赖；用 Cargo.lock 锁版本 |
| 用户 → GitHub Releases asset | 通过 cargo-dist 生成的 installer.sh 下载预编译二进制；asset 完整性由 GitHub Releases 的 attestation + SHA256 manifest 保护 |
| CI runner → 构建产物 | release.yml 写入 GitHub Release；用 GITHUB_TOKEN 权限边界限定 |

## STRIDE Threat Register

| Threat ID | Category | Component | Disposition | Mitigation Plan |
|-----------|----------|-----------|-------------|-----------------|
| T-04-06 | Tampering | release.yml 中 NASM 通过 chocolatey 安装（runner 环境注入风险） | accept | choco 官方包；windows-2022 runner 由 GitHub 维护；NASM 是固定版本依赖，被替换风险极低 |
| T-04-07 | DenialOfService | cargo-dist 0.32.0 自身的供应链；cargo install 拉取 | accept | cargo-dist 已在 CLAUDE.md 推荐且 axodotdev 官方维护，多个知名项目（uv, Rye）使用 |
| T-04-08 | InformationDisclosure | release.yml 中可能 echo GITHUB_TOKEN 到 log | mitigate | 使用 cargo-dist 生成的标准 release.yml；不要手动添加 echo $GITHUB_TOKEN；不要修改 dist plan/build/host 等 jobs |
| T-04-09 | Spoofing | x86_64-pc-windows-gnu 目标（D-04 原文）会因 ring/mio 不兼容产出无效或非预期二进制 | mitigate | D-04 已更新（2026-06-13）：切换到 x86_64-pc-windows-msvc，在 windows-2022 runner 上原生构建，已被 russh CI 验证可行 |
| T-04-SC | Tampering | cargo-dist 0.32.0 安装（仅开发者机器 + CI） | accept | cargo-dist 在 CLAUDE.md 明确推荐；本 plan 不向 [dependencies] 添加运行时 crate，dist 是构建工具不会被打包进二进制 |
</threat_model>

<verification>
- `grep -c '\[workspace.metadata.dist\]' Cargo.toml` == 1
- `grep -c 'targets = \[' Cargo.toml` == 1（dist 块内的 targets）
- 三平台 target triple 在 Cargo.toml 各出现一次：`x86_64-unknown-linux-gnu`、`aarch64-unknown-linux-gnu`、`x86_64-pc-windows-msvc`
- `test -f .cargo/config.toml` 退出码 0
- `test -f .github/workflows/release.yml` 退出码 0
- `grep -c 'fromJson(needs.plan.outputs' .github/workflows/release.yml` ≥ 1（动态 matrix）
- `cargo build --release` 退出码 0
- `cargo test --lib` 退出码 0（含新增 PLAT-04 CLI 解析测试）
- `cargo run -- install-windows; echo "code=$?"` stderr 含 "PLAT-04" 且 stdout 含 "code=1"
- `git diff .github/workflows/update-versions.yml` 输出为空（未被破坏）
- `cargo dist --version` 输出含 "0.32"
- `cargo dist plan` 退出码 0 且输出含三个 target triple
</verification>

<success_criteria>
- 四个 PLAT 需求 CLI/CI 层全部 cover：PLAT-01 (linux-gnu)、PLAT-02 (aarch64-linux-gnu)、PLAT-03 (windows-msvc 控制机)、PLAT-04 (install-windows 子命令存在并可解析)
- cargo-dist 0.32.0 已安装并验证可用（无 fallback / deferred）
- cargo-dist 配置完整且 valid（手动方式或 dist init 生成）
- cargo dist plan 在本地成功执行，输出含三平台构建矩阵
- release.yml 包含正确的 v* tag 触发条件 + Windows NASM 安装步骤 + 动态 matrix（fromJson）
- 现有 update-versions.yml 未被破坏
- cargo test 全绿；cargo build --release 通过；cargo run -- install-windows 显示 PLAT-04 placeholder 提示并退出码 1
- SUMMARY 中明确记录 D-04 + D-05 + D-07 三项 CONTEXT 更新已获用户授权（2026-06-13）并按更新后内容执行
</success_criteria>

<output>
Create `.planning/phases/04-release/04-02-SUMMARY.md` when done. SUMMARY 应记录：
- D-04 更新（windows-msvc 替代 windows-gnu）已于 2026-06-13 获用户授权（引用 04-CONTEXT.md L33）
- D-05 更新（apt + linker 替代 cross 容器）已于 2026-06-13 获用户授权（引用 04-CONTEXT.md L37）
- D-07 实现方式（eprintln + exit(1) 替代 todo!()）已于 2026-06-13 由 revision 阶段补录授权（引用本 plan frontmatter context_overrides D-07 条目）
- cargo-dist 实际安装方式（命令 + 版本输出）
- cargo dist init 是命令生成还是部分手动校正
- release.yml 中 NASM 步骤的精确位置（在哪个 job 的 steps 中）
- release.yml 中 build-local-artifacts matrix 的实际形式（验证是 fromJson 动态展开，非静态写死）
- cargo dist plan 的实际输出片段（含三个 triple 命中证据）
- PLAT-04 placeholder 的 stderr 文案
- 给 Plan 03 的接口：tag push 应触发的 workflow 名、aarch64 linker 配置是否在 dist plan 输出中可见
</output>
</content>
