# feat\(threads\): persist manual active thread order · PR \#9729 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/pull/9729)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

State: merged

Source repository: pingdotgg/t3code

Source branch: sidebar/active-order

Target branch: main

Head commit: d8f4d8673ec546b002ee3776bc91676cf5d895f8

[Discussion and reviews](https://git.cafe/pingdotgg/t3code/pull/9729)

[Commits](https://git.cafe/pingdotgg/t3code/pull/9729/commits?format=markdown)

[Files changed](https://git.cafe/pingdotgg/t3code/pull/9729/diffs?format=markdown)

## Description

---

Active threads always use their default order, so clients cannot save a manual arrangement. This adds a capability-gated `thread.active.reorder` command and persisted fractional order keys, using existing thread metadata events for compatibility with older clients. Reordering leaves activity timestamps unchanged; settling clears the saved active position, while pinning and snoozing preserve it.

Reordering reserves retained keys from hidden rows without writing to those rows, including when a keyless section first receives an arrangement.

Verified with 218 focused server, contract, and client-runtime tests covering command decisions, projection migration and replay, snapshots, key allocation, and ordering. This layer has no visual changes; the dependent client PRs contain the UI evidence.

Prepared with Codex.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches orchestration command handling, event projection, and a DB migration on core thread list state; behavior is heavily tested but incorrect replay or invariant gaps could mis-order threads across clients.
> 
> **Overview**
> Adds **manual ordering for active (non-pinned) threads** behind a new **`threadActiveReorder`** server capability and a **`thread.active.reorder`** command that writes fractional **`activeOrderKey`** values via the existing **`thread.meta-updated`** event (older clients can ignore the new field).
> 
> **Server/orchestration:** The decider rejects reorder on archived, deleted, pinned, or settled threads; reorder does **not** advance thread activity timestamps. **Settling clears** `activeOrderKey`; pin/snooze/metadata upserts **preserve** it. Projection pipeline, snapshots, and SQLite migration **048** add `active_order_key` with replay tests so lifecycle upserts do not drop saved placement.
> 
> **Client-runtime:** Exposes `reorderActiveThread`, shell/detail merge for `activeOrderKey`/`unsettledAt`, reducer/projector parity, **`sortActiveThreadsByOrderKey`** (new/reopened threads ahead of keyed order), and wider **`generateSpreadPinOrderKeys`** for large active lists reusing the pinned reorder planner.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 3c0b9d93d3c9c50933d305c5643f70dda727fcbe. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

<!-- Macroscope's pull request summary starts here -->
<!-- Macroscope will only edit the content between these invisible markers, and the markers themselves will not be visible in the GitHub rendered markdown. -->
<!-- If you delete either of the start / end markers from your PR's description, Macroscope will append its summary at the bottom of the description. -->
> [!NOTE]
> ### Persist manual active thread order across client, server, and projection
> - Adds `ThreadActiveReorderCommand` to the client and orchestration command unions, carrying a thread ID and a trimmed non-empty order key. The server decider emits a `thread.meta-updated` event with the key while preserving the thread's existing activity timestamp; deleted, pinned, and settled threads are rejected.
> - Adds an optional nullable `activeOrderKey` to `OrchestrationThread`, `OrchestrationThreadShell`, and `ThreadMetaUpdatedPayload`. The projector and `applyThreadDetailEvent` initialize it to `null` on creation, clear it on `thread.settled`, and apply it only when present in `thread.meta-updated`.
> - Client runtime gains `reorderActiveThread` in [commands.ts](https://github.com/pingdotgg/t3code/pull/9729/files#diff-ad05547de059ad3e91754003072e879a138860a947ae51d7d6be9221dc3afabd) and a serialized `reorderActive` environment command. `sortActiveThreadsByOrderKey` places keyless threads before keyed ones and applies deterministic thread/environment tie-breakers.
> - `generateSpreadPinOrderKeys` now widens the key space for larger lists. `planPinnedReorder` skips order keys held by hidden rows when inserting or materializing sections.
> - Migration 048 adds the `active_order_key` column to projected threads; the projection repository upserts and reads it. `ServerEnvironment` advertises `threadActiveReorder` in its capability descriptor.
> - Risk: `mergeEnvironmentThread` now takes `activeOrderKey` and `unsettledAt` from shell metadata instead of retaining cached detail values. Existing readers that relied on stale detail values for those fields will see shell-authoritative values.
>
> <!-- Macroscope's review summary starts here -->
>
> <sup><a href="https://app.macroscope.com">Macroscope</a> summarized 1052255.</sup>
> <!-- Macroscope's review summary ends here -->
>
<!-- Macroscope's pull request summary ends here -->
