An action input can demand a value but cannot constrain its shape, so a shadow cannot refuse a malformed set #322

Closed
opened 2026-08-27 03:52:40 +00:00 by coilyco-ops · 0 comments
Owner

Filed by Angie (platform, claude seat) as the follow-on blocker for coilyco-flight-deck/agentic-os#1105.

#317 named three limits. #319 cleared two of them: an action input can now carry an array, and required is enforced while inputs bind, which is genuinely before the write. That is presence.

agentic-os#1105 needs composition, and its acceptance is explicit about it:

The refusal names which of the two is missing. A bare rejection moves the problem rather than fixing it.

required cannot express "at least one priority/* and at least one autonomy/*", and it cannot name which axis is absent. Today an input's parsed fields are exactly positional | flag | required | help | default | array (guardfile.go applyInputField), none of which constrains a value.

Why the workarounds do not reach it

  • fail-when is post-write by construction, which #317 already established and agentic-os#1105 rejects by name.
  • An id allowlist in the guardfile is what agentic-os#1105 rules out: the label names are consistent across the estate and the ids are not, so an allowlist is one table per org that goes stale whenever a label is recreated.
  • Resolving names to ids in a lookup step is blocked separately. stepflow.SliceRefs documents it: "A step-output reference ($step.field) is never a list: only a declared array input is." So a projection over a list org-label response cannot become the create call's array argument.

The durable check is therefore on the name, in the tokens the caller already passed, which needs no lookup and no table.

Wanted

A value constraint on an action input, enforced where required is enforced:

input labels {
    flag
    required
    array
    matches "priority/*" message="no priority label: pass --labels priority/P2 (P0-P4)"
    matches "autonomy/*" message="no autonomy label: pass --labels autonomy/headless"
}

On an array input each constraint demands at least one matching element, so the constraints stay independent and a refusal names the axis that is missing rather than the whole set. On a scalar the bound value must match.

The spelling already exists elsewhere in the DSL, which is the argument for reusing it rather than inventing one: restrict <param> matches <glob...> at wrap scope, when first input matches ... on a fetch, and when / deny-when <sel> matches <glob...> in execverb. http/opcore already holds the matcher.

Note

A branch implementing this is ready and opens as a PR against this issue.

Filed by Angie (platform, `claude` seat) as the follow-on blocker for `coilyco-flight-deck/agentic-os#1105`. #317 named three limits. #319 cleared two of them: an action input can now carry an `array`, and `required` is enforced while inputs bind, which is genuinely before the write. That is presence. **agentic-os#1105 needs composition**, and its acceptance is explicit about it: > The refusal names **which** of the two is missing. A bare rejection moves the problem rather than fixing it. `required` cannot express "at least one `priority/*` and at least one `autonomy/*`", and it cannot name which axis is absent. Today an input's parsed fields are exactly `positional | flag | required | help | default | array` (`guardfile.go` `applyInputField`), none of which constrains a value. ## Why the workarounds do not reach it * **`fail-when`** is post-write by construction, which #317 already established and agentic-os#1105 rejects by name. * **An id allowlist in the guardfile** is what agentic-os#1105 rules out: the label names are consistent across the estate and the ids are not, so an allowlist is one table per org that goes stale whenever a label is recreated. * **Resolving names to ids in a lookup step** is blocked separately. `stepflow.SliceRefs` documents it: *"A step-output reference (`$step.field`) is never a list: only a declared `array` input is."* So a projection over a `list org-label` response cannot become the create call's array argument. The durable check is therefore on the **name**, in the tokens the caller already passed, which needs no lookup and no table. ## Wanted A value constraint on an action input, enforced where `required` is enforced: ```kdl input labels { flag required array matches "priority/*" message="no priority label: pass --labels priority/P2 (P0-P4)" matches "autonomy/*" message="no autonomy label: pass --labels autonomy/headless" } ``` On an `array` input each constraint demands **at least one** matching element, so the constraints stay independent and a refusal names the axis that is missing rather than the whole set. On a scalar the bound value must match. The spelling already exists elsewhere in the DSL, which is the argument for reusing it rather than inventing one: `restrict <param> matches <glob...>` at wrap scope, `when first input matches ...` on a `fetch`, and `when / deny-when <sel> matches <glob...>` in execverb. `http/opcore` already holds the matcher. ## Note A branch implementing this is ready and opens as a PR against this issue.
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#322
No description provided.