Skip to content

Re-derive HeaviestHourlyRefreshObservedCeilingSeconds from a job_history census: 594 s becomes 896 s, and the watch-line ordering inverts - #3168

Closed
erikdarlingdata wants to merge 4 commits into
devfrom
fix/3166-refresh-ceiling-census
Closed

erikdarlingdata wants to merge 4 commits into
devfrom
fix/3166-refresh-ceiling-census

Conversation

@erikdarlingdata

Copy link
Copy Markdown
Owner

Closes #3166.

HeaviestHourlyRefreshObservedCeilingSeconds was 594 s, the maximum of a 16-run record the comment itself described as half census and half sample. The comment also named the read that would settle that: timescaledb_information.job_history, every run since the boundary rather than sampled. That read has been done, and the constant is now 896 s.

The census

One row per run for job policy_refresh_continuous_aggregate / query_store_stats_interval_hourly on the store that carries this workload, read at 2026-09-08 01:37Z over timescaledb_information.job_history (TimescaleDB 2.28.1, PostgreSQL 18.4, timescaledb.enable_job_execution_logging = on). Boundary 2026-09-05 13:44:23, exclusion positional as recorded.

side of the boundary runs median maximum
at or before 304 1081.7 s 13300.7 s
after 57 418.3 s 896.1 s

Zero post-boundary runs failed and none is missing a finish time, so the succeeded filter removes nothing from the span — the census is the whole of it rather than a status-selected part.

The read reproduces the published record exactly where they overlap, which is what says the method is the same one: the nine boundary-day values (194, 222, 225, 335, 594, 465, 359, 293, 355) and the seven "days after" values (219, 225, 299, 376, 418, 546, 515) come back digit for digit, and the sampled seven turn out to be seven consecutive runs of 2026-09-06 with fifteen more either side of them that the sample never saw. #3119's separately-read closed day (2026-09-06, 22 runs, maximum 778.4 s) also reproduces.

The population is republished in full — 9 + 48 — so the estimator is still recomputable rather than trusted. Range 194–896 s, total 27799 s, median 418 s.

The estimator is unchanged and the decision was re-taken rather than inherited

The doc's reason for the maximum had two halves, and one was written to expire. At 16 readings the 95th percentile by nearest rank was the maximum, so a percentile bought no headroom. At 57 it is 830 s, 66 s below the maximum, and the 90th is 786 s, 110 s below. RefreshCeilingProvenancePinTests went red on exactly that clause, which is what it was for. The decision comes out the same way on the half that never referenced the sample size — a scheduling exclusion may accept no exceedance over its own record, and a 95th percentile is a promise to be wrong three times in sixty runs — so the estimator stays the maximum and the expiring clause is gone with the argument it guarded. There is no replacement expiry, because a reason independent of n has nothing left to expire; both percentiles and both gaps are now pinned as readings that track the population.

Three arguments in the old derivation expired with the sample, and none was re-argued to fit

  • The mean is not exactly stateable. 27799 s over 57 runs is 487.7017… s. The prose carries the exact total instead, which is a strictly stronger pin — a bump to any middle-ranked reading moves neither the range nor the median, and the total is what sees it.
  • The excluded run is no longer an outlier. 864 s is 0.96x of the census maximum, inside the post-boundary range rather than 1.45x past it, so the duration-based disqualification is dead. That was always an artefact of a population whose maximum was 302 s lower; the rule is positional and always was.
  • The snapshot readings are no longer disjoint from the population — they cannot be. All three (342, 348, 286 s) are census members, because the self-metrics snapshot reads this job's last run and a census contains every run. Disjointness was a property of the sample, so that pin was measuring the sample's incompleteness rather than the rule. It is inverted to containment, which is the same evidence read the right way round and fails toward the more useful label: a quoted reading with no census run behind it means the two reads disagree about what the job did. The rule the paragraph is actually about — which SOURCE may set the constant — stays where it was already load-bearing, against the shipped SQL of all three reads.
  • Occupancy moves from tenths of a minute to seconds (896 of 900): a tenth of a minute is only exact while the ceiling is a multiple of six seconds, and demanding that a measurement be divisible makes an honest figure unstateable rather than catching a drift.

The consequence, stated and not settled here

RefreshPhaseSlotSeconds is 900. Clearance is slot minus ceiling: 306 s before, 4 s now. The compression grid's spatial clearance survives — the nearest phase minute is 1320 s past the heaviest slot's start, and the discriminating minute :14 is still excluded — so nothing has overrun a slot.

What broke is the watch line ordering. RefreshSlotWarningSeconds is 750, and 896 is 146 s ABOVE it, so ClassifyRefreshSlotHeadroom bands the grid's own sizing figure ApproachingSlot. Seven of the 57 census readings are already at or past that line, where the 16-run record had none. RefreshSlotWarningSeconds' own summary pre-registered this: "a ceiling that rose past this line would put the grid's own sizing figure inside the warning band, and the pin says so rather than leaving a reader to notice."

Six test methods are therefore RED, and every condition is left exactly as written.

file method condition
RefreshCeilingProvenancePinTests TheDerivedFiguresAreExactlyStateable_AndTheConstantIsThePopulationMaximum a population reading is at or past the watch line; and Ceiling < WatchLine
TimescaleSupportTests NoCompressionMinuteStartsWhileTheHeaviestRefreshIsStillRunning ceiling < RefreshSlotWarningSeconds; Assert.Equal(26, (watch - ceiling) * 100 / ceiling) (now -16)
TimescaleSupportTests TheRefreshSlotClassifier_BandsTheLiveReadings_AndKeepsTheWatchLineAboveTheRecordedCeiling ClassifyRefreshSlotHeadroom(ceiling) == InsideSlot (now ApproachingSlot); ceiling < watch
TimescaleSupportTests TheRejectedWatchLineAlternative_SitsBelowTheRecordedCeiling_WhileTheChosenLineSitsAbove same two
TimescaleSupportTests TheRefreshSlotReading_CarriesItsOwnVerdict_AndReportsOverrunAsNegativeHeadroom atTheCeiling.Headroom == InsideSlot
TimescaleSupportTests TheRefreshSlotLogLine_IsLeveledByBand_AndSaysNothingWithoutAReading the line at the ceiling is Warning:, not Debug:

Two failure MESSAGES in the pin file were re-pointed, because the reason they gave — "the doc comment claims otherwise" — is no longer why they matter now that the comment states the crossings plainly. Both conditions are byte-identical, so the tests still go red on the same input.

What the arithmetic says about the slot, and where it stops. Restoring the five-sixths line above 896 s needs a slot of at least 1076 s — 18 minutes, which 60 does not divide, so the grid would gain slots of unequal width or lose one of its four. Moving the fraction instead, or making the refresh cheaper, are the other two routes. Choosing among them is a scheduling decision (#3035, #3044, #3107) and is deliberately not taken by re-deriving a measurement.

Trend, not calibration

The daily maximum went 594 s (boundary-day tail, 9 runs) → 778 s (2026-09-06, 22 runs) → 896 s (2026-09-07, 24 runs). #3166 listed 702 / 871 / 815 s; the census found a 896.1 s run at 2026-09-07 23:00 that the issue did not have, so the readings in the issue were not the ceiling. A census removes the "as observed" caveat and puts nothing in its place — the population is closed at a stated instant and it grows.

Verification

RefreshCeilingProvenancePinTests is pure and reads the real TimescaleSupport.cs through [CallerFilePath], so it was compiled unmodified into a net10.0 console harness behind a minimal xUnit shim and run: 8 of 9 pass, including the full derivation Verify and the one-number-at-a-time drift sweep over every captured figure; the 9th is the watch-line row above. Red-first proven by reverting the constant to 594 in a committed tree, confirming EveryDerivationClaim_... fails on max == Ceiling, and restoring via git checkout --. The red list in the table was produced by a second harness that evaluates each ceiling-dependent condition against the real Storage assembly rather than by reading the assertions.

CHANGELOG

Not edited here — entry text is in the lane report for the coordinator to batch.

…ory census

The constant was 594 s, the maximum of a 16-run record that mixed a census
of the boundary day's tail with a sample of the days after it. The census
read the comment named as the fix has been done: every run of job
policy_refresh_continuous_aggregate query_store_stats_interval_hourly since
the 13:44:23 boundary, one row per run from
timescaledb_information.job_history, read at 2026-09-08 01:37Z.

57 succeeded runs after the boundary, 194 s to 896 s, median 418 s, none
failed and none without a finish time; 304 runs at or before it, median
1081.7 s, maximum 13300.7 s. The estimator is unchanged, so the constant is
896.

Two pre-registered triggers fired together. The 95th percentile is no longer
the maximum at 57 readings (830 s, 66 s below it), so the sample-size half of
the maximum-versus-percentile argument is retired and the decision is re-taken
on the half that never referenced n. And the sizing figure now sits 146 s
ABOVE RefreshSlotWarningSeconds, so every assertion holding the grid clear of
it is false. Those are left with their conditions exactly as written.
… state it

At 896 s of a 900 s slot the refresh still fits, so "inside one phase slot"
is true and load-bearing; the margin that made it "well inside" is 4 s and
naming it as comfort is the claim that has stopped being true.
@claude

claude Bot commented Sep 8, 2026

Copy link
Copy Markdown

Review

This PR re-derives HeaviestHourlyRefreshObservedCeilingSeconds from a full census (57 runs) instead of the old 16-run sample, moving it from 594 s to 896 s, and updates the pin-test infrastructure in RefreshCeilingProvenancePinTests.cs / TimescaleSupportTests.cs / TimescaleContinuousAggregateTests.cs to match. Darling-only change (TimescaleDB-specific); no Lite counterpart exists for this feature, so no parity gap there.

Confirmed: an untracked test failure alongside the acknowledged ones

The new ceiling (896 s) sits above RefreshSlotWarningSeconds (750 s) for the first time. The PR is unusually explicit about this inversion and deliberately leaves three assertions red, each with a doc-comment explaining why and pointing at the follow-up scheduling decision (#3035/#3044/#3107):

  • RefreshCeilingProvenancePinTests.csAssert.True(Ceiling < WatchLine, ...)
  • TimescaleSupportTests.csTheRefreshSlotClassifier_BandsTheLiveReadings_AndKeepsTheWatchLineAboveTheRecordedCeiling
  • TimescaleSupportTests.csTheRefreshSlotReading_CarriesItsOwnVerdict_AndReportsOverrunAsNegativeHeadroom

There's a fourth spot with the identical inversion that the diff doesn't touch and doesn't mention: TimescaleSupportTests.cs, NoCompressionMinuteStartsWhileTheHeaviestRefreshIsStillRunning (lines 2419–2424, [Collection("live-postgres")]):

Assert.True(
    ceiling < TimescaleSupport.RefreshSlotWarningSeconds,   // 896 < 750 is now false
    ...);
Assert.Equal(26, (TimescaleSupport.RefreshSlotWarningSeconds - ceiling) * 100 / ceiling);  // now negative, not 26

With ceiling = 896 and RefreshSlotWarningSeconds = 750, both assertions fail the same way the three acknowledged ones do. Since it's pure arithmetic on constants (no DB access despite the live-postgres gate), it'll fail deterministically in the "Darling PostgreSQL tests" CI job — but as an unexplained red rather than a documented one, unlike its siblings.

Bigger picture

Given how meticulously every other consequence of the census re-derivation is traced through the doc comments and tests, this looks like a genuine oversight rather than an intentional omission — worth the same treatment (updated message + tracking reference) as the other three, or a fix, before merge. Separately, worth a maintainer's explicit sign-off that merging with multiple known-red assertions (pending the #3035/#3044/#3107 grid/scheduling decision) is the intended workflow here, since it means CI goes red on dev until that follow-up lands.

Three of the six failing methods carried a comment explaining why they are
left failing and which decision owns the remedy; three did not, so the same
inversion read as an oversight in half the places it appears. Conditions are
unchanged in all six.

NoCompressionMinuteStartsWhileTheHeaviestRefreshIsStillRunning also needed the
distinction its two halves now have: the grid clearance genuinely passes (the
nearest compression minute is 1,320 s past the heaviest start), and only the
watch-line ordering and the 26%-gap literal derived from it are false.
… asserted

The open-question paragraph closed on "the excluded run is disqualified by
its duration alone", which was the belt to the boundary's braces. At a 896 s
population maximum 864 s is an ordinary member of the post-boundary range, so
that belt is gone and the sentence was contradicting the paragraph above it.
The positional rule needs no second reason, which is why it was chosen over
one.
@claude

claude Bot commented Sep 8, 2026

Copy link
Copy Markdown

Review summary

This PR is a pure Darling-side change (constant + doc-comment/test re-derivation in TimescaleSupport.cs and its three test files) - no Lite equivalent exists (TimescaleDB/job_history has no Lite counterpart, confirmed by grep), so there's no Lite/Darling parity drift here, and no T-SQL is touched.

I independently re-summed and re-sorted the published 57-value population (boundary-day tail + days-after list) by hand: total 27799, median 418, 95th-percentile-by-nearest-rank 830, 90th 786, count >= 750 (the warning line) 7. All of these match the PR's stated figures exactly, and the constant (896 = population max) is correct. No arithmetic errors found in the doc-comment claims.

Main concern: this PR ships with 6 tests deliberately failing, and those tests run as required CI checks

The PR description is explicit that six test methods go RED as a direct result of this change and are left that way on purpose, pending a future scheduling decision (#3035/#3044/#3107):

  • RefreshCeilingProvenancePinTests.TheDerivedFiguresAreExactlyStateable_AndTheConstantIsThePopulationMaximum
  • TimescaleSupportTests.NoCompressionMinuteStartsWhileTheHeaviestRefreshIsStillRunning
  • TimescaleSupportTests.TheRefreshSlotClassifier_BandsTheLiveReadings_AndKeepsTheWatchLineAboveTheRecordedCeiling
  • TimescaleSupportTests.TheRejectedWatchLineAlternative_SitsBelowTheRecordedCeiling_WhileTheChosenLineSitsAbove
  • TimescaleSupportTests.TheRefreshSlotReading_CarriesItsOwnVerdict_AndReportsOverrunAsNegativeHeadroom
  • TimescaleSupportTests.TheRefreshSlotLogLine_IsLeveledByBand_AndSaysNothingWithoutAReading

I checked .github/workflows/build.yml: the "Run Darling tests" step (dotnet run --project Darling/Darling.Tests/Darling.Tests.csproj) has no continue-on-error, and it will run on this PR because the changed files are all under the Darling gate. The darling-pg job runs the same full suite via -trx TestResults/darling-pr.trx, and its own comments say it's meant to be a required check. None of the six methods are marked [Fact(Skip=...)] or otherwise excluded - they're plain [Fact]s that will fail as hard test failures.

Concretely, e.g. TimescaleSupportTests.cs line 2430 (Assert.True(ceiling < TimescaleSupport.RefreshSlotWarningSeconds, ...)) evaluates 896 < 750, which is false.

So, as far as I can tell, merging this PR as-is will turn the required Darling CI checks red on dev, and they'll stay red for every subsequent PR that touches Darling/core/root paths until the follow-on scheduling decision (widen the slot, move the watch line, or cheapen the refresh) actually lands. That's a materially different situation from the pre-existing "pin written to expire" pattern in this codebase (where a pin goes red later, organically, when reality drifts) - here the change that flips the six tests red is bundled into the same commit that's proposed for merge, with no companion fix.

Is the intent here to merge this with CI red and land the scheduling fix in a fast-follow, or should these six assertions be provisionally adjusted (with a tracking comment/issue) so CI stays green until #3035/#3044/#3107 is decided? Worth confirming explicitly before merge, since it affects everyone else's PRs through the shared required checks.

Everything else in the diff (the constant's derivation, the updated regex patterns in RefreshCeilingProvenancePinTests.cs, the containment-vs-disjointness inversion for the snapshot readings, the seconds-vs-tenths-of-a-minute occupancy restatement) checks out and is internally consistent with the shipped code.

Comment on lines +2389 to +2397
/// <para><b>The GRID half of this test still passes and the WATCH-LINE half now FAILS, on #3166's census
/// re-derivation, and the two conditions are left exactly as written.</b> The grid is genuinely clear: at
/// a 896 s ceiling the nearest compression minute is still 1,320 s past the heaviest refresh's start and
/// the discriminating minute is still excluded, so nothing overruns anything. What is false is
/// <c>ceiling &lt; RefreshSlotWarningSeconds</c>, and the 26%-gap literal derived from it — the sizing
/// figure is 146 s ABOVE the line, so the watch now reports the grid's own sizing rather than anything
/// new. Restoring the ordering means moving the line, moving the slot, or making the refresh cheaper,
/// which is a scheduling decision (#3035, #3044, #3107); re-typing either condition here is the only
/// edit that makes the failure go away while changing nothing about the store.</para>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doc comment (and the matching ones on the other five methods called out in the PR body) documents that the test's Assert.True(ceiling < TimescaleSupport.RefreshSlotWarningSeconds, ...) a few lines below now evaluates 896 < 750 and fails.

Worth confirming before merge: .github/workflows/build.yml's "Run Darling tests" step (and the darling-pg job, meant to be a required check per its own comments) run this suite with no continue-on-error and no Skip on these [Fact]s. So this isn't just a local/expected-red pin — merging as-is turns the required Darling CI checks red on dev until the follow-up scheduling decision (#3035/#3044/#3107) lands, blocking green CI for every subsequent PR that touches Darling/core/root paths in the meantime. If that's the intended trade-off, fine — but it's a different situation from this file's existing "pin written to expire" pattern (which goes red later, organically) since here the same commit causes and ships the red state together.

@erikdarlingdata

Copy link
Copy Markdown
Owner Author

Claude posting for Erik Darling

Closed as superseded by PR #3178, which merged to dev and absorbed four of this branch's commits. Not merged: this PR was red by design, and its two failing checks were exactly the two required checks on dev, so merging it would have blocked every subsequent PR.

Its work landed. The 594 → 896 re-derivation, the census, and the doc are all on dev via #3178. All six of the tests this branch deliberately left red are green there, and — the condition that mattered — none needed a band re-typed; they went green on geometry. The 'at least 1076 s' figure is corrected to 1,077 (the smallest s with s x 5/6 > 896), which was off by one because 1076 x 5 / 6 is 896 exactly and the classifier warns at >=.

Why it could not simply merge, recorded because the reasoning outlived the branch: its six reds needed watch > 896, so a slot of at least 1,077 s, so a step in {20, 30, 60} — and every one of those reddens the contention guard, while Other*4 < Heavy stays red at all steps because it contains no step term. So this branch was blocked on a grid redesign, not on the scheduling decision it was waiting for.

The lane's discipline here is the reason the redesign happened rather than a band being re-typed: it widened, inverted and deleted nothing, and named the 'provisionally adjust the six' option as the trap — each of those tests is an ordering or a verdict, so re-typing one greens CI while changing nothing about a 4-second clearance.

erikdarlingdata added a commit that referenced this pull request Sep 9, 2026
…of the PR bodies

The entry text does not only ride the PR description. #3150's own account of the previous
batch says 11 of its 66 entries came from a lane's handoff file in
`~/Documents/pm-issue-queue-inbox/inbox/` rather than the body, and three files there carry
dedicated entry text inside this range: `3164-changelog-entry.md`, `3166-changelog-entry.md`
and `3169-changelog-entry.md`. Searching only the bodies missed the first two.

- **#3164** (PR #3176) is no longer written from the PR description. Its lane wrote a full
  entry, and that text is used verbatim: it carries the 1,036 blocks/s upper bound, the
  reason the decoupling #3153 deferred is unreachable, the declined command-deadline route,
  the census deliberately not restated at the new ceiling, and the mutation that caught
  nothing. `[#2997]` joins the definition block at its ascending position, which that text
  cites and the file did not define.
- **#3166** (PR #3168, absorbed and superseded by #3178) had no entry at all. #3178 grouped
  it into its own citation with a one-line summary, but its lane wrote a separate entry for
  the census that took `HeaviestHourlyRefreshObservedCeilingSeconds` from 594 s to 896 s and
  inverted the watch-line ordering. It sits beside #3178's, on #3178's merge.
- **#3191's entry** said "#3184 named four of the six". #3184 was squash-merged and the
  four-of-six state never reached `dev`; it was a commit inside that PR, corrected before
  merge. "shipped" in this file means merged, so the clause now says a commit in #3184, which
  is what the #3187 lane's own report establishes.

`CHANGELOG.md` only, 60 insertions and no deletions against `origin/dev`: 33 entries
(6 Added, 3 Changed, 24 Fixed) and 27 definitions. All 15 definition runs keep the ordering
they had, 9 of 15 ascending before and after.
erikdarlingdata added a commit that referenced this pull request Sep 9, 2026
…ix merged pull requests (#3213)

* Record the CHANGELOG entries for thirty-one changes across twenty-five merged pull requests

Applies the `[Unreleased]` entries for the pull requests merged to `dev` from #3150 (the
previous batch) through #3203. Lanes do not edit `CHANGELOG.md` -- every one of them appends
to the same block, so a per-PR edit conflicts with whichever sibling merges first -- and the
entry text rides each PR's own description instead. This is the periodic commit that applies
it.

`CHANGELOG.md` only, 56 insertions and no deletions: 31 entries prepended inside
`## [Unreleased]` (6 under `### Added`, 2 under `### Changed`, 23 under `### Fixed`), newest
merge first within each section, plus the 25 link-reference definitions they need appended at
the foot of the file. Nothing already in the block was edited, reworded or reordered.

Three of the 28 pull requests in the range earn no entry, each on measured precedent rather
than judgement: #3150 is the previous batch pass itself, and no batch pass -- #2260, #2980,
#3150 -- is cited anywhere in the file; #3130 and #3131 are routine Dependabot bumps, and
none of the six merged Dependabot pull requests in the repository's history is cited either.

Two pull requests carried no entry text anywhere, so theirs are written from their
descriptions: #3176 (`pg_index_bloat`'s measured block rate) and #3184 (the PostgreSQL
target's permissions section).

* Record #3205's entry, which arrived on the merged base mid-batch

#3205 merged while this batch was assembled, so `origin/dev` was merged in rather than rebased
and its entry lands on top. An entry omitted because it arrived mid-batch is how a backlog
restarts, and the batch's purpose is to make `[Unreleased]` current at its own merged base.

One entry under `### Changed`, newest merge first ahead of #3195's, plus its link-reference
definition. #3205 has no other pull request in the range behind it.

* Take #3164's and #3166's entries from the lane handoff files instead of the PR bodies

The entry text does not only ride the PR description. #3150's own account of the previous
batch says 11 of its 66 entries came from a lane's handoff file in
`~/Documents/pm-issue-queue-inbox/inbox/` rather than the body, and three files there carry
dedicated entry text inside this range: `3164-changelog-entry.md`, `3166-changelog-entry.md`
and `3169-changelog-entry.md`. Searching only the bodies missed the first two.

- **#3164** (PR #3176) is no longer written from the PR description. Its lane wrote a full
  entry, and that text is used verbatim: it carries the 1,036 blocks/s upper bound, the
  reason the decoupling #3153 deferred is unreachable, the declined command-deadline route,
  the census deliberately not restated at the new ceiling, and the mutation that caught
  nothing. `[#2997]` joins the definition block at its ascending position, which that text
  cites and the file did not define.
- **#3166** (PR #3168, absorbed and superseded by #3178) had no entry at all. #3178 grouped
  it into its own citation with a one-line summary, but its lane wrote a separate entry for
  the census that took `HeaviestHourlyRefreshObservedCeilingSeconds` from 594 s to 896 s and
  inverted the watch-line ordering. It sits beside #3178's, on #3178's merge.
- **#3191's entry** said "#3184 named four of the six". #3184 was squash-merged and the
  four-of-six state never reached `dev`; it was a commit inside that PR, corrected before
  merge. "shipped" in this file means merged, so the clause now says a commit in #3184, which
  is what the #3187 lane's own report establishes.

`CHANGELOG.md` only, 60 insertions and no deletions against `origin/dev`: 33 entries
(6 Added, 3 Changed, 24 Fixed) and 27 definitions. All 15 definition runs keep the ordering
they had, 9 of 15 ascending before and after.

* Use the staged #3184 entry text instead of one written from the PR description

#3184's entry now exists, staged by the lane that did the work, so the batch no longer
invents one. Used verbatim, in `### Fixed` where its author targeted it and for the reason
they gave: the section did not merely lack detail, it asserted "One role covers every
collector", which is false in the direction that fails silently and which an operator would
act on.

Their text carries what a description-derived entry could not: the `pg_read_all_data` grant
is PostgreSQL 14+ and the pre-14 fallback the section gave was `GRANT SELECT` on a schema,
which is not valid PostgreSQL at all; the six collectors that need an extension, four of
which additionally need `shared_preload_libraries` and a server restart; and why
`pg_index_bloat` is unaffected by any grant. Both figures are live pre-change store reads
rather than estimates.

The citation is `[#3184]`, the pull request, because the change has no issue behind it, and
its definition uses the `/pull/` form. `CHANGELOG.md` only, still 60 insertions and no
deletions against `origin/dev`.
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