fix: review round 1 — dbt-script materialize, set-form rule, doc

- Refuse `// materialize` on a dbt script, the producer half of the rule the
  trigger loop already applies to `// on`: the graph ingest republishes that
  path's asset rows wholesale, so a declared write is wiped by the deploy that
  accepted it while its runs keep stamping the relation.
- `dormant_dbt_subscriptions` now spells the same predicate its singular sibling
  does: the producer set has to be non-empty (nothing produces it yet is deploy
  order, not a dormant edge) and excludes the subscriber's own path (a script
  never wakes itself). Both divergences are pinned by tests.
- The docs no longer claim the dbt deploy log covers a native producer that drops
  its `// materialize`; it does not, and nothing else reports that case.
- An integration test over the deploy contract, since only a real deploy proves
  the handler feeds `sole_dbt_producer` the canonical key `asset.path` holds —
  the spelling that has to agree across the materialize target, the `// on` ref
  and the refusal that joins them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ruben Fiszel
2026-09-04 17:55:51 +02:00
co-authored by Claude Opus 5
parent 73fc580766
commit addcb4c525
12 changed files with 314 additions and 49 deletions
+13 -2
View File
@@ -1582,6 +1582,18 @@ async fn create_script_internal<'c>(
// a `manual` script owns its DDL and skips them.
if let Some(m) = pipeline_annotations.materialize.as_ref() {
use windmill_parser::asset_parser::AssetKind as PAssetKind;
// The producer half of the rule the trigger loop below applies to `// on`:
// a dbt project's writes come from its manifest, and the graph ingest
// republishes this path's asset rows wholesale, so a declared one would be
// wiped by the very deploy that accepted it while its runs kept stamping
// the relation.
if ns.language == ScriptLang::Dbt {
return Err(Error::BadRequest(
"a dbt script cannot declare `// materialize`: what a project builds is read \
from its manifest and published by the graph ingest, not annotated."
.to_string(),
));
}
match m.target_kind {
PAssetKind::Ducklake => {
if ns.language != ScriptLang::DuckDb {
@@ -2441,8 +2453,7 @@ async fn create_script_internal<'c>(
)));
}
if let Some(dbt_owner) =
windmill_common::assets::sole_dbt_producer(&db, &w_id, relation, &ns.path)
.await?
windmill_common::assets::sole_dbt_producer(&db, &w_id, relation, &ns.path).await?
{
return Err(Error::BadRequest(format!(
"`{trigger_ref}` cannot be subscribed to: it is built by the dbt project at \