Skip to content

fix(agent,db,api,docs): a cap was reported as a count, and four more - #513

Merged
cevheri merged 4 commits into
mainfrom
chore/backlog-round16
Aug 27, 2026
Merged

fix(agent,db,api,docs): a cap was reported as a count, and four more#513
cevheri merged 4 commits into
mainfrom
chore/backlog-round16

Conversation

@cevheri

@cevheri cevheri commented Aug 27, 2026

Copy link
Copy Markdown
Member

Round 16 of the backlog loop. Five settled entries close - B77, B78, B80, T6, T7 - plus two defects this round's own audit found and one its review round found. Every one of them is a figure or a sentence that claimed more than it measured.

What was wrong, and what it is now

B77 - the curated health reading reported a cap as a count. The projection carried slowQueryCount and activeSessionCount off lists that every provider caps. The audit measured it: 8 of the 8 providers that can return a slow-query row cap it (5 rows on MySQL, PostgreSQL, MSSQL, Oracle, MongoDB, Couchbase and ClickHouse, 10 on both of Trino's through one TRINO_HEALTH_LIMIT), every provider caps the session list, and none of those statements carries a slowness predicate at all - "slow" is the ordering. So the figure was the limit, permanently, on any server with that many digests.

The entry's premise turned out to be too kind. Where the source cannot be read the projection did not report 0 but 1, or 2: PostgreSQL without pg_stat_statements pushes [{ query: "pg_stat_statements extension not enabled" }], MongoDB with its profiler off does the same, and every SQLite database answers two synthetic integrity rows. No field name fixes a figure with no referent, so both counts are gone from the projection and from its declared fields. The slow-queries and sessions kinds carry those facts with their rows visible, which is the only form in which they mean anything - and the tool description the model receives, "one row of connection, size and cache figures", is now exact where it was incomplete.

The design that was considered and rejected: naming the cap beside the figure (slowQueriesSampled + slowQuerySampleLimit). It costs all 15 type-ids and buys the model a saturated constant plus a second number explaining that the first one is meaningless.

The same rule, one field over (not a backlog entry - found by this round's audit). MSSQL, Oracle and MongoDB initialised activeConnections to 0 and swallowed the read's failure into it, so a denied DMV, an unprivileged V$SESSION and a whole failed serverStatus reached the model as a measured zero connections - defeating the optionality HealthInfo.activeConnections exists for and which src/lib/db/types.ts mandates in words. All three now omit the key; a real 0 still reads as 0; both arms are pinned per provider. MongoDB's outer catch deliberately still resolves rather than rethrows - a rethrow would turn POST /api/db/health into a 5xx and the fleet row into error for a server that is up.

The same rule, one reading over (found by the review round). runCuratedRead sliced rows to the budget's ceiling and reported rows.length as rowCount, so a 500-row reading arrived at the model as "result, 200 row(s)" - while the docblock directly above promised "REFUSED rather than truncated ... a delivered result is a complete one". The budget's ceiling is not a request: it now refuses with the existing READING_OVER_BUDGET, the way the byte ceiling beside it always did. A limit the model asked for is a request and still delivers exactly it.

B80 - the engine refusal read the same paragraph twice. The route now carries a machine-readable refused: "engine-unsupported" marker, tied to AgentRunFailureReason with Extract<> on both sides so a rename cannot leave the wire value behind. String comparison against the posture paragraph was rejected: it makes prose the protocol. The error line says what happened to the request and points at the explanation with an aria-describedby that resolves, because "above" is not a claim a screen reader can check. With the card gone the line carries the server's full paragraph again - that arm has its own test.

T7 - three of four sentences were never rendered by a test. Four arms of a nested ternary are one line, so 100% line coverage was reached with three of the four decline sentences undriven. The selection is now an exhaustive switch with no default, one arm per line - and the test's own rows are typed, so a fifth reason fails typecheck in the test file whichever arm it is folded onto. The line gate alone does not catch a reason folded onto an existing arm; the review round proved that, and the docblock now says it rather than claiming otherwise.

T6 - a shared fixture answered what no server answers. defaultMockExecute used to answer a digest statement naming sql_text, a column that table does not have on any build - which is exactly how a health read asking for it survived every gate and two reviews. The repair had been prophylactic and unpinned. A rule list evaluated in recordCall, the one funnel every fixture in that file goes through, now records any statement no MySQL-family server would answer; a file-scope afterEach asserts the list is empty; two tests drive the guard itself. Reverting the shared fixture is red now. The guard's reach is stated honestly in its own docblock and in docs/providers/mysql.md: it inspects only statements a test actually sends, and one fixture's revert is still not caught.

B78 - a premise that no test named. A test now demonstrates the misattribution against the real tracker (a sibling charge landing inside the failing execution's span) and asserts maxConcurrentExecutions: 1 with that demonstration as its failure message. One assertion pins elapsedMs through the ledger WRITE rather than only the parse. The entry's own claim that "nothing fails if that constant is raised" was false and is corrected in the source comment, which had contradicted itself eleven lines apart.

How this was verified

Three fresh-context adversarial reviewers, each in its own worktree, ran mutations rather than reading reports. They confirmed the work and found nine things worth changing, all fixed here, including: an API_DOCS sentence that generalised over 400 shapes it had not checked (the connection resolver's 400 carries code and statusCode), a comment that put Trino in the wrong cap group, a MongoDB justification borrowed from a storage-engine claim to a network-layer field, and an MSSQL permission distinction that Microsoft's own documentation settles the other way - sys.dm_exec_sessions needs VIEW SERVER PERFORMANCE STATE on SQL Server 2022 and later, the same permission as the measured refusal, not a sibling.

Two of the reviewers' proposed fixes were measured and did not work: the sketched test for the refusal-code admit list kills neither of the two gates, because they are in series; the honest replacement is a hook-level test plus a docblock that stops claiming a guarantee the suite does not have.

In a real Chrome, against the bundled LibreDB sample with a local Ollama model: the error line reads "No run was opened. The notice above says why, and what still runs on this engine.", its aria-describedby resolves to the card, and the 406-character paragraph has exactly one visible copy where it had two (the second copy is the 1x1 sr-only safety claim, which is why the comment says "visible" rather than "once"). After Switch to Plan the card is gone and the line carries the full paragraph with the pointer cleared. Live on that route: three distinct 400 bodies - {error, refused} for the engine, {error, code, statusCode} from the resolver, {error} alone from mode validation - exactly as the corrected contract says.

Gates, all six run locally on the final tree: format clean; lint 0 errors; typecheck clean; knip clean; bun run test 13081 pass / 0 fail; bun run build clean; merged coverage 43694/43694 lines (100.00%).

Filed rather than fixed

Three new backlog entries, each with the number it would cost:

  • D36 - a slow-query source nobody could read is still a row on three providers and silence on two others. The fix is HealthInfo.slowQueries becoming optional: 15 type-ids with their docs and tests, plus five pinned sentences.
  • D37 - the identical fabricated zero survives in getOverview on the same three providers, and there a person sees it: the Overview panel renders a confident 0 connections. Two other providers' docs cite mssql.ts as the "0 means not published" precedent and move with it.
  • D38 - two more surfaces read a capped list as a count: QueriesTab's Queries and Slow cards over a list capped at 10, and Trino's table-stats read slicing to 25 tables inside the provider, where nothing above it can see that rows were dropped.

Also noted and not touched, because it is pre-existing and outside this round: the 202 sample for POST /api/agent/runs in docs/API_DOCS.md omits autoExecute and thread, which the route does return.

No src/exports/ file changed, so build:lib and attw are unaffected, and there is no version bump, so the chart and operator gates do not apply.

Five backlog entries and two defects this round's own audit found. Each one is
a figure or a sentence that claimed more than it measured.

B77 - the agent's curated `health` reading projected `slowQueryCount` and
`activeSessionCount` off lists that EVERY provider caps: 8 of the 8 that can
return a row cap it (5 rows on MySQL, PostgreSQL, MSSQL, Oracle, MongoDB,
Couchbase, ClickHouse; 10 on both of Trino's), and none of those statements
carries a slowness predicate at all - "slow" is the ORDERING. So the figure was
the limit, permanently, on any server with that many digests. Where the source
could not be read it was worse than a zero: PostgreSQL without
`pg_stat_statements` pushes one sentence wearing a row's clothes, MongoDB with
its profiler off does the same, and every SQLite database answers two synthetic
integrity rows. No name fixes a figure with no referent, so both counts are
gone from the projection and from its declared `fields`. The `slow-queries` and
`sessions` kinds carry those facts with their rows visible, which is the only
form in which they mean anything - and the tool description the model receives
("one row of connection, size and cache figures") is now exact rather than
incomplete.

The same rule, one field over: MSSQL, Oracle and MongoDB initialised
`activeConnections` to 0 and swallowed the read's failure into it, so a denied
DMV, an unprivileged `V$SESSION` and a whole failed `serverStatus` reached the
model as a MEASURED zero connections - defeating the optionality
`HealthInfo.activeConnections` exists for. All three now omit the key, a real 0
still reads as 0, and `src/lib/db/types.ts` no longer prescribes the `?? 0`
fallback it forbids 646 lines above.

The same rule, one reading over: `runCuratedRead` sliced rows to the budget's
ceiling and reported `rows.length` as `rowCount`, so a 500-row reading arrived
as "result, 200 row(s)" while the docblock promised "REFUSED rather than
truncated - a delivered result is a complete one". The budget's ceiling is not
a request: it now refuses, the way the byte ceiling beside it always did. A
limit the MODEL asked for is a request and still delivers exactly it.

B80 - starting an agent-mode run on an engine with no read-only statement path
rendered the posture's whole 406-character paragraph in the error line while
the amber card two elements above showed the identical text. The route now
carries a machine-readable `refused: "engine-unsupported"` marker (string
comparison against the paragraph would make prose the protocol), and the error
line says what happened to the request and points at the explanation with an
`aria-describedby` that resolves - because "above" is not a claim a screen
reader can check. With the card gone the line carries the full paragraph again.
Measured in Chrome against the bundled LibreDB sample: one visible copy of the
paragraph where there were two, and three distinct 400 shapes on that route,
only one of which carries the marker.

T7 - the conversation-decline sentence was chosen by a four-arm nested ternary,
and 100% line coverage was reached with three of the four sentences never
rendered in a test, because four arms on one line are covered by one render.
The selection is now an exhaustive `switch` with no `default`, one arm per
line, and the test's own rows are typed so that a fifth reason fails
`typecheck` IN THE TEST FILE whichever arm it is folded onto - the line gate
alone does not catch a reason folded onto an existing arm, and the docblock now
says so instead of claiming it does.

T6 - `defaultMockExecute` used to answer a digest statement naming `sql_text`,
a column that table does not have on any build, which is exactly how a health
read asking for it survived every gate and two reviews. The repair was
prophylactic and unpinned: reverting it left the suite green. A rule list
evaluated in `recordCall` - the one funnel every fixture in that file goes
through - now records any statement no MySQL-family server would answer, a
file-scope `afterEach` asserts the list is empty, and two tests drive the guard
itself. Reverting the shared fixture is now red.

B78 - what makes a failed statement's recorded span a delta rather than a sum
is one frozen constant, `maxConcurrentExecutions: 1`. A test now demonstrates
the misattribution against the real tracker and asserts the ceiling with that
demonstration as its failure message, and one assertion pins `elapsedMs`
through the ledger WRITE rather than only the parse.
Closes B77, B78, B80, T6 and T7 - deleted, not struck through, per this file's
own rule. Their three `docs/AGENT.md` B-series bullets go with them, because
the drift guard asserts containment in both directions.

Opens what the round measured and did not fix:

D36 - a slow-query source nobody could read is still a row on three providers
(PostgreSQL, MongoDB twice, SQLite unconditionally) and silence on two others
(Redis and MongoDB `return []` on the kind path where MySQL now rejects, so a
denied grant reaches the model as an empty reading the run prompt tells it to
treat as an answer). The fix is `HealthInfo.slowQueries` becoming optional,
which is 15 type-ids and their docs and tests, plus five pinned sentences - the
reason it is filed rather than done.

D37 - the identical fabricated zero survives in `getOverview` on the same three
providers, against `DatabaseOverview.activeConnections`, and there the user sees
it: the Overview panel renders a confident 0 connections on a server that has
plenty. Two other providers' docs cite `mssql.ts` as the "0 means not
published" precedent and will need moving with it.

D38 - two more surfaces read a capped list as a count. `QueriesTab` computes a
call total and a slow count over a list capped at 10 and labels them "Queries"
and "Slow"; Trino's table-stats read slices to 25 tables inside the provider,
where no marker above it can see that anything was dropped.
Deleting a backlog entry breaks every source comment that cites it - the class
T4 is open for, and the one this round would have shipped. Every `B77`, `B78`,
`B80` and backlog-`T7` reference in `src/` and `tests/` now points at #513
instead of an id that no longer resolves.

Untouched on purpose: the dozens of `T6`/`T7` mentions that carry an issue
prefix (`#328 T6`, `#329 T6`, `#331 T7`). Those are milestone task ids, not
backlog entries, and repointing them would be the same mistake in reverse.
@sonarqubecloud

Copy link
Copy Markdown

@cevheri
cevheri merged commit 3c49f95 into main Aug 27, 2026
19 checks passed
@cevheri
cevheri deleted the chore/backlog-round16 branch August 27, 2026 12:13
cevheri added a commit that referenced this pull request Aug 27, 2026
…red it (#514)

#511 and #513 branched from the same commit and each appended three entries to the
"Drivers and connections" section. The bodies landed at different offsets, so git merged
them without reporting a conflict and main carried D36, D37 and D38 twice. The file's own
rule, stated in its header, is that every id is unique across the whole file because
cross-references use the bare id; a duplicate makes every such reference ambiguous.

#511 reached main first, so its three entries keep D36-D38 and #513's are renumbered to
D39-D41. The single citation in src/lib/schema-diff/migration-generator.ts names #511's
D36 and is unchanged; nothing cited the renumbered three.

The one line both branches did edit, the section index for Drivers and connections, did
conflict, and the resolution dropped its id range and its count altogether. A section
index that states nothing cannot be wrong, which is why nothing caught that either. It is
restored as the entries make it: D1-D41, U17, U22-U23, 19 entries.

tests/unit/backlog-structure.test.ts is the guard that was missing. It derives the index
block from the entry bodies rather than trusting it: no id appears twice, the sections
listed are the sections present and their anchors resolve, every id a line names exists,
each range's endpoints are the real extremes, and the trailing count is the real number of
entries. The endpoints are checked as extremes rather than as one canonical rendering
because the file legitimately writes a prefix two ways, U17, U22-U23 in one section and
X2-X14 in another.

Both defects were confirmed to fail the new test before the fix, and each of the four
index assertions was confirmed to fail against a deliberately corrupted line, so none of
them passes vacuously. The existing citation guard in tests/unit/agent-documentation.test.ts
only ever asked whether an id exists, which a duplicate satisfies.
cevheri added a commit that referenced this pull request Aug 27, 2026
Deleting an entry leaves every bare id that cited it pointing at nothing. This is the third
round in a row where that happened, and T4 in the backlog is the standing record of the
class, so the repointing is part of the same PR rather than a follow-up.

Nineteen citations across 13 files now name #515, the PR that closed the work, which a
reader can actually open. They were spread across code comments (the migration generator,
the Trino introspection pair, three monitoring tabs), one provider doc and seven test files.

One is deliberately left alone. `tests/unit/backlog-structure.test.ts` says "main carried
D36, D37 and D38 twice", which is a statement about what happened in #511 and #513, not a
citation of an entry. It is still true, and D37 is still an open entry, so repointing it
would have made a correct sentence wrong - the same trap as blanket-repointing the five
comments about the "0 means not published" encoding, two of which describe `maxConnections`
and remain correct.
cevheri added a commit that referenced this pull request Aug 27, 2026
Deleting an entry leaves every bare id that cited it pointing at nothing. This is the third
round in a row where that happened, and T4 in the backlog is the standing record of the
class, so the repointing is part of the same PR rather than a follow-up.

Nineteen citations across 13 files now name #515, the PR that closed the work, which a
reader can actually open. They were spread across code comments (the migration generator,
the Trino introspection pair, three monitoring tabs), one provider doc and seven test files.

One is deliberately left alone. `tests/unit/backlog-structure.test.ts` says "main carried
D36, D37 and D38 twice", which is a statement about what happened in #511 and #513, not a
citation of an entry. It is still true, and D37 is still an open entry, so repointing it
would have made a correct sentence wrong - the same trap as blanket-repointing the five
comments about the "0 means not published" encoding, two of which describe `maxConnections`
and remain correct.
cevheri added a commit that referenced this pull request Aug 27, 2026
* fix(db,ui,docs): a refusal drawn as a measurement, in five places

Five backlog entries close. Every one is the same defect wearing different clothes: a
reading that could not be taken, or a list that was cut, reaching a reader as a confident
figure. The absence rule (#477) is house law here, and these are the places it was not
applied.

D40 - the Overview panel's connection count. `DatabaseOverview.activeConnections` is
optional for the reason its docblock gives, and SQL Server, Oracle and MongoDB all
initialised a local to 0 and swallowed the read's failure into it. So a denied
VIEW SERVER PERFORMANCE STATE, an unprivileged V$SESSION or a serverStatus without a
connections section drew a confident "0 connections" on a server with plenty. All three now
omit the key, and a real zero still reads as zero - both arms pinned per provider.

The existing SQL Server test was asserting the fabrication rather than a measurement:
`expect(typeof overview.activeConnections).toBe("number")` passed because the fabricated 0
is a number. Its purpose-built fixture was dead code, shadowed by a generic branch that
matched the same statement, so the assertion had never seen the value it was written for.

D41 - two surfaces reading a cap as a count. The Queries tab's *Queries* card summed the
calls of a list every provider caps at ten and labelled it the database's query count;
*Slow* was bounded by ten however many slow statements the server held. Measured against a
MySQL server holding 59 digests for one schema, both cards were the ceiling wearing a
measurement's label. On the provider side TRINO_MAX_STATS_TABLES cut the table-stats read
inside the provider, where no marker above it could see the cut; it now refuses rather than
truncating, and its refusal names the real count.

D36 - the migration generator emitted `ALTER TABLE ... ADD CONSTRAINT ... FOREIGN KEY` for
SQLite, which has no ALTER that adds a constraint, so the file was a syntax error wherever
it was run. There were two emission sites, not the one the entry described: the ALTER path
and, undocumented, `generateCreateTable`, which writes a new table's foreign keys as a
separate ALTER. libSQL had the same defect there, so its earlier fix was incomplete. The
two sites want different answers - in the CREATE path the key belongs inside the CREATE
TABLE, where SQLite accepts it and nothing is lost; in the ALTER path it cannot be
expressed and is declined with a comment naming table recreation.

D38 - `getConnectionInfo` masked `:secret@` in an authority and nothing else, so a libSQL
connection string carrying its whole token as `?authToken=` was returned in the clear. The
mask now covers the authority and the value of every credential-shaped parameter, in any
position. Two rounds of review found two more shapes in the fix itself: a credential
LEADING an ADO-style string was returned verbatim, and narrowing the authority pattern had
stopped masking `postgres://user:p/w@host/db`, which the old one masked.

What that second one cannot do is covered rather than hidden. `postgres://host:5432/tenant@acme`
has the identical shape and carries no secret, so masking one masks the other, and drawing
`***` over a port asserts a credential the string never carried - a fabricated reading, which
this rule forbids more strongly than it forbids the miss. The docblock says so and the test
file pins the gap, so it is visible rather than assumed closed. D42 carries the parse-based
fix.

U23 - the Storage tab labelled the remainder of its breakdown "Other (TOAST, FSM)" on every
engine. TOAST and the free space map are PostgreSQL structures; SQLite, libSQL, MySQL,
Oracle, SQL Server, ClickHouse and the HTTP engines have neither. On libSQL it read 4.00 KB
under that label against a real 64 KB database: the number right, the words another
engine's.

Two defects found in the same files while fixing those, both pre-existing:

- The Storage tab was the third reader of `errors.tables` and the only one that did not
  read it. A refused table read arrived as an empty list, `every()` was vacuously true, and
  the tab drew a measured 0 B for Tables and Indexes with the remainder absorbing the whole
  database at 100%. It now carries the engine's own sentence, the way its two sibling panels
  already do, while a genuinely empty database still reads 0 B.
- Quick Stats published three cap-bounded figures as counts: "Slow Queries" off a list
  capped at 10, and "Active" and "Idle" off one list capped at 50, which the two badges
  split while reading as the server's totals. All three now name the rows they measure.

MongoDB's `getOverview` had a second fabricated zero beside the first: `databaseSizeBytes`
is optional for the same stated reason, and the Storage tab keys its entire breakdown off
whether that key is present. So a denied serverStatus did not hide a number, it replaced an
honest refusal with a breakdown over a zero-byte database - and with the table read still
answering, the remainder went negative, which `formatBytes` renders as the literal string
"NaN undefined". The success path had the same shape through `|| 0`, and so did `getHealth`,
whose `databaseSize` string is the one the agent forwards to the model verbatim.

Provider docs now cite code by name rather than by line, enforced by a new guard. A line
number hand-copied into prose has nothing measuring it. The eight seam rows in both search
docs were stale at birth: they entered in one commit as 751/811/831/844/860/873/884/898
while that same commit's declarations sat at 808/868/888/901/917/930/941/955 - a uniform
+57, which is exactly why nobody noticed. The rows stayed in ascending order and read as a
consistent, plausible list. Today the offset is +65. The guard bans the form, so a correct
line number fails it too, and its scope statement names what is not measured yet rather than
implying coverage.

* docs(backlog): five entries close, seven open in their place

Deleted: D36 (the migration generator's invalid SQLite DDL), D38 (the connection-string
mask), D40 (the Overview panel's fabricated connection zero), D41 (a cap read as a count),
U23 (PostgreSQL storage vocabulary on every engine). The three section index lines are
recomputed from the surviving headings rather than edited by hand, which the structure guard
added last round now checks in both directions.

The seven that open were all measured while closing those five, and each one is here rather
than in the PR for a stated reason:

D42 - the mask cannot cover an authority password holding a character RFC 3986 reserves,
because `postgres://host:5432/tenant@acme` has the identical shape and holds no secret. The
fix is parsing, not a better pattern.

D43 - SQLite gained `ALTER COLUMN ... SET/DROP NOT NULL` in 3.53.0, measured on 3.53.0:
it rewrites the stored schema and is enforced on insert. The generator declines it on both
ids. It cannot simply stop: sqld ships 3.47.0, and the `sqlite` provider runs on whichever
SQLite its runtime bundles, so a migration file handed to a human would run on one
deployment and fail on another.

D44 - `databaseSizeBytes` is fabricated as 0 wherever the size is unknown, in 14 of 16
type-ids, and the Storage tab keys its whole breakdown off whether the key is present.
Cassandra's own comment is the precedent and states the argument. 14 provider files, 14
docs, 14 test files, which is why it is an entry.

D45 - on SQL Server 2019 and earlier the connection count is under-reported rather than
refused. Microsoft documents `sys.dm_exec_sessions` as row-filtered, not refused, and
`sys.configurations` as needing only `public` below 2022, so the statement SUCCEEDS and
returns the caller's own session. Distinguishing that needs a permission probe and a live
instance with an ungranted login.

D46 - one fabricated connection zero survives the sweep, in the shared search provider, and
three of the five comments citing the old encoding are now false while two remain true: for
`maxConnections` zero and absence really are one fact, which is what Druid and Trino cite.
The three false ones are corrected here; moving the encoding is the entry.

U24 - two absences the Storage tab still renders as measurements: a measured zero total
draws the remainder bar full, and the same zero puts the literal string "NaN undefined" in
the cell beside it, because `formatBytes` takes `Math.log` of a negative. A refused overview
also drops the engine's sentence in a file that carries it for two other panels.

DOC4 - 62 line citations remain in the provider docs and every checkable one is stale: of
69 machine-checkable before this round, 68 were wrong; after 18 fixes, 62 remain and all 62
are wrong, over 12 docs. The other 209 point at expressions and table rows, so no heuristic
can judge them and no figure is claimed for them.

* docs(src,tests,docs): the five closed entries left 19 dangling citations

Deleting an entry leaves every bare id that cited it pointing at nothing. This is the third
round in a row where that happened, and T4 in the backlog is the standing record of the
class, so the repointing is part of the same PR rather than a follow-up.

Nineteen citations across 13 files now name #515, the PR that closed the work, which a
reader can actually open. They were spread across code comments (the migration generator,
the Trino introspection pair, three monitoring tabs), one provider doc and seven test files.

One is deliberately left alone. `tests/unit/backlog-structure.test.ts` says "main carried
D36, D37 and D38 twice", which is a statement about what happened in #511 and #513, not a
citation of an entry. It is still true, and D37 is still an open entry, so repointing it
would have made a correct sentence wrong - the same trap as blanket-repointing the five
comments about the "0 means not published" encoding, two of which describe `maxConnections`
and remain correct.
cevheri added a commit that referenced this pull request Aug 27, 2026
…ds a total (#517)

* fix(db,ui,docs): an unknown size is absent, not zero, and a share needs a total

Two providers stated both facts in one object. Trino paired
`databaseSize: TRINO_UNAVAILABLE_TEXT` with `databaseSizeBytes: 0`, and the search
seam paired `SEARCH_UNKNOWN_TEXT` with `sizeBytes ?? 0`, so one overview said
"unavailable" in the string and "zero bytes" in the number from a single input.
Both now omit the key, which is what the optional field is for, and a real zero
still reads as zero. Apache Cassandra had already written the argument down.

The search seam also held the last unconditional `activeConnections: 0`. It is
absent now, and `getHealth()` stops composing a key it has no source for. Not the
last zero of any kind: Trino, Druid, ClickHouse and Couchbase each degrade a
refused monitoring read to no rows and then map the absent row to zero, which is
the same encoding by a longer route and is filed rather than fixed here.

The Storage tab is where the fabrications were visible, and three of its figures
divided by a total it did not have. A share now requires `totalSize > 0`, which
excludes the absent size and the measured zero alike, so the remainder bar is empty
instead of full and the two Quick Stats percentages no longer sit under real
megabyte figures. The remainder's BYTES are gated on the contradiction rather than
on the share: a 0 total beside per-table reads that answered rendered a negative
byte count, while `0 - 0 - 0` is an honest zero and stays one. A refused overview
now carries the engine's own sentence, the way two sibling panels do.

Verified end to end in Chrome against the SQLite sample: unpatched, the breakdown
reads 504 + 288 + 4 = 796 KB with bars at -36.68% / -63.82% / -99.4975%; with the
overview's size forced to 0 while the table read answers, the remainder reads N/A,
all three bars are empty, and no percentage is drawn anywhere.

The shared `formatBytes` refused nothing. Measured live: -1, NaN and both
infinities all rendered "NaN undefined", and a petabyte rendered "1 undefined" - a
number wearing a missing unit, which scans as a figure rather than as garbage.
Non-magnitudes return "N/A", the string three other places in this codebase
already use for a size they do not have; the ladder gains PB and EB; and the index
is clamped, which makes indexing past the array unreachable rather than moving the
boundary up two rungs.

Also corrects three claims this repo made about itself. The Storage tab defines its
own local byte formatter and never imports the shared one, so the negative
remainder rendered "-1536 B" and not "NaN undefined" as mongodb.md, a mongodb.ts
comment and the backlog entry all said - a review lane reported exactly that last
round and was overruled by a measurement of the wrong function. The search docs'
claim that `TableStats` cannot say "unknown" is false for its four optional size
fields. And a new comment claiming the last zero anywhere was narrowed to the last
unconditional one.

* docs(backlog): three entries close, four open in their place

D46 and U24 close outright. D44 closes three of its fourteen type-ids - the ones
that contradicted themselves inside a single object - and its counts moved for a
second reason: DuckDB arrived as a seventeenth type-id in #516 and already writes
the key conditionally, so it is a fourth correct provider rather than a fifteenth
fabricating one. The title now reads 11 of 17, the remaining-work list names all
eleven files, and MongoDB is explicitly off that list because both its paths were
already right.

Four entries open, each measured rather than inferred:

D50 - the search seam zeroes two OPTIONAL `TableStats` size fields for a closed
index. Not fixed alongside the rest because omitting them takes the Data figure
away from every open index in the cluster, through `tables.every(...)`, which is a
visible panel change that wants its own decision rather than a drive-by.

D51 - Trino, Druid, ClickHouse and Couchbase each swallow an unavailable
monitoring surface into an empty result and then map the absent row to 0.
ClickHouse's does it to six fields at once, so one refused read publishes a
fully-zeroed overview that reads as measured. Filed as one entry because the fix
is one decision applied four times: the degrade step already knows the difference
and discards it before the mapper can act. Redis and PostgreSQL are named as NOT
measured rather than folded in on resemblance.

U25 - `formatBytes` exists three times, and the two component-local copies have no
guard: measured, they render "-1 B", "NaN B", "Infinity GB" and an exabyte in
gigabytes.

U26 - the admin fleet total re-parses formatted strings and has no `tb` branch, so
a 1 TB database contributes one byte, and this round's PB and EB rungs add two more
spellings that land in the same arm. The real defect is that `FleetHealthItem`
carries no numeric channel at all, which is also what makes an honest absence
inexpressible there.

The section index lines were recomputed from the surviving headings rather than
merged. Both new ids had to move: this branch took D47 and D48 before #516 merged
and published its own, and the bodies auto-merged without a conflict while only the
index line reported one - the same mechanism as #513, caught this time by the guard
#514 added.
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