Update: premise was true when filed, but is now stale
This issue was filed 2026-08-23T21:13:36Z. PR #222 merged 2026-08-24T05:43:53Z
— about 8.5 hours later — and it already rewrote the exact
pollUntilSettled doc comment this issue quotes and disputes.
On origin/master today, the doc comment no longer says "far inside that
budget." It reads:
A bounded retry loop with a hard cap (100 x 10ms = 1s). Used by exactly ONE
test case below — the one that deliberately keeps a real
ThreadPoolExecutor underneath the model (see its own comment). Every other
case in this file injects a StepExecutor and drives the worker by hand, so
it neither sleeps nor guesses at a budget. Do not reach for this helper for
a new case without the same explicit justification.
and the one retained call site's own comment now ends with:
The cost is the retry loop pollUntilSettled still carries. Paying it once,
for the properties only a real thread can show, beats paying it five times.
That is already an honest statement of a deliberate tradeoff, not the "far
inside that budget" framing this issue originally rebutted. #222 also cut the
blast radius from the 4 call sites this issue originally counted down to
exactly 1, by converting 3 of this file's 4 report-job tests to a
deterministic, hand-driven StepExecutor that removes polling entirely.
The bookmarks/pastebin claim is refuted
The original "why it survives #222" section below claimed
examples/bookmarks/tests/test_app.cpp and
examples/pastebin/tests/test_paste_model.cpp "use the same fixed-budget
shape." Against current master this is factually wrong:
$ git grep -n "for (int i = 0; i < 100" origin/master -- examples
origin/master:examples/ledger/tests/test_ledger_reports.cpp:57:
bookmarks/tests/test_app.cpp has no sleep_for at all — it uses
pumpUntil, which is already MORPH_LADDER_DEADLINE_MS-scaled.
pastebin/tests/test_paste_model.cpp's only mention of sleep_for is a
comment explaining it deliberately avoids one, in favor of a
condition-variable-based SqlLogger hook.
A repo-wide grep confirms the fixed 100x10ms loop shape exists in exactly
one place today, not four.
The timing concern itself still holds, narrower
Rebuilt and ran ladder_ledger_tests [reports] 10x on an idle machine:
0.218 s 0.219 s 0.219 s 0.220 s 0.167 s
0.219 s 0.216 s 0.235 s 0.237 s 0.219 s
167–237 ms against the 1000 ms cap: 4.2x–6.0x margin — the same
single-digit-x order of magnitude as this issue's original macOS measurement
(6.7x at its worst observed), not "far" in any open-ended sense. Under
synthetic full-core CPU contention, six runs came back at 237–326 ms
(margin 3.1x–4.2x) — a real, measurable degradation under load, confirming
the qualitative direction of the original concern even though the specific
numbers and file scope were stale. The cap has not been observed exhausted on
any machine or in CI history for this test.
Invariant 7 applies, but narrower than originally framed
"Would this still pass if the feature did nothing?" — pollUntilSettled is
not a control that measures nothing: it correctly fails the test
(REQUIRE(status.status == Done)) if the cap is exhausted while the job is
still Pending. The real, still-live gap is narrower: the cap itself doesn't
scale with MORPH_LADDER_DEADLINE_MS the way every Qt-side wait in this
codebase does via pump.hpp's pumpUntil, so on a slow or loaded runner the
margin measured above shrinks without any of the usual dials moving.
Corrected scope
Add a non-Qt waitUntil(pred, deadline) helper that shares pump.hpp's
MORPH_LADDER_DEADLINE_MS scale factor, and have the one remaining real-pool
poll in examples/ledger/tests/test_ledger_reports.cpp use it instead of its
hand-picked 100x10ms wall-clock budget. Whether this helper belongs inside
pump.hpp itself (whose current contract is "the Qt loop is the pump") or
beside it is a design call for whoever picks this up, not a mechanical one.
This is now a narrow test-infra robustness improvement affecting one call
site, not a multi-file defect in shipped behavior.
Original report (superseded above; kept for the measurement detail)
What was measured
examples/ledger/tests/test_ledger_reports.cpp::pollUntilSettled polls a submitted report job with a hard iteration cap:
for (int i = 0; i < 100; ++i) {
status = model.execute(ledger::GetReportStatus{.jobId = jobId});
if (status.status != ledger::ReportStatus::Pending) { break; }
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
Its own doc comment (before #222 rewrote it) claimed "A single report job over a tiny test ledger completes far inside that budget; exhausting the cap means a real stall … not a slow machine."
I instrumented the loop and counted the iterations it actually spends, on macOS 15.6 / arm64, Debug, /opt/homebrew/opt/llvm/bin/clang++, -DMORPH_LADDER_RUNGS=ledger, at 93a6168. Five consecutive runs of the retained real-pool case:
PROBE pollUntilSettled slept=15
PROBE pollUntilSettled slept=7
PROBE pollUntilSettled slept=9
PROBE pollUntilSettled slept=7
PROBE pollUntilSettled slept=12
So the job takes 70–150 ms, and varies by better than 2x run to run on an idle machine. Against a 1000 ms cap that is a margin of 6.7x at worst observed, not "far inside".
The other half of the measurement is what contention does to the same suite. Timing [reports] while another compile was running on the same machine, against the steady-state figure:
contended: 3.48 s, 6.58 s, 2.39 s
idle: 1.28 s, 1.29 s, 1.29 s
A 2.7–5.1x whole-suite slowdown. Applied to the worst observed 15 iterations, that is 40–77 iterations — and a 6.7x slowdown, inside the range already observed, puts it at 100.
Verification status
- Reproduced: the iteration counts and the contended/idle wall-clock figures above are measured output from this machine, not inferred.
- Not reproduced: I never actually saw the cap exhausted. The 6.7x figure is arithmetic over two separately measured quantities (per-job iterations, whole-suite slowdown under load), not an observed failure. Treat it as a margin estimate.
- Not measured on Linux/CI hardware at all.
This is the same shape as #147 — a budget that is fine on the machine it was written on.
What would change the verdict
- Close if the remaining real-pool polls move to a deadline scaled by
MORPH_LADDER_DEADLINE_MS, or if measurement on CI hardware shows the margin is comfortable there and the macOS figures are an outlier worth ignoring.
- Escalate if the cap is ever observed exhausted in CI or locally — that turns the estimate above into a reproduction.
Update: premise was true when filed, but is now stale
This issue was filed 2026-08-23T21:13:36Z. PR #222 merged 2026-08-24T05:43:53Z
— about 8.5 hours later — and it already rewrote the exact
pollUntilSettleddoc comment this issue quotes and disputes.On
origin/mastertoday, the doc comment no longer says "far inside thatbudget." It reads:
and the one retained call site's own comment now ends with:
That is already an honest statement of a deliberate tradeoff, not the "far
inside that budget" framing this issue originally rebutted. #222 also cut the
blast radius from the 4 call sites this issue originally counted down to
exactly 1, by converting 3 of this file's 4 report-job tests to a
deterministic, hand-driven
StepExecutorthat removes polling entirely.The bookmarks/pastebin claim is refuted
The original "why it survives #222" section below claimed
examples/bookmarks/tests/test_app.cppandexamples/pastebin/tests/test_paste_model.cpp"use the same fixed-budgetshape." Against current master this is factually wrong:
bookmarks/tests/test_app.cpphas nosleep_forat all — it usespumpUntil, which is alreadyMORPH_LADDER_DEADLINE_MS-scaled.pastebin/tests/test_paste_model.cpp's only mention ofsleep_foris acomment explaining it deliberately avoids one, in favor of a
condition-variable-based
SqlLoggerhook.A repo-wide grep confirms the fixed 100x10ms loop shape exists in exactly
one place today, not four.
The timing concern itself still holds, narrower
Rebuilt and ran
ladder_ledger_tests[reports]10x on an idle machine:167–237 ms against the 1000 ms cap: 4.2x–6.0x margin — the same
single-digit-x order of magnitude as this issue's original macOS measurement
(6.7x at its worst observed), not "far" in any open-ended sense. Under
synthetic full-core CPU contention, six runs came back at 237–326 ms
(margin 3.1x–4.2x) — a real, measurable degradation under load, confirming
the qualitative direction of the original concern even though the specific
numbers and file scope were stale. The cap has not been observed exhausted on
any machine or in CI history for this test.
Invariant 7 applies, but narrower than originally framed
"Would this still pass if the feature did nothing?" —
pollUntilSettledisnot a control that measures nothing: it correctly fails the test
(
REQUIRE(status.status == Done)) if the cap is exhausted while the job isstill
Pending. The real, still-live gap is narrower: the cap itself doesn'tscale with
MORPH_LADDER_DEADLINE_MSthe way every Qt-side wait in thiscodebase does via
pump.hpp'spumpUntil, so on a slow or loaded runner themargin measured above shrinks without any of the usual dials moving.
Corrected scope
Add a non-Qt
waitUntil(pred, deadline)helper that sharespump.hpp'sMORPH_LADDER_DEADLINE_MSscale factor, and have the one remaining real-poolpoll in
examples/ledger/tests/test_ledger_reports.cppuse it instead of itshand-picked 100x10ms wall-clock budget. Whether this helper belongs inside
pump.hppitself (whose current contract is "the Qt loop is the pump") orbeside it is a design call for whoever picks this up, not a mechanical one.
This is now a narrow test-infra robustness improvement affecting one call
site, not a multi-file defect in shipped behavior.
Original report (superseded above; kept for the measurement detail)
What was measured
examples/ledger/tests/test_ledger_reports.cpp::pollUntilSettledpolls a submitted report job with a hard iteration cap:Its own doc comment (before #222 rewrote it) claimed "A single report job over a tiny test ledger completes far inside that budget; exhausting the cap means a real stall … not a slow machine."
I instrumented the loop and counted the iterations it actually spends, on macOS 15.6 / arm64, Debug,
/opt/homebrew/opt/llvm/bin/clang++,-DMORPH_LADDER_RUNGS=ledger, at93a6168. Five consecutive runs of the retained real-pool case:So the job takes 70–150 ms, and varies by better than 2x run to run on an idle machine. Against a 1000 ms cap that is a margin of 6.7x at worst observed, not "far inside".
The other half of the measurement is what contention does to the same suite. Timing
[reports]while another compile was running on the same machine, against the steady-state figure:A 2.7–5.1x whole-suite slowdown. Applied to the worst observed 15 iterations, that is 40–77 iterations — and a 6.7x slowdown, inside the range already observed, puts it at 100.
Verification status
This is the same shape as #147 — a budget that is fine on the machine it was written on.
What would change the verdict
MORPH_LADDER_DEADLINE_MS, or if measurement on CI hardware shows the margin is comfortable there and the macOS figures are an outlier worth ignoring.