Schema-driven admin for all Hero services — generic renderer + x-admin annotations #15

Open
opened 2026-06-23 12:09:12 +00:00 by zaelgohary · 0 comments
Member

Schema-driven admin for all Hero services

Goal: one generic admin that renders each service's OpenRPC spec, so a service gets a working admin with zero UI code and its bespoke *_admin crate / module is deleted. Grounded against the working tree on revamp/schema-driven-admin; full plan + provenance in the author's notes.

The simplifying insight: the Playground tab is already a generic, spec-driven admin (router/playground.rs). The project is "promote that engine to the default admin, give CRUD-shaped method groups a nicer layout, annotate the few things the spec can't express." Mostly deletion + extraction, not new construction.

Architecture — one pipeline, no DSL

OpenRPC spec  +  x-admin annotations  ──serde──►  typed AdminHints  ──►  generic renderer (Dioxus)

One renderer with three modes: Resources (list/detail/form layout over the extracted schema-form engine), Methods (the existing Playground — zero-config fallback), Builtins (irreducible bespoke panes mounted by name — the only escape hatch). No detection on the hot path; a "resource" is just a method group an annotation lays out.

What's reused / extracted / deleted

  • Reuse as-is: admin/schema.rs (Resolver — the spec→widget brain), admin/layout.rs, admin/components/* (shell), admin/transport.rs, dioxus-bootstrap-css.
  • Extract once (the only real new code): the Playground schema-form engine → a pub widget; unify the 5 cell impls into one Cell enum; one use_poll(fetch,trigger); one status_color; one lookup resolver; promote ModalShell.
  • Delete: bespoke modules (proc/whiteboard/voice/aibroker ≈ 6,471 LOC, most of process.rs retained as <Builtin>); the string-markup config layer + roxmltree parser; scoped *_admin crates (~29,419 addressable LOC, excluding the hero_biz_admin Askama outlier).

The x-admin annotation model

Admin metadata lives on each service's OpenRPC spec (x-admin extension), deserialized by serde into typed structs — no central manifest store, no XML grammar, no parser. One source of truth, versioned with the service, served as runtime data. A service with no x-admin falls back to the Playground (still a usable admin). envelope (param-wrapper key) is the one field that can't be inferred. Implemented + unit-tested in crates/hero_components_app/src/admin/hints.rs.

"x-admin": {
  "resources": [{
    "schema": "Job", "envelope": "data", "group": "Data",
    "columns": [{ "field": "status", "as": "badge" },
                { "field": "run_id", "as": "lookup", "from": "run_list", "label": "name" }],
    "actions": [{ "label": "Restart", "method": "run_restart", "arg": "sid", "confirm": true }]
  }],
  "builtins": [{ "label": "Terminal", "name": "proc_terminal", "group": "Operations" }]
}

Minimal resource is two fields — { "schema": "Job", "envelope": "data" } — method names default to {snake(schema)}_{verb}. Lenient/forward-compatible: unknown fields ignored, unknown cell kinds degrade, invalid entries dropped + reported.

Context & auth — paired with writes

Genuinely absent today (router.contexts exists but ContextPicker is dead code; no auth machinery). Writes ship with a minimal context slice, not after — no destructive affordance is enabled until the selected context is forwarded on every call. Read-only generation can precede this; mutation cannot.

Phases

  • P0 — Reuse extraction + foundation: theme, promote schema-form engine, unify cell/poller/status_color/lookup, AdminHints + loader. (started — AdminHints landed)
  • P1 — Generic resource renderer, read-only. Pilot hero_proc (only detector-clean service + has reference views to A/B).
  • P2 — Write path + context/auth together; un-gate writes.
  • P3 — Annotate + delete pilots (hero_proc, whiteboard, hero_browser) behind a coverage gate. Largest deletion.
  • P4 — Org-wide retirement; <Builtin>/embed the hard tail (code/slides/office).
  • P5 — Optional offline scaffold generator (pre-fills x-admin).
  • P6 — Polish.

Success metric

Services served with zero bespoke per-service UI code — not a single LOC figure.

# Schema-driven admin for all Hero services **Goal:** one generic admin that renders each service's OpenRPC spec, so a service gets a working admin with **zero UI code** and its bespoke `*_admin` crate / module is deleted. Grounded against the working tree on `revamp/schema-driven-admin`; full plan + provenance in the author's notes. **The simplifying insight:** the Playground tab is *already* a generic, spec-driven admin (`router/playground.rs`). The project is "promote that engine to the default admin, give CRUD-shaped method groups a nicer layout, annotate the few things the spec can't express." Mostly **deletion + extraction**, not new construction. ## Architecture — one pipeline, no DSL ``` OpenRPC spec + x-admin annotations ──serde──► typed AdminHints ──► generic renderer (Dioxus) ``` One renderer with three modes: **Resources** (list/detail/form layout over the extracted schema-form engine), **Methods** (the existing Playground — zero-config fallback), **Builtins** (irreducible bespoke panes mounted by name — the only escape hatch). No detection on the hot path; a "resource" is just a method group an annotation lays out. ## What's reused / extracted / deleted - **Reuse as-is:** `admin/schema.rs` (`Resolver` — the spec→widget brain), `admin/layout.rs`, `admin/components/*` (shell), `admin/transport.rs`, dioxus-bootstrap-css. - **Extract once (the only real new code):** the Playground schema-form engine → a `pub` widget; unify the 5 cell impls into one `Cell` enum; one `use_poll(fetch,trigger)`; one `status_color`; one lookup resolver; promote `ModalShell`. - **Delete:** bespoke modules (`proc`/`whiteboard`/`voice`/`aibroker` ≈ 6,471 LOC, most of `process.rs` retained as `<Builtin>`); the string-markup config layer + roxmltree parser; scoped `*_admin` crates (~29,419 addressable LOC, excluding the `hero_biz_admin` Askama outlier). ## The `x-admin` annotation model Admin metadata lives **on each service's OpenRPC spec** (`x-admin` extension), deserialized by serde into typed structs — **no central manifest store, no XML grammar, no parser**. One source of truth, versioned with the service, served as runtime data. A service with no `x-admin` falls back to the Playground (still a usable admin). `envelope` (param-wrapper key) is the one field that can't be inferred. Implemented + unit-tested in `crates/hero_components_app/src/admin/hints.rs`. ```jsonc "x-admin": { "resources": [{ "schema": "Job", "envelope": "data", "group": "Data", "columns": [{ "field": "status", "as": "badge" }, { "field": "run_id", "as": "lookup", "from": "run_list", "label": "name" }], "actions": [{ "label": "Restart", "method": "run_restart", "arg": "sid", "confirm": true }] }], "builtins": [{ "label": "Terminal", "name": "proc_terminal", "group": "Operations" }] } ``` Minimal resource is two fields — `{ "schema": "Job", "envelope": "data" }` — method names default to `{snake(schema)}_{verb}`. Lenient/forward-compatible: unknown fields ignored, unknown cell kinds degrade, invalid entries dropped + reported. ## Context & auth — paired with writes Genuinely absent today (`router.contexts` exists but `ContextPicker` is dead code; no auth machinery). **Writes ship with a minimal context slice, not after** — no destructive affordance is enabled until the selected context is forwarded on every call. Read-only generation can precede this; mutation cannot. ## Phases - **P0** — Reuse extraction + foundation: theme, promote schema-form engine, unify cell/poller/status_color/lookup, `AdminHints` + loader. *(started — `AdminHints` landed)* - **P1** — Generic resource renderer, read-only. Pilot **hero_proc** (only detector-clean service + has reference views to A/B). - **P2** — Write path **+ context/auth together**; un-gate writes. - **P3** — Annotate + delete pilots (hero_proc, whiteboard, hero_browser) behind a coverage gate. Largest deletion. - **P4** — Org-wide retirement; `<Builtin>`/embed the hard tail (code/slides/office). - **P5** — Optional offline scaffold generator (pre-fills `x-admin`). - **P6** — Polish. ## Success metric *Services served with zero bespoke per-service UI code* — not a single LOC figure.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_components#15
No description provided.