# Dagshund

Colored diff summaries and interactive DAG visualizations for Databricks Asset Bundles. Requires a plan file from the [direct deployment engine](https://docs.databricks.com/aws/en/dev-tools/bundles/direct) (`databricks bundle plan -o json`). See what changed, what's new, and what's being deleted, in your terminal or in the browser.

![Dagshund resource graph](docs/resources.png)

## Install

Requires Python 3.12+.

```bash
# Run without installing (ephemeral)
uvx dagshund plan.json

# Persistent install
uv tool install dagshund

# Traditional pip
pip install dagshund
```

## Usage

By default, dagshund prints a colored text diff to the terminal:

```bash
dagshund plan.json
```

![Terminal output](docs/terminal.png)

Export an interactive HTML visualization with `-o`:

```bash
dagshund plan.json -o report.html
```

Open it in the browser automatically with `-b`:

```bash
dagshund plan.json -o report.html -b
```

Reads from stdin, so you can pipe directly from the Databricks CLI:

```bash
databricks bundle plan -o json | dagshund
databricks bundle plan -o json | dagshund -o report.html
```

Dagshund works anywhere, it just needs a plan JSON file. You don't need to run it from inside your bundle directory.

Use `-e` for CI-friendly exit codes (see [CI Exit Codes](#ci-exit-codes)).

## Interactive Visualization

The HTML report shows your resources as an interactive graph with diff highlighting. Resources are organized into visual groups:

- **Workspace**, jobs, alerts, experiments, pipelines, and other bundle resources
- **Unity Catalog**, catalogs, schemas, volumes, and registered models in their hierarchy
- **Lakebase**, database instances and synced tables (when present, flat resources move into an "Other Resources" group)

Click any node to open a detail panel with per-field structural diffs, old values in red, new values in green, unchanged fields for context.

![Structural diff detail panel](docs/schem_detail.png)

Jobs with task dependencies get their own DAG view. Switch between the Resources and Jobs tabs to navigate between them.

![Job task DAG](docs/dag.png)

### Phantom Nodes

If your schema references a parent catalog that isn't in your bundle, dagshund infers it and adds it to the graph as a **phantom node** (shown with a dashed border). Hierarchy phantoms like these always display because they hold the tree together. Inferred leaf nodes (like a warehouse referenced by an alert) can be toggled on or off with the **Inferred leaf nodes** button in the toolbar.

![Phantom nodes](docs/phantom_node.png)

### Lateral Dependencies

Many resources reference each other across hierarchies, an alert might target a SQL warehouse, or a serving endpoint might bind to a registered model. These relationships are hidden by default to keep the graph clean. Toggle **Lateral dependencies** in the toolbar to see how your resources connect across group boundaries.

![Lateral dependencies](docs/lateral_dependencies.png)

### Search

The search bar filters nodes by name or type. Non-matching nodes dim so matches stand out.

- Type a name to filter: `warehouse`, `analytics`
- Wrap in quotes for exact match: `"analytics"` finds only that node, not `analytics_pipeline`
- Prefix with `type:` to filter by badge: `type:wheel` highlights all wheel tasks
- Prefix with `status:` to filter by diff status: `status:added`, `status:modified`, `status:removed`
- Press **Escape** to clear

The diff filter buttons (**Added**, **Modified**, **Removed**) compose with search, when both are active, only nodes matching both criteria stay highlighted. When exactly one node matches, the viewport auto-centers on it.

## CI Exit Codes

Use `--detailed-exitcode` (or `-e`) for machine-readable exit codes in CI pipelines:

```bash
dagshund plan.json -e
```

| Exit Code | Meaning |
|-----------|---------|
| 0 | Plan parsed, no changes detected |
| 1 | Error (bad input, missing file, etc.) |
| 2 | Plan parsed, changes detected |

```bash
# Check for drift
dagshund plan.json -e
if [ $? -eq 2 ]; then echo "Drift detected"; fi

# Generate report AND get exit code
dagshund plan.json -o report.html -e
```

Without `-e`, dagshund always exits 0 on success.

## Contributing

Dagshund is a solo project and I'm not accepting pull requests at this time. If you run into a bug or have a feature request, please open an issue, I'm happy to hear what you need.

## Development

Requires [Bun](https://bun.sh) 1.3+, [uv](https://docs.astral.sh/uv/), and [just](https://just.systems/).

```bash
just install        # Install dependencies
just dev            # Dev server with hot reload (http://localhost:3000)
just build          # JS template + Python wheel
just check          # Lint + typecheck + all tests
```

For [Claude Code](https://docs.anthropic.com/en/docs/claude-code) LSP support (go-to-definition, type hover, find-references), install the language server binaries:

```bash
bun install -g pyright typescript-language-server typescript
```

The plugins are already configured in `.claude/settings.json`.

## License

MIT
