FR-107 交接日誌(append-only)

這個檔只追加,寫完的 block 永不修改。 發現舊 block 有誤 → 在新 block 的 「推翻了什麼」欄寫明更正,不要回頭改。

現況看 fr107-STATE.md;本檔是決策軌跡與教訓的來源。


§1

Block 1 — 2026-09-16 首腦(討論稿 → design → 開卡 → .1 三張驗收)

這一棒做了什麼

從決策者對 FR-030 Drive 版分類線的盤點討論開始,寫討論稿、拿到 D1–D12 裁示、落成 design.md、開母子卡(29 張連號),並跟到 .1 前三張(T-1.1/T-1.2/T-1.3)驗收通過。

commits

見 STATE 表 B。

關鍵決策(決策者拍板)

  • D1~D10:批次檔另開新表;容器改甲案(宿主先拉檔);舊 Drive 線並存一版;job 狀態 DB 化做最小版;分類結果沿用舊表加欄;未設儲存只在上傳路徑擋;清批次實刪實體;批次權限 manager 可寫、auditor 唯讀;不做跨來源去重;孤兒回填歸 ROOT。
  • D11(本文與討論稿最大差異):新功能全部進既有套件 jedi-evidence-classification, 不開新套件;主專案只留宿主 adapter 與前端。理由:批次的狀態機、歸檔演算法是引擎自己 的知識,開第二支套件等於兩處各長一半;被排除方案是新開 jedi-evidence-batch(違反插件 互不相依)與主專案自己做(重蹈 FR-069 之前「一半在主專案」的病)。
  • D12(寫作中追加):支援多供應商 AI,金鑰進系統設定(租戶級,比照 STORAGE_CONFIG) 而非只放環境變數——落地版客戶不會自己改伺服器 .env,改了也要重啟,無法自助切換供應商。
  • 六項補裁:容器 image 進出貨包(T-6.4);port 切法照 design 兩支分工; classification_profiles 不建 FK;profile CRUD 只給平台管理員;job_evidences 既存 重複列先查不合併;舊 route/FE 常數並存期可接受、.6 一起清。

推翻了什麼

無(本棒為新案起點,尚無被推翻的判斷;.1 三張 runner 對卡面規格有三處更正,見下)。

教訓

教訓 1|開卡時寫的檔案落點會撞 .gitignore,三張卡同一個錯。 T-1.1/T-1.2 卡上都寫 migration 落 scripts/sql/packages/<套件>/,但那個目錄是 build 時 從套件攤出來的產生物、在 .gitignore 內,放進去 commit 不了。正本該放套件自己的 migrations/,且不自帶 INSERT schema_migrations(build 腳本統一登記)。開卡引用 既有卡當範本時,範本本身如果錯,會被原樣複製三次——T-1.1 runner 第一個查出來, T-1.2/T-1.3 開卡時首腦已知道要改但沒有回頭巡一次已開好的卡面文字,是 runner 自己在 「已完成修正」段裡各自更正一次。

教訓 2|grep SQL 關鍵字判「零寫入/零影響」是盲區,本案在派工 prompt 就先寫明。 上一個 arc(FR-104/FR-105)已踩過 ORM session.flush() 不會被 SQL 關鍵字 grep 抓到, 本案 T-1.1 卡面「怎麼做②」已預先要求同時 grep session.add/merge/flush,算是把 教訓直接內化進卡片而非等 runner 撞到。

教訓 3|「剩下的孤兒歸 ROOT」字面執行會傷到真實使用者。 卡上 D10 寫「查不到歸屬的孤兒歸 ROOT」,T-1.1 runner 若照字面只做參照鏈回填,會有 1804 筆被掃進 ROOT——這些檔案的真正擁有者從此看不到自己的檔(RLS 擋掉)且無錯誤訊息。 runner 多加一層「退回上傳者所屬公司」並與參照鏈結果交叉比對(711 筆一致、0 衝突)驗證 後才用,孤兒降到 21 筆。寫回填規則時,「找不到就歸預設值」這句話本身要包含「先窮舉所有 可能的間接判定依據」,不能只給一條鏈路就當作規則完整。

未驗證的假設

  • T-1.4(FE 上傳到批次頁)與 .2(儲存 port/容器改造)尚未驗收,港灣式風險:T-1.3 已驗證 後端六條 API 全部實打過,但前端串接與容器實際掛目錄跑分類的行為本棒未觸及。

環境異動紀錄

  • DEV DB(localhost:5432/guidant_ai_dev):套了 T-1.1(套件 jedi-file-upload 004-upload-files-tenant-owner-rls.sql)、T-1.2(套件 jedi-evidence-classification 003-evidence-batches.sql)兩支 migration。
  • STG/POC:完全沒碰。

§2

Block 2 — 2026-09-17 首腦(T-1.4 退回兩項修正驗收、.2 三張夜跑核驗、T-1.4 UI 整頁退回)

這一棒做了什麼

復查 T-1.1 回填的兩項待修正、對 .2 三張(T-2.1/T-2.2/T-2.3)做夜跑核驗,並處理決策者 對 T-1.4(CM-1853)前端 UI 的驗收——整頁不接受,開新卡 T-1.4m 先出 HTML mockup 過目再 重做。

commits

見 STATE 表 B(本棒未新增 commit,僅驗收與開卡)。

關鍵決策(決策者拍板)

  • T-1.4 UI 整頁退回重做:決策者原話「最好用流程引導的方式,需要有一個歷史清單, 新建批次與上傳分開,功能應該叫『自動分類證據』不是『批次上傳證據』,上傳的檔案一律 要能預覽,檔案數量一多會一直往下長,整個重新設計,參考業界設計」。根因是上一棒跳過 guidant-design-system skill 明寫的「新頁面先出 HTML mockup 過目再實作」規定,直接刻 Vue,落成畫面後才被全盤打回。已定案:兩頁結構(歷史清單頁+四步流程頁)、第③步重用 既有審閱頁(換資料來源屬 T-3.4)、命名「自動分類證據」路由 auto-classify。開 T-1.4m(CM-1878)先出 mockup,過目後才重派 T-1.4。

推翻了什麼

  • 首腦誤判時區差 8 小時:復查 T-1.1 回填結果時,首腦一度認為 DB 記的時間與 API 回應 的時間對不上、疑似資料不一致,實際上 DB 存的是 +08 時區、API 回的是 GMT,是同一個 時刻的兩種表示法。runner 原本的復查是對的,首腦第一次判斷錯誤,重新核對後撤回。
  • T-1.4 跳過 mockup 直接實作:違反 guidant-design-system skill 的明文規定,是本棒 被推翻的實作方向,見上「關鍵決策」。

教訓

教訓 1|新頁面必須先出 mockup 過目,開實作卡時要把「mockup 已過目」列為前置條件。 T-1.4 卡面沒有把「先出 mockup 給決策者看過」寫成前置步驟,只當作 skill 背景知識期待 runner 自己記得,結果整頁做完才被打回,浪費一次實作與一次驗收的成本。之後開任何新頁面 實作卡,前置條件要明寫「mockup 已過目通過」而不是假設 runner 會自己觸發那個 skill。

教訓 2|runner 驗收打折要當場講明,.2 的 AI 401 標得好。 .2 三張夜跑核驗時,其中一張的驗收條件因為外部 AI 服務 401(金鑰過期或額度問題)沒辦法 用真實呼叫驗證,runner 在回報裡把「這一項用模擬/替代方式驗證」寫得很清楚,讓首腦知道 這是打折的驗收而不是無條件通過,符合「驗收環境打折要當場講明」的規則,值得延續。

未驗證的假設

  • T-2.1/T-2.2/T-2.3 為首腦實查後「建議通過」,尚未經決策者本人確認,狀態先標「修正待 驗證」等最終蓋章。
  • T-1.4m mockup 尚未產出,本棒只完成開卡與 CM-1853 的退回說明,實際兩頁 HTML 稿件由 下一棒(另開 session)做。

環境異動紀錄

無(本棒只做驗收復查與 Notion/文件作業,未動 DB、未動程式碼)。


§3

Block 3 — 2026-09-17 首腦(下午,T-3.2/T-5.1/T-5.4 驗收、D13 原廠 AI 金鑰裁定)

這一棒做了什麼

驗收 T-3.2、T-5.1、T-5.4 三張卡;T-5.4 抓到三處需修正、退回重做;就「AI 金鑰在落地版怎麼 發給客戶」跟決策者討論,拿到 D13 裁示並落成 design.md、開 T-5.5(CM-1879)。

commits

見 STATE 表 B(本棒為驗收與文件/開卡作業,無新增程式碼 commit)。

關鍵決策(決策者拍板,D13)

原廠給客戶一組 AI 金鑰,放進 ROOT 租戶(tenant_id=1)的 AI_PROVIDER_CONFIG(加密存 DB,不寫 guidant.env);AI 小幫手/AI Dashboard/證據分類三功能統一改成「租戶自己的設定 → ROOT 原廠鑰 → 環境變數」三層解析;第一版不開放租戶自填(畫面鎖 ROOT 平台管理員); install.sh 裝機時把原廠鑰寫進 ROOT,--upgrade 不覆寫既有值。被排除:明文存 DB、加密鑰 與密文放同位置、「原廠代理」完全藏鑰(列為獨立 FR 候選,未裁)。風險天花板與 Drive OAuth token 同級——防意外與 DB 外洩,不防客戶自己的 root 有心人(解密鑰仍在同一台機器上)。

推翻了什麼

  • 裁示 4「profile CRUD 只給平台管理員」修正為「放寬到租戶管理員」:實查發現四層解析鏈 裡租戶那兩層本來就只有租戶管理員自己建得出來,只鎖平台管理員的話那兩層永遠沒人用得到, 形同虛設。已在 design.md 裁示 4 旁加註 2026-09-17 修正說明,AI 供應商金鑰設定頁仍維持鎖 ROOT 平台管理員(D13,跟 profile 守門是不同軸,不要混為一談)。
  • 首腦誤把 T-5.2 當「進行中」:復核時發現 T-5.2 其實從未真正被派出去,只是先前某棒給過 一次建議 prompt,首腦誤把「給過 prompt」當「已派工」。派工狀態要看卡片實際狀態欄位, 不能靠印象推斷——之後回報一律先查卡片再說派出或待派。
    • 首腦誤判 T-5.4 金鑰頁該開放給租戶:討論初期一度覺得「技術上租戶各自能填金鑰比較 合理」,但決策者裁示第一版就是要鎖 ROOT、統一用原廠那把,不開放客戶自填——「怎麼做技術 上合理」不能取代「決策者的商業意圖」,這類「誰能設定什麼」的問題要在設計階段直接問決策 者,不能只靠技術推理自己下判斷。

教訓

教訓 1|「給過 prompt」不等於「已派工」,回報前先查卡片狀態。 首腦手上有派工 prompt 草稿不代表工作已經發出去,只有 Notion 卡片狀態真的挪動了才算派出; 下一棒接手看到「進行中」字樣,要先去卡片核對而不是照單全收。

教訓 2|「誰能設定什麼」屬商業決策,不能用技術合理性推論,要在設計時直接問決策者。 T-5.4 金鑰頁該不該開放租戶自填,從純技術角度兩種做法都說得通,但答案其實是決策者的商業 判斷(第一版鎖定原廠統一供應、避免客戶到處填錯或洩漏)。遇到這類「權限開放範圍」的題目, 不要自己推理出一個「看起來合理」的答案就往下做,要先問。

未驗證的假設

  • T-5.5(CM-1879)尚未派工,D13 的三功能統一解析器實作與 install.sh 寫入行為完全未觸碰, 現況仍是三處各自讀法(AI 小幫手/Dashboard 直讀 env,證據分類讀 DB 但容器注入仍讀 env)。
  • T-5.4 退回的三處修正尚未重做,需等下一棒依修正意見重新實作再驗。

環境異動紀錄

無(本棒只做驗收復查、design.md/STATE/LOG 文件更新、開 Notion 卡,未動 DB、未動程式碼)。


§4

Block 4 — 2026-09-17 首腦(下午~交接,T-5.2/5.4/5.5 收尾、T-3.3+5.3a 斷線接手、三張新派、交接雙檔重核)

這一棒做了什麼

T-5.2(catalog/報表)三輪修正後驗收、T-5.4(金鑰頁)驗收退回後重做三輪過關、T-5.5 (D13 三功能統一金鑰+install.sh)開卡直接做完;接手一個中斷的 session(T-3.3+T-5.3a 併做),拆成兩支獨立 commit 落地;新派三張卡(T-3.4/T-4.1/T-5.3b);本 block 收尾時 重新逐張核對 Notion 卡片真實狀態與四個 repo 實際 git log,據此重寫 STATE 表 A/表 B。

commits

見 STATE §4/§5(表 A 逐卡 commit、表 B 統整)。

關鍵決策(決策者拍板)

延續 D13;T-5.5 落地細節:AI_PROVIDER_CONFIG 三功能(AI 小幫手/AI Dashboard/證據 分類)共用同一支解析器,install.sh 在裝機階段寫入 ROOT 原廠鑰、--upgrade 路徑不覆寫 既有值(避免升級洗掉客戶已設定的租戶鑰或已輪替的原廠鑰)。

推翻了什麼

  • 本棒交接前的自我查核,推翻了「派工 prompt 本身」的多項事實:撰寫這份 STATE/LOG 更新前,逐張重查 Notion 與逐一重跑四個 repo 的 git log/git status,發現交接指令裡 當作「已實查」的下列事實其實是舊資訊,未反映最新進度: ①「T-3.4/T-4.1/T-5.3b 派出中」——正確,但需標明是同一天下午晚些才派、尚未有任何 runner 回報;②「T-1.4 待決策者手測」——已經是 Done,不需要再排驗收;③「T-5.2/T-5.4/ T-5.5 待派或退回中」——三張其實都已經 Done;④「T-1.1/T-1.2/T-1.3 標『✅ 驗收通過』」 ——Notion 實際狀態是「修正待驗證」,比「Done」低一階,尚未真正蓋章。「派工 prompt 裡 的『已實查』事實」本身也需要被下一棒當作「聲稱」而非「保證」來對待——這是 Block 3 教訓 1(「給過 prompt 不等於已派工」)的鏡像版本:這次踩到的是「首腦盤點過的事實,寫進 prompt 後也會在傳遞過程中變舊」。
  • T-5.4 三輪修正:第一輪驗收退回,具體三處修正意見與退回原因見 CM-1867 卡面歷史, 本 block 不重複抄(卡片是唯一真相來源,文件只記結論)。

教訓

教訓 1|「已實查」的事實會在交接鏈路上變舊,接手方仍要自己重驗一次高頻變動欄位 (卡片狀態、commit hash 清單),不能把「盤點過」直接當「當下為真」。 本案的教訓 3(Block 3)講的是「給過 prompt 不等於已派工」,這次是同一顆藥丸的另一面: 「盤點過」也不等於「維持到你讀到這份文件的當下還是真的」——尤其是卡片狀態與 commit 清單 這種每小時都可能變的欄位。寫交接文件的規則要更新為:卡片狀態與 commit 清單這兩類欄位, 永遠在動筆前用即時查詢重新核對一次,不可以沿用上一份文件裡抄來的版本,即使那份文件 自稱「皆已實查」。

教訓 2|表 B commits 清單這種會員爆炸成長的內容,不該逐條抄進 STATE,改成「去對應 卡片查」的索引式寫法。 本案 commits 數量已經到 BE 33 筆/套件 14 筆/FE 9 筆,且還在持續增加,逐條列在 STATE 會讓這個「≤200 行」的檔案被 commits 清單吃掉大半篇幅,且每棒都要重新核對每一條 hash 是否 還對得上(容易出現複製貼上時的手誤)。改成「commit 清單以 Notion 卡片『已完成修正』段 為準,STATE 表 B 只給逐卡一行 hash」大幅降低維護成本與出錯面。

未驗證的假設

  • T-3.4/T-4.1/T-5.3b 三張卡截至本 block 收尾時,Notion 狀態仍讀「Not started」(T-4.1 在 BE repo 有對應的未 commit 程式碼改動,疑似已有 runner 在動但尚未回寫卡片與 commit—— 下一棒接手前先看這三張卡片是否已更新,不要假設仍是「剛派出、什麼都沒開始」)。
  • T-3.4 完成後尚未實測「舊 Drive 分類路由是否仍打得開」(design.md D3 舊線並存的驗收項)。

環境異動紀錄

  • DEV DB:套了套件 jedi-evidence-classification 004/005(T-5.1/T-5.2 對應 migration)與主線 scripts/sql/2026-09-17-fr107-ai-service-config-menu.sql(T-5.5, 新增 main-line 選單項)。ROOT 租戶(tenant_id=1)的 AI_PROVIDER_CONFIG 已寫入三家 供應商加密金鑰(Anthropic 可用,OpenAI/Google 額度或金鑰無效,見 STATE §6)。
  • STG/POC:完全沒碰。

§5

Block 5 — 2026-09-17 首腦(第五棒:冷接→驗收十四張→手測修正→push→開發收口)

這一棒做了什麼

冷接後驗三張跑中卡(T-3.4/T-4.1/T-5.3b);開七張新卡(CM-1883 升級路徑補原廠鑰、CM-1884 批次詳情補欄、CM-1885 碼表換代+思考深度、CM-1886 e2e 拆出、CM-1887 小幫手/Dashboard 換代、CM-1888 手測第一批八項+五追加、CM-1889 基線重產);依序派出並驗收 T-4.2、CM-1884、T-4.3、T-6.1、T-6.2(發 1.2.0)、T-6.3(拆卡)、CM-1885、CM-1887、CM-1888、CM-1889 共十四張改 Done;決策者親手測整條線;三 repo push;決策者裁「先不發版」,出貨兩卡暫停。

commits

見 STATE §4/§5。

關鍵決策(決策者拍板)

  • OpenAI 鑰無額度不擋 T-5.3b:先 Done、儲值後補驗(後已儲值,CM-1885 真跑四支型號全過)。
  • 升級路徑也要補寫原廠 AI 金鑰(缺補、有不動)。
  • 模型碼表換代:Anthropic Sonnet 5(預設)/Opus 5;OpenAI 預設 gpt-5.6-terra、可選 gpt-5.6-luna(不放 sol/astra);小幫手 Haiku 4.5;Dashboard 三檔皆 Sonnet 5;新增「思考深度」三檔預設 medium。
  • 金鑰讀法不改:三功能已統一走 DB resolver(首腦清空 env 實驗證過)。
  • T-6.3 拆卡:SPEC 這半做,e2e 移 CM-1886 等 image。
  • 「歸檔方式」選項從未接線,整組拿掉;「刪除整批」UI 有就要做,硬刪不軟刪。
  • 先不發版,多 FR 一起發;模型設定驅動先不做;Drive 當儲存後端先不立案。
  • 授權 push 三 repo;CM-1889 放行寫 188 基線庫。

推翻了什麼

  • 首腦給的 GPT 型號與價格(gpt-5/gpt-5-mini)是第三方舊資料,決策者拿官方計費表糾正為 gpt-5.6 系列;CM-1885 卡面作廢改補充。
  • CM-1885 runner 說「DEV OpenAI 鑰是空字串」——它讀 .env,鑰在 DB;首腦解密實查三把都在。
  • T-6.3 runner 查出 190 沒有任何 image 含 FR-107 端點,原卡「stub 同名取代即可」前提不成立 → 拆卡。
  • CM-1888 ⑥ 比卡上寫的重:控制樹 jobs 根本沒 evidences 欄位,手動上傳與 Drive 同步從以前就沒算過。
  • 四支選單 migration 全漏登記 manifest(含前一棒 CM-1867),客戶端 migrate 會靜默不套。
  • 出貨基線待重產清單從「六支」修正為主線五支(套件四支走攤平不進基線)。
  • Block 4 說「三張跑中卡 Not started」——冷接時三張都已回寫「修正待驗證」,STATE 高頻欄位又一次過期(同 Block 4 教訓 1)。

教訓

  1. 價格與型號 id 這種外部事實,不能拿第三方搜尋直接給決策者——對官方頁或請決策者提供,否則整張卡型號名重寫。
  2. 「鑰在哪」要寫進派工卡:三功能統一走 DB resolver 後,runner 憑直覺讀 env 就得出「鑰是空的」並給三個都不對的選項;卡上一句「取鑰走 resolve_api_key,不讀 env」省一輪。
  3. 換模型世代不是換字串:5 系列 thinking 預設開,content[0].text 會炸、max_tokens 要放大、budget_tokens 回 400;每次換代把「回應形狀變了沒」當必查項。
  4. 彙總讀模型「有沒有算某型資料」要用 DB 真相對照 API 回應驗,光看 code 會低估缺口。
  5. migration 寫完必查 manifest 登記:漏登記=客戶端靜默不套,比忘記套 DEV 更難發現。
  6. 首腦驗收自己起 BE、自己建批次真跑(含 OpenAI)比讀回寫快,抓到「FE 沒送 provider」這種只有真打才出現的 400。
  7. 文件更新 subagent 讀大檔會 autocompact 爆掉:STATE/LOG 這種要整檔重寫的,首腦直接寫比派 sonnet 穩。

未驗證的假設

  • gemini-3.5-flash 型號 id 存在(runner 查官方文件,DEV Google 鑰無效未實打)。
  • 落地版 BE 容器能 docker run 分類容器——compose 沒掛 docker.sock,高度懷疑不能,T-6.4 要補。

環境異動紀錄

  • DEV DB:套件 006(effort 兩欄)、主線四支選單 migration(icon-fix/system-menu-regroup/cloud-integrations-regroup)+ job-evidences(T-4.1);驗收建的批次 19(run 35 歸檔進 round 78 的 14 張任務 21 筆證據)、579e00fd(run 34)、4aaaf23f(run 36/37);本機 evidence-classifier:dev 重 build 含 --effort。
  • 188 基線庫 guidant_ai(決策者放行):CM-1889 套五支主線 migration,備份 ~/backup/guidant_ai_baseline_20260917_1836_before_CM-1889.dump。
  • 三 repo push 到 origin/feature/review。STG/POC 零碰。