← На главную
Гайды· 06.07.2026· 4 мин чтения

$149 и 37 промптов: как Саймон Уиллисон отдал финальный релиз sqlite-utils на откуп Claude Fable

Саймон Уиллисон выпустил sqlite-utils 4.0rc2 с помощью Claude Fable — 37 промптов, 34 коммита, критический баг с потерей данных. Разбираем процесс по шагам.

$149 и 37 промптов: как Саймон Уиллисон отдал финальный релиз sqlite-utils на откуп Claude Fable
Материал подготовлен с помощью ИИ и проверен редактором

Саймон Уиллисон — соавтор Django и создатель Datasette — выпустил sqlite-utils 4.0rc2, и большую часть работы над ним сделала языковая модель. Не как эксперимент ради хайпа, а как рабочий процесс с реальным багом, который мог уйти в стабильный релиз и сломать данные пользователей.

Вот как это было устроено и что из этого можно взять в свой процесс.


Зачем вообще подключать модель к финальному ревью

Уиллисон придерживается SemVer строго: мажорные версии выходят редко, и каждая должна быть максимально чистой. Перед тем как выпустить стабильный 4.0, он запустил Claude Fable (через Claude Code for Web прямо с iPhone) с одним конкретным промптом:

`` Final review before shipping a stable 4.0 release - very important to spot any last minute things that would be a breaking change if we fix them later ``

Модель вернула отчёт с пятью «release blockers» — проблемами, которые сам автор ещё не встретил.


Самый опасный баг: delete_where() молча терял данные

Первый и худший из найденных багов — в методе Table.delete_where() в файле sqlite_utils/db.py:2948. Метод выполнял DELETE через голый self.db.execute() без обёртки atomic(). В результате соединение оставалось в состоянии in_transaction=True, и все последующие вызовы atomic() уходили в ветку savepoint — и никогда не коммитились.

Воспроизводится так:

```python import sqlite_utils

db = sqlite_utils.Database("dw.db") db["t"].insert_all([{"id": i} for i in range(3)], pk="id") db["t"].delete_where("id = ?", [0]) # conn.in_transaction теперь True db["t"].insert({"id": 50}) db["u"].insert({"a": 1}) db.close()

Переоткрываем базу — там строки [0, 1, 2]

Удаление, строка 50 и таблица u — всё пропало

```

Это именно потеря данных, а не просто неожиданное поведение. При этом, как отмечает сам Уиллисон, баг можно было бы исправить в 4.0.1 — это не архитектурный просчёт, который потребовал бы 5.0. Но всё равно очень хорошо, что он не ушёл в релиз.


Как выглядел рабочий процесс: 37 промптов за один день

Весь процесс занял 37 промптов, 34 коммита и затронул 30 файлов: +1 321 строка добавлена, −190 удалено. Уиллисон работал с моделью итерационно: давал задачу, ждал 10–15 минут пока Fable «перемалывает» её, и возвращался с следующим шагом.

Параллельно он ходил на парад по случаю 4 июля в Half Moon Bay — и это не метафора, а буквальное описание того, как работают агентные задачи: чем сложнее задача, тем больше времени можно делать что-то другое.

Ключевые изменения касались модели транзакций — главной новой фичи RC. Итоговое поведение библиотеки теперь описывается так:

```python from sqlite_utils import Database

db = Database("data.db") db.table("news").insert({"headline": "Dog wins award"})

Строка уже сохранена — commit() не нужен

```

Каждый метод, пишущий в базу (insert(), upsert(), update(), delete(), delete_where(), transform() и остальные), теперь сам оборачивается в транзакцию и коммитит её до возврата. Явный commit() нужен только в двух случаях: когда вы группируете операции через db.atomic() или когда сами открыли транзакцию через db.begin().


Почему одна модель ревьюит другую — и это работает

После того как Fable закончил работу, Уиллисон прогнал финальное ревью через GPT-5.5 (Codex Desktop, режим xhigh) с простым промптом:

`` Review changes since the last RC. Also confirm that the changelog is up-to-date. ``

GPT-5.5 нашёл два новых P1-бага:

  1. db.query("update ...") сначала выполняет и коммитит UPDATE, а потом бросает ValueError — потому что метод задокументирован только для SELECT-запросов. Данные уже изменены, а пользователь получает исключение.
  1. INSERT ... RETURNING через db.query() коммитится только после того, как генератор полностью исчерпан. Если вы делаете next(db.query("insert ... returning ...")) и не итерируете дальше — транзакция остаётся открытой и может откатиться при закрытии соединения. Это противоречит документации.

Уиллисон признаётся, что раньше считал идею «одна модель ревьюит другую» суеверием. Теперь это часть его стандартного процесса: лучшая модель Anthropic ревьюит работу OpenAI и наоборот.


Подводные камни

Python 3.12 и autocommit. В Python 3.12 появился параметр sqlite3.connect(..., autocommit=True/False). Оказалось, что sqlite-utils с ним практически не работал — почти весь тест-сьют падал. Fable нашёл это в документации, которую сам же написал, и Уиллисон отдельно проработал совместимость.

Документация как первый артефакт для ревью. Уиллисон специально читает документацию, которую пишет модель, раньше кода — это быстрый способ понять, что вообще изменилось, и поймать концептуальные ошибки до погружения в diff.

Агент не заменяет финальный взгляд автора. Переключение на ноутбук и ревью через GitHub PR interface — это осознанный шаг. Интерфейс меняет восприятие: в браузере видишь diff иначе, чем в IDE или терминале.

Стоимость не нулевая. $149.25 за один рабочий день — это не «бесплатно». Для open-source проекта это реальные деньги, и важно понимать, что речь идёт о Max-подписке с ограниченным временем доступа к Fable.


Что попробовать дальше

Если хотите воспроизвести похожий процесс на своём проекте:

  • Начните с промпта на «release blockers» перед любым мажорным релизом — даже если уверены в коде
  • Используйте разные модели для генерации и ревью: Anthropic → OpenAI или наоборот
  • Читайте документацию, которую пишет модель, до кода — это экономит время
  • Следите за поведением транзакций в sqlite-utils 4.0: новая модель автокоммита меняет привычные паттерны работы с библиотекой

Источники

Автор: PLai AI