512 lines
56 KiB
Markdown
512 lines
56 KiB
Markdown
# Sunprint — рабочее состояние (STATE.md)
|
||
|
||
> **Что это.** Единый источник истины о статусе работ. Любой Claude (Code,
|
||
> claude.ai, новая модель, новый разработчик) читает этот файл первым делом
|
||
> и за 60 секунд понимает, где мы и куда идём.
|
||
>
|
||
> **Где живёт.** Корень репо `print-calc` + дубликат загружен в Project
|
||
> Knowledge проекта Sunprint в claude.ai. Один файл — одна правда. Не
|
||
> плодить параллельные «активный трек», «текущая задача», «next.md».
|
||
>
|
||
> **Кто обновляет.** Claude в любой среде — последним действием перед
|
||
> закрытием сессии. Если изменений по треку не было — просто ставит новую
|
||
> дату и пишет «без изменений».
|
||
|
||
---
|
||
|
||
**Обновлено:** 2026-04-30, Claude Code (Opus 4.7) — гигиена закрыта (push 0e53251 + observation v8.1 closed + snapshot rm), готовы к Dieline Фаза 1
|
||
**Длительность сессии:** в процессе (30.04: pre-flight ✅ → push 0e53251 → observation closed → snapshot+.htpasswd rm → правило #7 + accepted-risk фиксация)
|
||
**Предыдущая сессия:** 2026-04-29, Claude Code (Opus 4.7), ~6ч — Gitea CE задеплоен на git.suntask.ru, public+private repos, web_fetch workflow через commit-pinned URL
|
||
|
||
> **Заметка о merge:** этот файл собран из двух источников — базой взят архив `2026-04-29_09-04-38_STATE_post-basicauth.md` (md5 `acda575a5dc67402169f8586af2a0563`), сверху наложены только новые блоки сессии 29.04 (dieline-трек, два бэклог-ряда, урок №12, лог-строка, связанные документы по dieline, новый риск про устаревший Project Knowledge). Регрессия зафиксирована как риск ниже.
|
||
|
||
---
|
||
|
||
## 🎯 Активный трек
|
||
|
||
**Observation v8.1 закрыт ✅. Готов к Dieline Фаза 1.**
|
||
|
||
Все большие треки 27-29 апреля закрыты (БАГ колод v8.1, SSO bridge, Basic Auth снят, Gitea CE деплой). Гигиена 30.04: snapshot v8.1 удалён, .htpasswd убран, observation чек-лист пройден (0 ошибок за 48h, `2×200 + 1×400 + 0×500` на `/api/v1/calculate`), правило #7 (accepted risk) добавлено в Security политику.
|
||
|
||
**Следующий шаг:** запуск **Dieline Фаза 1** (локальный плейграунд в `demo.html`, расширение каталога под микрогофру, AST-парсер, валидация геометрии). Полный план — раздел «🆕 Трек: Конструктор коробок» ниже, ~1-2 недели локально.
|
||
|
||
Альтернативные кандидаты (если приоритет сместится):
|
||
- 🟡 `/admin/approvals` UI (~3ч, владельческий функционал)
|
||
- 🟢 v8.2 rebase под v8.1 pipeline (НДС 22% / шелкография / equipment)
|
||
|
||
---
|
||
|
||
## 🆕 Трек: Конструктор коробок (dieline generator)
|
||
|
||
**Тип задачи:** новая большая фича для print-calc. Параметрический генератор
|
||
развёрток упаковки. Интегрируется в print-calc как `_type: 'box_construction'`
|
||
по аналогии с `card_deck` (см. Уроки 1-4).
|
||
|
||
### Контекст
|
||
|
||
- Передан полный handoff-пакет от другой сессии: `README.md`, `ARCHITECTURE.md`,
|
||
`SCHEMA.md`, `ROADMAP.md`, `INTEGRATION.md`, `DECISIONS.md`, `catalog.json`,
|
||
`core.js`, `dxf-export.js`, `demo.html`. Все в Project Knowledge claude.ai.
|
||
- Текущее состояние: **v0.4**, vanilla JS без сборки, 7 конструкций в 4
|
||
категориях (Slotted/Sleeve/Tray/Insert), параметрический редактор, SVG +
|
||
DXF (R2000, слои Cut/Crease, дуги через bulge — production-ready).
|
||
- Сейчас цена коробки в Sunprint считается **вручную** — задача калькулятора
|
||
как раз перенести эту логику.
|
||
|
||
### Решение оператора (зафиксировано 2026-04-29)
|
||
|
||
1. **Целевой нишевый сегмент:** самосборные коробки из микрогофры (подарочные,
|
||
под визитки, мерч). НЕ полный FEFCO-200 — топ 5-15 конструкций под Sunprint.
|
||
2. **Архитектура:** путь Б — встроить в print-calc как новый тип продукта.
|
||
3. **Связан с расчётом цены:** да, ожидается в ближайшее время.
|
||
4. **Режим работы:** «локально → потом часть сайта». Фазируем (см. ниже).
|
||
|
||
### Фаза 1 — локальный плейграунд (1-2 недели)
|
||
|
||
Без касания print-calc, без БД, без сервера. Code работает в `demo.html`
|
||
на машине оператора:
|
||
|
||
- **Расширение каталога** до топ-N конструкций под микрогофру (сначала 3-5,
|
||
потом 8-12). Каждая верифицируется по правилу ROADMAP: распечатать 1:1,
|
||
вырезать, сложить, проверить зазоры — час работы на конструкцию.
|
||
- **AST-парсер выражений** взамен `new Function()`. До многопользовательской
|
||
БД нельзя дальше с `eval`-эквивалентом. Реалистично — `expr-eval` (10KB
|
||
готовая либа) или ~300 строк своего PEG-парсера. См. ROADMAP уровень 1.
|
||
- **Bleed offset** если печать на коробках планируется (вопрос открыт).
|
||
- **Punch holes / окна** если нужны для подарочных конструкций.
|
||
- **Метки регистрации** — обязательны для офсетной/флексо-печати, тривиально.
|
||
- **Валидация геометрии** перед production: замкнутость контура,
|
||
самопересечения, влезание на лист, толщина в зазорах. См. ROADMAP уровень 1.
|
||
- **Выбор итоговых 5-15 конструкций** — приоритет по реальным заказам Sunprint
|
||
(рекомендуется ревью истории в SunTask на этапе планирования).
|
||
|
||
Артефакт Фазы 1: устойчивая локальная версия + JSON-каталог, который можно
|
||
без переделок мигрировать в БД print-calc.
|
||
|
||
### Фаза 2 — интеграция в print-calc (3-5 сессий)
|
||
|
||
Когда Фаза 1 даёт устойчивый каталог — переносим в Sunprint-стек:
|
||
|
||
- **Prisma миграция:** новая таблица `BoxConstruction` (или JSON-колонка
|
||
`geometryDef` в существующей `Template` — решение по «Открытому вопросу 3»).
|
||
- **Pipeline-handler:** новый case `box_construction` в `prepareEngineInput.ts`.
|
||
По аналогии с `card_deck`: UI шлёт `templateSlug + parameters`, pipeline
|
||
резолвит шаблон через `getTemplateBySlug()`, считает геометрию через
|
||
портированный TS-evaluator, превращает её в engine-input для расчёта цены.
|
||
- **Engine остаётся семантически тупым** (Урок 2). Цена считается от
|
||
`tirage` (количество коробок) + площади развёртки + материала. Геометрия
|
||
уходит в `_meta` для UI и для DXF.
|
||
- **UI-форма:** в `Calculator.tsx`/`ManagerCalculator.tsx` при
|
||
`extraFields._type === 'box_construction'` — рисуем поля параметров (L/W/D/t
|
||
и т.п.), live-превью SVG, кнопка «Скачать DXF».
|
||
- **DXF на скачивание:** генерируется на бэке (Node `dxf-export.ts`,
|
||
портированный из `dxf-export.js`). Клиент получает либо ссылку, либо
|
||
файл-аттач к КП. Решение по «Открытому вопросу 4».
|
||
- **Тесты:** prepareEngineInput тесты по образцу card_deck (8-12 кейсов),
|
||
отдельно тесты geometry-evaluator (10-15 кейсов с golden DXF/SVG).
|
||
- **Обязательные коды ошибок** (Урок 4): `BOX_PARAMS_REQUIRED`,
|
||
`BOX_TEMPLATE_NOT_FOUND`, `BOX_GEOMETRY_INVALID`, `UNHANDLED_TEMPLATE_TYPE`.
|
||
|
||
### Открытые решения для Фазы 2 (TBD)
|
||
|
||
| # | Решение | Влияние | Когда решать |
|
||
|---|---|---|---|
|
||
| 1 | Топ-5/10/15 конструкций под Sunprint | объём Фазы 1 | до старта Фазы 1, требует ревью SunTask-заказов |
|
||
| 2 | Модель цены: площадь / FEFCO-прайс / раскладка на лист | сложность handler'а | до старта Фазы 2 |
|
||
| 3 | Каталог: JSON в репо / `BoxConstruction` Prisma / JSON-колонка в `Template` | миграция + UI правок | до старта Фазы 2 |
|
||
| 4 | DXF: ссылка / аттач к КП / отправка в производство напрямую | API + UI правок | до старта Фазы 2 |
|
||
| 5 | Bleed offset нужен? | объём Фазы 1 (~200 строк или 0) | зависит от: «есть ли печать на коробках» |
|
||
| 6 | Кто работает с конструктором в проде: менеджер / клиент / оба | UI и Auth-уровень | Фаза 2 |
|
||
| 7 | Перенос evaluator на TS — переписывать или транспилировать JS как есть | трудозатраты Фазы 2 | до старта Фазы 2 |
|
||
|
||
### Граница: что Sunprint НЕ делает с этим конструктором
|
||
|
||
Чтобы не превратить инструмент в полный CAD (см. ROADMAP § «Что НЕ делать»):
|
||
|
||
- Не реплицируем ArtiosCAD/Esko Studio. Это software за десятки тысяч
|
||
долларов и десятилетия разработки.
|
||
- Не делаем визуальный редактор контура мышкой — JSON + параметры остаются
|
||
основным интерфейсом для дизайнера/админа.
|
||
- Не покрываем все 200 кодов FEFCO. Только то, что реально заказывают.
|
||
- Не делаем 3D-превью складывания на старте. Может появиться позже.
|
||
|
||
---
|
||
|
||
## ✅ Закрытые треки
|
||
|
||
### 2026-04-28 (15:21): Basic Auth снят с calc.suntask.ru — `/api/v1/calculate` open
|
||
|
||
**Цель:** убрать MVP-защиту Basic Auth (manager:<htpass>), которая мешала AJAX-вызовам из embed.
|
||
|
||
**Симптом** — после деплоя SSO bridge: при нажатии «Рассчитать» в калькуляторе из `/embed?token=...` появлялся нативный browser-диалог Basic Auth. JWT уже принят, юзер залогинен, но nginx перед proxy_pass требовал `auth_basic`. POST `/api/v1/calculate` всегда отдавал **401 574** до Next.js.
|
||
|
||
**Корень** в `/etc/nginx/vhosts/njsoft/calc.suntask.ru.conf`:
|
||
```nginx
|
||
server { # server-block корень
|
||
auth_basic "Print Calculator"; # ← применялось ко всему vhost'у
|
||
auth_basic_user_file /var/www/print-calc/.htpasswd;
|
||
|
||
location /api/auth/ { auth_basic off; ... } # явные исключения
|
||
location /login { auth_basic off; ... }
|
||
location /manager { auth_basic off; ... }
|
||
location /api/v1/quotes { auth_basic off; ... }
|
||
location /api/v1/clients { auth_basic off; ... }
|
||
location /embed { auth_basic off; ... }
|
||
# ...
|
||
|
||
location /api/ { ... } # ← БЕЗ auth_basic off!
|
||
# /api/v1/calculate, /api/v1/catalogs,
|
||
# /api/v1/templates сюда попадают
|
||
}
|
||
```
|
||
|
||
**Правка:** удалены 2 строки server-block через `sed -i`:
|
||
```diff
|
||
- auth_basic "Print Calculator";
|
||
- auth_basic_user_file /var/www/print-calc/.htpasswd;
|
||
```
|
||
|
||
Все 9 `auth_basic off;` в локациях оставлены (idempotent — no-op без главного `auth_basic`). Можно убрать в plain-text-чистке потом, не критично.
|
||
|
||
**Smoke 28.04 16:27:**
|
||
```
|
||
<manager-1-ip> GET /calc-redirect.php → 302
|
||
<manager-1-ip> GET /embed?token=<новый JWT> → 200 (1.8 KB)
|
||
<manager-1-ip> POST /api/v1/calculate → 200 (910 байт JSON)
|
||
<manager-2-ip> (другой manager) POST /api/v1/calculate → 200, 200 (15:57)
|
||
```
|
||
HTTP-distribution на `/api/v1/calculate`: было 4×401, стало **4×200** (Дима + другой менеджер). Никаких регрессий.
|
||
|
||
**Откат:** `cp /etc/nginx/vhosts/njsoft/calc.suntask.ru.conf.bak-pre-rm-basicauth-20260428-152138 → vhost && nginx -s reload`
|
||
|
||
### 2026-04-28: SunTask SSO Bridge — задеплоен и работает в проде
|
||
|
||
**Цель:** менеджер SunTask кликает «Калькулятор» в admin-меню → автоматически попадает в `calc.suntask.ru/embed` с правильной ролью без отдельного логина.
|
||
|
||
**Финальная архитектура (живая):**
|
||
```
|
||
suntask.ru/calc-redirect.php (standalone PHP вне ZF1)
|
||
↓ session_save_path('/var/www/html/sessions') + session_start()
|
||
↓ $email = $_SESSION['Zend_Auth']['storage']
|
||
↓ PDO connect через njsoft_sun_adv.db.host (creds из Lv7CMS/config.ini [production])
|
||
↓ SELECT id, email, role, real_name FROM _users WHERE email=? AND site='suntask' AND activity=1 AND deleted=0
|
||
↓ mapRole: administrator/developer → owner | staff → manager | else → 403
|
||
↓ JWT::encode HS256 (claims: email, name, role, iat, exp=300, iss=suntask.ru, sub=u<id>)
|
||
↓ 302 Location: https://calc.suntask.ru/embed?token=...&next=/manager
|
||
calc.suntask.ru
|
||
↓ JWT verify (тот же secret из application.ini production calc.jwt_secret)
|
||
↓ /embed page render → 200
|
||
↓ /api/v1/clients?limit=10 → 200 (live data)
|
||
менеджер работает в калькуляторе ✅
|
||
```
|
||
|
||
**Smoke на live (28.04 14:48 MSK):**
|
||
```
|
||
suntask: GET /calc-redirect.php → 302 (без ошибок в php-fpm.log/error.log)
|
||
calc: GET /embed?token=<JWT>&next=/manager → 200
|
||
calc: GET /_next/static/chunks/app/embed/page-*.js → 200
|
||
calc: GET /api/v1/clients?limit=10 → 200, 200, 200, 200 (Дима реально печатает в поиске)
|
||
```
|
||
|
||
JWT-payload (декодированный, при браузерном тесте Димы):
|
||
```json
|
||
{"email":"<owner email — в защищённом хранилище>",
|
||
"name":"<реальное имя owner-а>",
|
||
"role":"owner",
|
||
"iat":1777376890, "exp":1777377190, "iss":"suntask.ru", "sub":"u190"}
|
||
```
|
||
|
||
**Архитектурный путь** (3 версии файла + reverse-engineering Lv7CMS):
|
||
- v1 standalone с `Zend_Application::bootstrap()` → 302 на /admin для всех (Lv7CMS user-registry не заполняется без dispatch)
|
||
- v2/v3 CalcController внутри taskManager-модуля → 404 (Lv7CMS routing engine читает routes из БД, UI menu-add не регистрирует route)
|
||
- v4 standalone PHP без bootstrap → PDO `[2002] No such file or directory` (host=localhost из dev-секции config.ini)
|
||
- v5 + `session_save_path()` → дотянулся до session, но PDO та же ошибка
|
||
- **v6** + `parse_ini_file(true)['production']` → ✅ работает
|
||
|
||
Все 5 архитектурных уроков см. ниже в «📚 Уроки проекта».
|
||
|
||
### 2026-04-27: БАГ колод (тираж ×54) — v8.1 в проде
|
||
|
||
**Корень:** `extraFields.cardsPerDeck` хранился только в seed JSON, ни одна часть кода (engine, API, UI) его не читала. UI-загрузчик шаблона брал `defaultInput.tirage = 100` как тираж карт, менеджер интерпретировал как 100 колод. Engine считал 100 карт вместо 5400 → сметы в 54× дешевле.
|
||
|
||
**Решение:** pipeline между UI и engine (`apps/web/lib/calc/prepareEngineInput.ts`) с handler-registry. UI шлёт `templateSlug + decks`, pipeline переводит в `tirage = decks × cardsPerDeck`, engine не тронут. **30/30 engine-тестов остались зелёными**, +10 новых pipeline-тестов = **93/93**.
|
||
|
||
Smoke на live: 101 колода × 54 = 5454 карт ✅, productsPerSheet=21 (golden Excel match), авто-shrinkWrap 50₽×101=5050₽.
|
||
|
||
Контракт ошибок: TEMPLATE_NOT_FOUND/DECKS_REQUIRED → 400, UNHANDLED_TEMPLATE_TYPE → 500.
|
||
|
||
Бэкап `/var/www/print-calc.old.20260427-pre-v81` снят — 24-часовое observation прошло без регрессий (28.04 утром: 0 ошибок в logs за сутки, HTTP 4×200/1×400/0×500).
|
||
|
||
---
|
||
|
||
## 📋 Бэклог (по приоритету)
|
||
|
||
**Легенда приоритетов:**
|
||
- 🔴 **Критично** — кровотечение прямо сейчас (баги в проде, потеря денег, потеря данных). Бросаем всё и фиксим.
|
||
- 🟡 **Важно** — функционал ожидается заинтересованным лицом, без него работа неполная. Делается в плановом порядке.
|
||
- 🟢 **Полезно** — улучшения, которые не блокируют ничего. По возможности.
|
||
- 🔵 **Гигиена** — мелкие операционные задачи (смена паролей, очистка снэпшотов, обновление зависимостей).
|
||
|
||
| Приоритет | Трек | Статус | Оценка | Заметки |
|
||
|---|---|---|---|---|
|
||
| 🟡 | **Конструктор коробок (dieline) — Фаза 1: локальный плейграунд** | план зафиксирован, ждёт observation+пароли | ~1-2 недели локально | demo.html на машине оператора. Расширение каталога под микрогофру (топ 5-15), AST-парсер вместо new Function(), валидация геометрии. См. полный раздел «🆕 Трек: Конструктор коробок» выше |
|
||
| 🟡 | **Конструктор коробок — Фаза 2: интеграция в print-calc** | ждёт окончания Фазы 1 | ~3-5 сессий | Prisma `BoxConstruction`, handler `box_construction` в prepareEngineInput, UI-форма с DXF, цена. См. «Открытые решения для Фазы 2» выше — 7 пунктов TBD |
|
||
| 🟢 | calc-сторона Phase 4: NextAuth session из JWT | переоценить | 1-2 дня | **не блокер**: после снятия Basic Auth (28.04) `/api/v1/calculate` отдаёт 200 без NextAuth session. Phase 4 имеет смысл только если позже захотим разделить read/write права на calc-API (например аналитика для owner vs обычный расчёт). Понижено с 🟡 до 🟢 |
|
||
| 🟡 | print-calc /admin/approvals UI (owner) | не начат | 3 часа | владелец подтверждает скидки/наценки. ApprovalRequest пишется в БД с v7 |
|
||
| 🟢 | print-calc v8.2 rebase под v8.1 pipeline | не начат | пересмотреть | архив `print-calculator-v8-code.zip` устарел: содержит НДС 22%/курсы ЦБ/налог.окружение/шелкография/Equipment, но без pipeline-фикса колод. Нужен ремердж перед деплоем |
|
||
| 🟢 | Accountant CMS (редактор каталогов) | не начат | 2-3 дня | бухгалтер правит цены материалов/процессов |
|
||
| 🟢 | LIVE-CALC расчёты для тюнинга engine | не начат | по мере проблем | сравнение с реальными сметами |
|
||
| 🟢 | **njsoft_sun_adv DB password — accepted risk** | принято решение оператора | n/a | DB password засветился в claude.ai-чате 28.04 при `docker inspect`. **Не ротируется**: njsoft — external team с административным доступом к серверу, бэкап-канал для критических инцидентов, ротация порвёт этот канал в обмен на низкоинтенсивную гипотетическую защиту от утечки через Anthropic-инфраструктуру. См. правило 7 Security-политики |
|
||
| 🔵 | Сменить пароли manager + owner от 27.04 | shared server | TBD | висят в `/root/print-calc-initial.txt` (засветились 27.04 при `cat`). Ротация требует координации с другими пользователями сервера |
|
||
| 🟢 | password-source mystery в SunTask SSO bridge | не блокер | TBD | `application.ini` SunTask **не содержит** `db.default.params.password` (grep'ом 0 совпадений), но `calc-redirect.php` через `parse_ini_file('production')` живёт и SSO работает. Расследовать откуда читается DB password — для документирования архитектуры (расширение Урока #9). Кандидаты: `Lv7CMS/config.ini`, `resources.db.params.password` (другой Zend key), env-переменная контейнера |
|
||
| 🔵 | Поднять swap 4-8 GB на сервере | не начат | 5-10 мин | сейчас swap=0, PSI чистый — но без swap-парашюта при OOM-инциденте процесс падает мгновенно. Страховка для крупных билдов / неожиданных пиков. Замечено при diagnostic-разведке 29.04 |
|
||
| 🟢 | Очистка диска / расширение `/dev/md43` | в течение квартала | TBD | 79% занято (652G/878G, 181G свободно). Сейчас не блокер, но если рост контейнерных volumes продолжится — упрётся. Замечено при diagnostic 29.04 |
|
||
| 🟢 | Расследование `njsoft_sun_adv_db` 86% CPU spike | не срочно | TBD | snapshot 29.04 10:05 показал 86% CPU + 4.27 GB RAM на этом контейнере, load avg 1.20 (norm). Точечный пик, не критично. Если повторится в графике долгой нагрузки — log-analysis MySQL slow-query |
|
||
| 🔵 | Вычистить `.bak`-конфиги в `/etc/nginx/vhosts/njsoft/` | не срочно | 5 мин | два файла регистрируют `server_name suntask.ru` → `nginx -t`/reload каждый раз ругается warning'ами `conflicting server name`. Кандидаты: `calc.suntask.ru.conf.bak-ip-fix`, `calc.suntask.ru.conf.bak-pre-rm-basicauth-20260428-152138`. Перенести в `/root/nginx-bak/` или удалить — функционально не нужны |
|
||
|
||
---
|
||
|
||
## 🚧 Известные риски / грабли
|
||
|
||
- **claude.ai-сессии могут работать с устаревшим Project Knowledge.** 29.04 другая claude.ai-сессия обновила STATE.md от 27.04-baseline (две сессии назад), затёрла контент 28.04 вечера: трек «Basic Auth снят», трек «SunTask SSO Bridge», все 5 уроков Lv7CMS reverse + урок про nginx Basic Auth (6 уроков), упоминание `bak-pre-rm-basicauth-20260428-152138`. Поймали через md5-сверку в Code, восстановили merge'ем из архива. **Правило:** перед обновлением STATE.md в claude.ai сравнивать md5 локального Project Knowledge с актуальной версией в репо/на сервере. Если расходятся — сначала pull/refresh, потом правки. Архив на X-drive (`/x/-=SOFT=-/Claude/dima/2026-04-29_*_STATE_*.md`) — последний рубеж восстановления.
|
||
- **v8.2 архив устарел.** Pipeline-фикс делался поверх v7. v8 (НДС 22%, курсы ЦБ) собирался в claude.ai-чате до этого. Перед деплоем v8.2 — нужно смержить v8 правки с v8.1 (новые `lib/calc/`, `lib/templates.ts`, расширение Zod в route.ts).
|
||
- **CalcController.php в taskManager — мёртвый груз.** v3 deployed, но Lv7CMS routing его не использует. Можно оставить (reference для будущих случаев) или удалить. На усмотрение оператора. Не вредит.
|
||
- **Контекст между средами теряется** → правило: STATE.md обновляется в конце каждой сессии. Иначе следующая сессия теряет находки.
|
||
|
||
---
|
||
|
||
## 📦 Артефакты последней сессии
|
||
|
||
### SunTask SSO bridge — deployed (28.04.2026)
|
||
|
||
| Файл | md5 | Статус |
|
||
|---|---|---|
|
||
| `/var/www/sun-promo.docker/src/library/PhpJwt/JWT.php` | `cb678129885e1d97e2f36bba95fe95c3` | active (HS256 encode/decode) |
|
||
| `/var/www/sun-promo.docker/src/modules/taskManager/controllers/CalcController.php` | `96081670f57d8dec6e7a7200516cd176` | **мёртвый груз** (v3 deployed, Lv7CMS routing не использует — оставлен как archaeology) |
|
||
| `/var/www/sun-promo.docker/src/www/suntask.ru/application/configs/application.ini` | mod | `[production]` секция расширена 3 строками `calc.jwt_secret` / `calc.jwt_issuer` / `calc.url` |
|
||
| `/var/www/sun-promo.docker/src/www/suntask.ru/www/calc-redirect.php` | `1a4c8739537d81f58d7a524dcdc78bdf` | **active (v6) — основной endpoint SSO** |
|
||
| Menu-item `/admin/menu` UI | DB | URL изменён на `/calc-redirect.php` (Дима, 28.04 ~11:49) |
|
||
|
||
### Bookmark backups (на сервере /tmp, держать до 7 дней стабильности)
|
||
|
||
```
|
||
/tmp/suntask-application.ini.bak-20260424-140650 — ini до Шага 3 (calc.* keys) [Шаг 0.5]
|
||
/tmp/suntask-application.ini.bak-20260428-095617-pre-step3 — ini до правки awk на Шаге 3.3
|
||
/tmp/suntask-menu.sql.bak-20260424-140650 — mysqldump _sitestructure_node до Шага 0.5
|
||
/tmp/calc-redirect.php.bak-pre-v4-20260428-132849 — v1 standalone (с Zend_Application bootstrap)
|
||
/tmp/calc-redirect.php.bak-pre-v5-20260428-142041 — v4 (без bootstrap, до session_save_path)
|
||
/tmp/calc-redirect.php.bak-pre-v5-20260428-142315 — v4 дубль
|
||
/tmp/calc-redirect.php.bak-pre-v6-20260428-143950 — v5 (до parse_ini_file fix)
|
||
/tmp/CalcController.php.bak-pre-v3-20260428-105448 — CalcController v2 (до Lv7CMS_Controller_Backend)
|
||
/etc/nginx/vhosts/njsoft/calc.suntask.ru.conf.bak-pre-rm-basicauth-20260428-152138 — calc nginx vhost ДО снятия auth_basic
|
||
```
|
||
|
||
---
|
||
|
||
## 🔗 Связанные документы и среды
|
||
|
||
| Документ | Где | Содержит |
|
||
|---|---|---|
|
||
| `project_print_calc.md` | Claude Code memory | пути, контейнеры, env, deploy-команды |
|
||
| `project_suntask_sso_deploy_active.md` | Claude Code memory | подшаги 0-7 SSO деплоя (теперь historic) |
|
||
| `feedback_state_md_single_source.md` | Claude Code memory | регламент работы с этим файлом |
|
||
| `DEPLOY.md` | репо print-calc | план развёртывания calc.suntask.ru |
|
||
| `SKILL.md` (web-master) | `/mnt/skills/user/web-master/` | стандарты SEO/UX/UI |
|
||
| `SEO__и_сайт.rtf` | Project Knowledge | стандарты разделов sunprint.ru, NAP, копирайтинг |
|
||
| Excel-файлы (3 шт.) | Project Knowledge | исходная логика расчёта (для квиза-калькулятора) |
|
||
| `README.md`, `ARCHITECTURE.md`, `SCHEMA.md`, `ROADMAP.md`, `INTEGRATION.md`, `DECISIONS.md` (dieline) | Project Knowledge | handoff-пакет конструктора коробок v0.4 |
|
||
| `catalog.json`, `core.js`, `dxf-export.js`, `demo.html` (dieline) | Project Knowledge | данные + рабочий код v0.4, vanilla JS |
|
||
|
||
---
|
||
|
||
## 📐 Правила работы со STATE.md
|
||
|
||
1. **Один файл — одна правда.** Не плодить дубли.
|
||
2. **Обновлять в конце сессии.** Любой Claude — последним действием. Если по треку ничего не изменилось, ставится новая дата и «без изменений».
|
||
3. **Жёсткая структура.** Заголовки выше — фиксированные. Не переименовывать, не перемешивать. Это позволяет diff'ать между сессиями.
|
||
4. **Читать в начале сессии.** Любой Claude — первым делом. Если в контексте нет — спросить пользователя, где взять.
|
||
5. **Хранить в git.** Чтобы история изменений была. `git log STATE.md` — таймлайн всего проекта.
|
||
6. **Сюда НЕ кладём:** пароли, токены, API-ключи, личные данные клиентов, суммы конкретных сделок. Только техническая правда о ходе работ.
|
||
7. **Сюда кладём:** активный трек, что выяснено, гипотезы, следующий шаг, бэклог, риски, ссылки.
|
||
8. **Перед обновлением — md5-сверка.** Если работаешь в claude.ai через Project Knowledge, сравни md5 загруженной копии с актуальной (репо/сервер). При расхождении — сначала refresh, потом правки. Иначе риск регрессии (см. инцидент 29.04 в рисках).
|
||
9. **Public mirror работает только с redacted-копией.** Полный STATE.md (с реальными IP менеджеров, путями инструментов оператора, internal-деталями) живёт в **приватном** репо `git.suntask.ru/sunprint/print-calc` (или X-локально + сервер). Публичный mirror `git.suntask.ru/sunprint/sunprint-state` получает **redacted-версию**: IP-адреса менеджеров заменены на плейсхолдеры (`<manager-N-ip>`), сервер `212.41.28.51` остаётся (DNS-публичный, не секрет). При обновлении полного — повторно прогнать sed-redact на копии, проверить через `grep -E '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b'`, что остался только `212.41.28.51`, только потом коммит/push в public.
|
||
|
||
**Версионирование URL для claude.ai (обязательно):** claude.ai web_fetch держит собственный кеш на стороне Anthropic-инфраструктуры, **не реагирующий на downstream Cache-Control headers** (даже `no-cache, must-revalidate` — проверено 29.04). Поэтому branch-URL `/raw/branch/main/STATE.md` для claude.ai **НЕ работает** — отдаёт устаревшую версию (та, что claude.ai увидел при первом fetch'е).
|
||
|
||
Стандарт: для каждого STATE.md update Code в отчёте указывает short-hash последнего commit'а **и готовый URL** формата `https://git.suntask.ru/sunprint/sunprint-state/raw/commit/<short-hash>/STATE.md`. claude.ai делает web_fetch на этот URL — он уникален для каждого commit'а, кеша никогда не было, всегда отдаёт нужную версию.
|
||
|
||
nginx `Cache-Control: no-cache, must-revalidate` (с `X-Source: raw-passthrough` маркером) для path'ов `^/[^/]+/[^/]+/(raw|media)/` в `/etc/nginx/vhosts/njsoft/git.suntask.ru.conf` остаётся как страховка для остальных HTTP-клиентов (`curl`, браузеры, второй разработчик, будущие интеграции). Не вредит.
|
||
|
||
---
|
||
|
||
## 🔒 Security политика для claude.ai/Code сессий
|
||
|
||
Три инцидента с утечкой credentials в чаты за две недели: 27.04 (manager+owner пароли через `cat /root/print-calc-initial.txt`), 28.04 (DB password через `docker inspect`), 29.04 (Gitea token через clone-URL → `cat .git/config`). Этот раздел — правила, выработанные на этих инцидентах.
|
||
|
||
1. **Любой credentials, попавший в текстовый output одной из сессий (claude.ai или Code), считается скомпрометированным с момента отправки.** Без оценки вероятности утилизации, без «там же только минута». Ротируется немедленно. Никогда не делаем исключения вида «доделаем последнюю команду и потом revoke».
|
||
|
||
2. **Не использовать `cat` / `docker inspect` / `env` / `grep` на файлах с секретами.** Использовать точечный `awk` / `sed` по нужному ключу, передавать наружу **только результат**, не raw содержимое. Для проверок чистоты — бинарный exit code (`grep -q && echo OK || echo DIRTY`), не stdout значения.
|
||
|
||
3. **Token в URL ≠ inline аргумент.** `git clone https://user:token@...` записывает токен в `.git/config`. Использовать `GIT_ASKPASS`, `git credential approve`, env-var с inline `git -c credential.helper`, или SSH-ключи.
|
||
|
||
4. **Доставка токенов на сервер — через pipe, без промежуточного файла:**
|
||
```bash
|
||
echo -n 'TOKEN' | ssh root@HOST 'umask 077; cat > /root/.token-name; chmod 600 /root/.token-name; wc -c /root/.token-name'
|
||
```
|
||
Пробел перед `echo` пропускает команду из shell history если `HISTCONTROL=ignorespace` (или эквивалент). Не использовать `nano`/`vim` для секретов, не делать локальные tempfile'ы.
|
||
|
||
5. **TTL на все токены, никогда no-expiration.** Стандарт: 90 days для рабочих токенов, 30 days для разовых операций. Истекший токен = автоматическая страховка от забытого `shred`.
|
||
|
||
6. **claude.ai-сессии ≠ secure transport.** Чат-история передаётся через Anthropic-инфраструктуру и хранится у них. Code-сессии — тоже. Любой текст, попавший в чат, считается прошедшим минимум через Anthropic-логи. Это не приватная переписка двух людей.
|
||
|
||
7. **Accepted risk — отдельная категория, не «забыли ротировать».** Когда credentials попали в чат, но ротация имеет операционную стоимость выше рисковой стоимости (legitimate parties с доступом, бэкап-каналы, привязка к внешним конфигам не нашим), фиксируется как **accepted risk** с явным обоснованием в бэклоге, не как 🔵 pending-задача. Это отличает «забыли» от «знаем и приняли осознанно». Применимо: `njsoft_sun_adv` DB password (29.04 — accepted risk: external team backup-channel).
|
||
|
||
---
|
||
|
||
## 📚 Уроки проекта (постоянные принципы)
|
||
|
||
Эта секция — не про текущий трек, а про архитектурные решения, которые сложились в ходе работы над print-calc и должны соблюдаться следующими сессиями. Если приходит идея «давайте перенесём логику X в UI — так быстрее», сначала загляни сюда: возможно, мы уже это решили иначе и по веским причинам.
|
||
|
||
### print-calc — pipeline и engine
|
||
|
||
#### 1. Композитные продукты не живут в UI
|
||
|
||
Доменная семантика «1 колода = N карт» — в `apps/web/lib/calc/prepareEngineInput.ts`, не в `Calculator.tsx`/`ManagerCalculator.tsx`. UI спрашивает и показывает, сервисный слой решает «как это превратить в engine-input».
|
||
|
||
Когда появится следующий составной продукт (наборы открыток 12-в-1, многосекционные альбомы, альбомы стикеров) — добавляется новый `case` в `handlers` registry, **без правок UI и engine**. UI просто читает `extraFields._type` шаблона и рисует подходящие поля ввода; pipeline на сервере преобразует.
|
||
|
||
#### 2. Engine семантически туп
|
||
|
||
`packages/engine` принимает `tirage` как количество штук на резке, считает раскладку. Не знает про «колоды», «наборы», «секции». Это позволило не переписывать 30 зелёных engine-тестов при v8.1: контракт `QuoteInput.tirage` не изменился, добавился только сервисный слой над ним.
|
||
|
||
Любой новый смысл (составные единицы, многоэтажные расчёты, скидки дилеров) — кандидат **сначала в pipeline**, и только если паттерн повторится 2-3 раза — миграция в engine как нативная абстракция (см. план v9 по `productGrouping`).
|
||
|
||
#### 3. Unknown `_type` → 500, не silent passthrough
|
||
|
||
Если в `seed.json` появится шаблон с новым `extraFields._type`, а handler в `prepareEngineInput.ts` не зарегистрирован — pipeline падает с `UNHANDLED_TEMPLATE_TYPE` HTTP 500 + `console.warn` в логи. **Никакого тихого пропуска.**
|
||
|
||
Защищает от классики: добавили в БД новый тип через accountant CMS, забыли написать handler, расчёт молча идёт по неверной ветке (или сваливается в passthrough как обычный продукт), баг живёт месяцами и проявляется как «странные цифры в КП». Лучше явная 500 в первый же день.
|
||
|
||
#### 4. Явные коды ошибок на уровне pipeline
|
||
|
||
`TEMPLATE_NOT_FOUND` (400), `DECKS_REQUIRED` (400), `UNHANDLED_TEMPLATE_TYPE` (500) — клиент сразу понимает что чинить: поправить slug, добавить поле, написать handler. Не «something went wrong» и не engine-ошибки наружу.
|
||
|
||
При добавлении новых типов и handler'ов — следовать тому же паттерну: домен-специфичные коды (`SET_SIZE_REQUIRED`, `SECTIONS_REQUIRED` и т.п.), не обобщённые `INVALID_INPUT`.
|
||
|
||
#### 5. Recon перед фиксом окупается
|
||
|
||
Перед v8.1-фиксом вместо «сразу делаем legacy-режим на всякий случай» прошлись логами nginx, app-логами, БД, кодом suntask: 10 минут команд → обнаружено, что внешних API-клиентов **нет** → отказались от legacy-режима и сэкономили ~1 час кода + 1 тест-кейс. См. строку «БЛОК 1.1 — 263 запроса, все из 2 UI» в архиве чата.
|
||
|
||
Принцип: перед добавлением «защитного» кода (deprecation, fallback, dual mode) — 5-15 минут на проверку, нужна ли защита вообще. Часто оказывается, что нет.
|
||
|
||
### Lv7CMS reverse-engineering (28.04.2026)
|
||
|
||
Архитектурные сюрпризы Lv7CMS, которые мы проходили через 6 итераций при SSO-интеграции. Сохраняем чтобы следующие интеграции (или другие SunTask-задачи) не наступили на те же грабли.
|
||
|
||
#### 6. Lv7CMS routing — из БД, не из файла
|
||
|
||
Все маршруты SunTask регистрируются динамически в `Lv7CMS::init()` через `Zend_Registry::get('Routes')->findAll()` → `Zend_Controller_Front::addRoute()`. Источник — таблица в БД, читаемая через `Lv7CMS_Resource_Item_Route`.
|
||
|
||
UI «Добавить пункт меню» в `/admin/menu` создаёт **только меню-запись** (`_users_menu`), но **не route**. Если нужен новый URL вида `/admin/<module>/<controller>` для нового контроллера — нужен либо INSERT в `_routes` через UI/SQL, либо обходной путь.
|
||
|
||
**Standalone PHP в DocumentRoot** (`*.php` файл вне ZF1 routing) — самый простой обход: nginx → php-fpm напрямую, без Zend_Controller_Front. Так сделан `calc-redirect.php`.
|
||
|
||
#### 7. `Zend_Application::bootstrap()` без `->run()` ≠ полный bootstrap
|
||
|
||
`bootstrap()` инициализирует resource'ы (db, cache, view, FrontController), но **НЕ запускает dispatch loop**. Lv7CMS заполняет `Zend_Registry::set('user', ...)` через FrontController plugin (`Users_Plugin_Auth::routeStartup`), который активируется **только при `dispatch()` или `run()`**.
|
||
|
||
Standalone-скрипту, которому нужен текущий юзер: **читать `$_SESSION['Zend_Auth']['storage']` напрямую** (это email, identity hint), затем делать собственный PDO к `_users` и mapping role. Не ходить через `Zend_Registry::get('user')` — оно вернёт null.
|
||
|
||
#### 8. SunTask переопределяет `session.save_path` через application.ini
|
||
|
||
```ini
|
||
phpSettings.session.save_path = APPLICATION_PATH "/../../../sessions"
|
||
```
|
||
→ `/var/www/html/sessions/` (а не PHP-default `/tmp`).
|
||
|
||
Это применяется **только в ZF1 bootstrap** через `phpSettings.*`. Standalone-скрипты PHP **должны вручную** делать `session_save_path('/var/www/html/sessions')` ДО `session_start()`. Иначе session_start создаёт пустую сессию в `/tmp`, не находит существующего юзера, отправляет на login.
|
||
|
||
#### 9. `parse_ini_file($file, true, INI_SCANNER_RAW)` — process_sections=true ОБЯЗАТЕЛЕН
|
||
|
||
Lv7CMS/config.ini и SunTask application.ini имеют структуру:
|
||
```
|
||
[production]
|
||
db.default.params.host = "njsoft_sun_adv.db.host"
|
||
db.default.params.username = "sunprint"
|
||
|
||
[development : production] ← inheritance + override
|
||
db.default.params.host = "localhost"
|
||
db.default.params.username = "root"
|
||
```
|
||
|
||
`parse_ini_file($file, false)` (process_sections=false) читает плоско, последний ключ перезаписывает первый → **получим dev-значения вместо prod**. PDO connect ломается.
|
||
|
||
Правильно: `parse_ini_file($file, true, INI_SCANNER_RAW)['production']` — берёт **только production-секцию**, dev-override игнорирует.
|
||
|
||
#### 10. Docker env-переменные: видны под root, скрыты под app
|
||
|
||
`docker inspect <container> --format '{{range .Config.Env}}...'` показывает все env-переменные (включая `MYSQL_PASSWORD`) — но это работает **только под root**. Внутри контейнера под обычным пользователем (`app`, под которым работает php-fpm) `getenv('MYSQL_PASSWORD')` возвращает пустую строку.
|
||
|
||
Standalone PHP-скрипт **не может** надеяться на env-переменные для DB-credentials. Нужно **читать из конфиг-файла** (Lv7CMS/config.ini в нашем случае) с `parse_ini_file()`.
|
||
|
||
#### 11. MVP nginx-уровневый Basic Auth — снимать сразу как только полный auth-стек на месте
|
||
|
||
`auth_basic` в server-блоке nginx (вместе с `.htpasswd`) был MVP-защитой v7 — пока на calc-стороне не появился NextAuth + JWT-bridge. После того как полный auth-стек встал (NextAuth session для /manager, JWT для /embed) — Basic Auth превращается в **активный блокер** для всех AJAX-вызовов из подавторизованных страниц.
|
||
|
||
**Симптом:** при действии в подавторизованном UI всплывает нативный browser auth-dialog (поля username/password домена). Юзер видит «авторизуйся ещё раз» при том что только что прошёл JWT/SSO. Это почти всегда означает: nginx `auth_basic` срабатывает **до** proxy_pass на API endpoint, который не имеет `auth_basic off` исключения.
|
||
|
||
**Правило:** при добавлении любого временного nginx-уровневого auth — **сразу** добавить TODO-таск со сроком в STATE.md «снять при релизе X». Иначе он переживает все следующие auth-улучшения и мешает.
|
||
|
||
В нашем случае: 28.04 снят через `sed -i` 2 строк server-block + `nginx -s reload` (5 минут, без downtime, все `auth_basic off` локации стали idempotent).
|
||
|
||
### Архитектурные принципы за пределами card_deck
|
||
|
||
#### 12. Параметрическая геометрия живёт по тем же принципам, что калькулятор
|
||
|
||
Конструктор коробок (dieline) попадает в print-calc по той же модели, что `card_deck` (см. урок 1): данные в JSON/БД, чистые функции для расчёта, handler в `prepareEngineInput` — никакой геометрической логики в UI.
|
||
|
||
Конкретно:
|
||
- **Каталог конструкций — данные**, не код. Параметры (L/W/D/t), производные выражения, точки контура, сегменты резки/биговки — всё в JSON.
|
||
- **Evaluator чистый.** `buildGeometry(entry, params) → resolved geometry` без побочных эффектов. SVG/DXF-рендереры — отдельные чистые функции от результата evaluator'а.
|
||
- **`new Function()` нельзя оставлять в multi-tenant БД.** Если каталог редактируется не только владельцем — перед production-релизом обязательно заменить на AST-парсер выражений (`expr-eval` или PEG). Иначе любой сотрудник с правами правки шаблона может выполнить произвольный JS в браузере другого пользователя.
|
||
- **Граница ответственности.** Engine считает цену от tirage и площади. Геометрия — отдельный слой, который превращается в `_meta` для UI и в DXF на скачивание. По той же логике, что метаданные `units/itemsPerUnit` для card_deck.
|
||
|
||
Не пытаемся реплицировать ArtiosCAD. Цель — типовые конструкции с параметрами, для нишевого сегмента (микрогофра у Sunprint). Полный CAD — это отдельный мир и отдельная экономика.
|
||
|
||
### Security в claude.ai/Code сессиях
|
||
|
||
#### 13. Token в URL → токен в `.git/config`. Никогда `git clone https://user:token@...`
|
||
|
||
Git автоматически сохраняет URL аутентификации в `.git/config` нового клона под ключом `[remote "origin"] url`. Любая последующая команда, читающая `.git/config` (включая `cat`, `grep`, диагностические скрипты), выводит токен в свой output — а в claude.ai/Code это сразу попадает в чат-логи.
|
||
|
||
Безопасные альтернативы:
|
||
- `GIT_ASKPASS` обёртка с stdin
|
||
- `git credential approve` через pipe
|
||
- Inline `git -c credential.helper='!f() {...}; f'` (env-var живёт только время одной команды)
|
||
- SSH-ключи (когда настроим — сейчас приоритет в backlog)
|
||
|
||
Если **уже** клонировали с токеном в URL — **первой же командой**:
|
||
```bash
|
||
git remote set-url origin https://host/path.git # без креденшалов
|
||
grep -q 'token\|@host' .git/config && echo DIRTY || echo CLEAN
|
||
```
|
||
|
||
Никогда не делать `cat .git/config` или `grep .git/config` со stdout'ом значения — даже для проверки чистоты. Бинарный exit code через `grep -q ... && echo DIRTY || echo CLEAN` — единственный допустимый паттерн.
|
||
|
||
Инцидент 29.04: при smoke clone print-calc токен ушёл в чат через финальный `cat .git/config`. Поймали через 30 секунд после, отротировали. См. также раздел «🔒 Security политика для claude.ai/Code сессий» — этот урок №13 + 6 правил оттуда работают вместе.
|
||
|
||
---
|
||
|
||
## 📝 Лог сессий (последние 5)
|
||
|
||
| Дата | Среда | Длительность | Главное |
|
||
|---|---|---|---|
|
||
| 2026-04-30 | claude.ai (Opus 4.7) → Claude Code (Opus 4.7) | гигиена + push 0e53251 + правило #7 | (1) Push `0e53251` (правило #9 расширено — commit-pinned URL для claude.ai, ушло из локального pending в private remote). (2) Observation v8.1 формально закрыт: 0 ошибок за 48h, `2×200 + 1×400 + 0×500` на `/api/v1/calculate`, 0 quotes за 48h (менеджеры калькулировали без сохранения). (3) Snapshot `/var/www/print-calc.old.20260427-pre-v81` (956 KB) и `/var/www/print-calc/.htpasswd` (мёртвый артефакт) удалены. (4) `njsoft_sun_adv` DB password — accepted risk (правило #7 в Security политике): не ротируется, external team backup-channel. (5) Новый 🟢 backlog: «password-source mystery» — `application.ini` SunTask не содержит `db.default.params.password`, но SSO bridge живёт; расследовать откуда читается. Pre-flight memory↔Gitea API: чисто. |
|
||
| 2026-04-29 | claude.ai (Opus 4.7) → Claude Code (Opus 4.7) | планёрка + merge + Gitea-deployment + 2 incident'а + кеш-finding | (1) План dieline-трека: путь Б, Фаза 1 локально (1-2 нед), Фаза 2 интеграция (3-5 сессий). 7 решений TBD. (2) **Регрессия STATE.md:** claude.ai обновила файл от 27.04-baseline, затёрла 28.04. Поймали md5-сверкой, восстановили merge'ем. Урок №12 + правило #8. (3) **Gitea CE задеплоен на git.suntask.ru** — Let's Encrypt, nginx reverse-proxy на 127.0.0.1:3000, docker-compose в /opt/gitea, SQLite. Org `sunprint`, public `sunprint-state` (правило #9, redacted), private `print-calc`. (4) **Incident: token в URL** при smoke clone попал в чат через `cat .git/config`. Поймали за 30 сек, отротировали. Урок №13 + Security-секция. Новые backlog: swap, disk, db-spike, .bak-конфиги. (5) **Finding: claude.ai web_fetch имеет агрессивный собственный кеш на стороне Anthropic-инфраструктуры**, не уважающий downstream `Cache-Control` (даже `no-cache, must-revalidate`). nginx-override применён (для других клиентов остаётся защитой), но для claude.ai используется commit-pinned URL `/raw/commit/<hash>/STATE.md` как обязательный паттерн. Правило #9 расширено. |
|
||
| 2026-04-28 | Claude Code (Opus 4.7) | ~6ч | **Большой день. Полная end-to-end интеграция SunTask ↔ calc работает.** (1) SunTask SSO bridge: calc-redirect.php v1→v4→v5→v6, 6 итераций архитектуры. (2) Lv7CMS reverse-engineering: 5 уроков (routing из БД, bootstrap без dispatch, session_save_path override, parse_ini_file process_sections, env под app). (3) Basic Auth снят с calc.suntask.ru через `sed -i` + `nginx -s reload` — 6-й урок добавлен. Подтверждение: POST /api/v1/calculate → 200 от Димы (<manager-1-ip>) и от другого менеджера (<manager-2-ip>) — оба смогли реально использовать калькулятор |
|
||
| 2026-04-28 (утро) | Claude Code (Opus 4.7) | ~10мин | observation v8.1 прошёл — 24h без ошибок, 0 пятисоток, restic backup OK ночью |
|
||
| 2026-04-27 (вечер) | Claude Code (Opus 4.7) | ~3ч | **v8.1 БАГ колод задеплоен.** Pipeline `prepareEngineInput.ts` (вариант 1.5), 10 новых тестов, 93/93 зелёных, smoke на live прошёл |
|
||
| 2026-04-27 (утро) | claude.ai (Opus 4.7) | ~50мин | Корень бага колод: cardsPerDeck не пробрасывается в engine. Заведён STATE.md, договорились про pipeline-вариант |
|
||
|
||
> Старые сессии можно урезать до одной строки или удалять, оставляя последние 5-10. История целиком — в `git log`.
|