Skip to content

finding: a repeated ?paradigm=/?source=/?category=/?type= on the automation descriptor routes silently empties the designer palette (200, zero rows) #7360

Description

@os-help

Found while fixing #7300 (PR #7350) — same file, different routes, so filed rather than fixed there (that card's declared surface is the GET /:name/runs branch). Observation-class, finding disposition, unassigned.

Mechanism

packages/runtime/src/domains/automation.ts, the two descriptor routes (relocate by content, the lines will drift):

// GET /actions
if (query?.paradigm) {
    actions = actions.filter((a: any) => Array.isArray(a?.paradigms) && a.paradigms.includes(query.paradigm));
}
if (query?.source)   { actions = actions.filter((a: any) => a?.source === query.source); }
if (query?.category) { actions = actions.filter((a: any) => a?.category === query.category); }

// GET /connectors
if (query?.type) { connectors = connectors.filter((c) => c?.type === query.type); }

Every one of these compares a raw query value against a string field. A repeated parameter arrives as an array from every query parser these routes run behind, and an array is never === any string and is never a member of paradigms[], so ?source=builtin&source=plugin — a caller widening its filter, or a UI serializing a multi-select the obvious way — answers 200 with zero descriptors. A structured value (?type[$ne]=x) does the same. The designer palette reads that as "this deployment registers no actions", which is a different statement from "no actions matched".

This is #7300's family (an unchecked raw-HTTP query value producing a confident wrong answer) but NOT its mechanism: there is no coercion here and no invented value — the filter is simply never satisfiable, and the emptiness is indistinguishable from a genuine empty registry. That is why it is filed separately rather than folded into #7300's fix.

Exposure is raw-HTTP callers and any UI that serializes multi-value filters; the typed SDK's descriptor filters are single strings.

Suggested shape, if promoted

The refusal these want already exists and is now shared: parseStringParam(param, raw) in packages/runtime/src/query-param.ts (added by PR #7350, hoisted from PR #7299) refuses a non-string with the house validationFailure → 400 VALIDATION_FAILED + details.fields[] carrying an ADR-0114 invalid_type. Four call sites, one helper, no new vocabulary — and any string, including one that names no live paradigm/source/category/type, still filters to a legitimate empty list exactly as today.

Worth deciding at the same time whether ?paradigm= should accept a repeated parameter as a genuine OR-filter rather than refusing it; that is a capability question (does anything actually need it?) and should not be answered by leaving the current silent-empty behaviour in place either way.

Dedup

Searched open issues for automation descriptor filter query paradigm, domains/automation.ts query and getActionDescriptors filter — no hits. #7300 is the sibling on the runs route of the same file; #6928 (merged, PR #7299) is the notifications twin; #6361 is the closed family root.


Generated by Claude Code

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions