Skip to content

feat(dbt): manifest.json -> SysGraph (digital-thread box-1 front-end) - #30

Merged
elasticdotventures merged 6 commits into
mainfrom
feat/dbt-manifest-sysgraph
Sep 20, 2026
Merged

elasticdotventures merged 6 commits into
mainfrom
feat/dbt-manifest-sysgraph

Conversation

@elasticdotventures

Copy link
Copy Markdown
Member

Summary

Implements kr0ki's docs/superpowers/specs/2026-09-20-dbt-manifest-digital-thread-design.md (piece 1 of PLAN-KR0KI-006) — a new ufo_types::dbt module that parses a dbt manifest.json artifact and lowers it into ufo_types::sysgraph::SysGraph, the crate's existing canonical UFO semantic graph. Unconditional (no feature flag — no risky dependency, unlike reqif).

Built via subagent-driven development: 4 tasks (parsing, Upgrade impl for dbt's own versioned wire schema, node lowering, edge lowering + entry point), each independently reviewed, plus a final whole-branch review that caught and fixed one real cross-task bug before merge (see below).

Architecture

  • impl Upgrade for DbtManifest — not on SysGraph directly (Rust coherence only allows one impl Upgrade for SysGraph across the whole crate, and Kubernetes/Rust-source front-ends will need that slot too). This is the crate's first real Upgrade consumer.
  • Zero-drift identity: every dbt-derived ElementId is dbt:{unique_id} — no new provenance field added anywhere.
  • parse_and_lift(bytes, config) -> Result<SysGraph, DbtLiftError> is acquisition-neutral — bytes in, no file I/O/network/credentials.
  • No new types added to ontology.rs or sysgraph.rs.

Caught by the final whole-branch review (fixed before this PR)

Node lowering filtered to qualifying resource types (model/seed/snapshot/source); edge lowering didn't apply the same filter — any real dbt manifest with test.* nodes produced dangling edges. Fixed by deriving both passes from one shared qualifying-node set; added a regression fixture shaped like real dbt build output. Also fixed in the same pass: a cargo fmt gap, a documentation pass (this module now matches reqif.rs's doc-comment bar), and the spec's promised debug-only label suffix (disambiguates same-named models/sources by schema).

Test plan

  • cargo test — 263/263 pass
  • cargo clippy --all-targets -- -D warnings — clean
  • cargo fmt --all -- --check — clean

Add DbtManifest, DbtManifestMetadata, DbtNode, and DbtDependsOn structs
with serde deserialization support for parsing dbt manifest.json artifacts.
Includes test fixture validating parsing of metadata, model nodes, and source nodes.
- Add DbtLiftConfig placeholder struct for future configuration options
- Add dbt_element_id() to convert dbt unique_id to ElementId with "dbt:" prefix
- Add lower_nodes() to transform qualifying nodes (models, seeds, snapshots, sources)
  into OntologicalNode entries in a SysGraph with UfoStereotype::Kind("DbtModel")
- Add tests for node lowering and resource type filtering
- Fix import order (UfoStereotype after ontology, before sysgraph)
- Break long dep_exists line to stay within rustfmt's line width
…nodes

C1 (critical): lower_edges walked manifest.nodes unfiltered and checked
dep_exists against the unfiltered maps too, while lower_nodes only lowers
model/seed/snapshot/source resource types. Real manifests have test.* nodes
whose depends_on.nodes point at models, so lower_edges emitted edges from
nodes lower_nodes never created (and, symmetrically, could emit an edge to
a present-but-non-qualifying node like analysis.*) -- SysGraph::dangling_edges
was non-empty on any real-world manifest. Both functions now derive their
node set from a single qualifying_nodes() helper; DanglingDependency still
hard-errors, just against the correct set.

Also, per this review round:
- I1: fixed the two cargo fmt diffs in this file.
- I2: added a REALISTIC_BUILD_FIXTURE (source + dependent model + a test.*
  node depending on the model) and a regression test asserting
  dangling_edges().is_empty() -- the shape every prior fixture lacked.
- I3/I4: documented every public item and field, and expanded the module
  doc comment with the v12-only, dbt: identity, hard-error, and
  acquisition-neutral decisions a consumer needs.
- I5: implemented the spec's promised debug-only label suffix
  (`name (schema)`), which also resolves the FIXTURE's own label collision
  between model.jaffle_shop.customers and source.jaffle_shop.raw.customers
  (both previously labeled "customers").
@elasticdotventures
elasticdotventures merged commit 2deb795 into main Sep 20, 2026
2 checks passed
@elasticdotventures
elasticdotventures deleted the feat/dbt-manifest-sysgraph branch September 20, 2026 13:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant