hero_books_server: oschema-first migration + workspace de-freeze (issue #154) #158

Open
mahmoud wants to merge 9 commits from development_mahmoud into development
Owner

Migrates hero_books_server from the legacy OSIS / hero_rpc stack to the
oschema-first openrpc_server! stack, and de-freezes the whole workspace onto
hero_lib development (issue #154). Follows the hero_server_migration /
herolib_openrpc / hero_admin_ui skills; same shape as the merged hero_browser
migration.

What changed

Backend → oschema (5 domains on one rpc.sock, /api/{domain}/rpc):

  • collections (12), books (books + libraries + search = 11), docs (9),
    memory (5), admin (2). Dotted/camelCase legacy names renamed to verbatim
    snake_case (collections.getPagecollections_get_page). server.health /
    rpc.discover drop to the runtime.
  • src/api.rs = openrpc_server! over oschema/{domain}; main
    serve_domains_with(extra, …) where extra is the preserved REST / /img /
    /pdf / /mcp / /admin router. The hand-rolled 3.4k-line dispatcher, accept
    loop, and /rpc+/health handlers are removed; rpc/* bridge the generated
    typed surface onto the existing handler logic.

Workspace de-freeze (now 100% on hero_lib development):

  • Removed the dormant publishing::server OSIS layer + hero_rpc_osis and the
    [patch."hero_rpc.git"] set; herolib_otomlherolib_oschema.
  • SDK regenerated with herolib_macros::openrpc_client! (multi-domain bundle
    HeroBooksClient), replacing legacy hero_rpc_derive; dropped the last
    hero_blueprint [patch] and the hero_rpc_derive/hero_rpc2 deps. No frozen
    hero_macros_previous pins remain.
  • Ported every crate to the current hero_proc_sdk / hero_lifecycle APIs.

Verification

  • cargo build --workspace + cargo clippy --workspace --examples --tests clean
    (0 warnings).
  • Runtime smoke test on a live server (isolated socket): /api/domains.json
    lists all 5 domains; books_list / library_list / collections_list /
    admin_logs_get / books_scan / books_get (error path) return correct typed
    shapes; /health.json, /api/books/openrpc.json and the preserved REST
    /api/books all serve.

Admin UI

The hero_books admin pane (Books / Libraries / Collections) lands in
hero_components (development, separate change). The old Askama
hero_books_admin is retired separately after parity sign-off.

Not included (follow-ups)

  • Retire hero_books_admin (gated on parity + sign-off).
  • Update the hero_books_examples integration harness to /api/{domain}/rpc +
    snake_case names; lab infocheck.

🤖 Generated with Claude Code

Migrates `hero_books_server` from the legacy OSIS / `hero_rpc` stack to the oschema-first `openrpc_server!` stack, and de-freezes the whole workspace onto hero_lib `development` (issue #154). Follows the `hero_server_migration` / `herolib_openrpc` / `hero_admin_ui` skills; same shape as the merged hero_browser migration. ## What changed **Backend → oschema (5 domains on one `rpc.sock`, `/api/{domain}/rpc`):** - `collections` (12), `books` (books + libraries + search = 11), `docs` (9), `memory` (5), `admin` (2). Dotted/camelCase legacy names renamed to verbatim snake_case (`collections.getPage` → `collections_get_page`). `server.health` / `rpc.discover` drop to the runtime. - `src/api.rs` = `openrpc_server!` over `oschema/{domain}`; `main` → `serve_domains_with(extra, …)` where `extra` is the preserved REST / `/img` / `/pdf` / `/mcp` / `/admin` router. The hand-rolled 3.4k-line dispatcher, accept loop, and `/rpc`+`/health` handlers are removed; `rpc/*` bridge the generated typed surface onto the existing handler logic. **Workspace de-freeze (now 100% on hero_lib `development`):** - Removed the dormant `publishing::server` OSIS layer + `hero_rpc_osis` and the `[patch."hero_rpc.git"]` set; `herolib_otoml` → `herolib_oschema`. - SDK regenerated with `herolib_macros::openrpc_client!` (multi-domain bundle `HeroBooksClient`), replacing legacy `hero_rpc_derive`; dropped the last `hero_blueprint` `[patch]` and the `hero_rpc_derive`/`hero_rpc2` deps. No frozen `hero_macros_previous` pins remain. - Ported every crate to the current `hero_proc_sdk` / `hero_lifecycle` APIs. ## Verification - `cargo build --workspace` + `cargo clippy --workspace --examples --tests` clean (0 warnings). - Runtime smoke test on a live server (isolated socket): `/api/domains.json` lists all 5 domains; `books_list` / `library_list` / `collections_list` / `admin_logs_get` / `books_scan` / `books_get` (error path) return correct typed shapes; `/health.json`, `/api/books/openrpc.json` and the preserved REST `/api/books` all serve. ## Admin UI The hero_books admin pane (Books / Libraries / Collections) lands in `hero_components` (`development`, separate change). The old Askama `hero_books_admin` is retired separately after parity sign-off. ## Not included (follow-ups) - Retire `hero_books_admin` (gated on parity + sign-off). - Update the `hero_books_examples` integration harness to `/api/{domain}/rpc` + snake_case names; `lab infocheck`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Schema-first step of the legacy OSIS/hero_rpc → oschema migration (issue #154,
following the hero_server_migration skill). Reconstructs the live 36-method
JSON-RPC surface (hand-rolled dispatcher in web/rpc.rs) plus the undocumented
hero_memory delegation methods as typed oschema, split into 5 domains served at
/api/{domain}/rpc on the single rpc.sock:

- collections (12): scan/list/get/process/get_page(s)/get_image/metadata/
  add/remove/export_to_dir
- books (11): book browse/scan/export/reindex/get_page/pdf + library list/get
  + search_query (folds the legacy books.*/libraries.*/search.* groups)
- docs (9): docusaurus generation submitted as hero_proc jobs
- memory (5): hero_memory delegation (dynamic results typed as JSON str)
- admin (2): logs view/clear (server.health → runtime /health.json)

Dotted/camelCase legacy names renamed to bare snake_case verbatim
(collections.getPage → collections_get_page). No [rootobject]: identities are
on-disk names, not minted sids. These files are inert until main.rs is wired to
openrpc_server! (next phase), so the repo still builds on the existing stack.
Lifts the frozen hero_macros_previous pin so the workspace builds on the current
hero_lib `development` stack — the prerequisite for the openrpc_server! migration
(issue #154). The new server now generates its 5-domain OpenRPC spec via
`openrpc_server!`; serving is still the legacy dispatcher (wired next).

Workspace de-freeze:
- Remove the dormant `publishing::server` OSIS layer + `osis_impl_generated` +
  generated tests (nothing consumed them); drop `hero_rpc_osis` and the whole
  `[patch."hero_rpc.git"]` set.
- herolib_otoml → herolib_oschema (OTime / OtomlSerialize folded in).
- `cargo update` re-resolves every git dep to its `development` tip.
- Only the SDK's hero_blueprint `hero_rpc_derive`/`hero_rpc2` patch remains,
  removed in the next step when the SDK regenerates with openrpc_client!.

oschema server scaffolding:
- Add `src/api.rs` (openrpc_server! over oschema/{admin,books,collections,docs,
  memory}) + `pub mod api`; service.toml → canonical multi-domain rpc.sock.
- Generated specs land in openrpc/openrpc_*.json.

API ports for the new development stack (build green, clippy clean):
- hero_proc_sdk job API: HeroProcClient_::connect() + .jobs(); job_create now
  takes JobCreate{spec,context,inputs} and returns a string sid; job_status →
  job_info; job_logs → JobLogsArgs{sid,lines}; (action_id dedup dropped — the
  current job API has no such filter). Admin logs now empty (owned by hero_proc).
- hero_proc_sdk action types: KillOther/HealthCheck/HealthCheckPolicy →
  Action*; health_checks is now a Vec; service_status via .jobs(), no context.
- Lifecycle helpers moved herolib_core::base → hero_lifecycle::base across all
  bins; add hero_lifecycle dep where needed.
Switches the serving path from the hand-rolled JSON-RPC dispatcher to the
macro-generated oschema surface. Each of the 5 domains (admin/books/collections/
docs/memory) is now served at /api/{domain}/rpc on hero_books/rpc.sock.

- rpc_impls/{bridge,mod,collections,books,docs,memory,admin}.rs: thin trait
  impls. The bridge dispatches to the unchanged web::rpc handler logic by the
  legacy dotted method name and snake-cases the result keys into the typed
  Outputs (memory.* dynamic results returned as JSON strings; libraries.list
  unwrapped to the bare list).
- web/axum_server.rs: build_extra_router (REST JSON API + /img + /pdf + /mcp +
  /admin) replaces the all-routes builder; start_rpc_only_server now hands that
  extra router + the 5 domain services to api::serve_domains_with. The macro owns
  the socket bind, /rpc + /openrpc.json + /health + /.well-known, banner and
  SIGTERM loop — so the hand-rolled accept loop, shutdown_signal, handler_rpc,
  handler_rpc_discover, handler_health and handler_well_known are deleted.
- web/rpc.rs is kept as the handler/logic layer the bridge calls (the match
  dispatcher is no longer the entry point).

Build + clippy clean. Runtime per-method result-shape verification + SDK
regeneration (openrpc_client!) + dropping the last hero_blueprint patch are the
next step (Phase 4).
feat(sdk): regenerate with hero_lib openrpc_client! — workspace fully on development
Some checks failed
Test / test (pull_request) Failing after 15m0s
Test / integration (pull_request) Has been skipped
e3d49be675
Completes the de-freeze: the SDK is now generated by herolib_macros::openrpc_client!
from the server's oschema (verbatim snake_case wire names, multi-domain bundle),
replacing the legacy hero_rpc_derive. With its last consumer migrated, the frozen
hero_macros_previous mirrors are fully retired.

- hero_books_sdk: openrpc_client!("../hero_books_server/oschema", service =
  "hero_books", name = "HeroBooksClient") → bundle client with .admin()/
  .books()/.collections()/.docs()/.memory() domain accessors + per-domain typed
  Input/Output. Deps: herolib_macros/herolib_openrpc (was hero_rpc_derive/rpc2).
- Drop the [patch."hero_blueprint.git"] set and the hero_rpc_derive/hero_rpc2
  workspace deps; cargo update removes the frozen crates from the lockfile. The
  whole workspace now builds on hero_lib development — no frozen pins remain.
- Delete the orphan openrpc.client.generated.rs (vestigial hero_rpc2 client).
- Update examples to the bundle API (server.health → books.list liveness probe).
- Fix a pre-existing clippy lint in hero_books_docusaurus tests.

Build + clippy clean (workspace + examples + tests).
fix(server): restore legacy /rpc compat endpoint (fixes 'Unexpected end of JSON input')
Some checks failed
Test / test (pull_request) Failing after 15m9s
Test / integration (pull_request) Has been skipped
adc1084003
The multi-domain oschema surface serves /api/{domain}/rpc, but the in-repo
readers (hero_books_web's base.html rpc() helper, hero_books_app) still POST the
legacy dotted methods (server.health, books.list, search.query, …) to bare /rpc.
After the migration the server no longer answered /rpc, so those fetches got an
empty 404 body and resp.json() threw 'Unexpected end of JSON input'.

Re-add a /rpc compat route to the extra router that dispatches the dotted names
through the unchanged handle_rpc_request dispatcher (the same logic the oschema
bridge already calls). Both surfaces coexist — /api/{domain}/rpc (canonical) and
/rpc (legacy alias) — with no route collision. The alias can be dropped once the
web/app readers move to /api/{domain}/rpc.

Verified on a live server: POST /rpc {server.health|books.list|rpc.discover}
returns valid JSON; /api/books/rpc still works.
fix(admin): import via REST (/api/import, /api/namespaces) — drop removed importservice.* RPC
Some checks failed
Test / test (pull_request) Has been cancelled
Test / integration (pull_request) Has been cancelled
d824c4b8c4
The oschema migration removed the legacy OSIS `importservice.*` JSON-RPC methods,
so the admin import page failed with '[-32601] Method not found: importservice.start'.
Repoint loadNamespaces/submitImport to the REST endpoints (GET /api/namespaces,
POST /api/import) — the same ones hero_books_web uses, proxied through the admin —
matching the {namespaces:[{name,displayName}]} and {jobId} response shapes.

Verified against a live server: /api/namespaces and /api/import return the
expected shapes; the import job is created and pollable via /api/import/log/{id}.
fix(admin): proxy /api/* to the backend (fixes import 'Unexpected end of JSON input')
Some checks failed
Test / test (pull_request) Has been cancelled
Test / integration (pull_request) Has been cancelled
95fb02caa4
The admin router only forwarded /rpc to hero_books_server; /api/* was unrouted, so
after the import page moved to REST (/api/import, /api/namespaces) those fetches hit
a 404 with an empty body and resp.json() threw 'Unexpected end of JSON input'.

Route /api/{*rest} to the existing api_proxy (same sub-router + AppState as /rpc).
Verified end-to-end through admin.sock: GET /api/namespaces -> {namespaces:[...]},
POST /api/import -> {jobId}; /rpc still works.
feat(admin): live import progress — poll /api/import/log and show the result
Some checks failed
Test / test (pull_request) Failing after 9m24s
Test / integration (pull_request) Has been skipped
b432279878
The import page only showed 'Import started' and never polled, so a slow or failed
job looked like nothing happened. Add a progress panel that polls
/api/import/log/{jobId} (the same endpoint hero_books_web uses), streams the log
lines, shows a complete/failed/warnings status, and refreshes the library list on
completion. Makes import outcomes (incl. errors like a missing hero_memory)
visible instead of silent.
chore: gitignore generated OpenRPC specs (openrpc/openrpc_*.json)
Some checks failed
Test / test (pull_request) Failing after 7m1s
Test / integration (pull_request) Has been skipped
fba4fbdfc0
Pure build output — openrpc_server!(save_openrpc_dir="openrpc/") regenerates them
each build from oschema/, and the served spec comes from the embedded
OPENRPC_JSON, not these files. Untrack + ignore to stop per-build churn.
Some checks failed
Test / test (pull_request) Failing after 7m1s
Test / integration (pull_request) Has been skipped
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin development_mahmoud:development_mahmoud
git switch development_mahmoud

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch development
git merge --no-ff development_mahmoud
git switch development_mahmoud
git rebase development
git switch development
git merge --ff-only development_mahmoud
git switch development_mahmoud
git rebase development
git switch development
git merge --no-ff development_mahmoud
git switch development
git merge --squash development_mahmoud
git switch development
git merge --ff-only development_mahmoud
git switch development
git merge development_mahmoud
git push origin development
Sign in to join this conversation.
No reviewers
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_books!158
No description provided.