{% from '_rule_block.html' import pin_block, pin_new, rule_mark with context %} {% from '_status_cue.html' import status_cue %} {% from '_record_list.html' import record_list %} {# the node, not the section: three leaf tabs open at once should be tellable apart #} {% set page_title = node.name %} {% include '_header.html' %}
{# The same band as the dashboard — a deep breadcrumb and an ellipsed title — minus the parts a detail page has no business showing: it is not polled, so it carries no freshness stamp, and it has nothing to filter. #} {% include '_page_band.html' %} {% include '_said.html' %}

{{ node.name }}

{% if node.description %}

{{ node.description | markdown_inline }}

{% endif %} {#- What the node is lives in the band, folded, as on every view of it; this card says how it is. -#}
{#- What an admin's pin on this node would cover, as its editor's first sentence says it (ADR-0104 decision 3). A node whose lines can be pinned one by one counts them, because that is the choice the admin is making; prose is one condition in several sentences. -#} {%- set pin = node.maintenance_entry %} {%- set node_covers %} {%- if node.members and node.reasons | length > 1 -%} Silences all {{ node.reasons | length }} lines and any that appear; the check keeps running and counts again when the pin clears. {%- elif node.members and node.reasons -%} Silences its one line and any that appear; the check keeps running and counts again when the pin clears. {%- else -%} It will not alarm before the pin expires; the check keeps running and counts again when the pin clears. {%- endif -%} {%- endset %} {#- And a line's: what is left alarming beside it. A line that is silenced already is not one that keeps alarming, and under the node's pin nothing alarms at all — there a line's pin is for the day the node's ends. -#} {%- set others = node.reasons | rejectattr('quiet') | list | length - 1 %} {%- set line_covers %} {%- if pin -%} It stays silenced when the node's pin ends. {%- elif others > 1 -%} The other {{ others }} keep alarming. {%- elif others == 1 -%} The other one keeps alarming. {%- else -%} No other line is left to alarm. {%- endif -%} {%- endset %} {#- The status line: how the node is, and what is about the node rather than about one finding. The `node.actions` slot (ADR-0034, second slot) is the same action as the card head's, on the page where the node is the subject; it sits with the status rather than with the reason lines, because that is precisely the distinction it exists for (ADR-0036). An admin's *Set maintenance* is the one control that sets the node's pin, and it opens its editor here, where the pin's block will stand (ADR-0104 decision 3); a pinned node has *Edit* and *Clear* in that block instead. -#}
{#- "maintenance · error" rather than a bare "maintenance": naming what is being silenced is the transform being made visible (ADR-0032 rule 5), and before the keystone the reading it names was not merely hidden but overwritten. -#} Status: {{ node.code.name.lower() }}{% if node.own_code.name == 'MAINTENANCE' and node.reported_code.name not in ('MAINTENANCE', 'UNDEFINED') %} · {{ node.reported_code.name.lower() }}{% endif %} {{ slot('node.actions', node=node, surface='check') }} {%- if is_admin and not pin %}
{{ rule_mark('maintenance') }}Set maintenance {{ pin_new('node', node_covers, branch, '', 'node-maintenance') }}
{%- endif %}
{#- The node's pin (ADR-0104 decisions 1, 2 and 8): one block at the top of the card, heading a frame that runs round every line the pin covers. The head is a **list** — the rules acting on this node, of which maintenance is the first kind — so a second rule is a second row here and not a second place to look. -#} {%- if pin %}
    {{ pin_block(pin, 'node', branch, '', 'node-maintenance', is_admin, tag='li') }}
{%- endif %} {#- The dashboard's reason line, uncapped: every reason, and the same ``reason.actions`` slot (ADR-0034) beside each one, so a deployment's "raise a ticket" button is in both places the reason itself is. ``surface`` tells a filler which page it is on — this one is not polled. This is also the surface for **per-entry maintenance** (ADR-0036): where a check declared its reasons as members, one finding somebody is working on can go quiet while its nineteen siblings keep alarming. An admin gets a pause mark at each such line's end, on a seat in the rail the line's other actions share; it opens the pin's editor beneath the line, where its block will stand (ADR-0104 decisions 3 and 4). A quiet line stays visible in the maintenance tint and carries that block — who silenced it and until when, its note, *Edit* and *Clear*. Prose gets no mark — its unit of suppression is the node pin, and a per-line control there would offer to silence half of one condition. `?reason=` names one line (ADR-0036 decision 12): the maintenance index links here with it, and a line's set, edit and clear from this page come back with it, because on a node with 150 findings arriving at the right *page* is not the same as arriving at the *line*. The named line — and only it — gets the `pinned-reason` id, so one anchor exists instead of one per reason, and only when it was asked for. -#} {%- if focus_orphan %} {#- The pin is real and its line is absent: a slug the current run did not report is **suspended, not deleted** (decision 6). Say so — this is the one surface that can, and an anchor that simply failed to scroll would have left the operator guessing. -#}
The line this pin was set on is no longer reported by this check. The pin is still here and still expires {{ focus_orphan.expires_at | localtime }}; if the line comes back, it comes back silenced.
{%- endif %} {% for line in node.reasons %} {%- set focused = focus and line.slug == focus %} {%- set settable = is_admin and node.members and not line.quiet %} {#- A coded line carries its own claim (ADR-0042): state class, its state's own tint, and a labeled cue as its accessible name; quiet keeps the maintenance tint over any code, and is-running is display only — the words still say it. -#}
{#- The box the pause mark is placed in: the line's own paddings differ with its state, and the mark has to sit where the rail does. -#}
{{ slot('reason.actions', node=node, reason=line.text, line=line, index=loop.index0, surface='check') }}
{% if line.code %}{{ status_cue(line.code, labeled=true) }}{% endif %}{{ line.text | markdown }}
{#- The record (backlog #32): what the check read about this line's subject, beside the sentence it wrote. A list of dotted names and values — its times in the configured timezone, everything else as stored — on the node's own page only: a card shows the line, and this is the surface a reader comes to when the line is not enough. -#} {%- if line.data %} {{ record_list(line.data) }} {#- Where the seam refused the record (ADR-0093), the line stands as its grading wrote it and this says why, in the record's place — escaped like any other data, since a why may quote the value it refused. -#} {%- elif line.refused %}

Record refused: {{ line.refused }}

{%- endif %} {%- if settable %} {#- After the line's text in the document, so the editor opens beneath it in the flow; the mark itself is drawn on its seat in the rail. A mark and not the two words: on a node with 150 lines the words were a column of their own. -#}
{{ rule_mark('maintenance') }}Set maintenance for this line {{ pin_new('line', line_covers, branch, line.slug, 'reason-maintenance-' ~ loop.index) }}
{%- elif line.quiet %} {{ pin_block(line.maintenance, 'line', branch, line.slug, 'reason-maintenance-' ~ loop.index, is_admin, also=pin is not none) }} {%- endif %}
{% endfor %} {% if node.quiet_reasons %} {#- "nothing here is reporting" explains a node that went blue because its last member was pinned. Under a **node** pin it would take credit for the wrong thing — the block above already says why — so the count then reports only what stays quiet once that pin is cleared. -#}
{{ node.quiet_reasons }} of {{ node.reasons | length }} {% if node.maintenance %}also pinned individually{% else %}in maintenance{% if node.own_code.name == 'MAINTENANCE' %} — nothing here is reporting{% endif %}{% endif %}.
{% endif %} {%- if pin %}
{%- endif %} {%- if node.damping %} {#- The counter, in words rather than as a number nobody can place: what this node shows, how far the count has got, and what its parent is counting meanwhile. The leaf is honest from the first failure (backlog #38 B) — what waits is only the claim that everything above is broken. -#}
Showing {{ node.damping.pending.name.lower() }} — {{ node.damping.seen }} of {{ node.damping.needed }} consecutive readings before this escalates{% if node.contribution and node.contribution.code %}, so the level above still counts {{ node.contribution.code.name.lower() }}{% endif %}.
{%- endif %} {#- Observed, the Time card's word (ADR-0099 decision 3): a node the instance re-asserts on its own tick goes stale too, and nothing checks it. -#} {% if node.stale %}
Stale — last observed {{ node.age_seconds | elapsed }} ago.
{% endif %}
{#- Time, report and configuration — the shared node-facts block (_node_facts.html), which the branch page now renders too, so a container and a leaf say the same things in the same shapes (ADR-0044 decision 8). Directly under the status and the reasons: the node's document is its Copy view's, a tab in the band above, and this page carries none (ADR-0115 decision 7). -#} {% include '_node_facts.html' %}
{% include '_page_end.html' %}