Skip to content

feat(lineage): new CLL experience with impact-aware column annotations#1278

Merged
danyelf merged 29 commits intomainfrom
worktree-update-cll-mode
Apr 9, 2026
Merged

feat(lineage): new CLL experience with impact-aware column annotations#1278
danyelf merged 29 commits intomainfrom
worktree-update-cll-mode

Conversation

@danyelf
Copy link
Copy Markdown
Contributor

@danyelf danyelf commented Apr 6, 2026

PR checklist

  • Ensure you have added or ran the appropriate tests for your PR.
  • DCO signed

What type of PR is this?

Feature (behind --new-cll-experience flag)

What this PR does / why we need it:

Adds a new column-level lineage (CLL) experience that treats column lineage as an annotation overlay on the impact radius view, rather than a separate mode. Behind the --new-cll-experience CLI flag.

Specifically:

  • When running CLL (and at startup, if you're also using --impact_at_startup flag), draws nodes as "impacted" . Does NOT show specific impacted columns for any model
  • Maintains positions of nodes as you click and move through the UI
  • When examining any column's CLL, shows its ancestry on the current view.

Here's a brief table of how the previous model and the new CLL use the three API calls:

  Classic New CLL
/lineage lineage Layer 1: lineage for layout
/cll?change_analysis=true new layout; load CLL data; add impact marks Layer 2: keep layout; colors all nodes by impact; prepares sidebar for coloring
/cll?column=[foo] new layout; load column CLL data; add column annotations Layer 3: keep layout; keep colors from layer 2; add column annotations

If you run with both --new-cli-experience and --impact-at-startup then you get layers 1&2 together immediately

image image

Key behaviors:

  • Impact radius view: Models downstream of changes get amber backgrounds; impacted columns highlighted in the sidebar schema view
  • Column focus mode: Clicking a column shows its upstream ancestry chain as column nodes inside model nodes, with amber edges/backgrounds on impacted columns. Model-level coloring stays frozen — no visual mode switch
  • Stable impact state: Impacted node/column sets are snapshot during impact analysis and remain stable across column selections
  • Multi-column ancestry: A model can show multiple ancestor columns when a downstream column depends on several columns from the same upstream model
  • Graph boundary aware: Ancestry columns on models not in the current view are filtered out (no floating nodes)

What's new:

  • --new-cll-experience CLI flag + new_cll_experience server flag
  • computeIsImpacted — three-signal impact detection (changeStatus, CLL node flag, parent_map walk)
  • computeImpactedColumns — memoized DFS walk of parent_map for column-level impact
  • computeColumnAncestry — traces selected column's upstream ancestry chain
  • getStyleForImpacted — amber highlight style for impacted nodes/columns
  • Frozen impactedNodeIds / impactedColumnIds refs for cross-mode stability

Special notes for your reviewer:

All behind --new-cll-experience flag — zero impact on existing behavior. Use recce server --new-cll-experience --impact-at-startup to test.

TODO noted in code: move isImpacted to per-model state on node data instead of a lookup set.

UX Nites:

  • currently, you can exit CLL mode, and get back to a 'reset' state with the legacy Impact Radius buttons. I would like to make them go away in this new experience.
  • the highlighting/hover behavior in the sidebar is annoying, as is the tooltip there.

Open question:

  • Should we tweak column CLL to also give DOWNSTREAM effects of changes?
  • is the yellow highlight the right choice?

Does this PR introduce a user-facing change?:

New --new-cll-experience flag for recce server that enables an improved column-level lineage visualization with impact-aware column annotations.

Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 6, 2026

Codecov Report

❌ Patch coverage is 66.66667% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
recce/cli.py 66.66% 1 Missing ⚠️
Files with missing lines Coverage Δ
recce/cli.py 61.64% <66.66%> (+0.01%) ⬆️

... and 4 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

danyelf and others added 24 commits April 5, 2026 18:11
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Add visual verification stories for the new CLL amber highlight:
- Node Comparison: side-by-side impacted vs non-impacted nodes in
  light and dark mode for color tuning
- Full Lineage Canvas: jaffle-shop-expand impacted subgraph layout

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
- Pass newCllExperience/isImpacted through LineageNodeData for ReactFlow
- Build mock CLL data with realistic column-level impact scenarios
- Run each node through computeIsImpacted (same code path as production)
- Limit to ~25 nodes with mix of impacted/not-impacted/unrelated
- Export computeIsImpacted from @datarecce/ui/advanced
- Fix cll undefined→null coercion in GraphNodeOss

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Pass newCllExperience flag from useRecceServerFlag to both toReactFlow
call sites. When the flag is on, always capture existing node positions
so nodes never jump when CLL data changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
- Replace `as any` with proper ColumnLineageData cast in story
- Remove invalid `index` property from test column data
- Biome auto-formatting fixes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
computeIsImpacted now also checks cllNode.columns (not just the flat
cll.current.columns dict) for change_status. This catches the case where
a downstream node like 'orders' has impacted columns stored on the node
object rather than in the flat columns dict.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Rolling back toReactFlow newCllExperience option and LineageViewOss
position locking. Focusing on correctness of impact computation first —
columns and position stability will come after column-level impact
propagation is working.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Replace node.impacted flag and direct change_status checks with
computeImpactedColumns parent_map walk. A node is impacted if any
of its columns trace upstream to a changed column.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Columns that trace upstream to a changed column via CLL parent_map
now get an amber background in the schema sidebar. This helps users
predict which columns would show differences in a profile diff.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
When newCllExperience is on:
- toReactFlow skips column node creation (no columns on lineage map)
- LineageViewOss always preserves existing node positions
- Node height stays at base 60px (no column expansion)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
…ting

Signed-off-by: Danyel Fisher <danyel@gmail.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
…CLL modes

Signed-off-by: Danyel Fisher <danyel@gmail.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
@danyelf danyelf force-pushed the worktree-update-cll-mode branch from 539e715 to dd07950 Compare April 6, 2026 01:12
danyelf added 2 commits April 5, 2026 18:38
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
@danyelf danyelf self-assigned this Apr 6, 2026
@danyelf danyelf added the enhancement New feature or request label Apr 6, 2026
@danyelf danyelf added the javascript Pull requests that update javascript code label Apr 6, 2026
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new flagged column-level lineage (CLL) UX that overlays column ancestry/impact annotations on top of the existing impact-radius view, plus the supporting impact/ancestry computations and UI styling.

Changes:

  • Introduces --new-cll-experience / new_cll_experience server flag wiring (CLI → /api/flag consumption).
  • Adds impacted node/column detection + column-ancestry annotation rendering (ReactFlow) for the new CLL experience.
  • Updates schema sidebar + lineage node/column styling and extends tests/storybook coverage for the new behavior.

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
recce/cli.py Adds --new-cll-experience flag and includes new_cll_experience in server flag dict.
js/src/components/lineage/tests/LineageView.component.test.tsx Updates mocks for new server-flag usage in lineage view tests.
js/src/components/lineage/tests/GraphNode.test.tsx Extends contexts mock to include useRecceServerFlag.
js/packages/ui/src/lib/dataGrid/generators/toSchemaDataGrid.ts Adds isImpacted to schema rows and marks impacted columns for styling.
js/packages/ui/src/contexts/lineage/types.ts Extends lineage context/types with new CLL experience + impacted sets.
js/packages/ui/src/components/ui/dataGrid/dataGridFactory.tsx Minor refactor to pass typed schema options into generator.
js/packages/ui/src/components/schema/style.css Adds impacted-row background variables and classes for schema sidebar.
js/packages/ui/src/components/schema/SchemaView.tsx Reads server flag and highlights impacted columns using frozen impacted set.
js/packages/ui/src/components/lineage/styles.tsx Adds getStyleForImpacted amber background helper.
js/packages/ui/src/components/lineage/nodes/LineageNode.tsx Supports new CLL props and impacted background; disables dimming in new mode.
js/packages/ui/src/components/lineage/LineageViewOss.tsx Implements new CLL overlay flow: impacted sets, ancestry computation, layout preservation, cache patch helper.
js/packages/ui/src/components/lineage/lineage.ts Updates toReactFlow for new experience and adds ancestry annotation node/edge generation.
js/packages/ui/src/components/lineage/GraphNodeOss.tsx Passes newCllExperience and isImpacted down to node renderer.
js/packages/ui/src/components/lineage/GraphColumnNodeOss.tsx Threads isImpacted through column-node data.
js/packages/ui/src/components/lineage/computeIsImpacted.ts New helper to determine impacted nodes from changeStatus, node flag, and impacted columns.
js/packages/ui/src/components/lineage/computeImpactedColumns.ts New memoized DFS to derive impacted columns from parent_map + change_status.
js/packages/ui/src/components/lineage/computeColumnAncestry.ts New ancestry tracer for selected column producing per-model column annotations.
js/packages/ui/src/components/lineage/columns/LineageColumnNode.tsx Applies impacted background styling for annotated column nodes.
js/packages/ui/src/components/lineage/tests/styles.test.ts Adds unit tests for getStyleForImpacted.
js/packages/ui/src/components/lineage/tests/LineageNode.test.tsx Adds tests for “no dimming” + impacted background behavior.
js/packages/ui/src/components/lineage/tests/lineage.test.ts Adds test ensuring classic column nodes are skipped in new CLL experience.
js/packages/ui/src/components/lineage/tests/isImpacted.test.ts New tests for computeIsImpacted.
js/packages/ui/src/components/lineage/tests/computeImpactedColumns.test.ts New tests for computeImpactedColumns (chains, cycles, empties).
js/packages/ui/src/api/flag.ts Extends RecceServerFlags with new_cll_experience.
js/packages/ui/src/advanced.ts Exports new CLL helpers/types via @datarecce/ui/advanced.
js/packages/storybook/stories/lineage/CllExperience.stories.tsx Adds storybook fixtures/demos for new CLL impacted/ancestry visuals.
Comments suppressed due to low confidence (1)

js/src/components/lineage/tests/LineageView.component.test.tsx:141

  • The mocked module object defines useRecceServerFlag twice; in an object literal the later key silently overrides the earlier one, which can mask issues and make the test setup misleading. Remove the duplicate and return a single consistent shape for data (e.g. include new_cll_experience when needed).
vi.mock("@datarecce/ui/contexts", async () => {
  const React = await vi.importActual<typeof import("react")>("react");
  return {
    useRouteConfig: vi.fn(() => ({ basePath: "" })),
    useRecceServerFlag: vi.fn(() => ({ data: undefined })),
    useLineageGraphContext: vi.fn(() => mockLineageGraphContext),
    useRecceInstanceContext: vi.fn(() => mockRecceInstanceContext),
    useRecceServerFlag: vi.fn(() => ({ data: {} })),
    useRecceActionContext: vi.fn(() => ({

Comment on lines +129 to +155
/**
* Compute impacted node IDs and column IDs in a single pass over the CLL data.
*
* The expensive part is `computeImpactedColumns` (DFS over parent_map), so we
* run it once and thread the result into per-node checks.
*/
function computeImpactedSets(
lineageGraph: LineageGraph,
cll: ColumnLineageData,
): { nodeIds: Set<string>; columnIds: Set<string> } {
const columnIds = computeImpactedColumns(cll);
const nodeIds = new Set<string>();
for (const nodeId of Object.keys(lineageGraph.nodes)) {
const node = lineageGraph.nodes[nodeId];
if (
computeIsImpacted(
nodeId,
cll,
node.data.changeStatus as NodeChangeStatus,
columnIds,
)
) {
nodeIds.add(nodeId);
}
}
return { nodeIds, columnIds };
}
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

computeImpactedSets calls computeIsImpacted for every node, and computeIsImpacted linearly scans the full impactedColumns set (via startsWith). This makes the overall computation O(numNodes × numImpactedColumns) and can get expensive on large graphs. Consider deriving nodeIds directly while computing impacted columns (e.g. parse the nodeId prefix from impacted column IDs) or maintaining a nodeId→impacted lookup map.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keep as-is as this can be further perf enhancement.

Comment on lines +297 to +305
// Expand model node to fit its ancestry columns
if (annotations.length > 0) {
const modelNode = nodes.find(
(n) => n.id === modelId && n.type === "lineageGraphNode",
);
if (modelNode) {
modelNode.height = 60 + 20 + annotations.length * COLUMN_HEIGHT;
}
}
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inside addColumnAncestryNodes, expanding each model node uses nodes.find(...) inside the loop over columnAncestry, which becomes O(n²) as the node list grows. Consider building an id -> node map once (or tracking model nodes as you create them) and updating heights via that map.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keep as-is as this can be further perf enhancement.

@wcchang1115
Copy link
Copy Markdown
Collaborator

js/packages/ui/src/components/lineage/nodes/LineageNode.tsx:338

newCllExperience has a triple fallback (prop ?? data.newCllExperience ?? false) but data.newCllExperience is never set in production — GraphNodeOss passes it as a direct prop only. The data path is only used by some Storybook stories via adaptForCanvas. Consider removing newCllExperience from LineageNodeData and keeping it as a direct prop only — one path, no fallback chain.

js/packages/ui/src/components/lineage/nodes/LineageNode.tsx:398

This simplification is correct — iconColor and titleColor had identical logic before this PR. But it's a drive-by refactor unrelated to the CLL feature. Consider separating it into its own commit or reverting to keep this PR focused.

@wcchang1115
Copy link
Copy Markdown
Collaborator

Review Fixes Summary

# Source Suggestion Fixed? Reason
1 Copilot Missing impactedColumnIdsRef snapshot in refreshLayout Yes Real bug — sidebar highlighting goes stale
2 Copilot O(n×m) startsWith scan performance No CLL data is scoped to a subgraph, not a real perf issue at current scale
3 Copilot O(n²) nodes.find() in ancestry No Ancestry map is a handful of models, not a real perf issue
4 Copilot Docstring says != false but code checks truthy Yes Misleading docs, one-word fix
5 Copilot Duplicate useRecceServerFlag() call Yes Redundant hook, easy cleanup
6 Copilot (suppressed) Duplicate useRecceServerFlag mock key Yes Breaks pnpm type:check
7 wcchang Triple fallback for newCllExperience No Cannot fully remove data path — Storybook stories go through ReactFlow which requires it. Minimal improvement for the noise
8 wcchang Drive-by iconColor = titleColor refactor Yes (reverted) Unrelated to CLL, reverted to pre-PR IIFE to keep PR focused

- Add missing impactedColumnIdsRef snapshot in refreshLayout
- Remove duplicate useRecceServerFlag() hook call
- Fix docstring to match computeIsImpacted semantics (truthy, not !== false)
- Remove duplicate useRecceServerFlag mock key that broke type-check
- Revert drive-by iconColor simplification to keep PR focused

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Wei-Chun, Chang <wcchang@infuseai.io>
Copy link
Copy Markdown
Collaborator

@wcchang1115 wcchang1115 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!

@danyelf danyelf merged commit 69a601b into main Apr 9, 2026
20 checks passed
@danyelf danyelf deleted the worktree-update-cll-mode branch April 9, 2026 19:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request javascript Pull requests that update javascript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants