StatifierBlocks.Editor.Findings (StatifierBlocks v0.28.0)

Copy Markdown View Source

The document-level findings list (ADR-0005 decision 11), rendered as the drawer's Findings tab.

Decision 11 makes the anchor the whole routing mechanism, and the three inline positions - beneath a field, on a slot header, on a block's chrome

  • are rendered by the components that own those positions. What is left for this one is the list view: every finding in the document, in one place, where selecting one selects and reveals its anchor.

Why it is a drawer tab

It shipped as a text block under the canvas, and operator ruling R4 (2026-08-29) retired that position: a list of findings is a grid of rows about the whole document, which is exactly 1A's admission test for the drawer, and the canvas is for the document rather than for a report about it. The inspector's Findings tab is unaffected and stays the selected block's findings (3A), as do the per-card counts - three positions for three scopes, which is the distinction that stopped being legible while a fourth list sat under the canvas saying "Findings" with no scope on it.

A row is severity, subject, anchor tail, source and message, in that order, and row/1 is where that anatomy is defined - for this list and for both of the inspector's findings panels, which called to render a finding and got the bare message until campaign-019 ruling D4. The severity is what an author scans down, the subject is the block they will click, the tail and the chip say which part of that block and who is complaining, and the message is the sentence they read once they have found the row they want. The subject carries both the block's label and its id - the label is what they recognise, the id is what they will paste into a bug report - and it is this list's column alone, because in the inspector the block is the group heading.

Above the list, severity_pills/1 renders Shell.severity_counts/1 as a row of pills. The grouping is unchanged and still by block (D2): the pills say how much, the list says where.

Two cases are easy to render wrong and are handled explicitly.

A finding whose anchor names a block id the document does not contain is an orphan. StatifierBlocks.ViewModel collects those separately rather than dropping them, and they are listed here without a reveal control, because there is nothing to reveal. A caller that supplies a finding against a block that has since been deleted sees it, which is the only honest thing to do with it.

Severity is three-valued since the 2026-08-29 amendments, and every source except :lint produces :error. :lint covers both of the others. It renders as a warning for something still compilable and correct once the host acts - the record's example, an invoke type with no registered handler. It renders as :info for something worth the author's attention with nothing wrong at all: the first producer is StatifierBlocks.Datamodel's undeclared-path advisory (amendment 11e-11g), which arrives here rather than through a channel of its own, because 11g says there is no second channel to build.

Nothing in this panel branches on which of the two it is. The severity reaches the markup as a class and a data-severity, exactly as it did when there were two values, which is the property that let a third one be added without touching the list view.

Summary

Functions

The whole anchor, flattened into one string, for the data-anchor a row is stamped with on every surface.

Every finding in the document, each one reachable from its entry.

One finding, in the anatomy every surface shows a finding in (D4).

How many findings there are at each severity, as a row of pills above the list.

Functions

anchor_tag(arg)

@spec anchor_tag(StatifierBlocks.Finding.t()) :: String.t()

The whole anchor, flattened into one string, for the data-anchor a row is stamped with on every surface.

It is the anchor and not a summary of it - block id included - because it is what a test, a host's stylesheet or a debugging author uses to name one row exactly. The row/1 tail above is the reader-facing half of the same tuple; this is the machine-facing whole of it.

findings(assigns)

Every finding in the document, each one reachable from its entry.

Attributes

row(assigns)

One finding, in the anatomy every surface shows a finding in (D4).

Severity, subject, anchor tail, source, message. There were three renderings of a finding before this one - this list's row, the inspector's selected-block panel and its document panel - and they disagreed: two of them were the bare message, so the same finding told an author three different amounts depending on which pane they happened to be reading. This is the single renderer all three call, which is what makes "what a finding looks like" a thing with one answer.

  • Severity is a word as well as the row's colour, for the reason Shell.cell_word/1 records about truth-table cells: a reader who cannot tell two hues apart gets the same list as everyone else. It is the enum's own word - error, warning, info (ruling D3) - and not a synonym, so the word on screen is the value a host would match on. The colour stays on the row's own element (.sb-finding and its severity modifier), so a host restyling one severity restyles it in one place.
  • Subject is the block's label and its id, and it is the drawer's column alone. In the inspector the block is the group's heading, and a row repeating it under every heading would spend the widest column saying what the line above already said. An orphan's subject is its id and nothing else: Shell.label_for/2 falls back to the id for a block the tree does not hold, and a row reading "blk_gone blk_gone" says the same thing twice.
  • Anchor tail is the part of the anchor the subject does not already carry - config.duration for a {:config, id, key}, slot:body for a {:slot, id, name}, and nothing at all for a {:block, id}, whose anchor is the subject. It is what tells an author whether a finding is about a field, a slot or the block itself, which is decision 11's whole routing rule made visible.
  • Source is the enum value, as a chip: :config, :assignability, :resolution, :lint, :compile. It answers "who says so", which is the question an author asks about a finding they disagree with.
  • Message is the sentence, and the only cell allowed to wrap.

Attributes

  • finding (StatifierBlocks.Finding) (required)
  • root (:any) - the view model's root ViewModel.Node, read only for the subject's label. Defaults to nil.
  • subject (:boolean) - render the block's label and id, which only the drawer's list carries. Defaults to false.

severity_pills(assigns)

How many findings there are at each severity, as a row of pills above the list.

It renders on both document-level surfaces - this tab and the inspector's unselected Findings tab - because both are answering "what is wrong with this document", and a reader who has to count rows to learn there is one error among eleven advisories is reading the list to get a number that could have been given to them.

It is not a filter. Nothing here is clickable, the list beneath is unchanged, and the grouping stays by block (Shell.findings_groups/3): the pills say how much, the groups say where. The spike grouped by severity instead, and campaign-019 ruling D2 did not adopt that - a severity is a property of a finding, and the thing an author acts on is the block.

A severity with nothing at it has no pill; Shell.severity_counts/1 documents why, and holds the invariant that the pills sum to the count on the tab beside them.

Attributes

  • counts (:list) (required) - Shell.severity_counts/1's answer for the list the pills sit above.