Skip to content

csb tree -- whole-graph fork visualization with per-node stats #31

@djdarcy

Description

@djdarcy

csb tree -- whole-graph fork visualization with per-node stats

Problem

Even after #22 lands (parent/child columns + csb chain <uuid> for a single ancestry walk), there is no command that shows the full forest of fork relationships across all indexed sessions in one view. A user wanting to understand how their sessions evolved over time has to:

  1. Manually pick a session UUID
  2. Run csb chain <uuid> to see that one chain
  3. Repeat for every chain root they suspect exists
  4. Mentally assemble the cross-chain picture

This is fine for "I know which session I want to investigate," but it breaks down for:

  • "Show me every fork that ever happened in this project" (e.g., before pruning)
  • "Which root sessions have the most descendants?" (long-running threads worth preserving)
  • "Did I leave any orphan branches from /branch experiments I abandoned?"
  • "How did this morning's session evolve over the day?"

csb chain is the per-uuid drill-down. The missing complement is the per-project (or per-everything) forest view with per-node detail to scan at a glance.

Concrete current-state example:

$ csb chain dac78227
dac78227-... (3.1 MB, forked 2026-04-30)
   parent: f2d0d074-... (27.4 MB, forked same instant)
   parent: 23f58cd1-... (1.7 MB, forked 2026-02-23)
   root:   e0679878-... (72.4 MB, original)

# Now I have to know that e0679878 has other forks I haven't seen yet.
# No command lists them.

Proposed solution

A new csb tree subcommand that walks the full parent_session_id graph (from #22's schema) and renders an indented tree of every fork relationship. Per-node detail levels mirror csb list / csb scan exactly: bare, -f (level 1), -ff (level 2). Same --shortid, --sort, filter, and scope ergonomics as the rest of csb's session-listing commands.

Default form (csb tree):

e0679878-...  CHROME-PLUGIN__jump-to-my-reply              (72.4 MB)
  └── 23f58cd1-...  CHROME-PLUGIN__jump-to-my-reply         (1.7 MB) forked 2026-02-23
        └── f2d0d074-...  CHROME-PLUGIN__amd-intigriti      (27.4 MB) forked same instant
              └── dac78227-...  AMD_INTIGRITI__reply        (3.1 MB) forked 2026-04-30

a1b2c3d4-...  CSB__plan-restore-shoring-up                  (12.1 MB)
  └── e5f6a7b8-...  CSB__phase-1-byte-pure-restore          (4.3 MB) forked 2026-05-16
  └── d9c0e1f2-...  CSB__phase-2-git-fallback               (2.8 MB) forked 2026-05-16

(orphan root, no children: 47 sessions)

With -f (level 1):

e0679878-...  CHROME-PLUGIN__jump-to-my-reply              (72.4 MB)
                started 2026-03-17 14:22 (purge in 27d)
  └── 23f58cd1-...  CHROME-PLUGIN__jump-to-my-reply         (1.7 MB) forked 2026-02-23
                    started 2026-02-23 09:11 (purge in 4d)
        └── ...

With -ff (level 2):

Same as -f plus the folder list and N messages | vX.Y.Z meta line per node, identical to csb list -ff semantics.

Filtered / scoped:

csb tree                          # full forest, every chain
csb tree <filter>                 # filter by keyword (matches csb list <filter>)
csb tree -d <path>                # only chains where any node touched <path>
                                  # (mirrors csb scan -d's directory-scope semantics)
csb tree --root <uuid>            # single chain rooted at <uuid> (forest of one)
csb tree --orphans                # roots with no children (sessions that never forked)
csb tree --deleted only|all       # include deleted nodes (matches csb list)

Rendering rules

Default to ASCII box-drawing (├──, └──, ) since csb already does Rich rendering elsewhere. JSON output via --json for programmatic consumers (e.g., a future csb view integration -- #14).

Sort within a level (children of the same parent): by forked_at ascending so the temporal evolution reads left-to-right / top-to-bottom. Top-level roots sortable via the standard --sort {last-used,expiration,started,oldest,messages,size} choices that csb list accepts.

Truncation: when a chain exceeds N descendants (configurable; default 50), collapse with ... (M more, see csb tree --root <uuid>). Keeps the full-forest view scannable on machines with deep histories.

Cycle safety: parent pointers should be acyclic by construction, but guard with a visited-set during the walk and emit a warning if a cycle is detected (data corruption signal).

Per-node info levels (mirror csb list / csb scan)

Level Flag Per-node lines
0 (default) One line: <uuid> <name> (<size>) [forked <date>]
1 -f Adds started <date> (purge in Nd) line
2 -ff Adds folder list (start at: <path> + top N other folders + `N messages

Reuses timeline.relative_date / format_timestamp / purge_countdown and the same renderer helpers csb list -f / -ff use. Single source of truth for per-session display across list / scan / search / tree.

Acceptance criteria

  • csb tree command exists, registered in cli.py with _add_common_flags.
  • Walks parent_session_id from the schema added by csb chain-awareness: index parent_session_id from forkedFrom + csb show/chain commands #22 to build a forest in one DB pass.
  • Default output is a forest of ASCII trees, one per root session, sorted by chosen --sort.
  • Children within a parent sort by forked_at ascending.
  • -f / --full-info adds the started + purge-countdown line per node.
  • -ff adds folder list + meta line per node.
  • --shortid / -sid toggles compact UUID display per node (same as csb list).
  • --root <uuid> restricts output to one chain (forest of one).
  • --orphans shows only roots with zero children.
  • -d <path> / -D <path> restrict the forest to chains where any node touched the path (reuses csb scan's scope helpers).
  • Bare filter positional matches by keyword (same vocabulary as csb list <filter>).
  • --deleted [only|all] works the same as csb list.
  • --json emits one JSON object per root with nested children arrays.
  • Cycle guard emits a warning and prunes the cycle rather than infinite-looping.
  • Truncation at >50 descendants per chain with a "see csb tree --root" hint.
  • Tests cover: empty forest, single root + single child, deep chain, multi-root forest, cycle guard, orphan filter, -d scope, --shortid threading, all three info levels.
  • README "Recovery" or new "Lineage" section documents csb tree with example output.

Related issues

Design considerations

  • Why a forest, not a single tree? Most users have many root sessions over time. A single rooted tree would require an artificial root node and would scale poorly.
  • Why per-node stats mirror csb list / scan? The user explicitly asked for "varying degrees of directory information about each -- i.e. our general stats we should for a session in csb list and csb scan". One vocabulary across all surfaces means muscle memory transfers.
  • Why not just extend csb chain? csb chain <uuid> requires a starting UUID. The forest view's value is showing chains the user didn't know about. Different question, different command.
  • Sort vocabulary: reuse csb list --sort choices exactly. Adding a tree-specific --sort fork-depth could be a follow-up if useful.
  • Why not auto-detect chains via hash matching (Strategy 2 in Session fork tracking -- parent/child relationships across branched sessions #15)? Out of scope here. This issue is rendering; detection is Session fork tracking -- parent/child relationships across branched sessions #15. If csb chain-awareness: index parent_session_id from forkedFrom + csb show/chain commands #22's forkedFrom extraction is the only detection strategy live when csb tree ships, the tree shows what forkedFrom knows. Future detection strategies enrich the same view.

Analysis

No prior design doc -- companion planning doc to follow if implementation starts.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions