repository is now a presentation-ready development prototype

This commit is contained in:
2026-07-21 15:40:44 -06:00
parent 640165649d
commit 88161a6f16
24 changed files with 1989 additions and 946 deletions
+44 -22
View File
@@ -1,10 +1,18 @@
# Utah Vehicle Health dashboard
This directory is a dependency-free static prototype. It uses semantic HTML,
CSS, vanilla ES modules, and inline SVG generated from approved aggregate JSON.
It has no database client, server credential, analytics SDK, or external CDN.
This directory is a dependency-free static development prototype. It uses
semantic HTML, CSS, vanilla ES modules, and inline SVG generated from approved,
suppressed aggregate JSON. It has no database client, server credential,
analytics SDK, or external CDN.
## Run locally from the repository root
Every displayed result comes from the private 10,000-vehicle development
sample. Results are sample-based, are not population estimates, and must not be
used for individual decisions. The final prototype model is calibrated logistic
regression; the boosted tree is a benchmark only.
## Run locally
From the Utah Vehicle Health source-repository root:
```bash
node dashboard/server.mjs
@@ -23,6 +31,14 @@ Run the dependency-free contract checks with:
node --test dashboard/tests/contract.test.mjs
```
After the contents of `dashboard/` have been copied into a dashboard-only
repository or project root, use the self-contained equivalents:
```bash
npm start
npm test
```
## Public-data boundary
The browser requires and validates these files beneath `public/data/`:
@@ -41,27 +57,33 @@ contains a denied identifier-like field, the dashboard shows an unavailable
state and no estimates. Regenerate assets with the repository's private local
pipeline; never hand-edit public JSON to bypass suppression.
The next-test estimator is intentionally disabled. It must remain disabled
until a separately reviewed and suppressed `prediction_lookup` contract is
approved. Do not add a connection from this site to `countydata`.
The site intentionally contains no vehicle lookup, individual estimator,
operational connection, or row-level prediction output. VINs, plates, ZIPs,
stations, technician identifiers, raw source JSON, credentials, and operational
records must never enter this directory.
## Import into Bolt
## Publish through Bolt
The recommended publication boundary is a separate static Bolt project (and,
ideally, a separate deployment repository) containing only the contents of
`dashboard/`. Upload or copy this directory's contents so `index.html` is at the
new project root. There is no install or build step. If Bolt asks for a preview
command, use `node server.mjs` with `HOST=0.0.0.0`; Bolt supplies `PORT`.
Use a separate static Bolt project backed by a dashboard-only repository. Do
not import this source repository into Bolt.
Before publishing, confirm that `/public/data/data_manifest.json` resolves,
the development-preview banner remains visible, and the contract test passes.
The generated JSON bundle must also complete its privacy review.
1. Run `npm test` from this directory and confirm every contract check passes.
2. Create a clean dashboard-only repository or Bolt project.
3. Copy only the *contents* of `dashboard/` into that project, so `index.html`
is at the project root.
4. Confirm that private `data/`, `artifacts/`, `models/`, `.env` files, SQL,
notebooks, and database tooling are absent.
5. Preview the project. There is no install or build step; if Bolt requests a
preview command, use `node server.mjs` with `HOST=0.0.0.0` and let Bolt
provide `PORT`.
6. Verify all four views, the development-sample messaging on every view, and
that the in-app status reads **Validated sample aggregates**.
7. Use **Publish** only after the aggregate bundle completes privacy review.
Bolt hosting is the default; Netlify can instead be selected before the
first publish if desired.
Importing the whole source repository is a discouraged fallback because Bolt
would receive SQL, database tooling, and other files that are not needed by the
public site. If that has already happened, set the project/working root to
`dashboard`, never serve the repository root, and create a dashboard-only
project before production publication. Do not copy private `data/`,
`artifacts/`, `models/`, `.env`, or database tooling into the public project.
See Bolt's official documentation for [Git
integration](https://support.bolt.new/integrations/git) and [publishing with
Netlify](https://support.bolt.new/integrations/netlify).
No publishing action is performed by this repository.
+108 -102
View File
@@ -3,8 +3,9 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Utah Vehicle Health — an accessible, aggregate view of emissions-inspection outcomes.">
<meta name="description" content="Sample-based research on the first-attempt non-pass outcome of a returning vehicle's next emissions inspection.">
<meta name="referrer" content="no-referrer">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; font-src 'self'; object-src 'none'; base-uri 'none'; form-action 'none'">
<title>Overview · Utah Vehicle Health</title>
<link rel="stylesheet" href="./styles.css">
<script type="module" src="./js/app.js"></script>
@@ -16,7 +17,7 @@
<span class="preview-banner__icon" aria-hidden="true">◆</span>
<div>
<strong id="preview-title">Development preview</strong>
<span id="preview-copy">Sample results are not population estimates and must not be used for individual decisions.</span>
<span id="preview-copy">Private 10,000-vehicle development sample · suppressed aggregates only · not population estimates.</span>
</div>
</div>
@@ -29,7 +30,7 @@
</svg>
<span>
<span class="brand__name">Utah Vehicle Health</span>
<span class="brand__tagline">Next-episode inspection insights</span>
<span class="brand__tagline">Returning vehicles · next first attempt</span>
</span>
</a>
@@ -42,8 +43,8 @@
<nav class="primary-nav" aria-label="Dashboard views">
<div class="primary-nav__inner">
<a href="#overview" data-route="overview" aria-current="page">Overview</a>
<a href="#reliability" data-route="reliability">Reliability explorer</a>
<a href="#estimator" data-route="estimator">Next-test estimator</a>
<a href="#cohorts" data-route="cohorts">Sample cohorts</a>
<a href="#model" data-route="model">Model &amp; benchmark</a>
<a href="#methods" data-route="methods">Data &amp; methods</a>
</div>
</nav>
@@ -54,9 +55,9 @@
<div class="page-shell">
<header class="page-heading page-heading--split">
<div>
<p class="eyebrow">Inspection outcomes over time</p>
<h1 id="overview-heading">A clearer view of the next inspection</h1>
<p class="lede">Explore aggregate first-attempt outcomes for returning vehicles in participating Utah county feeds. Non-pass combines fail, reject, and abort.</p>
<p class="eyebrow">Private 10,000-vehicle development sample</p>
<h1 id="overview-heading">The next inspection, in sample context</h1>
<p class="lede">The research question is intentionally narrow: predict whether a returning vehicle's next inspection episode has a first-attempt non-pass. Non-pass combines fail, reject, and abort.</p>
</div>
<div class="data-stamp" aria-label="Dataset status">
<span>Data through</span>
@@ -65,6 +66,11 @@
</div>
</header>
<aside class="scope-strip" aria-label="Result scope">
<strong>Sample-based only</strong>
<span>Every value on this page describes the private development sample and covered feeds—not Utah's vehicle population.</span>
</aside>
<div class="unavailable-state" data-unavailable hidden role="status">
<div class="unavailable-state__icon" aria-hidden="true">!</div>
<div>
@@ -75,22 +81,22 @@
<section class="kpi-grid" aria-label="Overview statistics" data-requires-data>
<article class="kpi-card">
<span class="kpi-card__label">Published support</span>
<span class="kpi-card__label">Published episode support</span>
<strong id="kpi-eligible">—</strong>
<small>Rounded total; suppressed cells omitted</small>
<small>Rounded total; sampled vehicles may contribute multiple eligible episodes</small>
</article>
<article class="kpi-card kpi-card--pass">
<span class="kpi-card__label">Pass rate</span>
<span class="kpi-card__label">Observed sample pass rate</span>
<strong id="kpi-pass">—</strong>
<small>Recognized first-attempt outcomes</small>
</article>
<article class="kpi-card kpi-card--nonpass">
<span class="kpi-card__label">Non-pass rate</span>
<span class="kpi-card__label">Observed sample non-pass rate</span>
<strong id="kpi-nonpass">—</strong>
<small>Fail + reject + abort</small>
</article>
<article class="kpi-card">
<span class="kpi-card__label">Covered counties</span>
<span class="kpi-card__label">Covered county feeds</span>
<strong id="kpi-counties">—</strong>
<small>Not statewide coverage</small>
</article>
@@ -100,8 +106,8 @@
<article class="panel panel--wide">
<header class="panel__header">
<div>
<p class="eyebrow">Quarterly outcomes</p>
<h2>First-attempt non-pass trend</h2>
<p class="eyebrow">Sample association over time</p>
<h2>Observed first-attempt non-pass trend</h2>
</div>
<div class="chart-legend" aria-label="Outcome legend">
<span><i class="legend-swatch legend-swatch--fail"></i>Non-pass (fail + reject + abort)</span>
@@ -120,36 +126,41 @@
</header>
<div id="utah-coverage-map" class="utah-map" role="img" aria-label="Utah county-feed coverage"></div>
<div id="county-coverage-list" class="county-list" aria-label="County availability list"></div>
<p class="chart-note">Teal counties appear in the approved inspection feeds. Gray counties are unavailable, not zero.</p>
<p class="chart-note">Availability only—this is not a county outcome ranking. Teal feeds are represented in the sample; gray counties are unavailable, not zero.</p>
</article>
<article class="panel">
<header class="panel__header">
<div>
<p class="eyebrow">Age pattern</p>
<h2>Non-pass risk by vehicle age</h2>
<p class="eyebrow">Sample association by age</p>
<h2>Observed non-pass rate by vehicle age</h2>
</div>
</header>
<div id="age-risk-chart" class="chart" role="img" aria-label="Published non-pass risk by vehicle-age band"></div>
<p class="chart-note">The development bundle does not include confidence intervals; published aggregate rates are shown as points.</p>
<p class="chart-note">Sample aggregates only, without confidence intervals. The pattern is descriptive and not a population estimate or causal effect.</p>
</article>
</div>
<aside class="callout" data-requires-data>
<span class="callout__icon" aria-hidden="true">i</span>
<p><strong>What this measures:</strong> emissions-inspection outcomes for covered feeds. It is not a diagnosis of mechanical condition, safety, roadworthiness, or legal compliance.</p>
<p><strong>What this measures:</strong> sampled returning vehicles' next-episode first-attempt emissions-inspection outcomes in covered feeds. It is not a population estimate, mechanical diagnosis, safety assessment, or legal determination.</p>
</aside>
</div>
</section>
<section class="view" id="view-reliability" data-view="reliability" aria-labelledby="reliability-heading" hidden>
<section class="view" id="view-cohorts" data-view="cohorts" aria-labelledby="cohorts-heading" hidden>
<div class="page-shell">
<header class="page-heading">
<p class="eyebrow">Supported aggregate cohorts</p>
<h1 id="reliability-heading">Reliability explorer</h1>
<p class="lede">Compare published observed next-episode non-pass rates. Every result is a supported aggregate cohort—not an individual prediction.</p>
<p class="eyebrow">Private 10,000-vehicle development sample</p>
<h1 id="cohorts-heading">Sample cohort explorer</h1>
<p class="lede">Explore supported prior make-and-model cohorts and their observed next-episode first-attempt non-pass rates. These are sample associations—not reliability grades or individual predictions.</p>
</header>
<aside class="scope-strip" aria-label="Result scope">
<strong>Not a ranking of population reliability</strong>
<span>Rates use rounded, suppressed episode support from sampled vehicles. Cohorts can differ in age, history, period, and coverage.</span>
</aside>
<div class="unavailable-state" data-unavailable hidden role="status">
<div class="unavailable-state__icon" aria-hidden="true">!</div>
<div><h2>Explorer unavailable</h2><p data-unavailable-message>Approved scorecards could not be validated.</p></div>
@@ -158,7 +169,7 @@
<div class="explorer-layout" data-requires-data>
<aside class="filter-panel" aria-labelledby="filter-heading">
<div class="filter-panel__heading">
<h2 id="filter-heading">Refine cohorts</h2>
<h2 id="filter-heading">Find a cohort</h2>
<button class="text-button" id="reset-filters" type="button">Reset</button>
</div>
<form id="explorer-filters">
@@ -166,31 +177,7 @@
<span>Make or model</span>
<input id="cohort-search" type="search" autocomplete="off" placeholder="Search supported cohorts">
</label>
<label class="field">
<span>County</span>
<select id="filter-county"><option value="">All covered counties</option></select>
</label>
<label class="field">
<span>Vehicle age</span>
<select id="filter-age"><option value="">All age bands</option></select>
</label>
<label class="field">
<span>Fuel</span>
<select id="filter-fuel"><option value="">All supported fuels</option></select>
</label>
<label class="field">
<span>Program</span>
<select id="filter-program"><option value="">All approved programs</option></select>
</label>
<label class="field">
<span>Period</span>
<select id="filter-period"><option value="">All published periods</option></select>
</label>
<fieldset class="segmented-control">
<legend>Risk view</legend>
<label><input type="radio" name="risk-view" value="observed" checked><span>Observed</span></label>
<label><input type="radio" name="risk-view" value="adjusted"><span>Model-adjusted</span></label>
</fieldset>
<p class="filter-panel__note"><strong>Published grain:</strong> prior make and model only. County, age, fuel, program, and period slices are not available for these scorecards.</p>
</form>
</aside>
@@ -201,8 +188,8 @@
<span>Sort</span>
<select id="result-sort">
<option value="support">Largest support</option>
<option value="risk-desc">Highest non-pass risk</option>
<option value="risk-asc">Lowest non-pass risk</option>
<option value="risk-desc">Highest observed sample rate</option>
<option value="risk-asc">Lowest observed sample rate</option>
<option value="name">Make and model</option>
</select>
</label>
@@ -210,9 +197,10 @@
<article class="panel">
<header class="panel__header">
<div><p class="eyebrow">Published aggregate comparison</p><h2>Supported cohort scorecard</h2></div>
<div><p class="eyebrow">Descriptive sample comparison</p><h2>Observed cohort associations</h2></div>
</header>
<div id="cohort-dot-plot" class="chart chart--scorecard" role="img" aria-label="Ranked published cohort non-pass rates"></div>
<div id="cohort-dot-plot" class="chart chart--scorecard" role="img" aria-label="Observed development-sample cohort non-pass rates"></div>
<p class="chart-note">Ordering is a viewing aid within this sample, not a reliability ranking or population comparison.</p>
</article>
<div id="cohort-cards" class="cohort-cards"></div>
@@ -225,51 +213,64 @@
</div>
</section>
<section class="view" id="view-estimator" data-view="estimator" aria-labelledby="estimator-heading" hidden>
<section class="view" id="view-model" data-view="model" aria-labelledby="model-heading" hidden>
<div class="page-shell page-shell--narrow">
<header class="page-heading">
<p class="eyebrow">Cohort estimate · not an individual diagnosis</p>
<h1 id="estimator-heading">Next-test risk estimator</h1>
<p class="lede">This planned tool will combine approved, coarsened attributes to return a calibrated next-episode non-pass probability.</p>
<p class="eyebrow">Private 10,000-vehicle development sample</p>
<h1 id="model-heading">Model &amp; benchmark</h1>
<p class="lede">Calibrated logistic regression is the final development-prototype model for the next-episode first-attempt non-pass target. The boosted tree is retained only as a benchmark.</p>
</header>
<div class="locked-panel" role="status" aria-labelledby="estimator-status-title">
<span class="locked-panel__icon" aria-hidden="true">◇</span>
<div>
<p class="eyebrow">Intentionally disabled</p>
<h2 id="estimator-status-title">An approved prediction lookup is not available</h2>
<p>The estimator will remain off until a privacy-reviewed, suppressed <code>prediction_lookup</code> is published. The current model diagnostics are not enough to serve individual or row-level estimates.</p>
</div>
<aside class="scope-strip" aria-label="Result scope">
<strong>Development evidence only</strong>
<span>These 2024 values come from the partition used to fit Platt calibration. They are sample-based calibration checks—not independent final-performance estimates, population estimates, or locked-test results.</span>
</aside>
<div class="unavailable-state" data-unavailable hidden role="status">
<div class="unavailable-state__icon" aria-hidden="true">!</div>
<div><h2>Model diagnostics unavailable</h2><p data-unavailable-message>The approved diagnostic summary could not be validated.</p></div>
</div>
<form class="estimator-form" aria-describedby="estimator-disabled-copy">
<fieldset disabled>
<legend>Coarsened cohort attributes</legend>
<div class="form-grid">
<label class="field"><span>County</span><select><option>Select a covered county</option></select></label>
<label class="field"><span>Supported make and model</span><select><option>Select a cohort</option></select></label>
<label class="field"><span>Vehicle-age band</span><select><option>Select an age band</option></select></label>
<label class="field"><span>Fuel</span><select><option>Select a supported fuel</option></select></label>
<label class="field"><span>Prior episode outcome</span><select><option>Pass / fail / reject / abort</option></select></label>
<label class="field"><span>Time since prior episode</span><select><option>Select a coarsened interval</option></select></label>
<label class="field"><span>Season</span><select><option>Select a season</option></select></label>
<label class="field"><span>Program category</span><select><option>Select an approved program</option></select></label>
</div>
<button class="button" type="button">Estimate cohort risk</button>
</fieldset>
</form>
<p id="estimator-disabled-copy" class="form-note">No VIN, plate, exact address, station, free text, or current-test diagnostic will ever be requested.</p>
<section class="model-comparison" data-requires-data aria-label="Development model comparison">
<article class="model-card model-card--final">
<div class="model-card__topline"><span class="decision-badge decision-badge--final">Final prototype</span><span>2024 calibration-fit check</span></div>
<h2>Calibrated logistic regression</h2>
<p>The selected model for the research conclusion and any prototype-level discussion. Platt scaling calibrates its probabilities.</p>
<dl class="metric-list">
<div><dt>PR-AUC</dt><dd id="final-ap">—</dd></div>
<div><dt>ROC-AUC</dt><dd id="final-roc">—</dd></div>
<div><dt>Brier ↓</dt><dd id="final-brier">—</dd></div>
</dl>
</article>
<section class="method-preview" aria-labelledby="future-output-heading">
<article class="model-card">
<div class="model-card__topline"><span class="decision-badge">Benchmark only</span><span>2024 calibration-fit check</span></div>
<h2>Calibrated boosted tree</h2>
<p>Retained for development comparison only. It does not replace the logistic model or define dashboard outputs.</p>
<dl class="metric-list">
<div><dt>PR-AUC</dt><dd id="benchmark-ap">—</dd></div>
<div><dt>ROC-AUC</dt><dd id="benchmark-roc">—</dd></div>
<div><dt>Brier ↓</dt><dd id="benchmark-brier">—</dd></div>
</dl>
</article>
</section>
<aside class="callout" data-requires-data>
<span class="callout__icon" aria-hidden="true">i</span>
<p><strong>Calibration diagnostic—not a final performance estimate:</strong> Platt scaling was fit on this same 2024 partition. PR-AUC and ROC-AUC summarize discrimination; lower Brier is better for probability error. Opened 2025 holdout metrics are intentionally not displayed here.</p>
</aside>
<section class="publication-boundary" aria-labelledby="publication-boundary-heading">
<div>
<p class="eyebrow">Future approved output</p>
<h2 id="future-output-heading">Designed for calibrated context</h2>
<p class="eyebrow">Finished publication boundary</p>
<h2 id="publication-boundary-heading">No vehicle-level prediction service</h2>
<p>This static dashboard serves supported aggregate sample rates and diagnostic summaries only. It contains no vehicle lookup, personal inputs, operational connection, or row-level prediction output.</p>
</div>
<ul class="check-list">
<li>Calibrated non-pass probability and uncertainty</li>
<li>Relevant aggregate baseline</li>
<li>High-level factor contributions</li>
<li>Support and coverage limitations</li>
<li>Returning-vehicle next-episode target only</li>
<li>First attempt; pass versus non-pass</li>
<li>Final model: calibrated logistic regression</li>
<li>Boosted tree: benchmark only</li>
</ul>
</section>
</div>
@@ -278,11 +279,16 @@
<section class="view" id="view-methods" data-view="methods" aria-labelledby="methods-heading" hidden>
<div class="page-shell">
<header class="page-heading">
<p class="eyebrow">Transparent by design</p>
<p class="eyebrow">Private 10,000-vehicle development sample</p>
<h1 id="methods-heading">Data &amp; methods</h1>
<p class="lede">How episodes, labels, temporal evaluation, coverage limits, and privacy controls shape every published result.</p>
</header>
<aside class="scope-strip" aria-label="Result scope">
<strong>Sample-based, not population-based</strong>
<span>All displayed rates and diagnostics come from the development sample. County displays communicate feed coverage only.</span>
</aside>
<div class="unavailable-state" data-unavailable hidden role="status">
<div class="unavailable-state__icon" aria-hidden="true">!</div>
<div><h2>Live diagnostics unavailable</h2><p data-unavailable-message>Static methodology remains below; no performance values are being shown.</p></div>
@@ -330,13 +336,13 @@
<div class="dashboard-grid" data-requires-data>
<article class="panel">
<header class="panel__header"><div><p class="eyebrow" id="diagnostic-scope-label">Model diagnostics</p><h2>Published model diagnostics</h2></div></header>
<div class="diagnostic-kpis">
<div><span>PR-AUC</span><strong id="diagnostic-ap">—</strong></div>
<div><span>Brier score</span><strong id="diagnostic-brier">—</strong></div>
<div><span>Support</span><strong id="diagnostic-n">—</strong></div>
</div>
<div id="calibration-chart" class="chart" role="img" aria-label="Predicted versus observed non-pass risk calibration"></div>
<header class="panel__header"><div><p class="eyebrow">Model governance</p><h2>Evidence status</h2></div></header>
<ul class="limitation-list">
<li>Calibrated logistic regression is the declared final prototype model.</li>
<li>The boosted tree remains a benchmark only.</li>
<li>Published diagnostics cover train, tune, and calibration partitions.</li>
<li>No locked-test result or population performance claim is published.</li>
</ul>
</article>
<article class="panel">
@@ -344,7 +350,7 @@
<ul class="limitation-list" id="manifest-limitations">
<li>Participating inspection feeds do not cover all 29 counties.</li>
<li>Feed and program changes can resemble real-world trends.</li>
<li>DMV history has a 2021 gap and ends in March 2024.</li>
<li>The 10,000-vehicle draw retained 9,996 vehicles; page sampling can over-represent vehicles with more inspection records.</li>
<li>Reject and abort can reflect process or readiness issues.</li>
<li>Association is not causation or a mechanical diagnosis.</li>
</ul>
@@ -358,9 +364,9 @@
<span aria-hidden="true">→</span>
<span>Suppression<br><small>Minimum support + complements</small></span>
<span aria-hidden="true">→</span>
<span class="privacy-flow__public">Public assets<br><small>Aggregate JSON only</small></span>
<span class="privacy-flow__public">Public assets<br><small>Suppressed summaries only</small></span>
</div>
<p>Direct identifiers, private vehicle tokens, plates, ZIPs, stations, raw records, and row-level predictions are outside the public data contract.</p>
<p>VINs, plates, ZIPs, stations, technician identifiers, private vehicle tokens, raw source JSON, credentials, operational records, and row-level predictions are outside this site and its public data contract.</p>
</section>
</div>
</section>
@@ -368,8 +374,8 @@
<footer class="site-footer">
<div class="page-shell site-footer__inner">
<div><strong>Utah Vehicle Health</strong><p>Aggregate emissions-inspection research.</p></div>
<p>Not a diagnosis, certification, safety assessment, or guarantee of an inspection outcome.</p>
<div><strong>Utah Vehicle Health</strong><p>Private 10,000-vehicle development sample.</p></div>
<p>Sample-based research only—not a population estimate, diagnosis, certification, safety assessment, or guarantee of an inspection outcome.</p>
</div>
</footer>
+28 -79
View File
@@ -6,15 +6,14 @@ import {
renderAgeRisk,
renderCohortDotPlot,
renderCoverageHeatmap,
renderNoCalibration,
renderOutcomeTrend,
renderUtahCoverage,
} from "./charts.js";
const ROUTES = Object.freeze({
overview: "Overview",
reliability: "Reliability explorer",
estimator: "Next-test estimator",
cohorts: "Sample cohorts",
model: "Model & benchmark",
methods: "Data & methods",
});
@@ -66,13 +65,6 @@ function normalizeCounty(value) {
return String(value).trim().toLowerCase().replaceAll("_", " ").replace(/\s+/g, " ");
}
function displayCategory(value) {
return String(value)
.replaceAll("_", " ")
.toLowerCase()
.replace(/\b\w/g, (letter) => letter.toUpperCase());
}
function routeFromHash() {
const candidate = window.location.hash.replace(/^#/, "").toLowerCase();
return Object.hasOwn(ROUTES, candidate) ? candidate : "overview";
@@ -120,7 +112,7 @@ function setPreviewBanner(manifest) {
setText("preview-title", "Development preview");
setText(
"preview-copy",
"These suppressed sample aggregates are not population estimates and must not be used for individual decisions.",
"Private 10,000-vehicle development sample · suppressed aggregates only · not population estimates.",
);
}
@@ -144,25 +136,6 @@ function setUnavailable(error) {
setHeaderStatus("error", "Approved data unavailable");
}
function populateSelect(id, values, formatter = displayCategory) {
const select = byId(id);
if (!select) return;
for (const value of values) {
const option = document.createElement("option");
option.value = String(value);
option.textContent = formatter(value);
select.append(option);
}
}
function disableUnsupportedFilter(id, explanation) {
const select = byId(id);
if (!select) return;
select.disabled = true;
select.title = explanation;
select.options[0].textContent = explanation;
}
function renderCountyList(coveredCounties) {
const container = byId("county-coverage-list");
container.replaceChildren();
@@ -195,12 +168,12 @@ function renderOverview(data) {
setText("data-cutoff", latest ? `${latest.year} Q${latest.quarter}` : "Unavailable");
setText(
"model-version",
`Release ${data.manifest.release_id.slice(0, 8)} · ${data.manifest.model_versions.join(" / ")}`,
`Final: calibrated logistic · release ${data.manifest.release_id.slice(0, 8)}`,
);
renderOutcomeTrend(byId("outcome-trend-chart"), rows);
setText(
"outcome-trend-note",
"The current approved bundle publishes binary non-pass rates only. Four-class outcome mix and blank rates are not inferred or displayed.",
"Development-sample aggregates only, not a population trend. The bundle publishes binary non-pass rates; four-class outcome mix and blank rates are not inferred.",
);
renderAgeRisk(byId("age-risk-chart"), data.ageRisk.rows);
renderUtahCoverage(byId("utah-coverage-map"), counties);
@@ -230,7 +203,7 @@ function renderScorecardCards(rows) {
heading.textContent = scorecardLabel(row);
const meta = document.createElement("div");
meta.className = "cohort-card__meta";
meta.textContent = `${compactNumber(row.support_rounded)} rounded support`;
meta.textContent = `≈${compactNumber(row.support_rounded)} eligible sample episodes (rounded)`;
const bar = document.createElement("div");
bar.className = "risk-bar";
bar.setAttribute("aria-hidden", "true");
@@ -242,7 +215,7 @@ function renderScorecardCards(rows) {
const strong = document.createElement("strong");
strong.textContent = percent(row.nonpass_rate);
const small = document.createElement("small");
small.textContent = "Observed non-pass";
small.textContent = "Observed sample non-pass";
value.append(strong, small);
article.append(heading, meta, bar, value);
container.append(article);
@@ -259,33 +232,14 @@ function updateExplorer() {
visibleScorecards = sortedScorecards(matches, sortMode);
setText(
"result-summary",
`${visibleScorecards.length} supported cohort${visibleScorecards.length === 1 ? "" : "s"}; showing up to 18 cards and 12 chart rows.`,
`${visibleScorecards.length} supported sample cohort${visibleScorecards.length === 1 ? "" : "s"}; showing up to 18 cards and 12 chart rows.`,
);
byId("empty-results").hidden = visibleScorecards.length > 0;
renderCohortDotPlot(byId("cohort-dot-plot"), visibleScorecards);
renderScorecardCards(visibleScorecards);
}
function initializeExplorer(data) {
populateSelect("filter-county", data.filters.public_counties);
populateSelect("filter-age", data.filters.age_bands, (value) => String(value));
populateSelect(
"filter-period",
data.filters.periods.map((period) => period.year),
(value) => String(value),
);
// Current scorecards are make/model aggregates only. These planned controls
// remain visible but disabled so the UI never implies unsupported slicing.
disableUnsupportedFilter("filter-county", "Unavailable at current scorecard grain");
disableUnsupportedFilter("filter-age", "Unavailable at current scorecard grain");
disableUnsupportedFilter("filter-fuel", "Fuel not published in this bundle");
disableUnsupportedFilter("filter-program", "Program not published in this bundle");
disableUnsupportedFilter("filter-period", "Period not published in this scorecard");
const adjusted = document.querySelector('input[name="risk-view"][value="adjusted"]');
adjusted.disabled = true;
adjusted.parentElement.title = "Model-adjusted cohort scorecards are not published.";
function initializeExplorer() {
byId("cohort-search").addEventListener("input", updateExplorer);
byId("result-sort").addEventListener("change", updateExplorer);
byId("reset-filters").addEventListener("click", () => {
@@ -297,42 +251,37 @@ function initializeExplorer(data) {
updateExplorer();
}
function chooseDiagnostic(rows) {
const partitionRank = { calibrate: 3, tune: 2, train: 1 };
return [...rows].sort((left, right) => {
const preferredLeft = left.model === "logistic_platt" ? 1 : 0;
const preferredRight = right.model === "logistic_platt" ? 1 : 0;
return (
preferredRight - preferredLeft ||
(partitionRank[right.partition] ?? 0) - (partitionRank[left.partition] ?? 0)
);
})[0];
function findDiagnostic(rows, model) {
return rows.find((row) => row.model === model && row.partition === "calibrate");
}
function renderModel(data) {
const finalModel = findDiagnostic(data.diagnostics.rows, "logistic_platt");
const benchmark = findDiagnostic(data.diagnostics.rows, "hist_gradient_boosting_platt");
const metric = (row, key) => (row ? row[key].toFixed(3) : "—");
setText("final-ap", metric(finalModel, "average_precision"));
setText("final-roc", metric(finalModel, "roc_auc"));
setText("final-brier", metric(finalModel, "brier"));
setText("benchmark-ap", metric(benchmark, "average_precision"));
setText("benchmark-roc", metric(benchmark, "roc_auc"));
setText("benchmark-brier", metric(benchmark, "brier"));
}
function renderMethods(data) {
renderCoverageHeatmap(byId("coverage-heatmap"), data.coverage.rows);
const diagnostic = chooseDiagnostic(data.diagnostics.rows);
setText(
"diagnostic-scope-label",
diagnostic
? `${diagnostic.partition === "calibrate" ? "Calibration cohort" : "Development diagnostics"} · ${diagnostic.model}`
: "Development diagnostics · model unavailable",
);
setText("diagnostic-ap", diagnostic ? diagnostic.average_precision.toFixed(3) : "—");
setText("diagnostic-brier", diagnostic ? diagnostic.brier.toFixed(3) : "—");
setText("diagnostic-n", "Not published");
renderNoCalibration(byId("calibration-chart"));
}
function renderDashboard(data) {
dashboardData = data;
setPreviewBanner(data.manifest);
renderOverview(data);
initializeExplorer(data);
initializeExplorer();
renderModel(data);
renderMethods(data);
setHeaderStatus(
"ready",
data.manifest.development_preview ? "Validated development aggregates" : "Validated public aggregates",
data.manifest.development_preview ? "Validated sample aggregates" : "Validated public aggregates",
);
}
@@ -349,4 +298,4 @@ async function initialize() {
initialize();
export { displayCategory, normalizeCounty, routeFromHash };
export { normalizeCounty, routeFromHash };
+5 -12
View File
@@ -109,7 +109,7 @@ export function renderOutcomeTrend(container, rows) {
</svg>`;
container.setAttribute(
"aria-label",
`Quarterly aggregate non-pass trend. ${description}`,
`Development-sample quarterly aggregate non-pass rates, not population estimates. ${description}`,
);
}
@@ -155,7 +155,7 @@ export function renderAgeRisk(container, rows) {
</svg>`;
container.setAttribute(
"aria-label",
`Non-pass risk by vehicle-age band. ${points
`Observed development-sample non-pass rates by vehicle-age band, not population estimates. ${points
.map((point) => `${point.age_band}: ${percent(point.nonpass_rate)}`)
.join("; ")}`,
);
@@ -199,7 +199,7 @@ export function renderCohortDotPlot(container, rows) {
</svg>`;
container.setAttribute(
"aria-label",
`Ranked supported cohort non-pass risk. ${points
`Supported development-sample cohort associations, not reliability rankings or population estimates. ${points
.map((point) => `${point.prior_make} ${point.prior_model}: ${percent(point.nonpass_rate)}`)
.join("; ")}`,
);
@@ -234,7 +234,7 @@ export function renderUtahCoverage(container, coveredCounties) {
container.setAttribute(
"aria-label",
coveredCounties.length
? `Utah feed coverage includes ${coveredCounties.join(", ")}. Other counties are unavailable.`
? `Development-sample feed availability includes ${coveredCounties.join(", ")}. This is not an outcome ranking; other counties are unavailable.`
: "No county feed coverage is available.",
);
}
@@ -267,14 +267,7 @@ export function renderCoverageHeatmap(container, rows) {
</div>`;
container.setAttribute(
"aria-label",
`Source-era coverage from ${years[0]} through ${years[years.length - 1]} for ${eras.join(", ")}.`,
);
}
export function renderNoCalibration(container) {
emptyChart(
container,
"Calibration-bin data is not published in this development bundle. No calibration curve is shown.",
`Development-sample source-era coverage from ${years[0]} through ${years[years.length - 1]} for ${eras.join(", ")}.`,
);
}
+30
View File
@@ -1,5 +1,10 @@
const SCHEMA_VERSION = "dashboard_data_v1";
const APPROVED_PARTITIONS = Object.freeze(["train", "tune", "calibrate"]);
const APPROVED_TARGET = "first-attempt next-episode binary non-pass rate";
const REQUIRED_CALIBRATION_MODELS = Object.freeze([
"hist_gradient_boosting_platt",
"logistic_platt",
]);
const ENVELOPE_KEYS = Object.freeze([
"development_preview",
"population_estimate_allowed",
@@ -20,15 +25,22 @@ export const REQUIRED_ASSETS = Object.freeze({
const SENSITIVE_KEY_PARTS = new Set([
"address",
"certificate",
"credential",
"email",
"inspector",
"internal",
"ip",
"owner",
"password",
"pid",
"plate",
"prediction",
"probability",
"raw",
"secret",
"session",
"station",
"technician",
"token",
"user",
"vin",
@@ -337,6 +349,11 @@ export function validateAssetSet(rawAssets) {
requireExactKeys(rawAssets, Object.keys(REQUIRED_ASSETS), "asset set");
const manifest = rawAssets.manifest;
validateEnvelope(manifest, "data_manifest");
if (!manifest.development_preview || manifest.population_estimate_allowed) {
throw new DataContractError(
"The dashboard accepts development-sample, non-population assets only.",
);
}
requireExactKeys(
manifest,
[
@@ -427,6 +444,9 @@ export function validateAssetSet(rawAssets) {
requireInteger(manifest.definitions[key], `data_manifest.definitions.${key}`, { min: 1 });
}
requireString(manifest.definitions.target, "data_manifest.definitions.target");
if (manifest.definitions.target !== APPROVED_TARGET) {
throw new DataContractError("data_manifest.definitions.target is outside the approved scope.");
}
if (!Array.isArray(manifest.assets)) {
throw new DataContractError("data_manifest.assets must be an array.");
}
@@ -463,6 +483,16 @@ export function validateAssetSet(rawAssets) {
throw new DataContractError(`model_diagnostics.rows[${index}].model is not cataloged.`);
}
}
for (const model of REQUIRED_CALIBRATION_MODELS) {
const matches = validated.diagnostics.rows.filter(
(row) => row.model === model && row.partition === "calibrate",
);
if (matches.length !== 1) {
throw new DataContractError(
`model_diagnostics must contain exactly one approved calibration row for ${model}.`,
);
}
}
const minimumSupport = manifest.definitions.suppression_min_support;
const supportRounding = manifest.definitions.support_rounding;
for (const name of ["overview", "ageRisk", "scorecards", "coverage"]) {
+209 -2
View File
@@ -37,12 +37,16 @@
}
html {
max-width: 100%;
overflow-x: clip;
scroll-behavior: smooth;
}
body {
min-width: 320px;
max-width: 100%;
margin: 0;
overflow-x: clip;
color: var(--ink-950);
background:
radial-gradient(circle at 7% 2%, rgb(231 216 185 / 35%), transparent 30rem),
@@ -165,6 +169,11 @@ h2 {
margin-right: 0.35rem;
}
.preview-banner > div {
min-width: 0;
overflow-wrap: anywhere;
}
.preview-banner__icon {
color: var(--amber-700);
}
@@ -182,7 +191,8 @@ h2 {
.site-header__inner,
.primary-nav__inner,
.page-shell {
width: min(calc(100% - 2rem), var(--content));
width: calc(100% - 2rem);
max-width: var(--content);
margin-inline: auto;
}
@@ -302,10 +312,15 @@ h2 {
}
main {
width: 100%;
min-width: 0;
max-width: 100%;
min-height: 65vh;
}
.view {
min-width: 0;
max-width: 100%;
padding: clamp(2.5rem, 6vw, 5rem) 0 5rem;
}
@@ -318,9 +333,17 @@ main {
}
.page-heading {
min-width: 0;
margin-bottom: clamp(2rem, 5vw, 3.4rem);
}
.page-heading > *,
.dashboard-grid > *,
.kpi-grid > *,
.model-comparison > * {
min-width: 0;
}
.page-heading--split {
display: flex;
align-items: end;
@@ -370,6 +393,26 @@ main {
font-size: 1.04rem;
}
.scope-strip {
display: flex;
align-items: baseline;
gap: 0.7rem 1rem;
margin: -1.4rem 0 1rem;
padding: 0.75rem 1rem;
border: 1px solid #e9c77d;
border-radius: var(--radius-sm);
color: #503407;
background: rgb(249 236 205 / 72%);
font-size: 0.78rem;
}
.scope-strip strong {
flex: 0 0 auto;
font-size: 0.72rem;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.unavailable-state {
display: flex;
align-items: flex-start;
@@ -694,6 +737,14 @@ main {
margin: 0;
}
.filter-panel__note {
margin: 1rem 0 0;
padding-top: 1rem;
border-top: 1px solid var(--sand-200);
color: var(--ink-650);
font-size: 0.75rem;
}
.text-button {
padding: 0.2rem;
border: 0;
@@ -868,6 +919,124 @@ fieldset:disabled .field select {
text-align: center;
}
.model-comparison {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 1rem;
}
.model-card {
position: relative;
padding: clamp(1.25rem, 3vw, 1.7rem);
overflow: hidden;
border: 1px solid var(--sand-200);
border-radius: var(--radius-lg);
background: rgb(255 255 255 / 86%);
box-shadow: var(--shadow-sm);
}
.model-card::before {
position: absolute;
top: 0;
right: 0;
left: 0;
height: 4px;
background: var(--sand-400);
content: "";
}
.model-card--final {
border-color: #9bc9c6;
background: linear-gradient(145deg, rgb(220 239 238 / 58%), rgb(255 255 255 / 92%));
box-shadow: var(--shadow-md);
}
.model-card--final::before {
background: var(--teal-700);
}
.model-card__topline {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
margin-bottom: 1.2rem;
color: var(--ink-500);
font-size: 0.68rem;
}
.decision-badge {
padding: 0.25rem 0.55rem;
border: 1px solid var(--sand-400);
border-radius: 999px;
color: #6a4c18;
background: var(--amber-100);
font-size: 0.64rem;
font-weight: 800;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.decision-badge--final {
border-color: #83bbb7;
color: var(--teal-800);
background: var(--teal-100);
}
.model-card > p {
min-height: 5.1em;
margin-bottom: 1.2rem;
color: var(--ink-650);
font-size: 0.82rem;
}
.metric-list {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 0.5rem;
margin: 0;
}
.metric-list div {
padding: 0.72rem;
border-radius: var(--radius-sm);
background: rgb(237 240 237 / 78%);
}
.metric-list dt {
color: var(--ink-500);
font-size: 0.62rem;
font-weight: 700;
}
.metric-list dd {
margin: 0.18rem 0 0;
font-family: var(--font-display);
font-size: 1.35rem;
}
.publication-boundary {
display: grid;
grid-template-columns: 1.15fr 0.85fr;
gap: 2rem;
align-items: start;
margin-top: 1rem;
padding: clamp(1.4rem, 3vw, 1.9rem);
border-radius: var(--radius-lg);
color: var(--white);
background: var(--ink-950);
}
.publication-boundary p:last-child {
margin: 0;
color: #cad7d5;
font-size: 0.82rem;
}
.publication-boundary .eyebrow {
color: #9bd4d1;
}
.locked-panel {
display: flex;
gap: 1.2rem;
@@ -1242,6 +1411,8 @@ fieldset:disabled .field select {
.dashboard-grid--wide,
.explorer-layout,
.split-section,
.model-comparison,
.publication-boundary,
.privacy-panel {
grid-template-columns: 1fr;
}
@@ -1260,10 +1431,26 @@ fieldset:disabled .field select {
}
@media (max-width: 660px) {
.preview-banner {
align-items: flex-start;
justify-content: flex-start;
text-align: left;
}
.preview-banner__icon {
flex: 0 0 auto;
}
.site-header__inner {
min-height: 68px;
}
.page-shell {
width: auto;
max-width: calc(100% - 2rem);
margin-inline: 1rem;
}
.header-status,
.brand__tagline {
display: none;
@@ -1286,6 +1473,21 @@ fieldset:disabled .field select {
display: block;
}
.scope-strip {
align-items: flex-start;
flex-direction: column;
}
.lede,
.data-stamp,
.scope-strip,
.kpi-card,
.model-card,
.panel {
max-width: 100%;
overflow-wrap: anywhere;
}
.data-stamp {
width: 100%;
margin-top: 1.3rem;
@@ -1296,10 +1498,15 @@ fieldset:disabled .field select {
.method-grid,
.cohort-cards,
.form-grid,
.method-preview {
.method-preview,
.metric-list {
grid-template-columns: 1fr;
}
.model-card > p {
min-height: 0;
}
.kpi-card {
min-height: 130px;
}
+109 -13
View File
@@ -52,13 +52,25 @@ function filesRecursively(directory) {
});
}
function viewSource(html, route) {
const marker = `<section class="view" id="view-${route}"`;
const start = html.indexOf(marker);
if (start < 0) return "";
const remaining = html.slice(start + marker.length);
const nextView = remaining.indexOf('<section class="view" id="view-');
const mainEnd = remaining.indexOf("</main>");
const candidates = [nextView, mainEnd].filter((index) => index >= 0);
const end = candidates.length ? Math.min(...candidates) : remaining.length;
return html.slice(start, start + marker.length + end);
}
test("static shell exposes four semantic navigable views", () => {
const html = readFileSync(path.join(DASHBOARD_ROOT, "index.html"), "utf8");
assert.match(html, /<header\b/);
assert.match(html, /<nav\b[^>]*aria-label="Dashboard views"/);
assert.match(html, /<main\b/);
assert.match(html, /<footer\b/);
for (const route of ["overview", "reliability", "estimator", "methods"]) {
for (const route of ["overview", "cohorts", "model", "methods"]) {
assert.match(html, new RegExp(`data-route="${route}"`));
assert.match(html, new RegExp(`data-view="${route}"`));
}
@@ -67,20 +79,45 @@ test("static shell exposes four semantic navigable views", () => {
assert.doesNotMatch(html, /<strong>Locked test<\/strong>|<small>Final evaluation<\/small>/);
});
test("estimator remains disabled and contains no identifying input", () => {
test("narrow layouts constrain body content while preserving local nav scrolling", () => {
const css = readFileSync(path.join(DASHBOARD_ROOT, "styles.css"), "utf8");
assert.match(css, /html\s*{[^}]*max-width:\s*100%;[^}]*overflow-x:\s*clip;/s);
assert.match(css, /body\s*{[^}]*max-width:\s*100%;[^}]*overflow-x:\s*clip;/s);
assert.match(css, /\.primary-nav__inner\s*{[^}]*overflow-x:\s*auto;/s);
const mobile = css.match(/@media \(max-width: 660px\)\s*{[\s\S]*?(?=@media|$)/)?.[0];
assert.ok(mobile, "mobile breakpoint is present");
assert.match(mobile, /\.page-shell\s*{[^}]*width:\s*auto;[^}]*max-width:\s*calc\(100% - 2rem\);/s);
assert.match(mobile, /\.preview-banner\s*{[^}]*justify-content:\s*flex-start;/s);
assert.match(mobile, /overflow-wrap:\s*anywhere;/);
});
test("model view declares the final model, benchmark role, and publication boundary", () => {
const html = readFileSync(path.join(DASHBOARD_ROOT, "index.html"), "utf8");
const estimator = html.match(/<section class="view" id="view-estimator"[\s\S]*?<\/section>/)?.[0];
assert.ok(estimator, "estimator section is present");
assert.match(estimator, /<fieldset disabled>/);
const controlAttributes = [...estimator.matchAll(/<(?:input|select|textarea)\b([^>]*)>/g)].map(
(match) => match[1],
);
for (const attributes of controlAttributes) {
assert.doesNotMatch(
attributes,
/(?:name|id)\s*=\s*["'][^"']*(?:vin|plate|address|station|free.?text|zip)[^"']*["']/i,
);
const modelView = viewSource(html, "model");
assert.ok(modelView, "model section is present");
assert.match(modelView, /Calibrated logistic regression/);
assert.match(modelView, /Final prototype/);
assert.match(modelView, /Calibrated boosted tree/);
assert.match(modelView, /Benchmark only/);
assert.match(modelView, /partition used to fit Platt calibration/i);
assert.match(modelView, /not independent final-performance estimates/i);
assert.match(modelView, /No vehicle-level prediction service/);
assert.doesNotMatch(modelView, /<(?:input|select|textarea|form)\b/i);
assert.doesNotMatch(modelView, /prediction_lookup|planned tool|future approved output/i);
});
test("every result-facing view visibly labels the sample and non-population scope", () => {
const html = readFileSync(path.join(DASHBOARD_ROOT, "index.html"), "utf8");
assert.match(html, /Private 10,000-vehicle development sample/);
for (const route of ["overview", "cohorts", "model", "methods"]) {
const view = viewSource(html, route);
assert.ok(view, `${route} view is present`);
assert.match(view, /scope-strip/);
assert.match(view, /sample/i);
assert.match(view, /not[^.]{0,80}population|population estimate/i);
}
assert.match(html, /not a county outcome ranking/i);
assert.match(html, /not a reliability ranking or population comparison/i);
});
test("all required generated assets satisfy the browser contract", () => {
@@ -91,6 +128,12 @@ test("all required generated assets satisfy the browser contract", () => {
assert.equal(validated.manifest.definitions.locked_test_metrics_published, false);
assert.match(validated.manifest.release_id, /^[0-9a-f]{64}$/);
assert.ok(validated.manifest.model_versions.length > 0);
assert.ok(validated.diagnostics.rows.some(
(row) => row.model === "logistic_platt" && row.partition === "calibrate",
));
assert.ok(validated.diagnostics.rows.some(
(row) => row.model === "hist_gradient_boosting_platt" && row.partition === "calibrate",
));
assert.deepEqual(
validated.filters.partitions,
["train", "tune", "calibrate"],
@@ -151,10 +194,37 @@ test("contract fails closed for missing, inconsistent, or sensitive data", () =>
inconsistent.ageRisk.population_estimate_allowed = true;
assert.throws(() => validateAssetSet(inconsistent), DataContractError);
const populationClaim = assetSet();
populationClaim.manifest.population_estimate_allowed = true;
assert.throws(() => validateAssetSet(populationClaim), DataContractError);
const broadenedTarget = assetSet();
broadenedTarget.manifest.definitions.target = "any inspection outcome";
assert.throws(() => validateAssetSet(broadenedTarget), DataContractError);
const sensitive = assetSet();
sensitive.scorecards.rows[0].vehicle_token = "not-public";
assert.throws(() => validateAssetSet(sensitive), DataContractError);
for (const deniedField of [
"vin",
"plate",
"zip",
"station",
"technician_id",
"inspector_id",
"row_prediction",
"probability",
"raw_json",
"credential",
"password",
"secret",
]) {
const denied = assetSet();
denied.overview.rows[0][deniedField] = "not-public";
assert.throws(() => validateAssetSet(denied), DataContractError, deniedField);
}
const extraRowField = assetSet();
extraRowField.overview.rows[0].note = "unapproved";
assert.throws(() => validateAssetSet(extraRowField), DataContractError);
@@ -167,6 +237,20 @@ test("contract fails closed for missing, inconsistent, or sensitive data", () =>
extraPartition.filters.partitions.push("locked_test");
assert.throws(() => validateAssetSet(extraPartition), DataContractError);
const missingFinalDiagnostic = assetSet();
missingFinalDiagnostic.diagnostics.rows = missingFinalDiagnostic.diagnostics.rows.filter(
(row) => !(row.model === "logistic_platt" && row.partition === "calibrate"),
);
assert.throws(() => validateAssetSet(missingFinalDiagnostic), DataContractError);
const duplicatedBenchmarkDiagnostic = assetSet();
duplicatedBenchmarkDiagnostic.diagnostics.rows.push({
...duplicatedBenchmarkDiagnostic.diagnostics.rows.find(
(row) => row.model === "hist_gradient_boosting_platt" && row.partition === "calibrate",
),
});
assert.throws(() => validateAssetSet(duplicatedBenchmarkDiagnostic), DataContractError);
const unsortedVersions = assetSet();
unsortedVersions.manifest.model_versions = ["z_v1", "a_v1"];
assert.throws(() => validateAssetSet(unsortedVersions), DataContractError);
@@ -194,3 +278,15 @@ test("dashboard source has no external runtime dependency or credential marker",
assert.doesNotMatch(source, /<script[^>]+src=["']https?:|@import\s+url\(["']?https?:/i);
}
});
test("public data directory contains only the approved suppressed aggregate bundle", () => {
const expected = Object.values(REQUIRED_ASSETS).sort();
const observed = readdirSync(PUBLIC_DATA).sort();
assert.deepEqual(observed, expected);
const serialized = observed
.map((filename) => readFileSync(path.join(PUBLIC_DATA, filename), "utf8"))
.join("\n");
assert.doesNotMatch(serialized, /\b[A-HJ-NPR-Z0-9]{17}\b/);
assert.doesNotMatch(serialized, /(?:postgres(?:ql)?:\/\/|PGPASSWORD|BEGIN [A-Z ]*PRIVATE KEY)/i);
});