Internal Documentation internal
TalkIDE internal documentation

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:

  • UC-10012 — postpaid akruál + SOFT enforcement (F2)
  • UC-10015 — HARD enforcement DP-4-A (F5)

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/AUTOPAY source 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, source enum, 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é.

DPRozhodnutí (LOCKED)Pozn.
DP-0AI zůstává prepaid (UC-10007 topup + UC-10008 marže). Postpaid se týká JEN hostingu.User veto potvrzen.
DP-1Konfigurovatelný 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; 0null ⇒ od 1. Publish rovnou metered.
DP-2SEPARÁ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-3Dunning: 3 retry +0 / +2 / +5 dníPAST_DUE~7 dní graceSUSPENDED. Fixní rozvrh (ne Stripe-dunning).hosting_invoice.attempt_count / next_retry_at seam ponechán pro budoucí přechod na smart-retry.
DP-5Anchor = 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-6FE 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-4LOCKED (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:

FondModelMechanismus
AI creditprepaid (beze změny)UC-10007 topup + nově reálně aplikovaná marže (UC-10008)
Hostingpostpaid (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)

  1. C.2 poller zapíše raw řádek do hosting_cost_events (beze změny).
  2. Periodický reconciler vezme nezúčtované hosting_cost_events, pro každý charged = applyMarkup(raw, hostingMarkupPercent) (UC-10008 kontrakt), zapíše DEBIT do hosting_credit_ledger (source=HOSTING_USAGE, raw+charged+markup snapshot + FK na cost event; settlement_mode sloupec odstraněn v be#142) a inkrementuje hosting_billing_account.accrued_charged_usd. Idempotence: hosting_cost_event_id UNIQUE pro source=HOSTING_USAGE.
  3. Žádný odečet z balance (rozdíl proti prepaid Phase-1). Akruál jen roste.
  4. Na billing_anchor_day (default 1. v měsíci): uzavři období → vytvoř hosting_invoice se total_charged_usd = Σ DEBIT za období → Stripe PaymentIntent off-session uloženou kartou (UC-10001) → INVOICE_SETTLEMENT CREDIT řádek po payment_intent.succeeded (UC-10006 webhook, metadata.fund=HOSTING). Reset accrued_charged_usd=0, posun period.
  5. 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 / autopay hosting endpoint — hosting je výhradně postpaid (DP-7, sekce 6). topup je 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. Bere totalCost + cpuCost/ramCost/pvCost/networkCost CELÉHO namespace jako jeden řádek.
  • RecordHostingCostBatchProcessor.persistOne() mapuje nstenant-<slug>tenantRepository.findBySlug(slug)tenant.owner.id a zapíše celý namespace cost jako jeden hosting_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 jednom tenant-<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):

WorkloadCharakterLogická kategorie
user-app pody — dev preview (<uuid>.talkide.app)hostovaná user aplikaceHOSTING
user-app pody — prod published (<slug>.talkide.app)hostovaná user aplikaceHOSTING
talkide-worker pod (Mara/Theo agent runtime, dlouhoběžící, ADR-024 §1/§4)AI vývojový runtimeAI-DEV
gradle/test ephemeral K8s Joby (ADR-024 §4, dispatch z workeru)AI dev-loop build/testAI-DEV
Kaniko build Joby (ADR-019, už existují)image build pro deployAI-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.
  • filter rodina 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í

#VariantaProProti
V1Label-aware C.2 — provisioner labeluje VŠECHNY pody/Joby talkide.io/workload-class=hosting\&#124;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).
V2Worker/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.
V3V1 + worker compute aktivně do AI fonduai-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).
V4Ponechat 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-dev compute 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-dev compute 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:

  1. OpenCostClient: přidat dotaz s aggregate=namespace,label:talkide.io/workload-class (nebo 2× filterLabels dotaz) místo / vedle aggregate=namespace.
  2. Nový parsed model: per-ns rozpad na hosting / ai-dev / unknown část.
  3. RecordHostingCostBatchProcessor: zapsat do hosting_cost_events JEN hosting čá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).
  4. Idempotence klíč (namespace, window_start, window_end) zůstává — částka se mění (jen hosting podíl), schema beze změny. hosting_cost_events immutable invariant nedotčen (zapisujeme rovnou správnou hodnotu, nemutujeme).
  5. Závislost na infra/provisioner: label konvence talkide.io/workload-class musí být na všech tenant-ns pod/Job templatech (cross-repo: talkide-be provisioner + ADR-024 worker manifest + GradleJobBuilder + KanikoJobBuilder).
  6. 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 je ai-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-AJen 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-BPublished 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-CVš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_at nechá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_usd jako 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_usd sloupec (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_usd sloupec — 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_failed webhook 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_at v 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_day v 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_usd za období, žádný odhad.
  • B) A + non-binding projected end-of-month = lineární extrapolace accrued_charged_usd dle uplynulé části období (z hosting_cost_events agregace, 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 = Σ DEBIT př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-10015HARD hosting enforcement (F5): scale-to-zero published prod app podů při SUSPENDED stavu (DP-4-A). Viz UC-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=namespace zů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_mode sloupec, TOPUP/AUTOPAY/TRIAL_INCLUDED source hodnoty a hosting_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=namespace je 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.

Was this page helpful?

Thanks for the feedback.