Skip to content

UI: six refusal surfaces render the kernel's error code as the headline #700

Description

@JArmandoAnaya

Six refusal surfaces put the kernel's error code where the headline goes, so a person who hits one
reads SCHEMA_CHANGE_WOULD_ORPHAN or BATCH_NOT_IN_ANNOTATION as the first and largest thing on
the notice. The shared vocabulary that exists to prevent this is already imported by four of the
six, and used for the body text directly underneath the code.

All citations are at 2e4346d.

The rule this breaks

The ui-capabilities skill lists it fourth among its banned patterns: refusals render through the
shared code-to-prose vocabulary, one map product-wide, and a bare BATCH_NOT_IN_ANNOTATION badge is
not a message. data/refusals.ts is that map. Its own docstring records why it exists — a refusal
used to reach the user in three different shapes depending on the screen, and the bare-code sites
were named as the ones worth eliminating, because a kernel identifier in front of a person is not an
error message.

REFUSAL_PROSE has entries for the codes these six surfaces actually produce, including
SCHEMA_CHANGE_WOULD_ORPHAN, DESTRUCTIVE_SCHEMA_CHANGE, STALE_WRITE, BATCH_NOT_IN_ANNOTATION
and ASSET_NOT_WRITABLE. Nothing needs to be written for the common paths; the map needs to be
reached one line higher up.

The six sites, in two grades

Four render prose in the body and a machine code as the title:

file line body
frontend/ui-core/src/annotator/AnnotationPage.tsx 2714 refusalProse(openingRefusal)
frontend/ui-core/src/annotator/AnnotationPage.tsx 2733 refusalProse(save.error)
frontend/ui-core/src/annotator/AnnotationPage.tsx 2763 refusalProse(actionRefusal)
frontend/ui-core/src/screens/SchemaEditor.tsx 812 refusalProse(publish.error)

These are half-converted: somebody reached the vocabulary for the sentence and left the identifier
above it, which is the shape the map's docstring describes as the thing being removed one screen at
a time.

Two render the raw kernel sentence in the body as well, and are the worse pair:

file line body
frontend/ui-core/src/screens/SchemaEditor.tsx 796 {draftFailure.message}
frontend/ui-core/src/annotator/AddClassDialog.tsx 732 {failure.message}

ErrorBody.message's own field description states that its wording is not part of the contract, and
several kernel messages interpolate a project UUID or name an SDK keyword argument, so these two can
put both a machine code and an internal identifier in front of somebody at once.

The design question this issue has to answer

AlertProps.title is optional (frontend/ui-core/src/primitives/Badge.tsx:53), so there are two
honest shapes and this issue should pick one rather than leave each call site to choose:

  • No title. The prose is the whole notice. Simplest, and correct wherever the sentence stands
    alone.
  • A prose headline. A short human phrase above the sentence, for the surfaces where the notice
    competes with other content and needs a scannable line — AnnotationPage's save refusal already
    does this inside its body, with This frame could not be saved above the prose.

Whichever is chosen, the code stops being the headline. Where a code genuinely helps somebody
filing a bug report, it belongs somewhere a person can copy without it being the first thing they
read.

Acceptance criteria

  • No user-facing surface renders an error code as its title, headline, or badge text. A grep for
    title={.*\.code} across frontend/ returns nothing.
  • The two sites rendering message verbatim render prose from the shared map instead, so no project
    UUID and no SDK keyword spelling can reach a person through them.
  • Every refusal these six surfaces can produce has a REFUSAL_PROSE entry, or falls through to the
    server message deliberately, which the map already documents as its intended behaviour for codes
    whose server wording is good.
  • One test per converted surface forces the refusal and asserts the person sees prose rather than an
    identifier. The ui-capabilities skill already requires a refusal-rendering test for every
    mutation touched, so these largely exist and need their assertions tightened rather than written.

Notes

This was found while auditing the schema class-removal flow (#588), which requires that refusals
still reaching the UI render through the shared vocabulary. It is filed separately because five of
the six sites are outside the schema editor and the pattern spans the annotator, so correcting it
inside that flow would fix one screen and leave the rule with nowhere to live — the same failure the
refusal map was created to end. #588 is not settled by this issue and remains open on its own terms.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingfrontendannotator / ui-core / app packagesuxUser experience / interaction design

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions