fix(v2): close the correctness gaps the release audit found (#6671)

An end-to-end audit of the v0.8.1 release surfaced one regression the
release itself introduced and a set of filter/cursor gaps that let a
caller's spelling change what a query answered.

An `enrichment` workflow group stored no `workflowId`. The public
contract invites a caller to omit it, the write persisted caller input
through an `as WorkflowGroup` cast that hid the omission from the type
checker, and the response schema still required it — so the write
committed and then the outbound parse threw. Because the list presenter
maps every group through that schema, one such row made GET, PATCH and
DELETE on that table's groups fail from then on, with no public way to
remove it. The cast is gone rather than papered over, so the same class
of omission cannot recur silently.

Knowledge tag filters accepted any operator string and then ignored an
unrecognized one in opposite directions: the document list dropped the
predicate and answered with the whole knowledge base, while search fell
through to equality and answered a different question. Both now reject
at the boundary. `.strict()` is applied on the v2 chain only — v1 has
always stripped unrecognized keys, and the `between`-requires-`valueTo`
rule already closes the mis-cased `valueTo` trap on both versions.

Cursor scopes bound set-valued filters to the caller's ordering:
`all`/`any` clause order and `in`/`nin` operand order in the table
predicate, and the raw `resourceType` text on audit logs, whose query
splits it into an `inArray`. Audit logs is canonicalized on both sides,
because canonicalizing the scope alone would have given two genuinely
different result sets one fingerprint.

Also: the exposed-header list reached only the fallback CORS policy, so
all five matched rules — including the wildcard-origin execute route,
the only one that emits `X-Run-Id` — could not hand a browser the run id
or a 429's `Retry-After`; a bulk row update reported an uncoercible
value only when its filter happened to match; the cost and duration
windows accepted an inverted pair and answered it with an empty page;
and the sortless runs list advised callers to fix a `sortBy` it rejects.
This commit is contained in:
Waleed
2026-08-13 12:20:16 -07:00
committed by GitHub
parent 9c7b24369f
commit 8d319a430a
29 changed files with 1095 additions and 48 deletions
+17 -3
View File
@@ -2743,9 +2743,22 @@
},
"operator": {
"default": "eq",
"description": "Comparison operator; valid operators depend on the field type.",
"description": "Comparison operator; valid operators depend on the field type. Text tags accept eq, neq, contains, not_contains, starts_with, ends_with; number and date tags accept eq, neq, gt, gte, lt, lte, between; boolean tags accept eq, neq. An operator the tag's field type does not implement is rejected, never ignored.",
"examples": ["eq"],
"type": "string"
"type": "string",
"enum": [
"eq",
"neq",
"contains",
"not_contains",
"starts_with",
"ends_with",
"gt",
"gte",
"lt",
"lte",
"between"
]
},
"value": {
"anyOf": [
@@ -2763,7 +2776,7 @@
"examples": ["billing"]
},
"valueTo": {
"description": "Upper bound for the `between` operator.",
"description": "Upper bound for the `between` operator, and required whenever that operator is used.",
"anyOf": [
{
"type": "string"
@@ -2775,6 +2788,7 @@
}
},
"required": ["tagName", "value"],
"additionalProperties": false,
"title": "Knowledge search tag filter",
"description": "A structured tag filter applied to knowledge search."
},