* feat: add the workspace settings document, applied server-side
* feat: add wmill workspace get-settings and apply-settings
* fix: warn on unknown settings keys and refuse ee-only ones in the dry run
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test: stub the settings document endpoint in the push settings unit tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: refuse mistyped settings and keep 4xx status on a partial settings apply
Reject non-boolean switches and non-string Slack credentials instead of
coercing them into destructive writes, preserve the failing endpoint's
status when wrapping a partial-apply error, keep the stored Slack secret
when an older CLI pushes one encrypted under another workspace's key,
print the plan's warnings in apply-settings, and state the admin check
load_settings_document leaves to its caller.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: compare handler arguments verbatim and type-check scalar settings
A handler's extra_args is stored as sent and its nulls reach the job as
arguments, so an added or removed null argument is now a change. Scalar
settings are type-checked in the plan so a dry run never reports a change
the endpoint then refuses, and an error handler's omitted mute flags
compare as the false the endpoint stores.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: require a 32-bit integer rate limit in the settings plan
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: data table permissions and protection rules in the settings document
The settings document gains datatable_permissions, each data table's roles
named by their Postgres role name and written by catalog id, and
protection_rules, keyed by rule name. Both are managed only when stated and
stated as a whole. The dev workspace lock stays with the dev workspace
setting.
wmill sync carries datatable_permissions only with includeDatatablePermissions
(or --include-datatable-permissions), since tenants and roles are specific to
a workspace and an instance. protection_rules stay out of settings.yaml:
protection-rules.yaml syncs them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: validate the default data table role and plan permissions against a replaced datatable
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat: guide the AI when no resource type fits a service
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: report a failed resource type search and model a synced instance in evals
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: keep named resource types first and list only related names on search failure
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: point the sync note at the hub sync button only
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* refactor: list the instance's resource types when the search finds nothing
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: let the user create a resource type instead of syncing the hub
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: make the linked-variable resource the default, not the only option
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test: add a cli eval for a service without a hub resource type
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: keep the resource type search fresh and serve it without embeddings
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: let the global chat create a resource type after the user confirms it
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test: simulate create_resource_type in global evals and fix the in-house API checklists
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: reuse unchanged custom resource type vectors and match names case-insensitively
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: add the unsynced note whatever the resource type search returns
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: carry the no-type rule into flow inputs and script parameters, and ask the chat to relay the sync
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: tell script chat to hand the user the schema it cannot create
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: skip the resource type fingerprint query when no index is loaded
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: index resource types locally when the hub's embeddings cannot be fetched
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: match short service names in the resource type text fallback
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: name custom resource types with the c_ prefix the Resources page uses
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test: require the c_ prefix in in-house API evals and a real sync mention
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test: cover workspace scoping of the resource type text search
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: say the form adds the c_ prefix unless an admin turns it off
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: refresh resource type names and the resources tab after the chat creates a type
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* perf: fingerprint resource types without sorting every schema
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: tell the global chat in each message when the hub's types are not synced
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: send users who cannot deploy to an admin for a resource type, and have the cli list types first
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015YR13nJWziUSWktZwhC7dx
* refactor: move the resource type search index changes to their own pull request
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015YR13nJWziUSWktZwhC7dx
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat: stop offering deno for new scripts and ship it only on full images
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: keep deno in the integration test worker and narrow the missing-deno error
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: keep deno selectable on existing deno scripts when the saved order omits it
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: drop the deno workloads from the benchmark suite
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test: run bun instead of deno in tests that are not about the deno runtime
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test: drop the deno cases from the integration suite
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test: update the app policy hash for the bun inline script
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix: keep a resource default set in the schema when the script is redeployed
Fixes#11493
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: keep a schema resource default only while the arg stays that resource type
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: drop a scalar resource default when the arg becomes a list
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* feat: add trigger, tag, run-as and digest claims to job OIDC tokens
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: parse digest floats exactly and derive the codebase digest like push
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: give flows that reference flow nodes no digest and keep --json clean
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: list flow step digests from module positions only
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test: skip the pulled module-script digest check on windows
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to 71b8c1042fd8188c2d2882476f12b671cb5ba421
This commit updates the EE repository reference after PR #840 was merged in windmill-ee-private.
Previous ee-repo-ref: ba1870536789a43c5b6ec18522a93edb9cb07f3d
New ee-repo-ref: 71b8c1042fd8188c2d2882476f12b671cb5ba421
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* test(cli): stop CLI tests resolving a just-published windmill-client
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test: quote the Windows cert path in the pg attach assertion
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(cli): note when the windmill-client pin must move
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(cli): resolve fork specific items from the parent workspace entry
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(cli): keep fork resolution to the id-guarded inference
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(cli): stop CLI tests resolving a just-published windmill-client
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test: quote the Windows cert path in the pg attach assertion
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(cli): note when the windmill-client pin must move
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* feat: lint AI agent tool names and flow groups in wmill lint
* refactor: assemble chat and CLI AI guidance from one topic table
* feat: share flow groups, reuse and pipeline guidance with the CLI skills
* feat: share raw app, data table and secret guidance between chat and CLI
* docs: document the shared AI guidance source for contributors
* fix: keep wmill lint running on flows with malformed collections
* fix: reject skill descriptions that are not plain YAML text
* fix: tighten fence typos, script base scope and app prompt order
* fix: skip tool name checks on agent steps linked to a saved agent
* fix: catch any misspelled prompt fence and soften the tool name claim
* fix: align cli eval harness with the files and steps wmill init adds
* fix: drop cli eval checks that expect unrequested deploy commands
* fix: list ansible as mainless and c# Main in script base guidance
* fix(cli): stub the API client over its real exports in tests
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(cli): mock the API client once and dispatch to per-suite stubs
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: carry labels when deploying variables, resources and folders across workspaces
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: clear a folder's labels in the target when the source has none
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test: pin that the frontend deploy adapter carries variable labels
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* feat(cli): hash codebase extra_digest_paths and warn on uncovered bundle inputs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(cli): cache codebase digest only once complete, skip entry in warning
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(cli): accept string extra_digest_paths, always print uncovered-input warning
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(cli): clarify extra_digest_paths resolution and missing-path error
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(cli): sync workspace dependency files on Windows
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(cli): pin workspace dependency path parsing on both separators
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(cli): classify workspace dependency files on either separator
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(cli): check out the fork deploy clone without autocrlf
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(cli): read wmill.yaml from the branch a git-sync deploy writes to
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(cli): create the stateful dir once the deploy branch's config is read
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: keep instance groups when editing auto-invite and push them from sync
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: compare settings.yaml without undeclared instance groups on push
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: read a missing auto_invite as empty when diffing settings.yaml on push
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore: update ee-repo-ref to f2fced19fcae81de7f6dac545010ce404c052e1b
This commit updates the EE repository reference after PR #813 was merged in windmill-ee-private.
Previous ee-repo-ref: cf4258c1232720ca3b82db2b22ea1fb4bca9533e
New ee-repo-ref: f2fced19fcae81de7f6dac545010ce404c052e1b
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
* fix(cli): keep the workspace color when settings are synced from git
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* docs(cli): name the sync direction consistently in the identity-field comments
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(cli): apply the workspace color from settings.yaml only when the file sets one
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* feat(cli): list, get and restore trashed items from the CLI
* docs(cli): tell agents a sync push deletion is restorable with wmill trash
* refactor(cli): share the ApiError formatting and type trash flags as integers
* fix(cli): keep variables and resources a sync push repo never tracked
`wmill sync push` archives a script it no longer finds locally, but hard-deletes
a variable or a resource: the credentials go for good. A remote-only one is
equally a deletion being deployed and one the repository never had, provisioned
on the instance or written by a script at runtime, and reading the second as a
deletion is unrecoverable.
Committed history tells them apart. A push whose changeset deletes a variable or
resource now asks what this branch has ever tracked at `*.variable.*` /
`*.resource.*`; anything it has never recorded is kept on the remote (prompted
for on a TTY), and a real deletion, recorded before the commit that removed it,
still applies. Where the history cannot be read (shallow clone, sparse checkout,
no repository) there is no evidence either way, so the deletion stands as before
with a warning naming the remedy — the git-sync "Pull from repo" job runs in a
depth-1 clone and must keep deploying the deletions it always has.
`--delete-untracked-secrets` / `deleteUntrackedSecrets` opts a mirror-semantics
pipeline back into deleting them unattended.
Fixes GIT-980
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(cli): classify secret-bearing deletions the way the push itself does
Three ways the suffix match missed:
- A fileset child can be any file, `inner.resource.yaml` included, and its
deletion re-pushes the parent rather than deleting anything. Classifying with
the push's own `getTypeStrFromPath`, behind the same fileset exclusion the
apply loop uses, keeps the two in step.
- Deleting `f/x.resource.file.ini` deletes the resource `f/x` outright, so
without that file in the pathspecs every file resource walked past the check.
Its two files now count as the one resource they delete.
- A `specificItems` item is committed as `y.<workspace>.variable.yaml` while
`elementsToMap` collapses it to the base path the changeset carries, so a
deletion the user did commit read as never tracked. The history is searched
under both names.
`gitRecordedPaths` also reads its history with `core.quotePath=false`: a path
with a non-ASCII byte came back C-quoted and matched nothing.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(cli): judge held-back deletions per object, not per file
`DELETE /variables/delete` takes the resource at the same path down with it, and
`DELETE /resources/delete` does the same to the variables its value references,
so a tracked deletion could destroy an untracked object the push had just
reported it was keeping. A file resource had the same shape from the other end:
two files for one resource, either survivor deleting it.
The unit is the server-side object. One file left unaccounted for by history now
holds the whole object back, so nothing in a group reported as kept is deleted.
The residual is a resource whose value references a variable at another path,
which stays possible and is called out in the PR.
Also corrects what the messages claim. Deleting a variable or resource is not
irrecoverable: both move to the workspace trash, which keeps them for three days
(migrations/20260326000000_trashbin.up.sql, CE since v1.665.0). The asymmetry
with a script is real but narrower, and the prompt defaults to No, so it should
say what it actually costs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(cli): warn when sync push deletes variables the repo never tracked
`sync push` deletes a remote variable or resource that has no local file. An
object the repository has never tracked was provisioned outside it, by hand or
by a script at runtime, rather than deleted from it, and the change list said
nothing to tell the two apart.
The push still deploys every deletion — that is what the repo-is-the-mirror
contract means, and a shallow clone (the git-sync "Pull from repo" job, a
default actions/checkout) could not tell them apart anyway. What changes is that
the preview names the ones this branch's history has no record of, before the
prompt that confirms them, and points at the excludes that stop them recurring.
Deleting is also not final, which the CLI was alone in not saying: both handlers
move the item to the workspace trash first, restorable for three days
(migrations/20260326000000_trashbin.up.sql, CE since v1.665.0). A push that
deleted any now says so.
This replaces the earlier hold-back design. Keeping objects back changed what a
push deploys, needed a flag and a wmill.yaml key to opt out of, and could claim
to keep an object that a linked deletion then cascaded onto. Reporting cannot.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(cli): name the paths the untracked-deletion warning is about
A push can delete a tracked and a never-tracked resource together, where "1
resource" identified neither. The warning lists the paths instead of counting
them, so the reader knows which one to exclude.
Outside a git checkout it no longer opens "This branch's history", which
contradicted the reason it went on to give, and it drops the pronouns that
disagreed with a plural count. The history walk is skipped under --json-output,
where both notices are silenced and its result had no reader.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(cli): vouch for an object with any file in history, not just deleted ones
The tracked set was built from the deletions being judged, so a companion file
the push was not deleting could not vouch for its object: a file resource whose
`.resource.yaml` stays while its content file goes was reported as never
tracked, though the repository plainly owned it. It is built from the whole
history now, which also turns the workspace-specific lookup around — history is
normalized to base paths, the form the changeset already carries, instead of
each candidate being searched for under two names.
The warning also prints one line per server-side object rather than per file, so
a file resource is the one deletion it is rather than two.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(cli): name both kinds at a shared path, gate the history path conversion
Three from review:
A variable and a resource at one path are judged together, since deleting either
takes both, but they are two objects to name — keying the printed lines by path
alone dropped one of them.
`fromWorkspaceSpecificPath` strips a `.<workspace>` segment wherever it finds
one, so a history entry that merely looks workspace-suffixed was re-keyed onto a
different object, whose history then vouched for it. Only a path `specificItems`
claims is converted now. Not reachable from a server object (the backend rejects
`.` in paths), but history holds whatever was committed.
`secretBearingKey` lost its last caller when the tracked set moved to object
paths; removed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(cli): identify a secret-bearing object by kind as well as path
A variable and a resource can share a path and are still two backend objects:
`DELETE /variables/delete` drops the same-path resource unconditionally, while
`DELETE /resources/delete` drops only the variables its value references. Keying
tracked history by path alone let a committed variable vouch for a resource the
repository never had, which then went unmentioned. The cascade is a reason to
report both, not to treat them as one. A file resource's two files keep one id.
`git log HEAD` also fails on a repository with no commits, which was reported as
"its history could not be read. Check that git runs correctly in this directory"
— true of neither.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs(cli): tighten the comments on the untracked-deletion warning
Halves the prose without dropping a constraint: the trashbin retention is
stated where the message says it rather than twice more in doc comments, and the
two stacked comments at the print site had come to contradict each other, one
still describing a same-path variable and resource as judged together.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(cli): say where a deleted variable or resource went
`sync push` deletes a remote variable or resource that has no local file, and
said nothing more. Both handlers move the item to the workspace trash first,
restorable for three days (migrations/20260326000000_trashbin.up.sql, CE since
v1.665.0), and the CLI was the one surface never to mention it — the report
behind this concluded the deletion was final and there was nothing to restore.
A push that deleted any now ends with where they went and how long they have.
Drops the untracked-deletion warning this branch carried: distinguishing a
deletion the repo deployed from an object it never owned needs the branch's git
history, and roughly 130 lines to read it and be right about the answer, for a
claim the trash already softens. Two fixes it turned up in the data-table
migration guard, which reads history the same way, are kept: a path with a
non-ASCII byte came back C-quoted and matched nothing, and a repository with no
commits was reported as one where git does not run.
Fixes GIT-980
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(cli): name the trashbin correctly and say who can restore
The tab is labelled Trashbin, not Trash, and `restore_trash_item` requires
admin, so a non-admin reading the old line would go looking for a control they
do not have.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor(cli): move the migration-guard git fixes to their own PR
They fix `gitRecordedDatatableMigrationPaths`, which this PR no longer touches.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(cli): don't count a .lock deletion the push skips
The apply loop `continue`s past a non-raw-app, non-dbt `.lock` deletion before
reaching the delete switch, so nothing happens on the server. The classifier did
not mirror that, and `f/x.resource.file.lock` reaches it as a resource through
`isFileResource` — a resource type whose format_extension is literally `lock`
would have the notice announce a deletion the push never performed.
A raw-app or dbt `.lock`, the two that loop does not skip, classifies as its
bundle's own kind well before the file-resource check, so a suffix test is
enough.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs(cli): state what the classification tests protect
The header described the change rather than the invariant.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* refactor: make the app policy's principal the authority for its identity
* fix: align the app backfill with the sibling migration and audit the uncached address
* chore: refresh the sqlx cache after rebasing onto the merged base
* fix: resolve the app execution address uncached, it decides the job's authorization
* chore: cache the EE queries at the ref this branch pins
* chore: cache the EE queries at the ref this branch pins
* fix: derive the app draft's on-behalf-of address on read
* chore: cache the query the draft derivation test added
* fix: derive the app identity on the draft-table and version reads too
* docs: state the draft resolver's authorization contract
* fix: resolve a draft's principal against workspace membership only
* chore: cache the membership lookup the draft resolver added
* fix: drop an unresolvable draft's address instead of leaving it stale
* perf: evict the address cache on change so app dispatch can read it
* fix: evict on superadmin role changes, not only address changes
* refactor: make the app policy's address optional instead of derived on read
* fix: follow an external superadmin's rename into the apps that name them
* docs: state the removal gate once, and correctly
* refactor: drop the app-policy version constant that gated nothing
* docs: drop the last reference to the removed constant
* perf: read the address cache everywhere now that eviction reaches every replica
* fix: keep persisted addresses off the cache the poller evicts asynchronously
* docs: state where the cached address is accepted and where it is not
* docs: keep the cache rule in one place and drop the stale premise
* docs: sort the two lookups by how long a wrong answer lives
* fix: resolve the schedule address uncached where it is written to the row
* docs: name the release this actually ships in
* perf: evict a superadmin's key per workspace instead of the whole cache
* fix: evict every alias a superadmin principal can be spelled as
* docs: describe the trigger as it is
* docs: cover the round-tripped read in the cache rule
* docs: record why a stale dispatch address cannot escalate
* fix: validate a dispatch address against the principal's live binding
* fix: carry the validated address through to the job row and token
* fix: record the validated address on the job row, not the one handed in
* test: run the substep tag check as the non-superadmin it means to test
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* fix: rewrite a stored app address that disagrees with its principal
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* docs: record the accepted staleness window of the cached dispatch address
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* fix: record the validated address on the job's audit row
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* docs: record the accepted rename race of pre-transaction identity resolution
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* docs: separate the app's stored address from the derived one in the resolver doc
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* docs: describe the job identity fast path the push comments skipped
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* fix: backfill a legacy group-prefixed username as the group it names
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* fix: resolve a schedule edit's identity before opening its transaction
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* fix: never resolve a disabled member to a same-named superadmin
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* docs: state what the email-change notify buys, and rewrap two comment lines
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* fix: keep a group's runnables when offboarding a legacy group-prefixed member
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* fix: read the app author from the stored address, as execution does
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* docs: record the rename race's full consequence as a known, accepted limitation
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* docs: record the keep-target group address case as a known, accepted limitation
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(cli): keep permissioned_as on single-item push, as sync push does
* fix(cli): resolve syncBehavior from the target workspace, not the branch alone
* refactor(cli): share the workspace-name resolution between sync and single-item push
* test(cli): import the moved workspace-name helper from its new home
* fix: keep a local dbt descriptor under sync pull --keep-deleted
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0174o6mGTWoanipUgf5zNcVL
* fix: keep an added-shaped dbt descriptor removal under --keep-deleted too
A stateful pull compares `.wmill`, not the working tree, so a descriptor
missing from that map still arrives as `added` while a real file with the
project's warehouse and run arguments sits on disk. Counting only `edited`
left that file deletable, and silently: the flag logged nothing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0174o6mGTWoanipUgf5zNcVL
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(cli): keep svelte component styles in the raw-app bundle
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test: fold svelte style guard into the plugin test file
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs: record the editor-parity constraint on the svelte css option
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test(cli): pin esbuild's service cwd before any test file chdirs
esbuild's node API captures process.cwd() when its module is first
imported and spawns its service with that cwd on every (re)start.
createBundle stops the service after each bundle, so the cwd is reused
across the whole run.
Several test files chdir into a temp dir and delete it afterwards. The
first one to bundle therefore pinned the service to a directory that
stopped existing, and the next test to reach esbuild died with
The service was stopped: ENOENT: no such file or directory,
posix_spawn '.../@esbuild/linux-x64/bin/esbuild'
The binary is present; ENOENT is posix_spawn rejecting the missing cwd.
Which file tripped it depended on bun's readdir order, so renaming an
unrelated test file was enough to surface it. Importing esbuild from the
preload pins the service to a cwd that outlives the run, independent of
file ordering.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G88YF3sZFnJZUvTLVjqhZc
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
* fix: keep raw-app files within their app folder on sync pull
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: validate raw-app file keys as stored, closing nul and duplicate-field bypasses
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: guard raw-app runnable ids too and fail closed on unparseable value
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: strip only a leading slash on raw-app file keys to match backend
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: strip only a leading slash on raw-app file keys to match backend
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
* test: assert the unpacked repo symlink without following it
`unpack_keeps_a_link_that_stays_in_the_repo` read through the link it had just
unpacked. Windows stores a symlink's target verbatim and its object manager
rejects the `/` in a POSIX one, so `read_to_string` came back with
`ERROR_INVALID_NAME` and the release's `cargo_test_windows` job was red.
Pin what the function is responsible for on every platform — the link is kept
and materialized — and read through it only where a POSIX relative target
resolves.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P3WRtxdKNGdomWX9vaAYGx
* test: key the cli sync-map fixtures with the platform separator
A sync map is keyed with the platform separator on both sides — `FSFSElement`
walks the tree with `path.join`, and the remote `ZipFSElement` starts at
`"." + SEP` and joins from there — while an `!inline` reference is always
forward-slash. `lock_dedup.ts` follows that convention; the fixtures did not,
so on Windows they built a map shape the CLI never produces and 12 of them
failed. `getTypeStrFromPath` is the same story: it matches
`"dependencies" + SEP`, and the test handed it a forward-slashed path.
Build the fixture keys through the separator, leaving the `!inline` references
and the `present` map forward-slash, as `sync.ts` hands them over.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P3WRtxdKNGdomWX9vaAYGx
* ci: skip the discord comment relay when the thread lookup returns none
A rate-limited or unauthorized Discord response carries no thread list, and
under `bash -e` that aborted the step — jq cannot iterate null, nor parse the
HTML error page Cloudflare answers a 429 with — before it reached the "thread
not found, skipping" branch right below. Three comment relays failed that way
on the 1.794.0 head.
Keep the step green for both, but tell them apart: a response with no thread
list is a delivery that was dropped for a reason worth seeing, so it warns with
the body it got, while a PR that genuinely has no thread stays quiet.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P3WRtxdKNGdomWX9vaAYGx
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(cli): deduplicate identical script lockfiles into one per language
* test: pin shared lockfile path classification
* fix(cli): never delete a lockfile the dedup plan also writes
* fix(cli): plan lockfile dedup from the whole tree, not the sync scope
* fix(cli): keep dedup out of dry runs and stop hiding scripts from its scan
* test: pin which files the shared-lock scan counts as readers
* fix(cli): validate shared-lock refs and let the majority keep its file
* fix(cli): snapshot shared-lock ownership before regeneration moves it
* fix(cli): address dedup review nits (dry-run push, json shape, scan scope)
* refactor(cli): put shared lockfiles in a top-level locks/ directory
* fix(cli): claim only the shared lock names windmill writes, and only when on
* fix(cli): read the lock field itself, and count only scripts sync reads
* fix(cli): parse metadata by its real format and lint from the sync root
* fix(cli): never re-hash a script whose generation failed
* fix(cli): share sync's walk exclusions and fail closed on unreadable dirs
* fix(cli): keep a lockfile the metadata on disk still references
* fix(cli): decide a lock is unread from the metadata field sync reads
* refactor(cli): name shared lockfiles after the dependency file they resolve
* fix(cli): carry shared lockfiles a narrowed sync cannot speak for
* fix(cli): move a shared lockfile when its dependency file moved, not on a head count
* fix(cli): let the many correct a shared lockfile a lone variant planted
* fix(cli): read why a lock differs from the stamp the worker writes into it
* fix(cli): let an agreeing majority speak whatever the stamps say
* docs(cli): count the disjuncts the comment introduces
* fix(cli): keep a private lock for any script the worker locks differently
* fix(cli): match annotations by the worker's own names, not by shape
* fix(cli): recognize the py: interpreter pin the macro does not cover
* fix(cli): let the map speak for dependency-file deletions
* perf(cli): group lock entries without rebuilding the group per insert
* fix(cli): defer shared-lock deletions until the metadata has settled
* fix(cli): decide shared-lock readers by the lock field, failing closed
* fix(cli): read folded lock refs and keep locks read by unparseable metadata
* refactor(cli): one shared-lock reader scan, shared by the pull and push paths
* fix(cli): keep nested dependency set names out of shared lockfiles
* fix(cli): drop a shared-lock scan gate that no real repo took
* fix(cli): keep script retention, debounce and cache settings on push
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(cli): surface the create response when the fixture fails
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(cli): drop debounce settings the CI build refuses to accept
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* repair the script push up-to-date comparison (#10743)
* test: settle the backlog before the capped audit-export drain (#10737)
* test: settle the backlog before the capped audit-export drain
* chore: update ee-repo-ref to bd4de74eb37b32a2b6c7c69f6dedac031ef8436b
This commit updates the EE repository reference after PR #730 was merged in windmill-ee-private.
Previous ee-repo-ref: b5a5f9114df26088cfe976d91f10e55ba8bfcaa6
New ee-repo-ref: bd4de74eb37b32a2b6c7c69f6dedac031ef8436b
Automated by sync-ee-ref workflow.
---------
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* fix(cli): repair the script push up-to-date comparison
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(cli): drain dependency jobs and pin a non-1 priority skip
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(cli): describe the priority fixture without the old comparison
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor(cli): read cache_ignore_s3_path off the typed response
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(cli): stop redeploying bunnative scripts on every push
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* fix: default an omitted app policy execution_mode to publisher
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: drop stale comments claiming execution_mode is required
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: derive a raw app's policy on deploy instead of trusting the caller's
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: pin the ee ref to the companion branch
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: vendor the raw-app policy derivation into the bundle job
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: note the vendored raw-app policy bundle
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: derive the policy on a value-only raw-source update too
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: reject raw-app runnables whose shape yields an unusable grant
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: cache the new policy query and tighten raw-app runnable validation
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: let the policy bundle drift guard survive a CRLF checkout
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to 23431f5cf1d627051ded89111bbf2e301e9db456
This commit updates the EE repository reference after PR #729 was merged in windmill-ee-private.
Previous ee-repo-ref: 0bdf8818fa115ad6b0d14f3117a18e8a580cce4d
New ee-repo-ref: 23431f5cf1d627051ded89111bbf2e301e9db456
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* test: wait for the app dependency job before pulling in repro_diffname
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test: reuse waitForDeploymentJobs and assert pulled lock files
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test: condense the dependency-job wait comment
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(cli): route fileset children to their parent resource on sync push
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(cli): scope fileset pointer validation to sync pushes
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(cli): error on script push of file/fileset resource content files
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(cli): enforce server-canonical fileset pointers and fail fast before apply
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(cli): resolve ws-specific fileset metadata and validate pointers before dry-run
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(cli): prefer workspace-specific fileset metadata over base file
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(cli): make fileset metadata lookup assertions platform-separator safe
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(cli): stop dropping fileset children whose names look like typed metadata
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(cli): exempt fileset children from the current-workspace classifier too
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(cli): attach the right job path to preview runs
* fix(cli): keep a deliberately-absent file in the directory it was named in
* fix(cli): resolve links for a path naming a file that is not there
* feat: add session recording to wmill app dev
* fix: harden dev recorder shell, bundle staleness guard and save route
* fix: keep dev-server recordings out of the raw app sync diff
* style: drop em dashes from new cli comments
* fix: tighten dev recorder save route origin, naming and io
* test: pin that only the root recordings folder is skipped
* fix: survive an oversized recording upload and match paths on windows
* fix: keep the app at the root and settle runnables stranded by a reload
* fix: make the recorder bundle hash stable on a crlf checkout
* docs: state the preflight-free content type the origin check guards
* test: build the sync-skip fixture with the platform separator
* docs: align the origin-guard test comment with the code
* chore: mark generated .gen.ts files as generated
* fix: make on_behalf_of control permissions for scripts and flows
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: inherit the recorded on-behalf-of identity when a preserving deploy omits it
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: keep an omitted permissioned_as from re-versioning an unchanged script
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: derive the on-behalf-of principal from the email and reject mismatched pairs
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: stop workspace deploys from carrying a source-workspace principal
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: correct the onBehalfOfPermissionedAs param doc
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test: pin that workspace deploys never carry a source-workspace principal
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: correct the omitted-principal contract and refresh generated prompts
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: keep external-superadmin principals on email-only redeploys
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: scope the recorded principal to its workspace and prefer real accounts
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: carry the recorded principal correctly through drafts and set-permissioned-as
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: sweep draft identity pairs on email change and offboarding
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: leave group identities alone when sweeping a user's email
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: treat only g/ without an email as a group, and match the offboard preview
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: stop the group guard from skipping rows with no recorded principal
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: state the group guard once instead of restating it
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor: make the permissioned_as the only stored on-behalf-of identity
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* perf: skip resolving the on-behalf-of address for sync clients that discard it
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: address the local review of the identity refactor
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: resolve the on-behalf-of identity coherently across clones, offboarding and no-op deploys
* test: pin that a fork keeps only the on-behalf-of identities that resolve in it
* fix: decide a principal prefix-first everywhere and canonicalize bare addresses
* fix: prefix a slash-containing address so a reader cannot take it for a group
* fix: read an address as a username before the group- convention
* fix: rewrite the canonical principal when an account's address moves
* fix: keep the address form of a principal to accounts without a usr row
* fix: reject an identity a job row cannot carry and read it uncached at dispatch
* fix: count characters against the job identity width and cap the backfill
* refactor: name the script/flow principal on_behalf_of, as apps do
* docs: state the caller-must-authorize contract on the identity resolvers
* fix: keep writing on_behalf_of_email until every worker reads the principal
* fix: err high on the compatibility version and document the last resolver
* fix: keep the compatibility address current through identity mutations
* fix: carry the compatibility address with the principal on every copy path
* chore: re-pin the EE ref to the companion branch merged with EE main
* fix: key the dbt retry lookup on the stored principal
* fix: keep a mixed-version address recoverable through a fork
* fix: read a round-tripped address uncached so a redeploy is not rejected
* fix: refuse an email change that would make a principal unenqueueable
* chore: update ee-repo-ref to ac3d7d015296f041ae44ab6bc4953485f44d36e4
This commit updates the EE repository reference after PR #704 was merged in windmill-ee-private.
Previous ee-repo-ref: 219b0b03905a1a0028054b3a4985724e77d09036
New ee-repo-ref: ac3d7d015296f041ae44ab6bc4953485f44d36e4
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* fix(dbt): a project does not collide with its own descriptor
* fix(dbt): a descriptor skips its own project, not an ordinary sibling
* fix(dbt): the path a collision is judged on is the one both layouts deploy to