FR-069 LOG — 歷程(append-only)

FR-069 LOG(append-only,每棒追加一 block,不回改)

§1

2026-08-29 ~ 2026-08-30|第一棒(首腦 session,協調+驗收)

歷程

  1. 三棒平行盤點(全模組清單/耦合矩陣/jedi monorepo 格局)→ 拍板 P0–P8 第一階段+P9–P13 凍結路線圖(後由 D8 解凍)。
  2. FR-069 取號、design.md 落地(7e87c2ca)、Notion 開卡 9 張(CM-1435 母+CM-1436~1443 子,連號)。
  3. P0(前置清理)與 P1(jedi-ai-bot)平行——P0 含 D5 追加的 grc→flow_control 改名(6a0aa72c6f3d02cd);P1 定插件契約首例+extraction-sop.md 七階段(43bd2ec1),發版 cbcff277
  4. P2(jedi-integrity d8cca74c)+P4(jedi-remote-agent c9590b3f)平行;P4 定 migration 隨包五規則(SOP §4.2 首例)。合併發版 327a5963
  5. P3(jedi-log-forwarding 3de631da,含端到端「迷你接收器真的收到 log」實測)+P5(jedi-license-runtime 7d07d62b,驗簽引擎逐行比對零改動、執法留宿主)平行。合併發版 6daa0f67
  6. P7(jedi-common 擴充)兩批:批一 response_util(cb85f193)、批二通用工具與 enum(0d63684d)——先加後刪、主專案留 re-export shim、124+ 處消費端零改動。jedi-common 未發版(與 jedi-auth 併 P8 後一起,等令)。
  7. base_repository 誤判插曲:首腦把 jedi-common 頂層 7 行的 base_repository.py 誤判為「舊代待汰換」,發了清理棒;子棒查證發現它是 lazy session mixin、BaseRepositoryImpl 自己就繼承它、5 個目標檔全屬合法特殊 repo 用法——按紀律煞車回報,未改任何檔。結論改為「不汰換,第二階段正名(SessionMixin)+刪 0 引用的頂層 base_model.py」,入 design.md(ac9e93f3)。途中一支基於錯誤前提的記帳文件棒被叫停、半成品編輯已還原。
  8. 決策演進:D5 改名(b497e5c7)→ D6 插件契約(隨 6f3d02cd 入庫)+四插槽指引(5e8f711e)→ D7 harness+README(dbaf4bdc)→ D8 全路線解凍+四階段+身分脈絡標準件(e2a394a4)。
  9. P7 user 手測通過收 Done → P8 發棒(進行中)→ 本交接檔。

教訓(後棒引以為戒)

  1. 驗收發現「文件與實況不符」,先比對變更前後的原始碼再下結論——P7 實測信封鍵是 status,CLAUDE.md 寫 code;比對搬家前後證明兩版一字不差,是文件債不是搬壞。差點誤判 P7。
  2. 「看似殘留的重複」先查繼承鏈再發清理棒——base_repository 兩檔同名不同角色(session mixin vs 查詢介面),憑檔名與大小差異推斷「新舊兩代」是錯的;同名不同角色的基底類是「假清理」誘因,收斂靠正名不靠強制遷移。執行棒「前提被推翻就停手回報」的紀律救了這次。

§2

2026-08-30(同棒續)第一階段收官

P7/P8 收口與驗收要點

  • P7(jedi-common 擴充,CM-1442):分兩批 commit(cb85f193 response_util 批一/0d63684d 通用工具與 enum 批二),全程「先加後刪」——主專案舊位置全改 re-export shim,124 處消費端零改動、零檔案被碰。jedi-common 內主專案 import 0、自帶測試 153 passed。順帶第一刀 base_repository 清理案被執行棒查證推翻(見上方教訓 2),收斂改走「第二階段正名案」(commit ac9e93f3)。
  • P8(authz 主體域併 jedi-auth,CM-1443):拆兩半精準——主體域五檔(platform/admin/capability/decorators/signed_token)進 jedi_auth.authz、資源域四檔+license 軸留主專案;126 處消費點零改動(shim)。四個守門 error code 進套件 AuthzErrorCode字串值凍結 GRC_*(FE i18n key,實地驗過 8 筆翻譯都靠它)。權限行為實測:admin 200/無 token 401;主專案守衛擴到 47 passed。CLAUDE.md authz 段由 P8 棒同步更新(「改主體域改套件,勿在 shim 補邏輯」)。

發版第四輪(收官輪):jedi-common 0.0.32+jedi-auth 0.1.35 上 Nexus(monorepo 版號 commit a206cf7),主專案 pin commit ac696b02,poetry.lock 無連帶升版;全域煙測綠(守衛 47/auth/信封/authz decorator 三鏈 200/無 token 401)。至此七支套件發版全清。

身分族兩棒深度分析 → D9 立案:耦合盤點(login→auth 21 處穿透 infra、五支發版全同批、B 類通用膠水 1,300 行+C 類重複 320 行、死鏈三條)+全站漏網掃描(jwt_mw/socketio 驗簽/user_context_builder/安全政策定義等約 930 行主專案側身分程式;四髒:jedi-department 殭屍、root 判定三份、roles.is_admin 殭屍旗標、login 手測腳本)→ D9:合併為 jedi-identity,插隊 2.5 階段(commit ba44d274)。notification 獨立、agent 機器身分不併。前置確認:jedi-* 僅 Guidant AI 消費。

疆界盤點案立案(FR-069.9/CM-1446,commit 06266329:第四階段開工門檻——照 D9 方法對 26 支套件+主專案候選做疆界地圖;五組候選(檔案/問卷/檢測/OSCAL/追蹤)+兩項退役查證(jedi-log、jedi-oscal v1);願景 26→12–15 支疆界套件。

第二階段開卡:CM-1447(.10 ext 表全庫盤點)/CM-1448(.11 jedi-common 查詢層:JSONB+身分脈絡包+基底正名)/CM-1449(.12 收斂實作 auth/user 首批,等前兩張)。RLS 覆蓋缺口另案 CM-1445(三張授權關聯表無 RLS,獨立安全債不掛 FR-069)。

教訓補一條

  1. base_repository 誤判事件全程——首腦憑「兩檔同名+大小懸殊+修改日期差一年」推斷新舊兩代並發清理棒;執行棒開工前查繼承鏈發現頂層 7 行是被完整版繼承的 session 底座,前提錯→停手回報未改一檔;首腦叫停平行的欠帳記錄棒、還原半成品編輯、撤回對 user 的錯誤說法、改立正名案。完整鏈:誤判→子棒煞車→撤回→正名。派清理棒前先驗「誰繼承誰」,且執行棒的「前提被推翻就停」紀律是最後防線,開棒 prompt 要一直帶著。

§3

2026-08-30(同棒下半場)第二階段全收+2.5 開打

第二階段三棒收口與驗收

  • .10 ext 全庫盤點(CM-1447,b459da74:190 張表掃出 9 張延伸表形態——收回主表 1(project_extensions)/留置 6/待決策 2;推翻原假設:全庫沒有任何「純展示型客製欄位」,側掛表是「跨套件邊界」長出來的不是客製需求 → JSONB 容器定位改「預防性建設」(D15)。auth/user 域無可收斂表,.12 首批改打 project_extensions。順手發現三則(users.topt_secret 死欄位/projects RLS 停用但 policy 殘留/孤兒表)。涵蓋性自證:四偵測面 SQL 全附可重跑;subagent 初報 raw SQL JOIN 12 處、自己復驗實為 14 並註明。
  • .11 jedi-common 查詢層擴充(CM-1448,2394200:三件套全「用加的」——JSONB 查詢(_ext_/_extlike_/_extin_/_exthas_/_extcontains 前綴+排序分頁;袋內一律 AND 不混 OR 群組;缺 key 不炸;未宣告袋子的 repo 靜默忽略);identity/ 身分脈絡包(三名冊 port 同構);SessionMixin 正名(舊名 base_repository 留轉發)。jedi-common 測試 215 passed。
  • .12 project_extensions 收斂(CM-1449,主專案 4c6b90a8+review 修 6f1608e5、jedi-project 0d3ea71:四欄收回主表(皆帶業務邏輯轉正式欄位非 JSONB);五步搬遷走到第 4 步「切讀取」,drop 舊表等令;14 處 raw SQL JOIN 改完 grep 殘留 0;DEV 實查回填 211 列零不一致;STG 實查未被套(環境紅線守住)。讀取端「一手切完」(覆寫 ext repo 的 get_one_by_fields 由主表供值);雙寫收在 repo 層單一入口;審計欄不互蓋(實查兩表本記不同事)。user 手測一度誤連未套 migration 的庫報 UndefinedColumn——教訓見下。

Postman 全站 API 設定檔(CM-1454,38e8734a:交付「產生器即真相」——scripts/gen_postman_collection.py 產 collection(637 支/21 資料夾)+environment+端點對照表;驗收時首腦重跑產生器 git diff 空=與路由表零落差;憑證掃描 0。掃出 62 支裸路徑死端點(23 個 Resource 雙路徑註冊缺 handler;runner 逐支查 FE 零裸打、線上零影響;初判 84 支、改讀實際 signature 收斂 62——自我修正值得記)→ 裁決「開追蹤卡隨棒順手拔」= CM-1461705d8401),design.md 第三階段隨棒必做補一行。

發版第五輪:jedi-common 0.0.33+jedi-project 0.0.9(monorepo 593334e、主專案 pin 0f8c1b93)——九支全正式版、主專案零 path 依賴。push 委任:user 明示「push 到分支授權給你做」=兩 repo feature/FR-069 收口順手推(memory feedback_fr069_push_delegated_branch_only;main 仍禁)。

兩包退役(user 拍板追加 D13):jedi-department(四面查證零使用——程式/venv/DB 表不存在/套件依賴全零)+jedi-resource-store(程式 e2e DB 全零但主專案仍 pin)。退役棒:monorepo 標記 d765191、docs 29e124bdpin 拆不開處置——working tree 的 pyproject 混有 1455 的 path override 同檔拆不開,pin 移除留工作區、docs 先行記錄、託付 .18 收口棒一併入版。

2.5 階段拆卡六張(CM-1455–1460):.13 地基(骨架+遷入策略 A/B 案查證定案)→ .14 login/.15 mfa+captcha/.17 清理三棒平行 → .16 中介層 → .18 收口。地基棒已發(user 側 session),working tree 已見 jedi-identity 0.1.0+jedi-auth 轉發殼 0.1.36(疑似 A 案)。

教訓補兩條

  1. 平行棒共用 pyproject 時,退役/pin 類改動要嘛排在 path override 清掉之後,要嘛接受「docs 先行、pin 託付後棒」——同檔混不可入版改動時硬 commit 必夾帶違規,退役棒的處置是正解。
  2. 手測報錯先看 log 的 Host 埠與連的庫——.12 手測「無法顯示專案」實為 .env 連錯庫(未套 migration);平行棒臨時服務(8010)的錯誤與主線(8000)要用 Host 分辨,別誤判成主線 bug。

§4

2026-08-31|第三階段 3.1 首棒(CM-1467,runner session)jedi-iam 完全體

⚠️ 本 block 只記 3.1 這一棒(runner 視角)。2.5 階段 .13~.18 與 .H、改名 .G、 第三階段拆卡的歷程由首腦 session 記錄,本檔上方 2026-08-30 之後若有空缺以 STATE 現況清單與各 Notion 卡為準。

交付:身分模組從「套件出 service、route 留宿主」升格為真正隨插即用的插件。 commit:monorepo e614c34(本體)+d6b0dfa(blueprint 改名)/主專案 e483ba17 (本體)+d1a141c2(檔名改名),兩 repo 已 push feature/FR-069

六件工作:①route 上移(39 條進 jedi_iam/api/,主專案 api/auth/ 26 檔刪除); ②內部結構重整(頂層 common/codecommon/enum 兩套合一、mfa/common 併回頂層、 mfa/domain/entityentities);③四殼退役(主專案 80 檔改名+拔 pin+死名守衛); ④D16 port 化(三支認證 adapter 的 10 處自建 RepoImpl 收進 AuthRepositories); ⑤GoogleAuthAdapter__init__ 的 TypeError 修掉;⑥README 架構節+README.html。

設計上的關鍵取捨(後 11 支照抄,全文見 STATE「照抄要點」)

  • route 取 service 用 ctx().service(name) 而非 @inject——Provide[Containers.x.y] 要在 import 期寫死具體 container 路徑,那是宿主的組裝知識,寫進套件=套件反依賴主專案。 改由 register()provider 名冊放進 app.extensions存 provider 不存實例: 主專案 service 多為每 request 建新的(內含 session-bound repo),存實例是那種平常 看不出、併發才爆的壞。
  • 缺認證接線要拒絕掛載_assert_api_wiring)——不是「有就用沒有就跳過」。跳過的 後果是身分端點全變公開,而服務照常起得來、健康檢查照樣綠燈。三個插槽刻意無預設值。
  • 切分判準:「換一個產品,這段還會一樣嗎」——一樣的留套件(流程順序、組樹、欄位 正規化),不一樣的走 hook 回宿主(商務授權旗標/選單過濾/密碼政策來源/防帳號列舉)。 四個 hook 都是這樣切出來的。

驗證:拔掉測試(mount_api=False → 16 支端點全 404、其餘模組無感);harness POST /login 走通拿到真 JWT(不用 DI 框架、不碰主專案一行);行為表 8 列; URL/能力點/error code 三組集合機械比對相等;突變測試兩發(關掉守門→4 紅、 拼錯一條 URL→1 紅);套件 357 綠、守衛 53 綠;8000 重啟煙測全 200 log 零 ERROR。

隨棒:CM-1461 身分 13 支死路徑照 .18 正解拆 Create Route 類(500→405,POST 仍活),已回該卡勾銷;依賴衛生順掃(套件補三個 route 層相依宣告)。

追加改名(user 指示「以前叫 identity 的換 iam」):blueprint identityiam.13 殘留、.G 漏改;三 repo 對 identity.* endpoint 引用皆 0,對外 URL 零影響)、 common/identity.pyiam_ports.pycore/identity_wiring.pyiam_wiring.py

教訓(接續上方編號)

  1. harness 只證明「掛得上」是不夠的——要實打一支端點走通。3.1 讓 harness 真的打 POST /login,一路挖出四道宿主前提init_dbPROPAGATE_EXCEPTIONS=TrueJWT_IDENTITY_CLAIMcolumn_property 用的 6 支 DB 函式),原本 harness 只記兩道。 其中 PROPAGATE_EXCEPTIONS 最陰:flask-restful 的 Api 會包掉 handle_user_exception 自行處理所有例外,於是 register_error_handlers 註冊成功卻完全不生效, 401/403/404 一律變 500——「註冊成功但不生效」這種病,靜態看程式碼看不出來。
  2. 改名要掃「寫死字串前綴」的地方:blueprint 改名時有兩處寫死 "identity." (contract 測試與 harness),它們壞掉的方式是靜默匹配到 0 條而不是報錯——測試會 變成「掃了個寂寞卻顯示通過」。已改成從 plugin.DEFAULT_BLUEPRINT_NAME 取。
  3. 改名前先分辨「舊套件名」與「領域概念」:codebase 裡 identity 兩種意思混用。 JWT_IDENTITY_CLAIMuser_identity 欄位/get_jwt_identity()resolve_identity()Accept-Encoding: identity 都不能改(對外契約或第三方 API,改 JWT claim 名會讓 所有既有 token 當場失效)。我一度把 test_middleware_identity_core.py 也改了, 翻開發現它測的正是 resolve_identity 就改回去——檔名要跟著它測的東西走
  4. common/ 不得反向 import 上層是活的守衛:宿主接線檔初版放 common/,被 test_common_has_no_reverse_imports 當場抓到(它要 import app/infra/)。 組裝根的正確位置是 core/,與 app_factory.py 同層。

§5

2026-08-30 深夜 ~ 2026-08-31|第二任首腦(協調+驗收:2.5 全收+第三階段開打+形態定調)

歷程

  1. 接手驗收 2.5 全部棒次:.13 user 判收、.14(66 檔機械比對零行為差)、.15(MFA 全鏈親打含 redis 真碼)、.16(抓到真提權洞——X-Tenant-ID:0 觸發 is_super_admin 繞全 RLS,含 socket 整數 0 falsy 變體,皆修)、.17(root 判定收斂;is_admin 實查非殭屍→裁 (A) 另開 CM-1463)、.H 補搬(D9 缺角三塊;「消滅 raw SQL」照意圖不照字面——elevated_session 保留繞 RLS,root admin 保護是承重牆)、.18 收口(發版清單備妥)、CM-1465(socketio 一行修)。
  2. 命名定案 jedi-iam(PM 推翻 identity——「只涵蓋你是誰半邊,太狹隘」;IAM=類別正名,Keycloak 屬其解決方案)→ CM-1464 改名棒(monorepo git mv 321 檔+BE 96 處 import+三支 AST 死名守衛;jedi_identity 不留殼直接死名)。
  3. 驗證瘦身規則七條(user 拍板,實測驗證吃 6 成工時;判準「壞了會不會靜默」;同型棒信任遞減;首腦驗收改抽查)——入 STATE,CM-1464 起生效。
  4. 發版擱置(user 裁「先不發版直接進第三」)——五支清單(iam 0.1.0/auth 0.1.36/login 0.0.24/mfa 0.0.15 必發/captcha 0.0.15)與 patch 備妥等令;pyproject 三批託付改動續留 working tree。
  5. 第三階段拆卡六張(CM-1467–1472)→ 3.1 首棒收(39 URL 集合比對/api/auth 刪除/照抄要點 7 條=範本/authz 命名維持不改的實查裁量)→ 3.2 file-upload(兩輪:API 全過→16 test ERROR 退回→runner 三證推翻首腦歸因「本棒連帶」為既有存量債,接受更正收 Done)→ 3.3 notification 收(「拔掉≠寄信消失」雙用法誠實揭露;README 原是 issue 照抄件重寫)→ 3.4 煞車(19 條 route 17 條吃主專案 service,「兩個流程範本」同名不同物;裁 (a) 降級輕量+(c) route 歸屬併第四階段流程疆界三方設計)。
  6. flow_control 拆出升格第四階段議程(user 指示;確認指涉=稽核業務層 37 支 service 非範本管理;拆法定調「先單純拆出、切分後議」照 jedi-iam 先搬家後裝修先例)。
  7. 形態知識大量沉澱(user 連續追問插件模式的運作細節,全部落檔):架構手冊常設專區成立(docs/features/architecture-handbook/——插件架構指南+模組化路線圖,自 FR-069 站移出改名);插件指南擴至九節(使用流程五步/接線 QA 三題/資料關聯三律/查詢實戰 QA 六題+API 歸屬總表——「各檔口的菜檔口自己賣,套餐才開櫃檯窗口」)。
  8. 第四階段前置盤點完成:D12 查證(CM-1462:77 條 97% 服務層缺能力,建議中間路線)+跨疆界聚合 API 盤點docs/analysis/2026-08-31-cross-boundary-aggregation-api-inventory.md最重聚合在 infra 跨 schema raw SQL 非 service 注入——8 支聚合 query 約 2,000 行、冠軍一句 SQL 19 表七疆界;真聚合端點 25–30 條;三條建議待裁——readmodel 正名/AI registry 自註冊/TaskAssigneeService 切法)。
  9. 第四階段議程累積至六件:核心抽取、flow_control+流程疆界三方、設定 schema 申報制(作用域/fallback 細節已記)、資料關聯三律、聚合層設計(依盤點)、P13 中間路線待裁。

