STATUS: DESIGN — FINALIZOVÁNO (2026-05-21). Všechna rozhodnutí DP-0..DP-6 LOCKED. Tento dokument je archivní rationale pro postpaid hosting model. Finální implementovatelné UC jsou:
Phase-2a (UC-10008 marže do účtování) je kompletní a implementovatelná — dodává se PRVNÍ a je nezávislá na tomto dokumentu.
⚠️ NADŘAZENĚ PŘERÁMOVÁNO: Tento DESIGN je dále nadřazeně přerámován pod ADR-026 (Environment first-class) dokumentem UC-10009 ENVIRONMENT REVISION. LOCKED DP-0/1/2/3/5/6 níže zůstávají závazné a beze změny; DP-4 byl přeformulováno per-environment (ENVIRONMENT REVISION sekce 3) a be#125 z velké části padá (per-environment namespace = nativní cost attribution). Sekce 4a (DP-4 V1 label-split analýza) níže zůstává jako rationale audit-trail. DP-4 LOCKED 2026-05-21 — viz LOCKED tabulka níže a sekce 5.
0. PIVOT — co se změnilo oproti Phase-1 ANALYSIS
Phase-1 (UC-10008 ANALYSIS) navrhla prepaid hosting fond (topup + autopay + ledger-jako-balance). User rozhodl:
- Hosting jde POSTPAID. Metered akruál spotřeby → periodická (měsíční)
Stripe platba „charged on the 1st of each month” (design handoff
profile.jsx) + ADR-103 (tiered subscription flat + metered overage). - Rozpor s ADR-103 MIZÍ. Postpaid je v souladu s ADR-103 i design handoffem. ADR-103 zůstává platné a autoritativní — NEsuperseeduje se. Phase-1 FEEDBACK navrhoval „prepaid teď, možná revidovat ADR-103” — to už neplatí, jdeme s ADR-103, ne proti němu.
- Prepaid back-door ZRUŠEN (sekce 6, 2026-05-23). Dormant artefakty
(
settlement_mode,TOPUP/AUTOPAYsource hodnoty,hosting_credit_*sloupce) odstraněny v be#142. Hosting je výhradně postpaid — bez future doors-open. Ledger-as-truth (Phase-1 B.7) zůstává, DEBIT řádky se sčítají do měsíční faktury. - AI ZŮSTÁVÁ prepaid topup — viz rozhodovací bod DP-0 níže (user veto).
Co z Phase-1 ZŮSTÁVÁ platné (postpaid neruší)
- Ledger jako účetní pravda (append-only,
sourceenum, raw+charged snapshot). - Decoupled enforcement: AI vyčerpání ≠ shození hostingu (rationale Phase-1 B.1).
hosting_credit_*sloupce — viz poznámka v datovém modelu (odstraněny v be#142).- Žádná nová Stripe webhook infra — rozšíření UC-10006.
- Marže reálně do účtování = Phase-2a UC-10008 (model-agnostic, beze změny).
LOCKED DECISIONS (user potvrdil — závazné pro finalizaci)
Tyto rozhodovací body už NEjsou otevřené. Finální UC (sekce 5) se staví přesně podle nich. Detailní rozprava je dál v sekci 4 (ponechána jako rationale audit-trail), ale rozhodnutí je tady a je závazné.
| DP | Rozhodnutí (LOCKED) | Pozn. |
|---|---|---|
| DP-0 | AI zůstává prepaid (UC-10007 topup + UC-10008 marže). Postpaid se týká JEN hostingu. | User veto potvrzen. |
| DP-1 | Konfigurovatelný trial_days (property, 0 = trial vypnutý). Doporučený default ~14 dní. Není to ADR-103 Free tier konstrukt — je to jednoduchý časový trial seam; ADR-103 tier bundle zůstává ortogonální informativní vrstva. | hosting_billing_account.trial_ends_at se počítá z trial_days při 1. Publish; 0 ⇒ null ⇒ od 1. Publish rovnou metered. |
| DP-2 | SEPARÁTNÍ hosting cap — NOVÝ hosting_spending_limit_usd (NEreuse UC-10005 spending_limit_usd). AI cap a hosting cap jsou nezávislé stropy. | Reverzuje dřívější doporučení DP-2=B. Drží decoupling AI vs hosting i na úrovni enforcement. UC-10005 zůstává čistě AI. |
| DP-3 | Dunning: 3 retry +0 / +2 / +5 dní → PAST_DUE → ~7 dní grace → SUSPENDED. Fixní rozvrh (ne Stripe-dunning). | hosting_invoice.attempt_count / next_retry_at seam ponechán pro budoucí přechod na smart-retry. |
| DP-5 | Anchor = 1. kalendářního dne v měsíci (design handoff „charged on the 1st”). billing_anchor_day v modelu zůstává konfigurovatelný (default 1) jako seam. | |
| DP-6 | FE estimate = reálná data + non-binding projected (lineární extrapolace accrued_charged_usd). Mockup jazyk „Estimated this month / next invoice on 1st” se zachová (pod postpaid je pravdivý). UI disclaimer „odhad, ne závazná částka”. | |
| DP-4 | LOCKED (2026-05-21). (1) Atribuce: vše v tenant namespace = hosting/infrastructure; aggregate=namespace C.2 dotaz je korektní; label-split (V1/V3) se zamítá. (2) Enforcement: DP-4-A — neuhrazená hosting faktura suspenduje jen published prod app pody; worker, dev preview a dev-loop běží dál. | Issue be#125 zavřen jako invalid premise. Rationale v sekci 4a. |
DP-0 — SCOPE postpaid (POTVRZENO — viz LOCKED výše; rationale níže)
POTVRZENO userem (LOCKED DP-0). Sekce ponechána jako rationale.
„Postpaid teď” se vztahuje na HOSTING. AI kredit ZŮSTÁVÁ prepaid topup (UC-10007 je živé v produkci, reálný user si už dobíjí — překlopení AI na postpaid by zahodilo nasazenou produkční práci).
Cílový stav:
| Fond | Model | Mechanismus |
|---|---|---|
| AI credit | prepaid (beze změny) | UC-10007 topup + nově reálně aplikovaná marže (UC-10008) |
| Hosting | postpaid (nově) | metered akruál → měsíční Stripe charge |
DP-0 rozhodnutí: AI zůstává prepaid (předpoklad analytika). ☐ POTVRDIT ☐ ZAMÍTNOUT (→ AI také postpaid).
1. Cílový datový model (postpaid náčrt)
user_budget (EXISTUJE, recyklujeme)
├─ ai_credit_usd / ai_credit_initial_usd (prepaid AI — beze změny)
├─ hosting_credit_usd / hosting_credit_initial_usd (**DROPPED** v be#142 — prepaid back-door
│ odstraněn 2026-05-23; postpaid nepoužívá)
└─ spending_limit_usd (UC-10005 — REUSE jako hosting hard cap, DP-2)
hosting_billing_account (NOVÁ — postpaid účet stavu period)
user_id (PK, 1:1), billing_anchor_day (default 1),
current_period_start, current_period_end,
accrued_charged_usd (běžící součet charged za aktuální období),
status ACTIVE|PAST_DUE|SUSPENDED,
trial_ends_at (nullable — DP-1),
updated_at
hosting_credit_ledger (NOVÁ — append-only)
id, user_id, type CREDIT|DEBIT,
source HOSTING_USAGE|INVOICE_SETTLEMENT|ADJUSTMENT
(TOPUP/AUTOPAY/TRIAL_INCLUDED odstraněny v be#142 — prepaid back-door zrušen),
⚠️ settlement_mode DROPPED v be#142 (byl seam pro prepaid — viz sekce 6),
raw_amount_usd, charged_amount_usd, markup_percent_snapshot,
hosting_cost_event_id (nullable FK), billing_period_id (nullable),
ref_id, created_at
hosting_invoice (NOVÁ — měsíční postpaid faktura)
id, user_id, period_start, period_end,
total_charged_usd, stripe_invoice_id / stripe_payment_intent_id,
status DRAFT|OPEN|PAID|FAILED|VOID,
attempt_count, next_retry_at, created_at, settled_at
hosting_cost_events (C.2, EXISTUJE) — raw, IMMUTABLE, beze změny
pricing_markup_config (C.3, EXISTUJE) — beze změny (markup aplikace = UC-10008)
Invariant (beze změny z Phase-1): hosting_cost_events a usage_events
se NIKDY nemutují. Markup raw→charged aplikuje UC-10008 kontrakt při
akruálu DEBIT řádku do ledgeru; accrued_charged_usd na
hosting_billing_account je jen denormalizovaný běžící součet pro rychlý
read/enforcement.
2. Tok raw → charged → akruál → měsíční settle (postpaid)
- C.2 poller zapíše raw řádek do
hosting_cost_events(beze změny). - Periodický reconciler vezme nezúčtované
hosting_cost_events, pro každýcharged = applyMarkup(raw, hostingMarkupPercent)(UC-10008 kontrakt), zapíše DEBIT dohosting_credit_ledger(source=HOSTING_USAGE, raw+charged+markup snapshot + FK na cost event;settlement_modesloupec odstraněn v be#142) a inkrementujehosting_billing_account.accrued_charged_usd. Idempotence:hosting_cost_event_idUNIQUE prosource=HOSTING_USAGE. - Žádný odečet z balance (rozdíl proti prepaid Phase-1). Akruál jen roste.
- Na
billing_anchor_day(default 1. v měsíci): uzavři období → vytvořhosting_invoicesetotal_charged_usd = Σ DEBIT za období→ Stripe PaymentIntent off-session uloženou kartou (UC-10001) →INVOICE_SETTLEMENTCREDIT řádek popayment_intent.succeeded(UC-10006 webhook,metadata.fund=HOSTING). Resetaccrued_charged_usd=0, posun period. - Hard spend cap (DP-2) a dunning při selhání faktury (DP-3) — viz Open Decisions.
Analogicky AI zůstává prepaid (UC-10007 + UC-10008 odečet z ai_credit_usd).
3. API náčrt (finalizovat po potvrzení DP-1..DP-6)
GET /api/v1/users/me/billing/hosting— postpaid stav: aktuální období,accruedChargedUsd, projected end-of-month (DP-6), status, trial.GET /api/v1/users/me/billing/hosting/invoices— historie měsíčních faktur (lze sloučit s existujícím UC-10004 list-invoices — ověřit při finalizaci).- Hosting hard cap = REUSE existujícího
UC-10005 spending-limit(NEzavádět paralelní endpoint) — viz DP-2. - Žádný
topup/autopayhosting endpoint — hosting je výhradně postpaid (DP-7, sekce 6).topupje výhradně AI kredit (UC-10007). GET /api/v1/users/me/usage/breakdown(C.3) — rozšířit o hosting postpaid část; estimate = non-binding projected (DP-6).
4a. DP-4 ANALÝZA — cost-misattribution talkide-worker v tenant ns (jediný OTEVŘENÝ bod)
Cílená analýza dle zadání. Vše níže ověřeno z kódu/ADR, ne předpoklad.
4a.1 Faktický stav (ověřeno)
Co C.2 (HOTOVÁ, v prod main) reálně dělá:
OpenCostClient.fetchNamespaceAllocations()volá výhradněGET /allocation?aggregate=namespace&window=<window>. Žádný vnitřní rozpad namespace. BeretotalCost+cpuCost/ramCost/pvCost/networkCostCELÉHO namespace jako jeden řádek.RecordHostingCostBatchProcessor.persistOne()mapujens→tenant-<slug>→tenantRepository.findBySlug(slug)→tenant.owner.ida zapíše celý namespace cost jako jedenhosting_cost_eventsřádek toho usera. Non-tenant-ns se skipnou.- Namespace model =
tenant-{slug}(per-tenant, ADR-015 §1) — JEDEN namespace per tenant.mirek-dev/mirek-prod(namespace-per-tenant-env) z ADR-024 je separátní budoucí workstream (ADR-023 ř.460 „separátní workstream”), aktuálně NEaktivní. Tj. dnes by i preview i prod user-app pody byly v jednomtenant-<slug>ns.
Co ADR-024 přidá DO tenant-<slug> ns (= do stejného OpenCost namespace
bucketu, který C.2 celý sčítá jako hosting):
| Workload | Charakter | Logická kategorie |
|---|---|---|
user-app pody — dev preview (<uuid>.talkide.app) | hostovaná user aplikace | HOSTING |
user-app pody — prod published (<slug>.talkide.app) | hostovaná user aplikace | HOSTING |
| talkide-worker pod (Mara/Theo agent runtime, dlouhoběžící, ADR-024 §1/§4) | AI vývojový runtime | AI-DEV |
| gradle/test ephemeral K8s Joby (ADR-024 §4, dispatch z workeru) | AI dev-loop build/test | AI-DEV |
| Kaniko build Joby (ADR-019, už existují) | image build pro deploy | AI-DEV (build) |
⇒ Misattribution problém je reálný a deterministický: jakmile ADR-024 přistane, C.2 začne sčítat compute talkide-worker podu (Node SDK runtime ~24/7 dlouhoběžící — netriviální RAM/CPU) + gradle/test Job burst (CPU-heavy) do hosting účtu uživatele. Logicky to ale patří do AI/vývojové části (Mara/Theo = AI development), NE do hosting účtu hostované appky. Postpaid to zhoršuje: tenhle compute by uživateli přišel na měsíční hosting faktuře (reálné peníze přes Stripe), ne jen jako interní metrika.
4a.2 Lze tenant-ns cost rozpadnout? (OpenCost granularita)
Ano. OpenCost /allocation API (ověřeno proti veřejné OpenCost API
dokumentaci, neběháno proti clusteru) podporuje:
aggregate=namespace|controller|controllerKind|pod|deployment|label:<klíč>|node— a kombinace (CSV), např.aggregate=namespace,label:talkide.io/workload-class.filterrodina parametrů:filterNamespaces,filterLabels,filterControllers,filterControllerKinds— lze tedy dotáhnout cost JEN pro pody s daným labelem / controllerem uvnitř namespace.
Tj. technicky lze tenant-ns cost rozseknout na „hosting (user-app workloady)” vs „AI-dev (worker + build/test Joby)” — za předpokladu, že workloady nesou deterministický rozlišovací label/controller-kind, který provisioner nasazuje. To je předpoklad, který musí splnit ADR-024 implementace (jednotná konvence labelů — viz doporučení).
4a.3 Varianty řešení
| # | Varianta | Pro | Proti |
|---|---|---|---|
| V1 | Label-aware C.2 — provisioner labeluje VŠECHNY pody/Joby talkide.io/workload-class=hosting\|ai-dev. C.2 volá aggregate=namespace,label:talkide.io/workload-class (nebo 2 dotazy s filterLabels), zapíše do hosting_cost_events JEN hosting část; ai-dev část jde do AI fondu (nebo se ignoruje — viz D). | Přesné, data-driven, žádné heuristiky. Single source of truth (OpenCost). Drží decoupling AI vs hosting na úrovni měření. Funguje i v dnešním tenant-<slug> modelu i v budoucím per-tenant-env. | Vyžaduje koordinovanou změnu: provisioner (labely na user-app + worker + Job manifesty) + C.2 poller (label-aware query + parsing). Label musí být 100% pokrytý — neoznačený pod by spadl do „unknown” bucketu (nutný fallback: unknown → hosting konzervativně NEBO alarm). |
| V2 | Worker/build do JINÉHO namespace než user-app (např. tenant-<slug>-aidev vs tenant-<slug>). C.2 mapuje jen „čistý” hosting ns; aidev ns se buď skipne (mimo hosting) nebo nasměruje do AI fondu. | C.2 zůstává beze změny (namespace-level agregace pořád funguje, jen přibude druhý ns se suffixem který se nemapuje na hosting). Nejjasnější izolace (i RBAC, kvóty, blast-radius). | Velká infra změna v ADR-024/ADR-015 (namespace naming, provisioner, RBAC, NetworkPolicy, ResourceQuota per oba ns). ADR-024 explicitně dává worker DO tenant-env ns (§1) — V2 by ho reframovala. Víc K8s objektů per tenant. |
| V3 | V1 + worker compute aktivně do AI fondu — ai-dev část se nezahodí, ale zaúčtuje proti AI prepaid (DEBIT do AI ledgeru, source=AI_INFRA). | Nákladově poctivé (worker/build compute někdo platí — uživatel přes AI prepaid, konzistentní s „AI vývoj = AI fond”). Drží Phase-1 B.1 decoupling rationale plně. | Nejvíc práce: nové AI ledger source + markup rozhodnutí pro infra compute + UI breakdown. Pro alfu pravděpodobně over-scope (worker compute je dnes malý absolutní $ vs Anthropic API náklady). Riziko dvojí účtování vnímané userem (AI tokeny + AI infra). |
| V4 | Ponechat hrubé + korekční koeficient — C.2 sčítá celý ns, ale aplikuje fixní downward koeficient (např. „×0.8”) jako proxy za worker overhead. | Nulová infra změna, nejrychlejší. | Nepřesné, neauditovatelné, křehké (koeficient závisí na poměru worker/app, plánu, app velikosti — drift). Pro postpaid (reálná Stripe faktura) neakceptovatelné — uživatel zaplatí arbitrární korekci. Zamítnuto jako cílové řešení; max. dočasný stop-gap. |
4a.4 DOPORUČENÍ analytika
Primárně V1 (label-aware C.2 attribution). Worker/build → ai-dev,
user-app → hosting; do hosting_cost_events jen hosting část.
Otevřená dílčí volba pro usera, co s ai-dev částí:
- V1-only (jednodušší, doporučeno pro alfu):
ai-devcompute se neúčtuje uživateli vůbec — TalkIDE ho absorbuje jako provozní náklad AI vývoje (Anthropic API tokeny už uživatel platí přes AI prepaid; raw worker compute je malý a je to cena za to být na platformě). Čistý, žádné nové AI ledger konstrukty. - V1→V3 (later):
ai-devcompute zaúčtovat do AI prepaid fondu. Necháváme jako future-door (label už bude na místě, stačí přidat AI ledger source) — NEdělat pro alfu.
V2 jako fallback, pokud user/ADR-024 implementace chce tvrdší izolaci než label (RBAC/kvóta/network argument je legitimní) — ale to je primárně ADR-024/ADR-015 rozhodnutí, ne billing; billing se mu přizpůsobí (V2 je pro C.2 dokonce méně práce než V1). V4 zamítnut pro postpaid (reálné peníze nesmí stát na koeficientu).
Předpoklad pro V1/V3 (musí garantovat ADR-024 implementace): jednotná
povinná label konvence talkide.io/workload-class ∈ {hosting, ai-dev} na
všech pod/Job templatech, které provisioner do tenant ns vkládá (user-app
Deployment, worker Deployment, gradle/test Job, Kaniko Job). Bez 100% pokrytí
label-attribution tiše leakuje — proto C.2 follow-up MUSÍ mít explicitní
„unknown workload-class” handling (doporučeno: počítat jako hosting +
WARN log + metrika, aby se leak detekoval, ne tiše spolkl).
4a.5 Dopad na C.2 (HOTOVOU) + scope follow-up
Je C.2 jak je nasazená nesprávná, jakmile přijde talkide-worker? — ANO.
C.2 je dnes korektní (v tenant-<slug> ns dnes běží jen user-app pody +
provisioning artefakty → celý ns cost ≈ hosting). Stává se nesprávnou v
okamžiku, kdy ADR-024 nasadí worker pod / gradle-test Joby do tenant ns —
od té chvíle sčítá AI-dev compute jako hosting. Není to bug v C.2 dnes; je to
známá budoucí inkompatibilita, kterou je nutné vyřešit PŘED (nebo
synchronně s) ADR-024 cut-overem.
Scope follow-up GitLab issue (NEimplementovat teď, jen scope):
Title: C.2 hosting poller — label-aware workload attribution (ADR-024 readiness) Repo: talkide-be Blocking: ADR-024 worker extraction cut-over (MUSÍ být hotové dřív, jinak hosting faktury nadhodnocené o AI-dev compute) Scope:
OpenCostClient: přidat dotaz saggregate=namespace,label:talkide.io/workload-class(nebo 2×filterLabelsdotaz) místo / vedleaggregate=namespace.- Nový parsed model: per-ns rozpad na
hosting/ai-dev/unknownčást.RecordHostingCostBatchProcessor: zapsat dohosting_cost_eventsJENhostingčást.unknown→ konzervativně hosting + WARN + metrika (leak detektor).ai-dev→ V1-only: zahodit (jen INFO log/metrika); V3-later: AI ledger DEBIT (mimo scope tohoto issue).- Idempotence klíč
(namespace, window_start, window_end)zůstává — částka se mění (jen hosting podíl), schema beze změny.hosting_cost_eventsimmutable invariant nedotčen (zapisujeme rovnou správnou hodnotu, nemutujeme).- Závislost na infra/provisioner: label konvence
talkide.io/workload-classmusí být na všech tenant-ns pod/Job templatech (cross-repo: talkide-be provisioner + ADR-024 worker manifest + GradleJobBuilder + KanikoJobBuilder).- Test: OpenCost label-aggregate response fixture; ns s mixem hosting+ai-dev+unknown → jen hosting podíl persistován.
Not in scope: AI fond účtování worker compute (V3 — separátní future issue), namespace split (V2 — ADR-024/ADR-015 rozhodnutí).
4a.6 DP-4 přeformulované rozhodnutí pro usera (po vyřešení attribution)
DP-4 původně (sekce 4 níže) = „co se zastaví, když hosting faktura nezaplacena”. Po attribution fixu (V1) je odpověď čistá a drží AI↔hosting decoupling:
- Při
SUSPENDED(nezaplacený hosting po dunningu DP-3) se zastaví JEN published prod user-app pody (<slug>.talkide.app) — to je hosting postpaid doména. - talkide-worker pod, dev preview (
<uuid>.talkide.app), gradle/test Joby NEpadají — jejich compute jeai-dev(mimo hosting účet) a AI prepaid je samostatný fond (DP-0). Uživatel může dál vyvíjet (Mara/Theo) a vidět preview i s neuhrazenou hosting fakturou; jen jeho veřejně publikovaná prod app je suspendovaná do úhrady.
Varianty pro usera (DP-4 finální rozhodnutí):
| # | Co se při neuhrazeném hostingu zastaví | Pozn. |
|---|---|---|
| DP-4-A | Jen published prod app pody. Worker + preview + dev-loop běží dál. | DOPORUČENO. Maximálně drží decoupling (hosting problém ≠ zablokovaný vývoj). Konzistentní s Phase-1 B.1 a DP-0. Vyžaduje V1 attribution (jinak nelze čistě říct „hosting”). |
| DP-4-B | Published prod + dev preview pody (vše „runtime app”), ale worker (Mara/Theo) NE — AI vývoj jako čistě AI-fond doména běží dál. | Tvrdší (preview je taky „hostování”), ale pořád odděluje AI vývoj. Volba, pokud user chce, aby neuhrazený hosting bral i preview URL. |
| DP-4-C | Vše v tenant ns včetně workeru (AI vývoj taky padá). | NEdoporučeno — porušuje DP-0 decoupling (hosting dluh shodí i AI vývoj, za který user platí zvlášť). Uvedeno pro úplnost. |
Doporučení: DP-4-A (suspend jen published prod; worker/preview/dev-loop decoupled, drží DP-0 a Phase-1 B.1 rationale). Předpoklad = V1 attribution hotové (jinak „prod app pod” nelze cost-čistě izolovat — V1 je tedy hard dependency DP-4-A i samotné postpaid hosting korektnosti).
4. PŘEODVOZENÉ OPEN DECISIONS (postpaid framing) — pro usera
Phase-1 D1–D8 přeodvozeny POD POSTPAID. Uvádím jen ty, kterým postpaid reálně mění význam; u každé varianty + doporučení (baseline = duch Phase-1 doporučení, přepočítaný na postpaid). Decisions, které postpaid nemění (Phase-1 D2 prahy varování, D5 autopay-fail) jsou označené jako přesunuté/zaniklé.
DP-1 — Počáteční stav: trial / free tier (přeodvozeno z Phase-1 D1)
V postpaid neexistuje „grant” (není balance k nasypání). Otázka je, jak může user Publish vyzkoušet, než přijde 1. faktura.
- A) Žádný trial — od 1. Publish běží metered akruál, 1. v měsíci přijde faktura za cokoli naběhlo (i pár centů).
- B) Free tier dle ADR-103 — Free tier = hard quotas, žádný overage, žádná faktura dokud user nepřekročí inclusive bundle / neupgraduje na Pro+.
- C) Časově omezený trial (X dní hosting zdarma, pak postpaid).
- Doporučení: B. Přímo realizuje ADR-103 (Free tier = hard quotas, žádný
overage). Žádný umělý trial konstrukt, konzistentní s autoritativní spec.
trial_ends_atnecháváme v modelu nullable jen jako seam (C jako future varianta), ale neimplementuje se. (Phase-1 doporučovala „malý grant B” — pod postpaid to nedává smysl, přeodvozeno na ADR-103 Free tier.)
DP-2 — Hard spend cap aby metered náklad neutekl (přeodvozeno z Phase-1, napojení na UC-10005)
Postpaid riziko: metered náklad může utéct dřív než přijde měsíční faktura (runaway app). Potřeba strop.
- A) Nový paralelní hosting cap endpoint/sloupec.
- B) REUSE existující UC-10005
spending_limit_usdjako celkový měsíční hard cap (AI charged + hosting charged dohromady). Při dosažení → soft-stop (Mara blokována, UC-10005 sémantika) + hosting enforcement (DP-4). - C) REUSE UC-10005 sémantiky, ale separátní
hosting_spending_limit_usdsloupec (oddělený cap pro hosting vs AI). - Doporučení: B. Zadání explicitně říká „napoj na EXISTUJÍCÍ UC-10005,
nezaváděj paralelní”. Jeden srozumitelný měsíční $ strop pro usera (AI+hosting
charged) je nejjednodušší mentální model a nuluje duplicitní enforcement
kód. C je čistší izolačně, ale zavádí 2. limit UI/UX — pro alfu
over-engineering; lze přidat později (separátní
hosting_spending_limit_usdsloupec — LOCKED DP-2). Pod-otázka k potvrzení: počítá se cap z charged (konzistentní s UC-10008 Q7 — doporučeno) ano/ne.
DP-3 — Dunning / grace při SELHÁNÍ měsíční Stripe faktury (přeodvozeno z Phase-1 D5)
Phase-1 D5 řešil „autopay fail”. Postpaid ekvivalent = měsíční postpaid invoice payment fail (off-session PaymentIntent na uzavřené období declined / SCA).
- A) Faktura fail → okamžitý SUSPENDED (žádná grace).
- B) Retry 3× (např. den 1 / +2 / +5 dní) s notifikací; mezi tím
status=PAST_DUE, apps běží dál; po vyčerpání retry → SUSPENDED (DP-4 rozsah). Notifikace „faktura nezaplacena, aktualizuj kartu”. - C) Jako B + smart-retry (Stripe dunning /
invoice.payment_failedwebhook driven) místo fixního rozvrhu. - Doporučení: B (3 retry: +0 / +2 / +5 dní, pak SUSPENDED, grace ~7 dní
celkem). Symetrické s Phase-1 D5 doporučením (grace + 1 retry) přeškálované
na měsíční postpaid realitu (větší částka, delší grace férová). C je
produkčně nejlepší, ale Stripe Subscriptions/Invoicing dunning je víc infra
než alfa potřebuje —
hosting_invoice.attempt_count/next_retry_atv modelu necháváme jako seam pro pozdější přechod na C.
DP-4 — Co se zastaví když faktura nezaplacena: published prod vs preview/dev (přeodvozeno z Phase-1 D4)
Phase-1 D4 doporučení C (prod→hosting fond / preview→AI fond). Přeodvoženo pod postpaid:
- A) PAST_DUE→SUSPENDED zastaví VŠECHNO (published prod + preview/dev).
- B) Zastaví jen published prod apps; preview/dev jede dál.
- C) Zastaví published prod (= hosting postpaid doména); preview/dev compute se účtuje proti AI prepaid fondu (preview = součást vývoje) → hosting faktura fail neovlivní vývoj/preview. Vyžaduje, aby C.2 raw cost klasifikoval ns PREVIEW vs PROD.
- Doporučení: C (zachováno z Phase-1 D4=C, přeodvozeno: „hosting vyčerpání” → „hosting faktura nezaplacena”). Nejlépe drží decoupling rationale (hosting problém ≠ zablokovaný vývoj; AI problém ≠ shozený prod). Nejvíc práce (ns klasifikace) — pokud user chce minimální alfa rozsah → fallback B.
DP-5 — Billing cyklus / anchor (NOVÁ pod postpaid, byla implicitní)
- A) Měsíční, anchor = 1. kalendářního dne (design handoff: „charged on the 1st of each month”).
- B) Měsíční, anchor = den signupu / 1. Publish (rolling per-user).
- Doporučení: A. Přímo dle design handoffu (
profile.jsx„1st of each month”) a srozumitelné („příští faktura 1.X.”).billing_anchor_dayv modelu necháváme konfigurovatelný (default 1) jako seam, kdyby B bylo potřeba. ☐ POTVRDIT anchor = 1. v měsíci.
DP-6 — FE estimate = non-binding projected (přeodvozeno z Phase-1 D8=C)
Phase-1 D8 doporučení C (reálná data + nezávazný projected, bez „caps”/ „invoice” jazyka — protože tehdy byl prepaid a „invoice” jazyk byl falešný). Pod postpaid se framing OBRACÍ: „next invoice on 1st” a „estimated this month” už NEjsou falešné — postpaid reálně měsíčně fakturuje.
- A) FE zobrazí jen reálný
accrued_charged_usdza období, žádný odhad. - B) A + non-binding projected end-of-month = lineární extrapolace
accrued_charged_usddle uplynulé části období (zhosting_cost_eventsagregace, charged dle UC-10008). Mockup jazyk „Estimated this month / next invoice on 1st” se zachová (teď je pravdivý), caps zobrazené dle ADR-103 tier bundle (informativní). - Doporučení: B. Postpaid legitimizuje původně-mockup framing fe#10/C.4.
Projected je explicitně non-binding (UI disclaimer „odhad, ne závazná
částka”); skutečná faktura =
Σ DEBITpři uzávěrce, ne projekce. Caps = informativní ADR-103 tier bundle. (Toto je posun proti Phase-1 D8=C, kde se „invoice” jazyk měl odstranit — postpaid ho naopak činí správným.)
Zaniklé / přesunuté Phase-1 decisions
- Phase-1 D2 (prahy varování % vs absolutní): zůstává relevantní, ale postpaid ho nemění významově → necháno na finalizaci (doporučení Phase-1 B absolutní práh stále platí, navázáno na DP-2 cap %).
- Phase-1 D3 (grace při vyčerpání prepaid): splynulo s DP-3 (postpaid nemá „vyčerpání balance”; ekvivalent = invoice fail dunning).
- Phase-1 D5 (autopay fail): splynulo s DP-3 (postpaid nemá autopay; ekvivalent = postpaid invoice fail).
- Phase-1 D6 (pořadí dodání): rozhodnuto userem = varianta A → Phase-2a UC-10008 první (kompletní), Phase-2b tento dokument druhý (design).
- Phase-1 D7 (rozsah future-proof): rozhodnuto = A (jen model neuzavřít) → sekce 6.
5. Struktura finální UC — ROZPADNUTO (DP-4 LOCKED 2026-05-21)
FINALIZOVÁNO. Všechna rozhodnutí DP-0/1/2/3/4/5/6 jsou LOCKED. DESIGN doc byl rozpadnut na finální implementovatelné UC:
- UC-10012 — postpaid akruál + ledger + reconciler + měsíční faktura +
SOFT dunning enforcement (hotovo, viz
UC-10012_environment-billing-F2.md). - UC-10015 — HARD hosting enforcement (F5): scale-to-zero published
prod app podů při
SUSPENDEDstavu (DP-4-A). VizUC-10015_hosting-hard-enforcement-F5.md.
DP-4-A enforcement rozhodnutí (2026-05-21):
- Atribuce: vše v tenant namespace = hosting/infrastructure. C.2
aggregate=namespacezůstává beze změny; label-split (V1/V3) se zamítá. Rationale: worker compute je malý absolutní $ vs Anthropic API; marže z AI pokrývá i worker compute; namespace-aggregate atribuce je korektní. - Enforcement: jen published prod app pody se suspendují (
scale(0)jejich Deploymentu). Worker, dev preview a dev-loop běží dál nedotčeně. - Issue be#125 (label-aware cost attribution) zavřen jako invalid premise.
Napojení DP-2: hosting hard cap → user_budget.hosting_spending_limit_usd
(separátní, NE reuse UC-10005 spending_limit_usd). Řešeno v UC-10012 (F2).
FE projected: rozšíření C.3 breakdown (DP-6 = reálná data + non-binding
projected). Řešeno v UC-10012 (F2), endpoint
GET /api/v1/billing/hosting/estimate.
6. Locked: výhradně postpaid (2026-05-23)
Phase-1 prepaid návrh (topup + autopay + balance ledger) se neimplementuje.
Schema artefakty (hosting_credit_usd, hosting_credit_initial_usd, settlement_mode,
source enum hodnoty TOPUP/AUTOPAY/TRIAL_INCLUDED) odstraněny v be#142 (Liquibase 0050+).
Argumentace:
- Postpaid je operačně jednodušší (1 měsíční cyklus, žádný topup UX)
- Stripe vrstva funguje pro postpaid invoices (UC-10006/07/08)
- DP-4-A hard enforcement live (UC-10015)
- Dormant back-door artefakty matou agenty a mate UI/UX (FE HostingCreditPanel zobrazoval seed $50 jako ‘remaining credit’ navzdory postpaid účtování)
Návrat k prepaid by vyžadoval nový design + signature + nové DB migrace, ne reaktivaci back-door. Pokud bude potřeba v budoucnu, založit nový UC.
FEEDBACK
- Phase-2a (UC-10008 marže do účtování) je kompletní a implementovatelná — model-agnostic, nezávislá na tomto dokumentu, dodává se PRVNÍ (user D6=A). Marže 0 = vynucený no-op invariant → bezpečný deploy bez regrese.
- Rozpor s ADR-103 PIVOTEM zmizel — postpaid je přesně ADR-103 (tiered subscription + metered overage) + design handoff („1st of month”). ADR-103 zůstává platné, autoritativní, NEdotčené, NEsuperseednuté. Phase-1 FEEDBACK „možná revidovat ADR-103” je tímto zneplatněn — jdeme s ním.
- Prepaid back-door ZRUŠEN (sekce 6, 2026-05-23) —
settlement_modesloupec,TOPUP/AUTOPAY/TRIAL_INCLUDEDsource hodnoty ahosting_credit_*sloupce odstraněny v be#142. Hosting je výhradně postpaid. - DP-0 (AI zůstává prepaid) — LOCKED, user veto potvrzen. AI billing (UC-10007 topup + UC-10008 marže) je nezávislé na postpaid hosting.
- DP-2 LOCKED reverzoval dřívější doporučení — user zvolil separátní
hosting_spending_limit_usd(NE reuse UC-10005). Sekce 4 DP-2 níže ponechána jako rationale audit-trail, ale závazný je LOCKED řádek (separátní cap drží AI↔hosting decoupling i v enforcement). - DP-4 LOCKED (2026-05-21). Obě části rozhodnuty:
(1) Atribuce: vše v tenant namespace = hosting/infrastructure; C.2
aggregate=namespaceje korektní a zůstává beze změny; label-split (V1/V3) se zamítá. Issue be#125 zavřen jako invalid premise. Rationale: worker compute je malý absolutní $ vs Anthropic API; marže z AI pokrývá i worker compute. (2) Enforcement: DP-4-A — neuhrazená hosting faktura po dunningu suspenduje jen published prod app pody (scale(0)). Worker, dev preview a dev-loop nedotčeny; user dál vyvíjí i s nezaplacenou hosting fakturou. - Phase-2b FINALIZOVÁNO. DESIGN doc rozpadnut na finální UC: UC-10012 (postpaid akruál + SOFT enforcement, F2) a UC-10015 (HARD enforcement, F5). Sekvence / API / test tabulky v těchto UC souborech.
Thanks for the feedback.