Skip to content

Door front migration: design-code-proxy (18746) callers repoint to the façade — personas become routes first #1413

Open

Description

@dezren39

Part of #1146 (generic OpenAI façades on the server; design: specs/2026-11-01T04-10-00Z_design_openai-facades.md §8/§10 step 3, quirk §9.3). One issue per door front — this is the design-code door's turn under Drew's 2026-11-02 order to fold ALL proxy programs into OpenCode.

Door

bin/design-code-proxy.mjs (developing-today/code), port 18746, design-code-proxy.service active/running (After/Wants codex-proxy). Front: /v1/chat/completions, /v1/models. Backend: virtual design personas routing to codex / antigravity doors + AMR upstream (https://amr-link.open-design.ai/v1 appears in the deployed config as the direct opendesign provider).

Quirk that gates this door (§9.3)

Design-code's model ids are INVENTED PERSONAS — curated primary/fallback recipes plus an injected design system prompt — not upstream models. Façade rule: personas are expressed as routes in model_routes config (the recipe) plus the system prompt supplied by the caller or an agent definition; the façade does not grow a persona concept. This door's front retires only after its persona ids exist as route names.

Current state (verified 2026-11-02)

  • The deployed opencode.jsonc's design-code provider blocks on :18746 are currently COMMENTED OUT (four blocks); the AMR upstream is seated directly instead. The service still runs, and OpenClaw's HEARTBEAT.md probes :18746 /v1/models as a "lifeline" — a live monitoring consumer even if model traffic is zero.

Work

  1. Consumer inventory: grep :18746 / DESIGN_CODE_PROXY_PORT + persona model ids across repos, deployed configs, OpenClaw docs/heartbeat.
  2. Enumerate the persona ids from bin/design-code-proxy.mjs; express each as a model_routes entry (+ prompt home) in the deployed config via the normal config workflow; verify each route resolves and completes.
  3. Repoint any front caller (incl. the heartbeat probe) to the façade/routes; live-verify.
  4. Close the front: unit + bin file removed from developing-today/code via merged PR; switch in the normal queue. Record which personas, if any, were deliberately NOT carried over, with the reason, on Design: generic OpenAI façades on the server — /v1/chat/completions + /v1/responses, one translation layer, proxy doors retire #1146.

Acceptance criteria

  • A consumer inventory for :18746 / DESIGN_CODE_PROXY_PORT and the design-code persona ids is recorded (repos, deployed configs, OpenClaw heartbeat probe).
  • Every design-code persona id exists as a model_routes route name in the deployed config and resolves/completes in a live check, or is explicitly recorded on issue Design: generic OpenAI façades on the server — /v1/chat/completions + /v1/responses, one translation layer, proxy doors retire #1146 as deliberately not carried over, with the reason.
  • All front callers, including the OpenClaw HEARTBEAT.md :18746 probe, are repointed to the façade/routes and live-verified.
  • The design-code front is closed: unit and bin/design-code-proxy.mjs removed from developing-today/code via a merged PR.

Activity

  1. added
    kind/refactorCode restructuring with no behavior change
    source/humanFiled directly by a human
    priority/normalOrdinary priority (the default)
    effort/lLarge: multi-day or open-ended; expect staged check-ins
    meta/enrichedIntake review done: acceptance criteria, links, and normalized labels added
    on Nov 2, 2026
  2. dezren39 commented on Nov 2, 2026

    @dezren39
    Author

    Intake review

    Part of epic #1146 (OpenAI façades); design-code is the persona-gated door (§9.3 of the design spec): personas must become route names before the front retires. Filed by the proxy-fold coordinator (agent note on the issue). No open PR references this issue this run. Risks: the heartbeat 'lifeline' probe is a live consumer even at zero model traffic — repointing it is in the AC. Effort l: inventory, route authoring, and cross-repo verification.

    — fleet monitor intake pass, 2026-11-02. Labels normalized (kind/source/status/priority/effort) and acceptance criteria appended to the body; original body preserved above the new section.

  3. dezren39 commented on Nov 2, 2026

    @dezren39
    Author

    Gate 1 — credential story for design-code-proxy (:18746) — BEFORE integration work (#1146 comment 6584974808)

    Evidence gathered live on the laptop (amd-6) 2026-11-02 from the door source (developing-today/code bin/design-code-proxy.mjs, 517 lines, identical in ~/git/code and the deployed ~/code tree the unit executes), the live unit, the live process environment, and the backend doors' own sources. No credential values are recorded here — names, locations and ownership only.

    1. How callers authenticate to design-code itself

    It doesn't authenticate them. The server accepts any (or no) Authorization header for local dispatch. The header is only inspected as an AMR key carrier (below): the placeholder tokens design-local, local, none, dummy, test, opencode are explicitly treated as "no key". The commented-out provider blocks in the tracked opencode.jsonc used apiKey: "design-local" — a placeholder, not a secret. Merge consequence: the façade's own server auth (server password / Bearer, per the #1146 design §2) replaces this; no caller credential migrates, because none exists.

    2. Credential source today, per upstream

    Upstream What the door uses today Evidence
    codex door (:18744) Nothing. Local dispatch forwards only Content-Type/Content-Length — no Authorization header at all. The door rides the codex door's own auth. tryTarget() in design-code-proxy.mjs builds headers {Content-Type, Content-Length} only.
    antigravity-ls door (:18745, retired) → antigravity-direct (:18751 after this wave's repoint) Nothing, same as above. Same code path.
    AMR upstream (https://amr-link.open-design.ai/v1) No stored key in the door. hasUpstreamKey(): (a) env OPEN_DESIGN_API_KEY or AMR_API_KEY if set (>8 chars), else (b) the caller's own Authorization: Bearer <token> (minus placeholders above). If a key is present, the entire request is forwarded verbatim to AMR — no persona prompt injection, no recipe routing; on upstream error it falls back to local dispatch. Source hasUpstreamKey() + chat handler. Live check: the running unit's Environment= is only DESIGN_CODE_PROXY_PORT/DESIGN_CODE_PROXY_HOST, and the live process environment contains neither OPEN_DESIGN_API_KEY nor AMR_API_KEY — so today the AMR leg fires only when a caller supplies its own key.

    The AMR/Open Design key's fleet home is not the door: secrets/opendesign.sops.yaml in developing-today/code holds opendesign_api_key (sops-encrypted, at rest), and the OpenCode credential store (opencode.db, read-only check) holds an active credential row for integration opendesign — that is what the direct opendesign provider (the AMR upstream seated directly in the tracked config, baseURL https://amr-link.open-design.ai/v1, no apiKey in the tracked block) resolves through the fork's store machinery today.

    3. Merge pattern once personas are routes

    Personas become model_routes whose leaves are the providers that already own the credentials:

    • Antigravity leaves → provider antigravity-direct (:18751, placeholder apiKey: "antigravity-direct-local" in config). Its credential never touches opencode.db: bin/antigravity-direct.mjs reads the Antigravity CLI's OAuth token set from the OS keyring (Secret Service, service gemini / user antigravity) via secret-tool, caches the access token in memory only, and never writes tokens anywhere.
    • Codex leaves → provider openai-codex (:18744, placeholder apiKey: "codex-local"). The codex door syncs the Codex CLI login (~/.codex/auth.json) into the credential store: bin/codex-proxy.mjs syncCodexTokensToOpenCode() updates the active openai credential row in opencode.db in-process (node:sqlite), at startup and every 5 minutes — the existing sync-into-store example from Design: generic OpenAI façades on the server — /v1/chat/completions + /v1/responses, one translation layer, proxy doors retire #1146.
    • AMR/Open Design leaves (where a persona recipe is expressed via the direct provider instead of a local door) → provider opendesign, resolving the opendesign credential row from the store as above.

    So: read-in-place for antigravity-direct (keyring) and opendesign (store), sync-into-store for codex (existing door sync, unchanged). The persona routes themselves carry no credential material — exactly like every other route in the tracked config.

    4. Refresh ownership + failure mode (after the door is gone)

    • Antigravity OAuth: antigravity-direct refreshes via POST https://oauth2.googleapis.com/token with the keyring refresh token and the CLI's client id (Google does not rotate this refresh token, so this never invalidates the CLI's own login — verified in the door's source comments). Owner: the antigravity-direct door, unchanged by this wave. Failure mode if it lapses: antigravity leaves fail, route failover moves to the route's next target; the persona's design prompt and ordering are unaffected.
    • Codex: the Codex CLI owns the login; codex-proxy owns the sync into the store (5-minute cadence). Owner unchanged. Failure mode: stale openai row → codex leaves fail → failover to the antigravity leaf, same as the door's fallback today.
    • AMR/Open Design: the store's opendesign credential (sops file is the at-rest source). Owner: whoever rotates the Open Design key today — the door never owned it. Failure mode: opendesign leaves fail; local-door leaves of the same persona route are unaffected. Note the one behaviour that does not carry over: the door's "caller Bearer key ⇒ verbatim AMR forward, bypassing the persona" path. Under the façade a caller key is the server password, not an upstream key; callers who want raw AMR address the opendesign provider directly, as the deployed config already does. This is recorded as a named parity diff in Gate 2, not silently dropped.

    Gate 1 verdict: PASS — every upstream's credential source, merge pattern, refresh owner and failure mode is stated above with evidence. Integration work proceeds on this story.

  4. added a commit that references this issue on Nov 2, 2026
  5. dezren39 commented on Nov 2, 2026

    @dezren39
    Author

    Wave C wrap-up — gates passed, personas landed as routes, door NOT retired (blockers below)

    Gate 1 — credential story: PASS

    Posted before integration work: #1413 (comment) (no caller auth on the door; no stored AMR key in the door — caller-Bearer passthrough only, live unit env verified empty; routes resolve backend credentials in place: antigravity-direct keyring OAuth, codex sync-into-store, opendesign store row).

    #1409 unblock: DONE, issue CLOSED

    Code PR developing-today/code#4626 merged (mergeCommit 2f62ce6c900b4572b8a333cee708eacf70e57712), deployed to ~/code, service restarted; deployed design-code-flash call dispatched to 127.0.0.1:18751 in the service journal and returned a well-formed completion. Evidence + closure: #1409 (comment) — #1409 state CLOSED (API-verified).

    Personas → routes: DONE (11/11 carried over)

    Fork support (the gap: routes could not carry a system prompt, and the façade Generate path did not walk route candidates on failure):

    • Fork PR feat(routes): routes carry a system prompt + façade route failover — persona routes for design-code (#1413) #1453 MERGED (mergeCommit 0492fcd385c15d996bf04f2f81f1ec30565ca7c2): optional system on model_routes (schema → Definition → routing.system); Generate applies it leading the caller's system text on every candidate (door's exact composition) and suppresses the pass-through fast path for such routes; Generate complete/stream now walk routing.fallback candidates in order (stream: pre-emission only), each a ledgered attempt. Tests: core typecheck clean; 327 core tests across the affected files pre-merge (237 in the final merged tree's re-run of the Generate/parity/resolver files), server facade-parity 20, schema config 21 — all pass; OpenAPI regenerated.
    • Config PR code#4643 MERGED (mergeCommit 6f36af73999fbe501aa85c578859c46879bc0849): all 11 persona ids are now model_routes route names in the tracked opencode.jsonc with the door's recipes as ordered targets (antigravity legs on antigravity-direct :18751, codex legs on openai-codex :18744) and the design system prompt verbatim as each route's system. JSONC parse + per-route extraction verified programmatically.
    • Deliberately NOT carried over (also recorded on Design: generic OpenAI façades on the server — /v1/chat/completions + /v1/responses, one translation layer, proxy doors retire #1146): (1) the door's keyword fallback for unknown gpt/codex-shaped ids — unknown ids now fail selection cleanly in the façade envelope instead of being passed to the codex door under their own name; (2) the AMR caller-Bearer verbatim bypass — raw AMR remains the direct opendesign provider seat (Gate 1 story).

    Gate 2 — parity: PARITY-WITH-NAMED-DIFFS (one real-gap)

    The #1152 suite gained a design-code block (PR #1453, packages/core/test/facade-parity.test.ts), recorded from the door's code/fixtures plus live recordings taken 2026-11-02: persona inventory (11 recipes, no :18745 leg), non-stream parity (stub + live, usage included), stream grammar parity modulo the door's relayed empty initial role frame, system-prompt composition equality (applyRouteSystem vs the door's prepend), unknown-model 400 and all-backends-fail 502 outcome classes, AMR bypass. The failover walk + prompt application are pinned end-to-end in generate-persona-route.test.ts (primary 500 → fallback answers, prompt leading on both candidates, stream + non-stream).
    named diffs: error envelopes gain the OpenAI type (additive, §2); stream frame shift by the relayed empty role frame; AMR caller-key bypass (acceptable-by-design, Gate 1). REAL-GAP: persona routes are not advertised in the façade's /v1/models — ADVERTISED_MODELS is hardcoded to the six tier routes, so a persona route resolves/completes by name but is not listed (pinned by a parity test that fails when advertisement is fixed).

    Live verification (façade side)

    Scratch server from the #1453 worktree (real ServerFetch handler, persona-route config, in-memory DB) against the live backends: design-code-flash non-stream → 200, FACADE-PERSONA-OK, usage prompt_tokens: 72 (the door's same-shaped live call: 73 — the persona prompt is applied); gpt-6-luna stream → 200, persona model echo, streamed answer. Door-side live calls (pre-retirement baseline) are in the #1409 evidence.

    Consumer inventory for :18746 (AC)

    • Tracked opencode.jsonc: the design-code provider blocks are commented out (direct opendesign seat active instead) — no live model caller there.
    • OpenClaw: home/openclaw/workspace/HEARTBEAT.md probes http://127.0.0.1:18746/v1/models as a lifeline; home/openclaw/workspace/TOOLS.md + README.md document the door; home/openclaw/default.nix lists the unit among gateway dependencies and in the gateway prompt's proxy list.
    • home/common/default.nix: the design-code-proxy unit definition + a manual wrapper bin.
    • The door itself is the only live model-traffic consumer path found; route_attempt ledger design-code rows last appear 2026-10-24 (per the Door front migration: antigravity-ls-proxy (18745) callers repoint to the façade #1409 verification).

    Retirement: NOT done — precise blockers

    1. /v1/models advertisement real-gap (above): callers that enumerate — including the OpenClaw heartbeat lifeline probe — cannot discover persona routes from the façade. The probe repoint and the unit/bin removal wait on this fix; repointing the probe to a list that omits the personas would defeat its purpose.
    2. Deployed server rollout: the production OpenCode server is not yet running a build containing feat(routes): routes carry a system prompt + façade route failover — persona routes for design-code (#1413) #1453. Live verification above used a worktree scratch server with the merged code; the deployed config (code#4643) must not be picked up by a pre-feat(routes): routes carry a system prompt + façade route failover — persona routes for design-code (#1413) #1453 server build (unknown system field) — the normal switch/rollout ordering owns that step.
      With those two cleared: repoint HEARTBEAT.md's probe to the façade /v1/models, remove the unit + bin/design-code-proxy.mjs via a code PR, and this issue closes. The door stays running until then (service verified active after the repoint deploy).
  6. added a commit that references this issue on Nov 3, 2026
  7. developing-today commented on Nov 3, 2026

    @developing-today

    Retirement blocker #1 is removed: persona routes are now advertised at /v1/models.

    PR #1465 — "fix(facade): advertise configured model_routes at /v1/models, not a hardcoded list" — merged via merge commit 24ec863054488692e8906f1a5e5061a487d06018 (merged by developing-today[bot] at 2026-11-03T00:16:04Z; merge verified by API read-back).

    Next retirement step per the sequence recorded earlier in this issue: deploy a production server build containing this merge, then proceed with the repoint / zero-traffic window.

  8. added
    queue/queuedHas a queued task in the Supabase agent queue
    task/triageQueue packet kind: verify, reproduce, and route this item
    on Nov 3, 2026
  9. dezren39 commented on Nov 3, 2026

    @dezren39
    Author

    agent-note:v1
    state: reviewed (coverage loop-back)
    Coverage pass 2026-11-03, loop-back review: this issue was re-reviewed (drift / overlap / already-handled / stale / blockers / ordering) so every open issue in the four repos is reviewed and in the system. Verdict: still live; in the system as task 7158 (triage, queued, priority 30) in repo-health-opencode-pilot. Labels audited and synced. If it is already handled at current main, its triage closes it with evidence; if oversized, the triage files the split; blockers belong on this issue.
    Found via: coverage loop-back 2026-11-03

  10. dezren39 commented on Nov 3, 2026

    @dezren39
    Author

    🤖 Agent note · triaged — CONFIRMED live, door front still standing; work is code-repo side

  11. added
    status/blockedBlocked on something named in the issue/PR
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 3, 2026
  12. dezren39 commented on Nov 3, 2026

    @dezren39
    Author

    Update 2026-11-03: Drew ordered the design-code persona set removed outright — he does not run design personas ("the only design thing I have is opendesign because it has that name").

    Done in developing-today/code PR anomalyco#5106 (merged d6bf024a4a3d267f1043e1e3ca1e1b8a5d64474b), adopted on the laptop and verified live against the standing server:

    • All 11 persona routes added by code PR Add parentID support to all session events anomalyco/opencode#4643 (claude-haiku-5.5, gpt-6-luna, gpt-6.1-sol, deepseek-v4.1-flash, glm-5.3-flash, glm-5.3-flashx, mimo-v2.6-flash, mimo-v2.6-pro, design-code-pro, design-code-flash, open-design-pro), each carrying the door DESIGN_SYSTEM_PROMPT as its route system, are deleted from the tracked config.
    • The commented-out "open-design" door shell block (:18746) and its disabled_providers entry went with them (custom id, no catalog entry, so no tombstone — verified: no resurrection in the live provider registry after adoption).
    • Gate before deleting: no route fell back to a persona and no agent's model named one (agents address routes as opencode-route/); the runtime overlay and bin/sync-models.mjs carried no persona entries.
    • Untouched and verified live after adoption: the "opendesign" provider/pool and every route seating real opendesign models (a session pinned to opendesign/deepseek-v4.1-flash dispatched and reached the upstream — it answered with its current quota wall, 402 until the 21:02Z reset, which is the pool's known state, not a config fault). Served-config parity diff: exactly the removals, nothing else.

    The persona half of this issue's premise is therefore closed by removal rather than migration. The door program itself (code: bin/design-code-proxy.mjs) still exists; its retirement belongs to the door-retirement line, not this change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    effort/lLarge: multi-day or open-ended; expect staged check-inskind/refactorCode restructuring with no behavior changemeta/enrichedIntake review done: acceptance criteria, links, and normalized labels addedpriority/normalOrdinary priority (the default)source/humanFiled directly by a humanstatus/blockedBlocked on something named in the issue/PRtask/triageQueue packet kind: verify, reproduce, and route this item

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions