وہ اسکور جو ثابت ہو سکے
پچھلی تحریر ایجنٹ برج کے بارے میں تھی اور اس کا اختتام ایک ایسے جملے پر ہوا تھا جو اب ایک فہرستِ کار بن چکا ہے: گیم پہلے ہی ڈیٹرمنسٹک تھی، برج نے صرف اسے ضائع کرنا بند کیا۔
تیرہ دن بعد اس جملے پر ایک ستارہ لگانا پڑے گا۔ گیم اُس معنی میں ڈیٹرمنسٹک تھی جو اسے کھیلنے کے لیے کافی ہے، اور اُس معنی میں نہیں جو کسی اسکور کو ثابت کرنے کے لیے درکار ہے۔ 2 اگست سے 15 اگست کے درمیان اس سوٹ نے اپنی تقریباً ساری پیداوار اسی خلا کو پُر کرنے پر لگائی: وہ مقامات ڈھونڈنا جہاں سِمولیشن خاموشی سے غلط گھڑی پڑھ رہی تھی، ایک ایسی سروس بنانا جو جمع کرانے والے پر بھروسہ کیے بغیر ایک رن کی تصدیق کر سکے، اور ایک ہفتہ وار لیڈربورڈ جاری کرنا جسے صرف اور صرف وہی سروس کھلاتی ہے۔
یہ چار کہانیاں ہیں، اس لیے یہ ایک لمبی تحریر کے بجائے چار مختصر تحریریں ہیں۔
فہرست
- اختلاف کا تعاقب — ہر ری پلے فورک دراصل ایک ہی بگ تھا، بس لباس بدل کر۔
- ایک لیڈربورڈ جس سے جھوٹ نہیں بولا جا سکتا — تصدیق کرنے والا خود سِم چلانے سے کیوں انکار کرتا ہے۔
- ایک اسکور، چار ریپازٹریز — ایک رن کا سفر، براؤزر سے عوامی صفحے تک۔
- تیرہ دن — رسیدیں، اور وہ ایک عدد جو جان بوجھ کر پیچھے گیا۔
1. اختلاف کا تعاقب
ایک ڈیٹرمنسٹک سِمولیشن ایک وعدہ کرتی ہے: وہی seed، وہی ان پٹ، وہی رن۔ CandyRush ایک رن کو seed اور ان پٹ اسٹریمز کے طور پر ریکارڈ کرتی ہے، اور تصدیق کا مطلب ہے اس ریکارڈنگ کو دوبارہ سِم کرنا اور سِم حالت کے چلتے ہوئے digest کا اُس digest سے موازنہ کرنا جو اصل رن نے ثبت کیا تھا۔ اگر وعدہ قائم ہے تو دونوں اسٹریم بالکل ایک جیسی ہوں گی۔
وہ نہیں تھیں۔ ایک کیپچر 5,176 ٹِک چلا اور اُن میں سے 3,651 ٹِک مختلف نکلے — کسی دوسری مشین کے مقابلے میں نہیں، خود اپنے ہی مقابلے میں۔
ایک ہفتے کے تعاقب کے بعد سب سے قابلِ ذکر بات یہ ہے کہ ان میں سے کوئی بھی random number کا بگ نہیں تھا۔ ہر ایک گھڑی کا بگ تھا: کوئی نہ کوئی حالت frame گھڑی پر بدل رہی تھی، ایک ایسی سِمولیشن کے اندر جو ٹِک کی گھڑی پر آگے بڑھتی ہے۔ پھر یہ کہ تبدیلی کس ٹِک پر اتری، اس کا انحصار اس بات پر تھا کہ مشین اُس وقت کتنی تیز چل رہی تھی — اور ایک ہی ریکارڈنگ کے دو رن بغیر کسی RNG کے فورک ہو جاتے تھے۔
اس نے جو لباس پہنے:
- ایک لاش جو جانے کا نام نہ لیتی تھی۔ دشمن سِم رجسٹری سے اُس وقت نکلتا تھا جب
اس کا dissolve fade مکمل ہوتا، اور وہ fade wall-clock پر چلتا ہے۔ ناپا گیا:
دشمن
id=101ایک رن میں ٹِک 1331 پر رجسٹری سے نکلا اور دوسرے رن میں کبھی نہیں۔ اب موت اپنی ہی ٹِک پر رجسٹریشن ختم کرتی ہے اور fade خالص آرائشی ہے۔ صرف اس ایک جوڑی تبدیلی نے کیپچر کو 3,651 مختلف ٹِکس سے 2 پر، اور بعد کے ایک کیپچر میں 0 پر پہنچا دیا۔ - ایک کیمرہ جو ایک فریم پیچھے تھا۔ دشمنوں کے spawn کیمرے کے transform کے نسبت
رکھے جاتے تھے، جسے
LateUpdateہر فریم میں ایک بار لکھتا ہے۔ ایک ری پلے پمپ کے نیچے جو فی فریم سینکڑوں ٹِک آگے بڑھتا ہے، وہ transform کھلاڑی سے اتنے ٹِک پیچھے رہ جاتا ہے جتنے اُس پلیٹ فارم نے اکٹھے کیے۔ شہادت غیر معمولی حد تک صاف ہے: ٹِک 724 پر 25 دشمن بٹ-برابر تھے، جبکہ تازہ ترین 10 spawns ایک ہی مستقل انتقال سے ہٹے ہوئے تھے — (+2.25, +2.69)، مقدار 3.498 ورلڈ یونٹ — اور RNG draw کی گنتیاں ہر scope میں یکساں تھیں۔ وہی draws، ایک ہی سخت offset: یعنی spawn کا مرجع نقطہ مختلف تھا۔ پھر وہ ہٹے ہوئے دشمن آف اسکرین ٹیلی پورٹ کی حد مختلف لمحوں پر عبور کر گئے، جس سے ٹِک 773 پر ایک اضافی draw لگا اور digest اصل اختلاف کے 50 ٹِک بعد فورک ہوا۔ - ایک تین سطری float راؤنڈ ٹرپ۔ کھلاڑی کا رخ fixed point میں بالکل درست نکالا
جاتا تھا، پھر
float32میں تبدیل کر کے واپس لایا جاتا اور ایک fixed-point حد سے موازنہ کیا جاتا۔ اُس شرط کی باقی ہر اصطلاح درست تھی۔ جب موازنہ پلٹتا ہے تو ایک دشمن ٹیلی پورٹ کرتا ہے یا نہیں کرتا — یعنی ٹھیک ایک RNG draw کا اضافہ یا کمی، بغیر کھلاڑی کی جگہ بدلے اور بغیر دشمنوں کی تعداد بدلے۔ digest فورک ہو جاتا ہے جبکہ digest جن اجزا کی اطلاع دیتا ہے وہ سب اب بھی ملتے ہیں۔ یہی وجہ ہے کہ یہ فورک کئی دن تک کسی چیز سے منسوب نہ ہو سکا، اور یہی وجہ ہے کہ اس سے پہلے کی دو نسبتیں غلط تھیں۔
آخری بات دراصل بگ کی نہیں، آلے کی کہانی ہے۔ digest دشمنوں کی تعداد اٹھاتا تھا، اُن کی حالت نہیں — یعنی دشمنوں کی پوزیشنیں کسی ری پلے یا لیڈربورڈ چیک نے کبھی تصدیق ہی نہیں کی تھیں۔ اسی عرصے میں digest میں پہلے فی scope مجموعی RNG draw گنتیاں شامل ہوئیں، پھر پوزیشن، HP اور type کی فی اینٹٹی ہیشنگ؛ اور نمونہ گیری 60 ٹِک کی کھڑکی سے بدل کر فی ٹِک ہو گئی، تاکہ اختلاف کی رپورٹ ایک محلے کے بجائے ایک عین ٹِک کا نام لے۔ فی اینٹٹی ہیشنگ آنے پر تین میں سے دو کمٹ شدہ فکسچر بالکل نہ ہلے — اُن میں فولڈ کرنے کو کوئی اینٹٹی ہی نہیں، اور یہ خود اس بات کی کھری پیمائش ہے کہ وہ فکسچر کتنا کم کچھ آزما رہے تھے۔
اس مجموعے کا بدترین بگ سرے سے کوئی اختلاف تھا ہی نہیں۔ level-up کے آفرز چلتی ہوئی RNG اسٹریم سے نکالے جاتے تھے، اور ریکارڈنگ صرف اُس کارڈ کا انڈیکس محفوظ کرتی ہے جو کھلاڑی نے چنا۔ چنانچہ اوپر کہیں بھی کوئی اختلاف ہوتا تو پیش کیے گئے تین کارڈ بدل جاتے، اور ری پلے کوئی اور ability لگا کر کوئی اور build کھیل جاتا: ایک کیپچر کے ساتوں level-ups اُن abilities پر جا اترے جو کھلاڑی نے کبھی لی ہی نہیں تھیں۔ اب آفرز اسٹریم کی پوزیشن کے بجائے ایک مستحکم ordinal پر بنتے ہیں، ریکارڈنگ منتخب ability کی id ثبت کرتی ہے، اور playback اس کا موازنہ کرتا ہے۔ سات mismatch صفر ہو گئے، جبکہ رن کہیں اور اب بھی مختلف ہو رہا تھا — اور یہی صحیح شکل ہے: ایک آلہ، ایک خرابی، کوئی خلط ملط نہیں۔
اور جو مجھے سب سے زیادہ پسند ہے، وہ آج کچھ بھی نہیں ہلاتا۔ ہر chunk اپنا پس منظر ایک pool سے چنتا تھا، اور اس چناؤ کا RNG موجودہ ٹِک سے بنتا تھا، جبکہ اسے پکارنے والے streaming طریقے فریم کی رفتار پر چلتے ہیں۔ ہر شپنگ فیلڈ میں پس منظر کا صرف ایک prefab ہے، اس لیے یہ draw seed سے آزاد ہے اور کوئی digest نہیں ہلتا۔ جس دن کوئی فیلڈ دوسرا پس منظر بھیجے گی، یہ ایک زندہ فورک بن جائے گا — ایک ہی جمع کرائے گئے رن کی دو تصدیقیں ایک دوسرے سے اختلاف کرتی ہوئی، اور وہ بھی ایک ایسے کمٹ میں جس نے صرف آرٹ شامل کیا تھا۔
2. ایک لیڈربورڈ جس سے جھوٹ نہیں بولا جا سکتا
2 اگست کو SimVerify کا وجود ہی نہیں تھا۔ یہ 8 اگست کو کھڑا کیا گیا اور اب چار .NET پروجیکٹس ہیں، ٹیسٹ ملا کر تقریباً 21,000 سطریں، ایک سرنگ کے پیچھے سروس کے طور پر تعینات، صحت کے اینڈ پوائنٹ اور بلڈ mutex کے ساتھ۔
جس فیصلے نے باقی سب کچھ طے کیا، وہ ایک انکار ہے: SimVerify سِم نہیں چلاتی۔ ایک headless C# ری سِمولیشن کو تصدیق کا مرجع بنانے پر غور ہوا اور اسے صریحاً رد کر دیا گیا، کیونکہ وہ ایک تیسرا artifact ہوتا جس کا اپنا floating-point رویہ ہوتا — ڈیسک ٹاپ Mono/IL2CPP بمقابلہ براؤزر WASM — اور اس سے اتفاق اِس بارے میں کچھ ثابت نہیں کرتا کہ کھلاڑی کے براؤزر نے دراصل کیا کیا تھا۔ مرجع وہی شپ شدہ پروڈکشن بلڈ ہے۔ سروس ایک headless براؤزر کو اسی artifact پر چلاتی ہے، نتیجہ جمع کرتی ہے، اور digests کا موازنہ کرتی ہے۔ یہ انتظام کرتی ہے؛ سچ خود اخذ کبھی نہیں کرتی۔
یہی جبلت comparator پر بھی حکومت کرتی ہے۔ سروس اور گیم کو بالکل ایک ہی موازنہ کوڈ چلانا چاہیے، اس لیے comparator کا سورس UTools میں رہتا ہے اور یہاں ایک git submodule کے طور پر براہِ راست سروس میں کمپائل ہوتا ہے۔ NuGet کو کھلم کھلا رد کیا گیا: گیم UTools کے pin سے head پر بنتی ہے، جبکہ سرور کسی شائع شدہ ورژن سے بنتا، اور یہی وقفہ وہ خرابی ہے جس کے خلاف پورا ڈیزائن بنا ہے۔ ریپو کے آر پار linked source تو کچھ بھی pin نہیں کرتا۔
اس کے اوپر ایک cross-artifact گیٹ ہے: ایک ریکارڈنگ، دو artifacts، ایک ہی commit SHA۔ یہ پہلے WASM بلڈ کو دو بار چلاتا ہے، اور جو بلڈ خود اپنے ہی ساتھ اختلاف کرے، وہ رن وہیں مختصر ہو جاتا ہے اور native ٹانگ کبھی چلتی ہی نہیں — کیونکہ ایسا فیصلہ کسی چیز سے منسوب نہیں ہو سکتا، اور ایک ناقابلِ نسبت سرخ گیٹ نہ ہونے سے بدتر ہے۔
پھر وہ حصہ آیا جو کالر کو دشمن فرض کرتا ہے، اور اس نے تقاضے بڑھائے نہیں بلکہ بدل
دیے۔ جمع کرانے کا endpoint پہلے ایک matchesStamped فیلڈ کی بنیاد پر رن قبول کرتا
تھا — یعنی ایک ایسی قدر جو زیرِ آزمائش artifact خود نکالتا ہے۔ سروس کا اپنا کوڈ
پہلے ہی اسے مشورتی قرار دے چکا تھا۔ جو سرور اس پر بھروسہ کرے، وہ کچھ بھی تصدیق نہیں
کرتا۔ اب قبولیت سرور کی طرف سے مشترکہ comparator چلا کر طے ہوتی ہے، اور بورڈ پر وہ
اسکور درج ہوتا ہے جو دوبارہ تعمیر نے اخذ کیا، وہ ہرگز نہیں جو فائل نے دعویٰ کیا؛
دورانیے کا بھی یہی حال ہے۔ جس pass سے کوئی نتیجہ اخذ نہ ہو، وہ صفر بورڈ کرنے کے
بجائے کچھ بھی بورڈ نہیں کرتا۔
شناخت کے راستے پر بھی ایسی ہی ایک فہرست نکلی۔ Steam session tickets کی تصدیق Valve سے ہوتی ہے، اور تصدیق شدہ SteamID ہی کسی submission کی واحد شناخت ہے۔ وہاں چار باتیں دہرانے کے قابل ہیں، کیونکہ ہر ایک اُس وقت تک غیر مرئی ہے جب تک کوئی دیکھنے نہ جائے:
- ticket کال میں وہ
identityپیرامیٹر تھا ہی نہیں جسے Valve لازمی قرار دیتا ہے، اور وہ غلط host کی طرف اشارہ کر رہی تھی — جیسا کوڈ شپ ہوا تھا، کوئی حقیقی ticket کبھی تصدیق پا ہی نہیں سکتا تھا۔ - publisher کی Web API key query string میں سفر کرتی ہے، اور ڈیفالٹ HTTP کلائنٹ لاگر درخواست کے URI کو Information سطح پر چھاپتا ہے — یعنی ہر submission اس key کو سروس لاگ میں لکھ دیتی۔ لاگرز ہٹا دیے گئے؛ اب ایک ٹیسٹ یقینی بناتا ہے کہ یہ key کسی بھی سطح کی کسی بھی لاگ لائن میں نہ آئے، اور فکس کے بغیر وہ ٹیسٹ ناکام ہوتا ہے۔
AuthenticateUserTicketidempotent ہے، سو ایک پکڑا ہوا ticket بار بار بورڈ کر سکتا تھا۔ اب ticket ایک بار کا ہے، اور اسے credential محفوظ کیے بغیر SHA-256 سے ٹریک کیا جاتا ہے۔- فیصلے کا switch نامعلوم verdict پر کھلا ناکام ہوتا تھا۔
اب endpoint کے سامنے چار دروازے ہیں، اُسی ترتیب میں جس ترتیب سے ایک دشمن درخواست اُن تک پہنچتی ہے: routing سے پہلے لگی اپ لوڈ بائٹ کی حد (model binding کے بعد لگائی گئی حد دراصل ایک ایسی body پر حد ہے جو پوری پڑھی جا چکی)، غیر معتبر ریکارڈنگ کی schema تصدیق، وہ admission backpressure جو قطار میں لگانے کے بجائے انکار کرتی ہے، اور تصدیق شدہ SteamID پر sliding-window rate limit — 429 اور صرف 429، کبھی auth ناکامی یا تصدیقی ناکامی کے ساتھ خلط ملط نہیں۔
plausibility اسکورنگ اپنی الگ سطر کی مستحق ہے، کیونکہ یہی وہ واحد جگہ ہے جہاں سروس جان بوجھ کر نفاذ سے انکار کرتی ہے۔ shadow metrics حساب بھی ہوتے ہیں اور append-only corpus کے طور پر محفوظ بھی، جبکہ سخت ترین موڈ بھی مشتبہ رن کو رد کرنے کے بجائے Assisted ڈویژن کی طرف بھیجتا ہے۔ ایک غیر کیلیبریٹڈ حد جو کسی رن کو پھینک سکتی ہو، سب سے پہلے بہترین کھلاڑیوں کو ہی غلط پکڑے گی۔
3. ایک اسکور، چار ریپازٹریز
ایک اسکور اب پورا سفر یوں طے کرتا ہے۔
کھلاڑی ایک رن کھیلتا ہے۔ CandyRush seed اور ان پٹ، level-up اور cheat کی اسٹریمز
ریکارڈ کرتی ہے، اور ہر N ٹِک پر ایک digest نمونہ ثبت کرتی ہے۔ UTools fixed-point
ریاضی، digest hash کا فارمیٹ، اور وہ comparator فراہم کرتی ہے جس سے ان digests کا
موازنہ ہوتا ہے۔ جمع کرانے پر SimVerify Steam ticket کی تصدیق کرتی ہے، ایک
headless براؤزر کو شپ شدہ artifact پر چلاتی ہے، digest اسٹریم اور اسکور دوبارہ اخذ
کرتی ہے، اور اخذ شدہ اسکور ہفتہ وار بورڈ پر لکھ دیتی ہے۔ وہ بورڈ static JSON کے طور
پر شائع ہوتا ہے۔ PublicWebsite اس JSON کو کلائنٹ کی طرف سے لاتی ہے اور
/candyrush/leaderboards پر دکھاتی ہے — یعنی اسکور ہر وزٹ پر تازہ ہوتے ہیں، سائٹ کو
دوبارہ بنانے کی ضرورت نہیں، اور عوامی سائٹ تصدیقی سروس سے کبھی بات ہی نہیں کرتی۔
چار ریپازٹریز، چار اندرونی طور پر درست ٹکڑے — اور، جیسا کہ متوقع تھا، ہر دلچسپ بگ اُن کے درمیانی جوڑ میں رہتا ہے۔
سب سے تیز دھار بگ ایک ایسا فیصلہ ہے جو اپنی ہی schema سے بڑا ہو گیا۔ ایک بورڈ کی
شناخت (ہفتہ، commit SHA) ہے اور یہ مٹنے کے بجائے آرکائیو ہوتا ہے، لہٰذا اگر گیم
ہفتے کے بیچ نیا بلڈ بھیجے تو ایک ہفتہ ایک سے زیادہ بورڈ کا مالک ہو سکتا ہے۔ شائع شدہ
schema ہفتے کے حساب سے previousWeeks: string[] تھی، جو اس بات کا اظہار ہی نہیں کر
سکتی: سائٹ کا آرکائیو picker دو بورڈز کو ایک قطار میں سکیڑ دیتا اور دوسرا بورڈ ناقابلِ
رسائی رہ جاتا۔ حل ایک خالص اضافی schema v2 ہے — نیا board id، commit SHA اور بورڈز کی
مکمل فہرست، جبکہ پرانا فیلڈ اپنے پرانے معنی برقرار رکھتا ہے تاکہ v1 رینڈرر چلتا رہے —
اور سائٹ نئی فہرست کو ترجیح دیتی ہے جب اس سے کچھ ملے، ورنہ پرانی پر لوٹ آتی ہے۔
باقی سب جوڑ والے بگ ایک ہی خاندان کے ہیں: ایک ایسا مفروضہ جو حد کی ایک طرف سچ اور دوسری طرف جھوٹ تھا۔
- آرکائیو picker اُسی بورڈ سے اخذ ہوتا تھا جو اِس وقت لوڈ ہے۔ آرکائیو فائلیں ناقابلِ تغیر snapshots ہیں، سو اُن کی فہرست اشاعت کے لمحے منجمد ہو جاتی ہے اور بعد میں شائع ہونے والی کسی چیز کا نام نہیں لے سکتی۔ نتیجتاً آرکائیو کھولنے پر picker سکڑ جاتا اور وزیٹر پھنس جاتا۔ اب یہ فہرست ایک ایسا اتحاد ہے جو صرف بڑھتا ہے۔
- ہر آرکائیو شدہ بورڈ پر "شاید پرانا ہو" کا نشان لگا ہوتا تھا، کیونکہ آرکائیو بہ تعریف 24 گھنٹے کی حد سے پرانا ہوتا ہے۔ اب پرانا پن صرف زندہ بورڈ کی خاصیت ہے۔
- شائع کرنے والا بورڈ فائلوں کو 8 حروف کے SHA سے نام دیتا ہے۔ سائٹ سات دکھاتی تھی۔ ایک ہی SHA کا مختلف اختصار دکھانا کچھ نہ دکھانے سے بدتر ہے: ایک بلڈ کی دو تحریریں دو بلڈ پڑھی جاتی ہیں، اور یہی وہ الجھن ہے جسے دور کرنے کے لیے بورڈ کی شناخت commit SHA سے جوڑی گئی تھی۔
- Steam IDs پورے web لیئر میں strings ہی رہتے ہیں۔ 64-bit ids اُس حد سے بڑے ہیں جسے JavaScript کے اعداد بالکل درست رکھ سکیں، اور جو لیڈربورڈ خاموشی سے شناختیں گول کر دے، وہ کھلاڑیوں کو آپس میں ملا دینے والا لیڈربورڈ ہے۔
board ids نیٹ ورک سے آتے ہیں اور ایک fetch URL میں شامل ہوتے ہیں، اس لیے کسی بھی درخواست سے پہلے ان کی جانچ ہوتی ہے — traversal، protocol-relative اور مطلق URL والے ids سب مسترد۔ جوڑ یہی دوسری بات سکھاتے ہیں: کوئی قدر جیسے ہی ریپازٹری کی حد عبور کرتی ہے، وہ غیر معتبر ان پٹ بن جاتی ہے — چاہے دونوں طرف آپ نے خود لکھی ہو۔
4. تیرہ دن
اوپر کی ہر بات، رسیدوں کی صورت میں۔ گنتیاں 2 اگست سے 15 اگست کے درمیان ہر trunk پر اترنے والے merge کمٹس کی ہیں۔
| ریپازٹری | مرج شدہ PRs | کمٹس | کیا کر رہی تھی |
|---|---|---|---|
| CandyRush | 67 | 221 | ڈیٹرمنزم فکسز، digest آلات، ہفتہ وار چیلنج بوٹ، submission کلائنٹ |
| UTools | 23 | 75 | fixed-point ریاضی، digest hash v4→v6، مشترکہ comparator، RNG draw کاؤنٹرز |
| SimVerify | 17 | 44 | پوری سروس — ریپازٹری 8 اگست کو بنی |
| PublicWebsite | 2 | 6 | لیڈربورڈ صفحہ اور اس کا آرکائیو |
ان اعداد پر دو ایماندار باتیں۔ یہاں ایک pull request نظرثانی کی اکائی ہے، محنت کی
نہیں — ان میں سے کئی پہلے سے مرج شدہ PR پر آڈٹ ڈیلٹا ہیں، اور یہ نمونہ اتنی بار
دہرایا گیا ہے کہ اسے اتفاق کے بجائے ایک طریقۂ کار کہنا چاہیے۔ اور اس عرصے کے
CandyRush کے 139 غیر merge کمٹس میں سے 118 پر Co-Authored-By: Claude کا نشان ہے۔
ان تیرہ دنوں کی نمائندہ پیداوار کوئی فیچر نہیں، بلکہ آلات ہیں: ایک digest جو دشمن گننے سے بڑھ کر دشمنوں کو hash کرنے لگا، اُسی hash میں فولڈ ہوتی فی scope RNG draw گنتیاں، فی ٹِک کیپچر کی کھڑکیاں، کسی متعین ٹِک پر بغیر حد کے state dumps، ایڈیٹر کے اندر ایک تصدیقی harness، ایک cross-artifact گیٹ، اور ایک ایسی سروس جس کا سارا کام جمع کرائی گئی فائل پر یقین نہ کرنا ہے۔
اور یہیں وہ ایک عدد آتا ہے جو جان بوجھ کر پیچھے گیا۔ ہر ریکارڈنگ پر ثبت ہونے والا سِمولیشن ورژن تقریباً ایک ہفتے میں 3 → 4 → 5 → 6 تک چڑھ گیا، صرف اس لیے کہ ایک ڈیزائن دستاویز کا ایک استثنائی جملہ اس اجازت کے طور پر پڑھا گیا کہ digest کے معنی بدلتے ہی ورژن بڑھا دو۔ 10 اگست کو اسے 1 پر ری سیٹ کر کے منجمد کر دیا گیا، اور وہ جملہ حذف ہوا جس نے چار بار ورژن بڑھوایا تھا۔ سِمولیشن میں کوئی پسپائی نہیں ہوئی؛ ورژن نے بس ڈائری بننا چھوڑ دیا اور دوبارہ ایک compatibility گیٹ بن گیا جو لانچ تک نہیں ہلے گا۔
چاروں حصوں میں ایک ہی نمونہ ہے، اور اس کا تعلق دراصل گیمز سے نہیں۔ اس عرصے کی ہر ناکامی ایک حد کی ناکامی تھی — frame گھڑی اور ٹِک گھڑی کے درمیان، ایک بلڈ اور اس کے اپنے دوبارہ چلنے کے درمیان، ایک ہی commit کے دو artifacts کے درمیان، شائع کرنے والے کی schema اور پڑھنے والے کے مفروضے کے درمیان، کلائنٹ کے دعوے اور سرور کے فیصلے کے درمیان۔ ہر فریق اپنی جگہ درست تھا۔ غلط جوڑ تھا۔
اسکور اب ایسی چیز ہے جسے یہ سوٹ ثابت کر سکتی ہے، نہ کہ محض وہ جو کھلاڑی بتا دے۔ یہاں تک پہنچنے کا زیادہ تر مطلب یہ تھا کہ وہ مقامات ڈھونڈے جائیں جو خاموشی سے غلط اکائیوں میں ناپ رہے تھے — اور ان میں سے کوئی بھی اندر سے دیکھنے پر غلط نہیں لگتا تھا۔