fix(polygon): focus runs on operator playback
This commit is contained in:
parent
b037076506
commit
2e5c4ba1c5
|
|
@ -1244,7 +1244,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 2.4rem;
|
height: 2.4rem;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
gap: 1px;
|
gap: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin: 0 0.65rem;
|
margin: 0 0.65rem;
|
||||||
border-radius: 0.42rem;
|
border-radius: 0.42rem;
|
||||||
|
|
@ -1253,8 +1253,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.polygon-review-timeline button {
|
.polygon-review-timeline button {
|
||||||
min-width: 1px;
|
min-width: 0;
|
||||||
flex: 1 1 1px;
|
flex: 1 1 0;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
|
|
@ -1266,9 +1266,8 @@
|
||||||
|
|
||||||
.polygon-review-timeline button:hover,
|
.polygon-review-timeline button:hover,
|
||||||
.polygon-review-timeline button[data-active="true"] {
|
.polygon-review-timeline button[data-active="true"] {
|
||||||
min-width: 3px;
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
box-shadow: 0 0 0 1px rgb(255 255 255 / 0.86);
|
box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.95);
|
||||||
}
|
}
|
||||||
|
|
||||||
.polygon-review-timeline-note {
|
.polygon-review-timeline-note {
|
||||||
|
|
|
||||||
|
|
@ -88,17 +88,17 @@ function frameColors(
|
||||||
setRgb(
|
setRgb(
|
||||||
colors,
|
colors,
|
||||||
offset,
|
offset,
|
||||||
groundTruth ? 0.78 : 0.29,
|
groundTruth ? 0.76 : 0.29,
|
||||||
groundTruth ? 0.9 : 0.33,
|
groundTruth ? 0.86 : 0.33,
|
||||||
groundTruth ? 0.42 : 0.38,
|
groundTruth ? 0.52 : 0.36,
|
||||||
);
|
);
|
||||||
} else if (mode === "current") {
|
} else if (mode === "current") {
|
||||||
setRgb(
|
setRgb(
|
||||||
colors,
|
colors,
|
||||||
offset,
|
offset,
|
||||||
current ? 0.73 : 0.29,
|
current ? 0.88 : 0.29,
|
||||||
current ? 1 : 0.36,
|
current ? 0.68 : 0.34,
|
||||||
current ? 0.29 : 0.43,
|
current ? 0.28 : 0.37,
|
||||||
);
|
);
|
||||||
} else if (mode === "candidate") {
|
} else if (mode === "candidate") {
|
||||||
if (!candidateAssigned) {
|
if (!candidateAssigned) {
|
||||||
|
|
@ -107,9 +107,9 @@ function frameColors(
|
||||||
setRgb(
|
setRgb(
|
||||||
colors,
|
colors,
|
||||||
offset,
|
offset,
|
||||||
candidate ? 0.24 : 0.29,
|
candidate ? 0.78 : 0.29,
|
||||||
candidate ? 0.84 : 0.36,
|
candidate ? 0.91 : 0.34,
|
||||||
candidate ? 1 : 0.43,
|
candidate ? 0.48 : 0.37,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (mode === "disagreement") {
|
} else if (mode === "disagreement") {
|
||||||
|
|
@ -131,7 +131,7 @@ function frameColors(
|
||||||
disagreement ? 0.22 : 0.35,
|
disagreement ? 0.22 : 0.35,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
setRgb(colors, offset, candidate ? 0.24 : 0.29, candidate ? 0.84 : 0.36, 0.43);
|
setRgb(colors, offset, candidate ? 0.78 : 0.29, candidate ? 0.91 : 0.34, 0.4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return colors;
|
return colors;
|
||||||
|
|
|
||||||
|
|
@ -430,6 +430,41 @@ export function PolygonRunWorkspace({ route }: PolygonRunWorkspaceProps) {
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
<div className="polygon-review-controls">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
aria-label={playing ? "Пауза" : "Воспроизвести"}
|
||||||
|
onClick={() => setPlaying((value) => !value)}
|
||||||
|
>
|
||||||
|
{playing ? "Пауза" : "Play"}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
aria-label="Предыдущий кадр"
|
||||||
|
disabled={selectedPosition <= 0}
|
||||||
|
onClick={() => selectPosition(selectedPosition - 1)}
|
||||||
|
>
|
||||||
|
←
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
aria-label="Следующий кадр"
|
||||||
|
disabled={selectedPosition >= orderedFrames.length - 1}
|
||||||
|
onClick={() => selectPosition(selectedPosition + 1)}
|
||||||
|
>
|
||||||
|
→
|
||||||
|
</button>
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
min={0}
|
||||||
|
max={Math.max(0, orderedFrames.length - 1)}
|
||||||
|
value={selectedPosition}
|
||||||
|
aria-label="Позиция в записи"
|
||||||
|
onChange={(event) => selectPosition(Number(event.target.value))}
|
||||||
|
/>
|
||||||
|
<span>{selectedPosition + 1} / {orderedFrames.length}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="polygon-review-stage">
|
<div className="polygon-review-stage">
|
||||||
{scene ? (
|
{scene ? (
|
||||||
<LidarGroundPointCloud frame={scene} mode={reviewMode} />
|
<LidarGroundPointCloud frame={scene} mode={reviewMode} />
|
||||||
|
|
@ -469,41 +504,6 @@ export function PolygonRunWorkspace({ route }: PolygonRunWorkspaceProps) {
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<div className="polygon-review-controls">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
aria-label={playing ? "Пауза" : "Воспроизвести"}
|
|
||||||
onClick={() => setPlaying((value) => !value)}
|
|
||||||
>
|
|
||||||
{playing ? "Пауза" : "Play"}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
aria-label="Предыдущий кадр"
|
|
||||||
disabled={selectedPosition <= 0}
|
|
||||||
onClick={() => selectPosition(selectedPosition - 1)}
|
|
||||||
>
|
|
||||||
←
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
aria-label="Следующий кадр"
|
|
||||||
disabled={selectedPosition >= orderedFrames.length - 1}
|
|
||||||
onClick={() => selectPosition(selectedPosition + 1)}
|
|
||||||
>
|
|
||||||
→
|
|
||||||
</button>
|
|
||||||
<input
|
|
||||||
type="range"
|
|
||||||
min={0}
|
|
||||||
max={Math.max(0, orderedFrames.length - 1)}
|
|
||||||
value={selectedPosition}
|
|
||||||
aria-label="Позиция в записи"
|
|
||||||
onChange={(event) => selectPosition(Number(event.target.value))}
|
|
||||||
/>
|
|
||||||
<span>{selectedPosition + 1} / {orderedFrames.length}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="polygon-review-order">
|
<div className="polygon-review-order">
|
||||||
<div>
|
<div>
|
||||||
{orderLabels.map(([order, label]) => (
|
{orderLabels.map(([order, label]) => (
|
||||||
|
|
|
||||||
|
|
@ -847,7 +847,8 @@ show an explicit offline state.
|
||||||
|
|
||||||
The Polygon navigation is task-oriented:
|
The Polygon navigation is task-oriented:
|
||||||
|
|
||||||
- `Прогоны` owns live simulation lifecycle, history and qualification evidence;
|
- `Прогоны` owns recorded replay/simulation history and operator review of
|
||||||
|
qualification evidence;
|
||||||
- `Датасеты` owns admitted replay inputs and the entry point for a future
|
- `Датасеты` owns admitted replay inputs and the entry point for a future
|
||||||
qualification run.
|
qualification run.
|
||||||
|
|
||||||
|
|
@ -857,16 +858,27 @@ remain under Fleet. A dataset preview is not a qualification result; the
|
||||||
result is a versioned run with pipeline, sensor profile, metrics, provenance
|
result is a versioned run with pipeline, sensor profile, metrics, provenance
|
||||||
and decision.
|
and decision.
|
||||||
|
|
||||||
|
Live simulation is a different operator task. A rover scene, worker health and
|
||||||
|
start/control actions must not be mounted above an archived dataset replay.
|
||||||
|
They require a separate live surface that appears only when the Simulation
|
||||||
|
Worker capability is available. The current `Прогоны` implementation is
|
||||||
|
read-only and remains fully usable while that worker is offline.
|
||||||
|
|
||||||
### UI-0 — early read-only run view
|
### UI-0 — early read-only run view
|
||||||
|
|
||||||
Target S1B proves persisted start/health/stop history. UI-0 is implemented as a
|
Target S1B proves persisted start/health/stop history. The first direct
|
||||||
direct internal run view and exposes:
|
developer-oriented view exposed lifecycle, events, provider pins and artifact
|
||||||
|
hashes on the main screen. Operator review rejected that composition. The
|
||||||
|
current UI-0 makes the recorded evidence primary:
|
||||||
|
|
||||||
- run identity, lifecycle and terminal reason;
|
- full source-order frame index instead of five selected examples;
|
||||||
- authoritative clock and current pause/running state;
|
- Play/pause, previous/next and arbitrary scrub;
|
||||||
- provider health/process ownership;
|
- direct ground-truth, Current, Patchwork++ and error-map modes;
|
||||||
- qualification events and admitted artifact links;
|
- current-frame metrics and a clickable all-frame improvement/regression strip;
|
||||||
- explicit limitations and gate status.
|
- automatic best/worst ordering without preventing arbitrary manual selection;
|
||||||
|
- aggregate comparison after, rather than before, visual evidence;
|
||||||
|
- acceptance/degradation and run identity/provider/artifact details collapsed
|
||||||
|
below the operator workflow.
|
||||||
|
|
||||||
The backend reads the configured repository from
|
The backend reads the configured repository from
|
||||||
`MISSIONCORE_POLYGON_RUNS_ROOT` using
|
`MISSIONCORE_POLYGON_RUNS_ROOT` using
|
||||||
|
|
@ -875,12 +887,17 @@ The backend reads the configured repository from
|
||||||
```text
|
```text
|
||||||
GET /api/v1/polygon/runs
|
GET /api/v1/polygon/runs
|
||||||
GET /api/v1/polygon/runs/{run-id}
|
GET /api/v1/polygon/runs/{run-id}
|
||||||
|
GET /api/v1/polygon/runs/{run-id}/qualification
|
||||||
|
GET /api/v1/polygon/runs/{run-id}/qualification/review
|
||||||
|
GET /api/v1/polygon/runs/{run-id}/qualification/review/frames/{frame-id}
|
||||||
```
|
```
|
||||||
|
|
||||||
Missing/malformed configuration returns `503`; invalid or corrupt evidence
|
Missing/malformed configuration returns `503`; invalid or corrupt evidence
|
||||||
fails closed. Responses never contain the configured root, artifact bytes or
|
fails closed. The review index exposes no paths or digests. A selected frame is
|
||||||
command payloads. `?workspace=polygon-run[&run=<run-id>]` remains a compatible
|
verified against the content-bound review manifest before its bounded point
|
||||||
deep-link into the same product workspace.
|
preview is decoded. Responses never contain the configured root, raw dataset
|
||||||
|
bytes or command payloads. `?workspace=polygon-run[&run=<run-id>]` remains a
|
||||||
|
compatible deep-link into the same product workspace.
|
||||||
|
|
||||||
UI-0 itself has no PX4 transport or lifecycle/command operations. It remains a
|
UI-0 itself has no PX4 transport or lifecycle/command operations. It remains a
|
||||||
read-only evidence contract backed by the server-owned run repository and is
|
read-only evidence contract backed by the server-owned run repository and is
|
||||||
|
|
|
||||||
|
|
@ -312,7 +312,7 @@ points (required `>= 7`), natural-ground recall gain is `24.46` points
|
||||||
(required `>= 90%`), assigned fraction is `100%` (required `>= 99.9%`) and
|
(required `>= 90%`), assigned fraction is `100%` (required `>= 99.9%`) and
|
||||||
p95 latency is `23.40 ms` (required `<= 50 ms`). No frame exceeded the
|
p95 latency is `23.40 ms` (required `<= 50 ms`). No frame exceeded the
|
||||||
catastrophic regression boundary of `-20` Ground-IoU points; the worst observed
|
catastrophic regression boundary of `-20` Ground-IoU points; the worst observed
|
||||||
delta was `-13.51` points and remains visible as a bounded failure preview.
|
delta was `-13.51` points.
|
||||||
|
|
||||||
| Deterministic profile | Source coverage | Ground IoU | Natural recall | p95 latency |
|
| Deterministic profile | Source coverage | Ground IoU | Natural recall | p95 latency |
|
||||||
| --- | ---: | ---: | ---: | ---: |
|
| --- | ---: | ---: | ---: | ---: |
|
||||||
|
|
@ -327,9 +327,28 @@ delta was `-13.51` points and remains visible as a bounded failure preview.
|
||||||
Every degradation profile stayed inside the predeclared `15`-point Ground-IoU
|
Every degradation profile stayed inside the predeclared `15`-point Ground-IoU
|
||||||
loss, `20`-point natural-recall loss and `50 ms` p95 limits. The sealed
|
loss, `20`-point natural-recall loss and `50 ms` p95 limits. The sealed
|
||||||
decision is therefore `shadow-candidate`, explicitly
|
decision is therefore `shadow-candidate`, explicitly
|
||||||
`promoted_to_navigation_or_safety = false`. The compact `12 MB` run journal,
|
`promoted_to_navigation_or_safety = false`.
|
||||||
report and five worst-frame previews are available in `Polygon -> Runs`; the
|
|
||||||
source archive and crash-resume frame cache remain only on worker D.
|
The first aggregate-only Polygon screen was rejected as an operator product:
|
||||||
|
five selected failure previews could not let an operator verify the decision.
|
||||||
|
Commit `b037076` adds a separate immutable visual derivative bound to the
|
||||||
|
qualification report:
|
||||||
|
|
||||||
|
- schema `missioncore.goose-ground-review-pack/v1`;
|
||||||
|
- identity
|
||||||
|
`6ddc2de0154196088049b8654d9187fba0c52e28783e5ada6be5266dd16bd641`;
|
||||||
|
- all `961/961` validation frames in source order;
|
||||||
|
- deterministic `12,000`-point frame previews with remission, public
|
||||||
|
point-aligned ground truth, Current and Patchwork++ masks;
|
||||||
|
- compact size `67 MB`, versus multiple gigabytes for equivalent JSON;
|
||||||
|
- `Play`, frame scrubber, a clickable all-frame delta strip, best/worst
|
||||||
|
ordering and direct modes for geometry, ground truth, both providers and
|
||||||
|
both error maps.
|
||||||
|
|
||||||
|
The source archive and full-resolution crash-resume cache remain only on worker
|
||||||
|
D. A bounded review mirror may be served by a Mission Core backend, but SSH is
|
||||||
|
not a product data plane. The replay UI does not render the unrelated live
|
||||||
|
Ackermann rover and does not depend on Simulation Worker online status.
|
||||||
|
|
||||||
Primary source evidence:
|
Primary source evidence:
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue