Hoppa till innehåll

PR-samtal

En pull request är redan platsen där samtalet om kod äger rum. PR-samtal gör den till en yta i en Control Center-arbetsyta, på samma sätt som en chattbrygga gör Slack till en: @-nämn serverns GitHub App-bot i en PR, svara i dess granskningstrådar, eller lägg till etiketten ai-review, och arbetet sker i PR:ens granskningsyta — under samma tillskrivning och samma räcken som allt som skrivs i appen. En kommentar kräver dessutom medlemskap; etiketten gör det inte, eftersom den inte har någon avsändare. Botens svar kommer tillbaka på GitHub, i samma spår som frågan ställdes i.

Tre beslut formar funktionen, och vart och ett är samma beslut som chattbryggan fattade för chatt, formulerat om för en kodvärd: vilken riktning anslutningen går i, vad som auktoriserar en kommentar som kommer utifrån och vem varje sida tillskrivs.

GitHubs enda push-kanal för en app är en webhook, och en webhook kräver en inkommande URL — det enda som den här ytan medvetet aldrig behöver. Så upptäckten sker genom ett svep: ungefär varje minut ber en aliasbaserad sökning per appinstallation GitHub om PR:er som nämner boten (eller dess korta slug) och om öppna PR:er som bär granskningsetiketten, PR:er som redan har en granskningsyta får sina kommentarstrådar lästa, och allting jämförs via kommentars-id mot vad som sågs i förra svepet.

Designkonsekvenserna är värda att nämna:

  • Latensen är ett svepintervall, inte en händelse. 👀-bekräftelsen landar inom ungefär en minut. Inget brådskande hänger på den.
  • Exakt en gång, per kommentar. Kommentars-id är unika i hela GitHub; var och ett registreras i samma stund som det klassas som kvalificerat, innan något agerar på det. En krasch kan förlora hanteringen av en kommentar, men aldrig utlösa den två gånger.
  • Offlinetid kommer ikapp, inte spelas upp igen. Uppsättningen av sedda kommentarer sparas, så en omstart levererar det som kom in medan servern var nere. Det enda undantaget är det allra första svepet: utan något minne att jämföra mot registreras allt utestående tyst — en operatör som märkte tjugo PR:er innan den här servern fanns får inte komma tillbaka till tjugo körande granskningar.
  • Etiketten är en mängd, inte en händelse. Sökningen rapporterar etikettens förekomst, så en märkt PR utlöses en gång, för alltid. Att lägga tillbaka en etikett efter att ha tagit bort den är osynligt för svepet; en nämning är vägen till en ny körning.

Sökningen körs en gång per installationskonto med den ägarens egen klient, eftersom en installationstoken bara ser den egna installationens repon — en enda serverövergripande autentiseringsuppgift skulle tyst missa varje PR under appens andra installationer.

En PR-kommentar kommer in med föga mer än en avsändarinloggning — en oautentiserad extern identitet tills den bevisats. Boten matchar den mot ett omvänt index över arbetsytans medlemmar och deras anslutna GitHub-konton, som byggs om med ett TTL eftersom människor loggar in och blir inbjudna medan servern kör.

Att misslyckas stängt ser ut som en stege, och varje steg svarar i den tråd det nekade i:

  • En okänd inloggning — inte länkad, eller länkad av någon som inte är medlem i den här arbetsytan — uppmanas att logga in och se till att de hör hit
  • En skrivskyddad medlem (viewer eller guest) får veta att deras roll inte kan starta arbete

De två fallen med okänd inloggning delar ett svar: det omvända indexet returnerar bara en medlem i den här arbetsytan, så ett olänkat konto och en länkad icke-medlem är samma bom. Ingen av dem fortsätter. En kommentar tillskrivs aldrig någon som inte skickade den, och en främlings ord kommer aldrig in i en arbetsyta som de inte kan se.

Etiketten ai-review har ingen avsändare att matcha, så den klättrar inte i den här stegen. Att lägga till den startar granskningen i den arbetsyta som redan länkar repot.

Varje sida av samtalet bär en egen identitet, och skillnaden är själva funktionen:

  • På GitHub är boten servern. Varje kommentar, reaktion och svar som bryggan publicerar går via appens installationstoken. Ingen klickade på något i en klient, så inget lånar en persons konto — samma regel som håller webhooks och pollning borta från mänskliga autentiseringsuppgifter.
  • I ytan är frågan din. Meddelandet tillskrivs den medlem som avsändaren matchades till, och agentkörningen utförs på deras vägnar: commits anger dem som medförfattare, och ytans autonomi-inställning och åtgärdsräcken gäller precis som för ett meddelande som skrivs i appen. Skrivningar till kodvärden från körningen går fortfarande ut som appen — samma regel som punkten ovan — inte med medlemmens personliga token.

Att publicera granskningsfynd tillbaka till GitHub är fortfarande en persons beslut, fattat i Control Center. En nämning kan starta en granskning; bara en människa publicerar en.

Allt som skrivits av ett botkonto kastas innan någon grind körs — vårt specifikt, och varje [bot]-suffix, så att en annan apps granskningsbot inte heller kan väcka vår. Utan detta skulle botens egna svar komma in som nya kommentarer och utlösa den om och om igen för alltid.

Suffixet [bot] är också anledningen till att kortformen finns. GitHub låser ett appkontos inloggning till <slug>[bot], erbjuder ingen autokomplettering för den och matchar aldrig @slug till appen — så att acceptera den bara slugen är vår matchares beslut, och att upptäcka den kräver ett sökspår över rå kommentars-text i stället för GitHubs nämningsindex. Kostnaden sägs rakt ut i stället för att slätas över: en människa som delar appens slug adresserar boten vid varje kortformsnämning av sig själv. Särpräglade appnamn gör kollisionen hypotetisk.

  • Ingen automatisk publicering. Fynden landar i granskningsytan; publicering till GitHub är en separat, mänsklig åtgärd
  • Ingen suddig identitet. Avsändaren matchas till exakt en medlem eller nekas — det finns ingen gissad tillskrivning
  • Ingen GitHub App-webhook. Den här ytan registrerar aldrig någon, så det finns ingen inkommande URL att exponera för PR-kommentarer. Andra produktwebhooks (pipelines, ärendesynk) är en separat, valfri konfiguration.