docs(perception): record lifecycle ownership and expiry evidence

This commit is contained in:
DCCONSTRUCTIONS
2026-09-02 12:05:00 +03:00
parent ac69e3b444
commit 85349f42f9
3 changed files with 172 additions and 4 deletions
@@ -1,8 +1,8 @@
# Observatory: четыре этапа создания полного real-time Perception-профиля
Дата: 2026-09-01; обновлено 2026-09-02 11:00 МСК. **Этап 1 закрыт как engineering baseline; этап 2 начат, выполнен первый инкремент — общие queue/scheduler и per-cell expiry. Real-time qualification НЕ выдана.** Два последовательных PyTorch-пилота: по 128/128 без drops. Per-cell вариант: p95/p99 123.91/136.17 ms, 76 свежих six-layer результатов из 76 доступных sensor pairs; 52 missing pairs остаются NO_GO. Просроченные ячейки отдельно блокируются, а не объявляются свежими. Standalone/network/full-session/vehicle proof отсутствуют; этапы 3–4 не начаты. Default PyTorch, TensorRT numeric/material parity не пройдена.
Дата: 2026-09-01; обновлено 2026-09-02 12:00 МСК. **Этап 1 закрыт; этап 2 в работе: общие scheduler/per-cell expiry дополнены subprocess lifecycle и Worker-local cooperative lease/fencing. Real-time qualification НЕ выдана.** Полный normal pilot: 128/128 без drops, p95/p99 124.98/136.12 ms, fresh 76/128; 52 source gaps остаются NO_GO. Два намеренно аварийных прогона подтвердили lease-loss, полное освобождение и точный terminal accounting. Межконтейнерные contention/clean succession/crash quarantine проверены. Постоянный product controller, binary live ingress и standalone image ещё не готовы; этапы 3–4 не начаты. Default PyTorch, TensorRT parity не принята.
Последние изменения: `bcacb02` — общий bounded scheduler с корректным cancel/release; `1f8101e` — per-cell timestamps/expiry, повторная проверка и хеширование derived consumer view. Сырые model/geometry/motion/TGS/material outputs точны к reference на всех 128 кадрах в обоих runs. Mandatory layer guard не ослаблен. Старый whole-scene контроль дал 25 свежих результатов, новый per-cell — 76; это изменение адресности блокировки, не рост perceptual accuracy. Clock auto менялся самостоятельно: разница задержек не приписывается оптимизации. Следующий шаг этапа 2 — supervisor/StreamStart fencing и authoritative GPU ownership, затем binary ingress и standalone package. Условия Worker проверяет read-only evaluator; он пока не приобретает lease. Clocks/quotas/weights/thresholds не менялись; прежний fixed-clock PASS не переносится на новый runtime.
Последние изменения: `6acf468` — lifecycle и Worker-local lease; `ac69e3b` — подключение к полному пилоту и negative probes. Final expiry trace: stop через 45.74 ms после deadline, retirement через 4109.82 ms, receipts at/after deadline=0. Проверены все 229 опубликованных receipts и точная raw parity с соответствующими prefix reference. Модели/thresholds/clocks/quotas не менялись; auto latency deltas не являются доказанным ускорением. Следующий шаг — bounded binary ingress с неизвестной длиной источника, постоянная controller integration/recovery и standalone package. Локальный cooperative fence не заменяет backend claims, external GPU inventory или network authentication; эти gates остаются открытыми.
Текущий 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 с полным потоковым вычислением
**Текущий статус, 11:00 МСК:** в работе. Общие mailbox/serial GPU stage подключены к полному пилоту; per-cell expiry проверен двумя Worker-прогонами. Это первый инкремент, не завершённый supervisor/lease/data plane и не самостоятельный image. GO на этап 3 пока нет.
**Текущий статус, 12:00 МСК:** в работе. Общие mailbox/serial GPU stage, per-cell expiry, subprocess lifecycle и Worker-local cooperative lease подключены к полному пилоту. Межконтейнерное владение, crash quarantine и две реальные lease-expiry остановки проверены. Production admission/recovery, continuous GPU inventory/envelope monitoring, binary network ingress и standalone image ещё не закрыты. GO на этап 3 пока нет.
**Цель:** один самостоятельный контейнер принимает поток и реально рассчитывает DDRNet, детекции, расстояния, motion, TGS/costmap и policy-shadow; ничего не дорисовывается из старых LAB.
@@ -256,9 +256,14 @@ EoMT — семейство ViT-моделей сегментации изобр
- Два последовательных Worker runs: whole-scene p95/p99 125.01/130.31 ms, fresh 25/128; per-cell 123.91/136.17 ms, fresh 76/128. В обоих 128/128 без drops; raw functional parity 128/128, missing pairs 52. Per-cell блокирует 2195 cell×frame ground appearances (1994 при publication, ещё 201 при receipt); это не 2195 уникальных физических препятствий. Все 256 derived receipts повторно проверены по payload hashes/ages/policy.
- Продуктовый backend/frontend path и registry не переключены. Этап 2 остаётся незавершённым: общий supervisor/lease fencing, binary live ingress без знания EOF и standalone package ещё не реализованы. Этапы 3–4 не начаты. Следующий bounded шаг — lifecycle/ownership + неизвестная длина входного потока; старый batch materializer не ослаблять.
- Evidence первого инкремента этапа 2: `.runtime/perception-stage2-cells-worker-20260902T1055MSK/manifest.json`, 125 artifacts, SHA-256 `1a951fd9e807099e500fa66f8a40b1076071f96e4c8e0be4ab3094caaa33bf09`. Четыре сервиса восстановлены, Triton ready=200; Ollama/Frigate exited/restart=no, pilots=0, GPU auto P8, Mac 8000=200 и 8765 закрыт. Модели/сырые outputs не попали в Git.
- 2026-09-02 12:00 МСК: `6acf468` / `ac69e3b` — общий subprocess lifecycle и cooperative Worker-local lease/fencing подключены к полному пилоту. 223 focused tests PASS. Отдельные контейнеры подтвердили contention rejection, clean generation succession и crash quarantine. Global product queue не менялась; canonical persistent control root и backend integration не установлены.
- Normal pilot 128/128 без drops, p95/p99 124.98/136.12 ms, fresh 76/128. Intentional expiry A: 52 released = 51 complete + 1 GPU terminal drop; expiry trace B: 50 released = 50 complete. Оба `execution_complete=false`, reason=`lease-lost`, unaccounted=0, все процессы/входы освобождены. Final trace: stop-request deadline +45.74 ms, retirement +4109.82 ms, receipts at/after deadline=0. Все 229 опубликованных raw outputs exact к reference prefix; clocks/quotas/models не менялись.
- Evidence: `.runtime/perception-stage2-lifecycle-worker-20260902T1146MSK/manifest.json`, 259 artifacts, SHA-256 `00bd091a80845e50489a34ce4df2cbf3d2cd60d9ecb13d1d97accd03d44fdba4`. Временные probe containers и два test volumes удалены после сохранения owner records. Четыре сервиса восстановлены; Triton ready=200, Ollama/Frigate exited/restart=no, Mac 8000=200, 8765 закрыт. Этап 2 остаётся в работе; дальше binary ingress, постоянная controller/recovery integration и standalone package.
## Surprises / открытые вопросы
- 2026-09-02 12:00 МСК: истечение lease и освобождение GPU разделены. После crash OS lock свободен, но durable `active` не допускает новый запуск. Recovery намеренно отсутствует до trusted resource-release proof; нельзя удалять production marker ради продолжения. В пробах удалены только два заведомо временных experiment-owned volume после сохранения записей и остановки всех probe containers.
- Свежая camera/cloud/pose пара не равна свежему полному scene. У TGS last-seen разрешающих ячеек может быть старее camera anchor; новый guard сохраняет этот возраст. В финальном C пять scenes истекли за 14 ms между publisher guard и приёмом, хотя раньше выглядели бы свежими. Пока одна такая ячейка запрещает весь scene; per-cell expiry — следующий runtime increment без ослабления 250-ms TTL. 52 source gaps нельзя исправить GPU-ускорением.
- New operating-envelope evaluator — проверяемый контракт readiness, не уже подключённый orchestrator и не host clock manager. Power/P-state/limit остаются измеряемыми фактами; continuous monitoring и авторитетный lease должен предоставить общий runtime. Low GPU utilization не доказывает владение, а snapshot PASS не квалифицирует целый run.
@@ -298,6 +303,6 @@ EoMT — семейство ViT-моделей сегментации изобр
## Outcomes / retrospective
Этап 1 остаётся завершённым engineering baseline. Первый инкремент этапа 2 отделил scheduler от LAB-пилота и устранил whole-scene блокировку из-за одной старой ячейки: 76/76 доступных sensor-paired scenes дали свежий envelope, но expired/unknown cells внутри него остаются NO_GO. 52 source gaps не маскируются. Новый p99 136.17 ms — FAIL 125 ms; разницу auto timings не выдаём за причинное ускорение. Все сырые результаты моделей/геометрии/motion/TGS/material сохранены точно. Полноценный supervisor, cross-process lease/fencing, binary live ingress и standalone image ещё нужны; это не GO на этап 3. Default PyTorch сохранён, TensorRT parity не принята. Clock/quotas не менялись, четыре сервиса восстановлены, Ollama/Frigate выключены, Mac 8000 работает. Full suite не запускался; старый external-evidence-symlink baseline failure не исправлялся. Исторический NVCR403/image-layer blocker не перепроверялся новым build.
Этап 1 остаётся завершённым engineering baseline. В этапе 2 общий lifecycle теперь действительно управляет subprocess-backed пилотом: тёплые модели, admission, serial GPU/CPU lanes, result binding, watchdog и проверенный release. Cooperative lease проверен между контейнерами на общем томе; crash не превращается в разрешение новому владельцу. Normal run 128/128, 76 fresh envelopes, raw parity exact; p99 136.12 ms — всё ещё FAIL 125 ms. Expiry runs завершились намеренным FAIL с закрытым учётом и без остаточных владельцев/bytes, а не ложным successful completion. Следующий этап работы — binary unknown-duration ingress и постоянная controller/ownership/recovery integration, затем самостоятельная упаковка. Новый код не переключает старый batch/registry/UI; GO на этап 3 нет. Clocks/quotas/models не менялись, временные ресурсы убраны с сохранением evidence, четыре сервиса восстановлены, Ollama/Frigate выключены. Full suite и новый image build не запускались; исторические baseline-symlink и NVCR403 вопросы не объявлены исправленными.
После этапа 4 здесь будут перечислены digest самостоятельного полного образа, измеренные статусы и ошибки по recordings, реально проверенные source/hardware/config комбинации, состояние сохранённого EoMT-варианта и оставшиеся physical-live/quality/vehicle-integration ограничения. Готовый Docker и готовая автономия не отождествляются.
@@ -181,6 +181,44 @@ entries with exact per-reason totals; it is not the durable terminal ledger.
These primitives are NOT the controller supervisor, a cross-process GPU lease,
StreamStart fencing or the binary data plane. Those stage-2 boundaries remain open.
Stage-2 lifecycle increment, 2026-09-02 (`6acf468`, `ac69e3b`):
`StreamingLifecycle` now supplies the subprocess-backed profile supervisor using
the existing `GraphState` and full `StreamStart` identity. `WorkerLease` uses a
stable POSIX file lock on one controller-selected, private directory per Worker,
plus an atomic/fsynced ownership record. All managed profile containers must use
that SAME directory/volume. Neither the directory nor child commands come from
an incoming source/job. This cooperative local fence supplements, not replaces,
the backend claim and the trusted inventory of unmanaged GPU clients.
Open acquires ownership before process spawn/warmup; Ready enables admission.
Renewal cannot resurrect expired ownership. Admission, each GPU/CPU lane and
result publication/receipt validate run/source/worker/epoch/generation and pinned
image/profile/config/calibration/clock identities. A mismatched client is rejected
without cancelling the current owner. Each compute lane remains single-owner.
The watchdog fences an expired owner even without a new frame and terminates
only its dedicated child process groups. Cleanup keeps ownership while callbacks,
tracked threads, pending/active payloads or child groups remain. Clean retirement
persists `released` before unlocking; a higher generation and new epoch may then
activate. A controller crash leaves `active` even when the kernel unlocks, so
the next owner stays quarantined. No force/unverified recovery API exists.
Cross-container contention/clean succession/crash quarantine and full-profile
lease expiry were exercised on Worker 006. The final timed expiry trace had no
receipt at/after the lease deadline, stop requested after 45.74 ms and retirement
after 4109.82 ms. This is bounded evidence, not a hard-real-time scheduler guarantee.
The holder has a 2 s lease and receives local pilot heartbeats every 250 ms;
those settings and the 50 ms watchdog poll are NOT network/vehicle safety limits.
Current integration remains explicitly diagnostic: `pilot_lifecycle.py` acts as
a local controller, legacy GPU services are quiesced by the authorized launcher,
the named volume is an isolated test volume, and the image still has code/model
mounts. Mounted-code hashes remain in the experiment manifest; an image digest
alone cannot seal those overrides. Backend claims/production-wide canonical root,
trusted post-crash resource-release recovery, continuous GPU inventory/envelope
enforcement, network authentication/heartbeat and binary live ingress are NOT
installed or qualified by this increment. Standalone packaging must remove the
developer mounts and retain the same lifecycle boundaries.
`worker_operating_envelope.py` checks a trusted post-warmup snapshot against
preregistered hardware/driver/resource/clock conditions and StreamStart identities,
ownership and client inventory. Unknown or expired facts fail readiness. The
@@ -8,6 +8,9 @@ GO на инженерную реализацию этапа 2, не полны
Продолжение на 2026-09-02 11:00 МСК: начат этап 2, вынесен общий scheduler
и реализован per-cell expiry. Два новых Worker-прогона и точная parity приведены
в последнем разделе этого же журнала. Этап 2 целиком ещё не закрыт.
Продолжение на 12:00 МСК: добавлены subprocess lifecycle, Worker-local
cooperative ownership и fencing; normal/две expiry пробы и межконтейнерное
владение проверены. Последний раздел фиксирует этот второй инкремент этапа 2.
Обновление 20:51 UTC: совместный source-paced пилот выполнен. После оптимизации
128/128 кадров без drops; p95/p99 174.09/190.62 ms — FAIL исходного latency gate.
Подробности и evidence нового experiment `perception-joint-pilot-20260901T2040Z`
@@ -1216,3 +1219,125 @@ fencing, затем binary live ingress без знания длины/EOF и st
Не выдавать их за реализованные по факту общего scheduler. Stage 3 cutover,
full-session/network/quality/vehicle qualification и TensorRT promotion закрыты
до своих проверок. Профиль остаётся экспериментальным, actuation=false.
## Этап 2, второй инкремент — lifecycle и Worker-local lease, 2026-09-02 12:00 МСК
Коммиты: `6acf468` (core/negative tests), `ac69e3b` (подключение к полному
пилоту, cross-container probe, interrupted-source accounting). Нет изменения
моделей, thresholds, TGS/material политики, clock policy, Docker CPU/RAM limits
или product LAB path.
### Реализованный контракт и граница владения
`StreamingLifecycle` использует существующие `GraphState`, `StreamStart` и
общий scheduler. При open получает Worker-local lease до запуска дочерних
процессов; Ready открывает ingress только после warmup. Полный StreamStart
проверяется при admission, GPU/CPU работе и publication/receipt: run/source,
worker, epoch/generation, image/profile/effective-config/calibration/clock.
Старый клиент получает отказ, но не может остановить нового владельца.
После истечения lease нельзя восстановить разрешение поздним heartbeat.
Lease — stable POSIX file lock + atomic/fsynced ownership record в одном
private controller-selected каталоге на Worker. Каталог не приходит из job.
Все управляемые profile containers обязаны использовать один и тот же root.
Это cooperative fence, **не** доказательство отсутствия unmanaged GPU clients
и не замена backend claim. В этом эксперименте root — отдельный named volume;
четыре legacy GPU сервиса предварительно остановлены согласованным launcher.
Watchdog работает без ожидания следующего кадра: fence → прекращение admission
и публикации → stop owned child groups → release только после фактического
завершения процессов, active callbacks, tracked threads и input payloads.
Нельзя заменить последнюю проверку переданным снаружи `resources_released=true`.
Timeout удерживает владение; clean retirement сохраняет `released`, затем
unlock. После crash в журнале остаётся `active`, поэтому даже свободный OS lock
не разрешает новый профиль. Force/recovery API намеренно не добавлен: нужен
будущий trusted proof освобождения ресурсов, а не удаление production marker.
Heartbeat здесь генерирует локальный pilot-controller: каждые 250 ms, lease
2 s, watchdog poll 50 ms. Это не network/vehicle safety budgets. Remote
authentication/heartbeat, continuous hardware inventory/envelope enforcement
и постоянная product controller integration остаются отдельными gates.
Модели остаются в отдельных supervised processes одного контейнера.
### Последовательные Worker 006 probes
Experiment: `perception-stage2-lifecycle-worker-20260902T1146MSK`.
Диагностический image прежний, network none, 8 CPU / 8 GiB, один full GPU
profile; нет Mac model/load/build работы, новых source/model transfers,
полного source hash/pass или предварительного full decode. Calibration identity
считалась только по трём bounded static NPY arrays, не по облаку/всему архиву.
Сначала CPU-only межконтейнерные проверки на общем named volume:
- Holder generation 1 держит lease, contender generation 2 получает `already owned`.
- После clean stop generation 2 допускается и корректно retires.
- Другой fixture-контейнер завершён через `os._exit(17)` после durable active
record; следующий контейнер получает `previous owner unretired`. Это ожидаемый
PASS quarantine, а не восстановление после crash.
| Run | Released / completed / terminal drop | Fresh receipt | Итог |
| --- | --- | --- | --- |
| Normal 128 | 128 / 128 / 0 | 76 | execution PASS, latency FAIL |
| Expiry A после отключения renew на sequence 31 | 52 / 51 / 1 | 30 | ожидаемый lease-loss FAIL |
| Expiry trace B, тот же fault | 50 / 50 / 0 | 29 | ожидаемый lease-loss FAIL |
Во всех трёх unaccounted=0, `runtime_resources_released=true`, active GPU/CPU
calls=0, live children=0, residual payload bytes=0. В A late GPU sequence 51
получил terminal `gpu-failed`, но не scene. B сработал между кадрами. У двух
negative runs ожидаемый exit code 1 / execution_complete=false; их укороченные
latency distributions не считаются успехом полного replay. У interrupted decode
теперь есть отдельный `source_failed` исход: камера, уже выпущенная источником,
не теряется из accounting, даже если до mailbox admission дело не дошло.
Normal: mean/p95/p99 83.12/124.98/136.12 ms, warmup 9.361 s, stop 4.072 s,
first result 568.15 ms. VRAM peak 2363 MiB, cgroup memory peak 3407.88 MiB,
inflight peak 5,202,376 bytes. Fresh 76/128 — все 76 доступных sensor pairs;
52 missing pairs по исходному binding остаются NO_GO. Старые/rejected/unknown
клетки внутри свежих envelopes не превращаются в свободное пространство.
Auto GPU clocks варьируются; causal latency improvement от lifecycle не заявлен.
Порог p99 125 ms по-прежнему не пройден, network qualification=false.
После первых двух runs добавлены только timestamps deadline/stop/retirement и
renew count, без изменения вычисления, scheduling или guards. Финальная версия
повторно проверена в expiry trace B: stop-request через **45.740515 ms** после
lease deadline, retirement через **4109.819258 ms**. Последний receipt завершён
за **4.527079 ms до deadline**; receipts at/after deadline=0. Это измерение одного
bounded trace, не обещание hard-real-time stop при любой нагрузке.
Все опубликованные 128 + 51 + 50 = **229** segmentation hashes, proposals,
observations, tracks, threats, ranges, raw TGS states, materials и sensor bindings
точны к соответствующему prefix pinned PyTorch reference. Все receipts повторно
прочитаны: runtime bindings, layer hashes, freshness, effective costmap hash и
policy counts совпадают; permissive stale/non-ground/non-hard-surface cells=0.
### Evidence, проверки и остаток этапа
223 focused tests PASS; Ruff, strict mypy двух новых core modules, diff-check
PASS. Full suite не запускался. Прежний historical external-evidence-symlink
baseline failure не исправлялся/не перезапускался. Результаты GPU-проб и real
source outputs не попадают в Git.
Manifest: `.runtime/perception-stage2-lifecycle-worker-20260902T1146MSK/manifest.json`,
259 artifacts, SHA-256
`00bd091a80845e50489a34ce4df2cbf3d2cd60d9ecb13d1d97accd03d44fdba4`.
Копия — одноимённая папка `D:/NDC_MISSIONCORE/runtime/experiments/`.
Сохранены оба code snapshots (`probe`, `probe-trace`), commands, raw logs,
reports/scenes, owner records и final checks. Image/profile digests прежние;
effective config SHA `440f86f2fc3421b62846ed56943cf3a953549530a24cd2e07e39ae2fd955d777`,
bounded calibration SHA `d7f6ba5b46bc58f4d40c369f1125ece146181e34e58900f098d3f33f5475d522`.
Mounted code identities находятся отдельно в manifest: diagnostic image digest
сам по себе не запечатывает эти overrides.
Временные probe containers и два experiment-owned lease volumes удалены;
ownership records сохранены в `console-snapshot*.txt`. Это не удаление production
lease state ради обхода quarantine. Четыре Mission Core сервиса восстановлены,
Triton healthy/ready=200. Ollama/Frigate exited/restart=no; pilots=0. GPU auto
P8/210/405 MHz, 985 MiB, 450 W; Mac 8000=200, 8765 закрыт. Legacy restart loops
не изменены.
**Gate:** принят второй инкремент этапа 2. Не закрыты binary live ingress без
знания длины/EOF, постоянный controller/root и backend-claim integration,
trusted recovery/inventory enforcement и standalone image без code/model mounts.
Stage 3 UI/registry/batch cutover не начинался. Все real-time/network/full-session/
quality/vehicle gates и actuation=false сохраняются; новый image build и
исторический NVCR403 blocker в этом шаге не проверялись.