رفتن به محتوا

گفتگوهای pull request

pull request از پیش همان‌جایی است که گفتگو دربارهٔ کد در آن رخ می‌دهد. گفتگوهای PR آن را به سطحی روی یک فضای کاری Control Center تبدیل می‌کنند، همان‌طور که یک پل گفتگو اسلک را به چنین سطحی تبدیل می‌کند: ربات GitHub App سرور را روی یک PR با @ منشن کنید، داخل رشته‌های بازبینی آن پاسخ دهید، یا برچسب ai-review را اضافه کنید، و کار در فضای بازبینی همان PR انجام می‌شود — با همان انتساب و گاردریل‌هایی که برای هر چیزی که در برنامه تایپ می‌شود اعمال می‌شود. یک نظر همچنین مشروط به عضویت است؛ برچسب چنین نیست، چون نویسنده‌ای ندارد. پاسخ‌های ربات روی GitHub، در همان خطی که پرسش در آن مطرح شده بود، بازمی‌گردند.

سه تصمیم این قابلیت را شکل می‌دهند و هر یک همان تصمیمی است که پل گفتگو برای گفتگو گرفته بود، این بار برای یک forge بازگو شده است: اتصال در کدام جهت برقرار می‌شود، چه چیزی به نظری که از بیرون می‌رسد مجوز می‌دهد و هر طرف به چه کسی نسبت داده می‌شود.

تنها کانال push گیت‌هاب برای یک برنامه، webhook است و webhook به یک URL ورودی نیاز دارد — همان چیزی که این سطح به‌عمد هرگز نمی‌خواهد. پس کشف کار یک جاروب است: هر یک دقیقه یا حدود آن، یک جست‌وجوی مستعار به‌ازای هر نصب برنامه از گیت‌هاب PR‌هایی را می‌خواهد که ربات (یا slug کوتاه آن) را منشن کرده‌اند و PR‌های بازی را که برچسب بازبینی دارند؛ PR‌هایی که از پیش فضای بازبینی دارند، رشته‌های نظرشان خوانده می‌شود و تفاوت همه‌چیز با آنچه در جاروب پیشین دیده شده بود، بر پایهٔ شناسهٔ نظر محاسبه می‌شود.

پیامدهای طراحی ارزش نام‌بردن دارند:

  • تأخیر یک بازهٔ جاروب است، نه یک رویداد. تأیید 👀 ظرف حدود یک دقیقه می‌نشیند. هیچ کار فوری‌ای بر آن سوار نیست.
  • دقیقاً یک‌بار، به‌ازای هر نظر. شناسه‌های نظر در سراسر گیت‌هاب یکتا هستند؛ هر شناسه همان لحظه‌ای که واجد شرایط تشخیص داده شود ثبت می‌شود، پیش از آنکه چیزی روی آن عمل کند. یک ازکارافتادگی می‌تواند پردازش یک نظر را از دست بدهد، اما هرگز آن را دوبار اجرا نمی‌کند.
  • زمان آفلاین جبران می‌شود، نه بازپخش. مجموعهٔ دیده‌شده ماندگار است، پس یک راه‌اندازی مجدد آنچه را در زمان خاموشی رسیده تحویل می‌دهد. تنها استثنا نخستین جاروب تاریخ است: بدون حافظه‌ای برای مقایسه، همه‌چیزِ معوق بی‌صدا ثبت می‌شود — اپراتوری که بیست PR را پیش از وجود این سرور برچسب زده است نباید با بیست بازبینی در حال اجرا روبه‌رو شود.
  • برچسب یک مجموعه است، نه یک رویداد. جست‌وجو وجود برچسب را گزارش می‌کند، پس یک PR برچسب‌خورده یک‌بار و برای همیشه تریگر می‌شود. پس از برداشتن یک برچسب، افزودن دوبارهٔ آن برای جاروب نامرئی است؛ مسیر اجرای دوباره یک منشن است.

جست‌وجو یک‌بار به‌ازای هر حساب نصب، روی کلاینت خودِ همان مالک اجرا می‌شود، چون یک توکن نصب فقط مخزن‌های نصب خودش را می‌بیند — یک اعتبارنامهٔ یکتای سراسری بی‌صدا هر PR زیر نصب‌های دیگر برنامه را از دست می‌داد.

یک نظر PR چیزی بیش از نام کاربری نویسنده با خود نمی‌آورد — هویتی بیرونی و احرازنشده تا زمانی که اثبات شود. ربات آن را در برابر یک نمایهٔ معکوس از اعضای فضای کاری و حساب‌های GitHub متصل‌شان حل می‌کند؛ نمایه‌ای که با TTL بازسازی می‌شود، چون افراد هنگام اجرای سرور وارد می‌شوند و دعوت می‌شوند.

شکستِ امن شبیه یک نردبان است و هر پله در همان رشته‌ای پاسخ می‌دهد که در آن رد کرده است:

  • نام کاربری ناشناس — متصل‌نشده، یا متصل‌شده به‌دست کسی که عضو این فضای کاری نیست — پیام می‌گیرد که وارد شود و مطمئن شود که به اینجا تعلق دارد
  • عضو فقط‌خواندنی (viewer یا guest) پیام می‌گیرد که نقشش اجازهٔ شروع کار را ندارد

دو حالت نام کاربری ناشناس یک پاسخ مشترک دارند: نمایهٔ معکوس تنها عضوی از همین فضای کاری را بازمی‌گرداند، پس یک حساب متصل‌نشده و یک پیوندشدهٔ غیرعضو همان عدم تطابق‌اند. هیچ‌یک پیش نمی‌روند. یک نظر هرگز به کسی که آن را نفرستاده نسبت داده نمی‌شود و واژه‌های یک غریبه هرگز به فضای کاری‌ای که نمی‌تواند ببیند راه نمی‌یابد.

برچسب ai-review نویسنده‌ای برای حل کردن ندارد، پس از این نردبان بالا نمی‌رود. افزودن آن، بازبینی را در همان فضای کاری‌ای آغاز می‌کند که مخزن را از پیش پیوند داده است.

هر طرف گفتگو هویتی متفاوت دارد و همین تفاوت، خودِ قابلیت است:

  • در GitHub، ربات خودِ سرور است. هر نظر، واکنش و پاسخی که پل می‌فرستد با توکن نصب برنامه سوار می‌شود. هیچ‌کس در هیچ کلاینتی چیزی کلیک نکرده است، پس هیچ‌چیز حساب یک شخص را قرض نمی‌گیرد — همان قاعده‌ای که webhook‌ها و پولینگ را از اعتبارنامه‌های انسانی دور نگه می‌دارد.
  • در فضا، پرسش مالِ شماست. پیام به همان عضوی نسبت داده می‌شود که نویسنده به آن حل شده است و اجرای عامل به نمایندگی او انجام می‌شود: کامیت‌ها او را هم‌نویسنده می‌کنند و درجهٔ خودمختاری و گاردریل‌های کنشِ فضا دقیقاً همان‌گونه اعمال می‌شوند که برای پیامی که در برنامه تایپ می‌شود. نوشتن‌های forge از این اجرا همچنان به‌عنوان خودِ برنامه بیرون می‌روند — همان قاعدهٔ بند بالا — نه با توکن شخصی عضو.

انتشار یافته‌های بازبینی در GitHub همچنان تصمیمی انسانی است که در Control Center گرفته می‌شود. یک منشن می‌تواند یک بازبینی را آغاز کند؛ اما تنها یک انسان آن را منتشر می‌کند.

هر چیزی که یک حساب ربات نوشته باشد پیش از اجرای هر دروازه‌ای حذف می‌شود — به‌طور خاص مالِ خودمان، و هر پسوند [bot]، تا ربات بازبینی یک برنامهٔ دیگر هم نتواند ربات ما را بیدار کند. بدون این، پاسخ‌های خودِ ربات به‌شکل نظرهای تازه می‌رسیدند و آن را تا ابد دوباره تریگر می‌کردند.

پسوند [bot] همچنین دلیل وجود شکل کوتاه است. GitHub نام کاربری حساب یک برنامه را روی <slug>[bot] ثابت می‌کند، برای آن هیچ تکمیل خودکاری ارائه نمی‌دهد و هرگز @slug را به برنامه نگاشت نمی‌کند — پس پذیرفتن slug تنها، تصمیمِ تطبیق‌دهندهٔ ماست و کشف آن به یک خط جست‌وجوی خام روی متن نظر نیاز دارد، نه فهرست منشن‌های GitHub. هزینه‌اش گفته می‌شود نه صاف‌کاری: کاربر انسانی‌ای که slug برنامه را با خود سهیم است، با هر منشن کوتاهِ خودش ربات را خطاب قرار می‌دهد. نام‌های متمایزِ برنامه، این برخورد را فرضی می‌کند.

  • بدون انتشار خودکار. یافته‌ها در فضای بازبینی می‌نشینند؛ انتشار در GitHub کنشی جداگانه و انسانی است
  • بدون هویت فازی. نویسنده دقیقاً به یک عضو حل می‌شود یا رد می‌شود — هیچ انتساب حدس‌زدنی وجود ندارد
  • بدون webhook برای GitHub App. این سطح هرگز یکی ثبت نمی‌کند، پس هیچ URL ورودی‌ای برای افشا کردن به‌خاطر نظرهای PR وجود ندارد. webhook‌های دیگر محصول (پایپ‌لاین‌ها، همگام‌سازی تیکت) تنظیمی جداگانه و اختیاری‌اند.