Skip to content

refactor(ui): align Sessions and transcript with agent-sessions patterns - #485

Merged
bbingz merged 4 commits into
mainfrom
refactor/ui-agent-sessions-20261019
Oct 20, 2026
Merged

bbingz merged 4 commits into
mainfrom
refactor/ui-agent-sessions-20261019

Conversation

@bbingz

@bbingz bbingz commented Oct 19, 2026

Copy link
Copy Markdown
Owner

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 SwiftUI Table (★ / source / title / updated / project / messages) + footer status bar; right pane = embedded SessionDetailView (no back button; selection settles after a 150ms debounce).
  • Child sessions flatten into the same table: expandable parents with counts, lazy child-box loads, inline Confirm/Dismiss on suggested children. KPI cards replaced by the footer count bar.
  • Filters, pagination, generation guards, BrowseReloadCoalescer, and all sessions_* accessibility identifiers preserved.

Phase 2 — Transcript: collapsed tool runs + role palette

  • Consecutive tool-ish rows (≥2) collapse into a "› N tool calls" header row (TranscriptBlock + TranscriptBlockGrouper, new). Click expands in place; errors stay standalone so red rows remain scannable.
  • displayIndexed stays 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.
  • Assistant role color purple → agent-sessions warm brown (dark-mode aware via dynamic 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, duplicate DateFormatters deleted).
  • Card components and suggestion-action bodies assessed as distinct, deliberately not merged (reasoning in CHANGELOG).

Tests

  • New: buildRows ×3 (BrowseReloadCoalescerTests), transcript grouping ×5 (TranscriptLabelAndCopyTests).
  • Updated wiring scans: Table selection = keyboard reachable, favorite raw-id count 2→3 (new child-box load site), removed orphaned KPI screen accessors.
  • Local: Debug build green; 12 suites covering touched surfaces (360 tests) pass; full EngramTests 3374 passed / 1 flaky launcher timing test (passes solo — spawn-latency timeout under load, no launcher code touched).
  • UI smoke tests could not run locally: testmanagerd hangs before app launch (environment issue, pre-existing).

🤖 Generated with Claude Code

reviewer and others added 3 commits October 19, 2026 15:12
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>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-19T23:20:36.779340Z 41d530b PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines 598 to +601
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)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Comment on lines +656 to +659
// 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()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment on lines +793 to +796
// 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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

@github-actions

Copy link
Copy Markdown

📸 Screenshot Regression: 1 failure(s)

Screen SSIM Pixel Diff AI Status
detail_transcript 0.845 9.84% — ❌

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>
@bbingz
bbingz merged commit c22bc82 into main Oct 20, 2026
16 checks passed
@bbingz
bbingz deleted the refactor/ui-agent-sessions-20261019 branch October 20, 2026 00:34
bbingz pushed a commit that referenced this pull request Oct 20, 2026
Co-Authored-By: Claude Code <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant