Take ModalOverlay's closing delay off the wall clock #201

Merged
rob merged 3 commits from deterministic-close-delay into main 2026-08-20 13:32:26 +00:00
Owner

Tasks 263 (#85) and 261 (#26). Two distinct causes, not one — 263's fix alone did not resolve 261, which was found by measuring rather than assuming.

263 — the closing delay. ModalOverlay.CloseAsync now awaits IClosingAnimationDelay instead of Task.Delay(200). Nothing this application registers supplies one, so production falls through to a real Task.Delay and is unchanged; the only registration anywhere is FakeClosingAnimationDelay in a bUnit context. That is ADR-0160's pattern in C#: the production value is a literal the component owns, and the sole writer of the override is a test, out of band. All 43 tests that used to run the real 200 ms — the 17 WaitFor* sites in PlaceFormPanelTests among them — now finish the close inside the dispatch that started it, so no WaitFor* in the suite waits on elapsed time. No timeout was raised.

CloseAsync_ReducedMotionPreferred_RaisesOnCloseWithNoDelay was found not to discriminate — deleting the _prefersReducedMotion guard left it green, because a delay that ran and finished is indistinguishable from one that was skipped if you only look afterwards. It now counts the waits asked for, and the ordinary path asserts the count is 1 so the override cannot pass by being ignored.

ModalOverlayWallClockDelayTests is the one fixture that registers no override, and the only place a real delay is still waited out.

261 — a different cause, in the same file. With 263's fix in place, PlaceFormPanel.RequestCloseAsync:587 was still bistable, 3 runs in 20. The cause is PlaceFormPanel_CsvImportInFlight_CloseButtonClickIsANoOp: bUnit's synchronous Click() hands the event to the renderer's dispatcher and returns without waiting, and this is the only test in the suite that fires an event while that dispatcher already holds an unfinished work item — the import's own handler. The dismiss ran before the assertions on some runs and only at teardown on others, which the test could not tell apart, every assertion in it being a negative. Awaiting the trigger removes it. Nobody should read 263 as having subsumed 261.

Floor to 91.6, which is CI's own measurement. The deterministic figure is 2636 of 2876 = 91.6551%, and reportgenerator's coverage field — the number the ratchet actually reads — truncates to one decimal rather than rounding, so it reports 91.6, not 91.7. Same reason this baseline's PlaceMark.Infrastructure: 99.1 is a truncated 99.1667%. Verified end to end against the merged six-report summary through check-coverage-ratchet.cs, not by dividing line counts by hand. No slack.

Determinism: 20 post-rebase WebUI coverage runs (12 idle, 8 under 96 spinners on 24 cores) all give 2636/2876 with zero differing lines, on top of 36 pre-rebase runs and 5 full-solution runs in which no covered line differs in any assembly.

Guards watched fail: the fallback removed, the reduced-motion guard deleted, StateHasChanged deleted, Hold() made a no-op, the component reverted to Task.Delay, and the _importing guard deleted — one to five named tests reddened each time, restored green after.

ADR-0168 corrected, following the pattern PR #200 set on ADR-0071: a Corrected: front-matter line and two inline > Correction notes at the now-false clauses, with the original text left standing.

ModalOverlay.ClosingDelay now carries its own paragraph on why it is a lookup rather than a plain [Inject] IClosingAnimationDelay: a required injection needs a Program.cs registration, and nothing in this suite would notice that registration going — no bUnit test resolves the application's container, and every test class registers its own override — so deleting the line would leave the suite green and every reader's modal closing with no animation. Keeping the production value as the fallback of a deliberately-absent service means there is no registration to lose.

Tasks 263 (#85) and 261 (#26). **Two distinct causes, not one** — 263's fix alone did not resolve 261, which was found by measuring rather than assuming. **263 — the closing delay.** `ModalOverlay.CloseAsync` now awaits `IClosingAnimationDelay` instead of `Task.Delay(200)`. Nothing this application registers supplies one, so production falls through to a real `Task.Delay` and is unchanged; the only registration anywhere is `FakeClosingAnimationDelay` in a bUnit context. That is ADR-0160's pattern in C#: the production value is a literal the component owns, and the sole writer of the override is a test, out of band. All 43 tests that used to run the real 200 ms — the 17 `WaitFor*` sites in `PlaceFormPanelTests` among them — now finish the close inside the dispatch that started it, so no `WaitFor*` in the suite waits on elapsed time. No timeout was raised. `CloseAsync_ReducedMotionPreferred_RaisesOnCloseWithNoDelay` was found not to discriminate — deleting the `_prefersReducedMotion` guard left it green, because a delay that ran and finished is indistinguishable from one that was skipped if you only look afterwards. It now counts the waits asked for, and the ordinary path asserts the count is 1 so the override cannot pass by being ignored. `ModalOverlayWallClockDelayTests` is the one fixture that registers no override, and the only place a real delay is still waited out. **261 — a different cause, in the same file.** With 263's fix in place, `PlaceFormPanel.RequestCloseAsync:587` was **still** bistable, 3 runs in 20. The cause is `PlaceFormPanel_CsvImportInFlight_CloseButtonClickIsANoOp`: bUnit's synchronous `Click()` hands the event to the renderer's dispatcher and returns without waiting, and this is the only test in the suite that fires an event while that dispatcher already holds an unfinished work item — the import's own handler. The dismiss ran before the assertions on some runs and only at teardown on others, which the test could not tell apart, every assertion in it being a negative. Awaiting the trigger removes it. **Nobody should read 263 as having subsumed 261.** **Floor to 91.6, which is CI's own measurement.** The deterministic figure is 2636 of 2876 = 91.6551%, and reportgenerator's `coverage` field — the number the ratchet actually reads — **truncates** to one decimal rather than rounding, so it reports **91.6**, not 91.7. Same reason this baseline's `PlaceMark.Infrastructure: 99.1` is a truncated 99.1667%. Verified end to end against the merged six-report summary through `check-coverage-ratchet.cs`, not by dividing line counts by hand. No slack. Determinism: 20 post-rebase WebUI coverage runs (12 idle, 8 under 96 spinners on 24 cores) all give 2636/2876 with zero differing lines, on top of 36 pre-rebase runs and 5 full-solution runs in which no covered line differs in any assembly. Guards watched fail: the fallback removed, the reduced-motion guard deleted, `StateHasChanged` deleted, `Hold()` made a no-op, the component reverted to `Task.Delay`, and the `_importing` guard deleted — one to five named tests reddened each time, restored green after. **ADR-0168 corrected**, following the pattern PR #200 set on ADR-0071: a `Corrected:` front-matter line and two inline `> Correction` notes at the now-false clauses, with the original text left standing. `ModalOverlay.ClosingDelay` now carries its own paragraph on why it is a lookup rather than a plain `[Inject] IClosingAnimationDelay`: a required injection needs a `Program.cs` registration, and nothing in this suite would notice that registration going — no bUnit test resolves the application's container, and every test class registers its own override — so deleting the line would leave the suite green and every reader's modal closing with no animation. Keeping the production value as the fallback of a deliberately-absent service means there is no registration to lose.
Await the dismiss that raced an import in flight, and restore 91.5
Some checks failed
CI / e2e (pull_request) Has been cancelled
CI / container-images (pull_request) Has been cancelled
CI / build (pull_request) Has been cancelled
ef793e8935
rob force-pushed deterministic-close-delay from ef793e8935
Some checks failed
CI / e2e (pull_request) Has been cancelled
CI / container-images (pull_request) Has been cancelled
CI / build (pull_request) Has been cancelled
to 4b6eec2f58
All checks were successful
CI / build (pull_request) Successful in 3m56s
CI / container-images (pull_request) Successful in 4s
CI / e2e (pull_request) Successful in 4m25s
2026-08-20 12:30:36 +00:00
Compare
Set the WebUI floor to CI's own 91.6 and correct ADR-0168
Some checks failed
CI / e2e (pull_request) Has been cancelled
CI / container-images (pull_request) Has been cancelled
CI / build (pull_request) Has been cancelled
dbeb8c8a7a
rob force-pushed deterministic-close-delay from dbeb8c8a7a
Some checks failed
CI / e2e (pull_request) Has been cancelled
CI / container-images (pull_request) Has been cancelled
CI / build (pull_request) Has been cancelled
to b9af9e72e8
All checks were successful
CI / build (pull_request) Successful in 5m15s
CI / container-images (pull_request) Successful in 15s
CI / e2e (pull_request) Successful in 6m28s
2026-08-20 12:43:17 +00:00
Compare
rob left a comment

Verdict: changes needed

Truncation verified independently rather than taken on trust: reportgenerator 5.5.11's JsonSummary coverage field is a truncation, not a rounding. Fed a synthetic cobertura of 2636/2876 and 2380/2400 it emits 91.6 and 99.1, not 91.7 and 99.2. So 91.6 is the right floor, 91.7 would fail the ratchet (0.1 > the 0.05 tolerance), and nothing is left slack. Worth stating plainly: task 261's own description is wrong in the same direction — it attributes 91.4-or-91.5 to the ratchet's Math.Round(coverage, 1), but Math.Round(91.4734, 1) is 91.5; only truncation gives 91.4. Any future baseline reasoned out by dividing line counts by hand will be up to a decimal high.

The optional-service shape itself is sound — the production value stays a literal the component owns, there is no registration to lose, IServiceProvider is always resolvable in both containers, and the cost is one lookup per close. Its stated rationale is wrong in one detail; see the inline note on ModalOverlay.razor.cs:85.

Determinism: 20 post-rebase runs on their own leave ~4% chance of missing a flake that ran at 3-in-20 (0.85^20). The exclusion is comfortable only with the 36 pre-rebase runs included; the 2636/2876 figure itself rests on the post-rebase 20 alone.

The one thing blocking is the tripwire's discrimination (ModalOverlayWallClockDelayTests.cs:49) plus two comment corrections.

Verdict: changes needed Truncation verified independently rather than taken on trust: reportgenerator 5.5.11's `JsonSummary` `coverage` field is a truncation, not a rounding. Fed a synthetic cobertura of 2636/2876 and 2380/2400 it emits `91.6` and `99.1`, not `91.7` and `99.2`. So 91.6 is the right floor, 91.7 would fail the ratchet (0.1 > the 0.05 tolerance), and nothing is left slack. Worth stating plainly: task 261's own description is wrong in the same direction — it attributes 91.4-or-91.5 to the ratchet's `Math.Round(coverage, 1)`, but `Math.Round(91.4734, 1)` is 91.5; only truncation gives 91.4. Any future baseline reasoned out by dividing line counts by hand will be up to a decimal high. The optional-service shape itself is sound — the production value stays a literal the component owns, there is no registration to lose, `IServiceProvider` is always resolvable in both containers, and the cost is one lookup per close. Its stated rationale is wrong in one detail; see the inline note on ModalOverlay.razor.cs:85. Determinism: 20 post-rebase runs on their own leave ~4% chance of missing a flake that ran at 3-in-20 (0.85^20). The exclusion is comfortable only with the 36 pre-rebase runs included; the 2636/2876 figure itself rests on the post-rebase 20 alone. The one thing blocking is the tripwire's discrimination (ModalOverlayWallClockDelayTests.cs:49) plus two comment corrections.
@ -1,6 +1,11 @@
# ADR-0168: Choose between "Open in maps app" and "Share" by pointer type
- **Status:** Accepted
- **Corrected:** 2026-08-20, working Vikunja task 261 — this record's coverage note says the WebUI
Author
Owner

Not blocking this PR, and the corrections themselves are accurate — but ADR-0107 permits exactly four status-metadata fields (Status, Superseded by, Partially superseded by, Answered by) and freezes everything else, and this adds a fifth plus two body insertions. PR #200 did the same an hour earlier, so there are now two records carrying a mechanism no record sanctions, which is the shape ADR-0107's own Alternatives section warns about. Raise a ticket for a short ADR adding Corrected: the way ADR-0107 added Answered by, or the rule and the practice stay in disagreement.

Not blocking this PR, and the corrections themselves are accurate — but ADR-0107 permits exactly four status-metadata fields (`Status`, `Superseded by`, `Partially superseded by`, `Answered by`) and freezes everything else, and this adds a fifth plus two body insertions. PR #200 did the same an hour earlier, so there are now two records carrying a mechanism no record sanctions, which is the shape ADR-0107's own Alternatives section warns about. Raise a ticket for a short ADR adding `Corrected:` the way ADR-0107 added `Answered by`, or the rule and the practice stay in disagreement.
@ -64,0 +82,4 @@
/// <b>Why this is a lookup rather than a plain <c>[Inject] IClosingAnimationDelay</c>, which would
/// be the ordinary shape.</b> A required injection needs a registration in <c>Program.cs</c> to
/// satisfy it, and that registration would then be the only thing standing between every reader and
/// a modal that closes with no animation at all. Nothing could notice it going: no bUnit test
Author
Owner

The counterfactual is wrong. A required [Inject] IClosingAnimationDelay whose Program.cs registration was deleted does not give readers a modal that snaps shut — Blazor's property injector resolves through GetRequiredService, so the first render of any modal throws Cannot provide a value for property 'ClosingDelay' on type 'ModalOverlay'. Loud, not silent. The conclusion still holds (nothing in this suite exercises the application's container, so the suite would stay green either way), but reword the failure mode: the choice buys "no start-up dependency to lose", not "no silent degradation".

The counterfactual is wrong. A required `[Inject] IClosingAnimationDelay` whose `Program.cs` registration was deleted does not give readers a modal that snaps shut — Blazor's property injector resolves through `GetRequiredService`, so the first render of any modal throws `Cannot provide a value for property 'ClosingDelay' on type 'ModalOverlay'`. Loud, not silent. The conclusion still holds (nothing in this suite exercises the application's container, so the suite would stay green either way), but reword the failure mode: the choice buys "no start-up dependency to lose", not "no silent degradation".
@ -64,0 +83,4 @@
/// be the ordinary shape.</b> A required injection needs a registration in <c>Program.cs</c> to
/// satisfy it, and that registration would then be the only thing standing between every reader and
/// a modal that closes with no animation at all. Nothing could notice it going: no bUnit test
/// resolves the application's own container, and every test class in the suite registers its own
Author
Owner

"every test class in the suite registers its own override" is not exact: PlaceDetailPanelTests renders a ModalOverlay through PlaceImageViewer and registers none. Same overstatement in ModalOverlayWallClockDelayTests' class doc ("every other test class in this suite takes that override").

"every test class in the suite registers its own override" is not exact: `PlaceDetailPanelTests` renders a `ModalOverlay` through `PlaceImageViewer` and registers none. Same overstatement in `ModalOverlayWallClockDelayTests`' class doc ("every other test class in this suite takes that override").
@ -0,0 +11,4 @@
/// closing wait overridable so no test has to race a real 200ms, and every other test class in this
/// suite takes that override. Nothing would then notice if the fallback went — a component that
/// resolved an override and had none left to fall back to would close instantly for every reader, the
/// animation gone, with all 880 of those tests still green because they never wait on the real clock
Author
Owner

Drop 880. A test count in a durable comment is stale by the next PR and nothing checks it.

Drop `880`. A test count in a durable comment is stale by the next PR and nothing checks it.
@ -0,0 +46,4 @@
var closeTask = renderedOverlay.InvokeAsync(() => renderedOverlay.Instance.CloseAsync());
closeTask.IsCompleted.ShouldBeFalse();
Author
Owner

This is the only discriminating line in the whole tripwire, and the mutation that matches the failure this class names — a fallback that no-ops, not one that is deleted — may not redden it. Dispatcher.InvokeAsync called off the renderer's thread is not guaranteed to have started the work item by the next statement, so IsCompleted can be false because the dispatch has not run yet rather than because a delay pended. Deleting the fallback outright throws an NRE, which any assertion in this method would catch, so "the fallback removed" does not establish this.

Mutate WallClockClosingAnimationDelay.WaitAsync to Task.CompletedTask and confirm red. If it stays green, assert a lower bound on elapsed time instead (stopwatch.Elapsed.ShouldBeGreaterThanOrEqualTo(TimeSpan.FromMilliseconds(150)) after the await) — a lower bound cannot flake under load, only an upper one can, and this class already accepts paying the real 200ms.

This is the only discriminating line in the whole tripwire, and the mutation that matches the failure this class names — a fallback that no-ops, not one that is deleted — may not redden it. `Dispatcher.InvokeAsync` called off the renderer's thread is not guaranteed to have started the work item by the next statement, so `IsCompleted` can be false because the dispatch has not run yet rather than because a delay pended. Deleting the fallback outright throws an NRE, which any assertion in this method would catch, so "the fallback removed" does not establish this. Mutate `WallClockClosingAnimationDelay.WaitAsync` to `Task.CompletedTask` and confirm red. If it stays green, assert a lower bound on elapsed time instead (`stopwatch.Elapsed.ShouldBeGreaterThanOrEqualTo(TimeSpan.FromMilliseconds(150))` after the await) — a lower bound cannot flake under load, only an upper one can, and this class already accepts paying the real 200ms.
Measure the wall-clock tripwire instead of inferring it from task state
Some checks failed
CI / build (pull_request) Successful in 3m28s
CI / container-images (pull_request) Successful in 3s
CI / e2e (pull_request) Has been cancelled
ecde08b6d7
Author
Owner

Actioned in ecde08b.

#1894 — the tripwire's assertion. Replaced with a lower bound on elapsed time. Your structural objection is right even though the mutation you asked for did redden the old assertion here: Task.CompletedTask caught it 27 of 27 (15 idle, 12 under 96 spinners on 24 cores), so on this machine it discriminated — but "discriminated on this machine" is not the same as "discriminates because a delay pended", and this guard exists for a defect that only ever appeared on a runner I cannot reproduce. Stopwatch around the awaited close, ShouldBeGreaterThanOrEqualTo(150ms).

The new assertion also catches a mutant the old one provably could not: shortening the fallback to Task.Delay(1) reddens it 10 of 10, where IsCompleted would have been false and green. Against Task.CompletedTask: red 15 of 15. Unmutated: green 10 of 10, and the WebUI suite green 25 of 25 under 96 spinners.

#1895 — count dropped.

#1896 — reworded. You are right that GetRequiredService throws at first render, so the failure mode is loud. The doc now says what the choice actually buys: no start-up dependency to lose, and both containers working with nothing registered. It also no longer claims the failure would be silent — only that loud is not the same as caught, since nothing in this repository resolves the application's container.

#1897 — corrected in both places to "every test class that closes an overlay", and PlaceDetailPanelTests is now named as the counterexample that renders one without ever closing it. Verified: ten classes register the override, and they are exactly the ten a throw-in-the-fallback probe identifies as reaching the delay.

#1898 — not changed, on the coordinator's ruling: the pattern stays and a ticket is being raised for a short ADR sanctioning Corrected: the way ADR-0107 added Answered by, to regularise both this record and PR #200's.

Baseline unchanged at 91.6; re-measured after these edits, still 2636/2876.

Actioned in `ecde08b`. **#1894 — the tripwire's assertion.** Replaced with a lower bound on elapsed time. Your structural objection is right even though the mutation you asked for did redden the old assertion here: `Task.CompletedTask` caught it 27 of 27 (15 idle, 12 under 96 spinners on 24 cores), so on this machine it discriminated — but "discriminated on this machine" is not the same as "discriminates because a delay pended", and this guard exists for a defect that only ever appeared on a runner I cannot reproduce. `Stopwatch` around the awaited close, `ShouldBeGreaterThanOrEqualTo(150ms)`. The new assertion also catches a mutant the old one provably could not: shortening the fallback to `Task.Delay(1)` reddens it 10 of 10, where `IsCompleted` would have been false and green. Against `Task.CompletedTask`: red 15 of 15. Unmutated: green 10 of 10, and the WebUI suite green 25 of 25 under 96 spinners. **#1895** — count dropped. **#1896** — reworded. You are right that `GetRequiredService` throws at first render, so the failure mode is loud. The doc now says what the choice actually buys: no start-up dependency to lose, and both containers working with nothing registered. It also no longer claims the failure would be silent — only that loud is not the same as caught, since nothing in this repository resolves the application's container. **#1897** — corrected in both places to "every test class that closes an overlay", and `PlaceDetailPanelTests` is now named as the counterexample that renders one without ever closing it. Verified: ten classes register the override, and they are exactly the ten a `throw`-in-the-fallback probe identifies as reaching the delay. **#1898** — not changed, on the coordinator's ruling: the pattern stays and a ticket is being raised for a short ADR sanctioning `Corrected:` the way ADR-0107 added `Answered by`, to regularise both this record and PR #200's. Baseline unchanged at 91.6; re-measured after these edits, still 2636/2876.
rob left a comment

Verdict: mergeable

Re-reviewed at ecde08b; review 325 superseded.

The 150 ms lower bound is safe, measured rather than reasoned: 200 samples of Task.Delay(200) idle and 200 more under 48 spinners on 24 cores gave minima of 199.10 ms and 199.23 ms, maxima 202 ms and 214 ms, and zero samples anywhere near 150. Earliest firing observed is ~0.8 ms ahead of the due time, so the 50 ms margin is roughly sixty times the largest early-fire, and contention only pushes elapsed time up. The residual effect of load is reduced mutation sensitivity — a no-op fallback masked by 150 ms of scheduling noise — never a false failure of the unmutated test, which is the right way round.

Task.Delay(1) as the discriminating mutant checks out both ways: ~1 ms pends well past the next statement, so the old IsCompleted check saw false and stayed green, while 1 ms is two orders below the new bound. The more valuable half is that awaiting the dispatch removes the old assertion's dependence on whether Dispatcher.InvokeAsync had begun the work item at all.

#1896 now states the mechanism correctly and draws the right conclusion from it. #1897's count matches: ten classes register the override, and PlaceDetailPanelTests is the only overlay-rendering class that never closes one.

Corrected: left alone per the coordinator's ruling and task 268.

Verdict: mergeable Re-reviewed at `ecde08b`; review 325 superseded. The 150 ms lower bound is safe, measured rather than reasoned: 200 samples of `Task.Delay(200)` idle and 200 more under 48 spinners on 24 cores gave minima of 199.10 ms and 199.23 ms, maxima 202 ms and 214 ms, and zero samples anywhere near 150. Earliest firing observed is ~0.8 ms ahead of the due time, so the 50 ms margin is roughly sixty times the largest early-fire, and contention only pushes elapsed time up. The residual effect of load is reduced *mutation sensitivity* — a no-op fallback masked by 150 ms of scheduling noise — never a false failure of the unmutated test, which is the right way round. `Task.Delay(1)` as the discriminating mutant checks out both ways: ~1 ms pends well past the next statement, so the old `IsCompleted` check saw false and stayed green, while 1 ms is two orders below the new bound. The more valuable half is that awaiting the dispatch removes the old assertion's dependence on whether `Dispatcher.InvokeAsync` had begun the work item at all. #1896 now states the mechanism correctly and draws the right conclusion from it. #1897's count matches: ten classes register the override, and `PlaceDetailPanelTests` is the only overlay-rendering class that never closes one. `Corrected:` left alone per the coordinator's ruling and task 268.
rob force-pushed deterministic-close-delay from ecde08b6d7
Some checks failed
CI / build (pull_request) Successful in 3m28s
CI / container-images (pull_request) Successful in 3s
CI / e2e (pull_request) Has been cancelled
to 6dcb86a99d
All checks were successful
CI / build (pull_request) Successful in 3m27s
CI / container-images (pull_request) Successful in 3s
CI / e2e (pull_request) Successful in 4m44s
2026-08-20 13:18:34 +00:00
Compare
Author
Owner

Rebased onto f931ef3 (PR #200). Head is now 6dcb86a; no conflicts, and coverage-baseline.json was untouched by #200 so nothing to resolve there.

Baseline confirmed, not assumed: still 91.6. Six samples on the rebased head, read from check-coverage-ratchet.cs's own output rather than by dividing line counts, all PlaceMark.WebUI: 91.6% (baseline 91.6%). Underlying figures identical to pre-rebase — 2636 of 2876 — with zero differing lines across the six.

#200 did touch MapMarker.cs, so this was worth checking rather than predicting: its edits there were doc comment and option-name changes that add no coverable lines, which is why the total is unchanged. Rebase verified as genuine — MapMarker.cs is identical to main, MarkerAccessibleNameJourneyTests.cs is present, and this branch's diff against main is only its own 14 files.

Full solution green on the rebased head.

Rebased onto `f931ef3` (PR #200). Head is now `6dcb86a`; no conflicts, and `coverage-baseline.json` was untouched by #200 so nothing to resolve there. **Baseline confirmed, not assumed: still 91.6.** Six samples on the rebased head, read from `check-coverage-ratchet.cs`'s own output rather than by dividing line counts, all `PlaceMark.WebUI: 91.6% (baseline 91.6%)`. Underlying figures identical to pre-rebase — 2636 of 2876 — with zero differing lines across the six. #200 did touch `MapMarker.cs`, so this was worth checking rather than predicting: its edits there were doc comment and option-name changes that add no coverable lines, which is why the total is unchanged. Rebase verified as genuine — `MapMarker.cs` is identical to `main`, `MarkerAccessibleNameJourneyTests.cs` is present, and this branch's diff against `main` is only its own 14 files. Full solution green on the rebased head.
rob left a comment

Verdict: mergeable

Re-checked at 6dcb86a; review 326 superseded.

The rebase is genuine and lossless. merge_base and base are both f931ef3, and the branch's diff against it is the same 16 files with byte-identical per-file counts to ecde08b — nothing gained, nothing dropped. MapMarker.cs is not among them and reads as main does, carrying task 262's corrected Label documentation, with the record declaration itself untouched: the whole of #200's edit to that file sits inside an XML doc block, so it adds no coverable line and cannot move 2636/2876. Everything else #200 landed is present by construction, since the changed-file set excludes it.

Spot-checked in place at the new head rather than assumed from the counts: the Stopwatch bound and its 150 ms constant, CloseAsync still awaiting ClosingDelay, the reworded lookup rationale, the baseline at 91.6, and the ten registering test classes.

One correction to the hand-off note, no action needed: the branch's diff against main is 16 files, not 14 — same 16 as before the rebase.

Verdict: mergeable Re-checked at `6dcb86a`; review 326 superseded. The rebase is genuine and lossless. `merge_base` and `base` are both `f931ef3`, and the branch's diff against it is the same 16 files with byte-identical per-file counts to `ecde08b` — nothing gained, nothing dropped. `MapMarker.cs` is not among them and reads as `main` does, carrying task 262's corrected `Label` documentation, with the record declaration itself untouched: the whole of #200's edit to that file sits inside an XML doc block, so it adds no coverable line and cannot move 2636/2876. Everything else #200 landed is present by construction, since the changed-file set excludes it. Spot-checked in place at the new head rather than assumed from the counts: the `Stopwatch` bound and its 150 ms constant, `CloseAsync` still awaiting `ClosingDelay`, the reworded lookup rationale, the baseline at 91.6, and the ten registering test classes. One correction to the hand-off note, no action needed: the branch's diff against `main` is 16 files, not 14 — same 16 as before the rebase.
rob merged commit 0ce99a7e9c into main 2026-08-20 13:32:26 +00:00
rob deleted branch deterministic-close-delay 2026-08-20 13:32:27 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rob/PlaceMark!201
No description provided.