Coalesce DAG Viewer
A lightweight VS Code extension for visualizing a local Coalesce repo as a
DAG, with dbt-style include/exclude node selectors and a side panel for navigation.
Features
- DAG view (
Coalesce: Open DAG View) — a pan/zoom graph of all Coalesce nodes, laid out with
dagre, colored by node type, with edges drawn from the dependencies Coalesce records in each
node's YAML.
- Include / Exclude selectors — filter the graph live using a small dbt-inspired query
language (see below).
- Side panel ("Coalesce" activity bar icon) — two collapsible sections:
- Nodes — a tree of all nodes grouped by type. Click a node to open its YAML; use the inline
actions to reveal it in the DAG or view its ancestors/descendants.
- Subgraphs — lists the named subgraphs saved in the repo's own
subgraphs/**/*.yml files
(i.e. whatever you've grouped into a subgraph in the Coalesce UI). Click one to filter the DAG
down to exactly that saved node set; a chip in the DAG toolbar shows which subgraph is active,
with a button to clear it. Right-click for "Open Subgraph YAML".
- Go to Node (
Coalesce: Go to Node...) — fuzzy-searchable quick pick over all nodes.
- View Subgraph (ad hoc) — right-click a node (in the tree or the graph) to filter the DAG down
to just that node plus all of its ancestors and descendants. This is different from the
Subgraphs panel above: it's a live traversal from a single node, not one of the repo's saved
subgraph definitions.
- Live reload: the graph refreshes automatically when node/subgraph YAML files change on disk.
Selector syntax
Include/exclude fields accept one or more space/comma-separated selectors; results are unioned.
| Selector |
Matches |
ORDERS |
Node named ORDERS (case-insensitive) |
STG_* |
Glob match on node name |
+ORDERS |
ORDERS and all of its ancestors (upstream) |
ORDERS+ |
ORDERS and all of its descendants (downstream) |
+ORDERS+ |
ORDERS plus ancestors and descendants |
2+ORDERS |
ORDERS and ancestors up to 2 hops away |
ORDERS+2 |
ORDERS and descendants up to 2 hops away |
tag:core |
Nodes tagged core |
type:dimension |
Nodes whose node type matches (checks both the display name and the short code) |
location:SAMPLE |
Nodes whose storage location matches |
path:staging/** |
Nodes whose YAML file path (relative to the repo root) matches the glob |
Exclude is applied after include (include - exclude). Leave a field blank to mean "everything"
(include) or "nothing" (exclude).
How the graph is built
This was verified against a real Coalesce repo's node YAML (not just public docs), which turned up a
few surprises worth knowing about:
columns[].columnReference.stepCounter is self-referential — it identifies the column's own
node+column id, not an upstream dependency. It is not used for lineage.
- The real per-column lineage lives at
columns[].sourceColumnReferences[].columnReferences[].stepCounter,
pointing at the upstream node's id. A stepCounter of "0" (or an all-zero UUID) is Coalesce's
sentinel for "no source" on derived/system columns (e.g. transform: CURRENT_TIMESTAMP()) and is
ignored rather than treated as a broken reference.
- The top-level
type field is a structural constant (always "Node"), not the node's semantic
type. The real type is operation.sqlType (e.g. Source, Stage, Dimension, Fact).
- Multi-source nodes declare their dependencies explicitly and by name at
operation.metadata.sourceMapping[].dependencies[].nodeName. This is the most authoritative edge
source (labeled source in the parsed graph) and is checked first.
- Join/SQL templates reference other nodes via
{{ ref('LOCATION', 'NODE_NAME') }} (two-arg, unlike
dbt's single-arg ref('name') — both forms are handled) or ref_no_link(...) when a reference is
intentionally excluded from lineage (e.g. a postSQL cleanup statement). The whole document is
scanned for ref(...) calls as a fallback/supplement (labeled ref) for dependencies that don't
show up as structured sourceMapping or column lineage — ref_no_link(...) is correctly not
matched.
So three dependency signals are combined, in priority order: sourceMapping.dependencies (by name),
column-level sourceColumnReferences (by id), then a ref() text scan (by name) as a catch-all.
Node type (for coloring/grouping) is resolved from operation.sqlType against node type definitions
found at nodeTypes/**/definition.yml, if that folder exists in the repo (it's optional — real repos
may not check it in). The match key is each definition's top-level id — for built-in types this is
the human name ("Dimension"), but for custom/marketplace types it can be an opaque id (e.g. "4"),
which is exactly what operation.sqlType stores on the node instance. The actually-useful fields
(capitalized, short, tagColor) aren't top-level on the definition file — they're nested inside
metadata.nodeMetadataSpec, itself a YAML string that has to be parsed a second time. If nothing
matches, the node still gets a color hashed from its raw type string, and files that don't look like
node instances (or node type definitions, or subgraphs) are silently skipped rather than erroring.
Subgraphs (subgraphs/**/*.yml) are simple by comparison: id, name, and a steps array of member
node ids. Steps that don't resolve to a known node are dropped with a warning rather than breaking the
subgraph.
If nothing shows up (or edges look wrong) against your repo: open the Output/console for parse
warnings, and check coalesceViewer.nodesGlob — by default the extension scans
**/nodes/**/*.{yml,yaml}. src/parser.ts is a single, isolated module — if your repo's Coalesce
version uses different key names, that's the only file that needs to change.
Development
npm install
npm run watch # esbuild --watch for both the extension host and webview bundles
Press F5 (Run Coalesce Viewer) to launch an Extension Development Host preloaded against
fixtures/sample-repo, a tiny hand-built Coalesce-shaped repo used for manual testing.
npm run compile # typecheck (extension host + webview, separately since only the
# webview has the DOM lib)
npm run build # production esbuild bundle -> dist/
npm run package # vsce package -> .vsix
Configuration
| Setting |
Default |
Description |
coalesceViewer.nodesGlob |
**/nodes/**/*.{yml,yaml} |
Glob used to discover node files |
coalesceViewer.excludeGlob |
**/{node_modules,.git}/** |
Paths to ignore while scanning |