Skip to main content

Optimizing Curling Draw Schedules

· 26 min read
Dave Rapin
Dave Rapin
Founder @ Curling IO

This post is part of our Curling IO v3 sneak peek series, where we explore some of the new features available in the upcoming version.

Curling IO v3 includes a new draw scheduling screen for event games. The schedule is a grid of draws and club resources, with unassigned games kept in a queue beside it. You can drag and drop games, lock specific placements, and use Allocate and Optimize around those locks.

Unlike a separate schedule template generator, this editor works with the event's actual teams, stages, games, resources, and draw times. Saving the schedule updates the event directly.

You can try most of the scheduling interface now at CurlingSchedules.com. It uses generic teams and browser-local saves instead of an event's actual games, but the grid, drag-and-drop editing, locks, catalog schedules, fairness inspection, and optimization are available today.

A league draw schedule in the Curling IO v3 event editor

The Curling IO v3 schedule editor keeps the game queue, event controls, draw times, resources, and scheduled games in one view.

The Draw Scheduling Tool

Version 2 generates draw schedule templates and uses dropdowns to place games. It does not have this editor, the full canonical catalog, game locks, or local optimization around manual changes.

The v3 editor supports:

  • drag and drop games between the schedule and game queue
  • edit draw times, add draws as games are placed, or delete a draw and return its games to the queue
  • lock a game so Allocate and Optimize leave it where you put it
  • allocate round-robin, ad hoc, and bracket games under their different ordering rules, including queued games when requested
  • fill unused sheets with games from the next logical round when compact draws matter more than keeping every round in a fresh row
  • inspect exactly which games produce the Max, Total, and Back-to-back fairness numbers
  • add an ad hoc game to a specific round robin, so it remains part of that stage's standings and scoring
  • import CSV assignments for existing games without recreating the event's competition structure
  • save the current arrangement as a club draw schedule template, or deliberately apply a matching template
  • start from a validated canonical schedule, then optimize event-specific changes without moving locked games

These are curling-specific scheduling rules. The allocator distinguishes round-robin, ad hoc, and bracket games; understands logical rounds that may span several draws; accounts for selected resources and fixed placements; and scores how often teams return to the same sheet, including consecutive draws. It puts ad hoc games after the affected teams' round-robin games, and unlocked bracket games after round-robin and ad hoc play. A locked game is exempt from those placement rules.

When an event contains one complete round robin and no conflicting locks, Allocate checks our schedule catalog before doing any browser-side search. Multiple iterations repeat the matching catalog layout as a starting point. A matching club draw schedule template is presented separately and is applied only when the drawmaster chooses it. Once locks, ad hoc games, multiple stages, or manual placements change the problem, the browser optimizer works from the actual event schedule.

A locked game and an ad hoc game in the Curling IO v3 event editor

The blue lock marks a placement that automatic allocation must preserve. The queue contains an ad hoc second meeting between A and B, ready to be placed.

See the Draw Scheduling Workflow

This tutorial shows how to configure, allocate, edit, inspect, optimize, save, and reuse an event draw schedule.

For Drawmasters: How We Built the Schedule Catalog

The rest of this article gets technical. It covers how we scored schedules, tested algorithms, used exact solvers, and distinguished best-found schedules from proven optima. You've been warned!

The v3 scheduling system has two computational parts. An offline Rust application searches for, validates, and in some cases proves optimal base schedules. A JavaScript optimizer handles event-specific changes in the browser, including locked games, ad hoc games, and competition stages sharing the same resources.

The offline work produced a versioned catalog covering all 608 combinations from 2 through 20 teams and 1 through 16 sheets, under both supported draw-packing modes. Curling IO v3 loads this catalog directly when allocating an event schedule.

The catalog currently contains 356 layouts, and every one is proven optimal. That includes 354 distinct requirements in the practical catalog envelope and two historical outliers retained for compatibility.

Scheduling Objective

We score sheet fairness as an ordered tuple:

  1. Maximum repeat visits: the largest number of extra visits one team makes to one sheet.
  2. Total repeat visits: every visit by every team after its first visit to a sheet.
  3. Back-to-back repeat visits: cases where a team plays on the same sheet in adjacent draws.

Lower is better. We compare the first number first, then the second, then the third. Runtime never compensates for a worse schedule.

An eight-team, four-sheet result of (1, 24, 0) means no team visits one sheet more than twice, there are 24 repeat visits in total, and nobody returns to the same sheet in consecutive draws.

Those 24 repeats are unavoidable. Eight teams make 56 team-sheet visits across seven draws, but there are only 32 unique team-and-sheet combinations. Even a perfectly balanced schedule has at least 56 - 32 = 24 repeat visits. Reaching that lower bound with a maximum of one extra visit and no consecutive repeats proves that there is nothing left to improve under our scoring rules.

The fairness inspector showing where a team repeats on one sheet

The stats are inspectable in the schedule editor. Selecting one highlights the games and teams that produced it.

We chose an ordered tuple instead of a weighted score because weights hide tradeoffs. Is reducing one team's sixth visit to Sheet A worth creating three new consecutive repeats elsewhere? A scalar score can answer that only after someone invents a conversion rate. The tuple makes the priority explicit.

How Good Is AI for Draw Schedule Generation?

We tested whether current general-purpose AI models could produce these schedules from a normal drawmaster request. This was a direct, one-shot model test, not an agent with access to our validator or a repair loop.

The prompt took some work. Early versions that sounded completely natural often returned missing or duplicate matchups, especially with an odd number of teams. We refined it to approximate the clarifications a drawmaster might give in a short conversation. The final version stated the expected number of games, asked for one Markdown table, and asked the model to check its work. It did not provide the matchup inventory, minimum draw count, fairness tuple, cached schedule, circle method, or any other scheduling algorithm.

Read the exact 8-team, 4-sheet prompt

Create a curling draw schedule for a single round robin with 8 teams and 4 sheets. Label the teams 1 through 8 and the sheets A through D.

The schedule must contain exactly 28 games, one for each unique team pairing. A team cannot play more than once in the same draw.

Try to spread each team's games across the sheets, especially avoiding the same sheet in consecutive draws. Correct pairings matter more than perfect sheet balance.

Before answering, count the games and check for missing or duplicate matchups. Return one completed Markdown table, not a separate table for each draw. Use one row per draw and columns named Draw, Sheet A through Sheet D. Write each game as 1 vs 2, leave unused sheet cells blank, and include a Bye column when needed.

Only the team count, sheet count, labels, and expected game count changed for the other fixtures.

We sent that frozen prompt to OpenAI and Anthropic through their first-party APIs. We added Gemini in a separate pass through OpenRouter, pinned to Google AI Studio with provider fallback disabled. Each model received the same five fixtures once: 8 teams on 4 sheets, 8 on 2, 9 on 4, 11 on 5, and 14 on 7. Every request started with a fresh one-message context, used the provider's default reasoning behaviour, and had a hard five-minute timeout. We kept every response, including timeouts.

The familiar 8-team, 4-sheet layout has been published for years and may appear in model training data. We kept it as a calibration case, not proof that a model derived the answer. We also kept informal consumer-chat experiments out of this table because those products may add hidden instructions, model routing, and reasoning settings.

The results were parsed and checked independently. Harmless formatting could be normalized, but the evaluator could not add or remove a game, change a pairing, or improve a sheet assignment. A schedule had to contain every matchup exactly once, keep each team to one game per draw, and pass the same fairness calculation used by Drawmaster. Invalid schedules did not receive a fairness score.

API and modelValid resultsMedian response timeOutput tokensCalculated cost
OpenAI gpt-5.6-sol5/550.7 seconds21,079$0.64
Anthropic claude-opus-55/5108.3 seconds59,070$1.48
Gemini google/gemini-3-flash-preview2/53.7 seconds3,338$0.01

Reasoning tokens are included in the output totals, not added a second time. One pass over five fixtures is useful baseline evidence, but it is not a statistically strong ranking of the models.

OpenAI and Claude returned a valid, minimum-draw schedule for every fixture. OpenAI matched or beat Claude on all five. Sheet fairness was a different result:

Gemini finished every request in 5.1 seconds or less, but three of its five schedules were invalid. The 8×4 and 14×7 answers repeated matchups despite claiming in their own prose that every pairing had been verified. The 9×4 answer repeated two matchups and scheduled one team twice in its final draw. Its two valid schedules used the minimum number of draws, but neither matched the catalog's fairness score.

FixtureCurrent catalogBest AI resultComparison
8 teams, 4 sheets(1, 24, 0)(2, 24, 0)Curling IO is better
8 teams, 2 sheets(3, 40, 0)(3, 40, 0)Equal, proven optimal
9 teams, 4 sheets(1, 36, 0)(2, 36, 0)Curling IO is better
11 teams, 5 sheets(1, 55, 0)(2, 55, 0)Curling IO is better
14 teams, 7 sheets(1, 84, 0)(2, 84, 0)Curling IO is better

Our simple neutral-start Rust search matched OpenAI's best result on all five fixtures after a few seconds of combined local work per fixture. The much heavier offline process then produced the current catalog, which is better on four of the five fixtures and equal on the fifth.

AI is still useful here as another offline candidate generator. It can find a schedule our existing methods missed. We just treat its answer the same as any other untrusted candidate: parse it, validate every matchup and conflict, calculate the score ourselves, and prove optimality when we can. For custom event schedules, the browser optimizer works around locks and extra games under a 100 ms deadline instead of asking a general model to rebuild the schedule from prose.

Search Envelope

Our first practical envelope contains:

  • 2 through 20 teams
  • 1 through 16 selected sheets
  • one complete round robin
  • one iteration
  • no locked, ad hoc, or bracket games
  • two behaviours when one logical round spans multiple draws: start each round in a fresh draw, or fill blank sheets with games from the next round

That is 19 × 16 × 2 = 608 lookup keys.

The two packing settings cannot always produce different layouts. If all games in a round fit in one draw, for example, there are no blank sheets for the next round to fill. Aliasing those equivalent cases reduces the envelope to 354 distinct layout requirements. The catalog stores 356 layouts because it also retains two historically used shapes outside the envelope, 22 teams on 6 sheets and 26 teams on 7 sheets.

This boundary was not arbitrary. We analyzed Curling IO data. Among thousands of generated, single-iteration schedules where sheets were scarce, 98% had at most 20 teams and 8 sheets. The five most common shapes were 8×4, 12×6, 6×3, 10×5, and 9×4. Together they accounted for 63% of that cohort.

We extended sheet coverage to 16 because surplus-sheet layouts are cheap to store and useful to support. The 42-team outliers in the historical data did not justify expanding the initial interactive target.

Research Harness

The original CurlingSchedules implementation already had a circular round-robin generator, a greedy sheet allocator, local cleanup moves, and a small exact-match cache. We had also done a lot of experimentation, including a simulated annealing branch.

The first issue was measurement. Some historical runs used different weights, some used an older definition of maximum repeats, and some tests did not assert what their names claimed. Results using different objective functions could not be compared directly.

We built an application in Rust as a research harness with:

  • one reference schedule model and independent validator
  • deterministic fixtures and seeds
  • swappable search strategies
  • paired comparisons under equal budgets
  • text, JSON, and CSV reports
  • production-frequency weighting from Curling IO data
  • resumable, atomic checkpoints for long-running searches
  • a versioned catalog with the method, seed, effort, runtime, lower bound, and proof status attached to every layout

The search and validation workflow used to add a schedule to the canonical catalog

Generated schedules pass through heuristic search, exact methods, and an independent validator before entering the versioned catalog.

Cached layouts were comparison targets, never search inputs. Each algorithm had to produce its result from the same generated starting conditions.

Rust was a good fit for this work for many of the same reasons we chose it for the Curling IO v3 backend. Native execution let us evaluate large numbers of candidates, and the type system made it difficult to mix an invalid partial schedule into a supposedly valid result. The same harness could run bounded comparisons or resume long exact searches. This was a small research project we knew we would change constantly, but correctness still matters.

Evaluated Algorithms

No single algorithm won across every fixture. The final offline process uses several methods with different costs and strengths.

ApproachWhat it was good atWhat we learned
Greedy generationProducing a valid deterministic baseline quicklyLocal sheet choices are myopic, and more random rebuilds do not guarantee a better basin
Single, double, triple, and quadruple swapsCheap local improvementsTwo and three moves can escape some local optima, but deeper is not automatically better
Targeted movesSpending work on teams and sheets blocking the next fairness reductionReusing scoring ledgers improved both quality and speed
Tabu and plateau walkingMoving through equal-scoring statesStrict improvement-only search gets stuck too early
Greedy reconstructionEntering a different part of the search spaceIndependent restarts were more useful than repeatedly polishing one schedule
Elite pools and populationsPreserving several strong, different candidatesMore diversity does not automatically become better schedules
Simulated annealingOccasionally improving an unresolved fixtureIt was not a competitive general strategy under our measured budgets
Ruin-and-recreate, crossover, rotations, and sheet-column movesAdding specific neighbourhoods to a portfolioMost were fixture-sensitive, and several were useful only as negative controls
Constraint solving and branch-and-boundProving a target reachable or impossibleExact methods work best after the problem is decomposed correctly
Combinatorial constructionsSolving whole families directlySometimes the right answer is a design, not more search

The simplest optimizer tries one legal game swap and keeps it only if the tuple score improves. Our depth-two version tries a second swap even when the first move is awful, then keeps both only if their combined result beats the untouched schedule. Across 20 neutral eight-team starts, depth two beat single-swap search 16 times and reached the known optimum twice.

Depth three improved the odds again, but it did not dominate depth two. Across 1,000 paired starts it won 394, tied 310, and lost 296. Depth four then lost more often than it won against depth three. Bigger neighbourhoods created new paths, but they also spent more of a fixed budget wandering through unhelpful ones.

Targeting helped more consistently. We aimed most source-game choices at the team-sheet assignments blocking the next maximum-repeat reduction. Then we added tabu memory so the search could walk across previously unseen equal-fairness states instead of bouncing between the same arrangements.

Still, the classic 8×4 case exposed the limit of local moves. One exact check proved that a particular matchup grouping could do no better than (1,24,5). The global optimum was (1,24,0). No amount of sheet shuffling inside that grouping could close the gap because the pairings had to be regrouped across draws.

This is a limit of local search. Some improvements require changing which games share a draw, not only changing their sheet assignments.

Simulated Annealing

We tried simulated annealing twice, first in the old Gleam implementation and again as a deliberately smaller Rust experiment. The larger historical version had temperature schedules, reheating, restarts, adaptive move weights, sheet cycles, draw rotations, targeted moves, and several phases. It retained the best candidate, so returning a hot degraded state did not explain its performance.

Considering the effort we put into understanding and implementing simulated annealing, the results were disappointing. The smaller Rust version made the comparison easier to trust. It remained competitive on 8×4 but lost across the broader fixture suite. Annealing added some portfolio diversity on one shape, but the evidence did not support using it as a general strategy.

We retained the negative result in the research record. Further temperature tuning was lower priority than approaches that improved more fixtures under the same evaluation budget.

Reconstruction and Portfolios

Greedy reconstruction keeps the draws and competition rules but rebuilds sheet assignments from a new seeded ordering. Five independent reconstructed starts beat five ordinary starts under the same total evaluation budget. It did not solve every fixture, but it entered basins that swap-only search never saw.

Our offline portfolio eventually kept independent reconstruction and population workers, plus a separate lane that exploited the best validated candidate found so far. We tried forcing every worker back to the global best, but that reduced diversity and did not help. Sharing became useful only when it was additional work rather than a replacement for each worker's private search.

This is why we added checkpoints to the offline runner for each worker's private state as well as the global best. A long search can stop and resume with both the global best and the independent state of each worker intact.

Exact Methods and Constructions

Heuristic search gave us strong candidates, but proving optimality required exact methods.

We added exact methods in layers instead of handing the entire problem to one solver:

  1. Fixed-draw sheet assignment keeps matchups and draw membership fixed, then solves only which sheet hosts each game.
  2. Logical-round models can repartition games across the draws belonging to one round while preserving round boundaries.
  3. Packed factorization search chooses both the matchup grouping and sheet assignment for smaller full-draw schedules.
  4. Bounded repair queries ask whether a known target can be reached within a radius of a strong incumbent.
  5. Combinatorial constructions directly generate known design families such as partitioned balanced tournament designs.

The fixed-draw constraint solver brought several common layouts to their global lower bounds. A later exact pass promoted 64 of 74 previously unresolved high-sheet layouts to proven optimal, about 86%. Packed branch-and-bound also proved the 10×5 lower bound from generated input without reading the cached schedule, and handled small odd-team schedules using near-perfect matchings.

The exact results also identified limits in the search formulation. A 16×8 round robin is a partitioned balanced tournament design of side eight. A published starter-adder construction produced the global (1,112,0) lower bound directly. Local search had stalled because a strong heuristic schedule needed at least 41 game-sheet placements changed under its existing draw composition.

