diff --git a/docs/cli-options.md b/docs/cli-options.md
index 9fe1ea36f..d606e1bb4 100644
--- a/docs/cli-options.md
+++ b/docs/cli-options.md
@@ -874,8 +874,9 @@ AGENTIC_REPLAY only: upper bound (inclusive) on the random start position within
#### `--burst-phase-starts`
-AGENTIC_REPLAY only: collapse the WARMUP-start and PROFILING-start dispatches into synchronized bursts instead of spreading them by each request's recorded offset from t*. By default (False) the phase starts are SPREAD: WARMUP requests are aligned globally so every trajectory reaches its t* at the same instant (the warmup end), and each lane's first PROFILING request waits out its recorded gap after t* -- reproducing the recorded arrival pattern at both phase boundaries. The rest of the replay (inter-turn delays) is timing-faithful regardless of this flag; it governs ONLY the burst-vs-spread of the two phase starts. Pass --burst-phase-starts to fire each phase's first requests together (faster concurrency ramp, synchronized start), e.g. for a throughput-oriented run rather than a faithful arrival replay.
+AGENTIC_REPLAY only: collapse the WARMUP-start and PROFILING-start dispatches into synchronized bursts instead of spreading them by each request's recorded offset from t*. By default (True) the phase starts are BURST so configured concurrency becomes active inside the benchmark window. When False, WARMUP requests are aligned globally so every trajectory reaches its t* at the same instant (the warmup end), and each lane's first PROFILING request waits out its recorded gap after t*. The rest of the replay (inter-turn delays) is timing-faithful regardless of this setting; it governs ONLY the burst-vs-spread behavior at the two phase starts.
_Flag (no value required)_
+
_Default: `True`_
#### `--concurrency` ``
diff --git a/src/aiperf/common/config/loadgen_config.py b/src/aiperf/common/config/loadgen_config.py
index b8c7a7b96..7784c4701 100644
--- a/src/aiperf/common/config/loadgen_config.py
+++ b/src/aiperf/common/config/loadgen_config.py
@@ -262,22 +262,20 @@ def parse_concurrency_list(
description="AGENTIC_REPLAY only: collapse the WARMUP-start and "
"PROFILING-start dispatches into synchronized bursts instead of "
"spreading them by each request's recorded offset from t*. By "
- "default (False) the phase starts are SPREAD: WARMUP requests are "
- "aligned globally so every trajectory reaches its t* at the same "
- "instant (the warmup end), and each lane's first PROFILING request "
- "waits out its recorded gap after t* -- reproducing the recorded "
- "arrival pattern at both phase boundaries. The rest of the replay "
- "(inter-turn delays) is timing-faithful regardless of this flag; "
- "it governs ONLY the burst-vs-spread of the two phase starts. Pass "
- "--burst-phase-starts to fire each phase's first requests together "
- "(faster concurrency ramp, synchronized start), e.g. for a "
- "throughput-oriented run rather than a faithful arrival replay.",
+ "default (True) the phase starts are BURST so configured concurrency "
+ "becomes active inside the benchmark window. When False, WARMUP "
+ "requests are aligned globally so every trajectory reaches its t* "
+ "at the same instant (the warmup end), and each lane's first "
+ "PROFILING request waits out its recorded gap after t*. The rest "
+ "of the replay (inter-turn delays) is timing-faithful regardless "
+ "of this setting; it governs ONLY the burst-vs-spread behavior at "
+ "the two phase starts.",
),
CLIParameter(
name=("--burst-phase-starts",),
group=Groups.LOAD_GENERATOR,
),
- ] = False
+ ] = True
concurrency: Annotated[
Any, # CLI accepts string, validator converts to Union[int, list[int], None]
diff --git a/src/aiperf/timing/strategies/agentic_replay.py b/src/aiperf/timing/strategies/agentic_replay.py
index e786b9fcc..782b63600 100644
--- a/src/aiperf/timing/strategies/agentic_replay.py
+++ b/src/aiperf/timing/strategies/agentic_replay.py
@@ -20,10 +20,11 @@
not reproduced. This includes parents gated on a child join (they sent turn
n-1 before t* and resume at the join turn during PROFILING, so n-1 warms that
turn). Streams whose first request is at/after t* (``next_turn_index == 0``)
-have nothing to warm. By default the priming
-requests are SPREAD -- aligned globally on t* so every trajectory's t* lands
-at the warmup end (see ``_execute_warmup``); ``--burst-phase-starts`` fires
-them all at once instead. The phase exits via the standard
+have nothing to warm. By default the priming requests BURST together so the
+configured lanes become active inside the benchmark window. When
+``burst_phase_starts`` is false, requests are spread and aligned globally on
+t* so every trajectory's t* lands at the warmup end (see ``_execute_warmup``).
+The phase exits via the standard
``SendingCompleteStopCondition`` plus ``grace_period_sec=inf`` semantics
already in CreditPhaseConfig (count-driven: every turn-n-1 must return).
@@ -36,9 +37,10 @@
degraded trajectory pool.
PROFILING: each stream resumes at its first turn at/after t*
-(``next_turn_index``). Default dispatch preserves the stream's recorded offset
-from the replay boundary; ``--burst-phase-starts`` collapses each lane's first
-eligible request to profiling-time 0. Accelerated cache warmup synthesizes a
+(``next_turn_index``). Default dispatch collapses each lane's first eligible
+request to profiling-time 0. When ``burst_phase_starts`` is false, dispatch
+preserves the stream's recorded offset from the replay boundary. Accelerated
+cache warmup synthesizes a
new replay boundary at the warmup handoff and carries each live stream's
residual next-turn delay into profiling, so the handoff ramps instead of
firing every live stream at once. Subsequent turns honor trace inter-turn
@@ -210,13 +212,8 @@ def __init__(
self._benchmark_id: str = (
user_config.benchmark_id if user_config is not None else "unknown"
)
- # ``--burst-phase-starts`` (loadgen.burst_phase_starts). Default False:
- # the WARMUP and PROFILING phase starts are SPREAD by each request's
- # recorded offset from t* (warmup globally t*-aligned, profiling
- # preserving each lane's leading gap). When True, both phase starts
- # collapse into synchronized bursts. Governs ONLY the two phase-start
- # dispatch patterns; the rest of replay timing is faithful regardless.
- # ``is True`` guards MagicMock/None test configs -> default (spread).
+ # Real loadgen configs default to burst. Missing/mocked configs retain
+ # the spread fallback used by isolated strategy callers.
loadgen = getattr(user_config, "loadgen", None)
self._burst_phase_starts: bool = (
getattr(loadgen, "burst_phase_starts", False) is True
@@ -1429,14 +1426,12 @@ async def _dispatch_snapshot_for_profiling(
Each stream profiles from turn ``next_turn_index`` (the first turn at
or after t*; its predecessor, if any, was primed during WARMUP).
- Dispatch anchoring depends on ``--burst-phase-starts``: by default
- (spread) ``t0_offset = 0`` so each stream waits out its recorded offset
- from t* -- the leading t*->first-request gap is preserved and lanes
- ramp in. With ``--burst-phase-starts`` the trajectory's earliest
- post-t* request is anchored at profiling-time 0 (subtracting T0, the
- min offset) so the lane bursts at once. Relative timing among the
- trajectory's streams and turns is identical either way -- only the
- per-lane start offset differs.
+ By default, the trajectory's earliest post-t* request is anchored at
+ profiling-time 0 so every lane starts together. When
+ ``burst_phase_starts`` is false, ``t0_offset = 0`` and each stream waits
+ out its recorded offset from t*. Relative timing among the trajectory's
+ streams and turns is identical either way -- only the per-lane start
+ offset differs.
Gated parents (``waiting_on_children``) are not dispatched here; their
join is seeded with the orchestrator and their gated turn fires when