Commit Graph

3 Commits

Author SHA1 Message Date
Дмитрий 6c01d3e08c обзвон З-2.1: заведено хранилище записей разговоров и переезд звука в него
Портал давал человеку два обещания про запись его разговора — «живёт месяц и
стирается сама» и «потребуешь удалить свои данные — удалим и голос». Обе чистки
были построены и обе ходили в хранилище с именем obzvon_zapisi, которого в
настройках НЕ СУЩЕСТВОВАЛО. Врать они не врали — честно отвечали «не смог
удалить файл», — но удалять им было нечего. Оба обещания были бумажными.

Построено:
  • config/filesystems.php — диск obzvon_zapisi. Драйвер переключается одной
    переменной окружения OBZVON_ZAPISI_DRIVER: в день покупки объектного
    хранилища меняется настройка, а не код;
  • ObzvonZapisHranilishche — единственная дверь к звуку. Приём с проверкой,
    открытие ПОТОКОМ со следом в журнале ПДн ДО чтения, ссылка со сроком в час
    там, где хранилище её умеет, и NULL там, где не умеет;
  • PereveztiZapisJob — переезд записи с сервера робота. Не воскрешает стёртый
    голос, не создаёт вторую копию, не оставляет сироты при проигранной гонке;
  • ZvukovoyFayl — одно место, где живёт знание «что такое запись разговора»:
    расширения, подписи форматов, граница пустой записи в 44 байта.

Три замка изоляции, и все три нужны: RLS на строке, явная сверка клиента и
сверка САМОГО ПУТИ — recording_path заполняет чужая машина, и путь вида
7/../8/golos.wav увёл бы в папку другого клиента при законной сверке клиента.

Исправлена ложь в бумаге. В комментарии диска obzvon_materialy было написано,
что вызов url на закрытом диске бросит исключение и публичной ссылки не
появится. Замерено живым запуском: у местного драйвера url не бросает НИКОГДА,
а молча отдаёт /storage/путь. Ссылка ведёт в папку публичного диска, где записи
нет, — вреда нет, но и обещанной защиты нет. Настоящая защита в том, что url не
зовёт никто.

Ловушка круга: проверка З-2.2 утверждала, что хранилища нет, и покраснела бы от
самой постройки. Вычеркнута не была — переписана. Она стерегла живое: пометить
строку стёртой при живом файле значит выкинуть её из указателя под чистку
навсегда. Теперь «файл жив, а убрать не вышло» строится честно — подставлено
хранилище, у которого delete отвечает отказом.

22 новых сторожа, каждый показан красным в пять заходов. Убрана подмена
настроек в заготовке z22DiskZapisey — пять сторожей З-2.2 теперь охраняют и
наличие хранилища тоже.

Прогон обзвона 212 из 212, статанализ 0 ошибок.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 11:02:17 +03:00
Дмитрий 2b401298d8 feat: приём записей клиента для обучения робота обзвона — З-2.3
Клиент отдаёт нам записи СВОИХ холодных разговоров. Это голоса живых людей,
которые об этом не знают и согласия нам не давали. До этой правки принять их
было негде и нечем: положить так, чтобы клиент А не открыл запись клиента Б,
было некуда; срока жизни у записей не было; обращение к чужому голосу не
оставляло следа.

Что заведено:

- таблица obzvon_materialy_klienta — канон в db/schema_modules.sql раздел 24,
  журнал схемы v9.70. Изоляция клиентов защитой по строкам: ENABLE + FORCE,
  политика с USING и WITH CHECK. DELETE не выдан никому — строка остаётся
  носителем надписи «удалены по сроку хранения»;
- ДВА срока, а не один — решение владельца Р82: звук 30 дней, расшифровка
  шесть месяцев. Обе даты истечения NOT NULL: строка без даты означает голос,
  которого не найдёт ни одна чистка. Сами сроки — в настройках портала,
  не в коде;
- два следа удаления врозь: команда чистки из З-2.2 обязана уметь стереть
  звук, не тронув текст. Два замка не дают строке врать «стёрто», пока файл жив;
- закрытое хранилище obzvon_materialy: без ключа url и с serve=false, корень
  под storage/app/private. Публичной ссылки на чужой голос не появится даже
  по ошибке;
- служба MaterialyKlientaService: приём с отклонением чужого формата и битого
  файла, открытие с явной сверкой клиента поверх защиты по строкам, счёт
  записей без нижней границы — три берём, двадцать берём, ноль пускаем
  вторым путём;
- отказ MaterialNePrinyat отделяет наш сочинённый текст от дословного
  показания системы видимой границей. Настоящая причина не выбрасывается;
- след в журнале ПДн pd_processing_log готовым сервисом PdAuditLogger —
  на приём и на каждое открытие.

Стирает НЕ эта правка, а З-2.2, и она не построена. До её постройки материалы
не исчезнут — это известно и так задумано.

При накатке на боевой ОБЯЗАТЕЛЕН перезапуск db/03_service_bypass_policies.sql:
новой RLS-таблице мало политики и прав, иначе служебная роль молча правит
НОЛЬ строк.

Тронут один чужой сторож: SchemaDeltaTest, счётчик таблиц канона модулей
42 в 43. Так же его правили соседи на З-1.1 и З-1.3 — счётчик обязан расти
вместе с каноном, иначе прогон красный у всех.

Осталось открытым: от какой даты считать полгода — считаем от загрузки,
столбец recorded_at заведён под будущий ответ владельца.

24 сторожа, все 24 показаны красными двенадцатью ножами.
Полный прогон 4954 проверки, 4950 зелёных, 4 пропущено, красных 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 20:35:21 +03:00
Дмитрий 4d38f75826 phase1(scaffold): Laravel 11 + predis + .env под PG 16 + Memurai
Триггер фазы 1 запущен 08.05.2026 (вечер):
composer create-project laravel/laravel app

Стек подтверждён native (без Docker/WSL2/Sail):
- PostgreSQL 16.13 (Chocolatey, Windows-сервис, port 5432)
- Memurai Developer 4.1.8 (Redis 7-совм., port 6379) — TCP +PONG OK
- PHP 8.3.31 + 11/11 Laravel-required ext (pdo_pgsql, mbstring,
  openssl, tokenizer, xml, ctype, json, bcmath, fileinfo, curl, pgsql)
- Composer 2.9.7

Что в коммите (59 файлов, 11059 строк скаффолда Laravel 11 +
правки):
- composer require predis/predis (v3.4.2) — PHP-only Redis-клиент,
  т.к. php_redis ext не установлен (см. project_phase1_strategy.md)
- app/.env (gitignored) — APP_NAME=Liderra, APP_LOCALE=ru,
  APP_TIMEZONE=Europe/Moscow, DB_CONNECTION=pgsql → liderra@localhost,
  REDIS_CLIENT=predis
- app/.env.example — те же правки без секретов (для команды)

Smoke-test PG ↔ Laravel ↔ pdo_pgsql прошёл:
3/3 default-миграций → 9 таблиц в liderra (cache, cache_locks,
failed_jobs, job_batches, jobs, migrations, password_reset_tokens,
sessions, users).

Артефакт стартера app/database/database.sqlite (0 B) удалён —
sqlite не используется.

Что НЕ в этом коммите (следующие шаги фазы 1):
- Pest 3 swap (CTO-12) — composer remove phpunit + require pest
- Laravel Boost MCP + 9 guidelines disable по CLAUDE.md §5/§7
- Pint, Larastan, Roave/SecurityAdvisories, IDE Helper, squawk,
  pgFormatter (Прил. Н #11–18)
- resources/boost/guidelines/vuetify.blade.php

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 09:37:16 +03:00