۱۴۹ دلار؛ این مبلغ هزینه حسابرسی (Audit) فنی بود که کتابخانه sqlite-utils نسخه ۴.۰ را از یک باگ بحرانی حذف داده نجات داد. در ۵ جولای ۲۰۲۶، سایمون ویلسون (Simon Willison) از Claude Fable (که از طریق اشتراکهای Max در دسترس است) استفاده کرد تا تغییرات شکستهساز (breaking changes) را رصد کند. او متوجه شد متد delete_where() باعث مسموم شدن اتصالات پایگاه داده میشود و منجر به این میگردد که نوشتههای بعدی بهصورت بیصدا نادیده گرفته و ذخیره نشوند.
این تجربه گامی فراتر از تولید کد ساده است. در حالی که پوششهای قبلی ما توضیح داد که توسعهدهنده چگونه از ابزارهای جدید برای مدیریت پروژهها بهره میگیرند، از جمله اینکه کاربران چگونه از چتهای ساده به سمت مهندسی برنامهریزیشده با Claude Code حرکت کردهاند، این گردشکار بر نقش «عامل» (Agent) به عنوان یک بازرس سختگیر تضمین کیفیت (QA) در موقعیتهای حساس تأکید دارد. رویکرد ویلسون از یک درخواست تکپرامپتی شروع شد: «بازبینی نهایی پیش از انتشار نسخه پایدار ۴.۰؛ شناسایی هر موردی که اگر بعداً اصلاح شود، باعث شکست کد کاربران شود بسیار حیاتی است». اما این مسیر به یک چرخه شدید ۳۷ مرحلهای تبدیل شد که شامل ۳۴ کامیت و بیش از ۱۳۲۱ تغییر مثبت و ۱۹۰ تغییر منفی در ۳۰ فایل مجزا بود.
کشف «سد راه انتشار»
طبق گزارش منتشر شده، شدیدترین یافته، شکست در مدیریت تراکنشها بود. Claude Fable پنج مورد را شناسایی کرد که آنها را به عنوان «سد راه انتشار» (release blockers) دستهبندی کرد. بدترین این موارد، باگی در Table.delete_where() بود (واقع در sqlite_utils/db.py:2948) که عملیات DELETE را از طریق یک self.db.execute() ساده و بدون پوشش atomic() اجرا میکرد. در مقابل، متد Table.delete() در خط db.py:2944 به درستی در یک Wrapper قرار گرفته بود.
این نقص باعث میشد اتصال در حالتی باقی بماند که in_transaction=True باشد. در نتیجه، هر فراخوانی بعدی atomic() به جای شروع تراکنش جدید، وارد شاخه savepoint میشد (db.py:430-440) و در نهایت هرگز commit نمیشد. ویلسون این مورد را بهصورت کامل (end-to-end) بازتولید کرد:
۱. ایجاد یک پایگاه داده با دستور db = sqlite_utils.Database("dw.db") و درج سه سطر در جدول "t" با استفاده از 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})) و ایجاد یک جدول جدید "u" به همراه داده (db["u"].insert({"a": 1})).
۴. بستن پایگاه داده و باز کردن مجدد آن.
نتیجه فاجعهبار بود: عملیات حذف، سطر ۵۰ و جدول "u" همگی ناپدید شده بودند. اگرچه این مورد در یک نسخه اصلاحی ۴.۰.۱ قابل رفع بود، اما جلوگیری از آن در مرحله انتشار، مانع از یک نقص طراحی شد که احتمالاً توسعهدهندگان را مجبور به جهش به نسخه ۵.۰ میکرد.
خط لوله بازبینی چندمدلی
ویلسون برای اطمینان بیشتر، استراتژی «بازبینی متقاطع» (cross-review) را اجرا کرد. او از GPT-5.5 xhigh و Codex Desktop خواست تا کارهای انجام شده توسط مدل Claude را بررسی کنند. ویلسون از آنها خواست تغییرات اعمال شده از آخرین نسخه RC (Release Candidate) را بازبینی کرده و تأیید کنند که لیست تغییرات (changelog) بهروز است. این لایه دوم نظارت، دو مورد لبهای (edge case) دیگر را افشا کرد:
- کامیتهای زودهنگام: در
sqlite_utils/db.py:663متدdb.query()دستورات غیر-row را تنها بعد از فراخوانیdb.execute()رد میکرد. از آنجا کهdb.execute()(درdb.py:705) ابتدا نوشتهها را بهطور خودکار کامیت میکند، فراخوانی دستوری مثلdb.query("update ...")باعث میشد آپدیت پیش از بروز خطایValueErrorدر دیتابیس ذخیره شود. این رفتار با مستندات متد که به عنوان ابزاری برای SQLهای بازگرداننده سطر تعریف شده بود، در تضاد بود. - نشت ژنراتورها: در
sqlite_utils/db.py:672دستوراتINSERT ... RETURNINGاز طریقdb.query()تنها زمانی کامیت میشدند که ژنراتور بازگشتی کاملاً تخلیه (exhausted) شود. اگر کاربر ازnext(db.query(...))استفاده میکرد یا اصلاً روی نتایج پیمایش نمیکرد، تراکنش باز میماند و با بستن دیتابیس، نوشتهها rollback میشدند. این مورد با مستندات درdocs/changelog.rst:15وdocs/python-api.rst:232ناسازگار بود.
مهندسی پایداری نسخه ۴.۰
عامل هوش مصنوعی علاوه بر رفع باگها، به بازسازی کامل مدل تراکنش کمک کرد. در نسخه فعلی پایدار، تضمین شده است که هر متد نوشتاری — شامل insert()، upsert()، update()، delete()، delete_where()، transform()، create_table()، create_index() و enable_fts() — در تراکنش خود اجرا شده و پیش از بازگشت نتیجه، کامیت شود.
این بدان معناست که فراخوانی دستوری مانند db.table("news").insert({"headline": "Dog wins award"}) سطر را بلافاصله روی دیسک ذخیره میکند بدون اینکه کاربر نیاز به فراخوانی دستی commit() یا بستن دیتابیس داشته باشد. همین منطق برای SQLهای خام اجرا شده با db.execute() نیز اعمال میشود.
اکنون دقیقاً دو سناریو وجود دارد که کاربر باید تراکنشها را مدیریت کند:
۱. برای گروهبندی چندین عملیات نوشتاری تا همه با هم موفق یا شکست بخورند، با استفاده از db.atomic().
۲. مدیریت دستی تراکنش با db.begin()؛ در این حالت هیچ چیز تا فراخوانی commit() ذخیره نمیشود و کتابخانه هرگز تراکنشی که به این روش باز شده باشد را بهطور خودکار کامیت نمیکند.
ویلسون همچنین با کمک مدل، ناسازگاریهای تنظیمات autocommit در پایتون ۳.۱۲ را حل کرد. او کشف کرد که اتصالات ایجاد شده با sqlite3.connect(..., autocommit=True) یا autocommit=False پشتیبانی نمیشدند زیرا رفتار commit() و rollback() در آنها متفاوت بود. این نقص باعث شکست تقریباً تمام مجموعه تستهای کتابخانه میشد و نیاز به اصلاحات خاصی داشت تا کتابخانه برای کاربرانی که از تنظیمات جدید پایتون استفاده میکنند، متوقف نشود.
اصلاحات فنی دقیق
بهبودهای سیستمی دیگری نیز در نسخه ۴.۰ ادغام شدند:
اعتبارسنجی API و خطاها
- جایگزینی
AssertionErrorباValueErrorبرای خطاهای اعتبارسنجی. این کار مانع از آن میشود که خطاهایی (مانندcreate_table()بدون ستون،transform()روی جدولی که وجود ندارد، یا ارسال همزمانignore=Trueوreplace=True) در هنگام اجرای پایتون با پرچم بهینهسازی-Oبهصورت بیصدا نادیده گرفته شوند. - متدهای
table.upsert()وtable.upsert_all()اکنون اگر رکورد فاقد مقدار کلید اصلی باشد یا مقدار آنNoneباشد، خطایPrimaryKeyRequiredمیدهند. پیش از این، این رکوردها یا بهطور بیصدا بهعنوان سطور کاملاً جدید درج میشدند یا پس از درج، یک خطای گیجکنندهKeyErrorایجاد میکردند.
ابزارهای خط فرمان و دیتابیس
- متدهای
db.enable_wal()وdb.disable_wal()اکنون اگر در حین یک تراکنش باز فراخوانی شوند، خطایsqlite_utils.db.TransactionErrorمیدهند. پیش از این، آنها بیصدا تراکنش باز را کامیت میکردند تا حالت ژورنال را تغییر دهند، که این امر تضمین rollback درdb.atomic()را میشکست. - در کلاس
Viewمتدenable_fts()حذف شد. این متد پیش از این خطایNotImplementedErrorمیداد (چون FTS برای Viewها پشتیبانی نمیشود)؛ اکنون به درستی خطایAttributeErrorمیدهد و دیگر در مرجع API ظاهر نمیشود. - دستورات
drop-tableوdrop-viewاکنون صراحتاً از حذف اشیاء با نوع نادرست (حتی اگر نامها یکسان باشند) خودداری میکنند و با خطایی خارج شده و دستور صحیح را پیشنهاد میدهند. - پرچم
-d/--detect-typesاز دستورات insert/upsert حذف شد. از نسخه 4.0a1 تشخیص نوع برای CSV/TSV پیشفرض شده بود و این پرچم عملاً بیاثر (no-op) بود. با این حال،--no-detect-typesبرای غیرفعال کردن این قابلیت باقی مانده است.
مهاجرتها و کوئریها
- مهاجرتها (Migrations) اکنون همراه با رکورد ثبت کاربردشان، درون یک تراکنش اجرا میشوند. هرگونه استثنا باعث rollback میشود تا مهاجرتها بهسادگی قابل بازاجرا باشند. کاربران میتوانند با استفاده از
@migrations(transactional=False)برای دستوراتی که نمیتوانند در تراکنش اجرا شوند (مانندVACUUM)، این حالت را غیرفعال کنند. - دستور
db.table(name).insert({})اکنون از درج سطرهایی با مقادیر کاملاً پیشفرض از طریقINSERT INTO ... DEFAULT VALUESپشتیبانی میکند (مورد #759). - متدهای
upsertاکنون بهطور خودکار کلیدهای اصلی (یا کلیدهای ترکیبی) جدول موجود را شناسایی میکنند و آرگومانpk=را اختیاری میکنند. - در دستور
migrateپرچم--stop-beforeاکنون مقادیر ناشناخته مهاجرت را به عنوان خطا تلقی میکند. همچنین این پرچم با فایلهای قدیمیتر کلاسsqlite_migrate.Migrationsسازگار شده و--listاکنون یک عملیات read-only است که منجر به ایجاد فایل دیتابیس یا جدول ردیابی نمیشود. - متد
migrations.applied()اکنون مهاجرتها را به ترتیب اعمال شده برمیگرداند. - متدهای جدید
db.begin()،db.commit()وdb.rollback()برای کنترل دستی تراکنشها اضافه شدند.
اقتصاد بازبینی عاملمحور
ویلسون برای تسهیل این حجم از کار، اشتراک خود را به Claude Max (۲۰۰ دلار در ماه) ارتقا داد تا سهمیه Fable خود را پیش از «Fablepocalypse» در ۷ جولای (که پس از آن مشترکین باید هزینه کامل API را پرداخت کنند) افزایش دهد. او با استفاده از ابزاری به نام AgentsView (uvx agentsview) محاسبه کرد که هزینه خالص API این جلسه ۱۴۹.۲۵ دلار بوده است.
تفکیک هزینهها نشان میدهد بخش اعظم بار روی دوش عامل اصلی بوده است:
- جلسه اصلی (claude-fable-5): ۱۴۱.۰۲ دلار
- عامل بررسی سطح API: ۲.۴۰ دلار
- عامل بازبینی تراکنشها/atomic: ۲.۳۹ دلار
- عامل بازبینی کامیتهای پس از rc1: ۱.۷۲ دلار
- عامل بازبینی مهاجرتها: ۱.۴۰ دلار
- عامل شمارش پرامپتها (claude-opus-4-8): ۰.۳۲ دلار
این هزینه نشاندهنده قیمت بالای «عاملهای استدلالی» (reasoning-heavy) است که قادرند حالتهای پیچیده را در دهها فایل مختلف مدیریت کنند. ویلسون اشاره کرد که برای بهینهسازی هزینهها، باید از عاملهای ارزانتر برای کارهای سادهتر استفاده میکرد. ویلسون در حالی که در رژه ۴ جولای در Half Moon Bay حضور داشت، عامل هوش مصنوعی در بازههای ۱۰ تا ۱۵ دقیقهای روی تکالیف کار میکرد و او تنها از طریق آیفون خود گامهای بعدی را پرامپت میکرد.
برای خواننده، این بدان معناست که هزینه یک «حسابرسی» AI اکنون قابل محاسبه است. پرداخت حدود ۱۵۰ دلار برای جلوگیری از یک باگ حذف داده در کتابخانهای پرکاربرد، در مقایسه با بدهی فنی و ضربه به اعتبار ناشی از یک نسخه شکستخورده، معاملهای بسیار بهصرفه و ناچیز است.
این تغییر نشان میدهد که آینده پایداری نرمافزار تنها در تستهای بهتر نیست، بلکه در یک فاز «عامل خصمانه» (adversarial agent) است؛ جایی که یک مدل سعی میکند هر آنچه مدل دیگر ساخته را بشکند. نقش توسعهدهنده ارشد از «نویسنده کد» به «بازبین پیشنهادهای عامل» تغییر میکند. تاریخچه کامیتهای لیست تغییرات که توسط Fable با هر تغییر بهروز شده بود، اکنون به عنوان خلاصهای دقیق از این نسخه عمل میکند. ویلسون اشاره کرد که این یادداشتها — که باید خستهکننده، پیشبینیپذیر و دقیق باشند — در واقع بهتر از یادداشتهای انتشاری بودند که خودش بهصورت دستی مینوشت.




گفتگو