---
date: 2025-10-09T16:58:58+08:00
researcher: Claude (Sonnet 4.5)
git_commit: fe94aea0cd5a5a912aed548e2c32cd975abe381b
branch: makefile
repository: fwauto
topic: "Makefile 整合至 FWAuto 的現有架構分析"
tags: [research, codebase, makefile, build-node, flash-node, init-node, firmware-state]
status: complete
last_updated: 2025-10-09
last_updated_by: Claude (Sonnet 4.5)
---

# Research: Makefile 整合至 FWAuto 的現有架構分析

**Date**: 2025-10-09T16:58:58+08:00
**Researcher**: Claude (Sonnet 4.5)
**Git Commit**: fe94aea0cd5a5a912aed548e2c32cd975abe381b
**Branch**: makefile
**Repository**: fwauto

## 📋 Research 文檔規範

本研究專注於：
- ✅ 發現現有的 CLI-based 實作方式
- ✅ 分析硬編碼的部分與限制
- ✅ 了解 FirmwareState 與 nodes 的互動
- ✅ 探索 Makefile 整合的可能方向

## Research Question

**問題背景**：

目前 FWAuto 使用 CLI 指令直接呼叫 Keil uVision 和 ST-LINK 工具進行韌體編譯和燒錄。為了支援不同的 BSP（Board Support Package）、toolchain（Keil、GCC、IAR 等）和跨平台環境，計劃改用 Makefile 作為抽象層。

**研究目標**：

1. 理解現有 `build_node`、`flash_node`、`init_node` 的實作方式
2. 找出硬編碼的路徑、參數和專案結構綁定
3. 分析 `FirmwareState` 的屬性與各 node 的讀寫關係
4. 探索 Makefile 整合的技術方向與擴展點

## Summary

### 核心發現

**1. 現有架構採用直接 CLI 調用模式**

- `build_node` 硬編碼 Keil uVision 路徑 (`c:\Keil_v5\UV4\uv4.exe`)
- `flash_node` 硬編碼 ST-LINK 工具路徑
- `init_node` 綁定固定的專案目錄結構 (`C:\STM32F407\examples\實例XX *`)

**2. 硬編碼問題限制了擴展性**

| 限制 | 影響 |
|------|------|
| 固定工具鏈路徑 | 無法支援不同 toolchain |
| 固定專案結構 | 無法整合其他 BSP |
| Windows 專屬路徑 | 跨平台支援困難 |
| 硬編碼檔案名稱 | 無法彈性配置輸出 |

**3. FirmwareState 保持極簡設計**

- 共 19 個核心屬性（符合 CLAUDE.md 的最小化原則）
- 使用 TypedDict 而非 Pydantic（輕量級）
- Node 間透過 `{**state, ...}` 模式傳遞狀態
- 動態新增 `firmware_path` 屬性用於 build → flash 傳遞

**4. Makefile 整合方向明確**

根據設計文件 (`docs/makefile-design.md`)，整合策略為：

```mermaid
graph LR
    A[Python Node] -->|"make -f build/Makefile"| B[Makefile]
    B -->|執行| C[Keil/GCC/IAR]
    B -->|產生| D[".fwauto/build_output.toml"]
    A -->|讀取| D
```

**數據傳遞三管道**：
- Exit code → 成功/失敗判斷
- stdout/stderr → AI Brain 分析
- TOML 檔案 → 結構化資訊（hex 路徑、metadata）

## Detailed Findings

### 1. Build Node 實作分析

**位置**: `src/fwauto/nodes/build.py`

#### 1.1 硬編碼內容

**Keil 工具鏈** (`build.py:83`):
```python
cmd = f'c:\\Keil_v5\\UV4\\uv4.exe -b "{uvprojx_file}" -j0 -o "{build_log_txt}"'
```

**檔案結構假設**:

| 項目 | 硬編碼路徑 | 行號 |
|------|------------|------|
| 專案檔目錄 | `USER/*.uvprojx` | 58 |
| 輸出目錄 | `OBJ/` | 52 |
| HEX 檔名 | `Template.hex` | 53 |
| Build log | `build.log` | 82 |
| HTML log | `Template.build_log.htm` | 93 |

#### 1.2 Build 流程

**流程圖**:
```mermaid
graph TD
    A[開始 Build] --> B[檢查 project_initialized]
    B -->|未初始化| C[返回錯誤]
    B -->|已初始化| D[查找 .uvprojx 檔案]
    D --> E[清理 OBJ 目錄]
    E --> F[執行 Keil 編譯]
    F --> G[檢查 exit code]
    G -->|失敗| H[返回錯誤 + 遞增 iteration]
    G -->|成功| I[驗證 hex 檔案存在]
    I -->|不存在| H
    I -->|存在| J[驗證 hex 時間戳]
    J -->|舊檔案| H
    J -->|新檔案| K[寫入 firmware_path]
    K --> L[返回成功]
```

#### 1.3 與 FirmwareState 的互動

**讀取屬性**:
- `project_initialized` - 驗證專案已初始化
- `project_root` - 專案根目錄
- `iteration` - 當前重試次數
- `max_retries` - 最大重試次數

**寫入屬性**:
- `build_status` - `"success"` 或 `"error"`
- `build_errors` - 錯誤訊息列表
- `build_log` - 完整編譯日誌（stdout + stderr + log 檔案）
- `firmware_path` - **動態新增**，hex 檔案絕對路徑
- `iteration` - 失敗時遞增

#### 1.4 Log 處理機制

**多來源 log 合併** (`build.py:118-124`):
```python
full_output = ""
if result.stdout:
    full_output += f"STDOUT:\n{result.stdout}\n\n"
if result.stderr:
    full_output += f"STDERR:\n{result.stderr}\n\n"
if build_log_content:  # 從 OBJ/build.log 讀取
    full_output += f"BUILD LOG:\n{build_log_content}\n\n"
```

**編碼處理** (`build.py:100-112`):
- 嘗試順序：`utf-8` → `gbk` → `cp1252` → `latin1`
- 失敗時使用 binary read + `errors='ignore'`

---

### 2. Flash Node 實作分析

**位置**: `src/fwauto/nodes/flash.py`

#### 2.1 硬編碼內容

**ST-LINK 工具選擇** (`flash.py:66-77`):

| 優先級 | 工具 | 路徑 |
|--------|------|------|
| 1 | ST-LINK Utility | `C:\Program Files (x86)\STMicroelectronics\STM32 ST-LINK Utility\ST-LINK Utility\ST-LINK_CLI.exe` |
| 2 | STM32CubeProgrammer | `STM32_Programmer_CLI.exe` (假設在 PATH 中) |

**命令參數**:
- 連接介面：`SWD` (Serial Wire Debug)
- 操作流程：程式 → 驗證 → 重置

#### 2.2 Flash 流程

**流程圖**:
```mermaid
graph TD
    A[開始 Flash] --> B[檢查 project_initialized]
    B -->|未初始化| C[返回錯誤]
    B -->|已初始化| D[取得 hex 路徑]
    D -->|優先| E[從 state.firmware_path]
    D -->|Fallback| F[OBJ/Template.hex]
    E --> G[驗證 hex 檔案存在]
    F --> G
    G -->|不存在| H[返回錯誤]
    G -->|存在| I[選擇 flasher 工具]
    I --> J[執行燒錄命令]
    J --> K[檢查 exit code]
    K -->|失敗| L[分析錯誤原因]
    L --> M[返回錯誤]
    K -->|成功| N[二次檢查輸出]
    N -->|包含 error| M
    N -->|正常| O[返回成功]
```

#### 2.3 HEX 檔案路徑優先序

**路徑來源** (`flash.py:41-43`):

```python
hex_file = state.get("firmware_path")  # 優先使用 build_node 的輸出
if not hex_file:
    hex_file = os.path.join(project_path, "OBJ", "Template.hex")  # Fallback
```

**數據流**:
```
build_node (成功) → firmware_path → flash_node → hex_file
```

#### 2.4 錯誤分析邏輯

**詳細錯誤判斷** (`flash.py:99-115`):

| 輸出特徵 | 錯誤類型 |
|----------|---------|
| `"no st-link detected"` | ST-LINK 連接失敗 |
| `"target not found"` | 目標設備未找到 |
| `"access denied"` | 權限不足 |
| `"file not found"` | 檔案問題 |
| `"flash write error"` | Flash 寫入錯誤 |
| CLI 工具未找到 | 工具未安裝或路徑錯誤 |

---

### 3. Init Node 實作分析

**位置**: `src/fwauto/nodes/init.py`

#### 3.1 專案查找機制

**硬編碼基礎路徑** (`init.py:23`):
```python
base_path = "C:\\STM32F407\\examples"
```

**查找模式** (`init.py:28-36`):

```mermaid
graph TD
    A[接收 project_number] --> B["嘗試格式: 實例{num:02d} *"]
    B --> C[glob 查找]
    C -->|找到| D[返回第一個目錄]
    C -->|未找到| E["嘗試格式: 實例{num} *"]
    E --> F[glob 查找]
    F -->|找到| D
    F -->|未找到| G[返回錯誤]
```

**範例**:
- 輸入 `project_number = "04"`
- 嘗試 1: `C:\STM32F407\examples\實例04 *`
- 嘗試 2: `C:\STM32F407\examples\實例4 *`

#### 3.2 綁定的目錄結構

**「多專案」結構**:
```
C:\STM32F407\examples\
├── 實例01 XXX描述/
├── 實例02 XXX描述/
├── 實例04 UART通訊/
│   ├── USER/
│   │   └── *.uvprojx
│   └── OBJ/
│       └── Template.hex
├── ...
└── 實例12 OLED顯示/
```

**特徵**:
- 所有專案在同一父目錄下
- 統一命名規則：`實例{number} {description}`
- 透過專案編號查找

#### 3.3 初始化邏輯

**init_node 職責**:
- ✅ 查找專案目錄
- ✅ 驗證目錄存在
- ✅ 寫入 `project_root` 和 `project_initialized`
- ❌ **不檢查** USER 或 OBJ 目錄
- ❌ **不建立** 任何檔案或目錄
- ❌ **不讀取** 配置檔案

**與 FirmwareState 的互動**:

| 讀取 | 寫入 |
|------|------|
| `project_number` | `project_root` |
|  | `project_initialized` |
|  | `build_status` (失敗時) |
|  | `build_errors` (失敗時) |

---

### 4. FirmwareState 結構分析

**定義位置**: `src/fwauto/state.py`

#### 4.1 屬性分類

**核心屬性表**:

| 分類 | 屬性 | 型別 | 用途 |
|------|------|------|------|
| **專案初始化** | `user_prompt` | `str` | 使用者輸入 |
| | `project_root` | `str` | 專案根目錄 |
| | `project_number` | `str` | 專案編號 (如 "04") |
| | `project_initialized` | `bool` | 初始化狀態 |
| **Build 流程** | `build_status` | `Literal["success", "error", "pending"]` | 編譯狀態 |
| | `build_errors` | `list[str]` | 錯誤列表 |
| | `build_log` | `str` | 完整日誌 |
| **Flash 流程** | `flash_status` | `Literal["success", "error", "pending"]` | 燒錄狀態 |
| | `flash_errors` | `list[str]` | 錯誤列表 |
| | `flash_log` | `str` | 完整日誌 |
| **重試機制** | `iteration` | `int` | 當前重試次數 |
| | `max_retries` | `int` | 最大重試次數 |
| **指令系統** | `execution_mode` | `Literal[...] \| None` | 執行模式 |
| | `command` | `str \| None` | 指令名稱 |
| | `command_args` | `dict \| None` | 指令參數 |
| | `mode_metadata` | `dict \| None` | 模式 metadata |
| | `command_error` | `str \| None` | 指令錯誤 |
| **日誌分析** | `log_file_path` | `str \| None` | UART 日誌路徑 |
| | `log_analysis_result` | `str \| None` | AI 分析結果 |

**動態屬性** (TypedDict 未定義):
- `firmware_path` - build_node 成功後新增

#### 4.2 各 Node 的 State 讀寫表

| Node | 讀取屬性 | 寫入屬性 | 動態新增 |
|------|---------|---------|---------|
| `init_project_node` | `project_number` | `project_root`<br>`project_initialized`<br>`build_status`<br>`build_errors` | - |
| `build_node` | `project_initialized`<br>`project_root`<br>`iteration`<br>`max_retries` | `build_status`<br>`build_errors`<br>`build_log`<br>`iteration` | `firmware_path` |
| `flash_node` | `project_initialized`<br>`project_root`<br>`firmware_path` | `flash_status`<br>`flash_errors`<br>`flash_log` | - |
| `ai_brain_node` | `build_errors`<br>`build_status`<br>`execution_mode`<br>`project_root` | `build_status`<br>`build_errors`<br>`build_log`<br>`iteration` | - |
| `log_node` | `user_prompt`<br>`log_file_path` | `log_analysis_result` | - |

#### 4.3 State 更新模式

**所有 Node 使用相同模式**:
```python
return {
    **state,  # 保留所有現有屬性
    "field": "new_value",  # 更新特定欄位
}
```

**特性**:
- ✅ 不使用 LangGraph annotation reducer
- ✅ 完全覆寫模式（immutable）
- ✅ 符合 TypedDict 限制

#### 4.4 檔案路徑數據流

**專案路徑來源**:
```mermaid
graph LR
    A[CLI: project_number] --> B[init_project_node]
    B --> C[Glob Search]
    C --> D["C:\STM32F407\examples\實例XX *"]
    D --> E[project_root]
```

**Build 路徑衍生**:
```mermaid
graph TD
    A[project_root] --> B["USER/*.uvprojx"]
    A --> C[OBJ/]
    C --> D[Template.hex]
    C --> E[build.log]
    D --> F[firmware_path<br/>動態新增至 State]
```

**Flash 路徑優先序**:
```
1. state["firmware_path"] (來自 build_node)
   ↓ (若不存在)
2. {project_root}/OBJ/Template.hex (硬編碼 fallback)
```

---

### 5. 專案配置與目錄結構

#### 5.1 當前無外部配置檔案

**重要發現**: FWAuto **不使用外部配置檔案**初始化 State

**State 初始化來源**:
1. **CLI 指令** (`src/fwauto/cli.py`) - 所有屬性由 Typer 指令動態生成
2. **Node 運行時** - init/build/flash node 從檔案系統或工具輸出讀取

**硬編碼配置清單**:

| 項目 | 值 | 位置 |
|------|---|------|
| STM32 專案基礎路徑 | `C:\STM32F407\examples` | `init.py:23` |
| 最大重試次數 | `3` | CLI 各指令 |
| Keil 路徑 | `c:\Keil_v5\UV4\uv4.exe` | `build.py:83` |
| ST-LINK 路徑 | `C:\Program Files (x86)\...` | `flash.py:66` |

#### 5.2 設計中的配置系統

根據 `docs/makefile-design.md` 和 `docs/makefile-integration-design.md`：

**`.fwauto/config.toml`** (專案層級):
```toml
[project]
name = "STM32F4_UART_Demo"
number = "04"

[build]
makefile = ".fwauto/build/Makefile"
target = "build"

[flash]
makefile = ".fwauto/flash/Makefile"
target = "flash"
hex_arg = "HEX_FILE"
```

**`.fwauto/build_output.toml`** (自動生成):
```toml
status = "success"

[output]
hex_file = "/absolute/path/to/output.hex"
bin_file = "/path/to/output.bin"

[metadata]
toolchain = "keil"
build_time = "2025-10-09T10:30:00"
```

**`.fwauto/toolchain.toml`** (工具鏈配置):
```toml
[toolchain]
type = "keil"
uv4_path = "C:\\Keil_v5\\UV4\\uv4.exe"
project_file = ".\\USER\\MyProject.uvprojx"

[flasher]
type = "stm32cubeprogrammer"
exe_path = "C:\\...\\STM32_Programmer_CLI.exe"
interface = "SWD"
```

#### 5.3 專案目錄結構對比

**當前實作結構**:
```
C:\STM32F407\examples\實例04 UART\
├── USER\
│   └── *.uvprojx
└── OBJ\
    └── Template.hex
```

**設計中的結構**:
```
實例04 UART/
├── .fwauto/                    ← 新增配置目錄
│   ├── config.toml
│   ├── toolchain.toml
│   ├── build_output.toml
│   ├── build/
│   │   └── Makefile
│   └── flash/
│       └── Makefile
├── USER/
│   └── *.uvprojx
└── OBJ/
    └── Template.hex
```

---

### 6. Makefile 整合點與擴展方式

#### 6.1 命令執行封裝

**當前實作** - 直接使用 `subprocess.run()`:

**build_node** (`build.py:86`):
```python
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
```

**flash_node** (`flash.py:81`):
```python
result = subprocess.run(cmd, shell=True, capture_output=True, text=True,
                       encoding="utf-8", errors="replace")
```

**特性**:
- ✅ 統一的錯誤處理
- ✅ stdout/stderr 捕捉
- ✅ 多種編碼支援
- ❌ 無抽象層，直接調用工具

#### 6.2 LangGraph Node 註冊方式

**Graph 定義** (`src/fwauto/graph.py`):

```python
def create_firmware_graph():
    workflow = StateGraph(FirmwareState)

    # 註冊 Nodes
    workflow.add_node("init_project", init_project_node)
    workflow.add_node("build", build_node)
    workflow.add_node("flash", flash_node)
    workflow.add_node("ai_brain", ai_brain_node)
    workflow.add_node("log", log_node)

    # Conditional Edges
    workflow.add_conditional_edges(START, route_from_start, {...})
    workflow.add_conditional_edges("build", route_after_build, {...})

    return workflow.compile()
```

**Node 函數簽名**:
```python
def node_name(state: FirmwareState) -> dict[str, Any]:
    # 處理邏輯
    return {**state, "field": "value"}
```

#### 6.3 Makefile 整合設計

**架構圖**:
```mermaid
graph TD
    A[build_node] -->|1. 讀取 config| B[.fwauto/config.toml]
    A -->|2. 執行| C["make -f build/Makefile"]
    C -->|3. 呼叫| D[Keil/GCC/IAR]
    C -->|4. 產生| E[.fwauto/build_output.toml]
    A -->|5. 讀取| E
    A -->|6. 更新| F[FirmwareState]
```

**改寫後的 build_node 偽碼**:
```python
def build_node(state: FirmwareState) -> dict[str, Any]:
    # 1. 讀取配置
    config = load_toml(f"{project_root}/.fwauto/config.toml")
    makefile = config["build"]["makefile"]

    # 2. 執行 make
    result = subprocess.run(
        f"make -f {makefile} build",
        shell=True,
        capture_output=True,
        text=True
    )

    # 3. 判斷成功/失敗 (exit code)
    build_status = "success" if result.returncode == 0 else "error"

    # 4. 讀取結構化輸出
    output = load_toml(f"{project_root}/.fwauto/build_output.toml")
    hex_file = output["output"]["hex_file"]

    # 5. 返回更新的 state
    return {
        **state,
        "build_status": build_status,
        "build_log": result.stdout + result.stderr,
        "firmware_path": hex_file,
    }
```

#### 6.4 數據傳遞三管道

根據 `docs/makefile-design.md` 的設計：

| 管道 | 用途 | 處理方式 |
|------|------|---------|
| **Exit Code** | 成功/失敗判斷 | `result.returncode == 0` |
| **stdout/stderr** | 完整 log 給 AI Brain | `result.stdout + result.stderr` |
| **TOML 檔案** | 結構化資訊 (hex 路徑、metadata) | `load_toml(".fwauto/build_output.toml")` |

**優點**:
- ✅ 工具鏈無關 (exit code 是通用標準)
- ✅ 保留完整 log 供 AI 分析
- ✅ 結構化資訊易於程式處理
- ✅ Build 和 Flash 完全解耦

#### 6.5 擴展點設計

**當前無 Strategy Pattern**:
- 直接使用 if-else 選擇工具 (`flash.py:71-76`)

**設計文件建議** (`docs/developer-adapters.md`):

```python
# Adapter Pattern
class ToolchainAdapter(Protocol):
    def build(self, config_path: str) -> Result: ...

class FlasherAdapter(Protocol):
    def flash(self, config_path: str, artifact_path: str) -> Result: ...

# Result 統一格式
Result = {
    "success": bool,
    "message": str,
    "artifact_path": str,  # build only
    "exit_code": int,
    "stdout": str,
    "stderr": str,
}
```

**未來可透過配置選擇**:
```toml
[toolchain]
type = "keil"  # 或 "make", "gcc-arm", "iar"
```

---

## Architecture Insights

### 設計模式發現

**1. LangGraph StateGraph 模式**

FWAuto 採用 **State Machine** 架構：
- 所有 node 都是 pure function
- State 透過 immutable update 傳遞
- Conditional edges 控制流程分支

```mermaid
stateDiagram-v2
    [*] --> init_project
    init_project --> build
    build --> flash: success & flash_after_build
    build --> ai_brain: error & iteration < max_retries
    build --> [*]: success (no flash)
    ai_brain --> build: 修復後重試
    flash --> [*]
```

**2. 錯誤處理策略**

**三層驗證**:
1. Exit code 檢查
2. 檔案存在性檢查
3. 時間戳驗證（build_node）

**自動重試機制**:
```python
if build_status == "error" and iteration < max_retries:
    route_to = "ai_brain"  # AI 自動修復
```

**3. 日誌收集策略**

**多來源合併**:
- subprocess stdout/stderr
- 工具產生的 log 檔案（如 build.log）
- 多編碼嘗試（處理 Keil 中文輸出）

**4. 路徑傳遞策略**

**當前**: 透過 State 屬性傳遞
```
build_node → firmware_path → flash_node
```

**設計中**: 透過 TOML 檔案傳遞
```
build_node → .fwauto/build_output.toml → flash_node
```

### 技術可行性分析

#### Makefile 整合的技術障礙

**低障礙** (容易解決):
1. ✅ 新增 TOML 讀寫 - Python 3.11+ 內建 `tomllib`，舊版使用 `tomli`
2. ✅ 修改 subprocess 命令 - 從直接調用工具改為 `make -f ...`
3. ✅ 路徑處理 - 使用 `pathlib.Path` 統一處理

**中障礙** (需要設計):
1. ⚠️ 配置檔案驗證 - 需要 schema 驗證邏輯
2. ⚠️ 錯誤訊息對應 - Makefile 錯誤需轉譯為友善訊息
3. ⚠️ 向後相容 - 需支援無 `.fwauto/` 目錄的舊專案

**高障礙** (需要重構):
1. ❌ init_node 改寫 - 從專案編號查找改為 `.fwauto/` 錨點查找
2. ❌ CLI 參數調整 - 移除 `project_number`，支援在專案目錄執行
3. ❌ Adapter Pattern - 需要大幅重構 node 內部邏輯

#### 建議整合階段

**Phase 1: 最小可行整合** (保持當前架構):
- 新增配置檔案讀取邏輯
- build/flash_node 改為呼叫 make
- 讀取 build_output.toml
- **不改** init_node 和 CLI

**Phase 2: 配置系統** (改善 DX):
- 實作 `fwauto init` 產生 `.fwauto/` 目錄
- 互動式配置向導
- 驗證配置檔案格式

**Phase 3: 架構重構** (長期目標):
- init_node 改為 `.fwauto/` 錨點模式
- 實作 Adapter Pattern
- CLI 支援在專案目錄執行

### 與設計文件的對應關係

**設計目標 vs. 現有實作**:

| 設計目標 | 現有實作 | 差距 |
|---------|---------|------|
| Makefile 抽象層 | 直接調用工具 CLI | ❌ 需重構 |
| TOML 配置系統 | 無配置檔案 | ❌ 需實作 |
| build_output.toml | 透過 State 傳遞 | ⚠️ 需調整 |
| 跨平台支援 | Windows 專屬路徑 | ❌ 需改善 |
| 多 toolchain 支援 | 硬編碼 Keil | ❌ 需重構 |

**可保留的優點**:
- ✅ LangGraph 流程架構
- ✅ 錯誤處理與重試機制
- ✅ 多來源 log 收集
- ✅ AI Brain 自動修復

---

## Code References

| 檔案路徑 | 行號 | 說明 |
|---------|------|------|
| `src/fwauto/nodes/build.py` | 83 | 硬編碼 Keil 路徑 |
| `src/fwauto/nodes/build.py` | 52-53 | 硬編碼 OBJ 目錄和 Template.hex |
| `src/fwauto/nodes/build.py` | 58 | 硬編碼 USER 目錄查找 uvprojx |
| `src/fwauto/nodes/build.py` | 118-124 | 多來源 log 合併邏輯 |
| `src/fwauto/nodes/build.py` | 206-228 | hex 檔案時間戳驗證 |
| `src/fwauto/nodes/build.py` | 240 | 動態新增 firmware_path |
| `src/fwauto/nodes/flash.py` | 66-77 | ST-LINK 工具選擇邏輯 |
| `src/fwauto/nodes/flash.py` | 41-43 | hex 路徑優先序（firmware_path 優先） |
| `src/fwauto/nodes/flash.py` | 99-115 | 詳細錯誤分析邏輯 |
| `src/fwauto/nodes/init.py` | 23 | 硬編碼 STM32 專案基礎路徑 |
| `src/fwauto/nodes/init.py` | 28-36 | 專案目錄 glob 查找模式 |
| `src/fwauto/state.py` | 全檔 | FirmwareState TypedDict 定義 |
| `src/fwauto/graph.py` | 46-51 | 自動初始化路由邏輯 |
| `src/fwauto/graph.py` | 68-85 | build 後的路由決策 |
| `src/fwauto/cli.py` | 各 command | State 初始化來源 |
| `docs/makefile-design.md` | 全檔 | Makefile 整合高階設計 |
| `docs/makefile-integration-design.md` | 全檔 | 詳細實作設計 |
| `docs/developer-adapters.md` | 全檔 | Adapter Pattern 規範 |

