Skip to content

fix(coordinator): stop an unknown row count blanking the estimate already on screen - #2072

Merged
datlechin merged 1 commit into
mainfrom
fix/row-count-unknown-must-not-wipe
Aug 10, 2026
Merged

fix(coordinator): stop an unknown row count blanking the estimate already on screen#2072
datlechin merged 1 commit into
mainfrom
fix/row-count-unknown-must-not-wipe

Conversation

@datlechin

@datlechin datlechin commented Aug 10, 2026

Copy link
Copy Markdown
Member

Fixes the row count appearing under the grid and then vanishing about a second later. Regression from #2068, which revived a guard in resolveRowCount that had been dead since #2055, so this code ran for the first time and brought a latent bug with it.

Cause

RowCountOutcome.appliedTotal collapsed two different answers into one value:

var appliedTotal: (total: Int?, isApproximate: Bool) {
    guard case let .count(value, isApproximate) = self else { return (nil, false) }
    guard value > 0 || (value == 0 && !isApproximate) else { return (nil, false) }
    return (value, isApproximate)
}

(nil, false) meant both "the count is genuinely gone" and "I could not work it out". The caller wrote it either way.

Those are not the same thing, and the difference is visible. Phase 1 puts an estimate on screen synchronously from inline metadata. Phase 2's driver round trip lands a few hundred milliseconds later. When it comes back with an unusable estimate, which the doc comment on that very function already explains is normal (an un-analyzed table reports 0 or -1 while holding millions of rows), it blanked the number the user was already looking at.

The path needs no filters, which is worth stating because I twice told the user it did. rowCountPlan returns .approximate for a non-SQL source with no filters, that calls fetchApproximateRowCount, and a driver returning 0 or less lands straight on the nil branch.

Fix

appliedTotal becomes optional. nil means "no answer", and the caller skips the write entirely, leaving whatever is on screen alone. .clear still returns (nil, false) and still wipes the count, because a filter change genuinely invalidates it.

That is the whole behavioural change: an absent answer stops overwriting a present one.

Tests

The six RowCountOutcomeTests cases are updated to the new semantics in the same commit, including the two that now assert nothing is applied rather than asserting nil is applied. 36 tests pass across RowCountOutcomeTests, RowCountPlanTests, Phase2RowCountGuardTests, RowCountTaskLifecycleTests and MainContentCoordinatorRefreshTests. swiftlint lint --strict reports 0 violations in 1322 files.

Still open, not in this PR

Phase 2 has no tracer coverage, which is why this needed a round trip through the user to diagnose. Adding stages is not as cheap as it looks: the trace finishes right after mainRunLoopIdle, which is before the phase 2 row count lands, so a stage on a finished token is a no-op. Covering it properly means extending the trace lifetime to wait for phase 2, which risks leaving traces open, and is its own change.

Correction to an earlier claim of mine: I said StatusBarSnapshot+RowInfo.swift:23 hides the total for a genuinely empty table. It does not. A table with zero rows falls through to "No rows", which is better than "0-0 of 0 rows". There is no bug there.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@datlechin
datlechin merged commit 25177e4 into main Aug 10, 2026
4 checks passed
@datlechin
datlechin deleted the fix/row-count-unknown-must-not-wipe branch August 10, 2026 15:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant