Effect backend migration
Migrates the dashboard backend to typed, interruptible Effect programs end to end, per the approved design (docs/superpowers/specs/2026-06-11-effect-backend-migration-design.md). This is also a bug-fix campaign: the browser-side apollo client was structurally broken in deployed envs (cookie auth doesn't exist server-side — zero HTTP 200s among UI-sourced apollo calls in staging AND production logs).
What's in here
Phase 0 — Foundation (src/server/effect/)
- One globalThis-cached
ManagedRuntime (Datadog logger layer + Effect tracer bridged to the global OTel provider; span parentage verified by in-memory exporter tests)
- Services:
Db / Clickhouse (tenant-scoped clients only — scoping by construction), InternalDashboard (spec-typed via @composiohq/internal-dashboard-openapi, with 10s timeout + jittered retry for idempotent GETs, TestClock-verified), RequestContext (branded tenant ids, client IP)
- Adapters:
runProcedure (exhaustive errorMap — an unmapped tagged error is a compile error; runPromiseExit; interrupts are CLIENT_CLOSED_REQUEST, never error-logged) and runRoute (redirects as values)
- Contract-test harness: tRPC
createCallerFactory over prismock + fixture-matched fetch — tests run the REAL middleware chain, adapters, typed facade and retry stack; 55 fixtures auto-generated from the internal-dashboard spec (satisfies-checked, so a spec bump breaks stale fixtures at compile time)
- CI:
ci-test.yml (lint + machine-enforced ratchet + diff-scoped vitest), lint-ratchet.json baseline, pinned v3.1 types (typecheck no longer live-fetches staging), Renovate + nightly package-freshness workflow
- Lint:
no-effect-run-outside-adapter, no-class-outside-effect-tags, no-dynamic-client-error-messages, require-procedure-test, raw-callInternalDashboard + apollo-in-server bans
- Ambient client IP on every log/span;
network.client.ip Datadog attribute
Phase 2 — All tRPC routers converted (~150 procedures, one commit per endpoint, characterization-test-first)
triggers · apiKeys · authConfigs · billing · cliSessions · connectedAccounts · logs · onboarding · playground · org · project · sessions · support · toolkits · usage · users · webhooks · agent · decimal · health · consumer
Phase 3 (partial) — browser call-site swaps
triggers_types (3 sites) and tools (3 sites) now go through tRPC procedures (off the dead cookie-auth path)
Real bugs found & fixed by the migration (selection)
toolkit_versions map silently mis-serialized (JSON blob where hermes expects qs brackets) — fixed, regression-tested
updateAuthConfig input drifted from the upstream discriminated union (type optional → upstream 400s)
createInviteCode allowed roles upstream rejects for public_use invites
listApiKeys silently stripped spec-documented created_by/last_used fields
- Dozens more documented per-endpoint in commit messages (driftFindings)
Gated / follow-up
- Broken trigger toggle/delete + user api-key revoke need ComposioHQ/platform#10674 (draft) merged +
@composiohq/internal-dashboard-openapi republished; their UI call sites are untouched until then
- Link-token screens (public, token-authed) and remaining apollo client deletion land with the phase-4 decommission once ratchets hit zero
- Live Datadog trace verification of the span bridge on this PR's preview deployment
Post-review hardening (3 independent reviewers: security / functionality / best-practices)
- All review findings fixed: cross-org scope-header opt-out, schemaIssues gated to BAD_REQUEST, 4xx message-extractor hardened, completed the schema-changes swap, project-scoped listTools, payments timeout budget, vault error-tag de-collision, shared per-router error modules, cast cleanup
proxy() factory: 40 pure-proxy procedures collapsed to ~15 lines each (421 lines removed, behavior-neutral — every contract test passed unchanged); legacy handle*Error helpers and dead files deleted
- Production-code delta (src, excl. tests/generated): +9.4k / −6.4k across 178 files — the bulk of insertions are the 704 contract tests and generated spec-typed fixtures
Final state
- 704 contract tests across 133 files, all green (in-memory, full suite in ~3s)
tsgo --noEmit: 0 errors · oxlint: 0 errors
- Ratchet (lint-ratchet.json): runPromise-outside-adapter 88→3 (the 3 are documented helper debt; boundary files exempted per spec) · raw callInternalDashboard 57→7 (rawBody upload + key-minting utils) · legacy classes 12 · apollo-in-server 0 · procedures-without-tests 0 (rule now ERROR) · dynamic client error messages 0 (rule now ERROR)
- 156 commits, one per endpoint — any single conversion is independently revertable
🤖 Generated with Claude Code