Review B — PR 19799 (head d2c97fc5a8, merge-base b72740852f)
Scope: apps/server/src/vcs/GitVcsDriverCore.ts collectOutput (+ 2 tests). Read-only; no tests run.

Change: line-buffer decoding and emitCompleteLines are now gated on `onLine`, so callers that pass no line callback skip line splitting entirely.

Focus areas:
1. Callback registered after command starts: not possible. onLine is a parameter of collectOutput, bound once at spawn from input.progress?.onStdoutLine / onStderrLine (GitVcsDriverCore.ts ~993-1007). ExecuteGitProgress has readonly fields, and commit() (~2342) builds its wrappers before executeGit. The gate can never be false and later true for one stream. No bug.
2. Truncation/limit accounting with one buffer skipped: bytes, text, truncated, chunkToDecode, the GitCommandError path and the outputLength computation all sit outside the new `if (onLine)` and are unchanged. Each stream runs its own collectOutput with its own state, so commit() with only stderr wired (or progress lacking onStdoutLine) skips one buffer without touching the other. The post-truncation branch was already guarded by lineDecoder, which is null when onLine is absent. maxPendingLineBytes only touches lineBuffer. No bug.
3. UTF-8/NUL chunk splits: `decoder` (which produces text) still decodes every chunk with stream:!truncated and flushes in the remainder, exactly as before. lineDecoder/decoded feed only the gated buffer. NUL is never treated as a separator (OUTPUT_LINE_SEPARATOR = \r\n|\r|\n) and passes through text unchanged. The new test pushes one byte per chunk through both paths and asserts identical results; I checked its expected values by hand (é� on a cap of 3 bytes; outputLength 4). Consistent.
4. Callers relying on the line buffer without a callback: lineBuffer is local to collectOutput and is never returned or read elsewhere; the return is { text, truncated } only. Without onLine the old code built the buffer and dropped every line (`line.length > 0 && onLine`), so skipping it changes nothing anyone can observe. keepLineCallbacksAfterTruncation is used only by SourceControlRepositoryService, which passes onStderrLine. No caller is affected.

Non-blocking: the inner `&& onLine` checks in emitCompleteLines are now redundant. Harmless.
Known conflicts with current main are out of scope per brief.

No concrete bugs or blockers found.
VERDICT: MERGE
