🔴 修教材反向教壞:skill §7 自打臉+補「分支怎麼算成功」+刪 publish 死代碼

端到端 haiku 考 0/3、1/3,**斷點在教材不在引擎**(另一 agent 取證,別重查):
探針零 code 實測 n=10→TRUE、n=1→FALSE 兩次 success;**考生的作品其實會動**
(amount=5000→true、amount=100→false 條件求值全對),但它以為跑不通而放棄改寫 code;
考生第二次自己指認「MCP 說明聲稱不支援 ON_TRUE/ON_FALSE,與實際系統行為不符」。

① skill `write_intent_workflow` **同一份前後打架**(我 08-01 只改了 §2 沒掃全篇):
   §2.1 教用 ON_TRUE,§7 第 1 條卻把 ON_TRUE 列為「不存在的邊」⇒ 教材自我否定。
   改:§7 只留 ON_FAILURE(真的沒有),並明寫「ON_TRUE/ON_FALSE/ON_BRANCH 是存在的,
   見 §2.1,本行舊世代已更正」。全篇 grep 過確認無其他矛盾。

② **補 §2.2「怎麼確認分支真的走對」**——這是「看到對的結果卻以為失敗」的直接解:
   看 verdict,且**走 true 路時 false 路節點不出現=正確行為不是失敗**;
   附 08-01 實撞案例,明講「只有一條路有輸出」不該判定壞掉。
   MCP instructions 同步加這段(比 skill 更前置,AI 一連上就讀到)。

③ #23 殘留清除(leo 08-01:「已經沒有 publish 了,零件等級一律走 PR,這條路封了」):
   `git rm mcp/src/tools/arcrun_publish_component.ts`+拔掉 registry.ts 的 import
   (註冊呼叫本來就已註解掉=純死代碼配活 import)。刪前 grep 全 repo 呼叫方:
   除本檔與 registry.ts 外,其餘命中全是 md/SDD 的歷史記載(不需動)。tsc 綠。
   替代路徑=`Leo/arcrun-components` fork→PR→人審,已寫進 registry.ts 註解。

SDD: workflow-discovery 3.11|CP: arcrun-usable 步驟 5
This commit is contained in:
uncle6me-web
2026-07-31 18:37:48 +08:00
parent eefbed98b6
commit c735b911a0
4 changed files with 29 additions and 90 deletions
+3
View File
@@ -46,6 +46,9 @@ export async function handleMcpRequest(
"(配 `switch` 的每個 case`try_catch` 的 try·catch)。",
"🔴 **需要判斷時用分支邊,不要寫 code 判斷**——查零件的回應會附 `branch_hint`",
"(哪些邊型+可照抄範例),照著接即可。",
"🔴 **分支跑完怎麼判斷成功**:看 `verdict``arcrun_get_execution_trace` 或",
"`GET /workflows/<name>/executions`)。**走 true 路時 false 路的節點不出現=正確行為**,",
"不是失敗——別因為「只有一條路有輸出」就以為壞掉而改寫成 code(2026-08-01 實撞)。",
"第一個節點固定是 `input`。",
].join("\n");
-84
View File
@@ -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. 編譯為 .wasmbase64 編碼後提交
* 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<string, unknown>;
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,
};
}
}
);
}
+4 -5
View File
@@ -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);
+22 -1
View File
@@ -60,6 +60,25 @@ input >> ON_SUCCESS >> <第一步> >> ... → 丟 /cypher/search → 系統回
⚠️ 仍然**不要寫 `ON_FAILURE`**(沒有這種邊;要處理失敗用 `try_catch` `ON_BRANCH(catch)`)。
### 2.2 怎麼確認分支真的走對了(**別看不懂就以為壞掉**)
分支工作流「有沒有成功」看兩件事,**不是看某條沒走的路沒有輸出**:
1. **`verdict`**`GET /workflows/<name>/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.<subdomain>.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