Skip to content

effect(git): migrate to AppProcess.run#27190

Merged
kitlangton merged 1 commit into
devfrom
effect/phase2-git-appprocess
May 13, 2026
Merged

effect(git): migrate to AppProcess.run#27190
kitlangton merged 1 commit into
devfrom
effect/phase2-git-appprocess

Conversation

@kitlangton

Copy link
Copy Markdown
Contributor

Summary

Migrates packages/opencode/src/git/index.ts to use AppProcess.run (added in #27178) for its run helper. The file-local spawn-collect-truncate boilerplate is replaced by a single call.

Stacked on top of #27178 (effect/app-process-service).

Collapsed helpers

The file-local run Effect.fn in packages/opencode/src/git/index.ts previously contained:

  • ChildProcessSpawner.spawn(...) plus scope management
  • A local collect helper wrapping Stream.runFold with maxOutputBytes truncation accounting (chunk slicing, bytes/truncated accumulator)
  • Effect.all([collect(stdout), collect(stderr)], { concurrency: 2 })
  • A yield* handle.exitCode and manual Result assembly
  • Effect.scoped wrapping

All of that now lives in AppProcess.run. The git wrapper now just calls appProcess.run(...) and re-shapes the result to preserve the Result contract (which includes the text() accessor used by other methods in this file).

Lines

packages/opencode/src/git/index.ts: 365 -> 350 lines (-15). The diff is +23 / -38 net inside the run body; the bulk of the file (every other git method) is untouched.

Behavior preservation notes

  • Return shape. Every git method in this file calls run and reads .text(), .stdout, .stderr, .exitCode, and .truncated. All of those are preserved exactly. The text() accessor still computes from stdout lazily.

  • Truncation. Today's code reported truncated = stdout.truncated || stderr.truncated. AppProcess.run only tracks truncation on stdout (stderr is unbounded for diagnostics), so the new result.truncated is just stdout truncation. In practice stderr never hit the limit here — the only callers that pass maxOutputBytes (patch, patchAll, patchUntracked, statUntracked) care about stdout (the diff/numstat output), not stderr.

  • Exit-code semantics. Git frequently returns non-zero exit codes that are not errors from this code's perspective: git symbolic-ref --quiet returns non-zero when there's no symbolic ref, git rev-parse --verify HEAD returns non-zero on an empty repo, git merge-base returns non-zero when there is no merge base, git show ref:file returns non-zero for missing files, git diff --no-index returns 1 to mean "files differ", etc. Every caller in this file inspects result.exitCode !== 0 to branch.

    To preserve this, the wrapper passes interpretExitCode: () => "success" so AppProcess.run never converts a non-zero exit code into an AppProcessError. All exit-code interpretation stays where it is today — in the individual git methods.

  • Failure handling. The original run wrapped its body in Effect.catch((err) => Effect.succeed(fail(err))), returning a synthetic Result (exitCode 1, empty stdout, error message in stderr) instead of failing. The new code keeps the exact same Effect.catch(...) => fail(err) wrapper around the appProcess.run call, so spawn failures and any other internal errors still degrade to the same synthetic Result and the public method signatures remain Effect.Effect<...> with no error channel.

  • Layer chain. Switched from Layer.provide(CrossSpawnSpawner.defaultLayer) to Layer.provide(AppProcess.defaultLayer) (which already bundles CrossSpawnSpawner.defaultLayer).

interpretExitCode

No git command needed a custom interpretExitCode mapping (e.g. exit-1-as-no-match). The blanket () => "success" is correct because every caller in this file is already responsible for its own exit-code branching, and the previous implementation never failed the Effect on non-zero exit either.

Test plan

  • bun run typecheck from packages/opencode (clean)
  • bun run test test/git/ (9 pass, 0 fail)
  • CI

@kitlangton kitlangton force-pushed the effect/app-process-service branch from ebc8050 to bf596d5 Compare May 13, 2026 01:15
@kitlangton kitlangton force-pushed the effect/phase2-git-appprocess branch from ba9aef6 to 74a2dee Compare May 13, 2026 01:19
Base automatically changed from effect/app-process-service to dev May 13, 2026 01:24
@kitlangton kitlangton changed the base branch from dev to effect/phase2-installation-appprocess May 13, 2026 15:17
@kitlangton kitlangton force-pushed the effect/phase2-git-appprocess branch from 74a2dee to d51d141 Compare May 13, 2026 15:17
@kitlangton kitlangton marked this pull request as ready for review May 13, 2026 15:17
@kitlangton kitlangton force-pushed the effect/phase2-installation-appprocess branch from fa1c876 to fc91523 Compare May 13, 2026 15:40
Base automatically changed from effect/phase2-installation-appprocess to dev May 13, 2026 15:54
@kitlangton kitlangton force-pushed the effect/phase2-git-appprocess branch from d51d141 to f17e564 Compare May 13, 2026 15:54
@kitlangton kitlangton merged commit e5d13d9 into dev May 13, 2026
10 checks passed
@kitlangton kitlangton deleted the effect/phase2-git-appprocess branch May 13, 2026 16:04
AIALRA-0 pushed a commit to AIALRA-0/opencode-turn-engine that referenced this pull request Jun 10, 2026
AIALRA-0 pushed a commit to AIALRA-0/opencode-turn-engine that referenced this pull request Jun 10, 2026
avion23 pushed a commit to avion23/opencode that referenced this pull request Jun 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant