Skip to content

Desktop shell: session rail - #666

Merged
alexeyzimarev merged 25 commits into
mainfrom
alexeyzimarev/ai-2199-desktop-shell-session-rail
Aug 26, 2026
Merged

alexeyzimarev merged 25 commits into
mainfrom
alexeyzimarev/ai-2199-desktop-shell-session-rail

Conversation

@alexeyzimarev

@alexeyzimarev alexeyzimarev commented Aug 25, 2026 •

Copy link
Copy Markdown
Member

AI-2199

The navigation half of the desktop shell: a 310px session rail grouping repository → worktree → session, and the window restructure it forced — which, over the review iteration with the owner, landed T3-Code-shaped: the app lives on the Sessions surface, and the right pane's empty state IS the launcher.

What's here

  • Wire (additive): AgentStatusDto gains a trailing title — the launch prompt's first non-blank line, truncated to 80 chars, stamped by the daemon in SnapshotAgentsForStatus at the wire boundary (same place model is normalized). Null from older daemons or prompt-less launches; rows fall back to the vendor line. The app's launch payload now also carries the chosen model/effort through the fields the hub already had.
  • The rail: nested DynamicData groups over daemon.Agents (one ObserveOn at the top; inner group caches are mutated on the UI thread by the outer pipeline). Repo headers (caps leaf label, session count; No repository last with its explainer note), collapsible worktree rows (checkout-leaf label, branch glyph, count, amber needs-you pip on any Failed child), two-line session rows (title, vendor · model · age, status dot, guide-lined indent, raised selection). Everything starts expanded (owner revision of the canvas's collapsed-main default — the rail only carries current sessions); collapsing is an explicit sticky choice that survives DynamicData group recreation, and opening a session always re-expands its worktree. Footer: connection dot · word · tenant slug (the active profile name), full daemon identity on hover.
  • Tabless shell, launcher-first: the TabControl is gone and the window boots into Sessions — rail | launcher pane ("What should we build in {repo}?" over one composer card with repo picker, a T3-style searchable harness+model picker (vendor icon rail with real Simple-Icons marks, curated model catalog in HostedHarnessCatalog, typed-custom escape hatch), an effort menu, Remember, Start). Opening a session swaps the launcher for the AI-2195 workspace (Back button removed — the rail is the navigation). The Agents tab is deleted (rail + workspace header supersede it); Activity survives as a flyout off a chip on the launcher header, its polling gated on flyout-open AND launcher-visible AND window-visible; Home stays in the tree but dormant (nothing navigates to it) until it earns its keep. The client area extends into the title bar; window default grows to 1200×760, all surfaces on the Kcap token palette per the design canvas.
  • Composition: the rail is built in BuildAndShowMainWindow over the same daemon service and disposed mirroring HomeViewModel on both teardown paths.

Spec (rides this PR): docs/superpowers/specs/2026-08-25-ai2199-session-rail-design.md — also on the Linear issue, with an addendum comment recording the owner revisions made during review (launcher-as-empty-pane, dormant Home, all-expanded default, Activity flyout). Deferred: per-repo "+" quick-launch, branch names on worktree rows (wire field), dynamic Codex model listing via app-server, Home's full nav column, work-item lanes.

Testing

  • Exact-JSON pins for title (+ model/effort payload cases); daemon-side serialized-payload tests for the truncation contract.
  • Rail VM suites: grouping/sort at all three levels, pip roll-up and recovery, collapse persistence across dto revisions and group recreation, selection propagation, nested-pipeline disposal.
  • Headless smoke: tabless boot onto Sessions with the launcher pane, rail click opens the workspace, rendered-alpha assertion pinning the selection highlight, Activity gate test flipping each of its gates independently.
  • Full solution suite green on the branch (failures match the machine's known pre-existing/env/load-flaky list, verified by name); AOT publish gate clean (no IL2xxx/IL3xxx).

🤖 Generated with Claude Code

alexeyzimarev and others added 12 commits August 25, 2026 13:12
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ack removed

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A local attribute value outranks a style trigger, so the .selected/.holdsSelected
setters could never paint. The resting Transparent now lives in a railRow base
style declared ahead of them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
AgentRowViewModel had no consumer beyond the deleted window projection
(the tray's TrayMenuModel.Agents is an unrelated type), so it is fully
deleted along with MainWindowViewModel's Agents/_agentsSource/RowComparer
and the three now-unreferenced converters (GridEnabledOpacityConverter,
EmptyStateVisibleConverter, HeaderRowVisibleConverter). GridEnabled stays:
MainWindow.axaml still gates the status block's AgentCountText on it.
`actions`/`ticker` ctor params stay unchanged though both are now fully
unused by this VM, per plan.

AgentGridTests.cs is deleted with its grid/row-projection assertions;
its UptimeFormat boundary-table coverage moves to UptimeFormatTests.cs
(UptimeFormat is still live via SessionCardViewModel/RailSessionViewModel)
and its GridEnabled_reflects_attach_state test moves to
MainWindowViewModelTests.cs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@linear-code

linear-code Bot commented Aug 25, 2026

Copy link
Copy Markdown

AI-2199

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add session rail and tabless Home/Sessions desktop shell

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Adds repository → worktree → session navigation with selection, alerts, and persistent collapse
 state.
• Replaces desktop tabs with dedicated Home and Sessions surfaces around the existing workspace.
• Adds prompt-derived session titles and comprehensive wire, projection, navigation, and rendering
 tests.
Diagram

graph TD
  D["Daemon agents"] --> W["Status DTO"] --> R["Rail projection"] --> G["Repo groups"] --> T["Worktree groups"] --> S["Session rows"] --> M["Sessions shell"] --> X["Workspace pane"]
  M --> H["Home surface"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Rebuild a flat rail tree per agent changeset
  • ➕ Simpler ownership and disposal model
  • ➕ Fewer nested DynamicData operators to reason about
  • ➖ Recreates unaffected rows on every update
  • ➖ Makes collapse and selection preservation more manual
  • ➖ Scales less efficiently with active sessions

Recommendation: Keep the nested DynamicData projection used by the PR. It matches the live cache semantics, updates only affected groups, and preserves a clear repo/worktree/session ownership hierarchy; the external collapse-state store and disposal tests adequately address its main risks.

Files changed (34) +3286 / -453

Enhancement (14) +719 / -248
App.axaml.csCompose and dispose the session rail +14/-3

Compose and dispose the session rail

• Builds the rail over the shared daemon service, injects it into the window view model, and tracks it through both application teardown paths.

src/Capacitor.App/App.axaml.cs

MainWindowViewModel.csCoordinate Home/Sessions shell state and rail selection +63/-76

Coordinate Home/Sessions shell state and rail selection

• Adds top-level shell view state and commands, injects the rail, synchronizes workspace selection, expands opened worktrees, and avoids rebuilding an already-open session. Removes the obsolete Agents grid projection.

src/Capacitor.App/ViewModels/MainWindowViewModel.cs

RailCollapseState.csPersist worktree collapse choices outside group VMs +22/-0

Persist worktree collapse choices outside group VMs

• Introduces shared path-keyed collapse state so user choices survive DynamicData group destruction and recreation.

src/Capacitor.App/ViewModels/RailCollapseState.cs

RailRepoViewModel.csProject repository groups into sorted worktrees +62/-0

Project repository groups into sorted worktrees

• Adds repository labels, session counts, no-repository handling, and nested sorted worktree projections with disposal ownership.

src/Capacitor.App/ViewModels/RailRepoViewModel.cs

RailSessionViewModel.csPresent selectable session rail rows +55/-0

Present selectable session rail rows

• Adds title/vendor fallback text, model and age metadata, status and attention indicators, tooltips, selection tracking, and open commands.

src/Capacitor.App/ViewModels/RailSessionViewModel.cs

RailWorktreeViewModel.csManage collapsible worktree session groups +121/-0

Manage collapsible worktree session groups

• Adds worktree labeling, persistent expansion, counts, failed-session and selected-session roll-ups, sorted session rows, and nested subscription disposal.

src/Capacitor.App/ViewModels/RailWorktreeViewModel.cs

SessionRailViewModel.csBuild the live repository/worktree/session projection +111/-0

Build the live repository/worktree/session projection

• Observes daemon agents on the UI scheduler, memoizes repository resolution, creates sorted nested groups, tracks selected sessions, and expands opened worktrees.

src/Capacitor.App/ViewModels/SessionRailViewModel.cs

MainWindow.axamlReplace tabs with Home and Sessions surfaces +99/-145

Replace tabs with Home and Sessions surfaces

• Restructures the window into a larger Home surface and a 310px rail-plus-workspace Sessions surface. Moves Activity into a collapsed Home section and removes the Agents table.

src/Capacitor.App/Views/MainWindow.axaml

MainWindow.axaml.csGate Activity polling by visible Home expansion +18/-10

Gate Activity polling by visible Home expansion

• Replaces tab-selection gating with the conjunction of window visibility, Home view selection, and Activity expander state.

src/Capacitor.App/Views/MainWindow.axaml.cs

SessionRailView.axamlRender the hierarchical session rail +121/-0

Render the hierarchical session rail

• Adds navigation controls, repository/worktree/session templates, empty and no-repository states, connection footer, alerts, collapse controls, and visible selection styling.

src/Capacitor.App/Views/SessionRailView.axaml

SessionRailView.axaml.csAdd the session rail Avalonia view +9/-0

Add the session rail Avalonia view

• Defines the code-behind for the rail user control while preserving the inherited window data context.

src/Capacitor.App/Views/SessionRailView.axaml.cs

WorkspaceView.axamlRemove the workspace Back button +7/-12

Remove the workspace Back button

• Deletes Back navigation and realigns the workspace header actions for the always-adjacent rail layout.

src/Capacitor.App/Views/WorkspaceView.axaml

StatusIpc.csAdd nullable session title to agent status +4/-1

Add nullable session title to agent status

• Extends AgentStatusDto with a trailing nullable Title field to preserve positional and older-payload compatibility.

src/Capacitor.Cli.Core/LocalIpc/StatusIpc.cs

AgentOrchestrator.LocalIpc.csDerive status titles from launch prompts +13/-1

Derive status titles from launch prompts

• Extracts the first non-blank prompt line, trims and caps it at 80 characters, and stamps it onto status snapshots.

src/Capacitor.Cli.Daemon/Services/AgentOrchestrator.LocalIpc.cs

Refactor (4) +33 / -66
SessionCardViewModel.csReuse shared session status brushes +5/-29

Reuse shared session status brushes

• Moves Home session cards onto the common immutable status-dot mapping used by the new rail.

src/Capacitor.App/ViewModels/SessionCardViewModel.cs

SessionStatusDots.csCentralize session status dot mapping +23/-0

Centralize session status dot mapping

• Adds thread-safe shared immutable brushes for Running, Starting, Failed, and neutral session states.

src/Capacitor.App/ViewModels/SessionStatusDots.cs

WorkspaceViewModel.csRemove workspace Back navigation state +0/-8

Remove workspace Back navigation state

• Deletes the externally assigned Back command because the rail remains visible beside every workspace.

src/Capacitor.App/ViewModels/WorkspaceViewModel.cs

Converters.csReplace Agents-grid converters with rail chevrons +5/-29

Replace Agents-grid converters with rail chevrons

• Removes unused grid visibility and opacity converters and adds the worktree expansion glyph converter.

src/Capacitor.App/Views/Converters.cs

Tests (10) +666 / -132
MainWindowSmokeTests.csRetarget shell smoke tests to Home and Sessions +157/-123

Retarget shell smoke tests to Home and Sessions

• Removes Agents-tab assertions and verifies tabless boot, surface swaps, Activity gating, rail-driven workspace opening, and painted selection highlights.

test/Capacitor.App.Tests.Unit/MainWindowSmokeTests.cs

MainWindowViewModelTests.csTest shell view state and rail synchronization +104/-0

Test shell view state and rail synchronization

• Covers Home/Sessions commands, same-session no-op behavior, workspace closure semantics, rail selection, automatic expansion, and the retained connection status gate.

test/Capacitor.App.Tests.Unit/MainWindowViewModelTests.cs

RailSessionViewModelTests.csTest session rail row presentation and commands +91/-0

Test session rail row presentation and commands

• Covers title fallback, vendor/kind/model formatting, failure alerts, selection updates, and open callbacks.

test/Capacitor.App.Tests.Unit/RailSessionViewModelTests.cs

RailWorktreeViewModelTests.csTest worktree grouping, collapse, and disposal +116/-0

Test worktree grouping, collapse, and disposal

• Verifies labels, counts, failed-session roll-up, defaults, persisted toggles, row ordering, headerless groups, and subscription teardown.

test/Capacitor.App.Tests.Unit/RailWorktreeViewModelTests.cs

SessionRailViewModelTests.csTest nested session rail projection behavior +132/-0

Test nested session rail projection behavior

• Verifies repository and worktree grouping and sorting, no-repository placement, counts, collapse survival, selection expansion, alerts, and disposal.

test/Capacitor.App.Tests.Unit/SessionRailViewModelTests.cs

UptimeFormatTests.csPreserve shared uptime formatting coverage +20/-0

Preserve shared uptime formatting coverage

• Moves boundary coverage out of the deleted Agents-grid suite because Home cards and rail rows still use the formatter.

test/Capacitor.App.Tests.Unit/UptimeFormatTests.cs

WorkspaceNavigationTests.csRetarget workspace closure to window navigation +1/-4

Retarget workspace closure to window navigation

• Replaces the removed workspace Back command assertion with the window-level CloseWorkspaceCommand flow.

test/Capacitor.App.Tests.Unit/WorkspaceNavigationTests.cs

WorkspaceViewSmokeTests.csUpdate workspace control inventory after Back removal +3/-3

Update workspace control inventory after Back removal

• Drops BackButton from the named-control smoke test and updates the expected control count.

test/Capacitor.App.Tests.Unit/WorkspaceViewSmokeTests.cs

StatusIpcJsonTests.csPin title serialization and backward compatibility +15/-2

Pin title serialization and backward compatibility

• Extends exact JSON expectations for non-null and null titles and confirms payloads without the new member deserialize safely.

test/Capacitor.Cli.Core.Tests.Unit/LocalIpc/StatusIpcJsonTests.cs

AgentStatusSnapshotTests.csTest prompt-derived status title normalization +27/-0

Test prompt-derived status title normalization

• Covers short, multiline, overlong, blank, and absent prompts, including serialized value and null output.

test/Capacitor.Cli.Daemon.Tests.Unit/Services/AgentStatusSnapshotTests.cs

Documentation (5) +1866 / -6
2026-08-25-ai2199-session-rail.mdDocument the session rail implementation plan +1573/-0

Document the session rail implementation plan

• Adds the task-by-task implementation, testing, composition, disposal, and AOT verification plan for AI-2199.

docs/superpowers/plans/2026-08-25-ai2199-session-rail.md

2026-08-25-ai2199-session-rail-design.mdSpecify the tabless shell and session rail +288/-0

Specify the tabless shell and session rail

• Defines the Home/Sessions shell, rail hierarchy, wire title contract, UX decisions, test expectations, deferred scope, and risks.

docs/superpowers/specs/2026-08-25-ai2199-session-rail-design.md

UiTicker.csGeneralize ticker consumer documentation +2/-2

Generalize ticker consumer documentation

• Removes the deleted agent-row-specific wording from the ticker scheduling comment without changing behavior.

src/Capacitor.App/Services/UiTicker.cs

HomeViewModel.csRemove obsolete Agents projection reference +1/-1

Remove obsolete Agents projection reference

• Updates scheduler-safety documentation after the Agents grid projection was removed.

src/Capacitor.App/ViewModels/HomeViewModel.cs

HomeView.axaml.csRefresh empty-state converter documentation +2/-3

Refresh empty-state converter documentation

• Removes an obsolete comparison to the deleted Agents-grid header converter.

src/Capacitor.App/Views/HomeView.axaml.cs

Other (1) +2 / -1
AgentOrchestrator.csAllow seeded agents to carry prompts +2/-1

Allow seeded agents to carry prompts

• Extends the daemon test seeding seam so snapshot tests can exercise prompt-derived status titles.

src/Capacitor.Cli.Daemon/Services/AgentOrchestrator.cs

@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: cc48e63ccd

ℹ️ 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 +343 to +344
// Re-clicking the open session must not tear down and rebuild a live attach.
if (CurrentWorkspace?.AgentId == agentId) return;

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 Expand the rail before returning for the current session

When the user collapses the worktree containing the open workspace, switches to Home, and clicks that session's card, this early return occurs after switching back to Sessions but before Rail.NotifySessionOpened. The existing workspace remains selected inside a collapsed group, leaving no visible selected rail row; notify the rail before taking the same-session no-op path.

Useful? React with 👍 / 👎.

var line = raw.Trim();
if (line.Length == 0) continue;
return line.Length <= 80 ? line : line[..79] + "…";
}

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 Preserve Unicode scalars when truncating titles

When a prompt's first line exceeds 80 UTF-16 code units and a supplementary character crosses the cutoff—for example, 78 ASCII characters followed by an emoji and a suffix—line[..79] retains only the emoji's high surrogate. The resulting malformed title cannot preserve the original text and may be serialized with a replacement character; truncate by Unicode scalar or grapheme boundaries before appending the ellipsis.

Useful? React with 👍 / 👎.

@qodo-code-review

qodo-code-review Bot commented Aug 25, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (3) 📜 Skill insights (0)

Grey Divider


Action required

1. AI-2199 remains in comment ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The added source comment embeds the Linear identifier AI-2199, which is prohibited in source
comments. Remove the issue identifier or use a GitHub issue reference only if the reference is
genuinely necessary.
Code

src/Capacitor.App/ViewModels/MainWindowViewModel.cs[R146-147]

+    // Legacy name from the deleted Agents grid (AI-2199) — now gates only the "n of m agents"
+    // status-line text (MainWindow.axaml's AgentCountText), never a grid.
Evidence
Rule 28 prohibits Linear issue identifiers in comments, while the added comment explicitly includes
AI-2199.

CLAUDE.md: Comments Must Not Use Linear Issue Numbers
src/Capacitor.App/ViewModels/MainWindowViewModel.cs[146-147]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The source comment contains the prohibited Linear issue identifier `AI-2199`.

## Issue Context
Compliance rule 28 disallows Linear issue numbers in source and configuration comments.

## Fix Focus Areas
- src/Capacitor.App/ViewModels/MainWindowViewModel.cs[146-147]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Historical block cites AI-2199 ⊘ Outdated 📘 Rule violation ⚙ Maintainability
Description
This four-line comment includes the prohibited Linear identifier AI-2199 and narrates deleted-code
history and hypothetical future use. It violates both the issue-reference restriction and the
requirement that comments remain brief and reason-focused.
Code

src/Capacitor.App/ViewModels/MainWindowViewModel.cs[R330-333]

+            // `actions` and `ticker` feed nothing here since the Agents grid's row pipeline (the
+            // sole consumer of both) was deleted (AI-2199) — kept as ctor params unchanged rather
+            // than reshaping the signature every caller passes; a future Sessions-rail row action
+            // or live uptime would consume them again.
Evidence
The cited block explicitly names AI-2199, spans four lines, and explains deletion history and
possible future usage rather than a concise current constraint.

CLAUDE.md: Code and Configuration Comments Must Be Brief and Explain Non-Obvious Reasons
CLAUDE.md: Comments Must Not Use Linear Issue Numbers
src/Capacitor.App/ViewModels/MainWindowViewModel.cs[330-333]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The comment contains `AI-2199` and an unnecessarily long historical narrative about deleted code and possible future consumers.

## Issue Context
Comments should explain only the non-obvious current reason for retaining the constructor parameters, without Linear identifiers or speculative history.

## Fix Focus Areas
- src/Capacitor.App/ViewModels/MainWindowViewModel.cs[330-333]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Rail session tests misplaced 📘 Rule violation ⚙ Maintainability
Description
RailSessionViewModelTests is added at the test-project root even though its production owner is
under ViewModels. This breaks the required mirrored production/test directory ownership.
Code

test/Capacitor.App.Tests.Unit/RailSessionViewModelTests.cs[11]

+public class RailSessionViewModelTests {
Evidence
Rule 12 requires test directories to mirror production ownership; the new test file is at the test
root while RailSessionViewModel is in src/Capacitor.App/ViewModels.

CLAUDE.md: Test Projects Must Mirror Production Ownership and Share Utilities Through Helpers
test/Capacitor.App.Tests.Unit/RailSessionViewModelTests.cs[11-11]
src/Capacitor.App/ViewModels/RailSessionViewModel.cs[14-14]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new `RailSessionViewModelTests` file does not mirror the production `ViewModels` directory.

## Issue Context
Move the test to the corresponding `ViewModels` directory in `Capacitor.App.Tests.Unit` while preserving its namespace and project ownership conventions.

## Fix Focus Areas
- test/Capacitor.App.Tests.Unit/RailSessionViewModelTests.cs[11-11]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View high (4)
4. Test comment cites AI-2199 ⊘ Outdated 📘 Rule violation ⚙ Maintainability
Description
The added test comment contains the prohibited Linear identifier AI-2199 and narrates that the
test was retargeted from a deleted file. This historical detail is not needed to explain the test's
current behavior.
Code

test/Capacitor.App.Tests.Unit/MainWindowViewModelTests.cs[R188-189]

+    /// Retargeted from the deleted AgentGridTests.cs (AI-2199: the Agents grid is gone, but
+    /// GridEnabled itself survives — it now gates only the status block's AgentCountText).
Evidence
The comment explicitly includes AI-2199 and describes retargeting from deleted
AgentGridTests.cs, violating rules 27 and 28.

CLAUDE.md: Code and Configuration Comments Must Be Brief and Explain Non-Obvious Reasons
CLAUDE.md: Comments Must Not Use Linear Issue Numbers
test/Capacitor.App.Tests.Unit/MainWindowViewModelTests.cs[188-189]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The test comment contains `AI-2199` and describes migration history from a deleted test file.

## Issue Context
Keep only a concise explanation of what `GridEnabled` currently controls, without a Linear identifier.

## Fix Focus Areas
- test/Capacitor.App.Tests.Unit/MainWindowViewModelTests.cs[188-189]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Session rail tests misplaced 📘 Rule violation ⚙ Maintainability
Description
SessionRailViewModelTests is added at the test-project root instead of under a mirrored
ViewModels directory. The new test layout therefore does not follow its production owner's
directory structure.
Code

test/Capacitor.App.Tests.Unit/SessionRailViewModelTests.cs[8]

+public class SessionRailViewModelTests {
Evidence
Rule 12 requires mirrored directories; this new test is at the test root while
SessionRailViewModel is under the production ViewModels directory.

CLAUDE.md: Test Projects Must Mirror Production Ownership and Share Utilities Through Helpers
test/Capacitor.App.Tests.Unit/SessionRailViewModelTests.cs[8-8]
src/Capacitor.App/ViewModels/SessionRailViewModel.cs[19-19]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new `SessionRailViewModelTests` file does not mirror the production `ViewModels` directory.

## Issue Context
Move the test to the corresponding `ViewModels` directory in `Capacitor.App.Tests.Unit`.

## Fix Focus Areas
- test/Capacitor.App.Tests.Unit/SessionRailViewModelTests.cs[8-8]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Uptime comment cites AI-2199 ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The new class comment includes the prohibited Linear identifier AI-2199 and spends three lines
narrating the test's origin in a deleted suite. The history should be removed in favor of a concise
current-purpose description.
Code

test/Capacitor.App.Tests.Unit/UptimeFormatTests.cs[R3-5]

+/// UptimeFormat (spec §8 boundary table) — retargeted from the deleted AgentGridTests.cs (AI-2199:
+/// the Agents grid is gone, but SessionCardViewModel.Age and RailSessionViewModel still format
+/// uptime through this same shared utility).
Evidence
The added three-line comment explicitly contains AI-2199 and narrates retargeting from deleted
AgentGridTests.cs.

CLAUDE.md: Code and Configuration Comments Must Be Brief and Explain Non-Obvious Reasons
CLAUDE.md: Comments Must Not Use Linear Issue Numbers
test/Capacitor.App.Tests.Unit/UptimeFormatTests.cs[3-5]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The class comment contains `AI-2199` and unnecessary migration history.

## Issue Context
Describe only the current `UptimeFormat` boundary coverage, if a comment is needed at all.

## Fix Focus Areas
- test/Capacitor.App.Tests.Unit/UptimeFormatTests.cs[3-5]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Rail worktree tests misplaced 📘 Rule violation ⚙ Maintainability
Description
RailWorktreeViewModelTests is added at the test-project root rather than mirroring the production
ViewModels directory. This violates the required production ownership layout for tests.
Code

test/Capacitor.App.Tests.Unit/RailWorktreeViewModelTests.cs[13]

+public class RailWorktreeViewModelTests {
Evidence
The new test targets a type owned by src/Capacitor.App/ViewModels but places the test at the
test-project root, contrary to rule 12's mirrored-directory requirement.

CLAUDE.md: Test Projects Must Mirror Production Ownership and Share Utilities Through Helpers
test/Capacitor.App.Tests.Unit/RailWorktreeViewModelTests.cs[13-13]
src/Capacitor.App/ViewModels/RailWorktreeViewModel.cs[18-18]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new `RailWorktreeViewModelTests` file does not mirror the production `ViewModels` directory.

## Issue Context
Move the test to the corresponding `ViewModels` directory in `Capacitor.App.Tests.Unit`.

## Fix Focus Areas
- test/Capacitor.App.Tests.Unit/RailWorktreeViewModelTests.cs[13-13]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

8. Initial repo count stays blank 🐞 Bug ≡ Correctness
Description
RailRepoViewModel initializes CountText to an empty string and listens only for later
CountChanged emissions, so a newly formed repository containing one session shows no count until
that group gains or loses another session. Repository headers therefore omit the required session
count for stable one-session groups.
Code

src/Capacitor.App/ViewModels/RailRepoViewModel.cs[R45-47]

+        _countText = group.Cache.CountChanged
+            .Select(c => c == 1 ? "1 session" : $"{c} sessions")
+            .ToProperty(this, x => x.CountText, initialValue: "")
Evidence
The repository property starts as "" and receives only subsequent count changes, while repository
VMs are created from groups that already contain their first item. The sibling worktree
implementation explicitly uses StartWith(sessionsCache.Count), and the XAML renders CountText
directly in the repository header.

src/Capacitor.App/ViewModels/RailRepoViewModel.cs[45-57]
src/Capacitor.App/ViewModels/RailWorktreeViewModel.cs[81-89]
src/Capacitor.App/Views/SessionRailView.axaml[57-59]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Repository count text is blank when a group is created with its initial session because the count observable is not seeded from the current cache count.

## Issue Context
The sibling worktree implementation already seeds `CountChanged` with the current count and initializes its properties consistently.

## Fix Focus Areas
- src/Capacitor.App/ViewModels/RailRepoViewModel.cs[45-47]
- src/Capacitor.App/ViewModels/RailWorktreeViewModel.cs[83-88]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. Unicode title truncation corrupts text ⊘ Outdated 🐞 Bug ≡ Correctness
Description
TitleFromPrompt truncates a .NET string at UTF-16 code-unit index 79, so a supplementary Unicode
character crossing the cutoff is split, leaving an unpaired high surrogate before the ellipsis and
producing a corrupted title instead of a valid 80-character display string. Because the derived
title is placed directly into every AgentStatusDto status snapshot, affected session rows receive
malformed serialized and rendered display text.
Code

src/Capacitor.Cli.Daemon/Services/AgentOrchestrator.LocalIpc.cs[R43-45]

+            var line = raw.Trim();
+            if (line.Length == 0) continue;
+            return line.Length <= 80 ? line : line[..79] + "…";
Evidence
The helper selects and trims an arbitrary launch-prompt line, then applies line[..79] before
appending an ellipsis. Because .NET string indices address UTF-16 code units, a non-BMP character
beginning at code-unit index 78 consists of a high surrogate at index 78 and a low surrogate at
index 79, so the slice retains only the high surrogate; the resulting malformed value is then
assigned to AgentStatusDto.Title in every status snapshot for serialization and display in the
desktop session rail.

src/Capacitor.Cli.Daemon/Services/AgentOrchestrator.LocalIpc.cs[38-47]
src/Capacitor.Cli.Daemon/Services/AgentOrchestrator.LocalIpc.cs[56-69]
src/Capacitor.App/ViewModels/RailSessionViewModel.cs[30-39]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description

`TitleFromPrompt` truncates prompt text at UTF-16 code-unit index 79. If the cutoff falls within a surrogate pair, the function creates malformed display text by retaining an unpaired high surrogate before appending the ellipsis.

## Issue Context

Titles are derived from arbitrary user launch prompts, copied into every `AgentStatusDto` status snapshot, serialized, and rendered in the desktop session rail. Preserve the 80-character-with-ellipsis display contract while truncating on a Unicode-safe boundary, and add a boundary test containing a supplementary-plane character.

## Fix Focus Areas

- src/Capacitor.Cli.Daemon/Services/AgentOrchestrator.LocalIpc.cs[38-46]
- src/Capacitor.Cli.Daemon/Services/AgentOrchestrator.LocalIpc.cs[56-69]
- test/Capacitor.Cli.Daemon.Tests.Unit/Services/AgentStatusSnapshotTests.cs[183-208]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
Review mode: 🧠 Deep: This is a broad, logic-dense shell rewrite spanning UI composition, nested DynamicData pipelines, lifecycle/disposal, navigation state, wire compatibility, daemon behavior, and many independent code paths where redundant review is materially useful.

Grey Divider

Tip of the day
💡 Did you know, you can start a comment with 'qodo' or '@qodo' to chat about any finding

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/Capacitor.App/ViewModels/MainWindowViewModel.cs Outdated
Comment thread src/Capacitor.App/ViewModels/MainWindowViewModel.cs Outdated
Comment thread test/Capacitor.App.Tests.Unit/MainWindowViewModelTests.cs Outdated
Comment thread test/Capacitor.App.Tests.Unit/UptimeFormatTests.cs Outdated
/// RxSchedulers.MainThreadScheduler, which is not immediate in a bare test process — see
/// MainWindowViewModelTests' header comment. Every test here runs inside
/// AvaloniaSession.WithImmediateRxScheduler and carries [NotInParallel("AvaloniaSession")].
public class RailSessionViewModelTests {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

5. Rail session tests misplaced 📘 Rule violation ⚙ Maintainability

RailSessionViewModelTests is added at the test-project root even though its production owner is
under ViewModels. This breaks the required mirrored production/test directory ownership.
Agent Prompt
## Issue description
The new `RailSessionViewModelTests` file does not mirror the production `ViewModels` directory.

## Issue Context
Move the test to the corresponding `ViewModels` directory in `Capacitor.App.Tests.Unit` while preserving its namespace and project ownership conventions.

## Fix Focus Areas
- test/Capacitor.App.Tests.Unit/RailSessionViewModelTests.cs[11-11]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

/// not immediate in a bare test process — see MainWindowViewModelTests' header comment. Every
/// test here runs inside AvaloniaSession.WithImmediateRxScheduler and carries
/// [NotInParallel("AvaloniaSession")].
public class RailWorktreeViewModelTests {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

6. Rail worktree tests misplaced 📘 Rule violation ⚙ Maintainability

RailWorktreeViewModelTests is added at the test-project root rather than mirroring the production
ViewModels directory. This violates the required production ownership layout for tests.
Agent Prompt
## Issue description
The new `RailWorktreeViewModelTests` file does not mirror the production `ViewModels` directory.

## Issue Context
Move the test to the corresponding `ViewModels` directory in `Capacitor.App.Tests.Unit`.

## Fix Focus Areas
- test/Capacitor.App.Tests.Unit/RailWorktreeViewModelTests.cs[13-13]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


namespace Capacitor.App.Tests.Unit;

public class SessionRailViewModelTests {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

7. Session rail tests misplaced 📘 Rule violation ⚙ Maintainability

SessionRailViewModelTests is added at the test-project root instead of under a mirrored
ViewModels directory. The new test layout therefore does not follow its production owner's
directory structure.
Agent Prompt
## Issue description
The new `SessionRailViewModelTests` file does not mirror the production `ViewModels` directory.

## Issue Context
Move the test to the corresponding `ViewModels` directory in `Capacitor.App.Tests.Unit`.

## Fix Focus Areas
- test/Capacitor.App.Tests.Unit/SessionRailViewModelTests.cs[8-8]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +45 to +47
_countText = group.Cache.CountChanged
.Select(c => c == 1 ? "1 session" : $"{c} sessions")
.ToProperty(this, x => x.CountText, initialValue: "")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

8. Initial repo count stays blank 🐞 Bug ≡ Correctness

RailRepoViewModel initializes CountText to an empty string and listens only for later
CountChanged emissions, so a newly formed repository containing one session shows no count until
that group gains or loses another session. Repository headers therefore omit the required session
count for stable one-session groups.
Agent Prompt
## Issue description
Repository count text is blank when a group is created with its initial session because the count observable is not seeded from the current cache count.

## Issue Context
The sibling worktree implementation already seeds `CountChanged` with the current count and initializes its properties consistently.

## Fix Focus Areas
- src/Capacitor.App/ViewModels/RailRepoViewModel.cs[45-47]
- src/Capacitor.App/ViewModels/RailWorktreeViewModel.cs[83-88]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread src/Capacitor.Cli.Daemon/Services/AgentOrchestrator.LocalIpc.cs Outdated
alexeyzimarev and others added 13 commits August 25, 2026 18:55
Rail, window chrome, and workspace header restyled to the Hosted Agents
Shell artboard: Kcap surface/border tokens on the rail with caps repo
headers, branch glyphs, guide-lined session indents and pill pips; the
client area extends into the title bar (traffic lights over the rail,
drag handled by the chrome strips); Home's status block, Sessions entry
and Activity section themed; workspace header gets the vendor chip and
pill tab.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The consent feed leaves the page flow: an Activity chip beside Sessions
(top right) opens it as a flyout panel, whose open state drives the
polling gate; leaving Home closes it. Rail, workspace header, and Home
header fonts bumped ~1px toward Linear's density.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Home/Sessions icon buttons grow to 38x32 with a larger glyph; the
greeting row's Search and New session placeholders go (real search is a
later slice, and the button only focused the goal box sitting right
below it). The rail's New session row stays - it is the navigation from
Sessions back to the prompt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The 9px text glyph rendered as a dot; two stroked Paths (right/down)
in the muted color stay crisp at 12px. The orphaned
ExpanderChevronConverter goes with it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
T3-style: the window boots on the Sessions surface, and the right
pane's empty state is a centered launcher (What should we build in
{repo}? over the composer with repo/harness chips) — asking for a new
session and having none selected are the same screen. The rail's New
session row deselects instead of navigating; the Home/Sessions switcher
goes. Home stays in the tree, hidden, its launcher card moved to
LauncherPaneView and the daemon status block + Activity chip to the
launcher pane header. The Activity gate becomes flyout-open AND
launcher-visible AND window-visible.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The launch box grows T3's remaining knobs: a free-text Model chip
(blank = vendor default; resets when the harness changes — ids are
vendor-specific) and an Effort menu (Default/low/medium/high/xhigh),
both riding the wire fields the hub already carried. The daemon
identity block leaves the launcher's top-left — the rail footer is the
one indicator, now showing the tenant slug (the active profile's name)
with the full identity on hover; only the self-hiding Start/Retry
recovery strip remains in the pane.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The rail only carries current sessions, so nothing hides by default —
the canvas's collapsed-main rule goes (owner revision); collapsing is
an explicit, sticky choice, and opening a session still beats it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The harness and model chips merge into an AgentChip ("Claude · Fable 5")
opening a T3-style picker: search box over vendor-grouped rows — each
vendor leads with its Default and follows with curated suggestions
(hardcoded catalog in HostedHarnessCatalog, exactly how T3 ships
Claude's; T3 lists Codex's live from codex app-server, which our daemon
could grow an IPC for later). Unavailable vendors stay listed but
disabled, and a non-empty search always offers itself verbatim as a
custom model id, so catalog drift never blocks a launch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Vendor icon rail (tinted monogram tiles, active tile raised) beside an
underlined magnifier search over roomy bold rows with vendor sub-lines.
Empty search shows the active tab's models; typing searches across
vendors; the typed term stays offered verbatim as a custom id. The
flyout presenter itself is the panel via the new kcapPanel style.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Simple Icons path data (CC0) for claude, codex (OpenAI), cursor,
copilot, gemini, and opencode — filled Paths tinted per vendor
(monochrome brands in the text color, claude/gemini in their hues).
Kiro/antigravity keep monogram tiles (no published mark) and pi gets
the pi glyph. The AgentChip leads with the mark, T3-style. Geometries
parse per call, never cached statically — Geometry carries the same
thread affinity brushes do.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Four-angle cleanup (reuse/simplification/efficiency/altitude):
- One WindowChrome.BeginDrag replaces three identical drag handlers.
- AgentInstance caches Title once (SnapshotAgentsForStatus re-parsed the
  immutable Prompt on every status pulse); the cut is now surrogate-safe.
- PlatformPaths owns the platform comparer + raw-leaf primitive that
  HomeViewModel, RepoLabel, and the rail VMs each re-typed.
- SessionStatusDots.NeedsAttention is the one needs-you status rule.
- Vendor tiles and the effort ladder move into HostedHarnessCatalog
  beside the model catalog (one file to add a vendor), with a guard test.
- Dead weight out: GridEnabled (nothing binds it), the VM's unused
  actions/ticker ctor params (23 call sites trimmed), redundant
  SessionCount, duplicate selected/holdsSelected style blocks.
- HomeViewModel.SetVendor centralizes the model-reset invariant; the
  holdsSelected probe stops allocating a HashSet per changeset.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-desktop-shell-session-rail

# Conflicts:
#	src/Capacitor.App/App.axaml.cs
CI's linear-id lint scans every tracked C# comment, and the comment
rules ban ticket ids and evolution narration outright.

Co-Authored-By: Claude Fable 5 <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