Rasadnik Milić · roadmap 57 · WooCommerce master ↔ Vendure

Sync zaliha: gdje smo, kamo idemo, kako testirati

Jedna stranica za jedan plugin-par. Sve što ovdje piše kao „gotovo” ima commit i broj testova; sve što piše „čeka tebe” ne može nitko drugi napraviti.

Stanje na 16. rujna 2026. · putanje premjerene 18. rujna 2026. · gate i polazne brojke promptova premjereni 20. rujna 2026.: potprojekt 1 — faza 3 dokazana uživo (test na SKU 1452), sve tri faze u mainu Potprojekt 2 (nexus): uživo na kontrolnom serveru od 14. 9., E1–E9 gotovi; E10–E17 svi spojeni u main — E10 dorada po analizi, E11 prekidač kartice i modal dućana, E12 preglednost dijagnostike, E13 dorada po pregledu, E14 narudžbe po kalendaru, E15 pošta imenuje dućane, E16 rasponi i oblačići na povijesti i crti, E17 sekcije po dućanu u pošti i preimenovanje zaglavljeno → nesinkronizirano. Deploy je ručan i 16. rujna je prošao dvadeset puta (09:58 → 21:09), zadnji dovršeni u 20:58 sa ok=40 changed=10 · 623 testa (zadnji zabilježeni broj, nije prebrojan iznova), 9 commitova (oblačić na grafovima, dijagnostika javlja radi li skripta stranice, kalendarski rasponi na povijesti i crti, drugi tab povijesti sa starošću problema, sekcije po dućanu u pošti, popravak kreiranja adresnice). Deploy je ručan: zadnji je prošao 16. 9. u 17:18 (Ansible, 40 zadataka, 10 promjena), peti toga dana · 637 testova na grani (623 u mainu) Gate 20. 9. u 16 h: ./flora check → 8 prošlo, 0 palo, 2 bez gatea, izlazni kod 0, 4.443 testa kroz sedam gateova (flora-core 3.531 u 346 datoteka, sync-nexus 650, flora-libs 76, storefrontovi 78 + 75, flora-base 33; flora-atlas ne broji testove nego 187 poveznica). Osam redaka, a sedam gateova: flora-base se mjeri dvaput, i izvorno i u linux grani. Do 20. 9. je ovdje pisalo 6 prošlo, 2 palo, 2 bez gatea, 4.377 testova — to je brojka od 18. 9. u 22 h. Ništa nije popravljeno: obje su trgovine tada pale na astro build jer Vendure nije radio, a 20. 9. je radio (lsof -i :3000 ima slušatelja — vlasnikov). Rezultat gatea ovisi o tome je li port 3000 podignut, i to nije svojstvo koda. Prije toga je ovdje stajalo: gate 16. 9. u 21:20, 3.301 server u 322 datoteke + 3 + 17 + 75 + 78 = 3.474, jednom naredbom · 91 PHP i 504 testa plugina (iz Dockera, nisu ponovljeni 16. 9. — na računalu nema php) Spec: flora-atlas/specs/specs/2026-09-09-woocommerce-stock-sync-design.md · nadzor: .../2026-09-11-nadzor-zaliha-nexus-design.md

Putanje i gate premjereni 18. rujna 2026. Stranica je pisana 16. rujna, dan prije nego je monorepo razrezan na deset repozitorija, pa je svaka putanja u njoj pokazivala na jedno stablo. Najozbiljniji nalaz: 22 gotova prompta počinjala su rečenicom Radiš u ~/Dev/rasadnik/new-start — repozitoriju koji je od 17. 9. zamrznut i u kojem se po vlastitom PRESELJENO.md više ne razvija. Sada počinju u flora-baseu. Uz to: samples/wp-venture-sync je danas flora-core/wp-venture-sync, samples/sync-nexus je samostalan repozitorij sync-nexus, a gate više nije npm run check nego ./flora check, jedan po repozitoriju. Brojke gatea u promptovima zamijenjene su izmjerenima 18. 9. — stara baza od 3.102 bila je monorepo i danas ne postoji ni pod kojom naredbom.

Dopuna 18. 9. u 22 h — polazna brojka se pomakla drugi put istog dana, i to je sama po sebi poruka. Prompti su ujutro govorili 4.204 u šest gateova; navečer je ./flora check dao 4.377 u sedam (flora-base je dobio vlastiti gate, flora-core narastao za 140 testova). Brojke dolje su osvježene, ali ne vjeruj im na riječ — izmjeri svoju polaznu vrijednost prije nego išta dirneš.

Dopuna 20. 9. u 16 h — treći put. Polazna vrijednost u svih dvadesetak promptova ispod bila je do danas 4.377 (18. 9. u 22 h). ./flora check 20. 9. u 16:09 daje 4.443 testa, flora-core 3.531 u 346 datoteka, izlazni kod 0 — i to je sada upisano u svaki prompt, na 26 mjesta. Sesija koja bi krenula od stare brojke vidjela bi 66 testova viška i pomislila da je nešto pokvarila. Dva puta u tri dana polazna se brojka pomaknula ispod gotovih promptova; to nije razlog da se prompti ne pišu, nego razlog da svaki počne mjerenjem.

I jedna zamka koju prompti ne spominju: ./flora check danas vraća izlazni kod 1, ne 0, ako Vendure nije podignut. Obje trgovine padaju na astro build jer straža requireProductsForBuild odbija build s 0 proizvoda; testovi i type-check im prolaze. Gdje u promptu piše „exit 0", to vrijedi uz podignut Vendure — inače usporedi po repozitoriju, ne po skupnom izlaznom kodu.

Kod

Faza 1 je izgrađena na obje strane i spojena u main: WP plugin na masteru s hookovima (Task 6), Vendure plugin sa stranicom „Zalihe: WooCommerce”. Faza 0 je izmjerena. WP plugin je deployan i aktivan na dev-masteru. Faza 2 dokazana uživo. Faza 3 (automatika) izgrađena (plan 03, 8 taskova sa spec i quality reviewom, ~20 commita): izlazni dnevnik iz događaja, pull po kursoru s ref-idempotencijom i bravama, drain s nadjačavanjem, kick, raspored u workeru, dnevna rekoncilijacija, mail za greške, blok „Automatika” na stranici; WP 0.3.0 s ack i kickom iz shutdowna.

Dokaz

Faza 1 dokazana uživo 10. 9.: veza radi, 993 šifre upareno, 5 pa 988 primijenjeno bez greške, sljedeća razlika 0. Hookovi su vidjeli prvi živi pokret (SKU 617). Put child → master i varijabilni proizvodi mjere se tek na produkciji. Faza 2 dokazana uživo 10. 9. navečer: 2 šifre gurnute i primijenjene na masteru s točno jednim retkom dnevnika po pokretu, replay istog ref-a → duplicate, nepoznata šifra i stari paket odbijeni, pregled parkiran pa obrisan pravim pushom, razlika 0. Prvi push je pao na 726 siročadi u lookup tablici mastera (popravak 0.2.1 isti dan). Faza 3 dokazana uživo 11. 9. na SKU 1452 (WP 0.3.0, način „Odmah i po rasporedu”, interval 60 s): izmjena u Vendureu od −15 na masteru za nekoliko sekundi bez odjeka hookova; tri izmjene spojene prigušenjem u jedno slanje, svaki pokret sa svojim ref-om; promjena na masteru u Vendureu za 15 sekundi, pa unsentMovements 0 i lastAcknowledgedId 21 (potvrda radi); istodobno −1 u Vendureu i −2 na masteru → obje strane 72, delte se zbrojile; SALE −1 i CANCELLATION +1 poslani, ALLOCATION ništa; usklađivanje prijavilo razliku upisanu mimo dnevnika (applied 0, skipped 554, jedan unos) i nije ispravilo ništa; pet padova s 401 dalo točno dva maila, jedan po vrsti. Kraj: obje strane 69, ništa dugovano, kursor 27. Neizmjereno ostaje ono što dev ne može pokazati (child → master, varijabilni proizvodi, master koji zašuti usred paketa) i ono što u potprojektu 2 (nexus) još nije napisano: potprojekt 2 (nexus) je napisan do kraja, E1–E8 — devet tablica, potpisani prijem izvještaja, usporedba, pošta, retencija, pet ekrana, skripta za dućane, zadatak u Vendureu i Ansible deploy — i od 14. 9. radi uživo na kontrolnom serveru (sync-probe.rasadnik-milic.hr), gdje ga produkcijski master, oba childa i Vendure stvarno hrane. Sve poslije toga (E9) pisano je po onome što se na pravim podacima vidjelo: podkanali Vendurea kroz jednu tajnu, vrijeme pokreta izvučeno iz same narudžbe umjesto iz razlike snimaka, zanemarene razlike, izvor istine iza druge lozinke i prerađeno sučelje. 500 testova u flora-base/sync-nexus/.

Traka osam koraka: sedam do automatike, pa nadzor · ochre = sada

0Mjerenje na dev-masteruizmjereno 10. 9.
1aWP plugingotovo
1bVendure plugin + stranicagotovo
1cFaza 1 uživo: Woo → Vendure ručnodokazano 10. 9.
1dHookovi na masteru (Task 6)gotovo · 1 živi pokret
2Vendure → Woo ručno (plan 02)dokazano 10. 9.
3Automatski (planovi 03 i 04)dokazano 11. 9.
4Nadzor (nexus, potprojekt 2)uživo od 14. 9., E10–E17 u mainu, dvadeset deployeva 16. 9.

Što je gotovo po commitu, sve u main

DioŠto radiDokazStanje
Spec + plan 01Model bez šefa: razmjenjuju se pokreti (delte) s porijeklom, ne stanja; SKU je ključ; backorders se samo bilježi; odstupanja kroz nexus. Plan u 15 taskova, svaki s promptom.74d20a7 · acfd858 · 6a18330gotovo
Faza 0, izmjerenoDev-master: 1.000 proizvoda, 0 varijacija, svi sa SKU-om, bez duplikata, 989 vodi zalihu, backorders posvuda „no”, jedan proizvod na −160 (SKU 617, ručno ispraviti prije primjene). Presjek: svih 1.000 je u Vendureu, 547 Vendure SKU-ova master nema (podskup, očekivano). Hookovi pri spremanju na masteru: updated_post_meta _stock → product_set_stock → updated_props. Put child → master nije mjerljiv na devu (nema licence).ccc5505 · spec „Faza 0 — izmjereno”gotovo
WP pluginflora-core/wp-venture-sync/: dnevnik pokreta, HMAC nad metodom+rutom+vremenom+tijelom, REST health / snapshot / movements, wp-cli, admin stranica, README, hookovi (set_stock primarno, updated_post_meta kao mreža koja se dedupira po zahtjevu i ponovno naoruža). 24 PHP testa (Docker, bez composera).e95df2d … bb160e6gotovo
Vendure pluginflora-core/src/plugins/woocommerce-stock-sync/: postavke (tajna write-only), klijent, čista razlika po SKU-u, migracija 1790000087000, servis diff/apply (svaki korak u vlastitoj transakciji, dnevnik preživi rollback), Admin API, stranica Katalog → „Zalihe: WooCommerce” hr/sl/de. 102 vitest testa.554243a … 2a00959gotovo
Živa provjera, negativan putKlon baze, API na 3010, master nedostupan: stranica se renderira, postavke se spremaju, greške čitljive s URL-om, dnevnik izvršavanja, traka neuspjelih, „Označi viđeno”. Dva Dashboard gotcha-a nađena tek uživo i popravljena.520ee43 · 2a00959gotovo
DeployAnsible kao za prijašnje pluginove (inventory-sync, quick-vies-checkout): tar → backup postojeće mape → upload → vlasništvo. Cilja samo mastere, zadano dev. Pravi inventory lokalan i ignoriran. Izvršen na dev-master 10. 9. (failed=0), plugin aktiviran wp-cli-jem.54af926 · e1468d6obavljeno
Živa provjera, pozitivan putDev-master ↔ lokalni Vendure (3000), tajna na obje strane: „Veza radi · v0.1.0 · full”. Prva razlika: 993 upareno (7 od 1.000 je neobjavljeno, snapshot nosi samo objavljene), 993 promjene (114 količina/status, 879 samo backorders), 554 samo u Vendureu. Pet šifri primijenjeno i provjereno u bazi (200 = master, INHERIT netaknut, backorders „no”), pa 988 primijenjeno, 0 grešaka, sljedeća razlika 0. Prvi pokušaj pao na svih pet (id kao string → INSERT bez sku) i ništa nije upisao: rollback po koraku dokazan uživo. Dnevnik mastera ima prvi živi pokret (SKU 617, prvo viđenje).f101102 · spec „Faza 1 — izmjereno”gotovo
Faza 3, kod (plan 03)Vendure: izlazni dnevnik iz StockMovementEvent (vlastiti upisi se prepoznaju po symbol-oznaci na kontekstu, jer EventBus kopira ctx); pull() po kursoru, delta pod pg_advisory_xact_lock po šifri, primljeni ref u istoj transakciji (replay = duplicate), mutex pg_try_advisory_lock preko procesa, kursor po stranici pa ack; drain() dugovanih redaka s nadjačavanjem (ručni push poravna starije retke istih šifri), parkirani retci; reconcile() koji prijavljuje; kontroleri /stock-sync/kick|health|snapshot|movements s potpisom; job queue za kick (5 s → 60 s backoff); dva ScheduledTaska u workeru; mail za greške (jednom po kodu na sat); blok „Automatika” i tri gumba. WP 0.3.0: POST /movements/ack (pisac za sent_at), kick iz shutdown nakon fastcgi_finish_request (jer blocking=>false nije asinkron), „Check Vendure”. Plan 04: prekidač enabled postaje način off / schedule / immediate — u Dashboardu padajući izbornik „Automatika” („Isključeno”, „Samo po rasporedu”, „Odmah i po rasporedu”) i zasebna kvačica „Dnevno usklađivanje”, uz pločice za oboje. U načinu „odmah” izlazni pretplatnik gurne vlastiti pokret čim nastane, prigušeno na jedno slanje u 5 s po procesu, i to samo slanje (pokret iz Vendurea ne govori ništa o masterovu dnevniku); takav redak nosi okidač event („događaj”). Bez migracije: postavke su JSON (stari enabled: true čita se kao „samo po rasporedu”), a /health WordPressu i dalje vraća enabled = „nije isključeno”. 91 PHP + 504 vitest testa.9c313dd … faza 3gotovo · dokazano 11. 9.
DokumentacijaREADME oba plugina, flora-atlas/stanje/plugin-inventory.md (28 plugina, sync zaliha Live-verified), roadmap 57 s brojkama, spec s „Faza 0/1 — izmjereno”, odstupanja koda od plana zapisana u planu.5b69fe4 · e51ae36 · faza 1gotovo
Faza 2, kod (plan 02)WP 0.2.0: POST /apply (samo full; 400/401/403/422), redak dnevnika s ref-om ide prije upisa zalihe u istoj MySQL transakciji (UNIQUE = idempotencija), wc_update_product_stock pod guardom, hookovi šute; dry run parkira paket koji WP admin potvrđuje (hash, 15 min, confirm); wp vendure-stock apply; ledgerEngine u healthu. Vendure: planPush (obrnuti predznak, prodivost onHand > prag, granice), potpisani POST, push() s provjerom mastera, guardom „jedan upis odjednom”, izlaznim dnevnikom prije slanja, paketima po 500, sentAt na applied/duplicate, stabilnim ref-ovima po planu (pregled i push nose iste). Stranica: dva nova gumba, blok pregleda/rezultata, hr/sl/de. 71 PHP + 179 vitest testova.86ffff7 … 86c3f99gotovo · dokazano 10. 9.
Nexus E1: kosturflora-base/sync-nexus/: SQLite shema devet tablica, svaka s vlastitom retencijom upisanom u komentar CREATE-a; jedna ruta POST /api/report iza HMAC-SHA256 potpisa (shema prepisana iz potprojekta 1); idempotencija po izvoru i vremenu izrade, pa ponovljeni izvještaj vrati {"duplicate": true} i ne upiše ništa. Bez composera, frameworka i ijedne vanjske biblioteke. 41 test (prije njega 0).4b676ff · 1a95f6egotovo
Nexus E2: usporedbaPet parova izvora iz postavki, a ne iz koda; tri vrste nalaza po šifri (razlika, samo jedna strana, nema podatka) i prekidač only_one_side po paru; prije usporedbe se traži da su obje strane javile unutar zadnja četiri sata. Šifra se uspoređuje doslovno — bez trimanja, bez velikih/malih slova — jer bi tiho poravnavanje sakrilo baš onu vrstu greške koju nexus traži. 104 testa.0b5cb0a · 18b10a2 · 8314313gotovo
Nexus E3: poštaSMTP2GO preko njihova HTTP API-ja, namjerno ne Vendureovim putem: nadzor mora moći vikati dok je ono što nadzire pokvareno. Otisak nalaza i tihi prozor od 24 h da se isto neslaganje ne šalje svaka četiri sata; bin/silence-check.php iz crona javlja izvor koji je zašutio (4,5 h). Bez ključa uzbuna se uredno zapiše u alerts, imenuje se varijabla koja fali i ne šalje se ništa — bez pada na mail(). 167 testova, i nijedan ne dira mrežu (fake klijent broji koliko je puta izgrađen: 0).79edcd9gotovo
Nexus E4: retencijaNoćni posao u 3:00: sažmi dan u stock_daily, pa obriši, pa VACUUM — i to tim redom, jer je brisanje prije sažimanja dan povijesti koji nitko drugi ne čuva. Limiti se čitaju iz sqlite_master, ne iz konstante, pa shema i posao ne mogu razići; tablica bez komentara zaustavi posao imenom. Dokazano: s namjerno srušenim INSERT-om na bazi od 97.320 redaka posao je izašao 1 i nijedan redak nije nestao; suhi prolaz imenovao 852.591 redak, pravi obrisao 852.591. Suhi je prolaz usput nađen kako stvara bazu od 102.400 B na putanji koja je nema — popravljeno, i test to drži. 323 testa nakon spoja u main.811ad5f · 8ddb428 · f10b55fgotovo
Nexus E5: dashboard i APIPet ekrana (pločice, tablica neslaganja, 30 dana povijesti, sedmodnevna vremenska crta, narudžbe) i četiri JSON rute. Dvije brave koje se nikad ne sreću: kolačić sesije za ljude, HTTP Basic za strojeve — token na ekranu daje 302 na prijavu, kolačić na API 401. Baza se ne poslužuje ni izravno, ni kao -wal, ni kroz .. (svuda 404). Poveznica na proizvod po izvoru, iz predloška u okolini: dućani idu na pretragu administracije po šifri, Vendure na stranicu varijante, jer se ondje unosi količina. 226 testova.0b7a303 · 9ed40be · 6f6f44bgotovo
Nexus E6: skripta za dućaneJedna samostalna datoteka za wp eval-file, s obje grane u sebi: master čita dnevnik pokreta plugina iz potprojekta 1, child razliku računa prema vlastitom prethodnom snimku. Postavke stoje u wp-config.php, snimak izvan web roota (skripta odbija putanju unutar ABSPATH). Tri prolaza protiv živog lokalnog nexusa: prvi firstRun, drugi točno jedan pokret −15, treći bez promjene i izvještaj se svejedno šalje (šutnja je alarm). Prekinut upis snimka s kill -9 ostavio je sha256 nepromijenjen. 296 testova.3818006 · ff72207gotovo
Nexus E7: zadatak u VendureuTreći ScheduledTask woo-stock-report u flora-core/src/plugins/woocommerce-stock-sync/: svaka četiri sata šalje zalihe, pokrete i narudžbe. Nije vezan na način sinkronizacije — trgovina koja prestane javljati čim se sinkronizacija ugasi oslijepi baš kad netko po njoj radi — nego na vlastiti prekidač reportEnabled, a ekran naglas kaže da će nexus tada tu trgovinu prijaviti kao šutljivu. Tajna je zasebna od one prema masteru. productId je id varijante, što je jedino polje koje dashboardu treba za poveznicu.83992a3gotovo
Nexus E8: deployCompose (php:8.2-cli bez Dockerfilea — slika već nosi curl, json, pdo_sqlite, sqlite3), host nginx vhost i dva Ansible playbooka: nexus na kontrolni server, skripta na dućane. Dokazano dizanjem stacka: dimni test 400, svih pet ekrana 302 na prijavu, baza 404 i izravno i kroz .., kod montiran read-only, lsof pokazuje samo 127.0.0.1. PHP_CLI_SERVER_WORKERS=4 izmjereno: dva istodobna zahtjeva, dva PID-a, 2 s umjesto 4 s. Playbook: prvi prolaz changed=11, drugi changed=7 (samo mehanika backupa) i redak upisan u bazu između dva prolaza preživio; skripta na djeci 4 pa 0, mode=verify ne mijenja ništa. Nađena i popravljena prava rupa: iza nginxa je ograničenje prijave brojilo svima istu adresu (172.24.0.3), pa je pet krivih pokušaja bila jedna kvota za sve i napadač je mogao zaključati vlasnika — NEXUS_TRUSTED_PROXIES, dokazano stvarnim prijavama. 331 test (323 prije ovog koraka; svih 8 novih drži taj popravak). Pošteno rečeno: healthcheck zaglavljivanje čini vidljivim, ali compose sam ne restarta nezdrav kontejner.1cdb423 … 4845389gotovo · uživo od 14. 9.
Nexus E9: živi rad i doradaUživo na kontrolnom serveru od 14. 9., i od tada se mijenja po onome što se na pravim podacima vidjelo. Podkanali: Vendure javlja po kanalu, jednom tajnom — izvještaj nosi channel i popis channels sa statusom, a nexus ime izvora (vendure:plant-garden.hr) izvodi sam, pa se u NEXUS_SOURCES ne upisuje ništa; ugašen kanal razlikuje se od zašutjelog jer popis stiže uz svaki izvještaj. Vrijeme pokreta iz narudžbe: WooCommerce uz svaku stavku zapisuje _reduced_stock, pa pokret nosi minutu same narudžbe umjesto minute kad je razlika snimaka primijećena; razlika snimaka javlja još samo ostatak (ručna izmjena, dostava), a vremenska crta takav pokret crta kao crticu raspona s praznom točkom. Jednokratni prelazak: backfill=7 na dućanu i bin/migracija.php --apply na nexusu. Zanemarene razlike: pravilo po šifri, vrsti nalaza i paru (artikl koji se namjerno prodaje samo u jednoj zemlji) — ne broji se na pregledu ni u e-mailu, ali se i dalje uspoređuje i ostaje u izvozu. Zaglavljeno razilaženje: šifra koja je u neskladu u tri uzastopne usporedbe nije sync u letu nego promjena koja nikad nije stigla — vlasnikov slučaj „jedan store proda nešto a to se ne propagira na master". Podatak je već bio u comparison_items, pa je to upit i oznaka, bez nove tablice: velika kartica „izvor istine je van synca" iznad svega na početnoj (samo kad ima čega, s modalom koji imenuje svaku šifru i obje količine) i ista takva u e-mailu, složena kao tablica da je nacrta svaki klijent, jantarna oznaka u tablici, filtar samo zaglavljena, broj na kartici pregleda i dodatak u naslovu maila (zanemarena razlika se ne broji, inače bi artikl koji se namjerno prodaje u jednoj zemlji zauvijek bio zaglavljen). Čija je strana: zaglavljena razlika prema izvoru istine ne znači sama po sebi da katalog nije dobio promjenu — vlasnikov primjer „izvor istine 10, trgovina 1 osam, trgovina 2 deset" znači da odudara trgovina 1. Nexus sad odlučuje čija je strana (svi ostali se slažu, a izvor istine ne → katalog; jedan odudara → dućan; svatko svoje → ne odlučuje se), pravilo se bira uz izvor istine (konsenzus, vecina, svaka), a pregled i pošta crtaju dvije kartice: crvenu za katalog, jantarnu za dućan. Dijagnostika: tab u postavkama koji ispisuje aritmetiku iza brojki — po paru usporedbe u prozoru, zanemareni, zaglavljeni i najdulji niz, pa svaka zaglavljena šifra s krugovima i početkom — te izvoz cijele snimke u .json bez ijedne tajne, lozinke i adrese. Uz njega je ispravljena i sama brojka: kartica broji nalaze (jedna šifra zaglavljena prema dva dućana su dva nalaza) i uz njih piše na koliko je to šifri. Sučelje: izvor istine kao zaključan izbor iza druge lozinke i obavezno pitanje na svježem nexusu, postavke u četiri taba (uz SMTP2GO račun i probnu poruku), tema svijetla/tamna/automatska, pilule u jednom redu koji se vuče mišem. 500 testova (331 na kraju E8).54a3861 … 439621cu radu · uživo
Nexus E10: analiza i doradaAnaliza 15. 9. po pet kategorija (sigurnost, arhitektura, otpornost, nove mogućnosti, poboljšanja) dala je 25 prijedloga s mjerenjem na produkcijskoj snimci: prozor od 12 usporedbi trajao je 4 sata a ne dva dana, 35 od 36 zaglavljenih „od iste minute”, 9 mailova u 8 h za nepromijenjen skup, 99,3 % redaka usporedbi iz skrivenih parova. Vlasnik odabrao 11 (od toga tri veća — A2, A3, N2 — odgođena, S3 odbijen). Izgrađeno, sve na grani nexus-analiza-1: A1 usporedba samo na novom ulazu, S1 CSRF token, S2 display_errors ugašen + handler, S5 granice ulaza + nginx limit_req, P3 izvoz nosi sve retke i filtre, P1 zaglavljeno = 3 kruga + 2 h + obje strane („najmanje od”), P2 mail čeka izvor istine, N3 dnevnik promjena postavki, N5 nečitljivi artikli iz dućana, A5 sirovo tijelo izvještaja 7 dana. Ekran proširen na 96rem. Puni popis u tabu sync-probe.17020803 … 4ca51f24deployano 16. 9.
Nexus E11: prekidač kartice i modal na karticama dućanaVlasnik je 15. 9. navečer rastavio vlastitu ploču: na snimci od 20:15 stoji 35 zaglavljenih nalaza na 32 šifre — 4 na strani izvora istine, 16 na strani dućana, 15 nejasno. Crvena kartica broji onih 4, kartice dućana ispod nje svih 35 (HR 14, SI 21), obje su brojke točne i nijedna ne objašnjava drugu; k tome kvar iz njegova opisa (HR proda, master ne primi, SI prepiše master — P354: 300/295/300) po konsenzusu pada na stranu dućana i u crvenu karticu ni ne ulazi. Zato: kartica dobiva prekidač (alarm_kartica, uključena dok se ne isključi, u istom obrascu i iza iste brave kao način detekcije; isključena gasi točno taj natpis — oznake, filtar, dijagnostika i pošta ostaju), i svaka kartica dućana otvara svoj :target modal s popisom zaglavljenih te trgovine, stupcem „čija je strana” i podnaslovom koji se zbraja (K + L + O = N). Bez ijednog skripta: sloj-poveznica preko kartice, gumbi iznad njega. Demo baza dobiva treći krug pa se sve to napokon vidi i lokalno. 590 testova (bilo 580).774fcb11 … 144633fadeployano 16. 9.
Nexus E12: preglednost dijagnostikeTab Dijagnostika je narastao na pet tablica i oko 90 redaka bez ičega na vrhu što bi reklo je li to dobro ili loše — vlasnik, 16. 9.: „jako je dugačka i ne znam što je što”. Pitan što na njoj zapravo traži, označio je sva četiri posla (je li sve u redu · odakle ta brojka · gdje je kvar · da ovo nekome pošaljem), a ravan popis ne služi nijednom. Sad: pet pločica sa statusom (izvori, usporedbe, pošta, zaglavljeno, baza), svaka poveznica u svoj odsjek; naslovi su pitanja (Zaglavljeno razilaženje → Zašto pregled kaže 35 — po parovima); odsjeci su sklopivi <details> i onaj čija pločica nije zelena crta se otvoren, pa se kvar ne može kliknuti u stranu. Nijedan prag nije izmišljen — tišina je isti SILENCE_SECONDS koji crveni pilulice, a crveno/jantarno na zaglavljenom dijeli NexusKrivac kao i kartica; baza nema ocjenu jer prag nikad nije izmjeren. Ništa se ne gubi: 25 redaka pa „prikaži sve” na istoj stranici, ?razmotano=1 za Ctrl+F i ispis, i izvoz po odsjeku. Dnevnik pošte sa zadnjih 20 redaka na 7 dana s brojem po statusu i filtrom — odnos poslano/prigušeno je odgovor na „zašto mailovi ne dolaze svaka 4 sata”, jer je tihi prozor 24 h. 598 testova (bilo 590).ed5d6007 … fd24f899deployano 16. 9.
Nexus E13: dorada po pregleduVlasnik je pogledao E12 i našao šest stvari, dvije od njih kvarovi te iste runde. Pilulice statusa bile su obični linkovi pa je svaki klik dizao stranicu i bacao čitatelja prema dolje — sad nose uzivo, klasu čiji handler od 15. 9. mijenja <main> na mjestu. Kapa od 25 redaka bila je po paru, a parova je 17: oko 300 redaka u odsjeku koji se otvara sam, dakle točno ono zbog čega je tab i prepravljan — sad je 10 na cijeli odsjek, jedna tablica s parom kao stupcem. Uz to četiri stvari kojih nikad nije bilo: zašto je pošta prigušena (stajalo u alerts.note od prvog dana, nitko ga nije čitao); šifra vodi u dućan, par u tablicu neslaganja; tri :target panela koja objašnjavaju stupce, s primjerom retka i izričitim „što brojka nije” (usporedbi u prozoru se čita kao „danas”, krugovi kao sati); i popravak trake postotka, koja je imala tamnu podlogu na tamnocrvenoj kartici pa je nestajala. Hover više ne podiže karticu, a crvena je po izboru vlasnika jedva vidljiva ispuna. 603 testa (bilo 598).551e6c60deployano 16. 9.
Nexus E14: narudžbe po kalendaru, s grafomEkran narudžbi imao je jednu kontrolu — broj dana unatrag — a vlasnik je tražio ono što se stvarno kaže: danas, jučer, ovaj tjedan, uz pitanje koje je vrijedilo postaviti: „ne znam kako to voditi kad je sve u UTC-u a ja sad nisam u UTC-u”. Spremanje se ne miče — svaki zapis je i ostaje UTC; u zonu čitatelja seli se samo rub, pa „danas” znači od lokalne ponoći u Zagrebu do sada, izračunato ondje i predano upitu kao dva UTC trenutka. Nova NexusRaspon je ta računica i ništa drugo: čista funkcija ključa, trenutka i zone, bez baze i bez ekrana — jedino tako se teži dio da testirati. Testovi drže oba smjera pomicanja sata (25. 10. 2026. zatvoreni dan ima 25 sati, 29. 3. ima 23) i drže da „danas” i „zadnja 24 h” nisu isto pitanje, pa su i dva gumba a ne sinonimi. Uz to dva grafa iza tabova po vlasnikovoj ispravci — narudžbe složene po dućanu i zbroj količina — jer jedna narudžba od 40 komada i 40 narudžbi od jednog isti su stupac na jednom grafu i nisu ni blizu na drugom; kratki rasponi po satu, dulji po danu, prazna kanta dobiva vlas a ne rupu. Serija se gradi iz redaka koje ekran ionako dohvaća, bez ijednog novog upita. 618 testova (bilo 603).1f55c229 … a3aa3b41deployano 16. 9.
Nexus E15: pošta zove dućan imenom i čita se na telefonuVlasnik, 16. 9.: „mail templatovi i dalje koriste ono za master”. Jesu — lib/Alerts.php nije spominjao nikakvo imenovanje (grep dao nulu), pa je ispisivao jedino što je imao: woocommerce-master i woocommerce-master <-> woocommerce-hr, dok ekrani od 14. 9. kažu adresu. Sad ime() i imePara() idu istim redom kao ekrani, a i bilješka usporedbe (only on …) se prevodi. Dvije stvari je otkrio test koji tjera potpisan izvještaj kroz pravi handler: ulazni put nije prosljeđivao postavke, i poziv živi u afterStoredReport() a ne u handle(), pa je varijabla bila izvan dosega. Uz to poruka vodi blokom stanja — četiri pločice 2×2 u fluidnoj tablici i popis zaglavljenih po dućanu — jer mail ne smije pitati @media za pomoć (Outlook ga ignorira, Gmail baca blok u kojem živi). Redak nalaza više nije četiri stupca s nowrap nego dvije linije s presudom „čija strana”; gumb je pune širine; kapa 10 šifri po paru umjesto 50. Mjereno na tijelu poruke: 0 internih ključeva, 0 engleskih bilješki, 0 @media/flex/float, 0 klasa. 623 testa (bilo 618).0e4dfefc … d67e5a43deployano 16. 9.
Nexus E16: rasponi, oblačići, starost problema — i zašto se vlasniku stranica i dalje učitavalaČetiri stvari iz jednog pregleda. Oblačić: grafovi narudžbi i povijesti imali su samo <title>, koji se pojavi tek nakon sekunde mirovanja — sad crtaju isti oblačić koji vremenska crta ima od 14. 9., unutar SVG-a i bez novog skripta. Isti kalendarski rasponi na povijesti i vremenskoj crti, ali ne isti popis na oba: stupac povijesti je cijeli dan (comparison_daily sprema dan, ne trenutak) pa bi „zadnja 24 h” ondje bio jedan stupac, dok crta nudi svih sedam jer svaki njezin redak nosi trenutak. Usput je zatvoren rub koji nikad nije trebao: upit povijesti nije imao gornju granicu jer iznad „danas” nema što izuzeti — izmjereno, „prošli tjedan” sad crta 7.–13. 9. i nijedan stupac iz ovog tjedna. Povijest je dobila drugi tab po vlasnikovu izboru, jer je njegov sud o prvom grafu točan: taj broji nalaze po danu, pa dok se ništa ne popravlja jest i može biti samo niz jednakih stupaca — svojstvo pitanja, ne njegovih podataka. Drugi tab pita koliko dugo problem stoji. Starost se namjerno ne uzima iz NexusStuck: njegov prozor je dvanaest krugova, pa bi sve starije od dva dana zauvijek prijavljivalo ista dva dana i graf bi bio jedan stupac koji se ne miče. Računa se nad svih 30 dana koliko se comparison_items čuva, hodanjem unatrag po krugovima — problem koji je bio riješen pa se vratio mjeri se od povratka, što MIN(created_at) ne bi znao; preko 30 dana zadnja je kanta izrijekom donja granica. Četvrto je odgovor na dvaput prijavljeno „stranica se i dalje učitava kad filtriram poštu”: klik je izmjeren pravim mišem preko DevTools protokola — jedan fetch, nula zahtjeva za dokumentom, scroll nepomaknut — i na grani i u mainu. Kad se pinana skripta ne izvrši, sve tiho degradira i nigdje ne piše da je tako; sad piše. Dijagnostika čita data-skripta s korijena i imenuje uzrok (Cloudflareov Rocket Loader i Auto Minify prepisuju inline skriptu pa joj hash u CSP-u više ne odgovara). Obje su rečenice uvijek u HTML-u, stil bira koju pokazuje — nijedan novi skript da se prijavi da skripte nema. 632 testa (bilo 623).bcaf54ae … 3796a102spojeno 16. 9.
Nexus E17: pošta dobiva sekcije po dućanu, i prekidač koji vrijedi na oba mjestaVlasnik je pročitao pravu poruku i pitao ono što je trebalo pitati: „zašto nemamo sekcije za HR i SI … u stilu modala gdje je hero dio svakog od njih”. Zaslon je to imao dva dana, pošta je i dalje vodila jednom karticom o masteru i nizala ostalo kao ime para iznad dvostupčanih redaka. Sad svaki dućan ima svoju sekciju: naslov imenuje dućan pa izvor istine (dućan je ono što se ide popraviti), redak s brojkama kao u modalu, i kartica po nalazu sa svih pet stvari koje modal ima — šifra, naziv artikla, vrijednost u svakom dućanu, čija je strana, koliko dugo traje. Nijedan novi upit: podatke je pošta imala cijelo vrijeme i ispisivala dva od pet stupaca. Render je otkrio dva kvara, oba vidljiva i na vlasnikovoj snimci: kartica je pisala „4 ALAZA” jer je rezala strlen(broj) znakova s početka riječi koju plural() vraća bez broja (četveroznamenkasti broj dao bi „ZA”), a „Zaglavljeno po dućanu” brojalo je po paru — sa četiri izvora ima šest parova i dućan je na tri, pa se isti dućan ispisivao tri puta s tri broja (izmjereno: 1538, 35 i 20). Prekidač alarm_kartica sad vrijedi i za poštu. Od 11. 9. namjerno je stao na zaslonu — poruka se čita daleko od ploče i ne smije biti tiho uža od nje — ali to je prestalo vrijediti kad je poruka dobila vlastite pločice i sekcije: kartica je postala treće mjesto koje kaže isto, pa je vlasnik pitao čemu služi i odlučio da je prekidač gasi. Gasi jedan natpis i ništa više. 637 testova (bilo 632), a oba kvara i sama žica prekidača provjereni su tako da su vraćeni — testovi padnu.60727674 … (E17)spojeno 16. 9.

Što ostaje redoslijed je ovisnost · pregledano 18. 9. 2026.

Podjela od 17. 9. nije zatvorila nijednu stavku na ovom popisu, i nije ni mogla — rez repozitorija premješta kod, ne dokazuje sync. Promijenila je samo gdje se svaka stavka radi: WP strana je u flora-core/wp-venture-sync, Vendure plugin u flora-core/src/plugins/woocommerce-stock-sync, a nadzor je samostalan repozitorij sync-nexus sa svojim gateom. Jedna je stavka time postala teža: promjena koja dira obje strane sada je dva commita u dva repozitorija, a ništa ne provjerava da su otišla zajedno osim sha-lock.json, koji to bilježi ali ne provodi.

Tvoji koraci, obavljeni 10. 9. 4

  • SKU 617 na −160Postavljen na 0 kroz wp-cli; ujedno prvi red u dnevniku mastera.
  • Deploy WP plugina na dev-masterAnsible, failed=0; aktivacija wp-cli-jem.
  • Zajednička tajna na obje straneUpisana; „Veza radi · v0.1.0 · full”.
  • Pozitivni prolaz993 upareno → 5 primijenjeno → 988 primijenjeno → razlika 0.

Kod, sljedeće 2

  • Hookovi na produkcijiPut child → master (WooMultistore) tek se promatra na produkciji; mreža updated_post_meta ga hvata i ako ide mimo set_stock. Varijabilni proizvodi s zalihom na roditelju nemaju pisca po SKU-u varijacije (na devu 0 varijacija).
  • Plan 02: Vendure → Woo ručnoGotovo i dokazano uživo; brojke u specu „Faza 2 — izmjereno”. Neizmjereno: gumb „Apply this package” u WP adminu, read-only 403 kroz Dashboard, sharedStock (dev nema varijacija).
  • Planovi 03 i 04: automatikaIzgrađeno i dokazano uživo 11. 9. (test dolje, brojke u specu pod „Faza 3 — izmjereno”). Neizmjereno: dnevno usklađivanje koje se samo okine u svom satu, sudar kucanja i rasporeda, master koji zašuti usred paketa. Sljedeće je potprojekt 2 (nexus).

