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 convention — defaultValue 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

No activity

Activity on this issue will appear here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingmetadataDeclarative metadata — schema, security posture, UI surfacespriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions