Skip to content

Release v0.102.0 — cordless health needs a second opinion; the trunk is not a phone - #98

Merged
tesseractAZ merged 3 commits into
mainfrom
fix/monitoring-accuracy
Sep 15, 2026
Merged

tesseractAZ merged 3 commits into
mainfrom
fix/monitoring-accuracy

Conversation

@tesseractAZ

Copy link
Copy Markdown
Owner

Summary

These fixes come from the 2026-09-14 review of the live logs.

  • False cordless "degraded" alerts. The WP826 scored three non-playback legs at MOS 2.2: an assistant leg and two wake-up-menu legs. The PBX ledger scored the same legs excellent from the handset's own RTCP (0 % loss, mes_tx ~88).
    • A low handset score now degrades cordless_health only if the matched ledger leg corroborates it. Otherwise it is published as last_mos_uncorroborated.
    • Every low score is captured to a private, capped /data/state/cordless-mos.jsonl, so the mechanism can be studied. It is still unknown, so this adds no hypothesis patch.
    • last_mos_age_s is now published.
  • Playback-gate bypass. A playback leg next to a real call could borrow that call's confirmation. Handset records now match their nearest leg, preferring the cordless's own. A playback nearest leg is skipped.
  • Mass-drop threshold. The trunk's Endpoint trunk transitions were counted as phones. The mass-drop set now counts phones only, and the heartbeat still records trunk transitions.
  • Warm-up and the cordless. On the last three restarts, warm-up settled with ext 19 down, and HA showed it offline for about 300 s after it was back.
    • Warm-up now also holds for ever-registered non-gateway phones. It is still capped.
    • worst_rtt_is_partial no longer reads false with the cordless missing.
  • Heartbeat. Rows carry next_sleep_s. poll_interval_s stays 300, because the devhealth rollup staleness check and the HA stale automation depend on it.

Testing

  • The full suite passes.
  • Behaviour is exercised through run() and the poll loops.
  • Mutation batteries were run on the implementation and on the review fixes, with no survivors.
  • The branch was reviewed adversarially and every review item was fixed.
  • A trial merge with the other fix branches passed 775 tests.
  • The leak scan is clean.

🤖 Generated with Claude Code

tesseractAZ and others added 3 commits September 14, 2026 18:22
…t for the cordless

Accuracy fixes from the 2026-09-14 log review (links-01, calls-03,
consistency-cordless-mos-vs-rtcp, links-02, the heartbeat half of links-05)
plus three verified misses.

devhealth
- A handset MOS below mos_min degrades cordless_health only when the matched
  ledger leg corroborates it on the transmit side: loss_tx_pct >= 1.0 or a
  credible mes_tx < 78. Those lines come from callqos's own classify
  thresholds, checked against 190 non-playback ledger legs (lowest credible
  mes_tx 83.4; one leg ever at >= 1 % tx loss). An uncorroborated low score
  is still published, with last_mos_uncorroborated: true.
- Every handset record scoring below mos_min is captured once to
  /data/state/cordless-mos.jsonl. The file is 0600, capped at 512 KiB, never
  written to /share, and uses a fourth _rotate_tail copy that the drift test
  now covers. Each row holds the whole rtpStatus record, the matched leg and a
  verdict. The mechanism behind the 2.2 is still unknown and is not patched.
- Handset records are matched to their NEAREST ledger leg, with playback legs
  kept and tagged. A record whose nearest leg is playback is skipped. This
  closes the bypass where a playback leg next to a real call was confirmed by
  that call.
- last_mos_age_s is published.

rtpmon
- The between-samples mass-drop set counts phone extensions only. Trunk
  transitions stay in the heartbeat record.
- Warm-up also waits for extensions that have ever registered and are not
  gateway ports (the cordless), still capped at WARMUP_MAX_POLLS. The softphone
  that has never registered cannot hold it open.
- worst_rtt_is_partial now counts the durable ever-registered set, so a fresh
  process sees the missing cordless on every warm-up cycle.
- Heartbeat rows carry next_sleep_s, the sleep that actually follows the row.
  The published poll_interval_s stays the steady interval (that half of
  links-05 was refuted).

Tests drive the real run() loops of both pollers. Mutation battery: 43 of 43
mutants killed, with green baselines before and after.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ered guards

Review of 3146283 (verdict ship, three minors, three nits). All addressed.

Whose leg judges the handset score (devhealth). Matching and corroboration
ignored the leg's extension, so another phone's leg that hung up nearer could
judge the cordless's score with that phone's transmit figures. A leg's ext is
the channel that ran the context: the cordless for its own calls and for the
playbacks aimed at it, the caller for a call made to it, the dialling phone for
a page dialled from a handset. With cordless_ext set (the option defaults to
19 and devhealth already reads it):
- the cordless's own leg is preferred unless another leg hung up more than
  CALLQOS_CLOCK_SLOP_S (5 s) nearer. The three 2026-09-14 degraded notices put
  the two clocks at most 0.7, 0.8 and 1.8 s apart;
- a nearest playback leg of any extension still skips the record, so a dialled
  page logged under the dialler cannot slip in, and an own playback leg
  preferred over a nearer call skips too;
- another extension's leg reads 'unmeasured' and cannot degrade, because its
  figures are about a different phone;
- the capture row records match_rule (own-ext / nearest).
Replayed over the live 300-leg ledger: 1242 records at cordless legs judge
identically; at other phones' legs the chosen leg never changes, and the only
difference is clean -> unmeasured (491).

Tests that were missing:
- an unmeasured leg (mes_tx 0.0) publishes a low score as
  last_mos_uncorroborated true, stays ok, captures 'unmeasured' (through run());
- the cordless counts toward the between-samples mass drop: four wired ports
  plus ext 19 is one notice naming 19 (through run());
- own-leg preference, the slop edge, the page and playback gates, and
  CORDLESS_EXT reaching the matcher through run().
An existing run()-driven stub had a fixed three-argument probe signature. When
the call site grew an argument, the swallowed TypeError hung the suite, so the
stub now has the real signature and a sleep cap that turns that into a failure.

Nits: stale load_callqos_ts / last_call_mos references in switchboard-config,
switchboard-backup-pre, test_switchboard_config and the test_devhealth
docstring; the corroboration comment now says 1.0 % is classify()'s documented
inaudibility bound, not one of its cut-offs; the DOCS warm-up sentence names
every gateway port plus the other ever-registered phones.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…is not a phone

- devhealth: a low handset MOS degrades cordless_health only when the matched
  ledger leg corroborates it (loss_tx >= 1 % or mes_tx below callqos's 'good'
  line); otherwise it is published as last_mos_uncorroborated. Every low score
  is captured to /data/state/cordless-mos.jsonl (root-only, 512 KB cap).
  last_mos_age_s is published. Handset records match their nearest ledger leg,
  preferring the cordless's own leg, and a playback-tagged nearest leg is
  skipped (closes the neighbouring-call bypass).
- rtpmon: the between-samples mass-drop set counts phones only; warm-up also
  holds for ever-registered non-gateway phones (the cordless), capped as
  before; worst_rtt_is_partial falls back to ever_registered in a fresh
  process; heartbeat rows carry next_sleep_s. poll_interval_s stays 300.

Found in the 2026-09-14 live log review. 682+ tests; mutation batteries clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tesseractAZ
tesseractAZ merged commit f02493e into main Sep 15, 2026
10 checks passed
@tesseractAZ
tesseractAZ deleted the fix/monitoring-accuracy branch September 15, 2026 01:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant