Repository navigation
fix(trace): say what the metrics flag actually means, everywhere - #83
Merged
Merged
Conversation
Two claims about disabled metrics were false, and the console found both by
connecting a real client to a real server. Neither was reachable by the test
suite as it stood.
**"Empty when disabled" was never true.** `TraceMetrics.snapshots()` iterates
`byModule` with no reference to the flag; the flag gates `observe`, so turning it
off stops new observations and keeps everything already aggregated. A reverse grep
for that claim found it in four places, not one: the method's own javadoc, the
REST controller's, and -- worse -- the published guide in both languages, where a
consumer reads it. An empty result means "nothing was ever aggregated", never
"aggregation is off", and all four now say so.
**The `ready` ack disagreed with the MCP tool under the same field name.**
`TraceMetrics.enabled()` is this platform's answer to "are metrics on" and it
already folds in `trace.enabled`, because recording is gated before aggregation is
reached. `ModuleMetricsTool` reports that value. The ack I wrote reached past it
for the raw switch, so the same question had two answers depending on which
surface asked it. The ack now reports the effective value, and `metricsEnabled` is
defined in the contract as the conjunction rather than described by its value --
an implementation sending the raw switch is not conforming, which a wording that
only said "reports whether metrics are on" would have left open.
That fix removes one wrong answer and creates another, so the docs say that too:
`{tracesEnabled: false, metricsEnabled: true}` can no longer be reported, but on
`{false, false}` a client reading only `metricsEnabled` will offer to turn metrics
on, and turning them on changes nothing while recording is off. The field answers
"are metrics accruing", never "would enabling them help". The console raised this
after the first correction and it is the sharper statement of the limit.
Three tests, each failing on the specific regression rather than on shape:
retention across a disable, no accrual while recording is off although the switch
is on, and the ack carrying the effective value. The last one runs in the
configuration where the two readings diverge, so a change back to the raw switch
fails instead of shipping -- which matters because the test suite runs from
exploded classes and cannot otherwise observe this frame's honesty.
Verified: 599 tests, 0 failures, 0 skipped; PoC gate passes 6 combinations x 6
criteria with no SKIPPED and no unreported cell.
Co-Authored-By: htjulia <htjulia1@gmail.com>
htcom-code
force-pushed
the
fix/trace-flag-claims
branch
from
September 21, 2026 07:16
37b1534 to
1f22a11
Compare
The `ready` ack calls `tickMs` "the push period", and a client sizing a silence watchdog from it will read that as "a frame arrives every second". The ticker is scheduled with a fixed *delay*, so a cycle starts a second after the previous one ends -- and a cycle walks every module, recomputes the summary window over the whole ring, and serializes four frames to each connected client in turn. The effective interval stretches under exactly the load that makes a watchdog matter. So the number is a floor. A client that treats it as exact reports a healthy server as stalled, which is the failure the field was added to prevent. Found by the console session while consuming this contract: it sizes its stale timeout at six ticks and has room, but noticed that nothing in the contract said the value was a minimum. Co-Authored-By: htjulia <htjulia1@gmail.com>
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.
What & why
Two claims about disabled metrics were false, and the console found both by
connecting a real client to a real server. Neither was reachable by the test
suite as it stood.
"Empty when disabled" was never true
TraceMetrics.snapshots()iteratesbyModulewith no reference to the flag. Theflag gates
observe, so turning it off stops new observations and keepseverything already aggregated.
A reverse grep for that claim found it in four places, not one — the method's
own javadoc, the REST controller's, and, worse, the published guide in both
languages, where a consumer reads it:
An empty result means "nothing was ever aggregated", never "aggregation is
off", and all four now say so. The lesson is the scope rather than the rule: when
a comment is wrong, assume the claim has already been copied.
The
readyack disagreed with the MCP tool under the same field nameTraceMetrics.enabled()is this platform's answer to "are metrics on", and italready folds in
trace.enabled, because recording is gated before aggregation isreached.
ModuleMetricsToolreports that value. The ack added in #80 reached pastit for the raw switch:
So the same question had two answers depending on which surface asked it. The ack
now reports the effective value, and
metricsEnabledis defined in the contractas the conjunction rather than described by its value — an implementation
sending the raw switch is not conforming. Wording that only said "reports whether
metrics are on" would have left that open, and three implementations share this
contract.
The fix removes one wrong answer and creates another, so the docs say that too
{tracesEnabled: false, metricsEnabled: true}can no longer be reported. But on{false, false}a client reading onlymetricsEnabledwill offer to turn metricson, and turning them on changes nothing while recording is off. The field answers
"are metrics accruing", never "would enabling them help" — so
tracesEnabledstill has to be read first. The console raised this after the first correction and
it is the sharper statement of the limit.
Type of change
fix— bug fix (no new API)feat— new capability (dynamic loading, gates, isolation, MCP, debug, data access)perf— performance, no observable behaviour changedocs— documentation onlyrefactor/chore/test/ci— no behaviour changeThe Java API is unchanged; what changes is the value one SSE field carries.
Correctness & safety
Confined to the observability read path — no ClassLoader, lifecycle or gate code
is touched.
regression rather than on shape (see Verification).
TraceDisabledGatesMetricsTestboots a real context with
trace.enabled=false+trace.metrics.enabled=trueand asserts against the bound
ProteanProperties, not direct injection.Verification
gradle clean bootJarassemblesgradle testpasses (full regression, run after the jar exists) — 599tests, 0 failures, 0 skipped
docs/guide/04-rest-api.md+.ko.mdThree tests, each failing on the specific regression:
the row survives and the count does not grow.
served, nothing aggregates.
two readings diverge, so a change back to the raw switch fails here instead of
shipping.
The third matters because the suite runs from exploded classes and cannot
otherwise observe this frame's honesty — the same blind spot that let the version
in #82 go unchecked.