refactor: layer variables are named layer or layerXyz#16282

merged3
main
t3code/layer-naming
opened24d agobyJJulius Marminge
View on GitHubTwo-way sync enabledPR last imported 9d ago

Effect Layer variables and layer-building functions had a dozen naming styles (fooLayer, FooLive, FooLayerLive, fooLiveLayer, testLayer, bare live, …). This is follow-up to #16267 (merged), which moved callers to namespace imports.

The rule

  • A module's only or primary exported layer, the one that provides the module's own service if it has one, is plain layer. Callers reach it as import * as Mod from "./mod.ts" and Mod.layer, as docs/internals/effect-services.md already asks.
  • When a module exports more than one layer, the others are layerXyz with a short Xyz that does not repeat the module name: EffectOutbox.pruneWorkerLive becomes EffectOutbox.layerPruneWorker. If no layer is primary (route modules like apps/server/src/http.ts, infra/relay/src/http/Api.ts), every layer gets layerXyz.
  • Module-level and function-local layers follow the same naming: layerXyz, for example testLayer becomes layerTest.

How

  • Finding them (by type): a TypeScript compiler-API script ran per tsconfig project and listed every const/let declaration whose checked type resolves to Layer from effect/dist/Layer.d.ts, and whose name is not layer or layer[A-Z0-9]…. Matching on names alone would have flagged non-layers.
  • Imports: named imports of those exports (import { ServerLoggerLive } …) were converted to namespace imports named after the module. Each reference was rewritten by checker symbol identity.
  • Renames: every rename went through LanguageService.findRenameLocations, so each reference and namespace access moved with its declaration. No regex renames.
  • Hand fixes: two layers were reached through package exports (@t3tools/shared/httpObservability, and the @t3tools/client-runtime/state/threads barrel). Their consumers were updated by hand, and typecheck confirmed them.
  • Barrel change: the barrel now re-exports the module as BoundedThreadSnapshotLoader (export * as), matching the existing ShellSnapshotLoader.
  • Namespace collisions: a few call sites already used the module's name for the matching Services/ module, so the Layers module gets a …Layer suffix there:
    • ProviderRegistryLayer.layer in server.ts and two tests
    • OrchestrationEventStoreLayer
    • OrchestrationCommandReceiptsLayer
    • AcpRegistryCatalogLayer and ProviderOrchestrationAdapterInfrastructureLayer in ProviderInstanceRegistryHydration.ts
    • HookInboxObjectLayer in relay worker.ts
  • Scope collisions: none blocked a rename. In mcp/toolkits/pullRequests/handlers.ts the new module-level layer sits next to unrelated (layer) => … callback params over PR-stack layers. Shadowing there is harmless, so it was kept.

Counts (exported / module-level / local)

packageexportedmodulelocal
apps/server71323409
apps/desktop04994
apps/mobile7195
apps/web7163
infra/relay16717
packages/client-runtime1326
packages/ssh0016
packages/effect-acp016
packages/shared105
packages/effect-codex-app-server003

1105 renames in total. 344 files changed; part of the diff is vp fmt reflowing lines that the shorter names now fit.

Exported renames (old → Module.new)

104 exports

apps/server

  • serverLogger: ServerLoggerLive → ServerLogger.layer
  • observability/Layers/Observability: ObservabilityLive → Observability.layer
  • http.ts (route layers, none primary): httpCompressionLayer → ServerHttp.layerHttpCompression, browserApiCorsLayer → .layerBrowserApiCors, serverEnvironmentHttpApiLayer → .layerServerEnvironmentHttpApi, otlpTracesProxyRouteLayer → .layerOtlpTracesProxyRoute, assetRouteLayer → .layerAssetRoute, attachmentUploadRouteLayer → .layerAttachmentUploadRoute, staticAndDevRouteLayer → .layerStaticAndDevRoute
  • ws: websocketRpcRouteLayer → Ws.layer
  • auth/http: authHttpApiLayer → AuthHttp.layer, environmentAuthenticatedAuthLayer → AuthHttp.layerAuthenticatedAuth
  • auth/EnvironmentAuth: runtimeLayer → EnvironmentAuth.layerRuntime
  • environment/ServerEnvironment: identityLayer → ServerEnvironment.layerIdentity
  • cloud/http: connectHttpApiLayer → CloudHttp.layer
  • cloud/relayTracing: headlessRelayClientTracingLayer → RelayTracing.layerHeadlessRelayClient, serverRelayBrokerTracingLayer → RelayTracing.layerServerRelayBroker
  • device/DeviceHubProxy: deviceHubProxyRouteLayer → DeviceHubProxy.layer
  • project/http: projectHttpApiLayer → ProjectHttp.layer
  • pullRequest/http: pullRequestHttpApiLayer → PullRequestHttp.layer
  • scheduledTasks/webhookRoute: webhookHttpApiLayer → WebhookRoute.layer
  • orchestration-v2/http: orchestrationHttpApiLayer → OrchestrationHttp.layer
  • orchestration-v2/runtimeLayer: OrchestrationV2LayerLive → RuntimeLayer.layer, OrchestrationV2ProductionLayerLive → .layerProduction, OrchestrationEventInfrastructureLayerLive → .layerEventInfrastructure, OrchestrationV2EventSinkLayerLive → .layerEventSink, ProjectServiceLayerLive → .layerProjectService, ProjectSetupScriptRunnerLayerLive → .layerProjectSetupScriptRunner
  • orchestration-v2/EffectOutbox: pruneWorkerLive → EffectOutbox.layerPruneWorker
  • orchestration-v2/EffectWorker: executorLayer → EffectWorker.layerExecutor
  • orchestration-v2/ProviderContinuationService: workerLive → ProviderContinuationService.layer
  • orchestration-v2/UsageLimitRecoveryWorker: workerLive → UsageLimitRecoveryWorker.layer
  • orchestration-v2/ProviderEventIngestor: analyticsLive → ProviderEventIngestor.layerAnalytics
  • orchestration-v2/RunFinalizationService: observerLive → RunFinalizationService.layerObserver
  • orchestration-v2/ResourceCleanupService: live → ResourceCleanupService.layer
  • orchestration-v2/ProviderTurnStartService.testkit: worktreeRepairDependenciesTestLayer → ProviderTurnStartServiceTestkit.layer
  • orchestration-v2/Adapters/ClaudeAdapterV2: claudeAgentSdkQueryRunnerLiveLayer → ClaudeAdapterV2.layerQueryRunner
  • orchestration-v2/Adapters/CodexAdapterV2: codexAppServerClientFactoryFromSettingsLayer → CodexAdapterV2.layerAppServerClientFactory
  • orchestration-v2/Adapters/CursorAgentSdk: cursorAgentSdkRunnerLiveLayer → CursorAgentSdk.layer
  • persistence/Layers/Sqlite: SqlitePersistenceMemory → SqlitePersistence.layerMemory
  • persistence/Layers/OrchestrationEventStore: OrchestrationEventStoreLive → OrchestrationEventStore.layer
  • persistence/Layers/OrchestrationCommandReceipts: OrchestrationCommandReceiptRepositoryLive → OrchestrationCommandReceipts.layer
  • provider/opencodeRuntime: OpenCodeRuntimeLive → OpenCodeRuntime.layer
  • provider/Layers/…: AcpRegistryCatalogLive, CursorSdkCatalogLive, ProviderAuthServiceLive, ProviderInstanceRegistryHydrationLive, ProviderOrchestrationAdapterInfrastructureLive, ProviderRegistryLive, ProviderUsageLimitsIngestionLive → <Module>.layer
  • vcs/GitVcsDriver: vcsLayer → GitVcsDriver.layerVcs
  • vcs/VcsStatusBroadcaster: autoPullPolicyLayer → VcsStatusBroadcaster.layerAutoPullPolicy
  • mcp/McpHttpServer: {Html,Preview,Orchestrator,Thread,PullRequests,Device}ToolkitRegistrationLive → McpHttpServer.layer{Html,Preview,Orchestrator,Thread,PullRequests,Device}Toolkit
  • mcp/toolkits/*/handlers: single-layer modules → <X>Handlers.layer (attachment, environment, orchestrator, previewControls, project, pullRequests, thread, worktree); DeviceStandard/ScreenshotToolkitHandlersLive → DeviceHandlers.layerStandard, .layerScreenshot; PreviewStandard/SnapshotToolkitHandlersLive → PreviewHandlers.layerStandard, .layerSnapshot; HtmlPreview/RenderToolkitHandlersLive → HtmlHandlers.layerPreview, .layerRender

apps/web

  • connectionPlatformLayer → ConnectionPlatform.layer
  • connectionStorageLayer → ConnectionStorage.layer
  • runtimeContextLayer → Runtime.layer
  • browserCryptoLayer → Dpop.layer
  • primaryEnvironmentHttpLayer → PrimaryEnvironmentHttpLayer.layer
  • backgroundActivityReporterLayer → BackgroundActivityReporter.layer
  • backgroundActivityObserverLayer → BackgroundActivityReporter.layerObserver

apps/mobile

  • connectionPlatformLayer → ConnectionPlatform.layer
  • connectionStorageLayer → ConnectionStorage.layer
  • runtimeContextLayer → Runtime.layer
  • cryptoLayer → Dpop.layer
  • tracingLayer → Tracing.layer
  • mobileBackgroundActivityObserverLayer → BackgroundActivity.layerObserver
  • mobileBackgroundActivityReporterLayer → BackgroundActivity.layerReporter

infra/relay

  • worker: ApiLive (also the default export) → RelayWorker.layer
  • hooks/HookInboxObject: HookInboxObjectLive → HookInboxObject.layer
  • hooks/HookForwarder: hooksApi → HookForwarder.layerApi
  • http/Api: relayCors → RelayHttpApi.layerCors, relayNotFoundRoute → .layerNotFoundRoute, relayDocsRedirectRoute → .layerDocsRedirectRoute, relayClientAuthLayer → .layerClientAuth, relayEnvironmentAuthLayer → .layerEnvironmentAuth, relayDpopClientAuthLayer → .layerDpopClientAuth, {metadata,health,mobile,client,token,dpopClient,server}Api → .layer{Metadata,Health,Mobile,Client,Token,DpopClient,Server}Api

packages: public entry points changed (internal monorepo only)

  • @t3tools/shared/httpObservability: httpHeaderRedactionLayer → HttpObservability.layer
  • @t3tools/client-runtime/state/threads: boundedThreadSnapshotLoaderLayer (flat re-export) → BoundedThreadSnapshotLoader.layer

Layer-building functions (second commit)

The same rule applies to exported and module-level functions whose return type is a Layer; any make prefix is dropped. A function counts if every call signature returns Layer from effect's Layer.d.ts. Combinators that take a Layer and wrap it (withHostPlatform(layer, …)) are skipped unless their name already says Layer/Live.

Imports and renames were handled as in the first commit. Packages consumed through workspace specifiers were resolved by real path this time, so their callers were converted too. Three call sites that the checker could not reach, because they live under a different tsconfig root, were updated by hand: @t3tools/client-runtime/rpc, @t3tools/shared/observability and @t3tools/shared/relayTracing.

Collisions: in 15 test files the local variable already had the derived name (e.g. const layerEnvironment = makeEnvironmentLayer(dir)), so the factory became layerEnvironmentFor(dir) and plain makeLayer became layerFor(...). The span name Effect.fn("makeSqlitePersistenceLive") was left as-is so traces don't change.

Counts (exported / module-level): apps/server 15 / 93, apps/desktop 0 / 38, apps/mobile 2 / 3, apps/web 2 / 3, infra/relay 1 / 7, packages/shared 2 / 4, packages/client-runtime 1 / 2, packages/tailscale 0 / 2. 175 in total.

Exported function renames (old → Module.new)
  • rpcScopeAuthorizationLayer(scopes) → RpcAuthorization.layer(scopes)
  • bootServiceLayer(config) → CliService.layer(config) (apps/server/src/cli/service.ts)
  • ProviderInstanceRegistryMutableLayer(input) → ProviderInstanceRegistryLive.layer(input)
  • makeProviderRegistryLayer(providers) → ProviderRegistryMock.layer(providers)
  • ProviderAdapterRegistry.makeLayer / makeLayerEffect / makeSingleLayer / makeDriverLayer → ProviderAdapterRegistry.layerFromAdapters / layerFromAdaptersEffect / layerSingle / layerFromDrivers (the module's primary layer is already layerFromProviderInstanceRegistry)
  • makeSqlitePersistenceLive(dbPath) → SqlitePersistence.layerFromPath(dbPath)
  • makeCursorSdkCatalogTestLayer → CursorSdkCatalog.layerTest
  • makeOrchestratorV2ProviderReplayLayer → ProviderReplayHarness.layerProviderReplay; makeOrchestratorV2ReplayLayerWithRegistry → ProviderReplayHarness.layerWithRegistry
  • makeCodexProviderAdapterRegistryReplayLayer → CodexAdapterV2Testkit.layer; makeCursorProviderAdapterRegistryReplayLayer → CursorAdapterV2Testkit.layer; makePiProviderAdapterRegistryLayer → PiAdapterV2Testkit.layer
  • web and mobile: managedRelayClientLayer(relayUrl) → ManagedRelayLayer.layer(relayUrl)
  • web: makePrimaryEnvironmentHttpLayer() → PrimaryEnvironmentHttpLayer.layerForCurrentOrigin() (the module's layer is its result)
  • mobile: makeTracingLayer(config, resource) → Tracing.layerFromConfig(...) (the module's layer is its result)
  • relay: makeRelayTraceLayer → Observability.layer
  • package entry points: @t3tools/client-runtime/rpc remoteHttpClientLayer → RpcHttp.layerRemoteHttpClient (kept suffixed: the rpc entry re-exports client/http/protocol flat); @t3tools/shared/observability otlpSerializationLayer → SharedObservability.layerOtlpSerialization; @t3tools/shared/relayTracing makeRelayClientTracingLayer → RelayTracing.layer

The …Layer namespace aliases that exist only because of the Services/ vs Layers/ folder split (ProviderRegistryLayer, OrchestrationEventStoreLayer, …) are left for the separate folder merge.

Behavior-neutral: only identifiers and imports change.

Verification

Re-run after rebasing onto main at 758dc290e9 and adding the function renames:

  • vp run typecheck is clean in apps/server, web, desktop, mobile, infra/relay and packages/client-runtime, shared, tailscale, ssh, effect-acp, effect-codex-app-server and contracts.
  • apps/server: vp test run src/orchestration-v2/runtimeLayer.test.ts src/cloud src/scheduledTasks passes (22 files, 319 tests).
  • apps/server: vp test run src/mcp src/serverLogger.test.ts src/http.test.ts src/auth src/provider/Layers/ProviderRegistry.test.ts src/provider/Layers/ProviderInstanceRegistryLive.test.ts src/orchestration-v2/EffectWorker.test.ts src/orchestration-v2/ProviderSessionManager.test.ts src/orchestration-v2/Adapters/CodexAdapterV2.test.ts src/orchestration-v2/ProjectSettingsUpgrade.integration.test.ts passes (41 files, 592 tests).
  • infra/relay: vp test run src/hooks src/agentActivity/ApnsDeliveries.test.ts passes (4 files, 93 tests).
  • packages/client-runtime: vp test run src/rpc src/state/boundedThreadSnapshotHttp.test.ts src/relay passes (7 files, 81 tests). packages/shared: src/observability.test.ts passes (25 tests).
  • vp fmt was run on the changed files. vp lint on the changed files shows the same warnings as before both commits, with only the renamed identifiers differing in the text.
  • vp run knip:check passes.
  • Rescanning by type finds 0 non-conforming Layer variables and 0 non-conforming Layer-returning functions.

🤖 Generated with Claude Code

Checks

24 successful · 10 skipped · 1 neutral
All checks passed
313f02a
Synced from GitHub

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This is primarily a broad, mechanical layer-naming refactor with unchanged runtime composition, but it modifies authentication-directory files and authentication wiring. Those sensitive changes, plus a client-runtime export-surface reshaping, require human review.

Notes:

  • No code objects were reviewed. Approvability was decided on eligibility alone.

You can add or adjust custom eligibility rules. Learn more.

Synced from GitHub

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire5.0 KiB5.0 KiB0 B (0.0%)6.8 KiB✅
CodexThread snapshot wire3.8 KiB3.8 KiB0 B (0.0%)4.9 KiB✅
CodexLive turn WebSocket wire1.2 KiB1.2 KiB0 B (0.0%)2.0 KiB✅
CodexLive turn WebSocket decoded20.9 KiB20.9 KiB0 B (0.0%)29.3 KiB✅
CodexLive turn messages220 (0.0%)8✅
ClaudeTotal thread wire5.0 KiB5.0 KiB−24 B (−0.5%)6.8 KiB✅
ClaudeThread snapshot wire3.8 KiB3.8 KiB0 B (0.0%)4.9 KiB✅
ClaudeLive turn WebSocket wire1.2 KiB1.2 KiB−24 B (−2.0%)2.0 KiB✅
ClaudeLive turn WebSocket decoded21.2 KiB21.2 KiB−41 B (−0.2%)29.3 KiB✅
ClaudeLive turn messages21−1 (−50.0%)8✅

Baseline: 4df84a7 · PR result: 313f02a · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

Added 3 commits

  • refactor: layer variables are named layer or layerXyz909e8c4
  • refactor: functions that build a layer are named layer or layerXyz78f5a87
  • test(mobile): the runtime mock exports the renamed layer313f02a