feat: ship production deploy, Playwright e2e, and agent implementer

Add Docker atomic deploy tooling, CI, Playwright smoke/visual suite,
shared industrial theme, physics invariants, quality-mode wiring,
and a worktree-isolated agent implementer MVP with hard safety limits.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Даня Архипов
2026-07-15 17:14:01 +00:00
parent 985f7c327d
commit b5985f33bb
34 changed files with 2076 additions and 71 deletions

View File

@@ -0,0 +1,124 @@
# Implementer MVP (worktree-isolated)
**Status:** Implemented in `agent/cli.mjs`
**Date:** 2026-07-15
Safe, deterministic Implementer cycle that **does not** require an external LLM.
Patches run only inside a git worktree. The agent **never** merges to `main`, **never** deploys, and **never** pushes.
---
## Commands
| Command | Purpose |
| ------- | ------- |
| `dry-run` | Plan only (baseline + propose) |
| `run-once` | Verify baseline only (no patches) |
| `implement [--task <path>]` | Full Implementer cycle |
| `start` | Same as implement, but requires `AUTONOMOUS_AGENT_ENABLED=true` |
| `status` / `stop` / `resume` / `pause` / `report` | Control plane |
```bash
# Recommended successful demo cycle
node agent/cli.mjs implement --task agent/tasks/demo-docs.json
# Or via helper script
bash scripts/agent-implement.sh --task agent/tasks/demo-docs.json
# Gated continuous entry (still one cycle in MVP)
AUTONOMOUS_AGENT_ENABLED=true node agent/cli.mjs start --task agent/tasks/demo-docs.json
```
---
## Implementer cycle
1. **Watchdog** — kill switch, single lock, disk, load, memory
2. **Plan** — load task JSON (default: `agent/tasks/demo-docs.json`)
3. **Validate policy** — allowlist only: `docs`, `tests`, `data-testid`, `a11y`, `small-ui`, `logging-scripts`
4. **Create worktree** — `.agent/worktrees/<run-id>` on branch `agent/<run-id>/<task-slug>`
5. **Apply bounded patch** — e.g. create `docs/AGENT_CYCLE_DEMO.md`
6. **Enforce limits** — ≤12 files, ≤800 diff lines, ≤25 min cycle
7. **Verify** — `npm test && npm run build` inside the worktree
8. **Score + decide** — `ACCEPT_CANDIDATE` or `REJECT`
9. **Report** — `agent/reports/<run-id>.json` (+ `latest.json`)
### On REJECT
- Remove worktree
- Delete local agent branch
- Keep the JSON report
### On ACCEPT_CANDIDATE
- Keep worktree + branch
- Print manual review instructions
- Human must cherry-pick / merge into a feature branch if desired
---
## Watchdog
| Check | Fail condition |
| ----- | -------------- |
| Lock | `agent/state/agent.lock` held by a live PID (second agent refused) |
| Kill switch | `agent/state/KILL` present |
| Disk | `df /` usage **> 85%** |
| Load | loadavg 1m **> nproc** |
| Memory | MemAvailable **< 1 GiB** |
```bash
node agent/cli.mjs stop # write KILL
node agent/cli.mjs resume # clear KILL
node agent/cli.mjs status # lock + watchdog snapshot
```
---
## Example task
`agent/tasks/demo-docs.json` — docs-only safe demo:
- Policy: `docs`
- Patch: create `docs/AGENT_CYCLE_DEMO.md`
- No source / physics / deploy / `.env` changes
---
## Limits (also in `LIMITS` in `cli.mjs`)
| Limit | Value |
| ----- | ----: |
| max cycle | 25 min |
| max files | 12 |
| max diff lines | 800 |
| parallel agents | 1 |
Hard forbids: merge to main, production deploy, push, secret/`.env` access.
---
## Manual review after ACCEPT
```bash
# Inspect
cat agent/reports/latest.json
git -C .agent/worktrees/<run-id> show HEAD
# Accept into your feature branch yourself (example)
git cherry-pick <agent-commit-sha>
# Or discard
git worktree remove --force .agent/worktrees/<run-id>
git branch -D agent/<run-id>/demo-docs
```
---
## What this MVP does **not** do
- Call external LLMs
- Modify production nginx/docker/deploy
- Touch `.env`
- Auto-merge or auto-deploy
- Change demo physics (prefer docs-only demo task)

View File

@@ -0,0 +1,35 @@
# PERFORMANCE_AFTER_MAXIMUM_DEMO
Measured: 2026-07-15T17:11:06.031Z
Commit: `985f7c3`
## Bundle (measured)
| Метрика | Значение |
| ------- | -------: |
| 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 |
## Runtime (Playwright Chromium headless)
| Метрика | До | После | Цель | Статус |
| ------- | -: | ----: | ---: | ------ |
| 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 |
## 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

View File

@@ -0,0 +1,95 @@
# 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 |
## 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 |
Examples:
```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`)
## 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) |
Stable selectors live on `MainPage` as `data-testid` values (`demo-play`, `demo-hud`, `demo-case-{n}`, etc.).
## 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
```
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.
Snapshots intentionally target **`[data-testid="demo-hud"]`** so WebGL canvas noise does not fail CI.
## System deps
On a fresh Linux host, Chromium may need OS libraries:
```bash
npx playwright install chromium
npx playwright install-deps chromium
```
## Artifacts (gitignored)
- `test-results/` — failure screenshots / traces
- `playwright-report/` — HTML report
- `blob-report/` — blob reporter output
`}

View File

@@ -0,0 +1,64 @@
# PRODUCTION_DEPLOYMENT_REPORT
Дата: 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`).
Скрипт: `scripts/deploy-production.sh`.
Публичный доступ в этой среде:
- Loopback: `http://127.0.0.1:3100/`
- Quick tunnel cloudflared: `https://invitations-based-characters-accent.trycloudflare.com/`
- `https://arhipovdan.ru/` из контейнера даёт TLS SNI / openresty 404 — DNS/прокси домена вне текущего coder-окружения; проверяйте снаружи.
## Releases
| Поле | Значение |
| ---- | -------- |
| Old release container | `owl-web-1-backup-20260715-165347` / `…-1712` |
| Old bundle | `index-CCdZzxPJ.js` |
| New release | `20260715-1712` |
| New image | `owl-web:20260715-1712` |
| New bundle | `index-ncgt6PBL.js` |
| Backup HTML | `releases/backup-pre-*` |
## Команды
```bash
export DOCKER_HOST=tcp://127.0.0.1:2375
cd /home/coder/arhipovdan/app
npm test && npm run build
bash scripts/deploy-production.sh 20260715-1712
curl -s http://127.0.0.1:3100/ | grep -Eo 'index-[A-Za-z0-9_-]+\.js'
```
## Healthcheck
| Check | Result |
| ----- | ------ |
| `GET /` | 200 |
| `GET /details` (SPA) | 200 |
| Bundle | `index-ncgt6PBL.js` |
| Tunnel HTML | same bundle |
## Rollback
```bash
export DOCKER_HOST=tcp://127.0.0.1:2375
docker stop owl-web-1 && docker rm owl-web-1
docker rename owl-web-1-backup-20260715-1712 owl-web-1
docker start owl-web-1
```
Или восстановить HTML из `releases/backup-pre-<release>/`.
## Smoke (tunnel)
Подтверждено: CASE 1/10, Jump to case 1–10, Previous/Next, Present, Play Demo.