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.
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→buildFieldIndexresolves CEL field references against the authoredfieldsmap only. Every registry-injected system column is therefore invisible to it, and any expression referencing one is rejected at author time: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:
highlightFieldsentries naming an injected column produceWhy 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_idexplicitly on all 12 business objects (carryingsystem: trueso the clone path still strips it) purely to satisfy these two author-time checks — injection-only was unworkable:highlightFields … silently skippedwarning;contact_welcometrigger'shas(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
buildFieldIndexunion the authored fields with the columns the registry injects for that object'sownership/ system-field configuration, so injected columns resolve like any other field — in expression validation and in thehighlightFieldsexistence 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 declareowner_id;highlightFields: ['owner_id']does not warn on such an object;