7777414b70
規劃「資料庫就能完成的開發物」(recipe/workflow/template)的分享/發布/下載/導入/匯出。 泛化一套 /public-artifacts 讀端點 + 型別化 materialize adapter 寫;公庫存 KBDB; 依賴解析劃清界線(recipe/template co-pull、component 走 PR、credential 零傳輸)。 純文件,不改程式。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
208 lines
18 KiB
Markdown
208 lines
18 KiB
Markdown
# 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)
|
||
- 零件安裝(PR 路徑,另案);credential 分享;跨環境市場聚合層;recipe 從 KV 遷 KBDB;SaaS/api-key。(詳 requirements Out of Scope。)
|
||
|
||
---
|
||
|
||
## 設計
|
||
|
||
### 架構概覽
|
||
|
||
```
|
||
┌──────────────── 公庫(KBDB,長效資料)────────────────┐
|
||
發布 submit-p │ public_artifact entry(entry_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 KV │ │ 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 公庫儲存後端** | **workflow/template 公庫建在 KBDB**(`entry_type=public_artifact`),**recipe 維持既有 KV 公庫**、泛化端點 front 兩者 | 對齊儲存鐵律(長效→KBDB)+白拿 KBDB 語意搜尋(Vectorize embed,workflow-discovery 已證);recipe KV 公庫已上線穩定,重寫=在可用機制上重投資(mistakes 教訓) | ❌ 全部塞 KV(違鐵律、無語意搜尋);❌ 逼 recipe 立刻遷 KBDB(負 ROI,列未來選項) |
|
||
| **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 一起 pull;component 只偵測引導、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,不併真實計數,避免污染市場)。
|
||
|
||
### 可攜格式(export,R2 / 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 相容**:泛化端點內部對 `type=recipe` 轉呼既有 KV 公庫路徑(`listAllRecipes`/`installRecipeRecord`),對 `type=workflow|template` 走 KBDB `public_artifact` 路徑。對外一套 API,對內兩後端(K2)。可先只上 workflow/template,recipe 沿用既有 `/public-recipes`(見 tasks 分期)。
|
||
|
||
### 導入語意 + materialize adapter(R4)
|
||
|
||
`pull(type, canonical_id)` =
|
||
1. `GET /public-artifacts/:type/:canonical_id` 取全文(含 market 最佳版本 + dependency_manifest)。
|
||
2. **先解依賴**(見下),再落地。
|
||
3. **materialize(型別化)**:
|
||
- `workflow` → `POST /webhooks/named`(既有 push;owner_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`…) | **只偵測不安裝**:對照本部署 `acr parts`(component registry)。齊→放行;缺→回報「此 workflow 需零件 X,你的部署沒有。零件走 PR→併 main→部署安裝(`arcrun-components` repo),非 pull」。用 §7.5.6 式引導,**絕不自動抓程式** | 零件是程式=攻擊面,鎖 PR 閘(#23、component-gatekeeping)。這是本 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 教訓**:`publish_component` 零人閘(提交即 active/public + MCP 全域)=攻擊面。結構解已定:**廢 MCP 發布路徑、零件投稿唯一入口=PR**(`arcrun-components`)。
|
||
- **本 SDD 的分界**:資料 artifact(recipe/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 / 誘導填入釣魚 credential(import 端自己 `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。
|
||
|
||
### 資料模型
|
||
|
||
- 公庫 record:KBDB `public_artifact` entry(見上)。**不建新表**(entry_type + metadata_json 承載,KBDB 鐵律)。
|
||
- market_stat:復用既有 KBDB recipe-stats(key=uuid,型別無關)。
|
||
- 存證:`artifact_submission` entry。
|
||
- 私庫落地:沿用各型既有儲存(workflow=WEBHOOKS KV+KBDB 雙寫、template=KBDB /templates、recipe=RECIPES KV),**不改**。
|
||
|
||
---
|
||
|
||
## 技術限制
|
||
|
||
- **不能**:新建 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-Wall(D6)、UUID 身份模型(§7.5.5)。
|
||
- **效能**:公庫搜尋走 KBDB 語意/keyword(Vectorize 已在 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_manifest,grep 不到任何機密值。
|
||
- [ ] template 同上一套(submit-p/search/pull/export)跑通。
|
||
- [ ] 投稿寫 `artifact_submission` 存證 entry;自報 stat 不進真實計數。
|
||
- [ ] 一個引用「未具備零件」的 workflow,pull 時停在引導(不落地、不抓程式)。
|
||
|
||
---
|
||
|
||
## 相關文件
|
||
|
||
- `arcrun/kbdb-base/design.md` §7.5(recipe app-store / 公私庫雙向 / UUID 身份)— 本 SDD 的母範本。
|
||
- `component-gatekeeping/`+`arcrun-components` repo — 零件走 PR 的界線另一側。
|
||
- `workflow-discovery/` — workflow 已雙寫 KBDB + 語意搜尋,本 SDD 的公庫搜尋基礎。
|
||
- `data-exfil-warning/` — import 時外呼信任檢查層。
|
||
- `4-guides/arcrun-primer.md`(InkStoneCo 頂層)— A/B 兩類工作、紅線。
|
||
- Arcrun#23(publish_component 零人閘教訓)、Arcrun#13(workflow-store / 移除 consent 閘)。
|
||
|
||
---
|
||
|
||
## 待 leo / richblack 拍板的設計點
|
||
|
||
1. **K2**:recipe 公庫是否收斂到 KBDB(與 workflow/template 同後端),或永久維持 KV 雙後端?本 SDD 預設「不動 recipe、泛化端點 front 兩後端」,收斂列未來選項。
|
||
2. **export 預設格式**:YAML(對齊 workflow.yaml)vs 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 摘要依賴清單給人看一眼」的軟提示(非硬閘)。
|