Skip to content

fix: keep the Work items and Projects selection on the saved item after a reorder - #92

Merged
HemSoft merged 2 commits into
mainfrom
fix/issue-91-selection-follows-saved-item
Nov 7, 2026
Merged

HemSoft merged 2 commits into
mainfrom
fix/issue-91-selection-follows-saved-item

Conversation

@HemSoft

@HemSoft HemSoft commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

Risk level: 2/10 (project-dashboard)
Worked on by: claude-opus-5-5 on laptop, for Chief dispatch 358679b9

Summary

Manage > Work items and Manage > Projects stored the selection as a row number. A save that re-sorted the list left that number in place, so after Back the highlight, the scroll and Enter landed on whichever item now held the old row. The selection now follows the item's identity (work item id, project slug) among the rows returned by a reload or upsert. A Work item that remains beyond the first reloaded page still falls back to another row; that remaining criterion stays with issue #91.

  • dashboard-core/src/selection.rs: reselect, the window-free rule with unit tests. The row that now has the selected identity stays selected. If no row has it, the old position is kept within the new list. An empty list gives row 0, which holds nothing.
  • manage/work_items.rs: a first-page reload under the same filters reselects by id. Under new filters the existing behaviour stays: a filter chip has already reset the selection to row 0, and the clamped position is otherwise kept.
  • manage/projects.rs: upsert (save and re-read) and the list reload reselect by slug among the visible rows. Back now also scrolls to the selection, as Work items already did.

Nothing changes in the write path: same path, same If-Match, still exactly one write per save and no automatic retry. Late-save, generation and version guards, paging, read-only mode and the loading and error states are untouched.

Acceptance Criteria

  • Work items: delivered for an item present in the first reloaded page. The full criterion remains open when a saved item stays beyond that page. Evidence: a_priority_save_that_moves_the_item_keeps_it_selected_after_back (row 2 → 0), a_rank_save_that_moves_the_item_down_keeps_it_selected_after_back (row 0 → 1), a_moved_item_stays_selected_when_the_reload_brings_back_only_the_first_page (row 55 on page 2 → 0 after the first-page reload). Back scrolls to selected, which now holds the new row. Also the capture below.
  • Projects: after a priority save that moves the project, Back highlights it at its new position and Enter reopens it. Evidence: a_priority_save_that_moves_the_project_keeps_it_selected_after_back, and a_priority_changed_elsewhere_keeps_the_project_selected_after_the_conflict for the re-read path. Also the capture below.
  • A save that doesn't change the order keeps the same item selected. Evidence: a_save_that_keeps_the_order_keeps_the_same_row_selected and a_project_save_that_keeps_the_order_keeps_the_same_row_selected.
  • When the saved item is no longer listed, the selection is on an existing row and Enter opens that row's item. Evidence: an_item_that_leaves_the_status_filter_hands_the_selection_to_a_listed_row and an_untracked_project_hidden_from_the_list_hands_the_selection_to_a_listed_row (on ba301aed this one left the selection past the last row, so Enter opened nothing). An empty list opens nothing and writes nothing: when_the_last_item_leaves_the_list_enter_opens_nothing and when_the_last_project_leaves_the_list_enter_opens_nothing.
  • Filter changes still reset the selection to the first row. Evidence: a_filter_change_selects_the_first_row_even_when_the_selected_item_is_still_listed (Work items chip, item still listed) and a_project_filter_change_selects_the_first_row. One note on the issue's "as they do now": the Work items project-slug text field never reset to row 0. It keeps the clamped position, and this PR leaves that as it was.

Definition of Done

  • Fix in manage/work_items.rs and manage/projects.rs; the identity logic is in dashboard-core (selection.rs).
  • Headless UI tests in work_items_tests.rs (7 new) and projects_tests.rs (6 new) cover reorder, item-left-the-list, empty list and filter reset. The Work items fake gained an opt-in in_pmd_order listing (effective priority, rank, then newest, as pmd's read API documents), so the reorder really happens. Existing tests keep insertion order.
  • The new tests fail on ba301aed and pass with the fix. Six fail for the bug: Work items priority, rank and page-2 reorder; Projects priority save, conflict re-read and hidden untracked. The other seven are controls and contract checks (unchanged order, status-filter fallback, empty list, filter reset) and pass on both, as they should.
  • cargo fmt, cargo clippy, cargo test --workspace, cargo deny and standalone core pass locally on Linux (below). Exact-head CI 42323265012 completed successfully: all five required jobs and every executed step pass at d1a076168012dc1968409822b1025d792129e7b2.
  • Full issue completion. This PR is partial delivery; issue Keep the Work items and Projects selection on the saved item after a reorder #91 remains open for selection across reloaded pages.

Validation

Local runs, not CI. Linux runs were on Mini, from a git archive of the exact commit with every one of the 119 files checked against the commit's blob IDs, an isolated CARGO_TARGET_DIR, --offline and CARGO_BUILD_JOBS=4. $D is the scratch folder there.

RED: the new tests on ba301aed production (blobs 8eb9300 for manage/work_items.rs and a7475a0 for core projects.rs). The only additions were the test files and the #[cfg(test)] accessor ProjectsDialog::selected():

  • cd $D/baseline && cargo test -p dashboard-app --locked --offline work_items_tests: 25 passed, 3 failed (priority row 2 left at 2; rank left at 0; page 2 left at clamped 49 instead of 0).
  • cargo test -p dashboard-app --locked --offline projects_tests: 18 passed, 3 failed (priority save left at row 3; conflict left at row 4; hidden untracked left at row 4, past the last of 4 rows).

GREEN at d1a0761, Linux:

  • cargo fmt --all --check: pass.
  • cargo clippy --workspace --all-targets --locked --offline -- -D warnings: pass.
  • cargo test --workspace --locked --offline: pass (dashboard-app 197 passed, 16 ignored; every dashboard-core suite passed, 39 unit tests including 5 for selection).
  • cargo deny --locked check advisories (0.20.2): advisories ok.
  • Standalone core, separate clean target: cargo check -p dashboard-core --locked --offline and cargo test -p dashboard-core --locked --offline: pass.

I used one Cargo test filter per command. The issue's -- work_items_tests projects_tests form passes both names to libtest; I ran each name as its own command instead.

On Windows (this laptop), the same three Work items and three Projects tests fail on ba301aed production. At 729f965, work_items_tests gave 27 passed and 1 failed: the page-2 test's own focus step, which d1a0761 fixes and which then passed on Linux. The Windows Projects GREEN run didn't complete, because a reused scratch build cache failed in libsqlite3-sys. The fresh-target local Windows run was stopped while compiling because its test file was stale; it supplies no final-head Windows result. Exact-head CI 42323265012 passed both rust (windows-latest) and core (windows-latest), which are the Windows evidence for this head.

Screenshots (current head d1a0761)

Rendered offscreen on Linux (Mini, llvmpipe) by CAPTURE_DIR=$D/captures-head cargo test -p dashboard-app --locked --offline selection_after_reorder -- --ignored --test-threads=1, with the new captures_work_selection_after_reorder and captures_projects_selection_after_reorder: 2 passed. Default 1100×760 window, default theme, fixtures only. Each flow is keyboard only: Down to the item, Enter, Tab to P0, Enter, Ctrl+Enter, Tab to Back to list, Enter, then Enter again. Each test asserts the order, the selected row and the single write right after saving each frame.

Work items before the save: #202 is selected at row 5.

Work items list before the save, #202 selected at row 5

Work items after the P0 save and Back: #202 is first and still highlighted.

Work items list after Back, #202 highlighted at its new first row

Enter reopens #202.

Work items editor reopened on #202 by Enter

Projects before the save: gh-x (P3) is selected at row 4.

Projects list before the save, gh-x selected at row 4

Projects after the P0 save and Back: gh-x is first and still highlighted.

Projects list after Back, gh-x highlighted at its new first row

Enter reopens gh-x.

Projects editor reopened on gh-x by Enter

Before the fix, from the same capture tests on ba301aed production, which fail at the after-Back check: the highlight stays at the old row, on #201 and on hs-buddy.

Before the fix: Work items after Back highlights #201 at the old row

Before the fix: Projects after Back highlights hs-buddy at the old row

Recording: the eight current-head frames (Projects 1–4, then Work items 1–4, 1.5 s each). GPUI's headless renderer produces still frames, so this is a frame sequence, not a live screen recording.

selection-flow.mp4

Risks and deferred work

  • SFL Round 1 reports the remaining deep-page case: with more than 50 matching items, saving rank or priority on an item that stays beyond page 1 reloads only page 1, then clamps selection to its last row. The new page-2 test proves movement into page 1 only. This is a non-blocking Low review note, and it leaves the broader issue criterion incomplete. The initiating Chief parent retains the claim and implementation follow-through for that case.

  • The Projects list reload (load_projects) now keeps the selection the same way. It runs on open and on Retry, and no test re-sorts the list during a reload; the upsert paths are tested.

  • Manage > Fleet uses the same positional selection. It is out of scope here, as Keep the Work items and Projects selection on the saved item after a reorder #91 says.

Related to #91

🤖 Generated with Claude Code

HemSoft and others added 2 commits November 6, 2026 19:51
Manage > Work items and Manage > Projects kept the selection as a row
number. A priority or rank save re-sorted the list under it, so after
Back the highlight, the scroll and Enter went to whichever item now held
the old row.

The selection now follows the item's identity (work item id, project
slug) through the Work items reload and the Projects upsert and reload.
When the item is no longer listed, the old position is kept within the
list; an empty list selects nothing. Filter changes still start at the
first row. The identity rule is dashboard_core::selection::reselect.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Load more disappears with the last page and takes the focus with it, so
Tab had nothing to move from. Opening a row and going Back focuses the
list the way the dialog does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@HemSoft HemSoft added the model:claude-opus-5-5 PR authored by claude-opus-5-5 label Nov 7, 2026
@HemSoft

HemSoft commented Nov 7, 2026

Copy link
Copy Markdown
Contributor Author

@sfl review

@hemsoft-sfl-reviewer hemsoft-sfl-reviewer 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.

✅ Verdict: PASS • Round 1

Confidence: 8/10

Review done by claude-opus-5-5 (same family: same-family: codex gated: codex Weekly 164% SUS 61 🔴 pause, defer)

The PR makes the Manage > Work items and Manage > Projects lists keep their selection on the item's identity (id or slug) through reloads and upserts. It does this with a new dashboard_core::selection::reselect helper and adds headless tests and capture tests. I found no Critical, High or Medium defect. The one Low finding is that a reorder save on an item past the first page still loses the selection, and it does not block approval.

🔨 Full-Spectrum Review Summary

Effort: high
Review time: 2m 10s
Token count: Unavailable

Pass 🔴 Critical 🟠 High 🟡 Medium ⚪ Low
🔒 Security 0 0 0 0
✅ Accuracy 0 0 0 1
🧰 Quality & Maintainability 0 0 0 0
Total 0 0 0 1

Key Findings

  1. ⚪ LOW — Accuracy: A reorder save on an item that stays past page 1 still moves the selection to another item. crates/dashboard-app/src/manage/work_items.rs:291: Trigger: the list has more than 50 matching items and Load more has been used. The user opens an item past the first page and saves a rank or priority change that leaves it past page 1. For example, with 62 items at P2 rank 0, row 55 is set to rank 5. A reorder save resets the list to the first page (load_work(id, true, ..) at line 504), so reselect does not find the saved id and clamps the selection to row 49. Impact: after Back, the highlight is on a different item and Enter opens that item. That is the #91 wrong-target symptom, now limited to deep-page items. The behaviour predates this PR, and the new page-2 test covers only an item that moves into page 1. The PR presents this case as the documented 'no longer listed' fallback, and it causes no wrong write. (non-blocking)

Coverage

  • Files reviewed:
    • crates/dashboard-core/src/selection.rs
    • crates/dashboard-core/src/lib.rs
    • crates/dashboard-app/src/manage/work_items.rs
    • crates/dashboard-app/src/manage/projects.rs
    • crates/dashboard-app/src/work_items_tests.rs
    • crates/dashboard-app/src/projects_tests.rs
    • crates/dashboard-app/src/captures.rs
  • Context read:
    • Issue #91 body (acceptance criteria)
    • PR #92 body
  • Verified:
    • The compare's base_commit.sha is ba301aed65e9e3d29eab99510c4bf9e3de41fc53 and its last commit is d1a076168012dc1968409822b1025d792129e7b2, matching the PR's base.sha and head.sha; the head is ahead by 2 and behind by 0
    • reselect returns the first index that matches the identity, otherwise old clamped to len-1, and 0 for an empty list, which matches its doc comment and unit tests
    • In the Work items reload, previous is taken only when rows_filter == filter and only on the first-page (cursor.is_none()) branch; the Load more branch and the generation guard are unchanged
    • set_work_filter and the Projects filter observer and toggle_untracked still set selected = 0, so filter resets are kept
    • Projects keep_selection reads the previous slug from visible(cx) before the change and reselects among the visible rows after it; it is used in upsert (save and re-read paths) and in the load_projects success path
    • leave_project_editor now calls scroll_to_item(selected); the scroll container's children are exactly the visible rows, so the index matches, as move_in_projects already assumed
    • open_work_item and open_project_editor set selected to the opened row, so after Back the selection identifies the edited item
    • The write paths, the If-Match precondition and the single-write behaviour are not changed by the diff
    • Every FakeWork fixture sets priority, so the Option<u64> sort key in in_pmd_order never meets None
    • The new capture tests are #[ignore] and need CAPTURE_DIR, so they stay out of default runs
  • Not verified:
    • Runtime and GUI behaviour, and the CI results the PR body reports: execution is forbidden
    • Projects load_projects reselection during a re-sort: no test exercises it, and I checked it by reading only
    • Whether reopen_created_project's filter clear, through the deferred observer, later resets the selection: this is existing code outside the diff
    • The screenshots and recording linked in the PR body were not opened
    • Manage > Fleet positional selection: out of scope according to #91
  • Tests run: None. Reviewer execution is disabled.

HemSoft full-spectrum review • 3 evidence-based passes • Reviewed head d1a076168012dc1968409822b1025d792129e7b2 • SFL v0

@HemSoft
HemSoft merged commit f24f8b9 into main Nov 7, 2026
5 checks passed
@HemSoft
HemSoft deleted the fix/issue-91-selection-follows-saved-item branch November 7, 2026 01:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

model:claude-opus-5-5 PR authored by claude-opus-5-5

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant