Repository navigation
refactor(ui): align Sessions and transcript with agent-sessions patterns - #485
Conversation
A finished bootstrap deletes its root's frontier rows, the shadow/live storage separation is proven once per open and re-walked only when an ancestor's identity or mode changes, and a locator whose source cannot be opened and that was never captured leaves the dirty set. Built from the 2026-10-19 Daily sample; not deployed in this commit. Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Phase 1 — Sessions page rebuilt as a master-detail split view:
HSplitView with a native SwiftUI Table (star/source/title/updated/
project/messages) and a footer status bar on the left, an embedded
SessionDetailView on the right (selection settles after a 150ms
debounce). Child sessions flatten into the same table with
expandable parents and lazy child-box loads; suggested children
get inline Confirm/Dismiss. KPI cards are replaced by the footer
count bar. Filters, pagination, generation guards, reload
coalescing, and all sessions_* accessibility identifiers are
preserved.
Phase 2 — transcript: consecutive tool-ish rows (>=2) collapse
into a "N tool calls" header (new TranscriptBlock +
TranscriptBlockGrouper, pure projection over displayIndexed so
find/match machinery is untouched); find navigation expands a
collapsed run before scrolling. Errors stay standalone. Assistant
role color moves from purple to the agent-sessions warm brown,
dark-mode aware.
Phase 3 — cross-page dedupe: ProjectBadge.shortLabel (project
label x3), KPICard.compactNumber (KPI abbreviation x2),
ServiceDataFreshness.asOfText ("as of HH:mm" x2 + duplicate
formatters). Card components and suggestion-action bodies are
assessed as distinct and deliberately not merged.
Tests: buildRows x3, transcript grouping x5; wiring scans and
favorite raw-id count updated to the new structure. No schema,
service, or IPC changes.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 41d530b219
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| catch is CancellationError { throw CancellationError() } | ||
| catch { | ||
| try deferDirty(claim, root: root, now: now, sourceUnavailable: true) | ||
| // sourceGeneration throws only when the file cannot be opened. | ||
| try deferDirty(claim, root: root, now: now, sourceUnavailable: true, sourceVanished: true) |
There was a problem hiding this comment.
Retry non-ENOENT source-generation failures
When sourceGeneration fails for a transient condition such as EMFILE or EIO, this catch marks the source as vanished even though the error does not establish that. For a never-captured claim, drainUnavailableDeferrals then advances acknowledged_revision to dirty_revision and removes its retry; because clearing resource pressure need not generate a filesystem event, that session can remain permanently uncaptured. Only absence errors such as ENOENT should set sourceVanished; other failures need to retain the retry.
Useful? React with 👍 / 👎.
| // The child moves suggested → confirmed: refresh its parent box | ||
| // so the row re-renders in place instead of staying stale. | ||
| invalidateChildren(of: child.suggestedParentId) | ||
| await loadData() |
There was a problem hiding this comment.
Reload children after suggestion mutations
When confirming a suggestion under an expanded parent, invalidateChildren clears the box and starts a child load using the current generation, but the immediately following loadData() increments loadGeneration, causing that child result to be discarded. The completed full load only refreshes expanded parents whose box is non-nil, so the expanded parent remains empty until the user collapses and re-expands it; dismissal follows the same path.
Useful? React with 👍 / 👎.
| // A filter change can remove the selected row: release the stale | ||
| // selection instead of keeping an invisible detail pane target. | ||
| if let selected = selectedSessionId, findSession(selected) == nil { | ||
| selectedSessionId = nil |
There was a problem hiding this comment.
Clear cached child selections after filtering
If a user selects an expanded child and then changes a filter so its parent disappears, this stale-selection check still succeeds because findSession searches every retained childrenByParent box, including boxes belonging to parents no longer in sessions. The table therefore shows the filtered result set while the detail pane continues displaying an excluded session; clear/prune child boxes on filter reload or restrict this lookup to children of currently visible parents.
Useful? React with 👍 / 👎.
|
📸 Screenshot Regression: 1 failure(s)
View diff images in artifacts. |
… refactor - docs/invariants.md: drop backticks around bare symbol names in the three collector Enforced-by anchors added by 81bf3f1 — the ledger gate requires every backticked token to be a checked path, and the symbol mentions were failing tests/scripts/invariants-ledger.test.ts in both the Node quality and macOS gates jobs. - macos/EngramUITests/baselines/detail_transcript.png: regenerate from the CI smoke run's actual (SSIM 0.8449 vs the old baseline) — the transcript view intentionally changed (tool-run collapse + warm brown assistant role color). Local regeneration is blocked by the testmanagerd hang, so the CI-produced actual is the new baseline. Known follow-up: detail_transcript_dark, sessions_list_dark, and detail_findBar baselines are only compared by the post-merge ui-test-full job and will drift; they get regenerated from that run's screenshot-diffs-full artifact immediately after merge. Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Summary
Full-app UI alignment with jazzyalex/agent-sessions (the reference Engram's UI was originally modeled on), in three phases on one branch. No schema, service, or IPC changes.
Phase 1 — Sessions page rebuilt as a master-detail split view
HSplitView: left pane = native SwiftUITable(★ / source / title / updated / project / messages) + footer status bar; right pane = embeddedSessionDetailView(no back button; selection settles after a 150ms debounce).BrowseReloadCoalescer, and allsessions_*accessibility identifiers preserved.Phase 2 — Transcript: collapsed tool runs + role palette
TranscriptBlock+TranscriptBlockGrouper, new). Click expands in place; errors stay standalone so red rows remain scannable.displayIndexedstays the canonical row space — find indices, match counts, type chips untouched; blocks are a rendering projection. Find navigation into a collapsed run expands it before scrolling.NSColor).Phase 3 — Cross-page dedupe
ProjectBadge.shortLabel(project label ×3 → 1),KPICard.compactNumber(KPI abbreviation ×2 → 1, SourcePulseView gains the ≥1M tier),ServiceDataFreshness.asOfText("as of HH:mm" ×2 → 1, duplicateDateFormatters deleted).Tests
buildRows×3 (BrowseReloadCoalescerTests), transcript grouping ×5 (TranscriptLabelAndCopyTests).EngramTests3374 passed / 1 flaky launcher timing test (passes solo — spawn-latency timeout under load, no launcher code touched).testmanagerdhangs before app launch (environment issue, pre-existing).🤖 Generated with Claude Code