Summary
Extract the end-user surface (chat, connection switching, assistant-settings views) into a new packages/client (@openpalm/client) — a static, PWA-ready SvelteKit app — plus packages/ui-kit, a raw-source workspace package for shared components/icons/theme. packages/ui remains the host control plane and is not renamed or split further.
Ratified 2026-07-06: docs/technical/ui-client-split-assessment.md (options considered, evidence). Design: docs/technical/ui-runtime-modes-plan.md §6.11, Phase 5.
Why (short version)
- The transport forks anyway. Host modes chat through the same-origin server proxy with cookies; PWA/container modes need direct cross-origin guardian calls with Basic/Bearer and client-held connections. Two apps = one transport each; one app = mode branches in the data layer.
- A hosted install origin requires a static client. The
adapter-node control plane is machine-bound; https://app.openpalm.dev can only ever serve static files.
- Structural exclusion beats runtime 403s. Container and PWA artifacts contain no host-admin code and no
@openpalm/lib.
- SvelteKit has no per-build route exclusion (kit#6031 removed
config.kit.routes) — two thin apps sharing a workspace package is the grain-of-the-tool pattern.
- By LOC the client is the small slice: chat ≈ 6.6k vs admin/setup ≈ 22.5k of ~44k total. This is "move one-fifth", not "move four-fifths".
Design
packages/client/ # @openpalm/client — published, exact-pin delivery
svelte.config.js # adapter-static, SPA fallback
vite.config.ts # @vite-pwa/sveltekit (Phase 6)
src/routes/{chat,connections,assistant/settings}
src/lib/transport/ # ONE transport: guardian/OpenCode base URL + credentials
# connections in IndexedDB
packages/ui-kit/ # raw-source workspace package — NOT published
src/lib/components/ # common/, icons/, chrome primitives
src/lib/theme/ # tokens, design vocabulary (coordinates with #506)
Rules (plan §8):
client never bundles @openpalm/lib, has no src/lib/server/, never holds host credentials.
ui-kit is presentational only — no stores with server assumptions.
- Simplicity guardrail: two UI packages max (
client, ui-kit). No further splits.
Prerequisites (untangling, already scheduled in Phases 2–3)
After those land, this extraction is file relocation, not surgery.
Acceptance criteria
Unblocks
Related
Summary
Extract the end-user surface (chat, connection switching, assistant-settings views) into a new
packages/client(@openpalm/client) — a static, PWA-ready SvelteKit app — pluspackages/ui-kit, a raw-source workspace package for shared components/icons/theme.packages/uiremains the host control plane and is not renamed or split further.Ratified 2026-07-06:
docs/technical/ui-client-split-assessment.md(options considered, evidence). Design:docs/technical/ui-runtime-modes-plan.md§6.11, Phase 5.Why (short version)
adapter-nodecontrol plane is machine-bound;https://app.openpalm.devcan only ever serve static files.@openpalm/lib.config.kit.routes) — two thin apps sharing a workspace package is the grain-of-the-tool pattern.Design
Rules (plan §8):
clientnever bundles@openpalm/lib, has nosrc/lib/server/, never holds host credentials.ui-kitis presentational only — no stores with server assumptions.client,ui-kit). No further splits.Prerequisites (untangling, already scheduled in Phases 2–3)
endpoints-state↔chat-statebidirectional importNavbarstops importing chat stores into admin); chat stops importing the$lib/api.jsbarrelAfter those land, this extraction is file relocation, not surgery.
Acceptance criteria
packages/clientbuilds a self-contained static bundle;bun run checkclean in both appspackages/uichat is deleted once parity is confirmed@openpalm/clientadded to the publish DAG + exact-pin artifact table (OP_CLIENT_VERSION→PLATFORM_VERSION→ error)@openpalm/libUnblocks
Related
docs/technical/ui-client-split-assessment.md— decision recorddocs/technical/ui-runtime-modes-plan.md— §6.11, Phase 5ui-kit)