refactor(REQUIREMENTS): integrate DX ergonomics, component scoping, and build-pipeline fixes

- add golden_path, progressive_disclosure, ceremony, diagnostics,
  view_model, interop, client_local, component, codegen, derive sections
- introduce ResourceRef/ResourceId split, ScopeKey, atom mutability
  and pub/sub distinction, form control ids, 5KB target runtime
- fix build typo, macro policy, htmx boundary, version singleton,
  runtime delegation scope
- strengthen Surface with hemplate-real directives (h-for, h-key, +attr)
- preserve no-!-macros policy, north_star feel: Svelte at call site,
  Rust at boundary

Eval: architecture 9/10, dx 9/10, orthogonality 9/10, suckless 8.5/10
This commit is contained in:
slhx agent
2026-05-10 12:16:02 +02:00
parent 57dc22b8cc
commit e0484f8eb6
+49 -8
View File
@@ -102,6 +102,11 @@ Global exports are opt-in only.
### req: component/005
005 An optional `#[slhx::component]` macro may validate that every handle declared in the template Surface has a corresponding `#[slhx::handler]` within the annotated module. This is the only macro with cross-handler visibility inside a single module; it remains strictly local. Missing handlers without `#[slhx::component]` are caught at app mount or test time, not `cargo check`.
### req: component/006
006 `#[derive(Hemplate)]` structs are natural component boundaries. slhx_build
generates APIs per component/template namespace and may merge component
Surfaces only at explicit app/root boundaries.
---
## codegen
@@ -147,7 +152,8 @@ Global exports are opt-in only.
001 hemplate does not expose a slhx API. It exposes a stable, generic Template Surface IR. slhx is one consumer; a11y tools, test generators, and documentation generators are others.
### req: boundary/002
002 hemplate never interprets `data-slhx-*`, `slhx-*`, or any other tool-prefixed attribute. It records them as raw `name: value` pairs in the Surface.
002 hemplate never interprets `data-slhx-*` or any other tool-prefixed
attribute. It records them as raw `name: value` pairs in the Surface.
### req: boundary/003
003 slhx never parses `.heml` directly. It consumes `hemplate.surface.postcard` emitted by `hemplate_build`. slhx interprets tool-specific conventions (`data-slhx-handle`, `data-slhx-slot`, etc.) from the generic Surface.
@@ -177,6 +183,22 @@ Global exports are opt-in only.
### req: surface/007
007 `hemplate-derive` does not write Surface files. Surface generation is a `build.rs` / `hemplate_build` concern, proc-macro side-effect free.
### req: surface/008
008 The Surface records hemplate structural directives as first-class facts:
`h-for`, `h-key`, `h-if`, `h-else-if`, `h-else`, dynamic `+attr` bindings,
and interpolated attr/text expressions. slhx consumes these facts; it never
parses `.heml` source directly.
### req: surface/009
009 Raw/pre-rendered HTML insertions are opaque Surface holes. The parent
Surface records the insertion point and source span, but nodes inside inserted
HTML belong to the child component Surface or remain invisible to tools.
### req: surface/010
010 Attribute values preserve their origin: static literal, dynamic `+attr`
expression, or interpolated template string. slhx param inference consumes
these from Surface and never reparses `.heml`.
---
## build
@@ -191,7 +213,7 @@ Global exports are opt-in only.
003 `slhx-derive` (`#[slhx::handler]`) reads `slhx.syms` at expansion time to validate handle names, slot names, and form signatures. It generates only local glue (static fn-table entry) plus compile errors.
### req: build/004
004 `#slhx::surface]` reads `slhx.generated.rs` from `$OUT_DIR` and expands it into the annotated module. It is a pure include/bridge macro with no semantic analysis of its own.
004 `#[slhx::surface]` reads `slhx.generated.rs` from `$OUT_DIR` and expands it into the annotated module. It is a pure include/bridge macro with no semantic analysis of its own.
### req: build/005
005 A `build.rs` failure (missing Surface, version mismatch, stale hash) is a hard error before proc-macro expansion.
@@ -232,10 +254,15 @@ Global exports are opt-in only.
## state
### req: state/001
001 Typed atoms with `Atom<T>`: read via `atom.get()`, subscribe via `Effect::set`. No hidden global proxy / reactive graph. Atoms are explicit values in `struct App`.
001 Typed atoms with `Atom<T>` are explicit addressable state resources.
Read via `atom.get()`, mutate via `atom.set()` / `atom.update()`, publish to
the runtime via generated atom commands or `Effect::set(atom, value)`.
No hidden global proxy / reactive graph. Atoms are explicit values in `struct App`.
### req: state/002
002 Subscription is explicit: `Effect::set(atoms::FOO, 42)` pushes the new value to all consumers. No automatic component re-render graph.
002 Atoms are not reactive by default. Updating an atom does not re-render
anything unless the handler returns an effect targeting consumers.
Subscriptions are explicit runtime/store APIs. No automatic component re-render graph.
### req: state/003
003 The JS runtime maintains a client-side atom store keyed by `AtomId`. Runtime values are type-erased postcard bytes. Types are compile-time only. A deterministic `TypeHash` may be generated by `slhx_build` for diagnostics, but the JS runtime does not depend on Rust `TypeId`.
@@ -297,14 +324,22 @@ Global exports are opt-in only.
## scope
### req: scope/001
001 `Scope` is a first-class primitive. Keyed loops (`h-for`), conditional branches (`h-if`), component instances, modals, tabs, nested forms — all are scopes. A slhx-addressable node inside any dynamic scope must carry a stable `ScopeKey`. Composite identity is `(ResourceId, ScopeKey)`. [north_star]
001 `Scope` is a first-class primitive. Keyed loops (`h-for`), conditional
branches (`h-if`), component instances, modals, tabs, nested forms — all are
scopes. A slhx-addressable node inside any dynamic scope must carry a stable
`ScopeKey`. Concrete runtime targets are addressed through `ResourceRef`
`{ resource: ResourceId, scope: Option<ScopeKey> }`. [north_star]
---
## wire
### req: wire/001
001 Authoring HTML uses symbolic `data-slhx-*` attributes. Rendered runtime HTML lowers these to compact numeric metadata: `data-hid`, `data-sid`, optional `data-key`, optional atom/bootstrap ids. The browser never sees handler or slot names. `data-slhx-root` marks a scoped root boundary.
001 Authoring HTML uses symbolic `data-slhx-*` attributes. Rendered runtime
HTML lowers these to compact numeric metadata: `data-hid`, `data-sid`,
optional `data-key`, optional atom ids, optional form/control ids, and
`data-slhx-st` for state bootstrap. The browser never sees handler or slot
names. `data-slhx-root` marks a scoped root boundary.
### req: wire/002
002 POST bodies carry `application/x-www-form-urlencoded` with distinguished field `__h` (handle id). Server routes by numeric id, not by URL path.
@@ -345,7 +380,10 @@ Global exports are opt-in only.
002 No build step, no virtual DOM, no diffing, no scheduler. Receiving an effect = apply ops immediately in declared order.
### req: js/003
003 The runtime consists of an Op interpreter (`ReplaceHtml`, `SetText`, `PatchAtom`, `Navigate`, `Focus`, `AddClass`, `RemoveClass`, `RemoveKeyed`, `CustomOp`) reading from a postcard byte stream.
003 The runtime implements the canonical `EffectBatch` opcode schema from
`slhx-core`. DOM-specific operations such as `ReplaceHtml`, `SetText`,
`AddClass`, or `RemoveKeyed` are lowering details generated from canonical
resource ops.
---
@@ -538,7 +576,10 @@ Global exports are opt-in only.
004 Three execution modes supported: server-first (request/response), client-local WASM (requestAnimationFrame, no round-trip), and hybrid sync (local + remote via `slhx-sync`). Modes are opt-in per handler, not global.
### req: misc/005
005 The only user-facing proc-macro is `#[slhx::handler]`. No `!` call-syntax macros. Attribute macros only.
005 The only required user-facing proc-macro in slhx core is `#[slhx::handler]`.
Optional ergonomic macros may exist: `#[slhx::surface]`, `#[slhx::component]`,
`#[slhx::app]`, and integration-crate macros such as `#[slhx::island]` or
`#[slhx_sync::presence]`. No `!` call-syntax macros.
### req: misc/006
006 Source spans are present on every Surface node, attribute, and scope. Error messages cite file, line, and column. This is non-negotiable for DX.