fix(opcore): give an action's scalar arg the type its field declares #329

Merged
coilyco-ops merged 1 commit from aos/328-scalar-type into main 2026-08-27 04:46:35 +00:00
Owner

Closes #328. Sibling of #326, which landed in #327 - this is the half that did not make it into that merge.

The behaviour

BindSlice lowers array elements to the type the leaf's schema declares (#315, #319). Bind did not do the same for a scalar, so a body field declared integer received the raw CLI string:

body:
    milestone: "16"      <- quoted; the field is an int64

boolean had the same shape: closed would receive "true".

Why it matters

This is #316's defect one level up. A quoted numeral is not an integer, and Forgejo's answer to a wrong type is frequently a 200 that changes nothing - the entire subject of coilyco-flight-deck/agentic-os#1047.

It also decides which of a leaf's fields a shadow can correctly carry. #326 lets a shadow carry the optional ones at all; without this, carrying --milestone would silently send the wrong type, which is worse than dropping it. agentic-os#1105 restores --milestone on top of both.

The change

CoerceScalar mirrors CoerceItems: integer, number and boolean parse to their JSON type, anything else stays a string, and a value that does not parse is refused rather than sent.

Body fields only. PathVals and Query.Set keep taking the raw token, because path and query values are strings on the wire.

Verification

go build ./..., the http/... and pkg/... suites, and make lint (0 issues) all clean on this branch. Two tests: an integer field reaching the wire as a JSON number, and --milestone soon refused before the write rather than degrading to a string.

Confirmed end to end against a shadow built from this branch - --milestone 16 renders unquoted in the plan, and --milestone soon exits 1 with arg "milestone": value "soon" is not an integer.

Closes #328. Sibling of #326, which landed in #327 - this is the half that did not make it into that merge. ## The behaviour `BindSlice` lowers array elements to the type the leaf's schema declares (#315, #319). `Bind` did not do the same for a scalar, so a body field declared `integer` received the raw CLI string: ``` body: milestone: "16" <- quoted; the field is an int64 ``` `boolean` had the same shape: `closed` would receive `"true"`. ## Why it matters This is #316's defect one level up. A quoted numeral is not an integer, and Forgejo's answer to a wrong type is frequently a 200 that changes nothing - the entire subject of `coilyco-flight-deck/agentic-os#1047`. It also decides which of a leaf's fields a shadow can *correctly* carry. #326 lets a shadow carry the optional ones at all; without this, carrying `--milestone` would silently send the wrong type, which is worse than dropping it. `agentic-os#1105` restores `--milestone` on top of both. ## The change `CoerceScalar` mirrors `CoerceItems`: `integer`, `number` and `boolean` parse to their JSON type, anything else stays a string, and a value that does not parse is **refused rather than sent**. Body fields only. `PathVals` and `Query.Set` keep taking the raw token, because path and query values are strings on the wire. ## Verification `go build ./...`, the `http/...` and `pkg/...` suites, and `make lint` (0 issues) all clean on this branch. Two tests: an integer field reaching the wire as a JSON number, and `--milestone soon` refused before the write rather than degrading to a string. Confirmed end to end against a shadow built from this branch - `--milestone 16` renders unquoted in the plan, and `--milestone soon` exits 1 with `arg "milestone": value "soon" is not an integer`.
fix(opcore): give an action's scalar arg the type its field declares
All checks were successful
ci / secrets (pull_request) Successful in 11s
ci / lint (pull_request) Successful in 43s
ci / test (pull_request) Successful in 1m0s
f2e647092b
Closes #328. Sibling of #326 on the same branch: together they decide which of
a leaf's fields a shadow can carry, and whether carrying them is correct.

## The behaviour

`BindSlice` lowers array elements to the declared element type (#315, #319).
`Bind` did not do the same for a scalar, so a body field declared integer got
the raw CLI string:

    body:
        milestone: "16"      <- quoted; the field is an int64

`boolean` had the same shape. That is #316's defect one level up, and Forgejo's
answer to a wrong type is often a 200 that changes nothing, which is the whole
subject of agentic-os#1047.

## The change

`CoerceScalar` mirrors `CoerceItems`: integer, number and boolean parse to their
JSON type, anything else stays a string, and a value that does not parse is
refused rather than sent. Body fields only, since path and query values are
strings on the wire.

## Verification

`make test` (34 packages, no failures), `make vet`, `make lint` (0 issues),
`make tidy`, `godoc-current.txt` regenerated, `code-comments` 18 as on `main`.
Two new tests: an integer field reaching the wire as a JSON number, and a
non-integer refused before the write rather than degrading to a string.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Agent-Role: platform
coilyco-ops deleted branch aos/328-scalar-type 2026-08-27 04:46:36 +00:00
Sign in to join this conversation.
No reviewers
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
coilyco-flight-deck/umbra!329
No description provided.