Outcome unknown w automatyzacji API: co zrobić, gdy nie wiadomo, czy operacja się wykonała?

Gdy zewnętrzne API nie zwraca jednoznacznej odpowiedzi, automat nie ma podstaw, by oznaczyć sprawę jako wykonaną albo odrzuconą. Outcome_unknown oznacza, że system nie ma potwierdzenia skutku, mimo że żądanie mogło zostać przyjęte przez dostawcę. Taki stan blokuje ponowienie w ciemno i przenosi proces do odczytu albo decyzji operatora.
Trzeci wynik, którego nie ma w schemacie sukces albo porażka
Schemat sukces albo porażka działa tylko wtedy, gdy odpowiedź dostawcy jednoznacznie opisuje rezultat. Przy API granica sieci, limit czasu odpowiedzi albo brak uprawnień do późniejszego odczytu mogą zostawić rezultat bez jednoznacznego opisu. Outcome_unknown nie mówi, że operacja się nie udała. Mówi, że system nie ma podstaw, aby przypisać jej stan succeeded ani failed.
Dobry przykład daje publikacja na Stronie Facebooka: żądanie zakończyło się timeoutem odpowiedzi, a wpis powstał cztery sekundy po wysłaniu żądania. Gdyby system potraktował timeout jako porażkę, mógłby uruchomić kolejną publikację. Gdyby potraktował go jako sukces, ukryłby brak potwierdzenia. Trzeci stan przechowuje dokładnie tę różnicę: efekt mógł wystąpić, ale automat nie dostał dowodu. Od tego momentu bezpieczna ścieżka nie prowadzi przez kolejne żądanie zapisu, tylko przez próbę rozstrzygnięcia skutku.
Czym różni się potwierdzona porażka od wyniku nieznanego
Potwierdzona porażka ma źródło w odpowiedzi dostawcy albo w walidacji, która nie pozostawia miejsca na wykonanie akcji. Graph API odrzuca odczyt wpisów Strony tokenem użytkownika, zwracając kod błędu 190. Taki rezultat nie jest niepewnością; dostawca odmówił wykonania danej operacji w tej konfiguracji poświadczeń. System może wtedy zapisać przyczynę, pokazać ją operatorowi i nie udawać, że czeka na potwierdzenie.
Wynik nieznany powstaje gdzie indziej: na styku wykonanego żądania i braku rozstrzygającej informacji zwrotnej. Nie wolno go traktować jak zwykłego błędu technicznego, bo błąd techniczny po stronie odbioru odpowiedzi nie wyklucza skutku po stronie dostawcy. Ta różnica zmienia dalszy proces. Po potwierdzonej porażce można poprawić dane, uprawnienia albo konfigurację. Po outcome_unknown jedyną automatyczną czynnością powinien być odczyt stanu u dostawcy, jeśli kanał i poświadczenia na to pozwalają.
Dlaczego ponowienie w ciemno jest groźniejsze niż brak akcji
Ponowienie po wyniku nieznanym wygląda jak szybka naprawa, ale może wysłać drugie żądanie po tym, jak pierwsze zostało już przyjęte. Przy publikacji skutkiem może być drugi wpis. Przy automatyzacji sprzedaży może to być druga wiadomość do tej samej osoby albo drugi rekord leada utworzony z tego samego zdarzenia. Problem nie leży w samym retry, lecz w retry bez wiedzy, czy poprzednia próba zostawiła ślad u dostawcy.
W Company OS zadanie z wynikiem nieznanym nie jest ponawiane automatycznie w żadnym kanale. To ograniczenie oddziela awarię techniczną od niekontrolowanego powielania skutków. System może ponawiać tylko wtedy, gdy zna granice ponowienia. Dla zakończonego zadania istnieje inna blokada: zadanie publikacji w stanie succeeded odmawia ponownego wykonania kodem publication_already_succeeded. Outcome_unknown wymaga jeszcze ostrożniejszej ścieżki, bo nie ma potwierdzonego statusu, na którym można oprzeć decyzję o kolejnym zapisie.
Idempotencja: co realnie chroni, a czego nie obejmuje
Idempotencja w automatyzacji porządkuje powtarzalność zleceń po stronie własnego systemu. W Company OS klucz idempotencji powstaje z workspace'u, wpisu kalendarza, wersji treści, kanału i akcji, a nie z czasu wywołania. Dzięki temu to samo zlecenie ma ten sam identyfikator operacyjny, niezależnie od tego, kiedy proces próbuje je obsłużyć. Taki mechanizm pomaga zatrzymać ponowne wykonanie tej samej pracy w obrębie własnej logiki.
Ten mechanizm nie oznacza jednak, że dostawca zewnętrzny rozpozna i odrzuci duplikat. WordPress REST nie ma klucza idempotencji dla tworzenia wpisów. Jeżeli żądanie utworzenia wpisu zostało przyjęte, a odpowiedź nie wróciła, wewnętrzny klucz nie daje automatycznego dowodu, co powstało po stronie WordPressa. Idempotencja chroni przed powtórzeniem tego samego zlecenia w systemie sterującym. Nie zastępuje odczytu skutku u dostawcy i nie usuwa niepewności po timeoutcie.
Rozstrzygnięcie przez odczyt u dostawcy, nie przez założenie
Rozstrzygnięcie outcome_unknown polega na porównaniu zapisanego śladu operacji z tym, co da się odczytać u dostawcy. Rekonsyliacja w Company OS porównuje zapisany ślad z tym, co widać u dostawcy, i zwraca jeden z trzech wyników: found, not_found albo unknown. Found zamyka niepewność w stronę wykonania. Not_found daje podstawę do dalszej decyzji procesowej. Unknown zostawia sprawę bez automatycznego rozstrzygnięcia.
Odczyt wymaga właściwych poświadczeń. Odczyt wpisów Strony wymaga tokenu Strony, czyli tego samego rodzaju poświadczenia co zapis. Token użytkownika nie wystarcza w opisanym przypadku, bo Graph API odrzuca taki odczyt kodem 190. Nie każdy kanał daje też ścieżkę rekonsyliacji w danej konfiguracji: LinkedIn bez zakresu r_member_social nie pozwala odczytać opublikowanego posta, więc dla tego kanału rekonsyliacja pozostaje niedostępna. W WordPressie potwierdzenie, czy szkic powstał, jest możliwe tylko wtedy, gdy serwis ma zarejestrowane pole meta widoczne w REST.
Miejsce, w którym decyzję podejmuje człowiek
Człowiek wchodzi do procesu wtedy, gdy automat nie ma prawa zrobić kolejnego zapisu i nie potrafi rozstrzygnąć wyniku odczytem. To nie jest ręczne zastępowanie automatyzacji, tylko kontrola miejsca, w którym dalszy zapis mógłby powielić skutek. Operator powinien widzieć zadanie, kanał, akcję, zapisany ślad, odpowiedź albo jej brak oraz wynik próby rekonsyliacji.
Decyzja człowieka może polegać na oznaczeniu skutku jako znalezionego po ręcznej weryfikacji, oznaczeniu go jako niewykonanego albo pozostawieniu blokady do czasu uzupełnienia poświadczeń. Jeżeli odczyt wpisów Strony wymaga tokenu Strony, operator nie powinien omijać tego warunku ponowną publikacją. Jeżeli LinkedIn bez zakresu r_member_social nie pozwala odczytać opublikowanego posta, system powinien pokazać ograniczenie zamiast wybierać status za operatora. Miejsce decyzji człowieka zaczyna się tam, gdzie system nie ma już bezpiecznej operacji automatycznej poza zatrzymaniem procesu.
Co się dzieje, gdy tego stanu nie ma w systemie
Brak outcome_unknown zmusza system do fałszywej klasyfikacji. Timeout zostaje wtedy potraktowany jak porażka albo jak sukces, mimo że żadna z tych etykiet nie opisuje pełnej sytuacji. Pierwsza ścieżka uruchamia ryzyko ponowienia po żądaniu, które mogło zostać przyjęte. Druga ścieżka zamyka zadanie bez dowodu, że efekt faktycznie istnieje u dostawcy.
Koszt takiego uproszczenia pojawia się poza systemem sterującym. W kanale mogą powstać duplikaty publikacji. Odbiorca może dostać podwójną wiadomość. Zespół operacyjny traci jasny sygnał, czy automat zatrzymał się z powodu odmowy dostawcy, braku uprawnień do odczytu, czy niepewnego skutku zapisu. Bez osobnego stanu nie da się też wymusić reguły: po nieznanym wyniku nie wykonuj ponownie zapisu. System binarny ukrywa różnicę między awarią wykonania a awarią potwierdzenia, a to prowadzi do decyzji opartych na założeniu.
Lista kontrolna dla wdrożenia
- Dodaj jawny stan outcome_unknown obok succeeded i failed.
- Zapisuj ślad operacji potrzebny do późniejszego porównania z dostawcą.
- Zabroń automatycznego retry dla zadań z wynikiem nieznanym.
- Ustal, z jakich elementów powstaje klucz idempotencji; w Company OS są to workspace, wpis kalendarza, wersja treści, kanał i akcja.
- Sprawdź, czy kanał daje odczyt skutku przy dostępnych poświadczeniach.
- Dla Strony Facebooka użyj tokenu Strony do odczytu wpisów.
- Nie zakładaj rekonsyliacji tam, gdzie jej nie ma; LinkedIn bez r_member_social nie pozwala odczytać opublikowanego posta.
- Przy WordPressie sprawdź, czy pole meta widoczne w REST pozwala potwierdzić szkic.
- Pokaż operatorowi sprawy nierozstrzygnięte i wymagaj decyzji przed kolejnym zapisem.
- Jeżeli któregokolwiek punktu nie da się odhaczyć, potrzebna jest rozmowa o architekturze przed uruchomieniem automatu.