Potprojekt 2: nadzor uživo

  • NexusPrijem izvještaja, SQLite, usporedba svakog izvora sa svakim (parovi izvora istine se prikazuju), jedan mail preko SMTP2GO API-ja po skupu neslaganja, dashboard pod lozinkom, JSON API. Radi na kontrolnom serveru od 14. 9.
  • Skripte na WordPressimaMaster i child svaka 4 sata šalju potpuno stanje, promjene i narudžbe. Kod djece pokret koji je skinula narudžba nosi minutu te narudžbe (_reduced_stock), a razlika snimaka javlja samo ostatak.
  • VendureIsti izvještaj svaka 4 sata, po UTC-u, i zasebno po označenom kanalu — sve kroz jednu tajnu.
  • PromptoviOsam koraka (E1–E8), svaki s promptom za izvršavanje i promptom za provjeru, plus šest promptova za kasnije promjene — sve u tabu sync-probe.

Poznato, zapisano, ne blokira 3

  • Jedna API instancaPlan razlike živi u memoriji procesa; s više API instanca „Povuci razliku” i „Primijeni” moraju pogoditi istu. Plan 02 seli plan na red izvršavanja ako to zatreba.
  • Jedna lokacija zaliheViše od jedne → diff to prijavi i apply odbije, namjerno.
  • Prvi diff je backfillSvaki upareni SKU nosi backordersChange prvi put; brojač ih vodi odvojeno od promjena količine.

Test faze 1 Woo → Vendure, ručno · redom, kvačice se pamte u pregledniku

Deploy ide s tvog stroja (Ansible, ključ ~/.ssh/rasadnik). Ništa od ovoga ne dira produkciju: playbook zadano cilja samo dev_master, a Vendure je tvoj lokalni na 3000. Koraci označeni ti traže tvoju ruku; ostalo može sesija. Cijeli niz je prošao 10. 9. 2026. (koraci 0–7 i 9; korak 8 nije ponovljen kroz Dashboard); ostaje kao recept za ponavljanje nakon svake promjene plugina. Faza 2 je prošla isti dan navečer (dolje).

0

Faza 0: brojke s dev-mastera ti izmjereno 10. 9.

Kopiraj flora-samples/stock-checker/scripts/wvss-probe.php na dev-master i pokreni. Datoteku vrati u korijen repoa (ignorirana je u gitu).

wp --path=/var/www/dev-master.rasadnik-milic.hr eval-file wvss-probe.php > probe-master.json

Tracer hookova: kopiraj wvss-hook-trace.php na master, promijeni zalihu jednog proizvoda na HR childu kroz wp-admin, pričekaj da WooMultistore progura, pročitaj log, pa obriši i mu-plugin i log.

mkdir -p /var/www/dev-master.rasadnik-milic.hr/wp-content/mu-plugins && cp wvss-hook-trace.php /var/www/dev-master.rasadnik-milic.hr/wp-content/mu-plugins/
wp --path=/var/www/dev-master.rasadnik-milic.hr eval 'echo file_get_contents(WP_CONTENT_DIR."/wvss-hook-trace.log");'
rm /var/www/dev-master.rasadnik-milic.hr/wp-content/mu-plugins/wvss-hook-trace.php /var/www/dev-master.rasadnik-milic.hr/wp-content/wvss-hook-trace.log

Presjek SKU-ova, iz korijena repoa, s dev bazom gore:

docker compose -f compose.dev.yml exec -T postgres psql -U plant_garden -d plant_garden -Atc "select sku from product_variant where \"deletedAt\" is null and sku <> ''" > vendure-skus.txt
node flora-samples/stock-checker/scripts/compare-skus.mjs probe-master.json vendure-skus.txt
Zalijepi stats blok, cijeli log i izlaz usporedbe. Dva broja odlučuju dodatni posao: duplicate_skus i manage_stock_no_qty. Prazan log nakon stvarne promjene zalihe je nalaz, ne kvar.
1

Migracija i restart tvog Vendurea ti prošlo 10. 9.

Plugin je u main; tvoja dev baza još nema tablice. Ugasi :3000, pokreni migraciju, digni ponovno.

cd ecommerce && npx vendure migrate --run && cd .. && npm run dev
Log kaže Successfully ran 1 migrations (AddWoocommerceStockSync1790000087000). U Dashboardu pod Katalog stoji „Zalihe: WooCommerce” s pet blokova.
2

Deploy WP plugina na dev-master ti prošlo 10. 9.

Prvo pregled bez kopiranja, pa deploy. Playbook radi backup postojeće mape (ako je ima) u backups/, nikad ne aktivira.

cd flora-core/wp-venture-sync && ansible-playbook ansible/deploy.yml --check --diff
cd flora-core/wp-venture-sync && ansible-playbook ansible/deploy.yml

Aktivacija i prva provjera, na masteru:

wp --path=/var/www/dev-master.rasadnik-milic.hr plugin activate wp-venture-sync
wp --path=/var/www/dev-master.rasadnik-milic.hr vendure-stock status
status vraća ledgerTable: true, secretSet: false, ledgerRows: 0. Produkcijski master se dira samo s -e target_hosts=production_master, i ne sad.
3

Zajednička tajna na obje strane ti prošlo 10. 9.

openssl rand -hex 32

Isti niz upiši u WooCommerce → Vendure Stock Sync (Shared secret; Vendure URL može ostati prazan do plana 03) i u Katalog → Zalihe: WooCommerce → Postavke (Adresa WooCommercea = https://dev-master.rasadnik-milic.hr, Zajednička tajna). Spremi.

„Provjeri vezu” vraća Connected · v0.1.0 · full. Ako vrati 401, tajna nije ista; ako vrati unreachable, adresa ili HTTPS.
4

Snapshot iz konzole, prije Dashboarda sesija ili ti prošlo 10. 9.

Da razdvojiš mrežu od logike: potpisani GET na master, iz korijena repoa, s tajnom u varijabli (ne u povijesti shella).

read -s SECRET; ts=$(date +%s); route=/vendure-stock/v1/snapshot
sig="sha256=$(printf '%s\n%s\n%s\n%s' GET "$route" "$ts" '' | openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.* //')"
curl -s -H "X-WVSS-Timestamp: $ts" -H "X-WVSS-Signature: $sig" "https://dev-master.rasadnik-milic.hr/wp-json$route" | head -c 600
JSON s source, items (≈ broj SKU-ova iz faze 0), problems, truncated: false. Pola minute je normalno za cijeli katalog; Vendure čeka do dvije.
5

Povuci razliku ti prošlo 10. 9.

Na stranici „Zalihe: WooCommerce” klikni Povuci razliku. Ništa se ne piše; vidiš sažetak i tablicu po šifri.

Sažetak: podudaranja ≈ presjek iz faze 0, „samo u WooCommerceu” i „samo u Vendureu” ≈ brojevi iz compare-skus. Prvi put su gotovo sve promjene „samo backorders” (backfill) — to je očekivano. Nema upozorenja o nepotpunom snapshotu.
Ako je razlika u količinama velika i neočekivana, stani. Provjeri je li master vodio zalihu (manage_stock) i je li Vendure na točnom stanju od uvoza. Primjena piše zalihu.
6

Primijeni pet šifri, provjeri u bazi ti prošlo 10. 9.

Odznači sve, označi pet šifri s razlikom u količini, zapiši njihova stanja prije:

docker compose -f compose.dev.yml exec -T postgres psql -U plant_garden -d plant_garden -c "select v.sku, sl.\"stockOnHand\", v.\"trackInventory\", v.\"customFieldsWoobackorders\" from product_variant v join stock_level sl on sl.\"productVariantId\"=v.id where v.sku in ('SKU1','SKU2','SKU3','SKU4','SKU5')"

Klikni Primijeni odabrano, pa isti upit.

Razlika u stockOnHand je točno delta iz tablice; dnevnik ima red primjena · ručno s Primijenjeno 5 i Greške 0; stock_movement ima 5 novih ADJUSTMENT redaka. Ponovni „Povuci razliku” tih pet više ne pokazuje.
7

Primijeni sve, do nule ti prošlo 10. 9.

„Odaberi sve promjene” → „Primijeni odabrano”. Backfill backordersa ide u paketima po 100.

Ponovni „Povuci razliku” → „Ništa se ne razlikuje.” na svim uparenim šiframa. Ono što ostaje su samo redovi „samo u WooCommerceu” / „samo u Vendureu” / duplikati, koje sync namjerno ne dira.
8

Negativan put, da znaš kako izgleda kvar sesija ili ti nije ponovljeno

Promijeni tajnu na jednoj strani, klikni „Provjeri vezu” i „Povuci razliku”, pa vrati tajnu.

„Provjeri vezu” pada s 401 i ne stvara red u dnevniku; „Povuci razliku” stvara red s greškom, traka „Za pregled” (do faze 3 se zvala „Neuspjela izvršavanja”) ga pokazuje, „Označi viđeno” ga makne. Ništa nije upisano u zalihu.
9

Zapiši brojke sesija zapisano 10. 9.

Brojke iz koraka 0, 5, 6 i 7 idu u spec pod „Faza 0 — izmjereno” i „Faza 1 — izmjereno”, u flora-atlas/stanje/plugin-inventory.md (Built → Live-verified) i u roadmap 57. Tek onda Task 6 i plan 02.

Test faze 2 Vendure → Woo, ručno · redom, kvačice se pamte u pregledniku

Razlika je nakon faze 1 na nuli, pa se najprije napravi razlika na Vendure strani. Deploy WP plugina 0.2.0 ide s tvog stroja (Ansible), Vendure je tvoj lokalni na 3000 i traži restart jer API ne gleda promjene koda. Ništa ne dira produkciju.

1

Deploy WP plugina 0.2.0 na dev-master ti prošlo 10. 9.

Ista naredba kao u fazi 1; playbook zadano cilja samo dev_master. Nakon toga provjeri verziju i da nema parkiranog paketa.

cd flora-core/wp-venture-sync && ansible-playbook ansible/deploy.yml
ssh -i ~/.ssh/rasadnik paveljame@178.105.119.208 'wp --path=/var/www/dev-master.rasadnik-milic.hr plugin list --name=wp-venture-sync --fields=name,status,version; wp --path=/var/www/dev-master.rasadnik-milic.hr vendure-stock status'
version 0.2.0, status active; u statusu pendingApply: null i ledgerEngine: "InnoDB". Ako engine nije InnoDB, stani: transakcija oko primjene tada nije stvarna (vidi README WP plugina).
2

Restart Vendurea i pogled na stranicu ti prošlo 10. 9.

Kod je u main; nema nove migracije. Ugasi i ponovno digni npm run dev, pa otvori Katalog → „Zalihe: WooCommerce” i klikni „Provjeri vezu”.

Veza radi: v0.2.0 · full, novi podatak „Pogon dnevnika: InnoDB”. U bloku razlike, ispod „Primijeni odabrano”, drugi red gumba: „Pregled slanja na WooCommerce” i „Pošalji na WooCommerce (0)”.
3

Napravi razliku u Vendureu ti prošlo 10. 9.

U Dashboardu (Katalog → proizvod) postavi zalihu varijante SKU 621 s 200 na 190 i SKU 625 s 200 na 205. Pa „Povuci razliku”.

Dva retka: 621 razlika +10, 625 razlika −5 (to je količina koju bi Vendure morao dodati da se složi s masterom), Promjene 2, upareno 993.
4

Pregled slanja (dry run) ti prošlo 10. 9.

Odaberi oba retka i klikni „Pregled slanja na WooCommerce”. Onda na masteru pogledaj što je parkirano.

ssh -i ~/.ssh/rasadnik paveljame@178.105.119.208 'wp --path=/var/www/dev-master.rasadnik-milic.hr vendure-stock status'
Blok „Pregled slanja (2 odabrane šifre)”: 621 200 → 190 i 625 200 → 205, status „bi se primijenilo”. Na masteru pendingApply.movements: 2. U WP adminu (WooCommerce → Vendure Stock Sync) odjeljak „Pending package from Vendure” s ta dva retka i gumbima „Apply this package” / „Discard”. Kvačice i „Pošalji (2)” na Vendure strani ostaju.
5

Potvrda na WP strani ti preskočeno: push je stigao prije

U WP adminu klikni „Apply this package” (potvrdi dijalog). Pa provjeri zalihu i dnevnik na masteru, i „Povuci razliku” u Vendureu.

ssh -i ~/.ssh/rasadnik paveljame@178.105.119.208 'P=/var/www/dev-master.rasadnik-milic.hr; wp --path=$P eval '"'"'foreach (["621","625"] as $s) { $p = wc_get_product(wc_get_product_id_by_sku($s)); echo "$s -> " . $p->get_stock_quantity() . "\n"; }'"'"'; wp --path=$P vendure-stock status'
Obavijest „Applied 2 movement(s), 0 failed”. Zaliha 621 = 190, 625 = 205. U tail dva nova retka: origin vendure, note apply:vendure push:manual, delta −10 / +5, qtyAfter 190 / 205, pendingApply: null. U Vendureu „Povuci razliku” → „Ništa se ne razlikuje.” (promjene 0). Napomena: Vendure o ovoj primjeni ne zna (potvrda je bila na WP strani), pa u Izvršavanjima nema retka „slanje” — to je očekivano.
6

Izravno slanje + idempotencija po ref-u ti prošlo 10. 9.

U Vendureu vrati 621 na 200, „Povuci razliku” (621 razlika −10), odaberi 621 i klikni „Pošalji na WooCommerce (1)” bez pregleda. Zatim isti pokret pošalji ponovno wp-cli-jem s istim ref-om (uzmi ga iz Vendure baze).

docker compose -f compose.dev.yml exec -T postgres psql -U plant_garden -d plant_garden -c 'select ref, sku, delta, "qtyAfter", origin, "sentAt", note from woo_stock_movement order by id desc limit 3'
REF=ZALIJEPI-REF-OVDJE; ssh -i ~/.ssh/rasadnik paveljame@178.105.119.208 "P=/var/www/dev-master.rasadnik-milic.hr; printf '%s' '{\"source\":\"replay\",\"generatedAt\":\"'\$(date -u +%Y-%m-%dT%H:%M:%SZ)'\",\"movements\":[{\"ref\":\"$REF\",\"sku\":\"621\",\"delta\":10,\"qtyAfter\":200,\"stockStatus\":null,\"backorders\":null,\"origin\":\"vendure\",\"at\":\"2026-09-10T12:00:00Z\",\"note\":\"replay\"}]}' > /tmp/replay.json; wp --path=\$P vendure-stock apply --file=/tmp/replay.json"
Blok „Poslano (1 šifra)”: 621 primijenjeno 190 → 200; u Izvršavanjima red slanje · ručno, Primijenjeno 1, Greške 0; kvačice i tablica se prazne (plan je potrošen). U Vendure bazi redak sa sentAt popunjenim. Ponovljeni paket: status: duplicate, counts.duplicate 1, zaliha ostaje 200, dnevnik bez novog retka.
7

Nepoznata šifra, read-only i stari paket ti prošlo 10. 9.

Tri odbijanja koja moraju biti čitljiva. (a) wp-cli paket sa šifrom koje nema. (b) U WP adminu prebaci Mode na read-only, u Vendureu „Pregled slanja” za bilo koji redak, pa vrati na full. (c) Paket star više od 15 minuta.

ssh -i ~/.ssh/rasadnik paveljame@178.105.119.208 "P=/var/www/dev-master.rasadnik-milic.hr; printf '%s' '{\"source\":\"probe\",\"generatedAt\":\"'\$(date -u +%Y-%m-%dT%H:%M:%SZ)'\",\"movements\":[{\"ref\":\"'\$(cat /proc/sys/kernel/random/uuid)'\",\"sku\":\"NEMA-OVOG\",\"delta\":-1,\"qtyAfter\":0,\"stockStatus\":null,\"backorders\":null,\"origin\":\"vendure\",\"at\":\"2026-09-10T12:00:00Z\",\"note\":\"probe\"}]}' > /tmp/unknown.json; wp --path=\$P vendure-stock apply --file=/tmp/unknown.json --dry-run; printf '%s' '{\"source\":\"probe\",\"generatedAt\":\"2026-09-10T00:00:00Z\",\"movements\":[]}' > /tmp/stale.json; wp --path=\$P vendure-stock apply --file=/tmp/stale.json --dry-run; echo \"exit=\$?\""
(a) status: unknownSku, ništa upisano. (b) Vendure pokazuje grešku s riječima 403 … read-only, bez retka u Izvršavanjima (pregled se ne bilježi). (c) wp-cli izlazi s greškom „package is older than 15 minutes”, exit=1.
8

Hookovi šute dok apply piše ti prošlo 10. 9.

Za svaki primijenjeni pokret na masteru smije postojati točno jedan redak dnevnika. Ispiši sve retke od koraka 5 nadalje.

ssh -i ~/.ssh/rasadnik paveljame@178.105.119.208 'wp --path=/var/www/dev-master.rasadnik-milic.hr vendure-stock movements --since=1 --limit=50'
Za 621 i 625 samo retci origin vendure s ref-om iz Vendurea (tri ukupno: −10, +5, +10); nijedan dodatni origin woocommerce redak za iste šifre u istoj sekundi. Ako se pojavi dupli redak, to je nalaz: mreža hookova je uhvatila put kojim je prošao wc_update_product_stock.
9

Zapiši brojke sesija zapisano 10. 9.

Brojke iz koraka 1–8 idu u spec pod „Faza 2 — izmjereno”, u README oba plugina, flora-atlas/stanje/plugin-inventory.md (Live-verified, faze 1–2), roadmap 57 i ovu stranicu. Tek onda plan 03.

Test faze 3 automatika · SKU 1452 · redom, kvačice i izlazi se pamte u pregledniku

Polazno stanje izmjereno 11. 9. u 9:40: obje strane su na 65 za SKU 1452 (proizvod 174152 na masteru), dnevnik mastera ima 16 redaka, unsentMovements 0, lastAcknowledgedId 16. Ako se brojke u međuvremenu pomaknu, to nije problem: svaki korak kaže što gleda, ne koji je broj. Cijeli niz je prošao 11. 9. 2026. (koraci 1–9; brojke su u specu pod „Faza 3 — izmjereno”), pa ostaje kao recept za ponavljanje nakon svake promjene plugina. Radi korake redom i u polje ispod svakog zalijepi što si vidio, pa na kraju klikni „Kopiraj izvještaj” i pošalji ga u razgovor.

1

Polazno stanje i način ti prošlo 11. 9.

U Dashboardu, Katalog → „Zalihe: WooCommerce”, blok Automatika: način mora biti „Odmah i po rasporedu”, „Dnevno usklađivanje” uključeno. Zapiši i obje zalihe.

docker compose -f compose.dev.yml exec -T postgres psql -U plant_garden -d plant_garden -c "select v.sku, sum(sl.\"stockOnHand\") as vendure from product_variant v join stock_level sl on sl.\"productVariantId\"=v.id where v.sku='1452' group by v.sku"
ssh -i ~/.ssh/rasadnik paveljame@178.105.119.208 'P=/var/www/dev-master.rasadnik-milic.hr; wp --path=$P eval "echo \"master: \", wc_get_product(174152)->get_stock_quantity(), \"\n\";"; wp --path=$P vendure-stock status'
Obje zalihe jednake. Pločice: „Automatika: Odmah i po rasporedu”, „Dnevno usklađivanje: da”, „Neposlano” 0 ili mali broj. Na masteru mode full, ledgerEngine InnoDB, unsentMovements 0. Ako se zalihe razlikuju, prvo „Povuci razliku” i poravnaj ih (primijeni ili pošalji), pa tek onda dalje.
2

Odmah: promjena u Vendureu stiže na master za sekundu ti prošlo 11. 9.

U Dashboardu smanji zalihu SKU 1452 za 3 (npr. 65 → 62) i ne diraj ništa drugo. Pričekaj 10 sekundi, pa pokreni:

ssh -i ~/.ssh/rasadnik paveljame@178.105.119.208 'P=/var/www/dev-master.rasadnik-milic.hr; wp --path=$P eval "echo \"master: \", wc_get_product(174152)->get_stock_quantity(), \"\n\";"; wp --path=$P vendure-stock movements --since=15 --limit=10'
U Izvršavanjima novi red slanje · događaj, Primijenjeno 1, Greške 0. Master na 62, a u dnevniku mastera redak origin vendure, delta −3, after 62, bilješka apply:vendure event:ADJUSTMENT. Nijedan dodatni redak origin woocommerce za 1452 (hookovi šute dok sinkronizacija piše).
3

Prigušenje: dvije promjene u pet sekundi daju jedno slanje ti prošlo 11. 9.

Brzo, jedna za drugom (unutar 5 sekundi): 62 → 61, pa 61 → 60. Pričekaj 15 sekundi.

ssh -i ~/.ssh/rasadnik paveljame@178.105.119.208 'P=/var/www/dev-master.rasadnik-milic.hr; wp --path=$P eval "echo \"master: \", wc_get_product(174152)->get_stock_quantity(), \"\n\";"; wp --path=$P vendure-stock movements --since=15 --limit=10'
Master na 60. U Izvršavanjima jedan red slanje · događaj s Primijenjeno 2 (a ne dva reda s po jednim), jer je drugo slanje prigušeno i oba pokreta odlaze zajedno. U dnevniku mastera dva retka, svaki sa svojim ref-om, delte −1 i −1. Ako u međuvremenu odradi i raspored, moguća su dva reda; tada gledaj samo da je master na 60 i da su oba pokreta stigla.
4

Kucanje: promjena na masteru stiže u Vendure ti prošlo 11. 9.

Sad obrnuti smjer. Postavi na masteru 1452 na 75 i pričekaj 15 sekundi.

ssh -i ~/.ssh/rasadnik paveljame@178.105.119.208 'P=/var/www/dev-master.rasadnik-milic.hr; wp --path=$P wc product update 174152 --stock_quantity=75 --user=1 --porcelain; sleep 15; wp --path=$P vendure-stock status | grep -E "unsentMovements|lastAcknowledgedId"'
docker compose -f compose.dev.yml exec -T postgres psql -U plant_garden -d plant_garden -c "select v.sku, sum(sl.\"stockOnHand\") as vendure from product_variant v join stock_level sl on sl.\"productVariantId\"=v.id where v.sku='1452' group by v.sku"
U Izvršavanjima red povlačenje · poticaj (ili · raspored ako je kucanje palo u prigušenje), Primijenjeno 1. Vendure na 75. Na masteru unsentMovements 0 i lastAcknowledgedId jednak id-u novog retka: to je potvrda da je Vendure javio dokle je pročitao. Pločica „Kursor” u bloku Automatika pomaknuta.
5

Istodobnost: obje strane u istoj minuti ti prošlo 11. 9.

Ovo je ključni test. Zapiši polaznu zalihu Z (npr. 75). U razmaku od nekoliko sekundi: u Dashboardu smanji 1452 za 1 (Z → Z−1), a odmah zatim na masteru pokreni naredbu ispod koja mu oduzima 2 od njegove trenutne razine. Pričekaj dva prolaza (oko minute).

ssh -i ~/.ssh/rasadnik paveljame@178.105.119.208 'P=/var/www/dev-master.rasadnik-milic.hr; wp --path=$P eval "\$p=wc_get_product(174152); wc_update_product_stock(\$p, 2, \"decrease\"); echo \"master sada: \", wc_get_product(174152)->get_stock_quantity(), \"\n\";"'
sleep 70; docker compose -f compose.dev.yml exec -T postgres psql -U plant_garden -d plant_garden -c "select v.sku, sum(sl.\"stockOnHand\") as vendure from product_variant v join stock_level sl on sl.\"productVariantId\"=v.id where v.sku='1452' group by v.sku"; ssh -i ~/.ssh/rasadnik paveljame@178.105.119.208 'wp --path=/var/www/dev-master.rasadnik-milic.hr eval "echo \"master: \", wc_get_product(174152)->get_stock_quantity(), \"\n\";"'
Obje strane na Z − 3. Ako je jedna na Z − 2, jedna je promjena negdje prepisana: zalijepi obje brojke i zadnjih pet redaka oba dnevnika, to je nalaz.
6

Prava prodaja i otkaz ti prošlo 11. 9.

U storefrontu kupi 1 komad SKU 1452 i narudžbu dovedi do isporuke. Dok je samo rezervirana, pokret je ALLOCATION koji se namjerno ne šalje. Zatim tu narudžbu otkaži.

docker compose -f compose.dev.yml exec -T postgres psql -U plant_garden -d plant_garden -c "select id, type, quantity from stock_movement order by id desc limit 5"
docker compose -f compose.dev.yml exec -T postgres psql -U plant_garden -d plant_garden -c "select id, left(ref,8) ref, sku, delta, \"qtyAfter\", \"sentAt\" is not null as sent, note from woo_stock_movement order by id desc limit 6"
Nakon isporuke: redak event:SALE s delta −1, poslan, i master niži za 1. Nakon otkaza: redak event:CANCELLATION s delta +1, poslan, i master natrag gore. U stock_movement vidiš i ALLOCATION koji nije proizveo ništa prema masteru: to je ispravno, rezervacija nije pokret zalihe.
7

Usklađivanje prijavljuje, ne ispravlja ti prošlo 11. 9.

Upiši zalihu na masteru mimo dnevnika, pa u bloku Automatika klikni „Uskladi sad”.

ssh -i ~/.ssh/rasadnik paveljame@178.105.119.208 'P=/var/www/dev-master.rasadnik-milic.hr; PFX=$(wp --path=$P db prefix); wp --path=$P db query "UPDATE ${PFX}postmeta SET meta_value=999 WHERE post_id=174152 AND meta_key=\"_stock\""; wp --path=$P db query "UPDATE ${PFX}wc_product_meta_lookup SET stock_quantity=999 WHERE product_id=174152"; wp --path=$P cache flush; echo "master sada 999, dnevnik nije dirnut"'
Red usklađivanje · ručno s Promjene 1 u sažetku. Na traci „Za pregled” stavka „katalozi se razlikuju” u neutralnoj boji, ne crvenoj, i miče se gumbom „Označi viđeno”. Nijedna zaliha nije promijenjena. Ispravak radiš sam: „Povuci razliku” → odaberi 1452 → „Primijeni odabrano” (Vendure prihvaća 999) ili „Pošalji na WooCommerce” (master se vraća na Vendureov broj). Odaberi drugo i vrati stanje.
8

Prekidači: dnevno usklađivanje i način „Isključeno” ti prošlo 11. 9.

Prvo isključi kvačicu „Dnevno usklađivanje” i spremi: pločica mora reći ne, a gumb „Uskladi sad” i dalje raditi. Vrati je na uključeno.

Zatim postavi način na „Isključeno”, spremi, i u Dashboardu promijeni zalihu 1452 za −1. Pričekaj minutu.

Dok je isključeno: nema novog reda slanje, master se ne mijenja, a pločica „Neposlano” se poveća za 1 (dnevnik se i dalje puni). Kad vratiš način na „Odmah i po rasporedu” i spremiš, ta promjena odlazi na master u sekundama i „Neposlano” pada natrag. Uz „Isključeno” i „Neposlano” veće od nule, ispod izbornika mora pisati upozorenje koliko promjena čeka.
9

Mail za greške, jednom po vrsti na sat ti prošlo 11. 9.

U postavkama provjeri da je upisan e-mail za greške. U WP adminu (WooCommerce → Vendure Stock Sync) privremeno pokvari zajedničku tajnu (dodaj slovo) i spremi. Pričekaj dva prolaza, pa vrati tajnu.

Redovi povlačenje s greškom 401 na svakom prolazu i traka „Za pregled” ih pokazuje, ali na tvoju adresu stigne jedan mail po vrsti u sat vremena („Stock sync: pull failed”), ne jedan po prolazu. Ako je nešto bilo dugovano, past će i slanje, pa stiže i drugi mail („push”) — i dalje jedan po vrsti. Nakon vraćanja tajne prvi sljedeći prolaz je čist.
10

Zapiši brojke sesija zapisano 11. 9.

Kad pošalješ izvještaj, brojke idu u spec pod „Faza 3 — izmjereno”, u README oba plugina, u flora-atlas/stanje/plugin-inventory.md (Live-verified, faze 1–3), roadmap 57 i na ovu stranicu. Tek onda potprojekt 2 (nexus).

Izvještaj za sesiju

Slaže se sam iz kvačica i polja iznad. Kopiraj i zalijepi u razgovor — iz njega se vidi točno gdje je stalo, bez dodatnih pitanja.