effect(git): migrate to AppProcess.run#27190
Merged
Merged
Conversation
ebc8050 to
bf596d5
Compare
ba9aef6 to
74a2dee
Compare
74a2dee to
d51d141
Compare
fa1c876 to
fc91523
Compare
d51d141 to
f17e564
Compare
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Migrates
packages/opencode/src/git/index.tsto useAppProcess.run(added in #27178) for itsrunhelper. 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
runEffect.fn inpackages/opencode/src/git/index.tspreviously contained:ChildProcessSpawner.spawn(...)plus scope managementcollecthelper wrappingStream.runFoldwithmaxOutputBytestruncation accounting (chunk slicing,bytes/truncatedaccumulator)Effect.all([collect(stdout), collect(stderr)], { concurrency: 2 })yield* handle.exitCodeand manualResultassemblyEffect.scopedwrappingAll of that now lives in
AppProcess.run. The git wrapper now just callsappProcess.run(...)and re-shapes the result to preserve theResultcontract (which includes thetext()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 therunbody; the bulk of the file (every other git method) is untouched.Behavior preservation notes
Return shape. Every git method in this file calls
runand reads.text(),.stdout,.stderr,.exitCode, and.truncated. All of those are preserved exactly. Thetext()accessor still computes fromstdoutlazily.Truncation. Today's code reported
truncated = stdout.truncated || stderr.truncated.AppProcess.runonly tracks truncation on stdout (stderr is unbounded for diagnostics), so the newresult.truncatedis just stdout truncation. In practice stderr never hit the limit here — the only callers that passmaxOutputBytes(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 --quietreturns non-zero when there's no symbolic ref,git rev-parse --verify HEADreturns non-zero on an empty repo,git merge-basereturns non-zero when there is no merge base,git show ref:filereturns non-zero for missing files,git diff --no-indexreturns 1 to mean "files differ", etc. Every caller in this file inspectsresult.exitCode !== 0to branch.To preserve this, the wrapper passes
interpretExitCode: () => "success"soAppProcess.runnever converts a non-zero exit code into anAppProcessError. All exit-code interpretation stays where it is today — in the individual git methods.Failure handling. The original
runwrapped its body inEffect.catch((err) => Effect.succeed(fail(err))), returning a syntheticResult(exitCode 1, empty stdout, error message in stderr) instead of failing. The new code keeps the exact sameEffect.catch(...) => fail(err)wrapper around theappProcess.runcall, so spawn failures and any other internal errors still degrade to the same syntheticResultand the public method signatures remainEffect.Effect<...>with no error channel.Layer chain. Switched from
Layer.provide(CrossSpawnSpawner.defaultLayer)toLayer.provide(AppProcess.defaultLayer)(which already bundlesCrossSpawnSpawner.defaultLayer).interpretExitCode
No git command needed a custom
interpretExitCodemapping (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 typecheckfrompackages/opencode(clean)bun run test test/git/(9 pass, 0 fail)