hero_slides_server: align JSON-RPC dispatcher with openrpc.json schema (sweep) #49
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
PR #48 closed the
collection.*+rpc.examplesPathdispatcher gap surfaced during #47 validation. While exercising the rest of the dashboard's CRUD on top of that fix, three more schema-vs-dispatcher mismatches surfaced in the same class. Filing as a sweep so the dashboard's full UI is unblocked, not the patch-one-method-at-a-time pattern.Symptoms (each silently breaks a UI flow)
deck.listrecords:{collection, deck_name, name, slide_count, generated_count, has_pdf, has_background}(perDeckSummary){collection, name, path, slide_count, generated_count, has_pdf, has_background}(nodeck_name)${col}::undefinedso user can't switch decks; URL-hash auto-select doesn't fireslide.insertparams:{collection, deck, at, slug}{deck_path, at, slug}(legacy)slide.getContentparams:{collection, deck, slide}(per generated client){deck_path, slide_name}(legacy)These are all the same root cause as PR #48: the schema (and codegen + JS UI) are on the new
{collection, deck, ...}API; the dispatcher is partially still on the legacy{deck_path, ...}form. Each silent failure is invisible to the operator because the JS wraps intry { … } catch { toast(…) }—console.messagesstays empty, browser DevTools shows nothing, only the side-effect (counter not incrementing, dropdown not selectable) tells you something failed.Acceptance
deck.listresponse records includedeck_name(matchesDeckSummaryschema inopenrpc.json); UI deck dropdown options are uniquely-keyed.deck.listaccepts the optionalcollectionfilter param the JS sends inloadDecks().slide.insertaccepts{collection, deck, at, slug}per schema (withdeck_pathkept as a deprecation-warn fallback for one release if needed).slide.getContentaccepts{collection, deck, slide}per schema (likewise).crates/hero_slides_server/openrpc.jsonround-trips a sample call (whether bycargo testor a small smoke script) so any other drift surfaces in CI rather than a user click.hero_aibrokerreads its API keys (grep -rn 'GROQ_API_KEY\|GEMINI\|OPENAI_API_KEY' lhumina_code/hero_aibroker/crates/). Two cases: (a) reads from process env → ensure~/hero/cfg/env/env.shis sourced for the shell that startedhero_proc_server, since hero_proc-supervised actions inherit hero_proc's env (not the operator's). (b) reads from hero_proc secrets per home#225 META compliance → populate viaproc secret set GEMINI_API_KEY_PAID …etc. Then triggerdeck.generateon a deck with markdown slides + theme; tailproc logs get hero_aibrokerto see the outbound call; verify a generated.pnglands inexamples/<deck>/output/. Acceptance: at least one01_title.md→01_title.pnground-trip via the configured image model (gemini-3.1-flash-image-previewis the default per<meta image-model-basic>). Without this, the dashboard's centerpiece feature ('AI slide generation') stays unverified end-to-end on this stack.Design note
The schema is the single source of truth (per memory
project_openrpc_philosophy). The fix direction should be: bring the dispatcher to matchopenrpc.json, not the other way. Where backward compat matters (existing scripts hittingdeck_path), keep the legacy alias with atracing::warn!deprecation just like theparam_string(primary, legacy, …)helper already does fordeck_path/path.Follow-up
Also surfaced in #47's session 85 close-out:
service slides start --allbrings up proc → router → db but not mycelium even though hero_aibroker (a transitive prereq) needs it. Plus--clearon proc wipes prior mycelium registration. → file inhero_skillsseparately.--target rootis documented + dispatched inservice_mycelium.nu(line 521) but rejected byclients/target.nuvalidator ({driver|common|self}only). → file inhero_skillsseparately.Refs:
Signed-off-by: mik-tf
Session 86 closure — 2026-05-09
Merged: PR #50 (
632603f).Acceptance status
deck.listrecords includedeck_name; UI deck dropdown options uniquely-keyeddeck.listaccepts optionalcollectionfilter paramslide.insertaccepts{collection, deck, at, slug}(legacydeck_pathwarn-fallback)slide.getContentaccepts{collection, deck, slide}(legacyslide_namewarn-fallback)scripts/smoke_openrpc.pyshippedWhat shipped
PR #50 adds a single request-preprocessor (
legacy_param_shimincrates/hero_slides_server/src/rpc.rs) that translates the publicopenrpc.jsonshape ({collection, deck, slide, slides, to, filename}) into the legacy keys the existing handlers expect (deck_path,slide_name,slide_names,to_position,file,root_path,parent_path,src_deck_path,dst_deck_path, plus a synthesisedpathforbg.*). Reduces what would have been ~46 per-handler edits to one centralised translation step.Also adds:
deck_namefield ondeck.listrecords (was the load-bearing fix for the broken deck dropdown — every option was collapsing to${col}::undefined).collectionfilter ondeck.list.param_deck_path,param_collection_root,param_slide) for handlers that want to call the resolver directly.scripts/smoke_openrpc.py— Python stdlib smoke harness, classifies every method as OK / METHOD_MISSING / PARAM_MISMATCH / RUNTIME_ERROR.Smoke results
Zero PARAM_MISMATCH on read paths. Every UI flow that depended on the new
{collection, deck, slide}API now reaches its handler.Hero Browser MCP UI smoke
Against
http://127.0.0.1:9988/hero_slides/admin(1440×900 headless, dark theme, console interceptor on):hero_slides_examplescollection → 2 deck cards (hero_slides_intro,sample_deck) render with state badges + per-deck Preview/Play/PDF actions.hero_slides_intro→ all 6 slide thumbnails render with PNG previews.Editon01_title→ editor opens with markdown content + preview pane + Save / Save & Quit / Save & Generate / Image Model selector / Context selector.console.messages = []at every stage.Remaining work — filed separately
deckjobs.*(5),wizard.*(4),slide.setLink/clearLink/setImageModel/getStaleness/revertToLastGenerated/resolveContext(6),bg.extractTheme,deck.staleness,folder.pick. Same silent-failure class as thecollection.*gap PR #48 closed; these need new handlers, not just shape fixes. → filed as a separate issue.gemini-3.1-flash-image-preview(the dashboard's default per<meta name="image-model-basic">). Routing falls back to a Qwen model that doesn't accept Gemini'simage_configparameter. → filed as a hero_aibroker issue.service slides start --allbrings up proc → router → db but not mycelium even thoughhero_aibroker(transitive prereq) requires it;--clearon proc additionally wipes prior mycelium registration. (b)--target rootdocumented + dispatched inservice_mycelium.nuline 521 but rejected byclients/target.nuvalidator ({driver|common|self}only). → filed as 2 hero_skills issues.Closing as DONE for the schema-alignment scope. Continuation work is in the new tracker(s).
Signed-off-by: mik-tf