@webex/internal-plugin-metrics 3.12.0-webex-services-ready.4 → 3.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/.repo-context.json +466 -0
  2. package/.sdd/manifest.json +626 -0
  3. package/AGENTS.md +208 -0
  4. package/README.md +274 -0
  5. package/dist/call-diagnostic/call-diagnostic-metrics.js +155 -94
  6. package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -1
  7. package/dist/call-diagnostic/config.js +3 -1
  8. package/dist/call-diagnostic/config.js.map +1 -1
  9. package/dist/config.js +7 -0
  10. package/dist/config.js.map +1 -1
  11. package/dist/index.js +10 -1
  12. package/dist/index.js.map +1 -1
  13. package/dist/metrics.js +118 -5
  14. package/dist/metrics.js.map +1 -1
  15. package/dist/metrics.types.js.map +1 -1
  16. package/dist/network-telemetry.js +610 -0
  17. package/dist/network-telemetry.js.map +1 -0
  18. package/dist/new-metrics.js +47 -11
  19. package/dist/new-metrics.js.map +1 -1
  20. package/dist/privacy-and-security-permission-enricher/constants.js +18 -0
  21. package/dist/privacy-and-security-permission-enricher/constants.js.map +1 -0
  22. package/dist/privacy-and-security-permission-enricher/index.js +143 -0
  23. package/dist/privacy-and-security-permission-enricher/index.js.map +1 -0
  24. package/dist/privacy-and-security-permission-enricher/types.js +7 -0
  25. package/dist/privacy-and-security-permission-enricher/types.js.map +1 -0
  26. package/dist/privacy-and-security-permission-enricher/utils.js +71 -0
  27. package/dist/privacy-and-security-permission-enricher/utils.js.map +1 -0
  28. package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +12 -3
  29. package/dist/types/call-diagnostic/config.d.ts +2 -0
  30. package/dist/types/config.d.ts +8 -0
  31. package/dist/types/index.d.ts +2 -2
  32. package/dist/types/metrics.types.d.ts +7 -3
  33. package/dist/types/network-telemetry.d.ts +108 -0
  34. package/dist/types/new-metrics.d.ts +13 -1
  35. package/dist/types/privacy-and-security-permission-enricher/constants.d.ts +6 -0
  36. package/dist/types/privacy-and-security-permission-enricher/index.d.ts +40 -0
  37. package/dist/types/privacy-and-security-permission-enricher/types.d.ts +14 -0
  38. package/dist/types/privacy-and-security-permission-enricher/utils.d.ts +5 -0
  39. package/dist/types/unhandled-exception-telemetry/index.d.ts +60 -0
  40. package/dist/types/unhandled-exception-telemetry/utils.d.ts +6 -0
  41. package/dist/unhandled-exception-telemetry/index.js +331 -0
  42. package/dist/unhandled-exception-telemetry/index.js.map +1 -0
  43. package/dist/unhandled-exception-telemetry/utils.js +105 -0
  44. package/dist/unhandled-exception-telemetry/utils.js.map +1 -0
  45. package/docs/adr/index.md +27 -0
  46. package/docs/architecture.md +437 -0
  47. package/docs/getting-started.md +131 -0
  48. package/docs/index.md +52 -0
  49. package/docs/specs/README.md +88 -0
  50. package/package.json +11 -11
  51. package/schemas/repo-context.schema.json +796 -0
  52. package/src/call-diagnostic/call-diagnostic-metrics.ts +100 -30
  53. package/src/call-diagnostic/config.ts +2 -0
  54. package/src/call-diagnostic/docs/README.md +535 -0
  55. package/src/config.js +7 -0
  56. package/src/docs/README.md +703 -0
  57. package/src/index.ts +11 -0
  58. package/src/metrics.js +123 -5
  59. package/src/metrics.types.ts +13 -3
  60. package/src/network-telemetry.ts +757 -0
  61. package/src/new-metrics.ts +44 -2
  62. package/src/privacy-and-security-permission-enricher/constants.ts +33 -0
  63. package/src/privacy-and-security-permission-enricher/docs/README.md +356 -0
  64. package/src/privacy-and-security-permission-enricher/index.ts +142 -0
  65. package/src/privacy-and-security-permission-enricher/types.ts +21 -0
  66. package/src/privacy-and-security-permission-enricher/utils.ts +82 -0
  67. package/src/rtcMetrics/docs/README.md +447 -0
  68. package/src/unhandled-exception-telemetry/docs/README.md +536 -0
  69. package/src/unhandled-exception-telemetry/index.ts +404 -0
  70. package/src/unhandled-exception-telemetry/utils.ts +101 -0
  71. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +485 -21
  72. package/test/unit/spec/metrics.js +14 -5
  73. package/test/unit/spec/network-telemetry.ts +610 -0
  74. package/test/unit/spec/new-metrics.ts +190 -48
  75. package/test/unit/spec/privacy-and-security-permission-enricher.ts +318 -0
  76. package/test/unit/spec/unhandled-exception-telemetry/utils.ts +109 -0
  77. package/test/unit/spec/unhandled-exception-telemetry.ts +689 -0
@@ -0,0 +1,535 @@
1
+ ---
2
+ type: Module Spec
3
+ title: 'Call Analyzer diagnostics specification'
4
+ description: Responsibilities, boundaries, design, invariants, and verification for the Call Analyzer diagnostics module.
5
+ tags: [module, specification]
6
+ ---
7
+ <!-- sdd-generated-metadata
8
+ doc_kind: module-spec
9
+ generated_from: module-spec@0.3.0
10
+ generated_by: claude-code
11
+ approved_by: rarajes2@cisco.com
12
+ updated_at: 2026-10-07T04:50:31Z
13
+ validation_status: pass
14
+ -->
15
+
16
+ # Call Analyzer diagnostics
17
+
18
+ This source-local document at `src/call-diagnostic/docs/README.md` owns the stable specification for
19
+ **the Call Analyzer diagnostics module**. Ground every claim in repository evidence and link to the
20
+ [repository architecture](../../../docs/architecture.md) instead of repeating broader facts.
21
+
22
+ Related context: [documentation index](../../../docs/index.md) ·
23
+ [documentation agent instructions](../../../AGENTS.md)
24
+
25
+ ## Metadata
26
+
27
+ | Field | Value |
28
+ | ------------- | -------------------------------------------- |
29
+ | Owner | Cisco Webex JS SDK — telemetry |
30
+ | Source path | `src/call-diagnostic/` |
31
+ | Resource kind | module |
32
+ | Status | Active |
33
+ | Last verified | 2026-10-06 at `d6202dd73d` |
34
+ | Module id | `call-diagnostic` |
35
+ | Parent spec | [`src/docs/README.md`](../../docs/README.md) |
36
+ | Doc kind | Module spec |
37
+ | Coverage score | Pending coverage assessment |
38
+ | Validation status | pass (2026-10-07; validator runtime 01a1147a-c744-7083-9b15-95e91170c79a; 0 blocking, 0 important, 0 medium, 0 minor; conformance and source-fidelity gates pass, no code/spec mismatches found) |
39
+
40
+ ## Applicability
41
+
42
+ Sections preceded by an `Include if` comment are conditional. Retain a
43
+ conditional section only when source evidence or a confirmed developer answer
44
+ satisfies its condition.
45
+
46
+ | Condition ID | Status | Evidence or reason | Owned section |
47
+ | ------------------------------------ | ---------- | -------------------------------------------------------------------------------------------------------------------------- | ----------------------------- |
48
+ | `module.has_tiers` | N/A | The repository defines no operational or review tier policy for modules | Tier |
49
+ | `module.has_ui` | N/A | The module renders nothing | UI use-case flow |
50
+ | `module.crosses_service_boundaries` | Applicable | `src/call-diagnostic/call-diagnostic-metrics-batcher.ts` posts to the Webex metrics service | Cross-boundary use-case flow |
51
+ | `module.holds_client_state` | Applicable | Latency ledger in `src/call-diagnostic/call-diagnostic-metrics-latencies.ts`; delayed queues and limits in `src/call-diagnostic/call-diagnostic-metrics.ts` | Client state model |
52
+ | `module.enforces_domain_rules` | Applicable | Error-code mapping in `src/call-diagnostic/config.ts`; event limits and sanitization in `src/call-diagnostic/call-diagnostic-metrics.ts` | Business rules and invariants |
53
+ | `module.is_concurrent_async` | Applicable | Batched asynchronous submission and the deferred delayed-event flush | Concurrency and reactive flow |
54
+ | `module.owns_persistence` | N/A | No store, schema or migration; every structure is in-memory | Data, schema, and migration |
55
+ | `module.stateful_transitions` | Applicable | Delayed and immediate submission modes, and the Locus sync record lifecycle in `src/call-diagnostic/call-diagnostic-metrics-latencies.ts` | State machine |
56
+ | `module.exposes_wire_protocol` | Applicable | The diagnostic event envelope built in `src/call-diagnostic/call-diagnostic-metrics.ts` | Protocol and wire format |
57
+ | `module.ui_multi_screen` | N/A | No user interface | UI flow |
58
+ | `module.large_data_model` | N/A | The event model is owned externally by the Call Analyzer schema and only derived here | Data model |
59
+ | `module.returns_caller_errors` | Applicable | Event preparation throws when no call identity is supplied or when client type is unresolved | Caller-visible failure modes |
60
+ | `module.module_specific_conventions` | Applicable | Error-classification order and the empty-key sanitization exemption for media quality events | Module-specific rules |
61
+ | `module.published_package` | Applicable | The metrics class, latency class, utilities and config are re-exported from `src/index.ts` | Export stability |
62
+ | `module.embedded_in_host` | N/A | Not registered as a plugin; constructed by `src/new-metrics.ts` | Host integration and theming |
63
+ | `module.has_design_tradeoff` | Applicable | Delayed submission with preserved trigger time, and a fixed-priority error classification chain | Key design trade-off |
64
+ | `module.has_submodules` | N/A | Derived from the manifest module tree: this module has no child modules | Sub-modules |
65
+
66
+ ## Evidence register
67
+
68
+ | Evidence | What it establishes |
69
+ | --------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
70
+ | `src/call-diagnostic/call-diagnostic-metrics.ts` | Event construction, origin and identifier resolution, error-payload generation, event limits, delayed submission and the fetch path |
71
+ | `src/call-diagnostic/call-diagnostic-metrics-latencies.ts` | The latency timestamp ledger, precomputed latencies and the Locus sync latency record lifecycle |
72
+ | `src/call-diagnostic/call-diagnostic-metrics.util.ts` | IP anonymization, empty-key sanitization, error classifiers, build-type detection and the batch preparation step |
73
+ | `src/call-diagnostic/call-diagnostic-metrics-batcher.ts` | The transport to the client-metrics resource, the send-time stamp and the batch log lines |
74
+ | `src/call-diagnostic/config.ts` | Client error codes, the service-to-client error map, the client error payload catalog and the log prefixes |
75
+ | `src/prelogin-metrics-batcher.ts` | The unauthenticated pre-login transport this module uses when a pre-login identifier is supplied |
76
+ | `src/new-metrics.ts` | How the façade constructs this module, records timestamps and forwards delay state |
77
+ | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | Behavioral verification of origin, identifiers, submission routing, event limits, error payloads, delayed events and the fetch path |
78
+ | `test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts` | Verification of timestamp rules, clamping and Locus sync skew, TTL and cap behavior |
79
+ | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts` | Verification of sanitization, classifiers, build type and the join-time attachment in batch preparation |
80
+ | `test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts` | Verification of the batcher's request, success and failure paths |
81
+
82
+ ## Purpose and boundary
83
+
84
+ - Responsibility: building, enriching and submitting Call Analyzer events — client events, feature
85
+ events and media quality events — and maintaining the latency ledger from which join-time metrics
86
+ are derived.
87
+ - In scope: origin and identifier resolution; converting raw errors into Call Analyzer error
88
+ payloads; per-call limits on repetitive events; delayed submission; join-time and media setup
89
+ latency calculation; Locus sync latency records; the authenticated and pre-login submission paths;
90
+ and pre-built fetch options for submission during page unload.
91
+ - Out of scope: the event names and payload fields themselves, which the external Call Analyzer
92
+ schema owns; deciding when an event happens, which belongs to the meetings and calling layers;
93
+ and permission enrichment, which the sibling enricher module applies before events reach here.
94
+ - Consumers: `src/new-metrics.ts`, which owns the single instance of each class. The meetings plugin
95
+ and the device plugin reach the same instance through the façade, the latter to inject device
96
+ details. The classes, utilities and config are also exported from `src/index.ts`.
97
+
98
+ ## Structure and key files
99
+
100
+ | Path | Responsibility |
101
+ | ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------- |
102
+ | `src/call-diagnostic/call-diagnostic-metrics.ts` | The event builder and submitter; authoritative for envelope shape, routing, limits, error payloads and delay handling |
103
+ | `src/call-diagnostic/call-diagnostic-metrics-latencies.ts` | The latency ledger and every derived join-time calculation; authoritative for which timestamps are kept and how latencies clamp |
104
+ | `src/call-diagnostic/call-diagnostic-metrics.util.ts` | Stateless helpers and the batch preparation step that attaches join times and origin defaults |
105
+ | `src/call-diagnostic/call-diagnostic-metrics-batcher.ts` | The batcher bound to the client-metrics resource |
106
+ | `src/call-diagnostic/config.ts` | Error code constants, the service-to-client map and the client error payload catalog |
107
+
108
+ ## Public surface
109
+
110
+ | Surface | Consumer | Compatibility commitment | Source |
111
+ | ----------------------------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------- |
112
+ | `submitClientEvent` | `src/new-metrics.ts` | Queues when delay is requested; otherwise builds, limits and submits. Throws when no call identity is given | `src/call-diagnostic/call-diagnostic-metrics.ts` |
113
+ | `submitFeatureEvent` | `src/new-metrics.ts` | In-meeting only; submitted as a business-typed envelope | `src/call-diagnostic/call-diagnostic-metrics.ts` |
114
+ | `submitMQE` | `src/new-metrics.ts` | In-meeting only; exempt from empty-key sanitization | `src/call-diagnostic/call-diagnostic-metrics.ts` |
115
+ | `submitDelayedClientEvents`, `submitDelayedClientFeatureEvents` | `src/new-metrics.ts` | Submit every queued event with optional overrides, then empty the queue | `src/call-diagnostic/call-diagnostic-metrics.ts` |
116
+ | `buildClientEventFetchRequestOptions` | `src/new-metrics.ts` | Returns fetch options for the client-metrics or pre-login resource without submitting | `src/call-diagnostic/call-diagnostic-metrics.ts` |
117
+ | `getErrorPayloadForClientErrorCode`, `generateClientEventErrorPayload`, `isServiceErrorExpected` | Meetings plugin, through the façade | Map raw errors and codes to catalog payloads; the second caches by error object | `src/call-diagnostic/call-diagnostic-metrics.ts` |
118
+ | Event-limit, opt-out, Mercury status and device setters | Meetings and device plugins, through the façade | Mutate per-instance state consulted at build time | `src/call-diagnostic/call-diagnostic-metrics.ts` |
119
+ | `validator` | Overridable by callers | Called with each built event before submission; the default accepts everything | `src/call-diagnostic/call-diagnostic-metrics.ts` |
120
+ | `CallDiagnosticLatencies` | `src/new-metrics.ts`, meetings plugin | Timestamp, latency and Locus sync operations plus the derived join-time getters | `src/call-diagnostic/call-diagnostic-metrics-latencies.ts` |
121
+ | `CALL_DIAGNOSTIC_CONFIG`, `CallDiagnosticUtils` | Meetings plugin | Re-exported constants and helpers; codes and catalog entries are a wire-level contract | `src/index.ts` |
122
+
123
+ Event names and payload fields are not restated here; they are derived from the external Call
124
+ Analyzer schema through `src/metrics.types.ts`.
125
+
126
+ ## Dependencies
127
+
128
+ | Dependency | Why it is required | Failure behavior |
129
+ | ------------------------------------------- | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
130
+ | `@webex/webex-core` `StatelessWebexPlugin`, `WebexPlugin`, `Batcher` | Base classes and the batched transport | Network errors re-enqueue with backoff in the parent batcher; other errors reject the queued item |
131
+ | Meetings plugin, through the SDK instance | Basic meeting information, geo hint, configuration and stats analyzer | A missing meeting records a failed-to-send client metric and the event is not built |
132
+ | Device plugin, injected via `setDeviceInfo` | Device URL, organization, user and installation identifiers | Absent device falls back to the credentials user identifier, then to the pre-login identifier |
133
+ | `src/prelogin-metrics-batcher.ts` | The unauthenticated pre-login transport | Rejects when no pre-login identifier was saved |
134
+ | `src/metrics.js` `submitClientMetrics` | Records the failed-to-send metric when a meeting cannot be found | Fire-and-forget; its result is not observed |
135
+ | `@webex/event-dictionary-ts`, through `src/metrics.types.ts` | The authoritative event schema | A schema change surfaces at build time |
136
+ | `ip-anonymize`, `lodash`, `uuid` | Address truncation, deep merge and empty-key checks, event identifiers | A failed anonymization yields undefined and the field is dropped |
137
+
138
+ ## Requirements
139
+
140
+ | ID | WHAT | WHY | Source evidence | Test or example evidence | Assumptions or gaps | Confidence |
141
+ | --------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------ | --------------------------------------------------------------------- | ------------------- | ---------- |
142
+ | `MOD-001` | A client event is built in-meeting when a meeting identifier is supplied, pre-meeting when only a correlation identifier is supplied, and otherwise preparation throws | Call Analyzer correlates every event to one call; an event with no call identity cannot be attributed | `src/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none | Present |
143
+ | `MOD-002` | When a meeting identifier is supplied but the meeting cannot be found, a failed-to-send client metric is recorded instead of the event | The lost event must remain visible as a count rather than disappearing silently | `src/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none | Present |
144
+ | `MOD-003` | Origin requires a client type and sub-client type, from options or from meetings configuration, and throws when neither supplies both | Call Analyzer segments every event by client; an unclassified event is unusable | `src/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none | Present |
145
+ | `MOD-004` | Public and local network prefixes are anonymized before they enter the origin | Raw IP addresses are personal data | `src/call-diagnostic/call-diagnostic-metrics.util.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none | Present |
146
+ | `MOD-005` | The user identifier resolves from the device, then from credentials, then from the pre-login identifier | An event must carry the most authoritative identity available at the time it fires, including before device registration | `src/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none | Present |
147
+ | `MOD-006` | Empty keys are removed from client and feature events but not from media quality events | Call Analyzer rejects empty properties, while media quality intervals legitimately carry zeros and empty arrays that must be preserved | `src/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none | Present |
148
+ | `MOD-007` | Media render, receive and transmit start/stop events are sent at most once per correlation identifier and media type, or per share instance for share media | These events can fire repeatedly during one call and would otherwise flood Call Analyzer with duplicates | `src/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none | Present |
149
+ | `MOD-008` | ROAP message sent/received events are sent at most once per correlation identifier and ROAP message type | Repeated renegotiation would otherwise produce duplicate events of no extra diagnostic value | `src/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none | Present |
150
+ | `MOD-009` | Event limits can be cleared for all calls or for one correlation identifier only | A rejoin must reset its own limits without unblocking another concurrent call's limited events | `src/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none | Present |
151
+ | `MOD-010` | A raw error is classified in fixed priority order — browser media error, SDP offer failure, mapped service code, Locus 429/503, Locus service code, meeting info, network, unauthorized, then unknown | The first matching classifier wins, so the most specific diagnosis must be tried first | `src/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none | Present |
152
+ | `MOD-011` | A Locus HTTP 429 or 503 maps to the rate-limited or unavailable client code only when no earlier classifier matched and the raw error's request `uri` or `url` contains the literal substring `locus` | The same HTTP status from another service has a different meaning and must not be reported as a Locus condition; the check is a substring match, not a parsed host comparison | `src/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | Any URL containing `locus` anywhere, including in a path or query, also matches | Present |
153
+ | `MOD-012` | A generated error payload is cached per raw error object | The same error is often reported by several events in one failure; reclassifying it each time is wasted work and the cache flag is logged for diagnosis | `src/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none | Present |
154
+ | `MOD-013` | With delay enabled, client and feature events are queued with their original trigger time and submitted later with optional overrides | Events raised before the call's identity is known can be submitted once it is, without losing when they actually happened | `src/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none | Present |
155
+ | `MOD-014` | A client event with a pre-login identifier is submitted through the unauthenticated pre-login transport | Join flows that start before sign-in must still be diagnosable | `src/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none | Present |
156
+ | `MOD-015` | Client events are typed as diagnostic events and feature events as business events in the submitted envelope | Ingestion routes the two families to different stores | `src/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none | Present |
157
+ | `MOD-016` | Batch preparation attaches the join-time and media setup latencies that belong to each milestone event | Join-time metrics are only meaningful on the milestone that ends the measured span | `src/call-diagnostic/call-diagnostic-metrics.util.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts` | none | Present |
158
+ | `MOD-017` | Repeating milestones keep only their first timestamp, and a new local SDP resets the remote SDP timestamp | Retries and renegotiation would otherwise overwrite the start of the span being measured | `src/call-diagnostic/call-diagnostic-metrics-latencies.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts` | none | Present |
159
+ | `MOD-018` | Derived latencies are clamped to a non-negative 32-bit range | Clock jumps and missing timestamps must not produce negative or overflowing values in ingestion | `src/call-diagnostic/call-diagnostic-metrics-latencies.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts` | none | Present |
160
+ | `MOD-019` | A Locus sync record with any segment beyond the skew threshold is discarded; incomplete records expire after their TTL and are capped per dataset | Sleep and wake clock jumps corrupt sync timings, and churn must not grow memory without bound | `src/call-diagnostic/call-diagnostic-metrics-latencies.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts` | none | Present |
161
+ | `MOD-020` | Fetch options can be pre-built for a client event and later re-timed so triggered and sent times equal the moment of submission | During page unload the normal asynchronous path cannot complete; a keep-alive fetch must fire immediately with correct times | `src/call-diagnostic/call-diagnostic-metrics.util.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none | Present |
162
+ | `MOD-021` | A service error code is reported as expected only when its mapped catalog payload is in the expected category | Callers use this to keep expected failures out of error dashboards | `src/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none | Present |
163
+
164
+ ## Design overview
165
+
166
+ The module is three cooperating pieces owned by the façade: an event builder, a latency ledger, and
167
+ a batcher.
168
+
169
+ **The event builder** (`src/call-diagnostic/call-diagnostic-metrics.ts`) turns a name, a partial
170
+ payload and options into a complete Call Analyzer event. Every event family goes through the same
171
+ two stages: a family-specific object is built — in-meeting, pre-meeting or media-quality — and is
172
+ then wrapped by `prepareDiagnosticEvent`, which adds the event identifier, version, origin, trigger
173
+ time and sender country, and sanitizes empty keys for every family except media quality. Caller
174
+ payloads are deep-merged last, so a caller can override any computed field.
175
+
176
+ Origin and identifiers are resolved from the host on every event rather than cached, because they
177
+ change during a call: the meeting gains a Locus URL and conference identifiers, the device registers,
178
+ the user signs in. The identifier chain prefers the most authoritative source available and falls
179
+ back gracefully down to a pre-login identifier.
180
+
181
+ Errors are classified once and cached. `generateClientEventErrorPayload` walks a fixed-priority chain
182
+ of classifiers and stops at the first match; each match resolves a client error code that indexes the
183
+ payload catalog in `src/call-diagnostic/config.ts`. The order is the design: the most specific
184
+ diagnosis is tried first and the unknown code is only the last resort.
185
+
186
+ Submission is gated twice. Delay mode queues the call arguments, not a built event, together with
187
+ the original trigger time, so the event is built later with the identity known by then. The event
188
+ limiter then suppresses repeats of a small set of high-frequency media and ROAP events per call.
189
+
190
+ **The latency ledger** (`src/call-diagnostic/call-diagnostic-metrics-latencies.ts`) records a
191
+ timestamp for every event name the façade sees, plus precomputed latencies measured elsewhere. Join
192
+ time metrics are differences between pairs of timestamps, clamped, and are attached to the milestone
193
+ events during batch preparation rather than at build time. Locus sync latencies are a separate,
194
+ record-per-sync lifecycle keyed by meeting, dataset and tracking identifier, with skew rejection, a
195
+ pending TTL and a per-dataset cap.
196
+
197
+ **The batcher** (`src/call-diagnostic/call-diagnostic-metrics-batcher.ts`) inherits queueing and
198
+ backoff from `src/batcher.js`, runs the shared preparation step, stamps the send time on each item and
199
+ posts to the client-metrics resource.
200
+
201
+ ## Data flow and sequence coverage
202
+
203
+ Collection is an in-process method call; submission is a batched HTTP POST through the host request
204
+ pipeline. There are four operation groups, diagrammed separately where actor order or outcome differs.
205
+
206
+ | Operation group | Entry and outcome | Diagram or evidence | Failure and recovery coverage |
207
+ | -------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
208
+ | Submit a client or feature event | The façade forwards an event; it is queued when delayed, otherwise built, limited and batched | First sequence diagram below | Missing call identity throws; missing meeting records a failed-to-send metric; a limited event resolves unsent |
209
+ | Flush delayed events | Delay is disabled; every queued event is re-submitted with overrides and the queue empties | Second sequence diagram below | Each re-submission follows the first group's failure paths independently |
210
+ | Submit a media quality event | The façade forwards interval statistics for a meeting; the event is built unsanitized and batched | `src/call-diagnostic/call-diagnostic-metrics.ts` | No meeting identifier throws; a missing meeting records a failed-to-send metric |
211
+ | Unload-time fetch | Options are pre-built, then re-timed and fired with a keep-alive fetch | Third sequence diagram below | Not retried; the page is unloading |
212
+
213
+ ```mermaid
214
+ sequenceDiagram
215
+ participant Facade as newMetrics facade
216
+ participant CD as CallDiagnosticMetrics
217
+ participant Host as Meetings and device state
218
+ participant Limiter as Event limiter
219
+ participant Batcher as Call diagnostic batcher
220
+ participant Svc as Webex metrics service
221
+ Facade->>CD: submitClientEvent(name, payload, options, delay)
222
+ alt delay requested
223
+ CD->>CD: queue arguments with the current trigger time
224
+ CD-->>Facade: resolve
225
+ else immediate
226
+ opt raw error supplied
227
+ CD->>CD: classify error, or reuse cached payload
228
+ end
229
+ alt meeting identifier
230
+ CD->>Host: read meeting, identifiers and origin
231
+ opt meeting not found
232
+ CD->>Facade: record failed-to-send metric
233
+ end
234
+ else correlation identifier only
235
+ CD->>Host: read pre-meeting identifiers and origin
236
+ else no call identity
237
+ CD-->>Facade: throw
238
+ end
239
+ CD->>CD: merge caller payload, wrap envelope, remove empty keys
240
+ CD->>Limiter: should this event be sent
241
+ alt limit reached
242
+ CD-->>Facade: resolve without sending
243
+ else pre-login identifier supplied
244
+ CD->>Batcher: pre-login batcher request
245
+ else authenticated
246
+ CD->>Batcher: request diagnostic-event envelope
247
+ end
248
+ Batcher->>Batcher: attach join times, stamp sent time
249
+ Batcher->>Svc: POST clientmetrics
250
+ opt network error
251
+ Batcher->>Batcher: re-enqueue with doubling delay
252
+ end
253
+ end
254
+ ```
255
+
256
+ ```mermaid
257
+ sequenceDiagram
258
+ participant Facade as newMetrics facade
259
+ participant CD as CallDiagnosticMetrics
260
+ Facade->>CD: submitDelayedClientEvents(overrides)
261
+ alt queue is empty
262
+ CD-->>Facade: resolve
263
+ else queued events
264
+ loop for each queued event
265
+ CD->>CD: submitClientEvent with options merged with overrides
266
+ end
267
+ CD->>CD: empty the queue
268
+ CD-->>Facade: resolve when all submissions settle
269
+ end
270
+ ```
271
+
272
+ ```mermaid
273
+ sequenceDiagram
274
+ participant Client as First party client
275
+ participant Facade as newMetrics facade
276
+ participant CD as CallDiagnosticMetrics
277
+ participant Core as WebexCore
278
+ Client->>Facade: buildClientEventFetchRequestOptions
279
+ Facade->>CD: build event and prepare it as a batch item
280
+ CD->>Core: prepareFetchOptions for clientmetrics or clientmetrics-prelogin
281
+ Core-->>Client: fetch options
282
+ Note over Client: later, on beforeunload
283
+ Client->>Facade: setMetricTimingsAndFetch(options)
284
+ Facade->>Facade: set triggered and sent to now
285
+ Facade->>Core: setTimingsAndFetch with keep-alive
286
+ ```
287
+
288
+ ## Class and component relationships
289
+
290
+ ```mermaid
291
+ classDiagram
292
+ class NewMetrics
293
+ class CallDiagnosticMetrics {
294
+ -callDiagnosticEventsBatcher
295
+ -preLoginMetricsBatcher
296
+ -delayedClientEvents
297
+ -delayedClientFeatureEvents
298
+ -eventErrorCache
299
+ -eventLimitTracker
300
+ +validator
301
+ +submitClientEvent()
302
+ +submitFeatureEvent()
303
+ +submitMQE()
304
+ +generateClientEventErrorPayload()
305
+ +buildClientEventFetchRequestOptions()
306
+ }
307
+ class CallDiagnosticLatencies {
308
+ +latencyTimestamps
309
+ +precomputedLatencies
310
+ +meetingLatencies
311
+ +saveTimestamp()
312
+ +saveLatency()
313
+ +getLocusSyncLatency()
314
+ }
315
+ class CallDiagnosticEventsBatcher
316
+ class PreLoginMetricsBatcher
317
+ class MetricsBatcher
318
+ class CallDiagnosticUtils {
319
+ +prepareDiagnosticMetricItem()
320
+ +anonymizeIPAddress()
321
+ +clearEmptyKeysRecursively()
322
+ }
323
+ NewMetrics --> CallDiagnosticMetrics : owns
324
+ NewMetrics --> CallDiagnosticLatencies : owns
325
+ CallDiagnosticMetrics --> CallDiagnosticEventsBatcher : authenticated path
326
+ CallDiagnosticMetrics --> PreLoginMetricsBatcher : pre-login path
327
+ CallDiagnosticEventsBatcher --|> MetricsBatcher
328
+ PreLoginMetricsBatcher --|> MetricsBatcher
329
+ CallDiagnosticEventsBatcher --> CallDiagnosticUtils : prepares items
330
+ CallDiagnosticUtils --> CallDiagnosticLatencies : reads join times
331
+ ```
332
+
333
+ ## Use cases and flows
334
+
335
+ | Use case | Actor or caller | Primary steps and outcome | Failure or boundary behavior | Evidence |
336
+ | -------- | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
337
+ | `UC-001` | Meetings plugin | A join milestone event is submitted in-meeting; identifiers and origin are resolved, the event is batched and join times are attached | A missing meeting records a failed-to-send metric instead | `src/call-diagnostic/call-diagnostic-metrics.ts`, `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` |
338
+ | `UC-002` | Meetings plugin | A join fails with a raw error; the error is classified into a catalog payload and attached to the event | An unrecognized error falls back to the unknown code, carrying the service code and name | `src/call-diagnostic/call-diagnostic-metrics.ts`, `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` |
339
+ | `UC-003` | Media layer | Media transmit start fires repeatedly for one call and media type; only the first is sent | Clearing limits for the correlation identifier allows the next one through | `src/call-diagnostic/call-diagnostic-metrics.ts`, `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` |
340
+ | `UC-004` | First party client | Events fire before the call identity is known with delay enabled; disabling delay submits them with overrides and original trigger times | An empty queue submits nothing | `src/call-diagnostic/call-diagnostic-metrics.ts`, `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` |
341
+ | `UC-005` | Guest before sign-in | A pre-join event carries a pre-login identifier and is posted to the unauthenticated resource | The pre-login batcher rejects when no identifier is saved | `src/call-diagnostic/call-diagnostic-metrics.ts`, `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` |
342
+ | `UC-006` | First party client | The tab is closing; pre-built fetch options are re-timed and fired with keep-alive | No retry; the page is unloading | `src/call-diagnostic/call-diagnostic-metrics.util.ts`, `test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts` |
343
+ | `UC-007` | Meetings plugin | Locus sync milestones are recorded; a complete record yields sync latencies | A skewed record is discarded; an incomplete one expires after its TTL | `src/call-diagnostic/call-diagnostic-metrics-latencies.ts`, `test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts` |
344
+
345
+ <!-- Include if: the module calls another service or crosses an event or network boundary. [condition-id: module.crosses_service_boundaries] -->
346
+
347
+ ### Cross-boundary use-case flow
348
+
349
+ - Boundary and transport: HTTPS POST to the `metrics` service, resource `clientmetrics`, or
350
+ `clientmetrics-prelogin` with authorization disabled and the pre-login header. The unload path uses
351
+ a keep-alive fetch prepared by the host.
352
+ - Ordering: items are batched in arrival order; no cross-batch ordering is promised. Each event
353
+ carries its own trigger time, and delayed events keep the time they were first raised.
354
+ - Compatibility: event names and fields are owned by the external Call Analyzer schema; error codes
355
+ and catalog entries in `src/call-diagnostic/config.ts` are a wire-level contract with Call Analyzer.
356
+ - Timeout and retry: network errors re-enqueue with doubling delay up to the configured plateau in the
357
+ parent batcher; other HTTP errors reject the item and are logged with a batch identifier.
358
+ - Recovery: none beyond batcher backoff. A rejected item is lost.
359
+
360
+ <!-- Include if: the module holds client-side state. [condition-id: module.holds_client_state] -->
361
+
362
+ ## Client state model
363
+
364
+ | State or slice | Owner | Initial state | Transition triggers | Reset or persistence boundary |
365
+ | --------------------------- | -------------------------- | ------------- | --------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
366
+ | Latency timestamps | `CallDiagnosticLatencies` | Empty map | Every event name recorded through the façade | Cleared by the internal join-latency reset event |
367
+ | Precomputed latencies | `CallDiagnosticLatencies` | Empty map | Latencies measured elsewhere and saved, optionally accumulated | Cleared with the timestamps |
368
+ | Locus sync records | `CallDiagnosticLatencies` | Empty map | Sync milestones per meeting, dataset and tracking identifier | Consumed on completion; discarded on skew; pruned by TTL; capped per dataset |
369
+ | Delayed client and feature events | `CallDiagnosticMetrics` | Empty arrays | Submissions while delay is enabled | Emptied when delayed events are submitted |
370
+ | Event-limit counters | `CallDiagnosticMetrics` | Empty map | A limited event is sent | Cleared wholesale or per correlation identifier |
371
+ | Error payload cache | `CallDiagnosticMetrics` | Empty `WeakMap` | A raw error is classified | Garbage-collected with the error, or cleared explicitly |
372
+ | Device, opt-out and Mercury status | `CallDiagnosticMetrics` | Unset or false | Setters called by the device and meetings plugins | Instance lifetime |
373
+
374
+ <!-- Include if: the module enforces domain rules or entity invariants. [condition-id: module.enforces_domain_rules] -->
375
+
376
+ ## Business rules and invariants
377
+
378
+ | ID | Invariant | WHY | Enforcement source | Test evidence |
379
+ | --------- | -------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------- | --------------------------------------------------------------------- |
380
+ | `INV-001` | No event is built without a call identity and a classified client | Call Analyzer cannot attribute or segment such an event | `src/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` |
381
+ | `INV-002` | No raw IP address reaches the origin | IP addresses are personal data | `src/call-diagnostic/call-diagnostic-metrics.util.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` |
382
+ | `INV-003` | Every classified error resolves to a catalog payload, ending in the unknown code | An error event without an error code is undiagnosable | `src/call-diagnostic/config.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` |
383
+ | `INV-004` | Limited events are sent at most once per limit key | Prevents duplicate high-frequency events per call | `src/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` |
384
+ | `INV-005` | Media quality events are never empty-key sanitized | Zero and empty interval values are meaningful | `src/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` |
385
+ | `INV-006` | Derived latencies are clamped to a non-negative 32-bit integer range | Ingestion must not receive negative or overflowing durations | `src/call-diagnostic/call-diagnostic-metrics-latencies.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts` |
386
+ | `INV-007` | Locus sync records per meeting and dataset never exceed the cap | Bounds memory under sync churn | `src/call-diagnostic/call-diagnostic-metrics-latencies.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts` |
387
+
388
+ <!-- Include if: the module is concurrent, asynchronous, reactive, or event-driven. [condition-id: module.is_concurrent_async] -->
389
+
390
+ ## Concurrency and reactive flow
391
+
392
+ - Execution model: synchronous event building on the caller's turn, then asynchronous batched
393
+ submission through the `Batcher` queue inherited from `src/batcher.js`.
394
+ - Ordering guarantees: none across batches. Within a batch items keep arrival order. Delayed events
395
+ are re-submitted in queue order but settle independently.
396
+ - Idempotency and retry: each event has a fresh identifier, so a re-enqueued item is the same event,
397
+ not a duplicate. The event limiter is the only deduplication, and it is per limit key.
398
+ - Shared-state protection: single-threaded. All maps are mutated synchronously on the caller's turn.
399
+ - Blocking restrictions: event building must stay synchronous so the unload path can prepare options
400
+ in time; only the transport is asynchronous.
401
+
402
+ <!-- Include if: the module has non-trivial state transitions. [condition-id: module.stateful_transitions] -->
403
+
404
+ ## State machine
405
+
406
+ Two lifecycles matter: the façade-controlled submission mode, and one Locus sync latency record.
407
+
408
+ ```mermaid
409
+ stateDiagram-v2
410
+ state "Submission mode" as Mode {
411
+ [*] --> Immediate
412
+ Immediate --> Delayed: delay enabled
413
+ Delayed --> Delayed: event queued with trigger time
414
+ Delayed --> Immediate: delay disabled, queue submitted with overrides and emptied
415
+ }
416
+ state "Locus sync record" as Sync {
417
+ [*] --> Pending: first sync milestone
418
+ Pending --> Pending: further milestones recorded
419
+ Pending --> Completed: sync response and message received both present
420
+ Completed --> Reported: latencies read and record consumed
421
+ Completed --> Discarded: a segment exceeds the skew threshold
422
+ Pending --> Discarded: older than the pending TTL, over the per-dataset cap, or cleared by tracking id
423
+ Reported --> [*]
424
+ Discarded --> [*]
425
+ }
426
+ ```
427
+
428
+ Rejected transitions: delayed events are never submitted while the façade is not ready; a sync record
429
+ missing a required milestone is never reported, it returns undefined and is cleared.
430
+
431
+ <!-- Include if: callers depend on a wire protocol or serialized format. [condition-id: module.exposes_wire_protocol] -->
432
+
433
+ ## Protocol and wire format
434
+
435
+ | Message or frame | Version | Producer or serializer | Consumer or parser | Compatibility and ordering rule |
436
+ | ---------------------------- | ------- | --------------------------------------------------------- | ------------------------- | ----------------------------------------------------------------------------------------------- |
437
+ | Diagnostic event envelope | `1` | `src/call-diagnostic/call-diagnostic-metrics.ts` | Webex Call Analyzer | Fields are owned by the external schema; the envelope type tag is `diagnostic-event` |
438
+ | Feature event envelope | `1` | `src/call-diagnostic/call-diagnostic-metrics.ts` | Webex business metrics | Same envelope with the `business` type tag |
439
+ | Client error payload | n/a | `src/call-diagnostic/config.ts` | Webex Call Analyzer | Codes and categories are a shared vocabulary; changing a mapping changes dashboards |
440
+ | Batch body | n/a | `src/call-diagnostic/call-diagnostic-metrics-batcher.ts` | Webex metrics service | A `metrics` array; the send time is stamped per item immediately before posting |
441
+
442
+ The envelope is declared through `src/metrics.types.ts`; it is not restated here.
443
+
444
+ <!-- Include if: the module returns or raises errors callers must handle. [condition-id: module.returns_caller_errors] -->
445
+
446
+ ## Caller-visible failure modes
447
+
448
+ | Condition | Signal or result | Caller behavior | Retry or recovery | Evidence |
449
+ | ----------------------------------------------------- | ------------------------------------------------- | --------------------------------------------------- | -------------------------------------------- | ----------------------------------------------------------- |
450
+ | Neither meeting nor correlation identifier supplied | Synchronous `Error` from event preparation | Supply call identity before submitting | None; caller bug | `src/call-diagnostic/call-diagnostic-metrics.ts` |
451
+ | Client type or sub-client type unresolved | Synchronous `Error` from origin resolution | Configure the meetings metrics client types | None; configuration bug | `src/call-diagnostic/call-diagnostic-metrics.ts` |
452
+ | Correlation identifier resolves to nothing | Synchronous `Error` from identifier resolution | Supply a correlation identifier | None | `src/call-diagnostic/call-diagnostic-metrics.ts` |
453
+ | Media quality or feature event without meeting identifier | Synchronous `Error` | Only submit these in-meeting | None | `src/call-diagnostic/call-diagnostic-metrics.ts` |
454
+ | Meeting identifier supplied but meeting missing | No throw; failed-to-send client metric recorded | None required | The event is dropped | `src/call-diagnostic/call-diagnostic-metrics.ts` |
455
+ | Batch transport rejects | Rejected promise from the returned submission | Callers through the façade normally ignore it | Network errors re-enqueue with backoff first | `src/call-diagnostic/call-diagnostic-metrics-batcher.ts` |
456
+
457
+ ## Pitfalls and constraints
458
+
459
+ - The error classifier order is the behavior. Inserting a classifier changes which code wins for
460
+ errors matching more than one; add a test for the overlap.
461
+ - The Locus 429/503 mapping checks that the raw error's request `uri` (or `url`) contains the literal
462
+ substring `locus`. It does not parse or validate the host, so any URL with `locus` in its path or
463
+ query also matches. Status codes alone are not enough, and a 429 whose URL lacks the substring stays
464
+ unmapped.
465
+ - Event-limit keys are colon-joined with the correlation identifier as the second token;
466
+ `clearEventLimitsForCorrelationId` depends on that layout.
467
+ - Delay mode stores call arguments, not built events, so identity and origin are resolved at flush
468
+ time. Only the trigger time is captured at queue time.
469
+ - `getIdentifiers` defaults the correlation identifier to the string `unknown`, which Call Analyzer's
470
+ UUID validation rejects; the limiter treats `unknown` as unlimited for the same reason.
471
+ - Device details are injected by the device plugin to avoid a circular import; do not import the
472
+ device plugin here.
473
+
474
+ <!-- Include if: the module has conventions beyond repository-wide rules. [condition-id: module.module_specific_conventions] -->
475
+
476
+ ## Module-specific rules
477
+
478
+ - Do: add new error mappings to `src/call-diagnostic/config.ts` as code-to-payload entries rather than
479
+ as branches in the classifier.
480
+ - Do: log through the module prefix constants in `src/call-diagnostic/config.ts`.
481
+ - Do: keep media quality events out of empty-key sanitization.
482
+ - Do not: hand-declare event names or payload fields; derive them from `src/metrics.types.ts`.
483
+ - Do not: make event building asynchronous; the unload fetch path depends on it being synchronous.
484
+
485
+ <!-- Include if: the module is published or consumed as a package. [condition-id: module.published_package] -->
486
+
487
+ ## Export stability
488
+
489
+ | Export or entry point | Consumer | Stability | Versioning and deprecation rule | Declaration or API report |
490
+ | ------------------------- | ---------------------------------- | --------------------------------- | ------------------------------------------------------------------------------------- | ------------------------- |
491
+ | `CallDiagnosticMetrics` | Meetings plugin, via the façade | Internal plugin export; additive | Public method names are consumed across the monorepo; change with consumers together | `src/index.ts` |
492
+ | `CallDiagnosticLatencies` | Meetings plugin | Internal plugin export; additive | Getter names feed join-time fields; renames are breaking | `src/index.ts` |
493
+ | `CallDiagnosticUtils` | Meetings plugin | Internal plugin export | Helpers re-exported as a namespace | `src/index.ts` |
494
+ | `CALL_DIAGNOSTIC_CONFIG` | Meetings plugin | Internal plugin export | Codes are a wire contract with Call Analyzer | `src/index.ts` |
495
+
496
+ <!-- Include if: the module has a non-obvious design trade-off consumers or maintainers must preserve. [condition-id: module.has_design_tradeoff] -->
497
+
498
+ ## Key design trade-off
499
+
500
+ | Chosen trade-off | Preserved invariant or benefit | Cost or limitation | Decision evidence |
501
+ | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
502
+ | Queue call arguments in delay mode and build at flush time, preserving trigger time | Events raised before identity is known are attributed correctly and keep their real timing | Origin and host state reflect flush time, not trigger time | `src/call-diagnostic/call-diagnostic-metrics.ts` |
503
+ | A fixed-priority error classifier chain ending in an unknown code | Every error yields exactly one deterministic code | Overlapping classifiers silently prefer the earlier one | `src/call-diagnostic/call-diagnostic-metrics.ts` |
504
+ | Attach join times during batch preparation rather than at build time | Latencies use the most complete ledger available at send time | Unload-path events built early may carry incomplete join times | `src/call-diagnostic/call-diagnostic-metrics.util.ts` |
505
+ | Discard skewed Locus sync records instead of reporting them | Sleep and wake clock jumps never pollute sync latency data | Some genuine long syncs are lost | `src/call-diagnostic/call-diagnostic-metrics-latencies.ts` |
506
+
507
+ ## Verification
508
+
509
+ | Requirement or invariant | Test level | Positive evidence | Negative or boundary evidence | Gap |
510
+ | ----------------------------- | ---------- | ---------------------------------------------------------------------- | ---------------------------------------------------------------------- | ---- |
511
+ | `MOD-001` / `INV-001` | Unit | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none |
512
+ | `MOD-002` | Unit | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none |
513
+ | `MOD-003` | Unit | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none |
514
+ | `MOD-004` / `INV-002` | Unit | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none found | No case asserts an unanonymizable address is dropped |
515
+ | `MOD-005` | Unit | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none |
516
+ | `MOD-006` / `INV-005` | Unit | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts` | none |
517
+ | `MOD-007`, `MOD-008` / `INV-004` | Unit | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none |
518
+ | `MOD-009` | Unit | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none |
519
+ | `MOD-010` / `INV-003` | Unit | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none |
520
+ | `MOD-011` | Unit | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none |
521
+ | `MOD-012` | Unit | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none found | none |
522
+ | `MOD-013` | Unit | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none |
523
+ | `MOD-014` | Unit | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/prelogin-metrics-batcher.ts` | none |
524
+ | `MOD-015` | Unit | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none found | none |
525
+ | `MOD-016` | Unit | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts` | none |
526
+ | `MOD-017` | Unit | `test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts` | none |
527
+ | `MOD-018` / `INV-006` | Unit | `test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts` | none |
528
+ | `MOD-019` / `INV-007` | Unit | `test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts` | none |
529
+ | `MOD-020` | Unit | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts` | none |
530
+ | `MOD-021` | Unit | `test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts` | none found | No case asserts a non-expected code returns false |
531
+ | Batcher success and failure | Unit | `test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts` | `test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts` | none |
532
+
533
+ Record coverage gaps explicitly and link follow-up work. A module specification
534
+ is complete only when its public surface, invariants, failure modes, and test
535
+ evidence agree with the implementation.
package/src/config.js CHANGED
@@ -15,6 +15,13 @@ export default {
15
15
  },
16
16
  metrics: {
17
17
  appType: inBrowser ? 'browser' : 'nodejs',
18
+ unhandledExceptionTelemetry: {
19
+ enabled: false,
20
+ },
21
+ networkTelemetry: {
22
+ enabled: false,
23
+ intervalMs: 10 * 60 * 1_000,
24
+ },
18
25
  batcherWait: 500,
19
26
  batcherMaxCalls: 50,
20
27
  batcherMaxWait: 1500,