Per-column annotation
Per-column annotation on a dataset's Structure tab — description, tags, glossary terms, enum values, and business name editors on each column row, plus the write-path and UI caveats.
Every dataset in the catalog exposes a Structure tab next to its Overview tab. The Structure tab lists the dataset's columns and lets an operator annotate each column with a description, tags, business-glossary terms, enum values, and a business name — the column-level counterparts to the entity-level Overview surfaces. Each column row opens a composer that hosts five sub-editors stacked vertically; saving a sub-editor calls a distinct backend endpoint per sub-surface.
This page covers the composer's five sub-editors, the permissions each one is gated by, the audit-trail coverage per sub-surface, and three load-bearing write-path caveats plus two latent UI hazards.
Where to find it
Open any dataset's detail page → Structure tab. The columns table lists every column with its name, type, primary-key flag, source-side description, and the operator-authored annotations (when set). Clicking a column row expands the composer below the row, surfacing five sub-editors in order:
Description — free-text Markdown rendered with the same pipeline as the entity-level description.
Tags — column-scoped tag chips, sharing the deployment-wide tag vocabulary with Manual Object Tagging.
Glossary terms — business-glossary terms linked to the column, separately from terms linked to the parent dataset (see Business Glossary).
Enum values — operator-curated enumeration list documenting the column's allowed values (one row per enum entry with a label and a description).
Business name — a human-readable alternative to the technical column name, surfaced alongside the technical name everywhere the column is rendered (see Business names).
The composer is the only place to author these annotations; the API is the same surface — third-party clients calling the per-sub-editor endpoints listed below write the same fields with the same caveats.
Permissions
Each sub-editor is gated by a distinct permission:
Description
The Description sub-editor + PUT /api/datasetfields/{id}/description.
Term — add
The Add-term affordance on the Terms sub-editor. See the wiring-bug caveat below — this permission is not the one the server enforces today.
Term — delete
The remove-affordance on each term row + DELETE /api/datasetfields/{id}/terms/{term_id}.
Enum values
The Enum-values sub-editor + POST /api/datasetfields/{id}/enum_values.
Business name
The Business-name affordance + PUT /api/datasetfields/{id}/name.
All six are scoped against the dataset that owns the column in the URL, not the column itself — granting any of them on a dataset grants the corresponding edit on every column of that dataset.
Activity trail
Each sub-editor emits its own Activity Feed event on save:
Description
DATASET_FIELD_DESCRIPTION_UPDATED
Before / after description text.
Tags
DATASET_FIELD_TAGS_UPDATED
Before / after tag-name sets.
Term — add or delete
DATASET_FIELD_TERM_ASSIGNMENT_UPDATED
Before / after term-id sets.
Enum values
DATASET_FIELD_VALUES_UPDATED
Before / after enum-value sets.
Business name
DATASET_FIELD_INTERNAL_NAME_UPDATED
Before / after business name.
All five emit the full before/after payload — column-level annotations have a fuller audit shape than several entity-level mutations (see the Activity Feed scope summary and the Audit trail scope for the platform-wide audit-coverage matrix).
Known limitations and operator caveats
The field-level "Add term" affordance silently fails for users holding DATASET_FIELD_ADD_TERM. Two adjacent SecurityConstants entries are crossed in the platform's authorization wiring at SecurityConstants.java:295-299:
Operator-visible effect of the column-term bug. A user with DATASET_FIELD_ADD_TERM on a dataset sees the column-level Add-term button enabled (the UI gates on the documented permission). Clicking it submits the request and the server rejects it as 403 Forbidden. The UI catches the error silently — the term row never appears in the column's Terms list, and there is no visible error toast. The operator sees the affordance, clicks it, sees nothing happen, retries, sees nothing happen again, and gives up. The workaround today: grant DATA_ENTITY_ADD_TERM (the entity-scope permission) instead of, or in addition to, DATASET_FIELD_ADD_TERM.
Operator-visible effect of the alert-status bug. A user with DATASET_FIELD_ADD_TERM can resolve any alert on any entity. The intended permission for alert-status mutation is DATA_ENTITY_ALERT_RESOLVE (see Alerting). Audit a deployment's DATASET_FIELD_ADD_TERM grants if alert-resolution audit matters to compliance.
Mitigation today. Grant DATA_ENTITY_ADD_TERM to operators who need to link terms to columns; treat DATASET_FIELD_ADD_TERM grants as also-granting alert-resolution. The upstream fix is two one-line changes to the platform's security-rule wiring; until it ships, the documented permission list and the runtime gate diverge.
PUT /api/alerts/{alert_id}/status (resolve an alert)
DATA_ENTITY_ALERT_RESOLVE
DATASET_FIELD_ADD_TERM
POST /api/datasetfields/{dataset_field_id}/terms (add a term to a column)
DATASET_FIELD_ADD_TERM
DATA_ENTITY_ADD_TERM
Saving the column's enum-values list silently soft-deletes any pre-existing values not present in the submitted body. The platform's POST /api/datasetfields/{id}/enum_values endpoint declares its operation as createEnumValue in the OpenAPI spec (the name implies create-one), but the underlying service is a bulk replace — items whose id is null are inserted; items whose id is present are updated; every pre-existing row whose id is not in the submitted list is soft-deleted.
The Structure-tab UI handles this correctly: when the operator edits any single row, the composer submits the full current set (preserving all existing rows that the operator did not touch). The hazard is on the API surface — a third-party SDK consumer reading the operation name in the OpenAPI spec, assuming create-one semantics, and submitting only the new row will silently soft-delete every other enum value on the column.
The silent soft-delete applies only when the column's enum values are all operator-curated (INTERNAL). If even one collector-ingested (EXTERNAL) enum value is present on the column, the endpoint instead rejects any submission whose value-name set does not exactly match the values already on the column — the call fails with 400 Bad Request ("User cannot create or delete external enum values") rather than soft-deleting. On such a column you can only edit the descriptions of the existing values; you cannot add or remove values through this endpoint.
Mitigation today. Treat the endpoint as "submit the full target enum set, every time." A GET-then-POST pattern is the safe shape. The upstream fix is a rename to replaceEnumValues (or a true append-only createEnumValue plus a separate replaceEnumValues); the spec's description field already acknowledges the actual behaviour ("Creates/updates/deletes enum values with their description").
Saving the column's tag list as an empty array silently clears every operator-curated tag on the column. The platform's PUT /api/datasetfields/{id}/tags endpoint deletes every INTERNAL-origin tag-to-column row first, then re-inserts the submitted set. Submitting {"tagNameList": []} succeeds with no error and leaves the column with zero operator-curated tags. The full before/after payload is captured in the DATASET_FIELD_TAGS_UPDATED activity event — the deletion is auditable, but a user fast-clicking through the Structure tab does not see it as destructive.
EXTERNAL-origin tags (collector-ingested via the EXTERNAL_STATISTICS channel) survive the operation — only INTERNAL-origin tag rows are cleared. A column whose tags came entirely from a collector retains them; a column with operator-added tags loses them.
Mitigation today. Submit the full target tag set on every save, not an empty list. The Structure-tab UI submits the current tag set correctly; the hazard, again, is on the API surface for third-party SDK consumers.
Fast-switching between columns while a sub-editor is open may submit the previous column's data against the new column's id (unverified hazard). The composer mounts all five sub-editors unconditionally for the active column. When an operator clicks a different column row while a sub-editor's modal is open, the sub-editor stays mounted but the active-column id atom updates; the form state was initialised from the previous column's data. Depending on which sub-editor is open and how the form-state closure was captured at mount time, a save click on the open modal may submit the previous column's values against the new column's id.
This hazard is currently not verified by a runtime probe — the failure shape is inferred from the composer's source structure. Saves on the affected sub-editors are gated by their permissions and the activity feed captures the after-state, so a fast-switching mistake would be auditable. Until a probe confirms or rules out the hazard, always close the open sub-editor before clicking a different column.
The upstream fix is straightforward (a confirm-on-close + form-reset on column-id change); the doc-side caveat persists until the fix lands.
Where to next
Data entity detail page — the parent container for the Structure tab and the Overview tab; covers the per-class panel matrix that decides which tabs appear.
Entity description — the entity-level counterpart to the column-level description sub-editor; shares the Markdown renderer and the load-bearing no-write-time-sanitisation caveat across six surfaces (this page is one of the six).
Custom metadata — sibling per-entity Overview surface for typed key/value annotations; same authoring philosophy, different cardinality.
Manual Object Tagging — the read-side canonical home for tags, including the operator-caveat list for the global vocabulary the column-level Tags sub-editor writes into.
Business Glossary — the canonical home for terms; the column-level Terms sub-editor is one of three places terms can be linked (alongside the entity-level Terms panel and the term-mention
[[Namespace:Term]]auto-link in any Markdown description).Business names — the canonical home for business-name semantics; the column-level Business-name sub-editor shares the same vocabulary.
Permissions — the canonical home for the six
DATASET_FIELD_*permissions and the wiring-bug flag onDATASET_FIELD_ADD_TERM.
Last updated