Story 2093: turn credit metering ON, seed every firm $50

Why this exists (the catastrophe it prevents)

The whole credit machine already existed - the append-only ledger, the O(1) balance projection, the atomic per-run debit, the pre-flight gate, the pure money math. All of it was wired but switched off (CREDIT_METERING_ENABLED defaulted false), on purpose: Stories A1+A2 first had to make the broker bill the real provider cost. Metering wrong numbers into real wallets is worse than metering nothing.

A2 has merged, so real cost now flows. Two gaps remained before we could throw the switch:

Satellite view: switch + seed, nothing new invented

firm created ──► seed_starter_credit ($50 → 5,000,000 mc top-up, D-11)
run launches ──► _credit_gate (block if balance ≤ 0 and overage off)   [now enforced]
run completes ─► record_run_debit (real cost, keyed firm_id+run_id)    [now recording]

The gate and the debit code did not change. This story flips their default and guarantees a firm has money on day one.

Airplane view: what changed

SurfaceChange
_build_deps() (clerk_worker/handler.py) Both rollout switches now default true: CREDIT_METERING_ENABLED (record the debit) and CREDIT_GATE_ENFORCED (block a zero-balance firm before egress). Still per-function env-overridable - set either to "false" to roll back without a code deploy. The unit-test seam in runner.ClerkDeps stays False so the loop remains DB-free to test.
usd_to_millicredits() (shared/billing/credits.py) New pure helper: a raw grant conversion at the display peg (100 credits = $1) with no platform fee - a $50 grant is exactly 5,000 credits, unlike a run debit which is marked up.
seed_starter_credit() (shared/billing/credit_ledger_writes.py) New write. Appends one grant/topup ledger row (period NULL) and lands the credits in the never-expiring top-up bucket. Idempotent per firm via a NOT EXISTS guard (the ledger is append-only, so no ON CONFLICT). Runs under staff context, so it sets/restores app.current_firm_id itself - the same idiom as the other new-firm seeds.
firm_starter_credit_usd (shared/settings.py) New config field, default 50.0, alias FIRM_STARTER_CREDIT_USD. The amount is config, never hardcoded; set 0 to disable seeding.
create_firm() (domains/firm/service.py) Calls seed_starter_credit alongside the existing new-firm seeds (labels, matter types, roles), firm-wide.

Microscopic view: the empty period that saves the allowance

The subtle trap: the balance-projection row carries a period ('YYYY-MM') that drives the lazy monthly allowance grant. If the starter seed pre-created that row with the current period, the first run would see "already granted this month" and skip the seat allowance entirely - a new firm would silently lose its seats × 10,000-credit allowance for its creation month.

So the seed writes period = '' (never a real month). The first run's ensure_allowance_and_gate reads it as stale, grants the seat allowance, and leaves the top-up bucket untouched (D-11). Proven end-to-end: a fresh 10-seat firm reads back 105,000,000 mc = 100,000,000 allowance + 5,000,000 starter.

Documented non-goal

This is the truth layer only. The 3× markup, the firm wallet UI, and the monthly per-firm report surface are a separate Billing epic. This story records accurate per-firm consumption; it does not display or mark it up.

Verification

uv run ruff check src/shared/billing src/domains/firm/service.py src/clerk_worker/handler.py src/shared/settings.py
make test-db && uv run pytest tests/unit/test_firm_support_staff.py tests/unit/billing tests/unit/clerk_worker tests/integration/test_app_firm_credits.py -q
# full suite: 2251 passed, 2 skipped

Key assertions: test_usd_to_millicredits_at_peg_no_markup ($50 → 5,000,000 mc, no fee) and test_create_firm_seeds_starter_credit_firm_wide (a real firm created against a real DB reads back a $50 top-up, one period-NULL grant row, and - after the first-run allowance grant - the full 105,000,000 mc balance with the starter preserved).

In one sentence: metering is on, every run debits its real cost against a firm-wide balance, a broke firm is stopped before it spends, and no new firm starts empty.