Files
Arcrun/system-dev/docs/3-specs/arcrun/artifact-sharing/design.md
T
Leo 1d7f69215a docs(spec): artifact-sharing 審查對齊 ×2 — K2 改 KBDB 唯一公庫後端(對齊 #16)+ 零件界線照 #23/D29
審查意見兩處(總管 2026-07-07):
1. K2 原「recipe 維持 KV 公庫、泛化端點 front 兩後端」與 #16(RECIPES KV→KBDB)衝突
   → 改為 KBDB 是唯一公庫後端;#16 落地前對 type=recipe 的 KV 轉接明定為過渡碼、
   #16 完成即拆(1.5/5.2 對應改);KV 公庫不得寫成穩態。遷移本體=#16 負責,不重工。
2. component 界線照 #23/D29 定案改:零件貢獻整層搬到獨立 repo Leo/arcrun-components
   (fork→PR→人審);市場只偵測缺件→引導去該 repo 開 PR,永不傳輸/安裝零件程式;
   arcrun_publish_component 路徑廢除。design/requirements/tasks 凡提零件發布處照此改。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015d5jDbuqT5Htwv3Q88XXKk
2026-07-07 09:09:37 +00:00

208 lines
19 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.
# Artifact Sharing — Design
> 狀態:草稿(待 review
> 建立:2026-07-07 | 最後更新:2026-07-07
> 負責人:Arcrun subagent(受 InkStoneCo 總管指派)
---
## 一句話說明
把 recipe 的 app-store 公庫模型,泛化成「一套公庫 record 模型 + 一組泛化端點 + 三個型別化 materialize adapter」,讓 recipe / workflow / template 都能發布、搜尋、pull、export,並在 pull 時解析依賴。
---
## 背景與問題
見 requirements.md。核心:recipe 已有 `/public-recipes` + `submit` + `installRecipeRecord` 的完整範本(`cypher-executor/src/routes/recipes.ts`),workflow / template 缺這一整套。要複製它的**公私庫分界、UUID 身份、canonical_id/author、市場數據、pull=寫進自己私庫**這幾個成熟決策,而非重新發明。
---
## 範圍
### 包含(In Scope
- 統一的公庫 record 資料模型(三型共用身份/市場欄位)。
- 三型 artifact 的可攜格式(export)+依賴清單。
- 公庫端點:泛化的 `/public-artifacts` 一套打通三型(見「關鍵決策 K1」的取捨與結論)。
- 導入語意(pull)與型別化 materialize adapter。
- 依賴解析(recipe/template 共 pull、component 引導 PR、credential 引導填值)。
- MCP tools / CLI 命令三層對齊。
- 安全與審核設計(app-store 市場信任 vs 零件 PR 閘的分界)。
### 不包含(Out of Scope
- 零件安裝(`Leo/arcrun-components` fork→PR→人審路徑,另案);credential 分享;跨環境市場聚合層;recipe 從 KV 遷 KBDB 的**執行**#16 負責;本 SDD 只以「KBDB 唯一公庫後端」為前提設計);SaaS/api-key。(詳 requirements Out of Scope。)
---
## 設計
### 架構概覽
```
┌──────────────── 公庫(KBDB,長效資料)────────────────┐
發布 submit-p │ public_artifact entryentry_type=public_artifact │ 搜尋 / pull
────────────► │ { uuid, type, canonical_id, author, portable_body, │ ◄────────────
│ dependency_manifest, market_stat(per-uuid) } │
└───────────────────────────────────────────────────────┘
▲ │
(export 產可攜檔) │ │ pull = 取全文
│ ▼
┌──────────────────────┴───────┐ ┌─────────────────────────────────────┐
│ 自己 namespace(私庫) │ │ materialize adapter(型別化落地) │
│ workflow → WEBHOOKS KV+KBDB │◄───────│ workflow → push (/webhooks/named) │
│ template → KBDB /templates │ │ template → create_template │
│ recipe → RECIPES(→KBDB#16) │ │ recipe → installRecipeRecord │
└──────────────────────────────┘ │ + 依賴解析(遞迴 pull / 引導) │
└─────────────────────────────────────┘
```
- **讀寫都走既有 worker**:公庫 record 存 KBDB`kbdb` worker 的 `/entries`),泛化端點掛在 **cypher-executor**(已是 recipe 公庫 + workflow CRUD 的所在,且已有 KBDB fetch 慣例)。**不新建 worker、不 service binding、不 wrangler**arcrun-primer A 類)。
### 關鍵決策
| 決策 | 選擇 | 原因 | 放棄的選項 |
|------|------|------|----------|
| **K1 端點:泛化一套 vs 每型別一套** | **泛化一套 `/public-artifacts?type=` 讀,型別化 adapter 寫** | 讀側(搜/列/取全文/market_stat/落空引導)三型邏輯完全同構,泛化成一套省 3×重複、給 AI 一致心智模型(low-code);寫側(materialize 落自己 namespace)三型的家不同,必須型別化 | ❌ 每型一套 `/public-workflows`+`/public-templates`(3× 複製 recipe 端點,維護面爆、AI 要記三套);❌ 全泛化含寫(做不到,落地各異) |
| **K2 公庫儲存後端** | **KBDB 是唯一公庫後端**`entry_type=public_artifact`)。workflow/template 一開始就建在 KBDBrecipe 公庫隨 **#16RECIPES KV→KBDB 遷移)**收斂進同一後端。過渡期(#16 落地前)泛化端點可暫時把 `type=recipe` 轉呼既有 KV 路徑,但 **KV 公庫=過渡態、非穩態**#16 完成即拆轉接 | 對齊儲存鐵律(長效→KBDB)+白拿 KBDB 語意搜尋(Vectorize embedworkflow-discovery 已證);與 #16 同向不打架——遷移本體由 #16 負責,本 SDD 不對 KV 公庫做任何新投資 | ❌ 全部塞 KV(違鐵律、無語意搜尋);❌ 把「KV 公庫 front 兩後端」寫成穩態(與 #16 衝突,雙後端維護面永久化);❌ 本 SDD 自己動手遷 recipe(與 #16 重工) |
| **K3 身份模型** | **沿用 recipe UUID 身份模型**uuid=身份、canonical_id/author/type=屬性、submit-p=新增作者版本不覆蓋、per-uuid market_stat | 已被 recipe 驗證(kbdb-base §7.5.5);三型共用同一套,pull「選市場最佳作者版本」邏輯可共享 | ❌ 為 workflow/template 另設身份模型(分裂、無法共用選版邏輯) |
| **K4 依賴傳輸邊界** | **recipe/template 隨 workflow 一起 pullcomponent 只偵測引導、credential 只列清單** | component 是程式=攻擊面,鎖 PR 路徑(#23);credential 是機密,永不可攜(鐵律) | ❌ export 內嵌 component 程式(把 PR 閘繞掉,重演 #23);❌ export 內嵌 credential 值(機密外洩) |
| **K5 審核閘** | **資料 artifact 無 submit 前置人閘**(市場信任),**信任判斷點移到 import 時**(市場數據+依賴清單+外呼檢查) | leo 2026-06-29 已對 recipe 移除 exposure_consent 閘(arcrun 給 AI 用);資料 artifact 不在 host 上跑任意碼,風險≠零件 | ❌ 對 submit 加人類 consent 閘(與 recipe 已定案反向、增摩擦);❌ 完全無信任訊號(import 端沒判斷依據) |
### 統一公庫 record 模型(R1
公庫一筆 = KBDB entry`entry_type='public_artifact'``owner_id=author`(歸屬即隔離),`metadata_json` 承載身份與市場鍵,`content` 放 canonical_id(供 keyword/semantic search)+ `metadata_json.embed=true` 開語意搜尋:
```jsonc
// public_artifact entry.metadata_json
{
"uuid": "…", // 唯一身份(誕生即領,沿用 §7.5.5)
"type": "workflow", // recipe | workflow | template
"canonical_id": "daily_digest",
"author": "leo", // 該 uuid 投稿者;'system'=種子
"derived_from": "…uuid?", // 可選:fork 溯源
"display_name": "每日摘要",
"description": "…", // 供語意搜尋,強制非空(沿用 workflow-discovery R1
"embed": true, // 開 Vectorize
"portable_body": { }, // 見「可攜格式」
"dependency_manifest": [ ] // 見「依賴解析」
// market_stat 不存這裡——per-uuid 記在既有 recipe-stats 機制(KBDB),import 時 fetch
}
```
> **市場數據**:沿用 recipe `fetchMarketStat`KBDB `/recipe-stats/:id`per-uuid)。泛化為 `/artifact-stats/:uuid`(或直接復用 recipe-stats 表,key=uuid 本就型別無關)。**不在投稿時寫自報數**(沿用 §7.3:自報數只當存證 `*_submission` entry,不併真實計數,避免污染市場)。
### 可攜格式(exportR2 / US-3
每型 `portable_body` = 該型的自足定義,**去識別化 + 去機密**:
- **recipe**:既有 RecipeDefinition 去掉 `uuid/author/created_at`(導入時重領),保留 endpoint/method/headers/body/auth_service/`credentials_required`(只有 key 名,無值)。
- **workflow**`{ name, flow, config, description }`(cypher 三元組 + 節點設定)。`config` 內對零件的引用(`component: rec_xxx` / `component: <builtin>`)保留;credential 引用維持 `{{credential.xxx}}` 模板(無值)。
- **template**`{ name, slots[], description }`
export 檔(YAML/JSON,二選一,預設 YAML 對齊 workflow.yaml 慣例)= `portable_body` + `dependency_manifest` + `type`/`canonical_id` 標頭。自足=任何相同 Arcrun 部署 import 後可跑(前提:component 已具備、credential 已填)。
### 泛化端點(R3,K1 讀側泛化)
掛在 cypher-executor`type` 為必帶 query/param。行為對照 recipe `/public-recipes*` 完全同構:
- `GET /public-artifacts?type=&q=&limit=&offset=` — 搜/列公庫。同 canonical_id 回多筆(多作者),各附 `market_stat`。落空(q 無命中)→ `{ found:false, type, query, hint }` 創作引導(沿用 §7.5.6)。`type` 省略=跨型別搜(回傳項帶各自 type)。
- `GET /public-artifacts/:type/:canonical_id?author=` — 取單一全文(pull 用)。多作者→選 `market_stat.success_count` 最高版本(沿用 recipe 選版邏輯)。落空→創作引導。
- `POST /public-artifacts/submit` — 投稿(submit-p)。body 帶 `{ type, canonical_id, portable_body, dependency_manifest, author, stat? }`。**領新 uuid = 新增作者版本**(不覆蓋,§7.5.5);`stat` 只寫 `*_submission` 存證 entry,不併真實計數。
> **recipe 相容(過渡)**:終態=三型都走 KBDB `public_artifact` 路徑(K2KBDB 唯一公庫後端)。#16RECIPES KV→KBDB)落地前的過渡期,泛化端點對 `type=recipe` 暫時轉呼既有 KV 公庫路徑(`listAllRecipes`/`installRecipeRecord`)——這段轉接碼是**過渡碼**#16 完成後 `type=recipe` 改走 KBDB、轉接即拆,不留雙後端。可先只上 workflow/templaterecipe 沿用既有 `/public-recipes`(見 tasks 分期)。
### 導入語意 + materialize adapterR4
`pull(type, canonical_id)` =
1. `GET /public-artifacts/:type/:canonical_id` 取全文(含 market 最佳版本 + dependency_manifest)。
2. **先解依賴**(見下),再落地。
3. **materialize(型別化)**
- `workflow``POST /webhooks/named`(既有 pushowner_id=自己 api_key/namespace)。
- `template``POST /templates`(既有 create_template)。
- `recipe``installRecipeRecord`(既有)。
4. 回報:落地的 uuid + 依賴處理結果(已 pull 的 recipe/template、缺的 component、待填 credential)。
### 依賴解析(R5 / K4)—— 關鍵設計點
一個 workflow 常引用四類東西。import 時**分類處理,各走各的邊界**:
| 依賴類 | 來源(怎麼認) | 導入處理 | 為何這樣 |
|---|---|---|---|
| **recipe** | workflow `config``component: rec_xxx` / http_request 引用的 canonical_id | **遞迴 co-pull**:對每個 recipe canonical_id 走 `pull('recipe', …)` 寫進自己私庫。有界遞迴(recipe 通常不再引用其他 artifact;設深度上限防環) | recipe 是資料,同屬「資料庫就能完成」,一起帶走才可跑 |
| **template** | workflow 若寫 KBDB record(引用某 template name | **co-pull**template 不存在→`pull('template', …)`→create_template | 同上;template 是 slots 宣告,可攜 |
| **component(零件/code** | workflow `flow:` cypher 用到的 builtin 零件(`http_request`/`code`/`cron`…) | **只偵測缺口→引導開 PR,永不傳輸/安裝零件程式**:對照本部署 `acr parts`。齊→放行;缺→回報「此 workflow 需零件 X,你的部署沒有。零件貢獻已整層搬到獨立 repo **`Leo/arcrun-components`**fork→PR→人審→併 main→隨部署/更新安裝),非 pull」。用 §7.5.6 式引導 | 零件是程式=攻擊面,唯一入口=`Leo/arcrun-components` 的 PR 人審閘(#23/D29 定案;`arcrun_publish_component` 路徑已廢除)。這是本 SDD 與零件案的**界線落點** |
| **credential** | recipe 的 `credentials_required[].key` / workflow `{{credential.xxx}}` | **只列清單引導**:彙總所有需要的 key,回報「請 `acr creds push` 填:notion_token, …」。**值永不隨 artifact 傳輸** | 機密鐵律:值存 CF Worker Secrets,可攜檔只帶 key 名 |
- **dependency_manifest** 在 **export/submit 時就算好並存進 record**(掃 portable_body 抽引用),import 端不必反解 workflow 內部結構就能預覽「這東西會拉進哪些 recipe/template、需要哪些零件與 credential」——也是 import 的**信任預覽面**K5)。
- **遞迴有界**manifest 逐項 pull,維護 visited set 防環 + 深度上限(建議 5),超限回報中止並列未解項。
### 安全與審核(R7 / K5)—— 對照 Arcrun#23 教訓
- **#23 教訓 → #23/D29 定案**`publish_component` 零人閘(提交即 active/public + MCP 全域)=攻擊面。結構解已定案:**`arcrun_publish_component` 路徑廢除**;**零件貢獻整層搬到獨立 repo `Leo/arcrun-components`,唯一入口=fork→PR→人審**。市場/artifact 分享機制只做「缺件偵測→引導去該 repo 開 PR」,**永不傳輸、永不安裝零件程式**。
- **本 SDD 的分界**:資料 artifactrecipe/workflow/template**不是**零件那種攻擊面——它們是宣告式資料,pull 進來只會**執行預先審過的零件**、只會打 recipe 指定的 endpoint、只會用 template 的 slots。故:
1. **submit 無前置人閘**app-store 市場信任,沿用 leo 2026-06-29 對 recipe 移除 consent 的拍板)。壞版本靠市場數據淘汰,不靠事前審。
2. **信任判斷點在 import 時**,用三個既有訊號:① `market_stat`(真實使用成功率,per-uuid);② `dependency_manifest`(import 前預覽「會拉哪些依賴、打哪些 endpoint、要哪些 credential」);③ **`data-exfil-warning` 外呼檢查**(該 SDD 的 API 層:recipe endpoint 域名異常時警示)——pull 進來的 recipe 依賴同樣過這層。
3. **零件閘不鬆動**workflow 帶再多引用,也**不能**讓一個未經 PR 的新零件進入 host。缺零件=停在引導,這道牆就是防「拿 workflow 當殼夾帶惡意零件」。
4. **credential 零傳輸**:機密不可攜,杜絕投稿夾帶他人 token / 誘導填入釣魚 credentialimport 端自己 `acr creds push`)。
- **投稿存證**submit 寫一筆 `artifact_submission` entry(誰、何時、投了什麼 uuid、自報 stat)當**法律歸責軌跡**(沿用 recipe §7.3),不當門檻。
### API / 介面定義(R6 三層對齊)
| 層 | recipe(既有樣板) | 本 SDD 新增(workflow/template,泛化) |
|---|---|---|
| HTTP | `/public-recipes*``/recipes/submit` | `/public-artifacts*``/public-artifacts/submit``type=` 打通三型) |
| MCP | `arcrun_recipe_search/pull/submit_p` | `arcrun_artifact_search/pull/submit_p`(帶 `type` 參數)+ `arcrun_workflow_export` / `arcrun_template_export` |
| CLI | `acr recipe search/pull/submit-p` | `acr workflow search/pull/submit-p/export``acr template search/pull/submit-p/export`(薄殼包泛化端點) |
> MCP/CLI 面向使用者可保留型別化命名(`acr workflow pull` 比 `acr artifact pull --type=workflow` 更順口、更 low-code),**底層打同一泛化端點**。=「對外型別化措辭、對內泛化實作」,兼得可讀性與 DRY。
### 資料模型
- 公庫 recordKBDB `public_artifact` entry(見上),**KBDB 是唯一公庫後端**(K2;recipe 過渡期例外見上)。**不建新表**entry_type + metadata_json 承載,KBDB 鐵律)。
- market_stat:復用既有 KBDB recipe-statskey=uuid,型別無關)。
- 存證:`artifact_submission` entry。
- 私庫落地:沿用各型既有儲存(workflow=WEBHOOKS KV+KBDB 雙寫、template=KBDB /templates、recipe=RECIPES——現為 KV**隨 #16 遷 KBDB**),本 SDD **不改**私庫儲存、materialize adapter 打各型既有寫入 API 故遷移對本 SDD 透明。
---
## 技術限制
- **不能**:新建 Worker、寫 Worker 程式碼、service binding、跑 wrangler、建 D1 新表、SQL 直寫(arcrun-primer 紅線;端點加在既有 cypher-executor / kbdb worker)。
- **必須相容**recipe 既有 `/public-recipes*` 路徑(不破現有 pull/submit);workflow 既有 `/webhooks/named` push 與 workflow-discovery 雙寫;template 既有 `/templates`
- **必須遵守**:儲存鐵律(長效→KBDB、機密→Secrets)、API-as-WallD6)、UUID 身份模型(§7.5.5)。
- **效能**:公庫搜尋走 KBDB 語意/keywordVectorize 已在 workflow entry 證可行),避免 recipe KV `list({prefix})` 全掃在量大時疊加(kbdb-base §風險 已標)——這也是 workflow/template 公庫選 KBDB 而非 KV 的附帶好處。
---
## 驗收標準
完成的定義(CC 完成任何 task 前必須確認):
- [ ] `POST /public-artifacts/submit`type=workflow)投稿後,`GET /public-artifacts?type=workflow&q=` 搜得到、帶 market_stat。
- [ ] `GET /public-artifacts/workflow/:canonical_id` 回市場最佳作者版本;落空回 `{found:false,hint}`
- [ ] `acr workflow pull <id>` 把 workflow push 進自己 namespace,且遞迴 pull 其 recipe/template 依賴、列出缺的 component 與待填 credential。
- [ ] `acr workflow export <name>` 產出自足 YAML,含 dependency_manifestgrep 不到任何機密值。
- [ ] template 同上一套(submit-p/search/pull/export)跑通。
- [ ] 投稿寫 `artifact_submission` 存證 entry;自報 stat 不進真實計數。
- [ ] 一個引用「未具備零件」的 workflow,pull 時停在引導(不落地、不抓程式)。
---
## 相關文件
- `arcrun/kbdb-base/design.md` §7.5recipe app-store / 公私庫雙向 / UUID 身份)— 本 SDD 的母範本。
- `component-gatekeeping/`+獨立 repo **`Leo/arcrun-components`**#23/D29:零件貢獻整層外移,fork→PR→人審)— 零件界線的另一側。
- `workflow-discovery/` — workflow 已雙寫 KBDB + 語意搜尋,本 SDD 的公庫搜尋基礎。
- `data-exfil-warning/` — import 時外呼信任檢查層。
- `4-guides/arcrun-primer.md`InkStoneCo 頂層)— A/B 兩類工作、紅線。
- Arcrun#23publish_component 零人閘教訓)、Arcrun#13workflow-store / 移除 consent 閘)。
---
## 待 leo / richblack 拍板的設計點
1. ~~**K2**recipe 公庫是否收斂到 KBDB~~ **已定(審查 2026-07-07,對齊 #16**KBDB 是唯一公庫後端,recipe 隨 #16 收斂;KV 只是 #16 落地前的過渡轉接,不是穩態。剩餘協調點只有「轉接拆除時機跟 #16 進度對齊」。
2. **export 預設格式**YAML(對齊 workflow.yamlvs JSON。本 SDD 預設 YAML、兩者皆支援。
3. **MCP/CLI 命名**:型別化措辭(`acr workflow pull`vs 泛化措辭(`acr artifact pull --type=`)。本 SDD 建議型別化對外、泛化對內。
4. **依賴遞迴深度上限**:建議 5,是否合適。
5. **K5 信任**:資料 artifact submit 全程無人閘是否可接受於**公開**公庫場景(vs 現況多為 self-hosted 私庫 pull);若未來上真公開公庫,是否要加 import 端的「首次 pull 陌生作者 → AI 摘要依賴清單給人看一眼」的軟提示(非硬閘)。