agentleFS
MicrosoftKnown publishermicrosoft/webui/.github/skills/diagnostics/SKILL.md

diagnostics

Skill100 starsChanged 36 days agoMITView on GitHub

Error handling and build-time diagnostics conventions - Result-not-panic, structured Diagnostics with stable codes, actionable help, color/JSON presentation layering, exit codes, and cold-path performance.

We split this skill into the rules it gives an agent

6 rules. 0 are common, 0 are distinctive, and 6 are very specific to this project.

  • 0

    Common

    Most of the 3 edited versions we read keep these.

  • 0

    Distinctive

    This author's own choices. Worth a look.

  • 6

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Use this skill whenever you add, change, or review an error path: a build-time authoring error, a parser/handler failure, a CLI validation …

    line 8
  2. Very specific:

    Add dedicated variants instead of overloading a generic Generic(String) / Validation(String) so callers can match programmatically.

    line 48
  3. Very specific:

    Every "the developer wrote invalid template syntax" mistake is returned as ParserError::Template(Box<Diagnostic>) …

    line 53
  4. Very specific:

    Add a new authoring error with the parser helpers (authoring_error, authoring_error_at, html_error) and a new constant in diagnostic::codes.

    line 67
  5. Very specific:

    When one value feeds both a terminal and a non-terminal channel, split it: webui-dev-server's RebuildError { display, message } carries a …

    line 95
  6. Very specific:

    Keep hot fast-paths inlinable: a per-element check (e.g.

    line 129

“Common” is measured: kept by at least 6 in 10 of the 3 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Error Handling & Diagnostics

Use this skill whenever you add, change, or review an error path: a build-time authoring error, a parser/handler failure, a CLI validation error, or anything surfaced to a host (FFI/WASM/Node) or a tool/agent. WebUI errors must be recoverable, actionable, and machine-consumable — for humans and AI agents alike.

1 - Never panic on recoverable input

panic = "abort" in the release profile means a panic kills the process instantly — including any FFI/WASM/Node host embedding the framework. Bad template input, bad CLI input, and bad state are recoverable and must return Result, never panic!/unwrap()/expect().

SituationDo
Malformed template / CSS / route authored by a developerReturn Result with a structured Diagnostic (see §3).
Missing/invalid CLI input (file, port, flag)Return a typed CliError (see §6).
A genuinely impossible internal statePrefer ? with a typed error; only use unreachable!/expect with a justification comment, and never in a hot or host-reachable path.

unwrap()/expect() are banned in library code (clippy.toml disallowed-methods). todo!, unimplemented!, and dbg! are banned workspace-wide (clippy.toml disallowed-macros). Tests opt out with #[allow(clippy::disallowed_methods)].

Enforcement note. unwrap/expect/todo!/unimplemented!/dbg! are caught by clippy. panic! is not lint-banned (too entrenched) — keep it out of recoverable paths by review. The "no regex in core logic" rule is also review-enforced, not deny-banned: actix-web pulls regex transitively, so a crate-level ban would break the build and can't scope to first-party code.

2 - Error type conventions

Crate kindError type
Library (webui-parser, webui-handler, webui-expressions, webui-state, webui-protocol, webui-ffi)Custom enum via thiserror.
Binary (webui-cli, xtask)anyhow for orchestration; a typed enum when callers must branch on the cause (e.g. webui-cli's CliError for hints + exit codes).
  • Each error layer's Display describes only its own level; the #[source] chain carries the rest, so anyhow's {:#} never double-prints. Provide a flat chain_message() helper for hosts that don't walk the chain (Node, FFI).
  • Add dedicated variants instead of overloading a generic Generic(String) / Validation(String) so callers can match programmatically.

3 - Authoring errors are structured Diagnostics

Every "the developer wrote invalid template syntax" mistake is returned as ParserError::Template(Box<Diagnostic>) (crates/webui-parser/src/diagnostic.rs), so all build errors render identically. A Diagnostic carries:

  • code — a stable, machine-readable identifier (e.g. invalid-for-each). Defined in diagnostic::codes. Treat codes as a stable API: tools and AI agents branch on them, so rename only with a deliberate migration.
  • title — short, lowercase (invalid <for> each expression).
  • location — set from a byte offset via .at_offset(source, offset); rendered rustc-style --> owner:line:column (single forward scan, no regex, no recursion), falling back to in component <c> · element <e>.
  • snippet — the offending source text.
  • help: — an actionable fix (see §4).

Add a new authoring error with the parser helpers (authoring_error, authoring_error_at, html_error) and a new constant in diagnostic::codes. Validate at parse/build time and fail fast — never defer to render time.

4 - Make errors actionable (and typo-aware)

Tell the developer what is wrong and how to fix it. Every Diagnostic should carry a help: line. Where a mistake is likely a typo, suggest the intended name via suggest::closest_match (iterative Levenshtein — no recursion, no regex, cold path only):

  • Misspelled directive attribute: <for eahc=…> -> "did you mean each?"
  • Unknown component tag: suggest the closest same-namespace registered component (<mp-buton> -> <mp-button>). Prefix-guard the match (text before the first - must match) so a genuine third-party custom element (<md-button>) is never falsely flagged.

5 - Presentation layering: color belongs ONLY in the entry point

Libraries produce plain, color-free data. The entry point decides how to present it. Never embed ANSI in library output or in any machine/host channel.

ConsumerGets
webui-cli (terminal)Reads Diagnostic fields and colorizes with console::style() — the only approved styling method (see copilot-instructions "Terminal output styling").
FFI / WASM / NodeThe plain Display text through their native error channel (webui_last_error, JsValue, napi::Error).
Browser / tools (dev-server live-reload, SSE, console.error)Plain text. ANSI renders as garbage and breaks single-line SSE frames.

When one value feeds both a terminal and a non-terminal channel, split it: webui-dev-server's RebuildError { display, message } carries a colorized display for the reporter and a plain message for the browser.

Per-line color: when colorizing multi-line output, style each line independently (open + close the SGR span within the line). A single span that straddles newlines bleeds when the line is later re-prefixed (e.g. [server] under xtask dev).

6 - Machine-readable output and exit codes (webui-cli)

For editors, CI, and AI/agent tooling:

  • --format json (global flag) emits each error as one JSON object on stdout (no ANSI; decorative output suppressed): {severity, code, message, file, line, column, snippet, help, chain}. Branch on the stable code, not the human message. Build the object with the serde_json::Map API, not the json! macro (it unwraps internally and trips disallowed_methods).
  • Exit codes follow BSD sysexits.h (webui-cli's error::exit_code): 65 data/authoring error, 66 missing input, 69 port in use, 74 I/O, 2 usage (clap), 1 otherwise.
  • Replace fragile err_msg.contains("...") dispatch with typed errors that own their hint() and exit_code().

7 - Error construction is COLD - keep it off the hot path

Building a Diagnostic (format strings, suggestions, location scans) is rare, but if it inlines into a hot function it bloats that function and perturbs its code layout — a real, measurable regression (observed ~4-5% on parse benches with no added hot-path work).

  • Mark error builders #[cold] + #[inline(never)] (e.g. authoring_error*, html_error, css_diagnostic, *_error constructors, suggest::closest_match).
  • Keep hot fast-paths inlinable: a per-element check (e.g. split_once('-')) must stay inlined; only its cold fallback (the registry scan) goes out-of-line.
  • Validate layout-sensitive changes with cargo bench -p <crate> against the base branch (see skills/perf/SKILL.md). A "regression" with no added compute is usually layout — fix it with #[cold], don't shrug it off.

8 - Checklist for a new error

  • Returned as Result, never panicked (host-safe under panic = "abort").
  • Authoring mistake -> ParserError::Template(Box<Diagnostic>) with a new diagnostic::codes constant, location, snippet, and help:.
  • help: is actionable; add a "did you mean …?" suggestion if it's a typo.
  • No color/ANSI in library, host, browser, or JSON output.
  • Surfaced in --format json with the stable code; exit code classified.
  • Error construction is #[cold]/#[inline(never)]; hot path unchanged (confirm with a benchmark if it sits near a hot loop).
  • Tests: a regression test that fails without the error, asserting on the code (not the prose); JSON stays plain (no \x1b).
  • DESIGN.md updated if the error-handling architecture changed; public docs (incl. docs/ai.md) updated if a user-visible code/flag changed.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

Reviews can't be read right now.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.