Просмотр исходного кода

docs: document OpenCode v2 migration

Alvin Unreal 3 недель назад
Родитель
Сommit
5930d2f570
5 измененных файлов с 208 добавлено и 187 удалено
  1. 5 5
      .slim/clonedeps.json
  2. 1 1
      AGENTS.md
  3. 1 0
      README.md
  4. 27 2
      docs/clonedeps.md
  5. 174 179
      docs/opencode-v2-compatibility.md

+ 5 - 5
.slim/clonedeps.json

@@ -1,6 +1,6 @@
 {
   "version": "1.0.0",
-  "updatedAt": "2026-08-04T19:15:39.000Z",
+  "updatedAt": "2026-08-25T00:00:00.000Z",
   "dependencies": [
     {
       "name": "@opencode-ai/plugin",
@@ -31,12 +31,12 @@
     },
     {
       "name": "anomalyco/opencode",
-      "resolvedVersion": "dev",
+      "resolvedVersion": "beta",
       "repoUrl": "https://github.com/anomalyco/opencode.git",
-      "ref": "dev@f0afb6750e63ee0a60b052914531bde0afb9bc2b",
+      "ref": "beta@d6deb62379c54dc60468b80c498bd6a5899797cf",
       "path": ".slim/clonedeps/repos/opencode",
-      "packagePath": "packages/opencode",
-      "reason": "Latest OpenCode TypeScript runtime source with experimental background subagent support."
+      "packagePath": "packages/plugin",
+      "reason": "OpenCode v2 plugin SDK and runtime source for the v2 migration."
     },
     {
       "name": "agentclientprotocol/agent-client-protocol",

+ 1 - 1
AGENTS.md

@@ -180,7 +180,7 @@ Read-only dependency source repositories are available under
 `.slim/clonedeps/repos/` for inspection. Do not edit these clones.
 
 - `.slim/clonedeps/repos/anomalyco__opencode-v1.18.13/` - `https://github.com/anomalyco/opencode.git` at `v1.18.13@a105350812f05f914c768e468559dbd6bd508d8e`; inspect `packages/plugin` and `packages/sdk/js` for OpenCode plugin and SDK internals.
-- `.slim/clonedeps/repos/opencode/` - `https://github.com/anomalyco/opencode.git` at `dev@f0afb6750e63ee0a60b052914531bde0afb9bc2b`; inspect `packages/opencode` for latest TypeScript runtime internals and experimental background subagent support.
+- `.slim/clonedeps/repos/opencode/` - `https://github.com/anomalyco/opencode.git` at `beta@d6deb62379c54dc60468b80c498bd6a5899797cf`; inspect `packages/plugin`, `packages/sdk`, `packages/core`, and `packages/cli` for the OpenCode v2 migration.
 - `.slim/clonedeps/repos/modelcontextprotocol__typescript-sdk/` - `https://github.com/modelcontextprotocol/typescript-sdk.git` at `1.30.0@2d889f2b329e46680ec9bdd565de4616c497825a`; inspect it for MCP protocol and server integration internals.
 - `.slim/clonedeps/repos/agentclientprotocol__agent-client-protocol/` - `https://github.com/agentclientprotocol/agent-client-protocol.git` at `main@541daf8fa488c6b93aad4a874ac050b3daf9b282`; inspect it for ACP protocol specification and schema details.
 

+ 1 - 0
README.md

@@ -664,6 +664,7 @@ Use this section as a map: start with installation, then jump to features, confi
 | **[Configuration](docs/configuration.md)** | Config file locations, JSONC support, prompt overrides, and full option reference |
 | **[Project Customization](docs/project-local-customization.md)** | Repository-specific custom agents, prompt overrides, per-agent skills, and precedence |
 | **[Background Orchestration](docs/background-orchestration.md)** | Scheduler-first orchestrator model built around native background subagents |
+| **[OpenCode v2 Compatibility](docs/opencode-v2-compatibility.md)** | Supported v1/v2 hosts, adapter behavior, feature coverage, and v2 limitations |
 | **[Maintainer Guide](docs/maintainers.md)** | Issue triage rules, label meanings, support routing, and repo maintenance workflow |
 | **[Skills](docs/skills.md)** | Bundled skills such as `simplify`, `codemap`, `clonedeps`, `deepwork`, `verification-planning`, `reflect`, `worktrees`, and `oh-my-opencode-slim` |
 | **[MCPs](docs/mcps.md)** | `context7`, `gh_grep`, and how MCP permissions work per agent |

+ 27 - 2
docs/clonedeps.md

@@ -66,7 +66,10 @@ name, and may include a pinned-release suffix when multiple revisions need to
 coexist. For example, the TypeScript OpenCode repository
 `https://github.com/anomalyco/opencode.git` becomes
 `.slim/clonedeps/repos/anomalyco__opencode-v1.18.13/` when pinned to that
-release.
+release. This project also keeps a separate
+`.slim/clonedeps/repos/opencode/` checkout of the OpenCode `beta` source for
+intentional v2 migration work. That checkout is tracked as source by its branch
+and commit, not as an npm-pinned dependency.
 
 If multiple packages come from the same monorepo, they share one cloned repo path
 and use different `packagePath` values in the manifest.
@@ -75,7 +78,9 @@ These repositories are read-only reference source. Do not edit them.
 
 ### `.slim/clonedeps.json`
 
-This is the structured manifest. It is intentionally small and committable:
+This is the structured manifest. It is intentionally small and committable. The
+following abbreviated example shows the OpenCode entries; other manifest entries
+are omitted:
 
 ```json
 {
@@ -99,11 +104,27 @@ This is the structured manifest. It is intentionally small and committable:
       "path": ".slim/clonedeps/repos/anomalyco__opencode-v1.18.13",
       "packagePath": "packages/sdk/js",
       "reason": "Core SDK source used to inspect runtime behavior"
+    },
+    {
+      "name": "anomalyco/opencode",
+      "resolvedVersion": "beta",
+      "repoUrl": "https://github.com/anomalyco/opencode.git",
+      "ref": "beta@d6deb62379c54dc60468b80c498bd6a5899797cf",
+      "path": ".slim/clonedeps/repos/opencode",
+      "packagePath": "packages/plugin",
+      "reason": "OpenCode v2 plugin SDK and runtime source for the v2 migration"
     }
   ]
 }
 ```
 
+The actual manifest may contain additional dependency entries beyond this
+abbreviated example.
+
+The beta entry is a checked-out source reference: `resolvedVersion` identifies
+the beta channel, while `ref` records the branch and commit. It does not pin an
+npm package version.
+
 Future clonedeps runs read this file first instead of starting from a fresh scan.
 
 ### `AGENTS.md`
@@ -124,6 +145,10 @@ Read-only dependency source repositories are available under
   `anomalyco/opencode` at
   `v1.18.13@a105350812f05f914c768e468559dbd6bd508d8e`; inspect `packages/plugin` and
   `packages/sdk/js` for OpenCode plugin and SDK internals.
+- `.slim/clonedeps/repos/opencode/` - `anomalyco/opencode` at
+  `beta@d6deb62379c54dc60468b80c498bd6a5899797cf`; inspect the checked-out beta
+  source as an intentional OpenCode v2 migration reference. This is not an
+  npm-pinned dependency.
 ```
 
 ---

+ 174 - 179
docs/opencode-v2-compatibility.md

@@ -1,201 +1,196 @@
-# OpenCode v2 (`opencode2`) Compatibility
+# OpenCode v2 compatibility and native migration
 
-oh-my-opencode-slim installs and runs on **both** OpenCode v1 (`opencode`) and
-OpenCode v2 (`opencode2`) from a single published package. This document
-explains how the dual-compatibility works and what is supported on each host.
+## Current status
 
-## How it works
+OpenCode v2 compatibility is currently provided by a **v1-to-v2 adapter**. It
+is not a native v2 implementation, and native v2 support has not shipped.
 
-The package's default export is an object:
+The current path is `src/v2/setup.ts`:
 
-```ts
-export default {
-  id: 'oh-my-opencode-slim',
-  server: OhMyOpenCodeLite, // v1 plugin function (PluginInput) => Promise<Hooks>
-  setup: createV2Setup(),   // v2 promise-plugin setup (ctx) => Promise<cleanup>
-};
-```
+1. v2 calls the package's `default.setup(ctx)` entry point.
+2. The adapter builds a v1-shaped `PluginInput`, including a client shim and
+   `process.cwd()` as the project directory.
+3. It invokes the existing `OhMyOpenCodeLite` v1 factory and receives v1
+   `Hooks`.
+4. It translates those hooks into v2 agent, tool, command, session-context,
+   tool-lifecycle, and event registrations.
+5. It returns cleanup functions for the v2 registrations and the v1 factory.
+
+This preserves useful behavior while both hosts are supported, but it is a
+best-effort compatibility layer. Registration failures are independently
+isolated, so a plugin can load with only part of its behavior registered.
+
+The beta checkout used for the migration target is OpenCode `v2` at
+`d6deb62379c54dc60468b80c498bd6a5899797cf`.
+
+## Installing the current adapter on v2
 
-- **v1 loader** (`readV1Plugin` in `packages/opencode/src/plugin/shared.ts`)
-  detects an object with a `server` field and calls `plugin.server(input)`.
-  This is the original, unchanged v1 code path — v1 behavior is identical to
-  previous releases.
-- **v2 loader** (`PluginModule` schema in
-  `packages/core/src/plugin/supervisor.ts`) decodes `default` as
-  `{ id, setup }` (Effect Schema 4 rejects function defaults) and calls
-  `setup(ctx)` via the promise-plugin bridge.
-
-Two builds are produced:
-
-| Export | File | Build | Externals |
-|---|---|---|---|
-| `.` (main) | `dist/index.js` | `build:plugin` | zod, jsdom, @ast-grep/napi, @opencode-ai/* (shared with v1 host) |
-| `./server` | `dist/server.js` | `build:v2` | @ast-grep/napi, jsdom only (self-contained for v2) |
-
-v2's plugin resolver tries the `server` subpath first
-(`subpaths: ["server", ""]`), so a v2 package install loads the self-contained
-`dist/server.js`. v1 uses the main entry.
-
-## The v2 adapter (`src/v2/setup.ts`)
-
-`setup(ctx)` wraps the existing v1 factory rather than reimplementing it:
-
-1. Builds a v1-shaped `PluginInput` from the v2 context (`process.cwd()` for
-   `directory`; a shim `client` that delegates `session.abort/prompt/messages`,
-   `app.log`, and `tui.showToast` to the v2 context or graceful no-ops).
-2. Invokes `OhMyOpenCodeLite(pluginInput)` to reuse **all** existing build
-   logic (config, agents, tools, hooks, job board, multiplexer, companion).
-3. Runs the v1 `config()` hook against a synthesized config to resolve agent
-   models and the slash commands.
-4. Bridges the returned v1 `Hooks` into v2 registrations:
-   - `agent` → `ctx.agent.transform` (model/prompt/permission adaptation +
-     `subagent`/`execute` permission mapping + prompt rewrite `task`→`subagent`)
-   - `tool` → `ctx.tool.transform` (zod shape → JSON schema; execute shimmed)
-   - `command` → `ctx.command.transform` (deepwork/reflect/loop)
-   - `experimental.chat.system.transform` +
-     `experimental.chat.messages.transform` → `ctx.session.hook("context")`
-     (SystemPart[]/Message.content shape conversion)
-   - `tool.execute.before/after` → `ctx.tool.hook`
-   - `event` → `ctx.event.subscribe()` loop
-   - `dispose` → returned cleanup
-
-Each bridge is independently try/catch-guarded so one failure cannot disable
-the rest.
-
-## Feature matrix
-
-| Capability | v1 (`opencode`) | v2 (`opencode2`) | Notes |
-|---|---|---|---|
-| Orchestrator + specialist agents | ✅ | ✅ | |
-| Agent prompts / system injection | ✅ | ✅ | via `session.hook("context")` |
-| Delegation to subagents | ✅ `task` | ✅ `subagent` | prompts rewritten for v2 |
-| Tools (ast-grep, webfetch, task_message, task_cancel, task_revive, wait_for_user, acp_run) | ✅ | ✅* | `*` ast-grep/webfetch need `@ast-grep/napi`/`jsdom` resolvable |
-| Slash commands `/deepwork` `/reflect` `/loop` | ✅ | ✅ | |
-| Message transforms (phase reminder, skills filter, image routing, display-name rewrite) | ✅ | ✅ | |
-| Event handling (session tracking, lifecycle) | ✅ | ✅ | |
-| Tool execute hooks (apply-patch recovery, task-session, json-recovery) | ✅ | ✅ | |
-| Built-in MCPs (context7, grep.app) | ✅ | ⚠️ config-only | v2 has no programmatic MCP hook; add 2 lines to `opencode.json` — see [below](#restoring-built-in-mcps-on-v2) |
-| `/preset` (interactive switcher) | ✅ | ❌ at load only | the switcher is a v1-TUI 3-level UI; on v2 set `"preset"` in the config file (applies at load) |
-| Foreground model fallback (rate-limit failover) | ✅ | ❌ | v2 locks the model at session creation; the plugin API has no per-prompt model override, session model-setter, or `/model` command, so mid-flight switching is impossible |
-| Orchestrator wake scheduler (`backgroundJobs.orchestratorWake`) | ✅ | ❌ | Requires host `session.get` / `todo` / `children` / `status` / `promptAsync`; the v2 shim lacks these APIs so the capability-gated feature stays inactive |
-| Multiplexer (tmux/zellij/herdr/cmux panes) | ✅ | ❌ | v1-TUI-pane integration; v2 renders subagents natively instead |
-| Companion app | ✅ | ⚠️ unverified | independent desktop app; test separately against v2 |
-| Default agent on new session | ✅ orchestrator | ⚠️ TUI shows `build` | v1 sets `default_agent`; v2's TUI ignores that field and defaults to the first agent in its list (`build`). `run`/API still default to orchestrator. See [limitations](#limitations) |
-
-## Installing on v2
-
-Add to `~/.config/opencode2/opencode.json`:
+The existing package install remains. In the beta checkout, the executable is
+`opencode2` but the global configuration root is still named `opencode`:
+
+Add to `~/.config/opencode/opencode.json`:
 
 ```json
 {
-  "plugin": ["oh-my-opencode-slim@latest"]
+  "plugins": ["oh-my-opencode-slim@latest"]
 }
 ```
 
-For local development, point at the built `dist/server.js` directly:
+For local development, use the v2 bundle directly:
 
 ```json
 {
-  "plugin": ["/path/to/oh-my-opencode-slim/dist/server.js"]
+  "plugins": ["/path/to/oh-my-opencode-slim/dist/server.js"]
 }
 ```
 
-Then build:
+Build the two host bundles with:
 
 ```bash
 bun install
-bun run build   # produces dist/index.js (v1) AND dist/server.js (v2)
-```
-
-Verify with `opencode2 run "list your specialist agents" --standalone` — the
-orchestrator should name explorer, librarian, oracle, designer, fixer.
-
-## Configuring models on v2
-
-Agent models are resolved the same way as v1 (per-agent `model` in
-`oh-my-opencode-slim.json`, or inherited from the session/host default). On v2,
-set a working provider+model in your v2 config or the plugin's config file so
-delegated subagents can run.
-
-> **Rate-limit fallback is not available on v2.** v2 locks a session's model at
-> creation; the plugin context exposes no per-prompt model override, no
-> session-level model setter, and no `/model` command. If you hit a 429/rate
-> limit, switch the model manually (start a new session or change the configured
-> model) — the plugin cannot do this automatically on v2.
-
-## Restoring built-in MCPs on v2
-
-v2 has no programmatic MCP-registration hook, so the plugin's two built-in
-remote MCPs are not auto-registered. They are plain remote URLs — copy this into
-your `~/.config/opencode2/opencode.json` to restore them:
-
-```json
-{
-  "mcp": {
-    "context7": {
-      "type": "remote",
-      "url": "https://mcp.context7.com/mcp",
-      "headers": { "CONTEXT7_API_KEY": "$CONTEXT7_API_KEY" }
-    },
-    "gh_grep": { "type": "remote", "url": "https://mcp.grep.app" }
-  }
-}
+bun run build
 ```
 
-(`context7` needs `CONTEXT7_API_KEY`; `gh_grep` needs nothing. Drop either key
-if unused.) The librarian agent uses these for library-docs lookup and
-GitHub-wide code search; without them it still works via `webfetch`.
-
-## Limitations
-
-### Interview
-
-`/interview` is supported on v2 through a marker command and a trailing-message
-context bridge. The bridge keeps an in-memory transcript projection from v2
-context and streamed text events, and uses the v2 session methods for prompts,
-notifications, and renames. The markdown document remains the durable source
-of truth; completion responses without `<interview_state>` rewrite the current
-spec while retaining frontmatter and Q&A history.
-
-These are **v2 API constraints**, not adapter gaps — they cannot be fixed in the
-plugin without v2 adding the corresponding capability:
-
-- **Foreground model fallback impossible.** v2's `SessionPromptInput` has no
-  `model` field, the plugin `SessionDomain` exposes only
-  `create/get/prompt/generate/command/synthetic/interrupt` (no model setter),
-  and there is no `/model` command. A session's model is fixed at creation, so
-  the plugin cannot switch models on a rate-limited foreground session.
-  v1-only.
-- **Interactive `/preset` switcher impossible.** The switcher is a three-level
-  v1-TUI UI (`@opentui/solid`). v2 slash commands are template-only (no
-  interactive UI, no execute handler). **Workaround:** set `"preset"` in
-  `oh-my-opencode-slim.json` — it applies at plugin load and resolves all agent
-  models correctly.
-- **No programmatic MCP registration.** v2's plugin context has no MCP domain.
-  Declare MCPs in `opencode.json` (snippet above).
-- **Multiplexer panes.** tmux/zellij/herdr integration is a v1-TUI feature; v2
-  renders subagents natively, so this is intentionally not ported.
-- **TUI default agent is `build`, not `orchestrator`.** v1 sets
-  `default_agent = "orchestrator"` in its config hook and the v1 TUI honors it.
-  The v2 adapter does call `ctx.agent.transform(draft => draft.default("orchestrator"))`,
-  which makes `run`/API default to the orchestrator — but the v2 TUI ignores the
-  `default_agent` config field entirely and instead defaults to the **first agent
-  in its list** (`list()[0]`, insertion order), which is v2's built-in `build`.
-  The plugin API offers no list-reorder, and `AgentDraft` has no "move to front".
-  Effect: `opencode2 run` and programmatic sessions use the orchestrator; opening
-  the v2 TUI / starting a new session there defaults to `build` (switch once; the
-  choice is persisted per-session via `saved.session[id].agent`, but each brand-new
-  session resets to `build`). Requires an upstream v2 change (TUI honoring
-  `default_agent`, or a list-order/default API for plugins) to fix.
-
-These are adapter/environment caveats that can be worked around:
-
-- **Path-based dev loading.** When v2 loads the plugin by absolute file path it
-  appends a `?mtime=` cache-busting query, which can break resolution of
-  externalized bare imports (`@ast-grep/napi`, `jsdom`) from the plugin's
-  `node_modules`. The plugin still loads (these are lazy-imported only by the
-  ast-grep and webfetch tools); install as a package or ensure the externals are
-  resolvable to enable those tools locally.
-- **directory source.** v2's plugin context does not expose the project
-  directory, so the adapter uses `process.cwd()`. Run `opencode2` from your
-  project root (or use `--standalone`, which sets cwd to the project).
+The build currently produces `dist/index.js` for v1 and `dist/server.js` for
+the v2 adapter. Run OpenCode v2 from the project directory, or use
+`--standalone`, because the adapter has no project-directory field and uses
+`process.cwd()`.
+
+## Adapter limitations
+
+These are limitations of the transitional implementation, not proof that the
+official v2 API lacks the capability:
+
+- The adapter must construct fake v1 input and translate v1 hook shapes. It
+  cannot provide the type or lifecycle guarantees of a native v2 plugin.
+- It uses a client shim with only selected operations and graceful no-ops.
+  Legacy code that expects v1 client state, task/session helpers, headers, or
+  UI behavior cannot be made equivalent by the shim.
+- The v1 factory and synthesized `config()` hook remain the source of agent,
+  tool, command, and runtime wiring. Failed bridges are logged and skipped
+  rather than failing setup as a native plugin should.
+- The adapter currently does not register built-in MCP definitions through the
+  v2 MCP domain. OpenCode v2 does provide that domain; native migration must
+  use it rather than treating MCP support as config-only.
+- The adapter's interview bridge reconstructs only an in-memory projection
+  from v2 context and events. It is not a durable, reload-safe v2 history
+  implementation.
+- Absolute-path development loading can expose externalized `jsdom` and
+  `@ast-grep/napi` resolution problems. Installing the package or making those
+  dependencies resolvable avoids this adapter/build issue.
+
+## Supported native v2 surface
+
+The beta Promise plugin API uses `define({ id, setup(context) })`; setup returns
+cleanup and registrations are disposed with plugin scope. Its domains include:
+
+- `agent`: inspect/update/remove agents, set the default, and transform the
+  agent catalog;
+- `tool`: add tools and hook `execute.before`/`execute.after`;
+- `command`: add executable command definitions and reload commands;
+- `session`: create/get/switch agent/switch model, prompt, generate, command,
+  synthetic, interrupt, rename, wait, and hook `context`, `model.request`,
+  `http.request`, or `http.response`;
+- `event`: subscribe to the server event stream;
+- `mcp`: transform, add, update, remove, and reload MCP configurations;
+- `shell`, `skill`, `storage`, and `websearch`: supported domain APIs for the
+  corresponding native integrations.
+
+Session `context` is the supported native replacement for v1 raw message
+transforms. Native code should mutate the v2 `system`, `messages`, and `tools`
+context directly, without converting through v1 `{ info, parts }` objects.
+
+## Native target architecture
+
+The target is a separate native Promise-plugin composition, not a more capable
+adapter:
+
+1. Keep host-neutral configuration, agent definitions, prompt construction, and
+   tool definitions in shared modules.
+2. Move the existing v1 composition behind an explicit `src/v1` boundary.
+3. Implement a native `define({ id, setup })` composition that registers agents,
+   tools, executable commands, MCPs, session context/model hooks, tool hooks,
+   and event handling through the official domains.
+4. Replace the client shim, local v2 type mirror, and interview bridge with
+   native context and lifecycle modules.
+5. Make required registration failures fail setup; do not silently continue
+   with a partial native installation.
+
+The native implementation must not invoke the v1 factory, convert v2 values to
+v1 hook payloads, or retain legacy lifecycle ownership merely to preserve
+parity.
+
+## Behaviors to deliberately retire
+
+Exact v1 parity is not possible through the supported v2 Promise API. The
+following behaviors must be explicitly retired from the native v2 target,
+rather than emulated with shims:
+
+- legacy background-job ownership and orchestrator wake scheduling;
+- child-pane multiplexer integration (tmux, zellij, herdr, and cmux);
+- runtime foreground-model failover;
+- tool permission prompt/cancellation interception;
+- initiator-header injection;
+- interview-history reconstruction after a plugin reload.
+
+The beta API also has no exact plugin hooks for compaction prompt replacement,
+permission-prompt interception, or synthetic text completion. Those behaviors
+need a product-level replacement or explicit retirement; they must not be
+presented as native parity.
+
+TUI toasts and the interactive `/preset` switcher are **not** required
+retirements. The beta plugin package exposes TUI participation through
+`tui: true` and the TUI context surface, so native migration should evaluate
+those controls as API opportunities. The current adapter does not bridge them;
+that is an adapter gap, not a claim that the native API cannot support them.
+
+Small-model selection is an inventory decision, not a predeclared retirement.
+The current implementation uses `runtime.smallModel()` for SmartFetch's
+secondary-model selection. The native migration must determine the supported
+v2 equivalent and its parity requirements during inventory.
+
+The official API does provide MCP registration, session model switching, and
+executable command registration. Those are migration opportunities, not
+retirements. In particular, the old documentation claim that v2 has no model
+setter or programmatic MCP surface is outdated for the beta checkout.
+
+The current adapter does not preserve the v1 `/preset` TUI experience. Native
+migration should decide whether the v2 TUI context can provide an equivalent
+interactive control or whether a documented noninteractive config/command
+workflow is preferable; it must not claim v1 UI parity before that work is
+verified.
+
+## Verification gates
+
+Validation is required at each migration boundary:
+
+1. **Architecture gate:** approve the host-neutral boundary, native domain
+   mapping, parity definition, and deliberate retirements.
+2. **Static/runtime gate:** compile, lint, and run unit tests for shared code,
+   the v1 host, and native registrations. Required registration failures must
+   be observable failures.
+3. **v2 host smoke gate:** using the beta checkout's embedded-host patterns and
+   `bun run dev:live`, prove plugin loading, agent/tool/command/MCP registration,
+   a session-context transform, tool lifecycle hooks, event handling, and
+   cleanup.
+4. **v1 compatibility gate:** retain the existing v1 load/smoke path until the
+   cutover is complete; verify that the v1 factory remains unchanged during
+   the transition.
+5. **Release-readiness gate:** verify the published package entry points,
+   install path, native v2 smoke artifact, documented retirements, and absence
+   of adapter-only claims before removing the transitional path.
+
+## Migration roadmap
+
+1. **Inventory:** map each v1 behavior to an official v2 domain, classify it as
+   native, adapter-only, or retired, and obtain approval for the retirements.
+2. **Separate composition:** introduce shared host-neutral config/prompt/tool
+   logic, move v1 composition to `src/v1`, and implement native Promise setup
+   with strict registration and cleanup.
+3. **Prove both hosts:** run the v2 host smoke and v1 compatibility gates in
+   parallel; compare supported behavior without expanding the shim.
+4. **Cut over v2:** make the native composition the v2 entry point, remove the
+   v1 adapter, client shim, local v2 type mirror, and interview bridge, then
+   retain v1 only for the verified cutover period.
+5. **Complete migration:** after release-readiness review, decide whether the
+   v1 host can be retired and remove remaining compatibility-only code.