From 1a9330b541b3493785c1d7a84fbcc99b87d41adf Mon Sep 17 00:00:00 2001 From: Nucleic Date: Tue, 4 Aug 2026 00:09:36 -0700 Subject: [PATCH] Merge nucleic/vivid-spruce-robin-bacs into dev --- .../NucleicRemote/Views/SettingsView.swift | 6 ++--- .../IncrementalTranscriptProjection.swift | 26 +++++-------------- .../Transcript/TranscriptProjection.swift | 23 ++++++++-------- 3 files changed, 21 insertions(+), 34 deletions(-) diff --git a/NucleicRemote/NucleicRemote/Views/SettingsView.swift b/NucleicRemote/NucleicRemote/Views/SettingsView.swift index 6364efc..13151bf 100644 --- a/NucleicRemote/NucleicRemote/Views/SettingsView.swift +++ b/NucleicRemote/NucleicRemote/Views/SettingsView.swift @@ -144,9 +144,9 @@ struct SettingsView: View { } header: { Text("Transcript") } footer: { - Text("Advanced detail surfaces per-turn token counts, rate-limit notices, turn " - + "markers, and raw events. Hidden by default so consecutive tool calls " - + "read as one group.") + Text("Advanced detail surfaces model thought traces, per-turn token counts, " + + "rate-limit notices, turn markers, and raw events. Hidden by default " + + "so conversations stay focused and consecutive tool calls read as one group.") } Section { diff --git a/NucleicRemote/NucleicRemote/Views/Transcript/IncrementalTranscriptProjection.swift b/NucleicRemote/NucleicRemote/Views/Transcript/IncrementalTranscriptProjection.swift index 7eb7905..0031534 100644 --- a/NucleicRemote/NucleicRemote/Views/Transcript/IncrementalTranscriptProjection.swift +++ b/NucleicRemote/NucleicRemote/Views/Transcript/IncrementalTranscriptProjection.swift @@ -25,8 +25,8 @@ import NucleicProtocol /// projector resets and re-folds from scratch, so correctness never rests on them. /// 3. **No tool run is split.** `coalesceToolRuns` merges adjacent `.tool` items, so the seam /// only falls where the last folded item is a hard separator — a visible non-tool row that a -/// future tool call can't merge across. (Empty redacted-thinking rows are transparent to runs -/// and therefore to this rule too.) +/// future tool call can't merge across. (Thought traces and other advanced-detail rows are +/// transparent to runs and therefore to this rule too.) /// 4. **Lock notes fold exactly, even across the seam.** A lock's `released` note lands when the /// file lands in the parent — potentially many turns after the edit it brackets — so sealed /// edit cards stay reachable through a registry (`TranscriptProjection.PriorEdit`): a tail @@ -205,11 +205,8 @@ final class IncrementalTranscriptProjection { case separator /// A `.tool` row a future adjacent call could merge with. case tool - /// A thinking row: a separator iff its final text is non-empty (an empty redacted block - /// is transparent to run coalescing, so it must be transparent to the seam rule too). - case thinking(String) - /// A lock note that may fold away (dropping it can fuse the runs around it), so it - /// counts as nothing — the seam just waits for the next hard separator. + /// A thought trace or other row held aside from the conversation, or a lock note that + /// may fold away. It counts as nothing so the seam waits for the next hard separator. case transparent } @@ -232,9 +229,7 @@ final class IncrementalTranscriptProjection { var spans: [String: IDSpan] = [:] var creations: [Creation?] = Array(repeating: nil, count: n) - var thinkingText: [String: String] = [:] var seenMessageItem = Set() - var seenThinkingItem = Set() var seenToolItem = Set() var lastChunkInScope: [String: String] = [:] var lastBoundary = -1 // region index of the latest turnCompleted/runFinished @@ -256,9 +251,7 @@ final class IncrementalTranscriptProjection { if seenMessageItem.insert(c.messageID).inserted { creations[r] = .separator } case .thinking(let c): trackChunk(c, in: &spans, lastChunkInScope: &lastChunkInScope) - let existing = thinkingText[c.messageID] ?? "" - thinkingText[c.messageID] = c.isPartial ? existing + c.text : c.text - if seenThinkingItem.insert(c.messageID).inserted { creations[r] = .thinking(c.messageID) } + creations[r] = .transparent case .toolCallStarted(let c), .toolCallCompleted(let c): if seenToolItem.insert(c.toolCallID).inserted { creations[r] = .tool } case .toolResult(let result): @@ -310,20 +303,13 @@ final class IncrementalTranscriptProjection { } // Run-split rule (3): replay creations in item order; a seam is placeable after event r - // only while the last solid (visible, surviving) item is a hard separator. The replay - // resolves each thinking row against its *final* region text, which is exactly what the - // sealed fold will contain (open ids were already excluded by `cap`). + // only while the last solid (visible, surviving) item is a hard separator. var separatorOK = [Bool](repeating: false, count: n) var lastSolidIsSeparator = true // sealed prefix is empty or ends with a separator (invariant) for r in 0..