2026 03 21 Git Operations Graph And Components Design
Documentation for Plans
S254: Git Operations — Graph Visualization, Sub-Components & Multi-App Integration
Date: 2026-03-21
Session: 254
Agent: Claude Code (claude-opus-4-6)
Branch: session-252-255 (shared concurrent session)
1. Mission
Build a groundbreaking git graph visualization using a "Metro + Living Intelligence" visual paradigm, extract all UI into fine-grained reusable components within the @libs-structural/git-operations library, and wire the library into gograb-admin and vitto-studio alongside the existing gograb-dev integration.
2. Design Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Visual style | Metro + Living Intelligence | Metro thick lines and station dots for unique aesthetic; living behavior (glow, fade, density) communicates meaning through visual language |
| Orientation | Vertical | Natural page scroll, commit details alongside nodes, works in narrow panels (vitto-studio dock) |
| Interaction | Progressive disclosure | Click = inline expand (diff stats). "Full detail" button = slide-out panel with complete diff and file preview |
| Sub-components | 6 fine-grained + graph as star | Maximum reusability across apps with different layouts |
| Template filtering | Role-gated smart filter | Executive/developer can toggle; admin/user always filtered — platform-wide convention |
| vitto-studio | Studio dock panel | Fits the workspace tool pattern (file tree, module tree), not observatory AI tabs |
| gograb-admin | New "Platform" nav group | No platform section exists yet; create one with git-operations as first entry |
3. Architecture
3.1 Component Tree
All components live in libs/libs-structural/git-operations/src/lib/components/:
components/
├── git-graph/ ★ Star component
│ ├── git-graph.component.ts
│ ├── git-graph.component.html
│ ├── git-graph.component.scss
│ ├── git-graph-lane.util.ts # Pure fn: lane assignment algorithm
│ └── git-graph-renderer.util.ts # Pure fn: SVG path/node generation
├── git-repo-card/
│ ├── git-repo-card.component.ts
│ ├── git-repo-card.component.html
│ └── git-repo-card.component.scss
├── git-branch-sidebar/
│ ├── git-branch-sidebar.component.ts
│ ├── git-branch-sidebar.component.html
│ └── git-branch-sidebar.component.scss
├── git-file-browser/
│ ├── git-file-browser.component.ts
│ ├── git-file-browser.component.html
│ └── git-file-browser.component.scss
├── git-commit-list/
│ ├── git-commit-list.component.ts
│ ├── git-commit-list.component.html
│ └── git-commit-list.component.scss
├── git-server-metrics/
│ ├── git-server-metrics.component.ts
│ ├── git-server-metrics.component.html
│ └── git-server-metrics.component.scss
└── git-commit-detail-panel/
├── git-commit-detail-panel.component.ts
├── git-commit-detail-panel.component.html
└── git-commit-detail-panel.component.scss
3.2 Data Flow
- All components are dumb — they receive data via signal inputs and emit events via output callbacks
- The host page (in each app) injects
GitOperationsStateServiceand wires signals to component inputs - Exception:
git-graphcan accept an optionalstateServiceinput for convenience, since it orchestrates multiple sub-interactions internally
3.3 Component Inputs
git-graph: commits, branches, selectedSha, templateFilter, compact, onCommitClick, onBranchSwitch
git-repo-card: repo, onSelect
git-branch-sidebar: branches, selectedBranch, features, onSwitch, onMerge, onDelete, onCreate
git-file-browser: files, features, searchQuery, onFileSelect
git-commit-list: commits, expandedSha, diff, features, onToggle
git-server-metrics: metrics
git-commit-detail-panel: diff, visible, onClose
4. Git Graph — The Star Component
4.1 Data Model
New types in git-operations.types.ts:
interface GitCommitNode {
// From GitCommitInfo
sha: string;
shortSha: string;
message: string;
author: string;
date: string;
// NEW — from backend enrichment
parentShas: string[];
// Computed by lane algorithm
laneIndex: number;
color: string;
isHead: boolean; // tip of a branch
isMerge: boolean; // parentShas.length > 1
branchName?: string; // if this commit is a branch tip
age: 'recent' | 'active' | 'aging' | 'old';
// Optional enrichment
filesChanged?: number;
insertions?: number;
deletions?: number;
templateOnly?: boolean; // true if commit only touches core libs
}
interface GitGraphLane {
branchName: string;
color: string;
column: number;
active: boolean; // false = stale (dashed, faded)
lastCommitDate: string;
}
4.2 Lane Assignment Algorithm
Pure function in git-graph-lane.util.ts:
- Walk commits top-to-bottom (newest first)
- First commit gets lane 0 (main line)
- When a commit has a parent not in the current lane, fork: assign next available column
- When a merge commit appears (2+ parents), mark the merging lane as "closing" — draw merge path back to target lane
- Reuse closed lane columns to keep the graph narrow
- Staleness: if a lane's last commit is >7 days old (
oldtier), markactive: false. Lanes inagingtier (2d–7d) remainactive: truewith solid lines but their commit nodes render at reduced opacity per the age tier rules. Onlyold(>7d) lanes get the dashed/faded treatment.
4.3 SVG Rendering
Pure functions in git-graph-renderer.util.ts that take GitCommitNode[] and GitGraphLane[] and return SVG element descriptors:
| Element | Rendering |
|---|---|
| Branch lines | <path> with stroke-width: 5, stroke-linecap: round, stroke-linejoin: round (metro thick) |
| Glow filter | <feGaussianBlur> — intensity based on commit age |
| Station dots | Hollow circles: fill: background, stroke: branchColor, stroke-width: 2.5 |
| Merge nodes | Filled ring: outer fill: mergeColor + inner cutout (interchange station) |
| Stale branches | stroke-dasharray: 6 4, reduced opacity (0.25) |
| Merge paths | Bezier curves with Q control points for smooth lane-to-lane bends |
| Fade gradient | Older commits progressively reduce opacity: 1.0 → 0.3 |
| Pill labels | <rect rx="10"> + <text> for branch names at branch tips |
SVG column width: 20px + (laneCount × 28px). Each commit row: 32–48px tall depending on age tier.
4.4 Living Intelligence Rules
| Age Tier | Criteria | Visual Treatment |
|---|---|---|
recent |
< 4 hours | Full glow filter, bright color, rich commit card |
active |
4h – 2 days | Soft glow, normal color, standard row |
aging |
2d – 7d | No glow, reduced opacity (0.7), compact row |
old |
> 7 days | No glow, faded (0.35), minimal row |
Template-aware commits: If templateOnly: true, the commit renders one age tier older (e.g., recent → active). For non-executive/developer roles, template-only commits are hidden entirely.
4.5 Interaction Model
- Click commit node/row → Inline expansion showing diff stats (files, +/−), file list with type icons
- Click "Full detail" button in expanded row →
git-commit-detail-panelslides in from right - Hover commit node → Tooltip with SHA, author, time (from existing commit data — no extra API call)
- Click branch pill label → Switches to that branch (emits
onBranchSwitch) - Compact mode (
[compact]="true"for vitto-studio): Narrower rows, inline expansion only (no slide-out panel)
5. Sub-Components
5.1 git-repo-card
Overview card for a repository in the grid view.
- Inputs:
repo: GitRepoInfo,onSelect: EventEmitter - Shows: Org/repo name, branch count badge, file count badge, last commit message (truncated), last commit time (timeAgo)
- Visual: Card with subtle border, hover lifts with shadow. Org slug as colored dot prefix.
5.2 git-branch-sidebar
Branch list with role-gated management actions.
- Inputs:
branches: GitBranchInfo[],selectedBranch: string,features: GitOpsFeatureFlags,onSwitch,onMerge,onDelete,onCreate - Shows: Scrollable branch list with current-branch indicator, SHA prefix per branch
- Actions (role-gated): Merge to develop, delete, create new branch. Protected branches (main, develop) show lock icon, no delete.
- States: Loading/error for branch actions
5.3 git-file-browser
Grouped file tree with search and template filtering.
- Inputs:
files: GitFileTreeItem[],features: GitOpsFeatureFlags,searchQuery: string,onFileSelect - Shows: Files grouped by top-level directory, collapsible groups, file count per group, file type icons (TS, HTML, SCSS, JSON, etc.)
- Template filter:
features.canViewTemplateSources === false→ auto-filtersTEMPLATE_CORE_PATHS.=== true→ shows toggle ("Show core libraries"), defaults to off.
5.4 git-commit-list
Non-graph commit list for use outside graph context.
- Inputs:
commits: GitCommitInfo[],expandedSha: string | null,diff: GitCommitDiff | null,features: GitOpsFeatureFlags,onToggle - Shows: Commit rows with SHA, message, author, timeAgo. Click toggles inline expansion with diff stats.
- Template awareness: Same rules as file browser — template-only commits dimmed or hidden based on role.
5.5 git-server-metrics
Host metrics strip for Hetzner infrastructure.
- Inputs:
metrics: HetznerHostMetrics - Shows: CPU load, memory usage %, disk %, active VMs, uptime. Compact horizontal strip with icon + value pairs. Color-coded thresholds (green < 60%, yellow 60-80%, red > 80%).
5.6 git-commit-detail-panel
Slide-out detail panel for full commit inspection.
- Inputs:
diff: GitCommitDiff,visible: boolean,onClose - Shows: Full commit info header (SHA, author, date, message), file list with per-file insertion/deletion bars, total stats summary.
- Behavior: Slides in from right, overlay with backdrop. Close via button or backdrop click.
6. Template-Aware Filtering
6.1 Template Core Paths
New file models/git-template-paths.config.ts:
export const TEMPLATE_CORE_PATHS = [
'libs/core/',
'libs/libs-foundational/',
'libs/libs-ui/',
'libs/libs-ui-core/',
'libs/libs-ux/',
'libs/libs-ai/',
'libs/libs-api/',
'libs/libs-dev-tools/',
'libs/libs-private/',
'libs/theme-system/',
] as const;
export function isTemplatePath(filePath: string): boolean {
return TEMPLATE_CORE_PATHS.some(p => filePath.startsWith(p));
}
export function isTemplateOnlyCommit(files: { path: string }[]): boolean {
return files.length > 0 && files.every(f => isTemplatePath(f.path));
}
6.2 Role Gating
New flag in GitOpsFeatureFlags:
canViewTemplateSources: boolean
executive: true
developer: true
admin: false
user: false
Component behavior:
canViewTemplateSources === true→ Show toggle ("Show core libraries"), default off. User can switch.canViewTemplateSources === false→ Always filtered, no toggle visible.
6.3 Platform Convention
This pattern (TEMPLATE_CORE_PATHS + canViewTemplateSources flag) is designed to be promoted to a shared foundational location when other libs that show platform files need it. For S254, it lives in git-operations as the first consumer.
7. App Integration
7.1 gograb-dev (Refactor existing)
- Route:
/platform/git-operations(unchanged) - Change: Refactor monolithic component into thin shell composing library sub-components
- Layout: Overview =
git-server-metrics+git-repo-cardgrid. Detail =git-branch-sidebar(left) + tabbed:git-graph(default tab),git-file-browser,git-commit-list.git-commit-detail-paneloverlays. - Graph becomes default tab in detail view (replacing files)
- Provider: Already configured — no changes
- Role:
developertier
7.2 gograb-admin (New)
- Nav: Add new
platformnav group toadminNavGroupsinadmin-main-shell.component.ts - Route: Add
/platform/git-operationsinapp.routes.ts - Provider: Add
provideGitOperations()toapp.config.ts - Page: Thin component composing
git-repo-cardgrid (overview) +git-graph+git-file-browser(detail). No branch management actions (merge disabled for admin tier). - Role:
admintier —canViewTemplateSources: false, template files always filtered, no toggle - Note: Current
admintier hasshowServerMetrics: true. Update tofalsefor admin since admins don't need infrastructure visibility — server metrics are an executive/developer concern. This change goes ingit-operations-role.config.ts.
7.3 vitto-studio (New dock panel)
- Registration: Add to
STUDIO_DOCK_PANELSinstudio-dock-panels.config.tsunder the existing// === GIT ===section:
Note: Uses{ id: 'studio-git-graph', title: 'Graph', icon: 'commit', component: StudioGitGraphPanelComponent, closable: true, draggable: true, singleton: true, category: 'git' }category: 'git'to match existing git panels (studio-history,studio-branches,studio-diff). - Layout presets: Add
'studio-git-graph'toSTUDIO_CODE_LAYOUTin the git tabs section (lines 123-125, alongsidestudio-branches,studio-diff,studio-history). Not needed inSTUDIO_VISUAL_LAYOUT(visual builders rarely need git details). - New panel wrapper file:
libs/libs-structural/vitto-studio/src/lib/panels/git-graph-panel.component.ts— a thin wrapper component that:- Injects
GitOperationsStateServiceand sets space filter - Gets universe context from route params (same pattern as
studio-dock-shell.component.tslines 135-138: readsspaceSluganduniverseSlugfrom routeparamMap) - Renders
<git-graph [compact]="true">
- Injects
- Provider: Add
provideGitOperations()to studio's app config (via gograb-ai app.config.ts where studio providers are registered) - Behavior: Scoped to current universe's repo, compact mode, inline expansion only
- State service:
GitOperationsStateService.setSpaceFilter()already exists (line 349 in state service) — takes astring | nullfor the space slug
8. Backend Enrichment
8.1 Commit History — Add parentShas
File: APIs/sandbox-orchestrator/src/app/git/git.service.ts (lines 527-551)
Change: The current implementation uses git.log([branch, '--max-count=...']) with default log fields. We need to switch to custom format to include %P (parent SHAs).
Important: simple-git's LogOptions does NOT support { [branch]: true } as a dynamic key for branch filtering. The branch must be passed via the second argument array or customArgs. Keep the existing array-based approach and add --format manually:
// Approach: keep the working array-based git.log() pattern, add --format
const formatStr = '--format={"hash":"%H","shortHash":"%h","message":"%s","author":"%an","date":"%ai","parentHashes":"%P"}';
const result = await git.raw([
'log',
branch,
`--max-count=${limit}`,
formatStr,
]);
// Parse JSON lines (one per commit)
const commits = result
.trim()
.split('\n')
.filter(Boolean)
.map(line => {
const entry = JSON.parse(line);
return {
sha: entry.hash,
shortSha: entry.shortHash,
message: entry.message,
author: entry.author,
date: entry.date,
parentShas: entry.parentHashes ? entry.parentHashes.split(' ').filter(Boolean) : [],
};
});
Impact: Additive change, no breaking changes. Existing consumers ignore the new parentShas field. Uses git.raw() instead of git.log() to avoid simple-git's LogOptions limitations with custom format + branch filtering.
9. Files Summary
New Files (24 source files)
| Path | Purpose |
|---|---|
git-operations/src/lib/components/git-graph/git-graph.component.ts |
Graph container |
git-operations/src/lib/components/git-graph/git-graph.component.html |
Graph template |
git-operations/src/lib/components/git-graph/git-graph.component.scss |
Graph styles |
git-operations/src/lib/components/git-graph/git-graph-lane.util.ts |
Lane algorithm |
git-operations/src/lib/components/git-graph/git-graph-renderer.util.ts |
SVG generation |
git-operations/src/lib/components/git-repo-card/git-repo-card.component.ts/html/scss |
Repo card (3 files) |
git-operations/src/lib/components/git-branch-sidebar/git-branch-sidebar.component.ts/html/scss |
Branch sidebar (3 files) |
git-operations/src/lib/components/git-file-browser/git-file-browser.component.ts/html/scss |
File browser (3 files) |
git-operations/src/lib/components/git-commit-list/git-commit-list.component.ts/html/scss |
Commit list (3 files) |
git-operations/src/lib/components/git-server-metrics/git-server-metrics.component.ts/html/scss |
Metrics strip (3 files) |
git-operations/src/lib/components/git-commit-detail-panel/git-commit-detail-panel.component.ts/html/scss |
Detail panel (3 files) |
git-operations/src/lib/models/git-template-paths.config.ts |
Template path filter |
vitto-studio/src/lib/panels/git-graph-panel.component.ts |
Studio dock panel wrapper |
gograb-admin/.../git-operations/admin-git-operations.component.ts |
Admin page component |
Modified Files (9)
| File | Change |
|---|---|
git-operations/src/lib/models/git-operations.types.ts |
Add GitCommitNode, GitGraphLane, extend GitCommitInfo with parentShas |
git-operations/src/lib/models/git-operations-role.config.ts |
Add canViewTemplateSources flag to all tiers; set admin.showServerMetrics: false |
git-operations/src/index.ts |
Export new components, template config, new types |
git-operations/src/lib/services/git-operations-api.service.ts |
Update getCommitHistory return type to include parentShas |
APIs/sandbox-orchestrator/src/app/git/git.service.ts |
Add %P custom format for parent SHAs |
apps/gograb-dev/.../git-operations.component.ts/html/scss |
Refactor to compose library sub-components |
apps/gograb-admin/src/app/app.config.ts + app.routes.ts + admin-main-shell.component.ts |
Add provider, route, nav group |
apps/gograb-ai/src/app/app.config.ts (studio providers) |
Add provideGitOperations() |
libs/libs-structural/vitto-studio/src/lib/config/studio-dock-panels.config.ts |
Register studio-git-graph dock panel |
10. Edge Cases
- Root commit: Has no parents —
parentShas: []. Lane algorithm treats as lane 0 origin. - Octopus merges: 3+ parents (rare). Render as multi-merge interchange station, connecting all source lanes.
- Protected branches: main, develop — lock icon, no delete action. Backend guard already exists.
- Empty repos: No commits — graph shows "No commits yet" placeholder.
- 100+ commits: Use Angular CDK
ScrollingModule(cdk-virtual-scroll-viewport) for virtual scrolling. Each commit row is a fixed-height virtual scroll item (32–48px depending on age tier). Living intelligence naturally compresses old commits visually. - 3-level repo paths:
parseRepoPathtakes last 2 segments (existing fix from S251). - Stale branches >7d: Dashed lines, 0.25 opacity. Visible but clearly secondary.
- Template-only repos: If ALL files are template, the file browser shows "Only core library files" message with toggle hint (if role permits).
11. Implementation Ordering
The following dependency order must be followed:
- Models first: Update
git-operations.types.ts(addGitCommitNode,GitGraphLane,parentShasonGitCommitInfo) andgit-operations-role.config.ts(addcanViewTemplateSources, changeadmin.showServerMetrics). Creategit-template-paths.config.ts. All components depend on these types compiling. - API service: Update
git-operations-api.service.tsreturn type forgetCommitHistory. - Backend: Update
sandbox-orchestrator/git.service.tsto returnparentShas. - Pure utilities:
git-graph-lane.util.tsandgit-graph-renderer.util.ts— no Angular dependencies, highly testable. - Sub-components: Can be built in parallel —
git-repo-card,git-branch-sidebar,git-file-browser,git-commit-list,git-server-metrics,git-commit-detail-panel. - Star component:
git-graph— depends on lane util, renderer util, and the commit detail panel. - Index exports: Update
src/index.tsto export all new components, types (GitCommitNode,GitGraphLane), and template config utils (isTemplatePath,isTemplateOnlyCommit,TEMPLATE_CORE_PATHS). - App integrations: gograb-dev refactor, gograb-admin page + nav + route, vitto-studio dock panel. These depend on all library components being built.
12. Diff Stats Loading Strategy
Commit diff stats (filesChanged, insertions, deletions, file list) are loaded lazily on click, NOT pre-fetched:
- On commit click (inline expand): Call
getCommitDiff(org, repo, sha)— same existing endpoint. Cache the result in the state service'scommitDiffsignal. - On hover (tooltip): Show only data already available from the commit history response (
sha,author,date,message). No API call on hover — avoids N+1 problem. - Template-only detection: The
templateOnlyflag onGitCommitNodecan only be computed after the diff is loaded (since we need the file paths). For the initial graph render,templateOnlydefaults tofalse. Once a diff is loaded, the flag is updated and the graph re-renders that node's visual treatment. This means template-only dimming is progressive — it appears as users interact, which aligns with the "living" metaphor.
13. Admin Nav Group Definition
New nav group to add to adminNavGroups in admin-main-shell.component.ts:
{
id: 'platform',
label: 'Platform',
items: [
{ id: 'git-operations', label: 'Git Operations', icon: 'commit', route: '/platform/git-operations' },
],
}
This creates the Platform section in admin sidebar. Future platform tools (deployment status, environment config, etc.) can be added here.
14. Constraints
- Angular 20+ patterns: standalone, signals, OnPush,
inject() - Separate
.ts,.html,.scssfiles — no inline templates - DI tokens for config — never hardcode URLs or API keys
- No git commits during this session (concurrent session conflict avoidance)
TEMPLATE_CORE_PATHSmust stay in sync with the actual platform-base-template structure- Angular CDK
ScrollingModulefor virtual scrolling (already available in the monorepo)