Skip to content

fix(git): avoid blocking instruction file reads

MacroscopeApp / Macroscope - Effect Service Conventions failed Aug 30, 2026 in 1m 58s

Effect Service Conventions: 1 issue found

apps/server/src/git/GitManager.ts β€” readRepositoryInstructions acquires the FileSystem service for realPath, but performs the actual file read with readThemeFileGuarded (aliased as readFileGuarded), an imperative synchronous node:fs helper owned by the environmentTheme service module. This hides a filesystem dependency of the GitManager service from the Effect environment (a test FileSystem layer cannot intercept it) and performs blocking sync I/O inside a domain service. Suggested fix: keep the read in Effect using fileSystem.stat + fileSystem.readFileString with the existing Effect.orElseSucceed(() => "") fallback, matching the pattern used elsewhere in apps/server (cli/config.ts, provider/Layers/CursorProvider.ts), and drop the now-unused import on line 51.

An inline comment with a unified diff has been posted on the PR.

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 against the include patterns: apps/server/src/git/GitManager.ts, apps/server/src/git/GitManager.test.ts, apps/server/src/textGeneration/TextGenerationPrompts.ts (docs/user/source-control.md is out of scope).

Checks performed:

  • Imports: Effect modules are imported as subpath namespaces; local service modules (TextGeneration, ProviderRegistry, ServerSettings, GitVcsDriver) keep namespace form. The single named import added (readThemeFileGuarded) is a pure helper, so a named import is acceptable per the conventions β€” the finding is about the imperative read itself, not the import style.
  • Service definition: no changes to the Context.Service tag, inline interface, make, or layer shape; resolveStylePolicy signature widening from SourceControlWritingStyleSettings to the internal SourceControlTextGenerationSettings is a local, mechanical change with both call sites updated.
  • Dependency acquisition: fileSystem/path were hoisted into the top of make and are correctly obtained with yield*; no new service-instance parameters, ManagedRuntime.make, runPromise, runPromiseExit, or new Layer.succeed implementations were introduced.
  • Errors: no new error classes, predicates, or catchTag/catchIf usages; failures in the new helper degrade via Effect.orElseSucceed, consistent with the surrounding optional-read code.
  • Tests: the existing repository-conventions test was updated rather than duplicated, which matches the change-discipline guidance.

Out of scope for this check (not reported inline): the settings.modelSelection.instanceId === "claudeAgent" comparison against a driver identifier, and the prompt section limit increase in TextGenerationPrompts.ts.


πŸ” Agent activity β€” 8 tool calls

πŸ’­ I'm examining the changes in GitManager.ts, particularly checking whether the aliased import of the theme file reader constitutes a convention violation, and considering whether using a synchronous file read inside this Effect-based service module is appropriate given whether environmentTheme.ts counts as a service module. Since it's running inside Effect.gen where FileSystem is already available, using this imperative read instead of the fileSystem service's own read method looks like it's bypassing proper dependency acquisition β€” I want to check the actual repo to confirm.

1. browse_code β€” apps/server/src/environmentTheme.ts

2. browse_code β€” apps/server/src/git/GitManager.ts

πŸ’­ I'm noting that this import aliases readThemeFileGuarded from environmentTheme.ts, a service module β€” which raises the question of whether this violates the namespace-import convention for service boundaries, though the guidance also carves out exceptions for pure helper usage. But there's a bigger structural concern: GitManager already depends on FileSystem.FileSystem, yet this pulls in an imperative synchronous read helper from another module rather than using its own effectful dependency, which risks hiding a dependency outside the service's own acquisition pattern. However, thinking further, the realPath resolution before the guarded read is actually doing symlink containment checking β€” if AGENTS.md were a symlink escaping the root, realPath would resolve outside and the containment check would catch it, so the security logic seems sound despite the imperative I/O style concern. That said, I want to check the settings.modelSelection.instanceId === "claudeAgent" comparison β€” comparing an instanceId to what looks like a driver name feels like a logic bug, though it's outside the Effect-convention scope I'm supposed to focus on. I also want to check the Effect.orElseSucceed call that silently swallows an error into an empty string, which could be worth flagging. The other pieces look fine β€” the repository instructions helper returning "" on failure, the provider registry usage, and hoisting the FileSystem service reference. I want to view the actual code around lines 600-700 to confirm.

3. browse_code β€” apps/server/src/git/GitManager.ts

4. git_tools β€” readThemeFileGuarded

πŸ’­ I'm mapping this finding to conventions: GitManager reaching into an unrelated theme module for a synchronous fs read instead of using the already-acquired FileSystem service, aliasing the import to obscure its origin. This violates the rule against hiding dependencies in module globals or imperative APIs within service implementations, though the named import itself would be fine for a pure helper. But then I second-guess whether this is truly a violation β€” the sync read exists because FileSystem doesn't expose O_NOFOLLOW, and the same pattern is already sanctioned elsewhere in the repo (cli/theme.ts, environmentTheme.ts), where a comment explicitly justifies bypassing the service. That makes flagging it feel like flagging an accepted repo convention rather than a genuine deviation, though I note environmentTheme's usage may be a CLI boundary case distinct from GitManager's. Reusing that theme-specific helper from GitManager does look like cross-module coupling that should probably be pulled into a shared guarded-read module, though that's more of an architecture concern than an Effect-convention violation. I check whether GitManager imports any node builtins directly for the node-builtin-import rule β€” it doesn't, so that's clear. Scanning the rest of the diff: the resolveStylePolicy signature change looks fine, the test's use of writeFileSync is acceptable, and the instanceId-vs-driver-id comparison in settings.modelSelection looks like a possible bug but falls outside the Effect conventions I'm supposed to review. Import ordering and the empty-string fallback in readRepositoryInstructions both look fine too. I'm now weighing whether the layer-hiding-async-dependencies rule applies here β€” since GitManager's make already requires FileSystem and the read is synchronous, there's no hidden async service being smuggled through a closure, so that rule isn't violated. None of the ManagedRuntime/runPromise patterns show up either, and the aliased import of the guarded-read helper doesn't cross a real service boundary in a way that breaks convention. The alias rename itself looks like a naming nitpick rather than a real violation, since renaming a helper doesn't erase the module's namespace the way the flagged patterns do. Checking the rest of the diff β€” the prompt length change and the added test case β€” neither touches Effect conventions, so I'm leaning toward this being clear of violations, but I want to confirm there's nothing else worth flagging across the four files. I want to see if GitManager has an established pattern elsewhere for reading files, so I'm checking whether it typically goes through fileSystem.readFileString instead of the approach used here.

5. git_tools β€” fileSystem.(readFileString|exists|stat)

6. git_tools β€” AGENTS.md|CLAUDE.md

πŸ’­ I want to check how other server modules handle reading arbitrary files, like PrTemplateDetection.ts for source control, to see if it uses the FileSystem service as a comparison point.

7. git_tools β€” readFileString

πŸ’­ The repo convention is to read files through the FileSystem service with fallback for optional files, but this new code imports an imperative Node fs helper from an unrelated module into GitManager instead. That bypasses the acquired FileSystem service inside a domain service, making the read invisible to test layers. Given the check's rule against hiding dependencies or bypassing service-acquired APIs, I'll flag this as a finding and set the result to failure, since findings mean the check doesn't pass. Though I want to double check whether there's a legitimate reason for the guarded imperative read β€” like symlink resolution or a size cap on file reads β€” that the FileSystem service equivalent wouldn't handle as cleanly. I'm sketching an Effect-based replacement that resolves the real path, checks it stays within the root, stats the file to confirm it's a regular file under the size cap, then reads and trims it, falling back to empty string on any failure. This seems like a clean, self-contained fix I could offer as an inline diff suggestion, though I need to check whether removing the old guarded-read import leaves it unused elsewhere in the file. Since the fix touches an import used elsewhere, I'll go with a unified diff rather than an inline suggestion, and note in the same comment that the aliased import becomes unused. I'll skip flagging the unrelated instanceId check since it's out of scope, and pin down the head-file line numbers (615–623, with the guarded call around line 622) before posting the review comment.

8. modify_pr β€” Effect service conventions review: one finding β€” the new repository-instruction read bypasses the acquired FileSystem service and reaches into environmentTheme.ts for an imperative, synchronous Node fs helper.


Agent Credits: 58 credits

Token usage: 12 uncached input Β· 174,607 cache-read input Β· 44,378 cache-write input Β· 7,472 output
Agent Credits may also include non-token charges from external tools such as web research.