mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-19 00:02:03 +00:00
114c41251a
* feat(frontend): improve AI chat cancel and interrupted-turn handling - Escape stops the in-flight generation when focus is on the chat (composer, messages, panel) — capture-phase listener so neither the session Monaco editor nor mounted-but-closed modals swallow the key - When a turn yields no output (or is cancelled before any), roll it back and restore the message to the composer - When a turn is cancelled or fails mid-way, keep the completed tool-paired steps and the partial answer text as context so a follow-up like "continue" picks up from there - Animate the thinking-block collapse like tool boxes (slide 150ms) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): address review findings on interrupted-turn handling - Guard the catch in sendRequest so a post-commit throw (e.g. saveChat) cannot commit the turn a second time or mis-flag the user message - Delete the persisted chat entry when rolling back a first turn empties the transcript (saveChat no-ops on empty, leaving a stale entry) - restoreInstructions skips when the user already typed a new draft - Use stopImmediatePropagation so one Escape on body focus cannot cancel several mounted chat panels at once Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(frontend): tighten comments and dedupe tests in AI chat changes Keep each invariant comment once at the place it would be broken; drop narration and repeated rationale. Remove near-duplicate test cases (chatLoop boundary permutations, cancel-before-output subset). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: add comment policy to AGENTS.md core principles Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: comments must describe current code, not PR drafting history Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): treat completed reasoning-only turns as unsent A turn that finishes without abort but emits only reasoning produced a display bubble, so the empty-turn rollback (keyed on display output) skipped it and the user message was silently swallowed. Key the decision off usable output instead. Also trim comment blocks to the AGENTS.md 4-line norm, splitting rationale to its break-site. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
114 lines
8.2 KiB
Markdown
114 lines
8.2 KiB
Markdown
# Windmill
|
|
|
|
Open-source platform for internal tools, workflows, API integrations, background jobs, and UIs. Rust backend + Svelte 5 frontend.
|
|
|
|
## Workflow
|
|
|
|
1. **Understand**: Before coding, explore the codebase (see Code Navigation below). Use `outline` to understand file structure, `body` to read specific symbols, `def`/`callers`/`callees` to trace code, `Grep` to find usages. Read `docs/` for domain context.
|
|
2. **Plan**: For non-trivial changes, use plan mode. For large features, break into reviewable stages
|
|
3. **Execute**: Follow coding patterns from skills (`rust-backend`, `svelte-frontend`)
|
|
4. **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-backend` skill when writing Rust code
|
|
- **Frontend patterns**: use the `svelte-frontend` skill when writing Svelte code. Do NOT edit svelte files unless you have read that skill.
|
|
- **Frontend UUIDs**: do not call `crypto.randomUUID()` in frontend code. Import `randomUUID` from `$lib/utils/uuid` instead.
|
|
- **Code review**: review the current PR or branch against the shared review policy in `REVIEW.md` (severity triage, public-surface checklist, AGENTS.md compliance, test-coverage assessment). The skill at `.agents/skills/local-review/SKILL.md` orchestrates it. All three CLIs auto-discover the same SKILL — Claude reads `.claude/skills/` (symlinked to the canonical `.agents/skills/` file), Codex and Pi read `.agents/skills/` directly. Invoke with `/local-review` in Claude Code, `$local-review` (or `/skills` selector) in Codex, or `pi --skill local-review` / `/skill:local-review` in Pi.
|
|
- **Domain guides**: `.claude/skills/native-trigger/` and `frontend/tutorial-system-guide.mdc`
|
|
- **Brand/UI guidelines**: `frontend/brand-guidelines.md`
|
|
- **CLI commands**: when adding/modifying/removing a command, subcommand, option, or description in `cli/src/commands/`, run `python system_prompts/generate.py` to refresh `system_prompts/auto-generated/` and `cli/src/guidance/skills.gen.ts`. The CLI docs the agents use to operate `wmill` are derived from the source — stale generated files give agents the wrong flags.
|
|
|
|
## Dev Environment
|
|
|
|
- **Backend**: `cargo run` from `backend/` (API at http://localhost:8000)
|
|
- **Frontend**: `REMOTE=http://localhost:8000 npm run dev` from `frontend/` (port 3000+)
|
|
- **DB**: `psql postgres://postgres:changeme@localhost:5432/windmill`
|
|
- **Login**: `admin@windmill.dev` / `changeme`
|
|
- **Instance settings**: navigate to `/#superadmin-settings`
|
|
- **Migrations**: use `cargo sqlx migrate add -r <name>` from `backend/` to create new migrations (never generate timestamps manually)
|
|
|
|
## Verifying Frontend Changes
|
|
|
|
After modifying frontend code, drive the running dev server with the **Playwright MCP** to verify the change in a real browser — don't claim a UI change works without exercising it.
|
|
|
|
Two MCP servers are registered in `.mcp.json`:
|
|
- `playwright` — headless Chromium, default for devboxes (no display required)
|
|
- `playwright-headed` — windowed Chromium, when a display is available
|
|
|
|
**One-time setup:** run `npx playwright install chromium` to download the browser binary (Playwright won't fetch it automatically on first use).
|
|
|
|
Typical flow:
|
|
1. Ensure backend (`cargo run`) and frontend (`REMOTE=http://localhost:8000 npm run dev`) are running
|
|
2. `mcp__playwright__browser_navigate` to the relevant page (login at `admin@windmill.dev` / `changeme`)
|
|
3. `mcp__playwright__browser_snapshot` to inspect the accessibility tree (preferred over screenshots for reading the DOM)
|
|
4. `mcp__playwright__browser_click` / `browser_fill_form` / `browser_type` to interact
|
|
5. `mcp__playwright__browser_take_screenshot` for visual confirmation
|
|
6. `mcp__playwright__browser_console_messages` / `browser_network_requests` to surface errors
|
|
|
|
**Attach the screenshots to the PR.** For any change under `frontend/`, embed screenshots of the affected UI in the PR body — the `pr` skill requires this and carries the upload recipe.
|
|
|
|
If you cannot exercise a UI change (no dev server, etc.), say so explicitly rather than claiming success.
|
|
|
|
## 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:**
|
|
|
|
```svelte
|
|
let { my_prop = $bindable(default_value) }: { my_prop?: string } = $props()
|
|
```
|
|
|
|
**Correct alternatives:**
|
|
|
|
1. **Use `$derived` with nullish coalescing** — handle the potential `undefined` at the usage site:
|
|
|
|
```svelte
|
|
let { my_prop = $bindable() }: { my_prop?: string } = $props()
|
|
let effective_value = $derived(my_prop ?? default_value)
|
|
```
|
|
|
|
2. **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" --caller` instead of reading files to find which function contains each reference
|
|
- `callers "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.
|
|
|
|
```bash
|
|
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`/`callees` can'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
|
|
- `callees` shows all identifiers in a function body, not just actual calls
|
|
|
|
## Core Principles
|
|
|
|
- **MUST `outline` before `Read`** on unfamiliar files — then `body` or `Read` with 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
|
|
- **Comments record constraints, not narration.** Write a comment only for what the code can't show: why a non-obvious approach is required, what breaks if it's "simplified" away. State each invariant once, at the place where someone would break it, in ≤4 lines. Don't describe what the next line does, don't repeat the same rationale at multiple sites, and don't address the PR reviewer (justifying a change belongs in the PR description, not the code). Describe the code as it is, never its drafting history: "we no longer do X", "unchanged behavior", "instead of the previous approach" are meaningless to a reader who never saw the earlier iteration — before finishing, reread your comments as if the current state is the only state that ever existed.
|
|
- **Never attribute work to a specific customer, account, or "requested by a customer" in repo-tracked content** (PR descriptions, commit messages, code comments, docs). Describe changes by their technical motivation instead.
|