Files
Arcrun/system-dev/docs/3-specs/pending-changes.md
T
uncle6me-web 8286c8afec docs(pending-changes): 提案——認證儲存搬回 D1/KV(撤 D61 的儲存層決定,留其明顯失敗語意)
arcrun-rag#99 止血修法(安裝精靈傳 OAuth token 給 cypher 建第一個帳號)只解一格,
之後的寫入(換密碼/加使用者/存金鑰)仍永久 writable:false,是留債。

查證(讀碼+讀本機重打的 .worker-builds/manifest.json,非猜測):D61 想解的病根
(重裝時 binding 被照名字重新指到新建空資源)已在 2026-08-13 被更通用的
shared/resource-rule(Arcrun#97)解掉——SESSIONS_KV、KBDB 的 D1 都在 cypher 的
requires 清單裡,且 runInstall:1762 每次安裝/更新都會走這條規則。技術前提成立。

提案:認證資料搬回 D1/KV(走 binding,零外部 CF token、零過期、零新 OAuth scope、
零手動步驟),保留 D61 加的三個明顯失敗機制。附影響分析、遷移考量、信心水位
(靜態推論,未動態實測)、D84 三段式驗法(③ uninstall 目前做不到,如實標記)。

⏸ 等 leo/總管 confirm,本 commit 不動任何架構程式碼。

順手修:writeAuthStore 的錯誤訊息移除誤導的「/ CF_ACCOUNT_ID」(已查證全新實例
一定有這個 var,installer worker.js:1354 無條件注入,列兩項只會讓下一個人白工)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 12:06:50 +08:00

618 lines
50 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 本人與封測者
都實撞。根因:D612026-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,不分是不是認證用的——不是為了本提案才要新造的機制,是已經在保護
workflowsrecipes/總圖的既有基礎設施。
核實證據(讀碼+讀已生成的成品,非猜測):
- `.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 前還是後的 bundlemerge 進 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 → 寫回 D1KV),且要處理「這把 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 實例,逐段疊加、每段取兩次的較快值)**
```
① prepcode 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)
⑥ +assemblecode 8,421 ms (+628)
+ask_llmWorkers AI ≈10,400 ms (+2,000)
```
**6 個 KBDB 檢索節點合計約 7.6 s,佔全鏈 73%LLM 只佔 20%。**
而這 6 個節點**彼此完全獨立**kwsemtripletsblocks×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(入度 6fan-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.12recipe 三層)與本次 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-24CP2-F cypher 二~四刀拆分(leo 授權總管技術裁決)
> **裁決依據**leo 2026-07-24 原話「不影響現在 CP 的話就排下一個,這個你是需要判斷的,不能是我,完全技術問題」。
> **總管裁決**confirm。排程=A4 全通+t21/t24 合併重部之後(與在途不撞)。
> **四題**:①開工時點=同上 ②刀④驗證=**手寫**(瘦身工程不再引依賴;兩鏈手寫+測試)③新 worker 命名=**arcrun-api** ④CP2-F 記載更正已由總管代辦。
> 開工時照 D35 ④ 先開新 SDD 搬任務再寫 code。
### P-2026-07-24CP2-F 第二~四刀——執行引擎獨立成精簡 worker(cypher 瘦身收官)
> 提案人:總管交辦之 arcrun 偵察 subagent(唯讀分析,未動 code、未部署)。
> 觸發:頂層 CRITICAL-PATH.md CP2-Fw=9P3)——「免費起步」承諾不成立;
> leo 定調:workflow 執行引擎獨立成精簡 worker(只做調度),console/portal/credentials/webhooks 拆出去。
> **依 D35:現行 active SDDportal-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.1KBsourcemap byte 級歸因,非推測)
方法:`npx wrangler deploy --dry-run --outdir=<scratch>` 產 index.jsindex.js.map →
自寫 VLQ 解碼器把每個 byte 歸因到原始檔(mapped 507.5KBbundler 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) | CRUDseeds 搬;dispatcher 讀路徑留 |
| PortalRAG 多人) | 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`
被外部 callerTelegram webhook、Routine、CLI、MCP)焊死;引擎留原地=**外部零改動**。
反向(引擎搬新 worker)要全網改 URL,風險大得多,不採。
**佈局**:既有 25 顆(22 零件+cypherkbdbmcp)→ **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` CRUDnamed 管理(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**PortalConsole APIkbdb-proxy 搬 arcrun-api。
預估 -82.5KB → 引擎 ~446KB。console-ui apiBase 同步切。
- **刀③**credentials/auth CRUDwebhooks 管理面(先把 webhooks-named.ts 拆成 trigger/管理兩檔)+
recipes CRUDinit-seed(含兩包 seeds 30KB)+docs/openapi 搬過去。預估 -90KB → 引擎 ~355KB。
- **刀④(zod 逐出引擎)**`/validate` 搬 arcrun-apizod 隨行);`/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、單節點 workflowhttp_request×1)、
km_wiki_ingest6 節點)。記進 CP2-F 條目當證據。
4. 端到端頭尾驗:console-ui 兩 profile 登入+搜尋(打 arcrun-api)、portal 登入、
`acr push``acr run` 走引擎、**webhook trigger 原 URL 打通**(外部 caller 不改的承諾)。
**回退**:每刀=一個 PRarcrun-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-mapdraft**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-sharingconfirmed)各 Phase。
- **新增維護面(誠實記)**:多一顆 worker 要部署(官方+self-hosted 各一次);
deploy 腳本/文件要加 arcrun-api;兩 worker 共用 KV/D1 binding(同一批 id,讀寫面分離)。
#### 5. 待 leo 拍板點
1. **開工排序**CP 排 P3P1=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 —「一旦推進一個零件,就自動進 registryrecipe、workflow、app 都應該可以 registry
> 不然搜不到。一邊是寫進去,另一邊是搜到,當然要有。」
> 判準:「Arcrun = 讓 AI 輕易建立程式碼」「AI 要覺得 Arcrun 比 Python 還簡單,絕不可迷路」。
> **依 D35:本任務找不到對應的 active SDD(現行 activeportal-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-hostedleo21c)只能靠 workers.dev URLbackfill 腳本預設 `REGISTRY_URL` 也是官方域 —— 需帶環境變數才會打到自己的庫。
#### 重要更正:leo 要的東西**有一半已經存在,只是不在 registry 上**
`acr search <term>``cli/src/commands/search.ts`)已經是 leo 描述的「意圖驅動、跨類一次搜」——
它 fan-out 四個來源(component 靜態清單 / `/recipes` / `/auth-recipes` / `/webhooks/named`)。
**實測(2026-07-21leo21c,真實輸出見交辦回報)**
- `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 SDDportal-auth**:完全不受影響,本提案不碰其任務。
- **`component-registry-canon`status: paused2026-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-19artifact-sharing 增補「Appbundle)+實例譜系+訂閱更新+多源分享」
> 狀態:**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 artifactworkflow/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=拉新版重新 materializeinstalled_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-authMCP 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 鎖版 uuidlockfile 對應物。
### 排序定調:分享先行、投稿後行(leo 2026-07-19 收斂)
- **官方的本質=第一個策展人**(leo 定調):官方源不是特權角色,就是「把很多資源收集起來分享給大家的人」;公開市場=分享機制+官方獎勵制度(信譽層)疊上去,作為分享落地後的下一件事。
- **先做**:P6 最小版(官方源+直連外源)+P1+P3 → 這就是產品自己的部署通道(leo 官方源→客戶實例拉 arcrun-rag bundle;第一組 A/Bleo 與第一個客戶、以及 Mira);再 P2 訂閱更新。
- **後開**:公開投稿+信譽榜——空市場掛榜=鬼城,等 self-hosted 用戶密度夠再開(社群玩法)。
- **零代價保證**#31「事件是真相、統計是視圖」——pull/submission 事件 day-one append,信譽晚開也能從頭算,先分享不欠投稿任何債。
### 附:獎勵/信譽(確認既有設計,非新增)
- 「貢獻 recipe 得 reputation」=#31 作者信譽經濟已設計(作者頁/聚合信譽/選版複合分/獎勵層預留、author 綁認證身份);事件 day-one append 保證信譽可事後計算。
- 本提案只補一句:**bundle(App)作者同樣累積信譽**——組裝 App 與寫 recipe 同屬貢獻。
## 影響分析
- **不建表**:全部走 entry_typemetadata_jsonD6 鐵律);`artifact_subscription`/落籍 metadata 皆 template/slot 層。
- **與 Phase 1 關係**:不改既有 1.11.6 任務;本提案為 Phase 1.5bundle/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] 兩個真缺口要進 SDD2026-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.jsonexport 檔同形狀(graph 預編,已上 prod)。
- **不動的牆**KBDB 零接觸(export 讀 WEBHOOKS KV workflow recordimport 打既有
POST /webhooks/named);「部署≠發現」(import 不驗零件存在,執行時現形);
description 必填閘照舊。
- **影響分析**:新公開端點 1 個(租戶 key 認證,吐的是該租戶自己部署的 workflow 定義;
無跨租戶讀);CLI 新命令 2 個(薄殼,能力全在既有 API);無 schema migration、無新金鑰面。
- **狀態**:⏳ 待 leo confirm 後 3.9b 接線。
## 提案:Arcrun App ↔ Portal 掛載協定 v02026-08-09Leo/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-259tab :510-516VIEWS :718allowedViews :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 Schemarepo 目前無「可安裝單位」schema,這是第一份)。
- **不動的牆**KBDB 零 SQL、永不加表;App 前端不進 `arcrun-rag-ui` bundle、不進
`bundle-components.mjs`(否則回到「改核心才能加能力」);App 拿不到 session token
與租戶字串,資料一律走既有 `/portal/data/*` 的 owner_idlibrary 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 originES module)還是 iframe
——短期 App 是否都由我們自己寫?② v0 只開 `nav` 一個掛載點夠不夠?
- **狀態**:⏳ 待 confirm。沒 confirm 前照現行 SDD 走,不開新 SDD、不動功能程式碼。