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
@@ -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 в этом шаге не проверялись.