docs: README и замер Lighthouse mobile с прода

Performance 99, Accessibility 100, Best Practices 100, SEO 100
(медиана трёх прогонов, LCP 1.7 с, CLS 0).
This commit is contained in:
2026-10-17 17:37:21 +03:00
parent d8c09c900f
commit 523519fe40
3 changed files with 13382 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
# Кварта — лендинг обжарки с формой заявки
Тестовое задание [tz.vitai.world](https://tz.vitai.world/). Клиент вымышленный.
- Сайт: **https://kvarta.unuchak.com**
- Lighthouse mobile (прод, медиана трёх прогонов): **Performance 99 · Accessibility 100 · Best Practices 100 · SEO 100**; LCP 1.7 с, CLS 0, TBT 10 мс — отчёт `docs/lighthouse/mobile.html`
- Лог работы с ИИ: `docs/ai-session.md` (экспорт `/export` из Claude Code); журнал ошибок ИИ — `docs/ai-log.md`
## Что сделано
Лендинг «недельный календарь / штамп даты»: Кварта — «четвёртая», обжарка по четвергам; ассортимент свёрстан как этикетки пачек. Кнопки предзаполняют форму, на мобиле — липкая CTA. Форма уходит в отдельный сервис api: проверка белорусского телефона (любой ввод → `+375XXXXXXXXX`, одна функция на клиенте и сервере), повтор с того же номера в течение 10 минут дописывается к заявке как уточнение, сохраняются первое и последнее касание (utm, yclid, fbclid, referrer, страницы входа и отправки), rate-limit, honeypot, понятные ошибки с «позвоните». Заявки менеджер видит в теме Telegram-группы, сбои — в соседней теме «Система»; бэкапы базы каждый день уходят администратору в личку, восстановление — кнопкой в боте. 184 теста Vitest.
Стек: pnpm workspace — `apps/landing` (Next.js 16, Tailwind 4), `apps/api` (Hono, better-sqlite3), `packages/shared` (телефон и контракт заявки). Почему два сервиса — `docs/adr/0001`.
**Почему SQLite.** Один процесс и один файл в volume: проверка «есть ли заявка с этим номером за 10 минут» и запись идут в одной транзакции, и двойной клик не даёт дубля; бэкап — копия файла; база не зависит от Telegram — если он недоступен, заявка ждёт в очереди и уходит позже (так и случилось вживую при настройке). Внешняя БД для десятков заявок в день — лишняя движущаяся часть.
## Кто что делал
Я — решения и контроль: план согласован в интервью (`docs/plan.md`, `GLOSSARY.md`, ADR), выбор референсов, шрифтов и фото, вычитка всех текстов, настройка Telegram-группы и Tailscale, приёмка каждого шага. ИИ (Claude Code) — весь код, тексты-черновики, дизайн и деплой, разделённые между агентами: оркестратор (план, `shared`, коммиты, всё на сервере), api, landing-функционал, deploy, исследователь референсов, копирайтер, дизайнер, независимый критик по скриншотам (3 итерации: 7 → 8 → 9 из 10), аудитор. Инструменты: [impeccable](https://github.com/pbakaus/impeccable) (детектор AI-паттернов и audit), [slopotron](https://github.com/beaverbeard/slopotron) (чистка русских текстов).
## Где ИИ ошибся и как заметили
Полный журнал — `docs/ai-log.md` (каждая запись: ошибка → чем поймали → исправление). Самое показательное:
- **Не проверил окружение.** План исходил из того, что VPS ходит в api.telegram.org, — с сервера в РФ Telegram недоступен; бот молчал после `/setup`. Добавлен ретранслятор Bot API на домашнем сервере через Tailscale (`deploy/tg-relay`).
- **Rate-limit считал всех одним посетителем** — дважды: сначала в плане не учли, что nginx приходит в контейнер со шлюза docker-сети, потом Node отдал этот адрес как `::ffff:172.31.120.1`. Тесты проходили; поймал прогон на проде с двумя разными `X-Real-IP`.
- **Картинки не сжимались.** Sharp подключён по устаревшей документации Next; Next 16 молча отдавал исходный JPEG 838 КБ. Поймал Lighthouse на проде.
- **Самопроверка дизайнера по своим скриншотам** пропустила полупрозрачную подложку штампа, запрещённые его же DESIGN.md «hero-метрики» и двойной повтор «ЧТ» на каждой этикетке — нашёл только независимый критик без доступа к коду.
- **Оркестратор закоммитил чужую работу** (`git add -A` в чекауте, где агент верстал) — шаг 12 уехал в коммит про Telegram; история переписана с моего согласия.
## Допущения
- Хостинг — свой VPS, а не бесплатный: там уже есть nginx и certbot, и на нём работает SQLite с бэкапами.
- Стоимость доставки при заказе до 50 BYN в ТЗ не указана — «уточнит менеджер». Телефон +375 29 000-00-00 демонстрационный; Instagram и e-mail в ТЗ нет — на сайте их нет.
- «Кварта = четвёртая, обжарка по четвергам» — идея бренда, не факт из ТЗ. Штамп «дата обжарки» — визуальный приём, текст не утверждает, что дата стоит на пачке.
- Фото — Unsplash (авторы в подвале), остальное SVG.
## Что не успел и что сделал бы иначе
- Без JS кнопки CTA не работают, а форма не сохраняет заявку (уходит POST на страницу — данные хотя бы не попадают в URL). Нужен серверный приём `application/x-www-form-urlencoded`.
- Нет skip-link; при увеличении текста до 200% на мобиле появляется горизонтальный скролл (фиксированные px-ширины ленты недели и этикеток).
- Нет браузерного e2e-теста формы (Playwright) — форма проверена вручную, логика — юнит- и интеграционными тестами.
- Telegram зависит от домашнего сервера с ретранслятором. Иначе: VPS не в РФ или собственный Bot API server.
- Rate-limit в памяти процесса — сбрасывается при рестарте api.
- Сделал бы иначе: сразу изолировать параллельных агентов в отдельных worktree и проверять окружение (сеть до Telegram, Tailscale, эмуляция amd64) до плана, а не по ходу.
Подробнее: деплой и эксплуатация — `docs/deploy.md`, шаблоны ссылок для Директа и Instagram — `docs/links.md`, дизайн-система — `DESIGN.md`.