Skip to content

test(clock): time-budget facts run on a fake clock (STORY-442, closes #723) - #787

Merged
genwave-radio merged 4 commits into
mainfrom
test/fake-clock-442
Sep 16, 2026
Merged

genwave-radio merged 4 commits into
mainfrom
test/fake-clock-442

Conversation

@genwave-radio

Copy link
Copy Markdown
Collaborator

Closes #723. PR-3 of the gh-#777 epic (STORY-442).

⏱️ What

  • Microsoft's FakeTimeProvider replaces the two hand-rolled fakes in Orchestration.Tests and Tts.Tests (package added with dotnet add package; 34 consumers; Story241 sets its zone with SetLocalTimeZone). Timers now really fire on Advance.
  • Story243's render double delays on the injected clock, so the per-unit render budget is decided by due order, never by wall-clock milliseconds. PullUnitsRacingTheRenderBudgetAsync advances one budget-vs-render pair per step.
  • AdScriptWriter's per-attempt Llm:TimeoutSeconds budget is a CancellationTokenSource(delay, timeProvider) linked with the caller's token, replacing wall-clock CancelAfter. Production behaviour is unchanged; a never-answering LLM resolves to Timeout when fake time passes the budget.
  • Story442 specs green: 4 pins (Architecture), 6 handoff facts (Orchestration), 2 re-ask facts (Ads).

🔁 Fifty runs (T485 wire, dev box)

for i in $(seq 50); do dotnet test tests/GenWave.Orchestration.Tests --no-build --filter "FullyQualifiedName~RendersRideThePerUnitBudget|FullyQualifiedName~FeatureHandoffBudgetOnAFakeClock" | grep -q "Passed!" || echo FAIL $i; done
# (no FAIL lines) 50/50
Passed!  - Failed:     0, Passed:     7, Skipped:     0, Total:     7, Duration: 120 ms - GenWave.Orchestration.Tests.dll (net10.0)
for i in $(seq 50); do dotnet test tests/GenWave.Ads.Tests --no-build --filter "FullyQualifiedName~FeatureAdReaskBudgetOnAFakeClock" | grep -q "Passed!" || echo FAIL $i; done
# (no FAIL lines) 50/50
Passed!  - Failed:     0, Passed:     2, Skipped:     0, Total:     2, Duration: 71 ms - GenWave.Ads.Tests.dll (net10.0)

✅ Gate

dotnet test GenWave.sln --filter "Category!=Integration" -- xUnit.MaxParallelThreads=3: all nine projects green (Orchestration 443 passed / 3 skipped, Ads 222 / 3, Host 2909 / 203, Architecture 150 / 10; 0 failed).

📝 Follow-up (out of scope)

ScenarioEachAttemptHasItsOwnTimeoutBudget in Tts.Tests' Story390 still spends about 3.6 s on the real clock; it can ride the fake clock now that the writer's budget is TimeProvider-driven.

…ed ones (gh-#723)

Orchestration.Tests adds Microsoft.Extensions.TimeProvider.Testing via dotnet add
package (CPM already pinned it); both hand-rolled Fakes/FakeTimeProvider.cs are
deleted and 34 consumers across Orchestration.Tests/Tts.Tests switch to the real
fake. Story241's DST facts move the local time zone to a post-construction
SetLocalTimeZone call, since Microsoft's ctor has no localTimeZone parameter.
Story243's per-unit render-budget fact pumps the clock past each render's own
budget as an interim shape, since the render double itself still rides real wall
time until T483 puts it on the fake clock too. Story442's AC1/AC2 pins go green.

Timers now really fire on Advance instead of silently riding the wall clock, so
the per-unit render budget race stops depending on real-time scheduling luck.
)

FakeTtsSegmentSource now takes a TimeProvider (default TimeProvider.System)
and delays RenderAsync on it instead of the wall clock; both production-chain
harnesses (Story243, ProductionChainHarness) hand it the chain's own
FakeTimeProvider. PullUnitsRacingTheRenderBudgetAsync replaces T482's interim
real-wait pump, advancing one render-vs-budget pair per step so at most a
single fake timer fires per Advance call. Story442's six AC3/AC4 facts are
green: the handoff budget race is now decided purely by fake-clock due order,
never by which wall-clock timer happens to win on a given runner.
…ncellation (gh-#723)

Each attempt's Llm:TimeoutSeconds budget is now a CancellationTokenSource(delay, timeProvider)
linked with the caller's token, replacing the wall-clock CancelAfter; the NewAttemptBudget helper
is folded inline at both call sites. Story442 AC5/AC6 facts go green in ~50ms.

A never-answering LLM now resolves to Timeout as soon as fake time passes the budget, so the fact
no longer waits on the wall clock; production behaviour (real TimeProvider) is unchanged.
@genwave-radio
genwave-radio merged commit 2e62a4b into main Sep 16, 2026
11 checks passed
@genwave-radio
genwave-radio deleted the test/fake-clock-442 branch September 16, 2026 11:29
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 16, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

1 participant