ExplanationArchitecture Decision RecordsADR-0003: Remove attributes with a flag on ATTRIBUTE-UPDATE

ADR-0003: Remove attributes with a flag on ATTRIBUTE-UPDATE

Context

ATTRIBUTE-UPDATE could only set an attribute: the frontend called setAttribute and core-service stored the value. Boolean attributes (disabled, required, hidden, open, …) are on whenever present, including disabled="", so the Props tab could turn them on but never off. Clearing any optional attribute also left attr="" behind instead of removing it, and undo of “add attribute” could only restore an empty value.

Decision

Add an optional remove?: boolean to AttributeUpdateActionPayload. When true, the frontend calls removeAttribute and deletes the key from the JHTML node; core-service (action.go) does delete(attrs, attr). value is ignored. The Props writer uses it for null writes and for the reverse of adding an attribute that didn’t exist.

Alternatives considered

  • A new ATTRIBUTE-REMOVE action type — rejected: every consumer of action types (apply, commit, history labels, socket relay, backend switch) would need a new case for what is the same operation with a different outcome.
  • Treat value: "" as removal — rejected: empty values are meaningful (alt="" for decorative images, sandbox="" for full restrictions, boolean attributes are stored as "").
  • value: null — rejected: changes the field’s type for every consumer, and Go’s p["value"].(string) assertion would silently yield "".

Consequences

  • Old payloads are unaffected; remove defaults to absent/false.
  • Clients that don’t know the flag would set attr="" instead of removing it. All current consumers (frontend apply, core-service) were updated together.
  • Undo of adding an attribute now removes it instead of leaving it empty.