Found while implementing #6970 (PR #7126), which closed the same hole one layer up on ActionParamSchema.defaultValue. Filed unassigned per Prime Directive #10. Deliberately not fixed in that PR — it is not the same bug, and unlike #6970 it is not decision-free.
Measured
FieldSchema.defaultValue is z.unknown().optional() (packages/spec/src/data/field.zod.ts:513). Probed against FieldSchema.safeParse on origin/main (2026-08-09):
DEFAULT_VALUE_TOKENS = ["NOW()","current_user"]
FIELD number field + literal "abc" -> ACCEPT
FIELD datetime field + wall clock -> ACCEPT
FIELD select field + non-member -> ACCEPT
FIELD number field + current_user TOKEN -> ACCEPT
FIELD datetime field + NOW() TOKEN -> ACCEPT
FIELD date field + CEL envelope -> ACCEPT
The first three are the residual hole: a literal that can never satisfy the field's own valueSchemaFor contract is accepted silently at authoring.
Why the #6970 fix does NOT transfer
An action param's defaultValue is a pure LITERAL — objectui's ActionParamDialog seeds dialog state with it verbatim and serializeParamValues resolves nothing — so valueSchemaFor is the whole contract, and #6970 could run the default through it unchanged.
A FIELD's defaultValue is polymorphic by design, three legal shapes, documented in packages/spec/src/data/default-value-tokens.ts:
- a literal (
'open', 0, false) - a runtime TOKEN —
DEFAULT_VALUE_TOKENS: NOW(), current_user - an Expression envelope —
{ dialect: 'cel', source: 'today()' } (ROADMAP §M9.9b)
Running valueSchemaFor naively over this key would reject shipped, correct metadata. Measured instances:
examples/app-showcase/src/data/objects/field-zoo.object.ts:120 — Field.user({ defaultValue: 'current_user' })cloudpackages/service-ai/src/objects/*.object.ts — many datetime fields with defaultValue: 'NOW()' (ai-conversation, ai-trace, ai-message, ai-eval-run, ai-eval-case)
datetime + 'NOW()' is legitimate and would fail InstantValueSchema.
What has to be decided before anything is enforced
Any fix must first subtract the token/expression vocabulary, and that subtraction is a real design question rather than a mechanical tightening:
- Is the token set gated per type? Is
NOW() legal on a number? Is current_user legal on anything but a user / lookup('sys_user')? Today nothing says. - The DDL split already documented in
default-value-tokens.ts (NOW() becomes a physical column DEFAULT, current_user deliberately does not) suggests the answer is per-token AND per-type, not one flat allow-list. - A CEL envelope's RESULT type is not knowable at parse time at all, so that shape can only ever be accepted structurally.
That is why this is filed rather than folded into #6970: that card's triage explicitly ruled it "decision-free 的契约收紧 …… 不是新设计", and this one is not.
Why it still matters
Same argument the parent card makes about AI-authored metadata: a wrong literal here is accepted at authoring and only surfaces as bad data (a number column seeded with 'abc') or a runtime failure far from the cause. No shipped metadata in objectstack or cloud currently carries such a literal — the hole is real but has no known live instance, which is why it is being recorded rather than escalated.
Related
Found while implementing #6970 (PR #7126), which closed the same hole one layer up on
ActionParamSchema.defaultValue. Filed unassigned per Prime Directive #10. Deliberately not fixed in that PR — it is not the same bug, and unlike #6970 it is not decision-free.Measured
FieldSchema.defaultValueisz.unknown().optional()(packages/spec/src/data/field.zod.ts:513). Probed againstFieldSchema.safeParseonorigin/main(2026-08-09):The first three are the residual hole: a literal that can never satisfy the field's own
valueSchemaForcontract is accepted silently at authoring.Why the #6970 fix does NOT transfer
An action param's
defaultValueis a pure LITERAL — objectui'sActionParamDialogseeds dialog state with it verbatim andserializeParamValuesresolves nothing — sovalueSchemaForis the whole contract, and #6970 could run the default through it unchanged.A FIELD's
defaultValueis polymorphic by design, three legal shapes, documented inpackages/spec/src/data/default-value-tokens.ts:'open',0,false)DEFAULT_VALUE_TOKENS:NOW(),current_user{ dialect: 'cel', source: 'today()' }(ROADMAP §M9.9b)Running
valueSchemaFornaively over this key would reject shipped, correct metadata. Measured instances:examples/app-showcase/src/data/objects/field-zoo.object.ts:120—Field.user({ defaultValue: 'current_user' })cloudpackages/service-ai/src/objects/*.object.ts— manydatetimefields withdefaultValue: 'NOW()'(ai-conversation, ai-trace, ai-message, ai-eval-run, ai-eval-case)datetime+'NOW()'is legitimate and would failInstantValueSchema.What has to be decided before anything is enforced
Any fix must first subtract the token/expression vocabulary, and that subtraction is a real design question rather than a mechanical tightening:
NOW()legal on anumber? Iscurrent_userlegal on anything but auser/lookup('sys_user')? Today nothing says.default-value-tokens.ts(NOW()becomes a physical column DEFAULT,current_userdeliberately does not) suggests the answer is per-token AND per-type, not one flat allow-list.That is why this is filed rather than folded into #6970: that card's triage explicitly ruled it "decision-free 的契约收紧 …… 不是新设计", and this one is not.
Why it still matters
Same argument the parent card makes about AI-authored metadata: a wrong literal here is accepted at authoring and only surfaces as bad data (a
numbercolumn seeded with'abc') or a runtime failure far from the cause. No shipped metadata inobjectstackorcloudcurrently carries such a literal — the hole is real but has no known live instance, which is why it is being recorded rather than escalated.Related
defaultValueis typedz.unknown(), so a default that can never satisfy the param's own value contract is accepted at authoring time and only 400s at submit #6970 / PR fix(spec): validate action param defaultValue against the param's own value contract (#6970) #7126 — the action-param half, fixedvalueSchemaFor), ADR-0049 enforce-or-remove, ADR-0078