Fleet goals
One goal per population — score every member with a zero-shot foundation model, rank them, and get a live leaderboard with verified receipts.
A fleet goal watches a population of homogeneous signals instead of a single target: 3,000 vibration sensors, every SKU's daily sales, one series per customer. Each scoring run forecasts every member, converts the forecast into one calibrated score, and rebuilds a ranked leaderboard — the answer to "which ones matter right now?", not "what is the value of this one?"
create ──▶ resolve members ──▶ bake-off picks the model ──▶ scoring run
│
leaderboard ◀── rank ◀── score every member (batched) ──┘
│
├── receipts — each matured run verified against what happened
├── drivers — a hidden companion goal on the fleet aggregate
├── scenarios — shock a shared driver, watch the board reorder
└── alerts — board-shaped conditions, digest notificationsFleet goals reuse the goal object (POST /v1/goals, same lifecycle, same
alerts and realtime plumbing) with goal_kind: "fleet". Members are scored
zero-shot by a foundation model chosen empirically per fleet — no
per-member training, so a 5,000-member fleet costs a batched inference
pass, not 5,000 tournaments.
Two goal types
goal_type | Score | Read it as |
|---|---|---|
rank_by_risk | Probability the member breaches its threshold within the horizon, in [0, 1] | which members are about to go wrong |
rank_by_growth | Calibrated predicted change over the horizon | which members are about to move most |
For rank_by_risk, the threshold defaults to auto: each member gets its
own level from its recent behaviour, which keeps scores comparable across
members with different scales. Pass an absolute threshold only when the
whole fleet shares a unit.
Create a fleet goal
The target is a selector, not a key — a signal group or a glob pattern. Membership re-resolves on every run, so new signals join automatically:
curl -X POST "$API_BASE/v1/goals" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Workspace-Id: $WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{
"goal_kind": "fleet",
"goal_type": "rank_by_risk",
"name": "Pump fleet — failure watch",
"target_selector": { "type": "pattern", "value": "pump_*_vibration" },
"interval_seconds": 3600,
"horizon_bins": 24,
"options": {
"fleet": {
"run_interval_seconds": 3600,
"risk": { "direction": "above" }
}
}
}'from predictai import PredictAI
client = PredictAI(token="pa_live_…", workspace_id="ws_…")
data = client.goals.post_goals(json={
"goal_kind": "fleet",
"goal_type": "rank_by_risk",
"name": "Pump fleet — failure watch",
"target_selector": {"type": "pattern", "value": "pump_*_vibration"},
"interval_seconds": 3600,
"horizon_bins": 24,
"options": {
"fleet": {
"run_interval_seconds": 3600,
"risk": {"direction": "above"},
},
},
})
import { PredictAI } from "@predictai/sdk";
const client = new PredictAI({ token: "pa_live_…", workspaceId: "ws_…" });
const data = await client.goals.postGoals({
json: {
goal_kind: "fleet",
goal_type: "rank_by_risk",
name: "Pump fleet — failure watch",
target_selector: { type: "pattern", value: "pump_*_vibration" },
interval_seconds: 3600,
horizon_bins: 24,
options: {
fleet: {
run_interval_seconds: 3600,
risk: { direction: "above" },
},
},
},
});
{
"data": {
"goal_id": "7c19…",
"goal_kind": "fleet",
"member_count": 2841,
"status": "queued",
"credits_charged": 50
}
}Only goal_kind, goal_type and target_selector are required; horizon
and cadence have static defaults (12 bins, hourly) since there is no single
target to profile. target_selector.type is group (a signal group name)
or pattern (glob: * matches anything, ? one character). Member counts
are capped per plan (fleet_members in your plan limits).
Preview membership and cost before committing:
curl "$API_BASE/v1/goals/fleet/preview?selector_type=pattern&selector_value=pump_*_vibration" \
-H "Authorization: Bearer $TOKEN" -H "X-Workspace-Id: $WORKSPACE_ID"from predictai import PredictAI
client = PredictAI(token="pa_live_…", workspace_id="ws_…")
data = client.goals.get_goals_fleet_preview(params={
"selector_type": "pattern",
"selector_value": "pump_*_vibration",
})
import { PredictAI } from "@predictai/sdk";
const client = new PredictAI({ token: "pa_live_…", workspaceId: "ws_…" });
const data = await client.goals.getGoalsFleetPreview({
params: {
selector_type: "pattern",
selector_value: "pump_*_vibration",
},
});
Returns member_count, a readable sample, within_limit against your
plan, plus bakeoff_cost (one-time, charged at creation) and run_cost
(per scoring run, scales with fleet size).
The bake-off — how the model is chosen
At creation the fleet runs a sample bake-off: every installed, eligible zero-shot foundation family scores a member sample against held-out history, and candidates are judged as rankers, not point forecasters:
- Spearman correlation between the predicted ranking and the realized one — did the ordering hold?
- Top-decile precision — of the members the model put in the top 10%, how many actually ended up there?
The blended winner is stamped on the goal and used for every scoring run.
rank_by_risk fleets only consider families with a native quantile head
(risk is read off the forecast distribution). Inspect it any time:
curl "$API_BASE/v1/goals/$GOAL_ID/fleet/bakeoff" \
-H "Authorization: Bearer $TOKEN" -H "X-Workspace-Id: $WORKSPACE_ID"from predictai import PredictAI
client = PredictAI(token="pa_live_…", workspace_id="ws_…")
data = client.goals.get_goals_by_goal_id_fleet_bakeoff(GOAL_ID)
import { PredictAI } from "@predictai/sdk";
const client = new PredictAI({ token: "pa_live_…", workspaceId: "ws_…" });
const data = await client.goals.getGoalsByGoalIdFleetBakeoff(GOAL_ID);
Scoring runs and the leaderboard
Runs start on the fleet's cadence (options.fleet.run_interval_seconds,
default: the data interval) and are executed as a fan-out of batched
zero-shot inference chunks, reduced into one ranked board. Trigger one
manually with POST /v1/goals/{goal_id}/fleet/run (409 while one is in
flight or before the bake-off finishes).
curl "$API_BASE/v1/goals/$GOAL_ID/fleet/leaderboard?limit=5" \
-H "Authorization: Bearer $TOKEN" -H "X-Workspace-Id: $WORKSPACE_ID"from predictai import PredictAI
client = PredictAI(token="pa_live_…", workspace_id="ws_…")
data = client.goals.get_goals_by_goal_id_fleet_leaderboard(GOAL_ID, params={"limit": 5})
import { PredictAI } from "@predictai/sdk";
const client = new PredictAI({ token: "pa_live_…", workspaceId: "ws_…" });
const data = await client.goals.getGoalsByGoalIdFleetLeaderboard(GOAL_ID, { params: { limit: 5 } });
{
"data": {
"run": {
"run_id": "a01f…", "status": "completed",
"members_total": 2841, "members_scored": 2833,
"model": { "family": "chronos_2", "slug": "chronos-2" },
"stats": { "score_p50": 0.07, "entered_top_decile": 12, "top_decile_size": 284 }
},
"total": 2833,
"rows": [
{
"member_key": "pump_1187_vibration",
"score": 0.93, "rank": 1, "prev_rank": 4, "rank_delta": 3,
"point_summary": 12.4, "baseline": 8.1, "threshold": 11.6,
"quantiles": [9.8, 11.2, 12.4, 13.9, 15.7]
}
],
"histogram": [{ "bucket": 0.0, "count": 1922 }, { "bucket": 0.1, "count": 411 }]
}
}Query params: run_id (default: latest completed), offset/limit,
search (substring on the member key), min_score/max_score,
movers_only, and order=rank|movement (movement = biggest climbers
first). rank_delta is positive when the member climbed. Drill into one
member's score/rank history and recent context with
GET /v1/goals/{goal_id}/fleet/member?key=pump_1187_vibration, and list
past runs with GET /v1/goals/{goal_id}/fleet/runs.
Receipts — is the board trustworthy?
Once a run's horizon has fully elapsed, every prediction is verified against what actually happened, using the same outcome definition the bake-off used. The summary is the trust panel:
curl "$API_BASE/v1/goals/$GOAL_ID/fleet/receipts" \
-H "Authorization: Bearer $TOKEN" -H "X-Workspace-Id: $WORKSPACE_ID"from predictai import PredictAI
client = PredictAI(token="pa_live_…", workspace_id="ws_…")
data = client.goals.get_goals_by_goal_id_fleet_receipts(GOAL_ID)
import { PredictAI } from "@predictai/sdk";
const client = new PredictAI({ token: "pa_live_…", workspaceId: "ws_…" });
const data = await client.goals.getGoalsByGoalIdFleetReceipts(GOAL_ID);
{
"data": {
"receipts": {
"evaluated_runs": 18,
"base_rate": 0.09,
"top_decile_lift": 5.4,
"deciles": [{ "decile": 1, "n": 5112, "realized": 2481, "rate": 0.485 }],
"per_run": [{ "run_id": "a01f…", "top_decile_rate": 0.51, "base_rate": 0.09 }]
}
}
}top_decile_lift is the headline: how much more often the board's top
10% realized the outcome than the fleet average. 1× means the ranking
adds nothing; the per-decile rates should form a downward staircase.
Drivers and scenarios
The fleet maintains aggregate signals (fleet:<goal_id>:mean / :total)
and a hidden companion goal on the mean, which runs ordinary driver
discovery. Validated drivers of the aggregate become shared covariates
injected into every member's scoring — what moves the whole fleet informs
each member's forecast. GET /v1/goals/{goal_id}/fleet/drivers returns the
companion goal id, its status, and the covariate keys in play.
Those covariates power fleet scenarios — shock a shared driver and re-rank the top of the board:
curl -X POST "$API_BASE/v1/goals/$GOAL_ID/fleet/scenarios" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Workspace-Id: $WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{ "shocks": [{ "key": "ambient_temp", "delta_pct": 15 }], "top_n": 100 }'from predictai import PredictAI
client = PredictAI(token="pa_live_…", workspace_id="ws_…")
data = client.goals.post_goals_by_goal_id_fleet_scenarios(GOAL_ID, json={
"shocks": [{"key": "ambient_temp", "delta_pct": 15}],
"top_n": 100,
})
import { PredictAI } from "@predictai/sdk";
const client = new PredictAI({ token: "pa_live_…", workspaceId: "ws_…" });
const data = await client.goals.postGoalsByGoalIdFleetScenarios(GOAL_ID, {
json: {
shocks: [{ key: "ambient_temp", delta_pct: 15 }],
top_n: 100,
},
});
The scenario runs on the worker pool;
goal.fleet.scenario.completed fires over the
realtime WebSocket when the result is ready, or poll
GET /v1/goals/{goal_id}/fleet/scenarios/{scenario_id}. The result lists
each member's base vs shocked score and rank, sorted by impact.
Alerts
Fleet goals use the normal alerts surface with a board-shaped metric vocabulary, evaluated once per completed run:
| Metric | Fires when |
|---|---|
member_score | any member's score (percent) crosses the threshold |
top_decile_entries | more than N members entered the top decile this run |
rank_jump | a member climbed more than N positions in one run |
fleet_median_score | the fleet's median score (percent) crosses the threshold — drift |
Notifications arrive as a digest (the offending members, not one
notification each). Realtime events for the whole lifecycle —
goal.fleet.run.started / .progress / .completed / .failed,
goal.fleet.bakeoff, goal.fleet.scenario.completed — fan out on the
goal's channel.
Cost
| Operation | When | Credit op |
|---|---|---|
| Bake-off | Once at creation (re-queued automatically until a model is stamped) | discovery.fleet_bakeoff |
| Scoring run | Every run, priced in member blocks | discovery.fleet_run |
| Scenario | Per scenario | discovery.fleet_scenario |
Runs are pre-charged and refunded if the run fails to start. Scheduled runs
respect the workspace spend cap. Rates come from your plan's pricing —
/v1/goals/fleet/preview returns them resolved for your account.

