EE files (*_ee.rs, *_ee.ts, *_ee.svelte) are symlinks from windmill-ee-private that are gitignored. The walker skipped them because it respects .gitignore. This adds a separate recursive scan for _ee files and merges them into the index. Also fixes outline resolving symlinks via canonicalize, causing path mismatches. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
4.7 KiB
Windmill
Open-source platform for internal tools, workflows, API integrations, background jobs, and UIs. Rust backend + Svelte 5 frontend.
Workflow
- Understand: Before coding, explore the codebase (see Code Navigation below). Use
outlineto understand file structure,bodyto read specific symbols,def/callers/calleesto trace code,Grepto find usages. Readdocs/for domain context. - Plan: For non-trivial changes, use plan mode. For large features, break into reviewable stages
- Execute: Follow coding patterns from skills (
rust-backend,svelte-frontend) - Validate: After every change, run the appropriate checks per
docs/validation.md
Documentation
- Validation:
docs/validation.md— what checks to run based on what you changed - Enterprise:
docs/enterprise.md— EE file conventions and PR workflow - Backend patterns: use the
rust-backendskill when writing Rust code - Frontend patterns: use the
svelte-frontendskill when writing Svelte code. Do NOT edit svelte files unless you have read that skill. - Code review: use
/local-reviewto review a PR for bugs and CLAUDE.md compliance - Domain guides:
.claude/skills/native-trigger/andfrontend/tutorial-system-guide.mdc - Brand/UI guidelines:
frontend/brand-guidelines.md
Dev Environment
- Backend:
cargo runfrombackend/(API at http://localhost:8000) - Frontend:
REMOTE=http://localhost:8000 npm run devfromfrontend/(port 3000+) - DB:
psql postgres://postgres:changeme@localhost:5432/windmill - Login:
admin@windmill.dev/changeme - Instance settings: navigate to
/#superadmin-settings
Banned Patterns
$bindable(default_value) on optional props
Using $bindable(default_value) on props that can be undefined is banned. This pattern causes subtle bugs because the default value masks the undefined state.
Bad:
let { my_prop = $bindable(default_value) }: { my_prop?: string } = $props()
Correct alternatives:
-
Use
$derivedwith nullish coalescing — handle the potentialundefinedat the usage site:let { my_prop = $bindable() }: { my_prop?: string } = $props() let effective_value = $derived(my_prop ?? default_value) -
Create a
useMyPropState()helper — encapsulate the undefined-handling logic in a reusable function and call it higher in the component tree, so the child component always receives a defined value.
Code Navigation
wm-ts-nav is an AST-aware code navigator. Use wm-ts-nav for structural queries — it skips comments/strings and understands symbol boundaries.
MUST use outline before Read on unfamiliar files — a 500-line file costs ~500 lines of context, while outline costs ~20. Then MUST use body "X" instead of reading a full file to see one function/struct. Use Read with offset/limit only when you need surrounding context that body doesn't capture.
refs "X" --callerinstead of reading files to find which function contains each referencecallers "X"/callees "X"for call-graph questions
EE files (*_ee.rs, *_ee.ts, *_ee.svelte) are indexed — you can outline, def, body, refs etc. on them just like regular files.
NAV="sh wm-ts-nav/nav"
# Use --root backend for Rust, --root frontend/src for TS/Svelte
$NAV --root backend outline backend/path/to/file.rs # file structure
$NAV --root backend def "ServiceName" # find definition
$NAV --root backend body "decrypt_oauth_data" # extract source code
$NAV --root backend search "%" --parent ServiceName # methods on a type
$NAV --root backend search "Trigger" --kind struct # find by kind
$NAV --root backend refs "X" --file handler.rs --caller # scoped refs with caller
$NAV --root backend callers "X" # who calls X?
$NAV --root backend callees "X" # what does X call?
Limitations — syntax-level analysis, no type inference. Use Grep instead when completeness matters (finding all usages, exhaustiveness checks):
refs/callers/calleescan't follow re-exports, glob imports, or different import paths to the same symbol- Trait impls, macro-generated symbols (
sqlx::FromRow), and namespace member access (ns.X) are invisible calleesshows all identifiers in a function body, not just actual calls
Core Principles
- MUST
outlinebeforeReadon unfamiliar files — thenbodyorReadwith offset/limit for specifics - Search for existing code to reuse before writing new code
- Follow established patterns in the codebase
- Keep changes focused — don't refactor beyond what's asked