Segment/Folder Unification Design
JIRA: DEVAI-417Date: 2025-11-17 Status: In Design → In Progress Author: Claude Code (with user guidance)
Problem Statement
Currently, tdx has separate commands for segments and folders, but folders are containers for segments (similar to directories in a file system). The current command structure requires users to think of these as separate entities, which creates a fragmented user experience when navigating hierarchical segment structures.
Current limitations:
tdx segmentslists only child segments, not folders- Separate commands for folder operations (
segment folder list,segment folder show) - No support for nested folder paths (only
parent/childformat) - No context/navigation system for working within a specific folder
- No way to view folder hierarchy (tree view)
Design Goals
- Unified navigation model - Treat folders and segments as a cohesive hierarchy
- Context-aware operations - Support working within a specific folder context
- Path-based addressing - Support multi-level paths like
parent/folder1/folder2/item - Backward compatible where reasonable - Maintain existing workflows where possible
- Intuitive UX - Similar to file system navigation patterns users already know
User Requirements (from discussion)
✅ tdx segments should show both child segments AND folders ✅ tdx segments -r for recursive listing of nested hierarchy ✅ tdx segment use <path> for context switching (not top-level cd) ✅ After setting context, tdx segments lists the context folder contents ✅ All operations support paths: tdx segment <command> /path/to/item ✅ Auto-detect whether path is folder or segment ✅ NO top-level navigation commands (cd/ls/show) - keep everything under segment namespace
Data Model
Current Hierarchy (3 levels)
Parent Segment (Audience)
├── Folders (organizational containers)
│ └── Folders (support nesting via parentFolderId)
└── Child Segments (filtered subsets with rules)
└── Can be placed in folders via segmentFolderIdKey Relationships
- Parents contain Folders and Child Segments
- Folders can contain Folders (nesting) and Segments
- Segments reference their folder via
segmentFolderId - Both folders and segments belong to a parent via
audienceId
Proposed Command Structure
Navigation & Listing
# List contents (folders + segments) in current context
tdx segments # Non-recursive (current level only)
tdx segments -r # Recursive (show full hierarchy)
# Context management
tdx segment use <path> # Switch context to parent or folder
tdx use # Show current contextResource Operations (path-aware)
# Auto-detect type from path
tdx segment show <path>
tdx segment create <path>
tdx segment update <path>
tdx segment delete <path>
tdx segment move <from> <to>
tdx segment rename <path> <new-name>
# Segment-specific (only work on segments)
tdx segment sql <path>
tdx segment run <path>
tdx segment fields [parent]
tdx segment activate <path> ...Command Examples
Basic Navigation
# Start at root - list all parents
tdx segments
# Output:
# Parents:
# My Audience (50,000)
# Sales Leads (12,500)
# Switch to a parent
tdx segment use "My Audience"
# Now list shows folders and child segments together
tdx segments
# Output:
# Context: My Audience
#
# Folders:
# 📁 Marketing (5 segments, 2 folders)
# 📁 Sales (3 segments)
#
# Segments:
# 🎯 All Users (50,000)
# 🎯 Active Users (35,000)
# Navigate into folder
tdx segment use "My Audience/Marketing"
# List folder contents
tdx segments
# Output:
# Context: My Audience/Marketing
#
# Folders:
# 📁 Campaigns
# Segments:
# 🎯 Email Subscribers (15,000)
# 🎯 High Engagement (8,500)Recursive Listing
# Show full hierarchy from current context
tdx segments -r
# Output:
# Context: My Audience
#
# 📁 Marketing/
# 📁 Campaigns/
# 🎯 Email Q1 (5,234)
# 🎯 Email Q2 (6,122)
# 🎯 Email Subscribers (15,000)
# 🎯 High Engagement (8,500)
# 📁 Sales/
# 🎯 High Value (2,100)
# 🎯 Prospects (4,300)
# 🎯 All Users (50,000)
# 🎯 Active Users (35,000)Path-Based Operations
# Absolute paths (work from any context)
tdx segment show "My Audience/Marketing/Email Subscribers"
tdx segment sql "My Audience/Sales/High Value"
tdx segment delete "My Audience/Marketing/Campaigns/Old Campaign"
# Relative paths (from current context)
tdx segment use "My Audience/Marketing"
tdx segment show "Email Subscribers" # Relative to current context
tdx segment create "New Campaign" --type segment --rule '...'
tdx segment move "Email Subscribers" "../Sales"
# Navigate with relative paths
tdx segment use "Campaigns" # Into subfolder
tdx segment use .. # Up to parent folder
tdx segment use ../Sales # Sibling folder
tdx segment use / # Back to rootType Auto-Detection
# Commands automatically detect if path is folder or segment
tdx segment show "Marketing" # Detects it's a folder
tdx segment show "Email Subscribers" # Detects it's a segment
# If both exist with same name, prompts user:
# Multiple resources found named 'Marketing':
# 1. Folder: Marketing (3 segments, 1 folder)
# 2. Segment: Marketing (population: 1,000)
# Select [1-2]:Implementation Architecture
Phase 1: Path Parser & Infrastructure
New file: src/utils/segment-path-parser.ts
export type SegmentPath = {
segments: string[]; // Path components
isAbsolute: boolean; // Starts with / or explicit parent
raw: string; // Original input
};
export type ResolvedPath = {
parentId: string;
parentName: string;
folderId?: string;
folderPath?: string[]; // ['Marketing', 'Campaigns']
segmentId?: string;
segmentName?: string;
type: 'parent' | 'folder' | 'segment';
};
export function parsePath(path: string): SegmentPath;
export function validatePath(path: SegmentPath): void;
export function joinPaths(base: string, relative: string): string;Update: src/types/cdp.ts - Add path-related types
Phase 2: Context System
New file: src/core/segment-context.ts
export class SegmentContext {
// Get current context for the active site
getCurrentContext(): ResolvedPath | null;
// Set context (persisted to ~/.config/tdx/context.json)
setContext(path: ResolvedPath): void;
// Clear context (back to root)
clearContext(): void;
// Resolve relative path against current context
resolveRelativePath(path: string): string;
}Context file format: ~/.config/tdx/context.json
{
"us01": {
"path": "My Audience/Marketing",
"parentId": "123",
"parentName": "My Audience",
"folderId": "456",
"folderPath": ["Marketing"],
"type": "folder"
},
"jp01": null
}New commands:
src/commands/segment-use-command.ts- Context switchingsrc/commands/segment-pwd-command.ts- Show current location
Phase 3: Type Detection & Path Resolution
Update: src/sdk/segment.ts
export class SegmentAPI {
// Resolve path string to concrete resource
async resolvePathToResource(path: string): Promise<ResolvedPath>;
// Detect if name refers to folder, segment, or both
async detectResourceType(
parentId: string,
name: string
): Promise<'folder' | 'segment' | 'ambiguous'>;
// Traverse nested path: parent → folder1 → folder2 → item
private async traversePath(
pathSegments: string[]
): Promise<ResolvedPath>;
}Detection strategy:
- Try folder API first (lighter weight response)
- If not found, try segment API
- If both exist, return 'ambiguous' and prompt user
- Cache results during session for performance
Phase 4: Unified Segments Command
Update: src/commands/segments.ts (major redesign)
export class SegmentsCommand extends BaseCommand {
// New flag: -r/--recursive
async execute(context, options: { recursive?: boolean }) {
const currentContext = context.segmentContext.getCurrentContext();
if (!currentContext) {
// No context set - list all parents
return this.listParents(context);
}
if (options.recursive) {
// Recursive listing with tree view
return this.listRecursive(context, currentContext);
}
// Non-recursive: folders + segments at current level
return this.listUnified(context, currentContext);
}
private async listUnified(context, resolvedPath): Promise<void>;
private async listRecursive(context, resolvedPath): Promise<void>;
}New utility: src/utils/tree-formatter.ts
- Format hierarchical data as tree
- Icons: 📁 for folders, 🎯 for segments
- Indentation and color coding
Phase 5: Path-Aware Commands
Update existing commands:
segment-command.ts- All operations accept pathssegment-create-command.ts- Path-based creationsegment-update-command.ts- Path-based updates
New commands:
segment-move-command.ts- Move segments between folderssegment-rename-command.ts- Rename folders or segments
Update SDK: src/sdk/segment.ts
async moveSegment(
segmentPath: string,
targetFolderPath: string
): Promise<void>;
async renameResource(
path: string,
newName: string
): Promise<void>;Phase 6: CLI Integration
Update: src/cli.ts
- Register new commands (use, pwd, move, rename)
- Update help text for existing commands
- Add examples showing path syntax
- Remove/deprecate old commands
Phase 7: Testing & Documentation
New tests:
tests/segment-path-parser.test.ts- Path parsing edge casestests/segment-navigation.test.ts- Full navigation workflowstests/segment-recursive.test.ts- Recursive listingtests/segment-context.test.ts- Context persistence
Documentation updates:
README.md- Update segment commands section with examplesSEGMENT_MIGRATION.md- Migration guide for breaking changes
Breaking Changes
Deprecated Commands
| Old Command | New Command |
|---|---|
tdx segments <parent> | tdx segment use <parent> && tdx segments |
tdx segment folder list <parent> | tdx segment use <parent> && tdx segments |
tdx segment folder show <parent> <folder> | tdx segment show <parent>/<folder> |
Changed Behavior
tdx segmentsoutput format- Before: Lists only child segments
- After: Lists both folders and child segments together
Path format
- Before:
parent/child(2 levels only) - After:
parent/folder1/folder2/item(unlimited depth)
- Before:
Context awareness
- Before: No context system
- After: Context maintained across commands via
segment use
Migration Strategy
- Keep old commands working with deprecation warnings
- Provide clear migration guide with examples
- Update examples in documentation
- Add help text explaining new patterns
Technical Considerations
Performance
Caching:
- Cache folder hierarchy during recursive listing
- Cache type detection results during session
- Invalidate cache on create/update/delete operations
API optimization:
- Fetch folders + segments in parallel when listing
- Use pagination for large folder counts
- Limit recursive depth (default max: 10 levels)
Error Handling
Path not found:
- Show fuzzy matches ("Did you mean...?")
- Suggest using
tdx segmentsto see available items
Permission denied:
- Clear error message
- Suggest checking API key permissions
Invalid path syntax:
- Show correct format examples
- Validate path segments (no invalid characters)
Context errors:
- Handle stale context (parent/folder deleted)
- Auto-clear invalid context
- Inform user with helpful message
Edge Cases
- Empty folders: Show in list but indicate empty
- Special characters in names: URL encode properly in API calls
- Very long paths: Truncate in display, show full in JSON output
- Names with slashes: Should be rejected by API (validate client-side)
- Circular references: Shouldn't exist in API, but handle gracefully
- Concurrent modifications: Stale context detection and recovery
Data Structures
Tree Node (for recursive listing)
type TreeNode = {
type: 'folder' | 'segment';
id: string;
name: string;
population?: number;
children?: TreeNode[];
metadata: {
segmentCount?: number;
folderCount?: number;
description?: string;
};
};API Endpoints Used
From existing CDPClient:
GET /entities/parent_segments- List parentsGET /audiences/{parentId}/folders- List foldersGET /entities/folders/{id}- Get folder detailsGET /audiences/{parentId}/segments- List child segmentsGET /audiences/{parentId}/segments/{id}- Get segment detailsPOST /audiences/{parentId}/folders- Create folderPOST /audiences/{parentId}/segments- Create segmentPUT /audiences/{parentId}/segments/{id}- Update segment
Note: Folder nesting is represented by parentFolderId in folder objects.
Success Criteria
✅ tdx segments shows folders + segments together ✅ tdx segments -r shows full nested hierarchy ✅ tdx segment use <path> switches context successfully ✅ tdx use shows current location ✅ All commands accept paths: tdx segment show parent/folder/item ✅ Auto-detection works for folders vs segments ✅ Ambiguous cases prompt user for selection ✅ Relative paths work from context ✅ Move and rename operations function correctly ✅ Context persists across CLI sessions ✅ Backward compatibility maintained where possible ✅ Comprehensive test coverage (unit + integration) ✅ Updated documentation with clear examples
Timeline Estimate
Total: 18-20 days
- Phase 1: Path Parser & Infrastructure (3 days)
- Phase 2: Context System (2-3 days)
- Phase 3: Type Detection & Path Resolution (3 days)
- Phase 4: Unified Segments Command (3 days)
- Phase 5: Path-Aware Commands (4 days)
- Phase 6: CLI Integration (2 days)
- Phase 7: Testing & Documentation (3 days)
Open Questions
- Folder operations: Should we add folder update/delete commands?
- Depth limits: What should be the default max depth for recursive listing?
- Performance: At what hierarchy size should we warn users about performance?
- Aliases: Should we provide shorter aliases (e.g.,
tdx segfortdx segment)? - JSON output: How should recursive listing be formatted in JSON mode?
References
- JIRA: https://treasure-data.atlassian.net/browse/DEVAI-417
- Current implementation:
src/commands/segments.ts,src/commands/segment-command.ts - API documentation: Treasure Data CDP API
- Related work: Context handling system (2025-11-10-unified-context-handling.md)