> ## Documentation Index
> Fetch the complete documentation index at: https://playbook.smedleygroup.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Race Strategy Tool

> The six-widget strategy suite: an external race-solve engine's predictions, surfaced through one shared derivation layer.

## Overview

The Race Strategy Tool is a family of six Watchparty widgets that show what an external strategy-solving engine predicts for the rest of a race: **Strategy Status**, **Strategy Table**, **Predicted Race History**, **Predicted Pylon**, **Predicted Pit Window and SC**, and **Stop Sensitivity**. All six are race-only, all six read the same per-lap engine solve, and all six are built on one shared derivation module so their honest-state rules never drift apart.

> [!NOTE]
>
> The engine behind this tool is an interim build (`0.5.0-interim`). Probabilities are a uniform 1-in-n split, not a calibrated likelihood, and safety-car-now guidance is not yet implemented. Every widget says so in place rather than hiding the caveat.

***

## How It Works

**The engine.** `services/strategy` feeds live race state into an external RST (race strategy) engine, a compiled binary treated as a black box behind a versioned file-based contract (`services/strategy/engine/CONTRACT.md`). For each lap that settles, the engine runs a deterministic stage and a game-theory stage and returns four outputs: `race_history` (predicted cumulative time per driver per remaining lap), `strategy_rankings` (ranked stop-lap strategies per driver), `sensitivity` (predicted total time against stop lap), and `sc_now` (a safety-car-now recommendation, currently unimplemented).

**Publishing.** A solved lap produces a `strategy_snapshot` payload (the full engine output for that lap); every evaluated lap, solved or not, produces a `strategy_status` payload (gate result, skip reasons, degradations, residuals, timings). Both are published as `metric_outputs` rows and streamed as insight envelopes: `insight:strategy_snapshot` and `insight:strategy_status`, over the channel `vpw:v1:{session_key}:insight:{metric_id}`. `model_id` is `strategy_snapshot` / `strategy_status`; `model_version` is the composite `engine_version+WRAPPER_VERSION`, currently `0.5.0-interim+b1.9.0`.

**Hydration.** The webapp reads both metrics the same way every other insight does: a REST snapshot on session **enter** and on **rehydrate** (`GET /v1/sessions/{session_key}/metrics?metric_id=strategy_snapshot`, likewise for `strategy_status`), then live updates over the WebSocket channels above. No gateway or contract change was needed to add these two metrics; channel and endpoint derive from `metric_id` alone.

**The shared derivation layer.** `apps/pitwall-web/src/screens/strategyDerive.ts` is the one place all seven widgets' data passes through. It owns:

* **As-of-lap selection.** The six data widgets (Table, Predicted Race History, Predicted Pylon, Predicted Podium, Predicted Pit Window and SC, Stop Sensitivity) read the latest `strategy_snapshot` at or before the playhead lap, and stamp "as of lap N" when that snapshot is behind the playhead. The Status panel is the exception: it reads the exact playhead lap's `strategy_status` row, so a degraded or skipped lap is reported honestly rather than silently inheriting a neighbouring lap's data.
* **The 1e300 / `DBL_MAX` sentinel rule.** The engine writes the C++ `DBL_MAX` sentinel (`1.7976931348623157e308`) into any `predicted_time` or `delta` value for a strategy that cannot physically complete from the current race state. `strategy_rankings` rows arrive already normalised by the publisher (`null` plus an `unreachable: true` flag), but `race_history.cumulative_time` and `sensitivity.predicted_time` are lifted from the engine verbatim and can still carry the raw sentinel on the wire. `strategyDerive.ts`'s `isUnreachableTime` (magnitude at or above `1e300`) is the single client-side decoding point for that: it truncates a predicted race-history line at the last reachable lap, and drops (and counts) unreachable sensitivity points. No widget ever renders a raw sentinel value.
* **Shared empty-state text.** When no snapshot exists at or before the playhead, all four data widgets show the same message, built from the exact-lap status row's gate and reasons where one exists (`No prediction at lap N: <gate>: <reasons>`), or a generic `No strategy data at lap N.` when there is no status row either.
* **The gate strip.** One cell per evaluated lap, coloured by that lap's gate (`solved`, `degraded`, `skipped`, `suspended`), clickable to scrub the transport when the lap's on-track position is known. This lives in the Status panel but is derived once, in the shared module, for the whole race.

***

## Where You See It in the App

All seven widgets are entries in Watchparty's feature registry (`apps/pitwall-web/src/screens/features.tsx`): `strategystatus`, `strategytable`, `strategyracehistory`, `strategypylon`, `strategypodium`, `strategypitwindow`, `strategysensitivity`. Open them from the widget palette during a **race** session; every one of them renders "Session is not a race" outside a race, since the app never computes a strategy bundle at all for a non-race session.

The suite also has two modes that sit outside the ordinary lap-by-lap solve: a **pre-race** prediction from the round's own race data bundle, and no-write **what-if studies**. Both are covered on [Pre-Race and What-If Studies](/software/internal/watchparty/strategy/pre-race-and-study-mode).

A mixed `model_version` across stored rows (for example, a partial re-precompute) blanks both `strategy_snapshot` and `strategy_status` together, so all seven widgets fall back to the generic no-data state at once rather than one metric silently going stale while the other updates.

***

## FAQ

> **Why do delta and probability sometimes say 'unreachable' instead of a number?**
>
> That strategy cannot physically complete from the current race state (the engine's `DBL_MAX` sentinel, decoded client-side). A rank-1 strategy carrying that sentinel is a contract violation, not a display choice, and renders as an explicit error rather than a smoothed-over number.

> **Is the probability column a real confidence estimate?**
>
> Not on the current engine. It is a uniform 1-in-n split over a driver's valid strategies. Every widget that shows it says so.

***

## Next Steps

> **[Strategy Status](/software/internal/watchparty/strategy/status)**
>
> The gate, reasons, and whole-race health strip that explains the other four widgets.

> **[Strategy Table](/software/internal/watchparty/strategy/table)**
>
> Ranked stop-lap strategies per driver.

> **[Predicted Race History](/software/internal/watchparty/strategy/predicted-race-history)**
>
> Predicted remainder overlaid on the real race-history chart.

> **[Predicted Pylon](/software/internal/watchparty/strategy/predicted-pylon)**
>
> The predicted finishing order as a timing tower.

> **[Predicted Podium](/software/internal/watchparty/strategy/predicted-podium)**
>
> The predicted top three, the head of that same order.

> **[Predicted Pit Window and SC](/software/internal/watchparty/strategy/predicted-pit-window)**
>
> Each driver's next predicted stop, plus safety-car guidance.

> **[Stop Sensitivity](/software/internal/watchparty/strategy/stop-sensitivity)**
>
> Predicted total race time against stop lap.

> **[Pre-Race and What-If Studies](/software/internal/watchparty/strategy/pre-race-and-study-mode)**
>
> The lap-0 prediction, and running the engine against different inputs.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.