diff --git a/docs/OBSERVATORY_REALTIME_PROFILES_EXECPLAN.md b/docs/OBSERVATORY_REALTIME_PROFILES_EXECPLAN.md index 6d864cc..cc5902a 100644 --- a/docs/OBSERVATORY_REALTIME_PROFILES_EXECPLAN.md +++ b/docs/OBSERVATORY_REALTIME_PROFILES_EXECPLAN.md @@ -1,8 +1,8 @@ # Observatory: четыре этапа создания полного real-time Perception-профиля -Дата: 2026-09-01; обновлено 2026-09-02 12:35 МСК. **Этап 1 закрыт; этап 2 в работе. Третий инкремент: bounded binary IPC ingress с неизвестной длиной источника подключён к общему lifecycle/byte budget и проверен на Worker.** 103/103 исходных события доставлены без изменения bytes/metadata; камера, LiDAR и pose поступают до End. Это raw-digest consumer, **не** подключённый к этому ingress полный модельный граф. Real-time qualification НЕ выдана: последнее полное измерение остаётся 128/128, p95/p99 124.98/136.12 ms, fresh 76/128. Далее decoder→full graph bridge, network/controller и standalone image; этапы 3–4 не начаты. Default PyTorch, TensorRT parity не принята. +Дата: 2026-09-01; обновлено 2026-09-02 13:09 МСК. **Этап 1 закрыт; этап 2 в работе. Четвёртый инкремент: persistent H.264 decoder принимает отдельные fMP4-фрагменты без готового MP4, следующих кадров и EOF.** BGR совпадает с прежним декодером на 128/128 кадрах. Бинарный ingress + decoder отдельно проверены на 103 событиях / 32 кадрах. Это ещё **не** полный граф на новом входе. Последнее полное измерение не меняется: 128/128, p95/p99 124.98/136.12 ms, fresh 76/128, real-time qualification НЕ выдана. Далее causal sensor / supervised decoder → full graph bridge, затем controller/network и standalone image; этапы 3–4 не начаты. -Последние изменения: `da60fef` — binary v2 mapping существующих LiveIngressEvent, sender/receiver и общий byte budget; `a937400` — корректная фиксация End в probe; `79ce55d` — regression старого raw-first ingress. 276 focused tests PASS. CPU-only Worker probe: 32 camera frames + init + 34 LiDAR + 36 pose, 4,061,693 wire bytes, peak input 553,991 bytes, residue 0. Межпроцессная доставка до raw callback: p95/p99 0.419/0.601 ms; это не model/network latency. Обрыв между фрагментами не выдал payload и завершил lease. Модели/clocks/старые GPU-сервисы не менялись. Прежние lifecycle/lease/full-graph доказательства сохранены; product batch path пока не переключён. +Последние изменения: `350366b` — общий byte-only MP4 parser и source-neutral decoder; `f102712` — Worker parity/decoded-ingress probes. 246 focused tests PASS, отдельный повтор session/decoder — 55 PASS; Ruff/mypy PASS. В CPU-only 128-frame 1× пробе p95 нового decoder снизился с 19.927 до 11.156 ms после отказа от повторного demux/probing; это сравнение двух новых decoder-вариантов, не ускорение полного профиля. IPC + decode: 103/103 exact raw events, 32/32 exact BGR, peak input 5,727,751 bytes, остаток 0; первый кадр за 3.071 s до End. Модели не запускались, services/clocks не менялись; product batch path пока не переключён. Текущий evidence: [отчёт этапа 1](../experiments/perception/PERCEPTION_STREAM_STAGE1_2026-09-01.md), [ADR 0049](adr/0049-stream-first-perception-profiles.md), [candidate manifest](../config/perception/k1-perception-ddrnet39-rfdetr-tgs-prototype-v1.json). Запрет full-source preload закреплён в новом контракте; старый batch materializer пока не удалён и продуктовый путь не переключён. @@ -143,7 +143,7 @@ IPC/сети, копирований и оркестрации. Вычислит ## Этап 2 — Самостоятельный Docker с полным потоковым вычислением -**Текущий статус, 12:35 МСК:** в работе. Lifecycle/scheduler/per-cell expiry проверены полным пилотом; новый raw binary ingress — отдельным CPU-only межпроцессным probe. Их decoder/full-graph соединение ещё требуется: новый вход пока принимает и проверяет raw события, не рассчитывает модели. Production admission/recovery, continuous GPU inventory/envelope monitoring, binary network ingress и standalone image ещё не закрыты. GO на этап 3 пока нет. +**Текущий статус, 13:09 МСК:** в работе. Lifecycle/scheduler/per-cell expiry проверены полным пилотом; binary ingress и persistent fragment decoder — отдельными CPU-only probes. Новый вход уже выдаёт точные BGR-кадры, но causal cloud/pose binding и передача decoded bundles тому же модельному графу ещё не соединены. Нужны supervised native decoder child, общий ownership/scratch budget при handoff и новый full-graph parity/timing. Production admission/recovery, continuous GPU inventory/envelope monitoring, network ingress и standalone image ещё не закрыты. GO на этап 3 пока нет. **Цель:** один самостоятельный контейнер принимает поток и реально рассчитывает DDRNet, детекции, расстояния, motion, TGS/costmap и policy-shadow; ничего не дорисовывается из старых LAB. @@ -264,7 +264,11 @@ EoMT — семейство ViT-моделей сегментации изобр ## Surprises / открытые вопросы -- IPC probe отделил работоспособность raw ingress от работоспособности decoder/full graph. Наличие codec-init gate не проверяет NAL/IDR, access-unit framing или соответствие fisheye calibration. Оригинальные fMP4 segments не превращаются автоматически в model-ready frames; следующий bounded шаг — persistent incremental decoder и causal sensor adapter с allocation bounds, затем тот же полный граф. Старые model inputs/prepared paths не подмешивать в доказательство нового ingress. +- 2026-09-02 13:09 МСК: decoder-инкремент `350366b` / `f102712` принят отдельно от full graph. Evidence `.runtime/perception-stage2-decoded-ingress-worker-20260902T1310MSK/manifest.json`, 43 artifacts, SHA-256 `612295eabbc8f203ad9b037a69ac9cf97b2df3134b087d9e51a9786738423f2f`; связан с отдельными 128-frame demux/direct/reference manifests. Все 128 BGR совпадают; 32-camera binary canary и negative EOF освобождают весь input budget. Четыре Mission Core сервиса не останавливались; модели и GPU clocks не менялись. + +- Повторный `av.open(init + fragment)` не является полнофайловым барьером, но повторяет stream probing на каждом кадре. Принят узкий проверяемый ISO-BMFF layout → один AVCC sample → persistent codec; NumPy импортируется до readiness. p95 первого потокового варианта 19.927 ms → 11.156 ms. Это bounded CPU A/B без модели; не вычитать выигрыш из старого full-graph p99. Codec/parser не импортируют session store или materializer; archive API сохраняет прежние exception contracts. Иной codec/layout, B-frames, multi-sample, пропуск DTS или не-keyframe start требуют нового явно квалифицированного профиля/epoch, а не скрытого fallback. + +- Новый decoder не делает весь bridge готовым. Следом — causal sensor window на момент camera release, supervised CPU child с bounded RPC и передача владения decoded bundle в общий mailbox, затем тот же полный граф. Не подмешивать готовый MP4 или старые masks/geometry. Реальная калибровка в decode/hash probe не используется: совпадение BGR не доказывает calibration admission или field quality. - 2026-09-02 12:00 МСК: истечение lease и освобождение GPU разделены. После crash OS lock свободен, но durable `active` не допускает новый запуск. Recovery намеренно отсутствует до trusted resource-release proof; нельзя удалять production marker ради продолжения. В пробах удалены только два заведомо временных experiment-owned volume после сохранения записей и остановки всех probe containers. @@ -307,6 +311,6 @@ EoMT — семейство ViT-моделей сегментации изобр ## Outcomes / retrospective -Этап 1 остаётся завершённым engineering baseline. Этап 2 теперь имеет общий scheduler, per-cell expiry, subprocess lifecycle/lease и bounded raw binary IPC. Новый ingress прошёл 103 реальных source events без потерь/изменений и отдельный incomplete-fragment отказ. Общий byte budget удерживает raw/reassembly до выхода callback; старые input owners не освобождаются при cancel. Но raw-digest probe не равен полному графу: следующий шаг соединяет incremental decoder/causal sensor adapter с моделями и повторяет full-profile timing/parity на новом входе. Прежний полный p99 136.12 ms остаётся FAIL 125 ms. Network/gRPC/authentication, production controller/recovery/inventory и standalone image не приняты; старый batch/registry/UI не переключены. Четыре сервиса не останавливались в новом CPU-only probe, Ollama/Frigate остаются выключены. Full suite/image build не запускались; исторические baseline-symlink и NVCR403 вопросы не объявлены исправленными. +Этап 1 остаётся завершённым engineering baseline. Этап 2 имеет общий scheduler, per-cell expiry, subprocess lifecycle/lease, bounded raw binary IPC и persistent fragment decoder. Новый decoder воспроизвёл 128 BGR без отличий и без future-fragment/EOF barrier; combined IPC/decoder — 32 BGR и 103 raw events. Общий byte budget учитывает raw, scratch и retained codec metadata; normal/error cleanup оставляет 0 bytes. Native codec RSS измеряется отдельно, а его изоляция внутри полного GPU runtime ещё нужна. Следующий шаг соединяет causal sensors и supervised decoder с тем же полным графом и повторяет timing/parity. Прежний full-graph p99 136.12 ms остаётся FAIL 125 ms. Network/gRPC/authentication, production controller/recovery/inventory и standalone image не приняты; старый batch/registry/UI не переключены. GPU-прогонов, full suite и image build в этом инкременте не было; сервис 8000 сохранён, Ollama/Frigate выключены. После этапа 4 здесь будут перечислены digest самостоятельного полного образа, измеренные статусы и ошибки по recordings, реально проверенные source/hardware/config комбинации, состояние сохранённого EoMT-варианта и оставшиеся physical-live/quality/vehicle-integration ограничения. Готовый Docker и готовая автономия не отождествляются. diff --git a/docs/adr/0049-stream-first-perception-profiles.md b/docs/adr/0049-stream-first-perception-profiles.md index d45d894..379dc4d 100644 --- a/docs/adr/0049-stream-first-perception-profiles.md +++ b/docs/adr/0049-stream-first-perception-profiles.md @@ -140,6 +140,55 @@ feed incremental decode and causal sensors into the same graph without using the old concatenated camera file or precomputed geometry. Product source queues, backend routes and the browser are not switched by this increment. +Stage-2 decoder increment, 2026-09-02 (`350366b`, `f102712`): +`media_fragments.py` now owns the existing bounded ISO-BMFF timing parser. +Archive inspection delegates to it and preserves `SessionIntegrityError` at its +boundary. `perception.streaming_decoder.FragmentDecoder` imports neither the +session store nor its materializer. It receives only bounded init/fragment bytes; +source filenames, recording length and EOF are not decoder inputs. + +The measured subset is H.264, one 800×600 frame per fragment, one video track, +explicit moof-relative sample size/offset and no composition offset. The first +fragment must be random-access and the decoded frame must be keyframe; DTS must +remain contiguous. One persistent codec context, one slice thread, no flush or +future packet: each call must yield exactly one frame with the same PTS/raster. +Reordered/B-frame streams, unknown layouts, discontinuities and invalid output +fail closed. Other valid camera formats require explicit qualification, not a +hidden decoder fallback. These are current profile bounds, not a new K1 protocol. + +PyAV 18.0.0 is pinned to wheel SHA-256 +`ae56b40b6f8b067a8ad2dac664fbfbabac7f7a55b9a7bb031eb99289252bc017`. +It is installed only into a private experiment target and mounted read-only; +standalone packaging must include it. The public +[PyAV codec documentation](https://pyav.basswood.io/docs/stable/api/codec.html) +describes persistent packet decoding and thread modes; compatibility claims here +come from the actual 18.0.0 Worker probe, not from documentation version alone. + +Sequential CPU-only 128-frame 1× comparisons produced exact BGR parity against +the previous pinned OpenCV decoder. Direct validated sample extraction replaces +opening/demuxing init+fragment on every frame; NumPy loads before readiness. +Measured new-decoder p95 improves 19.927→11.156 ms, p99 20.920→18.675 ms. +This is one bounded component comparison, not a full-profile speedup claim. +The reference full-MP4 path exists only in the separate pixel-comparison process; +neither new decoder path mounts/reads that MP4. + +Combined IPC/decoder probe accepts 103 raw events and yields 32 exact BGR frames. +31 frames arrive before the source End marker; the final frame drains before +the receiver handles End. First decoded callback is 3.071 s before the marker. +Input reservations peak at 5,727,751 bytes including conservative decoder scratch +and init/extradata retention; normal and truncated-input cleanup return to zero. +Native codec/DPB allocations are bounded separately by the CPU-only container; +application payload counters alone do not constrain native allocation or execution +time. Before full-graph integration, native decoding must run in a supervised CPU +child with bounded RPC and explicit decoded-bundle ownership transfer. + +Point/pose are preserved raw in this probe, not causally fused into a scene; +calibration identity is explicitly non-scene/synthetic. No full graph, GPU model, +network, physical live feed, vehicle control or product cutover is qualified. +Next: preserve the existing causal sensor cut at camera release, connect decoded +bundles to the same full graph, then repeat full-profile parity/timing. The last +full-profile p95/p99 remains 124.98/136.12 ms, not this decoder's timing. + Open → validate bounded metadata/claim → warm models → Ready → start the replay clock → observations and incremental scenes → stop/drain bounded state → close and asynchronously seal the immutable receipt. A live source already producing diff --git a/experiments/perception/PERCEPTION_STREAM_STAGE1_2026-09-01.md b/experiments/perception/PERCEPTION_STREAM_STAGE1_2026-09-01.md index 3221309..f718b6a 100644 --- a/experiments/perception/PERCEPTION_STREAM_STAGE1_2026-09-01.md +++ b/experiments/perception/PERCEPTION_STREAM_STAGE1_2026-09-01.md @@ -1454,3 +1454,112 @@ camera decoder и causal sensor adapter, соединяющие этот ingress parity/timing на новом входе. Network/gRPC/authentication, constant controller root/recovery/inventory, standalone image и product cutover остаются открытыми. Этап 2 не закрыт, этапы 3–4 не начаты; actuation=false. + +## 2026-09-02 13:09 МСК — этап 2, инкремент 4: fMP4 → persistent decoder + +Коммиты: `350366b` (общий parser/decoder и synthetic tests), `f102712` +(Worker probes). Это входная часть runtime, **не новый запуск полного графа**. +Никакие модели, thresholds, source clock, GPU clocks или ограничения Docker +полного профиля не менялись. Четыре Mission Core GPU-сервиса не останавливались. + +### Реализованная граница + +Из `sessions.media` выделен прежний bounded ISO-BMFF timing parser; archive API +сохраняет свои исключения. Новый decoder не импортирует session store/materializer, +не принимает path, frame count, duration или EOF. Принимает init ≤64 KiB и один +полный fMP4 fragment ≤1 MiB. Объявленный subset: H.264, один video track, native +800×600, один sample, явные moof-relative offset/size, без composition offset. +Первый fragment/frame проверяется как random-access/keyframe; DTS непрерывен. +Persistent codec выдаёт ровно один BGR кадр текущего PTS, без flush и будущих +пакетов. B-frames, иной layout/raster, multi-sample, пропуск или неоднозначный +native output завершают decoder; молчаливого fallback нет. + +Первый рабочий вариант открывал `init + current fragment` через PyAV на каждом +кадре. Полнофайлового барьера уже не было, но повторялся stream probing. +В принятом варианте bounded parser проверяет размер/offset единственного AVCC +sample и передаёт его непосредственно persistent H.264 codec. NumPy импортируется +до readiness. Только init открывается через PyAV; decode не читает файлов. + +### Worker CPU-only A/B и отдельный reference + +Эксперименты `perception-stage2-decoder-worker-20260902T1250MSK` и +`perception-stage2-decoder-direct-worker-20260902T1305MSK`: последовательно, +network none, без GPU request, 2 CPU / 512 MiB; process address-space limit 1 GiB. +Исходные 128 fMP4 fragments поступают по одному на исходном 1× времени. +Старая готовая MP4 используется **только** отдельным reference-процессом OpenCV +для сравнения пикселей, не монтируется в новые decoder-прогоны. Reference не +paced, поэтому его throughput не сравнивается с новым source-paced latency. + +| Метрика, ms | Первый per-fragment demux | Direct sample / persistent codec | +| --- | ---: | ---: | +| p50 | 10.506 | 5.984 | +| p95 | 19.927 | 11.156 | +| p99 | 20.920 | 18.675 | +| max | 68.909 | 38.066 | +| Первый кадр | 68.909 | 18.675 | +| Codec init, отдельно | 17.854 | 21.103 | +| BGR parity с reference | 128/128 exact | 128/128 exact | + +Каждый кадр декодирован до чтения следующего source fragment. Direct peak RSS +59,192 KiB. Это один bounded CPU A/B, не статистическая квалификация всего +профиля и не обещание аналогичного выигрыша при одновременной CPU-части графа. + +### Бинарный вход с настоящим декодером + +`perception-stage2-decoded-ingress-worker-20260902T1310MSK`: существующий +отдельный source process → IPC v2 → FragmentDecoder в CPU-only probe container +(2 CPU / 768 MiB). Receiver по-прежнему не получает длину записи. Защита raw +headers/reassembly сохранена; init/extradata и decoder scratch резервируются +в общем 16-MiB mailbox **до** использования. Native codec/DPB дополнительно +ограничены container RSS envelope; это не всеобъемлющий Python byte counter. + +- 103/103 raw events exact: init 1 + camera 32 + LiDAR 34 + pose 36; timestamp, + sequence, source identity, payload length/SHA сохранены. +- 32/32 decoded BGR exact против независимого reference. Первый decoded callback + за 3.071 s до source End marker; 31/32 до marker, последний дренируется перед + обработкой End получателем. Это нормальная финальная доставка, не EOF barrier. +- Source-due → decoded/hash callback p50/p95/p99/max: + 9.257 / 19.317 / 26.019 / 26.019 ms. Decode+hash p95/p99 11.635/19.698 ms. + Эти интервалы не включают модели, geometry, result scene, сеть или browser. +- Wire 4,061,695 bytes; peak accounted input 5,727,751 bytes; остаток 0; + cgroup peak 84,332,544 bytes. Source release lag max 5.838 ms. +- Negative EOF между фрагментами: started=1, completed=0, incomplete=1; + никакого payload callback, terminal=failed, lease/input bytes освобождены. + +PyAV18 wheel отсутствовал в исходном diagnostic image. Загружен публичный +`av==18.0.0` (35,536,910 bytes, SHA-256 +`ae56b40b6f8b067a8ad2dac664fbfbabac7f7a55b9a7bb031eb99289252bc017`), затем +установлен offline/no-deps в приватный experiment target. Общая система, +Goose Python и image не менялись. Target read-only; его file hashes сохранены +в dependency manifest. Это явный временный mount, не standalone packaging. + +246 focused tests PASS; повтор session API + decoder — 55 PASS. Ruff и mypy +трёх source modules PASS; full suite и модели на Mac не запускались. +Все временные probe/dependency containers и source children завершены. +Triton ready=200, Mac 8000=200, 8765 закрыт. Ollama/Frigate exited/restart=no; +GPU auto P8, 982 MiB, power limit 450 W, без изменения частот. + +### Evidence и следующий шаг + +Принятый manifest: 43 artifacts, SHA-256 +`612295eabbc8f203ad9b037a69ac9cf97b2df3134b087d9e51a9786738423f2f`, +`.runtime/perception-stage2-decoded-ingress-worker-20260902T1310MSK/manifest.json`. +Связанные sealed manifests: demux/reference/dependency — +`6a0090f6e1ec11aba436441afacdc0146902a5f911a57a8f989038764dab5d2f` +(22 artifacts); direct decoder — +`0eb32c2c5823130dec87b4e11d825d984b7ee373453b8b4f8b17a4729d5f8134` +(14 artifacts). Снимки code packs, отчёты, per-frame hashes, timestamps, +команды и dependency inventory сохранены; реальные inputs в Git не добавлены. + +**Граница:** cloud/pose пока передаются без изменения, но не связываются здесь +в causal scene; calibration identity у hash/decode probe синтетическая и не +допускает model scene. Нативный decoder ещё требуется изолировать supervised +CPU child внутри полного runtime; добавить bounded RPC, causal sensor cut на +момент camera release и handoff decoded ownership в общий mailbox. После этого +запустить тот же DDRNet/RF-DETR/geometry/motion/TGS/policy граф и проверить +raw parity / timing на новом binary input, без `/source.mp4`. + +Последний **полный** p95/p99 остаётся 124.98/136.12 ms, fresh 76/128. Не вычитать +decoder-выигрыш из этих значений и не объявлять real-time qualification. +Network/controller/recovery/inventory, standalone image и product cutover +остаются открытыми. Этап 2 продолжается; этапы 3–4 не начаты; actuation=false.