Repository navigation
Effect Service Conventions: 1 issue found
apps/server/src/cloud/ManagedEndpointRuntime.tsβ the newisRetryableManagedEndpointRuntimeStatuspredicate decides retryability by prefix-matching the free-formreasonmessage ("Relay client is unsupported on "), and that decision drivesEffect.retry({ while: ... })inapps/server/src/server.ts. Control-flow discriminators should be structural: add a stable field to thefailedvariant ofCloudManagedEndpointRuntimeStatus(set whereexecutable.status === "unsupported"is already known) and branch on it, leaving the caller-visiblereasontext unchanged.
Previously reported findings are resolved at this head commit: the Effect.catchTag usages in infra/relay/src/environments/ManagedEndpointReaper.ts and infra/relay/src/environments/ManagedEndpointProvider.ts now use Effect.catchTags, and the startup recovery handler in apps/server/src/server.ts uses Effect.catch instead of Effect.catchCause, so interruption is no longer downgraded to a warning.
Details
Note
Your check run agent prompt is: .macroscope/check-run-agents/effect-service-conventions.md
More information about how Check Run Agents work can be found in our Help Center.
Files reviewed in scope: apps/server/src/cloud/ManagedEndpointRuntime.ts, apps/server/src/cloud/http.ts, apps/server/src/server.ts, infra/relay/src/environments/ManagedEndpointReaper.ts, ManagedEndpointAllocations.ts, ManagedEndpointProvider.ts, infra/relay/src/http/Api.ts, infra/relay/src/worker.ts, infra/relay/src/persistence/schema.ts, infra/relay/src/deploymentConfig.ts, packages/contracts/src/relay.ts, plus the touched test files.
Checked and found compliant:
- New
ManagedEndpointReapermodule follows the canonical layout (imports, result type,Context.Servicetag with inline interface,make,layer) and acquires all dependencies viayield* Foo.Foo; no service-instance parameters,ManagedRuntime.make, orrunPromisein service code. - Namespace imports from
effect/*subpaths throughout; named imports are limited to whole packages (drizzle-orm,@t3tools/contracts) and pure helpers (isSqlError,managedEndpointTunnelNamePrefix). ManagedEndpointAllocationPersistenceErrorextension keeps structuredoperation/stage/entity fields withcause, and the derivedmessageis built only from those attributes.- New tagged-failure handling uses
Effect.catchTags;Effect.resultin the sweep loop isolates per-tunnel failures so one bad tunnel does not abort the run, and both the failure-continuation and success paths are covered byManagedEndpointReaper.test.ts. - Behavior changes (recovery endpoint, generation-based optimistic concurrency, link-state lock serialization) come with focused tests using test layers rather than mocked business logic.
π Agent activity β 13 tool calls
π The earlier catchTag issue from commit 26c5820 looks resolved at the current head a86c6d1 β the diff now shows proper use of Effect.catchTags in the endpoint reaper and provider files, and Effect.catch in server.ts. I'm now moving on to scan the rest of the current diff for any new violations. I'm checking imports and structure across ManagedEndpointReaper.ts β the service definition, helper placement, and layer construction all look fine, and dependencies are properly acquired. I do notice a placeholder pattern using wildcard userId and environmentId values in listByTunnelNames, which seems minor, but I'm flagging the lockError helper in withClaimedTunnel since it appears to be a single-use constructor function that just wraps a new error object, which conflicts with the convention against trivial error-wrapping helpers. Actually, since it's used twice within the same function and provides shared context, I don't think it's worth flagging. I also want to check ManagedEndpointProvider.ts, and I want to verify catchTag removal by comparing against the current head versions of ManagedEndpointReaper.ts and server.ts.
1. git_tools β catchTag(|catchCause|from "effect"
2. git_tools β 26c5820..a86c6d1
π The prior commit isn't in the repo, likely due to a force-push, but the diff confirms the earlier findings were addressed with catchTags and Effect.catch in server.ts. Now I want to dig deeper into the new code, checking ManagedEndpointReaper.ts along with the relevant sections of ManagedEndpointProvider.ts and http.ts for any remaining issues.
3. browse_code β infra/relay/src/environments/ManagedEndpointReaper.ts
4. browse_code β infra/relay/src/environments/ManagedEndpointAllocations.ts
π I'm checking the enableRecovery error handling since it spreads the input object, including environmentPublicKey, into the error even though that field isn't part of the schema β likely just gets dropped. Since it's a public key rather than a secret, this isn't really a safety concern for error attributes. Then I'm noticing
listByTunnelNamesuses "*" as sentinel values for userId and environmentId since this operation isn't scoped to a specific entity β a bit hacky but probably fine given no real per-entity context exists here. I decide not to flag it since fixing it would require schema changes for a minor stylistic issue. Now I want to look at the HTTP layer and runtime files to check similar patterns there. I spotisRetryableManagedEndpointRuntimeStatustaking anunknownparameter with structural checks rather than using the typed status directly, which could be a case of a private schema predicate wrapped redundantly. I also notice string-matching on areasonfield likestartsWith("Relay client is unsupported on ")to drive control flow, which conventions suggest should instead be split into separate error classes when the reason determines user-facing messaging or caller behavior β though here it's operating on a status object rather than an error class, so I want to check what that status type actually looks like before deciding whether to flag it. Turning torecoverManagedCloudTunnelin http.ts, I see it maps errors to a newEnvironmentHttpInternalServerErrorwithout preserving the original cause, which conflicts with the convention of retaining the underlying error ascausewhen wrapping failures. I need to check whether that error class even supports a cause field before treating this as a real finding, so I'm going to look at the error definitions and the surrounding code in http.ts.
5. browse_code β apps/server/src/cloud/http.ts
6. browse_code β apps/server/src/cloud/http.ts
π I'm noticing that EnvironmentHttpInternalServerError looks like a legacy message-only error class, and the mapError call here discards the original cause. If that error class actually supports a cause field per convention, dropping it would be a real issue worth flagging, so I want to check its definition.
7. git_tools β class EnvironmentHttpInternalServerError|class EnvironmentHttpBadRequestError|class EnvironmentCloudEndpointUnavailableError
8. git_tools β EnvironmentHttpInternalServerError extends|EnvironmentCloudEndpointUnavailableError extends
π These wire contract errors are pre-existing and message-only, so dropping the cause field isn't a new issue. Next I want to check the isRetryableManagedEndpointRuntimeStatus predicate in ManagedEndpointRuntime.ts, which does structural checks on an unknown value including string prefix matching on the reason field, so I'll look at the status type and existing patterns there.
9. browse_code β apps/server/src/cloud/ManagedEndpointRuntime.ts
π I'm noting that this retryability check uses string-prefix matching on the reason field rather than a typed error class, since it's operating on a status union with an "unsupported" variant rather than a distinct error typeβwhich seems to conflict with the convention favoring separate error classes for semantically distinct failures that drive control flow.
10. git_tools β is unsupported on
π I notice the failed status variant relies on a free-form
reason: string, and the retry logic does prefix-matching against that human-readable text to control flow -- this violates the convention that semantically distinct failures should be separate error classes rather than encoded in a message string meant for display, not branching logic. The fix should add a structured field like akindorretryablediscriminator alongside the existing reason, since that reason string is caller-visible (transmitted over HTTP, possibly shown in UI), so I need to preserve it rather than replace it -- this is a real convention violation worth flagging since it's string-prefix matching on what's essentially a display message. I'll write one inline comment on ManagedEndpointRuntime.ts targeting the new predicate lines, suggesting the structured discriminator approach. It's a moderate ask since it touches the status type, construction site, and predicate, but it's legitimate given the guidance against deriving control flow from unstructured messages. Now checking the rest of the diff for other convention issues: the semaphore usage in server.ts and the service layer look fine, the error catching pattern with logWarning is good, and theHttpApiError.Unauthorizedreuse across distinct conditions in Api.ts seems like a deliberate security choice rather than a convention violation, so I'll leave that alone. I'm also verifying the test harness and mock layer usage look fine, then scanning the touched files for any consolidated imports from "effect" or non-namespace service imports that would break the established pattern.
11. git_tools β ^import {[^}]*} from "effect"|^import { (layer|make) as
12. browse_code β apps/server/src/server.ts
π I'm weighing whether the string-prefix retry predicate matching is worth flagging as a genuine finding under the errors/predicates conventions, and leaning toward posting an inline comment on that new predicate. I'm also checking whether the recovery logic here counts as a single entity versus multiple independent entities needing interruption handling, and looking at whether the server test changes actually cover both the retry-success and persistent-failure paths. Scanning
ManagedEndpointRuntime.ts, theapplyConfigreturn status looks fine. InManagedEndpointProvider.ts, thereleasefunction's new optional parameters use a non-null assertion despite an earlier undefined check -- that's a code-quality nitpick outside the conventions, so I'll skip it. I'm also considering whether theManagedEndpointAllocationPersistenceErrorusing a"*"sentinel foruserIdin the list-tunnels case counts as fabricated context that violates the guidance to capture stable, serializable domain identifiers, weighing whether that's worth reporting or too minor to flag. I'll skip that sentinel issue since there genuinely isn't an entity to reference there, leaving me with just one real finding: the status-check logic relies on prefix-matching a human-readable reason string rather than a structured discriminator, which I'll flag with an inline comment spanning the relevant lines in the diff, without a suggested diff since the fix touches multiple places. Since a prior review only caught catchTag/catchCause issues and this is a new violation, I'll mark the overall check as a failure per the conventions that default to failing when a clear violation exists. That said, the status union itself is pre-existing and unchanged, so the real target would be the predicate that parses it, not the type definition β and demanding a rewrite of legacy code feels out of scope. Still, the predicate does exactly what the conventions warn against: inferring control flow from a stringly-typed status when a structured "unsupported" variant already seems to exist. I'm weighing whether to post this as a finding and default to failure per the instructions, or reconsider and call it "all clear" given how borderline it is β the fact that a minimal, localized fix exists nudges me toward reporting it, but I want to double-check against what the prior run flagged before deciding.
13. modify_pr β One convention finding on the new retry predicate. Previously reported Effect.catchTag usages in ManagedEndpointReaper.ts, ManagedEndpointProvider.ts and the Effect.catchCause in server.ts are resolved in this head commit.
Agent Credits: 156 credits