diff --git a/openspec/changes/add-single-gpu-session-ai-review-mvp/design.md b/openspec/changes/add-single-gpu-session-ai-review-mvp/design.md new file mode 100644 index 000000000..5e1e4ce57 --- /dev/null +++ b/openspec/changes/add-single-gpu-session-ai-review-mvp/design.md @@ -0,0 +1,116 @@ +## 背景 + +repo 現況已具備外部 IFC Worker → `bim-review-coordinator` → `bim-streaming-server` → metadata-only callback 的最小 B 方案閉環,Kit WebRTC 有 primary 49100 + spectator 49110~49150、1 primary 最多 6 人同看的既有配置,且 `governance-service` 已有 issues store 與 BCF-IDS 匯出。缺口在於:多人 WebRTC 會議沒有任何生命週期管理(admission/回收/佇列/watchdog),第二個使用者一個誤觸即可擊落正在進行的會議;且 AI 審查→草稿→BCF 的半自動鏈尚未落地。 + +本 change 採 MVP 優先、疊加式:首要建立單 GPU session 生命週期,再疊加「AI 產草稿 → 人審轉正」最小切片。所有交付為新增 routes/欄位/頁面,不動凍結三檔(`app.py`/`conversion_authority.py`/`governanceProxy.ts`),`:8004` proxy byte-identical。 + +## 目標/非目標 + +**目標:** +- 讓單 GPU 上的多人 WebRTC 會議有可恢復、可稽核的生命週期:admission fail-closed、primary 佇列、spectator 分流、TTL/idle 回收、健康探針與自動復原。 +- 先量測後承諾:Phase 0 基準 harness 是 CAP-1 硬 gate,admission 參數必須引用實測報告數值。 +- 讓 AI 審查定位為「高召回證據準備者」:draft gate 結構性保證 AI 無路徑直建/關閉/指派/reopen 正式 issue。 +- 讓 issue 跨版本冪等收斂(120 命中→1 parent),版本回跑產機器可讀差異報告。 +- issue 契約一開始就 pin BCF-API 3.0 標準,驗證層 pin IFC 4.3、幾何走 adapter。 + +**非目標:** +- 不上 K8s/MIG/多 GPU 水平擴充(消費級 RTX 不支援 MIG,僅 A100/H100 等資料中心卡支援),只記錄 SessionBroker driver 介面約束。 +- 不做 Kit 串流內 fly-to 自動導覽、不做 live WebRTC 視角→BCF viewpoint 即時擷取橋接(MVP 用離線 bounding box viewpoint 替代)。 +- 不建 LLM 分類/分流層(僅凍結「可整層關閉不中斷審查」的降級接縫)、不建 IDS 1.0 完整規則引擎。 +- 不做 auth/RBAC/project-tenant 隔離(維持 `:8004/ui` 現況)、不做 AI 繪圖寫回、不做 BCFzip serializer、不做完整 OpenCDE Foundation API server。 +- 不含建築工作室 agent 的學習/訓練(使用者明示不屬本 repo 範疇)。 + +## 決策 + +### 1. 單 GPU 路線=單一常駐 warm primary + spectator 分流 + primary 佇列 + +**依據(簡報 confirmed)**:NVIDIA 官方明文 "limit each GPU worker instance to a single stream";論壇實測單機第二個 Kit session 會使前一個斷流,port remapping/multi-container 變通均失敗,社群建議改用多台各配單 GPU 的獨立機器。官方 production-grade 多 session 靠 K8s 控制面(Streaming Session Manager + Resource Management Control Plane + CRD/Helm + Min=Max 預熱池),但那是為多 GPU 雲端叢集設計。 + +**裁決**:單 GPU(消費級 RTX)首要路線放棄 MIG 與多 instance 預熱池,改走「單一常駐 warm Kit session + viewer 佇列/spectator 分流」,用 `omni.services.livestream.session` REST(GET `/v1/streaming/ready`、POST `/endsession`、POST `/creds`)自建輕量 app 層排程器。K8s/MIG 僅列未來多 GPU 水平擴充選項。 + +**取捨**:admission 競爭單位=會議 session(1 primary + N spectator)而非個別使用者——後加入者一律走 spectator 分流不佔新 GPU、不進 primary 佇列;只有請求「新 primary(=新會議)」才進佇列競爭。這把「會議已滿」(席位 fail-closed 拒絕)與「需要新 GPU」(進佇列)明確分流,避免誤導觸發不必要的佇列競爭。 + +### 2. 量測先於承諾(measure-before-commit)=Phase 0 硬 gate + +**依據(簡報 open_questions)**:單 GPU 靠 app 層排程可穩定支撐幾個並發 session、每 stream VRAM/vCPU/頻寬、Kit 長連線是否記憶體洩漏——官方全無數字,本 repo 亦無既有實測。簡報 open_question 特別點出「Kit WebRTC 連線期約 20GB/日記憶體洩漏(來自 Isaac Sim livestream 案例)是否適用本 repo 目前 Kit 版本,須本地長連線基線量測確認」。 + +**裁決**:洩漏 watchdog 門檻、並發上限、idle-timeout、TTFF 上限、建立成功率下限一律由本次本地實測決定,**不得引用簡報未涵蓋、未經驗證的任何外部系統數字(含任何 GB/日 之類外部洩漏率)**。無基準報告則 admission 參數不得上線。SLO 以具體數值寫入可稽核部署文件,禁「合理」「足夠」等模糊詞。 + +**環境指紋綁定**:所有 SLO 綁定 Phase 0 報告的環境指紋(GPU 型號/driver 版/Kit 版/量測 fixture hash+大小);指紋任一變動即令 SLO 失效須重跑基準,排程器啟動亦 fail-loud 攔截。此原則於 Phase 2 延伸至 fingerprint 幾何容差校準(R4.4)。 + +**bootstrap 切斷論證**:Phase 0 soak 於隔離 stack(沿用 repo「branch E2E 隔離」模式:獨立埠、獨立 governance)+獨佔量測窗執行,harness 內建最小 keepalive health probe 維持連線活性;量測標的=裸 Kit process 資源行為。因 SessionBroker 為 out-of-process 控制面(僅 admission/回收/佇列,不注入 per-session 渲染路徑),裸量結果可作排程器門檻依據。此切斷論證的邊界見 Open Question OQ-5(是否需在 k=5 spectator + 健康探針負載下量測)。 + +### 3. AI=高召回證據準備者,draft gate 結構性保證 + +**依據(簡報 confirmed)**:Text2BIM 等研究原型驗證「LLM 生成→IFC→model checker 幾何/碰撞檢查→輸出帶 GUID 的 BCF→Reviewer agent 讀 BCF 產優化建議」semi-automated 鏈可行,但無生產級開源可抄;建築簽證多法域仍要 direct supervision,涉安全部件判定可能落入 EU AI Act Annex III 高風險(強制人為監督/日誌/風險管理)。競品(Solibri Autorun、BIMcollab BCF Live、Autodesk Forma、Speckle Automate)成熟但差異化須落在整合原創性與治理嚴謹度,而非「AI 自主權威判定」。 + +**裁決**:自派發重定義=自動產生 + 路由人審佇列 + 去重排序;建立/關閉/指派/reopen 一律人審 gate。AI 產出一律 draft 狀態(issues store 疊加 `source_type=ai_review`),draft 不出現在正式 issue 清單、不觸發派發。正式 issue 淨增=人審 accept 數。 + +**規則引擎先行、LLM 層可整層關閉**:deterministic IfcClash 承擔主審查量;LLM 分流/優化建議層設計成故障或超預算時可整層停用而不中斷核心審查閉環。MVP 不建 LLM 層,僅凍結此降級接縫(見 Open Question OQ-2:MVP 的「AI 審查」實為零模型推論的確定性 clash detector,須向使用者揭示 headline 落差)。 + +### 4. 冪等價值=收斂非產量,兩層 fingerprint 兜底 GUID churn + +**依據(簡報 key_constraint + repo 現況)**:BCF issue 需跨 model 版本冪等,套用 repo 既有冪等鍵模式(`mw_` 前綴 hash / ConversionLedger atomic swap),避免每次重跑審查變 issue 工廠。repo 已知 IFC 重匯出常重配 GlobalId(GUID churn),是既有 A1–A4 反覆踩過的坑。 + +**裁決**:第一層=GUID 組合 + 規則 id 精確匹配;第一層 miss → 第二層=規則 id + 幾何量化位置 bucket,命中標 `guid_churn_suspected` 必路由人審、**禁自動 dedup/suppress**(防兩個實為不同的 finding 因幾何量化落同 bucket 被誤併而隱藏真實新 finding)。分群鍵寫死 `(rule_id, sorted 涉事元素 GUID 集合)`,明確不採距離/樓層/系統啟發式。幾何 bucket 大小引用 R4.4 校準報告(GUID 存活率與幾何容差曲線),禁模糊詞。**GUID churn 常態下的資料模型形狀(新 draft vs merge-candidate)與 parent 級一鍵確認語意仍未定義,見 Open Question OQ-1。** + +### 5. MVP viewpoint 用離線 bounding box、以 IFC 世界座標表述 + +**依據(簡報 open_question)**:Kit 即時視埠→BCF viewpoint 擷取橋接(把 live WebRTC 相機視角落成 BCF viewpoint)機制未規範,實查 governance issues 表只有 `ifc_guid`/`usd_prim_path` 無 camera/viewpoint 欄位,此為 Kit 與 BCF 唯一整合接縫。 + +**裁決**:MVP viewpoint 由幾何 bounding box 離線計算相機參數,以 IFC 模型世界座標系表述(符合 BCF 規範語意);轉檔管線 IFC→USD 的座標變換(含 georeference offset)記入 ConversionLedger,保留可稽核變換鏈供 viewpoint 反算與跨工具互通。live Kit 擷取橋接與 Kit 串流內 fly-to 延後為 future work——最小成本滿足「每 finding 必附 viewpoint」硬約束。會議中 issue 定位在 MVP=人看 triage 頁 + 匯出 BCF 給外部工具的協作流程,非 Kit 串流內自動導覽。 + +### 6. issue 契約 pin BCF-API 3.0、驗證層 pin IFC 4.3、幾何走 adapter + +**依據(簡報 confirmed)**:IFC 4.3=ISO 16739-1:2024(2024 正式通過);buildingSMART BCF-API 3.0(RESTful topic/comment/viewpoint + JSON schema,屬 OpenCDE API family)為 issue 資料契約;IFC5(IFCX,alpha,schema 引用 USD)走 USD 化元件化,保留遷移路徑。WebRTC 須遵 RFC 8825/8826/8827 強制 DTLS-SRTP。 + +**裁決**:匯出物以 BCF-API 3.0 官方 JSON schema 驗證通過為準,禁自造私有格式;MVP 只做匯出端點,不做完整 OpenCDE Foundation API server。驗證層 pin IFC 4.3,幾何存取走 adapter 不硬編 IFC4.3 entity,保留 IFC5/IFCX 遷移路徑。BCF-API JSON 與 BCFzip 共享同一 topic/comment/viewpoint 邏輯模型,MVP 交付 JSON 面,BCFzip 列 fast-follow(見 Open Question OQ-B)。 + +### 7. 成本基準=Omniverse 現免費、企業支援選配 + 制度化查證 gate + +**依據(簡報 confirmed)**:2026-07-01 官方公告 Omniverse 開發與生產雙雙免費化,取消強制訂閱 AI Enterprise($4,500/GPU/年已過時);但 2025-10 有 AI Enterprise/Omniverse Enterprise 整併為 NVIDIA Enterprise 之變動且來源曾 citation mismatch,簽約/生產前須調閱官方 EULA/pricing 逐字確認。可觀測性走 DCGM Exporter + Grafana(dashboard 12239)。 + +**裁決**:規格以「Omniverse 現免費、企業支援選配」為基準,但加制度化 gate G3:宣稱 production-ready 前 checklist 必有一項「Omniverse EULA/pricing 逐字確認完成,附官方文件連結與日期」。不把免費當永久前提。 + +### 8. 架構接縫(YAGNI 約束下的最小可替換介面) + +直接回應使用者「多 session 和多 GPU 先具備基礎能力」,讓單 GPU 實作未來擴充時只需換 driver 不改 caller,但以最小成本落地。 + +- **SEAM-1 SessionBroker driver 介面**:對外 API 不含單 GPU 假設;一級 API 明列 `create(model_ref)`(起新會議/新 primary,需獨佔 GPU、走佇列競爭)與 `join(session_id, role)`(加入既有會議,`role ∈ {primary 接手, spectator}`,spectator 不佔新 GPU、不進佇列)兩動詞。Phase 1 提供 `SingleGpuDriver` 唯一實作 + `InMemoryFakeDriver`(供 contract test)。**YAGNI 防鍍金 gate**:此介面必須在 Phase 1 內有一條走完整介面的垂直切片 + 一份 contract test 證明「換 driver 不改 caller」,否則砍掉抽象退回直呼。多 GPU/K8s driver 列 future work,僅記錄介面約束不建實作。 +- **SEAM-2 Finding SPI(checker 介面)**:IfcClash 為 Phase 2 唯一 checker 實作;介面預留 IDS 1.0 規則引擎與 LLM 分流層可插拔位,並要求 LLM 層設計為「可整層關閉不中斷主審查」。MVP 只落地 IfcClash checker,同受 YAGNI gate 約束。 + +## 資料流與 source-of-truth 權責 + +| 持久資料 | 權威 | +|---|---| +| Session 生命週期 state(admission/佇列/lease/idle/health event ledger) | `bim-review-coordinator` SessionBroker | +| Kit runtime lease/readiness/stage、WebRTC endpoint | `bim-streaming-server` / Kit runtime | +| Phase 0 基準報告 + 環境指紋 + SLO 數值 | 可稽核部署文件(tracked) | +| AI draft records(`source_type=ai_review`、fingerprint 鍵、版本號、evidence/last_seen) | coordinator draft store(單一寫入者序列化) | +| 正式 issue、annotation、BCF 持久化 | `governance-service` issues store | +| 稽核 ledger(accept/reject/edit、操作者/時間/AI 版本標記) | coordinator(持久化落點見 Open Question OQ-6,須在 rebuild/clean 洗除範圍外) | +| IFC→USD 座標變換鏈(含 georeference offset) | ConversionLedger | +| resolved-candidate 差異報告 JSON | coordinator(版本回跑產出) | + +## 驗證策略與環境限制 + +- **GPU/Kit 限制**:Kit GPU 渲染僅 host-native(Docker/WSL2 無 NVIDIA 繪圖驅動,repo 既知);Phase 0 量測與 6 人會議 E2E 皆須真實 host-native Kit + RTX GPU。 +- **health ≠ port-open**:健康判準=readyState=4 + 影像尺寸 + DataChannel 回應。 +- **隔離量測**:Phase 0 soak 用獨立埠 + 獨立 governance(沿用 branch E2E 隔離模式),不碰部署區 `:8004`。 +- **零破壞回歸**:`:8004` proxy byte-identical + 凍結三檔 git diff 為空列入驗收;既有 A1–A4/轉檔閉環測試全綠。 +- **contract test 位置**:driver 與 Finding SPI contract test 落 `tests/contracts/`;各服務單元/整合測試留在各自服務目錄避免 import cache 污染。 +- **假綠防護測試**:缺 OpenCASCADE 跑審查須明確報錯(非 0 findings)、大模型 size guard、health 非 port-open、環境指紋不符啟動 fail-loud、ingest 缺欄位 fail-closed、幾何 fallback 強制人審——各有自動化測試。 +- **stop-and-ask**:GIVEN Phase 0 實測 6 人 fixture 安全上限 < 6,THEN 觸發 stop-and-ask 呈使用者裁決(降人數/換 fixture/調品質參數/接受風險),禁規格靜默下修或硬湊 6 人。 + +## 反假綠檢核表(集中列出已知假綠模式) + +| 假綠模式 | 結構性防護 | +|---|---| +| 缺 OpenCASCADE 靜默回 0 findings | R3.1 has_occ hard guard,缺依賴 fail-loud + size guard 有測試 | +| 冷啟動假裝同步成功 | R1.4 冷啟動一律 202 + statusUrl,UI 顯示進度 | +| health 用 port-open 誤判存活 | R1.3 health=readyState=4 + 影像尺寸 + DataChannel 回應 | +| ingest 缺欄位仍進佇列 | R3.2 fail-closed:缺任一欄位進 abstain 桶不進佇列 | +| admission 資源忙碌仍多開 | R1.1 fail-closed:忙碌預設拒絕/佇列 | +| 以輸入事件判 idle 誤殺活會議 | R1.2 idle 綁 readyState=4 已連線 peer,禁用輸入/滑鼠活動判 idle | +| GUID churn 幾何 fallback 靜默 false-merge 隱藏新 finding | R4.1 `guid_churn_suspected` 命中強制人審,禁自動 dedup/suppress | +| 舊機器基準管新環境 | R1.7/R2.3 啟動時環境指紋比對,不符 fail-loud,不靜默沿用舊門檻 | diff --git a/openspec/changes/add-single-gpu-session-ai-review-mvp/proposal.md b/openspec/changes/add-single-gpu-session-ai-review-mvp/proposal.md new file mode 100644 index 000000000..8a9fcbd5f --- /dev/null +++ b/openspec/changes/add-single-gpu-session-ai-review-mvp/proposal.md @@ -0,0 +1,69 @@ +## Why + +使用者首要目標是「穩定多 session、單 GPU 的生命週期」,其上再疊加「AI 產草稿 → 人審轉正」的最小審查閉環,Kit 定位為前期渲染 / 3D viewer / WebRTC 多人會議檢討。四個問題驅動本 change: + +1. **單 GPU 是硬牆,且現況無生命週期管理**(簡報已驗證):NVIDIA 官方明文 "limit each GPU worker instance to a single stream";論壇實測單機第二個 Kit session 會使前一個斷流,port remapping/multi-container 變通均失敗。repo 現況已有 primary 49100 + spectator 49110~49150、1 primary 6 人同看的前例,但沒有 admission control、TTL/idle 回收、watchdog、佇列語意——第二個使用者一個誤觸就能弄斷正在進行的多人檢討會議(旅程斷點 J6)。此為 MVP 第一支柱。 +2. **最核心的量化缺口沒有答案**(簡報與 repo 皆無數字):單 GPU 靠 app 層排程可穩定支撐幾個並發 session、每 stream VRAM/vCPU/頻寬、Kit 長連線是否記憶體洩漏,官方全無數字,本 repo 亦無既有實測。MVP 哲學:先建量測 harness 拿到本地基準,再談擴充;不先蓋 K8s 空中樓閣,也不援引未經驗證的外部案例數字。 +3. **AI 自審自派發若一步到位就是 issue 工廠 + 簽證風險**(簡報已驗證):semi-automated 鏈(checker→帶 GUID 的 BCF→人審)可行但無生產級開源可抄;建築簽證多法域仍要 direct supervision,涉安全部件判定恐落入 EU AI Act Annex III 高風險。裁決:自派發重定義=自動產生 + 路由人審佇列 + 去重排序,建立/關閉/指派一律人審 gate。MVP 只做「AI 產草稿 → 人審轉正」。 +4. **A1–A4 既有閉環不能被改壞**(repo 現況):`:8004` proxy byte-identical、禁改凍結三檔(`app.py`/`conversion_authority.py`/`governanceProxy.ts`);ConversionLedger/MinIO/issues store/BCF-IDS 匯出已存在。MVP 一律疊加式,零破壞。 + +**驗收以單一 vertical slice 為準**(非整條 J0→J7 全量):用真實 IFC fixture 跑完一圈「轉檔 → AI 審查產草稿(含 GUID/viewpoint/引用/信心值)→ 人審 accept 轉正式 issue → BCF 3.0 匯出 → 6 人 WebRTC 會議看模型討論該 issue ≥30 分鐘不斷流 → 新版本回跑產 resolved-candidate 差異報告」,全程有基準數據與 E2E evidence。 + +## What Changes + +四個 capability、四個交付分期,全部疊加在既有 A1–A4 之上: + +- **Phase 0(先量測再承諾)**:GPU/session 基準量測 harness(`gpu-session-baseline`)— 是 `session-lifecycle` 的硬 gate。 +- **Phase 1(首要目標)**:單 GPU session 生命週期管理器 SessionBroker(`session-lifecycle`,吃 Phase 0 基準當 admission 參數)。 +- **Phase 2(最小 AI 審查環)**:AI 審查草稿管線(`ai-review-draft-pipeline`)+ 跨版本冪等去重(`issue-idempotency`)。 +- **Phase 3(人審轉正 + 標準出口)**:人審 triage 佇列(`human-triage-queue`)+ BCF-API 3.0 契約匯出(`bcf-contract-export`)。 + +主要行為變更(皆 additive,不動凍結三檔): + +- SessionBroker:admission control(fail-closed,競爭單位=會議 session 非個別使用者)、primary 佇列(requester-TTL + 認領視窗)、spectator 分流(49110~49150,滿員 fail-closed)、TTL/idle 回收(idle 綁 readyState=4 peer 存在性)、顯式 terminate、環境指紋啟動比對 fail-loud;封裝 `omni.services.livestream.session` REST(`/v1/streaming/ready`、`/endsession`、`/creds`)為 coordinator 疊加路由。 +- session 健康探針(readyState=4 + 影像尺寸 + DataChannel)、`-ResetUser` 自動復原、冷啟動 202 + 狀態輪詢端點。 +- Phase 0 量測 harness:隔離 stack + 獨佔量測窗 + 內建 keepalive probe、nvidia-smi + WebRTC health probe + TTFF 基準報告(含環境指紋必填欄位)、≥30 分鐘 soak 記憶體斜率、SLO 數值化寫入部署文件並綁定指紋。 +- IfcClash 規則式碰撞審查(has_occ hard guard fail-loud + 大模型 size guard),AI finding 證據包強制欄位(fail-closed ingest:GUID + 規則引用 + 離線 bounding box viewpoint + 信心值 + abstain 桶)。 +- issues store 疊加 `source_type=ai_review` 與 draft 狀態;AI 無直建正式 issue、無直接 reopen 正式 issue 路徑。 +- 兩層 fingerprint 冪等(精確 GUID+規則 / 幾何 fallback 標 `guid_churn_suspected` 強制人審)、parent/child 收斂、resolved 延續、reopen-candidate、機器可讀差異報告。 +- 人審 triage UI(console 疊加一頁):批量 accept/reject/edit、單一寫入者序列化 + per-draft 版本號 409 + 欄位所有權 fail-loud、操作稽核 ledger。 +- BCF-API 3.0 topic/comment/viewpoint JSON 匯出端點 + 官方 schema 驗證;驗證層 pin IFC 4.3、幾何走 adapter。 +- 橫切治理 checklist G1–G6(量測/SLO/EULA/硬體/能買就不要造/零破壞)。 + +## Capabilities + +### New Capabilities + +- `session-lifecycle`:單 GPU session 生命週期管理器 SessionBroker(driver 可替換 + fake driver contract test,`create`/`join` 一級動詞)——admission、佇列、spectator 分流、TTL/idle 回收、健康探針、`-ResetUser` 復原、冷啟動 202、環境指紋啟動比對、WebRTC 不降級。 +- `gpu-session-baseline`:Phase 0 量測 harness——GPU/MIG 盤點、VRAM/TTFF/建立成功率基準報告(含環境指紋)、≥30 分鐘 soak 記憶體斜率、SLO 數值化並綁定指紋。 +- `ai-review-draft-pipeline`:IfcClash 規則式碰撞審查(Finding SPI)、finding 證據包強制欄位 fail-closed ingest、draft gate。 +- `issue-idempotency`:兩層 fingerprint、容差校準、parent/child 收斂、resolved 延續、reopen-candidate、機器可讀差異報告。 +- `human-triage-queue`:人審 triage UI、accept/reject/edit、單一寫入者 + 版本號 409 + 欄位所有權、稽核 ledger。 +- `bcf-contract-export`:BCF-API 3.0 JSON 匯出端點 + 官方 schema 驗證、IFC 4.3 pin + 幾何 adapter。 + +## Impact + +- **所屬目錄/服務**:`bim-review-coordinator` 擁有 SessionBroker 排程/admission/佇列/生命週期 state、AI draft store(單一寫入者序列化)、triage accept 寫入正式 issue、稽核 ledger、BCF 匯出端點與冷啟動 202 輪詢 API;`bim-streaming-server` 提供 Kit runtime 起流/回收/`-ResetUser`、`omni.services.livestream.session` REST、IfcClash 執行環境與 IFC→USD 座標變換鏈;`governance-service` 維持既有 issues/annotations/BCF 持久化與匯出契約,draft 為 additive `source_type`;`web-viewer-sample` 擁有 triage 頁與冷啟動進度 UI(dist-ui 體系,build:ui 交付)。 +- **保留的外部邊界**:外部客戶落地端 IFC Worker 仍是 IFC producer;外部公司雲端 `bim-control` 仍擁有 tenant/RBAC/enterprise workflow。本 change 不新增跨 repo 外部工作室派發、不引入 auth/RBAC 模型(維持 `:8004/ui` 現況)。 +- **API/資料/儲存影響**:新增 additive session 生命週期 REST(create/join/status/terminate)、Phase 0 基準報告 schema、AI draft 記錄(issues store 疊加 `source_type=ai_review` + draft 狀態 + fingerprint 鍵 + 版本號)、resolved-candidate 差異報告 JSON、BCF-API 3.0 匯出端點。既有 `/api/external/ifc-ready`、conversion callback、`:8004` proxy 與凍結三檔均不變。 +- **Session/runtime 影響**:SessionBroker 為 out-of-process 控制面,只做 admission/回收/佇列,不注入 per-session 渲染路徑;WebRTC 維持 RFC 8825/8826/8827 DTLS-SRTP 不降級;Kit GPU 渲染僅 host-native(repo 既知 Docker/WSL2 無繪圖驅動)。 +- **量測依賴**:所有 admission SLO 綁定 Phase 0 報告環境指紋;硬體/driver/Kit/fixture 任一變動即令 SLO 失效須重跑基準。 +- **非目標**:不上 K8s/MIG/多 GPU 水平擴充(僅記錄 SessionBroker driver 介面約束)、不做 Kit 串流內 fly-to 或 live 視角→BCF viewpoint 擷取、不建 LLM 分類層(僅凍結可停用降級接縫)、不建 IDS 完整規則引擎、不做 BCFzip serializer、不做 auth/RBAC、不做 AI 繪圖寫回、不做完整 OpenCDE server、不含建築工作室 agent 學習訓練(使用者明示不屬本 repo)。詳見 design.md 與各 spec 的 scope。 + +## Open Questions + +以下為研究與對抗詰問後仍**未在本規格草案內解決**的殘留問題;各對應 spec 的相關 Requirement 標註了關聯,實作前應由使用者裁決或以 Phase 0/校準報告收斂。前二項另含使用者產品決策,**待使用者確認**。 + +### 待使用者確認的產品決策 + +- **OQ-A(max-hold hard cap,關聯 R1.6)**:MVP 顯式決定 primary 佇列無 preemption、無會議最長持有硬上限,餓死風險以「可見等待資訊+人際協調」吸收。此為使用者產品決策,需向使用者揭示並確認是否接受「單一忘關分頁/長會議可無限期霸佔 GPU」的行為,或改設 Phase 0 後可設定的 max-hold knob。 +- **OQ-B(BCF 交付面,關聯 R6.1)**:MVP 只交付 BCF-API 3.0 JSON。桌面工具(BIMcollab/Solibri/Revit)直接開啟需 BCFzip serializer(第二個 serializer,非 server),列 fast-follow。若 J5 目標為交付外部工作室,須向使用者揭示 JSON 無法被桌面 BCF 工具直接開啟,是否納入本期交使用者裁決。 + +### 對抗詰問殘留(實作前須收斂) + +- **OQ-1(GUID churn 冪等塌陷,關聯 R4.1/R4.2/R4.3)**:IFC re-export 幾乎必然重配 GlobalId(GUID churn 是常態)。真實新版本回跑時第一層幾乎全 miss、第二層幾乎全命中,每筆舊 finding 都變成 `guid_churn_suspected` 待人審項。**未定義**:(a) suspected 命中在資料模型上是「新開一筆 draft」還是「掛在既有 draft 上的待確認 merge 項」——這直接決定 R4.3 resolved/reopen lineage 是否斷裂;(b)「疑似同源群一鍵確認」的顯式人審語意;(c) 驗收僅斷言同模型重跑(第一層全命中),缺真實 churn 版本對(第一層全 miss)的收斂斷言。建議:suspected 命中收斂成 parent 級「疑似同源群」一鍵確認,且 R4.2 分群鍵在 fallback 模式下能把同一 parent 的 children 一起帶過去。 +- **OQ-2(AI 審查裡沒有 AI,關聯 CAP-3 全體)**:使用者原始目標明寫「AI agent 自審、自派發」,但 MVP 砍掉 LLM 層,`ai-review-draft-pipeline` 實為零模型推論的確定性 IfcClash,`source_type=ai_review` 與 R5.2「AI 版本標記」在無模型下實際只是規則集版本號。**須向使用者白紙黑字揭示 MVP=規則引擎、LLM=future work**,並確認是否接受此 headline 落差,或至少保留一條最小 LLM 分流層以名副其實。 +- **OQ-3(忘關分頁永久餓死,關聯 R1.2/R1.6)**:R1.2 定義 idle=連續 T 秒無任一 readyState=4 peer 且「只要仍有任一健康連線永不 idle」;R1.6 又不設 max-hold。兩者交集:presenter 關機走人但分頁沒關 → readyState 仍=4 → 永不 idle → 無 max-hold → 佇列永久餓死,而「人際協調」對已離場持有者無法觸及。另外「無主會議」(primary 連線掉但 spectator 還在、spectator 依賴 primary stage)算活還是連帶 teardown 未定。**須裁決**:是否引入「無人互動 last-activity 軟門檻」作為第二回收路徑,或接受單一忘關分頁無限期霸佔 GPU。 +- **OQ-4(認領視窗與冷啟動矛盾,關聯 R1.4/R1.6)**:R1.4 說冷啟動起流 30–40 秒,R1.6 說「認領視窗 N 秒內未起流則讓位」。若「起流」=達到 readyState=4 且 N < 冷啟動上限,佇列變成每個被通知者都在冷啟動途中逾時的空轉死結。**須精確定義**「起流/認領」判準(建議:認領=視窗內成功發出建立請求並進入 202 輪詢即鎖住 GPU,起流耗時不計入認領視窗),且若認領=ready 則 N 必須硬性 ≥ Phase 0 TTFF p99 上限(綁 R2.3)。 +- **OQ-5(soak 切斷論證漏 spectator 與探針負載,關聯 R2.2)**:R2.2 記憶體斜率 soak 只量「一條 warm primary」單條,並以「SessionBroker 是 out-of-process 控制面」論證裸量可作門檻依據。但驗收壓的是 1 primary + 5 spectator ≥30 分鐘,且 R1.3 排程器對每個 session 持續打健康探針(DataChannel 往返負載)。這兩項都不在裸 primary soak 裡。**須裁決**:soak 是否收緊為「在 k=5 spectator 且健康探針同時運行下量測記憶體斜率與 VRAM 水位」,否則 admission 上限與洩漏門檻是用比實際輕的負載算出、對 6 人會議不具保證力。 +- **OQ-6(ledger 持久化跨 docker 重建,關聯 R5.2/R5.3)**:R5.3 稱 draft store 靠 atomic swap 持久化、版本號存檔內作 409 樂觀鎖;R5.2 稽核 ledger 是 draft-gate 對抗 EU AI Act/簽證風險的唯一人審軌跡。但 repo 現況:coordinator store in-memory 重啟即清、`rebuild-test-deploy` 會 `git clean -fdx` 洗掉部署區 runtime 狀態。**須精確定義**:(a) ledger 與 draft store 的持久化落點是否在會被 rebuild/clean 洗掉的路徑之外(掛載卷或 MinIO);(b) coordinator 重啟後版本號計數器與序列化狀態如何從磁碟重載、重載後誰是權威;(c) 若稽核 ledger 隨一次 docker 重建蒸發,draft-gate 的法遵防禦是否等於歸零。 diff --git a/openspec/changes/add-single-gpu-session-ai-review-mvp/specs/ai-review-draft-pipeline/spec.md b/openspec/changes/add-single-gpu-session-ai-review-mvp/specs/ai-review-draft-pipeline/spec.md new file mode 100644 index 000000000..9e56a49a2 --- /dev/null +++ b/openspec/changes/add-single-gpu-session-ai-review-mvp/specs/ai-review-draft-pipeline/spec.md @@ -0,0 +1,49 @@ +## ADDED Requirements + +### Requirement: 規則式碰撞審查 SHALL 以 IfcClash 產 findings 且缺依賴 fail-loud + +審查引擎 SHALL 採 Finding SPI(checker 介面)。deterministic checker(IfcClash CLI/Python lib)SHALL 承擔主審查量;LLM 分流/優化建議層 SHALL 設計成故障或超預算時可整層停用而不中斷核心審查閉環(MVP SHALL NOT 建 LLM 層,僅凍結此降級接縫)。以 IfcClash 對已轉檔模型跑 clash set SHALL 產 findings。ifcopenshell 缺 OpenCASCADE(`has_occ=False`)時跑 clash SHALL 明確報錯(fail-loud)而非靜默回 0。超大模型進審查 SHALL 有 size guard 攔截避免逾時。 + +> Open Question OQ-2:MVP 的「AI 審查」實為零模型推論的確定性 IfcClash,`source_type=ai_review` 實為規則集版本號;須向使用者揭示 headline 落差,見 proposal.md。 + +#### Scenario: 正常碰撞審查 + +- **WHEN** 對已轉檔模型以 IfcClash 跑 clash set +- **THEN** SHALL 產出 clash findings + +#### Scenario: 缺 OpenCASCADE + +- **WHEN** ifcopenshell `has_occ=False` 時跑 clash +- **THEN** SHALL 明確報錯(fail-loud) +- **AND** SHALL NOT 靜默回 0 findings + +#### Scenario: 超大模型 + +- **WHEN** 超大模型進審查 +- **THEN** size guard SHALL 攔截避免逾時 + +### Requirement: finding ingest SHALL fail-closed 強制證據包欄位並以 IFC 世界座標表述 viewpoint + +一筆 finding 進入 triage 佇列前的 ingest 驗證 SHALL 強制附:元件 GUID + 規則/條文引用 + BCF viewpoint(MVP 由幾何 bounding box 離線計算相機參數,非 live Kit 擷取)+ 信心值 + abstain 標記;缺任一欄位 SHALL 拒收(fail-closed)並進 abstain 桶不進佇列。viewpoint 相機參數 SHALL 以 IFC 模型世界座標系表述;轉檔管線 IFC→USD 的座標變換(含 georeference offset)SHALL 記入 ConversionLedger,保留可稽核變換鏈供 viewpoint 反算與幾何驗證使用。 + +#### Scenario: 完整證據包 finding + +- **WHEN** 一筆 clash finding 含元件 GUID、clash set 規則名、以 IFC 座標表述的自動相機 viewpoint、信心值 +- **THEN** SHALL 通過 ingest 驗證並出現在佇列 +- **AND** SHALL 可視覺定位 + +#### Scenario: ingest 缺欄位 + +- **WHEN** finding 缺 GUID/規則引用/viewpoint/信心值任一欄位 +- **THEN** SHALL 拒收(fail-closed) +- **AND** SHALL 進 abstain 桶不進佇列 + +### Requirement: AI 審查產出 SHALL 一律為 draft 狀態,SHALL NOT 直接建立正式 issue + +AI 審查產出落庫 SHALL 一律為 draft 狀態(issues store 疊加 `source_type=ai_review` 與 draft 狀態,不改既有狀態機語意);draft SHALL NOT 出現在正式 issue 清單、SHALL NOT 觸發任何派發。 + +#### Scenario: 審查跑完落庫 + +- **WHEN** AI 審查跑完 120 筆 finding 落庫 +- **THEN** 正式 issues 淨增 SHALL 為 0 +- **AND** triage 佇列 SHALL 增加對應 draft(經去重後為分組視圖) diff --git a/openspec/changes/add-single-gpu-session-ai-review-mvp/specs/bcf-contract-export/spec.md b/openspec/changes/add-single-gpu-session-ai-review-mvp/specs/bcf-contract-export/spec.md new file mode 100644 index 000000000..94cf61aab --- /dev/null +++ b/openspec/changes/add-single-gpu-session-ai-review-mvp/specs/bcf-contract-export/spec.md @@ -0,0 +1,23 @@ +## ADDED Requirements + +### Requirement: 匯出 SHALL 產 BCF-API 3.0 JSON 並以官方 schema 驗證通過,禁自造私有格式 + +accepted issue 匯出時 SHALL 產 BCF-API 3.0 topic/comment/viewpoint JSON(含 guid、viewpoint 相機、GUID 綁定),SHALL NOT 自造私有格式;匯出物 SHALL 以官方 JSON schema 驗證通過為準。MVP SHALL 只做匯出端點(疊加於既有 BCF-IDS 匯出旁),SHALL NOT 實作完整 OpenCDE Foundation API server。 + +> Open Question OQ-B:BCF-API 3.0 JSON 與 BCFzip 是兩種互通面共享同一邏輯模型;MVP 交付 JSON(API 面),桌面工具(BIMcollab/Solibri/Revit)互通需 BCFzip serializer(第二個 serializer,非 server),列 fast-follow,是否納入本期待使用者確認,見 proposal.md。 + +#### Scenario: 匯出 accepted issues + +- **WHEN** 匯出 3 筆 accepted issues +- **THEN** SHALL 產 BCF-API 3.0 topic/comment/viewpoint JSON +- **AND** JSON SHALL 對 BCF-API 3.0 官方 schema 驗證 0 error + +### Requirement: 驗證層 SHALL pin IFC 4.3 且幾何走 adapter 保留遷移路徑 + +規則/驗證引用 SHALL pin IFC 4.3(ISO 16739-1:2024);幾何存取 SHALL 走 adapter,SHALL NOT 硬編 IFC4.3 entity,以保留 IFC5/IFCX 遷移路徑。 + +#### Scenario: 規則驗證引用版本 + +- **WHEN** 規則/驗證引用 IFC schema +- **THEN** SHALL pin IFC 4.3(ISO 16739-1:2024) +- **AND** 幾何存取 SHALL 走 adapter 不硬編 IFC4.3 entity diff --git a/openspec/changes/add-single-gpu-session-ai-review-mvp/specs/gpu-session-baseline/spec.md b/openspec/changes/add-single-gpu-session-ai-review-mvp/specs/gpu-session-baseline/spec.md new file mode 100644 index 000000000..21b76ddb4 --- /dev/null +++ b/openspec/changes/add-single-gpu-session-ai-review-mvp/specs/gpu-session-baseline/spec.md @@ -0,0 +1,55 @@ +## ADDED Requirements + +### Requirement: 基準量測腳本 SHALL 產出含環境指紋的結構化基準報告 + +執行 `measure-session-baseline.ps1` 時 SHALL 產出結構化基準報告,內容 SHALL 至少含:GPU 型號盤點(判定是否消費級 RTX、確認 MIG 不可用 → 鎖定軟體佇列路線)、1 primary + k spectator 下 VRAM 水位、time-to-first-frame(TTFF)、建立成功率。取樣 SHALL 使用 nvidia-smi(VRAM/利用率)+ WebRTC health probe + TTFF。報告 schema SHALL 含「環境指紋」必填欄位:GPU 型號、driver 版本、Kit 版本、量測 fixture 的 hash + 大小。 + +#### Scenario: 執行基準量測 + +- **WHEN** 在目標部署環境執行 `measure-session-baseline.ps1` +- **THEN** SHALL 產出含 GPU 型號盤點、VRAM 水位、TTFF、建立成功率的結構化報告 +- **AND** 報告 SHALL 含 GPU 型號/driver 版本/Kit 版本/fixture hash+大小的環境指紋必填欄位 + +#### Scenario: 缺環境指紋欄位 + +- **WHEN** 產生的基準報告缺任一環境指紋必填欄位 +- **THEN** 報告 SHALL 判定為不完整 +- **AND** 後續 SLO(R2.3)與排程器啟動比對(session-lifecycle R1.7)SHALL NOT 引用該報告 + +### Requirement: 長連線 soak test SHALL 在隔離量測窗產出記憶體斜率報告且門檻只由本地實測決定 + +一條 warm primary 連線 SHALL 跑 ≥30 分鐘(目標 2 小時)soak 並產出記憶體斜率報告。洩漏 watchdog 門檻 SHALL 完全依本次本地實測結果訂定,SHALL NOT 引用簡報未涵蓋、未經驗證的任何外部系統數字(含任何 GB/日 之類外部洩漏率)。soak SHALL 於隔離 stack(獨立埠、獨立 governance)+獨佔量測窗執行,harness SHALL 內建最小 keepalive health probe 維持連線活性,量測期間 SHALL NOT 共用入口。soak 期間自然斷流 SHALL 記為 finding;連兩次同點斷流 SHALL 判定為環境污染需查因,單次 SHALL 視為量測窗雜訊不逕自作結。 + +#### Scenario: 隔離 soak 產記憶體斜率 + +- **WHEN** 一條 warm primary 連線於隔離 stack + 獨佔量測窗跑 ≥30 分鐘 soak,keepalive probe 維持活性 +- **THEN** SHALL 產出記憶體斜率報告 +- **AND** 洩漏 watchdog 門檻 SHALL 只由本次實測訂出,不引用外部數字 + +#### Scenario: soak 期間自然斷流 + +- **WHEN** soak 期間發生單次自然斷流 +- **THEN** SHALL 記為 finding 並視為量測窗雜訊 +- **AND** 僅在連兩次同點斷流時 SHALL 判定為環境污染需查因 + +### Requirement: SLO SHALL 以具體數值形式化寫入部署文件並綁定環境指紋 + +設定 `session-lifecycle` admission 參數時 SHALL 以 Phase 0 報告為前提,SHALL 以具體數值指標形式化並寫入可稽核部署文件:session 建立成功率下限、TTFF 上限、探針逾時定義、並發上限、idle-timeout、洩漏門檻。SHALL NOT 使用「合理」「足夠」等模糊詞;無基準報告則 admission 參數 SHALL NOT 上線(硬 gate)。所有 SLO 數值 SHALL 綁定 R2.1 報告的環境指紋;指紋任一變動即令既有 SLO 失效須重跑基準。 + +#### Scenario: 有基準報告設定 SLO + +- **WHEN** Phase 0 報告已存在,設定 admission 參數 +- **THEN** SLO SHALL 以具體數值寫入部署文件且無模糊詞 +- **AND** 每項 SLO SHALL 綁定報告的環境指紋 + +#### Scenario: 無基準報告 + +- **WHEN** 尚無 Phase 0 基準報告 +- **THEN** admission 參數 SHALL NOT 上線 +- **AND** 系統 SHALL 阻擋排程器以未經量測的門檻起排程 + +#### Scenario: 環境指紋變動 + +- **WHEN** GPU 型號/driver/Kit/fixture 任一變動 +- **THEN** 既有 SLO SHALL 失效 +- **AND** SHALL 要求重跑基準取得新指紋 diff --git a/openspec/changes/add-single-gpu-session-ai-review-mvp/specs/human-triage-queue/spec.md b/openspec/changes/add-single-gpu-session-ai-review-mvp/specs/human-triage-queue/spec.md new file mode 100644 index 000000000..92af96c83 --- /dev/null +++ b/openspec/changes/add-single-gpu-session-ai-review-mvp/specs/human-triage-queue/spec.md @@ -0,0 +1,44 @@ +## ADDED Requirements + +### Requirement: triage UI SHALL 為 accept/reject/edit 唯一轉正入口且 AI 無直建路徑 + +reviewer 開 console 疊加的 triage 頁(既有 dist-ui 體系,build:ui 交付)執行單筆/批量 accept・reject・edit 時,accept SHALL 經既有 issues store 寫入正式 issue;正式 issue 的建立/關閉/指派 SHALL 只能由人在此觸發,AI SHALL 無任何直建路徑。 + +#### Scenario: 批量 triage + +- **WHEN** reviewer 批量 reject 低信心 30 筆、accept 3 個 parent +- **THEN** 正式 issues SHALL 淨增 3 +- **AND** 操作者與時間戳 SHALL 入 ledger + +### Requirement: 每筆 triage 操作 SHALL 記錄稽核 ledger 含 AI 版本標記與原始證據包 + +每筆 accept/reject/edit SHALL 記錄操作者、時間、AI 版本標記與原始證據包,以為未來 golden 基準集累積標註資料(MVP SHALL 只收集,不建門檻自動化)。 + +> Open Question OQ-6:ledger 與 draft store 的持久化落點須在 rebuild/`git clean -fdx` 洗除範圍外(掛載卷或 MinIO),否則一次 docker 重建即摧毀人審軌跡使法遵防禦歸零;重載後權威(檔案 vs 記憶體)須明確,見 proposal.md。 + +#### Scenario: 記錄一筆 accept + +- **WHEN** reviewer accept 一筆 draft +- **THEN** ledger SHALL 記錄操作者、時間、AI 版本標記與原始證據包 + +### Requirement: draft store 併發一致性 SHALL 以單一寫入者加版本號 409 加欄位所有權保證零遺失 + +(a) 單一寫入者:draft store 所有變更(AI 重跑 ingest 與人審 accept/reject/edit)SHALL 一律經 coordinator store service in-process 序列化;atomic swap SHALL 僅為持久化機制,非併發控制。(b) per-draft 版本號:版本號 SHALL 存於檔內,提交比對不符 SHALL 回 409 要求重讀(optimistic lock,MVP 不做自動合併策略)。(c) 欄位所有權:AI SHALL 僅可寫 `evidence`/`last_seen`/`occurrence` 類欄位,SHALL NOT 觸碰已有 triage 狀態的人審欄位,違反即 fail-loud。AI 重跑與人審並行操作同一筆 draft 後,人審標註 SHALL 零遺失。 + +#### Scenario: 版本號衝突 + +- **WHEN** 提交時 per-draft 版本號比對不符 +- **THEN** SHALL 回 409 要求重讀 +- **AND** SHALL NOT 自動合併 + +#### Scenario: AI 觸碰人審欄位 + +- **WHEN** AI 重跑試圖寫入已有 triage 狀態的人審欄位 +- **THEN** SHALL fail-loud 拒絕 +- **AND** AI SHALL 僅能寫 `evidence`/`last_seen`/`occurrence` 類欄位 + +#### Scenario: 並行操作零遺失 + +- **WHEN** AI 重跑 ingest 與人審 accept/reject/edit 並行操作同一筆 draft +- **THEN** 所有變更 SHALL 經 in-process 序列化 +- **AND** 人審標註 SHALL 零遺失 diff --git a/openspec/changes/add-single-gpu-session-ai-review-mvp/specs/issue-idempotency/spec.md b/openspec/changes/add-single-gpu-session-ai-review-mvp/specs/issue-idempotency/spec.md new file mode 100644 index 000000000..e13abd775 --- /dev/null +++ b/openspec/changes/add-single-gpu-session-ai-review-mvp/specs/issue-idempotency/spec.md @@ -0,0 +1,65 @@ +## ADDED Requirements + +### Requirement: 兩層 fingerprint SHALL 兜底 GUID churn 且幾何 fallback 命中禁靜默 dedup + +同一 IFC 模型連跑兩次審查時第二次 SHALL 產 0 筆新 draft,既有 draft SHALL 更新 `last_seen`;SHALL 沿用 repo `mw_` 前綴 hash 鍵 + ConversionLedger atomic swap 寫入。第一層(精確)SHALL 以 GUID 組合 + 規則 id 精確匹配。第一層 miss 時第二層(幾何 fallback)SHALL 以規則 id + 幾何量化位置 bucket 匹配,命中 SHALL 標記 `guid_churn_suspected`。標記 `guid_churn_suspected` 的第二層命中 SHALL 必路由人審確認,SHALL NOT 自動 suppress/dedup 為同一 finding(防兩個實為不同的 finding 因幾何量化落同 bucket 被誤併而隱藏真實新 finding)。 + +> Open Question OQ-1:真實 GUID churn 版本回跑(第一層全 miss、第二層全命中)下,suspected 命中是「新 draft」還是「掛既有 draft 的 merge-candidate」、以及 parent 級一鍵確認語意尚未定義,見 proposal.md。 + +#### Scenario: 同模型重跑 + +- **WHEN** 同一 IFC 模型連跑兩次審查 +- **THEN** 第二次 SHALL 產 0 筆新 draft +- **AND** 既有 draft SHALL 更新 `last_seen` + +#### Scenario: 幾何 fallback 命中 + +- **WHEN** 第一層精確匹配 miss、第二層幾何 bucket 命中 +- **THEN** SHALL 標記 `guid_churn_suspected` 並路由人審確認 +- **AND** SHALL NOT 自動 suppress/dedup + +### Requirement: parent/child 收斂 SHALL 以寫死分群鍵把同鍵命中收斂為一 parent + +分群鍵 SHALL 為 `(rule_id, sorted 涉事元素 GUID 集合)`;SHALL NOT 採距離/樓層/系統等啟發式分群。同鍵多命中去重時 SHALL 收斂為 1 parent + children,人審 SHALL 可對 parent 一鍵處置。若 R4.1 第二層幾何 fallback 啟用,分群鍵 SHALL 同步採 fallback 形式(`rule_id` + 幾何量化 bucket 集合)並沿用 `guid_churn_suspected` 人審路由,SHALL NOT 因分群而繞過人審確認。 + +#### Scenario: 同鍵多 clash 點收斂 + +- **WHEN** 同一對牆/管線之間 120 個 clash 點同鍵 +- **THEN** 佇列 SHALL 顯示 1 parent(child 計數 120) +- **AND** 人審 SHALL 可對 parent 一鍵處置 + +### Requirement: resolved 延續與 reopen-candidate SHALL 只由人審 accept 觸發狀態轉換並產機器可讀差異報告 + +新模型版本重跑時 fingerprint 命中已 resolved 的 finding 且幾何已消解 SHALL NOT 重生 draft。fingerprint 命中已 resolved 的 finding 但幾何仍衝突時 SHALL 產 reopen-candidate draft(鏈結原正式 issue + 完整歷史鏈)進 triage 佇列。正式 issue 的 resolved→reopened 狀態轉換 SHALL 僅由人審 accept 該 candidate 觸發;人審 reject 則 SHALL 維持 resolved 並將決策入 ledger。AI SHALL 無任何直接把正式 issue 由 resolved 轉 reopened 的路徑。版本回跑收斂結束 SHALL 產出機器可讀的 resolved-candidate 差異報告(新增/持續/已消解/reopen 四類,JSON 結構化)。 + +#### Scenario: resolved 且幾何已消解 + +- **WHEN** 新版本重跑,fingerprint 命中已 resolved 的 finding 且幾何已消解 +- **THEN** SHALL NOT 重生 draft + +#### Scenario: resolved 但幾何仍衝突 + +- **WHEN** fingerprint 命中已 resolved 的 finding 但幾何仍衝突 +- **THEN** SHALL 產 reopen-candidate draft(鏈結原正式 issue + 歷史鏈)進佇列 +- **AND** 正式 issue resolved→reopened SHALL 僅由人審 accept 觸發,reject 維持 resolved 並入 ledger + +#### Scenario: 版本回跑差異報告 + +- **WHEN** 版本回跑收斂結束 +- **THEN** SHALL 產出機器可讀 resolved-candidate 差異報告 +- **AND** 報告 SHALL 含新增/持續/已消解/reopen 四類 JSON 結構 + +### Requirement: fingerprint 容差 SHALL 由校準報告訂定且無校準報告不得上線 + +執行同模型 re-export 的版本對校準時 SHALL 產出 GUID 存活率與幾何容差曲線報告;R4.1 第二層幾何 bucket 大小 SHALL 引用該報告數值訂定,SHALL NOT 使用模糊詞。無校準報告則第二層 bucket 參數 SHALL NOT 上線(比照 Phase 0 硬 gate)。 + +#### Scenario: 有校準報告訂 bucket + +- **WHEN** 對同模型 re-export 版本對執行校準 +- **THEN** SHALL 產出 GUID 存活率與幾何容差曲線報告 +- **AND** 第二層幾何 bucket 大小 SHALL 引用該報告數值且無模糊詞 + +#### Scenario: 無校準報告 + +- **WHEN** 尚無容差校準報告 +- **THEN** 第二層幾何 bucket 參數 SHALL NOT 上線 diff --git a/openspec/changes/add-single-gpu-session-ai-review-mvp/specs/session-lifecycle/spec.md b/openspec/changes/add-single-gpu-session-ai-review-mvp/specs/session-lifecycle/spec.md new file mode 100644 index 000000000..85df762d0 --- /dev/null +++ b/openspec/changes/add-single-gpu-session-ai-review-mvp/specs/session-lifecycle/spec.md @@ -0,0 +1,102 @@ +## ADDED Requirements + +### Requirement: SessionBroker SHALL 以會議 session 為競爭單位執行 fail-closed admission control + +排程器核心以 SessionBroker 呈現,對外 API SHALL 不含單 GPU 假設。Admission 競爭單位 SHALL 是會議 session(1 primary + N spectator)而非個別使用者:後加入既有會議者 SHALL 一律走 spectator 分流,不佔新 GPU、不進 primary 佇列;只有請求「新 primary(=新會議,需獨佔 GPU)」SHALL 進入佇列競爭。SessionBroker SHALL 封裝 `omni.services.livestream.session` REST(GET `/v1/streaming/ready`、POST `/endsession`、POST `/creds`)為 coordinator 疊加路由,且 SHALL NOT 修改凍結三檔。 + +#### Scenario: 既有 primary 佔用下請求新 primary + +- **WHEN** 已有一個 active primary session 佔用 GPU,第二位使用者請求新 primary +- **THEN** SessionBroker SHALL 回 202 + 明確佇列位置/預估等待 +- **AND** SHALL NOT 直接再起第二個 Kit primary(fail-closed:資源忙碌預設拒絕/佇列而非嘗試多開) + +#### Scenario: spectator 席位未滿 + +- **WHEN** GPU 已被 primary 佔用且 spectator 席位未滿,收到 spectator 請求 +- **THEN** SessionBroker SHALL 分配 49110~49150 空埠並回可用 endpoint + +#### Scenario: spectator 席位已滿 + +- **WHEN** 在席 spectator ≥ `KIT_SPECTATOR_COUNT` 上限,再收到 spectator 請求 +- **THEN** SessionBroker SHALL fail-closed 回明確拒絕 + 滿員原因(例:「席位 5/5 已滿」) +- **AND** SHALL NOT 把該 spectator 請求轉入 primary 佇列 + +### Requirement: SessionBroker SHALL 以 readyState=4 peer 存在性判定 idle 並支援顯式 terminate 回收 + +idle SHALL 定義為該 session 連續 T 秒無任一 readyState=4 的已連線 viewer peer(primary 與 spectator 連線皆計入);只要仍有任一健康連線,該 session SHALL NOT 被判 idle。SessionBroker SHALL NOT 以輸入/滑鼠/鍵盤活動作為 idle 判準。idle-timeout SHALL 可設定,其預設值 SHALL 由 Phase 0 基準決定。 + +#### Scenario: session idle 逾時自動回收 + +- **WHEN** session idle 逾 idle-timeout(連續 T 秒無任一 readyState=4 連線),watchdog 判定 idle +- **THEN** SessionBroker SHALL 走 `/endsession` + teardown +- **AND** 佇列中下一位 SHALL 獲派並收到「輪到你」 + +#### Scenario: 顯式 terminate + +- **WHEN** 收到顯式 terminate 請求 +- **THEN** SessionBroker SHALL 走 `/endsession` + teardown 回收 +- **AND** 佇列中下一位 SHALL 獲派 + +### Requirement: SessionBroker SHALL 以 readyState=4 加影像尺寸加 DataChannel 回應定義健康並自動復原 + +health SHALL 定義為 readyState=4 + 影像尺寸 + DataChannel 回應,SHALL NOT 以 port-open 判定存活。連續 N 次健康探針失敗時 watchdog SHALL 觸發自動 `-ResetUser` 復原;復原失敗 SHALL teardown 回收並將事件寫入 session ledger 供排程器決策。 + +#### Scenario: viewer 卡死自動復原 + +- **WHEN** 連續 N 次健康探針失敗(viewer readyState=0 卡死) +- **THEN** watchdog SHALL 自動執行 `-ResetUser` +- **AND** 恢復後事件 SHALL 寫入 session ledger + +#### Scenario: 復原失敗 + +- **WHEN** `-ResetUser` 復原失敗 +- **THEN** SessionBroker SHALL teardown 回收 +- **AND** SHALL 將失敗事件寫入 session ledger 供排程器決策 + +### Requirement: SessionBroker SHALL 對冷啟動立即回 202 加可輪詢 statusUrl 而非假同步 + +冷啟動(起流 30–40 秒、shader cache 未預熱更久)時 SessionBroker SHALL 立即回 202 + 可輪詢 statusUrl,SHALL NOT 假裝同步成功。前端 SHALL 輪詢至 ready 才進 viewer,UI SHALL 顯示啟動進度。 + +#### Scenario: 冷啟動建立請求 + +- **WHEN** 收到冷啟動建立請求 +- **THEN** SessionBroker SHALL 立即回 202 + statusUrl +- **AND** UI SHALL 顯示啟動進度,前端 SHALL 輪詢至 ready 才進 viewer + +### Requirement: SessionBroker SHALL 維持 WebRTC 強制 DTLS-SRTP 不降級 + +任何連線建立路徑分配 endpoint 時 SessionBroker SHALL 維持 RFC 8825/8826/8827 強制 DTLS-SRTP,SHALL NOT 為相容引入未加密路徑。 + +#### Scenario: 分配連線 endpoint + +- **WHEN** SessionBroker 為任一連線建立路徑分配 endpoint +- **THEN** 連線 SHALL 強制 DTLS-SRTP +- **AND** SHALL NOT 引入任何未加密路徑 + +### Requirement: primary 佇列 SHALL 具 requester-TTL 與認領視窗語意且無 preemption + +佇列項的 requester-TTL 逾時(請求者離開/放棄)時 SHALL 自動出列、釋放佔位、後方遞補。輪到某請求者且前一 primary 回收釋放 GPU 時 SHALL 發「輪到你」通知並開啟認領視窗;認領視窗 N 秒內未起流 SHALL 讓位給下一位(本人可重新排隊)。MVP SHALL NOT 做搶佔(preemption),SHALL NOT 設會議最長持有硬上限(max-hold hard cap);餓死風險以可見等待資訊(佇列位置/預估)+人際協調吸收。 + +> Open Question OQ-4:認領視窗的「起流」判準(發出建立請求 vs 達到 readyState=4)與 N 的下限(是否須 ≥ Phase 0 TTFF p99)尚未定義,見 proposal.md。Open Question OQ-A:max-hold 是否改設 Phase 0 後可設定 knob,待使用者確認。 + +#### Scenario: 佇列請求者中途離開 + +- **WHEN** 佇列中的請求項 requester-TTL 逾時 +- **THEN** 該請求 SHALL 自動出列並釋放佔位 +- **AND** 後方請求者 SHALL 遞補 + +#### Scenario: 認領視窗逾時讓位 + +- **WHEN** 已通知某請求者「輪到你」,但認領視窗 N 秒內未起流 +- **THEN** SessionBroker SHALL 讓位給下一位 +- **AND** 原請求者 MAY 重新排隊 + +### Requirement: SessionBroker 啟動 SHALL 比對環境指紋,不符即 fail-loud + +SessionBroker 啟動並載入 `gpu-session-baseline` 基準 SLO 門檻時 SHALL 讀取當前環境指紋(GPU 型號/driver 版/Kit 版/量測 fixture hash)並與基準報告指紋比對;不符時 SHALL fail-loud(拒絕起排程或顯著告警),SHALL NOT 靜默沿用舊門檻。 + +#### Scenario: 環境指紋不符 + +- **WHEN** SessionBroker 啟動讀取的環境指紋與基準報告指紋不符 +- **THEN** SessionBroker SHALL fail-loud(拒絕起排程或顯著告警) +- **AND** SHALL NOT 靜默沿用舊 SLO 門檻 diff --git a/openspec/changes/add-single-gpu-session-ai-review-mvp/tasks.md b/openspec/changes/add-single-gpu-session-ai-review-mvp/tasks.md new file mode 100644 index 000000000..1389287ca --- /dev/null +++ b/openspec/changes/add-single-gpu-session-ai-review-mvp/tasks.md @@ -0,0 +1,72 @@ +## 0. 前置閘門與 Open Questions 收斂 + +- [ ] 0.1 向使用者揭示並取得裁決:OQ-A(max-hold 無硬上限是否可接受)、OQ-B(是否加 BCFzip fast-follow)、OQ-2(MVP AI 審查=零模型確定性 IfcClash 的 headline 落差);裁決與依據寫入可稽核文件,未決前對應 spec Requirement 標為待確認。 +- [ ] 0.2 收斂 OQ-1/OQ-3/OQ-4/OQ-5/OQ-6 為實作前設計決策:GUID churn 資料模型形狀(新 draft vs merge-candidate)、忘關分頁第二回收路徑、認領視窗「起流」判準與 N 下限、soak 是否納 spectator+探針負載、ledger 持久化落點;各寫入 design.md 補充或獨立 ADR。 +- [ ] 0.3 確認無 successor 衝突:`npx openspec list` 檢查無平行改同一 capability 的 active change;本 change 六個 capability 皆為 New,確認 `openspec/specs/` 無同名。 + +## 1. Phase 0 — gpu-session-baseline(量測 harness,`scripts/` + 部署文件) + +- [ ] 1.1 在 `scripts/` 建立 `measure-session-baseline.ps1`:nvidia-smi 取 VRAM/利用率 + WebRTC health probe + TTFF;輸出結構化 JSON 報告含 GPU 型號盤點(判定消費級 RTX、MIG 不可用)、1 primary + k spectator VRAM 水位、TTFF、建立成功率。 +- [ ] 1.2 在報告 schema 加「環境指紋」必填欄位(GPU 型號/driver 版/Kit 版/fixture hash+大小),缺欄位判不完整並拒絕被下游引用;於 `scripts/` 加最小驗證測試。 +- [ ] 1.3 實作 ≥30 分鐘(目標 2 小時)soak:隔離 stack(獨立埠 + 獨立 governance,沿用 branch E2E 隔離模式)+ 獨佔量測窗 + 內建 keepalive health probe;輸出記憶體斜率報告;自然斷流記 finding、連兩次同點才判污染。 +- [ ] 1.4 由 soak 報告訂洩漏 watchdog 門檻(只用本地實測、禁引用外部 GB/日 數字);將 session 建立成功率下限/TTFF 上限/探針逾時/並發上限/idle-timeout/洩漏門檻以具體數值寫入可稽核部署文件並綁定環境指紋(禁模糊詞)。 +- [ ] 1.5 撰寫「環境指紋變動即 SLO 失效須重跑基準」的部署文件段落與 G4 checklist 勾稽;驗證:無基準報告時 admission 參數 loader 拒絕上線(硬 gate 測試)。 + +## 2. Phase 1 — session-lifecycle(SessionBroker,`bim-review-coordinator` + `bim-streaming-server`) + +- [ ] 2.1(coordinator)建立 SessionBroker driver 介面(SEAM-1):一級 API `create(model_ref)`/`join(session_id, role)`;提供 `SingleGpuDriver` + `InMemoryFakeDriver`;driver 參數(並發上限/timeout)引用 Phase 0 報告,介面內不塞拍腦袋數字。 +- [ ] 2.2(coordinator)實作 admission control(fail-closed,競爭單位=會議 session):既有 primary 佔用下新 primary 請求回 202 + 佇列位置,絕不多開第二 Kit primary;spectator 未滿分配 49110~49150、滿員 fail-closed 明確拒絕不轉佇列。封裝 `omni.services.livestream.session` REST 疊加路由,不動凍結三檔。 +- [ ] 2.3(coordinator)實作 primary 佇列語意:requester-TTL 逾時自動出列遞補、輪到發「輪到你」+ 認領視窗、視窗內未起流讓位;MVP 無 preemption、無 max-hold(依 0.1 裁決)。 +- [ ] 2.4(coordinator)實作 TTL/idle 回收:idle=連續 T 秒無任一 readyState=4 peer(primary+spectator 皆計入),禁用輸入/滑鼠活動判 idle;顯式 terminate;idle-timeout 預設引用 Phase 0。 +- [ ] 2.5(streaming/Kit)實作健康探針(readyState=4 + 影像尺寸 + DataChannel 回應,非 port-open)與 `-ResetUser` 自動復原:連續 N 次探針失敗觸發復原,失敗則 teardown 並寫 session ledger。 +- [ ] 2.6(coordinator)實作冷啟動 202 + statusUrl 輪詢端點;`web-viewer-sample` UI 顯示啟動進度、輪詢至 ready 才進 viewer。 +- [ ] 2.7(coordinator)實作環境指紋啟動比對:載入基準 SLO 時讀當前指紋比對,不符 fail-loud(拒起排程或顯著告警),不靜默沿用舊門檻。 +- [ ] 2.8(coordinator)確認 WebRTC 分配 endpoint 維持 RFC 8825/8826/8827 DTLS-SRTP,無未加密路徑。 +- [ ] 2.9 在 `tests/contracts/` 寫 SessionBroker `InMemoryFakeDriver` contract test,證明換 driver 不改 caller、`create`/`join` 兩動詞語意覆蓋;YAGNI gate:若無法證明則移除抽象退回直呼並記錄。 +- [ ] 2.10 跑 coordinator/streaming affected 測試(各留服務目錄):admission fail-closed、佇列 requester-TTL/認領視窗、idle 綁 peer 存在性、`-ResetUser` 復原、冷啟動 202、指紋 fail-loud 各有自動化測試。 + +## 3. Phase 2a — ai-review-draft-pipeline(`bim-streaming-server` 審查執行 + `governance-service` draft store) + +- [ ] 3.1(streaming)建立 Finding SPI(SEAM-2)並落地唯一 IfcClash checker;凍結「LLM 層可整層關閉不中斷審查」降級接縫但不實作 LLM 層。 +- [ ] 3.2(streaming)IfcClash clash set 執行:`has_occ=False` 缺 OpenCASCADE hard guard fail-loud(非靜默回 0)、大模型 size guard 攔截逾時。 +- [ ] 3.3(streaming)離線 bounding box viewpoint 計算:以 IFC 世界座標表述相機參數;IFC→USD 座標變換(含 georeference offset)記入 ConversionLedger 保留變換鏈。 +- [ ] 3.4(governance-service)finding ingest fail-closed 驗證:強制 GUID + 規則引用 + viewpoint + 信心值 + abstain 標記,缺任一進 abstain 桶不進佇列。 +- [ ] 3.5(governance-service)issues store 疊加 `source_type=ai_review` 與 draft 狀態(不改既有狀態機語意);draft 不入正式 issue 清單、不觸發派發。 +- [ ] 3.6 跑 streaming pytest(服務目錄)+ governance 測試:缺 OCC fail-loud、size guard、ingest 缺欄位拒收、120 筆審查後正式 issues 淨增 0;語意測試:抽樣 viewpoint 視錐在 IFC 座標系包含目標 GUID bbox。 + +## 4. Phase 2b — issue-idempotency(`governance-service` + 校準腳本) + +- [ ] 4.1(校準腳本)對同模型 re-export 版本對執行容差校準,產 GUID 存活率與幾何容差曲線報告;無報告則第二層 bucket 不上線(硬 gate)。 +- [ ] 4.2(governance-service)兩層 fingerprint:第一層 GUID+規則精確匹配(沿用 `mw_` hash 鍵 + atomic swap);第一層 miss → 第二層規則 id + 幾何 bucket(大小引用 4.1 報告),命中標 `guid_churn_suspected` 強制人審、禁自動 dedup/suppress。 +- [ ] 4.3(governance-service)parent/child 收斂:分群鍵寫死 `(rule_id, sorted GUID 集合)`(fallback 模式改幾何 bucket 集合),同鍵收斂 1 parent + children,人審對 parent 一鍵處置。 +- [ ] 4.4(governance-service)resolved 延續/reopen-candidate:幾何已消解不重生 draft;幾何仍衝突產 reopen-candidate(鏈結原 issue + 歷史鏈);resolved→reopened 僅人審 accept 觸發,reject 維持 resolved 入 ledger,AI 無直接 reopen 路徑。 +- [ ] 4.5(governance-service)版本回跑產機器可讀 resolved-candidate 差異報告(新增/持續/已消解/reopen 四類 JSON)。 +- [ ] 4.6 跑 governance 測試:同模型重跑第二次 0 新 draft、群組收斂筆數 << 原始命中、GUID churn 版本對第二層命中標記+路由人審不靜默 dedup、reopen 流三態(產 candidate/僅 accept 轉 reopened/reject 維持 resolved)、差異報告四類斷言。 + +## 5. Phase 3a — human-triage-queue(`bim-review-coordinator` + `web-viewer-sample`) + +- [ ] 5.1(web-viewer-sample)console 疊加 triage 頁(dist-ui 體系,build:ui 交付):單筆/批量 accept・reject・edit;accept 經既有 issues store 寫入正式 issue;建立/關閉/指派只能人在此觸發。 +- [ ] 5.2(coordinator)併發一致性:draft store 所有變更經 coordinator store service in-process 序列化(atomic swap 僅持久化);per-draft 版本號存檔內、比對不符回 409 重讀;欄位所有權(AI 僅寫 evidence/last_seen/occurrence,觸碰人審欄位 fail-loud)。 +- [ ] 5.3(coordinator)稽核 ledger:每筆 accept/reject/edit 記操作者/時間/AI 版本標記/原始證據包;持久化落點依 0.2/OQ-6 裁決落在 rebuild/`git clean -fdx` 洗除範圍外(掛載卷或 MinIO),重啟後權威明確。 +- [ ] 5.4 跑 coordinator 測試(服務目錄)+ 前端 triage 頁測試:批量 accept 3 parent → 正式 issues +3、版本號 409、AI 觸碰人審欄位 fail-loud、AI 重跑與人審並行零遺失;前端有操作 route + 可見成功/失敗狀態 + E2E 截圖/trace。 + +## 6. Phase 3b — bcf-contract-export(`governance-service`) + +- [ ] 6.1(governance-service)BCF-API 3.0 topic/comment/viewpoint JSON 匯出端點(疊加於既有 BCF-IDS 匯出旁),含 guid/viewpoint 相機/GUID 綁定;不做完整 OpenCDE server。 +- [ ] 6.2(governance-service)匯出物以 BCF-API 3.0 官方 JSON schema 驗證;驗證層 pin IFC 4.3、幾何走 adapter 不硬編 entity。 +- [ ] 6.3 跑 governance 測試:匯出 3 筆 accepted issues → 官方 schema 驗證 0 error;IFC 4.3 pin 與 adapter 解耦有測試。 + +## 7. 橫切治理 gate G1–G6 與零破壞回歸 + +- [ ] 7.1 G1/G2:Phase 0 報告存在且含 GPU 盤點/VRAM/TTFF/成功率/soak 斜率/環境指紋;CAP-1 設定檔可追溯引用;admission SLO 數值化無模糊詞且綁定指紋——checklist 勾稽。 +- [ ] 7.2 G3:宣稱 production-ready 前 checklist 有「Omniverse EULA/pricing 逐字確認完成,附官方文件連結與日期」一項。 +- [ ] 7.3 G4:硬體/driver/Kit/fixture 任一變動之擴充或續用附重跑容量基準報告(含新指紋),否則不核定。 +- [ ] 7.4 G5:BCF/IDS/DCGM/IfcClash 標準件不於範圍外重造——review checklist 檢核。 +- [ ] 7.5 G6:`:8004` proxy byte-identical 回歸通過、凍結三檔(`app.py`/`conversion_authority.py`/`governanceProxy.ts`)git diff 為空、既有 A1–A4/轉檔閉環測試全綠。 + +## 8. Vertical slice E2E(真實 IFC fixture,host-native Kit + RTX) + +- [ ] 8.1 單一 vertical slice 一次跑通:轉檔 → AI 草稿(含 GUID/viewpoint/引用/信心值)→ 人審 accept 轉正式 issue → BCF 3.0 匯出 0 error → 6 人 WebRTC 會議看模型討論該 issue ≥30 分鐘不斷流 → 版本回跑產差異報告;收 E2E evidence(錄影/trace 落 `artifacts/e2e/`,PNG 需 `git add -f`)。 +- [ ] 8.2 不斷流驗證:primary 佔用下第二 primary 請求 100% 進佇列(0 次擊落)、spectator 滿員 fail-closed、6 人 ≥30 分鐘 readyState=4 trace。 +- [ ] 8.3 stop-and-ask:GIVEN Phase 0 實測 6 人 fixture 安全上限 < 6,呈使用者裁決(降人數/換 fixture/調品質/接受風險),裁決與依據入可稽核文件,禁靜默下修或硬湊 6 人。 +- [ ] 8.4 更新文件:session 生命週期 REST、Phase 0 報告 schema、draft/fingerprint 資料模型、BCF 匯出端點與 IFC→USD 變換鏈記錄,於相關 `docs/` 與服務 README 補充。