# MySQL TaskStore command_executions 實作前確認

## 1. 文件目的

本文件整理 `command_executions` 是否要納入 MySQL TaskStore 下一批實作前，必須先確認的資料來源、欄位、索引、寫入策略、測試範圍與停止線。

本文件只做實作前確認，不直接新增 table、不執行 DDL、不連真實 DB、不修改 `ITaskStore` public method 簽章，也不修改 `TaskExecutionRecord` / `TaskNodeExecutionRecord` public constructor 或 property。

## 2. 目前已完成狀態

| 項目 | 狀態 | 說明 |
| --- | --- | --- |
| `task_executions` | 已完成 | 已完成 Save / FindById / ListRecent 與真實 DB manual-only 驗證。 |
| `node_executions` | 已完成 | 已完成 schema、SQL builder、fake gateway、schema precheck、manual-only xUnit 入口與真實 DB 寫入 / 查詢 / cleanup 驗證。 |
| `TaskNodeExecutionRecord` | 已存在 | 可提供 NodeId、DeviceId、CommandName、Status、RetryCount、DataSummary、MetadataSummary，但沒有 RawRequest / RawResponse / AdapterTimeTakenMs。 |
| `WorkflowNodeExecutionSnapshot` | 已存在 | 可提供 RawRequestSummary、RawResponseSummary、AdapterTimeTakenMs、DataSummary、MetadataSummary，但目前只在 Workflow callback 當下使用。 |
| `LogEntry` | 已存在 | 已承接 TaskId、NodeId、DeviceId、CommandName、RawRequestSummary、RawResponseSummary、AdapterTimeTakenMs、ErrorCode、Message。 |
| `MySqlTaskStore` | 已完成 Task / Node | 目前負責任務主檔與節點摘要，不負責完整 log sink。 |

## 3. 為什麼不能直接照 node_executions 做

`node_executions` 可以直接由 `TaskExecutionRecord.NodeRecords` 寫入，資料來源已穩定。

`command_executions` 不同，因為它如果只寫 `TaskNodeExecutionRecord.CommandName`，會跟 `node_executions` 幾乎重複，沒有真正保存命令診斷價值；但如果要保存 RawRequest / RawResponse，現有 `TaskNodeExecutionRecord` 又沒有這些欄位。

因此 `command_executions` 的第一個決策不是「先建表」，而是先決定資料來源。

## 4. 資料來源候選

| 來源 | 可取得內容 | 優點 | 風險 / 限制 | 建議 |
| --- | --- | --- | --- | --- |
| `TaskNodeExecutionRecord` | TaskId、NodeId、DeviceId、CommandName、Status、RetryCount、DataSummary、MetadataSummary | `MySqlTaskStore.Save` 已可直接取得，不需改資料流 | 沒有 RawRequest / RawResponse / AdapterTimeTakenMs，命令表會與 node table 重複 | 不建議作為完整 `command_executions` 第一版來源。 |
| `WorkflowNodeExecutionSnapshot` | RawRequestSummary、RawResponseSummary、AdapterTimeTakenMs、DataSummary、MetadataSummary | 最接近 Adapter 命令完成瞬間，診斷資訊最完整 | 目前 TaskStore 拿不到；若要保存需新增資料流或擴充任務紀錄模型 | 建議作為命令明細的主要資料來源，但需先確認是否可調整資料模型。 |
| `LogEntry` | RawRequestSummary、RawResponseSummary、AdapterTimeTakenMs、Message、ErrorCode | 已是 Log 流，符合診斷資料定位 | 屬 `ILogWriter` 職責，不應塞進 `MySqlTaskStore.Save` | 若目標是保存 Raw 診斷，建議走 DB LogWriter / TraceStore。 |

## 5. 建議決策

建議不要把 `command_executions` 直接塞入目前 `MySqlTaskStore.Save(record)`。

原因：

1. `TaskStore` 的責任是任務主檔與節點摘要，不是完整命令診斷流。
2. RawRequest / RawResponse 可能包含敏感資訊或大量 payload，需要遮蔽與長度限制。
3. 一個 Workflow node 未來可能執行多個 Adapter command，不能假設 node 與 command 永遠一對一。
4. 若為了命令明細修改 `TaskNodeExecutionRecord` 或 `TaskExecutionRecord` public model，屬於停止線，必須先確認。

建議採兩段式：

