Skip to content

feat(groups): in-group hierarchy keys (K/J auto-promote + Shift+arrows indent/outdent)#848

Open
AdamiecRadek wants to merge 8 commits intoasheshgoplani:mainfrom
AdamiecRadek:feat/groups-promote-on-boundary
Open

feat(groups): in-group hierarchy keys (K/J auto-promote + Shift+arrows indent/outdent)#848
AdamiecRadek wants to merge 8 commits intoasheshgoplani:mainfrom
AdamiecRadek:feat/groups-promote-on-boundary

Conversation

@AdamiecRadek
Copy link
Copy Markdown
Contributor

@AdamiecRadek AdamiecRadek commented May 1, 2026

Closes #849.

Summary

Adds explicit in-group hierarchy gestures so the cursor's session can change parent without dropping to the CLI:

  • K / J (and Shift+↑ / Shift+↓) now auto-promote a sub-session to top-level when it is the first / last child of its parent. Previously they silently no-op'd at the parent's edge.
  • Shift+→ demotes the cursor's top-level session to a sub-session of the previous top-level peer (last child).
  • Shift+← promotes the cursor's sub-session back to a top-level peer.

All four shortcuts stay scoped to the current group. Cross-group moves remain on M.

Why

Sub-sessions of different parents are interleaved in the visual flat list, and the only way to move a sub-session out of its parent without dropping to the CLI was agent-deck session unset-parent <id> (or M to go to a different group, which is the wrong tool). K/J at the parent boundary was the one ergonomic gap left after #846.

Behaviour

Cursor location K / Shift+↑ J / Shift+↓ Shift+→ Shift+←
Sub-session, first child promote (insert before parent) swap with next sibling no-op (already a sub) promote
Sub-session, last child swap with prev sibling promote (insert after parent's block) no-op promote
Sub-session, middle swap with prev sibling swap with next sibling no-op promote
Top-level, mid-group swap with prev top-level swap with next top-level demote under prev peer no-op
Top-level, first in group no-op (group edge) swap with next top-level no-op (no prev peer) no-op
Top-level, last in group swap with prev top-level no-op (group edge) demote under prev peer no-op

Shift+→ (demote) is a no-op when the source has its own children — single-level nesting is preserved (same invariant session set-parent already enforces).

Implementation

  • MoveSessionUp / MoveSessionDown extended to call inst.ClearParent() and reposition the slice entry when no same-parent sibling exists in the move direction.
  • New GroupTree.PromoteSession(inst) — explicit clear-parent in the same group; same data mutation as session unset-parent.
  • New GroupTree.DemoteSession(inst) — set parent to previous top-level peer, insert at that peer's last-child slot. Mirrors session set-parent validation (no nested sub-sessions, no self-parent, no demote when source has children).
  • home.go binds shift+left and shift+right to the same path as shift+up / shift+down (rebuild flat items, restore cursor, save).

Tests

internal/session/groups_test.go:

  • TestMoveSessionUp_FirstChildPromotes
  • TestMoveSessionDown_LastChildPromotes
  • TestMoveSessionUp_OnlyChildPromotes, TestMoveSessionDown_OnlyChildPromotes
  • TestMoveSessionUp_TopLevelAtFirstNoOp, TestMoveSessionDown_TopLevelAtLastNoOp
  • TestPromoteSession_SubSessionBecomesTopLevel, TestPromoteSession_TopLevelNoOp
  • TestDemoteSession_TopLevelBecomesLastChild
  • TestDemoteSession_FirstTopLevelNoOp
  • TestDemoteSession_SubSessionNoOp
  • TestDemoteSession_WithChildrenNoOp

The existing TestMoveSessionUp_SubSessionAtFirstSiblingNoOp was replaced — its no-op contract is now an auto-promote.

Docs

  • In-app help overlay (?) gets explicit K / Shift+↑ and J / Shift+↓ rows (was a single combined row), plus a new Shift+→/← row. Reorder rows now mention the auto-promote at the parent edge.
  • skills/agent-deck/references/tui-reference.md — same direction split + new indent row.
  • CHANGELOG.md Unreleased / Added.

Test plan

  • go test -count=1 -race -run "TestMoveSession|TestPromoteSession|TestDemoteSession|TestHelpOverlay" ./internal/session/ ./internal/ui/
  • go vet ./internal/session/... ./internal/ui/...
  • Built locally and verified visually — boundary promote, explicit promote, explicit demote, last-child→demote-into-next-peer, first-top-level no-op, last-top-level no-op, demote with children no-op, help overlay layout at terminal width 120.

When a sub-session is the first or last child of its parent, K (move up)
and J (move down) now promote it to top-level instead of silently no-op.
Cross-group moves stay on M.

- K on first child: clear ParentSessionID, reposition the slice entry
  immediately before the parent so the renderer shows it as a top-level
  peer just above the parent block.
- J on last child: clear ParentSessionID and leave the slice entry at
  its current position (already after the parent's children block).
- Top-level at group's first/last position: no-op (no cross-group leak).

Tests cover first-child/last-child promotion, only-child variants on
both ends, and the top-level boundary no-ops.
GroupTree.PromoteSession(inst) converts a sub-session into a top-level
peer in the same group. Same data mutation as the existing CLI
`session unset-parent`, exposed through the GroupTree API so the home
view can bind it to a key (Shift+Left) without reaching into Instance
directly. Top-level sessions are unchanged.
GroupTree.DemoteSession(inst) makes a top-level session a sub-session
of the previous top-level peer in the same group, inserted as that
peer's last child. Mirrors the validation in `session set-parent`:

- No-op if the session is already a sub-session.
- No-op if the session has its own children (single-level nesting only).
- No-op if there is no previous top-level peer in the group (cross-group
  moves stay on M).

The slice entry is repositioned right after the parent's existing last
child so the renderer places it as the parent's last child rather than
somewhere visually unexpected.
Symmetric completion of the Shift+Up / Shift+Down (K/J) reorder pair:

- Shift+Right demotes the cursor's top-level session to a sub-session
  of the previous top-level peer in the same group (last child).
- Shift+Left promotes the cursor's sub-session to a top-level peer in
  the same group.

Both keys delegate to the same primitives the CLI uses for
`session set-parent` / `session unset-parent`, so single-level nesting,
cross-group safety, and child-count guards are enforced uniformly.
Adds a row to the SESSIONS help section so users discover the new
indent/outdent gestures without reading the changelog.
In-app help overlay now spells out that K/J auto-promotes at the parent
edge and that Shift+→ / Shift+← perform indent/outdent. The canonical
TUI reference (skills/agent-deck/references/tui-reference.md) gets the
same two clarifications. README itself is a curated subset and was
already pointing at the TUI reference for the full table.
The combined "Shift+→ / Shift+←" key column overflowed the 14-char
budget and wrapped onto two lines, breaking the table layout. Two
adjustments:

- Split reorder into one row per direction (parallel to the existing
  "j / Down" / "k / Up" rows in NAVIGATION) and surface the
  Shift+↑ / Shift+↓ alternates that the bindings already accept.
- Compact the indent row to "Shift+→/←" so it fits on one line.

TUI reference doc gets the same direction split for consistency.
@AdamiecRadek AdamiecRadek force-pushed the feat/groups-promote-on-boundary branch from d569934 to d669b87 Compare May 6, 2026 13:49
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.

feat: in-group hierarchy keys — auto-promote at parent edge + indent/outdent shortcuts

1 participant