Skip to content

feat(codex): submit thread feedback to OpenAI

MacroscopeApp / Macroscope - Effect Service Conventions failed Aug 23, 2026 in 2m 27s

Effect Service Conventions: 1 issue found

packages/contracts/src/provider.ts / apps/server/src/ws.ts — unstructured RPC error that drops the underlying failure

The new ProviderUploadFeedbackError declares only message: Schema.String with no structural attributes and no cause, and the WS_METHODS.providerUploadFeedback handler builds it with message: cause.message, discarding the structured ProviderServiceError. Conventions require service failures to carry stable structural attributes (thread/operation context), to derive message from those attributes rather than from cause.message, and to preserve the immediate underlying error as cause. Two inline comments were posted with a suggested fix.

No other violations found: the adapter, service, and session-runtime additions follow the existing routing, span annotation, domain-error pass-through, and namespace-import patterns, and the client-side changes stay at React/native callback boundaries.

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 examined in scope:

  • packages/contracts/src/provider.ts, packages/contracts/src/rpc.ts — new schemas, error class, and RPC registration.
  • apps/server/src/ws.ts — new RPC handler and error mapping (compared against the surrounding Effect.mapError handlers, which consistently pass structural context plus cause).
  • apps/server/src/provider/Layers/ProviderService.ts, Services/ProviderService.ts, Services/ProviderAdapter.ts, Services/CodexAdapter.ts — new uploadFeedback method: input decoding via decodeInputOrValidationError, session routing via resolveRoutableSession, unsupported-provider validation error, and span annotations all match the existing methods in the same module.
  • apps/server/src/provider/Layers/CodexAdapter.ts, CodexSessionRuntime.ts, CodexProvider.ts — uploadFeedback mirrors interruptTurn/readThread: it passes through ProviderAdapterSessionNotFoundError and normalizes runtime failures through the existing mapCodexRuntimeError.
  • packages/client-runtime/src/state/threadCommands.ts, threadFeedback.ts, threads.ts — pure helper plus an RPC command created with the existing createEnvironmentRpcCommand factory; named import of @t3tools/contracts is allowed.
  • apps/web/src/components/ChatView.tsx, apps/mobile/src/state/use-thread-composer-state.ts, apps/mobile/src/features/threads/ThreadComposer.tsx — command invocation stays inside React/native callback boundaries; no ManagedRuntime.make, runPromise, runPromiseExit, or service-instance injection was introduced. Cause is imported as import * as Cause from "effect/Cause".
  • Server test harnesses updated mechanically with uploadFeedback stubs; behavior changes are covered by focused tests in CodexAdapter.test.ts, ProviderService.test.ts, RpcAuthorization.test.ts, server.test.ts, and provider.test.ts.

No findings were raised about the pre-existing *Shape interfaces in apps/server/src/provider/Services/, since this PR only extends them rather than creating or moving a service, and repository-wide cleanup is out of scope for this check.


🔍 Agent activity — 10 tool calls

💭 I'm scanning the diff for Effect service convention violations, checking that imports use proper subpath namespaces and looking at how the ProviderUploadFeedbackError contract error is defined to see if it follows the expected pattern for service failures. I'm concerned about ws.ts mapping the cause's message into a new error, which erases the structured upstream error into a plain string—though RPC boundaries might require this for serialization, so I want to compare against how other RPC methods handle it. I'm also checking whether CodexAdapter.ts still retains a standalone shape interface that should have been removed per convention. But the ProviderAdapterShape and its uploadFeedback addition look consistent with existing patterns, since the shape predates this PR—so it's not something to flag. I want to verify how ws.ts actually maps errors to RPC-compatible schemas before deciding whether the CodexAdapter's error pass-through is correctly structured.

1. git_tools — new Provider.*Error({

2. git_tools — TaggedErrorClass

💭 There's precedent for message-only error schemas in existing wire errors, so this new error type follows established convention. But the stricter guidance says structured attributes should be preferred over an unstructured message field, so I need to weigh whether to follow the newer convention or match existing patterns. The mapError call in ws.ts collapses a structured ProviderServiceError down into just a message string, which is arguably erasing structure the guidance warns against. I want to check how other ws.ts handlers map provider or orchestration errors to see if this is actually the repo's established pattern before flagging it. There's also the uploadFeedback path where an undefined adapter triggers a validation error helper — I should verify that helper follows the expected error-construction shape, though it looks pre-existing.

3. git_tools — Effect.mapError

💭 I'm noticing that ProviderUploadFeedbackError only has a message field derived from cause.message, which erases the structured upstream ProviderServiceError into a plain string — violating the rule that wrapper messages must come from stable structural attributes, not from cause, and that the original error should be preserved as cause itself. That's a solid finding, worth two inline comments: one flagging the missing structural fields/cause in the contracts file, another flagging the message-from-cause.message issue in ws.ts. Now I'm scanning other areas — the mobile composer hook and web ChatView's use of runPromise/ManagedRuntime look fine since they're React boundaries, and threadFeedback.ts is a pure helper with no Effect usage. I check the named import of WS_METHODS from the contracts package, which is allowed, then verify CodexSessionRuntime.uploadFeedback returns a properly typed error. Looking at CodexAdapter.uploadFeedback's mapError logic, it passes through a known tagged failure or maps the runtime error — I'm weighing whether this matches the guideline about catching statically known tagged failures with Effect.catchTags, since this is mapError rather than catching. I'm still unsure whether the mapError also inadvertently covers requireSession failures, and whether mapCodexRuntimeError is an established normalizer elsewhere in the codebase, so I want to pull up similar patterns in CodexAdapter to compare.

4. git_tools — mapCodexRuntimeError

💭 The CodexAdapter's uploadFeedback matches existing patterns well. I'm now verifying that ProviderValidationError's usage is consistent with how ProviderService.ts handles unsupported operations elsewhere, then moving to check how the mobile composer hook handles error squashing. The mobile hook imports Cause properly and follows the same squashing approach as the web ChatView's squashAtomCommandFailure, so that's consistent. On the ProviderAdapterShape/CodexAdapterShape pattern, retaining standalone Shape interfaces here isn't a violation since this PR is extending existing shapes rather than creating a new service, so I'll skip flagging it as unnecessary repo-wide cleanup. Looking closer at ProviderUploadFeedbackError, it only carries an unstructured message with no cause field, which conflicts with the guidance to use structured attributes and preserve the underlying cause — though I notice AuthAccessStreamError and some EnvironmentHttp errors follow this same message-only boundary pattern already. The clearest issue is in ws.ts where the message is derived directly from cause.message and the cause itself is dropped entirely, which the instructions treat as authoritative over this older precedent. There's also a concern about copying an upstream error's raw message into the wire-facing message field, since guidelines call for keeping error attributes sanitized rather than passing through arbitrary defect text — I'll flag that too. I checked a few other spots (CodexSessionRuntime's uploadFeedback, the toValidationError helper, and span annotations in ProviderService) but those look consistent with existing patterns and don't seem problematic. I noticed ProviderUploadFeedbackError uses a plain Schema.String for its message field instead of TrimmedNonEmptyString like other contracts, which is a minor inconsistency worth mentioning. I'm now checking whether ProviderService has an existing pattern for optional adapter methods that don't support certain operations, to see if this new method should follow that convention.

5. git_tools — does not support

6. browse_code — apps/server/src/provider/Layers/ProviderService.ts

💭 ProviderService.uploadFeedback looks consistent with existing conventions. Checking ws.ts and the contracts, I see both AuthAccessStreamError and EnvironmentAuthorizationError as precedent for message-only errors, but the preferred newer pattern calls for structural attributes with cause and a message derived from that structure. I'll note this as a finding, likely with an inline comment pointing out the mismatch. I'm now deciding whether to place two separate review comments — one on ws.ts (where the structured ProviderServiceError gets erased and the message is built from cause.message without preserving cause) and one on contracts/provider.ts (where the error class lacks structural fields or a cause). To reduce noise, I'll pick specific line ranges: for ws.ts around lines 1544-1549 covering the mapError block, and for contracts/provider.ts around lines 124-128 covering the error class definition and message field, making sure both fall within the diff's added line ranges.

7. browse_code — packages/contracts/src/provider.ts

8. browse_code — apps/server/src/ws.ts

💭 Since this check defaults to failure whenever there are findings, I have a finding, so the state should be failure. Now I'm weighing whether the wire error genuinely violates the convention around defining service failures with Schema.TaggedError, checking against repo precedents to make sure it holds up. The new error only stores an unstructured message copied from the upstream error, discarding the structured ProviderServiceError context (threadId, operation, provider) and dropping the cause entirely — this looks like a real, newly-introduced violation, especially since the raw upstream message ends up crossing the wire into UI toasts, which conflicts with the rule against copying arbitrary defect text into user-facing fields. I'll skip the minor TrimmedNonEmptyString point and instead double-check the mobile hook file for a hidden runtime issue around runPromise usage in the changed function region.

9. browse_code — apps/mobile/src/state/use-thread-composer-state.ts

💭 I'm noting that the new wire error type only carries an unstructured message field copied from the upstream failure, missing structural context like threadId, provider, or reason, and lacking a cause chain—I should flag this alongside the related contracts change and suggest adding structured fields with optional metadata. But since fixing this properly means the schema needs to require a threadId and cause, and that would also require corresponding changes in ws.ts, an inline suggestion won't be self-contained. I'll instead propose a unified diff covering both files, keeping the contracts comment focused on the shape of the fix while pointing out the cross-file dependency. Now I'm drafting the second comment for ws.ts, showing how the error should carry the structural threadId and preserve the underlying cause instead of just its message. Then I'll move on to deciding whether this review should be flagged as a failing state.

10. modify_pr — Reviewed the new provider feedback path against the Effect service conventions. The adapter/service layers (CodexAdapter.uploadFeedback, ProviderService.uploadFeedback, CodexSessionRuntime.uploadFeedback) follow the existing routing, span-annotation, and domain-error pass-through patterns. One issue: the new RPC-boundary error is unstructured and drops the underlying failure.


Agent Credits: 123 credits