bool, a choice and a score can all have features, and a judgment does not need relations to have them.
Add features
Name them infeatures. This judgment reads an account’s notes, and keeps the account’s seats, the days since it signed, and the engine’s previous answer:
state.seatsis the number on the account itself.age(attributes.signed_at)is the days sincesigned_at, measured from the account’s ownupdated_atat the revision Brussle judged.previous.pis the engine’s ownpthe last time it judged the account. It is missing for the first answer.
features, and so does its evaluation. A write that changes only a feature, such as state.seats, re-records the answer with no engine call and no charge. With an age feature, so does a write of the same content, because the age moved. From 100 outcomes with 20 of each kind, the fit can weigh the features with the engine’s answer.
A feature change is a version change. Adding, removing or renaming a feature makes a new version, with its own fit. The earlier version’s answers read stale once you activate the new one. A new version starts with the engine’s p until its own outcomes are enough.
What a feature can read
A judgment keeps up to 12 features, from any of these sources:
Ages. An age is a number of days, with a fraction: 36 hours is 1.5.
- Each age is measured from the judged document’s own
updated_at, at the revision Brussle judged, never from a related document’s write or the clock. A write to a related document never moves an age. A new revision of the judged document does, even with the same content. - The time must be an RFC 3339 string, such as
"2026-10-01T09:30:00Z". A number, such as seconds since 1970, or other text is missing. - A time later than the judged document’s
updated_atgives a negative age. age(updated_at)is refused: at the judged revision it is always 0. Name the time you mean, such asage(attributes.approved_at).
previous.p is the engine’s p from the most recent time it judged the document, with the same engine version, under a version of the judgment that asks the same way.
- It is missing for the first answer, after the document is deleted and created again, after the engine version changes, and on a composite, which has no single
p. - It is the engine’s
p, never the corrected one. - When only a feature moves, it stays the same (see reuses).
- Outcomes replayed after the engine changes read it as missing, because their earlier answers came from the other engine.
invalid_request: for example state.refund.amount and a rule on state.refund. Such a feature would read the outcome itself. Brussle checks this when you create a version and when you set rules, against every version of the judgment. A rule’s path on a related document is not checked.
Ask about what will happen
The engine answers well about what a document shows now, and near chance about what will happen. For a question about what will happen:- Ask a narrow question about what the document shows now.
- Keep counts from related documents as
features, with"render": falseso the engine does not read them. - Set a
horizon, and record what happened as outcomes. - Let the fit on your outcomes weigh the engine’s answer and the counts.
held_out_metrics scores features (the features and the engine’s answer together) and features_only (the features alone) on the same held-out outcomes. If features_only scores as well as features, the engine adds nothing your counts do not already carry, and a feature threshold on the counts is enough.
How the fit with features works
- One probability. The answer has no
calibratedobject.pis the selected correction, applied when the answer is read, andraw.pis the engine’s own answer, whose meaning never changes.fitsays which correctionpused:id,interval,run,pinned,fitted_at,outcomes,methodandfrom_previous_epoch.methodisfeatureswhen the fit uses the features, andearlyorfullwhen it corrects only the engine’sp.fit.intervalis the 95% interval ofpat this answer’s features.fitisnullwhilepis the engine’s. - Chosen like any fit. Each run makes a fit that uses the features beside the fit on the engine’s answer alone, under the same minimums. The fit with features is chosen only when it beats both the engine’s
pand the other fit on outcomes it did not use; the outcomes of one document are always held out together. If neither beats the engine,pstays the engine’s. Below 100 outcomes the fit is reported and never applied (readers_need_full). - All readers use
p. Thresholds, filters, ranking, subscriptions and the judgments that read it use the correctedp(on achoiceorscore, the corrected numbers). A threshold on a feature still reads the stored feature. Groups cannot count a judgment with features. The evaluation log and the export keep the engine’spand the features. - Automate on the bound. Queries and subscriptions can filter and rank on
answers.<j>.interval.lowerand.upper. See automate on the bound. - Nothing is rewritten. When the correction changes, each read uses the new one at once, and Brussle brings the judgments that read this one up to date, billed, without a confirm. This happens only when the fit
preads changes; a pin or a promote is confirmed first. See events when the fit changes and roll-ups. - Your thresholds on
p. You chose a threshold against the engine’sp. Oncepis corrected, the same threshold selects different documents, andGETon the judgment shows afit_changedwarning until you set a threshold again. The threshold recommender works from the correctedp. - Templates. Each tenant reads the template’s pooled correction until it has enough outcomes of its own. See per-customer corrections.
Read each feature
In each epoch of the report,held_out_metrics.features gives the fit with features and reads each feature on its own, on the same held-out outcomes:
engine_weight: how much the fit weighs the engine’s answer. It is never negative, so for two answers with the same features, a higher rawpnever reads lower.- Entries come in the order of the definition.
weight: how strongly one standard deviation of the feature movesp, so you can compare features in different units. Positive raisesp; negative lowers it; near 0 means little importance. Here, more seats make a cancellation less likely, and older accounts a little more likely.centerandscale: the feature’s average and standard deviation over the outcomes the fit used. A missing value counts ascenter.missing: the share of those outcomes whose answer had no value. A feature that is often missing can say little.without.log_loss: the held-out log loss of the fit made again without this feature. Here, without seats the loss goes from 0.31 to 0.37.alone.log_loss: the held-out log loss of a fit on this feature alone, without the engine’s answer.warning:reads_like_the_outcome, or absent.
A feature that reads like the outcome
A number written once the outcome is known, such as a refund amount or a cancellation date, predicts the outcome almost perfectly. A fit on it looks very good on your past outcomes, but when an answer is needed the number is not there yet, and the fit is worth nothing. Brussle cannot know when you write a field, but it can see the result: when a feature alone scores below a tenth of the log loss of always answeringbase_rate, its entry has "warning": "reads_like_the_outcome", and the Calibration section shows reads like the outcome.
- Check when your system writes that field.
- If it is written once the outcome is known, post a new version without the feature.
Drop a feature that adds nothing
A feature whosewithout is not higher than the fit’s own log_loss adds nothing the engine and the other features do not already give. A weight near 0 with without equal to log_loss marks a candidate. Drop it in a new version, and keep the budget of 12 for numbers that matter. Compare the new version’s report with the old one before you activate it, or run a shadow report.
Features on a choice or a score
Achoice and a score take features as a bool does: the same sources, the same limit of 12, the same rules. Their fit weighs the features beside the engine’s whole distribution.
- The answer’s numbers are the corrected ones:
dist,escape_pandvalueon achoice;distandscoreon ascore.rawholds the engine’s:{dist, value, escape_p}or{dist, score}. There is nocalibratedobject, andfitnames the correction as on abool. - The pick can change.
valueis the most probable option of the correcteddist. With features, the fit can make another option, ornone_of_the_above, the most probable. Without features, the order among your options never changes: the fit only moves answers toward or away fromnone_of_the_above. - Every reader uses the corrected numbers: thresholds on
dist, filters and ranking onescape_p,score,valueanddist.<option>, subscriptions, the judgments that read it and links. A new fit that changes a pick moves a link to the new document and brings its readers up to date, billed. fit.intervalis onescape_pfor achoiceand onscorefor ascore. Filter and rank on its bounds.- Chosen like any fit: only when it beats the engine’s numbers and the fit without features on outcomes it did not use.
- In the report,
held_out_metricsscores the whole distribution, withaccuracy(and on ascore,mean_level_distance). On a fixedchoice,per_feature[].by_optiongives each feature’s weight on each option, andmoved_picksthe share of picks the fit changes.
Features on a choice among candidates
A choice among candidates (options.from) can keep two kinds of feature:
- Numbers about the judged document, such as
candidates.countor the payment’sstate.amount. They help decide whether a match exists, which is1 - escape_p. - Numbers about each candidate,
<from>.each(<path>), such ascandidates.each(state.open_amount)orcandidates.each(age(created_at)). They help decide which candidate it is.<path>is the candidate’sstate.<path>orattributes.<name>, orage(created_at),age(updated_at)orage(<state or attributes path>)of the candidate, as of the judged document’s revision. Each candidate the engine read keeps its numbers under the answer’scandidate_features, by candidateid. They count toward the 12.
- A candidate the engine gave no probability stays at none. The fit only weighs the candidates on the engine’s shortlist.
- An answer with no candidates stays
none_of_the_above, withescape_p1, whatever the fit. - Without numbers about candidates, the engine’s order among them is kept. With them, a candidate your numbers favour can rise above one they do not, at equal engine confidence: the invoice with nothing paid against it can rise above one already settled.
- Never a worse pick. The fit is used only when, on outcomes it did not use, its pick is right at least as often as the engine’s. A pick is right when it names the true match, or the escape where there was no match. When numbers about candidates would make it pick worse, Brussle fits again without them, keeping the engine’s order.
- A candidate’s answers cannot be a feature.
candidates.each(answers.<judgment>.<part>)isinvalid_request: through a link, it would read the matcher’s own pick back into its next answer. Keep the number on the candidate’sstateand name that. - Not under the promotion rule yet: each run’s fit replaces the current one.
held_out_metrics reads whether a match exists for the four readings, with the ranking, and held_out_metrics.candidates gives the rest:
joint: the held-out log loss of the whole answer (the escape and every candidate): raw, with the fit without features, and with the fit with features. Brussle chooses on it.selection: how often the pick was right, raw (raw_accuracy) and corrected (fitted_accuracy), over every outcome, so a fit that rightly says there is no match counts as picking right.guard.stateisfitted,kept_engine_order(with both accuracies before the fit was made again), ortoo_few_matched(fewer than 20 outcomes name a match among two or more candidates, so the engine’s order holds).accuracy_holdsistruewhen the fit picks at least as well.moved_picks: the share of the outcomes the fit used whose pick it changes. It is a number to read, not a condition.