Skip to content

Offer only unarchived projects, weightiest first, when creating a task - #168

Merged
MJohnson459 merged 1 commit into
mainfrom
picker-unarchived-weightiest
Aug 14, 2026
Merged

MJohnson459 merged 1 commit into
mainfrom
picker-unarchived-weightiest

Conversation

@MJohnson459

Copy link
Copy Markdown
Contributor

The project picker that opens when creating a task listed every project in the store, archived ones included, ordered alphabetically. Both halves were wrong. Store::create_task refuses an archived project (DESIGN.md §5), so offering one is offering a choice that can only fail — and in the Editor and Plan flows it fails late, after the operator has written the task out in $EDITOR or sat through a planning session. Alphabetical order also says nothing about which project the operator is working on this week; project weight already carries that answer (§7) and is what they edit every morning.

What changed

voro_core::projects_for_new_task (crates/voro-core/src/model.rs) is a pure helper over an already-loaded &[Project]: it drops archived projects and returns the rest weight-descending, name-ascending within a weight. It lives in core because the ordering is business logic, not rendering, and takes a slice rather than issuing a second query because App refreshes self.projects every tick. Store::projects() is untouched and keeps its name ordering — the projects screen, the header, and voro project list all depend on it.

All three create flows route through it. App::creatable_projects wraps the helper, and new_task now branches on the length of the filtered list: one live project beside archived ones goes straight to start_create on the live one rather than opening a picker, and a store whose every project is archived opens no picker and no longer fires NO_PROJECTS_HINT — it leaves a new ALL_PROJECTS_ARCHIVED_HINT status line pointing at the projects screen and the A key that unarchives, in the same no-op-with-an-explanation style as the neighbouring keys. key_pick_project and the Mode::PickProject draw arm both index the filtered list, so sel means the same thing to each; the mouse path needed no change, since clicks route through Hit::PickerOption into the same sel. Each picker row carries its weight ahead of the name, as the projects screen does ( 4 zeta), so weightiest-first reads as an order rather than a shuffle, and the popup widens from 44 to 48 columns to fit it.

Parked projects stay on the list — weight 0 is a snooze, not a retirement (§5) — and simply sort last. The cockpit/task-browser gate that fires while no project is registered still counts all projects, archived included, and the CLI is untouched: voro add names its project explicitly and create_task already refuses an archived one.

DESIGN.md §9 gains a passage recording that the create keys ask which project only when there is a choice, offer only projects that can take the work, and order them weightiest first — enforcement of §5's existing rule rather than a new one. CHANGELOG has an entry under Unreleased/Changed.

Verification

cargo test --workspace — 849 tests, all passing, including new ones: voro-core unit tests that archived projects are excluded at any weight and that ordering is weight-then-name with a parked project kept and sorted last; voro tests that n with one unarchived project beside three archived ones opens the create modal directly on the live one, that n/N with every project archived open no picker and leave the explanatory status, and that ⏎ on the picker's nth row starts the create flow on the project the filtered order puts there rather than the alphabetical one; and a ui.rs render test that the picker shows weights and omits an archived project's name despite it outweighing every row shown.

cargo clippy --workspace --all-targets -- -D warnings and cargo fmt --all clean.

The project picker listed every project alphabetically, archived ones
included. An archived project refuses new work (DESIGN.md §5), so
offering it is offering a choice that can only fail — and in the
$EDITOR and planning flows it fails only after the operator has
written the task out. Alphabetical order also says nothing about which
project the work is in; project weight already carries that answer.

The filtering and ordering live in voro-core as a pure helper over an
already-loaded &[Project], since App refreshes its list every tick and
the ordering policy is business logic rather than rendering.
Store::projects() keeps its name ordering — the projects screen, the
header, and `voro project list` all depend on it.

All three create flows route through the helper: new_task branches on
the filtered length, so one live project beside archived ones skips the
picker, and an all-archived store refuses with a status line pointing at
the projects screen instead of raising an empty picker. The key handler
and the draw arm index the same filtered list, so sel means the same
thing to each, and the mouse path rides Hit::PickerOption into that sel
unchanged. Rows carry the weight they sort on, as the projects screen
does, and the popup widens to 48 columns to fit it.

Verified with cargo test --workspace (unit tests on the helper's
filtering and ordering, TUI tests for the direct-create, all-archived
and nth-row cases, and a render test that the picker shows weights and
omits an archived project), clippy and fmt clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011QKHoB9Afi5zyB4TpqSgQg
@MJohnson459
MJohnson459 merged commit 925ffba into main Aug 14, 2026
7 checks passed
@MJohnson459
MJohnson459 deleted the picker-unarchived-weightiest branch August 14, 2026 14:32
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