How the dippin toolchain is organized — packages, data flow, and design decisions.
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.
parserirFrom the canonical IR, multiple output stages fan out:
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
Everything flows through ir.Workflow. Packages import ir but not each other (except analysis packages that compose). This decouples parsing from all downstream consumers.
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.
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.
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).
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.
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.
The ir package is a leaf dependency. Most packages import only ir. Analysis packages compose each other.