Skip to content

feat(extension): the hub-host client posture — amicode.hubServerUrl spawns nothing, the panel becomes one server (#1760 P4) - #1784

Merged
aarontrowbridge merged 5 commits into
live-test/stack-e2efrom
opencode/issue-1760-p4-hub-client
Oct 13, 2026
Merged

aarontrowbridge merged 5 commits into
live-test/stack-e2efrom
opencode/issue-1760-p4-hub-client

Conversation

@aarontrowbridge

Copy link
Copy Markdown
Member

Implements P4 (sharpened) — the hub-host client posture from #1760. TOP PRIORITY: the last prerequisite before P1's shared-secret can arm. Cut from live-test/stack-e2e @01ed5476 (the union base); PR targets that lane.

The confirmed root cause this kills

isFleetClientGuard (extension.ts:189) opens with if (process.platform !== "darwin") return false — on a Linux extension host (erlich, VS Code remote-SSH) the extension ALWAYS spawns its own service (:4095) + engine (--port 4094, racing shard 1 — the mystery three-bounce pattern), and that spawned engine becomes the panel's DEFAULT server: the global event stream + question dock ride a private bus where fleet events never arrive — stuck thinking while the engine runs fine, replies 404ing on an engine holding no questions. This one bug explains the remaining panel stuck-thinking AND the live-question-cards red (the #1756 shard-1 probe was the browser panel — one server, the hub — while the VS Code panel rode the spawn).

The four requirements (Aaron's contract, verbatim scope)

  1. amicode.hubServerUrl (string, default "" — the repo's empty-string-means-absent convention; a clientMode enum would need a companion URL key anyway). ALL platforms, explicit-only — never inferred from platform or fleet projection (inference is what made the darwin guard brittle). Armed → the hub branch runs no adopt-or-spawn, no ServerManager, no service, no keepalive — the spawn block is unreachable by branch structure. The darwin fleet posture is byte-identical (condition header only).
  2. One server: the framing IS the routing — opencodeReadyUrl = hub, every resolver rides it (SSE client, provider signal, frameUrl() for every panel site: ChatPanel ×9, DeckPanel, bug_report, compute-connect), and the app derives its ENTIRE server registry from the document origin → registry IS {hub}: global SDK, session views, question dock, replies. restartServer pokes the session (hysteresis-correct); goStandalone clears the setting before any spawn — the escape hatch never spawns under an armed setting.
  3. Reserved hub ports [4094–4097] guarded at every spawn port-pick path: the ServerManager.start choke point (all five construction sites), the opencodePort read site, the enginePort+1 derivation (a 4093 pin would have put the service ON 4094), and respawnForVault — with a RED test proving the unguarded 4096 pin health-checked against the live frontdoor and reported ready against the wrong server. The hub's own ops-spawned runner defaults are deliberately unguarded — it IS the hub.
  4. Verification: 16 new extension tests; the telemetry-visible assert — postEventStreamUrlAssert posts {"assert":"global-sdk-event-url","url":…} at stream start through the fix(app+ops): client telemetry — ungate the capture, root-relative ingest, heartbeat canary + freshness check #1748 channel, so a panel framed at the hub lands the proof in the live client-errors.log where the probe greps it; exact post-deploy probe commands in the notes (arm → reload → no-spawn output-channel proof → ss port proof → the assert line → the macbook ssereq.mjs probe).

Gates (orchestrator-run)

  • Extension suite: 241 files / 3756 passed / 48 skipped — baseline 238/3740/49 + 16 new, zero regressions
  • Extension typecheck: the 1 pre-existing sidebar_view.ts error, identical on base — zero new
  • App lane: 1188 pass with the 19 base-drift failures byte-identical to pristine; +2 new passing (the assert tests); app typecheck 4 errors = 4 on pristine; drift_gate.mjs PASS (895 files, minimal manifest diff)

Honest notes

  • activate() has no automated-test harness (repo-wide precedent — no test drives extension.ts); the wiring is thin delegation over the unit-tested seams, and the post-deploy probes are the end-to-end proof. A harness would be its own slice.
  • Boot smoke fails on any fresh worktree (the vendored engine binary isn't fetched) — pre-existing, unrelated.
  • The secret was never touched — zero occurrences of AMICODE_ENGINE_SHARED_SECRET in the diff; P1 stays in shadow until this lands + the P1-arming checklist in the notes passes (no-spawn proof, 4097-only panel, the assert line, macbook clients on the gateway).

@coderabbitai

coderabbitai Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 1caa5454-8aa3-4887-8650-cf2dddaa48d0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

… P4)

The hub-host client posture's pure core: readHubClientConfig (empty =
base posture byte-identical; a valid http(s) URL arms; garbage fails to
base with a named reason) and bootPosture (the explicit setting outranks
the inferred darwin fleet-client guard; unarmed keeps today's ladder).
The reserved hub port range 4094-4097 is declared (guard lands next).
 P4)

Nothing the extension starts may bind 4094-4097 (the hub plane's ports:
runner engine 4094, hub service 4095, frontdoor 4096/4097). A pinned
amicode.opencodePort in the range is refused with a named log line and
falls back to a free ephemeral port; the picked free port is re-checked.
Unguarded, a 4096-pinned spawn health-checked against the LIVE frontdoor
and reported ready while talking to the wrong server — the erlich
three-bounce pattern in miniature.
… surface (#1760 P4)

startHubClientSession mirrors the fleet-client tunnel poll's hysteresis
discipline against the configured hub URL: attach hands the caller the
hub URL for EVERY server resolution (ready URL, SSE, framed panel);
detach rides the failure-streak window; the session owns no spawn
surface at all. Injectable fetch/poll/interval keep it unit-tested
against a real mock hub.
 P4)

amicode.hubServerUrl armed → bootPosture hub-client branch: no adopt-or-
spawn, no ServerManager, no amicode service, no keepalive — the hub
session attaches every server resolution to the hub URL (the framed
app's registry IS { hub }: entry.tsx derives it from the document
origin). restartServer re-probes the hub (poke, hysteresis-correct);
Go Standalone clears the setting before the local spawn; deactivate
stops the session. The reserved-hub-port guard also wraps the two
opencodePort read sites and the service's enginePort+1 derivation.
The darwin fleet-client branch body is byte-identical (only the branch
condition carries the posture decision).
…nel (#1760 P4)

postEventStreamUrlAssert ships one greppable JSON line
{assert:global-sdk-event-url, url} to the serving origin's
/__amicode_client_log at stream start — the production-visible proof
that the panel's global SDK resolves the hub URL when the hub-host
client posture is armed (the headless probe greps the hub's
client-errors.log for it). Manifest hash synced (auto-refresh, minimal
diff: one file hash + extracted_at).
@aarontrowbridge
aarontrowbridge force-pushed the opencode/issue-1760-p4-hub-client branch from ca0fb06 to 0a825a9 Compare October 13, 2026 13:50
@aarontrowbridge
aarontrowbridge merged commit 39af8b2 into live-test/stack-e2e Oct 13, 2026
3 of 4 checks passed
aarontrowbridge added a commit that referenced this pull request Oct 13, 2026
# Conflicts:
#	packages/app-bundle/manifest.json
#	packages/app-bundle/overlay/packages/app/src/context/server-sdk.test.ts
#	packages/app-bundle/overlay/packages/app/src/context/server-sdk.tsx
#	packages/extension/src/extension.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant