Skip to content

Charging: Warn when the hardware never confirms a fixed limit - #60

Merged
d4rken merged 1 commit into
mainfrom
feat/hw-unconfirmed-warning
Aug 14, 2026
Merged

Charging: Warn when the hardware never confirms a fixed limit#60
d4rken merged 1 commit into
mainfrom
feat/hw-unconfirmed-warning

Conversation

@d4rken

@d4rken d4rken commented Aug 13, 2026

Copy link
Copy Markdown
Member

What changed

When a fixed charge limit is applied but the charging hardware never confirms it, the dashboard now shows an explicit warning ("the charging hardware hasn't confirmed the 80% limit yet — it may not be applying") instead of silently looking successful. Previously the "Applying…" spinner simply disappeared after 15 seconds whether or not the limit actually engaged — hiding exactly the failure class where the setting reads back fine while charging is never limited.

Technical Context

  • Adapters declare when a confirmation is expected (confirmationExpected): Pixel only for a plugged fixed limit on a live, unmasked channel — state 4 spans the entire plugged fixed-limit session, while Adaptive idles at the ambiguous state 1 and Unrestricted maps to the same 1, so their absence proves nothing and they never warn. Sync-readback and plug-latched (GrapheneOS) adapters carry no expectation structurally; GrapheneOS's post-replug case staying silent is deliberate scope.
  • The signal lives on ChargingState, not PendingRequest — mutating pending at expiry would infinite-loop the dashboard's settling-deadline observer. Deserving close review: computeUnconfirmedTarget + debounceUnconfirmed and their test matrices, and the clears in every apply publication path.
  • Three false-positive classes are designed out: an authoritative readback of a different configured policy (native/competing change) obsoletes the request instead of warning; a 15s stability debounce damps the plug-in transient (an old request legitimately reads state 1 for the ~12s HAL transition); a second scheduled refresh at the 30s threshold makes the warning appear even when the failing HAL emits no broadcast.
  • Residual limitations: on WSS-only Pixels a native change is indistinguishable from a failed old request (hidden settings unreadable — the journal wins and the warning may be wrong until any re-apply); whether wireless charging reports state 4 as reliably as wired is a pre-existing qualification gap; and a broken/thermal-masked status channel suppresses the warning rather than risking a false claim.
  • Also folds the per-refresh hardware reads into one sticky battery readout shared by the decode, the pending computation, and the expectation check (plug state must never be joined across two sticky reads).

@github-actions github-actions Bot added the enhancement New feature or request label Aug 13, 2026
@d4rken
d4rken changed the base branch from feat/adapter-ready-hint to main August 14, 2026 04:11
An ASYNC_HARDWARE settling window that expired without a hardware
confirmation was indistinguishable from success: the spinner vanished
and the dashboard read like the change applied. That silence hid the
silent-failure class where the configured setting reads back fine while
charging is never actually limited (the Settings Intelligence worker
not running, a HAL ignoring the key).

Adapters now declare when a hardware confirmation is EXPECTED
(confirmationExpected): Pixel expects one only for a plugged fixed
limit on a live, unmasked channel - state 4 spans the entire plugged
fixed-limit session, while adaptive idles at the ambiguous state 1 and
unrestricted maps to the same 1, and thermal states mask the signal.
A pure detector (computeUnconfirmedTarget) surfaces the standing
contradiction as ChargingState.unconfirmedTarget once a request is 30s
old (2x the settling window) and the expectation is still unmet; an
authoritative readback of a DIFFERENT configured policy - a native or
competing change - obsoletes the request instead of warning about it.
A 15s stability debounce (debounceUnconfirmed, carried across refreshes
in the repository) damps the plug-in transient, where an old fixed-limit
request legitimately reads state 1 for the ~12s HAL transition. The
settle scheduler enqueues a second one-shot refresh at the threshold so
the warning appears even when the failing HAL produces no broadcast of
its own.

The signal deliberately lives outside PendingRequest (mutating pending
at expiry would loop the dashboard's deadline observer). Every apply
publication - success, cancellation, metadata failure, write failure,
and the unsupported/needs-setup refusals - clears it, so a stale
warning can never sit over a new request or a state the detector's own
contract excludes. Sync-readback and plug-latched adapters carry no
expectation and never warn. The dashboard renders the contradiction as
an error-tinted warning line; single sticky battery readout per refresh
feeds the decode, the pending computation, and the expectation check.
@d4rken
d4rken force-pushed the feat/hw-unconfirmed-warning branch from 1ebb25f to 278fed3 Compare August 14, 2026 04:12
@d4rken
d4rken merged commit 3beb2f1 into main Aug 14, 2026
12 checks passed
@d4rken
d4rken deleted the feat/hw-unconfirmed-warning branch August 14, 2026 04:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant