# The Phase 2 fork: reopened, and why the first answer was wrong > **CORRECTION, same day.** An earlier version of this file concluded "the new pipeline does not > execute, keep the subprocess". **That was wrong, and wrong for an embarrassing reason: my probe.** > The conclusion is reversed below. The mistake is written up rather than deleted, because the shape of > it is the useful part. **Decision: the fork is worth taking. Not started, and gated on one open question.** --- ## What actually happened The serve exposes a newer `/api/session/*` surface, alongside the `/session/*` one every prior document was written against. It offers, natively, what the parity doc lists as impossible under `stdin: 'ignore'`: `delivery: "steer" | "queue"` on `POST /prompt`, `/interrupt`, a per-session `text/event-stream` with an `?after=` cursor, `/compact`, `prompt.files`, and `/permission` + `/question`. I probed it, saw prompts accepted and never executed, and concluded it was an unfinished pipeline ("`session.next.*` is the tell"). Every one of those probes passed an explicit `model: {providerID: 'opencode', id: 'claude-sonnet-4-6'}`. **That model silently does not run on the new pipeline.** No error, no event, no assistant message — the prompt is admitted, stored, `prompt.admitted` and `prompted` fire, and nothing else ever happens. Drop the model field and the identical request completes normally. So I had one broken variable in every experiment and read the result as a property of the system. ## What is actually true, measured on 1.18.16 (both machines upgraded 2026-08-10) | Claim | Verdict | | ----------------------------------------- | -------------------------------------------------------------------- | | The new pipeline executes turns | **Yes** — replies normally when no model is forced | | `delivery: "steer"` injects mid-turn | **Yes, verified** — steered a running turn, output changed to order | | `delivery: "queue"` runs after | **Yes, verified** — two replies, "ONE" then "TWO", zero errors | | Model selection works at all | **Yes** — `POST /api/session/{id}/model` → 204, then runs on it | | `claude-sonnet-4-6` works there | **No** — silent no-op, at create *and* via the model route | | `claude-sonnet-4-6` works via `run --dir` | **Yes** — verified end to end the same day | Steer and queue are exactly the two features we hand-built for Claude and that Andre called a game changer. Having them as primitives, plus a resumable per-session cursor that mirrors officer's durable replay, is a strong argument for migrating. ## The blocker, now characterised: only free models run It is not sonnet, and it is not `variant`. Swept four models through `POST /api/session/{id}/model` followed by a prompt: | Model | New pipeline | | -------------------- | ------------ | | `longcat-2.0-free` | **ran** | | `ling-3.0-tiny-free` | **ran** | | `claude-haiku-4-5` | never ran | | `claude-sonnet-4-6` | never ran | | `gpt-5.1-codex-mini` | never ran | **Every `-free` model runs; every paid model silently does not.** Ruled out along the way: - **Not `variant`.** `claude-sonnet-4-6` advertises `["low","medium","high","max"]` and session create echoes back `variant: "default"`, which is not among them — a promising theory that turned out to be wrong: setting `variant: "high"` explicitly also never ran. Tested rather than assumed, which is the whole lesson of this file. - **Not missing credentials.** `opencode auth list` shows an OpenCode Zen API key in `~/.local/share/opencode/auth.json` plus `ANTHROPIC_API_KEY` in the environment. - **Not the sidecar's environment.** The *same* sidecar process runs `claude-sonnet-4-6` correctly through `opencode run --model`, verified end to end. Same user, same home, same auth file. So the new pipeline does not resolve paid-model credentials, and fails **silently** rather than reporting it — while the legacy path and `run` both authenticate fine. Note it *can* surface provider auth errors when it reaches that far: alpha's default (`nano-gpt`) returned a clean `401 missing_api_key`. The silence is specific to opencode-zen paid models. This reads as an upstream bug in an in-progress pipeline, not something configurable on our side. **Consequence for the fork:** blocked, but precisely. Officer's users pick real models; a harness that works only on free tiers is not adoptable. Re-run the sweep above after each `opencode upgrade` — the day a paid model runs there, the migration is unblocked and worth doing immediately, because steer and queue are already proven. ## Revised recommendation **Take the fork, targeting the new surface, once the model question is answered.** Not the legacy `/session/{id}/message` path — that generates fine but has neither steer nor queue, so it buys streaming at the cost of SSE demux and warm-session lifetime for the two least interesting gaps. Until then `opencode run --dir` stays, and it is verified working: `session:init` → `assistant:text` → `result` with cost, on 1.18.16, after the upgrade. ## The lesson, which is the reason this file keeps its history This project has now hit the same trap three times, each time in a different costume: - `directory` in the body of `POST /session` — accepted, echoed, ignored. Produced a confident wrong answer about per-request directories. - `location.directory` in the parity doc — a field that did not exist on that surface, which would have compiled and silently produced `''`. - `model` on `POST /api/session` — accepted, echoed back in the response, and fatal to execution. **OpenCode's API accepts input it does not honour, and says nothing.** So a probe that changes one thing and sees nothing happen has not learned that the feature is missing; it has learned that *something* is wrong, and the next step is to remove variables, not to conclude. The corrected probes here each changed exactly one field. ## Findings worth keeping - `delivery` defaults to `"steer"` when omitted. - New-surface responses wrap in `{"data": …}`; the legacy surface returns bare objects, so reading `body.id` instead of `body.data.id` silently yields `undefined`. - The new surface is location-scoped per request: `x-opencode-directory` header, or `?location[directory]=` as a `deepObject` query. - `?after=` genuinely replays a finished session's events. - **opencode versions: both machines on 1.18.16** as of 2026-08-10 (Mac was 1.18.11, alpha 1.17.9). Several comments recorded these backwards; corrected. Policy from Andre: **write against the latest version regardless of what alpha happens to run.**