|
| 1 | +# WebUI — Copilot Instructions |
| 2 | + |
| 3 | +You are working on **WebUI**, a high-performance server-side rendering framework written in Rust that operates without JavaScript runtimes. It separates static and dynamic content at build time into a binary protocol that enables fast rendering in any host language. |
| 4 | + |
| 5 | +Read and internalize these instructions at the start of every session. They are non-negotiable. |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## Context you must load first |
| 10 | + |
| 11 | +Before suggesting or applying **any** change, read these files — they are the ground truth: |
| 12 | + |
| 13 | +1. **`DESIGN.md`** — The living technical specification. Architecture, protocol schema, module contracts, and behavioral rules all live here. Treat every constraint in it as mandatory unless the user explicitly asks to change one. |
| 14 | +2. **`Cargo.toml`** (workspace root) — Workspace members, dependency versions, and release profile. |
| 15 | +3. **`clippy.toml`** — Lint policy (bans `unwrap`/`expect`, caps cognitive complexity at 20, limits function arguments to 5). |
| 16 | +4. **`deny.toml`** — Allowed licenses and advisory ignore-list. |
| 17 | +5. The specific crate(s) under `crates/` that are relevant to the task at hand. |
| 18 | + |
| 19 | +--- |
| 20 | + |
| 21 | +## The one rule that gates everything |
| 22 | + |
| 23 | +Before creating **any** commit, run: |
| 24 | + |
| 25 | +```bash |
| 26 | +cargo xtask check |
| 27 | +``` |
| 28 | + |
| 29 | +This executes, in order: `fmt → clippy → deny → test → build`. Work is **not done** until this passes cleanly. If it fails, fix every reported issue before proceeding. No exceptions. |
| 30 | + |
| 31 | +--- |
| 32 | + |
| 33 | +## Performance is the top priority |
| 34 | + |
| 35 | +Every decision — API design, data structure choice, algorithm, error path — must be evaluated through a performance lens first. WebUI's value proposition is speed; nothing else matters if it is slow. |
| 36 | + |
| 37 | +### Hard constraints |
| 38 | + |
| 39 | +| Rule | Rationale | |
| 40 | +|------|-----------| |
| 41 | +| **No recursion** in core algorithms | Use iterative loops with an explicit stack. Recursion blows the call stack on deep templates and defeats branch prediction. | |
| 42 | +| **No regular expressions** in core logic | Deterministic scanners are faster and more predictable. | |
| 43 | +| **Minimal runtime computation** | Move work to build time (the `webui build` CLI step) whenever possible. | |
| 44 | +| **Protobuf binary serialization** via `prost` | Zero-copy decoding; JSON is for `webui inspect` debugging only. | |
| 45 | +| **Buffer consolidation** | Reuse buffers, pre-allocate, avoid unnecessary allocations. | |
| 46 | + |
| 47 | +### Allocation discipline |
| 48 | + |
| 49 | +- `Vec::with_capacity` / `String::with_capacity` when size is known or estimable. |
| 50 | +- `push_str` / `write!` into existing buffers — never `format!` in hot paths. |
| 51 | +- No unnecessary `.clone()` — pass `&str`, `&[T]`, or slices. Use `Cow<'_, str>` when a value is sometimes borrowed, sometimes owned. |
| 52 | +- Prefer explicit state machines and stack-based traversal over recursive AST walking. |
| 53 | + |
| 54 | +### Measurement |
| 55 | + |
| 56 | +- Identify hot paths first: parsing, expression evaluation, handler rendering, protocol serialization, state lookups. |
| 57 | +- Measure **before** changing anything: `cargo bench -p <crate>` in `--release` mode. |
| 58 | +- After the change, re-measure and report the delta (qualitatively if exact numbers aren't available). |
| 59 | +- The smallest safe change that improves CPU time, allocation count, or cache locality wins. Do not over-engineer. |
| 60 | + |
| 61 | +--- |
| 62 | + |
| 63 | +## Rust architecture standards |
| 64 | + |
| 65 | +### Error handling |
| 66 | +- Library crates (`webui-parser`, `webui-handler`, `webui-expressions`, `webui-state`, `webui-protocol`, `webui-ffi`) use **custom error enums** via `thiserror`. |
| 67 | +- Binary crates (`webui-cli`, `xtask`) may use `anyhow`. |
| 68 | +- **No `unwrap()` or `expect()`** in library code — `clippy.toml` enforces this. |
| 69 | +- Errors must be **actionable**: tell the caller what went wrong *and* what they can do about it. |
| 70 | + |
| 71 | +### Public API surface |
| 72 | +- Expose the minimum necessary. Use `pub(crate)` for internal helpers. |
| 73 | +- New public functions, structs, traits, and error variants must be documented with `///` doc-comments. |
| 74 | +- Use `#[must_use]` on functions whose return value should not be silently discarded. |
| 75 | + |
| 76 | +### Code organization |
| 77 | +- One concern per module. When a file approaches ~400 lines, split it. |
| 78 | +- Types are `PascalCase`, functions are `snake_case`, constants are `SCREAMING_SNAKE_CASE`. |
| 79 | +- `cargo fmt --all` is the sole formatting authority — never override or disable it. |
| 80 | + |
| 81 | +### Dependencies |
| 82 | +- Keep them minimal. Prefer the standard library. Before adding a crate, justify why std doesn't suffice. |
| 83 | +- Every dependency must pass `cargo deny check` (license allowlist + security advisories). |
| 84 | +- Prefer well-maintained crates under MIT or Apache-2.0. |
| 85 | + |
| 86 | +### Safety |
| 87 | +- No `unsafe` without a `// SAFETY:` comment explaining the invariant upheld. |
| 88 | +- Prefer zero-copy borrowing over cloning. Prefer slices over owned collections when lifetime allows. |
| 89 | +- Keep cognitive complexity under 20 per function (enforced by clippy). Refactor, don't suppress. |
| 90 | + |
| 91 | +### Concurrency (when applicable) |
| 92 | +- State explicit `Send + Sync` bounds. |
| 93 | +- Prefer `tokio::sync::mpsc` channels over `Arc<Mutex<_>>`. |
| 94 | +- Use atomics when a mutex is overkill. |
| 95 | + |
| 96 | +--- |
| 97 | + |
| 98 | +## Tests are mandatory |
| 99 | + |
| 100 | +Every code change ships with tests. No exceptions. |
| 101 | + |
| 102 | +| Scenario | Requirement | |
| 103 | +|----------|-------------| |
| 104 | +| New public API | At least one unit test per function/method. | |
| 105 | +| Bug fix | A regression test that **fails** without the fix. | |
| 106 | +| Performance change | Benchmark comparison (before/after). | |
| 107 | +| Refactor | Existing tests must continue to pass unchanged. | |
| 108 | + |
| 109 | +- Unit tests live alongside code in `#[cfg(test)]` modules. |
| 110 | +- Integration tests go in each crate's `tests/` directory. |
| 111 | +- Run the **targeted** crate first (`cargo test -p <crate>`), then the full workspace (`cargo test --workspace`). |
| 112 | +- Never remove, weaken, or `#[ignore]` an existing test unless the user explicitly asks. |
| 113 | + |
| 114 | +--- |
| 115 | + |
| 116 | +## DESIGN.md is the living specification |
| 117 | + |
| 118 | +`DESIGN.md` is not documentation — it **is** the specification. Code implements what `DESIGN.md` describes. |
| 119 | + |
| 120 | +- **Read it** before any architectural or API change. |
| 121 | +- **Update it** in the same commit whenever you add, remove, or modify a public API, protocol field, fragment type, error variant, or behavioral contract. |
| 122 | +- Keep its Rust code examples conceptually compilable and in sync with real code. |
| 123 | +- If `DESIGN.md` and the code disagree, that is a bug — fix both. |
| 124 | + |
| 125 | +--- |
| 126 | + |
| 127 | +## Developer docs (`/docs`) stay current |
| 128 | + |
| 129 | +The `docs/` directory is a VitePress site for external developers consuming WebUI. |
| 130 | + |
| 131 | +- Any change to user-visible behavior, CLI usage, or public API **must** include a corresponding docs update in the same PR. |
| 132 | +- New features get a guide page (`docs/guide/`) or tutorial (`docs/tutorials/`). |
| 133 | +- Verify with `cd docs && pnpm build` when possible. |
| 134 | + |
| 135 | +--- |
| 136 | + |
| 137 | +## Branch and commit discipline |
| 138 | + |
| 139 | +- **Never commit to `main` directly.** Create a branch: `<user>/<short-description>` (e.g. `mmansour/optimize-handler-allocs`). |
| 140 | +- One logical change per commit. Write imperative messages: *"Add …"* not *"Added …"*. |
| 141 | + |
| 142 | +--- |
| 143 | + |
| 144 | +## Commands reference |
| 145 | + |
| 146 | +| What | Command | |
| 147 | +|------|---------| |
| 148 | +| **Full gate (run before every commit)** | `cargo xtask check` | |
| 149 | +| Format | `cargo xtask fmt` | |
| 150 | +| Lint | `cargo xtask clippy` | |
| 151 | +| License & advisory audit | `cargo xtask deny` | |
| 152 | +| Tests (workspace) | `cargo xtask test` | |
| 153 | +| Build (workspace) | `cargo xtask build` | |
| 154 | +| Test a single crate | `cargo test -p webui-parser` (or any crate name) | |
| 155 | +| Benchmark a crate | `cargo bench -p webui-protocol` (or any crate name) | |
| 156 | +| Build in release mode | `cargo build --release` | |
| 157 | +| Docs site | `cd docs && pnpm build` | |
| 158 | + |
| 159 | +--- |
| 160 | + |
| 161 | +## FFI boundary (`webui-ffi`) |
| 162 | + |
| 163 | +The FFI crate exposes WebUI to **any** host language (C, C#, Go, Ruby, Python, Node.js, etc.) via a C-compatible ABI. Treat it as the project's most sensitive surface. |
| 164 | + |
| 165 | +- Every `pub extern "C" fn` must have a `# Safety` doc section explaining pointer validity, lifetime, and ownership expectations. |
| 166 | +- All `unsafe` blocks require a `// SAFETY:` comment. No exceptions. |
| 167 | +- Assume callers are in a different language with no Rust safety net — validate every input (null pointers, invalid UTF-8, out-of-range values) before dereferencing or converting. |
| 168 | +- Never panic across the FFI boundary. Catch all errors and return them as error codes or null pointers. A panic in FFI is undefined behavior. |
| 169 | +- C header generation is handled by `cbindgen` in `build.rs`. After changing any `#[no_mangle]` function signature, verify the generated header in `include/webui_ffi.h` is correct. |
| 170 | +- Keep the FFI surface minimal and stable — additions are easy, removals break every consumer. |
| 171 | +- Platform-specific code must be gated behind `#[cfg(target_os = "...")]` and every platform path must be tested or at least compile-checked. |
| 172 | +- Design for ABI stability: prefer opaque pointers and integer error codes over exposing Rust struct layouts. |
| 173 | + |
| 174 | +--- |
| 175 | + |
| 176 | +## Protobuf schema evolution |
| 177 | + |
| 178 | +The protocol is defined in `crates/webui-protocol/proto/webui.proto` and compiled by `prost` via `build.rs`. Schema changes cascade through the entire stack: **protocol → handler → FFI → CLI**. |
| 179 | + |
| 180 | +- Never remove or renumber existing proto fields — mark them `reserved` instead. |
| 181 | +- Add new fields as optional with sensible defaults so older serialized data remains decodable. |
| 182 | +- After any `.proto` change, rebuild the full workspace (`cargo xtask build`) and run all tests — not just the protocol crate. |
| 183 | +- Update `DESIGN.md` protocol section in the same commit. |
| 184 | + |
| 185 | +--- |
| 186 | + |
| 187 | +## Workspace dependency management |
| 188 | + |
| 189 | +All third-party dependency versions are centralized in the root `Cargo.toml` under `[workspace.dependencies]`. |
| 190 | + |
| 191 | +- **Never** add a dependency version directly in a crate-level `Cargo.toml`. Use `dep = { workspace = true }` instead. |
| 192 | +- Before adding any new dependency, check if the standard library or an existing workspace dependency covers the need. |
| 193 | +- New dependencies must pass `cargo deny check` (license allowlist + advisory audit). |
| 194 | + |
| 195 | +--- |
| 196 | + |
| 197 | +## Release profile awareness |
| 198 | + |
| 199 | +The workspace ships with an aggressive release profile (`Cargo.toml`): |
| 200 | + |
| 201 | +```toml |
| 202 | +[profile.release] |
| 203 | +lto = true # Full link-time optimization |
| 204 | +codegen-units = 1 # Maximum optimization (slower compile) |
| 205 | +panic = "abort" # No unwinding — smaller binary, but panics terminate immediately |
| 206 | +strip = true # Strip debug symbols |
| 207 | +``` |
| 208 | + |
| 209 | +- **`panic = "abort"` means panics kill the process instantly** — reinforcing why `unwrap`/`expect` are banned in library code. |
| 210 | +- Always validate performance claims in `--release` mode. Debug builds are not representative. |
| 211 | +- Be aware that LTO + single codegen unit makes release builds slow. Use `cargo test` (debug) for iteration, `cargo build --release` for final validation. |
| 212 | + |
| 213 | +--- |
| 214 | + |
| 215 | +## Shared test utilities (`webui-test-utils`) |
| 216 | + |
| 217 | +The `webui-test-utils` crate provides common test helpers, builders, and fixtures. |
| 218 | + |
| 219 | +- Before writing new test helpers, check if `webui-test-utils` already has what you need. |
| 220 | +- New shared test utilities belong in `webui-test-utils`, not duplicated across crates. |
| 221 | +- Add it as a `[dev-dependencies]` entry: `webui-test-utils = { path = "../webui-test-utils" }`. |
| 222 | + |
| 223 | +--- |
| 224 | + |
| 225 | +## Acceptance checklist |
| 226 | + |
| 227 | +Before finishing any task, confirm **all** of these: |
| 228 | + |
| 229 | +- [ ] `cargo xtask check` passes. |
| 230 | +- [ ] Changes include or update tests. |
| 231 | +- [ ] `DESIGN.md` is updated if any contract changed. |
| 232 | +- [ ] `docs/` is updated if any user-facing behavior changed. |
| 233 | +- [ ] No new recursion or regex in core paths. |
| 234 | +- [ ] No new `unwrap`/`expect` in library code. |
| 235 | +- [ ] No unnecessary allocations introduced; buffers reused where possible. |
| 236 | +- [ ] FFI changes include `# Safety` docs and never panic across the boundary. |
| 237 | +- [ ] Proto schema changes are backward-compatible and cascade-tested. |
| 238 | +- [ ] New dependencies use `workspace = true` and pass `cargo deny check`. |
| 239 | +- [ ] Commit is on a feature branch, not `main`. |
| 240 | +- [ ] Commit message is imperative with Copilot co-author trailer. |
0 commit comments