Files
Arcrun/system-dev/docs/3-specs/workflow-discovery/design.md
T
uncle6me-web 7bd7b4b26a SDD 生命週期鐵律遷移:單一活性制度上線(portal-auth=active,其餘 paused/draft)
- 鋪檔(自 system-dev-template v1.15.0):SDD-LIFECYCLE.md+pending-changes.md
  +sdd-guard.sh(覆蓋舊版,加單一活性檢查)+sdd-check.md+sdd-active-check.sh
- settings.json PreToolUse(Write|Edit|MultiEdit)掛上 sdd-guard.sh
- 全部 SDD design.md 掛 frontmatter:portal-auth=active(現行 portal 線,
  #61 demo 四件套剛 merge);artifact-sharing=draft(零任務動工);
  其餘 16 份=paused(皆有未完成任務,無明顯死件,不硬 close)
- CLAUDE.md 加「SDD 生命週期鐵律」段(指向 SDD-LIFECYCLE.md+濃縮五條)
  +SDD 速查表改以 frontmatter status: active 為現行判準
- 驗證:sdd-active-check exit 0(恰 1 份 active);guard pipe-test code 檔
  exit 0 帶現行 SDD 提示;反向測試(造 2 份 active)guard exit 2/check exit 1 全擋

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 17:03:16 +08:00

14 KiB
Raw Blame History

status, superseded_by
status superseded_by
paused

workflow-discovery — Design

狀態:草案,待確認。對應 requirements.md建立2026-06-27issue #8


1. 設計總綱

三件事,全部把能力落在 APIcypher-executor + KBDB),CLI/MCP 只暴露:

  1. 強制 description:部署端點驗證 description 非空 → 否則 422/400 擋下(兩條路徑共用同一驗證)。
  2. 可搜的 metadataworkflow metadata 同時寫進一個 embeddable entry(吃 issue #7 的 /entries/search 語意 + 降級)。
  3. search_workflow 工具:薄殼呼叫 cypher 的 workflow 搜尋端點,後者轉發 KBDB /entries/search,限本租戶。

2. 核心決策 Q1metadata 存哪(record vs entry vs 雙寫)

方案 做法
A. records 表加 q 搜尋 /records/searchq= LIKE 掃 slots 改動小 records 吃不到 #7 的 Vectorize 語意;要另接一套語意=重造 #7 已有的輪子
B. 改存 entry(取代 record workflow metadata 改寫進 entriesentry_type=workflow),description 進可 embed 欄位 直接吃 #7 語意 + 降級閉環,零重造 既有 u6u_list_workflows 讀 record 要改;migration 成本
C. 雙寫(record + entry 部署時同時寫 workflow_metadata record(保既有 list/get 不動) 一個 entry_type=workflow 的 embeddable entry(給搜尋) list/get 零回歸;搜尋吃 #7 語意;兩者解耦各司其職 一份資料兩處;要保證一致(部署原子性)

推薦:C(雙寫),理由:

  • 不回歸u6u_list_workflows / u6u_get_workflow 繼續讀 record,完全不動(降低框架級風險,守 C1)。
  • 吃現成語意:搜尋走 entry → 直接複用 #7 的 /entries/search?mode=semantic + 降級 + capability_hint零重造
  • 解耦record=「我有哪些 workflow」(精確列舉,list 場景);entry=「找做某事的 workflow」(語意檢索,search 場景)。職責不同、欄位不同,本就該分。
  • 一致性風險可控:雙寫在同一個部署 API handler 內,entry 寫失敗不阻塞部署但回 warningfire-and-forget + 可補建),對齊 #7 embedOnWritewaitUntil 非阻塞慣例。

被否的 Arecords 加 q 只能 LIKE,要語意還是得自己接 Vectorize=把 #7 在 entries 做好的事在 records 再做一遍。違反「不重造輪子」。 被否的 B:取代 record 會逼 list/get 一起改,框架級風險放大,不值得。


3. R1:強制 description(落 API

3.1 統一驗證點

⚠️ 實作期發現(2026-06-27,已讀 codePOST /workflows/deploy 在 cypher-executor 根本不存在route 清單只有 /workflows/:name/executions/workflows/resume)。MCP u6u_deploy_workflow.ts:22http://cypher-executor/workflows/deploy目前必 404。意味 MCP 部署路徑現狀是壞的 / 從未端到端驗過。這影響「兩條路徑」的定義,見 §3.1a 待決。

description 強制落在 cypher-executor 的部署端點。實際部署端點有三個:

  • POST /webhookstoken 式匿名):description optional。
  • POST /webhooks/namedCLI acr push,具名):description optional → 改必填
  • POST /workflows/deployMCP 目標):不存在(404

3.1a 待決:MCP 部署路徑怎麼收斂(新發現,需拍板)

兩個方向:

  • 方向 ① MCP 改打 /webhooks/named(推薦):MCP deploy 不再打死端點,改打 CLI 同一條 /webhooks/named(傳 name + graph + description)。→ CLI/MCP 真正共用一條部署端點(rule 07 薄殼一致的最強形態),死端點順手清掉,description 強制只需做在 /webhooks/named 一處。
  • 方向 ② 新建 /workflows/deploy:補上這條 route 給 MCP 專用。→ 但會變成「兩條部署端點各做一次 description 強制」,且與 /webhooks/named 職責重疊(都是部署具名 workflow),違反「能力只實作一次」。

leo 拍板(2026-06-27 issue #8):方向① 。CLI/MCP 共用一條部署端點=rule 07 最強形態;list 改讀 GET /webhooks/named(現成端點,回歸可控)。

強制規則:缺/空白 trim 後為空 → 400 { error: '<§3.2 定位訊息>', requires: 'description' }。驗證是 API 行為(R1.3),薄殼不判空(守 rule 07)。

3.1b 方向①實作期再發現:YAML→部署編排被寫在 CLI 介面層(藏的薄殼債)

讀 code 發現「MCP 改打 /webhooks/named」不是換 URL 就好——/webhooks/named 吃的是 graph 物件,不是 YAML。把 YAML 變 graph 的編排目前在 CLI push.ts 介面層做了一整段:

  1. loadWorkflowYaml + parseTriplets(解析 YAML flow → triplets
  2. POST /cypher/searchtriplets → 執行圖 graph
  3. config 套節點、組 {id, name, nodes, edges} graph
  4. POST /webhooks/named(傳 graph

而 MCP u6u_deploy_workflowyaml_content 字串、期待一個吃 YAML 的 server 端點(打 /workflows/deployContent-Type: application/yaml)——這端點從來不存在(故 404)。

張力:方向① 若讓 MCP 在介面層複製 CLI 那 4 步編排 → 兩個薄殼各做一次 YAML→graph 編排 = 違 rule 07「能力只實作一次」(正是薄殼原則要防的)。

收斂選項(待總管定,§3.1c

  • ①-a 最小修MCP 介面層複製 CLI 的 YAML→graph 步驟,打 /webhooks/named。→ 快,但複製編排=技術債(與方向①初衷「薄殼統一」自相矛盾)。
  • ①-b 編排下沉(正解) :新增 API 端點 POST /workflows/deploy(這次真的建,但吃 YAML、職責是「YAML→graph→部署」完整編排),CLI 和 MCP 都只傳 YAML。→ 真正一處編排,兩薄殼都瘦。但要把 CLI push.ts 的編排搬進 API(較大重構,且碰 exposure_consent 流程)。
  • ①-c 折中:先 ①-a 讓 MCP 通(解 404、issue #8 範圍內),①-b 編排下沉另開 issue(薄殼債獨立追)。

註:①-b 的 /workflows/deploy 與被否的「方向②」不同——②是「為 MCP 另開一條重複端點」,①-b 是「把編排下沉成唯一真相端點,CLI 也改用它」。前者增重複,後者消重複。

3.2 description 來源(Q2 定案:操盤 CC 據實生成、用戶可改)

leo 翻案,但守住誠實精神。目標用戶 low-code,不知道要填 description;強迫填一個不懂的欄位=違北極星「不增加用戶負擔」。調和關鍵=分清兩種「生成」:

  • 操盤的 CC 據實生成CC 剛幫用戶建這工作流,最懂它串了什麼、做什麼 → 由 CC 寫一句真實的「這做什麼」(leo 例:「呼叫可以 Upsert Google Sheets」)。這是真描述非假裝,完全不違 mindset §7。
  • 介面層機械塞佔位(從 name 複製、塞 workflow_xxx 預設)=假描述,仍禁(§3.2 原本防的就是這個,保留)。

落地規則

  • YAML description: 為主CC 寫 workflow YAML 時就據實填好 description(既有慣例,registry/examples 已示範)。
  • description 強制非空仍落 APIR1 不變,§3.1 驗證點不動)。
  • 部署若收到空 description不是擋下逼用戶手填,而是回明確訊息要求操盤 CC 據實補一句再部署(CC 是 description 的生成者,用戶可改)。錯誤訊息對齊此定位:「description 必填:請操盤的 AI 據實寫一句『這工作流能做什麼』(如「呼叫可 Upsert Google Sheets」),用戶可再改。」
  • 內容定位:一句「這工作流能做什麼」,不是寫文章。供語意搜尋命中,要點是「做什麼」可被自然語言意圖匹配。
  • 仍禁:介面層(CLI/MCP TS)機械從 name 生成佔位(守 rule 07 + 誠實,hook 7.x 範圍)。「據實生成」是操盤 CC 在寫 YAML 當下做的,不是介面層 deploy 時自動補。

4. R2:可搜的 entry + search_workflow

4.1 部署時寫 embeddable entry(雙寫的 entry 那半)

部署成功後(record 寫完),cypher 端再寫一個 entry 到 KBDB

POST /kbdb/entries   (經 cypher proxy,租戶隔離注 owner_id
{
  entry_type: "workflow",
  page_name: <workflow name>,
  content: <description>,                    // 被 embed 的主體
  metadata: {
    embed: true,                             // #7 精耕開關:只有標 true 才進 Vectorize
    workflow_id, name, deployed_at
  }
}
  • metadata.embed:true → 命中 #7 的 embedOnWrite 精耕條件,description 進 Vectorize(若模組開)。
  • 模組沒開 → entry 照樣存(D1),語意搜降級成 LIKE 仍能命中 content/name(不假綠)。
  • owner_id 由 cypher proxy 強制注入本租戶(沿用既有隔離,C4/R2.4)。

4.2 search_workflow 工具(薄殼)

新 MCP 工具 search_workflow(query),形態抄 u6u_search_components

search_workflow(query) →
  cypher GET /workflows/search?q=<query>&mode=semantic
    → KBDB /entries/search?q=&owner_id=&source=&mode=semantic&entry_type=workflow
      → mode=semantic 開:Vectorize 語意命中
      → 沒開:降級 keyword(LIKE) + capability_hint「叫 CC 幫你開語義查詢」
  • cypher 加一條 GET /workflows/search(薄轉發到 KBDB /entries/search,限 entry_type=workflow + 本租戶 owner_id)。
  • MCP 工具把結果格式化(含 capability_hint 透傳給 AIAI 看到就能主動問用戶開 Vectorize → R2.3)。
  • 命名:MCP 既有工具是 u6u_* 前綴(u6u_search_components)。本工具命名 u6u_search_workflows(複數對齊 u6u_list_workflows),對外描述「用自然語言找現成工作流」。

4.3 Vectorize 開啟(R2.3,不新造)

完全沿用 #7acr init 問 / kbdb_embed:true + acr update → deploy 建 Vectorize index。AI 從 search 回傳的 capability_hint 得知未開 → 主動問用戶要不要開。本 SDD 不碰 Vectorize 開關機制,只消費它。


5. R3:既有工作流回填

推薦:下次部署強制 + 一次性 backfill 端點(提示式,不自動跑)

  • 下次部署強制R1 生效後,任何 re-deploy 都會被逼填 description → 自然回填(隨用隨補)。
  • 一次性 backfill:加 POST /workflows/backfill-search-entries(限本租戶),把現有 workflow_metadata records 中已有 description 的補寫成 entry(讓它們可搜);無 description 的列出來回報「這些需要 re-deploy 補 description」,自動編造描述(誠實)。
  • backfill 是 AI/人主動呼叫CLI/MCP 暴露),不掛 cron/輪詢C2 flag 紅線)。
  • 不破壞執行:entry 只供搜尋,workflow trigger 仍讀 WEBHOOKS KV / record,互不影響(R3.2)。

6. 薄殼一致性(rule 07

能力 API(真相) MCP 薄殼 CLI 薄殼
強制 description cypher 部署端點驗證 deploy 工具透傳錯誤) push 透傳錯誤)
搜尋 workflow cypher /workflows/search → KBDB /entries/search u6u_search_workflows(本 SDD acr workflow searchR2.5,次階段,對稱補)
backfill cypher /workflows/backfill-search-entries 可選暴露 可選暴露

齊的單位是「能力」不是「端點」(decisions-summary 已釐清):search 能力 MCP 先到位(AI 先用),CLI 對稱補可列次階段,不阻塞。


7. flag 安全自檢(C2

  • search_workflowAI 收到自然語言意圖時主動 call 一次,無排程、無 webhook 觸發、無跨 repo fan-out。
  • backfill=人/AI 主動呼叫一次,非 cron。
  • 雙寫的 entry embed 走 waitUntil 非阻塞,是單次部署內的副作用,非輪詢。

8. 影響面(框架級,C1

  • 改 cypher-executor/webhooks/named + /workflows/deploy 加 description 強制;新增 /workflows/search + /workflows/backfill-search-entries;部署 handler 加雙寫 entry。
  • 改 MCPu6u_deploy_workflow 抓並傳 description;新增 u6u_search_workflows
  • 改 KBDB:可能不用改(複用 #7 /entries/search + entry_type=workflow 過濾;確認 /entries/search 支援 entry_type filter,若無則補一個 filter 參數——base 通用 filter,不寫死 workflow)。
  • self-hosted 影響description 強制對所有用戶生效 → 回填策略(R3)讓既有資產平滑過渡,不一刀斷。
  • 不改workflow 執行/trigger 路徑、Vectorize 開關機制(複用 #7)、零件搜尋。

9. 拍板結果(leo 2026-06-27 issue #84 點全定)

  1. Q1 方案 C(雙寫) — record 保 list/get 零回歸 + entry_type=workflow embeddable entry 吃 #7 語意,waitUntil 非阻塞。
  2. Q2 description「操盤 CC 據實生成、用戶可改」leo 翻案) — 非「介面層不生成」也非「逼用戶手填」。強制非空仍落 API;空時要求操盤 CC 據實補一句(非介面層機械塞佔位,仍禁佔位)。定位=一句「能做什麼」非文章。詳見 §3.2。
  3. Q3 回填「下次部署強制 + 提示式 backfill」 — 無 desc 列出待 re-deploy、不自動編造、不掛 cron。
  4. Q4 補 base 通用 entry_type filter — 總管查證:/entries/search 目前不支援 entry_type 過濾(只有 q/owner_id/source/mode),但 schema 有 entry_type 欄位 + 索引(0001_base.sql:16-20)。要改 4 處routeentries.ts:43-77/ searchEntries / semanticSearchembed.ts/ kbdb-proxy做成 base 通用 filter 不寫死 workflow

狀態:4 點拍定,已點頭實作。 按 tasks.md Phase 1→6 推進,leo21c 端到端綠燈為完成標準。