Codebase↔strategy audit: simplify the MCP-mesh machinery, converge deploy previews with sandboxes-as-production (agent-ready work items)
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 407
- Forks
- 57
- Avg merge
- 5h 6m
- Merged PRs (30d)
- 880
Description
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-Nblock into your agent's prompt, add "open a PR againstmain", 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 (onlyevent-subscriber.tsis present).grep -r "EVENT_PUBLISH\|EVENT_SUBSCRIBE" apps packages→ 0 matches.- 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: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 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."
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 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.
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 (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.
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_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.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/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
Label: refactor · Risk: LOW · Status: rule landed as warn; cleanup remaining
Done (PR #4469): new oxlint plugin plugins/ban-web-server-imports.js — files under apps/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, …). Allows import type, frontend-safe trees (@/mcp-apps, @/web, @/lib, @/shared), and workspace packages. 9 unit tests. Landed as warn.
Evidence — 21 pre-existing violations across 20 files to drain (the actual work item for an agent):
| Server tree | # | Fix |
|---|---|---|
@/tools/*/schema, registry/shared, registry-metadata |
10 | 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 |
@/api/routes/decopilot/mesh-storage-uri |
3 | parseMeshStorageKey → shared util. web/components/chat/message/parts/tool-call-part/{generate-image,take-screenshot,web-search}.tsx |
@/file-storage, @/commerce-discovery |
2 | homeDisplayName, normalizeCommerceSiteUrl → 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.)
Suggested order
- WI-1 (docs — today, unblocks correct agent work).
- WI-6, WI-2, WI-4, WI-6b (one cleanup PR — low risk, removes the mesh fictions).
- WI-7 → WI-9 → WI-8 → WI-10 (deploy previews; also advances M4 sandboxes-as-production).
- WI-3, WI-5 (verify usage, then trim).
- WI-11 (the prod-substrate gate) and Group E (invest where the strategy is thin).
- WI-12 cleanup (drain the 21 boundary violations, then flip the rule to
error) — rule already shipped (#4469).
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.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by choosing one WI and reading its named entry points, such as CLAUDE.md for WI-1, packages/sandbox/daemon/setup/orchestrator.ts for WI-7, or apps/mesh/src/tools/registry/registry-metadata.ts for WI-6b. Run the specified checks such as bun run check and bun test, then use that work item's acceptance criteria to determine when it is done; ⚠️ items require owner or usage confirmation first.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- bun, github-actions, kubernetes, typescript
- Domain
- backend, cloud, devops, documentation, tooling
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100