@cfxlabsinc/b2b-services
    Preparing search index...
    type QuoteOutput = {
        fees: ProductFee[];
        limitEvaluation: LimitEvaluation | null;
        minSettlementDate: Temporal.PlainDate;
        product: ProductRow;
        quote: QuoteShape | null;
        route: ResolvedRoute;
    }
    Index
    fees: ProductFee[]

    Effective fee template (rule + provider clip applied where applicable).

    limitEvaluation: LimitEvaluation | null

    Phase A's verdict — what the limit evaluation concluded, with no enforcement attached. Purely additive: every field above is unchanged, so existing callers that destructure fees / quote / route are unaffected.

    On estimate() this is the prediction: {outcome:'LIMIT_EXCEEDED', …} means "this would be rejected if the caller went on to quote() without a bypass". estimate() still returns ok — it never blocks, by design. Surfaces can use it to grey out an option or show "over your daily limit" without a second call.

    On quote() / update() it is the same value written to the audit row's limit_evaluation; it diverges from the returned error only for an authorized bypassLimits caller.

    null only when no evaluation ran: the idempotency-replay path on a pre-migration row that has no stored verdict.

    minSettlementDate: Temporal.PlainDate

    Earliest date funds settle by, computed as now + route.data.settlementBusinessDays (Mon–Fri, no holiday calendar) in route.data.cutoffTimezone. Operations uses this as a settle-by target; consumer-facing surfaces are free to display or hide it.

    product: ProductRow

    The quoted product. A rail service writes its row value from method.

    quote: QuoteShape | null

    Concrete amounts and fee items. null when amount was null.