{% include "_report_base.html" %}
{# The document says what produced it, beside what it is of. "VNtyper 2.0.21" was in a provenance panel and is now at the foot of the document with the rest of the run's provenance - which is the right place for the run's *settings* and the wrong one for the name of the tool, because a reader who has been handed this file, or found it in a folder years later, needs to know what made it before they need to know its schema version. It is a byline, not a second heading: smaller than the sample name, in the muted ink, on its own line. The version is monospaced because it is an exact string somebody will quote in a methods section. #} {# The gene symbol in italics, as HGNC has it. `` and not ``: this is a typographic convention for a technical term, not emphasis, which is exactly the distinction the element exists for. The symbol and the rest of the title are two autoescaped values rather than one string with markup in it. #}

{{ report_gene_symbol }} {{ report_title_description }} — {{ sample_name }}

{# THE MASTHEAD. Scan order is who this is about, what state the pipeline computed, then the sentence the configuration wrote for that state. `data-state` drives emphasis and nothing else. None of the three values it takes - `finding`, `no-finding`, `indeterminate` - is ever rendered as text: they are colour selectors, not a verdict vocabulary, and `tests/unit/test_generate_report.py` asserts that no report prints one. Every word here is either the pipeline's own or the configuration's. #}
{# One `
` per label/value pair, so a pair is one flex item and can never be split across a line break. A value the run did not record is marked as absent rather than drawn like a fact. #}
Sample
{{ sample_name }}
Assay
{{ assay_name }}
Assembly
{{- assembly_declared }}
Region
{{- region_resolved }}
{# The state, in words. The three algorithm chips are built in `screening_summary.state_chips` - a chip is the most compressed thing this report says about a stage, so it is the one most easily misread as a verdict, and choosing its wording is presentation logic that belongs in the pure module (AGENTS.md trap 11). The last two are here because they read booleans the computed state carries: coverage QC has a displayed verdict *and* a screening axis, and they are not the same fact - a run with no coverage step at all passes the axis while its verdict says it was never evaluated. The rule chip appears only when the configuration had no message for the computed state, which is what tells the reader that the sentence below is a fallback rather than the one written for this state. #} {# THE VERDICT AND THE STATE, SIDE BY SIDE. Prose wants a measure and a masthead is 1,132px wide, so a column of sentences capped at 44 and 68 characters left half the panel empty while the chips sat in a row above it using a third of theirs. Neither is a layout: they are two stacks that happened to be written in that order. The sentence keeps its measure - that is what makes it readable - and the chips take the width beside it, where a reader scanning for state finds them on one edge instead of reading across four boxes. Below 60rem the two become one column again and the original order returns. #}
{# The configured message, as the ordered parts it was authored in. Every part is a separate autoescaped element: it used to be one `summary_text` interpolation marked safe, and the only reason it was marked safe was the line-break separators inside it - the whole sentence was exempt from escaping to get a line break. Nothing is reworded here; the split lives in `report_config.json` beside the verbatim message, and `screening_summary.render_segments` pins that the two still agree. #}

{{ screening_segments[0] }}

{% for segment in screening_segments[1:] %}

{{ segment }}

{% endfor %} {% if coverage_not_measured_note %}

{{ coverage_not_measured_note }}

{% endif %} {# WHAT THE RUN NAMED. The second question anybody opens this report with, beside the first answer instead of in column eleven of a nineteen-column table, 1,600px down the page and 700px off its right edge. It is a repetition of rows that stay below it, never a replacement for them: the tables remain the record. Where the rows carry more than one distinct name, `variant_identity` returns every one of them and qualifies none - choosing which name to show at the top would be the report deciding which variant the reader gets, which is the defect this issue exists to remove. #} {% if variant_identity %}

{{ variant_identity.name }} {%- for other in variant_identity.other_names %} {{ other }} {%- endfor %} {% if variant_identity.tier %} {# The letter and the words for it, together and always. The letter is what the tables print and what a consumer matches on; the words are what make it mean anything to somebody meeting it for the first time. The full definition is in the reading key under the tables and in this element's `title`, so the hover is a shortcut to something already printed rather than the only route. #} {# The badge carries both call-specific sentences as its hover text: they explain something already stated, and four lines of prose under a two-character badge is the badge losing an argument with its own footnote. Both are printed by the rule below, because the archived PDF is what gets filed and a reader with no pointer has no hover - a hover here is a shortcut, never the only route. #} Tier {{ variant_identity.tier }} {{ variant_identity.tier_label }} {% endif %}

{% if variant_identity.ambiguity %}
Ambiguity interval
{{ variant_identity.ambiguity }}
{% endif %} {% if variant_identity.repeat_form %}
Repeat form
{{ variant_identity.repeat_form }}
{% endif %} {% if variant_identity.kestrel_name %}
Kestrel name
{{ variant_identity.kestrel_name }}
{% endif %} {% if variant_identity.advntr_name %}
adVNTR name
{{ variant_identity.advntr_name }}
{% endif %} {% if variant_identity.flags %}
Flags
    {% for flag in variant_identity.flags %}
  • {{ flag.token }}
  • {% endfor %}
{% endif %}
{# Which tier-A condition this call actually missed. "At least one tier-A condition is unmet" is a definition, not an answer: the report printed the letter, printed the flags, and left the reader to know that `motif-context-diverges` is one of the five names `reconcile` tests before it promotes a call. `tier_reason` states the connection, and says nothing at all when the blocker left no flag behind rather than guessing at one. #} {% if variant_identity.tier_reason %} {% endif %} {% if variant_identity.note %} {% endif %}
{% endif %}
    {% for chip in state_chips %}
  • {{ chip.label }} {{ chip.value }}
  • {% endfor %}
  • Coverage QC {{ coverage_qc_text }}
  • {% if not screening_state.matched_rule %}
  • Screening rule Not configured
  • {% endif %} {# The two depth figures, beside the state they qualify. "How confident should I be" is the third question this report is opened with, and answering it meant holding a depth score from the table and two coverage figures from a section 250px below it. They are numbers, not states, so they carry no tone - a chip's colour means a computed state here and a depth is not one. The caveat on each is what the coverage section below still exists to state, and the reason the two figures are labelled differently: the window mean is not comparable between assemblies and the flank depth is. #}
  • Mean coverage {{ mean_vntr_coverage }}
  • Flank depth {{ vntr_flank_mean_depth }}
{# The provenance line: the raw state the message above was selected by, spelled out in existing pipeline vocabulary so I2 is visible to a reader, not only to a test. Each algorithm's word comes from `screening_summary.algorithm_state_text`, because the computed result is only a result when the stage produced one: an absent or unreadable stage reduces to the block's `default`, so printing `kestrel_result` here read "Kestrel: negative" for a stage that never called anything. #} {# THE NOTICES, AS ONE BLOCK. Two things this report must say that are neither a result nor evidence: what the artefact may be used for, and that the alignment under every number in it could not be identified. They were a grey box inside the masthead and a red box outside it, in two type sizes, with a gap between them - two boxes, two treatments and about 150px for four lines of text. One block, one treatment, one row each, distinguished by the marker and the accent rather than by having a frame of its own. Both are always readable; neither shouts over the result above them. The warning is *first* and is the one that carries an accent: an alignment the pipeline cannot vouch for changes how every number below it should be read, which is a stronger claim than the standing use restriction. It used to be the last line of the fourth provenance panel, 1,000px down the page, where a reader who did not see the red was told nothing. `role="alert"` puts it in front of a screen-reader user and "Warning:" carries the same meaning in words, so neither depends on the colour. #} {# Each notice's text is one element, so the marker and the whole sentence are two grid items rather than the label and its text landing on separate rows. #}
    {% if header_warning %} {% endif %}
  • {{ research_use_statement }}.
{# THE EVIDENCE, FIRST. Until now the first variant table began at y=1580 of a 3142px document - 50% of the way down, 1.6 viewport-scrolls, behind 536px of provenance panels and a coverage section. On a 390px phone it began at 60%. It is what the reader came for, so it is the first thing under the answer. Coverage, cross-match and the alignment browser follow it: each of them qualifies these rows, and a qualifier is read after the thing it qualifies. #}

Kestrel Identified Variants

{# Four states, and they are four different facts (#242). A table of calls; a run that genotyped and called nothing; a run whose Kestrel step is recorded but produced no readable result (#212); and a summary with no Kestrel step in it at all, which `vntyper report` can legitimately be handed. The unreadable case is checked before the negative and says in words that it is not one. A stage that failed has established nothing about the sample, so a report that renders it as "no variant detected" states a negative the run never reached - which is the defect this whole issue exists to remove, in the one place a reader would never think to doubt it. `escaped_table_html` returns "" for an empty frame, and that empty string is what this branches on - `to_html` on one produces a headerless, bodyless table that renders as a stray empty box under the heading. The middle state was the commonest report in any cohort and it looked like a crashed pipeline: `output_empty_result` writes one row of the literal string "None" so that `kestrel_result.tsv` has a body, and the report tabulated it as `None None None None None None None None NaN Negative` with a count line above it calling it one Kestrel row. The count line is counted in Python from the frame that was rendered, so it cannot disagree with the table beneath it. DataTables' own "Showing 1 to 3 of 3 entries" footer is switched off: it exists only when the CDNs resolve, and it counted the rows left *after* the filter had removed the others. #} {# The switch that emphasises flagged values, beside the tables it emphasises. It used to sit in a strip of controls between the provenance panels and the coverage heading, 450px above the table it acts on and above a second switch with an unrelated scope - a control nobody looking at a table would find, and two controls grouped by nothing but having been written next to each other. It changes emphasis and nothing else. This switch used to drive a DataTables row-visibility predicate that removed every flagged row from the DOM until the reader ticked it: a single-patient report that narrates a flagged pathogenic variant and then renders it invisible is issue #242, and there is no code path in this file that can hide a results row. (The cohort report keeps its filter; hiding flagged rows is defensible for triage across samples.) Both switches were `display: none` inputs with a Bootstrap `