fix: stabilize public demo and align sorter twins
Document arhipovdan.ru DNS/TLS blocker, add production smoke and GPU perf tooling, unify twin layout, and expand visual regression coverage. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
# Demo Runbook — OZON Tech Sorter Simulation
|
||||
|
||||
**Назначение:** провести живую демонстрацию жюри/заказчику без сюрпризов.
|
||||
**Дата актуализации:** 2026-07-15
|
||||
**Публичный URL:** https://arhipovdan.ru/
|
||||
**Назначение:** провести живую демонстрацию жюри/заказчику без сюрпризов.
|
||||
**Дата актуализации:** 2026-07-15
|
||||
**Рабочий публичный URL (временный):** Quick Tunnel — см. `PRODUCTION_DEPLOYMENT_REPORT.md`
|
||||
**Ожидаемый постоянный домен:** https://arhipovdan.ru/ — **BLOCKED_EXTERNAL** (`PUBLIC_DOMAIN_DIAGNOSTIC.md`)
|
||||
**Локальный production:** http://127.0.0.1:3100/
|
||||
**Репозиторий:** `/home/coder/arhipovdan/app`
|
||||
|
||||
---
|
||||
@@ -24,7 +26,7 @@ npm test
|
||||
npm run build
|
||||
```
|
||||
|
||||
Ожидание: Vitest **153** passed / **16** files; build OK.
|
||||
Ожидание: Vitest **166** passed; build OK.
|
||||
|
||||
### 1.3. Health
|
||||
|
||||
@@ -105,16 +107,16 @@ npm run demo:stop
|
||||
|
||||
Порядок storytelling (см. `demoPlaylist.ts`):
|
||||
|
||||
1. Короб → **B**
|
||||
2. ЛанчБокс → **B**
|
||||
3. Негабарит → **C**
|
||||
4. Мелкий item (ручка) → **C** (min dims)
|
||||
5. Тарелка → **D** (K)
|
||||
6. Бутылка → **D**
|
||||
7. Oversized+round → **C** (priority)
|
||||
8. (edge / доп. кейс по playlist)
|
||||
9. **Jam**
|
||||
10. **Emergency stop**
|
||||
1. Короб → **B**
|
||||
2. ЛанчБокс → **B**
|
||||
3. Негабарит → **C**
|
||||
4. Мелкий item (ручка) → **C** (min dims)
|
||||
5. Тарелка → **D** (K)
|
||||
6. Бутылка → **D**
|
||||
7. Oversized+round → **C** (priority)
|
||||
8. (edge / доп. кейс по playlist)
|
||||
9. **Jam**
|
||||
10. **Emergency stop**
|
||||
|
||||
Точные id/SKU — в `src/domain/demoPlaylist.ts`.
|
||||
|
||||
@@ -141,9 +143,9 @@ git checkout backup/pre-maximum-demo-realism-20260715
|
||||
|
||||
## 7. Что не обещать жюри
|
||||
|
||||
- «Настоящий CV с камеры» — нет, pseudo-CV.
|
||||
- «Полный physics engine» — нет, кинематика.
|
||||
- «Агент сам выкатывает в prod» — запрещено.
|
||||
- «Настоящий CV с камеры» — нет, pseudo-CV.
|
||||
- «Полный physics engine» — нет, кинематика.
|
||||
- «Агент сам выкатывает в prod» — запрещено.
|
||||
- «`/` и `/details` — идентичная 3D-сцена» — пока нет.
|
||||
|
||||
---
|
||||
|
||||
40
docs/FINAL_DEMO_READINESS.md
Normal file
40
docs/FINAL_DEMO_READINESS.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# FINAL_DEMO_READINESS
|
||||
|
||||
Дата: 2026-07-15
|
||||
Ветка: `feature/maximum-demo-realism`
|
||||
Production release (не тронут этим коммитом): `20260715-1712` / `index-ncgt6PBL.js`
|
||||
|
||||
## Status board
|
||||
|
||||
| Область | Статус | Комментарий |
|
||||
| ------- | ------ | ----------- |
|
||||
| Git | PASS | Feature branch clean after commit; no push |
|
||||
| Production container | PASS | `owl-web-1` на `:3100`, bundle `index-ncgt6PBL.js` |
|
||||
| Permanent domain | BLOCKED_EXTERNAL | `arhipovdan.ru` → openresty 404 / TLS SNI fail |
|
||||
| TLS | BLOCKED_EXTERNAL | Нет SNI-сертификата на destination IP |
|
||||
| Main route | PASS | Local + Quick Tunnel |
|
||||
| Details route | PASS | SPA fallback 200 |
|
||||
| Unit tests | PASS | 166 |
|
||||
| E2E | PASS | 15 (excl. production) |
|
||||
| Visual regression | PASS | 10 snapshots |
|
||||
| GPU performance | PARTIAL | SwiftShader/xvfb only; hardware baseline blocked in coder |
|
||||
| Safety scenarios | PASS | Jam + E-stop e2e |
|
||||
| Rollback | PASS | Tag `backup/pre-maximum-demo-realism-20260715` + docker rename |
|
||||
| Agent safety | PASS | Worktree / dry-run / no auto DNS |
|
||||
|
||||
## Verdict
|
||||
|
||||
```text
|
||||
READY WITH EXTERNAL DOMAIN BLOCKER
|
||||
```
|
||||
|
||||
Live demo **можно** вести с:
|
||||
|
||||
- `http://127.0.0.1:3100/` (локально), или
|
||||
- актуального Quick Tunnel `https://invitations-based-characters-accent.trycloudflare.com/`
|
||||
|
||||
Постоянный домен `https://arhipovdan.ru/` **не готов** до DNS/proxy фикса пользователем (см. `PUBLIC_DOMAIN_DIAGNOSTIC.md`).
|
||||
|
||||
## Next deploy note
|
||||
|
||||
Новый код (perf overlay, twin layout, visual snapshots) собран как `index-DEbogfOZ.js` в `dist/`, но **не** задеплоен в `owl-web-1`, чтобы не нарушить работающий production. Redeploy — отдельный шаг после доменного фикса или явного запроса.
|
||||
53
docs/HEADED_GPU_PERFORMANCE_REPORT.md
Normal file
53
docs/HEADED_GPU_PERFORMANCE_REPORT.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# HEADED_GPU_PERFORMANCE_REPORT
|
||||
|
||||
Measured: 2026-07-15T17:48:54.064Z
|
||||
Commit: `b5985f3`
|
||||
Base URL: http://127.0.0.1:3101/
|
||||
Display: headed (:99)
|
||||
Software renderer detected: **YES**
|
||||
Verdict: **NOT A GPU BASELINE — software renderer (SwiftShader/llvmpipe/etc.)**
|
||||
|
||||
## Notes
|
||||
|
||||
- Headed Chromium with GPU-preferring flags.
|
||||
- PerfCollector only with `?perf=1`; quality forced via `?quality=`.
|
||||
- Prior headless ~9.6 FPS is labeled software/headless WebGL diagnostic baseline.
|
||||
- **Host has NVIDIA GTX 1080 ×2**, but Playwright Chromium in this coder/Xvfb session resolves WebGL to **SwiftShader** — numbers below are **not** a hardware GPU baseline.
|
||||
- For true GPU baseline: run `PLAYWRIGHT_BASE_URL=… npm run perf:gpu` on the presentation machine with a real `DISPLAY` and confirm renderer does **not** contain SwiftShader/llvmpipe.
|
||||
|
||||
## Mode table (after Play)
|
||||
|
||||
| Mode | Renderer | Avg FPS | Min FPS | p95 | Calls | Triangles | Heap |
|
||||
| ---- | -------- | ------: | ------: | --: | ----: | --------: | ---: |
|
||||
| low | ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device (Subzero) (0x0000C0DE)), SwiftShader driver) | 10.58 | 4.44 | 151.9 | 167 | 3546 | 16.26 |
|
||||
| medium | ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device (Subzero) (0x0000C0DE)), SwiftShader driver) | 9.91 | 4.68 | 158.28 | 167 | 3546 | 16.38 |
|
||||
| high | ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device (Subzero) (0x0000C0DE)), SwiftShader driver) | 8.69 | 4.03 | 182.2 | 167 | 3546 | 17.45 |
|
||||
| demo | ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device (Subzero) (0x0000C0DE)), SwiftShader driver) | 8.88 | 4.17 | 181.81 | 167 | 3546 | 21.1 |
|
||||
|
||||
## Memory after 3 seek+replay (demo mode)
|
||||
|
||||
```
|
||||
{
|
||||
"heapDeltaMb": 2.19,
|
||||
"geometriesDelta": 12,
|
||||
"texturesDelta": 0
|
||||
}
|
||||
```
|
||||
|
||||
## Prior software/headless WebGL diagnostic baseline (reference only)
|
||||
|
||||
```
|
||||
Average FPS: 9.6
|
||||
Minimum FPS: 3.3
|
||||
p95 frame time: 200 ms
|
||||
```
|
||||
|
||||
Do **not** treat the above as live-demo GPU characteristics.
|
||||
|
||||
## Chromium launch args
|
||||
|
||||
```
|
||||
--use-gl=angle --use-angle=gl-egl --enable-webgl --enable-webgl2 --ignore-gpu-blocklist --enable-gpu-rasterization --enable-precise-memory-info --enable-unsafe-swiftshader
|
||||
```
|
||||
|
||||
Raw JSON: `agent/reports/gpu-baseline.json`
|
||||
@@ -1,35 +1,38 @@
|
||||
# PERFORMANCE_AFTER_MAXIMUM_DEMO
|
||||
|
||||
Measured: 2026-07-15T17:11:06.031Z
|
||||
Commit: `985f7c3`
|
||||
Measured: 2026-07-15
|
||||
Commits referenced: `985f7c3` / tooling on current branch
|
||||
|
||||
## Bundle (measured)
|
||||
## Bundle (production container, unchanged)
|
||||
|
||||
| Метрика | Значение |
|
||||
| ------- | -------: |
|
||||
| Total JS | 1300.9 KB |
|
||||
| Total JS gzip | 358.1 KB |
|
||||
| Main chunk | index-ncgt6PBL.js (96.4 KB gzip) |
|
||||
| R3F chunk gzip | 226.1 KB |
|
||||
| Main chunk | `index-ncgt6PBL.js` |
|
||||
| Release | `20260715-1712` |
|
||||
|
||||
## Runtime (Playwright Chromium headless)
|
||||
## software/headless WebGL diagnostic baseline
|
||||
|
||||
| Метрика | До | После | Цель | Статус |
|
||||
| ------- | -: | ----: | ---: | ------ |
|
||||
| Average FPS | n/a (не измерялось) | 9.6 | ≥30 | CHECK |
|
||||
| Minimum FPS | n/a | 3.3 | ≥20 | CHECK |
|
||||
| p95 frame time | n/a | 200.1 ms | ≤33 ms | CHECK |
|
||||
| Load time | n/a | 1008 ms | ≤5000 | OK |
|
||||
| JS heap start | n/a | 13400000 | — | measured |
|
||||
| JS heap after interaction | n/a | 13400000 | no runaway | measured |
|
||||
| Canvas present | — | 1 | ≥1 | OK |
|
||||
| JS gzip (main+vendor) | ~335 KB main previously | 358.1 KB | — | measured |
|
||||
**Не является характеристикой живой демонстрации.**
|
||||
|
||||
```text
|
||||
Average FPS: 9.6
|
||||
Minimum FPS: 3.3
|
||||
p95 frame time: 200 ms
|
||||
```
|
||||
|
||||
Повторено под xvfb/SwiftShader (~8.9 FPS demo) — см. `HEADED_GPU_PERFORMANCE_REPORT.md`.
|
||||
|
||||
## Headed / GPU tooling
|
||||
|
||||
| Item | Status |
|
||||
| ---- | ------ |
|
||||
| `?perf=1` overlay | Implemented |
|
||||
| `?quality=low\|medium\|high\|demo` | Implemented |
|
||||
| `npm run perf:gpu` | Writes report + JSON |
|
||||
| Hardware NVIDIA WebGL in coder | **Not available** (Chromium → SwiftShader) |
|
||||
| True GPU baseline | Run on presentation laptop with DISPLAY + real GPU |
|
||||
|
||||
## Notes
|
||||
|
||||
- **Headless Chromium WebGL is not a GPU FPS measurement.** Values ~9 FPS / p95 ~200 ms reflect software/SwiftShader-like rendering in CI/headless, **not** desktop Chrome with NVIDIA. Do not claim 30–60 FPS from this pass.
|
||||
- For jury/demo FPS, measure in headed Chromium on the presentation machine (`npm run test:e2e:headed` + DevTools Performance) or Engineering Details FPS meter.
|
||||
- Draw calls / triangles require WebGL inspector in headed mode; not available in this headless pass.
|
||||
- Demo quality mode keeps `shadows=false` and `effectsEnabled=false` for stable real-device FPS (see `qualityMode.ts`). High mode may enable shadows.
|
||||
- Raw JSON: `agent/reports/perf-metrics.json`
|
||||
- Runtime error (if any): none
|
||||
- Demo quality keeps `shadows=false` for stable FPS.
|
||||
- Do not quote SwiftShader FPS to jury as demo capability.
|
||||
|
||||
@@ -1,95 +1,62 @@
|
||||
# Playwright E2E + Visual Regression
|
||||
|
||||
Lightweight end-to-end and visual regression coverage for the continuous demo on `/` and the SPA route `/details`.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Node.js with project deps installed (`npm install`)
|
||||
- Chromium for Playwright: `npx playwright install chromium`
|
||||
- App reachable at the base URL (default preview: `http://127.0.0.1:3101`)
|
||||
|
||||
By default tests **do not** start a server. Start preview yourself, for example:
|
||||
|
||||
```bash
|
||||
npm run build && npx vite preview --host 127.0.0.1 --port 3101
|
||||
```
|
||||
|
||||
Or point at an already-running preview/prod instance via `PLAYWRIGHT_BASE_URL`.
|
||||
|
||||
## Scripts
|
||||
|
||||
| Script | Command | Purpose |
|
||||
|--------|---------|---------|
|
||||
| `test:e2e` | `playwright test` | Run all e2e + visual tests |
|
||||
| `test:e2e:headed` | `playwright test --headed` | Same, headed browser |
|
||||
| `test:e2e:update` | `playwright test --update-snapshots` | Refresh visual baselines |
|
||||
| Script | Purpose |
|
||||
|--------|---------|
|
||||
| `npm run test:e2e` | Local e2e + visual (`--grep-invert @production`) |
|
||||
| `npm run test:e2e:headed` | Same, headed |
|
||||
| `npm run test:e2e:update` | Refresh visual baselines (manual review) |
|
||||
| `npm run test:e2e:production` | Manual production smoke via `PLAYWRIGHT_BASE_URL` |
|
||||
|
||||
## Environment
|
||||
|
||||
| Variable | Default | Meaning |
|
||||
|----------|---------|---------|
|
||||
| `PLAYWRIGHT_BASE_URL` | `http://127.0.0.1:3101` | Target app origin |
|
||||
| `PLAYWRIGHT_START_SERVER` | unset | Set to `1` to let Playwright run `vite preview` on port 3101 |
|
||||
| `PLAYWRIGHT_BASE_URL` | `http://127.0.0.1:3101` | Target origin |
|
||||
| `PLAYWRIGHT_START_SERVER` | unset | `1` → auto `vite preview` :3101 |
|
||||
|
||||
Examples:
|
||||
## Config
|
||||
|
||||
```bash
|
||||
# Against local preview (already running)
|
||||
PLAYWRIGHT_BASE_URL=http://127.0.0.1:3101 npm run test:e2e
|
||||
|
||||
# Against prod-style port
|
||||
PLAYWRIGHT_BASE_URL=http://127.0.0.1:3100 npm run test:e2e
|
||||
|
||||
# Auto-start preview if nothing is listening
|
||||
PLAYWRIGHT_START_SERVER=1 npm run test:e2e
|
||||
```
|
||||
|
||||
## Config highlights
|
||||
|
||||
- **workers: 1** — keep e2e light on shared servers
|
||||
- **retries: 1**
|
||||
- **chromium only**
|
||||
- Screenshot on failure; trace on first retry
|
||||
- Soft visual thresholds (`threshold: 0.3`, `maxDiffPixelRatio: 0.05`)
|
||||
- workers: **1**
|
||||
- retries: **1** (trace on first retry)
|
||||
- screenshot on failure
|
||||
- visual: `threshold: 0.35`, `maxDiffPixelRatio: 0.08`
|
||||
- CI: `npm ci` → `npm test` → `npm run build` → `npm run test:e2e` (no production smoke)
|
||||
|
||||
## Test map
|
||||
|
||||
| Spec | Coverage |
|
||||
|------|----------|
|
||||
| `e2e/smoke.spec.ts` | `/` loads, canvas (or loading→canvas), no `pageerror`, play works, replay if finished |
|
||||
| `e2e/controls.spec.ts` | next/prev seek, jump case 0 & 8, speed buttons, presentation toggle |
|
||||
| `e2e/safety.spec.ts` | jam (index 8) → FAULT; emergency (index 9) → EMERGENCY |
|
||||
| `e2e/routes.spec.ts` | `/details`, refresh stays on details, navigate back to `/` |
|
||||
| `e2e/visual.spec.ts` | Idle HUD panel snapshot only (not full WebGL canvas) |
|
||||
| `smoke` | canvas, play |
|
||||
| `controls` | seek, speed, presentation |
|
||||
| `safety` | jam / emergency |
|
||||
| `routes` | `/details` SPA |
|
||||
| `visual` | 10 snapshots (HUD B/C/D/jam/E-stop/recovery, presentation, details, perf overlay) |
|
||||
| `production.smoke` | `@production` — manual only |
|
||||
|
||||
Stable selectors live on `MainPage` as `data-testid` values (`demo-play`, `demo-hud`, `demo-case-{n}`, etc.).
|
||||
## Snapshot list
|
||||
|
||||
## Updating visual snapshots
|
||||
|
||||
1. Ensure the app at `PLAYWRIGHT_BASE_URL` matches the UI you want to lock.
|
||||
2. Run:
|
||||
|
||||
```bash
|
||||
npm run test:e2e:update
|
||||
```text
|
||||
01-home-idle-hud
|
||||
02-class-b-hud
|
||||
03-class-c-hud
|
||||
04-class-d-hud
|
||||
05-jam-fault-hud
|
||||
06-emergency-hud
|
||||
07-presentation-mode
|
||||
08-details-page
|
||||
09-recovery-hud
|
||||
10-perf-overlay
|
||||
```
|
||||
|
||||
3. Review diffs under `e2e/visual.spec.ts-snapshots/` (and commit those PNGs when intentional).
|
||||
4. Re-run `npm run test:e2e` to confirm green.
|
||||
HUD snapshots preferred over full WebGL pixel-perfect. Golden files are not auto-updated in CI.
|
||||
|
||||
Snapshots intentionally target **`[data-testid="demo-hud"]`** so WebGL canvas noise does not fail CI.
|
||||
## Latest local run
|
||||
|
||||
## System deps
|
||||
|
||||
On a fresh Linux host, Chromium may need OS libraries:
|
||||
|
||||
```bash
|
||||
npx playwright install chromium
|
||||
npx playwright install-deps chromium
|
||||
```text
|
||||
15 passed (e2e, excl. production)
|
||||
Production smoke @ :3100 — PASS (index-ncgt6PBL.js)
|
||||
Production smoke @ Quick Tunnel — PASS
|
||||
Production smoke @ arhipovdan.ru — FAIL (TLS/DNS blocker)
|
||||
```
|
||||
|
||||
## Artifacts (gitignored)
|
||||
|
||||
- `test-results/` — failure screenshots / traces
|
||||
- `playwright-report/` — HTML report
|
||||
- `blob-report/` — blob reporter output
|
||||
`}
|
||||
@@ -1,15 +1,15 @@
|
||||
# PRODUCTION_DEPLOYMENT_REPORT
|
||||
|
||||
Дата: 2026-07-15
|
||||
Ветка: `feature/maximum-demo-realism`
|
||||
Базовый commit этапа 1: `985f7c3`
|
||||
Дата: 2026-07-15
|
||||
Ветка: `feature/maximum-demo-realism`
|
||||
Базовый commit этапа 1: `985f7c3`
|
||||
Production bundle после этапа 2: `index-ncgt6PBL.js`
|
||||
|
||||
## Способ деплоя
|
||||
|
||||
Docker-контейнер `owl-web-1` (image `owl-web:<release>`), порт `127.0.0.1:3100→80`.
|
||||
Docker API: `DOCKER_HOST=tcp://127.0.0.1:2375` (из coder-контейнера).
|
||||
Compose-файл: `docker-compose.server.yml` (исторически `-p owl`).
|
||||
Docker-контейнер `owl-web-1` (image `owl-web:<release>`), порт `127.0.0.1:3100→80`.
|
||||
Docker API: `DOCKER_HOST=tcp://127.0.0.1:2375` (из coder-контейнера).
|
||||
Compose-файл: `docker-compose.server.yml` (исторически `-p owl`).
|
||||
Скрипт: `scripts/deploy-production.sh`.
|
||||
|
||||
Публичный доступ в этой среде:
|
||||
@@ -47,6 +47,14 @@ curl -s http://127.0.0.1:3100/ | grep -Eo 'index-[A-Za-z0-9_-]+\.js'
|
||||
| `GET /details` (SPA) | 200 |
|
||||
| Bundle | `index-ncgt6PBL.js` |
|
||||
| Tunnel HTML | same bundle |
|
||||
| `https://arhipovdan.ru/` | **BLOCKED_EXTERNAL** — `PUBLIC_DOMAIN_DIAGNOSTIC.md` |
|
||||
|
||||
## Public smoke
|
||||
|
||||
```bash
|
||||
PLAYWRIGHT_BASE_URL=http://127.0.0.1:3100 npm run test:e2e:production
|
||||
PLAYWRIGHT_BASE_URL=https://invitations-based-characters-accent.trycloudflare.com/ npm run test:e2e:production
|
||||
```
|
||||
|
||||
## Rollback
|
||||
|
||||
@@ -61,4 +69,4 @@ docker start owl-web-1
|
||||
|
||||
## Smoke (tunnel)
|
||||
|
||||
Подтверждено: CASE 1/10, Jump to case 1–10, Previous/Next, Present, Play Demo.
|
||||
Подтверждено: CASE 1/10, Jump to case 1–10, Previous/Next, Present, Play Demo, `/details`.
|
||||
|
||||
122
docs/PUBLIC_DOMAIN_DIAGNOSTIC.md
Normal file
122
docs/PUBLIC_DOMAIN_DIAGNOSTIC.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# PUBLIC_DOMAIN_DIAGNOSTIC
|
||||
|
||||
Дата: 2026-07-15
|
||||
Окружение: coder container `coder-arhipov-owlprime` на хосте OwlPrime
|
||||
|
||||
## Verdict
|
||||
|
||||
```text
|
||||
BLOCKED_EXTERNAL
|
||||
```
|
||||
|
||||
Постоянный домен `https://arhipovdan.ru/` **не обслуживается** текущим production-контейнером `owl-web-1`. Приложение на `127.0.0.1:3100` здорово и отдаёт актуальный bundle; проблема на уровне DNS → чужой openresty на публичном IP.
|
||||
|
||||
## DNS
|
||||
|
||||
| Поле | Значение |
|
||||
| ---- | -------- |
|
||||
| Nameservers | `ns1.reg.ru.`, `ns2.reg.ru.` (REG.RU, **не** Cloudflare) |
|
||||
| A | `185.160.137.162` (TTL ~86400 у registrar) |
|
||||
| AAAA | **нет** |
|
||||
| CNAME | нет на apex; `www` тоже → `185.160.137.162` |
|
||||
| Cloudflare Proxy | **нет** (NS не Cloudflare) |
|
||||
|
||||
Команды:
|
||||
|
||||
```bash
|
||||
dig A arhipovdan.ru +short # 185.160.137.162
|
||||
dig AAAA arhipovdan.ru +short # (empty)
|
||||
dig NS arhipovdan.ru +short # ns1.reg.ru. ns2.reg.ru.
|
||||
```
|
||||
|
||||
## Сетевая топология (факт)
|
||||
|
||||
| Проверка | Результат |
|
||||
| -------- | --------- |
|
||||
| Egress IP хоста (ipify) | `185.160.137.162` (совпадает с A) |
|
||||
| `127.0.0.1:80` / `:443` на хосте | **closed** |
|
||||
| `185.160.137.162:80` / `:443` | **open**, отвечает **openresty** |
|
||||
| `127.0.0.1:3100` | **open**, `owl-web-1` nginx → `index-ncgt6PBL.js` |
|
||||
|
||||
Вывод: порт-форвард / другой хост за тем же публичным IP принимает 80/443. Это **не** namespace текущего Docker host listener set (на host net видны лишь `:3100` и `:8082`).
|
||||
|
||||
## TLS / HTTP к домену
|
||||
|
||||
| Проверка | Результат |
|
||||
| -------- | --------- |
|
||||
| `curl -4 http://arhipovdan.ru/` | **404** `Server: openresty` |
|
||||
| `curl -4 https://arhipovdan.ru/` | **TLS fail**: `tlsv1 unrecognized name` (нет сертификата/vhost SNI для имени) |
|
||||
| `curl -6` | нет AAAA → resolve fail |
|
||||
| Certificate / SAN | получить нельзя (handshake abort) |
|
||||
| HTML / bundle | недоступны через постоянный домен |
|
||||
|
||||
## Cloudflare Tunnel
|
||||
|
||||
| Поле | Значение |
|
||||
| ---- | -------- |
|
||||
| Type | **Quick Tunnel** only |
|
||||
| Process | `/usr/local/bin/cloudflared tunnel --url http://127.0.0.1:3100` (pid в coder) |
|
||||
| Named Tunnel | **не найден** (нет systemd unit, нет config.yml credentials) |
|
||||
| Permanent ingress for arhipovdan.ru | **отсутствует** |
|
||||
| Working public URL | `https://invitations-based-characters-accent.trycloudflare.com/` → bundle `index-ncgt6PBL.js` |
|
||||
|
||||
Quick Tunnel **не** является завершённым production DNS-решением.
|
||||
|
||||
## Точная причина 404 / TLS fail
|
||||
|
||||
1. DNS A указывает на `185.160.137.162`.
|
||||
2. На этом IP:80 отвечает **чужой openresty** без vhost `arhipovdan.ru` → HTTP 404.
|
||||
3. На IP:443 нет SNI-сертификата для `arhipovdan.ru` → `unrecognized_name`.
|
||||
4. Актуальный sorter (`owl-web-1`) слушает только `127.0.0.1:3100` и **не** связан с этим openresty.
|
||||
|
||||
## Что было исправлено в коде/инфре приложения
|
||||
|
||||
- Production container уже отдаёт `index-ncgt6PBL.js` на `:3100`.
|
||||
- Добавлены диагностика, production smoke script, документация (этот файл).
|
||||
- DNS/openresty **не** изменялись из этого окружения (нет доступа к REG.RU и к openresty vhost).
|
||||
|
||||
## Действия пользователя (точные)
|
||||
|
||||
### Вариант 1 — предпочтительно: Cloudflare Named Tunnel + смена NS/DNS
|
||||
|
||||
1. Открыть Cloudflare → Add site `arhipovdan.ru` **или** создать Named Tunnel в существующем CF-аккаунте.
|
||||
2. В REG.RU → DNS управления доменом: либо делегировать NS на Cloudflare, либо оставить REG.RU и создать запись, которую выдаст `cloudflared tunnel route dns`.
|
||||
3. Ingress tunnel:
|
||||
- hostname: `arhipovdan.ru` (+ `www`)
|
||||
- service: `http://127.0.0.1:3100` (если cloudflared на том же host net, что и publish `3100`)
|
||||
4. Выключить/не использовать Quick Tunnel как постоянный URL.
|
||||
5. Проверить:
|
||||
|
||||
```bash
|
||||
dig A arhipovdan.ru +short
|
||||
curl -I https://arhipovdan.ru/
|
||||
curl -s https://arhipovdan.ru/ | grep -Eo 'index-[A-Za-z0-9_-]+\.js'
|
||||
# ожидается: index-ncgt6PBL.js
|
||||
```
|
||||
|
||||
### Вариант 2 — починить openresty на том, кто слушает 185.160.137.162:80/443
|
||||
|
||||
1. На сервере с openresty добавить `server_name arhipovdan.ru www.arhipovdan.ru`.
|
||||
2. `proxy_pass http://<IP_этого_хоста_в_LAN>:3100;` (нужен LAN-доступ к OwlPrime:3100; сейчас publish только `127.0.0.1:3100` — потребуется сменить publish на `0.0.0.0:3100` или LAN IP **осознанно**).
|
||||
3. Выпустить TLS (certbot/acme) для SNI `arhipovdan.ru`.
|
||||
4. Проверить теми же curl-командами.
|
||||
|
||||
### Вариант 3 — сменить A-запись в REG.RU
|
||||
|
||||
```text
|
||||
Тип: A
|
||||
Имя: @ (и www)
|
||||
Старое значение: 185.160.137.162
|
||||
Новое значение: <IP, где реально слушает ваш reverse proxy / CF>
|
||||
Proxy status: DNS only (если не Cloudflare) или Proxied (если Cloudflare)
|
||||
```
|
||||
|
||||
## Проверочные команды после фикса
|
||||
|
||||
```bash
|
||||
curl -I https://arhipovdan.ru/
|
||||
curl -I https://arhipovdan.ru/details
|
||||
curl -I https://arhipovdan.ru/assets/index-ncgt6PBL.js
|
||||
curl -s https://arhipovdan.ru/ | grep -Eo 'index-[A-Za-z0-9_-]+\.js'
|
||||
PLAYWRIGHT_BASE_URL=https://arhipovdan.ru/ npm run test:e2e:production
|
||||
```
|
||||
@@ -13,27 +13,27 @@
|
||||
|
||||
| ID | Риск | Вероятность | Влияние | Статус | Митигация |
|
||||
| -- | ---- | ----------- | ------- | ------ | --------- |
|
||||
| R01 | Production :3100 / https://arhipovdan.ru/ отдаёт старый dist | High | Critical | Open | `demo:health`; ручной redeploy; показывать :3101 при сомнении |
|
||||
| R02 | Визуальный разрыв `/` vs `/details` путает жюри | High | High | Open | Вести показ на `/`; unify twins в плане |
|
||||
| R01 | Production :3100 / https://arhipovdan.ru/ отдаёт старый dist / домен 404 | High | Critical | Open | Local+tunnel PASS; domain BLOCKED_EXTERNAL — `PUBLIC_DOMAIN_DIAGNOSTIC.md` |
|
||||
| R02 | Визуальный разрыв `/` vs `/details` путает жюри | Medium | High | Mitigated | `sharedLayout` + routeConsistency tests |
|
||||
| R03 | Жюри воспринимает кинематику как «фейк» | Medium | High | Mitigated | Proof HUD + честные формулировки; jam/e-stop |
|
||||
| R04 | Pseudo-CV раскрыт как «обман» | Medium | Medium | Accepted | RULE overlay + confidence; не обещать ML |
|
||||
| R05 | Просадка FPS на ноутбуке жюри | Medium | High | Mitigated | quality modes; shadows/effects off |
|
||||
| R06 | Регрессия classifier/min dims | Low | Critical | Mitigated | tests 153; measurement+DIMENSION_LIMITS |
|
||||
| R05 | Просадка FPS на ноутбуке жюри | Medium | High | Mitigated | quality modes; `?perf=1`; SwiftShader ≠ GPU |
|
||||
| R06 | Регрессия classifier/min dims | Low | Critical | Mitigated | tests 166; measurement+DIMENSION_LIMITS |
|
||||
| R07 | Fault freeze без recover ломает показ | Low | High | Mitigated | recover path + hotkeys R/N |
|
||||
| R08 | Agent/фоновые job портят ресурсы во время демо | Medium | High | Mitigated | kill switch; runbook stop |
|
||||
| R09 | Будущий auto-patch агента ломает main | Low (сейчас) | Critical | Mitigated | forbid merge/deploy; MVP verify-only |
|
||||
| R10 | Утечка секретов `.env` в отчёты/логи | Low | Critical | Mitigated | policy forbidSecretAccess; не документировать values |
|
||||
| R11 | Нет e2e в CI — UI регрессия незамечена | Medium | Medium | Open | ручные scripts; план CI 1 worker |
|
||||
| R11 | Нет e2e в CI — UI регрессия незамечена | Low | Medium | Mitigated | CI + 10 visual snapshots; production smoke manual |
|
||||
| R12 | OOM при локальной LLM + Playwright | Medium | High | Accepted (avoid) | Variant C; не совмещать |
|
||||
| R13 | Disk fill отчётами/скриншотами | Low | Medium | Open | ротация reports |
|
||||
| R14 | Нет docker CLI в operator env | High (факт) | Medium | Accepted | внешний deploy path документирован |
|
||||
| R14 | Нет docker CLI в operator env | High (факт) | Medium | Accepted | `DOCKER_HOST=tcp://127.0.0.1:2375` |
|
||||
| R15 | Swap thrash под нагрузкой | Low | High | Mitigated | лимиты 1 worker; demo mode без фона |
|
||||
| R16 | Расхождение playlist expectedCategory и classifyItem | Low | High | Mitigated | wire classifyItem; tests |
|
||||
| R17 | STL/fallback выглядят «игрушечно» | Medium | Low | Accepted | modelAssets notes; backlog textures |
|
||||
| R18 | Tunnel/cloudflared outage | Low | Critical | Open | fallback loopback :3100 / LAN preview |
|
||||
| R18 | Tunnel/cloudflared outage / permanent DNS | High | Critical | Open | Quick Tunnel temp; Named Tunnel + REG.RU DNS required |
|
||||
| R19 | Несогласованность документации и кода | Medium | Low | Mitigated | этот пакет docs = snapshot 2026-07-15 |
|
||||
| R20 | Попытка «добавить physics» перед показом → регрессия | Medium | High | Open | запрет P0-physics перед demo day |
|
||||
|
||||
| R21 | Hardware GPU baseline недоступен в coder | High | Medium | Accepted | NVIDIA есть, Chromium→SwiftShader; измерять на demo laptop |
|
||||
---
|
||||
|
||||
## 2. Детали по критическим рискам
|
||||
|
||||
63
docs/TWIN_GEOMETRY_ALIGNMENT.md
Normal file
63
docs/TWIN_GEOMETRY_ALIGNMENT.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# Twin Geometry Alignment
|
||||
|
||||
## Goal
|
||||
|
||||
Keep `/` (continuous twin) and `/details` (state-machine twin) as projections of one sorter layout — same belt height, zone X/Z, sensors, gate, and category colors — without rewriting both scenes or enabling a physics engine.
|
||||
|
||||
## What was unified
|
||||
|
||||
| Concern | Source of truth | Consumers |
|
||||
| --- | --- | --- |
|
||||
| Conveyor dims, `BELT_TOP_Y` (0.7 m), zones A/B/C/D, gate, roll cages | `src/domain/physicalLayout.ts` | Continuous twin, `conveyorNetwork`, cameras |
|
||||
| Shared bridge + details-derived layout | `src/domain/layout/sharedLayout.ts` | Details `TWIN_LAYOUT`, route/color tokens |
|
||||
| Category / route colors | `src/domain/industrialTheme.ts` → `CATEGORY_COLORS` | Both twins, details `ROUTE_COLORS` |
|
||||
| Class route directions (B→+X, C→+Z, D→−Z) | `CLASS_ROUTE_DIRECTIONS` in `sharedLayout` | Documentation / consistency |
|
||||
|
||||
### Details twin derivation
|
||||
|
||||
`itemMotion.ts` no longer hardcodes a compact layout (`beltY: 0.35`, divergent X). It exports:
|
||||
|
||||
```ts
|
||||
TWIN_LAYOUT = DETAILS_TWIN_LAYOUT // from sharedLayout
|
||||
ROUTE_COLORS = CATEGORY_COLORS
|
||||
PHYSICS_ENGINE_ENABLED = false
|
||||
```
|
||||
|
||||
Aligned meters (vs previous details values):
|
||||
|
||||
- `beltY` → `BELT_TOP_Y` (0.7)
|
||||
- `startX` / `zoneBX` / `zoneCZ` / `zoneDZ` → `ZONES.A/B/C/D`
|
||||
- `cameraX` / `laserX` / `gateX` → `ZONES.CAMERA/LASER/GATE`
|
||||
- `rollCageSize` → `ROLL_CAGE`
|
||||
- Progress keyframes (`progressForState`) unchanged — only absolute meters moved
|
||||
|
||||
Details scene helpers (conveyor materials, zone pads, sensor poles, label Y) use `TWIN_LAYOUT.beltY` or `INDUSTRIAL_PALETTE` so the presentation layer tracks the shared height.
|
||||
|
||||
### Consistency test
|
||||
|
||||
`src/domain/routeConsistency.test.ts` asserts for each non-fault `DEMO_PLAYLIST` case:
|
||||
|
||||
1. `classifyItem` category === `expectedCategory`
|
||||
2. Continuous playback `targetCategory` matches after seek
|
||||
3. `CATEGORY_COLORS` has an entry for that category
|
||||
4. `getPhysicalItemPose` at mid-routing has `activeRoute` === category and `phase === 'routing'`
|
||||
|
||||
## Intentional differences (keep)
|
||||
|
||||
| Continuous (`/`) | Details (`/details`) |
|
||||
| --- | --- |
|
||||
| Engineering HUD: measurement, events, physical surfaces from `conveyorNetwork` | Presentation / teaching twin: state-machine progress keyframes |
|
||||
| Deterministic `getPhysicalItemPose` along network surfaces | `itemPosition3D` progress along a simplified X (and Z for C/D) |
|
||||
| Light industrial floor / grid (`INDUSTRIAL_PALETTE` light) | Dark engineering projection of the same palette |
|
||||
| Full B receiver spur + chutes + cage floors | Simplified zone boxes + roll-cage visuals |
|
||||
| Ultrasonic not a separate continuous station | Details-only `ultrasonicX` between laser and gate |
|
||||
| Cinematic camera / playlist director | OrbitControls + machine-state HUD chips |
|
||||
|
||||
Motion remains **state-machine / kinematic** only (`PHYSICS_ENGINE_ENABLED === false`). Do not enable a physics engine for alignment.
|
||||
|
||||
## How to extend
|
||||
|
||||
1. Change geometry in `physicalLayout.ts` (or zone map there).
|
||||
2. Re-export / map in `layout/sharedLayout.ts` if details needs a new derived field.
|
||||
3. Keep details keyframe ratios in `itemMotion.ts` unless timing intentionally changes.
|
||||
4. Prefer `INDUSTRIAL_PALETTE` / `CATEGORY_COLORS` for new materials.
|
||||
Reference in New Issue
Block a user