jim800121chen 46514d77d7 docs(local-tool): M9 — Kneron Dongle FW 偵測 + 升降版(A+B、翻案 R5-Q9)
L 級新功能、PRD/Design/TDD/ADR 三方協作 + 互審 + M9-6 SDK 雙驗證、總計 ~9000 行文件。

範圍:
- A 階段(MVP、5 人天):KL520 + KL720 自動升級 KDP1 → KDP2
- B 階段(10.5 人天):手動降版面向一般使用者 + KL630 / KL730 擴展
- 合計 15.5 人天、安裝包 +7MB(保守 bundle 策略)

關鍵決策:
- 翻案 R5-Q9(progress.md 第二輪使用者決策「韌體燒錄 flash → B 砍掉」)
- 跨平台用 KneronPLUS Python C API、不用 DFUT.exe
- 多版本目錄結構選 C metadata(firmware/<chip>/{version}/ + CURRENT_VERSION)
- Kneron firmware redistribution 授權與 R5-B4 預置模型同性質、發佈前評估

文件產出:
- PRD v2.2(PRD-v2.md 495 行 + features/feature-firmware-management.md 599 行)
- Design v2.2(firmware-management.md 948 行 + control-panel.md §6a graceful shutdown)
- TDD v2.2(v2/firmware-management.md 823 行 + ADR-001 218 行)
- 8 份 research(含 M9-6 弱驗證 + 強驗證、~3200 行)
- 3 份三方互審報告(PM/Design/Architect cross-review)

M9-6 強驗證重大發現(影響 B 階段):
- KL730 product_id 實際是 0x732(不是 0x0730)
- KL630/KL730 firmware 是 embedded Linux rootfs(不是 .bin、不同代設計)
- KneronPLUS Python 沒 update_kdp_firmware_from_files 公開 API、warrenchen 走 ctypes
- 不影響 A 階段、B 階段 M9-8 需 spike

下一步:派 backend M9-1 起跑(bridge.py handle_firmware_upgrade)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 07:40:56 +08:00

24 KiB
Raw Blame History

手動降版面向一般使用者:流程設計與 Design 需求清單

對應 research index §42 範圍B2 階段——把 FW 降版功能暴露給一般使用者(不只 dev mode / 內部測試)、所需 driver / bridge / API endpoint 細節 + 給 Design Agent 的 UX 補強需求 撰寫日期2026-05-24 限制:架構面、不出 code、UX 細節留給 Design Agent 路徑使用相對路徑(相對於 /Users/jimchen/visionA/local-tool/


0. TL;DR

  1. 使用者決策2026-05-24翻案前一份研究 §3 的「降版僅內部測試」假設——降版面向一般使用者
  2. 一般使用者面向後、責任分工:
    • Architect本檔:標記技術需求、定義 safety guards、API 簽名、儲存結構、driver 與 bridge.py 介面
    • Design Agent另派:警告語、二次確認流程、版本 dropdown 視覺、降版進行中互動、失敗復原 UX
    • Frontend Agent:實作 UI、串 API、i18n
    • Backend Agent:實作 driver method、bridge.py handler、API endpoint
  3. 「面向一般使用者」≠ 「無腦讓使用者隨便降」——必須有多層 safety net
    • UI 警告 + 二次確認design 領域、本檔只標需求)
    • Driver 層 guard拒絕跨晶片誤匹配、拒絕「降版」到比 current 還新的版本)
    • bridge.py 層 guardchecksum 驗證 firmware 完整性、version 字串嚴格 match
  4. 暴露範圍Settings → 「韌體管理」面板(不在 Devices 頁主流程、避免誤觸)

1. 使用情境分析

1.1 一般使用者為什麼會降版

情境 頻率 描述 處理建議
「我的舊 model 在新 FW 上跑不出結果」 內建升版到 v2.2 後、舊 NEF modelv2.1 編的)相容性問題 提供降版回 v2.1 選項
「跟某個 third-party tool 不相容」 例如某些 model debugging tool 只認 KDP1 提供降版回 KDP1 選項KL520
「我升錯了、想回到原本狀態」 使用者誤觸升級、想 revert 提供「降版回上次的版本」選項
「測試環境需要特定版本」 開發者場景(但你說也要 face 一般使用者) 提供降版到任意 bundled 版本
「就是想試試看」 好奇心驅動 用警告語勸阻、但不阻止

→ 「跟一般使用者解釋降版」必須包裝成「FW 版本切換」、不要用「降版」這個帶負面含義詞——但內部技術文件仍叫 downgrade程式碼/log/Architect 文件用語)。

1.2 UI 觸發位置(建議給 Design Agent 評估)

位置 優點 缺點 建議
Devices 頁主卡片 醒目、與 FW badge 同位置 太容易誤觸、新手使用者不需要 不建議
Devices 頁 → Device Detail Modal 進階入口、需要點開 仍偏 device-centric 次佳
Settings → 韌體管理 進階入口、與其他 settings 同位置 較深入路徑 推薦
Settings → 進階advanced 最深入口 但使用者面向後不應藏太深 折衷

架構建議Settings → 韌體管理 是主入口、Devices 頁 device card 顯示 FW badge 但不放降版按鈕。


2. Driver / Bridge / API 詳細設計

2.1 driver interface 擴展

承前一份 30-integration-plan §1 M9-2 已定義的 UpgradeFirmware()、本檔加:

// 偽碼、不出 code給 architect 補 TDD 用)
type DeviceDriver interface {
    // ... 既有 methods 與 UpgradeFirmware() ...
    
    // B2 階段新增
    DowngradeFirmware(version string, progressCh chan<- FirmwareProgress) error
    ListFirmwareVersions() ([]FirmwareVersion, error)
    GetCurrentFirmwareVersion() (FirmwareVersion, error)
}

type FirmwareVersion struct {
    Version     string `json:"version"`        // "v2.2.0" / "v2.1.0" / "kdp1"
    DisplayName string `json:"displayName"`    // "v2.2.0 (current)" / "v2.1.0 (older)"
    IsCurrent   bool   `json:"isCurrent"`
    IsBundled   bool   `json:"isBundled"`      // 是否在安裝包內、總是 true不做線上更新
    ReleaseDate string `json:"releaseDate,omitempty"` // ISO 8601、optional
    Notes       string `json:"notes,omitempty"`        // 給使用者看的說明
}

type FirmwareProgress struct {
    // ... 既有欄位 ...
    Direction string `json:"direction"`   // "upgrade" / "downgrade"
}

2.2 bridge.py handler 設計

2.2.1 handle_firmware_list_versions

# 偽碼
def handle_firmware_list_versions(params):
    """List bundled firmware versions for a chip.
    
    Params:
      chip: str ("KL520" | "KL720" | "KL630" | "KL730", required)
    
    Returns:
      {"versions": [{"version":"v2.2.0", "isCurrent":true, "isBundled":true, ...}, ...]}
      or {"error": "..."}
    """
    chip = params.get("chip", "").upper()
    if chip not in ("KL520", "KL720", "KL630", "KL730"):
        return {"error": f"unknown chip: {chip}"}
    
    base = os.path.dirname(os.path.abspath(__file__))
    fw_dir = os.path.join(base, "firmware", chip)
    if not os.path.isdir(fw_dir):
        return {"error": f"firmware dir not found for {chip}"}
    
    # 讀 current/VERSION
    current_version = _read_version_file(os.path.join(fw_dir, "current", "VERSION"))
    
    versions = []
    for entry in os.listdir(fw_dir):
        entry_path = os.path.join(fw_dir, entry)
        if not os.path.isdir(entry_path):
            continue
        if entry == "current":
            continue  # current 是 alias、不重複列
        version_file = os.path.join(entry_path, "VERSION")
        if not os.path.exists(version_file):
            continue
        v = _read_version_file(version_file)
        versions.append({
            "version": v,
            "displayName": _format_display_name(v, current_version),
            "isCurrent": v == current_version,
            "isBundled": True,
            "directory": entry,  # 給 bridge.py 內部用、不外露給 API
        })
    
    return {"versions": versions, "current": current_version}

2.2.2 handle_firmware_downgrade

# 偽碼
def handle_firmware_downgrade(params):
    """Downgrade firmware to a specific bundled version.
    
    Params:
      port: int (USB port id, required)
      chip: str ("KL520" | "KL720" | "KL630" | "KL730", required)
      version: str (target version, e.g. "v2.1.0" / "kdp1", required)
    
    Returns:
      {"status":"downgraded", "before_version":"v2.2.0", "after_version":"v2.1.0",
       "duration_ms":31000, "stage":"done"}
      or {"error":"...", "stage":"validate|connect|download|verify"}
    """
    chip = params.get("chip", "")
    target_version = params.get("version", "")
    target_port = params.get("port", "")
    
    # Stage 1: validate
    # - chip 必須是支援的
    # - version 必須在 bundled list 內
    # - target_version 不能等於 current那是 no-op
    # - target_version 不能比 current 還新(那是 upgrade、走另一支
    if not _validate_downgrade_request(chip, target_version):
        return {"error": "invalid downgrade request", "stage": "validate"}
    
    # Stage 2: 解析 firmware paths
    fw_paths = _resolve_firmware_paths_versioned(chip, target_version)
    if fw_paths is None:
        return {"error": f"firmware not found: {chip}/{target_version}", "stage": "validate"}
    
    # Stage 3: connect
    # 跟 firmware_upgrade 一樣的 connect_with_magic 流程
    # ...
    
    # Stage 4: 跑 SDK API
    # 視 chip:
    # - KL520 KDP2 → KDP1: kp.core.update_kdp_firmware_from_files(loader, ..., auto_reboot=True) + load_firmware
    # - KL520 v2.2 → v2.1: 同上但用不同 .bin
    # - KL720: 同上KL720 是 flash-based、要寫 flash
    # - KL630/KL730: 用 update_*_from_tar APIM9-6 驗證後填)
    # ...
    
    # Stage 5: verify
    # disconnect → sleep 3s → rescan → 看 firmware 字串 / kn_number 是否符合預期
    # ...
    
    return {
        "status": "downgraded",
        "before_version": before_version,
        "after_version": after_version,
        "duration_ms": duration,
        "stage": "done",
    }

2.3 API endpoint 設計

Endpoint Method Request Body Response
GET /api/devices/:id/firmware/versions GET {success:true, data:{versions:[...], current:"v2.2.0"}}
POST /api/devices/:id/firmware/downgrade POST {version:"v2.1.0", confirmToken:"DOWNGRADE"} 202 {success:true, data:{taskId:"..."}}
WebSocket room firmware:<deviceId> progress events同 upgrade 流)

confirmToken 設計

  • API 層要求 body 含 confirmToken: "DOWNGRADE"(字面字串)
  • 沒帶 / 帶錯 → API 直接 400
  • 目的:防止 CSRF、防止前端 bug 誤觸發、強制 UI 二次確認流程
  • Frontend 必須先讓使用者輸入字面字串 / 點兩次按鈕、才把 token 加進 request

2.4 driver 層 safety guards

KneronDriver.DowngradeFirmware(version) 內必做:

  1. 不能跨晶片:呼叫前驗 versionListFirmwareVersions() 結果內、不接受任意字串
  2. 不能升版偽裝:比較 versionGetCurrentFirmwareVersion() 結果、若目標版本 >= current → 拒絕(要走升版 API
  3. 不能 no-op:若 version == current → 拒絕(節省時間 + 避免 device 不必要 reset
  4. status guarddevice 必須是 StatusDetectedStatusConnected、不能在 StatusInferencing / StatusFlashing / StatusUpgrading 期間降版(會卡 mutex
// 偽碼
func (d *KneronDriver) DowngradeFirmware(version string, progressCh chan<- driver.FirmwareProgress) error {
    d.mu.Lock()
    if d.info.Status == driver.StatusInferencing || d.info.Status == driver.StatusFlashing {
        d.mu.Unlock()
        return fmt.Errorf("device busy: %s", d.info.Status)
    }
    chip := d.chipType
    current := d.info.FirmwareVer
    d.mu.Unlock()
    
    // Validate version exists
    versions, err := d.ListFirmwareVersions()
    if err != nil {
        return err
    }
    var targetVer *driver.FirmwareVersion
    for _, v := range versions {
        if v.Version == version {
            targetVer = &v
            break
        }
    }
    if targetVer == nil {
        return fmt.Errorf("version %s not found in bundled firmware for %s", version, chip)
    }
    
    // Validate it's actually a downgrade順序比較邏輯 chip-specific
    if !isOlderVersion(version, current, chip) {
        return fmt.Errorf("target version %s is not older than current %s", version, current)
    }
    
    // 進實際降版流程
    // ... 跟 UpgradeFirmware 類似、call bridge.py firmware_downgrade ...
}

3. 多版本 firmware 儲存結構B2 細化)

3.1 完整目錄結構

server/scripts/firmware/
├── KL520/
│   ├── current/                    ← 預設 firmwareA 階段位置 = MVP 既有)
│   │   ├── fw_scpu.bin             ← KDP2 v2.2.0
│   │   ├── fw_ncpu.bin
│   │   ├── fw_loader.bin           ← KDP1→KDP2 升級用A 階段補進來)
│   │   └── VERSION                 ← "v2.2.0"
│   ├── v2.2.0/                     ← 跟 current 同內容、用於版本切換 reference
│   │   ├── fw_scpu.bin
│   │   ├── fw_ncpu.bin
│   │   ├── fw_loader.bin
│   │   └── VERSION
│   ├── v2.1.0/                     ← 舊版本(如果決定 bundle
│   │   ├── fw_scpu.bin
│   │   ├── fw_ncpu.bin
│   │   ├── fw_loader.bin
│   │   └── VERSION
│   └── kdp1/                       ← KDP1 降版
│       ├── fw_scpu.bin
│       ├── fw_ncpu.bin
│       └── VERSION                 ← "KDP1"(特殊標記、不是 semver
├── KL720/
│   ├── current/                    ← v2.2.0
│   ├── v2.2.0/
│   └── v2.1.0/                     ← 視是否 bundle
├── KL630/
│   ├── current/                    ← SDK-v2.5.7
│   │   ├── kp_firmware.tar
│   │   ├── kp_loader.tar
│   │   ├── extracted/              ← build time 解壓(如選策略 Y
│   │   │   ├── fw_scpu.bin
│   │   │   └── fw_ncpu.bin
│   │   └── VERSION                 ← "SDK-v2.5.7"
│   └── SDK-v2.4.0/                 ← 視是否 bundle 舊 SDK
│       └── ...
└── KL730/
    ├── current/                    ← SDK-v1.3.0
    │   ├── kp_firmware.tar
    │   ├── kp_loader.tar
    │   ├── extracted/
    │   └── VERSION
    └── ...

3.2 current/ 是什麼

選項 Asymbolic linkcurrent/ 是 symlink 指向 v2.2.0/

  • 優點:節省空間(不重複 binary
  • 缺點Windows symbolic link 需要 admin 權限、tar/zip 壓縮對 symlink 處理各 OS 不同
  • 不推薦(跨平台 symlink 太脆弱)

選項 B實體副本current/v2.2.0/ 的 file copy

  • 優點:跨平台、簡單
  • 缺點:每個 chip 多佔 1 份KL520 ~100KB、KL720 ~250KB、KL630/KL730 ~3-4MB、合計 ~7-8MB 額外
  • 推薦

選項 C取消 current/、用 metadata 記錄當前版本

  • 結構簡化:firmware/<chip>/{v2.2.0,v2.1.0,kdp1}/ + firmware/<chip>/CURRENT_VERSION(單行檔 = "v2.2.0"
  • 優點節省空間、structure 清晰
  • 缺點bridge.py 多一次 file read 才知道用哪個版本trivial 成本)
  • 可選(架構乾淨度 vs 簡單度的 trade-off

建議選 C——架構最乾淨、空間最省、跨平台無 symlink 風險。

3.3 採選項 C 的具體結構

server/scripts/firmware/
├── KL520/
│   ├── CURRENT_VERSION             ← 單行檔:"v2.2.0"
│   ├── v2.2.0/
│   │   ├── fw_scpu.bin
│   │   ├── fw_ncpu.bin
│   │   ├── fw_loader.bin
│   │   └── VERSION                 ← "v2.2.0"
│   ├── v2.1.0/
│   │   └── ...
│   └── kdp1/
│       └── ...
├── KL720/
│   └── ...同結構...
├── KL630/
│   └── ...同結構(.tar / extracted...
└── KL730/
    └── ...

_resolve_firmware_paths_versioned(chip, version=None)

  • version=None → 讀 CURRENT_VERSION 拿當前版本 → 找 <chip>/<version>/
  • version="v2.1.0" → 直接找 <chip>/v2.1.0/

A 階段MVP相容性A 階段 _resolve_firmware_paths(chip)<chip>/fw_scpu.bin、B2 階段重整目錄時、要把 A 階段檔案搬進 <chip>/v2.2.0/ 並加 CURRENT_VERSIONA 階段的 caller 必須升級成 _resolve_firmware_paths_versionedB2 migration step


4. Bundle 多版本對安裝包大小的精確估算

4.1 預估 bundle 策略

Chip current 額外 bundled 合計(每 chip
KL520 v2.2.0 (~100KB) v2.1.0 (~100KB) + kdp1 (~90KB) ~290KB
KL720 v2.2.0 (~250KB) v2.1.0 (~250KB)(如果 bundle ~500KB
KL630 SDK-v2.5.7 (~3MB) SDK-v2.4.0 (~3MB)(如果 bundle+ extracted (~3MB) ~9MB
KL730 SDK-v1.3.0 (~4MB) + extracted (~4MB) ~8MB

合計 B2 階段所有 bundled firmware~18MB多版本全部 bundle

4.2 與既有 dmg 163MB 比

階段 累計 衝擊
既有 163MB baseline
A 階段MVP 163MB + 10KB <0.01%
B 階段(單版本 KL630/KL730 + 降版用 KL520_kdp 163MB + 6-8MB ≈ 170MB +4-5%
B2 階段(每 chip 多 bundle 1 舊版) 163MB + 14-18MB ≈ 178-181MB +9-11%

使用者「+5MB 接受」對 B 階段成立、但 B2 多版本超出

4.3 取捨建議

策略 大小 取捨
保守:每 chip 只 bundle current + 1 個降版選項KL520 v2.1.0、其他不 bundle 舊版) +7MB 滿足使用者「+5MB 接受」邊緣、降版選擇變少
完整:每 chip current + 2 個舊版 +18MB 超出 +5MB 估算、但選擇豐富
極簡:只 bundle current + KDP1 降版KL520 only +5MB 嚴格 +5MB、其他 chip 沒降版選擇

建議選保守策略——KL520 提供「kdp1」「v2.1.0」兩個降版、KL720/KL630/KL730 提供 current + 1 個舊版(如有可用)。

重要前提:因 SDK release 不一定提供舊版 firmware、KL630/KL730 「舊版」可能取不到M9-6 待驗證)。


5. Design Agent 需求清單(給 Orchestrator 派 Design 用)

注意:以下是 architect 標記的「功能性需求」、Design Agent 負責設計具體 wireframe / 視覺 / 文案。Architect 不畫設計稿、不寫文案。

5.1 Settings → 韌體管理面板(新頁面)

功能需求

  • F1. 列出所有偵測到的 dongle、每張 dongle 一張卡片
  • F2. 卡片顯示dongle 名稱("KL520 #1"、kn_number、當前 FW 版本、bundled current 版本(讓使用者看出是否最新)
  • F3. 卡片內 actions
    • 「升級到最新」按鈕(如果 current 與 bundled 不同)
    • 「切換 FW 版本」按鈕(永遠顯示、暴露多版本選擇)
  • F4. 「切換 FW 版本」展開後:版本 dropdown + 詳細說明(每個版本一段話)+ 警告語 + 「降版」按鈕
  • F5. 二次確認 modal見 §5.3
  • F6. 降版進行中 progress UIprogress bar + 階段提示 + 不可中斷警告)
  • F7. 降版完成後 toast 通知 + 自動 rescan + 卡片更新到新版本

5.2 Devices 頁面 FW badge補強既有 A 階段)

功能需求

  • 既有:紅/黃/綠 badge 顯示 FW 健康度
  • 新增badge 旁加「⚙️」icon、點擊 deep-link 到 Settings → 韌體管理對應卡片
  • 不在 Devices 頁放降版按鈕(避免誤觸)

5.3 二次確認 modal最關鍵的 design 細節)

功能需求design 要全部達成

  • D1. 警告語必須包含:
    • 「降版可能導致現有 model 無法運作」
    • 「降版過程不可中斷、否則裝置可能損壞」
    • 「降版完成後可能需要重新插拔裝置」
    • 「請確認版本相容性、降版至 KDP1 的舊版會限制可用功能」
  • D2. 使用者必須輸入字面字串「DOWNGRADE」確認(防誤觸)
    • 不接受其他大小寫
    • 輸入欄為空 / 不對時、確認按鈕 disabled
  • D3. 顯示 before/after 版本比對表(當前 vs 目標)
  • D4. 顯示預估時間KL520 ~30s、KL720 ~180s、KL630/KL730 估計 ~60s
  • D5. modal 不可被點外部關閉(必須點「取消」明確關閉)
  • D6. 「確認降版」按鈕視覺要 destructive紅色 / 警告色)
  • D7. 確認後 modal 不關閉、直接切到「進行中」狀態(避免使用者誤以為什麼都沒發生)

5.4 「進行中」UI

功能需求

  • E1. progress bar雖然底層是 stage-based、給使用者看百分比更直觀
  • E2. 階段文字(連線中 / 載入引導程式 / 寫入韌體 / 驗證 / 完成)
  • E3. 不顯示「取消」按鈕(降版不可中斷、有按鈕會誘惑使用者點)
  • E4. 顯示「請勿拔除裝置」persistent banner不可關閉
  • E5. 如果失敗、顯示 friendly error 訊息 + 「重新插拔裝置後重試」指引

5.5 失敗復原 UX

功能需求

  • R1. 區分失敗類型:
    • 「升級時 device disconnect」→ 訊息:「裝置已斷開、請重新插入後重試」
    • 「Firmware 損毀 / 寫入失敗」→ 訊息:「韌體寫入失敗、請聯絡客服」(罕見、可能 brick
    • 「Timeout」→ 訊息:「升級時間過長、可能成功也可能未完成、請拔插裝置再 scan」
  • R2. 失敗後不自動關閉 modal、給使用者讀完訊息再關
  • R3. 提供「重試」按鈕(如果是可重試的 error
  • R4. 提供「複製錯誤訊息」按鈕(給技術支援用)

5.6 i18n keys給 frontend 估算)

預估 新增中英雙語 keys

  • Settings 韌體管理頁標題、說明:~3 個
  • 卡片內各欄位、按鈕:~8 個
  • 二次確認 modal~12 個
  • 進行中 UI~8 個
  • 失敗訊息:~10 個
  • toast / banner~5 個
  • 合計 ~46 個新 i18n keys

6. 一般使用者誤觸降版 brick 風險評估

6.1 風險矩陣

誤操作 機率 嚴重性 已緩解 殘餘風險
點錯按鈕(沒看清楚就降版) 二次確認 + 輸入「DOWNGRADE」字串
降版到不相容版本KL520 KDP2 → KDP1且使用者依賴 KDP2-only 功能) 警告語明示 KDP1 限制 中(使用者沒讀完警告)
降版中拔 USB 高(可能 brick persistent banner + 不可關 modal
降版中關 app server graceful shutdown 機制(既有)
升級當降版(誤把新版降到舊版、反向) driver guard不允許目標版本 >= current 已消除
跨晶片誤匹配 極低 driver guardversion 必須在 ListFirmwareVersions(chip) 已消除

6.2 殘餘風險最大的:使用者不讀警告就降版

對策

  • 二次確認字串「DOWNGRADE」不是 「OK」/「Yes」、強制使用者打字
  • 視覺破壞性 button color紅色
  • 「進行中」期間 banner persist不可關

無法完全消除——一般使用者場景下、總有人會無視警告。

6.3 Plan B給技術支援用的救磚 SOP

如果使用者真的把 dongle 弄 brick、需要

  • DFUT.exeWindows-only Qt 工具、可從 Kneron 拿)
  • 燒回 KDP2 standard firmware

不打包 DFUT.exe 進 visionA-local(因為跨平台限制 + 30MB 額外大小 + 安全性考量)、但提供 SOP 文件(內部 wiki / docs/troubleshooting/brick-recovery.md讓技術支援能幫忙。


7. 法律 / 合規 考量(待釐清)

承前一份 R-FW-5「打包 Kneron 官方 firmware 是否合法」。

B2 階段新增變數

  • 多版本 bundle 含舊版 firmwarev2.1.0、KDP1→ 法律問題加重?
  • 暴露給一般使用者降版 → Kneron 是否允許KDP1 是被棄用版本、Kneron 不一定希望使用者降回去)

建議

  • B 階段啟動前、與 Kneron 取得 firmware re-distribution 明確書面授權
  • 授權內容必須包含current + 舊版v2.1.0 等)+ KDP1
  • 如果 Kneron 不允許降版到 KDP1、調整 bundle 範圍

8. 給 Orchestrator 的決策點清單

  1. 使用者已決策手動降版面向一般使用者 → 本檔 §5 design 需求成立
  2. 使用者已決策 FW 內嵌進安裝包、+5MB 接受 → 本檔 §4.3「保守」策略對齊
  3. 使用者已決策不做線上更新通道 → 不實作 OTA、所有 firmware bundle
  4. 待使用者裁決
    • 多版本目錄結構選 A/B/C建議 C、§3.2
    • bundle 哪些舊版建議「保守」策略、§4.3
    • KneronPLUS wheel 升級 vs 不升級M9-6 驗證後決定)
    • 與 Kneron 取得 firmware redistribution 授權的時程?

9. 與其他研究檔的關係

連結 引用
30-integration-plan.md §6 階段 B 評估提示 本檔詳化 B2 階段細節
40-b-phase-kl630-kl730-extension.md §8 多版本 firmware 並存 本檔 §3 詳化儲存結構
40-b-phase-kl630-kl730-extension.md R-FW-11/R-FW-12 本檔 §5/§6 提供緩解措施
41-tar-firmware-handling.md §3 大小估算 本檔 §4 整合計算

10. 工時影響補充

B 階段拆三層之後、本檔內容對應 M9-11 + M9-12 兩個 milestone

Milestone 本檔 § 工時
M9-11 多版本後端 §2.1 ~ §2.4 + §3 + §4 1.5 人天
M9-12 降版 UI §5 + §6 + §7 Frontend 1 人天 + Design 1 人天 = 2 人天
合計 3.5 人天

(已含在 §40 B 階段 M9-6 ~ M9-13 工時表內、本檔不重複加總)