lab publish CI broken: openrpc_client! rejects {str: any} inputs from #140 (Rule 2) #143

Closed
opened 2026-06-08 15:48:43 +00:00 by mahmoud · 2 comments
Owner

Summary

After merging #140 (feat(jobs): restore typed inputs end-to-end) into development, the lab publish CI fails to build. lab depends on hero_proc_sdk as a git dependency, and the SDK's openrpc_client! macro now rejects the schema introduced by #140:

error: proc macro panicked
  --> crates/hero_proc_sdk/src/openrpc_client/mod.rs:22:1
   = help: message: Schema::to_openrpc: schema violates the method-input contract:
     - type `JobCreate` uses the `any` type, which is forbidden ... See /openrpc_principles, Rule 2.
     - type `RunSpec` uses the `any` type, which is forbidden ... See /openrpc_principles, Rule 2.

This cascades into ~9 E0432 unresolved-import errors (HeroProcClient_, JobCreate, RunSpec, Action, etc.) because codegen aborts, and the whole hero_proc_sdk lib fails to compile — breaking every downstream consumer (lab, admin, examples).

Root cause

#140 modeled per-invocation typed inputs as a free-form map using the any value type, in two oschema types:

  • crates/hero_proc_server/oschema/jobs/40_run.oschemaRunSpec.inputs: {str: any}
  • crates/hero_proc_server/oschema/jobs/30_job.oschemaJobCreate.inputs: {str: any}

{str: any} generates HashMap<String, serde_json::Value>. The openrpc_server! codegen tolerated this, so #140 built and tested green locally against the server, but the openrpc_client! codegen enforces openrpc principles Rule 2, which forbids any entirely (no constructor, no typed accessors, no compile-time check). The enforcement was tightened (see stopgap/openrpc-enforcement-b1), so the client-side build that lab performs now hard-fails.

Fix

Re-model inputs without any, using the existing in-repo precedent: Action.input_schema is already a str holding a JSON document (a JSON Schema). Model inputs the same way — a str holding a JSON object — instead of {str: any}:

  • RunSpec.inputs: str — JSON object string of per-invocation typed values; "" = none
  • JobCreate.inputs: str — JSON object string; "" = none

This preserves all documented §1.5 behavior with zero engine changes: the server parses the string into a serde_json::Value and feeds the unchanged apply_inputs/template.rs engine, so {{var}}/{{nested.path}} traversal, natural number/bool rendering, and HERO_INPUT_<UPPER> injection all keep working. The typed SDK builder API (.input(k, v: impl Into<serde_json::Value>) / .inputs(map)) is unchanged — the SDK serializes the map to a JSON string at the wire boundary.

Scope of changes

  • oschema: 2 fields {str: any} -> str (the only two any usages in the tree).
  • Regenerate crates/hero_proc_server/openrpc/openrpc_jobs.json.
  • Server jobs_impl.rs: parse req.inputs/spec.inputs JSON string -> Value (was to_value).
  • SDK factory.rs / builders.rs: serialize the builder's input map -> JSON string when constructing RunSpec/JobCreate.
  • Tests / examples: pass inputs as a JSON string literal.

Acceptance criteria

  • hero_proc_sdk compiles (both openrpc_server! and openrpc_client! codegen pass — no Rule 2 violation).
  • lab builds against the SDK git dependency (CI publish job green).
  • Typed inputs still render {{var}}/{{nested.path}}; numbers/bools render naturally; unresolved placeholders stay literal.
  • HERO_INPUT_<UPPER> still injected for non-ai interpreters.
  • input_schema still persists and round-trips.
  • No-inputs path unchanged (empty string short-circuits).
  • cargo build + cargo clippy -D warnings clean for the feature crates.

Notes

  • Regression from #140; development HEAD 14ee3ea is affected.
  • No behavior change intended versus #140 other than the wire encoding of inputs (map -> JSON string), which is invisible through the SDK builder API.
## Summary After merging #140 (`feat(jobs): restore typed inputs end-to-end`) into `development`, the **lab publish CI fails to build**. `lab` depends on `hero_proc_sdk` as a git dependency, and the SDK's `openrpc_client!` macro now rejects the schema introduced by #140: ``` error: proc macro panicked --> crates/hero_proc_sdk/src/openrpc_client/mod.rs:22:1 = help: message: Schema::to_openrpc: schema violates the method-input contract: - type `JobCreate` uses the `any` type, which is forbidden ... See /openrpc_principles, Rule 2. - type `RunSpec` uses the `any` type, which is forbidden ... See /openrpc_principles, Rule 2. ``` This cascades into ~9 `E0432` unresolved-import errors (`HeroProcClient_`, `JobCreate`, `RunSpec`, `Action`, etc.) because codegen aborts, and the whole `hero_proc_sdk` lib fails to compile — breaking every downstream consumer (`lab`, admin, examples). ## Root cause #140 modeled per-invocation typed inputs as a free-form map using the `any` value type, in two oschema types: - `crates/hero_proc_server/oschema/jobs/40_run.oschema` — `RunSpec.inputs: {str: any}` - `crates/hero_proc_server/oschema/jobs/30_job.oschema` — `JobCreate.inputs: {str: any}` `{str: any}` generates `HashMap<String, serde_json::Value>`. The `openrpc_server!` codegen tolerated this, so #140 built and tested green locally against the server, but the **`openrpc_client!` codegen enforces openrpc principles Rule 2**, which forbids `any` entirely (no constructor, no typed accessors, no compile-time check). The enforcement was tightened (see `stopgap/openrpc-enforcement-b1`), so the client-side build that `lab` performs now hard-fails. ## Fix Re-model `inputs` without `any`, **using the existing in-repo precedent**: `Action.input_schema` is already a `str` holding a JSON document (a JSON Schema). Model `inputs` the same way — a `str` holding a JSON object — instead of `{str: any}`: - `RunSpec.inputs: str` — JSON object string of per-invocation typed values; `""` = none - `JobCreate.inputs: str` — JSON object string; `""` = none This preserves **all** documented §1.5 behavior with zero engine changes: the server parses the string into a `serde_json::Value` and feeds the unchanged `apply_inputs`/`template.rs` engine, so `{{var}}`/`{{nested.path}}` traversal, natural number/bool rendering, and `HERO_INPUT_<UPPER>` injection all keep working. The typed SDK builder API (`.input(k, v: impl Into<serde_json::Value>)` / `.inputs(map)`) is unchanged — the SDK serializes the map to a JSON string at the wire boundary. ## Scope of changes - oschema: 2 fields `{str: any}` -> `str` (the only two `any` usages in the tree). - Regenerate `crates/hero_proc_server/openrpc/openrpc_jobs.json`. - Server `jobs_impl.rs`: parse `req.inputs`/`spec.inputs` JSON string -> `Value` (was `to_value`). - SDK `factory.rs` / `builders.rs`: serialize the builder's input map -> JSON string when constructing `RunSpec`/`JobCreate`. - Tests / examples: pass `inputs` as a JSON string literal. ## Acceptance criteria - [ ] `hero_proc_sdk` compiles (both `openrpc_server!` and `openrpc_client!` codegen pass — no Rule 2 violation). - [ ] `lab` builds against the SDK git dependency (CI publish job green). - [ ] Typed inputs still render `{{var}}`/`{{nested.path}}`; numbers/bools render naturally; unresolved placeholders stay literal. - [ ] `HERO_INPUT_<UPPER>` still injected for non-ai interpreters. - [ ] `input_schema` still persists and round-trips. - [ ] No-inputs path unchanged (empty string short-circuits). - [ ] `cargo build` + `cargo clippy -D warnings` clean for the feature crates. ## Notes - Regression from #140; `development` HEAD `14ee3ea` is affected. - No behavior change intended versus #140 other than the wire encoding of `inputs` (map -> JSON string), which is invisible through the SDK builder API.
Author
Owner

Implementation complete — PR #145

Root cause confirmed: {str: any} on RunSpec.inputs / JobCreate.inputs is rejected by openrpc_client! (Rule 2), which is what lab compiles via the SDK git dependency. The server macro tolerated it, so #140 built locally; the client macro did not, breaking the publish build.

Fix

Modeled inputs as a str holding a JSON object — same precedent as Action.input_schema (a str carrying a JSON document). Zero engine changes.

Changes (8 files)

  • oschema/jobs/40_run.oschema, oschema/jobs/30_job.oschemainputs: {str: any} -> str
  • src/rpc/impls/jobs_impl.rs — parse inputs JSON string -> Value before apply_inputs (job_create + run_quick_submit); ""/invalid => no-op
  • hero_proc_sdk/src/factory.rs, hero_proc_sdk/src/builders.rs — serialize the typed input map -> JSON string at the wire boundary; builder API unchanged
  • openrpc/openrpc_jobs.json — regenerated (both inputs blocks -> type: string)
  • hero_proc_admin/docs/api.md — docs updated to the JSON-string contract
  • tests/basic/jobs_quick.rs — e2e test passes inputs as a JSON object string

Test Results

  • Total: 1
  • Passed: 1
  • Failed: 0

basic::jobs_quick::typed_inputs_render_and_env PASS (isolated daemon): {{var}}/{{nested.path}} render, count=3 stays a number, HERO_INPUT_NAME injected, input_schema round-trips.

Also verified: hero_proc_sdk compiles (the failing client-macro gate), full workspace builds after merging latest development (#144).

## Implementation complete — PR #145 Root cause confirmed: `{str: any}` on `RunSpec.inputs` / `JobCreate.inputs` is rejected by `openrpc_client!` (Rule 2), which is what `lab` compiles via the SDK git dependency. The server macro tolerated it, so #140 built locally; the client macro did not, breaking the publish build. ### Fix Modeled `inputs` as a `str` holding a JSON object — same precedent as `Action.input_schema` (a `str` carrying a JSON document). Zero engine changes. ### Changes (8 files) - `oschema/jobs/40_run.oschema`, `oschema/jobs/30_job.oschema` — `inputs: {str: any}` -> `str` - `src/rpc/impls/jobs_impl.rs` — parse `inputs` JSON string -> `Value` before `apply_inputs` (job_create + run_quick_submit); `""`/invalid => no-op - `hero_proc_sdk/src/factory.rs`, `hero_proc_sdk/src/builders.rs` — serialize the typed input map -> JSON string at the wire boundary; builder API unchanged - `openrpc/openrpc_jobs.json` — regenerated (both `inputs` blocks -> `type: string`) - `hero_proc_admin/docs/api.md` — docs updated to the JSON-string contract - `tests/basic/jobs_quick.rs` — e2e test passes `inputs` as a JSON object string ### Test Results - Total: 1 - Passed: 1 - Failed: 0 `basic::jobs_quick::typed_inputs_render_and_env` PASS (isolated daemon): `{{var}}`/`{{nested.path}}` render, `count=3` stays a number, `HERO_INPUT_NAME` injected, `input_schema` round-trips. Also verified: `hero_proc_sdk` compiles (the failing client-macro gate), full workspace builds after merging latest `development` (#144).
mahmoud self-assigned this 2026-06-08 16:17:27 +00:00
mahmoud added this to the ACTIVE project 2026-06-08 16:17:29 +00:00
mahmoud added this to the now milestone 2026-06-08 16:17:32 +00:00
Author
Owner

Lint job — green

The lab/CI Lint job (cargo clippy --workspace -- -D warnings) was red on development due to pre-existing clippy debt that had been masked: the earlier {str: any} build failure aborted the first crates, so dependent crates were never checked. Fixing the build unblocked them and surfaced the backlog. Cleared it in a follow-up commit on this branch:

  • server: sort_by -> sort_by_key; contains_key+insert -> entry(); doc-comment blank line
  • sdk: box the large RunActionEntry::Inline variant; RangeInclusive::contains for the bound check
  • examples: checked_div for the ratio; trimmed a stale doc block
  • admin: dropped an identity map_err; #[allow(dead_code)] on the three base_path detail-template fields (threaded but not yet emitted by those partials)
  • test: assert! over assert_eq!(_, bool); is_empty() over len() >= 1; dropped useless String -> String conversions

Verified locally: cargo clippy --workspace -- -D warnings exits 0, cargo build --workspace clean, and the typed-inputs e2e test still passes.

## Lint job — green The lab/CI Lint job (`cargo clippy --workspace -- -D warnings`) was red on `development` due to pre-existing clippy debt that had been masked: the earlier `{str: any}` build failure aborted the first crates, so dependent crates were never checked. Fixing the build unblocked them and surfaced the backlog. Cleared it in a follow-up commit on this branch: - server: `sort_by` -> `sort_by_key`; `contains_key`+`insert` -> `entry()`; doc-comment blank line - sdk: box the large `RunActionEntry::Inline` variant; `RangeInclusive::contains` for the bound check - examples: `checked_div` for the ratio; trimmed a stale doc block - admin: dropped an identity `map_err`; `#[allow(dead_code)]` on the three `base_path` detail-template fields (threaded but not yet emitted by those partials) - test: `assert!` over `assert_eq!(_, bool)`; `is_empty()` over `len() >= 1`; dropped useless `String -> String` conversions Verified locally: `cargo clippy --workspace -- -D warnings` exits 0, `cargo build --workspace` clean, and the typed-inputs e2e test still passes.
Sign in to join this conversation.
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_proc#143
No description provided.