docs(adr): align ADR-024 IAnalytics with shipped interface
The implemented IAnalytics refined the signature ADR-024 sketched: events are attributed to the user set by identify() (the standard SDK model) rather than passed per track() call, the attribute parameter is named `attributes` consistently, and AnalyticsUser stays id-only with traits riding identify()'s second argument. The code is the more idiomatic contract and docs/guides/analytics.md already matched it — update the ADR's interface block, manifest example, and PII-boundary section to describe what shipped.
This commit is contained in:
@@ -50,28 +50,30 @@ export type AnalyticsAttributeValue = string | number | boolean;
|
|||||||
export type AnalyticsUser = {
|
export type AnalyticsUser = {
|
||||||
/** Stable identifier for this user. Joins events across sessions + devices. */
|
/** Stable identifier for this user. Joins events across sessions + devices. */
|
||||||
id: string;
|
id: string;
|
||||||
/**
|
|
||||||
* Traits associated with the user. Consumer-owned PII policy applies here —
|
|
||||||
* see ADR-024 § "PII boundary" + the consumer's privacy/cookie consent layer.
|
|
||||||
*/
|
|
||||||
traits?: Record<string, AnalyticsAttributeValue>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface IAnalytics {
|
export interface IAnalytics {
|
||||||
/** Emit a named event. The conformance gate cross-checks `event` against the manifest's analyticsEvents. */
|
/** Emit a named event. The conformance gate cross-checks `event` against the manifest's analyticsEvents. */
|
||||||
track(
|
track(
|
||||||
event: string,
|
event: string,
|
||||||
properties?: Record<string, AnalyticsAttributeValue>,
|
attributes?: Record<string, AnalyticsAttributeValue>,
|
||||||
user?: AnalyticsUser,
|
|
||||||
): void;
|
): void;
|
||||||
|
|
||||||
/** Associate traits with a user. Not gated by the manifest — it's identity establishment, not an event. */
|
/**
|
||||||
identify(user: AnalyticsUser): void;
|
* Associate the current analytics session with a user. The optional
|
||||||
|
* `attributes` carry user traits (plan, signup date, etc.) — see
|
||||||
|
* ADR-024 § "PII boundary". Not gated by the manifest — it's identity
|
||||||
|
* establishment, not an event.
|
||||||
|
*/
|
||||||
|
identify(
|
||||||
|
user: AnalyticsUser,
|
||||||
|
attributes?: Record<string, AnalyticsAttributeValue>,
|
||||||
|
): void;
|
||||||
|
|
||||||
/** Client-side route change. Server impls no-op by convention. */
|
/** Client-side route change. Server impls no-op by convention. */
|
||||||
pageView(
|
pageView(
|
||||||
path: string,
|
path: string,
|
||||||
properties?: Record<string, AnalyticsAttributeValue>,
|
attributes?: Record<string, AnalyticsAttributeValue>,
|
||||||
): void;
|
): void;
|
||||||
|
|
||||||
/** Drain the in-memory batch. Wire into SIGTERM / beforeExit / serverless-response-finish handlers. */
|
/** Drain the in-memory batch. Wire into SIGTERM / beforeExit / serverless-response-finish handlers. */
|
||||||
@@ -79,7 +81,7 @@ export interface IAnalytics {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Four methods. Mirrors Segment Spec's core minus the rarely-needed (`group`, `alias`, `screen`). `flush()` is on the interface because every meaningful server-side SDK batches by default and event loss on shutdown is the most common analytics bug in serverless deployments.
|
Four methods. Mirrors Segment Spec's core minus the rarely-needed (`group`, `alias`, `screen`). Events are attributed to the user established by the most recent `identify()` call — the standard SDK model — so `track()` carries only the event name and its attributes, and `AnalyticsUser` stays minimal (`id` only); traits ride the `attributes` argument of `identify()`. `flush()` is on the interface because every meaningful server-side SDK batches by default and event loss on shutdown is the most common analytics bug in serverless deployments.
|
||||||
|
|
||||||
### Manifest field
|
### Manifest field
|
||||||
|
|
||||||
@@ -97,7 +99,7 @@ useCases: {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`analyticsEvents` is an array of event slug literals. Same syntax as `audits` / `publishes` / `consumes`. The use case body emits via `analytics.track(slug, properties, user?)` calls.
|
`analyticsEvents` is an array of event slug literals. Same syntax as `audits` / `publishes` / `consumes`. The use case body emits via `analytics.track(slug, attributes?)` calls.
|
||||||
|
|
||||||
### Brand + wrapper
|
### Brand + wrapper
|
||||||
|
|
||||||
@@ -140,14 +142,14 @@ This is the only place the analytics channel structurally diverges from `ILogger
|
|||||||
|
|
||||||
The observability surface (`ILogger`, `ITracer`) is bound to ADR-017 §7: id-only user context, `sendDefaultPii: false` everywhere, CI grep gate, server-side PII scrubbing at the OTel processor layer. That policy is deliberate and remains untouched.
|
The observability surface (`ILogger`, `ITracer`) is bound to ADR-017 §7: id-only user context, `sendDefaultPii: false` everywhere, CI grep gate, server-side PII scrubbing at the OTel processor layer. That policy is deliberate and remains untouched.
|
||||||
|
|
||||||
The analytics surface is **structurally permissive**. `AnalyticsUser.traits` accepts arbitrary `Record<string, AnalyticsAttributeValue>`. The template makes no claim about what's allowed in it. Consumer is responsible for:
|
The analytics surface is **structurally permissive**. The `attributes` argument of `identify()` accepts arbitrary `Record<string, AnalyticsAttributeValue>` user traits. The template makes no claim about what's allowed in it. Consumer is responsible for:
|
||||||
|
|
||||||
- Cookie consent and legal basis (e.g. GDPR Art. 6)
|
- Cookie consent and legal basis (e.g. GDPR Art. 6)
|
||||||
- Retention policy in the analytics backend
|
- Retention policy in the analytics backend
|
||||||
- Trait allowlist enforcement in their application code (if they want stricter than "permissive")
|
- Trait allowlist enforcement in their application code (if they want stricter than "permissive")
|
||||||
- DSAR / right-to-erasure plumbing
|
- DSAR / right-to-erasure plumbing
|
||||||
|
|
||||||
This is documented in the interface's doc-comment on `AnalyticsUser.traits` and in `docs/guides/analytics.md`. No CI guardrail enforces it — the cross-cutting CI gates (ADR-022 library trace covering the backend, ADR-023 supply-chain scan) cover the actionable surface.
|
This is documented in the interface's doc-comment on `identify()` and in `docs/guides/analytics.md`. No CI guardrail enforces it — the cross-cutting CI gates (ADR-022 library trace covering the backend, ADR-023 supply-chain scan) cover the actionable surface.
|
||||||
|
|
||||||
The reason this divergence is explicit rather than emergent: analytics PII is product/legal scope, not template scope. Conflating it with observability PII would either cripple analytics (id-only is unworkable for funnel analysis) or weaken observability (allowing traits in `ILogger.setUser` would erode ADR-017's grep gate).
|
The reason this divergence is explicit rather than emergent: analytics PII is product/legal scope, not template scope. Conflating it with observability PII would either cripple analytics (id-only is unworkable for funnel analysis) or weaken observability (allowing traits in `ILogger.setUser` would erode ADR-017's grep gate).
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user