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
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.
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.
- Share only the tree walk (parent → children, ordering, fork
detection) and keep two renderers on top. Smaller win, no visual change.
- 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.
sc submitandsc restacknow render their stack through a shared primitive insrc/shortcake/_stack_view.py(build_layout+StackRenderer)._tree.py—used by
sc lsand the web UI — still has its own renderer, so there are twotree layouts in the codebase.
Folding
_tree.pyinto the primitive turned out not to be a mechanicalextraction, 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)lstoday:submittoday:Sharing one layout means picking one of these and changing the other. That's a
UX decision about how
lsshould look, not a refactor — andui.pybuilds onthe same
StackTree, so the web UI changes with it.Options
lsadopts indented arms. One layout everywhere. Changeslsoutput forany forked stack, plus the web UI,
tests/test_tree.pyande2e/docs/21-ls.md. Parallel columns arguably show sibling stacks better,so this is a real loss for wide stacks.
submitadopts parallel columns. Keepslsas-is, but the columns arehard to reconcile with a status column and with viewport windowing, which
assumes one row per branch.
detection) and keep two renderers on top. Smaller win, no visual change.
often.
Option 3 is probably the best value if
lsshould keep its current look.Notes
build_layout; the fork model is thepart that genuinely conflicts.
_tree.py's label content (commit subject, worktreepaths,
⟳ needs restack, PR draft/merged/closed state) is orthogonal and canstay where it is.