For 19 teams on 9 sheets, bounded repair could not reach (1,171,17) within 10 changed cells. We removed redundant variables and encoded the lower-bound condition directly: every team uses every sheet exactly twice. Z3 found (1,171,0) on the existing cyclic matchup structure. The solution differed from the starting sheet labels in 151 of 171 game cells, about 88%. The local repair radius had excluded the relevant solutions.

We then ran an exact round-order finishing pass. It treats each complete logical round as one block and finds the best path through those blocks without changing matchups, sheets, or the games grouped into each draw. That improved 28 catalog entries and proved 23 of them optimal. The pass deliberately rejects packed schedules whose draws cross round boundaries rather than guessing which games belong together.

A short constraint sweep produced another 51 proofs. Forty-nine finished in under one second on the development machine. The largest remaining back-to-back gap was 17 teams on 8 sheets, which improved from (1,136,16) to the global (1,136,0) lower bound after about 25 seconds. Both 13-team, 4-sheet packing modes remained unresolved after a one-minute attempt.

Finally, we generalized the full-round enumerator to allow surplus sheets. It exhausted six small search spaces, proved all six optima, and improved the 6-team, 5-sheet catalog entry from (1,3,0) to (1,2,0). These proofs matter because the cheap generic lower bound is not attainable for every shape.

Seventeen layouts were still marked best-found at that point. Graph-degree parity and a single-sheet transition bound proved 12 of them without changing their schedules. Cyclic edge-difference constructions then closed the 9-team and 13-team two-sheet layouts. Complete edge-colouring, draw-pairing, and draw ordering searches closed the 6-team and 7-team two-sheet layouts.

The last case was 9 teams on 8 sheets. Searching complete schedules carried too much symmetry, so we split it into smaller exact problems. A pseudo-Boolean model assigned the 36 games to sheets first. An exact factorization then formed nine legal draws, and a Hamiltonian path search ordered them without back-to-back repeats. The result improved from (1,5,0) to the global lower bound of (1,4,0).

Coverage and Proof Status

Catalog revision 21 resolves every team-and-sheet shape in the practical 2-through-20-team and 1-through-16-sheet envelope with a proven-optimal layout.

Of the 356 proven layouts, 333 reach the generic global lower bound. The other 23 need a tighter proof: 12 use a graph-degree parity bound, one uses a single-sheet transition bound, and ten use exhaustive search. The odd five-team, two-sheet layout is one example. Exact enumeration proved that four back-to-back repeats cannot be removed. Small surplus-sheet layouts provide others because games sharing a draw still need different sheets.

Every lookup in the defined envelope now has a proven-optimal result, and the catalog improvement queue is empty. Proven layouts stay in the regression suite, but receive no more offline search budget unless the catalog envelope or the scoring rules change.

Offline Rust and Browser JavaScript

Precomputing ordinary layouts changes the role of the browser optimizer. It is used for schedules that no longer match the canonical inputs.

A real event may have:

  • multiple round-robin pools sharing sheets
  • multiple iterations, where combining canonical layouts creates new repeat patterns
  • locked games that Allocate and Optimize must not move
  • ad hoc filler games for unbalanced round robins
  • bracket games that have to follow their predecessors

Those combinations are too event-specific to cache globally. They still need immediate and reversible optimization in the editor.

The in-browser optimizer runs in a cancellable Web Worker with a hard 100 ms deadline today. It always retains the input and the best valid result found, so running longer can fail to improve a schedule but cannot return a worse one.

Its current dispatcher uses three broad policies:

  • ordinary: an unlocked schedule that needs general local improvement
  • constrained repair: locks or fixed placements make stability important
  • bracket-safe: game ordering and fixed draw boundaries matter more than a wider reconstruction

It spends 70% of the deadline on depth-two search, then gives the remainder to the selected repair lane. The bracket-safe path stays with depth two for the whole deadline. Across 700 paired benchmark cases, this three-policy dispatcher beat the older five-route selector 22 times, tied 666, and lost 12.

The paired result supported using the simpler dispatcher. It nearly always returns the same or a better answer, runs off the main thread, and selects a policy from visible schedule features.

