hero_slides bucket C: slide linking + staleness + folder picker (closes 5/5 remaining undispatched methods) #54
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?
Status
Design locked 2026-05-10 by mik-tf. Implementation queued for session 89; will land as 3 mini-PRs (folder.pick → staleness pair → linking pair). The four "Recommendations needing approval" below are now Decisions — no further design discussion required before code.
TL;DR
Three small features close out the hero_slides JSON-RPC dispatcher (the last 5 of 15 undispatched methods surfaced by #49's schema audit). ~280 LOC across 3 mini-PRs. Design locked 2026-05-10 by mik-tf — recommendations below stand as-decided. Implementation queued for session 89.
slide.setLink,slide.clearLinkslide.getStaleness,deck.stalenessfolder.pickWhy now
Buckets A + B closed via PR #52 + PR #53. These 5 methods are the only ones still returning
-32000 "Method not found"to the dashboard's silent-catch JS callsites. Closing this finishes the hero_slides_server schema-vs-dispatcher alignment arc that started at #49.Each is a real user need, not just a tracker box-tick:
dashboard.js:4139.Decisions (locked 2026-05-10)
Slide linking — 4 design calls
metadata.tomlfield on the destination slide:linked_to = "<col>::<deck>::<slug>"A→B→AatsetLinktime (walk the chain, error on cycle)Staleness — 1 design call
A slide is stale if either:
.mdmtime > its rendered.pngmtime, ortheme.mdmtime > its rendered.pngmtime.StaleSlidecarries per-cause attribution (content_edited/theme_edited) so the dashboard badge can say which.Context-fingerprint drift (per ADR-0007) is intentionally not in the staleness signal yet — start simple, add later if user feedback demands it. Keeps the per-deck scan O(n) on filesystem mtimes only.
folder.pick— 1 architectural callCurated allowlist read from a hero_proc secret (
[folder.pick] allowed_roots = [...]). Default when unconfigured:~/Documents.Native OS file dialog (
rfd/nfd2crates) is ruled out — it would open on the server's display, not the user's browser. Doesn't make sense over JSON-RPC at all.Sequencing — 3 mini-PRs after approval
folder.pick(~30 LOC) — smallest, no contention. Can land first regardless of design calls above.dashboard.jsdoesn't already speak the new metadata shape.Acceptance
hero_slides_lib/src/deck.rs:1423-1448).scripts/smoke_openrpc.pyMETHOD_MISSING count drops 5 → 0.slide.setLink), 603, 648, 1090, 3566, 3628, 5330 (deck.staleness+slide.getStaleness), 4139 (folder.pick) stop catching-32000..md→deck.stalenessflags it within one call → dashboard's stale-badge updates.folder.pickreturns the configured allowlist; unconfigured server returns~/Documents.Out of scope
folder.pick(architecturally wrong over JSON-RPC).Refs
legacy_param_shim(bucket C handlers can rely on it)lhumina_code/hero_slides/crates/hero_slides_lib/src/deck.rs:1423-1448— the three lib stubslhumina_code/hero_slides/crates/hero_slides_server/openrpc.json— schema declarationsSigned-off-by: mik-tf
hero_slides_server: implement bucket C — slide.setLink/clearLink, slide.getStaleness, deck.staleness, folder.pick (5 methods blocked on missing lib code)to hero_slides bucket C: slide linking + staleness + folder picker (closes 5/5 remaining undispatched methods)Closed via hero_slides PR #55 (squash-merged 2026-05-10).
All 5 methods land against real
hero_slides_libimplementations — no stubs left indeck.rs:1423-1448.folder.pickfolder_pick_allowed_roots(contexthero_slides, newline-separated,~/Documentsfallback)deck.stalenessdeck_staleness()using ADR-0007DeckInputsContext::for_deck+compose_inputs_hash, per-componentreasonsvia comparison againstlast_*snapshot fieldsslide.getStalenessslide_staleness()wrapperslide.setLinkdeck_slide_link_setwrites typedSlideLinkonSlideMetaEntry.source_link; cycle detection rejectsA→B→Aand self-link; source PNG copied verbatimslide.clearLinkdeck_slide_link_clearclearssource_link, leaves PNGVerified locally end-to-end via the live
hero_slides_admindashboard. Drivingwindow.rpc()directly with the freshly-built binaries:examples/sample_deck/output/01_intro.pngmetadata.tomlnow records the full ADR-0007 snapshot (last_generated,last_theme_hash,last_context_fingerprint,last_prompts_hash,last_image_model)slide.getStalenessright after generate:is_stale=false01_intro.md→slide.getStaleness:is_stale=true, reasons=["content_edited"]← canonical positive case fires correctlysetLinkof two slides → md5 of source/dest PNGs match byte-for-byte"cannot link a slide to itself""linking 01_title → 01_intro would form a cycle"Three reconciliations vs the original issue body captured in workspace decision file
D-09-slide-linking-and-staleness.md:StaleSlidetype andcompute_inputs_hashinfrastructure was richer than the issue assumed.SlideLink+SlideMetaEntry.source_link, not a rawlinked_tostring field.folder.pickschema description flipped from"native OS dialog"to allowlist semantics; params/result shape unchanged.Coupled fix: hero_aibroker PR #68 was required for AI generate to work — the broker silently dropped
message.imagesfrom OpenRouter responses because theMessagestruct had no such field. Without that fix, everyslide.generatefailed with"No images in response"(a regression unrelated to bucket C but on the critical path for validation).Also fixes a pre-existing bug in
slide_generate_with_selectionandslide_generate_with_contextthat only persistedSlideMetaEntry.hashon AI render, never the ADR-0007 inputs-hash snapshot. Without those fields, every generated slide stayed in the back-fill rule forever andcontent_editednever fired.