open-standards/jcp/tools/counter-resolution/README.md
Transaction Science 6b8f237f73
Some checks failed
no-vanity-metrics / Scan committed Markdown for vanity metrics (pull_request) Has been cancelled
no-vanity-metrics / WAI public text names no comparison (pull_request) Has been cancelled
no-vanity-metrics / WAI public text names no company or product (pull_request) Has been cancelled
standards workspaces build + test / jouleclaw-rs (pull_request) Has been cancelled
standards workspaces build + test / aac-rs (pull_request) Has been cancelled
standards workspaces build + test / arl-rs (pull_request) Has been cancelled
standards workspaces build + test / eoc-rs (pull_request) Has been cancelled
standards workspaces build + test / jcp-rs (pull_request) Has been cancelled
standards workspaces build + test / joule-code-rs (pull_request) Has been cancelled
standards workspaces build + test / joule-ui-rs (pull_request) Has been cancelled
standards workspaces build + test / joulecontract-rs (pull_request) Has been cancelled
standards workspaces build + test / jouledesk-rs (pull_request) Has been cancelled
standards workspaces build + test / joulehook-rs (pull_request) Has been cancelled
standards workspaces build + test / map-rs (pull_request) Has been cancelled
standards workspaces build + test / mesh-rs (pull_request) Has been cancelled
standards workspaces build + test / openpay (pull_request) Has been cancelled
standards workspaces build + test / proof-rs (pull_request) Has been cancelled
standards workspaces build + test / sandbox-rs (pull_request) Has been cancelled
standards workspaces build + test / sense-rs (pull_request) Has been cancelled
standards workspaces build + test / smart-byte-rs (pull_request) Has been cancelled
standards workspaces build + test / macOS energy backends (pull_request) Has been cancelled
jcp-gateway: let a server advertise that it enforces, and a client require it
Everything this gateway does was **private convention**. It gates, meters,
debits and seals every `tools/call`, `resources/read` and `prompts/get` — and a
client had no way to discover that and no way to insist on it. A client that
dropped the `_meta` block got served anyway, and a client talking to a gateway
that enforced *nothing* could not tell the difference. Enforcement nobody can
require is a courtesy, not a control.

`initialize` is now the one discovery method not relayed verbatim: the gateway
adds its own entry to `result.capabilities.extensions`, under the same
reverse-domain key it already uses for per-call side data — one name for one
thing. MCP's 2026-07-28 revision formalised `capabilities.extensions` for
exactly this, with reverse-domain third-party keys and a 12-month deprecation
policy behind it, so the advertisement has a defined place to live rather than
an invented one. It names the profile it seals with and the methods it
enforces, so a client can require the parts it needs instead of the bundle.

`require_jcp(initialize_response, required)` is the half that makes the
advertisement worth making. A client calls it before sending anything and
**fails closed**: an endpoint advertising nothing is refused, and so is one
that enforces `tools/call` but not `resources/read` — which is the case that
matters, since the resource read is the egress door that was open until
971ef435. Garbage is refused rather than interpreted.

Three tests. The advertisement round-trips through `require_jcp` with the right
profile and method list; a client refuses a bare endpoint, refuses a partially
enforcing one for the method it does not enforce, and refuses non-JSON; and
advertising preserves everything else — an upstream's `protocolVersion`,
`serverInfo`, its own `capabilities.tools`, and a third party's existing
`extensions` entry are all untouched, while a response that is not a JSON-RPC
result comes back byte-identical. That last property is the same one
`attach_receipt` has to have: never mangle a payload you do not understand.

`RequireError`'s `Display` is written out rather than derived, because this
crate carries no `thiserror` dependency and three variants are not worth adding
one for.

396 tests pass across the jcp workspace, 0 clippy findings in the crate.

Also records a measurement in `jcp/tools/counter-resolution/`: the Apple
`GPU Energy` rail's republish gaps, now over 90 s rather than 6. The short run
gave a max of 992.2 ms, which sits just inside a one-second timeout and invites
the conclusion that one second is enough. Over 90 s the same host produced
**three gaps past 1000 ms — 1446, 1919 and 2033 ms** (n=2936) — so about 1 idle
gap in 1000 exceeds a one-second wait and the worst was 2× it. It also pins the
condition the right way round: under sustained load the rail is tight (max
42 ms, n=435); the long tail belongs to **idle**. Anything waiting a bounded
time for this rail to republish should bound above ~2.5 s, or count the
timeout rather than be surprised by it.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-10-05 09:16:08 -04:00

4.3 KiB
Raw Permalink Blame History

What a counter can actually resolve

Two harnesses behind the numbers jcp-energy declares. Both are the evidence for a constant in the crate; neither is a test of the crate.

counter_resolution.py — the per-process CPU counter's update period

ri_energy_nj is the only source in the survey that attributes energy to a principal rather than to silicon, so whether it may grade a span decides whether Apple silicon has any process-attributed reading at all.

Burns this thread's CPU for a fixed interval, samples the counter 60 times per interval, and reports the fraction of intervals in which it advanced — the crate's own liveness criterion is 80%.

Measured on an M5 Max, macOS 27:

interval advanced median step clears 80%
10 µs 0% — no
50 µs 0% — no
100 µs 2% 20.4 mJ no
500 µs 5% 21.1 mJ no
1 ms 35% 2.8 mJ no
2 ms 63% 4.9 mJ no
5 ms 87% 9.6 mJ yes
10 ms 100% 19.0 mJ yes
50 ms 100% 92.9 mJ yes

The crate declares 10 ms, not the 5 ms where it first clears the criterion: over-declaring a period refuses spans that would have been fine, while under-declaring grades spans that were not, and at a ceiling only the second error is dangerous.

Note the shape below 1 ms. The counter does not lag smoothly — on the few intervals where it advanced at all, the median step was ~20 mJ, two orders above the 1 ms step. It accrues in lumps, which is the same batched-refresh pathology the crate already refuses on IOReport's CPU rails. A sampler that read it every 500 µs would see nothing most of the time and a 20 mJ jump occasionally, and would report both as measurements.

This is also why a per-operation figure for an 11.85 ms decision is taken as the mean of 200 decisions over a 2.6 s span rather than as 200 counter reads.

ioreport_rails.py — which Apple rails exist and advance

Enumerates the IOReport Energy Model group unprivileged (364 rails on an M5 Max) and prints each channel's name, unit and delta. What it establishes: GPU Energy is live in nJ, every CPU Energy / MCPU* / PACC* / *_SRAM rail is frozen for an unprivileged reader, and ANE0 is the only ANE channel — no ANE Energy aggregate, no sub-rails. Units differ within one sample, so a summer that assumes one unit is wrong.

gpu_rail_period.py / gpu_rail_idle_tail.py — when the GPU rail republishes

The GPU Energy rail has no fixed period. It republishes when there is activity to report and holds its value when there is not, so the number you measure depends entirely on which condition you measure in.

Sampling every 2 ms on an M5 Max, macOS 27, the gap between actual changes:

condition p50 p90 p95 p99 max n
sustained MLX matmul 13.1 ms 15.0 ms — — 42.0 ms 435
idle, 6 s 12.7 ms 91.0 ms — — 992.2 ms 112
idle, 90 s 13.1 ms 30.6 ms 86.2 ms 436.2 ms 2032.8 ms 2936

Three things follow, and the third is why the long run exists.

Under sustained load the rail is tight; the long tail is an idle property. That is the opposite of the intuitive reading, and it reconciles an earlier fleet observation that put the hold at "300–600 ms under load": the GGUF decode it came from is dispatch-latency-bound with idle gaps between tokens, so it was sampling idle behaviour. 300–600 ms is about the idle p98–p99 here, consistent with having seen that region and not past it.

A declared period has to be the idle worst case. A measurer cannot guarantee the fabric stays busy, and a span landing entirely inside one hold reads zero — which a naive grader would report as a measurement of zero. jcp-energy declares 1 s for this rail on that basis.

The sample max is not a bound. The 6 s run gave a max of 992.2 ms, which sits just inside a 1 s timeout and invites the conclusion that 1 s is enough. Over 90 s the same host produced three gaps past 1000 ms — 1446, 1919 and 2033 ms — so roughly 1 idle gap in 1000 exceeds a one-second wait, and the worst was 2× it. Any code that waits a bounded time for this rail to republish should bound above ~2.5 s, or treat the timeout as a counted outcome rather than a rare surprise.