Skip to main content
If you run the same judgments for many customers, each in its own namespace under a template, Brussle corrects every customer from its first answer with the correction pooled across all of them. On Team and Scale, a customer with enough outcomes of its own also gets its own correction. That correction is held toward the shared one until the customer’s outcomes clearly say otherwise, and is kept or replaced by the same rule as any fit. The report says which correction each customer reads, and why. Here a customer of yours is a tenant, a namespace that inherits the judgment from a template’s prefix.

The pooled correction

Tenants post outcomes on their own namespace paths, never on the prefix, and the correction on the prefix pools them. Brussle makes one fit for each template version and engine epoch, fitted each night across every tenant’s outcomes, and the template’s run makes every tenant’s fit at the same time.
  • A tenant without its own fit reads the pooled fit, so Brussle corrects a tenant you create today from its first answer. The pooled fit was made on other tenants’ outcomes, so it is not yet measured on this one. Once the tenant posts outcomes, the report on its own path measures what it reads against them.
  • Tenants under a non-production prefix, such as acme/staging/, read the pool but add nothing to it and get no fit of their own, unless the template itself is under that prefix.
  • Settings live on the template, so you set implicit negatives, pins, promotions and named runs on the prefix path. A tenant that sets calibration, promotes or makes a named run gets invalid_request. Neither the prefix nor a tenant can set outcome rules.
  • The list of fits on a tenant’s path shows the template’s fits, since those are what its answers read.
  • A new version starts each tenant on that version’s pool again. Tenants keep following the template, and you do not detach anything.
  • A composite keeps one pooled combiner for all tenants.

Each tenant grows its own correction (Team and Scale)

A tenant’s data can differ from the pool’s (another language, a stricter review team, another base rate). So on Team and Scale, a tenant with 50 outcomes of its own in an epoch, 15 of each kind, also gets its own fit. Below that it reads the pool.
  • It starts at the pool’s fit and moves toward the tenant’s own outcomes as they grow, n / (n + 100) of the way on n outcomes. That is one third of the way at 50 and a half at 100. The report shows this as weight, and each corrected answer shows it as prior with kind: pool.
  • It is used only when it is clearly better than what the tenant would read otherwise (the pool or raw), on tenant outcomes that the fit did not see. Until then the tenant reads the pool, or raw if the pool does not beat raw.
  • It is kept by the same rule as the pool’s. Brussle fits the tenant again each night, and the new fit replaces the one the tenant reads only when it is clearly better on the tenant’s own outcomes since, and moves its numbers by at least 0.01. When the pool beats the tenant’s current fit by the same margin on those outcomes, the tenant goes back to the pool. See the rule.
  • When the pool’s fit changes, the pool part of each tenant’s blend changes with it. The pool is tested on every tenant’s outcomes, where one tenant is a small part. Each tenant’s report the next night shows the new blend against the pool, in tenant.current.since.
  • A promote on the template (POST .../calibration/fit with {"promote": true} on the prefix path) makes the newest fit current for the pool and every tenant.
  • Plans. Below Team, each tenant reads the pool. After a downgrade takes effect the next fit puts each tenant back on the pool, and after an upgrade the next fit gives tenants their own fits.

Its own weights over features

A count of open tickets can mean less in one tenant than across the pool. So on a bool with features, a tenant’s own fit has its own weights over the engine’s p and the features, from 100 of its own outcomes with 20 of each kind. Each weight starts at the pool’s, moves toward what the tenant’s outcomes show by the same n / (n + 100), and is kept by the same rule (a feature that none of the tenant’s documents has keeps the pool’s weight). The tenant’s p is then its own fit over the answer’s raw.p and features, and the answer’s fit has "tenant": true and the tenant’s own id. A choice or score with features reads the pooled fit in every tenant.

Which readers are judged again

On a judgment with features, readers act on p. When one tenant’s own fit changes, Brussle judges again the readers in that tenant only. When the pooled fit changes, Brussle judges again the readers in the tenants that read the pool, since the pool does not change a tenant that reads its own fit.

The interval on a tenant

When a tenant reads its own fit blended with the pool, the interval of each corrected number is the same blend of its own fit’s interval and the pool’s, widened to hold its own fit’s interval. The pool pulls the number toward the other tenants. The interval does not hide that: it is never narrower than the tenant’s own outcomes support. A tenant that reads the pool reads the pool’s interval, and on a bool with features, a tenant that reads its own fit reads its own fit’s interval. The corrected number on a tenant that reads a fit of its own blends the tenant’s fit with the template’s fit that fit names. A fit’s formula is the template’s, not the tenant’s own.

Pin a fit for every tenant

A pin holds one pooled fit for one version and epoch, and you set it on the prefix path with PATCH and calibration, as on a namespace.
  • Each tenant reads the pinned fit for that version and epoch, including a tenant that had its own. While the pin holds, no tenant gets its own fit for it, and each tenant’s report shows tenant.reason pinned.
  • Other versions and epochs do not change, and tenants keep their own fits there.
  • On a bool with features, the readers are those of every tenant that inherits the judgment. The PATCH returns their cost in downstream, from up to 100 tenants and scaled to all of them, as for a threshold change. With confirm, one resync job judges again the readers whose view of the judgment changes.
  • When you lift the pin, Brussle brings the next run forward, and on Team and Scale that run can give tenants their own fits again.

Read the report on a tenant

The report and threshold recommendations use the pooled outcomes on the prefix path. On a tenant’s path they use that tenant’s outcomes, and each epoch’s tenant says what the tenant reads:
On a bool with features, tenant also has method and weights:
  • applied is tenant_shrunk (the tenant’s own fit, held toward the pool’s), pooled or raw.
  • weight is how far the tenant’s own fit moves from the pool’s toward its own outcomes, n / (n + 100).
  • method, on a bool with features only, is features, which means the tenant has its own weights over the features.
  • weights, on a bool with features only, gives each input’s weight under the pool’s fit and the tenant’s own, side by side (first the engine’s p as engine, then each feature). The weights share one scale across the template, so you can compare inputs in different units. The difference between the columns is what the tenant’s own outcomes show (in the example, open tickets count about twice as much for this tenant as for the pool). weights is null while the tenant has no fit of its own.
  • reason says why:
    • With its own fit: tenant_better, pooled_better or raw_better.
    • Before that: too_few_outcomes, awaiting_fit (the template’s next fit makes one, within a day, or sooner when you ask for a fit) or plan_required.
    • non_production: the tenant is under a non-production prefix, so it reads the pool and adds nothing to it.
    • pooled_only: a choice or score with features, where each tenant reads the pool.
    • pinned: the template pins a fit for the version and epoch.
  • tenant.current and tenant.challenger hold the tenant’s own fit and the night’s, each measured against the pool (fallback: pooled) on the tenant’s own outcomes since. See read the current fit and the challenger.
The epoch’s stage, held_out, not_fitted and calibrated then describe the fit the tenant reads, measured on its own outcomes. On a tenant with its own fit, held_out_metrics is null and the tenant’s own test gives no holdout or standard errors. lift is null on every tenant, since the template’s report has the pool’s lift. Each epoch’s tenants on the prefix path counts the tenants with outcomes in the epoch, and how many of them read their own fit, the pool or raw.