From 28879665d906abad1aa79ddd4ef0aefde7d5a55d Mon Sep 17 00:00:00 2001 From: offalexjackson777-stack Date: Wed, 22 Jul 2026 22:11:53 +0400 Subject: [PATCH 01/67] Add generation-bound supervised lighting qualification --- apps/device-qualification/assets/app.css | 2 +- apps/device-qualification/assets/app.js | 27 +- apps/device-qualification/assets/contract.js | 20 +- .../local-qualification-view.schema.json | 4 +- .../tests/fixtures/rust-qualified-mouse.json | 34 +- .../tests/fixtures/rust-qualified-pair.json | 68 +- crates/hfx-ops/src/lib.rs | 2 + crates/hfx-ops/src/probe.rs | 2 +- crates/hfx-ops/src/qualification.rs | 280 +++- crates/hfx-ops/src/qualification_runner.rs | 1224 +++++++++++++++++ crates/hfx-ops/src/qualification_server.rs | 585 +++++++- crates/hfx-ops/src/qualification_state.rs | 310 +++++ tests/test_device_qualification_console.py | 15 + tools/hfxdev/package_pipeline.py | 1 + 14 files changed, 2410 insertions(+), 164 deletions(-) create mode 100644 crates/hfx-ops/src/qualification_runner.rs create mode 100644 crates/hfx-ops/src/qualification_state.rs diff --git a/apps/device-qualification/assets/app.css b/apps/device-qualification/assets/app.css index 364b2d9..7bdadf1 100644 --- a/apps/device-qualification/assets/app.css +++ b/apps/device-qualification/assets/app.css @@ -699,7 +699,7 @@ code, text-align: center; } -@media (max-width: 1080px) { +@media (max-width: 1200px) { .qualification-layout { grid-template-columns: 1fr; } .inspector { position: static; } .identity-strip { grid-template-columns: 1fr 1fr; row-gap: 15px; } diff --git a/apps/device-qualification/assets/app.js b/apps/device-qualification/assets/app.js index f0995e0..b816b71 100644 --- a/apps/device-qualification/assets/app.js +++ b/apps/device-qualification/assets/app.js @@ -9,7 +9,7 @@ const refreshButton = document.querySelector("#refresh-view"); const state = { view: null, - selectedDeviceId: null, + selectedDeviceKey: null, selectedStageId: null, busyActionId: null, loading: true, @@ -20,9 +20,9 @@ const state = { refreshButton.addEventListener("click", () => refreshView(true)); document.addEventListener("click", (event) => { - const deviceButton = event.target.closest("[data-device-id]"); + const deviceButton = event.target.closest("[data-device-key]"); if (deviceButton) { - state.selectedDeviceId = deviceButton.dataset.deviceId; + state.selectedDeviceKey = deviceButton.dataset.deviceKey; state.selectedStageId = null; state.hasDraft = false; render(); @@ -149,7 +149,7 @@ async function invokeStage(form) { async function fetchWithTimeout(path, options) { const controller = new AbortController(); - const timeout = window.setTimeout(() => controller.abort(), 6_000); + const timeout = window.setTimeout(() => controller.abort(), 15_000); try { return await fetch(path, { credentials: "same-origin", @@ -163,8 +163,8 @@ async function fetchWithTimeout(path, options) { function reconcileSelection() { const contexts = deviceContexts(); - if (!contexts.some(({ device }) => device.device_id === state.selectedDeviceId)) { - state.selectedDeviceId = contexts[0]?.device.device_id ?? null; + if (!contexts.some((context) => context.key === state.selectedDeviceKey)) { + state.selectedDeviceKey = contexts[0]?.key ?? null; state.selectedStageId = null; state.hasDraft = false; } @@ -208,10 +208,10 @@ function renderDeviceRail(contexts) {