| 階段 | 目標 | 做法 |
| --- | --- | --- |
| 第一段 | 固定 `command_executions` 欄位草案與資料來源 | 先完成本文件與後續設計，不寫程式。 |
| 第二段 | 若需要 Raw 診斷，先設計 DB LogWriter / TraceStore | 從 `LogEntry` 或 Workflow snapshot 承接，不污染 TaskStore。 |

## 6. 若仍要建立 command_executions，建議 table 定位

`command_executions` 應定位為「Adapter / Device command 執行明細」，不是任務主檔，也不是一般 log table。

| Table | 職責 |
| --- | --- |
| `task_executions` | 任務主檔與狀態摘要。 |
| `node_executions` | Workflow 節點歷程摘要。 |
| `command_executions` | 設備命令執行明細，包含裝置、命令、耗時、Raw 摘要與錯誤摘要。 |
| `task_log_traces` | 詳細 log / trace，建議由 DB LogWriter / TraceStore 管理。 |

## 7. 欄位草案

以下只是欄位草案，尚未同意前不得建立 table。

| 欄位 | 型別 | 必填 | 建議來源 | 說明 |
| --- | --- | --- | --- | --- |
| `task_id` | `varchar(64)` | 是 | `WorkflowNodeExecutionSnapshot.TaskId` 或 `LogEntry.TaskId` | 任務識別碼。 |
| `sequence_no` | `int` | 是 | 寫入端產生 | 任務內命令順序，由 1 開始。 |
| `node_id` | `varchar(64)` | 是 | Snapshot / LogEntry | Workflow 節點識別碼。 |
| `node_name` | `varchar(128)` | 否 | Snapshot / LogEntry | Workflow 節點名稱。 |
| `device_id` | `varchar(64)` | 否 | Snapshot / LogEntry | 設備識別碼。 |
| `command_name` | `varchar(128)` | 否 | Snapshot / LogEntry | Adapter 命令名稱。 |
| `status` | `varchar(32)` | 是 | Snapshot / LogEntry | 命令結果狀態。 |
| `retry_count` | `int` | 是 | Snapshot / LogEntry | 節點執行時的 retry count。 |
| `executed_at` | `datetime` | 是 | Log timestamp 或寫入當下 | 第一版若無 command start/end，只記錄完成或寫入時間。 |
| `node_time_taken_ms` | `int` | 否 | Snapshot / LogEntry | 節點整體耗時。 |
| `adapter_time_taken_ms` | `int` | 否 | Snapshot / LogEntry | Adapter 命令耗時。 |
| `error_code` | `varchar(64)` | 否 | Snapshot / LogEntry | 命令失敗錯誤代碼。 |
| `message` | `varchar(255)` | 否 | Snapshot / LogEntry | 命令結果訊息摘要。 |
| `data_summary` | `text` | 否 | Snapshot / LogEntry | Adapter Data 摘要。 |
| `raw_request_summary` | `text` | 否 | Snapshot / LogEntry | RawRequest 安全摘要，不保存未遮蔽完整 payload。 |
| `raw_response_summary` | `text` | 否 | Snapshot / LogEntry | RawResponse 安全摘要，不保存未遮蔽完整 payload。 |
| `metadata_summary` | `text` | 否 | Snapshot / LogEntry | Metadata 摘要。 |
| `created_at` | `datetime` | 是 | 寫入當下 | 紀錄建立時間。 |

## 8. 主鍵與索引草案

建議第一版使用：

```text
PRIMARY KEY (task_id, sequence_no)
```

建議索引：

| Index | Columns | 用途 |
| --- | --- | --- |
| `idx_command_executions_task_id` | `task_id` | 查詢指定任務命令明細。 |
| `idx_command_executions_node_id` | `node_id` | 查詢特定節點的命令紀錄。 |
| `idx_command_executions_device` | `device_id` | 查詢設備命令歷程。 |
| `idx_command_executions_command` | `command_name` | 查詢特定命令歷程。 |
| `idx_command_executions_status` | `status` | 查詢失敗命令。 |
| `idx_command_executions_executed_at` | `executed_at` | 依時間排序與清理資料。 |

## 9. 寫入策略草案

在資料來源確認前，不建議實作。

若未來確認由 Workflow snapshot 或 LogEntry 寫入，建議：

1. 每個 command 事件產生一筆 `command_executions`。
2. `sequence_no` 在同一 `task_id` 內遞增。
3. 寫入失敗不可影響 Workflow 執行結果，但必須記錄錯誤。
4. RawRequest / RawResponse 必須先經過摘要、遮蔽與長度限制。
5. 若與 `task_executions` / `node_executions` 同 transaction，需先確認是否接受命令明細寫入失敗導致整筆 task save 失敗。

## 10. 第一版不建議做的事

| 項目 | 不建議原因 |
| --- | --- |
| 直接由 `TaskNodeExecutionRecord` 寫 `command_executions` | 只能重複 node 摘要，無法保存 Raw 診斷。 |
| 為了 RawRequest / RawResponse 直接改 `TaskNodeExecutionRecord` public constructor | 會改 public contract，需先確認。 |
| 把 `command_executions` 跟 `node_executions` 一起塞入 `MySqlTaskStore.Save` | 職責可能過大，且一個 node 未來可能多 command。 |
| 保存完整 RawRequest / RawResponse | 可能包含敏感資訊或超大 payload。 |
| 一般 CI 執行真實 DB command 寫入測試 | 真實 DB 寫入必須 manual-only gate。 |
| 自動建立 `command_executions` table | DDL / ALTER TABLE 屬高風險操作，需另行確認。 |

## 11. 測試策略建議

### 11.1 設計階段測試

| 測試 | 目的 |
| --- | --- |
| schema class parse 測試 | 確認欄位、主鍵、索引與 comment 可由 C# class 解析。 |
| SQL generator 測試 | 確認 MySQL 5.6.2 建表 SQL 可產生。 |
| payload sanitizer 測試 | 確認 RawRequest / RawResponse 摘要會遮蔽敏感資訊並截斷長度。 |

### 11.2 實作階段測試

需先決定資料來源後才能寫測試：

| 資料來源 | 測試方向 |
| --- | --- |
| Workflow snapshot | 驗證 snapshot 可轉 command row，且不遺失 Raw summary / AdapterTimeTakenMs。 |
| LogEntry | 驗證 DB LogWriter / TraceStore 可寫入 command row，且不影響 FileLogWriter / ConsoleLogWriter。 |
| TaskStore | 只有在使用者接受模型調整後，才測 `MySqlTaskStore.Save` 是否保存 command rows。 |

### 11.3 manual-only 真實 DB 驗證

若後續完成第一版實作，真實 DB 驗證仍需另列節點：

1. 確認測試 DB，不使用正式 DB。
2. 確認 `command_executions` table、index、comment 已存在。
3. 明確開啟 manual-only gate。
4. 寫入至少兩筆 command rows，包含成功與失敗各一筆。
5. 查回 `task_id` 下的 command rows。
6. 驗證 RawRequest / RawResponse 已遮蔽與截斷。
7. cleanup 只清除本次 `manual-taskstore-poc-` 前綴資料。

## 12. 必須停下確認的事項

以下任一項出現時，Codex 必須停下，不得自動執行：

1. 修改 `ITaskStore` public method 簽章。
2. 修改 `TaskExecutionRecord` / `TaskNodeExecutionRecord` public constructor 或 property。
3. 新增正式 `command_executions` table 或執行 DDL。
4. 對真實 DB 執行未經確認的 `INSERT`、`UPDATE`、`DELETE`。
5. 把真實 DB 測試加入一般 CI。
6. 保存或輸出 password、完整 connection string、Token。
7. 保存未遮蔽完整 RawRequest / RawResponse。
8. 導入 WebApi、ServiceHost、背景服務或正式 DB apply 啟動流程。
9. 新增或更換資料庫套件。

## 13. 建議結論

`command_executions` 建議先不要直接實作進 `MySqlTaskStore.Save`。

較安全的下一步是先做「DB LogWriter / TraceStore 邊界設計」，確認 RawRequest / RawResponse 應由 Log 流保存，還是要擴充 TaskStore 模型。

若使用者確認仍要先做 `command_executions`，建議下一份文件先整理「command_executions 資料來源決策表」，在三個方案中選一個：

1. `TaskNodeExecutionRecord` 簡版命令表，不保存 Raw 診斷。
2. `WorkflowNodeExecutionSnapshot` 命令明細表，需新增資料流。
3. `LogEntry` / DB LogWriter 命令明細表，與 TaskStore 分離。

本次建議採第 3 種方向優先分析，避免把詳細診斷資料塞進 TaskStore。
