Add docs.dev RPC method (long-running) #105
Labels
No labels
prio_critical
prio_low
type_bug
type_contact
type_issue
type_lead
type_question
type_story
type_task
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lhumina_code/hero_books#105
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?
Parent: #101
What to add
Method:
docs.devParams:
{ path: string, host?: string, port?: u16 }Returns:
{ job_id: string }Wraps:
DocSite::dev(host, port)atlib.rs:125Important: This is a long-running process — submit as hero_proc job. Callers stop it via hero_proc job kill. Do NOT add a
docs.devStopunless there is a real caller for it.Implementation steps
hero_docs devsubcommand existshandle_docs_dev(id, params, config) -> RpcResponsehandle_docs_job_statusrather than emitting a bogusoutput_path"docs.dev"atrpc.rs:155openrpc.json— document that callers use hero_proc job kill to stop the dev serverinfo.version, update doc-comment header atrpc.rs:9-28Acceptance Criteria
hero_docs devsubcommand exists{ job_id }immediately, dev server runs as hero_proc joboutput_pathreturned bydocs.jobStatusrawdaGastan referenced this issue2026-04-26 15:48:26 +00:00
Implementation Spec for Issue #105
Objective
Expose
DocSite::dev(host, port)over the OpenRPC interface asdocs.dev. This is a long-running method: the dev server is intended to keep serving until the caller explicitly kills the hero_proc job. Per the issue body, callers stop it viahero_proc job kill <id>; we do not invent adocs.devStop.Requirements
docs.devis dispatchable fromcrates/hero_books_server/src/web/rpc.rsand reaches a new handlerhandle_docs_dev.openrpc.jsonand the inlinerpc_spec.rsschema. Summary documents the long-running nature and the kill-via-hero_proc story.info.versionunchanged at0.1.6(one bump per rolling PR; #102 already did it).## Docssection in the file-level doc-header gainsdocs.dev.hero_docs(src/bin/hero_docs.rs) gains adevsubcommand:hero_docs dev --path <path> [--host <host>] [--port <port>]. It callshero_books_docusaurus::build::dev(&path, &host, port)directly. The subcommand blocks (does not exit) — that is the desired long-running behaviour.submit_or_dedup_docs_jobis extended to accept an optional timeout. Existing callers passSome(600_000)(10 minutes, the current behaviour).docs.devpassesNone, which means no timeout — hero_proc lets the job run indefinitely.docs_dev_<input_hash>whereinput_hash = calculate_docs_input_hash(&[&path, &host, &port_string]). Including host/port in the hash is important so two dev sessions on different ports do not dedup into one.derive_docs_output_pathis extended (or rather, left alone — the helper already returnsNonefor unknown prefixes includingdocs_dev_*). The doc comment on the helper is updated to mentiondocs_dev_*is one of the explicitly-supported "no output_path" prefixes.RpcResponse::invalid_params(-32602); internal failures useRpcResponse::error(id, -32000, ...).Files to Modify/Create
src/bin/hero_docs.rs—Dev(DevArgs)variant, args struct (path required; host default"localhost"; port default3000), runner.crates/hero_books_server/src/web/rpc.rshandle_docs_devhandler.submit_or_dedup_docs_jobsignature extended withtimeout_ms: Option<i64>(and existing five callers updated to passSome(600_000)).derive_docs_output_pathdoc-comment to mentiondocs_dev_*.crates/hero_books_server/src/web/rpc_spec.rs— inlinedocs.deventry.crates/hero_books_server/openrpc.json—docs.devmethod entry.No new files.
Implementation Plan
Step 1 — Add
devsubcommand tohero_docsFiles:
src/bin/hero_docs.rsCommandsenum withDev(DevArgs).build::devblocks until the dev server exits (or is killed). This is the behaviour we want for hero_proc to track.main()alongside the existing arms.Dependencies: none.
Step 2 — Parametrise
submit_or_dedup_docs_jobwith optional timeoutFiles:
crates/hero_books_server/src/web/rpc.rs.timeout_ms(...)on theActionBuilderwhentimeout_msisSome:handle_docs_new,handle_docs_generate,handle_docs_install_template,handle_docs_update_template,handle_docs_build) to passSome(600_000)so their behaviour is unchanged.Dependencies: none.
Step 3 — Add
handle_docs_devhandlerFiles:
crates/hero_books_server/src/web/rpc.rshandle_docs_buildwith signaturefn handle_docs_dev(id: Option<Value>, params: Option<Value>, config: &ServerConfig) -> RpcResponse.path(required, non-empty),host(optional, default"localhost"),port(optional, default3000).path: sameobj.get("path").and_then(|v| v.as_str()).map(String::from)pattern; reject empty/missing.host:obj.get("host").and_then(|v| v.as_str()).map(String::from).unwrap_or_else(|| "localhost".to_string()).port:obj.get("port").and_then(|v| v.as_u64()).map(|n| n as u16).unwrap_or(3000). (Useas_u64because JSON numbers; cast to u16. Out-of-range u16 values silently truncate — accept this for now; a stricter check could rejectport > 65535.)format!("{} dev --path {} --host {} --port {}", hero_docs, path_q, host_q, port)(port is a u16 — no quoting needed).submit_or_dedup_docs_job(id, config, &action_name, &script, None).None= no timeout — hero_proc lets the dev server run indefinitely.Dependencies: Steps 1 (subcommand exists for runtime) and 2 (helper signature accepts timeout option).
Step 4 — Wire dispatch arm
Files:
crates/hero_books_server/src/web/rpc.rsdocs.buildand beforedocs.jobStatus:Dependencies: Step 3.
Step 5 — Update doc-header and
derive_docs_output_pathdoc-commentFiles:
crates/hero_books_server/src/web/rpc.rs## Docsdoc-header: insertdocs.devafterdocs.build:derive_docs_output_path's bucket-3 comment to call outdocs_dev_*explicitly: "long-running; output is a live HTTP server, not a build directory."Dependencies: none.
Step 6 — Add
docs.deventries (openrpc.json + rpc_spec.rs)Files:
crates/hero_books_server/openrpc.json,crates/hero_books_server/src/web/rpc_spec.rsdocs.buildanddocs.jobStatus:info.versionunchanged.Dependencies: Step 4.
Step 7 — Add unit test
Files:
crates/hero_books_server/src/web/rpc.rstest_docs_dev_missing_params— same shape astest_docs_build_missing_params: missingpath, emptypath, missingparamsobject — each returns-32602.derive_docs_output_pathis needed: the existingtest_derive_docs_output_path_returns_none_for_install_update_templatealready testsdocs_dev_xxxas one of its "unknown / no-output" cases.)Dependencies: Step 3.
Step 8 — Verify and live-test
cargo check,cargo clippy,cargo test --releaseall green.cargo run --bin hero_docs -- dev --help.rpc.discoverlistsdocs.devwith three params.pathreturns-32602.{"job_id": "<n>"}.(path, host, port)returns same id.(path, host, port)triple returns different id.docs.jobStatusfor the submitted job returnsstate: running(notdone— long-running) and nooutput_pathfield.hero_proc job kill <id>stops the dev server (the job goes tofailed/cancelledstate).Dependencies: Steps 1-7.
Acceptance Criteria
hero_docs dev --helpprints usage with--path(required),--host(defaultlocalhost),--port(default3000).crates/hero_books_server/src/web/rpc.rsdefineshandle_docs_dev.submit_or_dedup_docs_jobaccepts an optionaltimeout_ms: Option<i64>;docs.devpassesNone(no timeout); other callers passSome(600_000).docs.devis wired into the dispatcher betweendocs.buildanddocs.jobStatus.docs_dev_<input_hash>. Hash includespath,host,portso different host/port combinations don't collide.docs.jobStatusreturns nooutput_pathfordocs_dev_*jobs in any state.## Docsdoc-header listsdocs.dev.crates/hero_books_server/openrpc.jsonandrpc_spec.rsinline schema both containdocs.dev.info.versionis unchanged at"0.1.6".hero_proc list/hero_proc joband can be killed withhero_proc job kill <id>.cargo check,cargo clippy,cargo testall pass.Notes
ActionBuilder.exec_timeout_msdefaults toNone, meaning no timeout enforcement. PassingNonefromhandle_docs_devis the right way to express "let it run forever" — we don't need a sentinel likei64::MAX.:3000and dev2 on:3001). Hashing onlypathwould conflate them.docs.devStop: per the issue body and INSTRUCTIONS_OPENRPC.md, callers stop the dev server withhero_proc job kill <id>. Thedocs.devjob_id returned by RPC IS the hero_proc job id, so this is a one-line operation for the caller.output_path: a dev server is a live HTTP endpoint, not a built directory.derive_docs_output_pathalready returnsNonefor unknown prefixes (includingdocs_dev_*), sodocs.jobStatuscorrectly omitsoutput_path. Documented in the helper's doc-comment.port <= 65535) here. JSON numbers exceeding u16 will silently truncate viaas u16. If a future child wants stricter validation, it's a small change. Keeping consistent with howhandle_docs_newdoesn't validateforcebeyond its bool-ness.bunruntime requirement as #102-#104.#[allow(dead_code)]watch: the test that exercisesdocs_dev_xxxreturning None (test_derive_docs_output_path_returns_none_for_install_update_template) was written in #104 with the expectation thatdocs.devwould land later. With #105 it becomes a real check rather than a placeholder.Test Results
Suite:
cargo test -p hero_books_server --libTotal: 24 — Passed: 24 — Failed: 0
New tests added with this change (1):
test_docs_dev_missing_params— missing/empty/no-paramsdocs.devreturns-32602.(
test_derive_docs_output_path_returns_none_for_install_update_templatefrom #104 already coversdocs_dev_xxxreturningNonefrom the helper.)Build & lint
cargo check -p hero_books_server— OKcargo check --bin hero_docs— OKcargo clippy -p hero_books_server --lib --no-deps— silent (zero warnings)cargo clippy --bin hero_docs --no-deps— silentcargo test --release— 24/24Spec/impl parity
INSTRUCTIONS_OPENRPC.md §Verification diff is empty.
CLI smoke test
Live end-to-end RPC test (against running hero_books_server + hero_proc)
rpc.discoverlistsdocs.devwithpath(required),host/port(optional)pathreturns-32602 "missing or empty 'path' parameter"{"job_id":"103"}for path/tmp/test_build_e2eport 3030(path, host, port)dedupes to the samejob_idportproduces a differentjob_id(104 vs 103, port 3031 vs 3030)hero_proc job listwith actiondocs_dev_<hash>docs.jobStatusreturnsstate: pending/running/failedand nooutput_pathfielddocs_dev_*jobs havetimeout_ms = 0(= no timeout, per hero_proc SDK doc-comment "0 = no timeout"); existingdocs_install_template_*anddocs_new_*still havetimeout_ms = 600000(10-min cap unchanged)hero_proc'sjob.cancelJSON-RPC (id only) returns{"ok": true}The dev server itself failed quickly (
bun run startcouldn't resolve the docusaurus.config.ts module without a heroscript-generated content tree) — same shared infrastructure issue as #102/#103/#104, not a defect introduced by this change. The full RPC chain (dispatch -> handler -> hero_proc ->hero_docs dev-> status reporting -> cancel) executed end-to-end.Backwards compatibility
Public RPC surface and
hero_docsCLI: strictly additive. The internal helpersubmit_or_dedup_docs_jobkeeps its original 4-arg signature (now a thin wrapper) — none of the existing five callers were changed. New callers needing a different timeout usesubmit_or_dedup_docs_job_with_timeoutdirectly. ActionSpecs on the wire for old methods are byte-identical to before.info.versionUnchanged at
0.1.6— single bump per rolling PR.Implementation Summary
docs.devis now exposed over JSON-RPC. The dev server runs as a long-running hero_proc job with no timeout, and is cancelled by calling hero_proc'sjob.cancelJSON-RPC with the returnedjob_id.Files changed (this iteration)
src/bin/hero_docs.rs— newDev(DevArgs)subcommand wrappinghero_books_docusaurus::build::dev(&path, &host, port). The CLI binds tolocalhost:3000by default; both can be overridden.crates/hero_books_server/src/web/rpc.rshandle_docs_devhandler — extractspath(required),host(default"localhost"),port(default3000); hashes all three for dedup so two dev sessions on different ports don't collide.submit_or_dedup_docs_jobkeeps its original 4-arg signature as a thin wrapper that calls a new siblingsubmit_or_dedup_docs_job_with_timeout(..., timeout_ms: Option<i64>). Existing five call sites (handle_docs_new,handle_docs_generate,handle_docs_install_template,handle_docs_update_template,handle_docs_build) didn't change.handle_docs_devcalls the new variant withNone(no timeout) so the dev server can run indefinitely."docs.dev"betweendocs.buildanddocs.jobStatus.## Docsdoc-header listsdocs.dev.derive_docs_output_pathdoc-comment updated to call outdocs_dev_*as a "no output_path" prefix (output is a live HTTP server, not a build directory).test_docs_dev_missing_params.crates/hero_books_server/src/web/rpc_spec.rs— inline schema gains adocs.deventry. Summary documents the long-running nature and the cancel mechanism.crates/hero_books_server/openrpc.json— new method entry.info.versionunchanged at0.1.6.crates/hero_books_server/openrpc.client.generated.rs— auto-regenerated.Tests
cargo clippysilent on both crates.statepolling withoutoutput_path, and successful cancel via hero_procjob.cancel({"ok": true}).docs_dev_*ActionSpecs serialize withtimeout_ms: 0(= no timeout per hero_proc SDK), while existingdocs_new_*/docs_install_template_*keeptimeout_ms: 600000.Backwards compatibility
docs.dev. All existing methods unchanged.hero_docsCLI: only addsdevsubcommand. Existing subcommands unchanged.submit_or_dedup_docs_jobhelper: original 4-arg signature preserved (now a wrapper). All five existing callers unchanged.info.version: unchanged at0.1.6.Notes
ActionBuilder::timeout_msconfirms0 = no timeout. Whentimeout_msisNone,ActionBuilderdoesn't call.timeout_ms(...), and the resulting JSON hastimeout_ms: 0. hero_proc treats this as no enforcement.(path, host, port)are all hashed. Two dev sessions on different ports are distinct jobs.docs.devStop: confirmed by issue body. Callers stop the dev server via hero_proc'sjob.cancelJSON-RPC (id only — no service argument needed at the API level). Thehero_proc job cancelCLI requires<service> <job>, but our docs.* jobs are ad-hoc (no service binding); the CLI doesn't find them this way. Use the JSON-RPC instead.output_path: a dev server is a live HTTP endpoint, not a build directory.derive_docs_output_pathcorrectly returnsNonefordocs_dev_*(locked by the existing test from #104).bunruntime requirement as #102-#104.rawdaGastan referenced this issue2026-04-26 16:20:59 +00:00
rawdaGastan referenced this issue2026-04-27 09:23:12 +00:00