Bilko trunk crven 2026-08-16 — FX fail-closed, dva fail-open uslova, i zelen build koji nije mjerio nista (MC #107282/#107171/#107242)
Bilko trunk crven 2026-08-16 — FX fail-closed, dva fail-open uslova, i zelen build koji nije mjerio ništa
MC: #107282 (E2E), #107171 (Promote_Demo), #107242 (Build/CI_Gates), #107283 (produkt-rupa, otvoren)
PR-ovi: 344, 343, 345 — svi merge-ovani 2026-08-16/17
Main poslije svega: 68be6405
Šta se desilo
Main je bio crven tri builda zaredom — 1089 (13:55), 1091 (15:00), 1092 (18:00) — sva tri na
e970edfb, sva tri na istom mjestu: Playwright E2E (chromium, stage), Bash exited with code '1'.
Poruka iz testa:
invoice create failed: {"error":"Exchange rate not found for EUR -> RSD on 2026-08-16.
Add the rate before issuing documents in EUR.","code":"BAD_REQUEST"}
expect(received).toBe(201)
Komit e970edfb (#106892) je namjerno uveo fail-closed na FX kurs. Gate radi tačno kako je
projektovan. Problem je bio drugdje.
Korijen — i zašto je bio nevidljiv
storno-credit-note-106104.spec.ts je izdavao fakturu s hardkodiranim currencyCode: 'EUR'
(linija 126). Fajl je o storno/credit-note ponašanju: EUR se pojavljivao tačno jednom, a
tvrdnji o valuti, kursu ili baznom iznosu nije bilo nijedne. Valuta je bila slučajna — ušla je
iz HR/EUR helpera koji koristi i RS/RSD scenario-3 tenant.
Test je prolazio samo dok je kurs slučajno postojao. Fail-closed ga je pretvorio u dnevni pad: svaki dan bez unesenog kursa obara E2E, a E2E je zadnja kapija prije promocije — dakle blokira verifikaciju svakog rada na mainu.
Popravka (PR 344)
Fixture čita valutu organizacije umjesto da je pretpostavlja:
const currencyCode = await getOrganizationBaseCurrency(ctx, token)
Helper zove autentikovani GET /organization, traži HTTP 200 i troslovni kod. Postojeći ugovor,
ništa novo: SettingsRoutes.kt izlaže rutu, SettingsService vraća baseCurrency.
Po tenantu: HR/EUR i dalje šalje EUR (to mu jeste bazna valuta), RS/RSD sad šalje RSD.
apps/api netaknut, nijedna tvrdnja uklonjena.
Veći nalaz koji ovo NIJE riješilo — #107283
Ne postoji nijedan način da se kurs unese. Provjereno na azdo/main e970edfb:
- nula pisaca u
apps/api(nema insert/create/upsert/save nad tabelom kurseva) - nijedna Flyway migracija ne puni tabelu kurseva — ono što grep nađe u V105/V135/V144 je
kolona
exchange_ratena dokumentima, ne tabela kurseva SettingsServicesamo čita (451-481)- backend testovi upisuju direktno u svoju privatnu bazu, što Stage/Playwright ne može
- Prisma seed ima fiksni kurs za 01.01., ali Azure Stage vrti samo Flyway, pa taj seed nikad ne dođe
Dakle korisnik na RSD orgu ne može izdati EUR dokument. Nikad. To je produkt-odluka vlasnika #106892: ruta/admin ekran, scheduled importer (HNB/NBS), ili svjesno suziti fail-closed.
Dva fail-open uslova nađena usput
Ista porodica predikata, dvije različite izloženosti:
| linija | stage | stari uslov | izloženost |
|---|---|---|---|
| 1229 | Promote_Demo |
not(failed('E2E_UAT')) |
skip — pao uzvodni stage ⇒ E2E skipped ≠ failed ⇒ uslov se nikad ne razriješi ⇒ stage vječno pending, run vječno inProgress |
| 762 | Build |
not(failed('CI_Gates')) |
cancel — CI_Gates je prvi stage i nema condition pa se ne može preskočiti; ali otkazan CI_Gates ostavlja uslov istinitim |
Prva je proizvela devet duh-runova između 03. i 06.08. koje niko nije mogao razlikovati od
legitimnog čekanja na odobrenje: build 879 (Flyway pao → E2E preskočen → promocija nemoguća) i
build 1079 (svih pet zeleno, stvarno čeka CEO) izgledali su identično — oba pending.
Druga znači da otkazan CI_Gates može pustiti Build da gurne slike u bilkodemo.azurecr.io
(ACR login 783-790, API push 818-826, Web push 858-872) — a Trivy skenira tek poslije pusha.
Obje popravljene istim potezom, u odvojenim PR-ovima radi čistog reverta:
- not(failed('E2E_UAT')), → + succeeded('E2E_UAT'),
- not(failed('CI_Gates')), → + succeeded('CI_Gates'),
Time su svi stageovi usklađeni: Flyway_Migrate (924), Deploy_Stage (989), E2E_UAT (1139),
Promote_Demo (1229), Build (762) — svi na succeeded().
Lekcija koja se ponavlja: zeleno koje ne mjeri ništa
PR 343 je imao zelen validation build na kodu na kojem je main padao. Ista stvar, suprotan ishod — jer PR-validation buildovi preskaču E2E. Izmjereno, ne pretpostavljeno:
build 1093 (PR 344, zelen):
[Stage] E2E UAT (Playwright → Stage): result=skipped
Zato nijedan merge ovdje nije pravdan zelenim PR buildom. Dokaz je bio isključivo main build:
build 1092 (prije): [Task] Playwright E2E (chromium, stage): result=failed
build 1094 (poslije): [Task] Playwright E2E (chromium, stage): result=succeeded
Zamka u alatu koja je skoro proizvela pogrešan izvještaj
azdo-build-logs.sh ispisuje samo taskove koji nose upozorenje ili grešku. Čist task je tamo
nevidljiv. U buildu 1094 Playwright se nije pojavio — i u zelenom buildu 1087 se ne pojavljuje.
Odsustvo u tom alatu ne razlikuje „prošlo tiho" od „nikad se nije pokrenulo".
Zato je napravljen ~/system/tools/azdo-build-timeline.sh — čita timeline API i daje result= po
svakom stageu/jobu/tasku; ako traženog koraka nema, eksplicitno kaže da je odsutan iz timeline-a.
Uz njega ide ~/system/tools/run-azdo-build-wait.sh — blokira do kraja builda pa ispiše stvarni
timeline. Postoji zato što je „provjeriću kad build završi" obećanje bez mehanizma: čeka da ga neko
podsjeti. Ovako se orkestrator sam probudi.
Redoslijed isporuke (i zašto takav)
- PR 344 prvi — jedini koji otvara trunk
- main build 1094 → dokaz da E2E stvarno prolazi
- PR 343, uz rebase na novi main i requeue validacije (staro zeleno je bilo na drugom commitu)
- PR 345, isto rebase-ovan
Org ima jedan self-hosted CI slot, pa se buildovi ne smiju takmičiti; requeue je već ranije obarao žive buildove, zato se prije svakog requeue-a provjerava da ništa ne radi.
Kako je rađeno
Izvršilac: pi-orchestrator (openai-codex/gpt-5.6-sol) preko Company Mesh P2P kanala.
Orkestracija i verifikacija: John (Claude). Svaki nalaz je nezavisno provjeren prije nego je prenesen
— diff, linije u pinovanom blob-u, ruta i polje u API-ju, timeline builda.
Peer je jednom vratio BLOCKED umjesto da izmisli mehanizam za unos kursa; taj BLOCKED je i otkrio #107283. To je ispravno ponašanje izvršioca i vrijedi više od brzog PASS-a.
No comments to display
No comments to display