Merge nucleic/mellow-ember-shrew into dev

This commit is contained in:
2026-07-13 00:49:58 -07:00
parent 6a28d3e1d9
commit 80aca315e7
3 changed files with 50 additions and 9 deletions
@@ -60,9 +60,25 @@ struct TranscriptItem: Identifiable, Equatable {
return false
}
/// Items that draw no visible row. They must not break a run of tool calls when coalescing:
/// an invisible row sitting between two calls would otherwise split them into two cards.
var rendersNothing: Bool { isEmptyThinking }
/// Diagnostic, debug-level rows: per-turn token tallies, rate-limit notices, turn markers, and
/// unrecognized raw passthrough events. Hidden by default to keep the transcript clean — they're
/// the "token counts and other lines between tool calls" that fragmented a run of cards — and
/// surfaced only when "advanced detail" (`showRaw`) is on. Prose, thinking, tool calls,
/// session-start, approvals, run outcome, and errors are never debug. Mirrors the Mac's
/// `TranscriptItem.isDebug`.
var isDebug: Bool {
switch kind {
case .usage, .rateLimit, .turnBoundary, .raw: return true
default: return false
}
}
/// Items that draw no visible row (once debug rows are filtered out for display). They must not
/// break a run of tool calls when coalescing: a debug/invisible row sitting between two calls
/// would otherwise split them into two cards with a gap. Such rows are held aside during
/// coalescing and re-emitted after the block, so consecutive tool cards stay one contiguous
/// group. Mirrors the Mac's `rendersNothing` (`isDebug || isEmptyThinking`).
var rendersNothing: Bool { isDebug || isEmptyThinking }
}
/// One tool call's coalesced lifecycle: start → input deltas → complete → result → file changes.
@@ -169,10 +185,19 @@ enum TranscriptProjection {
var patches: [(toolCallID: String, lock: NoteLock)] = []
let flat = foldLockNotes(flatItems(topLevel, showRaw: showRaw, showLockEvents: showLockEvents),
worktreeRoot: root, priorEdits: priorEdits, priorLockPatches: &patches)
let items = coalesceToolRuns(flat).map {
let coalesced = coalesceToolRuns(flat).map {
attachSubagentChildren($0, byParent: byParent, depth: 0,
showRaw: showRaw, showLockEvents: showLockEvents)
}
// Drop the diagnostic rows (token tallies, rate-limit notices, turn markers, raw
// passthrough) unless "advanced detail" is on. `coalesceToolRuns` already held them aside
// from any tool run (they're `rendersNothing`), so removing them here leaves consecutive
// tool cards as one contiguous block instead of a stack fragmented by token-count lines.
// Filtering this shared choke point — rather than the view — keeps the incremental
// projector's `fold(prefix) ++ fold(tail)` byte-identical to `fold(whole)`: a debug row is
// never a tool edit or lock target, so the filter distributes cleanly over the seam and the
// per-segment `edits`/`priorLockPatches` bookkeeping below is unaffected.
let items = showRaw ? coalesced : coalesced.filter { !$0.isDebug }
let edits = flat.compactMap { item -> PriorEdit? in
guard case .tool(let group) = item.kind else { return nil }
let paths = editedPaths(toolName: group.name, input: group.input)