From 12ce9f3f8edc84064458b649e8615247641dad36 Mon Sep 17 00:00:00 2001 From: Igor Soarez Date: Fri, 23 Oct 2026 17:58:24 +0300 Subject: list working and idle agents most recent first within a status, the menu, panel, search, and `dingger status` listed done and blocked agents oldest first (the queue), and the rest by place. now working and idle agents are most recent first: the first idle row is the agent that finished, or that the user looked at, last. @dingger_since now means when the pane entered its current status, for every status (it was set only while the pane needed the user), and survives a daemon restart. the queue selects done and blocked panes by status, no longer by having a stamp. one ordering rule each in Rust (watcher::list_order) and Swift (listedBefore), with tests that fail on the old order; e2e step 11 makes an agent finish while watched and checks it moves to the top of the idle rows. agents idle since before this change share one stamp and keep their place order until they next change. --- menubar/main.swift | 9 +++++++++ menubar/model.swift | 19 ++++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) (limited to 'menubar') diff --git a/menubar/main.swift b/menubar/main.swift index 0d367b8..17b9fcc 100644 --- a/menubar/main.swift +++ b/menubar/main.swift @@ -388,6 +388,15 @@ if arguments.contains("--selftest") { if Hotkey.parse("cmd+shift+f12")?.display != "⇧⌘F12" { failures.append("hotkey: parse cmd+shift+f12") } if Hotkey.parse(Settings().jumpHotkey)?.display != "⌃⌥⌘U" { failures.append("hotkey: default jump is ⌃⌥⌘U") } if Hotkey.parse(Settings().searchHotkey)?.display != "⌃⌥⇧⌘U" { failures.append("hotkey: default search is ⌃⌥⇧⌘U") } + let order: [(String, Status, Int)] = [ + ("idle old", .idle, 10), ("done new", .done, 90), ("idle unknown", .idle, Int.max), + ("working old", .working, 20), ("idle new", .idle, 80), ("done old", .done, 30), + ("blocked", .blocked, 99), ("working new", .working, 70), + ] + let listed = order.map { Row(paneId: $0.0, agent: "pi", name: $0.0, status: $0.1, session: "s", window: "w", + since: $0.2, path: "~") }.sorted(by: listedBefore).map(\.name) + let expected = ["blocked", "done old", "done new", "working new", "working old", "idle new", "idle old", "idle unknown"] + if listed != expected { failures.append("order: waiting oldest first, the rest most recent first: \(listed)") } failures.forEach { print("FAIL \($0)") } print(failures.isEmpty ? "selftest ok" : "selftest: \(failures.count) failed") exit(failures.isEmpty ? 0 : 1) diff --git a/menubar/model.swift b/menubar/model.swift index da5ad15..a17b009 100644 --- a/menubar/model.swift +++ b/menubar/model.swift @@ -219,7 +219,9 @@ func runDinggerInLoginShell(_ args: [String]) -> (ok: Bool, output: String) { private let home = NSHomeDirectory() -/// Every agent pane, in herdr's priority order, oldest waiting first. +/// Every agent pane: herdr's status priority; within a status, done and +/// blocked oldest first (the queue), working and idle most recent first +/// (`@dingger_since`: when the pane entered its status); then by place. func fetchRows() -> [Row] { let format = [ "#{pane_id}", "#{@agent}", "#{@agent_state}", "#{@dingger_since}", "#{@agent_name}", @@ -236,11 +238,18 @@ func fetchRows() -> [Row] { session: f[5], window: window, since: Int(f[3]) ?? Int.max, path: f[9].hasPrefix(home) ? "~" + f[9].dropFirst(home.count) : f[9]) } - return rows.sorted { - if $0.status.priority != $1.status.priority { return $0.status.priority > $1.status.priority } - if $0.since != $1.since { return $0.since < $1.since } - return $0.location < $1.location + return rows.sorted(by: listedBefore) +} + +/// The list order (see `fetchRows`). +func listedBefore(_ a: Row, _ b: Row) -> Bool { + if a.status.priority != b.status.priority { return a.status.priority > b.status.priority } + if a.since != b.since { + // Int.max: never stamped; last either way. + if a.since == Int.max || b.since == Int.max { return a.since < b.since } + return a.waiting ? a.since < b.since : a.since > b.since } + return a.location < b.location } /// Show the pane and mark it attended; the same code path as the tmux -- cgit v1.2.3