From c7ca3aab9de50fcd4ed554f8c083a627e6e34686 Mon Sep 17 00:00:00 2001 From: slhx agent Date: Thu, 25 Jun 2026 17:52:36 +0200 Subject: [PATCH] docs(requirements): split handler rows Add explicit ring fields to derive_handler requirements and split handler parameter constraints without changing behavior. req: derive_handler/001 req: derive_handler/002 req: derive_handler/003 req: derive_handler/004 req: derive_handler/005 --- AGENTS.md | 2 +- README.md | 4 ++-- REQUIREMENTS.md | 11 +++++++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 7b056c7..6594b7d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -57,7 +57,7 @@ Keep it stable. Prefer pointers to canonical sources over copied structure, file - Use the same Workout command surface for tests, production build, and mobile release: `cargo run -p hemx-xtask -- workout test`, `cargo run -p hemx-xtask -- workout build`, `HEMX_WORKOUT_ORIGIN=https://workout.example.com cargo run -p hemx-xtask -- workout mobile-release`, and `HEMX_WORKOUT_ORIGIN=https://workout.example.com cargo run -p hemx-xtask -- workout mobile-verify`; Android/iOS SDKs, store submission targets, and signing remain external blockers, not repo-owned secrets, and do not imply a broad `hemx-mobile` framework. req: examples/006 req: examples/011 req: examples/013 - hemx core stays small: effects, typed ids, registries, and wire schema only; keep features in core only when they fit typed resources plus the closed EffectBatch op set, and treat DOM details as runtime lowering. Workspace crates stay separated, stable-Rust-compatible, and free of kitchen-sink boundaries; new primitives must delete special cases. Public identifiers should flow through typed wrappers over internal `ResourceId`/`ResourceRef`, not special-case opcodes. Wire output lowers symbolic authoring names to compact metadata and postcard/form-encoded envelopes, not JSON. ABI/schema versions and build fingerprints must guard runtime/server compatibility. v0 scope is the checked hypermedia core plus page/runtime/wire/diagnostic/test/axum proof, not optional sync/wasm/query/auth/router breadth. req: v0_scope/001 req: v0_scope/002 req: v0_scope/005 req: laws/001 req: invariant/001 req: invariant/005 req: typed_id/001 req: typed_id/003 req: effect_algebra/001 req: effect_algebra/006 req: wire/001 req: wire/002 req: wire/003 req: wire/004 req: wire/005 req: wire/006 req: abi/001 req: abi/002 req: abi/003 req: abi/004 req: abi/005 req: misc/001 req: misc/002 req: misc/003 req: misc/004 req: misc/005 req: misc/006 req: misc/007 req: misc/008 req: misc/009 req: misc/010 - Routing, auth, sessions, transport, transitions, sync, async data helpers, and storage belong in integration/user crates; hemx-axum preserves normal HTTP auth, credentials, CSRF, and progressive-enhancement semantics rather than defining policy in core. Sync is optional integration state reconciliation over push/transport, not core. req: auth/001 req: auth/002 req: auth/003 req: auth/004 req: auth/005 req: async_data/001 req: async_data/002 req: async_data/003 req: sync/001 req: sync/008 -- Public examples and beginner APIs should use templates plus Rust, generated component APIs, resources, view wrappers, render/page helpers, and `IntoEffect`, not atoms, raw ids, selectors, wire formats, runtime opcodes, manual registries, `$OUT_DIR` includes, raw render/lower calls, raw HTML construction, imperative DOM mutation, or raw effect constructors; keep advanced layers out of starters. req: canonical_authoring/001 req: canonical_authoring/004 req: canonical_authoring/006 req: canonical_authoring/010 req: canonical_authoring/015 req: invariant/003 req: dx/001 req: dx/002 req: dx/010 req: component/003 req: component/004 req: view/001 req: view/002 req: html_safety/001 req: html_safety/003 req: html_safety/005 req: public_api/001 req: public_api/002 req: public_api/003 req: public_api/005 req: public_api/006 req: progressive_disclosure/001 req: progressive_disclosure/002 req: progressive_disclosure/003 +- Public examples and beginner APIs should use templates plus Rust, generated component APIs, resources, view wrappers, render/page helpers, plain `#[hemx::handler]` functions, and `IntoEffect`, not atoms, raw ids, selectors, wire formats, runtime opcodes, manual registries, `$OUT_DIR` includes, raw render/lower calls, raw HTML construction, imperative DOM mutation, or raw effect constructors; keep advanced layers out of starters. req: canonical_authoring/001 req: canonical_authoring/004 req: canonical_authoring/006 req: canonical_authoring/010 req: canonical_authoring/015 req: invariant/003 req: dx/001 req: dx/002 req: dx/010 req: component/003 req: component/004 req: view/001 req: view/002 req: html_safety/001 req: html_safety/003 req: html_safety/005 req: public_api/001 req: public_api/002 req: public_api/003 req: public_api/005 req: public_api/006 req: progressive_disclosure/001 req: progressive_disclosure/002 req: progressive_disclosure/003 req: derive_handler/001 req: derive_handler/002 req: derive_handler/003 req: derive_handler/004 req: derive_handler/005 - Typed partial swaps should stay expressed as generated target plus rendered partial plus swap kind, not selector-driven rerendering; HTTP, page navigation, push, and island behavior adapt around that loop, and docs should layer new primitives progressively. Navigation is an effect/page-swap concern, not a core router framework, and enhanced links preserve real anchors/history semantics. Push streams carry postcard EffectBatch over server-owned SSE/WebSocket transport and keep `data-hemx-sse` root-scoped/same-origin by default. Preserve keyed/optional scope identity for addressable loop nodes and prefer generated keyed-slot helpers over low-level keyed calls. req: canonical_authoring/002 req: canonical_authoring/014 req: modes/001 req: scope/001 req: list/001 req: list/002 req: list/003 req: list/004 req: list/005 req: nav/001 req: nav/002 req: nav/003 req: nav/004 req: nav/005 req: push/001 req: push/002 req: push/003 req: push/004 req: push/005 req: push/006 req: push/007 req: progressive_disclosure/004 req: page_swap/001 req: page_swap/002 req: page_swap/003 - `examples/html_examples` is the copy-paste HTML pattern gallery for htmx-style examples; keep exact htmx URL slugs visible while translating behavior to boring `.heml`, generated resources, and server-owned Rust state, not HTMX syntax, selector targeting, or user-authored browser JavaScript. Shared runtime loading and declarative `data-hemx-*` are allowed. Boost containers enhance same-origin descendants only and preserve native external/download/new-tab behavior. req: htmx_equivalents/001 req: htmx_equivalents/003 req: htmx_equivalents/005 req: examples/005 req: examples/007 req: examples/012 req: page_swap/007 req: page_swap/008 - Use `cargo run -p hemx-xtask -- app new PATH` for the generic page/form/keyed-row/notice starter, and `cargo run -p hemx-xtask -- app new --mobile PATH` for the phone-first starter with host capabilities, recovery truth, and release-kit commands; do not treat it as a mobile framework or store-submission bot. req: ceremony/005 req: ceremony/006 req: ceremony/007 diff --git a/README.md b/README.md index 11ebe57..2d408db 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,8 @@ For beginner and production-shaped app code, stay on this path. req: public_api/ hemplate Surface and emits generated Rust helpers for slots, forms, handles, page targets, classes, and events. req: ceremony/003 req: build/001 3. **Handlers are plain Rust.** App code uses `#[hemx::handler]` functions with - ordinary domain types, framework extractors, generated `Form` inputs, and - `impl IntoEffect` or `Result` returns. req: derive_handler/004 + ordinary domain types, framework extractors, generated `Form` inputs, + explicit `data-*` params, and `impl IntoEffect` or fallible returns. req: derive_handler/001 req: derive_handler/002 req: derive_handler/003 req: derive_handler/004 req: derive_handler/005 4. **Handlers return generated commands.** Common handlers return helpers such as `todos.append(todo)`, `todo_row.replace(row)`, `new_todo.error("title", "Required")`, `new_todo.clear()`, `page.replace(view)`, or tuples of those diff --git a/REQUIREMENTS.md b/REQUIREMENTS.md index 2cafc64..f6a1a5f 100644 --- a/REQUIREMENTS.md +++ b/REQUIREMENTS.md @@ -1110,16 +1110,16 @@ what a valid business email is. [north_star] ## derive_handler ### req: derive_handler/001 -001 `#[hemx::handler]` validates: handle name exists in symbol table, params match form surface or `data-*` attributes, return type implements `IntoEffect`. Generate code registers the function in a static lookup table keyed by numeric handle id. +0 001 `#[hemx::handler]` validates handle name, params, and `IntoEffect` return type; generated code registers a static lookup entry keyed by numeric handle id. ### req: derive_handler/002 -002 Handler param inference from `data-*` attributes: when a template declares `data-card-id="{card.id}"` on a node with `data-hemx-handle`, the handler may declare `card_id: CardId` as a parameter. hemx-build checks attribute → param name and type mapping. +0 002 Handler param inference from `data-*` attributes allows template `data-card-id="{card.id}"` to map to a `card_id: CardId` handler parameter checked by hemx-build. ### req: derive_handler/003 -003 Handler parameters are inferred from four sources: `Form`, `data-*` attributes on the triggering node, route params supplied by integration crates, and explicit app/context parameters. Missing or incompatible params are compile-time errors with source spans. hemx does not implement selector-based `hx-include`; shared params are represented by forms, hidden inputs, scoped context, or explicit `data-*` attributes. +0 003 Handler parameters are inferred from `Form`, triggering-node `data-*` attributes, integration-supplied route params, and explicit app/context parameters. ### req: derive_handler/004 -004 The common handler signature forms are plain Rust functions, synchronous or async: +0 004 The common handler signature forms are plain Rust functions, synchronous or async: ```rust fn ping() -> impl IntoEffect async fn add(app: State, form: Form) -> impl IntoEffect @@ -1128,6 +1128,9 @@ async fn delete(app: State, todo_id: TodoId) -> impl IntoEffect ``` `State` is illustrative integration context; equivalent framework extractors or app references are adapter concerns. Form fields and `data-*` params parse through normal Rust `FromForm`/`FormValue`/`FromStr`-style traits, so domain newtypes remain user-authored. Handlers may return `impl IntoEffect` or `Result` for fallible database/domain work; `IntoEffect` values compose through tuples while `Result` paths preserve a typed error boundary (`IntoHandlerFailure` in the Axum adapter) for integrations to map failures to generated UI effects, toasts, events, or HTTP responses. Result-specific registry adapters are generated/integration internals; canonical app code uses the same `#[hemx::handler]` and `#[hemx::app]` authoring shape for plain and fallible handlers. +### req: derive_handler/005 +0 005 Missing or incompatible handler params are compile-time errors with source spans; shared params use forms, hidden inputs, scoped context, or explicit `data-*` attributes, not selector-based `hx-include`. + --- ## derive_app