Skip to content

[1/7] Data model & core hierarchical grouping algorithm #177

Description

@shouze

Context

groupByTeamPrefix() in src/group.ts is flat and single-level: each prefix in the comma-separated --group-by-team-prefix list is processed sequentially against the remaining repo pool, producing a flat TeamSection[] ({ label, groups }) always ending with an "other" section. TeamSection in src/types.ts has no concept of depth or nesting. This is the foundation every other sub-issue in this EPIC builds on.

Part of EPIC #125.

Solution

  • Redesign TeamSection (src/types.ts) into a tree node, e.g. { label, groups, level, children?: TeamSection[] } (exact shape decided during implementation; keep RepoGroup/sectionLabel round-trip compatible with downstream flatten/rebuild helpers).
  • Add a new grouping entry point in src/group.ts (e.g. groupByTeamHierarchy(groups, chains)) that takes an already-structured input (array of prefix chains, each chain an array of prefix strings representing one level per depth - CLI string parsing itself is issue [6/7]'s responsibility, not this one).
  • Recursively apply the existing single-level bucket-by-count / bucket-by-combo logic (currently inlined in groupByTeamPrefix) at each depth, scoped to the parent level's group.
  • Detect team-name prefix-of relationships among the teams matching a given level (e.g. gamme-lead-client is a prefix of gamme-lead-client-p1) and nest the latter under the former automatically, instead of listing them as unrelated siblings at the same depth.
  • Decide whether groupByTeamPrefix is kept as a degenerate 1-level case or fully replaced; document the decision in the PR (backward compatibility is not required here per EPIC scope).

Example tree shape

graph TD
    R1["gamme-lead-client (level 1)"] --> N1["gamme-lead-client-p1 (nested, same level, auto-detected overlap)"]
    R1 --> N2["squad-dashboard (level 2, chain gamme-/squad-)"]
    R2["gamme-client (level 1)"] --> N3["squad-frontend (level 2)"]
    N3 --> N4["other (level 3, no chapter- match at that node)"]
    R3["chapter-backend (level 1, independent chain)"]
Loading

Acceptance Criteria

  • A 2-level chain (e.g. ["gamme-", "squad-"]) groups repos first by gamme- matches, then sub-groups each resulting bucket by squad- matches.
  • A 3-level chain behaves the same way recursively.
  • Repos matching no prefix at a given level fall into an "other" node at that depth (mirroring today's behavior).
  • Team names that overlap within one level are nested automatically (gamme-lead-client parent, gamme-lead-client-p1 child).
  • Multiple independent prefix chains (comma-separated) are each grouped independently, same as today's multi-prefix behavior.
  • Function is pure (no I/O), matching the codebase's architectural rule.

Definition of Done

  • Full unit test coverage added to src/group.test.ts: 2-level, 3-level, overlapping-name nesting, empty/no-match edge cases.
  • bun test, bun run lint, bun run format:check, bun run knip, bun run build.ts all green.
  • No CLI or rendering wiring in this PR (that's issues 2-6) - this issue only ships the pure data model + algorithm.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions