{
  "metadata": {
    "id": "BP-ios-notification-image",
    "group_id": "ai-magic-editor",
    "type": "Blueprint",
    "feature": "ios-notification-image",
    "requirement": "REQ-ios-notification-image"
  },
  "info": {
    "status": "Implemented",
    "update_date": "2025-10-01",
    "implementation_date": "2025-10-01",
    "summary_context": "iOS Notification Service Extension 的技術架構，定義圖片下載、附加流程和組件設計。已完成實作並通過所有實機測試驗證。"
  },
  "title": "專案藍圖 (Blueprint): iOS Push Notification 圖片顯示",
  "sections": [
    {
      "id": "sec-core-data-structures",
      "title": "1. 核心資料結構 (Core Data Structures)",
      "summary": "定義圖片下載ＲＢＴ和通知處理所需的核心資料結構。",
      "blocks": [
        {
          "id": "blk-ds-imageinfo",
          "type": "code",
          "title": "NotificationImageInfo",
          "content": "struct NotificationImageInfo {\n    let imageUrl: URL          // 圖片 URL\n    let contentType: String?   // MIME type (可選)\n}"
        },
        {
          "id": "blk-ds-result",
          "type": "code",
          "title": "DownloadResult",
          "content": "enum DownloadResult {\n    case success(URL)  // 本地暫存檔案 URL\n    case failure       // 下載失敗（含 timeout）\n}"
        }
      ],
      "children": []
    },
    {
      "id": "sec-component-specs",
      "title": "2. 組件規格 (Component Specifications)",
      "summary": "定義實現 Notification Service Extension 所需的三大核心組件。",
      "blocks": [],
      "children": [
        {
          "id": "sec-spec-notificationservice",
          "title": "NotificationService (Extension 主體)",
          "summary": null,
          "blocks": [
            {
              "id": "blk-ns-intro",
              "type": "list",
              "title": "簡介",
              "items": [
                "Notification Service Extension 的主要類別，繼承 `UNNotificationServiceExtension`"
              ]
            },
            {
              "id": "blk-ns-duty",
              "type": "list",
              "title": "職責",
              "items": [
                "接收系統調用 `didReceive(_:withContentHandler:)`",
                "解析 FCM payload 取得圖片 URL",
                "協調 Downloader 和 Modifier 完成圖片附加",
                "處理 30 秒系統超時"
              ]
            },
            {
              "id": "blk-ns-input",
              "type": "list",
              "title": "輸入",
              "items": [
                "`request: UNNotificationRequest` (通知請求)",
                "`contentHandler: (UNNotificationContent) -> Void` (完成回調)"
              ]
            },
            {
              "id": "blk-ns-output",
              "type": "list",
              "title": "輸出",
              "items": [
                "修改後的 `UNNotificationContent`（含圖片附件或原始內容）"
              ]
            },
            {
              "id": "blk-ns-tasks",
              "type": "list",
              "title": "實作 Tasks",
              "items": [
                "`TASK-001-notification-service`"
              ]
            },
            {
              "id": "blk-ns-acceptance",
              "type": "list",
              "title": "技術驗收標準",
              "items": [
                "成功解析 FCM payload 中的圖片 URL",
                "正確協調 Downloader 和 Modifier",
                "下載/附加失敗時退回原始通知",
                "處理 `serviceExtensionTimeWillExpire()` 超時情況"
              ]
            }
          ],
          "children": []
        },
        {
          "id": "sec-spec-downloader",
          "title": "NotificationImageDownloader (圖片下載器)",
          "summary": null,
          "blocks": [
            {
              "id": "blk-dl-intro",
              "type": "list",
              "title": "簡介",
              "items": [
                "負責從 HTTPS URL 下載圖片到本地暫存"
              ]
            },
            {
              "id": "blk-dl-duty",
              "type": "list",
              "title": "職責",
              "items": [
                "使用 `URLSession.shared.downloadTask` 下載圖片",
                "設定 5 秒 timeout",
                "驗證 URL 為 HTTPS",
                "錯誤處理與日誌記錄"
              ]
            },
            {
              "id": "blk-dl-input",
              "type": "list",
              "title": "輸入",
              "items": [
                "`imageUrl: URL` (圖片 URL)",
                "`timeout: TimeInterval` (預設 5 秒)"
              ]
            },
            {
              "id": "blk-dl-output",
              "type": "list",
              "title": "輸出",
              "items": [
                "成功: 本地暫存檔案 `URL`",
                "失敗: `nil`（並記錄 log）"
              ]
            },
            {
              "id": "blk-dl-tasks",
              "type": "list",
              "title": "實作 Tasks",
              "items": [
                "`TASK-002-image-downloader`"
              ]
            },
            {
              "id": "blk-dl-acceptance",
              "type": "list",
              "title": "技術驗收標準",
              "items": [
                "只接受 HTTPS URL（HTTP 拒絕）",
                "5 秒 timeout 正確觸發",
                "下載成功返回有效的本地檔案 URL",
                "失敗時正確記錄錯誤日誌"
              ]
            }
          ],
          "children": []
        },
        {
          "id": "sec-spec-modifier",
          "title": "NotificationContentModifier (通知內容修改器)",
          "summary": null,
          "blocks": [
            {
              "id": "blk-mod-intro",
              "type": "list",
              "title": "簡介",
              "items": [
                "負責建立 `UNNotificationAttachment` 並附加到通知內容"
              ]
            },
            {
              "id": "blk-mod-duty",
              "type": "list",
              "title": "職責",
              "items": [
                "從本地檔案 URL 建立 `UNNotificationAttachment`",
                "附加到 `UNMutableNotificationContent.attachments`",
                "錯誤處理與日誌記錄"
              ]
            },
            {
              "id": "blk-mod-input",
              "type": "list",
              "title": "輸入",
              "items": [
                "`content: UNMutableNotificationContent` (可修改的通知內容)",
                "`imageFileUrl: URL` (本地圖片檔案 URL)"
              ]
            },
            {
              "id": "blk-mod-output",
              "type": "list",
              "title": "輸出",
              "items": [
                "成功: 附加圖片後的 `UNMutableNotificationContent`",
                "失敗: 原始 `content`（並記錄 log）"
              ]
            },
            {
              "id": "blk-mod-tasks",
              "type": "list",
              "title": "實作 Tasks",
              "items": [
                "`TASK-003-content-modifier`"
              ]
            },
            {
              "id": "blk-mod-acceptance",
              "type": "list",
              "title": "技術驗收標準",
              "items": [
                "成功建立 `UNNotificationAttachment`",
                "正確附加到 `content.attachments`",
                "附加失敗時不影響原始 content",
                "失敗時正確記錄錯誤日誌"
              ]
            }
          ],
          "children": []
        }
      ]
    },
    {
      "id": "sec-core-logic",
      "title": "3. 核心處理邏輯 (Core Processing Logic)",
      "summary": "詳細描述從收到通知到顯示圖片的完整處理流程。",
      "blocks": [],
      "children": [
        {
          "id": "sec-logic-mainflow",
          "title": "主流程 (NotificationService)",
          "summary": null,
          "blocks": [
            {
              "id": "blk-mf-step1",
              "type": "code",
              "title": "階段 1: 接收通知",
              "content": "iOS 系統收到 FCM 推播\n  ↓\n檢查 payload 是否含 mutable-content: 1\n  ↓\n系統啟動 NotificationService Extension\n  ↓\n調用 didReceive(_:withContentHandler:)"
            },
            {
              "id": "blk-mf-step2",
              "type": "code",
              "title": "階段 2: 解析 Payload",
              "content": "取得 request.content.userInfo\n  ↓\n優先解析: userInfo[\"fcm_options\"][\"image\"] (iOS 專用)\n  ↓\n備用解析: userInfo[\"notification\"][\"image\"] (通用)\n  ↓\n驗證 URL 格式 (必須是 HTTPS)"
            },
            {
              "id": "blk-mf-step2-note",
              "type": "list",
              "title": "⚠️ 待確認",
              "items": [
                "FCM payload 的實際欄位名稱（待後端提供實際格式）"
              ]
            },
            {
              "id": "blk-mf-step3",
              "type": "code",
              "title": "階段 3: 下載圖片",
              "content": "有效的圖片 URL？\n  ├─ NO  → 跳到階段 5（回傳原始內容）\n  └─ YES → 調用 NotificationImageDownloader.download(imageUrl)\n             ↓\n           URLSession.downloadTask (5秒 timeout)\n             ↓\n           成功？\n             ├─ NO  → 跳到階段 5（回傳原始內容 + log）\n             └─ YES → 繼續階段 4"
            },
            {
              "id": "blk-mf-step4",
              "type": "code",
              "title": "階段 4: 附加圖片",
              "content": "NotificationContentModifier.attach(imageFileUrl, to: content)\n  ↓\n建立 UNNotificationAttachment\n  ↓\n成功？\n  ├─ NO  → 跳到階段 5（回傳原始內容 + log）\n  └─ YES → content.attachments.append(attachment)"
            },
            {
              "id": "blk-mf-step5",
              "type": "code",
              "title": "階段 5: 回傳結果",
              "content": "調用 contentHandler(modifiedContent)\n  ↓\niOS 系統顯示通知\n  ↓\n有圖片附件 → Rich Notification（可展開查看圖片）\n無圖片附件 → 純文字通知"
            }
          ],
          "children": []
        },
        {
          "id": "sec-logic-timeout",
          "title": "超時處理",
          "summary": null,
          "blocks": [
            {
              "id": "blk-to-flow",
              "type": "code",
              "title": null,
              "content": "30 秒內未完成（系統限制）\n  ↓\niOS 調用 serviceExtensionTimeWillExpire()\n  ↓\n立即回傳當前的 bestAttemptContent\n  ↓\n即使圖片未下載完成，也顯示通知（不阻斷用戶）"
            }
          ],
          "children": []
        }
      ]
    },
    {
      "id": "sec-tech-details",
      "title": "4. 技術架構細節",
      "summary": null,
      "blocks": [],
      "children": [
        {
          "id": "sec-td-files",
          "title": "檔案結構",
          "summary": null,
          "blocks": [
            {
              "id": "blk-td-files-code",
              "type": "code",
              "title": null,
              "content": "MagicEditor/\n└── Sources/\n    └── Extensions/\n        └── NotificationService/\n            ├── NotificationService.swift          // 主體 (TASK-001)\n            ├── NotificationImageDownloader.swift  // 下載器 (TASK-002)\n            └── NotificationContentModifier.swift  // 修改器 (TASK-003)"
            }
          ],
          "children": []
        },
        {
          "id": "sec-td-tuist",
          "title": "Tuist 專案配置",
          "summary": null,
          "blocks": [
            {
              "id": "blk-td-tuist-list",
              "type": "list",
              "title": null,
              "items": [
                "新增 `NotificationServiceExtension` target",
                "Bundle ID: `{PRODUCT_BUNDLE_IDENTIFIER}.NotificationServiceExtension`",
                "Deployment Target: iOS 14.0+",
                "依賴: Foundation, UserNotifications",
                "Info.plist 配置: `NSExtension` 相關設定",
                "App Groups 共享（未來可能需要）"
              ]
            },
            {
              "id": "blk-td-tuist-tasks",
              "type": "list",
              "title": "實作 Tasks",
              "items": [
                "`TASK-004-tuist-configuration`"
              ]
            }
          ],
          "children": []
        },
        {
          "id": "sec-td-formats",
          "title": "圖片格式支援",
          "summary": null,
          "blocks": [
            {
              "id": "blk-td-formats-list",
              "type": "list",
              "title": null,
              "items": [
                "PNG (.png)",
                "JPEG (.jpg, .jpeg)",
                "GIF (.gif)",
                "其他格式由 `UNNotificationAttachment` 自動判斷"
              ]
            }
          ],
          "children": []
        },
        {
          "id": "sec-td-security",
          "title": "安全性考量",
          "summary": null,
          "blocks": [
            {
              "id": "blk-td-security-list",
              "type": "list",
              "title": null,
              "items": [
                "**只接受 HTTPS URL**（HTTP 拒絕）",
                "**驗證 Content-Type**（僅接受 image/* MIME types）",
                "**自動清理暫存檔案**（系統管理）"
              ]
            }
          ],
          "children": []
        }
      ]
    },
    {
      "id": "sec-risks",
      "title": "5. 風險、待辦事項與決策 (Risks, Open Questions & Decisions)",
      "summary": null,
      "blocks": [],
      "children": [
        {
          "id": "sec-risks-adr",
          "title": "設計決策記錄 (ADR)",
          "summary": null,
          "blocks": [
            {
              "id": "blk-risks-adr-table",
              "type": "table",
              "title": null,
              "content": "| 決策點 | 變更原因 | 最終實作選擇 | 記錄日期 |\n|--------|----------|----------|----------|\n| 組件拆分策略 | 未來擴充性（按鈕、自訂 UI） | 拆分為 3 個獨立檔案 | 2025-09-30 |\n| 圖片下載工具 | Extension 輕量化，不依賴第三方 | 原生 URLSession | 2025-09-30 |\n| 錯誤處理方式 | 不阻斷通知顯示 | 失敗退回純文字 + log | 2025-09-30 |\n| Timeout 時間 | 平衡速度與成功率 | 5 秒（系統總限制 30 秒） | 2025-09-30 |\n| 前景通知處理 | 降低複雜度 | 不處理（保持現有行為） | 2025-09-30 |\n| Completion Flag | 防止雙重回調風險 | 新增 isCompleted flag 和統一完成方法 | 2025-10-01 |\n| Content-Type 驗證 | 安全性與相容性平衡 | 驗證 image/* MIME type（寬鬆策略） | 2025-10-01 |"
            }
          ],
          "children": []
        },
        {
          "id": "sec-risks-tech",
          "title": "技術風險",
          "summary": null,
          "blocks": [
            {
              "id": "blk-risks-tech-1",
              "type": "list",
              "title": null,
              "items": [
                "1. **FCM Payload 格式不確定** (優先級: 高)",
                "* 風險: 解析欄位錯誤導致無法取得圖片 URL",
                "* 緩解: 待後端確認實際格式後更新程式碼",
                "* 備用方案: 同時嘗試多個可能的欄位名稱"
              ]
            },
            {
              "id": "blk-risks-tech-2",
              "type": "list",
              "title": null,
              "items": [
                "2. **圖片下載速度不穩定** (優先級: 中)",
                "* 風險: 網路不佳時 5 秒 timeout 不足",
                "* 緩解: 可調整 timeout 參數，或後端提供縮圖",
                "* 備用方案: 使用者仍收到純文字通知"
              ]
            },
            {
              "id": "blk-risks-tech-3",
              "type": "list",
              "title": null,
              "items": [
                "3. **30 秒系統超時** (優先級: 低)",
                "* 風險: 極慢網路可能觸發系統超時",
                "* 緩解: 5 秒下載 timeout 保留足夠緩衝",
                "* 備用方案: serviceExtensionTimeWillExpire 處理"
              ]
            }
          ],
          "children": []
        },
        {
          "id": "sec-risks-open",
          "title": "待辦事項 (Open Questions)",
          "summary": null,
          "blocks": [
            {
              "id": "blk-risks-open-list",
              "type": "list",
              "title": null,
              "items": [
                "[ ] **FCM Payload 格式確認**: 等待後端提供實際的 payload JSON 範例",
                "[ ] **後端 mutable-content 設定**: 確認後端已在 FCM 發送時設定此欄位",
                "[ ] **測試推播工具**: 準備測試用的圖片 URL 和推播發送方式",
                "[ ] **App Groups 配置**: 如果需要與主 App 共享資料（未來擴充）"
              ]
            }
          ],
          "children": []
        }
      ]
    },
    {
      "id": "sec-integration",
      "title": "6. 與現有架構的整合",
      "summary": null,
      "blocks": [],
      "children": [
        {
          "id": "sec-int-fcm",
          "title": "現有 FCM 整合點",
          "summary": null,
          "blocks": [
            {
              "id": "blk-int-fcm-appdelegate",
              "type": "list",
              "title": "AppDelegate (不需修改)",
              "items": [
                "`Messaging.messaging().delegate = self` 已設定",
                "`didReceiveRegistrationToken` 已處理 token 更新",
                "`UNUserNotificationCenter.delegate = self` 已設定"
              ]
            },
            {
              "id": "blk-int-fcm-manager",
              "type": "list",
              "title": "MessagingManager (不需修改)",
              "items": [
                "Token 管理已完善",
                "Extension 不直接與此互動"
              ]
            },
            {
              "id": "blk-int-fcm-foreground",
              "type": "list",
              "title": "前景通知處理 (不修改)",
              "items": [
                "`willPresent` 保持現有邏輯",
                "繼續顯示 `.banner, .sound, .badge`"
              ]
            }
          ],
          "children": []
        },
        {
          "id": "sec-int-backend",
          "title": "後端 API 依賴",
          "summary": null,
          "blocks": [
            {
              "id": "blk-int-backend-api",
              "type": "list",
              "title": "push-notification-api",
              "items": [
                "需確認 `REQ-push-notification-api` 是否已包含圖片支援",
                "需確認後端發送 FCM 時的 payload 格式",
                "需確認 `mutable-content: 1` 是否已設定"
              ]
            }
          ],
          "children": []
        }
      ]
    },
    {
      "id": "sec-task-breakdown",
      "title": "Task 拆解與追蹤 (Post Task Breakdown & Tracking)",
      "summary": null,
      "blocks": [],
      "children": [
        {
          "id": "sec-tb-overview",
          "title": "Task 概覽",
          "summary": null,
          "blocks": [
            {
              "id": "blk-tb-overview-table",
              "type": "table",
              "title": null,
              "content": "| Task ID | 標題 | 預估工時 | 依賴 | 優先級 |\n|---------|------|---------|------|--------|\n| TASK-001 | NotificationService 主體實作 | 1.5h | - | 高 |\n| TASK-002 | NotificationImageDownloader 實作 | 1h | - | 高 |\n| TASK-003 | NotificationContentModifier 實作 | 1h | - | 高 |\n| TASK-004 | Tuist 專案配置 | 2h | TASK-001, TASK-002, TASK-003 | 高 |\n| TASK-005 | 整合測試與除錯 | 1.5h | TASK-004 | 中 |"
            },
            {
              "id": "blk-tb-overview-total",
              "type": "list",
              "title": "預估總工時",
              "items": [
                "7 小時"
              ]
            }
          ],
          "children": []
        },
        {
          "id": "sec-tb-tracking",
          "title": "實作進度追蹤",
          "summary": null,
          "blocks": [
            {
              "id": "blk-tb-tracking-table",
              "type": "table",
              "title": null,
              "content": "| 組件名稱 | 對應 Tasks | 實作狀態 | 完成度 | 備註 |\n|----------|------------|--------|--------|------|\n| NotificationImageDownloader | TASK-002 | Done | 100% | 已實作完成 |\n| NotificationContentModifier | TASK-003 | Done | 100% | 已實作完成 |\n| NotificationService | TASK-001 | Done | 100% | 已實作完成（123 行）|\n| Tuist 專案配置 | TASK-004 | Done | 100% | 已完成，Extension target 配置成功並編譯通過 |\n| 整合測試 | TASK-005 | Done | 100% | 已完成實機測試，所有測試項目通過（2025-10-01）|\n\n---"
            }
          ],
          "children": []
        }
      ]
    },
    {
      "id": "sec-post-validation",
      "title": "實作後驗證與總結 (Post-Implementation Validation & Summary)",
      "summary": null,
      "blocks": [],
      "children": [
        {
          "id": "sec-pv-completion",
          "title": "實作完成度",
          "summary": null,
          "blocks": [
            {
              "id": "blk-pv-completion-code",
              "type": "list",
              "title": "程式碼實作: 100% ✅",
              "items": [
                "NotificationImageDownloader (105 lines)",
                "NotificationContentModifier (47 lines)",
                "NotificationService (123 lines)",
                "Tuist 專案配置完成",
                "Bundle ID: `com.cardinalblue.magiceditor.notificationserviceextension`"
              ]
            },
            {
              "id": "blk-pv-completion-test",
              "type": "list",
              "title": "整合測試: 50% 🔄",
              "items": [
                "測試計畫已完成",
                "等待 Apple Developer Portal 配置",
                "需實機測試環境"
              ]
            },
            {
              "id": "blk-pv-completion-total",
              "type": "list",
              "title": "總進度: 90%",
              "items": [
                "（程式碼完成，待實機驗證）"
              ]
            }
          ],
          "children": []
        },
        {
          "id": "sec-pv-acceptance",
          "title": "技術驗收",
          "summary": null,
          "blocks": [],
          "children": [
            {
              "id": "sec-pv-acceptance-done",
              "title": "已驗收項目 ✅",
              "summary": null,
              "blocks": [
                {
                  "id": "blk-pv-acceptance-done-list",
                  "type": "list",
                  "title": null,
                  "items": [
                    "✅ NotificationService 成功解析 FCM payload（支援雙格式）",
                    "✅ NotificationImageDownloader 正確協調 Downloader 和 Modifier",
                    "✅ HTTPS-only URL 驗證機制",
                    "✅ 5 秒 timeout 設定",
                    "✅ 失敗降級機制（退回純文字通知）",
                    "✅ 30 秒系統超時處理",
                    "✅ Extension target 成功編譯",
                    "✅ 所有程式碼包含追溯註解"
                  ]
                }
              ],
              "children": []
            },
            {
              "id": "sec-pv-acceptance-pending",
              "title": "待驗收項目 ⏳",
              "summary": null,
              "blocks": [
                {
                  "id": "blk-pv-acceptance-pending-list",
                  "type": "list",
                  "title": null,
                  "items": [
                    "⏳ 實機推播圖片顯示測試",
                    "⏳ PNG/JPG/GIF 格式支援驗證",
                    "⏳ 錯誤場景實機測試（HTTP URL, 404, 超時）",
                    "⏳ 前景行為驗證",
                    "⏳ FCM payload 實際格式確認（待後端提供）"
                  ]
                }
              ],
              "children": []
            }
          ]
        },
        {
          "id": "sec-pv-lessons",
          "title": "知識沉澱與教訓 (Lessons Learned)",
          "summary": null,
          "blocks": [],
          "children": [
            {
              "id": "sec-pv-lessons-debts",
              "title": "設計負債與技術債務",
              "summary": null,
              "blocks": [
                {
                  "id": "blk-pv-lessons-debts-list",
                  "type": "list",
                  "title": null,
                  "items": [
                    "**FCM Payload 格式待確認**: 當前使用假設格式實作，需後端提供實際 payload 範例",
                    "**日誌機制**: 使用 print 而非 os_log，未來可統一改為結構化日誌",
                    "**Timeout 值硬編碼**: 5 秒 timeout 寫死在程式碼中，未來可考慮可配置"
                  ]
                }
              ],
              "children": []
            },
            {
              "id": "sec-pv-lessons-design",
              "title": "低估與過度設計",
              "summary": null,
              "blocks": [
                {
                  "id": "blk-pv-lessons-design-list",
                  "type": "list",
                  "title": null,
                  "items": [
                    "**Bundle ID 格式**: 初期使用動態變數，後改為硬編碼（實際問題較預期簡單）",
                    "**拆分設計正確**: 三個組件（Downloader, Modifier, Service）拆分得宜，利於測試和維護",
                    "**無過度設計**: 實作保持簡潔，未引入不必要的複雜度"
                  ]
                }
              ],
              "children": []
            },
            {
              "id": "sec-pv-lessons-reusable",
              "title": "可復用模式/組件",
              "summary": null,
              "blocks": [
                {
                  "id": "blk-pv-lessons-reusable-1",
                  "type": "list",
                  "title": "Notification Service Extension 架構模式",
                  "items": [
                    "組件拆分: Downloader → Modifier → Service",
                    "錯誤處理: 失敗降級機制",
                    "超時管理: 雙層 timeout（5s 下載 + 30s 系統）"
                  ]
                },
                {
                  "id": "blk-pv-lessons-reusable-2",
                  "type": "list",
                  "title": "URLSession 下載處理模式",
                  "items": [
                    "HTTPS-only 驗證",
                    "Timeout 控制",
                    "檔案暫存管理"
                  ]
                },
                {
                  "id": "blk-pv-lessons-reusable-3",
                  "type": "list",
                  "title": "UNNotificationAttachment 建立模式",
                  "items": [
                    "do-catch 錯誤處理",
                    "優雅降級"
                  ]
                }
              ],
              "children": []
            }
          ]
        },
        {
          "id": "sec-pv-suggestions",
          "title": "後續建議",
          "summary": null,
          "blocks": [],
          "children": [
            {
              "id": "sec-pv-suggestions-immediate",
              "title": "立即行動（實機測試前）",
              "summary": null,
              "blocks": [
                {
                  "id": "blk-pv-suggestions-immediate-1",
                  "type": "list",
                  "title": "Apple Developer Portal 配置",
                  "items": [
                    "建立 App ID: `com.cardinalblue.magiceditor.notificationserviceextension`",
                    "啟用 Push Notifications capability",
                    "建立 Development Provisioning Profile"
                  ]
                },
                {
                  "id": "blk-pv-suggestions-immediate-2",
                  "type": "list",
                  "title": "後端協調",
                  "items": [
                    "確認 FCM payload 實際格式",
                    "確認後端發送時已設定 `mutable-content: 1`",
                    "提供測試用圖片 URL"
                  ]
                }
              ],
              "children": []
            },
            {
              "id": "sec-pv-suggestions-testing",
              "title": "實機測試階段 ✅",
              "summary": null,
              "blocks": [
                {
                  "id": "blk-pv-suggestions-testing-list",
                  "type": "list",
                  "title": null,
                  "items": [
                    "✅ 安裝 App 到測試設備",
                    "✅ 執行完整測試套件（正常 + 錯誤場景）",
                    "✅ 記錄測試結果和截圖",
                    "✅ 更新 TASK-005 測試覆蓋清單"
                  ]
                },
                {
                  "id": "blk-pv-suggestions-results",
                  "type": "list",
                  "title": "測試結果 (2025-10-01)",
                  "items": [
                    "✅ 實機推播圖片顯示測試通過",
                    "✅ PNG/JPG/GIF 格式支援驗證通過",
                    "✅ 錯誤場景實機測試通過（HTTP URL, 404, 超時）",
                    "✅ 前景/背景行為驗證通過",
                    "✅ FCM payload 實際格式確認通過"
                  ]
                }
              ],
              "children": []
            },
            {
              "id": "sec-pv-suggestions-future",
              "title": "未來擴充建議",
              "summary": null,
              "blocks": [
                {
                  "id": "blk-pv-suggestions-future-list",
                  "type": "list",
                  "title": null,
                  "items": [
                    "**App Groups 支援**: 如需與主 App 共享圖片快取",
                    "**通知 Action 按鈕**: 自訂操作按鈕",
                    "**圖片大小驗證**: 防止下載過大檔案",
                    "**進階日誌**: 整合 Firebase Analytics 或 Crashlytics"
                  ]
                }
              ],
              "children": []
            }
          ]
        },
        {
          "id": "sec-pv-summary",
          "title": "開發效率總結",
          "summary": null,
          "blocks": [
            {
              "id": "blk-pv-summary-hours",
              "type": "list",
              "title": "預估工時",
              "items": [
                "7 小時"
              ]
            },
            {
              "id": "blk-pv-summary-actual",
              "type": "list",
              "title": "實際工時",
              "items": [
                "~3 小時（程式碼實作 + 實機測試 + bug 修復）"
              ]
            },
            {
              "id": "blk-pv-summary-reasons",
              "type": "list",
              "title": "效率提升原因",
              "items": [
                "Task 拆分清晰，無相依阻塞",
                "使用 task-coding-agent 並行執行 TASK-001~003",
                "Tuist 配置已預先準備",
                "SwiftLint 自動修正節省時間"
              ]
            },
            {
              "id": "blk-pv-summary-factors",
              "type": "list",
              "title": "關鍵成功因素",
              "items": [
                "Blueprint 設計完整，減少返工",
                "組件拆分得宜，易於實作",
                "追溯註解完整，便於未來維護",
                "實機測試發現並修復 URL-encoded 路徑問題"
              ]
            },
            {
              "id": "blk-pv-summary-timeline",
              "type": "list",
              "title": "實作完成時間線",
              "items": [
                "2025-09-30: 完成 TASK-001~004 程式碼實作",
                "2025-10-01: 完成 TASK-005 實機測試與驗證",
                "2025-10-01: 修復檔名解析 bug 並移除 debug logs"
              ]
            }
          ],
          "children": []
        },
        {
          "id": "sec-pv-deploy",
          "title": "生產環境部署狀態 ✅",
          "summary": null,
          "blocks": [
            {
              "id": "blk-pv-deploy-prs",
              "type": "list",
              "title": "Pull Requests",
              "items": [
                "PR #377: iOS NotificationServiceExtension → `dev` branch",
                "PR #378: Backend Push Notification System → `dev` branch"
              ]
            },
            {
              "id": "blk-pv-deploy-checklist",
              "type": "list",
              "title": "部署檢查清單",
              "items": [
                "✅ 程式碼實作完成",
                "✅ 所有測試通過",
                "✅ SwiftLint 驗證通過",
                "✅ Apple Developer Portal 配置完成",
                "✅ Provisioning Profiles 建立完成",
                "✅ 實機測試驗證完成",
                "✅ 文檔更新完成"
              ]
            },
            {
              "id": "blk-pv-deploy-final",
              "type": "paragraph",
              "title": "功能已達到生產環境標準，可以準備上線。",
              "content": ""
            }
          ],
          "children": []
        }
      ]
    }
  ]
}