diff --git a/mcp/src/mcp-handler.ts b/mcp/src/mcp-handler.ts index e4e8a42..63a5bbd 100644 --- a/mcp/src/mcp-handler.ts +++ b/mcp/src/mcp-handler.ts @@ -46,6 +46,9 @@ export async function handleMcpRequest( "(配 `switch` 的每個 case/`try_catch` 的 try·catch)。", "🔴 **需要判斷時用分支邊,不要寫 code 判斷**——查零件的回應會附 `branch_hint`", "(哪些邊型+可照抄範例),照著接即可。", + "🔴 **分支跑完怎麼判斷成功**:看 `verdict`(`arcrun_get_execution_trace` 或", + "`GET /workflows//executions`)。**走 true 路時 false 路的節點不出現=正確行為**,", + "不是失敗——別因為「只有一條路有輸出」就以為壞掉而改寫成 code(2026-08-01 實撞)。", "第一個節點固定是 `input`。", ].join("\n"); diff --git a/mcp/src/tools/arcrun_publish_component.ts b/mcp/src/tools/arcrun_publish_component.ts deleted file mode 100644 index 4332703..0000000 --- a/mcp/src/tools/arcrun_publish_component.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { toolName } from "../brand.js"; -import { z } from "zod"; -import { Env } from "../types.js"; - -/** - * arcrun_publish_component — 提交 TinyGo WASM 零件至 Component Registry - * - * AI 工作流: - * 1. 先呼叫 arcrun_get_component_guide 取得開發指引 - * 2. 依指引用 TinyGo 撰寫零件(stdin/stdout JSON I/O) - * 3. 編譯為 .wasm,base64 編碼後提交 - * 4. Registry 自動執行沙盒驗收(體積、syscall 掃描、Gherkin 測試) - */ -export function registerPublishComponent(server: McpServer, env: Env, orgNamespace: string) { - server.tool( - toolName("publish_component"), - "提交 TinyGo WASM 零件至 Component Registry。需提供 component.contract.yaml 內容與編譯後的 .wasm base64。提交前請先呼叫 arcrun_get_component_guide 取得開發規範。", - { - contract: z.object({ - canonical_id: z.string().describe("零件功能名稱(小寫底線,如 validate_json)"), - display_name: z.string().describe("顯示名稱(可自由命名)"), - category: z.enum(["logic", "api", "ui", "style", "anim"]).describe("零件分類"), - version: z.string().describe("版本(格式 vN,如 v1)"), - wasi_target: z.literal("preview1"), - stability: z.enum(["floating", "stable", "pinned"]).default("floating"), - runtime_compat: z.array(z.string()).describe("相容 runtime,如 [\"cf-workers\",\"wazero\"]"), - constraints: z.object({ - max_size_kb: z.number().default(2048), - max_cold_start_ms: z.number().default(50), - no_network_syscall: z.boolean().default(true), - io_model: z.literal("stdin_stdout_json"), - }), - input_schema: z.record(z.unknown()).describe("JSON Schema"), - output_schema: z.record(z.unknown()).describe("JSON Schema"), - gherkin_tests: z.array(z.object({ - scenario: z.string(), - given: z.string().describe("JSON 字串"), - then_contains: z.string().describe("預期輸出包含的字串"), - })).min(2).describe("至少一個 happy path 和一個 error path"), - description: z.string().optional(), - tags: z.array(z.string()).optional(), - }).describe("component.contract.yaml 內容"), - wasm_base64: z.string().describe("編譯後的 .wasm 檔案 base64 編碼"), - }, - async ({ contract, wasm_base64 }) => { - try { - if (!env.COMPONENT_REGISTRY) { - return { - content: [{ type: "text", text: "Error: COMPONENT_REGISTRY service binding is not configured." }], - isError: true, - }; - } - - const response = await env.COMPONENT_REGISTRY.fetch("http://component-registry/components", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ contract, wasm_base64 }), - }); - - if (!response.ok) { - const errorText = await response.text(); - return { - content: [{ type: "text", text: `Publish failed: ${errorText}` }], - isError: true, - }; - } - - const result = await response.json() as Record; - return { - content: [{ - type: "text", - text: `零件 ${contract.canonical_id} v${contract.version} 提交成功:\n${JSON.stringify(result, null, 2)}`, - }], - }; - } catch (error) { - return { - content: [{ type: "text", text: `Internal Error: ${error instanceof Error ? error.message : String(error)}` }], - isError: true, - }; - } - } - ); -} diff --git a/mcp/src/tools/registry.ts b/mcp/src/tools/registry.ts index 929c775..c48fd47 100644 --- a/mcp/src/tools/registry.ts +++ b/mcp/src/tools/registry.ts @@ -1,7 +1,6 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { Env } from "../types.js"; import { registerSearchComponents } from "./arcrun_search_components.js"; -import { registerPublishComponent } from "./arcrun_publish_component.js"; import { registerSearchWorkflows } from "./arcrun_search_workflows.js"; import { registerListComponents } from "./arcrun_list_components.js"; import { registerGetComponent } from "./arcrun_get_component.js"; @@ -25,10 +24,10 @@ import { registerWhoami } from "./arcrun_whoami.js"; export function registerAllTools(server: McpServer, env: Env, orgNamespace: string, partnerToken: string) { registerSearchComponents(server, env, orgNamespace); // 🔴 2026-07-21 leo 拍板停用:零件走 PR、專業等級;recipe/workflow/app 誰都可以做。 - // 這兩個工具對一般使用者是「誤導危機」——搜不到東西時把人推向「去造零件」, - // 那是最難、最該擋的那條路(總管實測:問 foreach 怎麼用,回傳 TinyGo 寫 WASM 教學)。 - // 想貢獻零件 → 見 Arcrun repo 的 CONTRIBUTING-components.md。 - // registerPublishComponent(server, env, orgNamespace); + // 零件貢獻**只有一條路=PR 人審**(leo 2026-08-01:「已經沒有 publish 了, + // 零件等級一律走 PR,這條路封了」;Arcrun#23 已關)。實作路徑已 git rm + // (arcrun_publish_component.ts),不留死代碼當錯誤環境信號。 + // 想貢獻零件 → repo `Leo/arcrun-components` fork→PR→人審,見 CONTRIBUTING-components.md。 registerSearchWorkflows(server, env, orgNamespace, partnerToken); // workflow-discovery R2 registerListComponents(server, env, orgNamespace); registerGetComponent(server, env, orgNamespace); diff --git a/registry/skills/write_intent_workflow.md b/registry/skills/write_intent_workflow.md index dea2e51..6806ae6 100644 --- a/registry/skills/write_intent_workflow.md +++ b/registry/skills/write_intent_workflow.md @@ -60,6 +60,25 @@ input >> ON_SUCCESS >> <第一步> >> ... → 丟 /cypher/search → 系統回 ⚠️ 仍然**不要寫 `ON_FAILURE`**(沒有這種邊;要處理失敗用 `try_catch` + `ON_BRANCH(catch)`)。 +### 2.2 怎麼確認分支真的走對了(**別看不懂就以為壞掉**) + +分支工作流「有沒有成功」看兩件事,**不是看某條沒走的路沒有輸出**: + +1. **`verdict`**:`GET /workflows//executions?limit=1` + → `data.executions[0].verdict === "success"` 就是成功了。 +2. **`trace` 裡有沒有出現該走的節點**:走 TRUE 路時 FALSE 路的節點**本來就不該出現** + ——**那是正確行為,不是失敗**。 + +``` +# 條件成立 → 只有 true 那條的節點在 trace +{"amount": 5000} → if_control 回 branch="true" → 走 ON_TRUE 那條 +{"amount": 100} → if_control 回 branch="false" → 走 ON_FALSE 那條 +``` + +🔴 **實撞(2026-08-01 考試)**:有考生的分支工作流**其實完全正常** +(`amount=5000`→true、`amount=100`→false 都對),但它以為「跑不通」而放棄改寫成 code。 +**看到只有一條路有輸出=分支正在正確運作**,不要因此判定失敗。 + ## 3. 第一個節點固定是 `input` 所有真範本都以 `input` 起頭——那是「觸發時帶進來的資料」。 @@ -151,7 +170,9 @@ curl -s -X POST https://arcrun-cypher-executor..workers.dev/cypher/se ## 7. 常犯的錯 -1. **用不存在的邊**(`ON_FAILURE`/`ON_TRUE`)→ 只有 `ON_SUCCESS` 與 `對每個 X` +1. **用不存在的邊**(`ON_FAILURE`)→ 沒有這種邊;要處理失敗用 `try_catch` + `ON_BRANCH(catch)` + ⚠️ `ON_TRUE`/`ON_FALSE`/`ON_BRANCH` **是存在的**(2026-08-01 起),見 §2.1—— + 本行以前寫「ON_TRUE 不存在」是舊世代,已更正 2. **第一個節點不是 `input`** 3. **把 recipe 當零件寫**——`telegram_send`/`gmail`/`kbdb_get` 是 **recipe** 不是零件 → 寫成 `http_request` + 該 recipe