教訓(後棒引以為戒)

  1. 開卡前必查 design.md 對該對象的既有標記——3.4 卡片寫「route 上移」但 design.md §L360 早寫「flow-engine 未做完整判定、建議另立流程疆界棒」,首腦開卡沒引用導致前提錯誤;runner 靠「前提被推翻就停」救回。同型教訓在 is_admin(卡片寫殭屍、實查活線)已發生過一次——卡片斷言的查證責任在開卡人,煞車紀律是最後防線不是常規
  2. 首腦誤夾帶平行棒 staged 檔兩次(.18 的 captcha 刪檔/3.1 的 api/auth 刪檔)——commit 帶 pathspec 顯式列檔可防(git commit <files> -m),事後 reset --soft 拆開可救;平行棒在跑期間首腦最好完全不 commit
  3. 驗收歸因要三證才下結論——3.2 的 16 ERROR 首腦初判「本棒連帶」,runner 用「參數來源 commit/本棒未觸該檔/父節點同紅」三證推翻。壞在 setup 的測試(ERROR 非 FAIL)會遮住「測不存在方法+斷言與實作相反」的存量債。
  4. runner 未 push 不算違規——3.3 選擇等令(委任其實涵蓋),下一棒順手補即可;分辨「保守」與「漏做」。
§6

