Skip to content

Every owner field's celos.user.id`` default fails to evaluate — 127 warnings on one boot, and the default never applies #620

Description

@os-zhuang

Found while runtime-verifying #616 (booting a fresh install to count hook throws); filed unassigned per Prime Directive #10.

What happens

Ten business objects declare their owner field with a CEL default:

// src/objects/account.object.ts:130 — and campaign, case, contact, contract,
// forecast, knowledge_article, lead, opportunity, task
defaultValue: cel`os.user.id`,

On every insert the engine tries to evaluate it and fails:

WARN Failed to evaluate default expression
  {"object":"crm_account","field":"owner",
   "error":{"kind":"type","message":"Unknown variable: os\n\n>    1 | os.user.id\n         ^"}}

127 such warnings on one fresh boot (pnpm dev --fresh on a clean .objectstack/data, 277 seeded rows), covering all ten objects:

crm_account, crm_campaign, crm_case, crm_contact, crm_contract,
crm_forecast, crm_knowledge_article, crm_lead, crm_opportunity, crm_task

The declared default therefore never applies: a record created without an explicit owner gets owner = null, not the current user. It is a WARN, so nothing fails — the field is just quietly empty. Seed data hides it because the seeds set owner explicitly; a record created through a form, an API call or a flow does not.

Why it looks like the spelling is right

os.user.id is the form the platform documents for the current user — @objectstack/spec references it in two places:

  • expression.zod-*.d.ts: "scope is the same as CEL ({{record.x}}, {{os.user.id}})"
  • external-catalog.zod-*.d.ts: "expressions embedded in seed records (e.g. owner_id: cel\os.user.id``)"

So either the field-default evaluation path does not seed the os root into the CEL scope (a platform bug — the docs would then be describing a capability the runtime does not deliver on this path), or the authoring convention for a defaultValue is something else and ten objects are written against a spelling that only works elsewhere. Worth answering before either side is "fixed": the fix belongs at whichever end is actually wrong, not as a hook that papers over it.

Repro

rm -rf .objectstack/data
pnpm dev -- --fresh -p 38617 --seed-admin
# then, in the log:
grep -c 'Failed to evaluate default expression' <log>   # 127

Relationship to #548

Related but distinct. #548 is about owner (the app-authored lookup) vs owner_id (the platform's ownership column) diverging in meaning — reassigning Owner does not move access. This one is narrower and upstream of it: the owner lookup is not even getting populated by the default it declares. Fixing #548 in either direction still leaves this expression broken.

Suggested next step

Decide which end is wrong before writing code:

  • A. Platform bug — the default-expression evaluator does not expose os. Then this is an upstream issue and HotCRM's metadata is correct as written; the fix is a framework issue plus a version bump.
  • B. Wrong authoring conventiondefaultValue on a field expects a different scope/spelling than a seed record does. Then the ten objects get migrated to the correct one, and the platform should reject the unknown-variable expression at publish/validate time rather than warning per row at runtime, so an AI-authored object cannot ship a default that silently never fires.

B's publish-time rejection is worth having either way: a defaultValue that throws on every evaluation is exactly the kind of declared-≠-enforced metadata that only surfaces as "the field is mysteriously empty".


Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingmetadataDeclarative metadata — schema, security posture, UI surfacesprio:p1Next in line once P0s clear

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions