Files
hemx/docs/diagnostics.md
T
slhx agent c8c71deb68 docs(hemx): add diagnostics guide
Document the v1 diagnostics story for common template, derive, form, result-handler, and runtime/deploy mistakes. The guide names expected user-facing diagnostics and recurring verification gates without exposing raw ids/selectors as the beginner path.

req: diagnostics/001

req: diagnostics/002

req: diagnostics/003

req: failure/004

req: failure/005

req: public_api/005

req: test/003
2026-06-05 09:46:32 +02:00

77 lines
6.2 KiB
Markdown

# Diagnostics guide
hemx diagnostics should tell a Rust developer which template fact, generated
helper, or handler shape is wrong, and what to change next. They should not teach
raw ids, selector targeting, runtime opcodes, or Cargo internals in the normal
path. req: diagnostics/001 req: diagnostics/002 req: diagnostics/003
Use this guide as the v1 checklist for common mistakes in beginner and
production-shaped apps.
## Where errors happen
- **Template/build diagnostics** come from `hemx_build::app().run()` while reading
`.heml` files and CSS. Fix the template or generated-surface convention.
- **Derive/compile diagnostics** come from `#[hemx::surface]`, `#[hemx::form]`,
`#[hemx::handler]`, `#[hemx::component]`, and `#[hemx::app]`. Fix Rust code so
it matches the generated surface.
- **Runtime diagnostics** come from the tiny browser runtime when a deployed page
and response are incompatible or a target cannot be applied. Fix deployment or
recover with a full page response. req: failure/005
## Common mistakes and fixes
| Mistake | Diagnostic shape | Fix |
| --- | --- | --- |
| Handler has no matching template handle | `unknown hemx handle \`save\`; add \`data-hemx-handle="save"\`` | Add the handle to the template, rename the function, or put the handler in the matching component. |
| Component is missing a generated handler | `#[hemx::component] missing handler implementation(s): delete` | Add a `#[hemx::handler] fn delete(...)` in that component, or remove the template handle. |
| Handler name is ambiguous across components | `ambiguous generated handle name(s): save` | Scope the component with `#[hemx::component("todos")]` or rename handles so the generated path is unique. |
| Handler misses generated params | `hemx handler \`show\` is missing generated param argument(s): mode` | Add typed handler arguments for every `data-hemx-param-*` fact generated by the template. |
| Form handler omits the form argument | `handles a generated form and must accept a typed form argument` | Accept `Form<NewThing>`/`hemx::Form<NewThing>`/integration equivalent and derive `#[hemx::form("...")]` for the type. |
| Form struct misses a control | `hemx form \`new_todo\` is missing field \`title\`` | Add a Rust field matching the form control name, or rename the template control. |
| Required/multiple form control has wrong Rust shape | `required ... must not be Option<_>` or `accepts multiple values and must be Vec<_>` | Match HTML required/multiple semantics with `T`, `Option<T>`, or `Vec<T>` as appropriate. |
| Form field type cannot parse submitted values | compiler mentions `T: FormValue` / `T: hemx::FormValue` | Implement `FromStr`/the expected form value trait for the domain newtype, or use a parseable domain type. |
| Generated resources are unavailable | `could not find generated hemx module` / `could not find generated hemx symbols` plus `add hemx_build::app().run()? to build.rs` | Add or fix `build.rs`, then rerun `cargo check`; do not copy `$OUT_DIR` paths into app code. |
| Unknown `data-hemx-*` attribute | `unknown hemx attribute ... check the spelling or use a non-hemx data-* attribute` | Fix the spelling, use the supported hemx attribute, or rename app metadata to a non-hemx `data-*` attribute. |
| Selector-style targeting | ``data-hemx-target` is selector-style targeting; hemx uses generated resources` | Put `data-hemx-slot` on the local target and return a generated slot/page/form effect. |
| Generated target appears in a loop without a stable key | `inside an h-for without h-key; add a stable h-key="item.id"` | Add a stable `h-key` to the owning loop; use generated keyed helpers for row updates. |
| Page/SSE attributes are on the wrong element | `expected a real <a href=...>` / `expected placement on the same element as data-hemx-root` | Keep page navigation on anchors and put root-scoped runtime attributes on the root element. |
| Result handler error type is not mappable | compiler reports the error type does not satisfy `IntoHandlerFailure` | Implement `IntoHandlerFailure` for the app error, or keep expected validation as generated UI effects instead of `Err`. req: failure/004 |
| Old page talks to a new server/runtime | runtime refuses the partial update on fingerprint mismatch | Serve a self-consistent release or fall back to full page reload/navigation. See `docs/recipes/deploy-versioning.md`. req: abi/004 |
| Missing runtime target | runtime emits a missing-target diagnostic in development and fails/no-ops according to target kind | Fix the template/generated helper mismatch; do not retarget with selectors. req: failure/001 |
## What a good diagnostic should include
A v1-quality diagnostic should include:
- the user-facing name: handle, form, slot, key, param, class, event, or template
- the source area: template path, Rust item, or deployment/runtime boundary
- the concrete expected shape, not an internal representation
- one next action that preserves generated helpers and the tiny runtime
Avoid beginner-facing messages that suggest `ResourceId`, `ResourceRef`, raw
`Effect`, manual registries, selector strings, or runtime opcodes. If an advanced
escape hatch is genuinely required, say that it is advanced and name the safer
normal path first. req: public_api/002 req: public_api/005
## Verification anchors
Current recurring checks cover the most common classes:
- `cargo test -p hemx-build` covers template/build diagnostics such as unknown
hemx attributes, selector-style targeting, invalid runtime attribute values,
missing keys, and invalid page/SSE placement.
- `cargo test -p hemx-derive --test compile_fail` covers derive/compile
diagnostics for missing handlers, form mismatch, params, missing generated
files, unknown scoped components, ambiguous handles, and generated resource
lookup.
- `cargo test -p hemx-js` covers root-scoped runtime behavior, selectorless
targeting, fingerprint mismatch refusal, SSE application, and recoverable
runtime events.
- `cargo test -p hemx-test --test examples_contract` keeps public examples from
teaching forbidden normal-path constructs.
Before claiming the diagnostics story is closed for v1, run those gates plus
`cargo run -p hemx-xtask -- test`, `cargo check --workspace`, and
`redgate health --strict` on a clean tree. req: test/003 req: test/004