# Local Bridge and Qoder Cloud Runtime Implementation Plan

> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

**Goal:** Add a three-field Qoder Cloud Agent runtime and an outbound local CLI bridge, then make create, evaluate, run, and API execution prefer an online local runtime with cloud fallback.

**Architecture:** Keep `runtime_engines` as the canonical runtime registry. Add a pure Web API Qoder client shared by Node and Cloudflare, plus D1/SQLite-backed bridge pairing, host, job, and event records. Normalize Qoder and bridge output into the existing Skill run SSE event contract so the React pages do not need separate execution UIs.

**Tech Stack:** React 18, Hono, Cloudflare Workers/D1, Node.js 24 SQLite, Node child processes, Web Fetch/SSE, Node test runner.

---

### Task 1: Qoder Cloud protocol contract

**Files:**
- Create: `cf-worker/src/runtime/qoder-cloud.js`
- Create: `cf-worker/src/runtime/qoder-cloud.test.js`

**Step 1: Write the failing tests**

Cover configuration validation, masked serialization, base URL selection, Agent and Environment validation requests, Session creation, `user.message` dispatch, SSE parsing, and terminal text extraction. Use a stub `fetch` that returns deterministic JSON and an SSE body.

```js
test('runs one qoder cloud turn and returns normalized events', async () => {
  const events = []
  for await (const event of runQoderCloudTurn({
    runtime: { qoder_pat: 'pt-secret', qoder_environment_id: 'env_1', qoder_agent_id: 'agent_1' },
    prompt: 'hello',
    fetchImpl: fakeQoderFetch,
  })) events.push(event)
  assert.deepEqual(events.at(-1), { t: 'done', ok: true, sessionId: 'sess_1' })
  assert.equal(events.find(e => e.t === 'delta').v, 'world')
})
```

**Step 2: Verify the tests fail**

Run: `node --test cf-worker/src/runtime/qoder-cloud.test.js`

Expected: FAIL because `qoder-cloud.js` does not exist.

**Step 3: Implement the pure protocol client**

Export:

```js
export const QODER_CLOUD_BASES = {
  cn: 'https://api.qoder.com.cn/api/v1/cloud',
  global: 'https://api.qoder.com/api/v1/cloud',
}
export function missingQoderCloudConfig(row) {}
export function rowToQoderCloudRuntime(row) {}
export function sanitizeQoderCloudPatch(body, existing) {}
export async function validateQoderCloudRuntime(row, { fetchImpl = fetch } = {}) {}
export async function listQoderCloudResources(row, { fetchImpl = fetch } = {}) {}
export async function* runQoderCloudTurn({ runtime, prompt, sessionId, fetchImpl = fetch, signal }) {}
```

Do not return `qoder_pat`. Translate `agent.message`, `agent.content_block_delta`, `session.error`, and `session.status_idle` into `delta`, `error`, and `done` events. Use bounded retry for 429 and 5xx and no retry for 401, 403, or 404.

**Step 4: Verify the tests pass**

Run: `node --test cf-worker/src/runtime/qoder-cloud.test.js`

Expected: PASS.

**Step 5: Commit**

```bash
git add cf-worker/src/runtime/qoder-cloud.js cf-worker/src/runtime/qoder-cloud.test.js
git commit -m "feat: add qoder cloud runtime protocol"
```

### Task 2: Runtime schema and priority resolution

**Files:**
- Create: `cf-worker/migrations/0017_qoder_cloud_bridge.sql`
- Modify: `cf-worker/src/runtime/runtime-registry.js`
- Create: `cf-worker/src/runtime/runtime-priority.test.js`
- Modify: `cf-worker/scripts/db-init.cjs`

**Step 1: Write failing resolver tests**

Test these ordered cases: explicitly bound ready runtime; online bridge local runtime; ready `qoder-cloud`; ready `agentrun`; no ready runtime. Assert that an offline bridge host is skipped and that the result contains a fallback reason.

**Step 2: Verify failure**

Run: `node --test cf-worker/src/runtime/runtime-priority.test.js`

Expected: FAIL because online bridge readiness and Qoder Cloud are unknown.

**Step 3: Add migration and runtime row**

Add Qoder fields to `runtime_engines` and create bridge tables:

```sql
ALTER TABLE runtime_engines ADD COLUMN qoder_region TEXT DEFAULT 'cn';
ALTER TABLE runtime_engines ADD COLUMN qoder_pat TEXT;
ALTER TABLE runtime_engines ADD COLUMN qoder_environment_id TEXT;
ALTER TABLE runtime_engines ADD COLUMN qoder_agent_id TEXT;
ALTER TABLE runtime_engines ADD COLUMN fallback_enabled INTEGER DEFAULT 1;

CREATE TABLE runtime_bridge_pairings (...);
CREATE TABLE runtime_bridge_hosts (...);
CREATE TABLE runtime_bridge_jobs (...);
CREATE TABLE runtime_bridge_events (...);
```

Seed `qoder-cloud` with protocol `qoder-cloud`, purposes `create/evaluate/run/api`, disabled by default, and no secret values.

**Step 4: Implement readiness and priority**

Update registry serialization, patch validation, `setActiveRuntime`, and `resolveRuntimeForPurpose`. Keep direct `local-cli` readiness for Node and add bridge-host online readiness as an injectable resolver input.

**Step 5: Run migration and tests**

Run:

```bash
cd cf-worker
node scripts/db-init.cjs
cd ..
node --test cf-worker/src/runtime/runtime-priority.test.js cf-worker/src/runtime/qoder-cloud.test.js
```

Expected: migrations apply once and tests PASS.

**Step 6: Commit**

```bash
git add cf-worker/migrations/0017_qoder_cloud_bridge.sql cf-worker/src/runtime/runtime-registry.js cf-worker/src/runtime/runtime-priority.test.js cf-worker/scripts/db-init.cjs
git commit -m "feat: register qoder cloud and runtime fallback"
```

### Task 3: Qoder configuration routes and Node execution

**Files:**
- Modify: `cf-worker/src/routes/runtimes.js`
- Modify: `cf-worker/src/routes/skill-run.js`
- Modify: `cf-worker/src/routes/eval.js`
- Create: `cf-worker/src/routes/qoder-cloud-runtime.test.js`

**Step 1: Write failing route tests**

Test that `PATCH /api/runtimes/qoder-cloud` accepts only region, PAT, Environment ID, Agent ID, scopes, enabled, fallback, and notes; `GET /resources` never returns the PAT; `POST /test` performs Agent and Environment validation; Skill runs select Qoder and preserve `sess_` IDs.

**Step 2: Verify failure**

Run: `node --test cf-worker/src/routes/qoder-cloud-runtime.test.js`

Expected: FAIL with unsupported protocol.

**Step 3: Add routes**

Add:

```text
GET  /api/runtimes/qoder-cloud/resources
POST /api/runtimes/qoder-cloud/test
```

Route Qoder patches through `sanitizeQoderCloudPatch`. In Skill execution, branch on `runtime.protocol === 'qoder-cloud'`, compose the existing system/Agent/Skill prompt, stream normalized Qoder events, and preserve the external Session ID in the internal session metadata.

**Step 4: Verify tests**

Run: `node --test cf-worker/src/routes/qoder-cloud-runtime.test.js cf-worker/src/routes/skill-run-*.test.js`

Expected: PASS.

**Step 5: Commit**

```bash
git add cf-worker/src/routes/runtimes.js cf-worker/src/routes/skill-run.js cf-worker/src/routes/eval.js cf-worker/src/routes/qoder-cloud-runtime.test.js
git commit -m "feat: execute skills with qoder cloud"
```

### Task 4: Cloudflare Lite parity

**Files:**
- Modify: `cf-worker/src/cloudflare-lite.js`
- Create: `cf-worker/src/runtime/cloudflare-lite-qoder.test.js`

**Step 1: Write failing parity tests**

Use a mock D1 and mock fetch to assert that Lite lists Qoder without secrets, saves/tests it, chooses it for `/api/skill-run`, and returns normalized SSE events.

**Step 2: Verify failure**

Run: `node --test cf-worker/src/runtime/cloudflare-lite-qoder.test.js`

Expected: FAIL because Lite only recognizes AgentRun.

**Step 3: Implement parity**

Import the pure Qoder helpers. Replace `activeAgentRun`-only selection with a purpose-aware cloud runtime resolver. Keep AgentRun behavior unchanged and add the Qoder branch to runtime list, patch, active, test, config, evaluation, and Skill stream endpoints.

**Step 4: Verify tests**

Run: `node --test cf-worker/src/runtime/cloudflare-lite-qoder.test.js cf-worker/src/runtime/qoder-cloud.test.js`

Expected: PASS.

**Step 5: Commit**

```bash
git add cf-worker/src/cloudflare-lite.js cf-worker/src/runtime/cloudflare-lite-qoder.test.js
git commit -m "feat: support qoder cloud on cloudflare lite"
```

### Task 5: Minimal Qoder configuration UI

**Files:**
- Modify: `src/services/api.js`
- Modify: `src/pages/LlmSettings.jsx`
- Modify: `src/components/ProviderIcon.jsx`
- Create: `src/services/qoderCloudUi.test.js`

**Step 1: Write failing UI contract tests**

Assert that the runtime settings source contains exactly the visible Qoder fields `PAT`, `Environment`, and `Agent`, offers select-or-manual input, masks saved secrets, and calls the resource endpoint only after a PAT exists.

**Step 2: Verify failure**

Run: `node --test src/services/qoderCloudUi.test.js`

Expected: FAIL because Qoder Cloud UI is absent.

**Step 3: Implement API and form**

Add `listQoderCloudResources()` to `api.js`. Add `QoderCloudFields` to `LlmSettings.jsx` with a China/Global advanced setting collapsed by default. The primary visible form contains PAT, Environment, and Agent. Display masked saved PAT state and readiness errors without returning the token.

**Step 4: Verify UI and build**

Run:

```bash
node --test src/services/qoderCloudUi.test.js
npm run build
```

Expected: PASS and Vite build succeeds.

**Step 5: Commit**

```bash
git add src/services/api.js src/pages/LlmSettings.jsx src/components/ProviderIcon.jsx src/services/qoderCloudUi.test.js
git commit -m "feat: add minimal qoder cloud settings"
```

### Task 6: Bridge domain service and connector API

**Files:**
- Create: `cf-worker/src/runtime/runtime-bridge.js`
- Create: `cf-worker/src/runtime/runtime-bridge.test.js`
- Create: `cf-worker/src/routes/runtime-bridge.js`
- Modify: `cf-worker/src/index.js`
- Modify: `cf-worker/src/cloudflare-lite.js`

**Step 1: Write failing bridge tests**

Test one-time pairing exchange, hashed host credentials, heartbeat expiry, capability advertisement, atomic job claim, lease expiry, ordered event upload, completion, cancellation, and host revocation.

**Step 2: Verify failure**

Run: `node --test cf-worker/src/runtime/runtime-bridge.test.js`

Expected: FAIL because bridge service is absent.

**Step 3: Implement bridge service and endpoints**

Platform-admin endpoints:

```text
POST   /api/runtime-bridge/pairings
GET    /api/runtime-bridge/hosts
PATCH  /api/runtime-bridge/hosts/:id
DELETE /api/runtime-bridge/hosts/:id
```

Connector endpoints:

```text
POST /api/runtime-bridge/connect
POST /api/runtime-bridge/heartbeat
POST /api/runtime-bridge/jobs/claim
POST /api/runtime-bridge/jobs/:id/events
POST /api/runtime-bridge/jobs/:id/complete
```

Use SHA-256 hashes for credentials, constant-shape authorization failures, a 60-second heartbeat window, and a renewable job lease.

**Step 4: Verify tests**

Run: `node --test cf-worker/src/runtime/runtime-bridge.test.js`

Expected: PASS.

**Step 5: Commit**

