From c1b7cb7d61fc0cb0e95e75b40ce81d717dc976be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Sun, 21 Jun 2026 14:37:33 +0300 Subject: [PATCH] =?UTF-8?q?fix(i18n):=20=D1=80=D1=83=D1=81=D1=81=D0=BA?= =?UTF-8?q?=D0=B8=D0=B5=20=D1=81=D0=BE=D0=BE=D0=B1=D1=89=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=B2=D0=B0=D0=BB=D0=B8=D0=B4=D0=B0=D1=86=D0=B8=D0=B8?= =?UTF-8?q?=20=E2=80=94=20lang/ru/validation.php?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UI-аудит раунд 2: APP_LOCALE=ru, но директории lang/ не было → Laravel отдавал сырой ключ «validation.required» во все формы без кастомных messages() (профиль, создание проекта, реквизиты для оплаты и т.д.). Auth-формы свой messages() имеют. Добавлен каноничный ru-перевод (laravel-lang) + секция attributes с русскими именами полей продукта (Имя/Телефон/Лимит лидов в день/ИНН/…). Верификация: trans(validation.required)→«Поле … обязательно для заполнения.»; форма профиля в Playwright показывает «Поле Имя обязательно для заполнения.» вместо «validation.required». Co-Authored-By: Claude Opus 4.8 (1M context) --- app/lang/ru/validation.php | 204 +++++++++++++++++++++++++++++++++++++ docs/observer/STATUS.md | 43 ++++---- 2 files changed, 225 insertions(+), 22 deletions(-) create mode 100644 app/lang/ru/validation.php diff --git a/app/lang/ru/validation.php b/app/lang/ru/validation.php new file mode 100644 index 00000000..4ef14bd6 --- /dev/null +++ b/app/lang/ru/validation.php @@ -0,0 +1,204 @@ + 'Вы должны принять :attribute.', + 'accepted_if' => 'Вы должны принять :attribute, когда :other равно :value.', + 'active_url' => 'Поле :attribute содержит недействительный URL.', + 'after' => 'Поле :attribute должно содержать дату после :date.', + 'after_or_equal' => 'Поле :attribute должно содержать дату не раньше :date.', + 'alpha' => 'Поле :attribute может содержать только буквы.', + 'alpha_dash' => 'Поле :attribute может содержать только буквы, цифры, дефис и нижнее подчёркивание.', + 'alpha_num' => 'Поле :attribute может содержать только буквы и цифры.', + 'array' => 'Поле :attribute должно быть массивом.', + 'ascii' => 'Поле :attribute может содержать только однобайтовые буквенно-цифровые символы.', + 'before' => 'Поле :attribute должно содержать дату до :date.', + 'before_or_equal' => 'Поле :attribute должно содержать дату не позже :date.', + 'between' => [ + 'array' => 'Количество элементов в поле :attribute должно быть от :min до :max.', + 'file' => 'Размер файла в поле :attribute должен быть от :min до :max Кбайт.', + 'numeric' => 'Поле :attribute должно быть от :min до :max.', + 'string' => 'Количество символов в поле :attribute должно быть от :min до :max.', + ], + 'boolean' => 'Поле :attribute должно иметь значение логического типа.', + 'can' => 'Поле :attribute содержит недопустимое значение.', + 'confirmed' => 'Поле :attribute не совпадает с подтверждением.', + 'current_password' => 'Неверный пароль.', + 'date' => 'Поле :attribute не является датой.', + 'date_equals' => 'Поле :attribute должно содержать дату, равную :date.', + 'date_format' => 'Поле :attribute не соответствует формату :format.', + 'decimal' => 'Поле :attribute должно иметь :decimal знаков после запятой.', + 'declined' => 'Поле :attribute должно быть отклонено.', + 'declined_if' => 'Поле :attribute должно быть отклонено, когда :other равно :value.', + 'different' => 'Поля :attribute и :other должны различаться.', + 'digits' => 'Длина цифрового поля :attribute должна быть :digits.', + 'digits_between' => 'Длина цифрового поля :attribute должна быть от :min до :max.', + 'dimensions' => 'Поле :attribute имеет недопустимые размеры изображения.', + 'distinct' => 'Поле :attribute содержит повторяющееся значение.', + 'doesnt_end_with' => 'Поле :attribute не должно заканчиваться одним из следующих значений: :values.', + 'doesnt_start_with' => 'Поле :attribute не должно начинаться с одного из следующих значений: :values.', + 'email' => 'Поле :attribute должно быть действительным электронным адресом.', + 'ends_with' => 'Поле :attribute должно заканчиваться одним из следующих значений: :values.', + 'enum' => 'Выбранное значение для :attribute некорректно.', + 'exists' => 'Выбранное значение для :attribute некорректно.', + 'extensions' => 'Поле :attribute должно иметь одно из следующих расширений: :values.', + 'file' => 'Поле :attribute должно быть файлом.', + 'filled' => 'Поле :attribute обязательно для заполнения.', + 'gt' => [ + 'array' => 'Количество элементов в поле :attribute должно быть больше :value.', + 'file' => 'Размер файла в поле :attribute должен быть больше :value Кбайт.', + 'numeric' => 'Поле :attribute должно быть больше :value.', + 'string' => 'Количество символов в поле :attribute должно быть больше :value.', + ], + 'gte' => [ + 'array' => 'Количество элементов в поле :attribute должно быть не меньше :value.', + 'file' => 'Размер файла в поле :attribute должен быть не меньше :value Кбайт.', + 'numeric' => 'Поле :attribute должно быть не меньше :value.', + 'string' => 'Количество символов в поле :attribute должно быть не меньше :value.', + ], + 'hex_color' => 'Поле :attribute должно содержать корректный шестнадцатеричный цвет.', + 'image' => 'Поле :attribute должно быть изображением.', + 'in' => 'Выбранное значение для :attribute некорректно.', + 'in_array' => 'Поле :attribute должно существовать в :other.', + 'integer' => 'Поле :attribute должно быть целым числом.', + 'ip' => 'Поле :attribute должно быть действительным IP-адресом.', + 'ipv4' => 'Поле :attribute должно быть действительным IPv4-адресом.', + 'ipv6' => 'Поле :attribute должно быть действительным IPv6-адресом.', + 'json' => 'Поле :attribute должно быть JSON-строкой.', + 'lowercase' => 'Поле :attribute должно быть в нижнем регистре.', + 'lt' => [ + 'array' => 'Количество элементов в поле :attribute должно быть меньше :value.', + 'file' => 'Размер файла в поле :attribute должен быть меньше :value Кбайт.', + 'numeric' => 'Поле :attribute должно быть меньше :value.', + 'string' => 'Количество символов в поле :attribute должно быть меньше :value.', + ], + 'lte' => [ + 'array' => 'Количество элементов в поле :attribute должно быть не больше :value.', + 'file' => 'Размер файла в поле :attribute должен быть не больше :value Кбайт.', + 'numeric' => 'Поле :attribute должно быть не больше :value.', + 'string' => 'Количество символов в поле :attribute должно быть не больше :value.', + ], + 'mac_address' => 'Поле :attribute должно быть корректным MAC-адресом.', + 'max' => [ + 'array' => 'Количество элементов в поле :attribute не может превышать :max.', + 'file' => 'Размер файла в поле :attribute не может быть больше :max Кбайт.', + 'numeric' => 'Поле :attribute не может быть больше :max.', + 'string' => 'Количество символов в поле :attribute не может превышать :max.', + ], + 'max_digits' => 'Поле :attribute не должно содержать более :max цифр.', + 'mimes' => 'Поле :attribute должно быть файлом одного из типов: :values.', + 'mimetypes' => 'Поле :attribute должно быть файлом одного из типов: :values.', + 'min' => [ + 'array' => 'Количество элементов в поле :attribute должно быть не меньше :min.', + 'file' => 'Размер файла в поле :attribute должен быть не меньше :min Кбайт.', + 'numeric' => 'Поле :attribute должно быть не меньше :min.', + 'string' => 'Количество символов в поле :attribute должно быть не меньше :min.', + ], + 'min_digits' => 'Поле :attribute должно содержать не менее :min цифр.', + 'missing' => 'Поле :attribute должно отсутствовать.', + 'missing_if' => 'Поле :attribute должно отсутствовать, когда :other равно :value.', + 'missing_unless' => 'Поле :attribute должно отсутствовать, если :other не равно :value.', + 'missing_with' => 'Поле :attribute должно отсутствовать, когда :values присутствует.', + 'missing_with_all' => 'Поле :attribute должно отсутствовать, когда :values присутствуют.', + 'multiple_of' => 'Поле :attribute должно быть кратным :value.', + 'not_in' => 'Выбранное значение для :attribute некорректно.', + 'not_regex' => 'Поле :attribute имеет некорректный формат.', + 'numeric' => 'Поле :attribute должно быть числом.', + 'password' => [ + 'letters' => 'Поле :attribute должно содержать хотя бы одну букву.', + 'mixed' => 'Поле :attribute должно содержать буквы верхнего и нижнего регистра.', + 'numbers' => 'Поле :attribute должно содержать хотя бы одну цифру.', + 'symbols' => 'Поле :attribute должно содержать хотя бы один символ.', + 'uncompromised' => 'Указанное значение :attribute было найдено в утечке данных. Пожалуйста, выберите другое значение :attribute.', + ], + 'present' => 'Поле :attribute должно присутствовать.', + 'present_if' => 'Поле :attribute должно присутствовать, когда :other равно :value.', + 'present_unless' => 'Поле :attribute должно присутствовать, если :other не равно :value.', + 'present_with' => 'Поле :attribute должно присутствовать, когда :values присутствует.', + 'present_with_all' => 'Поле :attribute должно присутствовать, когда :values присутствуют.', + 'prohibited' => 'Поле :attribute запрещено.', + 'prohibited_if' => 'Поле :attribute запрещено, когда :other равно :value.', + 'prohibited_unless' => 'Поле :attribute запрещено, если :other не входит в :values.', + 'prohibits' => 'Поле :attribute запрещает присутствие :other.', + 'regex' => 'Поле :attribute имеет некорректный формат.', + 'required' => 'Поле :attribute обязательно для заполнения.', + 'required_array_keys' => 'Поле :attribute должно содержать записи для: :values.', + 'required_if' => 'Поле :attribute обязательно для заполнения, когда :other равно :value.', + 'required_if_accepted' => 'Поле :attribute обязательно для заполнения, когда :other принято.', + 'required_unless' => 'Поле :attribute обязательно для заполнения, когда :other не входит в :values.', + 'required_with' => 'Поле :attribute обязательно для заполнения, когда :values указано.', + 'required_with_all' => 'Поле :attribute обязательно для заполнения, когда :values указаны.', + 'required_without' => 'Поле :attribute обязательно для заполнения, когда :values не указано.', + 'required_without_all' => 'Поле :attribute обязательно для заполнения, когда ни одно из :values не указано.', + 'same' => 'Значение поля :attribute должно совпадать со значением :other.', + 'size' => [ + 'array' => 'Количество элементов в поле :attribute должно быть равным :size.', + 'file' => 'Размер файла в поле :attribute должен быть равен :size Кбайт.', + 'numeric' => 'Поле :attribute должно быть равным :size.', + 'string' => 'Количество символов в поле :attribute должно быть равным :size.', + ], + 'starts_with' => 'Поле :attribute должно начинаться с одного из следующих значений: :values.', + 'string' => 'Поле :attribute должно быть строкой.', + 'timezone' => 'Поле :attribute должно содержать корректную временную зону.', + 'unique' => 'Такое значение поля :attribute уже существует.', + 'uploaded' => 'Загрузка поля :attribute не удалась.', + 'uppercase' => 'Поле :attribute должно быть в верхнем регистре.', + 'url' => 'Поле :attribute должно содержать корректный URL.', + 'ulid' => 'Поле :attribute должно содержать корректный ULID.', + 'uuid' => 'Поле :attribute должно содержать корректный UUID.', + + /* + | Кастомные сообщения для конкретных пар «атрибут.правило». + */ + 'custom' => [ + 'attribute-name' => [ + 'rule-name' => 'custom-message', + ], + ], + + /* + | Человекочитаемые имена полей (вместо сырых first_name и т.п.). + */ + 'attributes' => [ + 'first_name' => 'Имя', + 'last_name' => 'Фамилия', + 'name' => 'Название', + 'email' => 'Email', + 'phone' => 'Телефон', + 'password' => 'Пароль', + 'password_confirmation' => 'Подтверждение пароля', + 'timezone' => 'Тайм-зона', + 'comment' => 'Комментарий', + 'daily_limit' => 'Лимит лидов в день', + 'signal_identifier' => 'Источник', + 'signal_type' => 'Тип', + 'sms_senders' => 'Отправители', + 'sms_keyword' => 'Ключевое слово', + 'regions' => 'Регионы', + 'inn' => 'ИНН', + 'kpp' => 'КПП', + 'ogrn' => 'ОГРН', + 'ogrnip' => 'ОГРНИП', + 'legal_address' => 'Юридический адрес', + 'organization_name' => 'Название организации', + 'contact_name' => 'Контактное имя', + 'contact_phone' => 'Контактный телефон', + 'bank_name' => 'Наименование банка', + 'bik' => 'БИК', + 'account_number' => 'Расчётный счёт', + 'corr_account' => 'Корреспондентский счёт', + 'message' => 'Сообщение', + 'contact' => 'Контакт', + ], +]; diff --git a/docs/observer/STATUS.md b/docs/observer/STATUS.md index 2b8ac785..76996eec 100644 --- a/docs/observer/STATUS.md +++ b/docs/observer/STATUS.md @@ -1,6 +1,6 @@ # Brain Status (auto-generated) -Last updated: 2026-06-21T01:43:28.079Z +Last updated: 2026-06-21T11:34:36.544Z | Контролёр | Состояние | Детали | |---|---|---| @@ -33,22 +33,29 @@ Last updated: 2026-06-21T01:43:28.079Z | enforce-coverage-verify.mjs | `enforce-coverage-verify.mjs` | 🔴 | | enforce-todowrite-skill-verifier.mjs | `enforce-todowrite-skill-verifier.mjs` | 🔴 | -Недавние escape владельца: 0 · Недавние блоки: 10 +Недавние escape владельца: 2 · Недавние блоки: 10 + +**Недавние escape владельца (детали):** + +| Время | Действие | Причина | +|---|---|---| +| 2026-06-21T06:38:08.264Z | plan-done | escape владельца | +| 2026-06-21T02:22:23.069Z | owner-seal:e0f8a7dad28f952f5f18bf323a26f82cabe9f7bd3fd45c8477773af19b8f1af1 | escape владельца | **Недавние блоки (детали):** | Время | Действие | Причина | |---|---|---| -| 2026-06-21T01:43:31.680Z | powershell:$f = "$env:TEMP\claude-economy-07bce0ef-8004-4d79-a417-5c6315d3bbd4.json"; if (Test-Path $f) { Get-Content $f | разговорный режим: только думать/спрашивать (реализация — после печати артефакта и плана) | -| 2026-06-21T01:43:28.478Z | write:c:/users/administrator/.claude/projects/c--------------claude-brain/07bce0ef-8004-4d79-a417-5c6315d3bbd4.jsonl | path «C:/Users/Administrator/.claude/projects/c--------------claude-brain/07bce0ef-8004-4d79-a417-5c6315d3bbd4.jsonl» pr | -| 2026-06-21T01:43:23.370Z | bash:ls $TEMP/claude-economy-07bce0ef-8004-4d79-a417-5c6315d3bbd4.json 2>/dev/null && cat $TEMP/claude-economy-07bce0ef- | разговорный режим: только думать/спрашивать (реализация — после печати артефакта и плана) | -| 2026-06-21T01:43:23.221Z | bash:ls $TEMP/claude-economy-07bce0ef-8004-4d79-a417-5c6315d3bbd4.json 2>/dev/null && cat $TEMP/claude-economy-07bce0ef- | floor: опасная по содержанию команда без аварийного выхода — блок (правило 8); FLOOR-ESCAPE: bash:ls $TEMP/claude-econom | -| 2026-06-21T01:42:58.754Z | write:c:/моя/проекты/claude-brain | разговорный режим: только думать/спрашивать (реализация — после печати артефакта и плана) | -| 2026-06-21T01:42:48.892Z | powershell:$path = "$env:TEMP\claude-economy-07bce0ef-8004-4d79-a417-5c6315d3bbd4.json"; if (Test-Path $path) { Get-Cont | разговорный режим: только думать/спрашивать (реализация — после печати артефакта и плана) | -| 2026-06-21T01:42:45.501Z | bash:cat "$TEMP/claude-economy-07bce0ef-8004-4d79-a417-5c6315d3bbd4.json" 2>/dev/null \|\| echo "FILE_NOT_FOUND" | разговорный режим: только думать/спрашивать (реализация — после печати артефакта и плана) | -| 2026-06-21T01:42:45.325Z | bash:cat "$TEMP/claude-economy-07bce0ef-8004-4d79-a417-5c6315d3bbd4.json" 2>/dev/null \|\| echo "FILE_NOT_FOUND" | floor: опасная по содержанию команда без аварийного выхода — блок (правило 8); FLOOR-ESCAPE: bash:cat "$TEMP/claude-econ | -| 2026-06-21T01:42:41.095Z | write:c:/users/administrator/.claude/projects/c--------------claude-brain/07bce0ef-8004-4d79-a417-5c6315d3bbd4.jsonl | path «C:/Users/Administrator/.claude/projects/c--------------claude-brain/07bce0ef-8004-4d79-a417-5c6315d3bbd4.jsonl» pr | -| 2026-06-21T01:37:32.150Z | write:c:/моя/проекты/claude-brain | разговорный режим: только думать/спрашивать (реализация — после печати артефакта и плана) | +| 2026-06-21T11:20:27.250Z | write:c:/моя/проекты/claude-brain | разговорный режим: только думать/спрашивать (реализация — после печати артефакта и плана) | +| 2026-06-21T11:20:18.178Z | write:c:/users/administrator/.claude/projects/c--------------claude-brain/dbbe0ee2-5c67-4b7f-87c5-7f47f8bd14bd.jsonl | path «C:/Users/Administrator/.claude/projects/c--------------claude-brain/dbbe0ee2-5c67-4b7f-87c5-7f47f8bd14bd.jsonl» pr | +| 2026-06-21T11:20:10.771Z | bash:cat "$TEMP/claude-economy-dbbe0ee2-5c67-4b7f-87c5-7f47f8bd14bd.json" 2>/dev/null \|\| echo "FILE_NOT_FOUND" | разговорный режим: только думать/спрашивать (реализация — после печати артефакта и плана) | +| 2026-06-21T11:20:10.641Z | bash:cat "$TEMP/claude-economy-dbbe0ee2-5c67-4b7f-87c5-7f47f8bd14bd.json" 2>/dev/null \|\| echo "FILE_NOT_FOUND" | floor: опасная по содержанию команда без аварийного выхода — блок (правило 8); FLOOR-ESCAPE: bash:cat "$TEMP/claude-econ | +| 2026-06-21T11:19:59.051Z | write:c:/моя/проекты/claude-brain | разговорный режим: только думать/спрашивать (реализация — после печати артефакта и плана) | +| 2026-06-21T11:19:48.786Z | write:c:/users/administrator/.claude/projects/c--------------claude-brain/dbbe0ee2-5c67-4b7f-87c5-7f47f8bd14bd.jsonl | path «C:/Users/Administrator/.claude/projects/c--------------claude-brain/dbbe0ee2-5c67-4b7f-87c5-7f47f8bd14bd.jsonl» pr | +| 2026-06-21T11:19:43.736Z | bash:cat "$TEMP/claude-economy-dbbe0ee2-5c67-4b7f-87c5-7f47f8bd14bd.json" 2>/dev/null \|\| echo "FILE_NOT_FOUND" | разговорный режим: только думать/спрашивать (реализация — после печати артефакта и плана) | +| 2026-06-21T11:19:43.631Z | bash:cat "$TEMP/claude-economy-dbbe0ee2-5c67-4b7f-87c5-7f47f8bd14bd.json" 2>/dev/null \|\| echo "FILE_NOT_FOUND" | floor: опасная по содержанию команда без аварийного выхода — блок (правило 8); FLOOR-ESCAPE: bash:cat "$TEMP/claude-econ | +| 2026-06-21T11:10:44.290Z | write:c:/моя/проекты/claude-brain | разговорный режим: только думать/спрашивать (реализация — после печати артефакта и плана) | +| 2026-06-21T11:10:36.250Z | bash:ls "$TEMP/claude-economy-dbbe0ee2-5c67-4b7f-87c5-7f47f8bd14bd.json" 2>/dev/null && cat "$TEMP/claude-economy-dbbe0e | разговорный режим: только думать/спрашивать (реализация — после печати артефакта и плана) | ## Метрики (информационные, не алерты) @@ -128,15 +135,7 @@ Episodes since last run: 542 / threshold: 10 ## System Health -Топ-3 процессов с CPU > 1ч: - -| PID | Имя | CPU-время | Возраст | -|---|---|---|---| -| 3024 | Code | 4.90ч | 0.0ч | -| 3260 | MsMpEng | 3.99ч | 0.0ч | -| 1108 | svchost | 2.07ч | NaNч | - -⚠️ Проверь, не «осиротевшие» ли это процессы от завершённых Claude-сессий. +Долго работающих процессов нет (порог CPU > 1ч). ## Очередь обучения роутера @@ -150,7 +149,7 @@ Episodes since last run: 542 / threshold: 10 ## Целостность журналов действий -🔴 Битые цепочки (3 из 97): +🔴 Битые цепочки (3 из 102): | session | broken at seq | |---|---|