56 KiB
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(md5acda575a5dc67402169f8586af2a0563), сверху наложены только новые блоки сессии 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/approvalsUI (~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)
- Целевой нишевый сегмент: самосборные коробки из микрогофры (подарочные, под визитки, мерч). НЕ полный FEFCO-200 — топ 5-15 конструкций под Sunprint.
- Архитектура: путь Б — встроить в print-calc как новый тип продукта.
- Связан с расчётом цены: да, ожидается в ближайшее время.
- Режим работы: «локально → потом часть сайта». Фазируем (см. ниже).
Фаза 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:), которая мешала 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:
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:
- 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 (декодированный, при браузерном тесте Димы):
{"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
-
Один файл — одна правда. Не плодить дубли.
-
Обновлять в конце сессии. Любой Claude — последним действием. Если по треку ничего не изменилось, ставится новая дата и «без изменений».
-
Жёсткая структура. Заголовки выше — фиксированные. Не переименовывать, не перемешивать. Это позволяет diff'ать между сессиями.
-
Читать в начале сессии. Любой Claude — первым делом. Если в контексте нет — спросить пользователя, где взять.
-
Хранить в git. Чтобы история изменений была.
git log STATE.md— таймлайн всего проекта. -
Сюда НЕ кладём: пароли, токены, API-ключи, личные данные клиентов, суммы конкретных сделок. Только техническая правда о ходе работ.
-
Сюда кладём: активный трек, что выяснено, гипотезы, следующий шаг, бэклог, риски, ссылки.
-
Перед обновлением — md5-сверка. Если работаешь в claude.ai через Project Knowledge, сравни md5 загруженной копии с актуальной (репо/сервер). При расхождении — сначала refresh, потом правки. Иначе риск регрессии (см. инцидент 29.04 в рисках).
-
Public mirror работает только с redacted-копией. Полный STATE.md (с реальными IP менеджеров, путями инструментов оператора, internal-деталями) живёт в приватном репо
git.suntask.ru/sunprint/print-calc(или X-локально + сервер). Публичный mirrorgit.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). Этот раздел — правила, выработанные на этих инцидентах.
-
Любой credentials, попавший в текстовый output одной из сессий (claude.ai или Code), считается скомпрометированным с момента отправки. Без оценки вероятности утилизации, без «там же только минута». Ротируется немедленно. Никогда не делаем исключения вида «доделаем последнюю команду и потом revoke».
-
Не использовать
cat/docker inspect/env/grepна файлах с секретами. Использовать точечныйawk/sedпо нужному ключу, передавать наружу только результат, не raw содержимое. Для проверок чистоты — бинарный exit code (grep -q && echo OK || echo DIRTY), не stdout значения. -
Token в URL ≠ inline аргумент.
git clone https://user:token@...записывает токен в.git/config. ИспользоватьGIT_ASKPASS,git credential approve, env-var с inlinegit -c credential.helper, или SSH-ключи. -
Доставка токенов на сервер — через pipe, без промежуточного файла:
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'ы. -
TTL на все токены, никогда no-expiration. Стандарт: 90 days для рабочих токенов, 30 days для разовых операций. Истекший токен = автоматическая страховка от забытого
shred. -
claude.ai-сессии ≠ secure transport. Чат-история передаётся через Anthropic-инфраструктуру и хранится у них. Code-сессии — тоже. Любой текст, попавший в чат, считается прошедшим минимум через Anthropic-логи. Это не приватная переписка двух людей.
-
Accepted risk — отдельная категория, не «забыли ротировать». Когда credentials попали в чат, но ротация имеет операционную стоимость выше рисковой стоимости (legitimate parties с доступом, бэкап-каналы, привязка к внешним конфигам не нашим), фиксируется как accepted risk с явным обоснованием в бэклоге, не как 🔵 pending-задача. Это отличает «забыли» от «знаем и приняли осознанно». Применимо:
njsoft_sun_advDB 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
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обёртка с stdingit credential approveчерез pipe- Inline
git -c credential.helper='!f() {...}; f'(env-var живёт только время одной команды) - SSH-ключи (когда настроим — сейчас приоритет в backlog)
Если уже клонировали с токеном в URL — первой же командой:
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 от Димы () и от другого менеджера () — оба смогли реально использовать калькулятор |
| 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.