fix(db,agent,security,docs): the MySQL health panel fabricated an absence, and four more - #512
Open
cevheri wants to merge 2 commits into
Open
fix(db,agent,security,docs): the MySQL health panel fabricated an absence, and four more#512cevheri wants to merge 2 commits into
cevheri wants to merge 2 commits into
Conversation
…ence, and four more
D32 - MySQL's health slow-query read asked for `LEFT(sql_text, 100)` from
`performance_schema.events_statements_summary_by_digest`, a column that table does not have,
and a bare catch turned the resulting `ERROR 1054` into the row
`{ query: "Performance schema not available", calls: 0, avgTime: "N/A" }`. Measured on four
live servers (MySQL 26.7.0 on and off, Percona 8.4.11-11, MariaDB 12.3.2): the sentence
appeared on every one of them, including the two where `@@performance_schema` is 1 and the
Queries panel beside it returned real rows. The health read now uses the statement the panel
uses, one mapper feeds both, and an unreadable source empties the line instead of describing
itself - a fabricated absence is exactly what the absence rule (#477) exists to prevent.
`getSlowQueries()` no longer swallows either, so a denied grant (1142) or an absent tenant
schema (1049) reaches the Queries panel as the server's own sentence.
AU3 - `Cross-Origin-Opener-Policy: same-origin` is delivered from `securityHeaders()`, and
`Cross-Origin-Resource-Policy` is refused with the reason recorded beside the header rule:
CORP acts only on a subresource, the only delivery path that reaches a subresource is
`next.config.ts`'s build-time `headers()`, and CORP's correct value is the one in the set
that depends on deployment topology. What that refusal gives up is now tracked as AU4.
`next.config.ts` derives its baked set by probing `securityHeaders()` rather than restating a
list of names, so a header that varies with an option cannot be baked by accident.
B76 - `declined: "repointed"` is split out of the collapsed `"unavailable"` code, and the five
that stay collapsed each carry the reason they have no distinct remedy. The first draft of the
rail's sentence claimed the decline persists until the connection is pointed back; it does not -
the route writes the new identity onto the run it opens, so exactly one question is declined.
Both halves of the corrected sentence are pinned, and a route test pins the behaviour.
B12 - a statement that failed at the database records the span the tracker charged it, so the
rail's database-time meter no longer counts completed reads only. The charge is a delta, pinned
by a two-execution harness run; a ledger entry written before this change folds as a statement
with no duration rather than a measured zero.
B38 - a run whose workflow sends statements is refused on an engine with no read-only execution
path at the point it is opened: 400 with the posture's own sentence, before a run id, a capability
probe or a model turn is spent. The `operations` workflow still opens on the same connection,
because it sends no statement at all. The drift guard behind the refusal now measures every
provider in the `DatabaseType` union rather than restating the list.
Also restores two backlog entries #510 overwrote when it branched before #509 merged, renumbered
D34/D35, and repoints the citation that named the wrong entry.
Verified in Chrome against the built app: COOP present on the document and absent on
`/logo.svg` and every `/monaco/vs/*` response, editor, five Monaco workers, a query and the ERD
export all unaffected; the B38 refusal answering 400 with no run opened, and an `operations` run
succeeding on the same connection; the fabricated MySQL sentence gone from the monitoring panel.
B79 and B80 record what that drive found and could not reach.
… closed Deleting an entry when the work lands is this repo's convention, and T4 records what it costs: nothing checks a citation from a source comment, so every comment naming a deleted id goes silently wrong. Closing D32, B12, B38, B76 and AU3 would have left fifty-odd of them across `src/` and `tests/`. Each one was read rather than substituted. Where the sentence explains why the code is the way it is, it now cites #512, the change that made it so. Where the id was the NAME of a defect class rather than a pointer - "a marker keyed on the throw would be emitted for something other than off-ness, D32 one level up" - the class is named instead, because a PR number does not carry that meaning. Where a comment described the defect in the present tense although the same round had fixed it, the sentence was wrong and not just its id: `digestTableMockExecute`'s docblock claimed the shared fixture still invents digest rows. And where what survives is a residual rather than the closed entry, the citation moved to the entry that still exists - T6 for the shared fixture nothing pins, B78 for the delta that rests on one frozen concurrency constant. `grep -rnE '\b(D32|B12|B38|B76|AU3)\b' src tests next.config.ts e2e scripts` is now empty. Comment-only: `bun run test` 13062 pass / 0 fail, typecheck and format clean.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Backlog round 15. Five entries closed, two restored that a merge had deleted, six new ones opened for what the work found.
Every change below was implemented by an agent, reviewed by a fresh-context adversarial reviewer that reproduced its findings, repaired, and then verified by a second fresh-context pass that re-proved each finding closed by mutation rather than by reading the report. What the reviewers found is in the sections; nothing was adopted on a report's word.
D32 - the MySQL health panel stated a capability as absent on servers that had it
getHealth()asked forLEFT(sql_text, 100)fromperformance_schema.events_statements_summary_by_digest. That table has nosql_textcolumn - the digest text isDIGEST_TEXT;SQL_TEXTbelongs toevents_statements_current. A barecatchturned the resultingERROR 1054 (42S22)into a row:Measured through the real provider on four servers brought up for it:
@@performance_schemagetSlowQueries()on the same connection--performance-schema=OFFSo the panel beside it disproved the statement, and off-ness never reached that catch at all: an OFF server answers the digest table with zero rows rather than raising. The health read now uses the statement the Queries panel uses, one mapper feeds both surfaces, and an unreadable source leaves the list empty rather than describing itself - a sentence wearing a row's clothes is a fabricated measurement, and
slowQueryCountcounts rows.getSlowQueries()no longer swallows either, so a denied grant (ERROR 1142) or a tenant with noperformance_schemadatabase (ERROR 1049, OceanBase) now reaches the Queries panel asPanelUnavailablenaming the server's own error, instead of an empty list explained by an instruction that cannot fix it.What the reviewer found, and what the repair did. The first implementation named
getMonitoringSnapshot()as the carrier for the refusal reason, in both the code comment anddocs/providers/mysql.md. There is no such method -grepreturned only those two lines. The repair made the carrier real (getSlowQueries()rejects ->getMonitoringData()recordserrors.slowQueries->QueriesTab->PanelUnavailable) and pinned it with a 1142 fixture. Separately, the Performance-Schema-OFF test passed with the fix reverted: its mock answered the brokensql_textstatement with[[], []], modelling a server that does not exist. All three digest fixtures now refusesql_textthe way a real server does, and the verifier reproduced the vacuity both ways - reverted code plus the unfaithful fixture passes, reverted code plus the faithful one fails.AU3 - the two cross-origin headers, decided
Cross-Origin-Opener-Policy: same-originis delivered.same-originand notsame-origin-allow-popups: nothing here opens a popup it then scripts - OIDC is a top-level redirect in both directions, the onewindow.openpasses"noopener,noreferrer". It is read only for a top-level traversable, so it has no place in the baked set; it is a no-op in a non-secure context, so the plain-HTTP channels are unaffected; and it is deliberately not paired withCOEP: require-corp, which would demand a CORP header from every cross-origin subresource and break the documented off-originNEXT_PUBLIC_MONACO_VS_PATHsetup for a capability nothing here uses.Cross-Origin-Resource-Policyis refused, and the reason is structural rather than a doubt about the value. It could never have broken the editor - Fetch reaches the CORP check only when response tainting isopaque, which a same-origin load never is. What refuses it: CORP acts only on a subresource;src/proxy.ts's matcher skips subresources; the one path that does reach them isnext.config.ts'sheaders(), baked at build time - and CORP's correct value is the single one in the set that depends on deployment topology rather than on the application. There is nowhere a configurable CORP can land where it would act. What it gives up is a same-site cross-origin timing oracle, now written intodocs/SECURITY.mdunder Known limits and tracked as AU4.next.config.tsno longer restates a list of header names. It probessecurityHeaders()with every optionsrc/lib/security/headers.tsdeclares and drops any header whose value or presence moves - so the CSP's-Report-Onlyrename reaches the filter as an absence - plus a named document-only set. A new header is refused until it is classified: addingCOEP: require-corptosecurityHeaders()fails two tests rather than being stamped onto/logo.svg.B76 -
declined: "unavailable"collapsed six causes, and the split's premise was wrongdeclined: "repointed"is now its own code, and the five that stay collapsed each carry the reason they have no distinct remedy, beside the union rather than only in a report.The first draft justified the split on "this cause's remedy differs: it persists until the operator changes the connection back", and shipped a sentence saying so. That is false, and the reviewer traced it before I did: the route writes the new identity onto the run it opens, so the next question continues that run and the conversation carries normally. Exactly one question is declined. The same refuted argument appeared in the union's doc comment, the thread-context docblock and the route comment; all four are corrected. The test had pinned only the true first half of the sentence - both halves are pinned now, and a route test pins the behaviour ("the decline lasts one question").
B12 - a failed statement recorded no duration
ExecutionBudgetTrackerchargesmaxTotalRunMson the failure path as well as the success one, buttool-refusedcarried no duration, so the rail's database-time meter sat below what was actually enforced whenever a statement failed. The refusal now carries the span - a T2 contract change, made in the union's own voice: a duration is a measurement the server took, unlike the engine's message, which stays quoted text.The reviewer's mutation: replacing
chargedBeforeMswith0- turning the delta into the run's cumulative total - left 256 pass / 0 fail. The only equality assertion ran on a run whose first and only execution was the failing one, so the delta was trivially the total. A two-execution harness run now separates them, on both refusal branches; a ledger entry written before this change folds as a statement with no duration rather than a measured zero.B38 - a run was offered on an engine that could not run it
POST /api/agent/runsnow answers 400 with the posture's own sentence when the mode is agent and the requested workflow sends statements on an engine with no read-only execution path - before a run id exists, before the capability gate probes the model, before any drive. Theoperationsworkflow still opens on the same connection, because it sends no statement at all. Under Automatic the refusal lands after the classify call, since the workflow has to be known first.The reviewer's finding: the list this refusal reads was advisory copy until now (it decided a sentence on the login hero) and its drift guard named 2 of 15 engines. Implementing
queryReadOnlyon a provider without updating the list would have turned a wrong sentence into a 400 refusing a run the factory would have executed - with every test green. The guard now derives theDatabaseTypeunion and the factory's dispatch from source and readsqueryReadOnlyoff each real provider class, so drift fails in both directions. Proved: the same mutation (aqueryReadOnlystub onTrinoProvider) fails the new guard and passes the old one.Restored: two entries a merge deleted
#510 branched before #509 merged, and its copy of
docs/BACKLOG.mdoverwrote both entries #509 had added - the SSH host-key pin and the live-SSH fixture gap. Measured knowledge, gone frommain, withdocs/providers/README.mdleft citing "D32" for a pin while D32 had become the MySQL entry. Both are restored from35294140as D34/D35 (D32/D33 are #510's now), the citation is repointed, and the section index has its count back.Browser verification
Built app, real Chrome via Playwright,
bun starton 3111.AU3.
/logincarries seven headers includingcross-origin-opener-policy: same-originand nocross-origin-resource-policy./logo.svg,/monaco/vs/loader.js,editor.main.jsandeditor.main.csscarrynosniffandDENYand nothing else - the document-only classification holds at the wire, not only in the config object. In the page: one Monaco editor, five workers loaded (json,css,html,ts,editorWorkerHost), a real query returned 5 rows in 2 ms, the ERD rendered 85 nodes through the ELK worker,crossOriginIsolated === false, 0 iframes.B38. On Sample (LibreDB), agent mode, workflow named explicitly:
POST /api/agent/runs => 400, the rail rendered the refusal, "No activity yet" - no run opened, no classify call, no drive. Thenoperationson the same connection: opened, drove a live model, 8 steps, succeeded. Both arms, one connection.D32. MySQL (local) monitoring:
"Performance schema not available"is absent from the page. The Queries panel shows real digest rows - and the top row is the slow-query read itself, which is B77 below, seen live.What was NOT verified, and why
STORAGE_PROVIDER=localmode only the seeds are, and editing a seed writes a browser-local copy, after which the rail refuses the run on the eligibility path before any thread check. The decline is driven end to end at the API level and its sentence is pinned by a component test; what has never been observed is a person reaching it. Recorded as B79 rather than left as an assumption.src/andtests/naming entries that no longer exist, which is the classT4tracks. The second commit repoints every one of them, read rather than substituted - where the id was the name of a defect class the class is named instead, where a comment described a defect the same round had fixed the sentence was wrong and not just its id, and where what survives is a residual the citation moved to the entry that still exists (T6, B78).grep -rnE '\b(D32|B12|B38|B76|AU3)\b' src tests next.config.ts e2e scriptsis empty.Gates
formatclean -lint0 errors -typecheckclean -knipclean -test13058 pass / 0 fail -buildexit 0 -test:coverage+coverage:check43652/43652 lines, 100.00% -build:lib+attwgreen on every subpath -security:checkOK.Opened by this round
AU4 the timing oracle CORP would have closed - B77
slowQueryCountis a cap, not a count, and the rows behind it are our own introspection traffic (59 digests,LIMIT 5, top row the slow-query read) - B78 the delta is unambiguous only because one frozen concurrency constant says so - B79 the re-pointed decline is unreachable from the UI - B80 the refusal renders one paragraph twice - T6 a shared mock that answers a statement its engine refuses makes every test built on it a test of the mock.