Repository navigation
Feat semantic compare merge - #52
Merged
Merged
Conversation
The top bar crammed brand, search, an unbounded subject-area legend, counts, diagnostics, and ten identical Unicode-glyph buttons into one flex row. Panel-openers looked the same as view actions, nothing was legible without hovering, and the legend shoved everything as it grew. Reorganised into information zones: - Two rows: row 1 is identity + search + live status (counts + validity pill clustered on the right); row 2 is the actions. - Actions grouped into bordered segments with quiet tags — Panels (Ontology/Layers/Changes/Decisions, icon+label since they are the least guessable), View (types/collapse/layout/fit, icon-only + tooltip), and Data (refresh, Export/Import). The grouping itself teaches which controls open a panel vs change the view vs move data. - The subject-area colour key moves to a thin reference strip below the toolbar, so it can never push the controls and wraps safely for many areas. - Real inline SVG icons (new canvas/src/icons.tsx, no new dependency) replace the Unicode glyphs, which rendered inconsistently across OSes. Wraps gracefully at narrow VS Code webview widths where the old single row overflowed. Verified: typecheck + build clean, panel toggles and layout menu work, no console errors, legible at 640px.
The Tier 1 GTM capability: one semantic diff/merge view reused across every model-change flow (preview, propose, drift, reverse-accept, import, conflicts) in both the Studio Canvas and VS Code. Free local review is the bottom-up funnel; the blocking CI gate stays the paid wedge. The toolbar declutter (prior commit) is the first step of making the Canvas the polished home for this review experience; the compare/merge view builds on the same surface.
Adds a logical Model Explorer to the VS Code side panel: every entity expands to its attributes (PK marked with a key icon), and clicking an entity or attribute opens logical/entities/<name>.yaml with the cursor on that object. This is the 'what is my model' view. The 'Entities' shown in the old Warehouse Config tree actually come from classifying dbt MODELS via reverse-config explain, which confused a user into thinking it was their logical model. That tree is renamed to 'Warehouse Mapping' (how your dbt models map to entities), so the two are no longer conflated: Model = the logical model, Warehouse Mapping = the warehouse classification. Backed by a new CLI command: - mdl model tree --format json — emits every entity with its attributes (name, domain, role, nullable, is_pk) and the source file per entity, so the extension stays thin and the shape is testable in pytest. Built on mdl_core.query.model_tree(model, path_for=...), reusing ModelRepo's file map for the click target. Tests: core query (pk flag, source file exists, subject-area scope), CLI (json shape, files resolve on disk, text format), and the tree-view rendering harness (entity→attribute drill-down, key icon, both clicks open the owning YAML, resting states). 932 pytest pass; all 6 extension gates green (44 commands). CLI reused, extension 0.3.17.
The review DiffView gains the first slice of the semantic compare/merge view (Tier 1 spec): display filters and per-field change selection, so a reviewer can isolate additions/renames/etc. and accept or reject changes one field at a time — not just whole objects. - Filters (All / Additions / Modifications / Renames / Deletions / Conflicts) are VIEW-ONLY: they change what is visible, never what is selected, so a hidden change can never be silently dropped from a proposal. Each chip shows its count; empty categories are hidden. - Per-field selection keys on (ulid, field). An object is included when ≥1 of its fields is selected; the header checkbox shows a tri-state (all/some/none) and toggles every field at once. The original per-object path (onToggle) is kept intact, so ReviewScreen works unchanged; the new per-field mode activates only when a host passes onToggleField. - Pure semantics live in reviewModel.ts (filter + selection), so they are unit-testable apart from the component. Testing (per the 'system + interaction tests matter now' directive): - Vitest + Testing Library added to canvas/ (first JS test runner here): reviewModel unit tests (8) + DiffView interaction tests (5) that render the real component and click filters + per-field checkboxes, asserting filtering never changes selection and per-field exclusion drops the right objects. - pytest server-side system test (test_review_diff_contract): the /api/preview ModelDiffDoc carries the (ulid, field) identity per-field selection depends on, and is stable across re-previews — the wire contract guard between server and the canvas selection. 935 pytest pass, 13 canvas tests pass, ruff clean, canvas typechecks.
Validating Phase 1 against a real project surfaced a gap: DiffView had the per-field capability but ReviewScreen still passed the per-OBJECT callback (onToggle), so on the real SME review screen the filter toolbar rendered but the per-field checkboxes stayed dormant — no caller activated them. Wire it through: ReviewScreen now tracks excluded (ulid, field) keys and passes onToggleField; SmeApp toggles field keys in its excluded set. The selected set is derived as all field keys minus the excluded ones (default: everything included), matching the object auto-derivation the DiffView tests assert. Verified live on the ibor demo: staged a real definition edit, opened the review screen, confirmed the per-field 'DEFINITION CHANGED' checkbox renders and that unchecking it derives the object header to excluded — the exact semantics the unit + interaction tests cover. 13 canvas tests pass, typecheck clean.
# Conflicts: # canvas/src/TopBar.tsx # canvas/src/icons.tsx # canvas/src/styles.css # packages/server/src/mdl_server/static/assets/ModelDiagram-C9iVTvG9.js # packages/server/src/mdl_server/static/assets/ModelDiagram-DfdI4kly.js # packages/server/src/mdl_server/static/assets/ModelDiagram-oAb1enKe.js # packages/server/src/mdl_server/static/assets/sme-rGO675Bx.js # packages/server/src/mdl_server/static/assets/styles-Cn8AFsR1.js # packages/server/src/mdl_server/static/index.html # packages/server/src/mdl_server/static/sme.html # uv.lock # vscode/package.json
Make an erwin import show the unified semantic review (filters, per-field
selection, severity, break-impact) BEFORE anything is written, with an
"Apply import" action that writes only the selected new objects. Reuses the
Phase 1 ReviewScreen/ModelDiffDoc end to end; no change to the propose flow.
Server
- POST /api/import/preview (erwin only; 422 otherwise): parse without writing,
diff the imported model against the current one by name, and return the
enriched ModelDiffDoc (paths + break-impact) plus the import's command list
and warnings for the apply action. Writes nothing.
- POST /api/import/stash + GET /api/import/stash/{token}: one-shot content
channel so a VS Code .xml can pre-fill the canvas review (the webview can't
read local files).
Core
- Extract remap_head_by_name() from diff_models_by_name() so a caller needing
both the diff and the ULID-remapped head (break-impact enrichment) shares one
remapping. Signature of diff_models_by_name unchanged.
Canvas
- ReviewScreen gains an optional `action` prop; when present it renders that
action's button (Apply import) over the included ULIDs, else the unchanged
"Submit for review" propose button.
- reviewModel: includedEntityNames (added-only) + filterImportChanges map a
field selection to the import commands that should be written.
- api: importPreview, importStashGet, applyImportBatch(tolerateExisting).
- SmeApp routes ?import=<token|1> to a pre-filled import → review → apply.
VS Code
- modelith.reviewImport command, framed as a Reverse Review view/title action
and an .xml explorer context action; stashes file content then opens
/sme?import=<token>.
Tests: import/preview diff-doc + writes-nothing, non-erwin 422, stash one-shot
roundtrip; reviewModel write-filter cases. Spec phasing marks Phases 1–2 shipped.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
One unified semantic diff/merge review -
A single
ModelDiffDocpayload and one ReviewScreen renderer - that every model-change flow reuses, in both the Canvas and VS Code. The spec (specs/modelith-semantic-compare-merge-spec.md) defines 6 phases; Phases 1 and 2 are shipped on this branch.Commits (newest first):
43260f9 Phase 2 — erwin import pre-write review: /api/import/preview (parse-without-write → enriched diff doc), ReviewScreen action prop + "Apply import" (writes only selected new objects), modelith.reviewImport in the Reverse Review view, .xml→stash→prefill channel.
749b40c Merge main into the branch (conflicts resolved).
889320d Wire per-field selection through the live review screen.
baef9ce Phase 1 — display filters + per-field selection on the 2-pane review.
3e0c2c1 VS Code Model tree: entities → attributes, click opens the YAML.
20892a3 The spec itself (Tier 1 semantic compare & merge).
6c0cb15 Toolbar declutter (grouped, labelled, two-row).