You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
How to use this issue. Each work item below is written to be handed to a coding agent almost verbatim: it states the goal, the evidence (with file:line), the concrete scope, acceptance criteria, and strategic risk. Copy a ### WI-N block into your agent's prompt, add "open a PR against main", and go. Items are ordered by leverage. Verify the ⚠️ items with the owner before deleting anything.
Where this came from
A structural audit of the monorepo built from a graphify knowledge graph (2,764 files → 16,839 nodes / 44,925 edges, deterministic tree-sitter AST), cross-referenced against the company 2028 plan (FUTURE.md / MILESTONES.md M1–M4). Companion doc: GRAPHIFY_FINDINGS.md (per-file bug findings). This issue is the strategy-level layer: what to simplify, delete, and re-architect so the code maps to where revenue actually is.
The reframe (root cause of most items)
The code describes itself as "an open-source control plane for MCP traffic." By mass and by strategy that's a stale label:
56% of the app is frontend (apps/mesh/src/web = 152k LOC). The three heaviest UI areas — chat (25k), sandbox/preview (19k), sections-editor (18k) — are the agentic operator console (M2) and the agentic site editor (M3). That is the product.
The MCP plumbing (connections, registry, virtual-MCPs, bindings, event-bus) is real, but it's substrate for the agentic editor + operator — not a standalone "MCP mesh" offering.
Everything built to satisfy "we are a generic MCP mesh" is a cut candidate. Everything built for "agentic commerce editor + operator" is an invest candidate. Guiding principle: the worst thing is to optimize what shouldn't exist.
Measured code mass (LOC, ts/tsx, excl. tests):
Area
LOC
Maps to
web/components/chat
24.9k
M2 operator console (keep)
web/components/sandbox
19k
M3 editor / M4 preview substrate (keep)
web/components/sections-editor
18.2k
M3 agentic editor (keep)
api/
25.8k
mixed
tools/
21.2k
mixed — registry(32), virtual(22) over-built
storage/
15.3k
mixed
packages/sandbox
19k
M4 deploy substrate (keep + extend)
Group A — Docs debt (do first, cheapest, highest-leverage)
WI-1 — Rewrite the CLAUDE.md "Event Bus" section to match reality
Label: documentation · Risk: none
Problem.CLAUDE.md's largest architecture section documents an Event Bus (CloudEvents v1.0, 20-attempt backoff, deliverAt, cron, 6 MCP tools EVENT_PUBLISH/EVENT_SUBSCRIBE/…, a worker, polling.ts, nats-notify.ts) that does not exist in code. Any coding agent reading it will build on a phantom.
Evidence (verified):
apps/mesh/src/tools/eventbus/ — directory does not exist (CLAUDE.md claims 6 tools live here).
packages/bindings/src/well-known/event-bus.ts — does not exist (only event-subscriber.ts is present).
What does exist: apps/mesh/migrations/008-event-bus.ts (schema), packages/runtime/src/events.ts (~599 LOC engine). Producers: exactly 1 (apps/mesh/src/api/routes/registry/public-publish-request.ts:103 inserts event_deliveries). Subscribers: 0 (nothing inserts event_subscriptions). Consumers/workers: 0.
Scope. Edit CLAUDE.md: replace the entire Event Bus section (files, tools, bindings, ON_EVENTS, NotifyStrategy, config) with an accurate short note — "an events schema + engine exist (runtime/src/events.ts, migration 008) with one producer and no consumers; the working trigger path is AutomationEventDispatcher + DBOS (see WI-2/WI-7)." Do not delete code in this WI — that's WI-6.
Acceptance: CLAUDE.md no longer references any file/symbol that grep can't find; a fresh reader can trust every path in the Event Bus section.
Group B — Delete / collapse over-built control-plane machinery
Removing these deletes ~3–4k LOC and three "we're a generic MCP mesh" fictions that keep pulling design attention off the commerce product.
WI-6 — Delete (or consciously defer) the Event Bus engine
Label: refactor · Risk: LOW ⚠️(confirm with owner: aspirational-but-unbuilt vs planned-next)
Evidence: see WI-1. 1 producer, 0 subscribers, 0 consumers, 0 workers. The real trigger feature bypasses it (AutomationEventDispatcher + DBOS idempotency keys).
Scope. Remove packages/runtime/src/events.ts event-bus engine paths, the events / event_subscriptions / event_deliveries schema (new down-migration; keep 008 in history), well-known/event-subscriber.ts binding, and the storage types. Redirect the single producer (public-publish-request.ts:103) to a direct call or drop it if the publish endpoint is itself unused (check first).
Acceptance: app builds and boots; bun run check + bun test pass; no event_subscriptions/event_deliveries references remain; a migration cleanly drops the tables.
WI-2 — Collapse the Virtual-MCP "3 strategies" abstraction
Label: refactor · Risk: LOW
Problem. Docs/README describe three runtime strategies (full-context / smart-selection / code-execution). Only passthrough ships.
Evidence:apps/mesh/src/mcp-clients/virtual-mcp/passthrough-client.ts is the only impl; the strategy field is a vestigial single-member union — virtual-mcp/index.ts:71_strategy: "passthrough" with the comment "Kept for backward compatibility, always uses passthrough."VirtualMCPEntitySchema has no strategy field. smart-selection/code-exec exist only on worktree branch happy-nibbling-matsumoto (where migration 012 self-labels them "metadata for now").
Scope. Remove the _strategy union and any dead branching; update apps/docs/ + CLAUDE.md to stop claiming three strategies until the worktree lands. Leave one clean extension seam if desired, but no dead code.
Acceptance: no _strategy union in shipped code; docs describe only what ships; typecheck + tests pass.
WI-4 — Prune unused well-known bindings
Label: refactor · Risk: LOW
Evidence (packages/bindings/src/well-known/):prompt.ts (PROMPTS_BINDING — 0 uses), registry.ts (REGISTRY_APP_BINDING — 0 external refs), event-subscriber.ts (removed by WI-6), and the client halves of assistant.ts / ai-gateway.ts / brand.ts (unused). Keep the 4 real ones:collections.ts (18 importers — backbone of the list/get tool surface), object-storage.ts (14 refs), trigger.ts (used in configure-trigger.ts:35); language-model.ts is @deprecated (see connection/list.ts:67) — mark for removal, don't break callers yet.
Scope. Delete the unused binding constants + their exports; leave collections/object-storage/trigger untouched.
Acceptance:bun run check passes; knip reports no new unused exports; grep confirms 0 references to the removed constants.
WI-6b — Derive registry-metadata.ts from tool definitions
Label: refactor · Risk: LOW
Evidence:apps/mesh/src/tools/registry/registry-metadata.ts — ~41KB, ~142 hand-maintained {name, description, category} entries with a manual "keep in sync with index.ts" warning (chronic drift hazard; flagged as a god-file by the graph).
Scope. Generate the metadata from the tool defineTool() definitions (name/description already live there; add category to the tool def if missing) instead of a parallel hand-maintained table.
Acceptance:registry-metadata.ts no longer hand-lists tools; the registry UI shows identical categories/descriptions; adding a tool requires no metadata edit.
Group C — Heavier simplifications (verify usage first)
WI-3 — Shrink or externalize the Registry MCP-monitor subsystem
Label: refactor · Risk: LOW-MED ⚠️(confirm no customer depends on health scores)
Evidence:apps/mesh/src/tools/registry/monitor-run-start.ts = 1,262 LOC / 38KB — the graph's #15 god-file. It's a full LLM agent that connects to third-party MCPs and exercises their tools (full_agent vs health_check modes), backed by 8 monitor tools + storage — ~35% of the 32-tool registry domain, for a catalog QA feature, not the revenue path.
Scope (pick one): (a) reduce to a plain listTools reachability check, or (b) extract the LLM-driven tester into a separate package/service. Also: fold the duplicate COLLECTION_REGISTRY_APP_* read tools into REGISTRY_ITEM_* with a privateOnly flag (collection-registry-app.ts:14-30 mirror REGISTRY_ITEM_* with one filter), and drop REGISTRY_AI_GENERATE if the UI doesn't call it (check web/ first).
Acceptance: registry catalog still lists/reaches MCPs; god-file gone or moved out of apps/mesh; tool count in the domain drops; no frontend caller breaks.
WI-5 — Reconsider the OpenAI-compatible gateway endpoint
Label: refactor · Risk: MED ⚠️(verify external API-key consumers before removing)
Evidence:apps/mesh/src/api/routes/openai-compat.ts = 773 LOC — a generic OpenAI-compatible gateway. Pure control-plane positioning; no tie to commerce/editor revenue.
Scope. Establish whether any external consumer hits this (logs/analytics). If not, externalize to the Deco AI Gateway service and remove from mesh. If yes, document the dependency and leave it.
Acceptance: a decision recorded (remove / externalize / keep-with-reason) backed by usage data.
Group D — Deployment: previews and prod are the SAME investment
Strategic anchor (M4, MILESTONES.md):"One platform — GCP cluster shut down, sandboxes-as-production, MCP-native." Today: Argo CD GitOps (decocms/argo-deploy-mesh, manual update-image-tag.yml), GCP k8s, staging→prod promoted by hand, no deploy previews for Studio itself.
Key finding: you already built ~80% of a preview system. A sandbox is a per-(user, branch) K8s SandboxClaim that shallow-clones a repo → installs deps → runs dev/start → gets a stable URL <branch-slug>-<hash>.preview.<domain> (per-claim Gateway HTTPRoute + wildcard TLS) → auto-GCs on 15-min idle. SANDBOX_START already does this for arbitrary GitHub repos. Branch → running app → stable URL → auto-teardown already works. The gaps for "Studio-of-a-branch preview" are the same gaps for "sandboxes-as-production."
WI-7 — Add a build step to the sandbox daemon setup pipeline
Label: enhancement · Risk: LOW · Prereq for WI-9/WI-10
Evidence:packages/sandbox/daemon/setup/orchestrator.ts runs a FIFO pipeline clone → install → start; start-commands are hard-coded to WELL_KNOWN_STARTERS = ["dev","start"] in packages/sandbox/shared.ts:18. There is no build step — apps run in Vite dev mode only.
Scope. Extend the Step pipeline to clone → install → build → start and broaden start-command resolution (support a built/production-serve command). Reuse everything else (clone, golden cache, probe, :9000 reverse proxy, HTTPRoute).
Acceptance: a repo with a build script produces a built app served behind the existing preview URL; existing dev/start repos still work unchanged.
Evidence: three accelerators/GC mechanisms exist but default off: warm pool (SandboxWarmPool CRD, deploy/helm/sandbox-env/templates/sandbox-warm-pool.yaml, warmPool.enabled:false); golden cache (GOLDEN_CACHE_ENABLED, ~1s vs the 180s readiness timeout); preview Gateway + wildcard cert + housekeeper (sandbox-preview-gateway.yaml, sandbox-preview-cert.yaml, files/housekeeper-sweep.sh).
Scope. Flip the Helm flags; provision the manual infra they need (wildcard DNS *.<domain>, a DNS-01 ClusterIssuer).
Acceptance: cold sandbox boot drops to low-single-digit seconds; previews are reachable over TLS at <handle>.preview.<domain>; idle sandboxes + orphan HTTPRoutes auto-clean.
WI-9 — Ephemeral Postgres + secret injection per sandbox
Label: enhancement · Risk: MED · (biggest net-new; shared prereq for previews AND prod)
Evidence: a sandbox provisions no database — it only clones+runs a repo. Studio needs Postgres + NATS + Better Auth + secrets. Injection channel exists (EnsureOptions.env, frozen plaintext — runner.ts:1075-1098); egress is locked down by a netinit iptables init-container (allows only 53/443 out; RFC1918 blocked — sandbox-template.yaml:125-202).
Scope. In the ensure/SANDBOX_START path: provision a branch/template-cloned ephemeral Postgres, run migrate + better-auth:migrate + seed, inject DATABASE_URL + BETTER_AUTH_SECRET + ENCRYPTION_KEY + OAuth keys via EnsureOptions.env, and add the DB host to the netinit egress allowlist.
Acceptance: a sandbox can boot a full Studio instance with an isolated DB and working auth; the DB is torn down with the sandbox.
Evidence: CI already builds the Studio image and dispatches to deco-apps-cd (release-mesh.yaml:564-571, repository_dispatch: studio-image-bump).
Scope. Add a PR workflow that calls ensure/SANDBOX_START with the Studio repo + PR branch + build-mode (WI-7) + ephemeral DB (WI-9), then comments <handle>.preview.<domain> on the PR and tears down on close.
Acceptance: every PR gets a working, isolated Studio preview URL posted as a comment; the sandbox GCs when the PR closes.
WI-11 — Durable/pinned claim mode + decouple app liveness from the daemon
Label: enhancement · high priority · Risk: MED · (the real gate for sandboxes-as-production)
Evidence: ephemerality is a design invariant — 15-min idle-TTL deletes claim+pod (runner.ts:148), RO rootfs, state is only git+org-fs. And app liveness is coupled to the daemon's single-event-loop /health: one missed poll → the whole sandbox is torn down (CLAUDE.md rule #8; see the daemon-blocking bugs in GRAPHIFY_FINDINGS.md#1/#2). A CPU-bound production request would kill the "server."
Scope. Add a durable claim mode (no shutdownTime / long TTL) and run the app as a daemon-supervised task with a forgiving health model, so a busy app process can't trip one-miss-dead teardown. Reuse the claim/adopt/resurrect machinery.
Acceptance: a pinned sandbox survives idle + a sustained CPU-bound request without teardown; recovery still works for genuinely dead pods.
Group E — Strategy gaps (not cuts — where the code is thin vs the plan)
Not immediately actionable as refactors, but the cuts above exist to free attention for these. Flagged so they're on the radar.
M1 diagnostic front-door lives outside this repo — engine is an external CF Worker (COMMERCE_DISCOVERY_MCP_URL = "https://commerce-skills.deco-cx.workers.dev/api/v2/mcp", packages/mesh-sdk/src/lib/constants.ts:38). This repo has only glue (web/routes/commerce-onboarding/, tools/commerce-discovery/). And it is not the default onboarding — /commerce-onboarding (web/index.tsx:187) has zero internal navigation, entered only via marketing links with ?siteUrl; the app lands on org-home (web/index.tsx:111). Strategy M1 says "diagnostic is Studio's default onboarding." Self-service Stripe billing is not in this repo (no Stripe SDK).
M2 operator pillars are largely unbuilt — no per-org long-term memory ("mothership"), no briefs, no inbox operator mode (sidebar/footer/inbox.tsx is just invites), no real task management (task-groups = thread grouping). "System Health" exists only as a raw MCP call-log viewer (monitoring/), not the client-site-health product M2 describes.
Group F — Frontend ↔ API boundary
Should we separate the frontend from the API? Analyzed: no full split — it's already separate where it counts, and the remaining coupling is mostly a feature. Build (build:client→dist/client vs build:server), image (nginx -web container vs Bun -api container with independent tags — deploy/helm/studio/templates/deployment.yaml:66-110), and runtime (SPA → HTTP → API via @decocms/mesh-sdk, 207 files) are already separated. Only the source tree is shared, and web/ → backend imports are almost all import type (erased at build; end-to-end type safety is worth keeping). A full apps/web package split would break ~240 type imports, duplicate vite/tailwind/react-compiler/tsconfig config, and add a versioned contract — high cost, low runtime gain. The real risk is the handful of value imports that leak server code into the browser bundle. Fix that with a lint boundary, not a split.
WI-12 — Enforce the web ↔ server import boundary (lint rule) ✅ shipped as PR #4469
move shared Zod schemas/constants to @/shared or mesh-sdk, or import type-only. e.g. web/components/details/connection/connection-sidebar.tsx:2 → @/tools/connection/schema; web/components/connections/create-connection-dialog.tsx:54; web/views/virtual-mcp/types.ts:5; web/views/registry/* → @/tools/registry/shared (PLUGIN_ID, MONITOR_AGENT_DEFAULT_SYSTEM_PROMPT); web/views/settings/org-role-detail.tsx:6 → @/tools/registry-metadata
@/ai-providers (model lists, tiers)
3
move CLAUDE_CODE_MODELS/CODEX_MODELS/resolveAgentTier data to a shared module. web/components/chat/select-model/agent-models.tsx:5-6, web/components/chat/use-agent-mode.ts:2
@/core (constants, org-archived)
3
web/utils/constants.ts:2 (MCP_MESH_KEY), web/layouts/shell-layout.tsx:39 + web/lib/auth-client.ts:9 (isOrgArchived) → move to @/shared
Acceptance: all 21 violations resolved (each value import either moved to a frontend-safe/shared location or converted to import type); flip .oxlintrc.json rule from warn → error; bun run lint + bun run check + client build pass.
(Full split into apps/web is explicitly NOT recommended now. Revisit only if: tsc on the monolith becomes an unfixable CI bottleneck, the API is open-sourced separately from the UI, a second frontend appears, or web/API get separate teams with divergent cadences.)
Generated with Claude Code from a graphify knowledge-graph audit. Every file:line above was cited by an agent that read the code; the Event Bus and diagnostic-location findings were independently grep-verified. ⚠️ items contradict what CLAUDE.md implies — confirm intent with the owner before deleting.
Where this came from
A structural audit of the monorepo built from a graphify knowledge graph (2,764 files → 16,839 nodes / 44,925 edges, deterministic tree-sitter AST), cross-referenced against the company 2028 plan (
FUTURE.md/MILESTONES.mdM1–M4). Companion doc:GRAPHIFY_FINDINGS.md(per-file bug findings). This issue is the strategy-level layer: what to simplify, delete, and re-architect so the code maps to where revenue actually is.The reframe (root cause of most items)
The code describes itself as "an open-source control plane for MCP traffic." By mass and by strategy that's a stale label:
apps/mesh/src/web= 152k LOC). The three heaviest UI areas — chat (25k), sandbox/preview (19k), sections-editor (18k) — are the agentic operator console (M2) and the agentic site editor (M3). That is the product.Everything built to satisfy "we are a generic MCP mesh" is a cut candidate. Everything built for "agentic commerce editor + operator" is an invest candidate. Guiding principle: the worst thing is to optimize what shouldn't exist.
Measured code mass (LOC, ts/tsx, excl. tests):
web/components/chatweb/components/sandboxweb/components/sections-editorapi/tools/storage/packages/sandboxGroup A — Docs debt (do first, cheapest, highest-leverage)
WI-1 — Rewrite the CLAUDE.md "Event Bus" section to match reality
Label: documentation · Risk: none
Problem.
CLAUDE.md's largest architecture section documents an Event Bus (CloudEvents v1.0, 20-attempt backoff,deliverAt, cron, 6 MCP toolsEVENT_PUBLISH/EVENT_SUBSCRIBE/…, a worker,polling.ts,nats-notify.ts) that does not exist in code. Any coding agent reading it will build on a phantom.Evidence (verified):
apps/mesh/src/tools/eventbus/— directory does not exist (CLAUDE.md claims 6 tools live here).packages/bindings/src/well-known/event-bus.ts— does not exist (onlyevent-subscriber.tsis present).grep -r "EVENT_PUBLISH\|EVENT_SUBSCRIBE" apps packages→ 0 matches.apps/mesh/migrations/008-event-bus.ts(schema),packages/runtime/src/events.ts(~599 LOC engine). Producers: exactly 1 (apps/mesh/src/api/routes/registry/public-publish-request.ts:103insertsevent_deliveries). Subscribers: 0 (nothing insertsevent_subscriptions). Consumers/workers: 0.Scope. Edit
CLAUDE.md: replace the entire Event Bus section (files, tools, bindings, ON_EVENTS, NotifyStrategy, config) with an accurate short note — "an events schema + engine exist (runtime/src/events.ts, migration 008) with one producer and no consumers; the working trigger path isAutomationEventDispatcher+ DBOS (see WI-2/WI-7)." Do not delete code in this WI — that's WI-6.Acceptance: CLAUDE.md no longer references any file/symbol that
grepcan't find; a fresh reader can trust every path in the Event Bus section.Group B — Delete / collapse over-built control-plane machinery
Removing these deletes ~3–4k LOC and three "we're a generic MCP mesh" fictions that keep pulling design attention off the commerce product.
WI-6 — Delete (or consciously defer) the Event Bus engine
Label: refactor · Risk: LOW⚠️ (confirm with owner: aspirational-but-unbuilt vs planned-next)
Evidence: see WI-1. 1 producer, 0 subscribers, 0 consumers, 0 workers. The real trigger feature bypasses it (
AutomationEventDispatcher+ DBOS idempotency keys).Scope. Remove
packages/runtime/src/events.tsevent-bus engine paths, theevents/event_subscriptions/event_deliveriesschema (new down-migration; keep008in history),well-known/event-subscriber.tsbinding, and the storage types. Redirect the single producer (public-publish-request.ts:103) to a direct call or drop it if the publish endpoint is itself unused (check first).Acceptance: app builds and boots;
bun run check+bun testpass; noevent_subscriptions/event_deliveriesreferences remain; a migration cleanly drops the tables.WI-2 — Collapse the Virtual-MCP "3 strategies" abstraction
Label: refactor · Risk: LOW
Problem. Docs/README describe three runtime strategies (full-context / smart-selection / code-execution). Only
passthroughships.Evidence:
apps/mesh/src/mcp-clients/virtual-mcp/passthrough-client.tsis the only impl; the strategy field is a vestigial single-member union —virtual-mcp/index.ts:71_strategy: "passthrough"with the comment "Kept for backward compatibility, always uses passthrough."VirtualMCPEntitySchemahas no strategy field. smart-selection/code-exec exist only on worktree branchhappy-nibbling-matsumoto(where migration012self-labels them "metadata for now").Scope. Remove the
_strategyunion and any dead branching; updateapps/docs/+ CLAUDE.md to stop claiming three strategies until the worktree lands. Leave one clean extension seam if desired, but no dead code.Acceptance: no
_strategyunion in shipped code; docs describe only what ships; typecheck + tests pass.WI-4 — Prune unused well-known bindings
Label: refactor · Risk: LOW
Evidence (
packages/bindings/src/well-known/):prompt.ts(PROMPTS_BINDING— 0 uses),registry.ts(REGISTRY_APP_BINDING— 0 external refs),event-subscriber.ts(removed by WI-6), and the client halves ofassistant.ts/ai-gateway.ts/brand.ts(unused). Keep the 4 real ones:collections.ts(18 importers — backbone of the list/get tool surface),object-storage.ts(14 refs),trigger.ts(used inconfigure-trigger.ts:35);language-model.tsis@deprecated(seeconnection/list.ts:67) — mark for removal, don't break callers yet.Scope. Delete the unused binding constants + their exports; leave
collections/object-storage/triggeruntouched.Acceptance:
bun run checkpasses;knipreports no new unused exports; grep confirms 0 references to the removed constants.WI-6b — Derive
registry-metadata.tsfrom tool definitionsLabel: refactor · Risk: LOW
Evidence:
apps/mesh/src/tools/registry/registry-metadata.ts— ~41KB, ~142 hand-maintained{name, description, category}entries with a manual "keep in sync with index.ts" warning (chronic drift hazard; flagged as a god-file by the graph).Scope. Generate the metadata from the tool
defineTool()definitions (name/description already live there; addcategoryto the tool def if missing) instead of a parallel hand-maintained table.Acceptance:
registry-metadata.tsno longer hand-lists tools; the registry UI shows identical categories/descriptions; adding a tool requires no metadata edit.Group C — Heavier simplifications (verify usage first)
WI-3 — Shrink or externalize the Registry MCP-monitor subsystem
Label: refactor · Risk: LOW-MED⚠️ (confirm no customer depends on health scores)
Evidence:
apps/mesh/src/tools/registry/monitor-run-start.ts= 1,262 LOC / 38KB — the graph's #15 god-file. It's a full LLM agent that connects to third-party MCPs and exercises their tools (full_agentvshealth_checkmodes), backed by 8 monitor tools + storage — ~35% of the 32-toolregistrydomain, for a catalog QA feature, not the revenue path.Scope (pick one): (a) reduce to a plain
listToolsreachability check, or (b) extract the LLM-driven tester into a separate package/service. Also: fold the duplicateCOLLECTION_REGISTRY_APP_*read tools intoREGISTRY_ITEM_*with aprivateOnlyflag (collection-registry-app.ts:14-30mirrorREGISTRY_ITEM_*with one filter), and dropREGISTRY_AI_GENERATEif the UI doesn't call it (checkweb/first).Acceptance: registry catalog still lists/reaches MCPs; god-file gone or moved out of
apps/mesh; tool count in the domain drops; no frontend caller breaks.WI-5 — Reconsider the OpenAI-compatible gateway endpoint
Label: refactor · Risk: MED⚠️ (verify external API-key consumers before removing)
Evidence:
apps/mesh/src/api/routes/openai-compat.ts= 773 LOC — a generic OpenAI-compatible gateway. Pure control-plane positioning; no tie to commerce/editor revenue.Scope. Establish whether any external consumer hits this (logs/analytics). If not, externalize to the Deco AI Gateway service and remove from mesh. If yes, document the dependency and leave it.
Acceptance: a decision recorded (remove / externalize / keep-with-reason) backed by usage data.
Group D — Deployment: previews and prod are the SAME investment
Strategic anchor (M4,
MILESTONES.md): "One platform — GCP cluster shut down, sandboxes-as-production, MCP-native." Today: Argo CD GitOps (decocms/argo-deploy-mesh, manualupdate-image-tag.yml), GCP k8s, staging→prod promoted by hand, no deploy previews for Studio itself.Key finding: you already built ~80% of a preview system. A sandbox is a per-
(user, branch)K8sSandboxClaimthat shallow-clones a repo → installs deps → runsdev/start→ gets a stable URL<branch-slug>-<hash>.preview.<domain>(per-claim GatewayHTTPRoute+ wildcard TLS) → auto-GCs on 15-min idle.SANDBOX_STARTalready does this for arbitrary GitHub repos. Branch → running app → stable URL → auto-teardown already works. The gaps for "Studio-of-a-branch preview" are the same gaps for "sandboxes-as-production."Reference files:
packages/sandbox/server/provider/agent-sandbox/runner.ts,packages/sandbox/daemon/setup/orchestrator.ts,packages/sandbox/daemon/proxy.ts,packages/sandbox/shared.ts,apps/mesh/src/sandbox/lifecycle.ts,apps/mesh/src/sandbox/preview-proxy.ts,apps/mesh/src/api/routes/sandbox-proxy.ts,apps/mesh/src/tools/sandbox/start.ts,deploy/helm/sandbox-env/(template, warm-pool, preview-gateway, housekeeper),.github/workflows/release-mesh.yaml,release-studio-sandbox.yaml.WI-7 — Add a
buildstep to the sandbox daemon setup pipelineLabel: enhancement · Risk: LOW · Prereq for WI-9/WI-10
Evidence:
packages/sandbox/daemon/setup/orchestrator.tsruns a FIFO pipelineclone → install → start; start-commands are hard-coded toWELL_KNOWN_STARTERS = ["dev","start"]inpackages/sandbox/shared.ts:18. There is no build step — apps run in Vite dev mode only.Scope. Extend the
Steppipeline toclone → install → build → startand broaden start-command resolution (support a built/production-serve command). Reuse everything else (clone, golden cache, probe,:9000reverse proxy, HTTPRoute).Acceptance: a repo with a
buildscript produces a built app served behind the existing preview URL; existingdev/startrepos still work unchanged.WI-8 — Turn ON the features that already ship OFF
Label: enhancement · Risk: LOW · (config + infra, ~no app code)
Evidence: three accelerators/GC mechanisms exist but default off: warm pool (
SandboxWarmPoolCRD,deploy/helm/sandbox-env/templates/sandbox-warm-pool.yaml,warmPool.enabled:false); golden cache (GOLDEN_CACHE_ENABLED, ~1s vs the 180s readiness timeout); preview Gateway + wildcard cert + housekeeper (sandbox-preview-gateway.yaml,sandbox-preview-cert.yaml,files/housekeeper-sweep.sh).Scope. Flip the Helm flags; provision the manual infra they need (wildcard DNS
*.<domain>, a DNS-01 ClusterIssuer).Acceptance: cold sandbox boot drops to low-single-digit seconds; previews are reachable over TLS at
<handle>.preview.<domain>; idle sandboxes + orphan HTTPRoutes auto-clean.WI-9 — Ephemeral Postgres + secret injection per sandbox
Label: enhancement · Risk: MED · (biggest net-new; shared prereq for previews AND prod)
Evidence: a sandbox provisions no database — it only clones+runs a repo. Studio needs Postgres + NATS + Better Auth + secrets. Injection channel exists (
EnsureOptions.env, frozen plaintext —runner.ts:1075-1098); egress is locked down by a netinit iptables init-container (allows only 53/443 out; RFC1918 blocked —sandbox-template.yaml:125-202).Scope. In the
ensure/SANDBOX_STARTpath: provision a branch/template-cloned ephemeral Postgres, runmigrate+better-auth:migrate+ seed, injectDATABASE_URL+BETTER_AUTH_SECRET+ENCRYPTION_KEY+ OAuth keys viaEnsureOptions.env, and add the DB host to the netinit egress allowlist.Acceptance: a sandbox can boot a full Studio instance with an isolated DB and working auth; the DB is torn down with the sandbox.
WI-10 — Studio PR-preview job (the visible win)
Label: enhancement · Risk: LOW · Depends on WI-7 + WI-9 (+WI-8 flags)
Evidence: CI already builds the Studio image and dispatches to
deco-apps-cd(release-mesh.yaml:564-571,repository_dispatch: studio-image-bump).Scope. Add a PR workflow that calls
ensure/SANDBOX_STARTwith the Studio repo + PR branch + build-mode (WI-7) + ephemeral DB (WI-9), then comments<handle>.preview.<domain>on the PR and tears down on close.Acceptance: every PR gets a working, isolated Studio preview URL posted as a comment; the sandbox GCs when the PR closes.
WI-11 — Durable/pinned claim mode + decouple app liveness from the daemon
Label: enhancement · high priority · Risk: MED · (the real gate for sandboxes-as-production)
Evidence: ephemerality is a design invariant — 15-min idle-TTL deletes claim+pod (
runner.ts:148), RO rootfs, state is only git+org-fs. And app liveness is coupled to the daemon's single-event-loop/health: one missed poll → the whole sandbox is torn down (CLAUDE.md rule #8; see the daemon-blocking bugs inGRAPHIFY_FINDINGS.md#1/#2). A CPU-bound production request would kill the "server."Scope. Add a durable claim mode (no
shutdownTime/ long TTL) and run the app as a daemon-supervised task with a forgiving health model, so a busy app process can't trip one-miss-dead teardown. Reuse the claim/adopt/resurrect machinery.Acceptance: a pinned sandbox survives idle + a sustained CPU-bound request without teardown; recovery still works for genuinely dead pods.
Group E — Strategy gaps (not cuts — where the code is thin vs the plan)
Not immediately actionable as refactors, but the cuts above exist to free attention for these. Flagged so they're on the radar.
COMMERCE_DISCOVERY_MCP_URL = "https://commerce-skills.deco-cx.workers.dev/api/v2/mcp",packages/mesh-sdk/src/lib/constants.ts:38). This repo has only glue (web/routes/commerce-onboarding/,tools/commerce-discovery/). And it is not the default onboarding —/commerce-onboarding(web/index.tsx:187) has zero internal navigation, entered only via marketing links with?siteUrl; the app lands on org-home (web/index.tsx:111). Strategy M1 says "diagnostic is Studio's default onboarding." Self-service Stripe billing is not in this repo (no Stripe SDK).sidebar/footer/inbox.tsxis just invites), no real task management (task-groups = thread grouping). "System Health" exists only as a raw MCP call-log viewer (monitoring/), not the client-site-health product M2 describes.Group F — Frontend ↔ API boundary
Should we separate the frontend from the API? Analyzed: no full split — it's already separate where it counts, and the remaining coupling is mostly a feature. Build (
build:client→dist/clientvsbuild:server), image (nginx-webcontainer vs Bun-apicontainer with independent tags —deploy/helm/studio/templates/deployment.yaml:66-110), and runtime (SPA → HTTP → API via@decocms/mesh-sdk, 207 files) are already separated. Only the source tree is shared, andweb/ → backendimports are almost allimport type(erased at build; end-to-end type safety is worth keeping). A fullapps/webpackage split would break ~240 type imports, duplicate vite/tailwind/react-compiler/tsconfig config, and add a versioned contract — high cost, low runtime gain. The real risk is the handful of value imports that leak server code into the browser bundle. Fix that with a lint boundary, not a split.WI-12 — Enforce the web ↔ server import boundary (lint rule) ✅ shipped as PR #4469
Label: refactor · Risk: LOW · Status: rule landed as
warn; cleanup remainingDone (PR #4469): new oxlint plugin
plugins/ban-web-server-imports.js— files underapps/mesh/src/web/may not value-import (or re-export / dynamic-import) from a@/<server-tree>/…specifier (storage,core,api,tools,auth,ai-providers,services,mcp-clients,event-bus,harnesses,sandbox, …). Allowsimport type, frontend-safe trees (@/mcp-apps,@/web,@/lib,@/shared), and workspace packages. 9 unit tests. Landed aswarn.Evidence — 21 pre-existing violations across 20 files to drain (the actual work item for an agent):
@/tools/*/schema,registry/shared,registry-metadata@/sharedormesh-sdk, or import type-only. e.g.web/components/details/connection/connection-sidebar.tsx:2→@/tools/connection/schema;web/components/connections/create-connection-dialog.tsx:54;web/views/virtual-mcp/types.ts:5;web/views/registry/*→@/tools/registry/shared(PLUGIN_ID, MONITOR_AGENT_DEFAULT_SYSTEM_PROMPT);web/views/settings/org-role-detail.tsx:6→@/tools/registry-metadata@/ai-providers(model lists, tiers)CLAUDE_CODE_MODELS/CODEX_MODELS/resolveAgentTierdata to a shared module.web/components/chat/select-model/agent-models.tsx:5-6,web/components/chat/use-agent-mode.ts:2@/core(constants,org-archived)web/utils/constants.ts:2(MCP_MESH_KEY),web/layouts/shell-layout.tsx:39+web/lib/auth-client.ts:9(isOrgArchived) → move to@/shared@/api/routes/decopilot/mesh-storage-uriparseMeshStorageKey→ shared util.web/components/chat/message/parts/tool-call-part/{generate-image,take-screenshot,web-search}.tsx@/file-storage,@/commerce-discoveryhomeDisplayName,normalizeCommerceSiteUrl→ sharedAcceptance: all 21 violations resolved (each value import either moved to a frontend-safe/shared location or converted to
import type); flip.oxlintrc.jsonrule fromwarn→error;bun run lint+bun run check+ client build pass.(Full split into
apps/webis explicitly NOT recommended now. Revisit only if:tscon the monolith becomes an unfixable CI bottleneck, the API is open-sourced separately from the UI, a second frontend appears, or web/API get separate teams with divergent cadences.)Suggested order
error) — rule already shipped (chore(lint): enforce web ↔ server import boundary (ban-web-server-imports) #4469).Generated with Claude Code from a graphify knowledge-graph audit. Every⚠️ items contradict what CLAUDE.md implies — confirm intent with the owner before deleting.
file:lineabove was cited by an agent that read the code; the Event Bus and diagnostic-location findings were independentlygrep-verified.