feat(speculation): add enumerator + dependency-limit extensions#315
Open
behinddwalls wants to merge 1 commit into
Open
feat(speculation): add enumerator + dependency-limit extensions#315behinddwalls wants to merge 1 commit into
behinddwalls wants to merge 1 commit into
Conversation
This was referenced Jul 8, 2026
4606cc7 to
e0d72ab
Compare
e547a50 to
13b7fe1
Compare
e0d72ab to
e4bb29a
Compare
13b7fe1 to
86d1bed
Compare
85b4ff2 to
9971247
Compare
86d1bed to
3f567d1
Compare
behinddwalls
added a commit
that referenced
this pull request
Jul 9, 2026
## Summary
Reshape the speculation tree data model around the Base/Head path that
the build stage actually consumes, and align its store with the
speculation design.
entity: SpeculationPath{Base, Head} becomes the unit;
SpeculationPathInfo carries the path plus its Score, controller-owned
Status
(candidate/selected/prioritized/building/passed/failed/cancelling/cancelled),
and BuildID — Path is immutable once persisted, Score/Status/BuildID are
updateable by the controller. Score is scorer-computed and
controller-persisted dynamic state — recomputed on every respeculate,
not set at enumeration. Selected/Prioritized and Cancelling/Cancelled
split desire vs budget-cleared and cancel-intent vs terminal, so the
persisted status carries the cross-stage contract. Adds
SpeculationPathAction (Promote/Cancel) and SpeculationPathDecision for
the selector and prioritizer seams. SpeculationTree.Speculations becomes
Paths, and the tree gains a Version for optimistic locking (starts at 1,
incremented per change). The Build entity uses the shared
SpeculationPath (Head = the batch under verification) and drops its own
Score, which was never populated or read.
storage: SpeculationTreeStore.UpdateSpeculations(batchID,
[]SpeculationInfo) becomes a pure conditional write — Update(ctx,
batchID, oldVersion, newVersion, paths) — returning ErrVersionMismatch
when the persisted version does not match, per the repo's
optimistic-locking pattern (version arithmetic owned by the controller).
The MySQL impl treats rowsAffected == 1 as success, 0 as version
mismatch, and anything else as a generic error. The MySQL column
speculations is renamed paths and the schema gains a version column.
MySQL impl, mock, and storage integration coverage (create/get
round-trip, duplicate, versioned whole-tree overwrite, stale-write
rejection, not-found) added/updated.
## Test Plan
## Issues
## Stack
1. @ #231
1. #315
1. #316
1. #317
1. #320
3f567d1 to
c22cd35
Compare
d953f24 to
fb3bf4a
Compare
albertywu
reviewed
Jul 10, 2026
Add the first speculation seam and its limit counterpart from the speculation RFC, as vendor-agnostic extension interfaces under submitqueue/extension/speculation/. enumerator: given a batch and its ordered active dependencies, mechanically lists the candidate Base/Head paths — pure, deterministic, and purely structural. It sets no score and no status; the controller stamps status on persist and the scorer fills score. (Its earlier scoring responsibility is dropped here, per the RFC, and moves to the scorer seam.) dependencylimit: the "how much" policy bounding how many active (in-flight) dependencies a batch may speculate over. It is the eligibility gate before enumeration; unlike the other speculation limits it is controller-held rather than injected into a seam, keeping the enumerator pure. The value is signal-driven, not a fixed constant. Each follows the repo extension contract (conflict.Analyzer reference shape): Factory.For(Config) (T, error) with Config carrying only QueueName; behavioral knobs and limit signals are integrator-injected at construction. Includes READMEs, gomock packages, and Makefile mock-gen wiring. Interfaces only; concrete impls and controller wiring are deferred.
fb3bf4a to
31e4474
Compare
albertywu
reviewed
Jul 10, 2026
Comment on lines
+40
to
+46
| // active dependency batches in arrival order. Each returned path carries a | ||
| // Base/Head split only: Score and Status are left unset — the controller | ||
| // stamps Status on persist and calls the scorer to fill Score. | ||
| // | ||
| // Enumeration is pure and deterministic: the same (batch, deps) always | ||
| // yields the same tree, so callers may regenerate safely. | ||
| Enumerate(ctx context.Context, batch entity.Batch, deps []entity.Batch) (entity.SpeculationTree, error) |
Contributor
There was a problem hiding this comment.
Score and Status are left unset
^ entity.SpeculationTree contains controller-owned Score, Status info. Should we return []entity.SpeculationPath and let the controller construct the persisted tree so there's clear ownership boundaries?
albertywu
reviewed
Jul 10, 2026
| // speculate over. | ||
| type Enumerator interface { | ||
| // Enumerate returns the speculation tree structure for the batch, given its | ||
| // active dependency batches in arrival order. Each returned path carries a |
Contributor
There was a problem hiding this comment.
in arrival order
^ unclear what arrival order of batches has to do with enumeration?
albertywu
reviewed
Jul 10, 2026
|
|
||
| // Set seeds the tree returned by Enumerate for the given batch ID. | ||
| func (e *Enumerator) Set(batchID string, tree entity.SpeculationTree) *Enumerator { | ||
| e.trees[batchID] = tree |
Contributor
There was a problem hiding this comment.
e.trees[batchID] = cloneTree(tree)
^ prevents callers from mutating the tree stored inside the fake
albertywu
reviewed
Jul 10, 2026
| return entity.SpeculationTree{}, e.err | ||
| } | ||
| if tree, ok := e.trees[batch.ID]; ok { | ||
| return tree, nil |
Contributor
There was a problem hiding this comment.
return cloneTree(tree), nil)
^ prevents callers from mutating the tree stored inside the fake
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
Add the first speculation seam and its limit counterpart from the speculation RFC, as vendor-agnostic extension interfaces under submitqueue/extension/speculation/.
enumerator: given a batch and its ordered active dependencies, mechanically lists the candidate Base/Head paths — pure, deterministic, and purely structural. It sets no score and no status; the controller stamps status on persist and the scorer fills score. (Its earlier scoring responsibility is dropped here, per the RFC, and moves to the scorer seam.)
dependencylimit: the "how much" policy bounding how many active (in-flight) dependencies a batch may speculate over. It is the eligibility gate before enumeration; unlike the other speculation limits it is controller-held rather than injected into a seam, keeping the enumerator pure. The value is signal-driven, not a fixed constant.
Each follows the repo extension contract (conflict.Analyzer reference shape): Factory.For(Config) (T, error) with Config carrying only QueueName; behavioral knobs and limit signals are integrator-injected at construction. Includes READMEs, gomock packages, and Makefile mock-gen wiring. Interfaces only; concrete impls and controller wiring are deferred.
Test Plan
Issues
Stack