From 112f33df801f4d41a818e6293dff959264441dd5 Mon Sep 17 00:00:00 2001 From: Diego Imbert Date: Wed, 30 Sep 2026 18:38:26 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20address=20review=20=E2=80=94=20live-vers?= =?UTF-8?q?ion=20save=20check,=20bounded=20writer=20lookups,=20script-only?= =?UTF-8?q?=20trigger=20writes,=20dbt=20sources,=20hidden=20pane?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 (1M context) --- .../assets/AssetGraph/resolveGraph.test.ts | 29 ++++++ .../assets/AssetGraph/resolveGraph.ts | 8 +- .../(logged)/pipeline/[folder]/+page.svelte | 92 +++++++++++++------ 3 files changed, 99 insertions(+), 30 deletions(-) diff --git a/frontend/src/lib/components/assets/AssetGraph/resolveGraph.test.ts b/frontend/src/lib/components/assets/AssetGraph/resolveGraph.test.ts index 2663c52e28..6d38a8c119 100644 --- a/frontend/src/lib/components/assets/AssetGraph/resolveGraph.test.ts +++ b/frontend/src/lib/components/assets/AssetGraph/resolveGraph.test.ts @@ -1322,6 +1322,35 @@ describe('pipelineNodeErrors', () => { ).toBe(0) }) + it('does not count a flow write as starting an asset-triggered script', () => { + const g = baseGraph({ + assets: [{ kind: 'ducklake', path: 'main/raw' }], + runnables: [ + { path: 'f/x/reader', usage_kind: 'script', in_pipeline: true }, + { path: 'f/x/loader', usage_kind: 'flow' } + ] as any, + edges: [ + { + runnable_path: 'f/x/loader', + runnable_kind: 'flow', + asset_kind: 'ducklake', + asset_path: 'main/raw', + access_type: 'w' + } + ], + triggers: [ + { + trigger_kind: 'asset', + asset_kind: 'ducklake', + asset_path: 'main/raw', + runnable_kind: 'script', + runnable_path: 'f/x/reader' + } + ] + } as any) + expect(pipelineNodeErrors(g, new Map()).untriggered.get('f/x/reader')).toBe('unwritten-inputs') + }) + it('clears a subscription the workspace writes outside the pipeline', () => { const errors = pipelineNodeErrors( graph, diff --git a/frontend/src/lib/components/assets/AssetGraph/resolveGraph.ts b/frontend/src/lib/components/assets/AssetGraph/resolveGraph.ts index 8c48e4d0fa..b178de3387 100644 --- a/frontend/src/lib/components/assets/AssetGraph/resolveGraph.ts +++ b/frontend/src/lib/components/assets/AssetGraph/resolveGraph.ts @@ -339,8 +339,7 @@ export type PipelineNodeErrors = { * Nodes the pipeline cannot work with, with the reason. Assets (keyed * `kind:path`): one nothing uses at all. Scripts (keyed by path): one * subscribed (`// on `) to an asset nothing writes, in the pipeline or, - * per `writtenElsewhere`, anywhere else in the workspace (asset triggers fire - * on any write), since the subscription can never fire; the asset itself is + * per `writtenElsewhere`, by a script anywhere else in the workspace, since the subscription can never fire; the asset itself is * fine, and is reported in `unwritten`. A body read of an external asset is fine and is not passed here: * only the explicit `// on` annotations, per script path, count as subscriptions. */ @@ -350,9 +349,12 @@ export function pipelineNodeErrors( writtenElsewhere: ReadonlySet = new Set() ): PipelineNodeErrors { const referenced = referencedAssetKeys(graph) + // Only a script's write fires an asset trigger: a flow's never does. const written = new Set( graph.edges - .filter((e) => e.access_type === 'w' || e.access_type === 'rw') + .filter( + (e) => e.runnable_kind === 'script' && (e.access_type === 'w' || e.access_type === 'rw') + ) .map((e) => `${e.asset_kind}:${e.asset_path}`) ) for (const k of writtenElsewhere) written.add(k) diff --git a/frontend/src/routes/(root)/(logged)/pipeline/[folder]/+page.svelte b/frontend/src/routes/(root)/(logged)/pipeline/[folder]/+page.svelte index bd6c799c08..09bd462cb8 100644 --- a/frontend/src/routes/(root)/(logged)/pipeline/[folder]/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/pipeline/[folder]/+page.svelte @@ -1,4 +1,5 @@