Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
# Live credits test (Local Demo Co)
|
||||
|
||||
**Date:** 2026-08-04
|
||||
**Tenant:** Local Demo Co (`ee246275-dec0-4446-9e83-58d0c16c258a`) via `demo@descrybe.local`
|
||||
**Stack:** API `:8080`, web `:5174` (Vite proxy), worker + Green Chat LLM, Postgres `:5433`
|
||||
**Proof rule:** Live wallet deltas only. **No `seed-demo` / AssignPlan reset used as proof.**
|
||||
|
||||
## Verdict
|
||||
|
||||
**PASS.** SEO AI, campaign AI, and `enhance_only` each debited **2** credits (formula-aligned).
|
||||
`/api/billing/credits`, `/api/billing/usage?range=30d`, `/api/auth/me`, open `billing_cycles.credits_used`, and `credit_balances` all agreed after the run.
|
||||
Billing page HTTP **200**; UI APIs (same session via `:5174` proxy) matched the DB.
|
||||
Enterprise remaining **label** stays “Unlimited”; numeric **used / total** reflect the wallet.
|
||||
|
||||
Wallet was **not** restored to 1M — burn was small (28 used of 1,000,000).
|
||||
|
||||
## Recorded balances
|
||||
|
||||
| Checkpoint | total | used | remaining | open cycle used |
|
||||
|------------|------:|-----:|----------:|----------------:|
|
||||
| Before this run | 1,000,000 | 22 | 999,978 | 22 |
|
||||
| After SEO + campaign + enhance | 1,000,000 | 28 | 999,972 | 28 |
|
||||
| **Delta (this run)** | — | **+6** | **−6** | **+6** |
|
||||
|
||||
Pre-existing `used=22` came from earlier live AI activity in the same open cycle (not from seed).
|
||||
|
||||
## Cost table (active)
|
||||
|
||||
| feature_name | cost_per_unit |
|
||||
|--------------|--------------:|
|
||||
| `seo_meta_ai` | 1 |
|
||||
| `campaign_copy` | 1 |
|
||||
| `product_processing` | 1 |
|
||||
| `openai_token_k` | 1 |
|
||||
|
||||
**Debit formula** (`billing.ConsumeCredits` / `EstimateDebit`):
|
||||
|
||||
`debit = cost(feature) + ceil(tokens/1000) * cost(openai_token_k)` (minimum 1 when charging).
|
||||
|
||||
With base=1 and tokens in (1..1000], expected debit = **2**.
|
||||
|
||||
## Operations
|
||||
|
||||
| Step | Endpoint | Result | Wallet debit | Notes |
|
||||
|------|----------|--------|-------------:|-------|
|
||||
| SEO AI | `POST /api/seo/apply` `mode=ai` product `d2c309ed-…` (Sample Gadget) | 200 | **2** | `credits_charged=2` matched Δused; title filled |
|
||||
| Campaign AI | `POST /api/campaigns` → `…/{id}/generate` `mode=ai` | 201/200 | **2** | Campaign `6c135dd1-…`; subject “Upgrade your home essentials” |
|
||||
| Enhance | `POST /api/processing/jobs` `enhance_only` raw `fa4d70c7-…` | 202→completed | **2** | Job `6a4f09fc-…`; steps `normalize`+`ai_enhance` done |
|
||||
| **Sum** | | | **6** | Equals SEO+campaign+enhance |
|
||||
|
||||
## Billing UI / API consistency
|
||||
|
||||
Same browser-origin session (`http://127.0.0.1:5174`, cookies `descrybe_session` + `descrybe_csrf`):
|
||||
|
||||
| Surface | used | remaining | plan |
|
||||
|---------|-----:|----------:|------|
|
||||
| DB `credit_balances` | 28 | 999972 | Enterprise |
|
||||
| `GET /api/billing/credits` | 28 | 999972 | Enterprise |
|
||||
| `GET /api/billing/usage?range=30d` | 28 | 999972 | (wallet fields) |
|
||||
| `GET /api/auth/me` → credits | 28 | 999972 | Enterprise |
|
||||
| Open `billing_cycles` | 28 | — | — |
|
||||
| `GET /billing` | HTTP 200 | — | — |
|
||||
|
||||
UI note: `formatCreditsRemaining` shows **Unlimited** for Enterprise; the “used of total” line stays numeric and matched the API.
|
||||
|
||||
## Checks (all PASS)
|
||||
|
||||
- SEO debit == `credits_charged` (2)
|
||||
- Campaign debit ≥ 1 (2)
|
||||
- Enhance debit ≥ 1 (2)
|
||||
- Total == sum of three (6)
|
||||
- Credits API == DB
|
||||
- Usage API == DB
|
||||
- `/auth/me` == DB
|
||||
- Plan Enterprise; active company Local Demo Co
|
||||
- Open cycle `credits_used` == wallet `used_credits`
|
||||
|
||||
Artifact: `artifacts/live-credits-summary.json` (`seeded: false`).
|
||||
|
||||
## Restore policy
|
||||
|
||||
Restore Enterprise **1,000,000 / 0** only if the demo wallet is depleted enough to block QA (e.g. near empty or wrong plan).
|
||||
After this run: **28 used / 999,972 remaining** — **no restore**.
|
||||
|
||||
If restore is ever required for ops (not as debit proof):
|
||||
|
||||
```sql
|
||||
-- operational reset only — not a substitute for live debit proof
|
||||
UPDATE credit_balances
|
||||
SET total_credits = 1000000, used_credits = 0, updated_at = now()
|
||||
WHERE company_id = 'ee246275-dec0-4446-9e83-58d0c16c258a';
|
||||
```
|
||||
|
||||
Prefer `AssignPlan`/admin billing over raw SQL when resetting plan windows; still do **not** treat that reset as evidence that debits work.
|
||||
|
||||
## Reproduce (PowerShell sketch)
|
||||
|
||||
```powershell
|
||||
# Session against web origin so Billing UI cookies apply
|
||||
$web = "http://127.0.0.1:5174"
|
||||
$jar = "artifacts/live-credits-run-cookies.txt"
|
||||
# GET /api/auth/me → CSRF; POST /api/auth/login; POST /api/auth/select-company
|
||||
# Record GET /api/billing/credits + DB credit_balances
|
||||
# POST /api/seo/apply {"product_id":"d2c309ed-fa99-4785-aa72-4f7d180f1f85","mode":"ai"}
|
||||
# POST /api/campaigns {"name":"…","template_key":"spring","use_default_prompt":true}
|
||||
# POST /api/campaigns/{id}/generate {"mode":"ai"}
|
||||
# POST /api/processing/jobs {"raw_product_ids":["fa4d70c7-938a-405e-ada7-f7341eb64b53"],"processing_type":"enhance_only"}
|
||||
# Poll job; re-read credits / usage / me / DB; assert deltas
|
||||
```
|
||||
|
||||
Use raw JSON bodies (avoid PowerShell `ConvertTo-Json` boolean/`True` quirks on create).
|
||||
|
||||
## Related
|
||||
|
||||
- [billing-credits-audit.md](billing-credits-audit.md) — prior grant/debit/UI fixes
|
||||
- [ai-full-smoke.md](ai-full-smoke.md) — Green Chat feature smoke
|
||||
- [demo-user.md](demo-user.md) — credentials / Enterprise pack
|
||||
- [free-tier.md](free-tier.md) — Free = 0 AI credits
|
||||
Reference in New Issue
Block a user