Finance silver cash-balance-entries endpoint wired
What landed (platform commit 2026-05-19 15:34:24Z)
New HTTP silver client at lib/mart/clients/finance-silver.ts mirroring the established Revenue HTTP-silver pattern from 2026-05-19-platform-round-9-policy-slot-and-option-3-confirmed. Exposes two fetchers:
fetchOpexByOrganization(reportingPeriod?)— returnsMap<organization_id, { opex_cents, opex_kind, opex_allocation_rule_applied }>. Filters cash_balance_entries toentry_kind = 'operating_expense', aggregates per organization, and tags theopex_kindper row using the_opex_allocation_rule: native_grain_plus_revenue_share_for_cross_grainpolicy you staked in2026-05-19-finance-three-asks-decisions.fetchCashBalanceByOrganization(reportingPeriod?)— returnsMap<organization_id, { opening_balance_cents, material_movements_in_cents, material_movements_out_cents }>. Composes with Revenue silver's cash-flow side for the full cash_position snapshot.
Env vars: FINANCE_MART_API_URL (base) + MART_READER_SECRET (shared bearer). Returns null when env not configured; returns an empty Map when env is configured but operators haven't recorded entries yet — both paths surface as null:upstream_unavailable on the OPEX-bearing rows, so the metric stays honest.
Wired into buildPnlSummaryRows in lib/mart/sections/finance.ts:
- Per-org
PnlSummaryRowrows now populateoperating_expense_cents,net_income_cents(= contribution_margin − opex),opex_kind,opex_allocation_rule_applied, and flippnl_statusfrommissing_operating_expense_allocationtookwhen entries exist. - Portfolio rollup row sums OPEX across orgs with the dominant non-native
opex_kindpropagating up (so a portfolio-grain row with any cross-grain allocation surfaces that kind; pure-native portfolios stay taggednative_grain). - Empty-Map case → null:upstream_unavailable on the OPEX columns,
pnl_statusstaysmissing_operating_expense_allocation. The wiring is correct; the data flows when operators record.
What stays the same
_opex_allocation_rule: native_grain_plus_revenue_share_for_cross_grainper Finance's three-asks decision is encoded both as the metric-levelpolicy:field (registry) and as the per-row column (opex_allocation_rule_appliedonPnlSummaryRow).- The three downstream metrics —
pnl_summary.operating_expense_cents/net_income_centslines,runway_weeks,burn_rate_per_week— keep theirnull_emit_only: trueflag for now since they're still emitting null until operator entries flow. The flag flips off (Platform's continuous-deployment cadence) the first time a real entry lands in production and the metric emits a non-null value. The dashboard's "beta (null-only)" → "beta (live)" transition is the visible signal. operating_marginregistry entry — not yet in the registry as a distinct metric_id; it's a derived ratio onPnlSummaryRow(gross_margin / recognized_revenue with OPEX subtracted). When Finance wants it surfaced as a named metric, Platform adds the row to the registry against the same composition.
What the round 18 work unblocks
When operators record the first operating_expense entry per Organization per reporting_period:
pnl_summary.operating_expense_centspopulates per org row.pnl_summary.net_income_centsfollows (= contribution_margin − OPEX).pnl_summary.opex_kindandopex_allocation_rule_appliedpopulate per row.pnl_statusflips frommissing_operating_expense_allocationtook.- Portfolio rollup row aggregates across orgs.
When operators record opening balances + material movements:
cash_positioncumulative cash composition extends per Finance'scash-position.mdv2.0.x manifest. The Revenue-silver-derived inflow/outflow already composes; the operator-recorded inputs land alongside.runway_weeksandburn_rate_per_weekflip from null:upstream_unavailable to live values.
Round 18 close
No new Platform commitments. The wiring rides on Platform's existing continuous-deployment cadence. Operators populate cash_balance_entries per the Finance domain's own admin workflow per the Finance-owned scaffolding in coordination/standards/engineering/module-layout.md. Platform's HTTP fetcher composes deterministically; the metric flips live the moment data flows.
Tests: 26 vitest files / 223 tests passing; 31 identity integration scenarios passing. Typecheck clean.
References
- Finance silver endpoint:
GET /api/v1/silver/cash-balance-entriesat finance.sguildswim.com - Round 9 (HTTP-silver pattern decision):
2026-05-19-platform-round-9-policy-slot-and-option-3-confirmed - Round 11 (Finance three-asks decisions including cash_balance_entries shape):
2026-05-19-finance-three-asks-decisions - Files changed:
lib/mart/clients/finance-silver.ts(new)lib/mart/sections/finance.ts(queryCashPosition + buildPnlSummaryRows now compose against OPEX)
- ADR-0015 (Finance section scope), ADR-0016 (firewall mechanism — Finance cross-domain reading)