2026-08-31 ~ 2026-09-01|第三任首腦(協調+驗收:第三階段收官+發版+第四階段 17 棒)

歷程

  1. 接手驗收 3.5(CM-1471 輕量批七支——守衛須 python -m pytest 否則假紅的坑在此發現)→ 3.6(CM-1472 退役四支,monorepo 26→24)→ 發版裁決:user 質疑四殼為何要發,重盤後裁不發改退役(.18 清單的前提已被 3.1 拔 pin 推翻)→ CM-1473 發版棒(11 支上 Nexus、pin 入版收三批託付、四殼刪除 24→20、守衛換軌)。
  2. 第四階段拆卡九張 CM-1475–1483(四待裁全拍板:聚合三建議全採/P13 中間路線/flow_control 排 P12 後/首棒 4.A)→ 與 CM-1474(DROP 清債:孤兒表退役+project_extensions 收尾,P-like 雙路徑驗證,抓到 .12 漏網 view 活依賴)平行跑。
  3. 平台化方向拍板(user 願景討論):project=插座不是插頭(被功能套件 register 的平台核心);flow_control 分家=流程骨架半供複用+稽核半自成插件(「分家是複用的前提」);終局五層包裝。決策頁 project-platform-decision.md+大架構圖 big-picture.md+相依圖 dependency-map.md 落架構手冊。
  4. 4.A(D17 資料關聯三律/D18 設定申報制入決策表+分類地圖十族;③readmodel 搬遷因名單衝突煞車→裁「純讀聚合」為準併入 P12 步驟 0)→ 4.B(AI registry 申報制,壞條目 2→4)→ P12 jedi-participant(readmodel 櫃檯建置+40 組參數 md5 比對+A/B 對打抓到 Containers 類別層真回歸;identity JOIN 債→CM-1484)。
  5. 4.2 全鏈:設計稿(切線 12/22/12+D-1~D-9)→ user 抓黑話→白話改寫棒(零漂移)→ CM-1485 盤點(camunda 程式死/環境有孤兒容器 189 user 裁不動/「只剩 BPMN 解析」推翻——執行面活著)→ CM-1486 退役 4,351 行(三護欄各突變實證)→ D-1~D-9 全採(D-7 補 port 閥)→ CM-1487 搬遷(runner 煞車推翻「226 檔比照 P12」——16 支依賴未套件化模組,裁 A 拆兩棒,190 檔進 jedi-flow-control)→ CM-1488 分家(平台層零稽核詞機械判準/兩處設計稿判反改判/D-10 材料:DB 四條 CASCADE FK vs import 僅 2 條)→ D-10 裁「償債維持兩包」→ CM-1490 第 3 步(FK 拆軟參照/申報制上線/問卷審核開關「設定不是客製」實證/D-1 job_service 三分)。
  6. CM-1489 detection:先煞車(首腦裁決「綁定表歸 flow_control」被推翻——用量反向+FK 指向 detection——裁 B 留檢測側)→ 服務化評估裁插件 → 抽出 147 支(裁示④漏項→CM-1491 小卡)。CM-1481 P10 evidence-classification(五張 port/兩表刻意不加 RLS/突變抓到自家守衛盲點)。
  7. 三包定名(user 註可能再調):flow-engine 不動(實查更正首腦誤述——執行引擎活著在套件內非「BPMN 工具箱」,BPMN generator 2,758 行實住主專案裁歸引擎)/task-platform/compliance-audit(不佔 audit 泛稱)。申報書選配化定調(survey 核心不依賴 task declaration)。CM-1492 打包棒+CM-1482 P11 兩棒發出(在途)。
  8. 族譜節入路線圖(已完成/退役/進行中/未完成全景);收官棒待辦累積節立於 STATE(readmodel 四規格+分組/族譜終版/四類清單/SQL 守衛可選)。

