Skip to content

feat(sessions): --weight sort override to pin deck/tab order (resubmit of #52)#62

Draft
doug-w wants to merge 2 commits into
puritysb:masterfrom
doug-w:feat/session-sort-weight
Draft

feat(sessions): --weight sort override to pin deck/tab order (resubmit of #52)#62
doug-w wants to merge 2 commits into
puritysb:masterfrom
doug-w:feat/session-sort-weight

Conversation

@doug-w

@doug-w doug-w commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Resubmits the session-weight feature from #52, rebased onto current master with the requested changes applied. #52 was closed as stale/conflicted with CI failing on omitted generated protocol artifacts; this branch fixes all three concerns.

What this adds

A --weight <n> flag on the session commands (claude, codex, opencode, monitor) that pins where a session lands in the Stream Deck deck — and in every other dashboard surface. Running several tabs on one project (Windows Terminal / iTerm), you tag each tab with a weight and the deck mirrors your tab order:

agentdeck claude --weight 1   # tab 1 → deck slot 1
agentdeck claude --weight 2   # tab 2 → deck slot 2

sortSessions gains a new primary key: weight ascending (negatives → unweighted/0 → positives), then the existing agentType → project → startedAt → id keys unchanged within a weight band. --weight is integer-validated (--weight foo fails loudly). Weight defaults to 0; leaving every session unweighted reproduces the pre-weight ordering byte-for-byte. Weight is a pure sort key — no behavioral change.

Changes vs #52 (addresses the close reasons)

  • Rebased onto current master — resolved the bridge/src/cli.ts conflict (master removed the d200h module key; kept master's module set and threaded weight). No other conflicts.
  • pnpm generate-protocol output committed — the CI "Protocol drift check" now passes. Regenerated generated/protocol/BridgeEvent.swift, BridgeEvent.kt, and bridge-event-schema.json add the weight field.
  • Integer-compatible wire value confirmed — the generator represents JSON numbers as Double, so the reference artifacts show weight: Double?. The consumed hand-written models use Int? (SessionInfo.weight in Swift/Kotlin) and the daemon emits an integer (parseWeight validates as int). JSON 2 decodes cleanly into both Int and Double, and the Swift dict-payload path coerces via NSNumber.intValue. So the reference-vs-consumed type split is intentional and wire-safe.

Local CI parity

Ran the full ci.yml + design-system.yml sequence locally before pushing — all green:

  • pnpm verify-version
  • pnpm build ✓ · pnpm typecheck
  • pnpm test1702 passed
  • npx vitest run --coverage — thresholds cleared (lines 42.4%, branches 37.8%, functions 39%, statements 40.9%)
  • Protocol drift checkpnpm generate-protocol produces no diff ✓ (the feat(sessions): --weight sort override to pin deck/tab order #52 blocker)
  • Token mirror sync + design-lint vs baseline ✓ (89 ≤ 93)

Data flow

CLI --weight → SessionOptions.weight → SessionEntry.weight (sessions.json)
  → EnrichedSession.weight → SessionInfo.weight (wire) → sortSessions / compareEntries

Cross-platform parity (hand-mirrored SSOT)

SSOT in shared/src/session-utils.ts (sessionWeight() + weight as key 0 in sortSessions), hand-mirrored in bridge (parseWeight flag on all four commands + registry/aggregator plumbing), Apple (DashboardDataRules.sessionWeight, all sortSessions overloads incl. dict path, daemon wire emit), and Android (sessionWeight + compareSessionsForDisplay, e-ink EinkAgentColumn, tablet SessionListPanel). Stream Deck plugin + TUI inherit via shared sortSessions.

Tests

  • shared (vitest) — 9 cases: band ordering, sessionWeight normalization (null/NaN/Infinity → 0), weight-beats-agentType, within-band fallback, tab→slot mapping, no-weight == legacy ordering
  • Apple (XCTest) — 2 mirror tests in ProtocolTests.swift
  • Android (JUnit) — 3 mirror tests in SessionDisplayOrderingTest.kt

Apple/Android native builds are out of scope on this dev box; those ports are hand-mirrored and covered by mirror tests. CI validates compilation.

🤖 Generated with Claude Code

Adds an explicit per-session sort weight so users can map their terminal
tab order (Windows Terminal / iTerm) onto the Stream Deck and every other
dashboard surface.

Session commands (claude/codex/opencode/monitor) accept `--weight <n>`
(any integer, default 0). sortSessions now orders by weight ascending
FIRST — negatives, then unweighted/0, then positives — and only falls
back to the existing agentType -> project -> startedAt -> id ordering
WITHIN the same weight band. When no session sets a weight, every session
shares weight 0 and the ordering is byte-for-byte identical to before, so
this is fully backward compatible.

Threads CLI --weight -> SessionOptions.weight -> SessionEntry.weight
(persisted in sessions.json) -> SessionInfo.weight (wire) -> every
consumer. Ported to all surfaces off the shared SSOT:

- shared: sessionWeight() helper + weight key in sortSessions; SessionInfo.weight
- bridge: --weight flag (integer-validated) on all session commands,
  registry + aggregator plumbing
- Stream Deck plugin + TUI: inherit via shared sortSessions (no change)
- Apple (macOS/iOS): DashboardDataRules.sessionWeight + weight in all three
  sortSessions overloads, DaemonSessionEntry.weight, daemon wire payload,
  SessionListPanel sortEntries
- Android: SessionInfo.weight, sessionWeight + compareSessionsForDisplay,
  EinkAgentColumn + monitor SessionListPanel compareEntries

Tests: 9 shared (vitest), 2 Apple (XCTest), 3 Android (JUnit) mirroring
the same weight ordering + backward-compat contract. Docs: README
"Pinning session order with --weight" + CLAUDE.md CLI note.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@puritysb

Copy link
Copy Markdown
Owner

Thanks for resubmitting this with the generated protocol artifacts and green CI. GitHub currently reports the PR as mergeable: false, mergeable_state: dirty, and rebaseable: false against the latest master, which has advanced since this branch was prepared. Please rebase/recreate the branch on current puritysb/AgentDeck master, resolve the conflicts without dropping the recent daemon/protocol/platform changes, and rerun the full CI matrix. I will do the substantive weight-ordering review on the conflict-free head so we do not review code that cannot be merged as submitted.

…eight

# Conflicts:
#	generated/protocol/BridgeEvent.kt
#	generated/protocol/BridgeEvent.swift

@puritysb puritysb left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for updating the branch. The new head is clean/mergeable and all four CI checks pass. I completed the substantive review and found several cross-surface gaps that need to be fixed before merge:

  1. Weight is lost on the Swift-daemon discovery path. startSession() persists weight in the Node SessionEntry, but DaemonWsClient does not include it in session_push_register, and Swift handleSessionPushRegister neither reads nor preserves it. The Swift daemon cannot read the CLI process's ~/.agentdeck/sessions.json; its own comment identifies this push registration as the sole discovery path. Consequently --weight does not reach the App Store macOS/iOS dashboards when the built-in Swift daemon is active, despite the added Swift model/sort fields. Please thread weight through the register frame and preserve it during Swift re-register/state entry reconstruction, with a regression test for the Node-session → Swift-daemon shape.

  2. Weighted Codex tabs are folded before they can be sorted. Stream Deck and D200H call foldCodexSessionsForDisplay() before sortSessions(). The fold key is only Codex kind + project, so two same-project Codex tabs with weights 1 and 2 collapse to one representative whose weight can change with state. I reproduced this in a focused test: expected [codex-tab-1, codex-tab-2], actual [codex-tab-2]; the existing 34 session-utils tests still pass. Explicitly distinct weights need to participate in the fold key or otherwise opt those sessions out of folding, mirrored in the Swift folding implementation/preview. Add a regression covering two weighted same-project Codex sessions and state changes.

  3. The CLI accepts integers that are not wire-safe and the native comparators can overflow. Number.isInteger(Number(value)) accepts values such as 1e100 and 9007199254740992; even 2147483648 cannot fit the Kotlin Int? model. Kotlin and Swift also subtract user-controlled weights, so opposite/extreme values can overflow (Swift may trap; Kotlin can reverse ordering). Please enforce a documented cross-platform range in parseWeight, use direct/three-way comparison instead of subtraction in TS/Swift/Kotlin, and test both bounds plus opposite-sign extremes.

  4. Required mirrors/docs are incomplete. D200HLayoutModel.D200HSession and its hand-ported sortSessions still have no weight, so Device Preview no longer mirrors the shared layout ordering. Because this adds behavior to App Store dashboard surfaces, add the required docs/appstore-feature-matrix.md row, and record the structural CLI→registry→wire→surface change in DEVELOPMENT_LOG.md rather than relying only on the CLAUDE/README text.

Verification at b27731d: shared/bridge TypeScript typechecks passed; existing session-utils tests passed 34/34; the focused weighted-Codex-fold regression failed as described; GitHub CI is green.

@puritysb puritysb added changes-requested Review feedback must be addressed before merge enhancement New feature or request platform:bridge Node bridge, daemon, hooks, and CLI labels Jul 18, 2026 — with ChatGPT Codex Connector

Copy link
Copy Markdown
Owner

Moving this PR back to Draft to match the unresolved cross-surface contract. The feature remains useful, but the current head now conflicts with master and still needs all four substantive review areas addressed:

  1. carry weight through session_push_register into the Swift daemon and preserve it on re-registration;
  2. prevent distinct weighted same-project Codex sessions from being folded together;
  3. enforce a documented cross-platform integer range and replace subtracting comparators with overflow-safe comparison;
  4. mirror D200H preview ordering and update the App Store feature matrix plus DEVELOPMENT_LOG.

Please rebase after implementing these changes, regenerate protocol mirrors, run the full TypeScript/Apple/Android test matrix, and only then mark ready for review.

@puritysb
puritysb marked this pull request as draft July 18, 2026 06:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changes-requested Review feedback must be addressed before merge enhancement New feature or request platform:bridge Node bridge, daemon, hooks, and CLI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants