Skip to content

list: a plugin-declared order per type, next's filters, and --paginate (nxf 6j6v.n698) - #47

Closed
cabcookie wants to merge 2 commits into
mainfrom
feat/n698-list-ranking
Closed

cabcookie wants to merge 2 commits into
mainfrom
feat/n698-list-ranking

Conversation

@cabcookie

Copy link
Copy Markdown
Member

Stacked on #46 (q29p): this branch builds on its shared paging helpers (Paging, Watermark, page_through). Merge #46 first; until then the diff here includes #46's commit.

Third cut of kfs7. Owner decision 2026-10-09: long-lived objects that are not tasks (persons, say) are ranked through nxf list, never through next.

Plugin declaration

[ranking.next]
order = [ { field = "status", precedence = ["in_progress", "open"] }, { field = "priority", dir = "asc" } ]

[ranking.list]
types = ["person", "project"]          # cross-type order, as for next
# order = [ ... ]                      # optional; defaults to the [ranking.next] default order
[ranking.list.person]
order = [ { field = "title", dir = "asc" } ]
[ranking.list.project]
order = [ { field = "priority", dir = "desc" } ]
  • The key language, the per-type tables, types, and the load-time rules are the same as z9jk's [ranking.next]. The same code parses and validates both; errors name [ranking.list…].
  • Ranking::list is always resolved at load. Without [ranking.list], or without its own order, the list default order is the [ranking.next] default order. That is exactly what list --sort rank ranked by before, so nothing changes for a plugin that declares nothing, both bundled plugins included.

Behaviour

  • The default list order stays id. The plugin order is selected with the existing --sort rank flag; no new sort key. The code comment on rank_cmp had already reserved this for n698.
  • Mixed types work as in z9jk: items of different types compare by their position in types (or in the default order's first precedence key on type). Unlisted types come after the listed ones, ordered by type name. Within one type, items compare by that type's own order (or the default order), then by id.
  • blocked and the lane verbs' --sort rank are unchanged: they use the [ranking.next] default order.

API (facade: changed, additive)

  • read::ListQuery (non_exhaustive builder: new/status/sort/filter/limit/paginate/token).
  • read::list_page(&PluginConfig, &Store, &ListQuery) -> Result<NextPage>.
  • Engine::list_query(&ListQuery) -> Result<NextPage>, Engine::list_query_value(&ListQuery) -> Result<Value>.
  • plugin::Ranking::list: RankSpec (new field; Ranking is non_exhaustive).
  • Internal: NextRank is now TypeRank over a &RankSpec, with TypeRank::next(cfg) and TypeRank::list(cfg). It is the one comparator for both verbs, so derived keys (x0wf) plug in once.

CLI

nxf list [--status S] [--type T]... [--in ID] [--label L] [--sort rank|id] [--limit N [--paginate [--token T]]]

  • --json without --limit: the same plain array as before.
  • With --limit: {"items": [...], "total": n}.
  • With --paginate, two more keys: next_token (null on the last page) and restarted.
  • The human view prints showing n of total and a next page: --paginate --token … line.

Paging uses next's machine-local cache, with the same expiry, caps and invalidation. The snapshot key names the read ("list"), the status, sort and filter, and a fingerprint of [ranking.list], so tokens never cross between next, mentioned_by and list, or between plugin orders.

Tests

  • crates/facade/tests/list_query.rs: 14 tests at the Engine::list_query seam, using two fixture plugins (per-type list order, and none).
  • plugin::tests: 5 new load/validation tests.
  • crates/cli/tests/list.rs: 5 new black-box tests.
  • Goldens under both plugins, guide EN/DE, docs/generated.

Run locally (all green): facade lib, list_query, next_rank_per_type, mentioned_by, read, engine, external_plugin_registration; cli list, golden, guide_examples, verb_seam, mention, next, sort, parity; cargo xtask docs check; cargo xtask changelog check origin/main; cargo fmt --all --check. Clippy is left to CI.

Not in this PR

  • The MCP flow_list tool did not gain the filters or paging; flow_next has not got them yet either. flow_list's sort: rank does use [ranking.list], because it goes through read::list.

🤖 Generated with Claude Code

https://claude.ai/code/session_016q5uMUkZJQW4fckJoA3FME

cabcookie and others added 2 commits October 11, 2026 01:47
…v.q29p)

- Engine::mentions(id): the outgoing targets, the existing read::mentions put on the seam.
- Engine::mentioned_by(id, &MentionedByQuery) / mentioned_by_value: the live items that mention
  a target, in id order, filtered by NextFilter (type, container, label) and cut/paged exactly
  like next_query.
- read::mentioners_of(store, targets): THE bulk reverse lookup (over the new
  Store::mentioned_by_bulk) that the derived ranking key count = "mentioned_by" (x0wf) is to
  share, per docs/specs/derived-ranking-keys.md section 11.
- The paging half of next_page is factored into page_through/Paging/Watermark, so next and
  mentioned_by share one snapshot table, expiry, caps and invalidation; the snapshot key names
  the read, so tokens do not cross between them. next's keys are unchanged.
- CLI: nxf mention by <id> [--type]... [--in] [--label] [--limit [--paginate [--token]]], with
  list records and next's --json envelope; goldens under both plugins, guide EN/DE, generated
  docs, verb-seam alias.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016q5uMUkZJQW4fckJoA3FME
…e (nxf 6j6v.n698)

- Plugin: [ranking.list] takes the same keys as [ranking.next]: per-type tables
  [ranking.list.<type>], the cross-type order types = [...], an optional default order.
  Parsed and validated by the same code (errors name their section). Ranking::list is
  always resolved at load: absent, or without its own order, it inherits the
  [ranking.next] default order, so list --sort rank is unchanged for every plugin that
  declares nothing (both bundled plugins).
- Ranking: NextRank becomes TypeRank over a &RankSpec; next ranks with
  TypeRank::next, list --sort rank with TypeRank::list - one comparator for both, the
  place derived keys (x0wf) plug in once.
- Paging: read::ListQuery + read::list_page reuse NextFilter (type alternatives,
  container = belongs_to OR contributes_to, label) and q29p's Paging / Watermark /
  page_through; the snapshot key names the read ("list"), status, sort, filter and a
  fingerprint of [ranking.list], so tokens never cross reads or plugin orders.
- Engine::list_query(&ListQuery) -> NextPage and Engine::list_query_value (the
  {"items","total"[,"next_token","restarted"]} envelope of list_value records).
- CLI: nxf list --type (repeatable) --in <id> --limit --paginate --token; plain array
  without --limit, next's envelope with it; human view discloses the cut and prints
  the token. Default order stays id.
- Tests at the seam (facade/tests/list_query.rs, plugin unit tests), CLI black-box
  (cli/tests/list.rs), goldens under both plugins, guide EN/DE, generated docs.

Stacked on #46 (q29p).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016q5uMUkZJQW4fckJoA3FME
cabcookie added a commit that referenced this pull request Oct 11, 2026
…paging, local derived ranking keys (nxf 6j6v.q29p, n698, x0wf) (#49)

Folds #46 (q29p), #47 (n698) and #48 (x0wf) plus the mitigation of their combined independent review.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016q5uMUkZJQW4fckJoA3FME
@cabcookie

Copy link
Copy Markdown
Member Author

Contained in #49, which folds #46, #47 and #48 plus the review mitigation and was merged in one squash.

@cabcookie cabcookie closed this Oct 11, 2026
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