教訓(後棒引以為戒)

  1. 卡片「比照先例」前必驗前提等價(CM-1487:P12 能機械搬因 out-degree 8,flow_control 纏五疆界照抄翻車)——已入 memory。
  2. 首腦裁決含事實斷言也要附實查(CM-1489:裁「綁定表歸 flow_control」只憑語意沒跑 grep/pg_constraint,被 runner 用量與 FK 方向推翻)——本任裁決被推翻共 2 次、卡片斷言被推翻共 4 次,煞車紀律全數救回。
  3. 平行棒共用 git index 事故(b74bd564 誤收平行棒 35 檔):顯式 add 防不了「別人已 staged 的混進來」——commit 前必 git diff --cached --stat 核清單,已入所有後續 prompt。
  4. 驗收抽打前先核 8000 跑的是不是最新碼(兩次撞到重啟空窗/舊碼——比對進程啟動時間 vs 最後 commit 時間)。
  5. view 與 SQL 字串是守衛盲區(CM-1474/1486 兩案實證)——已入 memory;D17 補 view 條款。
§7

2026-09-01|CM-1500 問卷共編即時同步修復(runner 兩階段:偵察煞車 → 裁定後收口)

背景:CM-1482 問卷疆界合併(P11)搬家後,user 手測抓到 8002 問卷共編整組失效—— 進問卷頁跳「即時同步失敗」,join/update 一律回「更新失敗,請稍後再試」。

歷程

  1. 第一階段(偵察+主專案側接線,97133497:實測複現坐實根因——socket handler 搬進套件後走 ctx()app.extensions["jedi_survey"],而該鑰匙只在掛 REST blueprint 時由 record_once 寫入;socketio 模式刻意不載 REST(FR-063.1c 不可倒退), 於是每個事件呼叫 ctx() 就 RuntimeError。連線與身分驗證皆通過(排除 auth/Redis/CORS); 不載 REST 的 app 其 extensions 清單實查確認無 jedi_survey。notification 的 socket handler 不經 ctx() 故未中招,對照吻合。
  2. 撞到卡片明訂的停止條件:修法要走的 register(..., mount_api=False) 簽名有、 行為不夠——該分支只做 _configure_runtime() 就返回,不寫 extensions。即 「不掛路由」與「拿得到 context」在套件裡是綁死的二選一,宿主端無論怎麼接線都補不出。 照卡片邊界停手回報(不繞道、不在 8002 掛 REST、不自行改套件)。為確認「缺的只有這一項」, 本機暫時改 site-packages 驗一輪即還原(md5 比對確認與發版版本逐位元組相同)。
  3. 第二階段(user 裁 A 案後收口):套件補 4 行(monorepo 28e8be3,jedi-survey 0.1.1 上 Nexus)+主專案 pin 跟上(9312ba3d)。語意刻意與 record_once 一致 ——無條件覆寫、最後一次贏(那邊是 __setitem__ 不做 already-exists 檢查; library 模式若寫成「已存在就跳過」,同一 app 先後接兩份 adapters 會得到相反結果)。
  4. 驗證:複現腳本三輪(修前 RED/本機暫改 GREEN/正式版 0.1.1 GREEN 且 DB 實查 答案確實寫入);雙客戶端共編實測(A 改答案 B 即時收到、在線名單互見、零錯誤); 8000 URL 424 條逐字同+帶 token 打 /surveys/menu 回 200;守衛 77 綠(併 iam_wiring 85); 兩模式重啟乾淨;DB 零淨異動(探測值與在線名單殘留皆清)。突變兩發:拿掉寫入 → 2 failed、 改 skip-if-exists → 1 failed。user 瀏覽器手測通過收 Done

決策

  1. 裁 A 案——只修 jedi-survey 單獨發 0.1.1,其餘七支同款缺口(iam/file-upload/ detection/notification/system-menu/issue/log)記帳不修,併 CM-1497 當 D6 契約 統一補齊。理由:那七支現無 socket 模式使用場景、缺口不發作,而修它們等於重發七支套件; 一次統一處理比七次 patch 發版乾淨。已 append 至 CM-1497(含修法與參考 commit)。

教訓(後棒引以為戒)

  1. 「逃生門存在」不等於「逃生門夠用」——卡片修法指向 mount_api=False,簽名確實有, 但實際行為缺一半。契約要看實作不看簽名;runner 照邊界停手回報,是這次沒被繞成 「在 socket 模式掛 REST」(會倒退 FR-063.1c)的原因。
  2. 套件全量測試的 baseline 要先量再歸因——jedi-survey tests/unittest/ 有 48 failed/42 errors,用 git stash 抽掉改動跑同一份指令對照確認改動前就存在, 本次是 +2 passing。不先量 baseline 就會把存量債誤記在自己頭上(同型教訓見第 12 條 3.2 的 16 ERROR 案)。
  3. 契約測試要同時焊「行為成立」與「語意一致」——只驗「拿得到 ctx()」的話, 把寫入改成 skip-if-exists 仍會綠;補第二條(重複 register 覆寫語意)才擋得住。 兩條各以突變實證有牙齒。

推翻了什麼

  1. STATE 的「8000 現跑舊碼/現在重啟必炸」已失效——CM-1499 發版棒還原 pin 後 path override 已全清,本棒實測 8000/8002 重啟皆乾淨。連帶:拉分支不再需要 pip install -e 六支poetry install 即可(該條交接事項已從 STATE 移除)。
§8

2026-09-01(晚)|第四任首腦(協調+驗收:收官串——review/修復/文件/補發版)

歷程

  1. 接手時第四階段 19 棒收 17,在途 CM-1492 打包棒+CM-1482 P11。兩棒收後驗收,其間 user 質疑「收 Done 的卡有沒有沒人接的遺留」→ P4+P3 共 24 張卡全文重讀盤點,抓出五件懸案開卡:CM-1493 償債(平台包反向依賴斷根/detection handler 640 行歸位/1482 shim 清理)、CM-1494 查證(殭屍定讞)、CM-1495 孤兒清理、CM-1496 SMTP bug(1469 說「另開卡」從未開)、CM-1497 套件債清理。原卡全部 append 指向,防再漏。
  2. user 手測期間三場環境修復:①問卷共編失效=CM-1482 搬家真回歸(socketio 模式不載 REST → survey 插件從未註冊 → ctx() 炸)→ CM-1500 兩階段修(runner 煞車:套件 mount_api=False 簽名有行為不夠,補 4 行發 0.1.1);②檢測「agent 怪怪的」=DB agent uid 與 agent 自持 uid 漂移(JWT aud 對不上)+分派表也存舊 uid+.151 SSH 公鑰遺失,三處修完派工鏈通;③SMTP 測試信=CM-1496 修(.get() 用錯+RLS 讀不到 ROOT 設定)。
  3. arc-review 5 agent 平行(type/silent/comment/spec/code-quality)→ 報告 1 Critical+10 Important;主題「綠著的守衛不代表在守」(守衛掃改名前舊名永遠綠/掃描根縮水/兩處 docstring 宣稱有守衛但不存在)。
  4. CM-1501 全修(user 裁全修):13 條處置完畢,突變 15 發;C-1 撞號 GRC_400105 一碼兩用修成 GRC_400121 三 repo 同步。
  5. CM-1502 文件收官(第一段)+CM-1503 補發版(四支 patch 上 Nexus,可拉性三層驗證含解包 grep)。
  6. 190 dev server 建置:user 裁「不編譯直跑」→ 後改 nginx 服務 build 產物;補套 26 支 migration(帶舊資料升級實地驗證);切 on-prem 形態;修好 nginx /socket.io 錯指 8000 的既有設定錯誤。

教訓(接續編號)

  1. 收 Done 不等於沒有遺留——卡片尾段的「留給後棒」若無人接卡就會靜默消失。P3/P4 掃卡抓出五件懸案,其中 1469 的 SMTP bug 從發現到開卡隔了兩天、1489↔︎1490 的 640 行 handler 互踢到兩卡都 Done 還沒人認領。判準:任何「交後續/另開卡/待議」的字樣,當下就要開卡或明記落點,否則它只活在那張卡的尾段。
  2. 環境漂移的症狀不指向原因(本任三場修復全屬此型):agent 身分漂移的表徵是「測試連線失敗」、SSH 公鑰遺失的表徵是「掃描 Authentication failed」、socket 插件未註冊的表徵是「即時同步失敗」——都要追到最底層才看得到真因(agent log 的 JWT aud 不符/私鑰直連實測/app.extensions 實查)。
  3. 「簽名有 ≠ 行為夠」(CM-1500):套件的 mount_api=False 逃生門存在但不寫 extensions,宿主端無論怎麼寫都補不出鑰匙。契約要看實作不看簽名;runner 照邊界停手回報而非繞道(繞道=在 socket 模式掛 REST,會倒退 FR-063.1c)。
  4. 守衛會靜默失效於改名與搬遷(arc-review 主題):四條守衛在改名/搬家後仍是綠的但守不到東西。修守衛必做突變驗證;新增守衛時想「這條會不會在下次改名後空轉」。