You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
check-durability-degradation-log-level: loggerLevel cannot see the (logger.error ?? logger.warn)(…) fallback, so a loud catch reads as silent-swallow — and the spelling it CAN see prints nothing #9657
Sub-issue of #8897, which records the receiver-name half of this same function's narrowness. This is the call-shape half. Filed unassigned. Not a claim.
Found while implementing #9609, which adds runWideningAlters to DURABILITY_CRITICAL_CALLEES. Unlike #8897's finding, this one is not latent — it produced a false red on real, loud code, and it is measured rather than reasoned.
Note for triage: #8897's own restart-when trigger reads "any PR touches scripts/check-durability-degradation-log-level.mjs" — the #9609 PR does.
The blind spot
loggerLevel() requires the call expression's callee to be a property access:
(this.logger.error ?? this.logger.warn)(msg, meta) is a call on a parenthesized expression, so the guard returns undefined and the catch collects no levels at all. The gate then reports it as catch swallows the failure with no log at all — the harshest verdict in the file, on a site that is loud at runtime.
Measured, with the reproduction
On the #9609 branch, with runWideningAlters in the vocabulary and both widening catches calling the fallback inline:
✗ 2 durability-critical catch(es) degrade quietly
packages/drivers/driver-sql/src/sql-driver.ts:8216
found : catch swallows the failure with no log at all
Routing the same two calls through a named same-file helper whose body is if (this.logger.error) this.logger.error(...) else this.logger.warn(...) turns them green and correctly classified:
✓ durability-degradation log levels: 29 durability-critical catch seam(s), all loud …
packages/drivers/driver-sql/src/sql-driver.ts:8255 → recovers on one branch, loud (error@4021 via logDurabilityFailure())
Why this direction of error is the dangerous one
⛔ The spelling the matcher does accept is this.logger.error?.(…) — an optional call, whose callee is a plain property access. It is by far the cheapest way to turn this red green, and against a sink that has no error it prints nothing at all. SqlDriver.logger declares error optional by design, and hosts do inject { warn } sinks — the #9609 test fixture was one until that PR changed it.
So the gate's cheapest satisfaction here converts a loud degradation into a genuinely silent one. That is the same "a gate whose cheapest satisfaction is harmful has the wrong shape" argument the file's own FAILURE_PROPAGATION_CALLEES header makes about bolting on a logger.error. Measured: with the helper body replaced by this.logger.error?.(msg, meta), the gate stays green while the no-error-sink test goes red.
The population
7 call sites use the fallback idiom today, none currently reachable by the vocabulary, so nothing is red on main:
packages/drivers/driver-sql/src/sql-driver.ts — 4 sites (the ADR-0120 D4 NULL-safe unique refusal and its restore, plus two more)
packages/drivers/driver-turso/src/turso-driver.ts:396 — (this.logger.error ?? this.logger.warn).call(this.logger, message), a third shape again
The idiom exists because logger.error is optional on these driver classes. It is the correct way to write the call; the matcher is what cannot read it.
Teach loggerLevel the fallback chain: for a call whose callee is a parenthesized ??/||, collect the level of every branch. (error ?? warn) then contributes both, and the existing levels.filter(LOUD) decides — no new policy, since the file already passes a catch containing both a warn and an error. Does not cover turso-driver.ts's .call(…) spelling.
Declare the fallback as a propagation-style vocabulary entry, which is a third declared list to keep from going stale.
Weak preference for (2): it introduces no new declared names, reuses the file's existing "a loud level anywhere in the catch counts" semantic, and removes the pressure toward the harmful ?. spelling. But it IS a loosening of a gate matcher and should be measured against the whole scan population before landing — the file's own 收窄先行 discipline.
⛔ Deliberately not resolved inside the #9609 PR: choosing among these is a policy call on a gate's classification surface, and #9609's contained answer (a named helper the gate already follows) needed none of it.
Sub-issue of #8897, which records the receiver-name half of this same function's narrowness. This is the call-shape half. Filed unassigned. Not a claim.
Found while implementing #9609, which adds
runWideningAlterstoDURABILITY_CRITICAL_CALLEES. Unlike #8897's finding, this one is not latent — it produced a false red on real, loud code, and it is measured rather than reasoned.Note for triage: #8897's own restart-when trigger reads "any PR touches scripts/check-durability-degradation-log-level.mjs" — the #9609 PR does.
The blind spot
loggerLevel()requires the call expression's callee to be a property access:(this.logger.error ?? this.logger.warn)(msg, meta)is a call on a parenthesized expression, so the guard returnsundefinedand the catch collects no levels at all. The gate then reports it ascatch swallows the failure with no log at all— the harshest verdict in the file, on a site that is loud at runtime.Measured, with the reproduction
On the #9609 branch, with
runWideningAltersin the vocabulary and both widening catches calling the fallback inline:Routing the same two calls through a named same-file helper whose body is
if (this.logger.error) this.logger.error(...) else this.logger.warn(...)turns them green and correctly classified:Why this direction of error is the dangerous one
⛔ The spelling the matcher does accept is
this.logger.error?.(…)— an optional call, whose callee is a plain property access. It is by far the cheapest way to turn this red green, and against a sink that has noerrorit prints nothing at all.SqlDriver.loggerdeclareserroroptional by design, and hosts do inject{ warn }sinks — the #9609 test fixture was one until that PR changed it.So the gate's cheapest satisfaction here converts a loud degradation into a genuinely silent one. That is the same "a gate whose cheapest satisfaction is harmful has the wrong shape" argument the file's own
FAILURE_PROPAGATION_CALLEESheader makes about bolting on alogger.error. Measured: with the helper body replaced bythis.logger.error?.(msg, meta), the gate stays green while the no-error-sink test goes red.The population
7 call sites use the fallback idiom today, none currently reachable by the vocabulary, so nothing is red on
main:packages/drivers/driver-sql/src/sql-driver.ts— 4 sites (the ADR-0120 D4 NULL-safe unique refusal and its restore, plus two more)packages/drivers/driver-turso/src/turso-driver.ts:396—(this.logger.error ?? this.logger.warn).call(this.logger, message), a third shape againwarn, but AGENTS.md's degradation rule names DDL-that-did-not-run aserror#9609 branch, now routed through the helperThe idiom exists because
logger.erroris optional on these driver classes. It is the correct way to write the call; the matcher is what cannot read it.Options, none free
warn, but AGENTS.md's degradation rule names DDL-that-did-not-run aserror#9609 did, and which check-durability-degradation-log-level:collectLoggedLevelsonly recognises a logger named logger/log/console, so a catch that reports through an injected logger reads as silent to BOTH rules #8897's author gave a weak preference to for the sibling case.loggerLevelthe fallback chain: for a call whose callee is a parenthesized??/||, collect the level of every branch.(error ?? warn)then contributes both, and the existinglevels.filter(LOUD)decides — no new policy, since the file already passes a catch containing both awarnand anerror. Does not coverturso-driver.ts's.call(…)spelling.Weak preference for (2): it introduces no new declared names, reuses the file's existing "a loud level anywhere in the catch counts" semantic, and removes the pressure toward the harmful
?.spelling. But it IS a loosening of a gate matcher and should be measured against the whole scan population before landing — the file's own 收窄先行 discipline.⛔ Deliberately not resolved inside the #9609 PR: choosing among these is a policy call on a gate's classification surface, and #9609's contained answer (a named helper the gate already follows) needed none of it.
Related
collectLoggedLevelsonly recognises a logger named logger/log/console, so a catch that reports through an injected logger reads as silent to BOTH rules #8897 — parent; the receiver-name half ofloggerLevel's narrowness.return, so acatchthat degrades by FALLING THROUGH into an empty accumulator is structurally invisible #8845 — the read-seam rule'sreturn-keying blind spot; same meta-shape, third recorded instance there.check:durability-log-level结构性看不见「读接缝把故障答成空值」这一类 —— #4825 / #5108 全家都在闸门盲区里 #5186 / finding(devx): #5186 读接缝规则的「编造空答案」词表看不见「原样返回入参」这一形状 —— #6116 是实证样本 #6451 — the read-seam rule and its prior measured extension.