原生平台與靜默無效
把裁決點放進 agent 行程裡,在平台不配合時不會變成「效果較小」,而是靜默無效——病歷完整、簽章鏈驗得過、零個裁決。三個缺口與一個把迴圈搬到父行程的裁決。
2026-09-29 這條線的狀態是設計裁決,效果未量到。這一頁記的是它量到的三個缺口。
#三個缺口
#W1 — 通道中介只認兩種金鑰變數
envmap.py 的 SECRET_VARS 刪掉了 OPENROUTER_API_KEY,discover_keys() 只讀 openai 與 anthropic 兩條。
量到的後果:opencode 的 openrouter provider 在本機 1.2 秒內 rc=1,requests_seen = 0,一次 wire 都沒發。換成固定埠的 openai-compatible provider 之後 requests_seen 變成 2——通道中介對 opencode 是通的,但 opencode 仍以非零碼結束。這條路沒有走完。
#W2 — opencode 沒有 stop 等價事件
vacant install 產生的 opencode plugin 只註冊 chat.message、tool.execute.before、tool.execute.after;pi 的同名模板有 ask("stop", …),opencode 模板沒有。Python 端 EVENT_MAP 有 "opencode": {… "stop": "stop" …},所以缺的是 plugin 側。
一格實測的簽章鏈事件統計:
trace_genesis 1, prompt 3, session_seen 2, step 37,
session_closed 3, unrecorded_change 1
stop / finding / feedback 事件: 0
delivery.md / delivery.json: 不存在
作者的原話:
37 個 step 的完整簽章病歷、零個裁決。這比不做更危險,因為 receipt 會看起來是對的。
#W3 — opencode 的專案根不由 cwd 決定
agent 原話是「There is no TASK.md in the working directory (/home/user1/ab-20260928)」。工作區只有一個檔時 100% 失敗;檔多時僥倖成功。arm C 三格崩潰(3 個 step_start 後 rc=1,無 stderr,無 429)。
這一條作者自認是自己的錯,已修(--dir + git init workspace)。
#為什麼這三個是同一個錯誤
W1 與 W2 都在做同一件事:把裁決點放進 agent 行程裡。W1 在網路層,W2 在回合邊界。
而平台不配合的時候,整條路徑不是「效果較小」,是靜默無效。
#裁決:把迴圈搬到父行程
任何「是一個可以 spawn 的 CLI」的 agent 平台都自動獲得 T3 能力,不需要掛鉤、不需要 wire、不需要金鑰變數白名單。掛鉤從必要條件降級成加速器(回合內中途回饋),沒有掛鉤也不掉級。
T0–T3 分層的現況:
| 層 | 狀態 |
|---|---|
| T3(bridge,父行程) | 可用——20 格中 17 accept、3 reject,17 格 release 全部 readback_ok=true。閘門不需要 wire 也不需要 Stop hook |
| T2(本裁決) | 可用 |
| T1(只有 tool-call 掛鉤) | 量到是死的(opencode) |
| T0 | 過時 |
新形狀把 T1 提到 T3,而且 T1 必須自我宣告:掛鉤跑完但沒有裁決時,產出一份明講「本次沒有裁決」的記錄。
#Wave 1:同號未解析
20 題 × 三臂(R534 題庫):
| 臂 | 是什麼 | 隱藏檢查全對 | 交付 | 拒交 |
|---|---|---|---|---|
| A plain | opencode,什麼都沒有 | 19/20 (95.0%) | 20/20 | 0 |
| B zero-cfg | vacant install 預設 + opencode 原生 plugin | 17/20 (85.0%) | 19/20 | 1 |
| C bridge | native_acceptance_bridge:prepare/judge/release | 17/20 (85.0%) | 17/20 | 3 |
#互動式 TUI 的七個量測錯誤
這一節是這一頁最該被記下來的部分,因為每一個錯誤的方向都是讓數字長得比實際好看。
| # | 錯誤 | 實際後果 |
|---|---|---|
| 1 | arm A 跑到別的模型 | 只複製 models.json 不會讓 pi 選那個 provider,實測落到 openrouter/moonshotai/kimi-k2.6,而且沒有一個 byte 經過這一跑的 proxy——arm A 把「模型失敗」記成「agent 失敗」 |
| 2 | settle 認錯信號 | arm C 是唯一跑多輪的臂,第一輪橫幅一出現就回報 settled 再殺 session,快照抓到第二輪還在想(token 凍結在 ↑5.9k ↓79)。兩半都是 harness 在說謊 |
| 3 | 只讀可見高度 | 約 50 行 vs 完整 scrollback 4000 行,而早先的裁決正好捲出畫面——要量的變數被截掉 |
| 4 | 把前言當成裁決 | 閘門先印的「驗收來源 dir:tests_visible…」與裁決同一個開頭但沒有裁決,於是每一格回來都是 gate_accepted: null |
| 5 | infra_void 被記成拒交 | bool(None) 把「閘門沒有裁決」歸檔成「裁決拒絕」——把 void 變成一個被計數的失敗,正是 repo 自己的 infra_void 規則禁止的事 |
| 6 | 兩個欄位互相矛盾 | 最後一輪是 2/2 的格報 1/2 |
| 7 | 停滯被算成 agent 失敗 | 模型呼叫不存在的工具 ls_r、退回 shell 就不再產出,而 backend 本身健康(緊接著 2 秒回 say ok) |
另外兩條在程式碼註解裡、commit message 未列的:
- arm A 的 8 格全部整輪停在
0.0%:提示從未被送出,TUI 就坐著不動,舊的 settle 規則(「idle 4 polls」,立刻成立)把它們全部記成settled+hidden_pass: false。那是 8 個捏造的結果。 - Enter 掉了 6 格:形狀都一樣——提示留在輸入框、狀態列
0.0%/262k。這是 arm B 看起來 void 掉 8 格裡的 5 格的原因,而真相是 5 格提示從未被送出。
全部已修,並為五種真實 pane 形狀加了測試(多輪最後通過/單輪拒絕/多輪最後拒絕/infra_void/無閘門)。
#pi 互動模式:裝了等於沒裝
第一個缺陷特別值得記:安裝報成功,proxy 起來了,但每一通都 sink,而且沒有任何錯誤訊息指出原因。 install() 呼叫沒有傳 upstreams/models/python,渲染出 KEY_FROM="" 與 UPSTREAM=""。
#通用收件口的五個靜默缺陷
同一種病:端到端量出來、單元測試抓不到。
最嚴重的一個:Codex 常駐安裝原本用 profile = "vacant",0.156.1 視為遺留鍵而拒絕啟動。作者寫「一個會讓 agent 起不來的安裝比沒裝更糟——這一格如果只做單元測試是抓不到的。」
另一個:OPENCODE_CONFIG_DIR 當每一跑的加法層會同時改掉全域 AGENTS.md 的讀取位置,使用者的全域規則會安靜地從提示裡消失。
#誠實邊界
- 互動模式的閘門不擋交付——檔案已經在工作區裡,它是標記不是攔截。agent 刪得掉 extension。
/vacant off是使用者的選擇不是失效,但 extension 會留一筆vacant_off進掛鉤日誌,收據不替它說謊。 - bridge 的 CONFORM 模式把重試交給「外層 harness 開一個新的完整 session」,但回饋怎麼送到下一個 session 的提示詞裡,bridge 沒有定義。
- 同 OS 帳號:arm 之間靠路徑隔離 +
chmod 0444,不是跨帳號強制。 - R535 的預期失敗率是設計意圖不是量測值。
- 效果未量到。 免費模型每日 1000 請求在批次中途用盡(arm B 2 格 429、wave 2 pilot 全部 429),任何跨 arm 的差異都不可歸因給 Vacant。
#接下來
- 產品的形狀:零設定 Vacant
- 追到步驟:可究責追緝
- 位置從哪來:可執行驗收層
- 整體輪廓:系統架構

