diff --git a/README.md b/README.md
index dde34f24..96f043ef 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@
Your vaults, your fleet, your devices, your pulses — composed by conversation.
-A VS Code extension · built on [Piccolo.jl](https://github.com/harmoniqs/Piccolo.jl) · open [opencode](https://github.com/sst/opencode) chat harness · telaio research harness by subscription (arriving) · model-agnostic — Kimi K3, Muse Spark, open models
+A VS Code extension · built on [Piccolo.jl](https://github.com/harmoniqs/Piccolo.jl) · open [opencode](https://github.com/anomalyco/opencode) chat harness · telaio research harness by subscription (arriving) · model-agnostic — Kimi K3, Muse Spark, open models
@@ -21,7 +21,7 @@ Amicode is an **open autonomous research studio** that lives in your editor —
Describe what you want in plain language — a gate, a state preparation, a calibration sweep — and Amicode designs the pulse, runs the solve, and shows you the result. Every run is captured, every pulse versioned for warm-start, and every session distilled into durable knowledge. The loop gets smarter as you use it.
-Leveraging **Kimi K3** (Moonshot AI), **Muse Spark** (Meta), and other open models — on the open [opencode](https://github.com/sst/opencode) ecosystem (SST, vendored from `sst/opencode`). Model-agnostic by design: the loop, not the model, is the product.
+Leveraging **Kimi K3** (Moonshot AI), **Muse Spark** (Meta), and other open models — on the open [opencode](https://github.com/anomalyco/opencode) ecosystem (vendored from `anomalyco/opencode`). Model-agnostic by design: the loop, not the model, is the product.
We start with **quantum control** because it is the hardest physical system to prove the loop on. If the studio works here — arbitrary Hamiltonians, hard constraints, **hardware in the loop via Strumento.jl / QICK** — it generalizes to any physical system you can model. Bring your own Hamiltonian; the loop is the same. That's **physical intelligence**: not one device or platform, but a composable way to do experimental science. Plenty will sell you a closed “superintelligence” that never touches the hardware. We ship open, down to the RFSoC.
diff --git a/docs/migration-canonical-opencode.md b/docs/migration-canonical-opencode.md
new file mode 100644
index 00000000..0f107c45
--- /dev/null
+++ b/docs/migration-canonical-opencode.md
@@ -0,0 +1,110 @@
+# Migration manifest — ship canonical opencode, retire the fork binary
+
+Program of record: [harmoniqs/amicode#451](https://github.com/harmoniqs/amicode/issues/451) ·
+vault spec `amicode/specs/spec-20260820-044920-ship-canonical-opencode.md` ·
+plan `amicode/plans/plan-20260820-044920-ship-canonical-opencode.md`.
+
+Upstream (canonical) = **`anomalyco/opencode`** (post-rename; `sst/opencode` is a stale alias).
+The fork = `harmoniqs/opencode`, branch `local/amicode`. Vocabulary per spec D4: never
+user-facing "fork"; the shipped server is "canonical opencode", our ported surface is "the
+Amicode service".
+
+This manifest is the M0 deliverable: feasibility-gate evidence, the full route × consumer
+inventory, the app-surface extraction inventory, and the frozen e2e quarantine list.
+
+---
+
+## 1. Feasibility gates — all five answered with evidence (2026-08-20)
+
+Evidence was gathered against **stock canonical `v1.18.19`** (upstream latest, released
+2026-08-20) — a binary downloaded from the public release, sha256-verified, booted in an
+isolated HOME. Not source spelunking: live probes.
+
+| Gate | Question | Verdict | Evidence |
+|---|---|---|---|
+| (a) | Plugin API custom server routes? | **No — M1 targets the extension-host HTTP service** | Upstream plugin surface is `Hooks` from `@opencode-ai/plugin` (tools + hooks only, loaded via `PluginLoader`); the server mounts routes exclusively through its internal Effect `HttpRouter`. The single-export legacy plugin contract we use is still supported (`getLegacyPlugins` throws on non-function exports — same constraint `amicode_tools.ts` documents). |
+| (b) | Per-boot route-auth parity on a stock binary? | **Yes, natively** | Upstream `packages/opencode/src/server/auth.ts` reads `OPENCODE_SERVER_PASSWORD` / `OPENCODE_SERVER_USERNAME`. Live probe: no-auth `GET /` and `/doc` → **401**; with `-u opencode:` → **200** on `/`, `/doc`, `/config`. The extension's `server_auth.ts` flow (mint password → env → Basic header) works against stock canonical unmodified. |
+| (c) | Verifiable release digests? | **Yes** | The GitHub releases API carries a `digest` field (`sha256:…`) per asset. Verified: downloaded `opencode-darwin-arm64.zip` hashes to exactly the API's `sha256:0026326b…`. All three required platform assets exist (`darwin-arm64.zip`, `linux-arm64.tar.gz`, `linux-x64.tar.gz`; sizes 46–60 MB). electron-builder `latest-*.yml` files exist as a secondary digest source. |
+| (d) | Iframe origin model vs extension-host service? | **Workable — one-line CSP widening + per-frame origin tracking** | The deck webview CSP is built at `deck_panel.ts` as `frame-src ${opencodeUrl.origin}` — a single-origin allowlist; adding the service origin is additive. The shell's postMessage lane checks `e.origin` against the single server origin (`deck/shell.ts`) — M1/M2 must track origin per pane once the app bundle moves. No structural blocker. |
+| (e) | Adopt-gate signals on stock canonical? | **Yes — all four** | (1) `--version` → `1.18.19`. (2) Boot liveness: stderr line `opencode server listening on http://127.0.0.1:` + HTTP 200 on `/doc` (authed). (3) **Plugin-load assert: a single-export legacy plugin with a module-level stamp write was imported at `serve` boot on the stock binary** — the stamp mechanism is our adopt-gate plugin check, no fork feature needed. (4) DB-compat probe rides the same boot against a DB copy (sqlite backup API), observable via (2). |
+
+Bonus finding: the `experimental.chat.system.transform` hook — the basis of the in-flight
+`amicode_context` plugin (uncommitted WIP in `extension.ts` / `opencode_config.ts`) — **exists
+in stock canonical** (`packages/plugin/src/index.ts`, consumed in `agent.ts` and
+`session/llm/request.ts`, covered by `test/plugin/trigger.test.ts`). The WIP is portable; it
+is NOT dead code and is left untouched.
+
+## 2. Route inventory — fork `httpapi/server.ts`, 31 routes × 18 modules
+
+All fork Amicode routes mount in one file:
+`packages/opencode/src/server/routes/instance/httpapi/server.ts` (imports at lines 72–82;
+`router.add` calls). Consumers: **ext** = VS Code extension (`packages/extension/src`),
+**widgets** = home-dashboard widgets (`server/amicode/widgets-src`, same-origin `amico.fetch`),
+**app** = fork app UI (`packages/ui/src/amicode/*`).
+
+| Module | Routes | Consumers | Destination |
+|---|---|---|---|
+| `vaults` | GET/POST `/amicode/vaults`, GET `/amicode/vault-files`, GET `/amicode/vault-file` | app (vault panel), ext | extension-host service |
+| `warrants` | GET `/amicode/warrants`, POST `/amicode/approve` | app, ext | extension-host service |
+| `file-resolve` | GET `/amicode/resolve-file` | app (file cards), ext | extension-host service |
+| `problems` | GET `/amicode/problems`, `/amicode/problem`, `/amicode/run-status`, `/amicode/run-series`, `/amicode/run-cards` | app (entity rail, home cards, run window), ext (run inspector), widgets (now-solving, jump-back-in) | extension-host service |
+| `widgets` + `widget-manifest` + `widget-runtime` + `widget-frame-html` | GET `/amicode/widgets`, `/amicode/widget-code`, `/amicode/widget-frame`; POST `/amicode/widget-fork` | app (widget grid), ext | extension-host service (serves widget frames + runtime) |
+| `dashboard` | GET/POST `/amicode/dashboard` | widgets | extension-host service |
+| `library` | GET/POST `/amicode/library` | widgets (library), ext | extension-host service |
+| `profile` | GET/POST `/amicode/profile` | widgets (about-you, pulse-bank), ext | extension-host service |
+| `connections` + `credentials` + `pasqal-secret` | GET `/amicode/connections`, `/amicode/connections/catalog`; POST `/amicode/connections/{auth,credential,revalidate,disconnect,remove,choose-project,add-custom}` | app (connections panel), ext (chat bridge) | extension-host service (credentials vault stays in the extension host — it already owns key storage) |
+| `project` | POST `/amicode/project`, GET `/amicode/projects` | app (project picker) | extension-host service |
+| `run-terminal` | (no dedicated amicode route — rides the PTY/httpapi surface) | ext (run controls) | extension-host service or canonical PTY API — decide in M1 |
+| `solver-mode` | (no HTTP route — injected per-session via transform) | app (solver toggle) | already portable: rides the `amicode_context`-style plugin hook (confirmed on canonical) |
+| `toml-lite` | (supporting lib — TOML parsing for the above) | — | ports with its consumers |
+
+Widget sources (7, served via `/amicode/widget-frame` + `widget-code`): `about-you`,
+`jump-back-in`, `library`, `meet-amico`, `now-solving`, `pulse-bank`, `showcase` — all become
+extension-service assets in M1.
+
+## 3. App-surface extraction inventory (M2)
+
+- **`packages/ui/src/amicode/*`** in the fork — the complete Amicode UI component set:
+ entity-rail, home-cards, connections (+test), widget-grid, widget-frame, widget-preview,
+ widget-allowlist (+schema/bridge tests), run-series, run-window, problem, card,
+ wave-geometry, and more. Extraction = move this directory into the amicode repo's app
+ bundle + adapt imports to canonical's public client SDK (`@opencode-ai/sdk`).
+- **Patch-stack deltas** (per `AMICODE-PATCHES.md`, the authoritative log): branding/fonts/
+ accents, KaTeX macros, AmicoSpinner sites, titlebar inline tab strip, ask-card/entity-rail
+ card dispatch, prompt-agnostic cassette matcher, markdown polish (~75 lines), the 72
+ amicode-era i18n keys (move into our bundle — ends the upstream parity-test fight),
+ deletions (side panel, `debug-bar.tsx`), `OPENCODE_CHANNEL=dev` build gate.
+- **Serving**: the app bundle is served for the deck's iframes by the extension-host service
+ (M1), composing upstream app components; CI pins it against canonical releases.
+
+## 4. Frozen e2e quarantine list (frozen here — M0; per spec, additions require replanning)
+
+1. `httpapi-v2-pty` — "serves location-wrapped PTY routes", ~1-in-3 timeout on clean upstream.
+2. `project-picker-recent-search` — drives upstream's home-projects surface the fork doesn't
+ render (per AMICODE-PATCHES.md 2026-08-04 entry).
+
+Known-red-on-fork suites are NOT quarantined — they are the ported surfaces and get the
+golden-fixture treatment per the spec (contract tests judged against fixtures recorded from
+the running fork server): pasqal connections (8 reds), amicode widgets (15 reds).
+
+## 5. Touch-up revision (spec amendment, honest record)
+
+The spec's M0 item "remove the dead `amicode_context.ts` config reference" is **void**: the
+reference is live WIP (the `experimental.chat.system.transform` hook plugin), the hook is
+confirmed present in stock canonical, and the uncommitted `extension.ts`/`opencode_config.ts`
+changes belong to it. Nothing removed. The one machine-level wrinkle — the server-bundle
+config references `amicode_context.ts` while no installed VSIX ships it yet — resolves itself
+when that WIP lands; not touched here.
+
+The stale-alias touch-up lands in the same branch: `sst/opencode` → `anomalyco/opencode` in
+the vendoring script defaults (`fetch_opencode.mjs`, `opencode_dev.mjs`), the test that pins
+those defaults, the README links, and the report-a-bug skill's upstream-check repo identity.
+
+## 6. Open items for M1 (carried, not blockers)
+
+- Per-pane origin tracking in the deck shell once panes can come from two origins.
+- Auth story for the extension-host service: mint a per-boot token alongside the canonical
+ password (the service binds localhost; origin checks in the shell cover the iframe lane, but
+ the HTTP surface wants its own 401 path).
+- Port allocation + lifecycle (server-manager owns it, like the canonical server today).
+- `run-terminal` destination decision (service vs canonical PTY API).
diff --git a/packages/extension/README.md b/packages/extension/README.md
index f779e7fd..009f7030 100644
--- a/packages/extension/README.md
+++ b/packages/extension/README.md
@@ -34,4 +34,4 @@ Run **Amicode: Healthcheck** from the Command Palette any time to verify your se
---
-A VS Code extension · built on [Piccolo.jl](https://github.com/harmoniqs/Piccolo.jl) · chat harness vendored from [opencode](https://github.com/sst/opencode) (MIT). Pre-release software.
+A VS Code extension · built on [Piccolo.jl](https://github.com/harmoniqs/Piccolo.jl) · chat harness vendored from [opencode](https://github.com/anomalyco/opencode) (MIT). Pre-release software.
diff --git a/packages/extension/scripts/fetch_opencode.mjs b/packages/extension/scripts/fetch_opencode.mjs
index a4735f2b..b51df2a3 100644
--- a/packages/extension/scripts/fetch_opencode.mjs
+++ b/packages/extension/scripts/fetch_opencode.mjs
@@ -51,12 +51,12 @@ export function resolvePlatform(manifest, flag) {
return key;
}
-/** Release coordinates: default = upstream sst/opencode at v; a manifest
+/** Release coordinates: default = upstream anomalyco/opencode at v; a manifest
* with `repo`/`tag` set points at our fork's release instead (harmoniqs/opencode,
* private — downloads go through the authenticated `gh` path in that case). */
export function releaseCoords(manifest) {
return {
- repo: manifest.repo ?? "sst/opencode",
+ repo: manifest.repo ?? "anomalyco/opencode",
tag: manifest.tag ?? `v${manifest.version}`,
private: manifest.repo != null, // our mirror is private; upstream is not
};
diff --git a/packages/extension/scripts/opencode_dev.mjs b/packages/extension/scripts/opencode_dev.mjs
index 2109ec0d..3d9422f5 100755
--- a/packages/extension/scripts/opencode_dev.mjs
+++ b/packages/extension/scripts/opencode_dev.mjs
@@ -114,7 +114,7 @@ export function pinFromRelease({ root = PKG_ROOT, tag, ref, download = ghDownloa
if (!tag) throw new Error("pin: a release tag is required (e.g. pnpm opencode:pin v1.17.3-amicode.5)");
const lockPath = join(root, "opencode.lock.json");
const m = JSON.parse(readFileSync(lockPath, "utf8"));
- const repo = m.repo ?? "sst/opencode";
+ const repo = m.repo ?? "anomalyco/opencode";
const shas = {};
for (const [key, p] of Object.entries(m.platforms ?? {})) {
const bytes = download(repo, tag, p.asset);
diff --git a/packages/extension/skills/report-a-bug/SKILL.md b/packages/extension/skills/report-a-bug/SKILL.md
index 9f5db664..44caadfb 100644
--- a/packages/extension/skills/report-a-bug/SKILL.md
+++ b/packages/extension/skills/report-a-bug/SKILL.md
@@ -59,9 +59,9 @@ Scrubbing is keyed to **content taint, not destination repo** — even a misrout
Both are read-only searches with **scrubbed terms only**, run before drafting so their results feed the footer. Silent on no match. If `gh` is unavailable or unauthenticated, skip both checks silently — the browser fallback in step 7 still files, and the footer records no upstream claim.
- **Dedup.** Search the target repo's open issues (`gh search issues --repo --state open`). On a likely-match open issue, offer **comment-on-existing vs file-anyway** — the only conditional prompt besides the gates.
-- **Upstream check (fork-vendored surfaces only — today, the vendored engine).** The pin makes "does the fix already exist upstream?" mechanically decidable: parse the release tag's upstream base (`v-amicode.` → `v` of `sst/opencode`), then search upstream issues **and** PRs with scrubbed terms:
+- **Upstream check (fork-vendored surfaces only — today, the vendored engine).** The pin makes "does the fix already exist upstream?" mechanically decidable: parse the release tag's upstream base (`v-amicode.` → `v` of `anomalyco/opencode`), then search upstream issues **and** PRs with scrubbed terms:
- **A matching merged fix** (merged PR / closed-as-fixed issue) at a release newer than the vendored base → offer an **upstream-bump chore issue instead of the `BUG:` filing** (merge the newer upstream; never re-implement what upstream already fixed).
- - **A matching open upstream issue** → file the intake bug normally, footer `upstream: sst/opencode#N (open)` — maturation watches rather than implements.
+ - **A matching open upstream issue** → file the intake bug normally, footer `upstream: anomalyco/opencode#N (open)` — maturation watches rather than implements.
- **No trace** → footer `upstream: none found`.
## 5. Compose the intake issue
diff --git a/packages/extension/test/fetch_opencode.test.ts b/packages/extension/test/fetch_opencode.test.ts
index 73f5516c..a4360d4d 100644
--- a/packages/extension/test/fetch_opencode.test.ts
+++ b/packages/extension/test/fetch_opencode.test.ts
@@ -194,9 +194,9 @@ describe("releaseCoords — fork-mirror pinning", async () => {
const platforms = { "linux-x64": { asset: "opencode-linux-x64.tar.gz", sha256: "a".repeat(64) } };
it("defaults to upstream at v, public", () => {
const m = { version: "1.17.3", platforms };
- expect(releaseCoords(m)).toEqual({ repo: "sst/opencode", tag: "v1.17.3", private: false });
+ expect(releaseCoords(m)).toEqual({ repo: "anomalyco/opencode", tag: "v1.17.3", private: false });
expect(assetUrl(m, "linux-x64")).toBe(
- "https://github.com/sst/opencode/releases/download/v1.17.3/opencode-linux-x64.tar.gz",
+ "https://github.com/anomalyco/opencode/releases/download/v1.17.3/opencode-linux-x64.tar.gz",
);
});
it("repo+tag repoint to the private mirror", () => {