feat(speculation): add probability path scorer#333
Open
behinddwalls wants to merge 1 commit into
Open
Conversation
## Summary ### Why? The pathscorer seam has no real implementation — only mocks and a programmable fake — yet its output is the ranking currency the selector and prioritizer act on. Speculation needs a default scorer whose scores mean something from the start: inert while trees hold a single path, but ranking chain-vs-fallback paths correctly the moment an enumerator produces alternatives. It is split out from the parity-stub impls because it is the one seam implementation with an actual model, worth reviewing on its own. ### What? `pathscorer/probability` — scores each path as the probability that exactly that path's assumption holds: `Score = p(head) × Π p(d) for base deps × Π (1−p(d)) for non-base head deps`. Each batch's probability resolves from its state, so resolved outcomes override predictions: `succeeded` → 1, `failed`/`cancelled` → 0, otherwise the score stage's predicted `Batch.Score` (neutral 0.5 when unscored; best-effort `cancelling` keeps its prediction). A dead dependency therefore zeroes every path built on it and boosts every path that excluded it by the full `(1−p)=1` factor — score mass shifts to the paths consistent with reality, with no cross-path coupling. Folding outcomes into path *status* stays the controller's reconcile job; the scorer only recomputes `Score`, over every path regardless of status. Dependencies are read one at a time through the injected `storage.BatchStore` per the store's key/value contract, each batch loaded at most once per call; store errors return wrapped and unclassified. The README walks a three-path worked example (chain / drop-B / alone) before and after a dependency failure. ## Test Plan ✅ `make gazelle && make fmt && bazel test //submitqueue/extension/speculation/...`. Unit coverage: probability-formula table including all state-resolution cases (succeeded/failed/cancelled in and out of base, cancelling keeps prediction, unscored fallbacks), a three-path dependency-failure scenario pinning the score-mass shift, per-dep single store read, error propagation, empty-tree short-circuit.
This was referenced Jul 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Why?
The pathscorer seam has no real implementation — only mocks and a programmable fake — yet its output is the ranking currency the selector and prioritizer act on. Speculation needs a default scorer whose scores mean something from the start: inert while trees hold a single path, but ranking chain-vs-fallback paths correctly the moment an enumerator produces alternatives. It is split out from the parity-stub impls because it is the one seam implementation with an actual model, worth reviewing on its own.
What?
pathscorer/probability— scores each path as the probability that exactly that path's assumption holds:Score = p(head) × Π p(d) for base deps × Π (1−p(d)) for non-base head deps.Each batch's probability resolves from its state, so resolved outcomes override predictions:
succeeded→ 1,failed/cancelled→ 0, otherwise the score stage's predictedBatch.Score(neutral 0.5 when unscored; best-effortcancellingkeeps its prediction). A dead dependency therefore zeroes every path built on it and boosts every path that excluded it by the full(1−p)=1factor — score mass shifts to the paths consistent with reality, with no cross-path coupling. Folding outcomes into path status stays the controller's reconcile job; the scorer only recomputesScore, over every path regardless of status.Dependencies are read one at a time through the injected
storage.BatchStoreper the store's key/value contract, each batch loaded at most once per call; store errors return wrapped and unclassified. The README walks a three-path worked example (chain / drop-B / alone) before and after a dependency failure.Test Plan
✅
make gazelle && make fmt && bazel test //submitqueue/extension/speculation/.... Unit coverage: probability-formula table including all state-resolution cases (succeeded/failed/cancelled in and out of base, cancelling keeps prediction, unscored fallbacks), a three-path dependency-failure scenario pinning the score-mass shift, per-dep single store read, error propagation, empty-tree short-circuit.Stack