@webex/internal-plugin-metrics 3.12.0 → 3.13.0-next.1
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.
- package/.repo-context.json +466 -0
- package/.sdd/manifest.json +626 -0
- package/AGENTS.md +208 -0
- package/README.md +274 -0
- package/dist/automated-user.js +19 -0
- package/dist/automated-user.js.map +1 -0
- package/dist/batcher.js +3 -0
- package/dist/batcher.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +639 -66
- package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics.js +240 -103
- package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics.util.js +7 -2
- package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -1
- package/dist/call-diagnostic/config.js +19 -4
- package/dist/call-diagnostic/config.js.map +1 -1
- package/dist/config.js +8 -0
- package/dist/config.js.map +1 -1
- package/dist/index.js +25 -1
- package/dist/index.js.map +1 -1
- package/dist/metrics.js +118 -5
- package/dist/metrics.js.map +1 -1
- package/dist/metrics.types.js +4 -0
- package/dist/metrics.types.js.map +1 -1
- package/dist/network-telemetry.js +610 -0
- package/dist/network-telemetry.js.map +1 -0
- package/dist/new-metrics.js +63 -17
- package/dist/new-metrics.js.map +1 -1
- package/dist/privacy-and-security-permission-enricher/constants.js +18 -0
- package/dist/privacy-and-security-permission-enricher/constants.js.map +1 -0
- package/dist/privacy-and-security-permission-enricher/index.js +143 -0
- package/dist/privacy-and-security-permission-enricher/index.js.map +1 -0
- package/dist/privacy-and-security-permission-enricher/types.js +7 -0
- package/dist/privacy-and-security-permission-enricher/types.js.map +1 -0
- package/dist/privacy-and-security-permission-enricher/utils.js +71 -0
- package/dist/privacy-and-security-permission-enricher/utils.js.map +1 -0
- package/dist/types/automated-user.d.ts +2 -0
- package/dist/types/call-diagnostic/call-diagnostic-metrics-latencies.d.ts +208 -5
- package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +83 -17
- package/dist/types/call-diagnostic/config.d.ts +6 -0
- package/dist/types/config.d.ts +9 -0
- package/dist/types/index.d.ts +5 -3
- package/dist/types/metrics.types.d.ts +11 -5
- package/dist/types/network-telemetry.d.ts +108 -0
- package/dist/types/new-metrics.d.ts +17 -1
- package/dist/types/privacy-and-security-permission-enricher/constants.d.ts +6 -0
- package/dist/types/privacy-and-security-permission-enricher/index.d.ts +40 -0
- package/dist/types/privacy-and-security-permission-enricher/types.d.ts +14 -0
- package/dist/types/privacy-and-security-permission-enricher/utils.d.ts +5 -0
- package/dist/types/unhandled-exception-telemetry/index.d.ts +60 -0
- package/dist/types/unhandled-exception-telemetry/utils.d.ts +6 -0
- package/dist/unhandled-exception-telemetry/index.js +331 -0
- package/dist/unhandled-exception-telemetry/index.js.map +1 -0
- package/dist/unhandled-exception-telemetry/utils.js +105 -0
- package/dist/unhandled-exception-telemetry/utils.js.map +1 -0
- package/docs/adr/index.md +27 -0
- package/docs/architecture.md +437 -0
- package/docs/getting-started.md +131 -0
- package/docs/index.md +52 -0
- package/docs/specs/README.md +88 -0
- package/package.json +12 -11
- package/schemas/repo-context.schema.json +796 -0
- package/src/automated-user.ts +16 -0
- package/src/batcher.js +4 -0
- package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +801 -74
- package/src/call-diagnostic/call-diagnostic-metrics.ts +184 -42
- package/src/call-diagnostic/call-diagnostic-metrics.util.ts +7 -1
- package/src/call-diagnostic/config.ts +16 -0
- package/src/call-diagnostic/docs/README.md +535 -0
- package/src/config.js +8 -0
- package/src/docs/README.md +703 -0
- package/src/index.ts +16 -0
- package/src/metrics.js +123 -5
- package/src/metrics.types.ts +29 -6
- package/src/network-telemetry.ts +757 -0
- package/src/new-metrics.ts +56 -6
- package/src/privacy-and-security-permission-enricher/constants.ts +33 -0
- package/src/privacy-and-security-permission-enricher/docs/README.md +356 -0
- package/src/privacy-and-security-permission-enricher/index.ts +142 -0
- package/src/privacy-and-security-permission-enricher/types.ts +21 -0
- package/src/privacy-and-security-permission-enricher/utils.ts +82 -0
- package/src/rtcMetrics/docs/README.md +447 -0
- package/src/unhandled-exception-telemetry/docs/README.md +536 -0
- package/src/unhandled-exception-telemetry/index.ts +404 -0
- package/src/unhandled-exception-telemetry/utils.ts +101 -0
- package/test/unit/spec/automated-user.ts +43 -0
- package/test/unit/spec/batcher.js +43 -0
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts +14 -2
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +1435 -304
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +1265 -207
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +27 -0
- package/test/unit/spec/metrics.js +14 -5
- package/test/unit/spec/network-telemetry.ts +610 -0
- package/test/unit/spec/new-metrics.ts +203 -50
- package/test/unit/spec/prelogin-metrics-batcher.ts +71 -36
- package/test/unit/spec/privacy-and-security-permission-enricher.ts +318 -0
- package/test/unit/spec/unhandled-exception-telemetry/utils.ts +109 -0
- package/test/unit/spec/unhandled-exception-telemetry.ts +689 -0
package/src/new-metrics.ts
CHANGED
|
@@ -19,17 +19,20 @@ import {
|
|
|
19
19
|
ClientEvent,
|
|
20
20
|
FeatureEvent,
|
|
21
21
|
EventPayload,
|
|
22
|
-
OperationalEvent,
|
|
23
22
|
MediaQualityEvent,
|
|
24
23
|
InternalEvent,
|
|
25
24
|
SubmitClientEventOptions,
|
|
26
25
|
Table,
|
|
27
26
|
DelayedClientEvent,
|
|
28
27
|
DelayedClientFeatureEvent,
|
|
28
|
+
PrivacyAndSecurityPermission,
|
|
29
29
|
} from './metrics.types';
|
|
30
30
|
import CallDiagnosticLatencies from './call-diagnostic/call-diagnostic-metrics-latencies';
|
|
31
31
|
import {setMetricTimings} from './call-diagnostic/call-diagnostic-metrics.util';
|
|
32
32
|
import {generateCommonErrorMetadata} from './utils';
|
|
33
|
+
import {isAutomatedUser as detectAutomatedUser} from './automated-user';
|
|
34
|
+
import PrivacyAndSecurityPermissionEnricher from './privacy-and-security-permission-enricher';
|
|
35
|
+
import {startUnhandledExceptionTelemetry} from './unhandled-exception-telemetry';
|
|
33
36
|
|
|
34
37
|
/**
|
|
35
38
|
* Metrics plugin to centralize all types of metrics.
|
|
@@ -67,6 +70,8 @@ class Metrics extends WebexPlugin {
|
|
|
67
70
|
|
|
68
71
|
delayedClientFeatureEventsOverrides: Partial<DelayedClientFeatureEvent['options']> = {};
|
|
69
72
|
|
|
73
|
+
private privacyAndSecurityPermissionEnricher: PrivacyAndSecurityPermissionEnricher;
|
|
74
|
+
|
|
70
75
|
/**
|
|
71
76
|
* Constructor
|
|
72
77
|
* @param args
|
|
@@ -77,8 +82,19 @@ class Metrics extends WebexPlugin {
|
|
|
77
82
|
constructor(...args) {
|
|
78
83
|
super(...args);
|
|
79
84
|
|
|
85
|
+
this.privacyAndSecurityPermissionEnricher = new PrivacyAndSecurityPermissionEnricher(
|
|
86
|
+
(error) => {
|
|
87
|
+
// @ts-ignore
|
|
88
|
+
this.webex.logger.error(
|
|
89
|
+
'NewMetrics: @submitClientEvent. Privacy and security permission enrichment failed.',
|
|
90
|
+
error
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
);
|
|
80
94
|
// @ts-ignore
|
|
81
95
|
this.callDiagnosticLatencies = new CallDiagnosticLatencies({}, {parent: this.webex});
|
|
96
|
+
// @ts-ignore
|
|
97
|
+
this.callDiagnosticMetrics = new CallDiagnosticMetrics({}, {parent: this.webex});
|
|
82
98
|
this.onReady();
|
|
83
99
|
}
|
|
84
100
|
|
|
@@ -88,8 +104,6 @@ class Metrics extends WebexPlugin {
|
|
|
88
104
|
private onReady() {
|
|
89
105
|
// @ts-ignore
|
|
90
106
|
this.webex.once('ready', () => {
|
|
91
|
-
// @ts-ignore
|
|
92
|
-
this.callDiagnosticMetrics = new CallDiagnosticMetrics({}, {parent: this.webex});
|
|
93
107
|
this.preLoginMetrics = new PreLoginMetrics(
|
|
94
108
|
// @ts-ignore
|
|
95
109
|
new PreLoginMetricsBatcher({}, {parent: this.webex}),
|
|
@@ -102,6 +116,8 @@ class Metrics extends WebexPlugin {
|
|
|
102
116
|
shouldDelay: this.delaySubmitClientEvents,
|
|
103
117
|
overrides: this.delayedClientEventsOverrides,
|
|
104
118
|
});
|
|
119
|
+
// @ts-ignore
|
|
120
|
+
startUnhandledExceptionTelemetry(this.webex);
|
|
105
121
|
});
|
|
106
122
|
}
|
|
107
123
|
|
|
@@ -182,6 +198,13 @@ class Metrics extends WebexPlugin {
|
|
|
182
198
|
return this.businessMetrics?.isReadyToSubmitEvents() ?? false;
|
|
183
199
|
}
|
|
184
200
|
|
|
201
|
+
/**
|
|
202
|
+
* @returns whether the current user agent belongs to an automated user
|
|
203
|
+
*/
|
|
204
|
+
isAutomatedUser() {
|
|
205
|
+
return detectAutomatedUser();
|
|
206
|
+
}
|
|
207
|
+
|
|
185
208
|
/**
|
|
186
209
|
* Behavioral event
|
|
187
210
|
* @param args
|
|
@@ -325,7 +348,7 @@ class Metrics extends WebexPlugin {
|
|
|
325
348
|
payload?: RecursivePartial<FeatureEvent['payload']>;
|
|
326
349
|
options: any;
|
|
327
350
|
}) {
|
|
328
|
-
if (!this.
|
|
351
|
+
if (!this.isReady) {
|
|
329
352
|
// @ts-ignore
|
|
330
353
|
this.webex.logger.log(
|
|
331
354
|
`NewMetrics: @submitFeatureEvent. Attempted to submit before webex.ready. Event name: ${name}`
|
|
@@ -360,7 +383,7 @@ class Metrics extends WebexPlugin {
|
|
|
360
383
|
payload?: RecursivePartial<ClientEvent['payload']>;
|
|
361
384
|
options?: SubmitClientEventOptions;
|
|
362
385
|
}): Promise<any> {
|
|
363
|
-
if (!this.
|
|
386
|
+
if (!this.isReady) {
|
|
364
387
|
// @ts-ignore
|
|
365
388
|
this.webex.logger.log(
|
|
366
389
|
`NewMetrics: @submitClientEvent. Attempted to submit before webex.ready. Event name: ${name}`
|
|
@@ -373,14 +396,41 @@ class Metrics extends WebexPlugin {
|
|
|
373
396
|
options: {meetingId: options?.meetingId},
|
|
374
397
|
});
|
|
375
398
|
|
|
376
|
-
|
|
399
|
+
const enrichedPayload = this.privacyAndSecurityPermissionEnricher.enrich({
|
|
377
400
|
name,
|
|
378
401
|
payload,
|
|
402
|
+
scope: this.getPermissionScope(options),
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
return this.callDiagnosticMetrics.submitClientEvent({
|
|
406
|
+
name,
|
|
407
|
+
payload: enrichedPayload,
|
|
379
408
|
options,
|
|
380
409
|
delaySubmitEvent: this.delaySubmitClientEvents,
|
|
381
410
|
});
|
|
382
411
|
}
|
|
383
412
|
|
|
413
|
+
/**
|
|
414
|
+
* Stores the latest browser permission state supplied by the client.
|
|
415
|
+
* @param permission latest normalized permission snapshot
|
|
416
|
+
*/
|
|
417
|
+
public setPrivacyAndSecurityPermission(permission: PrivacyAndSecurityPermission): void {
|
|
418
|
+
this.privacyAndSecurityPermissionEnricher.setPermission(permission);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Resolves event options to the call identity used by Call Diagnostic where possible.
|
|
423
|
+
* @param options client event options
|
|
424
|
+
* @returns the permission history scope
|
|
425
|
+
*/
|
|
426
|
+
private getPermissionScope(options?: SubmitClientEventOptions): string {
|
|
427
|
+
const meeting = options?.meetingId
|
|
428
|
+
? (this as any).webex.meetings?.getBasicMeetingInformation?.(options.meetingId)
|
|
429
|
+
: undefined;
|
|
430
|
+
|
|
431
|
+
return options?.correlationId ?? meeting?.correlationId ?? 'default';
|
|
432
|
+
}
|
|
433
|
+
|
|
384
434
|
/**
|
|
385
435
|
* Issue request to alias a user's pre-login ID with their CI UUID
|
|
386
436
|
* @param {string} preLoginId
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type {ClientEvent} from '../metrics.types';
|
|
2
|
+
import type {PermissionEnrichmentPolicy} from './types';
|
|
3
|
+
|
|
4
|
+
export const CAMERA_AND_MICROPHONE_PERMISSION_EVENTS = new Set<ClientEvent['name']>([
|
|
5
|
+
'client.call.initiated',
|
|
6
|
+
'client.media.capabilities',
|
|
7
|
+
'client.ice.end',
|
|
8
|
+
'client.locus.join.request',
|
|
9
|
+
'client.locus.join.response',
|
|
10
|
+
'client.media-engine.ready',
|
|
11
|
+
]);
|
|
12
|
+
|
|
13
|
+
export const MEDIA_TX_PERMISSION_EVENTS = new Set<ClientEvent['name']>([
|
|
14
|
+
'client.media.tx.start',
|
|
15
|
+
'client.media.tx.stop',
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
export const CONTENT_SHARE_PERMISSION_EVENTS = new Set<ClientEvent['name']>([
|
|
19
|
+
'client.share.initiated',
|
|
20
|
+
'client.share.floor-grant.request',
|
|
21
|
+
'client.share.floor-granted.local',
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
export const FINAL_PERMISSION_EVENTS = new Set<ClientEvent['name']>([
|
|
25
|
+
'client.call.leave',
|
|
26
|
+
'client.call.remote-ended',
|
|
27
|
+
'client.call.aborted',
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
export const NO_PERMISSION_ENRICHMENT: PermissionEnrichmentPolicy = {
|
|
31
|
+
resources: [],
|
|
32
|
+
terminal: false,
|
|
33
|
+
};
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Module Spec
|
|
3
|
+
title: 'Privacy and security permission enricher specification'
|
|
4
|
+
description: Responsibilities, boundaries, design, invariants, and verification for the privacy and security permission enricher.
|
|
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
|
+
# Privacy and security permission enricher
|
|
17
|
+
|
|
18
|
+
This source-local document at `src/privacy-and-security-permission-enricher/docs/README.md` owns the
|
|
19
|
+
stable specification for **the privacy and security permission enricher**. Ground every claim in
|
|
20
|
+
repository evidence and link to the
|
|
21
|
+
[repository architecture](../../../docs/architecture.md) instead of repeating broader facts.
|
|
22
|
+
|
|
23
|
+
Related context: [documentation index](../../../docs/index.md) ·
|
|
24
|
+
[documentation agent instructions](../../../AGENTS.md)
|
|
25
|
+
|
|
26
|
+
## Metadata
|
|
27
|
+
|
|
28
|
+
| Field | Value |
|
|
29
|
+
| ------------- | ------------------------------------------------------------ |
|
|
30
|
+
| Owner | Cisco Webex JS SDK — telemetry |
|
|
31
|
+
| Source path | `src/privacy-and-security-permission-enricher/` |
|
|
32
|
+
| Resource kind | module |
|
|
33
|
+
| Status | Active |
|
|
34
|
+
| Last verified | 2026-10-05 at `d6202dd73d` |
|
|
35
|
+
| Module id | `privacy-and-security-permission-enricher` |
|
|
36
|
+
| Parent spec | [`src/docs/README.md`](../../docs/README.md) |
|
|
37
|
+
| Doc kind | Module spec |
|
|
38
|
+
| Coverage score | Pending coverage assessment |
|
|
39
|
+
| 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) |
|
|
40
|
+
|
|
41
|
+
## Applicability
|
|
42
|
+
|
|
43
|
+
Sections preceded by an `Include if` comment are conditional. Retain a
|
|
44
|
+
conditional section only when source evidence or a confirmed developer answer
|
|
45
|
+
satisfies its condition.
|
|
46
|
+
|
|
47
|
+
| Condition ID | Status | Evidence or reason | Owned section |
|
|
48
|
+
| ------------------------------------ | ---------- | ---------------------------------------------------------------------------------------------------------------------- | ----------------------------- |
|
|
49
|
+
| `module.has_tiers` | N/A | The repository defines no operational or review tier policy for modules | Tier |
|
|
50
|
+
| `module.has_ui` | N/A | The module renders nothing; it transforms an event payload object | UI use-case flow |
|
|
51
|
+
| `module.crosses_service_boundaries` | N/A | No network, event or process boundary is crossed; every operation is a synchronous in-process call | Cross-boundary use-case flow |
|
|
52
|
+
| `module.holds_client_state` | Applicable | `src/privacy-and-security-permission-enricher/index.ts` holds the latest snapshot and a per-scope history map | Client state model |
|
|
53
|
+
| `module.enforces_domain_rules` | Applicable | `src/privacy-and-security-permission-enricher/constants.ts` and `src/privacy-and-security-permission-enricher/index.ts` | Business rules and invariants |
|
|
54
|
+
| `module.is_concurrent_async` | N/A | No promise, timer, subscription or worker; the enrichment call returns synchronously | Concurrency and reactive flow |
|
|
55
|
+
| `module.owns_persistence` | N/A | No store, schema or migration; all state is in-memory and per-instance | Data, schema, and migration |
|
|
56
|
+
| `module.stateful_transitions` | Applicable | Per-scope history is seeded, updated on change and deleted on a terminal event in `src/privacy-and-security-permission-enricher/index.ts` | State machine |
|
|
57
|
+
| `module.exposes_wire_protocol` | N/A | The module adds one field to a payload owned and serialized elsewhere; it performs no serialization | Protocol and wire format |
|
|
58
|
+
| `module.ui_multi_screen` | N/A | No user interface | UI flow |
|
|
59
|
+
| `module.large_data_model` | Applicable only to larger models; N/A here — the model is three permission resources, declared in `src/privacy-and-security-permission-enricher/types.ts` | Data model |
|
|
60
|
+
| `module.returns_caller_errors` | N/A | Body failures are routed to the injected handler and the original payload is returned; only a throwing handler or policy resolution could escape, and the façade's logging handler does not throw | Caller-visible failure modes |
|
|
61
|
+
| `module.module_specific_conventions` | Applicable | The rule table and the projection helpers impose conventions beyond repository-wide rules | Module-specific rules |
|
|
62
|
+
| `module.published_package` | N/A | Not re-exported from `src/index.ts`; reachable only through `src/new-metrics.ts` | Export stability |
|
|
63
|
+
| `module.embedded_in_host` | N/A | The module is not mounted into a host; its parent module owns the plugin registration contract | Host integration and theming |
|
|
64
|
+
| `module.has_design_tradeoff` | Applicable | Change-only reporting with per-scope history, versus reporting the full snapshot on every event | Key design trade-off |
|
|
65
|
+
| `module.has_submodules` | N/A | Derived from the manifest module tree: this module has no child modules | Sub-modules |
|
|
66
|
+
|
|
67
|
+
## Evidence register
|
|
68
|
+
|
|
69
|
+
| Evidence | What it establishes |
|
|
70
|
+
| ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- |
|
|
71
|
+
| `src/privacy-and-security-permission-enricher/index.ts` | The public surface, the rule table, the enrichment decision order and the per-scope history lifecycle |
|
|
72
|
+
| `src/privacy-and-security-permission-enricher/constants.ts` | The four event groups that are eligible for enrichment and the empty policy used for every other event |
|
|
73
|
+
| `src/privacy-and-security-permission-enricher/types.ts` | The policy, rule and context shapes the module is built around |
|
|
74
|
+
| `src/privacy-and-security-permission-enricher/utils.ts` | Resource resolution from media type, defensive projection and the change diff |
|
|
75
|
+
| `src/new-metrics.ts` | The sole call site: construction with the error handler, the public permission setter and the scope resolution rule |
|
|
76
|
+
| `src/metrics.types.ts` | The externally derived permission and client-event payload types this module operates on |
|
|
77
|
+
| `test/unit/spec/privacy-and-security-permission-enricher.ts` | Behavioral verification of every rule, the change-only reporting rule, scope isolation and the error path |
|
|
78
|
+
|
|
79
|
+
## Purpose and boundary
|
|
80
|
+
|
|
81
|
+
- Responsibility: deciding which browser permission state is attached to which Call Analyzer client
|
|
82
|
+
event, and attaching only what has changed since the last report within the same call.
|
|
83
|
+
- In scope: the eligible-event rule table; resolving which permission resources a given event cares
|
|
84
|
+
about; projecting the stored snapshot down to those resources; diffing against per-scope history;
|
|
85
|
+
returning either the original payload or an enriched copy; clearing history at the end of a call.
|
|
86
|
+
- Out of scope: obtaining permission state from the browser (the client supplies it through the
|
|
87
|
+
façade), building or submitting the event, and the meaning of the permission fields themselves,
|
|
88
|
+
which is owned by the external Call Analyzer schema.
|
|
89
|
+
- Consumers: `src/new-metrics.ts` only. The class is not re-exported from `src/index.ts`.
|
|
90
|
+
|
|
91
|
+
## Structure and key files
|
|
92
|
+
|
|
93
|
+
| Path | Responsibility |
|
|
94
|
+
| ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
95
|
+
| `src/privacy-and-security-permission-enricher/index.ts` | The enricher class, the exported rule table and the policy resolver; authoritative for the enrichment decision order |
|
|
96
|
+
| `src/privacy-and-security-permission-enricher/constants.ts` | The four event-name sets and the no-enrichment policy; authoritative for which events are eligible |
|
|
97
|
+
| `src/privacy-and-security-permission-enricher/types.ts` | The policy, rule and enrichment-context types; authoritative for the rule contract |
|
|
98
|
+
| `src/privacy-and-security-permission-enricher/utils.ts` | Media-type to resource resolution, defensive projection and the change diff; authoritative for what counts as a change |
|
|
99
|
+
|
|
100
|
+
## Public surface
|
|
101
|
+
|
|
102
|
+
| Surface | Consumer | Compatibility commitment | Source |
|
|
103
|
+
| ---------------------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
|
|
104
|
+
| `PrivacyAndSecurityPermissionEnricher` constructor | `src/new-metrics.ts` | Takes exactly one argument, the enrichment-error handler. Module-internal; not covered by any published guarantee | `src/privacy-and-security-permission-enricher/index.ts` |
|
|
105
|
+
| `setPermission` | `src/new-metrics.ts` | Stores a defensive projection of the supplied snapshot. Module-internal | `src/privacy-and-security-permission-enricher/index.ts` |
|
|
106
|
+
| `enrich` | `src/new-metrics.ts` | Never mutates its input, and returns the original payload reference when nothing is attached. Errors inside the enrichment body go to the injected handler and the original payload is returned; an error from policy resolution, which runs before the `try`, or from the handler itself propagates to the caller | `src/privacy-and-security-permission-enricher/index.ts` |
|
|
107
|
+
| `PERMISSION_ENRICHMENT_RULES` | Tests, and future rules | Exported for inspection and verification; the array order is the match order | `src/privacy-and-security-permission-enricher/index.ts` |
|
|
108
|
+
|
|
109
|
+
Exact permission field definitions are not restated here. They are derived from the external Call
|
|
110
|
+
Analyzer schema through the types in `src/metrics.types.ts`.
|
|
111
|
+
|
|
112
|
+
## Dependencies
|
|
113
|
+
|
|
114
|
+
| Dependency | Why it is required | Failure behavior |
|
|
115
|
+
| ------------------------------ | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
|
|
116
|
+
| `src/metrics.types.ts` | Supplies the permission, resource and client-event payload types | A type mismatch is a build-time failure, not a runtime one |
|
|
117
|
+
| `lodash` deep equality | Decides whether a projected resource differs from what was last reported | Any thrown error is caught by the enrichment handler and the original payload is returned |
|
|
118
|
+
| Enrichment-error handler | Supplied by the caller so the module never has to know how to log | The handler is invoked and the original payload is returned; an exception from the handler itself propagates to the caller |
|
|
119
|
+
|
|
120
|
+
## Requirements
|
|
121
|
+
|
|
122
|
+
| ID | WHAT | WHY | Source evidence | Test or example evidence | Assumptions or gaps | Confidence |
|
|
123
|
+
| --------- | -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | ------------------------------------------------------------- | ------------------- | ---------- |
|
|
124
|
+
| `MOD-001` | Only events listed in one of the four event sets are enriched; every other event name returns its payload unchanged | Permission state is only meaningful around capture, transmission, share and call-end milestones. Attaching it everywhere would inflate every event | `src/privacy-and-security-permission-enricher/constants.ts` | `test/unit/spec/privacy-and-security-permission-enricher.ts` | none | Present |
|
|
125
|
+
| `MOD-002` | Each production event name appears in exactly one rule | Rules are matched by first hit, so an event in two sets would silently take the earlier policy and the later one would be unreachable | `src/privacy-and-security-permission-enricher/index.ts` | `test/unit/spec/privacy-and-security-permission-enricher.ts` | none | Present |
|
|
126
|
+
| `MOD-003` | Media transmission and content-share events are scoped to the resource implied by the payload's media type, not to all resources | A microphone-only event must not carry camera state, which would misattribute a permission change to an unrelated action | `src/privacy-and-security-permission-enricher/utils.ts` | `test/unit/spec/privacy-and-security-permission-enricher.ts` | none | Present |
|
|
127
|
+
| `MOD-004` | A non-terminal event carries only the resources whose state changed since the last report for the same scope | Repeating unchanged state on every event would make it impossible to tell when a permission actually changed | `src/privacy-and-security-permission-enricher/index.ts` | `test/unit/spec/privacy-and-security-permission-enricher.ts` | none | Present |
|
|
128
|
+
| `MOD-005` | A terminal call event carries the full projection of all three resources and does not consult or update history | The end-of-call event is the single record of final permission state, so it must be complete regardless of what was reported earlier | `src/privacy-and-security-permission-enricher/index.ts` | `test/unit/spec/privacy-and-security-permission-enricher.ts` | none | Present |
|
|
129
|
+
| `MOD-006` | History is tracked per scope, and a terminal event deletes only its own scope | Concurrent calls must not leak permission history into one another, and one call ending must not reset another call's baseline | `src/privacy-and-security-permission-enricher/index.ts` | `test/unit/spec/privacy-and-security-permission-enricher.ts` | none | Present |
|
|
130
|
+
| `MOD-007` | A payload that already carries explicit permission state is returned unchanged; on a non-terminal event that explicit state is merged into the scope's baseline, while on a terminal event it seeds nothing and the scope's history is deleted | An explicitly supplied value is authoritative for that event; adopting it as the baseline prevents the next event from re-reporting the same state, but a terminal event ends the scope, so nothing is kept for it | `src/privacy-and-security-permission-enricher/index.ts` | `test/unit/spec/privacy-and-security-permission-enricher.ts` | none | Present |
|
|
131
|
+
| `MOD-008` | Enrichment never mutates the input payload or the stored snapshot; it returns a new object when it attaches anything | The caller reuses the payload it passed in, and the stored snapshot must stay the client-supplied truth | `src/privacy-and-security-permission-enricher/utils.ts` | `test/unit/spec/privacy-and-security-permission-enricher.ts` | none | Present |
|
|
132
|
+
| `MOD-009` | No permission is attached before the client has supplied a snapshot, and an unavailable resource is omitted rather than reported empty | Absent state and denied state are different facts; emitting an empty object would be read as a real permission reading | `src/privacy-and-security-permission-enricher/utils.ts` | `test/unit/spec/privacy-and-security-permission-enricher.ts` | none | Present |
|
|
133
|
+
| `MOD-010` | A failure inside the enrichment body is handed to the injected handler and the original payload is returned. Two paths are outside that guarantee: policy resolution runs before the try block, and an exception thrown by the injected handler itself propagates to the caller | Telemetry enrichment must not fail a client event submission; from `newMetrics` an escape needs a throwing logger in the façade's handler, or a payload whose fields read by the current resolvers have throwing getters | `src/privacy-and-security-permission-enricher/index.ts`, `src/new-metrics.ts` | `test/unit/spec/privacy-and-security-permission-enricher.ts` | No case covers a throwing handler or a throwing policy resolution | Present |
|
|
134
|
+
|
|
135
|
+
## Design overview
|
|
136
|
+
|
|
137
|
+
The module is one class plus a declarative rule table, and its whole design is the separation of
|
|
138
|
+
those two things.
|
|
139
|
+
|
|
140
|
+
The rule table in `src/privacy-and-security-permission-enricher/index.ts` pairs each event-name set
|
|
141
|
+
from `src/privacy-and-security-permission-enricher/constants.ts` with a resolver that returns a
|
|
142
|
+
policy: which permission resources this event cares about, and whether the event is terminal. Adding
|
|
143
|
+
or retargeting an event is therefore a change to the sets or the table, not to the enrichment
|
|
144
|
+
algorithm. Rules are matched by first hit, which is why the one-rule-per-event invariant matters and
|
|
145
|
+
why a test asserts it directly.
|
|
146
|
+
|
|
147
|
+
The class owns two pieces of state. `permission` is the latest snapshot the client supplied,
|
|
148
|
+
defensively projected to the three known resources on the way in so an unexpected field cannot reach
|
|
149
|
+
a payload. `lastReported` is a map from scope to the permission state most recently reported for that
|
|
150
|
+
scope; the scope comes from the caller and is the call's correlation identity, which is what makes
|
|
151
|
+
two concurrent calls independent.
|
|
152
|
+
|
|
153
|
+
`enrich` resolves the policy first, outside the try block, because the terminal flag drives the
|
|
154
|
+
`finally` cleanup and must be known even if the body throws. It then applies five decisions in order:
|
|
155
|
+
an explicitly supplied payload wins and, on a non-terminal event, seeds the baseline; an empty resource list means nothing to
|
|
156
|
+
do; a missing snapshot means nothing to report; a terminal event reports the full projection without
|
|
157
|
+
touching history; and otherwise the projection is diffed against history and only the changed
|
|
158
|
+
resources are attached. Because `getChangedPermission` compares whole resource objects by deep
|
|
159
|
+
equality, a change in any field of a resource — not only its status — counts as a change.
|
|
160
|
+
|
|
161
|
+
## Data flow and sequence coverage
|
|
162
|
+
|
|
163
|
+
The call style is a synchronous in-process method call. There is one operation group — enrich one
|
|
164
|
+
client event — because every public operation shares the same actor, ordering and absence of
|
|
165
|
+
transport or failure propagation. The error branch is shown as an `opt` on the same sequence rather
|
|
166
|
+
than as a separate diagram, since the actor order and the returned value are identical.
|
|
167
|
+
|
|
168
|
+
| Operation group | Entry and outcome | Diagram or evidence | Failure and recovery coverage |
|
|
169
|
+
| -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
|
|
170
|
+
| Store the latest snapshot | The client calls the façade setter; the projected snapshot replaces the previous one | `src/privacy-and-security-permission-enricher/index.ts` | No failure path; projection drops unknown resources rather than rejecting the snapshot |
|
|
171
|
+
| Enrich one client event | The façade passes name, payload and scope; the original payload or an enriched copy is returned, and history is updated or cleared | Sequence diagram below | Internal error routed to the handler, original payload returned; terminal cleanup still runs; a throwing handler propagates |
|
|
172
|
+
|
|
173
|
+
```mermaid
|
|
174
|
+
sequenceDiagram
|
|
175
|
+
participant Facade as newMetrics facade
|
|
176
|
+
participant Enricher as Permission enricher
|
|
177
|
+
participant History as Per-scope history
|
|
178
|
+
Facade->>Enricher: enrich(name, payload, scope)
|
|
179
|
+
Enricher->>Enricher: resolve policy from the rule table
|
|
180
|
+
alt payload already carries permission state
|
|
181
|
+
Enricher->>History: seed baseline unless terminal
|
|
182
|
+
Enricher-->>Facade: original payload
|
|
183
|
+
else no eligible resources or no snapshot stored
|
|
184
|
+
Enricher-->>Facade: original payload
|
|
185
|
+
else terminal call event
|
|
186
|
+
Enricher-->>Facade: payload plus full projection
|
|
187
|
+
else non-terminal eligible event
|
|
188
|
+
Enricher->>History: read last reported state for scope
|
|
189
|
+
Enricher->>Enricher: diff projection against history
|
|
190
|
+
opt nothing changed
|
|
191
|
+
Enricher-->>Facade: original payload
|
|
192
|
+
end
|
|
193
|
+
Enricher->>History: merge changed resources
|
|
194
|
+
Enricher-->>Facade: payload plus changed resources
|
|
195
|
+
end
|
|
196
|
+
opt internal error at any step
|
|
197
|
+
Enricher->>Facade: onEnrichmentError(error)
|
|
198
|
+
Enricher-->>Facade: original payload
|
|
199
|
+
end
|
|
200
|
+
opt policy is terminal
|
|
201
|
+
Enricher->>History: delete this scope
|
|
202
|
+
end
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Class and component relationships
|
|
206
|
+
|
|
207
|
+
```mermaid
|
|
208
|
+
classDiagram
|
|
209
|
+
class NewMetrics {
|
|
210
|
+
+setPrivacyAndSecurityPermission()
|
|
211
|
+
+submitClientEvent()
|
|
212
|
+
-getPermissionScope()
|
|
213
|
+
}
|
|
214
|
+
class PrivacyAndSecurityPermissionEnricher {
|
|
215
|
+
-permission
|
|
216
|
+
-lastReported
|
|
217
|
+
-onEnrichmentError
|
|
218
|
+
+setPermission()
|
|
219
|
+
+enrich()
|
|
220
|
+
}
|
|
221
|
+
class PermissionEnrichmentRules {
|
|
222
|
+
+events
|
|
223
|
+
+resolve()
|
|
224
|
+
}
|
|
225
|
+
class PermissionUtils {
|
|
226
|
+
+resolveMediaResources()
|
|
227
|
+
+resolveContentShareResources()
|
|
228
|
+
+projectPrivacyAndSecurityPermission()
|
|
229
|
+
+getChangedPermission()
|
|
230
|
+
}
|
|
231
|
+
NewMetrics --> PrivacyAndSecurityPermissionEnricher : owns one instance
|
|
232
|
+
PrivacyAndSecurityPermissionEnricher --> PermissionEnrichmentRules : matches first hit
|
|
233
|
+
PrivacyAndSecurityPermissionEnricher --> PermissionUtils : projects and diffs
|
|
234
|
+
PermissionEnrichmentRules --> PermissionUtils : resolves resources from media type
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Use cases and flows
|
|
238
|
+
|
|
239
|
+
| Use case | Actor or caller | Primary steps and outcome | Failure or boundary behavior | Evidence |
|
|
240
|
+
| -------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
241
|
+
| `UC-001` | First party client | Client supplies a permission snapshot, then a call-initiated event is submitted; camera and microphone state is attached once | A second call-initiated event with unchanged state attaches nothing | `src/privacy-and-security-permission-enricher/index.ts`, `test/unit/spec/privacy-and-security-permission-enricher.ts` |
|
|
242
|
+
| `UC-002` | Media layer, through the façade | An audio transmission event is submitted; only microphone state is attached, and only if it changed | A share media type on a camera-and-microphone event resolves to no resources and attaches nothing | `src/privacy-and-security-permission-enricher/utils.ts`, `test/unit/spec/privacy-and-security-permission-enricher.ts` |
|
|
243
|
+
| `UC-003` | Meetings layer | A call-leave event is submitted; the full three-resource projection is attached and the scope's history is deleted | History for other scopes is untouched, so a concurrent call keeps its baseline | `src/privacy-and-security-permission-enricher/index.ts`, `test/unit/spec/privacy-and-security-permission-enricher.ts` |
|
|
244
|
+
| `UC-004` | Caller with its own state | A payload already containing permission state is submitted; it is returned unchanged and recorded as the new baseline | A terminal event with explicit state clears the scope instead of seeding it | `src/privacy-and-security-permission-enricher/index.ts`, `test/unit/spec/privacy-and-security-permission-enricher.ts` |
|
|
245
|
+
| `UC-005` | Any caller, degraded case | An internal comparison throws; the handler receives the error and the submission proceeds with the original payload | The event is still submitted; only the enrichment is lost | `src/privacy-and-security-permission-enricher/index.ts`, `test/unit/spec/privacy-and-security-permission-enricher.ts` |
|
|
246
|
+
|
|
247
|
+
<!-- Include if: the module holds client-side state. [condition-id: module.holds_client_state] -->
|
|
248
|
+
|
|
249
|
+
## Client state model
|
|
250
|
+
|
|
251
|
+
| State or slice | Owner | Initial state | Transition triggers | Reset or persistence boundary |
|
|
252
|
+
| --------------------------- | -------------------- | ------------------ | ----------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
|
|
253
|
+
| Latest permission snapshot | The enricher instance | Undefined | The façade's public permission setter; stored as a projection of the three resources | Replaced wholesale on every set; never cleared, and never persisted |
|
|
254
|
+
| Per-scope reported history | The enricher instance | Empty map | Merged on every non-terminal enrichment that attaches something, and on an explicit payload | The scope's entry is deleted when a terminal call event is enriched for that scope |
|
|
255
|
+
|
|
256
|
+
<!-- Include if: the module enforces domain rules or entity invariants. [condition-id: module.enforces_domain_rules] -->
|
|
257
|
+
|
|
258
|
+
## Business rules and invariants
|
|
259
|
+
|
|
260
|
+
| ID | Invariant | WHY | Enforcement source | Test evidence |
|
|
261
|
+
| --------- | ----------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------- |
|
|
262
|
+
| `INV-001` | An event name belongs to at most one rule set | First-hit matching makes a duplicate silently unreachable | `src/privacy-and-security-permission-enricher/constants.ts` | `test/unit/spec/privacy-and-security-permission-enricher.ts` |
|
|
263
|
+
| `INV-002` | A projection contains only the requested resources, and is undefined rather than empty when none are present | An empty object in the payload would be read as a real, complete permission reading | `src/privacy-and-security-permission-enricher/utils.ts` | `test/unit/spec/privacy-and-security-permission-enricher.ts` |
|
|
264
|
+
| `INV-003` | Neither the input payload nor the stored snapshot is mutated | The caller reuses the payload, and the snapshot must remain the client-supplied truth for later diffs | `src/privacy-and-security-permission-enricher/utils.ts` | `test/unit/spec/privacy-and-security-permission-enricher.ts` |
|
|
265
|
+
| `INV-004` | A terminal event neither reads nor writes history, and always deletes its scope | The final record must be complete and must not depend on, or leave behind, intermediate state | `src/privacy-and-security-permission-enricher/index.ts` | `test/unit/spec/privacy-and-security-permission-enricher.ts` |
|
|
266
|
+
| `INV-005` | The enrichment body never throws to the caller; failures go to the injected handler, which is expected not to throw | A telemetry enrichment failure must not fail the client event it was decorating | `src/privacy-and-security-permission-enricher/index.ts` | `test/unit/spec/privacy-and-security-permission-enricher.ts` |
|
|
267
|
+
|
|
268
|
+
<!-- Include if: the module has non-trivial state transitions. [condition-id: module.stateful_transitions] -->
|
|
269
|
+
|
|
270
|
+
## State machine
|
|
271
|
+
|
|
272
|
+
The state is the per-scope history entry. There is one entry per scope, created on first report and
|
|
273
|
+
destroyed by the terminal event for that scope.
|
|
274
|
+
|
|
275
|
+
```mermaid
|
|
276
|
+
stateDiagram-v2
|
|
277
|
+
[*] --> NoHistory
|
|
278
|
+
NoHistory --> Reported: eligible event with available permission state
|
|
279
|
+
NoHistory --> NoHistory: ineligible event, no snapshot, or no eligible resources
|
|
280
|
+
NoHistory --> Reported: payload carries explicit non-terminal permission state
|
|
281
|
+
Reported --> Reported: a projected resource differs from the last reported value
|
|
282
|
+
Reported --> Reported: nothing changed, so nothing is attached
|
|
283
|
+
Reported --> [*]: terminal call event deletes this scope
|
|
284
|
+
NoHistory --> [*]: terminal call event deletes this scope
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Guards and rejected transitions:
|
|
288
|
+
|
|
289
|
+
- A terminal event never enters the `Reported` write path; it reports the full projection and leaves.
|
|
290
|
+
- An event whose resolved resource list is empty cannot create or advance history, which is what
|
|
291
|
+
keeps a share media type on a camera-and-microphone event from recording a baseline.
|
|
292
|
+
- Deleting a scope affects only that key, so another scope in `Reported` stays there.
|
|
293
|
+
|
|
294
|
+
## Pitfalls and constraints
|
|
295
|
+
|
|
296
|
+
- Rule order is significant. The rule table is matched by first hit, so moving an event name into an
|
|
297
|
+
earlier set silently changes its policy. The one-rule-per-event test is the guard; keep it passing
|
|
298
|
+
rather than relying on review.
|
|
299
|
+
- The policy must be resolved before the try block. The terminal flag drives the `finally` cleanup,
|
|
300
|
+
so resolving it inside the body would leave a scope uncleaned when the body throws. The cost is that
|
|
301
|
+
a throw during policy resolution is not caught, and neither is a throw from the injected error
|
|
302
|
+
handler: the containment guarantee holds only while that handler does not throw.
|
|
303
|
+
- An explicitly supplied permission payload is authoritative but not free: on a non-terminal event it
|
|
304
|
+
is merged into the baseline, so a caller that supplies state once and then stops will see the next
|
|
305
|
+
automatic report diffed against what it supplied.
|
|
306
|
+
- Change detection is whole-resource, not field-level. Any difference inside a resource object makes
|
|
307
|
+
the whole resource count as changed and be re-reported.
|
|
308
|
+
- The three permission resource names are hard-coded in the setter's projection and in the terminal
|
|
309
|
+
rule. Adding a fourth resource means updating both, not only the type.
|
|
310
|
+
|
|
311
|
+
<!-- Include if: the module has conventions beyond repository-wide rules. [condition-id: module.module_specific_conventions] -->
|
|
312
|
+
|
|
313
|
+
## Module-specific rules
|
|
314
|
+
|
|
315
|
+
- Do: add or retarget an event by editing the event sets in
|
|
316
|
+
`src/privacy-and-security-permission-enricher/constants.ts` or the rule table in
|
|
317
|
+
`src/privacy-and-security-permission-enricher/index.ts`, and extend the one-rule-per-event test.
|
|
318
|
+
- Do: keep every resolver pure. A resolver receives the payload and returns a policy; it must not
|
|
319
|
+
read or write enricher state.
|
|
320
|
+
- Do not: add a branch to `enrich` for a specific event name. Event-specific behavior belongs in the
|
|
321
|
+
rule table, which is what keeps the algorithm testable.
|
|
322
|
+
- Do not: let an error escape `enrich`. The handler exists so the module never needs to know how the
|
|
323
|
+
host logs, and so a client event is not lost to an enrichment bug. Policy resolution and the handler
|
|
324
|
+
call sit outside the guard today, so a new resolver must not throw and a handler must not throw.
|
|
325
|
+
- Do not: return the stored snapshot object, or any part of it, without projecting first. The
|
|
326
|
+
projection is what prevents a caller from mutating the enricher's state through a payload it holds.
|
|
327
|
+
|
|
328
|
+
<!-- Include if: the module has a non-obvious design trade-off consumers or maintainers must preserve. [condition-id: module.has_design_tradeoff] -->
|
|
329
|
+
|
|
330
|
+
## Key design trade-off
|
|
331
|
+
|
|
332
|
+
| Chosen trade-off | Preserved invariant or benefit | Cost or limitation | Decision evidence |
|
|
333
|
+
| ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------- |
|
|
334
|
+
| Report only what changed, keeping per-scope history, instead of attaching the full snapshot to every event | A permission change is visible as an event in the telemetry stream, and payload size stays proportional to real changes rather than to event volume | The enricher must hold mutable per-call state, and a consumer reading a single event in isolation cannot see unchanged resources | `src/privacy-and-security-permission-enricher/index.ts` |
|
|
335
|
+
| Exempt terminal call events from change-only reporting | One event per call always carries the complete final permission state, so a call can be interpreted without replaying every earlier event | That event is larger, and it duplicates state already reported earlier in the same call | `src/privacy-and-security-permission-enricher/index.ts` |
|
|
336
|
+
| Route enrichment-body errors into an injected handler rather than propagating | A client event is not lost because the enrichment body failed, provided the resolver and the handler do not throw | An enrichment bug is silent in the data: the event still arrives, just without permission state, so the handler's log line is the only signal | `src/new-metrics.ts` |
|
|
337
|
+
|
|
338
|
+
## Verification
|
|
339
|
+
|
|
340
|
+
| Requirement or invariant | Test level | Positive evidence | Negative or boundary evidence | Gap |
|
|
341
|
+
| ------------------------ | ---------- | ------------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
|
|
342
|
+
| `MOD-001` | Unit | `test/unit/spec/privacy-and-security-permission-enricher.ts` | `test/unit/spec/privacy-and-security-permission-enricher.ts` | none |
|
|
343
|
+
| `MOD-002` / `INV-001` | Unit | `test/unit/spec/privacy-and-security-permission-enricher.ts` | none found — the invariant is asserted directly over the rule table rather than by a failing case | none |
|
|
344
|
+
| `MOD-003` | Unit | `test/unit/spec/privacy-and-security-permission-enricher.ts` | `test/unit/spec/privacy-and-security-permission-enricher.ts` | none |
|
|
345
|
+
| `MOD-004` | Unit | `test/unit/spec/privacy-and-security-permission-enricher.ts` | `test/unit/spec/privacy-and-security-permission-enricher.ts` | none |
|
|
346
|
+
| `MOD-005` | Unit | `test/unit/spec/privacy-and-security-permission-enricher.ts` | `test/unit/spec/privacy-and-security-permission-enricher.ts` | none |
|
|
347
|
+
| `MOD-006` / `INV-004` | Unit | `test/unit/spec/privacy-and-security-permission-enricher.ts` | `test/unit/spec/privacy-and-security-permission-enricher.ts` | none |
|
|
348
|
+
| `MOD-007` | Unit | `test/unit/spec/privacy-and-security-permission-enricher.ts` | `test/unit/spec/privacy-and-security-permission-enricher.ts` | none |
|
|
349
|
+
| `MOD-008` / `INV-003` | Unit | `test/unit/spec/privacy-and-security-permission-enricher.ts` | none found — mutation is asserted absent rather than provoked | none |
|
|
350
|
+
| `MOD-009` / `INV-002` | Unit | `test/unit/spec/privacy-and-security-permission-enricher.ts` | `test/unit/spec/privacy-and-security-permission-enricher.ts` | none |
|
|
351
|
+
| `MOD-010` / `INV-005` | Unit | `test/unit/spec/privacy-and-security-permission-enricher.ts` | `test/unit/spec/privacy-and-security-permission-enricher.ts` | none |
|
|
352
|
+
| Scope resolution from event options | Unit | `test/unit/spec/new-metrics.ts` | none found | The fallback chain from correlation identifier to meeting correlation identifier to the default scope is exercised through the façade, not by a dedicated case |
|
|
353
|
+
|
|
354
|
+
Record coverage gaps explicitly and link follow-up work. A module specification
|
|
355
|
+
is complete only when its public surface, invariants, failure modes, and test
|
|
356
|
+
evidence agree with the implementation.
|