Repository navigation
Effect Service Conventions: 1 issue found
1 finding
apps/web/src/hooks/useThreadActions.ts(newreorderPinnedThread): the reorder-unsupported path fails with the existingThreadPinningUnsupportedError, whose caller-visible message ("...does not support pinning yet. Update the server to use Pin.") describes a different capability. The sidebar drag handler surfaces that message in its failure toast, so a reorder-capability gap reports misleading guidance. Conventions call for distinct error classes when the tag drives the user-facing message; aThreadPinReorderUnsupportedErroralongside the settlement/snooze equivalents is the expected fix. An inline comment was posted.
No other violations found in the changed Effect scope.
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.
Scope reviewed (include patterns only): packages/contracts/src/orchestration.ts, packages/contracts/src/environment.ts, apps/server/src/orchestration/{decider,projector,Schemas}.ts, apps/server/src/orchestration/Layers/{ProjectionPipeline,ProjectionSnapshotQuery}.ts, apps/server/src/persistence/** (including new migration 038_ProjectionThreadsPinOrderKey.ts), apps/server/src/environment/ServerEnvironment.ts, packages/client-runtime/src/{operations/commands.ts,state/*}, and the web/mobile call sites.
Checks performed and passing:
- Imports: all touched Effect usage imports subpath namespaces (
import * as Effect from "effect/Effect",import * as SqlClient from "effect/unstable/sql/SqlClient",import * as Cause from "effect/Cause"); no consolidated named imports from"effect".@t3tools/contractskeeps named imports, as intended. - New migration module matches the established shape (default-exported
Effect.gen,SqlClient.SqlClientfrom the environment, idempotent PRAGMA guard). - No new service definitions, tags, or layers were introduced, so no
Context.Service/make/layerordering or file-layout migration concerns apply; no compatibility shims for service modules were added. - Searched the diff and affected call sites for
Layer.succeed,ManagedRuntime.make,.runPromise,.runPromiseExit, and service-instance parameters: none introduced. Command dispatch stays insideEffect.fn(reorderPinnedThread) and the React/atom command boundary. - Decider failure construction (
OrchestrationCommandInvariantErrorwithcommandType+ structureddetail) matches the surrounding, pre-existing invariant style and does not stringify a cause. - Backend behavior change (new
thread.pin.reordercommand /thread.pin-reorderedevent) is covered by focused decider tests indecider.pinned.test.tsplus key-math tests inthreadSort.test.ts.
🔍 Agent activity — 1 tool call
💭 Looking at the changed files to understand how the Effect service conventions are being applied, particularly in the new migration file and comparing it against existing patterns in decider.ts and projector.ts. I need to verify the imports and error handling approach are consistent across the codebase, especially where OrchestrationC is being used. I'm noticing an inconsistency in how errors are being thrown in this code—the new decider is using
Effect.fail()to wrap the error, but the existing code at line 306 throws the error directly without that wrapper, and line 459 usesEffect.fail()as well, so I should check which pattern is the correct one to follow here. Looking at the imports and re-exports in Sidebar.logic.ts, I see there's a compatibility shim being set up that re-exports some functions from the thread-sort state module, including a renamed export ofsortPinnedThreadsByOrderKeyassortPinnedThreadsForSidebarV2for backwards compatibility. Looking at the error handling in the mobile thread list actions—it's using the Alert pattern witherror.messagefrom the squashed Cause, which matches what's already there. I noticeThreadPinningUnsupportedErrorgets reused in the reorder function with a message about server updates, which would be confusing to users in that context, though that's more of a UX issue than a convention problem. The conventions actually call for splitting semantically distinct failures into separate error classes so callers can pick the right user-facing message. Here, reordering unsupported and pinning unsupported are different failures but share the same error tag—the web toast would show "does not support pinning yet" for a reorder failure, which is misleading. There's precedent for this:ThreadSettlementUnsupportedErrorandThreadSnoozeUnsupportedErrorare separate classes with distinct messages. So by that standard, a newThreadPinReorderUnsupportedErrorwould be the right approach—that's a solid, specific finding worth flagging. posed through ... UI ... as behavior... Existing distinct caller-visible messages are evidence that the failures should normally remain distinct error tags". This is about reuse producing a wrong message. I'd flag it as a minor inline comment. But the check "defaults to failure" — so if I have findings, I fail. Careful: a false positive would be bad. Let me evaluate whether this failure path is user-visible. In SidebarV2's handlePinnedDragEnd, on failure it toasts "Failed to reorder pinned threads" with descriptionerror.message— which for this error would be "This environment's server does not support pinning yet. Update the server to use Pin." That's misleading/wrong guidance. So it's a genuine convention-relevant issue: distinct failure semantics sharing one error tag with a message that describes a different capability. The plain Error thrown in threadSort's pinOrderMidpoint helper isn't a service convention violation since it's guarded before I'm planning to add an inline comment on the added lines in the useThreadActions.ts file suggesting a new ThreadPinReorderUnsupportedError class, and I'll need to provide a fix that involves creating this error class in another location.
1. modify_pr — Reviewed the Effect-side changes (contracts schemas, decider/projector cases, migration 038, client-runtime command atoms, and the web/mobile call sites) against the service conventions. Imports, Effect.fn/Effect.gen usage, and the new migration module follow the existing patterns, and no Layer.succeed/ManagedRuntime.make/runPromise misuse was introduced. One error-modeling violation found.
Agent Credits: 76 credits