# ADR 0002: Környezet-infrastruktúra (BaseUrl, szerver-adatkörnyezet, env-badge) keretbe emelése
## Status
Accepted (2026-07-15) — végrehajtás lépésenként, per-lépés review-val; 1. lépés (BaseUrl → AcEnv) kész.
## Context
- **Kiváltó incidens:** a fbHub és a loggerHub URL-je két külön forrásból élt (`AcHubConnection:Url` appsettings vs. `FruitBankConstClient.BaseUrl` kód-konstans) → a kliens a helyi nop helyett a PROD `shop.fruitbank.hu`-ra töltötte fel a logokat, órákig félrevitt nyomozással. A gyökér: az URL-infrastruktúra a fogyasztói const-osztályon, több igazságforrással.
- **Frissen épült env-lánc (2026-07):** szerver-adatkörnyezet (PROD/DEV) kliens-oldali ismerete — first-connect kapu (await, fail-closed), szerver push-on-connect announce (env-váltás riasztással), `[PROD]/[DEV]` title-badge, MSTest prod-guard. Élesben igazolva (DEV→PROD DB-váltás restarttal: announce + riasztás + title-váltás működött). **Mindez FruitBank-oldalon született, pedig app-agnosztikus.**
- **Redundancia:** ugyanaz a fogalom kétszer — szerver `FruitBankConst.SetIsProd` + kliens `FruitBankConstClient.IsProd`; a teszt-guard, badge, jövőbeli destruktív-védelem mind külön-külön kötné be.
- **„IsProd" tengely-probléma:** három független „prod"-tengely létezik — (1) build-konfiguráció (Debug/Release), (2) adat-környezet (melyik DB-katalógus), (3) hosting env (ASPNETCORE_ENVIRONMENT). A Debug-build + PROD-DB + Development-hosting kombináció élő teszttel bizonyított — egyetlen `IsProd` név ezeket összemosná.
- **Időzítés:** a közös nagyker-réteg terv (VeggieVoyage) előtt; a ház-mintában van közvetlen precedens: **GridLayout-roaming** (framework-fenntartott tagek `AcSignalRTags` 9001x + framework-kliens-logika + fogyasztói endpoint-implementáció).
- **Scope IN:** AcEnv állapotok (BaseUrl, IsProdDb); framework-tagek; kliens-base env-gépezet; hub-base announce; Blazor title-réteg; FruitBank-vékonyítás delegátokkal. **Scope OUT:** hosting-env tengely (később, read-only diagnosztika); `IsProdBuild` (független bónusz); `AcEnv.SetConfiguration` config-egységesítés (ACCORE-LOG-T-N2D8 — szinergia, nem feltétel).
## Decision
**A környezet-infrastruktúra a keretbe kerül** — a fogyasztó csak opt-inel, endpointot implementál és domain-konstansokat tart:
1. **Egy-forrás állapotok az `AcEnv`-en** (AyCode.Core): `BaseUrl` ✅ (kész) és `IsProdDb` (+`Set`/`Hydrated`/`Changed` esemény) — a szervernél a DB-katalógus-besorolás tölti (fail-closed), a kliens tükrözi (kapu + announce). A megosztás legitim: a kliensnek nincs saját DB-je, az ő adatkörnyezete a szerveré.
2. **Tengelyenkénti nevezés:** `IsProdDb` (adat-tengely, wire-en szinkronizált) ≠ `IsProdBuild` (build-tengely, processenkénti, SOHA nem megy wire-ra) ≠ hosting env. Nincs csupasz `IsProd` a kereten.
3. **Framework-fenntartott SignalR-tagek** (`AcSignalRTags`, a GridLayout 9001x mintájára): query 90020, announce 90021 — a fogyasztói 210/211 kivezetve (wire-váltás: kliens+szerver együtt-deploy).
4. **Kliens-gépezet az `AcSignalRClientBase`-be:** first-connect kapu (await + dedup: pontosan 1 wire-hívás), `EnsureEnvironmentAsync`, fail-closed nyers query (timeout ≠ `default(bool)`!), announce-elfogás a dispatch-ben (a fogyasztói `MessageReceived` nem is látja). **Opt-in**: `EnvironmentFeatureEnabled => false` default — más fogyasztók (pl. TourIAm) szervere nem ismeri az endpointot, default-on fail-closed törést okozna.
5. **Hub-base announce:** push-on-connect az `AcEnv.IsProdDb`-ből (best-effort, a kapcsolatot nem dönti).
6. **Blazor title-réteg** (AyCode.Blazor.Components): `IAcAppTitleService` + browser-implementáció + ``; MAUI-implementáció fogyasztói marad (a keretnek nincs MAUI-assemblyje).
7. **Delegát-minta a fogyasztón:** a `FruitBankConstClient` érintett tagjai átlátszó delegátként maradnak (`BaseUrl` ✅) — nulla törés, a hívóhelyek változatlanok, a felfedezhetőség megmarad (static class nem örökölhet → tag-migráció + delegát, nem literál öröklés).
8. **Fogyasztónál marad:** domain-konstansok (táblanevek, DomainDescription, email-sablonok), a katalógus-besorolás hívási helye, a query-endpoint implementáció (framework-taggel, GridLayout-minta), MAUI title-impl, AppName.
## Consequences
**Positive:**
- Minden jövőbeli app (VeggieVoyage, közös nagyker-réteg) **ingyen** kapja a teljes env-láncot: kapu, announce+riasztás, badge, teszt-guard.
- A szerver/kliens redundancia megszűnik (egy állapot, egy esemény, sok olvasó); a BaseUrl-divergencia osztálya strukturálisan kizárt.
- Egységes fail-fast (hidratálatlan olvasás dob) + fail-closed (nem igazolható env = tiltás) szemantika keret-szinten.
**Negative:**
- Wire-tag átszámozás (210/211 → 9002x): kliens + szerver **együtt-deploy** kötelező a váltáskor.
- `AcEnv` statikus állapot: process-szintű — párhuzamos teszt-kollekciók osztoznak rajta (a jelen MSTest-felállásban nem gond, de tudni kell róla).
- Az opt-in flag elfelejtése = a feature **némán off** — a keret-doksiban kiemelten dokumentálandó.
- A delegát-réteg fegyelmet kér: új infra-tag a KERETRE kerüljön, ne a fogyasztói const-ra.
**Follow-ups required:**
- 2. lépés: `IsProdDb` → `AcEnv` (FruitBank-oldali `IsProd`-tagok delegátként).
- 3. lépés: `AcSignalRTags` 90020/90021 + kliens-base gépezet + opt-in flag + teszt-guard helper a keretbe.
- 4. lépés: hub-base announce.
- 5. lépés: Blazor title-réteg + FruitBank-átkötés (MainLayout → ``).
- Bónusz/később: `IsProdBuild` (+ opcionális build↔DB keresztellenőrzés, opt-in); `AcEnv.SetConfiguration` (N2D8); SignalR-intervallum konstansok configba (a `SignarlR` typóval együtt kivezetve); MAUI-Android `10.0.2.2` BaseUrl-kérdés.
## Alternatives considered
- **Fogyasztói szinten hagyni** (rejected): minden app újraimplementálná; a redundancia már most élő bugot szült (BaseUrl-divergencia).
- **Teljes átnevezés homlokzat nélkül** (a fogyasztói kód mindenhol `AcEnv.*`-ot hív) (rejected): ~12 hívóhely churn, és a csapat a megszokott helyen (`FruitBankConstClient`) keresi az értéket — a delegát ugyanazt az egy-forrást adja nulla töréssel.
- *Reversibility:* a delegát később bármikor kivezethető, ha a csapat átszokott.
- **Egyetlen `AcEnv.IsProd` név tengely-bontás nélkül** (rejected): a build/adat/hosting tengelyek összemosása — a Debug-build + PROD-DB élő eset bizonyítja, hogy függetlenek; a névnek a forrást kell mondania (`IsProdDb`).
- **Reconnect-enkénti újra-lekérdezés a push-on-connect helyett** (rejected mint végállapot): működik, de ellentmond a „csak az elején 1x" elvnek; a push-on-CHANGE pedig konstrukciósan halott (az env-váltás csak restarttal jöhet, ami bontja a csatornát, amin a push utazna) — ezért query-once + announce-on-connect a végleges alak.
- **Külön köztes osztály (`AcEnvironmentAwareSignalRClientBase`)** (rejected): felesleges hierarchia-szint; az opt-in flag az `AcSignalRClientBase`-en ugyanazt adja ceremónia nélkül.
## Related
- Related ADRs: —
- Related TODOs/Issues: `ACCORE-LOG-T-N2D8` (AcEnv.SetConfiguration — szinergia), `ACCORE-LOG-T-D7S3` (protokoll-diagnosztikai logger, átgondolandó), `ACCORE-SBP-T-T5R2` (protokoll-trace Trace-szintre), `ACBLAZOR-GRID-T-E3B6` (template-ErrorBoundary).
- Precedens: GridLayout-roaming (framework-tag `AcSignalRTags` 9001x + fogyasztói endpoint) — `Nop.Plugin.Misc.AIPlugin/docs/SIGNALR/README.md` „Grid Layout Roaming".
- External references: —