overengineeringstudio / overengineeringstudio/effect-utils

Genie: GenieGenerationFailedError with ParseError masks real errors (missing module resolution via megarepo symlinks)

Open
#129 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

type:agent-tooling type:bug
Dominant language
TypeScript
Stars
82
Forks
2
Avg merge
1d 8h
Merged PRs (30d)
121

Description

Bug Summary

Running genie in a repo that imports genie runtime modules via megarepo symlinks fails on all files with two layered errors:

  1. Root cause: Cannot find module '@effect/platform' — Bun resolves transitive imports relative to the real path of the source file (inside the megarepo checkout), not the genie binary or the consumer repo. Since the megarepo checkout has no node_modules/, all bare-specifier imports fail.

  2. Secondary bug: The Effect schema used to encode the generation result types the error channel as never, so when GenieGenerationFailedError propagates, the schema encoding itself fails with a ParseError, completely hiding the real per-file error messages.

Reproduction

Repro repo: https://github.com/schickling-repros/genie-generation-error

# In any repo using genie via megarepo symlinks (e.g. dotfiles):
MEGAREPO_ROOT_NEAREST=$PWD genie
Output (default)

The user only sees:

GenieGenerationFailedError: 9 file(s) failed to generate
ParseError: (parseJson <-> { readonly _tag: "Success"; ... } | { readonly _tag: "Failure"; ... })
└─ Type side transformation failure
   └─ ...
      └─ { readonly _tag: "Failure"; readonly cause: (CauseEncoded<never> <-> Cause<never>); ... }
         └─ ["cause"]
            └─ (CauseEncoded<never> <-> Cause<never>)
               └─ Type side transformation failure
                  └─ Cause<never>
                     └─ CauseEncoded<never>
                        └─ { readonly _tag: "Fail"; readonly error: never }
                           └─ ["error"]
                              └─ Expected never, actual GenieGenerationFailedError: 9 file(s) failed to generate
Output (ndjson — reveals real errors)
MEGAREPO_ROOT_NEAREST=$PWD genie --output ndjson 2>&1

Per-file errors all look like:

GenieImportError: Failed to import .../ci.yml.genie.ts:
  ResolveMessage: Cannot find module '@effect/platform' from
  '/home/.../.megarepo/.../effect-utils/.../packages/@overeng/genie/src/runtime/package-json/context.ts'

The only file that succeeds is megarepo.json because megarepo-config/mod.ts has no transitive dependency on @effect/platform.

Root Cause Analysis

The genie binary spawns Bun to execute .genie.ts files. These files import from the genie runtime source via relative paths that resolve through megarepo symlinks:

// .oxlintrc.json.genie.ts
import { oxlintConfig } from './repos/effect-utils/packages/@overeng/genie/src/runtime/mod.ts'

The mod.ts barrel re-exports from package-json/mod.ts, which imports context.ts, which has:

import { FileSystem, Path } from '@effect/platform'

Bun resolves @effect/platform relative to the real path of context.ts inside the megarepo checkout (~/.megarepo/.../effect-utils/). That directory has no node_modules/ — dependencies are only installed inside the genie binary's embedded filesystem (/$bunfs/root/genie).

Two Distinct Issues

Issue A: Module resolution failure with symlinked sources

When genie runtime source files are consumed via symlinks (megarepo), Bun can't find the runtime's npm dependencies (@effect/platform, effect, etc.) because there's no node_modules at the symlink target.

Possible fixes:

  • Bundle the genie runtime modules into the genie binary (they're currently loaded from source at runtime)
  • Ship a pre-built runtime package that consumers can import (no transitive deps needed)
  • Have genie inject Bun's module resolution paths to include the binary's embedded node_modules
Issue B: ParseError masks the real error

The schema used to encode the generation result (success/failure) types the error channel as Cause<never>. When a real error occurs (GenieGenerationFailedError), encoding fails because never doesn't accept it. This causes a ParseError that hides all per-file error details from the user.

Possible fix: Widen the error type in the result schema to accept GenieGenerationFailedError (or a broader error type).

Environment

  • genie: 0.1.0+8f58904 (committed 3 hours ago as of 2026-02-05)
  • bun: 1.3.8
  • OS: Linux x86_64 (NixOS)
  • megarepo checkout: ~/.megarepo/github.com/overengineeringstudio/effect-utils/refs/heads/main/ (no node_modules/)

Workaround

None found. Running pnpm install in the megarepo checkout might work but defeats the purpose of the hermetic genie binary.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the symlinked runtime path through mod.ts, package-json/mod.ts, and context.ts, then inspect the generation result schema involved in encoding GenieGenerationFailedError. Reproduce with MEGAREPO_ROOT_NEAREST=$PWD genie and compare default output with --output ndjson. Done means runtime imports resolve through the hermetic binary and generation failures retain their per-file error details instead of becoming ParseError.

Written by the indexing model from the issue text.

Assessment

Tech stack
bun, typescript
Domain
cli, tooling
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.