Codegen alignment + hero_rpc2 vendor + rename hero_service → hero_lifecycle #61
No reviewers
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
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lhumina_code/hero_rpc!61
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "issue-55-codegen-alignment"
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?
Closes #55.
Lands all 6 sections of the codegen-alignment work —
service.tomlas source-of-truth, hero_rpc2 vendor + HeroRequestContext rails,_adminscaffold usinghero_admin_lib,hero_service→hero_lifecyclerename, generatedmain.rsmatchinghero_proc_server, and canonical_admin/_sdknaming suffixes.Full section-by-section landing summary in the issue thread (last 7 comments). 141 tests pass,
cargo check --workspace+cargo fmt --checkclean.Follow-ups: #60 (method translators — clean rails-on), #59 (Generator per-target refactor — defer until #60 lands).
7 cross-repo branches
issue-55-hero-lifecycle-renamepinned to this branch are ready to flip + merge once this lands.Per hero_rpc#55 §4 and hero_skills#262 (locked decision B on hero_skills#261): the hero_service name is now reserved for the canonical service template repo, so this crate is renamed to hero_lifecycle. Code is unchanged. Touches: - crates/service/ → crates/hero_lifecycle/ (git mv) - Cargo.toml: workspace member + workspace.dependencies entry - crates/hero_lifecycle/Cargo.toml: package.name + description (notes rename) - crates/hero_lifecycle/src/{lib,hero_server,service,test}.rs: doc-comment imports - crates/hero_lifecycle/src/bin/main.rs: BINARY_NAME + clap name + help strings - crates/hero_lifecycle/examples/test_server.rs: use + cargo invocations - crates/server/{Cargo.toml,src/lib.rs,src/server/{cli,server,spawn}.rs,README.md} - crates/generator/src/build/{emit/bin.rs,scaffold.rs}: emit hero_lifecycle in generated/scaffolded code so downstream services pick up the new name Downstream consumer bumps (hero_osis, hero_inspector, hero_proxy, hero_fossil, hero_archipelagos, ...) land in follow-up commits per repo, per #55 thread q3a. Refs #55 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>Adds the canonical entry point for service-side build.rs files: fn main() { hero_rpc_osis::build::OschemaBuilder::from_service_toml() .generate() .expect("OSchema generation failed"); } Codegen READS service.toml; it never writes it (per #55 §1 and the hybrid direction locked in #55 comment 33680). The parsed ServiceToml is stored on OschemaBuildConfig::service_toml so future emitters (§3 _admin scaffold, §5 generated main.rs) can pull service name / version / binaries without re-parsing. Changes: - crates/generator/src/build/config.rs: * OschemaBuildConfig::service_toml: Option<ServiceToml> field * OschemaBuildConfig::from_service_toml() — reads $CARGO_MANIFEST_DIR/service.toml * OschemaBuildConfig::from_service_toml_at(path) — explicit-path variant * Both call herolib_core::base::validate_service_toml and emit cargo:rerun-if-changed for the file * Modern defaults: schemas_dir = "schemas", nested_layout = true, target = GenerationTarget::Server - crates/generator/src/build/builder.rs: * OschemaBuilder::from_service_toml() convenience * generate() now runs rustfmt over src/, sdk/, and configured client/server crate dirs after emission — best-effort, missing rustfmt is silently skipped. Lets consumers drop their hand-rolled format_generated_sources() helpers - crates/generator/src/build/fs.rs: * rustfmt_dir(dir, debug) + collect_rs_files(dir, &mut out) helpers - example/recipe_server/service.toml: * NEW — canonical service.toml for the example, owned by contributor - example/recipe_server/build.rs: * Collapsed from 41 lines to 5 — uses from_service_toml() cargo check --workspace + cargo fmt --check both green. Refs #55 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>§6 — naming suffixes -------------------- Per hero_rpc#55 §6 / hero_skills hero_service_check_fix.md, scaffolded crates now follow the canonical _server / _admin / _sdk suffixes. `_ui` and `_client` are gone. Touches: - crates/<name>_ui → crates/<name>_admin (workspace, dirs, Cargo.toml, buildenv.sh, generate_admin_crate, generate_admin_main_rs) - crates/<name>_client → crates/<name>_sdk (workspace, dirs, Cargo.toml, build.rs client_crate_dir target, generate_sdk_crate) - WorkspaceScaffolder API: with_ui()/without_ui() → with_admin()/without_admin(); deprecated with_http()/without_http() shims removed. `generate_ui` field → `generate_admin`. - CLI: `--no-ui` kept as a back-compat alias for `--no-admin` - generator/src/build/emit doc + scaffolder doc comments updated No external consumers — grep across lhumina_code finds zero call sites of the renamed methods outside this repo. §5 — main.rs boilerplate matching hero_proc_server -------------------------------------------------- Scaffolded `_server` and `_admin` main.rs now follow the spec: 1. `service_base!()` at module scope → SERVICE_TOML const 2. `validate_service_toml(SERVICE_TOML)` first in main → fail-fast on bad TOML 3. `handle_info_flag(SERVICE_TOML)` second → --info / --info --json 4. `print_startup_banner(...)` → via herolib_core::base 5. `prepare_sockets(...)` → via herolib_core::base `HeroLifecycle` import moved from `hero_rpc_server::{OServer, HeroLifecycle}` to the canonical `hero_lifecycle::HeroLifecycle` (per §4 rename) and `hero_rpc_server::OServer` separately. Banner + sockets are called inside the `OServer::run_cli` callback so they fire only in foreground mode (skipped on --start/--stop dispatch). Per-crate service.toml writes ----------------------------- `service_base!()` expands to `include_str!("../service.toml")` so every binary crate needs its own service.toml at the crate root. The scaffolder now emits one via the new `write_binary_service_toml` helper (write-only-if-missing, never clobbers contributor edits — per #55 §1). Coverage: 6 new assertions in test_scaffold_server_main_* + 2 new in test_scaffold_admin_main_uses_hero_ui_server pin the §5 boilerplate. Still pending in §5 area: the per-domain bin emitter (`emit/bin.rs::generate_per_domain_bins`) and the `generate_single_bin` orchestrator still emit `HeroLifecycle::new(...)` without the `service_base!()` pattern. Those bins live at `src/bin/<name>.rs` inside an existing crate, so the `../service.toml` path the macro expects doesn't resolve. Fixing that means either a custom macro or restructuring those bins as separate crates — both feel like a follow-up rather than this commit's scope. cargo check --workspace ✅ cargo fmt --check ✅ 8/8 scaffold tests ✅ Refs #55 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>Per hero_rpc#55 §3 (as corrected by hero_skills#262: "Build new admin features on hero_admin_lib, not from scratch"), the scaffolded `_admin` crate now consumes the shared framework primitives instead of rolling its own axum router on top of `HeroUiServer`. generate_admin_crate Cargo.toml: - Drops hero_lifecycle dep (HeroUiServer no longer used here) - Adds hero_admin_lib (git dep on hero_website_framework) - axum bumped 0.7 → 0.8 to match hero_admin_lib - tokio narrowed to {macros, rt-multi-thread, signal} features - tower-http dep dropped (CORS is not needed for hero_router-fronted admins) generate_admin_main_rs: - Imports hero_admin_lib::{middleware::base_path_middleware, routes::{health_response, heroservice_manifest}, socket::{admin_socket_path, bind_socket}} - /health: `health_response(service, version)` - /.well-known/heroservice.json: `heroservice_manifest(...)` - base_path_middleware as a layer (lifts X-Forwarded-Prefix / X-Hero-Theme / X-Hero-Context / X-Hero-Claims into req extensions so the contributor's handlers can branch on hero_router state) - Socket: admin_socket_path("<name>_admin") + bind_socket(&path) - axum::serve directly (no more HeroUiServer wrapper) - Still wraps the §5 startup boilerplate (service_base!() + validate_service_toml + handle_info_flag + print_startup_banner + prepare_sockets) — those stay regardless of which framework owns the router test_scaffold_admin_main_uses_hero_admin_lib (renamed from test_scaffold_admin_main_uses_hero_ui_server) pins the new imports + asserts HeroUiServer is gone. All 8 scaffold tests pass. cargo check --workspace ✅ cargo fmt --check ✅ §2 (hero_rpc2 vendor + hybrid SDK + Python target + HeroRequestContext header-lift) remains — that's the next and final §, almost certainly a separate PR given size. Refs #55 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>Closes the loop on the HeroRequestContext extension landed in part 2. The HTTP transport now: 1. Reads `lifted_headers` from `ServerBuilder` and threads it through `serve_http` and `dispatch` (`Arc<Vec<String>>`, cloned per request). 2. Per request, calls a new `build_context(headers, lifted)` helper that: - Always lifts `X-Hero-Context` and `X-Hero-Claims` into the typed `HeroRequestContext::{context_id, claims}` fields. - Lifts any other header named in `lifted_headers` into `HeroRequestContext::extras` (case-insensitive, last value wins). 3. Wraps the actual `dispatch_json` call in `with_context(ctx, ...)` so handlers — including any tasks they `await` — can read the context via `hero_rpc2::current_context()`. The line transport is unaffected: it has no HTTP headers, so handlers reached over `serve_line` see `current_context() == None`. New integration test: `tests/http_context_lift.rs` (uds-http feature) - ctx_is_absent_without_headers_and_without_config — Bare request gets HeroRequestContext::default() (`is_anonymous = true`); fields are null but the context itself is in scope. - x_hero_context_and_claims_are_always_lifted — Both typed headers populate the struct even without with_lifted_headers configured. - configured_extras_show_up_in_extras_map — Custom `X-Trace` declared via `with_lifted_headers` lands in `extras`; overlap with X-Hero-* is harmless (typed fields take precedence). All 3 new tests + all 19 pre-existing transport / discover / error / notification / batch / large-message tests pass. cargo check --workspace ✅ Refs #55 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>Per hero_rpc#55 comment 33681 ("Python SDK confirmed new + in scope. Added as a fourth target alongside JS/TS and Rhai"), this commit adds the codegen rails. Method translation is deliberately deferred to a follow-up commit; the scaffolds carry `# TODO hero_rpc#55 §2 part 5:` markers so the gap is visible. What this commit covers ----------------------- - `crates/generator/src/build/emit/python_sdk.rs` — new emit module with `OschemaBuilder::generate_python_sdk(sdk_base, domains, result)`. Writes: `<sdk>/python/hero_<svc>_sdk/<domain>.py` — class skeleton `<Pascal>Client(http)` with TODO marker for method emission `<sdk>/python/hero_<svc>_sdk/__init__.py` — re-exports the per- domain Client classes `<sdk>/python/pyproject.toml` — PEP 621 manifest, Python ≥ 3.10, httpx dep; preserved if it already exists - `OschemaBuildConfig::generate_python_sdk: bool` + builder method `.with_python_sdk()`. Defaults off. - `OschemaBuilder::generate()` dispatches to the emitter when the flag is set, after the Rust SDK and rust_rpc2 hooks. What's deliberately deferred (part 5 follow-up) ----------------------------------------------- - OSchema types → Python `@dataclass` definitions (SmartId → str, optional fields → `Optional[...]`, lists → `list[...]`, closed enums → `StrEnum` / `Literal[...]`). - OSchema methods → typed async client methods on the Client class, dispatching JSON-RPC 2.0 over httpx. - A round-trip test once Python toolchain lands in CI. Tests ----- 3 new unit tests: - `python_sdk_flag_defaults_off` — new flag doesn't change current behaviour for unrelated consumers. - `with_python_sdk_flips_flag` — builder method works. - `emit_python_domain_writes_expected_skeleton` — output contains the pascal-cased Client class (`recipes` → `RecipesClient`), the back- reference to #55, and the TODO marker. All 111 generator tests pass. cargo check --workspace ✅ Refs #55 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>