Skip to content

fix(spec): resolve composed object/array body field types (WBC-43)#31

Merged
ClayMav merged 1 commit into
mainfrom
clay/wbc-43-api-object-body-fields-json
Jun 12, 2026
Merged

fix(spec): resolve composed object/array body field types (WBC-43)#31
ClayMav merged 1 commit into
mainfrom
clay/wbc-43-api-object-body-fields-json

Conversation

@ClayMav

@ClayMav ClayMav commented Jun 12, 2026

Copy link
Copy Markdown
Member

Summary

Fixes WBC-43: wherobots api runs create-job-run --runpython <s3-uri> sent runPython as a JSON string, but POST /runs requires an object ({"uri": "..."}), so every call failed with HTTP 422. The same shape issue affected --runjar and any other object/array body field.

Root cause

The api command tree is generated at runtime from the Wherobots OpenAPI spec. resolveSchemaType in internal/spec/parser.go only inspected a schema's direct type, properties, and items. FastAPI/Pydantic encodes an optional object field as:

"runPython": { "anyOf": [ { "$ref": "#/components/schemas/RunPython" }, { "type": "null" } ] }

The wrapper schema has no direct type or properties, so resolution fell through to "string". The builder then registered a plain scalar flag (--runpython) that serialized the raw value via sjson.Set, producing "runPython":"s3://..." → 422.

(A bare $ref — used for required object fields — already resolved correctly; only the composition keywords were missed.)

Fix

Recurse into the allOf/anyOf/oneOf composition keywords and return the first member that resolves to a concrete (non-null) type, bounded by a recursion depth guard against circular $refs. The recursive worker returns "" (not "string") for an unresolved schema, so a genuine string alternative in a polymorphic union is preferred over a later, stricter type rather than skipped.

Object/array fields are now typed correctly, so the builder generates the existing JSON flag form (--runpython-json) that validates and emits the object body. This fixes runPython, runJar, and every other composed object/array body field uniformly — no field-specific special-casing.

Design note

I took the principled, generic fix (correct type resolution) rather than teaching the generic api command to wrap a bare string into {"uri": <value>} + add --runpython-args. That ergonomic shortcut belongs to — and already exists in — the curated wherobots job-runs create command. The generic api surface stays faithful to the spec, consistent with its existing contract ("Object and array values must be JSON strings") and the pre-existing --metadata-json precedent.

Usage after fix

wherobots api runs create-job-run \
  --name ftw-top5 \
  --runpython-json '{"uri":"s3://.../ftw_top5.py"}' \
  --runtime micro --region aws-us-west-2 --dry-run

Commits

  • fix(spec): resolve composed object/array body field types

Testing

  • internal/spec/parser_test.go:
    • TestParseResolvesComposedObjectBodyFieldsanyOf[$ref,null], bare $ref, and anyOf[array,null] all resolve to object/array (the WBC-43 regression guard).
    • TestParseScalarUnionPrefersFirstConcreteTypestr | int union resolves to string, not the stricter alternative.
  • internal/commands/builder_test.go:
    • TestObjectBodyFieldSerializesAsJSONObject — end-to-end --dry-run confirms an object body field serializes as "runPython":{"uri":"..."}.
  • go test ./..., go vet ./..., gofmt, and pre-commit run --all-files all pass.

🤖 Generated with Claude Code

FastAPI/Pydantic emits optional object fields as
`anyOf: [{$ref -> object}, {type: "null"}]`, leaving the wrapper schema
with no direct type or properties. resolveSchemaType only inspected the
top-level type, properties, and items, so these fields fell through to
"string". The generated `api` command then registered a plain scalar flag
(e.g. --runpython) that serialized the raw value as a JSON string,
producing `"runPython":"s3://..."` and an HTTP 422 from POST /runs, which
requires an object `{"uri": "..."}`.

Recurse into the allOf/anyOf/oneOf composition keywords and return the
first member that resolves to a concrete (non-null) type, with a depth
bound to guard against circular refs. The recursive worker returns ""
(not "string") for an unresolved schema, so a genuine string alternative
in a polymorphic union is preferred over a later, stricter type rather
than skipped. Object/array fields are now correctly typed, so the builder
generates the existing JSON flag form (--runpython-json) that accepts and
emits the object body. This fixes runPython, runJar, and every other
composed object/array body field uniformly.

Fixes WBC-43.
@ClayMav ClayMav marked this pull request as ready for review June 12, 2026 04:02
@ClayMav ClayMav requested a review from a team as a code owner June 12, 2026 04:02
@ClayMav ClayMav requested a review from sfishel18 June 12, 2026 04:03
@ClayMav ClayMav merged commit 00b3c11 into main Jun 12, 2026
3 checks passed
@ClayMav ClayMav deleted the clay/wbc-43-api-object-body-fields-json branch June 12, 2026 18:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants