Found while implementing objectstack#6437 (widening DroppedFieldsEvent.reason). Filed unassigned — recording only, not claiming.
Fact
packages/app-shell/src/providers/writeWarningToast.ts picks the toast wording with a binary test:
reason === 'readonly_when'
? t('detail.writeStrippedByState', { … "Not editable in this record's current state…" })
: t('detail.writeStrippedReadonly', { … 'Read-only, so it did not take effect: {{fields}}' })
The else arm is not "readonly" — it is "everything that is not readonly_when". Any reason added server-side lands there and is announced to the user as read-only.
Why this is not caught by types
packages/data-objectstack/src/index.ts re-exports THE spec type rather than a hand-widened string — deliberately, and its comment says why: a consumer-side widening "deletes the only compile-time signal that would tell AdapterProvider's toast wording (which branches on readonly_when) that a new reason had appeared."
That reasoning is right and the mechanism does not deliver it. A ternary is not an exhaustive branch, so tsc has nothing to complain about when the union grows — the type import is correct and still silent. The framework side of the same seam uses the shape that does work: service-automation's DROPPED_REASON_LABEL is a Record<DroppedFieldsEvent['reason'], string>, which fails to compile until the new arm is worded.
Activation
objectstack#6437 adds primary_key (a payload id the update dispatch ruled is not an identifier, stripped rather than written over the targeted rows' primary key). It reaches this toast through the ordinary REST droppedFields response. So the moment .objectui-sha's spec pin moves past that change, a user who triggers the primary-key strip is told the field is read-only — which is false, and points them at a permissions problem that does not exist. That is the same failure objectui#3484 / framework#3794 fixed for readonly_when, one reason later.
Suggested shape
Replace the ternary with an exhaustive map keyed by DroppedFieldsEvent['reason'] (mirroring DROPPED_REASON_LABEL), so a new reason is a compile error here instead of a wrong sentence in front of a user. Wording for primary_key is available in objectstack's crud-nodes.ts and in the spec .describe().
Related: objectstack#6437, objectstack#3407, objectstack#5126, objectstack#3794, objectui#3484, objectui#3160.
Generated by Claude Code
Found while implementing objectstack#6437 (widening
DroppedFieldsEvent.reason). Filed unassigned — recording only, not claiming.Fact
packages/app-shell/src/providers/writeWarningToast.tspicks the toast wording with a binary test:The
elsearm is not "readonly" — it is "everything that is notreadonly_when". Any reason added server-side lands there and is announced to the user as read-only.Why this is not caught by types
packages/data-objectstack/src/index.tsre-exports THE spec type rather than a hand-widenedstring— deliberately, and its comment says why: a consumer-side widening "deletes the only compile-time signal that would tellAdapterProvider's toast wording (which branches onreadonly_when) that a new reason had appeared."That reasoning is right and the mechanism does not deliver it. A ternary is not an exhaustive branch, so
tschas nothing to complain about when the union grows — the type import is correct and still silent. The framework side of the same seam uses the shape that does work:service-automation'sDROPPED_REASON_LABELis aRecord<DroppedFieldsEvent['reason'], string>, which fails to compile until the new arm is worded.Activation
objectstack#6437 adds
primary_key(a payloadidthe update dispatch ruled is not an identifier, stripped rather than written over the targeted rows' primary key). It reaches this toast through the ordinary RESTdroppedFieldsresponse. So the moment.objectui-sha's spec pin moves past that change, a user who triggers the primary-key strip is told the field is read-only — which is false, and points them at a permissions problem that does not exist. That is the same failure objectui#3484 / framework#3794 fixed forreadonly_when, one reason later.Suggested shape
Replace the ternary with an exhaustive map keyed by
DroppedFieldsEvent['reason'](mirroringDROPPED_REASON_LABEL), so a new reason is a compile error here instead of a wrong sentence in front of a user. Wording forprimary_keyis available in objectstack'scrud-nodes.tsand in the spec.describe().Related: objectstack#6437, objectstack#3407, objectstack#5126, objectstack#3794, objectui#3484, objectui#3160.
Generated by Claude Code