design
Maintenance Supervisor — design notes
Section titled “Maintenance Supervisor — design notes”The Maintenance Supervisor’s analysis desk for Tricycle Hardware. Web flow, 1440, canonical Atlas shell.
Built to the Argo Maintenance Supervisor Web Console PRD
(source Argo-Maintenance Supervisor-Web-Console-PRD.docx in ../../docs/attachments/). The PRD is
the requirement; where this doc and the PRD disagree, the PRD wins.
Build status. Complete. All sixteen views have their own renderer, both §10 workflows run end to end, and every §7 module the PRD specifies is built. Each remaining page arrives in its own batch and draws the canonical empty state until then, so navigation is honest from the first build rather than opening onto blank areas.
Why this was rebuilt rather than patched
Section titled “Why this was rebuilt rather than patched”A real build existed and predated the canon it now sits under. It did not link the
shared design system, switched pages with nav(pg) over .screen/.id-page
instead of the canonical .pg-view + showView, carried eight
token-value-drift warnings (its --r-f was 6px against canon’s 999px, --r-md
10px against 8px, and five colours off), and had real probe failures recorded in
the baseline rather than fixed. Both baseline entries are deleted in the same
commit as the rebuild: the rebuild is what earns zero tolerance, not the record of
having been forgiven.
Its domain fixture was worth keeping — a 45-entry controlled fault taxonomy with
real codes (MTR-BEARING, DRV-BREATHER-BLOCK, CTRL-THROTTLE-OPEN), real
records, cohorts, technician quality figures and proposed modes. That content is
carried forward; the structure around it is not.
Persona
Section titled “Persona”Okonjo Ige · Maintenance Supervisor. The same person as the Maintenance Supervisor at the bay, on a different surface. The mobile app validates one repair at a time in real time; this is the desk where records are compared across vehicles, telemetry and photos become one narrative, a cohort is spotted before it is a crisis, and the unclassified backlog gets cleared before it pollutes every reliability number downstream.
Scope is fleet-wide by default, deliberately unlike the mobile gate, which is bay-scoped because the Maintenance Supervisor is physically at that bay (§5.2).
No delegate tier
Section titled “No delegate tier”Unlike the VP, PM and Head of Maintenance consoles, §5.1 defines no delegate for this one. The Maintenance Supervisor is a single-altitude, individually accountable analytical role. An Maintenance Supervisor covering another’s queue does it under their own identity, visible in the audit trail, not through a delegation mechanism. Switching role in the account menu therefore changes which surface you are looking at, never who may act.
What this role cannot do
Section titled “What this role cannot do”Three boundaries, and §5.4 is unusually specific that they are architectural, not permission flags:
- Never mint, promote or merge a taxonomy code. Not from here and not from the mobile gate. Those verbs fail not with a 403 but by being genuinely absent from the UI and the API. This console’s taxonomy actions are Assign an existing code, Propose a new mode, and Recommend a merge.
- No Return-to-Service control of any kind. RTS is mobile-exclusive; there is no route here to reject.
- No dispatch, bay assignment or replenishment — those are the Head of Maintenance Console’s.
Navigation
Section titled “Navigation”Sidebar, three groups over the PRD §6 sitemap: Overview (Analysis home) · Investigation (Records, Cohorts, Unclassified) · Engineering (Evidence packets, Escalations) · Fidelity (Data quality). Counts sit on Unclassified and Evidence packets, because those two are the work that waits on this desk. Notifications and Settings are reached from the topbar and the account menu.
16 views. Rich records get a full detail page with .pg-back — never a dialog,
and never a side drawer. Dialogs are for confirmations, compact records and
reason-required actions.
| View | What it does |
|---|---|
v-home |
The fidelity band, and the Cohort Flow from captures through causes into cohorts |
v-records |
The three-pane workbench: rail, record detail, action inspector |
v-cohorts |
Every cohort, with how many of its records are still unclassified |
v-cohort |
Time to first failure, the cohort-scoped flow, its member vehicles |
v-vehicle |
The reliability timeline for one vehicle |
v-unclassified |
The daily backlog and the five things you can do about a record |
v-packets |
Every packet across draft, sent, acknowledged, returned and closed |
v-packet-new |
The builder, and its itemised completeness gate |
v-packet |
A packet as submitted, with its version history and diffs |
v-quality |
Six metrics, each drilling through to the records behind it |
v-quality-who |
The same metrics for one technician or one bay |
v-escalations |
Where Argo hands off to TH Engineering |
v-escalation |
One packet’s status timeline and comms history |
v-wo |
A work order, read-only, as the Head of Maintenance console shows it |
v-notifications |
The five Maintenance Supervisor event types |
v-settings |
Saved views, notification preferences, default record scope |
Every page renders from JS into a #*-body container. nav() switches top-level
pages and repaints its destination; showView() handles drill-ins.
Structure decisions already made
Section titled “Structure decisions already made”- The design system is linked, not inlined.
/tokens/tokens.cssand/components/rechaj-ds.csscome in by<link>, exactly as the starter ships them, so only genuinely flow-local rules sit in the<style>block and the whole class of half-transplant defect cannot exist here. That is also what cleared the eight token warnings. View through the dev server, notfile://. - A back STACK, not a single remembered page. Cohort → vehicle → work order is three levels deep, and one slot leaves Back pointing at whichever sidebar page you last visited.
- The sidebar stays lit on a drill-in. A detail page has no entry of its own, so the trail is walked back to the nearest top-level page. Tracked separately from the breadcrumb, which must still read Parent / Current on that same page.
nav()repaints the page it opens. Filter and pane state live outside the renderers so they survive; what does not survive is a stale table showing records that changed while you were elsewhere.- Page bodies stack their cards with the page showing between them, by one rule on the container.
- A KPI row where no tile carries media collapses the slot rather than reserving 36px of nothing between each number and its label.
The domain model, and why nothing is stored twice
Section titled “The domain model, and why nothing is stored twice”RECORDS is the population: 149 field records over a 60-day window, each with a
Tier-1 observed capture code, a Tier-2 confirmed code or none, a cohort, two
separate confidence fields, both validation-gate outcomes, its parts and its
photos. Everything else is computed from it at read time — the Sankey’s band
widths, the sidebar badges, the KPI band, cohort membership and recurrence, every
rate on the Data-Quality console. §4.4 requires the figures here to reconcile
exactly with the PM, Head of Maintenance and VP consoles at the same timestamp, and two stored
copies of one number always eventually disagree.
The population is specified as (Tier-1 → Tier-2 → cohort) clusters with a count, then expanded into individual records. That is what makes §7.1a’s acceptance criterion — every flow band’s count reconciles with the workbench’s own filtered count for the same combination — true by construction rather than by keeping two tables in step. Record ages are spread evenly inside each cluster, so a 30-day window preserves the population’s Tier-1 to Tier-2 ratios; with random ages the window resampled the mix and the concentration figure became noise.
The states a metric needs something real to drill into are placed, not sampled: three captures with no photo evidence (§18’s silently-failed upload), three cases later closed not-a-fault, two Gate #2 send-backs. A probability that happens to roll zero cases leaves a metric whose drill-through opens an empty list, which is worse than not having the metric.
7.1a The Cohort Flow
Section titled “7.1a The Cohort Flow”The signature module. A Sankey over Tier-1 observed capture → Tier-2 confirmed cause → Cohort, band width being record volume. Analysis Home shows the condensed two-tier read — captures into confirmed systems — and Full flow expands it to individual codes and the cohorts they feed. Hovering a band gives the count and both percentages; clicking a node or a band opens exactly those records in the workbench; clicking a cohort node opens Cohort Detail.
Decisions inside it worth keeping:
- One encoding per axis. Node colour carries diagnostic clarity and nothing else, as one hue at three strengths: how much of a Tier-1 capture landed on its single biggest confirmed cause. Confirmed causes and cohorts are neutral ink, because their height is their whole message. A second colour scale on the same diagram would have to borrow a hue from the first, and then the reader has to remember which column a colour belongs to.
- The scale has to be able to say different things. The first fixture put every
Tier-1 node in the middle band, which is a ramp that measures nothing. The record
population was reshaped so four captures read cleanly, three have a leading cause
and three genuinely scatter.
OBS-CREEPis 90% one cause;OBS-NO-DRIVEspreads across six with 24% on the largest, which is where diagnosis at capture is guessing and where the two-tier model earns its keep. - Under twenty records it refuses to draw. §7.1a is explicit that a Sankey over too few records is misleading, not merely thin. The 7-day window really does fall below the floor, so the state is reachable rather than theoretical.
- A text-equivalent summary, required for the chart to be accessible and the fastest read on the page: it names the cleanest capture and the most scattered one, with their figures.
- Labels are painted last with a halo in the surface colour, because a middle-column label has bands running under it and nowhere else to go. A column decides whether its labels are one line or two: twenty-four middle nodes cannot each carry two, and a sub-line landing on the next node’s label is worse than no sub-line.
Analysis Home
Section titled “Analysis Home”The four §7.1 KPI tiles each partition a whole their own bar shows — the unclassified queue split by SLA age, records classified against captured, the five quality metrics by tone, packets on this desk against packets with the PM. Uniform shape across the four, so the row reads as a set and no tile reserves 36px for nothing.
The data-quality tile takes its colour from its worst component, not from its own value. A mean of five rates reads 95.8% green while one of them is red, which is the number telling you not to look at the thing it measures.
Records needing attention ranks on one urgency score rather than listing whatever is unclassified: past the SLA outranks everything, then a confirmed record with no photo evidence, then a Gate #2 send-back, then low cause confidence, then an unclassified record still inside its SLA. Ranking by classification status alone made the module a duplicate of the Unclassified page. The footer says how many are below the line, because a silent top-ten reads as “that is all of them”.
Cohorts are a rule, not a list
Section titled “Cohorts are a rule, not a list”A cohort is defined as a Tier-2 code plus, where the pattern is tied to a build,
a vehicle attribute — CTRL-THROTTLE-OPEN on a kit-rev C vehicle, or
DRV-BREATHER-BLOCK on an RK-09 build. That is what lets membership be derived:
assign a code from this desk and the record joins its cohort in the same breath, so
FVEW-FR-001’s acceptance criterion (the code visible everywhere the record appears,
within one refresh cycle) needs no second write. It also means no cohort can claim
a member its own record does not support — verified at 0 contradictions across 149
records.
Where a vehicle’s attribute is already claimed by a conflicting cohort, the record simply does not join the second one. That is the honest outcome, and it shows up as a record with no cohort rather than as a count nothing supports.
7.4 The Unclassified Queue and the five actions
Section titled “7.4 The Unclassified Queue and the five actions”The page that did not exist in the old build. It is the backlog age that matters here, so age carries a coloured tag against the 5-business-day SLA, the banner names how far past it the oldest record is, and the KPI band’s own bar splits the queue by SLA band.
The five §7.4 actions live in one place rather than being written once for this page and once for the workbench’s Action Inspector, because two copies of “assign a code” is two places for the audit entry to be forgotten:
| Action | What it does to the record |
|---|---|
| Assign an existing code | Picked from the current taxonomy, no free-text path. Record becomes Confirmed and joins whichever cohort now matches. |
| Propose a new mode | Structured proposal into Head of Maintenance governance already at Endorsed, since the analytical review just done is the endorsing step. Record stays Unclassified, tagged pending Head of Maintenance decision. |
| Recommend a merge | A suggested surviving code raised to the Cluster Lead. A recommendation, never an executed merge. |
| Request additional evidence | Questions the completeness of the capture. Routes to the technician, record flagged awaiting more evidence. |
| Return for correction | Questions the capture itself. Goes back with a reason category and a note. |
Every one produces a durable, recorded outcome — §7.4 is explicit that no action here may just dismiss a record. A record’s audit trail is derived from the record plus the actions taken on it, so a state the record is in cannot fail to have a trail entry. Propose carries the §17 confirm step naming its exact consequence, in a second dialog that opens over the first.
Records that are waiting on someone else are listed separately, below the queue, still counted as unclassified: §7.4 requires a record awaiting governance or evidence to stay visible rather than sit in the queue looking like untouched work.
Two things the queue got wrong first and now does not: eight columns left the action column 21px short of its content and put a scrollbar under the card, so the row’s status moved under the reason it belongs to (§7.4’s own column list never asked for a status column); and a grey “Open” badge on seven of ten rows was noise, so the absence of a hold is now a quiet line rather than a badge.
Two defects worth naming, both of the same family. A <span> stack inside a
<span> flows inline — the action title and its explanation rendered as one run of
text, the same defect the canon’s own card-subtitle rule guards against — this flow
never uses .card-sub, it uses .card-note — and the fix is display:block on both. And a disabled primary button was pixel-identical
to an armed one, because the DS styles :disabled only on the pager; a gate that
works while the button lies about it is worse than no gate, so this flow styles
:disabled on every button class.
The three-pane workbench is canon-legal
Section titled “The three-pane workbench is canon-legal”§7.2 asks for rail / record / inspector, and canon permits exactly that. The rule
that bans a pinned detail pane, web-detail-pane-pinned, targets a two-column
table-plus-detail pair, and its own text says three tracks is a layout rather than a
master-detail pair. The sibling rule matches side-panel class names, none of which
appear here — no such component is used. So the workbench is built as the PRD
specifies, with the rail selection updating panes 2 and 3 in place.
7.2 The workbench, pane by pane
Section titled “7.2 The workbench, pane by pane”grid-template-columns: 300px minmax(0,1fr) 252px — measured at 300 / 513 / 252 at
1440. The rail and the inspector are sticky, because the record is the long
thing and the other two are how you move around it.
Pane 1, the rail. Every field §7.2 asks for, including both confidence fields kept visibly apart. Age carries the SLA tag on an unclassified row and a plain date on a confirmed one, because a confirmed record’s age is not a breach. Small flags mark the four conditions that change what you can do with a record: no photos, telemetry-dark, sent back at Gate #2, later closed not-a-fault.
Five facets is past the point where filters belong in a bar, so they live in a filter modal (six groups, forty options) with the active ones as removable chips above all three panes. The confidence facet says which confidence it means — capture or cause — because conflating the two is the specific mistake the two-field model exists to prevent. Saved views sit behind the sliders button; sorting is a select with an explicit direction on each option rather than a header click, since a rail has no column headers to click.
Pane 2, the record. A single .rec-col stack, not the starter’s two-column
.rec-grid: at 513px a two-column grid gives each card 248px, which squeezed Drive
cuts out intermittently into five lines inside the Tier-1 column. The starter’s
two-column record grid is for a full-width detail page; inside a workbench pane
the canonical single column is the right unit.
Observed against confirmed is the headline, full pane width, and it turns amber when the two differ — 62 of the 149 records — with a line naming what this capture usually resolves to. That difference is the diagnostic-skill signal the whole two-tier model exists to produce, so it is the first thing on the record rather than a field in a list.
Everything sourced from the mobile gate carries the read-only band and says who owns it: the corrective-action note, both confidence figures, the reproduced-on-retest outcome, both validation gates, and the requisition’s parts and serials. The record’s own notices are dismissible and cover §18’s cases — the silently-failed photo upload, a telemetry-dark vehicle, a Gate #2 send-back, a later not-a-fault closure.
Pane 3, the inspector. The same five actions as the queue, plus add-to-packet, flag-a-data-quality-issue, and three ways to look wider. It ends with what this desk cannot do, stated rather than merely absent: §5.4 makes promote, merge, reject and return-to-service architectural boundaries, and a boundary nobody can see reads as a missing feature.
Selecting a rail row updates panes 2 and 3 in place — verified: the view id is unchanged across a selection, which is §7.2’s own acceptance criterion.
The three visual modules in pane 2
Section titled “The three visual modules in pane 2”Evidence gallery. Photos are derived from the record: how many were captured, which gate each was attached to, and what it shows — a photo taken at fault capture and one taken at the return-to-service check are two different claims about one vehicle, so the tile says which is which instead of making them interchangeable. The tile stands for the photo; this is a prototype and there is no image to ship, so it carries the subject and the timestamp rather than a fake picture. §16 requires the lightbox to be fully keyboard-navigable, so it is a focusable overlay with arrow stepping and Escape, and Escape falls through the overlay stack in the right order (lightbox, then confirm, then action dialog, then the role modal).
Telemetry at the fault. A window ninety seconds either side of the fault timestamp. Which channels exist depends on the VCU, which the PRD flags as unresolved at [VERIFY-5] — so the model says so: VC2520 reports two channels, VC2610 three, VC2705 four, rather than pretending every vehicle reports everything.
Each channel is scaled to its own range inside the window, because volts, degrees and errors per second do not share an axis; the real values travel in the tooltip and on each key at the fault moment, and the note says that is what is happening. There is no shared y-axis because a shared y-axis over three units would be a decoration.
The fault does something specific to specific channels: a throttle open drops the throttle signal and spikes CAN errors, an overheat climbs the motor temperature, a DC-DC fault sags the pack. So the chart and the confirmed cause tell the same story, and the note says which channels moved and that this is consistent with the code — or, for a mechanical failure, that no channel moves and the case rests on the teardown rather than the telemetry. That last sentence is the honest version of a chart that would otherwise look like it had failed.
A telemetry-dark vehicle gets “no telemetry available for this window” and no chart at all, which is §7.2’s acceptance criterion: a flat trace reads as a measurement.
Prior cases and the recurrence lineage. One component, used small inside a record and full size on the vehicle page, so the two surfaces cannot disagree about a vehicle’s history. Dots are coloured by outcome (confirmed and released, unclassified, sent back at Gate #2, closed not-a-fault) and clicking one selects that record in the rail.
The rail was first scaled from the service-entry date and every dot landed in the last 4% of it — these vehicles enter service a year or more before the record window. The axis now covers the failure history, and the service-entry date with the days-to-first-failure is a caption instead. That was the information worth carrying, and it did not need an axis distorted to carry it.
Under the rail, the chain in order with the gap between recurrences on each row, because the gap is the number that separates wear from a defect, and a repeat inside 30 days is called out as what it is on the reliability figures.
7.3 Cohorts, and the time-to-first-failure question
Section titled “7.3 Cohorts, and the time-to-first-failure question”The cohort list carries §7.3’s columns, with two things stated rather than implied: the at-risk population beside the member count (nine members of the thirty vehicles carrying kit-rev C), and the sample size beside the recurrence rate. Under five member vehicles the rate is reported as a count instead — a percentage over four vehicles is not a measurement.
Time to first failure is the cohort’s central analytical tool, and the thing that makes it honest is right-censoring. The population is every vehicle carrying the cohort’s attribute; the clock starts at the later of service entry and the cohort’s own install date, because a kit-revision cohort’s clock starts when the kit went on. Vehicles that have not failed are kept in the distribution as a separate band with the median days they have survived, never excluded and never counted as day zero — either would make every cohort look like it fails on installation.
The install dates were then chosen so the distribution can say different things: CH-01 (campaign live) has a 31-day median, CH-05 (recall live) 42 days, against CH-03’s 177 and CH-06’s 183. One shape for every cohort is a chart that cannot answer the manufacturing-defect question it exists to answer, and the note under it reads the shape out loud — a tight median points at the build, a long tail is wear.
Clicking a band filters the member table; a band with nothing in it is not clickable, because it reads as a filter and then empties the table, which looks like a bug rather than a fact about the distribution.
The cohort-scoped flow is the same Sankey component as §7.1a. Under eight records it says so and points at the Records tab, which at that volume says more than a diagram would.
7.3a The vehicle drill-down and the read-only work order
Section titled “7.3a The vehicle drill-down and the read-only work order”The vehicle page reuses the same timeline component as the record’s recurrence lineage, so the two surfaces cannot disagree about a vehicle’s history. Beside it: the median gap between records, the count of repeats inside 30 days, and every cohort the vehicle belongs to with its own days-to-first-failure.
The work order is the Head of Maintenance Console’s page and says so in a read-only band. Dispatch, bay assignment and replenishment are its actions; this desk has none of them, so none appear.
Two defects of the same family, again. A <span> with a height and no
display:block collapses — the whole time-to-first-failure distribution drew as six
empty grey tracks. And the view shells ship a placeholder dash as the page title,
which nothing was setting: every drill-in wore an em dash where its record’s name
belongs.
And one measurement lesson. scrollWidth reports an overflow, not a wrap —
a column can measure clean at 0px while breaking CTRL-THROTTLE-OPEN across two
lines. The column audit now counts painted line boxes on leaf elements as well, and
every table on every cohort, both tabs, is clean on both checks.
7.5 The builder, and a gate that names what is missing
Section titled “7.5 The builder, and a gate that names what is missing”Four numbered steps down the page, with the completeness gate sticky beside them rather than underneath: the whole point of an itemised gate is that you can see what is still missing while you fix it.
§7.5’s four criteria are each validated and each reported individually —
at least one record, a narrative, a recommendation, at least one evidence item —
because a single “incomplete” tells the Maintenance Supervisor nothing about what to do next. Each
failing criterion says why it matters, not just that it failed: “auto-pulled data
alone is never a case; the narrative is what makes it one”. Submit is disabled,
not merely unvalidated, and its tooltip names every failing criterion.
The evidence list is auto-pulled and derived from the scope on every read, so excluding a record removes its photos and telemetry with it rather than leaving a stale item behind. Charts are pulled only where the scope can support them: a record-set packet has no time-to-first-failure curve, because that is a cohort’s statistic. The Maintenance Supervisor curates with include toggles and reorders with up/down — the order is the order the PM reads them in. A curated order is remembered, so adding a record appends rather than reshuffling what was already arranged.
§18’s drift cases are live, and they name the records. A cohort’s membership can
change and a record’s code can be merged while a draft is open, so the packet
snapshots its scope when it is created and compares against it: which records joined,
which left because their confirmed code no longer matches this cohort, and which
are still in but now carry a different code (flagged Reclassified on the row). A
count on its own — “two records left” — says the packet moved but not what moved, and
the record that left is exactly the one whose classification changed underneath. Low
root-cause confidence never blocks inclusion; it is flagged inline in the preview so
the packet cannot imply certainty the evidence does not carry.
Preview renders from the same data the submission carries and shows a content hash, because FVEW-FR-003’s acceptance test is a hash comparison against what lands in the PM’s inbox — two renderers would make that test meaningless. Sharing produces a read-only internal link carrying that hash, so a reviewer can tell whether they are looking at the version they were asked about, and it says plainly that sharing does not submit. Submit carries the §17 confirm step naming the exact consequence, and the success message names what changed rather than saying “done”.
Three small things fixed while measuring: plural() gives the noun but not the verb,
so a notice read “2 records has low confidence”; the element that overflowed the
records table was the header, not a cell, because the DS sets th to nowrap and
CAUSE CONF. uppercase at .06em needs 104px in a 97px column; and seven columns in a
733px table left every one of them about 105px, so Photos and Telemetry folded
into one Evidence column — they are both the same question.
A submitted packet is a snapshot
Section titled “A submitted packet is a snapshot”The distinction that shapes this page: §18 says a record reclassified after a packet was handed off does not retroactively alter the packet. So Packet Detail reads the version snapshot — title, narrative, recommendation, record list and content hash as they were at submission — and separately reports where the live record has since moved, on the row and in a notice. A draft has no submitted version, so it shows the live builder content instead.
Version history is an unbroken chain from v1, each version keeping its own content, hash and changelog. A changelog that only counts words is not a diff, so it reports the fields that changed with their word counts, and the records and evidence items that came and went; Compare opens the two versions side by side. EP-0038 carries two authored versions so the diff has something real to show — its v1 was returned for computing recurrence across every throttle code rather than the cohort’s own members, and v2 excludes two records that were later closed not-a-fault.
Single-version fixture packets fill their snapshot from the scope they still have rather than a hand-written count that goes stale the moment the record population changes. EP-0038 keeps its authored figures on purpose: it was submitted in June when its cohort was larger, and that difference is part of what its history says.
FVEW-FR-004, and the anti-gaming rule
Section titled “FVEW-FR-004, and the anti-gaming rule”Revising is the only forward path from Returned, and it is explicit about what it does before it does it: v(n) is kept exactly as submitted with its own hash, v(n+1) opens in the builder carrying v(n)’s content so the Maintenance Supervisor edits what came back rather than retyping it, and the same four completeness criteria apply.
The first submission date is never overwritten. It is what the PM’s inbox queues on, and overwriting it is exactly the gaming the Shared Operating Model forbids — so the packet list’s Submitted column shows the original date, the footer says so, and a revised packet gets a Queue position card stating first-submitted, latest revision, and which of the two the queue actually uses. A rule nobody can see is a rule nobody can trust.
Verified by driving it: EP-0043 went Returned v1 → revise → v2 Submitted and
revised, its original submission date unchanged, two versions in the chain, and a
diff reading narrative 37 words to 13, recommendation 21 to 5, record count 6 to 7,
11 evidence items added.
7.6 The Data-Quality Console
Section titled “7.6 The Data-Quality Console”The foundational brief’s specific complaint about the old build was that these were static tiles. So every metric here carries four things a tile does not: its own definition in words, its numerator over its denominator spelled out, a trend computed with the same definition, and a route through to the records behind the number.
The trend is six trailing 30-day windows five days apart, each recomputed with the
metric’s own definition. A five-day bucket would give denominators too small to mean
anything; a trailing window keeps the denominator the size the metric was defined
for. dqMetrics gained a raw mode for this, because computing the trend through the
console’s own window filter would have been the metric filtered twice — a different
number wearing the metric’s name.
§12’s small-sample rule is visible, not buried. Complaint-not-reproduced has three closures in the window, so the card shows 2 of 3 with an amber note saying the rate is withheld, and its sparkline says not enough history rather than drawing a line through three points.
Every rate opens its records. dqRecords returns each metric’s numerator rows;
clicking a card’s count or any cell in the technician or bay table opens them with a
why it counts column — 20 clickable rate cells across the breakdown. Where the
numerator is genuinely zero the drill says so and states the denominator, because
“nothing behind this number” is a finding.
The composite had a contradiction worth naming: its note said the colour came from the worst component while the bar was tinted by the composite’s own value. One of the two was lying. Now the bar is the mean and the marker beside the figure is the worst component’s tone, the note says exactly that, and the summary line names which metric is weakest.
Cache handling per §11: an explicit “as of” with the age in minutes, cached fifteen, and a stale tag past twice the interval — reachable through a labelled control rather than left as a state nobody can see. Refresh recomputes and says so.
By technician and by bay run the same definitions over a narrower population, worst composite first. A bay figure says the problem is the way the work is set up rather than any one person, and the page says that out loud. Corrective actions are a coaching record, not a sanction — this console has no disciplinary authority, so each flag is logged with its actor and date and trended on both the console and the person’s own page.
7.7 Escalations, and the boundary drawn rather than described
Section titled “7.7 Escalations, and the boundary drawn rather than described”The same packets as §7.5, at a different altitude: this page is about the handoff and the conversation, not the content. A packet appears here once it has left this desk, because a draft has not been escalated to anyone.
The page opens with the boundary as a diagram — what this console does on the left, what the PM console does on the right, a handoff line between them — because §7.7’s most important sentence is about where responsibility ends, and a paragraph saying so is easier to skip than a picture showing it. The downstream feedback item is mirrored with the read-only band and no controls: there is nothing here to control.
The status timeline is built from the packet’s own history rather than from a status field, which is what makes §7.7’s acceptance criterion — every transition reconstructable from the audit log — true by construction. It also names the stages that have not happened yet for this packet.
Comms are derived the same way: a status change cannot exist without the note that explains it, so return reasons and acknowledgements appear in the log because they are the conversation. The Maintenance Supervisor can add a note; that is the whole of the “lightweight threaded log, not a messaging system” §7.7 asks for.
A returned packet left alone is stalled, and both the list and the detail say so with the day count, because §7.5 is explicit that a returned packet’s only forward path is revise-and-resubmit and nobody else will pick it up.
7.8 Notifications, 7.9 Settings, and §5 made visible
Section titled “7.8 Notifications, 7.9 Settings, and §5 made visible”Notifications are derived from state, so the bell, this page, and the page a notification points at cannot disagree. All five §14 event types are live — an unclassified record past its SLA with escalating priority, a packet the PM acted on, a cohort with enough evidence to be worth a packet and no packet built from it, a data-quality metric past its threshold, a proposal resolved by Head of Maintenance. Every one carries the deep link §20.6 demands: the exact record, cohort or packet, verified by firing all fourteen and checking where each lands. Snooze and bulk mark-read work on the derived list rather than on a stored copy of it.
Settings covers §7.9’s four areas and then one the PRD only states in prose. Saved views can be made default, opened or deleted, and deleting the default moves it, because otherwise the workbench opens on nothing and the view menu holds an index past the end of its own array. Defaults for the records rail say plainly that they set what the workbench opens on, not what it can show. The SLA and the cache interval are shown as programme settings the Maintenance Supervisor does not control.
The fourth pane is Role and authority, and it is the §5 model as a page: six things this role can do, and five things that are absent rather than denied, each naming whose they are instead. §5.4 makes promote, merge, reject and return-to-service architectural boundaries, and this is where a reader finds that out without having to notice a missing button. Delegation gets its own row saying there is none, and why.
Both §10 workflows, end to end
Section titled “Both §10 workflows, end to end”Two of this console’s loops only close when someone else acts: Head of Maintenance decides a taxonomy proposal, and the PM triages a packet. Neither is an Maintenance Supervisor action. Without a way to replay those events the console can only ever be seen half-working, and the acceptance criteria that say “resolves on its own, without further Maintenance Supervisor action” cannot be demonstrated at all. So both are replayable, from a control that is labelled as an inbound event and styled to look nothing like the console’s own buttons.
§10.1, driven:
| Path | Result |
|---|---|
| A · Assign | Record → Confirmed, joined CH-01, unclassified 9 → 8, sidebar badge followed, audit entry appended |
| B · Propose → Promoted | DRV-SPLINE-WEAR minted into the taxonomy by Head of Maintenance, record resolved to it on its own, tag cleared, unclassified 8 → 7 |
| B · Propose → Merged | Record resolved to the surviving code with §8 rule 6 traceability: “Originally proposed as ‘Lever pivot seizure after immersion’ (TP-0219), resolved via merge to BRK-LEVER” |
| B · Propose → Rejected | Record back to Unclassified with its reason restored, tag cleared, rejection in the trail |
The merge target is the code in the proposal’s own system whose name and description share the most words with what was proposed. Taking the last code in the system merged a lever-pivot seizure into a hydraulic fault — a plausible-looking answer that is wrong about the vehicle.
One proposal is already resolved in the fixture (TP-0203 → HMI-ENC-CONN-RETAIN,
which the taxonomy already describes as promoted from a field-raised mode), so the
resolved state is history rather than only something you can make happen.
§10.2, driven: submit → delivery pending → receipt confirmed → PM acknowledges →
EF-134 and the downstream mirror appears. Then a second packet returned with a real
reason → revised → v2 Submitted and tagged revised, original submission date held.
§18’s “never a silent submission the Maintenance Supervisor has no way to verify” is why delivery is a state: a packet is Submitted in this console’s own record and not treated as delivered until the PM console confirms receipt.
Grouping the two long pages
Section titled “Grouping the two long pages”Measured before touching anything: Analysis Home 2187px, of which the two stacked lists were 1229. Cohort Detail 3240px, of which the member table alone was 2068 — thirty rows, uncapped.
Analysis Home → 1603px. Records needing attention and Recent packet activity now sit side by side: they answer the same question from two directions, what needs doing and what has moved. Nothing went behind a tab, because at 1440 there is room for both and a list you have to go and find is a list nobody reads.
The attention list became compact rows rather than a table. A six-column table cannot live in half a page, and making it fit would have meant dropping the bay and the age — the two fields that make a row urgent. Stacked, all six fit in three lines: id and age, then vehicle · bay · observed, then the flag and the confirmed code. Six rows shown, with the footer stating “6 of 36 flagged · 30 more” beside the workbench button.
Cohort Detail → 1505px. Time to first failure and How these records were captured are grouped side by side, and the member table shows a page of eight with Show all 30 ⇄ Show the first 8. Silent truncation reads as “that is all of them”, so the footer always says which it is.
The half-width flow gets its own narrower geometry rather than a scaled-down copy of the full-width one: scaling a 900-wide viewBox into 492px would have put its labels at 6.9px. Measured across all seven cohorts at the new width — scale 1.00, text at 11.5px, no clipping, no label overlap.
Re-audited after the restructure: 562 call-sites across 61 states, zero failures,
zero blank pages. And the column audit re-run over 47 vehicles × 2 cap states, 7
cohorts × 2 tabs × 2 cap states plus the five top-level tables: all clean. It caught
one real thing — HMI-ENC-CONN-RETAIN is 19 characters, the longest code in the
taxonomy and the one Head of Maintenance promoted from this desk’s own proposal, and two record tables
had been sized before it existed in the data.
One rule, two containers that were missing it
Section titled “One rule, two containers that were missing it”Every page body spaces its own children through a single rule keyed off
[id$="-body"]. Two stacking containers are not page bodies and were therefore
flush:
- The record pane is a cell of the workbench grid, not a page body, so a notice
sat hard against the card beneath it while the same notice on every other page had
its 16px.
.rec-idalso ships a bottom margin for a container that does not space its children, which would have doubled the gap once the pane did — so that margin is zeroed inside it. - A dialog body does not space its children either (its forms use
.fld’s own margin), so a notice inside one carries its own bottom margin.
Verified by measuring all 19 notices across every page, every record state that raises one, every cohort, every packet and both action dialogs: each sits at 16px from whatever follows it. Fixing this one report at a time would have meant waiting for each container to be noticed; measuring every notice at once is what found the second one.
Send feedback
Section titled “Send feedback”A topbar .icon-action sits beside Notifications and opens #dlg-feedback inside the
console’s single action dialog, filled by actDlg('Send feedback', …). Type (Bug,
Improvement) and Impact (Blocking, Slows me down, Minor) share a .fld-row; Where,
Details and an optional Screenshot file input follow as .fld blocks. Where is read off
the left nav’s own nav() pairs, so a page cannot be offered that is not reachable
there, and it preselects the .pg-view.on page, falling back to the lit sidebar section
on a drill-in. sendFeedback() requires Type, Impact and Details, each with its own
toast, then closes the dialog and confirms with one more. .fld input[type=file] is the
one added rule, matching the border, radius and surface of the other .fld controls.
Verification
Section titled “Verification”Three layers, plus two audits the layers do not cover.
flow-diff reports zero structural differences from the starter template.
lint-canon is at 0 errors and 0 warnings, down from the old build’s 8, with no
entry in any baseline — both of the old flow’s baseline entries were deleted in the
same commit as the rebuild. probe-ci passes 14 view-themes at exactly 1440 in
both themes.
The click audit. Every distinct onclick call-site reachable on every page,
record, tab, cohort, packet, settings pane and dialog: 531 call-sites across 70
states, zero failures, zero blank pages, zero uncaught errors. It earned four real
fixes rather than confirming what I believed:
- The button that closes §10.2 was dead. Its attribute came out as
dlgPmAction(''+p.id+'')— the interpolation was written into the attribute instead of evaluated into it, sopwas undefined at click time. - Saved views are addressed by index, and deleting one left every later index pointing past the end of the array. Each entry point checks before it reads.
- Five write actions read their form out of the DOM with no null check. A throw inside a submit handler is what leaves a record half-changed.
- The starter’s
filterSimple,openDetail,cardTabandopenModalhelpers were dead here, andopenDetailstill wrote into#dt-refand its siblings, which stopped existing when#detail-dlgbecame the §17 confirm step.
The canvas audit found something none of the other checks could: the /flows canvas
driver clears modal hosts between frames — correct for a flow that injects its
modals, wrong for one that reuses a skeleton — and it captures frames in a context
where an overlay outside .main can be absent altogether. So the dialog now owns its
own DOM: it ships in the markup for the linter and the diff, and it is recreated if
anything has emptied or removed it. A dialog that can be destroyed from outside and
never comes back is one dead click away from a page that looks broken. Fixing it also
took the canvas from 12 frames to 14, and row-detail coverage from 5/6 tables to
6/6.
Contrast, measured in both themes on every text-on-tint pair this flow
introduces. One genuine failure, and it was mine: --amber-text is #9A5B00 in
both themes — the DS dark block does not override it — so amber ink on an amber
tint measured 2.16:1. Fixed by moving the signal to where it belongs: the dot
carries the status and the figure stays in ink. That took the age tags from 3.27 to
15.98 in dark and the withheld-rate note from 2.16 to 10.54, with no token
touched.
The same measurement shows the DS’s --amber-text, --red, --blue and --purple
are not overridden for the dark theme, which puts .badge.b-red at 3.63 and
.note-bad at 3.94 across every Atlas flow. That is a design-system decision, not a
flow one, so it is flagged and not changed here — a token edit would move 36 flows
at once.
flow-diff reports zero structural differences from the starter template.
lint-canon is at 0 errors and 0 warnings, down from 8, with no entry in any
baseline. flowProbe passes on all seven top-level views, and a three-level
drill-in and unwind was driven with the sidebar staying lit on the right section.