feat(perception): qualify E31 source binding

This commit is contained in:
DCCONSTRUCTIONS
2026-07-27 11:33:07 +03:00
parent 001d597a89
commit 091d560ac8
8 changed files with 1819 additions and 14 deletions
@@ -0,0 +1,144 @@
# LAB E31 — source-time, calibration and self-mask qualification
Date: 2026-07-27
Status: accepted for diagnostic E32 binding; navigation, safety and command
authority are false
Immutable result:
`e31-source-qualification-b2460a5eb143688c7eea6821b2277e13aea79868abe81d83f7e78548c119159a`
## Decision under test
E31 tests whether the exact RAVNOVES00 source binding used by E29/E30 is
specific and stable enough to enter E32 full replay. It does not tune E29,
infer a raw LiDAR firing timestamp, invent a sensor height or promote the
result to navigation.
The gate is fail closed. It requires:
1. the immutable A3 engineering generation and complete human-exception
generation;
2. complete accounting of source-time bindings;
3. the exact factory KB4 calibration identity;
4. a predeclared offset sweep over evidenced camera↔LiDAR correspondences;
5. a source-evidenced self-mask decision that cannot silently erase accepted
object evidence.
## Immutable input chain
- Source pack:
`e10-lidar-pack-576c994a6c814e2592dd6240ace3902a5db94843312c759a73ba0c9166157d2b`.
- Local-surface model:
`k1-local-surface-23762244c8bdb97de26fb721ac957d7a00bc9a63571ac4cfa4be19c4effc7d55`.
- E30 materialization:
`e30-materialization-841af926d8d28ab93538c46d8f31278a2234c4d1c12c7dc4dc296b249d59735a`.
- A3 engineering generation:
`e30-engineering-generation-62a4fea10dea9b77f69ceac1af5bf0e4928d9c7716083c22258a03670fe5bd4f`.
- A3 human generation:
`e30-review-generation-7982a882558d0be690b4c7092e328c080bfcbf52478a220452be7e887a588250`.
- Factory calibration content identity:
`05f3ad9b38b3a4fc95388a8ec83da83c745e217709e51787b3d5aad0969f6fa9`.
- Camera binding: `sensor.camera.right`, `camera_1`, KB4, `800×600`.
Every input result and artifact digest is verified before E31 runs. Existing
results are re-opened only when the complete content identity matches.
## Source-time audit
The recording has 4,489 source frames. LiDAR/pose bindings exist for 3,928
frames (`87.50%`); 561 unavailable frames remain explicitly unavailable.
| timing relation | absolute p50 | absolute p95 | maximum | declared gate |
| --- | ---: | ---: | ---: | ---: |
| LiDAR ↔ camera | 25.303 ms | 70.789 ms | 99.574 ms | 100 ms |
| pose ↔ point cloud | 6.784 ms | 21.854 ms | 73.798 ms | 100 ms |
These are recorded host-monotonic arrival relations. They are not hardware
firing timestamps.
## Offset sensitivity
The correspondence set contains 87 A3 items where the projection was
`aligned`, point ownership was `object` and the E29 item was semantic. For each
hypothesis E31 selects the nearest available recorded cloud, projects it with
the camera-frame pose and evaluates occupied support inside the exact E29 bbox
inset (`0.03`).
| host-arrival hypothesis | evaluable | supported | supported fraction |
| ---: | ---: | ---: | ---: |
| -200 ms | 79 | 63 | 79.75% |
| -150 ms | 83 | 66 | 79.52% |
| -100 ms | 81 | 70 | 86.42% |
| -50 ms | 87 | 78 | 89.66% |
| 0 ms | 87 | 87 | 100.00% |
| +50 ms | 87 | 80 | 91.95% |
| +100 ms | 83 | 69 | 83.13% |
| +150 ms | 84 | 71 | 84.52% |
| +200 ms | 83 | 69 | 83.13% |
Zero offset is retained. Its support deficit relative to the best hypothesis is
zero. The p95 bbox support-centroid residual is `0.3947` bbox diagonals. This is
a diagnostic correspondence residual, not a measured calibration-target
residual.
## Calibration and self-mask result
The loaded factory calibration exactly matches the source-pack identity. The
camera-from-LiDAR rotation determinant is `1.00000037`, the maximum
orthonormal residual is `5.30e-7`, and translation norm is `0.1005 m`.
Eight A3 `self_points` person cases support a source-session semantic exclusion
rectangle:
```text
normalized xyxy = [0.191196, 0.730990, 0.625038, 0.992935]
application = person bbox centre inside rectangle
observed collateral = 0
```
This mask is bound to the source session and detector rule. It is not a
transferable physical vehicle mask.
The two geometry `self_points` cases cannot support a generic projected point
mask. Their union would erase 69 selected points from one accepted object and
22 from another. E31 therefore rejects that mask and retains the two exact A3
correction item identities instead.
Physical body dimensions and a body-frame mount transform are unavailable.
E31 records both as absent; it does not substitute a guessed height.
## Decision
All ten predeclared requirements pass. The result status is
`accepted-diagnostic-source-profile` and `eligible_for_e32=true`.
Acceptance means only:
- E32 may bind this exact source session, factory calibration, zero
host-arrival offset and source-scoped semantic mask;
- unavailable source evidence remains unavailable;
- the unsafe generic geometry mask remains disabled;
- the result has no command, navigation or safety authority.
It does not establish transfer to another physical mount. That remains an E36
question with a second eligible real source.
## Reproduction
```bash
PYTHONPATH=src .venv/bin/python \
experiments/perception/run_e31_source_qualification.py \
--source-pack .runtime/compute-experiments/e10/lidar-packs/e10-lidar-pack-576c994a6c814e2592dd6240ace3902a5db94843312c759a73ba0c9166157d2b \
--local-surface .runtime/compute-experiments/k1-local-surface-v1/models/k1-local-surface-23762244c8bdb97de26fb721ac957d7a00bc9a63571ac4cfa4be19c4effc7d55 \
--calibration .runtime/mission-core/evidence/sessions/private/device-calibration/20260720T105432Z_k1_factory_calibration_2e0e51ae5485 \
--materialization .runtime/compute-experiments/e30/materializations/e30-materialization-841af926d8d28ab93538c46d8f31278a2234c4d1c12c7dc4dc296b249d59735a \
--engineering-generation .runtime/compute-experiments/e30/engineering-generations/e30-engineering-generation-62a4fea10dea9b77f69ceac1af5bf0e4928d9c7716083c22258a03670fe5bd4f \
--human-generation .runtime/compute-experiments/e30/human-review-generations/e30-review-generation-7982a882558d0be690b4c7092e328c080bfcbf52478a220452be7e887a588250
```
The immutable result contains:
- `qualification-report.json`;
- `binding-profile.json`;
- `offset-sweep.json`;
- 87 per-item correspondence score rows;
- a digest-bound manifest.
@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""Build the immutable LAB E31 source qualification profile."""
from __future__ import annotations
import argparse
import json
from pathlib import Path
from k1link.compute.e31_source_qualification import (
build_e31_source_qualification,
)
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--source-pack", type=Path, required=True)
parser.add_argument("--local-surface", type=Path, required=True)
parser.add_argument("--calibration", type=Path, required=True)
parser.add_argument("--materialization", type=Path, required=True)
parser.add_argument("--engineering-generation", type=Path, required=True)
parser.add_argument("--human-generation", type=Path, required=True)
parser.add_argument(
"--output-root",
type=Path,
default=Path(".runtime/compute-experiments/e31/source-qualifications"),
)
args = parser.parse_args()
result = build_e31_source_qualification(
source_pack_root=args.source_pack,
local_surface_root=args.local_surface,
calibration_snapshot_root=args.calibration,
materialization_root=args.materialization,
engineering_generation_root=args.engineering_generation,
human_generation_root=args.human_generation,
output_root=args.output_root,
)
print(
json.dumps(
{
"result_id": result.result_id,
"result_root": str(result.result_root),
"status": result.report["status"],
"eligible_for_e32": result.report["eligible_for_e32"],
"requirements": result.report["requirements"],
"authority": result.report["authority"],
},
ensure_ascii=False,
indent=2,
)
)
if __name__ == "__main__":
main()