Problem
Most mistakes in Rake apps compile fine and only show up at runtime, in review, or never. AI agents make the same ones over and over, because nothing in the toolchain tells them the rules:
view!code that usesstyle={|s| ...}closures where a class would work, hard-codes colors instead of using the theme, keys loop items by index, or leaves icon buttons without anaria-label.- GPUI code that drops a
Task(which cancels it), forgetscx.notify()after changing state, creates entities insiderender, or updates an entity while it's already being updated (which panics). - Rake code that starts file pickers or clipboard requests from a loader instead of a user event, waits for guarded quit inside an action, or bypasses Rake's managed tasks.
Many of these rules are already written in docs/experimental-view.md, docs/platform-services.md and docs/managed-work.md, but only someone who reads the docs sees them. The view! macro catches syntax errors and missing loop IDs at compile time, but it can't see style or patterns. The only project linter today is the Dylint spacing check, which is for this repo, not for apps.
Proposal
Add rake lint, a project linter built around one catalogue of rules. Each rule has a stable ID, an explanation, and a before-and-after example. The same catalogue produces three things, so they can't drift apart:
- Diagnostics in the terminal, in rustc's style, with source spans.
rake lint --jsonfor agents and editors, matching the CLI's other--jsonoutput, with machine-applicable fixes where possible.- An agent guide.
rake lint --explain <ID>prints one rule, andrake lint --guidewrites the whole catalogue as an agent skill file (for example.agents/skills/rake-gpui/SKILL.md). The guide is generated, not hand-written.
Messages are written for both people and agents: what's wrong, why it matters, and the exact fix.
Two tiers
| Tier | How it works | Toolchain | Speed |
|---|---|---|---|
| Syntax (default) | Parses source with syn, reusing the view! parser from rake-macros | Stable Rust, no build | Fast enough for every save |
Deep (--deep) | Type-aware Dylint lints, like the existing spacing setup | Pinned nightly, installed by rake lint itself | Needs a build |
Most view! rules fit the syntax tier. Rules that need types, such as "this Task is dropped", go in the deep tier.
First rules
| Area | Rule | Tier | Auto-fix |
|---|---|---|---|
view! | Style closure that a class can express | Syntax | Yes |
view! | Hard-coded color or size when the app installs a theme | Syntax | Sometimes |
view! | Loop item ID built from the loop index instead of a data key | Syntax | No |
view! | Icon-only or dynamic button without aria-label | Syntax | No |
view! | Image without meaningful alt (use alt="" for decorative images) | Syntax | No |
| GPUI | Task dropped without .detach() or being stored | Deep | No |
| GPUI | State changed in a listener with no cx.notify() | Deep | Sometimes |
| GPUI | cx.new(...) inside render | Deep | No |
| GPUI | Updating an entity from inside its own update | Deep | No |
| GPUI | Blocking I/O or block_on on the UI thread | Deep | No |
| Rake | Picker, clipboard or download started outside a user event handler | Deep | No |
| Rake | Guarded quit awaited inside an action | Deep | No |
| Rake | devtools or development control enabled in a release profile | Syntax (manifest) | Yes |
Each rule needs a corpus of real cases from the examples before it ships. A noisy rule is worse than none: agents will either obey false positives or learn to ignore all warnings.
Configuration and suppression
- Each rule has a default level:
allow,warnordeny. Projects can change it in their Rake config. - Inline suppression with a reason, for example
// rake-lint: allow(view-index-key) rows never reorder. A suppression without a reason is itself a warning. - Decide whether the deep tier also accepts
#[allow(...)]attributes, since Dylint supports them.
Open questions
- Where the parser lives: expose the
view!parser fromrake-macrosas a small library crate, or keep the linter insiderake-cli. - Whether the deep tier is worth a nightly toolchain for app developers, or should stay opt-in for CI only.
- Which GPUI rules should wait for the move from GPUI CE to
gpui-pre, since some APIs differ. - Whether
rake devshould run the syntax tier on every rebuild and show results in the dev session and DevTools.
Out of scope
- Formatting
view!markup. That would be a separaterake fmt. - Replacing Clippy or rustc lints.
rake lintcovers only what those can't know about Rake and GPUI.
Done when
rake lintruns the syntax tier on stable Rust with no build, andrake lint --deepruns the type-aware rules.- At least the
view!rules above ship, each with tests for both firing and not firing, and the examples pass or have reasoned suppressions. --jsonoutput is documented and tested, and the--fixfixes are tested.rake lint --guideproduces the agent skill file, and CI checks that the committed copy matches the catalogue.- CI runs
rake linton the examples. - Docs cover the commands, configuration and how to add a rule.
Activity
2@Bluesachinkr thanks I will take a look!
Syntax tier done in #30 (merged):
rake lintwith view-button-label, view-image-alt and view-loop-index-id;[package.metadata.rake.lint]config;rake-view-syntaxparser.Remaining: the GPUI and Rake rule tiers, and lint fixes such as extracting long closures into
letbindings. Formatting is tracked separately in #31.