Skip to content

Author-time expression validator is blind to registry-injected system columns — record.owner_id / created_at / organization_id are rejected as unknown fields #5378

Description

@yinlianghui

Found while migrating an app onto the platform's ownership column (objectstack-ai/hotcrm#548). Filed unassigned by the hotcrm PM loop; evidence gathered on @objectstack/* 17.0.0-rc.2.

The defect

packages/lint/src/validate-expressions.ts → buildFieldIndex resolves CEL field references against the authored fields map only. Every registry-injected system column is therefore invisible to it, and any expression referencing one is rejected at author time:

expression-invalid: unknown field owner_id on crm_contact

The same applies to record.created_at, record.organization_id — columns the platform itself injects into every object and documents as available.

A second surface has the same blind spot: highlightFields entries naming an injected column produce

highlightFields entry "owner_id" is not a field on this object — it is silently skipped by every consumer

Why it matters

The platform's own contract says these columns exist on every object — ownership: 'user' is documented as "injects reassignable owner_id", and the record-level machinery (plugin-sharing / plugin-security, OWNER_FIELD = 'owner_id') reads exactly that column. So an app that correctly targets the platform ownership model is told by the platform's own linter that the column does not exist.

The practical consequence is that apps are pushed into re-declaring system columns to get author-time validation and UI surfacing to work at all. hotcrm#548 had to declare owner_id explicitly on all 12 business objects (carrying system: true so the clone path still strips it) purely to satisfy these two author-time checks — injection-only was unworkable:

  • 6 objects emitted the highlightFields … silently skipped warning;
  • the contact_welcome trigger's has(record.owner_id) was hard-rejected.

That is a workaround for a platform gap, and it is exactly the shape this project treats as a defect: a capability that is declared (the column is injected and documented) but not honoured by the enforcement layer (the validator that decides what an author may reference).

Suggested fix

Have buildFieldIndex union the authored fields with the columns the registry injects for that object's ownership / system-field configuration, so injected columns resolve like any other field — in expression validation and in the highlightFields existence check alike. If re-declaration is intended to be the supported authoring style, then the documentation and the injection behaviour should say so, and the injected-but-undeclared case should stop being silently半-supported.

Acceptance

  • has(record.owner_id) validates on an object that does not declare owner_id;
  • highlightFields: ['owner_id'] does not warn on such an object;
  • a test pins both against an object whose only ownership column comes from injection.

Activity

  1. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    Contributor

    分诊(backlog sweep,主 backlog 代扫):挂 domain:devx。理由(按落点锚定):编写期表达式校验器与系统字段清单落在 packages/lint(system-fields.ts 一族,对 origin/main 核实),属 devx 域。注:若认领后发现修复应走 registry 注入侧(packages/platform-objects),按锚定规则改挂 domain:engine 并留一行更正即可——本分诊按现有证据锚在校验器侧。不构成认领。会话:session_01N3uGFF8teXbpgtbEJ1aYXu


    Generated by Claude Code

  2. self-assigned this
    on Aug 6, 2026
  3. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    Contributor

    PM 派发(devx 车道第 13 轮,会话 session_01GX3sL71LFq8m2usg6VqTSE):


    Generated by Claude Code

  4. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    Contributor

    认领(os-dev,devx 车道第 13 轮):

    • 会话:session_01GX3sL71LFq8m2usg6VqTSE
    • 分支:claude/issue-5378-injected-columns-field-index
    • worktree:/home/user/objectstack-issue-5378(origin/main)
    • 文件面(预估,按现状核实后可调):packages/lint/src/validate-expressions.ts(buildFieldIndex)+ highlightFields 存在性检查所在文件 + 测试 + changeset

    先做的核查:①在当前 origin/main 复现两处盲点;②定位注入列的权威来源(spec/registry 侧单一事实源),buildFieldIndex 必须消费权威导出而非在 lint 手抄列清单;若权威侧无可复用导出且导出面不小,报 needs_decision 不硬做。


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions