В прогоне курса агент закончил разделение кабинета на школы за 91 ход и написал: «Изоляция работает корректно». Я запустил тесты сам. Из 32 тестов 13 упали, ещё 8 закончились ошибкой. Это 21 провал при отчёте «всё хорошо». С этого урока в П6 начинается главная линия: зелёные слова агента не равны зелёным тестам, а иногда и зелёные тесты врут.
Чему научишься в этом уроке:
• ставить агенту план переделки без правок и задание на разделение школ;
• называть все места, где одна школа может увидеть чужие данные;
• запускать тесты самому и сравнивать итог с отчётом агента;
• останавливать агента, который чинит вслепую или кончил деньги на ключе.
Что такое изоляция
Представь банковские ячейки. Хранилище одно, но у каждого клиента своя ячейка и свой ключ. Служащий банка видит, какие ячейки заняты и оплачены, но не открывает их. Так будет устроен наш кабинет: база одна, у каждой записи метка, какой школе она принадлежит (колонка school_id), и каждый запрос к данным идёт только с меткой своей школы. Это и называется изоляцией, а сервис, где в одной базе живут данные многих клиентов, называют мультиарендным.
Для 3–10 школ одной базы SQLite хватает. Другая база (PostgreSQL) и упаковка в контейнеры (Docker) из источника курса нам не нужны: второй набор технологий ради нескольких школ только добавит работы.
Сначала план, без правок
Перед началом сделай коммит. В прогоне git в папке проекта не было, и откатить агента одной командой было нельзя. Если у тебя тоже нет: git init, проверь, что .env и crm.db есть в .gitignore, затем git add . и git commit -m "перед П6" (урок 1.4).
Задание 43 (план). Первое задание просит только план, без правок:
Работай ТОЛЬКО в текущей папке проекта (<путь к папке проекта>) и на сервере <адрес сервера>. Другие папки на диске не открывай и не меняй.
Новая задача: превращаем «Кабинет заявок» в подписку для нескольких школ (мини-SaaS). Школа регистрируется сама, 14 дней пробно, дальше 1 490 ₽ в месяц через ЮKassa (тестовый магазин). У каждой школы свои заявки, пользователи, дашборд и выводы — школа А не видит ничего от школы Б. Нынешние данные «Кодиума» становятся школой №1. Новая роль admin — администратор сервиса: видит школы, сроки и оплаты, но не видит заявок. При неоплате кабинет школы закрыт («оплатите тариф»), но приём заявок от бота и формы продолжает работать.
Сейчас ничего не меняй — только план:
1. Какие таблицы и колонки добавятся, как перенести текущие данные в школу №1 без потерь.
2. Ключ приёма заявок — свой у каждой школы; что будет с нынешним ключом бота и формы.
3. Где в коде нужна проверка «своя школа» (список, карточка заявки по прямому адресу, выгрузка xlsx, аналитика, дашборд, выводы недели, журнал, пользователи) и как это проверить тестами.
4. Оплата: как создаётся платёж, как приходит уведомление на /pay/notify, как убедиться, что уведомление настоящее, как не продлить дважды.
5. С сервера выполни curl -sI https://api.yookassa.ru/v3/ и покажи код ответа.
Без PostgreSQL, Docker и новых сервисов — остаёмся на SQLite и одной службе. Ответ — план по пунктам и список рисков.
План вышел годный, но с одной выдумкой: агент описал проверку подписи уведомлений ЮKassa и сослался на документацию. Такой подписи у ЮKassa нет. Подробнее в уроке 7.4. Вывод для этого урока: план тоже проверяешь, особенно где агент ссылается на документацию. А curl показал, что сервер в Москве до ЮKassa достаёт: ответ 401, то есть «без ключа нельзя».
Задание на разделение
Задание 44 (разделение). Подставь свой путь к папке и данные своего заказчика:
Работай ТОЛЬКО в текущей папке проекта (<путь к папке проекта>). Другие папки на диске не открывай и не меняй. На сервер пока не выкладывай.
Это «Кабинет заявок» (FastAPI + Jinja2 + SQLite). Прочитай README.md и MIGRATION_REPORT.md. Превращаем его в подписку для нескольких школ. Сейчас делаем ТОЛЬКО разделение на школы, оплату — следующим шагом.
1. Таблица schools (название, ключ приёма заявок, пробный период до, оплачено до). Нынешние данные «Кодиума» — школа №1, нынешний ключ приёма из .env — её ключ; бот и форма продолжают работать без изменений. Миграция при старте приложения, идемпотентная, перед ней — копия базы.
2. school_id у заявок, пользователей, журнала, выводов недели. Каждый запрос к данным — только своей школы: список, карточка по прямому адресу (чужая — 404), смена статуса, удаление, выгрузка xlsx, аналитика, дашборд, выводы недели, журнал, пользователи.
3. Страница /register: школа регистрируется сама (название, логин и пароль владельца), получает 14 дней пробного периода и свой ключ приёма — показать владельцу в кабинете.
4. Роль admin: список школ со сроками, без доступа к заявкам (403). Первого admin создать командой manage.py, пароль — в файл с правами 600, не в чат.
5. Тесты изоляции: две школы, владелец А не видит заявок Б ни в одном из мест пункта 2; admin получает 403 на заявки. Старые тесты не удалять и не ослаблять.
pytest -q — полный итог. В ответе — список изменённых мест, где добавлена проверка школы.
Обрати внимание на пункт 5: «Старые тесты не удалять и не ослаблять». Без этой строки у агента есть простой путь к зелёному итогу. Его он и выбирал в прогоне не раз.
Где школа может увидеть чужое
Проверять изоляцию только в списке заявок мало. Чужие данные утекают через любую дверь, которую забыли запереть:
- Список заявок. Только свои.
- Карточка по прямому адресу. Владелец А вписывает в адресную строку номер заявки школы Б (
/zayavki/57) и должен получить 404, «не найдено». - Выгрузка xlsx. В файле только свои строки.
- Аналитика, дашборд и выводы недели. Числа только по своей школе, и модели уходят только свои сводки.
- Журнал и пользователи. Чужих менеджеров и чужих смен статуса не видно.
- Приём заявок. У каждой школы свой ключ приёма. Школу определяет ключ, а не поле в заявке: поле подделать легко.

21 провал при «изоляция работает»
После отчёта агента я запустил .venv/Scripts/python.exe -m pytest -q сам. Падали и старые тесты, которые до переделки проходили. Дальше было три захода на починку, и каждый учит своему.
Заход 1, задание 44b. Я привёл агенту свои цифры и запретил удалять, пропускать и ослаблять тесты:
Работай ТОЛЬКО в текущей папке проекта (<путь к папке проекта>). Другие папки не открывай. На сервер не выкладывай.
Прошлым шагом ты разделил кабинет на школы и написал «Изоляция работает корректно». Я запустил pytest -q сам: 13 failed, 11 passed, 8 errors. Падают в том числе старые тесты test_main.py (PermissionError WinError 32 — файл базы занят; sqlite3.OperationalError) и test_admin_can_access_schools_list (401 вместо 200).
Почини так, чтобы pytest -q прошёл полностью. Нельзя: удалять, пропускать (skip/xfail) или ослаблять тесты; добавлять в боевой код обходы для тестов. Если тест устарел из-за школ — поправь его подготовку данных, а проверку оставь той же силы, и перечисли такие тесты отдельно.
В ответе — вывод последней строки pytest -q целиком.
78 ходов, осталось 9 падений. И ответ пришёл на украинском, хотя ни я, ни задание по-украински не писали. Мелочь, но признак: читай ответ целиком, а не только последнюю строку. С тех пор в заданиях строка «Отвечай по-русски».
Заход 2. Агент снова менял код, но тесты не запускал. Он честно сказал почему: команда запуска тестов не была в списке разрешённых, и ему нужно подтверждение. Править тесты, не запуская их, это стрельба вслепую. Разрешил команду, задание 44d:
Теперь тебе разрешён запуск .venv/Scripts/python.exe -m pytest -q. Я запустил сам: 9 failed, 24 passed — твои правки без запуска тестов не помогли. Запусти тесты, разберись по выводу и почини до полного прохода с теми же запретами (не удалять, не пропускать, не ослаблять, без обходов в боевом коде). Отвечай по-русски. В ответе — последняя строка pytest -q целиком.
Заход 3. 150 ходов, 33 теста прошли. И тут посреди шага пришёл ответ CloseRouter:
402 API key spend limit exceeded
Месячный лимит учебного ключа CloseRouter в 5 $ кончился. На весь П6 агент потратил около 10 $, и самые дорогие шаги были именно такими длинными. Новая сессия выходит дешевле продолжения длинной. Чтобы она не начинала с нуля, веди тетрадь проекта из урока про поток: агент первым делом читает brief.json, а в конце шага дописывает итог.
Правило паттерна: тесты запускаешь сам и сравниваешь последнюю строку с отчётом агента. «Изоляция работает» без числа прошедших тестов ничего не значит.
Проверь глазами
- Зарегистрируй две школы через
/registerи войди ими в двух разных браузерах. - Под школой А открой заявку школы Б по прямому адресу: должно быть 404.
- Выгрузи xlsx под школой А и проверь, что чужих строк нет.
- Войди администратором и открой
/zayavki: должно быть 403. - Сравни число тестов до и после работы агента:
.venv/Scripts/python.exe -m pytest --collect-only -qпокажет, сколько тестов собрано. Стало меньше, значит, тесты удаляли.
Если не получилось
- Агент пишет «изоляция работает», а
pytest -qпоказывает падения → отправь ему последнюю строку и названия упавших тестов, повтори запреты. - Агент правит, но не запускает тесты → проверь, разрешена ли ему команда запуска, и разреши.
- Ответ на чужом языке → добавь «Отвечай по-русски» и перечитай ответ полностью.
- Ошибка 402
spend limit exceeded→ кончился лимит ключа; подними лимит в кабинете CloseRouter или дождись нового месяца, продолжай в новой сессии. - Падают старые тесты с
PermissionErrorна файле базы → на Windows не закрываются соединения с базой; это ошибка кода или подготовки тестов, а не повод их удалять. - Бот и форма перестали доставлять заявки → нынешний ключ приёма должен стать ключом школы №1; попроси тест на этот путь.
Контрольный вопрос. Агент написал «Изоляция работает корректно». Что ты делаешь первым?
Подсказка: Что в прогоне показал собственный запуск тестов?
На курсе задания проверяются автоматически. Решить первое задание бесплатно → · Подобрать курс
Контрольный вопрос. Владелец школы А открыл в адресной строке номер заявки школы Б. Какой ответ правильный?
Подсказка: Должна ли школа А вообще узнать, что такая заявка существует?
На курсе задания проверяются автоматически. Решить первое задание бесплатно → · Подобрать курс
Контрольный вопрос. Как кабинет должен узнавать, к какой школе относится заявка от бота?
Подсказка: Что из этого нельзя подделать, не зная секрета?
На курсе задания проверяются автоматически. Решить первое задание бесплатно → · Подобрать курс
Задание. МИССИЯ «Две школы, ни одной утечки». 1. Сделай коммит. Запиши число тестов до работы агента (pytest —collect-only -q). 2. Дай агенту «Задание 43 (план)» и проверь его: данные текущего заказчика уходят в школу №1, нет PostgreSQL и Docker. Отметь, если агент ссылается на документацию, и проверь хотя бы одну такую ссылку сам. 3. Дай «Задание 44 (разделение)». Подставь свой путь к папке, данные своего заказчика и свою цену. 4. Запусти pytest -q сам. Если есть падения, чини заходами из урока (задания 44b и 44d), пока не будет полного прохода. 5. Сломай и почини: под школой А открой заявку школы Б по прямому адресу. Если ответ не 404, дай агенту задание на исправление с тестом именно на этот адрес и повтори. Принято, если: приложены число тестов до и после (после не меньше), последняя строка pytest -q без падений, снимки двух школ в двух браузерах, снимок 404 на чужую заявку, снимок 403 у администратора на /zayavki и выгрузка xlsx школы А без чужих строк.
Подсказка: Начни проверку с прямого адреса чужой заявки: это дверь, которую забывают чаще всего.
✅ Готово, если: программа запускается без ошибок и выводит то, что просят в задании.
На курсе задания проверяются автоматически. Решить первое задание бесплатно → · Подобрать курс
Читайте также: AI-агент говорит «готово», а тесты упали: как проверять работу Claude Code
Что дальше
Школы разделены и проверены. Следующий шаг: оплата через ЮKassa в тестовом режиме. Там агент сделал верный код, отчитался «все тесты проходят» и не написал ни одного теста оплаты.
Итог:
• у каждой записи метка школы, у каждого запроса фильтр по ней, у каждой школы свой ключ приёма;
• изоляцию проверяешь у всех дверей: список, адрес, xlsx, дашборд, выводы, журнал, пользователи;
• отчёт агента сверяешь со своим запуском тестов и числом тестов;
• длинные сессии дорогие: следи за лимитом ключа и начинай новую.
