---
title: "Agent 主控台意見回饋 — 設計文件 (FR-192)"
brand: "Guidant AI · **FR-192** Agent 主控台意見回饋"
eyebrow: "FR-192 · 設計文件 v1.0 · 2026-11-09（D1～D8 定案；13 張開發卡 4 批，收口首腦做）"
h1: "Agent 主控台意見回饋 — 設計文件 v1.0"
subtitle: "在 Agent 主控台新增、瀏覽、修改、刪除自己的回饋；與主系統的回饋是同一份資料，兩邊都能改，以較新的為準"
lede: "本文件把 [討論稿 v4](discussion.md) 的八項定案落成可派工的設計：Agent 新開 `feedback` 模組照既有「本機先存、連上再補送」機制長；主系統多一支收單端點把 Agent 來的回饋建成 jedi-issue 的正常回饋並標來源；附件留在 Agent、主系統只記參照；修改與刪除兩邊都能做，Agent 到主系統走同一條待回傳佇列，主系統到 Agent 走既有設定通道回拉，衝突以較新者整筆為準。附件區趁這次抽成共用元件，主控台與主系統回饋表單一起換。原廠回覆（helpdesk）不在本期。"
chips: [
  {text: "D1～D8 定案", kind: ok},
  {text: "13 張開發卡 4 批；驗收／發版／改回釘版不開卡，合回後首腦做", kind: accent},
  {text: "四套件要發版：jedi-agent-protocol 0.6.0、jedi-issue 1.6.0、jedi-remote-agent（014）、jedi-iam（patch）", kind: accent},
  {text: "雛形：prototype/index.html（純畫面，給 runner）", kind: accent},
  {text: "陷阱：local-admin 不改名；manifest.tsv 只讓 M-3 碰；回拉只通知那一台", kind: crit}
]
footer: "FR-192 · 設計文件 v1.0 · 2026-11-09 · 討論稿：[discussion.md](discussion.md) · 沿革見 LOG 與 git log"
---

> 狀態：**v1.0 定案、已開卡**｜日期：2026-11-09｜母卡 [CM-3781](https://app.notion.com/p/FR-192-Agent-Agent-16-7-413346da4cd081cbb006f230bb834c9a)、子卡 CM-3782～CM-3797｜前作 FR-099（回饋整組進 jedi-issue）、FR-166（Agent 主控台）、FR-181（長輪詢與設定通道）、FR-166.1（主控台下載能力點）
> 開發 branch：四個 repo（main、fe、agent、jedi-packages）目前皆在 `feature/FR-172`；runner 開工 `git branch --show-current` 再確認，不對就停下問，不切

> 🔴 **這份是設計決策，不是現況。** 上線後的畫面與 API 現況看 SPEC 頁。定稿後若設計有異動，開 `design-v1.1.md`，本檔不改。

## 變更紀錄 {#changelog nav="變更紀錄"}

本檔每版一行；設計異動開新版檔，不改舊版。

- **2026-11-09 v1.0** 初版：討論稿 v4 八項定案落地；16 張卡 7 批，`handoff/cards.py` 兩個斷言過、零撞檔。同日開卡：母卡 CM-3781，子卡 CM-3782～CM-3797。
- **2026-11-11 v1.0 勘誤**：V-1／Z-1／Z-2 三張收口卡撤下（工作坊「這張不用做」），改為 §7 合回後收口清單由首腦做；拆分表 13 張 4 批。
- **2026-11-10 v1.0 勘誤**（設計不變、派工前提與座標更正，不開新版）：protocol 版號 0.7.0；jedi-issue 要接 `enrich`；remote-agent migration 號開工時算；Agent 設定類存 JSON 檔不是 kv；所有卡以 FR-190／FR-188 合回後為基底；E-1、P-1 重做。七張卡已 append 修正段。

## 1. 需求背景與端到端 {#background nav="背景"}

客戶的人多半在 Agent 主控台上操作，碰到問題沒地方講：回饋只能在主系統網頁送，Agent 斷線時連主系統都開不了。主系統的回饋功能已完整（新增、瀏覽、編輯、刪除、我的回饋、檢視全部），整組在 jedi-issue 套件；Agent 主控台什麼都沒有。背景細節、現況數字、既有機制的來龍去脈在討論稿第一節，這裡只放做完之後的樣子。

### 做完之後

Agent 主控台多一頁「意見回饋」，掛「作業」群組最下方，登入即可用。按「新增回饋」開對話框，欄位與主系統回饋表單完全相同（標題、類別、功能、內文、檔案上傳），功能固定「Agent 主控台」；儲存後清單立刻一列「待回傳」，連上主系統幾秒內變「已送達」。點任一列開「瀏覽回饋」對話框，可編輯、可刪除。離線一樣能用，連回自動補送。

主系統「系統回饋」與「我的回饋」兩頁多一欄「來源」，Agent 來的顯示「Agent · 名稱」，可篩選。送件人登入主系統在「我的回饋」看得到自己在 Agent 送的那筆，可以改、可以刪，改了 Agent 那邊跟著變。附件存在 Agent，主系統明細可預覽可下載（由主系統向 Agent 取檔）。本機管理員送的，建立人員顯示「本機管理員」，由持有「檢視全部回饋」的人代管。

```{.mermaid cap="圖 1 — 新增一筆回饋的路。修改與刪除走同一條佇列多帶一個動作欄位；主系統改了則走設定通道回拉（§5.4）。"}
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
sequenceDiagram
    participant U as 使用者（主控台）
    participant A as Agent
    participant M as 主系統
    participant I as jedi-issue
    U->>A: 新增回饋（multipart：json＋files）
    A->>A: 附件 blob_ops.save_upload；feedbacks 落一筆；outbox 入列 op=create
    A-->>U: 清單「待回傳」
    Note over A,M: 心跳 observer 觸發 outbox.drain
    A->>M: POST /agents/feedbacks {items:[…]}（控制面通行證）
    M->>M: 驗 created_by；每個 Agent 檔 register_agent_blob()
    M->>I: add_feedback(labels=[類別, Agent 主控台], attached_file_uids, source=agent)
    I-->>M: feedback uid
    M->>M: agent_feedbacks 對照表 (agent_id, local_uid) 唯一
    M-->>A: {accepted, inserted, rejected}
    A->>A: feedbacks.synced_at、cloud_uid
    A-->>U: 清單「已送達」
```

圖外兩件事：主系統 `rejected` 時 Agent 把原因寫進該筆、不再重送；同一筆的 create→update→delete 在 outbox 保序，前一筆沒送成後面不送。

## 2. 分工概述 {#overview nav="概述"}

七批，每批決策者怎麼檢查：

1. **前置**（E-1、P-1、P-2）。主系統改走四套件本機路徑；契約常數；jedi-iam 保留字。檢查：主系統起得來、import 路徑在 `.worktrees/`；建帳號叫 `local-admin` 被擋。
2. **看得見的**（A-1、I-1、F-1）。Agent 模組與主控台 API；jedi-issue 兩欄與參數；共用附件元件先換進主系統回饋表單。檢查：主系統回饋表單的附件區變成一個拖放區；DEV `feedback_issues` 多兩欄。
3. **接起來**（M-1、M-3、A-2、M-2）。主系統來源欄顯示與 seed；收單端點；主控台回饋頁；主系統兩頁來源欄。檢查：主控台新增一筆幾秒內「已送達」，主系統「系統回饋」看到那筆、來源「Agent · 名稱」；斷線新增再連上自動送達。
4. **改刪與回拉**（A-3、M-4、A-4）。檢查：Agent 改標題主系統變；主系統改內文 Agent 變；主系統刪 Agent 消失；Agent 離線改、主系統同時改、Agent 連回後顯示主系統版本。
5. **端到端驗收**（V-1）。真 Agent 跑 §7 十二步。
6. **結案套件側**（Z-1）。四套件發版。
7. **結案主專案側**（Z-2）。改回釘版。

**決策者功課**：批 3 與批 4 驗收要在一台真 Agent 上斷線試；Z-1 發版前看一眼四個版號。

## 3. 決策定案（D1～D8） {#decisions nav="決策定案"}

八項的定案、理由、被排除方案在討論稿第四節，每項獨立一段，這裡不重抄。只列定案一句與落到哪張卡：

- **D1** 來源兩欄放 jedi-issue 回饋表，Agent 名稱主系統補 → I-1、M-1。
- **D2** `local-admin` 不改名、直接當建立人員存、列保留字、由持「檢視全部回饋」者代管 → P-2、I-1（歸屬例外）、M-1（暱稱）、M-3（放行）。
- **D3** Agent 端「自己的」以登入身分比對；本機管理員看這台所有本機管理員送的 → A-1。
- **D4** 類別選項走設定通道下發 → P-1、A-1、M-3。
- **D5** 功能標籤「Agent 主控台」seed，Agent 來的自動填 → M-1、M-3。
- **D6** 雙向：Agent 改刪走佇列帶 op；主系統改刪走設定通道回拉；衝突以較新者整筆為準 → A-1（保序）、M-3（op、stale）、A-3、M-4、A-4。
- **D7** 附件區一個拖放區，共用元件，兩邊都換 → F-1、A-2、M-2。
- **D8** helpdesk 不碰，只用既有 `open` 狀態 → 無卡。

## 4. 現況接入點 {#inventory nav="現況盤點"}

**直接用的**：Agent `files/app/blob_ops.save_upload`（附件與證據檔同一套）；主系統 `RemoteAgentAdapter` 從 Agent 取檔與簽票下載；jedi-issue `SchemaExtensions.response_enricher`（空殼掛點第一次有人接）；「Agent 名稱雙向以較新者為準」的衝突規則；jedi-issue 既有 `issues.state=open`。

**加參數或加一段的**：jedi-issue `add_feedback`／`update_feedback`（`attached_file_uids`、`source`、`source_ref`）、`feedback_issues` 表（兩欄）、`_assert_owner`（local-admin 例外）；主系統 `RemoteAgentAdapter._add_reference` 抽成公開 `register_agent_blob()`；主系統 `_agent_config_sections.py`（兩類）與 `_agent_config_notify._WATCHED`（四張表）；Agent `sync/app/outbox.py`（一種 kind、保序、stale）；jedi-iam 建帳號驗證（保留字）；主系統回饋兩頁與表單。

**新開但照既有形狀**：Agent `guidant_agent/feedback/` 照 `scan/app/local_runs.py` 與 `console/api/runs_routes.py`；主系統 `agent_feedback_route.py`／`agent_feedback_intake_service.py` 照 `agent_manual_scan_route.py` 與 jedi-remote-agent `local_run_service.ingest`；主控台 `Feedback.vue` 照 `Runs.vue`；`AttachmentDropList.vue` 拖放照 `SourceFilePicker.vue`。

## 5. 詳細設計 {#design nav="詳細設計"}

### 5.1 Agent `feedback` 模組（A-1、A-3）

依賴方向 `console → feedback → platform`；透過 `sync` 的 outbox port 入列，不 import `sync` 的 adapters；SQL 只在 `adapters/feedback_repo.py`。

**表 `feedbacks`**（新版號一步，照 `console/migrations.py` 寫法，列舉值不下 CHECK）：

| 欄 | 型別 | 說明 |
|---|---|---|
| `uid` | VARCHAR(64) PK | Agent 端編號，送主系統當 `local_uid` |
| `title` | VARCHAR(255) | |
| `description` | TEXT | TipTap 產的 HTML，與主系統同 |
| `type_label_uid`、`type_label_name` | VARCHAR(50)、VARCHAR(100) | 名稱一併存，離線拉不到標籤時清單仍顯示 |
| `created_by` | VARCHAR(128) | session sub；本機管理員為 `local-admin` |
| `created_at`、`updated_at`、`deleted_at` | DATETIME | 軟刪 |
| `files` | TEXT JSON | `[{uid, name, size, mimetype, sha256, where: agent｜cloud}]` |
| `cloud_uid`、`cloud_updated_at` | VARCHAR(50)、DATETIME | 主系統回饋編號與主系統端更新時間（回拉覆寫的比較基準） |
| `synced_at`、`sync_error` | DATETIME、TEXT | 同 `local_runs` |

**app 層**：`create／update／delete／remove_file(uid, …, actor)` 寫表並 `outbox.enqueue(KIND_FEEDBACK, uid, op, payload)`；`list_mine(sub)`、`get(uid, sub)` 非本人一律 404；`record_sync(conn, uids, error)` 簽名照 `local_runs`；`apply_cloud(items)` 是設定類 `feedbacks` 的套用者，`cloud_updated_at` 較新者覆寫本機、`deleted` 標 `deleted_at`，被覆寫記事件碼。

**outbox**：`KIND_FEEDBACK` 進 `_NEVER_DROP`、`_ORDERED`、`SENDABLE`；`_post_feedbacks` 照 `_post_local_runs` 批次送 `FEEDBACKS_PATH`；回應 `accepted` settle、`rejected` 寫 `sync_error`、`stale` 寫「主系統已有較新版本」並 `config_pull.request()`。

**主控台 API**（掛 console blueprint，登入即可、不綁 scope，body 上限沿用主控台既有限制）八條：清單只回本人的並附同步狀態；單筆非本人一律 404；新增與修改收 multipart（一個 JSON 欄位帶標題、內文、類別編號，加多個檔案欄位），修改另收要拿掉的附件編號清單；刪除是軟刪並入列；刪單一附件會同時刪本機 blob 並入列一筆修改；立即重試照執行紀錄頁那支，離線回 409；類別選項讀設定類快取。路徑一律 `/console/api/feedbacks…`，與主系統 `/feedback` 端點同形狀，runner 開工照 `console/api/runs_routes.py` 的寫法。

### 5.2 契約（P-1）

`tasks.py`：`FEEDBACKS_PATH = "/api/1.0/agents/feedbacks"`、`FEEDBACK_OPS = ("create","update","delete")`、`FEEDBACK_ITEM_KEYS`、`FEEDBACK_FILE_KEYS`、`FEEDBACKS_MAX_BATCH = 50`。`config.py`：`KIND_FEEDBACK_LABELS`、`KIND_FEEDBACKS`。每筆 payload：

```json
{"local_uid": "…", "op": "create|update|delete",
 "title": "…", "description": "<p>…</p>", "type_label_uid": "…",
 "created_by": "wang.auditor", "created_at": "…", "updated_at": "…",
 "base_cloud_updated_at": "…（update 時帶；主系統比它新回 stale）",
 "files": [{"agent_file_uid": "…", "file_name": "…", "size": 0, "mimetype": "…", "sha256": "…"}],
 "remove_file_uids": ["…（update 時要拿掉的主系統附件 uid）"]}
```

### 5.3 jedi-issue（I-1）與 jedi-iam（P-2）

`feedback_issues` 加 `source VARCHAR(20) NOT NULL DEFAULT 'web'`、`source_ref VARCHAR(50)`，索引 `(source, source_ref)`。`add_feedback`／`update_feedback` 加 `attached_file_uids`，有值時不走 `upload_attachment`、直接 `add_issue_file_mapping`；建回饋另收 `source`／`source_ref`。`_assert_owner` 加例外：`source == "agent" and created_user == "local-admin"` 且呼叫者持 `feedback_read_all_capability` → 視為擁有者。新 `list_by_source(source, source_ref)`。`FeedbackResponse` 吐 `source`／`source_ref`；`FeedbackQueryRequest` 收 `source`。清單與單筆回應改先過 `schema_extensions.enrich()`——這個掛點是空殼、從沒被呼叫，不接的話主系統的 enricher 不會生效。刪除語意不改（硬刪），「已不存在」由主系統對照表告訴 Agent。外部整合（GitLab／GitHub）附件傳 None。

jedi-iam：保留字檢查抽成一支，建帳號、改帳號名、LDAP／AD 同步建帳號、租戶佈建四個寫入點都呼叫；只擋建帳號不夠。另附唯讀查詢確認既有帳號無撞名（DEV 開卡時 0 筆）。

### 5.4 主系統（M-1、M-3、M-4）

**收單端點** `POST /agents/feedbacks`，`@agent_pass_required`，body `{items:[…]}` 1～50。收單服務在 `agent_tenant_context` 內逐筆：

1. 驗 `created_by`（body 帶來的，不可信）：只准 `local-admin`，或**這台 Agent 有效離線操作者名單**內的 login_name（名單由主系統下發給這台，等於用通行證錨定「誰能在這台主控台登入」）；同租戶但未被授權這台的人也拒收。不用「是這租戶的啟用使用者」當判準，那會讓一台 Agent 替任何同租戶的人送件。
2. 依 `op`：目標一律由對照表以 `(agent_id, local_uid)` 反查，不收 body 的回饋編號，查不到 `rejected not_found`；`create` 撞唯一鍵即 accepted 不重建；`update` 比 `base_cloud_updated_at` 與主系統 `updated_at`，主系統較新回 `stale`；`delete` 走 `delete_feedback`，身分就是送件人（本機管理員那筆建立人員本來就是 `local-admin`，同名身分過既有歸屬檢查，不另開繞過）；成功後對照表寫 `deleted_at`。
3. `files` 每項 `register_agent_blob()` 建 `upload_files` 參照（`storage_type=remote_agent`、`save_file_name=agent_file_uid`、`path=agent.base_url`、`sha256` 用 Agent 報的、`created_user=created_by`、`storage_scope=customer`）；`remove_file_uids` 走既有刪附件。
4. 呼叫 jedi-issue，`labels=[type_label_uid, AGENT_CONSOLE_FUNCTION_LABEL_UID]`、`source="agent"`、`source_ref=agent.uid`；對照表 `agent_feedbacks` 寫一列。

**對照表** `compliance.agent_feedbacks(id, uid, tenant_id, org_unit_id, agent_id, local_uid, feedback_uid, deleted_at, created_at, updated_at)`，`(agent_id, local_uid)` 唯一，RLS 與 GRANT 照 `agent_local_runs`；jedi-remote-agent migration 號＝開工時套件目錄最大號加一（FR-188 合回後至少 022），主專案 `scripts/sql/packages/jedi_remote_agent/` 鏡像同號。

**來源欄顯示**：`core/plugins/issue.py` 傳 `SchemaExtensions(response_enricher=…)`；enricher 對 `source == "agent"` 的列補 `source_name`（`remote_agents.name`），對 `created_user == "local-admin"` 補 `created_user_name="本機管理員"`。

**設定通道**：`feedback_labels` provider 回 `FEEDBACK_TYPE` 標籤 `[{uid, name}]`；`feedbacks` provider 回該 Agent `list_by_source` 的結果加對照表 `deleted` 列，每項 `{local_uid, feedback_uid, title, description, type_label_uid, files:[{uid,name,size,where}], updated_at, deleted}`；版本皆內容雜湊。`_agent_config_notify._WATCHED` 加 `feedback_issues`／`issue_upload_files`／`issue_label_mapping` → `KIND_FEEDBACKS`（**只通知 `source_ref` 那一台**，照 `remote_agents.name` 單台通知形狀，不可全租戶）、`labels` → `KIND_FEEDBACK_LABELS`。

**seed**：主線 `phase=seed` migration 補 `labels(scope=FUNCTION, name='Agent 主控台')`，uid 固定寫在 `common/code/feedback_source.py`。

### 5.5 前端（F-1、A-2、M-2、A-4）

`AttachmentDropList.vue`（`src/components/common/`）：props `existing[]`、`pending[]`、`accept`、`maxSize`、`canDownload`、`canDelete`、`readonly`；emits `add`、`remove-pending`、`remove-existing`、`preview`、`download`。一個拖放區，兩類檔同清單，每列 hover 出動作，`where=cloud` 顯示「存於主系統」。套全站 `dark-card` 與字級，不自訂。

主控台 `Feedback.vue`：表格與回傳狀態照 `Runs.vue`；新增／編輯同一個對話框，欄位照 `SystemFeedbackForm.vue`，內文用 `@/views/project-summary-report/TipTapEditor.vue`；瀏覽對話框底部「刪除」「關閉」「編輯」，回傳失敗頂部原因與「立即重試」。文案依 i18n 既有用語重寫，不從雛形複製；驗收 `grep -rn "本案新增\|審稿\|D[0-9]\|場景"` 為 0。

主系統兩頁加「來源」欄與 `SegmentButtons`（全部／網頁／Agent）；表單來源為 agent 時頂部 `hint-bar--info` 一列。

### 5.6 安全邊界

收單端點只認通行證，body 身分欄位一律不採信；`created_by` 必須是該租戶的人；附件 SHA-256 記下不當驗過，下載時既有完整性檢查重算；標題 150、內文上限沿用主系統，超長拒收；Agent 端走主控台 session 與 CIDR，附件解析沿用 `files` 模組檔頭檢查與大小上限。回拉只通知該 Agent，不會把別台的回饋內容送出去。

## 6. 拆分（13 張開發卡、4 批） {#breakdown nav="拆分"}

**批（Stage）＝工作坊同時開幾個小弟一起跑的那一組**：同一批不改同一個既有檔、不依賴對方產出。底表在 [`handoff/cards.py`](handoff/cards.py)，兩個斷言已過、零撞檔。批 1＝E-1、P-1、P-2；批 2＝A-1、I-1、F-1；批 3＝M-1、M-3、A-2、M-2；批 4＝A-3、M-4、A-4；批 5＝V-1；批 6＝Z-1；批 7＝Z-2。

**登記型檔只讓一張卡碰**：`scripts/sql/manifest.tsv` 由 M-3 一次登記 014 鏡像與 M-1 的 seed（M-1 不碰）；`sync/app/outbox.py` 由 A-1 加 kind、A-3 只加 stale 分支（不同批）；console `router.js` 只有 A-2 碰；i18n 各卡各自新開檔。

**套件版號**：jedi-agent-protocol 0.6.0（P-1）、jedi-iam patch（P-2）、jedi-issue 1.6.0（I-1）、jedi-remote-agent 新 migration 014 隨 M-3 bump minor；Z-1 統一發版。

| 批 | 階段 | 卡 | repo | reuse／extend／new＋理由 | 改哪些既有檔 | 新開哪些檔 | 依賴 | 驗收 | model／effort：理由 |
|---|---|---|---|---|---|---|---|---|---|
| 1 | 前置 | **E-1** 環境卡：主系統開發期改走四套件本機路徑（jedi-agent-protocol／jedi-issue／jedi-remote-agent／jedi-iam）；FR-190／FR-188 合回後重做，BE 工作區共用 .venv 要先處理 | BE | reuse：既有 poetry path dependency 形式（pyproject.toml 第 132～135 行註解掉的那組）；指向各套件工作坊工作區 `.worktrees/<母卡號>/<套件>` | `pyproject.toml（不 commit，`git update-index --assume-unchanged`）` | （無） | — | `python main.py` 起得來；`python -c 'import jedi_issue,jedi_agent_protocol,jedi_remote_agent,jedi_iam;print(*(m.__file__ for m in (jedi_issue,jedi_agent_protocol,jedi_remote_agent,jedi_iam)),sep=chr(10))'` 四行都在 `.worktrees/` 底下；`git status` 不見 pyproject.toml | sonnet／medium：機械性：改四行、poetry update 指定套件名、驗 import 路徑 |
| 1 | 前置 | **P-1** jedi-agent-protocol：回饋端點路徑、動作列舉、鍵名清單、兩個設定類；0.6.0 → 0.7.0（0.6.0 已被 FR-190 發版用掉） | jedi 套件 | extend：照 `tasks.py` 既有 `LOCAL_RUNS_PATH` 與 `config.py` 既有 KIND_* 常數形狀加項，不另開檔 | `jedi-agent-protocol/jedi_agent_protocol/tasks.py（FEEDBACKS_PATH、FEEDBACK_OPS、FEEDBACK_ITEM_KEYS、FEEDBACK_FILE_KEYS、FEEDBACKS_MAX_BATCH）`、`jedi-agent-protocol/jedi_agent_protocol/config.py（KIND_FEEDBACK_LABELS、KIND_FEEDBACKS 進 ALL_KINDS）`、`jedi-agent-protocol/jedi_agent_protocol/__init__.py（export）`、`jedi-agent-protocol/pyproject.toml` | （無） | — | 以 FR-190 合回後的 0.6.0（含 console_scopes）為底；套件 tests 綠；`python -c 'import jedi_agent_protocol as p; assert p.FEEDBACKS_PATH and p.CONSOLE_SCOPES'` 過；`tests/test_mirror.py` 三邊綠；版號 0.7.0 | sonnet／medium：純契約常數；一次定完兩端都照它寫 |
| 1 | 前置 | **P-2** jedi-iam：保留字 `local-admin` 在每一條寫入帳號名的路徑都擋（建、改名、匯入、批次匯入、LDAP／AD 同步、佈建）；1.11.2 → 1.11.3 | jedi 套件 | extend：保留字檢查抽成 domain 一支 `assert_login_name_not_reserved()`，建帳號、改帳號名、LDAP／AD 同步建帳號、租戶佈建四個寫入點都呼叫它（不是只在 `verify_login_name_duplicate` 旁加一行）；錯誤碼走既有 login_name 不合法那顆 | `jedi-iam/jedi_iam/app/service/user_service.py 158（建）、273～323（update_user）`、`jedi-iam/jedi_iam/app/service/import_user_service.py 172`、`jedi-iam/jedi_iam/app/service/user_batch_import_service.py`、`jedi-iam/jedi_iam/app/service/tenant_provisioning_service.py`、`jedi-iam LDAP／AD 同步建帳號處（FR-188 合回後再 grep 一次）`、`jedi-iam/pyproject.toml（1.11.2 → 1.11.3，在 FR-188 之上）` | `jedi-iam/jedi_iam/domain/.../reserved_login_names.py（集合＋檢查函式）` | — | 套件 tests：建、改名、LDAP 同步、佈建四條路徑給 `local-admin`（含大小寫變體）都拋既有不合法錯誤；其他名字不受影響；突變（把檢查拿掉任一路徑要紅）；回寫附 DEV 唯讀查詢 `select count(*) from public.users where lower(login_name)='local-admin'`（開卡時為 0） | sonnet／low：四個寫入點一起擋才算擋；漏一條就是資安審查指的碰撞洞 |
| 2 | 看得見的 | **A-1** Agent 新模組 feedback：表、新增／清單／單筆、主控台 API、outbox 類型與保序、類別選項套用者 | evidence-agent | new（新模組，照型）：表欄位與 app 函式簽名照 `scan/app/local_runs.py`（`_insert`／`record_sync`／`list_page`／`get`）；console API 照 `console/api/runs_routes.py`（清單、404 不分查無與非本人、resend）；附件 reuse `files/app/blob_ops.save_upload`；extend：outbox 加 KIND_FEEDBACK 進 `_NEVER_DROP`／`_ORDERED`／`SENDABLE`，批次送 FEEDBACKS_PATH 照 `_post_local_runs` | `guidant_agent/sync/ports/result_outbox.py（KIND_FEEDBACK）`、`guidant_agent/sync/app/outbox.py（三個集合＋`_post_feedbacks`）`、`guidant_agent/platform/registry.py（MODULES 加 `_module("feedback", migrations=True, config=True)`；模組 `__init__.py` 申報 `config_appliers()` 照 identity）`、`guidant_agent/shared/event_codes.py（AGENT_FEEDBACK_CREATED／SYNCED／SYNC_FAILED）`、`common/code/（新 ConsoleFeedbackErrorCode 檔）` | `guidant_agent/feedback/__init__.py`、`guidant_agent/feedback/migrations.py（新版號：feedbacks 表）`、`guidant_agent/feedback/app/feedbacks.py（create／list_mine／get／record_sync）`、`guidant_agent/feedback/app/labels.py（KIND_FEEDBACK_LABELS 套用者，存 data_dir JSON 檔照 identity/app/offline_login_window.py；Agent 沒有 kv 表）`、`guidant_agent/feedback/adapters/feedback_repo.py（SQL 只在這）`、`guidant_agent/feedback/api/feedback_routes.py（GET list／GET one／POST／POST resend／GET labels）`、`test/feedback/test_feedbacks.py` | P-1 | 守衛六支綠（含 `test_security_rules_cover_sources.py` 新檔都被命中）；主控台 `POST /console/api/feedbacks` multipart 建一筆，`GET /console/api/feedbacks` 只回本人的，換另一個 session 打單筆 404；outbox 表多一列 kind=feedback；同一 uid 入列 create 再 update，`drain` 時 create 沒送成 update 不送（test）；離線時 resend 回 409；設定類 feedback_labels 套用後 `GET /console/api/feedback-labels` 回三個 | opus／high：基底＝FR-190（console 權限名）與 FR-188（outbox 立即送）合回後；新模組要過六支架構守衛（依賴方向、SQL 只在 adapters、背景元件、security rules paths）；outbox 保序改動碰核心 |
| 2 | 看得見的 | **I-1** jedi-issue：回饋表加來源兩欄、建改回饋收既有附件參照、歸屬例外、來源查詢、清單吐來源與篩選；1.5.0 → 1.6.0 | jedi 套件 | extend：`FeedbackService.add_feedback`／`update_feedback` 加 `attached_file_uids`／`source`／`source_ref`（有參照時走 `IssueUploadFilesRepoImpl.add_issue_file_mapping` 不走 `upload_attachment`）；`_assert_owner` 加一條例外；entity／DTO／serializer 各加兩欄；query entity 加 `source` 篩選；新 `list_by_source` | `jedi-issue/jedi_issue/app/feedback/service/feedback_service.py 175（_assert_owner）、243（add）、294（update）`、`jedi-issue/jedi_issue/app/feedback/service/feedback_issue_service.py（create_issue 傳參照）`、`jedi-issue/jedi_issue/app/feedback/dto/feedback_issue_dto.py`、`jedi-issue/jedi_issue/domain/feedback/entities/feedback_issue_entity.py`、`jedi-issue/jedi_issue/domain/feedback/entities/feedback_issue_query_entity.py`、`jedi-issue/jedi_issue/domain/feedback/service/feedback_issue_domain_service.py`、`jedi-issue/jedi_issue/infra/feedback/models/feedback_issue.py`、`jedi-issue/jedi_issue/infra/feedback/repository/（list_by_source）`、`jedi-issue/jedi_issue/api/serializers/feedback.py（source／source_ref 進 FeedbackResponse；FeedbackQueryRequest 加 source）`、`jedi-issue/jedi_issue/api/routes/feedback_route.py 65、76（清單與單筆回應先過 `rt.schema_extensions.enrich()`——掛點是空殼從沒人呼叫，不接 M-1 整張白做；post/put 簽名不動）`、`jedi-issue/pyproject.toml` | `jedi-issue/jedi_issue/migrations/006-feedback-issues-source.sql` | — | 套件 tests 綠；新 tests：`add_feedback(attached_file_uids=[x])` 不呼叫 upload 直接掛 mapping；`source` 預設 `web`；`_assert_owner` 對 `source=agent, created_user=local-admin` 持 read_all 者放行、不持者 403（突變：把例外拿掉要紅）；`list_by_source('agent', uid)` 只回該 Agent 的；DEV 套 006 後 `\d public.feedback_issues` 有 source／source_ref 與索引；既有回饋 source 全為 web | opus／high：套件公開 API 加參數、表加欄、歸屬檢查改動；要保既有 consumer 零影響 |
| 2 | 看得見的 | **F-1** FE 共用附件元件 AttachmentDropList：一個拖放區、既有檔與待上傳檔同清單、hover 動作、存放位置標籤；先換進主系統回饋表單 | FE 主站／console | new（照型）：拖放與 accept 照 `src/views/source-scan/SourceFilePicker.vue`（`ss-drop`、`onDrop`、`useAcceptedUploadTypes`），多檔清單與既有檔列是新寫；extend：`SystemFeedbackForm.vue` 附件區整段換成這個元件，`FileUpload` 兩區四樣式刪掉 | `src/views/feedback/system/SystemFeedbackForm.vue 302～420（附件區）`、`src/config/locales/i18n/zh-tw/feedback.json、en/feedback.json（附件區文案改寫；舊鍵 drag_drop_hint／ready_to_upload／uploaded_files 若無他處用則刪）` | `src/components/common/AttachmentDropList.vue（props：existing[]、pending[]、accept、maxSize、canDownload、canDelete、readonly；emits：add、remove-pending、remove-existing、preview、download）` | — | `npm run build` 過；主系統新增回饋：空的只有一行提示、拖兩個檔進區內兩列、hover 出移除、超 20 MB 顯示既有錯誤訊息；編輯既有回饋：既有檔與新加檔同一區、既有檔 hover 出預覽／下載／刪除且 v-can 照舊；`grep -n 'border-left\|font-size\|max-width' src/components/common/AttachmentDropList.vue` 為 0；文案全部來自 i18n 檔，回寫列出新增與沿用的鍵 | opus／medium：元件 API 要讓主站與主控台都能用；版面照雛形結構不抄樣式；套全站 dark-card 與字級 |
| 3 | 接起來 | **M-1** 主系統來源欄顯示：接 jedi-issue 回應擴充掛點補 Agent 名與「本機管理員」暱稱；功能標籤「Agent 主控台」seed | BE | reuse：jedi-issue `SchemaExtensions.response_enricher`（空殼掛點第一次有人接）；extend：`core/plugins/issue.py` `mount()` 傳 schema_extensions；seed 照 `2026-05-22-seed-oscal-ssp-roles.sql` 形狀 `ON CONFLICT DO NOTHING` | `core/plugins/issue.py 184（mount 加 schema_extensions）` | `core/plugins/_issue_source_enrich.py（source=agent 補 source_name＝remote_agents.name；created_user=local-admin 且 source=agent 補 created_user_name=本機管理員）`、`scripts/sql/2026-11-XX-fr192-seed-agent-console-function-label.sql（labels：scope=FUNCTION, name=Agent 主控台，uid 固定）`、`common/code/feedback_source.py（AGENT_CONSOLE_FUNCTION_LABEL_UID、SOURCE_WEB／AGENT、LOCAL_ADMIN_LOGIN_NAME）` | I-1 | DEV 套 seed 後 `select uid,name from labels where scope='FUNCTION' and name='Agent 主控台'` 一列且 uid 等於常數；手動 insert 一筆 source=agent 的回饋後 `POST /feedbacks` 回應該列有 `source_name`；created_user=local-admin 那列 `created_user_name` 為「本機管理員」；既有 web 回饋回應不變；BE 重啟實打；回寫明講「出貨基線待重產」 | sonnet／medium：接線與 seed，形狀都有先例。🔴 登記型共用檔 manifest.tsv 由同批 M-3 一次登記兩支（本卡 seed＋M-3 的 014 鏡像），本卡只寫 seed 檔、不碰 manifest |
| 3 | 接起來 | **M-3** 主系統收單端點 POST /agents/feedbacks：新增／修改／刪除、過期判定、Agent 附件建參照、對照表、類別選項設定類 | BE | new（照型）：route 照 `api/source_scan/routes/agent_manual_scan_route.py`、service 照 jedi-remote-agent `local_run_service.ingest`（通行證身分、批次、accepted／rejected）；extend：`RemoteAgentAdapter._add_reference` 抽成公開 `register_agent_blob()`，收單與既有上傳共用；設定類 provider 照 `_agent_config_sections.py` 既有 provider 形狀；對照表 migration 照 `011-remote-agent-offline-operators.sql` 的 RLS／GRANT | `infra/upload_file/remote_agent_adapter.py 158（_add_reference → register_agent_blob 公開）`、`core/plugins/_agent_config_sections.py（feedback_labels provider）`、`core/plugins/remote_agent.py（route 登記一行）`、`di_containers/remote_agent_container.py（intake service wiring）`、`common/enum/event_code.py（AGENT_FEEDBACK_RECEIVED）`、`scripts/sql/manifest.tsv（登記兩支：本卡 014 鏡像＋M-1 的 seed，M-1 不碰此檔）`、`scripts/sql/packages/jedi_remote_agent/（鏡像同號 0NN）` | `api/remote_agent/routes/agent_feedback_route.py`、`app/remote_agent/service/agent_feedback_intake_service.py`、`common/code/agent_feedback_error_code.py`、`jedi-remote-agent/jedi_remote_agent/migrations/0NN-remote-agent-feedbacks.sql（NN＝開工時套件目錄最大號＋1，FR-188 合回後至少 022；014 早被占用）`、`test/test_agent_feedback_intake.py` | A-1、I-1 | DEV 套 0NN 後 `\d compliance.agent_feedbacks` 有 RLS 四指令與 `(agent_id, local_uid)` 唯一；用 123 Agent 的通行證打 50 筆 create 回 accepted 50、第 51 筆 400；重送同 local_uid 回 accepted 不重建；`created_by` 不在這台 Agent 的有效離線操作者名單（同租戶但沒被授權這台的人也算）回 rejected unknown_user；`local-admin` 放行且 feedback_issues.created_user=local-admin；update／delete 帶別台 Agent 或不存在的 local_uid 回 rejected not_found（不收 body 的 feedback_uid）；帶 files 的那筆 `upload_files` 多一列 storage_type=remote_agent、save_file_name=Agent 檔 uid、path=該 Agent base_url；update 帶舊的 base_cloud_updated_at 回 stale；delete 後 feedback 不在、對照表 deleted_at 有值；`GET /agents/config` 回 feedback_labels 三個；BE 重啟實打；守衛測試綠；回寫明講「出貨基線待重產」 | opus／high：橋接兩個套件、身分與租戶邊界、附件參照、對照表唯一鍵；資安規則 endpoints／data 兩支都命中 |
| 3 | 接起來 | **A-2** 主控台前端 Feedback.vue：清單、新增回饋對話框、瀏覽回饋對話框、路由與 i18n；用 F-1 元件與主站 TipTapEditor | FE 主站／console | new（照型）：表格與回傳狀態欄照 `console/views/Runs.vue`（狀態字沿用 `agent_console_runs` 的 sync 鍵）；新增表單欄位照 `src/views/feedback/system/SystemFeedbackForm.vue`（標題整行、類別＋功能並排、內文 `@/views/project-summary-report/TipTapEditor.vue`、檔案上傳 `@/components/common/AttachmentDropList.vue`）；對話框照 `console/views/Logs.vue` 的 Dialog；extend：`router.js` 加一項 | `console/router.js（feedback 項：group work、scope null）`、`console/main.js（若 TipTap 需全域註冊才加）` | `console/views/Feedback.vue`、`console/feedbackApi.js`、`console/feedback.js（狀態對照、錯誤鍵）`、`src/config/locales/i18n/zh-tw/agent-console-feedback.json、en/agent-console-feedback.json` | F-1、A-1 | `npm run build:CONSOLE` 過；側欄「作業」群組最下多「意見回饋」；按「新增回饋」開對話框，欄位與主系統表單同、功能反灰「Agent 主控台」、沒拉到類別時新增鈕反灰；儲存後清單立刻一列「待回傳」；點列開「瀏覽回饋」對話框，回傳失敗的頂部有原因與立即重試；`grep -rn '本案新增\|審稿\|D[0-9]\|場景' console/views/Feedback.vue src/config/locales/i18n/*/agent-console-feedback.json` 為 0；`grep border-left console/` 為 0；回寫列出文案來源（沿用哪些鍵、新增哪些鍵） | opus／medium：畫面照雛形結構；文案全部重寫；要同時接主站兩個元件進 console build |
| 3 | 接起來 | **M-2** 主系統前端：系統回饋與我的回饋兩頁加「來源」欄與分段篩選；編輯回饋表單來源提示列 | FE 主站／console | extend：兩頁 `<Column>` 各加一欄、工具列加 `SegmentButtons`（全部／網頁／Agent，照 `feedback_list_page_filter_baseline_my_tasks` 規則）、filters 加 source；`SystemFeedbackForm.vue` 讀 `source` 顯示 `hint-bar--info` 一列 | `src/views/feedback/system/SystemFeedbackManage.vue 249～318（欄）、toolbar`、`src/views/feedback/system/SystemFeedbackView.vue`、`src/views/feedback/system/SystemFeedbackForm.vue（頂部提示列；附件區 F-1 已換）`、`src/config/locales/i18n/zh-tw/feedback.json、en/feedback.json（source_web／source_agent／source_hint）` | （無） | F-1、I-1 | `npm run build` 過；兩頁多「來源」欄，web 顯示「網頁」、agent 顯示「Agent · 名稱」；分段鈕切 Agent 只剩 Agent 來的；編輯 Agent 來的那筆頂部一列提示含 Agent 名與送出時間；`grep -n 'border-left' src/views/feedback/system/*.vue` 為 0 | sonnet／medium：既有頁加欄加篩選 |
| 4 | 改刪與回拉 | **A-3** Agent feedback 模組：修改、刪除、刪單一附件、回饋現況設定類套用者（較新者覆寫、已刪標記、stale 處理） | evidence-agent | extend：`feedbacks.py` 加 update／delete／remove_file／apply_cloud；`feedback_routes.py` 加 PUT／DELETE 兩條與刪附件；outbox 的 stale 回應處理（標 sync_error 並 `config_pull.request()`）；套用者照 `labels.py` 形狀 | `guidant_agent/feedback/app/feedbacks.py`、`guidant_agent/feedback/api/feedback_routes.py`、`guidant_agent/feedback/adapters/feedback_repo.py`、`guidant_agent/sync/app/outbox.py（stale 分支）`、`guidant_agent/shared/event_codes.py（UPDATED／DELETED／OVERWRITTEN_BY_CLOUD）`、`test/feedback/test_feedbacks.py` | `guidant_agent/feedback/app/cloud_state.py（KIND_FEEDBACKS 套用者）` | A-1、M-3 | 主控台 PUT 改標題 → outbox 一列 op=update；DELETE → op=delete、清單不列；收到設定類 feedbacks 內某筆 cloud_updated_at 比本機新 → 本機覆寫、稽核 OVERWRITTEN_BY_CLOUD；比本機舊 → 不動；deleted=true → 本機標 deleted_at；主系統回 stale → 該筆 sync_error 為「主系統已有較新版本」且觸發一次 config_pull；守衛六支綠 | opus／high：雙向覆寫邏輯與保序；要突變測試（把較新者判斷拿掉要紅） |
| 4 | 改刪與回拉 | **M-4** 主系統回拉：回饋現況設定類 provider、四張表改動監看、對照表刪除時間回寫 | BE | extend：`_agent_config_sections.py` 加 feedbacks provider（用 jedi-issue `list_by_source` ＋對照表 deleted 列）；`_agent_config_notify._WATCHED` 加 feedback_issues／issue_upload_files／issue_label_mapping → KIND_FEEDBACKS、labels → KIND_FEEDBACK_LABELS（只通知該 source_ref 那台，照 `remote_agents name` 的單台通知形狀）；主系統端刪回饋時對照表寫 deleted_at（掛 jedi-issue delete 後，走 ORM after_delete 事件或 intake service 共用函式） | `core/plugins/_agent_config_sections.py`、`core/plugins/_agent_config_notify.py 27（_WATCHED）`、`app/remote_agent/service/agent_feedback_intake_service.py（刪除路徑共用的 mark_deleted）`、`test/test_agent_feedback_intake.py` | `core/plugins/_agent_feedback_state.py（provider 本體，照 `_agent_vulndb.py` 形狀）` | M-3、I-1 | 主系統改 Agent 來的回饋標題 → `agent_requests` 多一列 config_changed 只對那台 → Agent 拉 `GET /agents/config?versions=feedbacks:舊` 回新內容；改 web 來的回饋不通知任何 Agent；主系統刪該筆 → provider 回該 local_uid deleted=true；改 labels → 通知 feedback_labels；BE 重啟實打 | opus／medium：表改動監看要只通知那一台（不是全租戶）；provider 內容雜湊版本 |
| 4 | 改刪與回拉 | **A-4** 主控台前端：瀏覽回饋對話框加編輯／刪除、存於主系統附件標示、被主系統覆寫與 stale 提示、最後更新列 | FE 主站／console | extend：`Feedback.vue` 瀏覽對話框底部加兩鈕（編輯開同一個表單對話框帶現值；刪除走既有 ConfirmDialog 形狀）；附件列 where=cloud 顯示「存於主系統」標籤且無預覽；sync_error 為 stale 時頂部提示 | `console/views/Feedback.vue`、`console/feedbackApi.js（PUT／DELETE／刪附件）`、`console/feedback.js`、`src/config/locales/i18n/zh-tw/agent-console-feedback.json、en/agent-console-feedback.json` | （無） | A-2 | `npm run build:CONSOLE` 過；瀏覽對話框有「編輯」「刪除」，編輯帶入現值、儲存後清單那筆變「待回傳」；刪除確認後清單消失；存於主系統的附件標籤正確且無預覽鈕；stale 那筆頂部有提示；`grep border-left console/` 為 0 | sonnet／medium：既有頁加兩鈕與兩種狀態顯示 |

## 7. 合回後收口清單與端到端驗收 {#acceptance nav="收口與驗收"}

決策者 2026-11-11 裁：驗收、套件發版、主專案改回釘版**不開卡、不進工作坊**，因為三件都要先把四個 repo 合回主線才能跑，排在尾批一定卡。批 4 最後一張開發卡驗收通過、工作坊 finish 合回之後，由首腦依序做：

1. 四 repo 合回 `feature/FR-172`（工作坊 finish 自動做，衝突由首腦解）。
2. 端到端驗收：下面十二步，在真 Agent 上跑；失敗項開修正卡。
3. 套件發版：protocol 0.7.0、jedi-iam 1.11.3、jedi-remote-agent（M-3 bump 的號）、jedi-issue 1.6.0，各跑全測後照 `docs/claude/jedi-packages.md` 末段推 Nexus；publish 不可逆。
4. 主專案改回釘版：取消 assume-unchanged、pin 四個新版、`poetry update` 指定套件名、起 BE 實打一筆回饋；Agent `pyproject.toml` 釘 protocol 0.7.0。
5. 回報出貨基線待重產（jedi-issue 006、jedi-remote-agent 新號、主線 fr192 seed）。
6. 母卡 Done、SPEC 與系統整合通訊說明更新。

**端到端十二步**（真 Agent 123 或 190，每步附截圖或指令輸出）：

1. 主控台以離線操作者登入，新增一筆帶兩張圖 → 清單「待回傳」→ 幾秒內「已送達」。
2. 主系統「系統回饋」看到那筆：來源「Agent · 名稱」、功能「Agent 主控台」、建立人員是該操作者；「我的回饋」該操作者看得到。
3. 主系統明細預覽與下載兩張圖，拿到原檔；Agent 拔網路後下載回無法連線錯誤。
4. Agent 拔網路，主控台新增一筆 → 「待回傳」；插回網路 → 自動「已送達」。
5. 主控台以本機管理員登入新增一筆 → 主系統建立人員「本機管理員」、來源那台；持「檢視全部回饋」者可編輯可刪除；一般使用者「我的回饋」看不到它。
6. 主控台編輯第 1 筆標題 → 主系統清單標題變。
7. 主系統編輯第 1 筆內文並補上傳一個檔 → Agent 瀏覽那筆內文變、附件多一個「存於主系統」。
8. 主系統刪第 4 筆 → Agent 清單消失。
9. Agent 拔網路改第 1 筆、主系統同時改第 1 筆、Agent 插回 → Agent 顯示主系統版本並提示被覆寫；主系統不變。
10. 主控台刪第 1 筆 → 主系統消失、`agent_feedbacks.deleted_at` 有值。
11. 主系統回饋表單（web 來源）附件區為新版：拖放、hover 動作、既有檔同區。
12. 主系統建帳號 `local-admin` 被擋。

## 8. 風險與陷阱 {#risks nav="風險"}

- **基底是 FR-190 與 FR-188 合回之後**：FR-190 發了 protocol 0.6.0 與 remote-agent 1.18.0、改了主控台權限名與 `require_session`；FR-188 改了 Agent outbox、主系統 `remote_agent_adapter.py`、jedi-iam（1.11.2）、remote-agent（1.18.1）、protocol（0.5.5，要對齊）。本案每張卡開工先 `git merge feature/FR-172` 跟上，版號在它們之上 bump：protocol 0.7.0、iam 1.11.3、remote-agent 1.18.2 以上。E-1 與 P-1 第一輪在舊基底做，要重做。
- **BE 工作區共用一份 `.venv`**：E-1 改 path 會把 FR-190 與決策者自己的 BE 一起切到 FR-192 的半成品套件，FR-190 收尾又切回來。要嘛 FR-192 工作區建獨立 `.venv`，要嘛等前兩案完全收口再開（決策者裁）。

- **`local-admin` 不改名**：主系統端兩處拿這個字串撈歷史稽核，Agent 端 SQLite 以它當主鍵；任何想「順手改名」的念頭停下。保留字要擋在**每一條**寫帳號名的路徑（建、改名、LDAP 同步、佈建），P-2 開工先 grep 寫入點。
- **送件人判準是「這台的離線操作者」不是「這租戶的使用者」**：資安審查點名；用租戶判準等於讓 Agent 替任何同事送件。
- **回拉只通知那一台**：`_WATCHED` 加表時若照預設通知全租戶，每次有人改任何回饋所有 Agent 都來拉，且 provider 會算全租戶內容。照 `remote_agents.name` 的單台路徑。
- **outbox 保序與既有 `_ORDERED`**：`KIND_EVENT`／`KIND_REPLY` 的保序是「各 kind 各排各的」，feedback 要的是「同一 uid 內保序」，實作時確認 `_ordered_heads_waiting` 的 key 是否要加 uid。
- **TipTap 進 console build**：主站元件帶 `@tiptap/*` 相依，console 包體積會變大；A-2 回寫 build 體積前後。
- **manifest.tsv 只讓 M-3 碰**：M-1 的 seed 檔寫好但不登記，M-3 登記時要把兩支都列；V-1 驗 `schema_migrations` 兩支都在。
- **衝突規則是整筆覆蓋**：使用者在 Agent 改了附件、主系統同時改了標題，較新者整筆蓋掉另一邊的改動；本期接受，文件與提示要講清楚。
- **主系統補上傳的附件 Agent 不可預覽**：檔在租戶儲存，Agent 拿不到；清單標「存於主系統」即可，不要嘗試從主系統拉檔到 Agent。

## 9. 這份文件的定位 {#positioning nav="定位"}

設計決策文件，v1.0 凍結。實作期間設計異動開 `design-v1.1.md`；上線後畫面與 API 現況以 SPEC 頁為準；每棒做了什麼、推翻了什麼進 `handoff/fr192-LOG.md` 與 commit message。
