Repository navigation
Effect Service Conventions: 2 issues found
apps/server/src/provider/Drivers/HermesDriver.ts— duplicates the canonicalwithInstanceIdentityhelper fromDrivers/instanceIdentity.tsinstead of importing it; every other driver (Codex, Claude, Cursor, Grok, OpenCode) uses the shared module. Inline comment posted with the fix.apps/server/src/provider/Layers/HermesAdapter.ts— new backend behavior (steer/merge turn accounting viapromptsInFlight, plan fingerprint de-duplication,full-accesspermission auto-approval, ACP mode alias resolution) ships without any adapter test, unlike every peer adapter. Raised in the review body.
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: all new/changed TypeScript under apps/** and packages/** in this PR (HermesDriver, HermesAdapter, HermesProvider, HermesAcpSupport, HermesTextGeneration, Services/HermesAdapter, serverSettings, contracts/settings, updated tests).
Checked and found conforming (compared against the established Codex/Cursor/Grok equivalents at the merge base):
- Imports: Effect modules are imported as subpath namespaces (
effect/Effect,effect/Layer,effect/unstable/process/ChildProcessSpawner);@t3tools/contractsuses named imports, which the conventions permit. - Service definition/layout: Hermes ships no new
Context.Servicetag;Services/HermesAdapter.tsonly re-declares the sharedProviderAdapterShapealias, mirroringServices/GrokAdapter.ts, and the driver bundles the adapter per instance. No compatibility shims or leftover files. - Dependency acquisition:
makeHermesAdapter,makeHermesTextGeneration, andcheckHermesProviderStatusall acquireCrypto,ChildProcessSpawner,FileSystem,Path,ServerConfig, andServerSettingsServicefrom the Effect environment. ThechildProcessSpawner/httpClientparameters onmakeHermesAcpRuntimeandenrichHermesSnapshotreproduce the existingGrokAcpSupport/CursorProvidersignatures required byAcpSessionRuntime.layer, and theresolveSettingsoption is the same documented test seam used byCursorAdapter/OmpAdapter. NoManagedRuntime.make,runPromise, orrunPromiseExitwas introduced. - Errors: failures are
Schema.TaggedErrorClassinstances with structural fields and a preservedcause;Effect.catchTagsis used for the knownSchemaErrortag; no new lint/type-checker suppressions were added beyond the existing@effect-diagnostics nodeBuiltinImport:offin the test file, which matches sibling test modules. Schema.isusage (isTextGenerationError) is a direct predicate, not a redundant wrapper function.
🔍 Agent activity — 25 tool calls
💭 I'm looking at the diff across HermesDriver.ts, HermesAdapter.ts, HermesProvider.ts, and related acp/text-generation files to check for Effect service convention violations, keeping in mind this PR largely mirrors the existing Grok/Cursor driver pattern in the repo. I want to verify against the analogous Grok files before flagging things: the
HermesAdapterShapeinterface inServices/HermesAdapter.tsmight just mirror an existingGrokAdapterShape, and the manualLayer.buildplus passing a rawChildProcessSpawnerservice instance intomakeHermesAcpRuntimecould similarly be a pattern already established in GrokAcpSupport rather than a new violation. I'm also noting the error construction using template-string details inProviderDriverErroras a potential issue to check against existing error-handling conventions. I'm also spotting several places wherecause.messagegets copied straight into an error'sdetailfield — in the adapter's process error, the request error duringsendTurn, and the driver's snapshot-build failure — all of which look like they violate the rule against deriving wrapper messages fromcause.messagerather than stable structural data. Meanwhile theEffect.catchTagsandEffect.catchusages in the text-generation and adapter files seem fine, and the privateSchema.ispredicate constant appears to be used directly rather than wrapped redundantly, so that's probably not a problem. I'm moving on to check the imports inHermesAdapter.ts, starting with theChildProcessSpawnernamespace import. I should treat the instructions as authoritative even where older code diverges, focusing only on concrete violations within the PR's changed scope rather than demanding unrelated cleanup — and since these are new files, they count as changed scope. Time to actually dig into the repo itself.
1. browse_code — apps/server/src/provider/**/Grok*.ts
2. browse_code — apps/server/src/provider/acp/*.ts
💭 I want to check GrokAcpSupport.ts and GrokDriver.ts for comparison.
3. browse_code — apps/server/src/provider/acp/GrokAcpSupport.ts
4. browse_code — apps/server/src/provider/Drivers/GrokDriver.ts
💭 I notice HermesDriver duplicates a local
withInstanceIdentityinstead of importing the shared helper GrokDriver uses from instanceIdentity.ts. That's duplication rather than an actual Effect Service Convention violation, so I want to check instanceIdentity.ts to see if it's worth flagging anyway. The Hermes snapshot error'sdetailinterpolatescause.message, mirroring an existing repo pattern in Grok — flagging it is defensible under the guidance against copying cause.message, but since this seems to be a wholesale copy of a repo-wide convention, I should check ProviderDriverError and other drivers' detail usage before deciding whether to report it.
5. git_tools — Failed to build .* snapshot
6. browse_code — apps/server/src/provider/Services/GrokAdapter.ts
💭 I notice HermesDriver and its adapter follow the same uniform pattern used across all six drivers, so that's not a deviation worth flagging—it's just the repo convention. But I'm second-guessing whether to flag the
detail: Failed to build X snapshotpattern, since the guidance explicitly says not to add adetailfield that merely duplicatescause.message. I want to be careful about false positives, so I'm narrowing my focus to genuine Hermes-specific deviations from the repo's own conventions and the explicit rules. One thing that stands out: HermesDriver defines its own localwithInstanceIdentityhelper instead of importing the sharedinstanceIdentity.tsmodule, which could be duplicated code if that shared file already existed at the merge base—I need to check its git history to confirm whether this duplication was actually introduced by this PR. Since GrokDriver at the PR head does import the shared helper frominstanceIdentity.ts, HermesDriver's local duplicate version (which also stamps adriver: DRIVER_KINDfield) is a real, if minor, inconsistency worth flagging. Checking the rest of the Hermes files against the Grok equivalents—the adapter's service usage, the text generation factory, the provider status check, and the ACP model discovery pattern—everything else appears to mirror the established conventions correctly. I'm also weighing whetherdiscoverHermesModelsViaAcpaccepting a rawChildProcessSpawnerservice type should be flagged, but since it matches the repo's existing pattern for Grok and Cursor ACP support (driven by whatAcpSessionRuntime.layerrequires), I won't call it out as a new issue. Now I'm scanning the diff for genuinely novel deviations, checking things like import naming and ordering conventions in HermesAdapter.ts. I notice HermesAdapter.ts constructsProviderAdapterProcessErrorwith adetailfield that just copiescause.message, which runs against the convention against duplicating the cause's message into a separate detail field used for the wrapper message. That looks like a real violation worth flagging. I want to check whether GrokAdapter follows this same pattern to see if it's a repo-wide convention or a real inconsistency, so I should grep fordetail: cause.messageacross the codebase. I'm also noting that the Hermes ACP CLI probe test file mirrors the structure used in the Grok probe test, and that HermesProvider.ts threads anHttpClient.HttpClientservice through viaEffect.provideService. Now I'm considering whetherHttpClient.HttpClientas a type here refers to the service tag's interface type in this version of Effect, since the driver yields the actual service instance and passes it intoenrichHermesSnapshotas a plain parameter. This looks like the kind of manual service injection pattern the rule about flagging factories acceptingFoo["Service"]would call out, but before flagging it I want to grep forenrichGrokSnapshotto confirm it follows the same convention.
7. git_tools — detail: cause.message
8. browse_code — apps/server/src/provider/Drivers/instanceIdentity.ts
💭 I'm checking whether the shared instanceIdentity.ts helper already existed at the merge base, since HermesDriver duplicates its logic locally — since the file isn't part of the diff but exists at head, it must have already been present at the merge base, unless the PR branch merged main in afterward. Stepping back, I'm weighing whether flagging this duplication actually fits the review scope, which focuses on Effect service conventions like imports, service definition, dependency handling, error patterns, and file layout. It's not explicitly listed but feels like a legitimate change-discipline concern around reusing canonical modules, so I want to verify it at merge base before deciding whether to flag it. Then I want to move on and scrutinize the other new Hermes files at head more closely, including checking the enrichGrokSnapshot signature for an httpClient parameter.
9. git_tools — withInstanceIdentity
10. git_tools — withInstanceIdentity
💭 It's odd that HermesDriver.ts seems to already exist at the merge base per the grep, even though the diff lists it as a new file. I suspect the grep tool isn't actually resolving to the tree at that specific commit despite passing the URL with /tree/, so I should double-check by explicitly specifying the rev parameter.
11. git_tools — withInstanceIdentity
💭 I confirm that at the merge base, HermesDriver.ts didn't exist and all five drivers relied on the shared withInstanceIdentity helper — but this new driver introduces its own duplicate local copy instead of reusing the canonical module. That's a legitimate finding worth flagging as a minor issue, since it fits the convention of keeping construction logic centralized rather than duplicated. Now I want to check for stronger findings by comparing other Hermes files against their Grok counterparts — looking at GrokProvider.ts's enrichGrokSnapshot for httpClient parameter consistency, comparing GrokTextGeneration.ts with HermesTextGeneration.ts, and verifying whether AcpSessionRuntime.ts properly exports its options type and layer as expected.
12. browse_code — apps/server/src/textGeneration/GrokTextGeneration.ts
💭 HermesTextGeneration mirrors the same pattern closely, just resolving environment slightly differently, and it hardcodes the mode while silently swallowing any errors from setting it via Effect.ignore — nothing in the conventions explicitly bars that, so I'm not sure it counts as a violation. Now I'll move on to checking GrokProvider's enrich signature.
13. git_tools — export const enrich(Grok|Cursor|Codex|OpenCode|Claude)Snapshot
💭 HermesProvider's enrich logic mirrors CursorProvider exactly, confirming the established pattern, with the main deviation being a local duplicate of
withInstanceIdentityinstead of using the sharedinstanceIdentity.ts. Now I'm checking HermesAdapter.ts against CursorAdapter/GrokAdapter for anything unusual in the server connection handling. I notice a more interesting issue:discoverHermesModelsViaAcpusesprocess.cwd()directly instead of getting cwd from the ServerConfig service like Grok's equivalent does, which could be a convention violation around hiding dependencies in module globals. I should check how Cursor's ACP discovery handles cwd, so let me search forprocess.cwd()usage across the provider directory.
14. git_tools — process.cwd()
15. git_tools — discoverCursorModelsViaAcp|checkCursorProviderStatus =
💭 HermesProvider follows the same pattern as CursorProvider, including its use of process.cwd(), so that's consistent with established convention. The main deviation I'm noticing is the duplicated withInstanceIdentity logic in HermesDriver.ts rather than importing the shared helper — I want to scan the rest of the new Hermes code for similar issues. I'm also comparing HermesAdapter against GrokAdapter to see how each handles Effect.service(ServerConfig), so I'll search the repo for other usages of that pattern.
16. git_tools — Effect.service(ServerConfig)
17. git_tools — const serverConfig = yield*
💭 The main concrete deviation I found is the duplicated
withInstanceIdentityin HermesDriver.ts. Now I'm checking whether the test-onlyresolveSettingshook in HermesAdapterLiveOptions has an equivalent in GrokAdapter's options, since introducing a test-only escape hatch into production code would be a mild concern if unique to Hermes. I'm also noting that HermesTextGeneration swallows errors by ignoring the result ofruntime.setMode("ask"), which Grok doesn't do and isn't in the listed conventions -- worth flagging as an unlisted deviation.
18. git_tools — resolveSettings
💭 I notice HermesAdapter has no corresponding test file, while CursorAdapter and GrokAdapter both do. Since this PR introduces a whole new provider adapter with substantial backend logic but zero unit tests, that's a legitimate gap under the change discipline requirement that new backend behavior needs focused tests. The duplicated helper is a smaller, more clear-cut convention violation worth flagging inline, whereas the missing adapter tests feel more like a summary-level note given the ambiguity around scope. I'm now cross-checking the error constructions in HermesAdapter against sibling adapters like Cursor and Grok to see if the patterns genuinely match, since the detail/cause fields look consistent with existing precedent so far. I'm confirming HermesDriver is properly registered in builtInDrivers and checking whether its environment dependencies, and the provider status check requirements, line up with what Grok's adapter does, since everything so far checks out as matching the established conventions. Though this is technically outside the scope of checking Effect service conventions, I want to quickly verify whether the client has a per-provider registry (icons, accent colors) that also needs a hermes entry, since a missing registration there would be a real bug even if unrelated to the service pattern itself — I'll grep for "grok" in apps/client to see if such an enumeration exists.
19. git_tools — "grok"
💭 With no client enumeration issue, I want to check packages/contracts for other places where provider kinds might be enumerated that hermes needs to be added to, like a provider order list or display settings, beyond the ProviderDriverKind branded string which already looks fine.
20. git_tools — grok
💭 I'm checking the driver-kind-to-default-model mapping and whether
defaultEnabledForDriverin settings.ts accounts for hermes, since the PR's settings test change didn't seem to add it there.
21. git_tools — defaultEnabledForDriver
22. browse_code — packages/contracts/src/model.ts
💭 The missing default model for Hermes seems intentional given the docs, so it's not a real convention issue. My main finding is that HermesDriver.ts duplicates the shared withInstanceIdentity helper instead of importing it like the other five drivers do — that's concrete and actionable with a clear fix. I'm now weighing whether to also flag the missing HermesAdapter tests as a separate point. I'm considering whether the brand-new 1060-line HermesAdapter lacking any test file is worth flagging, given every other adapter (Cursor, Grok, Codex, Claude, OpenCode) has extensive tests. It's more of a "change discipline" gap than an Effect service convention violation, so rather than an inline comment I'll note it in the review body instead. First let me confirm there's really no Hermes test file anywhere by globbing for it.
23. browse_code — **/Hermes
💭 No HermesAdapter.test.ts exists. Now I'm drafting the review, flagging the duplicated helper in HermesDriver.ts around line 76 where the instance identity wrapper starts, and I need to check the exact file range to craft a precise inline suggestion.
24. browse_code — apps/server/src/provider/Drivers/HermesDriver.ts
💭 Since the fix touches multiple spots—adding the import, passing
driverKind: DRIVER_KINDat the call site, and removing the now-unusedServerProvider/ServerProviderDrafttype imports—this isn't a clean single-hunk change, so I'll describe it as a unified diff rather than an inline suggestion. I'm drafting the review comment pointing out that HermesDriver duplicateswithInstanceIdentityinstead of using the shared helper ininstanceIdentity.tsthat every other driver already imports, and recommending it switch over. I'm also noting a second issue: there are no tests for HermesAdapter, despite it introducing nontrivial new behavior like steering turn merging, plan fingerprint dedupe, full-access auto-approval selection, and mode alias resolution—none of which is currently covered. I'm deciding to post the duplicated-helper issue as an inline comment and the missing test coverage as a body note, and I'm settling on marking the overall check as failure since these are genuine convention violations and the check defaults to failure when findings exist.
25. modify_pr — New Hermes adapter code (apps/server/src/provider/Layers/HermesAdapter.ts, ~1060 lines) ships without a HermesAdapter.test.ts, while every peer adapter (Codex, Claude, Cursor, Grok, OpenCode) has one. The adapter carries non-trivial new backend behavior that the existing Hermes tests (provider snapshot, ACP support, text generation) do not exercise: the promptsInFlight steer/merge rule in sendTurn (turn id reuse and "only the last prompt settles the turn"), the lastPlanFingerprint de-duplication in emitPlanUpdate, selectAutoApprovedPermissionOption under full-access, and the mode-alias resolution in resolveRequestedModeId. Consider adding focused tests for at least the steer path and the auto-approval selection against the existing ACP mock agent.
Agent Credits: 235 credits
Token usage: 36 uncached input · 1,876,017 cache-read input · 146,423 cache-write input · 15,070 output
Agent Credits may also include non-token charges from external tools such as web research.