fix(specverb): let a matches constraint carry alternative globs #325

Merged
coilyco-ops merged 2 commits from aos/1105-matches-variadic into main 2026-08-27 04:19:04 +00:00
Owner

Closes #324. Follow-up to #322, landed before it has any consumer, so this is a shape fix rather than a break.

The hole

#322 shipped matches with exactly one glob and made a second argument a parse error. Two problems.

It does not match its siblings. Every other glob guard in the DSL is variadic: restrict <param> matches <glob...>, and execverb's when/deny-when <sel> matches <glob...>.

It forced a wildcard where the vocabulary is fixed, and the wildcard leaks. Constraints stack with AND, so "one of these four" was not expressible. Measured against the real endpoint that consumes this, a name outside the vocabulary is dropped silently with a 200:

$ aosguard ops forgejo issue-label add coilyco-flight-deck agentic-os 1310 \
      --body-file <<< '{"labels":["priority/NOPE"]}'
EXIT=0
- name: autonomy/headless      <- the issue's EXISTING labels, echoed back
- name: priority/P4
$ # labels afterwards: unchanged

So under priority/* a typo like priority/p2 or priority/P9 passed the guard, the write proceeded, and nothing was applied. The control would report success over exactly the hazard it exists to prevent - the silent-success shape #316 removed for numeric ids and agentic-os#1047 is about, reappearing one layer up.

The change

Globs within one constraint are alternatives. Alternation within a constraint, AND across constraints, so a refusal still names one axis:

matches "priority/P[0-4]" message="..."
matches "autonomy/headless" "autonomy/live-collab" "autonomy/async-consult" "autonomy/epic" message="..."

Verification

make test (34 packages, no failures), make vet, make lint (0 issues), make tidy, godoc-current.txt regenerated. code-comments reports 18 violations on main unchanged; this branch adds none.

The parser test pins alternation round-tripping, and an empty glob anywhere in the list still fails closed. A new test proves priority/P9, priority/p2 and priority/NOPE are each refused, with the transport rigged to fail the test if anything reaches the wire.

The second commit removes a specgen binary staged by mistake in the first; the squash lands only the source change.

Closes #324. Follow-up to #322, landed before it has any consumer, so this is a shape fix rather than a break. ## The hole #322 shipped `matches` with exactly one glob and made a second argument a parse error. Two problems. **It does not match its siblings.** Every other glob guard in the DSL is variadic: `restrict <param> matches <glob...>`, and execverb's `when`/`deny-when <sel> matches <glob...>`. **It forced a wildcard where the vocabulary is fixed, and the wildcard leaks.** Constraints stack with AND, so "one of these four" was not expressible. Measured against the real endpoint that consumes this, a name outside the vocabulary is dropped **silently with a 200**: ``` $ aosguard ops forgejo issue-label add coilyco-flight-deck agentic-os 1310 \ --body-file <<< '{"labels":["priority/NOPE"]}' EXIT=0 - name: autonomy/headless <- the issue's EXISTING labels, echoed back - name: priority/P4 $ # labels afterwards: unchanged ``` So under `priority/*` a typo like `priority/p2` or `priority/P9` passed the guard, the write proceeded, and nothing was applied. The control would report success over exactly the hazard it exists to prevent - the silent-success shape #316 removed for numeric ids and agentic-os#1047 is about, reappearing one layer up. ## The change Globs within one constraint are alternatives. Alternation **within** a constraint, AND **across** constraints, so a refusal still names one axis: ```kdl matches "priority/P[0-4]" message="..." matches "autonomy/headless" "autonomy/live-collab" "autonomy/async-consult" "autonomy/epic" message="..." ``` ## Verification `make test` (34 packages, no failures), `make vet`, `make lint` (0 issues), `make tidy`, `godoc-current.txt` regenerated. `code-comments` reports **18 violations on `main` unchanged**; this branch adds none. The parser test pins alternation round-tripping, and an empty glob anywhere in the list still fails closed. A new test proves `priority/P9`, `priority/p2` and `priority/NOPE` are each refused, with the transport rigged to fail the test if anything reaches the wire. The second commit removes a `specgen` binary staged by mistake in the first; the squash lands only the source change.
Closes #324. Follow-up to #322, before it has a consumer.

## The hole

#322 shipped `matches` with exactly one glob and made a second a parse error.
Constraints stack with AND, so "one of these four" was not expressible and an
author had to reach for `autonomy/*`. Measured against the endpoint that
consumes this, a name outside the vocabulary is dropped silently with a 200:

    $ aosguard ops forgejo issue-label add ... '{"labels":["priority/NOPE"]}'
    EXIT=0, the issue's existing labels echoed back, nothing applied

So `priority/*` let `priority/p2` and `priority/P9` through the guard and the
write proceeded applying nothing. That is the silent-success shape #316 removed
for numeric ids, reappearing one layer up.

## The change

Globs within one constraint are alternatives, variadic like `restrict <param>
matches <glob...>` and execverb's `when <sel> matches <glob...>`. Alternation
within a constraint, AND across them, so a refusal still names one axis.

An enumerated vocabulary now refuses a near-miss before the write:

    matches "priority/P[0-4]"
    matches "autonomy/headless" "autonomy/live-collab" "autonomy/async-consult" "autonomy/epic"

## Verification

`make test` (34 packages, no failures), `make vet`, `make lint` (0 issues), and
`godoc-current.txt` regenerated. `code-comments` reports 18 on `main` unchanged.
The parser test now pins alternation round-tripping, an empty glob anywhere in
the list still fails closed, and a new case proves `priority/P9`, `priority/p2`
and `priority/NOPE` are each refused before anything reaches the wire.

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
chore: drop a build artifact committed by mistake
All checks were successful
ci / secrets (pull_request) Successful in 12s
ci / lint (pull_request) Successful in 35s
ci / test (pull_request) Successful in 55s
2e7c16885c
The specgen binary built locally to verify this change against agentic-os
should never have been staged. Removed before the squash lands.

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/1105-matches-variadic 2026-08-27 04:19:04 +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!325
No description provided.