Skip to content

Read the work item from one endpoint in the desktop work-context pane - #779

Merged
alexeyzimarev merged 3 commits into
mainfrom
feature/ai-2521-work-item-read
Sep 6, 2026
Merged

alexeyzimarev merged 3 commits into
mainfrom
feature/ai-2521-work-item-read

Conversation

@alexeyzimarev

Copy link
Copy Markdown
Member

Closes #777 — AI-2521

What & why

The desktop work-context pane rendered a session's work item from three reads and showed SOON pills for the item's state, per-part completion, the linked issue and who is on it, recovering the key by splitting the assignment label. The server now serves all of it in one read, GET /api/work-items/{id}. The pane reads it beside the topology call once the primary is known and fills every pill from it, plus the overview, with key and title from the endpoint's own fields.

Where to look

The card's identity is the served id: an absorbed item comes back under its survivor, and the assignments row may catch up a poll later, so ApplyReady keys "same primary" on it, with the requested id as the fallback when a read carried no item. Contributors render as initials: the app has no remote image loader, so avatar_url rides the view model unfetched. Reference-class links are dropped on purpose; the issue card is the first issue link of class link.

Verification

  • Capacitor.App.Tests.Unit: 1,421 passed, 0 failed.
  • Capacitor.Cli.Core.Tests.Unit: 2,985 passed; the one failure, a LocalIpc timing test, passes alone.
  • dotnet build Capacitor.slnx and a --no-incremental App build: 0 warnings.
  • dotnet publish src/Capacitor.Cli/Capacitor.Cli.csproj -c Release: no IL2026/IL3050 warnings.

🤖 Generated with Claude Code

…#777)

The card's identity is the id the server served: an absorbed item comes back under its survivor, and the assignments row may catch up to that id a poll later, so neither transition may drop the projection. Contributors render as initials because the app has no remote image loader.

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

linear-code Bot commented Sep 5, 2026

Copy link
Copy Markdown

AI-2521

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 5, 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-09-05T15:44:45.421651Z 5000c0d 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.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Populate desktop work context from canonical work-item read

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Reads canonical work-item details concurrently with topology after resolving the primary
 assignment.
• Replaces placeholders with state, progress, issue, overview, and contributor data.
• Preserves projections across transient failures and absorbed-item identity transitions.
Diagram

sequenceDiagram
    participant View as Work Pane
    participant VM as Context VM
    participant Reader as Context Reader
    participant Client as HTTP Client
    participant API as Work API
    View->>VM: Refresh session
    VM->>Reader: Read context
    Reader->>Client: Get assignments
    Client->>API: Session assignments
    API-->>Client: Primary assignment
    Client-->>Reader: Primary assignment
    par Canonical item
        Reader->>Client: Get work item
        Client->>API: GET work item
        API-->>Client: Item details
        Client-->>Reader: Item DTO
    and Topology
        Reader->>Client: Get topology
        Client->>API: GET topology
        API-->>Client: Graph context
        Client-->>Reader: Topology DTO
    and Session summary
        Reader->>Client: Get summary
        Client->>API: Session summary
        API-->>Client: Pull requests
        Client-->>Reader: Summary DTO
    end
    Reader-->>VM: Totalized read
    VM-->>View: Render projections
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Pane-specific aggregate endpoint
  • ➕ Could return assignments, item, topology, and summary in one network request
  • ➕ Would centralize consistency and absorbed-item resolution on the server
  • ➖ Introduces a desktop-specific API contract
  • ➖ Couples independently degradable sections and requires broader server changes
2. Extend the topology response
  • ➕ Could eliminate the additional canonical-item request
  • ➕ Reuses an endpoint already fetched by the pane
  • ➖ Overloads graph topology with identity, contributor, and issue concerns
  • ➖ Duplicates the canonical work-item representation

Recommendation: Keep the PR's approach: consume the existing canonical item endpoint and retain topology for graph-only concerns. Concurrent requests limit latency, while separate failure flags preserve useful sections during partial outages; a pane-specific aggregate is only justified if request count later becomes measurable overhead.

Files changed (17) +851 / -112

Enhancement (10) +381 / -83
RelativeTime.csAdd contributor activity time formatting +16/-0

Add contributor activity time formatting

• Adds deterministic relative timestamps for recent activity and invariant date formatting for older timestamps.

src/Capacitor.App/ViewModels/RelativeTime.cs

WorkContextItems.csExpand part and contributor view models +26/-5

Expand part and contributor view models

• Adds settled-part state and contributor projections with Unicode-safe initials, avatar metadata, and relative activity text. Link cards are generalized for pull requests and issues.

src/Capacitor.App/ViewModels/WorkContextItems.cs

WorkContextViewModel.Projections.csProject canonical work-item details into the pane +124/-16

Project canonical work-item details into the pane

• Projects item identity, key, title, overview, state, settled parts, issue, contributors, and session counts. Separates item and topology retention and uses the served identifier to preserve absorbed-item projections across polls.

src/Capacitor.App/ViewModels/WorkContextViewModel.Projections.cs

WorkContextViewModel.csInject time provider for contributor activity +3/-1

Inject time provider for contributor activity

• Stores the existing TimeProvider for deterministic relative-time projections and simplifies the no-repository message.

src/Capacitor.App/ViewModels/WorkContextViewModel.cs

WorkContextView.axamlRender live work-item state and participation details +115/-50

Render live work-item state and participation details

• Replaces placeholder pills with state styling, settled progress, overview, issue cards, contributor initials, activity rows, and session counts. Reuses a shared link-card template for pull requests and issues.

src/Capacitor.App/Views/WorkContextView.axaml

Models.csRegister work-item JSON source generation +1/-0

Register work-item JSON source generation

• Adds WorkItemDto to the generated serialization context used by the HTTP client.

src/Capacitor.Cli.Core/Models.cs

IWorkContextChannel.csAdd canonical work-item channel operation +2/-1

Add canonical work-item channel operation

• Extends the testable work-context transport seam with a work-item-by-ID read.

src/Capacitor.Cli.Core/WorkItems/IWorkContextChannel.cs

WorkContextClient.csCall the canonical work-item endpoint +5/-0

Call the canonical work-item endpoint

• Adds validated and escaped GET requests to '/api/work-items/{id}' using generated JSON metadata.

src/Capacitor.Cli.Core/WorkItems/WorkContextClient.cs

WorkContextReader.csFetch item and topology concurrently +18/-10

Fetch item and topology concurrently

• Starts canonical item and topology reads together after assignment resolution and carries item data through the totalized result. Item authorization and plan-gate failures affect the whole read, while other failures degrade only item-backed sections.

src/Capacitor.Cli.Core/WorkItems/WorkContextReader.cs

WorkItemDtos.csModel canonical work-item responses +71/-0

Model canonical work-item responses

• Defines null-tolerant DTOs for item identity, key, state, links, parts, contributors, and visible session counts.

src/Capacitor.Cli.Core/WorkItems/WorkItemDtos.cs

Tests (6) +430 / -29
RelativeTimeTests.csCover relative activity formatting boundaries +31/-0

Cover relative activity formatting boundaries

• Tests minute, hour, day, week, year, and future-timestamp formatting behavior.

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

WorkContextViewModelTests.csCover canonical item projection and retention +264/-25

Cover canonical item projection and retention

• Expands view-model coverage for settled parts, fallback labels, transient failures, absorbed identities, overview suppression, state pills, issue selection, contributors, and projection clearing.

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

WorkspaceViewSmokeTests.csValidate new pane controls resolve +3/-3

Validate new pane controls resolve

• Updates the workspace smoke test to locate state, overview, issue, contributor, session-count, and requester controls.

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

WorkContextClientTests.csTest canonical work-item HTTP requests +17/-0

Test canonical work-item HTTP requests

• Verifies route construction, response parsing, identifier refusal, and path-segment escaping for the new client operation.

test/Capacitor.Cli.Core.Tests.Unit/WorkItems/WorkContextClientTests.cs

WorkContextDtoTests.csTest work-item response deserialization +56/-0

Test work-item response deserialization

• Covers the complete server response shape, forward-compatible extra fields, null collections, and generated serialization metadata.

test/Capacitor.Cli.Core.Tests.Unit/WorkItems/WorkContextDtoTests.cs

WorkContextReaderTests.csTest item-read orchestration and failures +59/-1

Test item-read orchestration and failures

• Verifies item and topology concurrency, no-primary behavior, item degradation, plan gating, and sign-out classification for the added request.

test/Capacitor.Cli.Core.Tests.Unit/WorkItems/WorkContextReaderTests.cs

Documentation (1) +40 / -0
CHANGES.mdDocument the canonical work-item pane behavior +40/-0

Document the canonical work-item pane behavior

• Explains the new endpoint, projection ownership, absorbed-item identity handling, issue filtering, contributor rendering, and fallback behavior.

docs/CHANGES.md

@qodo-code-review

qodo-code-review Bot commented Sep 5, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

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

Grey Divider


Remediation recommended

1. Avatar updates remain stale ✓ Resolved 🐞 Bug ≡ Correctness
Description
ApplyItem considers contributor rows unchanged when only AvatarUrl changes, so Replace retains
the old view model across polls. The public Contributors projection therefore exposes a stale
avatar URL despite receiving the updated endpoint value.
Code

src/Capacitor.App/ViewModels/WorkContextViewModel.Projections.cs[213]

+        Replace(_contributors, people, c => (c.Name, c.LastActivityText));
Relevance

●●● Strong

Recent accepted correctness findings in the same App area support fixing stale projections caused by
incomplete change keys.

PR-#766

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Each poll creates contributors with the latest avatar URL, but the replacement key contains only
name and formatted activity. Since Replace skips rebuilding equal-key sequences and AvatarUrl is
immutable on the retained model, an avatar-only server update cannot reach the public projection.

src/Capacitor.App/ViewModels/WorkContextViewModel.Projections.cs[209-214]
src/Capacitor.App/ViewModels/WorkContextViewModel.Projections.cs[265-270]
src/Capacitor.App/ViewModels/WorkContextItems.cs[40-52]

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

## Issue description
Contributor projection replacement ignores `AvatarUrl`, causing updated avatar values to be discarded when the contributor name and formatted activity text remain unchanged.

## Issue Context
`WorkContextPersonViewModel` publicly carries an immutable `AvatarUrl`. `Replace` retains existing objects whenever its selected keys are equal, so all externally observable immutable fields must participate in the comparison.

## Fix Focus Areas
- src/Capacitor.App/ViewModels/WorkContextViewModel.Projections.cs[209-214]
- src/Capacitor.App/ViewModels/WorkContextItems.cs[40-52]
- test/Capacitor.App.Tests.Unit/WorkContextViewModelTests.cs[699-724]

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


Grey Divider

Context sources
✅ Compliance rules (platform): 58 rules
Review mode: 🧠 Deep: This is a broad, behavior-changing desktop/API integration spanning reader concurrency, DTO deserialization, identity transitions, view-model projections, UI markup, and multiple fallback/error paths, creating many independent opportunities for subtle defects.

Grey Divider

Tip of the day
💡 Did you know, you can show, collapse, or hide each part of a finding: code, evidence, and all

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/Capacitor.App/ViewModels/WorkContextViewModel.Projections.cs Outdated
alexeyzimarev and others added 2 commits September 5, 2026 17:47
Rows are kept across polls while their key matches, so an avatar-only change never reached the pane. The test fixtures also drop the Linear-shaped keys the CI lint refuses in C# source.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <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.

Desktop work-context pane: read the work item from GET /api/work-items/{id}

1 participant