Architecture Data Flow - aku11i/phantom GitHub Wiki
This document illustrates how data moves through Phantom's system, from user input to final output.
Data flows through Phantom in a predictable, unidirectional manner:
graph LR
A[User Input] --> B[CLI Parser]
B --> C[Command Handler]
C --> D[Core Logic]
D --> E[Git Operations]
E --> F[System Calls]
F --> G[Results]
G --> H[Output Formatting]
H --> I[User Output]
style A fill:#9f9
style I fill:#9f9
style D fill:#ff9
style E fill:#f9f
Example: phantom create feature-xyz origin/main
sequenceDiagram
participant User
participant CLI
participant CreateHandler
participant WorktreeCore
participant GitModule
participant FileSystem
User->>CLI: phantom create feature-xyz origin/main
CLI->>CreateHandler: {name: "feature-xyz", branch: "origin/main"}
CreateHandler->>WorktreeCore: validateWorktreeName("feature-xyz")
WorktreeCore-->>CreateHandler: Result.ok()
CreateHandler->>GitModule: getGitRoot()
GitModule->>FileSystem: git rev-parse --show-toplevel
FileSystem-->>GitModule: "/path/to/repo"
GitModule-->>CreateHandler: Result.ok("/path/to/repo")
CreateHandler->>GitModule: branchExists("origin/main")
GitModule->>FileSystem: git rev-parse --verify
FileSystem-->>GitModule: "commit-sha"
GitModule-->>CreateHandler: Result.ok(true)
CreateHandler->>WorktreeCore: createWorktree("feature-xyz", "origin/main")
WorktreeCore->>GitModule: addWorktree(name, branch, path)
GitModule->>FileSystem: git worktree add
FileSystem-->>GitModule: success
GitModule-->>WorktreeCore: Result.ok(WorktreeInfo)
WorktreeCore-->>CreateHandler: Result.ok(WorktreeInfo)
CreateHandler->>CLI: formatSuccess(WorktreeInfo)
CLI->>User: "Created phantom 'feature-xyz' at /tmp/phantom/repo/feature-xyz"
Example: phantom list
sequenceDiagram
participant User
participant CLI
participant ListHandler
participant WorktreeCore
participant GitModule
participant OutputFormatter
User->>CLI: phantom list
CLI->>ListHandler: {}
ListHandler->>WorktreeCore: listWorktrees()
WorktreeCore->>GitModule: listWorktrees()
GitModule->>GitModule: git worktree list --porcelain
GitModule-->>WorktreeCore: Result.ok(GitWorktreeData[])
WorktreeCore->>WorktreeCore: Transform to WorktreeInfo[]
WorktreeCore-->>ListHandler: Result.ok(WorktreeInfo[])
ListHandler->>OutputFormatter: formatTable(worktrees)
OutputFormatter-->>ListHandler: Formatted table string
ListHandler->>CLI: Output table
CLI->>User: Display formatted table
graph TB
A[Raw CLI Args] --> B[Parsed Arguments]
B --> C[Validated Input]
C --> D[Domain Objects]
subgraph "CLI Layer"
A
B
end
subgraph "Handler Layer"
C
end
subgraph "Core Layer"
D
end
Example Transformations:
// Raw input
["create", "my-feature", "origin/main"]
// Parsed arguments
{
command: "create",
args: ["my-feature", "origin/main"],
flags: {}
}
// Validated input
{
name: "my-feature",
branch: "origin/main"
}
// Domain object
{
name: "my-feature",
branch: "origin/main",
path: "/tmp/phantom/repo/my-feature"
}
graph LR
A[Git CLI Output] --> B[Raw String]
B --> C[Parsed Data]
C --> D[Domain Model]
D --> E[Result Type]
style A fill:#f9f
style E fill:#9f9
Example: Worktree List Parsing
// Git output (porcelain format)
`worktree /path/to/main
HEAD abc123
branch refs/heads/main
worktree /tmp/phantom/repo/feature
HEAD def456
branch refs/heads/feature`
// Parsed data
[
{
path: "/path/to/main",
head: "abc123",
branch: "refs/heads/main"
},
{
path: "/tmp/phantom/repo/feature",
head: "def456",
branch: "refs/heads/feature"
}
]
// Domain model
[
{
name: "main",
path: "/path/to/main",
branch: "main",
isMain: true
},
{
name: "feature",
path: "/tmp/phantom/repo/feature",
branch: "feature",
isMain: false
}
]
graph TB
A[System Error] --> B[Git Error]
B --> C[Domain Error]
C --> D[User Error]
subgraph "Infrastructure"
A
end
subgraph "Git Module"
B
end
subgraph "Core Module"
C
end
subgraph "CLI Layer"
D
end
style A fill:#f99
style D fill:#fcc
Error Transformation Example:
// System error
Error: Command failed: git worktree add
fatal: 'feature' is already checked out
// Git module error
GitError: Worktree 'feature' already exists
// Domain error
WorktreeError: A phantom named 'feature' already exists
// User message
Error: A phantom named 'feature' already exists.
Try a different name or delete the existing phantom first.
sequenceDiagram
participant Git
participant Core
participant Handler
participant User
Git->>Git: Execute command
alt Success
Git->>Core: Result.ok(data)
Core->>Core: Transform data
Core->>Handler: Result.ok(domainObject)
Handler->>User: Success message
else Failure
Git->>Core: Result.error(gitError)
Core->>Core: Map to domain error
Core->>Handler: Result.error(domainError)
Handler->>User: Error message + help
end
stateDiagram-v2
[*] --> NonExistent
NonExistent --> Creating: create command
Creating --> Active: git worktree add
Active --> Deleting: delete command
Deleting --> NonExistent: git worktree remove
Active --> Active: exec/shell commands
Creating --> NonExistent: error
Deleting --> Active: error
graph LR
A[Idle] --> B[Spawning]
B --> C[Running]
C --> D[Completed]
C --> E[Failed]
D --> F[Exit Code 0]
E --> G[Exit Code > 0]
style A fill:#ccc
style C fill:#9f9
style D fill:#9f9
style E fill:#f99
sequenceDiagram
participant Handler
participant Process
participant Shell
participant Output
Handler->>Process: exec(command)
Process->>Shell: spawn(command)
activate Shell
loop Output Stream
Shell-->>Process: stdout data
Process-->>Handler: stream chunk
Handler-->>Output: display chunk
end
Shell-->>Process: exit(code)
deactivate Shell
Process-->>Handler: Result<ExitCode>
Handler-->>Output: final status
graph TB
A[User Input] --> B{Valid Format?}
B -->|No| C[Format Error]
B -->|Yes| D{Valid Characters?}
D -->|No| E[Character Error]
D -->|Yes| F{Name Available?}
F -->|No| G[Conflict Error]
F -->|Yes| H[Proceed]
C --> I[Error Message]
E --> I
G --> I
style B fill:#ff9
style D fill:#ff9
style F fill:#ff9
style I fill:#f99
style H fill:#9f9
Validation Stages:
-
Format Validation
- Non-empty name
- No path separators
- No special characters
-
Character Validation
- Alphanumeric and hyphens only
- No spaces or dots
- No path traversal
-
Availability Check
- Name not already used
- No conflicts with existing
graph LR
A[Domain Objects] --> B[Extract Fields]
B --> C[Calculate Widths]
C --> D[Format Rows]
D --> E[Add Headers]
E --> F[Render Table]
subgraph "Data Preparation"
A
B
end
subgraph "Layout Calculation"
C
end
subgraph "Rendering"
D
E
F
end
Example: List Output
// Domain objects
[
{ name: "main", branch: "main", path: "/repo" },
{ name: "feature", branch: "feature/new", path: "/tmp/phantom/repo/feature" }
]
// Extracted fields
[
["main", "main", "/repo"],
["feature", "feature/new", "/tmp/phantom/repo/feature"]
]
// Calculated widths
{ name: 7, branch: 11, path: 25 }
// Formatted output
NAME BRANCH PATH
main main /repo
feature feature/new /tmp/phantom/repo/feature
graph TB
A[Minimize Transformations] --> B[Stream When Possible]
B --> C[Cache Expensive Operations]
C --> D[Fail Fast]
E[Git Calls] --> F[Batch Operations]
F --> G[Use Porcelain Format]
H[Output] --> I[Progressive Rendering]
I --> J[Lazy Evaluation]
Optimization Strategies:
-
Stream Processing
- Don't buffer entire output
- Process line by line
- Immediate user feedback
-
Efficient Git Usage
- Use
--porcelain
for parsing - Combine operations when possible
- Cache repository information
- Use
-
Fast Failure
- Validate early
- Check prerequisites first
- Avoid unnecessary work
interface Command {
validate(input: Input): Result<ValidatedInput>
execute(input: ValidatedInput): Result<Output>
format(output: Output): string
}
const pipeline =
parseArgs
.andThen(validate)
.andThen(execute)
.andThen(format)
.mapError(handleError)
spawn(command)
.stdout
.pipe(lineParser)
.pipe(transformer)
.pipe(output)
Phantom's data flow architecture ensures:
- Predictability: Data flows in one direction
- Traceability: Clear transformation points
- Type Safety: Strongly typed at boundaries
- Error Handling: Explicit error propagation
- Performance: Efficient data processing
The unidirectional flow and clear transformation boundaries make the system easy to understand, debug, and extend.