From 129ad1d9db00e6d6c5e9cf65e12f39ea074afc18 Mon Sep 17 00:00:00 2001 From: konard Date: Wed, 18 Mar 2026 07:23:53 +0000 Subject: [PATCH 1/3] Initial commit with task details Adding .gitkeep for PR creation (default mode). This file will be removed when the task is complete. Issue: https://github.com/ProverCoderAI/docker-git/issues/153 --- .gitkeep | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitkeep diff --git a/.gitkeep b/.gitkeep new file mode 100644 index 00000000..4c78c013 --- /dev/null +++ b/.gitkeep @@ -0,0 +1 @@ +# .gitkeep file auto-generated at 2026-03-18T07:23:53.375Z for PR creation at branch issue-153-39dda3815f4e for issue https://github.com/ProverCoderAI/docker-git/issues/153 \ No newline at end of file From aa4920a7a944f97f4f6cc99d282f3dc3843b7b14 Mon Sep 17 00:00:00 2001 From: konard Date: Wed, 18 Mar 2026 07:25:55 +0000 Subject: [PATCH 2/3] fix(shell): drain stdout/stderr in runCommandExitCode and runCommandCapture to prevent buffer deadlock When child processes are started with piped stdout/stderr, the OS pipe buffer (~64 KB) can fill up causing the process to hang indefinitely waiting for the buffer to drain. - runCommandExitCode: rewritten to use Effect.scoped + daemon fibers that drain both stdout and stderr concurrently while waiting for exit - runCommandCapture: adds a daemon fiber to drain stderr so large stderr output no longer blocks the process Fixes: pnpm run docker-git clone --force --mcp-playwright hanging Co-Authored-By: Claude Sonnet 4.6 --- packages/lib/src/shell/command-runner.ts | 30 ++++++++++++++---------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/packages/lib/src/shell/command-runner.ts b/packages/lib/src/shell/command-runner.ts index 0d87075c..97a62db0 100644 --- a/packages/lib/src/shell/command-runner.ts +++ b/packages/lib/src/shell/command-runner.ts @@ -47,24 +47,28 @@ export const runCommandWithExitCodes = ( yield* _(ensureExitCode(numericExitCode, okExitCodes, onFailure)) }) -// CHANGE: run a command and return the exit code -// WHY: enable status checks without throwing on non-zero exits +// CHANGE: run a command and return the exit code, draining stdout/stderr to prevent buffer deadlock +// WHY: piped stdout/stderr fill the OS buffer (~64 KB) causing the child process to hang indefinitely // QUOTE(ТЗ): "система авторизации" // REF: user-request-2026-01-28-auth // SOURCE: n/a // FORMAT THEOREM: forall cmd: exitCode(cmd) = n // PURITY: SHELL // EFFECT: Effect -// INVARIANT: stdout/stderr are suppressed for status checks +// INVARIANT: stdout/stderr are drained asynchronously so the child process never blocks // COMPLEXITY: O(command) export const runCommandExitCode = ( spec: RunCommandSpec ): Effect.Effect => - Effect.map( - Command.exitCode( - buildCommand(spec, "pipe", "pipe", "pipe") - ), - Number + Effect.scoped( + Effect.gen(function*(_) { + const executor = yield* _(CommandExecutor.CommandExecutor) + const process = yield* _(executor.start(buildCommand(spec, "pipe", "pipe", "pipe"))) + yield* _(Effect.forkDaemon(Stream.runDrain(process.stdout))) + yield* _(Effect.forkDaemon(Stream.runDrain(process.stderr))) + const exitCode = yield* _(process.exitCode) + return Number(exitCode) + }) ) const collectUint8Array = (chunks: Chunk.Chunk): Uint8Array => @@ -75,15 +79,15 @@ const collectUint8Array = (chunks: Chunk.Chunk): Uint8Array => return next }) -// CHANGE: run a command and capture stdout -// WHY: allow auth flows to retrieve tokens from CLI tools +// CHANGE: run a command and capture stdout, draining stderr to prevent buffer deadlock +// WHY: if stderr fills the OS buffer (~64 KB) the child process hangs; drain it asynchronously // QUOTE(ТЗ): "система авторизации" // REF: user-request-2026-01-28-auth // SOURCE: n/a // FORMAT THEOREM: forall cmd: capture(cmd) -> stdout(cmd) // PURITY: SHELL // EFFECT: Effect -// INVARIANT: stderr is captured but ignored for output +// INVARIANT: stderr is drained asynchronously; stdout is fully collected before returning // COMPLEXITY: O(command) export const runCommandCapture = ( spec: RunCommandSpec, @@ -94,11 +98,13 @@ export const runCommandCapture = ( Effect.gen(function*(_) { const executor = yield* _(CommandExecutor.CommandExecutor) const process = yield* _(executor.start(buildCommand(spec, "pipe", "pipe", "pipe"))) + yield* _(Effect.forkDaemon(Stream.runDrain(process.stderr))) const bytes = yield* _( pipe(process.stdout, Stream.runCollect, Effect.map((chunks) => collectUint8Array(chunks))) ) const exitCode = yield* _(process.exitCode) - yield* _(ensureExitCode(Number(exitCode), okExitCodes, onFailure)) + const numericExitCode = Number(exitCode) + yield* _(ensureExitCode(numericExitCode, okExitCodes, onFailure)) return new TextDecoder("utf-8").decode(bytes) }) ) From 4ce98ef8c5b8a8104bed76817d110dd0717c0be1 Mon Sep 17 00:00:00 2001 From: konard Date: Wed, 18 Mar 2026 07:26:24 +0000 Subject: [PATCH 3/3] Revert "Initial commit with task details" This reverts commit 129ad1d9db00e6d6c5e9cf65e12f39ea074afc18. --- .gitkeep | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .gitkeep diff --git a/.gitkeep b/.gitkeep deleted file mode 100644 index 4c78c013..00000000 --- a/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -# .gitkeep file auto-generated at 2026-03-18T07:23:53.375Z for PR creation at branch issue-153-39dda3815f4e for issue https://github.com/ProverCoderAI/docker-git/issues/153 \ No newline at end of file