```bash
git add cf-worker/src/runtime/runtime-bridge.js cf-worker/src/runtime/runtime-bridge.test.js cf-worker/src/routes/runtime-bridge.js cf-worker/src/index.js cf-worker/src/cloudflare-lite.js
git commit -m "feat: add outbound runtime bridge service"
```

### Task 7: Local bridge CLI and execution integration

**Files:**
- Create: `scripts/aipros-runtime-bridge.mjs`
- Create: `scripts/aipros-runtime-bridge.test.mjs`
- Modify: `package.json`
- Modify: `cf-worker/src/routes/skill-run.js`
- Modify: `cf-worker/src/cloudflare-lite.js`

**Step 1: Write failing CLI tests**

Test Codex, WorkBuddy, Qoder, and Trae detection; safe argument templates; temporary workspace creation; stdin prompt delivery; cancellation; output normalization; and cleanup. Use fake executables in a temporary PATH.

**Step 2: Verify failure**

Run: `node --test scripts/aipros-runtime-bridge.test.mjs`

Expected: FAIL because the CLI is absent.

**Step 3: Implement the CLI**

Add `npm run runtime:bridge -- connect <pairing-url>`. Persist the host credential under the user's config directory with owner-only permissions. The process sends heartbeats, claims jobs, materializes package input, executes the advertised adapter, uploads events, and handles SIGINT cleanly.

**Step 4: Integrate job dispatch**

When the resolver selects an online bridged `local-cli`, enqueue a bridge job and relay its events to the existing stream. If the host disappears before claim, resolve the next fallback runtime. Do not replay a claimed job after it has emitted a side-effect event.

**Step 5: Verify tests**

Run:

```bash
node --test scripts/aipros-runtime-bridge.test.mjs cf-worker/src/runtime/runtime-bridge.test.js
node --test cf-worker/src/routes/skill-run-*.test.js
```

Expected: PASS.

**Step 6: Commit**

```bash
git add scripts/aipros-runtime-bridge.mjs scripts/aipros-runtime-bridge.test.mjs package.json cf-worker/src/routes/skill-run.js cf-worker/src/cloudflare-lite.js
git commit -m "feat: run skills through local runtime bridge"
```

### Task 8: Bridge onboarding UI and end-to-end verification

**Files:**
- Modify: `src/services/api.js`
- Modify: `src/pages/LlmSettings.jsx`
- Create: `src/services/runtimeBridgeUi.test.js`
- Modify: `README-LITE-TEMPLATE.md`
- Modify: `README-STUDENT.md`

**Step 1: Write failing onboarding tests**

Assert the settings page can create a pairing, displays two copyable commands, polls host status, shows detected runtimes, and provides pause/revoke controls.

**Step 2: Verify failure**

Run: `node --test src/services/runtimeBridgeUi.test.js`

Expected: FAIL.

**Step 3: Implement onboarding and docs**

Add bridge API calls and a zero-knowledge onboarding card. Show online/offline/paused status, last heartbeat, detected CLI versions, and explicit warning that host tasks can execute tools in temporary workspaces.

**Step 4: Run the full automated suite**

Run:

```bash
npm run build
node --test src/services/*.test.js scripts/*.test.mjs
node --test cf-worker/src/runtime/*.test.js cf-worker/src/routes/*.test.js
```

Expected: PASS.

**Step 5: Run the local end-to-end smoke test**

Start the Node backend and frontend, create a pairing, start `npm run runtime:bridge`, and submit a real `RUNTIME_BRIDGE_OK` Skill run through an installed CLI. Verify host online status, job claim, streamed output, done event, and fallback after stopping the bridge.

If `QODER_PAT`, `QODER_CLOUD_AGENT_ENVIRONMENT_ID`, and `QODER_CLOUD_AGENT_ID` are available, run the same smoke prompt through Qoder Cloud. Otherwise run the Qoder contract test against the local deterministic mock and clearly report that live credentials were unavailable.

**Step 6: Commit**

```bash
git add src/services/api.js src/pages/LlmSettings.jsx src/services/runtimeBridgeUi.test.js README-LITE-TEMPLATE.md README-STUDENT.md
git commit -m "docs: add runtime bridge onboarding and verification"
```
