feat(desktop): rebuild WorkHub from Session transcripts - #3648
Conversation
Project a bounded recent WorkHub timeline directly from ordinary Session transcripts while keeping Session as the sole durable authority. Generated-by: Codex
Generated-by: Codex
Avoid rejecting the readiness promise before a slow transcript open can attach its consumer. Generated-by: Codex
Bound transcript opening, reject stale refreshes, preserve projected turn state, and canonicalize duplicate suppression. Generated-by: Codex
Astro-Han
left a comment
There was a problem hiding this comment.
Approving at a72a1bddcadc9470b2ae77383de681e64ebb191e. No P0-P3.
Provenance was established before review rather than taken from the compare view: four author commits, no replayed main history, and the file union matches the ten files reported for this PR. Scope is genuinely 777 added lines of this author's work.
The question this PR had to answer
"Rebuild WorkHub from Session transcripts" changes where WorkHub's content comes from, so the risks worth checking were a second authority, a redaction bypass, and guards lost in the rewrite. None of the three is present.
No dual authority. The concern was that workhub-controller.ts is only +33/-3 while workhub-session-port.ts is a new +123 — an old source left standing beside a new one. The three deleted lines are an inlined facts.filter(...).sort(...) hoisted into a local ordinary because two call sites now reuse it; that is de-duplication, not a retired authority. More to the point, turns is a new field: WorkHub had no turn timeline before, so this is not two sources for one thing but one source for something that did not previously exist. sessions still goes through deps.sessions.list(), untouched. The port's own comment pins the authority — missing transcripts are omitted rather than reconstructed elsewhere — and recentTurns honours it with a per-session try/catch whose comment states that one unavailable transcript must not mask other sessions, nor turn WorkHub into a second recovery authority.
Redaction does not apply here, on this repository's own stated grounds. redactSecrets is a credential scrubber, not a privacy filter: its key set is auth, authorization, credential(s), passwd, password, secret, token, with AWS-specific patterns. The relevant question is therefore whether transcript text could carry a pasted or echoed secret. conversation-markdown.ts:38-44 answers it directly: assistant text is run through redactSecrets defensively, because — in its own words — the backend already redacts at write time, and the extra pass exists so nothing survives "into a clipboard export that the user is going to paste somewhere public". That is also why thread-search redacts its snippets. Both are extraction surfaces. WorkHub renders in place, from text the backend already redacted, so the absence of a renderer-side pass is consistent with the convention rather than a gap in it. Worth noting the pre-existing state matches: boundedWorkHubTimelineText only trims and truncates at 600 characters, and did so at the merge-base too.
Nothing was removed from workhub-surface.tsx. The 23 deleted lines are a local interface WorkHubConversationTurn superseded by the controller's shared WorkHubProjectedTurn, some state wiring, and ChatMessage/ChatMessageBubble JSX. No rejection condition, bound, error branch or ordering constraint disappears. The change adds guards rather than dropping them: WORKHUB_TIMELINE_SESSION_LIMIT = 10 and WORKHUB_TIMELINE_TURN_LIMIT = 40 (workhub-session-port.ts:75-76) bound the work, :119 and :137 apply them, and :120-123 plus :143-150 contain per-session failures.
The de-duplication is done carefully and deserves saying so. visibleWorkHubProjectedTurns (workhub-surface.tsx:92) uses a counting multiset keyed on [target, turnId, boundedWorkHubTimelineText(text)], decrementing on each match. A Set would mis-pair repeated identical submissions; passing both sides through the same normaliser is what prevents a truncation-boundary mismatch from showing a turn twice. Both choices are easy to get wrong and both are right here.
One thing for the author and reviewers to settle, which is not a code question
This PR widens what WorkHub represents. Before, turns was a local action log keyed by requestId with routing/settled/failed states — what I initiated here. After, projectedTurns comes from the Session transcript — what happened in these sessions, whether or not WorkHub initiated it. That difference is the substance of the change and matches the intent in the tracking issue, so it is recorded here rather than graded. Whether WorkHub should surface conversation content is a product decision, not one a reviewer should make.
Gate
test is terminal green at this exact head; it was released from action_required, having never run before that. Zero review threads, no approval bound to any earlier commit, branch mergeable.
Approval only; merging is a human's call.
Summary
Refs #3492
Verification
npm run lint: pass (2653 files)npm run format:check: pass (1601 files)npm run build: passnpm run typecheck: passBefore opening the target Session:
After returning to WorkHub, including the newly routed request rebuilt from the Session transcript:
Projection boundary
The adapter opens at most the 10 most recent ordinary Session transcripts, keeps the latest 40 projected user entries, bounds displayed text, and closes every read after its ready snapshot. An unavailable transcript is omitted without hiding the remaining Sessions.
Older content outside the available bounded range and transient clarification/discussion UI state are not reconstructed here. Focus initialization, first-time natural-language correction, and ordinary stop delegation remain separate checklist items in #3492.
The pre-review adversarial pass also verified transcript-open cancellation, stale-refresh ordering, archived turn-state presentation, and bounded long-text duplicate suppression with focused regressions.
AI use
Select exactly one:
Tool(s) and scope: Codex implemented the projection seam, transcript adapter, UI rendering, tests, and PR documentation. Affected commits include
Generated-by: Codextrailers.Checklist
Does this PR entail a change in behavior?