9609924818
不是用辨識碼找那張紙,是用那張紙免掉辨識碼(t154)。 紙搬到帳號上後,免碼判準自然變成去帳號看一眼,並讓 #120 情境變誠實: 帳號清空=本來就是新裝,請填辨識碼是對的答案,不是把人關在門外。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
713 lines
57 KiB
Markdown
713 lines
57 KiB
Markdown
# Pending Changes(規格變更緩衝區)
|
||
|
||
> 規則來源:`SDD-LIFECYCLE.md` 第 3、4 條。
|
||
> 規格層變更(核心設計/方向改變)**只有這一條路**:CC 把 change proposal 寫進「待裁決」——
|
||
> 變更摘要與觸發原因+影響分析(現行 SDD 哪些任務作廢/修改/不受影響/尚未完成)——然後**停止**,
|
||
> 等使用者明說「confirm」才依第 4 條開新 SDD;沒 confirm 就繼續依現行 SDD 工作。
|
||
> 多個 proposal 可並存,由人一次裁決。本檔不是 SDD,不掛 status。
|
||
|
||
## 待裁決
|
||
|
||
### P?|認證儲存要不要搬回 D1/KV——resource-reuse 上線後,D61 想解的病可能已被更通用的機制解掉(2026-08-14)
|
||
|
||
**觸發**:`arcrun-rag#99`/`inkstone/Arcrun#119`——全新安裝卡在「建立第一個帳號」,leo 本人與封測者
|
||
都實撞。根因:D61(2026-08-10,`c4cee35`)把認證資料搬到 CF Workers per-script Secrets,需要 cypher
|
||
自己持有 `env.CF_SECRETS_API_TOKEN`;這把 token 從安裝那天起就沒被種過(07-29 已知缺口),於是**每
|
||
一台全新實例**永遠 `writable:false`。
|
||
|
||
**已做的止血只解掉一格,其餘留債**:先做的修法(安裝精靈把裝機當下自己還有效的 OAuth token 隨
|
||
`/console/setup`/`/portal/admin/bootstrap` 請求傳給 cypher,用這一次、不落地)讓「建立第一個帳號」
|
||
能通,但 `console-auth.ts:216`(`/console/setup/reset`)、`/portal/admin/users`、`POST /credentials`
|
||
(存 API 金鑰)、`/portal/password/change`——**這些路徑安裝精靈都不在場,永遠拿不到那個表頭**,裝完
|
||
之後這台實例對這些操作永遠 `writable:false`。leo 的判準是「別人不再出這個問題」且「後面不能留
|
||
債」,這不合格。
|
||
|
||
**排除掉的兩個止血選項(留給下一個人看,避免重新踩一次)**:
|
||
- **A. 用 OAuth 權限「代鑄」一把不過期的 CF API Token**(呼叫 `POST /user/tokens`)——需要新的
|
||
OAuth scope(目前 `OAUTH_SCOPES` 六項裡沒有「管理 API Tokens」這項),會讓用戶授權畫面多一條
|
||
同意項,是產品面決定,未經查證 CF 的 OAuth scope 目錄裡那個 scope 確切叫什麼。
|
||
- **B. 讓用戶自己去 CF Dashboard 建一把 Token 貼進安裝精靈**——不需要新 scope,但打破「一鍵安裝」,
|
||
也是產品面決定。
|
||
- **(已明確排除)把 OAuth access_token 直接當長效 secret 存**——access_token 16 小時過期,
|
||
refresh_token 是 rotation 制、單次有效,cypher 沒有能力自己做 OAuth refresh(那等於要 cypher 自己
|
||
再長一份 OAuth client,且會跟安裝器搶同一條 refresh 鏈的競態)。存了會變成「`writable` 顯示
|
||
`true`,但 16 小時後開始靜默 401,且沒有任何機制會發現」——比現在「明確顯示 `writable:false`」更
|
||
糟,直接違反 D61 commit 自己引用的 #10「寧可明顯失敗,不要靜默錯置」。
|
||
|
||
**本提案(C):查證結果——技術前提成立,有具體證據,不是推論**
|
||
|
||
D61 的病根是「重裝時 binding 被安裝器照名字重新指到新建的空資源」(console 帳密住 `SESSIONS_KV`、
|
||
portal_user 住 KBDB 的 D1)。**這個病根本身已經在 2026-08-13 被一個更早、更通用、範圍更廣的機制
|
||
解掉**——`shared/resource-rule`(`Arcrun#97`/`arcrun-rag#87`:「已部署的 worker 綁著什麼,那就是
|
||
事實 → 原封不動沿用,不管叫什麼名字」)。現在**每一次**安裝或更新都會先跑這條規則,涵蓋所有
|
||
`kv_namespace`/`d1` binding,不分是不是認證用的——不是為了本提案才要新造的機制,是已經在保護
|
||
workflows/recipes/總圖的既有基礎設施。
|
||
|
||
核實證據(讀碼+讀已生成的成品,非猜測):
|
||
- `.worker-builds/manifest.json`(本次為驗證這個提案,用 `scripts/build-worker-artifacts.mjs`
|
||
重打的真實成品):`arcrun-cypher-executor.requires.kv` 含 `"SESSIONS_KV"`;`requires.d1` =
|
||
`{binding:"CREDENTIALS_DB", database_name:"arcrun-kbdb"}`——與 `arcrun-kbdb` worker 自己的 `DB`
|
||
binding 指向同一個資料庫名(portal_user 的家)。
|
||
- `installer/oauth-prototype/worker.js:1762`:`runInstall` 真的呼叫
|
||
`resolveResourcesByRule(token, accountId, manifestRequirements(manifest, baseName, true), mode)`,
|
||
`mode ∈ {'init','update'}`——**每次安裝/更新都會走這條路,不是選配**。
|
||
- `manifestRequirements()`(`:818-843`)把 `requires.kv`/`requires.d1` 轉成規則輸入,
|
||
`SESSIONS_KV`/`CREDENTIALS_DB` 均未被排除。
|
||
- `shared/resource-rule/rule.mjs` 對 `kv_namespace`/`d1` 一視同仁,不分 binding 名字;同
|
||
`createName`(`arcrun-kbdb`)的 d1 需求會被 `shareSameResource` 收斂成同一顆。
|
||
|
||
**時序細節(誠實揭露,降低但不推翻結論)**:`Arcrun#97` 的觸發事故發生在 2026-08-12(比 D61 晚兩
|
||
天),當時症狀含「portal 登出」——代表 D61 上線後、resource-reuse 落地前,這類事故確實還發生過一
|
||
次。無法確認 leo21c 那次事故當下實際跑的是 D61 前還是後的 bundle(merge 進 main ≠ 部署到任何一台
|
||
實例,這個落差在別處也反覆出現)。不論是哪一種:**resource-reuse 落地之後(08-13 起)**,這個機制
|
||
對 `SESSIONS_KV`/KBDB D1 的保護,與 D61 對認證儲存的保護,範圍是重疊的——這件事在程式碼層級是確
|
||
定的,不是本節不確定的部分。
|
||
|
||
**建議做法**(confirm 後才動,這裡先寫方向不寫實作細節):
|
||
1. 認證資料搬回 D1(走 KBDB base API,比照 D61 之前的舊機制)+ console 帳密搬回 `SESSIONS_KV`——
|
||
寫入走 Workers binding,不需要任何外部 CF token,零過期、零新 OAuth scope、零手動步驟。
|
||
2. D61 加的三個「明顯失敗」機制**保留**,只是底層儲存換回 binding:讀不到不算密碼錯且不計入鎖定
|
||
(`auth_store_empty`)、`/console/setup` 遇既有帳號說清楚密碼沒被採用、`/health`/
|
||
`/console/auth-status` 吐儲存狀態。
|
||
3. `portal-auth-store.ts` 整份(CF Secrets 分片機制)——需要人決定留著當可切換的備援路徑,還是直
|
||
接移除單一化;本提案不預設答案。
|
||
|
||
**影響分析(依 SDD-LIFECYCLE 第 3 條格式)**:
|
||
- **現行 active SDD**:`system-dev/docs/3-specs/portal-auth/`——本提案不影響該 SDD 已完成的任務
|
||
(P2 bootstrap/登入/role 閘等),只改「認證資料住哪」這一層的實作,功能行為(登入/bootstrap/
|
||
改密碼的 API 契約)不變。
|
||
- **尚未完成、待搬移**:本提案本身沒有現成 tasks 可搬,confirm 後在新工作項目下處理。
|
||
- **既有相關**:`arcrun-rag#99`/`inkstone/Arcrun#119`(本次 P0 症狀單,confirm 後統一收斂修
|
||
法);D61(`Leo/arcrun-rag#55`)本身——confirm 即等於部分撤銷 D61 的儲存層決定(保留其明顯失敗
|
||
語意),需要在 ADR 記一筆「D61 補充/取代」。
|
||
- **已裝好、正在跑 D61(CF Secrets 認證儲存)的實例怎麼辦**:需要一次性遷移(讀出 CF Secrets 裡的
|
||
JSON → 寫回 D1/KV),且要處理「這把 CF Secrets 還留著沒人管」的收尾(刪除或忽略均可,留著不影
|
||
響正確性只是佔用 secret 額度)。confirm 後另立遷移步驟,不在本提案本身寫死做法。
|
||
|
||
**信心水位(誠實標,不假裝已驗證)**:上面「技術前提成立」的證據全部來自**靜態推論**——讀原始
|
||
碼、讀本機重打出的 `.worker-builds/manifest.json`、讀呼叫鏈——**沒有跑過一次真的部署+動態驗
|
||
證**。要把信心水位補到「實測過」,得先過 D20(部署 stage)才做得到,而部署本身要 leo 開閘 ⇒
|
||
不能把它當成「先驗證才准寫提案」的前置,否則變成「等閘才能寫提案、寫了提案才要得到閘」的死結
|
||
(總管裁決:先寫提案,把這段誠實揭露)。
|
||
|
||
**怎麼驗(confirm 後、真正動工前必跑,依 D84 之二三段式,不能只驗 update)**:
|
||
1. **測 update**:已裝好、正在跑 D61 的實例,走過一次性遷移後,`/console/auth-status` 回
|
||
`writable:true`,既有帳密登入不受影響。
|
||
2. **uninstall 拆掉後從乾淨狀態模擬第一次安裝**:全新帳號走一次安裝精靈,`/console/setup` 建帳密
|
||
成功、`/portal/admin/bootstrap` 建第一個 admin 成功,**且不需要安裝精靈傳任何表頭**(因為寫入
|
||
走 binding,不再依賴 CF_SECRETS_API_TOKEN 這條路)。
|
||
3. **③ 目前做不到,如實標記**:D84 之二要求「除了測 update 還要用 uninstaller 刪除後再模擬第一次
|
||
安裝」——**uninstaller 現在還不存在**(另一條 subagent 正在做,`Arcrun#120` 家族)。本提案的驗
|
||
收要等 uninstaller 落地才補得齊第 2 步的「先真的拆過一次」那個誠實版本;在那之前,第 2 步只能
|
||
用「全新 CF 帳號」代替「先 uninstall 再裝」,兩者不完全等價(後者才驗得到「舊資源殘留時是否誤
|
||
沿用」這個 resource-rule 專門處理的情境)。
|
||
|
||
**⏸ 等 leo/總管 confirm 才動**(依 SDD 生命週期鐵律第 3 條)。confirm 前,止血用的表頭修法
|
||
(`fix/install-bootstrap-cf-token-99`/`fix/installer-pass-install-token-99`,已 push 分支未合併)
|
||
繼續作為短期止血候選,兩者不衝突——表頭修法讓「建第一個帳號」現在能用,即使本提案要花時間走完整
|
||
個遷移流程也不擋這條路先出。
|
||
|
||
### P2|fan-out 並行執行(一個節點的多條出邊目前是循序跑)— 2026-08-03
|
||
|
||
**觸發**:leo 08-03 原話——「這是在測試中的計畫,**希望體驗很好**,我發現用 gemma4 的反應非常慢。」
|
||
把 rag_chat 的生成端換成 Workers AI 之後(16.87 s → 2.2 s),一量才發現
|
||
**慢的大頭根本不是 LLM**。
|
||
|
||
**實測(1.4.4 實例,逐段疊加、每段取兩次的較快值)**
|
||
```
|
||
① prep(code) 208 ms
|
||
② +kw_search 1,383 ms (+1,175)
|
||
③ +sem_search 3,240 ms (+1,857)
|
||
④ +fetch_triplets 5,198 ms (+1,958)
|
||
⑤ +fetch_blocks a/b/c 7,793 ms (+2,595)
|
||
⑥ +assemble(code) 8,421 ms (+628)
|
||
+ask_llm(Workers AI) ≈10,400 ms (+2,000)
|
||
```
|
||
⇒ **6 個 KBDB 檢索節點合計約 7.6 s,佔全鏈 73%;LLM 只佔 20%。**
|
||
而這 6 個節點**彼此完全獨立**(kw/sem/triplets/blocks×3 誰都不吃誰的輸出),
|
||
只是因為 flow 被寫成一條鏈才一個接一個跑。
|
||
|
||
**根因在引擎,不在 workflow**(`cypher-executor/src/graph-executor.ts`):
|
||
- 起點節點**已經是並行**的(`:85` `Promise.all(startNodes.map(...))`)
|
||
- fan-in **已經支援**(`:78-84` 入度 >1 的節點等所有上游到齊)
|
||
- 但**出邊是循序的**(`:427` `for (const edge of outEdges)`,迴圈內 `await executeNode`)
|
||
⇒ 一個節點分岔出 6 條 `ON_SUCCESS`,是一條跑完才跑下一條。
|
||
⇒ **改 workflow 解不了**:把 6 個節點都掛在 prep 底下,只是把「鏈」變成「6 條循序出邊」,一樣慢。
|
||
|
||
**提案(擇一,我建議 A)**
|
||
|
||
- **A. 出邊並行 + 沿用既有 fan-in**:把 `:427` 那個迴圈改成
|
||
「同型別、無條件分支的出邊」用 `Promise.all` 併發,其餘(`ON_TRUE`/`ON_FALSE`/`ON_BRANCH`/`ON_FAIL`)
|
||
維持原樣。rag_chat 的 6 個檢索節點改掛在 prep 底下、匯流進 assemble(入度 6,fan-in 現成)
|
||
⇒ 預估 **7.6 s → 約 2 s**,全鏈約 **10.4 s → 4.5 s**。
|
||
⚠️ **這是引擎核心、風險最高**(照本 repo 自己的規矩:先寫測試再改)。
|
||
需要先講清楚的語意:多條邊並行時 `result` 怎麼合併(現行是「後一條覆蓋前一條」的隱含語意)、
|
||
任一條失敗時的行為、trace 的順序。**既有 workflow 行為必須零變化,且要跑一次證明。**
|
||
|
||
- **B. 只砍節點數(完全不動引擎)**:`kw_search`/`sem_search` 在 v2 計分裡只是小加分
|
||
(kw 命中 +2、sem 前十 +0.4,主導權在 IDF 字面重疊)⇒ 拿掉這兩個節點可省約 3 s。
|
||
代價是**檢索品質**(少了兩個佐證訊號),屬產品取捨,不是我能裁的。
|
||
|
||
- **C. 不做**:接受目前的 10 s。
|
||
|
||
**影響分析**
|
||
- 現行 active SDD `workflow-discovery`:A 屬引擎能力,與 3.9(`ON_TRUE`/`ON_FALSE` 走邊)同一塊,
|
||
是**新增任務**,不作廢任何既有任務。
|
||
- B 只動 `arcrun-rag/workflows/rag-chat.local.yaml`,不碰本 repo。
|
||
- 三者都**不影響**已完成的 3.12(recipe 三層)與本次 Workers AI 換源。
|
||
|
||
**⏸ 停在這裡等 leo 裁**:回「A」我就先寫測試再改引擎;回「B」我只改 workflow;回「C」就記著不做。
|
||
|
||
### P1|host fn 二進位通道(解開「零件無法處理非文字檔」的框架級限制)— 2026-07-27
|
||
|
||
**觸發**:leo 07-27 原話(arcrun-rag t73 收尾時)——
|
||
> 「**解析各種檔案的零件還是要開發的,要記下來**,雖然跟現在無關,
|
||
> 是因為**這次我用他本機來跑閃掉了**。」
|
||
|
||
**現況=框架級硬牆(2026-07-27 實測,非推論)**:
|
||
Arcrun 的 host function 邊界是 **UTF-8 文字通道 + 64 KB 上限**
|
||
(`outBuf` 64KB/`res.text()`/host fn 內 `TextDecoder`)。後果:
|
||
1. **零件拿不到二進位** ⇒ PDF/docx/圖片這類檔案,**做不出解析零件**。
|
||
2. 雲端 workflow 的 `fetch_raw` 走同一顆 `http_request` ⇒ 同樣拿不到。
|
||
3. 連純文字都受 64KB 限:15 頁 PDF 抽出約 40KB 尚可,**25 頁以上會爆**
|
||
=「轉好了卻進不去」的靜默失敗。
|
||
|
||
**arcrun-rag 目前怎麼過關的(要講清楚,免得被誤會已解決)**:
|
||
把轉檔搬到 **daemon 本機**(`collector/convert.go` 調度層+PDFium-via-wazero)——
|
||
**這是繞過(work around),不是修好**。leo 的原話「用他本機來跑閃掉了」講的就是這件事。
|
||
|
||
**為什麼仍要做(不擋當前工作,但別遺忘)**:
|
||
- **能力分裂**:現在只有「裝了 daemon 的人」有轉檔能力;**純雲端用戶永遠沒有**。
|
||
- **地端/雲端兩版格式支援會分岔**,長期維護兩套。
|
||
- 64KB 上限是**既有**限制,`.md` 今天就會撞到,PDF 只是讓它更快浮現。
|
||
|
||
**變更範圍(提案,待 leo confirm 才動)**:
|
||
① host fn 加二進位/base64 通道(或 ArrayBuffer 傳遞)
|
||
② 放大或分塊化 `outBuf`(串流/分頁讀取,避免單次 64KB 天花板)
|
||
③ 之上才談「各格式解析零件」
|
||
|
||
**影響分析**:
|
||
- **不受影響**:現行 active SDD `portal-auth` 的所有任務(不同層)。
|
||
- **不擋**:arcrun-rag t73 本機轉檔層照做(已 commit `74d18ff`)。
|
||
- **牽動**:`http_request` 零件、code 零件的 host fn 契約 ⇒ 屬**全人共用框架**改動,
|
||
影響所有既有零件,**必須 leo 拍板**(跨專案結構決策)。
|
||
|
||
**現在不做的理由**:leo 明說「跟現在無關」+客戶在等 daemon;本條僅**立案留底**,
|
||
避免「本機能跑了」被後人誤讀成「這個限制已解決」。
|
||
|
||
## 已裁決
|
||
|
||
### ✅ confirmed 2026-07-24|CP2-F cypher 二~四刀拆分(leo 授權總管技術裁決)
|
||
|
||
> **裁決依據**:leo 2026-07-24 原話「不影響現在 CP 的話就排下一個,這個你是需要判斷的,不能是我,完全技術問題」。
|
||
> **總管裁決**:confirm。排程=A4 全通+t21/t24 合併重部之後(與在途不撞)。
|
||
> **四題**:①開工時點=同上 ②刀④驗證=**手寫**(瘦身工程不再引依賴;兩鏈手寫+測試)③新 worker 命名=**arcrun-api** ④CP2-F 記載更正已由總管代辦。
|
||
> 開工時照 D35 ④ 先開新 SDD 搬任務再寫 code。
|
||
|
||
### P-2026-07-24:CP2-F 第二~四刀——執行引擎獨立成精簡 worker(cypher 瘦身收官)
|
||
|
||
> 提案人:總管交辦之 arcrun 偵察 subagent(唯讀分析,未動 code、未部署)。
|
||
> 觸發:頂層 CRITICAL-PATH.md CP2-F(w=9,P3)——「免費起步」承諾不成立;
|
||
> leo 定調:workflow 執行引擎獨立成精簡 worker(只做調度),console/portal/credentials/webhooks 拆出去。
|
||
> **依 D35:現行 active SDD=portal-auth,與本題不同卷,故只寫 proposal 停下等 confirm,不動 code。**
|
||
|
||
#### 0. 先更正頂層 CP2-F 的過時記載(本次實測核實)
|
||
|
||
| 項目 | CP2-F 記載 | 2026-07-24 實測 | 出入原因 |
|
||
|---|---|---|---|
|
||
| 原始碼行數 | 15,446 行 | **11,050 行**(`find src -name '*.ts' \| xargs wc -l`) | 第一刀已砍 |
|
||
| bundle | 748KB | **528.1KB**(540,742 bytes,`wrangler deploy --dry-run --outdir` 實跑) | 同上 |
|
||
| 「ingest 必爆」 | 隱含現在式 | **已穩定**(第一刀驗收:連灌 10 張全過、同卡 6 連跑全過) | 5a16484 已修 |
|
||
| /health 5-7ms、9ms/節點 | 現在式 | **拆分前舊數,第一刀後未重測**——本提案每刀驗法含重測 | 未更新 |
|
||
|
||
第一刀=commit `5a16484`(07-21):console.ts(1623行)+portal-ui.ts(1390行) 搬去 `console-ui/` CF Pages
|
||
(arcrun-console-ui.pages.dev),748→528KB(-29%)。07-22 `ad367e4` 補 console-ui build 修復+
|
||
`deploy.targets.json` 具名部署目標(personal/enterprise 兩 profile,帳號+apiBase+專案名收一檔)。
|
||
→ **CP2-F 條目該由總管在頂層更新為「第一刀 ✅、二~四刀待裁」**,狀態從 ❌ 斷改 ◐ 半通。
|
||
|
||
#### 1. Bundle 實測解剖(528.1KB,sourcemap byte 級歸因,非推測)
|
||
|
||
方法:`npx wrangler deploy --dry-run --outdir=<scratch>` 產 index.js+index.js.map →
|
||
自寫 VLQ 解碼器把每個 byte 歸因到原始檔(mapped 507.5KB+bundler glue 20.5KB)。
|
||
|
||
| 模組 | KB | 佔比 | 主要檔案(行數) | 去向提議 |
|
||
|---|---|---|---|---|
|
||
| **dep: zod** | 130.6 | 24.7% | 只被 `lib/schemas.ts`+`lib/prompt-recipe-schema.ts` import | 刀④逐出引擎 |
|
||
| **dep: hono** | 76.9 | 14.6% | 框架 | 兩邊都要,留 |
|
||
| **執行引擎核心** | 74.3 | 14.1% | graph-executor(702)、component-loader(370)、wasi-shim(674)、execute/executions/resume/cypher routes | **留(引擎本體)** |
|
||
| Credentials/Auth | 48.0 | 9.1% | auth.ts(467)、credentials.ts(322)、auth-dispatcher(301)、auth-recipe-seeds(748行/21.9KB) | CRUD+seeds 搬;dispatcher 讀路徑留 |
|
||
| Portal(RAG 多人) | 42.3 | 8.0% | portal.ts(783)、portal-data.ts(457)、portal-auth、portal-seeds | 刀②搬 |
|
||
| Console API | 31.8 | 6.0% | console-dashboard(526)、console-auth(165)、兩 model(616) | 刀②搬 |
|
||
| dep: unenv/polyfill | 29.1 | 5.5% | nodejs_compat 代價 | 留(兩邊皆有) |
|
||
| Webhooks CRUD | 25.6 | 4.8% | webhooks-named(521)含觸發+管理混一檔 | **拆檔**:trigger/query 留、CRUD 搬 |
|
||
| Recipes CRUD/seeds | 24.1 | 4.6% | recipes.ts(563)、api-recipe-seeds、init-seed | CRUD/seeds 搬;`resolveRecipe` 讀路徑留 |
|
||
| Docs/OpenAPI | 12.5 | 2.4% | openapi.ts(306) | 刀③搬 |
|
||
| KBDB proxy | 8.4 | 1.6% | kbdb-proxy.ts(272) | 刀②搬 |
|
||
| scheduled/cron | 3.7 | 0.7% | scheduled.ts、cron-* | 留(觸發面) |
|
||
|
||
**三個關鍵事實**:
|
||
- zod 一家=24.7%,卻只服務「入口驗證」兩條鏈(execute→schemas、recipe-loader→prompt-recipe-schema)。
|
||
- 引擎真身(核心+hono+unenv)只要 ~180KB;其餘 ~350KB 全是管理/前端 API 面。
|
||
- 耦合點:`graph-executor.ts:6` import `resolveRecipe` from `routes/recipes.ts:406`——
|
||
**讀路徑長在 CRUD 路由檔裡**,拆分前要先抽到 lib(刀②-0 前置小步)。
|
||
|
||
#### 2. 拆分方案(Option A:引擎留原地,管理面搬出——推薦)
|
||
|
||
**為什麼引擎留在 cypher-executor 原 worker 不搬家**:webhook 觸發 URL
|
||
(`/webhooks/:token/trigger`、`/webhooks/named/:ns/:name/trigger`、`/q/:ns/:name`)
|
||
被外部 caller(Telegram webhook、Routine、CLI、MCP)焊死;引擎留原地=**外部零改動**。
|
||
反向(引擎搬新 worker)要全網改 URL,風險大得多,不採。
|
||
|
||
**佈局**:既有 25 顆(22 零件+cypher+kbdb+mcp)→ **26 顆**:新增 1 顆 `arcrun-api`(管理/控制面 TS worker)。
|
||
(arcrun-api 是框架自身的 API 面、非業務零件,與零件須 WASM 的鐵律不衝突——cypher 本身同理。)
|
||
|
||
| 端點群 | 去向 |
|
||
|---|---|
|
||
| `/execute`、`/cypher/*`、`/validate`(刀④前)、`/resume`、`/executions/*`、`/workflows/:name/executions`、webhook **trigger/query**、`/health`、scheduled cron | **cypher-executor(引擎)留** |
|
||
| `/portal/*`、`/portal/data/*`、`/console/*`、`/kbdb/*`(proxy)、`/credentials/*`、`/auth/*` CRUD、`/recipes/*` CRUD、`/webhooks` CRUD+named 管理(backfill/migrate/delete/list)、`/init-seed`、`/docs`+OpenAPI | **arcrun-api(新)搬** |
|
||
|
||
**Service binding 過渡(13 顆 SVC_\* 懶載現況)**:零風險——13 個 binding 服務的是
|
||
`component-loader.ts:73-85` LOGIC_BINDING_MAP 的邏輯零件調度,**全屬引擎**;引擎不搬家=
|
||
wrangler.toml `[[services]]` 原封不動。arcrun-api 完全不需要 SVC binding。
|
||
未配置時 fallback 公網 workers.dev 的既有機制(component-loader.ts:249-272)也不動。
|
||
self-hosted 的 deploy.ts 注入機制(D1 database_id 等)需對 arcrun-api 的 toml 複用同款注入——既有機制,非新開發。
|
||
|
||
**與 07-22 第一刀銜接**:console-ui Pages 的 `ARCRUN_API_BASE`(build 期參數)刀②時改指
|
||
arcrun-api;`deploy.targets.json` 兩 profile 各加一欄,部署命令不變。
|
||
|
||
**刀法(分三刀,每刀獨立可回退)**:
|
||
- **刀②(先抽 resolveRecipe 到 lib)**:Portal+Console API+kbdb-proxy 搬 arcrun-api。
|
||
預估 -82.5KB → 引擎 ~446KB。console-ui apiBase 同步切。
|
||
- **刀③**:credentials/auth CRUD+webhooks 管理面(先把 webhooks-named.ts 拆成 trigger/管理兩檔)+
|
||
recipes CRUD+init-seed(含兩包 seeds 30KB)+docs/openapi 搬過去。預估 -90KB → 引擎 ~355KB。
|
||
- **刀④(zod 逐出引擎)**:`/validate` 搬 arcrun-api(zod 隨行);`/execute` 入口驗證改手寫窄驗證
|
||
(graph shape 檢查 ~50 行)或換 valibot(~10KB);prompt-recipe-schema 同款處理。
|
||
預估 -130KB → **引擎 ~200-225KB(對 528KB 減 ~60%)**。
|
||
|
||
**目標與誠實邊界**:/health <2ms 主要靠 bundle 縮(冷啟 parse+常駐面縮)可期;
|
||
但**單節點 10ms 內不保證光靠瘦身達標**——9ms/節點若主要來自 per-node KV put
|
||
(graph-executor.ts:352 每節點 `kvSetNodeOutput`)+JSON 序列化,則需**刀④.5(備案)**:
|
||
node output 改「僅斷點/暫停時寫 KV、其餘記憶體傳遞」。是否需要動,以刀②後的 cpuTime 實測決定,不預先動。
|
||
|
||
#### 3. 每刀驗法(免費層 cpuTime 實測法)
|
||
|
||
每刀收工四件、缺一不算完成(守 CP 使用規則 6/7):
|
||
1. `wrangler deploy --dry-run` bundle size 對照表(貼實跑輸出,驗預估)。
|
||
2. vitest 全綠+tsc 0(現基線:cypher 169/170,唯一失敗=executor「不存在的零件」pre-existing)。
|
||
3. **免費層 cpuTime 實測**:部署後 `npx wrangler tail arcrun-cypher-executor --format=json` 抓
|
||
`cpuTime`,三個探針各打 10 次取 P50:/health、單節點 workflow(http_request×1)、
|
||
km_wiki_ingest(6 節點)。記進 CP2-F 條目當證據。
|
||
4. 端到端頭尾驗:console-ui 兩 profile 登入+搜尋(打 arcrun-api)、portal 登入、
|
||
`acr push`+`acr run` 走引擎、**webhook trigger 原 URL 打通**(外部 caller 不改的承諾)。
|
||
|
||
**回退**:每刀=一個 PR;arcrun-api 是加法(新 worker),引擎側只刪路由掛載——
|
||
回退=revert PR+重部引擎(arcrun-api 留著不礙事)。KV/D1 零 schema 變更,無資料遷移,無不可逆步驟。
|
||
|
||
#### 4. 影響分析(D35 第 3 條)
|
||
|
||
- **portal-auth(現行 active)**:P1-P4 已完成待部署排練。刀②把 `/portal/*` 路由搬 arcrun-api=
|
||
**只搬家不改行為**,其測試(portal 三套 56/56)隨檔案搬移後應原樣全綠;
|
||
但其「部署清單」要加一行(portal 部署目標從 cypher 變 arcrun-api)。**不作廢任何任務**。
|
||
- **第一刀遺留(5a16484「未做(第二刀)」清單)**:console-dashboard/portal/portal-data=本提案刀②,正式立案銷帳。
|
||
- **library-map(draft)**:M5 console 首頁區塊打 `/kbdb/map`——kbdb-proxy 刀②搬家後 console-ui apiBase 同步切,不改功能。
|
||
- **P-2026-07-21 registry proposal 的 B1**(「在 cypher-executor 開 GET /search」):若兩案都 confirm,
|
||
`/search` 統一面該開在 **arcrun-api** 而非引擎(搜尋是管理面不是執行面)——兩案不衝突,落點修一字。
|
||
- **不受影響**:22 顆零件、kbdb worker、mcp、CLI(`acr` 打的執行端點全留原 URL)、
|
||
webhook 外部 caller、13 個 service binding、artifact-sharing(confirmed)各 Phase。
|
||
- **新增維護面(誠實記)**:多一顆 worker 要部署(官方+self-hosted 各一次);
|
||
deploy 腳本/文件要加 arcrun-api;兩 worker 共用 KV/D1 binding(同一批 id,讀寫面分離)。
|
||
|
||
#### 5. 待 leo 拍板點
|
||
|
||
1. **開工排序**:CP 排 P3(P1=CP3-A、P1↑=A4 OAuth 在前)。本提案只求 confirm 方案,開工時點照 CP 排序走——除非 leo 要提前。
|
||
2. **刀④驗證庫選擇**:手寫窄驗證(零依賴、-130KB 全拿)vs valibot(保留 schema 風格、-120KB)——品味題。
|
||
3. **arcrun-api 命名**:`arcrun-api`?或併入既有規劃中的其他 worker?(跨 repo 佈局=總管/leo 層決策)
|
||
4. **頂層 CP2-F 條目更正**(§0 表)由總管執行——本 repo 只能在此記載,不碰頂層文件。
|
||
|
||
|
||
|
||
> 提案人:總管交辦之 arcrun subagent。
|
||
> 觸發:leo 2026-07-21 —「一旦推進一個零件,就自動進 registry;recipe、workflow、app 都應該可以 registry,
|
||
> 不然搜不到。一邊是寫進去,另一邊是搜到,當然要有。」
|
||
> 判準:「Arcrun = 讓 AI 輕易建立程式碼」「AI 要覺得 Arcrun 比 Python 還簡單,絕不可迷路」。
|
||
> **依 D35:本任務找不到對應的 active SDD(現行 active=portal-auth,與本題無關),
|
||
> 故不動 code、不自建 SDD,寫本 proposal 後停止等 leo confirm。**
|
||
|
||
#### 一句話
|
||
|
||
registry 的寫入機制**存在且可用**,但它唯一的自動觸發點長在 GitHub Actions 上;
|
||
Actions 因防 flag 鐵律被整個刪除後(commit `037cf9b`),**寫入端失去觸發者、庫從此是空的**。
|
||
這不是「沒有機制」,是「機制的手被砍掉、沒補上替代觸發點」。
|
||
|
||
#### 根因(file:line 級)
|
||
|
||
| 事實 | 證據 |
|
||
|---|---|
|
||
| 寫入端點存在 | `registry/src/routes/components.ts:112` `POST /components/index-only`(metadata-only 索引,冪等) |
|
||
| 寫入實作存在 | `registry/src/actions/indexOnlyComponent.ts:44` 寫 `comp:{hash}:{ver}` + `idx:{canonical_id}` 兩個 KV key |
|
||
| 批次灌注腳本存在 | `registry/scripts/backfill-index.mjs`(掃 22 個 contract.yaml → POST index-only) |
|
||
| 單顆註冊腳本存在 | `registry/scripts/register-component.sh`(註解自稱「本地+CI 共用 SSOT」) |
|
||
| **唯一自動觸發點已不存在** | `git show 037cf9b^:.github/workflows/deploy.yml` 第 269-275 行有 `Register component in registry` step 呼叫上述 sh;`037cf9b` 刪除整個 `.github/workflows/`,**該 step 隨之消失,無替代品** |
|
||
| 從未跑過的實證 | 對 leo21c 跑 `REGISTRY_URL=... node scripts/backfill-index.mjs --dry-run` → 22 顆待灌;線上 `/components/search?q=http` 回 `count:0` |
|
||
|
||
→ **答案:是「有機制但從沒跑過」**(且失去觸發者),不是「沒有寫入機制」。
|
||
→ 附帶事實:`registry/wrangler.toml:26` 的 `[[routes]]` 寫死 `registry.arcrun.dev`,
|
||
self-hosted(leo21c)只能靠 workers.dev URL,backfill 腳本預設 `REGISTRY_URL` 也是官方域 —— 需帶環境變數才會打到自己的庫。
|
||
|
||
#### 重要更正:leo 要的東西**有一半已經存在,只是不在 registry 上**
|
||
|
||
`acr search <term>`(`cli/src/commands/search.ts`)已經是 leo 描述的「意圖驅動、跨類一次搜」——
|
||
它 fan-out 四個來源(component 靜態清單 / `/recipes` / `/auth-recipes` / `/webhooks/named`)。
|
||
**實測(2026-07-21,leo21c,真實輸出見交辦回報)**:
|
||
- `acr search http` → 命中零件 `http_request`(驗收劇本 1 ✅)
|
||
- `acr search notify` → 命中 recipe `line_notify_send` + auth-recipe `line_notify`(劇本 2 部分達成)
|
||
- `acr search github` → 命中 auth-recipe `github`(驗收劇本 3 ✅)
|
||
- `acr search telegram` → 命中 recipe `telegram_send` + auth-recipe `telegram`
|
||
|
||
→ **能力在 CLI 有、在 registry/MCP 沒有**。這正是薄殼原則(rule 07)被違反的典型:
|
||
同一個「跨類搜尋」能力長在介面層(CLI)而非 API,於是另一個薄殼(MCP)享受不到。
|
||
**修法的方向不是在 registry 重造一套搜尋,而是把 `acr search` 的 fan-out 能力下沉成 API 端點,CLI/MCP 同吃。**
|
||
|
||
#### 提案內容(四件,全部需 confirm 後才動)
|
||
|
||
**A. 寫入端:補回失去的觸發點(不復活 Actions)**
|
||
- A1. `acr push` / `acr deploy` 成功後自動呼叫 `POST /components/index-only`(本機發起、低頻、單 repo,守 D20 讀寫界線與防 flag 鐵律)。
|
||
- A2. `acr init` / `acr update` 部署完 22 顆零件後跑一次 backfill(讓新裝的人開箱即有索引)。
|
||
- A3. **recipe / workflow 不需要新的 registry 寫入路徑**——它們本來就活在 store(KV),
|
||
`acr recipe push` / `acr push` 當下就已「寫進去」。缺的只是**搜尋端讀得到**(見 B)。
|
||
→ leo 說的「recipe、workflow、app 都應該可以 registry」,正解是**統一搜尋面**,不是把它們搬進 registry KV 再存一份(那會製造第二份真相源)。
|
||
- A4. `app`(bundle)已有 P-2026-07-19 artifact-sharing 卷涵蓋,本卷不重複立案,只在搜尋面預留類別。
|
||
|
||
**B. 搜尋端:能力下沉 + 語意**
|
||
- B1. 在 **cypher-executor** 開 `GET /search?q=`(不是 registry——因為 recipe/workflow 的真相源在 cypher 的 KV),
|
||
server 端做 `acr search` 現有的四類 fan-out,回統一結果(含 `type` 欄位)。
|
||
- B2. `acr search` 與 MCP 新 tool `arcrun_search`(或改造 `arcrun_search_components`)雙雙改為呼叫 B1,介面層不再自行 fan-out(回歸薄殼)。
|
||
- B3. 語意搜尋:KBDB 那條路已驗證可用(Vectorize),把四類的 description 灌進去,讓「我要打一個 HTTP API」這種自然語言句命中 `http_request`。
|
||
現行 `registry/src/actions/queryComponents.ts:104` 明載「這是 Phase 0 的純文字比對版本,Phase 2 接入 Vectorize」——本項即補完該 Phase 2。
|
||
- B4. 搜不到時的文案改為「導向 recipe / 既有 workflow」,**移除「可以用 arcrun_publish_component 提交新零件」的建議**(見 C)。
|
||
|
||
**C. 斷掉「推人去寫零件」的路**
|
||
- C1. `mcp/src/tools/arcrun_search_components.ts:50` 搜不到時明文建議 `arcrun_publish_component` —— 違反 leo 既定規矩(零件走 PR、專業等級)。改為導向 recipe/workflow。
|
||
- C2. `arcrun_get_component_guide`(回 TinyGo 寫 WASM 教學)與 `arcrun_publish_component` 對一般使用者是誤導 → 建議降級為「專業模式」才暴露(預設不掛載),或在描述首句明寫「99% 情況你不需要這個,先用 recipe」。
|
||
- C3. **查證結果:零件相關「已拆到另一個 repo」未獲證實**——`registry/components/` 22 顆零件仍在本 repo,
|
||
wiki 與 git 均查無拆分紀錄。leo 的印象可能來自 `.github-public/` 對外鏡像(`GitHub mirror 發佈模型`)。**此點需 leo 確認**。
|
||
|
||
**D. 過時項**
|
||
- D1. MCP 仍要求已廢的 `api_key` 參數者共 **15 處**:`arcrun_introspection.ts`(4)、`arcrun_recipe.ts`(6)、`arcrun_workflow_crud.ts`(5)。
|
||
2026-07-20 已改 namespace 明碼 → 這些應改為由 server 端 namespace 推導,不再要 AI 傳。
|
||
- D2. **1042 的真正解法已確認並已落地**(本項無需修 code,只需更正記載):
|
||
`global_fetch_strictly_public` compatibility flag,已實裝於 `cypher-executor/wrangler.toml:13`
|
||
與 `.component-builds/http_request/wrangler.toml:8`。
|
||
leo 說的「開了一個什麼,把所有呼叫都視為外部」=**這個 flag**(讓 same-zone fetch 走公網前門)。
|
||
wiki 已有正確記載:`system-dev/wiki/cards/decisions/same-zone-1042用flag解不用binding.md`、
|
||
`mistakes.md:101`、`decisions-summary.md:84`。
|
||
→ 「graph_neighbors 改用 recipe 繞過」的過時說法**在本 repo wiki 查無此記載**(status.md:50-52 只寫 MCP tool PR),
|
||
該過時記載可能在頂層 InkStoneCo wiki 或 MEMORY.md,需由總管在該層更正。
|
||
|
||
#### 影響分析(D35 第 3 條要求)
|
||
|
||
- **現行 active SDD(portal-auth)**:完全不受影響,本提案不碰其任務。
|
||
- **`component-registry-canon`(status: paused,2026-05-07 建)**:**本題其實早有此卷**,
|
||
其 §1.2 診斷的根因與今日實測完全一致(「registry 活著但 index 空的,AI 找不到零件就會繞回 Python」),
|
||
尚有 **29 個未完成任務**。→ **建議:不新開 SDD,改為把此卷 resume 成 active**(並把 A/B/C/D 的新項增補進其 tasks),
|
||
這比另立新卷更符合 D35 第 4 條「先搬移未完成任務」的精神,也避免第二份重疊規格。
|
||
⚠️ 但這需要先把 portal-auth 收尾或轉 paused(單一活性鐵律),**此為 leo 的排序決策,不是 CC 能裁的**。
|
||
- **`workflow-discovery`(paused)/`library-map`(draft)**:與 B1 統一搜尋面高度重疊,resume 時應一併檢視是否合併。
|
||
|
||
#### 待 leo 拍板的四點
|
||
|
||
1. **排序**:要不要把 `portal-auth` 讓位、resume `component-registry-canon` 來做這件事?(單一活性鐵律強制二選一)
|
||
2. **C2**:`get_component_guide` / `publish_component` 要「預設不掛載」還是「留著但改描述」?(品味/方向)
|
||
3. **C3**:零件是否真的已拆到另一個 repo?(leo 記憶待證實)
|
||
4. **B3 語意搜尋**:四類描述灌進 Vectorize 會產生 embedding 呼叫成本,確認可行?(花錢)
|
||
### P-2026-07-19:artifact-sharing 增補「App(bundle)+實例譜系+訂閱更新+多源分享」
|
||
|
||
> 狀態:**confirmed 2026-07-19**(leo「好的可走」)。四個拍板點總管採建議值(leo 可翻案):①排序=Phase 1.5 緊接 Phase 1、在 KV 退休(#16/#17)前後皆可並行 ②訂閱預設 policy=notify ③側載標記第一波=列表標記即可 ④P6 第一波=官方源+直連一個外源,org 私區留第二波。tasks 增補見 artifact-sharing/tasks.md「Phase 1.5」段。
|
||
|
||
> 提案人:雲端總管(leo 2026-07-19 對話三輪收斂後令「寫」)。
|
||
> 目標 SDD:`Leo/Arcrun` `system-dev/docs/3-specs/arcrun/artifact-sharing/`(#27+#31)。
|
||
> 依 D35:本檔為規格層 proposal,**寫入 pending-changes.md 後停下等 leo confirm**,confirm 前不動 tasks、不寫 code。
|
||
|
||
## 一句話
|
||
|
||
workflow/recipe/template 的實體在平台(CF/KBDB),不在 YAML——YAML 是隨叫隨到的視圖(搖桿)。
|
||
因此版本控制、譜系、更新通知必須是**平台內建能力**,git 降級為:引擎程式碼的家+人審 diff 面+災備快照。
|
||
本提案把 #27 公庫從「單品發布/拉取」補全成「App 商店+訂閱更新+實例譜系」。
|
||
|
||
## 背景(為什麼現在)
|
||
|
||
- 實證痛點:arcrun-rag(企業)與 Mira(個人)跑同族管線的兩份變體(rag_ingest v2 vs km_wiki_ingest),
|
||
「哪邊同步了沒」靠人腦;claude.ai 07-19 實測抓到的三個引擎缺口再證「修一次、兩實例都該自動受益」的通道不存在。
|
||
- T-pack-v2「repo 即真相源」是**過渡義肢**:因為平台缺版本層(KV 讀不回、無版本、無譜系),才用 git 代位。
|
||
義肢照 D15 前例:平台長出器官後拆。
|
||
- 地端版(workerd)將成第三個實例形態——在複雜度上升前把「App×實例×版本」模型立好。
|
||
|
||
## 提案內容(五件)
|
||
|
||
### P1. bundle 第四型(App)
|
||
- `public_artifact` 新增 `type=bundle`。portable_body=成員清單:
|
||
`[{type: workflow|recipe|template, canonical_id, uuid(鎖版) 或 version_policy(track-latest)}]`
|
||
+`install_params_schema`(安裝參數:多人開關、Gitea 端點、LLM provider…)+`conformance`(見 P5)。
|
||
- 「裝一個 App」=pull 一個 bundle,成員經既有 dependency_manifest 機制解析;
|
||
個人版與企業版=**同一個 bundle、不同 install params**。
|
||
- arcrun-rag 為第一個 bundle(現行 install.sh 即其手寫前身,落地時翻譯成 manifest)。
|
||
|
||
### P2. 訂閱與更新通知(可拒、可 pin)
|
||
- install/pull 時建立訂閱關係:新 entry `artifact_subscription`
|
||
(subscriber namespace、canonical_id、目前安裝 uuid、policy: notify|auto|pin)。
|
||
(`artifact_pull_event` 保持純事件帳本不改;訂閱是關係、事件是史料,分開存。)
|
||
- 新版 submit-p 後,訂閱者「得知」的通道**查詢式優先**:
|
||
`GET /subscriptions/updates`(實例/AI/console 隨時問「我有沒有落後」);
|
||
推播(notify workflow)為選配、且**禁止因發布事件自動 fan-out 到多實例**(D4 紅線——通知生成可以批次/排程,不掛事件觸發鏈)。
|
||
- 更新永遠是訂閱端的**顯式動作**:可更、可拒、可 pin 死版本。
|
||
|
||
### P3. installed_from 譜系+側載標記(隱形工作流可見化)
|
||
- 實例內每個 materialized artifact(workflow/recipe/template)落籍記
|
||
`{installed_from_uuid, installed_at, content_hash}`(entry metadata,不建表)。
|
||
- 平台即可回答四態:`up_to_date / behind / diverged(本地改過)/ sideloaded(無譜系,API 直建)`。
|
||
- list/console/MCP 的 workflow 列表帶狀態標記。**側載不禁止、只標記**(Android sideload 哲學);
|
||
「沒有 YAML 就不准」的 policy 不採納——對 AI 是降頻,可見性由本條提供。
|
||
- 既有 git 外掛式 drift 稽核(guard 對 hash)為過渡措施,本條落地後退役。
|
||
|
||
### P4. 更新與分岔語意
|
||
- update=拉新版重新 materialize(installed_from 換新 uuid)。
|
||
- `diverged` 者不自動覆蓋:提示二選一——fork(submit-p 推回公庫成自己的作者版本,derived_from 記溯源)或捨棄本地改動收新版。
|
||
- 對應 leo 模型:「Mira 和 Arcrun RAG 都 clone 了 rag app,都可以推回去存成新版本,收到更新通知但自己決定要不要更」。
|
||
|
||
### P5. conformance self-check 隨 bundle(驗收矩陣自動化)
|
||
- bundle 附驗收清單:介面×能力的機械檢查(例:portal 登入 200、MCP tools/list 含 kbdb_*+graph、
|
||
keyword/semantic/graph 三模式各一題 golden question 命中)。
|
||
- install/update 完自動跑、產報告;解「企業 GUI 驗了但企業 MCP/個人 GUI/個人 MCP 不知道、檢查到沒完沒了」——
|
||
可用性從人肉巡邏變安裝產物。地端(workerd)實例=同一張體檢單多跑一列。
|
||
|
||
### P6. registry=源(source)非地方:多源與點對點分享(leo 2026-07-19 追加)
|
||
- 公庫端點長在 cypher-executor=**每個實例都內建 registry 能力**;官方公開市場只是「預設源」。
|
||
- 實例可加多個源:公開市場/org 內部源/**另一個實例直連**(A 做了 App,B 把 A 加為源直接拉,CF-to-CF,不經公開市場——Homebrew tap 模式)。
|
||
- artifact 加 `visibility: public | org | unlisted`;外源拉取走既有 token 認證(portal-auth/MCP OAuth 機制,A 發「可拉取」token 給 B)。
|
||
- P3 落籍擴充:`installed_from` 記「源+uuid」;P2 訂閱跨源成立(B 訂閱「A 源上的 canonical_id」)。
|
||
- **信譽記分邊界**:私下/org 內分享的 pull 事件留在該源帳本,**不進公開信譽**;日後同 canonical 發布公開市場,溯源保留、私史不折算。
|
||
- 信任判斷仍在 import 端(#27 原則):來源是誰,列表標記說清楚。
|
||
|
||
### P6 鐵律:拉取=落地複本,runtime 零外部依賴(leo 2026-07-19 Drive 之問定形)
|
||
- pull/install=把 artifact(含 dependency_manifest 解析出的全部依賴,跨源亦然)**materialize 進本實例 KBDB**——複本式分發(npm/App Store),非引用式分享(Google Drive)。
|
||
- 源斷線/停止分享=只失去更新,**已裝的照跑**;狀態標 `source_lost`。互相參照在安裝時全落地,runtime 永不回頭找源;只有「查更新」動作碰源。
|
||
- 多源不亂的機制:裝完面對的是一張本地清單(每項帶源+五態標記);同 canonical_id 跨源衝突=安裝時顯式選源,不靜默合併;bundle 鎖版 uuid=lockfile 對應物。
|
||
|
||
### 排序定調:分享先行、投稿後行(leo 2026-07-19 收斂)
|
||
- **官方的本質=第一個策展人**(leo 定調):官方源不是特權角色,就是「把很多資源收集起來分享給大家的人」;公開市場=分享機制+官方獎勵制度(信譽層)疊上去,作為分享落地後的下一件事。
|
||
- **先做**:P6 最小版(官方源+直連外源)+P1+P3 → 這就是產品自己的部署通道(leo 官方源→客戶實例拉 arcrun-rag bundle;第一組 A/B=leo 與第一個客戶、以及 Mira);再 P2 訂閱更新。
|
||
- **後開**:公開投稿+信譽榜——空市場掛榜=鬼城,等 self-hosted 用戶密度夠再開(社群玩法)。
|
||
- **零代價保證**:#31「事件是真相、統計是視圖」——pull/submission 事件 day-one append,信譽晚開也能從頭算,先分享不欠投稿任何債。
|
||
|
||
### 附:獎勵/信譽(確認既有設計,非新增)
|
||
- 「貢獻 recipe 得 reputation」=#31 作者信譽經濟已設計(作者頁/聚合信譽/選版複合分/獎勵層預留、author 綁認證身份);事件 day-one append 保證信譽可事後計算。
|
||
- 本提案只補一句:**bundle(App)作者同樣累積信譽**——組裝 App 與寫 recipe 同屬貢獻。
|
||
|
||
## 影響分析
|
||
|
||
- **不建表**:全部走 entry_type+metadata_json(D6 鐵律);`artifact_subscription`/落籍 metadata 皆 template/slot 層。
|
||
- **與 Phase 1 關係**:不改既有 1.1–1.6 任務;本提案為 Phase 1.5(bundle/lineage/subscription 建在單品公庫之上)。
|
||
1.5 的 recipe KV 過渡(#16)不受影響。
|
||
- **與 D4 flag 鐵律**:通知採查詢式優先、禁事件 fan-out(見 P2),無 GitHub 流量、無 Actions。
|
||
- **與現行 installer**:過渡期 install.sh 續用;第一步可先讓 installer **代寫 installed_from 落籍**(義肢幫器官接生),
|
||
公庫端點好了再切換 pull 路徑——遷移平滑、無一次性大爆改。
|
||
- **受益者**:Mira 跟上企業版=「訂閱同一個 bundle 按更新」;未來 N 個客戶實例同此通道;地端版=第三個實例非第三套系統。
|
||
- **風險**:scope 擴張(#27 尚未動工就加型)——緩解:P1/P3 是最小核(bundle 形狀+落籍欄位),
|
||
P2 通知與 P5 self-check 可各自獨立分批落地;全部 compute-on-read 起步,物化快照沿用 #31 判準。
|
||
|
||
## 待 leo 拍板點
|
||
|
||
1. Phase 1.5 排序:在 Portal(#24/#25 已收官)之後、KV 退休(#16/#17)之前或之後?
|
||
2. 訂閱 policy 預設值:notify(通知不動手)?
|
||
3. 側載標記的呈現強度:只列表標記,或 console 加「無譜系 workflow」提醒區塊?
|
||
4. P6 多源第一波範圍:只做「官方源+直連一個外源」?org 私區(visibility=org)是否留第二波?
|
||
|
||
---
|
||
|
||
## [proposal] 兩個真缺口要進 SDD(2026-07-30,總管提,等 leo confirm)
|
||
|
||
**來源**:頂層 CP `critical-paths/arcrun-usable.md`(leo confirm 開工)逐步查證後,
|
||
發現兩件事**SDD 完全沒有**,而它們是「AI 不必寫 code」的前提。
|
||
|
||
leo 的判準(2026-07-30):
|
||
> 「如果在這裡編任務,不就是廢掉了原本的 SDD,那到底要照 SDD 做事還是照 CP?」
|
||
⇒ **照 SDD 做事、照 CP 排順序**。CP 不得自帶任務 ⇒ 這兩件必須進 SDD 才能做。
|
||
|
||
### 缺口 ①:引擎缺條件邊(`ON_TRUE`/`ON_FALSE`)→ 建議進 `arcrun-core-mvp`
|
||
|
||
- **實測**:`if_control` 的 output 是 `{result: boolean, branch: "true"|"false"}`,
|
||
但 `cypher-executor/src` 全域 grep `ON_TRUE|ON_FALSE` = **0**(圖的邊只有 `ON_SUCCESS`)
|
||
- **後果**:AI 照規矩用 `if_control` 也只拿到布林值,**還是得寫 code 判斷該走哪裡**
|
||
⇒ 這是「為什麼 workflow 全變成 code」的根(8 個 code 節點含 if×61)
|
||
- **佐證**:`kbdb_upsert_block` 契約自述「解 arcrun workflow **缺 IF/branch 能力**的缺口
|
||
(arcrun.md P1 #1)」;Gitea **Arcrun#5**「if_control 無法真擋分支,filter+FOREACH 才是真閘」
|
||
於 **2026-07-04** 就發現,至今未修
|
||
- **SDD 查證**:`grep "ON_TRUE|ON_FALSE|條件邊|分支"` 於全部 SDD → 7 命中**全是別義**
|
||
(「部署分支」「git 分支」),**條件邊本身完全沒設計過**
|
||
- **影響面**:改圖 schema + `graph-executor` 走邊邏輯=引擎核心,風險最高
|
||
⇒ 建議排在「查詢誠實」「節點替換」驗過之後
|
||
|
||
### 缺口 ②:recipe 缺 payload 與回應處理層 → 建議進 `recipe-system`
|
||
|
||
- **實測**:recipe schema 只有 `{canonical_id, endpoint, method, auth_service}`
|
||
(線上 `GET /recipes/telegram_send` 實查)
|
||
- **後果**:`telegram_send` 的 description 明寫「body 帶 chat_id+text」但**schema 存不住 body**
|
||
⇒ 任何要帶 body 的 API 都只能繞過 recipe、把 URL/header/body 整包寫進 workflow
|
||
⇒ Gemini 就是這樣寫死的(`ask_llm` 三層全塌進節點)
|
||
- **要補三層**
|
||
- `body_template`(payload)
|
||
- `response_map`(回應正規化:取值路徑/思考型模型旗標/淨化規則)
|
||
——現在這段是 workflow 裡 2786 字元的 code(`finalize`)
|
||
- `auth` 加第四型 `binding`(免金鑰;一次打開 `env.AI`/`VECTORIZE`/`BROWSER`/`QUEUE`)
|
||
- **SDD 查證**:`grep "body_template|response_map|payload"` → 17 命中**全是別的 payload**
|
||
- **不做的話**:LLM 換源永遠要改 workflow(而非換 recipe),違反「LLM 可換源」原則
|
||
|
||
### 誤標修正(誠實記錄)
|
||
總管原本在 CP 標了 **5 個新缺口**,逐條查 SDD 後**只有上述 2 個是真的**:
|
||
- `success_rate` 回寫 → **`arcrun-core-mvp/design.md:499` 早就設計**
|
||
(`success_rate = success_runs / total_runs * 100`、排序規則、UI 顯示),只是沒實作
|
||
- 8 個壞範例、意圖節點替換 → 相關設計散在既有 SDD,已改為引用
|
||
|
||
### ⏸ 等 leo confirm
|
||
- confirm 後依 D35:兩份 SDD 目前皆 `paused`,要動需先處理單一活性
|
||
(現行 active=`workflow-discovery`)
|
||
|
||
|
||
## 提案:workflow export/import 一等公民化(2026-07-31,總管代 leo 口頭定調立案)
|
||
|
||
- **來源**:leo 07-31 原話(見 workflow-discovery tasks.md 3.9 引文)——分享 workflow
|
||
給同事的正路=export 檔→import,不是「用 search 湊」。
|
||
- **變更面**:① cypher `GET /webhooks/named/:name/definition`(已實作,staging 試點中,
|
||
零新資料模型/同 list 認證)② `acr workflow export <name>`/`acr workflow import <file>`
|
||
新 CLI 命令(已寫未接線)③ 安裝器 workflows.json=export 檔同形狀(graph 預編,已上 prod)。
|
||
- **不動的牆**:KBDB 零接觸(export 讀 WEBHOOKS KV workflow record;import 打既有
|
||
POST /webhooks/named);「部署≠發現」(import 不驗零件存在,執行時現形);
|
||
description 必填閘照舊。
|
||
- **影響分析**:新公開端點 1 個(租戶 key 認證,吐的是該租戶自己部署的 workflow 定義;
|
||
無跨租戶讀);CLI 新命令 2 個(薄殼,能力全在既有 API);無 schema migration、無新金鑰面。
|
||
- **狀態**:⏳ 待 leo confirm 後 3.9b 接線。
|
||
|
||
|
||
## 提案:Arcrun App ↔ Portal 掛載協定 v0(2026-08-09,Leo/Arcrun#82)
|
||
|
||
- **設計全文住在票上,本檔只留指針**(leo 08-09 立:設計寫在 issue 上,別讓下一個人再翻一次源碼)
|
||
→ `Leo/Arcrun#82` 的「設計提案:Arcrun App ↔ Portal 掛載協定 v0」留言。
|
||
- **觸發**:leo 08-09——「要變成是一個 Arcrun App,含前端、template、slots 的設置,任何人可以
|
||
安裝後他的 Arcrun 就跳出筆記功能」+「Mira 的那些功能萃取都要變成可安裝」+追加指示
|
||
「等你搞清楚 Mira 的功能時直接改成 App 模式,**不要分兩段**,不然開發好了又要一次遷移」。
|
||
- **病根(實查)**:Portal 是單檔 HTML,「有哪些頁」在 `console-ui/public/portal/index.html`
|
||
裡寫了四遍(側欄 :253-259/tab :510-516/VIEWS :718/allowedViews :721-729);出貨時整包
|
||
內嵌成單檔 worker(`build-ui-bundle.mjs:154`),安裝器只會「整顆換掉」、無任何掛載點概念。
|
||
⇒ 加一個能力=改核心+重出 release。`Leo/mira#2` 那批能力若照現況搬,就是逐個焊死。
|
||
- **變更面**:① KBDB seed 一列 `arcrun_app` template(零新表,加進 `lib/portal-seeds.ts:21`)
|
||
② `/portal/session`(`routes/portal.ts:495-515`)多回唯讀 `apps`
|
||
③ 新增泛用端點 `POST /portal/apps/:id/actions/:name`(server 側代打既有 named webhook,
|
||
金鑰不外流,形狀同既有 `/portal/data/upload`,但**泛用**)
|
||
④ Portal 一次性泛用 loader(約 60 行)⑤ `acr app build|install|remove|list`
|
||
⑥ `arcrun-app.yaml` 的 JSON Schema(repo 目前無「可安裝單位」schema,這是第一份)。
|
||
- **不動的牆**:KBDB 零 SQL、永不加表;App 前端不進 `arcrun-rag-ui` bundle、不進
|
||
`bundle-components.mjs`(否則回到「改核心才能加能力」);App 拿不到 session token
|
||
與租戶字串,資料一律走既有 `/portal/data/*` 的 owner_id+library enforce;
|
||
App 自帶 workflow 必須自帶預編圖(冷實例即時編圖 25.7s > 15s timeout 的既有教訓)。
|
||
- **影響分析**:現行 active SDD=`workflow-discovery`,本提案**不作廢它任何任務**,屬新增一卷;
|
||
新公開端點 1 個(portal session 認證);新 template 1 列;無 schema migration、無新金鑰面;
|
||
既有 `/portal/data/upload` 可事後收編成一個 App,不必第一天做。
|
||
- **⏸ 等 leo 裁的兩題(票上 §七)**:① App 前端跑 Portal origin(ES module)還是 iframe
|
||
——短期 App 是否都由我們自己寫?② v0 只開 `nav` 一個掛載點夠不夠?
|
||
- **狀態**:⏳ 待 confirm。沒 confirm 前照現行 SDD 走,不開新 SDD、不動功能程式碼。
|
||
|
||
---
|
||
|
||
## 提案:安裝器的狀態判斷一律看帳號,中心帳本降級成純優化資料(leo 2026-08-14)
|
||
|
||
- **leo 原話(本提案的來源,也是判準)**:
|
||
> 「你吃飯了沒?是由我來判斷的嗎?我看到你沒有盤子,應該問你吃了嗎?
|
||
> 但我沒問,堅持說你吃過了,**因為我這裡登記你吃了,結果讓你不能買飯**。」
|
||
> 「我有**檢修孔**,需要去問問就好了,東西有沒有**一看就知道**,
|
||
> **根本不應該由中心判斷**。」
|
||
|
||
- **病根(同一天三次撞牆,同一個形狀)**:
|
||
安裝器用**自己的帳本**(`INSTALLER_KV` 的 `deployed:<accountId>:<subdomain>`)
|
||
決定「這台裝過了沒」,而不是去看**帳號上現在有什麼**——
|
||
但它手上一直握著那把 CF token,一次 API 就問得到(=leo 說的檢修孔)。
|
||
|
||
| 撞牆 | 帳本說 | 現場 | 結果 |
|
||
|---|---|---|---|
|
||
| leo(`#120`) | 裝過了 → `update` | leo21c 已被清空,0 顆 worker | `rule.mjs:257` 停手「找不到任何要更新的 worker」,**畫面上沒有出口** |
|
||
| 封測者(`#123`) | 沒人綁這個名字 → 可以建 | 名字已被上次半殘安裝佔走 | CF 回 `title already exists`,**帳號永久卡死** |
|
||
| 08-14 早上的處置 | — | — | **人工去 uncle6 刪掉那筆紀錄**才能繼續=把帳本改回跟現場一致。**那是繞過,不是修法** |
|
||
|
||
- **要達成什麼**:**帳本壞掉、被清掉、或根本不存在時,安裝器照樣裝得起來。**
|
||
|
||
- **變更面(規格層,不是實作指定)**:
|
||
1. **`mode`(init/update)的來源改成帳號現況**——「這個帳號上有沒有我們的 worker」。
|
||
`worker.js:1750-1756` 現在讀兩條通道的 KV 前綴決定,改成問帳號。
|
||
2. **`rule.mjs:257` 那道「說是更新卻一顆 worker 都找不到 → 停手」不再是錯誤**。
|
||
在新判準下這個組合根本不會出現:沒有 worker ⇒ `mode` 本來就會是 `init`。
|
||
(該閘原本是 `#97` 的另一道門,其防護目的由「2b 已部署綁定絕對優先」承接。)
|
||
3. **那張紙搬到用戶自己的帳號上,中心不再保管**(leo 2026-08-14 第三次收斂,比「降級」更徹底):
|
||
|
||
> 「**我覺得根本不用我 keep**,這次裝的 sha **記在一張紙放你家**,
|
||
> 下次又要裝我就把那張紙拿出來看你上次裝了什麼版本,
|
||
> **如果不符,以實際有幾顆為準**。」
|
||
|
||
⇒ 紀錄(每顆 worker 的 `sha256`、`bundleBase`)**寫在使用者自己的 Cloudflare 帳號上**,
|
||
不再寫進我們的 `INSTALLER_KV`。
|
||
|
||
🔑 **為什麼這個比「降級」更好——它讓那個 bug 變成不可能發生,而不是被處理掉**:
|
||
紙跟東西放在同一個地方 ⇒ **他把帳號清空時,紙跟著消失**
|
||
⇒ 結構上不可能出現「東西沒了、而我們手上還留著一張說他裝過的紙」。
|
||
`#120` 因此不是「被修好」,是**沒有地方可以發生**。
|
||
|
||
附帶好處:
|
||
· 跨通道互蓋(t157)不必再讀「對方通道的 KV」——紙就在帳號上,兩條通道看的是同一張
|
||
· 我們不再保管一份「誰裝了什麼」的中央登記簿(少一個要維護、會漂、會外洩的東西)
|
||
· 帳本壞掉/被清掉/從沒有過,安裝器照樣 work
|
||
|
||
🔗 **必須一併處理的勾連:辨識碼的「更新者免碼」**(leo 2026-08-14 問「辨識碼是用來找那張紙的嗎」,
|
||
查證後方向是**反的**):
|
||
· 辨識碼=**邀請閘**(`verifyInviteCode` 打 landing 的 `/api/verify-code`),與帳本無關;
|
||
真正認出使用者的是 **email**(`slugFromEmail` 決定所有資源名字),辨識碼不參與。
|
||
· 但 `worker.js:3246`(t154)**用那張紙免掉辨識碼**:碼留空 → 放行進 OAuth →
|
||
查此帳號有無部署紀錄 → 有=視同已核可免碼;無=退回要碼。
|
||
⇒ 紙搬到帳號上之後,「免碼」的判準自然從**查中心登記簿**變成**去帳號上看一眼**。
|
||
⇒ 附帶讓 `#120` 那個情境變**誠實**:帳號被清空 ⇒ 沒有 worker、沒有紙 ⇒ 你本來就是新裝,
|
||
**「請填辨識碼」是正確答案**;對照今天是「免碼放行 → 進去才說找不到 worker → 停手、沒有出口」。
|
||
**同一個情境,一個是請你填個碼,一個是把你關在門外。**
|
||
⚠️ 辨識碼**不可拿掉**——那是封測資格閘,與本提案無關。
|
||
|
||
⏸ **要 leo 一併裁的兩題**(實作前必須有答案,但**做法由收工方判斷**):
|
||
① 那張紙**貼在帳號的什麼位置**才會「跟著東西一起消失」——
|
||
要能被拆除工具連帶清掉,且不需要額外權限面
|
||
② `hasDeployRecordForToken`(t154:用一把 token 查「這些帳號有沒有裝過」)
|
||
現在靠中心前綴掃描,搬走後要改成逐帳號去看——**這是預期內的成本,不是阻礙**
|
||
4. 🔴 **鐵律:帳本永遠不得產生「停手」。** 帳本與現場不合時,
|
||
**以現場為準並順手把帳本改對**,不是把使用者擋在門外。
|
||
|
||
> leo 補充(2026-08-14,這句定義了帳本的身分):
|
||
> 「**我只是記錄上次的,這次去看看**,本來要記錄 sha,
|
||
> **一看東西都沒了,也不能硬說你裝過**。」
|
||
>
|
||
> ⇒ **帳本記的是「上次」,不是「現在」。** 它是歷史紀錄,不是狀態宣告。
|
||
> 每一趟都要去看現場;**看到的與帳本衝突時,看到的才算數**。
|
||
> ⇒ 這也是為什麼「帳本不得停手」不是一條例外處理,而是它本來就沒有那個權限——
|
||
> 一份講過去的紀錄,本來就不該否決現在觀察到的事實。
|
||
|
||
- **不動的牆**:
|
||
· 2b「已部署 worker 的綁定=事實,絕對優先」不得放寬(`#97` 的災情來源)
|
||
· 接管同名資源仍須呼叫端聲明 `createNameIsOurs`,預設 fail-closed(`#123` 的反向災情)
|
||
· 判斷仍只有**一份**,在 `shared/resource-rule/`;安裝器不得自建第二套
|
||
|
||
- **影響分析**:
|
||
· 動的是 `installer/oauth-prototype/worker.js` 的 mode 決定點 + `shared/resource-rule/rule.mjs`
|
||
的 update-mode 前置閘;**鏡像兩份要同批**(`MIRROR.json` 守著)
|
||
· 多一次帳號查詢(`listAllScripts` 本來就會查,可共用,預期零額外成本)
|
||
· 不動 KBDB、無 schema 異動、無新金鑰面、無新公開端點
|
||
· 會讓 `#120` 從「要人工清中心紀錄」變成不需要人介入
|
||
|
||
- **牽涉的票**:`inkstone/Arcrun#120`(重裝死結,本提案的主治對象)
|
||
|`inkstone/Arcrun#123`(已併 `9ad95ee`,是同一個病在資源層的那一半)
|
||
|`inkstone/arcrun-rag#78`(用戶版 uninstaller,受益但不因此結案)
|
||
|
||
- **狀態**:⏳ **待 leo confirm 範圍**。方向已由 leo 當面給定;未 confirm 前不動功能程式碼。
|