#22

`rake lint`: lints for `view!`, GPUI and Rake, written to teach agents

Open1byeopened 6d ago

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 uses style={|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 an aria-label.
  • GPUI code that drops a Task (which cancels it), forgets cx.notify() after changing state, creates entities inside render, 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:

  1. Diagnostics in the terminal, in rustc's style, with source spans.
  2. rake lint --json for agents and editors, matching the CLI's other --json output, with machine-applicable fixes where possible.
  3. An agent guide. rake lint --explain <ID> prints one rule, and rake lint --guide writes 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

TierHow it worksToolchainSpeed
Syntax (default)Parses source with syn, reusing the view! parser from rake-macrosStable Rust, no buildFast enough for every save
Deep (--deep)Type-aware Dylint lints, like the existing spacing setupPinned nightly, installed by rake lint itselfNeeds 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

AreaRuleTierAuto-fix
view!Style closure that a class can expressSyntaxYes
view!Hard-coded color or size when the app installs a themeSyntaxSometimes
view!Loop item ID built from the loop index instead of a data keySyntaxNo
view!Icon-only or dynamic button without aria-labelSyntaxNo
view!Image without meaningful alt (use alt="" for decorative images)SyntaxNo
GPUITask dropped without .detach() or being storedDeepNo
GPUIState changed in a listener with no cx.notify()DeepSometimes
GPUIcx.new(...) inside renderDeepNo
GPUIUpdating an entity from inside its own updateDeepNo
GPUIBlocking I/O or block_on on the UI threadDeepNo
RakePicker, clipboard or download started outside a user event handlerDeepNo
RakeGuarded quit awaited inside an actionDeepNo
Rakedevtools or development control enabled in a release profileSyntax (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, warn or deny. 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

  1. Where the parser lives: expose the view! parser from rake-macros as a small library crate, or keep the linter inside rake-cli.
  2. Whether the deep tier is worth a nightly toolchain for app developers, or should stay opt-in for CI only.
  3. Which GPUI rules should wait for the move from GPUI CE to gpui-pre, since some APIs differ.
  4. Whether rake dev should 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 separate rake fmt.
  • Replacing Clippy or rustc lints. rake lint covers only what those can't know about Rake and GPUI.

Done when

  • rake lint runs the syntax tier on stable Rust with no build, and rake lint --deep runs 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.
  • --json output is documented and tested, and the --fix fixes are tested.
  • rake lint --guide produces the agent skill file, and CI checks that the committed copy matches the catalogue.
  • CI runs rake lint on the examples.
  • Docs cover the commands, configuration and how to add a rule.

Activity

2
1bye5d ago

Syntax tier done in #30 (merged):

  • rake lint with view-button-label, view-image-alt and view-loop-index-id;
  • suppressions with reasons;
  • [package.metadata.rake.lint] config;
  • the shared rake-view-syntax parser.

Remaining: the GPUI and Rake rule tiers, and lint fixes such as extracting long closures into let bindings. Formatting is tracked separately in #31.

1bye5d ago

@Bluesachinkr thanks I will take a look!