Architecture

How the dippin toolchain is organized — packages, data flow, and design decisions.

Compiler Pipeline

Dippin is a multi-stage compiler pipeline. All downstream consumers program against the canonical IR — a set of Go structs defined in the ir package.

Source File
(.dip or .dot)
Dippin Parser
parser
Canonical IR
ir
Outputs

From the canonical IR, multiple output stages fan out:

IR
Validator / Linter
IR
Formatter
IR
DOT Exporter
IR
Simulator
IR
Cost / Coverage / Doctor

Package Map

dippin-lang/
├── ir/                 # Canonical intermediate representation (types only)
│   ├── ir.go           # Workflow, Node, NodeConfig, RetryConfig, NodeIO
│   ├── edge.go         # Edge, Condition, ConditionExpr
│   ├── source.go       # SourceLocation, SourceMap
│   └── lookup.go       # Helper methods (Node, EdgesFrom, EdgesTo, NodeIDs)
│
├── parser/             # Lexer + recursive descent parser
│   ├── lexer.go        # Indentation-aware tokenizer
│   ├── parser.go       # Produces ir.Workflow from tokens
│   ├── parse_defaults.go  # Defaults block parsing
│   ├── parse_edges.go     # Edge and condition parsing
│   ├── parse_nodes.go     # Node declaration parsing
│   ├── parse_stylesheet.go # Stylesheet section parsing
│   └── parse_helpers.go   # Shared utilities
│
├── validator/          # Graph validation + semantic linting
│   ├── validate.go     # 9 structural checks (DIP001-DIP009)
│   ├── lint.go         # Lint orchestration (DIP101-DIP133)
│   └── diagnostic.go   # Diagnostic type, Result, Severity
│
├── formatter/          # Canonical .dip source formatter
├── export/             # DOT graph exporter
├── migrate/            # DOT ↔ Dippin migration + parity checker
├── simulate/           # Reference graph executor (JSONL events)
├── cost/               # Cost estimation engine + pricing tables
├── coverage/           # Edge coverage analysis
├── doctor/             # Health report card (grade A-F)
├── optimize/           # Model optimization suggestions
├── diff/               # Semantic workflow comparison
├── feedback/           # Cost calibration from telemetry
├── unused/             # Dead-branch detection
├── graph/              # ASCII DAG rendering
├── testrunner/         # Scenario test runner
├── lsp/                # Language Server Protocol server
├── scaffold/           # Template scaffolding for dippin new
└── cmd/dippin/         # CLI entry point + command handlers

Key Design Decisions

IR as the universal interface

Everything flows through ir.Workflow. Packages import ir but not each other (except analysis packages that compose). This decouples parsing from all downstream consumers.

Sealed interfaces

Both NodeConfig and ConditionExpr use Go's sealed interface pattern with unexported methods. Only types within the ir package can implement them, preventing invalid configurations.

No short-circuiting validation

All 9 structural checks and all 30 semantic checks run unconditionally. A single call reports every issue, not just the first. This enables batch fixing.

Zero external dependencies

Core packages depend only on the Go standard library. The ir package imports only time. Only the lsp package uses external libraries (go.lsp.dev for JSON-RPC 2.0).

Idempotent formatter

The formatter is the authority on canonical form. Format(Parse(Format(Parse(source)))) always equals Format(Parse(source)). The fmt --check command uses this property.

Testable CLI

The Run function accepts args []string and io.Writer, making it fully testable without touching os.Args or os.Stdout. All commands are exercised via this interface.

Dependency Graph

The ir package is a leaf dependency. Most packages import only ir. Analysis packages compose each other.

Layer 0: Foundation

ir
event

Layer 1: Direct IR Consumers

parser
formatter
validator
export
migrate
scaffold
simulate
cost
coverage
graph
diff

Layer 2: Composition

doctor
validator + coverage + cost
optimize
cost
unused
coverage + cost
testrunner
simulate
lsp
parser + validator

Layer 3: CLI

cmd/dippin
all packages