Repository navigation
Effect Service Conventions: 2 issues found
apps/server/src/cli/theme.ts:296-298—ThemeFileUnreadableErroris constructed with a manufacturedcause: "no such file"on the path-shaped-but-missing branch, where no underlying failure exists. Makecauseoptional and omit it here socauseonly ever carries a real error.apps/server/src/cli/theme.ts:110-118—ThemeIdInvalidError.fromFileis a boolean discriminator used solely to select the user-facing remedy inmessage; these should be two distinct tagged error classes, each deriving its message from its own attributes.
Previously flagged items are resolved: the CLI now defines a Schema.TaggedErrorClass per distinct failure with structured attributes and preserved causes, and environmentTheme.ts exports make alongside layer.
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.
Reviewed the new/changed Effect service code in scope:
apps/server/src/environmentTheme.ts— canonical layout (imports, schema/predicate declarations,Context.Servicetag with inline interface, exportedmake,export const layer = Layer.effect(...)), dependencies acquired from the environment (ServerConfig.ServerConfig,FileSystem.FileSystem), namespace imports fromeffect/*subpaths, named imports from@t3tools/contracts. No findings.apps/server/src/server.ts,apps/server/src/ws.ts,apps/server/src/server.test.ts— consume the service via the module namespace (EnvironmentTheme.EnvironmentThemeService) and useEnvironmentThemeService["Service"]for the mock/test shape. No findings.apps/server/src/cli/theme.ts— per-failure tagged errors with structured attributes and messages derived from those attributes; two findings noted above.packages/contracts/src/server.ts,packages/contracts/src/settings.ts,packages/contracts/src/rpc.ts— schema/contract additions only; no service definitions.apps/web/src/hooks/useEnvironmentTheme.ts,useDefaultTheme.ts,state/server.ts,themePalette.ts— React/atom layer; noManagedRuntime.make,runPromise,runPromiseExit, service-instance parameters, orLayer.succeedhiding runtime-backed work were introduced anywhere in the changed scope.
Private const isEnvironmentThemeId = Schema.is(EnvironmentThemeId) constants in theme.ts and environmentTheme.ts match the prevailing repository pattern (unexported Schema.is constants at module scope, not wrapped in redundant functions) and were not flagged.
🔍 Agent activity — 6 tool calls
💭 Comparing the prior flagged commit against the new head, it looks like the separate TaggedErrorClass per failure and the exported
makein environmentTheme.ts both address the earlier review comments. Now I want to check the current files directly for any remaining import or namespace issues. Scanning theme.ts and environmentTheme.ts, the imports and file structure (imports, error/schema declarations, tag, make, layer) look correctly ordered. The one thing that catches my eye is the privateisEnvironmentThemeIdpredicate defined locally in both files rather than being exported once from a shared contracts module — that duplication seems worth flagging.
1. browse_code — apps/server/src/environmentTheme.ts
2. browse_code — apps/server/src/cli/theme.ts
💭 With both prior findings resolved, I'm now scanning for remaining violations, specifically looking at whether ThemeIdInvalidError's boolean fromFile field acts as a discriminator that should split into separate error classes per the convention. Filename-derived ids vs directly-provided ids really are different failures with different remedies, so I'll flag that as a low-severity split-class issue, though I'm not confident it should fail the check outright. Second, I'm looking at
ThemeFileUnreadableErrorwhere the cause is a fabricated string"no such file"rather than a real underlying error — this directly violates the rule against manufacturing a cause, since there's no genuine failure being wrapped and the cause should instead be optional. I'm also noting that the missing-file case gets lumped into "unreadable" with a misleading "Could not read" message, which seems worth a concrete inline comment. TheSchema.Defect()usage checks out since its fields structurally identify the error, but the private duplication ofisEnvironmentThemeId = Schema.is(EnvironmentThemeId)across two modules instead of exporting it once from contracts looks like another flaggable convention gap. I'm checking naming conventions for the service tag key against other services, then looking at how the scoped effect and finalizer work in environmentTheme.ts given the Layer.effect scoping behavior in this version of Effect — that seems fine. Now I'm moving on to look at ws.ts. Everything checks out: the namespace import pattern is consistent, server.test.ts correctly uses Layer.mock with the Service key, and the web hook imports Equal properly. I'm noting that the module-global mutable state in themePalette.ts is a client-side store rather than an Effect service, so the "don't hide dependencies in module globals" rule doesn't really apply there. I'm now checking useDefaultTheme.ts for any Effect service usage — there isn't any — and scanning the diff for stray runPromise or ManagedRuntime calls, finding none. Next I want to look at how contracts/src/server.ts is styled, specifically whether there are existing patterns likeSchema.isexports elsewhere in contracts to follow.
3. git_tools — Schema.is(
4. git_tools — = Schema.is(
💭 I'm noting that private
Schema.isconstants are a common convention, so I won't flag that duplication. Looking at the main issue, I'm concerned about fabricating a cause string like "no such file" forThemeFileUnreadableError—this conflates a missing file with an unreadable one, though I need to check what the test actually asserts before deciding whether a separate error type is warranted. Since the test only checks for "Could not read" regardless of whether the file exists, the simplest fix is makingcauseoptional rather than inventing a fake value, since this is exactly the legitimate case for an optional cause. I also want to flag the boolean discriminator inThemeIdInvalidError.fromFilethat picks the remedy message—per convention this should likely be split into separate error classes, one for direct argument errors and one for filename-derived ones, and I'll suggest that split while also checking the ws.ts changes and theenvironmentThemesgating logic to confirm those are behavioral rather than convention-related concerns. I'm now checking the server config for the environment themes directory addition and confirming the test layer setup is fine. I want to verify the naming and location conventions for the environment theme service relative to keybindings, check whether the scope/finalizer/fork pattern used for the file watcher counts as a hidden runtime concern, and compare the service tag string style and getter usage against how keybindings.ts handles similar patterns.
5. git_tools — Context.Service<
💭 I'm flagging that fabricated
cause: "no such file"string in theme.ts line 297 — the convention against manufacturing errors to populatecauseapplies equally to fake string literals, so I want to note this as an inline comment. For the second finding, thefromFileboolean at lines 110-118 acts as a discriminator selecting the user-facing message, which fits the convention about splitting semantically distinct failures into separate error classes — I'll suggest that as a comment too. Neither issue feels severe enough to fail the check outright, since both are genuine but modest violations. Since inline comments need to target lines actually present in the diff, and this is a new file so every line qualifies, I'll place comment 1 on the cause construction around line 297 and comment 2 spanning lines 110-118 for the fromFile discriminator. For the cause fix, making it optional would touch both the field declaration and call site, so I'll just describe the fix in prose rather than provide a diff; keeping the error message "Could not read" intact while making cause optional is simpler than introducing a whole new error class.
6. modify_pr — Two error-modeling nits in apps/server/src/cli/theme.ts; the earlier findings (per-failure tagged errors, exported make in environmentTheme.ts) look addressed.
Agent Credits: 87 credits
Token usage: 10 uncached input · 281,199 cache-read input · 81,336 cache-write input · 6,841 output
Agent Credits may also include non-token charges from external tools such as web research.