Skip to content

Unify the ls/ui tree renderer with the shared stack primitive #132

Description

@patrick91

sc submit and sc restack now render their stack through a shared primitive in
src/shortcake/_stack_view.py (build_layout + StackRenderer). _tree.py
used by sc ls and the web UI — still has its own renderer, so there are two
tree layouts in the codebase.

Folding _tree.py into the primitive turned out not to be a mechanical
extraction, which is why it was left out of the original change.

Why it's blocked

The two renderers use genuinely different visual models:

_tree.py (ls, ui) _stack_view.py (submit, restack)
Direction bottom-up (leaf first, trunk last) top-down (base first)
Forks parallel columns + merge connectors indented arms
Content commit subject, worktree paths, warnings, PR state status column

ls today:

│ │ ◯ add-world-module
│ │ │ Add world module
│ │ │
◯─┴─┘ add-hello-module
│     Add hello module
│
◯ main
  Initial commit

submit today:

  ● add-github-repository-analysis-service            #4672
  │
  ├─● analyze-repositories-in-github-repository-list  #4673
  │ │
  │ ● expose-github-repository-analysis-api           #4674
  │
  └─● 2026-07-24-add-github-rate-limit-aware-client   #4675

Sharing one layout means picking one of these and changing the other. That's a
UX decision about how ls should look, not a refactor — and ui.py builds on
the same StackTree, so the web UI changes with it.

Options

  1. ls adopts indented arms. One layout everywhere. Changes ls output for
    any forked stack, plus the web UI, tests/test_tree.py and
    e2e/docs/21-ls.md. Parallel columns arguably show sibling stacks better,
    so this is a real loss for wide stacks.
  2. submit adopts parallel columns. Keeps ls as-is, but the columns are
    hard to reconcile with a status column and with viewport windowing, which
    assumes one row per branch.
  3. Share only the tree walk (parent → children, ordering, fork
    detection) and keep two renderers on top. Smaller win, no visual change.
  4. Leave both. The duplication is ~80 lines and neither renderer changes
    often.

Option 3 is probably the best value if ls should keep its current look.

Notes

  • Direction alone could be a parameter of build_layout; the fork model is the
    part that genuinely conflicts.
  • Whatever is chosen, _tree.py's label content (commit subject, worktree
    paths, ⟳ needs restack, PR draft/merged/closed state) is orthogonal and can
    stay where it is.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions