Appearance
AI Generation Usage Wallet Runtime Proof
Date: 2026-07-14
Scope
This proof covers the opt-in internal generation settlement path only:
text
ai-classifier-service -> usage-service check -> wallet balance read
-> mock provider -> wallet USAGE_DEBIT -> usage-service consumeIt does not promote a default Gateway route, make AI the owner of quota or credit data, or make an upstream provider call idempotent.
Static Gates
bash
GOTOOLCHAIN=go1.25.11 go test ./services/ai-classifier-service/... -count=1
GOTOOLCHAIN=go1.25.11 go test ./services/ai-classifier-service/internal/usecase -run TestClassifierServiceChargedGeneration -count=1 -v
make test-monetization-event-chain
make test-ai-classification-job-route-guardThe focused adapter tests verify the four quota checks, wallet debit, four idempotent usage consumes, denied preflight behavior, and the order preflight -> provider -> settlement. The charged-generation recovery tests also prove that a settlement or successful-telemetry failure leaves a pending replay; the same-key retry calls settlement again without another provider execution.
The static gates were first passed on 2026-07-13 and were rerun against the current worktree on 2026-07-14.
Current Verification Run (2026-07-14)
The current worktree passed the following checks:
bash
GOTOOLCHAIN=go1.25.11 go test ./services/ai-classifier-service/... -count=1
make test-ai-generation-settlement-reconciliation
make test-ai-generation-settlement-fault-injection
AI_GENERATION_SETTLEMENT_LOCAL_POSTGRES_CONFIRM=local-postgres \
make test-ai-generation-settlement-local-postgres
AI_GENERATION_SETTLEMENT_RUNTIME_CONFIRM=disposable-runtime \
make test-ai-generation-settlement-runtimeThe HTTP fault injection confirmed one provider execution, four effective usage mutations, one effective wallet debit, and one worker retry after the intentional credits_weekly failure. The local PostgreSQL runner used a fresh initdb cluster under /tmp; its artifact is output/qa/ai-generation-settlement-local-postgres-20260714-103843/.
The Compose runner used a unique project and volumes, real owner binaries, and the same fail-once proxy. The restarted settlement worker recovered the pending replay and the runner removed the project and volumes; its artifact is output/qa/ai-generation-settlement-runtime-20260714-103855/.
Neither runner read or wrote a configured local, shared Docker, or legacy database. This is recovery evidence for the opt-in internal adapter only; it does not promote a default Gateway route or prove browser behavior.
Provider Result Settlement Recovery
P6-048 persists provider output and provider usage as a pending 24-hour replay before it calls the owner settlement APIs. When settlement or the successful AI-owned usage telemetry record fails, a retry for the same organization, actor, and idempotency key bypasses reference loading, preflight, and the provider, then replays settlement with the persisted usage payload. The replay becomes settled only after settlement and telemetry both succeed.
This is a retry-recovery guard, not a distributed transaction. It relies on the existing owner-service idempotency keys and does not compensate a partial wallet/usage sequence. It supplies no provider credential, browser, deployed Gateway, or default-route promotion evidence.
Local Runtime Evidence
- Started
usage-service,wallet-service, andai-classifier-servicewithAI_GENERATION_USAGE_WALLET_ENABLED=trueand the default mock provider. - Seeded isolated account
teacher_ai_generation_wallet_runtime_20260712in organizationorg_ai_generation_wallet_runtime_20260712with all four entitlement limits and 100 AZ Credits through owner APIs. - Posted
POST /v1/ai/generation/previewwith two requested questions. It returned200, two previews, andchargePolicy=usage_wallet_settled. - The first call created one six-credit
USAGE_DEBIT, left the wallet at 94, and consumedcredits_monthly=6,credits_weekly=6,credits_hourly=6, andtokens_monthly=6000. - The identical replay returned
200with the same settled policy. Wallet balance stayed 94, exactly one usage debit remained, and all four counters were unchanged, proving owner-service idempotency keys were reused. - Reset the isolated usage counters through usage-service and wrote a six-credit
REFUNDthrough wallet-service. The wallet returned to 100 and all counters returned to zero. The immutable ledger remains as the correct fixture audit trail.
Current-Run Gateway Evidence
On 2026-07-13, the proof was rerun from a fresh Compose project named hoctapaz-ai-proof-20260713. It enabled AI_GENERATION_USAGE_WALLET_ENABLED=true, used the deterministic mock provider, and seeded only the isolated account teacher_ai_generation_wallet_runtime_20260713 in org_ai_generation_wallet_runtime_20260713 through the owner APIs.
- The first direct internal preview for two questions returned
chargePolicy=usage_wallet_settled, wrote one six-creditUSAGE_DEBIT, and consumed6,6,6, and6000units for the monthly, weekly, hourly, and token quota keys respectively. - Replaying the identical request kept the wallet at
94, retained exactly oneUSAGE_DEBIT, and kept all four counters unchanged. - A temporary Gateway on
127.0.0.1:18086, configured only withroutes.teacher-ai-preview-native-localhost-example.json, accepted the same authenticated teacher request throughPOST /api/questions/ai-generation/generate. It returnedX-Hoctapaz-Gateway-Route: teacher-ai-generation-preview,X-Hoctapaz-Gateway-Route-State: native_write, and the settled policy without a second debit or consumption. - The same public request with a
STUDENTtoken returned403 AUTH_FORBIDDENbefore upstream execution. Wallet and relevant usage counters remained at94,6, and6000.
Captured request/response, route-header, ledger, counter, and summary artifacts are in output/qa/ai-generation-usage-wallet-runtime-20260713/. The temporary Gateway and Compose project were stopped after this proof.
Boundary And Rollback
- The adapter is off unless
AI_GENERATION_USAGE_WALLET_ENABLED=true. - AI calls owner APIs only; it never reads or writes usage/wallet tables.
- Keep
deploy/gateway/routes.jsonactive. The exact teacher preview route is still an opt-in rehearsal and can be rolled back by restoring that route table. - Do not enable a charged non-mock public path until a partial owner-settlement reconciliation or compensation contract, plus browser/runtime rollback evidence, are added.