---

## Historical Context (from .ai/rpi)

**相關設計文件**:
- `docs/makefile-design.md` - Makefile 整合設計文件（v1.1, 2025-10-09）
- `docs/flow-spec.md` - LangGraph 流程規格
- `docs/user-guide.md` - 使用者指南
- `docs/developer-adapters.md` - Adapter 開發者指南

**未找到先前的研究文件**:
- `.ai/rpi/research/` 目錄為空或不存在

---

## Related Research

目前尚無相關的研究文件。

---

## Open Questions

### 1. init_node 重構優先級

**問題**: init_node 是否應該在 Phase 1 就改為 `.fwauto/` 錨點模式？

**考量**:
- ✅ 優點：簡化專案查找邏輯，移除 project_number 依賴
- ❌ 缺點：需要同步修改 CLI，影響使用者體驗
- 🤔 建議：Phase 1 保持現有邏輯，先驗證 Makefile 整合可行性

### 2. firmware_path 是否應加入 TypedDict

**問題**: 動態新增的 `firmware_path` 屬性應該加入 `FirmwareState` 定義嗎？

**考量**:
- ✅ 優點：型別檢查更完整，IDE 提示更好
- ⚠️ 注意：需要設為 `str | None` 並處理初始狀態
- 🤔 建議：應該加入，避免型別不一致

### 3. 向後相容策略

**問題**: 整合 Makefile 後，如何支援沒有 `.fwauto/` 的舊專案？

**考量**:
- **方案 A**: 強制要求所有專案建立 `.fwauto/` 目錄（破壞性）
- **方案 B**: 檢測 `.fwauto/` 存在性，不存在時 fallback 到舊邏輯
- **方案 C**: 提供 `fwauto migrate` 命令自動轉換舊專案
- 🤔 建議：採用方案 B + C 組合

### 4. TOML vs. 環境變數

**問題**: 工具鏈路徑應該放在 TOML 還是環境變數？

**考量**:
- **TOML**: 專案獨立配置，適合多專案不同工具版本
- **環境變數**: 全域配置，適合同一開發環境
- 🤔 建議：TOML 優先，環境變數作為 fallback

### 5. Makefile 錯誤訊息處理

**問題**: Makefile 執行失敗時，如何提供友善的錯誤訊息？

**考量**:
- Makefile 錯誤可能來自 make 本身或底層工具
- stdout/stderr 可能包含大量不相關訊息
- 🤔 建議：
  - Makefile 使用統一的錯誤格式輸出
  - Python node 解析特定格式提取關鍵訊息
  - AI Brain 分析完整 log 提供建議

### 6. 跨平台路徑處理

**問題**: Windows 路徑 (`C:\`) vs. WSL 路徑 (`/mnt/c/`)，如何統一處理？

**考量**:
- 當前實作完全針對 Windows
- WSL 環境下需要路徑轉換
- 🤔 建議：
  - 使用 `pathlib.Path` 統一路徑操作
  - 偵測 WSL 環境並自動轉換路徑
  - Makefile 內部處理平台差異

### 7. 測試策略

**問題**: Makefile 整合後的測試策略？

**考量**:
- 需要真實的 Keil/ST-LINK 環境？
- 如何 mock 外部工具？
- 🤔 建議：
  - 單元測試：mock subprocess 和 TOML 讀寫
  - 整合測試：使用假的 Makefile（echo 模式）
  - E2E 測試：真實環境執行（CI 可選）
