Commit Graph

1405 Commits

Author SHA1 Message Date
Дмитрий 9bb858a3e8 docs(bot): прогон 250x2 — разбор находок, что починено и урок про сторожа
- Отчёт прогона: 250 вопросов дважды (гость + вошедший клиент), 500 ответов, механическая
  сверка с базой + 10 судей-агентов. Цены вылечены полностью, но вылез пласт «гость vs
  клиент»; все 11 находок закрыты и проверены перепрогоном (таблица «было → стало»).
- ПИЛОТ.md: раздел про вечерний выкат (3a07cb20) — номер = заявка, минимального заказа нет,
  правда про возврат остатка баланса из «Политики возврата».
- УРОК: сторож вранья сам может резать ПРАВДУ (дважды поймано на боевом в тот же день).
  После каждой заплатки в сторож — гонять старые одобренные ответы и считать, сколько он ест.

NB: cspell и markdownlint исключены — их жалобы на чужих строках ПИЛОТ.md (соседняя сессия).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 18:59:53 +03:00
Дмитрий 025bd98ea8 chore(security): phpstan-baseline в allowlist gitleaks — генерённый файл цитирует тест-фикстуры
Перегенерированный baseline дословно цитирует тексты ошибок PHPStan, включая
строковые литералы из тест-файлов (синтетические телефоны Казани 8432… из
tests/Unit/Autopodbor/Extract/HtmlPhoneScannerTest.php). Сами тесты уже в
allowlist; baseline — эхо их кода, своих ПДн не содержит.

Та же категория, что composer.lock и cspell-words.txt.
Найдено при догоняющей проверке gitleaks после merge-коммита.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 18:10:21 +03:00
Дмитрий 42e907c882 Merge gitea/main into feat/sales-finder — сведение с боевым перед выкатом
Ветка разошлась с боевым 28.06 (334 коммита в main / 215 у нас). Влито ВСЁ боевое:
автоподбор конкурентов, мобильный адаптив портала, свой учёт посетителей, мониторинг
внешних сервисов, фиксы поставщика/биллинга/бота/разборов.

ПОБАЙТОВАЯ СВЕРКА: из 1008 файлов, изменённых боевым, 989 совпадают точно;
19 отличаются — все с нашей законной работой (обе стороны внутри). Затёртых — 0.

24 конфликта разобраны вручную. Ключевое:
- VerifySupplierOrderJob — взята БОЕВАЯ версия (фикс инцидента 11-12.07: площадка
  берётся из src, а не из имени; наша была старой и вернула бы баг, терявший заявки).
- SyncSupplierProjectsJobTest — 15 боевых тестов + наш уникальный (limit-1 → только B1).
- routes/web, router/index, config/services, bootstrap/app — обе стороны сложены.
- NewProjectDialog — зелёные дни недели (наше) + мобильная раскладка (боевое).
- CHANGELOG схемы — номера версий столкнулись, наши перенумерованы в v8.67-v8.70.
- composer — обе зависимости (laravel-dompdf наш + geoip2 боевой).

Гейты: бэкенд 2907/2911 (0 падений), Larastan 0, фронт 1333/1333, сборка OK.
Baseline статанализа принял пре-существующий долг боевого кода (автоподбор/чат).
@mixin в 26 моделях — требование статанализа, dev-докблок, на рантайм не влияет.

Откат: git reset --hard pre-merge-main-20260714

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 17:49:28 +03:00
Дмитрий dab0a21e59 docs(prod): бот врал про цены — корень, фикс и урок в снимке боевого
Бот брал все четыре версии тарифной сетки разом (старые версии в pricing_tiers не
удаляются) → называл гостям несуществующие 500 ₽. Урок: если бот врёт числом, смотри,
что ему ПОДАЛИ, а не как он врёт — сторож вранья пропустил это правильно.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 15:58:18 +03:00
Дмитрий cd5d08e9f7 docs(prod): хвосты выката — main сведён с боевым, прототип лендинга = боевой, ПИЛОТ.md
- ПИЛОТ.md: раздел 14.07.2026 (чат с ИИ-помощником, разборы, фикс склейки; пароли из
  Lockbox по connection id, ловушка CSP со встроенными скриптами, лимиты денег, откат).
- landing-v5-claude.html приведён к боевому index.html побайтово: в боевом есть счётчик
  посетителей соседней сессии, в прототипе его не было — выкат лендинга из репозитория
  снёс бы счётчик.
- HANDOFF: закрыты хвосты (main отставал на 97 коммитов и был сведён; кадры в git;
  счёт AITunnel ≈7 ₽/сутки). Открытым остался живой прогон чата в кабинете.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 15:00:02 +03:00
Дмитрий 96a3939063 docs(deploy): снимок боевого выката — чат, ИИ-бот, разборы, фикс склейки
Что выкачено, как устроено на бою (Lockbox вместо старых паролей на сервере, CSP
режет встроенные скрипты, лендинг только по «/»), защита денег, откат и список
хвостов для следующей сессии.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 14:31:40 +03:00
Дмитрий 631f99ecff fix(guides): скрипт страницы разборов вынесен в файл — CSP боевого режет встроенные
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 14:15:00 +03:00
Дмитрий e517b7a256 Merge remote-tracking branch 'gitea/main' into worktree-jivo-bot-core
# Conflicts:
#	app/bootstrap/app.php
#	app/config/services.php
#	app/tests/Feature/Plan4/Schema/SchemaDeltaTest.php
#	db/CHANGELOG_schema.md
#	db/schema.sql
2026-07-14 09:44:56 +03:00
Дмитрий 4b09242bce fix(guides): пересняты «Отчёты», «Выгрузка» и «Добавить вручную» — без машинных слов
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 09:36:51 +03:00
Дмитрий 0b67defcf7 fix(guides): переснято на боевых ценах — 127 кадров, честные подписи
Цены на кадрах теперь как на боевом: 55 ₽ за заявку, сетка 55→25 ₽, сбор
конкурентов 50 ₽, сбор источников 10 ₽ (были тестовые 500→250 ₽ и 300/50 ₽).
Две подписи прямо врали ценой — исправлены.

Кадры перестали ловить экран на полпути: робот дожидается загрузки данных.
Подписи, забегавшие вперёд экрана, переписаны; добавлен настоящий кадр раздела
«Отчёты»; стрелка в «Пометках» больше не тычет в пустой блок; демо-проект —
стоматология, а не окна.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 09:19:38 +03:00
Дмитрий 203845011c feat(sales-finder): искать можно на ЛЮБОМ уровне — не только по конечной рубрике
По просьбе владельца: «детские или взрослые стоматологии нам не интересны» —
надо уметь собрать «Частные стоматологии» целиком, не уточняя.

- rubrics.build_catalog: ГРУППА теперь тоже ищется — по своему имени; лист —
  своей фразой 2ГИС. Не ищутся только раздел (слишком широко) и служебный
  ящик «Ещё рубрики» (BUCKET_LABEL). Добавлен is_searchable().
- web/ui.py: в дерево для JS добавлен флаг q (можно искать). Каскад берёт САМЫЙ
  ГЛУБОКИЙ выбранный искомый узел; уточнять глубже необязательно (плейсхолдер
  «— не уточнять —»); кнопка активна сразу, как выбрана ниша или группа.
- Живьём: группа «Частные стоматологии» → 12 клиник, «Медтовары» → 12 фирм,
  глубокий лист «Пластическая хирургия» → 12 клиник. 226 тестов.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 08:13:13 +03:00
Дмитрий 8ce5619177 docs(visitors): короткие ссылки для рассылок — liderra.ru/s, /hh, /tg
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 06:58:33 +03:00
Дмитрий 28563d0692 feat(sales-finder): глубокий каталог 2ГИС — 3-й эшелон рубрик + адаптивный каскад
Каталог перестроен из плоского (19×~100) в ДЕРЕВО реального рубрикатора 2ГИС:
19 разделов → 1522 листа, глубина 3 (раздел → ниша/группа → узкая рубрика).
Тот самый «3-й эшелон» за кнопкой «Ещё» 2ГИС (закрыт открытый вопрос §6.7g).

- tools/crawl_rubricator.py — краулер рубрикатора через xfetch (кэш, отсечка
  глобального меню 19 разделов, «Ещё» = отдельная группа). Данные заморожены
  в salesfinder/rubricator.json (кэш rub_cache/ и *_raw.json в git НЕ идут).
- rubrics.py переписан на дерево: build_catalog+slug, queries_for(leaf)=[query]
  (группа не ищется — поиск только по конечному листу), _LEGACY_LABELS для
  русских подписей старых списков.
- web/ui.py: адаптивный каскад — списки всплывают по мере надобности; лист
  уходит как rubric, кнопка заблокирована пока не выбран лист.
- Живьём: глубокий лист «Пластическая хирургия» → 12 клиник из 2ГИС. 223 теста.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 06:56:24 +03:00
Дмитрий ee03d9ad91 fix(guides): страница разборов не находила кадры и показывала «готовятся»
Пути к кадрам и списку тем были относительными, а страница открывается по
адресу /kak-eto-rabotaet (без слэша на конце) — браузер искал их этажом
выше и не находил ни одного. Пути от корня сайта: так же будет работать и
на боевом, где страница и папка guides/ лежат рядом с лендингом.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 06:31:53 +03:00
Дмитрий aa7383a240 docs(sales-finder): HANDOFF 13-14.07 — inn_fl, очередь, каталог, 2-уровневый выбор + новый resume-промт 2026-07-14 05:30:22 +03:00
Дмитрий ff36e0c10e fix(visitors): партиции нарезает только штатный механизм + сторож схемы на 80 таблиц
Полный прогон вскрыл реальный баг: миграция создавала партицию site_events_2026_07
своими границами (в местном времени), а MonthlyPartitionManager — site_events_y2026_m07
в UTC. Партиции перекрывались (42P17) и роняли 19 ЧУЖИХ тестов. Теперь партиции
нарезает только менеджер (ensureRange), как у всех остальных таблиц.

Плюс сторож схемы обновлён под +2 таблицы учёта (80 таблиц, 141 индекс) и
записаны готовые ссылки с метками для смс/hh.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 05:22:46 +03:00
Дмитрий e8e23bdbae docs(chat): снимок состояния — откат склейки починен, кадры на живых данных
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 05:22:46 +03:00
Дмитрий b059d86fc6 fix(guides): кадры пересняты на живых демо-данных — портал выглядит работающим 2026-07-14 05:07:48 +03:00
Дмитрий 40aa3e878a chore(security): разрешить сторожу демо-номера +7 999 000-XX-XX
Диапазон 999-000 у операторов не существует — заведён специально для
демо-данных и съёмки инструкций, чтобы в кадр не попал настоящий номер.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 04:17:37 +03:00
Дмитрий 3f26ae054b feat(visitors): маячок на лендинге (view/alive/cta_click)
Метрику и Вебвизор не трогает — работает параллельно. Исходник лендинга взят
из feat/sales-finder (в боевую ветку он никогда не попадал); при выкате маячок
вставляется в БОЕВУЮ версию файла, чтобы не тащить на прод незавершённую стройку.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 04:17:15 +03:00
Дмитрий d0808ec09a docs(guides): итог дня — разборы 10/10, снимок состояния, план стройки
Гость получает верную ссылку на разбор по всем 10 частым вопросам; где
разбора нет — бот молчит, в пустоту не шлёт. 20 тем, 126 кадров, единый
демо-клиент. Отдельно: найден баг боевой функции — поиск дублей считал
ноль признаком совпадения (на проде он есть, фикс в ветке).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 04:14:58 +03:00
Дмитрий ed5f9ffd4a docs(guides): проверка разборов — бот, страница, ПДн, вес 2026-07-13 20:18:11 +03:00
Дмитрий 24fc6ecad4 fix(guides): семь тем про конкурентов пересняты на стоматологии — единый демо-клиент 2026-07-13 20:05:00 +03:00
Дмитрий cc63a7403f fix(guides): кадры показываются крупно — надписи в портале читаются 2026-07-13 19:20:36 +03:00
Дмитрий 1ee42a223b feat(guides): разборы по заявкам, отчётам, деньгам и настройкам проекта 2026-07-13 18:36:10 +03:00
Дмитрий efb0212398 docs(plans): план учёта посетителей — 12 задач, TDD, выкат отдельным шагом
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 18:33:33 +03:00
Дмитрий f4c3d885aa docs(specs): собственный учёт посетителей + раздел «Посетители» в админке
Точное разделение робот/браузер/человек (отклик страницы, а не запрос),
сквозная склейка лендинг→портал через куку на .liderra.ru, воронка и каналы
в админке. Метрику и Вебвизор не трогаем.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 18:33:21 +03:00
Дмитрий 69a81a7f07 docs(plans): план учёта посетителей — 12 задач, TDD, выкат отдельным шагом
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 18:30:46 +03:00
Дмитрий dd50f3832f docs(specs): собственный учёт посетителей + раздел «Посетители» в админке
Точное разделение робот/браузер/человек (отклик страницы, а не запрос),
сквозная склейка лендинг→портал через куку на .liderra.ru, воронка и каналы
в админке. Метрику и Вебвизор не трогаем.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 18:17:21 +03:00
Дмитрий 9c668de390 feat(guides): разборы по Конкурентному полю и источникам 2026-07-13 18:01:33 +03:00
Дмитрий 379564e8c0 feat(guides): страница «Как это работает» — пошаговые разборы для гостей 2026-07-13 17:45:36 +03:00
Дмитрий c33e91b2a7 feat(guides): съёмщик разборов + тема «Как создать проект»
Playwright-съёмщик (record.mjs) логинится демо-клиентом, проходит сценарий
из topics/<slug>.mjs и рисует поверх каждого шага стрелку+рамку+подпись
(подписи — из app/resources/help/sozdat-proekt.md, без выдумок). Первая
тема — sozdat-proekt, 8 кадров.

Мелкие находки по пути: приветственный тур перехватывал клики (глушим через
localStorage-флаг), первый проект нового клиента открывает шаг «Реквизиты»
(проходим тихо, без кадра), собранный фронт портала был устаревшим и тянул
старый виджет JivoSite поверх экрана (пересобрал npm run build — исходники
уже используют свой чат-виджет), подпись снизу могла перекрыть стрелку на
кнопках у нижнего края диалога (авто-перенос наверх).
2026-07-13 17:33:14 +03:00
Дмитрий bcffafd98c docs(tours): показы по порталу для гостей — два формата на выбор
Клипы по сути (сразу с нужного экрана, без входа и хождения по меню)
и тот же показ 2-3 картинками. Четыре темы: заявки, пополнение, создание
проекта, Конкурентное поле. Телефоны в кадре размыты. Страница сравнения —
tours-compare.html. На боевом сайте этого нет.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 16:29:15 +03:00
Дмитрий 2392eb0a25 docs(chat): личные ответы вошедшему — прогон и снимок состояния
20 личных вопросов на тестовом клиенте — 20 правдивых ответов; границы
держит (паузу не ставит, ПДн не даёт, будущее не прогнозирует). Гостю —
вход в кабинет. Посторонний получает 404. Две находки прогона закрыты:
жёсткий отказ в замене ловится в любой форме, правда про ниши больше
не режется. Тесты 216/216. На проде этого нет.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 13:49:20 +03:00
Дмитрий bb6df90810 docs(chat): план стройки личных ответов вошедшему клиенту
9 задач по TDD: ClientFacts (деньги/ступень/проекты/заявки под RLS),
сторож на выдуманные числа, карточка в контексте вошедшего, гостю вход
в кабинет, владелец разговора (404 чужому), ключ разговора на каждый вход,
живой прогон с судьями.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 11:02:40 +03:00
Дмитрий 09f0ea44df docs(chat): замысел личных ответов вошедшему клиенту
Карточка фактов (баланс/ступень/проекты/заявки) считается кодом и всегда
кладётся боту, когда клиент вошёл. Сторож режет числа не из карточки.
Переписка вошедшего закрывается от посторонних. Данные — строго под RLS
тенанта. Бот только рассказывает, ничего не меняет.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 10:55:32 +03:00
Дмитрий 2b13980af8 docs(chat): хвосты закрыты — экскурсии по теме и только из кабинета
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 10:15:38 +03:00
Дмитрий 60b0b855c5 docs(bot): в снимок состояния — правило ответственности за источник
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 09:50:30 +03:00
Дмитрий 34c6d68cfe docs(sales-finder): HANDOFF 13.07 — повтор сайта, ДФО-перегон #15, публикация отложена, Keys.so финал + новый resume-промт
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 09:44:31 +03:00
Дмитрий f6b2fbe262 docs(chat): отчёт о прогоне через свой чат + снимок состояния
Прогон 46 диалогами (250 реплик) через новый чат: 250/250 ответов,
медиана 2,58 с, вранья 0 — переезд чистый. Одна находка (бот выдумал срок
«перезвонят в течение рабочего дня») починена правилом по смыслу.

Отдельно: счётчики от накрутки счёта сидели на файловом кэше, который считает
не атомарно (300 прибавок -> досчиталось 77). Переведены на Redis, 300/300.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 09:20:16 +03:00
Дмитрий 45b0e46279 feat(landing): подключить своё окошко чата вместо Jivo
Тот же скрипт, что в кабинете: /chat-widget.js отдаётся приложением
(на liderra.ru лендингу принадлежит только «/», остальные пути — приложению).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 09:02:22 +03:00
Дмитрий ac0c8f74fc feat(chat): окошко чата на портале и лендинге вместо виджета Jivo 2026-07-13 08:09:51 +03:00
Дмитрий 477a46b889 docs(chat): пошаговый план стройки своего чата
8 задач по TDD: журнал разговоров -> свой приём сообщений -> опрос ответов ->
счётчики от накрутки счёта -> письмо владельцу со всей перепиской -> окошко
на портале и лендинге -> вычистка Jivo из настроек -> прогон 46 диалогами.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 06:37:28 +03:00
Дмитрий 01789381ac docs(chat): замысел своего чата вместо Jivo + разворот в протоколе
Владелец 13.07.2026: уходим от Jivo (3142 руб/мес корпоративный тариф ради
Bot API, ответа их поддержки так и нет), пишем своё окошко чата.
Ключевое: в чужом виджете бот не знает, кто с ним говорит -> личные ответы
невозможны. Привязка к Jivo в коде - 94 строки; мозг бота, 49 статей и
130 тестов переезжают нетронутыми.

Согласовано по частям: чат без постоянного соединения (очередь + опрос),
кабинет + лендинг, эскалация письмом владельцу (живого оператора нет),
три уровня счётчиков от накрутки счёта (потолок 1500 ответов/сутки + тревога).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 06:27:29 +03:00
Дмитрий 6e40c0523f docs(bot): правило владельца — замены заявки нет, тему сглаживаем
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 05:59:29 +03:00
Дмитрий 204f7b50b8 docs(bot): снимок состояния ИИ-бота на 13.07.2026
Где что лежит, как поднять стенд, что держится кодом (не ослаблять),
что осталось открытым, порядок выката.

Главный вывод доводки: сторож на регулярках проигрывает гонку формулировок —
работает только правило по смыслу + запрет в промпте.

Код: ветка worktree-jivo-bot-core (004a0b17), тесты 126/126. Бот НЕ на проде.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 05:35:30 +03:00
Дмитрий fd9358104c docs(bot): отчёт прогона диалогами — 46 разговоров, 250 реплик
Что вскрылось (одиночные вопросы этого не видели): клиент оставил телефон →
бот молчал; бот забывал, что сам сказал; враньё «на паузе списаний нет»;
выдуманный телефон поддержки и юр-гарантии в новых оборотах.

Урок метода: машинный счётчик по запрещённым фразам показал 0 нарушений,
хотя враньё осталось другими словами — считать своими же регулярками нельзя.

Код и починка — в ветке worktree-jivo-bot-core (b037a216). Бот НЕ на проде.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 04:52:41 +03:00
Дмитрий bd5c16cfbe chore: gitleaks — исключение для примера телефона в докблоке бота
Пример формата в ContactCapture.php остался в истории коммита 6f72367b
и блокировал push. Не ПДн — образец для распознавания; в рабочем коде уже
замаскирован. Настоящие номера сторож ловит по-прежнему.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 20:54:16 +03:00
Дмитрий 368bbf2000 docs(bot): отчёт прогона 250 вопросов — финальная сборка (Haiku + сторож)
Все 250 пар «вопрос → ответ» с финальной конфигурацией бота: модель Claude
Haiku 4.5 через AITunnel, маска ПДн, чистка markdown, сторож ответа.
Итог: 239 ответил сам, 11 увёл к специалисту, 0 «не знаю», 0 запрещённых фраз.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 20:48:11 +03:00
Дмитрий 17ab3fa3a3 fix(sales-finder): рендер 2ГИС молча терял фирмы — повтор до появления карточек
Симптом: кластер ДФО дал Владивосток 11 фирм вместо реальных 60.
Причина: xfetch.render повторял только на ПУСТОМ ответе, а 2ГИС-антибот
отдаёт непустую SPA-оболочку без карточек — она проходила как успех,
и вся страница фирм терялась. Фикс: render(ok=...) повторяет пока ok(html)
ложно; fetcher требует наличие /firm/ (не просто непустоту), retries=4.
Живой контроль боевым fetcher: Владивосток 11 -> 60 (x5.5). 185 тестов зелёных.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 19:54:54 +03:00