You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit f5a2320
Browse filesBrowse the repository at this point in the historyBrowse files
fix(field): fold deprecated conditionalRequired into requiredWhen and drop the alias (#3754) (#3764)
Second instance of the alias-drift shape #3742 fixed for `action.execute`, found
while verifying that fix.
`requiredWhen` is canonical and `conditionalRequired` is its documented deprecated
alias, but `FieldSchema` had NO canonicalization at all: both keys stayed live in
the parsed output, leaving every consumer to re-implement the precedence — the
condition that produced #3713. The alias surviving parse was even test-pinned,
including a case named "requiredWhen and its alias conditionalRequired can
coexist", the inverse of the contract #3742 had just established one field over.
`FieldSchema` now lowers the alias into `requiredWhen` at parse time and removes it
from the output; canonical wins when both are declared. The pinning tests are
inverted, and a new case asserts the alias is gone from a field parsed through
`ObjectSchema` — the path a renderer actually receives.
This fixes no live bug: every reader we can see already prefers the canonical key
(`rule-validator.ts` reads `requiredWhen ?? conditionalRequired`). Nothing in the
contract MADE that right — this is hardening, removing the chance rather than a
defect.
objectql's fallback is deliberately kept: `evaluateValidationRules` is also handed
raw, unparsed field definitions, which still carry the alias.
Authoring is unchanged. Consumers of the parsed metadata must read `requiredWhen`;
`z.infer<typeof FieldSchema>` no longer carries the alias, so a stale reader fails
to compile instead of silently reading `undefined`. Adds `FieldParseInput`
(`z.input`) for the author-facing shape — `FieldInput` was already taken by an
unrelated `Partial<Field>` factory type in the same file.
Closes#3754.
Copy file name to clipboardExpand all lines: content/docs/data-modeling/field-types.mdx
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -642,7 +642,7 @@ These properties are available on **all** field types:
642
642
| `visibleWhen` | `Expression` | — | Show field only when predicate is true |
643
643
| `readonlyWhen` | `Expression` | — | Make field read-only when predicate is true |
644
644
| `requiredWhen` | `Expression` | — | Require field when predicate is true |
645
-
| `conditionalRequired` | `Expression` | — | Deprecated alias of `requiredWhen` |
645
+
| `conditionalRequired` | `Expression` | — | Deprecated alias of `requiredWhen`— lowered into it at parse time, then dropped from the parsed metadata |
Copy file name to clipboardExpand all lines: content/docs/data-modeling/validation-rules.mdx
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -34,7 +34,7 @@ These properties apply to **all** field types and are validated by the base `Fie
34
34
|`visibleWhen`|`string \| Expression`| — | CEL predicate; field is shown only when `TRUE`|
35
35
|`readonlyWhen`|`string \| Expression`| — | CEL predicate; field is read-only when `TRUE`|
36
36
|`requiredWhen`|`string \| Expression`| — | CEL predicate; field is required when `TRUE`|
37
-
|`conditionalRequired`|`string \| Expression`| — | Deprecated alias of `requiredWhen`|
37
+
|`conditionalRequired`|`string \| Expression`| — | Deprecated alias of `requiredWhen`— lowered into it at parse time, then dropped from the parsed metadata |
Copy file name to clipboardExpand all lines: content/docs/references/data/field.mdx
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -117,8 +117,8 @@ const result = Address.parse(data);
117
117
|**group**|`string`| optional | Field group name for organizing fields in forms and layouts (e.g., "contact_info", "billing", "system") |
118
118
|**visibleWhen**|`string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }`| optional | Predicate (CEL) — field is shown only when TRUE (else hidden). e.g. P`record.type == 'invoice'`|
119
119
|**readonlyWhen**|`string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }`| optional | Predicate (CEL) — field is read-only when TRUE. e.g. P`record.status == 'paid'`|
120
-
|**requiredWhen**|`string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }`| optional | Predicate (CEL) — field is required when TRUE. Canonical name for `conditionalRequired`. |
121
-
|**conditionalRequired**|`string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }`| optional |Predicate (CEL) — field is required when TRUE. Alias of `requiredWhen`. |
120
+
|**requiredWhen**|`string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }`| optional | Predicate (CEL) — field is required when TRUE. Canonical slot; the deprecated `conditionalRequired` alias is lowered into this one at parse time. |
121
+
|**conditionalRequired**|`string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }`| optional |@deprecated — Use requiredWhen instead. Lowered into requiredWhen during parsing and dropped from the parsed output; when both are set, requiredWhen wins. |
122
122
|**widget**|`string`| optional | Form widget override — names a registered field component (resolved as `field:<widget>`) to render this field instead of the `type` default. Degrades to the `type` renderer when unregistered. e.g. "object-ref", "filter-condition", "recipient-picker". |
123
123
|**hidden**|`boolean`| optional | Hidden from default UI |
124
124
|**readonly**|`boolean`| optional | Read-only — never editable in forms, AND server-enforced on BOTH write paths: a non-system write to this field is silently dropped from the payload on UPDATE (#2948/#3003) and on INSERT (#3043; a create can no longer directly seed e.g. `approval_status: "approved"`), symmetric with `readonlyWhen`. A stripped INSERT field still falls back to its `defaultValue`. Exempt from the strip: `isSystem` writes (seed replay, migration), and an opt-in "historical" import (`preserveAudit`, #3493) — which admits a whitelist (the audit/timestamp family plus author-declared business `readonly` fields). A normal (non-system) import is NOT system-context and still strips. |
Copy file name to clipboardExpand all lines: packages/spec/liveness/field.json
+2-1Lines changed: 2 additions & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -83,7 +83,8 @@
83
83
},
84
84
"conditionalRequired": {
85
85
"status": "live",
86
-
"note": "deprecated alias of requiredWhen — plan removal."
86
+
"evidence": "packages/spec/src/data/field.zod.ts:639 — the FieldSchema .transform lowers conditionalRequired -> requiredWhen and DROPS the alias, so authoring it changes what the validator enforces",
87
+
"note": "#3754: folded at parse time and removed from the parsed output (same shape as action.execute -> target in #3742, agent.knowledge.topics -> sources in #1891). Previously BOTH keys stayed live in the output with no canonicalization, so each consumer re-derived the precedence — the condition that produced the #3713 divergence. objectql rule-validator.ts keeps its `requiredWhen ?? conditionalRequired` fallback on purpose: raw (unparsed) metadata still reaches evaluateValidationRules. Removal of the input key is still the endgame."
visibleWhen: ExpressionInputSchema.optional().describe("Predicate (CEL) — field is shown only when TRUE (else hidden). e.g. P`record.type == 'invoice'`"),
526
526
readonlyWhen: ExpressionInputSchema.optional().describe("Predicate (CEL) — field is read-only when TRUE. e.g. P`record.status == 'paid'`"),
527
-
requiredWhen: ExpressionInputSchema.optional().describe("Predicate (CEL) — field is required when TRUE. Canonical name for `conditionalRequired`."),
527
+
requiredWhen: ExpressionInputSchema.optional().describe("Predicate (CEL) — field is required when TRUE. Canonical slot; the deprecated `conditionalRequired` alias is lowered into this one at parse time."),
528
528
529
529
/** Conditional Requirements
530
-
* @deprecated Alias of `requiredWhen` — kept for back-compat. */
531
-
conditionalRequired: ExpressionInputSchema.optional().describe('Predicate (CEL) — field is required when TRUE. Alias of `requiredWhen`.'),
530
+
* @deprecated Alias of `requiredWhen`. Accepted on input, lowered into
531
+
* `requiredWhen` at parse time, then **removed from the parsed output**
532
+
* (#3754) — so every consumer reads one canonical slot. When both are set,
533
+
* `requiredWhen` wins and this value is discarded. */
534
+
conditionalRequired: ExpressionInputSchema.optional().describe('@deprecated — Use requiredWhen instead. Lowered into requiredWhen during parsing and dropped from the parsed output; when both are set, requiredWhen wins.'),
532
535
533
536
/**
534
537
* Form widget override. Names a registered field/UI component to render this
0 commit comments