Onboarding Economics — collect at setup, consume governed (2026-08-21)

Directive: owner, 2026-08-21 — "These costs must be collected during the customer onboarding process along with API and other connectors" (extending owner ruling 2026-08-20 §2, which produced the tenant setup surface). Branch: claude/onboarding-economics-v1. Status of everything here: implemented (code + tests on the branch); nothing below is deployed or live-verified, and each consumer's arming posture is unchanged.

What already existed (measured before building)

The 2026-08-20 §2 build landed 2026-08-21 on main: /onboarding UI page → BFF tenant-costs routes → gateway tenant_costs.py, storing the net-yield cost block in the tenant vault (mizoki-connector-{tenant}-cost_config), vocabulary parity-pinned, offboarding-covered. Two gaps remained:

  1. Nothing consumed what onboarding collected. services/net-yield read only config/net_yield_costs.yaml (the all-null template — the "5 NULLs" operator item), so the surface could report complete while compute still priced nothing. Measured: zero vault references in services/net-yield/*.py before this branch.
  2. The other declared economics were not collectable. Treasury (F5), the F2 LTV discount factor, and F3 inventory policy had no onboarding surface at all — they remained "owner supplies config" items.

What this branch adds

1. Net-yield vault read leg (services/net-yield/vault_costs.py)

2. Declared-economics collection (tenant_economics.py, gateway)

New vault provider economics_declared on the same surface, one section per consumer, each validated in that consumer's own vocabulary (parity-pinned by path-import in test_tenant_economics.py):

Section Collectable keys Deliberately excluded (named in tests)
treasury (F5) liquidity_floor_usd, declared_cash_position_usd, position_as_of, base_proposal_cap_usd, covenant_max_proposal_usd max_position_age_days, tightening_curve (policy shape)
ltv (F2) quarterly_discount_factor min_observed_quarters, horizon_quarters (governance floors)
inventory (F3) skus{safety_stock_units, overstock_units, holding_cost_per_unit_day}, defaults, freshness fulfillment_nodes, triggers (routing policy)

Rules enforced at entry: refuse-never-guess (unknown keys/types/negatives 422); a declared cash position requires its as-of date (the F5 staleness rule); discount factor in (0, 1]; safety stock below overstock; freshness positive; roster bounded (500). Gap names follow each consumer's own vocabulary; the treasury gap rule mirrors TenantTreasury.usable() and the pin exercises the consumer's dataclass directly. Values are never logged. NON_CATALOG_VAULT_PROVIDERS gains the provider, so tenant-wide erasure (destroy_tenant_vault) covers it. Tests: 21.

COLLECTED is not ARMED. No consumer read path changed for F5/F2/F3: the governance trio's deploy verify step still asserts treasury not_configured, and each lane arms via its own reviewed config step. This store is the merchant-declared source of record those steps install from.

3. UI + BFF (/onboarding page)

Scope exclusions (named, with reasons)

Deploy surfaces on merge (per rule 04)

Service Deploy path Effect on merge
service-marketing-connectors merge-fired CI new routes serve (verify_caller-gated; collection inert until called)
miz-oki-command-center-ui merge-fired CI onboarding card serves (BFF fail-closed)
net-yield dispatch-only (ADR-NY-001) read leg merges but serves only on human dispatch — and stays dark even then until NET_YIELD_COSTS_VAULT is set (double-dark)

Operator tail (in order, when the owner chooses to arm)

  1. Merchant completes /onboarding (costs + declared economics) — collection is live as soon as the gateway/UI deploys land.
  2. Net-yield: RUNBOOK §0b — vault secretAccessor grant → env flag via the reviewed dispatch deploy → /health posture → one order end-to-end.
  3. F5/F2/F3: each lane's existing reviewed arming step, installing the declared values from GET /api/v1/tenant/economics.
← All docsView source on GitHub →