An action arg binds a scalar body field as a raw string, so an integer field receives "16" #328

Closed
opened 2026-08-27 04:38:50 +00:00 by coilyco-ops · 0 comments
Owner

Sibling of #326, found in the same place: the fields an action shadow can safely carry.

The behaviour

ArgBinder.BindSlice lowers array elements to the type the leaf's schema declares (#315, #319). Bind does not do the same for a scalar:

http/opcore/binder.go
case b.flagNames[name]:
    b.BodyObj[name] = val      // the raw CLI string, whatever the field declares

So an action arg bound to a body field declared type: integer sends a JSON string. Rendered from a real shadow over Forgejo's create issue, whose milestone is {"type":"integer","format":"int64"}:

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

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

Why this matters

It is the #316 defect one level up. A quoted numeral is not an integer, so the upstream either rejects the call or - the worse case, and the one Forgejo picks for labels - accepts it and does nothing. coilyco-flight-deck/agentic-os#1047 is the whole story of what that costs.

It also bounds what a shadow can safely do. #326 lets a shadow carry the leaf's optional fields; this decides which of them are correct to carry. Today only string and []string fields are safe, so agentic-os#1105's issue create shadow can carry --body, --assignees, --due_date and --ref but must leave --milestone and --closed behind, for a reason that has nothing to do with what they mean.

The fix

Mirror CoerceItems for the scalar case: a CoerceScalar(field.Type, val) used by Bind when the target is a body field, refusing a value that does not parse as the declared type rather than sending it as a string.

Scope it to the body case only. Path and query values are strings on the wire, so PathVals and Query.Set must keep taking the raw token.

  • #326 - an omitted optional arg failing the whole call, the other half of "which fields can a shadow carry".
  • #316 / agentic-os#1047 - the array form of exactly this, and what a silently-ignored wrong type costs.
  • #321 - a mapped leaf carrying a declared type.
Sibling of #326, found in the same place: the fields an action shadow can safely carry. ## The behaviour `ArgBinder.BindSlice` lowers array elements to the type the leaf's schema declares (#315, #319). `Bind` does not do the same for a scalar: ```go http/opcore/binder.go case b.flagNames[name]: b.BodyObj[name] = val // the raw CLI string, whatever the field declares ``` So an action arg bound to a body field declared `type: integer` sends a JSON **string**. Rendered from a real shadow over Forgejo's `create issue`, whose `milestone` is `{"type":"integer","format":"int64"}`: ``` body: milestone: "16" <- quoted; the field is an int64 title: t ``` `boolean` has the same shape: `closed` would receive `"true"`. ## Why this matters It is the #316 defect one level up. A quoted numeral is not an integer, so the upstream either rejects the call or - the worse case, and the one Forgejo picks for labels - accepts it and does nothing. `coilyco-flight-deck/agentic-os#1047` is the whole story of what that costs. It also bounds what a shadow can safely do. #326 lets a shadow carry the leaf's optional fields; this decides which of them are *correct* to carry. Today only `string` and `[]string` fields are safe, so `agentic-os#1105`'s `issue create` shadow can carry `--body`, `--assignees`, `--due_date` and `--ref` but must leave `--milestone` and `--closed` behind, for a reason that has nothing to do with what they mean. ## The fix Mirror `CoerceItems` for the scalar case: a `CoerceScalar(field.Type, val)` used by `Bind` when the target is a body field, refusing a value that does not parse as the declared type rather than sending it as a string. Scope it to the **body** case only. Path and query values are strings on the wire, so `PathVals` and `Query.Set` must keep taking the raw token. ## Related * #326 - an omitted optional arg failing the whole call, the other half of "which fields can a shadow carry". * #316 / agentic-os#1047 - the array form of exactly this, and what a silently-ignored wrong type costs. * #321 - a mapped leaf carrying a declared type.
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
coilyco-flight-deck/umbra#328
No description provided.