Skip to content

feat(web): optional activity-ordered sidebar - #10

Open
Antisophy wants to merge 1 commit into
CyberShadow:masterfrom
Antisophy:feat/sidebar-recency-order
Open

feat(web): optional activity-ordered sidebar#10
Antisophy wants to merge 1 commit into
CyberShadow:masterfrom
Antisophy:feat/sidebar-recency-order

Conversation

@Antisophy

Copy link
Copy Markdown
Contributor

Adds a sidebar_sort_by_recency option, off by default. Enabling it orders the sidebar by activity rather than creation: the most recently worked-on task sits at the top and the order updates as tasks are used.

  • A parent rises with its most recently active descendant, at any depth, so a task never sits stale while work happens beneath it.
  • Siblings re-sort among themselves and nothing is reparented, so the tree structure is unchanged.
  • Archive and Import move below the live tasks; New Task moves to the top.

Why a new timestamp

Ordering needs to know when a task was last worked on, and neither existing signal answers that.

last_active is cleared when a session starts, for crash recovery, and recovered from the transcript's mtime. The startup sweep resumes every in-flight session and each resume appends to its transcript, so one restart rewrites every mtime and collapses all tasks onto a single timestamp. Ordering on it produces essentially the order the sweep happened to touch things.

A resume writes session and meta records rather than a conversation turn, so the newest user/assistant record in the transcript is unaffected by it. last_turn_at holds that value.

Reading it

ae.workflow.history.last_turn scans the tail of the transcript rather than the whole file, since transcripts reach tens of megabytes and this runs per task at startup. The window grows if the tail contains no turn, so a repeatedly-resumed but unused session still resolves instead of reporting nothing.

Claude and Codex record shapes are both recognized. An unrecognized shape yields no value and that task falls back to its creation time, which is also what happens for a task that has never run, so a fresh draft sorts near the top rather than the floor.

The value is persisted but treated as a cache: recomputed from the transcript at every startup, so a stale entry cannot survive until the task is next used. Between startups touchTask maintains it, which fires on message-send and turn-completion and never on session lifecycle.

Default off

With the option unset, the creation-ordered list, the Archive and Import placement, and the New Task position are all exactly as before. The sort, the group placement and the New Task move are each gated on it.

Tests

Sidebar.test.ts covers both modes: unchanged order when off, recency order when on, a parent raised by a deep descendant, siblings re-sorting without reparenting, and the group placement in each mode. last_turn.d unit tests cover a transcript whose newest records are the meta ones a resume writes, a transcript with no turns at all, malformed lines, a missing file, the Codex shape, and a turn buried past the first scan window.

The hand-rolled scan was also checked against 9,599 real transcript lines across several Claude sessions and a Codex rollout, agreeing with a full JSON parse on every line, including that it reads each record's own timestamp rather than one nested in a payload.

nix flake check: full check set built. Five integration checks failed under parallel load and passed when re-run serially; a previous run of the same set failed a disjoint five, so they look load-related rather than related to this change.

Adds sidebar_sort_by_recency, off by default. With it enabled the sidebar
is ordered by activity rather than creation: the most recently worked-on
task sits at the top and the order updates as tasks are used. A parent
rises with its most recently active descendant at any depth, so a task
does not sit stale above or below work happening beneath it. Siblings
re-sort among themselves and nothing is reparented, leaving the tree
structure untouched. Archive and Import move below the live tasks, and
New Task moves to the top.

Ordering needs a timestamp for when a task was last worked on, and
neither existing signal answers that. last_active is cleared when a
session starts, for crash recovery, and recovered from the transcript's
mtime; the startup sweep resumes every in-flight session and each resume
appends to its transcript, so a single restart rewrites every mtime and
collapses all tasks onto one timestamp.

A resume writes session and meta records rather than a conversation turn,
so the newest user/assistant record in the transcript is unaffected by
it. last_turn_at holds that value, read from the tail of the file rather
than the whole thing since transcripts can reach tens of megabytes and
this runs per task at startup. The scan window grows if the tail holds no
turn, so a repeatedly-resumed but unused session still resolves. Claude
and Codex transcript shapes are both recognized; an unrecognized shape
yields no value and the task falls back to its creation time.

last_turn_at is persisted but treated as a cache, recomputed from the
transcript at every startup so a stale value cannot persist until the
task is next used. Between startups touchTask maintains it, which fires
on message-send and turn-completion and never on session lifecycle.

Default-off means the creation-ordered list, the Archive and Import
placement and the New Task position are all unchanged unless the option
is set.
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