feat(specverb): let an action input carry an array, and refuse before the write #319
No reviewers
Labels
No labels
burndown-2026-06
burndown-2026-08
sunday-sprint
autonomy
async-consult
autonomy
epic
autonomy
headless
autonomy
live-collab
coherence-core
priority
P0
priority
P1
priority
P2
priority
P3
priority
P4
qa-fixture
role/advocate
role/director
role/exec
role/frontend
role/gamedev
role/human
role/platform
role/qa
role/science
role/sysadmin
state
ambient
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
coilyco-flight-deck/umbra!319
Loading…
Reference in a new issue
No description provided.
Delete branch "aos/claude/sb46-array-action-inputs"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #317.
The blocker, and which parts of it this clears
#317 named three limits that composed into one:
fail-whenis post-write by construction,requiredis unavailable on acanleaf, and an action shadow could not carry an array. The shadow was the only route to a pre-write refusal, and the shadow could not pass a label array through to the create call.This clears the array limit and the
requiredlimit.requiredon acanleaf's body flag is untouched, and stays available as the cheaper shape for a generated leaf that wants to refuse without a shadow at all.arrayon an action inputThe flag becomes repeatable and its values project as a JSON array. The element type is not declared in the guardfile. It is read from the schema of the leaf field the arg binds, so a shadow encodes exactly what the leaf it shadows would:
items: {type: integer},--labels 199 --labels 333sends[199, 333]as numbers--labels headlessis refused, not sentitems: {}, each token resolves on its own, the union rule from #315Flags only. A positional list cannot be told from the arguments that follow it, so
arraypluspositionalis a parse error.requiredwas parsed and never readbindInputscheckedRequiredfor positionals only, soinput labels { flag; required }bound nothing and refused nothing. That is fixed here, and it is the half of #317 that actually produces the refusal: the check runs while inputs bind, which is before the request is assembled, so the run ends with the hazard absent rather than reported.fail-whencannot do that job, and this does not change whatfail-whenis for.What still carries scalars only
Both fail closed rather than flattening, which is the point:
collectstep pages a request built from string bindings, so binding anarrayinput there is a build-time error naming the limitShared encoding
The
items: {}union rule from #315 moves to opcore asCoerceItems/AnyItem. It was going to exist twice otherwise, once per path, which is how the two would drift.API break
v0.x, no deprecation cycle, noted per AGENTS.md.
stepflow.Runner'sFireandPlaneach take aSliceOf, andRunandPlanCallseach take the list bindings. A Runner over a surface that cannot carry a list passes nil and refuses one that arrives.Verification
make test,make vet,make lint(0 issues),make tidy, andgodoc-current.txtregenerated. Four new tests pin the behaviour: the array reaching the body as typed JSON, the wrong element type being refused before any request fires, the missing required flag refusing before the write, and the collect form failing at build.Refs coilysiren/inbox#426
🤖 Generated with Claude Code