Native platform and silent no-ops
Putting the decision point inside the agent process does not become "a smaller effect" when the platform doesn't cooperate; it becomes a silent no-op — a complete case record, a signature chain that verifies, zero verdicts. Three gaps, and one decision that moves the loop into the parent process.
This page cites 5 verdicts: Held 1 · Refuted 3 · Same sign, unresolved 1click one to jump to its claim card
- Refutedopencode has no stop event: a full 37-step case record, zero verdicts
- Same sign, unresolvedNative platform wave 1: Same sign, unresolved — not a negative result
- HeldSeven measurement errors in the interactive A/B harness, every one making the numbers look better than they were
- RefutedThe pi extension lacks three things; installing it is the same as not installing it
- RefutedEnd-to-end runs of the universal intake found five defects that "make you think it works when it doesn't"
As of 2026-09-29 the status of this line is a design decision, effect not measured. This page records the three gaps it did measure.
#Three gaps
#W1 — channel mediation only recognizes two kinds of key variable
SECRET_VARS in envmap.py dropped OPENROUTER_API_KEY, and discover_keys() reads only two: openai and anthropic.
The measured consequence: opencode's openrouter provider exited with rc=1 locally within 1.2 seconds, requests_seen = 0, without sending anything over the wire even once. After switching to an openai-compatible provider on a fixed port, requests_seen became 2 — channel mediation works for opencode, but opencode still exits with a nonzero code. This path was not completed.
#W2 — opencode has no stop-equivalent event
The opencode plugin generated by vacant install registers only chat.message, tool.execute.before and tool.execute.after; pi's template of the same name has ask("stop", …), and the opencode template does not. On the Python side, EVENT_MAP does have "opencode": {… "stop": "stop" …}, so what is missing is on the plugin side.
The signature-chain event counts from one measured cell:
trace_genesis 1, prompt 3, session_seen 2, step 37,
session_closed 3, unrecorded_change 1
stop / finding / feedback events: 0
delivery.md / delivery.json: does not exist
The author's own words:
"A complete signed case record of 37 steps, and zero verdicts. This is more dangerous than doing nothing, because the receipt will look right." (original: 「37 個 step 的完整簽章病歷、零個裁決。這比不做更危險,因為 receipt 會看起來是對的。」)
#W3 — opencode's project root is not decided by cwd
The agent's own words were "There is no TASK.md in the working directory (/home/user1/ab-20260928)". With only one file in the workspace it fails 100%; with more files it succeeds by luck. Arm C crashed in three cells (rc=1 after 3 step_starts, no stderr, no 429).
The author considers this one their own mistake, and it has been fixed (--dir + git init workspace).
#Why these three are the same mistake
W1 and W2 both do the same thing: put the decision point inside the agent process. W1 does it at the network layer, W2 at the turn boundary.
And when the platform doesn't cooperate, the whole path does not have "a smaller effect"; it is a silent no-op.
#Decision: move the loop into the parent process
Any agent platform that "is a CLI that can be spawned" automatically gets T3 capability, with no hooks, no wire and no whitelist of key variables needed. Hooks are downgraded from a necessary condition to an accelerator (mid-turn feedback within a turn); without hooks, nothing drops a level.
The current state of the T0–T3 tiers:
| Tier | Status |
|---|---|
| T3 (bridge, parent process) | Usable — of 20 cells, 17 accept and 3 reject, and all 17 released cells have readback_ok=true. The gate needs neither wire nor a Stop hook |
| T2 (this decision) | Usable |
| T1 (tool-call hooks only) | Measured as dead (opencode) |
| T0 | Obsolete |
The new shape raises T1 to T3, and T1 must declare itself: when the hooks finish running but there is no verdict, it produces a record that says outright "there was no verdict this time."
#Wave 1: Same sign, unresolved
20 tasks × three arms (R534 task bank):
| Arm | What it is | All hidden checks correct | Delivered | Refused delivery |
|---|---|---|---|---|
| A plain | opencode with nothing added | 19/20 (95.0%) | 20/20 | 0 |
| B zero-cfg | vacant install defaults + opencode's native plugin | 17/20 (85.0%) | 19/20 | 1 |
| C bridge | native_acceptance_bridge: prepare / judge / release | 17/20 (85.0%) | 17/20 | 3 |
#Seven measurement errors in the interactive TUI
This section is the part of this page most worth recording, because every one of the errors went in the direction of making the numbers look better than they really were.
| # | Error | Actual consequence |
|---|---|---|
| 1 | Arm A ran on a different model | Copying only models.json does not make pi choose that provider; in practice it landed on openrouter/moonshotai/kimi-k2.6, and not a single byte went through this run's proxy — arm A recorded "model failure" as "agent failure" |
| 2 | settle recognized the wrong signal | Arm C is the only arm that runs multiple rounds; as soon as the first round's banner appeared it reported settled and killed the session, and the snapshot caught the second round still thinking (tokens frozen at ↑5.9k ↓79). Both halves were the harness lying |
| 3 | Reading only the visible height | About 50 lines vs. a full scrollback of 4000 lines, and the earlier verdict had scrolled right off the screen — the variable being measured was cut off |
| 4 | Taking the preamble for the verdict | The gate's first printed line, "acceptance source dir:tests_visible…" (original: 「驗收來源 dir:tests_visible…」), starts the same way as a verdict but has no verdict, so every cell came back as gate_accepted: null |
| 5 | infra_void recorded as a refused delivery | bool(None) filed "the gate had no verdict" as "the verdict was refuse" — turning a void into a counted failure, which is exactly what the repo's own infra_void rule forbids |
| 6 | Two fields contradicting each other | A cell whose last round was 2/2 reported 1/2 |
| 7 | A stall counted as agent failure | The model called a nonexistent tool ls_r, fell back to the shell and stopped producing output, while the backend itself was healthy (it answered say ok in 2 seconds right afterwards) |
Two more that are in code comments but not listed in the commit message:
- All 8 cells of arm A stopped for the whole round at
0.0%: the prompt was never sent, the TUI just sat there, and the old settle rule ("idle 4 polls," satisfied immediately) recorded all of them assettled+hidden_pass: false. Those were 8 fabricated results. - Enter dropped in 6 cells: all the same shape — the prompt left in the input box, the status bar at
0.0%/262k. This is why arm B appeared to void 5 of its 8 cells, and the truth is that the prompt was never sent in those 5 cells.
All of them have been fixed, and tests were added for five real pane shapes (multi-round with a final pass / single round refused / multi-round with a final refusal / infra_void / no gate).
The measurement tool mismeasured seven times, and every time in its own favor
#pi interactive mode: installed is the same as not installed
The first defect is especially worth recording: the install reported success and the proxy came up, but every call went to the sink, and no error message pointed to the cause. The install() call did not pass upstreams / models / python, and rendered KEY_FROM="" and UPSTREAM="".
#Five silent defects in the general intake point
The same disease: measured end to end, invisible to unit tests.
The most serious one: Codex's persistent install originally used profile = "vacant", which 0.156.1 treats as a legacy key and refuses to start with. The author wrote: "An install that keeps the agent from starting is worse than no install — if this cell had only been unit-tested, it would not have been caught." (original: 「一個會讓 agent 起不來的安裝比沒裝更糟——這一格如果只做單元測試是抓不到的。」)
Another: using OPENCODE_CONFIG_DIR as a per-run additive layer also changes where the global AGENTS.md is read from, so the user's global rules quietly disappear from the prompt.
#Honest boundaries
- The gate in interactive mode does not stop delivery — the files are already in the workspace; it is a marker, not an interception. The agent can delete the extension.
/vacant offis the user's choice, not a failure, but the extension leaves avacant_offentry in the hook log; the receipt does not lie for it. - The bridge's CONFORM mode hands retries to "the outer harness opening a new, complete session," but the bridge does not define how the feedback gets into the next session's prompt.
- Same OS account: the arms are isolated by paths +
chmod 0444, not enforced across accounts. - R535's expected failure rate is a design intention, not a measured value.
- Effect not measured. The free model's 1000 requests per day ran out mid-batch (2 cells of arm B got 429, the wave 2 pilot got 429 throughout), so no cross-arm difference can be attributed to Vacant.
#Next
- The shape of the product: Zero-config Vacant
- Tracing down to the step: Accountable trace
- Where the position comes from: Executable acceptance layer
- The overall outline: Architecture

