Skip to content

feat(speculation): add enumerator + dependency-limit extensions#315

Open
behinddwalls wants to merge 1 commit into
mainfrom
preetam/ext/speculation-enumerator
Open

feat(speculation): add enumerator + dependency-limit extensions#315
behinddwalls wants to merge 1 commit into
mainfrom
preetam/ext/speculation-enumerator

Conversation

@behinddwalls

@behinddwalls behinddwalls commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

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

  1. @ feat(speculation): add enumerator + dependency-limit extensions #315
  2. feat(speculation): add path scorer extension #316
  3. feat(speculation): add selector + selection-limit extensions #317
  4. feat(speculation): add prioritizer + prioritization-limit extensions #320
  5. feat(storage,entity): path identity + speculation path→build mapping store #331
  6. feat(speculation): add parity default impls for the speculation seams #332
  7. feat(speculation): add probability path scorer #333

@behinddwalls behinddwalls marked this pull request as ready for review July 8, 2026 21:30
@behinddwalls behinddwalls requested review from a team and sbalabanov as code owners July 8, 2026 21:30
@behinddwalls behinddwalls marked this pull request as draft July 8, 2026 21:52
@behinddwalls behinddwalls force-pushed the preetam/ext/speculation-store branch from 4606cc7 to e0d72ab Compare July 8, 2026 23:13
@behinddwalls behinddwalls force-pushed the preetam/ext/speculation-enumerator branch from e547a50 to 13b7fe1 Compare July 8, 2026 23:13
@behinddwalls behinddwalls marked this pull request as ready for review July 8, 2026 23:17
@behinddwalls behinddwalls force-pushed the preetam/ext/speculation-store branch from e0d72ab to e4bb29a Compare July 9, 2026 17:01
@behinddwalls behinddwalls force-pushed the preetam/ext/speculation-enumerator branch from 13b7fe1 to 86d1bed Compare July 9, 2026 17:02
@behinddwalls behinddwalls marked this pull request as draft July 9, 2026 17:03
@behinddwalls behinddwalls force-pushed the preetam/ext/speculation-store branch from 85b4ff2 to 9971247 Compare July 9, 2026 17:41
@behinddwalls behinddwalls force-pushed the preetam/ext/speculation-enumerator branch from 86d1bed to 3f567d1 Compare July 9, 2026 17:41
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
@behinddwalls behinddwalls marked this pull request as ready for review July 9, 2026 20:36
@behinddwalls behinddwalls force-pushed the preetam/ext/speculation-enumerator branch from 3f567d1 to c22cd35 Compare July 10, 2026 01:31
@behinddwalls behinddwalls changed the base branch from preetam/ext/speculation-store to main July 10, 2026 01:31
@behinddwalls behinddwalls force-pushed the preetam/ext/speculation-enumerator branch from d953f24 to fb3bf4a Compare July 10, 2026 03:33
Comment thread submitqueue/extension/speculation/enumerator/enumerator.go Outdated
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.
@behinddwalls behinddwalls force-pushed the preetam/ext/speculation-enumerator branch from fb3bf4a to 31e4474 Compare July 10, 2026 19:24
@behinddwalls behinddwalls requested a review from albertywu July 10, 2026 19:50
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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

// 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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in arrival order

^ unclear what arrival order of batches has to do with enumeration?


// 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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e.trees[batchID] = cloneTree(tree)

^ prevents callers from mutating the tree stored inside the fake

return entity.SpeculationTree{}, e.err
}
if tree, ok := e.trees[batch.ID]; ok {
return tree, nil

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return cloneTree(tree), nil)

^ prevents callers from mutating the tree stored inside the fake

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants