Skip to content

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 segments lists only child segments, not folders
  • Separate commands for folder operations (segment folder list, segment folder show)
  • No support for nested folder paths (only parent/child format)
  • No context/navigation system for working within a specific folder
  • No way to view folder hierarchy (tree view)

Design Goals ​

  1. Unified navigation model - Treat folders and segments as a cohesive hierarchy
  2. Context-aware operations - Support working within a specific folder context
  3. Path-based addressing - Support multi-level paths like parent/folder1/folder2/item
  4. Backward compatible where reasonable - Maintain existing workflows where possible
  5. 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 segmentFolderId

Key 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 ​

bash
# 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 context

Resource Operations (path-aware) ​

bash
# 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 ​

bash
# 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 ​

bash
# 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 ​

bash
# 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 root

Type Auto-Detection ​

bash
# 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

typescript
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

typescript
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

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 switching
  • src/commands/segment-pwd-command.ts - Show current location

Phase 3: Type Detection & Path Resolution ​

Update: src/sdk/segment.ts

typescript
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:

  1. Try folder API first (lighter weight response)
  2. If not found, try segment API
  3. If both exist, return 'ambiguous' and prompt user
  4. Cache results during session for performance

Phase 4: Unified Segments Command ​

Update: src/commands/segments.ts (major redesign)

typescript
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 paths
  • segment-create-command.ts - Path-based creation
  • segment-update-command.ts - Path-based updates

New commands:

  • segment-move-command.ts - Move segments between folders
  • segment-rename-command.ts - Rename folders or segments

Update SDK: src/sdk/segment.ts

typescript
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 cases
  • tests/segment-navigation.test.ts - Full navigation workflows
  • tests/segment-recursive.test.ts - Recursive listing
  • tests/segment-context.test.ts - Context persistence

Documentation updates:

  • README.md - Update segment commands section with examples
  • SEGMENT_MIGRATION.md - Migration guide for breaking changes

Breaking Changes ​

Deprecated Commands ​

Old CommandNew 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 ​

  1. tdx segments output format

    • Before: Lists only child segments
    • After: Lists both folders and child segments together
  2. Path format

    • Before: parent/child (2 levels only)
    • After: parent/folder1/folder2/item (unlimited depth)
  3. Context awareness

    • Before: No context system
    • After: Context maintained across commands via segment use

Migration Strategy ​

  1. Keep old commands working with deprecation warnings
  2. Provide clear migration guide with examples
  3. Update examples in documentation
  4. 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 segments to 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) ​

typescript
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 parents
  • GET /audiences/{parentId}/folders - List folders
  • GET /entities/folders/{id} - Get folder details
  • GET /audiences/{parentId}/segments - List child segments
  • GET /audiences/{parentId}/segments/{id} - Get segment details
  • POST /audiences/{parentId}/folders - Create folder
  • POST /audiences/{parentId}/segments - Create segment
  • PUT /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 ​

  1. Folder operations: Should we add folder update/delete commands?
  2. Depth limits: What should be the default max depth for recursive listing?
  3. Performance: At what hierarchy size should we warn users about performance?
  4. Aliases: Should we provide shorter aliases (e.g., tdx seg for tdx segment)?
  5. JSON output: How should recursive listing be formatted in JSON mode?

References ​