Summary
The header chip strip on a record detail page is built from the object's highlightFields. Those chips are inline-editable and the edits save. There is no way to declare a highlight field read-only, so columns that are exclusively maintained by hooks can be overwritten by anyone who clicks on them.
Why there is no authoring knob
highlightFields: z.ZodOptional<z.ZodArray<z.ZodString>>;
// → highlightFields?: string[] | undefined
A plain array of field names. No per-field options at all — no readonly, no disabled, nothing.
The three things an app can reach for, and why each fails
-
View-level readonly on the form view's section fields. This works, but only for the form/dialog render path. The detail page's header chips are a different surface and do not consult it. That asymmetry is the reported symptom: the create/edit dialog refuses the edit while the detail page accepts it.
-
readonly: true on the object field. stripReadonlyFields drops caller-supplied readonly keys for any non-isSystem context. Our columns are written by cross-object hooks, which inherit the acting user's non-isSystem context, so marking them readonly makes the hook's writes silently vanish and the columns never update again.
-
Field-level editable: false in a permission set. getFieldPermissions has no super-user bypass — modifyAllRecords does not apply at field level, and field-level entries take precedence over the object-level decision. plugin-security's detectForbiddenWrites then throws 403 for non-isSystem writes, which kills the same hook write-back. Turning it off for the admin turns it off for the hook.
So an app that has hook-maintained columns and wants them on the header strip has no legal way to protect them.
Reproduce
- Declare a column written only by an
afterInsert/afterUpdate hook on another object.
- Put it in
highlightFields, and mark it readonly on the form view's section field.
- Open the record detail page. The header chip for that column is editable; changing it and saving persists the value, overwriting what the hook computed. The create/edit dialog for the same record refuses the same edit.
Impact
For us these are derived business figures — a customer grade written back by a rating approval, and a supply-share percentage rolled up from two child objects. A user clicking a chip silently replaces a computed value with a typed one, with no indication that the column is machine-owned.
A readonly flag on highlightFields entries — or having the header chips honour the form view's field-level readonly — would close this.
Environment
@objectstack/* protocol ^17.0.0-rc.1, console from @objectstack/console, driver-sql on SQLite.
Filed from a downstream app (hotcrm-heimao), tracked there as issue #61.
Summary
The header chip strip on a record detail page is built from the object's
highlightFields. Those chips are inline-editable and the edits save. There is no way to declare a highlight field read-only, so columns that are exclusively maintained by hooks can be overwritten by anyone who clicks on them.Why there is no authoring knob
A plain array of field names. No per-field options at all — no
readonly, nodisabled, nothing.The three things an app can reach for, and why each fails
View-level
readonlyon the form view's section fields. This works, but only for the form/dialog render path. The detail page's header chips are a different surface and do not consult it. That asymmetry is the reported symptom: the create/edit dialog refuses the edit while the detail page accepts it.readonly: trueon the object field.stripReadonlyFieldsdrops caller-supplied readonly keys for any non-isSystemcontext. Our columns are written by cross-object hooks, which inherit the acting user's non-isSystemcontext, so marking them readonly makes the hook's writes silently vanish and the columns never update again.Field-level
editable: falsein a permission set.getFieldPermissionshas no super-user bypass —modifyAllRecordsdoes not apply at field level, and field-level entries take precedence over the object-level decision.plugin-security'sdetectForbiddenWritesthen throws 403 for non-isSystemwrites, which kills the same hook write-back. Turning it off for the admin turns it off for the hook.So an app that has hook-maintained columns and wants them on the header strip has no legal way to protect them.
Reproduce
afterInsert/afterUpdatehook on another object.highlightFields, and mark itreadonlyon the form view's section field.Impact
For us these are derived business figures — a customer grade written back by a rating approval, and a supply-share percentage rolled up from two child objects. A user clicking a chip silently replaces a computed value with a typed one, with no indication that the column is machine-owned.
A
readonlyflag onhighlightFieldsentries — or having the header chips honour the form view's field-levelreadonly— would close this.Environment
@objectstack/*protocol^17.0.0-rc.1, console from@objectstack/console, driver-sql on SQLite.Filed from a downstream app (
hotcrm-heimao), tracked there as issue #61.