Перейти к содержимому

F-11 Build Debugging — 4 build issues уrok

Date: 2026-05-18 Task: Closing F-11 после того как agent ac1b2447 написал code но Docker build падал 8 commits подряд

Context

Agent написал большую часть F-11 (~2.7h actual): Next.js 16.2 + Tailwind 4 + shadcn + Phosphor + brand tokens + modules registry + auth skeleton + 9 страниц + 4 vitest tests + 5 playwright e2e. Build падал на VPS, agent timed out 403s. Я (interactive LLM) finalize за ~1.5h.

4 Issues найдено + fixed

1. Turbopack ignores turbopack.root в pnpm monorepo (Next.js bug #92540)

Symptom:

Error: Turbopack build failed
./src/app
Error: Next.js inferred your workspace root...
We couldn't find the Next.js package (next/package.json) from
the project directory: /app/apps/web/src/app

Root cause: known regression в Next.js 16.2.x — Turbopack ignores turbopack.root config в pnpm monorepos. Issue vercel/next.js#92540.

Fix: build с --webpack flag (Next 16 still ships webpack fallback). Updated:

  • apps/web/Dockerfile: pnpm exec next build --webpack
  • apps/web/package.json: "build": "next build --webpack"

Phase 1.5 TODO: revisit когда Next 16.3+ зафиксит upstream → вернуть Turbopack.

2. .gitignore line 15 lib/ greedily matched apps/web/src/lib/

Symptom: Webpack build (после Issue #1 fix) выдал:

Module not found: Can't resolve '@/lib/utils'
Module not found: Can't resolve '@/lib/auth/jwt'
Module not found: Can't resolve '@/lib/api/client'
Module not found: Can't resolve '@/lib/api/queries'

Root cause: Repo root .gitignore имел lib/ (Python setuptools artifacts pattern из стандартного Python .gitignore). Greedy unanchored pattern → matches любой lib/ dir, включая apps/web/src/lib/. Все 5 файлов (utils.ts, theme/tokens.ts, auth/jwt.ts, api/client.ts, api/queries.ts) никогда не были checked into git. Локально build работал (файлы в working dir), но Docker COPY копировал только git-tracked → missing.

Fix: changed lib//lib/ (anchored к repo root only). Commit 5 missing файлов.

General lesson для пользователя: always check git ls-files after agent создаёт код в типовых dirs (lib/, build/, dist/, vendor/, target/) — могут быть caught Python/Node/Java .gitignore patterns.

3. Next standalone не bundles deps в pnpm monorepo → “Cannot find module ‘next’”

Symptom: Container start crash loop:

Error: Cannot find module 'next'
Require stack: /app/server.js

Root cause: Next standalone build с pnpm + monorepo не находит deps т.к. /app/node_modules/next это symlink на ../../../node_modules/.pnpm/next@.../. В runtime stage скопирован только .next/standalone, без .pnpm/ store.

Fix: добавил outputFileTracingRoot: monorepoRoot в next.config.mjs. Next traces все workspace deps и bundles в .next/standalone/node_modules/. После fix server.js mirrors monorepo path → /app/apps/web/server.js (а не /app/server.js как было до tracing root).

4. Healthcheck wget connects к ::1 (IPv6), server bind 0.0.0.0 (IPv4)

Symptom: Container running, HTTP работает через Docker network, но healthcheck reports “unhealthy”:

wget: can't connect to remote host: Connection refused

Root cause: node:22-alpine wget resolves localhost::1 (IPv6). Next.js server binds 0.0.0.0:3000 (IPv4 only). IPv6 connection refused.

Fix: explicit http://127.0.0.1:3000/ в healthcheck test.

Bonus: Caddyfile bind-mount inode mismatch

После editing /opt/razmakh-stack/Caddyfile host inode changed (atomic write), container’s bind-mounted /etc/caddy/Caddyfile всё ещё old inode = old content. caddy reload re-reads file but file inside container ≠ file on host.

Fix: docker compose restart caddy — перемонтировал bind mount = новый inode.

Bonus: CI deploy stage wrong paths

.github/workflows/ci.yml deploy step had multiple legacy issues:

  • cd /opt/razmakh-stack && git pull — stack dir не git repo (repo at /opt/razmakh/repo)
  • docker compose build razmakh-api — actual service name api
  • Не билдил/перезапускал web → F-11 frontend никогда бы не deploy’ился
  • “dubious ownership” т.к. repo owned by root → нужен sudo

Fixed на правильные пути + sudo + добавил web build/up/healthcheck wait.

General lessons

  1. Verify git tracking после любого нового кода в стандартных dirs: git ls-files <new-dir>/ — если empty значит gitignore catched. Особенно для multi-language repos (Python .gitignore patterns affect JS!).

  2. Workaround known framework bugs быстро: Don’t fight Turbopack #92540 — --webpack flag решает за 1 commit, vs days of trying configs. Add TODO comment + Phase X.Y revisit.

  3. Test locally в близких к prod условиях: Local pnpm install использует strict checks которые agent’s Docker не имеет. Bypass next build напрямую через node node_modules/next/dist/bin/next build если pnpm install сам в pre-check падает.

  4. Bind mounts не survive atomic-write file changes: After mv tempfile target, container всё ещё видит старый file (старый inode). Restart container для re-mount.

  5. Alpine wget defaults to IPv6: Если Node binds 0.0.0.0, в healthcheck use explicit 127.0.0.1 или localhost4.

Time

  • Plan: 2-3 дня (16-24h)
  • Agent (ac1b2447): ~2.7h
  • Finalization (me): ~1h
  • Total actual: ~3.7h ≈ 6× faster than plan

Estimates-recalibration ratio holds: LLM-driven dev = plan/6-8.

Files affected

  • apps/web/Dockerfile (multiple commits)
  • apps/web/next.config.mjs (outputFileTracingRoot)
  • apps/web/package.json (—webpack flag)
  • .gitignore (anchor /lib/)
  • apps/web/src/lib/* (5 files added — were ignored)
  • services/docker-stack/docker-compose.yml (healthcheck 127.0.0.1)
  • .github/workflows/ci.yml (deploy stage rewrite)