The browser and Rust implementations share the schedule vocabulary, validator expectations, fairness tuple, fixtures, and evidence. The offline Rust budget is effectively unconstrained, so it can use populations, exact solvers, parallel workers, and long-running checkpoints. The browser budget is tightly constrained because longer optimization would noticeably affect the editor's responsiveness.

Catalog Distribution and Runtime Selection

Drawmaster writes a versioned JSON catalog. Each entry contains indexed teams, draws, and sheets plus its fairness tuple, lower bound, producing method, proof status, command, and independent validation evidence.

The catalog is owned by our shared Curling scheduling package and bundled into the Curling IO v3 event schedule builder. The free CurlingSchedules.com editor also consumes the same artifact, but its interface and local-storage persistence remain separate from Curling IO. An 8×4 lookup resolves to the same canonical schedule in both places.

The runtime order is:

  1. Look for an exact canonical layout.
  2. Apply it immediately when it maps safely to the teams and selected sheets.
  3. Repeat or compose catalog layouts for iterations and multiple pools.
  4. Repair around locks, extra games, bracket games, and manual placements.
  5. Generate directly only when no catalog starting point can be used.

An exact, single-iteration layout marked proven optimal disables Optimize. There is nothing useful for the button to do. The catalog format can still hold a best-found layout if the envelope expands before its optimum is proved. In that case, Optimize remains available. Repeated, composed, or repaired schedules also remain optimizable because the original one-iteration proof no longer covers the combined result.

Club draw schedule templates are separate. A drawmaster can deliberately apply a recurring local arrangement instead of the global default, and v3 warns before that template moves games already placed in the schedule. Templates express a club's preference. The canonical catalog expresses the best general result we can validate for a numerical shape.

Findings

The research produced the following working conclusions:

  • Complete validated coverage took priority over spending more search time on one already well-understood layout
  • No heuristic dominated every team-and-sheet shape. The amount of variance between shapes was larger than we expected
  • More search depth did not guarantee better results under a fixed budget
  • Equal-score plateau movement mattered, but it could not repair a bad matchup factorization
  • Reconstruction and independent portfolios found different basins more reliably than one long local walk
  • Simulated annealing did not perform well enough to become a general strategy
  • Exact solvers became practical after we split the problem into the right layers
  • Published combinatorial designs supplied direct solutions for applicable schedule families
  • Proof status belongs beside every cached answer
  • The best production optimizer is often a cache lookup followed by a small, domain-specific repair

The current catalog covers the v3 target envelope, and every stored layout is proven optimal. The remaining research is event-specific repair around locks, ad hoc games, bracket games, and shared resources, plus any future expansion of the catalog envelope.

Curling IO v3 maps each abstract catalog layout onto real event teams, resources, draws, and games. From there, the drawmaster can apply club templates, import assignments, add ad hoc games, and make manual changes while preserving any placements they choose to lock.

Glossary

  • Fairness tuple: The ordered (Max, Total, Back-to-back) score used to compare schedules. Lower is better, and the numbers are compared from left to right.
  • Heuristic: A method designed to find a strong schedule quickly without proving that it is the best possible schedule.
  • Local search: Improving a schedule by making nearby changes to its current arrangement.
  • Swap depth: The number of moves evaluated together before deciding whether to keep them. A depth-two search can keep two moves whose combined result improves the original schedule.
  • Plateau: A group of different schedules with the same score.
  • Plateau walking: Moving through equal-scoring schedules in search of a position from which an improvement becomes possible.
  • Tabu search: Keeping a short memory of recent moves or schedules so the search does not repeatedly cycle through them.
  • Simulated annealing: A search that sometimes accepts a worse move to escape a local optimum, with that willingness usually decreasing over time.
  • Basin: A region of the search space whose nearby moves tend to lead to the same local optimum.
  • Portfolio: Several search strategies or workers run independently, with the best validated result retained.
  • Exact method or solver: A method that can establish whether a target is reachable or prove that no better valid schedule exists.
  • Branch-and-bound: An exact method that stops exploring a branch when its mathematical bound proves it cannot beat the best result already found.
  • Lower bound: A score that no valid schedule can beat. Reaching an applicable lower bound proves optimality.
  • Proven optimal: A validated schedule for which no better fairness tuple can exist.
  • Best-found: The strongest validated schedule found so far, without proof that a better one does not exist.
  • Checkpoint: Saved search state that lets a long-running process resume without starting over.