docs(perception): record binary full-graph evidence and remaining gates

This commit is contained in:
DCCONSTRUCTIONS
2026-09-02 14:02:28 +03:00
parent 221e429c0a
commit f6d73d8bfb
3 changed files with 220 additions and 6 deletions
@@ -1563,3 +1563,158 @@ raw parity / timing на новом binary input, без `/source.mp4`.
decoder-выигрыш из этих значений и не объявлять real-time qualification.
Network/controller/recovery/inventory, standalone image и product cutover
остаются открытыми. Этап 2 продолжается; этапы 3–4 не начаты; actuation=false.
## 2026-09-02 13:55 МСК — этап 2, инкремент 5: binary input → полный граф
Код: `221e429`. Эксперимент:
`perception-stage2-binary-graph-worker-20260902T1335MSK`.
Это первый полный pilot на новом бинарном ingress с incremental H.264 decoder,
а не decoder-only timing и не повтор старого producer с готовым MP4.
Источник — тот же ограниченный 128-camera prefix RAVNOVES00, исходный clock 1×.
### Реализация и неизменные границы
- Recording adapter читает только текущие fMP4 fragments и инкременты прежнего
normalized map cloud/pose archive. Init ≤64 KiB, fragment ≤1 MiB; проверяются
path confinement, declared size и SHA текущего fragment. Нет полного source
copy/hash/predecode, конкатенации камеры или ожидания EOF перед результатом.
Существующий normalized archive — recording input, не новая реализация
физического vendor adapter. Source adapter пока находится в diagnostic container.
- Binary ABI v2 связывает поток с StreamStart/lease. Синхронный consumer до RPC
фиксирует camera-time causal cut, pose и rolling/fresh increments. Будущие
sensor events не подставляются. Прежние age/skew/preroll правила перенесены
в общий `streaming_sensors`; legacy pilot использует их же.
- Native PyAV18 decoder — отдельный CPU child под существующим supervisor,
address-space limit 1 GiB, CUDA_VISIBLE_DEVICES пуст. Bounded POSIX RPC:
header ≤4 KiB, input ≤1 MiB, ровно 1,440,000 bytes caller-owned BGR,
deadline 250 ms. Timeout/invalid response/lost lease прекращают поток;
повтор через повреждённое codec state запрещён.
- Header scratch 65,536 bytes и bounded sensor cache 3,216,384 bytes резервируются
заранее. Rolling и fresh ограничены каждый 64k points / 64 events. BGR и
concatenated sensor arrays резервируются **до** allocation; atomic handoff
переносит ownership в mailbox без double charge/неучтённого окна.
При rejection ticket остаётся у caller до удаления его buffers.
Raw reassembly, sensor retention, decoded pending/active учитываются совместно;
native allocations дополнительно ограничены контейнером, а не Python счётчиком.
- Полученный bundle идёт в прежний `GpuStage/JointGraph`: DDRNet-39 GOOSE eager
PyTorch reference + RF-DETR native608×800, online geometry/distances,
temporal/motion, TRAVEL TGS, per-cell aging, coarse materials/advisory policy.
Weights/thresholds/preprocess/TTL 250 ms не менялись. GPU inference последовательный,
overlap разрешён только с CPU tail; pending ≤2, input ≤16 MiB.
Image SHA `664824aa25de1db178f177d67a81b01541a938b479812b9383ddbf03f6b59dbe`,
profile SHA `e1c244e3be8b4880cb4b4d7e96a88feada69773dbb16f5c9a17ef663030138f3`.
8 CPU / 8 GiB, pids 256, shm 256 MiB, read-only/network-none; квоты и GPU clocks/
power limit не менялись. PyAV и модели — явные read-only mounts, не standalone.
Новый private code snapshot создан копированием прежнего **code-only** пакета
и текущих overrides; старые code/evidence пакеты не изменялись. `/source.mp4`
в команде/mounts нового прогона отсутствует. Measured source hashes сохранены.
### Проверки и результат полного прогона
CPU-only oracle до GPU: 32/32 точных bundles против отдельного выполнения
прежнего producer — points, rolling points/times, pose, binding, lineage.
Старый producer в oracle получает synthetic image, модели не запускаются и его
результаты не питают новый граф. Synthetic decoder hang после init превышает
250-ms RPC deadline: ingress failed, cameras 0/1/2 accounted failed, live children=0,
lease/input освобождены, residual=0. Никакой будущий кадр не выдаётся вместо зависшего.
| Полный binary 1× запуск | Результат |
| --- | --- |
| Camera released / completed / dropped / unaccounted | 128 / 128 / 0 / 0 |
| Input observations | 384/384: init 1 + camera 128 + LiDAR 124 + pose 131 |
| Wire bytes / incomplete observations | 17,194,165 / 0 |
| Первый результат от старта source clock | 583.149 ms |
| Результатов до source End | 126/128; два последних штатно дренируются |
| Full source-due → local receiver p50/p95/p99/max | 91.936 / 161.974 / 191.806 / 209.423 ms |
| Source release lag p95/p99/max | 2.375 / 6.263 / 8.823 ms |
| Warmup / stop | 7.073 s / 4.074 s |
| Peak pending / peak accounted input / residual | 1 / 8,883,895 bytes / 0 |
| GPU direct / buffered handoffs | 127 / 1 |
| Peak VRAM / container RAM | 2,367 MiB / 3,314.648 MiB |
| Backlog growth first 8 → last 8 | 4.494 ms, gate ≤25 ms PASS |
| Available causal sensor pairs / fresh complete at receipt | 76 / 74 из 128 |
Исходные 52 missing/stale sensor pairs сохранились. Дополнительно seq106/107
истекли по geometry/motion/costmap/policy к receipt. TTL не увеличен, unknown
не заменён разрешающим состоянием. Source reader прошёл 241/4598 pose rows,
229/4570 point batches, 545,590/10,751,258 xyz rows, включая bounded skipped
prefix/preroll; полного прохода по записи нет. У receiver source duration unknown.
Покадровый `source_release_lag_ms=null`: он неизвестен consumer и не подменяется
временем получения. Реальный source lag измерен отдельно и используется gate.
### Parity и отдельные отрицательные gates
BGR к pinned OpenCV reference — **128/128 exact**. К прежнему raw full-graph
reference (`perception-parity-pacing-worker-20260902T0911MSK/reference-locked-128-e`)
совпали 128/128 segmentation SHA, proposals, tracks, threats, range-estimator
metadata, costmap material, sensor binding/lineage и hash raw TGS costmap states.
Effective costmap/policy после TTL оцениваются отдельно, не объявляются raw parity.
`observations` exact лишь **126/128**. Найдены ровно два leaf-различия:
- seq13 / observation3 / metric_geometry.range_m:
`0.07335595296280842` против `0.07335595296280843`;
- seq29 / observation2 / metric_geometry.range_m:
`0.05775996681929105` против `0.05775996681929106`.
В обоих случаях delta = 1.3877787807814457e-17 m. Это не изменение определения
distance estimator, threshold или source point ownership. Причина численного
drift ещё не воспроизведена; не приписывать её SIMD/alignment без отдельного
контрольного опыта. **Strict raw parity=false**: не добавлен post-hoc tolerance,
не округлены результаты и не переписан reference. Execution/complete accounting
приняты как evidence, но полная bitwise parity и real-time gates не закрыты.
### Где задержка
| Участок, ms | p95 | p99 |
| --- | ---: | ---: |
| Native decode | 10.871 | 11.606 |
| Decoder RPC + bundle assembly | 18.053 | 38.991 |
| Очередь перед GPU | 44.284 | 84.492 |
| DDRNet RPC | 73.068 | 77.479 |
| RF-DETR | 17.776 | 19.476 |
| Online surface | 26.958 | 39.927 |
| CPU tail → receiver | 53.305 | 72.736 |
Эти percentiles не складываются: участки пересекаются и хвосты приходятся на
разные кадры. Auto memory clocks снова падают 10,251→405810 MHz, SM 2610→525930.
По последнему 0.5-s telemetry sample перед receipt high/low cohorts (39/89 кадров)
имеют p95 full latency 88.269/172.079 ms; это **корреляция**, не controlled A/B.
CPU throttled delta=0. Предыдущий полный auto run 124.98/136.12 ms нельзя считать
контролем чистой цены нового транспорта. Не доказаны ни исчерпание 24 GiB VRAM,
ни «5% вычислительной нагрузки»; измеренный peak VRAM не равен GPU utilization.
Общий p95/p99 нового запуска остаётся FAIL 125 ms, без отбора удобной части прогона.
### Review, cleanup и evidence
После полного замера добавлены cleanup при constructor/pre-start failure,
блоки finite-validation по 4096 scalar values для bounded scratch и тихое
завершение CPU-harness heartbeat. Exact reviewed файлы отдельно прошли Worker
CPU-only 32-frame oracle: 32/32 exact, residual 0. Это не второй GPU timing sample;
полный прогон привязан к своему сохранённому code snapshot. Первоначальный
CPU-canary shutdown traceback и ошибочный PowerShell postcheck (array treated
as one object) сохранены; повторные проверки исправлены и PASS.
271 focused tests PASS; Ruff check/format 13 files и mypy 5 common modules PASS.
Mac без моделей, full suite, image build и вторых web-серверов. Один GPU pilot;
четыре согласованных Mission Core сервиса временно остановлены и восстановлены,
Triton первым и ready 200 перед остальными. Ollama/Frigate exited/restart=no.
Owned children, probe containers и временный experiment lease volume удалены
после сохранения terminal owner record; исходники/модели/recordings не удалены.
Triton 200, Mac 8000=200, 8765 closed; GPU auto P8, 978 MiB, 450 W на финальной проверке.
Manifest: `.runtime/perception-stage2-binary-graph-worker-20260902T1335MSK/manifest.json`,
**161 artifacts**, SHA-256
`e224de4e9f2c5fd419fe0fb0b9be6e01ec267628568e104795ae211ec803bce4`.
Включены команды, UTC/monotonic времена, before/quiesced/after inventory,
exact probe snapshot, Worker code inventory, raw scenes/timings, CPU/negative
probes, analysis с leaf diff и final checks. 114 measured probe files verified;
реальные inputs и веса в Git не добавлены.
**Далее в этапе 2:** отдельно воспроизвести/объяснить два numerical differences,
продолжить измеряемую оптимизацию decoder/IPC/queue/CPU-tail при контролируемом
operating envelope; затем production controller/admission/recovery/inventory,
network ingress/egress и самостоятельный image. Старые product LAB/registry/UI
не переключены. Несколько/полная запись, physical source, field quality и
actuation не проверены. Этапы 3–4 не начаты; real-time qualification=false.