Wire @caia/billing (PR #607) into @chiefaia/claude-spawner so per-tenant
Claude usage is metered + invoiced via Stripe. Stripe API keys are not yet in
Infisical, so this PR ships in stub mode: real plumbing + structure,
gracefully degrades to a no-op meter when STRIPE_SECRET_KEY is absent. When
the operator drops the key into Infisical at caia_global.billing.stripe_secret_key,
the wiring activates automatically on the next monthly cron tick.
@caia/billing(PR #607) — extended; not forked.@chiefaia/claude-spawner— extended with a duck-typedUsageMeterHookcallback; no dep cycle into billing.@caia/secrets-adapter— used via.list()for BYOK detection.@chiefaia/tracing— used for warning logs + thewithClaudeSpawnerSpanwrapper the wizard routes already use.pgis a runtime dep of the dashboard (already declared); no new package.
reuseSearchResults:
- @caia/billing
- @chiefaia/claude-spawner
- @caia/secrets-adapter
- @chiefaia/tracing
@caia/billing:src/usage-meter.ts,src/claude-spawner-meter-hook.ts,migrations/0003_tenant_usage_meter.sql, public re-exports.@chiefaia/claude-spawner:UsageMeterContext+UsageMeterHookplumbed throughSpawnClaudeInput; post-spawn hook invocation inside the OTel span.apps/dashboard/scripts/aggregate-usage.ts: monthly cron.apps/wizard/lib/wizard/claude-meter.ts: drop-in helper for wizard routes, plustier-resolver.tslookup helper.interview/answerroute wired as the canonical example.- Tests: 30 new vitest cases in
tests/usage-meter.test.ts(+ 5 spawner hook tests) covering real-key path, stub-mode path, BYOK skip, aggregation correctness, monthly idempotency. - README operator runbook excerpt documenting stub-vs-live boundary and Stripe key activation flow.
- Spawner integration ships.
- Tests pass in both real-key (auto-skipped without env var) and stub-mode paths.
- Migration adds the per-tenant table.
- Cron script exists + README documents operator-side setup.
- True-Zero admin-merge after CI green (tolerate pre-existing TS2352 + lighthouse fails).